dagon_anticheese

This commit is contained in:
flo 2024-05-28 19:13:49 +00:00
parent 601712cf77
commit 5c575484f2
13 changed files with 180 additions and 70 deletions

View File

@ -1125,8 +1125,8 @@
], ],
), ),
Custom("CursekeeperFake"): ( Custom("CursekeeperFake"): (
primary: Simple(None, "common.abilities.custom.cursekeeper.summonshamanicspirit"), primary: Simple(None, "common.abilities.custom.cursekeeper.transform"),
secondary: Simple(None, "common.abilities.custom.cursekeeper.unlive"), secondary: Simple(None, "common.abilities.custom.cursekeeper.transform"),
abilities: [], abilities: [],
), ),
Custom("ShamanicSpirit"): ( Custom("ShamanicSpirit"): (

View File

@ -0,0 +1,7 @@
Transform(
buildup_duration: 0.3,
recover_duration: 0.5,
target: "common.entity.dungeon.terracotta.shamanic_spirit_key",
specifier: Some(Cursekeeper),
allow_players: false,
)

View File

@ -1,9 +0,0 @@
SelfBuff(
buildup_duration: 0.1,
cast_duration: 0.1,
recover_duration: 0.1,
buff_kind: Burning,
buff_strength: 2000.0,
buff_duration: Some(60.0),
energy_cost: 0,
)

View File

@ -2,7 +2,7 @@ BasicRanged(
energy_cost: 0, energy_cost: 0,
buildup_duration: 1.0, buildup_duration: 1.0,
recover_duration: 1.5, recover_duration: 1.5,
projectile: FireDroplet( projectile: DemolisherBomb(
damage: 30.0, damage: 30.0,
radius: 10.0, radius: 10.0,
min_falloff: 0.5, min_falloff: 0.5,

View File

@ -2,7 +2,7 @@ BasicRanged(
energy_cost: 0, energy_cost: 0,
buildup_duration: 1.0, buildup_duration: 1.0,
recover_duration: 1.5, recover_duration: 1.5,
projectile: FireDroplet( projectile: DemolisherBomb(
damage: 30.0, damage: 30.0,
radius: 10.0, radius: 10.0,
min_falloff: 0.5, min_falloff: 0.5,

View File

@ -3,7 +3,7 @@
name: Name("Cursekeeper"), name: Name("Cursekeeper"),
body: RandomWith("cursekeeper"), body: RandomWith("cursekeeper"),
alignment: Alignment(Enemy), alignment: Alignment(Enemy),
loot: Item("common.items.keys.terracotta_key_chest"), loot: Nothing,
inventory: ( inventory: (
loadout: Inline(( loadout: Inline((
active_hands: InHands((Item("common.items.npc_weapons.unique.cursekeeper_sceptre_fake"), None)), active_hands: InHands((Item("common.items.npc_weapons.unique.cursekeeper_sceptre_fake"), None)),

View File

@ -0,0 +1,14 @@
#![enable(implicit_some)]
(
name: Name("Shamanic Spirit"),
body: RandomWith("shamanic_spirit"),
alignment: Alignment(Enemy),
loot: Item("common.items.keys.terracotta_key_chest"),
inventory: (
loadout: Inline((
inherit: Asset("common.loadout.dungeon.terracotta.shamanic_spirit"),
active_hands: InHands((Item("common.items.npc_weapons.unique.shamanic_spirit"), None)),
)),
),
meta: [],
)

View File

@ -66,6 +66,13 @@ pub enum ProjectileConstructor {
min_falloff: f32, min_falloff: f32,
reagent: Option<Reagent>, reagent: Option<Reagent>,
}, },
DemolisherBomb {
damage: f32,
radius: f32,
energy_regen: f32,
min_falloff: f32,
reagent: Option<Reagent>,
},
Fireball { Fireball {
damage: f32, damage: f32,
radius: f32, radius: f32,
@ -316,6 +323,56 @@ impl ProjectileConstructor {
is_point: true, is_point: true,
} }
}, },
DemolisherBomb {
damage,
radius,
energy_regen,
min_falloff,
reagent,
} => {
let energy = AttackEffect::new(None, CombatEffect::EnergyReward(energy_regen))
.with_requirement(CombatRequirement::AnyDamage);
let buff = CombatEffect::Buff(CombatBuff {
kind: BuffKind::Burning,
dur_secs: 4.0,
strength: CombatBuffStrength::DamageFraction(1.0),
chance: 0.6,
})
.adjusted_by_stats(tool_stats);
let damage = AttackDamage::new(
Damage {
source: DamageSource::Explosion,
kind: DamageKind::Energy,
value: damage,
},
Some(GroupTarget::OutOfGroup),
instance,
)
.with_effect(buff);
let attack = Attack::default()
.with_damage(damage)
.with_precision(precision_mult)
.with_effect(energy)
.with_combo_increment();
let explosion = Explosion {
effects: vec![
RadiusEffect::Attack(attack),
RadiusEffect::TerrainDestruction(2.0, Rgb::black()),
],
radius,
reagent,
min_falloff,
};
Projectile {
hit_solid: vec![Effect::Explode(explosion.clone()), Effect::Vanish],
hit_entity: vec![Effect::Explode(explosion), Effect::Vanish],
time_left: Duration::from_secs(10),
owner,
ignore_group: true,
is_sticky: true,
is_point: true,
}
},
Fireball { Fireball {
damage, damage,
radius, radius,
@ -1037,6 +1094,16 @@ impl ProjectileConstructor {
*energy_regen *= regen; *energy_regen *= regen;
*radius *= range; *radius *= range;
}, },
DemolisherBomb {
ref mut damage,
ref mut energy_regen,
ref mut radius,
..
} => {
*damage *= power;
*energy_regen *= regen;
*radius *= range;
},
Fireball { Fireball {
ref mut damage, ref mut damage,
ref mut energy_regen, ref mut energy_regen,
@ -1160,6 +1227,7 @@ impl ProjectileConstructor {
Arrow { .. } => false, Arrow { .. } => false,
Knife { .. } => false, Knife { .. } => false,
FireDroplet { .. } => true, FireDroplet { .. } => true,
DemolisherBomb { .. } => true,
Fireball { .. } => true, Fireball { .. } => true,
Frostball { .. } => true, Frostball { .. } => true,
Poisonball { .. } => true, Poisonball { .. } => true,

View File

@ -21,6 +21,7 @@ use super::{
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum FrontendSpecifier { pub enum FrontendSpecifier {
Evolve, Evolve,
Cursekeeper,
} }
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -94,6 +95,17 @@ impl CharacterBehavior for Data {
}, },
)) ))
}, },
FrontendSpecifier::Cursekeeper => {
output_events.emit_local(crate::event::LocalEvent::CreateOutcome(
crate::outcome::Outcome::Explosion {
pos: data.pos.0,
power: 5.0,
radius: 2.0,
is_attack: false,
reagent: Some(Reagent::Purple),
},
))
},
} }
} }

View File

@ -1647,7 +1647,7 @@ impl<'a> AgentData<'a> {
rng, rng,
), ),
Tactic::CursekeeperFake => { Tactic::CursekeeperFake => {
self.handle_cursekeeper_fake_attack(agent, controller, &attack_data) self.handle_cursekeeper_fake_attack(controller, &attack_data)
}, },
Tactic::ShamanicSpirit => self.handle_shamanic_spirit_attack( Tactic::ShamanicSpirit => self.handle_shamanic_spirit_attack(
agent, agent,

View File

@ -5370,18 +5370,6 @@ impl<'a> AgentData<'a> {
read_data: &ReadData, read_data: &ReadData,
rng: &mut impl Rng, rng: &mut impl Rng,
) { ) {
let line_of_sight_with_target = || {
entities_have_line_of_sight(
self.pos,
self.body,
self.scale,
tgt_data.pos,
tgt_data.body,
tgt_data.scale,
read_data,
)
};
enum ActionStateTimers { enum ActionStateTimers {
TimerBeam, TimerBeam,
TimerSummon, TimerSummon,
@ -5412,7 +5400,6 @@ impl<'a> AgentData<'a> {
agent.combat_state.timers[ActionStateTimers::TimerSummon as usize] += read_data.dt.0; agent.combat_state.timers[ActionStateTimers::TimerSummon as usize] += read_data.dt.0;
} }
if line_of_sight_with_target() {
if agent.combat_state.timers[ActionStateTimers::TimerSummon as usize] > 32.0 { if agent.combat_state.timers[ActionStateTimers::TimerSummon as usize] > 32.0 {
match agent.combat_state.timers[ActionStateTimers::SelectSummon as usize] as i32 { match agent.combat_state.timers[ActionStateTimers::SelectSummon as usize] as i32 {
0 => controller.push_basic_input(InputKind::Ability(0)), 0 => controller.push_basic_input(InputKind::Ability(0)),
@ -5428,7 +5415,7 @@ impl<'a> AgentData<'a> {
} else { } else {
controller.push_basic_input(InputKind::Secondary); controller.push_basic_input(InputKind::Secondary);
} }
}
if attack_data.dist_sqrd > 10_f32.powi(2) { if attack_data.dist_sqrd > 10_f32.powi(2) {
self.path_toward_target( self.path_toward_target(
agent, agent,
@ -5476,23 +5463,11 @@ impl<'a> AgentData<'a> {
pub fn handle_cursekeeper_fake_attack( pub fn handle_cursekeeper_fake_attack(
&self, &self,
agent: &mut Agent,
controller: &mut Controller, controller: &mut Controller,
attack_data: &AttackData, attack_data: &AttackData,
) { ) {
enum Conditions {
AttackToggle = 0,
}
if attack_data.dist_sqrd < 25_f32.powi(2) { if attack_data.dist_sqrd < 25_f32.powi(2) {
if !agent.combat_state.conditions[Conditions::AttackToggle as usize] {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
if matches!(self.char_state, CharacterState::BasicSummon(c) if matches!(c.stage_section, StageSection::Recover))
{
agent.combat_state.conditions[Conditions::AttackToggle as usize] = true;
}
} else {
controller.push_basic_input(InputKind::Secondary);
}
} }
} }
@ -5507,11 +5482,38 @@ impl<'a> AgentData<'a> {
enum ActionStateTimers { enum ActionStateTimers {
TimerDagon = 0, TimerDagon = 0,
} }
let line_of_sight_with_target = || {
entities_have_line_of_sight(
self.pos,
self.body,
self.scale,
tgt_data.pos,
tgt_data.body,
tgt_data.scale,
read_data,
)
};
// when cheesed from behind the entry, change position to retarget
let home = agent.patrol_origin.unwrap_or(self.pos.0);
let exit = Vec3::new(home.x - 6.0, home.y - 6.0, home.z);
let (station_0, station_1) = (exit + 12.0, exit - 12.0);
if agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] > 2.5 { if agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] > 2.5 {
agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] = 0.0; agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] = 0.0;
} }
if !line_of_sight_with_target()
&& (tgt_data.pos.0 - exit).xy().magnitude_squared() < (10.0_f32).powi(2)
{
let station = if (tgt_data.pos.0 - station_0).xy().magnitude_squared()
< (tgt_data.pos.0 - station_1).xy().magnitude_squared()
{
station_0
} else {
station_1
};
self.path_toward_target(agent, controller, station, read_data, Path::Full, None);
}
// if target gets very close, shoot dagon bombs and lay out sea urchins // if target gets very close, shoot dagon bombs and lay out sea urchins
if attack_data.dist_sqrd < (2.0 * attack_data.min_attack_dist).powi(2) { else if attack_data.dist_sqrd < (2.0 * attack_data.min_attack_dist).powi(2) {
if agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] > 1.0 { if agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] > 1.0 {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] += read_data.dt.0; agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] += read_data.dt.0;
@ -5536,15 +5538,7 @@ impl<'a> AgentData<'a> {
controller.push_basic_input(InputKind::Ability(2)); controller.push_basic_input(InputKind::Ability(2));
} }
agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] += read_data.dt.0; agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] += read_data.dt.0;
} else if entities_have_line_of_sight( } else if line_of_sight_with_target() {
self.pos,
self.body,
self.scale,
tgt_data.pos,
tgt_data.body,
tgt_data.scale,
read_data,
) {
// if enemy in mid range shoot dagon bombs and steamwave // if enemy in mid range shoot dagon bombs and steamwave
if agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] > 1.0 { if agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] > 1.0 {
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);

View File

@ -1501,6 +1501,30 @@ impl ParticleMgr {
}, },
) )
}, },
states::transform::FrontendSpecifier::Cursekeeper => {
self.particles.resize_with(
self.particles.len()
+ usize::from(
self.scheduler.heartbeats(Duration::from_millis(10)),
),
|| {
let start_pos = interpolated.pos
+ (Vec2::unit_y()
* rng.gen::<f32>()
* body.max_radius())
.rotated_z(rng.gen_range(0.0..(PI * 2.0)))
.with_z(body.height() * rng.gen::<f32>());
Particle::new_directed(
Duration::from_millis(100),
time,
ParticleMode::FireworkPurple,
start_pos,
start_pos + Vec3::unit_z() * 2.0,
)
},
)
},
} }
} }
}, },

View File

@ -520,15 +520,15 @@ impl Structure for TerracottaPalace {
.fill(clay_unbroken.clone()); .fill(clay_unbroken.clone());
painter painter
.cylinder(Aabb { .cylinder(Aabb {
min: (center - (room_size / 4)).with_z(base + (3 * (room_size / 10)) - 1), min: (center - (room_size / 4) + 1).with_z(base + (3 * (room_size / 10)) - 1),
max: (center + (room_size / 4)).with_z(base + (3 * (room_size / 10)) + 2), max: (center + (room_size / 4) - 1).with_z(base + (3 * (room_size / 10)) + 2),
}) })
.clear(); .clear();
// center podium with spikes // center podium with spikes
painter painter
.cylinder(Aabb { .cylinder(Aabb {
min: (center - (room_size / 4)).with_z(base + (3 * (room_size / 10)) + 1), min: (center - (room_size / 4) + 1).with_z(base + (3 * (room_size / 10)) + 1),
max: (center + (room_size / 4)).with_z(base + (3 * (room_size / 10)) + 2), max: (center + (room_size / 4) - 1).with_z(base + (3 * (room_size / 10)) + 2),
}) })
.fill(Fill::Block(Block::air(SpriteKind::IronSpike))); .fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
painter painter