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"): (
|
||||
primary: Simple(None, "common.abilities.custom.cursekeeper.summonshamanicspirit"),
|
||||
secondary: Simple(None, "common.abilities.custom.cursekeeper.unlive"),
|
||||
primary: Simple(None, "common.abilities.custom.cursekeeper.transform"),
|
||||
secondary: Simple(None, "common.abilities.custom.cursekeeper.transform"),
|
||||
abilities: [],
|
||||
),
|
||||
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,
|
||||
buildup_duration: 1.0,
|
||||
recover_duration: 1.5,
|
||||
projectile: FireDroplet(
|
||||
projectile: DemolisherBomb(
|
||||
damage: 30.0,
|
||||
radius: 10.0,
|
||||
min_falloff: 0.5,
|
||||
|
@ -2,7 +2,7 @@ BasicRanged(
|
||||
energy_cost: 0,
|
||||
buildup_duration: 1.0,
|
||||
recover_duration: 1.5,
|
||||
projectile: FireDroplet(
|
||||
projectile: DemolisherBomb(
|
||||
damage: 30.0,
|
||||
radius: 10.0,
|
||||
min_falloff: 0.5,
|
||||
|
@ -3,10 +3,10 @@
|
||||
name: Name("Cursekeeper"),
|
||||
body: RandomWith("cursekeeper"),
|
||||
alignment: Alignment(Enemy),
|
||||
loot: Item("common.items.keys.terracotta_key_chest"),
|
||||
loot: Nothing,
|
||||
inventory: (
|
||||
loadout: Inline((
|
||||
active_hands: InHands((Item("common.items.npc_weapons.unique.cursekeeper_sceptre_fake"), None)),
|
||||
)), ),
|
||||
meta: [],
|
||||
)
|
||||
)
|
||||
|
@ -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,
|
||||
reagent: Option<Reagent>,
|
||||
},
|
||||
DemolisherBomb {
|
||||
damage: f32,
|
||||
radius: f32,
|
||||
energy_regen: f32,
|
||||
min_falloff: f32,
|
||||
reagent: Option<Reagent>,
|
||||
},
|
||||
Fireball {
|
||||
damage: f32,
|
||||
radius: f32,
|
||||
@ -316,6 +323,56 @@ impl ProjectileConstructor {
|
||||
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 {
|
||||
damage,
|
||||
radius,
|
||||
@ -1037,6 +1094,16 @@ impl ProjectileConstructor {
|
||||
*energy_regen *= regen;
|
||||
*radius *= range;
|
||||
},
|
||||
DemolisherBomb {
|
||||
ref mut damage,
|
||||
ref mut energy_regen,
|
||||
ref mut radius,
|
||||
..
|
||||
} => {
|
||||
*damage *= power;
|
||||
*energy_regen *= regen;
|
||||
*radius *= range;
|
||||
},
|
||||
Fireball {
|
||||
ref mut damage,
|
||||
ref mut energy_regen,
|
||||
@ -1160,6 +1227,7 @@ impl ProjectileConstructor {
|
||||
Arrow { .. } => false,
|
||||
Knife { .. } => false,
|
||||
FireDroplet { .. } => true,
|
||||
DemolisherBomb { .. } => true,
|
||||
Fireball { .. } => true,
|
||||
Frostball { .. } => true,
|
||||
Poisonball { .. } => true,
|
||||
|
@ -21,6 +21,7 @@ use super::{
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum FrontendSpecifier {
|
||||
Evolve,
|
||||
Cursekeeper,
|
||||
}
|
||||
|
||||
#[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,
|
||||
),
|
||||
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(
|
||||
agent,
|
||||
|
@ -5370,18 +5370,6 @@ impl<'a> AgentData<'a> {
|
||||
read_data: &ReadData,
|
||||
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 {
|
||||
TimerBeam,
|
||||
TimerSummon,
|
||||
@ -5412,23 +5400,22 @@ impl<'a> AgentData<'a> {
|
||||
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 {
|
||||
match agent.combat_state.timers[ActionStateTimers::SelectSummon as usize] as i32 {
|
||||
0 => controller.push_basic_input(InputKind::Ability(0)),
|
||||
1 => controller.push_basic_input(InputKind::Ability(1)),
|
||||
2 => controller.push_basic_input(InputKind::Ability(2)),
|
||||
3 => controller.push_basic_input(InputKind::Ability(3)),
|
||||
_ => controller.push_basic_input(InputKind::Ability(4)),
|
||||
}
|
||||
} else if agent.combat_state.timers[ActionStateTimers::TimerBeam as usize] < 6.0 {
|
||||
controller.push_basic_input(InputKind::Ability(6));
|
||||
} else if agent.combat_state.timers[ActionStateTimers::TimerBeam as usize] < 9.0 {
|
||||
controller.push_basic_input(InputKind::Primary);
|
||||
} else {
|
||||
controller.push_basic_input(InputKind::Secondary);
|
||||
if agent.combat_state.timers[ActionStateTimers::TimerSummon as usize] > 32.0 {
|
||||
match agent.combat_state.timers[ActionStateTimers::SelectSummon as usize] as i32 {
|
||||
0 => controller.push_basic_input(InputKind::Ability(0)),
|
||||
1 => controller.push_basic_input(InputKind::Ability(1)),
|
||||
2 => controller.push_basic_input(InputKind::Ability(2)),
|
||||
3 => controller.push_basic_input(InputKind::Ability(3)),
|
||||
_ => controller.push_basic_input(InputKind::Ability(4)),
|
||||
}
|
||||
} else if agent.combat_state.timers[ActionStateTimers::TimerBeam as usize] < 6.0 {
|
||||
controller.push_basic_input(InputKind::Ability(6));
|
||||
} else if agent.combat_state.timers[ActionStateTimers::TimerBeam as usize] < 9.0 {
|
||||
controller.push_basic_input(InputKind::Primary);
|
||||
} else {
|
||||
controller.push_basic_input(InputKind::Secondary);
|
||||
}
|
||||
|
||||
if attack_data.dist_sqrd > 10_f32.powi(2) {
|
||||
self.path_toward_target(
|
||||
agent,
|
||||
@ -5476,23 +5463,11 @@ impl<'a> AgentData<'a> {
|
||||
|
||||
pub fn handle_cursekeeper_fake_attack(
|
||||
&self,
|
||||
agent: &mut Agent,
|
||||
controller: &mut Controller,
|
||||
attack_data: &AttackData,
|
||||
) {
|
||||
enum Conditions {
|
||||
AttackToggle = 0,
|
||||
}
|
||||
if attack_data.dist_sqrd < 25_f32.powi(2) {
|
||||
if !agent.combat_state.conditions[Conditions::AttackToggle as usize] {
|
||||
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);
|
||||
}
|
||||
controller.push_basic_input(InputKind::Primary);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5507,11 +5482,38 @@ impl<'a> AgentData<'a> {
|
||||
enum ActionStateTimers {
|
||||
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 {
|
||||
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 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 {
|
||||
controller.push_basic_input(InputKind::Primary);
|
||||
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));
|
||||
}
|
||||
agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] += read_data.dt.0;
|
||||
} else if entities_have_line_of_sight(
|
||||
self.pos,
|
||||
self.body,
|
||||
self.scale,
|
||||
tgt_data.pos,
|
||||
tgt_data.body,
|
||||
tgt_data.scale,
|
||||
read_data,
|
||||
) {
|
||||
} else if line_of_sight_with_target() {
|
||||
// if enemy in mid range shoot dagon bombs and steamwave
|
||||
if agent.combat_state.timers[ActionStateTimers::TimerDagon as usize] > 1.0 {
|
||||
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());
|
||||
painter
|
||||
.cylinder(Aabb {
|
||||
min: (center - (room_size / 4)).with_z(base + (3 * (room_size / 10)) - 1),
|
||||
max: (center + (room_size / 4)).with_z(base + (3 * (room_size / 10)) + 2),
|
||||
min: (center - (room_size / 4) + 1).with_z(base + (3 * (room_size / 10)) - 1),
|
||||
max: (center + (room_size / 4) - 1).with_z(base + (3 * (room_size / 10)) + 2),
|
||||
})
|
||||
.clear();
|
||||
// center podium with spikes
|
||||
painter
|
||||
.cylinder(Aabb {
|
||||
min: (center - (room_size / 4)).with_z(base + (3 * (room_size / 10)) + 1),
|
||||
max: (center + (room_size / 4)).with_z(base + (3 * (room_size / 10)) + 2),
|
||||
min: (center - (room_size / 4) + 1).with_z(base + (3 * (room_size / 10)) + 1),
|
||||
max: (center + (room_size / 4) - 1).with_z(base + (3 * (room_size / 10)) + 2),
|
||||
})
|
||||
.fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
|
||||
painter
|
||||
|
Loading…
Reference in New Issue
Block a user