Addressed second round of balance feedback (no assets).

This commit is contained in:
Sam 2022-05-08 23:30:56 -04:00
parent d46cdf60b5
commit c0dd748bc7
16 changed files with 153 additions and 103 deletions

View File

@ -1004,7 +1004,7 @@ impl CharacterAbility {
}| {
// Do we want to make buff_strength affect this instead of power?
// Look into during modular weapon transition
*strength *= stats.power;
*strength *= stats.buff_strength;
},
);
*range *= stats.range;
@ -1046,7 +1046,7 @@ impl CharacterAbility {
} => {
// Do we want to make buff_strength affect this instead of power?
// Look into during modular weapon transition
*buff_strength *= stats.power;
*buff_strength *= stats.buff_strength;
*buildup_duration /= stats.speed;
*cast_duration /= stats.speed;
*recover_duration /= stats.speed;

View File

@ -35,12 +35,13 @@ impl<T: ItemDesc> From<&T> for ItemKey {
ItemDefinitionId::Modular { .. } => {
ItemKey::ModularWeapon(modular::weapon_to_key(item_desc))
},
ItemDefinitionId::Compound { .. } => ItemKey::Empty,
},
ItemKind::ModularComponent(mod_comp) => {
use modular::ModularComponent;
match mod_comp {
ModularComponent::ToolPrimaryComponent { .. } => {
if let Some(id) = item_definition_id.raw() {
if let ItemDefinitionId::Simple(id) = item_definition_id {
match modular::weapon_component_to_key(id, item_desc.components()) {
Ok(key) => ItemKey::ModularWeaponComponent(key),
// TODO: Maybe use a different ItemKey?
@ -51,7 +52,7 @@ impl<T: ItemDesc> From<&T> for ItemKey {
}
},
ModularComponent::ToolSecondaryComponent { .. } => {
if let Some(id) = item_definition_id.raw() {
if let ItemDefinitionId::Simple(id) = item_definition_id {
// TODO: Maybe use a different ItemKey?
ItemKey::Tool(id.to_owned())
} else {
@ -65,7 +66,7 @@ impl<T: ItemDesc> From<&T> for ItemKey {
ItemKind::Armor(Armor { kind, .. }) => ItemKey::Armor(kind.clone()),
ItemKind::Utility { kind, .. } => ItemKey::Utility(*kind),
ItemKind::Consumable { .. } => {
if let Some(id) = item_definition_id.raw() {
if let ItemDefinitionId::Simple(id) = item_definition_id {
ItemKey::Consumable(id.to_owned())
} else {
ItemKey::Empty

View File

@ -448,6 +448,10 @@ pub enum ItemDefinitionId<'a> {
pseudo_base: &'a str,
components: Vec<ItemDefinitionId<'a>>,
},
Compound {
simple_base: &'a str,
components: Vec<ItemDefinitionId<'a>>,
},
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
@ -457,6 +461,10 @@ pub enum ItemDefinitionIdOwned {
pseudo_base: String,
components: Vec<ItemDefinitionIdOwned>,
},
Compound {
simple_base: String,
components: Vec<ItemDefinitionIdOwned>,
},
}
impl ItemDefinitionIdOwned {
@ -470,15 +478,23 @@ impl ItemDefinitionIdOwned {
pseudo_base,
components: components.iter().map(|comp| comp.as_ref()).collect(),
},
Self::Compound {
ref simple_base,
ref components,
} => ItemDefinitionId::Compound {
simple_base,
components: components.iter().map(|comp| comp.as_ref()).collect(),
},
}
}
}
impl<'a> ItemDefinitionId<'a> {
pub fn raw(&self) -> Option<&str> {
pub fn itemdef_id(&self) -> Option<&str> {
match self {
Self::Simple(id) => Some(id),
Self::Modular { .. } => None,
Self::Compound { simple_base, .. } => Some(simple_base),
}
}
@ -492,6 +508,13 @@ impl<'a> ItemDefinitionId<'a> {
pseudo_base: String::from(*pseudo_base),
components: components.iter().map(|comp| comp.to_owned()).collect(),
},
Self::Compound {
simple_base,
components,
} => ItemDefinitionIdOwned::Compound {
simple_base: String::from(*simple_base),
components: components.iter().map(|comp| comp.to_owned()).collect(),
},
}
}
}
@ -894,7 +917,20 @@ impl Item {
pub fn item_definition_id(&self) -> ItemDefinitionId<'_> {
match &self.item_base {
ItemBase::Simple(item_def) => ItemDefinitionId::Simple(&item_def.item_definition_id),
ItemBase::Simple(item_def) => {
if self.components.is_empty() {
ItemDefinitionId::Simple(&item_def.item_definition_id)
} else {
ItemDefinitionId::Compound {
simple_base: &item_def.item_definition_id,
components: self
.components
.iter()
.map(|item| item.item_definition_id())
.collect(),
}
}
},
ItemBase::Modular(mod_base) => ItemDefinitionId::Modular {
pseudo_base: mod_base.pseudo_item_id(),
components: self

View File

@ -240,7 +240,7 @@ impl ModularComponent {
.iter()
.filter_map(|comp| {
comp.item_definition_id()
.raw()
.itemdef_id()
.and_then(|id| msm.0.get(id))
.copied()
.zip(Some(1))
@ -314,7 +314,7 @@ lazy_static! {
if let Ok(items) = Item::new_from_asset_glob(&directory) {
items
.into_iter()
.filter_map(|comp| Some(comp.item_definition_id().raw()?.to_owned()))
.filter_map(|comp| Some(comp.item_definition_id().itemdef_id()?.to_owned()))
.filter_map(|id| Arc::<ItemDef>::load_cloned(&id).ok())
.for_each(|comp_def| {
if let ItemKind::ModularComponent(ModularComponent::ToolSecondaryComponent { hand_restriction, .. }) = comp_def.kind {
@ -476,9 +476,11 @@ pub fn weapon_to_key(mod_weap: impl ItemDesc) -> ModularWeaponKey {
.iter()
.find_map(|comp| match &*comp.kind() {
ItemKind::ModularComponent(ModularComponent::ToolPrimaryComponent { .. }) => {
let component_id = comp.item_definition_id().raw()?.to_owned();
let component_id = comp.item_definition_id().itemdef_id()?.to_owned();
let material_id = comp.components().iter().find_map(|mat| match &*mat.kind() {
ItemKind::Ingredient { .. } => Some(mat.item_definition_id().raw()?.to_owned()),
ItemKind::Ingredient { .. } => {
Some(mat.item_definition_id().itemdef_id()?.to_owned())
},
_ => None,
});
Some((component_id, material_id))
@ -504,7 +506,7 @@ pub fn weapon_component_to_key(
components: &[Item],
) -> Result<ModularWeaponComponentKey, ModularWeaponComponentKeyError> {
match components.iter().find_map(|mat| match &*mat.kind() {
ItemKind::Ingredient { .. } => Some(mat.item_definition_id().raw()?.to_owned()),
ItemKind::Ingredient { .. } => Some(mat.item_definition_id().itemdef_id()?.to_owned()),
_ => None,
}) {
Some(material_id) => Ok((item_def_id.to_owned(), material_id)),

View File

@ -369,6 +369,7 @@ impl TradePricing {
_ if name.starts_with("common.items.crafting_ing.") => Good::Ingredients,
_ if name.starts_with("common.items.mineral.") => Good::Ingredients,
_ if name.starts_with("common.items.log.") => Good::Ingredients,
_ if name.starts_with("common.items.flowers.") => Good::Ingredients,
_ if name.starts_with("common.items.consumable.") => Good::Potions,

View File

@ -276,16 +276,16 @@ impl SwordTreeModifiers {
const fn get() -> Self {
Self {
dash: SwordDashModifiers {
energy_cost: 0.9,
energy_drain: 0.9,
base_damage: 1.1,
scaled_damage: 1.1,
forward_speed: 1.05,
energy_cost: 0.95,
energy_drain: 0.95,
base_damage: 1.05,
scaled_damage: 1.05,
forward_speed: 1.025,
},
spin: SwordSpinModifiers {
base_damage: 1.2,
swing_duration: 0.9,
energy_cost: 0.9,
base_damage: 1.1,
swing_duration: 0.95,
energy_cost: 0.95,
num: 1,
},
}
@ -315,15 +315,15 @@ impl AxeTreeModifiers {
const fn get() -> Self {
Self {
spin: AxeSpinModifiers {
base_damage: 1.2,
swing_duration: 0.85,
energy_cost: 0.85,
base_damage: 1.1,
swing_duration: 0.95,
energy_cost: 0.9,
},
leap: AxeLeapModifiers {
base_damage: 1.2,
knockback: 1.2,
energy_cost: 0.75,
leap_strength: 1.1,
base_damage: 1.1,
knockback: 1.1,
energy_cost: 0.85,
leap_strength: 1.05,
},
}
}
@ -359,17 +359,17 @@ impl HammerTreeModifiers {
Self {
single_strike: HammerStrikeModifiers { knockback: 1.25 },
charged: HammerChargedModifers {
scaled_damage: 1.2,
scaled_knockback: 1.3,
energy_drain: 0.85,
charge_rate: 1.15,
scaled_damage: 1.1,
scaled_knockback: 1.15,
energy_drain: 0.95,
charge_rate: 1.1,
},
leap: HammerLeapModifiers {
base_damage: 1.25,
knockback: 1.3,
energy_cost: 0.75,
leap_strength: 1.1,
range: 0.5,
base_damage: 1.15,
knockback: 1.15,
energy_cost: 0.85,
leap_strength: 1.05,
range: 0.25,
},
}
}
@ -412,25 +412,25 @@ impl BowTreeModifiers {
const fn get() -> Self {
Self {
universal: BowUniversalModifiers {
projectile_speed: 1.1,
projectile_speed: 1.05,
},
charged: BowChargedModifiers {
damage_scaling: 1.1,
regen_scaling: 1.1,
knockback_scaling: 1.1,
charge_rate: 1.1,
move_speed: 1.1,
damage_scaling: 1.05,
regen_scaling: 1.05,
knockback_scaling: 1.05,
charge_rate: 1.05,
move_speed: 1.05,
},
repeater: BowRepeaterModifiers {
power: 1.1,
energy_cost: 0.9,
max_speed: 1.2,
power: 1.05,
energy_cost: 0.95,
max_speed: 1.1,
},
shotgun: BowShotgunModifiers {
power: 1.1,
energy_cost: 0.9,
power: 1.05,
energy_cost: 0.95,
num_projectiles: 1,
spread: 0.9,
spread: 0.95,
},
}
}
@ -466,21 +466,21 @@ impl StaffTreeModifiers {
const fn get() -> Self {
Self {
fireball: StaffFireballModifiers {
power: 1.1,
regen: 1.1,
range: 1.1,
power: 1.05,
regen: 1.05,
range: 1.05,
},
flamethrower: StaffFlamethrowerModifiers {
damage: 1.2,
range: 1.1,
energy_drain: 0.9,
velocity: 1.1,
damage: 1.1,
range: 1.05,
energy_drain: 0.95,
velocity: 1.05,
},
shockwave: StaffShockwaveModifiers {
damage: 1.15,
knockback: 1.15,
duration: 1.1,
energy_cost: 0.9,
damage: 1.1,
knockback: 1.05,
duration: 1.05,
energy_cost: 0.95,
},
}
}
@ -517,21 +517,21 @@ impl SceptreTreeModifiers {
const fn get() -> Self {
Self {
beam: SceptreBeamModifiers {
damage: 1.1,
range: 1.1,
energy_regen: 1.1,
damage: 1.05,
range: 1.05,
energy_regen: 1.05,
lifesteal: 1.05,
},
healing_aura: SceptreHealingAuraModifiers {
strength: 1.05,
duration: 1.1,
range: 1.1,
energy_cost: 0.90,
duration: 1.05,
range: 1.05,
energy_cost: 0.95,
},
warding_aura: SceptreWardingAuraModifiers {
strength: 1.05,
duration: 1.1,
range: 1.1,
duration: 1.05,
range: 1.05,
energy_cost: 0.95,
},
}

View File

@ -228,13 +228,13 @@ pub mod tests {
material,
hands,
} => {
item::modular::random_weapon_primary_component(*tool, *material, *hands, &mut rng).unwrap_or_else(
|_| {
item::modular::random_weapon_primary_component(*tool, *material, *hands, &mut rng)
.unwrap_or_else(|_| {
panic!(
"Failed to synthesize a modular weapon primary component: {tool:?} made of {material:?} that had a hand restriction of {hands:?}."
"Failed to synthesize a modular weapon primary component: {tool:?} \
made of {material:?} that had a hand restriction of {hands:?}."
)
},
);
});
},
}
}

View File

@ -392,7 +392,8 @@ impl SitePrices {
if let Some(vec) = item
.name
.as_ref()
.raw()
// TODO: This won't handle compound items with components well, or pure modular items at all
.itemdef_id()
.and_then(TradePricing::get_materials)
{
vec.iter()

View File

@ -178,7 +178,7 @@ pub fn handle_mine_block(
tool,
state.ecs().uid_from_entity(entity),
item.item_definition_id()
.raw()
.itemdef_id()
.and_then(|id| RESOURCE_EXPERIENCE_MANIFEST.read().0.get(id).copied()),
) {
let skill_group = SkillGroupKind::Weapon(tool);
@ -222,7 +222,7 @@ pub fn handle_mine_block(
rng.gen_bool(chance_mod * f64::from(skill_level))
};
let double_gain = item.item_definition_id().raw().map_or(false, |id| {
let double_gain = item.item_definition_id().itemdef_id().map_or(false, |id| {
(id.contains("mineral.ore.") && need_double_ore(&mut rng))
|| (id.contains("mineral.gem.") && need_double_gem(&mut rng))
});

View File

@ -679,9 +679,9 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
} => {
let component_recipes = default_component_recipe_book().read();
let item_id = |slot| {
inventory
.get(slot)
.and_then(|item| item.item_definition_id().raw().map(String::from))
inventory.get(slot).and_then(|item| {
item.item_definition_id().itemdef_id().map(String::from)
})
};
if let Some(material_item_id) = item_id(material) {
component_recipes

View File

@ -167,6 +167,7 @@ pub fn convert_items_to_database_items(
let persistence_item_id = match item.item_definition_id() {
ItemDefinitionId::Simple(id) => id.to_owned(),
ItemDefinitionId::Compound { simple_base, .. } => simple_base.to_owned(),
ItemDefinitionId::Modular { pseudo_base, .. } => pseudo_base.to_owned(),
};

View File

@ -17,8 +17,7 @@ use common::{
comp::inventory::{
item::{
item_key::ItemKey,
modular,
modular::ModularComponent,
modular::{self, ModularComponent},
tool::{AbilityMap, ToolKind},
Item, ItemBase, ItemDef, ItemDesc, ItemKind, ItemTag, MaterialStatManifest, Quality,
TagExampleInfo,
@ -257,6 +256,10 @@ impl CraftingTab {
CraftingTab::Utility => item.tags().contains(&ItemTag::Utility),
CraftingTab::Weapon => match &*item.kind() {
ItemKind::Tool(_) => !item.tags().contains(&ItemTag::CraftingTool),
ItemKind::ModularComponent(
ModularComponent::ToolPrimaryComponent { .. }
| ModularComponent::ToolSecondaryComponent { .. },
) => true,
_ => false,
},
}
@ -900,7 +903,7 @@ impl<'a> Widget for Crafting<'a> {
.filter(|(key, _)| key.toolkind == toolkind)
.any(|(key, _)| {
Some(key.material.as_str())
== item.item_definition_id().raw()
== item.item_definition_id().itemdef_id()
})
})
} else {
@ -991,7 +994,7 @@ impl<'a> Widget for Crafting<'a> {
.filter(|(key, _)| key.toolkind == toolkind)
.any(|(key, _)| {
key.modifier.as_deref()
== item.item_definition_id().raw()
== item.item_definition_id().itemdef_id()
})
})
} else {
@ -1143,7 +1146,9 @@ impl<'a> Widget for Crafting<'a> {
if let Some(material) = primary_slot
.invslot
.and_then(|slot| self.inventory.get(slot))
.and_then(|item| item.item_definition_id().raw().map(String::from))
.and_then(|item| {
item.item_definition_id().itemdef_id().map(String::from)
})
{
let component_key = ComponentKey {
toolkind,
@ -1152,7 +1157,7 @@ impl<'a> Widget for Crafting<'a> {
.invslot
.and_then(|slot| self.inventory.get(slot))
.and_then(|item| {
item.item_definition_id().raw().map(String::from)
item.item_definition_id().itemdef_id().map(String::from)
}),
};
self.client.component_recipe_book().get(&component_key).map(
@ -1451,14 +1456,16 @@ impl<'a> Widget for Crafting<'a> {
RecipeKind::Component(toolkind) => {
if let Some(material) = modular_primary_slot
.and_then(|slot| self.inventory.get(slot))
.and_then(|item| item.item_definition_id().raw().map(String::from))
.and_then(|item| item.item_definition_id().itemdef_id().map(String::from))
{
let component_key = ComponentKey {
toolkind,
material,
modifier: modular_secondary_slot
.and_then(|slot| self.inventory.get(slot))
.and_then(|item| item.item_definition_id().raw().map(String::from)),
.and_then(|item| {
item.item_definition_id().itemdef_id().map(String::from)
}),
};
if let Some(comp_recipe) =
self.client.component_recipe_book().get(&component_key)
@ -1544,7 +1551,7 @@ impl<'a> Widget for Crafting<'a> {
slot.as_ref().and_then(|item| {
if item.matches_recipe_input(recipe_input, amount) {
item.item_definition_id()
.raw()
.itemdef_id()
.map(Arc::<ItemDef>::load_expect_cloned)
} else {
None
@ -1562,7 +1569,7 @@ impl<'a> Widget for Crafting<'a> {
slot.as_ref().and_then(|item| {
if item.matches_recipe_input(recipe_input, amount) {
item.item_definition_id()
.raw()
.itemdef_id()
.map(Arc::<ItemDef>::load_expect_cloned)
} else {
None
@ -1572,7 +1579,7 @@ impl<'a> Widget for Crafting<'a> {
.or_else(|| {
item_defs.first().and_then(|i| {
i.item_definition_id()
.raw()
.itemdef_id()
.map(Arc::<ItemDef>::load_expect_cloned)
})
}),

View File

@ -6,7 +6,7 @@ use super::{
};
use crate::ui::{fonts::Fonts, ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable};
use client::Client;
use common::comp::inventory::item::{ItemDef, ItemDesc, MaterialStatManifest, Quality};
use common::comp::inventory::item::{Item, ItemDesc, MaterialStatManifest, Quality};
use conrod_core::{
color,
position::Dimension,
@ -14,7 +14,7 @@ use conrod_core::{
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
};
use i18n::Localization;
use std::{collections::VecDeque, sync::Arc};
use std::collections::VecDeque;
widget_ids! {
struct Ids{
@ -97,7 +97,7 @@ impl<'a> LootScroller<'a> {
#[derive(Debug, PartialEq)]
pub struct LootMessage {
pub item: Arc<ItemDef>,
pub item: Item,
pub amount: u32,
}
@ -162,11 +162,9 @@ impl<'a> Widget for LootScroller<'a> {
state.update(|s| {
s.messages.retain(|(message, t)| {
if *t >= oldest_merge_pulse {
if let Some(i) = self
.new_messages
.iter()
.position(|m| m.item.id() == message.item.id())
{
if let Some(i) = self.new_messages.iter().position(|m| {
m.item.item_definition_id() == message.item.item_definition_id()
}) {
self.new_messages[i].amount += message.amount;
false
} else {
@ -310,7 +308,7 @@ impl<'a> Widget for LootScroller<'a> {
Quality::Artifact => self.imgs.inv_slot_orange,
_ => self.imgs.inv_slot_red,
};
let quality_col = get_quality_col(&**item);
let quality_col = get_quality_col(&*item);
Image::new(self.imgs.pixel)
.color(Some(shade_color(quality_col.alpha(0.7))))
@ -325,7 +323,7 @@ impl<'a> Widget for LootScroller<'a> {
.set(state.ids.message_icon_frames[i], ui);
Image::new(animate_by_pulse(
&self.item_imgs.img_ids_or_not_found_img((&**item).into()),
&self.item_imgs.img_ids_or_not_found_img((&*item).into()),
self.pulse,
))
.color(Some(shade_color(color::hsla(0.0, 0.0, 1.0, 1.0))))
@ -333,7 +331,7 @@ impl<'a> Widget for LootScroller<'a> {
.middle_of(state.ids.message_icon_bgs[i])
.with_item_tooltip(
self.item_tooltip_manager,
core::iter::once(&**item as &dyn ItemDesc),
core::iter::once(&*item as &dyn ItemDesc),
&None,
&item_tooltip,
)

View File

@ -3693,7 +3693,7 @@ impl Hud {
if let Some(item) = inventory.get(slot) {
if let Some(materials) = item
.item_definition_id()
.raw()
.itemdef_id()
.and_then(TradePricing::get_materials)
{
let unit_price: f32 = materials

View File

@ -226,6 +226,9 @@ impl CharacterCacheKey {
ItemDefinitionId::Modular { .. } => {
ToolKey::Modular(modular::weapon_to_key(item))
},
ItemDefinitionId::Compound { simple_base, .. } => {
ToolKey::Tool(String::from(simple_base))
},
};
Some(CharacterToolKey {
active: inventory

View File

@ -1340,7 +1340,7 @@ impl<'a> Widget for ItemTooltip<'a> {
// Price display
if let Some((buy, sell, factor)) = item
.item_definition_id()
.raw()
.itemdef_id()
.and_then(|id| util::price_desc(self.prices, id, i18n))
{
widget::Text::new(&buy)
@ -1439,7 +1439,7 @@ impl<'a> Widget for ItemTooltip<'a> {
// Price
let price_h: f64 = if let Some((buy, sell, _)) = item
.item_definition_id()
.raw()
.itemdef_id()
.and_then(|id| util::price_desc(self.prices, id, self.localized_strings))
{
//Get localized tooltip strings (gotten here because these should only show if