From 35242c71e1e81ff856ee91fc83ff8f843bb95ec7 Mon Sep 17 00:00:00 2001 From: Knightress Paladin Date: Tue, 6 Jul 2021 01:29:11 -0700 Subject: [PATCH] Added sceptre tactic and sceptre cultists --- .../common/entity/dungeon/tier-5/cultist.ron | 6 +- .../common/skillset/dungeon/tier-5/enemy.ron | 1 + .../skillset/dungeon/tier-5/sceptre.ron | 21 +++ server/src/sys/agent.rs | 132 +++++++++++++++++- 4 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 assets/common/skillset/dungeon/tier-5/sceptre.ron diff --git a/assets/common/entity/dungeon/tier-5/cultist.ron b/assets/common/entity/dungeon/tier-5/cultist.ron index 8b19091d2d..6d26df32c2 100644 --- a/assets/common/entity/dungeon/tier-5/cultist.ron +++ b/assets/common/entity/dungeon/tier-5/cultist.ron @@ -5,11 +5,7 @@ EntityConfig ( loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), main_tool: Some(Choice([ - (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))), - (2.0, Some(Item("common.items.weapons.sword.cultist"))), - (1.0, Some(Item("common.items.weapons.hammer.cultist_purp_2h-0"))), - (1.0, Some(Item("common.items.weapons.hammer_1h.orichalcum-0"))), - (1.0, Some(Item("common.items.weapons.bow.velorite"))), + (1.0, Some(Item("common.items.weapons.sceptre.sceptre_velorite_0"))), ])), second_tool: None, diff --git a/assets/common/skillset/dungeon/tier-5/enemy.ron b/assets/common/skillset/dungeon/tier-5/enemy.ron index d0ee8c79f6..4adcb390e3 100644 --- a/assets/common/skillset/dungeon/tier-5/enemy.ron +++ b/assets/common/skillset/dungeon/tier-5/enemy.ron @@ -4,4 +4,5 @@ Tree("common.skillset.dungeon.tier-5.axe"), Tree("common.skillset.dungeon.tier-5.hammer"), Tree("common.skillset.dungeon.tier-5.bow"), + Tree("common.skillset.dungeon.tier-5.sceptre"), ]) diff --git a/assets/common/skillset/dungeon/tier-5/sceptre.ron b/assets/common/skillset/dungeon/tier-5/sceptre.ron new file mode 100644 index 0000000000..7900d41229 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/sceptre.ron @@ -0,0 +1,21 @@ +([ + Group(Weapon(Sceptre)), + + // Beam + Skill((Sceptre(LDamage), Some(1))), + Skill((Sceptre(LRange), Some(1))), + Skill((Sceptre(LLifesteal), Some(1))), + Skill((Sceptre(LRegen), Some(1))), + + // Heal + Skill((Sceptre(HHeal), Some(1))), + Skill((Sceptre(HCost), Some(1))), + Skill((Sceptre(HRange), Some(1))), + + // Ward + Skill((Sceptre(UnlockAura), None)), + Skill((Sceptre(AStrength), Some(1))), + Skill((Sceptre(ADuration), Some(1))), + Skill((Sceptre(ARange), Some(1))), + Skill((Sceptre(ACost), Some(1))), +]) diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index f8696a0b87..925924cc3e 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -17,7 +17,7 @@ use common::{ tool::{AbilitySpec, ToolKind}, Item, ItemDesc, ItemKind, }, - skills::{AxeSkill, BowSkill, HammerSkill, Skill, StaffSkill, SwordSkill}, + skills::{AxeSkill, BowSkill, HammerSkill, Skill, StaffSkill, SwordSkill, SceptreSkill}, Agent, Alignment, BehaviorCapability, BehaviorState, Body, CharacterAbility, CharacterState, ControlAction, ControlEvent, Controller, Energy, Health, HealthChange, InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori, PhysicsState, Pos, @@ -99,6 +99,7 @@ pub enum Tactic { Sword, Bow, Staff, + Sceptre, StoneGolem, CircleCharge { radius: u32, circle_time: u32 }, QuadLowRanged, @@ -1613,6 +1614,7 @@ impl<'a> AgentData<'a> { let tool_tactic = |tool_kind| match tool_kind { ToolKind::Bow => Tactic::Bow, ToolKind::Staff => Tactic::Staff, + ToolKind::Sceptre => Tactic::Sceptre, ToolKind::Hammer => Tactic::Hammer, ToolKind::Sword | ToolKind::Spear => Tactic::Sword, ToolKind::Axe => Tactic::Axe, @@ -1630,6 +1632,7 @@ impl<'a> AgentData<'a> { "Axe Simple" | "Sword Simple" => Tactic::Sword, "Staff Simple" => Tactic::Staff, "Bow Simple" => Tactic::Bow, + "Sceptre Simple" => Tactic::Sceptre, "Stone Golem" => Tactic::StoneGolem, "Quad Med Quick" => Tactic::CircleCharge { radius: 3, @@ -1815,6 +1818,9 @@ impl<'a> AgentData<'a> { Tactic::Staff => { self.handle_staff_attack(agent, controller, &attack_data, &tgt_data, &read_data) }, + Tactic::Sceptre => { + self.handle_sceptre_attack(agent, controller, &attack_data, &tgt_data, &read_data) + }, Tactic::StoneGolem => self.handle_stone_golem_attack( agent, controller, @@ -2431,6 +2437,130 @@ impl<'a> AgentData<'a> { } } + fn handle_sceptre_attack( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + ) { + const DESIRED_ENERGY_LEVEL: u32 = 500; + // Logic to use abilities + if attack_data.dist_sqrd > attack_data.min_attack_dist.powi(2) + && can_see_tgt( + &*read_data.terrain, + self.pos, + tgt_data.pos, + attack_data.dist_sqrd, + ) + { + // If far enough away, and can see target, attempt to shoot beam + if self.energy.current() < DESIRED_ENERGY_LEVEL { + // If low on energy, use primary to attempt to regen energy + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } else if self + .skill_set + .has_skill(Skill::Sceptre(SceptreSkill::UnlockAura)) + && self.energy.current() > 100 + && thread_rng().gen_bool(0.7) + { + // Use ward if target is far enough away and have sufficient energy + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + } + else { + // If at desired energy level but not able to ward, just attack + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } + } else if attack_data.dist_sqrd < (2.0 * attack_data.min_attack_dist).powi(2) { + if self.body.map(|b| b.is_humanoid()).unwrap_or(false) + && self.energy.current() > CharacterAbility::default_roll().get_energy_cost() + && !matches!(self.char_state, CharacterState::BasicAura(c) if !matches!(c.stage_section, StageSection::Recover)) + { + // Else roll away if can roll and have enough energy, and not using beam + // (other attacks have interrupt handled above) unless in recover + controller + .actions + .push(ControlAction::basic_input(InputKind::Roll)); + } else { + if attack_data.angle < 15.0 { + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } + } + } + // Logic to move. Intentionally kept separate from ability logic so duplicated + // work is less necessary. + if attack_data.dist_sqrd < (2.0 * attack_data.min_attack_dist).powi(2) { + // Attempt to move away from target if too close + if let Some((bearing, speed)) = agent.chaser.chase( + &*read_data.terrain, + self.pos.0, + self.vel.0, + tgt_data.pos.0, + TraversalConfig { + min_tgt_dist: 1.25, + ..self.traversal_config + }, + ) { + controller.inputs.move_dir = + -bearing.xy().try_normalized().unwrap_or_else(Vec2::zero) * speed; + } + } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) { + // Else attempt to circle target if neither too close nor too far + if let Some((bearing, speed)) = agent.chaser.chase( + &*read_data.terrain, + self.pos.0, + self.vel.0, + tgt_data.pos.0, + TraversalConfig { + min_tgt_dist: 1.25, + ..self.traversal_config + }, + ) { + if can_see_tgt( + &*read_data.terrain, + self.pos, + tgt_data.pos, + attack_data.dist_sqrd, + ) && attack_data.angle < 45.0 + { + controller.inputs.move_dir = bearing + .xy() + .rotated_z(thread_rng().gen_range(0.5..1.57)) + .try_normalized() + .unwrap_or_else(Vec2::zero) + * speed; + } else { + // Unless cannot see target, then move towards them + controller.inputs.move_dir = + bearing.xy().try_normalized().unwrap_or_else(Vec2::zero) * speed; + self.jump_if(controller, bearing.z > 1.5); + controller.inputs.move_z = bearing.z; + } + } + // Sometimes try to roll + if self.body.map(|b| b.is_humanoid()).unwrap_or(false) + && attack_data.dist_sqrd < 16.0f32.powi(2) + && thread_rng().gen::() < 0.01 + { + controller + .actions + .push(ControlAction::basic_input(InputKind::Roll)); + } + } else { + // If too far, move towards target + self.path_toward_target(agent, controller, tgt_data, read_data, false, None); + } + } + fn handle_stone_golem_attack( &self, agent: &mut Agent,