blastmud/blastmud_game/src/message_handler/user_commands/allow.rs

874 lines
35 KiB
Rust

use super::{
VerbContext,
UserVerb,
UserVerbRef,
UResult,
user_error,
get_player_item_or_fail,
get_user_or_fail,
search_item_for_user,
parsing,
corp::check_corp_perm,
};
use async_trait::async_trait;
use crate::{
models::{
consent::{Consent, ConsentType, ConsentStatus, FightConsent},
corp::{CorpPermission, CorpCommType},
item::Item
},
db::ItemSearchParams,
static_content::room::room_map_by_code,
};
use ansi::ansi;
use itertools::Itertools;
#[derive(Debug, PartialEq)]
pub enum ConsentTarget<'t> {
CorpTarget { from_corp: &'t str, to_corp: &'t str },
UserTarget { to_user: &'t str }
}
#[derive(Debug, PartialEq)]
pub struct ConsentDetails<'t> {
pub duration_minutes: Option<u64>,
pub until_death: bool,
pub allow_private: bool,
pub only_in: Vec<&'t str>,
pub allow_pick: bool,
pub freely_revoke: bool,
}
impl <'t>ConsentDetails<'t> {
pub fn default_for(tp: &ConsentType) -> Self {
Self {
duration_minutes: None,
until_death: false,
allow_private: tp != &ConsentType::Fight,
only_in: vec!(),
allow_pick: false,
freely_revoke: false,
}
}
pub fn as_string(&self, consent_type: &ConsentType) -> String {
let mut buf = String::new();
match self.duration_minutes {
None => {},
Some(n) => buf.push_str(&format!("{} minutes ", n))
}
if self.until_death {
buf.push_str("until death ");
}
if *consent_type == ConsentType::Fight {
if self.allow_private {
buf.push_str("allow private ");
}
if self.allow_pick {
buf.push_str("allow pick ");
}
if self.freely_revoke {
buf.push_str("allow revoke ");
}
} else {
if !self.allow_private {
buf.push_str("disallow private ");
}
}
for loc in &self.only_in {
buf.push_str(&format!("in {} ", loc));
}
buf
}
}
#[derive(Debug, PartialEq)]
pub struct AllowCommand<'t> {
pub consent_type: ConsentType,
pub consent_target: ConsentTarget<'t>,
pub consent_details: ConsentDetails<'t>
}
#[derive(Debug)]
pub struct ConsentUpdateOutput {
pub new_consent: Option<Consent>,
pub first_party_message: Option<String>,
pub counterparty_message: Option<String>,
pub mirror_to_counterparty: bool
}
fn compute_new_consent_state(
player_display: &str,
player_possessive: &str,
counterplayer_display: &str,
counterplayer_possessive: &str,
selector: &str,
consent_type: &ConsentType,
new_consent_details: &ConsentDetails,
my_current_consent: &Option<Consent>,
their_current_consent: &Option<Consent>,
is_allow: bool) -> ConsentUpdateOutput {
if !is_allow {
if consent_type != &ConsentType::Fight {
if my_current_consent.is_none() {
return ConsentUpdateOutput {
new_consent: None,
first_party_message: Some("There was no matching consent in force to disallow".to_owned()),
counterparty_message: None,
mirror_to_counterparty: false
}
} else {
return ConsentUpdateOutput {
new_consent: None,
first_party_message:
Some(format!("You no longer allow {} from {}",
ConsentType::to_str(consent_type), counterplayer_display)),
counterparty_message:
Some(format!("{} no longer allows {} from you", player_display,
ConsentType::to_str(consent_type))),
mirror_to_counterparty: false
}
}
}
if my_current_consent.as_ref().and_then(|c| c.fight_consent.as_ref())
.map(|c| (c.status == ConsentStatus::PendingAdd)) == Some(true) {
return ConsentUpdateOutput {
new_consent: None,
first_party_message:
Some(format!("You are no longer offering to fight {}",
counterplayer_display)),
counterparty_message:
Some(format!("{} no longer wants to fight you", player_display)),
mirror_to_counterparty: false
};
}
if their_current_consent.as_ref().and_then(|c| c.fight_consent.as_ref())
.map(|c| c.freely_revoke || (c.status == ConsentStatus::PendingDelete)) == Some(true) {
return ConsentUpdateOutput {
new_consent: None,
first_party_message:
Some(format!("You no longer allow {} from {}",
ConsentType::to_str(consent_type), counterplayer_display)),
counterparty_message:
Some(format!("{} no longer allows {} from you", player_display,
ConsentType::to_str(consent_type))),
mirror_to_counterparty: true
};
}
match my_current_consent.as_ref() {
None => return ConsentUpdateOutput {
new_consent: None,
first_party_message: Some("There was no matching consent in force to disallow".to_owned()),
counterparty_message: None,
mirror_to_counterparty: false
},
Some(c) if c.fight_consent.as_ref().map(|fc| &fc.status) == Some(&ConsentStatus::PendingDelete) =>
return ConsentUpdateOutput {
new_consent: Some((*c).clone()),
first_party_message: Some(format!("You are already waiting on {} to agree to cancel consent to fight.",
counterplayer_display
)),
counterparty_message: None,
mirror_to_counterparty: false
},
Some(c) => return ConsentUpdateOutput {
new_consent: Some(Consent {
fight_consent: c.fight_consent.as_ref().map(|fc| FightConsent {
status: ConsentStatus::PendingDelete, pending_change: None, ..*fc }),
..((*c).clone()) }),
first_party_message:
Some(format!("Informing {} of your desire to withdraw consent to fight. The terms of your previous consent \
were that it was not freely revokable, so the change will only take effect on {} \
acceptance.",
counterplayer_display, counterplayer_possessive)),
counterparty_message:
Some(format!("{} wants to withdraw {} consent to fight, but only can if you agree. To agree, type disallow \
fight {}",
player_display, player_possessive, selector)),
mirror_to_counterparty: false
}
}
}
let their_target_consent = their_current_consent.as_ref().and_then(
|c| c.fight_consent.as_ref()
.map(|fc| fc.pending_change.as_ref()
.map(|c| (**c).clone()).unwrap_or(c.clone())));
let mut new_consent = Consent::default();
new_consent.only_in = new_consent_details.only_in.iter().map(|v| (*v).to_owned()).collect();
let expires_minutes = if *consent_type != ConsentType::Fight {
new_consent_details.duration_minutes
} else {
match new_consent_details.duration_minutes {
None => Some(60 * 24 * 7),
Some(n) => Some(n.min(60 * 24 * 7))
}
};
new_consent.expires = expires_minutes
.map(|n| chrono::Utc::now() +
chrono::Duration::minutes(n.min(60 * 24 * 365 * 25) as i64));
match their_target_consent.as_ref().and_then(|c| c.expires).and_then(
|t_them| new_consent.expires.map(|t_me| (t_me, t_them))) {
Some((t_me, t_them)) if (t_me - t_them).num_minutes().abs() < 5 => {
new_consent.expires = Some(t_them);
}
_ => {}
}
new_consent.allow_private = new_consent_details.allow_private;
new_consent.until_death = new_consent_details.until_death;
new_consent.fight_consent = if *consent_type == ConsentType::Fight {
Some(FightConsent {
pending_change: None,
allow_pick: new_consent_details.allow_pick,
freely_revoke: new_consent_details.freely_revoke,
..FightConsent::default()
})
} else {
None
};
if *consent_type == ConsentType::Fight {
if Some(&new_consent) == their_target_consent.as_ref() {
match new_consent.fight_consent.as_mut() {
None => (),
Some(mut m) => {
m.pending_change = None;
m.status = ConsentStatus::Active;
}
}
return ConsentUpdateOutput {
new_consent: Some(new_consent),
first_party_message: Some(format!("You can now fight {} - it's on!",
counterplayer_display)),
counterparty_message: Some(format!("{} accepted your offer to fight - it's on!",
player_display)),
mirror_to_counterparty: true
};
}
match my_current_consent.as_ref() {
None => {
match new_consent.fight_consent.as_mut() {
None => (),
Some(mut m) => { m.status = ConsentStatus::PendingAdd; }
}
return ConsentUpdateOutput {
new_consent: Some(new_consent),
first_party_message: Some(format!("{} has been asked for consent to fight.", counterplayer_display)),
counterparty_message: Some(format!("{} wants to fight! To accept, type: allow fight {} {}",
player_display, selector,
&new_consent_details.as_string(consent_type))),
mirror_to_counterparty: false
};
},
Some(current) if current.fight_consent.as_ref().map(|fc| fc.status == ConsentStatus::PendingAdd) == Some(true) => {
return ConsentUpdateOutput {
new_consent: Some(new_consent),
first_party_message: Some("Waiting for the other side to accept on the new terms".to_owned()),
counterparty_message:
Some(format!("{} changed {} proposed fight terms! To accept, type allow fight {} {}",
player_display, player_possessive, selector, &new_consent_details.as_string(consent_type))),
mirror_to_counterparty: false
};
},
Some(current) => {
return ConsentUpdateOutput {
new_consent: Some(Consent {
fight_consent: current.fight_consent.as_ref().map(|fc| FightConsent {
pending_change: Some(Box::new(new_consent)),
..(*fc).clone()
}),
..(*current).clone()
}),
first_party_message: Some("Waiting for the other side to accept the change of terms".to_owned()),
counterparty_message:
Some(format!("{} wants to amend the terms of the fight! To accept, type allow fight {} {}",
player_display, selector, &new_consent_details.as_string(consent_type))),
mirror_to_counterparty: false
};
}
}
}
ConsentUpdateOutput {
new_consent: Some(new_consent),
first_party_message: Some(format!("You now allow {} from {}", consent_type.to_str(), counterplayer_display)),
counterparty_message: Some(format!("{} now allows {} from you", player_display, consent_type.to_str())),
mirror_to_counterparty: false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compute_new_consent_state_cancels_non_fight() {
let result =
compute_new_consent_state(
"Foo", "his", "Bar", "her", "from bar",
&ConsentType::Sex,
&ConsentDetails::default_for(&ConsentType::Sex),
&Some(Consent::default()),
&None,
false
);
assert_eq!(result.new_consent, None);
assert_eq!(result.mirror_to_counterparty, false);
assert_eq!(result.first_party_message.is_some(), true);
assert_eq!(result.counterparty_message.is_some(), true);
}
#[test]
fn compute_new_consent_state_disallow_puts_into_pending_delete_fight() {
let result =
compute_new_consent_state(
"Foo", "his", "Bar", "her", "from bar",
&ConsentType::Fight,
&ConsentDetails::default_for(&ConsentType::Fight),
&Some(Consent {
fight_consent: Some(FightConsent {
status: ConsentStatus::Active,
..FightConsent::default()
}),
..Consent::default()
}),
&Some(Consent {
fight_consent: Some(FightConsent {
status: ConsentStatus::Active,
..FightConsent::default()
}),
..Consent::default()
}),
false
);
assert_eq!(result.new_consent.is_some(), true);
assert_eq!(result.new_consent.unwrap()
.fight_consent.unwrap().status, ConsentStatus::PendingDelete);
assert_eq!(result.first_party_message.is_some(), true);
assert_eq!(result.counterparty_message.is_some(), true);
assert_eq!(result.mirror_to_counterparty, false);
}
#[test]
fn compute_new_consent_state_disallow_cancels_fight_if_freely_revoke() {
let result =
compute_new_consent_state(
"Foo", "his", "Bar", "her", "from bar",
&ConsentType::Fight,
&ConsentDetails::default_for(&ConsentType::Fight),
&Some(Consent {
fight_consent: Some(FightConsent {
status: ConsentStatus::Active,
freely_revoke: true,
..FightConsent::default()
}),
..Consent::default()
}),
&Some(Consent {
fight_consent: Some(FightConsent {
status: ConsentStatus::Active,
freely_revoke: true,
..FightConsent::default()
}),
..Consent::default()
}),
false
);
assert_eq!(result.new_consent, None);
assert_eq!(result.first_party_message.is_some(), true);
assert_eq!(result.counterparty_message.is_some(), true);
assert_eq!(result.mirror_to_counterparty, true);
}
#[test]
fn compute_new_consent_state_disallow_cancels_pending_delete_fight() {
let result =
compute_new_consent_state(
"Foo", "his", "Bar", "her", "from bar",
&ConsentType::Fight,
&ConsentDetails::default_for(&ConsentType::Fight),
&Some(Consent {
fight_consent: Some(FightConsent {
status: ConsentStatus::Active,
..FightConsent::default()
}),
..Consent::default()
}),
&Some(Consent {
fight_consent: Some(FightConsent {
status: ConsentStatus::PendingDelete,
..FightConsent::default()
}),
..Consent::default()
}),
false
);
assert_eq!(result.new_consent, None);
assert_eq!(result.first_party_message.is_some(), true);
assert_eq!(result.counterparty_message.is_some(), true);
assert_eq!(result.mirror_to_counterparty, true);
}
#[test]
fn compute_new_consent_state_unilateral_double_disallow_doesnt_cancel() {
let result =
compute_new_consent_state(
"Foo", "his", "Bar", "her", "from bar",
&ConsentType::Fight,
&ConsentDetails::default_for(&ConsentType::Fight),
&Some(Consent {
fight_consent: Some(FightConsent {
status: ConsentStatus::PendingDelete,
..FightConsent::default()
}),
..Consent::default()
}),
&Some(Consent {
fight_consent: Some(FightConsent {
status: ConsentStatus::Active,
..FightConsent::default()
}),
..Consent::default()
}),
false
);
assert_eq!(result.new_consent.is_some(), true);
assert_eq!(result.first_party_message.is_some(), true);
assert_eq!(result.counterparty_message, None);
assert_eq!(result.mirror_to_counterparty, false);
}
#[test]
fn compute_new_consent_state_adds_nonfight_immediately() {
let result =
compute_new_consent_state(
"Foo", "his", "Bar", "her", "from bar",
&ConsentType::Sex,
&ConsentDetails::default_for(&ConsentType::Sex),
&None,
&None,
true
);
assert_eq!(result.new_consent.is_some(), true);
assert_eq!(result.mirror_to_counterparty, false);
assert_eq!(result.first_party_message.is_some(), true);
assert_eq!(result.counterparty_message.is_some(), true);
}
#[test]
fn compute_new_consent_state_creates_pending_add_for_fight() {
let result =
compute_new_consent_state(
"Foo", "his", "Bar", "her", "from bar",
&ConsentType::Fight,
&ConsentDetails::default_for(&ConsentType::Fight),
&None,
&None,
true
);
assert_eq!(result.new_consent.is_some(), true);
assert_eq!(result.new_consent.unwrap().fight_consent.unwrap().status, ConsentStatus::PendingAdd);
assert_eq!(result.mirror_to_counterparty, false);
assert_eq!(result.first_party_message.is_some(), true);
assert_eq!(result.counterparty_message.is_some(), true);
}
#[test]
fn compute_new_consent_state_goes_active_if_pending_add_matches() {
let result =
compute_new_consent_state(
"Foo", "his", "Bar", "her", "from bar",
&ConsentType::Fight,
&ConsentDetails::default_for(&ConsentType::Fight),
&None,
&Some(Consent {
expires: Some(chrono::Utc::now() + chrono::Duration::minutes(60 * 24 * 7)),
fight_consent: Some(FightConsent::default()),
..Consent::default()
}),
true
);
assert_eq!(result.new_consent.is_some(), true);
assert_eq!(result.new_consent.unwrap().fight_consent.unwrap().status, ConsentStatus::Active);
assert_eq!(result.mirror_to_counterparty, true);
assert_eq!(result.first_party_message.is_some(), true);
assert_eq!(result.counterparty_message.is_some(), true);
}
#[test]
fn compute_new_consent_state_creates_pending_update_to_change_fight() {
let result =
compute_new_consent_state(
"Foo", "his", "Bar", "her", "from bar",
&ConsentType::Fight,
&ConsentDetails {
freely_revoke: true,
..ConsentDetails::default_for(&ConsentType::Fight)
},
&Some(Consent {
expires: Some(chrono::Utc::now() + chrono::Duration::minutes(60 * 24 * 7)),
fight_consent: Some(FightConsent {
status: ConsentStatus::Active,
..FightConsent::default()
}),
..Consent::default()
}),
&Some(Consent {
expires: Some(chrono::Utc::now() + chrono::Duration::minutes(60 * 24 * 7)),
fight_consent: Some(FightConsent {
status: ConsentStatus::Active,
..FightConsent::default()
}),
..Consent::default()
}),
true
);
assert_eq!(result.new_consent.is_some(), true);
let fc = result.new_consent.as_ref().unwrap().fight_consent.as_ref().unwrap();
assert_eq!(fc.status, ConsentStatus::Active);
assert_eq!(fc.freely_revoke, false);
assert_eq!(fc.pending_change.is_some(), true);
assert_eq!(fc.pending_change.as_ref().unwrap()
.fight_consent.as_ref().unwrap().freely_revoke, true);
assert_eq!(result.mirror_to_counterparty, false);
assert_eq!(result.first_party_message.is_some(), true);
assert_eq!(result.counterparty_message.is_some(), true);
}
#[test]
fn compute_new_consent_state_accepts_pending_update_to_change_fight_if_matches() {
let result =
compute_new_consent_state(
"Foo", "his", "Bar", "her", "from bar",
&ConsentType::Fight,
&ConsentDetails {
freely_revoke: true,
..ConsentDetails::default_for(&ConsentType::Fight)
},
&Some(Consent {
expires: Some(chrono::Utc::now() + chrono::Duration::minutes(60 * 24 * 7)),
fight_consent: Some(FightConsent {
status: ConsentStatus::Active,
..FightConsent::default()
}),
..Consent::default()
}),
&Some(Consent {
expires: Some(chrono::Utc::now() + chrono::Duration::minutes(60 * 24 * 7)),
fight_consent: Some(FightConsent {
status: ConsentStatus::Active,
pending_change: Some(Box::new(
Consent {
expires: Some(chrono::Utc::now() + chrono::Duration::minutes(60 * 24 * 7)),
fight_consent: Some(FightConsent {
freely_revoke: true,
..FightConsent::default()
}),
..Consent::default()
}
)),
..FightConsent::default()
}),
..Consent::default()
}),
true
);
assert_eq!(result.new_consent.is_some(), true);
let fc = result.new_consent.as_ref().unwrap().fight_consent.as_ref().unwrap();
assert_eq!(fc.status, ConsentStatus::Active);
assert_eq!(fc.freely_revoke, true);
assert_eq!(fc.pending_change, None);
assert_eq!(result.mirror_to_counterparty, true);
assert_eq!(result.first_party_message.is_some(), true);
assert_eq!(result.counterparty_message.is_some(), true);
}
}
async fn handle_user_consent(ctx: &mut VerbContext<'_>, source_player: &Item,
to_username: &str, is_allow: bool, cmd: &AllowCommand<'_>) -> UResult<()> {
ctx.trans.delete_expired_user_consent().await?;
let to_user_item = search_item_for_user(ctx, &ItemSearchParams {
include_all_players: true,
..ItemSearchParams::base(source_player, to_username)
}).await?;
if source_player.item_code == to_user_item.item_code {
user_error("You know that's you, right?".to_owned())?;
}
let current_consent = ctx.trans.find_user_consent_by_parties_type(
&source_player.item_code,
&to_user_item.item_code,
&cmd.consent_type
).await?;
let converse_consent = if cmd.consent_type == ConsentType::Fight {
ctx.trans.find_user_consent_by_parties_type(
&to_user_item.item_code,
&source_player.item_code,
&cmd.consent_type
).await?
} else {
None
};
let update = compute_new_consent_state(
&source_player.display_for_sentence(false, 1, false),
&source_player.pronouns.possessive,
&to_user_item.display_for_sentence(false, 1, false),
&to_user_item.pronouns.possessive,
&("from ".to_owned() + &source_player.item_code),
&cmd.consent_type, &cmd.consent_details,
&current_consent, &converse_consent, is_allow
);
match update.new_consent.as_ref() {
None => ctx.trans.delete_user_consent(
&source_player.item_code,
&to_user_item.item_code,
&cmd.consent_type
).await?,
Some(consent) => ctx.trans.upsert_user_consent(
&source_player.item_code,
&to_user_item.item_code,
&cmd.consent_type,
consent
).await?,
}
if update.mirror_to_counterparty {
match update.new_consent.as_ref() {
None => ctx.trans.delete_user_consent(
&to_user_item.item_code,
&source_player.item_code,
&cmd.consent_type
).await?,
Some(consent) => ctx.trans.upsert_user_consent(
&to_user_item.item_code,
&source_player.item_code,
&cmd.consent_type,
consent
).await?,
}
}
match update.first_party_message {
None => {},
Some(msg) => ctx.trans.queue_for_session(&ctx.session, Some(&(msg + "\n"))).await?
}
match update.counterparty_message {
None => {},
Some(msg) => {
match ctx.trans.find_session_for_player(&to_user_item.item_code).await? {
None => {},
Some((session, session_dat)) =>
if cmd.consent_type != ConsentType::Sex || !session_dat.less_explicit_mode {
ctx.trans.queue_for_session(&session, Some(&(msg + "\n"))).await?;
}
}
}
}
Ok(())
}
async fn handle_corp_consent(ctx: &mut VerbContext<'_>, source_player: &Item,
from_corp: &str,
to_corp: &str,
is_allow: bool,
cmd: &AllowCommand<'_>
) -> UResult<()> {
let user = get_user_or_fail(ctx)?;
let (from_corp_id, from_corp, mem) =
match ctx.trans.match_user_corp_by_name(from_corp, &user.username).await? {
None => user_error("You don't seem to belong to a matching corp!".to_owned())?,
Some(c) => c
};
let (to_corp_id, to_corp) = match ctx.trans.find_corp_by_name(to_corp).await? {
None => user_error("I didn't find the corp you want to fight against.".to_owned())?,
Some(c) => c
};
if !check_corp_perm(&CorpPermission::War, &mem) {
user_error("You don't have permission to declare war on behalf of that corp.".to_owned())?;
}
if is_allow {
let mut not_allowing_users = vec!();
for (name, mem) in ctx.trans.list_corp_members(&from_corp_id).await? {
if name != source_player.item_code && !mem.allow_combat {
not_allowing_users.push(name);
}
}
if !not_allowing_users.is_empty() {
user_error(format!(ansi!("War can only be declared when all corp mates allow combat for that corp. Ask them to run <bold>corp allow combat from {}<reset>, or have them fired! <bold>corp config {} allow combat required<reset> will stop anyone joining without it / turning it off. Corp mates with it off: {}"),
&from_corp.name,
&from_corp.name,
&not_allowing_users.iter().join(", ")
))?;
}
}
ctx.trans.delete_expired_corp_consent().await?;
let current_consent = ctx.trans.find_corp_consent_by_parties_type(
&from_corp_id,
&to_corp_id,
&cmd.consent_type
).await?;
let converse_consent = if cmd.consent_type == ConsentType::Fight {
ctx.trans.find_corp_consent_by_parties_type(
&to_corp_id,
&from_corp_id,
&cmd.consent_type
).await?
} else {
None
};
let update = compute_new_consent_state(
&from_corp.name,
"their",
&to_corp.name,
"their",
&("against ".to_owned() + &to_corp.name + " by " + &from_corp.name),
&cmd.consent_type, &cmd.consent_details,
&current_consent, &converse_consent, is_allow
);
match update.new_consent.as_ref() {
None => ctx.trans.delete_corp_consent(
&from_corp_id,
&to_corp_id,
&cmd.consent_type
).await?,
Some(consent) => ctx.trans.upsert_corp_consent(
&from_corp_id,
&to_corp_id,
&cmd.consent_type,
consent
).await?,
}
if update.mirror_to_counterparty {
match update.new_consent.as_ref() {
None => ctx.trans.delete_corp_consent(
&to_corp_id,
&from_corp_id,
&cmd.consent_type
).await?,
Some(consent) => ctx.trans.upsert_corp_consent(
&to_corp_id,
&from_corp_id,
&cmd.consent_type,
consent
).await?,
}
}
match update.first_party_message {
None => {},
Some(msg) =>
if update.counterparty_message.is_some() {
let details_str = cmd.consent_details.as_string(&ConsentType::Fight);
let details_str = if details_str != "" {
format!(" ({})", &details_str.trim())
} else {
"".to_owned()
};
let action_str = if is_allow { "consented" } else { "withdrew consent" };
// If it goes to both corps, it is a diplomatic message so goes to whole source corp.
ctx.trans.broadcast_to_corp(
&from_corp_id,
&CorpCommType::Consent,
None,
&format!(ansi!("<red>{} {} to fight{} against {} on behalf \
of {}<reset>, with result: {}\n"),
&source_player.display_for_sentence(false, 1, true),
action_str,
&details_str,
&to_corp.name,
&from_corp.name,
&msg
)
).await?;
} else {
ctx.trans.queue_for_session(&ctx.session, Some(&(msg + "\n"))).await?;
}
}
match update.counterparty_message {
None => {},
Some(msg) => {
ctx.trans.broadcast_to_corp(
&to_corp_id,
&CorpCommType::Consent,
None,
&format!(ansi!("<yellow>Your wristpad buzzes with an corporate announcement to {}:<reset> {}\n"), &to_corp.name, &msg)
).await?
}
}
Ok(())
}
async fn handle_list_allows(ctx: &mut VerbContext<'_>, item: &Item) -> UResult<()> {
let consents = ctx.trans.list_consents(&item.item_code).await?;
let mut msg = String::new();
if consents.is_empty() {
msg.push_str(ansi!("You have no consents. Type <bold>help allow<reset> to learn how to consent."));
} else {
msg.push_str(ansi!("You are consenting to the following actions (type <bold>help allow<reset> to learn how to change):\n"));
msg.push_str(&format!(ansi!("<bgblue><white><bold>| {:20} | {:12} | {:40} |<reset>\n"),
"User", "Consent type", "Details"));
for (user, consent_type, details) in consents {
msg.push_str(&format!("| {:20} | {:12} | {:40} |\n",
user, consent_type.to_str(), details.to_str()));
}
}
ctx.trans.queue_for_session(&ctx.session, Some(&(msg + "\n"))).await?;
Ok(())
}
pub struct Verb;
#[async_trait]
impl UserVerb for Verb {
async fn handle(self: &Self, ctx: &mut VerbContext, verb: &str, remaining: &str) -> UResult<()> {
let player_item = get_player_item_or_fail(ctx).await?;
let is_allow = verb == "allow";
let remaining_trim = remaining.trim();
if remaining_trim == "" {
handle_list_allows(ctx, &player_item).await?;
} else {
let mut cmd = match parsing::parse_allow(remaining, !ctx.session_dat.less_explicit_mode) {
Err(msg) => user_error(msg)?,
Ok(cmd) => cmd
};
for place in cmd.consent_details.only_in.iter_mut() {
if place == &"here" {
let loc_code = match player_item.location.split_once("/") {
None => user_error("Can't use \"in here\" where you are now".to_owned())?,
Some((loc_type, _)) if loc_type != "room" =>
user_error("Can't use \"in here\" outside a public room".to_owned())?,
Some((_, loc_code)) => loc_code
};
*place = loc_code;
}
if room_map_by_code().get(*place).is_none() {
user_error(format!("Place {} not found", *place))?
}
}
match cmd.consent_target {
ConsentTarget::CorpTarget { from_corp, to_corp } =>
handle_corp_consent(ctx, &player_item, from_corp, to_corp, is_allow, &cmd).await?,
ConsentTarget::UserTarget { to_user } =>
handle_user_consent(ctx, &player_item, to_user, is_allow, &cmd).await?
}
}
Ok(())
}
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;