Let items become something else on exhaustion.

This commit is contained in:
Condorra 2023-03-02 23:25:08 +11:00
parent caa3d84081
commit 73d92e3074
8 changed files with 191 additions and 4 deletions

View File

@ -213,11 +213,33 @@ impl QueueCommandHandler for QueueHandler {
} }
ctx.trans.save_item_model(&player_mut).await?; ctx.trans.save_item_model(&player_mut).await?;
let mut item_mut = (*item).clone();
let mut save_item = false;
if item.possession_type.as_ref() if item.possession_type.as_ref()
.and_then(|poss_type| possession_data().get(&poss_type)) .and_then(|poss_type| possession_data().get(&poss_type))
.and_then(|poss_data| poss_data.charge_data.as_ref()).is_some() { .and_then(|poss_data| poss_data.charge_data.as_ref()).is_some() {
let mut item_mut = (*item).clone();
item_mut.charges -= 1; item_mut.charges -= 1;
save_item = true;
}
if item_mut.charges == 0 {
if let Some((new_poss, new_poss_dat)) = item.possession_type.as_ref()
.and_then(|pt| possession_data().get(pt))
.and_then(|poss_data| poss_data.becomes_on_spent.as_ref())
.and_then(|poss_type| possession_data().get(&poss_type)
.map(|poss_dat| (poss_type, poss_dat)))
{
item_mut.possession_type = Some(new_poss.clone());
item_mut.display = new_poss_dat.display.to_owned();
item_mut.display_less_explicit = new_poss_dat.display_less_explicit.map(|d| d.to_owned());
item_mut.details = Some(new_poss_dat.details.to_owned());
item_mut.details_less_explicit = new_poss_dat.details_less_explicit.map(|d| d.to_owned());
item_mut.aliases = new_poss_dat.aliases.iter().map(|al| (*al).to_owned()).collect();
item_mut.health = new_poss_dat.max_health;
item_mut.weight = new_poss_dat.weight;
save_item = true;
}
}
if save_item {
ctx.trans.save_item_model(&item_mut).await?; ctx.trans.save_item_model(&item_mut).await?;
} }
Ok(()) Ok(())

View File

@ -2,3 +2,4 @@ pub mod session;
pub mod user; pub mod user;
pub mod item; pub mod item;
pub mod task; pub mod task;
pub mod consent;

View File

@ -0,0 +1,36 @@
use serde::{Serialize, Deserialize};
use chrono::{DateTime, Utc};
#[derive(Serialize, Deserialize)]
pub enum ConsentType {
Fight,
Medicine,
Gifts,
Visit,
Sex
}
#[derive(Serialize, Deserialize)]
pub enum ConsentStatus {
PendingAdd, // Added but awaiting other party to ratify by giving matching consent.
Active, // Consent in force, no delete pending.
PendingDelete, // Pending cancellation but other party has to also disallow to ratify.
}
#[derive(Serialize, Deserialize)]
pub struct FightConsent {
status: ConsentStatus,
pending_change: Option<Box<Consent>>,
allow_pick: bool,
freely_revoke: bool,
}
#[derive(Serialize, Deserialize)]
pub struct Consent {
consent_type: ConsentType,
fight_consent: Option<FightConsent>,
expires: Option<DateTime<Utc>>,
only_in: Vec<String>,
allow_private: bool,
until_death: bool,
}

View File

@ -0,0 +1,10 @@
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct Corp {
pub name: String,
// If true, new members get allow_combat on, and members cannot turn
// allow_combat off. This will allow duly authorised corp members to
// consent to combat with other corps, and have it apply to members.
pub allow_combat_required: bool,
}

View File

