Allow for occassional 'power' attacks
They do more damage but take longer.
This commit is contained in:
parent
6ac3f676be
commit
aa4828469a
@ -55,6 +55,7 @@ pub mod open;
|
|||||||
mod page;
|
mod page;
|
||||||
pub mod parsing;
|
pub mod parsing;
|
||||||
pub mod pay;
|
pub mod pay;
|
||||||
|
mod pow;
|
||||||
pub mod put;
|
pub mod put;
|
||||||
mod quit;
|
mod quit;
|
||||||
pub mod recline;
|
pub mod recline;
|
||||||
@ -217,6 +218,11 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
|||||||
"reply" => page::VERB,
|
"reply" => page::VERB,
|
||||||
|
|
||||||
"pay" => pay::VERB,
|
"pay" => pay::VERB,
|
||||||
|
|
||||||
|
"pow" => pow::VERB,
|
||||||
|
"power" => pow::VERB,
|
||||||
|
"powerattack" => pow::VERB,
|
||||||
|
|
||||||
"put" => put::VERB,
|
"put" => put::VERB,
|
||||||
"recline" => recline::VERB,
|
"recline" => recline::VERB,
|
||||||
"remove" => remove::VERB,
|
"remove" => remove::VERB,
|
||||||
|
25
blastmud_game/src/message_handler/user_commands/pow.rs
Normal file
25
blastmud_game/src/message_handler/user_commands/pow.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use crate::services::combat::switch_to_power_attack;
|
||||||
|
|
||||||
|
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("Power attack while dead? You can't even do a regular attack.".to_owned())?;
|
||||||
|
}
|
||||||
|
switch_to_power_attack(ctx, &player_item).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static VERB_INT: Verb = Verb;
|
||||||
|
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
|
@ -319,11 +319,25 @@ pub enum ItemFlag {
|
|||||||
DontListInLook,
|
DontListInLook,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
pub enum AttackMode {
|
||||||
|
NORMAL,
|
||||||
|
POWER,
|
||||||
|
FEINT,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AttackMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
AttackMode::NORMAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct ActiveCombat {
|
pub struct ActiveCombat {
|
||||||
pub attacking: Option<String>,
|
pub attacking: Option<String>,
|
||||||
pub attacked_by: Vec<String>,
|
pub attacked_by: Vec<String>,
|
||||||
|
pub attack_mode: AttackMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ActiveCombat {
|
impl Default for ActiveCombat {
|
||||||
@ -331,6 +345,7 @@ impl Default for ActiveCombat {
|
|||||||
Self {
|
Self {
|
||||||
attacking: None,
|
attacking: None,
|
||||||
attacked_by: vec![],
|
attacked_by: vec![],
|
||||||
|
attack_mode: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,46 +471,63 @@ pub struct FollowData {
|
|||||||
pub state: FollowState,
|
pub state: FollowState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct TacticUse {
|
||||||
|
pub last_pow: Option<DateTime<Utc>>,
|
||||||
|
pub last_feint: Option<DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TacticUse {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
last_pow: None,
|
||||||
|
last_feint: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
pub item_code: String,
|
|
||||||
pub item_type: String,
|
|
||||||
pub possession_type: Option<PossessionType>,
|
|
||||||
pub display: String,
|
|
||||||
pub display_less_explicit: Option<String>,
|
|
||||||
pub details: Option<String>,
|
|
||||||
pub details_less_explicit: Option<String>,
|
|
||||||
pub details_dyn_suffix: Option<String>,
|
|
||||||
pub aliases: Vec<String>,
|
|
||||||
pub location: String, // Item reference as item_type/item_code.
|
|
||||||
pub action_type: LocationActionType,
|
pub action_type: LocationActionType,
|
||||||
pub action_type_started: Option<DateTime<Utc>>,
|
pub action_type_started: Option<DateTime<Utc>>,
|
||||||
pub presence_target: Option<String>, // e.g. what are they sitting on.
|
|
||||||
pub is_static: bool,
|
|
||||||
pub death_data: Option<DeathData>,
|
|
||||||
pub species: SpeciesType,
|
|
||||||
pub health: u64,
|
|
||||||
pub total_xp: u64,
|
|
||||||
pub total_stats: BTreeMap<StatType, f64>,
|
|
||||||
pub total_skills: BTreeMap<SkillType, f64>,
|
|
||||||
pub temporary_buffs: Vec<Buff>,
|
|
||||||
pub pronouns: Pronouns,
|
|
||||||
pub flags: Vec<ItemFlag>,
|
|
||||||
pub sex: Option<Sex>,
|
|
||||||
pub active_combat: Option<ActiveCombat>,
|
|
||||||
pub active_climb: Option<ActiveClimb>,
|
pub active_climb: Option<ActiveClimb>,
|
||||||
pub weight: u64,
|
pub active_combat: Option<ActiveCombat>,
|
||||||
pub charges: u8,
|
|
||||||
pub special_data: Option<ItemSpecialData>,
|
|
||||||
pub dynamic_entrance: Option<DynamicEntrance>,
|
|
||||||
pub owner: Option<String>,
|
|
||||||
pub door_states: Option<BTreeMap<Direction, DoorState>>,
|
|
||||||
pub following: Option<FollowData>,
|
|
||||||
pub queue: VecDeque<QueueCommand>,
|
|
||||||
pub urges: Option<Urges>,
|
|
||||||
pub liquid_details: Option<LiquidDetails>,
|
|
||||||
pub active_effects: Vec<(EffectType, i64)>,
|
pub active_effects: Vec<(EffectType, i64)>,
|
||||||
|
pub aliases: Vec<String>,
|
||||||
|
pub charges: u8,
|
||||||
|
pub death_data: Option<DeathData>,
|
||||||
|
pub details: Option<String>,
|
||||||
|
pub details_dyn_suffix: Option<String>,
|
||||||
|
pub details_less_explicit: Option<String>,
|
||||||
|
pub display: String,
|
||||||
|
pub display_less_explicit: Option<String>,
|
||||||
|
pub door_states: Option<BTreeMap<Direction, DoorState>>,
|
||||||
|
pub dynamic_entrance: Option<DynamicEntrance>,
|
||||||
|
pub flags: Vec<ItemFlag>,
|
||||||
|
pub following: Option<FollowData>,
|
||||||
|
pub health: u64,
|
||||||
|
pub is_static: bool,
|
||||||
|
pub item_code: String,
|
||||||
|
pub item_type: String,
|
||||||
|
pub liquid_details: Option<LiquidDetails>,
|
||||||
|
pub location: String, // Item reference as item_type/item_code.
|
||||||
|
pub owner: Option<String>,
|
||||||
|
pub possession_type: Option<PossessionType>,
|
||||||
|
pub presence_target: Option<String>, // e.g. what are they sitting on.
|
||||||
|
pub pronouns: Pronouns,
|
||||||
|
pub queue: VecDeque<QueueCommand>,
|
||||||
|
pub sex: Option<Sex>,
|
||||||
|
pub special_data: Option<ItemSpecialData>,
|
||||||
|
pub species: SpeciesType,
|
||||||
|
pub tactic_use: TacticUse,
|
||||||
|
pub temporary_buffs: Vec<Buff>,
|
||||||
|
pub total_skills: BTreeMap<SkillType, f64>,
|
||||||
|
pub total_stats: BTreeMap<StatType, f64>,
|
||||||
|
pub total_xp: u64,
|
||||||
|
pub urges: Option<Urges>,
|
||||||
|
pub weight: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Item {
|
impl Item {
|
||||||
@ -583,43 +615,44 @@ impl Item {
|
|||||||
impl Default for Item {
|
impl Default for Item {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
item_code: "unset".to_owned(),
|
|
||||||
item_type: "unset".to_owned(),
|
|
||||||
possession_type: None,
|
|
||||||
display: "Item".to_owned(),
|
|
||||||
display_less_explicit: None,
|
|
||||||
details: None,
|
|
||||||
details_less_explicit: None,
|
|
||||||
details_dyn_suffix: None,
|
|
||||||
aliases: vec![],
|
|
||||||
location: "room/storage".to_owned(),
|
|
||||||
action_type: LocationActionType::Normal,
|
action_type: LocationActionType::Normal,
|
||||||
action_type_started: None,
|
action_type_started: None,
|
||||||
presence_target: None,
|
|
||||||
is_static: false,
|
|
||||||
death_data: None,
|
|
||||||
species: SpeciesType::Human,
|
|
||||||
health: 24,
|
|
||||||
total_xp: 0,
|
|
||||||
total_stats: BTreeMap::new(),
|
|
||||||
total_skills: BTreeMap::new(),
|
|
||||||
temporary_buffs: Vec::new(),
|
|
||||||
pronouns: Pronouns::default_inanimate(),
|
|
||||||
flags: vec![],
|
|
||||||
sex: None,
|
|
||||||
active_combat: Some(Default::default()),
|
|
||||||
active_climb: None,
|
active_climb: None,
|
||||||
weight: 0,
|
active_combat: Some(Default::default()),
|
||||||
charges: 0,
|
|
||||||
special_data: None,
|
|
||||||
dynamic_entrance: None,
|
|
||||||
owner: None,
|
|
||||||
door_states: None,
|
|
||||||
following: None,
|
|
||||||
queue: VecDeque::new(),
|
|
||||||
urges: None,
|
|
||||||
liquid_details: None,
|
|
||||||
active_effects: vec![],
|
active_effects: vec![],
|
||||||
|
aliases: vec![],
|
||||||
|
charges: 0,
|
||||||
|
death_data: None,
|
||||||
|
details: None,
|
||||||
|
details_dyn_suffix: None,
|
||||||
|
details_less_explicit: None,
|
||||||
|
display: "Item".to_owned(),
|
||||||
|
display_less_explicit: None,
|
||||||
|
door_states: None,
|
||||||
|
dynamic_entrance: None,
|
||||||
|
flags: vec![],
|
||||||
|
following: None,
|
||||||
|
health: 24,
|
||||||
|
is_static: false,
|
||||||
|
item_code: "unset".to_owned(),
|
||||||
|
item_type: "unset".to_owned(),
|
||||||
|
liquid_details: None,
|
||||||
|
location: "room/storage".to_owned(),
|
||||||
|
owner: None,
|
||||||
|
possession_type: None,
|
||||||
|
presence_target: None,
|
||||||
|
pronouns: Pronouns::default_inanimate(),
|
||||||
|
queue: VecDeque::new(),
|
||||||
|
sex: None,
|
||||||
|
special_data: None,
|
||||||
|
species: SpeciesType::Human,
|
||||||
|
tactic_use: Default::default(),
|
||||||
|
temporary_buffs: Vec::new(),
|
||||||
|
total_skills: BTreeMap::new(),
|
||||||
|
total_stats: BTreeMap::new(),
|
||||||
|
total_xp: 0,
|
||||||
|
urges: None,
|
||||||
|
weight: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,11 @@ use crate::db::DBTrans;
|
|||||||
use crate::{
|
use crate::{
|
||||||
message_handler::user_commands::{
|
message_handler::user_commands::{
|
||||||
follow::cancel_follow_by_leader, stand::stand_if_needed, user_error, CommandHandlingError,
|
follow::cancel_follow_by_leader, stand::stand_if_needed, user_error, CommandHandlingError,
|
||||||
UResult,
|
UResult, VerbContext,
|
||||||
},
|
},
|
||||||
models::{
|
models::{
|
||||||
corp::CorpCommType,
|
corp::CorpCommType,
|
||||||
item::{DeathData, Item, ItemFlag, LocationActionType, SkillType, Subattack},
|
item::{AttackMode, DeathData, Item, ItemFlag, LocationActionType, SkillType, Subattack},
|
||||||
journal::JournalType,
|
journal::JournalType,
|
||||||
task::{Task, TaskDetails, TaskMeta},
|
task::{Task, TaskDetails, TaskMeta},
|
||||||
},
|
},
|
||||||
@ -31,7 +31,7 @@ use crate::{
|
|||||||
use ansi::ansi;
|
use ansi::ansi;
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::Utc;
|
use chrono::{Duration, Utc};
|
||||||
use mockall_double::double;
|
use mockall_double::double;
|
||||||
use rand::{prelude::IteratorRandom, Rng};
|
use rand::{prelude::IteratorRandom, Rng};
|
||||||
use rand_distr::{Distribution, Normal};
|
use rand_distr::{Distribution, Normal};
|
||||||
@ -221,6 +221,10 @@ async fn process_attack(
|
|||||||
Some(&msg_nonexp),
|
Some(&msg_nonexp),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
match attacker_item.active_combat.as_mut() {
|
||||||
|
Some(ac) => ac.attack_mode = AttackMode::NORMAL,
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
ctx.trans.save_item_model(&attacker_item).await?;
|
ctx.trans.save_item_model(&attacker_item).await?;
|
||||||
ctx.trans.save_item_model(&victim_item).await?;
|
ctx.trans.save_item_model(&victim_item).await?;
|
||||||
} else {
|
} else {
|
||||||
@ -244,6 +248,10 @@ async fn process_attack(
|
|||||||
.sample(&mut rand::thread_rng())
|
.sample(&mut rand::thread_rng())
|
||||||
.floor()
|
.floor()
|
||||||
.max(1.0) as i64;
|
.max(1.0) as i64;
|
||||||
|
match attacker_item.active_combat.as_mut() {
|
||||||
|
Some(ac) => ac.attack_mode = AttackMode::NORMAL,
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
ctx.trans.save_item_model(&attacker_item).await?;
|
ctx.trans.save_item_model(&attacker_item).await?;
|
||||||
let actual_damage = soak_damage(
|
let actual_damage = soak_damage(
|
||||||
&ctx.trans,
|
&ctx.trans,
|
||||||
@ -314,8 +322,14 @@ async fn process_attack(
|
|||||||
ctx.trans.save_item_model(victim_item).await?;
|
ctx.trans.save_item_model(victim_item).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg_exp = &(attack.start_message(&attacker_item, victim_item, true) + ".\n");
|
let msg_exp = &(weapon
|
||||||
let msg_nonexp = &(attack.start_message(&attacker_item, victim_item, false) + ".\n");
|
.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(
|
broadcast_to_room(
|
||||||
ctx.trans,
|
ctx.trans,
|
||||||
&attacker_item.location,
|
&attacker_item.location,
|
||||||
@ -373,7 +387,20 @@ impl TaskHandler for AttackTaskHandler {
|
|||||||
&mut attacker_item_mut,
|
&mut attacker_item_mut,
|
||||||
&weapon_it,
|
&weapon_it,
|
||||||
&mut victim_item,
|
&mut victim_item,
|
||||||
&weapon.normal_attack,
|
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
|
||||||
|
} else {
|
||||||
|
&weapon.normal_attack
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
&weapon.normal_attack
|
||||||
|
},
|
||||||
&weapon,
|
&weapon,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -747,8 +774,20 @@ async fn what_wielded(
|
|||||||
Ok((who.clone(), fist()))
|
Ok((who.clone(), fist()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attack_speed(_who: &Item) -> time::Duration {
|
fn attack_speed(who: &Item) -> time::Duration {
|
||||||
time::Duration::from_secs(5)
|
let base_time = 5;
|
||||||
|
|
||||||
|
let time_multiplier = who
|
||||||
|
.active_combat
|
||||||
|
.as_ref()
|
||||||
|
.map(|ac| match ac.attack_mode {
|
||||||
|
AttackMode::NORMAL => 1,
|
||||||
|
AttackMode::POWER => 2,
|
||||||
|
AttackMode::FEINT => 1,
|
||||||
|
})
|
||||||
|
.unwrap_or(1);
|
||||||
|
|
||||||
|
time::Duration::from_secs(base_time * time_multiplier)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_recursion]
|
#[async_recursion]
|
||||||
@ -828,10 +867,11 @@ pub async fn start_attack_mut(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
by_whom
|
let ac = by_whom
|
||||||
.active_combat
|
.active_combat
|
||||||
.get_or_insert_with(|| Default::default())
|
.get_or_insert_with(|| Default::default());
|
||||||
.attacking = Some(format!("{}/{}", &to_whom.item_type, &to_whom.item_code));
|
ac.attacking = Some(format!("{}/{}", &to_whom.item_type, &to_whom.item_code));
|
||||||
|
ac.attack_mode = AttackMode::NORMAL;
|
||||||
by_whom.action_type = LocationActionType::Attacking(Subattack::Normal);
|
by_whom.action_type = LocationActionType::Attacking(Subattack::Normal);
|
||||||
to_whom
|
to_whom
|
||||||
.active_combat
|
.active_combat
|
||||||
@ -887,6 +927,72 @@ pub async fn corpsify_item(trans: &DBTrans, base_item: &Item) -> DResult<Item> {
|
|||||||
Ok(new_item)
|
Ok(new_item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn switch_to_power_attack(ctx: &VerbContext<'_>, who: &Arc<Item>) -> UResult<()> {
|
||||||
|
let (wield_it, wielded) = what_wielded(&ctx.trans, who).await?;
|
||||||
|
let pow_att = match wielded.power_attack.as_ref() {
|
||||||
|
None => user_error(format!(
|
||||||
|
"{} is unsuitable for powerattacking",
|
||||||
|
wield_it.display_for_sentence(!ctx.session_dat.less_explicit_mode, 1, true)
|
||||||
|
))?,
|
||||||
|
Some(v) => v,
|
||||||
|
};
|
||||||
|
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 pow_delay: i64 = 30;
|
||||||
|
match who
|
||||||
|
.tactic_use
|
||||||
|
.last_pow
|
||||||
|
.map(|lp| (lp + Duration::seconds(pow_delay)) - Utc::now())
|
||||||
|
{
|
||||||
|
None => {}
|
||||||
|
Some(d) if d < Duration::seconds(0) => {}
|
||||||
|
Some(d) => user_error(format!(
|
||||||
|
"You can't powerattack 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,
|
||||||
|
};
|
||||||
|
let msg_exp = pow_att.start_message(who, &to_whom, true) + ".\n";
|
||||||
|
let msg_nonexp = pow_att.start_message(who, &to_whom, false) + ".\n";
|
||||||
|
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::POWER;
|
||||||
|
});
|
||||||
|
who_mut.tactic_use.last_pow = 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;
|
pub struct NPCRecloneTaskHandler;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl TaskHandler for NPCRecloneTaskHandler {
|
impl TaskHandler for NPCRecloneTaskHandler {
|
||||||
|
@ -135,6 +135,7 @@ pub struct WeaponData {
|
|||||||
pub raw_min_to_learn: f64,
|
pub raw_min_to_learn: f64,
|
||||||
pub raw_max_to_learn: f64,
|
pub raw_max_to_learn: f64,
|
||||||
pub normal_attack: WeaponAttackData,
|
pub normal_attack: WeaponAttackData,
|
||||||
|
pub power_attack: Option<WeaponAttackData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WeaponData {
|
impl Default for WeaponData {
|
||||||
@ -144,6 +145,7 @@ impl Default for WeaponData {
|
|||||||
raw_min_to_learn: 0.0,
|
raw_min_to_learn: 0.0,
|
||||||
raw_max_to_learn: 15.0,
|
raw_max_to_learn: 15.0,
|
||||||
normal_attack: Default::default(),
|
normal_attack: Default::default(),
|
||||||
|
power_attack: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -496,6 +498,26 @@ pub fn fist() -> &'static WeaponData {
|
|||||||
})],
|
})],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
power_attack: Some(WeaponAttackData {
|
||||||
|
start_messages: vec![Box::new(|attacker, victim, exp| {
|
||||||
|
format!(
|
||||||
|
"{} tenses for a power fist swings at {}",
|
||||||
|
&attacker.display_for_sentence(exp, 1, true),
|
||||||
|
&victim.display_for_sentence(exp, 1, false),
|
||||||
|
)
|
||||||
|
})],
|
||||||
|
success_messages: vec![Box::new(|attacker, victim, part, exp| {
|
||||||
|
format!(
|
||||||
|
"{}'s fists smash with great force into {}'s {}",
|
||||||
|
&attacker.display_for_sentence(exp, 1, true),
|
||||||
|
&victim.display_for_sentence(exp, 1, false),
|
||||||
|
&part.display(victim.sex.clone())
|
||||||
|
)
|
||||||
|
})],
|
||||||
|
mean_damage: 3.0,
|
||||||
|
stdev_damage: 4.0,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,30 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
|
|||||||
stdev_damage: 3.0,
|
stdev_damage: 3.0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
power_attack: Some(WeaponAttackData {
|
||||||
|
start_messages: vec!(
|
||||||
|
Box::new(|attacker, victim, exp|
|
||||||
|
format!("{} rears back {} antenna whip for a power attack on {}",
|
||||||
|
&attacker.display_for_sentence(exp, 1, true),
|
||||||
|
&attacker.pronouns.possessive,
|
||||||
|
&victim.display_for_sentence(exp, 1, false),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
success_messages: vec!(
|
||||||
|
Box::new(|attacker, victim, part, exp|
|
||||||
|
format!("{}'s antenna whip hits {}'s {} with great force",
|
||||||
|
&attacker.display_for_sentence(exp, 1, true),
|
||||||
|
&victim.display_for_sentence(exp, 1, false),
|
||||||
|
&part.display(victim.sex.clone())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
mean_damage: 9.0,
|
||||||
|
stdev_damage: 6.0,
|
||||||
|
crit_effects: vec![(0.05, EffectType::Bleed)],
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -80,6 +104,32 @@ pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
|
|||||||
other_damage_types: vec!((0.25, DamageType::Slash)),
|
other_damage_types: vec!((0.25, DamageType::Slash)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
power_attack: Some(WeaponAttackData {
|
||||||
|
start_messages: vec!(
|
||||||
|
Box::new(|attacker, victim, exp|
|
||||||
|
format!("{} rears back {} leather whip for a power strike on {}",
|
||||||
|
&attacker.display_for_sentence(exp, 1, true),
|
||||||
|
&attacker.pronouns.possessive,
|
||||||
|
&victim.display_for_sentence(exp, 1, false),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
success_messages: vec!(
|
||||||
|
Box::new(|attacker, victim, part, exp|
|
||||||
|
format!("{}'s leather whip hits {}'s {} with great force",
|
||||||
|
&attacker.display_for_sentence(exp, 1, true),
|
||||||
|
&victim.display_for_sentence(exp, 1, false),
|
||||||
|
&part.display(victim.sex.clone())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
mean_damage: 12.0,
|
||||||
|
stdev_damage: 4.0,
|
||||||
|
base_damage_type: DamageType::Beat,
|
||||||
|
crit_effects: vec![(0.3, EffectType::Bleed)],
|
||||||
|
other_damage_types: vec!((0.25, DamageType::Slash)),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
Loading…
Reference in New Issue
Block a user