Addressed second round of feedback.

This commit is contained in:
Sam 2020-09-12 20:33:46 -05:00
parent b06ab250cc
commit fe70b7fbce
12 changed files with 169 additions and 145 deletions

View File

@ -94,19 +94,19 @@
], ],
threshold: 0.5, threshold: 0.5,
), ),
Attack(ComboMelee(1), Sword): ( Attack(ComboMelee(Swing, 1), Sword): (
files: [ files: [
"voxygen.audio.sfx.abilities.swing_sword", "voxygen.audio.sfx.abilities.swing_sword",
], ],
threshold: 0.7, threshold: 0.7,
), ),
Attack(ComboMelee(2), Sword): ( Attack(ComboMelee(Swing, 2), Sword): (
files: [ files: [
"voxygen.audio.sfx.abilities.separated_second_swing", "voxygen.audio.sfx.abilities.separated_second_swing",
], ],
threshold: 0.7, threshold: 0.7,
), ),
Attack(ComboMelee(3), Sword): ( Attack(ComboMelee(Swing, 3), Sword): (
files: [ files: [
"voxygen.audio.sfx.abilities.separated_third_swing", "voxygen.audio.sfx.abilities.separated_third_swing",
], ],

View File

@ -20,7 +20,7 @@ pub enum CharacterAbilityType {
ChargedRanged, ChargedRanged,
DashMelee, DashMelee,
BasicBlock, BasicBlock,
ComboMelee(u32), ComboMelee(StageSection, u32),
LeapMelee, LeapMelee,
SpinMelee, SpinMelee,
GroundShockwave, GroundShockwave,
@ -35,7 +35,7 @@ impl From<&CharacterState> for CharacterAbilityType {
CharacterState::DashMelee(_) => Self::DashMelee, CharacterState::DashMelee(_) => Self::DashMelee,
CharacterState::BasicBlock => Self::BasicBlock, CharacterState::BasicBlock => Self::BasicBlock,
CharacterState::LeapMelee(_) => Self::LeapMelee, CharacterState::LeapMelee(_) => Self::LeapMelee,
CharacterState::ComboMelee(data) => Self::ComboMelee(data.stage), CharacterState::ComboMelee(data) => Self::ComboMelee(data.stage_section, data.stage),
CharacterState::SpinMelee(_) => Self::SpinMelee, CharacterState::SpinMelee(_) => Self::SpinMelee,
CharacterState::ChargedRanged(_) => Self::ChargedRanged, CharacterState::ChargedRanged(_) => Self::ChargedRanged,
CharacterState::GroundShockwave(_) => Self::ChargedRanged, CharacterState::GroundShockwave(_) => Self::ChargedRanged,

View File

@ -124,7 +124,7 @@ impl Tool {
base_damage: (100.0 * self.base_power()) as u32, base_damage: (100.0 * self.base_power()) as u32,
max_damage: (120.0 * self.base_power()) as u32, max_damage: (120.0 * self.base_power()) as u32,
damage_increase: (10.0 * self.base_power()) as u32, damage_increase: (10.0 * self.base_power()) as u32,
knockback: 8.0, knockback: 10.0,
range: 4.0, range: 4.0,
angle: 30.0, angle: 30.0,
base_buildup_duration: Duration::from_millis(500), base_buildup_duration: Duration::from_millis(500),
@ -137,7 +137,7 @@ impl Tool {
base_damage: (80.0 * self.base_power()) as u32, base_damage: (80.0 * self.base_power()) as u32,
max_damage: (110.0 * self.base_power()) as u32, max_damage: (110.0 * self.base_power()) as u32,
damage_increase: (15.0 * self.base_power()) as u32, damage_increase: (15.0 * self.base_power()) as u32,
knockback: 5.0, knockback: 12.0,
range: 3.5, range: 3.5,
angle: 180.0, angle: 180.0,
base_buildup_duration: Duration::from_millis(400), base_buildup_duration: Duration::from_millis(400),
@ -170,8 +170,8 @@ impl Tool {
energy_cost: 200, energy_cost: 200,
base_damage: (120.0 * self.base_power()) as u32, base_damage: (120.0 * self.base_power()) as u32,
max_damage: (260.0 * self.base_power()) as u32, max_damage: (260.0 * self.base_power()) as u32,
base_knockback: 5.0, base_knockback: 10.0,
max_knockback: 10.0, max_knockback: 20.0,
range: 5.0, range: 5.0,
angle: 45.0, angle: 45.0,
energy_drain: 500, energy_drain: 500,
@ -190,6 +190,7 @@ impl Tool {
buildup_duration: Duration::from_millis(700), buildup_duration: Duration::from_millis(700),
recover_duration: Duration::from_millis(300), recover_duration: Duration::from_millis(300),
base_healthchange: (-120.0 * self.base_power()) as i32, base_healthchange: (-120.0 * self.base_power()) as i32,
knockback: 0.0,
range: 3.5, range: 3.5,
max_angle: 20.0, max_angle: 20.0,
}, },
@ -272,6 +273,7 @@ impl Tool {
buildup_duration: Duration::from_millis(100), buildup_duration: Duration::from_millis(100),
recover_duration: Duration::from_millis(400), recover_duration: Duration::from_millis(400),
base_healthchange: (-50.0 * self.base_power()) as i32, base_healthchange: (-50.0 * self.base_power()) as i32,
knockback: 0.0,
range: 3.5, range: 3.5,
max_angle: 20.0, max_angle: 20.0,
}], }],

View File

@ -66,7 +66,7 @@ impl LoadoutBuilder {
} }
/// Builds loadout of creature when spawned /// Builds loadout of creature when spawned
pub fn build_loadout(body: Body, alignment: Alignment, mut main_tool: Option<Item>) -> Self { pub fn build_loadout(body: Body, alignment: Alignment, mut main_tool: Option<Item>, is_giant: bool) -> Self {
#![allow(clippy::single_match)] // For when this is done to more than just golems. #![allow(clippy::single_match)] // For when this is done to more than just golems.
match body { match body {
Body::Golem(golem) => match golem.species { Body::Golem(golem) => match golem.species {
@ -76,6 +76,11 @@ impl LoadoutBuilder {
)); ));
}, },
}, },
Body::Humanoid(_) => if is_giant {
main_tool = Some(Item::new_from_asset_expect(
"common.items.npc_weapons.sword.zweihander_sword_0",
));
},
_ => {}, _ => {},
}; };
@ -112,37 +117,27 @@ impl LoadoutBuilder {
}; };
let loadout = match body { let loadout = match body {
Body::Humanoid(_) => match alignment { Body::Humanoid(_) => match is_giant {
Alignment::Npc => Loadout { true => Loadout {
active_item, active_item,
second_item: None, second_item: None,
shoulder: None, shoulder: Some(Item::new_from_asset_expect(
"common.items.armor.shoulder.plate_0",
)),
chest: Some(Item::new_from_asset_expect( chest: Some(Item::new_from_asset_expect(
match rand::thread_rng().gen_range(0, 10) { "common.items.armor.chest.plate_green_0",
0 => "common.items.armor.chest.worker_green_0",
1 => "common.items.armor.chest.worker_green_1",
2 => "common.items.armor.chest.worker_red_0",
3 => "common.items.armor.chest.worker_red_1",
4 => "common.items.armor.chest.worker_purple_0",
5 => "common.items.armor.chest.worker_purple_1",
6 => "common.items.armor.chest.worker_yellow_0",
7 => "common.items.armor.chest.worker_yellow_1",
8 => "common.items.armor.chest.worker_orange_0",
_ => "common.items.armor.chest.worker_orange_1",
},
)), )),
belt: Some(Item::new_from_asset_expect( belt: Some(Item::new_from_asset_expect(
"common.items.armor.belt.leather_0", "common.items.armor.belt.plate_0",
)),
hand: Some(Item::new_from_asset_expect(
"common.items.armor.hand.plate_0",
)), )),
hand: None,
pants: Some(Item::new_from_asset_expect( pants: Some(Item::new_from_asset_expect(
"common.items.armor.pants.worker_blue_0", "common.items.armor.pants.plate_green_0",
)), )),
foot: Some(Item::new_from_asset_expect( foot: Some(Item::new_from_asset_expect(
match rand::thread_rng().gen_range(0, 2) { "common.items.armor.foot.plate_0",
0 => "common.items.armor.foot.leather_0",
_ => "common.items.armor.starter.sandals_0",
},
)), )),
back: None, back: None,
ring: None, ring: None,
@ -152,38 +147,79 @@ impl LoadoutBuilder {
head: None, head: None,
tabard: None, tabard: None,
}, },
Alignment::Enemy => Loadout { false => match alignment {
active_item, Alignment::Npc => Loadout {
second_item: None, active_item,
shoulder: Some(Item::new_from_asset_expect( second_item: None,
"common.items.armor.shoulder.cultist_shoulder_purple", shoulder: None,
)), chest: Some(Item::new_from_asset_expect(
chest: Some(Item::new_from_asset_expect( match rand::thread_rng().gen_range(0, 10) {
"common.items.armor.chest.cultist_chest_purple", 0 => "common.items.armor.chest.worker_green_0",
)), 1 => "common.items.armor.chest.worker_green_1",
belt: Some(Item::new_from_asset_expect( 2 => "common.items.armor.chest.worker_red_0",
"common.items.armor.belt.cultist_belt", 3 => "common.items.armor.chest.worker_red_1",
)), 4 => "common.items.armor.chest.worker_purple_0",
hand: Some(Item::new_from_asset_expect( 5 => "common.items.armor.chest.worker_purple_1",
"common.items.armor.hand.cultist_hands_purple", 6 => "common.items.armor.chest.worker_yellow_0",
)), 7 => "common.items.armor.chest.worker_yellow_1",
pants: Some(Item::new_from_asset_expect( 8 => "common.items.armor.chest.worker_orange_0",
"common.items.armor.pants.cultist_legs_purple", _ => "common.items.armor.chest.worker_orange_1",
)), },
foot: Some(Item::new_from_asset_expect( )),
"common.items.armor.foot.cultist_boots", belt: Some(Item::new_from_asset_expect(
)), "common.items.armor.belt.leather_0",
back: Some(Item::new_from_asset_expect( )),
"common.items.armor.back.dungeon_purple-0", hand: None,
)), pants: Some(Item::new_from_asset_expect(
ring: None, "common.items.armor.pants.worker_blue_0",
neck: None, )),
lantern: Some(Item::new_from_asset_expect("common.items.lantern.black_0")), foot: Some(Item::new_from_asset_expect(
glider: None, match rand::thread_rng().gen_range(0, 2) {
head: None, 0 => "common.items.armor.foot.leather_0",
tabard: None, _ => "common.items.armor.starter.sandals_0",
}, },
_ => LoadoutBuilder::animal(body).build(), )),
back: None,
ring: None,
neck: None,
lantern: None,
glider: None,
head: None,
tabard: None,
},
Alignment::Enemy => Loadout {
active_item,
second_item: None,
shoulder: Some(Item::new_from_asset_expect(
"common.items.armor.shoulder.cultist_shoulder_purple",
)),
chest: Some(Item::new_from_asset_expect(
"common.items.armor.chest.cultist_chest_purple",
)),
belt: Some(Item::new_from_asset_expect(
"common.items.armor.belt.cultist_belt",
)),
hand: Some(Item::new_from_asset_expect(
"common.items.armor.hand.cultist_hands_purple",
)),
pants: Some(Item::new_from_asset_expect(
"common.items.armor.pants.cultist_legs_purple",
)),
foot: Some(Item::new_from_asset_expect(
"common.items.armor.foot.cultist_boots",
)),
back: Some(Item::new_from_asset_expect(
"common.items.armor.back.dungeon_purple-0",
)),
ring: None,
neck: None,
lantern: Some(Item::new_from_asset_expect("common.items.lantern.black_0")),
glider: None,
head: None,
tabard: None,
},
_ => LoadoutBuilder::animal(body).build(),
}
}, },
Body::Golem(golem) => match golem.species { Body::Golem(golem) => match golem.species {
golem::Species::StoneGolem => Loadout { golem::Species::StoneGolem => Loadout {

View File

@ -77,13 +77,13 @@ impl CharacterBehavior for Data {
// Allows for other states to interrupt this state // Allows for other states to interrupt this state
if self.is_interruptible && !data.inputs.primary.is_pressed() { if self.is_interruptible && !data.inputs.primary.is_pressed() {
if data.inputs.roll.is_pressed() { handle_dodge_input(data, &mut update);
handle_dodge_input(data, &mut update); handle_ability2_input(data, &mut update);
return update; match update.character {
} CharacterState::ComboMelee(_) => {},
if data.inputs.secondary.is_pressed() { _ => {
handle_ability2_input(data, &mut update); return update;
return update; },
} }
} }

View File

@ -64,13 +64,13 @@ impl CharacterBehavior for Data {
// Allows for other states to interrupt this state // Allows for other states to interrupt this state
if self.static_data.is_interruptible && !data.inputs.secondary.is_pressed() { if self.static_data.is_interruptible && !data.inputs.secondary.is_pressed() {
if data.inputs.roll.is_pressed() { handle_dodge_input(data, &mut update);
handle_dodge_input(data, &mut update); handle_ability1_input(data, &mut update);
return update; match update.character {
} CharacterState::DashMelee(_) => {},
if data.inputs.primary.is_pressed() { _ => {
handle_ability1_input(data, &mut update); return update;
return update; },
} }
} }

View File

@ -350,7 +350,7 @@ pub fn unwrap_tool_data<'a>(data: &'a JoinData) -> Option<&'a Tool> {
/// Determines what portion a state is in. Used in all attacks (eventually). Is /// Determines what portion a state is in. Used in all attacks (eventually). Is
/// used to control aspects of animation code, as well as logic within the /// used to control aspects of animation code, as well as logic within the
/// character states. /// character states.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum StageSection { pub enum StageSection {
Buildup, Buildup,
Swing, Swing,

View File

@ -153,10 +153,11 @@ impl<'a> System<'a> for Sys {
}); });
} }
if attack.knockback != 0.0 && damage.healthchange != 0.0 { if attack.knockback != 0.0 && damage.healthchange != 0.0 {
let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0));
server_emitter.emit(ServerEvent::Knockback { server_emitter.emit(ServerEvent::Knockback {
entity: b, entity: b,
impulse: attack.knockback impulse: attack.knockback
* *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5), * *Dir::slerp(kb_dir, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5),
}); });
} }
attack.hit_count += 1; attack.hit_count += 1;

