broad updates to gnarling forts

This commit is contained in:
horblegorble 2024-05-30 17:17:50 +10:00
parent 3ece56eb4a
commit 67c79a5759
30 changed files with 286 additions and 130 deletions

View File

@ -771,8 +771,9 @@
primary: Simple(None, "common.abilities.custom.harvester.scythe"), primary: Simple(None, "common.abilities.custom.harvester.scythe"),
secondary: Simple(None, "common.abilities.custom.harvester.firebreath"), secondary: Simple(None, "common.abilities.custom.harvester.firebreath"),
abilities: [ abilities: [
Simple(None, "common.abilities.custom.harvester.ensnaringvines"),
Simple(None, "common.abilities.custom.harvester.explodingpumpkin"), Simple(None, "common.abilities.custom.harvester.explodingpumpkin"),
Simple(None, "common.abilities.custom.harvester.ensnaringvines_0"),
Simple(None, "common.abilities.custom.harvester.ensnaringvines_1"),
], ],
), ),
// TODO: Allow ability sets to expand other ability sets // TODO: Allow ability sets to expand other ability sets

View File

@ -4,7 +4,7 @@ SpriteSummon(
recover_duration: 0.3, recover_duration: 0.3,
sprite: EnsnaringVines, sprite: EnsnaringVines,
del_timeout: None, del_timeout: None,
summon_distance: (0, 25), summon_distance: (0, 30),
sparseness: 0.67, sparseness: 0.8,
angle: 360, angle: 360,
) )

View File

@ -0,0 +1,10 @@
SpriteSummon(
buildup_duration: 0.9,
cast_duration: 0.6,
recover_duration: 0.5,
sprite: EnsnaringVines,
del_timeout: None,
summon_distance: (0, 25),
sparseness: 0.6,
angle: 360,
)

View File

