mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
dagon_anticheese
This commit is contained in:
parent
601712cf77
commit
5c575484f2
@ -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"): (
|
||||||
|
7
assets/common/abilities/custom/cursekeeper/transform.ron
Normal file
7
assets/common/abilities/custom/cursekeeper/transform.ron
Normal 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,
|
||||||
|
)
|
@ -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,
|
|
||||||
)
|
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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)),
|
||||||
|
@ -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: [],
|
||||||
|
)
|
@ -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,
|
||||||
|
@ -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),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user