diff --git a/CHANGELOG.md b/CHANGELOG.md index b4cb7e1ebc..f6b18a86d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Airships can now have sprites, which can be interacted with. - Some sprites can be sat on. - Pet birds can now sit on the player's shoulder as they explore the world. +- Adlet caves ### Changed diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index b3b32f0b4f..263094e734 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -301,9 +301,11 @@ abilities: [], ), Custom("Tursus Claws"): ( - primary: Simple(None, "common.abilities.custom.tursus_claws.basic"), - secondary: Simple(None, "common.abilities.custom.tursus_claws.basic"), - abilities: [], + primary: Simple(None, "common.abilities.custom.tursus.tursus_claws"), + secondary: Simple(None, "common.abilities.custom.tursus.tusk_bash_leap"), + abilities: [ + Simple(None, "common.abilities.custom.tursus.tusk_stab"), + ], ), Custom("Wendigo Magic"): ( primary: Simple(None, "common.abilities.custom.wendigomagic.frostbomb"), @@ -352,6 +354,11 @@ secondary: Simple(None, "common.abilities.custom.quadmedbasic.triplestrike"), abilities: [], ), + Custom("Frostfang"): ( + primary: Simple(None, "common.abilities.custom.frostfang.singlestrike"), + secondary: Simple(None, "common.abilities.custom.frostfang.triplestrike"), + abilities: [], + ), Custom("Roshwalr"): ( primary: Simple(None, "common.abilities.custom.roshwalr.doublehusk"), secondary: Simple(None, "common.abilities.custom.roshwalr.slowcharge"), @@ -540,6 +547,14 @@ ], ), // TODO: Allow ability sets to expand other ability sets + Custom("Ice Drake"): ( + primary: Simple(None, "common.abilities.custom.icedrake.multi_bite"), + secondary: Simple(None, "common.abilities.custom.icedrake.icy_bite"), + abilities: [ + Simple(None, "common.abilities.custom.icedrake.icebombs"), + Simple(None, "common.abilities.custom.icedrake.icebreath"), + ], + ), Custom("Dagon"): ( primary: Simple(None, "common.abilities.custom.dagon.dagonbombs"), secondary: Simple(None, "common.abilities.custom.dagon.seaurchins"), @@ -683,6 +698,11 @@ secondary: Simple(None, "common.abilities.music.lyre"), abilities: [], ), + Custom("WildskinDrum"): ( + primary: Simple(None, "common.abilities.music.wildskin_drum"), + secondary: Simple(None, "common.abilities.music.wildskin_drum"), + abilities: [], + ), Custom("IcyTalharpa"): ( primary: Simple(None, "common.abilities.music.icy_talharpa"), secondary: Simple(None, "common.abilities.music.icy_talharpa"), @@ -745,4 +765,29 @@ secondary: Simple(None, "common.abilities.empty.basic"), abilities: [], ), + // Adlets + // TODO: Do we want to eventually convert these to simple variants of weapons? + Custom("Adlet Hunter"): ( + primary: Simple(None, "common.abilities.adlet.hunter.stab"), + secondary: Simple(None, "common.abilities.adlet.hunter.throw"), + abilities: [], + ), + Custom("Adlet Icepicker"): ( + primary: Simple(None, "common.abilities.adlet.icepicker.spike"), + secondary: Simple(None, "common.abilities.adlet.icepicker.leap"), + abilities: [], + ), + Custom("Adlet Tracker"): ( + primary: Simple(None, "common.abilities.adlet.tracker.arrow"), + secondary: Simple(None, "common.abilities.adlet.tracker.trap"), + abilities: [], + ), + Custom("Adlet Elder"): ( + primary: Simple(None, "common.abilities.adlet.elder.triplestrike"), + secondary: Simple(None, "common.abilities.adlet.elder.dash"), + abilities: [ + Simple(None, "common.abilities.adlet.elder.trap"), + Simple(None, "common.abilities.adlet.elder.leap"), + ], + ), }) diff --git a/assets/common/abilities/adlet/elder/dash.ron b/assets/common/abilities/adlet/elder/dash.ron new file mode 100644 index 0000000000..1134111589 --- /dev/null +++ b/assets/common/abilities/adlet/elder/dash.ron @@ -0,0 +1,27 @@ +DashMelee( + energy_cost: 10.0, + melee_constructor: ( + kind: Stab( + damage: 9.0, + poise: 40.0, + knockback: 8.0, + energy_regen: 0.0, + ), + scaled: Some(Stab( + damage: 16.0, + poise: 0.0, + knockback: 7.0, + energy_regen: 0.0, + )), + range: 5.0, + angle: 45.0, + ), + energy_drain: 0, + forward_speed: 4.0, + buildup_duration: 0.6, + charge_duration: 1.2, + swing_duration: 0.1, + recover_duration: 0.9, + ori_modifier: 0.3, + charge_through: false, +) diff --git a/assets/common/abilities/adlet/elder/leap.ron b/assets/common/abilities/adlet/elder/leap.ron new file mode 100644 index 0000000000..f8f6193bea --- /dev/null +++ b/assets/common/abilities/adlet/elder/leap.ron @@ -0,0 +1,27 @@ +LeapMelee( + energy_cost: 0.0, + buildup_duration: 0.5, + movement_duration: 0.8, + swing_duration: 0.15, + recover_duration: 0.2, + melee_constructor: ( + kind: Bash( + damage: 25.0, + poise: 40.0, + knockback: 25.0, + energy_regen: 0.0, + ), + range: 4.5, + angle: 360.0, + multi_target: Some(Normal), + damage_effect: Some(Buff(( + kind: Frozen, + dur_secs: 2.0, + strength: DamageFraction(0.1), + chance: 1.0, + ))), + ), + forward_leap_strength: 30.0, + vertical_leap_strength: 15.0, + specifier: Some(ElderLeap), +) \ No newline at end of file diff --git a/assets/common/abilities/adlet/elder/trap.ron b/assets/common/abilities/adlet/elder/trap.ron new file mode 100644 index 0000000000..333c7c6aa5 --- /dev/null +++ b/assets/common/abilities/adlet/elder/trap.ron @@ -0,0 +1,16 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.75, + recover_duration: 0.75, + projectile: Trap( + damage: 25, + knockback: 4, + energy_regen: 0, + ), + projectile_body: Object(AdletTrap), + projectile_light: None, + projectile_speed: 20.0, + num_projectiles: 6, + projectile_spread: 0.3, + move_efficiency: 0.8, +) \ No newline at end of file diff --git a/assets/common/abilities/adlet/elder/triplestrike.ron b/assets/common/abilities/adlet/elder/triplestrike.ron new file mode 100644 index 0000000000..5a3248f632 --- /dev/null +++ b/assets/common/abilities/adlet/elder/triplestrike.ron @@ -0,0 +1,63 @@ +ComboMelee2( + strikes: [ + ( + melee_constructor: ( + kind: Bash( + damage: 10, + poise: 15, + knockback: 5, + energy_regen: 0, + ), + range: 2.5, + angle: 30.0, + ), + buildup_duration: 0.9, + swing_duration: 0.07, + hit_timing: 0.5, + recover_duration: 0.4, + movement: ( + swing: Some(Forward(1.0)), + ), + ori_modifier: 0.7, + ), + ( + melee_constructor: ( + kind: Slash( + damage: 10, + poise: 18, + knockback: 5, + energy_regen: 0, + ), + range: 2.5, + angle: 30.0, + ), + buildup_duration: 0.6, + swing_duration: 0.07, + hit_timing: 0.5, + recover_duration: 0.4, + ori_modifier: 0.7, + ), + ( + melee_constructor: ( + kind: Bash( + damage: 10, + poise: 20, + knockback: 5, + energy_regen: 0, + ), + range: 2.5, + angle: 30.0, + ), + buildup_duration: 0.4, + swing_duration: 0.07, + hit_timing: 0.5, + recover_duration: 0.4, + movement: ( + swing: Some(Forward(1.0)), + ), + ori_modifier: 0.7, + ), + ], + energy_cost_per_strike: 0, + auto_progress: true, +) \ No newline at end of file diff --git a/assets/common/abilities/adlet/hunter/stab.ron b/assets/common/abilities/adlet/hunter/stab.ron new file mode 100644 index 0000000000..472ba9507d --- /dev/null +++ b/assets/common/abilities/adlet/hunter/stab.ron @@ -0,0 +1,17 @@ +BasicMelee( + energy_cost: 0, + buildup_duration: 0.4, + swing_duration: 0.2, + recover_duration: 0.4, + melee_constructor: ( + kind: Stab( + damage: 10, + poise: 5, + knockback: 0, + energy_regen: 0, + ), + range: 3, + angle: 5, + ), + ori_modifier: 1.0, +) diff --git a/assets/common/abilities/adlet/hunter/throw.ron b/assets/common/abilities/adlet/hunter/throw.ron new file mode 100644 index 0000000000..a157aa68c1 --- /dev/null +++ b/assets/common/abilities/adlet/hunter/throw.ron @@ -0,0 +1,16 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.8, + recover_duration: 0.8, + projectile: Arrow( + damage: 16, + knockback: 1, + energy_regen: 0, + ), + projectile_body: Object(AdletSpear), + projectile_light: None, + projectile_speed: 40.0, + num_projectiles: 1, + projectile_spread: 0.0, + move_efficiency: 1.0, +) diff --git a/assets/common/abilities/adlet/icepicker/leap.ron b/assets/common/abilities/adlet/icepicker/leap.ron new file mode 100644 index 0000000000..484f3ce0b7 --- /dev/null +++ b/assets/common/abilities/adlet/icepicker/leap.ron @@ -0,0 +1,20 @@ +LeapMelee( + energy_cost: 0.0, + buildup_duration: 0.6, + movement_duration: 0.2, + swing_duration: 0.1, + recover_duration: 0.3, + melee_constructor: ( + kind: Stab( + damage: 20, + poise: 20, + knockback: 0, + energy_regen: 0, + ), + range: 4, + angle: 360, + ), + forward_leap_strength: 24, + vertical_leap_strength: 16, + specifier: None, +) diff --git a/assets/common/abilities/adlet/icepicker/spike.ron b/assets/common/abilities/adlet/icepicker/spike.ron new file mode 100644 index 0000000000..47e415424e --- /dev/null +++ b/assets/common/abilities/adlet/icepicker/spike.ron @@ -0,0 +1,17 @@ +BasicMelee( + energy_cost: 0, + buildup_duration: 0.4, + swing_duration: 0.25, + recover_duration: 0.45, + melee_constructor: ( + kind: Stab( + damage: 12, + poise: 10, + knockback: 0, + energy_regen: 0, + ), + range: 3, + angle: 15, + ), + ori_modifier: 1.0, +) diff --git a/assets/common/abilities/adlet/tracker/arrow.ron b/assets/common/abilities/adlet/tracker/arrow.ron new file mode 100644 index 0000000000..9b42a8b8ed --- /dev/null +++ b/assets/common/abilities/adlet/tracker/arrow.ron @@ -0,0 +1,16 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.6, + recover_duration: 0.4, + projectile: Arrow( + damage: 12, + knockback: 0, + energy_regen: 0, + ), + projectile_body: Object(Arrow), + projectile_light: None, + projectile_speed: 100.0, + num_projectiles: 1, + projectile_spread: 0.0, + move_efficiency: 0.3, +) diff --git a/assets/common/abilities/adlet/tracker/trap.ron b/assets/common/abilities/adlet/tracker/trap.ron new file mode 100644 index 0000000000..cc90b8b842 --- /dev/null +++ b/assets/common/abilities/adlet/tracker/trap.ron @@ -0,0 +1,16 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.75, + recover_duration: 0.75, + projectile: Trap( + damage: 12, + knockback: 0, + energy_regen: 0, + ), + projectile_body: Object(AdletTrap), + projectile_light: None, + projectile_speed: 10.0, + num_projectiles: 1, + projectile_spread: 0.2, + move_efficiency: 0.3, +) \ No newline at end of file diff --git a/assets/common/abilities/axe/leap.ron b/assets/common/abilities/axe/leap.ron index 500576beab..25119259dc 100644 --- a/assets/common/abilities/axe/leap.ron +++ b/assets/common/abilities/axe/leap.ron @@ -17,4 +17,5 @@ LeapMelee( ), forward_leap_strength: 20.0, vertical_leap_strength: 8.0, + specifier: None, ) diff --git a/assets/common/abilities/bow/shotgun.ron b/assets/common/abilities/bow/shotgun.ron index a3ce87a906..66d1115115 100644 --- a/assets/common/abilities/bow/shotgun.ron +++ b/assets/common/abilities/bow/shotgun.ron @@ -12,4 +12,5 @@ BasicRanged( projectile_light: None, projectile_speed: 80.0, num_projectiles: 5, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/bowsimple/basic.ron b/assets/common/abilities/bowsimple/basic.ron index 05cd0597f5..b820fa9ceb 100644 --- a/assets/common/abilities/bowsimple/basic.ron +++ b/assets/common/abilities/bowsimple/basic.ron @@ -12,4 +12,5 @@ BasicRanged( projectile_speed: 100.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/arthropods/blackwidow/poisonball.ron b/assets/common/abilities/custom/arthropods/blackwidow/poisonball.ron index faad0f0faa..d93ae0899d 100644 --- a/assets/common/abilities/custom/arthropods/blackwidow/poisonball.ron +++ b/assets/common/abilities/custom/arthropods/blackwidow/poisonball.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 260.0, num_projectiles: 1, projectile_spread: 0.3, + move_efficiency: 0.3, ) \ No newline at end of file diff --git a/assets/common/abilities/custom/arthropods/hornbeetle/leap.ron b/assets/common/abilities/custom/arthropods/hornbeetle/leap.ron index 5e07e4f737..b4f5d192a9 100644 --- a/assets/common/abilities/custom/arthropods/hornbeetle/leap.ron +++ b/assets/common/abilities/custom/arthropods/hornbeetle/leap.ron @@ -17,4 +17,5 @@ LeapMelee( ), forward_leap_strength: 40.0, vertical_leap_strength: 7.5, + specifier: None, ) \ No newline at end of file diff --git a/assets/common/abilities/custom/arthropods/tarantula/leap.ron b/assets/common/abilities/custom/arthropods/tarantula/leap.ron index 054e3da70c..10bb730809 100644 --- a/assets/common/abilities/custom/arthropods/tarantula/leap.ron +++ b/assets/common/abilities/custom/arthropods/tarantula/leap.ron @@ -16,4 +16,5 @@ LeapMelee( ), forward_leap_strength: 25.0, vertical_leap_strength: 10.0, + specifier: None, ) \ No newline at end of file diff --git a/assets/common/abilities/custom/asp/firebomb.ron b/assets/common/abilities/custom/asp/firebomb.ron index 27e06879f4..452cf492e6 100644 --- a/assets/common/abilities/custom/asp/firebomb.ron +++ b/assets/common/abilities/custom/asp/firebomb.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 70.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/birdlargebreathe/firebomb.ron b/assets/common/abilities/custom/birdlargebreathe/firebomb.ron index 9862e7f0bf..cb309e83ec 100644 --- a/assets/common/abilities/custom/birdlargebreathe/firebomb.ron +++ b/assets/common/abilities/custom/birdlargebreathe/firebomb.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 60.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/birdlargefire/firebomb.ron b/assets/common/abilities/custom/birdlargefire/firebomb.ron index 9862e7f0bf..cb309e83ec 100644 --- a/assets/common/abilities/custom/birdlargefire/firebomb.ron +++ b/assets/common/abilities/custom/birdlargefire/firebomb.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 60.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/boreal_warrior/bow/shotgun.ron b/assets/common/abilities/custom/boreal_warrior/bow/shotgun.ron index 05837b1fa3..f40e0ea066 100644 --- a/assets/common/abilities/custom/boreal_warrior/bow/shotgun.ron +++ b/assets/common/abilities/custom/boreal_warrior/bow/shotgun.ron @@ -18,4 +18,5 @@ BasicRanged( strength: DamageFraction(0.1), chance: 1.0, ))), + move_efficiency: 0.3, ) \ No newline at end of file diff --git a/assets/common/abilities/custom/boreal_warrior/hammer/leap.ron b/assets/common/abilities/custom/boreal_warrior/hammer/leap.ron index 935ecc5398..cffca268b7 100644 --- a/assets/common/abilities/custom/boreal_warrior/hammer/leap.ron +++ b/assets/common/abilities/custom/boreal_warrior/hammer/leap.ron @@ -23,4 +23,5 @@ LeapMelee( ), forward_leap_strength: 20.0, vertical_leap_strength: 8.0, + specifier: None, ) \ No newline at end of file diff --git a/assets/common/abilities/custom/claygolem/rocket.ron b/assets/common/abilities/custom/claygolem/rocket.ron index b077118f29..e5b840bc5a 100644 --- a/assets/common/abilities/custom/claygolem/rocket.ron +++ b/assets/common/abilities/custom/claygolem/rocket.ron @@ -13,4 +13,5 @@ BasicRanged( projectile_speed: 30.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/cloudwyvern/windbomb.ron b/assets/common/abilities/custom/cloudwyvern/windbomb.ron index 458aa96cc6..544495ddf2 100644 --- a/assets/common/abilities/custom/cloudwyvern/windbomb.ron +++ b/assets/common/abilities/custom/cloudwyvern/windbomb.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 60.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/cyclops/optic_blast.ron b/assets/common/abilities/custom/cyclops/optic_blast.ron index e49a8b0f8a..e6ff41f675 100644 --- a/assets/common/abilities/custom/cyclops/optic_blast.ron +++ b/assets/common/abilities/custom/cyclops/optic_blast.ron @@ -13,4 +13,5 @@ BasicRanged( projectile_speed: 100.0, num_projectiles: 1, projectile_spread: 0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/dagon/dagonbombs.ron b/assets/common/abilities/custom/dagon/dagonbombs.ron index 851601575f..a860ff6e4a 100644 --- a/assets/common/abilities/custom/dagon/dagonbombs.ron +++ b/assets/common/abilities/custom/dagon/dagonbombs.ron @@ -13,4 +13,5 @@ BasicRanged( projectile_speed: 20.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/dullahan/fierce_darts.ron b/assets/common/abilities/custom/dullahan/fierce_darts.ron index 64a9b1f906..d9b3b379ce 100644 --- a/assets/common/abilities/custom/dullahan/fierce_darts.ron +++ b/assets/common/abilities/custom/dullahan/fierce_darts.ron @@ -12,4 +12,5 @@ BasicRanged( projectile_speed: 120.0, num_projectiles: 3, projectile_spread: 0.075, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/dullahan/knife_rain.ron b/assets/common/abilities/custom/dullahan/knife_rain.ron index e1712cb2f3..8ecba1eb09 100644 --- a/assets/common/abilities/custom/dullahan/knife_rain.ron +++ b/assets/common/abilities/custom/dullahan/knife_rain.ron @@ -13,4 +13,5 @@ BasicRanged( projectile_speed: 20.0, num_projectiles: 36, projectile_spread: 0.4, + move_efficiency: 0.3, ) \ No newline at end of file diff --git a/assets/common/abilities/custom/flamewyvern/firebomb.ron b/assets/common/abilities/custom/flamewyvern/firebomb.ron index 9862e7f0bf..cb309e83ec 100644 --- a/assets/common/abilities/custom/flamewyvern/firebomb.ron +++ b/assets/common/abilities/custom/flamewyvern/firebomb.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 60.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/frostfang/singlestrike.ron b/assets/common/abilities/custom/frostfang/singlestrike.ron new file mode 100644 index 0000000000..3f926554d9 --- /dev/null +++ b/assets/common/abilities/custom/frostfang/singlestrike.ron @@ -0,0 +1,31 @@ +ComboMelee2( + strikes: [ + ( + melee_constructor: ( + kind: Slash( + damage: 20, + poise: 28, + knockback: 3, + energy_regen: 0, + ), + range: 2.7, + angle: 60.0, + damage_effect: Some(Buff(( + kind: Frozen, + dur_secs: 10.0, + strength: Value(0.5), + chance: 1.0, + ))), + ), + buildup_duration: 0.8, + swing_duration: 0.1, + hit_timing: 0.5, + recover_duration: 0.8, + movement: ( + swing: Some(Forward(1.0)), + ), + ori_modifier: 0.6, + ), + ], + energy_cost_per_strike: 0, +) \ No newline at end of file diff --git a/assets/common/abilities/custom/frostfang/triplestrike.ron b/assets/common/abilities/custom/frostfang/triplestrike.ron new file mode 100644 index 0000000000..3874146167 --- /dev/null +++ b/assets/common/abilities/custom/frostfang/triplestrike.ron @@ -0,0 +1,63 @@ +ComboMelee2( + strikes: [ + ( + melee_constructor: ( + kind: Bash( + damage: 10, + poise: 15, + knockback: 5.0, + energy_regen: 0, + ), + range: 2.5, + angle: 30.0, + ), + buildup_duration: 0.9, + swing_duration: 0.07, + hit_timing: 0.5, + recover_duration: 0.4, + movement: ( + swing: Some(Forward(1.0)), + ), + ori_modifier: 0.7, + ), + ( + melee_constructor: ( + kind: Slash( + damage: 10, + poise: 18, + knockback: 5, + energy_regen: 0, + ), + range: 2.5, + angle: 30.0, + ), + buildup_duration: 0.8, + swing_duration: 0.07, + hit_timing: 0.5, + recover_duration: 0.4, + ori_modifier: 0.7, + ), + ( + melee_constructor: ( + kind: Bash( + damage: 10, + poise: 20, + knockback: 5, + energy_regen: 0, + ), + range: 2.5, + angle: 30.0, + ), + buildup_duration: 0.8, + swing_duration: 0.07, + hit_timing: 0.5, + recover_duration: 0.4, + movement: ( + swing: Some(Forward(1.0)), + ), + ori_modifier: 0.7, + ), + ], + energy_cost_per_strike: 0, + auto_progress: true, +) \ No newline at end of file diff --git a/assets/common/abilities/custom/frostwyvern/frostbomb.ron b/assets/common/abilities/custom/frostwyvern/frostbomb.ron index bc7ec6dc1f..5660366118 100644 --- a/assets/common/abilities/custom/frostwyvern/frostbomb.ron +++ b/assets/common/abilities/custom/frostwyvern/frostbomb.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 60.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/gigas_frost/ice_volley.ron b/assets/common/abilities/custom/gigas_frost/ice_volley.ron index a453ba6a5a..9b4c91a1fd 100644 --- a/assets/common/abilities/custom/gigas_frost/ice_volley.ron +++ b/assets/common/abilities/custom/gigas_frost/ice_volley.ron @@ -12,4 +12,5 @@ BasicRanged( projectile_speed: 25.0, num_projectiles: 5, projectile_spread: 0.07, + move_efficiency: 0.3, ) \ No newline at end of file diff --git a/assets/common/abilities/custom/harvester/explodingpumpkin.ron b/assets/common/abilities/custom/harvester/explodingpumpkin.ron index 95f1404eba..3f8df89196 100644 --- a/assets/common/abilities/custom/harvester/explodingpumpkin.ron +++ b/assets/common/abilities/custom/harvester/explodingpumpkin.ron @@ -13,4 +13,5 @@ BasicRanged( projectile_speed: 30.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/icedrake/icebombs.ron b/assets/common/abilities/custom/icedrake/icebombs.ron new file mode 100644 index 0000000000..8279f494c9 --- /dev/null +++ b/assets/common/abilities/custom/icedrake/icebombs.ron @@ -0,0 +1,15 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.6, + recover_duration: 1.0, + projectile: Frostball( + damage: 30.0, + radius: 3.0, + min_falloff: 0.1, + ), + projectile_body: Object(IceBomb), + projectile_speed: 25.0, + num_projectiles: 3, + projectile_spread: 0.07, + move_efficiency: 0.75, +) diff --git a/assets/common/abilities/custom/icedrake/icebreath.ron b/assets/common/abilities/custom/icedrake/icebreath.ron new file mode 100644 index 0000000000..b4181ffab2 --- /dev/null +++ b/assets/common/abilities/custom/icedrake/icebreath.ron @@ -0,0 +1,19 @@ +BasicBeam( + buildup_duration: 0.8, + recover_duration: 0.5, + beam_duration: 0.5, + damage: 10.0, + tick_rate: 3.0, + range: 15.0, + max_angle: 22.5, + damage_effect: Some(Buff(( + kind: Frozen, + dur_secs: 10.0, + strength: Value(0.25), + chance: 0.25, + ))), + energy_regen: 0, + energy_drain: 0, + ori_rate: 0.3, + specifier: Frost, +) diff --git a/assets/common/abilities/custom/icedrake/icy_bite.ron b/assets/common/abilities/custom/icedrake/icy_bite.ron new file mode 100644 index 0000000000..83a6cefee0 --- /dev/null +++ b/assets/common/abilities/custom/icedrake/icy_bite.ron @@ -0,0 +1,31 @@ +ComboMelee2( + strikes: [ + ( + melee_constructor: ( + kind: Bash( + damage: 40, + poise: 28, + knockback: 3, + energy_regen: 0, + ), + range: 2.0, + angle: 60.0, + damage_effect: Some(Buff(( + kind: Frozen, + dur_secs: 8.0, + strength: Value(0.3), + chance: 1.0, + ))), + ), + buildup_duration: 1.2, + swing_duration: 0.07, + hit_timing: 0.5, + recover_duration: 0.8, + movement: ( + swing: Some(Forward(3.0)), + ), + ori_modifier: 0.6, + ), + ], + energy_cost_per_strike: 0, +) diff --git a/assets/common/abilities/custom/icedrake/multi_bite.ron b/assets/common/abilities/custom/icedrake/multi_bite.ron new file mode 100644 index 0000000000..33276cdc93 --- /dev/null +++ b/assets/common/abilities/custom/icedrake/multi_bite.ron @@ -0,0 +1,65 @@ +ComboMelee2( + strikes: [ + ( + melee_constructor: ( + kind: Bash( + damage: 28, + poise: 15, + knockback: 3, + energy_regen: 0, + ), + range: 2.2, + angle: 30.0, + ), + buildup_duration: 1.3, + swing_duration: 0.07, + hit_timing: 0.5, + recover_duration: 0.6, + movement: ( + swing: Some(Forward(2.0)), + ), + ori_modifier: 0.65, + ), + ( + melee_constructor: ( + kind: Bash( + damage: 28, + poise: 18, + knockback: 3, + energy_regen: 0, + ), + range: 2.2, + angle: 30.0, + ), + buildup_duration: 0.8, + swing_duration: 0.07, + hit_timing: 0.5, + recover_duration: 0.6, + movement: ( + swing: Some(Forward(1.5)), + ), + ori_modifier: 0.65, + ), + ( + melee_constructor: ( + kind: Bash( + damage: 28, + poise: 20, + knockback: 3, + energy_regen: 0, + ), + range: 2.2, + angle: 30.0, + ), + buildup_duration: 0.8, + swing_duration: 0.07, + hit_timing: 0.5, + recover_duration: 0.6, + movement: ( + swing: Some(Forward(1.5)), + ), + ori_modifier: 0.65, + ), + ], + energy_cost_per_strike: 0, +) diff --git a/assets/common/abilities/custom/maneater/poisonball.ron b/assets/common/abilities/custom/maneater/poisonball.ron index d36d3902b9..4500a40c56 100644 --- a/assets/common/abilities/custom/maneater/poisonball.ron +++ b/assets/common/abilities/custom/maneater/poisonball.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 70.0, num_projectiles: 3, projectile_spread: 0.2, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/mindflayer/necroticsphere.ron b/assets/common/abilities/custom/mindflayer/necroticsphere.ron index 3327dc170d..e25d4a1a84 100644 --- a/assets/common/abilities/custom/mindflayer/necroticsphere.ron +++ b/assets/common/abilities/custom/mindflayer/necroticsphere.ron @@ -11,5 +11,6 @@ BasicRanged( projectile_speed: 100.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/quadlowranged/firebomb.ron b/assets/common/abilities/custom/quadlowranged/firebomb.ron index 4c5bd4ae78..e04816c4a6 100644 --- a/assets/common/abilities/custom/quadlowranged/firebomb.ron +++ b/assets/common/abilities/custom/quadlowranged/firebomb.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 70.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/quadmedjump/leap.ron b/assets/common/abilities/custom/quadmedjump/leap.ron index 5eab6d153a..5b0114b4c2 100644 --- a/assets/common/abilities/custom/quadmedjump/leap.ron +++ b/assets/common/abilities/custom/quadmedjump/leap.ron @@ -17,4 +17,5 @@ LeapMelee( ), forward_leap_strength: 45.0, vertical_leap_strength: 10.0, + specifier: None, ) diff --git a/assets/common/abilities/custom/quadmedjump/quickleap.ron b/assets/common/abilities/custom/quadmedjump/quickleap.ron index 914646e60c..e9190469a1 100644 --- a/assets/common/abilities/custom/quadmedjump/quickleap.ron +++ b/assets/common/abilities/custom/quadmedjump/quickleap.ron @@ -17,4 +17,5 @@ LeapMelee( ), forward_leap_strength: 20.0, vertical_leap_strength: 5.0, + specifier: None, ) diff --git a/assets/common/abilities/custom/roshwalr/freezeshockwave.ron b/assets/common/abilities/custom/roshwalr/freezeshockwave.ron index cd70c59205..a93c2f5efc 100644 --- a/assets/common/abilities/custom/roshwalr/freezeshockwave.ron +++ b/assets/common/abilities/custom/roshwalr/freezeshockwave.ron @@ -17,8 +17,8 @@ Shockwave( ori_rate: 1.0, damage_effect: Some(Buff(( kind: Frozen, - dur_secs: 4.0, - strength: DamageFraction(0.1), + dur_secs: 5.0, + strength: DamageFraction(0.06), chance: 1, ))), ) diff --git a/assets/common/abilities/custom/seawyvern/seabomb.ron b/assets/common/abilities/custom/seawyvern/seabomb.ron index 925d74104d..9bfb0a07ad 100644 --- a/assets/common/abilities/custom/seawyvern/seabomb.ron +++ b/assets/common/abilities/custom/seawyvern/seabomb.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 60.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/turret/arrows.ron b/assets/common/abilities/custom/turret/arrows.ron index 5d3bbb1494..5996477d69 100644 --- a/assets/common/abilities/custom/turret/arrows.ron +++ b/assets/common/abilities/custom/turret/arrows.ron @@ -12,4 +12,5 @@ BasicRanged( projectile_speed: 130.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/tursus_claws/basic.ron b/assets/common/abilities/custom/tursus/tursus_claws.ron similarity index 100% rename from assets/common/abilities/custom/tursus_claws/basic.ron rename to assets/common/abilities/custom/tursus/tursus_claws.ron diff --git a/assets/common/abilities/custom/tursus/tusk_bash_leap.ron b/assets/common/abilities/custom/tursus/tusk_bash_leap.ron new file mode 100644 index 0000000000..df6adbd18f --- /dev/null +++ b/assets/common/abilities/custom/tursus/tusk_bash_leap.ron @@ -0,0 +1,21 @@ +LeapMelee( + energy_cost: 35.0, + buildup_duration: 1.5, + movement_duration: 0.6, + swing_duration: 0.15, + recover_duration: 0.2, + melee_constructor: ( + kind: Bash( + damage: 25.0, + poise: 40.0, + knockback: 25.0, + energy_regen: 0.0, + ), + range: 4.5, + angle: 360.0, + multi_target: Some(Normal), + ), + forward_leap_strength: 20.0, + vertical_leap_strength: 10.0, + specifier: None, +) diff --git a/assets/common/abilities/custom/tursus/tusk_stab.ron b/assets/common/abilities/custom/tursus/tusk_stab.ron new file mode 100644 index 0000000000..be8e46ac7b --- /dev/null +++ b/assets/common/abilities/custom/tursus/tusk_stab.ron @@ -0,0 +1,24 @@ +ChargedMelee( + energy_cost: 0, + energy_drain: 0, + melee_constructor: ( + kind: Stab( + damage: 10.0, + poise: 50.0, + knockback: 0.0, + energy_regen: 0.0, + ), + scaled: Some(Stab( + damage: 90.0, + poise: 150.0, + knockback: 0.0, + energy_regen: 0.0, + )), + range: 3.5, + angle: 45.0, + ), + charge_duration: 1.6, + swing_duration: 0.1, + hit_timing: 0.8, + recover_duration: 1.0, +) diff --git a/assets/common/abilities/custom/wealdwyvern/firebomb.ron b/assets/common/abilities/custom/wealdwyvern/firebomb.ron index 9862e7f0bf..cb309e83ec 100644 --- a/assets/common/abilities/custom/wealdwyvern/firebomb.ron +++ b/assets/common/abilities/custom/wealdwyvern/firebomb.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 60.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/wendigomagic/frostbomb.ron b/assets/common/abilities/custom/wendigomagic/frostbomb.ron index 42ac390148..8d7494c572 100644 --- a/assets/common/abilities/custom/wendigomagic/frostbomb.ron +++ b/assets/common/abilities/custom/wendigomagic/frostbomb.ron @@ -15,4 +15,5 @@ BasicRanged( projectile_speed: 60.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/yeti/frostbreath.ron b/assets/common/abilities/custom/yeti/frostbreath.ron index 0574864e5f..25abaef325 100644 --- a/assets/common/abilities/custom/yeti/frostbreath.ron +++ b/assets/common/abilities/custom/yeti/frostbreath.ron @@ -2,7 +2,7 @@ BasicBeam( buildup_duration: 1.2, recover_duration: 0.5, beam_duration: 0.5, - damage: 1.5, + damage: 1.7, tick_rate: 5.0, range: 15.0, max_angle: 30.0, diff --git a/assets/common/abilities/custom/yeti/icespikes.ron b/assets/common/abilities/custom/yeti/icespikes.ron index a1452397b4..11baff03c9 100644 --- a/assets/common/abilities/custom/yeti/icespikes.ron +++ b/assets/common/abilities/custom/yeti/icespikes.ron @@ -3,7 +3,7 @@ Shockwave( buildup_duration: 0.9, swing_duration: 0.15, recover_duration: 2.0, - damage: 15.0, + damage: 18.0, poise_damage: 10, knockback: (strength: 18.0, direction: Up), shockwave_angle: 90.0, diff --git a/assets/common/abilities/custom/yeti/snowball.ron b/assets/common/abilities/custom/yeti/snowball.ron index f422698a0a..a23735dba2 100644 --- a/assets/common/abilities/custom/yeti/snowball.ron +++ b/assets/common/abilities/custom/yeti/snowball.ron @@ -3,7 +3,7 @@ BasicRanged( buildup_duration: 0.75, recover_duration: 1.8, projectile: Snowball( - damage: 30.0, + damage: 35.0, radius: 5.0, min_falloff: 0.7, ), @@ -11,4 +11,5 @@ BasicRanged( projectile_speed: 60.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/custom/yeti/strike.ron b/assets/common/abilities/custom/yeti/strike.ron index ef4792e52f..911bb6f07d 100644 --- a/assets/common/abilities/custom/yeti/strike.ron +++ b/assets/common/abilities/custom/yeti/strike.ron @@ -5,7 +5,7 @@ BasicMelee( recover_duration: 2.0, melee_constructor: ( kind: Bash( - damage: 10.0, + damage: 18.0, poise: 50.0, knockback: 50.0, energy_regen: 0.0, diff --git a/assets/common/abilities/debug/possess.ron b/assets/common/abilities/debug/possess.ron index 022d8b3b38..3d274e7938 100644 --- a/assets/common/abilities/debug/possess.ron +++ b/assets/common/abilities/debug/possess.ron @@ -11,4 +11,5 @@ BasicRanged( projectile_speed: 100.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) \ No newline at end of file diff --git a/assets/common/abilities/gnarling/blowgun/dart.ron b/assets/common/abilities/gnarling/blowgun/dart.ron index bbc473fdf4..448db67ec7 100644 --- a/assets/common/abilities/gnarling/blowgun/dart.ron +++ b/assets/common/abilities/gnarling/blowgun/dart.ron @@ -12,4 +12,5 @@ BasicRanged( projectile_speed: 80.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/gnarling/chieftain/firebarrage.ron b/assets/common/abilities/gnarling/chieftain/firebarrage.ron index bd703b01e9..aa86a12942 100644 --- a/assets/common/abilities/gnarling/chieftain/firebarrage.ron +++ b/assets/common/abilities/gnarling/chieftain/firebarrage.ron @@ -12,4 +12,5 @@ BasicRanged( projectile_speed: 25, num_projectiles: 8, projectile_spread: 0.125, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/hammer/leap.ron b/assets/common/abilities/hammer/leap.ron index d9a220b645..b694dd80bf 100644 --- a/assets/common/abilities/hammer/leap.ron +++ b/assets/common/abilities/hammer/leap.ron @@ -17,4 +17,5 @@ LeapMelee( ), forward_leap_strength: 20.0, vertical_leap_strength: 8.0, + specifier: None, ) diff --git a/assets/common/abilities/music/wildskin_drum.ron b/assets/common/abilities/music/wildskin_drum.ron new file mode 100644 index 0000000000..18f0a9b200 --- /dev/null +++ b/assets/common/abilities/music/wildskin_drum.ron @@ -0,0 +1,4 @@ +Music( + play_duration: 0.4, + ori_modifier: 1.0, +) \ No newline at end of file diff --git a/assets/common/abilities/staff/firebomb.ron b/assets/common/abilities/staff/firebomb.ron index 94696d0a6e..7a8db685c7 100644 --- a/assets/common/abilities/staff/firebomb.ron +++ b/assets/common/abilities/staff/firebomb.ron @@ -12,4 +12,5 @@ BasicRanged( projectile_speed: 60.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/abilities/staffsimple/firebomb.ron b/assets/common/abilities/staffsimple/firebomb.ron index 5a0a996df2..6b816dee4d 100644 --- a/assets/common/abilities/staffsimple/firebomb.ron +++ b/assets/common/abilities/staffsimple/firebomb.ron @@ -16,4 +16,5 @@ BasicRanged( projectile_speed: 60.0, num_projectiles: 1, projectile_spread: 0.0, + move_efficiency: 0.3, ) diff --git a/assets/common/entity/dungeon/adlet/elder.ron b/assets/common/entity/dungeon/adlet/elder.ron new file mode 100644 index 0000000000..c88884ae00 --- /dev/null +++ b/assets/common/entity/dungeon/adlet/elder.ron @@ -0,0 +1,11 @@ +#![enable(implicit_some)] +( + name: Automatic, + body: RandomWith("adlet_elder"), + alignment: Alignment(Enemy), + loot: MultiDrop(LootTable("common.loot_tables.dungeon.tier-1.elder"), 2, 2), + inventory: ( + loadout: FromBody, + ), + meta: [], +) \ No newline at end of file diff --git a/assets/common/entity/dungeon/tier-1/hunter.ron b/assets/common/entity/dungeon/adlet/hunter.ron similarity index 73% rename from assets/common/entity/dungeon/tier-1/hunter.ron rename to assets/common/entity/dungeon/adlet/hunter.ron index 7770cad9ef..136c518ea4 100644 --- a/assets/common/entity/dungeon/tier-1/hunter.ron +++ b/assets/common/entity/dungeon/adlet/hunter.ron @@ -6,11 +6,11 @@ loot: LootTable("common.loot_tables.dungeon.tier-1.enemy"), inventory: ( loadout: Inline(( - inherit: Asset("common.loadout.dungeon.tier-1.hunter"), + inherit: Asset("common.loadout.dungeon.adlet.hunter"), active_hands: InHands((Item("common.items.npc_weapons.biped_small.adlet.hunter"), None)), )), ), meta: [ - SkillSetAsset("common.skillset.preset.rank1.fullskill"), + SkillSetAsset("common.skillset.preset.rank1.general"), ], ) \ No newline at end of file diff --git a/assets/common/entity/dungeon/tier-1/icepicker.ron b/assets/common/entity/dungeon/adlet/icepicker.ron similarity index 73% rename from assets/common/entity/dungeon/tier-1/icepicker.ron rename to assets/common/entity/dungeon/adlet/icepicker.ron index 10a6132417..cab341de74 100644 --- a/assets/common/entity/dungeon/tier-1/icepicker.ron +++ b/assets/common/entity/dungeon/adlet/icepicker.ron @@ -6,11 +6,11 @@ loot: LootTable("common.loot_tables.dungeon.tier-1.enemy"), inventory: ( loadout: Inline(( - inherit: Asset("common.loadout.dungeon.tier-1.icepicker"), + inherit: Asset("common.loadout.dungeon.adlet.icepicker"), active_hands: InHands((Item("common.items.npc_weapons.biped_small.adlet.icepicker"), None)), )), ), meta: [ - SkillSetAsset("common.skillset.preset.rank1.fullskill"), + SkillSetAsset("common.skillset.preset.rank1.general"), ], ) \ No newline at end of file diff --git a/assets/common/entity/dungeon/tier-1/tracker.ron b/assets/common/entity/dungeon/adlet/tracker.ron similarity index 73% rename from assets/common/entity/dungeon/tier-1/tracker.ron rename to assets/common/entity/dungeon/adlet/tracker.ron index 8dc37a6d99..2d3fcc7604 100644 --- a/assets/common/entity/dungeon/tier-1/tracker.ron +++ b/assets/common/entity/dungeon/adlet/tracker.ron @@ -6,11 +6,11 @@ loot: LootTable("common.loot_tables.dungeon.tier-1.enemy"), inventory: ( loadout: Inline(( - inherit: Asset("common.loadout.dungeon.tier-1.tracker"), + inherit: Asset("common.loadout.dungeon.adlet.tracker"), active_hands: InHands((Item("common.items.npc_weapons.biped_small.adlet.tracker"), None)), )), ), meta: [ - SkillSetAsset("common.skillset.preset.rank1.fullskill"), + SkillSetAsset("common.skillset.preset.rank1.general"), ], ) \ No newline at end of file diff --git a/assets/common/entity/dungeon/tier-1/boss.ron b/assets/common/entity/dungeon/adlet/yeti.ron similarity index 100% rename from assets/common/entity/dungeon/tier-1/boss.ron rename to assets/common/entity/dungeon/adlet/yeti.ron diff --git a/assets/common/entity/dungeon/tier-1/rat.ron b/assets/common/entity/dungeon/tier-1/rat.ron deleted file mode 100644 index e5e670aebd..0000000000 --- a/assets/common/entity/dungeon/tier-1/rat.ron +++ /dev/null @@ -1,11 +0,0 @@ -#![enable(implicit_some)] -( - name: Name("Rat"), - body: RandomWith("rat"), - alignment: Alignment(Enemy), - loot: LootTable("common.loot_tables.creature.quad_small.generic"), - inventory: ( - loadout: FromBody, - ), - meta: [], -) \ No newline at end of file diff --git a/assets/common/entity/wild/aggressive/frostfang.ron b/assets/common/entity/wild/aggressive/frostfang.ron index 526fda35e2..5fbe36b6c2 100644 --- a/assets/common/entity/wild/aggressive/frostfang.ron +++ b/assets/common/entity/wild/aggressive/frostfang.ron @@ -3,7 +3,7 @@ name: Automatic, body: RandomWith("frostfang"), alignment: Alignment(Enemy), - loot: LootTable("common.loot_tables.creature.quad_medium.ice"), + loot: LootTable("common.loot_tables.creature.quad_medium.frostfang"), inventory: ( loadout: FromBody, ), diff --git a/assets/common/entity/wild/aggressive/icedrake.ron b/assets/common/entity/wild/aggressive/icedrake.ron index 7d0c331207..b088f5c79f 100644 --- a/assets/common/entity/wild/aggressive/icedrake.ron +++ b/assets/common/entity/wild/aggressive/icedrake.ron @@ -3,7 +3,7 @@ name: Automatic, body: RandomWith("icedrake"), alignment: Alignment(Enemy), - loot: LootTable("common.loot_tables.creature.quad_medium.ice"), + loot: LootTable("common.loot_tables.creature.quad_medium.icedrake"), inventory: ( loadout: FromBody, ), diff --git a/assets/common/items/armor/misc/head/bear_bonnet.ron b/assets/common/items/armor/misc/head/bear_bonnet.ron new file mode 100644 index 0000000000..c1c1ffc2a4 --- /dev/null +++ b/assets/common/items/armor/misc/head/bear_bonnet.ron @@ -0,0 +1,14 @@ +ItemDef( + name: "Bear Bonnet", + description: "Wearing the guise of a ferocious bear, its fury becomes your own.", + kind: Armor(( + kind: Head, + stats: Direct(( + protection: Some(Normal(12.0)), + crit_power: Some(0.0095), + stealth: Some(0.21), + )), + )), + quality: Epic, + tags: [], +) diff --git a/assets/common/items/armor/misc/head/howl_cowl.ron b/assets/common/items/armor/misc/head/howl_cowl.ron new file mode 100644 index 0000000000..bd5cd34afb --- /dev/null +++ b/assets/common/items/armor/misc/head/howl_cowl.ron @@ -0,0 +1,14 @@ +ItemDef( + name: "Howl Cowl", + description: "Wearing the guise of a fearsome wolf befits a fearsome hunter.", + kind: Armor(( + kind: Head, + stats: Direct(( + protection: Some(Normal(4.0)), + crit_power: Some(0.065), + stealth: Some(0.21), + )), + )), + quality: Epic, + tags: [], +) diff --git a/assets/common/items/keys/bone_key.ron b/assets/common/items/keys/bone_key.ron new file mode 100644 index 0000000000..19f91f83a0 --- /dev/null +++ b/assets/common/items/keys/bone_key.ron @@ -0,0 +1,11 @@ +ItemDef( + name: "Bone Key", + description: "Used to open bone locks. Will break after use.", + kind: Utility( + kind: Key, + ), + amount: 1, + quality: Common, + tags: [Utility], +) + diff --git a/assets/common/items/npc_armor/biped_large/tursus.ron b/assets/common/items/npc_armor/biped_large/tursus.ron new file mode 100644 index 0000000000..cd061e2201 --- /dev/null +++ b/assets/common/items/npc_armor/biped_large/tursus.ron @@ -0,0 +1,13 @@ +ItemDef( + name: "Tursus Skin", + description: "Born with it", + kind: Armor(( + kind: Chest, + stats: Direct(( + protection: Some(Normal(65.0)), + poise_resilience: Some(Normal(10.0)), + )), + )), + quality: Moderate, + tags: [], +) \ No newline at end of file diff --git a/assets/common/items/npc_armor/biped_large/yeti.ron b/assets/common/items/npc_armor/biped_large/yeti.ron index 12e262a993..c7699d210d 100644 --- a/assets/common/items/npc_armor/biped_large/yeti.ron +++ b/assets/common/items/npc_armor/biped_large/yeti.ron @@ -4,8 +4,8 @@ ItemDef( kind: Armor(( kind: Chest, stats: Direct(( - protection: Some(Normal(0.0)), - poise_resilience: Some(Normal(0.0)), + protection: Some(Normal(100.0)), + poise_resilience: Some(Normal(30.0)), )), )), quality: Legendary, diff --git a/assets/common/items/npc_armor/quadruped_medium/frostfang.ron b/assets/common/items/npc_armor/quadruped_medium/frostfang.ron new file mode 100644 index 0000000000..53f52283f2 --- /dev/null +++ b/assets/common/items/npc_armor/quadruped_medium/frostfang.ron @@ -0,0 +1,14 @@ +ItemDef( + name: "Frostfang's Thick Skin", + description: "testing123", + kind: Armor(( + kind: Chest, + stats: Direct(( + protection: Some(Normal(60.0)), + poise_resilience: Some(Normal(2.0)), + energy_max: Some(10), + )), + )), + quality: Epic, + tags: [], +) \ No newline at end of file diff --git a/assets/common/items/npc_armor/quadruped_medium/roshwalr.ron b/assets/common/items/npc_armor/quadruped_medium/roshwalr.ron index 717e55706f..45b253b657 100644 --- a/assets/common/items/npc_armor/quadruped_medium/roshwalr.ron +++ b/assets/common/items/npc_armor/quadruped_medium/roshwalr.ron @@ -4,7 +4,7 @@ ItemDef( kind: Armor(( kind: Chest, stats: Direct(( - protection: Some(Normal(100.0)), + protection: Some(Normal(75.0)), poise_resilience: Some(Normal(2.0)), energy_max: Some(10), )), diff --git a/assets/common/items/npc_weapons/biped_small/adlet/hunter.ron b/assets/common/items/npc_weapons/biped_small/adlet/hunter.ron index b44ff0ba1e..201ecef4be 100644 --- a/assets/common/items/npc_weapons/biped_small/adlet/hunter.ron +++ b/assets/common/items/npc_weapons/biped_small/adlet/hunter.ron @@ -5,11 +5,11 @@ ItemDef( kind: Spear, hands: Two, stats: ( - equip_time_secs: 0.0, - power: 1.05, + equip_time_secs: 0.1, + power: 1.0, effect_power: 1.0, - speed: 0.75, - crit_chance: 0.07589286, + speed: 1.0, + crit_chance: 0.1, range: 1.0, energy_efficiency: 1.0, buff_strength: 1.0, @@ -17,5 +17,5 @@ ItemDef( )), quality: Low, tags: [], - ability_spec: None, + ability_spec: Some(Custom("Adlet Hunter")), ) \ No newline at end of file diff --git a/assets/common/items/npc_weapons/biped_small/adlet/icepicker.ron b/assets/common/items/npc_weapons/biped_small/adlet/icepicker.ron index 8cc7147596..ce4731aae5 100644 --- a/assets/common/items/npc_weapons/biped_small/adlet/icepicker.ron +++ b/assets/common/items/npc_weapons/biped_small/adlet/icepicker.ron @@ -1,15 +1,15 @@ ItemDef( - name: "Icepicker Axe", + name: "Icepicker Pick", description: "", kind: Tool(( - kind: Axe, + kind: Pick, hands: Two, stats: ( - equip_time_secs: 0.0, - power: 0.7, - effect_power: 0.8, - speed: 0.7, - crit_chance: 0.05059524, + equip_time_secs: 0.1, + power: 1.0, + effect_power: 1.0, + speed: 1.0, + crit_chance: 0.1, range: 1.0, energy_efficiency: 1.0, buff_strength: 1.0, @@ -17,5 +17,5 @@ ItemDef( )), quality: Low, tags: [], - ability_spec: Some(Custom("Axe Simple")), + ability_spec: Some(Custom("Adlet Icepicker")), ) \ No newline at end of file diff --git a/assets/common/items/npc_weapons/biped_small/adlet/tracker.ron b/assets/common/items/npc_weapons/biped_small/adlet/tracker.ron index 6bce3d66e8..189ff272e3 100644 --- a/assets/common/items/npc_weapons/biped_small/adlet/tracker.ron +++ b/assets/common/items/npc_weapons/biped_small/adlet/tracker.ron @@ -6,16 +6,16 @@ ItemDef( hands: Two, stats: ( equip_time_secs: 0.0, - power: 1.05, - effect_power: 0.8, - speed: 0.4, - crit_chance: 0.08406594, + power: 1.0, + effect_power: 1.0, + speed: 1.0, + crit_chance: 0.1, range: 1.0, energy_efficiency: 1.0, buff_strength: 1.0, ), )), - quality: Moderate, + quality: Low, tags: [], - ability_spec: Some(Custom("Bow Simple")), + ability_spec: Some(Custom("Adlet Tracker")), ) \ No newline at end of file diff --git a/assets/common/items/npc_weapons/sword/adlet_elder_sword.ron b/assets/common/items/npc_weapons/sword/adlet_elder_sword.ron new file mode 100644 index 0000000000..5828d6e723 --- /dev/null +++ b/assets/common/items/npc_weapons/sword/adlet_elder_sword.ron @@ -0,0 +1,21 @@ +ItemDef( + name: "Adlet Elder Sword", + description: "Placeholder", + kind: Tool(( + kind: Sword, + hands: One, + stats: ( + equip_time_secs: 0.5, + power: 1.5, + effect_power: 1.0, + speed: 0.75, + crit_chance: 0.0625, + range: 1.0, + energy_efficiency: 1.0, + buff_strength: 1.0, + ), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Adlet Elder")), +) \ No newline at end of file diff --git a/assets/common/items/npc_weapons/unique/frostfang.ron b/assets/common/items/npc_weapons/unique/frostfang.ron new file mode 100644 index 0000000000..2b77c4e198 --- /dev/null +++ b/assets/common/items/npc_weapons/unique/frostfang.ron @@ -0,0 +1,21 @@ +ItemDef( + name: "Frostfang", + description: "testing123", + kind: Tool(( + kind: Natural, + hands: Two, + stats: ( + equip_time_secs: 0.01, + power: 1.0, + effect_power: 1.0, + speed: 1.0, + crit_chance: 0.0625, + range: 1.0, + energy_efficiency: 1.0, + buff_strength: 1.0, + ), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Frostfang")), +) \ No newline at end of file diff --git a/assets/common/items/npc_weapons/unique/icedrake.ron b/assets/common/items/npc_weapons/unique/icedrake.ron new file mode 100644 index 0000000000..7073c5f42b --- /dev/null +++ b/assets/common/items/npc_weapons/unique/icedrake.ron @@ -0,0 +1,21 @@ +ItemDef( + name: "Ice Drake", + description: "testing123", + kind: Tool(( + kind: Natural, + hands: Two, + stats: ( + equip_time_secs: 0.01, + power: 1.0, + effect_power: 1.0, + speed: 1.0, + crit_chance: 0.0625, + range: 1.0, + energy_efficiency: 1.0, + buff_strength: 1.0, + ), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Ice Drake")), +) \ No newline at end of file diff --git a/assets/common/items/tool/instruments/wildskin_drum.ron b/assets/common/items/tool/instruments/wildskin_drum.ron new file mode 100644 index 0000000000..4bd3dfbcc2 --- /dev/null +++ b/assets/common/items/tool/instruments/wildskin_drum.ron @@ -0,0 +1,21 @@ +ItemDef( + name: "Wildskin Drum", + description: "one, two, you know what to do!", + kind: Tool(( + kind: Instrument, + hands: Two, + stats: ( + equip_time_secs: 0.4, + power: 0.0, + effect_power: 1.0, + speed: 1.0, + crit_chance: 0.0, + range: 0.0, + energy_efficiency: 1.0, + buff_strength: 1.0, + ), + )), + quality: Legendary, + tags: [Utility], + ability_spec: Some(Custom("WildskinDrum")), +) \ No newline at end of file diff --git a/assets/common/loadout/dungeon/tier-1/hunter.ron b/assets/common/loadout/dungeon/adlet/hunter.ron similarity index 100% rename from assets/common/loadout/dungeon/tier-1/hunter.ron rename to assets/common/loadout/dungeon/adlet/hunter.ron diff --git a/assets/common/loadout/dungeon/tier-1/icepicker.ron b/assets/common/loadout/dungeon/adlet/icepicker.ron similarity index 100% rename from assets/common/loadout/dungeon/tier-1/icepicker.ron rename to assets/common/loadout/dungeon/adlet/icepicker.ron diff --git a/assets/common/loadout/dungeon/tier-1/tracker.ron b/assets/common/loadout/dungeon/adlet/tracker.ron similarity index 100% rename from assets/common/loadout/dungeon/tier-1/tracker.ron rename to assets/common/loadout/dungeon/adlet/tracker.ron diff --git a/assets/common/loot_tables/creature/quad_medium/frostfang.ron b/assets/common/loot_tables/creature/quad_medium/frostfang.ron new file mode 100644 index 0000000000..9ad48c6199 --- /dev/null +++ b/assets/common/loot_tables/creature/quad_medium/frostfang.ron @@ -0,0 +1,5 @@ +[ + (1.0, MultiDrop(Item("common.items.crafting_ing.animal_misc.icy_fang"), 1, 2)), + (1.0, MultiDrop(Item("common.items.crafting_ing.animal_misc.sharp_fang"), 1, 3)), + (1.0, Item("common.items.crafting_ing.hide.tough_hide")), +] \ No newline at end of file diff --git a/assets/common/loot_tables/creature/quad_medium/icedrake.ron b/assets/common/loot_tables/creature/quad_medium/icedrake.ron new file mode 100644 index 0000000000..a89e908895 --- /dev/null +++ b/assets/common/loot_tables/creature/quad_medium/icedrake.ron @@ -0,0 +1,5 @@ +[ + (1.0, MultiDrop(Item("common.items.crafting_ing.animal_misc.icy_fang"), 1, 2)), + (1.0, MultiDrop(Item("common.items.food.meat.tough_raw"), 1, 2)), + (1.0, Item("common.items.crafting_ing.hide.carapace")), +] \ No newline at end of file diff --git a/assets/common/loot_tables/dungeon/tier-1/boss.ron b/assets/common/loot_tables/dungeon/tier-1/boss.ron index 07d279f000..a07296fcfc 100644 --- a/assets/common/loot_tables/dungeon/tier-1/boss.ron +++ b/assets/common/loot_tables/dungeon/tier-1/boss.ron @@ -1,8 +1,9 @@ [ // Weapons - (4.0, LootTable("common.loot_tables.weapons.tier-2")), + (4.0, LootTable("common.loot_tables.weapons.tier-3")), // Armor - (4.0, LootTable("common.loot_tables.armor.tier-2")), + (4.0, LootTable("common.loot_tables.armor.tier-3")), // Misc - (2.0, Item("common.items.armor.misc.neck.pendant_of_protection")), + (1.0, Item("common.items.armor.misc.head.bear_bonnet")), + (1.0, Item("common.items.armor.misc.head.howl_cowl")), ] \ No newline at end of file diff --git a/assets/common/loot_tables/dungeon/tier-1/chest.ron b/assets/common/loot_tables/dungeon/tier-1/chest.ron index a0e5d20283..ff9dcc057a 100644 --- a/assets/common/loot_tables/dungeon/tier-1/chest.ron +++ b/assets/common/loot_tables/dungeon/tier-1/chest.ron @@ -6,6 +6,7 @@ // Currency (3.0, MultiDrop(Item("common.items.utility.coins"), 20, 50)), // Materials + (2.0, MultiDrop(Item("common.items.mineral.ore.veloritefrag"), 3, 10)), (1.0, MultiDrop(Item("common.items.crafting_ing.cloth.wool"), 3, 10)), (1.0, MultiDrop(Item("common.items.crafting_ing.leather.thick_leather"), 3, 10)), (1.0, MultiDrop(Item("common.items.mineral.ingot.iron"), 3, 10)), diff --git a/assets/common/loot_tables/dungeon/tier-1/elder.ron b/assets/common/loot_tables/dungeon/tier-1/elder.ron new file mode 100644 index 0000000000..e84bc8e7d6 --- /dev/null +++ b/assets/common/loot_tables/dungeon/tier-1/elder.ron @@ -0,0 +1,6 @@ +[ + // Misc + (5.0, Item("common.items.keys.bone_key")), + (1.0, LootTable("common.loot_tables.dungeon.tier-1.elder_extra")), + +] \ No newline at end of file diff --git a/assets/common/loot_tables/dungeon/tier-1/elder_extra.ron b/assets/common/loot_tables/dungeon/tier-1/elder_extra.ron new file mode 100644 index 0000000000..414283ba42 --- /dev/null +++ b/assets/common/loot_tables/dungeon/tier-1/elder_extra.ron @@ -0,0 +1,9 @@ +[ + // Weapons + (4.0, LootTable("common.loot_tables.weapons.tier-2")), + // Armor + (4.0, LootTable("common.loot_tables.armor.tier-2")), + // Misc + (2.0, Item("common.items.armor.misc.neck.pendant_of_protection")), + (0.5, Item("common.items.tool.instruments.wildskin_drum")), +] \ No newline at end of file diff --git a/assets/common/loot_tables/dungeon/tier-3/chest.ron b/assets/common/loot_tables/dungeon/tier-3/chest.ron index 544f723012..907562063d 100644 --- a/assets/common/loot_tables/dungeon/tier-3/chest.ron +++ b/assets/common/loot_tables/dungeon/tier-3/chest.ron @@ -10,6 +10,7 @@ (1.0, MultiDrop(Item("common.items.crafting_ing.hide.carapace"), 2, 6)), (1.0, MultiDrop(Item("common.items.mineral.ingot.cobalt"), 2, 6)), (1.0, MultiDrop(Item("common.items.log.ironwood"), 3, 7)), + (2.0, MultiDrop(Item("common.items.mineral.ore.velorite"), 3, 10)), // Consumables (2.0, LootTable("common.loot_tables.consumable.moderate")), ] diff --git a/assets/common/npc_names.ron b/assets/common/npc_names.ron index 793c879fa7..8723d82d26 100644 --- a/assets/common/npc_names.ron +++ b/assets/common/npc_names.ron @@ -965,6 +965,10 @@ gigas_frost: ( keyword: "gigas_frost", generic: "Frost Gigas" + ), + adlet_elder: ( + keyword: "adlet_elder", + generic: "Adlet Elder" ) ) ), diff --git a/assets/server/manifests/kits.ron b/assets/server/manifests/kits.ron index e1719de679..4bedef0acd 100644 --- a/assets/server/manifests/kits.ron +++ b/assets/server/manifests/kits.ron @@ -401,5 +401,6 @@ (Item("common.items.tool.instruments.melodica"),1), (Item("common.items.tool.instruments.sitar"),1), (Item("common.items.tool.instruments.washboard"),1), + (Item("common.items.tool.instruments.wildskin_drum"),1), ], }) diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index 8d5036dee4..a97129db14 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -871,6 +871,20 @@ threshold: 0.5, subtitle: "subtitle-instrument_washboard", ), + Music(Instrument, Custom("WildskinDrum")): ( + files: [ + "voxygen.audio.sfx.instrument.wildskin_drum.wildskin_drum_c", + "voxygen.audio.sfx.instrument.wildskin_drum.wildskin_drum_d", + "voxygen.audio.sfx.instrument.wildskin_drum.wildskin_drum_e", + "voxygen.audio.sfx.instrument.wildskin_drum.wildskin_drum_f", + "voxygen.audio.sfx.instrument.wildskin_drum.wildskin_drum_g", + "voxygen.audio.sfx.instrument.wildskin_drum.wildskin_drum_a", + "voxygen.audio.sfx.instrument.wildskin_drum.wildskin_drum_b", + "voxygen.audio.sfx.instrument.wildskin_drum.wildskin_drum_c1", + ], + threshold: 0.5, + subtitle: "subtitle-instrument_wildskin_drum", + ), Inventory(CollectedTool(Instrument)): ( files: [ "voxygen.audio.sfx.inventory.add_item", @@ -1261,6 +1275,12 @@ threshold: 0.2, subtitle: "subtitle-arrow_shot", ), + Yeet: ( + files: [ + "voxygen.audio.sfx.abilities.yeet", + ], + threshold: 0.3, + ), FireShot: ( files: [ "voxygen.audio.sfx.abilities.fire_shot_1", @@ -1283,6 +1303,18 @@ threshold: 0.2, subtitle: "subtitle-arrow_hit", ), + Klonk: ( + files: [ + "voxygen.audio.sfx.abilities.klonk", + ], + threshold: 0.4, + ), + SmashKlonk: ( + files: [ + "voxygen.audio.sfx.abilities.smashklonk", + ], + threshold: 0.4, + ), SkillPointGain: ( files: [ "voxygen.audio.sfx.character.level_up_sound_-_shorter_wind_up", @@ -1398,6 +1430,12 @@ threshold: 1.25, subtitle: "subtitle-attack-laser_beam", ), + Woosh: ( + files: [ + "voxygen.audio.sfx.abilities.woosh", + ], + threshold: 0.4, + ), CyclopsCharge: ( files: [ "voxygen.audio.sfx.abilities.cyclops_charge", diff --git a/assets/voxygen/audio/sfx/abilities/klonk.ogg b/assets/voxygen/audio/sfx/abilities/klonk.ogg new file mode 100644 index 0000000000..7a79f98030 --- /dev/null +++ b/assets/voxygen/audio/sfx/abilities/klonk.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dddee8abeda944c7c592ee70fe9f59137f4a799a96afce4c6816093b4b50b237 +size 8731 diff --git a/assets/voxygen/audio/sfx/abilities/smashklonk.ogg b/assets/voxygen/audio/sfx/abilities/smashklonk.ogg new file mode 100644 index 0000000000..2043bffc1b --- /dev/null +++ b/assets/voxygen/audio/sfx/abilities/smashklonk.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:62982830b6aa9055b0b6951df8885ea8f34fe33c9d9cb109a79d3af70a2d258b +size 9514 diff --git a/assets/voxygen/audio/sfx/abilities/woosh.ogg b/assets/voxygen/audio/sfx/abilities/woosh.ogg new file mode 100644 index 0000000000..4f228a016d --- /dev/null +++ b/assets/voxygen/audio/sfx/abilities/woosh.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c955d12401c80ef55ca1804185c95b029612afc72edef6524160f3e27f95430d +size 8678 diff --git a/assets/voxygen/audio/sfx/abilities/yeet.ogg b/assets/voxygen/audio/sfx/abilities/yeet.ogg new file mode 100644 index 0000000000..cd8622b004 --- /dev/null +++ b/assets/voxygen/audio/sfx/abilities/yeet.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:128d24a35a123735632c25cafa5814f388c521800b27ee5a40750e51270a2b81 +size 8063 diff --git a/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_a.ogg b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_a.ogg new file mode 100644 index 0000000000..4753f8e2f6 --- /dev/null +++ b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_a.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f57ed7d32dd21317967928b4ae5201aaf84520b45c1b17213c6d08fc1c2048f6 +size 26664 diff --git a/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_b.ogg b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_b.ogg new file mode 100644 index 0000000000..b75a459528 --- /dev/null +++ b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_b.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:20e9fd6af3a3ccb9f63ab1a35a65ca2bb919e8d23839ded680ca51b2a7fa8329 +size 25761 diff --git a/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_c.ogg b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_c.ogg new file mode 100644 index 0000000000..aa176f7738 --- /dev/null +++ b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_c.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29ca58b21f067bc46b7fa784d1daeed7e40c39eaf8ea58f459301a05003a1ee0 +size 26022 diff --git a/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_c1.ogg b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_c1.ogg new file mode 100644 index 0000000000..07441e7c50 --- /dev/null +++ b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_c1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e27c3480fe2e5b595f48fe9b564c7a47359175af90ed9a31a9c6bb3a01d510b3 +size 28035 diff --git a/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_d.ogg b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_d.ogg new file mode 100644 index 0000000000..4c9355f893 --- /dev/null +++ b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_d.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac60347e501b63d21705e5b9bf4b16c6ad90fe0cff3afdba0027a2946a354229 +size 29074 diff --git a/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_e.ogg b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_e.ogg new file mode 100644 index 0000000000..a880a8a251 --- /dev/null +++ b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_e.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e131fdff9f896a64bf2759786457c3cf45f2df7deb56e16827be547d1d1628a9 +size 28018 diff --git a/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_f.ogg b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_f.ogg new file mode 100644 index 0000000000..52df04e3bc --- /dev/null +++ b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_f.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33edfb0788106d068fa92836340508cba35d4b35dcba1c3dd315ccb2f1c73fc6 +size 27497 diff --git a/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_g.ogg b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_g.ogg new file mode 100644 index 0000000000..f8cfdf6bd6 --- /dev/null +++ b/assets/voxygen/audio/sfx/instrument/wildskin_drum/wildskin_drum_g.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:847f3b58bdbb142b98effe237730642779d728f40617520f6552ad6c12c6968f +size 26797 diff --git a/assets/voxygen/element/ui/map/buttons/adlet.png b/assets/voxygen/element/ui/map/buttons/adlet.png new file mode 100644 index 0000000000..008fb734af --- /dev/null +++ b/assets/voxygen/element/ui/map/buttons/adlet.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0150ada3a06879253b193c6d5e1887750966e5bc673604b2394cf1938d48b989 +size 3328 diff --git a/assets/voxygen/element/ui/map/buttons/adlet_bg.png b/assets/voxygen/element/ui/map/buttons/adlet_bg.png new file mode 100644 index 0000000000..000c24bd30 --- /dev/null +++ b/assets/voxygen/element/ui/map/buttons/adlet_bg.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93dc70e4dd1249a44b68501aa42a3f7f10b4674f728f71de9578598900f0837a +size 4958 diff --git a/assets/voxygen/element/ui/map/buttons/adlet_hover.png b/assets/voxygen/element/ui/map/buttons/adlet_hover.png new file mode 100644 index 0000000000..2b01f399cc --- /dev/null +++ b/assets/voxygen/element/ui/map/buttons/adlet_hover.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cff6c4dca92b9e3edb491a27cd695164c00848a010a18e80a6f8c4f9490d25db +size 3384 diff --git a/assets/voxygen/i18n/en/hud/map.ftl b/assets/voxygen/i18n/en/hud/map.ftl index a19834bf2d..a3a5e1bb09 100644 --- a/assets/voxygen/i18n/en/hud/map.ftl +++ b/assets/voxygen/i18n/en/hud/map.ftl @@ -33,4 +33,5 @@ hud-map-zoom_minimap_explanation = the area around you in higher detail hud-map-gnarling = Gnarling Fortification hud-map-chapel_site = Sea Chapel +hud-map-adlet = Adlet Stronghold hud-map-placed_by = Placed by { $name } \ No newline at end of file diff --git a/assets/voxygen/i18n/en/hud/subtitles.ftl b/assets/voxygen/i18n/en/hud/subtitles.ftl index 469593b339..8d12305a3c 100644 --- a/assets/voxygen/i18n/en/hud/subtitles.ftl +++ b/assets/voxygen/i18n/en/hud/subtitles.ftl @@ -80,6 +80,7 @@ subtitle-instrument_sitar = Sitar playing subtitle-instrument_guitar = Guitar playing subtitle-instrument_dark_guitar = Dark Guitar playing subtitle-instrument_washboard = Washboard playing +subtitle-instrument_wildskin_drum = Wildskin Drum playing subtitle-pickup_instrument = Pickup instrument subtitle-explosion = Explosion diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 4e97e7e35b..40dd2f6f69 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -1626,6 +1626,10 @@ "voxel.weapon.tool.wooden_lyre", (0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.0, ), + Simple("common.items.tool.instruments.wildskin_drum"): VoxTrans( + "voxel.weapon.tool.wildskin_drum", + (0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.0, + ), Simple("common.items.tool.instruments.icy_talharpa"): VoxTrans( "voxel.weapon.tool.icy_talharpa", (0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.0, @@ -2859,6 +2863,14 @@ "voxel.armor.misc.head.hog_hood", (0.0, 2.0, -0.0), (-110.0, 210.0,15.0), 1.4, ), + Simple("common.items.armor.misc.head.howl_cowl"): VoxTrans( + "voxel.armor.misc.head.howl_cowl", + (0.0, 2.0, -0.0), (-110.0, 210.0,15.0), 1.4, + ), + Simple("common.items.armor.misc.head.bear_bonnet"): VoxTrans( + "voxel.armor.misc.head.bear_bonnet", + (0.0, 2.0, -0.0), (-110.0, 210.0,15.0), 1.4, + ), Simple("common.items.armor.misc.head.bamboo_twig"): VoxTrans( "voxel.armor.misc.head.bamboo_twig", (2.5, 4.5, 0.0), (-120.0, -240.0,1.0), 2.3, @@ -4833,6 +4845,10 @@ "voxel.object.key_rusty-0", (0.0, 0.0, 0.0), (-100.0, 250.0, 15.0), 1.0, ), + Simple("common.items.keys.bone_key"): VoxTrans( + "voxel.object.key_bone", + (0.0, 0.0, 0.0), (-100.0, 250.0, 15.0), 1.0, + ), // Lockpicks Simple("common.items.utility.lockpick_0"): VoxTrans( "voxel.object.lockpick", diff --git a/assets/voxygen/voxel/armor/misc/head/bear_bonnet.vox b/assets/voxygen/voxel/armor/misc/head/bear_bonnet.vox new file mode 100644 index 0000000000..e0ffa72a05 --- /dev/null +++ b/assets/voxygen/voxel/armor/misc/head/bear_bonnet.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13b0013379e13b4044c45b95239fba4b097e3ece48f6f098f9fb812632553795 +size 49796 diff --git a/assets/voxygen/voxel/armor/misc/head/hog_hood.vox b/assets/voxygen/voxel/armor/misc/head/hog_hood.vox index a80867f58f..e8dfa70265 100644 --- a/assets/voxygen/voxel/armor/misc/head/hog_hood.vox +++ b/assets/voxygen/voxel/armor/misc/head/hog_hood.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ccfdc45490dca6bc25082c7d628e5ffdcd22d95f275571d1b6a9f4d38fce0d9c -size 69303 +oid sha256:d0738e33edc8153634fc6a375350112928cb140d05edf18c3d8e70b3676653f1 +size 52556 diff --git a/assets/voxygen/voxel/armor/misc/head/howl_cowl.vox b/assets/voxygen/voxel/armor/misc/head/howl_cowl.vox new file mode 100644 index 0000000000..09714cdaf1 --- /dev/null +++ b/assets/voxygen/voxel/armor/misc/head/howl_cowl.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63ac53b1741894697fc65ec165d234427d08fc086212b633fedf8ce798b4cb55 +size 52152 diff --git a/assets/voxygen/voxel/biped_large_central_manifest.ron b/assets/voxygen/voxel/biped_large_central_manifest.ron index a1a32c1529..90ba359938 100644 --- a/assets/voxygen/voxel/biped_large_central_manifest.ron +++ b/assets/voxygen/voxel/biped_large_central_manifest.ron @@ -1196,4 +1196,56 @@ central: ("armor.empty"), ) ), + (AdletElder, Male): ( + head: ( + offset: (-9.0, 0.0, -8.0), + central: ("npc.adlet.elder.male.head"), + ), + torso_upper: ( + offset: (-7.0, -7.0, -6.5), + central: ("npc.adlet.elder.male.torso_upper"), + ), + torso_lower: ( + offset: (-6.0, -6.0, -5.0), + central: ("npc.adlet.elder.male.torso_lower"), + ), + jaw: ( + offset: (-5.0, 0.0, -3.5), + central: ("npc.adlet.elder.male.jaw"), + ), + tail: ( + offset: (-2.0, -17.0, -1.0), + central: ("npc.adlet.elder.male.tail"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (AdletElder, Female): ( + head: ( + offset: (-9.0, 0.0, -8.0), + central: ("npc.adlet.elder.male.head"), + ), + torso_upper: ( + offset: (-7.0, -7.0, -6.5), + central: ("npc.adlet.elder.male.torso_upper"), + ), + torso_lower: ( + offset: (-6.0, -6.0, -5.0), + central: ("npc.adlet.elder.male.torso_lower"), + ), + jaw: ( + offset: (-5.0, 0.0, -3.5), + central: ("npc.adlet.elder.male.jaw"), + ), + tail: ( + offset: (-2.0, -17.0, -1.0), + central: ("npc.adlet.elder.male.tail"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), }) diff --git a/assets/voxygen/voxel/biped_large_lateral_manifest.ron b/assets/voxygen/voxel/biped_large_lateral_manifest.ron index 89239a3ce3..151932ef03 100644 --- a/assets/voxygen/voxel/biped_large_lateral_manifest.ron +++ b/assets/voxygen/voxel/biped_large_lateral_manifest.ron @@ -1563,4 +1563,72 @@ lateral: ("npc.gigas_frost.male.foot_r"), ) ), + (AdletElder, Male): ( + shoulder_l: ( + offset: (-3.5, -5.0, -6.0), + lateral: ("npc.adlet.elder.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-3.5, -5.0, -6.0), + lateral: ("npc.adlet.elder.male.shoulder_r"), + ), + hand_l: ( + offset: (-2.5, -2.5, -12.0), + lateral: ("npc.adlet.elder.male.hand_l"), + ), + hand_r: ( + offset: (-2.5, -2.5, -12.0), + lateral: ("npc.adlet.elder.male.hand_r"), + ), + leg_l: ( + offset: (-2.0, -1.5, -6.5), + lateral: ("npc.adlet.elder.male.leg_l"), + ), + leg_r: ( + offset: (-2.0, -1.5, -6.5), + lateral: ("npc.adlet.elder.male.leg_r"), + ), + foot_l: ( + offset: (-2.0, -5.0, -5.0), + lateral: ("npc.adlet.elder.male.foot_l"), + ), + foot_r: ( + offset: (-2.0, -5.0, -5.0), + lateral: ("npc.adlet.elder.male.foot_r"), + ), + ), + (AdletElder, Female): ( + shoulder_l: ( + offset: (-3.5, -5.0, -6.0), + lateral: ("npc.adlet.elder.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-3.5, -5.0, -6.0), + lateral: ("npc.adlet.elder.male.shoulder_r"), + ), + hand_l: ( + offset: (-2.5, -2.5, -12.0), + lateral: ("npc.adlet.elder.male.hand_l"), + ), + hand_r: ( + offset: (-2.5, -2.5, -12.0), + lateral: ("npc.adlet.elder.male.hand_r"), + ), + leg_l: ( + offset: (-2.0, -1.5, -6.5), + lateral: ("npc.adlet.elder.male.leg_l"), + ), + leg_r: ( + offset: (-2.0, -1.5, -6.5), + lateral: ("npc.adlet.elder.male.leg_r"), + ), + foot_l: ( + offset: (-2.0, -5.0, -5.0), + lateral: ("npc.adlet.elder.male.foot_l"), + ), + foot_r: ( + offset: (-2.0, -5.0, -5.0), + lateral: ("npc.adlet.elder.male.foot_r"), + ), + ), }) \ No newline at end of file diff --git a/assets/voxygen/voxel/biped_weapon_manifest.ron b/assets/voxygen/voxel/biped_weapon_manifest.ron index f0b62fd118..f49c3b2acd 100644 --- a/assets/voxygen/voxel/biped_weapon_manifest.ron +++ b/assets/voxygen/voxel/biped_weapon_manifest.ron @@ -1124,6 +1124,10 @@ vox_spec: ("weapon.tool.wooden_lyre", (-3.5, -7.0, 6.0)), color: None ), + Tool("common.items.tool.instruments.wildskin_drum"): ( + vox_spec: ("weapon.tool.wildskin_drum", (-4.5, -8.0, 8.0)), + color: None + ), Tool("common.items.tool.instruments.icy_talharpa"): ( vox_spec: ("weapon.tool.icy_talharpa", (-3.5, -7.0, 6.0)), color: None @@ -1610,6 +1614,10 @@ vox_spec: ("weapon.sword.long_2h_saurok", (-2.5, -4.0, -5.0)), color: None ), + Tool("common.items.npc_weapons.sword.adlet_elder_sword"): ( + vox_spec: ("weapon.sword.adlet_elder_sword", (-2.5, -5.0, -5.0)), + color: None + ), Tool("common.items.npc_weapons.bow.saurok_bow"): ( vox_spec: ("weapon.bow.longbow_saurok", (-2.5, -4.0, -16.5)), color: None diff --git a/assets/voxygen/voxel/humanoid_armor_head_manifest.ron b/assets/voxygen/voxel/humanoid_armor_head_manifest.ron index 59f4cb98ab..49db7892f0 100644 --- a/assets/voxygen/voxel/humanoid_armor_head_manifest.ron +++ b/assets/voxygen/voxel/humanoid_armor_head_manifest.ron @@ -125,6 +125,104 @@ (Orc, Female, "common.items.armor.misc.head.hog_hood"): ( vox_spec: ("armor.misc.head.hog_hood", (-3.0, -6.0, -8.0)), color: None + ), +// + (Human, Male, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-4.0, -5, -8.0)), + color: None + ), + (Human, Female, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-4.0, -4, -8.0)), + color: None + ), + (Elf, Male, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-3.0, -5.0, -8.0)), + color: None + ), + (Elf, Female, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-3.0, -5.0, -8.0)), + color: None + ), + (Dwarf, Male, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-5.0, -4.0, -8)), + color: None + ), + (Dwarf, Female, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-5.0, -4.0, -8.0)), + color: None + ), + (Danari, Male, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-2.0, -5, -6)), + color: None + ), + (Danari, Female, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-2.0, -5, -6)), + color: None + ), + (Draugr, Male, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-6.0, -5.0, -7.0)), + color: None + ), + (Draugr, Female, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-6.0, -5.0, -8.0)), + color: None + ), + (Orc, Male, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-3.0, -4.0, -6.0)), + color: None + ), + (Orc, Female, "common.items.armor.misc.head.howl_cowl"): ( + vox_spec: ("armor.misc.head.howl_cowl", (-3.0, -6.0, -8.0)), + color: None + ), +// + (Human, Male, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-4.0, -5, -8.0)), + color: None + ), + (Human, Female, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-4.0, -4, -8.0)), + color: None + ), + (Elf, Male, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-3.0, -5.0, -8.0)), + color: None + ), + (Elf, Female, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-3.0, -5.0, -8.0)), + color: None + ), + (Dwarf, Male, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-5.0, -4.0, -8)), + color: None + ), + (Dwarf, Female, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-5.0, -4.0, -8.0)), + color: None + ), + (Danari, Male, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-2.0, -5, -6)), + color: None + ), + (Danari, Female, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-2.0, -5, -6)), + color: None + ), + (Draugr, Male, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-6.0, -5.0, -7.0)), + color: None + ), + (Draugr, Female, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-6.0, -5.0, -8.0)), + color: None + ), + (Orc, Male, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-3.0, -4.0, -6.0)), + color: None + ), + (Orc, Female, "common.items.armor.misc.head.bear_bonnet"): ( + vox_spec: ("armor.misc.head.bear_bonnet", (-3.0, -6.0, -8.0)), + color: None ), // (Danari, Male, "common.items.armor.misc.head.bamboo_twig"): ( diff --git a/assets/voxygen/voxel/item_drop_manifest.ron b/assets/voxygen/voxel/item_drop_manifest.ron index 74580e5c1d..09c0f7cead 100644 --- a/assets/voxygen/voxel/item_drop_manifest.ron +++ b/assets/voxygen/voxel/item_drop_manifest.ron @@ -402,6 +402,7 @@ Simple("common.items.tool.instruments.flute"): "voxel.weapon.tool.wooden_flute", Simple("common.items.tool.instruments.glass_flute"): "voxel.weapon.tool.glass_flute", Simple("common.items.tool.instruments.lyre"): "voxel.weapon.tool.wooden_lyre", + Simple("common.items.tool.instruments.wildskin_drum"): "voxel.weapon.tool.wildskin_drum", Simple("common.items.tool.instruments.icy_talharpa"): "voxel.weapon.tool.icy_talharpa", Simple("common.items.tool.instruments.washboard"): "voxel.weapon.tool.washboard", Simple("common.items.tool.instruments.kalimba"): "voxel.weapon.tool.wooden_kalimba", @@ -720,6 +721,8 @@ //Hats Simple("common.items.armor.witch.hat"): "voxel.armor.witch.hat", Simple("common.items.armor.misc.head.hog_hood"): "voxel.armor.misc.head.hog_hood", + Simple("common.items.armor.misc.head.howl_cowl"): "voxel.armor.misc.head.howl_cowl", + Simple("common.items.armor.misc.head.bear_bonnet"): "voxel.armor.misc.head.bear_bonnet", Simple("common.items.armor.misc.head.bamboo_twig"): "voxel.armor.misc.head.bamboo_twig", Simple("common.items.armor.pirate.hat"): "voxel.armor.pirate.hat", Simple("common.items.armor.misc.head.bandana.thief"): "voxel.armor.misc.head.bandana.thief", @@ -922,6 +925,7 @@ Simple("common.items.log.wood"): "voxel.sprite.wood.item.wood", // Keys and Lockpicks Simple("common.items.keys.rusty_tower_key"): "voxel.object.key_rusty-0", + Simple("common.items.keys.bone_key"): "voxel.object.key_bone", Simple("common.items.utility.lockpick_0"): "voxel.object.lockpick", // Gliders Simple("common.items.glider.cloverleaf"): "voxel.glider.starter", diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/foot_l.vox b/assets/voxygen/voxel/npc/adlet/elder/male/foot_l.vox new file mode 100644 index 0000000000..a071ebe8b6 --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/foot_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aaf97980257a834b38860033c0fbcf169ec84ebdbfc70824813b8decc24f58ae +size 1436 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/foot_r.vox b/assets/voxygen/voxel/npc/adlet/elder/male/foot_r.vox new file mode 100644 index 0000000000..e7bae8b15e --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78d76756a1ecd676a83c20d802f89515555c038d76feb10f6ccbb7e8c48ef8c9 +size 1436 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/hand_l.vox b/assets/voxygen/voxel/npc/adlet/elder/male/hand_l.vox new file mode 100644 index 0000000000..4f492aef7d --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/hand_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36bbea359131910bbe5acd34718480b5f2d2d25c08286ad4e7f638dba38d399f +size 1820 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/hand_r.vox b/assets/voxygen/voxel/npc/adlet/elder/male/hand_r.vox new file mode 100644 index 0000000000..b5597c8358 --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/hand_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b021031c0b9a8a73ebce85c43088cc35f6d53f817d4607db33ba2632427eeb00 +size 1820 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/head.vox b/assets/voxygen/voxel/npc/adlet/elder/male/head.vox new file mode 100644 index 0000000000..0ee0ee186e --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:155f244174a7326a97738dd20b328636c00c8686d8c560c15a9f9b7e4a4caa30 +size 7296 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/jaw.vox b/assets/voxygen/voxel/npc/adlet/elder/male/jaw.vox new file mode 100644 index 0000000000..de8eb3b4a1 --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/jaw.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:386dacc42d342c45a52aced485412ce83bc14606f95491358b381e148dba31e5 +size 1904 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/leg_l.vox b/assets/voxygen/voxel/npc/adlet/elder/male/leg_l.vox new file mode 100644 index 0000000000..b8f3967b5d --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/leg_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66ea51edca61208b3944534887db1eeb67cbb2f2baaa2b07ca4d328e17527e6e +size 1480 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/leg_r.vox b/assets/voxygen/voxel/npc/adlet/elder/male/leg_r.vox new file mode 100644 index 0000000000..af90299b14 --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:365f190ea3fc126edce7d39ee3fd04b129298335375dcfebed4b3e7c49289257 +size 1480 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/shoulder_l.vox b/assets/voxygen/voxel/npc/adlet/elder/male/shoulder_l.vox new file mode 100644 index 0000000000..c34ea3f4a3 --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/shoulder_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:073189557c23a57868cc6976df51c9e13c079b22753150a9f82a398490ad548a +size 1776 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/shoulder_r.vox b/assets/voxygen/voxel/npc/adlet/elder/male/shoulder_r.vox new file mode 100644 index 0000000000..75c58ed43a --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/shoulder_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2b48b64e4a71b5e8dca82a4db902d320bd6ac48b36c2e6eebd71910e1faebf80 +size 1776 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/tail.vox b/assets/voxygen/voxel/npc/adlet/elder/male/tail.vox new file mode 100644 index 0000000000..5d91cea921 --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/tail.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f6640c086d4203a9f5b0c6939a21f12f98c7f0b36f042504644cf6ae1196f481 +size 1856 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/torso_lower.vox b/assets/voxygen/voxel/npc/adlet/elder/male/torso_lower.vox new file mode 100644 index 0000000000..8df0d62fd1 --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/torso_lower.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d4f4b1ec191aedc21cb56e860c5c5f0a643d7c8dce7070b060822d957db49e9e +size 2112 diff --git a/assets/voxygen/voxel/npc/adlet/elder/male/torso_upper.vox b/assets/voxygen/voxel/npc/adlet/elder/male/torso_upper.vox new file mode 100644 index 0000000000..55a0a36a37 --- /dev/null +++ b/assets/voxygen/voxel/npc/adlet/elder/male/torso_upper.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1ab6c529db0fa6d69480e902287285f27ffd2bd944ba09693895bda872f01e5 +size 5224 diff --git a/assets/voxygen/voxel/object/key_bone.vox b/assets/voxygen/voxel/object/key_bone.vox new file mode 100644 index 0000000000..a49e5093e8 --- /dev/null +++ b/assets/voxygen/voxel/object/key_bone.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d9a478f5ad192b64bae7cb4b1db21562e2ee008e5b0e24065299fc39868b240 +size 1232 diff --git a/assets/voxygen/voxel/object_manifest.ron b/assets/voxygen/voxel/object_manifest.ron index c21739a238..2ac3753d9c 100644 --- a/assets/voxygen/voxel/object_manifest.ron +++ b/assets/voxygen/voxel/object_manifest.ron @@ -919,4 +919,24 @@ central: ("armor.empty"), ) ), + AdletSpear: ( + bone0: ( + offset: (5.0, -22.0, -2.0), + central: ("weapon.projectile.adlet-spear"), + ), + bone1: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + AdletTrap: ( + bone0: ( + offset: (-4.5, -2.5, 0.0), + central: ("weapon.projectile.adlet-trap"), + ), + bone1: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), }) diff --git a/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_0.vox b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_0.vox new file mode 100644 index 0000000000..0daf87dfcf --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d63b1400c334992691c2a2ab4f73fb4da6f4951a4a2dbfbe851d4e608c243575 +size 4556 diff --git a/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_1.vox b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_1.vox new file mode 100644 index 0000000000..89e3ec457b --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f6f0e848cbb6e58730037fe0534f23c50594a0e290c758cd1b47ce801fbf5f4 +size 4888 diff --git a/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_2.vox b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_2.vox new file mode 100644 index 0000000000..b2caf6de7d --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ad74b7df72c51cf48924fbb04bce3d8c764504d5d9eaad6ced5c6dd216ed96d +size 8660 diff --git a/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_3.vox b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_3.vox new file mode 100644 index 0000000000..0daf87dfcf --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d63b1400c334992691c2a2ab4f73fb4da6f4951a4a2dbfbe851d4e608c243575 +size 4556 diff --git a/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_4.vox b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_4.vox new file mode 100644 index 0000000000..89e3ec457b --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f6f0e848cbb6e58730037fe0534f23c50594a0e290c758cd1b47ce801fbf5f4 +size 4888 diff --git a/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_5.vox b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_5.vox new file mode 100644 index 0000000000..b2caf6de7d --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/glow_ice_crystal_5.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ad74b7df72c51cf48924fbb04bce3d8c764504d5d9eaad6ced5c6dd216ed96d +size 8660 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_0.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_0.vox new file mode 100644 index 0000000000..23071bc1f1 --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7e29e2d9bc65e99db4863061fb600c41be7297a2fa583d12167d2dc59bd6c56 +size 1400 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_1.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_1.vox new file mode 100644 index 0000000000..4fd36da9cc --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da1e9fba0c978de0e32e740569ece65165f4b7f1f6e8c12c758e01eba44dc398 +size 1680 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_10.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_10.vox new file mode 100644 index 0000000000..eff7868806 --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_10.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a974dbdfb26b19399380df3879e13b2e5a70d9b8a56de9980cb9a3d6aa19af5 +size 1536 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_11.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_11.vox new file mode 100644 index 0000000000..b1d1df5f2a --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_11.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8712ac888aca908dae2d757b48bb04594e1c733c2f8d4dffed5246f91dec6273 +size 1420 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_2.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_2.vox new file mode 100644 index 0000000000..69c2aec997 --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea19c6aaa8dd5f9846faa2a368b43fb3d77fae98dfaac28b3084c063f4eca719 +size 1684 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_3.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_3.vox new file mode 100644 index 0000000000..7dafdb9bdf --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:991f55bdbe60ccec7bc77f9f0d0db3141f8af8f2b3a0d6bc305480879a406f61 +size 1448 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_4.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_4.vox new file mode 100644 index 0000000000..69c2aec997 --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea19c6aaa8dd5f9846faa2a368b43fb3d77fae98dfaac28b3084c063f4eca719 +size 1684 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_5.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_5.vox new file mode 100644 index 0000000000..7dafdb9bdf --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_5.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:991f55bdbe60ccec7bc77f9f0d0db3141f8af8f2b3a0d6bc305480879a406f61 +size 1448 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_6.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_6.vox new file mode 100644 index 0000000000..39d9e273d9 --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_6.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8f890bcb88d2957248a502c8f4cb276bae86fec9bc626c238bfea4837e1b4c0 +size 2012 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_7.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_7.vox new file mode 100644 index 0000000000..ae2dddddcd --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_7.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d888bbb86c0f68c5385e917c9b96cbb4786a3c4bb72b1c63f61e2a8a090dd8e5 +size 2128 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_8.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_8.vox new file mode 100644 index 0000000000..d8756d2dac --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_8.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad25d35fe4bc355dd4054309734c8febb670ce07eb7992aef127c19c0bc132cf +size 1496 diff --git a/assets/voxygen/voxel/sprite/crystal/ice_crystal_9.vox b/assets/voxygen/voxel/sprite/crystal/ice_crystal_9.vox new file mode 100644 index 0000000000..a3a4c2f5a1 --- /dev/null +++ b/assets/voxygen/voxel/sprite/crystal/ice_crystal_9.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9025cfffee42462ff9425f3063f31a11e5a17b88899b163d22023b7b33f92075 +size 1328 diff --git a/assets/voxygen/voxel/sprite/furniture/bone_door_block.vox b/assets/voxygen/voxel/sprite/furniture/bone_door_block.vox new file mode 100644 index 0000000000..40838b7b22 --- /dev/null +++ b/assets/voxygen/voxel/sprite/furniture/bone_door_block.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51f7278762e3ca9790386740a5310ae1abc0b6f7fdbfa9cd301305b659dbc8ce +size 2996 diff --git a/assets/voxygen/voxel/sprite/furniture/bone_keyhole_block.vox b/assets/voxygen/voxel/sprite/furniture/bone_keyhole_block.vox new file mode 100644 index 0000000000..51a22767ef --- /dev/null +++ b/assets/voxygen/voxel/sprite/furniture/bone_keyhole_block.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74fa661df843d5053b3dd01c002941cfc2f05b738ef0be3e7a8f61980e03c39c +size 3112 diff --git a/assets/voxygen/voxel/sprite/misc/fireblock.vox b/assets/voxygen/voxel/sprite/misc/fireblock.vox new file mode 100644 index 0000000000..3fbe2ecf7e --- /dev/null +++ b/assets/voxygen/voxel/sprite/misc/fireblock.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6f56d34164138bd6b095c1bd03942bc7ef28c99a5784870999e1559b8397a7c4 +size 6420 diff --git a/assets/voxygen/voxel/sprite_manifest.ron b/assets/voxygen/voxel/sprite_manifest.ron index 7e839871fc..25cf9057d9 100644 --- a/assets/voxygen/voxel/sprite_manifest.ron +++ b/assets/voxygen/voxel/sprite_manifest.ron @@ -1455,6 +1455,17 @@ Ember: Some(( ], wind_sway: 0.0, )), +// FireBlock +FireBlock: Some(( + variations: [ + ( + model: "voxygen.voxel.sprite.misc.fireblock", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ], + wind_sway: 0.0, +)), // Smoke dummy SmokeDummy: Some(( variations: [ @@ -3828,6 +3839,26 @@ KeyDoor: Some(( ], wind_sway: 0.0, )), +BoneKeyhole: Some(( + variations: [ + ( + model: "voxygen.voxel.sprite.furniture.bone_keyhole_block", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.5, 0.5, 0.5), + ), + ], + wind_sway: 0.0, +)), +BoneKeyDoor: Some(( + variations: [ + ( + model: "voxygen.voxel.sprite.furniture.bone_door_block", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.5, 0.5, 0.5), + ), + ], + wind_sway: 0.0, +)), // Bones Bones: Some(( variations: [ @@ -4285,4 +4316,104 @@ Candle: Some(( ], wind_sway: 0.0, )), +IceCrystal: Some(( + variations: [ + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_0", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_1", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_2", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_3", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_4", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_5", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_6", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_7", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_8", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_9", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_10", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.ice_crystal_11", + offset: (-5.5, -5.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ], + wind_sway: 0.0, +)), +GlowIceCrystal: Some(( + variations: [ + ( + model: "voxygen.voxel.sprite.crystal.glow_ice_crystal_0", + offset: (8.5, -9.0, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.glow_ice_crystal_1", + offset: (-8.0, -8.0, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.glow_ice_crystal_2", + offset: (-11.5, -12.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.glow_ice_crystal_3", + offset: (-8.5, -9.0, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.glow_ice_crystal_4", + offset: (-8.0, -8.0, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.crystal.glow_ice_crystal_5", + offset: (-11.5, -12.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ], + wind_sway: 0.0, +)), } diff --git a/assets/voxygen/voxel/weapon/projectile/adlet-spear.vox b/assets/voxygen/voxel/weapon/projectile/adlet-spear.vox new file mode 100644 index 0000000000..3af27ac05e --- /dev/null +++ b/assets/voxygen/voxel/weapon/projectile/adlet-spear.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51d1d713ac83f19ff82ea1bb4eb7c2a6cb918e2374c4d7e01a61dddff8f8ebb5 +size 1288 diff --git a/assets/voxygen/voxel/weapon/projectile/adlet-trap.vox b/assets/voxygen/voxel/weapon/projectile/adlet-trap.vox new file mode 100644 index 0000000000..b7358b7709 --- /dev/null +++ b/assets/voxygen/voxel/weapon/projectile/adlet-trap.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e0170b9cb61b349d9a64e38ec43f0e36645bb14ee39bb0c33307ed87803a2ab +size 1204 diff --git a/assets/voxygen/voxel/weapon/sword/adlet_elder_sword.vox b/assets/voxygen/voxel/weapon/sword/adlet_elder_sword.vox new file mode 100644 index 0000000000..35acfc1b6e --- /dev/null +++ b/assets/voxygen/voxel/weapon/sword/adlet_elder_sword.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcb0d9578ae636ae0cd9b839a992f7eee9913a1e7d7e712b7d2fbfd57b32ed37 +size 1356 diff --git a/assets/voxygen/voxel/weapon/tool/wildskin_drum.vox b/assets/voxygen/voxel/weapon/tool/wildskin_drum.vox new file mode 100644 index 0000000000..eb3d6236dc --- /dev/null +++ b/assets/voxygen/voxel/weapon/tool/wildskin_drum.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c3b5626549f5557d9e6419778d1c980460600371910d8fcd274afd6a2a328f6 +size 1772 diff --git a/assets/world/dungeon/difficulty_distribution.ron b/assets/world/dungeon/difficulty_distribution.ron index 789a94c2a9..7ebec5c934 100644 --- a/assets/world/dungeon/difficulty_distribution.ron +++ b/assets/world/dungeon/difficulty_distribution.ron @@ -16,7 +16,6 @@ /// 1) Set every probability to 0.0 and left one with any other number /// and you will have map full of dungeons of same level ([ - (1, 0.20), (2, 0.20), (3, 0.15), (4, 0.10), diff --git a/assets/world/manifests/site_structures/adlet/bonfire.ron b/assets/world/manifests/site_structures/adlet/bonfire.ron new file mode 100644 index 0000000000..2bc07864ff --- /dev/null +++ b/assets/world/manifests/site_structures/adlet/bonfire.ron @@ -0,0 +1,8 @@ +#![enable(unwrap_newtypes)] + +[ + ( + specifier: "world.structure.natural.bonfire", + center: (10, 10, 2) + ), +] diff --git a/assets/world/manifests/site_structures/adlet/igloo.ron b/assets/world/manifests/site_structures/adlet/igloo.ron new file mode 100644 index 0000000000..c391e400f5 --- /dev/null +++ b/assets/world/manifests/site_structures/adlet/igloo.ron @@ -0,0 +1,8 @@ +#![enable(unwrap_newtypes)] + +[ + ( + specifier: "world.structure.natural.igloo", + center: (14, 13, 3), + ), +] diff --git a/assets/world/structure/natural/bonfire.vox b/assets/world/structure/natural/bonfire.vox new file mode 100644 index 0000000000..1a36288dcd --- /dev/null +++ b/assets/world/structure/natural/bonfire.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5de790b6d1c722c78c7a857c52e18950e113d8e467da080ec14fdeadcc64ac10 +size 7728 diff --git a/common/net/src/msg/compression.rs b/common/net/src/msg/compression.rs index 6b6f41c63a..412b6b28f6 100644 --- a/common/net/src/msg/compression.rs +++ b/common/net/src/msg/compression.rs @@ -618,6 +618,11 @@ impl VoxelImageDecoding for TriPngEncoding Rgb { + r: 192, + g: 255, + b: 255, + }, Ice => Rgb { r: 150, g: 190, diff --git a/common/net/src/msg/world_msg.rs b/common/net/src/msg/world_msg.rs index c379ae0283..254bd69269 100644 --- a/common/net/src/msg/world_msg.rs +++ b/common/net/src/msg/world_msg.rs @@ -150,6 +150,7 @@ pub enum SiteKind { Gnarling, ChapelSite, Bridge, + Adlet, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 8e06a7e8aa..de5496bf9c 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -502,6 +502,7 @@ pub enum CharacterAbility { num_projectiles: u32, projectile_spread: f32, damage_effect: Option, + move_efficiency: f32, #[serde(default)] meta: AbilityMeta, }, @@ -594,6 +595,7 @@ pub enum CharacterAbility { forward_leap_strength: f32, vertical_leap_strength: f32, damage_effect: Option, + specifier: Option, #[serde(default)] meta: AbilityMeta, }, @@ -1019,6 +1021,7 @@ impl CharacterAbility { num_projectiles: _, projectile_spread: _, damage_effect: _, + move_efficiency: _, meta: _, } => { *buildup_duration /= stats.speed; @@ -1147,6 +1150,7 @@ impl CharacterAbility { forward_leap_strength: _, vertical_leap_strength: _, ref mut damage_effect, + specifier: _, meta: _, } => { *buildup_duration /= stats.speed; @@ -2169,6 +2173,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { num_projectiles, projectile_spread, damage_effect, + move_efficiency, meta: _, } => CharacterState::BasicRanged(basic_ranged::Data { static_data: basic_ranged::StaticData { @@ -2182,6 +2187,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { projectile_spread: *projectile_spread, ability_info, damage_effect: *damage_effect, + move_efficiency: *move_efficiency, }, timer: Duration::default(), stage_section: StageSection::Buildup, @@ -2338,6 +2344,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { forward_leap_strength, vertical_leap_strength, damage_effect, + specifier, meta: _, } => CharacterState::LeapMelee(leap_melee::Data { static_data: leap_melee::StaticData { @@ -2350,6 +2357,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { vertical_leap_strength: *vertical_leap_strength, ability_info, damage_effect: *damage_effect, + specifier: *specifier, }, timer: Duration::default(), stage_section: StageSection::Buildup, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 03209dbaeb..336c0bf629 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -289,6 +289,7 @@ impl Body { biped_large::Species::Mountaintroll => 600.0, biped_large::Species::Swamptroll => 600.0, biped_large::Species::Gigasfrost => 400.0, + biped_large::Species::AdletElder => 350.0, _ => 400.0, }, Body::BipedSmall(_) => 50.0, @@ -429,6 +430,7 @@ impl Body { biped_large::Species::Huskbrute => Vec3::new(4.6, 3.0, 5.0), biped_large::Species::Tursus => Vec3::new(4.0, 3.0, 4.0), biped_large::Species::Gigasfrost => Vec3::new(6.0, 3.0, 8.0), + biped_large::Species::AdletElder => Vec3::new(3.5, 3.0, 5.0), _ => Vec3::new(4.6, 3.0, 6.0), }, Body::BipedSmall(body) => match body.species { @@ -477,7 +479,7 @@ impl Body { quadruped_medium::Species::Lion => Vec3::new(2.0, 3.3, 2.0), quadruped_medium::Species::Moose => Vec3::new(2.0, 4.0, 2.5), quadruped_medium::Species::Bristleback => Vec3::new(2.0, 3.0, 2.0), - quadruped_medium::Species::Roshwalr => Vec3::new(2.0, 3.5, 2.2), + quadruped_medium::Species::Roshwalr => Vec3::new(3.4, 5.2, 3.7), quadruped_medium::Species::Saber => Vec3::new(2.0, 3.0, 2.0), quadruped_medium::Species::Tarasque => Vec3::new(2.0, 4.0, 2.6), quadruped_medium::Species::Yak => Vec3::new(2.0, 3.6, 3.0), @@ -792,7 +794,7 @@ impl Body { biped_large::Species::Dullahan => 600, biped_large::Species::Mindflayer => 1250, biped_large::Species::Tidalwarrior => 1600, - biped_large::Species::Yeti => 1200, + biped_large::Species::Yeti => 1800, biped_large::Species::Minotaur => 3000, biped_large::Species::Harvester => 1500, biped_large::Species::Blueoni => 240, @@ -801,6 +803,8 @@ impl Body { biped_large::Species::Cultistwarlord => 250, biped_large::Species::Cultistwarlock => 250, biped_large::Species::Gigasfrost => 20000, + biped_large::Species::AdletElder => 1500, + biped_large::Species::Tursus => 300, _ => 120, }, Body::BipedSmall(biped_small) => match biped_small.species { @@ -944,11 +948,17 @@ impl Body { BuffKind::Frozen => match self { Body::BipedLarge(b) => matches!( b.species, - biped_large::Species::Yeti | biped_large::Species::Gigasfrost + biped_large::Species::Yeti + | biped_large::Species::Gigasfrost + | biped_large::Species::Tursus ), Body::QuadrupedLow(q) => matches!(q.species, quadruped_low::Species::Icedrake), Body::BirdLarge(b) => matches!(b.species, bird_large::Species::FrostWyvern), Body::BipedSmall(b) => matches!(b.species, biped_small::Species::Boreal), + Body::QuadrupedMedium(b) => matches!( + b.species, + quadruped_medium::Species::Roshwalr | quadruped_medium::Species::Frostfang + ), _ => false, }, BuffKind::ProtectingWard => matches!(self, Body::Object(object::Body::BarrelOrgan)), diff --git a/common/src/comp/body/biped_large.rs b/common/src/comp/body/biped_large.rs index 8179dc7c1a..0cadddd284 100644 --- a/common/src/comp/body/biped_large.rs +++ b/common/src/comp/body/biped_large.rs @@ -73,6 +73,7 @@ make_case_elim!( Huskbrute = 20, Tursus = 21, Gigasfrost = 22, + AdletElder = 23, } ); @@ -104,6 +105,7 @@ pub struct AllSpecies { pub husk_brute: SpeciesMeta, pub tursus: SpeciesMeta, pub gigas_frost: SpeciesMeta, + pub adlet_elder: SpeciesMeta, } impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies { @@ -135,11 +137,12 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies Species::Huskbrute => &self.husk_brute, Species::Tursus => &self.tursus, Species::Gigasfrost => &self.gigas_frost, + Species::AdletElder => &self.adlet_elder, } } } -pub const ALL_SPECIES: [Species; 23] = [ +pub const ALL_SPECIES: [Species; 24] = [ Species::Ogre, Species::Cyclops, Species::Wendigo, @@ -163,6 +166,7 @@ pub const ALL_SPECIES: [Species; 23] = [ Species::Huskbrute, Species::Tursus, Species::Gigasfrost, + Species::AdletElder, ]; impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies { diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index f0983ee475..7f54895e01 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -104,6 +104,8 @@ make_case_elim!( SpectralSwordSmall = 89, SpectralSwordLarge = 90, LaserBeam = 91, + AdletSpear = 92, + AdletTrap = 93, } ); @@ -114,7 +116,7 @@ impl Body { } } -pub const ALL_OBJECTS: [Body; 92] = [ +pub const ALL_OBJECTS: [Body; 94] = [ Body::Arrow, Body::Bomb, Body::Scarecrow, @@ -207,6 +209,8 @@ pub const ALL_OBJECTS: [Body; 92] = [ Body::BarrelOrgan, Body::IceBomb, Body::LaserBeam, + Body::AdletSpear, + Body::AdletTrap, ]; impl From for super::Body { @@ -308,6 +312,8 @@ impl Body { Body::SpectralSwordSmall => "spectral_sword_small", Body::SpectralSwordLarge => "spectral_sword_large", Body::LaserBeam => "laser_beam", + Body::AdletSpear => "adlet_spear", + Body::AdletTrap => "adlet_trap", } } @@ -332,7 +338,9 @@ impl Body { | Body::Dart | Body::DagonBomb | Body::SpectralSwordSmall - | Body::SpectralSwordLarge => 500.0, + | Body::SpectralSwordLarge + | Body::AdletSpear + | Body::AdletTrap => 500.0, Body::Bomb => 2000.0, // I have no idea what it's supposed to be Body::Crate => 300.0, // let's say it's a lot of wood and maybe some contents Body::Scarecrow => 900.0, @@ -427,6 +435,8 @@ impl Body { Body::GnarlingTotemRed | Body::GnarlingTotemGreen | Body::GnarlingTotemWhite => 100.0, Body::IceBomb => 12298.0, // 2.5 m diamter but ice Body::LaserBeam => 80000.0, + Body::AdletSpear => 1.5, + Body::AdletTrap => 10.0, }; Mass(m) @@ -434,9 +444,13 @@ impl Body { pub fn dimensions(&self) -> Vec3 { match self { - Body::Arrow | Body::ArrowSnake | Body::MultiArrow | Body::ArrowTurret | Body::Dart => { - Vec3::new(0.01, 0.8, 0.01) - }, + Body::Arrow + | Body::ArrowSnake + | Body::MultiArrow + | Body::ArrowTurret + | Body::Dart + | Body::AdletSpear => Vec3::new(0.01, 0.8, 0.01), + Body::AdletTrap => Vec3::new(1.0, 0.6, 0.3), Body::BoltFire => Vec3::new(0.1, 0.1, 0.1), Body::SpectralSwordSmall => Vec3::new(0.2, 0.9, 0.1), Body::SpectralSwordLarge => Vec3::new(0.2, 1.5, 0.1), diff --git a/common/src/comp/inventory/item/armor.rs b/common/src/comp/inventory/item/armor.rs index a26ef044bb..a334a95aa5 100644 --- a/common/src/comp/inventory/item/armor.rs +++ b/common/src/comp/inventory/item/armor.rs @@ -70,7 +70,10 @@ impl Default for Friction { impl Friction { pub fn can_skate_on(&self, b: BlockKind) -> bool { match self { - Friction::Ski => matches!(b, BlockKind::Snow | BlockKind::Ice | BlockKind::Air), + Friction::Ski => matches!( + b, + BlockKind::Snow | BlockKind::ArtSnow | BlockKind::Ice | BlockKind::Air + ), Friction::Skate => b == BlockKind::Ice, _ => false, } @@ -80,6 +83,7 @@ impl Friction { pub fn get_friction(&self, b: BlockKind) -> (f32, f32) { match (self, b) { (Friction::Ski, BlockKind::Snow) => (0.01, 0.95), + (Friction::Ski, BlockKind::ArtSnow) => (0.01, 0.95), (Friction::Ski, BlockKind::Ice) => (0.001, 0.5), (Friction::Ski, BlockKind::Water) => (0.1, 0.7), (Friction::Ski, BlockKind::Air) => (0.0, 0.0), diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 4c614bff07..04794fb2ca 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -574,6 +574,9 @@ fn default_main_tool(body: &Body) -> Item { quadruped_medium::Species::Akhlut => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.akhlut", )), + quadruped_medium::Species::Frostfang => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.frostfang", + )), _ => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.quadmedbasic", )), @@ -607,6 +610,9 @@ fn default_main_tool(body: &Body) -> Item { quadruped_low::Species::Basilisk => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.basilisk", )), + quadruped_low::Species::Icedrake => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.icedrake", + )), _ => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.quadlowbasic", )), @@ -733,6 +739,9 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Gigasfrost, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.axe.gigas_frost_axe", )), + (biped_large::Species::AdletElder, _) => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.sword.adlet_elder_sword", + )), }, Body::Object(body) => match body { object::Body::Crossbow => Some(Item::new_from_asset_expect( @@ -921,6 +930,7 @@ impl LoadoutBuilder { biped_large::Species::Dullahan => { Some("common.items.npc_armor.biped_large.dullahan") }, + biped_large::Species::Tursus => Some("common.items.npc_armor.biped_large.tursus"), biped_large::Species::Cultistwarlord => { Some("common.items.npc_armor.biped_large.warlord") }, @@ -955,7 +965,8 @@ impl LoadoutBuilder { | quadruped_low::Species::Rocksnapper | quadruped_low::Species::Rootsnapper | quadruped_low::Species::Reefsnapper - | quadruped_low::Species::Sandshark => { + | quadruped_low::Species::Sandshark + | quadruped_low::Species::Icedrake => { Some("common.items.npc_armor.quadruped_low.generic") }, quadruped_low::Species::Dagon => Some("common.items.npc_armor.quadruped_low.dagon"), @@ -965,6 +976,9 @@ impl LoadoutBuilder { _ => None, }, Body::QuadrupedMedium(body) => match body.species { + quadruped_medium::Species::Frostfang => { + Some("common.items.npc_armor.quadruped_medium.frostfang") + }, quadruped_medium::Species::Roshwalr => { Some("common.items.npc_armor.quadruped_medium.roshwalr") }, diff --git a/common/src/comp/projectile.rs b/common/src/comp/projectile.rs index 5dd38e7160..a9441a47b7 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -125,6 +125,9 @@ pub enum ProjectileConstructor { knockback: f32, min_falloff: f32, }, + Trap { + damage: f32, + }, } impl ProjectileConstructor { @@ -790,6 +793,29 @@ impl ProjectileConstructor { is_point: true, } }, + Trap { damage } => { + let damage = AttackDamage::new( + Damage { + source: DamageSource::Explosion, + kind: DamageKind::Piercing, + value: damage, + }, + Some(GroupTarget::OutOfGroup), + instance, + ); + let attack = Attack::default() + .with_damage(damage) + .with_crit(crit_chance, crit_mult); + Projectile { + hit_solid: vec![], + hit_entity: vec![Effect::Attack(attack), Effect::Vanish], + time_left: Duration::from_secs(10), + owner, + ignore_group: true, + is_sticky: true, + is_point: false, + } + }, } } @@ -913,6 +939,9 @@ impl ProjectileConstructor { *damage *= power; *radius *= range; }, + Trap { ref mut damage, .. } => { + *damage *= power; + }, } self } @@ -935,6 +964,7 @@ impl ProjectileConstructor { WindBomb { .. } => true, IceBomb { .. } => true, LaserBeam { .. } => true, + Trap { .. } => false, } } } diff --git a/common/src/outcome.rs b/common/src/outcome.rs index 10a58150de..c486d88b38 100644 --- a/common/src/outcome.rs +++ b/common/src/outcome.rs @@ -122,6 +122,9 @@ pub enum Outcome { FailedSpriteUnlock { pos: Vec3, }, + Woosh { + pos: Vec3, + }, } impl Outcome { @@ -140,6 +143,7 @@ impl Outcome { | Outcome::PoiseChange { pos, .. } | Outcome::GroundSlam { pos } | Outcome::FlashFreeze { pos } + | Outcome::Woosh { pos } | Outcome::IceSpikes { pos } | Outcome::IceCrack { pos } | Outcome::Utterance { pos, .. } diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index d981543c1f..ea4d75bfca 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -34,8 +34,8 @@ pub struct StaticData { pub num_projectiles: u32, /// What key is used to press ability pub ability_info: AbilityInfo, - /// pub damage_effect: Option, + pub move_efficiency: f32, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -56,7 +56,7 @@ impl CharacterBehavior for Data { let mut update = StateUpdate::from(data); handle_orientation(data, &mut update, 1.0, None); - handle_move(data, &mut update, 0.3); + handle_move(data, &mut update, self.static_data.move_efficiency); handle_jump(data, output_events, &mut update, 1.0); match self.stage_section { diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 74a8c61e5a..7f11212b0d 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -54,6 +54,17 @@ pub struct Data { pub charge_amount: f32, } +impl Data { + /// How complete the charge is, on a scale of 0.0 to 1.0 + pub fn charge_frac(&self) -> f32 { + if let StageSection::Charge = self.stage_section { + (self.timer.as_secs_f32() / self.static_data.charge_duration.as_secs_f32()).min(1.0) + } else { + 0.0 + } + } +} + impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 07cf8ca7c5..2e5802fd67 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -1,6 +1,8 @@ use crate::{ combat::CombatEffect, comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate}, + event::LocalEvent, + outcome::Outcome, states::{ behavior::{CharacterBehavior, JoinData}, utils::{StageSection, *}, @@ -22,6 +24,8 @@ pub struct StaticData { pub recover_duration: Duration, /// Used to construct the Melee attack pub melee_constructor: MeleeConstructor, + /// Used to specify the sfx to the frontend + pub specifier: Option, /// Affects how far forward the player leaps pub forward_leap_strength: f32, /// Affects how high the player leaps @@ -69,6 +73,12 @@ impl CharacterBehavior for Data { stage_section: StageSection::Movement, ..*self }); + if let Some(FrontendSpecifier::ElderLeap) = self.static_data.specifier { + // Send local event used for frontend shenanigans + output_events.emit_local(LocalEvent::CreateOutcome(Outcome::Woosh { + pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()), + })); + } } }, StageSection::Movement => { @@ -157,3 +167,8 @@ impl CharacterBehavior for Data { update } } + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub enum FrontendSpecifier { + ElderLeap, +} diff --git a/common/src/states/sprite_interact.rs b/common/src/states/sprite_interact.rs index 4e3d9eb6b5..4821a0380e 100644 --- a/common/src/states/sprite_interact.rs +++ b/common/src/states/sprite_interact.rs @@ -188,7 +188,7 @@ impl From for Option { | SpriteKind::PotionMinor | SpriteKind::Seashells | SpriteKind::Bomb => Some(SpriteInteractKind::Collectible), - SpriteKind::Keyhole => Some(SpriteInteractKind::Unlock), + SpriteKind::Keyhole | SpriteKind::BoneKeyhole => Some(SpriteInteractKind::Unlock), // Collectible checked in addition to container for case that sprite requires a tool to // collect and cannot be collected by hand, yet still meets the container check _ if sprite_kind.is_container() && sprite_kind.is_collectible() => { diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 527a1d0d82..25970fa28c 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -58,6 +58,8 @@ make_case_elim!( // often want to experiment with new kinds of block without allocating them a // dedicated block kind. Misc = 0xFE, + // Snow to use with sites, to not attract snowfall particles + ArtSnow = 0xFF, } ); @@ -302,7 +304,7 @@ impl Block { BlockKind::GlowingMushroom => Some(20), _ => match self.get_sprite()? { SpriteKind::StreetLamp | SpriteKind::StreetLampTall => Some(24), - SpriteKind::Ember => Some(20), + SpriteKind::Ember | SpriteKind::FireBlock => Some(20), SpriteKind::WallLamp | SpriteKind::WallLampSmall | SpriteKind::WallSconce @@ -335,7 +337,7 @@ impl Block { | SpriteKind::EmeraldSmall | SpriteKind::SapphireSmall => Some(3), SpriteKind::Lantern => Some(24), - SpriteKind::SeashellLantern => Some(16), + SpriteKind::SeashellLantern | SpriteKind::GlowIceCrystal => Some(16), SpriteKind::SeaDecorEmblem => Some(12), SpriteKind::SeaDecorBlock => Some(10), _ => None, @@ -351,6 +353,7 @@ impl Block { BlockKind::Leaves => (9, 255.0), BlockKind::Wood => (6, 2.0), BlockKind::Snow => (6, 2.0), + BlockKind::ArtSnow => (6, 2.0), BlockKind::Ice => (4, 2.0), _ if self.is_opaque() => (0, 255.0), _ => (0, 0.0), @@ -381,7 +384,10 @@ impl Block { BlockKind::Lava => None, _ => self.get_sprite().and_then(|sprite| match sprite { sprite if sprite.is_container() => None, - SpriteKind::Keyhole | SpriteKind::KeyDoor => None, + SpriteKind::Keyhole + | SpriteKind::KeyDoor + | SpriteKind::BoneKeyhole + | SpriteKind::BoneKeyDoor => None, SpriteKind::Anvil | SpriteKind::Cauldron | SpriteKind::CookingPot @@ -405,7 +411,8 @@ impl Block { | SpriteKind::SeaDecorWindowHor | SpriteKind::SeaDecorWindowVer | SpriteKind::Rope - | SpriteKind::GlassBarrier => None, + | SpriteKind::GlassBarrier + | SpriteKind::FireBlock => None, SpriteKind::EnsnaringVines | SpriteKind::EnsnaringWeb | SpriteKind::SeaUrchin @@ -490,7 +497,7 @@ impl Block { #[inline] pub fn get_traction(&self) -> f32 { match self.kind() { - BlockKind::Snow => 0.8, + BlockKind::Snow | BlockKind::ArtSnow => 0.8, _ => 1.0, } } diff --git a/common/src/terrain/sprite.rs b/common/src/terrain/sprite.rs index 71380e4f22..c387eb0b79 100644 --- a/common/src/terrain/sprite.rs +++ b/common/src/terrain/sprite.rs @@ -244,6 +244,12 @@ make_case_elim!( RepairBench = 0xDA, Helm = 0xDB, DoorWide = 0xDC, + BoneKeyhole = 0xDD, + BoneKeyDoor = 0xDE, + // FireBlock for Burning Buff + FireBlock = 0xDF, + IceCrystal = 0xE0, + GlowIceCrystal = 0xE1, } ); @@ -335,6 +341,8 @@ impl SpriteKind { | SpriteKind::GlassBarrier | SpriteKind::Keyhole | SpriteKind::KeyDoor + | SpriteKind::BoneKeyhole + | SpriteKind::BoneKeyDoor | SpriteKind::Bomb => 1.0, // TODO: Figure out if this should be solid or not. SpriteKind::Shelf => 1.0, @@ -368,7 +376,7 @@ impl SpriteKind { SpriteKind::FountainArabic => 2.4, SpriteKind::Hearth => 2.3, SpriteKind::ForgeTools => 2.8, - SpriteKind::CliffDecorBlock => 1.0, + SpriteKind::CliffDecorBlock | SpriteKind::FireBlock => 1.0, SpriteKind::Wood | SpriteKind::Hardwood | SpriteKind::Ironwood @@ -461,7 +469,7 @@ impl SpriteKind { SpriteKind::Frostwood => item("common.items.log.frostwood"), SpriteKind::Eldwood => item("common.items.log.eldwood"), SpriteKind::MagicalBarrier => table("common.loot_tables.sprite.chest"), - SpriteKind::Keyhole => return Some(None), + SpriteKind::Keyhole | SpriteKind::BoneKeyhole => return Some(None), _ => return None, })) } @@ -560,6 +568,9 @@ impl SpriteKind { SpriteKind::CommonLockedChest => UnlockKind::Consumes( ItemDefinitionId::Simple("common.items.utility.lockpick_0").to_owned(), ), + SpriteKind::BoneKeyhole => UnlockKind::Consumes( + ItemDefinitionId::Simple("common.items.keys.bone_key").to_owned(), + ), _ => UnlockKind::Free, }) } @@ -646,7 +657,11 @@ impl SpriteKind { | SpriteKind::Gravestone | SpriteKind::MagicalBarrier | SpriteKind::Helm - | SpriteKind::DoorWide, + | SpriteKind::DoorWide + | SpriteKind::BoneKeyhole + | SpriteKind::BoneKeyDoor + | SpriteKind::IceCrystal + | SpriteKind::GlowIceCrystal, ) } } diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs index 3f22121b3a..557cdd0388 100644 --- a/common/src/terrain/structure.rs +++ b/common/src/terrain/structure.rs @@ -42,6 +42,7 @@ make_case_elim!( RotatedSprite(kind: SpriteKind, ori: u8) = 23, EntitySpawner(entitykind: String, spawn_chance: f32) = 24, Keyhole(consumes: String) = 25, + BoneKeyhole(consumes: String) = 26, } ); diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index e3493d395b..07463dbdd2 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -200,6 +200,24 @@ impl<'a> System<'a> for Sys { )), }); } + if matches!( + physics_state.on_ground.and_then(|b| b.get_sprite()), + Some(SpriteKind::FireBlock) + ) { + // If on FireBlock vines, apply burning buff + server_emitter.emit(ServerEvent::Buff { + entity, + buff_change: BuffChange::Add(Buff::new( + BuffKind::Burning, + BuffData::new(20.0, None, None), + Vec::new(), + BuffSource::World, + *read_data.time, + Some(&stat), + Some(health), + )), + }); + } if matches!( physics_state.in_fluid, Some(Fluid::Liquid { diff --git a/rtsim/src/gen/site.rs b/rtsim/src/gen/site.rs index f398a26aef..a32b4bf088 100644 --- a/rtsim/src/gen/site.rs +++ b/rtsim/src/gen/site.rs @@ -27,7 +27,10 @@ impl Site { | SiteKind::DesertCity(_) | SiteKind::SavannahPit(_) => Some(true), // Evil - SiteKind::Dungeon(_) | SiteKind::ChapelSite(_) | SiteKind::Gnarling(_) => Some(false), + SiteKind::Dungeon(_) + | SiteKind::ChapelSite(_) + | SiteKind::Gnarling(_) + | SiteKind::Adlet(_) => Some(false), // Neutral SiteKind::Settlement(_) | SiteKind::Castle(_) diff --git a/rtsim/src/rule/npc_ai.rs b/rtsim/src/rule/npc_ai.rs index d3b6bac726..fd747beaf6 100644 --- a/rtsim/src/rule/npc_ai.rs +++ b/rtsim/src/rule/npc_ai.rs @@ -73,7 +73,8 @@ fn path_in_site(start: Vec2, end: Vec2, site: &site2::Site) -> PathRes | TileKind::Tower(_) | TileKind::Keep(_) | TileKind::Gate - | TileKind::GnarlingFortification => 5.0, + | TileKind::GnarlingFortification + | TileKind::AdletStronghold => 5.0, }; let is_door_tile = |plot: Id, tile: Vec2| match site.plot(plot).kind() { site2::PlotKind::House(house) => house.door_tile == tile, diff --git a/server/agent/src/action_nodes.rs b/server/agent/src/action_nodes.rs index 255c4dbb09..b149438c5b 100644 --- a/server/agent/src/action_nodes.rs +++ b/server/agent/src/action_nodes.rs @@ -14,6 +14,7 @@ use common::{ combat::perception_dist_multiplier_from_stealth, comp::{ self, + ability::MAX_ABILITIES, agent::{Sound, SoundKind, Target}, inventory::slot::EquipSlot, item::{ @@ -1014,6 +1015,21 @@ impl<'a> AgentData<'a> { "Gnarling Chieftain" => Tactic::GnarlingChieftain, "Frost Gigas" => Tactic::FrostGigas, "Boreal Hammer" => Tactic::BorealHammer, + "Adlet Hunter" => Tactic::AdletHunter, + "Adlet Icepicker" => Tactic::AdletIcepicker, + "Adlet Tracker" => Tactic::AdletTracker, + "Ice Drake" => Tactic::IceDrake, + "Frostfang" => Tactic::RandomAbilities { + primary: 1, + secondary: 3, + abilities: [0; MAX_ABILITIES], + }, + "Tursus Claws" => Tactic::RandomAbilities { + primary: 2, + secondary: 1, + abilities: [4, 0, 0, 0, 0], + }, + "Adlet Elder" => Tactic::AdletElder, _ => Tactic::SimpleMelee, }, AbilitySpec::Tool(tool_kind) => tool_tactic(*tool_kind), @@ -1457,6 +1473,36 @@ impl<'a> AgentData<'a> { tgt_data, read_data, ), + Tactic::AdletHunter => { + self.handle_adlet_hunter(agent, controller, &attack_data, tgt_data, read_data, rng) + }, + Tactic::AdletIcepicker => { + self.handle_adlet_icepicker(agent, controller, &attack_data, tgt_data, read_data) + }, + Tactic::AdletTracker => { + self.handle_adlet_tracker(agent, controller, &attack_data, tgt_data, read_data) + }, + Tactic::IceDrake => { + self.handle_icedrake(agent, controller, &attack_data, tgt_data, read_data, rng) + }, + Tactic::RandomAbilities { + primary, + secondary, + abilities, + } => self.handle_random_abilities( + agent, + controller, + &attack_data, + tgt_data, + read_data, + rng, + primary, + secondary, + abilities, + ), + Tactic::AdletElder => { + self.handle_adlet_elder(agent, controller, &attack_data, tgt_data, read_data, rng) + }, } } diff --git a/server/agent/src/attack.rs b/server/agent/src/attack.rs index 3b1ca27cb5..eb28ee7513 100644 --- a/server/agent/src/attack.rs +++ b/server/agent/src/attack.rs @@ -5,7 +5,7 @@ use crate::{ }; use common::{ comp::{ - ability::{ActiveAbilities, AuxiliaryAbility, Stance, SwordStance}, + ability::{ActiveAbilities, AuxiliaryAbility, Stance, SwordStance, MAX_ABILITIES}, buff::BuffKind, item::tool::AbilityContext, skills::{AxeSkill, BowSkill, HammerSkill, SceptreSkill, Skill, StaffSkill, SwordSkill}, @@ -757,41 +757,24 @@ impl<'a> AgentData<'a> { ); let attack_failed = if attempt_attack { - let contexts = AbilityContext::from(self.stance, Some(self.inventory)); - let extract_ability = |input: AbilityInput| { - AbilityData::from_ability( - &self - .active_abilities - .activate_ability( - input, - Some(self.inventory), - self.skill_set, - self.body, - Some(self.char_state), - &contexts, - ) - .unwrap_or_default() - .0, - ) - }; - let primary = extract_ability(AbilityInput::Primary); - let secondary = extract_ability(AbilityInput::Secondary); + let primary = self.extract_ability(AbilityInput::Primary); + let secondary = self.extract_ability(AbilityInput::Secondary); let abilities = [ - extract_ability(AbilityInput::Auxiliary(0)), - extract_ability(AbilityInput::Auxiliary(1)), - extract_ability(AbilityInput::Auxiliary(2)), - extract_ability(AbilityInput::Auxiliary(3)), - extract_ability(AbilityInput::Auxiliary(4)), + self.extract_ability(AbilityInput::Auxiliary(0)), + self.extract_ability(AbilityInput::Auxiliary(1)), + self.extract_ability(AbilityInput::Auxiliary(2)), + self.extract_ability(AbilityInput::Auxiliary(3)), + self.extract_ability(AbilityInput::Auxiliary(4)), ]; let could_use_input = |input, desired_energy| match input { InputKind::Primary => primary.as_ref().map_or(false, |p| { - p.could_use(attack_data, self, tgt_data, desired_energy) + p.could_use(attack_data, self, tgt_data, read_data, desired_energy) }), InputKind::Secondary => secondary.as_ref().map_or(false, |s| { - s.could_use(attack_data, self, tgt_data, desired_energy) + s.could_use(attack_data, self, tgt_data, read_data, desired_energy) }), InputKind::Ability(x) => abilities[x].as_ref().map_or(false, |a| { - a.could_use(attack_data, self, tgt_data, desired_energy) + a.could_use(attack_data, self, tgt_data, read_data, desired_energy) }), _ => false, }; @@ -4585,4 +4568,417 @@ impl<'a> AgentData<'a> { ); } } + + pub fn handle_adlet_hunter( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + rng: &mut impl Rng, + ) { + const ROTATE_TIMER: usize = 0; + const ROTATE_DIR_CONDITION: usize = 0; + agent.action_state.timers[ROTATE_TIMER] -= read_data.dt.0; + if agent.action_state.timers[ROTATE_TIMER] < 0.0 { + agent.action_state.conditions[ROTATE_DIR_CONDITION] = rng.gen_bool(0.5); + agent.action_state.timers[ROTATE_TIMER] = rng.gen::() * 5.0; + } + let primary = self.extract_ability(AbilityInput::Primary); + let secondary = self.extract_ability(AbilityInput::Secondary); + let could_use_input = |input| match input { + InputKind::Primary => primary.as_ref().map_or(false, |p| { + p.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + InputKind::Secondary => secondary.as_ref().map_or(false, |s| { + s.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + _ => false, + }; + let move_forwards = if could_use_input(InputKind::Primary) { + controller.push_basic_input(InputKind::Primary); + false + } else if could_use_input(InputKind::Secondary) && attack_data.dist_sqrd > 8_f32.powi(2) { + controller.push_basic_input(InputKind::Secondary); + true + } else { + true + }; + + if move_forwards && attack_data.dist_sqrd > 3_f32.powi(2) { + self.path_toward_target( + agent, + controller, + tgt_data.pos.0, + read_data, + Path::Separate, + None, + ); + } else { + self.path_toward_target( + agent, + controller, + tgt_data.pos.0, + read_data, + Path::Separate, + None, + ); + let dir = if agent.action_state.conditions[ROTATE_DIR_CONDITION] { + 1.0 + } else { + -1.0 + }; + controller.inputs.move_dir.rotate_z(PI / 2.0 * dir); + } + } + + pub fn handle_adlet_icepicker( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + ) { + let primary = self.extract_ability(AbilityInput::Primary); + let secondary = self.extract_ability(AbilityInput::Secondary); + let could_use_input = |input| match input { + InputKind::Primary => primary.as_ref().map_or(false, |p| { + p.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + InputKind::Secondary => secondary.as_ref().map_or(false, |s| { + s.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + _ => false, + }; + let move_forwards = if could_use_input(InputKind::Primary) { + controller.push_basic_input(InputKind::Primary); + false + } else if could_use_input(InputKind::Secondary) && attack_data.dist_sqrd > 5_f32.powi(2) { + controller.push_basic_input(InputKind::Secondary); + false + } else { + true + }; + + if move_forwards && attack_data.dist_sqrd > 2_f32.powi(2) { + self.path_toward_target( + agent, + controller, + tgt_data.pos.0, + read_data, + Path::Separate, + None, + ); + } + } + + pub fn handle_adlet_tracker( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + ) { + const TRAP_TIMER: usize = 0; + agent.action_state.timers[TRAP_TIMER] += read_data.dt.0; + if agent.action_state.timers[TRAP_TIMER] > 20.0 { + agent.action_state.timers[TRAP_TIMER] = 0.0; + } + let primary = self.extract_ability(AbilityInput::Primary); + let could_use_input = |input| match input { + InputKind::Primary => primary.as_ref().map_or(false, |p| { + p.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + _ => false, + }; + let move_forwards = if agent.action_state.timers[TRAP_TIMER] < 3.0 { + controller.push_basic_input(InputKind::Secondary); + false + } else if could_use_input(InputKind::Primary) { + controller.push_basic_input(InputKind::Primary); + false + } else { + true + }; + + if move_forwards && attack_data.dist_sqrd > 2_f32.powi(2) { + self.path_toward_target( + agent, + controller, + tgt_data.pos.0, + read_data, + Path::Separate, + None, + ); + } + } + + pub fn handle_adlet_elder( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + rng: &mut impl Rng, + ) { + const TRAP_TIMER: usize = 0; + agent.action_state.timers[TRAP_TIMER] -= read_data.dt.0; + if matches!(self.char_state, CharacterState::BasicRanged(_)) { + agent.action_state.timers[TRAP_TIMER] = 15.0; + } + let primary = self.extract_ability(AbilityInput::Primary); + let secondary = self.extract_ability(AbilityInput::Secondary); + let abilities = [ + self.extract_ability(AbilityInput::Auxiliary(0)), + self.extract_ability(AbilityInput::Auxiliary(1)), + ]; + let could_use_input = |input| match input { + InputKind::Primary => primary.as_ref().map_or(false, |p| { + p.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + InputKind::Secondary => secondary.as_ref().map_or(false, |s| { + s.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + InputKind::Ability(x) => abilities[x].as_ref().map_or(false, |a| { + a.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + _ => false, + }; + let move_forwards = if matches!(self.char_state, CharacterState::DashMelee(s) if s.stage_section != StageSection::Recover) + { + controller.push_basic_input(InputKind::Secondary); + false + } else if agent.action_state.timers[TRAP_TIMER] < 0.0 && !tgt_data.considered_ranged() { + controller.push_basic_input(InputKind::Ability(0)); + false + } else if could_use_input(InputKind::Primary) { + controller.push_basic_input(InputKind::Primary); + false + } else if could_use_input(InputKind::Secondary) && rng.gen_bool(0.5) { + controller.push_basic_input(InputKind::Secondary); + false + } else if could_use_input(InputKind::Ability(1)) { + controller.push_basic_input(InputKind::Ability(1)); + false + } else { + true + }; + + if matches!(self.char_state, CharacterState::LeapMelee(_)) { + let tgt_vec = tgt_data.pos.0.xy() - self.pos.0.xy(); + if tgt_vec.magnitude_squared() > 2_f32.powi(2) { + if let Some(look_dir) = Dir::from_unnormalized(Vec3::from(tgt_vec)) { + controller.inputs.look_dir = look_dir; + } + } + } + + if move_forwards && attack_data.dist_sqrd > 2_f32.powi(2) { + self.path_toward_target( + agent, + controller, + tgt_data.pos.0, + read_data, + Path::Separate, + None, + ); + } + } + + pub fn handle_icedrake( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + rng: &mut impl Rng, + ) { + let primary = self.extract_ability(AbilityInput::Primary); + let secondary = self.extract_ability(AbilityInput::Secondary); + let abilities = [ + self.extract_ability(AbilityInput::Auxiliary(0)), + self.extract_ability(AbilityInput::Auxiliary(1)), + ]; + let could_use_input = |input| match input { + InputKind::Primary => primary.as_ref().map_or(false, |p| { + p.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + InputKind::Secondary => secondary.as_ref().map_or(false, |s| { + s.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + InputKind::Ability(x) => abilities[x].as_ref().map_or(false, |a| { + a.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + _ => false, + }; + + let continued_attack = match self.char_state.ability_info().map(|ai| ai.input) { + Some(input @ InputKind::Primary) => { + if !matches!(self.char_state.stage_section(), Some(StageSection::Recover)) + && could_use_input(input) + { + controller.push_basic_input(input); + true + } else { + false + } + }, + Some(input @ InputKind::Ability(1)) => { + if self + .char_state + .timer() + .map_or(false, |t| t.as_secs_f32() < 3.0) + && could_use_input(input) + { + controller.push_basic_input(input); + true + } else { + false + } + }, + _ => false, + }; + + let move_forwards = if !continued_attack { + if could_use_input(InputKind::Primary) && rng.gen_bool(0.4) { + controller.push_basic_input(InputKind::Primary); + false + } else if could_use_input(InputKind::Secondary) && rng.gen_bool(0.8) { + controller.push_basic_input(InputKind::Secondary); + false + } else if could_use_input(InputKind::Ability(1)) && rng.gen_bool(0.9) { + controller.push_basic_input(InputKind::Ability(1)); + true + } else if could_use_input(InputKind::Ability(0)) { + controller.push_basic_input(InputKind::Ability(0)); + true + } else { + true + } + } else { + false + }; + + if move_forwards { + self.path_toward_target( + agent, + controller, + tgt_data.pos.0, + read_data, + Path::Separate, + None, + ); + } + } + + pub fn handle_random_abilities( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + rng: &mut impl Rng, + primary_weight: u8, + secondary_weight: u8, + ability_weights: [u8; MAX_ABILITIES], + ) { + let primary = self.extract_ability(AbilityInput::Primary); + let secondary = self.extract_ability(AbilityInput::Secondary); + let abilities = [ + self.extract_ability(AbilityInput::Auxiliary(0)), + self.extract_ability(AbilityInput::Auxiliary(1)), + self.extract_ability(AbilityInput::Auxiliary(2)), + self.extract_ability(AbilityInput::Auxiliary(3)), + self.extract_ability(AbilityInput::Auxiliary(4)), + ]; + let could_use_input = |input| match input { + InputKind::Primary => primary.as_ref().map_or(false, |p| { + p.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + InputKind::Secondary => secondary.as_ref().map_or(false, |s| { + s.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + InputKind::Ability(x) => abilities[x].as_ref().map_or(false, |a| { + a.could_use(attack_data, self, tgt_data, read_data, 0.0) + }), + _ => false, + }; + + let primary_chance = primary_weight as f64 + / ((primary_weight + secondary_weight + ability_weights.iter().sum::()) as f64) + .max(0.01); + let secondary_chance = secondary_weight as f64 + / ((secondary_weight + ability_weights.iter().sum::()) as f64).max(0.01); + let ability_chances = { + let mut chances = [0.0; MAX_ABILITIES]; + chances.iter_mut().enumerate().for_each(|(i, chance)| { + *chance = ability_weights[i] as f64 + / (ability_weights + .iter() + .enumerate() + .filter_map(|(j, weight)| if j >= i { Some(weight) } else { None }) + .sum::() as f64) + .max(0.01) + }); + chances + }; + + if let Some(input) = self.char_state.ability_info().map(|ai| ai.input) { + match self.char_state { + CharacterState::ChargedMelee(c) => { + if c.charge_frac() < 1.0 && could_use_input(input) { + controller.push_basic_input(input); + } + }, + CharacterState::ChargedRanged(c) => { + if c.charge_frac() < 1.0 && could_use_input(input) { + controller.push_basic_input(input); + } + }, + _ => {}, + } + } + + let move_forwards = if could_use_input(InputKind::Primary) && rng.gen_bool(primary_chance) { + controller.push_basic_input(InputKind::Primary); + false + } else if could_use_input(InputKind::Secondary) && rng.gen_bool(secondary_chance) { + controller.push_basic_input(InputKind::Secondary); + false + } else if could_use_input(InputKind::Ability(0)) && rng.gen_bool(ability_chances[0]) { + controller.push_basic_input(InputKind::Ability(0)); + false + } else if could_use_input(InputKind::Ability(1)) && rng.gen_bool(ability_chances[1]) { + controller.push_basic_input(InputKind::Ability(1)); + false + } else if could_use_input(InputKind::Ability(2)) && rng.gen_bool(ability_chances[2]) { + controller.push_basic_input(InputKind::Ability(2)); + false + } else if could_use_input(InputKind::Ability(3)) && rng.gen_bool(ability_chances[3]) { + controller.push_basic_input(InputKind::Ability(3)); + false + } else if could_use_input(InputKind::Ability(4)) && rng.gen_bool(ability_chances[4]) { + controller.push_basic_input(InputKind::Ability(4)); + false + } else { + true + }; + + if move_forwards { + self.path_toward_target( + agent, + controller, + tgt_data.pos.0, + read_data, + Path::Separate, + None, + ); + } + } } diff --git a/server/agent/src/data.rs b/server/agent/src/data.rs index 1da7fee2be..0f34fed635 100644 --- a/server/agent/src/data.rs +++ b/server/agent/src/data.rs @@ -1,14 +1,19 @@ +use crate::util::*; use common::{ comp::{ - ability::CharacterAbility, + ability::{CharacterAbility, MAX_ABILITIES}, buff::{BuffKind, Buffs}, character_state::AttackFilters, group, - item::MaterialStatManifest, + inventory::{ + item::{tool::ToolKind, ItemKind, MaterialStatManifest}, + slot::EquipSlot, + }, ActiveAbilities, Alignment, Body, CharacterState, Combo, Energy, Health, Inventory, LightEmitter, LootOwner, Ori, PhysicsState, Poise, Pos, Presence, PresenceKind, Scale, SkillSet, Stance, Stats, Vel, }, + consts::GRAVITY, link::Is, mounting::{Mount, Rider, VolumeRider}, path::TraversalConfig, @@ -64,6 +69,7 @@ pub struct TargetData<'a> { pub char_state: Option<&'a CharacterState>, pub health: Option<&'a Health>, pub buffs: Option<&'a Buffs>, + pub drawn_weapons: (Option, Option), } impl<'a> TargetData<'a> { @@ -75,8 +81,55 @@ impl<'a> TargetData<'a> { char_state: read_data.char_states.get(target), health: read_data.healths.get(target), buffs: read_data.buffs.get(target), + drawn_weapons: { + let slotted_tool = |inv: &Inventory, slot| { + if let Some(ItemKind::Tool(tool)) = + inv.equipped(slot).map(|i| i.kind()).as_deref() + { + Some(tool.kind) + } else { + None + } + }; + read_data + .inventories + .get(target) + .map_or((None, None), |inv| { + ( + slotted_tool(inv, EquipSlot::ActiveMainhand), + slotted_tool(inv, EquipSlot::ActiveOffhand), + ) + }) + }, } } + + pub fn considered_ranged(&self) -> bool { + let is_ranged_tool = |tool| match tool { + Some( + ToolKind::Sword + | ToolKind::Axe + | ToolKind::Hammer + | ToolKind::Dagger + | ToolKind::Shield + | ToolKind::Spear + | ToolKind::Farming + | ToolKind::Pick + | ToolKind::Natural + | ToolKind::Empty, + ) + | None => false, + Some( + ToolKind::Bow + | ToolKind::Staff + | ToolKind::Sceptre + | ToolKind::Blowgun + | ToolKind::Debug + | ToolKind::Instrument, + ) => true, + }; + is_ranged_tool(self.drawn_weapons.0) || is_ranged_tool(self.drawn_weapons.1) + } } pub struct AttackData { @@ -120,6 +173,12 @@ pub enum Tactic { FixedTurret, RotatingTurret, RadialTurret, + // u8s are weights that each ability gets used, if it can be used + RandomAbilities { + primary: u8, + secondary: u8, + abilities: [u8; MAX_ABILITIES], + }, // Tool specific tactics Axe, @@ -132,7 +191,10 @@ pub enum Tactic { SwordSimple, // Broad creature tactics - CircleCharge { radius: u32, circle_time: u32 }, + CircleCharge { + radius: u32, + circle_time: u32, + }, QuadLowRanged, TailSlap, QuadLowQuick, @@ -171,6 +233,13 @@ pub enum Tactic { BorealHammer, Dullahan, Cyclops, + IceDrake, + + // Adlets + AdletHunter, + AdletIcepicker, + AdletTracker, + AdletElder, } #[derive(Copy, Clone)] @@ -331,6 +400,29 @@ pub enum AbilityData { blocked_attacks: AttackFilters, angle: f32, }, + BasicRanged { + energy: f32, + projectile_speed: f32, + }, + BasicMelee { + energy: f32, + range: f32, + angle: f32, + }, + LeapMelee { + energy: f32, + range: f32, + angle: f32, + forward_leap: f32, + vertical_leap: f32, + leap_dur: f32, + }, + BasicBeam { + energy_drain: f32, + range: f32, + angle: f32, + ori_rate: f32, + }, } impl AbilityData { @@ -453,6 +545,50 @@ impl AbilityData { angle: *max_angle, blocked_attacks: *blocked_attacks, }, + BasicRanged { + energy_cost, + projectile_speed, + .. + } => Self::BasicRanged { + energy: *energy_cost, + projectile_speed: *projectile_speed, + }, + BasicMelee { + energy_cost, + melee_constructor, + .. + } => Self::BasicMelee { + energy: *energy_cost, + range: melee_constructor.range, + angle: melee_constructor.angle, + }, + LeapMelee { + energy_cost, + movement_duration, + melee_constructor, + forward_leap_strength, + vertical_leap_strength, + .. + } => Self::LeapMelee { + energy: *energy_cost, + leap_dur: *movement_duration, + range: melee_constructor.range, + angle: melee_constructor.angle, + forward_leap: *forward_leap_strength, + vertical_leap: *vertical_leap_strength, + }, + BasicBeam { + range, + max_angle, + ori_rate, + energy_drain, + .. + } => Self::BasicBeam { + range: *range, + angle: *max_angle, + ori_rate: *ori_rate, + energy_drain: *energy_drain, + }, _ => return None, }; Some(inner) @@ -463,18 +599,30 @@ impl AbilityData { attack_data: &AttackData, agent_data: &AgentData, tgt_data: &TargetData, + read_data: &ReadData, desired_energy: f32, ) -> bool { let melee_check = |range: f32, angle, forced_movement: Option| { - let range_inc = forced_movement.map_or(0.0, |fm| match fm { - ForcedMovement::Forward(speed) => speed * 15.0, - ForcedMovement::Reverse(speed) => -speed, - _ => 0.0, + let (range_inc, min_mult) = forced_movement.map_or((0.0, 0.0), |fm| match fm { + ForcedMovement::Forward(speed) => (speed * 15.0, 1.0), + ForcedMovement::Reverse(speed) => (-speed, 1.0), + ForcedMovement::Leap { + vertical, forward, .. + } => ( + { + let dur = vertical * 2.0 / GRAVITY; + // 0.75 factor to allow for fact that agent looks down as they approach, so + // won't go as far + forward * dur * 0.75 + }, + 0.0, + ), + _ => (0.0, 0.0), }); let body_rad = agent_data.body.map_or(0.0, |b| b.max_radius()); attack_data.dist_sqrd < (range + range_inc + body_rad).powi(2) && attack_data.angle < angle - && attack_data.dist_sqrd > range_inc.powi(2) + && attack_data.dist_sqrd > (range_inc * min_mult).powi(2) }; let energy_check = |energy: f32| { agent_data.energy.current() >= energy @@ -487,6 +635,36 @@ impl AbilityData { .and_then(|cs| cs.attack_kind()) .map_or(false, |ak| attacks.applies(ak)) }; + let ranged_check = |proj_speed| { + let max_horiz_dist: f32 = { + let flight_time = proj_speed * 2_f32.sqrt() / GRAVITY; + proj_speed * 2_f32.sqrt() / 2.0 * flight_time + }; + attack_data.dist_sqrd < max_horiz_dist.powi(2) + && entities_have_line_of_sight( + agent_data.pos, + agent_data.body, + agent_data.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) + }; + let beam_check = |range: f32, angle, ori_rate: f32| { + let angle_inc = ori_rate.to_degrees(); + attack_data.dist_sqrd < range.powi(2) + && attack_data.angle < angle + angle_inc + && entities_have_line_of_sight( + agent_data.pos, + agent_data.body, + agent_data.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) + }; use AbilityData::*; match self { ComboMelee { @@ -587,6 +765,38 @@ impl AbilityData { .and_then(|cs| cs.stage_section()) .map_or(false, |ss| !matches!(ss, StageSection::Recover)) }, + BasicRanged { + energy, + projectile_speed, + } => ranged_check(*projectile_speed) && energy_check(*energy), + BasicMelee { + energy, + range, + angle, + } => melee_check(*range, *angle, None) && energy_check(*energy), + LeapMelee { + energy, + range, + angle, + leap_dur, + forward_leap, + vertical_leap, + } => { + use common::states::utils::MovementDirection; + let forced_move = Some(ForcedMovement::Leap { + vertical: *vertical_leap * *leap_dur * 2.0, + forward: *forward_leap, + progress: 0.0, + direction: MovementDirection::Look, + }); + melee_check(*range, *angle, forced_move) && energy_check(*energy) + }, + BasicBeam { + energy_drain, + range, + angle, + ori_rate, + } => beam_check(*range, *angle, *ori_rate) && energy_check(*energy_drain * 3.0), } } } diff --git a/server/agent/src/util.rs b/server/agent/src/util.rs index 7dfe62b4e2..e486ed2207 100644 --- a/server/agent/src/util.rs +++ b/server/agent/src/util.rs @@ -1,8 +1,11 @@ -use crate::data::{ActionMode, AgentData, AttackData, Path, ReadData, TargetData}; +use crate::data::{AbilityData, ActionMode, AgentData, AttackData, Path, ReadData, TargetData}; use common::{ comp::{ - agent::Psyche, buff::BuffKind, inventory::item::ItemTag, item::ItemDesc, Agent, Alignment, - Body, Controller, InputKind, Pos, Scale, + ability::AbilityInput, + agent::Psyche, + buff::BuffKind, + item::{tool::AbilityContext, ItemDesc, ItemTag}, + Agent, Alignment, Body, Controller, InputKind, Pos, Scale, }, consts::GRAVITY, terrain::Block, @@ -173,7 +176,7 @@ pub fn positions_have_line_of_sight(pos_a: &Pos, pos_b: &Pos, read_data: &ReadDa .cast() .0 .powi(2) - >= dist_sqrd + >= (dist_sqrd - 0.01) } pub fn is_dressed_as_cultist(entity: EcsEntity, read_data: &ReadData) -> bool { @@ -205,6 +208,24 @@ impl<'a> AgentData<'a> { .get(*self.entity) .map_or(false, |b| b.kinds.contains_key(&buff)) } + + pub fn extract_ability(&self, input: AbilityInput) -> Option { + let context = AbilityContext::from(self.stance, Some(self.inventory)); + AbilityData::from_ability( + &self + .active_abilities + .activate_ability( + input, + Some(self.inventory), + self.skill_set, + self.body, + Some(self.char_state), + &context, + ) + .unwrap_or_default() + .0, + ) + } } // Probably works best for melee (or maybe only for melee considering its diff --git a/server/src/cmd.rs b/server/src/cmd.rs index b8faaa8f64..963495615c 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -3133,6 +3133,7 @@ fn handle_debug_column( let river = &chunk.river; let flux = chunk.flux; let path = chunk.path; + let cliff_height = chunk.cliff_height; Some(format!( r#"wpos: {:?} @@ -3149,7 +3150,8 @@ humidity {:?} rockiness {:?} tree_density {:?} spawn_rate {:?} -path {:?} "#, +path {:?} +cliff_height {:?} "#, wpos, alt, col.alt, @@ -3167,6 +3169,7 @@ path {:?} "#, tree_density, spawn_rate, path, + cliff_height, )) }; if let Some(s) = msg_generator(&calendar) { diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index f506b43ff7..ed66d81b44 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -320,7 +320,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv // If the block was a keyhole, remove nearby door blocks // TODO: Abstract this code into a generalised way to do block updates? - if matches!(block.get_sprite(), Some(SpriteKind::Keyhole)) { + if matches!( + block.get_sprite(), + Some(SpriteKind::Keyhole | SpriteKind::BoneKeyhole) + ) { let dirs = [ Vec3::unit_x(), -Vec3::unit_x(), @@ -342,17 +345,35 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv let next_pending = pending.iter().next().copied(); if let Some(pos) = next_pending { pending.remove(&pos); - if !destroyed.contains(&pos) - && matches!( - terrain.get(pos).ok().and_then(|b| b.get_sprite()), - Some(SpriteKind::KeyDoor) - ) - { - block_change.try_set(pos, Block::empty()); - destroyed.insert(pos); + match block.get_sprite() { + Some(SpriteKind::Keyhole) => { + if !destroyed.contains(&pos) + && matches!( + terrain.get(pos).ok().and_then(|b| b.get_sprite()), + Some(SpriteKind::KeyDoor) + ) + { + block_change.try_set(pos, Block::empty()); + destroyed.insert(pos); - pending.extend(dirs.into_iter().map(|dir| pos + dir)); - } + pending.extend(dirs.into_iter().map(|dir| pos + dir)); + } + }, + Some(SpriteKind::BoneKeyhole) => { + if !destroyed.contains(&pos) + && matches!( + terrain.get(pos).ok().and_then(|b| b.get_sprite()), + Some(SpriteKind::BoneKeyDoor) + ) + { + block_change.try_set(pos, Block::empty()); + destroyed.insert(pos); + + pending.extend(dirs.into_iter().map(|dir| pos + dir)); + } + }, + _ => {}, + }; } else { break; } diff --git a/voxygen/anim/src/biped_large/alpha.rs b/voxygen/anim/src/biped_large/alpha.rs index cbc2c01084..f384b9e2f3 100644 --- a/voxygen/anim/src/biped_large/alpha.rs +++ b/voxygen/anim/src/biped_large/alpha.rs @@ -515,7 +515,7 @@ impl Animation for AlphaAnimation { * Quaternion::rotation_y(move1 * -0.8 + move2 * 0.6); } }, - Some("common.abilities.custom.tursus_claws.basic") => { + Some("common.abilities.custom.tursus.tursus_claws") => { next.shoulder_l.position = Vec3::new(-s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2); next.shoulder_r.position = diff --git a/voxygen/anim/src/biped_large/chargemelee.rs b/voxygen/anim/src/biped_large/chargemelee.rs index 651aae777e..792fa69c90 100644 --- a/voxygen/anim/src/biped_large/chargemelee.rs +++ b/voxygen/anim/src/biped_large/chargemelee.rs @@ -199,6 +199,65 @@ impl Animation for ChargeMeleeAnimation { next.main.orientation = Quaternion::rotation_y(move1 * 0.4 + move2 * -0.6) * Quaternion::rotation_x(move2 * -0.4); }, + Some("common.abilities.custom.tursus.tusk_stab") => { + next.second.scale = Vec3::one() * 0.0; + next.head.position = Vec3::new( + 0.0, + s_a.head.0 + move1 * 15.0, + s_a.head.1 + move1 * 6.0 + move2 * -4.0, + ); + next.head.orientation = Quaternion::rotation_x(move1 * 1.3 + move2 * -0.7); + next.jaw.position = Vec3::new(0.0, s_a.jaw.0, s_a.jaw.1); + next.jaw.orientation = Quaternion::rotation_x(move2 * -0.3); + next.control_l.position = Vec3::new(-0.5, 4.0, 1.0); + next.control_r.position = Vec3::new(-0.5, 4.0, 1.0); + next.control_l.orientation = Quaternion::rotation_x(PI / 2.0); + next.control_r.orientation = Quaternion::rotation_x(PI / 2.0); + next.weapon_l.position = + Vec3::new(-16.0 + (move1 * 10.0).min(6.0), -1.0, -15.0); + next.weapon_r.position = + Vec3::new(16.0 + (move1 * -10.0).max(-6.0), -1.0, -15.0); + + next.weapon_l.orientation = Quaternion::rotation_x(-PI / 2.0 - 0.1) + * Quaternion::rotation_z(move1 * -0.8); + next.weapon_r.orientation = Quaternion::rotation_x(-PI / 2.0 - 0.1) + * Quaternion::rotation_z(move1 * 0.8); + + next.shoulder_l.orientation = + Quaternion::rotation_x(-0.3 + move1 * -0.4 + move2 * 0.4); + + next.shoulder_r.orientation = + Quaternion::rotation_x(-0.3 + move1 * -0.4 + move2 * 0.4); + + next.control.orientation = Quaternion::rotation_x(move1 * -0.7 + move2 * 0.7); + + next.upper_torso.position = + Vec3::new(0.0, s_a.upper_torso.0, s_a.upper_torso.1); + next.upper_torso.orientation = + Quaternion::rotation_x(move1 * 0.6 + move2 * -1.1); + next.lower_torso.orientation = + Quaternion::rotation_x(move1 * -0.2 + move2 * 0.6); + + if speed < 0.1 { + next.foot_l.position = Vec3::new( + -s_a.foot.0, + s_a.foot.1 + move1 * -7.0 + move2 * 7.0, + s_a.foot.2, + ); + next.foot_l.orientation = + Quaternion::rotation_x(move1 * -0.8 + move2 * 0.8) + * Quaternion::rotation_z(move1 * 0.3 + move2 * -0.3); + + next.foot_r.position = Vec3::new( + s_a.foot.0, + s_a.foot.1 + move1 * 5.0 + move2 * -5.0, + s_a.foot.2, + ); + next.foot_r.orientation = + Quaternion::rotation_y(move1 * -0.3 + move2 * 0.3) + * Quaternion::rotation_z(move1 * 0.4 + move2 * -0.4); + } + }, _ => {}, }, _ => {}, diff --git a/voxygen/anim/src/biped_large/combomelee.rs b/voxygen/anim/src/biped_large/combomelee.rs new file mode 100644 index 0000000000..f5255e2450 --- /dev/null +++ b/voxygen/anim/src/biped_large/combomelee.rs @@ -0,0 +1,116 @@ +use super::{ + super::{vek::*, Animation}, + BipedLargeSkeleton, SkeletonAttr, +}; +use common::states::utils::{AbilityInfo, StageSection}; +use core::f32::consts::PI; + +pub struct ComboAnimation; +impl Animation for ComboAnimation { + type Dependency<'a> = ( + Option<&'a str>, + Option, + Option, + usize, + Vec2, + ); + type Skeleton = BipedLargeSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"biped_large_combo\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "biped_large_combo")] + fn update_skeleton_inner( + skeleton: &Self::Skeleton, + (ability_id, stage_section, _ability_info, current_strike, _move_dir): Self::Dependency<'_>, + anim_time: f32, + rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + *rate = 1.0; + let mut next = (*skeleton).clone(); + + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_z(0.0); + next.second.position = Vec3::new(0.0, 0.0, 0.0); + next.second.orientation = Quaternion::rotation_z(0.0); + let multi_strike_pullback = 1.0 + - if matches!(stage_section, Some(StageSection::Recover)) { + anim_time.powi(4) + } else { + 0.0 + }; + + for strike in 0..=current_strike { + match ability_id { + Some("common.abilities.adlet.elder.triplestrike") => { + let (move1, move2) = if strike == current_strike { + match stage_section { + Some(StageSection::Buildup) => { + (((anim_time.max(0.4) - 0.4) * 1.5).powf(0.5), 0.0) + }, + Some(StageSection::Action) => (1.0, (anim_time.min(0.4) * 2.5).powi(2)), + Some(StageSection::Recover) => (1.0, 1.0), + _ => (0.0, 0.0), + } + } else { + (1.0, 1.0) + }; + let move1 = move1 * multi_strike_pullback; + let move2 = move2 * multi_strike_pullback; + next.second.scale = Vec3::one() * 1.0; + next.main.position = Vec3::new(-s_a.grip.0 + 1.0, s_a.grip.0 * 2.0, 0.0); + next.second.position = Vec3::new(s_a.grip.0, s_a.grip.0 * 2.0, 0.0); + next.hand_l.position = Vec3::new(-s_a.grip.0, s_a.grip.0 + 1.0, 0.0); + next.hand_r.position = Vec3::new(s_a.grip.0, s_a.grip.0 + 1.0, 0.0); + next.hand_l.orientation = Quaternion::rotation_x(PI / 2.0); + next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0); + + next.main.orientation = + Quaternion::rotation_x(PI / -3.0) * Quaternion::rotation_z(PI / -4.0); + next.second.orientation = + Quaternion::rotation_x(PI / -3.0) * Quaternion::rotation_z(PI / 4.0); + + next.head.orientation = Quaternion::rotation_x(move1 * -0.2 + move2 * 0.4); + match strike { + 0 => { + next.weapon_l.position = Vec3::new(0.0, -8.0, -5.0); + next.weapon_l.orientation = + Quaternion::rotation_x(move1 * 0.3 + move2 * -0.2); + next.weapon_r.orientation = + Quaternion::rotation_z(move1 * -1.0 + move2 * 1.9); + next.shoulder_r.orientation = Quaternion::rotation_x(move1 * 1.0) + * Quaternion::rotation_y(move1 * -0.9 + move2 * 0.9); + }, + 1 => { + next.weapon_r.position = Vec3::new(0.0, -8.0, -5.0); + next.weapon_r.orientation = + Quaternion::rotation_x(move1 * 0.3 + move2 * -0.2); + next.weapon_l.orientation = + Quaternion::rotation_z(move1 * 1.0 + move2 * -1.9); + next.shoulder_l.orientation = + Quaternion::rotation_x(move1 * 1.0 * move2 * 1.0) + * Quaternion::rotation_y(move1 * 0.9 + move2 * -0.9); + }, + + 2 => { + next.weapon_l.orientation = + Quaternion::rotation_z(move1 * 1.0 + move2 * -1.2); + next.weapon_r.orientation = + Quaternion::rotation_z(move1 * -1.0 + move2 * 1.2); + next.shoulder_l.orientation = + Quaternion::rotation_x(move1 * 1.0 * move2 * 1.0) + * Quaternion::rotation_y(move1 * 0.9 + move2 * -0.7); + next.shoulder_r.orientation = + Quaternion::rotation_x(move1 * 1.0 * move2 * 1.0) + * Quaternion::rotation_y(move1 * -0.9 + move2 * 0.7); + }, + _ => {}, + } + }, + _ => {}, + } + } + next + } +} diff --git a/voxygen/anim/src/biped_large/dash.rs b/voxygen/anim/src/biped_large/dash.rs index 167b0dcace..d441cd071a 100644 --- a/voxygen/anim/src/biped_large/dash.rs +++ b/voxygen/anim/src/biped_large/dash.rs @@ -99,32 +99,64 @@ impl Animation for DashAnimation { Quaternion::rotation_x(0.6 * speednorm + (footrotl * -0.2) * speednorm); next.torso.orientation = Quaternion::rotation_z(0.0); match active_tool_kind { - Some(ToolKind::Sword) => { - next.control_l.position = Vec3::new(-1.0, 1.0, 1.0); - next.control_r.position = Vec3::new(0.0, 2.0, -3.0); - next.head.orientation = Quaternion::rotation_x(move1 * -0.25) - * Quaternion::rotation_z(move1 * -0.2 + move2 * 0.6); - next.control.position = Vec3::new( - -3.0 + move1 * -2.0 + move2 * 2.0, - 5.0 + s_a.grip.0 / 1.2 + move1 * -4.0 + move2 * 2.0 + move3 * 8.0, - -4.0 + -s_a.grip.0 / 2.0 + move2 * -5.0 + move3 * 5.0, - ); - next.upper_torso.orientation = Quaternion::rotation_x(move2 * -0.2 + move3 * 0.2) - * Quaternion::rotation_z(move1 * 0.8 + move3 * -0.7); - next.lower_torso.orientation = Quaternion::rotation_x(move2 * 0.2 + move3 * -0.2) - * Quaternion::rotation_z(move1 * -0.8 + move3 * 0.7); - next.control_l.orientation = - Quaternion::rotation_x(PI / 2.0 + move1 * -0.5 + move2 * 1.5) - * Quaternion::rotation_y(-0.2); - next.control_r.orientation = - Quaternion::rotation_x(PI / 2.2 + move1 * -0.5 + move2 * 1.5) - * Quaternion::rotation_y(0.2) - * Quaternion::rotation_z(0.0); + Some(ToolKind::Sword) => match ability_id { + Some("common.abilities.adlet.elder.dash") => { + next.head.orientation = Quaternion::rotation_x(move1 * 0.2 + move3 * 0.1) + * Quaternion::rotation_z(move1 * -0.1 + move3 * -0.2); + next.upper_torso.orientation = + Quaternion::rotation_x(move1 * -0.2 + move3 * 0.3) + * Quaternion::rotation_z(move1 * 0.2 + move3 * -0.2); + next.control_l.position = Vec3::new(0.0, 2.0, -2.0); + next.control_r.position = Vec3::new(0.0, 2.0, -2.0); + next.weapon_l.position = + Vec3::new(-5.5 + move1 * -4.0 + move2 * 6.0, 5.0, -18.0); + next.weapon_r.position = + Vec3::new(6.5 + move1 * 4.0 + move2 * -6.0, 5.0, -18.0); + next.second.scale = Vec3::one() * 1.0; - next.control.orientation = - Quaternion::rotation_x(-0.2 + move1 * 0.5 + move2 * -1.5 + move3 * -0.2) - * Quaternion::rotation_y(-0.1 + move1 * -0.5 + move2 * 1.5 + move3 * -1.0) - * Quaternion::rotation_z(-move3 * -1.5); + next.weapon_l.orientation = Quaternion::rotation_x(-1.67 + move1 * 0.4) + * Quaternion::rotation_z(move2 * -0.5); + next.weapon_r.orientation = Quaternion::rotation_x(-1.67 + move1 * 0.3) + * Quaternion::rotation_z(move2 * 0.5); + + next.control_l.orientation = Quaternion::rotation_x(PI / 2.0); + next.control_r.orientation = Quaternion::rotation_x(PI / 2.0); + + next.control.orientation = Quaternion::rotation_x(move1 * PI + move2 * -PI); + next.shoulder_l.orientation = Quaternion::rotation_x(move1 * PI + move2 * -PI); + next.shoulder_r.orientation = Quaternion::rotation_x(move1 * PI + move2 * -PI); + }, + _ => { + next.control_l.position = Vec3::new(-1.0, 1.0, 1.0); + next.control_r.position = Vec3::new(0.0, 2.0, -3.0); + next.head.orientation = Quaternion::rotation_x(move1 * -0.25) + * Quaternion::rotation_z(move1 * -0.2 + move2 * 0.6); + next.control.position = Vec3::new( + -3.0 + move1 * -2.0 + move2 * 2.0, + 5.0 + s_a.grip.0 / 1.2 + move1 * -4.0 + move2 * 2.0 + move3 * 8.0, + -4.0 + -s_a.grip.0 / 2.0 + move2 * -5.0 + move3 * 5.0, + ); + next.upper_torso.orientation = + Quaternion::rotation_x(move2 * -0.2 + move3 * 0.2) + * Quaternion::rotation_z(move1 * 0.8 + move3 * -0.7); + next.lower_torso.orientation = + Quaternion::rotation_x(move2 * 0.2 + move3 * -0.2) + * Quaternion::rotation_z(move1 * -0.8 + move3 * 0.7); + next.control_l.orientation = + Quaternion::rotation_x(PI / 2.0 + move1 * -0.5 + move2 * 1.5) + * Quaternion::rotation_y(-0.2); + next.control_r.orientation = + Quaternion::rotation_x(PI / 2.2 + move1 * -0.5 + move2 * 1.5) + * Quaternion::rotation_y(0.2) + * Quaternion::rotation_z(0.0); + + next.control.orientation = + Quaternion::rotation_x(-0.2 + move1 * 0.5 + move2 * -1.5 + move3 * -0.2) + * Quaternion::rotation_y( + -0.1 + move1 * -0.5 + move2 * 1.5 + move3 * -1.0, + ) + * Quaternion::rotation_z(-move3 * -1.5); + }, }, Some(ToolKind::Axe) => { next.control_l.position = Vec3::new(-1.0, 2.0, 12.0 + move3 * 3.0); diff --git a/voxygen/anim/src/biped_large/leapmelee.rs b/voxygen/anim/src/biped_large/leapmelee.rs index 632f0f8b40..5ee5a04eb2 100644 --- a/voxygen/anim/src/biped_large/leapmelee.rs +++ b/voxygen/anim/src/biped_large/leapmelee.rs @@ -14,6 +14,7 @@ impl Animation for LeapAnimation { Vec3, f32, Option, + Option<&'a str>, ); type Skeleton = BipedLargeSkeleton; @@ -23,7 +24,7 @@ impl Animation for LeapAnimation { #[cfg_attr(feature = "be-dyn-lib", export_name = "biped_large_leapmelee")] fn update_skeleton_inner( skeleton: &Self::Skeleton, - (active_tool_kind, _second_tool_kind, _velocity, _global_time, stage_section): Self::Dependency<'_>, + (active_tool_kind, _second_tool_kind, _velocity, _global_time, stage_section, ability_id): Self::Dependency<'_>, anim_time: f32, rate: &mut f32, s_a: &SkeletonAttr, @@ -39,49 +40,146 @@ impl Animation for LeapAnimation { _ => (0.0, 0.0, 0.0, 0.0), }; - if let Some(ToolKind::Hammer) = active_tool_kind { - next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2); - next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3); - next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2); - next.hand_r.orientation = Quaternion::rotation_x(s_a.hhr.3); - next.main.position = Vec3::new(0.0, 0.0, 0.0); - next.main.orientation = Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); + match active_tool_kind { + Some(ToolKind::Hammer) => { + next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2); + next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3); + next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2); + next.hand_r.orientation = Quaternion::rotation_x(s_a.hhr.3); + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); - next.control.position = Vec3::new( - s_a.hc.0 + movement2 * -10.0 + movement3 * 6.0, - s_a.hc.1 + movement2 * 5.0 + movement3 * 7.0, - s_a.hc.2 + movement2 * 5.0 + movement3 * -10.0, - ); - next.control.orientation = - Quaternion::rotation_x(s_a.hc.3 + movement2 * PI / 2.0 + movement3 * -2.3) - * Quaternion::rotation_y(s_a.hc.4 + movement2 * 1.3) - * Quaternion::rotation_z(s_a.hc.5 + movement2 * -1.0 + movement3 * 0.5); - next.upper_torso.orientation = - Quaternion::rotation_x( + next.control.position = Vec3::new( + s_a.hc.0 + movement2 * -10.0 + movement3 * 6.0, + s_a.hc.1 + movement2 * 5.0 + movement3 * 7.0, + s_a.hc.2 + movement2 * 5.0 + movement3 * -10.0, + ); + next.control.orientation = + Quaternion::rotation_x(s_a.hc.3 + movement2 * PI / 2.0 + movement3 * -2.3) + * Quaternion::rotation_y(s_a.hc.4 + movement2 * 1.3) + * Quaternion::rotation_z(s_a.hc.5 + movement2 * -1.0 + movement3 * 0.5); + next.upper_torso.orientation = Quaternion::rotation_x( movement1 * 0.3 + movement2 * 0.3 + movement3 * -0.9 + movement4 * 0.3, - ) * Quaternion::rotation_z(movement1 * 0.5 + movement2 * 0.2 + movement3 * -0.7); + ) * Quaternion::rotation_z( + movement1 * 0.5 + movement2 * 0.2 + movement3 * -0.7, + ); - next.head.orientation = Quaternion::rotation_x(movement3 * 0.2) - * Quaternion::rotation_y(0.0 + movement2 * -0.1) - * Quaternion::rotation_z(movement1 * -0.4 + movement2 * -0.2 + movement3 * 0.6); + next.head.orientation = Quaternion::rotation_x(movement3 * 0.2) + * Quaternion::rotation_y(0.0 + movement2 * -0.1) + * Quaternion::rotation_z(movement1 * -0.4 + movement2 * -0.2 + movement3 * 0.6); - //next.hand_l.position = Vec3::new(-12.0 + movement3 * 10.0, 0.0, 0.0); + //next.hand_l.position = Vec3::new(-12.0 + movement3 * 10.0, 0.0, 0.0); - next.foot_l.position = Vec3::new( - -s_a.foot.0, - s_a.foot.1 + movement3 * 13.0, - s_a.foot.2 + movement3 * -2.0, - ); - next.foot_l.orientation = Quaternion::rotation_x(-0.8 + movement3 * 1.7); + next.foot_l.position = Vec3::new( + -s_a.foot.0, + s_a.foot.1 + movement3 * 13.0, + s_a.foot.2 + movement3 * -2.0, + ); + next.foot_l.orientation = Quaternion::rotation_x(-0.8 + movement3 * 1.7); - next.foot_r.position = Vec3::new( - s_a.foot.0, - s_a.foot.1 + 8.0 + movement3 * -13.0, - s_a.foot.2 + 5.0 + movement3 * -5.0, - ); - next.foot_r.orientation = Quaternion::rotation_x(0.9 + movement3 * -1.7); + next.foot_r.position = Vec3::new( + s_a.foot.0, + s_a.foot.1 + 8.0 + movement3 * -13.0, + s_a.foot.2 + 5.0 + movement3 * -5.0, + ); + next.foot_r.orientation = Quaternion::rotation_x(0.9 + movement3 * -1.7); + }, + Some(ToolKind::Natural) => match ability_id { + Some("common.abilities.custom.tursus.tusk_bash_leap") => { + next.head.position = Vec3::new( + 0.0, + s_a.head.0 + movement3 * 12.0, + s_a.head.1 + movement3 * 8.0, + ); + next.upper_torso.position = + Vec3::new(0.0, s_a.upper_torso.0, s_a.upper_torso.1 + movement1 * -4.0); + next.upper_torso.orientation = Quaternion::rotation_x( + movement1 * -0.3 + movement2 * -0.3 + movement3 * 0.2, + ) * Quaternion::rotation_z( + movement1 * 0.1 + movement2 * 0.2 + movement3 * -0.3, + ); + + next.head.orientation = Quaternion::rotation_x( + movement1 * -0.1 + movement2 * -0.1 + movement3 * 1.4, + ); + next.foot_l.position = Vec3::new( + -s_a.foot.0, + s_a.foot.1 + movement3 * 1.0, + s_a.foot.2 + movement1 * -0.3 + movement3 * -2.0, + ); + next.foot_l.orientation = Quaternion::rotation_x( + movement1 * -0.1 + movement2 * -0.2 + movement3 * 0.7, + ); + + next.foot_r.position = Vec3::new( + s_a.foot.0, + s_a.foot.1 + 1.0 + movement3 * -1.0, + s_a.foot.2 - 1.0 + movement1 * -0.3 + movement3 * -2.0, + ); + next.foot_r.orientation = Quaternion::rotation_x( + movement1 * -0.1 + movement2 * -0.2 + movement3 * -0.7, + ); + next.shoulder_l.orientation = Quaternion::rotation_x( + movement1 * 0.5 + movement2 * 0.1 + movement3 * -1.5, + ); + next.shoulder_r.orientation = Quaternion::rotation_x( + movement1 * 0.9 + movement2 * 0.1 + movement3 * -1.5, + ); + next.hand_l.orientation = Quaternion::rotation_x( + movement1 * 0.5 + movement2 * 0.1 + movement3 * -1.5, + ); + next.hand_r.orientation = Quaternion::rotation_x( + movement1 * 0.9 + movement2 * 0.1 + movement3 * -1.5, + ); + }, + _ => {}, + }, + Some(ToolKind::Sword) => match ability_id { + Some("common.abilities.adlet.elder.leap") => { + next.hand_l.position = Vec3::new(s_a.hhl.0 * 1.5, -s_a.hhl.1, 5.0); + next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3); + next.hand_r.position = Vec3::new(s_a.hhr.0 / 2.0, 12.0, 5.0); + next.hand_r.orientation = Quaternion::rotation_x(s_a.hhr.3); + next.main.position = Vec3::new(-6.0, 18.0, 4.0); + next.main.orientation = + Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); + next.second.position = Vec3::new(-2.0, 20.0, 4.0); + next.second.orientation = + Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.control.orientation = + Quaternion::rotation_x(movement2 * PI / 2.5 + movement3 * -2.3); + next.upper_torso.orientation = Quaternion::rotation_x( + movement1 * 0.3 + movement2 * 0.3 + movement3 * -0.9 + movement4 * 0.3, + ) * Quaternion::rotation_z( + movement1 * 0.5 + movement2 * 0.2 + movement3 * -0.7, + ); + + next.head.orientation = Quaternion::rotation_x(movement3 * 0.2) + * Quaternion::rotation_y(0.0 + movement2 * -0.1) + * Quaternion::rotation_z( + movement1 * -0.4 + movement2 * -0.2 + movement3 * 0.6, + ); + + next.foot_l.position = Vec3::new( + -s_a.foot.0, + s_a.foot.1 + movement3 * 13.0, + s_a.foot.2 + movement3 * -2.0, + ); + next.foot_l.orientation = Quaternion::rotation_x(-0.8 + movement3 * 1.7); + + next.foot_r.position = Vec3::new( + s_a.foot.0, + s_a.foot.1 + 8.0 + movement3 * -13.0, + s_a.foot.2 + 5.0 + movement3 * -5.0, + ); + next.foot_r.orientation = Quaternion::rotation_x(0.9 + movement3 * -1.7); + }, + _ => {}, + }, + _ => {}, } next diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index 7be0e999b1..b0dfae671f 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -4,6 +4,7 @@ pub mod beta; pub mod blink; pub mod charge; pub mod chargemelee; +pub mod combomelee; pub mod dash; pub mod equip; pub mod idle; @@ -24,12 +25,12 @@ pub mod wield; // Reexports pub use self::{ alpha::AlphaAnimation, beam::BeamAnimation, beta::BetaAnimation, blink::BlinkAnimation, - charge::ChargeAnimation, chargemelee::ChargeMeleeAnimation, dash::DashAnimation, - equip::EquipAnimation, idle::IdleAnimation, jump::JumpAnimation, leapmelee::LeapAnimation, - leapshockwave::LeapShockAnimation, run::RunAnimation, selfbuff::SelfBuffAnimation, - shockwave::ShockwaveAnimation, shoot::ShootAnimation, spin::SpinAnimation, - spinmelee::SpinMeleeAnimation, spritesummon::SpriteSummonAnimation, stunned::StunnedAnimation, - summon::SummonAnimation, wield::WieldAnimation, + charge::ChargeAnimation, chargemelee::ChargeMeleeAnimation, combomelee::ComboAnimation, + dash::DashAnimation, equip::EquipAnimation, idle::IdleAnimation, jump::JumpAnimation, + leapmelee::LeapAnimation, leapshockwave::LeapShockAnimation, run::RunAnimation, + selfbuff::SelfBuffAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation, + spin::SpinAnimation, spinmelee::SpinMeleeAnimation, spritesummon::SpriteSummonAnimation, + stunned::StunnedAnimation, summon::SummonAnimation, wield::WieldAnimation, }; use super::{make_bone, vek::*, FigureBoneData, Offsets, Skeleton}; @@ -263,6 +264,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Huskbrute, _) => (8.5, 4.0), (Tursus, _) => (-4.5, -14.0), (Gigasfrost, _) => (-1.5, 5.0), + (AdletElder, _) => (-8.0, 10.0), }, jaw: match (body.species, body.body_type) { (Ogre, _) => (0.0, 0.0), @@ -288,6 +290,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Huskbrute, _) => (-5.0, -5.0), (Tursus, _) => (4.0, 10.5), (Gigasfrost, _) => (-1.0, 5.5), + (AdletElder, _) => (10.5, -7.0), }, upper_torso: match (body.species, body.body_type) { (Ogre, Male) => (0.0, 27.5), @@ -314,6 +317,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Huskbrute, _) => (-1.0, 23.5), (Tursus, _) => (3.0, 26.0), (Gigasfrost, _) => (-1.0, 30.0), + (AdletElder, _) => (3.0, 19.0), }, lower_torso: match (body.species, body.body_type) { (Ogre, Male) => (1.0, -7.0), @@ -340,6 +344,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Huskbrute, _) => (-0.5, -7.0), (Tursus, _) => (-5.0, -9.0), (Gigasfrost, _) => (0.0, -5.5), + (AdletElder, _) => (0.0, -4.0), }, tail: match (body.species, body.body_type) { (Werewolf, _) => (-5.5, -2.0), @@ -347,6 +352,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Mightysaurok, _) => (-4.5, -6.0), (Slysaurok, _) => (-4.5, -6.0), (Minotaur, _) => (-3.0, -6.0), + (AdletElder, _) => (-4.5, -6.0), _ => (0.0, 0.0), }, shoulder: match (body.species, body.body_type) { @@ -374,6 +380,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Huskbrute, _) => (10.5, 0.0, -1.5), (Tursus, _) => (12.5, -2.5, -2.0), (Gigasfrost, _) => (10.5, 0.5, 0.0), + (AdletElder, _) => (8.5, 1.0, 2.5), }, hand: match (body.species, body.body_type) { (Ogre, Male) => (14.5, 0.0, -4.0), @@ -400,6 +407,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Huskbrute, _) => (13.0, 0.5, -4.0), (Tursus, _) => (15.5, 0.0, -7.0), (Gigasfrost, _) => (17.0, 0.5, -6.0), + (AdletElder, _) => (8.0, 1.5, -2.5), }, leg: match (body.species, body.body_type) { (Ogre, Male) => (0.0, 0.0, -4.0), @@ -426,6 +434,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Huskbrute, _) => (4.0, 0.0, -7.5), (Tursus, _) => (4.5, 1.0, -9.0), (Gigasfrost, _) => (6.0, 0.0, -10.0), + (AdletElder, _) => (3.0, -1.5, -4.0), }, foot: match (body.species, body.body_type) { (Ogre, Male) => (4.0, 1.0, -12.0), @@ -452,6 +461,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Huskbrute, _) => (4.5, 0.5, -12.5), (Tursus, _) => (5.5, 3.0, -14.5), (Gigasfrost, _) => (6.5, 2.0, -19.5), + (AdletElder, _) => (4.0, 3.5, -10.0), }, scaler: match (body.species, body.body_type) { (Ogre, Male) => 1.12, @@ -478,6 +488,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Huskbrute, _) => 1.2, (Tursus, _) => 1.0, (Gigasfrost, _) => 1.7, + (AdletElder, _) => 1.0, }, tempo: match (body.species, body.body_type) { (Ogre, Male) => 0.9, @@ -515,6 +526,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Huskbrute, _) => (12.5, 0.0), (Tursus, _) => (13.0, 0.0), (Gigasfrost, _) => (16.0, 0.0), + (AdletElder, _) => (10.0, 0.0), }, shl: match (body.species, body.body_type) { (Dullahan, _) => (-4.75, -11.0, 8.5, 1.47, -0.2, 0.0), @@ -606,6 +618,7 @@ fn mount_point(body: &Body) -> Vec3 { (Huskbrute, _) => (0.0, 3.0, 3.0), (Tursus, _) => (0.0, 2.0, 3.0), (Gigasfrost, _) => (1.0, 2.0, 4.0), + (AdletElder, _) => (0.0, 0.0, -1.0), } .into() } diff --git a/voxygen/anim/src/biped_large/shoot.rs b/voxygen/anim/src/biped_large/shoot.rs index f78dc4ac9f..ed4e889357 100644 --- a/voxygen/anim/src/biped_large/shoot.rs +++ b/voxygen/anim/src/biped_large/shoot.rs @@ -118,6 +118,74 @@ impl Animation for ShootAnimation { next.foot_l.orientation = Quaternion::rotation_y(move1 * 0.3 + move2 * -0.3); next.foot_r.orientation = Quaternion::rotation_y(move1 * 0.3 + move2 * -0.3); }, + Some("common.abilities.adlet.elder.air_blade") => { + let (move1base, move2base, move3) = match stage_section { + Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Recover) => (1.0, 1.0, anim_time), + _ => (0.0, 0.0, 0.0), + }; + let pullback = 1.0 - move3; + let move1abs = move1base * pullback; + let move2abs = move2base * pullback; + next.main.position = Vec3::new(-10.0, -8.0, 12.0); + next.main.orientation = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(PI / 2.0); + + next.hand_l.position = + Vec3::new(-s_a.hand.0, s_a.hand.1 + 1.0, s_a.hand.2 + 5.0); + next.hand_r.position = + Vec3::new(s_a.hand.0, s_a.hand.1 + 1.0, s_a.hand.2 + 5.0); + + next.hand_r.orientation = + Quaternion::rotation_x(move1abs * 4.0 + move2abs * -0.7) + * Quaternion::rotation_y(0.0 + move1abs * -0.7); + next.hand_l.orientation = + Quaternion::rotation_x(move1abs * 4.0 + move2abs * -0.7); + next.hand_r.orientation = + Quaternion::rotation_x(move1abs * 4.0 + move2abs * -0.7); + + next.shoulder_l.orientation = + Quaternion::rotation_x(move1abs * 4.0 + move2abs * -0.7); + next.shoulder_r.orientation = + Quaternion::rotation_x(move1abs * 4.0 + move2abs * -0.7); + next.head.orientation = + Quaternion::rotation_x(move1abs * 0.4 + move2abs * -0.2); + }, + Some("common.abilities.adlet.elder.trap") => { + let (move1base, move2base, move3) = match stage_section { + Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Recover) => (1.0, 1.0, anim_time), + _ => (0.0, 0.0, 0.0), + }; + let pullback = 1.0 - move3; + let move1abs = move1base * pullback; + let move2abs = move2base * pullback; + // next.main.position = Vec3::new(-10.0, -8.0, 12.0); + // next.main.orientation = Quaternion::rotation_y(2.5) * + // Quaternion::rotation_z(PI / 2.0); + + next.hand_l.position = + Vec3::new(-s_a.hand.0, s_a.hand.1 + 1.0, s_a.hand.2 + 5.0); + next.hand_r.position = + Vec3::new(s_a.hand.0, s_a.hand.1 + 1.0, s_a.hand.2 + 5.0); + + next.hand_r.orientation = + Quaternion::rotation_x(move1abs * 4.0 + move2abs * -0.7) + * Quaternion::rotation_y(0.0 + move1abs * -0.7); + next.hand_l.orientation = + Quaternion::rotation_x(move1abs * 4.0 + move2abs * -0.7); + next.hand_r.orientation = + Quaternion::rotation_x(move1abs * 4.0 + move2abs * -0.7); + + next.shoulder_l.orientation = + Quaternion::rotation_x(move1abs * 4.0 + move2abs * -0.7); + next.shoulder_r.orientation = + Quaternion::rotation_x(move1abs * 4.0 + move2abs * -0.7); + next.head.orientation = + Quaternion::rotation_x(move1abs * 0.4 + move2abs * -0.2); + }, _ => {}, }, Some(ToolKind::Hammer) => match ability_id { diff --git a/voxygen/anim/src/biped_small/alpha.rs b/voxygen/anim/src/biped_small/alpha.rs index 38bf624c00..fcc28b1f20 100644 --- a/voxygen/anim/src/biped_small/alpha.rs +++ b/voxygen/anim/src/biped_small/alpha.rs @@ -113,7 +113,7 @@ impl Animation for AlphaAnimation { next.tail.orientation = Quaternion::rotation_x(0.05 * fastalt * speednormcancel) * Quaternion::rotation_z(fast * 0.15 * speednormcancel); }, - Some(ToolKind::Axe) | Some(ToolKind::Hammer) => { + Some(ToolKind::Axe | ToolKind::Hammer | ToolKind::Pick) => { next.head.orientation = Quaternion::rotation_z(move1abs * 0.3 + move2abs * -0.6); next.control_l.position = Vec3::new(2.0 - s_a.grip.0 * 2.0, 1.0, 3.0); next.control_r.position = Vec3::new( diff --git a/voxygen/anim/src/biped_small/leapmelee.rs b/voxygen/anim/src/biped_small/leapmelee.rs index 809c5e2b47..01f7c6dbf9 100644 --- a/voxygen/anim/src/biped_small/leapmelee.rs +++ b/voxygen/anim/src/biped_small/leapmelee.rs @@ -39,7 +39,7 @@ impl Animation for LeapAnimation { _ => (0.0, 0.0, 0.0, 0.0), }; - if let Some(ToolKind::Hammer) = active_tool_kind { + if let Some(ToolKind::Hammer | ToolKind::Pick) = active_tool_kind { next.hand_l.position = Vec3::new(s_a.grip.0 * 2.0, 0.0, s_a.grip.2); next.hand_r.position = Vec3::new(-s_a.grip.0 * 2.0, 0.0, s_a.grip.2); next.hand_l.orientation = Quaternion::rotation_x(0.0); diff --git a/voxygen/anim/src/biped_small/shoot.rs b/voxygen/anim/src/biped_small/shoot.rs index 1e7b86e54b..231f466e97 100644 --- a/voxygen/anim/src/biped_small/shoot.rs +++ b/voxygen/anim/src/biped_small/shoot.rs @@ -7,7 +7,7 @@ use std::f32::consts::PI; pub struct ShootAnimation; -type ShootAnimationDependency = ( +type ShootAnimationDependency<'a> = ( Option, Vec3, Vec3, @@ -17,10 +17,11 @@ type ShootAnimationDependency = ( f32, Option, f32, + Option<&'a str>, ); impl Animation for ShootAnimation { - type Dependency<'a> = ShootAnimationDependency; + type Dependency<'a> = ShootAnimationDependency<'a>; type Skeleton = BipedSmallSkeleton; #[cfg(feature = "use-dyn-lib")] @@ -40,6 +41,7 @@ impl Animation for ShootAnimation { _acc_vel, stage_section, _timer, + ability_id, ): Self::Dependency<'_>, anim_time: f32, _rate: &mut f32, @@ -69,46 +71,98 @@ impl Animation for ShootAnimation { next.tail.orientation = Quaternion::rotation_x(0.05 * fastalt * speednormcancel) * Quaternion::rotation_z(fast * 0.15 * speednormcancel); - next.main.position = Vec3::new(0.0, 0.0, 0.0); - next.main.orientation = Quaternion::rotation_x(0.0); + match ability_id { + Some("common.abilities.adlet.hunter.throw") => { + next.main.position = Vec3::new(8.0, -7.0, 2.0); + next.main.orientation = Quaternion::rotation_x(PI / -2.0); - next.hand_l.position = Vec3::new(s_a.grip.0 * 4.0, 0.0, s_a.grip.2); - next.hand_r.position = Vec3::new(-s_a.grip.0 * 4.0, 0.0, s_a.grip.2); + next.hand_l.position = Vec3::new(s_a.grip.0 * 4.0, 0.0, s_a.grip.2); + next.hand_r.position = Vec3::new(5.0, -2.0, s_a.grip.2); - next.hand_l.orientation = Quaternion::rotation_x(0.0); - next.hand_r.orientation = Quaternion::rotation_x(0.0); + next.hand_l.orientation = Quaternion::rotation_x(0.0); + next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0); + }, + Some("common.abilities.adlet.tracker.trap") => { + next.main.position = Vec3::new(10.0, 0.0, 9.0); + + next.hand_l.position = Vec3::new(s_a.grip.0 * 4.0, 0.0, s_a.grip.2); + next.hand_r.position = Vec3::new(5.0, -2.0, s_a.grip.2); + + next.hand_l.orientation = Quaternion::rotation_x(0.0); + next.hand_r.orientation = Quaternion::rotation_x(PI / 2.0); + }, + _ => { + next.main.position = Vec3::new(0.0, 0.0, 0.0); + next.main.orientation = Quaternion::rotation_x(0.0); + + next.hand_l.position = Vec3::new(s_a.grip.0 * 4.0, 0.0, s_a.grip.2); + next.hand_r.position = Vec3::new(-s_a.grip.0 * 4.0, 0.0, s_a.grip.2); + + next.hand_l.orientation = Quaternion::rotation_x(0.0); + next.hand_r.orientation = Quaternion::rotation_x(0.0); + }, + }; match active_tool_kind { - Some(ToolKind::Bow) => { - let (move1base, move2base, move3) = match stage_section { - Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), - Some(StageSection::Recover) => (1.0, 1.0, anim_time), - _ => (0.0, 0.0, 0.0), - }; - let pullback = 1.0 - move3; - let move1abs = move1base * pullback; - let move2abs = move2base * pullback; - next.control_l.position = Vec3::new( - 1.0 - s_a.grip.0 * 2.0 + move2abs * -4.0, - move2abs * -8.0, - 0.0, - ); - next.control_r.position = Vec3::new(-1.0 + s_a.grip.0 * 2.0, 6.0, -2.0); + Some(ToolKind::Bow) => match ability_id { + Some("common.abilities.adlet.tracker.trap") => { + let (move1base, move2base, move3) = match stage_section { + Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Recover) => (1.0, 1.0, anim_time), + _ => (0.0, 0.0, 0.0), + }; + let pullback = 1.0 - move3; + let move1abs = move1base * pullback; + let move2abs = move2base * pullback; + next.main.position = Vec3::new(2.0, -3.0, -3.0); - next.control.position = Vec3::new( - -1.0, - 2.0 + move1abs * 3.0 + s_a.grip.2, - 3.0 + move1abs * 7.0 - s_a.grip.2 / 2.5 + s_a.grip.0 * -2.0, - ); + next.hand_l.position = Vec3::new(-s_a.hand.0, s_a.hand.1, s_a.hand.2 - 2.0); + next.hand_r.position = Vec3::new( + s_a.hand.0, + s_a.hand.1 + move2abs * 1.5, + s_a.hand.2 - 2.5 + move1abs * 2.5, + ); - next.control_l.orientation = - Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_y(-0.3); - next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + s_a.grip.0 * 0.2) - * Quaternion::rotation_y(0.5 + s_a.grip.0 * 0.2); + next.hand_r.orientation = + Quaternion::rotation_x(move1abs * 4.0 + move2abs * -0.7) + * Quaternion::rotation_y(0.0 + move1abs * -0.7); - next.control.orientation = Quaternion::rotation_x(-0.3 + move1abs * 0.4) - * Quaternion::rotation_y(0.5 * speednorm); + next.hand_l.orientation = Quaternion::rotation_x(PI / 3.0 * move1abs) + * Quaternion::rotation_y(-0.7 * move1abs + move2abs * 0.1); + }, + _ => { + let (move1base, move2base, move3) = match stage_section { + Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Recover) => (1.0, 1.0, anim_time), + _ => (0.0, 0.0, 0.0), + }; + let pullback = 1.0 - move3; + let move1abs = move1base * pullback; + let move2abs = move2base * pullback; + next.control_l.position = Vec3::new( + 1.0 - s_a.grip.0 * 2.0 + move2abs * -4.0, + move2abs * -8.0, + 0.0, + ); + next.control_r.position = Vec3::new(-1.0 + s_a.grip.0 * 2.0, 6.0, -2.0); + + next.control.position = Vec3::new( + -1.0, + 2.0 + move1abs * 3.0 + s_a.grip.2, + 3.0 + move1abs * 7.0 - s_a.grip.2 / 2.5 + s_a.grip.0 * -2.0, + ); + + next.control_l.orientation = + Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_y(-0.3); + next.control_r.orientation = + Quaternion::rotation_x(PI / 2.0 + s_a.grip.0 * 0.2) + * Quaternion::rotation_y(0.5 + s_a.grip.0 * 0.2); + + next.control.orientation = Quaternion::rotation_x(-0.3 + move1abs * 0.4) + * Quaternion::rotation_y(0.5 * speednorm); + }, }, Some(ToolKind::Blowgun) => { let (move1base, move2base, move3) = match stage_section { @@ -175,6 +229,51 @@ impl Animation for ShootAnimation { * Quaternion::rotation_y(-0.2 * speednorm) * Quaternion::rotation_z(0.5 + move1abs * 0.6); }, + Some(ToolKind::Spear) => { + let (move1base, move2base, move3) = match stage_section { + Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Recover) => (1.0, 1.0, anim_time), + _ => (0.0, 0.0, 0.0), + }; + let pullback = 1.0 - move3; + let move1abs = move1base * pullback; + let move2abs = move2base * pullback; + next.control_l.position = Vec3::new( + 0.0, + 0.0 + move1abs * 4.0 + move2abs * -4.0, + -2.0 + move1abs * -8.0, + ); + next.control_r.position = Vec3::new( + -1.0 + s_a.grip.0 * 2.0, + 2.0 + move3 * -4.0, + -2.0 + move3 * -4.0, + ); + + next.control.position = Vec3::new( + -1.0, + 0.0 + move1abs * -8.0 + move2abs * 8.0 + s_a.grip.2, + 3.0 + move1abs * 7.0 - s_a.grip.2 / 2.5 + s_a.grip.0 * -2.0, + ); + + next.control_l.orientation = + Quaternion::rotation_x(PI / 2.0) * Quaternion::rotation_y(-0.3); + next.control_r.orientation = Quaternion::rotation_x(PI / 2.0 + s_a.grip.0 * 0.2) + * Quaternion::rotation_y(0.5 + s_a.grip.0 * 0.2 + move3 * -1.5); + + next.control.orientation = + Quaternion::rotation_x(-0.3 + move1abs * 0.5 + move2abs * -0.5 + move3 * 0.5) + * Quaternion::rotation_y(0.5 * speednorm); + + if matches!(stage_section, Some(StageSection::Recover)) { + if move3 < 0.05 { + next.main.position += Vec3::new(0.0, 100000.0, -10000000.0); + } + if move3 < 0.3 { + next.main.scale = Vec3::zero(); + } + } + }, _ => {}, } diff --git a/voxygen/anim/src/biped_small/wield.rs b/voxygen/anim/src/biped_small/wield.rs index 04f717834a..33600489b7 100644 --- a/voxygen/anim/src/biped_small/wield.rs +++ b/voxygen/anim/src/biped_small/wield.rs @@ -167,7 +167,7 @@ impl Animation for WieldAnimation { * Quaternion::rotation_y(-0.2 * speednorm) * Quaternion::rotation_z(0.5); }, - Some(ToolKind::Axe) | Some(ToolKind::Hammer) => { + Some(ToolKind::Axe | ToolKind::Hammer | ToolKind::Pick) => { next.control_l.position = Vec3::new(2.0 - s_a.grip.0 * 2.0, 1.0, 3.0); next.control_r.position = Vec3::new(9.0 + s_a.grip.0 * 2.0, -1.0, -2.0 + speednorm * -3.0); diff --git a/voxygen/anim/src/character/music.rs b/voxygen/anim/src/character/music.rs index 244fddc78f..84cfdc3f78 100644 --- a/voxygen/anim/src/character/music.rs +++ b/voxygen/anim/src/character/music.rs @@ -194,6 +194,7 @@ impl Animation for MusicAnimation { * Quaternion::rotation_z(PI / -3.0); }, Some("common.abilities.music.lyre") + | Some("common.abilities.music.wildskin_drum") | Some("common.abilities.music.icy_talharpa") => { next.hand_l.position = Vec3::new( 3.0 - s_a.hand.0, diff --git a/voxygen/anim/src/character/wield.rs b/voxygen/anim/src/character/wield.rs index 0fae93c927..7863d3a424 100644 --- a/voxygen/anim/src/character/wield.rs +++ b/voxygen/anim/src/character/wield.rs @@ -308,7 +308,7 @@ impl Animation for WieldAnimation { Some(ToolKind::Instrument) => { if let Some(AbilitySpec::Custom(spec)) = active_tool_spec { match spec.as_str() { - "Lyre" | "IcyTalharpa" => { + "Lyre" | "IcyTalharpa" | "WildskinDrum" => { if speed < 0.5 { next.head.orientation = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x( diff --git a/voxygen/anim/src/quadruped_low/combomelee.rs b/voxygen/anim/src/quadruped_low/combomelee.rs new file mode 100644 index 0000000000..ffa6e85d60 --- /dev/null +++ b/voxygen/anim/src/quadruped_low/combomelee.rs @@ -0,0 +1,109 @@ +use super::{ + super::{vek::*, Animation}, + QuadrupedLowSkeleton, SkeletonAttr, +}; +use common::states::utils::{AbilityInfo, StageSection}; + +pub struct ComboAnimation; +impl Animation for ComboAnimation { + type Dependency<'a> = ( + Option<&'a str>, + Option, + Option, + usize, + f32, + f32, + ); + type Skeleton = QuadrupedLowSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"quadruped_low_combo\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "quadruped_low_combo")] + fn update_skeleton_inner( + skeleton: &Self::Skeleton, + (ability_id, stage_section, _ability_info, current_strike, global_time, timer): Self::Dependency<'_>, + anim_time: f32, + rate: &mut f32, + _s_a: &SkeletonAttr, + ) -> Self::Skeleton { + *rate = 1.0; + let mut next = (*skeleton).clone(); + + let _multi_strike_pullback = 1.0 + - if matches!(stage_section, Some(StageSection::Recover)) { + anim_time.powi(4) + } else { + 0.0 + }; + + for strike in 0..=current_strike { + match ability_id { + Some( + "common.abilities.custom.icedrake.multi_bite" + | "common.abilities.custom.icedrake.icy_bite", + ) => { + let (movement1base, movement2base, movement3) = match stage_section { + Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0), + Some(StageSection::Action) => (1.0, anim_time.powi(4), 0.0), + Some(StageSection::Recover) => (1.0, 1.0, anim_time), + _ => (0.0, 0.0, 0.0), + }; + let pullback = 1.0 - movement3; + let subtract = global_time - timer; + let check = subtract - subtract.trunc(); + let mirror = (check - 0.5).signum(); + let twitch3 = (mirror * movement3 * 9.0).sin(); + let movement1 = mirror * movement1base * pullback; + let movement2 = mirror * movement2base * pullback; + let movement1abs = movement1base * pullback; + let movement2abs = movement2base * pullback; + + match strike { + 0 | 2 => { + next.head_upper.orientation = Quaternion::rotation_z(twitch3 * -0.7); + + next.head_lower.orientation = + Quaternion::rotation_x(movement1abs * 0.35 + movement2abs * -0.9) + * Quaternion::rotation_y(movement1 * 0.7 + movement2 * -1.0); + + next.jaw.orientation = + Quaternion::rotation_x(movement1abs * -0.5 + movement2abs * 0.5); + next.chest.orientation = + Quaternion::rotation_y(movement1 * -0.08 + movement2 * 0.15) + * Quaternion::rotation_z(movement1 * -0.2 + movement2 * 0.6); + + next.tail_front.orientation = Quaternion::rotation_x(0.15) + * Quaternion::rotation_z(movement1 * -0.4 + movement2 * -0.2); + + next.tail_rear.orientation = Quaternion::rotation_x(-0.12) + * Quaternion::rotation_z(movement1 * -0.4 + movement2 * -0.2); + }, + 1 => { + next.head_upper.orientation = Quaternion::rotation_z(twitch3 * 0.2); + + next.head_lower.orientation = + Quaternion::rotation_x(movement1abs * 0.15 + movement2abs * -0.6) + * Quaternion::rotation_y(movement1 * -0.1 + movement2 * 0.15); + + next.jaw.orientation = + Quaternion::rotation_x(movement1abs * -0.9 + movement2abs * 0.9); + next.chest.orientation = + Quaternion::rotation_y(movement1 * 0.08 + movement2 * -0.15) + * Quaternion::rotation_z(movement1 * 0.2 + movement2 * -0.3); + + next.tail_front.orientation = Quaternion::rotation_x(0.15) + * Quaternion::rotation_z(movement1 * 0.4 + movement2 * 0.2); + + next.tail_rear.orientation = Quaternion::rotation_x(-0.12) + * Quaternion::rotation_z(movement1 * 0.4 + movement2 * 0.2); + }, + _ => {}, + } + }, + _ => {}, + } + } + next + } +} diff --git a/voxygen/anim/src/quadruped_low/mod.rs b/voxygen/anim/src/quadruped_low/mod.rs index a885d0ce98..5ef296779f 100644 --- a/voxygen/anim/src/quadruped_low/mod.rs +++ b/voxygen/anim/src/quadruped_low/mod.rs @@ -1,6 +1,7 @@ pub mod alpha; pub mod beta; pub mod breathe; +pub mod combomelee; pub mod dash; pub mod idle; pub mod jump; @@ -13,10 +14,10 @@ pub mod tailwhip; // Reexports pub use self::{ - alpha::AlphaAnimation, beta::BetaAnimation, breathe::BreatheAnimation, dash::DashAnimation, - idle::IdleAnimation, jump::JumpAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, - shoot::ShootAnimation, spritesummon::SpriteSummonAnimation, stunned::StunnedAnimation, - tailwhip::TailwhipAnimation, + alpha::AlphaAnimation, beta::BetaAnimation, breathe::BreatheAnimation, + combomelee::ComboAnimation, dash::DashAnimation, idle::IdleAnimation, jump::JumpAnimation, + run::RunAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation, + spritesummon::SpriteSummonAnimation, stunned::StunnedAnimation, tailwhip::TailwhipAnimation, }; use super::{make_bone, vek::*, FigureBoneData, Offsets, Skeleton}; diff --git a/voxygen/anim/src/quadruped_medium/combomelee.rs b/voxygen/anim/src/quadruped_medium/combomelee.rs new file mode 100644 index 0000000000..c52a3a5304 --- /dev/null +++ b/voxygen/anim/src/quadruped_medium/combomelee.rs @@ -0,0 +1,109 @@ +use super::{ + super::{vek::*, Animation}, + QuadrupedMediumSkeleton, SkeletonAttr, +}; +use common::states::utils::{AbilityInfo, StageSection}; + +pub struct ComboAnimation; +impl Animation for ComboAnimation { + type Dependency<'a> = ( + Option<&'a str>, + Option, + Option, + usize, + f32, + f32, + ); + type Skeleton = QuadrupedMediumSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"quadruped_medium_combo\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "quadruped_medium_combo")] + fn update_skeleton_inner( + skeleton: &Self::Skeleton, + (ability_id, stage_section, _ability_info, current_strike, global_time, timer): Self::Dependency<'_>, + anim_time: f32, + rate: &mut f32, + _s_a: &SkeletonAttr, + ) -> Self::Skeleton { + *rate = 1.0; + let mut next = (*skeleton).clone(); + + let _multi_strike_pullback = 1.0 + - if matches!(stage_section, Some(StageSection::Recover)) { + anim_time.powi(4) + } else { + 0.0 + }; + + for strike in 0..=current_strike { + match ability_id { + Some( + "common.abilities.custom.frostfang.singlestrike" + | "common.abilities.custom.frostfang.triplestrike", + ) => { + let (movement1base, movement2base, movement3) = match stage_section { + Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0), + Some(StageSection::Action) => (1.0, anim_time.powi(4), 0.0), + Some(StageSection::Recover) => (1.0, 1.0, anim_time), + _ => (0.0, 0.0, 0.0), + }; + let pullback = 1.0 - movement3; + let subtract = global_time - timer; + let check = subtract - subtract.trunc(); + let mirror = (check - 0.5).signum(); + let twitch3 = (mirror * movement3 * 9.0).sin(); + let movement1 = mirror * movement1base * pullback; + let movement2 = mirror * movement2base * pullback; + let movement1abs = movement1base * pullback; + let movement2abs = movement2base * pullback; + + match strike { + 0 | 2 => { + next.head.orientation = Quaternion::rotation_z(twitch3 * -0.7); + + next.head.orientation = + Quaternion::rotation_x(movement1abs * 0.35 + movement2abs * -0.9) + * Quaternion::rotation_y(movement1 * 0.7 + movement2 * -1.0); + + next.jaw.orientation = + Quaternion::rotation_x(movement1abs * -0.5 + movement2abs * 0.5); + next.torso_front.orientation = + Quaternion::rotation_y(movement1 * -0.08 + movement2 * 0.15) + * Quaternion::rotation_z(movement1 * -0.2 + movement2 * 0.6); + + next.torso_back.orientation = Quaternion::rotation_x(0.15) + * Quaternion::rotation_z(movement1 * -0.4 + movement2 * -0.2); + + next.tail.orientation = Quaternion::rotation_x(-0.12) + * Quaternion::rotation_z(movement1 * -0.4 + movement2 * -0.2); + }, + 1 => { + next.head.orientation = Quaternion::rotation_z(twitch3 * 0.2); + + next.neck.orientation = + Quaternion::rotation_x(movement1abs * 0.15 + movement2abs * -0.6) + * Quaternion::rotation_y(movement1 * -0.1 + movement2 * 0.15); + + next.jaw.orientation = + Quaternion::rotation_x(movement1abs * -0.9 + movement2abs * 0.9); + next.torso_front.orientation = + Quaternion::rotation_y(movement1 * 0.08 + movement2 * -0.15) + * Quaternion::rotation_z(movement1 * 0.2 + movement2 * -0.3); + + next.torso_back.orientation = Quaternion::rotation_x(0.15) + * Quaternion::rotation_z(movement1 * 0.4 + movement2 * 0.2); + + next.tail.orientation = Quaternion::rotation_x(-0.12) + * Quaternion::rotation_z(movement1 * 0.4 + movement2 * 0.2); + }, + _ => {}, + } + }, + _ => {}, + } + } + next + } +} diff --git a/voxygen/anim/src/quadruped_medium/mod.rs b/voxygen/anim/src/quadruped_medium/mod.rs index 943a351a64..0dad7ff478 100644 --- a/voxygen/anim/src/quadruped_medium/mod.rs +++ b/voxygen/anim/src/quadruped_medium/mod.rs @@ -1,5 +1,6 @@ pub mod alpha; pub mod beta; +pub mod combomelee; pub mod dash; pub mod feed; pub mod hoof; @@ -12,9 +13,10 @@ pub mod stunned; // Reexports pub use self::{ - alpha::AlphaAnimation, beta::BetaAnimation, dash::DashAnimation, feed::FeedAnimation, - hoof::HoofAnimation, idle::IdleAnimation, jump::JumpAnimation, leapmelee::LeapMeleeAnimation, - run::RunAnimation, shockwave::ShockwaveAnimation, stunned::StunnedAnimation, + alpha::AlphaAnimation, beta::BetaAnimation, combomelee::ComboAnimation, dash::DashAnimation, + feed::FeedAnimation, hoof::HoofAnimation, idle::IdleAnimation, jump::JumpAnimation, + leapmelee::LeapMeleeAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, + stunned::StunnedAnimation, }; use super::{make_bone, vek::*, FigureBoneData, Offsets, Skeleton}; @@ -650,7 +652,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Tarasque, _) => 1.05, (Tiger, _) => 0.95, (Catoblepas, _) => 1.05, - (Roshwalr, _) => 1.05, + (Roshwalr, _) => 1.75, (Barghest, _) => 1.2, (Antelope, _) => 0.95, (Kelpie, _) => 1.1, diff --git a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs index dada4b3a4b..e5d224cf05 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs @@ -206,7 +206,7 @@ impl MovementEventMapper { SfxEvent::Sneak } else { match underfoot_block_kind { - BlockKind::Snow => SfxEvent::Run(BlockKind::Snow), + BlockKind::Snow | BlockKind::ArtSnow => SfxEvent::Run(BlockKind::Snow), BlockKind::Rock | BlockKind::WeakRock | BlockKind::GlowingRock @@ -238,7 +238,7 @@ impl MovementEventMapper { SfxEvent::Swim } else if physics_state.on_ground.is_some() && vel.magnitude() > 0.1 { match underfoot_block_kind { - BlockKind::Snow => SfxEvent::Run(BlockKind::Snow), + BlockKind::Snow | BlockKind::ArtSnow => SfxEvent::Run(BlockKind::Snow), BlockKind::Rock | BlockKind::WeakRock | BlockKind::GlowingRock @@ -264,7 +264,7 @@ impl MovementEventMapper { SfxEvent::Swim } else if physics_state.on_ground.is_some() && vel.magnitude() > 0.1 { match underfoot_block_kind { - BlockKind::Snow => SfxEvent::QuadRun(BlockKind::Snow), + BlockKind::Snow | BlockKind::ArtSnow => SfxEvent::QuadRun(BlockKind::Snow), BlockKind::Rock | BlockKind::WeakRock | BlockKind::GlowingRock diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 03938a20fd..565cc0ebb5 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -174,6 +174,10 @@ pub enum SfxEvent { CyclopsCharge, LaserBeam, Music(ToolKind, AbilitySpec), + Yeet, + Klonk, + SmashKlonk, + Woosh, } #[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)] @@ -493,6 +497,10 @@ impl SfxMgr { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::LaserBeam); audio.emit_sfx(sfx_trigger_item, *pos, None, underwater); }, + Body::Object(object::Body::AdletTrap) => { + let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Yeet); + audio.emit_sfx(sfx_trigger_item, *pos, None, underwater); + }, _ => { // not mapped to sfx file }, @@ -529,6 +537,23 @@ impl SfxMgr { audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), underwater); } }, + Body::Object(object::Body::AdletTrap) => { + if target.is_none() { + let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Klonk); + audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), underwater); + } else if *source == client.uid() { + let sfx_trigger_item = triggers.get_key_value(&SfxEvent::SmashKlonk); + audio.emit_sfx( + sfx_trigger_item, + client.position().unwrap_or(*pos), + Some(2.0), + underwater, + ); + } else { + let sfx_trigger_item = triggers.get_key_value(&SfxEvent::SmashKlonk); + audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), underwater); + } + }, _ => {}, }, Outcome::SkillPointGain { uid, .. } => { @@ -672,6 +697,15 @@ impl SfxMgr { _ => {}, }; }, + Outcome::Woosh { pos, .. } => { + let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Woosh); + audio.emit_sfx( + sfx_trigger_item, + pos.map(|e| e + 0.5), + Some(3.0), + underwater, + ); + }, Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => {}, } } diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index e171d538e6..f3aa7b33ad 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -532,6 +532,9 @@ image_ids! { mmap_site_gnarling: "voxygen.element.ui.map.buttons.gnarling", mmap_site_gnarling_hover: "voxygen.element.ui.map.buttons.gnarling_hover", mmap_site_gnarling_bg: "voxygen.element.ui.map.buttons.gnarling_bg", + mmap_site_adlet: "voxygen.element.ui.map.buttons.adlet", + mmap_site_adlet_hover: "voxygen.element.ui.map.buttons.adlet_hover", + mmap_site_adlet_bg: "voxygen.element.ui.map.buttons.adlet_bg", mmap_site_minotaur: "voxygen.element.ui.map.buttons.minotaur", mmap_site_minotaur_hover: "voxygen.element.ui.map.buttons.minotaur_hover", mmap_site_minotaur_bg: "voxygen.element.ui.map.buttons.minotaur_bg", diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index 1a94317184..0868a28036 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -924,6 +924,7 @@ impl<'a> Widget for Map<'a> { SiteKind::Gnarling => i18n.get_msg("hud-map-gnarling"), SiteKind::ChapelSite => i18n.get_msg("hud-map-chapel_Site"), SiteKind::Bridge => i18n.get_msg("hud-map-bridge"), + SiteKind::Adlet => i18n.get_msg("hud-map-adlet"), }); let (difficulty, desc) = match &site.kind { SiteKind::Town => (None, i18n.get_msg("hud-map-town")), @@ -950,6 +951,7 @@ impl<'a> Widget for Map<'a> { SiteKind::Gnarling => (Some(0), i18n.get_msg("hud-map-gnarling")), SiteKind::ChapelSite => (Some(3), i18n.get_msg("hud-map-chapel_site")), SiteKind::Bridge => (None, i18n.get_msg("hud-map-bridge")), + SiteKind::Adlet => (Some(1), i18n.get_msg("hud-map-adlet")), }; let desc = desc.into_owned() + &get_site_economy(site_rich); let site_btn = Button::image(match &site.kind { @@ -959,6 +961,7 @@ impl<'a> Widget for Map<'a> { SiteKind::Cave => self.imgs.mmap_site_cave, SiteKind::Tree => self.imgs.mmap_site_tree, SiteKind::Gnarling => self.imgs.mmap_site_gnarling, + SiteKind::Adlet => self.imgs.mmap_site_adlet, SiteKind::Dungeon { difficulty } => match difficulty { 4 => self.imgs.mmap_site_minotaur, 5 => self.imgs.mmap_site_mindflayer, @@ -979,6 +982,7 @@ impl<'a> Widget for Map<'a> { SiteKind::Cave => self.imgs.mmap_site_cave_hover, SiteKind::Tree => self.imgs.mmap_site_tree_hover, SiteKind::Gnarling => self.imgs.mmap_site_gnarling_hover, + SiteKind::Adlet => self.imgs.mmap_site_adlet_hover, SiteKind::Dungeon { difficulty } => match difficulty { 4 => self.imgs.mmap_site_minotaur_hover, 5 => self.imgs.mmap_site_mindflayer_hover, @@ -995,15 +999,16 @@ impl<'a> Widget for Map<'a> { match &site.kind { SiteKind::Town => TEXT_COLOR, SiteKind::Castle => TEXT_COLOR, - SiteKind::Dungeon { .. } | SiteKind::Gnarling | SiteKind::ChapelSite => { - match difficulty { - Some(0) => QUALITY_LOW, - Some(1) => QUALITY_COMMON, - Some(2) => QUALITY_MODERATE, - Some(3) => QUALITY_HIGH, - Some(4 | 5) => QUALITY_EPIC, - _ => TEXT_COLOR, - } + SiteKind::Dungeon { .. } + | SiteKind::Gnarling + | SiteKind::ChapelSite + | SiteKind::Adlet => match difficulty { + Some(0) => QUALITY_LOW, + Some(1) => QUALITY_COMMON, + Some(2) => QUALITY_MODERATE, + Some(3) => QUALITY_HIGH, + Some(4 | 5) => QUALITY_EPIC, + _ => TEXT_COLOR, }, SiteKind::Cave => TEXT_COLOR, SiteKind::Tree => TEXT_COLOR, @@ -1022,9 +1027,10 @@ impl<'a> Widget for Map<'a> { // Only display sites that are toggled on let show_site = match &site.kind { SiteKind::Town => show_towns, - SiteKind::Dungeon { .. } | SiteKind::Gnarling | SiteKind::ChapelSite => { - show_dungeons - }, + SiteKind::Dungeon { .. } + | SiteKind::Gnarling + | SiteKind::ChapelSite + | SiteKind::Adlet => show_dungeons, SiteKind::Castle => show_castles, SiteKind::Cave => show_caves, SiteKind::Tree => show_trees, @@ -1081,7 +1087,10 @@ impl<'a> Widget for Map<'a> { dif_img.set(state.ids.site_difs[i], ui) } }, - SiteKind::Dungeon { .. } | SiteKind::Gnarling | SiteKind::ChapelSite => { + SiteKind::Dungeon { .. } + | SiteKind::Gnarling + | SiteKind::ChapelSite + | SiteKind::Adlet => { if show_dungeons { dif_img.set(state.ids.site_difs[i], ui) } diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 8cfcae7930..bca1be959a 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -701,6 +701,7 @@ impl<'a> Widget for MiniMap<'a> { SiteKind::Tree => None, SiteKind::Gnarling => Some(0), SiteKind::Bridge => None, + SiteKind::Adlet => Some(1), }; Image::new(match &site.kind { @@ -712,6 +713,7 @@ impl<'a> Widget for MiniMap<'a> { SiteKind::Tree => self.imgs.mmap_site_tree, SiteKind::Gnarling => self.imgs.mmap_site_gnarling_bg, SiteKind::Bridge => self.imgs.mmap_site_bridge_bg, + SiteKind::Adlet => self.imgs.mmap_site_adlet_bg, }) .x_y_position_relative_to( state.ids.map_layers[0], @@ -738,6 +740,7 @@ impl<'a> Widget for MiniMap<'a> { SiteKind::Tree => self.imgs.mmap_site_tree, SiteKind::Gnarling => self.imgs.mmap_site_gnarling, SiteKind::Bridge => self.imgs.mmap_site_bridge, + SiteKind::Adlet => self.imgs.mmap_site_adlet, }) .middle_of(state.ids.mmap_site_icons_bgs[i]) .w_h(20.0, 20.0) diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index bcff1114c3..f444e4b23a 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -505,6 +505,7 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id { "common.abilities.music.double_bass" => imgs.instrument, "common.abilities.music.glass_flute" => imgs.instrument, "common.abilities.music.lyre" => imgs.instrument, + "common.abilities.music.wildskin_drum" => imgs.instrument, "common.abilities.music.icy_talharpa" => imgs.instrument, "common.abilities.music.washboard" => imgs.instrument, _ => imgs.not_found, diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 2f27899030..30e719a271 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -2685,6 +2685,38 @@ impl FigureMgr { ), } }, + CharacterState::ComboMelee2(s) => { + let timer = s.timer.as_secs_f32(); + let current_strike = s.completed_strikes % s.static_data.strikes.len(); + let strike_data = s.static_data.strikes[current_strike]; + let progress = match s.stage_section { + StageSection::Buildup => { + timer / strike_data.buildup_duration.as_secs_f32() + }, + StageSection::Action => { + timer / strike_data.swing_duration.as_secs_f32() + }, + StageSection::Recover => { + timer / strike_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + + anim::quadruped_medium::ComboAnimation::update_skeleton( + &target_base, + ( + ability_id, + Some(s.stage_section), + Some(s.static_data.ability_info), + current_strike, + time, + state.state_time, + ), + progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, CharacterState::Stunned(s) => { let stage_time = s.timer.as_secs_f32(); let stage_progress = match s.stage_section { @@ -2991,6 +3023,38 @@ impl FigureMgr { }, } }, + CharacterState::ComboMelee2(s) => { + let timer = s.timer.as_secs_f32(); + let current_strike = s.completed_strikes % s.static_data.strikes.len(); + let strike_data = s.static_data.strikes[current_strike]; + let progress = match s.stage_section { + StageSection::Buildup => { + timer / strike_data.buildup_duration.as_secs_f32() + }, + StageSection::Action => { + timer / strike_data.swing_duration.as_secs_f32() + }, + StageSection::Recover => { + timer / strike_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + + anim::quadruped_low::ComboAnimation::update_skeleton( + &target_base, + ( + ability_id, + Some(s.stage_section), + Some(s.static_data.ability_info), + current_strike, + time, + state.state_time, + ), + progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, CharacterState::ComboMelee(s) => { let stage_index = (s.stage - 1) as usize; let stage_time = s.timer.as_secs_f32(); @@ -3760,6 +3824,7 @@ impl FigureMgr { state.acc_vel, Some(s.stage_section), state.state_time, + ability_id, ), stage_progress, &mut state_animation_rate, @@ -3791,6 +3856,7 @@ impl FigureMgr { state.acc_vel, Some(s.stage_section), state.state_time, + ability_id, ), stage_progress, &mut state_animation_rate, @@ -3854,6 +3920,7 @@ impl FigureMgr { state.acc_vel, Some(s.stage_section), state.state_time, + ability_id, ), stage_progress, &mut state_animation_rate, @@ -5259,6 +5326,37 @@ impl FigureMgr { skeleton_attr, ) }, + CharacterState::ComboMelee2(s) => { + let timer = s.timer.as_secs_f32(); + let current_strike = s.completed_strikes % s.static_data.strikes.len(); + let strike_data = s.static_data.strikes[current_strike]; + let progress = match s.stage_section { + StageSection::Buildup => { + timer / strike_data.buildup_duration.as_secs_f32() + }, + StageSection::Action => { + timer / strike_data.swing_duration.as_secs_f32() + }, + StageSection::Recover => { + timer / strike_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + + anim::biped_large::ComboAnimation::update_skeleton( + &target_base, + ( + ability_id, + Some(s.stage_section), + Some(s.static_data.ability_info), + current_strike, + move_dir, + ), + progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, CharacterState::BasicRanged(s) => { let stage_time = s.timer.as_secs_f32(); @@ -5586,6 +5684,7 @@ impl FigureMgr { rel_vel, time, Some(s.stage_section), + ability_id, ), stage_progress, &mut state_animation_rate, diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 8fa1436428..8a28fa0c79 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -391,6 +391,7 @@ impl ParticleMgr { | Outcome::IceSpikes { .. } | Outcome::IceCrack { .. } | Outcome::Glider { .. } + | Outcome::Woosh { .. } | Outcome::LaserBeam { .. } => {}, } } diff --git a/world/examples/dungeon_voxel_export.rs b/world/examples/dungeon_voxel_export.rs index f4eade09ad..4bb44da365 100644 --- a/world/examples/dungeon_voxel_export.rs +++ b/world/examples/dungeon_voxel_export.rs @@ -51,12 +51,16 @@ fn main() -> Result { for x in aabb.min.x..aabb.max.x { for y in aabb.min.y..aabb.max.y { + let col = canvas + .col(Vec2::new(x, y)) + .map(|col| col.get_info()) + .unwrap_or_default(); for z in aabb.min.z..aabb.max.z { let pos = Vec3::new(x, y, z); let _ = volume.map(pos, |block| { if let Some(block) = - fill.sample_at(&prim_tree, prim, pos, canvas, block) + fill.sample_at(&prim_tree, prim, pos, canvas, block, &col) { block } else { diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 0bbd819917..33ccff42d5 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -348,6 +348,16 @@ pub fn block_from_structure( }), )); }, + StructureBlock::BoneKeyhole(consumes) => { + return Some(( + Block::air(SpriteKind::BoneKeyhole), + Some(SpriteCfg { + unlock: Some(UnlockKind::Consumes(ItemDefinitionIdOwned::Simple( + consumes.clone(), + ))), + }), + )); + }, }; Some((block?, None)) diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index e4cc771cc5..8397cf46c6 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -248,6 +248,14 @@ impl Civs { )?, SiteKind::ChapelSite, ), + 44..=49 => ( + find_site_loc( + &mut ctx, + &ProximityRequirements::new().avoid_all_of(this.gnarling_enemies(), 40), + SiteKind::Adlet, + )?, + SiteKind::Adlet, + ), _ => ( find_site_loc( &mut ctx, @@ -289,6 +297,7 @@ impl Civs { SiteKind::Gnarling => (16i32, 10.0), SiteKind::Citadel => (16i32, 0.0), SiteKind::Bridge(_, _) => (0, 0.0), + SiteKind::Adlet => (16i32, 0.0), }; let (raise, raise_dist, make_waypoint): (f32, i32, bool) = match &site.kind { @@ -420,6 +429,12 @@ impl Civs { *a, *b, )), + SiteKind::Adlet => WorldSite::adlet(site2::Site::generate_adlet( + &Land::from_sim(ctx.sim), + &mut rng, + wpos, + index_ref, + )), } }); sim_site.site_tmp = Some(site); @@ -1260,6 +1275,13 @@ impl Civs { }) } + fn adlet_enemies(&self) -> impl Iterator> + '_ { + self.sites().filter_map(|s| match s.kind { + SiteKind::Tree | SiteKind::GiantTree => None, + _ => Some(s.center), + }) + } + fn chapel_site_enemies(&self) -> impl Iterator> + '_ { self.sites().filter_map(|s| match s.kind { SiteKind::Tree | SiteKind::GiantTree => None, @@ -1545,6 +1567,7 @@ pub enum SiteKind { Gnarling, Citadel, Bridge(Vec2, Vec2), + Adlet, } impl SiteKind { @@ -1672,6 +1695,7 @@ impl SiteKind { && (-0.3..0.4).contains(&chunk.temp) && chunk.tree_density > 0.75 }, + SiteKind::Adlet => chunk.temp < -0.2 && chunk.cliff_height > 25.0, SiteKind::GiantTree | SiteKind::Tree => { on_land() && on_flat_terrain() diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index bf5fb29b32..03cfb8ef8e 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1245,3 +1245,24 @@ pub struct ColumnSample<'a> { pub chunk: &'a SimChunk, } + +impl ColumnSample<'_> { + pub fn get_info(&self) -> ColInfo { + ColInfo { + alt: self.alt, + basement: self.basement, + cliff_offset: self.cliff_offset, + cliff_height: self.cliff_height, + } + } +} + +// For a version of ColumnSample that can easily be moved around. Feel free to +// add non reference fields as needed. +#[derive(Clone, Default)] +pub struct ColInfo { + pub alt: f32, + pub basement: f32, + pub cliff_offset: f32, + pub cliff_height: f32, +} diff --git a/world/src/layer/spot.rs b/world/src/layer/spot.rs index 70eac1c3e7..1407ec04f6 100644 --- a/world/src/layer/spot.rs +++ b/world/src/layer/spot.rs @@ -633,9 +633,9 @@ pub fn apply_spots_to(canvas: &mut Canvas, _dynamic_rng: &mut impl Rng) { base_structures: Some("spots_general.igloo"), entity_radius: 2.0, entities: &[ - (3..5, "common.entity.dungeon.tier-1.hunter"), - (3..5, "common.entity.dungeon.tier-1.icepicker"), - (2..3, "common.entity.dungeon.tier-1.tracker"), + (3..5, "common.entity.dungeon.adlet.hunter"), + (3..5, "common.entity.dungeon.adlet.icepicker"), + (2..3, "common.entity.dungeon.adlet.tracker"), ], }, Spot::PirateHideout => SpotConfig { diff --git a/world/src/lib.rs b/world/src/lib.rs index 5031773127..2ef6f778cf 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -177,6 +177,7 @@ impl World { civ::SiteKind::ChapelSite => world_msg::SiteKind::ChapelSite, civ::SiteKind::Citadel => world_msg::SiteKind::Castle, civ::SiteKind::Bridge(_, _) => world_msg::SiteKind::Bridge, + civ::SiteKind::Adlet => world_msg::SiteKind::Adlet, }, wpos: site.center * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), } diff --git a/world/src/site/economy/context.rs b/world/src/site/economy/context.rs index b9e427c363..4db0cece91 100644 --- a/world/src/site/economy/context.rs +++ b/world/src/site/economy/context.rs @@ -129,6 +129,7 @@ impl Environment { SiteKind::Tree(_) => (), SiteKind::GiantTree(_) => (), SiteKind::Gnarling(_) => {}, + SiteKind::Adlet(_) => {}, SiteKind::ChapelSite(_) => {}, SiteKind::Bridge(_) => {}, } diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index b25d7e2e7c..644c126a50 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -74,6 +74,7 @@ pub enum SiteKind { GiantTree(site2::Site), Gnarling(site2::Site), Bridge(site2::Site), + Adlet(site2::Site), } impl Site { @@ -98,6 +99,13 @@ impl Site { } } + pub fn adlet(g: site2::Site) -> Self { + Self { + kind: SiteKind::Adlet(g), + economy: Economy::default(), + } + } + pub fn castle(c: Castle) -> Self { Self { kind: SiteKind::Castle(c), @@ -175,6 +183,7 @@ impl Site { SiteKind::GiantTree(gt) => gt.radius(), SiteKind::Gnarling(g) => g.radius(), SiteKind::Bridge(b) => b.radius(), + SiteKind::Adlet(g) => g.radius(), } } @@ -192,6 +201,7 @@ impl Site { SiteKind::GiantTree(gt) => gt.origin, SiteKind::Gnarling(g) => g.origin, SiteKind::Bridge(b) => b.origin, + SiteKind::Adlet(g) => g.origin, } } @@ -209,6 +219,7 @@ impl Site { SiteKind::GiantTree(gt) => gt.spawn_rules(wpos), SiteKind::Gnarling(g) => g.spawn_rules(wpos), SiteKind::Bridge(b) => b.spawn_rules(wpos), + SiteKind::Adlet(g) => g.spawn_rules(wpos), } } @@ -226,6 +237,7 @@ impl Site { SiteKind::GiantTree(gt) => gt.name(), SiteKind::Gnarling(g) => g.name(), SiteKind::Bridge(b) => b.name(), + SiteKind::Adlet(g) => g.name(), } } @@ -262,6 +274,7 @@ impl Site { SiteKind::GiantTree(gt) => gt.render(canvas, dynamic_rng), SiteKind::Gnarling(g) => g.render(canvas, dynamic_rng), SiteKind::Bridge(b) => b.render(canvas, dynamic_rng), + SiteKind::Adlet(g) => g.render(canvas, dynamic_rng), } } @@ -292,6 +305,7 @@ impl Site { SiteKind::GiantTree(gt) => gt.apply_supplement(dynamic_rng, wpos2d, supplement), SiteKind::Gnarling(g) => g.apply_supplement(dynamic_rng, wpos2d, supplement), SiteKind::Bridge(b) => b.apply_supplement(dynamic_rng, wpos2d, supplement), + SiteKind::Adlet(g) => g.apply_supplement(dynamic_rng, wpos2d, supplement), } } @@ -322,6 +336,7 @@ impl Site { SiteKind::GiantTree(site2) => Some(site2), SiteKind::Gnarling(site2) => Some(site2), SiteKind::Bridge(site2) => Some(site2), + SiteKind::Adlet(site2) => Some(site2), } } } diff --git a/world/src/site/namegen.rs b/world/src/site/namegen.rs index a9d5453556..4fd6e6576e 100644 --- a/world/src/site/namegen.rs +++ b/world/src/site/namegen.rs @@ -643,4 +643,28 @@ impl<'a, R: Rng> NameGen<'a, R> { ]; self.generate_theme_from_parts(&start, &middle, &vowel, &end) } + + // inuit inspired location names for adlet stronghold + pub fn generate_adlet(mut self) -> String { + let start = [ + "p", "k", "h", "q", "s", "t", "y", "n", "m", "an", "ap", "ang", "am", "at", "arn", + "ats", "av", "aj", "in", "ikk", "ig", "im", "iq", "uak", "ual", "uj", "uk", "um", "ul", + "unn", "ut", "adl", + ]; + let middle = [ + "m", "k", "n", "ks", "gl", "ll", "g", "l", "t", "nn", "kt", "tt", "rj", "rl", "ts", + "qt", "ng", "gg", "ss", "kk", + ]; + let vowel = ["a", "i", "u", "ia", "ui", "ua", "aa", "uu"]; + let end = [ + "uktu", "akut", "ukut", "aakut", "uvit", "avit", "ivit", "anuq", "attiq", "aqtuq", + "akka", "ikka", "ipit", "ugit", "agit", "uvusi", "avusi", "ivusi", "aali", "uli", + "aluk", "uluk", "alu", "uipit", "unga", "asuna", "uulik", "ivun", "avun", "adlet", + "adlit", "anit", "unit", "aluit", "angit", "arluut", "uluut", "araaq", "anait", + "uqsuit", "aruq", "ualik", "auti", "aliit", "igiit", "aana", "akkuq", "amali", "igit", + "irniq", "aasi", "ashi", "asik", "ukkit", "ajuq", "anook", "ivak", "attiq", "iksik", + "aaluk", "anik", "usuuq", + ]; + self.generate_theme_from_parts(&start, &middle, &vowel, &end) + } } diff --git a/world/src/site2/gen.rs b/world/src/site2/gen.rs index 5b3bf22158..3567e3d9fe 100644 --- a/world/src/site2/gen.rs +++ b/world/src/site2/gen.rs @@ -4,6 +4,7 @@ use {crate::LIB, std::ffi::CStr}; use super::*; use crate::{ block::block_from_structure, + column::ColInfo, site2::util::Dir, util::{RandomField, Sampler}, CanvasInfo, @@ -72,6 +73,7 @@ pub enum Primitive { /// A sampling function is always a subset of another primitive to avoid /// needing infinite bounds Sampling(Id, Box) -> bool>), + ColSampling(Id, Box, &ColInfo) -> bool>), Prefab(Box), // Combinators @@ -139,6 +141,7 @@ impl std::fmt::Debug for Primitive { .field(&height) .finish(), Primitive::Sampling(prim, _) => f.debug_tuple("Sampling").field(&prim).finish(), + Primitive::ColSampling(prim, _) => f.debug_tuple("ColSampling").field(&prim).finish(), Primitive::Prefab(prefab) => f.debug_tuple("Prefab").field(&prefab).finish(), Primitive::Intersect(a, b) => f.debug_tuple("Intersect").field(&a).field(&b).finish(), Primitive::Union(a, b) => f.debug_tuple("Union").field(&a).field(&b).finish(), @@ -180,6 +183,13 @@ impl Primitive { Self::Sampling(a.into(), f) } + pub fn column_sampling( + a: impl Into>, + f: Box, &ColInfo) -> bool>, + ) -> Self { + Self::ColSampling(a.into(), f) + } + pub fn translate(a: impl Into>, trans: Vec3) -> Self { Self::Translate(a.into(), trans) } @@ -216,7 +226,12 @@ pub enum Fill { } impl Fill { - fn contains_at(tree: &Store, prim: Id, pos: Vec3) -> bool { + fn contains_at( + tree: &Store, + prim: Id, + pos: Vec3, + col: &ColInfo, + ) -> bool { // Custom closure because vek's impl of `contains_point` is inclusive :( let aabb_contains = |aabb: Aabb, pos: Vec3| { (aabb.min.x..aabb.max.x).contains(&pos.x) @@ -375,19 +390,20 @@ impl Fill { let z_check = (projected_z..=(projected_z + height)).contains(&(pos.z as f32)); xy_check && z_check }, - Primitive::Sampling(a, f) => Self::contains_at(tree, *a, pos) && f(pos), + Primitive::Sampling(a, f) => Self::contains_at(tree, *a, pos, col) && f(pos), + Primitive::ColSampling(a, f) => Self::contains_at(tree, *a, pos, col) && f(pos, col), Primitive::Prefab(p) => !matches!(p.get(pos), Err(_) | Ok(StructureBlock::None)), Primitive::Intersect(a, b) => { - Self::contains_at(tree, *a, pos) && Self::contains_at(tree, *b, pos) + Self::contains_at(tree, *a, pos, col) && Self::contains_at(tree, *b, pos, col) }, Primitive::Union(a, b) => { - Self::contains_at(tree, *a, pos) || Self::contains_at(tree, *b, pos) + Self::contains_at(tree, *a, pos, col) || Self::contains_at(tree, *b, pos, col) }, Primitive::Without(a, b) => { - Self::contains_at(tree, *a, pos) && !Self::contains_at(tree, *b, pos) + Self::contains_at(tree, *a, pos, col) && !Self::contains_at(tree, *b, pos, col) }, Primitive::Translate(prim, vec) => { - Self::contains_at(tree, *prim, pos.map2(*vec, i32::saturating_sub)) + Self::contains_at(tree, *prim, pos.map2(*vec, i32::saturating_sub), col) }, Primitive::Scale(prim, vec) => { let center = Self::get_bounds(tree, *prim).as_::().center(); @@ -395,12 +411,17 @@ impl Fill { let spos = (center + ((fpos - center) / vec)) .map(|x| x.round()) .as_::(); - Self::contains_at(tree, *prim, spos) + Self::contains_at(tree, *prim, spos, col) }, Primitive::RotateAbout(prim, mat, vec) => { let mat = mat.as_::().transposed(); let vec = vec - 0.5; - Self::contains_at(tree, *prim, (vec + mat * (pos.as_::() - vec)).as_()) + Self::contains_at( + tree, + *prim, + (vec + mat * (pos.as_::() - vec)).as_(), + col, + ) }, Primitive::Repeat(prim, offset, count) => { if count == &0 { @@ -419,7 +440,7 @@ impl Fill { .reduce_min() .clamp(0, count as i32); let pos = pos - offset * min; - Self::contains_at(tree, *prim, pos) + Self::contains_at(tree, *prim, pos, col) } }, } @@ -432,8 +453,9 @@ impl Fill { pos: Vec3, canvas_info: &CanvasInfo, old_block: Block, + col: &ColInfo, ) -> Option { - if Self::contains_at(tree, prim, pos) { + if Self::contains_at(tree, prim, pos, col) { match self { Fill::Block(block) => Some(*block), Fill::Sprite(sprite) => Some(if old_block.is_filled() { @@ -553,7 +575,9 @@ impl Fill { }; vec![Aabb { min, max }] }, - Primitive::Sampling(a, _) => Self::get_bounds_inner(tree, *a), + Primitive::Sampling(a, _) | Primitive::ColSampling(a, _) => { + Self::get_bounds_inner(tree, *a) + }, Primitive::Prefab(p) => vec![p.get_bounds()], Primitive::Intersect(a, b) => or_zip_with( Self::get_bounds_opt(tree, *a), @@ -693,6 +717,7 @@ impl Painter { | Primitive::SegmentPrism { .. } | Primitive::Prefab(_) => prev_depth, Primitive::Sampling(a, _) + | Primitive::ColSampling(a, _) | Primitive::Translate(a, _) | Primitive::Scale(a, _) | Primitive::RotateAbout(a, _, _) @@ -1261,6 +1286,17 @@ impl<'a> PrimitiveRef<'a> { .prim(Primitive::sampling(self, Box::new(sampling))) } + /// Returns a `PrimitiveRef` that conforms to the provided sampling + /// function. + #[must_use] + pub fn sample_with_column( + self, + sampling: impl Fn(Vec3, &ColInfo) -> bool + 'static, + ) -> PrimitiveRef<'a> { + self.painter + .prim(Primitive::column_sampling(self, Box::new(sampling))) + } + /// Rotates a primitive about it's own's bounds minimum point, #[must_use] pub fn rotate_about_min(self, mat: Mat3) -> PrimitiveRef<'a> { diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index 707f786eb7..72e6049ccf 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -105,6 +105,7 @@ impl Site { .filter_map(|plot| match &plot.kind { PlotKind::Dungeon(d) => Some(d.spawn_rules(wpos)), PlotKind::Gnarling(g) => Some(g.spawn_rules(wpos)), + PlotKind::Adlet(a) => Some(a.spawn_rules(wpos)), _ => None, }) .fold(base_spawn_rules, |a, b| a.combine(b)) @@ -470,6 +471,42 @@ impl Site { site } + pub fn generate_adlet( + land: &Land, + rng: &mut impl Rng, + origin: Vec2, + index: IndexRef, + ) -> Self { + let mut rng = reseed(rng); + let mut site = Site { + origin, + ..Site::default() + }; + site.demarcate_obstacles(land); + let adlet_stronghold = plot::AdletStronghold::generate(origin, land, &mut rng, index); + site.name = adlet_stronghold.name().to_string(); + let (cavern_aabr, wall_aabr) = adlet_stronghold.plot_tiles(origin); + let plot = site.create_plot(Plot { + kind: PlotKind::Adlet(adlet_stronghold), + root_tile: cavern_aabr.center(), + tiles: aabr_tiles(cavern_aabr) + .chain(aabr_tiles(wall_aabr)) + .collect(), + seed: rng.gen(), + }); + site.blit_aabr(cavern_aabr, Tile { + kind: TileKind::AdletStronghold, + plot: Some(plot), + hard_alt: None, + }); + site.blit_aabr(wall_aabr, Tile { + kind: TileKind::AdletStronghold, + plot: Some(plot), + hard_alt: None, + }); + site + } + pub fn generate_giant_tree(land: &Land, rng: &mut impl Rng, origin: Vec2) -> Self { let mut rng = reseed(rng); let mut site = Site { @@ -1341,6 +1378,7 @@ impl Site { PlotKind::SeaChapel(sea_chapel) => sea_chapel.render_collect(self, canvas), PlotKind::Dungeon(dungeon) => dungeon.render_collect(self, canvas), PlotKind::Gnarling(gnarling) => gnarling.render_collect(self, canvas), + PlotKind::Adlet(adlet) => adlet.render_collect(self, canvas), PlotKind::GiantTree(giant_tree) => giant_tree.render_collect(self, canvas), PlotKind::CliffTower(cliff_tower) => cliff_tower.render_collect(self, canvas), PlotKind::SavannahPit(savannah_pit) => savannah_pit.render_collect(self, canvas), @@ -1371,7 +1409,8 @@ impl Site { for x in aabb.min.x..aabb.max.x { for y in aabb.min.y..aabb.max.y { - let col_tile = self.wpos_tile(Vec2::new(x, y)); + let wpos = Vec2::new(x, y); + let col_tile = self.wpos_tile(wpos); if /* col_tile.is_building() && */ col_tile @@ -1383,12 +1422,18 @@ impl Site { continue; } let mut last_block = None; + + let col = canvas + .col(wpos) + .map(|col| col.get_info()) + .unwrap_or_default(); + for z in aabb.min.z..aabb.max.z { let pos = Vec3::new(x, y, z); canvas.map(pos, |block| { let current_block = - fill.sample_at(&prim_tree, prim, pos, &info, block); + fill.sample_at(&prim_tree, prim, pos, &info, block, &col); if let (Some(last_block), None) = (last_block, current_block) { spawn(pos, last_block); } @@ -1420,6 +1465,7 @@ impl Site { match &plot.kind { PlotKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, supplement), PlotKind::Gnarling(g) => g.apply_supplement(dynamic_rng, wpos2d, supplement), + PlotKind::Adlet(a) => a.apply_supplement(dynamic_rng, wpos2d, supplement), _ => {}, } } diff --git a/world/src/site2/plot.rs b/world/src/site2/plot.rs index 6afb2f1ba5..e347e191fa 100644 --- a/world/src/site2/plot.rs +++ b/world/src/site2/plot.rs @@ -1,3 +1,4 @@ +mod adlet; mod bridge; mod castle; mod citadel; @@ -13,10 +14,11 @@ mod sea_chapel; mod workshop; pub use self::{ - bridge::Bridge, castle::Castle, citadel::Citadel, cliff_tower::CliffTower, - desert_city_multiplot::DesertCityMultiPlot, desert_city_temple::DesertCityTemple, - dungeon::Dungeon, giant_tree::GiantTree, gnarling::GnarlingFortification, house::House, - savannah_pit::SavannahPit, sea_chapel::SeaChapel, workshop::Workshop, + adlet::AdletStronghold, bridge::Bridge, castle::Castle, citadel::Citadel, + cliff_tower::CliffTower, desert_city_multiplot::DesertCityMultiPlot, + desert_city_temple::DesertCityTemple, dungeon::Dungeon, giant_tree::GiantTree, + gnarling::GnarlingFortification, house::House, savannah_pit::SavannahPit, + sea_chapel::SeaChapel, workshop::Workshop, }; use super::*; @@ -67,6 +69,7 @@ pub enum PlotKind { Road(Path>), Dungeon(Dungeon), Gnarling(GnarlingFortification), + Adlet(AdletStronghold), GiantTree(GiantTree), CliffTower(CliffTower), Citadel(Citadel), diff --git a/world/src/site2/plot/adlet.rs b/world/src/site2/plot/adlet.rs new file mode 100644 index 0000000000..7dd1d5cebc --- /dev/null +++ b/world/src/site2/plot/adlet.rs @@ -0,0 +1,2390 @@ +use super::*; +use crate::{ + assets::AssetHandle, + site2::{gen::PrimitiveTransform, util::Dir}, + util::{attempt, sampler::Sampler, FastNoise, RandomField, NEIGHBORS, NEIGHBORS3}, + IndexRef, Land, +}; +use common::{ + generation::{ChunkSupplement, EntityInfo}, + terrain::{Structure as PrefabStructure, StructuresGroup}, +}; +use lazy_static::lazy_static; +use rand::prelude::*; +use std::{ + f32::consts::{PI, TAU}, + sync::Arc, +}; +use vek::{num_integer::Roots, *}; + +pub struct AdletStronghold { + name: String, + entrance: Vec2, + surface_radius: i32, + // Structure indicates the kind of structure it is, vec2 is relative position of structure + // compared to wall_center, dir tells which way structure should face + outer_structures: Vec<(AdletStructure, Vec2, Dir)>, + tunnel_length: i32, + cavern_center: Vec2, + cavern_alt: f32, + cavern_radius: i32, + // Structure indicates the kind of structure it is, vec2 is relative position of structure + // compared to cavern_center, dir tells which way structure should face + cavern_structures: Vec<(AdletStructure, Vec2, Dir)>, +} + +#[derive(Copy, Clone)] +enum AdletStructure { + Igloo, + TunnelEntrance, + SpeleothemCluster, + Bonfire, + YetiPit, + Tannery, + AnimalPen, + CookFire, + RockHut, + BoneHut, + BossBoneHut, +} + +impl AdletStructure { + fn required_separation(&self, other: &Self) -> i32 { + let radius = |structure: &Self| match structure { + Self::Igloo => 20, + Self::TunnelEntrance => 32, + Self::SpeleothemCluster => 4, + Self::YetiPit => 32, + Self::Tannery => 6, + Self::AnimalPen => 8, + Self::CookFire => 3, + Self::RockHut => 4, + Self::BoneHut => 11, + Self::BossBoneHut => 14, + Self::Bonfire => 10, + }; + + let additional_padding = match (self, other) { + (Self::Igloo, Self::Igloo) => 3, + (Self::BoneHut, Self::BoneHut) => 3, + (Self::Tannery, Self::Tannery) => 5, + (Self::CookFire, Self::CookFire) => 20, + (Self::AnimalPen, Self::AnimalPen) => 8, + // Keep these last + (Self::SpeleothemCluster, Self::SpeleothemCluster) => 0, + (Self::SpeleothemCluster, _) | (_, Self::SpeleothemCluster) => 5, + _ => 0, + }; + + radius(self) + radius(other) + additional_padding + } +} + +impl AdletStronghold { + pub fn generate(wpos: Vec2, land: &Land, rng: &mut impl Rng, _index: IndexRef) -> Self { + let name = NameGen::location(rng).generate_adlet(); + let entrance = wpos; + + let surface_radius: i32 = { + let unit_size = rng.gen_range(10..12); + let num_units = rng.gen_range(4..8); + let variation = rng.gen_range(20..30); + unit_size * num_units + variation + }; + + // Find direction that allows for deep enough site + let angle_samples = (0..64).map(|x| x as f32 / 64.0 * TAU); + // Sample blocks 40-50 away, use angle where these positions are highest + // relative to entrance + let angle = angle_samples + .max_by_key(|theta| { + let entrance_height = land.get_alt_approx(entrance); + let height = + |pos: Vec2| land.get_alt_approx(pos.as_() + entrance) - entrance_height; + let (x, y) = (theta.cos(), theta.sin()); + (40..=50) + .map(|r| { + let rpos = Vec2::new(r as f32 * x, r as f32 * y); + height(rpos) as i32 + }) + .sum::() + }) + .unwrap_or(0.0); + + let cavern_radius: i32 = { + let unit_size = rng.gen_range(10..15); + let num_units = rng.gen_range(5..8); + let variation = rng.gen_range(20..40); + unit_size * num_units + variation + }; + + let tunnel_length = rng.gen_range(35_i32..50); + + let cavern_center = entrance + + (Vec2::new(angle.cos(), angle.sin()) * (tunnel_length as f32 + cavern_radius as f32)) + .as_(); + + let cavern_alt = (land.get_alt_approx(cavern_center) - cavern_radius as f32) + .min(land.get_alt_approx(entrance)); + + let mut outer_structures = Vec::<(AdletStructure, Vec2, Dir)>::new(); + + let entrance_dir = Dir::from_vector(entrance - cavern_center); + outer_structures.push((AdletStructure::TunnelEntrance, Vec2::zero(), entrance_dir)); + + let desired_structures = surface_radius.pow(2) / 100; + for _ in 0..desired_structures { + if let Some((rpos, kind)) = attempt(50, || { + let structure_kind = AdletStructure::Igloo; + /* + // Choose structure kind + let structure_kind = match rng.gen_range(0..10) { + // TODO: Add more variants + _ => AdletStructure::Igloo, + }; + */ + // Choose relative position + let structure_center = { + let theta = rng.gen::() * TAU; + // 0.8 to keep structures not directly against wall + let radius = surface_radius as f32 * rng.gen::().sqrt() * 0.8; + let x = radius * theta.sin(); + let y = radius * theta.cos(); + Vec2::new(x, y).as_() + }; + + let tunnel_line = LineSegment2 { + start: entrance, + end: entrance - entrance_dir.to_vec2() * 100, + }; + + // Check that structure not in the water or too close to another structure + if land + .get_chunk_wpos(structure_center.as_() + entrance) + .map_or(false, |c| c.is_underwater()) + || outer_structures.iter().any(|(kind, rpos, _dir)| { + structure_center.distance_squared(*rpos) + < structure_kind.required_separation(kind).pow(2) + }) + || tunnel_line + .as_::() + .distance_to_point((structure_center + entrance).as_::()) + < 25.0 + { + None + } else { + Some((structure_center, structure_kind)) + } + }) { + let dir_to_wall = Dir::from_vector(rpos); + let door_rng: u32 = rng.gen_range(0..9); + let door_dir = match door_rng { + 0..=3 => dir_to_wall, + 4..=5 => dir_to_wall.rotated_cw(), + 6..=7 => dir_to_wall.rotated_ccw(), + // Should only be 8 + _ => dir_to_wall.opposite(), + }; + outer_structures.push((kind, rpos, door_dir)); + } + } + + let mut cavern_structures = Vec::<(AdletStructure, Vec2, Dir)>::new(); + + fn valid_cavern_struct_pos( + structures: &[(AdletStructure, Vec2, Dir)], + structure: AdletStructure, + rpos: Vec2, + ) -> bool { + structures.iter().all(|(kind, rpos2, _dir)| { + rpos.distance_squared(*rpos2) > structure.required_separation(kind).pow(2) + }) + } + + // Add speleothem clusters (stalagmites/stalactites) + let desired_speleothem_clusters = cavern_radius.pow(2) / 2500; + for _ in 0..desired_speleothem_clusters { + if let Some(mut rpos) = attempt(25, || { + let rpos = { + let theta = rng.gen_range(0.0..TAU); + // sqrt biases radius away from center, leading to even distribution in circle + let radius = rng.gen::().sqrt() * cavern_radius as f32; + Vec2::new(theta.cos() * radius, theta.sin() * radius).as_() + }; + valid_cavern_struct_pos(&cavern_structures, AdletStructure::SpeleothemCluster, rpos) + .then_some(rpos) + }) { + // Dir doesn't matter since these are directionless + cavern_structures.push((AdletStructure::SpeleothemCluster, rpos, Dir::X)); + let desired_adjacent_clusters = rng.gen_range(1..5); + for _ in 0..desired_adjacent_clusters { + // Choose a relative position adjacent to initial speleothem cluster + let adj_rpos = { + let theta = rng.gen_range(0.0..TAU); + let radius = rng.gen_range(1.0..5.0); + let rrpos = Vec2::new(theta.cos() * radius, theta.sin() * radius).as_(); + rpos + rrpos + }; + if valid_cavern_struct_pos( + &cavern_structures, + AdletStructure::SpeleothemCluster, + adj_rpos, + ) { + cavern_structures.push(( + AdletStructure::SpeleothemCluster, + adj_rpos, + Dir::X, + )); + // Set new rpos to next cluster is adjacent to most recently placed + rpos = adj_rpos; + } else { + // If any cluster ever fails to place, break loop and stop creating cluster + // chain + break; + } + } + } + } + + // Attempt to place central boss bone hut + if let Some(rpos) = attempt(50, || { + let rpos = { + let theta = rng.gen_range(0.0..TAU); + let radius = rng.gen::() * cavern_radius as f32 * 0.5; + Vec2::new(theta.cos() * radius, theta.sin() * radius).as_() + }; + valid_cavern_struct_pos(&cavern_structures, AdletStructure::BossBoneHut, rpos) + .then_some(rpos) + }) + .or_else(|| { + attempt(100, || { + let rpos = { + let theta = rng.gen_range(0.0..TAU); + // If selecting a spot near the center failed, find a spot anywhere in the + // cavern + let radius = rng.gen::().sqrt() * cavern_radius as f32; + Vec2::new(theta.cos() * radius, theta.sin() * radius).as_() + }; + valid_cavern_struct_pos(&cavern_structures, AdletStructure::BossBoneHut, rpos) + .then_some(rpos) + }) + }) { + // Direction doesn't matter for boss bonehut + cavern_structures.push((AdletStructure::BossBoneHut, rpos, Dir::X)); + } + + // Attempt to place yetipit near the cavern edge + if let Some(rpos) = attempt(50, || { + let rpos = { + let theta = rng.gen_range(0.0..TAU); + let radius = cavern_radius as f32; + Vec2::new(theta.cos() * radius, theta.sin() * radius).as_() + }; + valid_cavern_struct_pos(&cavern_structures, AdletStructure::YetiPit, rpos) + .then_some(rpos) + }) + .or_else(|| { + attempt(100, || { + let rpos = { + let theta = rng.gen_range(0.0..TAU); + // If selecting a spot near the cavern edge failed, find a spot anywhere in the + // cavern + let radius = rng.gen::().sqrt() * cavern_radius as f32; + Vec2::new(theta.cos() * radius, theta.sin() * radius).as_() + }; + valid_cavern_struct_pos(&cavern_structures, AdletStructure::YetiPit, rpos) + .then_some(rpos) + }) + }) { + // Direction doesn't matter for yetipit + cavern_structures.push((AdletStructure::YetiPit, rpos, Dir::X)); + } + + // Attempt to place big bonfire + if let Some(rpos) = attempt(50, || { + let rpos = { + let theta = rng.gen_range(0.0..TAU); + let radius = rng.gen::().sqrt() * cavern_radius as f32 * 0.9; + Vec2::new(theta.cos() * radius, theta.sin() * radius).as_() + }; + valid_cavern_struct_pos(&cavern_structures, AdletStructure::Bonfire, rpos) + .then_some(rpos) + }) { + // Direction doesn't matter for central bonfire + cavern_structures.push((AdletStructure::Bonfire, rpos, Dir::X)); + } + + // Attempt to place some rock huts around the outer edge + let desired_rock_huts = cavern_radius / 5; + for _ in 0..desired_rock_huts { + if let Some(rpos) = attempt(25, || { + let rpos = { + let theta = rng.gen_range(0.0..TAU); + let radius = cavern_radius as f32 - 1.0; + Vec2::new(theta.cos() * radius, theta.sin() * radius).as_() + }; + valid_cavern_struct_pos(&cavern_structures, AdletStructure::RockHut, rpos) + .then_some(rpos) + }) { + // Rock huts need no direction + cavern_structures.push((AdletStructure::RockHut, rpos, Dir::X)); + } + } + + // Attempt to place some general structures + let desired_structures = cavern_radius.pow(2) / 200; + for _ in 0..desired_structures { + if let Some((structure, rpos)) = attempt(45, || { + let rpos = { + let theta = rng.gen_range(0.0..TAU); + // sqrt biases radius away from center, leading to even distribution in circle + let radius = rng.gen::().sqrt() * cavern_radius as f32 * 0.9; + Vec2::new(theta.cos() * radius, theta.sin() * radius).as_() + }; + let structure = match rng.gen_range(0..7) { + 0..=2 => AdletStructure::BoneHut, + 3..=4 => AdletStructure::CookFire, + 5 => AdletStructure::Tannery, + _ => AdletStructure::AnimalPen, + }; + + valid_cavern_struct_pos(&cavern_structures, structure, rpos) + .then_some((structure, rpos)) + }) { + // Direction facing the central bonfire + let dir = Dir::from_vector(rpos).opposite(); + cavern_structures.push((structure, rpos, dir)); + } + } + + Self { + name, + entrance, + surface_radius, + outer_structures, + tunnel_length, + cavern_center, + cavern_radius, + cavern_alt, + cavern_structures, + } + } + + pub fn name(&self) -> &str { &self.name } + + // pub fn origin(&self) -> Vec2 { self.cavern_center } + + pub fn radius(&self) -> i32 { self.cavern_radius + self.tunnel_length + 5 } + + pub fn plot_tiles(&self, origin: Vec2) -> (Aabr, Aabr) { + // Cavern + let size = self.cavern_radius / tile::TILE_SIZE as i32; + let offset = (self.cavern_center - origin) / tile::TILE_SIZE as i32; + let cavern_aabr = Aabr { + min: Vec2::broadcast(-size) + offset, + max: Vec2::broadcast(size) + offset, + }; + // Surface + let size = (self.surface_radius * 5 / 4) / tile::TILE_SIZE as i32; + let offset = (self.entrance - origin) / tile::TILE_SIZE as i32; + let surface_aabr = Aabr { + min: Vec2::broadcast(-size) + offset, + max: Vec2::broadcast(size) + offset, + }; + (cavern_aabr, surface_aabr) + } + + pub fn spawn_rules(&self, wpos: Vec2) -> SpawnRules { + SpawnRules { + waypoints: false, + trees: wpos.distance_squared(self.entrance) > (self.surface_radius * 5 / 4).pow(2), + ..SpawnRules::default() + } + } + + // TODO: Find a better way of spawning entities in site2 + pub fn apply_supplement( + &self, + // NOTE: Used only for dynamic elements like chests and entities! + _dynamic_rng: &mut impl Rng, + wpos2d: Vec2, + _supplement: &mut ChunkSupplement, + ) { + let rpos = wpos2d - self.cavern_center; + let _area = Aabr { + min: rpos, + max: rpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32), + }; + } +} + +impl Structure for AdletStronghold { + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"render_adletstronghold\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "render_adletstronghold")] + fn render_inner(&self, _site: &Site, land: &Land, painter: &Painter) { + let snow_ice_fill = Fill::Sampling(Arc::new(|wpos| { + Some(match (RandomField::new(0).get(wpos)) % 250 { + 0..=2 => Block::new(BlockKind::Ice, Rgb::new(120, 160, 255)), + 3..=10 => Block::new(BlockKind::ArtSnow, Rgb::new(138, 147, 217)), + 11..=20 => Block::new(BlockKind::ArtSnow, Rgb::new(213, 213, 242)), + 21..=35 => Block::new(BlockKind::ArtSnow, Rgb::new(231, 230, 247)), + 36..=62 => Block::new(BlockKind::ArtSnow, Rgb::new(180, 181, 227)), + _ => Block::new(BlockKind::ArtSnow, Rgb::new(209, 212, 238)), + }) + })); + let snow_ice_air_fill = Fill::Sampling(Arc::new(|wpos| { + Some(match (RandomField::new(0).get(wpos)) % 250 { + 0..=2 => Block::new(BlockKind::Ice, Rgb::new(120, 160, 255)), + 3..=5 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)), + 6..=10 => Block::new(BlockKind::ArtSnow, Rgb::new(138, 147, 217)), + 11..=20 => Block::new(BlockKind::ArtSnow, Rgb::new(213, 213, 242)), + 21..=35 => Block::new(BlockKind::ArtSnow, Rgb::new(231, 230, 247)), + 36..=62 => Block::new(BlockKind::ArtSnow, Rgb::new(180, 181, 227)), + _ => Block::new(BlockKind::ArtSnow, Rgb::new(209, 212, 238)), + }) + })); + let bone_fill = Fill::Brick(BlockKind::Misc, Rgb::new(200, 160, 140), 1); + let ice_fill = Fill::Block(Block::new(BlockKind::Ice, Rgb::new(120, 160, 255))); + let dirt_fill = Fill::Brick(BlockKind::Earth, Rgb::new(55, 25, 8), 24); + let grass_fill = Fill::Sampling(Arc::new(|wpos| { + Some(match (RandomField::new(0).get(wpos)) % 5 { + 1 => Block::air(SpriteKind::ShortGrass), + _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)), + }) + })); + let rock_fill = Fill::Sampling(Arc::new(|wpos| { + Some(match (RandomField::new(0).get(wpos)) % 4 { + 0 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)), + _ => Block::new(BlockKind::Rock, Rgb::new(90, 110, 150)), + }) + })); + let bone_shrub = Fill::Sampling(Arc::new(|wpos| { + Some(match (RandomField::new(0).get(wpos)) % 40 { + 0 => Block::new(BlockKind::Misc, Rgb::new(200, 160, 140)), + _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)), + }) + })); + let yetipit_sprites = Fill::Sampling(Arc::new(|wpos| { + Some(match (RandomField::new(0).get(wpos)) % 60 { + 0..=2 => Block::air(SpriteKind::Bones), + 4..=5 => Block::air(SpriteKind::GlowIceCrystal), + 6..=8 => Block::air(SpriteKind::IceCrystal), + _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)), + }) + })); + let yetipit_sprites_deep = Fill::Sampling(Arc::new(|wpos| { + Some(match (RandomField::new(0).get(wpos)) % 275 { + 0..=8 => Block::air(SpriteKind::Bones), + 9..=19 => Block::air(SpriteKind::GlowIceCrystal), + 20..=28 => Block::air(SpriteKind::IceCrystal), + 29..=30 => Block::air(SpriteKind::DungeonChest3), + _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)), + }) + })); + let yeti_bones_fill = Fill::Sampling(Arc::new(|wpos| { + Some(match (RandomField::new(0).get(wpos)) % 20 { + 0 => Block::air(SpriteKind::Bones), + _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)), + }) + })); + let mut rng = thread_rng(); + + // Tunnel + let dist: f32 = self.cavern_center.as_().distance(self.entrance.as_()); + let dir = Dir::from_vector(self.entrance - self.cavern_center); + let tunnel_start: Vec3 = match dir { + Dir::X => Vec2::new(self.entrance.x + 7, self.entrance.y), + Dir::Y => Vec2::new(self.entrance.x, self.entrance.y + 7), + Dir::NegX => Vec2::new(self.entrance.x - 7, self.entrance.y), + Dir::NegY => Vec2::new(self.entrance.x, self.entrance.y - 7), + } + .as_() + .with_z(self.cavern_alt - 1.0); + // Adds cavern radius to ensure that tunnel fully bores into cavern + let raw_tunnel_end = + ((self.cavern_center.as_() - self.entrance.as_()) * self.tunnel_length as f32 / dist) + .with_z(self.cavern_alt - 1.0) + + self.entrance.as_(); + + let offset = 15.0; + let tunnel_end = match dir { + Dir::X => Vec3::new(raw_tunnel_end.x - offset, tunnel_start.y, raw_tunnel_end.z), + Dir::Y => Vec3::new(tunnel_start.x, raw_tunnel_end.y - offset, raw_tunnel_end.z), + Dir::NegX => Vec3::new(raw_tunnel_end.x + offset, tunnel_start.y, raw_tunnel_end.z), + Dir::NegY => Vec3::new(tunnel_start.x, raw_tunnel_end.y + offset, raw_tunnel_end.z), + }; + // Platform + painter + .line( + tunnel_start + Vec3::new(0.0, 0.0, 5.0), + { + let end = tunnel_start + (dir.to_vec2().as_().with_z(1.0) * 20.0); + end.with_z(end.z + 5.0) + }, + 10.0, + ) + .clear(); + painter + .sphere(Aabb { + min: (self.entrance - 15).with_z(self.cavern_alt as i32 - 15), + max: (self.entrance + 15).with_z(self.cavern_alt as i32 + 15), + }) + .fill(snow_ice_fill.clone()); + + painter + .cylinder(Aabb { + min: (self.entrance - 15).with_z(self.cavern_alt as i32), + max: (self.entrance + 15).with_z(self.cavern_alt as i32 + 20), + }) + .clear(); + painter + .cylinder(Aabb { + min: (self.entrance - 14).with_z(self.cavern_alt as i32 - 1), + max: (self.entrance + 14).with_z(self.cavern_alt as i32), + }) + .clear(); + painter + .cylinder(Aabb { + min: (self.entrance - 12).with_z(self.cavern_alt as i32 - 40), + max: (self.entrance + 12).with_z(self.cavern_alt as i32 - 10), + }) + .fill(snow_ice_fill.clone()); + + let valid_entrance = painter.segment_prism(tunnel_start, tunnel_end, 20.0, 30.0); + painter + .segment_prism(tunnel_start, tunnel_end, 10.0, 10.0) + .clear(); + painter + .line( + tunnel_start + Vec3::new(0.0, 0.0, 10.0), + tunnel_end + Vec3::new(0.0, 0.0, 10.0), + 10.0, + ) + .clear(); + painter + .line( + tunnel_start + + match dir { + Dir::X => Vec3::new(0.0, 4.0, 7.0), + Dir::Y => Vec3::new(4.0, 0.0, 7.0), + Dir::NegX => Vec3::new(0.0, 4.0, 7.0), + Dir::NegY => Vec3::new(4.0, 0.0, 7.0), + }, + tunnel_end + + match dir { + Dir::X => Vec3::new(0.0, 4.0, 7.0), + Dir::Y => Vec3::new(4.0, 0.0, 7.0), + Dir::NegX => Vec3::new(0.0, 4.0, 7.0), + Dir::NegY => Vec3::new(4.0, 0.0, 7.0), + }, + 8.0, + ) + .intersect(valid_entrance) + .clear(); + painter + .line( + tunnel_start + + match dir { + Dir::X => Vec3::new(0.0, -4.0, 7.0), + Dir::Y => Vec3::new(-4.0, 0.0, 7.0), + Dir::NegX => Vec3::new(0.0, -4.0, 7.0), + Dir::NegY => Vec3::new(-4.0, 0.0, 7.0), + }, + tunnel_end + + match dir { + Dir::X => Vec3::new(0.0, -4.0, 7.0), + Dir::Y => Vec3::new(-4.0, 0.0, 7.0), + Dir::NegX => Vec3::new(0.0, -4.0, 7.0), + Dir::NegY => Vec3::new(-4.0, 0.0, 7.0), + }, + 8.0, + ) + .intersect(valid_entrance) + .clear(); + // Ensure there is a path to the cave if the above is weird (e.g. when it is at + // or near a 45 degrees angle) + painter + .line( + tunnel_end.with_z(tunnel_end.z + 4.0), + raw_tunnel_end.with_z(raw_tunnel_end.z + 4.0), + 4.0, + ) + .clear(); + + // Cavern + let cavern = painter + .sphere_with_radius( + self.cavern_center.with_z(self.cavern_alt as i32), + self.cavern_radius as f32, + ) + .intersect(painter.aabb(Aabb { + min: (self.cavern_center - self.cavern_radius).with_z(self.cavern_alt as i32), + max: self.cavern_center.with_z(self.cavern_alt as i32) + self.cavern_radius, + })) + .sample_with_column({ + let origin = self.cavern_center.with_z(self.cavern_alt as i32); + let radius_sqr = self.cavern_radius.pow(2); + move |pos, col| { + let alt = col.basement - col.cliff_offset; + let sphere_alt = ((radius_sqr - origin.xy().distance_squared(pos.xy())) as f32) + .sqrt() + + origin.z as f32; + // Some sort of smooth min + let alt = if alt < sphere_alt { + alt + } else if sphere_alt - alt < 10.0 { + f32::lerp(sphere_alt, alt, 1.0 / (alt - sphere_alt).max(1.0)) + } else { + sphere_alt + }; + + let noise = FastNoise::new(333); + let alt_offset = noise.get(pos.with_z(0).as_() / 5.0).powi(2) * 15.0; + + let alt = alt - alt_offset; + + pos.z < alt as i32 + } + }); + let alt = self.cavern_alt; + cavern.clear(); + + // snow cylinder for cavern ground and to carve out yetipit + painter + .cylinder(Aabb { + min: (self.cavern_center - self.cavern_radius).with_z(alt as i32 - 200), + max: (self.cavern_center + self.cavern_radius).with_z(alt as i32), + }) + .fill(snow_ice_fill.clone()); + + for (structure, wpos, alt, dir) in self + .outer_structures + .iter() + .map(|(structure, rpos, dir)| { + let wpos = rpos + self.entrance; + (structure, wpos, land.get_alt_approx(wpos), dir) + }) + .chain(self.cavern_structures.iter().map(|(structure, rpos, dir)| { + (structure, rpos + self.cavern_center, self.cavern_alt, dir) + })) + { + match structure { + AdletStructure::TunnelEntrance => { + let rib_width_curve = |i: f32| 0.5 * (0.4 * i + 1.0).log2() + 5.5; + let spine_curve_amplitude = 0.0; + let spine_curve_wavelength = 1.0; + let spine_curve_function = |i: f32, amplitude: f32, wavelength: f32| { + amplitude * (2.0 * PI * (1.0 / wavelength) * i).sin() + }; + let rib_cage_config = RibCageGenerator { + dir: *dir, + spine_radius: 2.5, + length: 40, + spine_curve_function, + spine_curve_amplitude, + spine_curve_wavelength, + spine_height: self.cavern_alt + 16.0, + spine_start_z_offset: 2.0, + spine_ctrl0_z_offset: 3.0, + spine_ctrl1_z_offset: 5.0, + spine_end_z_offset: 1.0, + spine_ctrl0_length_fraction: 0.3, + spine_ctrl1_length_fraction: 0.7, + rib_base_alt: self.cavern_alt - 1.0, + rib_spacing: 7, + rib_radius: 1.7, + rib_run: 5.0, + rib_ctrl0_run_fraction: 0.3, + rib_ctrl1_run_fraction: 0.5, + rib_ctrl0_width_offset: 5.0, + rib_ctrl1_width_offset: 3.0, + rib_width_curve, + rib_ctrl0_height_fraction: 0.8, + rib_ctrl1_height_fraction: 0.4, + vertebra_radius: 4.0, + vertebra_width: 1.0, + vertebra_z_offset: 0.3, + }; + let rib_cage = + rib_cage_config.bones(wpos + 40 * dir.opposite().to_vec2(), painter); + for bone in rib_cage { + bone.fill(bone_fill.clone()); + } + }, + AdletStructure::Igloo => { + let igloo_pos = wpos; + let igloo_size = 8.0; + let height_handle = 0; + let bones_size = igloo_size as i32; + painter + .cylinder_with_radius( + (igloo_pos).with_z(alt as i32 - 5 + height_handle), + 11.0, + 45.0, + ) + .clear(); + // Foundation + let foundation = match RandomField::new(0).get((wpos).with_z(alt as i32)) % 5 { + 0 => painter + .sphere(Aabb { + min: (igloo_pos - 15).with_z(alt as i32 - 45 + height_handle), + max: (igloo_pos + 15).with_z(alt as i32 - 15 + height_handle), + }) + .union(painter.sphere(Aabb { + min: (igloo_pos - 10).with_z(alt as i32 - 20 + height_handle), + max: (igloo_pos + 10).with_z(alt as i32 - 5 + height_handle), + })), + _ => painter + .sphere(Aabb { + min: (igloo_pos - 15).with_z(alt as i32 - 60 + height_handle), + max: (igloo_pos + 15).with_z(alt as i32 - 30 + height_handle), + }) + .union(painter.cone(Aabb { + min: (igloo_pos - 15).with_z(alt as i32 - 45 + height_handle), + max: (igloo_pos + 15).with_z(alt as i32 + 8 + height_handle), + })), + }; + foundation.fill(snow_ice_fill.clone()); + foundation.intersect(cavern).clear(); + // Platform + painter + .sphere(Aabb { + min: (igloo_pos - 13).with_z(alt as i32 - 11 + height_handle), + max: (igloo_pos + 13).with_z(alt as i32 + 11 + height_handle), + }) + .fill(snow_ice_air_fill.clone()); + + painter + .cylinder(Aabb { + min: (igloo_pos - 13).with_z(alt as i32 - 4 + height_handle), + max: (igloo_pos + 13).with_z(alt as i32 + 16 + height_handle), + }) + .clear(); + // 2 igloo variants + match RandomField::new(0).get((igloo_pos).with_z(alt as i32)) % 4 { + 0 => { + // clear room + painter + .sphere_with_radius( + igloo_pos.with_z(alt as i32 - 1 + height_handle), + (igloo_size as i32 - 2) as f32, + ) + .clear(); + let pos_var = RandomField::new(0).get(igloo_pos.with_z(alt as i32)) % 5; + let radius = 8 + pos_var; + let bones = 8.0 + pos_var as f32; + let phi = TAU / bones; + for n in 1..=bones as i32 { + let bone_hide_fill = Fill::Sampling(Arc::new(|pos| { + Some(match (RandomField::new(0).get(pos)) % 35 { + 0 => Block::new(BlockKind::Wood, Rgb::new(73, 29, 0)), + 1 => Block::new(BlockKind::Wood, Rgb::new(78, 67, 43)), + 2 => Block::new(BlockKind::Wood, Rgb::new(83, 74, 41)), + 3 => Block::new(BlockKind::Wood, Rgb::new(14, 36, 34)), + _ => Block::new(BlockKind::Misc, Rgb::new(200, 160, 140)), + }) + })); + + let pos = Vec2::new( + igloo_pos.x + (radius as f32 * ((n as f32 * phi).cos())) as i32, + igloo_pos.y + (radius as f32 * ((n as f32 * phi).sin())) as i32, + ); + let bone_var = RandomField::new(0).get(pos.with_z(alt as i32)) % 5; + + match RandomField::new(0).get((igloo_pos - 1).with_z(alt as i32)) + % 3 + { + 0 => { + painter + .line( + pos.with_z(alt as i32 - 6 + height_handle), + igloo_pos.with_z(alt as i32 + 8 + height_handle), + 1.0, + ) + .fill(bone_hide_fill.clone()); + }, + _ => { + painter + .cubic_bezier( + pos.with_z(alt as i32 - 6 + height_handle), + (pos - ((igloo_pos - pos) / 2)).with_z( + alt as i32 + + 12 + + bone_var as i32 + + height_handle, + ), + (pos + ((igloo_pos - pos) / 2)) + .with_z(alt as i32 + 9 + height_handle), + igloo_pos.with_z(alt as i32 + 5 + height_handle), + 1.0, + ) + .fill(bone_hide_fill.clone()); + }, + }; + } + let outside_wolfs = 2 + + (RandomField::new(0) + .get((igloo_pos - 1).with_z(alt as i32 - 5 + height_handle)) + % 3) as i32; + for _ in 0..outside_wolfs { + let igloo_mob_spawn = + (igloo_pos - 1).with_z(alt as i32 - 5 + height_handle); + painter.spawn(wolf(igloo_mob_spawn.as_(), &mut rng)) + } + }, + _ => { + // top decor bone with some hide + painter + .aabb(Aabb { + min: igloo_pos + .with_z((alt as i32) + bones_size + height_handle), + max: (igloo_pos + 1) + .with_z((alt as i32) + bones_size + 3 + height_handle), + }) + .fill(bone_fill.clone()); + painter + .aabb(Aabb { + min: Vec2::new(igloo_pos.x, igloo_pos.y - 1) + .with_z((alt as i32) + bones_size + 3 + height_handle), + max: Vec2::new(igloo_pos.x + 1, igloo_pos.y + 2) + .with_z((alt as i32) + bones_size + 4 + height_handle), + }) + .fill(bone_fill.clone()); + painter + .aabb(Aabb { + min: igloo_pos + .with_z((alt as i32) + bones_size + 3 + height_handle), + max: (igloo_pos + 1) + .with_z((alt as i32) + bones_size + 4 + height_handle), + }) + .clear(); + let top_color = Fill::Sampling(Arc::new(|igloo_pos| { + Some(match (RandomField::new(0).get(igloo_pos)) % 10 { + 0 => Block::new(BlockKind::Wood, Rgb::new(73, 29, 0)), + 1 => Block::new(BlockKind::Wood, Rgb::new(78, 67, 43)), + 2 => Block::new(BlockKind::Wood, Rgb::new(83, 74, 41)), + 3 => Block::new(BlockKind::Wood, Rgb::new(14, 36, 34)), + _ => Block::new(BlockKind::Rock, Rgb::new(200, 160, 140)), + }) + })); + painter + .aabb(Aabb { + min: (igloo_pos - 1) + .with_z((alt as i32) + bones_size - 1 + height_handle), + max: (igloo_pos + 2) + .with_z((alt as i32) + bones_size + 1 + height_handle), + }) + .fill(top_color.clone()); + // igloo snow + painter + .sphere_with_radius(igloo_pos.with_z(alt as i32 - 1), igloo_size) + .fill(snow_ice_fill.clone()); + // 4 hide pieces + for dir in CARDINALS { + let hide_size = 5 + + (RandomField::new(0) + .get((igloo_pos + dir).with_z(alt as i32)) + % 4); + let hide_color = match RandomField::new(0) + .get((igloo_pos + dir).with_z(alt as i32)) + % 4 + { + 0 => Fill::Block(Block::new( + BlockKind::Wood, + Rgb::new(73, 29, 0), + )), + 1 => Fill::Block(Block::new( + BlockKind::Wood, + Rgb::new(78, 67, 43), + )), + 2 => Fill::Block(Block::new( + BlockKind::Wood, + Rgb::new(83, 74, 41), + )), + _ => Fill::Block(Block::new( + BlockKind::Wood, + Rgb::new(14, 36, 34), + )), + }; + painter + .sphere_with_radius( + (igloo_pos + (2 * dir)) + .with_z((alt as i32) + 1 + height_handle), + hide_size as f32, + ) + .fill(hide_color.clone()); + } + // clear room + painter + .sphere_with_radius( + igloo_pos.with_z(alt as i32 - 1 + height_handle), + (igloo_size as i32 - 2) as f32, + ) + .clear(); + // clear entries + painter + .aabb(Aabb { + min: Vec2::new( + igloo_pos.x - 1, + igloo_pos.y - igloo_size as i32 - 2, + ) + .with_z(alt as i32 - 4 + height_handle), + max: Vec2::new( + igloo_pos.x + 1, + igloo_pos.y + igloo_size as i32 + 2, + ) + .with_z(alt as i32 - 2 + height_handle), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new( + igloo_pos.x - igloo_size as i32 - 2, + igloo_pos.y - 1, + ) + .with_z(alt as i32 - 4 + height_handle), + max: Vec2::new( + igloo_pos.x + igloo_size as i32 + 2, + igloo_pos.y + 1, + ) + .with_z(alt as i32 - 2 + height_handle), + }) + .clear(); + // bones + for h in 0..(bones_size + 4) { + painter + .line( + (igloo_pos - bones_size) + .with_z((alt as i32) - 5 + h + height_handle), + (igloo_pos + bones_size) + .with_z((alt as i32) - 5 + h + height_handle), + 0.5, + ) + .intersect(painter.sphere_with_radius( + igloo_pos.with_z((alt as i32) - 2 + height_handle), + 9.0, + )) + .fill(bone_fill.clone()); + + painter + .line( + Vec2::new( + igloo_pos.x - bones_size, + igloo_pos.y + bones_size, + ) + .with_z((alt as i32) - 4 + h + height_handle), + Vec2::new( + igloo_pos.x + bones_size, + igloo_pos.y - bones_size, + ) + .with_z((alt as i32) - 4 + h + height_handle), + 0.5, + ) + .intersect(painter.sphere_with_radius( + igloo_pos.with_z((alt as i32) - 2 + height_handle), + 9.0, + )) + .fill(bone_fill.clone()); + } + painter + .sphere_with_radius( + igloo_pos.with_z((alt as i32) - 2 + height_handle), + 5.0, + ) + .clear(); + + // WallSconce + painter.rotated_sprite( + Vec2::new( + igloo_pos.x - bones_size + 4, + igloo_pos.y + bones_size - 5, + ) + .with_z((alt as i32) - 1 + height_handle), + SpriteKind::WallSconce, + 0_u8, + ); + let igloo_mobs = 1 + + (RandomField::new(0) + .get((igloo_pos - 1).with_z(alt as i32 - 5 + height_handle)) + % 2) as i32; + + for _ in 0..igloo_mobs { + let igloo_mob_spawn = + (igloo_pos - 1).with_z(alt as i32 - 5 + height_handle); + painter.spawn(random_adlet(igloo_mob_spawn.as_(), &mut rng)); + } + }, + }; + // igloo floor + painter + .cylinder_with_radius( + (igloo_pos).with_z(alt as i32 - 7 + height_handle), + (igloo_size as i32 - 4) as f32, + 2.0, + ) + .fill(snow_ice_fill.clone()); + + // FireBowl + painter.sprite( + igloo_pos.with_z(alt as i32 - 5 + height_handle), + SpriteKind::FireBowlGround, + ); + }, + AdletStructure::SpeleothemCluster => { + let layer_color = Fill::Sampling(Arc::new(|wpos| { + Some( + match (RandomField::new(0).get(Vec3::new(wpos.z, 0, 0))) % 6 { + 0 => Block::new(BlockKind::Rock, Rgb::new(100, 128, 179)), + 1 => Block::new(BlockKind::Rock, Rgb::new(95, 127, 178)), + 2 => Block::new(BlockKind::Rock, Rgb::new(101, 121, 169)), + 3 => Block::new(BlockKind::Rock, Rgb::new(61, 109, 145)), + 4 => Block::new(BlockKind::Rock, Rgb::new(74, 128, 168)), + _ => Block::new(BlockKind::Rock, Rgb::new(69, 123, 162)), + }, + ) + })); + for dir in NEIGHBORS { + let cone_radius = 3 + + (RandomField::new(0).get((wpos + dir).with_z(alt as i32)) % 3) as i32; + let cone_offset = 3 + + (RandomField::new(0).get((wpos + 1 + dir).with_z(alt as i32)) % 4) + as i32; + let cone_height = 15 + + (RandomField::new(0).get((wpos + 2 + dir).with_z(alt as i32)) % 50) + as i32; + // cones + painter + .cone_with_radius( + (wpos + (dir * cone_offset)).with_z(alt as i32 - (cone_height / 8)), + cone_radius as f32, + cone_height as f32, + ) + .fill(layer_color.clone()); + // small cone tops + let top_pos = (RandomField::new(0).get((wpos + 3 + dir).with_z(alt as i32)) + % 2) as i32; + painter + .aabb(Aabb { + min: (wpos + (dir * cone_offset) - top_pos).with_z(alt as i32), + max: (wpos + (dir * cone_offset) + 1 - top_pos) + .with_z((alt as i32) + cone_height - (cone_height / 6)), + }) + .fill(layer_color.clone()); + } + }, + AdletStructure::Bonfire => { + let bonfire_pos = wpos; + let fire_fill = Fill::Sampling(Arc::new(|bonfire_pos| { + Some(match (RandomField::new(0).get(bonfire_pos)) % 200 { + 0 => Block::air(SpriteKind::Ember), + _ => Block::air(SpriteKind::FireBlock), + }) + })); + let fire_pos = bonfire_pos.with_z(alt as i32 + 2); + lazy_static! { + pub static ref FIRE: AssetHandle = + PrefabStructure::load_group("site_structures.adlet.bonfire"); + } + let fire_rng = RandomField::new(0).get(fire_pos) % 10; + let fire = FIRE.read(); + let fire = fire[fire_rng as usize % fire.len()].clone(); + painter + .prim(Primitive::Prefab(Box::new(fire.clone()))) + .translate(fire_pos) + .fill(Fill::Prefab(Box::new(fire), fire_pos, fire_rng)); + painter + .sphere_with_radius((bonfire_pos).with_z(alt as i32 + 5), 4.0) + .fill(fire_fill.clone()); + painter + .cylinder_with_radius((bonfire_pos).with_z(alt as i32 + 2), 6.5, 1.0) + .fill(fire_fill); + }, + AdletStructure::YetiPit => { + let yetipit_center = self.cavern_center; + let yetipit_entrance_pos = wpos; + let storeys = (3 + RandomField::new(0).get((yetipit_center).with_z(alt as i32)) + % 2) as i32; + for s in 0..storeys { + let down = 10_i32; + let level = (alt as i32) - 50 - (s * (3 * down)); + let room_size = (25 + + RandomField::new(0).get((yetipit_center * s).with_z(level)) % 5) + as i32; + let sprites_fill = match s { + 0..=1 => yetipit_sprites.clone(), + _ => yetipit_sprites_deep.clone(), + }; + if s == (storeys - 1) { + // yeti room + painter + .cylinder_with_radius( + yetipit_center.with_z(level - (3 * down) - 5), + room_size as f32, + ((room_size / 3) + 5) as f32, + ) + .clear(); + painter + .cylinder_with_radius( + yetipit_center.with_z(level - (3 * down) - 6), + room_size as f32, + 1.0, + ) + .fill(snow_ice_fill.clone()); + // sprites: icecrystals, bones + for r in 0..4 { + painter + .cylinder_with_radius( + yetipit_center.with_z(level - (3 * down) - 2 - r), + (room_size + 2 - r) as f32, + 1.0, + ) + .fill(snow_ice_fill.clone()); + painter + .cylinder_with_radius( + yetipit_center.with_z(level - (3 * down) - 1 - r), + (room_size - r) as f32, + 1.0, + ) + .fill(sprites_fill.clone()); + painter + .cylinder_with_radius( + yetipit_center.with_z(level - (3 * down) - 2 - r), + (room_size - 1 - r) as f32, + 2.0, + ) + .clear(); + } + painter + .cylinder_with_radius( + yetipit_center.with_z(level - (3 * down) - 5), + (room_size - 4) as f32, + 1.0, + ) + .fill(yeti_bones_fill.clone()); + painter + .cone_with_radius( + yetipit_center.with_z(level - (3 * down) + (room_size / 3) - 2), + room_size as f32, + (room_size / 3) as f32, + ) + .clear(); + // snow covered speleothem cluster + for dir in NEIGHBORS { + let cluster_pos = yetipit_center + dir * room_size - 3; + for dir in NEIGHBORS3 { + let cone_radius = 3 + + (RandomField::new(0) + .get((cluster_pos + dir).with_z(alt as i32)) + % 3) as i32; + let cone_offset = 3 + + (RandomField::new(0) + .get((cluster_pos + 1 + dir).with_z(alt as i32)) + % 4) as i32; + let cone_height = 15 + + (RandomField::new(0) + .get((cluster_pos + 2 + dir).with_z(alt as i32)) + % 10) as i32; + // cones + painter + .cone_with_radius( + (cluster_pos + (dir * cone_offset)) + .with_z(level - (3 * down) - 4 - (cone_height / 8)), + cone_radius as f32, + cone_height as f32, + ) + .fill(snow_ice_fill.clone()); + // small cone tops + let top_pos = (RandomField::new(0) + .get((cluster_pos + 3 + dir).with_z(level)) + % 2) + as i32; + painter + .aabb(Aabb { + min: (cluster_pos + (dir * cone_offset) - top_pos) + .with_z(level - (3 * down) - 3), + max: (cluster_pos + (dir * cone_offset) + 1 - top_pos) + .with_z( + (level - (3 * down) - 2) + cone_height + - (cone_height / 6) + + 3, + ), + }) + .fill(snow_ice_fill.clone()); + } + } + // ceiling snow covered speleothem cluster + for dir in NEIGHBORS { + for c in 0..8 { + let cluster_pos = yetipit_center + dir * (c * (room_size / 5)); + for dir in NEIGHBORS3 { + let cone_radius = 3 + + (RandomField::new(0) + .get((cluster_pos + dir).with_z(alt as i32)) + % 3) + as i32; + let cone_offset = 3 + + (RandomField::new(0) + .get((cluster_pos + 1 + dir).with_z(alt as i32)) + % 4) + as i32; + let cone_height = 15 + + (RandomField::new(0) + .get((cluster_pos + 2 + dir).with_z(alt as i32)) + % 10) + as i32; + // cones + painter + .cone_with_radius( + (cluster_pos + (dir * cone_offset)).with_z( + level - (3 * down) - 4 - (cone_height / 8), + ), + cone_radius as f32, + (cone_height - 1) as f32, + ) + .rotate_about( + Mat3::rotation_x(PI).as_(), + yetipit_center.with_z(level - (2 * down) - 1), + ) + .fill(snow_ice_fill.clone()); + // small cone tops + let top_pos = (RandomField::new(0) + .get((cluster_pos + 3 + dir).with_z(level)) + % 2) + as i32; + painter + .aabb(Aabb { + min: (cluster_pos + (dir * cone_offset) - top_pos) + .with_z(level - (3 * down) - 3), + max: (cluster_pos + (dir * cone_offset) + 1 + - top_pos) + .with_z( + (level - (3 * down) - 2) + cone_height + - (cone_height / 6) + - 2, + ), + }) + .rotate_about( + Mat3::rotation_x(PI).as_(), + yetipit_center.with_z(level - (2 * down)), + ) + .fill(snow_ice_fill.clone()); + } + } + } + // frozen ponds + for dir in NEIGHBORS3 { + let pond_radius = (RandomField::new(0) + .get((yetipit_center + dir).with_z(alt as i32)) + % 8) as i32; + let pond_pos = + yetipit_center + (dir * ((room_size / 4) + (pond_radius))); + painter + .cylinder_with_radius( + pond_pos.with_z(level - (3 * down) - 6), + pond_radius as f32, + 1.0, + ) + .fill(ice_fill.clone()); + } + // yeti + let yeti_spawn = yetipit_center.with_z(level - (3 * down) - 4); + painter.spawn(yeti(yeti_spawn.as_(), &mut rng)); + } else { + // mob rooms + painter + .cylinder_with_radius( + yetipit_center.with_z(level - (3 * down) - 5), + room_size as f32, + ((room_size / 3) + 5) as f32, + ) + .clear(); + // sprites: icecrystals, bones + for r in 0..4 { + painter + .cylinder_with_radius( + yetipit_center.with_z(level - (3 * down) - 2 - r), + (room_size + 2 - r) as f32, + 1.0, + ) + .fill(snow_ice_fill.clone()); + painter + .cylinder_with_radius( + yetipit_center.with_z(level - (3 * down) - 1 - r), + (room_size - r) as f32, + 1.0, + ) + .fill(sprites_fill.clone()); + painter + .cylinder_with_radius( + yetipit_center.with_z(level - (3 * down) - 2 - r), + (room_size - 1 - r) as f32, + 2.0, + ) + .clear(); + } + let yetipit_mobs = 1 + + (RandomField::new(0) + .get(yetipit_center.with_z(level - (3 * down) - 3)) + % 2) as i32; + for _ in 0..yetipit_mobs { + let yetipit_mob_spawn = + yetipit_center.with_z(level - (3 * down) - 3); + painter + .spawn(random_yetipit_mob(yetipit_mob_spawn.as_(), &mut rng)); + } + painter + .cone_with_radius( + yetipit_center.with_z(level - (3 * down) + (room_size / 3) - 2), + room_size as f32, + (room_size / 3) as f32, + ) + .clear(); + // snow covered speleothem cluster + for dir in NEIGHBORS { + let cluster_pos = yetipit_center + dir * room_size; + for dir in NEIGHBORS { + let cone_radius = 3 + + (RandomField::new(0) + .get((cluster_pos + dir).with_z(alt as i32)) + % 3) as i32; + let cone_offset = 3 + + (RandomField::new(0) + .get((cluster_pos + 1 + dir).with_z(alt as i32)) + % 4) as i32; + let cone_height = 15 + + (RandomField::new(0) + .get((cluster_pos + 2 + dir).with_z(alt as i32)) + % 10) as i32; + // cones + painter + .cone_with_radius( + (cluster_pos + (dir * cone_offset)) + .with_z(level - (3 * down) - 4 - (cone_height / 8)), + cone_radius as f32, + cone_height as f32, + ) + .fill(snow_ice_fill.clone()); + // small cone tops + let top_pos = (RandomField::new(0) + .get((cluster_pos + 3 + dir).with_z(level)) + % 2) + as i32; + painter + .aabb(Aabb { + min: (cluster_pos + (dir * cone_offset) - top_pos) + .with_z(level - (3 * down) - 3), + max: (cluster_pos + (dir * cone_offset) + 1 - top_pos) + .with_z( + (level - (3 * down) - 2) + cone_height + - (cone_height / 6) + + 3, + ), + }) + .fill(snow_ice_fill.clone()); + } + } + // ceiling snow covered speleothem cluster + for dir in NEIGHBORS { + for c in 0..5 { + let cluster_pos = yetipit_center + dir * (c * (room_size / 3)); + for dir in NEIGHBORS { + let cone_radius = 3 + + (RandomField::new(0) + .get((cluster_pos + dir).with_z(alt as i32)) + % 3) + as i32; + let cone_offset = 3 + + (RandomField::new(0) + .get((cluster_pos + 1 + dir).with_z(alt as i32)) + % 4) + as i32; + let cone_height = 15 + + (RandomField::new(0) + .get((cluster_pos + 2 + dir).with_z(alt as i32)) + % 10) + as i32; + // cones + painter + .cone_with_radius( + (cluster_pos + (dir * cone_offset)).with_z( + level - (3 * down) - 4 - (cone_height / 8), + ), + cone_radius as f32, + (cone_height - 1) as f32, + ) + .rotate_about( + Mat3::rotation_x(PI).as_(), + yetipit_center.with_z(level - (2 * down) - 1), + ) + .fill(snow_ice_fill.clone()); + // small cone tops + let top_pos = (RandomField::new(0) + .get((cluster_pos + 3 + dir).with_z(level)) + % 2) + as i32; + painter + .aabb(Aabb { + min: (cluster_pos + (dir * cone_offset) - top_pos) + .with_z(level - (3 * down) - 3), + max: (cluster_pos + (dir * cone_offset) + 1 + - top_pos) + .with_z( + (level - (3 * down) - 2) + cone_height + - (cone_height / 6) + - 2, + ), + }) + .rotate_about( + Mat3::rotation_x(PI).as_(), + yetipit_center.with_z(level - (2 * down)), + ) + .fill(snow_ice_fill.clone()); + } + } + } + // frozen pond + painter + .cylinder_with_radius( + yetipit_center.with_z(level - (3 * down) - 4), + (room_size / 8) as f32, + 1.0, + ) + .fill(ice_fill.clone()); + } + let tunnels = (2 + RandomField::new(0) + .get((yetipit_center + s).with_z(level)) + % 2) as i32; + for t in 1..tunnels { + let away1 = (50 + + RandomField::new(0).get((yetipit_center * (s + t)).with_z(level)) + % 20) as i32; + let away2 = (50 + + RandomField::new(0) + .get((yetipit_center * (s + (2 * t))).with_z(level)) + % 20) as i32; + let away3 = (50 + + RandomField::new(0) + .get((yetipit_center * (s + (3 * t))).with_z(level)) + % 20) as i32; + let away4 = (50 + + RandomField::new(0) + .get((yetipit_center * (s + (4 * t))).with_z(level)) + % 20) as i32; + + let dir1 = 1 - 2 + * (RandomField::new(0).get((yetipit_center).with_z(t * level)) % 2) + as i32; + let dir2 = 1 - 2 + * (RandomField::new(0) + .get((yetipit_center).with_z((2 * t) * level)) + % 2) as i32; + // caves + painter + .cubic_bezier( + yetipit_center.with_z(level - 3), + Vec2::new( + yetipit_center.x + ((away1 + (s * (down / 4))) * dir1), + yetipit_center.y + ((away2 + (s * (down / 4))) * dir2), + ) + .with_z(level - (2 * down)), + Vec2::new( + yetipit_center.x + ((away3 + (s * (down / 4))) * dir1), + yetipit_center.y + ((away4 + (s * (down / 4))) * dir2), + ) + .with_z(level - (3 * down)), + yetipit_center.with_z(level - (3 * down)), + 6.0, + ) + .clear(); + } + } + // yetipit entrance + // rocks + painter + .sphere(Aabb { + min: (yetipit_entrance_pos - 8).with_z(alt as i32 - 8), + max: (yetipit_entrance_pos + 8).with_z(alt as i32 + 8), + }) + .fill(rock_fill.clone()); + // repaint ground + painter + .cylinder(Aabb { + min: (yetipit_entrance_pos - 8).with_z(alt as i32 - 20), + max: (yetipit_entrance_pos + 8).with_z(alt as i32), + }) + .fill(snow_ice_fill.clone()); + // tunnel + let door_dist = self.cavern_center - yetipit_entrance_pos; + let door_dir = door_dist + / Vec2::new((door_dist.x).pow(2).sqrt(), (door_dist.y).pow(2).sqrt()); + painter + .cubic_bezier( + (yetipit_entrance_pos + door_dir * 10).with_z(alt as i32 + 2), + (yetipit_entrance_pos - door_dir * 16).with_z(alt as i32 - 10), + (yetipit_entrance_pos + door_dir * 20).with_z((alt as i32) - 30), + self.cavern_center.with_z((alt as i32) - 50), + 4.0, + ) + .clear(); + // bone door + painter + .cylinder(Aabb { + min: Vec2::new(yetipit_entrance_pos.x - 7, yetipit_entrance_pos.y - 7) + .with_z(alt as i32 - 8), + max: Vec2::new(yetipit_entrance_pos.x + 7, yetipit_entrance_pos.y + 7) + .with_z((alt as i32) - 7), + }) + .fill(snow_ice_fill.clone()); + + painter + .cylinder(Aabb { + min: Vec2::new(yetipit_entrance_pos.x - 3, yetipit_entrance_pos.y - 3) + .with_z(alt as i32 - 8), + max: Vec2::new(yetipit_entrance_pos.x + 3, yetipit_entrance_pos.y + 3) + .with_z((alt as i32) - 7), + }) + .fill(Fill::Block(Block::air(SpriteKind::BoneKeyDoor))); + painter + .aabb(Aabb { + min: Vec2::new(yetipit_entrance_pos.x - 1, yetipit_entrance_pos.y) + .with_z(alt as i32 - 8), + max: Vec2::new(yetipit_entrance_pos.x, yetipit_entrance_pos.y + 1) + .with_z((alt as i32) - 7), + }) + .fill(Fill::Block(Block::air(SpriteKind::BoneKeyhole))); + }, + AdletStructure::Tannery => { + // shattered bone pieces + painter + .cylinder_with_radius(wpos.with_z(alt as i32), 7.0, 1.0) + .fill(bone_shrub.clone()); + // bones upright + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 6, wpos.y).with_z(alt as i32), + max: Vec2::new(wpos.x + 6, wpos.y + 1).with_z((alt as i32) + 8), + }) + .fill(bone_fill.clone()); + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 5, wpos.y).with_z(alt as i32), + max: Vec2::new(wpos.x + 5, wpos.y + 1).with_z((alt as i32) + 8), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 6, wpos.y - 1).with_z(alt as i32 + 8), + max: Vec2::new(wpos.x + 6, wpos.y + 2).with_z((alt as i32) + 9), + }) + .fill(bone_fill.clone()); + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 5, wpos.y - 1).with_z(alt as i32 + 8), + max: Vec2::new(wpos.x + 5, wpos.y + 2).with_z((alt as i32) + 9), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 6, wpos.y).with_z(alt as i32 + 8), + max: Vec2::new(wpos.x + 6, wpos.y + 1).with_z((alt as i32) + 9), + }) + .clear(); + // bones lying + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 6, wpos.y + 3).with_z(alt as i32), + max: Vec2::new(wpos.x + 6, wpos.y + 4).with_z((alt as i32) + 2), + }) + .fill(bone_fill.clone()); + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 5, wpos.y + 3).with_z(alt as i32), + max: Vec2::new(wpos.x - 3, wpos.y + 4).with_z((alt as i32) + 1), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(wpos.x + 3, wpos.y + 3).with_z(alt as i32), + max: Vec2::new(wpos.x + 5, wpos.y + 4).with_z((alt as i32) + 1), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 2, wpos.y + 3).with_z(alt as i32), + max: Vec2::new(wpos.x + 2, wpos.y + 4).with_z((alt as i32) + 1), + }) + .clear(); + // hide + for n in 0..10 { + let hide_color = + match RandomField::new(0).get((wpos + n).with_z(alt as i32)) % 4 { + 0 => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(73, 29, 0))), + 1 => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(78, 67, 43))), + 2 => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(83, 74, 41))), + _ => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(14, 36, 34))), + }; + let rand_length = + (RandomField::new(0).get((wpos - n).with_z(alt as i32)) % 7) as i32; + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 5, wpos.y).with_z(alt as i32 + rand_length), + max: Vec2::new(wpos.x - 4 + n, wpos.y + 1).with_z((alt as i32) + 8), + }) + .fill(hide_color.clone()); + } + let tannery_mobs = + 1 + (RandomField::new(0).get(wpos.with_z(alt as i32)) % 2) as i32; + for _ in 0..tannery_mobs { + let tannery_mob_spawn = wpos.with_z(alt as i32); + painter.spawn(random_adlet(tannery_mob_spawn.as_(), &mut rng)); + } + }, + AdletStructure::AnimalPen => { + let pen_size = 8.0; + painter + .sphere_with_radius( + wpos.with_z(alt as i32 + (pen_size as i32 / 4) - 1), + pen_size as f32, + ) + .fill(bone_fill.clone()); + painter + .sphere_with_radius( + wpos.with_z(alt as i32 + (pen_size as i32 / 2) - 1), + pen_size as f32, + ) + .clear(); + painter + .cylinder(Aabb { + min: (wpos - (pen_size as i32)).with_z(alt as i32 - (pen_size as i32)), + max: (wpos + (pen_size as i32)).with_z(alt as i32), + }) + .fill(dirt_fill.clone()); + painter + .cylinder(Aabb { + min: (wpos - (pen_size as i32) + 1).with_z(alt as i32), + max: (wpos + (pen_size as i32) - 1).with_z(alt as i32 + 1), + }) + .fill(grass_fill.clone()); + enum AnimalPenKind { + Rat, + Wolf, + Bear, + } + let (kind, num) = { + let rand_field = RandomField::new(1).get(wpos.with_z(alt as i32)); + match RandomField::new(0).get(wpos.with_z(alt as i32)) % 4 { + 0 => (AnimalPenKind::Bear, 1 + rand_field % 2), + 1 => (AnimalPenKind::Wolf, 2 + rand_field % 2), + _ => (AnimalPenKind::Rat, 3 + rand_field % 3), + } + }; + for _ in 0..num { + let animalpen_mob_spawn = wpos.with_z(alt as i32); + match kind { + AnimalPenKind::Rat => { + painter.spawn(rat(animalpen_mob_spawn.as_(), &mut rng)) + }, + AnimalPenKind::Wolf => { + painter.spawn(wolf(animalpen_mob_spawn.as_(), &mut rng)) + }, + AnimalPenKind::Bear => { + painter.spawn(bear(animalpen_mob_spawn.as_(), &mut rng)) + }, + } + } + }, + AdletStructure::CookFire => { + painter + .cylinder(Aabb { + min: (wpos - 3).with_z(alt as i32), + max: (wpos + 4).with_z(alt as i32 + 1), + }) + .fill(bone_fill.clone()); + let cook_sprites = Fill::Sampling(Arc::new(|wpos| { + Some(match (RandomField::new(0).get(wpos)) % 20 { + 0 => Block::air(SpriteKind::Pot), + 1 => Block::air(SpriteKind::Bowl), + 2 => Block::air(SpriteKind::Pot), + 3 => Block::air(SpriteKind::VialEmpty), + 4 => Block::air(SpriteKind::Lantern), + _ => Block::air(SpriteKind::Empty), + }) + })); + painter + .cylinder(Aabb { + min: (wpos - 3).with_z(alt as i32 + 1), + max: (wpos + 4).with_z(alt as i32 + 2), + }) + .fill(cook_sprites); + painter + .cylinder(Aabb { + min: (wpos - 2).with_z(alt as i32), + max: (wpos + 3).with_z(alt as i32 + 2), + }) + .clear(); + painter + .aabb(Aabb { + min: (wpos).with_z(alt as i32), + max: (wpos + 1).with_z(alt as i32 + 1), + }) + .fill(bone_fill.clone()); + painter.sprite(wpos.with_z(alt as i32 + 1), SpriteKind::FireBowlGround); + let cookfire_mobs = + 1 + (RandomField::new(0).get(wpos.with_z(alt as i32)) % 2) as i32; + for _ in 0..cookfire_mobs { + let cookfire_mob_spawn = wpos.with_z(alt as i32); + painter.spawn(random_adlet(cookfire_mob_spawn.as_(), &mut rng)); + } + }, + AdletStructure::RockHut => { + painter + .sphere_with_radius(wpos.with_z(alt as i32), 5.0) + .fill(rock_fill.clone()); + painter + .sphere_with_radius(wpos.with_z(alt as i32), 4.0) + .clear(); + // clear entries + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 6, wpos.y - 1).with_z(alt as i32), + max: Vec2::new(wpos.x + 6, wpos.y + 1).with_z(alt as i32 + 2), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 1, wpos.y - 6).with_z(alt as i32), + max: Vec2::new(wpos.x + 1, wpos.y + 6).with_z(alt as i32 + 2), + }) + .clear(); + // fill with dirt + painter + .cylinder(Aabb { + min: (wpos - 5).with_z((alt as i32) - 5), + max: (wpos + 5).with_z(alt as i32 - 1), + }) + .fill(dirt_fill.clone()); + painter.sprite(wpos.with_z(alt as i32) - 1, SpriteKind::FireBowlGround); + let rockhut_mobs = + 1 + (RandomField::new(0).get(wpos.with_z(alt as i32)) % 2) as i32; + for _ in 0..rockhut_mobs { + let rockhut_mob_spawn = wpos.with_z(alt as i32); + painter.spawn(random_adlet(rockhut_mob_spawn.as_(), &mut rng)); + } + }, + AdletStructure::BoneHut => { + let hut_radius = 5; + // top decor bone with some hide + painter + .aabb(Aabb { + min: wpos.with_z((alt as i32) + hut_radius + 4), + max: (wpos + 1).with_z((alt as i32) + hut_radius + 7), + }) + .fill(bone_fill.clone()); + painter + .aabb(Aabb { + min: Vec2::new(wpos.x, wpos.y - 1) + .with_z((alt as i32) + hut_radius + 7), + max: Vec2::new(wpos.x + 1, wpos.y + 2) + .with_z((alt as i32) + hut_radius + 8), + }) + .fill(bone_fill.clone()); + painter + .aabb(Aabb { + min: wpos.with_z((alt as i32) + hut_radius + 7), + max: (wpos + 1).with_z((alt as i32) + hut_radius + 8), + }) + .clear(); + let top_color = Fill::Sampling(Arc::new(|wpos| { + Some(match (RandomField::new(0).get(wpos)) % 10 { + 0 => Block::new(BlockKind::Wood, Rgb::new(73, 29, 0)), + 1 => Block::new(BlockKind::Wood, Rgb::new(78, 67, 43)), + 2 => Block::new(BlockKind::Wood, Rgb::new(83, 74, 41)), + 3 => Block::new(BlockKind::Wood, Rgb::new(14, 36, 34)), + _ => Block::new(BlockKind::Rock, Rgb::new(200, 160, 140)), + }) + })); + painter + .aabb(Aabb { + min: (wpos - 1).with_z((alt as i32) + hut_radius + 3), + max: (wpos + 2).with_z((alt as i32) + hut_radius + 5), + }) + .fill(top_color.clone()); + // 4 hide pieces + for dir in CARDINALS { + let hide_size = + 6 + (RandomField::new(0).get((wpos + dir).with_z(alt as i32)) % 2); + let hide_color = + match RandomField::new(0).get((wpos + dir).with_z(alt as i32)) % 4 { + 0 => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(73, 29, 0))), + 1 => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(78, 67, 43))), + 2 => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(83, 74, 41))), + _ => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(14, 36, 34))), + }; + painter + .sphere_with_radius( + (wpos + (2 * dir)).with_z((alt as i32) + 2), + hide_size as f32, + ) + .fill(hide_color.clone()); + } + // clear room + painter + .sphere_with_radius(wpos.with_z((alt as i32) + 2), 6.0) + .intersect(painter.aabb(Aabb { + min: (wpos - 6).with_z(alt as i32), + max: (wpos + 6).with_z(alt as i32 + 2 * hut_radius), + })) + .clear(); + //clear entries + painter + .aabb(Aabb { + min: Vec2::new(wpos.x - 1, wpos.y - hut_radius - 6).with_z(alt as i32), + max: Vec2::new(wpos.x + 1, wpos.y + hut_radius + 6) + .with_z((alt as i32) + 3), + }) + .clear(); + + // bones + for h in 0..(hut_radius + 4) { + painter + .line( + (wpos - hut_radius).with_z((alt as i32) + h), + (wpos + hut_radius).with_z((alt as i32) + h), + 0.5, + ) + .intersect( + painter.sphere_with_radius(wpos.with_z((alt as i32) + 2), 9.0), + ) + .fill(bone_fill.clone()); + + painter + .line( + Vec2::new(wpos.x - hut_radius, wpos.y + hut_radius) + .with_z((alt as i32) + h), + Vec2::new(wpos.x + hut_radius, wpos.y - hut_radius) + .with_z((alt as i32) + h), + 0.5, + ) + .intersect( + painter.sphere_with_radius(wpos.with_z((alt as i32) + 2), 9.0), + ) + .fill(bone_fill.clone()); + } + painter + .sphere_with_radius(wpos.with_z((alt as i32) + 2), 5.0) + .intersect(painter.aabb(Aabb { + min: (wpos - 5).with_z(alt as i32), + max: (wpos + 5).with_z((alt as i32) + 2 * hut_radius), + })) + .clear(); + + // WallSconce + painter.rotated_sprite( + Vec2::new(wpos.x - hut_radius + 1, wpos.y + hut_radius - 2) + .with_z((alt as i32) + 3), + SpriteKind::WallSconce, + 0_u8, + ); + // FireBowl + painter.sprite(wpos.with_z(alt as i32), SpriteKind::FireBowlGround); + let bonehut_mobs = + 1 + (RandomField::new(0).get(wpos.with_z(alt as i32)) % 2) as i32; + for _ in 0..bonehut_mobs { + let bonehut_mob_spawn = wpos.with_z(alt as i32); + painter.spawn(random_adlet(bonehut_mob_spawn.as_(), &mut rng)); + } + // chests + let chest = Fill::Sampling(Arc::new(|wpos| { + Some(match (RandomField::new(0).get(wpos)) % 2 { + 0 => Block::air(SpriteKind::DungeonChest1), + _ => Block::air(SpriteKind::Empty), + }) + })); + painter + .aabb(Aabb { + min: (wpos - 3).with_z(alt as i32), + max: (wpos - 2).with_z((alt as i32) + 1), + }) + .fill(chest); + }, + AdletStructure::BossBoneHut => { + let bosshut_pos = wpos; + let hut_radius = 10; + // 4 hide pieces + for dir in CARDINALS { + let hide_size = 10 + + (RandomField::new(0).get((bosshut_pos + dir).with_z(alt as i32)) % 4); + let hide_color = match RandomField::new(0) + .get((bosshut_pos + dir).with_z(alt as i32)) + % 4 + { + 0 => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(73, 29, 0))), + 1 => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(78, 67, 43))), + 2 => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(83, 74, 41))), + _ => Fill::Block(Block::new(BlockKind::Wood, Rgb::new(14, 36, 34))), + }; + painter + .sphere_with_radius( + (bosshut_pos + (3 * dir)).with_z((alt as i32) + 2), + hide_size as f32, + ) + .fill(hide_color.clone()); + } + // bones + for h in 0..(hut_radius + 4) { + painter + .line( + (bosshut_pos - hut_radius + 1).with_z((alt as i32) + h), + (bosshut_pos + hut_radius - 1).with_z((alt as i32) + h), + 1.5, + ) + .intersect( + painter + .sphere_with_radius(bosshut_pos.with_z((alt as i32) + 2), 14.0), + ) + .intersect( + painter.aabb(Aabb { + min: (bosshut_pos - 2 * hut_radius).with_z(alt as i32), + max: (bosshut_pos + 2 * hut_radius) + .with_z((alt as i32) + 2 * hut_radius), + }), + ) + .fill(bone_fill.clone()); + + painter + .line( + Vec2::new( + bosshut_pos.x - hut_radius + 1, + bosshut_pos.y + hut_radius - 2, + ) + .with_z((alt as i32) + h), + Vec2::new( + bosshut_pos.x + hut_radius - 1, + bosshut_pos.y - hut_radius + 2, + ) + .with_z((alt as i32) + h), + 1.5, + ) + .intersect( + painter + .sphere_with_radius(bosshut_pos.with_z((alt as i32) + 2), 14.0), + ) + .intersect( + painter.aabb(Aabb { + min: (bosshut_pos - 2 * hut_radius).with_z(alt as i32), + max: (bosshut_pos + 2 * hut_radius) + .with_z((alt as i32) + 2 * hut_radius), + }), + ) + .fill(bone_fill.clone()); + } + painter + .sphere_with_radius(bosshut_pos.with_z((alt as i32) + 2), 9.0) + .intersect(painter.aabb(Aabb { + min: (bosshut_pos - 9).with_z(alt as i32), + max: (bosshut_pos + 9).with_z(alt as i32 + 11), + })) + .clear(); + + for n in 0..2 { + // large entries + + painter + .sphere_with_radius( + (Vec2::new( + bosshut_pos.x, + bosshut_pos.y - hut_radius + (2 * (hut_radius * n)), + )) + .with_z((alt as i32) + 2), + 7.0, + ) + .intersect( + painter.aabb(Aabb { + min: Vec2::new( + bosshut_pos.x - 7, + bosshut_pos.y - hut_radius + (2 * (hut_radius * n) - 7), + ) + .with_z(alt as i32), + max: Vec2::new( + bosshut_pos.x + 7, + bosshut_pos.y - hut_radius + (2 * (hut_radius * n) + 7), + ) + .with_z(alt as i32 + 9), + }), + ) + .clear(); + let entry_start = Vec2::new( + bosshut_pos.x - hut_radius + 3, + bosshut_pos.y - hut_radius - 2 + (n * ((2 * hut_radius) + 4)), + ) + .with_z(alt as i32); + let entry_peak = Vec2::new( + bosshut_pos.x, + bosshut_pos.y - hut_radius + (n * (2 * hut_radius)), + ) + .with_z(alt as i32 + hut_radius + 2); + let entry_end = Vec2::new( + bosshut_pos.x + hut_radius - 3, + bosshut_pos.y - hut_radius - 2 + (n * ((2 * hut_radius) + 4)), + ) + .with_z(alt as i32); + painter + .cubic_bezier(entry_start, entry_peak, entry_peak, entry_end, 1.0) + .fill(bone_fill.clone()); + } + + // top decor bone with some hide + painter + .aabb(Aabb { + min: bosshut_pos.with_z((alt as i32) + hut_radius + 5), + max: (bosshut_pos + 1).with_z((alt as i32) + hut_radius + 8), + }) + .fill(bone_fill.clone()); + painter + .aabb(Aabb { + min: Vec2::new(bosshut_pos.x, bosshut_pos.y - 1) + .with_z((alt as i32) + hut_radius + 8), + max: Vec2::new(bosshut_pos.x + 1, bosshut_pos.y + 2) + .with_z((alt as i32) + hut_radius + 9), + }) + .fill(bone_fill.clone()); + painter + .aabb(Aabb { + min: bosshut_pos.with_z((alt as i32) + hut_radius + 8), + max: (bosshut_pos + 1).with_z((alt as i32) + hut_radius + 9), + }) + .clear(); + + let top_color = Fill::Sampling(Arc::new(|bosshut_pos| { + Some(match (RandomField::new(0).get(bosshut_pos)) % 10 { + 0 => Block::new(BlockKind::Wood, Rgb::new(73, 29, 0)), + 1 => Block::new(BlockKind::Wood, Rgb::new(78, 67, 43)), + 2 => Block::new(BlockKind::Wood, Rgb::new(83, 74, 41)), + 3 => Block::new(BlockKind::Wood, Rgb::new(14, 36, 34)), + _ => Block::new(BlockKind::Rock, Rgb::new(200, 160, 140)), + }) + })); + painter + .aabb(Aabb { + min: (bosshut_pos - 1).with_z((alt as i32) + hut_radius + 5), + max: (bosshut_pos + 2).with_z((alt as i32) + hut_radius + 6), + }) + .fill(top_color); + // WallSconces + for dir in SQUARE_4 { + let corner_pos = Vec2::new( + bosshut_pos.x - (hut_radius / 2) - 1, + bosshut_pos.y - (hut_radius / 2) - 2, + ); + let sprite_pos = Vec2::new( + corner_pos.x + dir.x * (hut_radius + 1), + corner_pos.y + dir.y * (hut_radius + 3), + ) + .with_z(alt as i32 + 3); + painter.rotated_sprite( + sprite_pos, + SpriteKind::WallSconce, + (2 + (4 * dir.x)) as u8, + ); + } + let boss_spawn = wpos.with_z(alt as i32); + painter.spawn(adlet_elder(boss_spawn.as_(), &mut rng)); + }, + } + } + } +} + +struct RibCageGenerator { + dir: Dir, + length: u32, + spine_height: f32, + spine_radius: f32, + /// Defines how the spine curves given the ratio along the spine from 0.0 to + /// 1.0, the amplitude, and the wavelength + spine_curve_function: fn(f32, f32, f32) -> f32, + spine_curve_amplitude: f32, + // FIXME: CAN CAUSE DIV BY 0 IF VALUE IS 0.0 + spine_curve_wavelength: f32, + spine_start_z_offset: f32, + spine_ctrl0_z_offset: f32, + spine_ctrl1_z_offset: f32, + spine_end_z_offset: f32, + spine_ctrl0_length_fraction: f32, + spine_ctrl1_length_fraction: f32, + rib_base_alt: f32, + rib_spacing: usize, + rib_radius: f32, + rib_run: f32, + rib_ctrl0_run_fraction: f32, + rib_ctrl1_run_fraction: f32, + rib_ctrl0_width_offset: f32, + rib_ctrl1_width_offset: f32, + /// Defines how much ribs flare out as you go along the rib cage given the + /// ratio along the spine from 0.0 to 1.0 + rib_width_curve: fn(f32) -> f32, + rib_ctrl0_height_fraction: f32, + rib_ctrl1_height_fraction: f32, + vertebra_radius: f32, + vertebra_width: f32, + vertebra_z_offset: f32, +} + +impl RibCageGenerator { + fn bones<'a>(&self, origin: Vec2, painter: &'a Painter) -> Vec> { + let RibCageGenerator { + dir, + length, + spine_height, + spine_radius, + spine_curve_function, + spine_curve_amplitude, + spine_curve_wavelength, + spine_start_z_offset, + spine_ctrl0_z_offset, + spine_ctrl1_z_offset, + spine_end_z_offset, + spine_ctrl0_length_fraction, + spine_ctrl1_length_fraction, + rib_base_alt, + rib_spacing, + rib_radius, + rib_run, + rib_ctrl0_run_fraction, + rib_ctrl1_run_fraction, + rib_ctrl0_width_offset, + rib_ctrl1_width_offset, + rib_width_curve, + rib_ctrl0_height_fraction, + rib_ctrl1_height_fraction, + vertebra_radius, + vertebra_width, + vertebra_z_offset, + } = self; + let length_f32 = *length as f32; + + let mut bones = Vec::new(); + + let spine_start = origin + .map(|e| e as f32) + .with_z(spine_height + spine_start_z_offset) + + spine_curve_function(0.0, *spine_curve_amplitude, *spine_curve_wavelength) + * Vec3::unit_y(); + let spine_ctrl0 = origin + .map(|e| e as f32) + .with_z(spine_height + spine_ctrl0_z_offset) + + length_f32 * spine_ctrl0_length_fraction * Vec3::unit_x() + + spine_curve_function( + length_f32 * spine_ctrl0_length_fraction, + *spine_curve_amplitude, + *spine_curve_wavelength, + ) * Vec3::unit_y(); + let spine_ctrl1 = origin + .map(|e| e as f32) + .with_z(spine_height + spine_ctrl1_z_offset) + + length_f32 * spine_ctrl1_length_fraction * Vec3::unit_x() + + spine_curve_function( + length_f32 * spine_ctrl1_length_fraction, + *spine_curve_amplitude, + *spine_curve_wavelength, + ) * Vec3::unit_y(); + let spine_end = origin + .map(|e| e as f32) + .with_z(spine_height + spine_end_z_offset) + + length_f32 * Vec3::unit_x() + + spine_curve_function(length_f32, *spine_curve_amplitude, *spine_curve_wavelength) + * Vec3::unit_y(); + let spine_bezier = CubicBezier3 { + start: spine_start, + ctrl0: spine_ctrl0, + ctrl1: spine_ctrl1, + end: spine_end, + }; + let spine = painter.cubic_bezier( + spine_start, + spine_ctrl0, + spine_ctrl1, + spine_end, + *spine_radius, + ); + + let rotation_origin = Vec3::new(spine_start.x, spine_start.y + 0.5, spine_start.z); + let rotate = |prim: PrimitiveRef<'a>, dir: &Dir| -> PrimitiveRef<'a> { + match dir { + Dir::X => prim, + Dir::Y => prim.rotate_about(Mat3::rotation_z(0.5 * PI).as_(), rotation_origin), + Dir::NegX => prim.rotate_about(Mat3::rotation_z(PI).as_(), rotation_origin), + Dir::NegY => prim.rotate_about(Mat3::rotation_z(1.5 * PI).as_(), rotation_origin), + } + }; + + let spine_rotated = rotate(spine, dir); + bones.push(spine_rotated); + + for i in (0..*length).step_by(*rib_spacing) { + enum Side { + Left, + Right, + } + + let rib = |side| -> PrimitiveRef { + let y_offset_multiplier = match side { + Side::Left => 1.0, + Side::Right => -1.0, + }; + let rib_start: Vec3 = spine_bezier + .evaluate((i as f32 / length_f32).clamped(0.0, 1.0)) + + y_offset_multiplier * Vec3::unit_y(); + let rib_ctrl0 = Vec3::new( + rib_start.x + rib_ctrl0_run_fraction * rib_run, + rib_start.y + + y_offset_multiplier * rib_ctrl0_width_offset + + y_offset_multiplier * rib_width_curve(i as f32), + rib_base_alt + rib_ctrl0_height_fraction * (rib_start.z - rib_base_alt), + ); + let rib_ctrl1 = Vec3::new( + rib_start.x + rib_ctrl1_run_fraction * rib_run, + rib_start.y + + y_offset_multiplier * rib_ctrl1_width_offset + + y_offset_multiplier * rib_width_curve(i as f32), + rib_base_alt + rib_ctrl1_height_fraction * (rib_start.z - rib_base_alt), + ); + let rib_end = Vec3::new( + rib_start.x + rib_run, + rib_start.y + y_offset_multiplier * rib_width_curve(i as f32), + *rib_base_alt, + ); + painter.cubic_bezier(rib_start, rib_ctrl0, rib_ctrl1, rib_end, *rib_radius) + }; + let l_rib = rib(Side::Left); + let l_rib_rotated = rotate(l_rib, dir); + bones.push(l_rib_rotated); + + let r_rib = rib(Side::Right); + let r_rib_rotated = rotate(r_rib, dir); + bones.push(r_rib_rotated); + + let vertebra_start: Vec3 = + spine_bezier.evaluate((i as f32 / length_f32).clamped(0.0, 1.0)) + Vec3::unit_y(); + let vertebra = painter.ellipsoid(Aabb { + min: Vec3::new( + vertebra_start.x - vertebra_width, + vertebra_start.y - 1.0 - vertebra_radius, + vertebra_start.z - vertebra_radius + vertebra_z_offset, + ) + .map(|e| e.round() as i32), + max: Vec3::new( + vertebra_start.x + vertebra_width, + vertebra_start.y - 1.0 + vertebra_radius, + vertebra_start.z + vertebra_radius + vertebra_z_offset, + ) + .map(|e| e.round() as i32), + }); + let vertebra_rotated = rotate(vertebra, dir); + bones.push(vertebra_rotated); + } + + bones + } +} + +fn adlet_hunter(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)) + .with_asset_expect("common.entity.dungeon.adlet.hunter", rng) +} + +fn adlet_icepicker(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)) + .with_asset_expect("common.entity.dungeon.adlet.icepicker", rng) +} + +fn adlet_tracker(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)) + .with_asset_expect("common.entity.dungeon.adlet.tracker", rng) +} + +fn random_adlet(pos: Vec3, rng: &mut R) -> EntityInfo { + match rng.gen_range(0..3) { + 0 => adlet_hunter(pos, rng), + 1 => adlet_icepicker(pos, rng), + _ => adlet_tracker(pos, rng), + } +} + +fn adlet_elder(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)) + .with_asset_expect("common.entity.dungeon.adlet.elder", rng) +} + +fn rat(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)).with_asset_expect("common.entity.wild.peaceful.rat", rng) +} + +fn wolf(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)) + .with_asset_expect("common.entity.wild.aggressive.wolf", rng) +} + +fn bear(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)) + .with_asset_expect("common.entity.wild.aggressive.bear", rng) +} + +fn frostfang(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)) + .with_asset_expect("common.entity.wild.aggressive.frostfang", rng) +} + +fn roshwalr(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)) + .with_asset_expect("common.entity.wild.aggressive.roshwalr", rng) +} + +fn icedrake(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)) + .with_asset_expect("common.entity.wild.aggressive.icedrake", rng) +} + +fn tursus(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)) + .with_asset_expect("common.entity.wild.aggressive.tursus", rng) +} + +fn random_yetipit_mob(pos: Vec3, rng: &mut R) -> EntityInfo { + match rng.gen_range(0..4) { + 0 => frostfang(pos, rng), + 1 => roshwalr(pos, rng), + 2 => icedrake(pos, rng), + _ => tursus(pos, rng), + } +} + +fn yeti(pos: Vec3, rng: &mut R) -> EntityInfo { + EntityInfo::at(pos.map(|x| x as f32)).with_asset_expect("common.entity.dungeon.adlet.yeti", rng) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_creating_entities() { + let pos = Vec3::zero(); + let mut rng = thread_rng(); + + adlet_hunter(pos, &mut rng); + adlet_icepicker(pos, &mut rng); + adlet_tracker(pos, &mut rng); + random_adlet(pos, &mut rng); + adlet_elder(pos, &mut rng); + rat(pos, &mut rng); + wolf(pos, &mut rng); + bear(pos, &mut rng); + frostfang(pos, &mut rng); + roshwalr(pos, &mut rng); + icedrake(pos, &mut rng); + tursus(pos, &mut rng); + } +} diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 1b092edcc9..6be1381bce 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -227,7 +227,6 @@ impl Room { // Toss mobs in the center of the room if tile_pos == enemy_spawn_tile && wpos2d == tile_wcenter.xy() { let entities = match self.difficulty { - 1 => enemy_1(dynamic_rng, tile_wcenter), 2 => enemy_2(dynamic_rng, tile_wcenter), 3 => enemy_3(dynamic_rng, tile_wcenter), 4 => enemy_4(dynamic_rng, tile_wcenter), @@ -285,7 +284,6 @@ impl Room { if tile_pos == miniboss_spawn_tile && tile_wcenter.xy() == wpos2d { let entities = match self.difficulty { - 1 => mini_boss_1(dynamic_rng, tile_wcenter), 2 => mini_boss_2(dynamic_rng, tile_wcenter), 3 => mini_boss_3(dynamic_rng, tile_wcenter), 4 => mini_boss_4(dynamic_rng, tile_wcenter), @@ -318,7 +316,6 @@ impl Room { if tile_pos == boss_spawn_tile && wpos2d == tile_wcenter.xy() { let entities = match self.difficulty { - 1 => boss_1(dynamic_rng, tile_wcenter), 2 => boss_2(dynamic_rng, tile_wcenter), 3 => boss_3(dynamic_rng, tile_wcenter), 4 => boss_4(dynamic_rng, tile_wcenter), @@ -669,22 +666,6 @@ impl Floor { } } -fn enemy_1(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let number = dynamic_rng.gen_range(2..=4); - let mut entities = Vec::new(); - entities.resize_with(number, || { - // TODO: give enemies health skills? - let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32)); - match dynamic_rng.gen_range(0..=4) { - 0 => entity.with_asset_expect("common.entity.dungeon.tier-1.tracker", dynamic_rng), - 1 => entity.with_asset_expect("common.entity.dungeon.tier-1.icepicker", dynamic_rng), - _ => entity.with_asset_expect("common.entity.dungeon.tier-1.hunter", dynamic_rng), - } - }); - - entities -} - fn enemy_2(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { let number = dynamic_rng.gen_range(2..=4); let mut entities = Vec::new(); @@ -768,13 +749,6 @@ fn turret_5(dynamic_rng: &mut impl Rng, pos: Vec3) -> EntityInfo { EntityInfo::at(pos).with_asset_expect("common.entity.dungeon.tier-5.turret", dynamic_rng) } -fn boss_1(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - vec![ - EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_asset_expect("common.entity.dungeon.tier-1.boss", dynamic_rng), - ] -} - fn boss_2(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) @@ -812,15 +786,6 @@ fn boss_fallback(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec) -> Vec { - let mut entities = Vec::new(); - entities.resize_with(8, || { - EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_asset_expect("common.entity.dungeon.tier-1.rat", dynamic_rng) - }); - entities -} - fn mini_boss_2(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { let mut entities = Vec::new(); entities.resize_with(6, || { @@ -1208,8 +1173,6 @@ impl Floor { Box::new(move |pos| RandomField::new(seed).chance(pos, loot_density * 0.5)), )); let chest_sprite_fill = Fill::Block(Block::air(match difficulty { - 0 => SpriteKind::DungeonChest0, - 1 => SpriteKind::DungeonChest1, 2 => SpriteKind::DungeonChest2, 3 => SpriteKind::DungeonChest3, 4 => SpriteKind::DungeonChest4, @@ -1429,7 +1392,6 @@ mod tests { fn test_creating_bosses() { let mut dynamic_rng = thread_rng(); let tile_wcenter = Vec3::new(0, 0, 0); - boss_1(&mut dynamic_rng, tile_wcenter); boss_2(&mut dynamic_rng, tile_wcenter); boss_3(&mut dynamic_rng, tile_wcenter); boss_4(&mut dynamic_rng, tile_wcenter); @@ -1442,7 +1404,6 @@ mod tests { fn test_creating_enemies() { let mut dynamic_rng = thread_rng(); let random_position = Vec3::new(0, 0, 0); - enemy_1(&mut dynamic_rng, random_position); enemy_2(&mut dynamic_rng, random_position); enemy_3(&mut dynamic_rng, random_position); enemy_4(&mut dynamic_rng, random_position); @@ -1455,7 +1416,6 @@ mod tests { fn test_creating_minibosses() { let mut dynamic_rng = thread_rng(); let tile_wcenter = Vec3::new(0, 0, 0); - mini_boss_1(&mut dynamic_rng, tile_wcenter); mini_boss_2(&mut dynamic_rng, tile_wcenter); mini_boss_3(&mut dynamic_rng, tile_wcenter); mini_boss_4(&mut dynamic_rng, tile_wcenter); diff --git a/world/src/site2/tile.rs b/world/src/site2/tile.rs index 4de6d01db8..9b4c5e80b6 100644 --- a/world/src/site2/tile.rs +++ b/world/src/site2/tile.rs @@ -192,6 +192,7 @@ pub enum TileKind { Gate, GnarlingFortification, Bridge, + AdletStronghold, } #[derive(Clone, PartialEq)] @@ -221,7 +222,12 @@ impl Tile { pub fn is_empty(&self) -> bool { self.kind == TileKind::Empty } - pub fn is_natural(&self) -> bool { matches!(self.kind, TileKind::Empty | TileKind::Hazard(_)) } + pub fn is_natural(&self) -> bool { + matches!( + self.kind, + TileKind::Empty | TileKind::Hazard(_) | TileKind::AdletStronghold + ) + } pub fn is_road(&self) -> bool { matches!(