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

246 lines
8.8 KiB
Rust

use super::{
get_player_item_or_fail, get_user_or_fail, get_user_or_fail_mut, search_items_for_user,
user_error, UResult, UserError, UserVerb, UserVerbRef, VerbContext,
};
use crate::{
db::ItemSearchParams,
models::{
item::{Item, ItemFlag, ItemSpecialData},
task::{Task, TaskDetails, TaskMeta},
},
regular_tasks::{TaskHandler, TaskRunContext},
static_content::npc::npc_by_code,
DResult,
};
use ansi::ansi;
use async_trait::async_trait;
use chrono::{Duration, Utc};
use std::sync::Arc;
use std::time;
static RESIGNATION_NOTICE: &'static str = ansi!("You're a <red>terrible employer<reset>, you <bold>HAVEN'T PAID MY WAGES!<reset> Do you even have the credits? I don't work for free, so you can forget it! I quit, effective immediately!");
pub struct ChargeWagesTaskHandler;
#[async_trait]
impl TaskHandler for ChargeWagesTaskHandler {
async fn do_task(&self, ctx: &mut TaskRunContext) -> DResult<Option<time::Duration>> {
let npc_code = match &mut ctx.task.details {
TaskDetails::ChargeWages { npc } => npc,
_ => Err("Expected ChargeWages type")?,
};
let hire_dat = match npc_by_code()
.get(npc_code.as_str())
.and_then(|npci| npci.hire_data.as_ref())
{
None => return Ok(None), // I guess it is free until fixed in the db?
Some(d) => d,
};
let npc: Arc<Item> = match ctx.trans.find_item_by_type_code("npc", &npc_code).await? {
None => return Ok(None),
Some(d) => d,
};
let hired_by = match npc.special_data.as_ref() {
Some(ItemSpecialData::HireData {
hired_by: Some(hired_by),
}) => hired_by,
_ => return Ok(None),
};
let mut bill_user = match ctx.trans.find_by_username(&hired_by).await? {
None => {
// Happens if the user is deleted while hiring an NPC.
let mut npc_mut: Item = (*npc).clone();
npc_mut.special_data = Some(ItemSpecialData::HireData { hired_by: None });
// No player, so the NPC fires itself.
hire_dat
.handler
.fire_handler(&ctx.trans, &npc, &mut npc_mut)
.await?;
ctx.trans.save_item_model(&npc_mut).await?;
return Ok(None);
}
Some(user) => user,
};
let bill_player = ctx
.trans
.find_item_by_type_code("player", &hired_by)
.await?
.ok_or_else(|| "Player hiring NPC missing but user still there.")?;
let sess_and_dat = ctx.trans.find_session_for_player(&hired_by).await?;
if hire_dat.price > bill_user.credits {
if let Some((sess, _sess_dat)) = sess_and_dat {
ctx.trans
.queue_for_session(
&sess,
Some(&format!(
"<yellow>{} says: <reset><bold>\"{}\"<reset>\n",
&npc.display_for_sentence(1, false),
RESIGNATION_NOTICE
)),
)
.await?;
}
let mut npc_mut = (*npc).clone();
hire_dat
.handler
.fire_handler(&ctx.trans, &bill_player, &mut npc_mut)
.await?;
npc_mut.special_data = Some(ItemSpecialData::HireData { hired_by: None });
ctx.trans.save_item_model(&npc_mut).await?;
return Ok(None);
}
bill_user.credits -= hire_dat.price;
ctx.trans.save_user_model(&bill_user).await?;
match sess_and_dat.as_ref() {
None => {},
Some((sess, _sess_dat)) => ctx.trans.queue_for_session(
sess, Some(&format!(
ansi!("<yellow>Your wristpad beeps for a deduction of ${} for wages for {}.<reset>\n"),
&hire_dat.price, &npc.display_for_sentence(1, false)
))
).await?
}
Ok(Some(time::Duration::from_secs(hire_dat.frequency_secs)))
}
}
pub static CHARGE_WAGES_HANDLER: &'static (dyn TaskHandler + Sync + Send) = &ChargeWagesTaskHandler;
pub struct Verb;
#[async_trait]
impl UserVerb for Verb {
async fn handle(
self: &Self,
ctx: &mut VerbContext,
_verb: &str,
remaining: &str,
) -> UResult<()> {
let npc_name = remaining.trim();
let player_item = get_player_item_or_fail(ctx).await?;
if npc_name == "" {
let staff = ctx
.trans
.find_staff_by_hirer(&player_item.item_code)
.await?;
let mut msg: String = String::new();
if staff.is_empty() {
msg.push_str("You aren't currently employing anyone.\n");
} else {
msg.push_str("You are currently employing:\n");
for emp in staff {
if let Some(hire_dat) = npc_by_code()
.get(emp.item_code.as_str())
.as_ref()
.and_then(|npci| npci.hire_data.as_ref())
{
msg.push_str(&format!(
"* {} @ ${} / {}\n",
emp.display_for_sentence(1, false),
hire_dat.price,
humantime::format_duration(std::time::Duration::from_secs(
hire_dat.frequency_secs,
)),
));
}
}
}
ctx.trans.queue_for_session(ctx.session, Some(&msg)).await?;
return Ok(());
}
let to_hire_if_free = search_items_for_user(
ctx,
&ItemSearchParams {
include_loc_contents: true,
item_type_only: Some("npc"),
..ItemSearchParams::base(&player_item, npc_name)
},
)
.await?;
let npc = to_hire_if_free
.into_iter()
.find(|it| {
it.flags.contains(&ItemFlag::Hireable)
&& match it.special_data {
Some(ItemSpecialData::HireData { hired_by: Some(_) }) => false,
_ => true,
}
})
.ok_or_else(|| UserError("Nothing here is available for hire right now.".to_owned()))?;
let hire_dat = npc_by_code()
.get(npc.item_code.as_str())
.as_ref()
.and_then(|npci| npci.hire_data.as_ref())
.ok_or_else(|| UserError("Sorry, I've forgotten how to hire that out!".to_owned()))?;
let user = get_user_or_fail(ctx)?;
if user.credits < hire_dat.price {
user_error(format!(
ansi!(
"<yellow>{} says: <reset><bold>\"You wouldn't be able to afford me.\"<reset>"
),
npc.display_for_sentence(1, false)
))?;
}
if ctx
.trans
.count_staff_by_hirer(&player_item.item_code)
.await?
>= 5
{
user_error(
"You don't think you could supervise that many employees at once!".to_owned(),
)?;
}
let user_mut = get_user_or_fail_mut(ctx)?;
user_mut.credits -= hire_dat.price;
ctx.trans
.queue_for_session(
&ctx.session,
Some(&format!(
"You hire {}, and your wristpad beeps for a deduction of ${}.\n",
npc.display_for_sentence(1, false),
hire_dat.price
)),
)
.await?;
let mut npc_mut = (*npc).clone();
hire_dat
.handler
.hire_handler(&ctx.trans, &ctx.session, &player_item, &mut npc_mut)
.await?;
npc_mut.special_data = Some(ItemSpecialData::HireData {
hired_by: Some(player_item.item_code.clone()),
});
ctx.trans.save_item_model(&npc_mut).await?;
ctx.trans
.upsert_task(&Task {
meta: TaskMeta {
task_code: npc.item_code.clone(),
is_static: false,
recurrence: None, // Managed by the handler.
next_scheduled: Utc::now() + Duration::seconds(hire_dat.frequency_secs as i64),
..Default::default()
},
details: TaskDetails::ChargeWages {
npc: npc.item_code.clone(),
},
})
.await?;
Ok(())
}
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;