View File

@ -621,7 +621,7 @@ fn handle_spawn(
.create_npc( .create_npc(
pos, pos,
comp::Stats::new(get_npc_name(id).into(), body), comp::Stats::new(get_npc_name(id).into(), body),
LoadoutBuilder::build_loadout(body, alignment, None).build(), LoadoutBuilder::build_loadout(body, alignment, None, false).build(),
body, body,
) )
.with(comp::Vel(vel)) .with(comp::Vel(vel))

View File

@ -1,7 +1,7 @@
use super::SysTimer; use super::SysTimer;
use crate::{chunk_generator::ChunkGenerator, client::Client, Tick}; use crate::{chunk_generator::ChunkGenerator, client::Client, Tick};
use common::{ use common::{
comp::{self, bird_medium, Alignment, CharacterAbility, Player, Pos}, comp::{self, bird_medium, Alignment, Player, Pos},
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
generation::get_npc_name, generation::get_npc_name,
msg::ServerMsg, msg::ServerMsg,
@ -13,7 +13,7 @@ use common::{
}; };
use rand::Rng; use rand::Rng;
use specs::{Join, Read, ReadStorage, System, Write, WriteExpect, WriteStorage}; use specs::{Join, Read, ReadStorage, System, Write, WriteExpect, WriteStorage};
use std::{sync::Arc, time::Duration}; use std::sync::Arc;
use vek::*; use vek::*;
/// This system will handle loading generated chunks and unloading /// This system will handle loading generated chunks and unloading
@ -115,18 +115,12 @@ impl<'a> System<'a> for Sys {
let mut body = entity.body; let mut body = entity.body;
let name = entity.name.unwrap_or_else(|| "Unnamed".to_string()); let name = entity.name.unwrap_or_else(|| "Unnamed".to_string());
let alignment = entity.alignment; let alignment = entity.alignment;
let mut main_tool = entity.main_tool; let main_tool = entity.main_tool;
let mut stats = comp::Stats::new(name, body); let mut stats = comp::Stats::new(name, body);
// let damage = stats.level.level() as i32; TODO: Make NPC base damage // let damage = stats.level.level() as i32; TODO: Make NPC base damage
// non-linearly depend on their level // non-linearly depend on their level
if entity.is_giant { let loadout = LoadoutBuilder::build_loadout(body, alignment, main_tool, entity.is_giant).build();
main_tool = Some(comp::Item::new_from_asset_expect(
"common.items.npc_weapons.sword.zweihander_sword_0",
));
}
let mut loadout = LoadoutBuilder::build_loadout(body, alignment, main_tool).build();
let mut scale = entity.scale; let mut scale = entity.scale;
@ -157,36 +151,6 @@ impl<'a> System<'a> for Sys {
body, body,
); );
} }
loadout = comp::Loadout {
active_item,
second_item: None,
shoulder: Some(comp::Item::new_from_asset_expect(
"common.items.armor.shoulder.plate_0",
)),
chest: Some(comp::Item::new_from_asset_expect(
"common.items.armor.chest.plate_green_0",
)),
belt: Some(comp::Item::new_from_asset_expect(
"common.items.armor.belt.plate_0",
)),
hand: Some(comp::Item::new_from_asset_expect(
"common.items.armor.hand.plate_0",
)),
pants: Some(comp::Item::new_from_asset_expect(
"common.items.armor.pants.plate_green_0",
)),
foot: Some(comp::Item::new_from_asset_expect(
"common.items.armor.foot.plate_0",
)),
back: None,
ring: None,
neck: None,
lantern: None,
glider: None,
head: None,
tabard: None,
};
stats.level.set_level(rand::thread_rng().gen_range(30, 35)); stats.level.set_level(rand::thread_rng().gen_range(30, 35));
scale = 2.0 + rand::random::<f32>(); scale = 2.0 + rand::random::<f32>();
} }

