From 74b703921912c42975b754d21c761c338950d1a8 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 5 Jun 2021 13:25:47 -0500 Subject: [PATCH] Yeti AI --- .../abilities/custom/yeti/frostbreath.ron | 4 +- .../abilities/custom/yeti/icespikes.ron | 14 ++-- .../common/abilities/custom/yeti/strike.ron | 2 +- common/src/comp/body.rs | 5 +- server/src/sys/agent.rs | 76 +++++++++++++++++++ 5 files changed, 89 insertions(+), 12 deletions(-) diff --git a/assets/common/abilities/custom/yeti/frostbreath.ron b/assets/common/abilities/custom/yeti/frostbreath.ron index 0319a28508..e2c99281d7 100644 --- a/assets/common/abilities/custom/yeti/frostbreath.ron +++ b/assets/common/abilities/custom/yeti/frostbreath.ron @@ -1,11 +1,11 @@ BasicBeam( buildup_duration: 0.4, recover_duration: 0.25, - beam_duration: 0.5, + beam_duration: 0.25, damage: 50, tick_rate: 1.0, range: 15.0, - max_angle: 22.5, + max_angle: 30.0, damage_effect: Some(Buff(( kind: Frozen, dur_secs: 3.0, diff --git a/assets/common/abilities/custom/yeti/icespikes.ron b/assets/common/abilities/custom/yeti/icespikes.ron index f61411800c..818369591b 100644 --- a/assets/common/abilities/custom/yeti/icespikes.ron +++ b/assets/common/abilities/custom/yeti/icespikes.ron @@ -3,15 +3,15 @@ Shockwave( buildup_duration: 0.6, swing_duration: 0.12, recover_duration: 1.2, - damage: 300, - poise_damage: 30, - knockback: (strength: 40.0, direction: Up), + damage: 200, + poise_damage: 10, + knockback: (strength: 30.0, direction: Up), shockwave_angle: 90.0, shockwave_vertical_angle: 15.0, - shockwave_speed: 25.0, - shockwave_duration: 1.0, - requires_ground: false, - move_efficiency: 0.0, + shockwave_speed: 50.0, + shockwave_duration: 0.5, + requires_ground: true, + move_efficiency: 0.5, damage_kind: Piercing, specifier: IceSpikes, ) \ No newline at end of file diff --git a/assets/common/abilities/custom/yeti/strike.ron b/assets/common/abilities/custom/yeti/strike.ron index 8d4e10ed6f..8753f5d1f8 100644 --- a/assets/common/abilities/custom/yeti/strike.ron +++ b/assets/common/abilities/custom/yeti/strike.ron @@ -5,7 +5,7 @@ BasicMelee( recover_duration: 0.5, base_damage: 150, base_poise_damage: 50, - knockback: ( strength: 100.0, direction: Away), + knockback: ( strength: 50.0, direction: Away), range: 4.0, max_angle: 20.0, damage_effect: None, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 1a95d6c6d6..73724f5f7d 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -471,7 +471,7 @@ impl Body { biped_large::Species::Dullahan => 3000, biped_large::Species::Mindflayer => 12500, biped_large::Species::Tidalwarrior => 16000, - biped_large::Species::Yeti => 4000, + biped_large::Species::Yeti => 12000, biped_large::Species::Minotaur => 30000, biped_large::Species::Harvester => 3000, biped_large::Species::Blueoni => 2400, @@ -586,7 +586,7 @@ impl Body { biped_large::Species::Wendigo => 80, biped_large::Species::Troll => 60, biped_large::Species::Dullahan => 120, - biped_large::Species::Yeti => 80, + biped_large::Species::Yeti => 0, biped_large::Species::Harvester => 80, // Boss enemies have their health set, not adjusted by level. biped_large::Species::Mindflayer => 0, @@ -650,6 +650,7 @@ impl Body { biped_large::Species::Mindflayer => 4.8, biped_large::Species::Minotaur => 3.2, biped_large::Species::Tidalwarrior => 2.25, + biped_large::Species::Yeti => 2.0, _ => 1.0, }, Body::Golem(g) => match g.species { diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 55e27466d6..fbf827e382 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -119,6 +119,7 @@ pub enum Tactic { Minotaur, ClayGolem, TidalWarrior, + Yeti, } #[derive(SystemData)] @@ -1609,6 +1610,7 @@ impl<'a> AgentData<'a> { "Clay Golem" => Tactic::ClayGolem, "Tidal Warrior" => Tactic::TidalWarrior, "Tidal Totem" => Tactic::RadialTurret, + "Yeti" => Tactic::Yeti, _ => Tactic::Melee, }, AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind), @@ -1697,6 +1699,18 @@ impl<'a> AgentData<'a> { ), ) }, + Tactic::Yeti if matches!(self.char_state, CharacterState::BasicRanged(_)) => { + const SNOWBALL_SPEED: f32 = 60.0; + aim_projectile( + SNOWBALL_SPEED, + Vec3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z + eye_offset), + Vec3::new( + tgt_data.pos.0.x, + tgt_data.pos.0.y, + tgt_data.pos.0.z + tgt_eye_offset, + ), + ) + }, _ => Dir::from_unnormalized( Vec3::new( tgt_data.pos.0.x, @@ -1865,6 +1879,9 @@ impl<'a> AgentData<'a> { &tgt_data, &read_data, ), + Tactic::Yeti => { + self.handle_yeti_attack(agent, controller, &attack_data, &tgt_data, &read_data) + }, } } @@ -3476,6 +3493,65 @@ impl<'a> AgentData<'a> { self.path_toward_target(agent, controller, tgt_data, read_data, false, None); } + fn handle_yeti_attack( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + ) { + const ICE_SPIKES_RANGE: f32 = 20.0; + const ICE_BREATH_RANGE: f32 = 15.0; + const ICE_BREATH_TIMER: f32 = 5.0; + const SNOWBALL_MAX_RANGE: f32 = 50.0; + + agent.action_state.counter += read_data.dt.0; + + if attack_data.dist_sqrd < ICE_BREATH_RANGE.powi(2) && attack_data.angle < 60.0 { + if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(1)) + { + // Keep using ice breath until a second has passed + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + } else if agent.action_state.counter > ICE_BREATH_TIMER { + // Use ice breath if timer has gone for long enough + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + + if matches!(self.char_state, CharacterState::BasicBeam(_)) { + // Resets action counter when using beam + agent.action_state.counter = 0.0; + } + } else if attack_data.in_min_range() { + // Basic attack if on top of them + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } else { + // Use ice spikes if too far for other abilities + controller + .actions + .push(ControlAction::basic_input(InputKind::Secondary)); + } + } else if attack_data.dist_sqrd < ICE_SPIKES_RANGE.powi(2) && attack_data.angle < 60.0 { + // Use ice spikes if in range + controller + .actions + .push(ControlAction::basic_input(InputKind::Secondary)); + } else if attack_data.dist_sqrd < SNOWBALL_MAX_RANGE.powi(2) && attack_data.angle < 60.0 { + // Otherwise, chuck all the snowballs + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(1))); + } + + // Always attempt to path towards target + self.path_toward_target(agent, controller, tgt_data, read_data, false, None); + } + fn follow( &self, agent: &mut Agent,