blastmud/blastmud_game/src/models/item.rs

608 lines
16 KiB
Rust
Raw Normal View History

use super::session::Session;
2023-01-22 22:43:44 +11:00
use crate::{
language,
regular_tasks::queued_command::QueueCommand,
static_content::{
fixed_item::fixed_item_properties,
possession_type::{possession_data, EatData, PossessionData, PossessionType},
room::Direction,
species::SpeciesType,
},
2023-01-22 22:43:44 +11:00
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::collections::VecDeque;
2022-12-26 01:30:59 +11:00
2023-05-28 21:59:09 +10:00
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, PartialOrd)]
2022-12-26 01:30:59 +11:00
pub enum BuffCause {
WaitingTask {
task_code: String,
task_type: String,
},
ByItem {
item_code: String,
item_type: String,
},
2022-12-26 01:30:59 +11:00
}
2023-05-28 21:59:09 +10:00
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
2022-12-26 01:30:59 +11:00
pub enum BuffImpact {
2023-05-28 21:59:09 +10:00
ChangeStat { stat: StatType, magnitude: f64 },
ChangeSkill { skill: SkillType, magnitude: f64 },
2022-12-26 01:30:59 +11:00
}
2023-05-28 21:59:09 +10:00
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
2022-12-26 01:30:59 +11:00
pub struct Buff {
2023-01-20 23:08:40 +11:00
pub description: String,
pub cause: BuffCause,
pub impacts: Vec<BuffImpact>,
2022-12-26 01:30:59 +11:00
}
2023-01-15 23:16:02 +11:00
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum SkillType {
2023-01-20 23:08:40 +11:00
Appraise,
2023-01-15 23:16:02 +11:00
Blades,
Bombs,
Chemistry,
Climb,
Clubs,
Craft,
Dodge,
Fish,
Fists,
Flails,
2023-01-20 23:08:40 +11:00
Focus,
2023-01-15 23:16:02 +11:00
Fuck,
Hack,
Locksmith,
Medic,
Persuade,
Pilot,
Pistols,
Quickdraw,
Repair,
Ride,
Rifles,
Scavenge,
Science,
Sneak,
Spears,
Swim,
Teach,
Throw,
Track,
Wrestle,
Whips,
2023-01-15 23:16:02 +11:00
}
2023-01-20 23:08:40 +11:00
impl SkillType {
pub fn values() -> Vec<SkillType> {
use SkillType::*;
vec![
Appraise, Blades, Bombs, Chemistry, Climb, Clubs, Craft, Dodge, Fish, Fists, Flails,
Focus, Fuck, Hack, Locksmith, Medic, Persuade, Pilot, Pistols, Quickdraw, Repair, Ride,
Rifles, Scavenge, Science, Sneak, Spears, Swim, Teach, Throw, Track, Wrestle, Whips,
]
2023-01-20 23:08:40 +11:00
}
pub fn display(&self) -> &'static str {
use SkillType::*;
match self {
Appraise => "appraise",
Blades => "blades",
Bombs => "bombs",
Chemistry => "chemistry",
Climb => "climb",
Clubs => "clubs",
Craft => "craft",
Dodge => "dodge",
Fish => "fish",
Fists => "fists",
Flails => "flails",
Focus => "focus",
Fuck => "fuck",
Hack => "hack",
Locksmith => "locksmith",
Medic => "medic",
Persuade => "persuade",
Pilot => "pilot",
Pistols => "pistols",
Quickdraw => "quickdraw",
Repair => "repair",
Ride => "ride",
Rifles => "rifles",
Scavenge => "scavenge",
Science => "science",
Sneak => "sneak",
Spears => "spears",
Swim => "swim",
Teach => "teach",
Throw => "throw",
Track => "track",
Wrestle => "wrestle",
Whips => "whips",
2023-01-20 23:08:40 +11:00
}
}
}
2023-01-15 23:16:02 +11:00
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum StatType {
Brains,
Senses,
Brawn,
Reflexes,
Endurance,
Cool,
2023-01-15 23:16:02 +11:00
}
2023-01-20 23:08:40 +11:00
impl StatType {
pub fn values() -> Vec<Self> {
use StatType::*;
vec![Brains, Senses, Brawn, Reflexes, Endurance, Cool]
2023-01-20 23:08:40 +11:00
}
2023-02-26 22:34:26 +11:00
pub fn display(&self) -> &'static str {
use StatType::*;
match self {
Brains => "brains",
Senses => "senses",
Brawn => "brawn",
Reflexes => "reflexes",
Endurance => "endurance",
Cool => "cool",
2023-02-26 22:34:26 +11:00
}
}
2023-01-20 23:08:40 +11:00
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum LiquidType {
Water,
}
impl LiquidType {
pub fn drink_data(&self) -> Option<EatData> {
match self {
LiquidType::Water => Some(EatData {
hunger_impact: 0,
thirst_impact: -1, // 0.01% per mL
}),
}
}
pub fn display(&self) -> &str {
match self {
LiquidType::Water => "water",
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct LiquidDetails {
// In mLs...
pub contents: BTreeMap<LiquidType, u64>,
}
impl Default for LiquidDetails {
fn default() -> Self {
Self {
contents: BTreeMap::<LiquidType, u64>::new(),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Pronouns {
pub subject: String,
pub object: String,
pub intensive: String,
pub possessive: String,
// And some miscellaneous details to determine context
pub is_plural: bool, // ... are instead of ... is
pub is_proper: bool, // When naming, just ... instead of The ...
}
impl Pronouns {
pub fn default_inanimate() -> Pronouns {
Pronouns {
subject: "it".to_owned(),
object: "it".to_owned(),
intensive: "itself".to_owned(),
possessive: "its".to_owned(),
is_plural: false,
is_proper: true,
}
}
pub fn default_animate() -> Pronouns {
Pronouns {
subject: "they".to_owned(),
object: "them".to_owned(),
intensive: "themselves".to_owned(),
possessive: "their".to_owned(),
is_plural: true,
is_proper: true,
}
}
#[allow(dead_code)]
pub fn default_male() -> Pronouns {
Pronouns {
subject: "he".to_owned(),
object: "him".to_owned(),
intensive: "himself".to_owned(),
possessive: "his".to_owned(),
is_plural: false,
is_proper: true,
}
}
#[allow(dead_code)]
pub fn default_female() -> Pronouns {
Pronouns {
subject: "she".to_owned(),
object: "her".to_owned(),
intensive: "herself".to_owned(),
possessive: "her".to_owned(),
is_plural: false,
is_proper: true,
}
}
}
2022-12-26 01:30:59 +11:00
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Subattack {
Normal,
Powerattacking,
Feinting,
Grabbing,
Wrestling,
2022-12-26 01:30:59 +11:00
}
2022-12-31 19:33:31 +11:00
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
2022-12-26 01:30:59 +11:00
pub enum LocationActionType {
Normal,
Sitting,
Reclining,
Worn, // Clothing etc...
Wielded,
2023-04-16 01:54:03 +10:00
Attacking(Subattack),
InstalledOnDoorAsLock(Direction),
2022-12-26 01:30:59 +11:00
}
2023-04-21 23:33:23 +10:00
impl LocationActionType {
pub fn is_visible_in_look(&self) -> bool {
use LocationActionType::*;
match self {
InstalledOnDoorAsLock(_) => false,
_ => true,
2023-04-21 23:33:23 +10:00
}
}
pub fn is_in_direction(&self, dir: &Direction) -> bool {
use LocationActionType::*;
match self {
InstalledOnDoorAsLock(d) if d == dir => true,
_ => false,
2023-04-21 23:33:23 +10:00
}
}
}
2022-12-31 19:33:31 +11:00
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Sex {
Male,
Female,
}
2022-12-31 19:33:31 +11:00
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum ItemFlag {
2023-01-08 00:18:56 +11:00
NoSay,
NoSeeContents,
2023-04-16 01:54:03 +10:00
DroppedItemsDontExpire,
PrivatePlace,
Hireable,
NPCsDontAttack,
CanLoad,
Bench,
Book,
Instructions,
HasUrges,
NoUrgesHere,
}
2023-01-15 17:30:23 +11:00
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[serde(default)]
pub struct ActiveCombat {
pub attacking: Option<String>,
pub attacked_by: Vec<String>,
2023-01-15 17:30:23 +11:00
}
impl Default for ActiveCombat {
fn default() -> Self {
Self {
attacking: None,
attacked_by: vec![],
2023-01-15 17:30:23 +11:00
}
}
}
2023-05-21 22:31:52 +10:00
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[serde(default)]
pub struct ActiveClimb {
pub height: u64,
2023-05-21 22:31:52 +10:00
}
impl Default for ActiveClimb {
fn default() -> Self {
Self { height: 0 }
2023-05-21 22:31:52 +10:00
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[serde(default)]
pub struct Urge {
pub last_value: u16,
pub value: u16, // In hundreths of a percent (0-10000).
pub growth: i16,
}
impl Default for Urge {
fn default() -> Self {
Self {
last_value: 0,
value: 0,
growth: 42, // About 4 hours of once a minute ticks to hit 10k.
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
#[serde(default)]
pub struct Urges {
pub hunger: Urge,
pub bladder: Urge,
pub thirst: Urge,
pub stress: Urge,
}
impl Default for Urges {
fn default() -> Self {
Self {
hunger: Default::default(),
bladder: Default::default(),
thirst: Default::default(),
stress: Default::default(),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
pub enum ItemSpecialData {
ItemWriting {
text: String,
},
DynroomData {
dynzone_code: String,
dynroom_code: String,
},
DynzoneData {
zone_exit: Option<String>,
vacate_after: Option<DateTime<Utc>>,
},
HireData {
hired_by: Option<String>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
pub struct DynamicEntrance {
pub direction: Direction,
pub source_item: String,
}
2023-04-16 01:54:03 +10:00
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
2023-04-16 22:12:19 +10:00
#[serde(default)]
2023-04-16 01:54:03 +10:00
pub struct DoorState {
pub open: bool,
pub description: String,
2023-04-16 22:12:19 +10:00
}
impl Default for DoorState {
fn default() -> Self {
Self {
open: false,
description: "a solid looking wooden door".to_owned(),
}
}
2023-04-16 01:54:03 +10:00
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
#[serde(default)]
pub struct DeathData {
pub parts_remaining: Vec<PossessionType>,
}
impl Default for DeathData {
fn default() -> Self {
Self {
parts_remaining: vec![],
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
pub enum FollowState {
// Every move is mirrored to the follower.
Active,
// If the followee is in the same room, mirror a movement and go to Active,
// otherwise ignore and don't mirror. This happens after a mirrored move fails,
// or the follower moves independently.
IfSameRoom,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
pub struct FollowData {
pub follow_whom: String,
pub state: FollowState,
}
2023-01-20 23:08:40 +11:00
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
#[serde(default)]
2022-12-26 01:30:59 +11:00
pub struct Item {
pub item_code: String,
pub item_type: String,
2023-01-30 22:28:43 +11:00
pub possession_type: Option<PossessionType>,
pub display: String,
pub display_less_explicit: Option<String>,
2022-12-29 18:44:50 +11:00
pub details: Option<String>,
pub details_less_explicit: Option<String>,
2023-01-20 23:38:57 +11:00
pub aliases: Vec<String>,
2022-12-26 01:30:59 +11:00
pub location: String, // Item reference as item_type/item_code.
pub action_type: LocationActionType,
pub action_type_started: Option<DateTime<Utc>>,
2022-12-26 01:30:59 +11:00
pub presence_target: Option<String>, // e.g. what are they sitting on.
pub is_static: bool,
pub death_data: Option<DeathData>,
2023-01-22 22:43:44 +11:00
pub species: SpeciesType,
pub health: u64,
2022-12-26 01:30:59 +11:00
pub total_xp: u64,
2023-01-20 23:08:40 +11:00
pub total_stats: BTreeMap<StatType, f64>,
pub total_skills: BTreeMap<SkillType, f64>,
2022-12-26 01:30:59 +11:00
pub temporary_buffs: Vec<Buff>,
pub pronouns: Pronouns,
pub flags: Vec<ItemFlag>,
pub sex: Option<Sex>,
2023-01-15 17:30:23 +11:00
pub active_combat: Option<ActiveCombat>,
2023-05-21 22:31:52 +10:00
pub active_climb: Option<ActiveClimb>,
2023-02-03 23:26:24 +11:00
pub weight: u64,
pub charges: u8,
pub special_data: Option<ItemSpecialData>,
pub dynamic_entrance: Option<DynamicEntrance>,
pub owner: Option<String>,
pub door_states: Option<BTreeMap<Direction, DoorState>>,
pub following: Option<FollowData>,
pub queue: VecDeque<QueueCommand>,
pub urges: Option<Urges>,
pub liquid_details: Option<LiquidDetails>,
}
impl Item {
2023-01-15 17:30:23 +11:00
pub fn display_for_sentence(&self, explicit_ok: bool, pluralise: usize, caps: bool) -> String {
let mut buf = String::new();
if self.death_data.is_some() {
2023-01-15 17:30:23 +11:00
if pluralise > 1 {
buf.push_str("the bodies of ");
} else {
buf.push_str("the body of ");
}
}
let singular = if explicit_ok {
&self.display
} else {
self.display_less_explicit.as_ref().unwrap_or(&self.display)
};
2023-01-15 17:30:23 +11:00
if !self.pronouns.is_proper && pluralise == 1 {
buf.push_str(language::indefinite_article(&singular));
buf.push(' ');
2023-01-15 17:30:23 +11:00
}
if pluralise > 1 {
buf.push_str(&format!("{} ", pluralise));
}
if pluralise > 1 {
buf.push_str(&language::pluralise(singular));
} else {
buf.push_str(singular);
}
if caps {
language::caps_first(&buf)
} else {
buf
}
}
pub fn display_for_session<'l>(self: &'l Self, session: &Session) -> String {
self.display_for_sentence(!session.less_explicit_mode, 1, false)
}
pub fn details_for_session<'l>(self: &'l Self, session: &Session) -> Option<&'l str> {
self.details.as_ref().map(|dets| {
session.explicit_if_allowed(
dets.as_str(),
self.details_less_explicit.as_ref().map(String::as_str),
)
})
}
2023-04-16 01:54:03 +10:00
pub fn refstr(&self) -> String {
format!("{}/{}", &self.item_type, &self.item_code)
}
pub fn max_carry(&self) -> u64 {
if self.item_type == "possession" {
if let Some(container_data) = self.possession_type.as_ref().and_then(|pt| {
possession_data()
.get(pt)
.and_then(|pd| pd.container_data.as_ref())
}) {
container_data.max_weight
} else {
0
}
} else {
(50.0 * 2.0_f64.powf(*self.total_stats.get(&StatType::Brawn).unwrap_or(&0.0))).ceil()
as u64
}
}
pub fn static_data(&self) -> Option<&'static PossessionData> {
if self.item_type == "possession" {
self.possession_type
.as_ref()
.and_then(|pt| possession_data().get(pt))
.copied()
} else if self.item_type == "fixed_item" {
fixed_item_properties().get(self.item_code.as_str())
} else {
None
}
}
2022-12-26 01:30:59 +11:00
}
impl Default for Item {
fn default() -> Self {
2023-01-15 17:30:23 +11:00
Self {
item_code: "unset".to_owned(),
item_type: "unset".to_owned(),
2023-01-30 22:28:43 +11:00
possession_type: None,
display: "Item".to_owned(),
display_less_explicit: None,
2022-12-29 18:44:50 +11:00
details: None,
details_less_explicit: None,
aliases: vec![],
location: "room/storage".to_owned(),
action_type: LocationActionType::Normal,
action_type_started: None,
presence_target: None,
is_static: false,
death_data: None,
2023-01-22 22:43:44 +11:00
species: SpeciesType::Human,
2023-01-23 22:52:01 +11:00
health: 24,
total_xp: 0,
total_stats: BTreeMap::new(),
total_skills: BTreeMap::new(),
temporary_buffs: Vec::new(),
pronouns: Pronouns::default_inanimate(),
flags: vec![],
2023-01-15 17:30:23 +11:00
sex: None,
active_combat: Some(Default::default()),
2023-05-21 22:31:52 +10:00
active_climb: None,
2023-02-03 23:26:24 +11:00
weight: 0,
2023-02-23 22:55:02 +11:00
charges: 0,
special_data: None,
dynamic_entrance: None,
owner: None,
2023-04-16 01:54:03 +10:00
door_states: None,
following: None,
queue: VecDeque::new(),
urges: None,
liquid_details: None,
}
}
}