View File

@ -113,15 +113,26 @@ fn matches_ability_stage() {
}); });
let result = CombatEventMapper::map_event( let result = CombatEventMapper::map_event(
&CharacterState::TripleStrike(states::triple_strike::Data { &CharacterState::ComboMelee(states::combo_melee::Data {
base_damage: 10, stage_data: vec![combo_melee::Stage {
stage: states::triple_strike::Stage::First, stage: 1,
stage_time_active: Duration::default(), base_damage: 100,
stage_exhausted: false, max_damage: 120,
initialized: true, damage_increase: 10,
transition_style: states::triple_strike::TransitionStyle::Hold( knockback: 10.0,
states::triple_strike::HoldingState::Released, range: 4.0,
), angle: 30.0,
base_buildup_duration: Duration::from_millis(500),
base_swing_duration: Duration::from_millis(200),
base_recover_duration: Duration::from_millis(400),
forward_movement: 0.5,
}],
initial_energy_gain: 0,
max_energy_gain: 100,
energy_increase: 20,
speed_increase: 0.05,
max_speed_increase: 1.8,
is_interruptible: true,
}), }),
&PreviousEntityState { &PreviousEntityState {
event: SfxEvent::Idle, event: SfxEvent::Idle,
@ -134,7 +145,7 @@ fn matches_ability_stage() {
assert_eq!( assert_eq!(
result, result,
SfxEvent::Attack( SfxEvent::Attack(
CharacterAbilityType::TripleStrike(states::triple_strike::Stage::First), CharacterAbilityType::ComboMelee(states::utils::StageSection::Swing, 1),
ToolCategory::Sword ToolCategory::Sword
) )
); );
@ -154,15 +165,26 @@ fn ignores_different_ability_stage() {
}); });
let result = CombatEventMapper::map_event( let result = CombatEventMapper::map_event(
&CharacterState::TripleStrike(states::triple_strike::Data { &CharacterState::ComboMelee(states::combo_melee::Data {
base_damage: 10, stage_data: vec![combo_melee::Stage {
stage: states::triple_strike::Stage::Second, stage: 1,
stage_time_active: Duration::default(), base_damage: 100,
stage_exhausted: false, max_damage: 120,
initialized: true, damage_increase: 10,
transition_style: states::triple_strike::TransitionStyle::Hold( knockback: 10.0,
states::triple_strike::HoldingState::Released, range: 4.0,
), angle: 30.0,
base_buildup_duration: Duration::from_millis(500),
base_swing_duration: Duration::from_millis(200),
base_recover_duration: Duration::from_millis(400),
forward_movement: 0.5,
}],
initial_energy_gain: 0,
max_energy_gain: 100,
energy_increase: 20,
speed_increase: 0.05,
max_speed_increase: 1.8,
is_interruptible: true,
}), }),
&PreviousEntityState { &PreviousEntityState {
event: SfxEvent::Idle, event: SfxEvent::Idle,
@ -175,7 +197,7 @@ fn ignores_different_ability_stage() {
assert_ne!( assert_ne!(
result, result,
SfxEvent::Attack( SfxEvent::Attack(
CharacterAbilityType::TripleStrike(states::triple_strike::Stage::First), CharacterAbilityType::ComboMelee(states::utils::StageSection::Swing, 1),
ToolCategory::Sword ToolCategory::Sword
) )
); );

View File

@ -71,11 +71,10 @@
//! ], //! ],
//! threshold: 1.2, //! threshold: 1.2,
//! ), //! ),
//! // A multi-stage attack ability which depends on the weapon (To do: update example) //! // A multi-stage attack ability which depends on the weapon
//! Attack(TripleStrike(First), Sword): ( //! Attack(ComboMelee(Swing, 1), Sword): (
//! files: [ //! files: [
//! "voxygen.audio.sfx.weapon.sword_03", //! "voxygen.audio.sfx.abilities.swing_sword",
//! "voxygen.audio.sfx.weapon.sword_04",
//! ], //! ],
//! threshold: 0.5, //! threshold: 0.5,
//! ), //! ),