@ -1,26 +1,25 @@
BasicRanged( BasicRanged(
energy_cost: 0, energy_cost: 0,
buildup_duration: 0.75, buildup_duration: 1.0,
recover_duration: 1.6, recover_duration: 1.6,
projectile: ( projectile: (
kind: Explosive( kind: Explosive(
radius: 5, radius: 7.5,
min_falloff: 0.6, min_falloff: 0.6,
reagent: Some(Red), reagent: Some(Red),
terrain: Some((5, Black)), terrain: Some((5, Black)),
), ),
attack: Some(( attack: Some((
damage: 37.5, damage: 20,
knockback: Some(25), knockback: Some(22),
energy: 0, energy: 0,
buff: Some(( buff: Some((
kind: Burning, kind: Burning,
dur_secs: 5, dur_secs: 4,
strength: DamageFraction(0.2), strength: DamageFraction(0.2),
chance: 1.0, chance: 1.0,
)), )),
)), )),
),
projectile_body: Object(Pumpkin), projectile_body: Object(Pumpkin),
projectile_light: None, projectile_light: None,
projectile_speed: 30.0, projectile_speed: 30.0,

View File

@ -2,14 +2,14 @@ BasicBeam(
buildup_duration: 1.4, buildup_duration: 1.4,
recover_duration: 0.9, recover_duration: 0.9,
beam_duration: 1.0, beam_duration: 1.0,
damage: 9.0, damage: 5.0,
tick_rate: 1.5, tick_rate: 1.5,
range: 20.0, range: 20.0,
max_angle: 15.0, max_angle: 15.0,
damage_effect: Some(Buff(( damage_effect: Some(Buff((
kind: Burning, kind: Burning,
dur_secs: 10.0, dur_secs: 3.0,
strength: DamageFraction(1.0), strength: DamageFraction(0.8),
chance: 1.0, chance: 1.0,
))), ))),
energy_regen: 0, energy_regen: 0,

View File

@ -1,12 +1,12 @@
BasicMelee( BasicMelee(
energy_cost: 0, energy_cost: 0,
buildup_duration: 0.9, buildup_duration: 1.0,
swing_duration: 0.1, swing_duration: 0.2,
hit_timing: 0.5, hit_timing: 0.5,
recover_duration: 1.2, recover_duration: 1.0,
melee_constructor: ( melee_constructor: (
kind: Slash( kind: Slash(
damage: 21.0, damage: 13.0,
poise: 10.0, poise: 10.0,
knockback: 10.0, knockback: 10.0,
energy_regen: 0.0, energy_regen: 0.0,

View File

@ -7,7 +7,7 @@ BasicMelee(
melee_constructor: ( melee_constructor: (
kind: SonicWave( kind: SonicWave(
damage: 10, damage: 10,
poise: 100, poise: 35,
knockback: 20, knockback: 20,
energy_regen: 0, energy_regen: 0,
), ),

View File

@ -4,25 +4,26 @@ BasicRanged(
recover_duration: 0.6, recover_duration: 0.6,
projectile: ( projectile: (
kind: Explosive( kind: Explosive(
radius: 2, radius: 3,
min_falloff: 0.5, min_falloff: 0.7,
reagent: Some(Red), reagent: Some(Red),
terrain: Some((2, Black)) terrain: Some((2, Black))
), ),
attack: Some(( attack: Some((
damage: 13.5, damage: 5.5,
energy: 10, energy: 10,
buff: Some(( buff: Some((
kind: Burning, kind: Burning,
dur_secs: 5, dur_secs: 3,
strength: DamageFraction(0.1), strength: DamageFraction(0.3),
chance: 0.1, chance: 0.1,
)), )),
)), )),
), ),
projectile_body: Object(BoltFire), projectile_body: Object(BoltFire),
projectile_speed: 25, projectile_speed: 25,
num_projectiles: 8, num_projectiles: 5,
projectile_spread: 0.125, projectile_spread: 0.125,
move_efficiency: 0.3, move_efficiency: 0.3,
) )

View File

@ -1,14 +1,14 @@
Shockwave( Shockwave(
energy_cost: 0, energy_cost: 0,
buildup_duration: 0.975, buildup_duration: 1.2,
swing_duration: 0.1, swing_duration: 0.1,
recover_duration: 0.6, recover_duration: 0.8,
damage: 30, damage: 16,
poise_damage: 0, poise_damage: 0,
knockback: ( strength: 25, direction: Away), knockback: ( strength: 15, direction: Away),
shockwave_angle: 360, shockwave_angle: 360,
shockwave_vertical_angle: 90, shockwave_vertical_angle: 90,
shockwave_speed: 10, shockwave_speed: 12,
shockwave_duration: 1, shockwave_duration: 1,
dodgeable: Roll, dodgeable: Roll,
move_efficiency: 0, move_efficiency: 0,

View File

@ -1,21 +1,21 @@
BasicMelee( BasicMelee(
energy_cost: 0, energy_cost: 0,
buildup_duration: 0.6, buildup_duration: 0.7,
swing_duration: 0.1, swing_duration: 0.35,
hit_timing: 0.5, hit_timing: 0.5,
recover_duration: 0.45, recover_duration: 0.6,
melee_constructor: ( melee_constructor: (
kind: Bash( kind: Bash(
damage: 12, damage: 9,
poise: 10, poise: 10,
knockback: 0, knockback: 0,
energy_regen: 0, energy_regen: 0,
), ),
range: 7.5, range: 4.0,
angle: 60.0, angle: 60.0,
damage_effect: Some(Buff(( damage_effect: Some(Buff((
kind: Burning, kind: Burning,
dur_secs: 10.0, dur_secs: 4.0,
strength: DamageFraction(0.5), strength: DamageFraction(0.5),
chance: 0.5, chance: 0.5,
))), ))),

View File

@ -1,24 +1,24 @@
BasicAura( BasicAura(
buildup_duration: 0.375, buildup_duration: 0.2,
cast_duration: 0.5, cast_duration: 0.4,
recover_duration: 0.375, recover_duration: 0.2,
targets: InGroup, targets: InGroup,
auras: [ auras: [
( (
kind: Regeneration, kind: Regeneration,
strength: 7.5, strength: 1,
duration: Some(5), duration: Some(1),
category: Magical, category: Magical,
), ),
( (
kind: ProtectingWard, kind: ProtectingWard,
strength: 0.75, strength: 1,
duration: Some(5), duration: Some(1),
category: Magical, category: Magical,
), ),
], ],
aura_duration: Some(2), aura_duration: Some(1),
range: 50, range: 15,
energy_cost: 0, energy_cost: 0,
scales_with_combo: false, scales_with_combo: false,
) )

View File

@ -1,18 +1,18 @@
BasicAura( BasicAura(
buildup_duration: 0.375, buildup_duration: 0.2,
cast_duration: 0.5, cast_duration: 0.4,
recover_duration: 0.375, recover_duration: 0.2,
targets: OutOfGroup, targets: OutOfGroup,
auras: [ auras: [
( (
kind: Burning, kind: Burning,
strength: 0.75, strength: 1,
duration: Some(5), duration: Some(1),
category: Magical, category: Magical,
), ),
], ],
aura_duration: Some(2), aura_duration: Some(1),
range: 50, range: 15,
energy_cost: 0, energy_cost: 0,
scales_with_combo: false, scales_with_combo: false,
) )

View File

@ -1,18 +1,18 @@
BasicAura( BasicAura(
buildup_duration: 0.375, buildup_duration: 0.2,
cast_duration: 0.5, cast_duration: 0.4,
recover_duration: 0.375, recover_duration: 0.2,
targets: InGroup, targets: InGroup,
auras: [ auras: [
( (
kind: Hastened, kind: Hastened,
strength: 0.75, strength: 0.4,
duration: Some(5), duration: Some(1),
category: Magical, category: Magical,
), ),
], ],
aura_duration: Some(2), aura_duration: Some(1),
range: 50, range: 15,
energy_cost: 0, energy_cost: 0,
scales_with_combo: false, scales_with_combo: false,
) )

View File

@ -2,7 +2,7 @@
#![enable(implicit_some)] #![enable(implicit_some)]
( (
name: Name("Gnarling Chieftain"), name: Name("Gnarling Chieftain"),
body: RandomWith("gnarling"), body: RandomWith("gnarling_chieftain"),
alignment: Alignment(Enemy), alignment: Alignment(Enemy),
loot: LootTable("common.loot_tables.dungeon.gnarling.chieftain"), loot: LootTable("common.loot_tables.dungeon.gnarling.chieftain"),
inventory: ( inventory: (

View File

@ -8,6 +8,6 @@ ItemDef(
flicker_thousandths: 600, flicker_thousandths: 600,
), ),
), ),
quality: High, quality: Moderate,
tags: [Utility], tags: [Utility],
) )

View File

@ -1,12 +1,18 @@
[ [
// Weapons (1, All([
(5.0, LootTable("common.loot_tables.weapons.tier-1")), MultiDrop(Item("common.items.mineral.ore.veloritefrag"), 4, 6),
// Armor Lottery([
(5.0, LootTable("common.loot_tables.armor.tier-1")), (1.0, LootTable("common.loot_tables.armor.tier-1")),
// Misc (1.0, LootTable("common.loot_tables.weapons.tier-1")),
]),
Lottery([
(1.0, LootTable("common.loot_tables.armor.tier-0")),
(1.0, LootTable("common.loot_tables.weapons.tier-0")),
]),
Lottery([
(3.0, Item("common.items.armor.misc.neck.scratched")), (3.0, Item("common.items.armor.misc.neck.scratched")),
(2.0, Item("common.items.armor.misc.head.wanderers_hat")), (1.0, Item("common.items.armor.misc.head.wanderers_hat")),
(3.0, Item("common.items.armor.misc.head.bamboo_twig")),
// Chieftain Mask
(1.0, Item("common.items.armor.misc.head.gnarling_mask")), (1.0, Item("common.items.armor.misc.head.gnarling_mask")),
])
]))
] ]

View File

@ -1,10 +1,18 @@
[ [
// Weapons (1, All([
(5.0, LootTable("common.loot_tables.weapons.tier-3")), MultiDrop(Item("common.items.mineral.ore.velorite"), 2, 3),
// Armor Lottery([
(5.0, LootTable("common.loot_tables.armor.tier-3")), (1.0, LootTable("common.loot_tables.armor.tier-2")),
// Misc (1.0, LootTable("common.loot_tables.weapons.tier-2")),
(3.0, Item("common.items.armor.misc.neck.scratched")), ]),
(2.0, Item("common.items.lantern.pumpkin")), Lottery([
(1.0, LootTable("common.loot_tables.armor.tier-1")),
(1.0, LootTable("common.loot_tables.weapons.tier-1")),
])
Lottery([
(3.0, Nothing),
(1.0, Item("common.items.lantern.pumpkin")),
(1.0, Item("common.items.glider.moth")), (1.0, Item("common.items.glider.moth")),
])
]))
] ]

View File

@ -1,5 +1,5 @@
[ [
(1.0, LootTable("common.loot_tables.weapons.components.tier-1")), (1.0, LootTable("common.loot_tables.weapons.components.tier-0")),
(1.0, LootTable("common.loot_tables.armor.cloth")), (1.0, LootTable("common.loot_tables.armor.cloth")),
(0.5, Item("common.items.recipes.explosives")), (0.5, Item("common.items.recipes.explosives")),
] ]

View File

@ -1,7 +1,16 @@
[ [
(1.0, LootTable("common.loot_tables.weapons.components.tier-0")), (0.5, LootTable("common.loot_tables.weapons.components.tier-0")),
(1.0, LootTable("common.loot_tables.weapons.components.tier-1")), (0.25, LootTable("common.loot_tables.weapons.tier-0")),
(1.0, LootTable("common.loot_tables.armor.cloth")), (0.25, LootTable("common.loot_tables.armor.tier-0")),
(0.25, Item("common.items.armor.misc.head.bamboo_twig")),
// Currency
(3.0, MultiDrop(Item("common.items.utility.coins"), 25, 50)),
// Materials
(0.5, MultiDrop(Item("common.items.mineral.ore.veloritefrag"), 5, 10)),
// Consumables
(2.0, MultiDrop(Item("common.items.consumable.potion_minor"), 2, 5)),
// Food
(1.0, MultiDrop(LootTable("common.loot_tables.food.prepared"), 1, 2)),
(0.2, Item("common.items.recipes.explosives")), (0.2, Item("common.items.recipes.explosives")),
(0.5, Item("common.items.recipes.instruments")), (0.5, Item("common.items.recipes.instruments")),
(0.2, Item("common.items.recipes.charms")), (0.2, Item("common.items.recipes.charms")),

View File

@ -1233,6 +1233,10 @@
keyword: "treasure_egg", keyword: "treasure_egg",
generic: "Treasure Egg", generic: "Treasure Egg",
), ),
gnarling_chieftain: (
keyword: "gnarling_chieftain",
generic: "Gnarling Chieftain"
),
) )
), ),
fish_small: ( fish_small: (

View File

@ -379,7 +379,8 @@ impl<'a> From<&'a Body> for Psyche {
| biped_small::Species::Irrwurz | biped_small::Species::Irrwurz
| biped_small::Species::ShamanicSpirit | biped_small::Species::ShamanicSpirit
| biped_small::Species::Jiangshi | biped_small::Species::Jiangshi
| biped_small::Species::Bushly => 0.0, | biped_small::Species::Bushly
| biped_small::Species::GnarlingChieftain => 0.0,
_ => 0.5, _ => 0.5,
}, },

View File

@ -552,6 +552,7 @@ impl Body {
biped_small::Species::Jiangshi => Vec3::new(1.3, 1.8, 2.5), biped_small::Species::Jiangshi => Vec3::new(1.3, 1.8, 2.5),
biped_small::Species::Flamekeeper => Vec3::new(1.5, 1.5, 2.5), biped_small::Species::Flamekeeper => Vec3::new(1.5, 1.5, 2.5),
biped_small::Species::TreasureEgg => Vec3::new(1.1, 1.1, 1.4), biped_small::Species::TreasureEgg => Vec3::new(1.1, 1.1, 1.4),
biped_small::Species::GnarlingChieftain => Vec3::new(1.0, 0.75, 1.4),
_ => Vec3::new(1.0, 0.75, 1.4), _ => Vec3::new(1.0, 0.75, 1.4),
}, },
Body::BirdLarge(body) => match body.species { Body::BirdLarge(body) => match body.species {
@ -948,7 +949,7 @@ impl Body {
biped_large::Species::Tidalwarrior => 1600, biped_large::Species::Tidalwarrior => 1600,
biped_large::Species::Yeti => 1800, biped_large::Species::Yeti => 1800,
biped_large::Species::Minotaur => 3000, biped_large::Species::Minotaur => 3000,
biped_large::Species::Harvester => 1500, biped_large::Species::Harvester => 1300,
biped_large::Species::Blueoni => 240, biped_large::Species::Blueoni => 240,
biped_large::Species::Redoni => 240, biped_large::Species::Redoni => 240,
biped_large::Species::Huskbrute => 800, biped_large::Species::Huskbrute => 800,
@ -969,6 +970,7 @@ impl Body {
}, },
Body::BipedSmall(biped_small) => match biped_small.species { Body::BipedSmall(biped_small) => match biped_small.species {
biped_small::Species::Gnarling => 50, biped_small::Species::Gnarling => 50,
biped_small::Species::GnarlingChieftain => 150,
biped_small::Species::Adlet => 65, biped_small::Species::Adlet => 65,
biped_small::Species::Sahagin => 85, biped_small::Species::Sahagin => 85,
biped_small::Species::Haniwa => 100, biped_small::Species::Haniwa => 100,
@ -990,9 +992,9 @@ impl Body {
object::Body::BarrelOrgan => 500, object::Body::BarrelOrgan => 500,
object::Body::HaniwaSentry => 60, object::Body::HaniwaSentry => 60,
object::Body::SeaLantern => 100, object::Body::SeaLantern => 100,
object::Body::GnarlingTotemGreen => 25,
object::Body::TerracottaStatue => 600, object::Body::TerracottaStatue => 600,
object::Body::GnarlingTotemRed | object::Body::GnarlingTotemWhite => 35, object::Body::GnarlingTotemGreen => 15,
object::Body::GnarlingTotemRed | object::Body::GnarlingTotemWhite => 15,
_ => 1000, _ => 1000,
}, },
Body::ItemDrop(_) => 1000, Body::ItemDrop(_) => 1000,

View File

@ -53,6 +53,7 @@ make_case_elim!(
ShamanicSpirit = 16, ShamanicSpirit = 16,
Jiangshi = 17, Jiangshi = 17,
TreasureEgg = 18, TreasureEgg = 18,
GnarlingChieftain = 19,
} }
); );
@ -80,6 +81,7 @@ pub struct AllSpecies<SpeciesMeta> {
pub shamanic_spirit: SpeciesMeta, pub shamanic_spirit: SpeciesMeta,
pub jiangshi: SpeciesMeta, pub jiangshi: SpeciesMeta,
pub treasure_egg: SpeciesMeta, pub treasure_egg: SpeciesMeta,
pub gnarling_chieftain: SpeciesMeta,
} }
impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta> { impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta> {
@ -107,11 +109,12 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta>
Species::ShamanicSpirit => &self.shamanic_spirit, Species::ShamanicSpirit => &self.shamanic_spirit,
Species::Jiangshi => &self.jiangshi, Species::Jiangshi => &self.jiangshi,
Species::TreasureEgg => &self.treasure_egg, Species::TreasureEgg => &self.treasure_egg,
Species::GnarlingChieftain => &self.gnarling_chieftain,
} }
} }
} }
pub const ALL_SPECIES: [Species; 19] = [ pub const ALL_SPECIES: [Species; 20] = [
Species::Gnome, Species::Gnome,
Species::Sahagin, Species::Sahagin,
Species::Adlet, Species::Adlet,
@ -131,6 +134,7 @@ pub const ALL_SPECIES: [Species; 19] = [
Species::ShamanicSpirit, Species::ShamanicSpirit,
Species::Jiangshi, Species::Jiangshi,
Species::TreasureEgg, Species::TreasureEgg,
Species::GnarlingChieftain,
]; ];
impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> { impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {

View File

@ -543,7 +543,7 @@ impl SpriteKind {
| SpriteKind::Gold => 0.6, | SpriteKind::Gold => 0.6,
SpriteKind::EnsnaringVines SpriteKind::EnsnaringVines
| SpriteKind::CavernLillypadBlue | SpriteKind::CavernLillypadBlue
| SpriteKind::EnsnaringWeb => 0.1, | SpriteKind::EnsnaringWeb => 0.15,
SpriteKind::LillyPads => 0.1, SpriteKind::LillyPads => 0.1,
SpriteKind::WindowArabic | SpriteKind::BookshelfArabic => 1.9, SpriteKind::WindowArabic | SpriteKind::BookshelfArabic => 1.9,
SpriteKind::DecorSetArabic => 2.6, SpriteKind::DecorSetArabic => 2.6,

View File

@ -206,9 +206,27 @@ impl<'a> System<'a> for Sys {
} }
if matches!( if matches!(
physics_state.on_ground.and_then(|b| b.get_sprite()), physics_state.on_ground.and_then(|b| b.get_sprite()),
Some(SpriteKind::EnsnaringVines) | Some(SpriteKind::EnsnaringWeb) Some(SpriteKind::EnsnaringVines)
) { ) {
// If on ensnaring vines, apply ensnared debuff // If on ensnaring vines, apply partial ensnared debuff
emitters.emit(BuffEvent {
entity,
buff_change: BuffChange::Add(Buff::new(
BuffKind::Ensnared,
BuffData::new(0.5, Some(Secs(1.0))),
Vec::new(),
BuffSource::World,
*read_data.time,
dest_info,
None,
)),
});
}
if matches!(
physics_state.on_ground.and_then(|b| b.get_sprite()),
Some(SpriteKind::EnsnaringWeb)
) {
// If on ensnaring web, apply ensnared debuff
emitters.emit(BuffEvent { emitters.emit(BuffEvent {
entity, entity,
buff_change: BuffChange::Add(Buff::new( buff_change: BuffChange::Add(Buff::new(

View File

@ -1617,9 +1617,14 @@ impl<'a> AgentData<'a> {
Tactic::Yeti => { Tactic::Yeti => {
self.handle_yeti_attack(agent, controller, &attack_data, tgt_data, read_data) self.handle_yeti_attack(agent, controller, &attack_data, tgt_data, read_data)
}, },
Tactic::Harvester => { Tactic::Harvester => self.handle_harvester_attack(
self.handle_harvester_attack(agent, controller, &attack_data, tgt_data, read_data) agent,
}, controller,
&attack_data,
tgt_data,
read_data,
rng,
),
Tactic::Cardinal => self.handle_cardinal_attack( Tactic::Cardinal => self.handle_cardinal_attack(
agent, agent,
controller, controller,

View File

@ -4756,13 +4756,39 @@ impl<'a> AgentData<'a> {
attack_data: &AttackData, attack_data: &AttackData,
tgt_data: &TargetData, tgt_data: &TargetData,
read_data: &ReadData, read_data: &ReadData,
rng: &mut impl Rng,
) { ) {
const VINE_CREATION_THRESHOLD: f32 = 0.50; const FIRST_VINE_CREATION_THRESHOLD: f32 = 0.60;
const FIRE_BREATH_RANGE: f32 = 20.0; const SECOND_VINE_CREATION_THRESHOLD: f32 = 0.30;
const MAX_PUMPKIN_RANGE: f32 = 50.0; const MAX_PUMPKIN_RANGE: f32 = 50.0;
const FIRE_BREATH_RANGE: f32 = 20.0;
const FIRE_BREATH_TIME: f32 = 4.0;
const FIRE_BREATH_SHORT_TIME: f32 = 2.0;
const FIRE_BREATH_COOLDOWN: f32 = 3.0;
const CLOSE_MIXUP_COOLDOWN: f32 = 6.0;
enum ActionStateConditions { enum ActionStateConditions {
ConditionHasSummonedVines = 0, ConditionHasSummonedFirstVines = 0,
ConditionHasSummonedSecondVines,
}
enum ActionStateTimers {
TimerFire = 0,
TimerCloseMixup,
}
match self.char_state {
CharacterState::BasicBeam(_) => {
agent.combat_state.timers[ActionStateTimers::TimerFire as usize] = 0.0;
},
CharacterState::BasicRanged(_) => {
agent.combat_state.timers[ActionStateTimers::TimerCloseMixup as usize] = 0.0;
},
_ => {
agent.combat_state.timers[ActionStateTimers::TimerFire as usize] += read_data.dt.0;
agent.combat_state.timers[ActionStateTimers::TimerCloseMixup as usize] +=
read_data.dt.0;
},
} }
let health_fraction = self.health.map_or(0.5, |h| h.fraction()); let health_fraction = self.health.map_or(0.5, |h| h.fraction());
@ -4778,36 +4804,86 @@ impl<'a> AgentData<'a> {
) )
}; };
if health_fraction < VINE_CREATION_THRESHOLD if (health_fraction < FIRST_VINE_CREATION_THRESHOLD
&& !agent.combat_state.conditions && !agent.combat_state.conditions
[ActionStateConditions::ConditionHasSummonedVines as usize] [ActionStateConditions::ConditionHasSummonedFirstVines as usize])
|| (health_fraction < SECOND_VINE_CREATION_THRESHOLD
&& !agent.combat_state.conditions
[ActionStateConditions::ConditionHasSummonedSecondVines as usize])
{ {
// Summon vines when reach threshold of health if health_fraction < SECOND_VINE_CREATION_THRESHOLD {
controller.push_basic_input(InputKind::Ability(0)); // Summon second vines when reach threshold of health
controller.push_basic_input(InputKind::Ability(2));
if matches!(self.char_state, CharacterState::SpriteSummon(c) if matches!(c.stage_section, StageSection::Recover)) if matches!(self.char_state, CharacterState::SpriteSummon(c) if matches!(c.stage_section, StageSection::Recover))
{ {
agent.combat_state.conditions agent.combat_state.conditions
[ActionStateConditions::ConditionHasSummonedVines as usize] = true; [ActionStateConditions::ConditionHasSummonedSecondVines as usize] = true;
} }
} else if attack_data.dist_sqrd < FIRE_BREATH_RANGE.powi(2) { } else {
if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs(5)) // Summon first vines when reach threshold of health
&& line_of_sight_with_target() controller.push_basic_input(InputKind::Ability(1));
if matches!(self.char_state, CharacterState::SpriteSummon(c) if matches!(c.stage_section, StageSection::Recover))
{
agent.combat_state.conditions
[ActionStateConditions::ConditionHasSummonedFirstVines as usize] = true;
}
}
} else if attack_data.in_min_range() {
if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs_f32(FIRE_BREATH_SHORT_TIME))
{ {
// Keep breathing fire if close enough, can see target, and have not been // Keep breathing fire if close enough, can see target, and have not been
// breathing for more than 5 seconds // breathing for more than short limit
controller.push_basic_input(InputKind::Secondary); controller.push_basic_input(InputKind::Secondary);
} else if attack_data.in_min_range() && attack_data.angle < 60.0 { } else if agent.combat_state.timers[ActionStateTimers::TimerCloseMixup as usize]
> CLOSE_MIXUP_COOLDOWN
&& line_of_sight_with_target()
{
if agent.combat_state.timers[ActionStateTimers::TimerFire as usize]
< FIRE_BREATH_COOLDOWN
{
// Throw a close range pumpkin to knock back player
controller.push_basic_input(InputKind::Ability(0));
} else {
let randomise = rng.gen_range(1..=3);
match randomise {
1 => controller.push_basic_input(InputKind::Secondary), /* start fire
* breath */
_ => controller.push_basic_input(InputKind::Ability(0)), /* close range
* pumpkin
* _ => controller.push_basic_input(InputKind::Primary), // scythe */
}
}
} else if attack_data.angle < 60.0 {
// Scythe them if they're in range and angle // Scythe them if they're in range and angle
controller.push_basic_input(InputKind::Primary); controller.push_basic_input(InputKind::Primary);
} else if attack_data.angle < 30.0 && line_of_sight_with_target() { }
} else if attack_data.dist_sqrd < FIRE_BREATH_RANGE.powi(2) {
if line_of_sight_with_target() {
if matches!(self.char_state, CharacterState::BasicBeam(c) if c.timer < Duration::from_secs_f32(FIRE_BREATH_TIME))
{
// Keep breathing fire if close enough, can see target, and have not been
// breathing for more than upper limit
controller.push_basic_input(InputKind::Secondary);
} else if attack_data.angle < 30.0
&& agent.combat_state.timers[ActionStateTimers::TimerFire as usize]
> FIRE_BREATH_COOLDOWN
{
// Start breathing fire at them if close enough, in angle, and can see target // Start breathing fire at them if close enough, in angle, and can see target
controller.push_basic_input(InputKind::Secondary); controller.push_basic_input(InputKind::Secondary);
} else if agent.combat_state.timers[ActionStateTimers::TimerCloseMixup as usize]
> CLOSE_MIXUP_COOLDOWN
{
// Throw a close range pumpkin to knock back player
controller.push_basic_input(InputKind::Ability(0));
}
} }
} else if attack_data.dist_sqrd < MAX_PUMPKIN_RANGE.powi(2) && line_of_sight_with_target() { } else if attack_data.dist_sqrd < MAX_PUMPKIN_RANGE.powi(2) && line_of_sight_with_target() {
// Throw a pumpkin at them if close enough and can see them // Throw a pumpkin at them if close enough and can see them
controller.push_basic_input(InputKind::Ability(1)); controller.push_basic_input(InputKind::Ability(0));
} }
// Always attempt to path towards target // Always attempt to path towards target
self.path_toward_target( self.path_toward_target(
agent, agent,
@ -5810,8 +5886,8 @@ impl<'a> AgentData<'a> {
read_data: &ReadData, read_data: &ReadData,
rng: &mut impl Rng, rng: &mut impl Rng,
) { ) {
const TOTEM_TIMER: f32 = 10.0; const TOTEM_TIMER: f32 = 20.0;
const HEAVY_ATTACK_WAIT_TIME: f32 = 15.0; const HEAVY_ATTACK_WAIT_TIME: f32 = 10.0;
enum ActionStateTimers { enum ActionStateTimers {
TimerSummonTotem = 0, TimerSummonTotem = 0,
@ -5846,7 +5922,7 @@ impl<'a> AgentData<'a> {
3 => Some(BuffKind::Hastened), 3 => Some(BuffKind::Hastened),
_ => None, _ => None,
}; };
if buff_kind.map_or(true, |b| self.has_buff(read_data, b)) if buff_kind.map_or(false, |b| self.has_buff(read_data, b))
&& matches!(self.char_state, CharacterState::Wielding { .. }) && matches!(self.char_state, CharacterState::Wielding { .. })
{ {
// If already under effects of buff from totem that would be summoned, don't // If already under effects of buff from totem that would be summoned, don't
@ -5892,6 +5968,7 @@ impl<'a> AgentData<'a> {
read_data.dt.0 * 3.3; read_data.dt.0 * 3.3;
} }
if !attack_data.in_min_range() {
self.path_toward_target( self.path_toward_target(
agent, agent,
controller, controller,
@ -5901,6 +5978,7 @@ impl<'a> AgentData<'a> {
None, None,
); );
} }
}
pub fn handle_sword_simple_attack( pub fn handle_sword_simple_attack(
&self, &self,

View File

@ -113,7 +113,8 @@ impl Animation for SpriteSummonAnimation {
* Quaternion::rotation_y(move2 * -0.1); * Quaternion::rotation_y(move2 * -0.1);
}, },
Some(ToolKind::Natural) => match ability_id { Some(ToolKind::Natural) => match ability_id {
Some("common.abilities.custom.harvester.ensnaringvines") => { Some("common.abilities.custom.harvester.ensnaringvines_0")
| Some("common.abilities.custom.harvester.ensnaringvines_1") => {
let (move1, move1pow, move2, move3) = match stage_section { let (move1, move1pow, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time, anim_time.powf(0.1), 0.0, 0.0), Some(StageSection::Buildup) => (anim_time, anim_time.powf(0.1), 0.0, 0.0),
Some(StageSection::Action) => { Some(StageSection::Action) => {

View File

@ -168,6 +168,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(ShamanicSpirit, _) => (-0.5, 4.5), (ShamanicSpirit, _) => (-0.5, 4.5),
(Jiangshi, _) => (-1.0, 6.5), (Jiangshi, _) => (-1.0, 6.5),
(TreasureEgg, _) => (-1.0, 9.0), (TreasureEgg, _) => (-1.0, 9.0),
(GnarlingChieftain, _) => (0.0, 6.0),
}, },
chest: match (body.species, body.body_type) { chest: match (body.species, body.body_type) {
(Gnome, _) => (0.0, 9.0), (Gnome, _) => (0.0, 9.0),
@ -189,6 +190,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(ShamanicSpirit, _) => (0.0, 14.5), (ShamanicSpirit, _) => (0.0, 14.5),
(Jiangshi, _) => (0.0, 14.0), (Jiangshi, _) => (0.0, 14.0),
(TreasureEgg, _) => (0.0, 3.0), (TreasureEgg, _) => (0.0, 3.0),
(GnarlingChieftain, _) => (0.0, 7.5),
}, },
pants: match (body.species, body.body_type) { pants: match (body.species, body.body_type) {
(Gnome, _) => (0.0, -3.0), (Gnome, _) => (0.0, -3.0),
@ -210,6 +212,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(ShamanicSpirit, _) => (0.0, -8.0), (ShamanicSpirit, _) => (0.0, -8.0),
(Jiangshi, _) => (0.5, -6.0), (Jiangshi, _) => (0.5, -6.0),
(TreasureEgg, _) => (0.0, 1.0), (TreasureEgg, _) => (0.0, 1.0),
(GnarlingChieftain, _) => (0.0, -3.0),
}, },
tail: match (body.species, body.body_type) { tail: match (body.species, body.body_type) {
(Gnome, _) => (0.0, 0.0), (Gnome, _) => (0.0, 0.0),
@ -231,6 +234,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(ShamanicSpirit, _) => (0.0, 0.0), (ShamanicSpirit, _) => (0.0, 0.0),
(Jiangshi, _) => (0.0, 0.0), (Jiangshi, _) => (0.0, 0.0),
(TreasureEgg, _) => (0.0, 0.0), (TreasureEgg, _) => (0.0, 0.0),
(GnarlingChieftain, _) => (-2.0, 1.5),
}, },
hand: match (body.species, body.body_type) { hand: match (body.species, body.body_type) {
(Gnome, _) => (4.0, 0.5, -1.0), (Gnome, _) => (4.0, 0.5, -1.0),
@ -252,6 +256,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(ShamanicSpirit, _) => (5.0, 0.0, 1.0), (ShamanicSpirit, _) => (5.0, 0.0, 1.0),
(Jiangshi, _) => (5.0, -1.0, 3.0), (Jiangshi, _) => (5.0, -1.0, 3.0),
(TreasureEgg, _) => (5.0, 2.0, 5.0), (TreasureEgg, _) => (5.0, 2.0, 5.0),
(GnarlingChieftain, _) => (4.0, 0.0, 1.5),
}, },
foot: match (body.species, body.body_type) { foot: match (body.species, body.body_type) {
(Gnome, _) => (3.0, 0.0, 4.0), (Gnome, _) => (3.0, 0.0, 4.0),
@ -273,6 +278,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(ShamanicSpirit, _) => (3.5, 3.0, 7.0), (ShamanicSpirit, _) => (3.5, 3.0, 7.0),
(Jiangshi, _) => (3.0, 0.0, 8.0), (Jiangshi, _) => (3.0, 0.0, 8.0),
(TreasureEgg, _) => (2.0, 0.5, 4.0), (TreasureEgg, _) => (2.0, 0.5, 4.0),
(GnarlingChieftain, _) => (2.5, 1.0, 5.0),
}, },
grip: match (body.species, body.body_type) { grip: match (body.species, body.body_type) {
(Gnome, _) => (0.0, 0.0, 5.0), (Gnome, _) => (0.0, 0.0, 5.0),
@ -294,6 +300,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(ShamanicSpirit, _) => (0.0, 0.0, 8.0), (ShamanicSpirit, _) => (0.0, 0.0, 8.0),
(Jiangshi, _) => (0.0, 0.0, 8.0), (Jiangshi, _) => (0.0, 0.0, 8.0),
(TreasureEgg, _) => (0.0, 0.0, 7.0), (TreasureEgg, _) => (0.0, 0.0, 7.0),
(GnarlingChieftain, _) => (0.0, 0.0, 7.0),
}, },
scaler: match (body.species, body.body_type) { scaler: match (body.species, body.body_type) {
(Gnome, _) => 0.8, (Gnome, _) => 0.8,
@ -315,6 +322,7 @@ impl<'a> From<&'a Body> for SkeletonAttr {
(ShamanicSpirit, _) => 1.0, (ShamanicSpirit, _) => 1.0,
(Jiangshi, _) => 1.0, (Jiangshi, _) => 1.0,
(TreasureEgg, _) => 1.0, (TreasureEgg, _) => 1.0,
(GnarlingChieftain, _) => 0.8,
}, },
} }
} }

View File

@ -1894,11 +1894,11 @@ impl ParticleMgr {
kind: buff::BuffKind::Burning, kind: buff::BuffKind::Burning,
.. ..
} => { } => {
let num_particles = aura.radius.powi(2) * dt / 250.0; let heartbeats = self.scheduler.heartbeats(Duration::from_millis(5));
let num_particles = num_particles.floor() as usize self.particles.resize_with(
+ usize::from(rng.gen_bool(f64::from(num_particles % 1.0))); self.particles.len()
self.particles + aura.radius.powi(2) as usize * usize::from(heartbeats) / 300,
.resize_with(self.particles.len() + num_particles, || { || {
let rand_pos = { let rand_pos = {
let theta = rng.gen::<f32>() * TAU; let theta = rng.gen::<f32>() * TAU;
let radius = aura.radius * rng.gen::<f32>().sqrt(); let radius = aura.radius * rng.gen::<f32>().sqrt();
@ -1918,7 +1918,8 @@ impl ParticleMgr {
rand_pos.with_z(pos.z), rand_pos.with_z(pos.z),
rand_pos.with_z(pos.z + 1.0), rand_pos.with_z(pos.z + 1.0),
) )
}); },
);
}, },
aura::AuraKind::Buff { aura::AuraKind::Buff {
kind: buff::BuffKind::Hastened, kind: buff::BuffKind::Hastened,