@ -286,6 +286,11 @@ impl Default for ActiveCombat {
} }
} }
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
pub enum ItemSpecialData {
ItemWriting { text: String }
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
#[serde(default)] #[serde(default)]
pub struct Item { pub struct Item {
@ -314,7 +319,8 @@ pub struct Item {
pub sex: Option<Sex>, pub sex: Option<Sex>,
pub active_combat: Option<ActiveCombat>, pub active_combat: Option<ActiveCombat>,
pub weight: u64, pub weight: u64,
pub charges: u8 pub charges: u8,
pub special_data: Option<ItemSpecialData>,
} }
impl Item { impl Item {
@ -392,6 +398,7 @@ impl Default for Item {
active_combat: Some(Default::default()), active_combat: Some(Default::default()),
weight: 0, weight: 0,
charges: 0, charges: 0,
special_data: None,
} }
} }
} }

View File

@ -1,11 +1,12 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::{ use crate::{
models::item::{SkillType, Item, Pronouns} models::item::{SkillType, Item, ItemSpecialData, Pronouns}
}; };
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use super::species::BodyPart; use super::species::BodyPart;
use ansi::ansi;
pub type AttackMessageChoice = Vec<Box<dyn Fn(&Item, &Item, bool) -> String + 'static + Sync + Send>>; pub type AttackMessageChoice = Vec<Box<dyn Fn(&Item, &Item, bool) -> String + 'static + Sync + Send>>;
pub type AttackMessageChoicePart = Vec<Box<dyn Fn(&Item, &Item, &BodyPart, bool) -> String + 'static + Sync + Send>>; pub type AttackMessageChoicePart = Vec<Box<dyn Fn(&Item, &Item, &BodyPart, bool) -> String + 'static + Sync + Send>>;
@ -111,6 +112,7 @@ pub struct PossessionData {
pub max_health: u64, pub max_health: u64,
pub charge_data: Option<ChargeData>, pub charge_data: Option<ChargeData>,
pub use_data: Option<UseData>, pub use_data: Option<UseData>,
pub becomes_on_spent: Option<PossessionType>,
pub weight: u64, pub weight: u64,
} }
@ -126,6 +128,7 @@ impl Default for PossessionData {
max_health: 10, max_health: 10,
weight: 100, weight: 100,
charge_data: None, charge_data: None,
becomes_on_spent: None,
use_data: None, use_data: None,
} }
} }
@ -161,6 +164,9 @@ pub enum PossessionType {
// Real possessions from here on: // Real possessions from here on:
AntennaWhip, AntennaWhip,
MediumTraumaKit, MediumTraumaKit,
EmptyMedicalBox,
NewCorpLicence,
CertificateOfIncorporation,
} }
impl Into<Item> for PossessionType { impl Into<Item> for PossessionType {
@ -496,6 +502,52 @@ pub fn possession_data() -> &'static BTreeMap<PossessionType, PossessionData> {
}), }),
..Default::default() ..Default::default()
}), }),
becomes_on_spent: Some(EmptyMedicalBox),
..Default::default()
}),
(EmptyMedicalBox, PossessionData {
display: "empty medical box",
details: "An empty box that looks like it once had something medical in it.",
aliases: vec!("box"),
..Default::default()
}),
(NewCorpLicence, PossessionData {
display: "new corp licence",
details: ansi!("A blank form that you can <bold>use<reset> to establish a new corp. It rests on a clipboard with a pencil attached by a chain. There is a space to <bold>write<reset> on it [try <bold>write Blah on licence<reset> followed by <bold>use licence<reset> to create a corp named Blah]"),
aliases: vec!("form", "license", "licence", "new"),
use_data: Some(UseData {
uses_skill: SkillType::Persuade,
diff_level: 4.0,
crit_fail_effects: vec!(),
fail_effects: vec!(),
success_effects: vec!(
UseEffect::BroadcastMessage {
messagef: Box::new(|player, _item, _target| (
format!(
"{} signs a contract establishing Blah as a corp\n",
&player.display_for_sentence(true, 1, true),
),
format!("{} signs a contract establishing Blah as a corp\n",
&player.display_for_sentence(false, 1, true),
)))
},
),
errorf: Box::new(
|item, _target|
match item.special_data {
Some(ItemSpecialData::ItemWriting { .. }) => None,
_ => Some("You have to your corp's name on it first!".to_owned())
}),
..Default::default()
}),
weight: 10,
becomes_on_spent: Some(CertificateOfIncorporation),
..Default::default()
}),
(CertificateOfIncorporation, PossessionData {
display: "certificate of incorporation",
details: "A certificate recording the formation of a corp.",
weight: 10,
..Default::default() ..Default::default()
}), }),
).into_iter().collect() ).into_iter().collect()

