288 lines
8.5 KiB
Rust

use poise::serenity_prelude::utils::MessageBuilder;
use serde::{Deserialize, Serialize};
use std::collections::hash_map::DefaultHasher;
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
use std::str::FromStr;
#[derive(Debug, Clone)]
pub enum InventoryError {
NotEnoughItems,
UnkownItem,
}
impl Display for InventoryError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
InventoryError::NotEnoughItems => write!(f, "Not enough items"),
InventoryError::UnkownItem => write!(f, "Not sure that that is, maybe check Walmart?"),
}
}
}
impl std::error::Error for InventoryError {}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
#[serde(tag = "type", content = "data")]
pub enum ItemData {
Nft(String),
}
#[derive(
Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq, Copy, poise::ChoiceParameter,
)]
#[allow(clippy::upper_case_acronyms)]
pub enum ItemType {
#[name = "Cancel Insurance"]
CancelInsurance,
#[name = "The Concept of Love"]
TheConceptOfLove,
#[name = "Good Fortune"]
GoodFortune,
#[name = "NFT"]
Nft,
#[name = "License to be Horny"]
LicenseToBeHorny,
#[name = "Kill Gun"]
KillGun,
#[name = "Cancel Ray"]
CancelRay,
#[name = "Helmet"]
Helmet,
#[name = "EMP"]
EMP,
}
impl ItemType {
pub fn description(&self) -> String {
match self {
ItemType::CancelInsurance => "Automatically used to prevent being canceled".to_string(),
ItemType::TheConceptOfLove => "Understand!".to_string(),
ItemType::GoodFortune => {
"Used to give a good fortune. `!use good fortune am I not cringe?`".to_string()
}
ItemType::Nft => "A Non Fungible Token on the REAL FREN blockchain network".to_string(),
ItemType::LicenseToBeHorny => "Allows one horny time".to_string(),
ItemType::KillGun => "Used to kill people. `!use kill gun @Austin`".to_string(),
ItemType::CancelRay => "Used to cancel people. `!use cancel ray @Austin`".to_string(),
ItemType::Helmet => "Automatically used to block being killed".to_string(),
ItemType::EMP => "Disables weapons and defenses".to_string(),
}
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Copy)]
#[allow(dead_code)]
pub enum Operation {
Sell,
Buy,
Custom(i64),
}
impl FromStr for ItemType {
type Err = InventoryError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let item = s.to_lowercase().replace(' ', "");
if item.starts_with("cancelinsurance") {
Ok(ItemType::CancelInsurance)
} else if item.starts_with("theconceptoflove") {
Ok(ItemType::TheConceptOfLove)
} else if item.starts_with("goodfortune") {
Ok(ItemType::GoodFortune)
} else if item.starts_with("nft") {
Ok(ItemType::Nft)
} else if item.starts_with("licensetobehorny") {
Ok(ItemType::LicenseToBeHorny)
} else if item.starts_with("killgun") {
Ok(ItemType::KillGun)
} else if item.starts_with("helmet") {
Ok(ItemType::Helmet)
} else if item.starts_with("cancelray") {
Ok(ItemType::CancelRay)
} else if item.starts_with("emp") {
Ok(ItemType::EMP)
} else {
Err(InventoryError::UnkownItem)
}
}
}
impl Display for ItemType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let name = match self {
ItemType::CancelInsurance => "Cancel Insurance".to_string(),
ItemType::TheConceptOfLove => "The Concept of Love".to_string(),
ItemType::GoodFortune => "Good Fortune".to_string(),
ItemType::Nft => "NFT".to_string(),
ItemType::LicenseToBeHorny => "License to be Horny".to_string(),
ItemType::KillGun => "Kill Gun".to_string(),
ItemType::Helmet => "Helmet".to_string(),
ItemType::CancelRay => "Cancel Ray".to_string(),
ItemType::EMP => "EMP".to_string(),
};
write!(f, "{name}")
}
}
pub fn nft_value(s: &String) -> i64 {
let mut hasher = DefaultHasher::new();
s.hash(&mut hasher);
let hash = hasher.finish();
let per_value = (hash as f64) / (u64::MAX as f64);
((2000.0 * per_value) as i64) - 500
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InventorySlot {
pub quantity: i64,
pub item_type: ItemType,
pub item_data: Option<ItemData>,
}
impl InventorySlot {
pub fn value(&self) -> i64 {
match self.item_type {
ItemType::CancelInsurance => 10_000,
ItemType::TheConceptOfLove => 300,
ItemType::GoodFortune => 75,
ItemType::Nft => 250,
ItemType::LicenseToBeHorny => 250,
ItemType::KillGun => 2_000,
ItemType::Helmet => 10_000,
ItemType::CancelRay => 2_000,
ItemType::EMP => 50_000,
}
}
pub fn sell_value(&self) -> i64 {
match self.item_type {
ItemType::Nft => {
if let Some(ItemData::Nft(s)) = &self.item_data {
nft_value(s)
} else {
0
}
}
_ => self.value() / 2,
}
}
pub fn use_cost(&self) -> i64 {
match self.item_type {
ItemType::Nft => 0,
ItemType::Helmet => 0,
ItemType::CancelInsurance => 0,
_ => 1,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct InventoryManager {
pub inventory: Vec<InventorySlot>,
}
impl InventoryManager {
pub fn get_item_mut(&mut self, item_type: ItemType) -> Option<&mut InventorySlot> {
if let Some(inv_ndx) = self.inventory.iter().position(|i| i.item_type == item_type) {
Some(&mut self.inventory[inv_ndx])
} else {
None
}
}
pub fn get_item(&self, item_type: ItemType) -> Option<&InventorySlot> {
if let Some(inv_ndx) = self.inventory.iter().position(|i| i.item_type == item_type) {
Some(&self.inventory[inv_ndx])
} else {
None
}
}
pub fn give_item(&mut self, item_type: ItemType, quantity: i64, item_data: Option<ItemData>) {
match self.get_item_mut(item_type) {
None => self.inventory.push(InventorySlot {
quantity,
item_type,
item_data,
}),
Some(slot) => {
slot.item_data = item_data;
// Only one NFT at a time
if item_type != ItemType::Nft {
slot.quantity += quantity;
} else {
slot.quantity = 1;
}
}
}
}
pub fn try_take_item(
&mut self,
item: ItemType,
quantity: i64,
) -> Result<Option<ItemData>, InventoryError> {
let slot = self.get_item_mut(item);
if let Some(slot) = slot {
if slot.quantity < quantity {
return Err(InventoryError::NotEnoughItems);
}
slot.quantity -= quantity;
Ok(slot.item_data.clone())
} else {
Err(InventoryError::NotEnoughItems)
}
}
pub fn list_items(&self, buy_value: bool) -> String {
let mut msg_builder = MessageBuilder::new();
for item_slot in &self.inventory {
if item_slot.quantity != 0 {
let value = if buy_value {
item_slot.value()
} else {
item_slot.sell_value()
};
msg_builder.push_line(format!(
"* {} [{} coins] (x{})",
item_slot.item_type, value, item_slot.quantity
));
}
}
msg_builder.build()
}
pub fn try_use_item(
&mut self,
item: ItemType,
force: bool,
) -> Result<Option<ItemData>, InventoryError> {
let item_slot = self
.get_item_mut(item)
.ok_or(InventoryError::NotEnoughItems)?;
if item_slot.quantity < item_slot.use_cost() || item_slot.quantity == 0 {
return Err(InventoryError::NotEnoughItems);
}
if force {
item_slot.quantity -= 1;
} else {
item_slot.quantity -= item_slot.use_cost();
}
Ok(item_slot.item_data.clone())
}
}