diff --git a/assets/common/abilities/adlet/elder/triplestrike.ron b/assets/common/abilities/adlet/elder/triplestrike.ron index 3874146167..30fcc1af1a 100644 --- a/assets/common/abilities/adlet/elder/triplestrike.ron +++ b/assets/common/abilities/adlet/elder/triplestrike.ron @@ -5,7 +5,7 @@ ComboMelee2( kind: Bash( damage: 10, poise: 15, - knockback: 5.0, + knockback: 5, energy_regen: 0, ), range: 2.5, diff --git a/server/agent/src/action_nodes.rs b/server/agent/src/action_nodes.rs index c96bfb32b1..b692cd13ca 100644 --- a/server/agent/src/action_nodes.rs +++ b/server/agent/src/action_nodes.rs @@ -1029,6 +1029,7 @@ impl<'a> AgentData<'a> { secondary: 1, abilities: [4, 0, 0, 0, 0], }, + "Adlet Elder" => Tactic::AdletElder, _ => Tactic::SimpleMelee, }, AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind), @@ -1499,6 +1500,9 @@ impl<'a> AgentData<'a> { secondary, abilities, ), + Tactic::AdletElder => { + self.handle_adlet_elder(agent, controller, &attack_data, tgt_data, read_data) + }, } } diff --git a/server/agent/src/attack.rs b/server/agent/src/attack.rs index 8ebb4ec7f2..67e8c6308e 100644 --- a/server/agent/src/attack.rs +++ b/server/agent/src/attack.rs @@ -4716,6 +4716,59 @@ impl<'a> AgentData<'a> { } } + pub fn handle_adlet_elder( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + ) { + const TRAP_TIMER: usize = 0; + agent.action_state.timers[TRAP_TIMER] -= read_data.dt.0; + if matches!(self.char_state, CharacterState::BasicRanged(_)) { + agent.action_state.timers[TRAP_TIMER] = 15.0; + } + let primary = self.extract_ability(AbilityInput::Primary); + let secondary = self.extract_ability(AbilityInput::Secondary); + let could_use_input = |input| match input { + InputKind::Primary => primary.as_ref().map_or(false, |p| { + p.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + InputKind::Secondary => secondary.as_ref().map_or(false, |s| { + s.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + _ => false, + }; + let move_forwards = if matches!(self.char_state, CharacterState::DashMelee(s) if s.stage_section != StageSection::Recover) + { + controller.push_basic_input(InputKind::Secondary); + false + } else if agent.action_state.timers[TRAP_TIMER] < 0.0 && !tgt_data.considered_ranged() { + controller.push_basic_input(InputKind::Ability(0)); + false + } else if could_use_input(InputKind::Primary) { + controller.push_basic_input(InputKind::Primary); + false + } else if could_use_input(InputKind::Secondary) { + controller.push_basic_input(InputKind::Secondary); + false + } else { + true + }; + + if move_forwards && attack_data.dist_sqrd > 2_f32.powi(2) { + self.path_toward_target( + agent, + controller, + tgt_data.pos.0, + read_data, + Path::Separate, + None, + ); + } + } + pub fn handle_icedrake( &self, agent: &mut Agent, diff --git a/server/agent/src/data.rs b/server/agent/src/data.rs index c7bd0ecf1c..d183adb88d 100644 --- a/server/agent/src/data.rs +++ b/server/agent/src/data.rs @@ -5,7 +5,10 @@ use common::{ buff::{BuffKind, Buffs}, character_state::AttackFilters, group, - item::MaterialStatManifest, + inventory::{ + item::{tool::ToolKind, ItemKind, MaterialStatManifest}, + slot::EquipSlot, + }, ActiveAbilities, Alignment, Body, CharacterState, Combo, Energy, Health, Inventory, LightEmitter, LootOwner, Ori, PhysicsState, Poise, Pos, Presence, PresenceKind, Scale, SkillSet, Stance, Stats, Vel, @@ -66,6 +69,7 @@ pub struct TargetData<'a> { pub char_state: Option<&'a CharacterState>, pub health: Option<&'a Health>, pub buffs: Option<&'a Buffs>, + pub drawn_weapons: (Option, Option), } impl<'a> TargetData<'a> { @@ -77,8 +81,55 @@ impl<'a> TargetData<'a> { char_state: read_data.char_states.get(target), health: read_data.healths.get(target), buffs: read_data.buffs.get(target), + drawn_weapons: { + let slotted_tool = |inv: &Inventory, slot| { + if let Some(ItemKind::Tool(tool)) = + inv.equipped(slot).map(|i| i.kind()).as_deref() + { + Some(tool.kind) + } else { + None + } + }; + read_data + .inventories + .get(target) + .map_or((None, None), |inv| { + ( + slotted_tool(inv, EquipSlot::ActiveMainhand), + slotted_tool(inv, EquipSlot::ActiveOffhand), + ) + }) + }, } } + + pub fn considered_ranged(&self) -> bool { + let is_ranged_tool = |tool| match tool { + Some( + ToolKind::Sword + | ToolKind::Axe + | ToolKind::Hammer + | ToolKind::Dagger + | ToolKind::Shield + | ToolKind::Spear + | ToolKind::Farming + | ToolKind::Pick + | ToolKind::Natural + | ToolKind::Empty, + ) + | None => false, + Some( + ToolKind::Bow + | ToolKind::Staff + | ToolKind::Sceptre + | ToolKind::Blowgun + | ToolKind::Debug + | ToolKind::Instrument, + ) => true, + }; + is_ranged_tool(self.drawn_weapons.0) || is_ranged_tool(self.drawn_weapons.1) + } } pub struct AttackData { @@ -188,6 +239,7 @@ pub enum Tactic { AdletHunter, AdletIcepicker, AdletTracker, + AdletElder, } #[derive(Copy, Clone)]