forked from blasthavers/blastmud
246 lines
8.8 KiB
Rust
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;
|