forked from blasthavers/blastmud
		
	
		
			
				
	
	
		
			165 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			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(())
 | |
| }
 |