engine/rpg_components/src/items/item.rs
2024-08-27 14:06:06 +02:00

343 lines
11 KiB
Rust

use anyhow::Result;
use engine::prelude::*;
use std::str::FromStr;
use std::sync::Arc;
use crate::{
components::{
attributes::Attributes,
inventory::Storable,
statistic_types::{
AgilityStatisticTypes, IntelligenceStatisticTypes, StatisticType,
StrengthStatisticTypes,
},
},
config::items::ItemSettings,
};
use super::{ability_book::Ability, ItemSlots, ItemSystem, Jewel, Rarities, Tooltip};
const ITEM_SNIPPETS: [&'static str; 8] = [
include_str!("../../resources/items/slots_0.xml"),
include_str!("../../resources/items/slots_1.xml"),
include_str!("../../resources/items/slots_2.xml"),
include_str!("../../resources/items/slots_3.xml"),
include_str!("../../resources/items/slots_4.xml"),
include_str!("../../resources/items/slots_5.xml"),
include_str!("../../resources/items/slots_6.xml"),
include_str!("../../resources/items/slots_7.xml"),
];
#[derive(Debug, PartialEq, Clone)]
pub enum ItemAffix {
Socket(Option<Jewel>),
Stat(StatisticType),
}
impl ItemAffix {
pub fn from_persistent<'a>(
affix_type: &str,
split: &mut impl Iterator<Item = &'a str>,
) -> Result<Self> {
Ok(match affix_type {
"socket" => {
let socket_content = split.next().unwrap();
match socket_content {
"empty" => ItemAffix::Socket(None),
"some" => ItemAffix::Socket(Some(Jewel::from_persistent(split)?)),
_ => unreachable!(),
}
}
"stat" => ItemAffix::Stat(StatisticType::from_str(split.next().unwrap())?),
_ => unreachable!(),
})
}
fn squash<'a>(v: impl Iterator<Item = &'a Self>) -> (Vec<StatisticType>, Vec<Option<Jewel>>) {
let mut jewels = Vec::new();
let mut stats = Vec::new();
for affix in v {
match affix {
ItemAffix::Socket(j) => jewels.push(j.clone()),
ItemAffix::Stat(s) => Self::insert_stat(s, &mut stats),
}
}
(stats, jewels)
}
fn insert_stat(val: &StatisticType, vec: &mut Vec<StatisticType>) {
match vec
.iter_mut()
.find(|stat| StatisticType::same_type(stat, val))
{
Some(stat) => {
*stat += val.clone();
}
None => vec.push(val.clone()),
}
}
}
impl std::fmt::Display for ItemAffix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ItemAffix::Socket(jewel) => match jewel {
Some(jewel) => write!(f, "socket|some|{}", jewel.into_persistent()),
None => write!(f, "socket|empty"),
},
ItemAffix::Stat(stat) => write!(f, "stat|{}", stat),
}
}
}
impl FromStr for ItemAffix {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self> {
let mut split = s.split('|');
let affix_type = split.next().unwrap();
Self::from_persistent(affix_type, &mut split)
}
}
#[derive(Debug, Clone)]
pub struct Item {
pub rarity: Rarities,
pub slot: ItemSlots,
pub level: u32,
pub attributes: Attributes,
pub affixes: Vec<ItemAffix>,
pub icon: Arc<Image>,
}
impl Item {
const INSPECTOR_OFFSET: usize = 3;
pub fn empty(rarity: Rarities, slot: ItemSlots, level: u32, icon: Arc<Image>) -> Self {
Self {
rarity,
slot,
level,
attributes: Attributes::zero(),
affixes: Vec::new(),
icon,
}
}
pub fn into_persistent(&self) -> String {
let mut base = format!(
"{}|{}|{}|{}|{}|{}",
self.slot,
self.rarity,
self.level,
self.attributes.strength().raw(),
self.attributes.agility().raw(),
self.attributes.intelligence().raw(),
);
for affix in self.affixes.iter() {
base = format!("{base}|{affix}");
}
base
}
pub fn from_persistent<'a, A: Ability>(
mut split: impl Iterator<Item = &'a str>,
item_system: &ItemSystem<A>,
) -> Result<Self> {
let slot = ItemSlots::from_str(split.next().unwrap())?;
let rarity = Rarities::from_str(split.next().unwrap())?;
let level = split.next().unwrap().parse::<u32>()?;
let strength = split.next().unwrap().parse::<u32>()?;
let agility = split.next().unwrap().parse::<u32>()?;
let intelligence = split.next().unwrap().parse::<u32>()?;
let mut affixes = Vec::new();
while let Some(affix_type) = split.next() {
affixes.push({
let mut affix = ItemAffix::from_persistent(affix_type, &mut split)?;
if let ItemAffix::Socket(Some(jewel)) = &mut affix {
jewel.icon =
Some(item_system.jewel_icon(jewel.rarity, jewel.level, jewel.attribute));
}
affix
});
}
let attributes = Attributes::load(strength, agility, intelligence);
Ok(item_system.item(rarity, slot, level, attributes, affixes))
}
pub fn randomize_attributes(&mut self, item_settings: &ItemSettings) {
// sum of stats is the level
self.attributes
.randomize((self.level as f32 * item_settings.general.attribute_multiplier) as u32);
}
fn stat_type(&self, attribute_number: u32) -> Option<StatisticType> {
if attribute_number <= self.attributes.strength().raw()
&& self.attributes.strength().raw() != 0
{
return Some(StrengthStatisticTypes::random().into());
} else if attribute_number
<= (self.attributes.strength().raw() + self.attributes.agility().raw())
&& self.attributes.agility().raw() != 0
{
return Some(AgilityStatisticTypes::random().into());
} else if self.attributes.intelligence().raw() != 0 {
return Some(IntelligenceStatisticTypes::random().into());
}
None
}
pub fn randomize_stats(&mut self, item_settings: &ItemSettings) {
self.affixes = (0..item_settings.rarity_slot_settings.from_rarity(self.rarity))
.map(|_| {
if Random::range_f32(0.0, 1.0) < 0.05 {
ItemAffix::Socket(None)
} else {
let mut stat_type = None;
while stat_type.is_none() {
stat_type = self.stat_type(Random::range(0, self.attributes.sum()));
}
let mut stat_type = stat_type.unwrap();
stat_type.apply_for_item(&self.attributes, item_settings);
ItemAffix::Stat(stat_type)
}
})
.collect();
}
pub fn create_tooltip(
&self,
gui_handler: &Arc<GuiHandler>,
attributes: &Attributes,
position: (i32, i32),
) -> Result<Tooltip> {
let (stats, jewels) = ItemAffix::squash(self.affixes.iter());
let count = stats.len() + jewels.len();
let inspector_snippet = GuiBuilder::from_str(gui_handler, &ITEM_SNIPPETS[count])?;
let item_icon: Arc<Icon> = inspector_snippet.element("item_icon")?;
let slot_label: Arc<Label> = inspector_snippet.element("slot_label")?;
let level_label: Arc<Label> = inspector_snippet.element("level_label")?;
let rarity_label: Arc<Label> = inspector_snippet.element("rarity_label")?;
let strength_field: Arc<Label> = inspector_snippet.element("strength_field")?;
let agility_field: Arc<Label> = inspector_snippet.element("agility_field")?;
let intelligence_field: Arc<Label> = inspector_snippet.element("intelligence_field")?;
let inspector_grid: Arc<Grid> = inspector_snippet.element("item_grid")?;
inspector_grid.change_position_unscaled(position.0, position.1)?;
item_icon.set_icon(&self.icon)?;
slot_label.set_text(&format!("{}", self.slot))?;
level_label.set_text(&format!("Lvl {}", self.level))?;
rarity_label.set_text(&format!("{}", self.rarity))?;
strength_field.set_text(&format!("{}", self.attributes.strength().raw()))?;
agility_field.set_text(&format!("{}", self.attributes.agility().raw()))?;
intelligence_field.set_text(&format!("{}", self.attributes.intelligence().raw()))?;
if attributes.strength().raw() < self.attributes.strength().raw() {
strength_field.set_background(Color::try_from("#AB7474")?)?;
}
if attributes.agility().raw() < self.attributes.agility().raw() {
agility_field.set_background(Color::try_from("#AB7474")?)?;
}
if attributes.intelligence().raw() < self.attributes.intelligence().raw() {
intelligence_field.set_background(Color::try_from("#AB7474")?)?;
}
let mut index = Self::INSPECTOR_OFFSET;
for stat in stats {
let stat_type_snippet = GuiSnippet::from_str(
gui_handler,
include_str!("../../resources/stat_type_snippet.xml"),
)?;
let stat_type_label: Arc<Label> = stat_type_snippet.element("stat_type")?;
let stat_value_label: Arc<Label> = stat_type_snippet.element("stat_value")?;
stat_type_label.set_text(&format!("{}", stat))?;
stat_value_label.set_text(&stat.display_value())?;
inspector_grid.attach(stat_type_snippet, 0, index, 1, 1)?;
index += 1;
}
for jewel in jewels {
let socket_snippet = GuiSnippet::from_str(
gui_handler,
include_str!("../../resources/item_socket_snippet.xml"),
)?;
let socket_icon: Arc<Icon> = socket_snippet.element("socket_icon")?;
let stat_type: Arc<Label> = socket_snippet.element("stat_type")?;
match jewel {
Some(jewel) => {
socket_icon.set_icon(&jewel.icon())?;
stat_type.set_text(format!("{}", jewel.stat))?;
}
None => stat_type.set_text("Empty Socket")?,
}
inspector_grid.attach(socket_snippet, 0, index, 1, 1)?;
index += 1;
}
Ok(Tooltip::new(
inspector_grid,
inspector_snippet,
gui_handler.clone(),
))
}
}
impl PartialEq for Item {
fn eq(&self, other: &Self) -> bool {
self.rarity == other.rarity
&& self.slot == other.slot
&& self.level == other.level
&& self.attributes == other.attributes
&& self.affixes == other.affixes
}
}
impl Storable for Item {
fn rarity(&self) -> Rarities {
self.rarity
}
fn icon(&self) -> Arc<Image> {
self.icon.clone()
}
}