From cc566abe5c1c72bff5cc8f68272f9f1ddec57fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Wed, 9 Jun 2021 21:14:53 +0100 Subject: [PATCH 01/38] Update wgpu and check for best swap chain format --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- voxygen/src/render/renderer.rs | 6 +++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 145b1f4fee..28f9e22c5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6721,7 +6721,7 @@ dependencies = [ [[package]] name = "wgpu" version = "0.8.0" -source = "git+https://github.com/gfx-rs/wgpu-rs.git?rev=7486bdad64bb5d17b709ecccb41e063469efff88#7486bdad64bb5d17b709ecccb41e063469efff88" +source = "git+https://github.com/gfx-rs/wgpu.git?rev=a92b8549a8e2cb9dac781bafc5ed32828f3caf46#a92b8549a8e2cb9dac781bafc5ed32828f3caf46" dependencies = [ "arrayvec", "js-sys", @@ -6741,7 +6741,7 @@ dependencies = [ [[package]] name = "wgpu-core" version = "0.8.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=53eab747a32414232be45d47cae8a43a369395d0#53eab747a32414232be45d47cae8a43a369395d0" +source = "git+https://github.com/gfx-rs/wgpu.git?rev=a92b8549a8e2cb9dac781bafc5ed32828f3caf46#a92b8549a8e2cb9dac781bafc5ed32828f3caf46" dependencies = [ "arrayvec", "bitflags", @@ -6781,7 +6781,7 @@ dependencies = [ [[package]] name = "wgpu-types" version = "0.8.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=53eab747a32414232be45d47cae8a43a369395d0#53eab747a32414232be45d47cae8a43a369395d0" +source = "git+https://github.com/gfx-rs/wgpu.git?rev=a92b8549a8e2cb9dac781bafc5ed32828f3caf46#a92b8549a8e2cb9dac781bafc5ed32828f3caf46" dependencies = [ "bitflags", "serde", diff --git a/Cargo.toml b/Cargo.toml index 88c76c276e..76f9d2fc02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,7 +118,7 @@ nativeBuildInputs = ["cmake", "python3", "gnumake"] winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" } vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics2" } # patch wgpu so we can use wgpu-profiler crate -wgpu = { git = "https://github.com/gfx-rs/wgpu-rs.git", rev = "7486bdad64bb5d17b709ecccb41e063469efff88" } +wgpu = { git = "https://github.com/gfx-rs/wgpu.git", rev = "a92b8549a8e2cb9dac781bafc5ed32828f3caf46" } # # use the latest fixes in naga (remove when updates trickle down to wgpu-rs) # naga = { git = "https://github.com/gfx-rs/naga.git", rev = "3a0f0144112ff621dd7f731bf455adf6cab19164" } diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 6870c32e42..d5e9f2157d 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -241,9 +241,13 @@ impl Renderer { "selected graphics device" ); + let format = adapter + .get_swap_chain_preferred_format(&surface) + .expect("No supported swap chain format found"); + let sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::RENDER_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, + format, width: dims.width, height: dims.height, present_mode: mode.present_mode.into(), From 62eaabfe88fcc54a72ddcec9e56f00559ccf8288 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Fri, 11 Jun 2021 22:00:06 +0300 Subject: [PATCH 02/38] Introduce hit_timing for combo_melee bonus: speeding up first swing of sword from 0.15 to 0.1 and reducing poise damage of hammer from 25 to 20 --- assets/common/abilities/axe/doublestrike.ron | 2 ++ .../abilities/axesimple/doublestrike.ron | 2 ++ .../custom/basilisk/singlestrike.ron | 1 + .../custom/basilisk/triplestrike.ron | 3 ++ .../custom/birdlargebreathe/triplestrike.ron | 3 ++ .../custom/birdlargefire/triplestrike.ron | 3 ++ .../abilities/custom/husk/singlestrike.ron | 1 + .../abilities/custom/husk/triplestrike.ron | 3 ++ .../custom/quadlowbasic/singlestrike.ron | 1 + .../custom/quadlowbasic/triplestrike.ron | 3 ++ .../custom/quadlowbreathe/triplestrike.ron | 3 ++ .../custom/quadlowquick/quadstrike.ron | 4 +++ .../custom/quadlowranged/singlestrike.ron | 1 + .../custom/quadlowtail/triplestrike.ron | 3 ++ .../custom/quadmedbasic/singlestrike.ron | 1 + .../custom/quadmedbasic/triplestrike.ron | 3 ++ .../custom/quadmedcharge/doublestrike.ron | 2 ++ .../custom/quadmedjump/doublestrike.ron | 2 ++ .../custom/quadmedquick/triplestrike.ron | 3 ++ .../custom/quadsmallbasic/singlestrike.ron | 1 + .../custom/stonegolemfist/singlestrike.ron | 1 + .../custom/theropodbasic/singlestrike.ron | 1 + .../custom/theropodbasic/triplestrike.ron | 3 ++ .../custom/theropodbird/singlestrike.ron | 1 + .../custom/theropodbird/triplestrike.ron | 3 ++ .../custom/wendigomagic/singlestrike.ron | 1 + .../common/abilities/hammer/singlestrike.ron | 3 +- .../abilities/hammersimple/doublestrike.ron | 2 ++ .../common/abilities/spear/doublestrike.ron | 2 ++ .../common/abilities/sword/triplestrike.ron | 5 ++- .../abilities/swordsimple/doublestrike.ron | 2 ++ common/src/comp/ability.rs | 1 + common/src/states/charged_melee.rs | 2 +- common/src/states/combo_melee.rs | 36 ++++++++++++++++--- .../audio/sfx/event_mapper/combat/tests.rs | 4 +++ 35 files changed, 104 insertions(+), 8 deletions(-) diff --git a/assets/common/abilities/axe/doublestrike.ron b/assets/common/abilities/axe/doublestrike.ron index 545aab3ae9..029bb4cdaf 100644 --- a/assets/common/abilities/axe/doublestrike.ron +++ b/assets/common/abilities/axe/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 50.0, base_buildup_duration: 0.15, base_swing_duration: 0.075, + hit_timing: 0.6, base_recover_duration: 0.35, forward_movement: 0.5, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.1, + hit_timing: 0.6, base_recover_duration: 0.35, forward_movement: 0.25, damage_kind: Slashing, diff --git a/assets/common/abilities/axesimple/doublestrike.ron b/assets/common/abilities/axesimple/doublestrike.ron index 2a87b78073..d97b3dbcce 100644 --- a/assets/common/abilities/axesimple/doublestrike.ron +++ b/assets/common/abilities/axesimple/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 50.0, base_buildup_duration: 0.6, base_swing_duration: 0.12, + hit_timing: 0.5, base_recover_duration: 0.6, forward_movement: 3.5, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.5, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 1.2, forward_movement: 4.5, damage_kind: Slashing, diff --git a/assets/common/abilities/custom/basilisk/singlestrike.ron b/assets/common/abilities/custom/basilisk/singlestrike.ron index 3f21ccc88a..65846146d6 100644 --- a/assets/common/abilities/custom/basilisk/singlestrike.ron +++ b/assets/common/abilities/custom/basilisk/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/basilisk/triplestrike.ron b/assets/common/abilities/custom/basilisk/triplestrike.ron index fced56cfda..cd4942d2bb 100644 --- a/assets/common/abilities/custom/basilisk/triplestrike.ron +++ b/assets/common/abilities/custom/basilisk/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 2.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/birdlargebreathe/triplestrike.ron b/assets/common/abilities/custom/birdlargebreathe/triplestrike.ron index eb332435a4..b0a727e645 100644 --- a/assets/common/abilities/custom/birdlargebreathe/triplestrike.ron +++ b/assets/common/abilities/custom/birdlargebreathe/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 2.0, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Slashing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Slashing, diff --git a/assets/common/abilities/custom/birdlargefire/triplestrike.ron b/assets/common/abilities/custom/birdlargefire/triplestrike.ron index eb332435a4..b0a727e645 100644 --- a/assets/common/abilities/custom/birdlargefire/triplestrike.ron +++ b/assets/common/abilities/custom/birdlargefire/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 2.0, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Slashing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Slashing, diff --git a/assets/common/abilities/custom/husk/singlestrike.ron b/assets/common/abilities/custom/husk/singlestrike.ron index 5d4aa94c8b..92e1e2d3eb 100644 --- a/assets/common/abilities/custom/husk/singlestrike.ron +++ b/assets/common/abilities/custom/husk/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.25, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.25, forward_movement: 0.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/husk/triplestrike.ron b/assets/common/abilities/custom/husk/triplestrike.ron index d14f182feb..25e985d3d2 100644 --- a/assets/common/abilities/custom/husk/triplestrike.ron +++ b/assets/common/abilities/custom/husk/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.22, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 0.0, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowbasic/singlestrike.ron b/assets/common/abilities/custom/quadlowbasic/singlestrike.ron index 8f60297e99..c0bd5a47fb 100644 --- a/assets/common/abilities/custom/quadlowbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadlowbasic/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowbasic/triplestrike.ron b/assets/common/abilities/custom/quadlowbasic/triplestrike.ron index 3fae8726f5..f7b49ad5ac 100644 --- a/assets/common/abilities/custom/quadlowbasic/triplestrike.ron +++ b/assets/common/abilities/custom/quadlowbasic/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 2.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron b/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron index 19320571bb..6944d309f3 100644 --- a/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron +++ b/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 2.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowquick/quadstrike.ron b/assets/common/abilities/custom/quadlowquick/quadstrike.ron index 2bd6e570f3..3c7f848f43 100644 --- a/assets/common/abilities/custom/quadlowquick/quadstrike.ron +++ b/assets/common/abilities/custom/quadlowquick/quadstrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.6, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.1, forward_movement: 1.5, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.15, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.1, forward_movement: 0.8, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.1, forward_movement: 0.8, damage_kind: Crushing, @@ -56,6 +59,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.1, forward_movement: 0.8, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowranged/singlestrike.ron b/assets/common/abilities/custom/quadlowranged/singlestrike.ron index 7d36295027..81b81f3cd0 100644 --- a/assets/common/abilities/custom/quadlowranged/singlestrike.ron +++ b/assets/common/abilities/custom/quadlowranged/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowtail/triplestrike.ron b/assets/common/abilities/custom/quadlowtail/triplestrike.ron index a35efe048e..457328d1c0 100644 --- a/assets/common/abilities/custom/quadlowtail/triplestrike.ron +++ b/assets/common/abilities/custom/quadlowtail/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 2.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedbasic/singlestrike.ron b/assets/common/abilities/custom/quadmedbasic/singlestrike.ron index fef9810b42..2dcb7c3eb5 100644 --- a/assets/common/abilities/custom/quadmedbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadmedbasic/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedbasic/triplestrike.ron b/assets/common/abilities/custom/quadmedbasic/triplestrike.ron index 4b8544b345..1a5577da1d 100644 --- a/assets/common/abilities/custom/quadmedbasic/triplestrike.ron +++ b/assets/common/abilities/custom/quadmedbasic/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.45, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 0.0, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.07, + hit_timing: 0.5, base_recover_duration: 0.2, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedcharge/doublestrike.ron b/assets/common/abilities/custom/quadmedcharge/doublestrike.ron index 9ec940c20f..83ba1ae46b 100644 --- a/assets/common/abilities/custom/quadmedcharge/doublestrike.ron +++ b/assets/common/abilities/custom/quadmedcharge/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 0.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedjump/doublestrike.ron b/assets/common/abilities/custom/quadmedjump/doublestrike.ron index 635eb2bfac..d57e0c63df 100644 --- a/assets/common/abilities/custom/quadmedjump/doublestrike.ron +++ b/assets/common/abilities/custom/quadmedjump/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedquick/triplestrike.ron b/assets/common/abilities/custom/quadmedquick/triplestrike.ron index cdf07c952d..596df64343 100644 --- a/assets/common/abilities/custom/quadmedquick/triplestrike.ron +++ b/assets/common/abilities/custom/quadmedquick/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 40.0, base_buildup_duration: 0.6, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 0.3, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 40.0, base_buildup_duration: 0.4, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 0.5, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 40.0, base_buildup_duration: 0.4, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 0.5, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron b/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron index 4ce36a7a0f..0448be9345 100644 --- a/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 50.0, base_buildup_duration: 0.3, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/stonegolemfist/singlestrike.ron b/assets/common/abilities/custom/stonegolemfist/singlestrike.ron index 74d2853a78..0c22b0bdb7 100644 --- a/assets/common/abilities/custom/stonegolemfist/singlestrike.ron +++ b/assets/common/abilities/custom/stonegolemfist/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.9, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.9, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/theropodbasic/singlestrike.ron b/assets/common/abilities/custom/theropodbasic/singlestrike.ron index 17c2f98b28..08cf1209e4 100644 --- a/assets/common/abilities/custom/theropodbasic/singlestrike.ron +++ b/assets/common/abilities/custom/theropodbasic/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 60.0, base_buildup_duration: 0.5, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/theropodbasic/triplestrike.ron b/assets/common/abilities/custom/theropodbasic/triplestrike.ron index 5c19bf5d62..c8766e2bbd 100644 --- a/assets/common/abilities/custom/theropodbasic/triplestrike.ron +++ b/assets/common/abilities/custom/theropodbasic/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.9, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.5, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.15, forward_movement: 1.0, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.35, base_swing_duration: 0.125, + hit_timing: 0.5, base_recover_duration: 0.9, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/theropodbird/singlestrike.ron b/assets/common/abilities/custom/theropodbird/singlestrike.ron index db41b51c21..1401500a4b 100644 --- a/assets/common/abilities/custom/theropodbird/singlestrike.ron +++ b/assets/common/abilities/custom/theropodbird/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.4, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 3.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/theropodbird/triplestrike.ron b/assets/common/abilities/custom/theropodbird/triplestrike.ron index 0ae2472f83..9693aa46d0 100644 --- a/assets/common/abilities/custom/theropodbird/triplestrike.ron +++ b/assets/common/abilities/custom/theropodbird/triplestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.65, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 1.0, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.4, base_swing_duration: 0.15, + hit_timing: 0.5, base_recover_duration: 0.15, forward_movement: 1.0, damage_kind: Crushing, @@ -41,6 +43,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.35, base_swing_duration: 0.125, + hit_timing: 0.5, base_recover_duration: 0.9, forward_movement: 1.0, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/wendigomagic/singlestrike.ron b/assets/common/abilities/custom/wendigomagic/singlestrike.ron index c6fe60fade..39185a761d 100644 --- a/assets/common/abilities/custom/wendigomagic/singlestrike.ron +++ b/assets/common/abilities/custom/wendigomagic/singlestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.6, base_swing_duration: 0.2, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 5.0, damage_kind: Crushing, diff --git a/assets/common/abilities/hammer/singlestrike.ron b/assets/common/abilities/hammer/singlestrike.ron index 5a7657c7ea..85da88b26e 100644 --- a/assets/common/abilities/hammer/singlestrike.ron +++ b/assets/common/abilities/hammer/singlestrike.ron @@ -3,13 +3,14 @@ ComboMelee( stage: 1, base_damage: 150, damage_increase: 10, - base_poise_damage: 25, + base_poise_damage: 20, poise_damage_increase: 0, knockback: 5.0, range: 4.5, angle: 50.0, base_buildup_duration: 0.2, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.45, forward_movement: 0.0, damage_kind: Crushing, diff --git a/assets/common/abilities/hammersimple/doublestrike.ron b/assets/common/abilities/hammersimple/doublestrike.ron index 4bc8e7f3b2..13d7423c1e 100644 --- a/assets/common/abilities/hammersimple/doublestrike.ron +++ b/assets/common/abilities/hammersimple/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 50.0, base_buildup_duration: 0.6, base_swing_duration: 0.08, + hit_timing: 0.5, base_recover_duration: 0.6, forward_movement: 3.5, damage_kind: Crushing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.5, base_swing_duration: 0.25, + hit_timing: 0.5, base_recover_duration: 1.2, forward_movement: 2.0, damage_kind: Crushing, diff --git a/assets/common/abilities/spear/doublestrike.ron b/assets/common/abilities/spear/doublestrike.ron index e7e1c92980..075850a90c 100644 --- a/assets/common/abilities/spear/doublestrike.ron +++ b/assets/common/abilities/spear/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.35, base_swing_duration: 0.075, + hit_timing: 0.5, base_recover_duration: 0.4, forward_movement: 0.7, damage_kind: Piercing, @@ -26,6 +27,7 @@ ComboMelee( angle: 15.0, base_buildup_duration: 0.5, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.5, forward_movement: 0.7, damage_kind: Piercing, diff --git a/assets/common/abilities/sword/triplestrike.ron b/assets/common/abilities/sword/triplestrike.ron index ea05f387d8..b078f9f180 100644 --- a/assets/common/abilities/sword/triplestrike.ron +++ b/assets/common/abilities/sword/triplestrike.ron @@ -9,8 +9,9 @@ ComboMelee( knockback: 1.0, range: 4.0, angle: 30.0, - base_buildup_duration: 0.15, + base_buildup_duration: 0.1, base_swing_duration: 0.075, + hit_timing: 0.5, base_recover_duration: 0.15, forward_movement: 0.5, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 40.0, base_buildup_duration: 0.1, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, forward_movement: 0.0, damage_kind: Slashing, @@ -41,6 +43,7 @@ ComboMelee( angle: 10.0, base_buildup_duration: 0.15, base_swing_duration: 0.1, + hit_timing: 0.2, base_recover_duration: 0.35, forward_movement: 1.2, damage_kind: Piercing, diff --git a/assets/common/abilities/swordsimple/doublestrike.ron b/assets/common/abilities/swordsimple/doublestrike.ron index 5aefeb50a6..f8a9b3860e 100644 --- a/assets/common/abilities/swordsimple/doublestrike.ron +++ b/assets/common/abilities/swordsimple/doublestrike.ron @@ -11,6 +11,7 @@ ComboMelee( angle: 50.0, base_buildup_duration: 0.4, base_swing_duration: 0.08, + hit_timing: 0.5, base_recover_duration: 0.5, forward_movement: 2.5, damage_kind: Slashing, @@ -26,6 +27,7 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.7, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.7, forward_movement: 2.0, damage_kind: Slashing, diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 8ab75b8928..d0eeb5d074 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1383,6 +1383,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { ori_modifier: *ori_modifier as f32, ability_info, }, + exhausted: false, stage: 1, timer: Duration::default(), stage_section: StageSection::Buildup, diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 7ad6b9cb04..27d5c11896 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -62,7 +62,7 @@ pub struct Data { pub stage_section: StageSection, /// Timer for each stage pub timer: Duration, - /// Whether the attack fired already + /// Whether the attack executed already pub exhausted: bool, /// How much the attack charged by pub charge_amount: f32, diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index a59315043a..fd547dfd6a 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -33,6 +33,8 @@ pub struct Stage { /// Duration of stage spent in swing (controls animation stuff, and can also /// be used to handle movement separately to buildup) pub base_swing_duration: T, + /// At what fraction of the swing duration to apply the melee "hit" + pub hit_timing: f32, /// Initial recover duration of stage (how long until character exits state) pub base_recover_duration: T, /// How much forward movement there is in the swing portion of the stage @@ -53,6 +55,7 @@ impl Stage { range: self.range, angle: self.angle, base_buildup_duration: Duration::from_secs_f32(self.base_buildup_duration), + hit_timing: self.hit_timing, base_swing_duration: Duration::from_secs_f32(self.base_swing_duration), base_recover_duration: Duration::from_secs_f32(self.base_recover_duration), forward_movement: self.forward_movement, @@ -112,6 +115,8 @@ pub struct Data { /// Struct containing data that does not change over the course of the /// character state pub static_data: StaticData, + /// Whether the attack was executed already + pub exhausted: bool, /// Indicates what stage the combo is in pub stage: u32, /// Timer for each stage @@ -155,8 +160,24 @@ impl CharacterBehavior for Data { stage_section: StageSection::Swing, ..*self }); + } + }, + StageSection::Swing => { + if self.timer.as_secs_f32() + > self.static_data.stage_data[stage_index].hit_timing + * self.static_data.stage_data[stage_index] + .base_swing_duration + .as_secs_f32() + && !self.exhausted + { + // Swing + update.character = CharacterState::ComboMelee(Data { + static_data: self.static_data.clone(), + timer: tick_attack_or_default(data, self.timer, None), + exhausted: true, + ..*self + }); - // Hit attempt let damage = self.static_data.stage_data[stage_index].base_damage + (self .static_data @@ -177,6 +198,7 @@ impl CharacterBehavior for Data { CombatEffect::Poise(poise), ) .with_requirement(CombatRequirement::AnyDamage); + let knockback = AttackEffect::new( Some(GroupTarget::OutOfGroup), CombatEffect::Knockback(Knockback { @@ -185,13 +207,17 @@ impl CharacterBehavior for Data { }), ) .with_requirement(CombatRequirement::AnyDamage); + let energy = self.static_data.max_energy_gain.min( self.static_data.initial_energy_gain + data.combo.counter() as f32 * self.static_data.energy_increase, ); + let energy = AttackEffect::new(None, CombatEffect::EnergyReward(energy)) .with_requirement(CombatRequirement::AnyDamage); + let buff = CombatEffect::Buff(CombatBuff::default_physical()); + let damage = AttackDamage::new( Damage { source: DamageSource::Melee, @@ -201,8 +227,10 @@ impl CharacterBehavior for Data { Some(GroupTarget::OutOfGroup), ) .with_effect(buff); + let (crit_chance, crit_mult) = get_crit_data(data, self.static_data.ability_info); + let attack = Attack::default() .with_damage(damage) .with_crit(crit_chance, crit_mult) @@ -228,10 +256,8 @@ impl CharacterBehavior for Data { }) .filter(|(_, tool)| tool == &Some(ToolKind::Pick)), }); - } - }, - StageSection::Swing => { - if self.timer < self.static_data.stage_data[stage_index].base_swing_duration { + } else if self.timer < self.static_data.stage_data[stage_index].base_swing_duration + { handle_orientation(data, &mut update, 0.4 * self.static_data.ori_modifier); // Forward movement diff --git a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs index a024d559ea..73df919023 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs @@ -128,6 +128,7 @@ fn matches_ability_stage() { angle: 30.0, base_buildup_duration: Duration::from_millis(500), base_swing_duration: Duration::from_millis(200), + hit_timing: 0.5, base_recover_duration: Duration::from_millis(400), forward_movement: 0.5, damage_kind: DamageKind::Slashing, @@ -142,6 +143,7 @@ fn matches_ability_stage() { ori_modifier: 1.0, ability_info: empty_ability_info(), }, + exhausted: false, stage: 1, timer: Duration::default(), stage_section: states::utils::StageSection::Swing, @@ -187,6 +189,7 @@ fn ignores_different_ability_stage() { angle: 30.0, base_buildup_duration: Duration::from_millis(500), base_swing_duration: Duration::from_millis(200), + hit_timing: 0.5, base_recover_duration: Duration::from_millis(400), forward_movement: 0.5, damage_kind: DamageKind::Slashing, @@ -201,6 +204,7 @@ fn ignores_different_ability_stage() { ori_modifier: 1.0, ability_info: empty_ability_info(), }, + exhausted: false, stage: 1, timer: Duration::default(), stage_section: states::utils::StageSection::Swing, From 8568dfb38e4f792505e854a2d1acf1cc794926c9 Mon Sep 17 00:00:00 2001 From: asumface Date: Mon, 14 Jun 2021 22:03:57 +0200 Subject: [PATCH 03/38] Bump rodio to 0.14 --- CHANGELOG.md | 2 ++ Cargo.lock | 4 ++-- voxygen/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2694d34f41..195158a9c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. +- Updated audio library (rodio 0.13 -> 0.14). ### Removed ### Fixed +- Cases where no audio output could be produced before. ## [0.10.0] - 2021-06-12 diff --git a/Cargo.lock b/Cargo.lock index cff916f047..9733a92724 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4377,9 +4377,9 @@ dependencies = [ [[package]] name = "rodio" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65c2eda643191f6d1bb12ea323a9db8d9ba95374e9be3780b5a9fb5cfb8520f" +checksum = "4d98f5e557b61525057e2bc142c8cd7f0e70d75dc32852309bec440e6e046bf9" dependencies = [ "cpal", "lewton", diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index b84a19dcb5..c322e6a1af 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -95,7 +95,7 @@ num = "0.4" ordered-float = { version = "2.0.1", default-features = false } rand = "0.8" rayon = "1.5" -rodio = {version = "0.13", default-features = false, features = ["vorbis"]} +rodio = {version = "0.14", default-features = false, features = ["vorbis"]} ron = {version = "0.6", default-features = false} serde = {version = "1.0", features = [ "rc", "derive" ]} strum = "0.20" From 6fe3a3c77f8e8b6f4e3e0a9f81324e34ad9e6fec Mon Sep 17 00:00:00 2001 From: Imbris Date: Mon, 14 Jun 2021 21:42:50 -0400 Subject: [PATCH 04/38] Fallback to executable strategy for the userdata rather than panicking when USERDATA_STRATEGY isn't set and the executable is moved out of the project folder --- common/base/src/userdata_dir.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/common/base/src/userdata_dir.rs b/common/base/src/userdata_dir.rs index a038c348d7..e1fe66c479 100644 --- a/common/base/src/userdata_dir.rs +++ b/common/base/src/userdata_dir.rs @@ -1,4 +1,5 @@ use std::path::PathBuf; +use tracing::warn; const VELOREN_USERDATA_ENV: &str = "VELOREN_USERDATA"; @@ -52,14 +53,20 @@ pub fn userdata_dir(workspace: bool, strategy: Option<&str>, manifest_dir: &str) } let exe_path = std::env::current_exe() .expect("Failed to retrieve executable path!"); - // Ensure this path exists - // Ensure that the binary path is prefixed by this path - if !path.exists() || !exe_path.starts_with(&path) { - panic!("Recompile with VELOREN_USERDATA_STRATEGY set to \"system\" or \"executable\" to run the binary outside of the project folder"); + // If this path exists + // and the binary path is prefixed by this path + // put the userdata folder there + if path.exists() && exe_path.starts_with(&path) { + path.push("userdata"); + path + } else { + // otherwise warn and fallback to the executable strategy + warn!("This binary was moved to outside the project folder where it was compiled and was not compiled with VELOREN_USERDATA_STRATEGY set to \"system\" or \"executable\". Falling back the to the \"executable\" strategy (the userdata folder will be placed in the same folder as the executable)"); + let mut path = exe_path; + path.pop(); + path.push("userdata"); + path } - - path.push("userdata"); - path }) } From cbf0a6baf100656ca1fa2279ee0aaff3dc9648cc Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 01:53:00 -0400 Subject: [PATCH 05/38] Fix trying to create a zero sized shadow texture when resizing window down to a small size --- voxygen/src/render/renderer/shadow_map.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/voxygen/src/render/renderer/shadow_map.rs b/voxygen/src/render/renderer/shadow_map.rs index b7361adebe..5832c77ff3 100644 --- a/voxygen/src/render/renderer/shadow_map.rs +++ b/voxygen/src/render/renderer/shadow_map.rs @@ -188,7 +188,9 @@ impl ShadowMap { let diag_two_size = u32::checked_next_power_of_two(diag_size) .filter(|&e| e <= max_texture_size) // Limit to max texture resolution rather than error. - .unwrap_or(max_texture_size); + .unwrap_or(max_texture_size) + // Make sure we don't try to create a zero sized texture (divided by 4 below) + .max(4); let point_shadow_tex = wgpu::TextureDescriptor { label: None, From f3657903c9c5f76d6f87d8af37c61708d7f27396 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 02:02:10 -0400 Subject: [PATCH 06/38] [iced renderer] Skip content processing if the clip intersection is zero sized or invalid --- voxygen/src/ui/ice/renderer/mod.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/voxygen/src/ui/ice/renderer/mod.rs b/voxygen/src/ui/ice/renderer/mod.rs index 83eb8f1320..ae83454051 100644 --- a/voxygen/src/ui/ice/renderer/mod.rs +++ b/voxygen/src/ui/ice/renderer/mod.rs @@ -711,14 +711,9 @@ impl IcedRenderer { if intersection.is_valid() && intersection.size().map(|s| s > 0).reduce_and() { intersection } else { - // Create a aabr with width 1 and height 1, technically - // it would be more correct to do it with 0,0 but that's - // a validation error in wgpu so enjoy your funky pixel - // in the top left of your screen - Aabr { - min: Vec2::zero(), - max: Vec2::one(), - } + // If the intersection is invalid or zero sized we don't need to process + // the content primitive + return; } }; // Not expecting this case: new_scissor == current_scissor @@ -737,8 +732,8 @@ impl IcedRenderer { self.draw_commands.push(DrawCommand::Scissor(new_scissor)); // TODO: support nested clips? - // TODO: if last command is a clip changing back to the default replace it with - // this + // TODO: if previous command was a clip changing back to the default replace it + // with this // TODO: cull primitives outside the current scissor // Renderer child From 97ce50e5d46ee25b90d1b52ec979ee963c33ef1f Mon Sep 17 00:00:00 2001 From: Snowram Date: Wed, 26 May 2021 02:37:31 +0200 Subject: [PATCH 07/38] Add troll variants and roc npcs --- .../common/abilities/ability_set_manifest.ron | 12 ++ .../custom/birdlargebasic/summontornadoes.ron | 13 ++ .../common/abilities/custom/tornado/spin.ron | 19 ++ .../npc_weapons/unique/birdlargebasic.ron | 19 ++ assets/common/npc_names.ron | 18 +- assets/voxygen/shaders/particle-vert.glsl | 11 + .../voxel/biped_large_central_manifest.ron | 138 ++++++++++-- .../voxel/biped_large_lateral_manifest.ron | 196 +++++++++++++++--- .../voxel/bird_large_central_manifest.ron | 52 +++++ .../voxel/bird_large_lateral_manifest.ron | 84 ++++++++ assets/voxygen/voxel/npc/roc/male/beak.vox | 3 + assets/voxygen/voxel/npc/roc/male/chest.vox | 3 + assets/voxygen/voxel/npc/roc/male/foot_r.vox | 3 + assets/voxygen/voxel/npc/roc/male/head.vox | 3 + assets/voxygen/voxel/npc/roc/male/leg_r.vox | 3 + assets/voxygen/voxel/npc/roc/male/neck.vox | 3 + .../voxygen/voxel/npc/roc/male/tail_front.vox | 3 + .../voxygen/voxel/npc/roc/male/tail_rear.vox | 3 + .../voxygen/voxel/npc/roc/male/wing_in_r.vox | 3 + .../voxygen/voxel/npc/roc/male/wing_mid_r.vox | 3 + .../voxygen/voxel/npc/roc/male/wing_out_r.vox | 3 + .../voxygen/voxel/npc/troll/male/foot_l.vox | 3 - .../voxygen/voxel/npc/troll/male/foot_r.vox | 3 - .../voxygen/voxel/npc/troll/male/hand_l.vox | 3 - .../voxygen/voxel/npc/troll/male/hand_r.vox | 3 - assets/voxygen/voxel/npc/troll/male/head.vox | 3 - assets/voxygen/voxel/npc/troll/male/jaw.vox | 3 - assets/voxygen/voxel/npc/troll/male/leg_l.vox | 3 - assets/voxygen/voxel/npc/troll/male/leg_r.vox | 3 - .../voxel/npc/troll/male/shoulder_l.vox | 3 - .../voxel/npc/troll/male/shoulder_r.vox | 3 - .../voxel/npc/troll/male/torso_lower.vox | 3 - .../voxel/npc/troll/male/torso_upper.vox | 3 - .../voxel/npc/troll_cave/male/foot_l.vox | 3 + .../voxel/npc/troll_cave/male/foot_r.vox | 3 + .../voxel/npc/troll_cave/male/hand_l.vox | 3 + .../voxel/npc/troll_cave/male/hand_r.vox | 3 + .../voxel/npc/troll_cave/male/head.vox | 3 + .../voxygen/voxel/npc/troll_cave/male/jaw.vox | 3 + .../voxel/npc/troll_cave/male/leg_l.vox | 3 + .../voxel/npc/troll_cave/male/leg_r.vox | 3 + .../voxel/npc/troll_cave/male/shoulder_l.vox | 3 + .../voxel/npc/troll_cave/male/shoulder_r.vox | 3 + .../voxel/npc/troll_cave/male/torso_lower.vox | 3 + .../voxel/npc/troll_cave/male/torso_upper.vox | 3 + .../voxel/npc/troll_mountain/male/foot_l.vox | 3 + .../voxel/npc/troll_mountain/male/foot_r.vox | 3 + .../voxel/npc/troll_mountain/male/hand_l.vox | 3 + .../voxel/npc/troll_mountain/male/hand_r.vox | 3 + .../voxel/npc/troll_mountain/male/head.vox | 3 + .../voxel/npc/troll_mountain/male/jaw.vox | 3 + .../voxel/npc/troll_mountain/male/leg_l.vox | 3 + .../voxel/npc/troll_mountain/male/leg_r.vox | 3 + .../npc/troll_mountain/male/shoulder_l.vox | 3 + .../npc/troll_mountain/male/shoulder_r.vox | 3 + .../npc/troll_mountain/male/torso_lower.vox | 3 + .../npc/troll_mountain/male/torso_upper.vox | 3 + .../voxel/npc/troll_swamp/male/foot_l.vox | 3 + .../voxel/npc/troll_swamp/male/foot_r.vox | 3 + .../voxel/npc/troll_swamp/male/hand_l.vox | 3 + .../voxel/npc/troll_swamp/male/hand_r.vox | 3 + .../voxel/npc/troll_swamp/male/head.vox | 3 + .../voxel/npc/troll_swamp/male/jaw.vox | 3 + .../voxel/npc/troll_swamp/male/leg_l.vox | 3 + .../voxel/npc/troll_swamp/male/leg_r.vox | 3 + .../voxel/npc/troll_swamp/male/shoulder_l.vox | 3 + .../voxel/npc/troll_swamp/male/shoulder_r.vox | 3 + .../npc/troll_swamp/male/torso_lower.vox | 3 + .../npc/troll_swamp/male/torso_upper.vox | 3 + assets/voxygen/voxel/object_manifest.ron | 10 + common/src/comp/body.rs | 9 +- common/src/comp/body/biped_large.rs | 42 ++-- common/src/comp/body/bird_large.rs | 5 +- common/src/comp/body/object.rs | 6 +- common/src/comp/inventory/loadout_builder.rs | 11 +- server/src/events/entity_manipulation.rs | 6 +- server/src/sys/agent.rs | 40 ++++ voxygen/anim/src/biped_large/mod.rs | 59 +++--- voxygen/anim/src/bird_large/mod.rs | 13 ++ voxygen/src/render/pipelines/particle.rs | 1 + voxygen/src/scene/particle.rs | 23 ++ world/src/layer/mod.rs | 2 +- world/src/layer/wildlife.rs | 5 +- 83 files changed, 832 insertions(+), 139 deletions(-) create mode 100644 assets/common/abilities/custom/birdlargebasic/summontornadoes.ron create mode 100644 assets/common/abilities/custom/tornado/spin.ron create mode 100644 assets/common/items/npc_weapons/unique/birdlargebasic.ron create mode 100644 assets/voxygen/voxel/npc/roc/male/beak.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/chest.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/foot_r.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/head.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/leg_r.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/neck.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/tail_front.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/tail_rear.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/wing_in_r.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/wing_mid_r.vox create mode 100644 assets/voxygen/voxel/npc/roc/male/wing_out_r.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/foot_l.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/foot_r.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/hand_l.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/hand_r.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/head.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/jaw.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/leg_l.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/leg_r.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/shoulder_l.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/shoulder_r.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/torso_lower.vox delete mode 100644 assets/voxygen/voxel/npc/troll/male/torso_upper.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/foot_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/foot_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/hand_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/hand_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/head.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/jaw.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/leg_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/leg_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/shoulder_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/shoulder_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/torso_lower.vox create mode 100644 assets/voxygen/voxel/npc/troll_cave/male/torso_upper.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/foot_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/foot_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/hand_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/hand_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/head.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/jaw.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/leg_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/leg_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/shoulder_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/shoulder_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/torso_lower.vox create mode 100644 assets/voxygen/voxel/npc/troll_mountain/male/torso_upper.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/foot_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/foot_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/hand_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/hand_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/head.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/jaw.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/leg_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/leg_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/shoulder_l.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/shoulder_r.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/torso_lower.vox create mode 100644 assets/voxygen/voxel/npc/troll_swamp/male/torso_upper.vox diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 6a3c4f4f57..7c300eebeb 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -264,6 +264,18 @@ (None, "common.abilities.custom.birdlargefire.fireshockwave"), ], ), + Custom("Bird Large Basic"): ( + primary: "common.abilities.custom.birdlargebreathe.firebomb", + secondary: "common.abilities.custom.birdlargebreathe.triplestrike", + abilities: [ + (None, "common.abilities.custom.birdlargebasic.summontornadoes"), + ], + ), + Custom("Tornado"): ( + primary: "common.abilities.custom.tornado.spin", + secondary: "common.abilities.empty.basic", + abilities: [], + ), Tool(Debug): ( primary: "common.abilities.debug.forwardboost", secondary: "common.abilities.debug.upboost", diff --git a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron new file mode 100644 index 0000000000..9ad07751b4 --- /dev/null +++ b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron @@ -0,0 +1,13 @@ +BasicSummon( + buildup_duration: 0.5, + cast_duration: 1.0, + recover_duration: 0.5, + summon_amount: 6, + summon_info: ( + body: Object(Tornado), + scale: None, + health_scaling: 80, + loadout_config: None, + skillset_config: None, + ), +) \ No newline at end of file diff --git a/assets/common/abilities/custom/tornado/spin.ron b/assets/common/abilities/custom/tornado/spin.ron new file mode 100644 index 0000000000..df0320d9c2 --- /dev/null +++ b/assets/common/abilities/custom/tornado/spin.ron @@ -0,0 +1,19 @@ +SpinMelee( + buildup_duration: 0.2, + swing_duration: 0.6, + recover_duration: 0.2, + base_damage: 70, + base_poise_damage: 25, + knockback: ( strength: 0.0, direction: Away), + range: 3.5, + damage_effect: None, + energy_cost: 100, + is_infinite: true, + movement_behavior: AxeHover, + is_interruptible: false, + forward_speed: 0.0, + num_spins: 1, + specifier: None, + target: Some(OutOfGroup), + damage_kind: Slashing, +) diff --git a/assets/common/items/npc_weapons/unique/birdlargebasic.ron b/assets/common/items/npc_weapons/unique/birdlargebasic.ron new file mode 100644 index 0000000000..a97d610359 --- /dev/null +++ b/assets/common/items/npc_weapons/unique/birdlargebasic.ron @@ -0,0 +1,19 @@ +ItemDef( + name: "Bird Large Basic", + description: "testing123", + kind: Tool(( + kind: Natural, + hands: Two, + stats: Direct(( + equip_time_secs: 0.01, + power: 1.0, + poise_strength: 1.0, + speed: 1.0, + crit_chance: 0.0625, + crit_mult: 1.9142857, + )), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Bird Large Basic")), +) \ No newline at end of file diff --git a/assets/common/npc_names.ron b/assets/common/npc_names.ron index 2729cacb40..ca8be884c0 100644 --- a/assets/common/npc_names.ron +++ b/assets/common/npc_names.ron @@ -826,9 +826,17 @@ keyword: "wendigo", generic: "Wendigo" ), - troll: ( - keyword: "troll", - generic: "Troll" + troll_cave: ( + keyword: "troll_cave", + generic: "Cave Troll" + ), + troll_mountain: ( + keyword: "troll_mountain", + generic: "Mountain Troll" + ), + troll_swamp: ( + keyword: "troll_swamp", + generic: "Swamp Troll" ), dullahan: ( keyword: "dullahan", @@ -1080,6 +1088,10 @@ keyword: "cockatrice", generic: "Cockatrice" ), + roc: ( + keyword: "roc", + generic: "Roc" + ), ) ), quadruped_low: ( diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index a97e1bdffe..bb3cc72945 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -71,6 +71,7 @@ const int BUBBLES = 29; const int WATER = 30; const int ICE_SPIKES = 31; const int DRIP = 32; +const int TORNADO = 33; // meters per second squared (acceleration) const float earth_gravity = 9.807; @@ -543,6 +544,16 @@ void main() { spin_in_axis(vec3(1,0,0),0) ); break; + case TORNADO: + f_reflect = 0.0; // Fire doesn't reflect light, it emits it + attr = Attr( + //vec3(sin(lifetime * 400.0) * 3.0 * percent(), cos(lifetime * 400.0) * 3.0 * percent(), lifetime * 5.0), + spiral_motion(vec3(0, 0, 5), abs(rand0) + abs(rand1) * percent() * 3.0, percent(), 15.0 * abs(rand2), rand3), + vec3((2.5 * (1 - slow_start(0.05)))), + vec4(1.2 + 0.5 * percent(), 1.2 + 0.5 * percent(), 1.2 + 0.5 * percent(), 2.5), + spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9) + ); + break; default: attr = Attr( linear_motion( diff --git a/assets/voxygen/voxel/biped_large_central_manifest.ron b/assets/voxygen/voxel/biped_large_central_manifest.ron index e002711ae2..4d2830c1b2 100644 --- a/assets/voxygen/voxel/biped_large_central_manifest.ron +++ b/assets/voxygen/voxel/biped_large_central_manifest.ron @@ -156,22 +156,22 @@ central: ("armor.empty"), ) ), - (Troll, Male): ( + (Cavetroll, Male): ( head: ( - offset: (-8.0, -8.5, -9.0), - central: ("npc.troll.male.head"), + offset: (-8.0, -8.0, -8.5), + central: ("npc.troll_cave.male.head"), ), torso_upper: ( - offset: (-8.0, -7.5, -11.0), - central: ("npc.troll.male.torso_upper"), + offset: (-8.0, -9.5, -11.0), + central: ("npc.troll_cave.male.torso_upper"), ), torso_lower: ( - offset: (-6.0, -3.5, -5.0), - central: ("npc.troll.male.torso_lower"), + offset: (-7.0, -5.5, -7.0), + central: ("npc.troll_cave.male.torso_lower"), ), jaw: ( - offset: (-4.0, 0.0, -4.5), - central: ("npc.troll.male.jaw"), + offset: (-5.0, 0.0, -4.5), + central: ("npc.troll_cave.male.jaw"), ), tail: ( offset: (0.0, 0.0, 0.0), @@ -182,22 +182,126 @@ central: ("armor.empty"), ) ), - (Troll, Female): ( + (Cavetroll, Female): ( head: ( - offset: (-8.0, -8.5, -9.0), - central: ("npc.troll.male.head"), + offset: (-8.0, -8.0, -8.5), + central: ("npc.troll_cave.male.head"), ), torso_upper: ( - offset: (-8.0, -7.5, -11.0), - central: ("npc.troll.male.torso_upper"), + offset: (-8.0, -9.5, -11.0), + central: ("npc.troll_cave.male.torso_upper"), ), torso_lower: ( - offset: (-6.0, -3.5, -5.0), - central: ("npc.troll.male.torso_lower"), + offset: (-7.0, -5.5, -7.0), + central: ("npc.troll_cave.male.torso_lower"), + ), + jaw: ( + offset: (-5.0, 0.0, -4.5), + central: ("npc.troll_cave.male.jaw"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Mountaintroll, Male): ( + head: ( + offset: (-9.0, -8.5, -10.0), + central: ("npc.troll_mountain.male.head"), + ), + torso_upper: ( + offset: (-9.0, -10.0, -14.5), + central: ("npc.troll_mountain.male.torso_upper"), + ), + torso_lower: ( + offset: (-7.0, -5.5, -7.0), + central: ("npc.troll_mountain.male.torso_lower"), ), jaw: ( offset: (-4.0, 0.0, -4.5), - central: ("npc.troll.male.jaw"), + central: ("npc.troll_mountain.male.jaw"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Mountaintroll, Female): ( + head: ( + offset: (-9.0, -8.5, -10.0), + central: ("npc.troll_mountain.male.head"), + ), + torso_upper: ( + offset: (-9.0, -10.0, -14.5), + central: ("npc.troll_mountain.male.torso_upper"), + ), + torso_lower: ( + offset: (-7.0, -5.5, -7.0), + central: ("npc.troll_mountain.male.torso_lower"), + ), + jaw: ( + offset: (-4.0, 0.0, -4.5), + central: ("npc.troll_mountain.male.jaw"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Swamptroll, Male): ( + head: ( + offset: (-10.0, -6.5, -6.0), + central: ("npc.troll_swamp.male.head"), + ), + torso_upper: ( + offset: (-9.0, -9.0, -12.5), + central: ("npc.troll_swamp.male.torso_upper"), + ), + torso_lower: ( + offset: (-8.0, -6.5, -10.0), + central: ("npc.troll_swamp.male.torso_lower"), + ), + jaw: ( + offset: (-6.0, 0.0, -4.0), + central: ("npc.troll_swamp.male.jaw"), + ), + tail: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + second: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), + (Swamptroll, Female): ( + head: ( + offset: (-10.0, -6.5, -6.0), + central: ("npc.troll_swamp.male.head"), + ), + torso_upper: ( + offset: (-9.0, -9.0, -12.5), + central: ("npc.troll_swamp.male.torso_upper"), + ), + torso_lower: ( + offset: (-8.0, -6.5, -10.0), + central: ("npc.troll_swamp.male.torso_lower"), + ), + jaw: ( + offset: (-6.0, 0.0, -4.0), + central: ("npc.troll_swamp.male.jaw"), ), tail: ( offset: (0.0, 0.0, 0.0), diff --git a/assets/voxygen/voxel/biped_large_lateral_manifest.ron b/assets/voxygen/voxel/biped_large_lateral_manifest.ron index 0ba3855037..8c8f0c046b 100644 --- a/assets/voxygen/voxel/biped_large_lateral_manifest.ron +++ b/assets/voxygen/voxel/biped_large_lateral_manifest.ron @@ -203,72 +203,208 @@ lateral: ("npc.wendigo.male.foot_r"), ), ), - (Troll, Male): ( + (Cavetroll, Male): ( shoulder_l: ( - offset: (-5.0, -4.5, -11.0), - lateral: ("npc.troll.male.shoulder_l"), + offset: (-5.5, -4.0, -8.0), + lateral: ("npc.troll_cave.male.shoulder_l"), ), shoulder_r: ( - offset: (-5.0, -4.5, -11.0), - lateral: ("npc.troll.male.shoulder_r"), + offset: (-5.5, -4.0, -8.0), + lateral: ("npc.troll_cave.male.shoulder_r"), ), hand_l: ( - offset: (-3.5, -4.0, -12.0), - lateral: ("npc.troll.male.hand_l"), + offset: (-4.5, -4.0, -14.0), + lateral: ("npc.troll_cave.male.hand_l"), ), hand_r: ( - offset: (-3.5, -4.0, -12.0), - lateral: ("npc.troll.male.hand_r"), + offset: (-4.5, -4.0, -14.0), + lateral: ("npc.troll_cave.male.hand_r"), ), leg_l: ( - offset: (-3.0, -2.5, -4.5), - lateral: ("npc.troll.male.leg_l"), + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_cave.male.leg_l"), ), leg_r: ( - offset: (-3.0, -2.5, -4.5), - lateral: ("npc.troll.male.leg_r"), + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_cave.male.leg_r"), ), foot_l: ( offset: (-3.0, -5.0, -2.5), - lateral: ("npc.troll.male.foot_l"), + lateral: ("npc.troll_cave.male.foot_l"), ), foot_r: ( offset: (-3.0, -5.0, -2.5), - lateral: ("npc.troll.male.foot_r"), + lateral: ("npc.troll_cave.male.foot_r"), ), ), - (Troll, Female): ( + (Cavetroll, Female): ( shoulder_l: ( - offset: (-5.0, -4.5, -11.0), - lateral: ("npc.troll.male.shoulder_l"), + offset: (-5.5, -4.0, -8.0), + lateral: ("npc.troll_cave.male.shoulder_l"), ), shoulder_r: ( - offset: (-5.0, -4.5, -11.0), - lateral: ("npc.troll.male.shoulder_r"), + offset: (-5.5, -4.0, -8.0), + lateral: ("npc.troll_cave.male.shoulder_r"), ), hand_l: ( - offset: (-3.5, -4.0, -12.0), - lateral: ("npc.troll.male.hand_l"), + offset: (-4.5, -4.0, -14.0), + lateral: ("npc.troll_cave.male.hand_l"), ), hand_r: ( - offset: (-3.5, -4.0, -12.0), - lateral: ("npc.troll.male.hand_r"), + offset: (-4.5, -4.0, -14.0), + lateral: ("npc.troll_cave.male.hand_r"), ), leg_l: ( - offset: (-3.0, -2.5, -4.5), - lateral: ("npc.troll.male.leg_l"), + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_cave.male.leg_l"), ), leg_r: ( - offset: (-3.0, -2.5, -4.5), - lateral: ("npc.troll.male.leg_r"), + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_cave.male.leg_r"), ), foot_l: ( offset: (-3.0, -5.0, -2.5), - lateral: ("npc.troll.male.foot_l"), + lateral: ("npc.troll_cave.male.foot_l"), ), foot_r: ( offset: (-3.0, -5.0, -2.5), - lateral: ("npc.troll.male.foot_r"), + lateral: ("npc.troll_cave.male.foot_r"), + ), + ), + (Mountaintroll, Male): ( + shoulder_l: ( + offset: (-6.5, -5.0, -8.5), + lateral: ("npc.troll_mountain.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-6.5, -5.0, -8.5), + lateral: ("npc.troll_mountain.male.shoulder_r"), + ), + hand_l: ( + offset: (-5.0, -4.5, -14.0), + lateral: ("npc.troll_mountain.male.hand_l"), + ), + hand_r: ( + offset: (-5.0, -4.5, -14.0), + lateral: ("npc.troll_mountain.male.hand_r"), + ), + leg_l: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_mountain.male.leg_l"), + ), + leg_r: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_mountain.male.leg_r"), + ), + foot_l: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_mountain.male.foot_l"), + ), + foot_r: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_mountain.male.foot_r"), + ), + ), + (Mountaintroll, Female): ( + shoulder_l: ( + offset: (-6.5, -5.0, -8.5), + lateral: ("npc.troll_mountain.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-6.5, -5.0, -8.5), + lateral: ("npc.troll_mountain.male.shoulder_r"), + ), + hand_l: ( + offset: (-5.0, -4.5, -14.0), + lateral: ("npc.troll_mountain.male.hand_l"), + ), + hand_r: ( + offset: (-5.0, -4.5, -14.0), + lateral: ("npc.troll_mountain.male.hand_r"), + ), + leg_l: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_mountain.male.leg_l"), + ), + leg_r: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_mountain.male.leg_r"), + ), + foot_l: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_mountain.male.foot_l"), + ), + foot_r: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_mountain.male.foot_r"), + ), + ), + (Swamptroll, Male): ( + shoulder_l: ( + offset: (-5.5, -5.0, -8.5), + lateral: ("npc.troll_swamp.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-5.5, -5.0, -8.5), + lateral: ("npc.troll_swamp.male.shoulder_r"), + ), + hand_l: ( + offset: (-5.0, -4.0, -16.0), + lateral: ("npc.troll_swamp.male.hand_l"), + ), + hand_r: ( + offset: (-5.0, -4.0, -16.0), + lateral: ("npc.troll_swamp.male.hand_r"), + ), + leg_l: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_swamp.male.leg_l"), + ), + leg_r: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_swamp.male.leg_r"), + ), + foot_l: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_swamp.male.foot_l"), + ), + foot_r: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_swamp.male.foot_r"), + ), + ), + (Swamptroll, Female): ( + shoulder_l: ( + offset: (-5.5, -5.0, -8.5), + lateral: ("npc.troll_swamp.male.shoulder_l"), + ), + shoulder_r: ( + offset: (-5.5, -5.0, -8.5), + lateral: ("npc.troll_swamp.male.shoulder_r"), + ), + hand_l: ( + offset: (-5.0, -4.0, -16.0), + lateral: ("npc.troll_swamp.male.hand_l"), + ), + hand_r: ( + offset: (-5.0, -4.0, -16.0), + lateral: ("npc.troll_swamp.male.hand_r"), + ), + leg_l: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_swamp.male.leg_l"), + ), + leg_r: ( + offset: (-3.0, -3.0, -4.0), + lateral: ("npc.troll_swamp.male.leg_r"), + ), + foot_l: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_swamp.male.foot_l"), + ), + foot_r: ( + offset: (-3.0, -5.0, -2.5), + lateral: ("npc.troll_swamp.male.foot_r"), ), ), (Dullahan, Male): ( diff --git a/assets/voxygen/voxel/bird_large_central_manifest.ron b/assets/voxygen/voxel/bird_large_central_manifest.ron index f8a0999986..a706aa79cf 100644 --- a/assets/voxygen/voxel/bird_large_central_manifest.ron +++ b/assets/voxygen/voxel/bird_large_central_manifest.ron @@ -103,4 +103,56 @@ central: ("npc.cockatrice.male.tail_rear"), ) ), + (Roc, Male): ( + head: ( + offset: (-4.5, -4.0, -6.0), + central: ("npc.roc.male.head"), + ), + beak: ( + offset: (-3.5, 0.0, -8.0), + central: ("npc.roc.male.beak"), + ), + neck: ( + offset: (-5.5, 0.0, -10.5), + central: ("npc.roc.male.neck"), + ), + chest: ( + offset: (-8.5, -12.5, -13.5), + central: ("npc.roc.male.chest"), + ), + tail_front: ( + offset: (-4.5, -9.0, -8.0), + central: ("npc.roc.male.tail_front"), + ), + tail_rear: ( + offset: (-9.5, -28.0, -6.0), + central: ("npc.roc.male.tail_rear"), + ) + ), + (Roc, Female): ( + head: ( + offset: (-4.5, -4.0, -6.0), + central: ("npc.roc.male.head"), + ), + beak: ( + offset: (-3.5, 0.0, -8.0), + central: ("npc.roc.male.beak"), + ), + neck: ( + offset: (-5.5, 0.0, -10.5), + central: ("npc.roc.male.neck"), + ), + chest: ( + offset: (-8.5, -12.5, -13.5), + central: ("npc.roc.male.chest"), + ), + tail_front: ( + offset: (-4.5, -9.0, -8.0), + central: ("npc.roc.male.tail_front"), + ), + tail_rear: ( + offset: (-9.5, -28.0, -6.0), + central: ("npc.roc.male.tail_rear"), + ) + ), }) diff --git a/assets/voxygen/voxel/bird_large_lateral_manifest.ron b/assets/voxygen/voxel/bird_large_lateral_manifest.ron index 3eca741462..fadf7ddb56 100644 --- a/assets/voxygen/voxel/bird_large_lateral_manifest.ron +++ b/assets/voxygen/voxel/bird_large_lateral_manifest.ron @@ -167,4 +167,88 @@ lateral: ("npc.cockatrice.male.foot_r"), ) ), + (Roc, Male): ( + wing_in_l: ( + offset: (-13.0, -14.0, -2.5), + lateral: ("npc.roc.male.wing_in_r"), + ), + wing_in_r: ( + offset: (0.0, -14.0, -2.5), + lateral: ("npc.roc.male.wing_in_r"), + ), + wing_mid_l: ( + offset: (-11.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_mid_r"), + ), + wing_mid_r: ( + offset: (0.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_mid_r"), + ), + wing_out_l: ( + offset: (-20.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_out_r"), + ), + wing_out_r: ( + offset: (0.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_out_r"), + ), + leg_l: ( + offset: (-4.0, -5.5, -7.5), + lateral: ("npc.roc.male.leg_r"), + ), + leg_r: ( + offset: (-4.0, -5.5, -7.5), + lateral: ("npc.roc.male.leg_r"), + ), + foot_l: ( + offset: (-5.5, -3.0, -12.0), + lateral: ("npc.roc.male.foot_r"), + ), + foot_r: ( + offset: (-5.5, -3.0, -12.0), + lateral: ("npc.roc.male.foot_r"), + ) + ), + (Roc, Female): ( + wing_in_l: ( + offset: (-13.0, -14.0, -2.5), + lateral: ("npc.roc.male.wing_in_r"), + ), + wing_in_r: ( + offset: (0.0, -14.0, -2.5), + lateral: ("npc.roc.male.wing_in_r"), + ), + wing_mid_l: ( + offset: (-11.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_mid_r"), + ), + wing_mid_r: ( + offset: (0.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_mid_r"), + ), + wing_out_l: ( + offset: (-20.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_out_r"), + ), + wing_out_r: ( + offset: (0.0, -18.0, -2.0), + lateral: ("npc.roc.male.wing_out_r"), + ), + leg_l: ( + offset: (-4.0, -5.5, -7.5), + lateral: ("npc.roc.male.leg_r"), + ), + leg_r: ( + offset: (-4.0, -5.5, -7.5), + lateral: ("npc.roc.male.leg_r"), + ), + foot_l: ( + offset: (-5.5, -3.0, -12.0), + lateral: ("npc.roc.male.foot_r"), + ), + foot_r: ( + offset: (-5.5, -3.0, -12.0), + lateral: ("npc.roc.male.foot_r"), + ) + ), }) \ No newline at end of file diff --git a/assets/voxygen/voxel/npc/roc/male/beak.vox b/assets/voxygen/voxel/npc/roc/male/beak.vox new file mode 100644 index 0000000000..eead2a2c19 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/beak.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:908494c22844e83f749eba053878f7b9a2025dac1ef3645bb5ff7c4c8a319a7a +size 1716 diff --git a/assets/voxygen/voxel/npc/roc/male/chest.vox b/assets/voxygen/voxel/npc/roc/male/chest.vox new file mode 100644 index 0000000000..1f836f255d --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/chest.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0fc31c5cc5ad16914dec01999994d4f3c38caf3d5fc2ef38a09d22ba8f07ce45 +size 16556 diff --git a/assets/voxygen/voxel/npc/roc/male/foot_r.vox b/assets/voxygen/voxel/npc/roc/male/foot_r.vox new file mode 100644 index 0000000000..12e6eacd68 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41697e4f9cf309df975058049be4d2b72cbce83e0e48a30fae5245cd368966fc +size 2216 diff --git a/assets/voxygen/voxel/npc/roc/male/head.vox b/assets/voxygen/voxel/npc/roc/male/head.vox new file mode 100644 index 0000000000..b4949cf1fb --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f5b391704993b817759d13f50f413ca2718c742a542b110fdac48cd27a86d01 +size 3236 diff --git a/assets/voxygen/voxel/npc/roc/male/leg_r.vox b/assets/voxygen/voxel/npc/roc/male/leg_r.vox new file mode 100644 index 0000000000..ec21c11e00 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87045fb82df52741d6b01bdb1712499d58db6ac10a2d1bea5124310219c0e850 +size 3400 diff --git a/assets/voxygen/voxel/npc/roc/male/neck.vox b/assets/voxygen/voxel/npc/roc/male/neck.vox new file mode 100644 index 0000000000..3dce2215a0 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/neck.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d68bc72562b91296c1426ccc4e96df0da1736c257ec5717a1315e9ed3ed9ed38 +size 5768 diff --git a/assets/voxygen/voxel/npc/roc/male/tail_front.vox b/assets/voxygen/voxel/npc/roc/male/tail_front.vox new file mode 100644 index 0000000000..eff23adc77 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/tail_front.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3ca88120eae851fba31dde22cedce0404d207f539d1d3a1515296bcb1c056e1 +size 2240 diff --git a/assets/voxygen/voxel/npc/roc/male/tail_rear.vox b/assets/voxygen/voxel/npc/roc/male/tail_rear.vox new file mode 100644 index 0000000000..c884b56c4f --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/tail_rear.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc3d03630c67f788a46e463d24cdb8734d67157eca5c06ee3a18fb01ad6b5581 +size 3400 diff --git a/assets/voxygen/voxel/npc/roc/male/wing_in_r.vox b/assets/voxygen/voxel/npc/roc/male/wing_in_r.vox new file mode 100644 index 0000000000..a3a212a3e2 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/wing_in_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed3f018074a5c18226bcd857ec549bb1558c2c23b558e7a2b3ca89628ad994b8 +size 3112 diff --git a/assets/voxygen/voxel/npc/roc/male/wing_mid_r.vox b/assets/voxygen/voxel/npc/roc/male/wing_mid_r.vox new file mode 100644 index 0000000000..51239e7c94 --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/wing_mid_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:afa35e2aaf6c7572db4369aae76892583a0b9d0dac95dc145ec938143261cfba +size 3448 diff --git a/assets/voxygen/voxel/npc/roc/male/wing_out_r.vox b/assets/voxygen/voxel/npc/roc/male/wing_out_r.vox new file mode 100644 index 0000000000..bfd0ff77bb --- /dev/null +++ b/assets/voxygen/voxel/npc/roc/male/wing_out_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0bf07f759bff02d4ce47fff3529fb5abc5806a3fc28763132d266cb17036d5b0 +size 4440 diff --git a/assets/voxygen/voxel/npc/troll/male/foot_l.vox b/assets/voxygen/voxel/npc/troll/male/foot_l.vox deleted file mode 100644 index 4d01fb8f7b..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/foot_l.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7841f6c64137537d9339ce27f57530714f7da32a05570abc5a53df5755c733bc -size 1772 diff --git a/assets/voxygen/voxel/npc/troll/male/foot_r.vox b/assets/voxygen/voxel/npc/troll/male/foot_r.vox deleted file mode 100644 index 614e74f9e0..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/foot_r.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dadb50f16c2ecec316b734490125ceec5600dd3814fba5aa7c69b39f3bc6d163 -size 1772 diff --git a/assets/voxygen/voxel/npc/troll/male/hand_l.vox b/assets/voxygen/voxel/npc/troll/male/hand_l.vox deleted file mode 100644 index 0eac100b7e..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/hand_l.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e6499cdfbca1a5fd80a3160709345f486475e531556bbf1967a7597c4adcf493 -size 2912 diff --git a/assets/voxygen/voxel/npc/troll/male/hand_r.vox b/assets/voxygen/voxel/npc/troll/male/hand_r.vox deleted file mode 100644 index 4802cd12a1..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/hand_r.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:65d2871153549e7de7eb336d0d8b2d240c99d97285b44bc80442a37711f85963 -size 2912 diff --git a/assets/voxygen/voxel/npc/troll/male/head.vox b/assets/voxygen/voxel/npc/troll/male/head.vox deleted file mode 100644 index 6d56c9ee94..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/head.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6090d7c9b1b722fe84eabaaa3fd7b0678dd16b35ac59ffa4f997e8d5fdb52253 -size 4704 diff --git a/assets/voxygen/voxel/npc/troll/male/jaw.vox b/assets/voxygen/voxel/npc/troll/male/jaw.vox deleted file mode 100644 index 23ccf45db1..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/jaw.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3560d24655bb6dfc1e54e8f526814f99a43d626af7b3fc5a4404acd0093048aa -size 1940 diff --git a/assets/voxygen/voxel/npc/troll/male/leg_l.vox b/assets/voxygen/voxel/npc/troll/male/leg_l.vox deleted file mode 100644 index e80f022e90..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/leg_l.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dd23c042d142e17a606119472bc52c2e3f773dd83ef8bee9d368baca65320ab6 -size 1664 diff --git a/assets/voxygen/voxel/npc/troll/male/leg_r.vox b/assets/voxygen/voxel/npc/troll/male/leg_r.vox deleted file mode 100644 index 4db2576313..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/leg_r.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:61f27cd9bdf30ac16748abfa090eacc6113097ca73f7e937f9201c40a6bae57a -size 1664 diff --git a/assets/voxygen/voxel/npc/troll/male/shoulder_l.vox b/assets/voxygen/voxel/npc/troll/male/shoulder_l.vox deleted file mode 100644 index f1c7a99037..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/shoulder_l.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:15606bec0680a834eff98efab9a06331c857a2a3dea9f140af3d0dc0ec169af2 -size 2964 diff --git a/assets/voxygen/voxel/npc/troll/male/shoulder_r.vox b/assets/voxygen/voxel/npc/troll/male/shoulder_r.vox deleted file mode 100644 index 535dc664ce..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/shoulder_r.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c111e35534f571b31d72eee59ed8c512c0f9545a55040981f34b473464c51fff -size 2964 diff --git a/assets/voxygen/voxel/npc/troll/male/torso_lower.vox b/assets/voxygen/voxel/npc/troll/male/torso_lower.vox deleted file mode 100644 index e69fe423eb..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/torso_lower.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:04cb99c4a0fe61494d022df4b7a557d00017e3672e3244fe1123a1d6b3b81ac7 -size 2336 diff --git a/assets/voxygen/voxel/npc/troll/male/torso_upper.vox b/assets/voxygen/voxel/npc/troll/male/torso_upper.vox deleted file mode 100644 index 8c900374a4..0000000000 --- a/assets/voxygen/voxel/npc/troll/male/torso_upper.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4abae28c1df2eaaa2b75fec7b3b754f330f159590a66fd89a69387781de22ab7 -size 10424 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/foot_l.vox b/assets/voxygen/voxel/npc/troll_cave/male/foot_l.vox new file mode 100644 index 0000000000..4c97a42ee0 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/foot_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1480dfe9aa192f690217f831b441303546520bf27c9eadc44ef29eb0be3eca60 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/foot_r.vox b/assets/voxygen/voxel/npc/troll_cave/male/foot_r.vox new file mode 100644 index 0000000000..243373daaf --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4beb7a960e88dce016f74c945918135c983980716d30ee60019d683f0b8c1e48 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/hand_l.vox b/assets/voxygen/voxel/npc/troll_cave/male/hand_l.vox new file mode 100644 index 0000000000..18086a74e3 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/hand_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bda1327dced03be263a98b2ddc8d248b6f1df914a3400a48d1b50bb5945557b5 +size 3892 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/hand_r.vox b/assets/voxygen/voxel/npc/troll_cave/male/hand_r.vox new file mode 100644 index 0000000000..770b893669 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/hand_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f2c60a45ffdf3446e5b84f029050a43405b8daffbba6f4cac14d06e089570bf +size 3892 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/head.vox b/assets/voxygen/voxel/npc/troll_cave/male/head.vox new file mode 100644 index 0000000000..6975647fa5 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a7a1c16e75cc3c5422dcbbdabd59516df4a9218559902204226144a38762e2d +size 4860 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/jaw.vox b/assets/voxygen/voxel/npc/troll_cave/male/jaw.vox new file mode 100644 index 0000000000..66b423fb91 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/jaw.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c365c9c309e55d58cc289da3d5451a24f6b9d312ecb9912e97187f0601d5ca5 +size 2032 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/leg_l.vox b/assets/voxygen/voxel/npc/troll_cave/male/leg_l.vox new file mode 100644 index 0000000000..c5ef8c1c31 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/leg_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c8f500708cdba942b42890741d20b275f7b9f5458ae45fac3813882c19b6fa8 +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/leg_r.vox b/assets/voxygen/voxel/npc/troll_cave/male/leg_r.vox new file mode 100644 index 0000000000..7ce31c07a9 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34dfcd5f9ebdfff349942c2533c995e0d07a526374e467acff78dd839a53cf4d +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/shoulder_l.vox b/assets/voxygen/voxel/npc/troll_cave/male/shoulder_l.vox new file mode 100644 index 0000000000..46ad49ff2d --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/shoulder_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d33ab14221792cbb9867b26d80e78db0ed417d704c2bd95b17d14321cbae7d9 +size 3576 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/shoulder_r.vox b/assets/voxygen/voxel/npc/troll_cave/male/shoulder_r.vox new file mode 100644 index 0000000000..7898013ed7 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/shoulder_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c98dced3fb3614e412ea30f321936575d9a98bc547e72740766b8e41d0c4e76 +size 3576 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/torso_lower.vox b/assets/voxygen/voxel/npc/troll_cave/male/torso_lower.vox new file mode 100644 index 0000000000..3b2cc83635 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/torso_lower.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2177e310320e46de10ba867446175bc9d8adc34f4516550b644fdb6c78f66ed +size 3016 diff --git a/assets/voxygen/voxel/npc/troll_cave/male/torso_upper.vox b/assets/voxygen/voxel/npc/troll_cave/male/torso_upper.vox new file mode 100644 index 0000000000..2252446a88 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_cave/male/torso_upper.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:787fa629bcd7cdec77b7e4fc4bdcfa0830b9cdf5644eb2e06d565ff4a9ea3beb +size 12836 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/foot_l.vox b/assets/voxygen/voxel/npc/troll_mountain/male/foot_l.vox new file mode 100644 index 0000000000..3a8d7dbdce --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/foot_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10449f2e7f7a32bd1829c32d2efc600b1fd1a5450aefa70be70818c84eba5522 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/foot_r.vox b/assets/voxygen/voxel/npc/troll_mountain/male/foot_r.vox new file mode 100644 index 0000000000..fb5a17f957 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78d433a27a4a292dd9b67d301e6ce6ea4feb0a41ac4a5f4fdc6c425b4a7543e7 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/hand_l.vox b/assets/voxygen/voxel/npc/troll_mountain/male/hand_l.vox new file mode 100644 index 0000000000..c000331be5 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/hand_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a8fbcde634a5d5c876f649353e456035522144e9fd713dea169d8d20d3b0a9d +size 4380 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/hand_r.vox b/assets/voxygen/voxel/npc/troll_mountain/male/hand_r.vox new file mode 100644 index 0000000000..563acd4fc5 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/hand_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59e83cf8d8cb6241a65ecdc14c84057030f44589ca7557f9a28fff8c17531c87 +size 4380 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/head.vox b/assets/voxygen/voxel/npc/troll_mountain/male/head.vox new file mode 100644 index 0000000000..53ee848827 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0ee0b6c88e917361d7f035895c85ad2193c3e7596251adb98dc69962dacc4c4 +size 5032 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/jaw.vox b/assets/voxygen/voxel/npc/troll_mountain/male/jaw.vox new file mode 100644 index 0000000000..6b43d3a792 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/jaw.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b39d0ae768f6213c4c0446274f7085f484b66e9dbb87bd527147448abcc6df35 +size 1716 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/leg_l.vox b/assets/voxygen/voxel/npc/troll_mountain/male/leg_l.vox new file mode 100644 index 0000000000..5b69dd1ea5 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/leg_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d6dd45bed68be177695948a44fb88f8c87112d1757d10d4cd3b823ba4c2cd2b +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/leg_r.vox b/assets/voxygen/voxel/npc/troll_mountain/male/leg_r.vox new file mode 100644 index 0000000000..fff2e85edd --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3462c6eeb62e0166543725bc65a0745314042c62470fe5774f1b38de5b6166a0 +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_l.vox b/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_l.vox new file mode 100644 index 0000000000..306a820bd7 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4cbe941566ac9ca3959466198f481a1a4073a36b8329bc4eed5c47eac8213b72 +size 4280 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_r.vox b/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_r.vox new file mode 100644 index 0000000000..10ab7e8e53 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/shoulder_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd60e29f9c631a0beeb69950f705df6a110935c413ae3d6aa5d1a94a6a8b97fb +size 4280 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/torso_lower.vox b/assets/voxygen/voxel/npc/troll_mountain/male/torso_lower.vox new file mode 100644 index 0000000000..f5cdbce07c --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/torso_lower.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0658b7b334a1b1a358dbdfdb717c821fbd6e8e860951d4f80a7a964992db4144 +size 3352 diff --git a/assets/voxygen/voxel/npc/troll_mountain/male/torso_upper.vox b/assets/voxygen/voxel/npc/troll_mountain/male/torso_upper.vox new file mode 100644 index 0000000000..dfd42a133a --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_mountain/male/torso_upper.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22e91d9fc7ec260437e2b93758c85cce0c8c4964c6dc449b41dc708b8fac5134 +size 17700 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/foot_l.vox b/assets/voxygen/voxel/npc/troll_swamp/male/foot_l.vox new file mode 100644 index 0000000000..e653439955 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/foot_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7642ee74ae6e7ce95953e5a2d3a8bb943bc5b55f3c0930c995ad2a2655ef5ce9 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/foot_r.vox b/assets/voxygen/voxel/npc/troll_swamp/male/foot_r.vox new file mode 100644 index 0000000000..71fbf5c5be --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/foot_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:324949b49db1d316abe1dbe73ac4b6df9e6ab1010e59ed516716638dfb4b5ae2 +size 1792 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/hand_l.vox b/assets/voxygen/voxel/npc/troll_swamp/male/hand_l.vox new file mode 100644 index 0000000000..86817a9b1d --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/hand_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c27c05d8c56968cc23a2bdfb107f7f7860d64d15a5df7ab1d9c40b5abf562b0 +size 3864 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/hand_r.vox b/assets/voxygen/voxel/npc/troll_swamp/male/hand_r.vox new file mode 100644 index 0000000000..89f24c221d --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/hand_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:257249a24fc89f1005fdaf6c80d9360405178dfff9d3b6c24a597b48d0c14da2 +size 3864 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/head.vox b/assets/voxygen/voxel/npc/troll_swamp/male/head.vox new file mode 100644 index 0000000000..7aa317ed56 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/head.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:652e49b00fab3d180ef9c1afb60f55fd11e139df4be2a498ff59261ae3a571de +size 5000 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/jaw.vox b/assets/voxygen/voxel/npc/troll_swamp/male/jaw.vox new file mode 100644 index 0000000000..817683d5f5 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/jaw.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7cc30f4d2a4a394ed269c27824f7ffea58479bc68ecac24571108324569b088 +size 3240 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/leg_l.vox b/assets/voxygen/voxel/npc/troll_swamp/male/leg_l.vox new file mode 100644 index 0000000000..b20571cd2d --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/leg_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75810c18ae8e9edc2f453cd06e7c53946f70f0f3196f312a29f269d1f7c36653 +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/leg_r.vox b/assets/voxygen/voxel/npc/troll_swamp/male/leg_r.vox new file mode 100644 index 0000000000..06a40b57a4 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/leg_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76a925036b31a3ca58a74ab2340c896c12c36c0e2104ae97df94b99f7bfc6225 +size 1844 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_l.vox b/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_l.vox new file mode 100644 index 0000000000..c18e498017 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_l.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9880df256c9aef3efbdfc8398338d342b37a8872414f1d2a440aeed54cce0348 +size 4216 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_r.vox b/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_r.vox new file mode 100644 index 0000000000..51431710c4 --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/shoulder_r.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6dd821308ff92b0cdb902d3df6d00570bc97164959babf8dfe177e9c75a751b +size 4208 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/torso_lower.vox b/assets/voxygen/voxel/npc/troll_swamp/male/torso_lower.vox new file mode 100644 index 0000000000..33ef62a61a --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/torso_lower.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b28a8af5f99d52a8676be3810ba27c485c26cc4efefafeb80087ff5d168fffb9 +size 4256 diff --git a/assets/voxygen/voxel/npc/troll_swamp/male/torso_upper.vox b/assets/voxygen/voxel/npc/troll_swamp/male/torso_upper.vox new file mode 100644 index 0000000000..9d36e25b7e --- /dev/null +++ b/assets/voxygen/voxel/npc/troll_swamp/male/torso_upper.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a6a36f03d750445c13299892ca9156f2b6da1e582cbb42f0d18bd64b45d49be +size 16488 diff --git a/assets/voxygen/voxel/object_manifest.ron b/assets/voxygen/voxel/object_manifest.ron index 54f85607bb..8f7cfaace4 100644 --- a/assets/voxygen/voxel/object_manifest.ron +++ b/assets/voxygen/voxel/object_manifest.ron @@ -759,4 +759,14 @@ central: ("armor.empty"), ) ), + Tornado: ( + bone0: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ), + bone1: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), }) diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 2cc9582c5a..10854409bb 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -412,6 +412,7 @@ impl Body { Body::BirdLarge(body) => match body.species { bird_large::Species::Cockatrice => 4000, bird_large::Species::Phoenix => 6000, + bird_large::Species::Roc => 5000, }, Body::Humanoid(_) => 750, _ => 1000, @@ -477,7 +478,9 @@ impl Body { biped_large::Species::Ogre => 3200, biped_large::Species::Cyclops => 3200, biped_large::Species::Wendigo => 2800, - biped_large::Species::Troll => 2400, + biped_large::Species::Cavetroll => 2400, + biped_large::Species::Mountaintroll => 2400, + biped_large::Species::Swamptroll => 2400, biped_large::Species::Dullahan => 3000, biped_large::Species::Mindflayer => 12500, biped_large::Species::Tidalwarrior => 16000, @@ -594,7 +597,9 @@ impl Body { biped_large::Species::Ogre => 70, biped_large::Species::Cyclops => 80, biped_large::Species::Wendigo => 80, - biped_large::Species::Troll => 60, + biped_large::Species::Cavetroll => 60, + biped_large::Species::Mountaintroll => 60, + biped_large::Species::Swamptroll => 60, biped_large::Species::Dullahan => 120, biped_large::Species::Yeti => 0, biped_large::Species::Harvester => 80, diff --git a/common/src/comp/body/biped_large.rs b/common/src/comp/body/biped_large.rs index 3891c0a4f8..d152693658 100644 --- a/common/src/comp/body/biped_large.rs +++ b/common/src/comp/body/biped_large.rs @@ -37,19 +37,21 @@ make_case_elim!( Ogre = 0, Cyclops = 1, Wendigo = 2, - Troll = 3, - Dullahan = 4, - Werewolf = 5, - Occultsaurok = 6, - Mightysaurok = 7, - Slysaurok = 8, - Mindflayer = 9, - Minotaur = 10, - Tidalwarrior = 11, - Yeti = 12, - Harvester = 13, - Blueoni = 14, - Redoni = 15, + Cavetroll = 3, + Mountaintroll = 4, + Swamptroll = 5, + Dullahan = 6, + Werewolf = 7, + Occultsaurok = 8, + Mightysaurok = 9, + Slysaurok = 10, + Mindflayer = 11, + Minotaur = 12, + Tidalwarrior = 13, + Yeti = 14, + Harvester = 15, + Blueoni = 16, + Redoni = 17, } ); @@ -61,7 +63,9 @@ pub struct AllSpecies { pub ogre: SpeciesMeta, pub cyclops: SpeciesMeta, pub wendigo: SpeciesMeta, - pub troll: SpeciesMeta, + pub troll_cave: SpeciesMeta, + pub troll_mountain: SpeciesMeta, + pub troll_swamp: SpeciesMeta, pub dullahan: SpeciesMeta, pub werewolf: SpeciesMeta, pub saurok_occult: SpeciesMeta, @@ -85,7 +89,9 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies Species::Ogre => &self.ogre, Species::Cyclops => &self.cyclops, Species::Wendigo => &self.wendigo, - Species::Troll => &self.troll, + Species::Cavetroll => &self.troll_cave, + Species::Mountaintroll => &self.troll_mountain, + Species::Swamptroll => &self.troll_swamp, Species::Dullahan => &self.dullahan, Species::Werewolf => &self.werewolf, Species::Occultsaurok => &self.saurok_occult, @@ -102,11 +108,13 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies } } -pub const ALL_SPECIES: [Species; 16] = [ +pub const ALL_SPECIES: [Species; 18] = [ Species::Ogre, Species::Cyclops, Species::Wendigo, - Species::Troll, + Species::Cavetroll, + Species::Mountaintroll, + Species::Swamptroll, Species::Dullahan, Species::Werewolf, Species::Occultsaurok, diff --git a/common/src/comp/body/bird_large.rs b/common/src/comp/body/bird_large.rs index 629317082a..8d282c0480 100644 --- a/common/src/comp/body/bird_large.rs +++ b/common/src/comp/body/bird_large.rs @@ -36,6 +36,7 @@ make_case_elim!( pub enum Species { Phoenix = 0, Cockatrice = 1, + Roc = 2, } ); @@ -46,6 +47,7 @@ make_case_elim!( pub struct AllSpecies { pub phoenix: SpeciesMeta, pub cockatrice: SpeciesMeta, + pub roc: SpeciesMeta, } impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies { @@ -56,11 +58,12 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies match index { Species::Phoenix => &self.phoenix, Species::Cockatrice => &self.cockatrice, + Species::Roc => &self.roc, } } } -pub const ALL_SPECIES: [Species; 2] = [Species::Phoenix, Species::Cockatrice]; +pub const ALL_SPECIES: [Species; 3] = [Species::Phoenix, Species::Cockatrice, Species::Roc]; impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies { type IntoIter = std::iter::Copied>; diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index 95d8978b9d..2cdb72aaac 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -88,6 +88,7 @@ make_case_elim!( BirdMeat = 73, FishMeat = 74, SmallMeat = 75, + Tornado = 76, } ); @@ -98,7 +99,7 @@ impl Body { } } -pub const ALL_OBJECTS: [Body; 76] = [ +pub const ALL_OBJECTS: [Body; 77] = [ Body::Arrow, Body::Bomb, Body::Scarecrow, @@ -175,6 +176,7 @@ pub const ALL_OBJECTS: [Body; 76] = [ Body::BirdMeat, Body::FishMeat, Body::SmallMeat, + Body::Tornado, ]; impl From for super::Body { @@ -260,6 +262,7 @@ impl Body { Body::BirdMeat => "bird_meat", Body::FishMeat => "fish_meat", Body::SmallMeat => "small_meat", + Body::Tornado => "tornado", } } @@ -357,6 +360,7 @@ impl Body { Body::FishMeat => 10.0, Body::BirdMeat => 10.0, Body::SmallMeat => 10.0, + Body::Tornado => 50.0, }; Mass(m) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index eb8fc67148..6f316f1e62 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -253,7 +253,13 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Ogre, biped_large::BodyType::Female) => Some( Item::new_from_asset_expect("common.items.npc_weapons.staff.ogre_staff"), ), - (biped_large::Species::Troll, _) => Some(Item::new_from_asset_expect( + (biped_large::Species::Cavetroll, _) => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.hammer.troll_hammer", + )), + (biped_large::Species::Mountaintroll, _) => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.hammer.troll_hammer", + )), + (biped_large::Species::Swamptroll, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.hammer.troll_hammer", )), (biped_large::Species::Wendigo, _) => Some(Item::new_from_asset_expect( @@ -320,6 +326,9 @@ fn default_main_tool(body: &Body) -> Item { (bird_large::Species::Phoenix, _) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.birdlargefire", )), + (bird_large::Species::Roc, _) => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.birdlargebasic", + )), }, _ => None, }; diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 15dd48bae3..3ce4a8178f 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -431,7 +431,11 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc biped_large::Species::Wendigo => { "common.loot_tables.creature.biped_large.wendigo" }, - biped_large::Species::Troll => "common.loot_tables.creature.biped_large.troll", + biped_large::Species::Cavetroll + | biped_large::Species::Mountaintroll + | biped_large::Species::Swamptroll => { + "common.loot_tables.creature.biped_large.troll" + }, biped_large::Species::Occultsaurok | biped_large::Species::Mightysaurok | biped_large::Species::Slysaurok => { diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 152e49030b..d22a68604d 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -116,10 +116,12 @@ pub enum Tactic { Mindflayer, BirdLargeBreathe, BirdLargeFire, + BirdLargeBasic, Minotaur, ClayGolem, TidalWarrior, Yeti, + Tornado, } #[derive(SystemData)] @@ -1605,6 +1607,7 @@ impl<'a> AgentData<'a> { "Haniwa Sentry" => Tactic::RotatingTurret, "Bird Large Breathe" => Tactic::BirdLargeBreathe, "Bird Large Fire" => Tactic::BirdLargeFire, + "Bird Large Basic" => Tactic::BirdLargeBasic, "Mindflayer" => Tactic::Mindflayer, "Minotaur" => Tactic::Minotaur, "Clay Golem" => Tactic::ClayGolem, @@ -1833,6 +1836,7 @@ impl<'a> AgentData<'a> { &tgt_data, &read_data, ), + Tactic::Tornado => self.handle_tornado_attack(controller), Tactic::Mindflayer => self.handle_mindflayer_attack( agent, controller, @@ -1855,6 +1859,13 @@ impl<'a> AgentData<'a> { &tgt_data, &read_data, ), + Tactic::BirdLargeBasic => self.handle_birdlarge_basic_attack( + agent, + controller, + &attack_data, + &tgt_data, + &read_data, + ), Tactic::Minotaur => { self.handle_minotaur_attack(agent, controller, &attack_data, &tgt_data, &read_data) }, @@ -2906,6 +2917,12 @@ impl<'a> AgentData<'a> { } } + fn handle_tornado_attack(&self, controller: &mut Controller) { + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } + fn handle_mindflayer_attack( &self, agent: &mut Agent, @@ -3261,6 +3278,29 @@ impl<'a> AgentData<'a> { } } + fn handle_birdlarge_basic_attack( + &self, + agent: &mut Agent, + controller: &mut Controller, + attack_data: &AttackData, + tgt_data: &TargetData, + read_data: &ReadData, + ) { + if can_see_tgt( + &*read_data.terrain, + self.pos, + tgt_data.pos, + attack_data.dist_sqrd, + ) && attack_data.angle < 15.0 + { + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + } else { + agent.target = None; + } + } + fn handle_minotaur_attack( &self, agent: &mut Agent, diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index 0db26fca2b..b16f0cb019 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -210,7 +210,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (1.0, 7.5), (Cyclops, _) => (9.5, 7.5), (Wendigo, _) => (3.0, 7.5), - (Troll, _) => (6.0, 10.0), + (Cavetroll, _) => (9.0, 7.0), + (Mountaintroll, _) => (13.0, 2.0), + (Swamptroll, _) => (11.0, 2.0), (Dullahan, _) => (3.0, 6.0), (Werewolf, _) => (11.5, 1.0), (Occultsaurok, _) => (6.0, 3.5), @@ -228,7 +230,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, _) => (0.0, 0.0), (Cyclops, _) => (-4.5, -6.0), (Wendigo, _) => (0.0, 0.0), - (Troll, _) => (2.0, -4.0), + (Cavetroll, _) => (0.0, -4.0), + (Mountaintroll, _) => (-1.0, -8.0), + (Swamptroll, _) => (-4.0, -4.5), (Dullahan, _) => (0.0, 0.0), (Werewolf, _) => (5.0, -4.5), (Occultsaurok, _) => (1.0, -2.5), @@ -247,7 +251,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (0.0, 28.0), (Cyclops, _) => (-2.0, 31.0), (Wendigo, _) => (-1.0, 29.0), - (Troll, _) => (-1.0, 26.5), + (Cavetroll, _) => (-1.0, 26.5), + (Mountaintroll, _) => (-1.0, 30.5), + (Swamptroll, _) => (-1.0, 28.5), (Dullahan, _) => (0.0, 29.0), (Werewolf, _) => (3.0, 26.0), (Occultsaurok, _) => (3.0, 24.0), @@ -266,7 +272,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (0.0, -6.0), (Cyclops, _) => (1.0, -8.5), (Wendigo, _) => (-1.5, -6.0), - (Troll, _) => (1.0, -10.5), + (Cavetroll, _) => (1.0, -9.5), + (Mountaintroll, _) => (1.0, -13.5), + (Swamptroll, _) => (1.5, -11.5), (Dullahan, _) => (0.0, -6.5), (Werewolf, _) => (1.0, -10.0), (Occultsaurok, _) => (0.0, -5.0), @@ -293,7 +301,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (8.0, 0.5, 2.0), (Cyclops, _) => (15.0, 3.5, 1.5), (Wendigo, _) => (9.0, 0.5, 2.5), - (Troll, _) => (11.0, 0.5, 4.5), + (Cavetroll, _) => (13.0, 0.0, 0.5), + (Mountaintroll, _) => (14.0, -0.5, -2.0), + (Swamptroll, _) => (14.0, 0.0, 0.0), (Dullahan, _) => (14.0, 0.5, 3.5), (Werewolf, _) => (9.0, 4.0, -3.0), (Occultsaurok, _) => (7.5, 1.0, 1.5), @@ -312,7 +322,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (9.0, 0.5, -4.5), (Cyclops, _) => (14.0, 2.0, -5.5), (Wendigo, _) => (12.0, 0.0, -3.5), - (Troll, _) => (11.5, 0.0, -5.5), + (Cavetroll, _) => (13.5, 1.0, -6.0), + (Mountaintroll, _) => (13.5, 0.0, -10.0), + (Swamptroll, _) => (17.0, 1.0, -8.0), (Dullahan, _) => (14.5, 0.0, -2.5), (Werewolf, _) => (10.0, 2.5, -11.0), (Occultsaurok, _) => (8.0, 1.5, -5.5), @@ -331,7 +343,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (0.0, 0.0, -2.0), (Cyclops, _) => (4.5, 1.0, -8.5), (Wendigo, _) => (2.0, 2.0, -2.5), - (Troll, _) => (5.0, 0.0, -6.0), + (Cavetroll, _) => (4.5, -1.0, -7.5), + (Mountaintroll, _) => (3.5, 0.0, -7.5), + (Swamptroll, _) => (4.5, -0.5, -7.5), (Dullahan, _) => (0.0, 0.0, -5.0), (Werewolf, _) => (4.5, 1.0, -5.0), (Occultsaurok, _) => (3.0, 0.5, -4.0), @@ -350,7 +364,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (4.0, 0.5, -13.5), (Cyclops, _) => (6.0, 3.5, -15.5), (Wendigo, _) => (5.0, 2.5, -17.0), - (Troll, _) => (6.0, 1.5, -13.0), + (Cavetroll, _) => (5.5, 0.0, -14.0), + (Mountaintroll, _) => (4.5, 1.0, -14.0), + (Swamptroll, _) => (5.5, 0.0, -14.0), (Dullahan, _) => (4.0, 2.5, -14.0), (Werewolf, _) => (5.5, 3.0, -6.5), (Occultsaurok, _) => (3.5, 3.5, -10.0), @@ -369,7 +385,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => 1.12, (Cyclops, _) => 1.28, (Wendigo, _) => 1.1, - (Troll, _) => 1.1, + (Cavetroll, _) => 1.1, + (Mountaintroll, _) => 1.1, + (Swamptroll, _) => 1.1, (Dullahan, _) => 1.12, (Werewolf, _) => 1.0, (Occultsaurok, _) => 1.0, @@ -387,7 +405,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Male) => 0.9, (Ogre, Female) => 0.9, (Cyclops, _) => 0.8, - (Troll, _) => 0.9, + (Cavetroll, _) => 0.9, + (Mountaintroll, _) => 0.9, + (Swamptroll, _) => 0.9, (Dullahan, _) => 0.8, (Minotaur, _) => 0.8, _ => 1.0, @@ -397,7 +417,9 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Ogre, Female) => (8.0, 0.0), (Cyclops, _) => (12.0, 0.0), (Wendigo, _) => (15.0, 0.0), - (Troll, _) => (12.0, 0.0), + (Cavetroll, _) => (13.0, 1.5), + (Mountaintroll, _) => (13.0, 1.5), + (Swamptroll, _) => (13.0, 1.5), (Dullahan, _) => (15.0, 0.0), (Werewolf, _) => (13.0, 0.0), (Occultsaurok, _) => (10.0, 0.0), @@ -428,29 +450,14 @@ impl<'a> From<&'a Body> for SkeletonAttr { }, hhl: match (body.species, body.body_type) { (Ogre, Male) => (-9.0, -10.0, 23.0, 1.57, -0.57, 0.0), - (Cyclops, _) => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), - (Troll, _) => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), - (Yeti, _) => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), - (Blueoni, _) => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), - (Redoni, _) => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), _ => (-6.0, -10.0, 17.0, 1.57, -0.57, 0.0), }, hhr: match (body.species, body.body_type) { (Ogre, Male) => (-5.0, -13.0, 0.0, 1.57, -0.57, 0.0), - (Cyclops, _) => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), - (Troll, _) => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), - (Yeti, _) => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), - (Blueoni, _) => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), - (Redoni, _) => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), _ => (-6.0, -10.0, 0.0, 1.57, -0.57, 0.0), }, hc: match (body.species, body.body_type) { (Ogre, Male) => (11.5, 9.0, -13.0, -0.57, -1.57, 1.0), - (Cyclops, _) => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), - (Troll, _) => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), - (Yeti, _) => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), - (Blueoni, _) => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), - (Redoni, _) => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), _ => (8.5, 6.0, -12.0, -0.57, -1.57, 1.0), }, sthl: match (body.species, body.body_type) { diff --git a/voxygen/anim/src/bird_large/mod.rs b/voxygen/anim/src/bird_large/mod.rs index 55ac715d38..1c4c83663e 100644 --- a/voxygen/anim/src/bird_large/mod.rs +++ b/voxygen/anim/src/bird_large/mod.rs @@ -149,54 +149,67 @@ impl<'a> From<&'a Body> for SkeletonAttr { chest: match (body.species, body.body_type) { (Phoenix, _) => (2.5, 16.0), (Cockatrice, _) => (2.5, 16.0), + (Roc, _) => (2.5, 27.5), }, neck: match (body.species, body.body_type) { (Phoenix, _) => (2.5, -5.5), (Cockatrice, _) => (5.0, -1.5), + (Roc, _) => (9.5, -1.5), }, head: match (body.species, body.body_type) { (Phoenix, _) => (6.0, 12.0), (Cockatrice, _) => (8.0, 4.5), + (Roc, _) => (17.0, -3.5), }, beak: match (body.species, body.body_type) { (Phoenix, _) => (5.0, 3.0), (Cockatrice, _) => (2.0, -3.0), + (Roc, _) => (0.0, -3.0), }, tail_front: match (body.species, body.body_type) { (Phoenix, _) => (-9.5, -1.0), (Cockatrice, _) => (-5.0, -2.5), + (Roc, _) => (-7.5, -3.5), }, tail_rear: match (body.species, body.body_type) { (Phoenix, _) => (-11.0, 0.0), (Cockatrice, _) => (-8.0, -3.0), + (Roc, _) => (-8.0, -3.0), }, wing_in: match (body.species, body.body_type) { (Phoenix, _) => (3.0, 2.5, 2.0), (Cockatrice, _) => (3.5, 7.0, 3.5), + (Roc, _) => (5.5, 7.5, -1.0), }, wing_mid: match (body.species, body.body_type) { (Phoenix, _) => (10.0, 1.0, 0.0), (Cockatrice, _) => (6.0, 0.0, 0.0), + (Roc, _) => (12.0, 1.0, -0.5), }, wing_out: match (body.species, body.body_type) { (Phoenix, _) => (7.0, 2.0, 1.5), (Cockatrice, _) => (4.0, -1.0, 1.0), + (Roc, _) => (10.0, -2.0, 0.0), }, leg: match (body.species, body.body_type) { (Phoenix, _) => (4.0, 1.5, 12.0), (Cockatrice, _) => (3.5, 2.5, 13.0), + (Roc, _) => (5.5, -1.5, 17.5), }, foot: match (body.species, body.body_type) { (Phoenix, _) => (0.5, -0.5, -2.5), (Cockatrice, _) => (0.5, -3.0, -3.0), + (Roc, _) => (2.5, -2.5, -5.5), }, scaler: match (body.species, body.body_type) { (Phoenix, _) => (1.0), (Cockatrice, _) => (1.0), + (Roc, _) => (1.0), }, feed: match (body.species, body.body_type) { (Phoenix, _) => (-0.65), (Cockatrice, _) => (-0.5), + (Roc, _) => (-0.65), }, } } diff --git a/voxygen/src/render/pipelines/particle.rs b/voxygen/src/render/pipelines/particle.rs index 947c861695..381ff30cd2 100644 --- a/voxygen/src/render/pipelines/particle.rs +++ b/voxygen/src/render/pipelines/particle.rs @@ -83,6 +83,7 @@ pub enum ParticleMode { Water = 30, IceSpikes = 31, Drip = 32, + Tornado = 33, } impl ParticleMode { diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 33452ec101..d21640e0d6 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -323,6 +323,9 @@ impl ParticleMgr { Body::Object(object::Body::BoltNature) => { self.maintain_boltnature_particles(scene_data, pos, vel) }, + Body::Object(object::Body::Tornado) => { + self.maintain_tornado_particles(scene_data, pos, vel) + }, Body::Object( object::Body::Bomb | object::Body::FireworkBlue @@ -498,6 +501,26 @@ impl ParticleMgr { ); } + fn maintain_tornado_particles(&mut self, scene_data: &SceneData, pos: &Pos, vel: Option<&Vel>) { + let time = scene_data.state.get_time(); + let dt = scene_data.state.get_delta_time(); + let mut rng = thread_rng(); + + // nature + self.particles.resize_with( + self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(5))), + || { + Particle::new( + Duration::from_millis(1000), + time, + ParticleMode::Tornado, + pos.0.map(|e| e + rng.gen_range(-0.25..0.25)) + + vel.map_or(Vec3::zero(), |v| -v.0 * dt * rng.gen::()), + ) + }, + ); + } + fn maintain_bomb_particles(&mut self, scene_data: &SceneData, pos: &Pos, vel: Option<&Vel>) { span!( _guard, diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 9c12b4c130..bff44eb784 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -489,7 +489,7 @@ pub fn apply_caves_supplement<'a>( 0 => comp::biped_large::Species::Blueoni, _ => comp::biped_large::Species::Redoni, }, - _ => comp::biped_large::Species::Troll, + _ => comp::biped_large::Species::Cavetroll, }; comp::biped_large::Body::random_with(dynamic_rng, &species) .into() diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index f15ec3a9bc..5c240d1235 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -454,9 +454,8 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( 0 => { biped_large::Body::random_with(rng, &biped_large::Species::Ogre).into() }, - 1 => { - biped_large::Body::random_with(rng, &biped_large::Species::Troll).into() - }, + 1 => biped_large::Body::random_with(rng, &biped_large::Species::Swamptroll) + .into(), _ => biped_large::Body::random_with(rng, &biped_large::Species::Cyclops) .into(), }) From 3ba0500b90c7fcb35fd356a09e5db7ec7bf68ba5 Mon Sep 17 00:00:00 2001 From: Snowram Date: Fri, 4 Jun 2021 00:42:50 +0200 Subject: [PATCH 08/38] Tornado summoning attack --- .../custom/birdlargebasic/summontornadoes.ron | 7 +- .../custom/mindflayer/summonminions.ron | 4 +- .../abilities/custom/tidalwarrior/totem.ron | 4 +- .../common/abilities/custom/tornado/spin.ron | 14 ++-- .../items/npc_weapons/unique/tornado.ron | 19 +++++ common/src/comp/ability.rs | 6 ++ common/src/comp/inventory/loadout_builder.rs | 3 + common/src/event.rs | 3 +- common/src/states/basic_summon.rs | 77 +++++++++++++++++-- common/src/states/behavior.rs | 4 + common/systems/src/character_behavior.rs | 7 +- server/src/cmd.rs | 4 +- server/src/events/entity_creation.rs | 9 ++- server/src/events/mod.rs | 2 + server/src/rtsim/tick.rs | 3 +- server/src/state_ext.rs | 6 +- server/src/sys/terrain.rs | 3 +- voxygen/anim/src/bird_large/mod.rs | 2 +- 18 files changed, 147 insertions(+), 30 deletions(-) create mode 100644 assets/common/items/npc_weapons/unique/tornado.ron diff --git a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron index 9ad07751b4..21c90ae04a 100644 --- a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron +++ b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron @@ -3,11 +3,16 @@ BasicSummon( cast_duration: 1.0, recover_duration: 0.5, summon_amount: 6, + summon_distance: (1, 5), summon_info: ( body: Object(Tornado), scale: None, - health_scaling: 80, + health_scaling: None, loadout_config: None, skillset_config: None, ), + duration: Some(( + secs: 5, + nanos: 0, + )), ) \ No newline at end of file diff --git a/assets/common/abilities/custom/mindflayer/summonminions.ron b/assets/common/abilities/custom/mindflayer/summonminions.ron index 99117ce2c0..7fdfb01ec1 100644 --- a/assets/common/abilities/custom/mindflayer/summonminions.ron +++ b/assets/common/abilities/custom/mindflayer/summonminions.ron @@ -3,14 +3,16 @@ BasicSummon( cast_duration: 1.0, recover_duration: 0.5, summon_amount: 6, + summon_distance: (3, 3), summon_info: ( body: BipedSmall(( species: Husk, body_type: Male, )), scale: None, - health_scaling: 80, + health_scaling: Some(80), loadout_config: Some(HuskSummon), skillset_config: None, ), + duration: None, ) diff --git a/assets/common/abilities/custom/tidalwarrior/totem.ron b/assets/common/abilities/custom/tidalwarrior/totem.ron index 31df64a7cd..c997b8954c 100644 --- a/assets/common/abilities/custom/tidalwarrior/totem.ron +++ b/assets/common/abilities/custom/tidalwarrior/totem.ron @@ -3,11 +3,13 @@ BasicSummon( cast_duration: 1.0, recover_duration: 0.5, summon_amount: 1, + summon_distance: (1, 1), summon_info: ( body: Object(SeaLantern), scale: None, - health_scaling: 0, + health_scaling: Some(0), loadout_config: None, skillset_config: None, ), + duration: None, ) diff --git a/assets/common/abilities/custom/tornado/spin.ron b/assets/common/abilities/custom/tornado/spin.ron index df0320d9c2..de072c981e 100644 --- a/assets/common/abilities/custom/tornado/spin.ron +++ b/assets/common/abilities/custom/tornado/spin.ron @@ -1,13 +1,13 @@ SpinMelee( - buildup_duration: 0.2, - swing_duration: 0.6, - recover_duration: 0.2, - base_damage: 70, - base_poise_damage: 25, - knockback: ( strength: 0.0, direction: Away), + buildup_duration: 0.0, + swing_duration: 0.5, + recover_duration: 0.0, + base_damage: 15000, + base_poise_damage: 200, + knockback: ( strength: 200.0, direction: Away), range: 3.5, damage_effect: None, - energy_cost: 100, + energy_cost: 0, is_infinite: true, movement_behavior: AxeHover, is_interruptible: false, diff --git a/assets/common/items/npc_weapons/unique/tornado.ron b/assets/common/items/npc_weapons/unique/tornado.ron new file mode 100644 index 0000000000..14c1054395 --- /dev/null +++ b/assets/common/items/npc_weapons/unique/tornado.ron @@ -0,0 +1,19 @@ +ItemDef( + name: "Tornado", + description: "Tornado weapon", + kind: Tool(( + kind: Natural, + hands: Two, + stats: Direct(( + equip_time_secs: 0.01, + power: 1.0, + poise_strength: 1.0, + speed: 1.0, + crit_chance: 0.0, + crit_mult: 0.0, + )), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Tornado")), +) \ No newline at end of file diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index d224061a8e..f755fe58d5 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -280,7 +280,9 @@ pub enum CharacterAbility { cast_duration: f32, recover_duration: f32, summon_amount: u32, + summon_distance: (f32, f32), summon_info: basic_summon::SummonInfo, + duration: Option, }, SelfBuff { buildup_duration: f32, @@ -1737,15 +1739,19 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { cast_duration, recover_duration, summon_amount, + summon_distance, summon_info, + duration, } => CharacterState::BasicSummon(basic_summon::Data { static_data: basic_summon::StaticData { buildup_duration: Duration::from_secs_f32(*buildup_duration), cast_duration: Duration::from_secs_f32(*cast_duration), recover_duration: Duration::from_secs_f32(*recover_duration), summon_amount: *summon_amount, + summon_distance: *summon_distance, summon_info: *summon_info, ability_info, + duration: *duration, }, summon_count: 0, timer: Duration::default(), diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 6f316f1e62..1978cf3fd9 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -306,6 +306,9 @@ fn default_main_tool(body: &Body) -> Item { object::Body::SeaLantern => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.tidal_totem", )), + object::Body::Tornado => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.tornado", + )), _ => None, }, Body::BipedSmall(biped_small) => match (biped_small.species, biped_small.body_type) { diff --git a/common/src/event.rs b/common/src/event.rs index d21eee05c5..ffa178fdd4 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -117,7 +117,7 @@ pub enum ServerEvent { pos: comp::Pos, stats: comp::Stats, skill_set: comp::SkillSet, - health: comp::Health, + health: Option, poise: comp::Poise, loadout: comp::inventory::loadout::Loadout, body: comp::Body, @@ -127,6 +127,7 @@ pub enum ServerEvent { home_chunk: Option, drop_item: Option, rtsim_entity: Option, + projectile: Option, }, CreateShip { pos: comp::Pos, diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 6a49e33c60..44c71b9f47 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -2,7 +2,7 @@ use crate::{ comp::{ self, inventory::loadout_builder::{self, LoadoutBuilder}, - Behavior, BehaviorCapability, CharacterState, StateUpdate, + Behavior, BehaviorCapability, CharacterState, Projectile, StateUpdate, }, event::{LocalEvent, ServerEvent}, outcome::Outcome, @@ -11,9 +11,13 @@ use crate::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, + terrain::Block, + vol::ReadVol, }; +use rand::Rng; use serde::{Deserialize, Serialize}; -use std::time::Duration; +use std::{f32::consts::PI, time::Duration}; +use vek::*; /// Separated out to condense update portions of character state #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -26,10 +30,14 @@ pub struct StaticData { pub recover_duration: Duration, /// How many creatures the state should summon pub summon_amount: u32, + /// Range of the summons relative to the summonner + pub summon_distance: (f32, f32), /// Information about the summoned creature pub summon_info: SummonInfo, /// Miscellaneous information about the ability pub ability_info: AbilityInfo, + /// Duration of the summoned entity + pub duration: Option, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -102,15 +110,67 @@ impl CharacterBehavior for Data { }; let stats = comp::Stats::new("Summon".to_string()); + + let health_scaling = self + .static_data + .summon_info + .health_scaling + .map(|health_scaling| comp::Health::new(body, health_scaling)); + + // Ray cast to check where summon should happen + let summon_frac = + self.summon_count as f32 / self.static_data.summon_amount as f32; + + let length = rand::thread_rng().gen_range( + self.static_data.summon_distance.0..self.static_data.summon_distance.1, + ); + + // Summon in a clockwise fashion + let ray_vector = Vec3::new( + (summon_frac * 2.0 * PI).sin() * length, + (summon_frac * 2.0 * PI).cos() * length, + data.body.eye_height(), + ); + + // Check for collision on the xy plane + let obstacle_xy = data + .terrain + .ray(data.pos.0, data.pos.0 + length * ray_vector) + .until(Block::is_solid) + .cast() + .0; + + let collision_vector = Vec3::new( + data.pos.0.x + (summon_frac * 2.0 * PI).sin() * obstacle_xy, + data.pos.0.y + (summon_frac * 2.0 * PI).cos() * obstacle_xy, + data.pos.0.z, + ); + + // Check for collision in z up to 50 blocks + let obstacle_z = data + .terrain + .ray(collision_vector, collision_vector - Vec3::unit_z() * 50.0) + .until(Block::is_solid) + .cast() + .0; + + // If a duration is specified, create a projectile componenent for the npc + let projectile = self.static_data.duration.map(|duration| Projectile { + hit_solid: Vec::new(), + hit_entity: Vec::new(), + time_left: duration, + owner: Some(*data.uid), + ignore_group: true, + is_sticky: false, + is_point: false, + }); + // Send server event to create npc update.server_events.push_front(ServerEvent::CreateNpc { - pos: *data.pos, + pos: comp::Pos(collision_vector - Vec3::unit_z() * obstacle_z), stats, skill_set, - health: comp::Health::new( - body, - self.static_data.summon_info.health_scaling, - ), + health: health_scaling, poise: comp::Poise::new(body), loadout, body, @@ -129,6 +189,7 @@ impl CharacterBehavior for Data { home_chunk: None, drop_item: None, rtsim_entity: None, + projectile, }); // Send local event used for frontend shenanigans @@ -186,7 +247,7 @@ impl CharacterBehavior for Data { pub struct SummonInfo { body: comp::Body, scale: Option, - health_scaling: u16, + health_scaling: Option, // TODO: use assets for specifying skills and loadout? loadout_config: Option, skillset_config: Option, diff --git a/common/src/states/behavior.rs b/common/src/states/behavior.rs index a1fea34724..d66c827b9a 100644 --- a/common/src/states/behavior.rs +++ b/common/src/states/behavior.rs @@ -5,6 +5,7 @@ use crate::{ InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, SkillSet, StateUpdate, Stats, Vel, }, resources::DeltaTime, + terrain::TerrainGrid, uid::Uid, }; use specs::{ @@ -96,6 +97,7 @@ pub struct JoinData<'a> { pub msm: &'a MaterialStatManifest, pub combo: &'a Combo, pub alignment: Option<&'a comp::Alignment>, + pub terrain: &'a TerrainGrid, } type RestrictedMut<'a, C> = PairedStorage< @@ -128,6 +130,7 @@ pub struct JoinStruct<'a> { pub skill_set: &'a SkillSet, pub combo: &'a Combo, pub alignment: Option<&'a comp::Alignment>, + pub terrain: &'a TerrainGrid, } impl<'a> JoinData<'a> { @@ -161,6 +164,7 @@ impl<'a> JoinData<'a> { msm, combo: j.combo, alignment: j.alignment, + terrain: j.terrain, } } } diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index e7db1e0b36..382067f7d7 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -1,6 +1,6 @@ use specs::{ - shred::ResourceId, Entities, Join, LazyUpdate, Read, ReadStorage, SystemData, World, Write, - WriteStorage, + shred::ResourceId, Entities, Join, LazyUpdate, Read, ReadExpect, ReadStorage, SystemData, + World, Write, WriteStorage, }; use common::{ @@ -16,6 +16,7 @@ use common::{ self, behavior::{CharacterBehavior, JoinData, JoinStruct}, }, + terrain::TerrainGrid, uid::Uid, }; use common_ecs::{Job, Origin, Phase, System}; @@ -67,6 +68,7 @@ pub struct ReadData<'a> { msm: Read<'a, MaterialStatManifest>, combos: ReadStorage<'a, Combo>, alignments: ReadStorage<'a, comp::Alignment>, + terrain: ReadExpect<'a, TerrainGrid>, } /// ## Character Behavior System @@ -280,6 +282,7 @@ impl<'a> System<'a> for Sys { skill_set: &skill_set, combo: &combo, alignment: read_data.alignments.get(entity), + terrain: &read_data.terrain, }; for action in actions { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index b8d56c4fbb..fcdd197e1a 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1014,7 +1014,7 @@ fn handle_spawn( pos, comp::Stats::new(get_npc_name(id, npc::BodyType::from_body(body))), comp::SkillSet::default(), - comp::Health::new(body, 1), + Some(comp::Health::new(body, 1)), comp::Poise::new(body), inventory, body, @@ -1116,7 +1116,7 @@ fn handle_spawn_training_dummy( pos, stats, skill_set, - health, + Some(health), poise, Inventory::new_empty(), body, diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index c66317cf0e..014ed849a7 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -53,7 +53,7 @@ pub fn handle_create_npc( pos: Pos, stats: Stats, skill_set: SkillSet, - health: Health, + health: Option, poise: Poise, loadout: Loadout, body: Body, @@ -63,6 +63,7 @@ pub fn handle_create_npc( drop_item: Option, home_chunk: Option, rtsim_entity: Option, + projectile: Option, ) { let inventory = Inventory::new_with_loadout(loadout); @@ -96,6 +97,12 @@ pub fn handle_create_npc( entity }; + let entity = if let Some(projectile) = projectile { + entity.with(projectile) + } else { + entity + }; + let new_entity = entity.build(); // Add to group system if a pet diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 7079b3bda3..7ab52e6b60 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -147,6 +147,7 @@ impl Server { home_chunk, drop_item, rtsim_entity, + projectile, } => handle_create_npc( self, pos, @@ -162,6 +163,7 @@ impl Server { drop_item, home_chunk, rtsim_entity, + projectile, ), ServerEvent::CreateShip { pos, diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index c729445fa9..70f0608f61 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -127,7 +127,7 @@ impl<'a> System<'a> for Sys { pos: comp::Pos(spawn_pos), stats: comp::Stats::new(entity.get_name()), skill_set: comp::SkillSet::default(), - health: comp::Health::new(body, 10), + health: Some(comp::Health::new(body, 10)), loadout: match body { comp::Body::Humanoid(_) => entity.get_loadout(), _ => LoadoutBuilder::new().build(), @@ -146,6 +146,7 @@ impl<'a> System<'a> for Sys { drop_item: None, home_chunk: None, rtsim_entity, + projectile: None, }, }; server_emitter.emit(event); diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index a929d8224d..339ec7b921 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -39,7 +39,7 @@ pub trait StateExt { pos: comp::Pos, stats: comp::Stats, skill_set: comp::SkillSet, - health: comp::Health, + health: Option, poise: comp::Poise, inventory: comp::Inventory, body: comp::Body, @@ -175,7 +175,7 @@ impl StateExt for State { pos: comp::Pos, stats: comp::Stats, skill_set: comp::SkillSet, - health: comp::Health, + health: Option, poise: comp::Poise, inventory: comp::Inventory, body: comp::Body, @@ -215,7 +215,7 @@ impl StateExt for State { )) .with(stats) .with(skill_set) - .with(health) + .maybe_with(health) .with(poise) .with(comp::Alignment::Npc) .with(comp::CharacterState::default()) diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 458c710b28..eb668c7690 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -239,7 +239,7 @@ impl<'a> System<'a> for Sys { loadout_builder.build() }; - let health = comp::Health::new(body, entity.level.unwrap_or(0)); + let health = Some(comp::Health::new(body, entity.level.unwrap_or(0))); let poise = comp::Poise::new(body); let can_speak = match body { @@ -293,6 +293,7 @@ impl<'a> System<'a> for Sys { home_chunk: Some(comp::HomeChunk(key)), drop_item: entity.loot_drop, rtsim_entity: None, + projectile: None, }) } diff --git a/voxygen/anim/src/bird_large/mod.rs b/voxygen/anim/src/bird_large/mod.rs index 1c4c83663e..67e7da5166 100644 --- a/voxygen/anim/src/bird_large/mod.rs +++ b/voxygen/anim/src/bird_large/mod.rs @@ -209,7 +209,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { feed: match (body.species, body.body_type) { (Phoenix, _) => (-0.65), (Cockatrice, _) => (-0.5), - (Roc, _) => (-0.65), + (Roc, _) => (-0.4), }, } } From cb0566299a0765f8877e919fa2c2a653ee7f92fa Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Thu, 3 Jun 2021 21:25:42 -0400 Subject: [PATCH 09/38] Make tornado (and empty models in general) work on WGPU. --- .../common/abilities/ability_set_manifest.ron | 4 +- .../abilities/custom/birdlargebasic/dash.ron | 20 ++ .../custom/birdlargebasic/summontornadoes.ron | 8 +- .../custom/birdlargebasic/triplestrike.ron | 57 ++++++ .../common/abilities/custom/tornado/spin.ron | 6 +- common/src/comp/body.rs | 10 +- common/src/comp/body/object.rs | 1 + common/src/comp/inventory/loadout_builder.rs | 13 +- server/src/rtsim/tick.rs | 5 + server/src/sys/agent.rs | 53 +++++- voxygen/anim/src/biped_large/mod.rs | 2 +- voxygen/anim/src/bird_large/dash.rs | 177 ++++++++++++++++++ voxygen/anim/src/bird_large/mod.rs | 8 +- voxygen/anim/src/bird_large/summon.rs | 114 +++++++++++ voxygen/src/render/pipelines/figure.rs | 2 +- voxygen/src/scene/figure/mod.rs | 75 +++++++- voxygen/src/scene/simple.rs | 20 +- world/src/layer/wildlife.rs | 23 ++- 18 files changed, 544 insertions(+), 54 deletions(-) create mode 100644 assets/common/abilities/custom/birdlargebasic/dash.ron create mode 100644 assets/common/abilities/custom/birdlargebasic/triplestrike.ron create mode 100644 voxygen/anim/src/bird_large/dash.rs create mode 100644 voxygen/anim/src/bird_large/summon.rs diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 7c300eebeb..e84291d621 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -265,8 +265,8 @@ ], ), Custom("Bird Large Basic"): ( - primary: "common.abilities.custom.birdlargebreathe.firebomb", - secondary: "common.abilities.custom.birdlargebreathe.triplestrike", + primary: "common.abilities.custom.birdlargebasic.dash", + secondary: "common.abilities.custom.birdlargebasic.triplestrike", abilities: [ (None, "common.abilities.custom.birdlargebasic.summontornadoes"), ], diff --git a/assets/common/abilities/custom/birdlargebasic/dash.ron b/assets/common/abilities/custom/birdlargebasic/dash.ron new file mode 100644 index 0000000000..2421a1dd02 --- /dev/null +++ b/assets/common/abilities/custom/birdlargebasic/dash.ron @@ -0,0 +1,20 @@ +DashMelee( + energy_cost: 0, + base_damage: 70, + scaled_damage: 150, + base_poise_damage: 50, + scaled_poise_damage: 100, + base_knockback: 12.0, + scaled_knockback: 17.0, + range: 6.0, + angle: 20.0, + energy_drain: 0, + forward_speed: 1.5, + buildup_duration: 0.5, + charge_duration: 1.2, + swing_duration: 0.1, + recover_duration: 1.1, + charge_through: true, + is_interruptible: false, + damage_kind: Crushing, +) diff --git a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron index 21c90ae04a..9e487d8922 100644 --- a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron +++ b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron @@ -1,9 +1,9 @@ BasicSummon( buildup_duration: 0.5, - cast_duration: 1.0, - recover_duration: 0.5, + cast_duration: 0.2, + recover_duration: 0.2, summon_amount: 6, - summon_distance: (1, 5), + summon_distance: (1, 3), summon_info: ( body: Object(Tornado), scale: None, @@ -12,7 +12,7 @@ BasicSummon( skillset_config: None, ), duration: Some(( - secs: 5, + secs: 10, nanos: 0, )), ) \ No newline at end of file diff --git a/assets/common/abilities/custom/birdlargebasic/triplestrike.ron b/assets/common/abilities/custom/birdlargebasic/triplestrike.ron new file mode 100644 index 0000000000..eb332435a4 --- /dev/null +++ b/assets/common/abilities/custom/birdlargebasic/triplestrike.ron @@ -0,0 +1,57 @@ +ComboMelee( + stage_data: [ + ( + stage: 1, + base_damage: 100, + damage_increase: 0, + base_poise_damage: 0, + poise_damage_increase: 0, + knockback: 5.0, + range: 4.5, + angle: 30.0, + base_buildup_duration: 0.4, + base_swing_duration: 0.1, + base_recover_duration: 0.3, + forward_movement: 2.0, + damage_kind: Slashing, + ), + ( + stage: 2, + base_damage: 80, + damage_increase: 0, + base_poise_damage: 0, + poise_damage_increase: 0, + knockback: 5.0, + range: 3.5, + angle: 30.0, + base_buildup_duration: 0.4, + base_swing_duration: 0.1, + base_recover_duration: 0.3, + forward_movement: 1.5, + damage_kind: Slashing, + ), + ( + stage: 3, + base_damage: 130, + damage_increase: 0, + base_poise_damage: 0, + poise_damage_increase: 0, + knockback: 10.0, + range: 3.5, + angle: 30.0, + base_buildup_duration: 0.65, + base_swing_duration: 0.1, + base_recover_duration: 0.3, + forward_movement: 1.5, + damage_kind: Slashing, + ), + ], + initial_energy_gain: 0, + max_energy_gain: 0, + energy_increase: 0, + speed_increase: 0.0, + max_speed_increase: 0.0, + scales_from_combo: 0, + is_interruptible: false, + ori_modifier: 0.7, +) diff --git a/assets/common/abilities/custom/tornado/spin.ron b/assets/common/abilities/custom/tornado/spin.ron index de072c981e..2418c50b38 100644 --- a/assets/common/abilities/custom/tornado/spin.ron +++ b/assets/common/abilities/custom/tornado/spin.ron @@ -2,9 +2,9 @@ SpinMelee( buildup_duration: 0.0, swing_duration: 0.5, recover_duration: 0.0, - base_damage: 15000, - base_poise_damage: 200, - knockback: ( strength: 200.0, direction: Away), + base_damage: 50, + base_poise_damage: 100, + knockback: ( strength: 50.0, direction: Away), range: 3.5, damage_effect: None, energy_cost: 0, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 10854409bb..7b1765ebed 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -472,7 +472,10 @@ impl Body { }, Body::FishMedium(_) => 250, Body::Dragon(_) => 5000, - Body::BirdLarge(_) => 3000, + Body::BirdLarge(bird_large) => match bird_large.species { + bird_large::Species::Roc => 2400, + _ => 3000, + }, Body::FishSmall(_) => 20, Body::BipedLarge(biped_large) => match biped_large.species { biped_large::Species::Ogre => 3200, @@ -591,7 +594,10 @@ impl Body { }, Body::FishMedium(_) => 10, Body::Dragon(_) => 500, - Body::BirdLarge(_) => 120, + Body::BirdLarge(bird_large) => match bird_large.species { + bird_large::Species::Roc => 100, + _ => 120, + }, Body::FishSmall(_) => 10, Body::BipedLarge(biped_large) => match biped_large.species { biped_large::Species::Ogre => 70, diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index 2cdb72aaac..b452715362 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -376,6 +376,7 @@ impl Body { Body::HaniwaSentry => Vec3::new(0.8, 0.8, 1.4), Body::SeaLantern => Vec3::new(0.5, 0.5, 1.0), Body::Snowball => Vec3::broadcast(2.5), + Body::Tornado => Vec3::new(2.0, 2.0, 3.4), _ => Vec3::broadcast(0.5), } } diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 1978cf3fd9..c1ced2481c 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -253,13 +253,12 @@ fn default_main_tool(body: &Body) -> Item { (biped_large::Species::Ogre, biped_large::BodyType::Female) => Some( Item::new_from_asset_expect("common.items.npc_weapons.staff.ogre_staff"), ), - (biped_large::Species::Cavetroll, _) => Some(Item::new_from_asset_expect( - "common.items.npc_weapons.hammer.troll_hammer", - )), - (biped_large::Species::Mountaintroll, _) => Some(Item::new_from_asset_expect( - "common.items.npc_weapons.hammer.troll_hammer", - )), - (biped_large::Species::Swamptroll, _) => Some(Item::new_from_asset_expect( + ( + biped_large::Species::Mountaintroll + | biped_large::Species::Swamptroll + | biped_large::Species::Cavetroll, + _, + ) => Some(Item::new_from_asset_expect( "common.items.npc_weapons.hammer.troll_hammer", )), (biped_large::Species::Wendigo, _) => Some(Item::new_from_asset_expect( diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index 70f0608f61..cb4457f37f 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -137,6 +137,11 @@ impl<'a> System<'a> for Sys { agent, alignment: match body { comp::Body::Humanoid(_) => comp::Alignment::Npc, + comp::Body::BirdLarge(bird_large) => match bird_large.species { + comp::bird_large::Species::Roc => comp::Alignment::Enemy, + comp::bird_large::Species::Cockatrice => comp::Alignment::Enemy, + _ => comp::Alignment::Wild, + }, _ => comp::Alignment::Wild, }, scale: match body { diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index d22a68604d..33d181693c 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -3286,18 +3286,57 @@ impl<'a> AgentData<'a> { tgt_data: &TargetData, read_data: &ReadData, ) { - if can_see_tgt( - &*read_data.terrain, - self.pos, - tgt_data.pos, - attack_data.dist_sqrd, - ) && attack_data.angle < 15.0 + if !read_data + .terrain + .ray(self.pos.0, self.pos.0 - (Vec3::unit_z() * 2.0)) + .until(Block::is_solid) + .cast() + .1 + .map_or(true, |b| b.is_some()) + { + // Fly to target + controller + .actions + .push(ControlAction::basic_input(InputKind::Fly)); + let move_dir = tgt_data.pos.0 - self.pos.0; + controller.inputs.move_dir = + move_dir.xy().try_normalized().unwrap_or_else(Vec2::zero) * 2.0; + controller.inputs.move_z = move_dir.z - 0.5; + } else if agent.action_state.timer > 7.0 { + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + // Reset timer + agent.action_state.timer = 0.0; + } else if attack_data.angle < 90.0 + && attack_data.dist_sqrd < (1.5 * attack_data.min_attack_dist).powi(2) + && agent.action_state.timer < 6.0 + { + controller.inputs.move_dir = Vec2::zero(); + controller + .actions + .push(ControlAction::basic_input(InputKind::Secondary)); + agent.action_state.timer += read_data.dt.0; + } else if attack_data.dist_sqrd < (3.0 * attack_data.min_attack_dist).powi(2) + && attack_data.dist_sqrd > (2.0 * attack_data.min_attack_dist).powi(2) + && attack_data.angle < 90.0 + && agent.action_state.timer < 6.0 { controller .actions .push(ControlAction::basic_input(InputKind::Primary)); + controller.inputs.move_dir = (tgt_data.pos.0 - self.pos.0) + .xy() + .rotated_z(-0.47 * PI) + .try_normalized() + .unwrap_or_else(Vec2::unit_y); + agent.action_state.timer += read_data.dt.0; + } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) { + self.path_toward_target(agent, controller, tgt_data, read_data, true, None); + agent.action_state.timer += read_data.dt.0; } else { - agent.target = None; + self.path_toward_target(agent, controller, tgt_data, read_data, false, None); + agent.action_state.timer += read_data.dt.0; } } diff --git a/voxygen/anim/src/biped_large/mod.rs b/voxygen/anim/src/biped_large/mod.rs index b16f0cb019..cee364ed3b 100644 --- a/voxygen/anim/src/biped_large/mod.rs +++ b/voxygen/anim/src/biped_large/mod.rs @@ -419,7 +419,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { (Wendigo, _) => (15.0, 0.0), (Cavetroll, _) => (13.0, 1.5), (Mountaintroll, _) => (13.0, 1.5), - (Swamptroll, _) => (13.0, 1.5), + (Swamptroll, _) => (15.0, 0.5), (Dullahan, _) => (15.0, 0.0), (Werewolf, _) => (13.0, 0.0), (Occultsaurok, _) => (10.0, 0.0), diff --git a/voxygen/anim/src/bird_large/dash.rs b/voxygen/anim/src/bird_large/dash.rs new file mode 100644 index 0000000000..65717f32fd --- /dev/null +++ b/voxygen/anim/src/bird_large/dash.rs @@ -0,0 +1,177 @@ +use super::{ + super::{vek::*, Animation}, + BirdLargeSkeleton, SkeletonAttr, +}; +use common::states::utils::StageSection; +use std::f32::consts::PI; + +pub struct DashAnimation; +type DashAnimationDependency<'a> = ( + Vec3, + Vec3, + Vec3, + f32, + Option, + f32, + f32, +); + +impl Animation for DashAnimation { + type Dependency<'a> = DashAnimationDependency<'a>; + type Skeleton = BirdLargeSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"bird_large_dash\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "bird_large_dash")] + fn update_skeleton_inner<'a>( + skeleton: &Self::Skeleton, + (velocity, orientation, last_ori, acc_vel, stage_section, global_time, timer): Self::Dependency<'a>, + anim_time: f32, + rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + let speed = (Vec2::::from(velocity).magnitude()).min(22.0); + *rate = 1.0; + + let (movement1base, chargemovementbase, movement2base, movement3, legtell) = + match stage_section { + Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0, 0.0, anim_time), + Some(StageSection::Charge) => (1.0, 1.0, 0.0, 0.0, 0.0), + Some(StageSection::Swing) => (1.0, 0.0, anim_time.powi(4), 0.0, 1.0), + Some(StageSection::Recover) => (1.0, 0.0, 1.0, anim_time, 1.0), + _ => (0.0, 0.0, 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 movement1abs = movement1base * pullback; + let movement2abs = movement2base * pullback; + let legtwitch = (legtell * 6.0).sin() * pullback; + let legswing = legtell * pullback; + let chargeanim = (chargemovementbase * anim_time * 15.0).sin(); + + //let speednorm = speed / 13.0; + let speednorm = (speed / 13.0).powf(0.25); + + let speedmult = 0.8; + let lab: f32 = 0.6; //6 + + // acc_vel and anim_time mix to make sure phase lenght isn't starting at + // +infinite + let mixed_vel = acc_vel + anim_time * 5.0; //sets run frequency using speed, with anim_time setting a floor + + let short = ((1.0 + / (0.72 + + 0.28 * ((mixed_vel * 1.0 * lab * speedmult + PI * -0.15 - 0.5).sin()).powi(2))) + .sqrt()) + * ((mixed_vel * 1.0 * lab * speedmult + PI * -0.15 - 0.5).sin()) + * speednorm; + + // + let shortalt = (mixed_vel * 1.0 * lab * speedmult + PI * 3.0 / 8.0 - 0.5).sin() * speednorm; + + let ori: Vec2 = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); + let tilt = if ::vek::Vec2::new(ori, last_ori) + .map(|o| o.magnitude_squared()) + .map(|m| m > 0.001 && m.is_finite()) + .reduce_and() + && ori.angle_between(last_ori).is_finite() + { + ori.angle_between(last_ori).min(0.2) + * last_ori.determine_side(Vec2::zero(), ori).signum() + } else { + 0.0 + } * 1.3; + + next.head.scale = Vec3::one() * 0.98; + next.neck.scale = Vec3::one() * 1.02; + next.leg_l.scale = Vec3::one() / 8.0 * 0.98; + next.leg_r.scale = Vec3::one() / 8.0 * 0.98; + next.foot_l.scale = Vec3::one() * 1.02; + next.foot_r.scale = Vec3::one() * 1.02; + next.chest.scale = Vec3::one() * s_a.scaler / 8.0; + + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = Quaternion::rotation_x( + -0.1 * speednorm + short * -0.05 + movement1abs * -0.8 + movement2abs * 0.2, + ) * Quaternion::rotation_y(tilt * 0.2) + * Quaternion::rotation_z(shortalt * -0.05 - tilt * 1.5); + + next.beak.position = Vec3::new(0.0, s_a.beak.0, s_a.beak.1); + next.beak.orientation = + Quaternion::rotation_x(short * -0.02 - 0.02 + movement1abs * -0.4 + movement2abs * 0.4); + + next.neck.position = Vec3::new(0.0, s_a.neck.0, s_a.neck.1); + next.neck.orientation = Quaternion::rotation_x(-0.1 * speednorm + short * -0.04) + * Quaternion::rotation_y(tilt * 0.1) + * Quaternion::rotation_z(shortalt * -0.1 - tilt * 0.5); + + next.chest.position = Vec3::new( + 0.0, + s_a.chest.0, + s_a.chest.1 + short * 0.5 + 0.5 * speednorm, + ) * s_a.scaler + / 8.0; + next.chest.orientation = + Quaternion::rotation_x(short * 0.07 + movement1abs * 0.8 + movement2abs * -1.2) + * Quaternion::rotation_y(tilt * 0.8) + * Quaternion::rotation_z(shortalt * 0.10); + + next.tail_front.position = Vec3::new(0.0, s_a.tail_front.0, s_a.tail_front.1); + next.tail_front.orientation = + Quaternion::rotation_x(0.6 + short * -0.02 + movement1abs * -0.8 + movement2abs * 0.8); + + next.tail_rear.position = Vec3::new(0.0, s_a.tail_rear.0, s_a.tail_rear.1); + next.tail_rear.orientation = Quaternion::rotation_x(-0.2 + short * -0.1); + + next.wing_in_l.position = Vec3::new(-s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + next.wing_in_r.position = Vec3::new(s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + + next.wing_in_l.orientation = + Quaternion::rotation_y( + -0.8 + movement1abs * 1.0 + chargeanim * 0.2 - movement2abs * 0.6, + ) * Quaternion::rotation_z(0.2 - movement1abs * 0.6 - movement2abs * 0.6); + next.wing_in_r.orientation = + Quaternion::rotation_y( + 0.8 - movement1abs * 1.0 - chargeanim * 0.2 + movement2abs * 0.6, + ) * Quaternion::rotation_z(-0.2 + movement1abs * 0.6 + movement2abs * 0.6); + + next.wing_mid_l.position = Vec3::new(-s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_r.position = Vec3::new(s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_l.orientation = Quaternion::rotation_y(-0.1) * Quaternion::rotation_z(0.7); + next.wing_mid_r.orientation = Quaternion::rotation_y(0.1) * Quaternion::rotation_z(-0.7); + + next.wing_out_l.position = Vec3::new(-s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_r.position = Vec3::new(s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_l.orientation = + Quaternion::rotation_y(-0.2 + short * 0.05) * Quaternion::rotation_z(0.2); + next.wing_out_r.orientation = + Quaternion::rotation_y(0.2 + short * -0.05) * Quaternion::rotation_z(-0.2); + + if legtell > 0.0 { + if mirror.is_sign_positive() { + next.leg_l.orientation = Quaternion::rotation_x(legswing * 1.1); + + next.foot_l.orientation = Quaternion::rotation_x(legswing * -1.1 + legtwitch * 0.5); + + next.leg_r.orientation = Quaternion::rotation_x(0.0); + + next.foot_r.orientation = Quaternion::rotation_x(0.0); + } else { + next.leg_l.orientation = Quaternion::rotation_x(0.0); + + next.foot_l.orientation = Quaternion::rotation_x(0.0); + + next.leg_r.orientation = Quaternion::rotation_x(legswing * 1.1); + + next.foot_r.orientation = Quaternion::rotation_x(legswing * -1.1 + legtwitch * 0.5); + } + } + + next + } +} diff --git a/voxygen/anim/src/bird_large/mod.rs b/voxygen/anim/src/bird_large/mod.rs index 67e7da5166..58a3ae27a1 100644 --- a/voxygen/anim/src/bird_large/mod.rs +++ b/voxygen/anim/src/bird_large/mod.rs @@ -1,5 +1,6 @@ pub mod alpha; pub mod breathe; +pub mod dash; pub mod feed; pub mod fly; pub mod idle; @@ -7,13 +8,14 @@ pub mod run; pub mod shockwave; pub mod shoot; pub mod stunned; +pub mod summon; pub mod swim; // Reexports pub use self::{ - alpha::AlphaAnimation, breathe::BreatheAnimation, feed::FeedAnimation, fly::FlyAnimation, - idle::IdleAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation, - stunned::StunnedAnimation, swim::SwimAnimation, + alpha::AlphaAnimation, breathe::BreatheAnimation, dash::DashAnimation, feed::FeedAnimation, + fly::FlyAnimation, idle::IdleAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, + shoot::ShootAnimation, stunned::StunnedAnimation, summon::SummonAnimation, swim::SwimAnimation, }; use super::{make_bone, vek::*, FigureBoneData, Skeleton}; diff --git a/voxygen/anim/src/bird_large/summon.rs b/voxygen/anim/src/bird_large/summon.rs new file mode 100644 index 0000000000..5176540e77 --- /dev/null +++ b/voxygen/anim/src/bird_large/summon.rs @@ -0,0 +1,114 @@ +use super::{ + super::{vek::*, Animation}, + BirdLargeSkeleton, SkeletonAttr, +}; +use common::{states::utils::StageSection, util::Dir}; + +pub struct SummonAnimation; + +type SummonAnimationDependency = (f32, Option, f32, Dir, bool); + +impl Animation for SummonAnimation { + type Dependency<'a> = SummonAnimationDependency; + type Skeleton = BirdLargeSkeleton; + + #[cfg(feature = "use-dyn-lib")] + const UPDATE_FN: &'static [u8] = b"bird_large_summon\0"; + + #[cfg_attr(feature = "be-dyn-lib", export_name = "bird_large_summon")] + #[allow(clippy::approx_constant)] // TODO: Pending review in #587 + fn update_skeleton_inner<'a>( + skeleton: &Self::Skeleton, + (global_time, stage_section, timer, look_dir, on_ground): Self::Dependency<'a>, + anim_time: f32, + rate: &mut f32, + s_a: &SkeletonAttr, + ) -> Self::Skeleton { + *rate = 1.0; + let mut next = (*skeleton).clone(); + + let (movement1base, movement2base, movement3, twitch) = match stage_section { + Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0, 0.0), + Some(StageSection::Cast) => (1.0, anim_time.min(1.0).powf(0.1), 0.0, anim_time), + Some(StageSection::Recover) => (1.0, 1.0, anim_time.min(1.0).powi(2), 1.0), + _ => (0.0, 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 twitch2 = mirror * (twitch * 20.0).sin() * pullback; + + let movement1abs = movement1base * pullback; + let movement2abs = movement2base * pullback; + + let wave_slow_cos = (anim_time * 4.5).cos(); + + next.head.scale = Vec3::one() * 0.98; + next.neck.scale = Vec3::one() * 1.02; + next.leg_l.scale = Vec3::one() / 8.0 * 0.98; + next.leg_r.scale = Vec3::one() / 8.0 * 0.98; + next.foot_l.scale = Vec3::one() * 1.02; + next.foot_r.scale = Vec3::one() * 1.02; + next.chest.scale = Vec3::one() * s_a.scaler / 8.0; + + next.chest.position = Vec3::new( + 0.0, + s_a.chest.0, + s_a.chest.1 + wave_slow_cos * 0.06 + twitch2 * 0.1, + ) * s_a.scaler + / 8.0; + + next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1); + next.head.orientation = + Quaternion::rotation_x(movement1abs * -1.0 - movement2abs * 0.1 + look_dir.z * 0.4); + + next.beak.position = Vec3::new(0.0, s_a.beak.0, s_a.beak.1); + next.beak.orientation = Quaternion::rotation_x(movement1abs * -0.7 + twitch2 * 0.1); + + if on_ground { + next.neck.position = Vec3::new(0.0, s_a.neck.0, s_a.neck.1); + next.neck.orientation = Quaternion::rotation_x(movement1abs * 0.5 - movement2abs * 0.5); + + next.chest.orientation = + Quaternion::rotation_x(movement1abs * 1.1 - movement2abs * 0.1); + + next.wing_in_l.position = Vec3::new(-s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + next.wing_in_r.position = Vec3::new(s_a.wing_in.0, s_a.wing_in.1, s_a.wing_in.2); + + next.wing_in_l.orientation = + Quaternion::rotation_x(movement1abs * 0.4 - movement2abs * 0.4) + * Quaternion::rotation_y(-1.0 + movement1abs * 1.6 - movement2abs * 1.8) + * Quaternion::rotation_z(0.2 - movement1abs * 1.8 + movement2abs * 0.4); + next.wing_in_r.orientation = + Quaternion::rotation_x(movement1abs * 0.4 - movement2abs * 0.4) + * Quaternion::rotation_y(1.0 - movement1abs * 1.6 + movement2abs * 1.8) + * Quaternion::rotation_z(-0.2 + movement1abs * 1.8 - movement2abs * 0.4); + + next.wing_mid_l.position = Vec3::new(-s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_r.position = Vec3::new(s_a.wing_mid.0, s_a.wing_mid.1, s_a.wing_mid.2); + next.wing_mid_l.orientation = + Quaternion::rotation_y(-0.1 - movement2abs * 0.4) * Quaternion::rotation_z(0.7); + next.wing_mid_r.orientation = + Quaternion::rotation_y(0.1 + movement2abs * 0.4) * Quaternion::rotation_z(-0.7); + + next.wing_out_l.position = Vec3::new(-s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_r.position = Vec3::new(s_a.wing_out.0, s_a.wing_out.1, s_a.wing_out.2); + next.wing_out_l.orientation = + Quaternion::rotation_y(-0.2 - movement2abs * 0.4) * Quaternion::rotation_z(0.2); + next.wing_out_r.orientation = + Quaternion::rotation_y(0.2 + movement2abs * 0.4) * Quaternion::rotation_z(-0.2); + + next.tail_front.position = Vec3::new(0.0, s_a.tail_front.0, s_a.tail_front.1); + next.tail_front.orientation = + Quaternion::rotation_x(-movement1abs * 0.1 + movement2abs * 0.1 + twitch2 * 0.02); + next.tail_rear.position = Vec3::new(0.0, s_a.tail_rear.0, s_a.tail_rear.1); + next.tail_rear.orientation = + Quaternion::rotation_x(-movement1abs * 0.1 + movement2abs * 0.1 + twitch2 * 0.02); + } else { + } + + next + } +} diff --git a/voxygen/src/render/pipelines/figure.rs b/voxygen/src/render/pipelines/figure.rs index 662f5db4c6..5502b4babd 100644 --- a/voxygen/src/render/pipelines/figure.rs +++ b/voxygen/src/render/pipelines/figure.rs @@ -80,7 +80,7 @@ impl Default for BoneData { } pub struct FigureModel { - pub opaque: Model, + pub opaque: Option>, /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different * LOD levels. */ } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 167bbf2571..c43d262185 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -88,11 +88,12 @@ pub struct FigureModelEntry { } impl FigureModelEntry { - pub fn lod_model(&self, lod: usize) -> SubModel { + pub fn lod_model(&self, lod: usize) -> Option> { // Note: Range doesn't impl Copy even for trivially Cloneable things self.model .opaque - .submodel(self.lod_vertex_ranges[lod].clone()) + .as_ref() + .map(|m| m.submodel(self.lod_vertex_ranges[lod].clone())) } } @@ -3480,6 +3481,70 @@ impl FigureMgr { skeleton_attr, ) }, + CharacterState::BasicSummon(s) => { + let stage_time = s.timer.as_secs_f32(); + let stage_progress = match s.stage_section { + StageSection::Buildup => { + stage_time / s.static_data.buildup_duration.as_secs_f32() + }, + + StageSection::Cast => { + stage_time / s.static_data.cast_duration.as_secs_f32() + }, + StageSection::Recover => { + stage_time / s.static_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + + anim::bird_large::SummonAnimation::update_skeleton( + &target_base, + ( + time, + Some(s.stage_section), + state.state_time, + look_dir, + physics.on_ground, + ), + stage_progress, + &mut state_animation_rate, + skeleton_attr, + ) + }, + CharacterState::DashMelee(s) => { + let stage_time = s.timer.as_secs_f32(); + let stage_progress = match s.stage_section { + StageSection::Buildup => { + stage_time / s.static_data.buildup_duration.as_secs_f32() + }, + StageSection::Charge => { + stage_time / s.static_data.charge_duration.as_secs_f32() + }, + StageSection::Swing => { + stage_time / s.static_data.swing_duration.as_secs_f32() + }, + StageSection::Recover => { + stage_time / s.static_data.recover_duration.as_secs_f32() + }, + _ => 0.0, + }; + anim::bird_large::DashAnimation::update_skeleton( + &target_base, + ( + rel_vel, + // TODO: Update to use the quaternion. + ori * anim::vek::Vec3::::unit_y(), + state.last_ori * anim::vek::Vec3::::unit_y(), + state.acc_vel, + Some(s.stage_section), + time, + state.state_time, + ), + stage_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 { @@ -5163,7 +5228,7 @@ impl FigureMgr { model_entry.lod_model(0) }; - Some((bound, model, col_lights_.texture(model_entry))) + Some((bound, model?, col_lights_.texture(model_entry))) } else { // trace!("Body has no saved figure"); None @@ -5221,9 +5286,7 @@ impl FigureColLights { let col_lights = renderer.figure_bind_col_light(col_lights); let model_len = u32::try_from(opaque.vertices().len()) .expect("The model size for this figure does not fit in a u32!"); - let model = renderer - .create_model(&opaque) - .expect("The model contains no vertices!"); + let model = renderer.create_model(&opaque); vertex_ranges.iter().for_each(|range| { assert!( diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index 54fd63bea4..19cec5b20d 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -366,20 +366,20 @@ impl Scene { ); if let Some(model) = model { - figure_drawer.draw( - model.lod_model(0), - self.figure_state.bound(), - &self.col_lights.texture(model), - ); + if let Some(lod) = model.lod_model(0) { + figure_drawer.draw( + lod, + self.figure_state.bound(), + &self.col_lights.texture(model), + ); + } } } if let Some((model, state)) = &self.backdrop { - figure_drawer.draw( - model.lod_model(0), - state.bound(), - &self.col_lights.texture(model), - ); + if let Some(lod) = model.lod_model(0) { + figure_drawer.draw(lod, state.bound(), &self.col_lights.texture(model)); + } } drop(figure_drawer); diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index 5c240d1235..362fcf2fed 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -119,9 +119,15 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body( - biped_large::Body::random_with(rng, &biped_large::Species::Wendigo).into(), - ) + .with_body(match rng.gen_range(0..2) { + 0 => biped_large::Body::random_with(rng, &biped_large::Species::Wendigo) + .into(), + _ => biped_large::Body::random_with( + rng, + &biped_large::Species::Mountaintroll, + ) + .into(), + }) .with_alignment(Alignment::Enemy) }, group_size: 1..2, @@ -623,7 +629,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0..4) { + .with_body(match rng.gen_range(0..5) { 0 => theropod::Body::random_with(rng, &theropod::Species::Odonto).into(), 1 => { biped_large::Body::random_with(rng, &biped_large::Species::Mightysaurok) @@ -633,6 +639,8 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( biped_large::Body::random_with(rng, &biped_large::Species::Occultsaurok) .into() }, + 3 => bird_large::Body::random_with(rng, &bird_large::Species::Cockatrice) + .into(), _ => biped_large::Body::random_with(rng, &biped_large::Species::Slysaurok) .into(), }) @@ -652,12 +660,11 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Entry { make_entity: |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0..4) { + .with_body(match rng.gen_range(0..3) { 0 => bird_medium::Body::random_with(rng, &bird_medium::Species::Parrot) .into(), - 1 => bird_large::Body::random_with(rng, &bird_large::Species::Cockatrice) - .into(), - 2 => quadruped_small::Body::random_with( + + 1 => quadruped_small::Body::random_with( rng, &quadruped_small::Species::Quokka, ) From f2c78361619f11dc95c1f2f3c2faffd529b060ec Mon Sep 17 00:00:00 2001 From: Snowram Date: Fri, 11 Jun 2021 03:15:58 +0200 Subject: [PATCH 10/38] Makes summon range inclusive --- .../abilities/custom/birdlargebasic/dash.ron | 10 ++++---- assets/voxygen/shaders/particle-vert.glsl | 5 ++-- common/src/comp/body.rs | 4 ++-- common/src/states/basic_summon.rs | 2 +- server/src/sys/agent.rs | 24 ++++++++++++++----- voxygen/src/scene/particle.rs | 10 ++++---- 6 files changed, 32 insertions(+), 23 deletions(-) diff --git a/assets/common/abilities/custom/birdlargebasic/dash.ron b/assets/common/abilities/custom/birdlargebasic/dash.ron index 2421a1dd02..8741cc774a 100644 --- a/assets/common/abilities/custom/birdlargebasic/dash.ron +++ b/assets/common/abilities/custom/birdlargebasic/dash.ron @@ -4,17 +4,17 @@ DashMelee( scaled_damage: 150, base_poise_damage: 50, scaled_poise_damage: 100, - base_knockback: 12.0, - scaled_knockback: 17.0, - range: 6.0, + base_knockback: 6.0, + scaled_knockback: 12.0, + range: 2.0, angle: 20.0, energy_drain: 0, forward_speed: 1.5, buildup_duration: 0.5, - charge_duration: 1.2, + charge_duration: 3.0, swing_duration: 0.1, recover_duration: 1.1, - charge_through: true, + charge_through: false, is_interruptible: false, damage_kind: Crushing, ) diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index bb3cc72945..3cd9721a47 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -545,12 +545,11 @@ void main() { ); break; case TORNADO: - f_reflect = 0.0; // Fire doesn't reflect light, it emits it + f_reflect = 0.0; attr = Attr( - //vec3(sin(lifetime * 400.0) * 3.0 * percent(), cos(lifetime * 400.0) * 3.0 * percent(), lifetime * 5.0), spiral_motion(vec3(0, 0, 5), abs(rand0) + abs(rand1) * percent() * 3.0, percent(), 15.0 * abs(rand2), rand3), vec3((2.5 * (1 - slow_start(0.05)))), - vec4(1.2 + 0.5 * percent(), 1.2 + 0.5 * percent(), 1.2 + 0.5 * percent(), 2.5), + vec4(vec3(1.2 + 0.5 * percent()), 1), spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9) ); break; diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 7b1765ebed..470763465a 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -473,7 +473,7 @@ impl Body { Body::FishMedium(_) => 250, Body::Dragon(_) => 5000, Body::BirdLarge(bird_large) => match bird_large.species { - bird_large::Species::Roc => 2400, + bird_large::Species::Roc => 2800, _ => 3000, }, Body::FishSmall(_) => 20, @@ -595,7 +595,7 @@ impl Body { Body::FishMedium(_) => 10, Body::Dragon(_) => 500, Body::BirdLarge(bird_large) => match bird_large.species { - bird_large::Species::Roc => 100, + bird_large::Species::Roc => 110, _ => 120, }, Body::FishSmall(_) => 10, diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 44c71b9f47..9ff74f016b 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -122,7 +122,7 @@ impl CharacterBehavior for Data { self.summon_count as f32 / self.static_data.summon_amount as f32; let length = rand::thread_rng().gen_range( - self.static_data.summon_distance.0..self.static_data.summon_distance.1, + self.static_data.summon_distance.0..=self.static_data.summon_distance.1, ); // Summon in a clockwise fashion diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 33d181693c..448af696d9 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -3286,6 +3286,7 @@ impl<'a> AgentData<'a> { tgt_data: &TargetData, read_data: &ReadData, ) { + // If higher than 2 blocks if !read_data .terrain .ray(self.pos.0, self.pos.0 - (Vec3::unit_z() * 2.0)) @@ -3294,7 +3295,7 @@ impl<'a> AgentData<'a> { .1 .map_or(true, |b| b.is_some()) { - // Fly to target + // Fly to target and land controller .actions .push(ControlAction::basic_input(InputKind::Fly)); @@ -3302,26 +3303,35 @@ impl<'a> AgentData<'a> { controller.inputs.move_dir = move_dir.xy().try_normalized().unwrap_or_else(Vec2::zero) * 2.0; controller.inputs.move_z = move_dir.z - 0.5; - } else if agent.action_state.timer > 7.0 { + // If near a target and timer higher than 7 + } else if agent.action_state.timer > 6.0 + && attack_data.dist_sqrd < (3.0 * attack_data.min_attack_dist).powi(2) + { + // Cast tornadoes controller .actions .push(ControlAction::basic_input(InputKind::Ability(0))); // Reset timer agent.action_state.timer = 0.0; + // If near and in front of target and timer lower than 6 } else if attack_data.angle < 90.0 && attack_data.dist_sqrd < (1.5 * attack_data.min_attack_dist).powi(2) - && agent.action_state.timer < 6.0 + && agent.action_state.timer < 5.0 { + // Basic strike controller.inputs.move_dir = Vec2::zero(); controller .actions .push(ControlAction::basic_input(InputKind::Secondary)); + // Increase timer agent.action_state.timer += read_data.dt.0; + // If far from the target and timer lower than 6 } else if attack_data.dist_sqrd < (3.0 * attack_data.min_attack_dist).powi(2) - && attack_data.dist_sqrd > (2.0 * attack_data.min_attack_dist).powi(2) - && attack_data.angle < 90.0 - && agent.action_state.timer < 6.0 + && attack_data.dist_sqrd > (1.5 * attack_data.min_attack_dist).powi(2) + && attack_data.angle < 60.0 + && agent.action_state.timer < 5.0 { + // Dash controller .actions .push(ControlAction::basic_input(InputKind::Primary)); @@ -3331,7 +3341,9 @@ impl<'a> AgentData<'a> { .try_normalized() .unwrap_or_else(Vec2::unit_y); agent.action_state.timer += read_data.dt.0; + // If very far from the player } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) { + // Walk to the player self.path_toward_target(agent, controller, tgt_data, read_data, true, None); agent.action_state.timer += read_data.dt.0; } else { diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index d21640e0d6..76bd714891 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -324,7 +324,7 @@ impl ParticleMgr { self.maintain_boltnature_particles(scene_data, pos, vel) }, Body::Object(object::Body::Tornado) => { - self.maintain_tornado_particles(scene_data, pos, vel) + self.maintain_tornado_particles(scene_data, pos) }, Body::Object( object::Body::Bomb @@ -501,12 +501,11 @@ impl ParticleMgr { ); } - fn maintain_tornado_particles(&mut self, scene_data: &SceneData, pos: &Pos, vel: Option<&Vel>) { + fn maintain_tornado_particles(&mut self, scene_data: &SceneData, pos: &Pos) { let time = scene_data.state.get_time(); - let dt = scene_data.state.get_delta_time(); let mut rng = thread_rng(); - // nature + // air particles self.particles.resize_with( self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(5))), || { @@ -514,8 +513,7 @@ impl ParticleMgr { Duration::from_millis(1000), time, ParticleMode::Tornado, - pos.0.map(|e| e + rng.gen_range(-0.25..0.25)) - + vel.map_or(Vec3::zero(), |v| -v.0 * dt * rng.gen::()), + pos.0.map(|e| e + rng.gen_range(-0.25..0.25)), ) }, ); From 8351aab25d67d8a006f3aa7668d3e4580d348115 Mon Sep 17 00:00:00 2001 From: Snowram Date: Tue, 15 Jun 2021 01:34:17 +0200 Subject: [PATCH 11/38] Addresses comments about roc AI --- .../common/abilities/ability_set_manifest.ron | 6 +- .../abilities/custom/birdlargebasic/dash.ron | 6 +- .../custom/birdlargebasic/summontornadoes.ron | 4 +- .../custom/birdlargebasic/triplestrike.ron | 21 +++--- .../common/abilities/custom/tornado/spin.ron | 6 +- .../items/npc_weapons/unique/tornado.ron | 2 +- .../loot_tables/creature/bird_large/roc.ron | 4 + common/src/states/basic_summon.rs | 2 +- server/src/events/entity_manipulation.rs | 1 + server/src/sys/agent.rs | 74 +++++++++---------- 10 files changed, 65 insertions(+), 61 deletions(-) create mode 100644 assets/common/loot_tables/creature/bird_large/roc.ron diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index e84291d621..ef9979ce3f 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -265,10 +265,10 @@ ], ), Custom("Bird Large Basic"): ( - primary: "common.abilities.custom.birdlargebasic.dash", - secondary: "common.abilities.custom.birdlargebasic.triplestrike", + primary: "common.abilities.custom.birdlargebasic.triplestrike", + secondary: "common.abilities.custom.birdlargebasic.summontornadoes", abilities: [ - (None, "common.abilities.custom.birdlargebasic.summontornadoes"), + (None, "common.abilities.custom.birdlargebasic.dash"), ], ), Custom("Tornado"): ( diff --git a/assets/common/abilities/custom/birdlargebasic/dash.ron b/assets/common/abilities/custom/birdlargebasic/dash.ron index 8741cc774a..0760657973 100644 --- a/assets/common/abilities/custom/birdlargebasic/dash.ron +++ b/assets/common/abilities/custom/birdlargebasic/dash.ron @@ -1,6 +1,6 @@ DashMelee( energy_cost: 0, - base_damage: 70, + base_damage: 80, scaled_damage: 150, base_poise_damage: 50, scaled_poise_damage: 100, @@ -9,11 +9,11 @@ DashMelee( range: 2.0, angle: 20.0, energy_drain: 0, - forward_speed: 1.5, + forward_speed: 1.9, buildup_duration: 0.5, charge_duration: 3.0, swing_duration: 0.1, - recover_duration: 1.1, + recover_duration: 0.7, charge_through: false, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron index 9e487d8922..66240b64a6 100644 --- a/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron +++ b/assets/common/abilities/custom/birdlargebasic/summontornadoes.ron @@ -2,8 +2,8 @@ BasicSummon( buildup_duration: 0.5, cast_duration: 0.2, recover_duration: 0.2, - summon_amount: 6, - summon_distance: (1, 3), + summon_amount: 12, + summon_distance: (4, 9), summon_info: ( body: Object(Tornado), scale: None, diff --git a/assets/common/abilities/custom/birdlargebasic/triplestrike.ron b/assets/common/abilities/custom/birdlargebasic/triplestrike.ron index eb332435a4..ae24969a40 100644 --- a/assets/common/abilities/custom/birdlargebasic/triplestrike.ron +++ b/assets/common/abilities/custom/birdlargebasic/triplestrike.ron @@ -2,7 +2,7 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 100, + base_damage: 110, damage_increase: 0, base_poise_damage: 0, poise_damage_increase: 0, @@ -11,13 +11,14 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, - forward_movement: 2.0, - damage_kind: Slashing, + forward_movement: 3.0, + damage_kind: Crushing, ), ( stage: 2, - base_damage: 80, + base_damage: 90, damage_increase: 0, base_poise_damage: 0, poise_damage_increase: 0, @@ -26,13 +27,14 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.4, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, - forward_movement: 1.5, - damage_kind: Slashing, + forward_movement: 3.0, + damage_kind: Crushing, ), ( stage: 3, - base_damage: 130, + base_damage: 140, damage_increase: 0, base_poise_damage: 0, poise_damage_increase: 0, @@ -41,9 +43,10 @@ ComboMelee( angle: 30.0, base_buildup_duration: 0.65, base_swing_duration: 0.1, + hit_timing: 0.5, base_recover_duration: 0.3, - forward_movement: 1.5, - damage_kind: Slashing, + forward_movement: 3.5, + damage_kind: Crushing, ), ], initial_energy_gain: 0, diff --git a/assets/common/abilities/custom/tornado/spin.ron b/assets/common/abilities/custom/tornado/spin.ron index 2418c50b38..f5108fad61 100644 --- a/assets/common/abilities/custom/tornado/spin.ron +++ b/assets/common/abilities/custom/tornado/spin.ron @@ -2,14 +2,14 @@ SpinMelee( buildup_duration: 0.0, swing_duration: 0.5, recover_duration: 0.0, - base_damage: 50, - base_poise_damage: 100, + base_damage: 400, + base_poise_damage: 0, knockback: ( strength: 50.0, direction: Away), range: 3.5, damage_effect: None, energy_cost: 0, is_infinite: true, - movement_behavior: AxeHover, + movement_behavior: ForwardGround, is_interruptible: false, forward_speed: 0.0, num_spins: 1, diff --git a/assets/common/items/npc_weapons/unique/tornado.ron b/assets/common/items/npc_weapons/unique/tornado.ron index 14c1054395..ee38a3be6b 100644 --- a/assets/common/items/npc_weapons/unique/tornado.ron +++ b/assets/common/items/npc_weapons/unique/tornado.ron @@ -7,7 +7,7 @@ ItemDef( stats: Direct(( equip_time_secs: 0.01, power: 1.0, - poise_strength: 1.0, + poise_strength: 0.0, speed: 1.0, crit_chance: 0.0, crit_mult: 0.0, diff --git a/assets/common/loot_tables/creature/bird_large/roc.ron b/assets/common/loot_tables/creature/bird_large/roc.ron new file mode 100644 index 0000000000..a5ec41abcf --- /dev/null +++ b/assets/common/loot_tables/creature/bird_large/roc.ron @@ -0,0 +1,4 @@ +[ + (0.5, Item("common.items.food.meat.beast_large_raw")), + (1.0, Item("common.items.crafting_ing.animal_misc.raptor_feather")), +] \ No newline at end of file diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 9ff74f016b..80eab6e301 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -143,7 +143,7 @@ impl CharacterBehavior for Data { let collision_vector = Vec3::new( data.pos.0.x + (summon_frac * 2.0 * PI).sin() * obstacle_xy, data.pos.0.y + (summon_frac * 2.0 * PI).cos() * obstacle_xy, - data.pos.0.z, + data.pos.0.z + data.body.eye_height(), ); // Check for collision in z up to 50 blocks diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 3ce4a8178f..0eb466a80c 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -423,6 +423,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc bird_large::Species::Cockatrice => { "common.loot_tables.creature.bird_large.cockatrice" }, + bird_large::Species::Roc => "common.loot_tables.creature.bird_large.roc", _ => "common.loot_tables.creature.bird_large.phoenix", }, Some(common::comp::Body::FishMedium(_)) => "common.loot_tables.creature.fish", diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 448af696d9..addd351b2f 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -3286,6 +3286,11 @@ impl<'a> AgentData<'a> { tgt_data: &TargetData, read_data: &ReadData, ) { + const BIRD_ATTACK_RANGE: f32 = 4.0; + const BIRD_CHARGE_DISTANCE: f32 = 15.0; + let bird_attack_distance = self.body.map_or(0.0, |b| b.radius()) + BIRD_ATTACK_RANGE; + // Increase action timer + agent.action_state.timer += read_data.dt.0; // If higher than 2 blocks if !read_data .terrain @@ -3303,53 +3308,44 @@ impl<'a> AgentData<'a> { controller.inputs.move_dir = move_dir.xy().try_normalized().unwrap_or_else(Vec2::zero) * 2.0; controller.inputs.move_z = move_dir.z - 0.5; - // If near a target and timer higher than 7 - } else if agent.action_state.timer > 6.0 - && attack_data.dist_sqrd < (3.0 * attack_data.min_attack_dist).powi(2) - { - // Cast tornadoes - controller - .actions - .push(ControlAction::basic_input(InputKind::Ability(0))); - // Reset timer - agent.action_state.timer = 0.0; - // If near and in front of target and timer lower than 6 - } else if attack_data.angle < 90.0 - && attack_data.dist_sqrd < (1.5 * attack_data.min_attack_dist).powi(2) - && agent.action_state.timer < 5.0 - { - // Basic strike - controller.inputs.move_dir = Vec2::zero(); + } else if agent.action_state.timer > 8.0 { + // If action timer higher than 8, make bird summon tornadoes controller .actions .push(ControlAction::basic_input(InputKind::Secondary)); - // Increase timer - agent.action_state.timer += read_data.dt.0; - // If far from the target and timer lower than 6 - } else if attack_data.dist_sqrd < (3.0 * attack_data.min_attack_dist).powi(2) - && attack_data.dist_sqrd > (1.5 * attack_data.min_attack_dist).powi(2) - && attack_data.angle < 60.0 - && agent.action_state.timer < 5.0 + if matches!(self.char_state, CharacterState::BasicSummon(c) if matches!(c.stage_section, StageSection::Recover)) + { + // Reset timer + agent.action_state.timer = 0.0; + } + } else if matches!(self.char_state, CharacterState::DashMelee(c) if !matches!(c.stage_section, StageSection::Recover)) { - // Dash + // If already in dash, keep dashing if not in recover + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + } else if matches!(self.char_state, CharacterState::ComboMelee(c) if matches!(c.stage_section, StageSection::Recover)) + { + // If already in combo keep comboing if not in recover controller .actions .push(ControlAction::basic_input(InputKind::Primary)); - controller.inputs.move_dir = (tgt_data.pos.0 - self.pos.0) - .xy() - .rotated_z(-0.47 * PI) - .try_normalized() - .unwrap_or_else(Vec2::unit_y); - agent.action_state.timer += read_data.dt.0; - // If very far from the player - } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) { - // Walk to the player - self.path_toward_target(agent, controller, tgt_data, read_data, true, None); - agent.action_state.timer += read_data.dt.0; - } else { - self.path_toward_target(agent, controller, tgt_data, read_data, false, None); - agent.action_state.timer += read_data.dt.0; + } else if attack_data.dist_sqrd > BIRD_CHARGE_DISTANCE.powi(2) { + // Charges at target if they are far enough away + if attack_data.angle < 60.0 { + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + } + } else if attack_data.dist_sqrd < bird_attack_distance.powi(2) { + // Combo melee target + controller + .actions + .push(ControlAction::basic_input(InputKind::Primary)); + agent.action_state.condition = true; } + // Make bird move towards target + self.path_toward_target(agent, controller, tgt_data, read_data, true, None); } fn handle_minotaur_attack( From 3f176ca068fc080a7c6b2039e8d79fc545cfb7e0 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Tue, 15 Jun 2021 19:02:30 +0300 Subject: [PATCH 12/38] Panic if can't parse file --- voxygen/i18n/src/analysis.rs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/voxygen/i18n/src/analysis.rs b/voxygen/i18n/src/analysis.rs index 13344b9c30..50887955fd 100644 --- a/voxygen/i18n/src/analysis.rs +++ b/voxygen/i18n/src/analysis.rs @@ -231,17 +231,14 @@ fn complete_key_versions<'a>( let full_path = i18n_file.path(); let path = full_path.strip_prefix(root_dir).unwrap(); let i18n_blob = read_file_from_path(&repo, &head_ref, &path); - let i18n: LocalizationFragment = match from_bytes(i18n_blob.content()) { - Ok(v) => v, - Err(e) => { - eprintln!( + let i18n: LocalizationFragment = + from_bytes(i18n_blob.content()).unwrap_or_else(|e| { + panic!( "Could not parse {} RON file, skipping: {}", i18n_file.path().to_string_lossy(), e - ); - continue; - }, - }; + ) + }); i18n_key_versions.extend(generate_key_version(&repo, &i18n, &path, &i18n_blob)); } } @@ -304,17 +301,13 @@ fn test_localization_directory( // Find the localization entry state let current_blob = read_file_from_path(&repo, &head_ref, &relfile); - let current_loc: RawLocalization = match from_bytes(current_blob.content()) { - Ok(v) => v, - Err(e) => { - eprintln!( - "Could not parse {} RON file, skipping: {}", - relfile.to_string_lossy(), - e - ); - return None; - }, - }; + let current_loc: RawLocalization = from_bytes(current_blob.content()).unwrap_or_else(|e| { + panic!( + "Could not parse {} RON file, skipping: {}", + relfile.to_string_lossy(), + e + ) + }); // Gather state of current localization let mut current_i18n = gather_state( From fbb34b73312a2041ca1420a44d96d8b8f58875c2 Mon Sep 17 00:00:00 2001 From: "K. Kisa" Date: Tue, 15 Jun 2021 16:36:41 +0000 Subject: [PATCH 13/38] Veloren/veloren/master --- assets/voxygen/i18n/ru_RU/_manifest.ron | 780 +++++-------------- assets/voxygen/i18n/ru_RU/char_selection.ron | 32 + assets/voxygen/i18n/ru_RU/common.ron | 117 +++ assets/voxygen/i18n/ru_RU/gameinput.ron | 73 ++ assets/voxygen/i18n/ru_RU/main.ron | 8 +- assets/voxygen/i18n/ru_RU/skills.ron | 10 +- 6 files changed, 407 insertions(+), 613 deletions(-) create mode 100644 assets/voxygen/i18n/ru_RU/char_selection.ron create mode 100644 assets/voxygen/i18n/ru_RU/common.ron create mode 100644 assets/voxygen/i18n/ru_RU/gameinput.ron diff --git a/assets/voxygen/i18n/ru_RU/_manifest.ron b/assets/voxygen/i18n/ru_RU/_manifest.ron index b3844898b4..25d1d25c93 100644 --- a/assets/voxygen/i18n/ru_RU/_manifest.ron +++ b/assets/voxygen/i18n/ru_RU/_manifest.ron @@ -1,18 +1,6 @@ -/// Translation document instructions -/// -/// In order to keep localization documents readible please follow the following -/// rules: -/// - separate the string map sections using a commentary describing the purpose -/// of the next section -/// - prepend multi-line strings with a commentary -/// - append one blank lines after a multi-line strings and two after sections -/// -/// To add a new language in Veloren, just write an additional `.ron` file in -/// `assets/voxygen/i18n` and that's it! -/// /// WARNING: Localization files shall be saved in UTF-8 format without BOM -/// Localization for "global" Russian +/// Localization for RUS ( metadata: ( language_name: "Русский", @@ -41,516 +29,9 @@ scale_ratio: 1.0, ), }, - sub_directories: [], + sub_directories: [], + string_map: { - /// Start Common section - // Texts used in multiple locations with the same formatting - "common.username": "Имя пользователя", - "common.singleplayer": "Одиночная игра", - "common.multiplayer": "Мультиплеер", - "common.servers": "Сервера", - "common.quit": "Выход", - "common.settings": "Настройки", - "common.languages": "Язык", - "common.interface": "Интерфейс", - "common.gameplay": "Геймплей", - "common.controls": "Управление", - "common.video": "Видео", - "common.sound": "Звук", - "common.languages": "Языки", - "common.resume": "Продолжить", - "common.characters": "Персонажи", - "common.close": "Закрыть", - "common.yes": "Да", - "common.no": "Нет", - "common.back": "Назад", - "common.create": "Создать", - "common.okay": "Окей", - "common.accept": "Принять", - "common.decline": "Отклонить", - "common.disclaimer": "Дисклеймер", - "common.cancel": "Отмена", - "common.none": "Нет", - "common.error": "Ошибка", - "common.fatal_error": "Критическая ошибка", - "common.you": "Вы", - "common.automatic": "Авто", - "common.random": "Случайно", - // Settings Window title - "common.interface_settings": "Настройки интерфейса", - "common.gameplay_settings": "Настройки геймплея", - "common.controls_settings": "Настройки управления", - "common.video_settings": "Графические настройки", - "common.sound_settings": "Звуковые настройки", - "common.language_settings": "Языковые настройки", - - // Message when connection to the server is lost - "common.connection_lost": r#"Соединение потеряно! -Сервер перезагрузился? -Клиент обновлен до последней версии?"#, - - - "common.species.orc": "Орк", - "common.species.human": "Человек", - "common.species.dwarf": "Дварф", - "common.species.elf": "Эльф", - "common.species.undead": "Нежить", - "common.species.danari": "Данари", - - "common.weapons.axe": "Топор", - "common.weapons.sword": "Меч", - "common.weapons.staff": "Посох", - "common.weapons.bow": "Лук", - "common.weapons.hammer": "Молот", - "common.weapons.sceptre": "Лечащий посох", - "common.rand_appearance": "Случайная внешность и имя", - /// End Common section - - - /// Start Main screen section - "main.username": "Имя пользователя", - "main.server": "Сервер", - "main.password": "Пароль", - "main.connecting": "Подключение", - "main.creating_world": "Создание мира", - "main.tip": "Совет:", - - // Welcome notice that appears the first time Veloren is started - "main.notice": r#"Добро пожаловать в Veloren-Alpha! - -Прежде чем начать веселье, прими во внимание следующие вещи: - -- Это очень ранняя альфа. Тут есть баги, крайне незавершенный геймплей, неотполированные механики и отсутсвующие фичи. - -- Если у вас есть конструктивный фидбек или сообщение об ошибке, вы можете связаться с нами через Reddit, GitLab или наш Discord-сервер. - -- Veloren лицензирован GPL 3 open-source licence. Это означает, игра бесплатна, ее можно модифицировать и переделывать на свой вкус (при условии, что готовая работа тоже лицензирована GPL 3). - -- Veloren - некоммерческий проект, каждый работает над ним добровольно. -Если тебе нравится, что ты видишь, милости просим присоединиться к команде разработчиков или художественной команде! - -Спасибо за прочтение, мы надеемся, вам понравится игра! - -~ Команда разработчиков Veloren"#, - - // Login process description - "main.login_process": r#"Информация по входу: - -Обратите внимание, что теперь вам нужен аккаунт -играть на серверах с включенной аутентификацией. - -Вы можете создать аккаунт тут: - -https://veloren.net/account/."#, - "main.login.server_not_found": "Сервер не найден", - "main.login.authentication_error": "Ошибка аутентификации на сервер", - "main.login.server_full": "Сервер полон", - "main.login.untrusted_auth_server": "Аутентификация не пройдена", - "main.login.outdated_client_or_server": "ServerWentMad: Возможно, версии несовместимы. Проверьте наличие обновлений.", - "main.login.timeout": "Timeout: Сервер не ответил вовремя. (Перегрузка или проблемы с сетью).", - "main.login.server_shut_down": "Сервер выключен", - "main.login.network_error": "Ошибка сети", - "main.login.failed_sending_request": "Запрос аутентификации провален", - "main.login.invalid_character": "Выбранный персонаж недоступен", - "main.login.client_crashed": "Клиент вылетел", - "main.login.not_on_whitelist": "Чтобы войти, необходимо быть внесенным в Вайтлист", - "main.login.banned": "Вы были забанены по следующей причине", - "main.login.kicked": "Вы были кикнуты по следующей причине", - "main.login.select_language": "Выбрать язык", - - "main.servers.select_server": "Выбрать сервер", - - /// End Main screen section - - - /// Start HUD Section - "hud.do_not_show_on_startup": "Не показывать это при запуске", - "hud.show_tips": "Показать советы", - "hud.quests": "Квесты", - "hud.you_died": "Вы мертвы", - "hud.waypoint_saved": "Точка спауна сохранена", - - "hud.press_key_to_show_keybindings_fmt": "[{key}] Раскладка", - "hud.press_key_to_toggle_lantern_fmt": "[{key}] Фонарь", - "hud.press_key_to_show_debug_info_fmt": "Нажмите {key}, чтобы показать панель отладки", - "hud.press_key_to_toggle_keybindings_fmt": "Нажмите {key}, чтобы показать раскладку", - "hud.press_key_to_toggle_debug_info_fmt": "Нажмите {key}, чтобы включить панель отладки", - - // Chat outputs - "hud.chat.online_msg": "[{name}] сейчас онлайн.", - "hud.chat.offline_msg": "[{name}] сейчас оффлайн.", - - "hud.chat.default_death_msg": "[{name}] умер", - "hud.chat.environmental_kill_msg": "[{name}] умер от {environment}", - "hud.chat.fall_kill_msg": "[{name}] разбился", - "hud.chat.suicide_msg": "[{name}] сам стал виной своей смерти", - - "hud.chat.pvp_melee_kill_msg": "[{attacker}] одолел [{victim}]", - "hud.chat.pvp_ranged_kill_msg": "[{attacker}] подстрелил [{victim}]", - "hud.chat.pvp_explosion_kill_msg": "[{attacker}] взорвал [{victim}]", - "hud.chat.pvp_energy_kill_msg": "[{attacker}] убил [{victim}] с помощью магии", - "hud.chat.pvp_buff_kill_msg": "[{attacker}] убил [{victim}]", - - - "hud.chat.npc_melee_kill_msg": "{attacker} убил [{victim}]", - "hud.chat.npc_ranged_kill_msg": "{attacker} подстрелил [{victim}]", - "hud.chat.npc_explosion_kill_msg": "{attacker} взорвал [{victim}]", - "hud.chat.npc_energy_kill_msg": "{attacker} убил [{victim}] с помощью магии", - "hud.chat.npc_other_kill_msg": "{attacker} убил [{victim}]", - - "hud.chat.loot_msg": "Вы подобрали [{item}]", - "hud.chat.loot_fail": "Ваш инвентарь полон!", - "hud.chat.goodbye": "До встречи!", - "hud.chat.connection_lost": "Соединение потеряно. Кик через {time} секунд.", - - // SCT outputs - "hud.sct.experience": "{amount} Опыт", - "hud.sct.block": "ЗАБЛОКИРОВАНО", - - // Respawn message - "hud.press_key_to_respawn": r#"Нажмите {key}, чтобы возродиться на последнем костре, который вы посетили."#, - - // Welcome message - "hud.welcome": r#"Добро пожаловать в Veloren-Alpha!, - - -Немного советов перед тем началом игры: - - -Нажмите F1, чтобы увидеть доступые команды. - -Напишите /help, чтобы увидеть команды чата. - - -В мире есть сундуки и другие рандомно генерируемые объекты! - -Нажмите ПКМ, чтобы собрать их. - -Чтобы использовать то, что вы нашли в сундуках, откройте свой инвентарь 'B'. - -Двойной клик на предмет в инвентаре использует или экипирует его. - -Чтобы выкинуть его, кликните на предмет, а потом кликните вне инвентаря. - - -Ночи в Veloren могут быть довольно темными. - -Зажгите свой фонарь, нажав на 'G'. - - -Хотите увидеть курсор, чтобы закрыть это окно? Нажмите TAB! - - -Наслаждайтесь миром Veloren."#, - -"hud.temp_quest_headline": r#"Пожалуйста, путешественник, помоги нам!"#, -"hud.temp_quest_text": r#"Подземелья наполнены злыми культистами, -которые появились вокруг наших мирных городов! - - -Собери компанию, запасись едой -и победи мерзкого лидера и его приспешников. - - -Может быть ты даже сможешь получить один из их -магических предметов?"#, - - - - // Inventory - "hud.bag.inventory": "Инвентарь", - "hud.bag.stats_title": "Характеристики", - "hud.bag.exp": "Опыт", - "hud.bag.armor": "Броня", - "hud.bag.stats": "Статы", - "hud.bag.head": "Голова", - "hud.bag.neck": "Шея", - "hud.bag.tabard": "Накидка", - "hud.bag.shoulders": "Плечи", - "hud.bag.chest": "Нагрудник", - "hud.bag.hands": "Руки", - "hud.bag.lantern": "Фонарь", - "hud.bag.glider": "Глайдер", - "hud.bag.belt": "Пояс", - "hud.bag.ring": "Кольцо", - "hud.bag.back": "Спина", - "hud.bag.legs": "Ноги", - "hud.bag.feet": "Ботинки", - "hud.bag.mainhand": "Главная рука", - "hud.bag.offhand": "Второстепенная рука", - - - // Map and Questlog - "hud.map.map_title": "Карта", - "hud.map.qlog_title": "Квесты", - - // Settings - "hud.settings.general": "Общие", - "hud.settings.none": "Нет", - "hud.settings.press_behavior.toggle": "Переключить", - "hud.settings.press_behavior.hold": "Держать", - "hud.settings.help_window": "Окно помощи", - "hud.settings.debug_info": "Панель отладки", - "hud.settings.tips_on_startup": "Советы на старте", - "hud.settings.ui_scale": "Размер интерфейса", - "hud.settings.relative_scaling": "Относительное масштабирование", - "hud.settings.custom_scaling": "Пользовательское масштабирование", - "hud.settings.crosshair": "Перекрестие", - "hud.settings.transparency": "Прозрачность", - "hud.settings.hotbar": "Хотбар", - "hud.settings.toggle_shortcuts": "Отображать ярлыки", - "hud.settings.buffs_skillbar": "Баффы на панели навыков", - "hud.settings.buffs_mmap": "Баффы на миникарте", - "hud.settings.toggle_bar_experience": "Отображать полоску опыта", - "hud.settings.scrolling_combat_text": "Боевой журнал", - "hud.settings.single_damage_number": "Отдельные числа урона", - "hud.settings.cumulated_damage": "Суммарный урон", - "hud.settings.incoming_damage": "Входящий урон", - "hud.settings.cumulated_incoming_damage": "Суммарный входящий урон", - "hud.settings.speech_bubble": "Текстовые облачка", - "hud.settings.speech_bubble_dark_mode": "Темный режим текстовых облачков", - "hud.settings.speech_bubble_icon": "Значок текстовых облачков", - "hud.settings.energybar_numbers": "Отображение полоски энергии", - "hud.settings.values": "Значение", - "hud.settings.percentages": "Проценты", - "hud.settings.chat": "Чат", - "hud.settings.background_transparency": "Прозрачность заднего фона", - "hud.settings.chat_character_name": "Имя персонажа в чате", - "hud.settings.loading_tips": "Советы при загрузке", - - "hud.settings.pan_sensitivity": "Чувствительность камеры", - "hud.settings.zoom_sensitivity": "Чувствительность зума", - "hud.settings.invert_scroll_zoom": "Инвертировать прокрутку зума", - "hud.settings.invert_mouse_y_axis": "Инвертировать ось Y", - "hud.settings.enable_mouse_smoothing": "Размытие камеры", - "hud.settings.free_look_behavior": "Настройка свободной камеры", - "hud.settings.auto_walk_behavior": "Автодвижение", - "hud.settings.stop_auto_walk_on_input": "Остановить автодвижение на кнопку движения", - - "hud.settings.view_distance": "Дальность прорисовки", - "hud.settings.sprites_view_distance": "Дальность прорисовки спрайтов", - "hud.settings.figures_view_distance": "Дальность прорисовки объектов", - "hud.settings.maximum_fps": "Максимум FPS", - "hud.settings.fov": "Поле зрения (градусы)", - "hud.settings.gamma": "Гамма", - "hud.settings.exposure": "Экспозиция", - "hud.settings.ambiance": "Яркость окружения", - "hud.settings.antialiasing_mode": "Сглаживание", - "hud.settings.cloud_rendering_mode": "Рендер облаков", - "hud.settings.fluid_rendering_mode": "Рендер жидкостей", - "hud.settings.fluid_rendering_mode.cheap": "Низко", - "hud.settings.fluid_rendering_mode.shiny": "Высоко", - "hud.settings.cloud_rendering_mode.minimal": "Минимально", - "hud.settings.cloud_rendering_mode.low": "Низко", - "hud.settings.cloud_rendering_mode.medium": "Средне", - "hud.settings.cloud_rendering_mode.high": "Высоко", - "hud.settings.cloud_rendering_mode.ultra": "Ультра", - "hud.settings.fullscreen": "Полный экран", - "hud.settings.fullscreen_mode": "Режим полного экрана", - "hud.settings.fullscreen_mode.exclusive": "Особый", - "hud.settings.fullscreen_mode.borderless": "Без рамок", - "hud.settings.particles": "Частицы", - "hud.settings.resolution": "Разрешение", - "hud.settings.bit_depth": "Разрядность", - "hud.settings.refresh_rate": "Частота обновления", - "hud.settings.save_window_size": "Сохранить размер окна", - "hud.settings.lighting_rendering_mode": "Режим рендера освещения", - "hud.settings.lighting_rendering_mode.ashikhmin": "Тип A - Высоко ", - "hud.settings.lighting_rendering_mode.blinnphong": "Тип B - Средне", - "hud.settings.lighting_rendering_mode.lambertian": "Тип L - Низко", - "hud.settings.shadow_rendering_mode": "Режим рендера теней", - "hud.settings.shadow_rendering_mode.none": "Нет", - "hud.settings.shadow_rendering_mode.cheap": "Низко", - "hud.settings.shadow_rendering_mode.map": "Карта", - "hud.settings.shadow_rendering_mode.map.resolution": "Разрешение", - "hud.settings.lod_detail": "LoD-детали", - "hud.settings.save_window_size": "Сохранить размер окна", - - - "hud.settings.music_volume": "Громкость музыки", - "hud.settings.sound_effect_volume": "Громкость звуковых эффектов", - "hud.settings.audio_device": "Аудио устройство", - - "hud.settings.awaitingkey": "Нажми клавишу...", - "hud.settings.unbound": "Ничего", - "hud.settings.reset_keybinds": "По-умолчанию", - - "hud.social": "Другие игроки", - "hud.social.online": "Онлайн", - "hud.social.friends": "Друзья", - "hud.social.not_yet_available": "Пока недоступно", - "hud.social.faction": "Фракция", - "hud.social.play_online_fmt": "{nb_player} игрок(ов) онлайн", - "hud.social.name": "Имя", - "hud.social.level": "Ур.", - "hud.social.zone": "Зона", - "hud.social.account": "Аккаунт", - - - "hud.crafting": "Крафт", - "hud.crafting.recipes": "Рецепты", - "hud.crafting.ingredients": "Ингредиенты:", - "hud.crafting.craft": "Создать", - "hud.crafting.tool_cata": "Требуется:", - - "hud.group": "Группа", - "hud.group.invite_to_join": "[{name}] пригласил вас в свою группу!", - "hud.group.invite": "Пригласить", - "hud.group.kick": "Кикнуть", - "hud.group.assign_leader": "Назначить лидером", - "hud.group.leave": "Покинуть группу", - "hud.group.dead" : "Мертв", - "hud.group.out_of_range": "Слишком далеко", - "hud.group.add_friend": "Добавить в друзья", - "hud.group.link_group": "Объединить группы", - "hud.group.in_menu": "В меню", - "hud.group.members": "Участники группы", - - "hud.spell": "Заклинания", - - "hud.free_look_indicator": "Свободная камера активна. Нажмите {key}, чтобы выключить", - "hud.auto_walk_indicator": "Автодвижение активно", - - "hud.map.difficulty": "Сложность", - "hud.map.towns": "Города", - "hud.map.castles": "Замки", - "hud.map.dungeons": "Данжи", - "hud.map.caves": "Пещеры", - "hud.map.cave": "Пещера", - "hud.map.town": "Город", - "hud.map.castle": "Замок", - "hud.map.dungeon": "Данж", - "hud.map.difficulty_dungeon": "Сложность данжа: {difficulty}", - "hud.map.drag": "Перетащить", - "hud.map.zoom": "Зум", - "hud.map.recenter": "Рецентрировать", - - /// End HUD section - - - /// Start GameInput section - - "gameinput.primary": "Основная атака", - "gameinput.secondary": "Второстепенная атака/Блок/Прицел", - "gameinput.slot1": "Быстрый слот 1", - "gameinput.slot2": "Быстрый слот 2", - "gameinput.slot3": "Быстрый слот 3", - "gameinput.slot4": "Быстрый слот 4", - "gameinput.slot5": "Быстрый слот 5", - "gameinput.slot6": "Быстрый слот 6", - "gameinput.slot7": "Быстрый слот 7", - "gameinput.slot8": "Быстрый слот 8", - "gameinput.slot9": "Быстрый слот 9", - "gameinput.slot10": "Быстрый слот 10", - "gameinput.swaploadout": "Сменить снаряжение", - "gameinput.togglecursor": "Отображать курсор", - "gameinput.help": "Отображать окно помощи", - "gameinput.toggleinterface": "Отображать интерфейс", - "gameinput.toggledebug": "Отображать FPS и экран отладки", - "gameinput.screenshot": "Сделать скриншот", - "gameinput.toggleingameui": "Отображать неймтеги", - "gameinput.fullscreen": "Включить полный экран", - "gameinput.moveforward": "Двигаться вперед", - "gameinput.moveleft": "Двигаться влево", - "gameinput.moveright": "Двигаться вправо", - "gameinput.moveback": "Двигаться назад", - "gameinput.jump": "Прыжок", - "gameinput.glide": "Глайдер", - "gameinput.roll": "Кувырок", - "gameinput.climb": "Карабкаться", - "gameinput.climbdown": "Карабкаться вниз", - "gameinput.wallleap": "Прыжок от стены", - "gameinput.togglelantern": "Включить фонарь", - "gameinput.mount": "Оседлать", - "gameinput.enter": "Войти", - "gameinput.chat": "Чат", - "gameinput.command": "Командовать", - "gameinput.escape": "Выйти", - "gameinput.map": "Карта", - "gameinput.bag": "Рюкзак", - "gameinput.social": "Социальное", - "gameinput.sit": "Сесть", - "gameinput.spellbook": "Заклинания", - "gameinput.settings": "Настройки", - "gameinput.respawn": "Возродиться", - "gameinput.charge": "Зарядить", - "gameinput.togglewield": "Достать/убрать оружие", - "gameinput.interact": "Взаимодействовать", - "gameinput.freelook": "Свободная камера", - "gameinput.autowalk": "Автодвижение", - "gameinput.dance": "Танцевать", - "gameinput.select": "Выбрать объект", - "gameinput.acceptgroupinvite": "Принять приглашение в группу", - "gameinput.declinegroupinvite": "Отклонить приглашение в группу", - "gameinput.crafting": "Крафт", - "gameinput.fly": "Полет", - "gameinput.sneak": "Скрытность", - "gameinput.swimdown": "Плыть вниз", - "gameinput.swimup": "Плыть вверх", - - /// End GameInput section - - - /// Start chracter selection section - "char_selection.loading_characters": "Загрузка персонажей...", - "char_selection.delete_permanently": "Навсегда удалить этого персонажа?", - "char_selection.deleting_character": "Удаление персонажа...", - "char_selection.change_server": "Сменить сервер", - "char_selection.enter_world": "Войти в мир", - "char_selection.logout": "Выйти в меню", - "char_selection.create_new_character": "Создать нового персонажа", - "char_selection.creating_character": "Создание персонажа...", - "char_selection.character_creation": "Создание персонажа", - - "char_selection.human_default": "Стандартный человек", - "char_selection.level_fmt": "Уровень {level_nb}", - "char_selection.uncanny_valley": "Wilderness", - "char_selection.plains_of_uncertainty": "Plains of Uncertainty", - "char_selection.beard": "Борода", - "char_selection.hair_style": "Прическа", - "char_selection.hair_color": "Цвет волос", - "char_selection.eye_color": "Цвет глаз", - "char_selection.skin": "Кожа", - "char_selection.eyeshape": "Детали глаз", - "char_selection.accessories": "Аксессуары", - "char_selection.create_info_name": "Вашему персонажу необходимо имя!", - - /// End chracter selection section - - - /// Start character window section - "character_window.character_name": "Имя персонажа", - // Charater stats - "character_window.character_stats": r#"Стойкость - -Выносливость - -Сила воли - -Защита -"#, - /// End character window section - - - /// Start Escape Menu Section - "esc_menu.logout": "Выйти в меню", - "esc_menu.quit_game": "Выйти из игры", - /// End Escape Menu Section - - /// Buffs and Debuffs - "buff.remove": "Кликните, чтобы убрать", - "buff.title.missing": "Отсутствует название", - "buff.desc.missing": "Отсутствует описание", - // Buffs - "buff.title.heal": "Лечение", - "buff.desc.heal": "Лечит в течении времени.", - "buff.title.potion": "Зелье", - "buff.desc.potion": "Пьем...", - "buff.title.saturation": "Сыт", - "buff.desc.saturation": "Получайте здоровье от расходников в течении времени.", - // Debuffs - "buff.title.bleed": "Кровотечение", - "buff.desc.bleed": "Наносит переодический урон.", }, @@ -558,105 +39,196 @@ https://veloren.net/account/."#, vector_map: { "loading.tips": [ "Нажмите 'G', чтобы зажечь фонарь.", - "Нажмите 'F1', чтобы увидеть управление по-умолчанию.", - "Вы можете написать /say или /s, чтобы обратиться только к игрокам вблизи вас.", - "Вы можете написать /region или /r, чтобы обратиться к игрокам в нескольких сотнях блоков вокруг вас.", - "Вы можете написать /group или /g, чтобы обратиться к игрокам в вашей группе.", - "Чтобы отправить приватное сообщение, напишите /tell, а затем имя персонажа и сообщение.", - "NPC одного уровня могут быть разной сложности.", - "Осматривайтесь, чтобы найти еду, сундуки и другой лут!", - "Инвентарь забит едой? Попробуйте скрафтить из нее еду получше!", - "Думаете, чем заняться? Подземелья отмечены коричневыми метками на карте!", - "Не забудьте настроить графику под свою систему. Нажмите 'N', чтобы открыть настройки.", - "Играть с остальными веселее! Нажмите 'O', чтобы посмотреть кто онлайн.", - "NPC с черепом около полоски здоровья намного сильнее, по сравнению с вами.", - "Нажмите 'J', чтобы танцевать. Тусовка!", - "Нажмите 'L-Shift', чтобы открыть Глайдер и покорить небеса.", - "Veloren все еще пре-альфа. Мы стараемся улучшать его каждый день!", - "Если вы хотите присоединиться к команде разработчиков или просто пообщаться с нами, заходите на наш Дискорд-сервер.", - "Вы можете включить отображение чисел на полосе здоровья в настройках.", - "Чтобы увидеть свои характеристики нажмите на 'Статы' в инвентаре.", + "Нажмите 'F1', чтобы просмотреть все клавиши по умолчанию.", + "Вы можете ввести /tell или /s, чтобы общаться только с игроками непосредственно вокруг вас.", + "Вы можете ввести /region или /r, чтобы общаться только с игроками в паре сотен блоков вокруг вас.", + "Администраторы могут использовать команду /build для входа в режим постройки.", + "Вы можете ввести /group или /g, чтобы общаться только с игроками в вашей текущей группе.", + "Чтобы отправить личные сообщения, введите /tell, а затем имя игрока и ваше сообщение.", + "Смотрите внимательно чтобы найти еду, сундуки и другие предметы, разбросанные по всему миру!", + "Инвентарь, заполненный едой? Попробуйте приготовить из нее еду получше!", + "Интересно, чем можно заняться? Попробуйте пройти одно из подземелий, отмеченных на карте!", + "Не забудьте настроить графику для вашей системы. Нажмите 'N', чтобы открыть настройки.", + "Играть с другими-это весело! Нажмите 'О', чтобы узнать, кто находится в сети.", + "Нажмите 'J', чтобы танцевать. Вечеринка!", + "Нажмите 'L-Shift', чтобы открыть свой дельтаплан и покорить небо.", + "Veloren все еще находится в Пре-Альфе. Мы делаем все возможное, чтобы улучшать его каждый день!", + "Если вы хотите присоединиться к команде разработчиков или просто пообщаться с нами, присоединяйтесь к нашему серверу Discord.", + "Вы можете переключить отображение количества здоровья на панели здоровья в настройках.", + "Сядьте у костра (с помощью клавиши 'К'), чтобы залечить свои раны.", + "Вам нужно больше сумок или лучшая броня, чтобы продолжить свое путешествие? Нажмите 'C', чтобы открыть меню крафта!", + ], + "npc.speech.villager": [ + "Хорошая погода?", + "Как дела?", + "Доброе утро!", + "Интересно, что думает Catoblepas когда ест траву.", /// in original Catoblepas (киса) но яб сменил на овцу в оригинале + "Хорошая погода, не правда ли?", + "Мысли об этих подземельях пугают меня. Надеюсь их кто-нибудь уничтожит", + "Когда я вырасту, я буду исследовать пещеры.", + "Вы не видели моего кота?", + "Вы когда-нибудь слышали о свирепых сухопутных акулах? Я слышал, они живут в пустынях", + "Говорят, в пещерах можно найти блестящие драгоценные камни всех видов.", + "Я просто помешан на сыре!", + "Ты не зайдешь? Мы как раз собирались съесть немного сыра", + "Говорят, мухоморы полезны для здоровья. Сам я их не ем.", + "Не забудь про печенья!", + "Я просто обожаю сыр дварфов. Я бы хотел научиться его готовить.", + "Интересно, что по ту сторону гор.", + "Я надеюсь сделать свой собственный дельтаплан когда-нибудь.", + "Хочешь, покажу тебе свой сад? Ладно, может в следующий раз.", + "Прекрасный день для прогулки по лесу!", + "Быть или не быть? Я подумываю о том чтобы стать фермером.", + "Тебе не кажется, что наша деревня самая лучшая?", + "Как ты думаешь, что заставляет Glowing Remains светься?", + "Время второго завтрака!", + "Ты когда - нибудь ловил светлячка?", + "I just can't understand where those Sauroks keep coming from.", + "Я бы хотел, чтобы кто-нибудь держал волков подальше от деревнию.", + "Прошлой ночью мне приснился чудесный сон о сыре. Что это значит?", + "Я оставила немного сыра у брата. Теперь я не знаю, съеден сыр или нет. Я называю его сыром Шредингера.", + "Я оставил немного сыра у сестры. Теперь я не знаю, съеден сыр или нет. Я называю его сыром Шредингера.", + "Кто-то должен что-то сделать с этими культистами. Желательно не я.", + "Надеюсь, скоро пойдет дождь. Это было бы хорошо для урожая.", + "Я люблю мед! И я ненавижу пчел.", + "Я хочу однажды увидеть мир. В жизни должно быть что-то большее, чем эта деревня.", + ], + "npc.speech.villager_decline_trade": [ + "Извините, мне нечем торговать.", + "Торговля? Как будто у меня есть что-то, что может вас заинтересовать.", + "Мой дом принадлежит мне, я не променяю его ни на что.", + ], + "npc.speech.merchant_advertisement": [ + "Могу ли я заинтересовать вас сделкой?", + "Ты хочешь со мной поторговать?", + "У меня много товаров, не хочешь взглянуть?" + ], + "npc.speech.merchant_busy": [ + "Эй, подожди своей очереди.", + "Пожалуйста, подождите, я здесь один на всех.", + "Видите другого человека перед собой?", + "Минутку, дай мне закончить.", + "Не лезь вне очереди!", + "Я занят, зайди попозже." + ], + "npc.speech.merchant_trade_successful": [ + "Спасибо, что торгуете со мной!", + "Спасибо вам!", + ], + "npc.speech.merchant_trade_declined": [ + "Может быть, в другой раз, хорошего дня!", + "Жаль, тогда, может быть, в следующий раз!" + ], + "npc.speech.villager_cultist_alarm": [ + "Берегись! На свободе разгуливает культист!", + "К оружию! Культисты атакуют!", + "Как посмели культисты напасть на нашу деревню!", + "Смерть культистам!", + "Культистов здесь не потерпят!", + "Кровожадный культист!", + "Попробуй на вкус острие моего меча, грязный культист!", + "Ничего не сможет смыть кровь с твоих рук, культист!", + "Миллиарды пузырящихся синих ракушек! Культист среди нас!", + "Зло этого культиста вот-вот закончится!", + "Этот культист мой!", + "Приготовься встретить своего создателя, грязный культист!", + "Я вижу культиста! Схватите его!", + "Я вижу культиста! В атаку!", + "Я вижу культиста! Не дайте им сбежать!", + "Будет ли самый почтенный культист заботиться о какой-то СМЕРТИ?!", + "Никогда не прощу! Никогда не забуду! Культист, сожалею!", + "Умри, культист!", + "Ваше царство террора захвачено!", + "Вот тебе за все, что ты сделал!", + "Мы не очень хорошо относимся к вашим людям здесь.", + "Тебе следовало оставаться под землей!", ], "npc.speech.villager_under_attack": [ - "Помогите, меня атакуют!", - "Помогите, меня атакуют!", - "Ай! Меня атакуют!", - "Ай! Меня атакуют! На помощь!", - "Помогите мне! Меня атакуют!", + "Помогите, Меня атакуют!", + "Помогите, Меня атакуют!", + "Оуч, Меня атакуют!", + "Оуч, Меня атакуют!", + "Помоги мне! Меня атакуют!", "Меня атакуют! Помогите!", - "Меня атакуют!! На помощь!", + "Меня атакуют! Помогите мне!", "Помогите!", - "На помощь! На помощь!", + "Помогите! Помогите!", "Помогите! Помогите! Помогите!", "Меня атакуют!", - "ААА! Меня атакуют!", - "AAA! Меня атакуют! На помощь!", - "Помогите! Нас атакуют!", - "На помощь! Убийца!", - "Помогите! Здесь убийца!", - "На помощь! Меня пытаются убить!", - "Стража, меня атакуют!", - "Стража, на помощь!", + "АААААА! Меня атакуют!", + "АААААА! Меня атакуют! Помогите!", + "Помогите! Мы атакованны!", + "Помогите! Убийца!", + "Помогите! Убийца на свободе!", + "Помогите! Они пытаются меня убить!", + "Стража, Меня атакуют!", + "Стража! Меня атакуют!", "Меня атакуют! Стража!", "Помогите! Стража! Меня атакуют!", - "Стража! Cкорее!", + "Стража! Скорее!", "Стража! Стража!", - "Стража! На меня напали!", - "Стража, убейте этого мерзкого злодея", - "Стража! Тут убийца!", - "Стража! Помогите мне!", - "Тебе это не сойдет с рук! Охрана!", - "Ты враг!", - "Помогите!", - "На помощь! Пожалуйста!", - "Ай! Стража, помогите!", - "Они пришли за мной!", - "На помощь, на помощь, на меня напали!", + "Стража! Этот злодей бьёт меня!", + "Стража, Схватите этого негодяя!", + "Стража! Здесь убийца!", + "Стража! Помогите me!", + "Тебе это не сойдет с рук! Стража!", + "Ты изверг!", + "Помогите мне!", + "Помогите! Пожалуйста!", + "Ой! Стража! Помогите!", + "Они идут за мной!", + "Помогите! Помогите! Меня постигла расплата!", "Ах, теперь мы видим насилие, присущее системе.", "Это всего лишь царапина!", - "Прекрати!", - "Что я вообще тебе сделал?!", - "Пожалуйста, прекрати меня бить!", - "Эй, поаккуратнее с этой штукой!", - "Мерзкий негодняй, отстань!", - "Остановись! Уходи!", - "Ты злишь меня!", - "Ай! Кем ты себя возомнил?!", - "Я лишу тебя головы за это!", - "Остановись! У меня нет ничего ценного!", - "Я натравлю на тебя братьев! Они больше меня!", - "Нееет, я расскажу маме!", + "Остановитесь!", + "Что я тебе сделал?!", + "Пожалуйста, не бей!", + "Эй! Смотри, куда направляешь эту штуку", + "Гнусный негодяй, проваливай отсюда!", + "Прекрати! Уходи!", + "Ты уже достал!", + "Эй! Что ты возомнил о себе?!", + "Я тебе башку оторву!", + "Остановись пожалуйста. У меня ничего нет!", + "Я позову брата, он больше меня", + "Нет! Я расскажу маме!", "Будь ты проклят!", - "Пожалуйста, не надо!", - "Это было недружелюбно!", - "Хорошо, ты сильный, а теперь убери оружие!", - "Пощади меня!", + "Пожалуйста, не делай этого.", + "Это не приятно!", + "Ваше оружие работает, вы можете убрать его прямо сейчас!", + "Пощади!", "Пожалуйста, у меня семья!", - "Я слишком молод, чтобы умирать!", - "Мы можем решить все словами?", + "Я слишком молод чтобы умереть!", + "Может договоримся?", "Насилие не выход!", - "Так и знал, что день будет плохим...", - "Эй, больно же!", - "Эй!", - "Как некультурно!", - "Остановись, я прошу!", - "Проклятие!", + "Сегодня выдался плохой день...", + "Эй, это больно!", + "Ик!", + "Как грубо!", + "Остановись, прошу тебя!", + "Чтоб ты сдох!", "Это не смешно.", "Как ты смеешь?!", "Ты заплатишь за это!", - "Не продолжай, а то пожалеешь!", - "Не заставляй делать тебе больно!", - "Ты все неправильно понял!", - "Зачем ты так?!", - "Проваливай, вражина!", - "Это было больно!", - "Почему ты это делаешь?", - "Ради духов, уймись!", - "Ты меня с кем-то спутал!", - "Я не заслужил этого!", - "Пожалуйста, не делай так больше.", - "Стража, киньте этого монстра в озеро!", - "Я натравлю на тебя своего тараска!", + "Ты об этом пожалеешь!", + "Не заставляй меня делать тебе больно!", + "Произошла какая то ошибка!", + "Не делай этого!", + "Изыди, дьявол", + "Это очень больно!", + "Зачем ты это сделал?", + "Ради всего святого, прекрати!", + "Ты меня перепутал с кем то", + "Я не заслуживаю этого!", + "Пожалуйста, больше так не делай.", + "Стража, утопите этого монстра в озере!", + "Я натравлю своего tarasque на тебя!", + "Почему я?", ], + "npc.speech.villager_enemy_killed": [ + "Я уничтожил врага!", + "Наконец-то мир!", + "... что же я наделал?", + ] } ) diff --git a/assets/voxygen/i18n/ru_RU/char_selection.ron b/assets/voxygen/i18n/ru_RU/char_selection.ron new file mode 100644 index 0000000000..a990ea7775 --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/char_selection.ron @@ -0,0 +1,32 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "char_selection.loading_characters": "Загрузка персонажей...", + "char_selection.delete_permanently": "Навсегда удалить этого персонажа?", + "char_selection.deleting_character": "Удаление Персонажа...", + "char_selection.change_server": "Сменить сервер", + "char_selection.enter_world": "Войти в мир", + "char_selection.logout": "Разлогиниться", + "char_selection.create_new_character": "Создать нового персонажа", + "char_selection.creating_character": "Создание персонажа...", + "char_selection.character_creation": "Персонаж создан", + "char_selection.human_default": "Стандартный человек", + "char_selection.level_fmt": "Уровень {level_nb}", + "char_selection.uncanny_valley": "Wilderness", + "char_selection.plains_of_uncertainty": "Plains of Uncertainty", + "char_selection.beard": "Борода", + "char_selection.hair_style": "Прическа", + "char_selection.hair_color": "Цвет волос", + "char_selection.eye_color": "Цвет глаз", + "char_selection.skin": "Кожа", + "char_selection.eyeshape": "Детали глаз", + "char_selection.accessories": "Аксессуары", + "char_selection.create_info_name": "Вашему персонажу нужно имя!", + "char_selection.version_mismatch": "ПРЕДУПРЕЖДЕНИЕ! На этом сервере работает другая, возможно, несовместимая версия игры. Пожалуйста, обновите свою игру.", + }, + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/common.ron b/assets/voxygen/i18n/ru_RU/common.ron new file mode 100644 index 0000000000..b72812e10c --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/common.ron @@ -0,0 +1,117 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + // Texts used in multiple locations with the same formatting + "common.username": "Юзернейм", + "common.singleplayer": "Одиночная игра", + "common.multiplayer": "Онлайн игра", + "common.servers": "Сервера", + "common.quit": "Выход", + "common.settings": "Настройки", + "common.languages": "Языки", + "common.interface": "Интерфейс", + "common.gameplay": "Гемплей", + "common.controls": "Управление", + "common.video": "Графика", + "common.sound": "Звук", + "common.chat": "Чат", + "common.resume": "Продолжить", + "common.characters": "Персонажи", + "common.close": "Закрыть", + "common.yes": "Да", + "common.no": "Нет", + "common.back": "Назад", + "common.create": "Создание", + "common.okay": "Окей", + "common.add": "Добавить", + "common.accept": "Принять", + "common.decline": "Отказаться", + "common.disclaimer": "Предупреждение", + "common.cancel": "Отменить", + "common.none": "Отключены", + "common.error": "Ошибка", + "common.fatal_error": "Фатальная ошибка", + "common.you": "Вы", + "common.automatic": "Авто", + "common.random": "Случайно", + "common.empty": "Пустой", + + // Settings Window title + "common.interface_settings": "Настройки интерфейса", + "common.gameplay_settings": "Настройки гемплея", + "common.controls_settings": "Настройки управления", + "common.video_settings": "Графические настройки", + "common.sound_settings": "Настройки звука", + "common.language_settings": "Настройки языка", + "common.chat_settings": "Настройки чата", + + // Message when connection to the server is lost + "common.connection_lost": r#"Связь потеряна! +Клиент обновлен до версии сервера?"#, + + + "common.species.orc": "Орк", + "common.species.human": "Человек", + "common.species.dwarf": "Дварфы", + "common.species.elf": "Эльфы", + "common.species.undead": "Нежить", + "common.species.danari": "Данари", + + "common.weapons.axe": "Топор", + "common.weapons.sword": "Меч", + "common.weapons.staff": "Посох", + "common.weapons.bow": "Лук", + "common.weapons.hammer": "Молот", + "common.weapons.general": "Общий бой", + "common.weapons.sceptre": "Скипетр", + "common.weapons.shield": "Защита", + "common.weapons.spear": "Копье", + "common.weapons.hammer_simple": "Простой молот", + "common.weapons.sword_simple": "Простой меч", + "common.weapons.staff_simple": "Простой посох", + "common.weapons.axe_simple": "Простой топор", + "common.weapons.bow_simple": "Простой лук", + "common.weapons.unique": "Уникальный", + "common.tool.debug": "Дебаг", + "common.tool.faming": "Сельскохозяйственный инструмент", + "common.tool.pick": "Кирка", + "common.kind.modular_component": "Компонент", + "common.kind.glider": "Дельтаплан", + "common.kind.consumable": "Расходуемый", + "common.kind.throwable": "Можно бросить", + "common.kind.utility": "Полезность", + "common.kind.ingredient": "Ингредиент", + "common.kind.lantern": "Фонарь", + "common.hands.one": "Одноручное", + "common.hands.two": "Двуручное", + + "common.rand_appearance": "Случайная внешность", + "common.rand_name": "Случайное имя", + + "common.stats.combat_rating": "CR", + "common.stats.power": "Сила", + "common.stats.speed": "Скорость", + "common.stats.poise": "Равновесие", + "common.stats.crit_chance": "Крит шанс", + "common.stats.crit_mult": "Множитель крита", + "common.stats.armor": "Броня", + "common.stats.poise_res":"Оглушение", + "common.stats.energy_max": "Максимальная выносливость", + "common.stats.energy_reward": "Востановление выносливости", + "common.stats.crit_power": "Сила крита", + "common.stats.stealth": "Скрытность", + "common.stats.slots": "Слоты", + + "common.material.metal": "Метал", + "common.material.wood": "Дерево", + "common.material.stone": "Камень", + "common.material.cloth": "Ткань", + "common.material.hide": "Кожа", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/gameinput.ron b/assets/voxygen/i18n/ru_RU/gameinput.ron new file mode 100644 index 0000000000..db8d764eac --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/gameinput.ron @@ -0,0 +1,73 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + "gameinput.primary": "Основная атака", + "gameinput.secondary": "Вторичная атака", + "gameinput.block": "Блок", + "gameinput.slot1": "Быстрое меню 1", + "gameinput.slot2": "Быстрое меню 2", + "gameinput.slot3": "Быстрое меню 3", + "gameinput.slot4": "Быстрое меню 4", + "gameinput.slot5": "Быстрое меню 5", + "gameinput.slot6": "Быстрое меню 6", + "gameinput.slot7": "Быстрое меню 7", + "gameinput.slot8": "Быстрое меню 8", + "gameinput.slot9": "Быстрое меню 9", + "gameinput.slot10": "Быстрое меню 10", + "gameinput.swaploadout": "Сменить снаряжение", + "gameinput.togglecursor": "ВКЛ/ВЫКЛ курсор", + "gameinput.help": "ВКЛ/ВЫКЛ окно помощи", + "gameinput.toggleinterface": "ВКЛ/ВЫКЛ интерфейс", + "gameinput.toggledebug": "ВКЛ/ВЫКЛ FPS и отладочную информацию", + "gameinput.screenshot": "Сделать скриншот", + "gameinput.toggleingameui": "ВЫКЛ/ВЫКЛ никнеймы и имена", + "gameinput.fullscreen": "ВКЛ/ВЫКЛ полноэкранный режим", + "gameinput.moveforward": "Двигаться вперед", + "gameinput.moveleft": "Двигаться влево", + "gameinput.moveright": "Двигаться вправо", + "gameinput.moveback": "Двигаться назад", + "gameinput.jump": "Прыжок", + "gameinput.glide": "Дельтаплан", + "gameinput.roll": "Перекат", + "gameinput.climb": "Взбираться", + "gameinput.climbdown": "Спускаться", + "gameinput.wallleap": "Прыжок от стены", + "gameinput.togglelantern": "ВКЛ/ВЫКЛ фонарь", + "gameinput.mount": "Оседлать", + "gameinput.chat": "Чат", + "gameinput.command": "Команда", + "gameinput.escape": "Назад", + "gameinput.map": "Карта", + "gameinput.bag": "Рюкзак", + "gameinput.trade": "Обмен", + "gameinput.social": "Список игроков", + "gameinput.sit": "Сидеть", + "gameinput.spellbook": "Навыки", + "gameinput.settings": "Настройки", + "gameinput.respawn": "Возродиться", + "gameinput.charge": "Зарядить", + "gameinput.togglewield": "Переключить Управление", + "gameinput.interact": "Взаимодействовать", + "gameinput.freelook": "Свободная камера", + "gameinput.autowalk": "Автодвижение", + "gameinput.cameraclamp": "Фиксирование камеры", + "gameinput.dance": "Танцевать", + "gameinput.select": "Выбор объекта", + "gameinput.acceptgroupinvite": "Принять приглашение в группу", + "gameinput.declinegroupinvite": "Отвергнуть приглашение в группу", + "gameinput.cyclecamera": "Переключение камеры", + "gameinput.crafting": "Крафтинг", + "gameinput.fly": "Полет", + "gameinput.sneak": "Красться", + "gameinput.swimdown": "Погружение (в воду)", + "gameinput.swimup": "Всплыть", + "gameinput.mapzoomin": "Увеличение масштаба карты", + "gameinput.mapzoomout": "Уменьшение масштаба карты", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/main.ron b/assets/voxygen/i18n/ru_RU/main.ron index 26a42b40d1..752188d27f 100644 --- a/assets/voxygen/i18n/ru_RU/main.ron +++ b/assets/voxygen/i18n/ru_RU/main.ron @@ -1,6 +1,6 @@ /// WARNING: Localization files shall be saved in UTF-8 format without BOM -/// Localization for RU +/// Localization for RUS ( string_map: { /// Start Main screen section @@ -52,14 +52,14 @@ https://veloren.net/account/."#, "main.login.network_wrong_version": "Несоответствие версии сервера и клиента, пожалуйста, обновите свой игровой клиент.", "main.login.failed_sending_request": "Не удалось выполнить запрос на сервер аутентификации", "main.login.invalid_character": "Выбранный символ недопустим", - "main.login.client_crashed": "Клиент разбился", + "main.login.client_crashed": "Клиент крашнулся", "main.login.not_on_whitelist": "Вам нужна запись в Белом списке от администратора, чтобы присоединиться", "main.login.banned": "Вы были забанены по следующей причине", "main.login.kicked": "Вас выгнали по следующей причине", "main.login.select_language": "Выберите язык", "main.login.client_version": "Версия клиента", - "main.login.server_version": "Server Version", - "main.servers.select_server": "Версия сервера", + "main.login.server_version": "Версия сервера", + "main.servers.select_server": "Выбор сервера", /// End Main screen section }, diff --git a/assets/voxygen/i18n/ru_RU/skills.ron b/assets/voxygen/i18n/ru_RU/skills.ron index 5b898545cf..8b347dcd22 100644 --- a/assets/voxygen/i18n/ru_RU/skills.ron +++ b/assets/voxygen/i18n/ru_RU/skills.ron @@ -1,6 +1,6 @@ /// WARNING: Localization files shall be saved in UTF-8 format without BOM -/// Localization for "global" English +/// Localization for RUS ( string_map: { "hud.rank_up": "Новый скиллпоинт", @@ -169,11 +169,11 @@ "hud.skill.hmr_single_strike_knockback" : "Увеличьте потенциал отбрасывания на 50%{SP}", "hud.skill." : "", // Sword - "hud.skill.sw_trip_str_title": "Тройное вращение", - "hud.skill.sw_trip_str": "Вращение на 3 оборота", + "hud.skill.sw_trip_str_title": "Тройной удар", + "hud.skill.sw_trip_str": "Атака из 3х ударов в комбо", "hud.skill.sw_trip_str_combo_title": "Тройной удар Комбо", - "hud.skill.sw_trip_str_combo": "Разблокирует комбо при тройном вращении{SP}", - "hud.skill.sw_trip_str_dmg_title": "Урон тройного вращения", + "hud.skill.sw_trip_str_combo": "Разблокирует комбо для тройного удара{SP}", + "hud.skill.sw_trip_str_dmg_title": "Урон тройного удара", "hud.skill.sw_trip_str_dmg": "Увеличивает урон, наносимый каждым последующим ударом{SP}", "hud.skill.sw_trip_str_sp_title": "Скорость атаки", "hud.skill.sw_trip_str_sp": "Увеличивает скорость атаки, получаемую при каждом последующем ударе{SP}", From 84080607cd9c5f0d6cbd471ebd0ee1763b1989a1 Mon Sep 17 00:00:00 2001 From: Snowram Date: Tue, 15 Jun 2021 19:36:20 +0200 Subject: [PATCH 14/38] Truncates consumables value to the first decimal place --- voxygen/src/hud/util.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 7f93c96049..c66f332cfd 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -116,13 +116,13 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String { let buff_desc = match buff.kind { BuffKind::Saturation | BuffKind::Regeneration | BuffKind::Potion => i18n .get("buff.stat.health") - .replace("{str_total}", &str_total.to_string()), + .replace("{str_total}", &*format!("{:.1}", &str_total)), BuffKind::IncreaseMaxEnergy => i18n .get("buff.stat.increase_max_stamina") - .replace("{strength}", &strength.to_string()), + .replace("{strength}", &*format!("{:.1}", &strength)), BuffKind::IncreaseMaxHealth => i18n .get("buff.stat.increase_max_health") - .replace("{strength}", &strength.to_string()), + .replace("{strength}", &*format!("{:.1}", &strength)), BuffKind::Invulnerability => i18n.get("buff.stat.invulnerability").to_string(), BuffKind::Bleeding | BuffKind::Burning From 52c965fb6d1d92f52fe623a831b0fba71303531c Mon Sep 17 00:00:00 2001 From: Zakru Date: Tue, 15 Jun 2021 20:52:59 +0300 Subject: [PATCH 15/38] Avoid saving settings if maps are not updating Fixes #1193 --- voxygen/src/hud/map.rs | 14 +++++++++----- voxygen/src/hud/minimap.rs | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index f2389230ad..7ea03cf1b8 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -327,7 +327,9 @@ impl<'a> Widget for Map<'a> { .sum(); // Drag represents offset of view from the player_pos in chunk coords let drag_new = drag + dragged / map_size / zoom * max_zoom; - events.push(Event::SettingsChange(MapDrag(drag_new))); + if drag_new != drag { + events.push(Event::SettingsChange(MapDrag(drag_new))); + } let rect_src = position::Rect::from_xy_dim( [ @@ -392,10 +394,12 @@ impl<'a> Widget for Map<'a> { .scrolls() .map(|scroll| scroll.y) .sum(); - let new_zoom_lvl = (self.global_state.settings.interface.map_zoom - * (scrolled * 0.05 * -1.0).exp2()) - .clamped(1.25, max_zoom / 64.0); - events.push(Event::SettingsChange(MapZoom(new_zoom_lvl as f64))); + if scrolled != 0.0 { + let new_zoom_lvl = (self.global_state.settings.interface.map_zoom + * (scrolled * 0.05 * -1.0).exp2()) + .clamped(1.25, max_zoom / 64.0); + events.push(Event::SettingsChange(MapZoom(new_zoom_lvl as f64))); + } // Icon settings // Alignment Rectangle::fill_with([150.0, 200.0], color::TRANSPARENT) diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 7eb760a8aa..6eabfb7275 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -514,6 +514,7 @@ impl<'a> Widget for MiniMap<'a> { // Set the image dimensions here, rather than recomputing each time. zoom = min_zoom.max(zoom / ZOOM_FACTOR); // set_image_dims(zoom); + events.push(Event::SettingsChange(MinimapZoom(zoom))); } if Button::image(self.imgs.mmap_plus) .w_h(18.0 * SCALE, 18.0 * SCALE) @@ -528,6 +529,7 @@ impl<'a> Widget for MiniMap<'a> { { zoom = min_zoom.max(zoom * ZOOM_FACTOR); // set_image_dims(zoom); + events.push(Event::SettingsChange(MinimapZoom(zoom))); } // Always northfacing button @@ -555,8 +557,6 @@ impl<'a> Widget for MiniMap<'a> { events.push(Event::SettingsChange(MinimapFaceNorth(!is_facing_north))); } - events.push(Event::SettingsChange(MinimapZoom(zoom))); - // Coordinates let player_pos = self .client From afc04ae22fd25326c8350509cbd4db5238e0568a Mon Sep 17 00:00:00 2001 From: Zakru Date: Tue, 15 Jun 2021 20:56:06 +0300 Subject: [PATCH 16/38] Fix settings not saving when zooming map with keys --- voxygen/src/hud/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 983277688d..99710dda72 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3394,10 +3394,12 @@ impl Hud { .clamped(1.25, max_zoom / 64.0); global_state.settings.interface.map_zoom = new_zoom_lvl; + global_state.settings.save_to_file_warn(); } else if global_state.settings.interface.minimap_show { let new_zoom_lvl = global_state.settings.interface.minimap_zoom * factor; global_state.settings.interface.minimap_zoom = new_zoom_lvl; + global_state.settings.save_to_file_warn(); } show.map && global_state.settings.interface.minimap_show From 4d3362586fc9f15fdaa9dd0e6a37cc293e797bc0 Mon Sep 17 00:00:00 2001 From: Snowram Date: Tue, 15 Jun 2021 22:11:06 +0200 Subject: [PATCH 17/38] Double the speed factor of pos interp --- voxygen/src/ecs/sys/interpolation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/voxygen/src/ecs/sys/interpolation.rs b/voxygen/src/ecs/sys/interpolation.rs index 97dba3a071..0657846372 100644 --- a/voxygen/src/ecs/sys/interpolation.rs +++ b/voxygen/src/ecs/sys/interpolation.rs @@ -43,7 +43,7 @@ impl<'a> System<'a> for Sys { { // Update interpolation values, but don't interpolate far things or objects if i.pos.distance_squared(pos.0) < 64.0 * 64.0 && !matches!(body, Body::Object(_)) { - i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, 10.0 * dt.0); + i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, 20.0 * dt.0); i.ori = Ori::slerp(i.ori, *ori, base_ori_interp(body) * dt.0); } else { i.pos = pos.0; From b82dde23320ee07fe1d4543a1966f8b71138b4d8 Mon Sep 17 00:00:00 2001 From: Snowram Date: Tue, 15 Jun 2021 22:12:11 +0000 Subject: [PATCH 18/38] Revert "Merge branch 'snowram/snappier-pos-interp' into 'master'" This reverts merge request !2457 --- voxygen/src/ecs/sys/interpolation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/voxygen/src/ecs/sys/interpolation.rs b/voxygen/src/ecs/sys/interpolation.rs index 0657846372..97dba3a071 100644 --- a/voxygen/src/ecs/sys/interpolation.rs +++ b/voxygen/src/ecs/sys/interpolation.rs @@ -43,7 +43,7 @@ impl<'a> System<'a> for Sys { { // Update interpolation values, but don't interpolate far things or objects if i.pos.distance_squared(pos.0) < 64.0 * 64.0 && !matches!(body, Body::Object(_)) { - i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, 20.0 * dt.0); + i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, 10.0 * dt.0); i.ori = Ori::slerp(i.ori, *ori, base_ori_interp(body) * dt.0); } else { i.pos = pos.0; From e5f23eb41ab834c55b090c2cd599cbb86d352075 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 01:04:42 -0400 Subject: [PATCH 19/38] Add debug line tables and avoid building unwinding landing pads in release builds --- .gitlab-ci.yml | 2 +- CHANGELOG.md | 1 + Cargo.toml | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c497e9c1c1..4967ab327b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ variables: # https://docs.gitlab.com/ee/ci/yaml/#shallow-cloning GIT_DEPTH: 3 GIT_CLEAN_FLAGS: -f - CACHE_IMAGE_TAG: 55629eab + CACHE_IMAGE_TAG: 8490f4b9 default: # https://docs.gitlab.com/ee/ci/pipelines/settings.html#auto-cancel-pending-pipelines diff --git a/CHANGELOG.md b/CHANGELOG.md index 2694d34f41..a09f064b99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added a skill tree for mining, which gains xp from mining ores and gems. +- Added debug line info to release builds, enhancing the usefulness of panic backtraces ### Changed - Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. diff --git a/Cargo.toml b/Cargo.toml index bd0e04061c..1f5d7b0750 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,8 @@ opt-level = 3 overflow-checks = false debug-assertions = false lto = true -debug = false +debug = 1 # line tables so we can have useful backtraces +panic = "abort" # don't need unwinding so we can skip including the landing pads for that # used for cargo bench [profile.bench] From b7162ac15c64c96870ed6d690c43550cad230be4 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 04:01:57 -0400 Subject: [PATCH 20/38] Upgrade to winit 0.25 --- Cargo.lock | 168 ++++++------------ Cargo.toml | 2 - voxygen/Cargo.toml | 8 +- voxygen/src/lib.rs | 2 +- voxygen/src/main.rs | 2 +- voxygen/src/menu/char_selection/ui/mod.rs | 2 +- voxygen/src/menu/main/ui/mod.rs | 2 +- voxygen/src/ui/ice/mod.rs | 7 +- voxygen/src/ui/ice/renderer/widget/button.rs | 2 +- .../ui/ice/widget/aspect_ratio_container.rs | 6 +- .../src/ui/ice/widget/background_container.rs | 6 +- voxygen/src/ui/ice/widget/mouse_detector.rs | 4 +- voxygen/src/ui/ice/widget/overlay.rs | 40 +++-- voxygen/src/ui/ice/widget/tooltip.rs | 6 +- 14 files changed, 102 insertions(+), 155 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aeb3a27172..37398ea1ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -613,15 +613,6 @@ dependencies = [ "objc_id", ] -[[package]] -name = "clipboard_wayland" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61bcb8cde0387fde807b9b7af66ce8bd1665ef736e46e6e47fda82ea003e6ade" -dependencies = [ - "smithay-clipboard", -] - [[package]] name = "clipboard_wayland" version = "0.2.0" @@ -631,16 +622,6 @@ dependencies = [ "smithay-clipboard", ] -[[package]] -name = "clipboard_x11" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40403aa5220e5cd303d32dc4248cac8aa92bf47e3ae31e0e2481081755a63ff1" -dependencies = [ - "thiserror", - "x11rb", -] - [[package]] name = "clipboard_x11" version = "0.3.1" @@ -931,8 +912,8 @@ dependencies = [ "lazy_static", "libc", "mach 0.3.2", - "ndk 0.3.0", - "ndk-glue 0.3.0", + "ndk", + "ndk-glue", "nix 0.20.0", "oboe", "parking_lot 0.11.1", @@ -1054,6 +1035,20 @@ dependencies = [ "itertools 0.9.0", ] +[[package]] +name = "crossbeam" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-channel", + "crossbeam-deque 0.8.0", + "crossbeam-epoch 0.9.5", + "crossbeam-queue", + "crossbeam-utils 0.8.5", +] + [[package]] name = "crossbeam-channel" version = "0.5.1" @@ -2386,13 +2381,13 @@ dependencies = [ [[package]] name = "iced_core" -version = "0.3.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.4.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" [[package]] name = "iced_futures" -version = "0.2.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.3.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" dependencies = [ "futures", "log", @@ -2401,8 +2396,8 @@ dependencies = [ [[package]] name = "iced_graphics" -version = "0.1.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.2.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" dependencies = [ "bytemuck", "glam", @@ -2414,8 +2409,8 @@ dependencies = [ [[package]] name = "iced_native" -version = "0.3.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.4.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" dependencies = [ "iced_core", "iced_futures", @@ -2426,16 +2421,16 @@ dependencies = [ [[package]] name = "iced_style" -version = "0.2.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.3.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" dependencies = [ "iced_core", ] [[package]] name = "iced_winit" -version = "0.2.0" -source = "git+https://github.com/hecrj/iced?rev=8d882d787e6b7fd7c2435f42f82933e2ed904edf#8d882d787e6b7fd7c2435f42f82933e2ed904edf" +version = "0.3.0" +source = "git+https://github.com/Imberflur/iced?tag=winit-0.25#18a48a5eeefd2aa6e50f5dd0751ac2e94c88a9eb" dependencies = [ "iced_futures", "iced_graphics", @@ -2443,7 +2438,7 @@ dependencies = [ "log", "thiserror", "winapi 0.3.9", - "window_clipboard 0.1.4", + "window_clipboard", "winit", ] @@ -2637,7 +2632,7 @@ dependencies = [ [[package]] name = "keyboard-keynames" version = "0.1.0" -source = "git+https://gitlab.com/Frinksy/keyboard-keynames.git?rev=a97ae509cdb9dc70cf1bf0af762d2d1d3a0d6e0c#a97ae509cdb9dc70cf1bf0af762d2d1d3a0d6e0c" +source = "git+https://gitlab.com/Frinksy/keyboard-keynames.git?rev=9ae8f89014d0b0c5b61d0e821c5aeb6140c5c0dc#9ae8f89014d0b0c5b61d0e821c5aeb6140c5c0dc" dependencies = [ "libc", "memmap", @@ -3028,6 +3023,18 @@ dependencies = [ "slab", ] +[[package]] +name = "mio-misc" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ddf05411bb159cdb5801bb10002afb66cb4572be656044315e363460ce69dc2" +dependencies = [ + "crossbeam", + "crossbeam-queue", + "log", + "mio 0.7.11", +] + [[package]] name = "miow" version = "0.2.2" @@ -3097,18 +3104,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "ndk" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" -dependencies = [ - "jni-sys", - "ndk-sys", - "num_enum 0.4.3", - "thiserror", -] - [[package]] name = "ndk" version = "0.3.0" @@ -3117,24 +3112,10 @@ checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab" dependencies = [ "jni-sys", "ndk-sys", - "num_enum 0.5.1", + "num_enum", "thiserror", ] -[[package]] -name = "ndk-glue" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" -dependencies = [ - "lazy_static", - "libc", - "log", - "ndk 0.2.1", - "ndk-macro", - "ndk-sys", -] - [[package]] name = "ndk-glue" version = "0.3.0" @@ -3144,7 +3125,7 @@ dependencies = [ "lazy_static", "libc", "log", - "ndk 0.3.0", + "ndk", "ndk-macro", "ndk-sys", ] @@ -3465,16 +3446,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_enum" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" -dependencies = [ - "derivative", - "num_enum_derive 0.4.3", -] - [[package]] name = "num_enum" version = "0.5.1" @@ -3482,19 +3453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" dependencies = [ "derivative", - "num_enum_derive 0.5.1", -] - -[[package]] -name = "num_enum_derive" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" -dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.27", - "quote 1.0.9", - "syn 1.0.72", + "num_enum_derive", ] [[package]] @@ -3574,8 +3533,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa187b38ae20374617b7ad418034ed3dc90ac980181d211518bd03537ae8f8d" dependencies = [ "jni", - "ndk 0.3.0", - "ndk-glue 0.3.0", + "ndk", + "ndk-glue", "num-derive", "num-traits", "oboe-sys", @@ -6147,7 +6106,7 @@ dependencies = [ "veloren-world", "wgpu", "wgpu-profiler", - "window_clipboard 0.2.1", + "window_clipboard", "winit", "winres", ] @@ -6850,19 +6809,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "window_clipboard" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37cf16659e398a96f4ab8deff2b9db2ca0c3c5d6c1b59b1d577b7f888f0f03c6" -dependencies = [ - "clipboard-win 4.2.1", - "clipboard_macos", - "clipboard_wayland 0.1.2", - "clipboard_x11 0.2.0", - "raw-window-handle", -] - [[package]] name = "window_clipboard" version = "0.2.1" @@ -6871,15 +6817,16 @@ checksum = "33a4518b538a45ad39d138a8c3bea8f6b4452174aeb38143d1dd643a3a838ccc" dependencies = [ "clipboard-win 4.2.1", "clipboard_macos", - "clipboard_wayland 0.2.0", - "clipboard_x11 0.3.1", + "clipboard_wayland", + "clipboard_x11", "raw-window-handle", ] [[package]] name = "winit" -version = "0.24.0" -source = "git+https://gitlab.com/veloren/winit.git?branch=macos-test-spiffed#488c511802dfd95ca54f6f76a38547c93c7b02c9" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8" dependencies = [ "bitflags", "cocoa", @@ -6891,15 +6838,16 @@ dependencies = [ "lazy_static", "libc", "log", - "mio 0.6.23", - "mio-extras", - "ndk 0.2.1", - "ndk-glue 0.2.1", + "mio 0.7.11", + "mio-misc", + "ndk", + "ndk-glue", "ndk-sys", "objc", "parking_lot 0.11.1", "percent-encoding", "raw-window-handle", + "scopeguard", "serde", "smithay-client-toolkit", "wayland-client 0.28.5", diff --git a/Cargo.toml b/Cargo.toml index db690b31c4..0342211826 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,8 +111,6 @@ buildInputs = ["openssl"] nativeBuildInputs = ["pkg-config"] [patch.crates-io] -# macos CI fix isn't released yet -winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" } vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics2" } # patch wgpu so we can use wgpu-profiler crate wgpu = { git = "https://github.com/gfx-rs/wgpu.git", rev = "a92b8549a8e2cb9dac781bafc5ed32828f3caf46" } diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index c322e6a1af..150f3d35fb 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -44,7 +44,7 @@ anim = {package = "veloren-voxygen-anim", path = "anim"} i18n = {package = "veloren-i18n", path = "i18n"} # Graphics -winit = {version = "0.24.0", features = ["serde"]} +winit = {version = "0.25.0", features = ["serde"]} wgpu = { version = "=0.8.0", features = ["trace", "cross"] } wgpu-profiler = { git = "https://github.com/Imberflur/wgpu-profiler", tag = "wgpu-0.8" } bytemuck = { version="1.4", features=["derive"] } @@ -54,11 +54,11 @@ shaderc = "0.6.2" conrod_core = {git = "https://gitlab.com/veloren/conrod.git", branch="copypasta_0.7"} conrod_winit = {git = "https://gitlab.com/veloren/conrod.git", branch="copypasta_0.7"} euc = "0.5.0" -iced = {package = "iced_native", git = "https://github.com/hecrj/iced", rev = "8d882d787e6b7fd7c2435f42f82933e2ed904edf"} -iced_winit = {git = "https://github.com/hecrj/iced", rev = "8d882d787e6b7fd7c2435f42f82933e2ed904edf"} +iced = {package = "iced_native", git = "https://github.com/Imberflur/iced", tag = "winit-0.25"} +iced_winit = {git = "https://github.com/Imberflur/iced", tag = "winit-0.25"} window_clipboard = "0.2" glyph_brush = "0.7.0" -keyboard-keynames = { git = "https://gitlab.com/Frinksy/keyboard-keynames.git", rev = "a97ae509cdb9dc70cf1bf0af762d2d1d3a0d6e0c" } +keyboard-keynames = { git = "https://gitlab.com/Frinksy/keyboard-keynames.git", rev = "9ae8f89014d0b0c5b61d0e821c5aeb6140c5c0dc" } # ECS specs = {git = "https://github.com/amethyst/specs.git", rev = "f985bec5d456f7b0dd8aae99848f9473c2cd9d46"} diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 4d2a1a04dd..7532eb903d 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -62,7 +62,7 @@ pub struct GlobalState { pub singleplayer: Option, // TODO: redo this so that the watcher doesn't have to exist for reloading to occur pub i18n: LocalizationHandle, - pub clipboard: Option, + pub clipboard: iced_winit::Clipboard, // NOTE: This can be removed from GlobalState if client state behavior is refactored to not // enter the game before confirmation of successful character load /// An error returned by Client that needs to be displayed by the UI diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 89ad6e7b9d..10e7742d10 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -181,7 +181,7 @@ fn main() { // Create window let (mut window, event_loop) = Window::new(&settings).expect("Failed to create window!"); - let clipboard = iced_winit::Clipboard::new(window.window()); + let clipboard = iced_winit::Clipboard::connect(window.window()); let lazy_init = SpriteRenderContext::new(window.renderer_mut()); diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index cc1c853910..a874e8c91e 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -1564,7 +1564,7 @@ impl CharSelectionUi { .view(&global_state.settings, &client, &self.error, &i18n), global_state.window.renderer_mut(), None, - global_state.clipboard.as_ref(), + &mut global_state.clipboard, ); if self.enter_pressed { diff --git a/voxygen/src/menu/main/ui/mod.rs b/voxygen/src/menu/main/ui/mod.rs index d17c06309f..cb333a9671 100644 --- a/voxygen/src/menu/main/ui/mod.rs +++ b/voxygen/src/menu/main/ui/mod.rs @@ -569,7 +569,7 @@ impl MainMenuUi { self.controls.view(&global_state.settings, dt.as_secs_f32()), global_state.window.renderer_mut(), None, - global_state.clipboard.as_ref(), + &mut global_state.clipboard, ); messages.into_iter().for_each(|message| { diff --git a/voxygen/src/ui/ice/mod.rs b/voxygen/src/ui/ice/mod.rs index 849ab7a76a..91393ad0cf 100644 --- a/voxygen/src/ui/ice/mod.rs +++ b/voxygen/src/ui/ice/mod.rs @@ -148,7 +148,7 @@ impl IcedUi { root: E, renderer: &mut Renderer, pool: Option<&SlowJobPool>, - clipboard: Option<&Clipboard>, + clipboard: &mut Clipboard, ) -> (Vec, mouse::Interaction) { span!(_guard, "maintain", "IcedUi::maintain"); // Handle window resizing, dpi factor change, and scale mode changing @@ -194,11 +194,8 @@ impl IcedUi { let _event_status_list = user_interface.update( &self.events, cursor_position, - match clipboard { - Some(c) => Some(c), - None => None, - }, &self.renderer, + clipboard, &mut messages, ); messages diff --git a/voxygen/src/ui/ice/renderer/widget/button.rs b/voxygen/src/ui/ice/renderer/widget/button.rs index cf05a448bf..40a6bff71c 100644 --- a/voxygen/src/ui/ice/renderer/widget/button.rs +++ b/voxygen/src/ui/ice/renderer/widget/button.rs @@ -5,7 +5,7 @@ impl button::Renderer for IcedRenderer { // TODO: what if this gets large enough to not be copied around? type Style = style::button::Style; - const DEFAULT_PADDING: u16 = 0; + const DEFAULT_PADDING: iced::Padding = iced::Padding::ZERO; fn draw( &mut self, diff --git a/voxygen/src/ui/ice/widget/aspect_ratio_container.rs b/voxygen/src/ui/ice/widget/aspect_ratio_container.rs index 8db9bd33ca..e420f22441 100644 --- a/voxygen/src/ui/ice/widget/aspect_ratio_container.rs +++ b/voxygen/src/ui/ice/widget/aspect_ratio_container.rs @@ -119,17 +119,17 @@ where event: Event, layout: Layout<'_>, cursor_position: Point, - messages: &mut Vec, renderer: &R, - clipboard: Option<&dyn Clipboard>, + clipboard: &mut dyn Clipboard, + messages: &mut Vec, ) -> iced::event::Status { self.content.on_event( event, layout.children().next().unwrap(), cursor_position, - messages, renderer, clipboard, + messages, ) } diff --git a/voxygen/src/ui/ice/widget/background_container.rs b/voxygen/src/ui/ice/widget/background_container.rs index ce267b7a13..53de3a647d 100644 --- a/voxygen/src/ui/ice/widget/background_container.rs +++ b/voxygen/src/ui/ice/widget/background_container.rs @@ -255,17 +255,17 @@ where event: Event, layout: Layout<'_>, cursor_position: Point, - messages: &mut Vec, renderer: &R, - clipboard: Option<&dyn Clipboard>, + clipboard: &mut dyn Clipboard, + messages: &mut Vec, ) -> iced::event::Status { self.content.on_event( event, layout.children().next().unwrap(), cursor_position, - messages, renderer, clipboard, + messages, ) } diff --git a/voxygen/src/ui/ice/widget/mouse_detector.rs b/voxygen/src/ui/ice/widget/mouse_detector.rs index a93df67ae5..036a2f19a9 100644 --- a/voxygen/src/ui/ice/widget/mouse_detector.rs +++ b/voxygen/src/ui/ice/widget/mouse_detector.rs @@ -48,9 +48,9 @@ where event: Event, layout: Layout<'_>, _cursor_position: Point, - _messages: &mut Vec, _renderer: &R, - _clipboard: Option<&dyn Clipboard>, + _clipboard: &mut dyn Clipboard, + _messages: &mut Vec, ) -> iced::event::Status { if let Event::Mouse(mouse::Event::CursorMoved { position: Point { x, y }, diff --git a/voxygen/src/ui/ice/widget/overlay.rs b/voxygen/src/ui/ice/widget/overlay.rs index c9b1ff66b9..0bcd0d3833 100644 --- a/voxygen/src/ui/ice/widget/overlay.rs +++ b/voxygen/src/ui/ice/widget/overlay.rs @@ -1,6 +1,6 @@ use iced::{ - layout, mouse, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle, - Size, Widget, + layout, mouse, Align, Clipboard, Element, Event, Hasher, Layout, Length, Padding, Point, + Rectangle, Size, Widget, }; use std::hash::Hash; @@ -10,7 +10,7 @@ use std::hash::Hash; /// the front widget /// Alignment and padding is used for the front widget pub struct Overlay<'a, M, R: self::Renderer> { - padding: u16, + padding: Padding, width: Length, height: Length, max_width: u32, @@ -32,7 +32,7 @@ where U: Into>, { Self { - padding: 0, + padding: Padding::ZERO, width: Length::Shrink, height: Length::Shrink, max_width: u32::MAX, @@ -44,8 +44,8 @@ where } } - pub fn padding(mut self, pad: u16) -> Self { - self.padding = pad; + pub fn padding>(mut self, pad: P) -> Self { + self.padding = pad.into(); self } @@ -99,8 +99,6 @@ where fn height(&self) -> Length { self.height } fn layout(&self, renderer: &R, limits: &layout::Limits) -> layout::Node { - let padding = self.padding as f32; - let limits = limits .loose() .max_width(self.max_width) @@ -111,16 +109,22 @@ where let under = self.under.layout(renderer, &limits.loose()); let under_size = under.size(); - let limits = limits.pad(padding); + let limits = limits.pad(self.padding); let mut over = self.over.layout(renderer, &limits.loose()); let over_size = over.size(); - let size = limits.resolve(Size { - width: under_size.width.max(over_size.width + padding * 2.0), - height: under_size.height.max(over_size.height + padding * 2.0), - }); + let size = limits.resolve( + Size { + width: under_size.width.max(over_size.width), + height: under_size.height.max(over_size.height), + } + .pad(self.padding), + ); - over.move_to(Point::new(padding, padding)); + over.move_to(Point::new( + self.padding.left.into(), + self.padding.top.into(), + )); over.align(self.horizontal_alignment, self.vertical_alignment, size); layout::Node::with_children(size, vec![over, under]) @@ -131,9 +135,9 @@ where event: Event, layout: Layout<'_>, cursor_position: Point, - messages: &mut Vec, renderer: &R, - clipboard: Option<&dyn Clipboard>, + clipboard: &mut dyn Clipboard, + messages: &mut Vec, ) -> iced::event::Status { let mut children = layout.children(); let over_layout = children.next().unwrap(); @@ -143,9 +147,9 @@ where event.clone(), over_layout, cursor_position, - messages, renderer, clipboard, + messages, ); // If mouse press check if over the overlay widget before sending to under @@ -158,9 +162,9 @@ where event, children.next().unwrap(), cursor_position, - messages, renderer, clipboard, + messages, ) .merge(status) } else { diff --git a/voxygen/src/ui/ice/widget/tooltip.rs b/voxygen/src/ui/ice/widget/tooltip.rs index 2e616a1f85..7ef1e58a42 100644 --- a/voxygen/src/ui/ice/widget/tooltip.rs +++ b/voxygen/src/ui/ice/widget/tooltip.rs @@ -189,17 +189,17 @@ where event: Event, layout: Layout<'_>, cursor_position: Point, - messages: &mut Vec, renderer: &R, - clipboard: Option<&dyn Clipboard>, + clipboard: &mut dyn Clipboard, + messages: &mut Vec, ) -> iced::event::Status { self.content.on_event( event, layout, cursor_position, - messages, renderer, clipboard, + messages, ) } From f26de7bbe9450df5ada5e21fff1a89571328a62f Mon Sep 17 00:00:00 2001 From: donovanlank Date: Tue, 15 Jun 2021 22:01:39 -0700 Subject: [PATCH 21/38] lowered spin.ron base poise damage to 13 from 25 --- assets/common/abilities/sword/spin.ron | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/common/abilities/sword/spin.ron b/assets/common/abilities/sword/spin.ron index ac5cdee91f..faf3361b1c 100644 --- a/assets/common/abilities/sword/spin.ron +++ b/assets/common/abilities/sword/spin.ron @@ -3,7 +3,7 @@ SpinMelee( swing_duration: 0.4, recover_duration: 0.5, base_damage: 160, - base_poise_damage: 25, + base_poise_damage: 13, knockback: ( strength: 10.0, direction: Away), range: 3.5, damage_effect: None, From 345add91dbaf9a010b88591f2d00c72c9ed42c81 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 16 Jun 2021 01:36:14 -0400 Subject: [PATCH 22/38] Eliminate panics when .current_monitor() returns None --- voxygen/src/hud/settings_window/video.rs | 5 +- voxygen/src/window.rs | 73 ++++++++++++++++-------- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs index c064da77de..0ce0cc2b0e 100644 --- a/voxygen/src/hud/settings_window/video.rs +++ b/voxygen/src/hud/settings_window/video.rs @@ -164,9 +164,8 @@ impl<'a> Widget for Video<'a> { .window .window() .current_monitor() - .unwrap() - .video_modes() - .collect(); + .map(|monitor| monitor.video_modes().collect()) + .unwrap_or_default(); State { ids: Ids::new(id_gen), diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index e72590f769..2dfe0a9c85 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -1130,7 +1130,10 @@ impl Window { pub fn is_fullscreen(&self) -> bool { self.fullscreen.enabled } - pub fn select_video_mode_rec( + /// Select a video mode that fits the specified requirements + /// Returns None if a matching video mode doesn't exist or if + /// the current monitor can't be retrieved + fn select_video_mode_rec( &self, resolution: [u16; 2], bit_depth: Option, @@ -1142,15 +1145,16 @@ impl Window { // if a previous iteration of this method filtered the available video modes for // the correct resolution already, load that value, otherwise filter it // in this iteration - let correct_res = correct_res.unwrap_or_else(|| { - self.window - .current_monitor() - .unwrap() + let correct_res = match correct_res { + Some(correct_res) => correct_res, + None => self + .window + .current_monitor()? .video_modes() .filter(|mode| mode.size().width == resolution[0] as u32) .filter(|mode| mode.size().height == resolution[1] as u32) - .collect() - }); + .collect(), + }; match bit_depth { // A bit depth is given @@ -1270,12 +1274,12 @@ impl Window { } } - pub fn select_video_mode( + fn select_video_mode( &self, resolution: [u16; 2], bit_depth: Option, refresh_rate: Option, - ) -> VideoMode { + ) -> Option { // (resolution, bit depth, refresh rate) represents a video mode // spec: as specified // max: maximum value available @@ -1285,24 +1289,34 @@ impl Window { // (spec, spec, max), (spec, max, spec) // (spec, max, max) // (max, max, max) - self.select_video_mode_rec(resolution, bit_depth, refresh_rate, None, None, None) - // if there is no video mode with the specified resolution, fall back to the video mode with max resolution, bit depth and refresh rate - .unwrap_or_else(|| { + match self.select_video_mode_rec(resolution, bit_depth, refresh_rate, None, None, None) { + Some(mode) => Some(mode), + // if there is no video mode with the specified resolution, + // fall back to the video mode with max resolution, bit depth and refresh rate + None => { warn!( "Resolution specified in settings is incompatible with the monitor. Choosing \ highest resolution possible instead." ); + if let Some(monitor) = self.window.current_monitor() { + let mode = monitor + .video_modes() + // Prefer bit depth over refresh rate + .sorted_by_key(|mode| mode.refresh_rate()) + .sorted_by_key(|mode| mode.bit_depth()) + .max_by_key(|mode| mode.size().width); - self - .window - .current_monitor().unwrap() - .video_modes() - // Prefer bit depth over refresh rate - .sorted_by_key(|mode| mode.refresh_rate()) - .sorted_by_key(|mode| mode.bit_depth()) - .max_by_key(|mode| mode.size().width) - .expect("No video modes available!!") - }) + if mode.is_none() { + warn!("Failed to select video mode, no video modes available!!") + } + + mode + } else { + warn!("Failed to select video mode, can't get the current monitor!"); + None + } + }, + } } pub fn set_fullscreen_mode(&mut self, fullscreen: FullScreenSettings) { @@ -1310,14 +1324,23 @@ impl Window { self.fullscreen = fullscreen; window.set_fullscreen(fullscreen.enabled.then(|| match fullscreen.mode { FullscreenMode::Exclusive => { - winit::window::Fullscreen::Exclusive(self.select_video_mode( + if let Some(video_mode) = self.select_video_mode( fullscreen.resolution, fullscreen.bit_depth, fullscreen.refresh_rate, - )) + ) { + winit::window::Fullscreen::Exclusive(video_mode) + } else { + warn!( + "Failed to select a video mode for exclusive fullscreen. Falling back to \ + borderless fullscreen." + ); + winit::window::Fullscreen::Borderless(None) + } }, FullscreenMode::Borderless => { - winit::window::Fullscreen::Borderless(window.current_monitor()) + // None here will fullscreen on the current monitor + winit::window::Fullscreen::Borderless(None) }, })); } From 8f429d532a1f58540f1f63a7611dd5e8752b92bb Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 23:18:25 -0400 Subject: [PATCH 23/38] Add note on the reasoning for the visual position interpolation rate value in voxygen --- voxygen/src/ecs/sys/interpolation.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/voxygen/src/ecs/sys/interpolation.rs b/voxygen/src/ecs/sys/interpolation.rs index 97dba3a071..9abe8af9c5 100644 --- a/voxygen/src/ecs/sys/interpolation.rs +++ b/voxygen/src/ecs/sys/interpolation.rs @@ -43,7 +43,11 @@ impl<'a> System<'a> for Sys { { // Update interpolation values, but don't interpolate far things or objects if i.pos.distance_squared(pos.0) < 64.0 * 64.0 && !matches!(body, Body::Object(_)) { - i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, 10.0 * dt.0); + // Note, these values are specifically tuned for smoother motion with high + // network latency or low network sampling rate and for smooth + // block hopping (which is instantaneous) + const POS_LERP_RATE_FACTOR: f32 = 10.0; + i.pos = Lerp::lerp(i.pos, pos.0 + vel.0 * 0.03, POS_LERP_RATE_FACTOR * dt.0); i.ori = Ori::slerp(i.ori, *ori, base_ori_interp(body) * dt.0); } else { i.pos = pos.0; From 09a914aa843f3f2ea908c965e40bcf69c4342506 Mon Sep 17 00:00:00 2001 From: Imbris Date: Tue, 15 Jun 2021 23:28:04 -0400 Subject: [PATCH 24/38] Log format used for the swapchain --- voxygen/src/render/renderer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index d5e9f2157d..d94fb5be73 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -244,6 +244,7 @@ impl Renderer { let format = adapter .get_swap_chain_preferred_format(&surface) .expect("No supported swap chain format found"); + info!("Using {:?} as the swapchain format", format); let sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::RENDER_ATTACHMENT, From 19f741b33fe7cc959b2af4eab7968e4cba671014 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 16 Jun 2021 00:10:33 -0400 Subject: [PATCH 25/38] Show the graphics backend in the hud debug info and include the adapter info when panicking in the wgpu error handler --- voxygen/src/hud/mod.rs | 12 +++++++++++ voxygen/src/render/renderer.rs | 37 +++++++++++++++++++++------------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 99710dda72..a012c63847 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -239,6 +239,7 @@ widget_ids! { num_lights, num_figures, num_particles, + graphics_backend, gpu_timings[], // Game Version @@ -2211,6 +2212,17 @@ impl Hud { .font_size(self.fonts.cyri.scale(14)) .set(self.ids.num_particles, ui_widgets); + // Graphics backend + Text::new(&format!( + "Graphics backend: {}", + global_state.window.renderer().graphics_backend(), + )) + .color(TEXT_COLOR) + .down_from(self.ids.num_particles, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .set(self.ids.graphics_backend, ui_widgets); + // GPU timing for different pipelines let gpu_timings = global_state.window.renderer().timings(); if !gpu_timings.is_empty() { diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index d94fb5be73..8f2c3e8b94 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -139,6 +139,9 @@ pub struct Renderer { // This checks is added because windows resizes the window to 0,0 when // minimizing and this causes a bunch of validation errors is_minimized: bool, + + // To remember the backend info after initialization for debug purposes + graphics_backend: String, } impl Renderer { @@ -188,6 +191,17 @@ impl Renderer { )) .ok_or(RenderError::CouldNotFindAdapter)?; + let info = adapter.get_info(); + info!( + ?info.name, + ?info.vendor, + ?info.backend, + ?info.device, + ?info.device_type, + "selected graphics device" + ); + let graphics_backend = format!("{:?}", &info.backend); + let limits = wgpu::Limits { max_push_constant_size: 64, ..Default::default() @@ -213,12 +227,12 @@ impl Renderer { // Set error handler for wgpu errors // This is better for use than their default because it includes the error in // the panic message - device.on_uncaptured_error(|error| { + device.on_uncaptured_error(move |error| { error!("{}", &error); panic!( - "wgpu error (handling all wgpu errors as fatal): {:?}", - &error, - ) + "wgpu error (handling all wgpu errors as fatal):\n{:?}\n{:?}", + &error, &info, + ); }); let profiler_features_enabled = device @@ -231,16 +245,6 @@ impl Renderer { ); } - let info = adapter.get_info(); - info!( - ?info.name, - ?info.vendor, - ?info.backend, - ?info.device, - ?info.device_type, - "selected graphics device" - ); - let format = adapter .get_swap_chain_preferred_format(&surface) .expect("No supported swap chain format found"); @@ -402,9 +406,14 @@ impl Renderer { profiler_features_enabled, is_minimized: false, + + graphics_backend, }) } + /// Get the graphics backend being used + pub fn graphics_backend(&self) -> &str { &self.graphics_backend } + /// Check the status of the intial pipeline creation /// Returns `None` if complete /// Returns `Some((total, complete))` if in progress From 2d7b82c2f4622807c249c0f8aac07a4a32c9f0cf Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 16 Jun 2021 00:24:47 -0400 Subject: [PATCH 26/38] Do some checks to make sure WGPU_TRACE_DIR is useable if set and exit early if it isn't to avoid the user not being aware that they are failing to collect a trace --- voxygen/src/render/renderer.rs | 57 ++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 8f2c3e8b94..06cf8b6386 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -207,22 +207,47 @@ impl Renderer { ..Default::default() }; - let (device, queue) = futures_executor::block_on( - adapter.request_device( - &wgpu::DeviceDescriptor { - // TODO - label: None, - features: wgpu::Features::DEPTH_CLAMPING - | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER - | wgpu::Features::PUSH_CONSTANTS - | (adapter.features() & wgpu_profiler::GpuProfiler::REQUIRED_WGPU_FEATURES), - limits, - }, - std::env::var_os("WGPU_TRACE_DIR") - .as_ref() - .map(|v| std::path::Path::new(v)), - ), - )?; + let (device, queue) = futures_executor::block_on(adapter.request_device( + &wgpu::DeviceDescriptor { + // TODO + label: None, + features: wgpu::Features::DEPTH_CLAMPING + | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER + | wgpu::Features::PUSH_CONSTANTS + | (adapter.features() & wgpu_profiler::GpuProfiler::REQUIRED_WGPU_FEATURES), + limits, + }, + std::env::var_os("WGPU_TRACE_DIR").as_ref().map(|v| { + let path = std::path::Path::new(v); + // We don't want to continue if we can't actually collect the api trace + if !path.exists() { + panic!( + "WGPU_TRACE_DIR is set to the path \"{}\" which doesn't exist", + path.display() + ); + } + if !path.is_dir() { + panic!( + "WGPU_TRACE_DIR is set to the path \"{}\" which is not a directory", + path.display() + ); + } + if path + .read_dir() + .expect("Could not read the directory that is specified by WGPU_TRACE_DIR") + .next() + .is_some() + { + panic!( + "WGPU_TRACE_DIR is set to the path \"{}\" which already contains other \ + files", + path.display() + ); + } + + path + }), + ))?; // Set error handler for wgpu errors // This is better for use than their default because it includes the error in From 1483c2be7458b31525a95e4583431809e617bfd1 Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 16 Jun 2021 00:39:26 -0400 Subject: [PATCH 27/38] Reset the slot 1 bind group to shadows when dropping the DebugDrawer --- voxygen/src/render/renderer/drawer.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/voxygen/src/render/renderer/drawer.rs b/voxygen/src/render/renderer/drawer.rs index 7e25c349d2..a41a1df1a7 100644 --- a/voxygen/src/render/renderer/drawer.rs +++ b/voxygen/src/render/renderer/drawer.rs @@ -5,7 +5,7 @@ use super::{ model::{DynamicModel, Model, SubModel}, pipelines::{ blit, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox, sprite, - terrain, ui, ColLights, GlobalsBindGroup, + terrain, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup, }, }, Renderer, ShadowMap, ShadowMapRenderer, @@ -198,6 +198,7 @@ impl<'frame> Drawer<'frame> { borrow: &self.borrow, pipelines, globals: self.globals, + shadows: &shadow.bind, }) } @@ -522,6 +523,7 @@ pub struct FirstPassDrawer<'pass> { borrow: &'pass RendererBorrow<'pass>, pipelines: &'pass super::Pipelines, globals: &'pass GlobalsBindGroup, + shadows: &'pass ShadowTexturesBindGroup, } impl<'pass> FirstPassDrawer<'pass> { @@ -540,7 +542,10 @@ impl<'pass> FirstPassDrawer<'pass> { render_pass.set_pipeline(&self.pipelines.debug.pipeline); set_quad_index_buffer::(&mut render_pass, &self.borrow); - DebugDrawer { render_pass } + DebugDrawer { + render_pass, + shadows: self.shadows, + } } pub fn draw_lod_terrain<'data: 'pass>(&mut self, model: &'data Model) { @@ -613,6 +618,7 @@ impl<'pass> FirstPassDrawer<'pass> { pub struct DebugDrawer<'pass_ref, 'pass: 'pass_ref> { render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, + shadows: &'pass ShadowTexturesBindGroup, } impl<'pass_ref, 'pass: 'pass_ref> DebugDrawer<'pass_ref, 'pass> { @@ -627,6 +633,14 @@ impl<'pass_ref, 'pass: 'pass_ref> DebugDrawer<'pass_ref, 'pass> { } } +impl<'pass_ref, 'pass: 'pass_ref> Drop for DebugDrawer<'pass_ref, 'pass> { + fn drop(&mut self) { + // Maintain that the shadow bind group is set in + // slot 1 by default during the main pass + self.render_pass + .set_bind_group(1, &self.shadows.bind_group, &[]); + } +} pub struct FigureDrawer<'pass_ref, 'pass: 'pass_ref> { render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>, } From cb55676bb959d6fee239c1a0bd4f2586020262f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Wed, 16 Jun 2021 14:07:23 +0100 Subject: [PATCH 28/38] poll device to cleanup resources --- voxygen/src/lib.rs | 1 + voxygen/src/render/renderer.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 7532eb903d..f975e0bbed 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -80,6 +80,7 @@ impl GlobalState { pub fn maintain(&mut self, dt: std::time::Duration) { span!(_guard, "maintain", "GlobalState::maintain"); self.audio.maintain(dt); + self.window.renderer().maintain() } #[cfg(feature = "singleplayer")] diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 06cf8b6386..f917a83e2a 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -600,6 +600,8 @@ impl Renderer { Ok(()) } + pub fn maintain(&self) { self.device.poll(wgpu::Maintain::Poll) } + /// Create render target views fn create_rt_views( device: &wgpu::Device, From 559311e1b0b2945be00fce8044fc6d1b63eeba99 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 15 Jun 2021 17:15:58 +0100 Subject: [PATCH 29/38] Chasing sounds --- assets/voxygen/audio/sfx.ron | 6 ++++++ .../audio/sfx/utterance/ogre_angry.ogg | 3 +++ .../audio/sfx/utterance/ogre_angry2.ogg | 3 +++ .../audio/sfx/utterance/ogre_angry3.ogg | 3 +++ .../audio/sfx/utterance/wendigo_angry.mp3 | Bin 0 -> 64307 bytes .../audio/sfx/utterance/wendigo_angry.ogg | 3 +++ common/src/comp/agent.rs | 7 +++--- common/src/comp/controller.rs | 8 +++++++ common/src/comp/mod.rs | 2 +- common/src/outcome.rs | 10 +++++++-- common/systems/src/controller.rs | 20 ++++++++++++++++-- server/src/events/interaction.rs | 17 ++++++++++++++- server/src/sys/agent.rs | 6 +++++- voxygen/src/audio/sfx/mod.rs | 6 ++++++ voxygen/src/scene/particle.rs | 3 ++- 15 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 assets/voxygen/audio/sfx/utterance/ogre_angry.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 create mode 100644 assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index bd9434f765..3eb3a6d828 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -831,5 +831,11 @@ ], threshold: 0.2, ), + Utterance(Angry, BipedLarge((species: Wendigo, body_type: Female))): ( + files: [ + "voxygen.audio.sfx.utterance.wendigo_angry", + ], + threshold: 4.0, + ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry.ogg new file mode 100644 index 0000000000..819348a228 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/ogre_angry.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78f705397c01f73fd2bfad14f0aa3949a5409556cd5f004c185dea358fbd9701 +size 34809 diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg new file mode 100644 index 0000000000..65c67e368c --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83bbec96fb1192ac5d5c0907062242c5c37ba7f8e6f330556ee22e29423f14c6 +size 53337 diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg new file mode 100644 index 0000000000..f4080ed7c2 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f4ad549122e5fec0722260c11309079e99e20ec9d42be3655a44a416027d3f9 +size 55056 diff --git a/assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 b/assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..87d5aec70f739eaa729bacb12fcb87c9a4f950a6 GIT binary patch literal 64307 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 literal 0 HcmV?d00001 diff --git a/assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg b/assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg new file mode 100644 index 0000000000..4c02e0bd36 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfbac68b83de452e7be824075ee9104559ca96a9a710be303c89ede0ab4a6a03 +size 42373 diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 8b792f361f..7c64f62fca 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{humanoid, quadruped_low, quadruped_medium, quadruped_small, ship, Body}, + comp::{humanoid, quadruped_low, quadruped_medium, quadruped_small, ship, Body, UtteranceKind}, path::Chaser, rtsim::RtSimController, trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult}, @@ -96,7 +96,7 @@ bitflags::bitflags! { /// # Behavior Component /// This component allow an Entity to register one or more behavior tags. -/// These tags act as flags of what an Entity can do, or what it is doing. +/// These tags act as flags of what an Entity can do, or what it is doing. /// Behaviors Tags can be added and removed as the Entity lives, to update its /// state when needed #[derive(Default, Copy, Clone, Debug)] @@ -117,7 +117,7 @@ impl From for Behavior { } impl Behavior { - /// Builder function + /// Builder function /// Set capabilities if Option is Some pub fn maybe_with_capabilities( mut self, @@ -297,6 +297,7 @@ pub enum SoundKind { Explosion, Beam, Shockwave, + Utterance(UtteranceKind, Body), } #[derive(Clone, Debug)] diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 9e13b4e327..d157b87853 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -96,6 +96,13 @@ pub enum GroupManip { AssignLeader(Uid), } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum UtteranceKind { + Calm, + Angry, + Surprised, +} + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum ControlEvent { //ToggleLantern, @@ -111,6 +118,7 @@ pub enum ControlEvent { GroupManip(GroupManip), RemoveBuff(BuffKind), Respawn, + Utterance(UtteranceKind), } #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 8b6b775349..c8998b4a7d 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -67,7 +67,7 @@ pub use self::{ combo::Combo, controller::{ Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputAttr, - InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting, + InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting, UtteranceKind, }, energy::{Energy, EnergyChange, EnergySource}, fluid_dynamics::Fluid, diff --git a/common/src/outcome.rs b/common/src/outcome.rs index d1ec35e15c..feeb1e8fec 100644 --- a/common/src/outcome.rs +++ b/common/src/outcome.rs @@ -1,5 +1,5 @@ use crate::{comp, uid::Uid}; -use comp::{beam, item::Reagent, poise::PoiseState, skills::SkillGroupKind}; +use comp::{beam, item::Reagent, poise::PoiseState, skills::SkillGroupKind, UtteranceKind}; use hashbrown::HashSet; use serde::{Deserialize, Serialize}; use vek::*; @@ -73,6 +73,11 @@ pub enum Outcome { GroundSlam { pos: Vec3, }, + Utterance { + pos: Vec3, + body: comp::Body, + kind: UtteranceKind, + }, } impl Outcome { @@ -87,7 +92,8 @@ impl Outcome { | Outcome::Damage { pos, .. } | Outcome::Block { pos, .. } | Outcome::PoiseChange { pos, .. } - | Outcome::GroundSlam { pos } => Some(*pos), + | Outcome::GroundSlam { pos } + | Outcome::Utterance { pos, .. } => Some(*pos), Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)), Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None, } diff --git a/common/systems/src/controller.rs b/common/systems/src/controller.rs index 42b448ad80..daeb8f36e3 100644 --- a/common/systems/src/controller.rs +++ b/common/systems/src/controller.rs @@ -1,5 +1,5 @@ use common::{ - comp::{BuffChange, ControlEvent, Controller}, + comp::{BuffChange, ControlEvent, Controller, Pos, Body, agent::{Sound, SoundKind}}, event::{EventBus, ServerEvent}, uid::UidAllocator, }; @@ -7,7 +7,7 @@ use common_ecs::{Job, Origin, Phase, System}; use specs::{ saveload::{Marker, MarkerAllocator}, shred::ResourceId, - Entities, Join, Read, SystemData, World, WriteStorage, + Entities, Join, Read, SystemData, World, WriteStorage, ReadStorage, }; use vek::*; @@ -16,6 +16,8 @@ pub struct ReadData<'a> { entities: Entities<'a>, uid_allocator: Read<'a, UidAllocator>, server_bus: Read<'a, EventBus>, + positions: ReadStorage<'a, Pos>, + bodies: ReadStorage<'a, Body>, } #[derive(Default)] @@ -92,6 +94,20 @@ impl<'a> System<'a> for Sys { server_emitter.emit(ServerEvent::GroupManip(entity, manip)) }, ControlEvent::Respawn => server_emitter.emit(ServerEvent::Respawn(entity)), + ControlEvent::Utterance(kind) => { + if let (Some(pos), Some(body)) = ( + read_data.positions.get(entity), + read_data.bodies.get(entity), + ) { + let sound = Sound::new( + SoundKind::Utterance(kind, *body), + pos.0, + 8.0, // TODO: Come up with a better way of determining this + 1.0, + ); + server_emitter.emit(ServerEvent::Sound { sound }); + } + }, } } } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 03b5fe992a..f3b380f231 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -6,7 +6,7 @@ use common::{ assets, comp::{ self, - agent::{AgentEvent, Sound, MAX_LISTEN_DIST}, + agent::{AgentEvent, Sound, MAX_LISTEN_DIST, SoundKind}, dialogue::Subject, inventory::slot::EquipSlot, item, @@ -386,6 +386,7 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { let positions = &ecs.read_storage::(); let agents = &mut ecs.write_storage::(); + // TODO: Reduce the complexity of this problem by using spatial partitioning system for (agent, agent_pos) in (agents, positions).join() { // TODO: Use pathfinding for more dropoff around obstacles let agent_dist_sqrd = agent_pos.0.distance_squared(sound.pos); @@ -402,5 +403,19 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { .inbox .push_back(AgentEvent::ServerSound(propagated_sound)); } + + // Attempt to turn this sound into an outcome to be received by frontends. + if let Some(outcome) = match sound.kind { + SoundKind::Utterance(kind, body) => Some(Outcome::Utterance { + kind, + pos: sound.pos, + body, + }), + _ => None, + } { + ecs + .write_resource::>() + .push(outcome); + } } } diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index addd351b2f..67dc39634d 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -21,7 +21,7 @@ use common::{ Agent, Alignment, BehaviorCapability, BehaviorState, Body, CharacterAbility, CharacterState, ControlAction, ControlEvent, Controller, Energy, Health, HealthChange, InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori, PhysicsState, Pos, - Scale, SkillSet, Stats, UnresolvedChatMsg, Vel, + Scale, SkillSet, Stats, UnresolvedChatMsg, Vel, UtteranceKind, }, consts::GRAVITY, effect::{BuffEffect, Effect}, @@ -1548,6 +1548,10 @@ impl<'a> AgentData<'a> { .min_by_key(|(_, e_pos, _, _, _, _, _)| (e_pos.0.distance_squared(self.pos.0) * 100.0) as i32) // TODO choose target by more than just distance .map(|(e, _, _, _, _, _, _)| e); + if agent.target.is_none() && target.is_some() { + controller.push_event(ControlEvent::Utterance(UtteranceKind::Angry)); + } + agent.target = target.map(|target| Target { target, hostile: true, diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index f0ab9e4a55..57198031ed 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -96,6 +96,7 @@ use common::{ object, poise::PoiseState, Body, CharacterAbilityType, InventoryUpdateEvent, + UtteranceKind, }, outcome::Outcome, terrain::{BlockKind, TerrainChunk}, @@ -182,6 +183,7 @@ pub enum SfxEvent { FlameThrower, PoiseChange(PoiseState), GroundSlam, + Utterance(UtteranceKind, Body), } #[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] @@ -452,6 +454,10 @@ impl SfxMgr { audio.emit_sfx(sfx_trigger_item, *pos, None, false); }, }, + Outcome::Utterance { pos, kind, body } => { + let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Utterance(*kind, *body)); + audio.emit_sfx(sfx_trigger_item, *pos, None, false); + }, Outcome::ExpChange { .. } | Outcome::ComboChange { .. } | Outcome::SummonedCreature { .. } => {}, diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 76bd714891..1b70bd660f 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -248,7 +248,8 @@ impl ParticleMgr { | Outcome::SkillPointGain { .. } | Outcome::ComboChange { .. } | Outcome::Damage { .. } - | Outcome::PoiseChange { .. } => {}, + | Outcome::PoiseChange { .. } + | Outcome::Utterance { .. } => {}, } } From 0ef29a6989162bbee3dfc9184acb514078571acb Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 15 Jun 2021 17:57:52 +0100 Subject: [PATCH 30/38] VoiceKind for better sound effect specs --- assets/voxygen/audio/sfx.ron | 15 +++++++- .../audio/sfx/utterance/ogre_angry2.ogg | 4 +-- .../audio/sfx/utterance/ogre_angry3.ogg | 3 -- .../audio/sfx/utterance/saurok_angry.ogg | 3 ++ .../audio/sfx/utterance/wendigo_angry.mp3 | Bin 64307 -> 0 bytes common/src/comp/mod.rs | 3 +- common/systems/src/controller.rs | 7 ++-- server/src/events/interaction.rs | 9 +++-- server/src/sys/agent.rs | 2 +- voxygen/src/audio/sfx/mod.rs | 34 ++++++++++++++---- 10 files changed, 59 insertions(+), 21 deletions(-) delete mode 100644 assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/saurok_angry.ogg delete mode 100644 assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index 3eb3a6d828..cecb6f25d0 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -831,11 +831,24 @@ ], threshold: 0.2, ), - Utterance(Angry, BipedLarge((species: Wendigo, body_type: Female))): ( + Utterance(Angry, Wendigo): ( files: [ "voxygen.audio.sfx.utterance.wendigo_angry", ], threshold: 4.0, ), + Utterance(Angry, BipedLarge): ( + files: [ + "voxygen.audio.sfx.utterance.ogre_angry", + "voxygen.audio.sfx.utterance.ogre_angry2", + ], + threshold: 4.0, + ), + Utterance(Angry, Saurok): ( + files: [ + "voxygen.audio.sfx.utterance.saurok_angry", + ], + threshold: 4.0, + ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg index 65c67e368c..da49817cca 100644 --- a/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg +++ b/assets/voxygen/audio/sfx/utterance/ogre_angry2.ogg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83bbec96fb1192ac5d5c0907062242c5c37ba7f8e6f330556ee22e29423f14c6 -size 53337 +oid sha256:5c97dcb8f2ff2543a041d07147c04a74b59e43421dd7452eccb095a947bd7466 +size 52973 diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg deleted file mode 100644 index f4080ed7c2..0000000000 --- a/assets/voxygen/audio/sfx/utterance/ogre_angry3.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9f4ad549122e5fec0722260c11309079e99e20ec9d42be3655a44a416027d3f9 -size 55056 diff --git a/assets/voxygen/audio/sfx/utterance/saurok_angry.ogg b/assets/voxygen/audio/sfx/utterance/saurok_angry.ogg new file mode 100644 index 0000000000..a550c1bbe2 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/saurok_angry.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f859d590ac38bac163f0821eec8e2157f4f021d34d59575ccca2aeedde919ce +size 56816 diff --git a/assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 b/assets/voxygen/audio/sfx/utterance/wendigo_angry.mp3 deleted file mode 100644 index 87d5aec70f739eaa729bacb12fcb87c9a4f950a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64307 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 diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index c8998b4a7d..2242ad236e 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -67,7 +67,8 @@ pub use self::{ combo::Combo, controller::{ Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputAttr, - InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting, UtteranceKind, + InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting, + UtteranceKind, }, energy::{Energy, EnergyChange, EnergySource}, fluid_dynamics::Fluid, diff --git a/common/systems/src/controller.rs b/common/systems/src/controller.rs index daeb8f36e3..c576e22dc6 100644 --- a/common/systems/src/controller.rs +++ b/common/systems/src/controller.rs @@ -1,5 +1,8 @@ use common::{ - comp::{BuffChange, ControlEvent, Controller, Pos, Body, agent::{Sound, SoundKind}}, + comp::{ + agent::{Sound, SoundKind}, + Body, BuffChange, ControlEvent, Controller, Pos, + }, event::{EventBus, ServerEvent}, uid::UidAllocator, }; @@ -7,7 +10,7 @@ use common_ecs::{Job, Origin, Phase, System}; use specs::{ saveload::{Marker, MarkerAllocator}, shred::ResourceId, - Entities, Join, Read, SystemData, World, WriteStorage, ReadStorage, + Entities, Join, Read, ReadStorage, SystemData, World, WriteStorage, }; use vek::*; diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index f3b380f231..3a04f63aec 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -6,7 +6,7 @@ use common::{ assets, comp::{ self, - agent::{AgentEvent, Sound, MAX_LISTEN_DIST, SoundKind}, + agent::{AgentEvent, Sound, SoundKind, MAX_LISTEN_DIST}, dialogue::Subject, inventory::slot::EquipSlot, item, @@ -386,7 +386,8 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { let positions = &ecs.read_storage::(); let agents = &mut ecs.write_storage::(); - // TODO: Reduce the complexity of this problem by using spatial partitioning system + // TODO: Reduce the complexity of this problem by using spatial partitioning + // system for (agent, agent_pos) in (agents, positions).join() { // TODO: Use pathfinding for more dropoff around obstacles let agent_dist_sqrd = agent_pos.0.distance_squared(sound.pos); @@ -413,9 +414,7 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { }), _ => None, } { - ecs - .write_resource::>() - .push(outcome); + ecs.write_resource::>().push(outcome); } } } diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 67dc39634d..b0b3d88e34 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -21,7 +21,7 @@ use common::{ Agent, Alignment, BehaviorCapability, BehaviorState, Body, CharacterAbility, CharacterState, ControlAction, ControlEvent, Controller, Energy, Health, HealthChange, InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori, PhysicsState, Pos, - Scale, SkillSet, Stats, UnresolvedChatMsg, Vel, UtteranceKind, + Scale, SkillSet, Stats, UnresolvedChatMsg, UtteranceKind, Vel, }, consts::GRAVITY, effect::{BuffEffect, Effect}, diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 57198031ed..782825d0a4 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -91,12 +91,11 @@ use client::Client; use common::{ assets::{self, AssetExt, AssetHandle}, comp::{ - beam, + beam, biped_large, item::{ItemKind, ToolKind}, object, poise::PoiseState, - Body, CharacterAbilityType, InventoryUpdateEvent, - UtteranceKind, + Body, CharacterAbilityType, InventoryUpdateEvent, UtteranceKind, }, outcome::Outcome, terrain::{BlockKind, TerrainChunk}, @@ -183,7 +182,29 @@ pub enum SfxEvent { FlameThrower, PoiseChange(PoiseState), GroundSlam, - Utterance(UtteranceKind, Body), + Utterance(UtteranceKind, VoiceKind), +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] +pub enum VoiceKind { + Mute, + Human, + BipedLarge, + Wendigo, + Saurok, +} + +fn body_to_voice(body: &Body) -> VoiceKind { + match body { + Body::BipedLarge(body) => match body.species { + biped_large::Species::Wendigo => VoiceKind::Wendigo, + biped_large::Species::Occultsaurok + | biped_large::Species::Mightysaurok + | biped_large::Species::Slysaurok => VoiceKind::Saurok, + _ => VoiceKind::BipedLarge, + }, + _ => VoiceKind::Mute, + } } #[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] @@ -455,8 +476,9 @@ impl SfxMgr { }, }, Outcome::Utterance { pos, kind, body } => { - let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Utterance(*kind, *body)); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + let sfx_trigger_item = + triggers.get_key_value(&SfxEvent::Utterance(*kind, body_to_voice(body))); + audio.emit_sfx(sfx_trigger_item, *pos, Some(2.5), false); }, Outcome::ExpChange { .. } | Outcome::ComboChange { .. } From fb51fd3230ddfa7e24a4967b7e42d0cbc2a6fc10 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 15 Jun 2021 23:01:16 +0100 Subject: [PATCH 31/38] Emit sound from head --- common/src/comp/controller.rs | 1 + common/systems/src/controller.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index d157b87853..fafd138da2 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -101,6 +101,7 @@ pub enum UtteranceKind { Calm, Angry, Surprised, + Hurt, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/common/systems/src/controller.rs b/common/systems/src/controller.rs index c576e22dc6..2aea04b55a 100644 --- a/common/systems/src/controller.rs +++ b/common/systems/src/controller.rs @@ -104,7 +104,7 @@ impl<'a> System<'a> for Sys { ) { let sound = Sound::new( SoundKind::Utterance(kind, *body), - pos.0, + pos.0 + Vec3::unit_z() * body.eye_height(), 8.0, // TODO: Come up with a better way of determining this 1.0, ); From e3c44ba7fe4716622106d59cc991c2f8415d3d13 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 16 Jun 2021 13:49:43 +0100 Subject: [PATCH 32/38] More voice kinds, more opportunities for sounds --- assets/voxygen/audio/sfx.ron | 8 +++- .../audio/sfx/utterance/bird_angry.ogg | 3 ++ common/src/comp/controller.rs | 1 + server/src/sys/agent.rs | 6 +++ voxygen/src/audio/mod.rs | 4 +- voxygen/src/audio/sfx/mod.rs | 42 +++++++++++++++++-- 6 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 assets/voxygen/audio/sfx/utterance/bird_angry.ogg diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index cecb6f25d0..b4f3a0f5e8 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -844,11 +844,17 @@ ], threshold: 4.0, ), - Utterance(Angry, Saurok): ( + Utterance(Angry, Reptile): ( files: [ "voxygen.audio.sfx.utterance.saurok_angry", ], threshold: 4.0, ), + Utterance(Angry, Bird): ( + files: [ + "voxygen.audio.sfx.utterance.bird_angry", + ], + threshold: 4.0, + ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/bird_angry.ogg b/assets/voxygen/audio/sfx/utterance/bird_angry.ogg new file mode 100644 index 0000000000..bd0c9a7e77 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/bird_angry.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e028570750b872652dc16dff1dd4cafe34ad568bc079bd8ba379a5ceac66942 +size 11508 diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index fafd138da2..c27a4c1c50 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -102,6 +102,7 @@ pub enum UtteranceKind { Angry, Surprised, Hurt, + Greeting, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index b0b3d88e34..dd77610f29 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -945,6 +945,10 @@ impl<'a> AgentData<'a> { controller.actions.push(ControlAction::Unwield); } + if thread_rng().gen_bool(0.001) { + controller.push_event(ControlEvent::Utterance(UtteranceKind::Calm)); + } + // Sit if thread_rng().gen::() < 0.0035 { controller.actions.push(ControlAction::Sit); @@ -990,6 +994,8 @@ impl<'a> AgentData<'a> { if self.look_toward(controller, read_data, &target) { controller.actions.push(ControlAction::Stand); controller.actions.push(ControlAction::Talk); + controller.push_event(ControlEvent::Utterance(UtteranceKind::Greeting)); + match subject { Subject::Regular => { if let ( diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index d6811f77ab..c0fb8dc175 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -209,7 +209,7 @@ impl AudioFrontend { // TODO: Should this take `underwater` into consideration? match self.play_sfx(sfx_file, self.listener.pos, None, false) { Ok(_) => {}, - Err(e) => warn!("Failed to play sfx. {}", e), + Err(e) => warn!("Failed to play sfx '{:?}'. {}", sfx_file, e), } } else { debug!("Missing sfx trigger config for external sfx event.",); @@ -244,7 +244,7 @@ impl AudioFrontend { match self.play_sfx(sfx_file, position, volume, underwater) { Ok(_) => {}, - Err(e) => warn!("Failed to play sfx. {}", e), + Err(e) => warn!("Failed to play sfx '{:?}'. {}", sfx_file, e), } } else { debug!( diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 782825d0a4..c4a0905337 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -95,7 +95,8 @@ use common::{ item::{ItemKind, ToolKind}, object, poise::PoiseState, - Body, CharacterAbilityType, InventoryUpdateEvent, UtteranceKind, + quadruped_medium, quadruped_small, Body, CharacterAbilityType, InventoryUpdateEvent, + UtteranceKind, }, outcome::Outcome, terrain::{BlockKind, TerrainChunk}, @@ -191,18 +192,53 @@ pub enum VoiceKind { Human, BipedLarge, Wendigo, - Saurok, + Reptile, + Bird, + Critter, + Sheep, + Pig, + Cow, + Canine, + BigCat, } fn body_to_voice(body: &Body) -> VoiceKind { match body { + Body::Humanoid(_) => VoiceKind::Human, + Body::QuadrupedSmall(body) => match body.species { + quadruped_small::Species::Sheep => VoiceKind::Sheep, + quadruped_small::Species::Pig | quadruped_small::Species::Boar => VoiceKind::Pig, + _ => VoiceKind::Critter, + }, + Body::QuadrupedMedium(body) => match body.species { + quadruped_medium::Species::Saber + | quadruped_medium::Species::Tiger + | quadruped_medium::Species::Lion + | quadruped_medium::Species::Frostfang + | quadruped_medium::Species::Snowleopard => VoiceKind::BigCat, + quadruped_medium::Species::Wolf + | quadruped_medium::Species::Roshwalr + | quadruped_medium::Species::Tarasque + | quadruped_medium::Species::Darkhound + | quadruped_medium::Species::Bonerattler + | quadruped_medium::Species::Grolgar => VoiceKind::Canine, + quadruped_medium::Species::Cattle + | quadruped_medium::Species::Catoblepas + | quadruped_medium::Species::Highland + | quadruped_medium::Species::Yak + | quadruped_medium::Species::Moose + | quadruped_medium::Species::Dreadhorn => VoiceKind::Cow, + _ => VoiceKind::Mute, + }, + Body::BirdMedium(_) | Body::BirdLarge(_) => VoiceKind::Bird, Body::BipedLarge(body) => match body.species { biped_large::Species::Wendigo => VoiceKind::Wendigo, biped_large::Species::Occultsaurok | biped_large::Species::Mightysaurok - | biped_large::Species::Slysaurok => VoiceKind::Saurok, + | biped_large::Species::Slysaurok => VoiceKind::Reptile, _ => VoiceKind::BipedLarge, }, + Body::Theropod(_) | Body::Dragon(_) => VoiceKind::Reptile, _ => VoiceKind::Mute, } } From 9510869870504692e9f7b3c21ad9bec3a9a1eefa Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 16 Jun 2021 15:04:00 +0100 Subject: [PATCH 33/38] Significantly more efficient sound effect processing, more NPC sounds --- assets/voxygen/audio/sfx.ron | 24 ++++++++++++++ .../voxygen/audio/sfx/utterance/cow_calm.ogg | 3 ++ .../sfx/utterance/humanmale_greeting.ogg | 3 ++ .../voxygen/audio/sfx/utterance/pig_calm.ogg | 3 ++ .../audio/sfx/utterance/sheep_calm.ogg | 3 ++ server/src/sys/agent.rs | 16 +++++++++- voxygen/src/audio/mod.rs | 12 +++---- voxygen/src/audio/sfx/mod.rs | 10 ++++-- voxygen/src/audio/soundcache.rs | 32 +++++++++---------- 9 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 assets/voxygen/audio/sfx/utterance/cow_calm.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/pig_calm.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/sheep_calm.ogg diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index b4f3a0f5e8..1f6ae01af3 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -856,5 +856,29 @@ ], threshold: 4.0, ), + Utterance(Calm, Pig): ( + files: [ + "voxygen.audio.sfx.utterance.pig_calm", + ], + threshold: 4.0, + ), + Utterance(Calm, Cow): ( + files: [ + "voxygen.audio.sfx.utterance.cow_calm", + ], + threshold: 4.0, + ), + Utterance(Calm, Sheep): ( + files: [ + "voxygen.audio.sfx.utterance.sheep_calm", + ], + threshold: 4.0, + ), + Utterance(Greeting, HumanMale): ( + files: [ + "voxygen.audio.sfx.utterance.humanmale_greeting", + ], + threshold: 4.0, + ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/cow_calm.ogg b/assets/voxygen/audio/sfx/utterance/cow_calm.ogg new file mode 100644 index 0000000000..fcb6058d93 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/cow_calm.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb54f07923524c80c8cb35d908165fa55582f0b085790473802fbb578766f347 +size 22691 diff --git a/assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg b/assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg new file mode 100644 index 0000000000..69dbe5a088 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac12e8915ff261097d5aa24debf5685c4da3bc439ba64e6fd0bc12070f2ebff7 +size 12604 diff --git a/assets/voxygen/audio/sfx/utterance/pig_calm.ogg b/assets/voxygen/audio/sfx/utterance/pig_calm.ogg new file mode 100644 index 0000000000..b5660f8b39 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/pig_calm.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:397fca64b10178b05d5007b86e166e2ff335380dae69491f95df0e5724b7c2a5 +size 10729 diff --git a/assets/voxygen/audio/sfx/utterance/sheep_calm.ogg b/assets/voxygen/audio/sfx/utterance/sheep_calm.ogg new file mode 100644 index 0000000000..23e9151f17 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/sheep_calm.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32fd1868f9f58774e1cd93b5e577795e20a6c3f93683ba4adc7666f2157d0eb3 +size 12028 diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index dd77610f29..d6dab17616 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -409,6 +409,14 @@ impl<'a> System<'a> for Sys { .uid_allocator .retrieve_entity_internal(by.id()) { + if agent.target.is_none() { + controller.push_event( + ControlEvent::Utterance( + UtteranceKind::Angry, + ), + ); + } + agent.target = Some(Target { target: attacker, hostile: true, @@ -511,6 +519,12 @@ impl<'a> System<'a> for Sys { &mut event_emitter, ); } else { + if agent.target.is_none() { + controller.push_event(ControlEvent::Utterance( + UtteranceKind::Angry, + )); + } + agent.target = Some(Target { target: attacker, hostile: true, @@ -945,7 +959,7 @@ impl<'a> AgentData<'a> { controller.actions.push(ControlAction::Unwield); } - if thread_rng().gen_bool(0.001) { + if thread_rng().gen::() < 0.0015 { controller.push_event(ControlEvent::Utterance(UtteranceKind::Calm)); } diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index c0fb8dc175..e6f50f9e9f 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -265,8 +265,8 @@ impl AudioFrontend { ) -> Result<(), rodio::decoder::DecoderError> { if self.audio_stream.is_some() { let sound = OggSound::load_expect(sound) - .cloned() - .decoder()? + .read() + .to_source() .amplify(vol.unwrap_or(1.0)); let listener = self.listener.clone(); @@ -291,9 +291,7 @@ impl AudioFrontend { ) { if self.audio_stream.is_some() { if let Some(channel) = self.get_ambient_channel(channel_tag, volume_multiplier) { - if let Ok(sound) = OggSound::load_expect(sound).cloned().decoder() { - channel.play(sound); - } + channel.play(OggSound::load_expect(sound).read().to_source()); } } } @@ -349,9 +347,7 @@ impl AudioFrontend { fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag) { if self.music_enabled() { if let Some(channel) = self.get_music_channel(channel_tag) { - if let Ok(sound) = OggSound::load_expect(sound).cloned().decoder() { - channel.play(sound, channel_tag); - } + channel.play(OggSound::load_expect(sound).read().to_source(), channel_tag); } } } diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index c4a0905337..99193012be 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -91,7 +91,7 @@ use client::Client; use common::{ assets::{self, AssetExt, AssetHandle}, comp::{ - beam, biped_large, + beam, biped_large, humanoid, item::{ItemKind, ToolKind}, object, poise::PoiseState, @@ -189,7 +189,8 @@ pub enum SfxEvent { #[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] pub enum VoiceKind { Mute, - Human, + HumanFemale, + HumanMale, BipedLarge, Wendigo, Reptile, @@ -204,7 +205,10 @@ pub enum VoiceKind { fn body_to_voice(body: &Body) -> VoiceKind { match body { - Body::Humanoid(_) => VoiceKind::Human, + Body::Humanoid(body) => match &body.body_type { + humanoid::BodyType::Female => VoiceKind::HumanFemale, + humanoid::BodyType::Male => VoiceKind::HumanMale, + }, Body::QuadrupedSmall(body) => match body.species { quadruped_small::Species::Sheep => VoiceKind::Sheep, quadruped_small::Species::Pig | quadruped_small::Species::Boar => VoiceKind::Pig, diff --git a/voxygen/src/audio/soundcache.rs b/voxygen/src/audio/soundcache.rs index 545f6cef42..7fcdae187f 100644 --- a/voxygen/src/audio/soundcache.rs +++ b/voxygen/src/audio/soundcache.rs @@ -1,6 +1,7 @@ //! Handles caching and retrieval of decoded `.ogg` sfx sound data, eliminating //! the need to decode files on each playback -use common::assets; +use common::assets::{self, Loader}; +use rodio::{source::Buffered, Decoder, Source}; use std::{borrow::Cow, io, sync::Arc}; use tracing::warn; @@ -10,16 +11,16 @@ use tracing::warn; pub struct SoundLoader; #[derive(Clone)] -pub struct OggSound(Arc>); +pub struct OggSound(Buffered>>>); -impl AsRef<[u8]> for OggSound { - fn as_ref(&self) -> &[u8] { &self.0 } -} +// impl AsRef<[u8]> for OggSound { +// fn as_ref(&self) -> &[u8] { &self.0 } +// } -impl assets::Loader for SoundLoader { +impl Loader for SoundLoader { fn load(content: Cow<[u8]>, _: &str) -> Result { - let arc = Arc::new(content.into_owned()); - Ok(OggSound(arc)) + let source = Decoder::new(io::Cursor::new(content.into_owned()))?.buffered(); + Ok(OggSound(source)) } } @@ -37,16 +38,13 @@ impl assets::Asset for OggSound { /// Wrapper for decoded audio data impl OggSound { - pub fn decoder( - self, - ) -> Result>, rodio::decoder::DecoderError> { - let cursor = io::Cursor::new(self); - rodio::Decoder::new(cursor) - } + pub fn to_source(&self) -> impl Source + Iterator { self.0.clone() } pub fn empty() -> OggSound { - OggSound(Arc::new( - include_bytes!("../../../assets/voxygen/audio/null.ogg").to_vec(), - )) + SoundLoader::load( + Cow::Borrowed(include_bytes!("../../../assets/voxygen/audio/null.ogg")), + "empty", + ) + .unwrap() } } From b8749dc21941996e975e8886e0a6369202f5dbf6 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 16 Jun 2021 17:16:05 +0100 Subject: [PATCH 34/38] Added more cow noises, prepare for merge --- CHANGELOG.md | 4 + assets/voxygen/audio/sfx.ron | 102 +++++++++--------- .../voxygen/audio/sfx/utterance/cow_calm2.ogg | 3 + .../voxygen/audio/sfx/utterance/cow_calm3.ogg | 3 + client/src/lib.rs | 6 +- server/src/events/interaction.rs | 22 ++-- voxygen/src/audio/sfx/mod.rs | 30 ++++-- voxygen/src/audio/soundcache.rs | 6 +- voxygen/src/session/mod.rs | 7 +- voxygen/src/settings/control.rs | 1 + voxygen/src/window.rs | 3 + 11 files changed, 108 insertions(+), 79 deletions(-) create mode 100644 assets/voxygen/audio/sfx/utterance/cow_calm2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/cow_calm3.ogg diff --git a/CHANGELOG.md b/CHANGELOG.md index a556f9bff8..6ad28d3149 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added a skill tree for mining, which gains xp from mining ores and gems. - Added debug line info to release builds, enhancing the usefulness of panic backtraces +- NPCs and animals can now make sounds in response to certain events +- Players can press H to greet others ### Changed - Entity-entity pushback is no longer applied in forced movement states like rolling and leaping. @@ -18,7 +20,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed ### Fixed + - Cases where no audio output could be produced before. +- Significantly improved the performance of playing sound effects ## [0.10.0] - 2021-06-12 diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index 1f6ae01af3..81bca564d1 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -5,7 +5,7 @@ // Campfire: ( files: [ - "voxygen.audio.sfx.ambient.fire", + "voxygen.audio.sfx.ambient.fire", ], threshold: 21.835, ), @@ -831,54 +831,56 @@ ], threshold: 0.2, ), - Utterance(Angry, Wendigo): ( - files: [ - "voxygen.audio.sfx.utterance.wendigo_angry", - ], - threshold: 4.0, - ), - Utterance(Angry, BipedLarge): ( - files: [ - "voxygen.audio.sfx.utterance.ogre_angry", - "voxygen.audio.sfx.utterance.ogre_angry2", - ], - threshold: 4.0, - ), - Utterance(Angry, Reptile): ( - files: [ - "voxygen.audio.sfx.utterance.saurok_angry", - ], - threshold: 4.0, - ), - Utterance(Angry, Bird): ( - files: [ - "voxygen.audio.sfx.utterance.bird_angry", - ], - threshold: 4.0, - ), - Utterance(Calm, Pig): ( - files: [ - "voxygen.audio.sfx.utterance.pig_calm", - ], - threshold: 4.0, - ), - Utterance(Calm, Cow): ( - files: [ - "voxygen.audio.sfx.utterance.cow_calm", - ], - threshold: 4.0, - ), - Utterance(Calm, Sheep): ( - files: [ - "voxygen.audio.sfx.utterance.sheep_calm", - ], - threshold: 4.0, - ), - Utterance(Greeting, HumanMale): ( - files: [ - "voxygen.audio.sfx.utterance.humanmale_greeting", - ], - threshold: 4.0, - ), + Utterance(Angry, Wendigo): ( + files: [ + "voxygen.audio.sfx.utterance.wendigo_angry", + ], + threshold: 0.2, + ), + Utterance(Angry, BipedLarge): ( + files: [ + "voxygen.audio.sfx.utterance.ogre_angry", + "voxygen.audio.sfx.utterance.ogre_angry2", + ], + threshold: 0.2, + ), + Utterance(Angry, Reptile): ( + files: [ + "voxygen.audio.sfx.utterance.saurok_angry", + ], + threshold: 0.2, + ), + Utterance(Angry, Bird): ( + files: [ + "voxygen.audio.sfx.utterance.bird_angry", + ], + threshold: 0.2, + ), + Utterance(Calm, Pig): ( + files: [ + "voxygen.audio.sfx.utterance.pig_calm", + ], + threshold: 0.2, + ), + Utterance(Calm, Cow): ( + files: [ + "voxygen.audio.sfx.utterance.cow_calm", + "voxygen.audio.sfx.utterance.cow_calm2", + "voxygen.audio.sfx.utterance.cow_calm3", + ], + threshold: 0.2, + ), + Utterance(Calm, Sheep): ( + files: [ + "voxygen.audio.sfx.utterance.sheep_calm", + ], + threshold: 0.2, + ), + Utterance(Greeting, HumanMale): ( + files: [ + "voxygen.audio.sfx.utterance.humanmale_greeting", + ], + threshold: 0.2, + ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/cow_calm2.ogg b/assets/voxygen/audio/sfx/utterance/cow_calm2.ogg new file mode 100644 index 0000000000..f060217fe4 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/cow_calm2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:581a6c86eb6f06ed2694d0cdfa08691fa8bf38fd7fffb57680b41ed5dc1060ed +size 23328 diff --git a/assets/voxygen/audio/sfx/utterance/cow_calm3.ogg b/assets/voxygen/audio/sfx/utterance/cow_calm3.ogg new file mode 100644 index 0000000000..59ae620508 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/cow_calm3.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb77a69af59eb015c20de58bad181f7e02bc82649d33c274302018c8baf4b85a +size 14690 diff --git a/client/src/lib.rs b/client/src/lib.rs index 661c2ad5f7..9f09ffb9d0 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -28,7 +28,7 @@ use common::{ skills::Skill, slot::Slot, ChatMode, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputKind, - InventoryAction, InventoryEvent, InventoryUpdateEvent, + InventoryAction, InventoryEvent, InventoryUpdateEvent, UtteranceKind, }, event::{EventBus, LocalEvent}, grid::Grid, @@ -1224,6 +1224,10 @@ impl Client { } } + pub fn utter(&mut self, kind: UtteranceKind) { + self.send_msg(ClientGeneral::ControlEvent(ControlEvent::Utterance(kind))); + } + pub fn toggle_sneak(&mut self) { let is_sneaking = self .state diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 3a04f63aec..acd935f9a6 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -404,17 +404,17 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { .inbox .push_back(AgentEvent::ServerSound(propagated_sound)); } + } - // Attempt to turn this sound into an outcome to be received by frontends. - if let Some(outcome) = match sound.kind { - SoundKind::Utterance(kind, body) => Some(Outcome::Utterance { - kind, - pos: sound.pos, - body, - }), - _ => None, - } { - ecs.write_resource::>().push(outcome); - } + // Attempt to turn this sound into an outcome to be received by frontends. + if let Some(outcome) = match sound.kind { + SoundKind::Utterance(kind, body) => Some(Outcome::Utterance { + kind, + pos: sound.pos, + body, + }), + _ => None, + } { + ecs.write_resource::>().push(outcome); } } diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 99193012be..1f5fe7ab89 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -106,7 +106,7 @@ use event_mapper::SfxEventMapper; use hashbrown::HashMap; use rand::prelude::*; use serde::Deserialize; -use tracing::warn; +use tracing::{debug, warn}; use vek::*; /// We watch the states of nearby entities in order to emit SFX at their @@ -186,9 +186,8 @@ pub enum SfxEvent { Utterance(UtteranceKind, VoiceKind), } -#[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)] pub enum VoiceKind { - Mute, HumanFemale, HumanMale, BipedLarge, @@ -203,8 +202,8 @@ pub enum VoiceKind { BigCat, } -fn body_to_voice(body: &Body) -> VoiceKind { - match body { +fn body_to_voice(body: &Body) -> Option { + Some(match body { Body::Humanoid(body) => match &body.body_type { humanoid::BodyType::Female => VoiceKind::HumanFemale, humanoid::BodyType::Male => VoiceKind::HumanMale, @@ -232,7 +231,7 @@ fn body_to_voice(body: &Body) -> VoiceKind { | quadruped_medium::Species::Yak | quadruped_medium::Species::Moose | quadruped_medium::Species::Dreadhorn => VoiceKind::Cow, - _ => VoiceKind::Mute, + _ => return None, }, Body::BirdMedium(_) | Body::BirdLarge(_) => VoiceKind::Bird, Body::BipedLarge(body) => match body.species { @@ -243,8 +242,8 @@ fn body_to_voice(body: &Body) -> VoiceKind { _ => VoiceKind::BipedLarge, }, Body::Theropod(_) | Body::Dragon(_) => VoiceKind::Reptile, - _ => VoiceKind::Mute, - } + _ => return None, + }) } #[derive(Clone, Debug, PartialEq, Deserialize, Hash, Eq)] @@ -516,9 +515,18 @@ impl SfxMgr { }, }, Outcome::Utterance { pos, kind, body } => { - let sfx_trigger_item = - triggers.get_key_value(&SfxEvent::Utterance(*kind, body_to_voice(body))); - audio.emit_sfx(sfx_trigger_item, *pos, Some(2.5), false); + if let Some(voice) = body_to_voice(body) { + let sfx_trigger_item = + triggers.get_key_value(&SfxEvent::Utterance(*kind, voice)); + if let Some(sfx_trigger_item) = sfx_trigger_item { + audio.emit_sfx(Some(sfx_trigger_item), *pos, Some(2.5), false); + } else { + debug!( + "No utterance sound effect exists for ({:?}, {:?})", + kind, voice + ); + } + } }, Outcome::ExpChange { .. } | Outcome::ComboChange { .. } diff --git a/voxygen/src/audio/soundcache.rs b/voxygen/src/audio/soundcache.rs index 7fcdae187f..f08a4bdab6 100644 --- a/voxygen/src/audio/soundcache.rs +++ b/voxygen/src/audio/soundcache.rs @@ -2,7 +2,7 @@ //! the need to decode files on each playback use common::assets::{self, Loader}; use rodio::{source::Buffered, Decoder, Source}; -use std::{borrow::Cow, io, sync::Arc}; +use std::{borrow::Cow, io}; use tracing::warn; // Implementation of sound taken from this github issue: @@ -13,10 +13,6 @@ pub struct SoundLoader; #[derive(Clone)] pub struct OggSound(Buffered>>>); -// impl AsRef<[u8]> for OggSound { -// fn as_ref(&self) -> &[u8] { &self.0 } -// } - impl Loader for SoundLoader { fn load(content: Cow<[u8]>, _: &str) -> Result { let source = Decoder::new(io::Cursor::new(content.into_owned()))?.buffered(); diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 9abef76774..104459f2b8 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -15,7 +15,7 @@ use common::{ inventory::slot::{EquipSlot, Slot}, invite::InviteKind, item::{tool::ToolKind, ItemDef, ItemDesc}, - ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Vel, + ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, UtteranceKind, Vel, }, consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE}, outcome::Outcome, @@ -525,6 +525,11 @@ impl PlayState for SessionState { self.client.borrow_mut().toggle_dance(); } }, + GameInput::Greet => { + if state { + self.client.borrow_mut().utter(UtteranceKind::Greeting); + } + }, GameInput::Sneak => { if state { self.stop_auto_walk(); diff --git a/voxygen/src/settings/control.rs b/voxygen/src/settings/control.rs index 67b4defb63..c0b9eb1bd6 100644 --- a/voxygen/src/settings/control.rs +++ b/voxygen/src/settings/control.rs @@ -123,6 +123,7 @@ impl ControlSettings { GameInput::Jump => KeyMouse::Key(VirtualKeyCode::Space), GameInput::Sit => KeyMouse::Key(VirtualKeyCode::K), GameInput::Dance => KeyMouse::Key(VirtualKeyCode::J), + GameInput::Greet => KeyMouse::Key(VirtualKeyCode::H), GameInput::Glide => KeyMouse::Key(VirtualKeyCode::LShift), GameInput::Climb => KeyMouse::Key(VirtualKeyCode::Space), GameInput::ClimbDown => KeyMouse::Key(VirtualKeyCode::LControl), diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 2dfe0a9c85..118fe109e9 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -39,6 +39,7 @@ pub enum GameInput { Jump, Sit, Dance, + Greet, Glide, Climb, ClimbDown, @@ -94,6 +95,7 @@ impl GameInput { GameInput::Jump => "gameinput.jump", GameInput::Sit => "gameinput.sit", GameInput::Dance => "gameinput.dance", + GameInput::Greet => "gameinput.greet", GameInput::Glide => "gameinput.glide", GameInput::Climb => "gameinput.climb", GameInput::ClimbDown => "gameinput.climbdown", @@ -159,6 +161,7 @@ impl GameInput { GameInput::Jump, GameInput::Sit, GameInput::Dance, + GameInput::Greet, GameInput::Glide, GameInput::Climb, GameInput::ClimbDown, From 9454c34cc2c9535c463577a4758f0aa77603b3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Wed, 16 Jun 2021 20:22:33 +0100 Subject: [PATCH 35/38] Submit queue when maintaining and minimized --- voxygen/src/render/renderer.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index f917a83e2a..b43e7cb2e8 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -600,7 +600,13 @@ impl Renderer { Ok(()) } - pub fn maintain(&self) { self.device.poll(wgpu::Maintain::Poll) } + pub fn maintain(&self) { + if self.is_minimized { + self.queue.submit(std::iter::empty()); + } + + self.device.poll(wgpu::Maintain::Poll) + } /// Create render target views fn create_rt_views( From 0941f03608ad2305f0767070fb8e65a6efed4252 Mon Sep 17 00:00:00 2001 From: jshipsey Date: Tue, 15 Jun 2021 01:43:49 -0400 Subject: [PATCH 36/38] buffs to stretch progression --- .../common/abilities/ability_set_manifest.ron | 15 ++- assets/common/abilities/axe/doublestrike.ron | 4 +- .../common/abilities/custom/asp/firebomb.ron | 18 ++++ .../{quadlowranged => asp}/singlestrike.ron | 8 +- .../abilities/custom/basilisk/petrify.ron | 19 ++++ .../custom/basilisk/triplestrike.ron | 14 +-- .../abilities/custom/husk/singlestrike.ron | 4 +- .../abilities/custom/husk/triplestrike.ron | 24 ++--- .../abilities/custom/maneater/poisonball.ron | 18 ++++ .../{basilisk => maneater}/singlestrike.ron | 14 +-- .../custom/mindflayer/necroticsphere.ron | 2 +- .../custom/mindflayer/necroticvortex.ron | 4 +- .../custom/quadlowbasic/singlestrike.ron | 4 +- .../custom/quadlowbasic/triplestrike.ron | 12 +-- .../abilities/custom/quadlowbreathe/dash.ron | 4 +- .../custom/quadlowbreathe/flamethrower.ron | 2 +- .../custom/quadlowbreathe/triplestrike.ron | 18 ++-- .../custom/quadlowquick/quadstrike.ron | 6 +- .../custom/quadlowranged/firebomb.ron | 6 +- .../abilities/custom/quadlowtail/charged.ron | 2 +- .../custom/quadmedbasic/singlestrike.ron | 2 +- .../custom/quadmedcharge/doublestrike.ron | 4 +- .../abilities/custom/quadmedhoof/basic.ron | 6 +- .../custom/quadsmallbasic/singlestrike.ron | 2 +- .../custom/theropodbasic/singlestrike.ron | 4 +- .../custom/theropodbasic/triplestrike.ron | 18 ++-- .../custom/wendigomagic/frostbomb.ron | 2 +- .../custom/wendigomagic/singlestrike.ron | 4 +- .../common/abilities/hammer/singlestrike.ron | 2 +- .../abilities/hammersimple/doublestrike.ron | 16 ++-- .../common/abilities/sword/triplestrike.ron | 4 +- assets/common/cave_scatter/dark_floor.ron | 7 +- assets/common/cave_scatter/deep_floor.ron | 10 +- assets/common/cave_scatter/shallow_floor.ron | 2 +- .../items/npc_armor/biped_large/generic.ron | 17 ++++ .../npc_armor/biped_large/mindflayer.ron | 2 +- .../items/npc_armor/quadruped_low/generic.ron | 17 ++++ .../items/npc_armor/quadruped_low/shell.ron | 17 ++++ .../items/npc_armor/theropod/rugged.ron | 17 ++++ .../unique/{quadlowranged.ron => asp.ron} | 4 +- .../items/npc_weapons/unique/maneater.ron | 18 ++++ .../creature/quad_small/generic.ron | 2 +- assets/common/loot_tables/trading.ron | 13 ++- assets/common/recipe_book.ron | 91 +++++++++---------- assets/voxygen/i18n/en/hud/misc.ron | 4 +- common/src/comp/body.rs | 9 +- common/src/comp/inventory/loadout_builder.rs | 49 +++++++++- common/src/states/utils.rs | 4 +- server/src/sys/agent.rs | 6 +- world/src/layer/mod.rs | 10 +- 50 files changed, 380 insertions(+), 181 deletions(-) create mode 100644 assets/common/abilities/custom/asp/firebomb.ron rename assets/common/abilities/custom/{quadlowranged => asp}/singlestrike.ron (79%) create mode 100644 assets/common/abilities/custom/basilisk/petrify.ron create mode 100644 assets/common/abilities/custom/maneater/poisonball.ron rename assets/common/abilities/custom/{basilisk => maneater}/singlestrike.ron (67%) create mode 100644 assets/common/items/npc_armor/biped_large/generic.ron create mode 100644 assets/common/items/npc_armor/quadruped_low/generic.ron create mode 100644 assets/common/items/npc_armor/quadruped_low/shell.ron create mode 100644 assets/common/items/npc_armor/theropod/rugged.ron rename assets/common/items/npc_weapons/unique/{quadlowranged.ron => asp.ron} (80%) create mode 100644 assets/common/items/npc_weapons/unique/maneater.ron diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index ef9979ce3f..a9e33c3b0b 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -149,13 +149,18 @@ abilities: [], ), Custom("Basilisk"): ( - primary: "common.abilities.custom.basilisk.singlestrike", - secondary: "common.abilities.custom.basilisk.triplestrike", + primary: "common.abilities.custom.basilisk.triplestrike", + secondary: "common.abilities.custom.basilisk.petrify", abilities: [], ), - Custom("Quad Low Ranged"): ( - primary: "common.abilities.custom.quadlowranged.singlestrike", - secondary: "common.abilities.custom.quadlowranged.firebomb", + Custom("Asp"): ( + primary: "common.abilities.custom.asp.singlestrike", + secondary: "common.abilities.custom.asp.firebomb", + abilities: [], + ), + Custom("Maneater"): ( + primary: "common.abilities.custom.maneater.singlestrike", + secondary: "common.abilities.custom.maneater.poisonball", abilities: [], ), Custom("Quad Low Breathe"): ( diff --git a/assets/common/abilities/axe/doublestrike.ron b/assets/common/abilities/axe/doublestrike.ron index 029bb4cdaf..80de5e37e0 100644 --- a/assets/common/abilities/axe/doublestrike.ron +++ b/assets/common/abilities/axe/doublestrike.ron @@ -6,7 +6,7 @@ ComboMelee( base_poise_damage: 12, damage_increase: 10, poise_damage_increase: 0, - knockback: 8.0, + knockback: 5.0, range: 3.5, angle: 50.0, base_buildup_duration: 0.15, @@ -22,7 +22,7 @@ ComboMelee( base_poise_damage: 20, damage_increase: 15, poise_damage_increase: 0, - knockback: 12.0, + knockback: 6.0, range: 3.5, angle: 30.0, base_buildup_duration: 0.2, diff --git a/assets/common/abilities/custom/asp/firebomb.ron b/assets/common/abilities/custom/asp/firebomb.ron new file mode 100644 index 0000000000..383f35ee39 --- /dev/null +++ b/assets/common/abilities/custom/asp/firebomb.ron @@ -0,0 +1,18 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.8, + recover_duration: 0.35, + projectile: Fireball( + damage: 130.0, + radius: 5.0, + energy_regen: 0, + ), + projectile_body: Object(BoltFire), + /*projectile_light: Some(LightEmitter { + col: (1.0, 0.75, 0.11).into(), + ..Default::default() + }),*/ + projectile_speed: 70.0, + num_projectiles: 1, + projectile_spread: 0.0, +) diff --git a/assets/common/abilities/custom/quadlowranged/singlestrike.ron b/assets/common/abilities/custom/asp/singlestrike.ron similarity index 79% rename from assets/common/abilities/custom/quadlowranged/singlestrike.ron rename to assets/common/abilities/custom/asp/singlestrike.ron index 81b81f3cd0..ec3acbcd37 100644 --- a/assets/common/abilities/custom/quadlowranged/singlestrike.ron +++ b/assets/common/abilities/custom/asp/singlestrike.ron @@ -2,18 +2,18 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 80, + base_damage: 140, damage_increase: 0, base_poise_damage: 28, poise_damage_increase: 0, knockback: 3.0, range: 3.5, angle: 60.0, - base_buildup_duration: 0.4, + base_buildup_duration: 0.6, base_swing_duration: 0.1, hit_timing: 0.5, - base_recover_duration: 0.4, - forward_movement: 3.0, + base_recover_duration: 0.2, + forward_movement: 2.0, damage_kind: Crushing, ), ], diff --git a/assets/common/abilities/custom/basilisk/petrify.ron b/assets/common/abilities/custom/basilisk/petrify.ron new file mode 100644 index 0000000000..c8fb5b3021 --- /dev/null +++ b/assets/common/abilities/custom/basilisk/petrify.ron @@ -0,0 +1,19 @@ +BasicBeam( + buildup_duration: 0.9, + recover_duration: 1.0, + beam_duration: 1.0, + damage: 420, + tick_rate: 0.5, + range: 22.0, + max_angle: 5.0, + damage_effect: Some(Buff(( + kind: Frozen, + dur_secs: 0.8, + strength: Value(5.0), + chance: 1.0, + ))), + energy_regen: 0, + energy_drain: 0, + orientation_behavior: Normal, + specifier: Cultist, +) \ No newline at end of file diff --git a/assets/common/abilities/custom/basilisk/triplestrike.ron b/assets/common/abilities/custom/basilisk/triplestrike.ron index cd4942d2bb..db05cf3e26 100644 --- a/assets/common/abilities/custom/basilisk/triplestrike.ron +++ b/assets/common/abilities/custom/basilisk/triplestrike.ron @@ -2,14 +2,14 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 100, + base_damage: 180, damage_increase: 0, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.8, angle: 30.0, - base_buildup_duration: 0.65, + base_buildup_duration: 0.7, base_swing_duration: 0.07, hit_timing: 0.5, base_recover_duration: 0.3, @@ -18,11 +18,11 @@ ComboMelee( ), ( stage: 2, - base_damage: 100, + base_damage: 180, damage_increase: 0, base_poise_damage: 18, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.8, angle: 30.0, base_buildup_duration: 0.4, @@ -34,11 +34,11 @@ ComboMelee( ), ( stage: 3, - base_damage: 100, + base_damage: 180, damage_increase: 0, base_poise_damage: 20, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.8, angle: 30.0, base_buildup_duration: 0.4, diff --git a/assets/common/abilities/custom/husk/singlestrike.ron b/assets/common/abilities/custom/husk/singlestrike.ron index 92e1e2d3eb..d64184bfa8 100644 --- a/assets/common/abilities/custom/husk/singlestrike.ron +++ b/assets/common/abilities/custom/husk/singlestrike.ron @@ -2,12 +2,12 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 90, + base_damage: 160, damage_increase: 0, base_poise_damage: 12, poise_damage_increase: 0, knockback: 5.0, - range: 3.5, + range: 2.5, angle: 60.0, base_buildup_duration: 0.25, base_swing_duration: 0.07, diff --git a/assets/common/abilities/custom/husk/triplestrike.ron b/assets/common/abilities/custom/husk/triplestrike.ron index 25e985d3d2..d4b66e6a50 100644 --- a/assets/common/abilities/custom/husk/triplestrike.ron +++ b/assets/common/abilities/custom/husk/triplestrike.ron @@ -2,12 +2,12 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 120, + base_damage: 160, damage_increase: 0, - base_poise_damage: 8, + base_poise_damage: 6, poise_damage_increase: 0, - knockback: 5.0, - range: 3.5, + knockback: 3.0, + range: 2.5, angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, @@ -18,12 +18,12 @@ ComboMelee( ), ( stage: 2, - base_damage: 120, + base_damage: 160, damage_increase: 0, - base_poise_damage: 10, + base_poise_damage: 8, poise_damage_increase: 0, - knockback: 5.0, - range: 3.5, + knockback: 3.0, + range: 2.5, angle: 30.0, base_buildup_duration: 0.22, base_swing_duration: 0.07, @@ -34,12 +34,12 @@ ComboMelee( ), ( stage: 3, - base_damage: 120, + base_damage: 160, damage_increase: 0, - base_poise_damage: 12, + base_poise_damage: 10, poise_damage_increase: 0, - knockback: 5.0, - range: 3.5, + knockback: 3.0, + range: 2.5, angle: 30.0, base_buildup_duration: 0.2, base_swing_duration: 0.07, diff --git a/assets/common/abilities/custom/maneater/poisonball.ron b/assets/common/abilities/custom/maneater/poisonball.ron new file mode 100644 index 0000000000..f8a2b14c9b --- /dev/null +++ b/assets/common/abilities/custom/maneater/poisonball.ron @@ -0,0 +1,18 @@ +BasicRanged( + energy_cost: 0, + buildup_duration: 0.8, + recover_duration: 0.35, + projectile: NecroticSphere( + damage: 260.0, + radius: 5.0, + energy_regen: 0, + ), + projectile_body: Object(FireworkPurple), + /*projectile_light: Some(LightEmitter { + col: (1.0, 0.75, 0.11).into(), + ..Default::default() + }),*/ + projectile_speed: 70.0, + num_projectiles: 3, + projectile_spread: 0.2, +) diff --git a/assets/common/abilities/custom/basilisk/singlestrike.ron b/assets/common/abilities/custom/maneater/singlestrike.ron similarity index 67% rename from assets/common/abilities/custom/basilisk/singlestrike.ron rename to assets/common/abilities/custom/maneater/singlestrike.ron index 65846146d6..45ebb89522 100644 --- a/assets/common/abilities/custom/basilisk/singlestrike.ron +++ b/assets/common/abilities/custom/maneater/singlestrike.ron @@ -2,18 +2,18 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 130, + base_damage: 200, damage_increase: 0, base_poise_damage: 28, poise_damage_increase: 0, knockback: 3.0, - range: 3.0, + range: 3.5, angle: 60.0, - base_buildup_duration: 0.4, - base_swing_duration: 0.07, + base_buildup_duration: 0.5, + base_swing_duration: 0.075, hit_timing: 0.5, - base_recover_duration: 0.4, - forward_movement: 3.0, + base_recover_duration: 0.2, + forward_movement: 2.0, damage_kind: Crushing, ), ], @@ -24,5 +24,5 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, - ori_modifier: 0.6, + ori_modifier: 0.65, ) diff --git a/assets/common/abilities/custom/mindflayer/necroticsphere.ron b/assets/common/abilities/custom/mindflayer/necroticsphere.ron index e8ab890b78..08c2039552 100644 --- a/assets/common/abilities/custom/mindflayer/necroticsphere.ron +++ b/assets/common/abilities/custom/mindflayer/necroticsphere.ron @@ -3,7 +3,7 @@ BasicRanged( buildup_duration: 0.75, recover_duration: 0.4, projectile: NecroticSphere( - damage: 300.0, + damage: 450.0, radius: 5.0, ), projectile_body: Object(FireworkPurple), diff --git a/assets/common/abilities/custom/mindflayer/necroticvortex.ron b/assets/common/abilities/custom/mindflayer/necroticvortex.ron index 896b380b62..b4d750fea2 100644 --- a/assets/common/abilities/custom/mindflayer/necroticvortex.ron +++ b/assets/common/abilities/custom/mindflayer/necroticvortex.ron @@ -1,12 +1,12 @@ SpinMelee( - buildup_duration: 0.5, + buildup_duration: 0.8, swing_duration: 0.2, recover_duration: 0.6, base_damage: 80.0, base_poise_damage: 1.0, knockback: ( strength: 7.0, direction: Towards), range: 16.0, - damage_effect: Some(Lifesteal(1.0)), + damage_effect: Some(Lifesteal(2.0)), energy_cost: 0.0, is_infinite: true, movement_behavior: Stationary, diff --git a/assets/common/abilities/custom/quadlowbasic/singlestrike.ron b/assets/common/abilities/custom/quadlowbasic/singlestrike.ron index c0bd5a47fb..d0ce162fcd 100644 --- a/assets/common/abilities/custom/quadlowbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadlowbasic/singlestrike.ron @@ -2,14 +2,14 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 100, + base_damage: 200, damage_increase: 0, base_poise_damage: 28, poise_damage_increase: 0, knockback: 3.0, range: 2.0, angle: 60.0, - base_buildup_duration: 0.4, + base_buildup_duration: 0.6, base_swing_duration: 0.07, hit_timing: 0.5, base_recover_duration: 0.4, diff --git a/assets/common/abilities/custom/quadlowbasic/triplestrike.ron b/assets/common/abilities/custom/quadlowbasic/triplestrike.ron index f7b49ad5ac..db548b316b 100644 --- a/assets/common/abilities/custom/quadlowbasic/triplestrike.ron +++ b/assets/common/abilities/custom/quadlowbasic/triplestrike.ron @@ -2,11 +2,11 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 80, + base_damage: 140, damage_increase: 0, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.2, angle: 30.0, base_buildup_duration: 0.65, @@ -18,11 +18,11 @@ ComboMelee( ), ( stage: 2, - base_damage: 80, + base_damage: 140, damage_increase: 0, base_poise_damage: 18, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.2, angle: 30.0, base_buildup_duration: 0.4, @@ -34,11 +34,11 @@ ComboMelee( ), ( stage: 3, - base_damage: 80, + base_damage: 140, damage_increase: 0, base_poise_damage: 20, poise_damage_increase: 0, - knockback: 7.0, + knockback: 3.0, range: 2.2, angle: 30.0, base_buildup_duration: 0.4, diff --git a/assets/common/abilities/custom/quadlowbreathe/dash.ron b/assets/common/abilities/custom/quadlowbreathe/dash.ron index 645e4b8516..32ae96a698 100644 --- a/assets/common/abilities/custom/quadlowbreathe/dash.ron +++ b/assets/common/abilities/custom/quadlowbreathe/dash.ron @@ -1,7 +1,7 @@ DashMelee( energy_cost: 0, - base_damage: 50, - scaled_damage: 100, + base_damage: 80, + scaled_damage: 150, base_poise_damage: 25, scaled_poise_damage: 0, base_knockback: 4.0, diff --git a/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron b/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron index 6a24929f17..ddc09e0ef6 100644 --- a/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron +++ b/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron @@ -2,7 +2,7 @@ BasicBeam( buildup_duration: 0.4, recover_duration: 0.25, beam_duration: 0.5, - damage: 40, + damage: 70, tick_rate: 3.0, range: 15.0, max_angle: 22.5, diff --git a/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron b/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron index 6944d309f3..6c68f6f61a 100644 --- a/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron +++ b/assets/common/abilities/custom/quadlowbreathe/triplestrike.ron @@ -2,11 +2,11 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 75, + base_damage: 220, damage_increase: 0, - base_poise_damage: 0, + base_poise_damage: 20, poise_damage_increase: 0, - knockback: 10.0, + knockback: 3.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.65, @@ -18,11 +18,11 @@ ComboMelee( ), ( stage: 2, - base_damage: 75, + base_damage: 220, damage_increase: 0, - base_poise_damage: 0, + base_poise_damage: 20, poise_damage_increase: 0, - knockback: 10.0, + knockback: 3.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.4, @@ -34,11 +34,11 @@ ComboMelee( ), ( stage: 3, - base_damage: 75, + base_damage: 220, damage_increase: 0, - base_poise_damage: 0, + base_poise_damage: 20, poise_damage_increase: 0, - knockback: 10.0, + knockback: 3.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.4, diff --git a/assets/common/abilities/custom/quadlowquick/quadstrike.ron b/assets/common/abilities/custom/quadlowquick/quadstrike.ron index 3c7f848f43..fdf237aebf 100644 --- a/assets/common/abilities/custom/quadlowquick/quadstrike.ron +++ b/assets/common/abilities/custom/quadlowquick/quadstrike.ron @@ -6,7 +6,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 2.0, + knockback: 1.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.6, @@ -22,7 +22,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 2.0, + knockback: 1.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.15, @@ -38,7 +38,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 2.0, + knockback: 1.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.2, diff --git a/assets/common/abilities/custom/quadlowranged/firebomb.ron b/assets/common/abilities/custom/quadlowranged/firebomb.ron index beb3c27f1c..4c758438cf 100644 --- a/assets/common/abilities/custom/quadlowranged/firebomb.ron +++ b/assets/common/abilities/custom/quadlowranged/firebomb.ron @@ -2,12 +2,12 @@ BasicRanged( energy_cost: 0, buildup_duration: 0.8, recover_duration: 0.35, - projectile: Fireball( - damage: 80.0, + projectile: NecroticSphere( + damage: 130.0, radius: 5.0, energy_regen: 0, ), - projectile_body: Object(BoltFire), + projectile_body: Object(FireworkPurple), /*projectile_light: Some(LightEmitter { col: (1.0, 0.75, 0.11).into(), ..Default::default() diff --git a/assets/common/abilities/custom/quadlowtail/charged.ron b/assets/common/abilities/custom/quadlowtail/charged.ron index fb03117f1c..d2c563f69c 100644 --- a/assets/common/abilities/custom/quadlowtail/charged.ron +++ b/assets/common/abilities/custom/quadlowtail/charged.ron @@ -13,6 +13,6 @@ ChargedMelee( charge_duration: 0.8, swing_duration: 0.7, hit_timing: 0.9, - recover_duration: 1.2, + recover_duration: 0.7, damage_kind: Crushing, ) diff --git a/assets/common/abilities/custom/quadmedbasic/singlestrike.ron b/assets/common/abilities/custom/quadmedbasic/singlestrike.ron index 2dcb7c3eb5..d5659cd650 100644 --- a/assets/common/abilities/custom/quadmedbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadmedbasic/singlestrike.ron @@ -6,7 +6,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 28, poise_damage_increase: 0, - knockback: 5.0, + knockback: 3.0, range: 2.7, angle: 60.0, base_buildup_duration: 0.4, diff --git a/assets/common/abilities/custom/quadmedcharge/doublestrike.ron b/assets/common/abilities/custom/quadmedcharge/doublestrike.ron index 83ba1ae46b..d535ccb97a 100644 --- a/assets/common/abilities/custom/quadmedcharge/doublestrike.ron +++ b/assets/common/abilities/custom/quadmedcharge/doublestrike.ron @@ -6,7 +6,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 22, poise_damage_increase: 0, - knockback: 10.0, + knockback: 4.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.65, @@ -22,7 +22,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 0, poise_damage_increase: 22, - knockback: 10.0, + knockback: 4.0, range: 2.5, angle: 30.0, base_buildup_duration: 0.4, diff --git a/assets/common/abilities/custom/quadmedhoof/basic.ron b/assets/common/abilities/custom/quadmedhoof/basic.ron index a7608d6b22..b1d70e5f7f 100644 --- a/assets/common/abilities/custom/quadmedhoof/basic.ron +++ b/assets/common/abilities/custom/quadmedhoof/basic.ron @@ -1,12 +1,12 @@ BasicMelee( energy_cost: 0, - buildup_duration: 0.45, - swing_duration: 0.5, + buildup_duration: 0.65, + swing_duration: 0.3, recover_duration: 0.35, base_damage: 100, base_poise_damage: 28, knockback: ( strength: 25.0, direction: Away), - range: 1.2, + range: 0.8, max_angle: 50.0, damage_effect: None, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron b/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron index 0448be9345..d494ab6974 100644 --- a/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron +++ b/assets/common/abilities/custom/quadsmallbasic/singlestrike.ron @@ -6,7 +6,7 @@ ComboMelee( damage_increase: 0, base_poise_damage: 10, poise_damage_increase: 0, - knockback: 3.0, + knockback: 1.0, range: 1.5, angle: 50.0, base_buildup_duration: 0.3, diff --git a/assets/common/abilities/custom/theropodbasic/singlestrike.ron b/assets/common/abilities/custom/theropodbasic/singlestrike.ron index 08cf1209e4..9c607d0058 100644 --- a/assets/common/abilities/custom/theropodbasic/singlestrike.ron +++ b/assets/common/abilities/custom/theropodbasic/singlestrike.ron @@ -2,14 +2,14 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 130, + base_damage: 270, damage_increase: 0, base_poise_damage: 40, poise_damage_increase: 0, knockback: 4.0, range: 7.5, angle: 60.0, - base_buildup_duration: 0.5, + base_buildup_duration: 0.4, base_swing_duration: 0.15, hit_timing: 0.5, base_recover_duration: 0.4, diff --git a/assets/common/abilities/custom/theropodbasic/triplestrike.ron b/assets/common/abilities/custom/theropodbasic/triplestrike.ron index c8766e2bbd..7157a77620 100644 --- a/assets/common/abilities/custom/theropodbasic/triplestrike.ron +++ b/assets/common/abilities/custom/theropodbasic/triplestrike.ron @@ -2,14 +2,14 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 140, + base_damage: 300, damage_increase: 0, base_poise_damage: 35, poise_damage_increase: 0, - knockback: 5.0, + knockback: 3.0, range: 7.5, angle: 30.0, - base_buildup_duration: 0.9, + base_buildup_duration: 0.7, base_swing_duration: 0.15, hit_timing: 0.5, base_recover_duration: 0.3, @@ -18,14 +18,14 @@ ComboMelee( ), ( stage: 2, - base_damage: 160, + base_damage: 340, damage_increase: 0, base_poise_damage: 35, poise_damage_increase: 0, - knockback: 5.0, + knockback: 3.0, range: 5.5, angle: 30.0, - base_buildup_duration: 0.5, + base_buildup_duration: 0.4, base_swing_duration: 0.15, hit_timing: 0.5, base_recover_duration: 0.15, @@ -34,14 +34,14 @@ ComboMelee( ), ( stage: 3, - base_damage: 200, + base_damage: 400, damage_increase: 0, base_poise_damage: 35, poise_damage_increase: 0, - knockback: 5.0, + knockback: 25.0, range: 5.5, angle: 30.0, - base_buildup_duration: 0.35, + base_buildup_duration: 0.3, base_swing_duration: 0.125, hit_timing: 0.5, base_recover_duration: 0.9, diff --git a/assets/common/abilities/custom/wendigomagic/frostbomb.ron b/assets/common/abilities/custom/wendigomagic/frostbomb.ron index 5cf95514b2..d49f94fcc1 100644 --- a/assets/common/abilities/custom/wendigomagic/frostbomb.ron +++ b/assets/common/abilities/custom/wendigomagic/frostbomb.ron @@ -3,7 +3,7 @@ BasicRanged( buildup_duration: 0.5, recover_duration: 0.35, projectile: Frostball( - damage: 80.0, + damage: 120.0, radius: 5.0, ), projectile_body: Object(BoltFire), // TODO: Get ice projectile model diff --git a/assets/common/abilities/custom/wendigomagic/singlestrike.ron b/assets/common/abilities/custom/wendigomagic/singlestrike.ron index 39185a761d..efdf2b7bc9 100644 --- a/assets/common/abilities/custom/wendigomagic/singlestrike.ron +++ b/assets/common/abilities/custom/wendigomagic/singlestrike.ron @@ -2,12 +2,12 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 120, + base_damage: 180, damage_increase: 0, base_poise_damage: 40, poise_damage_increase: 0, knockback: 3.0, - range: 3.5, + range: 2.5, angle: 30.0, base_buildup_duration: 0.6, base_swing_duration: 0.2, diff --git a/assets/common/abilities/hammer/singlestrike.ron b/assets/common/abilities/hammer/singlestrike.ron index 85da88b26e..d4aef1f59c 100644 --- a/assets/common/abilities/hammer/singlestrike.ron +++ b/assets/common/abilities/hammer/singlestrike.ron @@ -5,7 +5,7 @@ ComboMelee( damage_increase: 10, base_poise_damage: 20, poise_damage_increase: 0, - knockback: 5.0, + knockback: 3.5, range: 4.5, angle: 50.0, base_buildup_duration: 0.2, diff --git a/assets/common/abilities/hammersimple/doublestrike.ron b/assets/common/abilities/hammersimple/doublestrike.ron index 13d7423c1e..fa333528e3 100644 --- a/assets/common/abilities/hammersimple/doublestrike.ron +++ b/assets/common/abilities/hammersimple/doublestrike.ron @@ -2,12 +2,12 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 90, + base_damage: 240, damage_increase: 10, - base_poise_damage: 30, + base_poise_damage: 40, poise_damage_increase: 0, knockback: 4.0, - range: 3.5, + range: 4.5, angle: 50.0, base_buildup_duration: 0.6, base_swing_duration: 0.08, @@ -18,14 +18,14 @@ ComboMelee( ), ( stage: 2, - base_damage: 130, + base_damage: 320, damage_increase: 15, - base_poise_damage: 30, + base_poise_damage: 40, poise_damage_increase: 0, knockback: 16.0, - range: 1.5, + range: 2.5, angle: 30.0, - base_buildup_duration: 0.5, + base_buildup_duration: 0.6, base_swing_duration: 0.25, hit_timing: 0.5, base_recover_duration: 1.2, @@ -40,5 +40,5 @@ ComboMelee( max_speed_increase: 0.0, scales_from_combo: 0, is_interruptible: false, - ori_modifier: 0.6, + ori_modifier: 0.65, ) diff --git a/assets/common/abilities/sword/triplestrike.ron b/assets/common/abilities/sword/triplestrike.ron index b078f9f180..93ae1ffd07 100644 --- a/assets/common/abilities/sword/triplestrike.ron +++ b/assets/common/abilities/sword/triplestrike.ron @@ -6,7 +6,7 @@ ComboMelee( damage_increase: 10, base_poise_damage: 10, poise_damage_increase: 0, - knockback: 1.0, + knockback: 0.0, range: 4.0, angle: 30.0, base_buildup_duration: 0.1, @@ -38,7 +38,7 @@ ComboMelee( damage_increase: 20, base_poise_damage: 15, poise_damage_increase: 0, - knockback: 4.0, + knockback: 2.0, range: 6.0, angle: 10.0, base_buildup_duration: 0.15, diff --git a/assets/common/cave_scatter/dark_floor.ron b/assets/common/cave_scatter/dark_floor.ron index 1b3f456bff..bd89d3a6dd 100644 --- a/assets/common/cave_scatter/dark_floor.ron +++ b/assets/common/cave_scatter/dark_floor.ron @@ -1,10 +1,11 @@ [ - (20, Velorite), - (30, VeloriteFrag), - (5, CaveMushroom), + (50, Velorite), + (60, VeloriteFrag), + (40, CaveMushroom), (16, SapphireSmall), (12, EmeraldSmall), (15, Cobalt), + (30, Bloodstone), (40, Coal), (10, RubySmall), ] diff --git a/assets/common/cave_scatter/deep_floor.ron b/assets/common/cave_scatter/deep_floor.ron index 66c2e3ce59..c18d335e7a 100644 --- a/assets/common/cave_scatter/deep_floor.ron +++ b/assets/common/cave_scatter/deep_floor.ron @@ -1,14 +1,14 @@ [ - (30, Velorite), + (40, Velorite), (40, VeloriteFrag), - (10, CaveMushroom), + (30, CaveMushroom), (30, Mushroom), - (10, AmethystSmall), - (10, TopazSmall), + (30, AmethystSmall), + (30, TopazSmall), (16, SapphireSmall), (60, CrystalLow), (12, EmeraldSmall), - (5, Cobalt), + (15, Cobalt), (40, Coal), (70, Iron), (10, RubySmall), diff --git a/assets/common/cave_scatter/shallow_floor.ron b/assets/common/cave_scatter/shallow_floor.ron index 290a476ac3..f80601194a 100644 --- a/assets/common/cave_scatter/shallow_floor.ron +++ b/assets/common/cave_scatter/shallow_floor.ron @@ -1,6 +1,6 @@ [ (110, Stones), - (150, ShortGrass), + (250, ShortGrass), (50, CaveMushroom), (50, Mushroom), (30, AmethystSmall), diff --git a/assets/common/items/npc_armor/biped_large/generic.ron b/assets/common/items/npc_armor/biped_large/generic.ron new file mode 100644 index 0000000000..edf92287cb --- /dev/null +++ b/assets/common/items/npc_armor/biped_large/generic.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Generic Biped Large", + description: "Worn by bipeds.", + kind: Armor(( + kind: Chest("GenericBipedLarge"), + stats: ( + protection: Normal(45.0), + poise_resilience: Normal(1.0), + energy_max: 0, + energy_reward: 0.0, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) \ No newline at end of file diff --git a/assets/common/items/npc_armor/biped_large/mindflayer.ron b/assets/common/items/npc_armor/biped_large/mindflayer.ron index cfddaadbfa..2d1696b29f 100644 --- a/assets/common/items/npc_armor/biped_large/mindflayer.ron +++ b/assets/common/items/npc_armor/biped_large/mindflayer.ron @@ -4,7 +4,7 @@ ItemDef( kind: Armor(( kind: Chest("Mindflayer"), stats: ( - protection: Normal(60.0), + protection: Normal(110.0), poise_resilience: Normal(1.0), energy_max: 0, energy_reward: 0.0, diff --git a/assets/common/items/npc_armor/quadruped_low/generic.ron b/assets/common/items/npc_armor/quadruped_low/generic.ron new file mode 100644 index 0000000000..a5e634b032 --- /dev/null +++ b/assets/common/items/npc_armor/quadruped_low/generic.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Quad Low Generic", + description: "Scaly.", + kind: Armor(( + kind: Chest("QuadrupedLowGeneric"), + stats: ( + protection: Normal(40.0), + poise_resilience: Normal(0.0), + energy_max: 0, + energy_reward: 0.0, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) diff --git a/assets/common/items/npc_armor/quadruped_low/shell.ron b/assets/common/items/npc_armor/quadruped_low/shell.ron new file mode 100644 index 0000000000..0fcf7e270e --- /dev/null +++ b/assets/common/items/npc_armor/quadruped_low/shell.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Quad Low Shell", + description: "Shell.", + kind: Armor(( + kind: Chest("QuadrupedLowShell"), + stats: ( + protection: Normal(750.0), + poise_resilience: Normal(0.0), + energy_max: 0, + energy_reward: 0.0, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) diff --git a/assets/common/items/npc_armor/theropod/rugged.ron b/assets/common/items/npc_armor/theropod/rugged.ron new file mode 100644 index 0000000000..01c260de60 --- /dev/null +++ b/assets/common/items/npc_armor/theropod/rugged.ron @@ -0,0 +1,17 @@ +ItemDef( + name: "Theropod Rugged", + description: "stronk.", + kind: Armor(( + kind: Chest("TheropodRugged"), + stats: ( + protection: Normal(80.0), + poise_resilience: Normal(0.0), + energy_max: 0, + energy_reward: 0.0, + crit_power: 0.0, + stealth: 0.0, + ), + )), + quality: Moderate, + tags: [], +) diff --git a/assets/common/items/npc_weapons/unique/quadlowranged.ron b/assets/common/items/npc_weapons/unique/asp.ron similarity index 80% rename from assets/common/items/npc_weapons/unique/quadlowranged.ron rename to assets/common/items/npc_weapons/unique/asp.ron index 2f4bcf2464..4312766e53 100644 --- a/assets/common/items/npc_weapons/unique/quadlowranged.ron +++ b/assets/common/items/npc_weapons/unique/asp.ron @@ -1,5 +1,5 @@ ItemDef( - name: "Quad Low Ranged", + name: "Asp", description: "testing123", kind: Tool(( kind: Natural, @@ -14,5 +14,5 @@ ItemDef( )), quality: Low, tags: [], - ability_spec: Some(Custom("Quad Low Ranged")), + ability_spec: Some(Custom("Asp")), ) \ No newline at end of file diff --git a/assets/common/items/npc_weapons/unique/maneater.ron b/assets/common/items/npc_weapons/unique/maneater.ron new file mode 100644 index 0000000000..93fc149b6c --- /dev/null +++ b/assets/common/items/npc_weapons/unique/maneater.ron @@ -0,0 +1,18 @@ +ItemDef( + name: "Maneater", + description: "testing123", + kind: Tool(( + kind: Natural, + hands: Two, + stats: Direct(( + equip_time_secs: 0.01, + power: 1.0, + poise_strength: 1.0, + speed: 1.0, + crit_chance: 0.0625, + )), + )), + quality: Low, + tags: [], + ability_spec: Some(Custom("Maneater")), +) \ No newline at end of file diff --git a/assets/common/loot_tables/creature/quad_small/generic.ron b/assets/common/loot_tables/creature/quad_small/generic.ron index 431e082771..d7aeb6531a 100644 --- a/assets/common/loot_tables/creature/quad_small/generic.ron +++ b/assets/common/loot_tables/creature/quad_small/generic.ron @@ -1,4 +1,4 @@ [ - (1.0, Item("common.items.crafting_ing.hide.animal_hide")), + (1.0, ItemQuantity("common.items.crafting_ing.hide.animal_hide", 1, 2)), (0.25, Item("common.items.food.meat.beast_small_raw")), ] \ No newline at end of file diff --git a/assets/common/loot_tables/trading.ron b/assets/common/loot_tables/trading.ron index c109d6db31..9db4df6cdb 100644 --- a/assets/common/loot_tables/trading.ron +++ b/assets/common/loot_tables/trading.ron @@ -8,12 +8,17 @@ (1.0, Item("common.items.crafting_ing.hide.animal_hide")), (0.5, Item("common.items.crafting_ing.hide.tough_hide")), (0.2, Item("common.items.crafting_ing.hide.scales")), - (0.08, Item("common.items.crafting_ing.animal_misc.fur")), - (0.08, Item("common.items.crafting_ing.animal_misc.grim_eyeball")), - (0.08, Item("common.items.crafting_ing.animal_misc.icy_fang")), + (0.8, Item("common.items.crafting_ing.animal_misc.fur")), + (0.15, Item("common.items.crafting_ing.animal_misc.grim_eyeball")), + (0.1, Item("common.items.crafting_ing.animal_misc.icy_fang")), (0.08, Item("common.items.crafting_ing.animal_misc.large_horn")), - (0.08, Item("common.items.crafting_ing.animal_misc.lively_vine")), + (0.15, Item("common.items.crafting_ing.animal_misc.lively_vine")), (0.08, Item("common.items.crafting_ing.animal_misc.phoenix_feather")), + (1.0, Item("common.items.food.meat.beast_small_raw")), + (0.6, Item("common.items.food.meat.beast_large_raw")), + (1.3, Item("common.items.food.meat.bird_raw")), + (1.2, Item("common.items.food.meat.fish_raw")), + (0.8, Item("common.items.food.meat.tough_raw")), (0.2, Item("common.items.mineral.ore.bloodstone")), (1.0, Item("common.items.mineral.ore.coal")), (0.4, Item("common.items.mineral.ore.cobalt")), diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron index 85f6d48076..9f8f48e7dd 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book.ron @@ -418,10 +418,9 @@ (Item("common.items.crafting_ing.animal_misc.raptor_feather"), 6), (Item("common.items.crafting_ing.twigs"), 5), (Item("common.items.crafting_ing.leather.thick_leather"), 4), - (Item("common.items.crafting_ing.cloth.linen"), 5), - (Item("common.items.mineral.gem.ruby"), 1), + (Item("common.items.crafting_ing.cloth.wool"), 5), + (Item("common.items.mineral.gem.sapphire"), 1), (Item("common.items.tool.craftsman_hammer"), 0), - (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(CraftingBench), ), @@ -431,11 +430,10 @@ (Item("common.items.crafting_ing.animal_misc.raptor_feather"), 6), (Item("common.items.crafting_ing.twigs"), 5), (Item("common.items.crafting_ing.leather.thick_leather"), 4), - (Item("common.items.crafting_ing.cloth.linen"), 5), + (Item("common.items.crafting_ing.cloth.silk"), 5), (Item("common.items.crafting_ing.animal_misc.icy_fang"), 1), (Item("common.items.mineral.gem.ruby"), 1), (Item("common.items.tool.craftsman_hammer"), 0), - (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(CraftingBench), ), @@ -443,12 +441,11 @@ output: ("common.items.glider.glider_woodraptor", 1), inputs: [ (Item("common.items.crafting_ing.animal_misc.raptor_feather"), 6), - (Item("common.items.crafting_ing.twigs"), 15), - (Item("common.items.crafting_ing.leather.leather_strips"), 5), - (Item("common.items.crafting_ing.cloth.linen"), 5), - (Item("common.items.mineral.gem.ruby"), 1), + (Item("common.items.crafting_ing.twigs"), 5), + (Item("common.items.crafting_ing.leather.thick_leather"), 4), + (Item("common.items.crafting_ing.cloth.lifecloth"), 5), + (Item("common.items.mineral.gem.emerald"), 1), (Item("common.items.tool.craftsman_hammer"), 0), - (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(CraftingBench), ), @@ -466,7 +463,6 @@ inputs: [ (Item("common.items.crafting_ing.leather.leather_strips"), 8), (Item("common.items.crafting_ing.twigs"), 6), - (Item("common.items.crafting_ing.stones"), 0), ], craft_sprite: Some(CraftingBench), ), @@ -476,7 +472,6 @@ (Item("common.items.crafting_ing.leather.leather_strips"), 4), (Item("common.items.crafting_ing.twigs"), 10), (Item("common.items.mineral.ore.veloritefrag"), 1), - (Item("common.items.crafting_ing.stones"), 0), ], craft_sprite: Some(Anvil), ), @@ -662,8 +657,8 @@ "carapace back": ( output: ("common.items.armor.hide.carapace.back", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 3), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 2), ], craft_sprite: Some(CraftingBench), @@ -671,8 +666,8 @@ "carapace belt": ( output: ("common.items.armor.hide.carapace.belt", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 2), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 2), ], craft_sprite: Some(CraftingBench), @@ -680,8 +675,8 @@ "carapace chest": ( output: ("common.items.armor.hide.carapace.chest", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 2), (Item("common.items.crafting_ing.hide.carapace"), 10), + (Item("common.items.mineral.ingot.steel"), 2), (Item("common.items.crafting_ing.leather.leather_strips"), 4), ], craft_sprite: Some(CraftingBench), @@ -689,8 +684,8 @@ "carapace feet": ( output: ("common.items.armor.hide.carapace.foot", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 3), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 2), ], craft_sprite: Some(CraftingBench), @@ -698,8 +693,8 @@ "carapace hands": ( output: ("common.items.armor.hide.carapace.hand", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 3), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 2), ], craft_sprite: Some(CraftingBench), @@ -707,8 +702,8 @@ "carapace pants": ( output: ("common.items.armor.hide.carapace.pants", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 8), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 4), ], craft_sprite: Some(CraftingBench), @@ -716,8 +711,8 @@ "carapace shoulder": ( output: ("common.items.armor.hide.carapace.shoulder", 1), inputs: [ - (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.hide.carapace"), 8), + (Item("common.items.mineral.ingot.steel"), 1), (Item("common.items.crafting_ing.leather.leather_strips"), 6), ], craft_sprite: Some(CraftingBench), @@ -726,9 +721,9 @@ "primal back": ( output: ("common.items.armor.hide.primal.back", 1), inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 3), (Item("common.items.crafting_ing.animal_misc.large_horn"), 1), (Item("common.items.crafting_ing.cloth.lifecloth"), 1), - (Item("common.items.crafting_ing.hide.plate"), 3), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), ], craft_sprite: Some(CraftingBench), @@ -736,8 +731,8 @@ "primal belt": ( output: ("common.items.armor.hide.primal.belt", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.hide.plate"), 2), + (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), ], craft_sprite: Some(CraftingBench), @@ -745,9 +740,9 @@ "primal chest": ( output: ("common.items.armor.hide.primal.chest", 1), inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 10), (Item("common.items.crafting_ing.animal_misc.large_horn"), 1), (Item("common.items.crafting_ing.cloth.lifecloth"), 2), - (Item("common.items.crafting_ing.hide.plate"), 10), (Item("common.items.crafting_ing.leather.rigid_leather"), 4), ], craft_sprite: Some(CraftingBench), @@ -755,8 +750,8 @@ "primal feet": ( output: ("common.items.armor.hide.primal.foot", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.hide.plate"), 3), + (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), ], craft_sprite: Some(CraftingBench), @@ -764,8 +759,8 @@ "primal hands": ( output: ("common.items.armor.hide.primal.hand", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.hide.plate"), 3), + (Item("common.items.crafting_ing.cloth.lifecloth"), 1), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), ], craft_sprite: Some(CraftingBench), @@ -773,9 +768,9 @@ "primal pants": ( output: ("common.items.armor.hide.primal.pants", 1), inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 8), (Item("common.items.crafting_ing.animal_misc.large_horn"), 1), (Item("common.items.crafting_ing.cloth.lifecloth"), 2), - (Item("common.items.crafting_ing.hide.plate"), 8), (Item("common.items.crafting_ing.leather.rigid_leather"), 4), ], craft_sprite: Some(CraftingBench), @@ -783,9 +778,9 @@ "primal shoulder": ( output: ("common.items.armor.hide.primal.shoulder", 1), inputs: [ + (Item("common.items.crafting_ing.hide.plate"), 8), (Item("common.items.crafting_ing.animal_misc.large_horn"), 2), (Item("common.items.crafting_ing.cloth.lifecloth"), 2), - (Item("common.items.crafting_ing.hide.plate"), 8), (Item("common.items.crafting_ing.leather.rigid_leather"), 6), ], craft_sprite: Some(CraftingBench), @@ -794,8 +789,8 @@ "dragonscale back": ( output: ("common.items.armor.hide.dragonscale.back", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.dragon_scale"), 3), + (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.leather.rigid_leather"), 1), (Item("common.items.crafting_ing.hide.scales"), 2), ], @@ -804,8 +799,8 @@ "dragonscale belt": ( output: ("common.items.armor.hide.dragonscale.belt", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.dragon_scale"), 2), + (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.scales"), 2), ], craft_sprite: Some(CraftingBench), @@ -813,8 +808,8 @@ "dragonscale chest": ( output: ("common.items.armor.hide.dragonscale.chest", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.hide.dragon_scale"), 10), + (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), (Item("common.items.crafting_ing.hide.scales"), 4), ], @@ -823,8 +818,8 @@ "dragonscale feet": ( output: ("common.items.armor.hide.dragonscale.foot", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.dragon_scale"), 3), + (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.scales"), 2), ], craft_sprite: Some(CraftingBench), @@ -832,8 +827,8 @@ "dragonscale hands": ( output: ("common.items.armor.hide.dragonscale.hand", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.dragon_scale"), 3), + (Item("common.items.mineral.ingot.bloodsteel"), 2), (Item("common.items.crafting_ing.hide.scales"), 2), ], craft_sprite: Some(CraftingBench), @@ -841,8 +836,8 @@ "dragonscale pants": ( output: ("common.items.armor.hide.dragonscale.pants", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.hide.dragon_scale"), 8), + (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.leather.rigid_leather"), 1), (Item("common.items.crafting_ing.hide.scales"), 4), ], @@ -851,8 +846,8 @@ "dragonscale shoulder": ( output: ("common.items.armor.hide.dragonscale.shoulder", 1), inputs: [ - (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.hide.dragon_scale"), 8), + (Item("common.items.mineral.ingot.bloodsteel"), 3), (Item("common.items.crafting_ing.leather.rigid_leather"), 2), (Item("common.items.crafting_ing.hide.scales"), 6), ], @@ -1109,6 +1104,7 @@ inputs: [ (Item("common.items.crafting_ing.cloth.moonweave"), 3), (Item("common.items.crafting_ing.hide.leather_troll"), 3), + (Item("common.items.mineral.ingot.silver"), 1), (Item("common.items.mineral.ingot.cobalt"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), ], @@ -1129,6 +1125,7 @@ inputs: [ (Item("common.items.crafting_ing.cloth.moonweave"), 10), (Item("common.items.crafting_ing.hide.leather_troll"), 5), + (Item("common.items.mineral.ingot.silver"), 1), (Item("common.items.mineral.ingot.cobalt"), 3), (Item("common.items.crafting_tools.sewing_set"), 0), ], @@ -1180,7 +1177,7 @@ inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 3), (Item("common.items.crafting_ing.animal_misc.phoenix_feather"), 1), - (Item("common.items.mineral.ore.silver"), 1), + (Item("common.items.mineral.ingot.gold"), 1), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1189,7 +1186,7 @@ output: ("common.items.armor.cloth.sunsilk.belt", 1), inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 2), - (Item("common.items.mineral.ore.silver"), 1), + (Item("common.items.mineral.ingot.gold"), 1), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1199,7 +1196,7 @@ inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 10), (Item("common.items.crafting_ing.animal_misc.phoenix_feather"), 2), - (Item("common.items.mineral.ore.silver"), 2), + (Item("common.items.mineral.ingot.gold"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1208,7 +1205,7 @@ output: ("common.items.armor.cloth.sunsilk.foot", 1), inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 3), - (Item("common.items.mineral.ore.silver"), 2), + (Item("common.items.mineral.ingot.gold"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1217,7 +1214,7 @@ output: ("common.items.armor.cloth.sunsilk.hand", 1), inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 3), - (Item("common.items.mineral.ore.silver"), 2), + (Item("common.items.mineral.ingot.gold"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1226,7 +1223,7 @@ output: ("common.items.armor.cloth.sunsilk.pants", 1), inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 8), - (Item("common.items.mineral.ore.silver"), 3), + (Item("common.items.mineral.ingot.gold"), 3), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1235,7 +1232,7 @@ output: ("common.items.armor.cloth.sunsilk.shoulder", 1), inputs: [ (Item("common.items.crafting_ing.cloth.sunsilk"), 8), - (Item("common.items.mineral.ore.silver"), 3), + (Item("common.items.mineral.ingot.gold"), 3), (Item("common.items.crafting_tools.sewing_set"), 0), ], craft_sprite: Some(Loom), @@ -1635,13 +1632,13 @@ "tiny leather pouch": ( output: ("common.items.armor.misc.bag.tiny_leather_pouch", 1), inputs: [ - (Item("common.items.crafting_ing.leather.leather_strips"), 6), + (Item("common.items.crafting_ing.leather.leather_strips"), 12), ], ), "knitted red pouch": ( output: ("common.items.armor.misc.bag.knitted_red_pouch", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.linen_red"), 3), + (Item("common.items.crafting_ing.cloth.wool"), 6), (Item("common.items.armor.misc.bag.tiny_red_pouch"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), ], @@ -1649,7 +1646,7 @@ "woven red bag": ( output: ("common.items.armor.misc.bag.woven_red_bag", 1), inputs: [ - (Item("common.items.crafting_ing.cloth.linen_red"), 6), + (Item("common.items.crafting_ing.cloth.silk"), 6), (Item("common.items.armor.misc.bag.knitted_red_pouch"), 1), (Item("common.items.crafting_tools.sewing_set"), 0), ], @@ -1659,7 +1656,7 @@ inputs: [ (Item("common.items.mineral.gem.diamond"), 2), (Item("common.items.crafting_ing.twigs"), 2), - (Item("common.items.crafting_ing.cloth.linen"), 3), + (Item("common.items.crafting_ing.cloth.silk"), 4), (Item("common.items.crafting_ing.leather.leather_strips"), 3), (Item("common.items.armor.misc.bag.tiny_leather_pouch"), 2), (Item("common.items.crafting_tools.sewing_set"), 0), @@ -1669,9 +1666,9 @@ "sturdy red backpack": ( output: ("common.items.armor.misc.bag.sturdy_red_backpack", 1), inputs: [ - (Item("common.items.mineral.gem.diamond"), 2), + (Item("common.items.mineral.gem.amethyst"), 2), (Item("common.items.crafting_ing.cloth.linen_red"), 3), - (Item("common.items.crafting_ing.leather.thick_leather"), 3), + (Item("common.items.crafting_ing.leather.thick_leather"), 6), (Item("common.items.armor.misc.bag.woven_red_bag"), 1), (Item("common.items.crafting_tools.sewing_set"), 0), ], @@ -1689,7 +1686,7 @@ output: ("common.items.armor.misc.bag.mindflayer_spellbag", 1), inputs: [ (Item("common.items.crafting_ing.mindflayer_bag_damaged"), 1), - (Item("common.items.crafting_ing.leather.thick_leather"), 8), + (Item("common.items.crafting_ing.leather.rigid_leather"), 8), (Item("common.items.mineral.gem.diamond"), 4), (Item("common.items.mineral.ore.veloritefrag"), 10), (Item("common.items.crafting_tools.sewing_set"), 0), diff --git a/assets/voxygen/i18n/en/hud/misc.ron b/assets/voxygen/i18n/en/hud/misc.ron index 04b74ef477..d447525a9f 100644 --- a/assets/voxygen/i18n/en/hud/misc.ron +++ b/assets/voxygen/i18n/en/hud/misc.ron @@ -32,9 +32,9 @@ You are welcome to take whatever you need on your journey! Look at the bottom right of the screen to find various things like your bag, the crafting menu and the map. -The crafting menu allows you to create armor, weapons, food and much more! +The crafting stations allow you to create armor, weapons, food and much more! -The wild animals all around town are a great source of Leather Scraps to create some protection against the dangers of the world. +The wild animals all around town are a great source of Animal Hide to create some protection against the dangers of the world. Whenever you feel ready, try to get even better equipment from the many challenges marked on your map! "#, diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 470763465a..72a2f4748f 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -517,6 +517,7 @@ impl Body { Body::Theropod(theropod) => match theropod.species { theropod::Species::Archaeos => 3500, theropod::Species::Odonto => 3000, + theropod::Species::Ntouka => 3000, _ => 1100, }, Body::QuadrupedLow(quadruped_low) => match quadruped_low.species { @@ -527,7 +528,7 @@ impl Body { quadruped_low::Species::Tortoise => 900, quadruped_low::Species::Rocksnapper => 1400, quadruped_low::Species::Pangolin => 400, - quadruped_low::Species::Maneater => 700, + quadruped_low::Species::Maneater => 1300, quadruped_low::Species::Sandshark => 900, quadruped_low::Species::Hakulaq => 500, quadruped_low::Species::Lavadrake => 1600, @@ -685,7 +686,11 @@ impl Body { pub fn base_poise(&self) -> u32 { match self { Body::Humanoid(_) => 100, - Body::BipedLarge(_) => 250, + Body::BipedLarge(biped_large) => match biped_large.species { + biped_large::Species::Mindflayer => 320, + biped_large::Species::Minotaur => 280, + _ => 250, + }, Body::Golem(_) => 300, _ => 100, } diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index c1ced2481c..5c8f48345f 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -197,9 +197,12 @@ fn default_main_tool(body: &Body) -> Item { )), }, Body::QuadrupedLow(quadruped_low) => match quadruped_low.species { - quadruped_low::Species::Maneater | quadruped_low::Species::Asp => Some( - Item::new_from_asset_expect("common.items.npc_weapons.unique.quadlowranged"), - ), + quadruped_low::Species::Maneater => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.maneater", + )), + quadruped_low::Species::Asp => Some(Item::new_from_asset_expect( + "common.items.npc_weapons.unique.asp", + )), quadruped_low::Species::Crocodile | quadruped_low::Species::Alligator | quadruped_low::Species::Salamander => Some(Item::new_from_asset_expect( @@ -414,12 +417,52 @@ impl LoadoutBuilder { }) => self.chest(Some(Item::new_from_asset_expect( "common.items.npc_armor.biped_large.harvester", ))), + Body::BipedLarge(biped_large::Body { + species: + biped_large::Species::Ogre + | biped_large::Species::Cyclops + | biped_large::Species::Blueoni + | biped_large::Species::Redoni + | biped_large::Species::Troll + | biped_large::Species::Wendigo, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.generic", + ))), Body::Golem(golem::Body { species: golem::Species::ClayGolem, .. }) => self.chest(Some(Item::new_from_asset_expect( "common.items.npc_armor.golem.claygolem", ))), + Body::QuadrupedLow(quadruped_low::Body { + species: + quadruped_low::Species::Basilisk + | quadruped_low::Species::Asp + | quadruped_low::Species::Lavadrake + | quadruped_low::Species::Maneater + | quadruped_low::Species::Rocksnapper + | quadruped_low::Species::Sandshark, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.quadruped_low.generic", + ))), + Body::QuadrupedLow(quadruped_low::Body { + species: quadruped_low::Species::Tortoise, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.quadruped_low.shell", + ))), + Body::Theropod(theropod::Body { + species: + theropod::Species::Archaeos + | theropod::Species::Yale + | theropod::Species::Ntouka + | theropod::Species::Odonto, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.theropod.rugged", + ))), _ => self, }; diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 3d933bd396..eefa5e1a15 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -92,7 +92,7 @@ impl Body { quadruped_low::Species::Alligator => 110.0, quadruped_low::Species::Salamander => 85.0, quadruped_low::Species::Monitor => 160.0, - quadruped_low::Species::Asp => 130.0, + quadruped_low::Species::Asp => 110.0, quadruped_low::Species::Tortoise => 60.0, quadruped_low::Species::Rocksnapper => 70.0, quadruped_low::Species::Pangolin => 120.0, @@ -100,7 +100,7 @@ impl Body { quadruped_low::Species::Sandshark => 160.0, quadruped_low::Species::Hakulaq => 140.0, quadruped_low::Species::Lavadrake => 100.0, - quadruped_low::Species::Basilisk => 120.0, + quadruped_low::Species::Basilisk => 90.0, quadruped_low::Species::Deadwood => 140.0, }, Body::Ship(_) => 0.0, diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index d6dab17616..a4b45e9149 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -1617,8 +1617,10 @@ impl<'a> AgentData<'a> { circle_time: 1, }, "Quad Med Basic" => Tactic::QuadMedBasic, - "Quad Low Ranged" => Tactic::QuadLowRanged, - "Quad Low Breathe" | "Quad Low Beam" => Tactic::QuadLowBeam, + "Asp" | "Maneater" => Tactic::QuadLowRanged, + "Quad Low Breathe" | "Quad Low Beam" | "Basilisk" => { + Tactic::QuadLowBeam + }, "Quad Low Tail" => Tactic::TailSlap, "Quad Low Quick" => Tactic::QuadLowQuick, "Quad Low Basic" => Tactic::QuadLowBasic, diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index bff44eb784..847650313c 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -328,7 +328,7 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { // Scatter things in caves if cave_depth > 40.0 && cave_depth < 80.0 { - if rng.gen::() < 0.2 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { + if rng.gen::() < 0.14 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { let kind = *Lottery::::load_expect("common.cave_scatter.shallow_floor") .read() @@ -349,7 +349,7 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { ); } } else if cave_depth < 200.0 && cave_depth > 80.0 { - if rng.gen::() < 0.12 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { + if rng.gen::() < 0.08 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { let kind = *Lottery::::load_expect("common.cave_scatter.deep_floor") .read() @@ -370,7 +370,7 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { ); } } else { - if rng.gen::() < 0.12 * (cave_x.max(0.5).powf(4.0)) + if rng.gen::() < 0.08 * (cave_x.max(0.5).powf(4.0)) && cave_depth > 40.0 && !vein_condition { @@ -445,7 +445,7 @@ pub fn apply_caves_supplement<'a>( .map_or(true, |b| b.is_fluid()) }) }) { - if RandomField::new(index.seed).chance(wpos2d.into(), 0.0018) + if RandomField::new(index.seed).chance(wpos2d.into(), 0.0014) && cave_base < surface_z as i32 - 40 { let is_hostile: bool; @@ -470,7 +470,7 @@ pub fn apply_caves_supplement<'a>( }; comp::quadruped_low::Body::random_with(dynamic_rng, &species) .into() - } else if cave_depth < 200.0 { + } else if cave_depth < 190.0 { is_hostile = true; let species = match dynamic_rng.gen_range(0..3) { 0 => comp::quadruped_low::Species::Rocksnapper, From a42e6e20b8929c5fde02821a83ef7f5188297d00 Mon Sep 17 00:00:00 2001 From: jshipsey Date: Wed, 16 Jun 2021 21:48:24 -0400 Subject: [PATCH 37/38] basilisk tweaks --- .../common/abilities/ability_set_manifest.ron | 8 +++++--- .../common/abilities/custom/basilisk/dash.ron | 20 +++++++++++++++++++ assets/common/cave_scatter/deep_floor.ron | 2 +- common/src/comp/inventory/loadout_builder.rs | 2 +- world/src/layer/mod.rs | 2 +- 5 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 assets/common/abilities/custom/basilisk/dash.ron diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index a9e33c3b0b..12eb751aad 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -149,9 +149,11 @@ abilities: [], ), Custom("Basilisk"): ( - primary: "common.abilities.custom.basilisk.triplestrike", - secondary: "common.abilities.custom.basilisk.petrify", - abilities: [], + primary: "common.abilities.custom.basilisk.petrify", + secondary: "common.abilities.custom.basilisk.triplestrike", + abilities: [ + (None, "common.abilities.custom.basilisk.dash"), + ], ), Custom("Asp"): ( primary: "common.abilities.custom.asp.singlestrike", diff --git a/assets/common/abilities/custom/basilisk/dash.ron b/assets/common/abilities/custom/basilisk/dash.ron new file mode 100644 index 0000000000..8221e8092d --- /dev/null +++ b/assets/common/abilities/custom/basilisk/dash.ron @@ -0,0 +1,20 @@ +DashMelee( + energy_cost: 0, + base_damage: 120, + scaled_damage: 180, + base_poise_damage: 25, + scaled_poise_damage: 0, + base_knockback: 4.0, + scaled_knockback: 17.0, + range: 2.5, + angle: 45.0, + energy_drain: 0, + forward_speed: 4.0, + buildup_duration: 0.8, + charge_duration: 1.0, + swing_duration: 0.1, + recover_duration: 1.0, + charge_through: true, + is_interruptible: false, + damage_kind: Crushing, +) diff --git a/assets/common/cave_scatter/deep_floor.ron b/assets/common/cave_scatter/deep_floor.ron index c18d335e7a..0594c8679a 100644 --- a/assets/common/cave_scatter/deep_floor.ron +++ b/assets/common/cave_scatter/deep_floor.ron @@ -6,7 +6,7 @@ (30, AmethystSmall), (30, TopazSmall), (16, SapphireSmall), - (60, CrystalLow), + (100, CrystalLow), (12, EmeraldSmall), (15, Cobalt), (40, Coal), diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 5c8f48345f..d4396b2749 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -423,7 +423,7 @@ impl LoadoutBuilder { | biped_large::Species::Cyclops | biped_large::Species::Blueoni | biped_large::Species::Redoni - | biped_large::Species::Troll + | biped_large::Species::Cavetroll | biped_large::Species::Wendigo, .. }) => self.chest(Some(Item::new_from_asset_expect( diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 847650313c..f448f71f79 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -349,7 +349,7 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { ); } } else if cave_depth < 200.0 && cave_depth > 80.0 { - if rng.gen::() < 0.08 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { + if rng.gen::() < 0.065 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { let kind = *Lottery::::load_expect("common.cave_scatter.deep_floor") .read() From 9a5b95bc1696c9141bf074dce1f97dd1fae77faa Mon Sep 17 00:00:00 2001 From: DaforLynx Date: Thu, 17 Jun 2021 05:49:09 +0000 Subject: [PATCH 38/38] Added npc hurt sfx --- assets/voxygen/audio/sfx.ron | 118 ++++++++++++++---- .../audio/sfx/utterance/adlet_angry1.ogg | 3 + .../audio/sfx/utterance/adlet_angry2.ogg | 3 + .../audio/sfx/utterance/adlet_hurt1.ogg | 3 + .../audio/sfx/utterance/adlet_hurt2.ogg | 3 + .../audio/sfx/utterance/alligator_angry1.ogg | 3 + .../audio/sfx/utterance/alligator_angry2.ogg | 3 + .../audio/sfx/utterance/antelope_angry1.ogg | 3 + .../audio/sfx/utterance/antelope_hurt1.ogg | 3 + .../{bird_angry.ogg => bird_angry1.ogg} | 0 .../utterance/{cow_calm.ogg => cow_calm1.ogg} | 0 ...e_greeting.ogg => humanmale_greeting1.ogg} | 0 .../audio/sfx/utterance/humanmale_hurt1.ogg | 3 + .../audio/sfx/utterance/mandragora_hurt1.ogg | 3 + .../audio/sfx/utterance/maneater_hurt1.ogg | 3 + .../audio/sfx/utterance/marlin_hurt1.ogg | 3 + .../audio/sfx/utterance/mindflayer_hurt1.ogg | 3 + .../{ogre_angry.ogg => ogre_angry1.ogg} | 0 .../utterance/{pig_calm.ogg => pig_calm1.ogg} | 0 .../{saurok_angry.ogg => saurok_angry1.ogg} | 0 .../{sheep_calm.ogg => sheep_calm1.ogg} | 0 .../{wendigo_angry.ogg => wendigo_angry1.ogg} | 0 common/src/comp/agent.rs | 1 + server/src/events/entity_manipulation.rs | 9 +- server/src/sys/agent.rs | 31 ++++- voxygen/src/audio/music.rs | 2 +- voxygen/src/audio/sfx/mod.rs | 47 ++++--- 27 files changed, 203 insertions(+), 44 deletions(-) create mode 100644 assets/voxygen/audio/sfx/utterance/adlet_angry1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/adlet_angry2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/adlet_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/adlet_hurt2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/alligator_angry1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/alligator_angry2.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/antelope_angry1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/antelope_hurt1.ogg rename assets/voxygen/audio/sfx/utterance/{bird_angry.ogg => bird_angry1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{cow_calm.ogg => cow_calm1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{humanmale_greeting.ogg => humanmale_greeting1.ogg} (100%) create mode 100644 assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/mandragora_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/maneater_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/marlin_hurt1.ogg create mode 100644 assets/voxygen/audio/sfx/utterance/mindflayer_hurt1.ogg rename assets/voxygen/audio/sfx/utterance/{ogre_angry.ogg => ogre_angry1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{pig_calm.ogg => pig_calm1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{saurok_angry.ogg => saurok_angry1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{sheep_calm.ogg => sheep_calm1.ogg} (100%) rename assets/voxygen/audio/sfx/utterance/{wendigo_angry.ogg => wendigo_angry1.ogg} (100%) diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index 81bca564d1..cb25f11d92 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -831,56 +831,126 @@ ], threshold: 0.2, ), - Utterance(Angry, Wendigo): ( - files: [ - "voxygen.audio.sfx.utterance.wendigo_angry", - ], - threshold: 0.2, - ), Utterance(Angry, BipedLarge): ( files: [ - "voxygen.audio.sfx.utterance.ogre_angry", + "voxygen.audio.sfx.utterance.ogre_angry1", "voxygen.audio.sfx.utterance.ogre_angry2", ], - threshold: 0.2, - ), - Utterance(Angry, Reptile): ( - files: [ - "voxygen.audio.sfx.utterance.saurok_angry", - ], - threshold: 0.2, + threshold: 1.0, ), Utterance(Angry, Bird): ( files: [ - "voxygen.audio.sfx.utterance.bird_angry", + "voxygen.audio.sfx.utterance.bird_angry1", ], - threshold: 0.2, + threshold: 1.0, ), Utterance(Calm, Pig): ( files: [ - "voxygen.audio.sfx.utterance.pig_calm", + "voxygen.audio.sfx.utterance.pig_calm1", ], - threshold: 0.2, + threshold: 1.0, + ), + Utterance(Angry, Adlet): ( + files: [ + "voxygen.audio.sfx.utterance.adlet_angry1", + "voxygen.audio.sfx.utterance.adlet_angry2", + ], + threshold: 1.0, + ), + Utterance(Angry, Alligator): ( + files: [ + "voxygen.audio.sfx.utterance.alligator_angry1", + "voxygen.audio.sfx.utterance.alligator_angry2", + ], + threshold: 1.0, + ), + Utterance(Angry, Antelope): ( + files: [ + "voxygen.audio.sfx.utterance.antelope_angry1", + ], + threshold: 1.0, + ), + Utterance(Angry, Reptile): ( + files: [ + "voxygen.audio.sfx.utterance.alligator_angry1", + "voxygen.audio.sfx.utterance.alligator_angry2", + ], + threshold: 1.0, + ), + Utterance(Angry, Saurok): ( + files: [ + "voxygen.audio.sfx.utterance.saurok_angry1", + ], + threshold: 1.0, + ), + Utterance(Angry, Wendigo): ( + files: [ + "voxygen.audio.sfx.utterance.wendigo_angry1", + ], + threshold: 1.0, ), Utterance(Calm, Cow): ( files: [ - "voxygen.audio.sfx.utterance.cow_calm", + "voxygen.audio.sfx.utterance.cow_calm1", "voxygen.audio.sfx.utterance.cow_calm2", "voxygen.audio.sfx.utterance.cow_calm3", ], - threshold: 0.2, + threshold: 1.0, ), Utterance(Calm, Sheep): ( files: [ - "voxygen.audio.sfx.utterance.sheep_calm", + "voxygen.audio.sfx.utterance.sheep_calm1", ], - threshold: 0.2, + threshold: 1.0, ), Utterance(Greeting, HumanMale): ( files: [ - "voxygen.audio.sfx.utterance.humanmale_greeting", + "voxygen.audio.sfx.utterance.humanmale_greeting1", ], - threshold: 0.2, + threshold: 1.0, + ), + Utterance(Hurt, Adlet): ( + files: [ + "voxygen.audio.sfx.utterance.adlet_hurt1", + "voxygen.audio.sfx.utterance.adlet_hurt2", + ], + threshold: 1.0, + ), + Utterance(Hurt, Antelope): ( + files: [ + "voxygen.audio.sfx.utterance.antelope", + ], + threshold: 1.0, + ), + Utterance(Hurt, HumanMale): ( + files: [ + "voxygen.audio.sfx.utterance.humanmale_hurt1", + ], + threshold: 1.0, + ), + Utterance(Hurt, Lion): ( + files: [ + "voxygen.audio.sfx.utterance.lion_hurt1", + ], + threshold: 1.0, + ), + Utterance(Hurt, Marlin): ( + files: [ + "voxygen.audio.sfx.utterance.marlin_hurt1", + ], + threshold: 1.0, + ), + Utterance(Hurt, Maneater): ( + files: [ + "voxygen.audio.sfx.utterance.maneater_hurt1", + ], + threshold: 1.0, + ), + Utterance(Hurt, Mindflayer): ( + files: [ + "voxygen.audio.sfx.utterance.mindflayer_hurt1", + ], + threshold: 1.0, ), } ) diff --git a/assets/voxygen/audio/sfx/utterance/adlet_angry1.ogg b/assets/voxygen/audio/sfx/utterance/adlet_angry1.ogg new file mode 100644 index 0000000000..2989a25e5e --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/adlet_angry1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79edc121cf180d76d07d879de2f7886938c562be46d6fe52e7e04cb31384d78a +size 14109 diff --git a/assets/voxygen/audio/sfx/utterance/adlet_angry2.ogg b/assets/voxygen/audio/sfx/utterance/adlet_angry2.ogg new file mode 100644 index 0000000000..4550863e5b --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/adlet_angry2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0a280ff8c3556bb4492358a7de1801e138b727c1f9da4878e7737e1bbeafad2 +size 17091 diff --git a/assets/voxygen/audio/sfx/utterance/adlet_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/adlet_hurt1.ogg new file mode 100644 index 0000000000..1d3eb6fa3d --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/adlet_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2fe322b89f4370904f56ee67e60b4e5019692fd1a7cb4a1743931d169aed0d1c +size 9159 diff --git a/assets/voxygen/audio/sfx/utterance/adlet_hurt2.ogg b/assets/voxygen/audio/sfx/utterance/adlet_hurt2.ogg new file mode 100644 index 0000000000..1c7fe8305a --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/adlet_hurt2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64173b57b5cfa18f86d83d3fb850cdee3a7a1ec58f0714a314381643f4544d28 +size 12955 diff --git a/assets/voxygen/audio/sfx/utterance/alligator_angry1.ogg b/assets/voxygen/audio/sfx/utterance/alligator_angry1.ogg new file mode 100644 index 0000000000..7980df43f9 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/alligator_angry1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be7e1a79c23e049c46f6ace589907bcb47a4373476dd1c1ff6831f23f90c2b28 +size 20861 diff --git a/assets/voxygen/audio/sfx/utterance/alligator_angry2.ogg b/assets/voxygen/audio/sfx/utterance/alligator_angry2.ogg new file mode 100644 index 0000000000..7f0b1ca572 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/alligator_angry2.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0e767a3979318558bc583ed97c729025924411b9ac4cf1b40f6fb76997d1215 +size 15381 diff --git a/assets/voxygen/audio/sfx/utterance/antelope_angry1.ogg b/assets/voxygen/audio/sfx/utterance/antelope_angry1.ogg new file mode 100644 index 0000000000..bcc9688a46 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/antelope_angry1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24def83183120c92f0e4722a83afdebf990758e5cc54f1d31c2688ed12da720c +size 16845 diff --git a/assets/voxygen/audio/sfx/utterance/antelope_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/antelope_hurt1.ogg new file mode 100644 index 0000000000..8816597048 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/antelope_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3afdd23faea117d7c6f51604a823f349230b37a05714ce14b6c4c47f11eba67f +size 15679 diff --git a/assets/voxygen/audio/sfx/utterance/bird_angry.ogg b/assets/voxygen/audio/sfx/utterance/bird_angry1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/bird_angry.ogg rename to assets/voxygen/audio/sfx/utterance/bird_angry1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/cow_calm.ogg b/assets/voxygen/audio/sfx/utterance/cow_calm1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/cow_calm.ogg rename to assets/voxygen/audio/sfx/utterance/cow_calm1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg b/assets/voxygen/audio/sfx/utterance/humanmale_greeting1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/humanmale_greeting.ogg rename to assets/voxygen/audio/sfx/utterance/humanmale_greeting1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg new file mode 100644 index 0000000000..f9db0309e9 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/humanmale_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bca43c8a18501f7345b2e5314af80c33bca3e464a16a8d9caef5b6e1adb57d5e +size 10669 diff --git a/assets/voxygen/audio/sfx/utterance/mandragora_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/mandragora_hurt1.ogg new file mode 100644 index 0000000000..737590936a --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/mandragora_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85a420c21ce7150d4841ad895f065a0bc0d2204b1799c6ec3acb09e2ed65c8ee +size 9531 diff --git a/assets/voxygen/audio/sfx/utterance/maneater_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/maneater_hurt1.ogg new file mode 100644 index 0000000000..cba5563006 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/maneater_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee1d5b9976d7db6c4978d865653658eeb2d98b14e7bbb0a5e5fd2d92fc6d0922 +size 22347 diff --git a/assets/voxygen/audio/sfx/utterance/marlin_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/marlin_hurt1.ogg new file mode 100644 index 0000000000..bd24a8d35e --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/marlin_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b539cb75f734be4cf5353fe99342899c6d4e612ab25de6347d372eca83986b5f +size 20057 diff --git a/assets/voxygen/audio/sfx/utterance/mindflayer_hurt1.ogg b/assets/voxygen/audio/sfx/utterance/mindflayer_hurt1.ogg new file mode 100644 index 0000000000..adc0c22a35 --- /dev/null +++ b/assets/voxygen/audio/sfx/utterance/mindflayer_hurt1.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78902402673d74be5e3abf8980a95963e08fd52b3ec5ad2de7440ee95297a383 +size 15376 diff --git a/assets/voxygen/audio/sfx/utterance/ogre_angry.ogg b/assets/voxygen/audio/sfx/utterance/ogre_angry1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/ogre_angry.ogg rename to assets/voxygen/audio/sfx/utterance/ogre_angry1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/pig_calm.ogg b/assets/voxygen/audio/sfx/utterance/pig_calm1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/pig_calm.ogg rename to assets/voxygen/audio/sfx/utterance/pig_calm1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/saurok_angry.ogg b/assets/voxygen/audio/sfx/utterance/saurok_angry1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/saurok_angry.ogg rename to assets/voxygen/audio/sfx/utterance/saurok_angry1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/sheep_calm.ogg b/assets/voxygen/audio/sfx/utterance/sheep_calm1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/sheep_calm.ogg rename to assets/voxygen/audio/sfx/utterance/sheep_calm1.ogg diff --git a/assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg b/assets/voxygen/audio/sfx/utterance/wendigo_angry1.ogg similarity index 100% rename from assets/voxygen/audio/sfx/utterance/wendigo_angry.ogg rename to assets/voxygen/audio/sfx/utterance/wendigo_angry1.ogg diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 7c64f62fca..6b8059cedc 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -261,6 +261,7 @@ pub enum AgentEvent { )>, ), ServerSound(Sound), + Hurt, } #[derive(Copy, Clone, Debug)] diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 0eb466a80c..1dfba60f4e 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1,7 +1,7 @@ use crate::{ client::Client, comp::{ - agent::{Sound, SoundKind}, + agent::{Agent, AgentEvent, Sound, SoundKind}, biped_large, bird_large, quadruped_low, quadruped_medium, quadruped_small, skills::SkillGroupKind, theropod, PhysicsState, @@ -60,6 +60,13 @@ pub fn handle_damage(server: &Server, entity: EcsEntity, change: HealthChange) { if let Some(mut health) = ecs.write_storage::().get_mut(entity) { health.change_by(change); } + // This if statement filters out anything under 5 damage, for DOT ticks + // TODO: Find a better way to separate direct damage from DOT here + if change.amount < -50 { + if let Some(agent) = ecs.write_storage::().get_mut(entity) { + agent.inbox.push_front(AgentEvent::Hurt); + } + } } pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3) { diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index a4b45e9149..fb4f013f69 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -633,11 +633,27 @@ impl<'a> AgentData<'a> { } // Interact if incoming messages if !agent.inbox.is_empty() { - if !matches!(agent.inbox.front(), Some(AgentEvent::ServerSound(_))) { + if matches!( + agent.inbox.front(), + Some(AgentEvent::ServerSound(_)) | Some(AgentEvent::Hurt) + ) { + let sound = agent.inbox.pop_front(); + match sound { + Some(AgentEvent::ServerSound(sound)) => { + agent.sounds_heard.push(sound); + agent.awareness += sound.vol; + }, + Some(AgentEvent::Hurt) => { + // Hurt utterances at random upon receiving damage + if thread_rng().gen::() < 0.4 { + controller.push_event(ControlEvent::Utterance(UtteranceKind::Hurt)); + } + }, + //Note: this should be unreachable + Some(_) | None => return, + } + } else { agent.action_state.timer = 0.1; - } else if let Some(AgentEvent::ServerSound(sound)) = agent.inbox.pop_front() { - agent.sounds_heard.push(sound); - agent.awareness += sound.vol; } } if agent.action_state.timer > 0.0 { @@ -676,6 +692,13 @@ impl<'a> AgentData<'a> { return; } + if let Some(AgentEvent::Hurt) = agent.inbox.pop_front() { + // Hurt utterances at random upon receiving damage + if thread_rng().gen::() < 0.4 { + controller.push_event(ControlEvent::Utterance(UtteranceKind::Hurt)); + } + } + if let Some(Target { target, selected_at, diff --git a/voxygen/src/audio/music.rs b/voxygen/src/audio/music.rs index 5298e006b8..502bb61ced 100644 --- a/voxygen/src/audio/music.rs +++ b/voxygen/src/audio/music.rs @@ -316,7 +316,7 @@ impl MusicMgr { // Adds a bit of randomness between plays let silence_between_tracks_seconds: f32 = if matches!(music_state, MusicState::Activity(MusicActivity::Explore)) { - rng.gen_range(60.0..120.0) + rng.gen_range(90.0..180.0) } else { 0.0 }; diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 1f5fe7ab89..65623f5bd2 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -91,12 +91,12 @@ use client::Client; use common::{ assets::{self, AssetExt, AssetHandle}, comp::{ - beam, biped_large, humanoid, + beam, biped_large, biped_small, humanoid, item::{ItemKind, ToolKind}, object, poise::PoiseState, - quadruped_medium, quadruped_small, Body, CharacterAbilityType, InventoryUpdateEvent, - UtteranceKind, + quadruped_low, quadruped_medium, quadruped_small, Body, CharacterAbilityType, + InventoryUpdateEvent, UtteranceKind, }, outcome::Outcome, terrain::{BlockKind, TerrainChunk}, @@ -199,7 +199,14 @@ pub enum VoiceKind { Pig, Cow, Canine, - BigCat, + Lion, + Mindflayer, + Marlin, + Maneater, + Adlet, + Antelope, + Alligator, + Saurok, } fn body_to_voice(body: &Body) -> Option { @@ -208,6 +215,11 @@ fn body_to_voice(body: &Body) -> Option { humanoid::BodyType::Female => VoiceKind::HumanFemale, humanoid::BodyType::Male => VoiceKind::HumanMale, }, + Body::QuadrupedLow(body) => match body.species { + quadruped_low::Species::Maneater => VoiceKind::Maneater, + quadruped_low::Species::Alligator => VoiceKind::Alligator, + _ => return None, + }, Body::QuadrupedSmall(body) => match body.species { quadruped_small::Species::Sheep => VoiceKind::Sheep, quadruped_small::Species::Pig | quadruped_small::Species::Boar => VoiceKind::Pig, @@ -218,7 +230,7 @@ fn body_to_voice(body: &Body) -> Option { | quadruped_medium::Species::Tiger | quadruped_medium::Species::Lion | quadruped_medium::Species::Frostfang - | quadruped_medium::Species::Snowleopard => VoiceKind::BigCat, + | quadruped_medium::Species::Snowleopard => VoiceKind::Lion, quadruped_medium::Species::Wolf | quadruped_medium::Species::Roshwalr | quadruped_medium::Species::Tarasque @@ -231,17 +243,24 @@ fn body_to_voice(body: &Body) -> Option { | quadruped_medium::Species::Yak | quadruped_medium::Species::Moose | quadruped_medium::Species::Dreadhorn => VoiceKind::Cow, + quadruped_medium::Species::Antelope => VoiceKind::Antelope, _ => return None, }, Body::BirdMedium(_) | Body::BirdLarge(_) => VoiceKind::Bird, + Body::BipedSmall(body) => match body.species { + biped_small::Species::Adlet => VoiceKind::Adlet, + _ => return None, + }, Body::BipedLarge(body) => match body.species { biped_large::Species::Wendigo => VoiceKind::Wendigo, biped_large::Species::Occultsaurok | biped_large::Species::Mightysaurok - | biped_large::Species::Slysaurok => VoiceKind::Reptile, + | biped_large::Species::Slysaurok => VoiceKind::Saurok, + biped_large::Species::Mindflayer => VoiceKind::Mindflayer, _ => VoiceKind::BipedLarge, }, Body::Theropod(_) | Body::Dragon(_) => VoiceKind::Reptile, + Body::FishSmall(_) | Body::FishMedium(_) => VoiceKind::Marlin, _ => return None, }) } @@ -480,15 +499,15 @@ impl SfxMgr { }, Outcome::Damage { pos, .. } => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Damage); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); }, Outcome::Block { pos, parry, .. } => { if *parry { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Parry); - audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); } else { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Block); - audio.emit_sfx(sfx_trigger_item, *pos, Some(2.0), false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); } }, Outcome::PoiseChange { pos, state, .. } => match state { @@ -496,22 +515,22 @@ impl SfxMgr { PoiseState::Interrupted => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::Interrupted)); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); }, PoiseState::Stunned => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::Stunned)); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); }, PoiseState::Dazed => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::Dazed)); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); }, PoiseState::KnockedDown => { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::PoiseChange(PoiseState::KnockedDown)); - audio.emit_sfx(sfx_trigger_item, *pos, None, false); + audio.emit_sfx(sfx_trigger_item, *pos, Some(1.5), false); }, }, Outcome::Utterance { pos, kind, body } => { @@ -519,7 +538,7 @@ impl SfxMgr { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Utterance(*kind, voice)); if let Some(sfx_trigger_item) = sfx_trigger_item { - audio.emit_sfx(Some(sfx_trigger_item), *pos, Some(2.5), false); + audio.emit_sfx(Some(sfx_trigger_item), *pos, Some(1.5), false); } else { debug!( "No utterance sound effect exists for ({:?}, {:?})",