Make abilities depend on weapon

This commit is contained in:
timokoesters 2020-03-14 16:40:29 +01:00
parent ac1c279a9b
commit fe19698d52
12 changed files with 228 additions and 223 deletions

View File

@ -1,10 +1,12 @@
use crate::comp::CharacterState;
use specs::{Component, DenseVecStorage, FlaggedStorage, HashMapStorage}; use specs::{Component, DenseVecStorage, FlaggedStorage, HashMapStorage};
use std::time::Duration;
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub enum AbilityState { pub enum CharacterAbility {
BasicAttack { BasicAttack {
/// Amount of energy required to use ability buildup_duration: Duration,
cost: i32, recover_duration: Duration,
}, },
BasicBlock, BasicBlock,
Roll, Roll,
@ -14,30 +16,43 @@ pub enum AbilityState {
cost: i32, cost: i32,
}, },
} }
impl Default for AbilityState {
fn default() -> Self { Self::BasicAttack { cost: -100 } }
}
impl Component for AbilityState { impl Component for CharacterAbility {
type Storage = DenseVecStorage<Self>; type Storage = DenseVecStorage<Self>;
} }
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)]
pub struct AbilityPool { pub struct AbilityPool {
pub primary: Option<AbilityState>, pub primary: Option<CharacterAbility>,
pub secondary: Option<AbilityState>, pub secondary: Option<CharacterAbility>,
pub block: Option<AbilityState>, pub block: Option<CharacterAbility>,
pub dodge: Option<AbilityState>, pub dodge: Option<CharacterAbility>,
} }
impl Default for AbilityPool { impl From<CharacterAbility> for CharacterState {
fn default() -> Self { fn from(ability: CharacterAbility) -> Self {
Self { match ability {
primary: Some(AbilityState::default()), CharacterAbility::BasicAttack {
// primary: Some(AbilityState::TimedCombo), buildup_duration,
secondary: Some(AbilityState::BasicBlock), recover_duration,
block: None, } => CharacterState::BasicAttack {
dodge: Some(AbilityState::Roll), exhausted: false,
buildup_duration,
recover_duration,
},
CharacterAbility::BasicBlock { .. } => CharacterState::BasicBlock {},
CharacterAbility::Roll { .. } => CharacterState::Roll {
remaining_duration: Duration::from_millis(600),
},
CharacterAbility::ChargeAttack { .. } => CharacterState::ChargeAttack {
remaining_duration: Duration::from_millis(600),
},
CharacterAbility::TimedCombo { .. } => CharacterState::TimedCombo {
stage: 1,
stage_time_active: Duration::default(),
stage_exhausted: false,
can_transition: false,
},
} }
} }
} }

View File

