mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Changed Item to have ItemBase instead of ItemDef. NO ASSETS.
This commit is contained in:
parent
5bacf526ad
commit
08b7bb781f
@ -1091,8 +1091,9 @@ impl Client {
|
||||
|
||||
// Closure to get inner modular component info from item in a given slot
|
||||
let unwrap_modular = |slot| {
|
||||
if let Some(ItemKind::ModularComponent(mod_comp)) =
|
||||
inventory.and_then(|inv| inv.get(slot).map(|item| &item.kind))
|
||||
if let Some(ItemKind::ModularComponent(mod_comp)) = inventory
|
||||
.and_then(|inv| inv.get(slot).map(|item| item.kind()))
|
||||
.as_deref()
|
||||
{
|
||||
Some(mod_comp.modkind)
|
||||
} else {
|
||||
|
@ -3,11 +3,7 @@ use crate::comp::buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource};
|
||||
use crate::{
|
||||
comp::{
|
||||
inventory::{
|
||||
item::{
|
||||
armor::Protection,
|
||||
tool::{self, Tool, ToolKind},
|
||||
Item, ItemDesc, ItemKind, MaterialStatManifest,
|
||||
},
|
||||
item::{armor::Protection, tool::ToolKind, ItemDesc, ItemKind, MaterialStatManifest},
|
||||
slot::EquipSlot,
|
||||
},
|
||||
skillset::SkillGroupKind,
|
||||
@ -949,26 +945,28 @@ impl CombatBuff {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn equipped_item_and_tool(inv: &Inventory, slot: EquipSlot) -> Option<(&Item, &Tool)> {
|
||||
inv.equipped(slot).and_then(|i| {
|
||||
if let ItemKind::Tool(tool) = &i.kind() {
|
||||
Some((i, tool))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn get_weapons(inv: &Inventory) -> (Option<ToolKind>, Option<ToolKind>) {
|
||||
pub fn get_weapon_kinds(inv: &Inventory) -> (Option<ToolKind>, Option<ToolKind>) {
|
||||
(
|
||||
equipped_item_and_tool(inv, EquipSlot::ActiveMainhand).map(|(_, tool)| tool.kind),
|
||||
equipped_item_and_tool(inv, EquipSlot::ActiveOffhand).map(|(_, tool)| tool.kind),
|
||||
inv.equipped(EquipSlot::ActiveMainhand).and_then(|i| {
|
||||
if let ItemKind::Tool(tool) = &*i.kind() {
|
||||
Some(tool.kind)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
inv.equipped(EquipSlot::ActiveOffhand).and_then(|i| {
|
||||
if let ItemKind::Tool(tool) = &*i.kind() {
|
||||
Some(tool.kind)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn weapon_rating<T: ItemDesc>(item: &T, msm: &MaterialStatManifest) -> f32 {
|
||||
// TODO: Either remove msm or use it as argument in fn kind
|
||||
pub fn weapon_rating<T: ItemDesc>(item: &T, _msm: &MaterialStatManifest) -> f32 {
|
||||
const DAMAGE_WEIGHT: f32 = 2.0;
|
||||
const SPEED_WEIGHT: f32 = 3.0;
|
||||
const CRIT_CHANCE_WEIGHT: f32 = 1.25;
|
||||
@ -978,8 +976,8 @@ pub fn weapon_rating<T: ItemDesc>(item: &T, msm: &MaterialStatManifest) -> f32 {
|
||||
const ENERGY_EFFICIENCY_WEIGHT: f32 = 0.0;
|
||||
const BUFF_STRENGTH_WEIGHT: f32 = 0.0;
|
||||
|
||||
if let ItemKind::Tool(tool) = item.kind() {
|
||||
let stats = tool::Stats::from((msm, item.components(), tool));
|
||||
if let ItemKind::Tool(tool) = &*item.kind() {
|
||||
let stats = tool.stats;
|
||||
|
||||
// TODO: Look into changing the 0.5 to reflect armor later maybe?
|
||||
// Since it is only for weapon though, it probably makes sense to leave
|
||||
@ -1018,7 +1016,7 @@ pub fn weapon_rating<T: ItemDesc>(item: &T, msm: &MaterialStatManifest) -> f32 {
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn weapon_skills(inventory: &Inventory, skill_set: &SkillSet) -> f32 {
|
||||
let (mainhand, offhand) = get_weapons(inventory);
|
||||
let (mainhand, offhand) = get_weapon_kinds(inventory);
|
||||
let mainhand_skills = if let Some(tool) = mainhand {
|
||||
skill_set.earned_sp(SkillGroupKind::Weapon(tool)) as f32
|
||||
} else {
|
||||
@ -1034,19 +1032,17 @@ fn weapon_skills(inventory: &Inventory, skill_set: &SkillSet) -> f32 {
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn get_weapon_rating(inventory: &Inventory, msm: &MaterialStatManifest) -> f32 {
|
||||
let mainhand_rating =
|
||||
if let Some((item, _)) = equipped_item_and_tool(inventory, EquipSlot::ActiveMainhand) {
|
||||
weapon_rating(item, msm)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let mainhand_rating = if let Some(item) = inventory.equipped(EquipSlot::ActiveMainhand) {
|
||||
weapon_rating(item, msm)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let offhand_rating =
|
||||
if let Some((item, _)) = equipped_item_and_tool(inventory, EquipSlot::ActiveOffhand) {
|
||||
weapon_rating(item, msm)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let offhand_rating = if let Some(item) = inventory.equipped(EquipSlot::ActiveOffhand) {
|
||||
weapon_rating(item, msm)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
mainhand_rating.max(offhand_rating)
|
||||
}
|
||||
@ -1118,7 +1114,7 @@ pub fn compute_crit_mult(inventory: Option<&Inventory>) -> f32 {
|
||||
inventory.map_or(1.25, |inv| {
|
||||
inv.equipped_items()
|
||||
.filter_map(|item| {
|
||||
if let ItemKind::Armor(armor) = &item.kind() {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.crit_power()
|
||||
} else {
|
||||
None
|
||||
@ -1136,7 +1132,7 @@ pub fn compute_energy_reward_mod(inventory: Option<&Inventory>) -> f32 {
|
||||
inventory.map_or(1.0, |inv| {
|
||||
inv.equipped_items()
|
||||
.filter_map(|item| {
|
||||
if let ItemKind::Armor(armor) = &item.kind() {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.energy_reward()
|
||||
} else {
|
||||
None
|
||||
@ -1154,7 +1150,7 @@ pub fn compute_max_energy_mod(inventory: Option<&Inventory>) -> f32 {
|
||||
inventory.map_or(0.0, |inv| {
|
||||
inv.equipped_items()
|
||||
.filter_map(|item| {
|
||||
if let ItemKind::Armor(armor) = &item.kind() {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.energy_max()
|
||||
} else {
|
||||
None
|
||||
@ -1186,7 +1182,7 @@ 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() {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.stealth()
|
||||
} else {
|
||||
None
|
||||
@ -1206,7 +1202,7 @@ pub fn compute_protection(inventory: Option<&Inventory>) -> Option<f32> {
|
||||
inventory.map_or(Some(0.0), |inv| {
|
||||
inv.equipped_items()
|
||||
.filter_map(|item| {
|
||||
if let ItemKind::Armor(armor) = &item.kind() {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.protection()
|
||||
} else {
|
||||
None
|
||||
|
@ -94,7 +94,7 @@ impl ActiveAbilities {
|
||||
) -> [AuxiliaryAbility; MAX_ABILITIES] {
|
||||
let tool_kind = |slot| {
|
||||
inv.and_then(|inv| inv.equipped(slot))
|
||||
.and_then(|item| match item.kind() {
|
||||
.and_then(|item| match &*item.kind() {
|
||||
ItemKind::Tool(tool) => Some(tool.kind),
|
||||
_ => None,
|
||||
})
|
||||
@ -148,12 +148,12 @@ impl ActiveAbilities {
|
||||
};
|
||||
|
||||
let scale_ability = |ability: CharacterAbility, equip_slot| {
|
||||
let tool_kind =
|
||||
inv.and_then(|inv| inv.equipped(equip_slot))
|
||||
.and_then(|item| match &item.kind {
|
||||
ItemKind::Tool(tool) => Some(tool.kind),
|
||||
_ => None,
|
||||
});
|
||||
let tool_kind = inv
|
||||
.and_then(|inv| inv.equipped(equip_slot))
|
||||
.and_then(|item| match &*item.kind() {
|
||||
ItemKind::Tool(tool) => Some(tool.kind),
|
||||
_ => None,
|
||||
});
|
||||
ability.adjusted_by_skills(skill_set, tool_kind)
|
||||
};
|
||||
|
||||
|
@ -62,7 +62,7 @@ impl From<Body> for super::Body {
|
||||
|
||||
impl From<&Item> for Body {
|
||||
fn from(item: &Item) -> Self {
|
||||
match item.kind() {
|
||||
match &*item.kind() {
|
||||
ItemKind::Tool(Tool { kind, .. }) => Body::Tool(*kind),
|
||||
ItemKind::ModularComponent(_) => Body::ModularComponent,
|
||||
ItemKind::Lantern(_) => Body::Lantern,
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
assets::AssetExt,
|
||||
comp::inventory::item::{
|
||||
armor::{Armor, ArmorKind},
|
||||
modular, tool, Glider, ItemDef, ItemDesc, ItemKind, Lantern, Throwable, Utility,
|
||||
modular, Glider, ItemDef, ItemDesc, ItemKind, Lantern, Throwable, Utility,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -26,15 +26,14 @@ pub enum ItemKey {
|
||||
|
||||
impl<T: ItemDesc> From<&T> for ItemKey {
|
||||
fn from(item_desc: &T) -> Self {
|
||||
let item_kind = item_desc.kind();
|
||||
let item_definition_id = item_desc.item_definition_id();
|
||||
|
||||
match item_kind {
|
||||
ItemKind::Tool(tool) => {
|
||||
use tool::StatKind;
|
||||
match tool.stats {
|
||||
StatKind::Direct(_) => ItemKey::Tool(item_definition_id.to_owned()),
|
||||
StatKind::Modular => ItemKey::ModularWeapon(modular::weapon_to_key(item_desc)),
|
||||
match &*item_desc.kind() {
|
||||
ItemKind::Tool(_) => {
|
||||
if item_desc.is_modular() {
|
||||
ItemKey::ModularWeapon(modular::weapon_to_key(item_desc))
|
||||
} else {
|
||||
ItemKey::Tool(item_definition_id.to_owned())
|
||||
}
|
||||
},
|
||||
ItemKind::ModularComponent(_) => {
|
||||
|
@ -4,7 +4,7 @@ pub mod modular;
|
||||
pub mod tool;
|
||||
|
||||
// Reexports
|
||||
pub use modular::{ModularComponent, ModularComponentKind};
|
||||
pub use modular::{ModularBase, ModularComponent, ModularComponentKind};
|
||||
pub use tool::{AbilitySet, AbilitySpec, Hands, MaterialStatManifest, Tool, ToolKind};
|
||||
|
||||
use crate::{
|
||||
@ -18,7 +18,6 @@ use core::{
|
||||
convert::TryFrom,
|
||||
mem,
|
||||
num::{NonZeroU32, NonZeroU64},
|
||||
ops::Deref,
|
||||
};
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use serde::{de, Deserialize, Serialize, Serializer};
|
||||
@ -71,12 +70,6 @@ pub struct Glider {
|
||||
pub kind: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum QualityKind {
|
||||
Direct(Quality),
|
||||
Modular,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Copy, PartialOrd, Ord)]
|
||||
pub enum Quality {
|
||||
Low, // Grey
|
||||
@ -90,10 +83,10 @@ pub enum Quality {
|
||||
}
|
||||
|
||||
pub trait TagExampleInfo {
|
||||
fn name(&self) -> Cow<'static, str>;
|
||||
fn name(&self) -> &str;
|
||||
/// What item to show in the crafting hud if the player has nothing with the
|
||||
/// tag
|
||||
fn exemplar_identifier(&self) -> Cow<'static, str>;
|
||||
fn exemplar_identifier(&self) -> &str;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, IntoStaticStr)]
|
||||
@ -215,11 +208,9 @@ impl Material {
|
||||
}
|
||||
|
||||
impl TagExampleInfo for Material {
|
||||
fn name(&self) -> Cow<'static, str> { Cow::Borrowed(self.into()) }
|
||||
fn name(&self) -> &str { self.into() }
|
||||
|
||||
fn exemplar_identifier(&self) -> Cow<'static, str> {
|
||||
Cow::Borrowed(self.asset_identifier().unwrap_or(""))
|
||||
}
|
||||
fn exemplar_identifier(&self) -> &str { self.asset_identifier().unwrap_or("") }
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -238,36 +229,36 @@ pub enum ItemTag {
|
||||
}
|
||||
|
||||
impl TagExampleInfo for ItemTag {
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
ItemTag::Material(material) => material.name(),
|
||||
ItemTag::MaterialKind(material_kind) => Cow::Borrowed(material_kind.into()),
|
||||
ItemTag::Leather => Cow::Borrowed("leather"),
|
||||
ItemTag::Cultist => Cow::Borrowed("cultist"),
|
||||
ItemTag::Potion => Cow::Borrowed("potion"),
|
||||
ItemTag::Food => Cow::Borrowed("food"),
|
||||
ItemTag::BaseMaterial => Cow::Borrowed("basemat"),
|
||||
ItemTag::CraftingTool => Cow::Borrowed("tool"),
|
||||
ItemTag::Utility => Cow::Borrowed("utility"),
|
||||
ItemTag::Bag => Cow::Borrowed("bag"),
|
||||
ItemTag::SalvageInto(_) => Cow::Borrowed("salvage"),
|
||||
ItemTag::MaterialKind(material_kind) => material_kind.into(),
|
||||
ItemTag::Leather => "leather",
|
||||
ItemTag::Cultist => "cultist",
|
||||
ItemTag::Potion => "potion",
|
||||
ItemTag::Food => "food",
|
||||
ItemTag::BaseMaterial => "basemat",
|
||||
ItemTag::CraftingTool => "tool",
|
||||
ItemTag::Utility => "utility",
|
||||
ItemTag::Bag => "bag",
|
||||
ItemTag::SalvageInto(_) => "salvage",
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Autogenerate these?
|
||||
fn exemplar_identifier(&self) -> Cow<'static, str> {
|
||||
fn exemplar_identifier(&self) -> &str {
|
||||
match self {
|
||||
ItemTag::Material(_) => Cow::Borrowed("common.items.tag_examples.placeholder"),
|
||||
ItemTag::MaterialKind(_) => Cow::Borrowed("common.items.tag_examples.placeholder"),
|
||||
ItemTag::Leather => Cow::Borrowed("common.items.tag_examples.leather"),
|
||||
ItemTag::Cultist => Cow::Borrowed("common.items.tag_examples.cultist"),
|
||||
ItemTag::Potion => Cow::Borrowed("common.items.tag_examples.placeholder"),
|
||||
ItemTag::Food => Cow::Borrowed("common.items.tag_examples.placeholder"),
|
||||
ItemTag::BaseMaterial => Cow::Borrowed("common.items.tag_examples.placeholder"),
|
||||
ItemTag::CraftingTool => Cow::Borrowed("common.items.tag_examples.placeholder"),
|
||||
ItemTag::Utility => Cow::Borrowed("common.items.tag_examples.placeholder"),
|
||||
ItemTag::Bag => Cow::Borrowed("common.items.tag_examples.placeholder"),
|
||||
ItemTag::SalvageInto(_) => Cow::Borrowed("common.items.tag_examples.placeholder"),
|
||||
ItemTag::Material(_) => "common.items.tag_examples.placeholder",
|
||||
ItemTag::MaterialKind(_) => "common.items.tag_examples.placeholder",
|
||||
ItemTag::Leather => "common.items.tag_examples.leather",
|
||||
ItemTag::Cultist => "common.items.tag_examples.cultist",
|
||||
ItemTag::Potion => "common.items.tag_examples.placeholder",
|
||||
ItemTag::Food => "common.items.tag_examples.placeholder",
|
||||
ItemTag::BaseMaterial => "common.items.tag_examples.placeholder",
|
||||
ItemTag::CraftingTool => "common.items.tag_examples.placeholder",
|
||||
ItemTag::Utility => "common.items.tag_examples.placeholder",
|
||||
ItemTag::Bag => "common.items.tag_examples.placeholder",
|
||||
ItemTag::SalvageInto(_) => "common.items.tag_examples.placeholder",
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -346,10 +337,10 @@ pub struct Item {
|
||||
/// could change invariants like whether it was stackable (invalidating
|
||||
/// the amount).
|
||||
#[serde(
|
||||
serialize_with = "serialize_item_def",
|
||||
deserialize_with = "deserialize_item_def"
|
||||
serialize_with = "serialize_item_base",
|
||||
deserialize_with = "deserialize_item_base"
|
||||
)]
|
||||
item_def: Arc<ItemDef>,
|
||||
item_base: ItemBase,
|
||||
/// components is hidden to maintain the following invariants:
|
||||
/// - It should only contain modular components (and enhancements, once they
|
||||
/// exist)
|
||||
@ -374,7 +365,7 @@ use std::hash::{Hash, Hasher};
|
||||
// Used to find inventory item corresponding to hotbar slot
|
||||
impl Hash for Item {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.item_def.item_definition_id.hash(state);
|
||||
self.item_definition_id().hash(state);
|
||||
self.components.hash(state);
|
||||
}
|
||||
}
|
||||
@ -382,33 +373,43 @@ impl Hash for Item {
|
||||
// Custom serialization for ItemDef, we only want to send the item_definition_id
|
||||
// over the network, the client will use deserialize_item_def to fetch the
|
||||
// ItemDef from assets.
|
||||
fn serialize_item_def<S: Serializer>(field: &Arc<ItemDef>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
fn serialize_item_base<S: Serializer>(field: &ItemBase, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&field.item_definition_id)
|
||||
serializer.serialize_str(match field {
|
||||
ItemBase::Raw(item_def) => &item_def.item_definition_id,
|
||||
// TODO: Encode this data somehow
|
||||
ItemBase::Modular(_) => "modular",
|
||||
})
|
||||
}
|
||||
|
||||
// Custom de-serialization for ItemDef to retrieve the ItemDef from assets using
|
||||
// its asset specifier (item_definition_id)
|
||||
fn deserialize_item_def<'de, D>(deserializer: D) -> Result<Arc<ItemDef>, D::Error>
|
||||
fn deserialize_item_base<'de, D>(deserializer: D) -> Result<ItemBase, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct ItemDefStringVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for ItemDefStringVisitor {
|
||||
type Value = Arc<ItemDef>;
|
||||
type Value = ItemBase;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("item def string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, item_definition_id: &str) -> Result<Self::Value, E>
|
||||
fn visit_str<E>(self, serialized_item_base: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Arc::<ItemDef>::load_expect_cloned(item_definition_id))
|
||||
Ok(match serialized_item_base {
|
||||
// TODO: Make this work
|
||||
"modular" => ItemBase::Modular(ModularBase::Tool(ToolKind::Empty)),
|
||||
item_definition_id => {
|
||||
ItemBase::Raw(Arc::<ItemDef>::load_expect_cloned(item_definition_id))
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -422,14 +423,36 @@ pub enum ItemName {
|
||||
Component(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum ItemBase {
|
||||
Raw(Arc<ItemDef>),
|
||||
Modular(modular::ModularBase),
|
||||
}
|
||||
|
||||
impl ItemBase {
|
||||
fn num_slots(&self) -> u16 {
|
||||
match self {
|
||||
ItemBase::Raw(item_def) => item_def.num_slots(),
|
||||
ItemBase::Modular(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn item_definition_id(&self) -> &str {
|
||||
match &self {
|
||||
ItemBase::Raw(item_def) => &item_def.item_definition_id,
|
||||
ItemBase::Modular(mod_base) => mod_base.pseudo_item_id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ItemDef {
|
||||
#[serde(default)]
|
||||
item_definition_id: String,
|
||||
pub name: ItemName,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub kind: ItemKind,
|
||||
pub quality: QualityKind,
|
||||
pub quality: Quality,
|
||||
pub tags: Vec<ItemTag>,
|
||||
#[serde(default)]
|
||||
pub slots: u16,
|
||||
@ -457,17 +480,18 @@ impl TryFrom<(&Item, &AbilityMap, &MaterialStatManifest)> for ItemConfig {
|
||||
type Error = ItemConfigError;
|
||||
|
||||
fn try_from(
|
||||
(item, ability_map, msm): (&Item, &AbilityMap, &MaterialStatManifest),
|
||||
// TODO: Either remove msm or use it as argument in fn kind
|
||||
(item, ability_map, _msm): (&Item, &AbilityMap, &MaterialStatManifest),
|
||||
) -> Result<Self, Self::Error> {
|
||||
if let ItemKind::Tool(tool) = &item.kind {
|
||||
if let ItemKind::Tool(tool) = &*item.kind() {
|
||||
// If no custom ability set is specified, fall back to abilityset of tool kind.
|
||||
let tool_default = |tool_kind| {
|
||||
let key = &AbilitySpec::Tool(tool_kind);
|
||||
ability_map.get_ability_set(key)
|
||||
};
|
||||
let abilities = if let Some(set_key) = item.ability_spec() {
|
||||
if let Some(set) = ability_map.get_ability_set(set_key) {
|
||||
set.clone().modified_by_tool(tool, msm, &item.components)
|
||||
if let Some(set) = ability_map.get_ability_set(&*set_key) {
|
||||
set.clone().modified_by_tool(tool)
|
||||
} else {
|
||||
error!(
|
||||
"Custom ability set: {:?} references non-existent set, falling back to \
|
||||
@ -477,7 +501,7 @@ impl TryFrom<(&Item, &AbilityMap, &MaterialStatManifest)> for ItemConfig {
|
||||
tool_default(tool.kind).cloned().unwrap_or_default()
|
||||
}
|
||||
} else if let Some(set) = tool_default(tool.kind) {
|
||||
set.clone().modified_by_tool(tool, msm, &item.components)
|
||||
set.clone().modified_by_tool(tool)
|
||||
} else {
|
||||
error!(
|
||||
"No ability set defined for tool: {:?}, falling back to default ability set.",
|
||||
@ -504,16 +528,6 @@ impl ItemDef {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_modular(&self) -> bool {
|
||||
matches!(
|
||||
&self.kind,
|
||||
ItemKind::Tool(tool::Tool {
|
||||
stats: tool::StatKind::Modular,
|
||||
..
|
||||
}) | ItemKind::ModularComponent(_)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_component(&self, kind: ModularComponentKind) -> bool {
|
||||
if let ItemKind::ModularComponent(ModularComponent { modkind, .. }) = self.kind {
|
||||
kind == modkind
|
||||
@ -568,28 +582,21 @@ impl ItemDef {
|
||||
/// please don't rely on this for anything!
|
||||
impl PartialEq for Item {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.item_def.item_definition_id == other.item_def.item_definition_id
|
||||
if let (ItemBase::Raw(self_def), ItemBase::Raw(other_def)) =
|
||||
(&self.item_base, &other.item_base)
|
||||
{
|
||||
self_def.item_definition_id == other_def.item_definition_id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Item {
|
||||
type Target = ItemDef;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.item_def }
|
||||
}
|
||||
|
||||
impl assets::Compound for ItemDef {
|
||||
fn load<S: assets::source::Source + ?Sized>(
|
||||
cache: &assets::AssetCache<S>,
|
||||
specifier: &str,
|
||||
) -> Result<Self, BoxedError> {
|
||||
// load from the filesystem first, but if the file doesn't exist, see if it's a
|
||||
// programmatically-generated asset
|
||||
let raw = match cache.load::<RawItemDef>(specifier) {
|
||||
Ok(handle) => handle.cloned(),
|
||||
Err(e) => modular::synthesize_modular_asset(specifier).ok_or(e)?,
|
||||
};
|
||||
|
||||
let RawItemDef {
|
||||
name,
|
||||
description,
|
||||
@ -598,7 +605,7 @@ impl assets::Compound for ItemDef {
|
||||
tags,
|
||||
slots,
|
||||
ability_spec,
|
||||
} = raw;
|
||||
} = cache.load::<RawItemDef>(specifier)?.cloned();
|
||||
|
||||
// Some commands like /give_item provide the asset specifier separated with \
|
||||
// instead of .
|
||||
@ -622,10 +629,10 @@ impl assets::Compound for ItemDef {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename = "ItemDef")]
|
||||
struct RawItemDef {
|
||||
name: ItemName,
|
||||
name: String,
|
||||
description: String,
|
||||
kind: ItemKind,
|
||||
quality: QualityKind,
|
||||
quality: Quality,
|
||||
tags: Vec<ItemTag>,
|
||||
#[serde(default)]
|
||||
slots: u16,
|
||||
@ -646,25 +653,22 @@ impl Item {
|
||||
// loadout when no weapon is present
|
||||
pub fn empty() -> Self { Item::new_from_asset_expect("common.items.weapons.empty.empty") }
|
||||
|
||||
pub fn new_from_item_def(
|
||||
inner_item: Arc<ItemDef>,
|
||||
pub fn new_from_item_base(
|
||||
inner_item: ItemBase,
|
||||
input_components: &[Item],
|
||||
ability_map: &AbilityMap,
|
||||
msm: &MaterialStatManifest,
|
||||
) -> Self {
|
||||
let mut components = Vec::new();
|
||||
if inner_item.is_modular() {
|
||||
// recipe ensures that types match (i.e. no axe heads on a sword hilt, or double
|
||||
// sword blades)
|
||||
components.extend(
|
||||
input_components
|
||||
.iter()
|
||||
.map(|comp| comp.duplicate(ability_map, msm)),
|
||||
);
|
||||
}
|
||||
components.extend(
|
||||
input_components
|
||||
.iter()
|
||||
.map(|comp| comp.duplicate(ability_map, msm)),
|
||||
);
|
||||
|
||||
let item_hash = {
|
||||
let mut s = DefaultHasher::new();
|
||||
inner_item.item_definition_id.hash(&mut s);
|
||||
inner_item.item_definition_id().hash(&mut s);
|
||||
components.hash(&mut s);
|
||||
s.finish()
|
||||
};
|
||||
@ -673,8 +677,8 @@ impl Item {
|
||||
item_id: Arc::new(AtomicCell::new(None)),
|
||||
amount: NonZeroU32::new(1).unwrap(),
|
||||
components,
|
||||
slots: vec![None; inner_item.slots as usize],
|
||||
item_def: inner_item,
|
||||
slots: vec![None; inner_item.num_slots() as usize],
|
||||
item_base: inner_item,
|
||||
item_config: None,
|
||||
hash: item_hash,
|
||||
};
|
||||
@ -685,11 +689,7 @@ impl Item {
|
||||
/// Creates a new instance of an `Item` from the provided asset identifier
|
||||
/// Panics if the asset does not exist.
|
||||
pub fn new_from_asset_expect(asset_specifier: &str) -> Self {
|
||||
let inner_item = Arc::<ItemDef>::load_expect_cloned(asset_specifier);
|
||||
// TODO: Figure out better way to get msm and ability_map
|
||||
let msm = MaterialStatManifest::default();
|
||||
let ability_map = AbilityMap::default();
|
||||
Item::new_from_item_def(inner_item, &[], &ability_map, &msm)
|
||||
Item::new_from_asset(asset_specifier).expect("Expected asset to exist")
|
||||
}
|
||||
|
||||
/// Creates a Vec containing one of each item that matches the provided
|
||||
@ -703,18 +703,26 @@ impl Item {
|
||||
/// Creates a new instance of an `Item from the provided asset identifier if
|
||||
/// it exists
|
||||
pub fn new_from_asset(asset: &str) -> Result<Self, Error> {
|
||||
let inner_item = Arc::<ItemDef>::load_cloned(asset)?;
|
||||
let inner_item = ItemBase::Raw(Arc::<ItemDef>::load_cloned(asset)?);
|
||||
// TODO: Get msm and ability_map less hackily
|
||||
let msm = MaterialStatManifest::default();
|
||||
let ability_map = AbilityMap::default();
|
||||
Ok(Item::new_from_item_def(inner_item, &[], &ability_map, &msm))
|
||||
Ok(Item::new_from_item_base(
|
||||
inner_item,
|
||||
&[],
|
||||
&ability_map,
|
||||
&msm,
|
||||
))
|
||||
}
|
||||
|
||||
/// Duplicates an item, creating an exact copy but with a new item ID
|
||||
#[must_use]
|
||||
pub fn duplicate(&self, ability_map: &AbilityMap, msm: &MaterialStatManifest) -> Self {
|
||||
let mut new_item = Item::new_from_item_def(
|
||||
Arc::clone(&self.item_def),
|
||||
let mut new_item = Item::new_from_item_base(
|
||||
match &self.item_base {
|
||||
ItemBase::Raw(item_def) => ItemBase::Raw(Arc::clone(item_def)),
|
||||
ItemBase::Modular(mod_base) => ItemBase::Modular(mod_base.duplicate()),
|
||||
},
|
||||
&self.components,
|
||||
ability_map,
|
||||
msm,
|
||||
@ -827,18 +835,28 @@ impl Item {
|
||||
self.slots.iter_mut().filter_map(mem::take)
|
||||
}
|
||||
|
||||
pub fn item_definition_id(&self) -> &str { &self.item_def.item_definition_id }
|
||||
pub fn item_definition_id(&self) -> &str {
|
||||
match &self.item_base {
|
||||
ItemBase::Raw(item_def) => &item_def.item_definition_id,
|
||||
// TODO: Should this be handled better?
|
||||
ItemBase::Modular(_) => "",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_same_item_def(&self, item_def: &ItemDef) -> bool {
|
||||
self.item_def.item_definition_id == item_def.item_definition_id
|
||||
if let ItemBase::Raw(self_def) = &self.item_base {
|
||||
self_def.item_definition_id == item_def.item_definition_id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn matches_recipe_input(&self, recipe_input: &RecipeInput) -> bool {
|
||||
match recipe_input {
|
||||
RecipeInput::Item(item_def) => self.is_same_item_def(item_def),
|
||||
RecipeInput::Tag(tag) => self.item_def.tags.contains(tag),
|
||||
RecipeInput::Tag(tag) => self.tags().contains(tag),
|
||||
RecipeInput::TagSameItem(tag, amount) => {
|
||||
self.item_def.tags.contains(tag) && u32::from(self.amount) >= *amount
|
||||
self.tags().contains(tag) && u32::from(self.amount) >= *amount
|
||||
},
|
||||
RecipeInput::ListSameItem(item_defs, amount) => item_defs.iter().any(|item_def| {
|
||||
self.is_same_item_def(item_def) && u32::from(self.amount) >= *amount
|
||||
@ -847,48 +865,65 @@ impl Item {
|
||||
}
|
||||
|
||||
pub fn is_salvageable(&self) -> bool {
|
||||
self.item_def
|
||||
.tags
|
||||
self.tags()
|
||||
.iter()
|
||||
.any(|tag| matches!(tag, ItemTag::SalvageInto(_)))
|
||||
}
|
||||
|
||||
pub fn salvage_output(&self) -> impl Iterator<Item = &str> {
|
||||
self.item_def
|
||||
.tags
|
||||
.iter()
|
||||
.filter_map(|tag| {
|
||||
if let ItemTag::SalvageInto(material) = tag {
|
||||
Some(material)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|material| material.asset_identifier())
|
||||
self.tags().iter().filter_map(|tag| {
|
||||
if let ItemTag::SalvageInto(material) = tag {
|
||||
material.asset_identifier()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Cow<'_, str> {
|
||||
match &self.item_def.name {
|
||||
ItemName::Direct(name) => Cow::Borrowed(name),
|
||||
ItemName::Modular => modular::modular_name(self, ""),
|
||||
ItemName::Component(name) => modular::modular_name(self, name),
|
||||
pub fn name(&self) -> Cow<str> {
|
||||
match &self.item_base {
|
||||
ItemBase::Raw(item_def) => Cow::Borrowed(&item_def.name),
|
||||
ItemBase::Modular(mod_base) => mod_base.generate_name(self.components()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn description(&self) -> &str { &self.item_def.description }
|
||||
pub fn description(&self) -> &str {
|
||||
match &self.item_base {
|
||||
ItemBase::Raw(item_def) => &item_def.description,
|
||||
// TODO: See if James wanted to make description, else leave with none
|
||||
ItemBase::Modular(_) => "",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> &ItemKind { &self.item_def.kind }
|
||||
pub fn kind(&self) -> Cow<ItemKind> {
|
||||
// TODO: Try to move further upward
|
||||
let msm = MaterialStatManifest::default();
|
||||
match &self.item_base {
|
||||
ItemBase::Raw(item_def) => Cow::Borrowed(&item_def.kind),
|
||||
ItemBase::Modular(mod_base) => mod_base.kind(self.components(), &msm),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn amount(&self) -> u32 { u32::from(self.amount) }
|
||||
|
||||
pub fn is_stackable(&self) -> bool {
|
||||
match &self.item_base {
|
||||
ItemBase::Raw(item_def) => item_def.is_stackable(),
|
||||
// TODO: Let whoever implements stackable modular items deal with this
|
||||
ItemBase::Modular(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_slots(&self) -> u16 { self.item_base.num_slots() }
|
||||
|
||||
/// NOTE: invariant that amount() ≤ max_amount(), 1 ≤ max_amount(),
|
||||
/// and if !self.is_stackable(), self.max_amount() = 1.
|
||||
pub fn max_amount(&self) -> u32 { if self.is_stackable() { u32::MAX } else { 1 } }
|
||||
|
||||
pub fn quality(&self) -> Quality {
|
||||
match self.item_def.quality {
|
||||
QualityKind::Direct(quality) => quality,
|
||||
QualityKind::Modular => modular::resolve_quality(self),
|
||||
match &self.item_base {
|
||||
ItemBase::Raw(item_def) => item_def.quality,
|
||||
ItemBase::Modular(mod_base) => mod_base.compute_quality(self.components()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -916,7 +951,27 @@ impl Item {
|
||||
block.get_sprite()?.collectible_id()?.to_item()
|
||||
}
|
||||
|
||||
pub fn ability_spec(&self) -> Option<&AbilitySpec> { self.item_def.ability_spec.as_ref() }
|
||||
pub fn ability_spec(&self) -> Option<Cow<AbilitySpec>> {
|
||||
match &self.item_base {
|
||||
ItemBase::Raw(item_def) => item_def.ability_spec.as_ref().map(Cow::Borrowed),
|
||||
ItemBase::Modular(mod_base) => mod_base.ability_spec(self.components()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tags(&self) -> &[ItemTag] {
|
||||
match &self.item_base {
|
||||
ItemBase::Raw(item_def) => &item_def.tags,
|
||||
// TODO: Do this properly. It'll probably be important at some point.
|
||||
ItemBase::Modular(_) => &[],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_modular(&self) -> bool {
|
||||
match &self.item_base {
|
||||
ItemBase::Raw(_) => false,
|
||||
ItemBase::Modular(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn item_hash(&self) -> u64 { self.hash }
|
||||
|
||||
@ -935,19 +990,21 @@ impl Item {
|
||||
/// for either an `Item` containing the definition, or the actual `ItemDef`
|
||||
pub trait ItemDesc {
|
||||
fn description(&self) -> &str;
|
||||
fn name(&self) -> Cow<'_, str>;
|
||||
fn kind(&self) -> &ItemKind;
|
||||
fn name(&self) -> Cow<str>;
|
||||
fn kind(&self) -> Cow<ItemKind>;
|
||||
fn quality(&self) -> Quality;
|
||||
fn num_slots(&self) -> u16;
|
||||
fn item_definition_id(&self) -> &str;
|
||||
fn tags(&self) -> &[ItemTag];
|
||||
fn concrete_item(&self) -> Option<&Item>;
|
||||
|
||||
fn is_modular(&self) -> bool;
|
||||
|
||||
fn components(&self) -> &[Item] { self.concrete_item().map_or(&[], |i| i.components()) }
|
||||
|
||||
fn tool(&self) -> Option<&Tool> {
|
||||
if let ItemKind::Tool(tool) = self.kind() {
|
||||
Some(tool)
|
||||
fn tool_info(&self) -> Option<ToolKind> {
|
||||
if let ItemKind::Tool(tool) = &*self.kind() {
|
||||
Some(tool.kind)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -955,48 +1012,33 @@ pub trait ItemDesc {
|
||||
}
|
||||
|
||||
impl ItemDesc for Item {
|
||||
fn description(&self) -> &str { &self.item_def.description }
|
||||
fn description(&self) -> &str { self.description() }
|
||||
|
||||
fn name(&self) -> Cow<'_, str> { self.name() }
|
||||
fn name(&self) -> Cow<str> { self.name() }
|
||||
|
||||
fn kind(&self) -> &ItemKind { &self.item_def.kind }
|
||||
fn kind(&self) -> Cow<ItemKind> { self.kind() }
|
||||
|
||||
fn quality(&self) -> Quality { self.quality() }
|
||||
|
||||
fn num_slots(&self) -> u16 { self.item_def.slots }
|
||||
fn num_slots(&self) -> u16 { self.num_slots() }
|
||||
|
||||
fn item_definition_id(&self) -> &str { &self.item_def.item_definition_id }
|
||||
fn item_definition_id(&self) -> &str { self.item_definition_id() }
|
||||
|
||||
fn tags(&self) -> &[ItemTag] { &self.item_def.tags }
|
||||
fn tags(&self) -> &[ItemTag] { self.tags() }
|
||||
|
||||
fn concrete_item(&self) -> Option<&Item> { Some(self) }
|
||||
|
||||
fn is_modular(&self) -> bool { self.is_modular() }
|
||||
}
|
||||
|
||||
impl ItemDesc for ItemDef {
|
||||
fn description(&self) -> &str { &self.description }
|
||||
|
||||
fn name(&self) -> Cow<'_, str> {
|
||||
match &self.name {
|
||||
ItemName::Direct(name) | ItemName::Component(name) => Cow::Borrowed(name),
|
||||
ItemName::Modular => {
|
||||
let toolkind = if let ItemKind::Tool(tool) = &self.kind {
|
||||
tool.kind.identifier_name()
|
||||
} else {
|
||||
"Weapon"
|
||||
};
|
||||
Cow::Owned(format!("Modular {}", toolkind))
|
||||
},
|
||||
}
|
||||
}
|
||||
fn name(&self) -> Cow<str> { Cow::Borrowed(&self.name) }
|
||||
|
||||
fn kind(&self) -> &ItemKind { &self.kind }
|
||||
fn kind(&self) -> Cow<ItemKind> { Cow::Borrowed(&self.kind) }
|
||||
|
||||
fn quality(&self) -> Quality {
|
||||
match &self.quality {
|
||||
QualityKind::Direct(quality) => *quality,
|
||||
QualityKind::Modular => Quality::Common,
|
||||
}
|
||||
}
|
||||
fn quality(&self) -> Quality { self.quality }
|
||||
|
||||
fn num_slots(&self) -> u16 { self.slots }
|
||||
|
||||
@ -1005,6 +1047,8 @@ impl ItemDesc for ItemDef {
|
||||
fn tags(&self) -> &[ItemTag] { &self.tags }
|
||||
|
||||
fn concrete_item(&self) -> Option<&Item> { None }
|
||||
|
||||
fn is_modular(&self) -> bool { false }
|
||||
}
|
||||
|
||||
impl Component for Item {
|
||||
@ -1021,9 +1065,9 @@ impl Component for ItemDrop {
|
||||
impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T {
|
||||
fn description(&self) -> &str { (*self).description() }
|
||||
|
||||
fn name(&self) -> Cow<'_, str> { (*self).name() }
|
||||
fn name(&self) -> Cow<str> { (*self).name() }
|
||||
|
||||
fn kind(&self) -> &ItemKind { (*self).kind() }
|
||||
fn kind(&self) -> Cow<ItemKind> { (*self).kind() }
|
||||
|
||||
fn quality(&self) -> Quality { (*self).quality() }
|
||||
|
||||
@ -1036,6 +1080,8 @@ impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T {
|
||||
fn tags(&self) -> &[ItemTag] { (*self).tags() }
|
||||
|
||||
fn concrete_item(&self) -> Option<&Item> { None }
|
||||
|
||||
fn is_modular(&self) -> bool { (*self).is_modular() }
|
||||
}
|
||||
|
||||
/// Returns all item asset specifiers
|
||||
|
@ -1,13 +1,136 @@
|
||||
use super::{
|
||||
tool::{self, Hands},
|
||||
Item, ItemDesc, ItemKind, ItemName, RawItemDef, ToolKind,
|
||||
tool::{self, AbilitySpec, Hands, MaterialStatManifest, Stats},
|
||||
Item, ItemBase, ItemDesc, ItemKind, Quality, ToolKind,
|
||||
};
|
||||
use crate::{assets::AssetExt, lottery::Lottery, recipe};
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum ModularBase {
|
||||
Tool(ToolKind),
|
||||
}
|
||||
|
||||
impl ModularBase {
|
||||
pub(super) fn duplicate(&self) -> Self {
|
||||
match self {
|
||||
ModularBase::Tool(toolkind) => ModularBase::Tool(*toolkind),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kind(&self, components: &[Item], msm: &MaterialStatManifest) -> Cow<ItemKind> {
|
||||
fn resolve_hands(components: &[Item]) -> Hands {
|
||||
// Checks if weapon has components that restrict hands to two. Restrictions to
|
||||
// one hand or no restrictions default to one-handed weapon.
|
||||
let is_two_handed = components.iter().any(|item| matches!(&*item.kind(), ItemKind::ModularComponent(mc) if matches!(mc.hand_restriction, Some(Hands::Two))));
|
||||
// If weapon is two handed, make it two handed
|
||||
if is_two_handed {
|
||||
Hands::Two
|
||||
} else {
|
||||
Hands::One
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_stats(components: &[Item], msm: &MaterialStatManifest) -> Stats {
|
||||
let mut stats = Stats::one();
|
||||
let mut material_multipliers: Vec<Stats> = Vec::new();
|
||||
for item in components.iter() {
|
||||
match &*item.kind() {
|
||||
// Modular components directly multiply against the base stats
|
||||
ItemKind::ModularComponent(mc) => {
|
||||
let inner_stats = mc.stats * resolve_stats(item.components(), msm);
|
||||
stats *= inner_stats;
|
||||
},
|
||||
// Ingredients push multiplier to vec as the ingredient multipliers are averaged
|
||||
ItemKind::Ingredient { .. } => {
|
||||
if let Some(mult_stats) = msm.0.get(item.item_definition_id()) {
|
||||
material_multipliers.push(*mult_stats);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// Take the average of the material multipliers
|
||||
if !material_multipliers.is_empty() {
|
||||
let mut average_mult = Stats::zero();
|
||||
for stat in material_multipliers.iter() {
|
||||
average_mult += *stat;
|
||||
}
|
||||
average_mult /= material_multipliers.len();
|
||||
stats *= average_mult;
|
||||
}
|
||||
stats
|
||||
}
|
||||
|
||||
match self {
|
||||
ModularBase::Tool(toolkind) => Cow::Owned(ItemKind::Tool(tool::Tool {
|
||||
kind: *toolkind,
|
||||
hands: resolve_hands(components),
|
||||
stats: resolve_stats(components, msm),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
/// Modular weapons are named as "{Material} {Weapon}" where {Weapon} is
|
||||
/// from the damage component used and {Material} is from the material
|
||||
/// the damage component is created from.
|
||||
pub fn generate_name(&self, components: &[Item]) -> Cow<str> {
|
||||
match self {
|
||||
ModularBase::Tool(toolkind) => {
|
||||
// Closure to get material name from an item
|
||||
fn material_name(component: &Item) -> String {
|
||||
component
|
||||
.components()
|
||||
.iter()
|
||||
.filter_map(|comp| match &*comp.kind() {
|
||||
ItemKind::Ingredient { descriptor, .. } => Some(descriptor.to_owned()),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.unwrap_or_else(|| "Modular".to_owned())
|
||||
}
|
||||
|
||||
let main_component = components.iter().find(|comp| {
|
||||
matches!(&*comp.kind(), ItemKind::ModularComponent(ModularComponent { modkind, .. })
|
||||
if *modkind == ModularComponentKind::main_component(*toolkind)
|
||||
)
|
||||
});
|
||||
let (material_name, weapon_name) = if let Some(component) = main_component {
|
||||
let material_name = material_name(component);
|
||||
let weapon_name = if let ItemKind::ModularComponent(ModularComponent {
|
||||
weapon_name,
|
||||
..
|
||||
}) = &*component.kind()
|
||||
{
|
||||
weapon_name.to_owned()
|
||||
} else {
|
||||
toolkind.identifier_name().to_owned()
|
||||
};
|
||||
(material_name, weapon_name)
|
||||
} else {
|
||||
("Modular".to_owned(), toolkind.identifier_name().to_owned())
|
||||
};
|
||||
|
||||
Cow::Owned(format!("{} {}", material_name, weapon_name))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_quality(&self, components: &[Item]) -> Quality {
|
||||
components
|
||||
.iter()
|
||||
.fold(Quality::Low, |a, b| a.max(b.quality()))
|
||||
}
|
||||
|
||||
pub fn ability_spec(&self, _components: &[Item]) -> Option<Cow<AbilitySpec>> {
|
||||
match self {
|
||||
ModularBase::Tool(toolkind) => Some(Cow::Owned(AbilitySpec::Tool(*toolkind))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Look into changing to: Primary, Secondary
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum ModularComponentKind {
|
||||
Damage,
|
||||
@ -53,106 +176,8 @@ const SUPPORTED_TOOLKINDS: [ToolKind; 6] = [
|
||||
|
||||
const WEAPON_PREFIX: &str = "common.items.weapons.modular";
|
||||
|
||||
fn make_weapon_def(toolkind: ToolKind) -> (String, RawItemDef) {
|
||||
let identifier = format!("{}.{}", WEAPON_PREFIX, toolkind.identifier_name());
|
||||
let name = ItemName::Modular;
|
||||
let tool = tool::Tool {
|
||||
kind: toolkind,
|
||||
hands: tool::HandsKind::Modular,
|
||||
stats: tool::StatKind::Modular,
|
||||
};
|
||||
let kind = ItemKind::Tool(tool);
|
||||
let item = RawItemDef {
|
||||
name,
|
||||
description: "".to_string(),
|
||||
kind,
|
||||
quality: super::QualityKind::Modular,
|
||||
tags: Vec::new(),
|
||||
slots: 0,
|
||||
ability_spec: None,
|
||||
};
|
||||
(identifier, item)
|
||||
}
|
||||
|
||||
fn initialize_modular_assets() -> HashMap<String, RawItemDef> {
|
||||
let mut itemdefs = HashMap::new();
|
||||
for &toolkind in &SUPPORTED_TOOLKINDS {
|
||||
let (identifier, item) = make_weapon_def(toolkind);
|
||||
itemdefs.insert(identifier, item);
|
||||
}
|
||||
itemdefs
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref ITEM_DEFS: HashMap<String, RawItemDef> = initialize_modular_assets();
|
||||
}
|
||||
|
||||
/// Synthesize modular assets programmatically, to allow for the following:
|
||||
/// - Allow the modular tag_examples to auto-update with the list of applicable
|
||||
/// components
|
||||
pub(super) fn synthesize_modular_asset(specifier: &str) -> Option<RawItemDef> {
|
||||
let ret = ITEM_DEFS.get(specifier).cloned();
|
||||
tracing::trace!("synthesize_modular_asset({:?}) -> {:?}", specifier, ret);
|
||||
ret
|
||||
}
|
||||
|
||||
/// Modular weapons are named as "{Material} {Weapon}" where {Weapon} is from
|
||||
/// the damage component used and {Material} is from the material the damage
|
||||
/// component is created from.
|
||||
pub(super) fn modular_name<'a>(item: &'a Item, arg1: &'a str) -> Cow<'a, str> {
|
||||
// Closure to get material name from an item
|
||||
let material_name = |component: &Item| {
|
||||
component
|
||||
.components()
|
||||
.iter()
|
||||
.filter_map(|comp| match comp.kind() {
|
||||
ItemKind::Ingredient { descriptor, .. } => Some(descriptor.to_owned()),
|
||||
_ => None,
|
||||
})
|
||||
.last()
|
||||
.unwrap_or_else(|| "Modular".to_owned())
|
||||
};
|
||||
|
||||
match item.kind() {
|
||||
ItemKind::Tool(tool) => {
|
||||
let main_components = item.components().iter().filter(|comp| {
|
||||
matches!(comp.kind(), ItemKind::ModularComponent(ModularComponent { modkind, .. })
|
||||
if *modkind == ModularComponentKind::main_component(tool.kind)
|
||||
)
|
||||
});
|
||||
// Last fine as there should only ever be one damage component on a weapon
|
||||
let (material_name, weapon_name) = if let Some(component) = main_components.last() {
|
||||
let material_name = material_name(component);
|
||||
let weapon_name =
|
||||
if let ItemKind::ModularComponent(ModularComponent { weapon_name, .. }) =
|
||||
component.kind()
|
||||
{
|
||||
weapon_name
|
||||
} else {
|
||||
tool.kind.identifier_name()
|
||||
};
|
||||
(material_name, weapon_name)
|
||||
} else {
|
||||
("Modular".to_owned(), tool.kind.identifier_name())
|
||||
};
|
||||
|
||||
Cow::Owned(format!("{} {}", material_name, weapon_name))
|
||||
},
|
||||
ItemKind::ModularComponent(comp) => match comp.modkind {
|
||||
ModularComponentKind::Damage => {
|
||||
let material_name = material_name(item);
|
||||
Cow::Owned(format!("{} {}", material_name, arg1))
|
||||
},
|
||||
ModularComponentKind::Held => Cow::Borrowed(arg1),
|
||||
},
|
||||
_ => Cow::Borrowed("Modular Item"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn resolve_quality(item: &Item) -> super::Quality {
|
||||
item.components
|
||||
.iter()
|
||||
.fold(super::Quality::Low, |a, b| a.max(b.quality()))
|
||||
fn make_weapon_id(toolkind: ToolKind) -> String {
|
||||
format!("{}.{}", WEAPON_PREFIX, toolkind.identifier_name())
|
||||
}
|
||||
|
||||
/// Returns directory that contains components for a particular combination of
|
||||
@ -167,18 +192,14 @@ fn make_mod_comp_dir_spec(tool: ToolKind, mod_kind: ModularComponentKind) -> Str
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates initial item for a modular weapon
|
||||
pub fn initialize_modular_weapon(toolkind: ToolKind) -> Item {
|
||||
Item::new_from_asset_expect(&make_weapon_def(toolkind).0)
|
||||
}
|
||||
|
||||
/// Creates a random modular weapon when provided with a toolkind, material, and
|
||||
/// optionally the handedness
|
||||
pub fn random_weapon(tool: ToolKind, material: super::Material, hands: Option<Hands>) -> Item {
|
||||
// Returns inner modular component of an item if it has one
|
||||
fn unwrap_modular_component(item: &Item) -> Option<&ModularComponent> {
|
||||
if let ItemKind::ModularComponent(mod_comp) = item.kind() {
|
||||
Some(mod_comp)
|
||||
fn unwrap_modular_component(item: &Item) -> Option<ModularComponent> {
|
||||
if let ItemKind::ModularComponent(mod_comp) = &*item.kind() {
|
||||
// TODO: Maybe get rid of clone?
|
||||
Some(mod_comp.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -188,9 +209,6 @@ pub fn random_weapon(tool: ToolKind, material: super::Material, hands: Option<Ha
|
||||
let ability_map = Default::default();
|
||||
let msm = Default::default();
|
||||
|
||||
// Initialize modular weapon
|
||||
let mut modular_weapon = initialize_modular_weapon(tool);
|
||||
|
||||
// Load recipe book (done to check that material is valid for a particular
|
||||
// component)
|
||||
let recipe::RawRecipeBook(recipes) =
|
||||
@ -236,7 +254,7 @@ pub fn random_weapon(tool: ToolKind, material: super::Material, hands: Option<Ha
|
||||
})
|
||||
// Filter by if component does not have a material, or if material can be used in the modular component
|
||||
.filter(|item| {
|
||||
matches!(unwrap_modular_component(item), Some(ModularComponent { modkind, .. }) if *modkind != material_comp)
|
||||
matches!(unwrap_modular_component(item), Some(ModularComponent { modkind, .. }) if modkind != material_comp)
|
||||
|| is_composed_of(item.item_definition_id())
|
||||
})
|
||||
.map(|item| (1.0, item))
|
||||
@ -272,12 +290,14 @@ pub fn random_weapon(tool: ToolKind, material: super::Material, hands: Option<Ha
|
||||
},
|
||||
}
|
||||
|
||||
// Insert components onto modular weapon
|
||||
modular_weapon.add_component(damage_component, &ability_map, &msm);
|
||||
modular_weapon.add_component(held_component, &ability_map, &msm);
|
||||
|
||||
// Returns fully created modular weapon
|
||||
modular_weapon
|
||||
// Create modular weapon
|
||||
let components = vec![damage_component, held_component];
|
||||
Item::new_from_item_base(
|
||||
ItemBase::Modular(ModularBase::Tool(tool)),
|
||||
&components,
|
||||
&ability_map,
|
||||
&msm,
|
||||
)
|
||||
}
|
||||
|
||||
/// This is used as a key to uniquely identify the modular weapon in asset
|
||||
@ -285,14 +305,14 @@ pub fn random_weapon(tool: ToolKind, material: super::Material, hands: Option<Ha
|
||||
pub type ModularWeaponKey = (String, String, Hands);
|
||||
|
||||
pub fn weapon_to_key(mod_weap: &dyn ItemDesc) -> ModularWeaponKey {
|
||||
let hands = if let ItemKind::Tool(tool) = mod_weap.kind() {
|
||||
tool.hands.resolve_hands(mod_weap.components())
|
||||
let hands = if let ItemKind::Tool(tool) = &*mod_weap.kind() {
|
||||
tool.hands
|
||||
} else {
|
||||
Hands::One
|
||||
};
|
||||
|
||||
let (main_comp, material) = if let Some(main_comp) = mod_weap.components().iter().find(|comp| {
|
||||
matches!(comp.kind(), ItemKind::ModularComponent(mod_comp) if ModularComponentKind::main_component(mod_comp.toolkind) == mod_comp.modkind)
|
||||
matches!(&*comp.kind(), ItemKind::ModularComponent(mod_comp) if ModularComponentKind::main_component(mod_comp.toolkind) == mod_comp.modkind)
|
||||
}) {
|
||||
let material = if let Some(material) = main_comp.components().iter().filter_map(|mat| {
|
||||
if let Some(super::ItemTag::Material(material)) = mat.tags().iter().find(|tag| matches!(tag, super::ItemTag::Material(_))) {
|
||||
@ -326,7 +346,7 @@ pub enum ModularWeaponComponentKeyError {
|
||||
pub fn weapon_component_to_key(
|
||||
mod_weap_comp: &dyn ItemDesc,
|
||||
) -> Result<ModularWeaponComponentKey, ModularWeaponComponentKeyError> {
|
||||
if let ItemKind::ModularComponent(mod_comp) = mod_weap_comp.kind() {
|
||||
if let ItemKind::ModularComponent(mod_comp) = &*mod_weap_comp.kind() {
|
||||
if ModularComponentKind::main_component(mod_comp.toolkind) == mod_comp.modkind {
|
||||
let material = if let Some(material) = mod_weap_comp
|
||||
.components()
|
||||
|
@ -3,12 +3,12 @@
|
||||
|
||||
use crate::{
|
||||
assets::{self, Asset, AssetExt},
|
||||
comp::{item::ItemKind, skills::Skill, CharacterAbility, Item},
|
||||
comp::{skills::Skill, CharacterAbility},
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
ops::{AddAssign, DivAssign, MulAssign, Sub},
|
||||
ops::{AddAssign, DivAssign, Mul, MulAssign, Sub},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
@ -75,30 +75,16 @@ impl ToolKind {
|
||||
| ToolKind::Shield
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum HandsKind {
|
||||
Direct(Hands),
|
||||
Modular,
|
||||
}
|
||||
|
||||
impl HandsKind {
|
||||
pub fn resolve_hands(&self, components: &[Item]) -> Hands {
|
||||
match self {
|
||||
HandsKind::Direct(hands) => *hands,
|
||||
HandsKind::Modular => {
|
||||
// Checks if weapon has components that restrict hands to two. Restrictions to
|
||||
// one hand or no restrictions default to one-handed weapon.
|
||||
let is_two_handed = components.iter().any(|item| matches!(item.kind(), ItemKind::ModularComponent(mc) if matches!(mc.hand_restriction, Some(Hands::Two))));
|
||||
// If weapon is two handed, make it two handed
|
||||
if is_two_handed {
|
||||
Hands::Two
|
||||
} else {
|
||||
Hands::One
|
||||
}
|
||||
},
|
||||
}
|
||||
pub fn can_block(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
ToolKind::Sword
|
||||
| ToolKind::Axe
|
||||
| ToolKind::Hammer
|
||||
| ToolKind::Shield
|
||||
| ToolKind::Dagger
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,16 +94,6 @@ pub enum Hands {
|
||||
Two,
|
||||
}
|
||||
|
||||
impl Hands {
|
||||
// Changing this will break persistence of modular weapons
|
||||
pub fn identifier_name(&self) -> &'static str {
|
||||
match self {
|
||||
Hands::One => "one-handed",
|
||||
Hands::Two => "two-handed",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Stats {
|
||||
pub equip_time_secs: f32,
|
||||
@ -131,7 +107,7 @@ pub struct Stats {
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
pub fn zeroed() -> Stats {
|
||||
pub fn zero() -> Stats {
|
||||
Stats {
|
||||
equip_time_secs: 0.0,
|
||||
power: 0.0,
|
||||
@ -156,14 +132,6 @@ impl Stats {
|
||||
buff_strength: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn clamp_speed(mut self) -> Self {
|
||||
// if a tool has 0.0 speed, that panics due to being infinite duration, so
|
||||
// enforce speed >= 0.1 on the final product (but not the intermediates)
|
||||
self.speed = self.speed.max(0.1);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Asset for Stats {
|
||||
@ -180,6 +148,7 @@ impl AddAssign<Stats> for Stats {
|
||||
self.speed += other.speed;
|
||||
self.crit_chance += other.crit_chance;
|
||||
self.range += other.range;
|
||||
self.energy_efficiency += other.energy_efficiency;
|
||||
self.buff_strength += other.buff_strength;
|
||||
}
|
||||
}
|
||||
@ -191,20 +160,35 @@ impl MulAssign<Stats> for Stats {
|
||||
self.speed *= other.speed;
|
||||
self.crit_chance *= other.crit_chance;
|
||||
self.range *= other.range;
|
||||
self.energy_efficiency *= other.energy_efficiency;
|
||||
self.buff_strength *= other.buff_strength;
|
||||
}
|
||||
}
|
||||
impl Mul<Stats> for Stats {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, other: Self) -> Self {
|
||||
Self {
|
||||
equip_time_secs: self.equip_time_secs * other.equip_time_secs,
|
||||
power: self.power * other.power,
|
||||
effect_power: self.effect_power * other.effect_power,
|
||||
speed: self.speed * other.speed,
|
||||
crit_chance: self.crit_chance * other.crit_chance,
|
||||
range: self.range * other.range,
|
||||
energy_efficiency: self.energy_efficiency * other.energy_efficiency,
|
||||
buff_strength: self.buff_strength * other.buff_strength,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl DivAssign<usize> for Stats {
|
||||
fn div_assign(&mut self, scalar: usize) {
|
||||
self.equip_time_secs /= scalar as f32;
|
||||
// since averaging occurs when the stats are used multiplicatively, don't permit
|
||||
// multiplying an equip_time_secs by 0, since that would be overpowered
|
||||
self.equip_time_secs = self.equip_time_secs.max(0.001);
|
||||
self.power /= scalar as f32;
|
||||
self.effect_power /= scalar as f32;
|
||||
self.speed /= scalar as f32;
|
||||
self.crit_chance /= scalar as f32;
|
||||
self.range /= scalar as f32;
|
||||
self.energy_efficiency /= scalar as f32;
|
||||
self.buff_strength /= scalar as f32;
|
||||
}
|
||||
}
|
||||
@ -244,80 +228,24 @@ impl Default for MaterialStatManifest {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum StatKind {
|
||||
Direct(Stats),
|
||||
Modular,
|
||||
}
|
||||
|
||||
impl StatKind {
|
||||
pub fn resolve_stats(&self, msm: &MaterialStatManifest, components: &[Item]) -> Stats {
|
||||
let mut stats = match self {
|
||||
StatKind::Direct(stats) => *stats,
|
||||
StatKind::Modular => Stats::one(),
|
||||
};
|
||||
let mut multipliers: Vec<Stats> = Vec::new();
|
||||
for item in components.iter() {
|
||||
match item.kind() {
|
||||
// Modular components directly multiply against the base stats
|
||||
ItemKind::ModularComponent(mc) => {
|
||||
let inner_stats =
|
||||
StatKind::Direct(mc.stats).resolve_stats(msm, item.components());
|
||||
stats *= inner_stats;
|
||||
},
|
||||
// Ingredients push multiplier to vec as the ingredient multipliers are averaged
|
||||
ItemKind::Ingredient { .. } => {
|
||||
if let Some(mult_stats) = msm.0.get(item.item_definition_id()) {
|
||||
multipliers.push(*mult_stats);
|
||||
}
|
||||
},
|
||||
// TODO: add stats from enhancement slots
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
// Take the average of the material multipliers
|
||||
if !multipliers.is_empty() {
|
||||
let mut average_mult = Stats::zeroed();
|
||||
for stat in multipliers.iter() {
|
||||
average_mult += *stat;
|
||||
}
|
||||
average_mult /= multipliers.len();
|
||||
stats *= average_mult;
|
||||
}
|
||||
stats
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&MaterialStatManifest, &[Item], &Tool)> for Stats {
|
||||
fn from((msm, components, tool): (&MaterialStatManifest, &[Item], &Tool)) -> Self {
|
||||
tool.stats.resolve_stats(msm, components).clamp_speed()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Tool {
|
||||
pub kind: ToolKind,
|
||||
pub hands: HandsKind,
|
||||
pub stats: StatKind,
|
||||
pub hands: Hands,
|
||||
pub stats: Stats,
|
||||
// TODO: item specific abilities
|
||||
}
|
||||
|
||||
impl Tool {
|
||||
// DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING
|
||||
// Added for CSV import of stats
|
||||
pub fn new(kind: ToolKind, hands: Hands, stats: Stats) -> Self {
|
||||
Self {
|
||||
kind,
|
||||
hands: HandsKind::Direct(hands),
|
||||
stats: StatKind::Direct(stats),
|
||||
}
|
||||
}
|
||||
pub fn new(kind: ToolKind, hands: Hands, stats: Stats) -> Self { Self { kind, hands, stats } }
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
kind: ToolKind::Empty,
|
||||
hands: HandsKind::Direct(Hands::One),
|
||||
stats: StatKind::Direct(Stats {
|
||||
hands: Hands::One,
|
||||
stats: Stats {
|
||||
equip_time_secs: 0.0,
|
||||
power: 1.00,
|
||||
effect_power: 1.00,
|
||||
@ -326,56 +254,30 @@ impl Tool {
|
||||
range: 1.0,
|
||||
energy_efficiency: 1.0,
|
||||
buff_strength: 1.0,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Keep power between 0.5 and 2.00
|
||||
pub fn base_power(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 {
|
||||
self.stats.resolve_stats(msm, components).power
|
||||
pub fn base_power(&self) -> f32 { self.stats.power }
|
||||
|
||||
pub fn base_effect_power(&self) -> f32 { self.stats.effect_power }
|
||||
|
||||
pub fn base_speed(&self) -> f32 {
|
||||
// Has floor to prevent infinite durations being created later down due to a
|
||||
// divide by zero
|
||||
self.stats.speed.max(0.1)
|
||||
}
|
||||
|
||||
pub fn base_effect_power(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 {
|
||||
self.stats.resolve_stats(msm, components).effect_power
|
||||
}
|
||||
pub fn base_crit_chance(&self) -> f32 { self.stats.crit_chance }
|
||||
|
||||
pub fn base_speed(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 {
|
||||
self.stats
|
||||
.resolve_stats(msm, components)
|
||||
.clamp_speed()
|
||||
.speed
|
||||
}
|
||||
pub fn base_range(&self) -> f32 { self.stats.range }
|
||||
|
||||
pub fn base_crit_chance(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 {
|
||||
self.stats.resolve_stats(msm, components).crit_chance
|
||||
}
|
||||
pub fn base_energy_efficiency(&self) -> f32 { self.stats.energy_efficiency }
|
||||
|
||||
pub fn base_range(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 {
|
||||
self.stats.resolve_stats(msm, components).range
|
||||
}
|
||||
pub fn base_buff_strength(&self) -> f32 { self.stats.buff_strength }
|
||||
|
||||
pub fn base_energy_efficiency(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 {
|
||||
self.stats.resolve_stats(msm, components).energy_efficiency
|
||||
}
|
||||
|
||||
pub fn base_buff_strength(&self, msm: &MaterialStatManifest, components: &[Item]) -> f32 {
|
||||
self.stats.resolve_stats(msm, components).buff_strength
|
||||
}
|
||||
|
||||
pub fn equip_time(&self, msm: &MaterialStatManifest, components: &[Item]) -> Duration {
|
||||
Duration::from_secs_f32(self.stats.resolve_stats(msm, components).equip_time_secs)
|
||||
}
|
||||
|
||||
pub fn can_block(&self) -> bool {
|
||||
matches!(
|
||||
self.kind,
|
||||
ToolKind::Sword
|
||||
| ToolKind::Axe
|
||||
| ToolKind::Hammer
|
||||
| ToolKind::Shield
|
||||
| ToolKind::Dagger
|
||||
)
|
||||
}
|
||||
pub fn equip_time(&self) -> Duration { Duration::from_secs_f32(self.stats.equip_time_secs) }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@ -387,16 +289,10 @@ pub struct AbilitySet<T> {
|
||||
|
||||
impl AbilitySet<AbilityItem> {
|
||||
#[must_use]
|
||||
pub fn modified_by_tool(
|
||||
self,
|
||||
tool: &Tool,
|
||||
msm: &MaterialStatManifest,
|
||||
components: &[Item],
|
||||
) -> Self {
|
||||
let stats = Stats::from((msm, components, tool));
|
||||
pub fn modified_by_tool(self, tool: &Tool) -> Self {
|
||||
self.map(|a| AbilityItem {
|
||||
id: a.id,
|
||||
ability: a.ability.adjusted_by_stats(stats),
|
||||
ability: a.ability.adjusted_by_stats(tool.stats),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::comp::{
|
||||
inventory::{
|
||||
item::{Hands, ItemKind},
|
||||
item::{tool::Tool, Hands, ItemKind},
|
||||
slot::{ArmorSlot, EquipSlot},
|
||||
InvSlot,
|
||||
},
|
||||
@ -168,9 +168,13 @@ impl Loadout {
|
||||
assert_eq!(self.swap(equip_slot_a, item_b), None);
|
||||
|
||||
// Check if items are valid in their new positions
|
||||
if !self.slot_can_hold(equip_slot_a, self.equipped(equip_slot_a))
|
||||
|| !self.slot_can_hold(equip_slot_b, self.equipped(equip_slot_b))
|
||||
{
|
||||
if !self.slot_can_hold(
|
||||
equip_slot_a,
|
||||
self.equipped(equip_slot_a).map(|x| x.kind()).as_deref(),
|
||||
) || !self.slot_can_hold(
|
||||
equip_slot_b,
|
||||
self.equipped(equip_slot_b).map(|x| x.kind()).as_deref(),
|
||||
) {
|
||||
// If not, revert the swap
|
||||
let item_a = self.swap(equip_slot_a, None);
|
||||
let item_b = self.swap(equip_slot_b, item_a);
|
||||
@ -183,11 +187,11 @@ impl Loadout {
|
||||
/// returned, or if there are no free slots then the first occupied slot
|
||||
/// will be returned. The bool part of the tuple indicates whether an item
|
||||
/// is already equipped in the slot.
|
||||
pub(super) fn get_slot_to_equip_into(&self, item: &Item) -> Option<EquipSlot> {
|
||||
pub(super) fn get_slot_to_equip_into(&self, item_kind: &ItemKind) -> Option<EquipSlot> {
|
||||
let mut suitable_slots = self
|
||||
.slots
|
||||
.iter()
|
||||
.filter(|s| self.slot_can_hold(s.equip_slot, Some(item)));
|
||||
.filter(|s| self.slot_can_hold(s.equip_slot, Some(item_kind)));
|
||||
|
||||
let first = suitable_slots.next();
|
||||
|
||||
@ -199,14 +203,15 @@ impl Loadout {
|
||||
.or_else(|| first.map(|x| x.equip_slot))
|
||||
}
|
||||
|
||||
/// Returns all items currently equipped that an item could replace
|
||||
pub(super) fn equipped_items_replaceable_by<'a>(
|
||||
/// Returns all items currently equipped that an item of the given ItemKind
|
||||
/// could replace
|
||||
pub(super) fn equipped_items_of_kind<'a>(
|
||||
&'a self,
|
||||
item: &'a Item,
|
||||
item_kind: &'a ItemKind,
|
||||
) -> impl Iterator<Item = &'a Item> {
|
||||
self.slots
|
||||
.iter()
|
||||
.filter(move |s| self.slot_can_hold(s.equip_slot, Some(item)))
|
||||
.filter(move |s| self.slot_can_hold(s.equip_slot, Some(item_kind)))
|
||||
.filter_map(|s| s.slot.as_ref())
|
||||
}
|
||||
|
||||
@ -288,7 +293,7 @@ impl Loadout {
|
||||
let loadout_slot = self
|
||||
.slots
|
||||
.iter()
|
||||
.find(|s| s.slot.is_none() && self.slot_can_hold(s.equip_slot, Some(&item)))
|
||||
.find(|s| s.slot.is_none() && self.slot_can_hold(s.equip_slot, Some(&*item.kind())))
|
||||
.map(|s| s.equip_slot);
|
||||
if let Some(slot) = self
|
||||
.slots
|
||||
@ -307,55 +312,53 @@ impl Loadout {
|
||||
}
|
||||
|
||||
/// Checks that a slot can hold a given item
|
||||
pub(super) fn slot_can_hold(&self, equip_slot: EquipSlot, item: Option<&Item>) -> bool {
|
||||
pub(super) fn slot_can_hold(
|
||||
&self,
|
||||
equip_slot: EquipSlot,
|
||||
item_kind: Option<&ItemKind>,
|
||||
) -> bool {
|
||||
// Disallow equipping incompatible weapon pairs (i.e a two-handed weapon and a
|
||||
// one-handed weapon)
|
||||
if !(match equip_slot {
|
||||
EquipSlot::ActiveMainhand => {
|
||||
Loadout::is_valid_weapon_pair(item, self.equipped(EquipSlot::ActiveOffhand))
|
||||
},
|
||||
EquipSlot::ActiveOffhand => {
|
||||
Loadout::is_valid_weapon_pair(self.equipped(EquipSlot::ActiveMainhand), item)
|
||||
},
|
||||
EquipSlot::InactiveMainhand => {
|
||||
Loadout::is_valid_weapon_pair(item, self.equipped(EquipSlot::InactiveOffhand))
|
||||
},
|
||||
EquipSlot::InactiveOffhand => {
|
||||
Loadout::is_valid_weapon_pair(self.equipped(EquipSlot::InactiveMainhand), item)
|
||||
},
|
||||
EquipSlot::ActiveMainhand => Loadout::is_valid_weapon_pair(
|
||||
item_kind,
|
||||
self.equipped(EquipSlot::ActiveOffhand)
|
||||
.map(|x| x.kind())
|
||||
.as_deref(),
|
||||
),
|
||||
EquipSlot::ActiveOffhand => Loadout::is_valid_weapon_pair(
|
||||
self.equipped(EquipSlot::ActiveMainhand)
|
||||
.map(|x| x.kind())
|
||||
.as_deref(),
|
||||
item_kind,
|
||||
),
|
||||
EquipSlot::InactiveMainhand => Loadout::is_valid_weapon_pair(
|
||||
item_kind,
|
||||
self.equipped(EquipSlot::InactiveOffhand)
|
||||
.map(|x| x.kind())
|
||||
.as_deref(),
|
||||
),
|
||||
EquipSlot::InactiveOffhand => Loadout::is_valid_weapon_pair(
|
||||
self.equipped(EquipSlot::InactiveMainhand)
|
||||
.map(|x| x.kind())
|
||||
.as_deref(),
|
||||
item_kind,
|
||||
),
|
||||
_ => true,
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
item.map_or(true, |item| equip_slot.can_hold(item))
|
||||
item_kind.map_or(true, |item| equip_slot.can_hold(item))
|
||||
}
|
||||
|
||||
fn is_valid_weapon_pair(main_hand: Option<&Item>, off_hand: Option<&Item>) -> bool {
|
||||
// Checks that a valid weapon pair is equipped, returns true if...
|
||||
match (
|
||||
main_hand.map(|i| (i.kind(), i.components())),
|
||||
off_hand.map(|i| (i.kind(), i.components())),
|
||||
) {
|
||||
// A weapon is being equipped in the mainhand, but not in the offhand
|
||||
(Some((ItemKind::Tool(_), _)), None) => true,
|
||||
// A weapon is being equipped in both slots, and both weapons are 1 handed
|
||||
(
|
||||
Some((ItemKind::Tool(tool_1), components_1)),
|
||||
Some((ItemKind::Tool(tool_2), components_2)),
|
||||
) => {
|
||||
matches!(
|
||||
(
|
||||
tool_1.hands.resolve_hands(components_1),
|
||||
tool_2.hands.resolve_hands(components_2)
|
||||
),
|
||||
(Hands::One, Hands::One)
|
||||
)
|
||||
},
|
||||
// A weapon is being unequipped that will result in both slots being empty
|
||||
(None, None) => true,
|
||||
_ => false,
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
fn is_valid_weapon_pair(main_hand: Option<&ItemKind>, off_hand: Option<&ItemKind>) -> bool {
|
||||
matches!((main_hand, off_hand),
|
||||
(Some(ItemKind::Tool(Tool { hands: Hands::One, .. })), None) |
|
||||
(Some(ItemKind::Tool(Tool { hands: Hands::Two, .. })), None) |
|
||||
(Some(ItemKind::Tool(Tool { hands: Hands::One, .. })), Some(ItemKind::Tool(Tool { hands: Hands::One, .. }))) |
|
||||
(None, None))
|
||||
}
|
||||
|
||||
pub(super) fn swap_equipped_weapons(&mut self) {
|
||||
@ -363,7 +366,7 @@ impl Loadout {
|
||||
// nothing is equipped in slot
|
||||
let valid_slot = |equip_slot| {
|
||||
self.equipped(equip_slot)
|
||||
.map_or(true, |i| self.slot_can_hold(equip_slot, Some(i)))
|
||||
.map_or(true, |i| self.slot_can_hold(equip_slot, Some(&*i.kind())))
|
||||
};
|
||||
|
||||
// If every weapon is currently in a valid slot, after this change they will
|
||||
@ -456,12 +459,10 @@ mod tests {
|
||||
loadout.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(get_test_bag(1)));
|
||||
|
||||
let result = loadout
|
||||
.get_slot_to_equip_into(&Item::create_test_item_from_kind(ItemKind::Armor(
|
||||
Armor::test_armor(
|
||||
ArmorKind::Bag("test".to_string()),
|
||||
Protection::Normal(0.0),
|
||||
Protection::Normal(0.0),
|
||||
),
|
||||
.get_slot_to_equip_into(&ItemKind::Armor(Armor::test_armor(
|
||||
ArmorKind::Bag("test".to_string()),
|
||||
Protection::Normal(0.0),
|
||||
Protection::Normal(0.0),
|
||||
)))
|
||||
.unwrap();
|
||||
|
||||
@ -478,12 +479,10 @@ mod tests {
|
||||
loadout.swap(EquipSlot::Armor(ArmorSlot::Bag4), Some(get_test_bag(1)));
|
||||
|
||||
let result = loadout
|
||||
.get_slot_to_equip_into(&Item::create_test_item_from_kind(ItemKind::Armor(
|
||||
Armor::test_armor(
|
||||
ArmorKind::Bag("test".to_string()),
|
||||
Protection::Normal(0.0),
|
||||
Protection::Normal(0.0),
|
||||
),
|
||||
.get_slot_to_equip_into(&ItemKind::Armor(Armor::test_armor(
|
||||
ArmorKind::Bag("test".to_string()),
|
||||
Protection::Normal(0.0),
|
||||
Protection::Normal(0.0),
|
||||
)))
|
||||
.unwrap();
|
||||
|
||||
|
@ -1073,7 +1073,10 @@ impl LoadoutBuilder {
|
||||
#[must_use = "Method consumes builder and returns updated builder."]
|
||||
fn with_equipment(mut self, equip_slot: EquipSlot, item: Option<Item>) -> Self {
|
||||
// Panic if item doesn't correspond to slot
|
||||
assert!(item.as_ref().map_or(true, |item| equip_slot.can_hold(item)));
|
||||
assert!(
|
||||
item.as_ref()
|
||||
.map_or(true, |item| equip_slot.can_hold(&*item.kind()))
|
||||
);
|
||||
|
||||
self.0.swap(equip_slot, item);
|
||||
self
|
||||
|
@ -2,14 +2,14 @@ use core::ops::Not;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::{Component, DerefFlaggedStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
use std::{borrow::Cow, convert::TryFrom, mem, ops::Range};
|
||||
use std::{convert::TryFrom, mem, ops::Range};
|
||||
use tracing::{debug, trace, warn};
|
||||
use vek::Vec3;
|
||||
|
||||
use crate::{
|
||||
comp::{
|
||||
inventory::{
|
||||
item::{tool::AbilityMap, ItemDef, MaterialStatManifest, TagExampleInfo},
|
||||
item::{tool::AbilityMap, ItemDef, ItemKind, MaterialStatManifest, TagExampleInfo},
|
||||
loadout::Loadout,
|
||||
slot::{EquipSlot, Slot, SlotError},
|
||||
},
|
||||
@ -128,8 +128,8 @@ impl Inventory {
|
||||
// Quality is sorted in reverse since we want high quality items first
|
||||
InventorySortOrder::Quality => Ord::cmp(&b.quality(), &a.quality()),
|
||||
InventorySortOrder::Tag => Ord::cmp(
|
||||
&a.tags.first().map_or(Cow::Borrowed(""), |tag| tag.name()),
|
||||
&b.tags.first().map_or(Cow::Borrowed(""), |tag| tag.name()),
|
||||
&a.tags().first().map_or("", |tag| tag.name()),
|
||||
&b.tags().first().map_or("", |tag| tag.name()),
|
||||
),
|
||||
});
|
||||
|
||||
@ -540,7 +540,7 @@ impl Inventory {
|
||||
#[must_use = "Returned items will be lost if not used"]
|
||||
pub fn equip(&mut self, inv_slot: InvSlotId) -> Vec<Item> {
|
||||
self.get(inv_slot)
|
||||
.and_then(|item| self.loadout.get_slot_to_equip_into(item))
|
||||
.and_then(|item| self.loadout.get_slot_to_equip_into(&*item.kind()))
|
||||
.map(|equip_slot| self.swap_inventory_loadout(inv_slot, equip_slot))
|
||||
.unwrap_or_else(Vec::new)
|
||||
}
|
||||
@ -551,7 +551,7 @@ impl Inventory {
|
||||
pub fn free_after_equip(&self, inv_slot: InvSlotId) -> i32 {
|
||||
let (inv_slot_for_equipped, slots_from_equipped) = self
|
||||
.get(inv_slot)
|
||||
.and_then(|item| self.loadout.get_slot_to_equip_into(item))
|
||||
.and_then(|item| self.loadout.get_slot_to_equip_into(&*item.kind()))
|
||||
.and_then(|equip_slot| self.equipped(equip_slot))
|
||||
.map_or((1, 0), |item| (0, item.slots().len()));
|
||||
|
||||
@ -758,7 +758,7 @@ impl Inventory {
|
||||
pub fn can_swap(&self, inv_slot_id: InvSlotId, equip_slot: EquipSlot) -> bool {
|
||||
// Check if loadout slot can hold item
|
||||
if !self.get(inv_slot_id).map_or(true, |item| {
|
||||
self.loadout.slot_can_hold(equip_slot, Some(item))
|
||||
self.loadout.slot_can_hold(equip_slot, Some(&*item.kind()))
|
||||
}) {
|
||||
trace!("can_swap = false, equip slot can't hold item");
|
||||
return false;
|
||||
@ -775,11 +775,11 @@ impl Inventory {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn equipped_items_replaceable_by<'a>(
|
||||
pub fn equipped_items_of_kind<'a>(
|
||||
&'a self,
|
||||
item: &'a Item,
|
||||
item_kind: &'a ItemKind,
|
||||
) -> impl Iterator<Item = &'a Item> {
|
||||
self.loadout.equipped_items_replaceable_by(item)
|
||||
self.loadout.equipped_items_of_kind(item_kind)
|
||||
}
|
||||
|
||||
pub fn swap_equipped_weapons(&mut self) { self.loadout.swap_equipped_weapons() }
|
||||
|
@ -110,28 +110,22 @@ pub enum ArmorSlot {
|
||||
}
|
||||
|
||||
impl Slot {
|
||||
pub fn can_hold(self, item: &item::Item) -> bool {
|
||||
match (self, item) {
|
||||
pub fn can_hold(self, item_kind: &item::ItemKind) -> bool {
|
||||
match (self, item_kind) {
|
||||
(Self::Inventory(_), _) => true,
|
||||
(Self::Equip(slot), item) => slot.can_hold(item),
|
||||
(Self::Equip(slot), item_kind) => slot.can_hold(item_kind),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EquipSlot {
|
||||
pub fn can_hold(self, item: &item::Item) -> bool {
|
||||
match (self, item.kind()) {
|
||||
pub fn can_hold(self, item_kind: &item::ItemKind) -> bool {
|
||||
match (self, item_kind) {
|
||||
(Self::Armor(slot), ItemKind::Armor(armor::Armor { kind, .. })) => slot.can_hold(kind),
|
||||
(Self::ActiveMainhand, ItemKind::Tool(_)) => true,
|
||||
(Self::ActiveOffhand, ItemKind::Tool(tool)) => matches!(
|
||||
tool.hands.resolve_hands(item.components()),
|
||||
tool::Hands::One
|
||||
),
|
||||
(Self::ActiveOffhand, ItemKind::Tool(tool)) => matches!(tool.hands, tool::Hands::One),
|
||||
(Self::InactiveMainhand, ItemKind::Tool(_)) => true,
|
||||
(Self::InactiveOffhand, ItemKind::Tool(tool)) => matches!(
|
||||
tool.hands.resolve_hands(item.components()),
|
||||
tool::Hands::One
|
||||
),
|
||||
(Self::InactiveOffhand, ItemKind::Tool(tool)) => matches!(tool.hands, tool::Hands::One),
|
||||
(Self::Lantern, ItemKind::Lantern(_)) => true,
|
||||
(Self::Glider, ItemKind::Glider(_)) => true,
|
||||
_ => false,
|
||||
|
@ -230,7 +230,7 @@ impl Poise {
|
||||
let protection = inventory
|
||||
.equipped_items()
|
||||
.filter_map(|item| {
|
||||
if let ItemKind::Armor(armor) = &item.kind() {
|
||||
if let ItemKind::Armor(armor) = &*item.kind() {
|
||||
armor.poise_resilience()
|
||||
} else {
|
||||
None
|
||||
|
@ -2,7 +2,9 @@ use crate::{
|
||||
assets::{self, AssetExt, AssetHandle},
|
||||
comp::{
|
||||
inventory::slot::InvSlotId,
|
||||
item::{modular, tool::AbilityMap, ItemDef, ItemKind, ItemTag, MaterialStatManifest},
|
||||
item::{
|
||||
modular, tool::AbilityMap, ItemBase, ItemDef, ItemKind, ItemTag, MaterialStatManifest,
|
||||
},
|
||||
Inventory, Item,
|
||||
},
|
||||
terrain::SpriteKind,
|
||||
@ -123,8 +125,12 @@ impl Recipe {
|
||||
}
|
||||
let (item_def, quantity) = &self.output;
|
||||
|
||||
let mut crafted_item =
|
||||
Item::new_from_item_def(Arc::clone(item_def), &[], ability_map, msm);
|
||||
let mut crafted_item = Item::new_from_item_base(
|
||||
ItemBase::Raw(Arc::clone(item_def)),
|
||||
&[],
|
||||
ability_map,
|
||||
msm,
|
||||
);
|
||||
for component in components {
|
||||
crafted_item.add_component(component, ability_map, msm);
|
||||
}
|
||||
@ -244,19 +250,22 @@ pub fn modular_weapon(
|
||||
) -> Result<Item, ModularWeaponError> {
|
||||
use modular::{ModularComponent, ModularComponentKind};
|
||||
// Closure to get inner modular component info from item in a given slot
|
||||
let unwrap_modular = |slot| -> Option<&ModularComponent> {
|
||||
if let Some(ItemKind::ModularComponent(mod_comp)) = inv.get(slot).map(|item| &item.kind) {
|
||||
Some(mod_comp)
|
||||
fn unwrap_modular(inv: &Inventory, slot: InvSlotId) -> Option<ModularComponent> {
|
||||
if let Some(ItemKind::ModularComponent(mod_comp)) =
|
||||
inv.get(slot).map(|item| item.kind()).as_deref()
|
||||
{
|
||||
// TODO: Remove
|
||||
Some(mod_comp.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Checks if both components are comptabile, and if so returns the toolkind to
|
||||
// make weapon of
|
||||
let compatiblity = if let (Some(damage_component), Some(held_component)) = (
|
||||
unwrap_modular(damage_component),
|
||||
unwrap_modular(held_component),
|
||||
unwrap_modular(inv, damage_component),
|
||||
unwrap_modular(inv, held_component),
|
||||
) {
|
||||
// Checks that damage and held component slots each contain a damage and held
|
||||
// modular component respectively
|
||||
@ -296,14 +305,14 @@ pub fn modular_weapon(
|
||||
.take(held_component, ability_map, msm)
|
||||
.expect("Expected component to exist");
|
||||
|
||||
// Initialize modular weapon
|
||||
let mut modular_weapon = modular::initialize_modular_weapon(tool_kind);
|
||||
|
||||
// Insert components into modular weapon item
|
||||
modular_weapon.add_component(damage_component, ability_map, msm);
|
||||
modular_weapon.add_component(held_component, ability_map, msm);
|
||||
|
||||
Ok(modular_weapon)
|
||||
// Create modular weapon
|
||||
let components = vec![damage_component, held_component];
|
||||
Ok(Item::new_from_item_base(
|
||||
ItemBase::Modular(modular::ModularBase::Tool(tool_kind)),
|
||||
&components,
|
||||
ability_map,
|
||||
msm,
|
||||
))
|
||||
},
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
arthropod, biped_large, biped_small,
|
||||
character_state::OutputEvents,
|
||||
inventory::slot::{EquipSlot, Slot},
|
||||
item::{Hands, Item, ItemKind, Tool, ToolKind},
|
||||
item::{Hands, ItemKind, ToolKind},
|
||||
quadruped_low, quadruped_medium, quadruped_small,
|
||||
skills::{Skill, SwimSkill, SKILL_MODIFIERS},
|
||||
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
|
||||
@ -581,11 +581,10 @@ pub fn attempt_wield(data: &JoinData<'_>, update: &mut StateUpdate) {
|
||||
let equip_time = |equip_slot| {
|
||||
data.inventory
|
||||
.and_then(|inv| inv.equipped(equip_slot))
|
||||
.and_then(|item| match item.kind() {
|
||||
ItemKind::Tool(tool) => Some((item, tool)),
|
||||
.and_then(|item| match &*item.kind() {
|
||||
ItemKind::Tool(tool) => Some(tool.equip_time()),
|
||||
_ => None,
|
||||
})
|
||||
.map(|(item, tool)| tool.equip_time(data.msm, item.components()))
|
||||
};
|
||||
|
||||
// Calculates time required to equip weapons, if weapon in mainhand and offhand,
|
||||
@ -704,7 +703,7 @@ pub fn handle_manipulate_loadout(
|
||||
if let Some((item_kind, item)) = data
|
||||
.inventory
|
||||
.and_then(|inv| inv.get(inv_slot))
|
||||
.and_then(|item| Option::<ItemUseKind>::from(item.kind()).zip(Some(item)))
|
||||
.and_then(|item| Option::<ItemUseKind>::from(&*item.kind()).zip(Some(item)))
|
||||
{
|
||||
let (buildup_duration, use_duration, recover_duration) = item_kind.durations();
|
||||
// If item returns a valid kind for item use, do into use item character state
|
||||
@ -948,7 +947,7 @@ pub fn attempt_input(
|
||||
|
||||
/// Checks that player can block, then attempts to block
|
||||
pub fn handle_block_input(data: &JoinData<'_>, update: &mut StateUpdate) {
|
||||
let can_block = |equip_slot| matches!(unwrap_tool_data(data, equip_slot), Some((tool, _)) if tool.can_block());
|
||||
let can_block = |equip_slot| matches!(unwrap_tool_data(data, equip_slot), Some((kind, _)) if kind.can_block());
|
||||
let hands = get_hands(data);
|
||||
if input_is_pressed(data, InputKind::Block)
|
||||
&& (can_block(EquipSlot::ActiveMainhand)
|
||||
@ -1000,16 +999,14 @@ pub fn is_strafing(data: &JoinData<'_>, update: &StateUpdate) -> bool {
|
||||
}
|
||||
|
||||
/// Returns tool and components
|
||||
pub fn unwrap_tool_data<'a>(
|
||||
data: &'a JoinData,
|
||||
equip_slot: EquipSlot,
|
||||
) -> Option<(&'a Tool, &'a [Item])> {
|
||||
if let Some((ItemKind::Tool(tool), components)) = data
|
||||
pub fn unwrap_tool_data(data: &JoinData, equip_slot: EquipSlot) -> Option<(ToolKind, Hands)> {
|
||||
if let Some(ItemKind::Tool(tool)) = data
|
||||
.inventory
|
||||
.and_then(|inv| inv.equipped(equip_slot))
|
||||
.map(|i| (i.kind(), i.components()))
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some((tool, components))
|
||||
Some((tool.kind, tool.hands))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -1017,12 +1014,13 @@ pub fn unwrap_tool_data<'a>(
|
||||
|
||||
pub fn get_hands(data: &JoinData<'_>) -> (Option<Hands>, Option<Hands>) {
|
||||
let hand = |slot| {
|
||||
if let Some((ItemKind::Tool(tool), components)) = data
|
||||
if let Some(ItemKind::Tool(tool)) = data
|
||||
.inventory
|
||||
.and_then(|inv| inv.equipped(slot))
|
||||
.map(|i| (i.kind(), i.components()))
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(tool.hands.resolve_hands(components))
|
||||
Some(tool.hands)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -1046,8 +1044,8 @@ pub fn get_crit_data(data: &JoinData<'_>, ai: AbilityInfo) -> (f32, f32) {
|
||||
})
|
||||
.and_then(|slot| data.inventory.and_then(|inv| inv.equipped(slot)))
|
||||
.and_then(|item| {
|
||||
if let ItemKind::Tool(tool) = item.kind() {
|
||||
Some(tool.base_crit_chance(data.msm, item.components()))
|
||||
if let ItemKind::Tool(tool) = &*item.kind() {
|
||||
Some(tool.base_crit_chance())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -1068,8 +1066,8 @@ pub fn get_buff_strength(data: &JoinData<'_>, ai: AbilityInfo) -> f32 {
|
||||
})
|
||||
.and_then(|slot| data.inventory.and_then(|inv| inv.equipped(slot)))
|
||||
.and_then(|item| {
|
||||
if let ItemKind::Tool(tool) = item.kind() {
|
||||
Some(tool.base_buff_strength(data.msm, item.components()))
|
||||
if let ItemKind::Tool(tool) = &*item.kind() {
|
||||
Some(tool.base_buff_strength())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -1178,10 +1176,12 @@ impl AbilityInfo {
|
||||
} else {
|
||||
unwrap_tool_data(data, EquipSlot::ActiveMainhand)
|
||||
};
|
||||
let (tool, hand) = (
|
||||
tool_data.map(|(t, _)| t.kind),
|
||||
tool_data.map(|(t, components)| HandInfo::from_main_tool(t, components, from_offhand)),
|
||||
);
|
||||
let (tool, hand) = tool_data.map_or((None, None), |(kind, hands)| {
|
||||
(
|
||||
Some(kind),
|
||||
Some(HandInfo::from_main_tool(hands, from_offhand)),
|
||||
)
|
||||
});
|
||||
|
||||
Self {
|
||||
tool,
|
||||
@ -1200,8 +1200,8 @@ pub enum HandInfo {
|
||||
}
|
||||
|
||||
impl HandInfo {
|
||||
pub fn from_main_tool(tool: &Tool, components: &[Item], from_offhand: bool) -> Self {
|
||||
match tool.hands.resolve_hands(components) {
|
||||
pub fn from_main_tool(tool_hands: Hands, from_offhand: bool) -> Self {
|
||||
match tool_hands {
|
||||
Hands::Two => Self::TwoHanded,
|
||||
Hands::One => {
|
||||
if from_offhand {
|
||||
|
@ -1111,7 +1111,7 @@ fn handle_exp_gain(
|
||||
let mut add_tool_from_slot = |equip_slot| {
|
||||
let tool_kind = inventory
|
||||
.equipped(equip_slot)
|
||||
.and_then(|i| match &i.kind() {
|
||||
.and_then(|i| match &*i.kind() {
|
||||
ItemKind::Tool(tool) if tool.kind.gains_combat_xp() => Some(tool.kind),
|
||||
_ => None,
|
||||
});
|
||||
|
@ -51,22 +51,22 @@ pub fn handle_lantern(server: &mut Server, entity: EcsEntity, enable: bool) {
|
||||
.map_or(true, |h| !h.is_dead)
|
||||
{
|
||||
let inventory_storage = ecs.read_storage::<Inventory>();
|
||||
let lantern_opt = inventory_storage
|
||||
let lantern_info = inventory_storage
|
||||
.get(entity)
|
||||
.and_then(|inventory| inventory.equipped(EquipSlot::Lantern))
|
||||
.and_then(|item| {
|
||||
if let comp::item::ItemKind::Lantern(l) = item.kind() {
|
||||
Some(l)
|
||||
if let comp::item::ItemKind::Lantern(l) = &*item.kind() {
|
||||
Some((l.color(), l.strength()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(lantern) = lantern_opt {
|
||||
if let Some((col, strength)) = lantern_info {
|
||||
let _ =
|
||||
ecs.write_storage::<comp::LightEmitter>()
|
||||
.insert(entity, comp::LightEmitter {
|
||||
col: lantern.color(),
|
||||
strength: lantern.strength(),
|
||||
col,
|
||||
strength,
|
||||
flicker: 0.35,
|
||||
animated: true,
|
||||
});
|
||||
|
@ -32,11 +32,11 @@ use common_net::msg::ServerGeneral;
|
||||
pub fn swap_lantern(
|
||||
storage: &mut WriteStorage<comp::LightEmitter>,
|
||||
entity: EcsEntity,
|
||||
lantern: &item::Lantern,
|
||||
(lantern_color, lantern_strength): (Rgb<f32>, f32),
|
||||
) {
|
||||
if let Some(mut light) = storage.get_mut(entity) {
|
||||
light.strength = lantern.strength();
|
||||
light.col = lantern.color();
|
||||
light.strength = lantern_strength;
|
||||
light.col = lantern_color;
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,16 +264,19 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
Slot::Inventory(slot) => {
|
||||
use item::ItemKind;
|
||||
|
||||
let (is_equippable, lantern_opt) =
|
||||
inventory.get(slot).map_or((false, None), |i| {
|
||||
(i.kind().is_equippable(), match i.kind() {
|
||||
ItemKind::Lantern(lantern) => Some(lantern),
|
||||
let is_equippable = inventory
|
||||
.get(slot)
|
||||
.map_or(false, |i| i.kind().is_equippable());
|
||||
if is_equippable {
|
||||
if let Some(lantern_info) =
|
||||
inventory.get(slot).and_then(|i| match &*i.kind() {
|
||||
ItemKind::Lantern(lantern) => {
|
||||
Some((lantern.color(), lantern.strength()))
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
});
|
||||
if is_equippable {
|
||||
if let Some(lantern) = lantern_opt {
|
||||
swap_lantern(&mut state.ecs().write_storage(), entity, lantern);
|
||||
{
|
||||
swap_lantern(&mut state.ecs().write_storage(), entity, lantern_info);
|
||||
}
|
||||
if let Some(pos) = state.ecs().read_storage::<comp::Pos>().get(entity) {
|
||||
dropped_items.extend(inventory.equip(slot).into_iter().map(|x| {
|
||||
@ -292,7 +295,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
&state.ecs().read_resource::<AbilityMap>(),
|
||||
&state.ecs().read_resource::<item::MaterialStatManifest>(),
|
||||
) {
|
||||
match item.kind() {
|
||||
match &*item.kind() {
|
||||
ItemKind::Consumable { effects, .. } => {
|
||||
maybe_effect = Some(effects.clone());
|
||||
Some(comp::InventoryUpdateEvent::Consumed(
|
||||
|
@ -167,7 +167,7 @@ impl<'a> System<'a> for Sys {
|
||||
.equipped(EquipSlot::Glider)
|
||||
.as_ref()
|
||||
.map_or(false, |item| {
|
||||
matches!(item.kind(), comp::item::ItemKind::Glider(_))
|
||||
matches!(&*item.kind(), comp::item::ItemKind::Glider(_))
|
||||
});
|
||||
|
||||
let is_gliding = matches!(
|
||||
@ -701,7 +701,7 @@ impl<'a> AgentData<'a> {
|
||||
.equipped(EquipSlot::Lantern)
|
||||
.as_ref()
|
||||
.map_or(false, |item| {
|
||||
matches!(item.kind(), comp::item::ItemKind::Lantern(_))
|
||||
matches!(&*item.kind(), comp::item::ItemKind::Lantern(_))
|
||||
});
|
||||
let lantern_turned_on = self.light_emitter.is_some();
|
||||
let day_period = DayPeriod::from(read_data.time_of_day.0);
|
||||
@ -1496,7 +1496,7 @@ impl<'a> AgentData<'a> {
|
||||
let healing_value = |item: &Item| {
|
||||
let mut value = 0.0;
|
||||
|
||||
if let ItemKind::Consumable { kind, effects, .. } = &item.kind {
|
||||
if let ItemKind::Consumable { kind, effects, .. } = &*item.kind() {
|
||||
if matches!(kind, ConsumableKind::Drink)
|
||||
|| (relaxed && matches!(kind, ConsumableKind::Food))
|
||||
{
|
||||
@ -1639,7 +1639,7 @@ impl<'a> AgentData<'a> {
|
||||
.as_ref()
|
||||
.map(|item| {
|
||||
if let Some(ability_spec) = item.ability_spec() {
|
||||
match ability_spec {
|
||||
match &*ability_spec {
|
||||
AbilitySpec::Custom(spec) => match spec.as_str() {
|
||||
"Oni" | "Sword Simple" => Tactic::Sword,
|
||||
"Staff Simple" => Tactic::Staff,
|
||||
@ -1697,7 +1697,7 @@ impl<'a> AgentData<'a> {
|
||||
},
|
||||
AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind),
|
||||
}
|
||||
} else if let ItemKind::Tool(tool) = &item.kind() {
|
||||
} else if let ItemKind::Tool(tool) = &*item.kind() {
|
||||
tool_tactic(tool.kind)
|
||||
} else {
|
||||
Tactic::SimpleMelee
|
||||
|
@ -146,7 +146,7 @@ impl CombatEventMapper {
|
||||
inventory: &Inventory,
|
||||
) -> SfxEvent {
|
||||
if let Some(item) = inventory.equipped(EquipSlot::ActiveMainhand) {
|
||||
if let ItemKind::Tool(data) = item.kind() {
|
||||
if let ItemKind::Tool(data) = &*item.kind() {
|
||||
if character_state.is_attack() {
|
||||
return SfxEvent::Attack(
|
||||
CharacterAbilityType::from(character_state),
|
||||
|
@ -302,7 +302,7 @@ impl From<&InventoryUpdateEvent> for SfxEvent {
|
||||
InventoryUpdateEvent::Collected(item) => {
|
||||
// Handle sound effects for types of collected items, falling
|
||||
// back to the default Collected event
|
||||
match &item.kind() {
|
||||
match &*item.kind() {
|
||||
ItemKind::Tool(tool) => {
|
||||
SfxEvent::Inventory(SfxInventoryEvent::CollectedTool(tool.kind))
|
||||
},
|
||||
|
@ -192,11 +192,11 @@ impl CraftingTab {
|
||||
match self {
|
||||
CraftingTab::All | CraftingTab::Dismantle => true,
|
||||
CraftingTab::Food => item.tags().contains(&ItemTag::Food),
|
||||
CraftingTab::Armor => match item.kind() {
|
||||
CraftingTab::Armor => match &*item.kind() {
|
||||
ItemKind::Armor(_) => !item.tags().contains(&ItemTag::Bag),
|
||||
_ => false,
|
||||
},
|
||||
CraftingTab::Glider => matches!(item.kind(), ItemKind::Glider(_)),
|
||||
CraftingTab::Glider => matches!(&*item.kind(), ItemKind::Glider(_)),
|
||||
CraftingTab::Potion => item.tags().contains(&ItemTag::Potion),
|
||||
CraftingTab::ProcessedMaterial => item.tags().iter().any(|tag| {
|
||||
matches!(
|
||||
@ -207,7 +207,7 @@ impl CraftingTab {
|
||||
CraftingTab::Bag => item.tags().contains(&ItemTag::Bag),
|
||||
CraftingTab::Tool => item.tags().contains(&ItemTag::CraftingTool),
|
||||
CraftingTab::Utility => item.tags().contains(&ItemTag::Utility),
|
||||
CraftingTab::Weapon => match item.kind() {
|
||||
CraftingTab::Weapon => match &*item.kind() {
|
||||
ItemKind::Tool(_) => !item.tags().contains(&ItemTag::CraftingTool),
|
||||
_ => false,
|
||||
},
|
||||
@ -468,8 +468,8 @@ impl<'a> Widget for Crafting<'a> {
|
||||
SearchFilter::Input => recipe.inputs().any(|(input, _, _)| {
|
||||
let input_name = match input {
|
||||
RecipeInput::Item(def) => def.name(),
|
||||
RecipeInput::Tag(tag) => tag.name(),
|
||||
RecipeInput::TagSameItem(tag, _) => tag.name(),
|
||||
RecipeInput::Tag(tag) => Cow::Borrowed(tag.name()),
|
||||
RecipeInput::TagSameItem(tag, _) => Cow::Borrowed(tag.name()),
|
||||
// Works, but probably will have some...interesting false positives
|
||||
// Code reviewers probably required to do magic to make not hacky
|
||||
RecipeInput::ListSameItem(defs, _) => {
|
||||
@ -916,8 +916,7 @@ impl<'a> Widget for Crafting<'a> {
|
||||
RecipeInput::Item(item_def) => Arc::clone(item_def),
|
||||
RecipeInput::Tag(tag) | RecipeInput::TagSameItem(tag, _) => {
|
||||
Arc::<ItemDef>::load_expect_cloned(
|
||||
self
|
||||
.inventory
|
||||
self.inventory
|
||||
.slots()
|
||||
.find_map(|slot| {
|
||||
slot.as_ref().and_then(|item| {
|
||||
@ -928,12 +927,11 @@ impl<'a> Widget for Crafting<'a> {
|
||||
}
|
||||
})
|
||||
})
|
||||
.unwrap_or(&tag.exemplar_identifier()),
|
||||
.unwrap_or_else(|| tag.exemplar_identifier()),
|
||||
)
|
||||
},
|
||||
RecipeInput::ListSameItem(item_defs, _) => Arc::<ItemDef>::load_expect_cloned(
|
||||
self
|
||||
.inventory
|
||||
self.inventory
|
||||
.slots()
|
||||
.find_map(|slot| {
|
||||
slot.as_ref().and_then(|item| {
|
||||
|
@ -4,7 +4,7 @@ use common::{
|
||||
inventory::trade_pricing::TradePricing,
|
||||
item::{
|
||||
armor::{Armor, ArmorKind, Protection},
|
||||
tool::{Hands, StatKind, Stats, Tool, ToolKind},
|
||||
tool::{Hands, Stats, Tool, ToolKind},
|
||||
Item, ItemDesc, ItemKind, MaterialKind, MaterialStatManifest, ModularComponent,
|
||||
},
|
||||
BuffKind,
|
||||
@ -71,12 +71,12 @@ pub fn price_desc(
|
||||
}
|
||||
|
||||
pub fn kind_text<'a>(item: &dyn ItemDesc, i18n: &'a Localization) -> Cow<'a, str> {
|
||||
match item.kind() {
|
||||
match &*item.kind() {
|
||||
ItemKind::Armor(armor) => Cow::Borrowed(armor_kind(armor, i18n)),
|
||||
ItemKind::Tool(tool) => Cow::Owned(format!(
|
||||
"{} ({})",
|
||||
tool_kind(tool, i18n),
|
||||
tool_hands(tool, item.components(), i18n)
|
||||
tool_hands(tool, i18n)
|
||||
)),
|
||||
ItemKind::ModularComponent(_mc) => Cow::Borrowed(i18n.get("common.bag.shoulders")),
|
||||
ItemKind::Glider(_glider) => Cow::Borrowed(i18n.get("common.kind.glider")),
|
||||
@ -106,7 +106,7 @@ pub fn modular_component_desc(
|
||||
msm: &MaterialStatManifest,
|
||||
description: &str,
|
||||
) -> String {
|
||||
let stats = StatKind::Direct(mc.stats).resolve_stats(msm, components);
|
||||
let stats = mc.stats;
|
||||
let statblock = statblock_desc(&stats);
|
||||
let mut result = format!("Modular Component\n\n{}\n\n{}", statblock, description);
|
||||
if !components.is_empty() {
|
||||
@ -121,7 +121,7 @@ pub fn modular_component_desc(
|
||||
}
|
||||
|
||||
pub fn stats_count(item: &dyn ItemDesc) -> usize {
|
||||
let mut count = match item.kind() {
|
||||
let mut count = match &*item.kind() {
|
||||
ItemKind::Armor(armor) => {
|
||||
if matches!(armor.kind, ArmorKind::Bag(_)) {
|
||||
0
|
||||
@ -139,7 +139,7 @@ pub fn stats_count(item: &dyn ItemDesc) -> usize {
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
let is_bag = match item.kind() {
|
||||
let is_bag = match &*item.kind() {
|
||||
ItemKind::Armor(armor) => matches!(armor.kind, ArmorKind::Bag(_)),
|
||||
_ => false,
|
||||
};
|
||||
@ -274,8 +274,8 @@ fn tool_kind<'a>(tool: &Tool, i18n: &'a Localization) -> &'a str {
|
||||
}
|
||||
|
||||
/// Output the number of hands needed to hold a tool
|
||||
pub fn tool_hands<'a>(tool: &Tool, components: &[Item], i18n: &'a Localization) -> &'a str {
|
||||
let hands = match tool.hands.resolve_hands(components) {
|
||||
pub fn tool_hands<'a>(tool: &Tool, i18n: &'a Localization) -> &'a str {
|
||||
let hands = match tool.hands {
|
||||
Hands::One => i18n.get("common.hands.one"),
|
||||
Hands::Two => i18n.get("common.hands.two"),
|
||||
};
|
||||
|
@ -152,6 +152,7 @@ impl CharacterCacheKey {
|
||||
})) = inventory
|
||||
.equipped(EquipSlot::Armor(ArmorSlot::Head))
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(armor.clone())
|
||||
} else {
|
||||
@ -163,6 +164,7 @@ impl CharacterCacheKey {
|
||||
})) = inventory
|
||||
.equipped(EquipSlot::Armor(ArmorSlot::Shoulders))
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(armor.clone())
|
||||
} else {
|
||||
@ -174,6 +176,7 @@ impl CharacterCacheKey {
|
||||
})) = inventory
|
||||
.equipped(EquipSlot::Armor(ArmorSlot::Chest))
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(armor.clone())
|
||||
} else {
|
||||
@ -185,6 +188,7 @@ impl CharacterCacheKey {
|
||||
})) = inventory
|
||||
.equipped(EquipSlot::Armor(ArmorSlot::Belt))
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(armor.clone())
|
||||
} else {
|
||||
@ -196,6 +200,7 @@ impl CharacterCacheKey {
|
||||
})) = inventory
|
||||
.equipped(EquipSlot::Armor(ArmorSlot::Back))
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(armor.clone())
|
||||
} else {
|
||||
@ -207,6 +212,7 @@ impl CharacterCacheKey {
|
||||
})) = inventory
|
||||
.equipped(EquipSlot::Armor(ArmorSlot::Legs))
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(armor.clone())
|
||||
} else {
|
||||
@ -233,15 +239,19 @@ impl CharacterCacheKey {
|
||||
} else {
|
||||
None
|
||||
},
|
||||
lantern: if let Some(ItemKind::Lantern(lantern)) =
|
||||
inventory.equipped(EquipSlot::Lantern).map(|i| i.kind())
|
||||
lantern: if let Some(ItemKind::Lantern(lantern)) = inventory
|
||||
.equipped(EquipSlot::Lantern)
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(lantern.kind.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
glider: if let Some(ItemKind::Glider(glider)) =
|
||||
inventory.equipped(EquipSlot::Glider).map(|i| i.kind())
|
||||
glider: if let Some(ItemKind::Glider(glider)) = inventory
|
||||
.equipped(EquipSlot::Glider)
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(glider.kind.clone())
|
||||
} else {
|
||||
@ -253,6 +263,7 @@ impl CharacterCacheKey {
|
||||
})) = inventory
|
||||
.equipped(EquipSlot::Armor(ArmorSlot::Hands))
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(armor.clone())
|
||||
} else {
|
||||
@ -264,6 +275,7 @@ impl CharacterCacheKey {
|
||||
})) = inventory
|
||||
.equipped(EquipSlot::Armor(ArmorSlot::Feet))
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(armor.clone())
|
||||
} else {
|
||||
@ -275,6 +287,7 @@ impl CharacterCacheKey {
|
||||
})) = inventory
|
||||
.equipped(EquipSlot::Armor(ArmorSlot::Head))
|
||||
.map(|i| i.kind())
|
||||
.as_deref()
|
||||
{
|
||||
Some(armor.clone())
|
||||
} else {
|
||||
|
@ -873,12 +873,8 @@ impl FigureMgr {
|
||||
inventory
|
||||
.and_then(|i| i.equipped(equip_slot))
|
||||
.map(|i| {
|
||||
if let ItemKind::Tool(tool) = i.kind() {
|
||||
(
|
||||
Some(tool.kind),
|
||||
Some(tool.hands.resolve_hands(i.components())),
|
||||
i.ability_spec(),
|
||||
)
|
||||
if let ItemKind::Tool(tool) = &*i.kind() {
|
||||
(Some(tool.kind), Some(tool.hands), i.ability_spec())
|
||||
} else {
|
||||
(None, None, None)
|
||||
}
|
||||
@ -888,8 +884,10 @@ impl FigureMgr {
|
||||
|
||||
let (active_tool_kind, active_tool_hand, active_tool_spec) =
|
||||
tool_info(EquipSlot::ActiveMainhand);
|
||||
let active_tool_spec = active_tool_spec.as_deref();
|
||||
let (second_tool_kind, second_tool_hand, second_tool_spec) =
|
||||
tool_info(EquipSlot::ActiveOffhand);
|
||||
let second_tool_spec = second_tool_spec.as_deref();
|
||||
|
||||
let hands = (active_tool_hand, second_tool_hand);
|
||||
|
||||
|
@ -283,11 +283,8 @@ impl Scene {
|
||||
inventory
|
||||
.and_then(|inv| inv.equipped(equip_slot))
|
||||
.and_then(|i| {
|
||||
if let ItemKind::Tool(tool) = i.kind() {
|
||||
Some((
|
||||
Some(tool.kind),
|
||||
Some(tool.hands.resolve_hands(i.components())),
|
||||
))
|
||||
if let ItemKind::Tool(tool) = &*i.kind() {
|
||||
Some((Some(tool.kind), Some(tool.hands)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -410,8 +410,8 @@ impl PlayState for SessionState {
|
||||
.inventories()
|
||||
.get(player_entity)
|
||||
.and_then(|inv| inv.equipped(EquipSlot::ActiveMainhand))
|
||||
.and_then(|item| item.tool())
|
||||
.map_or(false, |tool| tool.kind == ToolKind::Pick)
|
||||
.and_then(|item| item.tool_info())
|
||||
.map_or(false, |tool_kind| tool_kind == ToolKind::Pick)
|
||||
&& client.is_wielding() == Some(true);
|
||||
|
||||
// Check to see whether we're aiming at anything
|
||||
|
@ -462,11 +462,9 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
|
||||
let quality = get_quality_col(item);
|
||||
|
||||
let equip_slot = item
|
||||
.concrete_item()
|
||||
.map(|item| inventory.equipped_items_replaceable_by(item))
|
||||
.into_iter()
|
||||
.flatten();
|
||||
let item_kind = &*item.kind();
|
||||
|
||||
let equip_slot = inventory.equipped_items_of_kind(item_kind);
|
||||
|
||||
let (title, desc) = (item.name().to_string(), item.description().to_string());
|
||||
|
||||
@ -582,12 +580,12 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
.set(state.ids.subtitle, ui);
|
||||
|
||||
// Stats
|
||||
match item.kind() {
|
||||
match &*item.kind() {
|
||||
ItemKind::Tool(tool) => {
|
||||
let power = tool.base_power(self.msm, item.components()) * 10.0;
|
||||
let speed = tool.base_speed(self.msm, item.components());
|
||||
let effect_power = tool.base_effect_power(self.msm, item.components()) * 10.0;
|
||||
let crit_chance = tool.base_crit_chance(self.msm, item.components()) * 100.0;
|
||||
let power = tool.base_power() * 10.0;
|
||||
let speed = tool.base_speed();
|
||||
let effect_power = tool.base_effect_power() * 10.0;
|
||||
let crit_chance = tool.base_crit_chance() * 100.0;
|
||||
let combat_rating = combat::weapon_rating(&item, self.msm) * 10.0;
|
||||
|
||||
// Combat Rating
|
||||
@ -667,14 +665,8 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
|
||||
if let Some(equipped_item) = first_equipped {
|
||||
if let ItemKind::Tool(equipped_tool) = equipped_item.kind() {
|
||||
let tool_stats = tool
|
||||
.stats
|
||||
.resolve_stats(self.msm, item.components())
|
||||
.clamp_speed();
|
||||
let equipped_tool_stats = equipped_tool
|
||||
.stats
|
||||
.resolve_stats(self.msm, equipped_item.components())
|
||||
.clamp_speed();
|
||||
let tool_stats = tool.stats;
|
||||
let equipped_tool_stats = equipped_tool.stats;
|
||||
let diff = tool_stats - equipped_tool_stats;
|
||||
let power_diff =
|
||||
util::comparison(tool_stats.power, equipped_tool_stats.power);
|
||||
@ -970,7 +962,7 @@ impl<'a> Widget for ItemTooltip<'a> {
|
||||
}
|
||||
|
||||
if let Some(equipped_item) = first_equipped {
|
||||
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 protection_diff = util::option_comparison(
|
||||
&armor.protection(),
|
||||
|
Loading…
Reference in New Issue
Block a user