Allow feinting during battle
This commit is contained in:
parent
aa4828469a
commit
76b2874077
@ -31,6 +31,7 @@ mod describe;
|
||||
pub mod drink;
|
||||
pub mod drop;
|
||||
pub mod eat;
|
||||
mod feint;
|
||||
pub mod fill;
|
||||
mod fire;
|
||||
pub mod follow;
|
||||
@ -197,6 +198,7 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
||||
"look" => look::VERB,
|
||||
"read" => look::VERB,
|
||||
|
||||
"feint" => feint::VERB,
|
||||
"list" => list::VERB,
|
||||
|
||||
"load" => load::VERB,
|
||||
|
@ -4,7 +4,7 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
db::ItemSearchParams,
|
||||
models::{consent::ConsentType, item::ItemFlag},
|
||||
models::{consent::ConsentType, effect::EffectType, item::ItemFlag},
|
||||
services::{check_consent, combat::start_attack},
|
||||
};
|
||||
use ansi::ansi;
|
||||
@ -24,6 +24,13 @@ impl UserVerb for Verb {
|
||||
if player_item.death_data.is_some() {
|
||||
user_error("It doesn't really seem fair, but you realise you won't be able to attack anyone while you're dead!".to_string())?;
|
||||
}
|
||||
if player_item
|
||||
.active_effects
|
||||
.iter()
|
||||
.any(|v| v.0 == EffectType::Stunned)
|
||||
{
|
||||
user_error("You're too stunned to attack.".to_owned())?;
|
||||
}
|
||||
|
||||
let attack_whom = search_item_for_user(
|
||||
ctx,
|
||||
|
37
blastmud_game/src/message_handler/user_commands/feint.rs
Normal file
37
blastmud_game/src/message_handler/user_commands/feint.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use crate::{models::effect::EffectType, services::combat::switch_to_feint};
|
||||
|
||||
use super::{get_player_item_or_fail, user_error, UResult, UserVerb, UserVerbRef, VerbContext};
|
||||
use async_trait::async_trait;
|
||||
|
||||
pub struct Verb;
|
||||
#[async_trait]
|
||||
impl UserVerb for Verb {
|
||||
async fn handle(
|
||||
self: &Self,
|
||||
ctx: &mut VerbContext,
|
||||
_verb: &str,
|
||||
_remaining: &str,
|
||||
) -> UResult<()> {
|
||||
let player_item = get_player_item_or_fail(ctx).await?;
|
||||
if player_item.death_data.is_some() {
|
||||
user_error("Feint while dead? You can't even do a regular attack.".to_owned())?;
|
||||
}
|
||||
|
||||
if player_item
|
||||
.active_effects
|
||||
.iter()
|
||||
.any(|v| v.0 == EffectType::Stunned)
|
||||
{
|
||||
user_error(
|
||||
"You stay still like a stunned mullet, unable to gain the composure to feint."
|
||||
.to_owned(),
|
||||
)?;
|
||||
}
|
||||
|
||||
switch_to_feint(ctx, &player_item).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
static VERB_INT: Verb = Verb;
|
||||
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
|
@ -14,6 +14,7 @@ use crate::{
|
||||
language,
|
||||
models::{
|
||||
consent::ConsentType,
|
||||
effect::EffectType,
|
||||
item::{ActiveClimb, DoorState, Item, ItemSpecialData, LocationActionType, SkillType},
|
||||
},
|
||||
regular_tasks::queued_command::{
|
||||
@ -354,6 +355,16 @@ async fn attempt_move_immediate(
|
||||
ctx.item.location.clone()
|
||||
};
|
||||
|
||||
if ctx
|
||||
.item
|
||||
.active_effects
|
||||
.iter()
|
||||
.any(|v| v.0 == EffectType::Stunned)
|
||||
&& !ctx.item.death_data.is_some()
|
||||
{
|
||||
user_error("You're too stunned to move.".to_owned())?;
|
||||
}
|
||||
|
||||
let session = ctx.get_session().await?;
|
||||
|
||||
match is_door_in_direction(ctx.trans, direction, &use_location).await? {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::services::combat::switch_to_power_attack;
|
||||
use crate::{models::effect::EffectType, services::combat::switch_to_power_attack};
|
||||
|
||||
use super::{get_player_item_or_fail, user_error, UResult, UserVerb, UserVerbRef, VerbContext};
|
||||
use async_trait::async_trait;
|
||||
@ -16,6 +16,14 @@ impl UserVerb for Verb {
|
||||
if player_item.death_data.is_some() {
|
||||
user_error("Power attack while dead? You can't even do a regular attack.".to_owned())?;
|
||||
}
|
||||
if player_item
|
||||
.active_effects
|
||||
.iter()
|
||||
.any(|v| v.0 == EffectType::Stunned)
|
||||
{
|
||||
user_error("You stay still like a stunned mullet, unable to gain the composure to powerattack.".to_owned())?;
|
||||
}
|
||||
|
||||
switch_to_power_attack(ctx, &player_item).await?;
|
||||
|
||||
Ok(())
|
||||
|
@ -6,6 +6,7 @@ pub enum EffectType {
|
||||
Ephemeral, // i.e. no enduring impact to show in status.
|
||||
Bandages,
|
||||
Bleed,
|
||||
Stunned,
|
||||
}
|
||||
|
||||
pub struct EffectSet {
|
||||
|
@ -7,7 +7,11 @@ use crate::{
|
||||
},
|
||||
models::{
|
||||
corp::CorpCommType,
|
||||
item::{AttackMode, DeathData, Item, ItemFlag, LocationActionType, SkillType, Subattack},
|
||||
effect::EffectType,
|
||||
item::{
|
||||
AttackMode, DeathData, Item, ItemFlag, LocationActionType, SkillType, StatType,
|
||||
Subattack,
|
||||
},
|
||||
journal::JournalType,
|
||||
task::{Task, TaskDetails, TaskMeta},
|
||||
},
|
||||
@ -24,7 +28,7 @@ use crate::{
|
||||
possession_type::{
|
||||
fist, possession_data, DamageDistribution, DamageType, WeaponAttackData, WeaponData,
|
||||
},
|
||||
species::{species_info_map, BodyPart},
|
||||
species::{species_info_map, BodyPart, SpeciesType},
|
||||
},
|
||||
DResult,
|
||||
};
|
||||
@ -33,11 +37,11 @@ use async_recursion::async_recursion;
|
||||
use async_trait::async_trait;
|
||||
use chrono::{Duration, Utc};
|
||||
use mockall_double::double;
|
||||
use rand::{prelude::IteratorRandom, Rng};
|
||||
use rand::{prelude::IteratorRandom, thread_rng, Rng};
|
||||
use rand_distr::{Distribution, Normal};
|
||||
use std::{sync::Arc, time};
|
||||
|
||||
use super::effect::{default_effects_for_type, run_effects};
|
||||
use super::effect::{cancel_effect, default_effects_for_type, run_effects};
|
||||
|
||||
pub async fn soak_damage<DamageDist: DamageDistribution>(
|
||||
trans: &DBTrans,
|
||||
@ -128,6 +132,31 @@ pub async fn soak_damage<DamageDist: DamageDistribution>(
|
||||
Ok(total_damage)
|
||||
}
|
||||
|
||||
async fn start_next_attack(
|
||||
ctx: &mut TaskRunContext<'_>,
|
||||
attacker_item: &Item,
|
||||
victim_item: &Item,
|
||||
weapon: &WeaponData,
|
||||
) -> DResult<()> {
|
||||
let msg_exp = &(weapon
|
||||
.normal_attack
|
||||
.start_message(&attacker_item, victim_item, true)
|
||||
+ ".\n");
|
||||
let msg_nonexp = &(weapon
|
||||
.normal_attack
|
||||
.start_message(&attacker_item, victim_item, false)
|
||||
+ ".\n");
|
||||
broadcast_to_room(
|
||||
ctx.trans,
|
||||
&attacker_item.location,
|
||||
None,
|
||||
msg_exp,
|
||||
Some(msg_nonexp),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn process_attack(
|
||||
ctx: &mut TaskRunContext<'_>,
|
||||
attacker_item: &mut Item,
|
||||
@ -163,6 +192,14 @@ async fn process_attack(
|
||||
Some(&msg_nonexp),
|
||||
)
|
||||
.await?;
|
||||
|
||||
match attacker_item.active_combat.as_mut() {
|
||||
Some(ac) if ac.attack_mode != AttackMode::NORMAL => {
|
||||
ac.attack_mode = AttackMode::NORMAL;
|
||||
ctx.trans.save_item_model(&attacker_item).await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@ -322,22 +359,154 @@ async fn process_attack(
|
||||
ctx.trans.save_item_model(victim_item).await?;
|
||||
}
|
||||
|
||||
let msg_exp = &(weapon
|
||||
.normal_attack
|
||||
.start_message(&attacker_item, victim_item, true)
|
||||
+ ".\n");
|
||||
let msg_nonexp = &(weapon
|
||||
.normal_attack
|
||||
.start_message(&attacker_item, victim_item, false)
|
||||
+ ".\n");
|
||||
broadcast_to_room(
|
||||
ctx.trans,
|
||||
&attacker_item.location,
|
||||
None,
|
||||
msg_exp,
|
||||
Some(msg_nonexp),
|
||||
)
|
||||
.await?;
|
||||
start_next_attack(ctx, &attacker_item, victim_item, weapon).await?;
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn process_feint(
|
||||
ctx: &mut TaskRunContext<'_>,
|
||||
attacker_item: &mut Item,
|
||||
victim_item: &mut Item,
|
||||
) -> DResult<bool> {
|
||||
if attacker_item
|
||||
.urges
|
||||
.as_ref()
|
||||
.map(|u| u.stress.value)
|
||||
.unwrap_or(0)
|
||||
> 8000
|
||||
{
|
||||
let msg_exp = format!(
|
||||
"{} looks like {} wanted to feint {}, but was too tired and stressed to do it.\n",
|
||||
attacker_item.display_for_sentence(true, 1, true),
|
||||
attacker_item.pronouns.subject,
|
||||
victim_item.display_for_sentence(true, 1, false),
|
||||
);
|
||||
let msg_nonexp = format!(
|
||||
"{} looks like {} wanted to feint {}, but was too tired and stressed to do it.\n",
|
||||
attacker_item.display_for_sentence(false, 1, true),
|
||||
attacker_item.pronouns.subject,
|
||||
victim_item.display_for_sentence(false, 1, false),
|
||||
);
|
||||
broadcast_to_room(
|
||||
ctx.trans,
|
||||
&attacker_item.location,
|
||||
None,
|
||||
&msg_exp,
|
||||
Some(&msg_nonexp),
|
||||
)
|
||||
.await?;
|
||||
match attacker_item.active_combat.as_mut() {
|
||||
Some(ac) => {
|
||||
ac.attack_mode = AttackMode::NORMAL;
|
||||
ctx.trans.save_item_model(&attacker_item).await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let attacker_brn = *attacker_item
|
||||
.total_stats
|
||||
.get(&StatType::Brains)
|
||||
.unwrap_or(&8.0);
|
||||
let victim_brn = *victim_item
|
||||
.total_stats
|
||||
.get(&StatType::Brains)
|
||||
.unwrap_or(&8.0);
|
||||
let fuzzed_diff = Normal::new(attacker_brn - victim_brn, 2.0)?.sample(&mut rand::thread_rng());
|
||||
|
||||
if fuzzed_diff <= -1.0 {
|
||||
broadcast_to_room(
|
||||
&ctx.trans,
|
||||
&attacker_item.location,
|
||||
None,
|
||||
&format!(
|
||||
"{} seems to have pulled off the feint so poorly {} confused {}!\n",
|
||||
&attacker_item.display_for_sentence(true, 1, true),
|
||||
&attacker_item.pronouns.object,
|
||||
&attacker_item.pronouns.intensive,
|
||||
),
|
||||
Some(&format!(
|
||||
"{} seems to have pulled off the feint so poorly {} confused {}!\n",
|
||||
&attacker_item.display_for_sentence(false, 1, true),
|
||||
&attacker_item.pronouns.object,
|
||||
&attacker_item.pronouns.intensive,
|
||||
)),
|
||||
)
|
||||
.await?;
|
||||
run_effects(
|
||||
&ctx.trans,
|
||||
default_effects_for_type()
|
||||
.get(&EffectType::Stunned)
|
||||
.unwrap(),
|
||||
attacker_item,
|
||||
&attacker_item.clone(),
|
||||
None,
|
||||
0.0,
|
||||
)
|
||||
.await?;
|
||||
} else if fuzzed_diff >= 1.0
|
||||
&& !victim_item
|
||||
.active_effects
|
||||
.iter()
|
||||
.any(|v| v.0 == EffectType::Stunned)
|
||||
{
|
||||
broadcast_to_room(
|
||||
&ctx.trans,
|
||||
&attacker_item.location,
|
||||
None,
|
||||
&format!(
|
||||
"{} is confused by {}'s antics!\n",
|
||||
&victim_item.display_for_sentence(true, 1, true),
|
||||
&attacker_item.display_for_sentence(true, 1, false),
|
||||
),
|
||||
Some(&format!(
|
||||
"{} is confused by {}'s antics!\n",
|
||||
&victim_item.display_for_sentence(false, 1, true),
|
||||
&attacker_item.display_for_sentence(false, 1, false),
|
||||
)),
|
||||
)
|
||||
.await?;
|
||||
run_effects(
|
||||
&ctx.trans,
|
||||
default_effects_for_type()
|
||||
.get(&EffectType::Stunned)
|
||||
.unwrap(),
|
||||
victim_item,
|
||||
attacker_item,
|
||||
None,
|
||||
0.0,
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
broadcast_to_room(
|
||||
&ctx.trans,
|
||||
&attacker_item.location,
|
||||
None,
|
||||
&format!(
|
||||
"{} doesn't seem to have fallen for {}'s unenlightened attempt at a feint.\n",
|
||||
&victim_item.display_for_sentence(true, 1, true),
|
||||
&attacker_item.display_for_sentence(true, 1, false)
|
||||
),
|
||||
Some(&format!(
|
||||
"{} doesn't seem to have fallen for {}'s unenlightened attempt at a feint.\n",
|
||||
&victim_item.display_for_sentence(false, 1, true),
|
||||
&attacker_item.display_for_sentence(false, 1, false)
|
||||
)),
|
||||
)
|
||||
.await?;
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
match attacker_item.active_combat.as_mut() {
|
||||
Some(ac) => {
|
||||
ac.attack_mode = AttackMode::NORMAL;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ctx.trans.save_item_model(&attacker_item).await?;
|
||||
ctx.trans.save_item_model(&victim_item).await?;
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
@ -382,28 +551,51 @@ impl TaskHandler for AttackTaskHandler {
|
||||
let (weapon_it, weapon) = what_wielded(ctx.trans, &attacker_item).await?;
|
||||
|
||||
let mut attacker_item_mut = (*attacker_item).clone();
|
||||
process_attack(
|
||||
ctx,
|
||||
&mut attacker_item_mut,
|
||||
&weapon_it,
|
||||
&mut victim_item,
|
||||
if attacker_item
|
||||
.active_combat
|
||||
.as_ref()
|
||||
.map(|ac| ac.attack_mode == AttackMode::POWER)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if let Some(pow) = weapon.power_attack.as_ref() {
|
||||
pow
|
||||
|
||||
if attacker_item_mut
|
||||
.active_effects
|
||||
.iter()
|
||||
.any(|v| v.0 == EffectType::Stunned)
|
||||
{
|
||||
match attacker_item_mut.active_combat.as_mut() {
|
||||
Some(ac) if ac.attack_mode != AttackMode::NORMAL => {
|
||||
ac.attack_mode = AttackMode::NORMAL;
|
||||
ctx.trans.save_item_model(&attacker_item_mut).await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return Ok(Some(attack_speed(&attacker_item_mut)));
|
||||
}
|
||||
|
||||
let mode = attacker_item
|
||||
.active_combat
|
||||
.as_ref()
|
||||
.map(|ac| ac.attack_mode.clone())
|
||||
.unwrap_or(AttackMode::NORMAL);
|
||||
|
||||
if mode == AttackMode::FEINT {
|
||||
if process_feint(ctx, &mut attacker_item_mut, &mut victim_item).await? {
|
||||
start_next_attack(ctx, &mut attacker_item_mut, &mut victim_item, &weapon).await?;
|
||||
}
|
||||
} else {
|
||||
process_attack(
|
||||
ctx,
|
||||
&mut attacker_item_mut,
|
||||
&weapon_it,
|
||||
&mut victim_item,
|
||||
if mode == AttackMode::POWER {
|
||||
if let Some(pow) = weapon.power_attack.as_ref() {
|
||||
pow
|
||||
} else {
|
||||
&weapon.normal_attack
|
||||
}
|
||||
} else {
|
||||
&weapon.normal_attack
|
||||
}
|
||||
} else {
|
||||
&weapon.normal_attack
|
||||
},
|
||||
&weapon,
|
||||
)
|
||||
.await?;
|
||||
},
|
||||
&weapon,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// We re-check this on the next tick, rather than going off if the victim
|
||||
// died. That prevents a bug when re-focusing where we re-schedule and then
|
||||
@ -652,6 +844,10 @@ pub async fn handle_resurrect(trans: &DBTrans, player: &mut Item) -> DResult<boo
|
||||
user.experience.xp_change_for_this_reroll -= lost_xp as i64;
|
||||
player.temporary_buffs = vec![];
|
||||
player.flags.push(ItemFlag::HasUrges);
|
||||
for effect in &player.active_effects {
|
||||
cancel_effect(trans, player, effect).await?;
|
||||
}
|
||||
player.active_effects = vec![];
|
||||
calculate_total_stats_skills_for_user(player, &user);
|
||||
recalculate_urge_growth(trans, player).await?;
|
||||
|
||||
@ -993,6 +1189,107 @@ pub async fn switch_to_power_attack(ctx: &VerbContext<'_>, who: &Arc<Item>) -> U
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn switch_to_feint(ctx: &VerbContext<'_>, who: &Arc<Item>) -> UResult<()> {
|
||||
let (attacking_type, attacking_code) = match who
|
||||
.active_combat
|
||||
.as_ref()
|
||||
.and_then(|ac| ac.attacking.as_ref())
|
||||
.and_then(|a| a.split_once("/"))
|
||||
{
|
||||
None => user_error("Calm down satan, you're not currently attacking anyone!".to_owned())?,
|
||||
Some(v) => v,
|
||||
};
|
||||
|
||||
let feint_delay: i64 = 30;
|
||||
match who
|
||||
.tactic_use
|
||||
.last_feint
|
||||
.map(|lp| (lp + Duration::seconds(feint_delay)) - Utc::now())
|
||||
{
|
||||
None => {}
|
||||
Some(d) if d < Duration::seconds(0) => {}
|
||||
Some(d) => user_error(format!(
|
||||
"You can't feint again for another {} seconds.",
|
||||
d.num_seconds()
|
||||
))?,
|
||||
}
|
||||
|
||||
let to_whom = match ctx
|
||||
.trans
|
||||
.find_item_by_type_code(attacking_type, attacking_code)
|
||||
.await?
|
||||
{
|
||||
None => user_error("They seem to be gone!".to_owned())?,
|
||||
Some(v) => v,
|
||||
};
|
||||
|
||||
if to_whom.species != SpeciesType::Human && to_whom.species != SpeciesType::Robot {
|
||||
user_error(format!(
|
||||
"You don't think {} will pay any attention to a feint.",
|
||||
to_whom.display_for_session(&ctx.session_dat)
|
||||
))?;
|
||||
}
|
||||
|
||||
let feints = vec![
|
||||
|p: &str, v: &str| {
|
||||
format!(
|
||||
"{} flicks sand in {}'s eyes and shouts \"pocket sand\".\n",
|
||||
&p, &v
|
||||
)
|
||||
},
|
||||
|p: &str, v: &str| {
|
||||
format!(
|
||||
"{} waves a piece of string at {} and asks \"how long is a piece of string?\".\n",
|
||||
&p, &v
|
||||
)
|
||||
},
|
||||
|p: &str, v: &str| format!("{} points out the futility of violence to {}.\n", &p, &v),
|
||||
|p: &str, v: &str| format!("{} references {}'s mother in a pejorative way.\n", &p, &v),
|
||||
|p: &str, v: &str| {
|
||||
format!(
|
||||
"{} asks if {} suffers from pneumonoultramicroscopicsilicovolcanoconiosis.\n",
|
||||
&p, &v
|
||||
)
|
||||
},
|
||||
|p: &str, v: &str| {
|
||||
format!(
|
||||
"{} notes that consciousness is an illusion, so {} might as well not bother.\n",
|
||||
&p, &v
|
||||
)
|
||||
},
|
||||
];
|
||||
let feint = feints.iter().choose(&mut thread_rng()).unwrap();
|
||||
let msg_exp = feint(
|
||||
&who.display_for_sentence(true, 1, true),
|
||||
&to_whom.display_for_sentence(true, 1, false),
|
||||
);
|
||||
let msg_nonexp = feint(
|
||||
&who.display_for_sentence(false, 1, true),
|
||||
&to_whom.display_for_sentence(false, 1, false),
|
||||
);
|
||||
broadcast_to_room(ctx.trans, &who.location, None, &msg_exp, Some(&msg_nonexp)).await?;
|
||||
|
||||
let mut who_mut = (**who).clone();
|
||||
who_mut.active_combat.as_mut().map(|ac| {
|
||||
ac.attack_mode = AttackMode::FEINT;
|
||||
});
|
||||
who_mut.tactic_use.last_feint = Some(Utc::now());
|
||||
ctx.trans.save_item_model(&who_mut).await?;
|
||||
ctx.trans
|
||||
.upsert_task(&Task {
|
||||
meta: TaskMeta {
|
||||
task_code: format!("{}/{}", &who.item_type, &who.item_code),
|
||||
next_scheduled: Utc::now()
|
||||
+ chrono::Duration::milliseconds(attack_speed(&who_mut).as_millis() as i64),
|
||||
..Default::default()
|
||||
},
|
||||
details: TaskDetails::AttackTick,
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct NPCRecloneTaskHandler;
|
||||
#[async_trait]
|
||||
impl TaskHandler for NPCRecloneTaskHandler {
|
||||
@ -1062,3 +1359,18 @@ impl TaskHandler for RotCorpseTaskHandler {
|
||||
}
|
||||
}
|
||||
pub static ROT_CORPSE_HANDLER: &'static (dyn TaskHandler + Sync + Send) = &RotCorpseTaskHandler;
|
||||
|
||||
#[cfg(test)]
|
||||
mod Tests {
|
||||
use crate::{models::effect::EffectType, services::effect::default_effects_for_type};
|
||||
|
||||
#[test]
|
||||
fn default_for_stunned() {
|
||||
assert_eq!(
|
||||
default_effects_for_type()
|
||||
.get(&EffectType::Stunned)
|
||||
.is_some(),
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -332,11 +332,45 @@ pub async fn run_effects(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cancel_effect(
|
||||
trans: &DBTrans,
|
||||
character: &Item,
|
||||
effect: &(EffectType, i64),
|
||||
) -> DResult<()> {
|
||||
trans
|
||||
.delete_task(
|
||||
"DelayedMessage",
|
||||
&format!(
|
||||
"{}/{}/{}",
|
||||
&character.item_type, &character.item_code, effect.1
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
trans
|
||||
.delete_task(
|
||||
"DelayedHealth",
|
||||
&format!(
|
||||
"{}/{}/{}",
|
||||
&character.item_type, &character.item_code, effect.1
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
trans
|
||||
.delete_task(
|
||||
"DispelEffect",
|
||||
&format!(
|
||||
"{}/{}/{}",
|
||||
&character.item_type, &character.item_code, effect.1
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
|
||||
static MAP: OnceCell<BTreeMap<EffectType, EffectSet>> = OnceCell::new();
|
||||
MAP.get_or_init(|| {
|
||||
vec![(
|
||||
EffectType::Bleed,
|
||||
vec![
|
||||
EffectSet {
|
||||
effect_type: EffectType::Bleed,
|
||||
effects: vec![
|
||||
@ -444,8 +478,37 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
|
||||
},
|
||||
],
|
||||
},
|
||||
)]
|
||||
.into_iter()
|
||||
.collect()
|
||||
EffectSet {
|
||||
effect_type: EffectType::Stunned,
|
||||
effects: vec![
|
||||
Effect::BroadcastMessage {
|
||||
delay_secs: 0,
|
||||
messagef: Box::new(|_player, _item, target| (
|
||||
format!(ansi!("<blue>{} is stunned!<reset>\n"),
|
||||
target.display_for_sentence(true, 1, true),
|
||||
),
|
||||
format!(ansi!("<blue>{} is stunned!<reset>\n"),
|
||||
target.display_for_sentence(false, 1, true))
|
||||
))
|
||||
},
|
||||
Effect::BroadcastMessage {
|
||||
delay_secs: 30,
|
||||
messagef: Box::new(|_player, _item, target| (
|
||||
format!(ansi!("<blue>{} seems to have returned to {} senses!<reset>\n"),
|
||||
target.display_for_sentence(true, 1, true),
|
||||
&target.pronouns.object,
|
||||
),
|
||||
format!(ansi!("<blue>{} seems to have returned to {} senses!<reset>\n"),
|
||||
target.display_for_sentence(false, 1, true),
|
||||
&target.pronouns.object,
|
||||
)
|
||||
))
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
.into_iter()
|
||||
.map(|et| (et.effect_type.clone(), et))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user