View File

@ -2312,10 +2312,41 @@ pub fn room_list() -> Vec<Room> {
target: ExitTarget::UseGPS, target: ExitTarget::UseGPS,
exit_type: ExitType::Free exit_type: ExitType::Free
}, },
Exit {
direction: Direction::EAST,
target: ExitTarget::UseGPS,
exit_type: ExitType::Free
},
), ),
should_caption: false, should_caption: false,
..Default::default() ..Default::default()
}, },
Room {
zone: "melbs",
secondary_zones: vec!(),
code: "kings_office",
name: "Kings Office",
short: ansi!("<bgyellow><white>KO<reset>"),
description: ansi!("A dilapidated office that clearly was once a grand governor's office under the empire. It is now barely maintained. The self-styled king slouches behind a desk in the corner, shuffling paperwork around his desk. [Use <bold>list<reset> to see contracts for sale here]"),
description_less_explicit: None,
grid_coords: GridCoords { x: 6, y: 5, z: 0 },
exits: vec!(
Exit {
direction: Direction::WEST,
target: ExitTarget::UseGPS,
exit_type: ExitType::Free
},
),
should_caption: true,
stock_list: vec!(
RoomStock {
possession_type: PossessionType::NewCorpLicence,
list_price: 5000,
..Default::default()
}
),
..Default::default()
},
Room { Room {
zone: "melbs", zone: "melbs",
secondary_zones: vec!(), secondary_zones: vec!(),

View File

@ -53,3 +53,31 @@ CREATE TABLE tasks (
CREATE UNIQUE INDEX tasks_by_code_type ON tasks((details->>'task_code'), (details->>'task_type')); CREATE UNIQUE INDEX tasks_by_code_type ON tasks((details->>'task_code'), (details->>'task_type'));
CREATE INDEX tasks_by_static ON tasks((cast(details->>'is_static' as boolean))); CREATE INDEX tasks_by_static ON tasks((cast(details->>'is_static' as boolean)));
CREATE INDEX tasks_by_scheduled ON tasks((details->>'next_scheduled')); CREATE INDEX tasks_by_scheduled ON tasks((details->>'next_scheduled'));
CREATE TABLE corps (
corp_id BIGSERIAL NOT NULL PRIMARY KEY,
details JSONB NOT NULL
);
CREATE INDEX corp_by_name ON corps((details->>'name'));
CREATE TABLE corp_membership (
corp_id BIGSERIAL NOT NULL REFERENCES corps(corp_id),
member_username TEXT NOT NULL REFERENCES users(username),
details JSONB NOT NULL,
PRIMARY KEY (corp_id, member_username)
);
CREATE TABLE user_consent (
consenting_user TEXT NOT NULL REFERENCES users(username),
consented_user TEXT NOT NULL REFERENCES users(username),
details JSONB NOT NULL,
PRIMARY KEY (consenting_user, consented_user)
);
CREATE INDEX user_consent_by_consented ON user_consent (consented_user);
CREATE TABLE corp_consent (
consenting_corp BIGINT NOT NULL REFERENCES corps(corp_id),
consented_corp BIGINT NOT NULL REFERENCES corps(corp_id),
details JSONB NOT NULL,
PRIMARY KEY (consenting_corp, consented_corp)
);
CREATE INDEX corp_consent_by_consented ON corp_consent (consented_corp);