2020-07-26 03:06:53 +00:00
|
|
|
use crate::{
|
|
|
|
comp::{
|
|
|
|
projectile, Body, CharacterState, EnergySource, Gravity, LightEmitter, Projectile,
|
|
|
|
StateUpdate,
|
|
|
|
},
|
|
|
|
event::ServerEvent,
|
|
|
|
states::utils::*,
|
|
|
|
sys::character_behavior::*,
|
|
|
|
};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub struct Data {
|
|
|
|
/// Whether the attack fired already
|
|
|
|
pub exhausted: bool,
|
|
|
|
/// How much energy is drained per second when charging
|
|
|
|
pub energy_drain: u32,
|
|
|
|
/// How much damage is dealt with no charge
|
|
|
|
pub initial_damage: u32,
|
|
|
|
/// How much damage is dealt with max charge
|
|
|
|
pub max_damage: u32,
|
2020-08-25 12:21:25 +00:00
|
|
|
/// How much knockback there is with no charge
|
2020-07-26 03:06:53 +00:00
|
|
|
pub initial_knockback: f32,
|
|
|
|
/// How much knockback there is at max charge
|
|
|
|
pub max_knockback: f32,
|
|
|
|
/// How long the weapon needs to be prepared for
|
|
|
|
pub prepare_duration: Duration,
|
|
|
|
/// How long it takes to charge the weapon to max damage and knockback
|
|
|
|
pub charge_duration: Duration,
|
|
|
|
/// How long the state has been charging
|
|
|
|
pub charge_timer: Duration,
|
|
|
|
/// How long the state has until exiting
|
|
|
|
pub recover_duration: Duration,
|
|
|
|
/// Projectile information
|
|
|
|
pub projectile_body: Body,
|
|
|
|
pub projectile_light: Option<LightEmitter>,
|
2020-08-06 18:17:38 +00:00
|
|
|
pub projectile_gravity: Option<Gravity>,
|
|
|
|
pub initial_projectile_speed: f32,
|
|
|
|
pub max_projectile_speed: f32,
|
2020-07-26 03:06:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CharacterBehavior for Data {
|
|
|
|
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
|
|
|
let mut update = StateUpdate::from(data);
|
|
|
|
|
|
|
|
handle_move(data, &mut update, 0.3);
|
|
|
|
handle_jump(data, &mut update);
|
|
|
|
|
|
|
|
if self.prepare_duration != Duration::default() {
|
|
|
|
// Prepare (draw the bow)
|
|
|
|
update.character = CharacterState::ChargedRanged(Data {
|
|
|
|
exhausted: self.exhausted,
|
|
|
|
energy_drain: self.energy_drain,
|
|
|
|
initial_damage: self.initial_damage,
|
|
|
|
max_damage: self.max_damage,
|
|
|
|
initial_knockback: self.initial_knockback,
|
|
|
|
max_knockback: self.max_knockback,
|
|
|
|
prepare_duration: self
|
|
|
|
.prepare_duration
|
|
|
|
.checked_sub(Duration::from_secs_f32(data.dt.0))
|
|
|
|
.unwrap_or_default(),
|
|
|
|
charge_duration: self.charge_duration,
|
|
|
|
charge_timer: self.charge_timer,
|
|
|
|
recover_duration: self.recover_duration,
|
|
|
|
projectile_body: self.projectile_body,
|
|
|
|
projectile_light: self.projectile_light,
|
2020-08-06 18:17:38 +00:00
|
|
|
projectile_gravity: self.projectile_gravity,
|
|
|
|
initial_projectile_speed: self.initial_projectile_speed,
|
|
|
|
max_projectile_speed: self.max_projectile_speed,
|
2020-07-26 03:06:53 +00:00
|
|
|
});
|
|
|
|
} else if data.inputs.secondary.is_pressed()
|
|
|
|
&& self.charge_timer < self.charge_duration
|
|
|
|
&& update.energy.current() > 0
|
|
|
|
{
|
|
|
|
// Charge the bow
|
|
|
|
update.character = CharacterState::ChargedRanged(Data {
|
|
|
|
exhausted: self.exhausted,
|
|
|
|
energy_drain: self.energy_drain,
|
|
|
|
initial_damage: self.initial_damage,
|
|
|
|
max_damage: self.max_damage,
|
|
|
|
initial_knockback: self.initial_knockback,
|
|
|
|
max_knockback: self.max_knockback,
|
|
|
|
prepare_duration: self.prepare_duration,
|
|
|
|
charge_timer: self
|
|
|
|
.charge_timer
|
|
|
|
.checked_add(Duration::from_secs_f32(data.dt.0))
|
|
|
|
.unwrap_or_default(),
|
|
|
|
charge_duration: self.charge_duration,
|
|
|
|
recover_duration: self.recover_duration,
|
|
|
|
projectile_body: self.projectile_body,
|
|
|
|
projectile_light: self.projectile_light,
|
2020-08-06 18:17:38 +00:00
|
|
|
projectile_gravity: self.projectile_gravity,
|
|
|
|
initial_projectile_speed: self.initial_projectile_speed,
|
|
|
|
max_projectile_speed: self.max_projectile_speed,
|
2020-07-26 03:06:53 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Consumes energy if there's enough left and RMB is held down
|
|
|
|
update.energy.change_by(
|
|
|
|
-(self.energy_drain as f32 * data.dt.0) as i32,
|
|
|
|
EnergySource::Ability,
|
|
|
|
);
|
|
|
|
} else if data.inputs.secondary.is_pressed() {
|
|
|
|
// Charge the bow
|
|
|
|
update.character = CharacterState::ChargedRanged(Data {
|
|
|
|
exhausted: self.exhausted,
|
|
|
|
energy_drain: self.energy_drain,
|
|
|
|
initial_damage: self.initial_damage,
|
|
|
|
max_damage: self.max_damage,
|
|
|
|
initial_knockback: self.initial_knockback,
|
|
|
|
max_knockback: self.max_knockback,
|
|
|
|
prepare_duration: self.prepare_duration,
|
|
|
|
charge_timer: self.charge_timer,
|
|
|
|
charge_duration: self.charge_duration,
|
|
|
|
recover_duration: self.recover_duration,
|
|
|
|
projectile_body: self.projectile_body,
|
|
|
|
projectile_light: self.projectile_light,
|
2020-08-06 18:17:38 +00:00
|
|
|
projectile_gravity: self.projectile_gravity,
|
|
|
|
initial_projectile_speed: self.initial_projectile_speed,
|
|
|
|
max_projectile_speed: self.max_projectile_speed,
|
2020-07-26 03:06:53 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Consumes energy if there's enough left and RMB is held down
|
|
|
|
update.energy.change_by(
|
|
|
|
-(self.energy_drain as f32 * data.dt.0 / 5.0) as i32,
|
|
|
|
EnergySource::Ability,
|
|
|
|
);
|
|
|
|
} else if !self.exhausted {
|
|
|
|
let charge_amount =
|
|
|
|
(self.charge_timer.as_secs_f32() / self.charge_duration.as_secs_f32()).min(1.0);
|
|
|
|
// Fire
|
|
|
|
let mut projectile = Projectile {
|
|
|
|
hit_solid: vec![projectile::Effect::Stick],
|
|
|
|
hit_entity: vec![
|
|
|
|
projectile::Effect::Damage(
|
|
|
|
-(self.initial_damage as i32
|
|
|
|
+ (charge_amount * (self.max_damage - self.initial_damage) as f32)
|
|
|
|
as i32),
|
|
|
|
),
|
|
|
|
projectile::Effect::Knockback(
|
|
|
|
self.initial_knockback
|
|
|
|
+ charge_amount * (self.max_knockback - self.initial_knockback),
|
|
|
|
),
|
|
|
|
projectile::Effect::Vanish,
|
|
|
|
],
|
|
|
|
time_left: Duration::from_secs(15),
|
|
|
|
owner: None,
|
2020-09-19 08:47:40 +00:00
|
|
|
ignore_group: true,
|
2020-07-26 03:06:53 +00:00
|
|
|
};
|
|
|
|
projectile.owner = Some(*data.uid);
|
|
|
|
update.server_events.push_front(ServerEvent::Shoot {
|
|
|
|
entity: data.entity,
|
|
|
|
dir: data.inputs.look_dir,
|
|
|
|
body: self.projectile_body,
|
|
|
|
projectile,
|
|
|
|
light: self.projectile_light,
|
2020-08-06 18:17:38 +00:00
|
|
|
gravity: self.projectile_gravity,
|
2020-08-08 22:22:21 +00:00
|
|
|
speed: self.initial_projectile_speed
|
|
|
|
+ charge_amount * (self.max_projectile_speed - self.initial_projectile_speed),
|
2020-07-26 03:06:53 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
update.character = CharacterState::ChargedRanged(Data {
|
|
|
|
exhausted: true,
|
|
|
|
energy_drain: self.energy_drain,
|
|
|
|
initial_damage: self.initial_damage,
|
|
|
|
max_damage: self.max_damage,
|
|
|
|
initial_knockback: self.initial_knockback,
|
|
|
|
max_knockback: self.max_knockback,
|
|
|
|
prepare_duration: self.prepare_duration,
|
|
|
|
charge_timer: self.charge_timer,
|
|
|
|
charge_duration: self.charge_duration,
|
|
|
|
recover_duration: self.recover_duration,
|
|
|
|
projectile_body: self.projectile_body,
|
|
|
|
projectile_light: self.projectile_light,
|
2020-08-06 18:17:38 +00:00
|
|
|
projectile_gravity: self.projectile_gravity,
|
|
|
|
initial_projectile_speed: self.initial_projectile_speed,
|
|
|
|
max_projectile_speed: self.max_projectile_speed,
|
2020-07-26 03:06:53 +00:00
|
|
|
});
|
|
|
|
} else if self.recover_duration != Duration::default() {
|
|
|
|
// Recovery
|
|
|
|
update.character = CharacterState::ChargedRanged(Data {
|
|
|
|
exhausted: self.exhausted,
|
|
|
|
energy_drain: self.energy_drain,
|
|
|
|
initial_damage: self.initial_damage,
|
|
|
|
max_damage: self.max_damage,
|
|
|
|
initial_knockback: self.initial_knockback,
|
|
|
|
max_knockback: self.max_knockback,
|
|
|
|
prepare_duration: self.prepare_duration,
|
|
|
|
charge_timer: self.charge_timer,
|
|
|
|
charge_duration: self.charge_duration,
|
|
|
|
recover_duration: self
|
|
|
|
.recover_duration
|
|
|
|
.checked_sub(Duration::from_secs_f32(data.dt.0))
|
|
|
|
.unwrap_or_default(),
|
|
|
|
projectile_body: self.projectile_body,
|
|
|
|
projectile_light: self.projectile_light,
|
2020-08-06 18:17:38 +00:00
|
|
|
projectile_gravity: self.projectile_gravity,
|
|
|
|
initial_projectile_speed: self.initial_projectile_speed,
|
|
|
|
max_projectile_speed: self.max_projectile_speed,
|
2020-07-26 03:06:53 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// Done
|
|
|
|
update.character = CharacterState::Wielding;
|
|
|
|
}
|
|
|
|
|
|
|
|
update
|
|
|
|
}
|
|
|
|
}
|