blastmud/blastmud_game/src/services/effect.rs

136 lines
4.7 KiB
Rust
Raw Normal View History

2023-02-25 23:49:46 +11:00
use crate::{
models::{
item::Item,
task::{
Task,
TaskMeta,
TaskDetails,
}
},
DResult,
static_content::{
possession_type::UseEffect,
},
regular_tasks::{
TaskHandler,
TaskRunContext,
}
};
use super::{
comms::broadcast_to_room,
2023-02-25 23:49:46 +11:00
combat::change_health,
};
use async_trait::async_trait;
use std::time;
use serde::{Serialize, Deserialize};
use std::collections::{BTreeMap, VecDeque};
use chrono::Utc;
use log::info;
use mockall_double::double;
#[double] use crate::db::DBTrans;
2023-02-25 23:49:46 +11:00
#[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.is_dead {
return Ok(None);
}
2023-02-25 23:49:46 +11:00
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(())
}