From e473c47bcfb104b99755f223c29b91432fe641b0 Mon Sep 17 00:00:00 2001 From: holychowders Date: Wed, 4 May 2022 08:24:19 -0500 Subject: [PATCH 1/6] Distinguish stealth from armor and stealth from sneaking. Also fixes #1525. - Armor and sneaking have exclusive effects on overall stealth, rather than armor taking effect only while sneaking. Gameplay: - Agents factor in stealth from armor in all cases, not only when sneaking. - Max stealth takes place when sneaking (final multiplier of `0.7`) and with stealth from armor up to `0.7` (`0.3` multiplier), resulting in a max distance modifier of about `0.5`, approximately what it was previously. - Min stealth score from armor is now 0 instead of 2. Internals: - Stealth getter accounts for sneaking in final calculation, not just armor. - Prevents potential division by zero. - Stealth getter returns value that should be multiplied instead of divided. - Legitimized stealth as a score between 0 and 1. Notes: - FIXME: Someone more familiar with the different armor types may want to adjust their stealths. - Armor stealths seem to be valued between `0.0` and `1.0`, and I've reinforced this in the code. However, it is possible, particularly for the `Dragonscale` armors, to cumulatively reach a value of `2.0`. --- common/src/combat.rs | 52 +++++++++++++++++++++++++++++++++++------ server/src/sys/agent.rs | 27 +++++++-------------- 2 files changed, 54 insertions(+), 25 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index dd305c19b9..d0b9c4575c 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -34,6 +34,9 @@ use std::{ops::MulAssign, time::Duration}; #[cfg(not(target_arch = "wasm32"))] use vek::*; #[cfg(not(target_arch = "wasm32"))] + +const STEALTH_SUM_FROM_ITEMS_MAX: f32 = 0.3; + #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum GroupTarget { InGroup, @@ -1164,13 +1167,48 @@ pub fn compute_max_energy_mod(inventory: Option<&Inventory>) -> f32 { }) } -/// Computes the sneak coefficient from armor. Agent perception distances are -/// divided by the resulting f32. +pub fn is_stealth_from_items_maxed(inventory: Option<&Inventory>) -> bool { + let sum = compute_stealth_sum_from_items(inventory); + + sum >= STEALTH_SUM_FROM_ITEMS_MAX +} + +/// Returns a value to be included as a multiplicative factor in perception +/// distance checks. #[cfg(not(target_arch = "wasm32"))] -pub fn compute_stealth_coefficient(inventory: Option<&Inventory>) -> f32 { - // Starts with a value of 2.0 when summing the stats from each armor piece, and - // defaults to a value of 2.0 if no inventory is equipped - inventory.map_or(2.0, |inv| { +pub fn perception_dist_multiplier_from_stealth( + inventory: Option<&Inventory>, + character_state: Option<&CharacterState>, +) -> f32 { + const SNEAK_MULTIPLIER: f32 = 0.7; + const STEALTH_SUM_FROM_ITEMS_MIN: f32 = 0.0; + const MULTIPLIER_FROM_ITEMS_MAX: f32 = 0.7; + const MULTIPLIER_FROM_ITEMS_MIN: f32 = 1.0; + + let stealth_sum = compute_stealth_sum_from_items(inventory); + let is_sneaking = character_state.map_or(false, |state| state.is_stealthy()); + + let mut multiplier; + + if stealth_sum > STEALTH_SUM_FROM_ITEMS_MAX { + multiplier = MULTIPLIER_FROM_ITEMS_MAX; + } else if stealth_sum < STEALTH_SUM_FROM_ITEMS_MIN { + multiplier = MULTIPLIER_FROM_ITEMS_MIN; + } else { + multiplier = 1.0 - stealth_sum; + } + + if is_sneaking { + multiplier *= SNEAK_MULTIPLIER; + } + + multiplier +} + +/// Returns sum of stealth from items equipped. +#[cfg(not(target_arch = "wasm32"))] +pub fn compute_stealth_sum_from_items(inventory: Option<&Inventory>) -> f32 { + inventory.map_or(0.0, |inv| { inv.equipped_items() .filter_map(|item| { if let ItemKind::Armor(armor) = &item.kind() { @@ -1179,7 +1217,7 @@ pub fn compute_stealth_coefficient(inventory: Option<&Inventory>) -> f32 { None } }) - .fold(2.0, |a, b| a + b.max(0.0)) + .fold(0.0, |a, b| a + b) }) } diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index fe3908161f..3d1e142920 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -20,7 +20,7 @@ use crate::{ }, }; use common::{ - combat::compute_stealth_coefficient, + combat::perception_dist_multiplier_from_stealth, comp::{ self, agent::{ @@ -2429,26 +2429,17 @@ impl<'a> AgentData<'a> { other_pos: &Pos, read_data: &ReadData, ) -> bool { - let other_stealth_coefficient = { - let is_other_stealthy = read_data - .char_states - .get(other) - .map_or(false, CharacterState::is_stealthy); + let other_stealth_multiplier = { + let other_inventory = read_data.inventories.get(other); + let other_char_state = read_data.char_states.get(other); - if is_other_stealthy { - // TODO: We shouldn't have to check CharacterState. This should be factored in - // by the function (such as the one we're calling below) that supposedly - // computes a coefficient given stealthy-ness. - compute_stealth_coefficient(read_data.inventories.get(other)) - } else { - 1.0 - } + perception_dist_multiplier_from_stealth(other_inventory, other_char_state) }; - let dist_sqrd = other_pos.0.distance_squared(self.pos.0); - let within_sight_dist = { - let sight_dist = agent.psyche.sight_dist / other_stealth_coefficient; + let sight_dist = agent.psyche.sight_dist * other_stealth_multiplier; + let dist_sqrd = other_pos.0.distance_squared(self.pos.0); + dist_sqrd < sight_dist.powi(2) }; @@ -2458,7 +2449,7 @@ impl<'a> AgentData<'a> { let other_body = read_data.bodies.get(other); - within_sight_dist + (within_sight_dist) && within_fov && entities_have_line_of_sight(self.pos, self.body, other_pos, other_body, read_data) } From dc9dbc30b49d75875d8ae8c5740fabfe4c70aaaf Mon Sep 17 00:00:00 2001 From: holychowders Date: Wed, 4 May 2022 08:48:57 -0500 Subject: [PATCH 2/6] - Adjust Stats Diary UI to show stealth as a percentage from armor items. - `(Max)` is also shown when stealth from items is maxed out (currently 30%). Notes: - FIXME: Someone more familiar with the different armor types may want to adjust their stealths. - In addition, the current max stealth sum from armor is `0.3`, beyond which no stealth effect is caused. --- voxygen/src/hud/diary.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index 19188b69ae..631411a6da 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -1112,7 +1112,7 @@ impl<'a> Widget for Diary<'a> { "Stun-Resistance", "Crit-Power", "Energy Reward", - "Stealth", + "Stealth (Items)", "Weapon Power", "Weapon Speed", "Weapon Poise", @@ -1206,9 +1206,20 @@ impl<'a> Widget for Diary<'a> { combat::compute_energy_reward_mod(Some(self.inventory)); format!("{:+.0}%", (energy_rew - 1.0) * 100.0) }, - "Stealth" => { - let stealth = combat::compute_stealth_coefficient(Some(self.inventory)); - format!("{:.2}", stealth) + "Stealth (Items)" => { + let stealth_perception_multiplier = + combat::perception_dist_multiplier_from_stealth( + Some(self.inventory), + None, + ); + let mut txt = + format!("{:+.1}%", (1.0 - stealth_perception_multiplier) * 100.0); + + if combat::is_stealth_from_items_maxed(Some(self.inventory)) { + txt = format!("{} (Max)", txt); + } + + txt }, "Weapon Power" => match (main_weap_stats, off_weap_stats) { (Some(m_stats), Some(o_stats)) => { From 4890ac99da89be7e8be0bea9c36d7906e7cbd239 Mon Sep 17 00:00:00 2001 From: holychowders Date: Wed, 4 May 2022 12:13:41 -0500 Subject: [PATCH 3/6] Update changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a662753018..17f294785c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [Server] Kick clients who send messages on the wrong stream - Reworked Merchant trade price calculation, Merchants offer more wares - Enable new giant trees, changed what entities spawn at them +- Stealth is now shown as a percentage in Stats Diary UI +- Stealth effects from sneaking and armor are evaluated independently. Armor now has effects even when not sneaking ### Removed From 1ebbf335e196cca0625961d291cb949df51604de Mon Sep 17 00:00:00 2001 From: holychowders Date: Fri, 6 May 2022 15:41:55 -0500 Subject: [PATCH 4/6] Simplify stealth logic. Remove max item stealth upper limit and restore close to original curve. --- common/src/combat.rs | 35 ++++++++--------------------------- voxygen/src/hud/diary.rs | 6 +----- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index d0b9c4575c..7c57d55648 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -34,9 +34,6 @@ use std::{ops::MulAssign, time::Duration}; #[cfg(not(target_arch = "wasm32"))] use vek::*; #[cfg(not(target_arch = "wasm32"))] - -const STEALTH_SUM_FROM_ITEMS_MAX: f32 = 0.3; - #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum GroupTarget { InGroup, @@ -1167,12 +1164,6 @@ pub fn compute_max_energy_mod(inventory: Option<&Inventory>) -> f32 { }) } -pub fn is_stealth_from_items_maxed(inventory: Option<&Inventory>) -> bool { - let sum = compute_stealth_sum_from_items(inventory); - - sum >= STEALTH_SUM_FROM_ITEMS_MAX -} - /// Returns a value to be included as a multiplicative factor in perception /// distance checks. #[cfg(not(target_arch = "wasm32"))] @@ -1181,34 +1172,22 @@ pub fn perception_dist_multiplier_from_stealth( character_state: Option<&CharacterState>, ) -> f32 { const SNEAK_MULTIPLIER: f32 = 0.7; - const STEALTH_SUM_FROM_ITEMS_MIN: f32 = 0.0; - const MULTIPLIER_FROM_ITEMS_MAX: f32 = 0.7; - const MULTIPLIER_FROM_ITEMS_MIN: f32 = 1.0; - let stealth_sum = compute_stealth_sum_from_items(inventory); + let item_stealth_multiplier = stealth_multiplier_from_items(inventory); let is_sneaking = character_state.map_or(false, |state| state.is_stealthy()); - let mut multiplier; - - if stealth_sum > STEALTH_SUM_FROM_ITEMS_MAX { - multiplier = MULTIPLIER_FROM_ITEMS_MAX; - } else if stealth_sum < STEALTH_SUM_FROM_ITEMS_MIN { - multiplier = MULTIPLIER_FROM_ITEMS_MIN; - } else { - multiplier = 1.0 - stealth_sum; - } + let mut multiplier = item_stealth_multiplier; if is_sneaking { multiplier *= SNEAK_MULTIPLIER; } - multiplier + multiplier.clamp(0.0, 1.0) } -/// Returns sum of stealth from items equipped. #[cfg(not(target_arch = "wasm32"))] -pub fn compute_stealth_sum_from_items(inventory: Option<&Inventory>) -> f32 { - inventory.map_or(0.0, |inv| { +pub fn stealth_multiplier_from_items(inventory: Option<&Inventory>) -> f32 { + let stealth_sum = inventory.map_or(0.0, |inv| { inv.equipped_items() .filter_map(|item| { if let ItemKind::Armor(armor) = &item.kind() { @@ -1218,7 +1197,9 @@ pub fn compute_stealth_sum_from_items(inventory: Option<&Inventory>) -> f32 { } }) .fold(0.0, |a, b| a + b) - }) + }); + + (1.0 / (1.0 + stealth_sum)).clamp(0.0, 1.0) } /// Computes the total protection provided from armor. Is used to determine the diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index 631411a6da..0e8ddcee78 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -1212,13 +1212,9 @@ impl<'a> Widget for Diary<'a> { Some(self.inventory), None, ); - let mut txt = + let txt = format!("{:+.1}%", (1.0 - stealth_perception_multiplier) * 100.0); - if combat::is_stealth_from_items_maxed(Some(self.inventory)) { - txt = format!("{} (Max)", txt); - } - txt }, "Weapon Power" => match (main_weap_stats, off_weap_stats) { From 4c76e9314d2fb57f06f70d154da265f721c3ef17 Mon Sep 17 00:00:00 2001 From: holychowders Date: Fri, 6 May 2022 15:46:09 -0500 Subject: [PATCH 5/6] Use `sum` instead of `fold`. --- common/src/combat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 7c57d55648..4d407478dd 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -1196,7 +1196,7 @@ pub fn stealth_multiplier_from_items(inventory: Option<&Inventory>) -> f32 { None } }) - .fold(0.0, |a, b| a + b) + .sum() }); (1.0 / (1.0 + stealth_sum)).clamp(0.0, 1.0) From fc2095803dc5532ab15d6cb5bbd5f5a56a435b4c Mon Sep 17 00:00:00 2001 From: holychowders Date: Mon, 9 May 2022 22:23:08 -0500 Subject: [PATCH 6/6] Use inline conditional to simplify assignment. --- common/src/combat.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/common/src/combat.rs b/common/src/combat.rs index 4d407478dd..3d271016ac 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -1176,11 +1176,7 @@ pub fn perception_dist_multiplier_from_stealth( let item_stealth_multiplier = stealth_multiplier_from_items(inventory); let is_sneaking = character_state.map_or(false, |state| state.is_stealthy()); - let mut multiplier = item_stealth_multiplier; - - if is_sneaking { - multiplier *= SNEAK_MULTIPLIER; - } + let multiplier = item_stealth_multiplier * if is_sneaking { SNEAK_MULTIPLIER } else { 1.0 }; multiplier.clamp(0.0, 1.0) }