From 6bacb487f3f39e10d8c0ca9427d3e3a1a9cb3cb4 Mon Sep 17 00:00:00 2001 From: James Melkonian Date: Sat, 22 Oct 2022 18:18:23 -0700 Subject: [PATCH] Bat AI --- .../common/abilities/ability_set_manifest.ron | 5 ++ .../custom/simpleflyingmelee/singlestrike.ron | 28 +++++++++ .../npc_weapons/unique/simpleflyingbasic.ron | 21 +++++++ common/src/comp/agent.rs | 1 + common/src/comp/body.rs | 13 ++++- common/src/comp/inventory/loadout_builder.rs | 8 ++- server/agent/src/action_nodes.rs | 47 +++++++++++++++ server/agent/src/attack.rs | 58 +++++++++++++++++++ server/agent/src/data.rs | 1 + server/src/cmd.rs | 1 + 10 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 assets/common/abilities/custom/simpleflyingmelee/singlestrike.ron create mode 100644 assets/common/items/npc_weapons/unique/simpleflyingbasic.ron diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index f448a5711c..ae616991c8 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -121,6 +121,11 @@ (None, "common.abilities.custom.woodgolem.shockwave") ], ), + Custom("Simple Flying Melee"): ( + primary: "common.abilities.custom.simpleflyingmelee.singlestrike", + secondary: "common.abilities.custom.simpleflyingmelee.singlestrike", + abilities: [], + ), Custom("Sword Simple"): ( primary: "common.abilities.swordsimple.doublestrike", secondary: "common.abilities.swordsimple.dash", diff --git a/assets/common/abilities/custom/simpleflyingmelee/singlestrike.ron b/assets/common/abilities/custom/simpleflyingmelee/singlestrike.ron new file mode 100644 index 0000000000..d78682ef8a --- /dev/null +++ b/assets/common/abilities/custom/simpleflyingmelee/singlestrike.ron @@ -0,0 +1,28 @@ +ComboMelee( + stage_data: [ + ( + stage: 1, + base_damage: 4.0, + damage_increase: 0, + base_poise_damage: 0, + poise_damage_increase: 0, + knockback: 0.0, + range: 2.5, + angle: 150.0, + base_buildup_duration: 0.1, + base_swing_duration: 0.07, + hit_timing: 0.5, + base_recover_duration: 0.2, + forward_movement: 0.0, + damage_kind: Piercing, + ), + ], + 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.6, +) diff --git a/assets/common/items/npc_weapons/unique/simpleflyingbasic.ron b/assets/common/items/npc_weapons/unique/simpleflyingbasic.ron new file mode 100644 index 0000000000..50ccd3585b --- /dev/null +++ b/assets/common/items/npc_weapons/unique/simpleflyingbasic.ron @@ -0,0 +1,21 @@ +ItemDef( + name: "Simple Flying Melee", + description: "I believe I can fly!!!!!", + kind: Tool(( + kind: Natural, + hands: Two, + stats: ( + equip_time_secs: 0.01, + power: 1.0, + effect_power: 1.0, + speed: 1.0, + crit_chance: 0.0625, + range: 1.0, + energy_efficiency: 1.0, + buff_strength: 1.0, + ), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Simple Flying Melee")), +) diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 65d000d25a..96fe88c0e4 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -289,6 +289,7 @@ impl<'a> From<&'a Body> for Psyche { bird_medium::Species::Peacock => 0.4, bird_medium::Species::Eagle => 0.3, bird_medium::Species::Parrot => 0.8, + bird_medium::Species::Bat => 0.0, _ => 0.5, }, Body::BirdLarge(_) => 0.1, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index cfe8fa847a..c93459c768 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -27,7 +27,7 @@ use specs::{Component, DerefFlaggedStorage}; use strum::Display; use vek::*; -use super::{BuffKind, Collider, Density, Mass}; +use super::{BuffKind, Collider, Density, Mass, Scale}; make_case_elim!( body, @@ -231,6 +231,17 @@ impl Body { ) } + pub fn scale(&self) -> Scale { + let s = match self { + Body::BirdMedium(bird_medium) => match bird_medium.species { + bird_medium::Species::Bat => 0.5, + _ => 1.0, + }, + _ => 1.0, + }; + Scale(s) + } + /// Average density of the body // Units are based on kg/m³ pub fn density(&self) -> Density { diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index c8334a2ba1..401f4fe850 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -1,7 +1,7 @@ use crate::{ assets::{self, AssetExt}, comp::{ - arthropod, biped_large, biped_small, bird_large, golem, + arthropod, biped_large, biped_small, bird_large, bird_medium, golem, inventory::{ loadout::Loadout, slot::{ArmorSlot, EquipSlot}, @@ -778,6 +778,12 @@ fn default_main_tool(body: &Body) -> Item { "common.items.npc_weapons.unique.birdlargebasic", )), }, + Body::BirdMedium(bird_medium) => match bird_medium.species { + bird_medium::Species::Bat => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.simpleflyingbasic", + )), + _ => None, + }, _ => None, }; diff --git a/server/agent/src/action_nodes.rs b/server/agent/src/action_nodes.rs index 5d85e1927e..b30dc4f07c 100644 --- a/server/agent/src/action_nodes.rs +++ b/server/agent/src/action_nodes.rs @@ -322,6 +322,44 @@ impl<'a> AgentData<'a> { } } } else { + // Bats should fly + // Use a proportional controller as the bouncing effect mimics bat flight + if self.traversal_config.can_fly + && self + .inventory + .equipped(EquipSlot::ActiveMainhand) + .as_ref() + .map_or(false, |item| { + item.ability_spec().map_or(false, |a_s| match &*a_s { + AbilitySpec::Custom(spec) => match spec.as_str() { + "Simple Flying Melee" => true, + _ => false, + }, + _ => false, + }) + }) + { + // Bats don't like the ground, so make sure they are always flying + controller.push_basic_input(InputKind::Fly); + if read_data + .terrain + .ray(self.pos.0, self.pos.0 - (Vec3::unit_z() * 5.0)) + .until(Block::is_solid) + .cast() + .1 + .map_or(true, |b| b.is_some()) + { + // Fly up + controller.inputs.move_z = 1.0; + // If on the ground, jump + if self.physics_state.on_ground.is_some() { + controller.push_basic_input(InputKind::Jump); + } + } else { + // Fly down + controller.inputs.move_z = -1.0; + } + } agent.bearing += Vec2::new(rng.gen::() - 0.5, rng.gen::() - 0.5) * 0.1 - agent.bearing * 0.003 - agent.patrol_origin.map_or(Vec2::zero(), |patrol_origin| { @@ -768,6 +806,7 @@ impl<'a> AgentData<'a> { AbilitySpec::Custom(spec) => match spec.as_str() { "Oni" | "Sword Simple" => Tactic::Sword, "Staff Simple" => Tactic::Staff, + "Simple Flying Melee" => Tactic::SimpleFlyingMelee, "Bow Simple" => Tactic::Bow, "Stone Golem" => Tactic::StoneGolem, "Quad Med Quick" => Tactic::CircleCharge { @@ -1000,6 +1039,14 @@ impl<'a> AgentData<'a> { // Match on tactic. Each tactic has different controls depending on the distance // from the agent to the target. match tactic { + Tactic::SimpleFlyingMelee => self.handle_simple_flying_melee( + agent, + controller, + &attack_data, + tgt_data, + read_data, + rng, + ), Tactic::SimpleMelee => { self.handle_simple_melee(agent, controller, &attack_data, tgt_data, read_data, rng) }, diff --git a/server/agent/src/attack.rs b/server/agent/src/attack.rs index a7360a3100..1c75da5e5a 100644 --- a/server/agent/src/attack.rs +++ b/server/agent/src/attack.rs @@ -53,6 +53,64 @@ 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 fly around + pub fn handle_simple_flying_melee( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + rng: &mut impl Rng, + ) { + // Fly to target + let dir_to_target = ((tgt_data.pos.0 + Vec3::unit_z() * 1.5) - self.pos.0) + .try_normalized() + .unwrap_or_else(Vec3::zero); + let speed = 1.0; + controller.inputs.move_dir = dir_to_target.xy() * speed; + + // Always fly! If the floor can't touch you, it can't hurt you... + controller.push_basic_input(InputKind::Fly); + // Flee from the ground! The internet told me it was lava! + // If on the ground, jump with every last ounce of energy, holding onto all that + // is dear in life and straining for the wide open skies. + if self.physics_state.on_ground.is_some() { + controller.push_basic_input(InputKind::Jump); + } else { + // Only fly down if close enough to target in the xy plane + // Otherwise fly towards the target bouncing around a 5 block altitude + let mut maintain_altitude = |altitude| { + if read_data + .terrain + .ray(self.pos.0, self.pos.0 - (Vec3::unit_z() * altitude)) + .until(Block::is_solid) + .cast() + .1 + .map_or(true, |b| b.is_some()) + { + // Fly up + controller.inputs.move_z = 1.0; + } else { + // Fly down + controller.inputs.move_z = -1.0; + } + }; + if (tgt_data.pos.0 - self.pos.0).xy().magnitude_squared() > (5.0_f32).powi(2) { + // If above 5 blocks, fly down + maintain_altitude(5.0); + } else { + maintain_altitude(2.0); + + // Attack if in range + if attack_data.dist_sqrd < 3.5_f32.powi(2) && attack_data.angle < 150.0 { + controller.push_basic_input(InputKind::Primary); + } + } + } + } + // 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 diff --git a/server/agent/src/data.rs b/server/agent/src/data.rs index 891aac9b6d..9cae73b4de 100644 --- a/server/agent/src/data.rs +++ b/server/agent/src/data.rs @@ -74,6 +74,7 @@ impl AttackData { pub enum Tactic { // General tactics SimpleMelee, + SimpleFlyingMelee, SimpleBackstab, ElevatedRanged, Turret, diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7f273b87bf..4160b33e83 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1212,6 +1212,7 @@ fn handle_spawn( body, ) .with(comp::Vel(vel)) + .with(body.scale()) .with(alignment); if ai {