From c162827f9767cac6858b1434fc0177cff1c7f445 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 20 Jun 2021 12:04:06 -0500 Subject: [PATCH] Harvester AI --- server/src/sys/agent.rs | 83 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index a11989aad7..475115cc36 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -122,6 +122,7 @@ pub enum Tactic { TidalWarrior, Yeti, Tornado, + Harvester, } #[derive(SystemData)] @@ -1663,6 +1664,7 @@ impl<'a> AgentData<'a> { "Tidal Warrior" => Tactic::TidalWarrior, "Tidal Totem" => Tactic::RadialTurret, "Yeti" => Tactic::Yeti, + "Harvester" => Tactic::Harvester, _ => Tactic::Melee, }, AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind), @@ -1763,6 +1765,18 @@ impl<'a> AgentData<'a> { ), ) }, + Tactic::Harvester if matches!(self.char_state, CharacterState::BasicRanged(_)) => { + const PUMPKIN_SPEED: f32 = 30.0; + aim_projectile( + PUMPKIN_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, @@ -1942,6 +1956,9 @@ impl<'a> AgentData<'a> { Tactic::Yeti => { self.handle_yeti_attack(agent, controller, &attack_data, &tgt_data, &read_data) }, + Tactic::Harvester => { + self.handle_harvester_attack(agent, controller, &attack_data, &tgt_data, &read_data) + }, } } @@ -3693,6 +3710,72 @@ impl<'a> AgentData<'a> { self.path_toward_target(agent, controller, tgt_data, read_data, false, None); } + fn handle_harvester_attack( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + ) { + const VINE_CREATION_THRESHOLD: f32 = 0.50; + const FIRE_BREATH_RANGE: f32 = 20.0; + const MAX_PUMPKIN_RANGE: f32 = 50.0; + let health_fraction = self.health.map_or(0.5, |h| h.fraction()); + + if health_fraction < VINE_CREATION_THRESHOLD && !agent.action_state.condition { + // Summon vines when reach threshold of health + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + + if matches!(self.char_state, CharacterState::SpriteSummon(c) if matches!(c.stage_section, StageSection::Recover)) + { + agent.action_state.condition = true; + } + } else if attack_data.dist_sqrd < FIRE_BREATH_RANGE.powi(2) { + if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(10)) + { + // Keep breathing fire if close enough and have not been breathing for more than + // 10 seconds + controller + .actions + .push(ControlAction::basic_input(InputKind::Secondary)); + } else if attack_data.in_min_range() && attack_data.angle < 60.0 { + // Scythe them if they're in range and angle + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } else if attack_data.angle < 30.0 + && can_see_tgt( + &*read_data.terrain, + self.pos, + tgt_data.pos, + attack_data.dist_sqrd, + ) + { + // Start breathing fire at them if close enough, in angle, and can see target + controller + .actions + .push(ControlAction::basic_input(InputKind::Secondary)); + } + } else if attack_data.dist_sqrd < MAX_PUMPKIN_RANGE.powi(2) + && can_see_tgt( + &*read_data.terrain, + self.pos, + tgt_data.pos, + attack_data.dist_sqrd, + ) + { + // Throw a pumpkin at them if close enough and can see them + 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,