blastmud/blastmud_game/src/services/effect.rs
Condorra 590d4640dd Implement craft on benches
Initially just a stove
Also update Rust.
2023-07-24 22:46:50 +10:00

165 lines
5.3 KiB
Rust

use super::{combat::change_health, comms::broadcast_to_room};
#[double]
use crate::db::DBTrans;
use crate::{
models::{
item::Item,
task::{Task, TaskDetails, TaskMeta},
},
regular_tasks::{TaskHandler, TaskRunContext},
static_content::possession_type::UseEffect,
DResult,
};
use async_trait::async_trait;
use chrono::Utc;
use log::info;
use mockall_double::double;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, VecDeque};
use std::time;
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct DelayedHealthEffect {
magnitude: i64,
delay: u64,
message: String,
message_nonexp: String,
}
pub struct DelayedHealthTaskHandler;
#[async_trait]
impl TaskHandler for DelayedHealthTaskHandler {
async fn do_task(&self, ctx: &mut TaskRunContext) -> DResult<Option<time::Duration>> {
let ref mut item_effect_series = match &mut ctx.task.details {
TaskDetails::DelayedHealth {
item,
ref mut effect_series,
} => (item, effect_series),
_ => Err("Expected DelayedHealth type")?,
};
let (item_type, item_code) = match item_effect_series.0.split_once("/") {
None => {
info!(
"Invalid item {} to DelayedHealthTaskHandler",
item_effect_series.0
);
return Ok(None);
}
Some((item_type, item_code)) => (item_type, item_code),
};
let item = match ctx
.trans
.find_item_by_type_code(item_type, item_code)
.await?
{
None => {
return Ok(None);
}
Some(it) => it,
};
if item.death_data.is_some() {
return Ok(None);
}
match item_effect_series.1.pop_front() {
None => Ok(None),
Some(DelayedHealthEffect {
magnitude,
message,
message_nonexp,
..
}) => {
let mut item_mut = (*item).clone();
change_health(
ctx.trans,
magnitude,
&mut item_mut,
&message,
&message_nonexp,
)
.await?;
ctx.trans.save_item_model(&item_mut).await?;
Ok(item_effect_series
.1
.front()
.map(|it| time::Duration::from_secs(it.delay)))
}
}
}
}
pub static DELAYED_HEALTH_HANDLER: &'static (dyn TaskHandler + Sync + Send) =
&DelayedHealthTaskHandler;
pub async fn run_effects(
trans: &DBTrans,
effects: &Vec<UseEffect>,
player: &mut Item,
item: &Item,
// None if target is player
target: &mut Option<Item>,
level: f64,
task_ref: &str,
) -> DResult<()> {
let mut target_health_series = BTreeMap::<String, VecDeque<DelayedHealthEffect>>::new();
for effect in effects {
match effect {
UseEffect::BroadcastMessage { messagef } => {
let (msg_exp, msg_nonexp) =
messagef(player, item, target.as_ref().unwrap_or(player));
broadcast_to_room(trans, &player.location, None, &msg_exp, Some(&msg_nonexp))
.await?;
}
UseEffect::ChangeTargetHealth {
delay_secs,
base_effect,
skill_multiplier,
max_effect,
message,
} => {
let health_impact =
(*base_effect + ((skill_multiplier * level) as i64).min(*max_effect)) as i64;
let (msg, msg_nonexp) = message(target.as_ref().unwrap_or(player));
if *delay_secs == 0 {
change_health(
trans,
health_impact,
target.as_mut().unwrap_or(player),
&msg,
&msg_nonexp,
)
.await?;
} else {
let target_it = target.as_ref().unwrap_or(player);
let fx = DelayedHealthEffect {
magnitude: health_impact,
delay: *delay_secs,
message: msg,
message_nonexp: msg_nonexp,
};
target_health_series
.entry(format!("{}/{}", target_it.item_type, target_it.item_code))
.and_modify(|l| l.push_back(fx.clone()))
.or_insert(VecDeque::from([fx]));
}
}
}
}
for (eff_item, l) in target_health_series.into_iter() {
trans
.upsert_task(&Task {
meta: TaskMeta {
task_code: format!("{}/{}", eff_item, task_ref),
next_scheduled: Utc::now() + chrono::Duration::seconds(l[0].delay as i64),
..Default::default()
},
details: TaskDetails::DelayedHealth {
effect_series: l,
item: eff_item,
},
})
.await?;
}
Ok(())
}