288 lines
8.5 KiB
Rust
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())
|
|
}
|
|
}
|