@ -19,8 +19,8 @@ pub struct StateUpdate {
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub enum CharacterState { pub enum CharacterState {
Idle {}, Idle,
Climb {}, Climb,
Sit {}, Sit {},
Equipping { Equipping {
/// The weapon being equipped /// The weapon being equipped
@ -32,16 +32,18 @@ pub enum CharacterState {
/// The weapon being wielded /// The weapon being wielded
tool: ToolData, tool: ToolData,
}, },
Glide {}, Glide,
/// A basic attacking state /// A basic attacking state
BasicAttack { BasicAttack {
/// How long the state has until exiting /// How long till the state deals damage
remaining_duration: Duration, buildup_duration: Duration,
/// How long till the state remains after dealing damage
recover_duration: Duration,
/// Whether the attack can deal more damage /// Whether the attack can deal more damage
exhausted: bool, exhausted: bool,
}, },
/// A basic blocking state /// A basic blocking state
BasicBlock {}, BasicBlock,
ChargeAttack { ChargeAttack {
/// How long the state has until exiting /// How long the state has until exiting
remaining_duration: Duration, remaining_duration: Duration,
@ -53,8 +55,6 @@ pub enum CharacterState {
/// A three-stage attack where play must click at appropriate times /// A three-stage attack where play must click at appropriate times
/// to continue attack chain. /// to continue attack chain.
TimedCombo { TimedCombo {
/// The tool this state will read to handle damage, etc.
tool: ToolData,
/// `int` denoting what stage (of 3) the attack is in. /// `int` denoting what stage (of 3) the attack is in.
stage: i8, stage: i8,
/// How long current stage has been active /// How long current stage has been active

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
assets::{self, Asset}, assets::{self, Asset},
comp::AbilityState, comp::CharacterAbility,
effect::Effect, effect::Effect,
terrain::{Block, BlockKind}, terrain::{Block, BlockKind},
}; };
@ -25,47 +25,55 @@ pub enum ToolKind {
Dagger, Dagger,
Staff, Staff,
Shield, Shield,
Debug(Debug), Debug(DebugKind),
}
impl Default for ToolKind {
fn default() -> Self { Self::Axe }
} }
impl ToolData { impl ToolData {
pub fn equip_time(&self) -> Duration { Duration::from_millis(self.equip_time_millis) } pub fn equip_time(&self) -> Duration { Duration::from_millis(self.equip_time_millis) }
pub fn attack_buildup_duration(&self) -> Duration { pub fn get_abilities(&self) -> Vec<CharacterAbility> {
Duration::from_millis(self.attack_buildup_millis) use CharacterAbility::*;
} use DebugKind::*;
pub fn attack_recover_duration(&self) -> Duration {
Duration::from_millis(self.attack_recover_millis)
}
pub fn attack_duration(&self) -> Duration {
self.attack_buildup_duration() + self.attack_recover_duration()
}
pub fn get_primary_abilities(&self) -> Vec<AbilityState> {
use AbilityState::*;
use SwordKind::*;
use ToolKind::*; use ToolKind::*;
let default_return = vec![AbilityState::default()];
match self.kind { match self.kind {
Sword(kind) => match kind { Sword(_) => vec![TimedCombo { cost: -150 }],
Rapier => vec![TimedCombo { cost: -150 }], Axe => vec![BasicAttack {
Scimitar => default_return, buildup_duration: Duration::from_millis(1000),
recover_duration: Duration::from_millis(500),
}],
Hammer => vec![BasicAttack {
buildup_duration: Duration::from_millis(1000),
recover_duration: Duration::from_millis(500),
}],
Bow => vec![BasicAttack {
buildup_duration: Duration::from_millis(1000),
recover_duration: Duration::from_millis(500),
}],
Dagger => vec![BasicAttack {
buildup_duration: Duration::from_millis(1000),
recover_duration: Duration::from_millis(500),
}],
Staff => vec![BasicAttack {
buildup_duration: Duration::from_millis(1000),
recover_duration: Duration::from_millis(500),
}],
Shield => vec![BasicAttack {
buildup_duration: Duration::from_millis(1000),
recover_duration: Duration::from_millis(500),
}],
Debug(kind) => match kind {
Boost => vec![],
Possess => vec![],
}, },
_ => default_return,
_ => vec![],
} }
} }
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Debug { pub enum DebugKind {
Boost, Boost,
Possess, Possess,
} }
@ -109,7 +117,7 @@ pub enum Ingredient {
Grass, Grass,
} }
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ToolData { pub struct ToolData {
pub kind: ToolKind, pub kind: ToolKind,
equip_time_millis: u64, equip_time_millis: u64,

View File

@ -1,7 +1,7 @@
pub mod item; pub mod item;
// Reexports // Reexports
pub use item::{Consumable, Debug, Item, ItemKind, SwordKind, ToolData, ToolKind}; pub use item::{Consumable, DebugKind, Item, ItemKind, SwordKind, ToolData, ToolKind};
use crate::assets; use crate::assets;
use specs::{Component, FlaggedStorage, HashMapStorage}; use specs::{Component, FlaggedStorage, HashMapStorage};

View File

@ -16,7 +16,7 @@ mod stats;
mod visual; mod visual;
// Reexports // Reexports
pub use ability::{AbilityPool, AbilityState}; pub use ability::{AbilityPool, CharacterAbility};
pub use admin::Admin; pub use admin::Admin;
pub use agent::{Agent, Alignment}; pub use agent::{Agent, Alignment};
pub use body::{ pub use body::{

View File

@ -21,7 +21,7 @@ sum_type! {
Mass(comp::Mass), Mass(comp::Mass),
Gravity(comp::Gravity), Gravity(comp::Gravity),
Sticky(comp::Sticky), Sticky(comp::Sticky),
AbilityState(comp::AbilityState), CharacterAbility(comp::CharacterAbility),
AbilityPool(comp::AbilityPool), AbilityPool(comp::AbilityPool),
Attacking(comp::Attacking), Attacking(comp::Attacking),
CharacterState(comp::CharacterState), CharacterState(comp::CharacterState),
@ -45,7 +45,7 @@ sum_type! {
Mass(PhantomData<comp::Mass>), Mass(PhantomData<comp::Mass>),
Gravity(PhantomData<comp::Gravity>), Gravity(PhantomData<comp::Gravity>),
Sticky(PhantomData<comp::Sticky>), Sticky(PhantomData<comp::Sticky>),
AbilityState(PhantomData<comp::AbilityState>), CharacterAbility(PhantomData<comp::CharacterAbility>),
AbilityPool(PhantomData<comp::AbilityPool>), AbilityPool(PhantomData<comp::AbilityPool>),
Attacking(PhantomData<comp::Attacking>), Attacking(PhantomData<comp::Attacking>),
CharacterState(PhantomData<comp::CharacterState>), CharacterState(PhantomData<comp::CharacterState>),
@ -69,7 +69,7 @@ impl sync::CompPacket for EcsCompPacket {
EcsCompPacket::Mass(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Mass(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Gravity(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Gravity(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::AbilityState(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::CharacterAbility(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::AbilityPool(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::AbilityPool(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Attacking(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Attacking(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::CharacterState(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::CharacterState(comp) => sync::handle_insert(comp, entity, world),
@ -91,7 +91,7 @@ impl sync::CompPacket for EcsCompPacket {
EcsCompPacket::Mass(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Mass(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Gravity(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Gravity(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::AbilityState(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::CharacterAbility(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::AbilityPool(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::AbilityPool(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Attacking(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Attacking(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::CharacterState(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::CharacterState(comp) => sync::handle_modify(comp, entity, world),
@ -115,8 +115,8 @@ impl sync::CompPacket for EcsCompPacket {
EcsCompPhantom::Mass(_) => sync::handle_remove::<comp::Mass>(entity, world), EcsCompPhantom::Mass(_) => sync::handle_remove::<comp::Mass>(entity, world),
EcsCompPhantom::Gravity(_) => sync::handle_remove::<comp::Gravity>(entity, world), EcsCompPhantom::Gravity(_) => sync::handle_remove::<comp::Gravity>(entity, world),
EcsCompPhantom::Sticky(_) => sync::handle_remove::<comp::Sticky>(entity, world), EcsCompPhantom::Sticky(_) => sync::handle_remove::<comp::Sticky>(entity, world),
EcsCompPhantom::AbilityState(_) => { EcsCompPhantom::CharacterAbility(_) => {
sync::handle_remove::<comp::AbilityState>(entity, world) sync::handle_remove::<comp::CharacterAbility>(entity, world)
}, },
EcsCompPhantom::AbilityPool(_) => { EcsCompPhantom::AbilityPool(_) => {
sync::handle_remove::<comp::AbilityPool>(entity, world) sync::handle_remove::<comp::AbilityPool>(entity, world)

View File

@ -107,7 +107,7 @@ impl State {
ecs.register_sync_marker(); ecs.register_sync_marker();
// Register server -> all clients synced components. // Register server -> all clients synced components.
ecs.register::<comp::AbilityPool>(); ecs.register::<comp::AbilityPool>();
ecs.register::<comp::AbilityState>(); ecs.register::<comp::CharacterAbility>();
ecs.register::<comp::Projectile>(); ecs.register::<comp::Projectile>();
ecs.register::<comp::Body>(); ecs.register::<comp::Body>();
ecs.register::<comp::Player>(); ecs.register::<comp::Player>();

View File

@ -18,51 +18,65 @@ pub fn behavior(data: &JoinData) -> StateUpdate {
if let CharacterState::BasicAttack { if let CharacterState::BasicAttack {
exhausted, exhausted,
remaining_duration, buildup_duration,
recover_duration,
} = data.character } = data.character
{ {
let tool_kind = data.stats.equipment.main.as_ref().map(|i| i.kind); let tool_kind = data.stats.equipment.main.as_ref().map(|i| i.kind);
if let Some(Tool(tool)) = tool_kind { handle_move(data, &mut update);
handle_move(data, &mut update);
let mut new_exhausted = *exhausted; if buildup_duration != &Duration::default() {
// Start to swing
if !*exhausted && *remaining_duration < tool.attack_recover_duration() { update.character = CharacterState::BasicAttack {
buildup_duration: buildup_duration
.checked_sub(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
recover_duration: *recover_duration,
exhausted: false,
};
} else if !*exhausted {
// Swing hits
if let Some(Tool(tool)) = tool_kind {
data.updater.insert(data.entity, Attacking { data.updater.insert(data.entity, Attacking {
weapon: Some(tool), weapon: Some(tool),
applied: false, applied: false,
hit_count: 0, hit_count: 0,
}); });
new_exhausted = true;
} }
let new_remaining_duration = remaining_duration
.checked_sub(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default();
if let Some(attack) = data.attacking {
if attack.applied && attack.hit_count > 0 {
data.updater.remove::<Attacking>(data.entity);
update.energy.change_by(100, EnergySource::HitEnemy);
}
}
// Tick down
update.character = CharacterState::BasicAttack { update.character = CharacterState::BasicAttack {
remaining_duration: new_remaining_duration, buildup_duration: *buildup_duration,
exhausted: new_exhausted, recover_duration: *recover_duration,
exhausted: true,
}; };
} else if recover_duration != &Duration::default() {
// Check if attack duration has expired // Recover from swing
if new_remaining_duration == Duration::default() { update.character = CharacterState::BasicAttack {
buildup_duration: *buildup_duration,
recover_duration: recover_duration
.checked_sub(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
exhausted: true,
}
} else {
// Done
if let Some(Tool(tool)) = tool_kind {
update.character = CharacterState::Wielding { tool }; update.character = CharacterState::Wielding { tool };
data.updater.remove::<Attacking>(data.entity); data.updater.remove::<Attacking>(data.entity);
} else {
update.character = CharacterState::Idle;
} }
update
} else {
update
} }
// More handling
if let Some(attack) = data.attacking {
if attack.applied && attack.hit_count > 0 {
data.updater.remove::<Attacking>(data.entity);
update.energy.change_by(100, EnergySource::HitEnemy);
}
}
update
} else { } else {
update.character = CharacterState::Idle {}; update.character = CharacterState::Idle {};
update update

View File

@ -17,77 +17,81 @@ pub fn behavior(data: &JoinData) -> StateUpdate {
}; };
if let CharacterState::TimedCombo { if let CharacterState::TimedCombo {
tool,
stage, stage,
stage_time_active, stage_time_active,
stage_exhausted, stage_exhausted,
can_transition, can_transition,
} = data.character } = data.character
{ {
let mut new_can_transition = *can_transition; // Sorry adam, I don't want to fix this rn, check out basic_attack to
let mut new_stage_exhausted = *stage_exhausted; // see how to do it
let new_stage_time_active = stage_time_active //
.checked_add(Duration::from_secs_f32(data.dt.0)) /*
.unwrap_or(Duration::default()); let mut new_can_transition = *can_transition;
let mut new_stage_exhausted = *stage_exhausted;
let new_stage_time_active = stage_time_active
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or(Duration::default());
match stage { match stage {
1 => { 1 => {
if new_stage_time_active > tool.attack_buildup_duration() { if new_stage_time_active > tool.attack_buildup_duration() {
if !*stage_exhausted { if !*stage_exhausted {
// Try to deal damage // Try to deal damage
data.updater.insert(data.entity, Attacking { data.updater.insert(data.entity, Attacking {
weapon: Some(*tool), weapon: Some(*tool),
applied: false, applied: false,
hit_count: 0, hit_count: 0,
}); });
new_stage_exhausted = true; new_stage_exhausted = true;
} else { } else {
// Make sure to remove Attacking component // Make sure to remove Attacking component
data.updater.remove::<Attacking>(data.entity); data.updater.remove::<Attacking>(data.entity);
}
// Check if player has timed click right
if data.inputs.primary.is_just_pressed() {
println!("Can transition");
new_can_transition = true;
}
} }
// Check if player has timed click right if new_stage_time_active > tool.attack_duration() {
if data.inputs.primary.is_just_pressed() { if new_can_transition {
println!("Can transition"); update.character = CharacterState::TimedCombo {
new_can_transition = true; tool: *tool,
} stage: 2,
} stage_time_active: Duration::default(),
stage_exhausted: false,
if new_stage_time_active > tool.attack_duration() { can_transition: false,
if new_can_transition { }
update.character = CharacterState::TimedCombo { } else {
tool: *tool, println!("Failed");
stage: 2, attempt_wield(data, &mut update);
stage_time_active: Duration::default(),
stage_exhausted: false,
can_transition: false,
} }
} else { } else {
println!("Failed"); update.character = CharacterState::TimedCombo {
attempt_wield(data, &mut update); tool: *tool,
stage: 1,
stage_time_active: new_stage_time_active,
stage_exhausted: new_stage_exhausted,
can_transition: new_can_transition,
}
} }
} else { },
update.character = CharacterState::TimedCombo { 2 => {
tool: *tool, println!("2");
stage: 1, attempt_wield(data, &mut update);
stage_time_active: new_stage_time_active, },
stage_exhausted: new_stage_exhausted, 3 => {
can_transition: new_can_transition, println!("3");
} attempt_wield(data, &mut update);
} },
}, _ => {
2 => { // Should never get here.
println!("2"); },
attempt_wield(data, &mut update); }
}, */
3 => {
println!("3");
attempt_wield(data, &mut update);
},
_ => {
// Should never get here.
},
}
} }
update update

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
comp::{AbilityState, CharacterState, EnergySource, ItemKind::Tool, StateUpdate, ToolData}, comp::{CharacterAbility, CharacterState, EnergySource, ItemKind::Tool, StateUpdate, ToolData},
event::LocalEvent, event::LocalEvent,
sys::{character_behavior::JoinData, phys::GRAVITY}, sys::{character_behavior::JoinData, phys::GRAVITY},
}; };
@ -191,8 +191,8 @@ pub fn handle_primary_input(data: &JoinData, update: &mut StateUpdate) {
/// Attempts to go into `ability_pool.primary` if is `Some()` on `AbilityPool` /// Attempts to go into `ability_pool.primary` if is `Some()` on `AbilityPool`
pub fn attempt_primary_ability(data: &JoinData, update: &mut StateUpdate) { pub fn attempt_primary_ability(data: &JoinData, update: &mut StateUpdate) {
if let Some(ability_state) = data.ability_pool.primary { if let Some(ability) = data.ability_pool.primary {
update.character = ability_to_character_state(data, update, ability_state); update.character = ability.into();
} }
} }
@ -201,15 +201,15 @@ pub fn attempt_primary_ability(data: &JoinData, update: &mut StateUpdate) {
pub fn handle_secondary_input(data: &JoinData, update: &mut StateUpdate) { pub fn handle_secondary_input(data: &JoinData, update: &mut StateUpdate) {
if data.inputs.secondary.is_pressed() { if data.inputs.secondary.is_pressed() {
if let CharacterState::Wielding { .. } = update.character { if let CharacterState::Wielding { .. } = update.character {
attempt_seconday_ability(data, update); attempt_secondary_ability(data, update);
} }
} }
} }
/// Attempts to go into `ability_pool.secondary` if is `Some()` on `AbilityPool` /// Attempts to go into `ability_pool.secondary` if is `Some()` on `AbilityPool`
pub fn attempt_seconday_ability(data: &JoinData, update: &mut StateUpdate) { pub fn attempt_secondary_ability(data: &JoinData, update: &mut StateUpdate) {
if let Some(ability_state) = data.ability_pool.secondary { if let Some(ability) = data.ability_pool.secondary {
update.character = ability_to_character_state(data, update, ability_state); update.character = ability.into();
} }
} }
@ -232,63 +232,7 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
} }
pub fn attempt_dodge_ability(data: &JoinData, update: &mut StateUpdate) { pub fn attempt_dodge_ability(data: &JoinData, update: &mut StateUpdate) {
if let Some(ability_state) = data.ability_pool.dodge { if let Some(ability) = data.ability_pool.dodge {
update.character = ability_to_character_state(data, update, ability_state); update.character = ability.into();
}
}
// TODO: Might need a fn `CharacterState::new(data, update)` if
// initialization gets too lengthy.
/// Maps from `AbilityState`s to `CharacterStates`s. Also handles intializing
/// the new `CharacterState`
pub fn ability_to_character_state(
data: &JoinData,
update: &mut StateUpdate,
ability_state: AbilityState,
) -> CharacterState {
match ability_state {
AbilityState::BasicAttack { cost, .. } => {
if let Some(tool) = unwrap_tool_data(data) {
if update.energy.try_change_by(cost, EnergySource::HitEnemy).is_ok() {
return CharacterState::BasicAttack {
exhausted: false,
remaining_duration: tool.attack_duration(),
};
}
}
*data.character
},
AbilityState::BasicBlock { .. } => CharacterState::BasicBlock {},
AbilityState::Roll { .. } => CharacterState::Roll {
remaining_duration: Duration::from_millis(600),
},
AbilityState::ChargeAttack { .. } => CharacterState::ChargeAttack {
remaining_duration: Duration::from_millis(600),
},
AbilityState::TimedCombo { .. } => {
if let Some(tool) = unwrap_tool_data(data) {
CharacterState::TimedCombo {
tool,
stage: 1,
stage_time_active: Duration::default(),
stage_exhausted: false,
can_transition: false,
}
} else {
*data.character
}
},
// Do not use default match
// _ => *data.character
}
}
pub fn unwrap_tool_data(data: &JoinData) -> Option<ToolData> {
if let Some(Tool(tool)) = data.stats.equipment.main.as_ref().map(|i| i.kind) {
Some(tool)
} else {
None
} }
} }

View File

@ -272,7 +272,7 @@ impl Server {
let spawn_point = state.ecs().read_resource::<SpawnPoint>().0; let spawn_point = state.ecs().read_resource::<SpawnPoint>().0;
state.write_component(entity, body); state.write_component(entity, body);
state.write_component(entity, comp::Stats::new(name, body, main)); state.write_component(entity, comp::Stats::new(name, body, main.clone()));
state.write_component(entity, comp::Energy::new(1000)); state.write_component(entity, comp::Energy::new(1000));
state.write_component(entity, comp::Controller::default()); state.write_component(entity, comp::Controller::default());
state.write_component(entity, comp::Pos(spawn_point)); state.write_component(entity, comp::Pos(spawn_point));
@ -286,7 +286,27 @@ impl Server {
entity, entity,
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::default()), comp::InventoryUpdate::new(comp::InventoryUpdateEvent::default()),
); );
state.write_component(entity, comp::AbilityPool::default());
state.write_component(
entity,
if let Some(comp::Item {
kind: comp::ItemKind::Tool(tool),
..
}) = main
{
let mut abilities = tool.get_abilities();
let mut ability_drain = abilities.drain(..);
comp::AbilityPool {
primary: ability_drain.next(),
secondary: ability_drain.next(),
block: Some(comp::CharacterAbility::BasicBlock),
dodge: Some(comp::CharacterAbility::Roll),
}
} else {
comp::AbilityPool::default()
},
);
// Make sure physics are accepted. // Make sure physics are accepted.
state.write_component(entity, comp::ForceUpdate); state.write_component(entity, comp::ForceUpdate);

View File

@ -10,7 +10,7 @@ use crate::{
use common::{ use common::{
assets::load_expect, assets::load_expect,
comp::{ comp::{
item::{Debug, ToolData, ToolKind}, item::{DebugKind, ToolData, ToolKind},
CharacterState, ControllerInputs, Energy, ItemKind, Stats, CharacterState, ControllerInputs, Energy, ItemKind, Stats,
}, },
}; };
@ -597,7 +597,7 @@ impl<'a> Widget for Skillbar<'a> {
ToolKind::Axe => self.imgs.twohaxe_m1, ToolKind::Axe => self.imgs.twohaxe_m1,
ToolKind::Bow => self.imgs.bow_m1, ToolKind::Bow => self.imgs.bow_m1,
ToolKind::Staff => self.imgs.staff_m1, ToolKind::Staff => self.imgs.staff_m1,
ToolKind::Debug(Debug::Boost) => self.imgs.flyingrod_m1, ToolKind::Debug(DebugKind::Boost) => self.imgs.flyingrod_m1,
_ => self.imgs.twohaxe_m1, _ => self.imgs.twohaxe_m1,
}, },
_ => self.imgs.twohaxe_m1, _ => self.imgs.twohaxe_m1,
@ -690,7 +690,7 @@ impl<'a> Widget for Skillbar<'a> {
ToolKind::Axe => self.imgs.twohaxe_m2, ToolKind::Axe => self.imgs.twohaxe_m2,
ToolKind::Bow => self.imgs.bow_m2, ToolKind::Bow => self.imgs.bow_m2,
ToolKind::Staff => self.imgs.staff_m2, ToolKind::Staff => self.imgs.staff_m2,
ToolKind::Debug(Debug::Boost) => self.imgs.flyingrod_m2, ToolKind::Debug(DebugKind::Boost) => self.imgs.flyingrod_m2,
_ => self.imgs.twohaxe_m2, _ => self.imgs.twohaxe_m2,
}, },
_ => self.imgs.twohaxe_m2, _ => self.imgs.twohaxe_m2,