From e1f164d0996cccb15b3db49b9aa24eec235b73f7 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 19 Jan 2022 20:15:50 -0500 Subject: [PATCH] Initial AI for gnarling logger/mugger. --- .../common/abilities/ability_set_manifest.ron | 13 ++-- .../abilities/axesimple/doublestrike.ron | 56 ----------------- .../abilities/daggersimple/singlestrike.ron | 34 ----------- assets/common/abilities/gnarling/axe/chop.ron | 18 ++++++ .../common/abilities/gnarling/dagger/stab.ron | 18 ++++++ .../entity/dungeon/gnarling/chieftain.ron | 6 +- .../common/entity/dungeon/gnarling/logger.ron | 6 +- .../common/entity/dungeon/gnarling/mugger.ron | 6 +- .../entity/dungeon/gnarling/stalker.ron | 6 +- .../biped_small/gnarling/logger.ron | 7 ++- .../biped_small/gnarling/mugger.ron | 3 +- .../chieftain.ron} | 0 .../dungeon/{tier-0 => gnarling}/logger.ron | 0 .../dungeon/{tier-0 => gnarling}/mugger.ron | 0 .../dungeon/{tier-0 => gnarling}/stalker.ron | 0 server/src/sys/agent.rs | 11 +++- server/src/sys/agent/attack.rs | 60 ++++++++++++++++++- server/src/sys/agent/data.rs | 1 + voxygen/src/scene/figure/mod.rs | 32 ++++++++++ 19 files changed, 163 insertions(+), 114 deletions(-) delete mode 100644 assets/common/abilities/axesimple/doublestrike.ron delete mode 100644 assets/common/abilities/daggersimple/singlestrike.ron create mode 100644 assets/common/abilities/gnarling/axe/chop.ron create mode 100644 assets/common/abilities/gnarling/dagger/stab.ron rename assets/common/loadout/dungeon/{tier-0/gnarling_chieftain.ron => gnarling/chieftain.ron} (100%) rename assets/common/loadout/dungeon/{tier-0 => gnarling}/logger.ron (100%) rename assets/common/loadout/dungeon/{tier-0 => gnarling}/mugger.ron (100%) rename assets/common/loadout/dungeon/{tier-0 => gnarling}/stalker.ron (100%) diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 7f4e5a0ce1..d70ddbc8b9 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -63,14 +63,15 @@ secondary: "common.abilities.hammersimple.doublestrike", abilities: [], ), - Custom("Axe Simple"): ( - primary: "common.abilities.axesimple.doublestrike", - secondary: "common.abilities.axesimple.doublestrike", + // TODO: Later investigate if we want to make this back to a simple axe when more things need a simpler axe ability set + Custom("Gnarling Axe"): ( + primary: "common.abilities.gnarling.axe.chop", + secondary: "common.abilities.gnarling.axe.chop", abilities: [], ), - Custom("Dagger Simple"): ( - primary: "common.abilities.daggersimple.singlestrike", - secondary: "common.abilities.daggersimple.singlestrike", + Custom("Gnarling Dagger"): ( + primary: "common.abilities.gnarling.dagger.stab", + secondary: "common.abilities.gnarling.dagger.stab", abilities: [], ), Custom("Sword Simple"): ( diff --git a/assets/common/abilities/axesimple/doublestrike.ron b/assets/common/abilities/axesimple/doublestrike.ron deleted file mode 100644 index 443680ebc8..0000000000 --- a/assets/common/abilities/axesimple/doublestrike.ron +++ /dev/null @@ -1,56 +0,0 @@ -ComboMelee( - stage_data: [ - ( - stage: 1, - base_damage: 8.0, - damage_increase: 1.0, - base_poise_damage: 15, - poise_damage_increase: 0, - knockback: 8.0, - range: 3.5, - angle: 50.0, - base_buildup_duration: 0.4, - base_swing_duration: 0.08, - hit_timing: 0.5, - base_recover_duration: 0.5, - forward_movement: 2.5, - damage_kind: Slashing, - damage_effect: Some(Buff(( - kind: Bleeding, - dur_secs: 10.0, - strength: DamageFraction(0.1), - chance: 0.1, - ))), - ), - ( - stage: 2, - base_damage: 10.0, - damage_increase: 1.5, - base_poise_damage: 20, - poise_damage_increase: 0, - knockback: 12.0, - range: 3.5, - angle: 30.0, - base_buildup_duration: 0.7, - base_swing_duration: 0.1, - hit_timing: 0.5, - base_recover_duration: 0.7, - forward_movement: 2.0, - damage_kind: Slashing, - damage_effect: Some(Buff(( - kind: Bleeding, - dur_secs: 10.0, - strength: DamageFraction(0.1), - chance: 0.1, - ))), - ), - ], - initial_energy_gain: 0, - max_energy_gain: 0, - energy_increase: 0, - speed_increase: 0.0, - max_speed_increase: 0.0, - scales_from_combo: 0, - is_interruptible: false, - ori_modifier: 0.7, -) diff --git a/assets/common/abilities/daggersimple/singlestrike.ron b/assets/common/abilities/daggersimple/singlestrike.ron deleted file mode 100644 index 202617116c..0000000000 --- a/assets/common/abilities/daggersimple/singlestrike.ron +++ /dev/null @@ -1,34 +0,0 @@ -ComboMelee( - stage_data: [ - ( - stage: 1, - base_damage: 2.0, - damage_increase: 1.0, - base_poise_damage: 8, - poise_damage_increase: 0, - knockback: 8.0, - range: 3.5, - angle: 50.0, - base_buildup_duration: 0.1, - base_swing_duration: 0.05, - hit_timing: 0.5, - base_recover_duration: 0.05, - forward_movement: 1.0, - damage_kind: Slashing, - damage_effect: Some(Buff(( - kind: Bleeding, - dur_secs: 10.0, - strength: DamageFraction(0.1), - chance: 0.1, - ))), - ), - ], - initial_energy_gain: 0, - max_energy_gain: 0, - energy_increase: 0, - speed_increase: 0.0, - max_speed_increase: 0.0, - scales_from_combo: 0, - is_interruptible: false, - ori_modifier: 0.7, -) diff --git a/assets/common/abilities/gnarling/axe/chop.ron b/assets/common/abilities/gnarling/axe/chop.ron new file mode 100644 index 0000000000..cbbe6ec444 --- /dev/null +++ b/assets/common/abilities/gnarling/axe/chop.ron @@ -0,0 +1,18 @@ +BasicMelee( + energy_cost: 0, + buildup_duration: 0.2, + swing_duration: 0.05, + recover_duration: 0.3, + base_damage: 6, + base_poise_damage: 5, + knockback: ( strength: 0, direction: Away), + range: 3, + max_angle: 30, + damage_effect: Some(Buff(( + kind: Bleeding, + dur_secs: 10, + strength: DamageFraction(0.1), + chance: 0.1, + ))), + damage_kind: Slashing, +) diff --git a/assets/common/abilities/gnarling/dagger/stab.ron b/assets/common/abilities/gnarling/dagger/stab.ron new file mode 100644 index 0000000000..00046bc35b --- /dev/null +++ b/assets/common/abilities/gnarling/dagger/stab.ron @@ -0,0 +1,18 @@ +BasicMelee( + energy_cost: 0, + buildup_duration: 0.1, + swing_duration: 0.05, + recover_duration: 0.25, + base_damage: 4, + base_poise_damage: 0, + knockback: ( strength: 0, direction: Away), + range: 2, + max_angle: 15, + damage_effect: Some(Buff(( + kind: Bleeding, + dur_secs: 10, + strength: DamageFraction(0.2), + chance: 0.3, + ))), + damage_kind: Piercing, +) diff --git a/assets/common/entity/dungeon/gnarling/chieftain.ron b/assets/common/entity/dungeon/gnarling/chieftain.ron index 1a673878af..c427bd4a6a 100644 --- a/assets/common/entity/dungeon/gnarling/chieftain.ron +++ b/assets/common/entity/dungeon/gnarling/chieftain.ron @@ -3,10 +3,10 @@ body: RandomWith("gnarling"), alignment: Alignment(Enemy), loadout: Extended( - hands: TwoHanded(Item("common.items.npc_weapons.biped_small.gnarling.chieftain")), - base_asset: Loadout("common.loadout.dungeon.tier-0.gnarling_chieftain"), + hands: TwoHanded(Item("common.items.npc_weapons.biped_small.gnarling.chieftain_staff")), + base_asset: Loadout("common.loadout.dungeon.gnarling.chieftain"), inventory: [], ), loot: LootTable("common.loot_tables.dungeon.tier-0.enemy"), meta: [], -) \ No newline at end of file +) diff --git a/assets/common/entity/dungeon/gnarling/logger.ron b/assets/common/entity/dungeon/gnarling/logger.ron index 47c999b27f..33b305d055 100644 --- a/assets/common/entity/dungeon/gnarling/logger.ron +++ b/assets/common/entity/dungeon/gnarling/logger.ron @@ -3,10 +3,10 @@ body: RandomWith("gnarling"), alignment: Alignment(Enemy), loadout: Extended( - hands: TwoHanded(Item("common.items.npc_weapons.biped_small.gnarling.logger")), - base_asset: Loadout("common.loadout.dungeon.tier-0.logger"), + hands: TwoHanded(Item("common.items.npc_weapons.biped_small.gnarling.axe")), + base_asset: Loadout("common.loadout.dungeon.gnarling.logger"), inventory: [], ), loot: LootTable("common.loot_tables.dungeon.tier-0.enemy"), meta: [], -) \ No newline at end of file +) diff --git a/assets/common/entity/dungeon/gnarling/mugger.ron b/assets/common/entity/dungeon/gnarling/mugger.ron index 26c5483858..15e119f8b3 100644 --- a/assets/common/entity/dungeon/gnarling/mugger.ron +++ b/assets/common/entity/dungeon/gnarling/mugger.ron @@ -3,10 +3,10 @@ body: RandomWith("gnarling"), alignment: Alignment(Enemy), loadout: Extended( - hands: TwoHanded(Item("common.items.npc_weapons.biped_small.gnarling.mugger")), - base_asset: Loadout("common.loadout.dungeon.tier-0.mugger"), + hands: TwoHanded(Item("common.items.npc_weapons.biped_small.gnarling.dagger")), + base_asset: Loadout("common.loadout.dungeon.gnarling.mugger"), inventory: [], ), loot: LootTable("common.loot_tables.dungeon.tier-0.enemy"), meta: [], -) \ No newline at end of file +) diff --git a/assets/common/entity/dungeon/gnarling/stalker.ron b/assets/common/entity/dungeon/gnarling/stalker.ron index dfb9b694d7..c1528a0375 100644 --- a/assets/common/entity/dungeon/gnarling/stalker.ron +++ b/assets/common/entity/dungeon/gnarling/stalker.ron @@ -3,10 +3,10 @@ body: RandomWith("gnarling"), alignment: Alignment(Enemy), loadout: Extended( - hands: TwoHanded(Item("common.items.npc_weapons.biped_small.gnarling.stalker")), - base_asset: Loadout("common.loadout.dungeon.tier-0.stalker"), + hands: TwoHanded(Item("common.items.npc_weapons.biped_small.gnarling.blowgun")), + base_asset: Loadout("common.loadout.dungeon.gnarling.stalker"), inventory: [], ), loot: LootTable("common.loot_tables.dungeon.tier-0.enemy"), meta: [], -) \ No newline at end of file +) diff --git a/assets/common/items/npc_weapons/biped_small/gnarling/logger.ron b/assets/common/items/npc_weapons/biped_small/gnarling/logger.ron index 2582a28283..c953ec0572 100644 --- a/assets/common/items/npc_weapons/biped_small/gnarling/logger.ron +++ b/assets/common/items/npc_weapons/biped_small/gnarling/logger.ron @@ -6,10 +6,10 @@ ItemDef( hands: Two, stats: Direct(( equip_time_secs: 0.0, - power: 0.3, + power: 1.0, effect_power: 1.0, speed: 1.0, - crit_chance: 0.12037037, + crit_chance: 0.1, range: 1.0, energy_efficiency: 1.0, buff_strength: 1.0, @@ -17,5 +17,6 @@ ItemDef( )), quality: Low, tags: [], - ability_spec: Some(Custom("Axe Simple")), + // TODO: Later investigate if we want to make this back to a simple axe when more things need a simpler axe ability set + ability_spec: Some(Custom("Gnarling Axe")), ) \ No newline at end of file diff --git a/assets/common/items/npc_weapons/biped_small/gnarling/mugger.ron b/assets/common/items/npc_weapons/biped_small/gnarling/mugger.ron index 64bef011da..5ece43f52a 100644 --- a/assets/common/items/npc_weapons/biped_small/gnarling/mugger.ron +++ b/assets/common/items/npc_weapons/biped_small/gnarling/mugger.ron @@ -17,5 +17,6 @@ ItemDef( )), quality: Low, tags: [], - ability_spec: Some(Custom("Dagger Simple")), + // TODO: Later investigate if we want to make this back to a simple dagger when more things need a simpler dagger ability set + ability_spec: Some(Custom("Gnarling Dagger")), ) \ No newline at end of file diff --git a/assets/common/loadout/dungeon/tier-0/gnarling_chieftain.ron b/assets/common/loadout/dungeon/gnarling/chieftain.ron similarity index 100% rename from assets/common/loadout/dungeon/tier-0/gnarling_chieftain.ron rename to assets/common/loadout/dungeon/gnarling/chieftain.ron diff --git a/assets/common/loadout/dungeon/tier-0/logger.ron b/assets/common/loadout/dungeon/gnarling/logger.ron similarity index 100% rename from assets/common/loadout/dungeon/tier-0/logger.ron rename to assets/common/loadout/dungeon/gnarling/logger.ron diff --git a/assets/common/loadout/dungeon/tier-0/mugger.ron b/assets/common/loadout/dungeon/gnarling/mugger.ron similarity index 100% rename from assets/common/loadout/dungeon/tier-0/mugger.ron rename to assets/common/loadout/dungeon/gnarling/mugger.ron diff --git a/assets/common/loadout/dungeon/tier-0/stalker.ron b/assets/common/loadout/dungeon/gnarling/stalker.ron similarity index 100% rename from assets/common/loadout/dungeon/tier-0/stalker.ron rename to assets/common/loadout/dungeon/gnarling/stalker.ron diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index dd99385152..6683823bb4 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -1703,7 +1703,7 @@ impl<'a> AgentData<'a> { if let Some(ability_spec) = item.ability_spec() { match ability_spec { AbilitySpec::Custom(spec) => match spec.as_str() { - "Axe Simple" | "Oni" | "Sword Simple" => Tactic::Sword, + "Oni" | "Sword Simple" => Tactic::Sword, "Staff Simple" => Tactic::Staff, "Bow Simple" => Tactic::Bow, "Stone Golem" => Tactic::StoneGolem, @@ -1745,6 +1745,7 @@ impl<'a> AgentData<'a> { "Tidal Totem" => Tactic::RadialTurret, "Yeti" => Tactic::Yeti, "Harvester" => Tactic::Harvester, + "Gnarling Dagger" => Tactic::Backstab, _ => Tactic::Melee, }, AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind), @@ -2110,6 +2111,14 @@ impl<'a> AgentData<'a> { Tactic::Harvester => { self.handle_harvester_attack(agent, controller, &attack_data, tgt_data, read_data) }, + Tactic::Backstab => self.handle_backstab_attack( + agent, + controller, + &attack_data, + tgt_data, + read_data, + rng, + ), } } diff --git a/server/src/sys/agent/attack.rs b/server/src/sys/agent/attack.rs index 9c89a30f8b..498fdc3162 100644 --- a/server/src/sys/agent/attack.rs +++ b/server/src/sys/agent/attack.rs @@ -19,6 +19,8 @@ use std::{f32::consts::PI, time::Duration}; use vek::*; impl<'a> AgentData<'a> { + // Intended for any agent that has one attack, that attack is a melee attack, + // and the agent is able to freely walk around pub fn handle_melee_attack( &self, agent: &mut Agent, @@ -28,7 +30,7 @@ impl<'a> AgentData<'a> { read_data: &ReadData, rng: &mut impl Rng, ) { - if attack_data.in_min_range() && attack_data.angle < 45.0 { + if attack_data.in_min_range() && attack_data.angle < 30.0 { controller.push_basic_input(InputKind::Primary); controller.inputs.move_dir = Vec2::zero(); } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) { @@ -45,6 +47,62 @@ impl<'a> AgentData<'a> { } } + // Intended for any agent that has one attack, that attack is a melee attack, + // the agent is able to freely walk around, and the agent is trying to attack + // from behind its target + pub fn handle_backstab_attack( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + rng: &mut impl Rng, + ) { + // Handle attacking of agent + if attack_data.in_min_range() && attack_data.angle < 30.0 { + controller.push_basic_input(InputKind::Primary); + controller.inputs.move_dir = Vec2::zero(); + } + + // Handle movement of agent + let target_ori = agent + .target + .and_then(|t| read_data.orientations.get(t.target)) + .map(|ori| ori.look_vec()) + .unwrap_or_default(); + let vec_to_target = (tgt_data.pos.0 - self.pos.0).xy(); + let in_front_of_target = target_ori.dot(self.pos.0 - tgt_data.pos.0) > 0.0; + if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) { + // If in front of the target, circle to try and get behind, else just make a + // beeline for the back of the agent + if in_front_of_target { + // Checks both CW and CCW rotation + let potential_move_dirs = [ + vec_to_target + .rotated_z(PI / 2.) + .try_normalized() + .unwrap_or_default(), + vec_to_target + .rotated_z(-PI / 2.) + .try_normalized() + .unwrap_or_default(), + ]; + // Finds shortest path to get behind + if let Some(move_dir) = potential_move_dirs + .iter() + .find(|move_dir| target_ori.xy().dot(**move_dir) < 0.0) + { + controller.inputs.move_dir = *move_dir; + } + } else { + controller.inputs.move_dir = vec_to_target.try_normalized().unwrap_or_default(); + } + } else { + self.path_toward_target(agent, controller, tgt_data, read_data, false, true, None); + } + } + pub fn handle_axe_attack( &self, agent: &mut Agent, diff --git a/server/src/sys/agent/data.rs b/server/src/sys/agent/data.rs index 3b60ebe1a9..de19c81cf7 100644 --- a/server/src/sys/agent/data.rs +++ b/server/src/sys/agent/data.rs @@ -103,6 +103,7 @@ pub enum Tactic { Yeti, Tornado, Harvester, + Backstab, } #[derive(SystemData)] diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 6b230f011d..9b196c1979 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -3182,6 +3182,38 @@ impl FigureMgr { ), } }, + CharacterState::BasicMelee(s) => { + let stage_time = s.timer.as_secs_f32(); + let stage_progress = match s.stage_section { + StageSection::Buildup => { + stage_time / s.static_data.buildup_duration.as_secs_f32() + }, + StageSection::Action => { + stage_time / s.static_data.swing_duration.as_secs_f32() + }, + StageSection::Recover => { + stage_time / s.static_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + anim::biped_small::AlphaAnimation::update_skeleton( + &target_base, + ( + active_tool_kind, + rel_vel, + ori * anim::vek::Vec3::::unit_y(), + state.last_ori * anim::vek::Vec3::::unit_y(), + time, + rel_avg_vel, + state.acc_vel, + Some(s.stage_section), + state.state_time, + ), + stage_progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, // TODO! _ => target_base, };