diff --git a/.gitignore b/.gitignore index ad6f7022f6..5621834e78 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,9 @@ todo.txt userdata heaptrack.* +# allow asset hud settings +!assets/voxygen/i18n/*/hud/settings.ron + # Export data *.csv diff --git a/.gitlab/CI/build.gitlab-ci.yml b/.gitlab/CI/build.gitlab-ci.yml index 9e68e7ee91..005dd3ef95 100644 --- a/.gitlab/CI/build.gitlab-ci.yml +++ b/.gitlab/CI/build.gitlab-ci.yml @@ -11,6 +11,25 @@ unittests: retry: max: 2 +translation: + extends: .release + stage: build + image: registry.gitlab.com/veloren/veloren-docker-ci/cache/quality:${CACHE_IMAGE_TAG} + script: + - ln -s /dockercache/target target + - cat ./.gitlab/scripts/translation.sh + - source ./.gitlab/scripts/translation.sh + - TAGUUID="Z$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)" || echo "ignore this returncode, dont ask me why, it works" + - echo $TAGUUID # Use TAGUUID to mitigate https://xkcd.com/327/ in the branch name + - echo 'SET veloren.timestamp = "'"$(git show --no-patch --no-notes --pretty='%cd' HEAD)"'";' > upload.sql + - echo "SET veloren.branch = \$${TAGUUID}\$${CI_COMMIT_REF_NAME}\$${TAGUUID}\$;" >> upload.sql + - echo "SET veloren.sha = \$${TAGUUID}\$${CI_COMMIT_SHA}\$${TAGUUID}\$;" >> upload.sql + - echo '\copy translations ("country_code", "file_name", "translation_key", "status", "git_commit") from '"'translation_analysis.csv' csv header" >> upload.sql + - cat upload.sql + - PGPASSWORD="${CIDBPASSWORD}" PGSSLROOTCERT="./.gitlab/ci-db.crt" psql "sslmode=verify-ca host=grafana.veloren.net port=15432 dbname=translations" -U hgseehzjtsrghtjdcqw -f upload.sql; + retry: + max: 2 + benchmarks: extends: .release stage: build @@ -23,7 +42,7 @@ benchmarks: - cat ./.gitlab/scripts/benchmark.sh - source ./.gitlab/scripts/benchmark.sh - TAGUUID="Z$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)" || echo "ignore this returncode, dont ask me why, it works" - - echo $TAGUUID + - echo $TAGUUID # Use TAGUUID to mitigate https://xkcd.com/327/ in the branch name - echo 'SET veloren.timestamp = "'"$(git show --no-patch --no-notes --pretty='%cd' HEAD)"'";' > upload.sql - echo "SET veloren.branch = \$${TAGUUID}\$${CI_COMMIT_REF_NAME}\$${TAGUUID}\$;" >> upload.sql - echo "SET veloren.sha = \$${TAGUUID}\$${CI_COMMIT_SHA}\$${TAGUUID}\$;" >> upload.sql diff --git a/.gitlab/CI/release.yml b/.gitlab/CI/release.yml index 87484d7816..2a67b406ad 100644 --- a/.gitlab/CI/release.yml +++ b/.gitlab/CI/release.yml @@ -14,6 +14,8 @@ - if: $CI_PIPELINE_SOURCE != "merge_request_event" && $CI_PIPELINE_SOURCE != "schedule" && ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+/ || $CI_COMMIT_REF_NAME =~ /^r[0-9]+\.[0-9]+/) when: on_success - when: never + retry: + max: 1 # Template to only run if pushes to master or a tag happened for scheduled builds .release-nightly-tmp-fix-airshipper: @@ -22,3 +24,5 @@ - if: $CI_PIPELINE_SOURCE == "schedule" && ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+/ || $CI_COMMIT_REF_NAME =~ /^r[0-9]+\.[0-9]+/) when: on_success - when: never + retry: + max: 1 diff --git a/.gitlab/scripts/translation.sh b/.gitlab/scripts/translation.sh new file mode 100755 index 0000000000..d05fbf0b2a --- /dev/null +++ b/.gitlab/scripts/translation.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export VELOREN_ASSETS="$(pwd)/assets" +rm -r target/debug/incremental/veloren_* || echo "all good" # TMP FIX FOR 2021-03-22-nightly +time cargo test --package veloren-voxygen-i18n --lib test_all_localizations -- --nocapture --ignored \ No newline at end of file diff --git a/.gitlab/scripts/unittest.sh b/.gitlab/scripts/unittest.sh index a4193d3914..5e693fbed2 100755 --- a/.gitlab/scripts/unittest.sh +++ b/.gitlab/scripts/unittest.sh @@ -1,7 +1,6 @@ #!/bin/bash export VELOREN_ASSETS="$(pwd)/assets" rm -r target/debug/incremental/veloren_* || echo "all good" # TMP FIX FOR 2021-03-22-nightly -time cargo test --package veloren-i18n --lib test_all_localizations -- --nocapture --ignored && time cargo test --package veloren-common-assets asset_tweak::tests --features asset_tweak --lib && ( rm -r target/debug/incremental* || echo "all good" ) && # TMP FIX FOR 2021-03-22-nightly time cargo test \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4845b4b3ab..b3379ab9f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Quotes and escape codes can be used in command arguments - Toggle chat with a shortcut (default is F5) - Pets are now saved on logout 🐕 🦎 🐼 +- Dualwielded, one-handed swords as starting weapons (Will be replaced by daggers in the future!) +- Healing sceptre crafting recipe +- NPCs can now warn players before engaging in combat +- Custom error message when a supported graphics backend can not be found +- Add server setting with PvE/PvP switch +- Can now tilt glider while only wielding it +- Experimental terrain persistence (see server documentation) +- Add GPU filtering using WGPU_ADAPTER environment variable ### Changed @@ -40,11 +48,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Mushroom Curry gives long-lasting Regeneration buff - Trades now consider if items can stack in full inventories. - The types of animals that can be tamed as pets are now limited to certain species, pending further balancing of pets +- Made server-cli admin add/remove command use positional arguments again +- Usage of "stamina" replaced with "energy" +- Glider dimensions now depend on character height +- Glider dimensions somewhat increased overall +- Dungeon difficulty level starts at 1 instead of 0 ### Removed - Enemies no longer spawn in dungeon boss room - Melee critical hit no longer applies after reduction by armour +- Enemies no more spawn in dungeon boss room +- Melee critical hit no more applies after reduction by armour +- Removed Healing Sceptre as a starting weapon as it is considered an advanced weapon ### Fixed @@ -55,6 +71,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Campfires now despawn when underwater - Players no longer spawn underground if their waypoint is underground - Map will now zoom around the cursor's position and drag correctly +- No more jittering while running down slopes with the glider out +- Axe normal attack rewards energy without skill points +- Gliders no longer suffer from unreasonable amounts of induced drag +- Camera is now clipping a lot less ## [0.10.0] - 2021-06-12 diff --git a/Cargo.lock b/Cargo.lock index 854545ffb5..8631344d8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2718,7 +2718,7 @@ dependencies = [ [[package]] name = "keyboard-keynames" version = "0.1.0" -source = "git+https://gitlab.com/Frinksy/keyboard-keynames.git?rev=9ae8f89014d0b0c5b61d0e821c5aeb6140c5c0dc#9ae8f89014d0b0c5b61d0e821c5aeb6140c5c0dc" +source = "git+https://gitlab.com/Frinksy/keyboard-keynames.git?rev=721c8b301f8003332d0cc767ab2649fdd330e842#721c8b301f8003332d0cc767ab2649fdd330e842" dependencies = [ "libc", "memmap", @@ -2726,7 +2726,7 @@ dependencies = [ "winapi 0.3.9", "winit", "xcb", - "xkbcommon-sys 0.7.5 (git+https://github.com/Frinksy/rust-xkbcommon-sys.git?rev=d5d69e05a81f7ee8d2f65a824ae692610ed7cb14)", + "xkbcommon-sys 0.7.6", ] [[package]] @@ -3063,7 +3063,7 @@ dependencies = [ "winapi 0.3.9", "x11-dl", "xkb", - "xkbcommon-sys 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", + "xkbcommon-sys 0.7.5", ] [[package]] @@ -4831,28 +4831,6 @@ dependencies = [ "syn 1.0.73", ] -[[package]] -name = "serial_test" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" -dependencies = [ - "lazy_static", - "parking_lot 0.11.1", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" -dependencies = [ - "proc-macro2 1.0.27", - "quote 1.0.9", - "syn 1.0.73", -] - [[package]] name = "sha1" version = "0.6.0" @@ -5920,7 +5898,6 @@ dependencies = [ "lazy_static", "ron", "serde", - "serial_test", "tracing", "walkdir 2.3.2", ] @@ -6017,20 +5994,6 @@ dependencies = [ "veloren-common-net", ] -[[package]] -name = "veloren-i18n" -version = "0.10.0" -dependencies = [ - "clap", - "deunicode", - "git2", - "hashbrown 0.11.2", - "ron", - "serde", - "tracing", - "veloren-common-assets", -] - [[package]] name = "veloren-network" version = "0.3.0" @@ -6112,6 +6075,7 @@ version = "0.10.0" dependencies = [ "atomicwrites", "authc", + "bincode", "chrono", "crossbeam-channel", "futures-util", @@ -6198,7 +6162,6 @@ dependencies = [ "egui_winit_platform", "enum-iterator", "euc", - "futures-executor", "gilrs", "glyph_brush", "guillotiere", @@ -6238,10 +6201,10 @@ dependencies = [ "veloren-common-net", "veloren-common-state", "veloren-common-systems", - "veloren-i18n", "veloren-server", "veloren-voxygen-anim", "veloren-voxygen-egui", + "veloren-voxygen-i18n", "veloren-world", "wgpu", "wgpu-profiler", @@ -6300,6 +6263,20 @@ dependencies = [ "veloren-voxygen-egui", ] +[[package]] +name = "veloren-voxygen-i18n" +version = "0.10.0" +dependencies = [ + "clap", + "deunicode", + "git2", + "hashbrown 0.11.2", + "ron", + "serde", + "tracing", + "veloren-common-assets", +] + [[package]] name = "veloren-world" version = "0.10.0" @@ -7087,7 +7064,7 @@ checksum = "aec02bc5de902aa579f3d2f2c522edaf40fa42963cbaffe645b058ddcc68fdb2" dependencies = [ "bitflags", "libc", - "xkbcommon-sys 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", + "xkbcommon-sys 0.7.5", ] [[package]] @@ -7103,8 +7080,8 @@ dependencies = [ [[package]] name = "xkbcommon-sys" -version = "0.7.5" -source = "git+https://github.com/Frinksy/rust-xkbcommon-sys.git?rev=d5d69e05a81f7ee8d2f65a824ae692610ed7cb14#d5d69e05a81f7ee8d2f65a824ae692610ed7cb14" +version = "0.7.6" +source = "git+https://github.com/Frinksy/rust-xkbcommon-sys.git?rev=8f615dd6cd90a4ab77c45627830dde49b592b9b5#8f615dd6cd90a4ab77c45627830dde49b592b9b5" dependencies = [ "libc", "pkg-config", diff --git a/assets/common/abilities/axe/doublestrike.ron b/assets/common/abilities/axe/doublestrike.ron index 932a87780e..042e9f0b72 100644 --- a/assets/common/abilities/axe/doublestrike.ron +++ b/assets/common/abilities/axe/doublestrike.ron @@ -2,7 +2,7 @@ ComboMelee( stage_data: [ ( stage: 1, - base_damage: 90, + base_damage: 110, base_poise_damage: 12, damage_increase: 10, poise_damage_increase: 0, diff --git a/assets/common/abilities/axesimple/dash.ron b/assets/common/abilities/axesimple/dash.ron index b9e0e8f05a..23074786f2 100644 --- a/assets/common/abilities/axesimple/dash.ron +++ b/assets/common/abilities/axesimple/dash.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 3.0, swing_duration: 0.35, recover_duration: 1.2, + ori_modifier: 0.3, charge_through: false, is_interruptible: true, damage_kind: Slashing, diff --git a/assets/common/abilities/custom/basilisk/dash.ron b/assets/common/abilities/custom/basilisk/dash.ron index b95514175c..f4e8c56c40 100644 --- a/assets/common/abilities/custom/basilisk/dash.ron +++ b/assets/common/abilities/custom/basilisk/dash.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 1.0, swing_duration: 0.1, recover_duration: 1.0, + ori_modifier: 0.3, charge_through: false, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/basilisk/petrify.ron b/assets/common/abilities/custom/basilisk/petrify.ron index 20b6022fb7..3ef55daf04 100644 --- a/assets/common/abilities/custom/basilisk/petrify.ron +++ b/assets/common/abilities/custom/basilisk/petrify.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 0, - orientation_behavior: Normal, ori_rate: 0.3, specifier: Cultist, ) diff --git a/assets/common/abilities/custom/birdlargebasic/dash.ron b/assets/common/abilities/custom/birdlargebasic/dash.ron index 0760657973..7e600ce698 100644 --- a/assets/common/abilities/custom/birdlargebasic/dash.ron +++ b/assets/common/abilities/custom/birdlargebasic/dash.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 3.0, swing_duration: 0.1, recover_duration: 0.7, + ori_modifier: 0.3, charge_through: false, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron b/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron index dfbd2227dc..cf3e4c63b6 100644 --- a/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron +++ b/assets/common/abilities/custom/birdlargebreathe/flamethrower.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 0, - orientation_behavior: Normal, ori_rate: 0.3, specifier: Flamethrower, ) diff --git a/assets/common/abilities/custom/birdlargefire/flamethrower.ron b/assets/common/abilities/custom/birdlargefire/flamethrower.ron index dfbd2227dc..cf3e4c63b6 100644 --- a/assets/common/abilities/custom/birdlargefire/flamethrower.ron +++ b/assets/common/abilities/custom/birdlargefire/flamethrower.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 0, - orientation_behavior: Normal, ori_rate: 0.3, specifier: Flamethrower, ) diff --git a/assets/common/abilities/custom/claygolem/laser.ron b/assets/common/abilities/custom/claygolem/laser.ron index c93f0f7ce8..95782454f0 100644 --- a/assets/common/abilities/custom/claygolem/laser.ron +++ b/assets/common/abilities/custom/claygolem/laser.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 50, energy_drain: 0, - orientation_behavior: FromOri, ori_rate: 0.07, specifier: ClayGolem, ) diff --git a/assets/common/abilities/custom/claygolem/rocket.ron b/assets/common/abilities/custom/claygolem/rocket.ron index c58011bfb1..762b84408c 100644 --- a/assets/common/abilities/custom/claygolem/rocket.ron +++ b/assets/common/abilities/custom/claygolem/rocket.ron @@ -4,7 +4,7 @@ BasicRanged( recover_duration: 0.5, projectile: ClayRocket( damage: 500.0, - knockback: 25.0, + knockback: 18.0, radius: 5.0, ), projectile_body: Object(ClayRocket), diff --git a/assets/common/abilities/custom/claygolem/shockwave.ron b/assets/common/abilities/custom/claygolem/shockwave.ron index ac4b9a8ae1..b23ed1ba62 100644 --- a/assets/common/abilities/custom/claygolem/shockwave.ron +++ b/assets/common/abilities/custom/claygolem/shockwave.ron @@ -5,7 +5,7 @@ Shockwave( recover_duration: 1.2, damage: 500, poise_damage: 50, - knockback: (strength: 40.0, direction: TowardsUp), + knockback: (strength: 30.0, direction: TowardsUp), shockwave_angle: 180.0, shockwave_vertical_angle: 90.0, shockwave_speed: 15.0, diff --git a/assets/common/abilities/custom/harvester/firebreath.ron b/assets/common/abilities/custom/harvester/firebreath.ron index 94f0a66de4..ac52ce7561 100644 --- a/assets/common/abilities/custom/harvester/firebreath.ron +++ b/assets/common/abilities/custom/harvester/firebreath.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 0, - orientation_behavior: Normal, ori_rate: 0.2, specifier: Flamethrower, ) diff --git a/assets/common/abilities/custom/mindflayer/cursedflames.ron b/assets/common/abilities/custom/mindflayer/cursedflames.ron index b74005d138..8ce5a5cfb5 100644 --- a/assets/common/abilities/custom/mindflayer/cursedflames.ron +++ b/assets/common/abilities/custom/mindflayer/cursedflames.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 0, - orientation_behavior: FromOri, ori_rate: 0.2, specifier: Cultist, ) diff --git a/assets/common/abilities/custom/mindflayer/necroticvortex.ron b/assets/common/abilities/custom/mindflayer/necroticvortex.ron index b4d750fea2..e6f89f6750 100644 --- a/assets/common/abilities/custom/mindflayer/necroticvortex.ron +++ b/assets/common/abilities/custom/mindflayer/necroticvortex.ron @@ -1,8 +1,8 @@ SpinMelee( buildup_duration: 0.8, - swing_duration: 0.2, + swing_duration: 0.5, recover_duration: 0.6, - base_damage: 80.0, + base_damage: 200.0, base_poise_damage: 1.0, knockback: ( strength: 7.0, direction: Towards), range: 16.0, diff --git a/assets/common/abilities/custom/minotaur/charge.ron b/assets/common/abilities/custom/minotaur/charge.ron index e313f18a92..b8324ee500 100644 --- a/assets/common/abilities/custom/minotaur/charge.ron +++ b/assets/common/abilities/custom/minotaur/charge.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 4.0, swing_duration: 0.1, recover_duration: 0.5, + ori_modifier: 0.3, charge_through: false, is_interruptible: false, damage_kind: Piercing, diff --git a/assets/common/abilities/custom/quadlowbeam/lifestealbeam.ron b/assets/common/abilities/custom/quadlowbeam/lifestealbeam.ron index 97262f80f0..bf9f67567c 100644 --- a/assets/common/abilities/custom/quadlowbeam/lifestealbeam.ron +++ b/assets/common/abilities/custom/quadlowbeam/lifestealbeam.ron @@ -9,7 +9,6 @@ BasicBeam( damage_effect: Some(Lifesteal(0.15)), energy_regen: 25, energy_drain: 0, - orientation_behavior: Normal, ori_rate: 0.3, specifier: LifestealBeam, ) diff --git a/assets/common/abilities/custom/quadlowbreathe/dash.ron b/assets/common/abilities/custom/quadlowbreathe/dash.ron index d84f614549..887298f7a5 100644 --- a/assets/common/abilities/custom/quadlowbreathe/dash.ron +++ b/assets/common/abilities/custom/quadlowbreathe/dash.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 1.0, swing_duration: 0.1, recover_duration: 1.0, + ori_modifier: 0.3, charge_through: false, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron b/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron index 5f4dab269a..5d6fd2895e 100644 --- a/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron +++ b/assets/common/abilities/custom/quadlowbreathe/flamethrower.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 0, - orientation_behavior: Normal, ori_rate: 0.3, specifier: Flamethrower, ) diff --git a/assets/common/abilities/custom/quadlowquick/dash.ron b/assets/common/abilities/custom/quadlowquick/dash.ron index 13fb06b122..42838c2b98 100644 --- a/assets/common/abilities/custom/quadlowquick/dash.ron +++ b/assets/common/abilities/custom/quadlowquick/dash.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 0.8, swing_duration: 0.1, recover_duration: 1.0, + ori_modifier: 0.3, charge_through: false, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedcharge/dash.ron b/assets/common/abilities/custom/quadmedcharge/dash.ron index 95f94277c1..75a399c0cd 100644 --- a/assets/common/abilities/custom/quadmedcharge/dash.ron +++ b/assets/common/abilities/custom/quadmedcharge/dash.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 1.2, swing_duration: 0.1, recover_duration: 1.1, + ori_modifier: 0.3, charge_through: false, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/quadmedquick/dash.ron b/assets/common/abilities/custom/quadmedquick/dash.ron index ded884beb1..1bb33c9825 100644 --- a/assets/common/abilities/custom/quadmedquick/dash.ron +++ b/assets/common/abilities/custom/quadmedquick/dash.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 1.0, swing_duration: 0.1, recover_duration: 1.0, + ori_modifier: 0.3, charge_through: false, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/theropodbasic/dash.ron b/assets/common/abilities/custom/theropodbasic/dash.ron index 0835777247..8552a30b75 100644 --- a/assets/common/abilities/custom/theropodbasic/dash.ron +++ b/assets/common/abilities/custom/theropodbasic/dash.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 1.2, swing_duration: 0.1, recover_duration: 1.1, + ori_modifier: 0.3, charge_through: false, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/tidalwarrior/bubbles.ron b/assets/common/abilities/custom/tidalwarrior/bubbles.ron index a34d1cb777..3d35c86f50 100644 --- a/assets/common/abilities/custom/tidalwarrior/bubbles.ron +++ b/assets/common/abilities/custom/tidalwarrior/bubbles.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 0, - orientation_behavior: Normal, ori_rate: 0.3, specifier: Bubbles, ) diff --git a/assets/common/abilities/custom/tidalwarrior/pincer.ron b/assets/common/abilities/custom/tidalwarrior/pincer.ron index 64656e7f8a..3678f7f840 100644 --- a/assets/common/abilities/custom/tidalwarrior/pincer.ron +++ b/assets/common/abilities/custom/tidalwarrior/pincer.ron @@ -5,7 +5,7 @@ BasicMelee( recover_duration: 0.6, base_damage: 50.0, base_poise_damage: 0.0, - knockback: ( strength: 100.0, direction: Towards), + knockback: ( strength: 50.0, direction: Towards), range: 5.0, max_angle: 60.0, damage_effect: None, diff --git a/assets/common/abilities/custom/tidalwarrior/scuttle.ron b/assets/common/abilities/custom/tidalwarrior/scuttle.ron index 28552b4f46..31499e4f9d 100644 --- a/assets/common/abilities/custom/tidalwarrior/scuttle.ron +++ b/assets/common/abilities/custom/tidalwarrior/scuttle.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 2.0, swing_duration: 0.1, recover_duration: 0.5, + ori_modifier: 0.3, charge_through: false, is_interruptible: false, damage_kind: Crushing, diff --git a/assets/common/abilities/custom/tidalwarrior/totem_wave.ron b/assets/common/abilities/custom/tidalwarrior/totem_wave.ron index 0bc8176d3c..bc1e7426ef 100644 --- a/assets/common/abilities/custom/tidalwarrior/totem_wave.ron +++ b/assets/common/abilities/custom/tidalwarrior/totem_wave.ron @@ -5,7 +5,7 @@ Shockwave( recover_duration: 3.5, damage: 10, poise_damage: 0, - knockback: ( strength: 25.0, direction: Away), + knockback: ( strength: 18.0, direction: Away), shockwave_angle: 360.0, shockwave_vertical_angle: 30.0, shockwave_speed: 10.0, diff --git a/assets/common/abilities/custom/turret/flamethrower.ron b/assets/common/abilities/custom/turret/flamethrower.ron index 33f5dc51c9..da84dd0160 100644 --- a/assets/common/abilities/custom/turret/flamethrower.ron +++ b/assets/common/abilities/custom/turret/flamethrower.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 0, - orientation_behavior: Normal, ori_rate: 0.3, specifier: Flamethrower, ) diff --git a/assets/common/abilities/custom/yeti/frostbreath.ron b/assets/common/abilities/custom/yeti/frostbreath.ron index 7f35a26ae7..2f29bf5e94 100644 --- a/assets/common/abilities/custom/yeti/frostbreath.ron +++ b/assets/common/abilities/custom/yeti/frostbreath.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 0, - orientation_behavior: FromOri, ori_rate: 0.1, specifier: Frost, ) diff --git a/assets/common/abilities/custom/yeti/icespikes.ron b/assets/common/abilities/custom/yeti/icespikes.ron index 38e9130bd5..ef46d632c5 100644 --- a/assets/common/abilities/custom/yeti/icespikes.ron +++ b/assets/common/abilities/custom/yeti/icespikes.ron @@ -5,7 +5,7 @@ Shockwave( recover_duration: 1.0, damage: 100, poise_damage: 10, - knockback: (strength: 25.0, direction: Up), + knockback: (strength: 18.0, direction: Up), shockwave_angle: 90.0, shockwave_vertical_angle: 15.0, shockwave_speed: 15.0, diff --git a/assets/common/abilities/custom/yeti/strike.ron b/assets/common/abilities/custom/yeti/strike.ron index fc8cb528ee..f54fb79408 100644 --- a/assets/common/abilities/custom/yeti/strike.ron +++ b/assets/common/abilities/custom/yeti/strike.ron @@ -5,7 +5,7 @@ BasicMelee( recover_duration: 1.0, base_damage: 100, base_poise_damage: 50, - knockback: ( strength: 70.0, direction: Away), + knockback: ( strength: 50.0, direction: Away), range: 4.0, max_angle: 20.0, damage_effect: None, diff --git a/assets/common/abilities/hammer/charged.ron b/assets/common/abilities/hammer/charged.ron index 73871798a8..0820ef5bdf 100644 --- a/assets/common/abilities/hammer/charged.ron +++ b/assets/common/abilities/hammer/charged.ron @@ -3,8 +3,8 @@ ChargedMelee( energy_drain: 300, initial_damage: 10, scaled_damage: 160, - initial_poise_damage: 20, - scaled_poise_damage: 60, + initial_poise_damage: 5, + scaled_poise_damage: 75, initial_knockback: 5.0, scaled_knockback: 20.0, range: 3.5, diff --git a/assets/common/abilities/sceptre/lifestealbeam.ron b/assets/common/abilities/sceptre/lifestealbeam.ron index 5236219eb4..7d37405d23 100644 --- a/assets/common/abilities/sceptre/lifestealbeam.ron +++ b/assets/common/abilities/sceptre/lifestealbeam.ron @@ -9,7 +9,6 @@ BasicBeam( damage_effect: Some(Lifesteal(0.125)), energy_regen: 50, energy_drain: 0, - orientation_behavior: Normal, ori_rate: 0.3, specifier: LifestealBeam ) diff --git a/assets/common/abilities/spear/dash.ron b/assets/common/abilities/spear/dash.ron index 837a91b629..1914b1f67d 100644 --- a/assets/common/abilities/spear/dash.ron +++ b/assets/common/abilities/spear/dash.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 1.0, swing_duration: 0.1, recover_duration: 0.8, + ori_modifier: 0.3, charge_through: false, is_interruptible: true, damage_kind: Piercing, diff --git a/assets/common/abilities/staff/flamethrower.ron b/assets/common/abilities/staff/flamethrower.ron index 8f5c80ac0a..aefa0236b8 100644 --- a/assets/common/abilities/staff/flamethrower.ron +++ b/assets/common/abilities/staff/flamethrower.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 350, - orientation_behavior: Normal, ori_rate: 0.3, specifier: Flamethrower, ) diff --git a/assets/common/abilities/staffsimple/flamethrower.ron b/assets/common/abilities/staffsimple/flamethrower.ron index b024bae2e4..d676f32c10 100644 --- a/assets/common/abilities/staffsimple/flamethrower.ron +++ b/assets/common/abilities/staffsimple/flamethrower.ron @@ -14,7 +14,6 @@ BasicBeam( ))), energy_regen: 0, energy_drain: 350, - orientation_behavior: FromOri, ori_rate: 0.3, specifier: Flamethrower, ) diff --git a/assets/common/abilities/sword/dash.ron b/assets/common/abilities/sword/dash.ron index 5c932b85aa..6ae5fdba0e 100644 --- a/assets/common/abilities/sword/dash.ron +++ b/assets/common/abilities/sword/dash.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 1.2, swing_duration: 0.1, recover_duration: 0.5, + ori_modifier: 0.3, charge_through: true, is_interruptible: true, damage_kind: Piercing, diff --git a/assets/common/abilities/swordsimple/dash.ron b/assets/common/abilities/swordsimple/dash.ron index f231f2500b..b9ae042353 100644 --- a/assets/common/abilities/swordsimple/dash.ron +++ b/assets/common/abilities/swordsimple/dash.ron @@ -14,6 +14,7 @@ DashMelee( charge_duration: 1.2, swing_duration: 0.1, recover_duration: 0.9, + ori_modifier: 0.3, charge_through: false, is_interruptible: true, damage_kind: Piercing, diff --git a/assets/common/entity/dungeon/tier-5/cultist.ron b/assets/common/entity/dungeon/tier-5/cultist.ron index 88e29509b0..283fd9f054 100644 --- a/assets/common/entity/dungeon/tier-5/cultist.ron +++ b/assets/common/entity/dungeon/tier-5/cultist.ron @@ -6,11 +6,11 @@ EntityConfig ( loot: LootTable("common.loot_tables.dungeon.tier-5.enemy"), hands: TwoHanded(Choice([ - (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))), - (2.0, Some(Item("common.items.weapons.sword.cultist"))), - (1.0, Some(Item("common.items.weapons.hammer.cultist_purp_2h-0"))), - (1.0, Some(Item("common.items.weapons.hammer_1h.orichalcum-0"))), - (1.0, Some(Item("common.items.weapons.bow.velorite"))), + (2.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))), + (4.0, Some(Item("common.items.weapons.sword.cultist"))), + (2.0, Some(Item("common.items.weapons.hammer.cultist_purp_2h-0"))), + (2.0, Some(Item("common.items.weapons.hammer_1h.orichalcum-0"))), + (2.0, Some(Item("common.items.weapons.bow.velorite"))), (1.0, Some(Item("common.items.weapons.sceptre.sceptre_velorite_0"))), ])), diff --git a/assets/common/items/weapons/sword_1h/starter.ron b/assets/common/items/weapons/sword_1h/starter.ron new file mode 100644 index 0000000000..665c22a547 --- /dev/null +++ b/assets/common/items/weapons/sword_1h/starter.ron @@ -0,0 +1,21 @@ +ItemDef( + name: "Damaged Gladius", + description: "This blade has seen better days.", + kind: Tool(( + kind: Sword, + hands: One, + stats: Direct(( + equip_time_secs: 0.3, + power: 0.5, + effect_power: 1.0, + speed: 1.0, + crit_chance: 0.16, + range: 1.0, + energy_efficiency: 1.0, + buff_strength: 1.0, + )), + )), + quality: Low, + tags: [], + ability_spec: None, +) diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron index 5a1f2a8515..b46eece6a9 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book.ron @@ -506,6 +506,15 @@ craft_sprite: Some(CraftingBench), is_recycling: false, ), + "Healing Sceptre": ( + output: ("common.items.weapons.sceptre.starter_sceptre", 1), + inputs: [ + (Item("common.items.crafting_ing.twigs"), 10), + (Item("common.items.crafting_ing.stones"), 0), + ], + craft_sprite: None, + is_recycling: false, + ), "Soothing Loop": ( output: ("common.items.weapons.sceptre.loops0", 1), inputs: [ @@ -514,7 +523,7 @@ (Item("common.items.mineral.gem.ruby"), 4), (Item("common.items.tool.craftsman_hammer"), 0), ], - craft_sprite: None, + craft_sprite: Some(CraftingBench), is_recycling: false, ), "Hunting Bow": ( diff --git a/assets/common/skillset/dungeon/tier-5/staff.ron b/assets/common/skillset/dungeon/tier-5/staff.ron new file mode 100644 index 0000000000..59d5e9ae24 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/staff.ron @@ -0,0 +1,21 @@ +([ + Group(Weapon(Staff)), + + // Fireball + Skill((Staff(BDamage), Some(1))), + Skill((Staff(BRegen), Some(1))), + Skill((Staff(BRadius), Some(1))), + + // Flamethrower + Skill((Staff(FDamage), Some(1))), + Skill((Staff(FRange), Some(1))), + Skill((Staff(FDrain), Some(1))), + Skill((Staff(FVelocity), Some(1))), + + // Shockwave + Skill((Staff(UnlockShockwave), None)), + Skill((Staff(SDamage), Some(1))), + Skill((Staff(SKnockback), Some(1))), + Skill((Staff(SRange), Some(1))), + Skill((Staff(SCost), Some(1))), +]) diff --git a/assets/server/manifests/kits.ron b/assets/server/manifests/kits.ron index bf3ed02c9e..d17c2997a8 100644 --- a/assets/server/manifests/kits.ron +++ b/assets/server/manifests/kits.ron @@ -45,41 +45,30 @@ ("common.items.food.tomatosalad", 100), ], "jewellery": [ - // TODO: remove duplicates and handle quantity of non-stackable items - // Necklace - ("common.items.armor.misc.neck.plain_1",1), - ("common.items.armor.misc.neck.shell",1), + ("common.items.armor.misc.neck.shell", 1), + ("common.items.armor.misc.neck.plain_1", 1), // Rings // With effects - ("common.items.armor.misc.ring.diamond",1), - ("common.items.armor.misc.ring.diamond",1), + ("common.items.armor.misc.ring.diamond", 2), - ("common.items.armor.misc.ring.ruby",1), - ("common.items.armor.misc.ring.ruby",1), + ("common.items.armor.misc.ring.ruby", 2), - ("common.items.armor.misc.ring.emerald",1), - ("common.items.armor.misc.ring.emerald",1), + ("common.items.armor.misc.ring.emerald", 2), - ("common.items.armor.misc.ring.sapphire",1), - ("common.items.armor.misc.ring.sapphire",1), + ("common.items.armor.misc.ring.sapphire", 2), - ("common.items.armor.misc.ring.topaz",1), - ("common.items.armor.misc.ring.topaz",1), + ("common.items.armor.misc.ring.topaz", 2), - ("common.items.armor.misc.ring.amethyst",1), - ("common.items.armor.misc.ring.amethyst",1), + ("common.items.armor.misc.ring.amethyst", 2), // Without effects - ("common.items.armor.misc.ring.scratched",1), - ("common.items.armor.misc.ring.scratched",1), + ("common.items.armor.misc.ring.scratched", 2), - ("common.items.armor.misc.ring.gold",1), - ("common.items.armor.misc.ring.gold",1), + ("common.items.armor.misc.ring.gold", 2), - ("common.items.armor.misc.ring.skull",1), - ("common.items.armor.misc.ring.skull",1), + ("common.items.armor.misc.ring.skull", 2), ], "endgame": [ // Cultist weapons @@ -326,8 +315,7 @@ // weapons ("common.items.weapons.sword.stone-0", 1), - ("common.items.weapons.axe_1h.stone-0", 1), - ("common.items.weapons.axe_1h.stone-1", 1), + ("common.items.weapons.axe_1h.stone-0", 2), ("common.items.weapons.hammer.stone_hammer-0", 1), ("common.items.weapons.bow.wood-0", 1), ("common.items.weapons.staff.bent_fuse", 1), diff --git a/assets/server/manifests/presets.ron b/assets/server/manifests/presets.ron index 136ef46054..c29c3b5b3f 100644 --- a/assets/server/manifests/presets.ron +++ b/assets/server/manifests/presets.ron @@ -119,6 +119,7 @@ (Sceptre(LRegen), 2), (Sceptre(HHeal), 3), + (Sceptre(HDuration), 2), (Sceptre(HCost), 2), (Sceptre(HRange), 2), diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index 083c7ef964..7caad4aa6a 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -242,31 +242,31 @@ ], threshold: 0.5, ), - Attack(ComboMelee(Swing, 1), Sword): ( + Attack(ComboMelee(Action, 1), Sword): ( files: [ "voxygen.audio.sfx.abilities.swing_sword", ], threshold: 0.7, ), - Attack(ComboMelee(Swing, 2), Sword): ( + Attack(ComboMelee(Action, 2), Sword): ( files: [ "voxygen.audio.sfx.abilities.separated_second_swing", ], threshold: 0.7, ), - Attack(ComboMelee(Swing, 3), Sword): ( + Attack(ComboMelee(Action, 3), Sword): ( files: [ "voxygen.audio.sfx.abilities.separated_third_swing", ], threshold: 0.7, ), - Attack(DashMelee(Swing), Sword): ( + Attack(DashMelee(Action), Sword): ( files: [ "voxygen.audio.sfx.abilities.sword_dash", ], threshold: 0.8, ), - Attack(SpinMelee(Swing), Sword): ( + Attack(SpinMelee(Action), Sword): ( files: [ "voxygen.audio.sfx.abilities.swing_sword", ], @@ -294,19 +294,19 @@ ], threshold: 0.5, ), - Attack(ComboMelee(Swing, 1), Hammer): ( + Attack(ComboMelee(Action, 1), Hammer): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.7, ), - Attack(ChargedMelee(Swing), Hammer): ( + Attack(ChargedMelee(Action), Hammer): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.7, ), - Attack(LeapMelee(Swing), Hammer): ( + Attack(LeapMelee(Action), Hammer): ( files: [ "voxygen.audio.sfx.abilities.swing", ], @@ -334,25 +334,25 @@ ], threshold: 0.5, ), - Attack(ComboMelee(Swing, 1), Axe): ( + Attack(ComboMelee(Action, 1), Axe): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.7, ), - Attack(ComboMelee(Swing, 2), Axe): ( + Attack(ComboMelee(Action, 2), Axe): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.7, ), - Attack(SpinMelee(Swing), Axe): ( + Attack(SpinMelee(Action), Axe): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.8, ), - Attack(LeapMelee(Swing), Axe): ( + Attack(LeapMelee(Action), Axe): ( files: [ "voxygen.audio.sfx.abilities.swing", ], @@ -470,7 +470,7 @@ ], threshold: 0.8, ), - Attack(DashMelee(Swing), Dagger): ( + Attack(DashMelee(Action), Dagger): ( files: [ "voxygen.audio.sfx.abilities.sword_dash", ], diff --git a/assets/voxygen/element/skills/skilltree/stamina_plus.png b/assets/voxygen/element/skills/skilltree/energy_plus.png similarity index 100% rename from assets/voxygen/element/skills/skilltree/stamina_plus.png rename to assets/voxygen/element/skills/skilltree/energy_plus.png diff --git a/assets/voxygen/element/ui/bag/icons/stamina.png b/assets/voxygen/element/ui/bag/icons/energy.png similarity index 100% rename from assets/voxygen/element/ui/bag/icons/stamina.png rename to assets/voxygen/element/ui/bag/icons/energy.png diff --git a/assets/voxygen/element/ui/bag/inv_bg_bag.png b/assets/voxygen/element/ui/bag/inv_bg_bag.png index 8e76c879e1..dd6187da10 100644 --- a/assets/voxygen/element/ui/bag/inv_bg_bag.png +++ b/assets/voxygen/element/ui/bag/inv_bg_bag.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d70b26aad275d29436a70e3685e29ab3cf2022ed6718f389053b7557a82f810 -size 66660 +oid sha256:8248da20b7e27e85748b65560c1a1f473549642f5f54d364c30429d0803a8c35 +size 93343 diff --git a/assets/voxygen/element/ui/bag/inv_frame_bag.png b/assets/voxygen/element/ui/bag/inv_frame_bag.png index cefd5e2c46..63b9f051e1 100644 --- a/assets/voxygen/element/ui/bag/inv_frame_bag.png +++ b/assets/voxygen/element/ui/bag/inv_frame_bag.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63cf18de0cbe0c6273e5a56841ee390465aec38592678dad08fb5c4ae6fe773c -size 5045 +oid sha256:2deacf661a411b7618576b9c17ae9372ec85be88cb456cc2baa07b7e091504c7 +size 12547 diff --git a/assets/voxygen/element/ui/bag/inv_middle_bg_bag.png b/assets/voxygen/element/ui/bag/inv_middle_bg_bag.png new file mode 100644 index 0000000000..024c171fbf --- /dev/null +++ b/assets/voxygen/element/ui/bag/inv_middle_bg_bag.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e09eaf4b52f0dc94735ce86d2d73ccfd7597d1d0987b26a2c223af0949640f14 +size 72972 diff --git a/assets/voxygen/element/ui/bag/inv_middle_frame.png b/assets/voxygen/element/ui/bag/inv_middle_frame.png new file mode 100644 index 0000000000..d03587893a --- /dev/null +++ b/assets/voxygen/element/ui/bag/inv_middle_frame.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9eb0ca5f1a442676e17987466b6de80d35c843f3b57c815395b1dcb07e37ca12 +size 12070 diff --git a/assets/voxygen/element/ui/bag/player_inv_bg_bag.png b/assets/voxygen/element/ui/bag/player_inv_bg_bag.png new file mode 100644 index 0000000000..8e76c879e1 --- /dev/null +++ b/assets/voxygen/element/ui/bag/player_inv_bg_bag.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d70b26aad275d29436a70e3685e29ab3cf2022ed6718f389053b7557a82f810 +size 66660 diff --git a/assets/voxygen/element/ui/bag/player_inv_frame_bag.png b/assets/voxygen/element/ui/bag/player_inv_frame_bag.png new file mode 100644 index 0000000000..cefd5e2c46 --- /dev/null +++ b/assets/voxygen/element/ui/bag/player_inv_frame_bag.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63cf18de0cbe0c6273e5a56841ee390465aec38592678dad08fb5c4ae6fe773c +size 5045 diff --git a/assets/voxygen/element/ui/bag/second_phase_scrollbar_bg.png b/assets/voxygen/element/ui/bag/second_phase_scrollbar_bg.png new file mode 100644 index 0000000000..4a6277fe21 --- /dev/null +++ b/assets/voxygen/element/ui/bag/second_phase_scrollbar_bg.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a729f672e0b7d1fbe2138fe6d144d92a58b04ec82a7f947bb13b7c78f4d720fd +size 5778 diff --git a/assets/voxygen/element/ui/char_select/portraits/danari_f.png b/assets/voxygen/element/ui/char_select/portraits/danari_f.png index 503e55cb56..a7dd41216b 100644 --- a/assets/voxygen/element/ui/char_select/portraits/danari_f.png +++ b/assets/voxygen/element/ui/char_select/portraits/danari_f.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8877b0f6b59742996a64e9e35f07028e15c2e2df17eb7f0b80a1714cc7b30c96 -size 6323 +oid sha256:b3fe6ed254e8d1fc4dbdec0014070bd8a421e924f581f59642837b999f47fef3 +size 685 diff --git a/assets/voxygen/element/ui/char_select/portraits/danari_m.png b/assets/voxygen/element/ui/char_select/portraits/danari_m.png index 739e877c1d..c1845faeb4 100644 --- a/assets/voxygen/element/ui/char_select/portraits/danari_m.png +++ b/assets/voxygen/element/ui/char_select/portraits/danari_m.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e5ab138c073d970481e9213ba9f7c4c681011eba92114faef37c7c7edfde387 -size 12016 +oid sha256:d0a3c0ecf9309e439361d80b93f5292941fc677603e43531f628a024fe5917b3 +size 676 diff --git a/assets/voxygen/element/ui/map/buttons/dungeon_hover.png b/assets/voxygen/element/ui/map/buttons/dungeon_hover.png index 538853936d..480ac2a0ae 100644 --- a/assets/voxygen/element/ui/map/buttons/dungeon_hover.png +++ b/assets/voxygen/element/ui/map/buttons/dungeon_hover.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:076043e069acf0350db3cf3e7a48eb5432ac0eb932298518e71133676bc18637 -size 142 +oid sha256:ffe704cb645b9b0db11b84094b8f9ee11e04793e2fc716f4208371c5e93e57d5 +size 140 diff --git a/assets/voxygen/element/ui/map/icons/dif_3.png b/assets/voxygen/element/ui/map/icons/dif_3.png index 7693512c70..b88f520bdb 100644 --- a/assets/voxygen/element/ui/map/icons/dif_3.png +++ b/assets/voxygen/element/ui/map/icons/dif_3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68bfc4b33e4ca4c3144054d4df585187a92ee618f5e485c1c818b0f5f140a581 -size 82 +oid sha256:bf21e452bffcdec7c74d59e86189f61e678a83e64800c76556216e25ef5cf35b +size 91 diff --git a/assets/voxygen/element/ui/map/icons/dif_4.png b/assets/voxygen/element/ui/map/icons/dif_4.png index 2aaba70368..317a36fa0c 100644 --- a/assets/voxygen/element/ui/map/icons/dif_4.png +++ b/assets/voxygen/element/ui/map/icons/dif_4.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7fbd97ef333a1837cad086d1ec1bbf24570b4fd2db516b0c85d322e2839b4dd -size 83 +oid sha256:0710afbbe2b4f333ba9a1e268af6da940b79b887a089b6258978571e2e26e59b +size 98 diff --git a/assets/voxygen/element/ui/map/icons/dif_5.png b/assets/voxygen/element/ui/map/icons/dif_5.png index f1a1cd22de..cd194e84f6 100644 --- a/assets/voxygen/element/ui/map/icons/dif_5.png +++ b/assets/voxygen/element/ui/map/icons/dif_5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea721c094f259fd9581030d25f7a80e2dbda65f0e9488c6f2d9ac1b64fc1e966 -size 83 +oid sha256:9c11a3e00c8f1ba1481e86d7df65f9d3a5cbf2b5366dab9b623605872a9e77f9 +size 95 diff --git a/assets/voxygen/element/ui/map/icons/dif_unknown.png b/assets/voxygen/element/ui/map/icons/dif_unknown.png new file mode 100644 index 0000000000..233c0b91b3 --- /dev/null +++ b/assets/voxygen/element/ui/map/icons/dif_unknown.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e58b9fb6b567a0c7d3f938d266ef13df642fbf8133c327aacabd18b7051ca896 +size 95 diff --git a/assets/voxygen/element/ui/skillbar/stamina_bg.png b/assets/voxygen/element/ui/skillbar/energy_bg.png similarity index 100% rename from assets/voxygen/element/ui/skillbar/stamina_bg.png rename to assets/voxygen/element/ui/skillbar/energy_bg.png diff --git a/assets/voxygen/element/ui/skillbar/stamina_frame.png b/assets/voxygen/element/ui/skillbar/energy_frame.png similarity index 100% rename from assets/voxygen/element/ui/skillbar/stamina_frame.png rename to assets/voxygen/element/ui/skillbar/energy_frame.png diff --git a/assets/voxygen/element/weapons/swords.png b/assets/voxygen/element/weapons/swords.png new file mode 100644 index 0000000000..fa2a4f1ade --- /dev/null +++ b/assets/voxygen/element/weapons/swords.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d48c06c9dab55e392528037e8efbbae35c8586ece413976cdc72c0d84f4ad117 +size 316 diff --git a/assets/voxygen/i18n/cz_CZ/_manifest.ron b/assets/voxygen/i18n/cz_CZ/_manifest.ron index d5a6c1bef5..38530e96b1 100644 --- a/assets/voxygen/i18n/cz_CZ/_manifest.ron +++ b/assets/voxygen/i18n/cz_CZ/_manifest.ron @@ -1,4 +1,4 @@ -/// Localization for Polish / Tłumaczenia dla języka polskiego +/// Localization for Čeština ( metadata: ( language_name: "Czech", @@ -26,1043 +26,5 @@ asset_key: "voxygen.font.OpenSans-Regular", scale_ratio: 1.0, ), - }, - string_map: { - // Texts used in multiple locations with the same formatting - "common.username": "jméno", - "common.singleplayer": "Hra jednoho hráče", - "common.multiplayer": "Hra více hráčů", - "common.servers": "Servery", - "common.quit": "Vypnout", - "common.settings": "Nastavení", - "common.languages": "Jazyky", - "common.interface": "Rozhraní", - "common.gameplay": "Hra", - "common.controls": "Ovládání", - "common.video": "Grafika", - "common.sound": "Zvuk", - "common.resume": "Pokračovat", - "common.characters": "Postavy", - "common.close": "Zavřít", - "common.yes": "Ano", - "common.no": "Ne", - "common.back": "Zpět", - "common.create": "Vytvořik", - "common.okay": "OK", - "common.add": "Přidat", - "common.accept": "Příjmout", - "common.decline": "Odmítnout", - "common.disclaimer": "Zřeknutí se odpovědnosti", - "common.cancel": "Zrušit", - "common.none": "Nic", - "common.error": "Chyba", - "common.fatal_error": "Fatalní Chyba", - "common.you": "ty", - "common.automatic": "Auto", - "common.random": "Náhodný", - "common.empty": "Prázdný", - - // Settings Window title - "common.interface_settings": "Rozhraní", - "common.gameplay_settings": "Hra", - "common.controls_settings": "Ovládání", - "common.video_settings": "Grafika", - "common.sound_settings": "Zvuk", - "common.language_settings": "Jazyk", - - // Message when connection to the server is lost - "common.connection_lost": r#"Spojení ztraceno! - Restartoval se server? - Je klient aktuální?"#, - - - "common.species.orc": "Ork", - "common.species.human": "Člověk", - "common.species.dwarf": "Trpaslík", - "common.species.elf": "Elf", - "common.species.undead": "Nemrtvý", - "common.species.danari": "Danari", - - "common.weapons.axe": "Sekera", - "common.weapons.sword": "Meč", - "common.weapons.staff": "Hůl", - "common.weapons.bow": "Luk", - "common.weapons.hammer": "Kladivo", - "common.weapons.general": "Všeobecný", - "common.weapons.sceptre": "Žezlo", - "common.weapons.shield": "Štít", - "common.weapons.spear": "Kopí", - "common.weapons.hammer_simple": "Jednoduché Kladivo", - "common.weapons.sword_simple": "Jednoduchý Meč", - "common.weapons.staff_simple": "Jednoduchá Hůl", - "common.weapons.axe_simple": "Jednoduchá Sekera", - "common.weapons.bow_simple": "Jednoduchý Luk", - "common.weapons.unique": "Unikátní", - "common.tool.debug": "Debug", - "common.tool.faming": "Farmářský Nástroj", - "common.tool.pick": "Krumpáč", - "common.kind.modular_component": "Modulární komponenta", - "common.kind.glider": "Kluzák", - "common.kind.consumable": "Spotřební", - "common.kind.throwable": "Lze házet", - "common.kind.utility": "Užitečné", - "common.kind.ingredient": "Ingredience", - "common.kind.lantern": "Lampa", - "common.hands.one": "Jednoruční", - "common.hands.two": "Dvojruční", - - "common.rand_appearance": "Náhodný vzhled", - "common.rand_name": "Náhodné Jméno", - - "common.stats.dps": "DPS", - "common.stats.power": "Moc", - "common.stats.speed": "Rychlost", - "common.stats.poise": "Postoj", - "common.stats.crit_chance": "Kritická šance", - "common.stats.crit_mult": "Crit Mult", - "common.stats.armor": "Zbroj", - "common.stats.poise_res": "Postojové res", - "common.stats.slots": "Sloty", - - /// End Common section - - - /// Start Main screen section - "main.connecting": "Připojování", - "main.creating_world": "Tvorba Světa", - - // Welcome notice that appears the first time Veloren is started - "main.notice": r#"Vítejte v alfa verzi Veloren! - -Než začnete, mějte na paměti, že: - -- Hra je v rané verzi alfa. Očekávejte chyby, extrémně nedokončený příběh, nedostatečně rozvinuté mechaniky a chybějící funkce. - -- Pokud se chcete podělit o svůj názor, máte návrhy nebo nápady nebo chcete nahlásit chybu, můžete nás kontaktovat prostřednictvím Redditu, GitLabu nebo Discordu. - -- Veloren je licencován pod licencí open source GPL 3. To znamená, že máte právo hru upravovat a distribuovat, jakkoliv chcete (pokud je vaše práce licencována také pod GPL 3). - -- Veloren je neziskový projekt, kde je každý pracující člověk dobrovolníkem. Pokud se vám tato hra líbí, připojte se k našemu týmu! - -Děkujeme za přečtení této zprávy a doufáme, že se vám tato hra bude líbit! - -~ Tvůrci Veloren"#, - - // Login process description - "main.login_process": r#"Informace o procesu přihlášení: - -Pokud máte problémy s přihlášením: - -Nezapomeňte, že k připojení na server -se zapnutým ověřením potřebujete účet. - -Účet si můžete vytvořit na webu: - -https://veloren.net/account/."#, - "main.login.server_not_found": "Server nenalezen", - "main.login.authentication_error": "Chyba ověření", - "main.login.server_full": "Server je plný", - "main.login.untrusted_auth_server": "Ověřovací server je nedůvěryhodný", - "main.login.outdated_client_or_server": "ServerWentMad: Pravděpodobně jsou verze nekompatibilní, zkontrolujte aktualizace!", - "main.login.network_wrong_version": "Server běží na jiné verzy hry. Aktualizuj si klienta.", - "main.login.timeout": "Timeout: Server neodpověděl včas. (Přetížení nebo chyby v síti).", - "main.login.server_shut_down": "Server Uzavřen", - "main.login.network_error": "Chyba sítě.", - "main.login.failed_sending_request": "Dotaz na ověřovací server se nezdařil", - "main.login.client_crashed": "Pád klienta", - "main.servers.select_server": "Výběr serveru", - "main.server": "Server", - "main.password": "Heslo", - "main.username": "Jméno", - "main.tip":"Rada:", - "main.login.invalid_character": "Vybraná postava je neplatná", - "main.login.not_on_whitelist": "Potřebuješ být ve whitelistu. Kontaktuj Admina serveru.", - "main.login.banned": "Byl/a si zabanován/á z tohoto důvodu", - "main.login.kicked": "Byl/a si vyhozen/á z tohoto důvodu", - "main.login.select_language": "Vyber si svůj Jazyk", - "main.login.insecure_auth_scheme": "Schéma ověření HTTP NENÍ podporováno. Je to nejisté! Pro účely vývoje je HTTP povolen pro „localhost“ nebo pro ladění", - "main.login.failed_auth_server_url_invalid": "Připojení k ověřovacímu serveru se nezdařilo.", - /// End Main screen section - - - /// Start HUD Section - - - "hud.press_key_to_show_keybindings_fmt": "Kliknutím {key} zobrazíte klávesy", - "hud.press_key_to_show_debug_info_fmt": "Kliknutím {key} zobrazíte rozšířené informace", - "hud.press_key_to_toggle_keybindings_fmt": "Kliknutím {key} přepnete klávesové zkratky", - "hud.press_key_to_toggle_debug_info_fmt": "Kliknutím {key} přepnete rozšířené informace", - - // Respawn message - "hud.press_key_to_respawn": r#"Kliknutím {key} se zrodíte u posledního navštíveného ohniště."#, - - // Welcome message - "hud.welcome": r#"Vítejte v alfa verzi Veloren! - - -Několik tipů, než začnete: - -Klepnutím na F1 zkontrolujte výchozí klíče. - -Napište /help v chatu a podívejte se na dostupné příkazy - - -Po celém světě jsou náhodně skryté truhly a další předměty s náhodnou kořistí! - -Klikněte pravým tlačítkem pro sebrání. - -Chcete-li použít nebo nasadit předmět, použijte klávesu „B“ k otevření inventáře. - -Poklepáním na položku ji můžete použít nebo nosit. - -Nepotřebné položky vytáhnete z oblasti inventáře, zahodíte je. - - -Noci ve Velorenu mohou být temné. - -Vybavte si lampu a rozsvíťte ji kliknutím na tlačítko „G“ - - -Chcete odemknout systémový kurzor ze hry? Klikněte na 'TAB' - - -Užijte si svůj pobyt ve světě Veloren!"#, - - // Chat - "hud.chat.online_msg": "[{name}] je online", - "hud.chat.offline_msg": "[{name}] šel/šla offline", - - "hud.chat.default_death_msg": "[{name}] zemřel", - "hud.chat.environmental_kill_msg": "[{name}] zemřel v {environment}", - "hud.chat.fall_kill_msg": "[{name}] zemřel z pádu", - "hud.chat.suicide_msg": "[{name}] zemřel díky svému zapříčinění", - - "hud.chat.pvp_buff_kill_msg": "[{victim}] zemřel z {buff} od [{attacker}]", - "hud.chat.pvp_melee_kill_msg": "[{attacker}] porazil [{victim}]", - "hud.chat.pvp_ranged_kill_msg": "[{attacker}] postřelil [{victim}]", - "hud.chat.pvp_explosion_kill_msg": "[{attacker}] odpálil [{victim}]", - "hud.chat.pvp_energy_kill_msg": "[{attacker}] zabil [{victim}] pomocí magie", - - "hud.chat.nonexistent_buff_kill_msg": "[{victim}] zemřel pomocí {buff}", - - "hud.chat.npc_buff_kill_msg": "[{victim}] died of {buff} od {attacker}", - "hud.chat.npc_melee_kill_msg": "{attacker} zabil [{victim}]", - "hud.chat.npc_ranged_kill_msg": "{attacker} postřelil [{victim}]", - "hud.chat.npc_explosion_kill_msg": "{attacker} odpálil [{victim}]", - "hud.chat.npc_energy_kill_msg": "{attacker} zabil [{victim}] pomocí magie", - "hud.chat.npc_other_kill_msg": "{attacker} zabil [{victim}]", - - "hud.chat.loot_msg": "Sebral si [{item}]", - "hud.chat.loot_fail": "Tvůj inventář je plný!", - "hud.chat.goodbye": "nashledanou!", - "hud.chat.connection_lost": "Spojení ztraceno. Odpojuji za {time} sekund.", - - - // Inventory - "hud.bag.glider": "Glider", - "hud.bag.bag": "Batoh", - "hud.bag.health": "Život", - "hud.bag.stamina": "Výdrž", - "hud.bag.combat_rating": "Bojové hodnocení", - "hud.bag.protection": "Ochrana", - "hud.bag.combat_rating_desc": "Vypočítáno z života/zbroje.", - "hud.bag.protection_desc": "Redukce poškození přes brnení", - "hud.bag.inventory": "Inventář {playername}", - "hud.bag.stats_title": "Statistiky {playername}", - "hud.bag.exp": "Zkušenosti", - "hud.bag.armor": "Výzbroj", - "hud.bag.stats": "Statistiky", - "hud.bag.head": "Hlava", - "hud.bag.neck": "Krk", - "hud.bag.tabard": "Tabard", - "hud.bag.shoulders": "Ramena", - "hud.bag.chest": "Hruď", - "hud.bag.hands": "Ruce", - "hud.bag.lantern": "Lampa", - "hud.bag.belt": "Pas", - "hud.bag.ring": "Prsten", - "hud.bag.back": "Záda", - "hud.bag.legs": "Nohy", - "hud.bag.feet": "Chodidla", - "hud.bag.mainhand": "Hlavní Ruka", - "hud.bag.offhand": "Druhá Ruka", - - - // Map and Questlog - "hud.map.map_title": "Mapa", - "hud.map.qlog_title": "Úkoly", - "hud.map.difficulty": "Obtížnost", - "hud.map.towns": "Města", - "hud.map.castles": "Hrady", - "hud.map.dungeons": "Kobky", - "hud.map.caves": "Jeskyně", - "hud.map.cave": "Jeskyně", - "hud.map.trees": "Velké Stromy", - "hud.map.tree": "Velký Strom", - "hud.map.town": "Město", - "hud.map.castle": "Hrad", - "hud.map.dungeon": "Kobka", - "hud.map.difficulty_dungeon": "Kobka\n\nDifficulty: {difficulty}", - "hud.map.drag": "Pohybovat", - "hud.map.zoom": "Přiblížit", - "hud.map.recenter": "Recentrovat", - "hud.rank_up": "Nový Skillpoint", - "hud.skill.sp_available": "{number} SP dostupných", - "hud.skill.not_unlocked": "Není ještě odemčeno", - "hud.skill.req_sp": "\n\nVyžaduje {number} SP", - - // Skills - // General - "hud.skill.inc_health_title": "Zvýší Zdraví", - "hud.skill.inc_health": "Zvýší maximální zdraví o 5{SP}", - "hud.skill.inc_stam_title": "Zvýší Výdrž", - "hud.skill.inc_stam": "Zvýší Výdrž o 5{SP}", - "hud.skill.unlck_sword_title": "Odemknutí Meče", - "hud.skill.unlck_sword": "Odemkne strom dovedností pro meče{SP}", - "hud.skill.unlck_axe_title": "Odemknutí Sekery", - "hud.skill.unlck_axe": "Odemkne strom dovedností pro sekery{SP}", - "hud.skill.unlck_hammer_title": "Odemknutí Kladiva", - "hud.skill.unlck_hammer": "Odemkne strom dovedností pro kladivo{SP}", - "hud.skill.unlck_bow_title": "Odemknutí Luku", - "hud.skill.unlck_bow": "Odemkne strom dovedností pro luk{SP}", - "hud.skill.unlck_staff_title": "Odemknutí Hole", - "hud.skill.unlck_staff": "Odemkne strom dovedností pro hůl{SP}", - "hud.skill.unlck_sceptre_title": "Odemknutí Žezla", - "hud.skill.unlck_sceptre": "Odemkne strom dovedností pro žezlo{SP}", - "hud.skill.dodge_title": "Vyhnutí", - "hud.skill.dodge": "Vyhnese útokům zblízka{SP}", - "hud.skill.roll_stamina_title": "Menší spotřeba Výdrže pro Kotrmelec", - "hud.skill.roll_stamina": "Kotrmelec použije o 20% méně Výdrže{SP}", - "hud.skill.roll_speed_title": "Rychlost Kotrmelce", - "hud.skill.roll_speed": "Kotrmelec je o 20% rychlejší{SP}", - "hud.skill.roll_dur_title": "Délka Kotrmelce", - "hud.skill.roll_dur": "Kotrmelec je delší o 20%{SP}", - "hud.skill.climbing_title": "Lezení", - "hud.skill.climbing": "Skočíš výš", - "hud.skill.climbing_cost_title": "Lezení", - "hud.skill.climbing_cost": "Lezení spotřebuje o 20% méně Výdrže{SP}", - "hud.skill.climbing_speed_title": "Rychlost Lezení", - "hud.skill.climbing_speed": "Lezení je o 20% rychlejší{SP}", - "hud.skill.swim_title": "Plavání", - "hud.skill.swim": "Pohyb v mokrém prostředí", - "hud.skill.swim_speed_title": "Rychlost plávání", - "hud.skill.swim_speed": "Plaveš o 40% rychleji{SP}", - // Sceptre - "hud.skill.sc_lifesteal_title": "Paprsek Životokrádeže", - "hud.skill.sc_lifesteal": "Krade život z nepřátel", - "hud.skill.sc_lifesteal_damage_title": "Poškození", - "hud.skill.sc_lifesteal_damage": "Přídá o 20% více poškození{SP}", - "hud.skill.sc_lifesteal_range_title": "Dosah", - "hud.skill.sc_lifesteal_range": "Paprsek dosáhne o 25% dále{SP}", - "hud.skill.sc_lifesteal_lifesteal_title": "Životokrádež", - "hud.skill.sc_lifesteal_lifesteal": "Konverutuje o 30% více poškození na život{SP}", - "hud.skill.sc_lifesteal_regen_title": "Obnova Výdrže", - "hud.skill.sc_lifesteal_regen": "Doplní výdrž o 25%{SP}", - "hud.skill.sc_heal_title": "Paprsek Léčby", - "hud.skill.sc_heal": "Vyléčí vaše přátelé pomocí krve nepřátel", - "hud.skill.sc_heal_heal_title": "Léčba", - "hud.skill.sc_heal_heal": "Zvýší efektivnost Léčby o 20%{SP}", - "hud.skill.sc_heal_cost_title": "Spotřeba Výdrže", - "hud.skill.sc_heal_cost": "Léčení spotřebuje o 20% méně Výdrže{SP}", - "hud.skill.sc_heal_range_title": "Dosah", - "hud.skill.sc_heal_range": "Paprsek dosáhne dále o 25% {SP}", - "hud.skill.sc_wardaura_unlock_title": "Ochranářská Aura", - "hud.skill.sc_wardaura_unlock": "Dovolí ochránit přátele před útoky{SP}", - "hud.skill.sc_wardaura_strength_title": "Síla", - "hud.skill.sc_wardaura_strength": "Síla ochrany se zvýší o 20%{SP}", - "hud.skill.sc_wardaura_duration_title": "Délka", - "hud.skill.sc_wardaura_duration": "Efekt vaší ochrany potrvá o 30% déle{SP}", - "hud.skill.sc_wardaura_range_title": "Rádius", - "hud.skill.sc_wardaura_range": "Ochrana dosáhne o 25% dále{SP}", - "hud.skill.sc_wardaura_cost_title": "Spotřeba Výdrže", - "hud.skill.sc_wardaura_cost": "Tvorba ochrany stojí o 20% méně Energie{SP}", - // Staff - "hud.skill.st_shockwave_range_title" : "Dosah Rázové vlny", - "hud.skill.st_shockwave_range" : "Umožný hodit věci mimo dosah. Dosah se zvýšil o 20%{SP}", - "hud.skill.st_shockwave_cost_title" : "Spotřeba Rázové vlny", - "hud.skill.st_shockwave_cost" : "Snižuje spotřebu energie pro házení bezbraných vesničanů o 20%{SP}", - "hud.skill.st_shockwave_knockback_title" : "Ráz Rázové vlny", - "hud.skill.st_shockwave_knockback" : "Zvyšuje odhození o 30%{SP}", - "hud.skill.st_shockwave_damage_title" : "Poškození Rázové Vlny", - "hud.skill.st_shockwave_damage" : "Zvyšuje poškození o 30%{SP}", - "hud.skill.st_shockwave_unlock_title" : "Shockwave Unlock", - "hud.skill.st_shockwave_unlock" : "Umožnuje odhazovat objekty pomocí ohně{SP}", - "hud.skill.st_flamethrower_title" : "Plamenomet", - "hud.skill.st_flamethrower" : "Podpaluje, jdeme péct", - "hud.skill.st_flame_velocity_title" : "Rychlost metání", - "hud.skill.st_flame_velocity" : "Zvýší rychlost ohně o 25% {SP}", - "hud.skill.st_flamethrower_range_title" : "Dosah Plamenometu", - "hud.skill.st_flamethrower_range" : "Když plameny nedosáhnou, tak je potřeba je zvětšit o 25% {SP}", - "hud.skill.st_energy_drain_title" : "Spotřeba Energie", - "hud.skill.st_energy_drain" : "Zmenší spotřebu energie o 20%{SP}", - "hud.skill.st_flamethrower_damage_title" : "Poškození Plamenometem", - "hud.skill.st_flamethrower_damage" : "Zvýší poškození o 30%{SP}", - "hud.skill.st_explosion_radius_title" : "Rozsah Exploze", - "hud.skill.st_explosion_radius" : "Cím větší, tím lepší. Zvětší Rádius Exploze o 10%{SP}", - "hud.skill.st_stamina_regen_title" : "Obnova Výdrže", - "hud.skill.st_stamina_regen" : "Zvýší generaci výdrže o 20%{SP}", - "hud.skill.st_fireball_title" : "Ohnivá Koule", - "hud.skill.st_fireball" : "Hrajte aport s nepřáteli", - "hud.skill.st_damage_title" : "Poškození", - "hud.skill.st_damage" : "Zvětší Poškození o 20%{SP}", - "hud.skill.st_explosion_title" : "Exploze", - "hud.skill.st_explosion" : "Když oheň nestačí{SP}", - // Bow - "hud.skill.bow_projectile_speed_title" : "Rychlost Projektilu", - "hud.skill.bow_projectile_speed" : "Zvětšuje rozsah, rychlost o 30%{SP}", - "hud.skill.bow_arrow_count_title" : "Počet Šípů", - "hud.skill.bow_arrow_count" : "Vystřelí další šíp při odskoku{SP}", - "hud.skill.bow_repeater_cost_title" : "Zmenšení Spotřeby", - "hud.skill.bow_repeater_cost" : "Sníží spotřebu Energie o 30%{SP}", - "hud.skill.bow_repeater_glide_title" : "Plachtění Opakovače", - "hud.skill.bow_repeater_glide" : "Doplachti dál při opakování{SP}", - "hud.skill.bow_repeater_damage_title" : "Poškození během opakování", - "hud.skill.bow_repeater_damage" : "Zvýší poškození o 40%{SP}", - "hud.skill.bow_repeater_unlock_title" : "Odemčení Opakovače", - "hud.skill.bow_repeater_unlock" : "Odemkne možnost odskočit ve vzduchu a vystřelit haldu šípů{SP}", - "hud.skill.bow_charged_title" : "Silný Výstřel", - "hud.skill.bow_charged" : "Protože si počkal/a dýl", - "hud.skill.bow_charged_knockback_title" : "Nabitý Ráz", - "hud.skill.bow_charged_knockback" : "Odhoď neprátele o 25% dále{SP}", - "hud.skill.bow_charged_move_speed_title" : "Rychlost při míření", - "hud.skill.bow_charged_move_speed" : "Zvýší pohyb během natahování luku o 25%{SP}", - "hud.skill.bow_charged_speed_title" : "Nabitá rychlost ", - "hud.skill.bow_charged_speed" : "Zvýší jak rychle lze střílet o 10%{SP}", - "hud.skill.bow_charged_projectile_speed_title" : "Rychlost Nabitého Projektilu", - "hud.skill.bow_charged_projectile_speed" : "Rychlost projektilu se zvětší o 20% během nabijení{SP}", - "hud.skill.bow_charged_drain_title" : "Spotřeba během Nabijení", - "hud.skill.bow_charged_drain" : "Snižuje spotřebu nabití o 15%{SP}", - "hud.skill.bow_charged_damage_title" : "Nabitý Poškození", - "hud.skill.bow_charged_damage" : "Zvyšuje Poškození o 20%{SP}", - "hud.skill.bow_energy_regen_title" : "Regenerace Energie", - "hud.skill.bow_energy_regen" : "Zvyšuje zisk Energie o 40%{SP}", - "hud.skill.bow_title" : "Výstřel Šípu", - "hud.skill.bow" : "Nekoneční toulec, nepatří do ruky dětem", - "hud.skill.bow_damage_title" : "Poškození", - "hud.skill.bow_damage" : "Zvyšuje poškození o 25%{SP}", - // Hammer - "hud.skill.hmr_leap_radius_title" : "Rádius Vyšlehnutí", - "hud.skill.hmr_leap_radius" : "Zvětšuje rádius boucnutí do země by 1 meter{SP}", - "hud.skill.hmr_leap_distance_title" : "Vzdálenost Vyšlehnutí", - "hud.skill.hmr_leap_distance" : "Zvětší vzdálenost o 25%{SP}", - "hud.skill.hmr_leap_cost_title" : "Spotřeba při Vyšlehnutí", - "hud.skill.hmr_leap_cost" : "Sníží cenu skoku o 25%{SP}", - "hud.skill.hmr_leap_knockback_title" : "Odražení", - "hud.skill.hmr_leap_knockback" : "Zvyšuje odražení z Vyšlehnutí o 50%{SP}", - "hud.skill.hmr_leap_damage_title" : "Poškození Vyšlehnutí", - "hud.skill.hmr_leap_damage" : "Zvyšuje poškození při Vyšlehnutí 40%{SP}", - "hud.skill.hmr_unlock_leap_title" : "Odemknout Vyšlehnutí", - "hud.skill.hmr_unlock_leap" : "Unlocks a leap{SP}", - "hud.skill.hmr_charged_melee_title" : "Nabitý Rozkmit", - "hud.skill.hmr_charged_melee" : "Útok na blízko, ale silnější", - "hud.skill.hmr_charged_rate_title" : "Rychlost Nabití", - "hud.skill.hmr_charged_rate" : "Zvýší rychlost nabití o 25%{SP}", - "hud.skill.hmr_charged_melee_nrg_drain_title" : "Menší Spotřeba Energie", - "hud.skill.hmr_charged_melee_nrg_drain" : "Snižuje spotřebu energie během Srážky o 25%{SP}", - "hud.skill.hmr_charged_melee_damage_title" : "Poškození", - "hud.skill.hmr_charged_melee_damage" : "Zvětší poškození nabitého rozkmitu o 25%{SP}", - "hud.skill.hmr_charged_melee_knockback_title" : "Knockback", - "hud.skill.hmr_charged_melee_knockback" : "Masivně zvětšuje knockback Rozkmitu o 50%{SP}", - "hud.skill.hmr_single_strike_title" : "Jednoduchý Úder", - "hud.skill.hmr_single_strike" : "Jednoduchý jako ty", - "hud.skill.hmr_single_strike_regen_title" : "Regenerace Výdrže", - "hud.skill.hmr_single_strike_regen" : "Zvyšuje Výdrž při každém povedeném úderu{SP}", - "hud.skill.hmr_single_strike_speed_title" : "Rychlost Jednoduchého Útoku", - "hud.skill.hmr_single_strike_speed" : "Zvýší rychlost útoku pro Jednoduchý Útok při trefě{SP}", - "hud.skill.hmr_single_strike_damage_title" : "Poškození Jednoduchého Útoku", - "hud.skill.hmr_single_strike_damage" : "Při každém povedeném úderu zvyšuje Poškození{SP}", - "hud.skill.hmr_single_strike_knockback_title" : "Odražení pomocí Jednoduchého Útoku", - "hud.skill.hmr_single_strike_knockback" : "Zvyšuje potencial odhození o 50%{SP}", - "hud.skill." : "", - // Sword - "hud.skill.sw_trip_str_title": "Trojtý Úder", - "hud.skill.sw_trip_str": "Udeříš až 3x", - "hud.skill.sw_trip_str_combo_title": "Kombo Trojtého Úderu", - "hud.skill.sw_trip_str_combo": "Odemkne škálování kombíček na trojtým útoku{SP}", - "hud.skill.sw_trip_str_dmg_title": "Poškození Trojtým Úderem", - "hud.skill.sw_trip_str_dmg": "Zvyšuje poškození při každém podařeném úderu{SP}", - "hud.skill.sw_trip_str_sp_title": "Rychlost Trojtého Úderu", - "hud.skill.sw_trip_str_sp": "Zvýší rychlost útoku při každém úspešném úderu{SP}", - "hud.skill.sw_trip_str_reg_title": "Regenerace při Trojtém Úderu", - "hud.skill.sw_trip_str_reg": "Regeneruje Výdrž při každém úspešném úderu{SP}", - "hud.skill.sw_dash_title": "Dash", - "hud.skill.sw_dash": "Proběhni přes nepřátele", - "hud.skill.sw_dash_dmg_title": "Poškození Dashem", - "hud.skill.sw_dash_dmg": "Zvýší poškození o 20%{SP}", - "hud.skill.sw_dash_drain_title": "Menší spotřeba při Dashy", - "hud.skill.sw_dash_drain": "Sníží průběžnou spotřebu energie o 25%{SP}", - "hud.skill.sw_dash_cost_title": "Cena Dashe ", - "hud.skill.sw_dash_cost": "Sníží celkovou spotřebu energie o 25%{SP}", - "hud.skill.sw_dash_speed_title": "Rychlost Dashe", - "hud.skill.sw_dash_speed": "Zvýší rychlost při Dashování o 30%{SP}", - "hud.skill.sw_dash_inf_title": "Nekoneční Dash", - "hud.skill.sw_dash_inf": "Nechá tě používát Dash nekonečně dlouho, než ti dojde energie{SP}", - "hud.skill.sw_dash_scale_title": "Škálování Poškození při Dashy", - "hud.skill.sw_dash_scale": "Zvýší škálování poškození o 20%{SP}", - "hud.skill.sw_spin_title": "Odemknout Roztočení", - "hud.skill.sw_spin": "Odemkne roztočení{SP}", - "hud.skill.sw_spin_dmg_title": "Poškození Roztočení", - "hud.skill.sw_spin_dmg": "Zvýší poškození, které udělíš o 40%{SP}", - "hud.skill.sw_spin_spd_title": "Rychlost Roztočení", - "hud.skill.sw_spin_spd": "Zvýší rychlost roztočení o 25%{SP}", - "hud.skill.sw_spin_cost_title": "Cena Roztočení", - "hud.skill.sw_spin_cost": "Sníží cenu roztočení o 25%{SP}", - "hud.skill.sw_spin_spins_title": "Počet točení při Roztočení", - "hud.skill.sw_spin_spins": "Zvýší počet otoček během roztočení{SP}", - "hud.skill.sw_interrupt_title": "Překažení Útoků", - "hud.skill.sw_interrupt": "Dokáže ihned přerušit útok dalším útokem{SP}", - // Axe - "hud.skill.axe_double_strike_title": "Dvojtý Úder", - "hud.skill.axe_double_strike": "Skácej své nepřátele", - "hud.skill.axe_double_strike_combo_title": "Kombo Dvojtého Úderu", - "hud.skill.axe_double_strike_combo": "Odemkne Dvojtý Útok{SP}", - "hud.skill.axe_double_strike_damage_title": "Poškození Dvojtého Úderu", - "hud.skill.axe_double_strike_damage": "Zvyšuje poškození při každém podařeném úderu{SP}", - "hud.skill.axe_double_strike_speed_title": "Rychlost Dvojtého Úderu", - "hud.skill.axe_double_strike_speed": "Zvyšuje Rychlost Útoku při každém podařeném úderu{SP}", - "hud.skill.axe_double_strike_regen_title": "Regenerace při Dvojtém Útoku", - "hud.skill.axe_double_strike_regen": "Zvyšuje regeneraci Výdrže při každém podařeném úderu{SP}", - "hud.skill.axe_spin_title": "Roztočení Sekery", - "hud.skill.axe_spin": "Točíš se správným směrem...", - "hud.skill.axe_infinite_axe_spin_title": "Nekonečné Roztočení Sekery", - "hud.skill.axe_infinite_axe_spin": "Toč Sekeru dokaď máš Energii{SP}", - "hud.skill.axe_spin_damage_title": "Poškození Roztočení", - "hud.skill.axe_spin_damage": "Zvýší poškození, které udělíš o 30%{SP}", - "hud.skill.axe_spin_helicopter_title": "Vrtulníček", - "hud.skill.axe_spin_helicopter": "Při pádu budeš padat pomaleji při Roztočení Sekery{SP}", - "hud.skill.axe_spin_speed_title": "Rychlost Roztočení", - "hud.skill.axe_spin_speed": "Zvýší Rychlost Roztočení o 25%{SP}", - "hud.skill.axe_spin_cost_title": "Cena Roztočení", - "hud.skill.axe_spin_cost": "Sníží spotřebu výdrže o 25%{SP}", - "hud.skill.axe_unlock_leap_title": "Odemknout Vyšlehnutí", - "hud.skill.axe_unlock_leap": "Odemkne Vyšlehnutí{SP}", - "hud.skill.axe_leap_damage_title": "Poškození při Vyšlehnutí", - "hud.skill.axe_leap_damage": "Zvýší poškození Vyšlehnutí o 35%{SP}", - "hud.skill.axe_leap_knockback_title": "Odražení při Vyšlehnutí", - "hud.skill.axe_leap_knockback": "Při Vyšlehnutí, zvýší odražení o 40%{SP}", - "hud.skill.axe_leap_cost_title": "Cena Vyšlehnutí", - "hud.skill.axe_leap_cost": "Sníží cenu vyšlehnutí o 25%{SP}", - "hud.skill.axe_leap_distance_title": "Dosah Vyšlehnutí", - "hud.skill.axe_leap_distance": "Zvýší dosah o 20%{SP}", - - - // crafting - "hud.crafting": "Výroba", - "hud.crafting.recipes": "Recepty", - "hud.crafting.ingredients": "Ingredience:", - "hud.crafting.craft": "Vyrobit", - "hud.crafting.tool_cata": "Potřeba:", - - // Group - - "hud.group": "Skupina", - "hud.group.invite_to_join": "[{name}] tě pozval do skupiny!", - "hud.group.invite_to_trade": "[{name}] by chtěl s tebou obchodovat.", - "hud.group.invite": "Pozvat", - "hud.group.kick": "Vyhodit", - "hud.group.assign_leader": "Nastavit Vůdcem", - "hud.group.leave": "Opustit Skupinu", - "hud.group.dead" : "Mrtvý", - "hud.group.out_of_range": "Nedosažitelný", - "hud.group.add_friend": "Přidat Přítele", - "hud.group.link_group": "Propojit Skupiny", - "hud.group.in_menu": "V Menu", - "hud.group.members": "Členové Skupiny", - - - - - // Settings - "hud.settings.general": "Obecné", - "hud.settings.none": "Nic", - "hud.settings.press_behavior.toggle": "Přepnout", - "hud.settings.press_behavior.hold": "Podržet", - "hud.settings.help_window": "Okno nápovědy", - "hud.settings.debug_info": "Ladící informace", - "hud.settings.tips_on_startup": "Rady", - "hud.settings.ui_scale": "Velikost UI", - "hud.settings.relative_scaling": "Relativní škálování", - "hud.settings.custom_scaling": "Vlastní škálování", - "hud.settings.crosshair": "Zaměřování", - "hud.settings.transparency": "Průhlednost", - "hud.settings.hotbar": "Hotbar", - "hud.settings.toggle_shortcuts": "Povolit Zkratky", - "hud.settings.buffs_skillbar": "Buffy u Skillbaru", - "hud.settings.buffs_mmap": "Buffy u Minimapy", - "hud.settings.toggle_bar_experience": "Povolit Zkušenostní Bar", - "hud.settings.scrolling_combat_text": "Létajíci Combat Text", - "hud.settings.single_damage_number": "Jednotná čísla Poškození", - "hud.settings.cumulated_damage": "Seskupený Poškození", - "hud.settings.incoming_damage": "Příchozí Poškození", - "hud.settings.cumulated_incoming_damage": "Seskupený Příchozí Poškození", - "hud.settings.speech_bubble": "Povídací Bublina", - "hud.settings.speech_bubble_dark_mode": "Tmavá Povídací Bublina", - "hud.settings.speech_bubble_icon": "Ikona Povídací Bubliny", - "hud.settings.energybar_numbers": "Čísla u Baru Energie", - "hud.settings.values": "Hodnoty", - "hud.settings.percentages": "Procenta", - "hud.settings.chat": "Chat", - "hud.settings.background_transparency": "Průhlednost Pozadí", - "hud.settings.chat_character_name": "Jména Postav v Chatu", - "hud.settings.loading_tips": "Rady v Načítání", - "hud.settings.reset_interface": "Výchozí Nastavení", - - "hud.settings.pan_sensitivity": "Citlivost Rozhledu", - "hud.settings.zoom_sensitivity": "Citlivost Přiblížení", - "hud.settings.camera_clamp_angle": "Úhel pro vertikální režim upnutí kamery", - "hud.settings.invert_scroll_zoom": "Invertovat rolování zvětšení", - "hud.settings.invert_mouse_y_axis": "Obrátit osu Y myši", - "hud.settings.invert_controller_y_axis": "Obrátit osu Y ovladače", - "hud.settings.enable_mouse_smoothing": "Vyhlazení kamery", - "hud.settings.free_look_behavior": "Chování volného pohledu", - "hud.settings.auto_walk_behavior": "Chování Auto chůze", - "hud.settings.camera_clamp_behavior": "Chování připnuté kamery", - "hud.settings.stop_auto_walk_on_input": "Přestat Auto chodit při pohybu", - "hud.settings.reset_gameplay": "Výchozí Nastavení", - - "hud.settings.view_distance": "Vykreslovací Vzdálenost", - "hud.settings.sprites_view_distance": "Vykreslovací Vzdálenost Spritů", - "hud.settings.figures_view_distance": "Vykreslovací Vzdálenost Entit", - "hud.settings.maximum_fps": "Maximum FPS", - "hud.settings.fov": "Úhel pohledu (stupně)", - "hud.settings.gamma": "Gama", - "hud.settings.exposure": "Expozice", - "hud.settings.ambiance": "Jas prostředí", - "hud.settings.antialiasing_mode": "Režim AntiAliasing", - "hud.settings.upscale_factor": "Interní Rozlišení", - "hud.settings.cloud_rendering_mode": "Vykreslování Mraků", - "hud.settings.fluid_rendering_mode": "Vykreslování Vody", - "hud.settings.fluid_rendering_mode.cheap": "Levný", - "hud.settings.fluid_rendering_mode.shiny": "Blíštivý", - "hud.settings.cloud_rendering_mode.minimal": "Minimalní", - "hud.settings.cloud_rendering_mode.low": "Nizký", - "hud.settings.cloud_rendering_mode.medium": "Střední", - "hud.settings.cloud_rendering_mode.high": "Vysoký", - "hud.settings.cloud_rendering_mode.ultra": "Ultra", - "hud.settings.fullscreen": "Celá Obrazovka", - "hud.settings.fullscreen_mode": "Režim Celé obrazovky", - "hud.settings.fullscreen_mode.exclusive": "Exkluzivní", - "hud.settings.fullscreen_mode.borderless": "Bezokrajový", - "hud.settings.particles": "Částice", - "hud.settings.resolution": "Rozlišení", - "hud.settings.bit_depth": "Bitová Hloubka", - "hud.settings.refresh_rate": "Obnovovací Frekvence", - "hud.settings.lighting_rendering_mode": "Vykreslování Osvětlení", - "hud.settings.lighting_rendering_mode.ashikhmin": "Typ A - Vysoký ", - "hud.settings.lighting_rendering_mode.blinnphong": "Typ B - Střední", - "hud.settings.lighting_rendering_mode.lambertian": "Typ L - Levný", - "hud.settings.shadow_rendering_mode": "Vykreslování Stínů", - "hud.settings.shadow_rendering_mode.none": "Nic", - "hud.settings.shadow_rendering_mode.cheap": "Levný", - "hud.settings.shadow_rendering_mode.map": "Mapa", - "hud.settings.shadow_rendering_mode.map.resolution": "Rozlišení", - "hud.settings.lod_detail": "LoD Detail", - "hud.settings.save_window_size": "Pamatovat Okno", - "hud.settings.reset_graphics": "Výchozí Nastavení", - - - "hud.settings.music_volume": "Hlasitost Hudby", - "hud.settings.sound_effect_volume": "Hlasitost Efektů", - "hud.settings.audio_device": "Vstupní Zařízení", - "hud.settings.reset_sound": "Výchozí Nastavení", - - "hud.settings.awaitingkey": "Zmáčkni klávesu...", - "hud.settings.unbound": "Žádná", - "hud.settings.reset_keybinds": "Výchozí", - - /// Socialní - - "hud.social": "Společnost", - "hud.social.online": "Aktivní", - "hud.social.friends": "Přátelé", - "hud.social.not_yet_available": "Ještě není dostupné", - "hud.social.faction": "Cech", - "hud.social.play_online_fmt": "{nb_player} je aktivní", - "hud.social.name": "Jméno", - "hud.social.level": "Úroveň", - "hud.social.zone": "Zóna", - "hud.social.account": "Účet", - - /// Obchod - - "hud.trade.trade_window": "Obchod", - "hud.trade.phase1_description": "Přetáhněte položky.", - "hud.trade.phase2_description": "Obchod je nyní uzamčen, abyste měli\n čas jej zkontrolovat.", - /// Phase3 should only be visible for a few milliseconds if everything is working properly, but is included for completeness - "hud.trade.phase3_description": "Probíhá zpracování.", - "hud.trade.persons_offer": "Nabídka {playername}", - "hud.trade.has_accepted": "{playername}přijal obchod", - "hud.trade.accept": "Akceptovat", - "hud.trade.decline": "Odmítnout", - "hud.trade.invite_sent": "Žádost o obchod zaslána {playername}.", - "hud.trade.result.completed": "Obchod proběhl úspěšně.", - "hud.trade.result.declined": "Obchod odmítnut.", - "hud.trade.result.nospace": "Nedostatek místa pro dokončení.", - "hud.trade.buy_price": "Cena koupě", - "hud.trade.sell_price": "Cena prodeje", - "hud.trade.coin": "Peníze", - - - /// misc - - "hud.do_not_show_on_startup": "Nezobrazovat toto při spuštění", - "hud.show_tips": "Zobrazit Rady", - "hud.quests": "Úkoly", - "hud.you_died": "Umřel si", - "hud.waypoint_saved": "Waypoint uložen", - "hud.sp_arrow_txt": "SP", - - "hud.press_key_to_show_keybindings_fmt": "[{key}] Ovládání", - "hud.press_key_to_toggle_lantern_fmt": "[{key}] Lampa", - "hud.press_key_to_show_debug_info_fmt": "Zmáčkni {key} pro Ladící Menu", - "hud.press_key_to_toggle_keybindings_fmt": "Zmáčkni {key} pro Hastavení Ovládání", - "hud.press_key_to_toggle_debug_info_fmt": "Zmáčkni {key} pro Ladící Informace", - - // Respawn message - "hud.press_key_to_respawn": r#"Zmáčkni {key} pro oživení u posledního navštíveného ohně."#, - - // Tutorial Button - "hud.tutorial_btn": r#"Tutorial"#, - "hud.tutorial_click_here": r#"Zmáčkni [ {key} ] pro odemčení kurzoru a zmáčkni na toto tlačítko"#, - "hud.tutorial_elements": r#"Výroba"#, - -"hud.temp_quest_headline": r#"vítej Poutníku"#, -"hud.temp_quest_text": r#"Na začátek své cesty bys ses mohl prohledat tuto vesnici a sbírat zásoby. - -Je ti dovoleno vzít si to co potřebuješ na svou cestu! - -V pravém dolním rohu obrazovky najdete různé věci, jako je taška, nabídka tvorby a mapa. - -Nabídka Tvorby vám umožní vytvářet brnění, zbraně, jídlo a mnoho dalšího! - -Divoká zvířata po celém městě jsou skvělým zdrojem koženého odpadu, který vytváří určitou ochranu před nebezpečím tam venku. - -Kdykoli se budeš cítit připraven, pokus se získat ještě lepší vybavení z mnoha výzev vyznačených na mapě! -"#, - - "hud.spell": "Dovednosti", - // Diary - "hud.diary": "Deník", - - "hud.free_look_indicator": "Volný pohled je povolený. Můžeš ho vypnout pomocí {key} .", - "hud.camera_clamp_indicator": "Vertikální upnutá kamera je zaplá, Můžeš to vypnout pomocí {key}", - "hud.auto_walk_indicator": "Auto Chůze je zaplá", - - // SCT outputs - "hud.sct.experience": "{amount} Zkušeností", - "hud.sct.block": "BLOKOVÁNO", - - /// End HUD section - - - // Buffs - "buff.remove": "Klikni pro zrušení", - "buff.title.missing": "Chybějící název", - "buff.desc.missing": "Chybějící popis", - "buff.title.heal": "Léčba", - "buff.desc.heal": "Přídá život během určitého času.", - "buff.title.potion": "Lektvar", - "buff.desc.potion": "Piju...", - "buff.title.saturation": "Nasycení", - "buff.desc.saturation": "Přídá život během času ze Spotřebních.", - "buff.title.campfire_heal": "Léčba Táborákem", - "buff.desc.campfire_heal": "Odpočinek u ohně léčí 1% za sekundu.", - "buff.title.invulnerability": "Nezranitelnost", - "buff.desc.invulnerability": "Žádný útok tě nezraní.", - "buff.title.protectingward": "Ochraná Vizita", - "buff.desc.protectingward": "Jsi chráněn, nějak, před útoky.", - // Debuffs - "buff.title.bleed": "Krvácení", - "buff.desc.bleed": "Způsobuje pravidelné poškození.", - "buff.title.cursed": "Prokletí", - "buff.desc.cursed": "Jsi prokletý.", - // Buffs stats - "buff.stat.health": "Obnoví {str_total} Životů", - "buff.stat.increase_max_stamina": "Zvedne Maximalní Výdrž o {strength}", - "buff.stat.increase_max_health": "Zvedne Maximální počet životů o {strength}", - "buff.stat.invulnerability": "Zaručuje Nezranitelnost", - // Text - "buff.text.over_seconds": "více než {dur_secs} sekund", - "buff.text.for_seconds": "po dobu {dur_secs} sekund", - - /// Start GameInput section - - "gameinput.primary": "Základní Útok", - "gameinput.secondary": "Druhý Útok/Blok/Míření", - "gameinput.slot1": "Hotbar Slot 1", - "gameinput.slot2": "Hotbar Slot 2", - "gameinput.slot3": "Hotbar Slot 3", - "gameinput.slot4": "Hotbar Slot 4", - "gameinput.slot5": "Hotbar Slot 5", - "gameinput.slot6": "Hotbar Slot 6", - "gameinput.slot7": "Hotbar Slot 7", - "gameinput.slot8": "Hotbar Slot 8", - "gameinput.slot9": "Hotbar Slot 9", - "gameinput.slot10": "Hotbar Slot 10", - "gameinput.swaploadout": "Výměna Vybavení", - "gameinput.togglecursor": "Povolit Kurzor", - "gameinput.help": "Povolit Okno Nápovědy", - "gameinput.toggleinterface": "Povolit Rozhraní", - "gameinput.toggledebug": "Povolit FPS a Debug info", - "gameinput.screenshot": "Pořídit Snímek Obrazovky", - "gameinput.toggleingameui": "Povolit Jména", - "gameinput.fullscreen": "Povolit Celou Obrazovku", - "gameinput.moveforward": "Pohyb Dopředu", - "gameinput.moveleft": "Pohyb Doleva", - "gameinput.moveright": "Pohyb Doprava", - "gameinput.moveback": "Pohyb Dozádu", - "gameinput.jump": "Skok", - "gameinput.glide": "Kluzák", - "gameinput.roll": "Kotrmelec", - "gameinput.climb": "Lézt", - "gameinput.climbdown": "Lézt Dolů", - "gameinput.wallleap": "Wall Leap", - "gameinput.togglelantern": "Lucerna", - "gameinput.mount": "Nasednout", - "gameinput.chat": "Chat", - "gameinput.command": "Príkaz", - "gameinput.escape": "Nabídka", - "gameinput.map": "Mapa", - "gameinput.bag": "Taška", - "gameinput.trade": "Obchod", - "gameinput.social": "Společenost", - "gameinput.sit": "Sednout", - "gameinput.spellbook": "Dovednosti", - "gameinput.settings": "Nastavení", - "gameinput.respawn": "Oživení", - "gameinput.charge": "Charge", - "gameinput.togglewield": "Vzít zbraň do ruky", - "gameinput.interact": "Interagovat", - "gameinput.freelook": "Volný Pohled", - "gameinput.autowalk": "Auto Chůze", - "gameinput.cameraclamp": "Připnutí Kamery", - "gameinput.dance": "Tanec", - "gameinput.select": "Vybrat Entitu", - "gameinput.acceptgroupinvite": "Příjmout pozvání do Skupiny", - "gameinput.declinegroupinvite": "Odmítnout pozvání do Skupiny", - "gameinput.cyclecamera": "Druhy kamer", - "gameinput.crafting": "Tvorba", - "gameinput.fly": "Létání", - "gameinput.sneak": "Plížení", - "gameinput.swimdown": "Plavat Dolů", - "gameinput.swimup": "Plavat Nahoru", - - - /// End GameInput section - - - /// Start chracter selection section - "char_selection.loading_characters": "Načítání postavy...", - "char_selection.delete_permanently": "Chcete smazat tuto postavu?", - "char_selection.deleting_character": "Probíhá mazání postavy...", - "char_selection.change_server": "Změnit server", - "char_selection.enter_world": "Vstup do světa", - "char_selection.logout": "Odhlásit", - "char_selection.create_new_character": "Nová Postava", - "char_selection.creating_character": "Probíhá tvorba nové postavy", - "char_selection.character_creation": "Tvůrce postavy", - "char_selection.human_default": "Výchozí postava", - "char_selection.level_fmt": "úroveň {level_nb}", - "char_selection.uncanny_valley": "Divočina", - "char_selection.plains_of_uncertainty": "Pole nejistoty", - "char_selection.beard": "Brada", - "char_selection.hair_style": "Styl vlasů", - "char_selection.hair_color": "Barva vlasů", - "char_selection.chest_color": "Barva hrudníku", - "char_selection.eye_color": "Barva očí", - "char_selection.skin": "Kůže", - "char_selection.eyebrows": "Obočí", - "char_selection.accessories": "Doplňky", - "char_selection.create_new_charater": "Nová postava", - "char_selection.eyeshape": "Tvar očí", - "char_selection.create_info_name": "Tvá postava potřebuje jméno!", - - /// End chracter selection section - - - /// Start character window section - "character_window.character_name": "Jméno postavy", - // Charater stats - "character_window.character_stats": r#"Síla - -Zdatnost - -Síla vůle -"#, - - - /// ???Start character window section - - - /// Start Escape Menu Section - "esc_menu.logout": "Odhlásit", - "esc_menu.quit_game": "Opustit hru", - /// End Escape Menu Section - }, - - vector_map: { -"loading.tips": [ - "Stisknutím G rozsvítíš svou lucernu.", - "Stisknutím klávesy 'F1' zobrazíš všechny výchozí klávesy.", - "Můžetš použít /say nebo /s pro chat pouze s hráči přímo kolem vás.", - "Můžeš napsat /region nebo /r pro chat s hráči pát stovek bloků od vás.", - "Admini mohou napsat /build příkaz pro spuštění Stavívího Módu.", - "Můžeš napsat /group nebo /g pro chat s vaší Skupinou.", - "Pro posílání privátních zpráv napiš /tell poté jméno hráce a zprávu.", - "Dávej si pozor na jídlo, truhly a jiný kořisti rozmístěné po celém světě!", - "Inventář plný jídla? Zkus vytvořit z toho lepší jídlo!", - "Přemýšlíš co dělat? Zkus nějakou Kobku označenou na mapě!", - "Nezapomeň si nastavit Grafiku pro svůj systém. Klávesou 'N' otevřeš Nastavení.", - "Hraní s hráči je zábava! Klávesou 'O' se podívej kdo je Online.", - "Klávesou 'J' začneš Tancovat. Párty!", - "Klávesou 'L-Shift' otevřeš Kluzák a můžeš dobýt nebesa.", - "Veloren je stále v Pre-Alpha. Snažíme se hru zlepšit co to jde!", - "Jestli se chceš napojit k dev teamu nebo jen si napsat, připoj se na náš Discord server.", - "Můžeš povolit zobrazení tvého životu v healthbaru v Nastavení.", - "Sedni si k Táboráku (Klávesa 'K') pro pomalé léčení.", - "Potřebuješ více tašek, či lepší zbroj pro vaše dobrodrušství? Klávesou 'C' otevřeš nabídku Tvorby!", - ], - "npc.speech.villager": [ - "Není dnes tak překrásný den?", - "Jak se dneska máš?", - "Dobré ráno!", - "Zajímalo by mě, co si Catoblepové myslí, když jí trávu.", - "Co si myslíš o tomto počasí?", - "Přemýšlení o těchto kobkách mě děsí. Doufám, že je někdo vyčistí.", - "Rád bych šel prozkoumávat jeskyni, až budu silnější.", - "Neviděl si moji kočku?", - "Už si někdy slyšel o divokých Pozemních Žralocích? Slyšel jsem, že žijí v pouštích.", - "Říká se, že v jeskyních se nacházejí lesklé drahokamy všeho druhu.", - "Jsem jen o sýrovích šušenkách!", - "Nepůjdeš dovnitř? Právě jsem si chtěl dát sýr!", - "Říká se, že houby jsou dobré pro vaše zdraví. Nikdy jsem je neměl.", - "Nezapomeňte na sušenky!", - "Prostě zbožňuji trpasličí sýr. Přál bych si, abych ho uměl dělat.", - "Zajímalo by mě, co je na druhé straně hor.", - "Doufám, že si někdy vyrobím vlastní kluzák.", - "Chceš vidět moji zahradu? Dobře, možná někdy jindy.", - "Krásný den na procházku do lesa!", - "Být či nebýt? Myslím, že budu farmář.", - "Nemyslíš si, že naše vesnice je nejlepší?", - "Co podle vás září v Glowing Remains?", - "Myslím, že je čas na druhou snídani!", - "Už si někdy chytil světlušku?", - "Prostě nechápu, odkud ti Saurokové stále přicházejí.", - "Přál bych si, aby někdo držel vlky daleko od vesnice.", - "Minulou noc jsem měl nádherný sen o sýru. Co to znamená?", - "Nechal jsem trochu sýra s bratrem osamotě. Teď nevím, jestli existuje nebo ne. Říkám tomu Schrödingerův sýr.", - "Nechal jsem trochu sýra se setrou osamotě. Teď nevím, jestli existuje nebo ne. Říkám tomu Schrödingerův sýr.", - "Někdo by měl s těmi kultisty něco udělat. Nejlepší kdyý já ne.", - "Doufám, že brzy bude pršet. Bylo by to dobré pro plodiny.", - "Miluji med! A nesnáším včely.", - "Chci jednoho dne vidět svět. Musí tu být více života než v této vesnici.", - ], - "npc.speech.villager_cultist_alarm": [ - "Bacha! Je tu kultista!", - "Do zbroje! Kultisti útočí!", - "Jak se Kultisti opovážli útočit na naší vesnici!", - "Smrt kultistům!", - "Kultisti tu nejsou tolerováni!", - "Vražední Kultisti!", - "Ochutnej mojí čepel Kultisto", - "Nic nedokáže vyčistit krev na tvých rukách Kultisto!", - "Billions of blistering blue barnacles! A cultist among us!", - "Zlo tohoto Kultisty je ukonce!", - "Tento Kultista je můj!", - "Připrav se potkat svého Stvořitele blbý Kultisto!", - "Vidím Kultistu! Na něj!", - "Vidím Kultistu! Útok!", - "Vidím Kultistu! Nenech je utéct!", - "Uvažoval někdy úctiví Kultitsta o SMRŤI?!", - "Nikdy neodpustit! Nikdy nezapomenout! Kultista bude pikat!", - "Umři Kultisto!", - "Tvů začátek chaosu bude u konce!", - "Tady je vše za to, co si udělal!", - "Nejsme moc přátelští k lidem tvého druhu.", - "Měl si zůstat v podzemí!", - ], - "npc.speech.villager_under_attack": [ - "Pomoc, jsem pod útokem!", - "Pomoc! Jsem pod útokem!", - "Au! Jsem pod útokem!", - "Auh! Jsem pod útokem! Pomoc!", - "Pomoc! Jsem pod útokem!", - "Jsem pod útokem, Pomoc!", - "Jsem pod útokem! Pomoc!", - "Pomoc!", - "Pomoc! Pomoc!", - "Pomoc! Pomoc! Pomoc!", - "Jsem pod útokem!", - "AAAHHH! Jsem pod útokem!", - "AAAHHH! Jsem pod útokem! Pomoc!", - "Pomoc! Jseme pod útokem!", - "Pomoc! Vrah!", - "Pomoc! Je tu vrah!", - "Pomoc! Snaží se mě zabít!", - "Stráže, Jsem pod útokem!", - "Stráže! Jsem pod útokem!", - "Jsem pod útokem! Stráže!", - "Pomoc! Stráže! Jsem pod útokem!", - "Stráže! Rychle!", - "Stráže! Stráže!", - "Stráže! Padouch na mě útočí!", - "Stráže, zabte tohoto padoucha!", - "Stráže! Je tu vrah!", - "Stráže! Pomoc!", - "S tím neutečeš jen tak! Stráže!", - "Příteli!", - "Pomoc !", - "Pomoc! Prosím!", - "Ouch! Stráže! Pomoc!", - "Jdou po mě!", - "Pomoc! Pomoc! Snaží se mě utlačit!", - "Ah, teď vidíme násilí v systému", - "Tohle je jen škrábnutí!", - "Nech toho!", - "Co jsem ti provedl?!", - "Prosím přestaň na mě útočit!", - "Hey! Dávej bacha co s tím děláš!", - "Hajze, zmiz!", - "Dost! Jdi do háje!", - "Teď mě štveš!", - "Oi! Co si myslíš že jsi?!", - "Tak teď chci tvojí hlavu!", - "Přestaň, Prosím! Nemám nic u sebe ceného!", - "Pošlu na tebe bratra, je větší než já!", - "Neeee, já půjdu žalovat matce!", - "Proklínám tě!", - "Prosím nedělej to.", - "Toto nebylo pěkné!", - "Tvá zbraň funguje, teď vypadni", - "Ušetři mě!", - "Prosím, mám rodinu!", - "Jsem moc mladý, abych umřel!", - "Můžeme si o tom promluvit?", - "Násilí není nikdy odpověď!", - "Dnešek bude pěkně na nic...", - "Hey, to bolí!", - "Eek!", - "Jak hnusný!", - "Stop, prosím tě!", - "A pox upon you!", - "Tohle není sranda.", - "Jak se opovažuješ?!", - "Za to zaplatíš!", - "Ještě chvilku porkačuj a budeš toho litovat!", - "Nechtěj, abych tě zranil!", - "To musí být omyl!", - "Nepotřebuješ toto dělat!", - "Vypdni kamaráda!", - "To opravdu bolelo!", - "Proč bys to dělal?", - "ve jménu ducha svatého, vypadni!", - "Musel si se splést!", - "To si nezasloužím!", - "Prosím, nedělej to znova.", - "Stráže, hoďte toto monstrum do jezera!", - "vypustím na tebe Tarasque!", - "Proč jáááá?", - ], - - }, + } ) diff --git a/assets/voxygen/i18n/cz_CZ/buff.ron b/assets/voxygen/i18n/cz_CZ/buff.ron new file mode 100644 index 0000000000..a9e2d9cc8f --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/buff.ron @@ -0,0 +1,40 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + // Buffs + "buff.remove": "Klikni pro zrušení", + "buff.title.missing": "Chybějící název", + "buff.desc.missing": "Chybějící popis", + "buff.title.heal": "Léčba", + "buff.desc.heal": "Přídá život během určitého času.", + "buff.title.potion": "Lektvar", + "buff.desc.potion": "Piju...", + "buff.title.saturation": "Nasycení", + "buff.desc.saturation": "Přídá život během času ze Spotřebních.", + "buff.title.campfire_heal": "Léčba Táborákem", + "buff.desc.campfire_heal": "Odpočinek u ohně léčí 1% za sekundu.", + "buff.title.invulnerability": "Nezranitelnost", + "buff.desc.invulnerability": "Žádný útok tě nezraní.", + "buff.title.protectingward": "Ochraná Vizita", + "buff.desc.protectingward": "Jsi chráněn, nějak, před útoky.", + // Debuffs + "buff.title.bleed": "Krvácení", + "buff.desc.bleed": "Způsobuje pravidelné poškození.", + "buff.title.cursed": "Prokletí", + "buff.desc.cursed": "Jsi prokletý.", + // Buffs stats + "buff.stat.health": "Obnoví {str_total} Životů", + "buff.stat.increase_max_energy": "Zvedne Maximalní Výdrž o {strength}", + "buff.stat.increase_max_health": "Zvedne Maximální počet životů o {strength}", + "buff.stat.invulnerability": "Zaručuje Nezranitelnost", + // Text + "buff.text.over_seconds": "více než {dur_secs} sekund", + "buff.text.for_seconds": "po dobu {dur_secs} sekund", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/char_selection.ron b/assets/voxygen/i18n/cz_CZ/char_selection.ron new file mode 100644 index 0000000000..7d9dd2ab01 --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/char_selection.ron @@ -0,0 +1,34 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + "char_selection.loading_characters": "Načítání postavy...", + "char_selection.delete_permanently": "Chcete smazat tuto postavu?", + "char_selection.deleting_character": "Probíhá mazání postavy...", + "char_selection.change_server": "Změnit server", + "char_selection.enter_world": "Vstup do světa", + "char_selection.logout": "Odhlásit", + "char_selection.create_new_character": "Nová Postava", + "char_selection.creating_character": "Probíhá tvorba nové postavy", + "char_selection.character_creation": "Tvůrce postavy", + "char_selection.human_default": "Výchozí postava", + "char_selection.level_fmt": "úroveň {level_nb}", + "char_selection.uncanny_valley": "Divočina", + "char_selection.plains_of_uncertainty": "Pole nejistoty", + "char_selection.beard": "Brada", + "char_selection.hair_style": "Styl vlasů", + "char_selection.hair_color": "Barva vlasů", + "char_selection.chest_color": "Barva hrudníku", + "char_selection.eye_color": "Barva očí", + "char_selection.skin": "Kůže", + "char_selection.eyebrows": "Obočí", + "char_selection.accessories": "Doplňky", + "char_selection.create_new_charater": "Nová postava", + "char_selection.eyeshape": "Tvar očí", + "char_selection.create_info_name": "Tvá postava potřebuje jméno!", + }, + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/common.ron b/assets/voxygen/i18n/cz_CZ/common.ron new file mode 100644 index 0000000000..d1f690bcd0 --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/common.ron @@ -0,0 +1,106 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + // Texts used in multiple locations with the same formatting + "common.username": "jméno", + "common.singleplayer": "Hra jednoho hráče", + "common.multiplayer": "Hra více hráčů", + "common.servers": "Servery", + "common.quit": "Vypnout", + "common.settings": "Nastavení", + "common.languages": "Jazyky", + "common.interface": "Rozhraní", + "common.gameplay": "Hra", + "common.controls": "Ovládání", + "common.video": "Grafika", + "common.sound": "Zvuk", + "common.resume": "Pokračovat", + "common.characters": "Postavy", + "common.close": "Zavřít", + "common.yes": "Ano", + "common.no": "Ne", + "common.back": "Zpět", + "common.create": "Vytvořik", + "common.okay": "OK", + "common.add": "Přidat", + "common.accept": "Příjmout", + "common.decline": "Odmítnout", + "common.disclaimer": "Zřeknutí se odpovědnosti", + "common.cancel": "Zrušit", + "common.none": "Nic", + "common.error": "Chyba", + "common.fatal_error": "Fatalní Chyba", + "common.you": "ty", + "common.automatic": "Auto", + "common.random": "Náhodný", + "common.empty": "Prázdný", + + // Settings Window title + "common.interface_settings": "Rozhraní", + "common.gameplay_settings": "Hra", + "common.controls_settings": "Ovládání", + "common.video_settings": "Grafika", + "common.sound_settings": "Zvuk", + "common.language_settings": "Jazyk", + + // Message when connection to the server is lost + "common.connection_lost": r#"Spojení ztraceno! + Restartoval se server? + Je klient aktuální?"#, + + + "common.species.orc": "Ork", + "common.species.human": "Člověk", + "common.species.dwarf": "Trpaslík", + "common.species.elf": "Elf", + "common.species.undead": "Nemrtvý", + "common.species.danari": "Danari", + + "common.weapons.axe": "Sekera", + "common.weapons.sword": "Meč", + "common.weapons.staff": "Hůl", + "common.weapons.bow": "Luk", + "common.weapons.hammer": "Kladivo", + "common.weapons.general": "Všeobecný", + "common.weapons.sceptre": "Žezlo", + "common.weapons.shield": "Štít", + "common.weapons.spear": "Kopí", + "common.weapons.hammer_simple": "Jednoduché Kladivo", + "common.weapons.sword_simple": "Jednoduchý Meč", + "common.weapons.staff_simple": "Jednoduchá Hůl", + "common.weapons.axe_simple": "Jednoduchá Sekera", + "common.weapons.bow_simple": "Jednoduchý Luk", + "common.weapons.unique": "Unikátní", + "common.tool.debug": "Debug", + "common.tool.faming": "Farmářský Nástroj", + "common.tool.pick": "Krumpáč", + "common.kind.modular_component": "Modulární komponenta", + "common.kind.glider": "Kluzák", + "common.kind.consumable": "Spotřební", + "common.kind.throwable": "Lze házet", + "common.kind.utility": "Užitečné", + "common.kind.ingredient": "Ingredience", + "common.kind.lantern": "Lampa", + "common.hands.one": "Jednoruční", + "common.hands.two": "Dvojruční", + + "common.rand_appearance": "Náhodný vzhled", + "common.rand_name": "Náhodné Jméno", + + "common.stats.dps": "DPS", + "common.stats.power": "Moc", + "common.stats.speed": "Rychlost", + "common.stats.poise": "Postoj", + "common.stats.crit_chance": "Kritická šance", + "common.stats.crit_mult": "Crit Mult", + "common.stats.armor": "Zbroj", + "common.stats.poise_res": "Postojové res", + "common.stats.slots": "Sloty", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/esc_menu.ron b/assets/voxygen/i18n/cz_CZ/esc_menu.ron new file mode 100644 index 0000000000..f219600d73 --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/esc_menu.ron @@ -0,0 +1,13 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + "esc_menu.logout": "Odhlásit", + "esc_menu.quit_game": "Opustit hru", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/gameinput.ron b/assets/voxygen/i18n/cz_CZ/gameinput.ron new file mode 100644 index 0000000000..2d2e337ef3 --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/gameinput.ron @@ -0,0 +1,70 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + "gameinput.primary": "Základní Útok", + "gameinput.secondary": "Druhý Útok/Blok/Míření", + "gameinput.slot1": "Hotbar Slot 1", + "gameinput.slot2": "Hotbar Slot 2", + "gameinput.slot3": "Hotbar Slot 3", + "gameinput.slot4": "Hotbar Slot 4", + "gameinput.slot5": "Hotbar Slot 5", + "gameinput.slot6": "Hotbar Slot 6", + "gameinput.slot7": "Hotbar Slot 7", + "gameinput.slot8": "Hotbar Slot 8", + "gameinput.slot9": "Hotbar Slot 9", + "gameinput.slot10": "Hotbar Slot 10", + "gameinput.swaploadout": "Výměna Vybavení", + "gameinput.togglecursor": "Povolit Kurzor", + "gameinput.help": "Povolit Okno Nápovědy", + "gameinput.toggleinterface": "Povolit Rozhraní", + "gameinput.toggledebug": "Povolit FPS a Debug info", + "gameinput.screenshot": "Pořídit Snímek Obrazovky", + "gameinput.toggleingameui": "Povolit Jména", + "gameinput.fullscreen": "Povolit Celou Obrazovku", + "gameinput.moveforward": "Pohyb Dopředu", + "gameinput.moveleft": "Pohyb Doleva", + "gameinput.moveright": "Pohyb Doprava", + "gameinput.moveback": "Pohyb Dozádu", + "gameinput.jump": "Skok", + "gameinput.glide": "Kluzák", + "gameinput.roll": "Kotrmelec", + "gameinput.climb": "Lézt", + "gameinput.climbdown": "Lézt Dolů", + "gameinput.wallleap": "Wall Leap", + "gameinput.togglelantern": "Lucerna", + "gameinput.mount": "Nasednout", + "gameinput.chat": "Chat", + "gameinput.command": "Príkaz", + "gameinput.escape": "Nabídka", + "gameinput.map": "Mapa", + "gameinput.bag": "Taška", + "gameinput.trade": "Obchod", + "gameinput.social": "Společenost", + "gameinput.sit": "Sednout", + "gameinput.spellbook": "Dovednosti", + "gameinput.settings": "Nastavení", + "gameinput.respawn": "Oživení", + "gameinput.charge": "Charge", + "gameinput.togglewield": "Vzít zbraň do ruky", + "gameinput.interact": "Interagovat", + "gameinput.freelook": "Volný Pohled", + "gameinput.autowalk": "Auto Chůze", + "gameinput.cameraclamp": "Připnutí Kamery", + "gameinput.dance": "Tanec", + "gameinput.select": "Vybrat Entitu", + "gameinput.acceptgroupinvite": "Příjmout pozvání do Skupiny", + "gameinput.declinegroupinvite": "Odmítnout pozvání do Skupiny", + "gameinput.cyclecamera": "Druhy kamer", + "gameinput.crafting": "Tvorba", + "gameinput.fly": "Létání", + "gameinput.sneak": "Plížení", + "gameinput.swimdown": "Plavat Dolů", + "gameinput.swimup": "Plavat Nahoru", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/hud/bag.ron b/assets/voxygen/i18n/cz_CZ/hud/bag.ron new file mode 100644 index 0000000000..aa61261425 --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/hud/bag.ron @@ -0,0 +1,39 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + // Inventory + "hud.bag.glider": "Glider", + "hud.bag.bag": "Batoh", + "hud.bag.health": "Život", + "hud.bag.energy": "Výdrž", + "hud.bag.combat_rating": "Bojové hodnocení", + "hud.bag.protection": "Ochrana", + "hud.bag.combat_rating_desc": "Vypočítáno z života/zbroje.", + "hud.bag.protection_desc": "Redukce poškození přes brnení", + "hud.bag.inventory": "Inventář {playername}", + "hud.bag.stats_title": "Statistiky {playername}", + "hud.bag.exp": "Zkušenosti", + "hud.bag.armor": "Výzbroj", + "hud.bag.stats": "Statistiky", + "hud.bag.head": "Hlava", + "hud.bag.neck": "Krk", + "hud.bag.tabard": "Tabard", + "hud.bag.shoulders": "Ramena", + "hud.bag.chest": "Hruď", + "hud.bag.hands": "Ruce", + "hud.bag.lantern": "Lampa", + "hud.bag.belt": "Pas", + "hud.bag.ring": "Prsten", + "hud.bag.back": "Záda", + "hud.bag.legs": "Nohy", + "hud.bag.feet": "Chodidla", + "hud.bag.mainhand": "Hlavní Ruka", + "hud.bag.offhand": "Druhá Ruka", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/hud/char_window.ron b/assets/voxygen/i18n/cz_CZ/hud/char_window.ron new file mode 100644 index 0000000000..f0506e82b6 --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/hud/char_window.ron @@ -0,0 +1,20 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + /// Start character window section + "character_window.character_name": "Jméno postavy", + // Charater stats + "character_window.character_stats": r#"Síla + +Zdatnost + +Síla vůle +"#, + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/hud/chat.ron b/assets/voxygen/i18n/cz_CZ/hud/chat.ron new file mode 100644 index 0000000000..83bb7da283 --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/hud/chat.ron @@ -0,0 +1,38 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + "hud.chat.online_msg": "[{name}] je online", + "hud.chat.offline_msg": "[{name}] šel/šla offline", + + "hud.chat.default_death_msg": "[{name}] zemřel", + "hud.chat.environmental_kill_msg": "[{name}] zemřel v {environment}", + "hud.chat.fall_kill_msg": "[{name}] zemřel z pádu", + "hud.chat.suicide_msg": "[{name}] zemřel díky svému zapříčinění", + + "hud.chat.pvp_buff_kill_msg": "[{victim}] zemřel z {buff} od [{attacker}]", + "hud.chat.pvp_melee_kill_msg": "[{attacker}] porazil [{victim}]", + "hud.chat.pvp_ranged_kill_msg": "[{attacker}] postřelil [{victim}]", + "hud.chat.pvp_explosion_kill_msg": "[{attacker}] odpálil [{victim}]", + "hud.chat.pvp_energy_kill_msg": "[{attacker}] zabil [{victim}] pomocí magie", + + "hud.chat.nonexistent_buff_kill_msg": "[{victim}] zemřel pomocí {buff}", + + "hud.chat.npc_buff_kill_msg": "[{victim}] died of {buff} od {attacker}", + "hud.chat.npc_melee_kill_msg": "{attacker} zabil [{victim}]", + "hud.chat.npc_ranged_kill_msg": "{attacker} postřelil [{victim}]", + "hud.chat.npc_explosion_kill_msg": "{attacker} odpálil [{victim}]", + "hud.chat.npc_energy_kill_msg": "{attacker} zabil [{victim}] pomocí magie", + "hud.chat.npc_other_kill_msg": "{attacker} zabil [{victim}]", + + "hud.chat.loot_msg": "Sebral si [{item}]", + "hud.chat.loot_fail": "Tvůj inventář je plný!", + "hud.chat.goodbye": "nashledanou!", + "hud.chat.connection_lost": "Spojení ztraceno. Odpojuji za {time} sekund.", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/hud/crafting.ron b/assets/voxygen/i18n/cz_CZ/hud/crafting.ron new file mode 100644 index 0000000000..a1662ebe10 --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/hud/crafting.ron @@ -0,0 +1,16 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + "hud.crafting": "Výroba", + "hud.crafting.recipes": "Recepty", + "hud.crafting.ingredients": "Ingredience:", + "hud.crafting.craft": "Vyrobit", + "hud.crafting.tool_cata": "Potřeba:", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/hud/group.ron b/assets/voxygen/i18n/cz_CZ/hud/group.ron new file mode 100644 index 0000000000..f9cfb73264 --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/hud/group.ron @@ -0,0 +1,24 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + "hud.group": "Skupina", + "hud.group.invite_to_join": "[{name}] tě pozval do skupiny!", + "hud.group.invite_to_trade": "[{name}] by chtěl s tebou obchodovat.", + "hud.group.invite": "Pozvat", + "hud.group.kick": "Vyhodit", + "hud.group.assign_leader": "Nastavit Vůdcem", + "hud.group.leave": "Opustit Skupinu", + "hud.group.dead" : "Mrtvý", + "hud.group.out_of_range": "Nedosažitelný", + "hud.group.add_friend": "Přidat Přítele", + "hud.group.link_group": "Propojit Skupiny", + "hud.group.in_menu": "V Menu", + "hud.group.members": "Členové Skupiny", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/hud/map.ron b/assets/voxygen/i18n/cz_CZ/hud/map.ron new file mode 100644 index 0000000000..96b68fae6e --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/hud/map.ron @@ -0,0 +1,33 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + // Map and Questlog + "hud.map.map_title": "Mapa", + "hud.map.qlog_title": "Úkoly", + "hud.map.difficulty": "Obtížnost", + "hud.map.towns": "Města", + "hud.map.castles": "Hrady", + "hud.map.dungeons": "Kobky", + "hud.map.caves": "Jeskyně", + "hud.map.cave": "Jeskyně", + "hud.map.trees": "Velké Stromy", + "hud.map.tree": "Velký Strom", + "hud.map.town": "Město", + "hud.map.castle": "Hrad", + "hud.map.dungeon": "Kobka", + "hud.map.difficulty_dungeon": "Kobka\n\nDifficulty: {difficulty}", + "hud.map.drag": "Pohybovat", + "hud.map.zoom": "Přiblížit", + "hud.map.recenter": "Recentrovat", + "hud.rank_up": "Nový Skillpoint", + "hud.skill.sp_available": "{number} SP dostupných", + "hud.skill.not_unlocked": "Není ještě odemčeno", + "hud.skill.req_sp": "\n\nVyžaduje {number} SP", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/it_IT/template.ron b/assets/voxygen/i18n/cz_CZ/hud/misc.ron similarity index 80% rename from assets/voxygen/i18n/it_IT/template.ron rename to assets/voxygen/i18n/cz_CZ/hud/misc.ron index f93a64c33e..54c050c0eb 100644 --- a/assets/voxygen/i18n/it_IT/template.ron +++ b/assets/voxygen/i18n/cz_CZ/hud/misc.ron @@ -1,9 +1,8 @@ /// WARNING: Localization files shall be saved in UTF-8 format without BOM -/// Localization for italian +/// Localization for Čeština ( string_map: { - }, diff --git a/assets/voxygen/i18n/uk_UA/template.ron b/assets/voxygen/i18n/cz_CZ/hud/sct.ron similarity index 80% rename from assets/voxygen/i18n/uk_UA/template.ron rename to assets/voxygen/i18n/cz_CZ/hud/sct.ron index 68d7bf1cf3..54c050c0eb 100644 --- a/assets/voxygen/i18n/uk_UA/template.ron +++ b/assets/voxygen/i18n/cz_CZ/hud/sct.ron @@ -1,9 +1,8 @@ /// WARNING: Localization files shall be saved in UTF-8 format without BOM -/// Localization for Ukrainian +/// Localization for Čeština ( string_map: { - }, diff --git a/assets/voxygen/i18n/cz_CZ/hud/settings.ron b/assets/voxygen/i18n/cz_CZ/hud/settings.ron new file mode 100644 index 0000000000..639e5d879c --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/hud/settings.ron @@ -0,0 +1,108 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + // Settings + "hud.settings.general": "Obecné", + "hud.settings.none": "Nic", + "hud.settings.press_behavior.toggle": "Přepnout", + "hud.settings.press_behavior.hold": "Podržet", + "hud.settings.help_window": "Okno nápovědy", + "hud.settings.debug_info": "Ladící informace", + "hud.settings.tips_on_startup": "Rady", + "hud.settings.ui_scale": "Velikost UI", + "hud.settings.relative_scaling": "Relativní škálování", + "hud.settings.custom_scaling": "Vlastní škálování", + "hud.settings.crosshair": "Zaměřování", + "hud.settings.opacity": "Průhlednost", + "hud.settings.hotbar": "Hotbar", + "hud.settings.toggle_shortcuts": "Povolit Zkratky", + "hud.settings.buffs_skillbar": "Buffy u Skillbaru", + "hud.settings.buffs_mmap": "Buffy u Minimapy", + "hud.settings.toggle_bar_experience": "Povolit Zkušenostní Bar", + "hud.settings.scrolling_combat_text": "Létajíci Combat Text", + "hud.settings.single_damage_number": "Jednotná čísla Poškození", + "hud.settings.cumulated_damage": "Seskupený Poškození", + "hud.settings.incoming_damage": "Příchozí Poškození", + "hud.settings.cumulated_incoming_damage": "Seskupený Příchozí Poškození", + "hud.settings.speech_bubble": "Povídací Bublina", + "hud.settings.speech_bubble_dark_mode": "Tmavá Povídací Bublina", + "hud.settings.speech_bubble_icon": "Ikona Povídací Bubliny", + "hud.settings.energybar_numbers": "Čísla u Baru Energie", + "hud.settings.values": "Hodnoty", + "hud.settings.percentages": "Procenta", + "hud.settings.chat": "Chat", + "hud.settings.background_opacity": "Průhlednost Pozadí", + "hud.settings.chat_character_name": "Jména Postav v Chatu", + "hud.settings.loading_tips": "Rady v Načítání", + "hud.settings.reset_interface": "Výchozí Nastavení", + + "hud.settings.pan_sensitivity": "Citlivost Rozhledu", + "hud.settings.zoom_sensitivity": "Citlivost Přiblížení", + "hud.settings.camera_clamp_angle": "Úhel pro vertikální režim upnutí kamery", + "hud.settings.invert_scroll_zoom": "Invertovat rolování zvětšení", + "hud.settings.invert_mouse_y_axis": "Obrátit osu Y myši", + "hud.settings.invert_controller_y_axis": "Obrátit osu Y ovladače", + "hud.settings.enable_mouse_smoothing": "Vyhlazení kamery", + "hud.settings.free_look_behavior": "Chování volného pohledu", + "hud.settings.auto_walk_behavior": "Chování Auto chůze", + "hud.settings.camera_clamp_behavior": "Chování připnuté kamery", + "hud.settings.stop_auto_walk_on_input": "Přestat Auto chodit při pohybu", + "hud.settings.reset_gameplay": "Výchozí Nastavení", + + "hud.settings.view_distance": "Vykreslovací Vzdálenost", + "hud.settings.sprites_view_distance": "Vykreslovací Vzdálenost Spritů", + "hud.settings.figures_view_distance": "Vykreslovací Vzdálenost Entit", + "hud.settings.maximum_fps": "Maximum FPS", + "hud.settings.fov": "Úhel pohledu (stupně)", + "hud.settings.gamma": "Gama", + "hud.settings.exposure": "Expozice", + "hud.settings.ambiance": "Jas prostředí", + "hud.settings.antialiasing_mode": "Režim AntiAliasing", + "hud.settings.upscale_factor": "Interní Rozlišení", + "hud.settings.cloud_rendering_mode": "Vykreslování Mraků", + "hud.settings.fluid_rendering_mode": "Vykreslování Vody", + "hud.settings.fluid_rendering_mode.cheap": "Levný", + "hud.settings.fluid_rendering_mode.shiny": "Blíštivý", + "hud.settings.cloud_rendering_mode.minimal": "Minimalní", + "hud.settings.cloud_rendering_mode.low": "Nizký", + "hud.settings.cloud_rendering_mode.medium": "Střední", + "hud.settings.cloud_rendering_mode.high": "Vysoký", + "hud.settings.cloud_rendering_mode.ultra": "Ultra", + "hud.settings.fullscreen": "Celá Obrazovka", + "hud.settings.fullscreen_mode": "Režim Celé obrazovky", + "hud.settings.fullscreen_mode.exclusive": "Exkluzivní", + "hud.settings.fullscreen_mode.borderless": "Bezokrajový", + "hud.settings.particles": "Částice", + "hud.settings.resolution": "Rozlišení", + "hud.settings.bit_depth": "Bitová Hloubka", + "hud.settings.refresh_rate": "Obnovovací Frekvence", + "hud.settings.lighting_rendering_mode": "Vykreslování Osvětlení", + "hud.settings.lighting_rendering_mode.ashikhmin": "Typ A - Vysoký ", + "hud.settings.lighting_rendering_mode.blinnphong": "Typ B - Střední", + "hud.settings.lighting_rendering_mode.lambertian": "Typ L - Levný", + "hud.settings.shadow_rendering_mode": "Vykreslování Stínů", + "hud.settings.shadow_rendering_mode.none": "Nic", + "hud.settings.shadow_rendering_mode.cheap": "Levný", + "hud.settings.shadow_rendering_mode.map": "Mapa", + "hud.settings.shadow_rendering_mode.map.resolution": "Rozlišení", + "hud.settings.lod_detail": "LoD Detail", + "hud.settings.save_window_size": "Pamatovat Okno", + "hud.settings.reset_graphics": "Výchozí Nastavení", + + + "hud.settings.music_volume": "Hlasitost Hudby", + "hud.settings.sound_effect_volume": "Hlasitost Efektů", + "hud.settings.audio_device": "Vstupní Zařízení", + "hud.settings.reset_sound": "Výchozí Nastavení", + + "hud.settings.awaitingkey": "Zmáčkni klávesu...", + "hud.settings.unbound": "Žádná", + "hud.settings.reset_keybinds": "Výchozí", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/hud/skills.ron b/assets/voxygen/i18n/cz_CZ/hud/skills.ron new file mode 100644 index 0000000000..723f1191dd --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/hud/skills.ron @@ -0,0 +1,243 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + // General + "hud.skill.inc_health_title": "Zvýší Zdraví", + "hud.skill.inc_health": "Zvýší maximální zdraví o 5{SP}", + "hud.skill.inc_energy_title": "Zvýší Výdrž", + "hud.skill.inc_energy": "Zvýší Výdrž o 5{SP}", + "hud.skill.unlck_sword_title": "Odemknutí Meče", + "hud.skill.unlck_sword": "Odemkne strom dovedností pro meče{SP}", + "hud.skill.unlck_axe_title": "Odemknutí Sekery", + "hud.skill.unlck_axe": "Odemkne strom dovedností pro sekery{SP}", + "hud.skill.unlck_hammer_title": "Odemknutí Kladiva", + "hud.skill.unlck_hammer": "Odemkne strom dovedností pro kladivo{SP}", + "hud.skill.unlck_bow_title": "Odemknutí Luku", + "hud.skill.unlck_bow": "Odemkne strom dovedností pro luk{SP}", + "hud.skill.unlck_staff_title": "Odemknutí Hole", + "hud.skill.unlck_staff": "Odemkne strom dovedností pro hůl{SP}", + "hud.skill.unlck_sceptre_title": "Odemknutí Žezla", + "hud.skill.unlck_sceptre": "Odemkne strom dovedností pro žezlo{SP}", + "hud.skill.dodge_title": "Vyhnutí", + "hud.skill.dodge": "Vyhnese útokům zblízka{SP}", + "hud.skill.roll_energy_title": "Menší spotřeba Výdrže pro Kotrmelec", + "hud.skill.roll_energy": "Kotrmelec použije o 20% méně Výdrže{SP}", + "hud.skill.roll_speed_title": "Rychlost Kotrmelce", + "hud.skill.roll_speed": "Kotrmelec je o 20% rychlejší{SP}", + "hud.skill.roll_dur_title": "Délka Kotrmelce", + "hud.skill.roll_dur": "Kotrmelec je delší o 20%{SP}", + "hud.skill.climbing_title": "Lezení", + "hud.skill.climbing": "Skočíš výš", + "hud.skill.climbing_cost_title": "Lezení", + "hud.skill.climbing_cost": "Lezení spotřebuje o 20% méně Výdrže{SP}", + "hud.skill.climbing_speed_title": "Rychlost Lezení", + "hud.skill.climbing_speed": "Lezení je o 20% rychlejší{SP}", + "hud.skill.swim_title": "Plavání", + "hud.skill.swim": "Pohyb v mokrém prostředí", + "hud.skill.swim_speed_title": "Rychlost plávání", + "hud.skill.swim_speed": "Plaveš o 40% rychleji{SP}", + // Sceptre + "hud.skill.sc_lifesteal_title": "Paprsek Životokrádeže", + "hud.skill.sc_lifesteal": "Krade život z nepřátel", + "hud.skill.sc_lifesteal_damage_title": "Poškození", + "hud.skill.sc_lifesteal_damage": "Přídá o 20% více poškození{SP}", + "hud.skill.sc_lifesteal_range_title": "Dosah", + "hud.skill.sc_lifesteal_range": "Paprsek dosáhne o 25% dále{SP}", + "hud.skill.sc_lifesteal_lifesteal_title": "Životokrádež", + "hud.skill.sc_lifesteal_lifesteal": "Konverutuje o 30% více poškození na život{SP}", + "hud.skill.sc_lifesteal_regen_title": "Obnova Výdrže", + "hud.skill.sc_lifesteal_regen": "Doplní výdrž o 25%{SP}", + "hud.skill.sc_heal_title": "Paprsek Léčby", + "hud.skill.sc_heal": "Vyléčí vaše přátelé pomocí krve nepřátel", + "hud.skill.sc_heal_heal_title": "Léčba", + "hud.skill.sc_heal_heal": "Zvýší efektivnost Léčby o 20%{SP}", + "hud.skill.sc_heal_cost_title": "Spotřeba Výdrže", + "hud.skill.sc_heal_cost": "Léčení spotřebuje o 20% méně Výdrže{SP}", + "hud.skill.sc_heal_range_title": "Dosah", + "hud.skill.sc_heal_range": "Paprsek dosáhne dále o 25% {SP}", + "hud.skill.sc_wardaura_unlock_title": "Ochranářská Aura", + "hud.skill.sc_wardaura_unlock": "Dovolí ochránit přátele před útoky{SP}", + "hud.skill.sc_wardaura_strength_title": "Síla", + "hud.skill.sc_wardaura_strength": "Síla ochrany se zvýší o 20%{SP}", + "hud.skill.sc_wardaura_duration_title": "Délka", + "hud.skill.sc_wardaura_duration": "Efekt vaší ochrany potrvá o 30% déle{SP}", + "hud.skill.sc_wardaura_range_title": "Rádius", + "hud.skill.sc_wardaura_range": "Ochrana dosáhne o 25% dále{SP}", + "hud.skill.sc_wardaura_cost_title": "Spotřeba Výdrže", + "hud.skill.sc_wardaura_cost": "Tvorba ochrany stojí o 20% méně Energie{SP}", + // Staff + "hud.skill.st_shockwave_range_title" : "Dosah Rázové vlny", + "hud.skill.st_shockwave_range" : "Umožný hodit věci mimo dosah. Dosah se zvýšil o 20%{SP}", + "hud.skill.st_shockwave_cost_title" : "Spotřeba Rázové vlny", + "hud.skill.st_shockwave_cost" : "Snižuje spotřebu energie pro házení bezbraných vesničanů o 20%{SP}", + "hud.skill.st_shockwave_knockback_title" : "Ráz Rázové vlny", + "hud.skill.st_shockwave_knockback" : "Zvyšuje odhození o 30%{SP}", + "hud.skill.st_shockwave_damage_title" : "Poškození Rázové Vlny", + "hud.skill.st_shockwave_damage" : "Zvyšuje poškození o 30%{SP}", + "hud.skill.st_shockwave_unlock_title" : "Shockwave Unlock", + "hud.skill.st_shockwave_unlock" : "Umožnuje odhazovat objekty pomocí ohně{SP}", + "hud.skill.st_flamethrower_title" : "Plamenomet", + "hud.skill.st_flamethrower" : "Podpaluje, jdeme péct", + "hud.skill.st_flame_velocity_title" : "Rychlost metání", + "hud.skill.st_flame_velocity" : "Zvýší rychlost ohně o 25% {SP}", + "hud.skill.st_flamethrower_range_title" : "Dosah Plamenometu", + "hud.skill.st_flamethrower_range" : "Když plameny nedosáhnou, tak je potřeba je zvětšit o 25% {SP}", + "hud.skill.st_energy_drain_title" : "Spotřeba Energie", + "hud.skill.st_energy_drain" : "Zmenší spotřebu energie o 20%{SP}", + "hud.skill.st_flamethrower_damage_title" : "Poškození Plamenometem", + "hud.skill.st_flamethrower_damage" : "Zvýší poškození o 30%{SP}", + "hud.skill.st_explosion_radius_title" : "Rozsah Exploze", + "hud.skill.st_explosion_radius" : "Cím větší, tím lepší. Zvětší Rádius Exploze o 10%{SP}", + "hud.skill.st_energy_regen_title" : "Obnova Výdrže", + "hud.skill.st_energy_regen" : "Zvýší generaci výdrže o 20%{SP}", + "hud.skill.st_fireball_title" : "Ohnivá Koule", + "hud.skill.st_fireball" : "Hrajte aport s nepřáteli", + "hud.skill.st_damage_title" : "Poškození", + "hud.skill.st_damage" : "Zvětší Poškození o 20%{SP}", + "hud.skill.st_explosion_title" : "Exploze", + "hud.skill.st_explosion" : "Když oheň nestačí{SP}", + // Bow + "hud.skill.bow_projectile_speed_title" : "Rychlost Projektilu", + "hud.skill.bow_projectile_speed" : "Zvětšuje rozsah, rychlost o 30%{SP}", + "hud.skill.bow_arrow_count_title" : "Počet Šípů", + "hud.skill.bow_arrow_count" : "Vystřelí další šíp při odskoku{SP}", + "hud.skill.bow_repeater_cost_title" : "Zmenšení Spotřeby", + "hud.skill.bow_repeater_cost" : "Sníží spotřebu Energie o 30%{SP}", + "hud.skill.bow_repeater_glide_title" : "Plachtění Opakovače", + "hud.skill.bow_repeater_glide" : "Doplachti dál při opakování{SP}", + "hud.skill.bow_repeater_damage_title" : "Poškození během opakování", + "hud.skill.bow_repeater_damage" : "Zvýší poškození o 40%{SP}", + "hud.skill.bow_repeater_unlock_title" : "Odemčení Opakovače", + "hud.skill.bow_repeater_unlock" : "Odemkne možnost odskočit ve vzduchu a vystřelit haldu šípů{SP}", + "hud.skill.bow_charged_title" : "Silný Výstřel", + "hud.skill.bow_charged" : "Protože si počkal/a dýl", + "hud.skill.bow_charged_knockback_title" : "Nabitý Ráz", + "hud.skill.bow_charged_knockback" : "Odhoď neprátele o 25% dále{SP}", + "hud.skill.bow_charged_move_speed_title" : "Rychlost při míření", + "hud.skill.bow_charged_move_speed" : "Zvýší pohyb během natahování luku o 25%{SP}", + "hud.skill.bow_charged_speed_title" : "Nabitá rychlost ", + "hud.skill.bow_charged_speed" : "Zvýší jak rychle lze střílet o 10%{SP}", + "hud.skill.bow_charged_projectile_speed_title" : "Rychlost Nabitého Projektilu", + "hud.skill.bow_charged_projectile_speed" : "Rychlost projektilu se zvětší o 20% během nabijení{SP}", + "hud.skill.bow_charged_drain_title" : "Spotřeba během Nabijení", + "hud.skill.bow_charged_drain" : "Snižuje spotřebu nabití o 15%{SP}", + "hud.skill.bow_charged_damage_title" : "Nabitý Poškození", + "hud.skill.bow_charged_damage" : "Zvyšuje Poškození o 20%{SP}", + "hud.skill.bow_energy_regen_title" : "Regenerace Energie", + "hud.skill.bow_energy_regen" : "Zvyšuje zisk Energie o 40%{SP}", + "hud.skill.bow_title" : "Výstřel Šípu", + "hud.skill.bow" : "Nekoneční toulec, nepatří do ruky dětem", + "hud.skill.bow_damage_title" : "Poškození", + "hud.skill.bow_damage" : "Zvyšuje poškození o 25%{SP}", + // Hammer + "hud.skill.hmr_leap_radius_title" : "Rádius Vyšlehnutí", + "hud.skill.hmr_leap_radius" : "Zvětšuje rádius boucnutí do země by 1 meter{SP}", + "hud.skill.hmr_leap_distance_title" : "Vzdálenost Vyšlehnutí", + "hud.skill.hmr_leap_distance" : "Zvětší vzdálenost o 25%{SP}", + "hud.skill.hmr_leap_cost_title" : "Spotřeba při Vyšlehnutí", + "hud.skill.hmr_leap_cost" : "Sníží cenu skoku o 25%{SP}", + "hud.skill.hmr_leap_knockback_title" : "Odražení", + "hud.skill.hmr_leap_knockback" : "Zvyšuje odražení z Vyšlehnutí o 50%{SP}", + "hud.skill.hmr_leap_damage_title" : "Poškození Vyšlehnutí", + "hud.skill.hmr_leap_damage" : "Zvyšuje poškození při Vyšlehnutí 40%{SP}", + "hud.skill.hmr_unlock_leap_title" : "Odemknout Vyšlehnutí", + "hud.skill.hmr_unlock_leap" : "Unlocks a leap{SP}", + "hud.skill.hmr_charged_melee_title" : "Nabitý Rozkmit", + "hud.skill.hmr_charged_melee" : "Útok na blízko, ale silnější", + "hud.skill.hmr_charged_rate_title" : "Rychlost Nabití", + "hud.skill.hmr_charged_rate" : "Zvýší rychlost nabití o 25%{SP}", + "hud.skill.hmr_charged_melee_nrg_drain_title" : "Menší Spotřeba Energie", + "hud.skill.hmr_charged_melee_nrg_drain" : "Snižuje spotřebu energie během Srážky o 25%{SP}", + "hud.skill.hmr_charged_melee_damage_title" : "Poškození", + "hud.skill.hmr_charged_melee_damage" : "Zvětší poškození nabitého rozkmitu o 25%{SP}", + "hud.skill.hmr_charged_melee_knockback_title" : "Knockback", + "hud.skill.hmr_charged_melee_knockback" : "Masivně zvětšuje knockback Rozkmitu o 50%{SP}", + "hud.skill.hmr_single_strike_title" : "Jednoduchý Úder", + "hud.skill.hmr_single_strike" : "Jednoduchý jako ty", + "hud.skill.hmr_single_strike_regen_title" : "Regenerace Výdrže", + "hud.skill.hmr_single_strike_regen" : "Zvyšuje Výdrž při každém povedeném úderu{SP}", + "hud.skill.hmr_single_strike_speed_title" : "Rychlost Jednoduchého Útoku", + "hud.skill.hmr_single_strike_speed" : "Zvýší rychlost útoku pro Jednoduchý Útok při trefě{SP}", + "hud.skill.hmr_single_strike_damage_title" : "Poškození Jednoduchého Útoku", + "hud.skill.hmr_single_strike_damage" : "Při každém povedeném úderu zvyšuje Poškození{SP}", + "hud.skill.hmr_single_strike_knockback_title" : "Odražení pomocí Jednoduchého Útoku", + "hud.skill.hmr_single_strike_knockback" : "Zvyšuje potencial odhození o 50%{SP}", + "hud.skill." : "", + // Sword + "hud.skill.sw_trip_str_title": "Trojtý Úder", + "hud.skill.sw_trip_str": "Udeříš až 3x", + "hud.skill.sw_trip_str_combo_title": "Kombo Trojtého Úderu", + "hud.skill.sw_trip_str_combo": "Odemkne škálování kombíček na trojtým útoku{SP}", + "hud.skill.sw_trip_str_dmg_title": "Poškození Trojtým Úderem", + "hud.skill.sw_trip_str_dmg": "Zvyšuje poškození při každém podařeném úderu{SP}", + "hud.skill.sw_trip_str_sp_title": "Rychlost Trojtého Úderu", + "hud.skill.sw_trip_str_sp": "Zvýší rychlost útoku při každém úspešném úderu{SP}", + "hud.skill.sw_trip_str_reg_title": "Regenerace při Trojtém Úderu", + "hud.skill.sw_trip_str_reg": "Regeneruje Výdrž při každém úspešném úderu{SP}", + "hud.skill.sw_dash_title": "Dash", + "hud.skill.sw_dash": "Proběhni přes nepřátele", + "hud.skill.sw_dash_dmg_title": "Poškození Dashem", + "hud.skill.sw_dash_dmg": "Zvýší poškození o 20%{SP}", + "hud.skill.sw_dash_drain_title": "Menší spotřeba při Dashy", + "hud.skill.sw_dash_drain": "Sníží průběžnou spotřebu energie o 25%{SP}", + "hud.skill.sw_dash_cost_title": "Cena Dashe ", + "hud.skill.sw_dash_cost": "Sníží celkovou spotřebu energie o 25%{SP}", + "hud.skill.sw_dash_speed_title": "Rychlost Dashe", + "hud.skill.sw_dash_speed": "Zvýší rychlost při Dashování o 30%{SP}", + "hud.skill.sw_dash_inf_title": "Nekoneční Dash", + "hud.skill.sw_dash_inf": "Nechá tě používát Dash nekonečně dlouho, než ti dojde energie{SP}", + "hud.skill.sw_dash_scale_title": "Škálování Poškození při Dashy", + "hud.skill.sw_dash_scale": "Zvýší škálování poškození o 20%{SP}", + "hud.skill.sw_spin_title": "Odemknout Roztočení", + "hud.skill.sw_spin": "Odemkne roztočení{SP}", + "hud.skill.sw_spin_dmg_title": "Poškození Roztočení", + "hud.skill.sw_spin_dmg": "Zvýší poškození, které udělíš o 40%{SP}", + "hud.skill.sw_spin_spd_title": "Rychlost Roztočení", + "hud.skill.sw_spin_spd": "Zvýší rychlost roztočení o 25%{SP}", + "hud.skill.sw_spin_cost_title": "Cena Roztočení", + "hud.skill.sw_spin_cost": "Sníží cenu roztočení o 25%{SP}", + "hud.skill.sw_spin_spins_title": "Počet točení při Roztočení", + "hud.skill.sw_spin_spins": "Zvýší počet otoček během roztočení{SP}", + "hud.skill.sw_interrupt_title": "Překažení Útoků", + "hud.skill.sw_interrupt": "Dokáže ihned přerušit útok dalším útokem{SP}", + // Axe + "hud.skill.axe_double_strike_title": "Dvojtý Úder", + "hud.skill.axe_double_strike": "Skácej své nepřátele", + "hud.skill.axe_double_strike_combo_title": "Kombo Dvojtého Úderu", + "hud.skill.axe_double_strike_combo": "Odemkne Dvojtý Útok{SP}", + "hud.skill.axe_double_strike_damage_title": "Poškození Dvojtého Úderu", + "hud.skill.axe_double_strike_damage": "Zvyšuje poškození při každém podařeném úderu{SP}", + "hud.skill.axe_double_strike_speed_title": "Rychlost Dvojtého Úderu", + "hud.skill.axe_double_strike_speed": "Zvyšuje Rychlost Útoku při každém podařeném úderu{SP}", + "hud.skill.axe_double_strike_regen_title": "Regenerace při Dvojtém Útoku", + "hud.skill.axe_double_strike_regen": "Zvyšuje regeneraci Výdrže při každém podařeném úderu{SP}", + "hud.skill.axe_spin_title": "Roztočení Sekery", + "hud.skill.axe_spin": "Točíš se správným směrem...", + "hud.skill.axe_infinite_axe_spin_title": "Nekonečné Roztočení Sekery", + "hud.skill.axe_infinite_axe_spin": "Toč Sekeru dokaď máš Energii{SP}", + "hud.skill.axe_spin_damage_title": "Poškození Roztočení", + "hud.skill.axe_spin_damage": "Zvýší poškození, které udělíš o 30%{SP}", + "hud.skill.axe_spin_helicopter_title": "Vrtulníček", + "hud.skill.axe_spin_helicopter": "Při pádu budeš padat pomaleji při Roztočení Sekery{SP}", + "hud.skill.axe_spin_speed_title": "Rychlost Roztočení", + "hud.skill.axe_spin_speed": "Zvýší Rychlost Roztočení o 25%{SP}", + "hud.skill.axe_spin_cost_title": "Cena Roztočení", + "hud.skill.axe_spin_cost": "Sníží spotřebu výdrže o 25%{SP}", + "hud.skill.axe_unlock_leap_title": "Odemknout Vyšlehnutí", + "hud.skill.axe_unlock_leap": "Odemkne Vyšlehnutí{SP}", + "hud.skill.axe_leap_damage_title": "Poškození při Vyšlehnutí", + "hud.skill.axe_leap_damage": "Zvýší poškození Vyšlehnutí o 35%{SP}", + "hud.skill.axe_leap_knockback_title": "Odražení při Vyšlehnutí", + "hud.skill.axe_leap_knockback": "Při Vyšlehnutí, zvýší odražení o 40%{SP}", + "hud.skill.axe_leap_cost_title": "Cena Vyšlehnutí", + "hud.skill.axe_leap_cost": "Sníží cenu vyšlehnutí o 25%{SP}", + "hud.skill.axe_leap_distance_title": "Dosah Vyšlehnutí", + "hud.skill.axe_leap_distance": "Zvýší dosah o 20%{SP}", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/cz_CZ/hud/social.ron b/assets/voxygen/i18n/cz_CZ/hud/social.ron new file mode 100644 index 0000000000..1a5f5f09ee --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/hud/social.ron @@ -0,0 +1,24 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + /// Socialní + + "hud.social": "Společnost", + "hud.social.online": "Aktivní", + "hud.social.friends": "Přátelé", + "hud.social.not_yet_available": "Ještě není dostupné", + "hud.social.faction": "Cech", + "hud.social.play_online_fmt": "{nb_player} je aktivní", + "hud.social.name": "Jméno", + "hud.social.level": "Úroveň", + "hud.social.zone": "Zóna", + "hud.social.account": "Účet", + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/cz_CZ/hud/trade.ron b/assets/voxygen/i18n/cz_CZ/hud/trade.ron new file mode 100644 index 0000000000..c3a9f86c0d --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/hud/trade.ron @@ -0,0 +1,30 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + /// Obchod + + "hud.trade.trade_window": "Obchod", + "hud.trade.phase1_description": "Přetáhněte položky.", + "hud.trade.phase2_description": "Obchod je nyní uzamčen, abyste měli\n čas jej zkontrolovat.", + /// Phase3 should only be visible for a few milliseconds if everything is working properly, but is included for completeness + "hud.trade.phase3_description": "Probíhá zpracování.", + "hud.trade.persons_offer": "Nabídka {playername}", + "hud.trade.has_accepted": "{playername}přijal obchod", + "hud.trade.accept": "Akceptovat", + "hud.trade.decline": "Odmítnout", + "hud.trade.invite_sent": "Žádost o obchod zaslána {playername}.", + "hud.trade.result.completed": "Obchod proběhl úspěšně.", + "hud.trade.result.declined": "Obchod odmítnut.", + "hud.trade.result.nospace": "Nedostatek místa pro dokončení.", + "hud.trade.buy_price": "Cena koupě", + "hud.trade.sell_price": "Cena prodeje", + "hud.trade.coin": "Peníze", + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/cz_CZ/main.ron b/assets/voxygen/i18n/cz_CZ/main.ron new file mode 100644 index 0000000000..ce6f65bfd6 --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/main.ron @@ -0,0 +1,88 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + /// Start Main screen section + "main.connecting": "Připojování", + "main.creating_world": "Tvorba Světa", + + // Welcome notice that appears the first time Veloren is started + "main.notice": r#"Vítejte v alfa verzi Veloren! + +Než začnete, mějte na paměti, že: + +- Hra je v rané verzi alfa. Očekávejte chyby, extrémně nedokončený příběh, nedostatečně rozvinuté mechaniky a chybějící funkce. + +- Pokud se chcete podělit o svůj názor, máte návrhy nebo nápady nebo chcete nahlásit chybu, můžete nás kontaktovat prostřednictvím Redditu, GitLabu nebo Discordu. + +- Veloren je licencován pod licencí open source GPL 3. To znamená, že máte právo hru upravovat a distribuovat, jakkoliv chcete (pokud je vaše práce licencována také pod GPL 3). + +- Veloren je neziskový projekt, kde je každý pracující člověk dobrovolníkem. Pokud se vám tato hra líbí, připojte se k našemu týmu! + +Děkujeme za přečtení této zprávy a doufáme, že se vám tato hra bude líbit! + +~ Tvůrci Veloren"#, + + // Login process description + "main.login_process": r#"Informace o procesu přihlášení: + +Pokud máte problémy s přihlášením: + +Nezapomeňte, že k připojení na server +se zapnutým ověřením potřebujete účet. + +Účet si můžete vytvořit na webu: + +https://veloren.net/account/."#, + "main.login.server_not_found": "Server nenalezen", + "main.login.authentication_error": "Chyba ověření", + "main.login.server_full": "Server je plný", + "main.login.untrusted_auth_server": "Ověřovací server je nedůvěryhodný", + "main.login.outdated_client_or_server": "ServerWentMad: Pravděpodobně jsou verze nekompatibilní, zkontrolujte aktualizace!", + "main.login.network_wrong_version": "Server běží na jiné verzy hry. Aktualizuj si klienta.", + "main.login.timeout": "Timeout: Server neodpověděl včas. (Přetížení nebo chyby v síti).", + "main.login.server_shut_down": "Server Uzavřen", + "main.login.network_error": "Chyba sítě.", + "main.login.failed_sending_request": "Dotaz na ověřovací server se nezdařil", + "main.login.client_crashed": "Pád klienta", + "main.servers.select_server": "Výběr serveru", + "main.server": "Server", + "main.password": "Heslo", + "main.username": "Jméno", + "main.tip":"Rada:", + "main.login.invalid_character": "Vybraná postava je neplatná", + "main.login.not_on_whitelist": "Potřebuješ být ve whitelistu. Kontaktuj Admina serveru.", + "main.login.banned": "Byl/a si zabanován/á z tohoto důvodu", + "main.login.kicked": "Byl/a si vyhozen/á z tohoto důvodu", + "main.login.select_language": "Vyber si svůj Jazyk", + "main.login.insecure_auth_scheme": "Schéma ověření HTTP NENÍ podporováno. Je to nejisté! Pro účely vývoje je HTTP povolen pro „localhost“ nebo pro ladění", + "main.login.failed_auth_server_url_invalid": "Připojení k ověřovacímu serveru se nezdařilo.", + /// End Main screen section + }, + + + vector_map: { + "loading.tips": [ + "Stisknutím G rozsvítíš svou lucernu.", + "Stisknutím klávesy 'F1' zobrazíš všechny výchozí klávesy.", + "Můžetš použít /say nebo /s pro chat pouze s hráči přímo kolem vás.", + "Můžeš napsat /region nebo /r pro chat s hráči pát stovek bloků od vás.", + "Admini mohou napsat /build příkaz pro spuštění Stavívího Módu.", + "Můžeš napsat /group nebo /g pro chat s vaší Skupinou.", + "Pro posílání privátních zpráv napiš /tell poté jméno hráce a zprávu.", + "Dávej si pozor na jídlo, truhly a jiný kořisti rozmístěné po celém světě!", + "Inventář plný jídla? Zkus vytvořit z toho lepší jídlo!", + "Přemýšlíš co dělat? Zkus nějakou Kobku označenou na mapě!", + "Nezapomeň si nastavit Grafiku pro svůj systém. Klávesou 'N' otevřeš Nastavení.", + "Hraní s hráči je zábava! Klávesou 'O' se podívej kdo je Online.", + "Klávesou 'J' začneš Tancovat. Párty!", + "Klávesou 'L-Shift' otevřeš Kluzák a můžeš dobýt nebesa.", + "Veloren je stále v Pre-Alpha. Snažíme se hru zlepšit co to jde!", + "Jestli se chceš napojit k dev teamu nebo jen si napsat, připoj se na náš Discord server.", + "Můžeš povolit zobrazení tvého životu v healthbaru v Nastavení.", + "Sedni si k Táboráku (Klávesa 'K') pro pomalé léčení.", + "Potřebuješ více tašek, či lepší zbroj pro vaše dobrodrušství? Klávesou 'C' otevřeš nabídku Tvorby!", + ], + } +) diff --git a/assets/voxygen/i18n/cz_CZ/npc.ron b/assets/voxygen/i18n/cz_CZ/npc.ron new file mode 100644 index 0000000000..06ee417757 --- /dev/null +++ b/assets/voxygen/i18n/cz_CZ/npc.ron @@ -0,0 +1,152 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Čeština +( + string_map: { + }, + + vector_map: { + "npc.speech.villager": [ + "Není dnes tak překrásný den?", + "Jak se dneska máš?", + "Dobré ráno!", + "Zajímalo by mě, co si Catoblepové myslí, když jí trávu.", + "Co si myslíš o tomto počasí?", + "Přemýšlení o těchto kobkách mě děsí. Doufám, že je někdo vyčistí.", + "Rád bych šel prozkoumávat jeskyni, až budu silnější.", + "Neviděl si moji kočku?", + "Už si někdy slyšel o divokých Pozemních Žralocích? Slyšel jsem, že žijí v pouštích.", + "Říká se, že v jeskyních se nacházejí lesklé drahokamy všeho druhu.", + "Jsem jen o sýrovích šušenkách!", + "Nepůjdeš dovnitř? Právě jsem si chtěl dát sýr!", + "Říká se, že houby jsou dobré pro vaše zdraví. Nikdy jsem je neměl.", + "Nezapomeňte na sušenky!", + "Prostě zbožňuji trpasličí sýr. Přál bych si, abych ho uměl dělat.", + "Zajímalo by mě, co je na druhé straně hor.", + "Doufám, že si někdy vyrobím vlastní kluzák.", + "Chceš vidět moji zahradu? Dobře, možná někdy jindy.", + "Krásný den na procházku do lesa!", + "Být či nebýt? Myslím, že budu farmář.", + "Nemyslíš si, že naše vesnice je nejlepší?", + "Co podle vás září v Glowing Remains?", + "Myslím, že je čas na druhou snídani!", + "Už si někdy chytil světlušku?", + "Prostě nechápu, odkud ti Saurokové stále přicházejí.", + "Přál bych si, aby někdo držel vlky daleko od vesnice.", + "Minulou noc jsem měl nádherný sen o sýru. Co to znamená?", + "Nechal jsem trochu sýra s bratrem osamotě. Teď nevím, jestli existuje nebo ne. Říkám tomu Schrödingerův sýr.", + "Nechal jsem trochu sýra se setrou osamotě. Teď nevím, jestli existuje nebo ne. Říkám tomu Schrödingerův sýr.", + "Někdo by měl s těmi kultisty něco udělat. Nejlepší kdyý já ne.", + "Doufám, že brzy bude pršet. Bylo by to dobré pro plodiny.", + "Miluji med! A nesnáším včely.", + "Chci jednoho dne vidět svět. Musí tu být více života než v této vesnici.", + ], + "npc.speech.villager_cultist_alarm": [ + "Bacha! Je tu kultista!", + "Do zbroje! Kultisti útočí!", + "Jak se Kultisti opovážli útočit na naší vesnici!", + "Smrt kultistům!", + "Kultisti tu nejsou tolerováni!", + "Vražední Kultisti!", + "Ochutnej mojí čepel Kultisto", + "Nic nedokáže vyčistit krev na tvých rukách Kultisto!", + "Billions of blistering blue barnacles! A cultist among us!", + "Zlo tohoto Kultisty je ukonce!", + "Tento Kultista je můj!", + "Připrav se potkat svého Stvořitele blbý Kultisto!", + "Vidím Kultistu! Na něj!", + "Vidím Kultistu! Útok!", + "Vidím Kultistu! Nenech je utéct!", + "Uvažoval někdy úctiví Kultitsta o SMRŤI?!", + "Nikdy neodpustit! Nikdy nezapomenout! Kultista bude pikat!", + "Umři Kultisto!", + "Tvů začátek chaosu bude u konce!", + "Tady je vše za to, co si udělal!", + "Nejsme moc přátelští k lidem tvého druhu.", + "Měl si zůstat v podzemí!", + ], + "npc.speech.villager_under_attack": [ + "Pomoc, jsem pod útokem!", + "Pomoc! Jsem pod útokem!", + "Au! Jsem pod útokem!", + "Auh! Jsem pod útokem! Pomoc!", + "Pomoc! Jsem pod útokem!", + "Jsem pod útokem, Pomoc!", + "Jsem pod útokem! Pomoc!", + "Pomoc!", + "Pomoc! Pomoc!", + "Pomoc! Pomoc! Pomoc!", + "Jsem pod útokem!", + "AAAHHH! Jsem pod útokem!", + "AAAHHH! Jsem pod útokem! Pomoc!", + "Pomoc! Jseme pod útokem!", + "Pomoc! Vrah!", + "Pomoc! Je tu vrah!", + "Pomoc! Snaží se mě zabít!", + "Stráže, Jsem pod útokem!", + "Stráže! Jsem pod útokem!", + "Jsem pod útokem! Stráže!", + "Pomoc! Stráže! Jsem pod útokem!", + "Stráže! Rychle!", + "Stráže! Stráže!", + "Stráže! Padouch na mě útočí!", + "Stráže, zabte tohoto padoucha!", + "Stráže! Je tu vrah!", + "Stráže! Pomoc!", + "S tím neutečeš jen tak! Stráže!", + "Příteli!", + "Pomoc !", + "Pomoc! Prosím!", + "Ouch! Stráže! Pomoc!", + "Jdou po mě!", + "Pomoc! Pomoc! Snaží se mě utlačit!", + "Ah, teď vidíme násilí v systému", + "Tohle je jen škrábnutí!", + "Nech toho!", + "Co jsem ti provedl?!", + "Prosím přestaň na mě útočit!", + "Hey! Dávej bacha co s tím děláš!", + "Hajze, zmiz!", + "Dost! Jdi do háje!", + "Teď mě štveš!", + "Oi! Co si myslíš že jsi?!", + "Tak teď chci tvojí hlavu!", + "Přestaň, Prosím! Nemám nic u sebe ceného!", + "Pošlu na tebe bratra, je větší než já!", + "Neeee, já půjdu žalovat matce!", + "Proklínám tě!", + "Prosím nedělej to.", + "Toto nebylo pěkné!", + "Tvá zbraň funguje, teď vypadni", + "Ušetři mě!", + "Prosím, mám rodinu!", + "Jsem moc mladý, abych umřel!", + "Můžeme si o tom promluvit?", + "Násilí není nikdy odpověď!", + "Dnešek bude pěkně na nic...", + "Hey, to bolí!", + "Eek!", + "Jak hnusný!", + "Stop, prosím tě!", + "A pox upon you!", + "Tohle není sranda.", + "Jak se opovažuješ?!", + "Za to zaplatíš!", + "Ještě chvilku porkačuj a budeš toho litovat!", + "Nechtěj, abych tě zranil!", + "To musí být omyl!", + "Nepotřebuješ toto dělat!", + "Vypdni kamaráda!", + "To opravdu bolelo!", + "Proč bys to dělal?", + "ve jménu ducha svatého, vypadni!", + "Musel si se splést!", + "To si nezasloužím!", + "Prosím, nedělej to znova.", + "Stráže, hoďte toto monstrum do jezera!", + "vypustím na tebe Tarasque!", + "Proč jáááá?", + ], + + }, +) diff --git a/assets/voxygen/i18n/de_DE/_manifest.ron b/assets/voxygen/i18n/de_DE/_manifest.ron index 509da15753..155ae923be 100644 --- a/assets/voxygen/i18n/de_DE/_manifest.ron +++ b/assets/voxygen/i18n/de_DE/_manifest.ron @@ -28,205 +28,5 @@ asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", scale_ratio: 1.0, ), - }, - - string_map: { - - }, - - vector_map: { - "loading.tips": [ - "Mit 'G' aktivierst du deine Laterne.", - "Mit 'F1' kannst du die Standard-Tastenbelegung einsehen.", - "Mit /say oder /s kannst du mit Spielern in deiner unmittelbaren Umgebung chatten.", - "Mit /region oder /r kannst du mit Spielern chatten, die sich ein paar hundert Meter um dich herum befinden.", - "Als Admin kannst du mit /build den Baumodus nutzen.", - "Mit /group oder /g können deine Nachrichten nur von Mitgliedern deiner Gruppe gelesen werden.", - "Um private Nachrichten zu verschicken, kannst du /tell, gefolgt vom Namen des Spielers, der die Nachricht erhalten soll, nutzen.", - "Halte Ausschau nach Essen, Kisten oder anderen Schätzen überall auf der Welt!", - "Deine Taschen sind voll mit Essen? Versuche besseres Essen damit herzustellen!", - "Du weißt nicht was du tun sollst? Versuche dich doch mal an den Katakomben, die auf der Karte eingezeichnet sind!", - "Vergiss nicht die Grafikeinstellungen an deine Hardware anzupassen. Mit 'N' öffnest du die Grafikeinstellungen.", - "Mit Anderen zusammen zu spielen macht noch viel mehr Spaß! Mit 'O' kannst du sehen, wer gerade online ist.", - "Mit 'J' kannst du das Tanzbein schwingen. Party!", - "Mit 'L-Shift' kannst du den Gleiter öffnen und hohe Lüfte erobern.", - "Veloren ist immernoch in der Pre-Alpha. Wir tun unser Bestes, um es jeden Tag ein Stück besser zu machen!", - "Du möchtest aktiv an der Entwicklung mitwirken oder einfach nur chatten? Dann komm auf unseren Discord-Server.", - "Die Art, wie dir deine Lebensenergie angezeigt wird, kannst du in den Einstellungen ändern.", - "Setze dich neben ein Lagerfeuer (drücke dafür 'K') um dich langsam von deinen Verletzungen zu erholen.", - "Du brauchst einen größeren Beutel oder bessere Rüstung? Dann drücke 'C', um das Crafting-Menü zu öffnen!", - ], - "npc.speech.villager": [ - "Ist das nicht ein wunderbarer Tag?", - "Wie geht's dir heute?", - "Einen schönen guten Morgen wünsche ich dir!", - "Ich frage mich, was der Catoblepas denkt, wenn er Gras isst.", - "Was hälst du vom Wetter?", - "Wenn ich an die Katakomben denke, bekomme ich Angst. Ich hoffe jemand befreit sie vom Unheil.", - "Ich würde gerne in eine der Höhlen gehen, wenn ich größer und stärker bin.", - "Hast du meine Katze gesehen?", - "Hast du schonmal etwas von den gefährlichen Landhaien gehört? Ich habe gehört sie leben in der Wüste.", - "Man sagt, es gebe Juwelen aller Art in den Höhlen der Gegend.", - "Ich bin einfach verrückt nach Käse!", - "Möchtest du nicht rein kommen? Bei uns gibt es gleich himmlischen Käse!", - "Man sagt Pilze seien gut für die Gesundheit. Ich selber habe noch nie welche gegessen.", - "Vergiss dein Feuerwerk nicht!", - "Ich liebe Zwergenkäse einfach. Ich wünschte, ich könnte selber welchen herstellen.", - "Ich frage mich, was sich auf der anderen Seite der Berge verbirgt.", - "Ich wünschte, ich könnte mir eines Tages meinen eigenen Gleiter bauen.", - "Möchtest du dir mal meinen Garten ansehen? Okay, vielleicht beim nächsten Mal.", - "Ein perfekter Tag, um in den Wald zu gehen!", - "Sein oder nicht sein? Ich glaube ich werde Landwirt.", - "Findest du nicht auch, dass unser Dorf das schönste ist?", - "Was glaubst du, was glühende Überreste zum Glühen bringt?", - "Ich glaube es ist an der Zeit für ein zweites Frühstück!", - "Hast du jemals ein Glühwürmchen gefangen?", - "Ich verstehe einfach nicht wo die ganzen Sauroks herkommen.", - "Ich wünschte, jemand könnte die ganzen Wölfe vom Dorf fernhalten.", - "Ich hatte letzte Nacht einen wunderbaren Traum über Käse. Was könnte das bedeuten?", - "Ich habe meinem Bruder ein wenig Käse dagelassen. Nun weiß ich nicht, ob er ihn schon aß oder nicht. Ich nenne es Schrödingers Käse.", - "Ich habe meiner Schwester ein wenig Käse dagelassen. Nun weiß ich nicht, ob sie ihn schon aß oder nicht. Ich nenne es Schrödingers Käse.", - "Jemand sollte etwas gegen diese Kultisten unternehmen. Vorzugsweise nicht ich.", - "Ich hoffe es regnet bald wieder. Der Ernte würde es gut tun.", - "Ich liebe Honig! Und hasse Bienen.", - "Eines Tages möchte ich die Welt bereisen. Es muss doch mehr im Leben geben als dieses Dorf.", - ], - "npc.speech.villager_decline_trade": [ - "Tut mir leid, ich habe nichts, was ich mit dir handeln könnte.", - "Du möchtest handeln? Als ob ich etwas hätte, das dich interessieren könnte.", - "Mein Haus gehört mir, ich würde es für nichts auf der Welt hergeben.", - ], - "npc.speech.merchant_advertisement": [ - "Darf ich dir einen Handel vorschlagen?", - "Möchtest du mit mir handeln?", - "Ich habe viele Waren. Möchtest du sie dir mal ansehen?" - ], - "npc.speech.merchant_busy": [ - "Hey, warte bis du an der Reihe bist.", - "Einen Moment bitte, ich kann mich nicht zerteilen.", - "Siehst du nicht, dass jemand vor dir steht?", - "Einen Moment bitte, ich bin gleich bei dir.", - "Nicht vordrängeln bitte.", - "Ich bin gerade beschäftigt, komm doch später wieder." - ], - "npc.speech.merchant_trade_successful": [ - "Vielen Dank für das Geschäft!", - "Vielen Dank!", - ], - "npc.speech.merchant_trade_declined": [ - "Vielleicht beim nächsten Mal, einen schönen Tag noch!", - "Zu schade, vielleicht beim nächsten mal, bis dann!" - ], - "npc.speech.villager_cultist_alarm": [ - "Achtung! Ein Kultist schleicht hier herum!", - "Zu den Waffen! Die Kultisten attackieren uns!", - "Wie können die Kultisten es wagen, unser Dorf zu attackieren!", - "Tod den Kultisten!", - "Kultisten werden hier nicht toleriert!", - "Mörderischer Kultist!", - "Eine Kostprobe meines Schwertes gefällig? Du reudiger Kultist!", - "Nichts kann das Blut von deinen Händen waschen, Kultist!", - "Ja brat' mir doch einer 'nen Storch! Ein Kultist weilt unter uns!", - "Die Schreckensherrschaft der Kultisten wird bald vorrüber sein!", - "Der Kultist gehört mir!", - "Bereite dich darauf vor deinem Schöpfer gegenüberzutreten, du mieser Kultist!", - "Ich sehe einen Kultisten! Ergreift ihn!", - "Ich sehe einen Kultisten!", - "Ich sehe einen Kultisten! Lasst ihn nicht entkommen!", - "Ist der ehrenwerte Kultist an ein wenig TOD interessiert?!", - "Wir vergeben nicht! Wir vergessen nicht! Kultist, büße!", - "Stirb, Kultist!", - "Deine Schreckensherrschaft wird enden!", - "Nimm das! Für alles, was du getan hast!", - "Wir wollen Abschaum wie euch hier nicht!", - "Du hättest unter der Erde bleiben sollen! Da wo du hingehörst!", - ], - "npc.speech.villager_under_attack": [ - "Hilfe, ich werde angegriffen!", - "Hilfe, ich werde angegriffen!", - "Autsch! ich werde angegriffen!", - "Autsch! ich werde angegriffen! Hilfe!", - "Helft mir! Ich werde angegriffen!", - "Ich werde angegriffen! Hilfe!", - "Ich werde angegriffen! Helft mir!", - "Hilfe!", - "Hilfe! Hilfe!", - "Hilfe! Hilfe! Hilfe!", - "Ich werde angegriffen!", - "AAAHHH! Ich werde angegriffen!", - "AAAHHH! Ich werde angegriffen! So helft mir doch!", - "Hilfe! Wir werden angegriffen!", - "Hilfe! Mörder!", - "Hilfe! Ein Mörder läuft frei herum!", - "Hilfe! Man versucht, mich zu töten!", - "Wachen, ich werde angegriffen!", - "Wachen! Ich werde angegriffen!", - "Ich werde angegriffen! Wachen!", - "Hilfe! Wachen! Ich werde angegriffen!", - "Wachen! Kommt schnell!", - "Wachen! Wachen!", - "Wachen! Ein Schurke attackiert mich!", - "Wachen, erschlagt diesen üblen Schurken!", - "Wachen! Ein Mörder!", - "Wachen! Helft mir!", - "Damit wirst du nicht davonkommen! Wachen!", - "Du Unmensch!", - "Helft mir!", - "Helft mir! Bitte!", - "Autsch! Wachen! Hilfe!", - "Sie kommen, um mich zu holen!", - "Hilfe! Hilfe! Ich werde angefeindet!", - "Ah, jetzt sehen wir die Gewalt, die dem System innewohnt.", - "Ach nur'n Kratzer!", - "Hört auf!", - "Was habe ich euch angetan?!", - "Bitte, hört auf mich anzugreifen!", - "Hey! Pass auf wo du mit dem Ding hinziehlst!", - "Abscheulicher Schuft, hinfort mit dir!", - "Halt! Verzieh dich!", - "Du bringst mich noch zur Weißglut!", - "Oi! Was denkst du wer du bist?!", - "Dafür reiße ich dir den Kopf ab!", - "Stop, bitte! Ich habe nichts wertvolles bei mir!", - "Ich setze meinen Bruder auf dich an, er ist größer als ich!", - "Neiiiin, das erzähle ich meiner Mutter!", - "Verflucht seist du!", - "Bitte, tu's nicht.", - "Das war nicht sehr nett von dir!", - "Deine Waffe erfüllt ihren Zweck, du kannst sie jetzt wegstecken!", - "Verschone mich!", - "Bitte, ich habe Familie!", - "Ich bin zu jung zum sterben!", - "Können wir darüber reden?", - "Gewalt ist keine Lösung!", - "Der heutige Tag entwickelt sich zu einem sehr schlechten Tag...", - "Hey, das tat weh!", - "AAAAHHH!", - "Wie rüpelhaft!", - "Stop, ich flehe dich an!", - "Ein Fluch soll dich heimsuchen!", - "Das ist nicht lustig.", - "Wie könnt Ihr es wagen?!", - "Dafür wirst du bezahlen!", - "Mach weiter so und es wird dir leid tun!", - "Zwing mich nicht, dir weh zu tun!", - "Das muss ein Missverständnis sein!", - "Du musst das nicht tun!", - "Fort mit dir, Schurke!", - "Das hat echt wehgetan!", - "Warum tust du das?", - "Bei den Göttern, hört auf!", - "Du musst mich mit jemand anderem verwechseln!", - "Ich verdiene das nicht!", - "Bitte, tu das nicht nochmal.", - "Wachen, in den Kerker mit diesem Schuft!", - "Ich werde meinen Tarasque auf dich hetzen!", - "Warum iiiich?", - ], - "npc.speech.villager_enemy_killed": [ - "Ich habe den Feind vernichtet!", - "Endlich Frieden!", - "... also, wo war ich stehengeblieben?", - ] } ) diff --git a/assets/voxygen/i18n/de_DE/buff.ron b/assets/voxygen/i18n/de_DE/buff.ron index f42d807832..f2e149b547 100644 --- a/assets/voxygen/i18n/de_DE/buff.ron +++ b/assets/voxygen/i18n/de_DE/buff.ron @@ -26,7 +26,7 @@ "buff.desc.cursed": "Du bist verflucht.", // Buffs stats "buff.stat.health": "Stellt {str_total} Leben wiederher", - "buff.stat.increase_max_stamina": "Erhöt die maximale Ausdauer um {strength}", + "buff.stat.increase_max_energy": "Erhöt die maximale Ausdauer um {strength}", "buff.stat.increase_max_health": "Erhöht die maximale Lebensenergie um {strength}", "buff.stat.invulnerability": "Gewährt Unverwundbarkeit", // Text diff --git a/assets/voxygen/i18n/de_DE/common.ron b/assets/voxygen/i18n/de_DE/common.ron index 856412b251..5a25a5e77e 100644 --- a/assets/voxygen/i18n/de_DE/common.ron +++ b/assets/voxygen/i18n/de_DE/common.ron @@ -60,6 +60,8 @@ Ist das Spiel auf dem neusten Stand?"#, "common.weapons.axe": "Axt", "common.weapons.sword": "Schwert", + "common.weapons.greatsword": "Zweihänder", + "common.weapons.shortswords": "Kurzschwerter", "common.weapons.staff": "Stab", "common.weapons.bow": "Bogen", "common.weapons.hammer": "Hammer", diff --git a/assets/voxygen/i18n/de_DE/hud/bag.ron b/assets/voxygen/i18n/de_DE/hud/bag.ron index fafb68eb3e..24c2252fea 100644 --- a/assets/voxygen/i18n/de_DE/hud/bag.ron +++ b/assets/voxygen/i18n/de_DE/hud/bag.ron @@ -26,7 +26,7 @@ "hud.bag.offhand": "Nebenhand", "hud.bag.bag": "Beutel", "hud.bag.health": "Leben", - "hud.bag.stamina": "Ausdauer", + "hud.bag.energy": "Ausdauer", "hud.bag.combat_rating": "Kampfwertung", "hud.bag.protection": "Schutz", "hud.bag.stun_res": "Betäubungsresistenz", diff --git a/assets/voxygen/i18n/de_DE/hud/hud_settings.ron b/assets/voxygen/i18n/de_DE/hud/settings.ron similarity index 97% rename from assets/voxygen/i18n/de_DE/hud/hud_settings.ron rename to assets/voxygen/i18n/de_DE/hud/settings.ron index b5e65a87e2..79124ffe70 100644 --- a/assets/voxygen/i18n/de_DE/hud/hud_settings.ron +++ b/assets/voxygen/i18n/de_DE/hud/settings.ron @@ -15,7 +15,7 @@ "hud.settings.relative_scaling": "Relative Skalierung", "hud.settings.custom_scaling": "Benutzerdefinierte Skalierung", "hud.settings.crosshair": "Fadenkreuz", - "hud.settings.transparency": "Transparenz", + "hud.settings.opacity": "Sichtbarkeit", "hud.settings.hotbar": "Hotbar", "hud.settings.toggle_shortcuts": "Tastenkürzel umschalten", "hud.settings.buffs_skillbar": "Effekte an der Skillbar", @@ -33,7 +33,7 @@ "hud.settings.values": "Werte", "hud.settings.percentages": "Prozentangaben", "hud.settings.chat": "Chat", - "hud.settings.background_transparency": "Hintergrundtransparenz", + "hud.settings.background_opacity": "Hintergrundsichtbarkeit", "hud.settings.chat_character_name": "Charakternamen im Chat", "hud.settings.loading_tips": "Tipps auf dem Ladebildschirm", "hud.settings.reset_interface": "Auf Standardwerte zurücksetzen", diff --git a/assets/voxygen/i18n/de_DE/skills.ron b/assets/voxygen/i18n/de_DE/hud/skills.ron similarity index 97% rename from assets/voxygen/i18n/de_DE/skills.ron rename to assets/voxygen/i18n/de_DE/hud/skills.ron index d8915a90a4..edf044c638 100644 --- a/assets/voxygen/i18n/de_DE/skills.ron +++ b/assets/voxygen/i18n/de_DE/hud/skills.ron @@ -11,8 +11,8 @@ // General "hud.skill.inc_health_title": "Leben erhöhen", "hud.skill.inc_health": "Erhöht max. Leben um {boost}{SP}", - "hud.skill.inc_stam_title": "Ausdauer", - "hud.skill.inc_stam": "Erhöht die max. Ausdauer um {boost}{SP}", + "hud.skill.inc_energy_title": "Ausdauer", + "hud.skill.inc_energy": "Erhöht die max. Ausdauer um {boost}{SP}", "hud.skill.unlck_sword_title": "Schwert freischalten", "hud.skill.unlck_sword": "Schaltet den Schwert-Fähigkeitenbaum frei{SP}", "hud.skill.unlck_axe_title": "Axt freischalten", @@ -27,8 +27,8 @@ "hud.skill.unlck_sceptre": "Schaltet den Zepter-Fähigkeitenbaum frei{SP}", "hud.skill.dodge_title": "Ausweichen", "hud.skill.dodge": "Weicht Nahkampfattacken aus{SP}", - "hud.skill.roll_stamina_title": "Ausdauerkosten beim Rollen", - "hud.skill.roll_stamina": "Rollen nutzt {boost}% weniger Ausdauer{SP}", + "hud.skill.roll_energy_title": "Ausdauerkosten beim Rollen", + "hud.skill.roll_energy": "Rollen nutzt {boost}% weniger Ausdauer{SP}", "hud.skill.roll_speed_title": "Rollgeschwindigkeit", "hud.skill.roll_speed": "Rolle um {boost}% schneller{SP}", "hud.skill.roll_dur_title": "Rolldauer", @@ -95,8 +95,8 @@ "hud.skill.st_flamethrower_damage" : "Erhöht den Schaden um {boost}%{SP}", "hud.skill.st_explosion_radius_title" : "Explosionsradius", "hud.skill.st_explosion_radius" : "Größer ist besser, erhöht den Radius der Explosion um {boost}%{SP}", - "hud.skill.st_stamina_regen_title" : "Ausdauerregeneration", - "hud.skill.st_stamina_regen" : "Erhöht den Ausdauergewinn um {boost}%{SP}", + "hud.skill.st_energy_regen_title" : "Ausdauerregeneration", + "hud.skill.st_energy_regen" : "Erhöht den Ausdauergewinn um {boost}%{SP}", "hud.skill.st_fireball_title" : "Feuerball", "hud.skill.st_fireball" : "Sende brennende Kugeln in Richtung deiner Feinde", "hud.skill.st_damage_title" : "Schaden", diff --git a/assets/voxygen/i18n/de_DE/main.ron b/assets/voxygen/i18n/de_DE/main.ron index eadf4766a8..154bca3479 100644 --- a/assets/voxygen/i18n/de_DE/main.ron +++ b/assets/voxygen/i18n/de_DE/main.ron @@ -66,5 +66,26 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "Mit 'G' aktivierst du deine Laterne.", + "Mit 'F1' kannst du die Standard-Tastenbelegung einsehen.", + "Mit /say oder /s kannst du mit Spielern in deiner unmittelbaren Umgebung chatten.", + "Mit /region oder /r kannst du mit Spielern chatten, die sich ein paar hundert Meter um dich herum befinden.", + "Als Admin kannst du mit /build den Baumodus nutzen.", + "Mit /group oder /g können deine Nachrichten nur von Mitgliedern deiner Gruppe gelesen werden.", + "Um private Nachrichten zu verschicken, kannst du /tell, gefolgt vom Namen des Spielers, der die Nachricht erhalten soll, nutzen.", + "Halte Ausschau nach Essen, Kisten oder anderen Schätzen überall auf der Welt!", + "Deine Taschen sind voll mit Essen? Versuche besseres Essen damit herzustellen!", + "Du weißt nicht was du tun sollst? Versuche dich doch mal an den Katakomben, die auf der Karte eingezeichnet sind!", + "Vergiss nicht die Grafikeinstellungen an deine Hardware anzupassen. Mit 'N' öffnest du die Grafikeinstellungen.", + "Mit Anderen zusammen zu spielen macht noch viel mehr Spaß! Mit 'O' kannst du sehen, wer gerade online ist.", + "Mit 'J' kannst du das Tanzbein schwingen. Party!", + "Mit 'L-Shift' kannst du den Gleiter öffnen und hohe Lüfte erobern.", + "Veloren ist immernoch in der Pre-Alpha. Wir tun unser Bestes, um es jeden Tag ein Stück besser zu machen!", + "Du möchtest aktiv an der Entwicklung mitwirken oder einfach nur chatten? Dann komm auf unseren Discord-Server.", + "Die Art, wie dir deine Lebensenergie angezeigt wird, kannst du in den Einstellungen ändern.", + "Setze dich neben ein Lagerfeuer (drücke dafür 'K') um dich langsam von deinen Verletzungen zu erholen.", + "Du brauchst einen größeren Beutel oder bessere Rüstung? Dann drücke 'C', um das Crafting-Menü zu öffnen!", + ], } ) diff --git a/assets/voxygen/i18n/de_DE/npc.ron b/assets/voxygen/i18n/de_DE/npc.ron new file mode 100644 index 0000000000..64dcbda40b --- /dev/null +++ b/assets/voxygen/i18n/de_DE/npc.ron @@ -0,0 +1,183 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Germany German +( + string_map: { + + }, + + vector_map: { + "npc.speech.villager": [ + "Ist das nicht ein wunderbarer Tag?", + "Wie geht's dir heute?", + "Einen schönen guten Morgen wünsche ich dir!", + "Ich frage mich, was der Catoblepas denkt, wenn er Gras isst.", + "Was hälst du vom Wetter?", + "Wenn ich an die Katakomben denke, bekomme ich Angst. Ich hoffe jemand befreit sie vom Unheil.", + "Ich würde gerne in eine der Höhlen gehen, wenn ich größer und stärker bin.", + "Hast du meine Katze gesehen?", + "Hast du schonmal etwas von den gefährlichen Landhaien gehört? Ich habe gehört sie leben in der Wüste.", + "Man sagt, es gebe Juwelen aller Art in den Höhlen der Gegend.", + "Ich bin einfach verrückt nach Käse!", + "Möchtest du nicht rein kommen? Bei uns gibt es gleich himmlischen Käse!", + "Man sagt Pilze seien gut für die Gesundheit. Ich selber habe noch nie welche gegessen.", + "Vergiss dein Feuerwerk nicht!", + "Ich liebe Zwergenkäse einfach. Ich wünschte, ich könnte selber welchen herstellen.", + "Ich frage mich, was sich auf der anderen Seite der Berge verbirgt.", + "Ich wünschte, ich könnte mir eines Tages meinen eigenen Gleiter bauen.", + "Möchtest du dir mal meinen Garten ansehen? Okay, vielleicht beim nächsten Mal.", + "Ein perfekter Tag, um in den Wald zu gehen!", + "Sein oder nicht sein? Ich glaube ich werde Landwirt.", + "Findest du nicht auch, dass unser Dorf das schönste ist?", + "Was glaubst du, was glühende Überreste zum Glühen bringt?", + "Ich glaube es ist an der Zeit für ein zweites Frühstück!", + "Hast du jemals ein Glühwürmchen gefangen?", + "Ich verstehe einfach nicht wo die ganzen Sauroks herkommen.", + "Ich wünschte, jemand könnte die ganzen Wölfe vom Dorf fernhalten.", + "Ich hatte letzte Nacht einen wunderbaren Traum über Käse. Was könnte das bedeuten?", + "Ich habe meinem Bruder ein wenig Käse dagelassen. Nun weiß ich nicht, ob er ihn schon aß oder nicht. Ich nenne es Schrödingers Käse.", + "Ich habe meiner Schwester ein wenig Käse dagelassen. Nun weiß ich nicht, ob sie ihn schon aß oder nicht. Ich nenne es Schrödingers Käse.", + "Jemand sollte etwas gegen diese Kultisten unternehmen. Vorzugsweise nicht ich.", + "Ich hoffe es regnet bald wieder. Der Ernte würde es gut tun.", + "Ich liebe Honig! Und hasse Bienen.", + "Eines Tages möchte ich die Welt bereisen. Es muss doch mehr im Leben geben als dieses Dorf.", + ], + "npc.speech.villager_decline_trade": [ + "Tut mir leid, ich habe nichts, was ich mit dir handeln könnte.", + "Du möchtest handeln? Als ob ich etwas hätte, das dich interessieren könnte.", + "Mein Haus gehört mir, ich würde es für nichts auf der Welt hergeben.", + ], + "npc.speech.merchant_advertisement": [ + "Darf ich dir einen Handel vorschlagen?", + "Möchtest du mit mir handeln?", + "Ich habe viele Waren. Möchtest du sie dir mal ansehen?" + ], + "npc.speech.merchant_busy": [ + "Hey, warte bis du an der Reihe bist.", + "Einen Moment bitte, ich kann mich nicht zerteilen.", + "Siehst du nicht, dass jemand vor dir steht?", + "Einen Moment bitte, ich bin gleich bei dir.", + "Nicht vordrängeln bitte.", + "Ich bin gerade beschäftigt, komm doch später wieder." + ], + "npc.speech.merchant_trade_successful": [ + "Vielen Dank für das Geschäft!", + "Vielen Dank!", + ], + "npc.speech.merchant_trade_declined": [ + "Vielleicht beim nächsten Mal, einen schönen Tag noch!", + "Zu schade, vielleicht beim nächsten mal, bis dann!" + ], + "npc.speech.villager_cultist_alarm": [ + "Achtung! Ein Kultist schleicht hier herum!", + "Zu den Waffen! Die Kultisten attackieren uns!", + "Wie können die Kultisten es wagen, unser Dorf zu attackieren!", + "Tod den Kultisten!", + "Kultisten werden hier nicht toleriert!", + "Mörderischer Kultist!", + "Eine Kostprobe meines Schwertes gefällig? Du reudiger Kultist!", + "Nichts kann das Blut von deinen Händen waschen, Kultist!", + "Ja brat' mir doch einer 'nen Storch! Ein Kultist weilt unter uns!", + "Die Schreckensherrschaft der Kultisten wird bald vorrüber sein!", + "Der Kultist gehört mir!", + "Bereite dich darauf vor deinem Schöpfer gegenüberzutreten, du mieser Kultist!", + "Ich sehe einen Kultisten! Ergreift ihn!", + "Ich sehe einen Kultisten!", + "Ich sehe einen Kultisten! Lasst ihn nicht entkommen!", + "Ist der ehrenwerte Kultist an ein wenig TOD interessiert?!", + "Wir vergeben nicht! Wir vergessen nicht! Kultist, büße!", + "Stirb, Kultist!", + "Deine Schreckensherrschaft wird enden!", + "Nimm das! Für alles, was du getan hast!", + "Wir wollen Abschaum wie euch hier nicht!", + "Du hättest unter der Erde bleiben sollen! Da wo du hingehörst!", + ], + "npc.speech.villager_under_attack": [ + "Hilfe, ich werde angegriffen!", + "Hilfe, ich werde angegriffen!", + "Autsch! ich werde angegriffen!", + "Autsch! ich werde angegriffen! Hilfe!", + "Helft mir! Ich werde angegriffen!", + "Ich werde angegriffen! Hilfe!", + "Ich werde angegriffen! Helft mir!", + "Hilfe!", + "Hilfe! Hilfe!", + "Hilfe! Hilfe! Hilfe!", + "Ich werde angegriffen!", + "AAAHHH! Ich werde angegriffen!", + "AAAHHH! Ich werde angegriffen! So helft mir doch!", + "Hilfe! Wir werden angegriffen!", + "Hilfe! Mörder!", + "Hilfe! Ein Mörder läuft frei herum!", + "Hilfe! Man versucht, mich zu töten!", + "Wachen, ich werde angegriffen!", + "Wachen! Ich werde angegriffen!", + "Ich werde angegriffen! Wachen!", + "Hilfe! Wachen! Ich werde angegriffen!", + "Wachen! Kommt schnell!", + "Wachen! Wachen!", + "Wachen! Ein Schurke attackiert mich!", + "Wachen, erschlagt diesen üblen Schurken!", + "Wachen! Ein Mörder!", + "Wachen! Helft mir!", + "Damit wirst du nicht davonkommen! Wachen!", + "Du Unmensch!", + "Helft mir!", + "Helft mir! Bitte!", + "Autsch! Wachen! Hilfe!", + "Sie kommen, um mich zu holen!", + "Hilfe! Hilfe! Ich werde angefeindet!", + "Ah, jetzt sehen wir die Gewalt, die dem System innewohnt.", + "Ach nur'n Kratzer!", + "Hört auf!", + "Was habe ich euch angetan?!", + "Bitte, hört auf mich anzugreifen!", + "Hey! Pass auf wo du mit dem Ding hinziehlst!", + "Abscheulicher Schuft, hinfort mit dir!", + "Halt! Verzieh dich!", + "Du bringst mich noch zur Weißglut!", + "Oi! Was denkst du wer du bist?!", + "Dafür reiße ich dir den Kopf ab!", + "Stop, bitte! Ich habe nichts wertvolles bei mir!", + "Ich setze meinen Bruder auf dich an, er ist größer als ich!", + "Neiiiin, das erzähle ich meiner Mutter!", + "Verflucht seist du!", + "Bitte, tu's nicht.", + "Das war nicht sehr nett von dir!", + "Deine Waffe erfüllt ihren Zweck, du kannst sie jetzt wegstecken!", + "Verschone mich!", + "Bitte, ich habe Familie!", + "Ich bin zu jung zum sterben!", + "Können wir darüber reden?", + "Gewalt ist keine Lösung!", + "Der heutige Tag entwickelt sich zu einem sehr schlechten Tag...", + "Hey, das tat weh!", + "AAAAHHH!", + "Wie rüpelhaft!", + "Stop, ich flehe dich an!", + "Ein Fluch soll dich heimsuchen!", + "Das ist nicht lustig.", + "Wie könnt Ihr es wagen?!", + "Dafür wirst du bezahlen!", + "Mach weiter so und es wird dir leid tun!", + "Zwing mich nicht, dir weh zu tun!", + "Das muss ein Missverständnis sein!", + "Du musst das nicht tun!", + "Fort mit dir, Schurke!", + "Das hat echt wehgetan!", + "Warum tust du das?", + "Bei den Göttern, hört auf!", + "Du musst mich mit jemand anderem verwechseln!", + "Ich verdiene das nicht!", + "Bitte, tu das nicht nochmal.", + "Wachen, in den Kerker mit diesem Schuft!", + "Ich werde meinen Tarasque auf dich hetzen!", + "Warum iiiich?", + ], + "npc.speech.villager_enemy_killed": [ + "Ich habe den Feind vernichtet!", + "Endlich Frieden!", + "... also, wo war ich stehengeblieben?", + ] + } +) diff --git a/assets/voxygen/i18n/en/_manifest.ron b/assets/voxygen/i18n/en/_manifest.ron index 087c8d13ef..993c8fd4dd 100644 --- a/assets/voxygen/i18n/en/_manifest.ron +++ b/assets/voxygen/i18n/en/_manifest.ron @@ -28,206 +28,5 @@ asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", scale_ratio: 1.0, ), - }, - - string_map: { - - }, - - vector_map: { - "loading.tips": [ - "Press 'G' to light your lantern.", - "Press 'F1' to see all default keybindings.", - "You can type /say or /s to only chat with players directly around you.", - "You can type /region or /r to only chat with players a couple of hundred blocks around you.", - "Admins can use the /build command to enter build mode.", - "You can type /group or /g to only chat with players in your current group.", - "To send private messages type /tell followed by a player name and your message.", - "Keep an eye out for food, chests and other loot spread all around the world!", - "Inventory filled with food? Try crafting better food from it!", - "Wondering what there is to do? Try out one of the dungeons marked on the map!", - "Don't forget to adjust the graphics for your system. Press 'N' to open the settings.", - "Playing with others is fun! Press 'O' to see who is online.", - "Press 'J' to dance. Party!", - "Press 'L-Shift' to open your Glider and conquer the skies.", - "Veloren is still in Pre-Alpha. We do our best to improve it every day!", - "If you want to join the dev team or just have a chat with us, join our Discord server.", - "You can toggle showing your amount of health on the healthbar in the settings.", - "Sit near a campfire (with the 'K' key) to slowly recover from your injuries.", - "Need more bags or better armor to continue your journey? Press 'C' to open the crafting menu!", - "Try jumping when rolling through creatures.", - ], - "npc.speech.villager": [ - "Isn't it such a lovely day?", - "How are you today?", - "Top of the morning to you!", - "I wonder what the Catoblepas thinks when it eats grass.", - "What do you think about this weather?", - "Thinking about those dungeons makes me scared. I hope someone will clear them out.", - "I'd like to go spelunking in a cave when I'm stronger.", - "Have you seen my cat?", - "Have you ever heard of the ferocious Land Sharks? I hear they live in deserts.", - "They say shiny gems of all kinds can be found in caves.", - "I'm just crackers about cheese!", - "Won't you come in? We were just about to have some cheese!", - "They say mushrooms are good for your health. Never eat them myself.", - "Don't forget the crackers!", - "I simply adore dwarven cheese. I wish I could make it.", - "I wonder what is on the other side of the mountains.", - "I hope to make my own glider someday.", - "Would you like to see my garden? Okay, maybe some other time.", - "Lovely day for a stroll in the woods!", - "To be, or not to be? I think I'll be a farmer.", - "Don't you think our village is the best?", - "What do you suppose makes Glowing Remains glow?", - "I think it's time for second breakfast!", - "Have you ever caught a firefly?", - "I just can't understand where those Sauroks keep coming from.", - "I wish someone would keep the wolves away from the village.", - "I had a wonderful dream about cheese last night. What does it mean?", - "I left some cheese with my brother. Now I don't know if it exists or not. I call it Schrödinger's cheese.", - "I left some cheese with my sister. Now I don't know if it exists or not. I call it Schrödinger's cheese.", - "Someone should do something about those cultists. Preferably not me.", - "I hope it rains soon. Would be good for the crops.", - "I love honey! And I hate bees.", - "I want to see the world one day. There's got to be more to life than this village.", - ], - "npc.speech.villager_decline_trade": [ - "Sorry, I don't have anything to trade.", - "Trade? Like I got anything that may interest you.", - "My house is mine, I won't trade it for anything.", - ], - "npc.speech.merchant_advertisement": [ - "Can I interest you in a trade?", - "Do you want to trade with me?", - "I have plenty of goods, Do you want to take a look?" - ], - "npc.speech.merchant_busy": [ - "Hey, wait your turn.", - "Please wait, I'm only one person.", - "Do you see the other person in front of you?", - "Just a moment, let me finish.", - "No cutting in line.", - "I'm busy, come back later." - ], - "npc.speech.merchant_trade_successful": [ - "Thank you for trading with me!", - "Thank you!", - ], - "npc.speech.merchant_trade_declined": [ - "Maybe another time, have a good day!", - "Too bad, maybe next time, then!" - ], - "npc.speech.villager_cultist_alarm": [ - "Lookout! There is a cultist on the loose!", - "To arms! The cultists are attacking!", - "How dare the cultists attack our village!", - "Death to the cultists!", - "Cultists will not be tolerated here!", - "Murderous cultist!", - "Taste the edge of my sword, you dirty cultist!", - "Nothing can clean the blood from your hands, cultist!", - "Billions of blistering blue barnacles! A cultist among us!", - "The evils of this cultist are about to be over!", - "This cultist is mine!", - "Prepare to meet your maker, foul cultist!", - "I see a cultist! Get them!", - "I see a cultist! Attack!", - "I see a cultist! Don't let them escape!", - "Would the most honorable cultist care for some DEATH?!", - "Never forgive! Never forget! Cultist, regret!", - "Die, cultist!", - "Your reign of terror will seize!", - "Here's for all that you've done!", - "We don't take kindly to your types around here.", - "You should have stayed underground!", - ], - "npc.speech.villager_under_attack": [ - "Help, I'm under attack!", - "Help! I'm under attack!", - "Ouch! I'm under attack!", - "Ouch! I'm under attack! Help!", - "Help me! I'm under attack!", - "I'm under attack! Help!", - "I'm under attack! Help me!", - "Help!", - "Help! Help!", - "Help! Help! Help!", - "I'm under attack!", - "AAAHHH! I'm under attack!", - "AAAHHH! I'm under attack! Help!", - "Help! We're under attack!", - "Help! Murderer!", - "Help! There's a murderer on the loose!", - "Help! They're trying to kill me!", - "Guards, I'm under attack!", - "Guards! I'm under attack!", - "I'm under attack! Guards!", - "Help! Guards! I'm under attack!", - "Guards! Come quick!", - "Guards! Guards!", - "Guards! There's a villain attacking me!", - "Guards, slay this foul villain!", - "Guards! There's a murderer!", - "Guards! Help me!", - "You won't get away with this! Guards!", - "You fiend!", - "Help me!", - "Help! Please!", - "Ouch! Guards! Help!", - "They're coming for me!", - "Help! Help! I'm being repressed!", - "Ah, now we see the violence inherent in the system.", - "'Tis but a scratch!", - "Stop that!", - "What did I ever do to you?!", - "Please stop attacking me!", - "Hey! Watch where you point that thing!", - "Heinous wretch, be gone with you!", - "Stop it! Go away!", - "Now you're making me mad!", - "Oi! Who do you think you are?!", - "I'll have your head for that!", - "Stop, please! I carry nothing of value!", - "I'll set my brother on you, he's bigger than I am!", - "Nooo, I'm telling mother!", - "Curse you!", - "Please don't do that.", - "That wasn't very nice!", - "Your weapon works, you can put it away now!", - "Spare me!", - "Please, I have a family!", - "I'm too young to die!", - "Can we talk about this?", - "Violence is never the answer!", - "Today is turning out to be a very bad day...", - "Hey, that hurt!", - "Eek!", - "How rude!", - "Stop, I beg you!", - "A pox upon you!", - "This isn't fun.", - "How dare you?!", - "You'll pay for that!", - "Keep that up and you'll be sorry!", - "Don't make me hurt you!", - "There must be some misunderstanding!", - "You don't need to do this!", - "Be gone, fiend!", - "That really hurt!", - "Why would you do that?", - "By the spirits, cease!", - "You must have me confused with someone else!", - "I don't deserve this!", - "Please don't do that again.", - "Guards, throw this monster in the lake!", - "I'll set my tarasque on you!", - "Why meeeeeee?", - ], - "npc.speech.villager_enemy_killed": [ - "I have destroyed my enemy!", - "Finally at peace!", - "... now what was I doing?", - ] } ) diff --git a/assets/voxygen/i18n/en/buff.ron b/assets/voxygen/i18n/en/buff.ron index e2c4ed10c6..8b63f58575 100644 --- a/assets/voxygen/i18n/en/buff.ron +++ b/assets/voxygen/i18n/en/buff.ron @@ -38,7 +38,7 @@ "buff.desc.ensnared": "Vines grasp at your legs, impeding your movement.", // Buffs stats "buff.stat.health": "Restores {str_total} Health", - "buff.stat.increase_max_stamina": "Raises Maximum Stamina by {strength}", + "buff.stat.increase_max_energy": "Raises Maximum Energy by {strength}", "buff.stat.increase_max_health": "Raises Maximum Health by {strength}", "buff.stat.invulnerability": "Grants invulnerability", // Text diff --git a/assets/voxygen/i18n/en/common.ron b/assets/voxygen/i18n/en/common.ron index 1dd6bf2c10..efbffbcda5 100644 --- a/assets/voxygen/i18n/en/common.ron +++ b/assets/voxygen/i18n/en/common.ron @@ -61,12 +61,14 @@ Is the client up to date?"#, "common.species.danari": "Danari", "common.weapons.axe": "Axe", + "common.weapons.greatsword": "Greatsword", + "common.weapons.shortswords": "Shortswords", "common.weapons.sword": "Sword", - "common.weapons.staff": "Staff", + "common.weapons.staff": "Firestaff", "common.weapons.bow": "Bow", "common.weapons.hammer": "Hammer", "common.weapons.general": "General Combat", - "common.weapons.sceptre": "Sceptre", + "common.weapons.sceptre": "Healing Sceptre", "common.weapons.shield": "Shield", "common.weapons.spear": "Spear", "common.weapons.hammer_simple": "Simple Hammer", diff --git a/assets/voxygen/i18n/en/gameinput.ron b/assets/voxygen/i18n/en/gameinput.ron index ab2605acb3..316b489ace 100644 --- a/assets/voxygen/i18n/en/gameinput.ron +++ b/assets/voxygen/i18n/en/gameinput.ron @@ -21,6 +21,7 @@ "gameinput.help": "Toggle Help Window", "gameinput.toggleinterface": "Toggle Interface", "gameinput.toggledebug": "Toggle FPS and Debug Info", + "gameinput.toggle_egui_debug": "Toggle EGUI Debug Info", "gameinput.togglechat": "Toggle Chat", "gameinput.screenshot": "Take Screenshot", "gameinput.toggleingameui": "Toggle Nametags", diff --git a/assets/voxygen/i18n/en/hud/bag.ron b/assets/voxygen/i18n/en/hud/bag.ron index b8a0271369..b427d18147 100644 --- a/assets/voxygen/i18n/en/hud/bag.ron +++ b/assets/voxygen/i18n/en/hud/bag.ron @@ -30,13 +30,13 @@ "hud.bag.swap_equipped_weapons_desc": "Press {key}", "hud.bag.bag": "Bag", "hud.bag.health": "Health", - "hud.bag.stamina": "Stamina", + "hud.bag.energy": "Energy", "hud.bag.combat_rating": "Combat Rating", "hud.bag.protection": "Protection", "hud.bag.stun_res": "Stun Resilience", "hud.bag.combat_rating_desc": "Calculated from your\nequipment and health.", "hud.bag.protection_desc": "Damage reduction through armor", - "hud.bag.stun_res_desc": "Resilience against being stunned by consecutive hits.\nRegenerates like Stamina.", + "hud.bag.stun_res_desc": "Resilience against being stunned by consecutive hits.\nRegenerates like Energy.", "hud.bag.sort_by_name": "Sort by Name", "hud.bag.sort_by_quality": "Sort by Quality", "hud.bag.sort_by_category": "Sort by Category", diff --git a/assets/voxygen/i18n/en/hud/sct.ron b/assets/voxygen/i18n/en/hud/sct.ron index e044a9ebb4..62e47bc983 100644 --- a/assets/voxygen/i18n/en/hud/sct.ron +++ b/assets/voxygen/i18n/en/hud/sct.ron @@ -6,7 +6,7 @@ // SCT outputs "hud.sct.experience": "{amount} Exp", "hud.sct.block": "BLOCKED", - }, + }, vector_map: { diff --git a/assets/voxygen/i18n/en/hud/hud_settings.ron b/assets/voxygen/i18n/en/hud/settings.ron similarity index 98% rename from assets/voxygen/i18n/en/hud/hud_settings.ron rename to assets/voxygen/i18n/en/hud/settings.ron index 83f6624406..f16d039e63 100644 --- a/assets/voxygen/i18n/en/hud/hud_settings.ron +++ b/assets/voxygen/i18n/en/hud/settings.ron @@ -17,7 +17,7 @@ "hud.settings.relative_scaling": "Relative Scaling", "hud.settings.custom_scaling": "Custom Scaling", "hud.settings.crosshair": "Crosshair", - "hud.settings.transparency": "Transparency", + "hud.settings.opacity": "Opacity", "hud.settings.hotbar": "Hotbar", "hud.settings.toggle_shortcuts": "Toggle Shortcuts", "hud.settings.buffs_skillbar": "Buffs at Skillbar", @@ -35,7 +35,7 @@ "hud.settings.values": "Values", "hud.settings.percentages": "Percentages", "hud.settings.chat": "Chat", - "hud.settings.background_transparency": "Background Transparency", + "hud.settings.background_opacity": "Background Opacity", "hud.settings.chat_character_name": "Character Names in chat", "hud.settings.loading_tips": "Loading Screen Tips", "hud.settings.reset_interface": "Reset to Defaults", diff --git a/assets/voxygen/i18n/en/skills.ron b/assets/voxygen/i18n/en/hud/skills.ron similarity index 93% rename from assets/voxygen/i18n/en/skills.ron rename to assets/voxygen/i18n/en/hud/skills.ron index 472af3e40e..de0fd8aced 100644 --- a/assets/voxygen/i18n/en/skills.ron +++ b/assets/voxygen/i18n/en/hud/skills.ron @@ -11,8 +11,8 @@ // General "hud.skill.inc_health_title": "Increase Health", "hud.skill.inc_health": "Increases max health by {boost}{SP}", - "hud.skill.inc_stam_title": "Increase Stamina", - "hud.skill.inc_stam": "Increases max stamina by {boost}{SP}", + "hud.skill.inc_energy_title": "Increase Energy", + "hud.skill.inc_energy": "Increases max energy by {boost}{SP}", "hud.skill.unlck_sword_title": "Unlock Sword", "hud.skill.unlck_sword": "Unlocks sword skill tree{SP}", "hud.skill.unlck_axe_title": "Unlock Axe", @@ -27,8 +27,8 @@ "hud.skill.unlck_sceptre": "Unlocks sceptre skill tree{SP}", "hud.skill.dodge_title": "Dodge", "hud.skill.dodge": "Dodge rolls are triggered with middle-click, and grant temporary immunity to melee attacks (iframes) while you're rolling.", - "hud.skill.roll_stamina_title": "Roll Stamina Cost", - "hud.skill.roll_stamina": "Rolling uses {boost}% less stamina{SP}", + "hud.skill.roll_energy_title": "Roll Energy Cost", + "hud.skill.roll_energy": "Rolling uses {boost}% less energy{SP}", "hud.skill.roll_speed_title": "Rolling Speed", "hud.skill.roll_speed": "Roll {boost}% faster{SP}", "hud.skill.roll_dur_title": "Rolling Duration", @@ -36,7 +36,7 @@ "hud.skill.climbing_title": "Climbing", "hud.skill.climbing": "Jumping higher", "hud.skill.climbing_cost_title": "Climbing Cost", - "hud.skill.climbing_cost": "Climbing uses {boost}% less stamina{SP}", + "hud.skill.climbing_cost": "Climbing uses {boost}% less energy{SP}", "hud.skill.climbing_speed_title": "Climbing Speed", "hud.skill.climbing_speed": "Climb {boost}% faster{SP}", "hud.skill.swim_title": "Swimming", @@ -52,14 +52,14 @@ "hud.skill.sc_lifesteal_range": "Your beam reaches {boost}% further{SP}", "hud.skill.sc_lifesteal_lifesteal_title": "Lifesteal", "hud.skill.sc_lifesteal_lifesteal": "Convert an additional {boost}% of damage into health{SP}", - "hud.skill.sc_lifesteal_regen_title": "Stamina Regen", - "hud.skill.sc_lifesteal_regen": "Replenish your stamina by an additional {boost}%{SP}", + "hud.skill.sc_lifesteal_regen_title": "Energy Regen", + "hud.skill.sc_lifesteal_regen": "Replenish your energy by an additional {boost}%{SP}", "hud.skill.sc_heal_title": "Healing Aura", "hud.skill.sc_heal": "Heal your allies using the blood of your enemies, requires combo to activate", "hud.skill.sc_heal_heal_title": "Heal", "hud.skill.sc_heal_heal": "Increases the amount you heal by {boost}%{SP}", - "hud.skill.sc_heal_cost_title": "Stamina Cost", - "hud.skill.sc_heal_cost": "Healing requires {boost}% less stamina{SP}", + "hud.skill.sc_heal_cost_title": "Energy Cost", + "hud.skill.sc_heal_cost": "Healing requires {boost}% less energy{SP}", "hud.skill.sc_heal_duration_title": "Duration", "hud.skill.sc_heal_duration": "The effects of your healing aura last {boost}% longer{SP}", "hud.skill.sc_heal_range_title": "Radius", @@ -72,7 +72,7 @@ "hud.skill.sc_wardaura_duration": "The effects of your ward last {boost}% longer{SP}", "hud.skill.sc_wardaura_range_title": "Radius", "hud.skill.sc_wardaura_range": "Your ward reaches {boost}% further{SP}", - "hud.skill.sc_wardaura_cost_title": "Stamina Cost", + "hud.skill.sc_wardaura_cost_title": "Energy Cost", "hud.skill.sc_wardaura_cost": "Creating the ward requries {boost}% less energy{SP}", // Staff "hud.skill.st_shockwave_range_title" : "Shockwave Range", @@ -97,8 +97,8 @@ "hud.skill.st_flamethrower_damage" : "Increases damage by {boost}%{SP}", "hud.skill.st_explosion_radius_title" : "Explosion Radius", "hud.skill.st_explosion_radius" : "Bigger is better, increases explosion radius by {boost}%{SP}", - "hud.skill.st_stamina_regen_title" : "Stamina Regen", - "hud.skill.st_stamina_regen" : "Increases stamina gain by {boost}%{SP}", + "hud.skill.st_energy_regen_title" : "Energy Regen", + "hud.skill.st_energy_regen" : "Increases energy gain by {boost}%{SP}", "hud.skill.st_fireball_title" : "Fireball", "hud.skill.st_fireball" : "Shoots a fireball that explodes on impact", "hud.skill.st_damage_title" : "Damage", @@ -111,7 +111,7 @@ "hud.skill.bow_charged_damage_title" : "Charged Damage", "hud.skill.bow_charged_damage" : "Increases damage by {boost}%{SP}", "hud.skill.bow_charged_energy_regen_title" : "Charged Regen", - "hud.skill.bow_charged_energy_regen" : "Increases stamina recovery by {boost}%{SP}", + "hud.skill.bow_charged_energy_regen" : "Increases energy recovery by {boost}%{SP}", "hud.skill.bow_charged_knockback_title" : "Charged Knockback", "hud.skill.bow_charged_knockback" : "Knock enemies further back by {boost}%{SP}", "hud.skill.bow_charged_speed_title" : "Charged Speed", @@ -162,7 +162,7 @@ "hud.skill.hmr_single_strike_title" : "Single Strike", "hud.skill.hmr_single_strike" : "As single as you are", "hud.skill.hmr_single_strike_regen_title" : "Single Strike Regen", - "hud.skill.hmr_single_strike_regen" : "Increases stamina gain with each successive strike{SP}", + "hud.skill.hmr_single_strike_regen" : "Increases energy gain with each successive strike{SP}", "hud.skill.hmr_single_strike_speed_title" : "Single Strike Speed", "hud.skill.hmr_single_strike_speed" : "Increases the attack speed with each successive strike{SP}", "hud.skill.hmr_single_strike_damage_title" : "Single Strike Damage", @@ -180,7 +180,7 @@ "hud.skill.sw_trip_str_sp_title": "Triple Strike Speed", "hud.skill.sw_trip_str_sp": "Increases attack speed gained by each successive strike{SP}", "hud.skill.sw_trip_str_reg_title": "Triple Strike Regen", - "hud.skill.sw_trip_str_reg": "Increases stamina gain on each successive strike{SP}", + "hud.skill.sw_trip_str_reg": "Increases energy gain on each successive strike{SP}", "hud.skill.sw_dash_title": "Dash", "hud.skill.sw_dash": "Pin through your enemies", "hud.skill.sw_dash_dmg_title": "Dash Damage", @@ -217,7 +217,7 @@ "hud.skill.axe_double_strike_speed_title": "Double Strike Speed", "hud.skill.axe_double_strike_speed": "Increases the attack speed with each successive strike{SP}", "hud.skill.axe_double_strike_regen_title": "Double Strike Regen", - "hud.skill.axe_double_strike_regen": "Increases stamina gain with each successive strike{SP}", + "hud.skill.axe_double_strike_regen": "Increases energy gain with each successive strike{SP}", "hud.skill.axe_spin_title": "Axe Spin", "hud.skill.axe_spin": "You spin it right round ...", "hud.skill.axe_infinite_axe_spin_title": "Infinite Axe Spin", @@ -229,7 +229,7 @@ "hud.skill.axe_spin_speed_title": "Spin Speed", "hud.skill.axe_spin_speed": "Increases your spin speed by {boost}%{SP}", "hud.skill.axe_spin_cost_title": "Spin Cost", - "hud.skill.axe_spin_cost": "Decreases stamina cost of spinning by {boost}%{SP}", + "hud.skill.axe_spin_cost": "Decreases energy cost of spinning by {boost}%{SP}", "hud.skill.axe_unlock_leap_title": "Unlock Leap", "hud.skill.axe_unlock_leap": "Unlocks a leap spin{SP}", "hud.skill.axe_leap_damage_title": "Leap Damage", diff --git a/assets/voxygen/i18n/en/hud/trade.ron b/assets/voxygen/i18n/en/hud/trade.ron index 3298ff71aa..8528c3a5ca 100644 --- a/assets/voxygen/i18n/en/hud/trade.ron +++ b/assets/voxygen/i18n/en/hud/trade.ron @@ -3,24 +3,26 @@ /// Localization for "global" English ( string_map: { - "hud.trade.trade_window": "Trade window", - "hud.trade.phase1_description": "Drag the items you want to trade\n into the corresponding area.", - "hud.trade.phase2_description": "The trade is now locked to give you\n time to review it.", - /// Phase3 should only be visible for a few milliseconds if everything is working properly, but is included for completeness - "hud.trade.phase3_description": "Trade is being processed.", - "hud.trade.persons_offer": "{playername}'s offer", - "hud.trade.has_accepted": "{playername}\nhas accepted", - "hud.trade.accept": "Accept", - "hud.trade.decline": "Decline", - "hud.trade.invite_sent": "Trade request sent to {playername}.", - "hud.trade.result.completed": "Trade completed successfully.", - "hud.trade.result.declined": "Trade declined.", - "hud.trade.result.nospace": "Not enough space to complete the trade.", - "hud.trade.buy_price": "Buy price", - "hud.trade.sell_price": "Sell Price", - "hud.trade.coin": "coin(s)", - "hud.trade.tooltip_hint_1": "", - "hud.trade.tooltip_hint_2": "", + "hud.trade.trade_window": "Trade", + "hud.trade.phase1_description": "Drag the items you want to trade\n into the corresponding area.", + "hud.trade.phase2_description": "The trade is now locked to give you\n time to review it.", + /// Phase3 should only be visible for a few milliseconds if everything is working properly, but is included for completeness + "hud.trade.phase3_description": "Trade is being processed.", + "hud.trade.persons_offer": "{playername}'s offer", + "hud.trade.has_accepted": "{playername}\nhas accepted", + "hud.trade.accept": "Accept", + "hud.trade.decline": "Decline", + "hud.trade.invite_sent": "Trade request sent to {playername}.", + "hud.trade.result.completed": "Trade completed successfully.", + "hud.trade.result.declined": "Trade declined.", + "hud.trade.result.nospace": "Not enough space to complete the trade.", + "hud.trade.buy_price": "Buy price", + "hud.trade.sell_price": "Sell Price", + "hud.trade.coin": "coin(s)", + "hud.trade.tooltip_hint_1": "", + "hud.trade.tooltip_hint_2": "", + "hud.trade.your_offer": "Your offer", + "hud.trade.their_offer": "Their offer", }, diff --git a/assets/voxygen/i18n/en/main.ron b/assets/voxygen/i18n/en/main.ron index 6bf3f4da12..3f48895725 100644 --- a/assets/voxygen/i18n/en/main.ron +++ b/assets/voxygen/i18n/en/main.ron @@ -66,5 +66,27 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "Press 'G' to light your lantern.", + "Press 'F1' to see all default keybindings.", + "You can type /say or /s to only chat with players directly around you.", + "You can type /region or /r to only chat with players a couple of hundred blocks around you.", + "Admins can use the /build command to enter build mode.", + "You can type /group or /g to only chat with players in your current group.", + "To send private messages type /tell followed by a player name and your message.", + "Keep an eye out for food, chests and other loot spread all around the world!", + "Inventory filled with food? Try crafting better food from it!", + "Wondering what there is to do? Try out one of the dungeons marked on the map!", + "Don't forget to adjust the graphics for your system. Press 'N' to open the settings.", + "Playing with others is fun! Press 'O' to see who is online.", + "Press 'J' to dance. Party!", + "Press 'L-Shift' to open your Glider and conquer the skies.", + "Veloren is still in Pre-Alpha. We do our best to improve it every day!", + "If you want to join the dev team or just have a chat with us, join our Discord server.", + "You can toggle showing your amount of health on the healthbar in the settings.", + "Sit near a campfire (with the 'K' key) to slowly recover from your injuries.", + "Need more bags or better armor to continue your journey? Press 'C' to open the crafting menu!", + "Try jumping when rolling through creatures.", + ], } ) diff --git a/assets/voxygen/i18n/en/npc.ron b/assets/voxygen/i18n/en/npc.ron new file mode 100644 index 0000000000..b2f5683c05 --- /dev/null +++ b/assets/voxygen/i18n/en/npc.ron @@ -0,0 +1,190 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + }, + + vector_map: { + "npc.speech.villager": [ + "Isn't it such a lovely day?", + "How are you today?", + "Top of the morning to you!", + "I wonder what the Catoblepas thinks when it eats grass.", + "What do you think about this weather?", + "Thinking about those dungeons makes me scared. I hope someone will clear them out.", + "I'd like to go spelunking in a cave when I'm stronger.", + "Have you seen my cat?", + "Have you ever heard of the ferocious Land Sharks? I hear they live in deserts.", + "They say shiny gems of all kinds can be found in caves.", + "I'm just crackers about cheese!", + "Won't you come in? We were just about to have some cheese!", + "They say mushrooms are good for your health. Never eat them myself.", + "Don't forget the crackers!", + "I simply adore dwarven cheese. I wish I could make it.", + "I wonder what is on the other side of the mountains.", + "I hope to make my own glider someday.", + "Would you like to see my garden? Okay, maybe some other time.", + "Lovely day for a stroll in the woods!", + "To be, or not to be? I think I'll be a farmer.", + "Don't you think our village is the best?", + "What do you suppose makes Glowing Remains glow?", + "I think it's time for second breakfast!", + "Have you ever caught a firefly?", + "I just can't understand where those Sauroks keep coming from.", + "I wish someone would keep the wolves away from the village.", + "I had a wonderful dream about cheese last night. What does it mean?", + "I left some cheese with my brother. Now I don't know if it exists or not. I call it Schrödinger's cheese.", + "I left some cheese with my sister. Now I don't know if it exists or not. I call it Schrödinger's cheese.", + "Someone should do something about those cultists. Preferably not me.", + "I hope it rains soon. Would be good for the crops.", + "I love honey! And I hate bees.", + "I want to see the world one day. There's got to be more to life than this village.", + ], + "npc.speech.villager_decline_trade": [ + "Sorry, I don't have anything to trade.", + "Trade? Like I got anything that may interest you.", + "My house is mine, I won't trade it for anything.", + ], + "npc.speech.merchant_advertisement": [ + "Can I interest you in a trade?", + "Do you want to trade with me?", + "I have plenty of goods, Do you want to take a look?" + ], + "npc.speech.merchant_busy": [ + "Hey, wait your turn.", + "Please wait, I'm only one person.", + "Do you see the other person in front of you?", + "Just a moment, let me finish.", + "No cutting in line.", + "I'm busy, come back later." + ], + "npc.speech.merchant_trade_successful": [ + "Thank you for trading with me!", + "Thank you!", + ], + "npc.speech.merchant_trade_declined": [ + "Maybe another time, have a good day!", + "Too bad, maybe next time, then!" + ], + "npc.speech.villager_cultist_alarm": [ + "Lookout! There is a cultist on the loose!", + "To arms! The cultists are attacking!", + "How dare the cultists attack our village!", + "Death to the cultists!", + "Cultists will not be tolerated here!", + "Murderous cultist!", + "Taste the edge of my sword, you dirty cultist!", + "Nothing can clean the blood from your hands, cultist!", + "Billions of blistering blue barnacles! A cultist among us!", + "The evils of this cultist are about to be over!", + "This cultist is mine!", + "Prepare to meet your maker, foul cultist!", + "I see a cultist! Get them!", + "I see a cultist! Attack!", + "I see a cultist! Don't let them escape!", + "Would the most honorable cultist care for some DEATH?!", + "Never forgive! Never forget! Cultist, regret!", + "Die, cultist!", + "Your reign of terror will seize!", + "Here's for all that you've done!", + "We don't take kindly to your types around here.", + "You should have stayed underground!", + ], + "npc.speech.villager_under_attack": [ + "Help, I'm under attack!", + "Help! I'm under attack!", + "Ouch! I'm under attack!", + "Ouch! I'm under attack! Help!", + "Help me! I'm under attack!", + "I'm under attack! Help!", + "I'm under attack! Help me!", + "Help!", + "Help! Help!", + "Help! Help! Help!", + "I'm under attack!", + "AAAHHH! I'm under attack!", + "AAAHHH! I'm under attack! Help!", + "Help! We're under attack!", + "Help! Murderer!", + "Help! There's a murderer on the loose!", + "Help! They're trying to kill me!", + "Guards, I'm under attack!", + "Guards! I'm under attack!", + "I'm under attack! Guards!", + "Help! Guards! I'm under attack!", + "Guards! Come quick!", + "Guards! Guards!", + "Guards! There's a villain attacking me!", + "Guards, slay this foul villain!", + "Guards! There's a murderer!", + "Guards! Help me!", + "You won't get away with this! Guards!", + "You fiend!", + "Help me!", + "Help! Please!", + "Ouch! Guards! Help!", + "They're coming for me!", + "Help! Help! I'm being repressed!", + "Ah, now we see the violence inherent in the system.", + "'Tis but a scratch!", + "Stop that!", + "What did I ever do to you?!", + "Please stop attacking me!", + "Hey! Watch where you point that thing!", + "Heinous wretch, be gone with you!", + "Stop it! Go away!", + "Now you're making me mad!", + "Oi! Who do you think you are?!", + "I'll have your head for that!", + "Stop, please! I carry nothing of value!", + "I'll set my brother on you, he's bigger than I am!", + "Nooo, I'm telling mother!", + "Curse you!", + "Please don't do that.", + "That wasn't very nice!", + "Your weapon works, you can put it away now!", + "Spare me!", + "Please, I have a family!", + "I'm too young to die!", + "Can we talk about this?", + "Violence is never the answer!", + "Today is turning out to be a very bad day...", + "Hey, that hurt!", + "Eek!", + "How rude!", + "Stop, I beg you!", + "A pox upon you!", + "This isn't fun.", + "How dare you?!", + "You'll pay for that!", + "Keep that up and you'll be sorry!", + "Don't make me hurt you!", + "There must be some misunderstanding!", + "You don't need to do this!", + "Be gone, fiend!", + "That really hurt!", + "Why would you do that?", + "By the spirits, cease!", + "You must have me confused with someone else!", + "I don't deserve this!", + "Please don't do that again.", + "Guards, throw this monster in the lake!", + "I'll set my tarasque on you!", + "Why meeeeeee?", + ], + "npc.speech.villager_enemy_killed": [ + "I have destroyed my enemy!", + "Finally at peace!", + "... now what was I doing?", + ], + "npc.speech.menacing": [ + "I'm warning you!", + "Any closer and I'll attack!", + "You don't scare me!", + "Get away from here!", + "Turn around if you want to live!", + "You're not welcome here!", + ], + } +) diff --git a/assets/voxygen/i18n/es_ES/_manifest.ron b/assets/voxygen/i18n/es_ES/_manifest.ron index 0af09c90cb..75f7decf9f 100644 --- a/assets/voxygen/i18n/es_ES/_manifest.ron +++ b/assets/voxygen/i18n/es_ES/_manifest.ron @@ -28,204 +28,5 @@ asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", scale_ratio: 1.0, ), - }, - - string_map: { - - }, - - vector_map: { - "loading.tips": [ - "Pulsa 'G' para encender tu linterna.", - "Pulsa 'F1' para ver todos las atajos de teclado predeterminados.", - "Puedes escribir /say o /s para chatear solo con jugadores que estén a tu alrededor.", - "Puedes escribir /region o /r para chatear con jugadores que se encuentren a cien bloques a tu alrededor.", - "Puedes escribir /group o /g para chatear con jugadores en tu grupo.", - "Para enviar mensajes privados escribe /tell seguido del nombre y tu mensaje.", - "NPCs con el mismo nivel pueden tener una dificultad diferente.", - "¡Observa el terreno en búsqueda de comida, cofres y botines!", - "¿Inventario lleno de comida? ¡Intenta elaborar comida mejorada con ella!", - "En busqueda de una aventura? ¡Prueba una de las mazmorras marcadas en el mapa!", - "No te olvides de ajustar los gráficos de tu pc. Pulsa 'N' para abrir la configuración.", - "¡Jugar con otros jugadores es mas divertido! Pulsa 'O' para ver quien esta en linea.", - "Un NPC con un cráneo debajo su barra de vida indica claramente que es mas poderoso que tu.", - "Pulsa 'J' para bailar. ¡Fiesta!", - "Pulsa 'Shift-Izquierdo' para desplegar tu planeador y conquistar los cielos", - "Veloren se encuentra todavia en Pre-Alpha. ¡Hacemos todo lo posible para mejorar la experiencia de juego día a día!", - "Si quieres unirte al equipo de desarrollo o solo conversar con nosotros, únete a nuestro servidor en Discord.", - "Puedes mostrar o ocultar tu total de salud en la barra de salud en configuración.", - "Para ver tus atributos, haz clic en el botón 'Atributos' del inventario.", - "Siéntate cerca de una fogata (con la tecla 'K') para descansar - recibirás una lenta sanación periódicamente.", "Need more bags or better armor to continue your journey? Press 'C' to open the crafting menu!", - "¿Necesitas más mochilas o mejores armaduras para continuar tu viaje? Pulsa 'C' para abrir el menú de elaboración.", - ], - "npc.speech.villager": [ - "¿A que es un dia bonito?", - "¿Cómo estás hoy?", - "¡Que tengas una buena mañana!", - "Me pregunto qué piensa Catoblepas cuando come hierba", - "¿Qué piensas de este tiempo?", - "Es solo pensar en esas mazmorras y me da miedo. Espero que alguien pueda poner orden ahi dentro", - "Me gustaría hacer espeleología en una cueva cuando sea más fuerte", - "¿Has visto a mi gato?", - "¿Has oído hablar de los feroces tiburones de tierra? He oído que viven en los desiertos", - "Dicen que en las cuevas se pueden encontrar gemas brillantes de todo tipo", - "¡Sólo me interesa el queso!", - "¿No vas a entrar? Estábamos a punto de comer queso!", - "Dicen que las setas son buenas para la salud. Yo nunca las he comido", - "¡No te olvides las galletas!", - "Adoro el queso enano. Me encantaria aprender hacerlo", - "Me pregunto qué habrá al otro lado de las montañas", - "Espero hacer mi propio planeador algún día", - "¿Te gustaría ver mi jardín? Bueno, tal vez en otro momento", - "¡Hace un día precioso para pasear por el bosque!", - "¿Ser o no ser? Creo que seré granjero", - "¿No crees que nuestro pueblo es el mejor?", - "¿Qué supones que hace brillar a los Restos Luminosos?", - "¡Creo que es la hora del segundo desayuno!", - "¿Has cogido alguna vez una luciérnaga?", - "No puedo entender de dónde vienen esos Sauroks", - "Me gustaría que alguien mantuviera a los lobos alejados del pueblo", - "Anoche tuve un sueño maravilloso sobre el queso. ¿Qué significa?", - "He dejado algo de queso con mi hermano. Ahora no se si existe o no. Lo llamo el queso de Schrödinger.", - "He dejado algo de queso con mi hermana. Ahora no se si existe o no. Lo llamo el queso de Schrödinger.", - "Alguien debería de hacer algo con esos cultistas. Preferiblemente alguien que no sea yo", - "Espero que llueva pronto. Sería bueno para los cultivos.", - "¡Me encanta la miel! Y odio las abejas.", - "Quiero ver el mundo algun día. Tiene que haber algo más en esta vida que este pueblo.", - ], - "npc.speech.villager_decline_trade": [ - "Lo siento, no tengo nada para comerciar.", - "¿Comerciar? Como si tuviera algo que te pueda interesar.", - "Mi casa es mía y no la cambiaré por nada.", - ], - "npc.speech.merchant_advertisement": [ - "¿Te interesaría comerciar conmigo?", - "¿Querrías comerciar conmigo?", - "Tengo muchos bienes. ¿Quieres echar un vistazo?" - ], - "npc.speech.merchant_busy": [ - "Hey, espera tu turno", - "Ten paciencia, solo soy una persona.", - "¿Que no ves que estoy comerciando con alguien?", - "Un momento, dejame acabar.", - "Nada de colarse.", - "Estoy ocupado, vuelve más tarde." - ], - "npc.speech.merchant_trade_successful": [ - "¡Gracias por comerciar conmigo!", - "¡Muchas gracias!", - ], - "npc.speech.merchant_trade_declined": [ - "Quizás en otra ocasión, ¡ten un buen día!", - "Vaya, supongo que en otra ocasión." - ], - "npc.speech.villager_cultist_alarm": [ - "¡Cuidado! ¡Hay cultistas sueltos!", - "¡A las armas! ¡Los cultistas nos atacan!", - "¡Como se atreven los cultistas a atacar esta aldea!", - "¡Muerte a los cultistas!", - "¡Aquí no toleramos a los cultistas!", - "¡Cultista asesino!", - "¡Prueba el filo de mi espada, sucio cultista!", - "¡Nada podrá limpiar la sangre de tus manos, cultista!", - "¡Esos malvados cultistas seran aniquilados!", - "¡Este cultista es mío!", - "¡Prepárate para conocer a tu creados, sucio cultista!", - "¡Veo un cultista! ¡Atrapadlo!", - "¡Veo un cultista! ¡Atacadlo!", - "¡Veo un cultista! ¡No dejéis que escape!", - "¡Nunca perdonamos!¡Nunca olvidamos!¡Muere, cultista!", - "¡Muere, cultista!", - "¡Tu reinado de terror llegará a su fin!", - "¡Pagarás por todo lo que has hecho!", - "Por aquí no tratamos con amabilidad a los de tu especie.", - "¡Deberías de haberte quedado bajo tierra!", - ], - "npc.speech.villager_under_attack": [ - "Ayuda, ¡me atacan!", - "¡Ayuda! ¡Me atacan!", - "¡Auch! ¡Me atacan!", - "¡Auch! ¡Me atacan! ¡Ayuda!", - "¡Ayúdame! ¡Me atacan!", - "¡Me atacan! ¡Ayuda!", - "¡Me atacan! ¡Ayúdame!", - "¡Ayuda!", - "¡Ayuda! ¡Ayuda!", - "¡Ayuda! ¡Ayuda! ¡Ayuda!", - "¡Me atacan!", - "¡AAAHHH! ¡Me atacan!", - "¡AAAHHH! ¡Me atacan! ¡Ayuda!", - "¡Ayuda! ¡Nos están atacando!", - "¡Ayuda! ¡Asesino!", - "¡Ayuda! ¡Hay un asesino suelto!", - "¡Ayuda! ¡Me están intentando matar!", - "Guardias, ¡me están atacando!", - "Guardias, ¡me están atacando!", - "¡Me están atacando! ¡Guardias!", - "¡Ayuda! ¡Guardias! ¡Me están atacando!", - "¡Guardias! ¡Venid rápido!", - "¡Guardias! ¡Guardias!", - "¡Guardias! ¡Un villano me ataca!", - "Guardias, ¡acabad con este infame villano!", - "¡Guardias! ¡Hay un asesino!", - "¡Guardias! ¡Ayudadme!", - "¡No te saldrás con la tuya! ¡Guardias!", - "¡Desalmado!", - "¡Ayúdame!", - "¡Ayuda! ¡Por favor!", - "¡Auch! ¡Guardias! ¡Ayuda!", - "¡Vienen a por mí!", - "¡Ayuda! ¡Ayuda! Me están reprimiendo", - "Ah, ahora vemos la violencia inherente al sistema.", - "¡No es más que un rasguño!", - "¡Deja de hacer eso!", - "¡¿Qué te he hecho?!", - "¡Porfavor deja de atacarme!", - "¡Eh! ¡Mira a dónde apuntas esa cosa!", - "Desgraciado, vete de aqui!", - "Para ya! Vete!", - "¡Me estas haciendo enfadar!", - "¡Oye! ¡¿Quién te crees que eres?!", - "Te arrancaré la cabeza por eso!", - "¡Para, por favor! ¡No llevo nada de valor!", - "¡Mi hermano es más grande que yo y te machacara!", - "¡Nooo, se lo diré a mamá!", - "¡Maldito seas!", - "Por favor, no hagas eso", - "¡Eso no fue muy agradable!", - "Tu arma funciona, ahora aléjala!", - "¡Perdóname!", - "¡Por favor, tengo familia!", - "¡Soy demasiado joven para morir!", - "¿Podemos hablar de esto?", - "¡La violencia nunca es la respuesta!", - "Hoy se está torciendo el día...", - "¡Eh, eso duele!", - "¡Ayy!", - "¡Qué grosero!", - "¡Para, te lo ruego!", - "¡Ojala te enfermes!", - "Esto no es divertido", - "¡¿Cómo te atreves?!", - "¡Pagarás por eso!", - "¡Sigue así y te arrepentirás!", - "¡No hagas que pegue!", - "¡Debe haber algún malentendido!", - "¡No hay necesidad de hacer esto!", - "¡Vete, demonio!", - "¡Eso sí que duele!", - "¿Por qué harías eso?", - "Por los espíritus, ¡basta!", - "¡Debes haberme confundido con otra persona!", - "¡No me merezco esto!", - "Por favor, no vuelvas a hacer eso.", - "¡Guardias, arrojed a este monstruo al lago!", - "¡Enviare mi Tarasca a por ti!", - ], - "npc.speech.villager_enemy_killed": [ - "¡He acabado con el enemigo!", - "¡Por fin, paz!", - "... ¿por dónde me había quedado?", - ] } ) diff --git a/assets/voxygen/i18n/es_ES/buff.ron b/assets/voxygen/i18n/es_ES/buff.ron index 6cf979b30c..014a67c146 100644 --- a/assets/voxygen/i18n/es_ES/buff.ron +++ b/assets/voxygen/i18n/es_ES/buff.ron @@ -26,7 +26,7 @@ "buff.desc.cursed": "Estás maldito.", // Buffs stats "buff.stat.health": "Restaura {str_total} salud", - "buff.stat.increase_max_stamina": "Aumenta la resistencia máxima en {strength}", + "buff.stat.increase_max_energy": "Aumenta la resistencia máxima en {strength}", "buff.stat.increase_max_health": "Aumenta la salud máxima en {strength}", "buff.stat.invulnerability": "Ofrece invulnerabilidad", // Text diff --git a/assets/voxygen/i18n/es_ES/hud/bag.ron b/assets/voxygen/i18n/es_ES/hud/bag.ron index db22d86dfb..c0b84fe65d 100644 --- a/assets/voxygen/i18n/es_ES/hud/bag.ron +++ b/assets/voxygen/i18n/es_ES/hud/bag.ron @@ -26,7 +26,7 @@ "hud.bag.offhand": "Mano secundaria", "hud.bag.bag": "Mochila", "hud.bag.health": "Vida", - "hud.bag.stamina": "Resistencia", + "hud.bag.energy": "Resistencia", "hud.bag.combat_rating": "Puntuación de combate", "hud.bag.protection": "Protección", "hud.bag.combat_rating_desc": "Calculado por tu\nequipamiento y vida", diff --git a/assets/voxygen/i18n/es_ES/hud/hud_settings.ron b/assets/voxygen/i18n/es_ES/hud/settings.ron similarity index 97% rename from assets/voxygen/i18n/es_ES/hud/hud_settings.ron rename to assets/voxygen/i18n/es_ES/hud/settings.ron index fd69a2f39c..586de9564a 100644 --- a/assets/voxygen/i18n/es_ES/hud/hud_settings.ron +++ b/assets/voxygen/i18n/es_ES/hud/settings.ron @@ -15,7 +15,7 @@ "hud.settings.relative_scaling": "Escala relativa", "hud.settings.custom_scaling": "Escala personalizada", "hud.settings.crosshair": "Punto de mira", - "hud.settings.transparency": "Transparencia", + "hud.settings.opacity": "Transparencia", "hud.settings.hotbar": "Barra rápida", "hud.settings.toggle_shortcuts": "Alternar atajos", "hud.settings.buffs_skillbar": "Buffs en la barra de habilidades.", @@ -33,7 +33,7 @@ "hud.settings.values": "Valores", "hud.settings.percentages": "Porcentaje", "hud.settings.chat": "Chat", - "hud.settings.background_transparency": "Transparencia del fondo", + "hud.settings.background_opacity": "Transparencia del fondo", "hud.settings.chat_character_name": "Nombres de personajes en el chat", "hud.settings.loading_tips": "Consejos en pantalla de carga", "hud.settings.reset_interface": "Restablecer por defecto", diff --git a/assets/voxygen/i18n/es_ES/skills.ron b/assets/voxygen/i18n/es_ES/hud/skills.ron similarity index 97% rename from assets/voxygen/i18n/es_ES/skills.ron rename to assets/voxygen/i18n/es_ES/hud/skills.ron index 05bf911f4f..6fdf55cab1 100644 --- a/assets/voxygen/i18n/es_ES/skills.ron +++ b/assets/voxygen/i18n/es_ES/hud/skills.ron @@ -11,8 +11,8 @@ // General "hud.skill.inc_health_title": "Aumentar la salud", "hud.skill.inc_health": "Aumenta la salud máxima en {boost}{SP}", - "hud.skill.inc_stam_title": "Aumenta la resistencia", - "hud.skill.inc_stam": "Aumenta la resistencia máxima en {boost}{SP}", + "hud.skill.inc_energy_title": "Aumenta la resistencia", + "hud.skill.inc_energy": "Aumenta la resistencia máxima en {boost}{SP}", "hud.skill.unlck_sword_title": "Desbloquear la espada", "hud.skill.unlck_sword": "Desbloquea el árbol de habilidades de la espada{SP}", "hud.skill.unlck_axe_title": "Desbloquear el hacha", @@ -27,8 +27,8 @@ "hud.skill.unlck_sceptre": "Desbloquea el árbol de habilidades del cetro{SP}", "hud.skill.dodge_title": "Esquivar", "hud.skill.dodge": "Esquivar evita los ataques cuerpo a cuerpo{SP}", - "hud.skill.roll_stamina_title": "Coste de resistencia Rodar", - "hud.skill.roll_stamina": "Rodar usa un {boost}% menos de resistencia{SP}", + "hud.skill.roll_energy_title": "Coste de resistencia Rodar", + "hud.skill.roll_energy": "Rodar usa un {boost}% menos de resistencia{SP}", "hud.skill.roll_speed_title": "Velocidad al rodar", "hud.skill.roll_speed": "Rodar es un {boost}% más rápido{SP}", "hud.skill.roll_dur_title": "Duración de rodar", @@ -95,8 +95,8 @@ "hud.skill.st_flamethrower_damage" : "Aumenta el daño en un {boost}%{SP}", "hud.skill.st_explosion_radius_title" : "Radio de explosión", "hud.skill.st_explosion_radius" : "Más grande es mejor, aumenta el radio de explosión en un {boost}%{SP}", - "hud.skill.st_stamina_regen_title" : "Regeneración de resistencia", - "hud.skill.st_stamina_regen" : "Aumenta la regeneración de resistencia en un {boost}%{SP}", + "hud.skill.st_energy_regen_title" : "Regeneración de resistencia", + "hud.skill.st_energy_regen" : "Aumenta la regeneración de resistencia en un {boost}%{SP}", "hud.skill.st_fireball_title" : "Bola de fuego", "hud.skill.st_fireball" : "Juega a perseguir a los enemigos", "hud.skill.st_damage_title" : "Daño", diff --git a/assets/voxygen/i18n/es_ES/main.ron b/assets/voxygen/i18n/es_ES/main.ron index 2799b71d91..a5ac5f0f8c 100644 --- a/assets/voxygen/i18n/es_ES/main.ron +++ b/assets/voxygen/i18n/es_ES/main.ron @@ -64,5 +64,28 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "Pulsa 'G' para encender tu linterna.", + "Pulsa 'F1' para ver todos las atajos de teclado predeterminados.", + "Puedes escribir /say o /s para chatear solo con jugadores que estén a tu alrededor.", + "Puedes escribir /region o /r para chatear con jugadores que se encuentren a cien bloques a tu alrededor.", + "Puedes escribir /group o /g para chatear con jugadores en tu grupo.", + "Para enviar mensajes privados escribe /tell seguido del nombre y tu mensaje.", + "NPCs con el mismo nivel pueden tener una dificultad diferente.", + "¡Observa el terreno en búsqueda de comida, cofres y botines!", + "¿Inventario lleno de comida? ¡Intenta elaborar comida mejorada con ella!", + "En busqueda de una aventura? ¡Prueba una de las mazmorras marcadas en el mapa!", + "No te olvides de ajustar los gráficos de tu pc. Pulsa 'N' para abrir la configuración.", + "¡Jugar con otros jugadores es mas divertido! Pulsa 'O' para ver quien esta en linea.", + "Un NPC con un cráneo debajo su barra de vida indica claramente que es mas poderoso que tu.", + "Pulsa 'J' para bailar. ¡Fiesta!", + "Pulsa 'Shift-Izquierdo' para desplegar tu planeador y conquistar los cielos", + "Veloren se encuentra todavia en Pre-Alpha. ¡Hacemos todo lo posible para mejorar la experiencia de juego día a día!", + "Si quieres unirte al equipo de desarrollo o solo conversar con nosotros, únete a nuestro servidor en Discord.", + "Puedes mostrar o ocultar tu total de salud en la barra de salud en configuración.", + "Para ver tus atributos, haz clic en el botón 'Atributos' del inventario.", + "Siéntate cerca de una fogata (con la tecla 'K') para descansar - recibirás una lenta sanación periódicamente.", "Need more bags or better armor to continue your journey? Press 'C' to open the crafting menu!", + "¿Necesitas más mochilas o mejores armaduras para continuar tu viaje? Pulsa 'C' para abrir el menú de elaboración.", + ], } ) diff --git a/assets/voxygen/i18n/es_ES/npc.ron b/assets/voxygen/i18n/es_ES/npc.ron new file mode 100644 index 0000000000..57d8413b95 --- /dev/null +++ b/assets/voxygen/i18n/es_ES/npc.ron @@ -0,0 +1,180 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Spanish (Spain) +( + string_map: { + + }, + + vector_map: { + "npc.speech.villager": [ + "¿A que es un dia bonito?", + "¿Cómo estás hoy?", + "¡Que tengas una buena mañana!", + "Me pregunto qué piensa Catoblepas cuando come hierba", + "¿Qué piensas de este tiempo?", + "Es solo pensar en esas mazmorras y me da miedo. Espero que alguien pueda poner orden ahi dentro", + "Me gustaría hacer espeleología en una cueva cuando sea más fuerte", + "¿Has visto a mi gato?", + "¿Has oído hablar de los feroces tiburones de tierra? He oído que viven en los desiertos", + "Dicen que en las cuevas se pueden encontrar gemas brillantes de todo tipo", + "¡Sólo me interesa el queso!", + "¿No vas a entrar? Estábamos a punto de comer queso!", + "Dicen que las setas son buenas para la salud. Yo nunca las he comido", + "¡No te olvides las galletas!", + "Adoro el queso enano. Me encantaria aprender hacerlo", + "Me pregunto qué habrá al otro lado de las montañas", + "Espero hacer mi propio planeador algún día", + "¿Te gustaría ver mi jardín? Bueno, tal vez en otro momento", + "¡Hace un día precioso para pasear por el bosque!", + "¿Ser o no ser? Creo que seré granjero", + "¿No crees que nuestro pueblo es el mejor?", + "¿Qué supones que hace brillar a los Restos Luminosos?", + "¡Creo que es la hora del segundo desayuno!", + "¿Has cogido alguna vez una luciérnaga?", + "No puedo entender de dónde vienen esos Sauroks", + "Me gustaría que alguien mantuviera a los lobos alejados del pueblo", + "Anoche tuve un sueño maravilloso sobre el queso. ¿Qué significa?", + "He dejado algo de queso con mi hermano. Ahora no se si existe o no. Lo llamo el queso de Schrödinger.", + "He dejado algo de queso con mi hermana. Ahora no se si existe o no. Lo llamo el queso de Schrödinger.", + "Alguien debería de hacer algo con esos cultistas. Preferiblemente alguien que no sea yo", + "Espero que llueva pronto. Sería bueno para los cultivos.", + "¡Me encanta la miel! Y odio las abejas.", + "Quiero ver el mundo algun día. Tiene que haber algo más en esta vida que este pueblo.", + ], + "npc.speech.villager_decline_trade": [ + "Lo siento, no tengo nada para comerciar.", + "¿Comerciar? Como si tuviera algo que te pueda interesar.", + "Mi casa es mía y no la cambiaré por nada.", + ], + "npc.speech.merchant_advertisement": [ + "¿Te interesaría comerciar conmigo?", + "¿Querrías comerciar conmigo?", + "Tengo muchos bienes. ¿Quieres echar un vistazo?" + ], + "npc.speech.merchant_busy": [ + "Hey, espera tu turno", + "Ten paciencia, solo soy una persona.", + "¿Que no ves que estoy comerciando con alguien?", + "Un momento, dejame acabar.", + "Nada de colarse.", + "Estoy ocupado, vuelve más tarde." + ], + "npc.speech.merchant_trade_successful": [ + "¡Gracias por comerciar conmigo!", + "¡Muchas gracias!", + ], + "npc.speech.merchant_trade_declined": [ + "Quizás en otra ocasión, ¡ten un buen día!", + "Vaya, supongo que en otra ocasión." + ], + "npc.speech.villager_cultist_alarm": [ + "¡Cuidado! ¡Hay cultistas sueltos!", + "¡A las armas! ¡Los cultistas nos atacan!", + "¡Como se atreven los cultistas a atacar esta aldea!", + "¡Muerte a los cultistas!", + "¡Aquí no toleramos a los cultistas!", + "¡Cultista asesino!", + "¡Prueba el filo de mi espada, sucio cultista!", + "¡Nada podrá limpiar la sangre de tus manos, cultista!", + "¡Esos malvados cultistas seran aniquilados!", + "¡Este cultista es mío!", + "¡Prepárate para conocer a tu creados, sucio cultista!", + "¡Veo un cultista! ¡Atrapadlo!", + "¡Veo un cultista! ¡Atacadlo!", + "¡Veo un cultista! ¡No dejéis que escape!", + "¡Nunca perdonamos!¡Nunca olvidamos!¡Muere, cultista!", + "¡Muere, cultista!", + "¡Tu reinado de terror llegará a su fin!", + "¡Pagarás por todo lo que has hecho!", + "Por aquí no tratamos con amabilidad a los de tu especie.", + "¡Deberías de haberte quedado bajo tierra!", + ], + "npc.speech.villager_under_attack": [ + "Ayuda, ¡me atacan!", + "¡Ayuda! ¡Me atacan!", + "¡Auch! ¡Me atacan!", + "¡Auch! ¡Me atacan! ¡Ayuda!", + "¡Ayúdame! ¡Me atacan!", + "¡Me atacan! ¡Ayuda!", + "¡Me atacan! ¡Ayúdame!", + "¡Ayuda!", + "¡Ayuda! ¡Ayuda!", + "¡Ayuda! ¡Ayuda! ¡Ayuda!", + "¡Me atacan!", + "¡AAAHHH! ¡Me atacan!", + "¡AAAHHH! ¡Me atacan! ¡Ayuda!", + "¡Ayuda! ¡Nos están atacando!", + "¡Ayuda! ¡Asesino!", + "¡Ayuda! ¡Hay un asesino suelto!", + "¡Ayuda! ¡Me están intentando matar!", + "Guardias, ¡me están atacando!", + "Guardias, ¡me están atacando!", + "¡Me están atacando! ¡Guardias!", + "¡Ayuda! ¡Guardias! ¡Me están atacando!", + "¡Guardias! ¡Venid rápido!", + "¡Guardias! ¡Guardias!", + "¡Guardias! ¡Un villano me ataca!", + "Guardias, ¡acabad con este infame villano!", + "¡Guardias! ¡Hay un asesino!", + "¡Guardias! ¡Ayudadme!", + "¡No te saldrás con la tuya! ¡Guardias!", + "¡Desalmado!", + "¡Ayúdame!", + "¡Ayuda! ¡Por favor!", + "¡Auch! ¡Guardias! ¡Ayuda!", + "¡Vienen a por mí!", + "¡Ayuda! ¡Ayuda! Me están reprimiendo", + "Ah, ahora vemos la violencia inherente al sistema.", + "¡No es más que un rasguño!", + "¡Deja de hacer eso!", + "¡¿Qué te he hecho?!", + "¡Porfavor deja de atacarme!", + "¡Eh! ¡Mira a dónde apuntas esa cosa!", + "Desgraciado, vete de aqui!", + "Para ya! Vete!", + "¡Me estas haciendo enfadar!", + "¡Oye! ¡¿Quién te crees que eres?!", + "Te arrancaré la cabeza por eso!", + "¡Para, por favor! ¡No llevo nada de valor!", + "¡Mi hermano es más grande que yo y te machacara!", + "¡Nooo, se lo diré a mamá!", + "¡Maldito seas!", + "Por favor, no hagas eso", + "¡Eso no fue muy agradable!", + "Tu arma funciona, ahora aléjala!", + "¡Perdóname!", + "¡Por favor, tengo familia!", + "¡Soy demasiado joven para morir!", + "¿Podemos hablar de esto?", + "¡La violencia nunca es la respuesta!", + "Hoy se está torciendo el día...", + "¡Eh, eso duele!", + "¡Ayy!", + "¡Qué grosero!", + "¡Para, te lo ruego!", + "¡Ojala te enfermes!", + "Esto no es divertido", + "¡¿Cómo te atreves?!", + "¡Pagarás por eso!", + "¡Sigue así y te arrepentirás!", + "¡No hagas que pegue!", + "¡Debe haber algún malentendido!", + "¡No hay necesidad de hacer esto!", + "¡Vete, demonio!", + "¡Eso sí que duele!", + "¿Por qué harías eso?", + "Por los espíritus, ¡basta!", + "¡Debes haberme confundido con otra persona!", + "¡No me merezco esto!", + "Por favor, no vuelvas a hacer eso.", + "¡Guardias, arrojed a este monstruo al lago!", + "¡Enviare mi Tarasca a por ti!", + ], + "npc.speech.villager_enemy_killed": [ + "¡He acabado con el enemigo!", + "¡Por fin, paz!", + "... ¿por dónde me había quedado?", + ] + } +) diff --git a/assets/voxygen/i18n/es_ES/template.ron b/assets/voxygen/i18n/es_ES/template.ron deleted file mode 100644 index 0ce3ada5d1..0000000000 --- a/assets/voxygen/i18n/es_ES/template.ron +++ /dev/null @@ -1,12 +0,0 @@ -/// WARNING: Localization files shall be saved in UTF-8 format without BOM - -/// Localization for Spanish (Spain) -( - string_map: { - - }, - - - vector_map: { - } -) \ No newline at end of file diff --git a/assets/voxygen/i18n/es_LA/_manifest.ron b/assets/voxygen/i18n/es_LA/_manifest.ron new file mode 100644 index 0000000000..39c6bc6d5a --- /dev/null +++ b/assets/voxygen/i18n/es_LA/_manifest.ron @@ -0,0 +1,44 @@ +/// 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 "latinoamericano" Latin-American +( + metadata: ( + language_name: "Español Latino", + language_identifier: "es_LA", + ), + convert_utf8_to_ascii: false, + fonts: { + "opensans": Font ( + asset_key: "voxygen.font.OpenSans-Regular", + scale_ratio: 1.0, + ), + "metamorph": Font ( + asset_key: "voxygen.font.Metamorphous-Regular", + scale_ratio: 1.0, + ), + "alkhemi": Font ( + asset_key: "voxygen.font.Alkhemikal", + scale_ratio: 1.0, + ), + "wizard": Font ( + asset_key: "voxygen.font.wizard", + scale_ratio: 1.0, + ), + "cyri": Font ( + asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", + scale_ratio: 1.0, + ), + } +) diff --git a/assets/voxygen/i18n/es_LA/buff.ron b/assets/voxygen/i18n/es_LA/buff.ron new file mode 100644 index 0000000000..1752e1a8c5 --- /dev/null +++ b/assets/voxygen/i18n/es_LA/buff.ron @@ -0,0 +1,26 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "buff.remove": "Click para quitar", + "buff.title.missing": "Sin Título", + "buff.desc.missing": "Sin Descripción", + // Buffs + "buff.title.heal": "Curación", + "buff.desc.heal": "Recupera vida durante un tiempo.", + "buff.title.potion": "Poción", + "buff.desc.potion": "Bebiendo...", + "buff.title.saturation": "Saturación", + "buff.desc.saturation": "Recupera vida durante un tiempo por objetos.", + "buff.title.campfire_heal": "Curación de fogata", + "buff.desc.campfire_heal": "Descansar en una fogata recupera {rate}% por segundo.", + // Debuffs + "buff.title.bleed": "Sangrando", + "buff.desc.bleed": "Inflinge daño regularmente.", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/char_selection.ron b/assets/voxygen/i18n/es_LA/char_selection.ron new file mode 100644 index 0000000000..19fa8c4fbf --- /dev/null +++ b/assets/voxygen/i18n/es_LA/char_selection.ron @@ -0,0 +1,32 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "char_selection.loading_characters": "Cargando personajes...", + "char_selection.delete_permanently": "¿Borrar este Personaje permanentemente?", + "char_selection.deleting_character": "Borrando Personaje...", + "char_selection.change_server": "Cambiar Servidor", + "char_selection.enter_world": "Entrar al Mundo", + "char_selection.logout": "Cerrar Sesión", + "char_selection.create_new_character": "Crear Nuevo Personaje", + "char_selection.creating_character": "Creando Personaje...", + "char_selection.character_creation": "Creación de Personaje", + + "char_selection.human_default": "Humano por defecto", + "char_selection.level_fmt": "Nivel {level_nb}", + "char_selection.uncanny_valley": "Valle Misterioso", + "char_selection.plains_of_uncertainty": "Planicies de la Incertidumbre", + "char_selection.beard": "Barba", + "char_selection.hair_style": "Peinado", + "char_selection.hair_color": "Color de Pelo", + "char_selection.eye_color": "Color de Ojos", + "char_selection.skin": "Color de Piel", + "char_selection.eyeshape": "Detalles de los Ojos", + "char_selection.accessories": "Accesorios", + "char_selection.create_info_name": "Tu Personaje necesita un nombre!", + }, + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/common.ron b/assets/voxygen/i18n/es_LA/common.ron new file mode 100644 index 0000000000..92e4272b23 --- /dev/null +++ b/assets/voxygen/i18n/es_LA/common.ron @@ -0,0 +1,71 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + // Texts used in multiple locations with the same formatting + "common.username": "Usuario", + "common.singleplayer": "Un Jugador", + "common.multiplayer": "Multijugador", + "common.servers": "Servidores", + "common.quit": "Salir", + "common.settings": "Configuración", + "common.languages": "Idiomas", //this one is repeated in en.ron (53 and 59) + "common.interface": "Interfaz", + "common.gameplay": "Jugabilidad", + "common.controls": "Controles", + "common.video": "Gráficos", + "common.sound": "Sonido", + "common.resume": "Continuar", + "common.characters": "Personajes", + "common.close": "Cerrar", + "common.yes": "Sí", + "common.no": "No", + "common.back": "Volver", + "common.create": "Crear", + "common.okay": "Ok", + "common.add": "Agregar", + "common.accept": "Aceptar", + "common.decline": "Rechazar", + "common.disclaimer": "Cuidado", + "common.cancel": "Cancelar", + "common.none": "Ninguno", + "common.error": "Error", + "common.fatal_error": "Error Fatal", + "common.you": "Tú", + "common.automatic": "Automático", + "common.random": "Aleatorio", + // Settings Window title + "common.interface_settings": "Ajustes de Interfaz", + "common.gameplay_settings": "Ajustes de Jugabilidad", + "common.controls_settings": "Ajustes de Controles", + "common.video_settings": "Ajustes de Graficos", + "common.sound_settings": "Ajustes de Sonido", + "common.language_settings": "Ajustes de Idiomas", + + // Message when connection to the server is lost + "common.connection_lost": r#"Conexión perdida! +Se reinició el servidor? +El cliente está actualizado?"#, + + + "common.species.orc": "Orco", + "common.species.human": "Humano", + "common.species.dwarf": "Enano", + "common.species.elf": "Elfo", + "common.species.undead": "No-Muerto", + "common.species.danari": "Danari", + + "common.weapons.axe": "Hacha", + "common.weapons.sword": "Espada", + "common.weapons.staff": "Vara Mágica", + "common.weapons.bow": "Arco", + "common.weapons.hammer": "Martillo", + "common.weapons.sceptre": "Cetro curativo", + "common.rand_appearance": "Nombre y Apariencia Aleatoria", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/esc_menu.ron b/assets/voxygen/i18n/es_LA/esc_menu.ron new file mode 100644 index 0000000000..b17495ff1c --- /dev/null +++ b/assets/voxygen/i18n/es_LA/esc_menu.ron @@ -0,0 +1,13 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "esc_menu.logout": "Cerrar Sesión", + "esc_menu.quit_game": "Salir del Juego", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/gameinput.ron b/assets/voxygen/i18n/es_LA/gameinput.ron new file mode 100644 index 0000000000..d85d2495e6 --- /dev/null +++ b/assets/voxygen/i18n/es_LA/gameinput.ron @@ -0,0 +1,68 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "gameinput.primary": "Ataque Básico", + "gameinput.secondary": "Ataque Secundario/Bloquear/Apuntar", + "gameinput.slot1": "Ranura de Inventario Rápido 1", + "gameinput.slot2": "Ranura de Inventario Rápido 2", + "gameinput.slot3": "Ranura de Inventario Rápido 3", + "gameinput.slot4": "Ranura de Inventario Rápido 4", + "gameinput.slot5": "Ranura de Inventario Rápido 5", + "gameinput.slot6": "Ranura de Inventario Rápido 6", + "gameinput.slot7": "Ranura de Inventario Rápido 7", + "gameinput.slot8": "Ranura de Inventario Rápido 8", + "gameinput.slot9": "Ranura de Inventario Rápido 9", + "gameinput.slot10": "Ranura de Inventario Rápido 10", + "gameinput.swaploadout": "Cambiar Equipamiento", + "gameinput.togglecursor": "Mostrar Cursor", + "gameinput.help": "Mostrar Ventana de Ayuda", + "gameinput.toggleinterface": "Mostrar Interfaz", + "gameinput.toggledebug": "Mostrar FPS y la Info de Depuración", + "gameinput.screenshot": "Tomar Captura de Pantalla", + "gameinput.toggleingameui": "Mostrar Nombres", + "gameinput.fullscreen": "Pantalla Completa", + "gameinput.moveforward": "Moverse hacia Adelante", + "gameinput.moveleft": "Moverse hacia la Izquierda", + "gameinput.moveright": "Moverse hacia la Derecha", + "gameinput.moveback": "Moverse hacia Atras", + "gameinput.jump": "Saltar", + "gameinput.glide": "Planeador", + "gameinput.roll": "Rodar", + "gameinput.climb": "Trepar", + "gameinput.climbdown": "Descender", + "gameinput.wallleap": "Saltar a la Pared", + "gameinput.togglelantern": "Encender Farol", + "gameinput.mount": "Montar", + "gameinput.chat": "Abrir Chat", + "gameinput.enter": "Entrar", + "gameinput.command": "Insertar Comandos", + "gameinput.escape": "Escapar", + "gameinput.map": "Mapa", + "gameinput.bag": "Mochila", + "gameinput.social": "Lista de jugadores", + "gameinput.sit": "Sentarse", + "gameinput.spellbook": "Hechizos", + "gameinput.settings": "Configuración", + "gameinput.respawn": "Reaparecer", + "gameinput.charge": "Cargar", + "gameinput.togglewield": "Alternar empuñadura", + "gameinput.interact": "Interactuar", + "gameinput.freelook": "Vista Libre", + "gameinput.autowalk": "Caminata Automática", + "gameinput.dance": "Bailar", + "gameinput.select": "Seleccione la Entidad", + "gameinput.acceptgroupinvite": "Aceptar invitación al grupo", + "gameinput.declinegroupinvite": "Rechazar invitación al grupo", + "gameinput.crafting": "Craftear", + "gameinput.fly": "Volar", + "gameinput.sneak": "Agacharse", + "gameinput.swimdown": "Sumergirse", + "gameinput.swimup": "Nadar hacia arriba", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/hud/bag.ron b/assets/voxygen/i18n/es_LA/hud/bag.ron new file mode 100644 index 0000000000..45224648ad --- /dev/null +++ b/assets/voxygen/i18n/es_LA/hud/bag.ron @@ -0,0 +1,31 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "hud.bag.inventory": "Inventario de {playername}", + "hud.bag.stats_title": "Estadísticas de {playername}", + "hud.bag.exp": "Exp", + "hud.bag.armor": "Armadura", + "hud.bag.stats": "Estadísticas", + "hud.bag.head": "Cabeza", + "hud.bag.neck": "Cuello", + "hud.bag.tabard": "Tabardo", + "hud.bag.shoulders": "Hombros", + "hud.bag.chest": "Torso", + "hud.bag.hands": "Manos", + "hud.bag.lantern": "Linterna", + "hud.bag.glider": "Planeador", + "hud.bag.belt": "Cinturón", + "hud.bag.ring": "Anillo", + "hud.bag.back": "Espalda", + "hud.bag.legs": "Piernas", + "hud.bag.feet": "Pies", + "hud.bag.mainhand": "Mano Principal", + "hud.bag.offhand": "Mano Secundaria", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/hud/char_window.ron b/assets/voxygen/i18n/es_LA/hud/char_window.ron new file mode 100644 index 0000000000..402e5073eb --- /dev/null +++ b/assets/voxygen/i18n/es_LA/hud/char_window.ron @@ -0,0 +1,22 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + /// Start character window section + "character_window.character_name": "Nombre de Personaje", + // Character stats + "character_window.character_stats": r#"Resistencia + +Estado Físico + +Valentía + +Protección +"#, + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/hud/chat.ron b/assets/voxygen/i18n/es_LA/hud/chat.ron new file mode 100644 index 0000000000..2be6aea288 --- /dev/null +++ b/assets/voxygen/i18n/es_LA/hud/chat.ron @@ -0,0 +1,36 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "hud.chat.online_msg": "[{name}] se ha conectado.", + "hud.chat.offline_msg": "[{name}] se ha desconectado.", + + "hud.chat.default_death_msg": "[{name}] murió", + "hud.chat.environmental_kill_msg": "[{name}] murió en {environment}", + "hud.chat.fall_kill_msg": "[{name}] murió por daño de caída", + "hud.chat.suicide_msg": "[{name}] murió por heridas autoinfligidas", + + "hud.chat.pvp_melee_kill_msg": "[{attacker}] derrotó a [{victim}]", + "hud.chat.pvp_ranged_kill_msg": "[{attacker}] le disparó a [{victim}]", + "hud.chat.pvp_explosion_kill_msg": "[{attacker}] hizo explotar a [{victim}]", + "hud.chat.pvp_energy_kill_msg": "[{attacker}] usó magia para matar a [{victim}]", + "hud.chat.pvp_buff_kill_msg": "[{attacker}] mató a [{victim}]", + + + "hud.chat.npc_melee_kill_msg": "{attacker} mató a [{victim}]", + "hud.chat.npc_ranged_kill_msg": "{attacker} le disparó a [{victim}]", + "hud.chat.npc_explosion_kill_msg": "{attacker} hizo explotar a [{victim}]", + "hud.chat.npc_energy_kill_msg": "{attacker} usó magia para matar a [{victim}]", + "hud.chat.npc_other_kill_msg": "{attacker} mató a [{victim}]", + + "hud.chat.loot_msg": "Recogiste [{item}]", + "hud.chat.loot_fail": "Tu inventario está lleno!", + "hud.chat.goodbye": "Adiós!", + "hud.chat.connection_lost": "Conexión perdida. Expulsando en {time} segundos.", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/hud/crafting.ron b/assets/voxygen/i18n/es_LA/hud/crafting.ron new file mode 100644 index 0000000000..4f959ad3a3 --- /dev/null +++ b/assets/voxygen/i18n/es_LA/hud/crafting.ron @@ -0,0 +1,16 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "hud.crafting": "Crafteo", + "hud.crafting.recipes": "Recetas", + "hud.crafting.ingredients": "Ingredientes:", + "hud.crafting.craft": "Fabricar", + "hud.crafting.tool_cata": "Requisitos:", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/hud/group.ron b/assets/voxygen/i18n/es_LA/hud/group.ron new file mode 100644 index 0000000000..59059f49a4 --- /dev/null +++ b/assets/voxygen/i18n/es_LA/hud/group.ron @@ -0,0 +1,23 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "hud.group": "Grupo", + "hud.group.invite_to_join": "[{name}] Te invito a su Grupo!", + "hud.group.invite": "Invitar", + "hud.group.kick": "Echar", + "hud.group.assign_leader": "Asignar Lider", + "hud.group.leave": "Salir del Grupo", + "hud.group.dead" : "Muerto", + "hud.group.out_of_range": "Fuera de Alcance", + "hud.group.add_friend": "Agregar a Amigos", + "hud.group.link_group": "Conectar Grupos", + "hud.group.in_menu": "Eligiendo Personaje", + "hud.group.members": "Miembros del Grupo", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/hud/map.ron b/assets/voxygen/i18n/es_LA/hud/map.ron new file mode 100644 index 0000000000..e4992d5bfc --- /dev/null +++ b/assets/voxygen/i18n/es_LA/hud/map.ron @@ -0,0 +1,26 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "hud.map.map_title": "Mapa", + "hud.map.qlog_title": "Misiones", + "hud.map.difficulty": "Dificultad", + "hud.map.towns": "Pueblos", + "hud.map.castles": "Castillos", + "hud.map.dungeons": "Calabozos", + "hud.map.caves": "Cuevas", + "hud.map.cave": "Cueva", + "hud.map.town": "Pueblo", + "hud.map.castle": "Castillo", + "hud.map.dungeon": "Calabozo", + "hud.map.difficulty_dungeon": "Dificultad de\n\nCalabozo: {difficulty}", + "hud.map.drag": "Arrastrar", + "hud.map.zoom": "Zoom", + "hud.map.recenter": "Centrar", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/hud/misc.ron b/assets/voxygen/i18n/es_LA/hud/misc.ron new file mode 100644 index 0000000000..9fc5821904 --- /dev/null +++ b/assets/voxygen/i18n/es_LA/hud/misc.ron @@ -0,0 +1,74 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "hud.do_not_show_on_startup": "No muestres esto al iniciar", + "hud.show_tips": "Mostrar Consejos", + "hud.quests": "Misiones", + "hud.you_died": "Moriste", + "hud.waypoint_saved": "Marcador Guardado", + + "hud.press_key_to_show_keybindings_fmt": "Presiona {key} para mostrar los controles del teclado", + "hud.press_key_to_toggle_lantern_fmt": "[{key}] Linterna", + "hud.press_key_to_show_debug_info_fmt": "Presiona {key} para mostrar información de depuración", + "hud.press_key_to_toggle_keybindings_fmt": "Presiona {key} para alternar los controles del teclado", + "hud.press_key_to_toggle_debug_info_fmt": "Presiona {key} para alternar la información de depuración", + + // Respawn message + "hud.press_key_to_respawn": r#"Presiona {key} para reaparecer en la ultima fogata que visitaste."#, + + // Welcome message + "hud.welcome": r#"Bienvenido a la alfa de Veloren! + + +Algunos consejos antes de que empieces: + + +Presiona F1 para ver los controles del teclado disponibles. + +Escribe /help en el chat para ver los comandos del chat + + +Hay cofres y otros objetos que aparecen al azar en el Mundo! + +Presiona E para recogerlos. + +Para usar lo que consigas de los cofres abre tu inventario con 'B'. + +Doble click en los objetos en tu bolsa para usarlos o equiparlos. + +Deshazte de ellos haciendo click en ellos y luego arrastralos fuera de la bolsa. + + +Las noches pueden volverse bastante oscuras en Veloren. + +Enciende tu Linterna escribiendo /lantern en el chat o presionando la G. + + +Quieres liberar tu cursor para cerrar esta ventana? Presiona TAB! + + +Disfruta tu estadía en el Mundo de Veloren."#, + +"hud.temp_quest_headline": r#"Por favor, ayúdanos Viajero!"#, +"hud.temp_quest_text": r#"Calabozos llenos de cultistas malvados +han emergido alrededor de nuestros pacíficos pueblos! + + +Consigue alguien que te acompañe, re-abastecete con comida +y derrota sus viles lideres y acólitos. + + +Tal vez incluso obtengas uno de sus +objetos infundidos con magia?"#, + "hud.spell": "Hechizos", + + "hud.free_look_indicator": "Vista libre activa", + "hud.auto_walk_indicator": "Caminata automática activa", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/hud/sct.ron b/assets/voxygen/i18n/es_LA/hud/sct.ron new file mode 100644 index 0000000000..88760ebbfb --- /dev/null +++ b/assets/voxygen/i18n/es_LA/hud/sct.ron @@ -0,0 +1,14 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + // SCT outputs + "hud.sct.experience": "{amount} Exp", + "hud.sct.block": "BLOQUEADO", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_LA/hud/settings.ron b/assets/voxygen/i18n/es_LA/hud/settings.ron new file mode 100644 index 0000000000..7764217fa7 --- /dev/null +++ b/assets/voxygen/i18n/es_LA/hud/settings.ron @@ -0,0 +1,101 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "hud.settings.general": "General", + "hud.settings.none": "Ninguno", + "hud.settings.press_behavior.toggle": "Alternar", + "hud.settings.press_behavior.hold": "Mantener", + "hud.settings.help_window": "Ventana de Ayuda", + "hud.settings.debug_info": "Info de Depuración", + "hud.settings.tips_on_startup": "Consejos de Inicio", + "hud.settings.ui_scale": "Escala de la Interfaz", + "hud.settings.relative_scaling": "Escalado Relativo", + "hud.settings.custom_scaling": "Escalado Personalizado", + "hud.settings.crosshair": "Mira", + "hud.settings.opacity": "Transparencia", + "hud.settings.hotbar": "Inventario Rápido", + "hud.settings.toggle_shortcuts": "Alternar Atajos", + "hud.settings.buffs_skillbar": "Buffs en la barra de habilidades.", + "hud.settings.buffs_mmap": "Buffs en el Minimapa", + "hud.settings.toggle_bar_experience": "Alternar Barra de Experiencia", + "hud.settings.scrolling_combat_text": "Texto de Combate con Desplazamiento", + "hud.settings.single_damage_number": "Números de Daño Singular", + "hud.settings.cumulated_damage": "Daño Acumulado", + "hud.settings.incoming_damage": "Daño Recibido", + "hud.settings.cumulated_incoming_damage": "Daño Recibido Acumulado", + "hud.settings.speech_bubble": "Burbuja de Diálogo", + "hud.settings.speech_bubble_dark_mode": "Burbuja de Diálogo en Modo Oscuro", + "hud.settings.speech_bubble_icon": "Burbuja de Diálogo en Modo Oscuro", + "hud.settings.energybar_numbers": "Números de la Barra de Energia", + "hud.settings.values": "Valores", + "hud.settings.percentages": "Porcentajes", + "hud.settings.chat": "Chat", + "hud.settings.background_opacity": "Transparencia del Fondo", + "hud.settings.chat_character_name": "Nombres de Personajes en el chat", + "hud.settings.loading_tips": "Consejos en Pantalla de Carga", + + "hud.settings.pan_sensitivity": "Sensibilidad de Desplazamiento de la Cámara", + "hud.settings.zoom_sensitivity": "Sensibilidad del Zoom", + "hud.settings.invert_scroll_zoom": "Invertir Desplazamiento de Zoom", + "hud.settings.invert_mouse_y_axis": "Invertir eje Y del Ratón", + "hud.settings.enable_mouse_smoothing": "Suavizado de la Cámara", + "hud.settings.free_look_behavior": "Modo de vista libre", + "hud.settings.auto_walk_behavior": "Modo de caminata automática", + "hud.settings.stop_auto_walk_on_input": "Frenar caminata automática", + + "hud.settings.view_distance": "Distancia de Visión", + "hud.settings.sprites_view_distance": "Distancia de Visión de Sprites", + "hud.settings.figures_view_distance": "Distancia de Visión de Entidades", + "hud.settings.maximum_fps": "FPS Máximos", + "hud.settings.fov": "Campo de Visión (grados)", + "hud.settings.gamma": "Gama", + "hud.settings.exposure": "Exposición", + "hud.settings.ambiance": "Brillo del Ambiente", + "hud.settings.antialiasing_mode": "Modo Anti-Aliasing", + "hud.settings.upscale_factor": "Factor de Escala", + "hud.settings.cloud_rendering_mode": "Modo de Renderizado de Nubes", + "hud.settings.fluid_rendering_mode": "Modo de Renderizado del Agua", + "hud.settings.fluid_rendering_mode.cheap": "Bajo", + "hud.settings.fluid_rendering_mode.shiny": "Alto", + "hud.settings.cloud_rendering_mode.minimal": "Mínimo", + "hud.settings.cloud_rendering_mode.low": "Bajo", + "hud.settings.cloud_rendering_mode.medium": "Medio", + "hud.settings.cloud_rendering_mode.high": "Alto", + "hud.settings.cloud_rendering_mode.ultra": "Ultra", + "hud.settings.fullscreen": "Pantalla Completa", + "hud.settings.fullscreen_mode": "Modo de Pantalla Completa", + "hud.settings.fullscreen_mode.exclusive": "Completo", + "hud.settings.fullscreen_mode.borderless": "Con Bordes", + "hud.settings.particles": "Particulas", + "hud.settings.resolution": "Resolución", + "hud.settings.bit_depth": "Profundidad de Bits", + "hud.settings.refresh_rate": "Taza de Refresco", + "hud.settings.save_window_size": " Guardar tamaño de ventana", + "hud.settings.lighting_rendering_mode": "Renderizado de la luz de la Linterna", + "hud.settings.lighting_rendering_mode.ashikhmin": "Tipo A - Alto", + "hud.settings.lighting_rendering_mode.blinnphong": "Tipo B - Medio", + "hud.settings.lighting_rendering_mode.lambertian": "Tipo L - Bajo", + "hud.settings.shadow_rendering_mode": "Renderizado de Sombras", + "hud.settings.shadow_rendering_mode.none": "Ninguno", + "hud.settings.shadow_rendering_mode.cheap": "Bajo", + "hud.settings.shadow_rendering_mode.map": "Alto", + "hud.settings.shadow_rendering_mode.map.resolution": "Resolución", + "hud.settings.lod_detail": "Detalle de LoD", + "hud.settings.save_window_size": "Recordar tamaño de ventana", //It's repeated in en.ron (359 and 370) + + + "hud.settings.music_volume": "Volumen de Música", + "hud.settings.sound_effect_volume": "Volumen de Efectos de Sonido", + "hud.settings.audio_device": "Dispositivo de Audio", + + "hud.settings.awaitingkey": "Presiona una tecla...", + "hud.settings.unbound": "Ninguno", + "hud.settings.reset_keybinds": "Reestablecer Controles", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/PL/template.ron b/assets/voxygen/i18n/es_LA/hud/skills.ron similarity index 77% rename from assets/voxygen/i18n/PL/template.ron rename to assets/voxygen/i18n/es_LA/hud/skills.ron index d104f24ca9..f5c5c45d15 100644 --- a/assets/voxygen/i18n/PL/template.ron +++ b/assets/voxygen/i18n/es_LA/hud/skills.ron @@ -1,9 +1,11 @@ /// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American ( string_map: { - }, + vector_map: { } ) diff --git a/assets/voxygen/i18n/es_LA/hud/social.ron b/assets/voxygen/i18n/es_LA/hud/social.ron new file mode 100644 index 0000000000..2a4fb78afd --- /dev/null +++ b/assets/voxygen/i18n/es_LA/hud/social.ron @@ -0,0 +1,22 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "hud.social": "Lista de jugadores", + "hud.social.online": "En Línea", + "hud.social.friends": "Amigos", + "hud.social.not_yet_available": "Aún no esta disponible", + "hud.social.faction": "Facción", + "hud.social.play_online_fmt": "{nb_player} jugador(es) en línea", + "hud.social.name": "Nombre", + "hud.social.level": "Nivel", + "hud.social.zone": "Zona", + "hud.social.account": "Cuenta", + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/sv/template.ron b/assets/voxygen/i18n/es_LA/hud/trade.ron similarity index 77% rename from assets/voxygen/i18n/sv/template.ron rename to assets/voxygen/i18n/es_LA/hud/trade.ron index 014f73c1cc..7f309aea52 100644 --- a/assets/voxygen/i18n/sv/template.ron +++ b/assets/voxygen/i18n/es_LA/hud/trade.ron @@ -1,12 +1,12 @@ /// WARNING: Localization files shall be saved in UTF-8 format without BOM -/// Localization for "global" Swedish +/// Localization for Latin-American ( string_map: { - }, vector_map: { } ) + diff --git a/assets/voxygen/i18n/es_LA/main.ron b/assets/voxygen/i18n/es_LA/main.ron new file mode 100644 index 0000000000..f3c30f5008 --- /dev/null +++ b/assets/voxygen/i18n/es_LA/main.ron @@ -0,0 +1,83 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Latin-American +( + string_map: { + "main.username": "Usuario", + "main.server": "Servidor", + "main.password": "Contraseña", + "main.connecting": "Conectando", + "main.creating_world": "Creando Mundo", + "main.tip": "Consejo:", + + // Welcome notice that appears the first time Veloren is started + "main.notice": r#"Bienvenido a la version alfa de Veloren! + +Antes de que te diviertas, por favor ten en cuenta lo siguiente: + +- Esta es una alfa muy temprana, espera fallos, jugabilidad extremadamente incompleta, mecánicas sin pulir y características faltantes. + +- Si tienes críticas constructivas o reportes de fallos, puedes contactarnos por reddit, GitLab, o por el server de Discord de nuestra comunidad. + +- Veloren esta licenciado bajo la licencia GPL 3 open-source (código abierto). Eso significa que tienes la libertad de jugar, modificar, y redistribuir el Juego como + desees (siempre y cuando dicho trabajo también este licenciado como GPL 3). + +- Veloren es un proyecto en comunidad sin ánimo de lucro, y todos los que trabajan en el son voluntarios. +Si te gusta lo que ves, eres bienvenido a unirte a los equipos de desarrollo o de arte! + +Gracias por tomarte el tiempo de leer este mensaje, esperamos que disfrutes el juego! + +~ Los Desarrolladores de Veloren"#, + + // Login process description + "main.login_process": r#"Información sobre el proceso para Iniciar Sesión: + +Por favor ten en cuenta que ahora necesitas una cuenta +para jugar en servidores con autenticación activada. + +Puedes crearte una cuenta en + +https://veloren.net/account/."#, + "main.login.server_not_found": "No se encontró el servidor", + "main.login.authentication_error": "Error de autenticación en el servidor", + "main.login.server_full": "El servidor está lleno", + "main.login.untrusted_auth_server": "El servidor de autenticación no es confiable", + "main.login.outdated_client_or_server": "ServidorEnloquecido: Probablemente las versiones son incompatibles, intenta actualizar tu cliente.", + "main.login.timeout": "Tiempo de espera agotado: El servidor no respondio a tiempo. (Puede estar sobrecargado o tener problemas de red).", + "main.login.server_shut_down": "El servidor se apagó", + "main.login.network_error": "Error de red", + "main.login.failed_sending_request": "El pedido al servidor de autenticacion fallo", + "main.login.invalid_character": "El personaje seleccionado no es válido", + "main.login.client_crashed": "El cliente crasheó", + "main.login.not_on_whitelist": "No estás en la lista. Contacta al Dueño del Servidor si quieres unirte.", + "main.login.banned": "Usted ha sido baneado por la siguiente razón", + "main.login.kicked": "Te han echado por la siguiente razón", + "main.login.select_language": "Elige un idioma", + + "main.servers.select_server": "Elige un servidor", + }, + + + vector_map: { + "loading.tips": [ + "Presiona 'G' para encender tu linterna.", + "Presiona 'F1' para ver los controles predeterminados.", + "Puedes escribir /say o /s para chatear solo con jugadores alrededor tuyo.", + "Puedes escribr /region o /r para chatear solo con jugadores que están a unos cien bloques alrededor tuyo.", + "Para enviar mensajes privados escribe /tell seguido de el nombre de un jugador y luego tu mensaje.", + "NPCs con el mismo nivel pueden tener una dificultad diferente.", + "Observa el terreno en búsqueda de comida, cofres y botines!", + "¿Inventario lleno de comida? Intenta craftear mejor comida con ella!", + "¿Te preguntas dónde debes hacerlo? Los Dungeons están marcados con puntos marrones en el mapa!", + "No te olvides de ajustar los gráficos de tu pc. Presiona 'N' para abrir la configuración.", + "Jugar con otros es divertido! Presiona 'O' para ver quien esta conectado.", + "Un NPC con un craneo debajo de su barra de vida es bastante más poderoso comparado contigo.", + "Presiona 'J' para bailar. Fiesta!", + "Presiona 'Shift-Izquierdo' para abrir tu planeador y conquistar los cielos.", + "Veloren está aún en Alfa temprana. Hacemos lo mejor para mejorar día a día!", + "Si te quieres unir al equipo de desarrolladores o solo chatear con nosotros, únete a nuestro servidor en Discord.", + "Puedes elegir mostrar tu cantidad de vida en la barra de vida en la configuración.", + "Para ver tus estadísticas, haz click en el botón 'Estadísticas' en el inventario.", + ], + } +) diff --git a/assets/voxygen/i18n/es_LA/npc.ron b/assets/voxygen/i18n/es_LA/npc.ron new file mode 100644 index 0000000000..b2ac47bf75 --- /dev/null +++ b/assets/voxygen/i18n/es_LA/npc.ron @@ -0,0 +1,93 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "latinoamericano" Latin-American +( + string_map: { + + }, + + + vector_map: { + "npc.speech.villager_under_attack": [ + "Ayuda, ¡Me están atacando!", + "¡Ayuda! ¡Me están atacando!", + "¡Auch! ¡Me están atacando!", + "¡Auch! ¡Me están atacando! ¡Ayuda!", + "¡Ayudenme! ¡Me están atacando!", + "¡Me están atacando! ¡Ayuda!", + "¡Me están atacando! ¡Ayudenme!", + "¡Ayuda!", + "¡Ayuda! ¡Ayuda!", + "¡Ayuda! ¡Ayuda! ¡Ayuda!", + "¡Me están atacando!", + "¡AAAHH! ¡Me están atacando!", + "¡AAAHH! ¡Me están atacando! ¡Ayuda!", + "Ayuda! Nos están atacando!", + "¡Ayuda! ¡Asesino!", + "¡Ayuda! ¡Hay un asesino suelto!", + "¡Ayuda! ¡están intentando matarme!", + "Guardias, ¡Me están atacando!", + "¡Guardias! ¡Me están atacando!", + "¡Me están atacando! ¡Guardias!", + "¡Ayuda! ¡Guardias! ¡Me están atacando!", + "¡Guardias! ¡Vengan rapido!", + "¡Guardias, Guardias!", + "¡Guardias! ¡Me esta atacando un villano!", + "¡Guardias, eliminen a este desagradable villano!", + "¡Guardias! ¡Ahi esta el asesino!", + "¡Guardias! ¡Ayúdenme!", + "No te saldrás con la tuya, ¡Guardias!", + "¡Eres despreciable!", + "¡Ayúdenme!", + "¡Ayuda! ¡Porfavor!", + "¡Auch! ¡Guardias! ¡Ayuda!", + "¡Vienen por mi!", + "¡Ayuda! ¡Ayuda! Estoy siendo atacado", + "Ah, se nota que la violencia es parte del sistema.", + "¡Esto no es más que un rasguño!", + "Deja de hacer eso!", + "¿Qué te hice para merecer esto?", + "Por favor, para de atacarme!", + "Hey! Mira hacia adonde apuntas con esa cosa", + "Desgraciado, vete de aqui!", + "Para ya! Vete!", + "Me estas haciendo enojar!", + "Hey!¿Quién te piensas que eres?", + "Te arrancaré la cabeza por eso!", + "Detente, por favor! No llevo nada de valor!", + "Te voy a mandar a mi hermano, el es más grande que yo!", + "Nooo, le contaré a mi madre!", + "Maldito seas!", + "Por favor no lo hagas.", + "Eso no fue agradable!", + "Tu arma funciona, ahora aléjala!", + "Si claro...", + "Por favor, tengo familia!", + "Soy demasiado jóven para morir!", + "¿Podemos hablar sobre esto?", + "La violencia no resuelve nada!", + "Este día se esta convirtiendo en uno muy feo...", + "Hey, eso dolió!", + "Ayy!", + "Qué violento!", + "Detente, te lo suplico!", + "Ojala te enfermes!", + "Esto no es divertido.", + "¡¿Cómo te atreves?!", + "Vas a pagar por eso!", + "Sigue con eso y lo lamentarás!", + "No hagas que te lastime!", + "Tiene que ser un malentendido!", + "No necesitas hacer esto!", + "Vete, demonio!", + "Eso realmente dolió!", + "¿Por qué harias eso?", + "Por todos los Santos, para!", + "Me habrás confudido con alguien más!", + "No me merezco esto!", + "Por favor, no lo hagas de nuevo", + "Guardias, tiren este monstruo al lago", + "Invocaré mis demonios en ti!", + ], + } +) diff --git a/assets/voxygen/i18n/es_la/_manifest.ron b/assets/voxygen/i18n/es_la/_manifest.ron deleted file mode 100644 index f746bc9812..0000000000 --- a/assets/voxygen/i18n/es_la/_manifest.ron +++ /dev/null @@ -1,665 +0,0 @@ -/// 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 "latinoamericano" Latin-American -( - metadata: ( - language_name: "Español Latino", - language_identifier: "es_la", - ), - convert_utf8_to_ascii: false, - fonts: { - "opensans": Font ( - asset_key: "voxygen.font.OpenSans-Regular", - scale_ratio: 1.0, - ), - "metamorph": Font ( - asset_key: "voxygen.font.Metamorphous-Regular", - scale_ratio: 1.0, - ), - "alkhemi": Font ( - asset_key: "voxygen.font.Alkhemikal", - scale_ratio: 1.0, - ), - "wizard": Font ( - asset_key: "voxygen.font.wizard", - scale_ratio: 1.0, - ), - "cyri": Font ( - asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", - scale_ratio: 1.0, - ), - }, - string_map: { - /// Start Common section - // Texts used in multiple locations with the same formatting - "common.username": "Usuario", - "common.singleplayer": "Un Jugador", - "common.multiplayer": "Multijugador", - "common.servers": "Servidores", - "common.quit": "Salir", - "common.settings": "Configuración", - "common.languages": "Idiomas", //this one is repeated in en.ron (53 and 59) - "common.interface": "Interfaz", - "common.gameplay": "Jugabilidad", - "common.controls": "Controles", - "common.video": "Gráficos", - "common.sound": "Sonido", - "common.resume": "Continuar", - "common.characters": "Personajes", - "common.close": "Cerrar", - "common.yes": "Sí", - "common.no": "No", - "common.back": "Volver", - "common.create": "Crear", - "common.okay": "Ok", - "common.add": "Agregar", - "common.accept": "Aceptar", - "common.decline": "Rechazar", - "common.disclaimer": "Cuidado", - "common.cancel": "Cancelar", - "common.none": "Ninguno", - "common.error": "Error", - "common.fatal_error": "Error Fatal", - "common.you": "Tú", - "common.automatic": "Automático", - "common.random": "Aleatorio", - // Settings Window title - "common.interface_settings": "Ajustes de Interfaz", - "common.gameplay_settings": "Ajustes de Jugabilidad", - "common.controls_settings": "Ajustes de Controles", - "common.video_settings": "Ajustes de Graficos", - "common.sound_settings": "Ajustes de Sonido", - "common.language_settings": "Ajustes de Idiomas", - - // Message when connection to the server is lost - "common.connection_lost": r#"Conexión perdida! -Se reinició el servidor? -El cliente está actualizado?"#, - - - "common.species.orc": "Orco", - "common.species.human": "Humano", - "common.species.dwarf": "Enano", - "common.species.elf": "Elfo", - "common.species.undead": "No-Muerto", - "common.species.danari": "Danari", - - "common.weapons.axe": "Hacha", - "common.weapons.sword": "Espada", - "common.weapons.staff": "Vara Mágica", - "common.weapons.bow": "Arco", - "common.weapons.hammer": "Martillo", - "common.weapons.sceptre": "Cetro curativo", - "common.rand_appearance": "Nombre y Apariencia Aleatoria", - /// End Common section - - - /// Start Main screen section - "main.username": "Usuario", - "main.server": "Servidor", - "main.password": "Contraseña", - "main.connecting": "Conectando", - "main.creating_world": "Creando Mundo", - "main.tip": "Consejo:", - - // Welcome notice that appears the first time Veloren is started - "main.notice": r#"Bienvenido a la version alfa de Veloren! - -Antes de que te diviertas, por favor ten en cuenta lo siguiente: - -- Esta es una alfa muy temprana, espera fallos, jugabilidad extremadamente incompleta, mecánicas sin pulir y características faltantes. - -- Si tienes críticas constructivas o reportes de fallos, puedes contactarnos por reddit, GitLab, o por el server de Discord de nuestra comunidad. - -- Veloren esta licenciado bajo la licencia GPL 3 open-source (código abierto). Eso significa que tienes la libertad de jugar, modificar, y redistribuir el Juego como - desees (siempre y cuando dicho trabajo también este licenciado como GPL 3). - -- Veloren es un proyecto en comunidad sin ánimo de lucro, y todos los que trabajan en el son voluntarios. -Si te gusta lo que ves, eres bienvenido a unirte a los equipos de desarrollo o de arte! - -Gracias por tomarte el tiempo de leer este mensaje, esperamos que disfrutes el juego! - -~ Los Desarrolladores de Veloren"#, - - // Login process description - "main.login_process": r#"Información sobre el proceso para Iniciar Sesión: - -Por favor ten en cuenta que ahora necesitas una cuenta -para jugar en servidores con autenticación activada. - -Puedes crearte una cuenta en - -https://veloren.net/account/."#, - "main.login.server_not_found": "No se encontró el servidor", - "main.login.authentication_error": "Error de autenticación en el servidor", - "main.login.server_full": "El servidor está lleno", - "main.login.untrusted_auth_server": "El servidor de autenticación no es confiable", - "main.login.outdated_client_or_server": "ServidorEnloquecido: Probablemente las versiones son incompatibles, intenta actualizar tu cliente.", - "main.login.timeout": "Tiempo de espera agotado: El servidor no respondio a tiempo. (Puede estar sobrecargado o tener problemas de red).", - "main.login.server_shut_down": "El servidor se apagó", - "main.login.network_error": "Error de red", - "main.login.failed_sending_request": "El pedido al servidor de autenticacion fallo", - "main.login.invalid_character": "El personaje seleccionado no es válido", - "main.login.client_crashed": "El cliente crasheó", - "main.login.not_on_whitelist": "No estás en la lista. Contacta al Dueño del Servidor si quieres unirte.", - "main.login.banned": "Usted ha sido baneado por la siguiente razón", - "main.login.kicked": "Te han echado por la siguiente razón", - "main.login.select_language": "Elige un idioma", - - "main.servers.select_server": "Elige un servidor", - - /// End Main screen section - - - /// Start HUD Section - "hud.do_not_show_on_startup": "No muestres esto al iniciar", - "hud.show_tips": "Mostrar Consejos", - "hud.quests": "Misiones", - "hud.you_died": "Moriste", - "hud.waypoint_saved": "Marcador Guardado", - - "hud.press_key_to_show_keybindings_fmt": "Presiona {key} para mostrar los controles del teclado", - "hud.press_key_to_toggle_lantern_fmt": "[{key}] Linterna", - "hud.press_key_to_show_debug_info_fmt": "Presiona {key} para mostrar información de depuración", - "hud.press_key_to_toggle_keybindings_fmt": "Presiona {key} para alternar los controles del teclado", - "hud.press_key_to_toggle_debug_info_fmt": "Presiona {key} para alternar la información de depuración", - - // Chat outputs - "hud.chat.online_msg": "[{name}] se ha conectado.", - "hud.chat.offline_msg": "[{name}] se ha desconectado.", - - "hud.chat.default_death_msg": "[{name}] murió", - "hud.chat.environmental_kill_msg": "[{name}] murió en {environment}", - "hud.chat.fall_kill_msg": "[{name}] murió por daño de caída", - "hud.chat.suicide_msg": "[{name}] murió por heridas autoinfligidas", - - "hud.chat.pvp_melee_kill_msg": "[{attacker}] derrotó a [{victim}]", - "hud.chat.pvp_ranged_kill_msg": "[{attacker}] le disparó a [{victim}]", - "hud.chat.pvp_explosion_kill_msg": "[{attacker}] hizo explotar a [{victim}]", - "hud.chat.pvp_energy_kill_msg": "[{attacker}] usó magia para matar a [{victim}]", - "hud.chat.pvp_buff_kill_msg": "[{attacker}] mató a [{victim}]", - - - "hud.chat.npc_melee_kill_msg": "{attacker} mató a [{victim}]", - "hud.chat.npc_ranged_kill_msg": "{attacker} le disparó a [{victim}]", - "hud.chat.npc_explosion_kill_msg": "{attacker} hizo explotar a [{victim}]", - "hud.chat.npc_energy_kill_msg": "{attacker} usó magia para matar a [{victim}]", - "hud.chat.npc_other_kill_msg": "{attacker} mató a [{victim}]", - - "hud.chat.loot_msg": "Recogiste [{item}]", - "hud.chat.loot_fail": "Tu inventario está lleno!", - "hud.chat.goodbye": "Adiós!", - "hud.chat.connection_lost": "Conexión perdida. Expulsando en {time} segundos.", - - // SCT outputs - "hud.sct.experience": "{amount} Exp", - "hud.sct.block": "BLOQUEADO", - - // Respawn message - "hud.press_key_to_respawn": r#"Presiona {key} para reaparecer en la ultima fogata que visitaste."#, - - // Welcome message - "hud.welcome": r#"Bienvenido a la alfa de Veloren! - - -Algunos consejos antes de que empieces: - - -Presiona F1 para ver los controles del teclado disponibles. - -Escribe /help en el chat para ver los comandos del chat - - -Hay cofres y otros objetos que aparecen al azar en el Mundo! - -Presiona E para recogerlos. - -Para usar lo que consigas de los cofres abre tu inventario con 'B'. - -Doble click en los objetos en tu bolsa para usarlos o equiparlos. - -Deshazte de ellos haciendo click en ellos y luego arrastralos fuera de la bolsa. - - -Las noches pueden volverse bastante oscuras en Veloren. - -Enciende tu Linterna escribiendo /lantern en el chat o presionando la G. - - -Quieres liberar tu cursor para cerrar esta ventana? Presiona TAB! - - -Disfruta tu estadía en el Mundo de Veloren."#, - -"hud.temp_quest_headline": r#"Por favor, ayúdanos Viajero!"#, -"hud.temp_quest_text": r#"Calabozos llenos de cultistas malvados -han emergido alrededor de nuestros pacíficos pueblos! - - -Consigue alguien que te acompañe, re-abastecete con comida -y derrota sus viles lideres y acólitos. - - -Tal vez incluso obtengas uno de sus -objetos infundidos con magia?"#, - - - - // Inventory - "hud.bag.inventory": "Inventario de {playername}", - "hud.bag.stats_title": "Estadísticas de {playername}", - "hud.bag.exp": "Exp", - "hud.bag.armor": "Armadura", - "hud.bag.stats": "Estadísticas", - "hud.bag.head": "Cabeza", - "hud.bag.neck": "Cuello", - "hud.bag.tabard": "Tabardo", - "hud.bag.shoulders": "Hombros", - "hud.bag.chest": "Torso", - "hud.bag.hands": "Manos", - "hud.bag.lantern": "Linterna", - "hud.bag.glider": "Planeador", - "hud.bag.belt": "Cinturón", - "hud.bag.ring": "Anillo", - "hud.bag.back": "Espalda", - "hud.bag.legs": "Piernas", - "hud.bag.feet": "Pies", - "hud.bag.mainhand": "Mano Principal", - "hud.bag.offhand": "Mano Secundaria", - - - // Map and Questlog - "hud.map.map_title": "Mapa", - "hud.map.qlog_title": "Misiones", - - // Settings - "hud.settings.general": "General", - "hud.settings.none": "Ninguno", - "hud.settings.press_behavior.toggle": "Alternar", - "hud.settings.press_behavior.hold": "Mantener", - "hud.settings.help_window": "Ventana de Ayuda", - "hud.settings.debug_info": "Info de Depuración", - "hud.settings.tips_on_startup": "Consejos de Inicio", - "hud.settings.ui_scale": "Escala de la Interfaz", - "hud.settings.relative_scaling": "Escalado Relativo", - "hud.settings.custom_scaling": "Escalado Personalizado", - "hud.settings.crosshair": "Mira", - "hud.settings.transparency": "Transparencia", - "hud.settings.hotbar": "Inventario Rápido", - "hud.settings.toggle_shortcuts": "Alternar Atajos", - "hud.settings.buffs_skillbar": "Buffs en la barra de habilidades.", - "hud.settings.buffs_mmap": "Buffs en el Minimapa", - "hud.settings.toggle_bar_experience": "Alternar Barra de Experiencia", - "hud.settings.scrolling_combat_text": "Texto de Combate con Desplazamiento", - "hud.settings.single_damage_number": "Números de Daño Singular", - "hud.settings.cumulated_damage": "Daño Acumulado", - "hud.settings.incoming_damage": "Daño Recibido", - "hud.settings.cumulated_incoming_damage": "Daño Recibido Acumulado", - "hud.settings.speech_bubble": "Burbuja de Diálogo", - "hud.settings.speech_bubble_dark_mode": "Burbuja de Diálogo en Modo Oscuro", - "hud.settings.speech_bubble_icon": "Burbuja de Diálogo en Modo Oscuro", - "hud.settings.energybar_numbers": "Números de la Barra de Energia", - "hud.settings.values": "Valores", - "hud.settings.percentages": "Porcentajes", - "hud.settings.chat": "Chat", - "hud.settings.background_transparency": "Transparencia del Fondo", - "hud.settings.chat_character_name": "Nombres de Personajes en el chat", - "hud.settings.loading_tips": "Consejos en Pantalla de Carga", - - "hud.settings.pan_sensitivity": "Sensibilidad de Desplazamiento de la Cámara", - "hud.settings.zoom_sensitivity": "Sensibilidad del Zoom", - "hud.settings.invert_scroll_zoom": "Invertir Desplazamiento de Zoom", - "hud.settings.invert_mouse_y_axis": "Invertir eje Y del Ratón", - "hud.settings.enable_mouse_smoothing": "Suavizado de la Cámara", - "hud.settings.free_look_behavior": "Modo de vista libre", - "hud.settings.auto_walk_behavior": "Modo de caminata automática", - "hud.settings.stop_auto_walk_on_input": "Frenar caminata automática", - - "hud.settings.view_distance": "Distancia de Visión", - "hud.settings.sprites_view_distance": "Distancia de Visión de Sprites", - "hud.settings.figures_view_distance": "Distancia de Visión de Entidades", - "hud.settings.maximum_fps": "FPS Máximos", - "hud.settings.fov": "Campo de Visión (grados)", - "hud.settings.gamma": "Gama", - "hud.settings.exposure": "Exposición", - "hud.settings.ambiance": "Brillo del Ambiente", - "hud.settings.antialiasing_mode": "Modo Anti-Aliasing", - "hud.settings.upscale_factor": "Factor de Escala", - "hud.settings.cloud_rendering_mode": "Modo de Renderizado de Nubes", - "hud.settings.fluid_rendering_mode": "Modo de Renderizado del Agua", - "hud.settings.fluid_rendering_mode.cheap": "Bajo", - "hud.settings.fluid_rendering_mode.shiny": "Alto", - "hud.settings.cloud_rendering_mode.minimal": "Mínimo", - "hud.settings.cloud_rendering_mode.low": "Bajo", - "hud.settings.cloud_rendering_mode.medium": "Medio", - "hud.settings.cloud_rendering_mode.high": "Alto", - "hud.settings.cloud_rendering_mode.ultra": "Ultra", - "hud.settings.fullscreen": "Pantalla Completa", - "hud.settings.fullscreen_mode": "Modo de Pantalla Completa", - "hud.settings.fullscreen_mode.exclusive": "Completo", - "hud.settings.fullscreen_mode.borderless": "Con Bordes", - "hud.settings.particles": "Particulas", - "hud.settings.resolution": "Resolución", - "hud.settings.bit_depth": "Profundidad de Bits", - "hud.settings.refresh_rate": "Taza de Refresco", - "hud.settings.save_window_size": " Guardar tamaño de ventana", - "hud.settings.lighting_rendering_mode": "Renderizado de la luz de la Linterna", - "hud.settings.lighting_rendering_mode.ashikhmin": "Tipo A - Alto", - "hud.settings.lighting_rendering_mode.blinnphong": "Tipo B - Medio", - "hud.settings.lighting_rendering_mode.lambertian": "Tipo L - Bajo", - "hud.settings.shadow_rendering_mode": "Renderizado de Sombras", - "hud.settings.shadow_rendering_mode.none": "Ninguno", - "hud.settings.shadow_rendering_mode.cheap": "Bajo", - "hud.settings.shadow_rendering_mode.map": "Alto", - "hud.settings.shadow_rendering_mode.map.resolution": "Resolución", - "hud.settings.lod_detail": "Detalle de LoD", - "hud.settings.save_window_size": "Recordar tamaño de ventana", //It's repeated in en.ron (359 and 370) - - - "hud.settings.music_volume": "Volumen de Música", - "hud.settings.sound_effect_volume": "Volumen de Efectos de Sonido", - "hud.settings.audio_device": "Dispositivo de Audio", - - "hud.settings.awaitingkey": "Presiona una tecla...", - "hud.settings.unbound": "Ninguno", - "hud.settings.reset_keybinds": "Reestablecer Controles", - - "hud.social": "Lista de jugadores", - "hud.social.online": "En Línea", - "hud.social.friends": "Amigos", - "hud.social.not_yet_available": "Aún no esta disponible", - "hud.social.faction": "Facción", - "hud.social.play_online_fmt": "{nb_player} jugador(es) en línea", - "hud.social.name": "Nombre", - "hud.social.level": "Nivel", - "hud.social.zone": "Zona", - "hud.social.account": "Cuenta", - - - "hud.crafting": "Crafteo", - "hud.crafting.recipes": "Recetas", - "hud.crafting.ingredients": "Ingredientes:", - "hud.crafting.craft": "Fabricar", - "hud.crafting.tool_cata": "Requisitos:", - - "hud.group": "Grupo", - "hud.group.invite_to_join": "[{name}] Te invito a su Grupo!", - "hud.group.invite": "Invitar", - "hud.group.kick": "Echar", - "hud.group.assign_leader": "Asignar Lider", - "hud.group.leave": "Salir del Grupo", - "hud.group.dead" : "Muerto", - "hud.group.out_of_range": "Fuera de Alcance", - "hud.group.add_friend": "Agregar a Amigos", - "hud.group.link_group": "Conectar Grupos", - "hud.group.in_menu": "Eligiendo Personaje", - "hud.group.members": "Miembros del Grupo", - - "hud.spell": "Hechizos", - - "hud.free_look_indicator": "Vista libre activa", - "hud.auto_walk_indicator": "Caminata automática activa", - - "hud.map.difficulty": "Dificultad", - "hud.map.towns": "Pueblos", - "hud.map.castles": "Castillos", - "hud.map.dungeons": "Calabozos", - "hud.map.caves": "Cuevas", - "hud.map.cave": "Cueva", - "hud.map.town": "Pueblo", - "hud.map.castle": "Castillo", - "hud.map.dungeon": "Calabozo", - "hud.map.difficulty_dungeon": "Dificultad de\n\nCalabozo: {difficulty}", - "hud.map.drag": "Arrastrar", - "hud.map.zoom": "Zoom", - "hud.map.recenter": "Centrar", - - /// End HUD section - - - /// Start GameInput section - - "gameinput.primary": "Ataque Básico", - "gameinput.secondary": "Ataque Secundario/Bloquear/Apuntar", - "gameinput.slot1": "Ranura de Inventario Rápido 1", - "gameinput.slot2": "Ranura de Inventario Rápido 2", - "gameinput.slot3": "Ranura de Inventario Rápido 3", - "gameinput.slot4": "Ranura de Inventario Rápido 4", - "gameinput.slot5": "Ranura de Inventario Rápido 5", - "gameinput.slot6": "Ranura de Inventario Rápido 6", - "gameinput.slot7": "Ranura de Inventario Rápido 7", - "gameinput.slot8": "Ranura de Inventario Rápido 8", - "gameinput.slot9": "Ranura de Inventario Rápido 9", - "gameinput.slot10": "Ranura de Inventario Rápido 10", - "gameinput.swaploadout": "Cambiar Equipamiento", - "gameinput.togglecursor": "Mostrar Cursor", - "gameinput.help": "Mostrar Ventana de Ayuda", - "gameinput.toggleinterface": "Mostrar Interfaz", - "gameinput.toggledebug": "Mostrar FPS y la Info de Depuración", - "gameinput.screenshot": "Tomar Captura de Pantalla", - "gameinput.toggleingameui": "Mostrar Nombres", - "gameinput.fullscreen": "Pantalla Completa", - "gameinput.moveforward": "Moverse hacia Adelante", - "gameinput.moveleft": "Moverse hacia la Izquierda", - "gameinput.moveright": "Moverse hacia la Derecha", - "gameinput.moveback": "Moverse hacia Atras", - "gameinput.jump": "Saltar", - "gameinput.glide": "Planeador", - "gameinput.roll": "Rodar", - "gameinput.climb": "Trepar", - "gameinput.climbdown": "Descender", - "gameinput.wallleap": "Saltar a la Pared", - "gameinput.togglelantern": "Encender Farol", - "gameinput.mount": "Montar", - "gameinput.chat": "Abrir Chat", - "gameinput.enter": "Entrar", - "gameinput.command": "Insertar Comandos", - "gameinput.escape": "Escapar", - "gameinput.map": "Mapa", - "gameinput.bag": "Mochila", - "gameinput.social": "Lista de jugadores", - "gameinput.sit": "Sentarse", - "gameinput.spellbook": "Hechizos", - "gameinput.settings": "Configuración", - "gameinput.respawn": "Reaparecer", - "gameinput.charge": "Cargar", - "gameinput.togglewield": "Alternar empuñadura", - "gameinput.interact": "Interactuar", - "gameinput.freelook": "Vista Libre", - "gameinput.autowalk": "Caminata Automática", - "gameinput.dance": "Bailar", - "gameinput.select": "Seleccione la Entidad", - "gameinput.acceptgroupinvite": "Aceptar invitación al grupo", - "gameinput.declinegroupinvite": "Rechazar invitación al grupo", - "gameinput.crafting": "Craftear", - "gameinput.fly": "Volar", - "gameinput.sneak": "Agacharse", - "gameinput.swimdown": "Sumergirse", - "gameinput.swimup": "Nadar hacia arriba", - - /// End GameInput section - - - /// Start chracter selection section - "char_selection.loading_characters": "Cargando personajes...", - "char_selection.delete_permanently": "¿Borrar este Personaje permanentemente?", - "char_selection.deleting_character": "Borrando Personaje...", - "char_selection.change_server": "Cambiar Servidor", - "char_selection.enter_world": "Entrar al Mundo", - "char_selection.logout": "Cerrar Sesión", - "char_selection.create_new_character": "Crear Nuevo Personaje", - "char_selection.creating_character": "Creando Personaje...", - "char_selection.character_creation": "Creación de Personaje", - - "char_selection.human_default": "Humano por defecto", - "char_selection.level_fmt": "Nivel {level_nb}", - "char_selection.uncanny_valley": "Valle Misterioso", - "char_selection.plains_of_uncertainty": "Planicies de la Incertidumbre", - "char_selection.beard": "Barba", - "char_selection.hair_style": "Peinado", - "char_selection.hair_color": "Color de Pelo", - "char_selection.eye_color": "Color de Ojos", - "char_selection.skin": "Color de Piel", - "char_selection.eyeshape": "Detalles de los Ojos", - "char_selection.accessories": "Accesorios", - "char_selection.create_info_name": "Tu Personaje necesita un nombre!", - - /// End chracter selection section - - - /// Start character window section - "character_window.character_name": "Nombre de Personaje", - // Character stats - "character_window.character_stats": r#"Resistencia - -Estado Físico - -Valentía - -Protección -"#, - /// End character window section - - - /// Start Escape Menu Section - "esc_menu.logout": "Cerrar Sesión", - "esc_menu.quit_game": "Salir del Juego", - /// End Escape Menu Section - - /// Buffs and Debuffs - "buff.remove": "Click para quitar", - "buff.title.missing": "Sin Título", - "buff.desc.missing": "Sin Descripción", - // Buffs - "buff.title.heal": "Curación", - "buff.desc.heal": "Recupera vida durante un tiempo.", - "buff.title.potion": "Poción", - "buff.desc.potion": "Bebiendo...", - "buff.title.saturation": "Saturación", - "buff.desc.saturation": "Recupera vida durante un tiempo por objetos.", - "buff.title.campfire_heal": "Curación de fogata", - "buff.desc.campfire_heal": "Descansar en una fogata recupera {rate}% por segundo.", - // Debuffs - "buff.title.bleed": "Sangrando", - "buff.desc.bleed": "Inflinge daño regularmente.", - - - }, - - - vector_map: { - "loading.tips": [ - "Presiona 'G' para encender tu linterna.", - "Presiona 'F1' para ver los controles predeterminados.", - "Puedes escribir /say o /s para chatear solo con jugadores alrededor tuyo.", - "Puedes escribr /region o /r para chatear solo con jugadores que están a unos cien bloques alrededor tuyo.", - "Para enviar mensajes privados escribe /tell seguido de el nombre de un jugador y luego tu mensaje.", - "NPCs con el mismo nivel pueden tener una dificultad diferente.", - "Observa el terreno en búsqueda de comida, cofres y botines!", - "¿Inventario lleno de comida? Intenta craftear mejor comida con ella!", - "¿Te preguntas dónde debes hacerlo? Los Dungeons están marcados con puntos marrones en el mapa!", - "No te olvides de ajustar los gráficos de tu pc. Presiona 'N' para abrir la configuración.", - "Jugar con otros es divertido! Presiona 'O' para ver quien esta conectado.", - "Un NPC con un craneo debajo de su barra de vida es bastante más poderoso comparado contigo.", - "Presiona 'J' para bailar. Fiesta!", - "Presiona 'Shift-Izquierdo' para abrir tu planeador y conquistar los cielos.", - "Veloren está aún en Alfa temprana. Hacemos lo mejor para mejorar día a día!", - "Si te quieres unir al equipo de desarrolladores o solo chatear con nosotros, únete a nuestro servidor en Discord.", - "Puedes elegir mostrar tu cantidad de vida en la barra de vida en la configuración.", - "Para ver tus estadísticas, haz click en el botón 'Estadísticas' en el inventario.", - ], - "npc.speech.villager_under_attack": [ - "Ayuda, ¡Me están atacando!", - "¡Ayuda! ¡Me están atacando!", - "¡Auch! ¡Me están atacando!", - "¡Auch! ¡Me están atacando! ¡Ayuda!", - "¡Ayudenme! ¡Me están atacando!", - "¡Me están atacando! ¡Ayuda!", - "¡Me están atacando! ¡Ayudenme!", - "¡Ayuda!", - "¡Ayuda! ¡Ayuda!", - "¡Ayuda! ¡Ayuda! ¡Ayuda!", - "¡Me están atacando!", - "¡AAAHH! ¡Me están atacando!", - "¡AAAHH! ¡Me están atacando! ¡Ayuda!", - "Ayuda! Nos están atacando!", - "¡Ayuda! ¡Asesino!", - "¡Ayuda! ¡Hay un asesino suelto!", - "¡Ayuda! ¡están intentando matarme!", - "Guardias, ¡Me están atacando!", - "¡Guardias! ¡Me están atacando!", - "¡Me están atacando! ¡Guardias!", - "¡Ayuda! ¡Guardias! ¡Me están atacando!", - "¡Guardias! ¡Vengan rapido!", - "¡Guardias, Guardias!", - "¡Guardias! ¡Me esta atacando un villano!", - "¡Guardias, eliminen a este desagradable villano!", - "¡Guardias! ¡Ahi esta el asesino!", - "¡Guardias! ¡Ayúdenme!", - "No te saldrás con la tuya, ¡Guardias!", - "¡Eres despreciable!", - "¡Ayúdenme!", - "¡Ayuda! ¡Porfavor!", - "¡Auch! ¡Guardias! ¡Ayuda!", - "¡Vienen por mi!", - "¡Ayuda! ¡Ayuda! Estoy siendo atacado", - "Ah, se nota que la violencia es parte del sistema.", - "¡Esto no es más que un rasguño!", - "Deja de hacer eso!", - "¿Qué te hice para merecer esto?", - "Por favor, para de atacarme!", - "Hey! Mira hacia adonde apuntas con esa cosa", - "Desgraciado, vete de aqui!", - "Para ya! Vete!", - "Me estas haciendo enojar!", - "Hey!¿Quién te piensas que eres?", - "Te arrancaré la cabeza por eso!", - "Detente, por favor! No llevo nada de valor!", - "Te voy a mandar a mi hermano, el es más grande que yo!", - "Nooo, le contaré a mi madre!", - "Maldito seas!", - "Por favor no lo hagas.", - "Eso no fue agradable!", - "Tu arma funciona, ahora aléjala!", - "Si claro...", - "Por favor, tengo familia!", - "Soy demasiado jóven para morir!", - "¿Podemos hablar sobre esto?", - "La violencia no resuelve nada!", - "Este día se esta convirtiendo en uno muy feo...", - "Hey, eso dolió!", - "Ayy!", - "Qué violento!", - "Detente, te lo suplico!", - "Ojala te enfermes!", - "Esto no es divertido.", - "¡¿Cómo te atreves?!", - "Vas a pagar por eso!", - "Sigue con eso y lo lamentarás!", - "No hagas que te lastime!", - "Tiene que ser un malentendido!", - "No necesitas hacer esto!", - "Vete, demonio!", - "Eso realmente dolió!", - "¿Por qué harias eso?", - "Por todos los Santos, para!", - "Me habrás confudido con alguien más!", - "No me merezco esto!", - "Por favor, no lo hagas de nuevo", - "Guardias, tiren este monstruo al lago", - "Invocaré mis demonios en ti!", - ], - } -) diff --git a/assets/voxygen/i18n/fr_FR/_manifest.ron b/assets/voxygen/i18n/fr_FR/_manifest.ron index f7c3372ce9..f44b21ee01 100644 --- a/assets/voxygen/i18n/fr_FR/_manifest.ron +++ b/assets/voxygen/i18n/fr_FR/_manifest.ron @@ -26,198 +26,5 @@ asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", scale_ratio: 0.9, ), - }, - string_map: { - - }, - - - vector_map: { - "loading.tips": [ - "Appuyez sur 'G' pour allumer ta lanterne.", - "Appuyez sur 'F1' pour voir les raccourcis clavier par défaut.", - "Vous pouvez taper /say ou /s pour discuter aux joueurs directement à côté toi.", - "Vous pouvez taper /region ou /r pour discuter avec les joueurs situés à quelques centaines de blocs de toi.", - "Pour envoyer un message privé, tapez /tell suivi par un nom de joueur puis votre message.", - "Des PNJs avec le même niveau peuvent varier en difficulté.", - "Regardez le sol pour trouver de la nourriture, des coffres et d'autres butins !", - "Votre inventaire est rempli de nourriture ? Essayez de créer un meilleur repas avec !", - "Vous cherchez une activité ? Essayez de visiter un des donjons marqués sur la carte !", - "N'oubliez pas d'ajuster les graphismes pour votre système. Appuyez sur 'N' pour ouvrir les paramètres.", - "Jouer à plusieurs est amusant ! Appuyez sur 'O' pour voir qui est en ligne.", - "Un PNJ avec une tête de mort sous sa barre de vie est plus puissant que vous.", - "Appuyez sur 'J' pour danser. C'est la fête !", - "Appuyez sur 'L-Shift' pour ouvrir votre deltaplane et conquérir les cieux.", - "Veloren est encore en Pré-Alpha. Nous faisons de notre mieux pour l'améliorer chaque jour !", - "Si vous voulez vous joindre à l'équipe de développement ou juste discuter avec nous, rejoignez notre serveur Discord.", - "Vous pouvez afficher ou non combien de santé vous avez dans les options.", - "Pour voir vos statistiques, cliquez sur le bouton 'Stats' dans l'inventaire", - "Asseyez-vous près d'un feu de camp (avec la touche 'K') pour vous reposer - cela régénèrera votre santé.", - ], - "npc.speech.villager": [ - "N'est-ce pas une journée magnifique ?", - "Comment allez-vous aujourd'hui ?", - "Bien le bonjour à vous !", - "Je me demande ce que pense le Catoblepas quand il mange de l'herbe.", - "Que pensez-vous de ce temps ?", - "Penser à ces donjons m'angoisse un peu. J'espère que quelqu'un ira les nettoyer.", - "J'aimerais aller faire de la spéléologie dans une grotte quand je serai plus fort.", - "Avez-vous vu mon chat ?", - "Avez-vous déjà entendu parler des féroces Requins Terrestres ? J'ai entendu dire qu'ils vivent dans les déserts.", - "On dit que des pierres précieuses de toutes sortes peuvent être trouvées dans les grottes.", - "Je suis juste un mordu du fromage !", - "Vous ne voulez pas entrer ? Nous étions sur le point de manger du fromage !", - "On dit que les champignons sont bons pour la santé. Je n'en ai jamais mangé.", - "N'oubliez pas les biscuits !", - "J'adore tout simplement le fromage des nains. J'aimerais pouvoir en faire par moi-même.", - "Je me demande ce qu'il y a de l'autre côté des montagnes", - "J'espère pouvoir fabriquer mon propre planeur un jour.", - "Vous voulez voir mon jardin ? D'accord, peut-être une autre fois.", - "Belle journée pour une promenade dans les bois !", - "Être ou ne pas être ? Je pense que je serai un fermier.", - "Ne pensez-vous pas que notre village est le meilleur ?", - "D'après vous, qu'est-ce qui fait briller les Restes Lumineux ?", - "Je crois que c'est l'heure du deuxième petit-déjeuner !", - "Avez-vous déjà attrapé une luciole ?", - "Je n'arrive pas à comprendre d'où viennent ces Sauroks.", - "J'aimerais que quelqu'un garde les loups loin du village.", - "J'ai fait un merveilleux rêve sur le fromage la nuit dernière. Qu'est-ce que cela signifie ?", - ], - "npc.speech.villager_decline_trade": [ - "Désolé, je n'ai rien à vous échanger.", - "Un échange ? Comme si que j'avais quoi que ce soit qui vous intéresserait.", - "Ma maison n'est pas à vendre.", - ], - "npc.speech.merchant_advertisement": [ - "Seriez-vous intéressé par un échange ?", - "Voulez-vous faire un échange ?", - "J'ai pu amasser beaucoup d'objets, vous voulez y jeter un oeil ?" - ], - "npc.speech.merchant_busy": [ - "Hey, attendez votre tour.", - "Attendez s'il vous plait, je ne suis pas une pieuvre.", - "Un moment, laissez-moi terminer.", - "On ne dépasse pas.", - "Je suis occupé, revenez plus tard." - ], - "npc.speech.merchant_trade_successful": [ - "Merci d'avoir échangé avec moi", - "Merci!", - "Ce fut un plaisir d'échanger avec vous!" - ], - "npc.speech.merchant_trade_declined": [ - "Peut-être pour un autre jour, bonne journée !", - "Dommage, à la revoyure !" - ], - "npc.speech.villager_cultist_alarm": [ - "Attention ! Il y a un cultiste en liberté !", - "Aux armes ! Les cultistes attaquent !", - "Comment les cultistes osent-ils attaquer notre village ?", - "Mort aux cultistes !", - "Les cultistes ne seront pas tolérés ici !", - "Cultistes meurtriers !", - "Goûte le tranchant de mon épée, espèce de sale cultiste !", - "Rien ne peut nettoyer le sang de tes mains, cultiste !", - "Milles milliards de bernacles bleus foudroyants ! Un cultiste parmi nous !", - "On va mettre un terme aux méfaits de ce cultiste !", - "Ce cultiste est à moi !", - "Prépare-toi à rencontrer ton créateur, sale cultiste !", - "J'aperçois un cultiste ! Attrapez-le !", - "J'aperçois un cultiste ! Attaquez !", - "J'aperçois un cultiste ! Ne le laissez pas s'échapper !", - "Le plus honorable des cultistes aurait-il envie de MOURIR ?!", - "Ne pardonnez jamais ! N'oubliez jamais ! Cultiste, regrette !", - "Meurs, cultiste !", - "Votre règne de terreur va cesser !", - "Voilà, pour tout ce que vous avez fait !", - "On n'aime pas trop les gens comme vous par ici.", - "Vous auriez dû rester sous terre !", - ], - "npc.speech.villager_under_attack": [ - "À l'aide, on m'attaque !", - "À l'aide ! On m'attaque !", - "Aïe ! On m'attaque !", - "Aïe ! On m'attaque ! À l'aide !", - "Aidez-moi! On m'attaque !", - "On m'attaque ! À l'aide !", - "On m'attaque ! Aidez-moi !", - "À l'aide !", - "À l'aide ! À l'aide !", - "À l'aide ! À l'aide ! À l'aide !", - "On m'attaque !", - "AAAHHH ! On m'attaque !", - "AAAHHH ! On m'attaque ! À l'aide !", - "À l'aide ! Nous sommes attaqués !", - "À l'aide ! Assassin !", - "À l'aide ! Il y a un assassin en liberté !", - "À l'aide ! On essaie de me tuer !", - "Gardes, on m'attaque !", - "Gardes ! On m'attaque !", - "On m'attaque ! Gardes !", - "À l'aide ! Gardes ! On m'attaque !", - "Gardes ! Venez vite !", - "Gardes ! Gardes !", - "Gardes ! Un scélérat m'attaque !", - "Gardes, abattez ce scélérat !", - "Gardes ! Il y a un meurtrier !", - "Gardes ! Aidez-moi!", - "Vous ne vous en tirerez pas comme ça! Gardes !", - "Monstre !", - "Aidez-moi!", - "À l'aide ! S'il vous plait !", - "Aïe ! Gardes ! À l'aide !", - "Ils viennent pour moi !", - "À l'aide ! À l'aide ! Je me fais réprimer !", - "Ah, nous voyons maintenant la violence inhérente au système.", - "C'est seulement une égratignure.", - "Arrêtez ça !", - "Qu'est ce que je vous ai fait ?!", - "S'il vous plaît arrêtez de m'attaquer !", - "Hé! Regardez où vous pointez cette chose !", - "Misérable, allez-vous-en !", - "Arrêtez ! Partez ! Arrêtez !", - "Vous m'avez ennervé !", - "Oi ! Qui croyez-vous être ?!", - "J'aurais votre tête pour ça !", - "Arrêtez, s'il vous plaît ! Je ne transporte rien de valeur !", - "Je vais appeler mon frère, il est plus grand que moi !", - "Nooon, Je vais le dire à ma mère !", - "Soyez maudit !", - "Ne faites pas ça.", - "Ce n'était pas très gentil !", - "Ton arme fonctionne, tu peux la ranger maintenant !", - "Épargnez-moi !", - "Pitié, J'ai une famille !", - "Je suis trop jeune pour mourrir !", - "On peut en parler ?", - "La violence n'est jamais la solution !", - "Aujourd'hui est une très mauvaise journée...", - "Hé, ça fait mal !", - "Aïe !", - "Quelle impolitesse !", - "Stop, je vous en prie !", - "Que la peste vous emporte !", - "Ce n'est pas amusant.", - "Comment osez-vous ?!", - "Vous allez payer !", - "Continue et tu vas le regretter !", - "Ne m'obligez pas à vous faire du mal !", - "Il doit y avoir erreur !", - "Vous n'avez pas besoin de faire ça !", - "Fuyez, monstre !", - "Ça fait vraiment mal !", - "Pourquoi faites-vous cela ?", - "Par les esprits, cessez !", - "Vous devez m'avoir confondu avec quelqu'un d'autre !", - "Je ne mérite pas cela !", - "Ne faites plus cela.", - "Gardes, jetez ce monstre dans le lac !", - "Je vais t'envoyer ma tarrasque !", - ], - "npc.speech.villager_enemy_killed": [ - "J'ai détruit mon ennemi !", - "Enfin en paix !", - "... qu'est-ce que je faisais déjà ?", - ], } ) diff --git a/assets/voxygen/i18n/fr_FR/buff.ron b/assets/voxygen/i18n/fr_FR/buff.ron index d764b7968a..86d5d45b15 100644 --- a/assets/voxygen/i18n/fr_FR/buff.ron +++ b/assets/voxygen/i18n/fr_FR/buff.ron @@ -26,7 +26,7 @@ "buff.desc.cursed": "Vous êtes maudit.", // Buffs stats "buff.stat.health": "Restaure {str_total} points de vie", - "buff.stat.increase_max_stamina": "Augmente la vigueur maximale de {strength}", + "buff.stat.increase_max_energy": "Augmente la vigueur maximale de {strength}", "buff.stat.increase_max_health": "Augmente la santé maximale de {strength}", "buff.stat.invulnerability": "Rend invincible", // Text diff --git a/assets/voxygen/i18n/fr_FR/hud/bag.ron b/assets/voxygen/i18n/fr_FR/hud/bag.ron index 7865db0406..020f1ef603 100644 --- a/assets/voxygen/i18n/fr_FR/hud/bag.ron +++ b/assets/voxygen/i18n/fr_FR/hud/bag.ron @@ -26,7 +26,7 @@ "hud.bag.offhand": "Main Secondaire", "hud.bag.bag": "Sac", "hud.bag.health": "Santé", - "hud.bag.stamina": "Vigueur", + "hud.bag.energy": "Vigueur", "hud.bag.combat_rating": "Niveau de Combat", "hud.bag.protection": "Protection", "hud.bag.combat_rating_desc": "Calculé depuis votre\néquipement et votre santé", diff --git a/assets/voxygen/i18n/fr_FR/hud/hud_settings.ron b/assets/voxygen/i18n/fr_FR/hud/settings.ron similarity index 97% rename from assets/voxygen/i18n/fr_FR/hud/hud_settings.ron rename to assets/voxygen/i18n/fr_FR/hud/settings.ron index 73894da599..9c74911606 100644 --- a/assets/voxygen/i18n/fr_FR/hud/hud_settings.ron +++ b/assets/voxygen/i18n/fr_FR/hud/settings.ron @@ -15,7 +15,7 @@ "hud.settings.relative_scaling": "Echelle relative", "hud.settings.custom_scaling": "Echelle personalisée", "hud.settings.crosshair": "Réticule", - "hud.settings.transparency": "Transparence", + "hud.settings.opacity": "Transparence", "hud.settings.hotbar": "Barre d'action", "hud.settings.toggle_shortcuts": "Activer les raccourcis", "hud.settings.buffs_skillbar": "Effets sur la barre de compétences", @@ -33,7 +33,7 @@ "hud.settings.values": "Valeurs", "hud.settings.percentages": "Pourcentages", "hud.settings.chat": "Tchat", - "hud.settings.background_transparency": "Transparence de l'arrière-plan", + "hud.settings.background_opacity": "Transparence de l'arrière-plan", "hud.settings.chat_character_name": "Noms des personnages dans le tchat", "hud.settings.loading_tips": "Astuces sur l'écran de chargement", "hud.settings.reset_interface": "Restaurer par Défaut", diff --git a/assets/voxygen/i18n/fr_FR/skills.ron b/assets/voxygen/i18n/fr_FR/hud/skills.ron similarity index 97% rename from assets/voxygen/i18n/fr_FR/skills.ron rename to assets/voxygen/i18n/fr_FR/hud/skills.ron index 3fa6636704..5008c09588 100644 --- a/assets/voxygen/i18n/fr_FR/skills.ron +++ b/assets/voxygen/i18n/fr_FR/hud/skills.ron @@ -11,8 +11,8 @@ // General "hud.skill.inc_health_title": "Augmentation de Santé", "hud.skill.inc_health": "Augmente les points de vie max de {boost}{SP}", - "hud.skill.inc_stam_title": "Augmentation d'Endurance", - "hud.skill.inc_stam": "Augmente les points d'endurance max de {boost}{SP}", + "hud.skill.inc_energy_title": "Augmentation d'Endurance", + "hud.skill.inc_energy": "Augmente les points d'endurance max de {boost}{SP}", "hud.skill.unlck_sword_title": "Maniement de l'Épée", "hud.skill.unlck_sword": "Débloque l'arbre de compétence pour l'épée{SP}", "hud.skill.unlck_axe_title": "Maniement de la Hache", @@ -27,8 +27,8 @@ "hud.skill.unlck_sceptre": "Débloque l'arbre de compétence pour le sceptre{SP}", "hud.skill.dodge_title": "Roulade", "hud.skill.dodge": "Esquivez les attaques de mélée en faisant des roulades{SP}", - "hud.skill.roll_stamina_title": "Coût d'Endurance de la Roulade", - "hud.skill.roll_stamina": "Rouler coûte {boost}% moins d'endurance{SP}", + "hud.skill.roll_energy_title": "Coût d'Endurance de la Roulade", + "hud.skill.roll_energy": "Rouler coûte {boost}% moins d'endurance{SP}", "hud.skill.roll_speed_title": "Vitesse de la Roulade", "hud.skill.roll_speed": "Rouler {boost}% plus vite{SP}", "hud.skill.roll_dur_title": "Durée de la Roulade", @@ -95,8 +95,8 @@ "hud.skill.st_flamethrower_damage" : "Augmente de {boost}% les dégâts{SP}", "hud.skill.st_explosion_radius_title" : "Portée de l'Explosion", "hud.skill.st_explosion_radius" : "Augmente la portée d'explosion de {boost}%{SP}", - "hud.skill.st_stamina_regen_title" : "Régénération d'Endurance des Boules de feu", - "hud.skill.st_stamina_regen" : "Augmente de {boost}% la régénération d'endurance{SP}", + "hud.skill.st_energy_regen_title" : "Régénération d'Endurance des Boules de feu", + "hud.skill.st_energy_regen" : "Augmente de {boost}% la régénération d'endurance{SP}", "hud.skill.st_fireball_title" : "Boule de Feu", "hud.skill.st_fireball" : "Joue à la balle avec les ennemis", "hud.skill.st_damage_title" : "Dégâts des boules de feu", diff --git a/assets/voxygen/i18n/fr_FR/main.ron b/assets/voxygen/i18n/fr_FR/main.ron index 690870a7e3..e5d969e2fb 100644 --- a/assets/voxygen/i18n/fr_FR/main.ron +++ b/assets/voxygen/i18n/fr_FR/main.ron @@ -60,5 +60,26 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "Appuyez sur 'G' pour allumer ta lanterne.", + "Appuyez sur 'F1' pour voir les raccourcis clavier par défaut.", + "Vous pouvez taper /say ou /s pour discuter aux joueurs directement à côté toi.", + "Vous pouvez taper /region ou /r pour discuter avec les joueurs situés à quelques centaines de blocs de toi.", + "Pour envoyer un message privé, tapez /tell suivi par un nom de joueur puis votre message.", + "Des PNJs avec le même niveau peuvent varier en difficulté.", + "Regardez le sol pour trouver de la nourriture, des coffres et d'autres butins !", + "Votre inventaire est rempli de nourriture ? Essayez de créer un meilleur repas avec !", + "Vous cherchez une activité ? Essayez de visiter un des donjons marqués sur la carte !", + "N'oubliez pas d'ajuster les graphismes pour votre système. Appuyez sur 'N' pour ouvrir les paramètres.", + "Jouer à plusieurs est amusant ! Appuyez sur 'O' pour voir qui est en ligne.", + "Un PNJ avec une tête de mort sous sa barre de vie est plus puissant que vous.", + "Appuyez sur 'J' pour danser. C'est la fête !", + "Appuyez sur 'L-Shift' pour ouvrir votre deltaplane et conquérir les cieux.", + "Veloren est encore en Pré-Alpha. Nous faisons de notre mieux pour l'améliorer chaque jour !", + "Si vous voulez vous joindre à l'équipe de développement ou juste discuter avec nous, rejoignez notre serveur Discord.", + "Vous pouvez afficher ou non combien de santé vous avez dans les options.", + "Pour voir vos statistiques, cliquez sur le bouton 'Stats' dans l'inventaire", + "Asseyez-vous près d'un feu de camp (avec la touche 'K') pour vous reposer - cela régénèrera votre santé.", + ], } ) diff --git a/assets/voxygen/i18n/fr_FR/npc.ron b/assets/voxygen/i18n/fr_FR/npc.ron new file mode 100644 index 0000000000..ec75dfa123 --- /dev/null +++ b/assets/voxygen/i18n/fr_FR/npc.ron @@ -0,0 +1,175 @@ +/// Localization for French (France locale) +( + string_map: { + + }, + + + vector_map: { + "npc.speech.villager": [ + "N'est-ce pas une journée magnifique ?", + "Comment allez-vous aujourd'hui ?", + "Bien le bonjour à vous !", + "Je me demande ce que pense le Catoblepas quand il mange de l'herbe.", + "Que pensez-vous de ce temps ?", + "Penser à ces donjons m'angoisse un peu. J'espère que quelqu'un ira les nettoyer.", + "J'aimerais aller faire de la spéléologie dans une grotte quand je serai plus fort.", + "Avez-vous vu mon chat ?", + "Avez-vous déjà entendu parler des féroces Requins Terrestres ? J'ai entendu dire qu'ils vivent dans les déserts.", + "On dit que des pierres précieuses de toutes sortes peuvent être trouvées dans les grottes.", + "Je suis juste un mordu du fromage !", + "Vous ne voulez pas entrer ? Nous étions sur le point de manger du fromage !", + "On dit que les champignons sont bons pour la santé. Je n'en ai jamais mangé.", + "N'oubliez pas les biscuits !", + "J'adore tout simplement le fromage des nains. J'aimerais pouvoir en faire par moi-même.", + "Je me demande ce qu'il y a de l'autre côté des montagnes", + "J'espère pouvoir fabriquer mon propre planeur un jour.", + "Vous voulez voir mon jardin ? D'accord, peut-être une autre fois.", + "Belle journée pour une promenade dans les bois !", + "Être ou ne pas être ? Je pense que je serai un fermier.", + "Ne pensez-vous pas que notre village est le meilleur ?", + "D'après vous, qu'est-ce qui fait briller les Restes Lumineux ?", + "Je crois que c'est l'heure du deuxième petit-déjeuner !", + "Avez-vous déjà attrapé une luciole ?", + "Je n'arrive pas à comprendre d'où viennent ces Sauroks.", + "J'aimerais que quelqu'un garde les loups loin du village.", + "J'ai fait un merveilleux rêve sur le fromage la nuit dernière. Qu'est-ce que cela signifie ?", + ], + "npc.speech.villager_decline_trade": [ + "Désolé, je n'ai rien à vous échanger.", + "Un échange ? Comme si que j'avais quoi que ce soit qui vous intéresserait.", + "Ma maison n'est pas à vendre.", + ], + "npc.speech.merchant_advertisement": [ + "Seriez-vous intéressé par un échange ?", + "Voulez-vous faire un échange ?", + "J'ai pu amasser beaucoup d'objets, vous voulez y jeter un oeil ?" + ], + "npc.speech.merchant_busy": [ + "Hey, attendez votre tour.", + "Attendez s'il vous plait, je ne suis pas une pieuvre.", + "Un moment, laissez-moi terminer.", + "On ne dépasse pas.", + "Je suis occupé, revenez plus tard." + ], + "npc.speech.merchant_trade_successful": [ + "Merci d'avoir échangé avec moi", + "Merci!", + "Ce fut un plaisir d'échanger avec vous!" + ], + "npc.speech.merchant_trade_declined": [ + "Peut-être pour un autre jour, bonne journée !", + "Dommage, à la revoyure !" + ], + "npc.speech.villager_cultist_alarm": [ + "Attention ! Il y a un cultiste en liberté !", + "Aux armes ! Les cultistes attaquent !", + "Comment les cultistes osent-ils attaquer notre village ?", + "Mort aux cultistes !", + "Les cultistes ne seront pas tolérés ici !", + "Cultistes meurtriers !", + "Goûte le tranchant de mon épée, espèce de sale cultiste !", + "Rien ne peut nettoyer le sang de tes mains, cultiste !", + "Milles milliards de bernacles bleus foudroyants ! Un cultiste parmi nous !", + "On va mettre un terme aux méfaits de ce cultiste !", + "Ce cultiste est à moi !", + "Prépare-toi à rencontrer ton créateur, sale cultiste !", + "J'aperçois un cultiste ! Attrapez-le !", + "J'aperçois un cultiste ! Attaquez !", + "J'aperçois un cultiste ! Ne le laissez pas s'échapper !", + "Le plus honorable des cultistes aurait-il envie de MOURIR ?!", + "Ne pardonnez jamais ! N'oubliez jamais ! Cultiste, regrette !", + "Meurs, cultiste !", + "Votre règne de terreur va cesser !", + "Voilà, pour tout ce que vous avez fait !", + "On n'aime pas trop les gens comme vous par ici.", + "Vous auriez dû rester sous terre !", + ], + "npc.speech.villager_under_attack": [ + "À l'aide, on m'attaque !", + "À l'aide ! On m'attaque !", + "Aïe ! On m'attaque !", + "Aïe ! On m'attaque ! À l'aide !", + "Aidez-moi! On m'attaque !", + "On m'attaque ! À l'aide !", + "On m'attaque ! Aidez-moi !", + "À l'aide !", + "À l'aide ! À l'aide !", + "À l'aide ! À l'aide ! À l'aide !", + "On m'attaque !", + "AAAHHH ! On m'attaque !", + "AAAHHH ! On m'attaque ! À l'aide !", + "À l'aide ! Nous sommes attaqués !", + "À l'aide ! Assassin !", + "À l'aide ! Il y a un assassin en liberté !", + "À l'aide ! On essaie de me tuer !", + "Gardes, on m'attaque !", + "Gardes ! On m'attaque !", + "On m'attaque ! Gardes !", + "À l'aide ! Gardes ! On m'attaque !", + "Gardes ! Venez vite !", + "Gardes ! Gardes !", + "Gardes ! Un scélérat m'attaque !", + "Gardes, abattez ce scélérat !", + "Gardes ! Il y a un meurtrier !", + "Gardes ! Aidez-moi!", + "Vous ne vous en tirerez pas comme ça! Gardes !", + "Monstre !", + "Aidez-moi!", + "À l'aide ! S'il vous plait !", + "Aïe ! Gardes ! À l'aide !", + "Ils viennent pour moi !", + "À l'aide ! À l'aide ! Je me fais réprimer !", + "Ah, nous voyons maintenant la violence inhérente au système.", + "C'est seulement une égratignure.", + "Arrêtez ça !", + "Qu'est ce que je vous ai fait ?!", + "S'il vous plaît arrêtez de m'attaquer !", + "Hé! Regardez où vous pointez cette chose !", + "Misérable, allez-vous-en !", + "Arrêtez ! Partez ! Arrêtez !", + "Vous m'avez ennervé !", + "Oi ! Qui croyez-vous être ?!", + "J'aurais votre tête pour ça !", + "Arrêtez, s'il vous plaît ! Je ne transporte rien de valeur !", + "Je vais appeler mon frère, il est plus grand que moi !", + "Nooon, Je vais le dire à ma mère !", + "Soyez maudit !", + "Ne faites pas ça.", + "Ce n'était pas très gentil !", + "Ton arme fonctionne, tu peux la ranger maintenant !", + "Épargnez-moi !", + "Pitié, J'ai une famille !", + "Je suis trop jeune pour mourrir !", + "On peut en parler ?", + "La violence n'est jamais la solution !", + "Aujourd'hui est une très mauvaise journée...", + "Hé, ça fait mal !", + "Aïe !", + "Quelle impolitesse !", + "Stop, je vous en prie !", + "Que la peste vous emporte !", + "Ce n'est pas amusant.", + "Comment osez-vous ?!", + "Vous allez payer !", + "Continue et tu vas le regretter !", + "Ne m'obligez pas à vous faire du mal !", + "Il doit y avoir erreur !", + "Vous n'avez pas besoin de faire ça !", + "Fuyez, monstre !", + "Ça fait vraiment mal !", + "Pourquoi faites-vous cela ?", + "Par les esprits, cessez !", + "Vous devez m'avoir confondu avec quelqu'un d'autre !", + "Je ne mérite pas cela !", + "Ne faites plus cela.", + "Gardes, jetez ce monstre dans le lac !", + "Je vais t'envoyer ma tarrasque !", + ], + "npc.speech.villager_enemy_killed": [ + "J'ai détruit mon ennemi !", + "Enfin en paix !", + "... qu'est-ce que je faisais déjà ?", + ], + } +) diff --git a/assets/voxygen/i18n/hu_HU/_manifest.ron b/assets/voxygen/i18n/hu_HU/_manifest.ron index 6d816329b8..7427a781ee 100644 --- a/assets/voxygen/i18n/hu_HU/_manifest.ron +++ b/assets/voxygen/i18n/hu_HU/_manifest.ron @@ -28,206 +28,5 @@ asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", scale_ratio: 1.0, ), - }, - - string_map: { - - }, - - vector_map: { - "loading.tips": [ - "Lámpásod meggyújtásához nyomd meg a 'G' gombot!", - "Az alapértelmezett billentyű-összerendelések megtekintéséhez nyomd meg az 'F1' gombot!", - "Ha beírod a csevegésbe a /say vagy a /s parancsot, lehetőséged nyílik arra, hogy csak a közvetlen közeledben lévő játékosokkal beszélgess.", - "Ha beírod a csevegésbe a /region vagy a /r parancsot, lehetőséged nyílik arra, hogy csak a tőled max pár száz blokkra lévő játékosokkal beszélgess.", - "Az adminisztrátorok használhatják a /build parancsot – ezzel léphetnek be az építő módba.", - "Ha beírod a csevegésbe a /group vagy a /g parancsot, lehetőséged nyílik arra, hogy csak a csoportodban lévő játékosokkal beszélgess.", - "Privát üzenetek küldéséhez írd be a csevegésbe a /tell parancsot, majd a játékos nevét, végül pedig az üzenetet.", - "Érdemes nyitott szemmel járni, hiszen rengeteg étel, láda és egyéb zsákmány található szerte a világban!", - "Túl sok az étel a holmijaid között? Próbálj meg jobb ételt kotyvasztani belőlük!", - "Azon tűnődsz, hogy mit kellene csinálnod? Tedd magad próbára a térképen megjelölt kazamaták valamelyikében!", - "Ne felejtsd el a rendszeredhez igazítani a grafikát! A beállítások megtekintéséhez nyomd meg az 'N' gombot!", - "Másokkal játszani bizony jó muri! Hogy lásd, épp ki van fönn a hálózaton nyomd meg az 'O' gombot!", - "A táncoláshoz nyomd meg a 'J' gombot. Bulira föl!", - "Nyomd meg a 'Bal Ctrl'-t és hódítsd meg sárkányrepülőddel az eget!", - "A Veloren még mindig előzetes alfában van. Igyekszünk mindent megtenni annak érdekében, hogy napról-napra jobb és jobb legyen!", - "Szeretnél a feljesztői csapat tagja lenni, vagy netán csak csevegnél velünk? Csatlakozz a Discord szerverünkhöz!", - "A beállításokban azt is eldöntheted, hogy megjelenjen-e az életerőd mennyisége az életerő sávban, vagy sem.", - "Ha lehuppansz (a 'K' gombbal) egy tábortűz mellé szép lassan be fognak gyógyulni a sebeid.", - "Több holmitartóra vagy jobb páncélzatra van szükséged? Nyomd meg a 'C' gombot, és nézz szét a tárgykészítési menüben!", - "Próbálj meg ugrani egyet, miközben keresztülbukfencezel másokon!", - ], - "npc.speech.villager": [ - "Csodás napunk van, nemde?", - "Hogy vagy?", - "Áldott jó reggelt kívánok!", - "Vajon mire gondol a Catoblepas, miközben legelészik?", - "Mit szólsz ehhez az időjáráshoz?", - "Akárhányszor azokra a kazamatákre gondolok elfog a félelem. Remélem valaki majd megtisztítja őket...", - "Majd szeretnék elmenni barlangászni, amikor már erősebb leszek.", - "Nem láttad a macskámat?", - "Hallottál már a veszedelmes földi cápákról? Úgy tudom a sivatagokban élnek.", - "Azt mondják a barlangok tele vannak mindenféle fényes drágakővel.", - "Megőrülök a sajtért!", - "Nem nézel be hozzánk? Épp most terveztünk nekiállni sajtozni!", - "Azt mondják a gomba jót tesz az egészségednek. Én magam nem szoktam enni.", - "A kekszeket ne feledd!", - "Egyszerűen rajongok a törpe sajtért. Bárcsak én is el tudnám készíteni...", - "Mi lehet vajon a hegyen túl?", - "Remélem egyszer majd elkészíthetem a saját sárkányrepülőmet.", - "Szeretnéd látni a kertem? Hát jó, talán majd máskor.", - "Kitűnő nap ez a mai egy jó erdei sétához!", - "Lenni, vagy nem lenni? Én azt hiszem farmer leszek.", - "Hát nem a mi városkánk a legjobb?", - "Szerinted mitől ragyognak a ragyogó maradványok?", - "Azt hiszem eljött a második reggeli ideje!", - "Fogtál már szentjánosbogarat?", - "Egyszerűen képtelen vagyok rájönni, hogy ugyan honnan jönnek ezek a Saurokok...", - "Bárcsak távol tartaná valaki a farkasokat a városkától...", - "Csodás, sajtos álomban volt tegnap este részem. Mit jelenthet ez vajon?", - "Hagytam egy kis sajtot az öcsémnél. Nem tudom, hogy megvan-e még. Úgy hívom, hogy Schrödinger sajtja.", - "Hagytam egy kis sajtot a húgomnál. Nem tudom, hogy megvan-e még. Úgy hívom, hogy Schrödinger sajtja.", - "Valaki igazán kezdhetne már valamit ezekkel a kultistákkal! Valaki rajtam kívül...", - "Remélem jön némi eső hamarosan, jót tenne a terménynek.", - "Imádom a mézet... és utálom a méheket.", - "Egy napon majd szeretnék világot látni. Csak tartogat nekem valamit az élet ezen a városkán kívül is.", - ], - "npc.speech.villager_decline_trade": [ - "Sajnálom, én nem tudok mit áruba bocsájtani.", - "Üzletelni? Velem? Mintha lenne bármim, ami fel tudná kelteni az érdeklődésedet...", - "A ház az enyém! Nem fogom elcserélni semmiért!", - ], - "npc.speech.merchant_advertisement": [ - "Ajánlhatok személyemben egy kereskedőpartnert?", - "Szeretnél velem üzletelni?", - "Rengeteg árum van, szeretnél vetni rájuk egy pillantást?" - ], - "npc.speech.merchant_busy": [ - "Hé, várd ki a sorod!", - "Várj egy kicsit légyszíves, belőlem is csak egy van.", - "Látod ezt a másik embert előtted?", - "Egy pillanat, hadd fejezzem be!", - "Ne vágj be mások elé!", - "Most épp nem érek rá, gyere vissza később!" - ], - "npc.speech.merchant_trade_successful": [ - "Köszönöm, hogy velem üzleteltél!", - "Hálás köszönet!", - ], - "npc.speech.merchant_trade_declined": [ - "Talán majd máskor. Legyen szép napod!", - "Kár, majd talán legközelebb!" - ], - "npc.speech.villager_cultist_alarm": [ - "Vigyázat! Elszabadult egy kultista!", - "Fegyverbe! Támadnak a kultisták!", - "Hogy merik a kultisták megtámadni a mi városkánkat?!", - "Halál a kultistákra!", - "Itt nem tűrünk meg egy kultistát sem!", - "Te véres kultista!", - "Ízleld meg a kardom élét, mocskos kultista!", - "Semmi nem moshatja le a vért a kezedről, kultista!", - "Millió mérgező mályvaszín medúza! Egy kultista van köztünk!", - "A kultisták gonoszkodásainak hamarosan vége!", - "Ez a kultista az enyém!", - "Ideje, hogy találkozz a teremtőddel, aljas kultista!", - "Itt egy kultista! Kapjuk el!", - "Itt egy kultista! Támadás!", - "Itt egy kultista! Ne hagyjuk megszökni!", - "Nem óhajt kultista őméltósága egy kis HALÁLT?!", - "Nincs bocsánat! Nincs feledés! Kultisták, bűnhődjetek!", - "Pusztulj, kultista!", - "Egyszer véget ér a rémuralmatok!", - "Ezt azt eddigi tetteitekért!", - "Nem látjuk szívesen a fajtátokat errefelé!", - "Maradtatok volna a föld alatt!", - ], - "npc.speech.villager_under_attack": [ - "Segítség, Megtámadtak!", - "Segítség, Megtámadtak!", - "Aúú! Megtámadtak!!", - "Aúú! Megtámadtak! Segítség!", - "Segítsetek! Megtámadtak!", - "Megtámadtak! Segítség!", - "Megtámadtak! Segítsetek!", - "Segítség!", - "Segítség! Segítség!", - "Segítség! Segítség! Segítség!", - "Megtámadtak!", - "ÁÁÁÁÁÁ! Megtámadtak!", - "ÁÁÁÁÁÁ! Megtámadtak! Segítség!", - "Segítség! Megtámadtak minket!", - "Segítség! Gyilkos!", - "Segítség! Elszabadult egy gyilkos!", - "Segítség! Meg akarnak ölni!", - "Őrség, Megtámadtak!", - "Őrség! Megtámadtak!", - "Megtámadtak! Őrség!", - "Segítség! Őrség! Megtámadtak!", - "Őrség! Gyertek gyorsan!", - "Őrség! Őrség!", - "Őrség! Egy gazember megtámadott!", - "Őrség, aprítsátok fel ezt az alávaló gazembert!", - "Őrség! Itt egy gyilkos!", - "Őrség! Segítsetek!", - "Ezt nem úszod meg szárazon! Őrség!", - "Te senkiházi!", - "Valaki segítsen!!", - "Segítsetek! Kérlek!", - "Aúú! Őrség! Segítség!", - "Engem akarnak!", - "Segítség! Segítség! Valaki le akar gyűrni!", - "Áá, most láthatjuk a rendszerrel járó erőszakot.", - "Ez csak egy karcolás!", - "Hagyd abba!", - "Ártottam én neked valaha?!", - "Kérlek ne bántalmazz tovább!", - "Hé! Vigyázz, merre irányzod azt a dolgot!", - "Takarodj innen, te mocskos gazember!", - "Hagyd abba! Menj innen!", - "Sikerült igencsak felbőszítened!", - "Hé! Kinek képzeled magad?!", - "Ezért a fejeddel fogsz fizetni!", - "Hagyd abba, kérlek! Nincs nálam semmi értékes!", - "Rád uszítom a bátyámat, ő nagyobb, mint én!", - "Neee, megmondalak anyának!", - "Légy átkozott!", - "Kérlek ne tedd ezt!", - "Ez nem volt szép tőled!", - "Hatásos a fegyvered, most már igazán elrakhatnád!", - "Könyörülj rajtam!", - "Kérlek, családom van!", - "Még túl fiatal vagyok a halálhoz!", - "Nem tudnánk ezt megbeszélni?", - "Az erőszak nem megoldás!", - "Úgy látszik igencsak pocsék nap ez a mai...", - "Hé, ez fáj!", - "Ííí!", - "Milyen durva vagy!", - "Könyörögve kérlek, hagyd abba!", - "A fene vigyen el!", - "Ez nem vicces!", - "Hogy merészeled?!", - "Ezért megfizetsz!", - "Nagyon meg fogod bánni, ha ezt tovább folytatod!", - "Ne akard, hogy bántsalak!", - "Itt valami félreértés lesz!", - "Erre nincs semmi szükség!", - "Takarodj, te pokolfajzat!", - "Ez nagyon fáj!", - "Miért tennél ilyet?!", - "A szellemekre, hagyd abba!", - "Biztos keversz engem valakivel!", - "Én nem ezt érdemlem!", - "Ezt kérlek ne!", - "Őrség, dobjátok ezt a szörnyeteget a tóba!", - "Rád uszítom a taraszkomat!", - "Mért éééééén?", - ], - "npc.speech.villager_enemy_killed": [ - "Eltiportam az ellenségemet!", - "Végre ismét békességben!", - "...no, hol is tartottam?", - ] } ) diff --git a/assets/voxygen/i18n/hu_HU/buff.ron b/assets/voxygen/i18n/hu_HU/buff.ron index 7b1633b8d2..5a0f8eee85 100644 --- a/assets/voxygen/i18n/hu_HU/buff.ron +++ b/assets/voxygen/i18n/hu_HU/buff.ron @@ -38,7 +38,7 @@ "buff.desc.ensnared": "Indák kapaszkodnak a lábadba, így szinte lehetetlenné vált számodra a mozgás.", // Buffs stats "buff.stat.health": "{str_total} életerőt tölt vissza", - "buff.stat.increase_max_stamina": "Megnöveli a maximális energiát ennyivel: {strength}", + "buff.stat.increase_max_energy": "Megnöveli a maximális energiát ennyivel: {strength}", "buff.stat.increase_max_health": "Megnöveli a maximális életerőt ennyivel: {strength}", "buff.stat.invulnerability": "Sebezhetetlenné tesz", // Text diff --git a/assets/voxygen/i18n/hu_HU/hud/bag.ron b/assets/voxygen/i18n/hu_HU/hud/bag.ron index fec5dfa1f6..ca11e5a763 100644 --- a/assets/voxygen/i18n/hu_HU/hud/bag.ron +++ b/assets/voxygen/i18n/hu_HU/hud/bag.ron @@ -30,7 +30,7 @@ "hud.bag.swap_equipped_weapons_desc": "Nyomd meg a(z) '{key}' gombot", "hud.bag.bag": "Holmitartó", "hud.bag.health": "Életerő", - "hud.bag.stamina": "Energia", + "hud.bag.energy": "Energia", "hud.bag.combat_rating": "Küzdelmi érték", "hud.bag.protection": "Védelem", "hud.bag.stun_res": "Megszédíthetőség", diff --git a/assets/voxygen/i18n/hu_HU/hud/hud_settings.ron b/assets/voxygen/i18n/hu_HU/hud/settings.ron similarity index 98% rename from assets/voxygen/i18n/hu_HU/hud/hud_settings.ron rename to assets/voxygen/i18n/hu_HU/hud/settings.ron index 03d955849e..bde7bc0ecc 100644 --- a/assets/voxygen/i18n/hu_HU/hud/hud_settings.ron +++ b/assets/voxygen/i18n/hu_HU/hud/settings.ron @@ -17,7 +17,7 @@ "hud.settings.relative_scaling": "Relatív méretezés", "hud.settings.custom_scaling": "Egyéni méretezés", "hud.settings.crosshair": "Célkereszt", - "hud.settings.transparency": "Átlátszóság", + "hud.settings.opacity": "Átlátszóság", "hud.settings.hotbar": "Gyorssáv", "hud.settings.toggle_shortcuts": "Gyorsbillentyűk megjelenítése/elrejtése", "hud.settings.buffs_skillbar": "Hatások a gyossávnál", @@ -35,7 +35,7 @@ "hud.settings.values": "Értékek", "hud.settings.percentages": "Százalékok", "hud.settings.chat": "Csevegés", - "hud.settings.background_transparency": "Háttér átlátszósága", + "hud.settings.background_opacity": "Háttér átlátszósága", "hud.settings.chat_character_name": "Karakternevek a csevegésben", "hud.settings.loading_tips": "Tippek a töltőképernyőn", "hud.settings.reset_interface": "Alapértékek visszaállítása", diff --git a/assets/voxygen/i18n/hu_HU/skills.ron b/assets/voxygen/i18n/hu_HU/hud/skills.ron similarity index 97% rename from assets/voxygen/i18n/hu_HU/skills.ron rename to assets/voxygen/i18n/hu_HU/hud/skills.ron index e3926b27c8..4cee0264a8 100644 --- a/assets/voxygen/i18n/hu_HU/skills.ron +++ b/assets/voxygen/i18n/hu_HU/hud/skills.ron @@ -11,8 +11,8 @@ // General "hud.skill.inc_health_title": "Életerő növelése", "hud.skill.inc_health": "Maximális életerő növelése {boost} ponttal{SP}", - "hud.skill.inc_stam_title": "Energia növelése", - "hud.skill.inc_stam": "Maximális energia növelése {boost} ponttal{SP}", + "hud.skill.inc_energy_title": "Energia növelése", + "hud.skill.inc_energy": "Maximális energia növelése {boost} ponttal{SP}", "hud.skill.unlck_sword_title": "Kard feloldása", "hud.skill.unlck_sword": "Kard képességfájának feloldása{SP}", "hud.skill.unlck_axe_title": "Fejsze feloldása", @@ -27,8 +27,8 @@ "hud.skill.unlck_sceptre": "Varázsjogar képességfájának feloldása{SP}", "hud.skill.dodge_title": "Kitérés", "hud.skill.dodge": "A kitérőbukfencet görgőkattintással lehet aktiválni. A gurulás ideje alatt nem lehet megsebezni téged közelharci támadással.", - "hud.skill.roll_stamina_title": "Bukfencezés energiaigénye", - "hud.skill.roll_stamina": "A bukfencezés {boost}%-kal kevesebb energiát használ{SP}", + "hud.skill.roll_energy_title": "Bukfencezés energiaigénye", + "hud.skill.roll_energy": "A bukfencezés {boost}%-kal kevesebb energiát használ{SP}", "hud.skill.roll_speed_title": "Bukfencezés sebessége", "hud.skill.roll_speed": "{boost}%-kal gyorsabb bukfencezés{SP}", "hud.skill.roll_dur_title": "Bukfencezés időtartama", @@ -95,8 +95,8 @@ "hud.skill.st_flamethrower_damage" : "Megnöveli a sebzést {boost}%-kal{SP}", "hud.skill.st_explosion_radius_title" : "Robbanás sugara", "hud.skill.st_explosion_radius" : "Nagyobb a jobb, megnöveli a robbanás sugarát {boost}%-kal{SP}", - "hud.skill.st_stamina_regen_title" : "Energia visszatöltődés", - "hud.skill.st_stamina_regen" : "Megnöveli az energia visszatöltődést {boost}%-kal{SP}", + "hud.skill.st_energy_regen_title" : "Energia visszatöltődés", + "hud.skill.st_energy_regen" : "Megnöveli az energia visszatöltődést {boost}%-kal{SP}", "hud.skill.st_fireball_title" : "Tűzgolyó", "hud.skill.st_fireball" : "Egy tűzgolyót lő ki, amely becsapódáskor felrobban", "hud.skill.st_damage_title" : "Sebzés", diff --git a/assets/voxygen/i18n/hu_HU/main.ron b/assets/voxygen/i18n/hu_HU/main.ron index 9275a0a5f8..017b4c8d9a 100644 --- a/assets/voxygen/i18n/hu_HU/main.ron +++ b/assets/voxygen/i18n/hu_HU/main.ron @@ -67,5 +67,27 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "Lámpásod meggyújtásához nyomd meg a 'G' gombot!", + "Az alapértelmezett billentyű-összerendelések megtekintéséhez nyomd meg az 'F1' gombot!", + "Ha beírod a csevegésbe a /say vagy a /s parancsot, lehetőséged nyílik arra, hogy csak a közvetlen közeledben lévő játékosokkal beszélgess.", + "Ha beírod a csevegésbe a /region vagy a /r parancsot, lehetőséged nyílik arra, hogy csak a tőled max pár száz blokkra lévő játékosokkal beszélgess.", + "Az adminisztrátorok használhatják a /build parancsot – ezzel léphetnek be az építő módba.", + "Ha beírod a csevegésbe a /group vagy a /g parancsot, lehetőséged nyílik arra, hogy csak a csoportodban lévő játékosokkal beszélgess.", + "Privát üzenetek küldéséhez írd be a csevegésbe a /tell parancsot, majd a játékos nevét, végül pedig az üzenetet.", + "Érdemes nyitott szemmel járni, hiszen rengeteg étel, láda és egyéb zsákmány található szerte a világban!", + "Túl sok az étel a holmijaid között? Próbálj meg jobb ételt kotyvasztani belőlük!", + "Azon tűnődsz, hogy mit kellene csinálnod? Tedd magad próbára a térképen megjelölt kazamaták valamelyikében!", + "Ne felejtsd el a rendszeredhez igazítani a grafikát! A beállítások megtekintéséhez nyomd meg az 'N' gombot!", + "Másokkal játszani bizony jó muri! Hogy lásd, épp ki van fönn a hálózaton nyomd meg az 'O' gombot!", + "A táncoláshoz nyomd meg a 'J' gombot. Bulira föl!", + "Nyomd meg a 'Bal Ctrl'-t és hódítsd meg sárkányrepülőddel az eget!", + "A Veloren még mindig előzetes alfában van. Igyekszünk mindent megtenni annak érdekében, hogy napról-napra jobb és jobb legyen!", + "Szeretnél a feljesztői csapat tagja lenni, vagy netán csak csevegnél velünk? Csatlakozz a Discord szerverünkhöz!", + "A beállításokban azt is eldöntheted, hogy megjelenjen-e az életerőd mennyisége az életerő sávban, vagy sem.", + "Ha lehuppansz (a 'K' gombbal) egy tábortűz mellé szép lassan be fognak gyógyulni a sebeid.", + "Több holmitartóra vagy jobb páncélzatra van szükséged? Nyomd meg a 'C' gombot, és nézz szét a tárgykészítési menüben!", + "Próbálj meg ugrani egyet, miközben keresztülbukfencezel másokon!", + ], } ) diff --git a/assets/voxygen/i18n/hu_HU/npc.ron b/assets/voxygen/i18n/hu_HU/npc.ron new file mode 100644 index 0000000000..0a487b19dc --- /dev/null +++ b/assets/voxygen/i18n/hu_HU/npc.ron @@ -0,0 +1,183 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Hungarian +( + string_map: { + + }, + + vector_map: { + "npc.speech.villager": [ + "Csodás napunk van, nemde?", + "Hogy vagy?", + "Áldott jó reggelt kívánok!", + "Vajon mire gondol a Catoblepas, miközben legelészik?", + "Mit szólsz ehhez az időjáráshoz?", + "Akárhányszor azokra a kazamatákre gondolok elfog a félelem. Remélem valaki majd megtisztítja őket...", + "Majd szeretnék elmenni barlangászni, amikor már erősebb leszek.", + "Nem láttad a macskámat?", + "Hallottál már a veszedelmes földi cápákról? Úgy tudom a sivatagokban élnek.", + "Azt mondják a barlangok tele vannak mindenféle fényes drágakővel.", + "Megőrülök a sajtért!", + "Nem nézel be hozzánk? Épp most terveztünk nekiállni sajtozni!", + "Azt mondják a gomba jót tesz az egészségednek. Én magam nem szoktam enni.", + "A kekszeket ne feledd!", + "Egyszerűen rajongok a törpe sajtért. Bárcsak én is el tudnám készíteni...", + "Mi lehet vajon a hegyen túl?", + "Remélem egyszer majd elkészíthetem a saját sárkányrepülőmet.", + "Szeretnéd látni a kertem? Hát jó, talán majd máskor.", + "Kitűnő nap ez a mai egy jó erdei sétához!", + "Lenni, vagy nem lenni? Én azt hiszem farmer leszek.", + "Hát nem a mi városkánk a legjobb?", + "Szerinted mitől ragyognak a ragyogó maradványok?", + "Azt hiszem eljött a második reggeli ideje!", + "Fogtál már szentjánosbogarat?", + "Egyszerűen képtelen vagyok rájönni, hogy ugyan honnan jönnek ezek a Saurokok...", + "Bárcsak távol tartaná valaki a farkasokat a városkától...", + "Csodás, sajtos álomban volt tegnap este részem. Mit jelenthet ez vajon?", + "Hagytam egy kis sajtot az öcsémnél. Nem tudom, hogy megvan-e még. Úgy hívom, hogy Schrödinger sajtja.", + "Hagytam egy kis sajtot a húgomnál. Nem tudom, hogy megvan-e még. Úgy hívom, hogy Schrödinger sajtja.", + "Valaki igazán kezdhetne már valamit ezekkel a kultistákkal! Valaki rajtam kívül...", + "Remélem jön némi eső hamarosan, jót tenne a terménynek.", + "Imádom a mézet... és utálom a méheket.", + "Egy napon majd szeretnék világot látni. Csak tartogat nekem valamit az élet ezen a városkán kívül is.", + ], + "npc.speech.villager_decline_trade": [ + "Sajnálom, én nem tudok mit áruba bocsájtani.", + "Üzletelni? Velem? Mintha lenne bármim, ami fel tudná kelteni az érdeklődésedet...", + "A ház az enyém! Nem fogom elcserélni semmiért!", + ], + "npc.speech.merchant_advertisement": [ + "Ajánlhatok személyemben egy kereskedőpartnert?", + "Szeretnél velem üzletelni?", + "Rengeteg árum van, szeretnél vetni rájuk egy pillantást?" + ], + "npc.speech.merchant_busy": [ + "Hé, várd ki a sorod!", + "Várj egy kicsit légyszíves, belőlem is csak egy van.", + "Látod ezt a másik embert előtted?", + "Egy pillanat, hadd fejezzem be!", + "Ne vágj be mások elé!", + "Most épp nem érek rá, gyere vissza később!" + ], + "npc.speech.merchant_trade_successful": [ + "Köszönöm, hogy velem üzleteltél!", + "Hálás köszönet!", + ], + "npc.speech.merchant_trade_declined": [ + "Talán majd máskor. Legyen szép napod!", + "Kár, majd talán legközelebb!" + ], + "npc.speech.villager_cultist_alarm": [ + "Vigyázat! Elszabadult egy kultista!", + "Fegyverbe! Támadnak a kultisták!", + "Hogy merik a kultisták megtámadni a mi városkánkat?!", + "Halál a kultistákra!", + "Itt nem tűrünk meg egy kultistát sem!", + "Te véres kultista!", + "Ízleld meg a kardom élét, mocskos kultista!", + "Semmi nem moshatja le a vért a kezedről, kultista!", + "Millió mérgező mályvaszín medúza! Egy kultista van köztünk!", + "A kultisták gonoszkodásainak hamarosan vége!", + "Ez a kultista az enyém!", + "Ideje, hogy találkozz a teremtőddel, aljas kultista!", + "Itt egy kultista! Kapjuk el!", + "Itt egy kultista! Támadás!", + "Itt egy kultista! Ne hagyjuk megszökni!", + "Nem óhajt kultista őméltósága egy kis HALÁLT?!", + "Nincs bocsánat! Nincs feledés! Kultisták, bűnhődjetek!", + "Pusztulj, kultista!", + "Egyszer véget ér a rémuralmatok!", + "Ezt azt eddigi tetteitekért!", + "Nem látjuk szívesen a fajtátokat errefelé!", + "Maradtatok volna a föld alatt!", + ], + "npc.speech.villager_under_attack": [ + "Segítség, Megtámadtak!", + "Segítség, Megtámadtak!", + "Aúú! Megtámadtak!!", + "Aúú! Megtámadtak! Segítség!", + "Segítsetek! Megtámadtak!", + "Megtámadtak! Segítség!", + "Megtámadtak! Segítsetek!", + "Segítség!", + "Segítség! Segítség!", + "Segítség! Segítség! Segítség!", + "Megtámadtak!", + "ÁÁÁÁÁÁ! Megtámadtak!", + "ÁÁÁÁÁÁ! Megtámadtak! Segítség!", + "Segítség! Megtámadtak minket!", + "Segítség! Gyilkos!", + "Segítség! Elszabadult egy gyilkos!", + "Segítség! Meg akarnak ölni!", + "Őrség, Megtámadtak!", + "Őrség! Megtámadtak!", + "Megtámadtak! Őrség!", + "Segítség! Őrség! Megtámadtak!", + "Őrség! Gyertek gyorsan!", + "Őrség! Őrség!", + "Őrség! Egy gazember megtámadott!", + "Őrség, aprítsátok fel ezt az alávaló gazembert!", + "Őrség! Itt egy gyilkos!", + "Őrség! Segítsetek!", + "Ezt nem úszod meg szárazon! Őrség!", + "Te senkiházi!", + "Valaki segítsen!!", + "Segítsetek! Kérlek!", + "Aúú! Őrség! Segítség!", + "Engem akarnak!", + "Segítség! Segítség! Valaki le akar gyűrni!", + "Áá, most láthatjuk a rendszerrel járó erőszakot.", + "Ez csak egy karcolás!", + "Hagyd abba!", + "Ártottam én neked valaha?!", + "Kérlek ne bántalmazz tovább!", + "Hé! Vigyázz, merre irányzod azt a dolgot!", + "Takarodj innen, te mocskos gazember!", + "Hagyd abba! Menj innen!", + "Sikerült igencsak felbőszítened!", + "Hé! Kinek képzeled magad?!", + "Ezért a fejeddel fogsz fizetni!", + "Hagyd abba, kérlek! Nincs nálam semmi értékes!", + "Rád uszítom a bátyámat, ő nagyobb, mint én!", + "Neee, megmondalak anyának!", + "Légy átkozott!", + "Kérlek ne tedd ezt!", + "Ez nem volt szép tőled!", + "Hatásos a fegyvered, most már igazán elrakhatnád!", + "Könyörülj rajtam!", + "Kérlek, családom van!", + "Még túl fiatal vagyok a halálhoz!", + "Nem tudnánk ezt megbeszélni?", + "Az erőszak nem megoldás!", + "Úgy látszik igencsak pocsék nap ez a mai...", + "Hé, ez fáj!", + "Ííí!", + "Milyen durva vagy!", + "Könyörögve kérlek, hagyd abba!", + "A fene vigyen el!", + "Ez nem vicces!", + "Hogy merészeled?!", + "Ezért megfizetsz!", + "Nagyon meg fogod bánni, ha ezt tovább folytatod!", + "Ne akard, hogy bántsalak!", + "Itt valami félreértés lesz!", + "Erre nincs semmi szükség!", + "Takarodj, te pokolfajzat!", + "Ez nagyon fáj!", + "Miért tennél ilyet?!", + "A szellemekre, hagyd abba!", + "Biztos keversz engem valakivel!", + "Én nem ezt érdemlem!", + "Ezt kérlek ne!", + "Őrség, dobjátok ezt a szörnyeteget a tóba!", + "Rád uszítom a taraszkomat!", + "Mért éééééén?", + ], + "npc.speech.villager_enemy_killed": [ + "Eltiportam az ellenségemet!", + "Végre ismét békességben!", + "...no, hol is tartottam?", + ] + } +) diff --git a/assets/voxygen/i18n/it_IT/_manifest.ron b/assets/voxygen/i18n/it_IT/_manifest.ron index 99f5180143..67862b8fbc 100644 --- a/assets/voxygen/i18n/it_IT/_manifest.ron +++ b/assets/voxygen/i18n/it_IT/_manifest.ron @@ -28,205 +28,5 @@ asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", scale_ratio: 1.0, ), - }, - - string_map: { - - }, - - vector_map: { - "loading.tips": [ - "Premi 'G' per accendere la tua lanterna.", - "Premi 'F1' per vedere tutte le combinazioni di tasti base.", - "Puoi scrivere /say o /s per chattare solo con giocatori attorno a te.", - "Puoi scrivere /region o /r per chattare solo con giocatori entro qualche centinaio di blocchi attorno a te.", - "Gli Amministratori possono usare il comando /build per entrare in modalità costruzione.", - "Puoi scrivere /group o /g per chattare solo con giocatori attualmente nel tuo gruppo.", - "Per mandare messaggi privati scrivi /tell seguito dal nome di un giocatore e dal tuo messaggio.", - "Fai attenzione al cibo, ai forzieri e ad altro bottino sparso per tutto il mondo!", - "Inventario pieno di cibo? Prova a utilizzarlo per crearne di migiliore!", - "Ti stai chiedendo cosa ci sia da fare? Prova uno dei dungeon segnati sulla mappa!", - "Non dimenticare di sistemare la grafica secondo il tuo sistema. Premi 'N' per aprire le impostazioni.", - "Giocare con altri è divertente! Premi 'O' per vedere chi è online.", - "Premi 'J' per ballare. Festeggia!", - "Premi 'Shift Sinistro' per aprire il tuo Aliante e conquistare i cieli.", - "Veloren è ancora in Pre-Alpha. Facciamo del nostro meglio per migliorarlo ogni giorno!", - "Se vuoi unirti al team di sviluppo o se vuoi solamente chattare con noi, unisciti al nostro server Discord.", - "Puoi scegliere di mostrare la tua quantità di vita sulla barra della vita dalle impostazioni.", - "Siedi vicino a un fuoco (col tasto 'K') per riprenderti lentamente dalle lesioni.", - "Hai bisogno di più borse o di armature migliori per continuare la tua avventura? Premi 'C' per aprire il menu di creazione!", - ], - "npc.speech.villager": [ - "Non è una così bella giornata?", - "Come stai oggi?", - "Il meglio della mattina a te!", - "Mi chiedo cosa pensi il Catoblepas quando mangia l'erba.", - "Cosa ne pensi di questo tempo?", - "Pensare a quei dungeon mi spaventa. Spero che qualcuno li ripulisca.", - "Mi piacerebbe andare a fare speleologia in una grotta quando sarò più forte.", - "Hai visto il mio gatto?", - "Hai mai sentito dei feroci Squali di Terra? Ho sentito dire che vivono nei deserti.", - "Dicono che gemme luccicanti di tutti i tipi possono essere trovate nelle caverne.", - "Vado semplicemente pazzo per il formaggio!", - "Non vieni dentro? Stavamo per mangiare del formaggio!", - "Dicono che i funghi fanno bene alla salute. Non li ho mai mangiati io.", - "Non dimenticare i crackers!", - "Semplicemente adoro il formaggio nanico. Mi piacerebbe poterlo fare.", - "Mi chiedo cosa ci sia dall'altro lato delle montagne.", - "Spero di potermi fare il mio aliante un giorno.", - "Ti piacerebbe vedere il mio giardino? Okay, forse qualche altra volta.", - "Una piacevole giornata per una passeggiata nel bosco!", - "Essere, o non essere? Penso che sarò un contadino.", - "Non pensi che il nostro villaggio sia il migliore?", - "Cosa pensi che renda i Resti Splendenti splendenti?", - "Penso che sia il momento per una seconda colazione!", - "Hai mai catturato una lucciola?", - "Proprio non capisco da dove continuino ad arrivare quei Sauroks.", - "Spero che qualcuno tenga i lupi alla larga dal villaggio.", - "Ho avuto un sogno meraviglio sul formaggio la scorsa notte. Cosa vorrà dire?", - "Ho lasciato del formaggio con mio fratello. Ora non so se esista o meno. Io lo chiamo il formaggio di Schrödinger.", - "Ho lasciato del formaggio con mio sorella. Ora non so se esista o meno. Io lo chiamo il formaggio di Schrödinger.", - "Qualcuno dovrebbe fare qualcosa a proposito di quei cultisti. Preferibilmente non io.", - "Spero che piova presto. Sarebbe buono per il raccolto.", - "Amo il miele! E odio le api.", - "Voglio vedere il mondo un giorno. Ci deve essere dell'altro nella vita oltre questo villaggio.", - ], - "npc.speech.villager_decline_trade": [ - "Mi dispiace, non ho nulla da commerciare.", - "Commerciare? Come se avessi qualcosa che potrebbe interessarti.", - "La mia casa è mia, e non la scambierei per nulla.", - ], - "npc.speech.merchant_advertisement": [ - "Posso interessarti in uno scambio?", - "Vuoi commerciare con me?", - "Ho molti beni, vuoi dare un'occhiata?" - ], - "npc.speech.merchant_busy": [ - "Ehi, aspetta il tuo turno.", - "Ti prego di aspettare, sono solo una persona.", - "La vedi l'altra persona davanti a te?", - "Solo un momento, lasciami finire.", - "Non si salta la fila.", - "Sono occupato, torna più tardi." - ], - "npc.speech.merchant_trade_successful": [ - "Grazie per aver fatto affari con me!", - "Grazie a te!", - ], - "npc.speech.merchant_trade_declined": [ - "Magari un'altra volta, abbi una buona giornata!", - "Peccato, magari la prossima volta allora!" - ], - "npc.speech.villager_cultist_alarm": [ - "Fai attenzione! C'è un cultista in giro!", - "Alle armi! I cultisti stanno attaccando!", - "Come osano i cultisti attaccare il nostro villaggio!", - "Morte ai cultisti!", - "I cultisti non saranno tollerati qui!", - "Cultista assassino!", - "Assaggia la lama della mia spada, sporco cultista!", - "Nulla può pulire il sangue dalle tue mani, cultista!", - "Per miliardi di cirripedi blu ricoperti di vesciche! Un cultista è tra noi!", - "I mali di questo cultista stanno per finire!", - "Questo cultista è mio!", - "Preparati a incontrare il tuo creatore, schifoso cultista!", - "Vedo un cultista! Prendeteli!", - "Vedo un cultista! Attaccate!", - "Vedo un cultista! Non lasciateli scappare!", - "L'onorabile cultista gradirebbe della MORTE?!", - "Mai perdonare! Mai dimenticare! Cultista, pentiti!", - "Muori, cultista!", - "Il tuo regno di terrore cesserà!", - "Questo è per tutto quello che hai fatto!", - "Non ci vanno a genio quelli come te qui in giro.", - "Saresti dovuto rimanere nel sottosuolo!", - ], - "npc.speech.villager_under_attack": [ - "Aiuto, Sono sotto attacco!", - "Aiuto! Sono sotto attacco!", - "Ouch! Sono sotto attacco!", - "Ouch! Sono sotto attacco! Aiuto!", - "Aiutatemi! Sono sotto attacco!", - "Sono sotto attacco! Aiuto!", - "Sono sotto attacco! Aiutatemi!", - "Aiuto!", - "Aiuto! Aiuto!", - "Aiuto! Aiuto! Aiuto!", - "Sono sotto attacco!", - "AAAHHH! Sono sotto attacco!", - "AAAHHH! Sono sotto attacco! Aiuto!", - "Aiuto! Siamo sotto attacco!", - "Aiuto! Assassino!", - "Aiuto! C'è un assassino a piede libero!", - "Aiuto! Stanno cercando di uccidermi", - "Guardie, Sono sotto attacco!", - "Guardie! Sono sotto attacco!", - "Sono sotto attacco! Guardie!", - "Aiuto! Guardie! Sono sotto attacco!", - "Guardie! Venite veloci!", - "Guardie! Guardie!", - "Guardie! C'è un deliquente che mi sta attaccando!", - "Guardie, uccidete questa schifosa canaglia!", - "Guardie! C'è un assassino!", - "Guardie! Aiuto!", - "Non la farai franca! Guardie!", - "Mostro!", - "Aiuto!", - "Aiuto! Per favore!", - "Ouch! Guardie! Aiuto!", - "Stanno venendo per me!", - "Aiuto! Aiuto! Sto venendo represso!", - "Ah, ora vediamo la violenza innata nel sistema.", - "È solo un graffio!", - "Smettila!", - "Che ti ho mai fatto?!", - "Per favore smettila di attaccarmi!", - "Ehi! Stai attento a dove punti quella cosa!", - "Odioso miserabile, finiscila!", - "Smettila! Vai via!", - "Ora mi stai facendo arrabbiare!", - "Oi! Chi pensi di essere?!", - "Avrò la tua testa per ciò!", - "Smettila, per favore! Non porto nulla di valore!", - "Ti metterò contro mio fratello, è più grande di me!", - "Nooo, lo dico alla mamma!", - "Maledetto!", - "Per favore non farlo.", - "Non è stato molto carino!", - "La tua arma funziona, puoi metterla via ora!", - "Risparmiami!", - "Per favore, ho una famiglia!", - "Sono troppo giovane per morire!", - "Possiamo parlarne?", - "La violenza non è mai la risposta!", - "Oggi la giornata sta diventando brutta...", - "Ehi, ha fatto male!", - "Eek!", - "Quanto rude!", - "Fermati, ti prego!", - "Che ti ammali!", - "Non è divertente.", - "Come ti permetti?!", - "La pagherai!", - "Continua così e te ne pentirai!", - "Non mi costringere a farti del male!", - "Deve esserci un qualche malinteso!", - "Non hai bisogno di fare così!", - "Scompari, mostro!", - "Ha fatto molto male!", - "Perché mai dovresti farlo?", - "Per gli spiriti, finiscila!", - "Devi avermi confuso con qualcun'altro!", - "Non me lo merito!", - "Per favore non lo fare di nuovo.", - "Guardie, lanciate questo mostro nel lago!", - "Sguinzaglierò il mio tarasqua su di te!", - "Perché meeeeeee?", - ], - "npc.speech.villager_enemy_killed": [ - "Ho distrutto il mio nemico!", - "Finalmente in pace!", - "... ora, che cosa stavo facendo?", - ] } ) \ No newline at end of file diff --git a/assets/voxygen/i18n/it_IT/buff.ron b/assets/voxygen/i18n/it_IT/buff.ron index 3bed46b05c..254bb051d5 100644 --- a/assets/voxygen/i18n/it_IT/buff.ron +++ b/assets/voxygen/i18n/it_IT/buff.ron @@ -32,7 +32,7 @@ "buff.desc.crippled": "Il tuo movimento è storpio dal momento che le tue gambe sono gravemente ferite.", // Buffs stats "buff.stat.health": "Rigenera {str_total} di Salute", - "buff.stat.increase_max_stamina": "Aumenta la Stamina Massima di {strength}", + "buff.stat.increase_max_energy": "Aumenta la Stamina Massima di {strength}", "buff.stat.increase_max_health": "Aumenta la Salute Massima di {strength}", "buff.stat.invulnerability": "Dona invulnerabilità", // Text diff --git a/assets/voxygen/i18n/it_IT/hud/bag.ron b/assets/voxygen/i18n/it_IT/hud/bag.ron index c763a7bb66..517ee552f6 100644 --- a/assets/voxygen/i18n/it_IT/hud/bag.ron +++ b/assets/voxygen/i18n/it_IT/hud/bag.ron @@ -26,7 +26,7 @@ "hud.bag.offhand": "Mano Secondaria", "hud.bag.bag": "Borsa", "hud.bag.health": "Salute", - "hud.bag.stamina": "Stamina", + "hud.bag.energy": "Stamina", "hud.bag.combat_rating": "Grado di Combattimento", "hud.bag.protection": "Protezione", "hud.bag.stun_res": "Resilienza allo Stordimento", diff --git a/assets/voxygen/i18n/it_IT/hud/hud_settings.ron b/assets/voxygen/i18n/it_IT/hud/settings.ron similarity index 98% rename from assets/voxygen/i18n/it_IT/hud/hud_settings.ron rename to assets/voxygen/i18n/it_IT/hud/settings.ron index 7d00082ec5..0defe68179 100644 --- a/assets/voxygen/i18n/it_IT/hud/hud_settings.ron +++ b/assets/voxygen/i18n/it_IT/hud/settings.ron @@ -15,7 +15,7 @@ "hud.settings.relative_scaling": "Ridimensionamento Relativo", "hud.settings.custom_scaling": "Ridimensionamento Personalizzato", "hud.settings.crosshair": "Mirino", - "hud.settings.transparency": "Trasparenza", + "hud.settings.opacity": "Trasparenza", "hud.settings.hotbar": "Slot Rapido", "hud.settings.toggle_shortcuts": "Attiva/Disattiva Tasti rapidi", "hud.settings.buffs_skillbar": "Buff alla Barra delle Abilità", @@ -33,7 +33,7 @@ "hud.settings.values": "Valori", "hud.settings.percentages": "Percentuali", "hud.settings.chat": "Chat", - "hud.settings.background_transparency": "Trasparenza Sfondo", + "hud.settings.background_opacity": "Trasparenza Sfondo", "hud.settings.chat_character_name": "Nome Personaggi in chat", "hud.settings.loading_tips": "Consigli Schermata di Caricamento", "hud.settings.reset_interface": "Ripristina Predefiniti", diff --git a/assets/voxygen/i18n/it_IT/skills.ron b/assets/voxygen/i18n/it_IT/hud/skills.ron similarity index 97% rename from assets/voxygen/i18n/it_IT/skills.ron rename to assets/voxygen/i18n/it_IT/hud/skills.ron index 98f6c0a93f..8e0f41e237 100644 --- a/assets/voxygen/i18n/it_IT/skills.ron +++ b/assets/voxygen/i18n/it_IT/hud/skills.ron @@ -11,8 +11,8 @@ // General "hud.skill.inc_health_title": "Aumenta Salute", "hud.skill.inc_health": "Incrementa la salute massima di {boost}{SP}", - "hud.skill.inc_stam_title": "Aumenta Stamina", - "hud.skill.inc_stam": "Incrementa la stamina massima di {boost}{SP}", + "hud.skill.inc_energy_title": "Aumenta Stamina", + "hud.skill.inc_energy": "Incrementa la stamina massima di {boost}{SP}", "hud.skill.unlck_sword_title": "Sblocca Spada", "hud.skill.unlck_sword": "Sblocca l'albero di abilità per la spada{SP}", "hud.skill.unlck_axe_title": "Sblocca Ascia", @@ -27,8 +27,8 @@ "hud.skill.unlck_sceptre": "Sblocca l'albero di abilità per lo scettro{SP}", "hud.skill.dodge_title": "Schivata", "hud.skill.dodge": "Le rotolate per schivare vengono attivate col click del tasto centrale del mouse, e concede immunità temporanea ad attacchi ravvicinati (iframes) mentre stai rotolando.", - "hud.skill.roll_stamina_title": "Costo di Stamina Rotolata", - "hud.skill.roll_stamina": "Rotolare usa il {boost}% in meno di stamina{SP}", + "hud.skill.roll_energy_title": "Costo di Stamina Rotolata", + "hud.skill.roll_energy": "Rotolare usa il {boost}% in meno di stamina{SP}", "hud.skill.roll_speed_title": "Velocità Rotolata", "hud.skill.roll_speed": "Rotola il {boost}% più velocemente{SP}", "hud.skill.roll_dur_title": "Durata Rotolata", @@ -95,8 +95,8 @@ "hud.skill.st_flamethrower_damage" : "Incrementa il danno del {boost}%{SP}", "hud.skill.st_explosion_radius_title" : "Portata Esplosione", "hud.skill.st_explosion_radius" : "Più grande è meglio, aumenta la portata dell'esplosione del {boost}%{SP}", - "hud.skill.st_stamina_regen_title" : "Rigenerazione Stamina", - "hud.skill.st_stamina_regen" : "Incrementa la stamina ottenuta del {boost}%{SP}", + "hud.skill.st_energy_regen_title" : "Rigenerazione Stamina", + "hud.skill.st_energy_regen" : "Incrementa la stamina ottenuta del {boost}%{SP}", "hud.skill.st_fireball_title" : "Palla di Fuoco", "hud.skill.st_fireball" : "Spara una palla di fuoco che esplode all'impatto", "hud.skill.st_damage_title" : "Danno", diff --git a/assets/voxygen/i18n/it_IT/main.ron b/assets/voxygen/i18n/it_IT/main.ron index e2fce13eec..953dd47856 100644 --- a/assets/voxygen/i18n/it_IT/main.ron +++ b/assets/voxygen/i18n/it_IT/main.ron @@ -67,5 +67,26 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "Premi 'G' per accendere la tua lanterna.", + "Premi 'F1' per vedere tutte le combinazioni di tasti base.", + "Puoi scrivere /say o /s per chattare solo con giocatori attorno a te.", + "Puoi scrivere /region o /r per chattare solo con giocatori entro qualche centinaio di blocchi attorno a te.", + "Gli Amministratori possono usare il comando /build per entrare in modalità costruzione.", + "Puoi scrivere /group o /g per chattare solo con giocatori attualmente nel tuo gruppo.", + "Per mandare messaggi privati scrivi /tell seguito dal nome di un giocatore e dal tuo messaggio.", + "Fai attenzione al cibo, ai forzieri e ad altro bottino sparso per tutto il mondo!", + "Inventario pieno di cibo? Prova a utilizzarlo per crearne di migiliore!", + "Ti stai chiedendo cosa ci sia da fare? Prova uno dei dungeon segnati sulla mappa!", + "Non dimenticare di sistemare la grafica secondo il tuo sistema. Premi 'N' per aprire le impostazioni.", + "Giocare con altri è divertente! Premi 'O' per vedere chi è online.", + "Premi 'J' per ballare. Festeggia!", + "Premi 'Shift Sinistro' per aprire il tuo Aliante e conquistare i cieli.", + "Veloren è ancora in Pre-Alpha. Facciamo del nostro meglio per migliorarlo ogni giorno!", + "Se vuoi unirti al team di sviluppo o se vuoi solamente chattare con noi, unisciti al nostro server Discord.", + "Puoi scegliere di mostrare la tua quantità di vita sulla barra della vita dalle impostazioni.", + "Siedi vicino a un fuoco (col tasto 'K') per riprenderti lentamente dalle lesioni.", + "Hai bisogno di più borse o di armature migliori per continuare la tua avventura? Premi 'C' per aprire il menu di creazione!", + ], } ) diff --git a/assets/voxygen/i18n/it_IT/npc.ron b/assets/voxygen/i18n/it_IT/npc.ron new file mode 100644 index 0000000000..fd4a705a27 --- /dev/null +++ b/assets/voxygen/i18n/it_IT/npc.ron @@ -0,0 +1,183 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for italian +( + string_map: { + + }, + + vector_map: { + "npc.speech.villager": [ + "Non è una così bella giornata?", + "Come stai oggi?", + "Il meglio della mattina a te!", + "Mi chiedo cosa pensi il Catoblepas quando mangia l'erba.", + "Cosa ne pensi di questo tempo?", + "Pensare a quei dungeon mi spaventa. Spero che qualcuno li ripulisca.", + "Mi piacerebbe andare a fare speleologia in una grotta quando sarò più forte.", + "Hai visto il mio gatto?", + "Hai mai sentito dei feroci Squali di Terra? Ho sentito dire che vivono nei deserti.", + "Dicono che gemme luccicanti di tutti i tipi possono essere trovate nelle caverne.", + "Vado semplicemente pazzo per il formaggio!", + "Non vieni dentro? Stavamo per mangiare del formaggio!", + "Dicono che i funghi fanno bene alla salute. Non li ho mai mangiati io.", + "Non dimenticare i crackers!", + "Semplicemente adoro il formaggio nanico. Mi piacerebbe poterlo fare.", + "Mi chiedo cosa ci sia dall'altro lato delle montagne.", + "Spero di potermi fare il mio aliante un giorno.", + "Ti piacerebbe vedere il mio giardino? Okay, forse qualche altra volta.", + "Una piacevole giornata per una passeggiata nel bosco!", + "Essere, o non essere? Penso che sarò un contadino.", + "Non pensi che il nostro villaggio sia il migliore?", + "Cosa pensi che renda i Resti Splendenti splendenti?", + "Penso che sia il momento per una seconda colazione!", + "Hai mai catturato una lucciola?", + "Proprio non capisco da dove continuino ad arrivare quei Sauroks.", + "Spero che qualcuno tenga i lupi alla larga dal villaggio.", + "Ho avuto un sogno meraviglio sul formaggio la scorsa notte. Cosa vorrà dire?", + "Ho lasciato del formaggio con mio fratello. Ora non so se esista o meno. Io lo chiamo il formaggio di Schrödinger.", + "Ho lasciato del formaggio con mio sorella. Ora non so se esista o meno. Io lo chiamo il formaggio di Schrödinger.", + "Qualcuno dovrebbe fare qualcosa a proposito di quei cultisti. Preferibilmente non io.", + "Spero che piova presto. Sarebbe buono per il raccolto.", + "Amo il miele! E odio le api.", + "Voglio vedere il mondo un giorno. Ci deve essere dell'altro nella vita oltre questo villaggio.", + ], + "npc.speech.villager_decline_trade": [ + "Mi dispiace, non ho nulla da commerciare.", + "Commerciare? Come se avessi qualcosa che potrebbe interessarti.", + "La mia casa è mia, e non la scambierei per nulla.", + ], + "npc.speech.merchant_advertisement": [ + "Posso interessarti in uno scambio?", + "Vuoi commerciare con me?", + "Ho molti beni, vuoi dare un'occhiata?" + ], + "npc.speech.merchant_busy": [ + "Ehi, aspetta il tuo turno.", + "Ti prego di aspettare, sono solo una persona.", + "La vedi l'altra persona davanti a te?", + "Solo un momento, lasciami finire.", + "Non si salta la fila.", + "Sono occupato, torna più tardi." + ], + "npc.speech.merchant_trade_successful": [ + "Grazie per aver fatto affari con me!", + "Grazie a te!", + ], + "npc.speech.merchant_trade_declined": [ + "Magari un'altra volta, abbi una buona giornata!", + "Peccato, magari la prossima volta allora!" + ], + "npc.speech.villager_cultist_alarm": [ + "Fai attenzione! C'è un cultista in giro!", + "Alle armi! I cultisti stanno attaccando!", + "Come osano i cultisti attaccare il nostro villaggio!", + "Morte ai cultisti!", + "I cultisti non saranno tollerati qui!", + "Cultista assassino!", + "Assaggia la lama della mia spada, sporco cultista!", + "Nulla può pulire il sangue dalle tue mani, cultista!", + "Per miliardi di cirripedi blu ricoperti di vesciche! Un cultista è tra noi!", + "I mali di questo cultista stanno per finire!", + "Questo cultista è mio!", + "Preparati a incontrare il tuo creatore, schifoso cultista!", + "Vedo un cultista! Prendeteli!", + "Vedo un cultista! Attaccate!", + "Vedo un cultista! Non lasciateli scappare!", + "L'onorabile cultista gradirebbe della MORTE?!", + "Mai perdonare! Mai dimenticare! Cultista, pentiti!", + "Muori, cultista!", + "Il tuo regno di terrore cesserà!", + "Questo è per tutto quello che hai fatto!", + "Non ci vanno a genio quelli come te qui in giro.", + "Saresti dovuto rimanere nel sottosuolo!", + ], + "npc.speech.villager_under_attack": [ + "Aiuto, Sono sotto attacco!", + "Aiuto! Sono sotto attacco!", + "Ouch! Sono sotto attacco!", + "Ouch! Sono sotto attacco! Aiuto!", + "Aiutatemi! Sono sotto attacco!", + "Sono sotto attacco! Aiuto!", + "Sono sotto attacco! Aiutatemi!", + "Aiuto!", + "Aiuto! Aiuto!", + "Aiuto! Aiuto! Aiuto!", + "Sono sotto attacco!", + "AAAHHH! Sono sotto attacco!", + "AAAHHH! Sono sotto attacco! Aiuto!", + "Aiuto! Siamo sotto attacco!", + "Aiuto! Assassino!", + "Aiuto! C'è un assassino a piede libero!", + "Aiuto! Stanno cercando di uccidermi", + "Guardie, Sono sotto attacco!", + "Guardie! Sono sotto attacco!", + "Sono sotto attacco! Guardie!", + "Aiuto! Guardie! Sono sotto attacco!", + "Guardie! Venite veloci!", + "Guardie! Guardie!", + "Guardie! C'è un deliquente che mi sta attaccando!", + "Guardie, uccidete questa schifosa canaglia!", + "Guardie! C'è un assassino!", + "Guardie! Aiuto!", + "Non la farai franca! Guardie!", + "Mostro!", + "Aiuto!", + "Aiuto! Per favore!", + "Ouch! Guardie! Aiuto!", + "Stanno venendo per me!", + "Aiuto! Aiuto! Sto venendo represso!", + "Ah, ora vediamo la violenza innata nel sistema.", + "È solo un graffio!", + "Smettila!", + "Che ti ho mai fatto?!", + "Per favore smettila di attaccarmi!", + "Ehi! Stai attento a dove punti quella cosa!", + "Odioso miserabile, finiscila!", + "Smettila! Vai via!", + "Ora mi stai facendo arrabbiare!", + "Oi! Chi pensi di essere?!", + "Avrò la tua testa per ciò!", + "Smettila, per favore! Non porto nulla di valore!", + "Ti metterò contro mio fratello, è più grande di me!", + "Nooo, lo dico alla mamma!", + "Maledetto!", + "Per favore non farlo.", + "Non è stato molto carino!", + "La tua arma funziona, puoi metterla via ora!", + "Risparmiami!", + "Per favore, ho una famiglia!", + "Sono troppo giovane per morire!", + "Possiamo parlarne?", + "La violenza non è mai la risposta!", + "Oggi la giornata sta diventando brutta...", + "Ehi, ha fatto male!", + "Eek!", + "Quanto rude!", + "Fermati, ti prego!", + "Che ti ammali!", + "Non è divertente.", + "Come ti permetti?!", + "La pagherai!", + "Continua così e te ne pentirai!", + "Non mi costringere a farti del male!", + "Deve esserci un qualche malinteso!", + "Non hai bisogno di fare così!", + "Scompari, mostro!", + "Ha fatto molto male!", + "Perché mai dovresti farlo?", + "Per gli spiriti, finiscila!", + "Devi avermi confuso con qualcun'altro!", + "Non me lo merito!", + "Per favore non lo fare di nuovo.", + "Guardie, lanciate questo mostro nel lago!", + "Sguinzaglierò il mio tarasqua su di te!", + "Perché meeeeeee?", + ], + "npc.speech.villager_enemy_killed": [ + "Ho distrutto il mio nemico!", + "Finalmente in pace!", + "... ora, che cosa stavo facendo?", + ] + } +) \ No newline at end of file diff --git a/assets/voxygen/i18n/ja_JP/_manifest.ron b/assets/voxygen/i18n/ja_JP/_manifest.ron index 94b254a111..70a840119c 100644 --- a/assets/voxygen/i18n/ja_JP/_manifest.ron +++ b/assets/voxygen/i18n/ja_JP/_manifest.ron @@ -28,200 +28,5 @@ asset_key: "voxygen.font.bdfUMplus-outline", scale_ratio: 0.9, ), - }, - - string_map: { - - }, - - vector_map: { - "loading.tips": [ - "Gキーを押すとランタンが灯ります。", - "F1キーを押すとすべてのデフォルトキー設定の確認できます。", - "/sayまたは/sと入力すると自分の周りのプレイヤーとだけチャットできます。", - "/regionまたは/rと入力すると自分の周辺、数百ブロックのプレーヤーとだけチャットできます。", - "管理者は、/buildコマンドでビルドモードに入れます。", - "/groupまたは/gと入力すると現在所属しているグループのプレイヤーとだけチャットできます。", - "プライベートメッセージを送るには、/tellと入力したあと、プレイヤー名とメッセージを入力します。", - "世界中に散らばっている食料やチェストに注目してみましょう!", - "持ち物が食料でいっぱいですか?それを使ってもっといい食料をクラフトしてみましょう!", - "何をしたらいいかわからないですか?地図にあるダンジョンに挑戦してみましょう!", - "お使いのシステムに合わせてグラフィックの調整もお忘れなく。Nキーを押すと設定が開きます。", - "誰かと一緒に遊ぶのは楽しいですよね!Oキーを押すと誰がオンラインかわかります。", - "Jキーを押すとダンスします。パーティ!", - "左Shiftキーを押すとグライダーが開いて空を攻略できます。", - "Velorenは、まだアルファ版です。私たちは日々、改善のためにベストを尽くしています!", - "開発チームに参加したい方や私たちとチャットしたい方はDiscordサーバーに参加しましょう。", - "体力バーに表示される体力量の表示は、設定で切り替えられます。", - "Kキーを押して焚き火のそばに座ると体力がゆっくり回復します", - "冒険のためにバッグやいい鎧が欲しいですか?Cキーを押すとクラフトメニューが開きます。", - ], - "npc.speech.villager": [ - "こんな素敵な日があってもいいよね?", - "ご機嫌いかが?", - "最高の朝を迎えましょう!", - "カトブレパスは なにを考えて草を食べてるのだろう", - "この天気についてどうお考えですか?", - "あのダンジョンの事を考えると怖くなります。誰かがあそこをクリアしてくれないだろうか", - "強くなったら洞窟に探検に行きたいですね", - "私の猫チャンを見かけなかった?", - "ランドシャークという獰猛なサメをご存知ですか?なんでも砂漠に住んでるらしいですよ", - "洞窟には さまざまな種類の光り輝く宝石が見つかるらしいですよ", - "やっぱ チーズにはクラッカー!", - "寄っていきませんか?ちょうどチーズを食べようとしてたところなんです", - "マッシュルームは健康に良いと言われてます。自分は食べないですけど", - "クラッカーを忘れるな!", - "私はシンプルにドワーフチーズに憧れてます。作れたらなぁ", - "山の向こう側には何があるのだろう", - "いつの日か 自分のグライダーを作ってみたいですね", - "私の庭を見ていかない?うん また今度ね", - "森を散歩するには なんて素敵な日なんだ!", - "なるべきか ならざるべきか… 僕は農家にだけど", - "私たちの村が一番だと思わない?", - "なんで光る死体は光っているんだろう?", - "二度目の朝ゴハンの時間だよ!", - "ホタルを捕まえたことはある?", - "あのサウロクたちが どこから来ているのか想像できませんね", - "誰か狼を村から遠ざけてくれればいいのに", - "昨晩 素敵なチーズの夢を見ました。それってどういう意味なの?", - "兄にチーズを残してきました。でも 今となってはそれが存在するのかわかりません。だから 私は「シュレディンガーのチーズ」と呼んでいます", - "妹にチーズを残してきました。でも 今となってはそれが存在するのかわかりません。だから 私は「シュレディンガーのチーズ」と呼んでいます", - "誰かが あのカルト教団をなんとかしないといけない。私は やりたくないけど", - "早く雨が降ってほしい。雨は作物にいいからね", - "ハチミツ大好き! でも ハチは大嫌い!", - "私はいつか世界を見てみたい。この村よりももっと豊かな人生があるはずだから", - ], - "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": [ - "助けて、襲撃されてます!", - "助けて!襲われてます!", - "ああっ!襲われてます!", - "ああっ!襲撃されてます!助けて!", - "助けてください!襲われてます!", - "襲われています!助けて!", - "襲われています!助けてください!", - "助けて!", - "助けて!助けて!", - "助けて!助けて!助けて!", - "襲われています!", - "あああああ!襲われてます!", - "あああああ!襲撃されてます!助けて!", - "助けて!私たちは襲撃されてます!", - "助けて!人殺し!", - "助けて!人殺しが逃亡しています!", - "助けて!私を殺そうとしています!", - "衛兵さん、襲われています!", - "衛兵!襲われてます!", - "襲われています!衛兵!", - "助けて!衛兵!襲われてます!", - "衛兵!早く来て!", - "衛兵!衛兵!", - "衛兵!悪党に襲ってきた!", - "衛兵、うす汚い悪党を切って!", - "衛兵!人殺しがいる!", - "衛兵!助けてください!", - "このままでは許さないぞ!衛兵!", - "この、悪魔め!", - "助けてください!", - "助けて!お願い!", - "ああっ!衛兵!助けて!", - "狙われてます!", - "助けて!助けて!私は抑圧されてる!", - "ああ、システムに内在する暴力が見えてきた。", - "ただのかすり傷だ!", - "止めてくれ!", - "お前に何をしたっていうんだよ!", - "お願いですから、攻撃を止めてください!", - "ヘイ!どこに向けてるんだ!", - "凶暴な奴らは消えてしまえ!", - "止まれ!出ていけ!", - "私は怒っている!", - "おい!!お前は何様だと思ってるんだ!", - "お前の首を取ってやるぜ!", - "止めて、お願い!金目のものは持ってないんだ!", - "兄貴を連れてくるぞ、俺よりデカいんだからな!", - "ノー!母ちゃんにいいつけてやる!", - "呪ってやる!", - "お願いだから止めてください。", - "ひでえな!", - "武器は使ったんだから、もうしまってくれよ!", - "惜しい!", - "お願い、私には家族がいます!", - "私は死ぬには若すぎます!", - "話し合いませんか?", - "暴力は解決策ではないぞ!", - "今日は最悪の日だ…", - "おい、痛いって!", - "えっ!", - "失礼な!", - "止めてくださいよ!", - "毒を食わわば皿まで!", - "それは楽しくないです。", - "よくもそんなことを!", - "この代償は大きいぞ!", - "そんな調子だと後悔するぞ!", - "痛い目に合わせないでくれよ!", - "なにか勘違いしていないか!", - "こんなことするなよ!", - "悪魔よ出ていけ!", - "めっちゃ痛い!", - "なぜこんなことをするの?", - "霊よ、静まれ!", - "誰かと勘違いしてるんじゃないか!", - "私にはもったいないです!", - "二度としないで。", - "衛兵、この化け物を湖に沈めて!", - "俺のタラスクを送り込んでやるぜ!", - "なぜ、わたしがー?", - ], } ) diff --git a/assets/voxygen/i18n/ja_JP/buff.ron b/assets/voxygen/i18n/ja_JP/buff.ron index da5885664c..1cb53bbdd0 100644 --- a/assets/voxygen/i18n/ja_JP/buff.ron +++ b/assets/voxygen/i18n/ja_JP/buff.ron @@ -26,7 +26,7 @@ "buff.desc.cursed": "あなたは呪われた。", // Buffs stats "buff.stat.health": "体力を{str_total}回復", - "buff.stat.increase_max_stamina": "スタミナ最大値を{strength}上げる", + "buff.stat.increase_max_energy": "スタミナ最大値を{strength}上げる", "buff.stat.increase_max_health": "体力最大値を{strength}上げる", "buff.stat.invulnerability": "不死身になる", // Text diff --git a/assets/voxygen/i18n/ja_JP/hud/bag.ron b/assets/voxygen/i18n/ja_JP/hud/bag.ron index 5492894b4d..e25c97ff1c 100644 --- a/assets/voxygen/i18n/ja_JP/hud/bag.ron +++ b/assets/voxygen/i18n/ja_JP/hud/bag.ron @@ -26,7 +26,7 @@ "hud.bag.offhand": "非利き手", "hud.bag.bag": "バッグ", "hud.bag.health": "体力", - "hud.bag.stamina": "スタミナ", + "hud.bag.energy": "スタミナ", "hud.bag.combat_rating": "戦闘レート", "hud.bag.protection": "防御力", "hud.bag.combat_rating_desc": "装備と体力から\n計算されます", diff --git a/assets/voxygen/i18n/ja_JP/hud/hud_settings.ron b/assets/voxygen/i18n/ja_JP/hud/settings.ron similarity index 98% rename from assets/voxygen/i18n/ja_JP/hud/hud_settings.ron rename to assets/voxygen/i18n/ja_JP/hud/settings.ron index c9bb53334e..10d7ee3d21 100644 --- a/assets/voxygen/i18n/ja_JP/hud/hud_settings.ron +++ b/assets/voxygen/i18n/ja_JP/hud/settings.ron @@ -15,7 +15,7 @@ "hud.settings.relative_scaling": "相対的スケール", "hud.settings.custom_scaling": "カスタムスケール", "hud.settings.crosshair": "十字線", - "hud.settings.transparency": "透明度", + "hud.settings.opacity": "透明度", "hud.settings.hotbar": "ホットバー", "hud.settings.toggle_shortcuts": "ショートカット表示切り替え", "hud.settings.buffs_skillbar": "Buffs at Skillbar", @@ -33,7 +33,7 @@ "hud.settings.values": "数値", "hud.settings.percentages": "パーセント", "hud.settings.chat": "チャット", - "hud.settings.background_transparency": "背景透明度", + "hud.settings.background_opacity": "背景透明度", "hud.settings.chat_character_name": "チャット内にキャラクター名表示", "hud.settings.loading_tips": "ロード画面のヒント表示", "hud.settings.reset_interface": "デフォルトに戻す", diff --git a/assets/voxygen/i18n/ja_JP/skills.ron b/assets/voxygen/i18n/ja_JP/hud/skills.ron similarity index 97% rename from assets/voxygen/i18n/ja_JP/skills.ron rename to assets/voxygen/i18n/ja_JP/hud/skills.ron index 6f94dc94f4..1ce14d2843 100644 --- a/assets/voxygen/i18n/ja_JP/skills.ron +++ b/assets/voxygen/i18n/ja_JP/hud/skills.ron @@ -11,8 +11,8 @@ // General "hud.skill.inc_health_title": "体力追加", "hud.skill.inc_health": "体力の最大値を{boost}増加 {SP}", - "hud.skill.inc_stam_title": "スタミナ追加", - "hud.skill.inc_stam": "スタミナ最大値を{boost}増加 {SP}", + "hud.skill.inc_energy_title": "スタミナ追加", + "hud.skill.inc_energy": "スタミナ最大値を{boost}増加 {SP}", "hud.skill.unlck_sword_title": "剣解除", "hud.skill.unlck_sword": "剣のスキルツリーを解除 {SP}", "hud.skill.unlck_axe_title": "斧解除", @@ -27,8 +27,8 @@ "hud.skill.unlck_sceptre": "セプターのスキルツリーを解除 {SP}", "hud.skill.dodge_title": "回避", "hud.skill.dodge": "近接攻撃を素早く避ける {SP}", - "hud.skill.roll_stamina_title": "ローリング スタミナコスト", - "hud.skill.roll_stamina": "ローリングのスタミナ消費を{boost}%減少 {SP}", + "hud.skill.roll_energy_title": "ローリング スタミナコスト", + "hud.skill.roll_energy": "ローリングのスタミナ消費を{boost}%減少 {SP}", "hud.skill.roll_speed_title": "ローリング 速度", "hud.skill.roll_speed": "ローリングの速度を{boost}%加速 {SP}", "hud.skill.roll_dur_title": "ローリング 時間", @@ -95,8 +95,8 @@ "hud.skill.st_flamethrower_damage" : "ダメージを{boost}%増加 {SP}", "hud.skill.st_explosion_radius_title" : "エクスプロージョン 範囲", "hud.skill.st_explosion_radius" : "大きいのはいいですよね。爆発半径を{boost}%増加 {SP}", - "hud.skill.st_stamina_regen_title" : "スタミナ 再生", - "hud.skill.st_stamina_regen" : "スタミナ獲得量を{boost}%増加 {SP}", + "hud.skill.st_energy_regen_title" : "スタミナ 再生", + "hud.skill.st_energy_regen" : "スタミナ獲得量を{boost}%増加 {SP}", "hud.skill.st_fireball_title" : "ファイアーボール", "hud.skill.st_fireball" : "敵と一緒に遊びましょう", "hud.skill.st_damage_title" : "ダメージ", diff --git a/assets/voxygen/i18n/ja_JP/main.ron b/assets/voxygen/i18n/ja_JP/main.ron index 096dfcb786..0bcf76aabc 100644 --- a/assets/voxygen/i18n/ja_JP/main.ron +++ b/assets/voxygen/i18n/ja_JP/main.ron @@ -64,5 +64,26 @@ https://veloren.net/account/"#, vector_map: { + "loading.tips": [ + "Gキーを押すとランタンが灯ります。", + "F1キーを押すとすべてのデフォルトキー設定の確認できます。", + "/sayまたは/sと入力すると自分の周りのプレイヤーとだけチャットできます。", + "/regionまたは/rと入力すると自分の周辺、数百ブロックのプレーヤーとだけチャットできます。", + "管理者は、/buildコマンドでビルドモードに入れます。", + "/groupまたは/gと入力すると現在所属しているグループのプレイヤーとだけチャットできます。", + "プライベートメッセージを送るには、/tellと入力したあと、プレイヤー名とメッセージを入力します。", + "世界中に散らばっている食料やチェストに注目してみましょう!", + "持ち物が食料でいっぱいですか?それを使ってもっといい食料をクラフトしてみましょう!", + "何をしたらいいかわからないですか?地図にあるダンジョンに挑戦してみましょう!", + "お使いのシステムに合わせてグラフィックの調整もお忘れなく。Nキーを押すと設定が開きます。", + "誰かと一緒に遊ぶのは楽しいですよね!Oキーを押すと誰がオンラインかわかります。", + "Jキーを押すとダンスします。パーティ!", + "左Shiftキーを押すとグライダーが開いて空を攻略できます。", + "Velorenは、まだアルファ版です。私たちは日々、改善のためにベストを尽くしています!", + "開発チームに参加したい方や私たちとチャットしたい方はDiscordサーバーに参加しましょう。", + "体力バーに表示される体力量の表示は、設定で切り替えられます。", + "Kキーを押して焚き火のそばに座ると体力がゆっくり回復します", + "冒険のためにバッグやいい鎧が欲しいですか?Cキーを押すとクラフトメニューが開きます。", + ], } ) diff --git a/assets/voxygen/i18n/ja_JP/npc.ron b/assets/voxygen/i18n/ja_JP/npc.ron new file mode 100644 index 0000000000..d0b5e8da72 --- /dev/null +++ b/assets/voxygen/i18n/ja_JP/npc.ron @@ -0,0 +1,178 @@ +/// 警告: ローカライズファイルは、UTF-8のBOM無しで保存する必要があります + +/// Localization for 日本語 Japanese +( + string_map: { + + }, + + vector_map: { + "npc.speech.villager": [ + "こんな素敵な日があってもいいよね?", + "ご機嫌いかが?", + "最高の朝を迎えましょう!", + "カトブレパスは なにを考えて草を食べてるのだろう", + "この天気についてどうお考えですか?", + "あのダンジョンの事を考えると怖くなります。誰かがあそこをクリアしてくれないだろうか", + "強くなったら洞窟に探検に行きたいですね", + "私の猫チャンを見かけなかった?", + "ランドシャークという獰猛なサメをご存知ですか?なんでも砂漠に住んでるらしいですよ", + "洞窟には さまざまな種類の光り輝く宝石が見つかるらしいですよ", + "やっぱ チーズにはクラッカー!", + "寄っていきませんか?ちょうどチーズを食べようとしてたところなんです", + "マッシュルームは健康に良いと言われてます。自分は食べないですけど", + "クラッカーを忘れるな!", + "私はシンプルにドワーフチーズに憧れてます。作れたらなぁ", + "山の向こう側には何があるのだろう", + "いつの日か 自分のグライダーを作ってみたいですね", + "私の庭を見ていかない?うん また今度ね", + "森を散歩するには なんて素敵な日なんだ!", + "なるべきか ならざるべきか… 僕は農家にだけど", + "私たちの村が一番だと思わない?", + "なんで光る死体は光っているんだろう?", + "二度目の朝ゴハンの時間だよ!", + "ホタルを捕まえたことはある?", + "あのサウロクたちが どこから来ているのか想像できませんね", + "誰か狼を村から遠ざけてくれればいいのに", + "昨晩 素敵なチーズの夢を見ました。それってどういう意味なの?", + "兄にチーズを残してきました。でも 今となってはそれが存在するのかわかりません。だから 私は「シュレディンガーのチーズ」と呼んでいます", + "妹にチーズを残してきました。でも 今となってはそれが存在するのかわかりません。だから 私は「シュレディンガーのチーズ」と呼んでいます", + "誰かが あのカルト教団をなんとかしないといけない。私は やりたくないけど", + "早く雨が降ってほしい。雨は作物にいいからね", + "ハチミツ大好き! でも ハチは大嫌い!", + "私はいつか世界を見てみたい。この村よりももっと豊かな人生があるはずだから", + ], + "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": [ + "助けて、襲撃されてます!", + "助けて!襲われてます!", + "ああっ!襲われてます!", + "ああっ!襲撃されてます!助けて!", + "助けてください!襲われてます!", + "襲われています!助けて!", + "襲われています!助けてください!", + "助けて!", + "助けて!助けて!", + "助けて!助けて!助けて!", + "襲われています!", + "あああああ!襲われてます!", + "あああああ!襲撃されてます!助けて!", + "助けて!私たちは襲撃されてます!", + "助けて!人殺し!", + "助けて!人殺しが逃亡しています!", + "助けて!私を殺そうとしています!", + "衛兵さん、襲われています!", + "衛兵!襲われてます!", + "襲われています!衛兵!", + "助けて!衛兵!襲われてます!", + "衛兵!早く来て!", + "衛兵!衛兵!", + "衛兵!悪党に襲ってきた!", + "衛兵、うす汚い悪党を切って!", + "衛兵!人殺しがいる!", + "衛兵!助けてください!", + "このままでは許さないぞ!衛兵!", + "この、悪魔め!", + "助けてください!", + "助けて!お願い!", + "ああっ!衛兵!助けて!", + "狙われてます!", + "助けて!助けて!私は抑圧されてる!", + "ああ、システムに内在する暴力が見えてきた。", + "ただのかすり傷だ!", + "止めてくれ!", + "お前に何をしたっていうんだよ!", + "お願いですから、攻撃を止めてください!", + "ヘイ!どこに向けてるんだ!", + "凶暴な奴らは消えてしまえ!", + "止まれ!出ていけ!", + "私は怒っている!", + "おい!!お前は何様だと思ってるんだ!", + "お前の首を取ってやるぜ!", + "止めて、お願い!金目のものは持ってないんだ!", + "兄貴を連れてくるぞ、俺よりデカいんだからな!", + "ノー!母ちゃんにいいつけてやる!", + "呪ってやる!", + "お願いだから止めてください。", + "ひでえな!", + "武器は使ったんだから、もうしまってくれよ!", + "惜しい!", + "お願い、私には家族がいます!", + "私は死ぬには若すぎます!", + "話し合いませんか?", + "暴力は解決策ではないぞ!", + "今日は最悪の日だ…", + "おい、痛いって!", + "えっ!", + "失礼な!", + "止めてくださいよ!", + "毒を食わわば皿まで!", + "それは楽しくないです。", + "よくもそんなことを!", + "この代償は大きいぞ!", + "そんな調子だと後悔するぞ!", + "痛い目に合わせないでくれよ!", + "なにか勘違いしていないか!", + "こんなことするなよ!", + "悪魔よ出ていけ!", + "めっちゃ痛い!", + "なぜこんなことをするの?", + "霊よ、静まれ!", + "誰かと勘違いしてるんじゃないか!", + "私にはもったいないです!", + "二度としないで。", + "衛兵、この化け物を湖に沈めて!", + "俺のタラスクを送り込んでやるぜ!", + "なぜ、わたしがー?", + ], + } +) diff --git a/assets/voxygen/i18n/ja_JP/template.ron b/assets/voxygen/i18n/ja_JP/template.ron deleted file mode 100644 index 02abc19286..0000000000 --- a/assets/voxygen/i18n/ja_JP/template.ron +++ /dev/null @@ -1,12 +0,0 @@ -/// 警告: ローカライズファイルは、UTF-8のBOM無しで保存する必要があります - -/// Localization for 日本語 Japanese -( - string_map: { - - }, - - - vector_map: { - } -) diff --git a/assets/voxygen/i18n/nl/_manifest.ron b/assets/voxygen/i18n/nl/_manifest.ron deleted file mode 100644 index b6324abc02..0000000000 --- a/assets/voxygen/i18n/nl/_manifest.ron +++ /dev/null @@ -1,620 +0,0 @@ -/// 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" English -( - metadata: ( - language_name: "Nederlands", - language_identifier: "nl", - ), - convert_utf8_to_ascii: false, - fonts: { - "opensans": Font ( - asset_key: "voxygen.font.OpenSans-Regular", - scale_ratio: 1.0, - ), - "metamorph": Font ( - asset_key: "voxygen.font.Metamorphous-Regular", - scale_ratio: 1.0, - ), - "alkhemi": Font ( - asset_key: "voxygen.font.Alkhemikal", - scale_ratio: 1.0, - ), - "wizard": Font ( - asset_key: "voxygen.font.wizard", - scale_ratio: 1.0, - ), - "cyri": Font ( - asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", - scale_ratio: 1.0, - ), - }, - string_map: { - /// Start Common section - // Texts used in multiple locations with the same formatting - "common.username": "Spelernaam", - "common.singleplayer": "Alleen spelen", - "common.multiplayer": "Samen spelen", - "common.servers": "Servers", - "common.quit": "Afsluiten", - "common.settings": "Instellingen", - "common.languages": "Talen", - "common.interface": "Interface", - "common.gameplay": "Gameplay", - "common.controls": "Besturing", - "common.video": "Grafische weergave", - "common.sound": "Geluid", - "common.languages": "Talen", - "common.resume": "Verder spelen", - "common.characters": "Karakters", - "common.close": "Sluiten", - "common.yes": "Ja", - "common.no": "Nee", - "common.back": "Terug", - "common.create": "Aanmaken", - "common.okay": "Oké", - "common.accept": "Aanvaarden", - "common.decline": "Afwijzen", - "common.disclaimer": "Disclaimer", - "common.cancel": "Annuleren", - "common.none": "Geen", - "common.error": "Fout", - "common.fatal_error": "Fatale fout", - "common.you": "Jij", - "common.automatic": "Auto", - "common.random": "Willekeurig", - // Settings Window title - "common.interface_settings": "Interface Instellingen", - "common.gameplay_settings": "Gameplay Instellingen", - "common.controls_settings": "Besturing Instellingen", - "common.video_settings": "Grafische instellingen", - "common.sound_settings": "Geluids instellingen", - "common.language_settings": "Taal instellingen", - - // Message when connection to the server is lost - "common.connection_lost": r#"Verbinding verloren! -Is de server opniew opgestart? -Is je client nog up to date?"#, - - - "common.species.orc": "Orc", - "common.species.human": "Mens", - "common.species.dwarf": "Dwerg", - "common.species.elf": "Elf", - "common.species.undead": "Ondood", - "common.species.danari": "Danari", - - "common.weapons.axe": "Bijl", - "common.weapons.sword": "Zwaard", - "common.weapons.staff": "Staf", - "common.weapons.bow": "Boog", - "common.weapons.hammer": "Hamer", - "common.weapons.sceptre": "Genezende scepter", - "common.rand_appearance": "Willekeurig uiterlijk en naam", - /// End Common section - - - /// Start Main screen section - "main.connecting": "Verbinden", - "main.creating_world": "Wereld aan het maken", - "main.tip": "Tip:", - - // Welcome notice that appears the first time Veloren is started - "main.notice": r#"Welkom bij de Alpha versie van Veloren! - -Voordat de fun begint zijn hier enkele dingen om rekening mee te houden: - -- Dit is een hele vroege alpha. Verwacht bugs, extreem onafgemaakte gameplay, ruwe spelmechanics en missende functies. - -- Als je constructieve feedback hebt of bugs wil melden, kan je ons contacteren via Reddit, Gitlab of onze community Discord. - -- Veloren is onder de GPL 3 open-source licentie gelicensieerd. Dat betekent dat je vrij bent om het spel te spelen, bewerken en doorgeven op welke manier -je ook wil (zo lang afgeleide werken ook onder GPL 3 gelicensieerd zijn) - -- Veloren is een non-profit community project, en iedereen dat er aan meehelpt is een vrijwilliger. -Als je interesse hebt kan je je aansluiten bij onze ontwikkeling- of ontwerpteams! - -Bedankt voor de tijd te nemen om deze melding te lezen, we hopen dat je van het spel zult genieten! -Thanks for taking the time to read this notice, we hope you enjoy the game! - -~ De Veloren ontwikkelaars"#, - - // Login process description - "main.login_process": r#"Information over het loginproces: - -Hou er rekening mee dat je tegenwoordig een account nodig hebt -om op servers met authenticatie te spelen - -Je kan een account maken op - -https://veloren.net/account/."#, - "main.login.server_not_found": "Server nie gevonden", - "main.login.authentication_error": "Authenticatie mislukt", - "main.login.server_full": "Server zit vol", - "main.login.untrusted_auth_server": "Onvertrouwde authenticatie server", - "main.login.outdated_client_or_server": "Versieprobleem: De versie tussen de client en de server komen niet overeen. Check of er updates zijn/", - "main.login.timeout": "Timeout: De server deed er te lang over om te antwoorden. (Overblast of netwerk problemen).", - "main.login.server_shut_down": "Server is afgesloten", - "main.login.network_error": "Network problemen", - "main.login.failed_sending_request": "Kon verzoek niet naar authenticatie server sturen", - "main.login.invalid_character": "Het geselecteerde karakter is ongeldig", - "main.login.client_crashed": "Client is gecrashed", - "main.login.not_on_whitelist": "Je moet op de whitelist staan om deze wereld te joinen", - "main.login.banned": "Je bent verbannen voor de volgende reden", - "main.login.kicked": "Je bent gekicked voor de volgende reden", - "main.login.select_language": "Kies een taal", - - - /// End Main screen section - - - /// Start HUD Section - "hud.do_not_show_on_startup": "Niet meer bij opstarten tonen", - "hud.show_tips": "Tips tonen", - "hud.quests": "Quests", - "hud.you_died": "Je bent gedood", - "hud.waypoint_saved": "Waypoint Opgeslagen", - - "hud.press_key_to_show_keybindings_fmt": "[{key}] Besturing", - "hud.press_key_to_toggle_lantern_fmt": "[{key}] Lantaarn", - "hud.press_key_to_show_debug_info_fmt": "Druk op {key} om debug info te tonen", - "hud.press_key_to_toggle_keybindings_fmt": "Druk op {key} om te de besturing aan/uit te zetten", - "hud.press_key_to_toggle_debug_info_fmt": "Druk op {key} om debug info aan/uit te zetten", - - // Chat outputs - "hud.chat.online_msg": "[{name}] is nu online", - "hud.chat.offline_msg": "[{name}] is offline gegaan", - - "hud.chat.default_death_msg": "[{name}] is gestorven", - "hud.chat.environmental_kill_msg": "[{name}] is gestorven in {environment}", - "hud.chat.fall_kill_msg": "[{name}] is gestorven door te vallen", - "hud.chat.suicide_msg": "[{name}] is gestorven door zelf toegebrachte verwondingen", - - "hud.chat.pvp_melee_kill_msg": "[{attacker}] versloeg [{victim}]", - "hud.chat.pvp_ranged_kill_msg": "[{attacker}] schoot [{victim}] neer", - "hud.chat.pvp_explosion_kill_msg": "[{attacker}] blies [{victim}] op", - "hud.chat.pvp_energy_kill_msg": "[{attacker}] gebruikte magie om [{victim}] te vermoorden", - - "hud.chat.npc_melee_kill_msg": "{attacker} dode [{victim}]", - "hud.chat.npc_ranged_kill_msg": "{attacker} schoot [{victim}]", - "hud.chat.npc_explosion_kill_msg": "{attacker} blies [{victim}] op", - - "hud.chat.loot_msg": "Je raapte [{item}] op", - "hud.chat.loot_fail": "Jouw inventory is vol!", - "hud.chat.goodbye": "Vaarwel!", - "hud.chat.connection_lost": "Verbinding verloren. Je wordt in {time} seconden gekicked.", - - // SCT outputs - "hud.sct.experience": "{amount} Exp", - "hud.sct.block": "GEBLOCKED", - - // Respawn message - "hud.press_key_to_respawn": r#"Druk op {key} om te respawnen bij het laatste kampvuur dat je bezocht."#, - - // Welcome message - "hud.welcome": r#"Welcome bij de Veloren Alpha! - -Wat tips voor je start: - - -Druk op F1 om de besturing te zien. - -Type /help in chat om de chat commando's te zien. - - -Er verschijnen kisten en andere objecten willekeurig in de wereld! - -Gebruik rechtermuisknop om ze te verzamelen! - -Om effectief hetgene dat je van de kisten krijgt te gebruiken, open je je inventory met 'B'. - -Dubbelklik op de voorwerpen in je rugzak om ze aan te doen of te gebruiken. - -Gooi dingen weg door ze eenmaal aan te klikken en dan buiten je rugzak te klikken. - - -Nachten kunnen vrij donker worden in Veloren. - -Gebruik je lantaarn door 'G' te drukken - - -Wil je je cursor gebruiken om dit venster te sluiten? Druk TAB! - - -Geniet van je verblijf in de wereld van Veloren."#, - -"hud.temp_quest_headline": r#"Help ons alsjeblieft, reiziger!"#, -"hud.temp_quest_text": r#"Kerkers vol met kwaaraardige sektes -komen overal tevoorschijn rond onze vreedzame dorpen! - - -Verzamel wat gezelschap, vul je voedselrantsoenen aan -en versla hun kwaadaardige leiders en volgelingen. - - -Misschien kan je zelfs een van hun -Maybe you can even obtain one of their -met magie gevulde objecten bemachtigen?"#, - - - - // Inventory - "hud.bag.inventory": "{playername}'s Rugzak", - "hud.bag.stats_title": "{playername}'s Stats", - "hud.bag.exp": "Exp", - "hud.bag.armor": "Armor", - "hud.bag.stats": "Stats", - "hud.bag.head": "Hoofd", - "hud.bag.neck": "Nek", - "hud.bag.tabard": "Tabberd", - "hud.bag.shoulders": "Schouders", - "hud.bag.chest": "Borstkas", - "hud.bag.hands": "Handen", - "hud.bag.lantern": "Lantaarn", - "hud.bag.glider": "Deltavlieger", - "hud.bag.belt": "Riem", - "hud.bag.ring": "Ring", - "hud.bag.back": "Rug", - "hud.bag.legs": "Benen", - "hud.bag.feet": "Voeten", - "hud.bag.mainhand": "Dominante hand", - "hud.bag.offhand": "Tweede hand", - - - // Map and Questlog - "hud.map.map_title": "Kaart", - "hud.map.qlog_title": "Quests", - - // Settings - "hud.settings.general": "Algemeen", - "hud.settings.none": "Geen", - "hud.settings.press_behavior.toggle": "Omschakelbaar", - "hud.settings.press_behavior.hold": "Inhouden", - "hud.settings.help_window": "Help Venster", - "hud.settings.debug_info": "Debug Informatie", - "hud.settings.tips_on_startup": "Tips-Bij-Opstarten", - "hud.settings.ui_scale": "UI-Schaal", - "hud.settings.relative_scaling": "Relatief Schalen", - "hud.settings.custom_scaling": "Aangepast Schalen", - "hud.settings.crosshair": "Richtkruis", - "hud.settings.transparency": "Doorschijnbaarheid", - "hud.settings.hotbar": "Hotbar", - "hud.settings.toggle_shortcuts": "Toon Sneltoetsen", - "hud.settings.toggle_bar_experience": "Toon Experience Balk", - "hud.settings.scrolling_combat_text": "Pop-up Gevechtstext", - "hud.settings.single_damage_number": "Aparte Schade Getallen", - "hud.settings.cumulated_damage": "Cumulatieve Schade Getallen", - "hud.settings.incoming_damage": "Inkomende Schade Getallen", - "hud.settings.cumulated_incoming_damage": "Cumulatieve Inkomende Schade Getallen", - "hud.settings.speech_bubble": "Spraakbubbel", - "hud.settings.speech_bubble_dark_mode": "Donkere Modus", - "hud.settings.speech_bubble_icon": "Spraakbubbel Icoon", - "hud.settings.energybar_numbers": "Energiebalk Getallen", - "hud.settings.values": "Waarden", - "hud.settings.percentages": "Percentages", - "hud.settings.chat": "Chat", - "hud.settings.background_transparency": "Transparantie Achtergrond", - "hud.settings.chat_character_name": "Karakternamen in de Chat", - "hud.settings.loading_tips": "Laadscherm Tips", - - "hud.settings.pan_sensitivity": "Camera-gevoeligheid", - "hud.settings.zoom_sensitivity": "Zoom-gevoeligheid", - "hud.settings.invert_scroll_zoom": "Inverteer Zoom Scrollen", - "hud.settings.invert_mouse_y_axis": "Inverteer muis Y As", - "hud.settings.enable_mouse_smoothing": "Camerabeweging gelijkmaken", - "hud.settings.free_look_behavior": "Vrije camera gedrag", - "hud.settings.auto_walk_behavior": "Automatisch wandelgedrag", - "hud.settings.stop_auto_walk_on_input": "Stop automatisch -wandelen bij bewegen", - - "hud.settings.view_distance": "Kijkafstand", - "hud.settings.sprites_view_distance": "Kijkafstand sprites", - "hud.settings.figures_view_distance": "Kijkafstand entiteiten", - "hud.settings.maximum_fps": "Maximum FPS", - "hud.settings.fov": "Gezichtsveld (graden)", - "hud.settings.gamma": "Gamma", - "hud.settings.ambiance": "Omgevingshelderheid", - "hud.settings.antialiasing_mode": "AntiAliasing Modus", - "hud.settings.cloud_rendering_mode": "Wolk Rendering Modus", - "hud.settings.fluid_rendering_mode": "Vloeistoffen Rendering Mode", - "hud.settings.fluid_rendering_mode.cheap": "Goedkoop", - "hud.settings.fluid_rendering_mode.shiny": "Schitterend", - "hud.settings.cloud_rendering_mode.regular": "Gewoon", - "hud.settings.fullscreen": "Volledig scherm", - "hud.settings.fullscreen_mode": "Volledige scherm modus", - "hud.settings.fullscreen_mode.exclusive": "Exclusief", - "hud.settings.fullscreen_mode.borderless": "Randloos", - "hud.settings.particles": "Deeltjes", - "hud.settings.resolution": "Resolutie", - "hud.settings.bit_depth": "Bit diepte", - "hud.settings.refresh_rate": "Vernieuwingssnelheid", - "hud.settings.save_window_size": "Venster grootte opslaan", - "hud.settings.lighting_rendering_mode": "Licht Rendering Modus", - "hud.settings.lighting_rendering_mode.ashikhmin": "Type A", - "hud.settings.lighting_rendering_mode.blinnphong": "Type B", - "hud.settings.lighting_rendering_mode.lambertian": "Type L", - "hud.settings.shadow_rendering_mode": "Schaduw Rendering Mode", - "hud.settings.shadow_rendering_mode.none": "Geen", - "hud.settings.shadow_rendering_mode.cheap": "Goedkoop", - "hud.settings.shadow_rendering_mode.map": "Kaart", - "hud.settings.shadow_rendering_mode.map.resolution": "Resolutie", - "hud.settings.lod_detail": "LoD Detail", - "hud.settings.save_window_size": "Venster grootte opslaan", - - - "hud.settings.music_volume": "Muziek Volume", - "hud.settings.sound_effect_volume": "Geluidseffecten Volume", - "hud.settings.audio_device": "Audio Apparaat", - - "hud.settings.awaitingkey": "Druk op een toets...", - "hud.settings.unbound": "Geen", - "hud.settings.reset_keybinds": "Standaardwaarden -herstellen", - - "hud.social": "Andere spelers", - "hud.social.online": "Online:", - "hud.social.friends": "Vrienden", - "hud.social.not_yet_available": "Nog niet beschikbaar", - "hud.social.faction": "Factie", - "hud.social.play_online_fmt": "{nb_player} speler(s) online", - "hud.social.name": "Naam", - "hud.social.level": "Level", - "hud.social.zone": "Zone", - "hud.social.account": "Account", - - - "hud.crafting": "Fabriceren", - "hud.crafting.recipes": "Recepten", - "hud.crafting.ingredients": "Ingrediënten:", - "hud.crafting.craft": "Maak", - "hud.crafting.tool_cata": "Vereisten:", - - "hud.group": "Groep", - "hud.group.invite_to_join": "[{name}] heeft je voor zijn/haar groep uitgenodigd!", - "hud.group.invite": "Uitnodigen", - "hud.group.kick": "Kick", - "hud.group.assign_leader": "Maak Leider", - "hud.group.leave": "Groep Verlaten", - "hud.group.dead" : "Dood", - "hud.group.out_of_range": "Buiten Bereik", - "hud.group.add_friend": "Aan Vrienden Toevoegen", - "hud.group.link_group": "Link Groups", - "hud.group.in_menu": "In Menu", - "hud.group.members": "Groepsleden", - - "hud.spell": "Spreuken", - - "hud.free_look_indicator": "Vrij rondkijken aan. Druk {key} om uit te zetten.", - "hud.auto_walk_indicator": "Automatisch wandelen aan", - - /// End HUD section - - - /// Start GameInput section - - "gameinput.primary": "Gewone aanval", - "gameinput.secondary": "Alternatieve Aanval/Block/Mikken", - "gameinput.slot1": "Hotbar Slot 1", - "gameinput.slot2": "Hotbar Slot 2", - "gameinput.slot3": "Hotbar Slot 3", - "gameinput.slot4": "Hotbar Slot 4", - "gameinput.slot5": "Hotbar Slot 5", - "gameinput.slot6": "Hotbar Slot 6", - "gameinput.slot7": "Hotbar Slot 7", - "gameinput.slot8": "Hotbar Slot 8", - "gameinput.slot9": "Hotbar Slot 9", - "gameinput.slot10": "Hotbar Slot 10", - "gameinput.swaploadout": "Uitrusting Wisselen", - "gameinput.togglecursor": "Cursor Aan/Uit", - "gameinput.help": "Help Venster Aan/Uit", - "gameinput.toggleinterface": "Interface Aan/Uit", - "gameinput.toggledebug": "FPS en Debug Info Aan/Uit", - "gameinput.screenshot": "Screenshot maken", - "gameinput.toggleingameui": "Naamplaatjes Aan/Uit", - "gameinput.fullscreen": "Volledig Scherm Aan/Uit", - "gameinput.moveforward": "Voorwaards Bewegen", - "gameinput.moveleft": "Links Bewegen", - "gameinput.moveright": "Rechts Bewegen", - "gameinput.moveback": "Achteruit Bewegen", - "gameinput.jump": "Springen", - "gameinput.glide": "Deltavlieger", - "gameinput.roll": "Rollen", - "gameinput.climb": "Klimmen", - "gameinput.climbdown": "Naar Beneden Klimmen", - "gameinput.wallleap": "Muursprong", - "gameinput.togglelantern": "Lantaarn Aan/Uit", - "gameinput.mount": "Berijden", - "gameinput.chat": "Chat", - "gameinput.command": "Commando", - "gameinput.escape": "Sluiten", - "gameinput.map": "Kaart", - "gameinput.bag": "Rugzak", - "gameinput.social": "Sociaal", - "gameinput.sit": "Zit", - "gameinput.spellbook": "Spreuken", - "gameinput.settings": "Instellingen", - "gameinput.respawn": "Respawn", - "gameinput.charge": "Opladen", - "gameinput.togglewield": "Wapen Vastnemen/Wegsteken", - "gameinput.interact": "Gebruik", - "gameinput.freelook": "Vrije Camera", - "gameinput.autowalk": "Automatisch Wandelen", - "gameinput.dance": "Dans", - "gameinput.select": "Selecteer Entiteit", - "gameinput.acceptgroupinvite": "Accepteer Groepsuitnodiging", - "gameinput.declinegroupinvite": "Wijs Groepsuitnodiging Af", - "gameinput.crafting": "Fabriceermenu", - "gameinput.sneak": "Sluipen", - "gameinput.swimdown": "Naar Beneden Zwemmen", - "gameinput.swimup": "Naar Boven Zwemmen", - - /// End GameInput section - - - /// Start chracter selection section - "char_selection.loading_characters": "Karakters Worden Geladen...", - "char_selection.delete_permanently": "Karakter Permanent Verwijderen?", - "char_selection.deleting_character": "Karakter Wordt Verwijderen...", - "char_selection.change_server": "Server Wisselen", - "char_selection.enter_world": "Wereld Betreden", - "char_selection.logout": "Uitloggen", - "char_selection.create_new_charater": "Nieuw Karakter Maken", - "char_selection.creating_character": "Karakter Wordt Aangemaakt...", - "char_selection.character_creation": "Karaktercreatie", - - "char_selection.human_default": "Standaard Mens", - "char_selection.level_fmt": "Level {level_nb}", - "char_selection.uncanny_valley": "Wildernis", - "char_selection.plains_of_uncertainty": "Vlaktes van Onzekerheid", - "char_selection.beard": "Baard", - "char_selection.hair_style": "Haarstijl", - "char_selection.hair_color": "Haarkleur", - "char_selection.eye_color": "Oogkleur", - "char_selection.skin": "Huid", - "char_selection.eyeshape": "Oogdetails", - "char_selection.accessories": "Accessoires", - "char_selection.create_info_name": "Je karakter heeft een naam nodig!", - - /// End chracter selection section - - - /// Start character window section - "character_window.character_name": "Karakter Naam", - // Character stats - "character_window.character_stats": r#"Uithouding - -Fitheid - -Willskracht - -Bescherming -"#, - /// End character window section - - - /// Start Escape Menu Section - "esc_menu.logout": "Uitloggen", - "esc_menu.quit_game": "Spel Verlaten", - /// End Escape Menu Section - - }, - - - vector_map: { - "loading.tips": [ - "Druk op 'G' om je lantaarn te doen schijnen.", - "Druk op 'F1' om alle standaardbesturingen te zien.", - "Je kan /say of /s typen om enkel met spelers rondom je te praten.", - "Je kan /region of /r typen om enkel met spelers binnen een hondertal blokken te praten.", - "Je kan /group of /g typen om enkel met je groepsleden te praten.", - "Om privéberichten te sturen gebruik je /tell gevolgd door de speler zijn naam en dan je bericht", - "NPCs met hetzelfde level zijn niet per sé altijd even sterk.", - "Hou je ogen open voor eten, kisten en andere buit verspreid over de wereld!", - "Zit je Inventory vol met eten? Probeer beter eten er van te craften!", - "Weet je niet wat te doen? Kerkers zijn met een bruine vlek op je kaart aangeduid!", - "Vergeet je grafische instellingen niet aan te passen naar je systeem. Druk 'N' om het instellingenmenu te openen.", - "Met anderen spelen is plezanter! Druk op 'O' om te zien wie er nog online is!", - "Een NPC met een doodshoofd onder zijn levensbalk is zeer sterk vergeleken met jezelf.", - "Druk op 'J' om te dansen. Feestje!", - "Druk op 'L-Shift' om je deltavlieger te openen en het luchtruim te veroveren.", - "Veloren is nog steeds in Pre-Alpha. We doen ons best om het elke dag te verbeteren!", - "Als je het ontwikkelingsteam wil versterken of gewoon een babbeltje wil doen met ons, kom dan langs op onze Discord-Server.", - "Je kan numerieke waarden in de levensbalk aan of uit zetten in de instellingen.", - "Om je stats te zien kan je op 'Stats' klikken in je Inventory.", - ], - "npc.speech.villager_under_attack": [ - "Help, Ik word aangevallen!", - "Help! Ik word aangevallen!", - "Ow! Ik word aangevallen!", - "Ow! Ik word aangevallen! Help!", - "Help me! Ik word aangevallen!", - "Ik word aangevallen! Help!", - "Ik word aangevallen! Help me!", - "Help!", - "Help! Help!", - "Help! Help! Help!", - "Ik word aangevallen!", - "AAAHHH! Ik word aangevallen!", - "AAAHHH! Ik word aangevallen! Help!", - "Help! We worden aangevallen!", - "Help! Moordenaar!", - "Help! Er loopt een moordenaar rond!", - "Help! Ze proberen mij te vermoorden!", - "Wachters, Ik word aangevallen!", - "Wachters! Ik word aangevallen!", - "Ik word aangevallen! Wachters!", - "Help! Wachters! Ik word aangevallen!", - "Wachters! Kom snel!", - "Wachters! Wachters!", - "Wachters! Er is een snodaard mij aan het aanvallen!", - "Wachters, slacht deze kwaadaardige snodaard!", - "Wachters! Er is een moordenaar!", - "Wachters! Help me!", - "Hier kom je niet mee weg! Wachters!", - "Jij boosdoener!", - "Help me!", - "Help! Alsjeblieft!", - "Ow! Wachters! Help!", - "Ze zitten achter me aan!", - "Help! Help! Ik word onderdrukt!", - "Ah, nu zien we het geweld eigen aan het systeem.", - "Het is maar een schrammetje!", - "Stop daarmee!", - "Wat heb ik jouw ooit aangedaan?!", - "Alsjeblieft stop met aanvallen!", - "Hey! Pas op waar je dat ding wijst!", - "Gruwelijke ellendeling, wegwezen!", - "Stop ermee! Wegwezen!", - "Nu maak je me boos!", - "Oi! Wie denk je wel niet dat je bent?!", - "Hiervoor ga je boeten!", - "Stop, Alsjeblieft! Ik heb niks waardevol!", - "Ik stuur mijn broer op je af, hij is groter dan ik!", - "Neeeee, ik ga het tegen je mama zeggen!", - "Vervloek u!", - "Alsjeblieft niet doen.", - "Dat was niet erg vriendelijk!", - "Je wapen werkt, je mag het nu wegsteken hoor!", - "Genade!", - "Ik smeek je, ik heb een familie!", - "Ik ben te jong om te sterven!", - "Ik ben te mooi om te sterven!", - "Kunnen we erover praten?", - "Geweld is nooit het antwoord!", - "Vandaag zit vol met tegenslagen...", - "Hey, dat doet pijn!", - "Eek!", - "Hoe onbeleefd!", - "Stop, ik smeek je!", - "Ik wens je de mazelen toe!", - "Dit is niet plezant.", - "Hoe durf je?!", - "Ik zet het je betaald!", - "Blijf zo doorgaan en je zult spijt krijgen!", - "Zorg er niet voor dat ik je moet pijn doen!", - "Er moet een soort van misverstand zijn!", - "Je moet dit niet doen!", - "Vertrek, duivel!", - "Dat deed echt pijn!", - "Waarom doe je dit?", - "Bij alles dat heilig is, stop!", - "Je verward mij met iemand anders!", - "Ik verdien dit niet!", - "Doe dat alsjeblieft niet meer.", - "Wachters, gooi dit monster het meer in!", - "Ik laat mijn Tarasque op je los!", - ], - } -) diff --git a/assets/voxygen/i18n/nl_NL/_manifest.ron b/assets/voxygen/i18n/nl_NL/_manifest.ron new file mode 100644 index 0000000000..960e6999b9 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/_manifest.ron @@ -0,0 +1,44 @@ +/// 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 netherlands Dutch +( + metadata: ( + language_name: "Nederlands", + language_identifier: "nl_NL", + ), + convert_utf8_to_ascii: false, + fonts: { + "opensans": Font ( + asset_key: "voxygen.font.OpenSans-Regular", + scale_ratio: 1.0, + ), + "metamorph": Font ( + asset_key: "voxygen.font.Metamorphous-Regular", + scale_ratio: 1.0, + ), + "alkhemi": Font ( + asset_key: "voxygen.font.Alkhemikal", + scale_ratio: 1.0, + ), + "wizard": Font ( + asset_key: "voxygen.font.wizard", + scale_ratio: 1.0, + ), + "cyri": Font ( + asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", + scale_ratio: 1.0, + ), + } +) diff --git a/assets/voxygen/i18n/nl_NL/buff.ron b/assets/voxygen/i18n/nl_NL/buff.ron new file mode 100644 index 0000000000..c782b41883 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/buff.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/char_selection.ron b/assets/voxygen/i18n/nl_NL/char_selection.ron new file mode 100644 index 0000000000..de347f01a9 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/char_selection.ron @@ -0,0 +1,32 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + "char_selection.loading_characters": "Karakters Worden Geladen...", + "char_selection.delete_permanently": "Karakter Permanent Verwijderen?", + "char_selection.deleting_character": "Karakter Wordt Verwijderen...", + "char_selection.change_server": "Server Wisselen", + "char_selection.enter_world": "Wereld Betreden", + "char_selection.logout": "Uitloggen", + "char_selection.create_new_charater": "Nieuw Karakter Maken", + "char_selection.creating_character": "Karakter Wordt Aangemaakt...", + "char_selection.character_creation": "Karaktercreatie", + + "char_selection.human_default": "Standaard Mens", + "char_selection.level_fmt": "Level {level_nb}", + "char_selection.uncanny_valley": "Wildernis", + "char_selection.plains_of_uncertainty": "Vlaktes van Onzekerheid", + "char_selection.beard": "Baard", + "char_selection.hair_style": "Haarstijl", + "char_selection.hair_color": "Haarkleur", + "char_selection.eye_color": "Oogkleur", + "char_selection.skin": "Huid", + "char_selection.eyeshape": "Oogdetails", + "char_selection.accessories": "Accessoires", + "char_selection.create_info_name": "Je karakter heeft een naam nodig!", + }, + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/common.ron b/assets/voxygen/i18n/nl_NL/common.ron new file mode 100644 index 0000000000..26433554a5 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/common.ron @@ -0,0 +1,71 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + // Texts used in multiple locations with the same formatting + "common.username": "Spelernaam", + "common.singleplayer": "Alleen spelen", + "common.multiplayer": "Samen spelen", + "common.servers": "Servers", + "common.quit": "Afsluiten", + "common.settings": "Instellingen", + "common.languages": "Talen", + "common.interface": "Interface", + "common.gameplay": "Gameplay", + "common.controls": "Besturing", + "common.video": "Grafische weergave", + "common.sound": "Geluid", + "common.languages": "Talen", + "common.resume": "Verder spelen", + "common.characters": "Karakters", + "common.close": "Sluiten", + "common.yes": "Ja", + "common.no": "Nee", + "common.back": "Terug", + "common.create": "Aanmaken", + "common.okay": "Oké", + "common.accept": "Aanvaarden", + "common.decline": "Afwijzen", + "common.disclaimer": "Disclaimer", + "common.cancel": "Annuleren", + "common.none": "Geen", + "common.error": "Fout", + "common.fatal_error": "Fatale fout", + "common.you": "Jij", + "common.automatic": "Auto", + "common.random": "Willekeurig", + // Settings Window title + "common.interface_settings": "Interface Instellingen", + "common.gameplay_settings": "Gameplay Instellingen", + "common.controls_settings": "Besturing Instellingen", + "common.video_settings": "Grafische instellingen", + "common.sound_settings": "Geluids instellingen", + "common.language_settings": "Taal instellingen", + + // Message when connection to the server is lost + "common.connection_lost": r#"Verbinding verloren! +Is de server opniew opgestart? +Is je client nog up to date?"#, + + + "common.species.orc": "Orc", + "common.species.human": "Mens", + "common.species.dwarf": "Dwerg", + "common.species.elf": "Elf", + "common.species.undead": "Ondood", + "common.species.danari": "Danari", + + "common.weapons.axe": "Bijl", + "common.weapons.sword": "Zwaard", + "common.weapons.staff": "Staf", + "common.weapons.bow": "Boog", + "common.weapons.hammer": "Hamer", + "common.weapons.sceptre": "Genezende scepter", + "common.rand_appearance": "Willekeurig uiterlijk en naam", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/esc_menu.ron b/assets/voxygen/i18n/nl_NL/esc_menu.ron new file mode 100644 index 0000000000..68690cd821 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/esc_menu.ron @@ -0,0 +1,13 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + "esc_menu.logout": "Uitloggen", + "esc_menu.quit_game": "Spel Verlaten", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/gameinput.ron b/assets/voxygen/i18n/nl_NL/gameinput.ron new file mode 100644 index 0000000000..4105707186 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/gameinput.ron @@ -0,0 +1,66 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + "gameinput.primary": "Gewone aanval", + "gameinput.secondary": "Alternatieve Aanval/Block/Mikken", + "gameinput.slot1": "Hotbar Slot 1", + "gameinput.slot2": "Hotbar Slot 2", + "gameinput.slot3": "Hotbar Slot 3", + "gameinput.slot4": "Hotbar Slot 4", + "gameinput.slot5": "Hotbar Slot 5", + "gameinput.slot6": "Hotbar Slot 6", + "gameinput.slot7": "Hotbar Slot 7", + "gameinput.slot8": "Hotbar Slot 8", + "gameinput.slot9": "Hotbar Slot 9", + "gameinput.slot10": "Hotbar Slot 10", + "gameinput.swaploadout": "Uitrusting Wisselen", + "gameinput.togglecursor": "Cursor Aan/Uit", + "gameinput.help": "Help Venster Aan/Uit", + "gameinput.toggleinterface": "Interface Aan/Uit", + "gameinput.toggledebug": "FPS en Debug Info Aan/Uit", + "gameinput.screenshot": "Screenshot maken", + "gameinput.toggleingameui": "Naamplaatjes Aan/Uit", + "gameinput.fullscreen": "Volledig Scherm Aan/Uit", + "gameinput.moveforward": "Voorwaards Bewegen", + "gameinput.moveleft": "Links Bewegen", + "gameinput.moveright": "Rechts Bewegen", + "gameinput.moveback": "Achteruit Bewegen", + "gameinput.jump": "Springen", + "gameinput.glide": "Deltavlieger", + "gameinput.roll": "Rollen", + "gameinput.climb": "Klimmen", + "gameinput.climbdown": "Naar Beneden Klimmen", + "gameinput.wallleap": "Muursprong", + "gameinput.togglelantern": "Lantaarn Aan/Uit", + "gameinput.mount": "Berijden", + "gameinput.chat": "Chat", + "gameinput.command": "Commando", + "gameinput.escape": "Sluiten", + "gameinput.map": "Kaart", + "gameinput.bag": "Rugzak", + "gameinput.social": "Sociaal", + "gameinput.sit": "Zit", + "gameinput.spellbook": "Spreuken", + "gameinput.settings": "Instellingen", + "gameinput.respawn": "Respawn", + "gameinput.charge": "Opladen", + "gameinput.togglewield": "Wapen Vastnemen/Wegsteken", + "gameinput.interact": "Gebruik", + "gameinput.freelook": "Vrije Camera", + "gameinput.autowalk": "Automatisch Wandelen", + "gameinput.dance": "Dans", + "gameinput.select": "Selecteer Entiteit", + "gameinput.acceptgroupinvite": "Accepteer Groepsuitnodiging", + "gameinput.declinegroupinvite": "Wijs Groepsuitnodiging Af", + "gameinput.crafting": "Fabriceermenu", + "gameinput.sneak": "Sluipen", + "gameinput.swimdown": "Naar Beneden Zwemmen", + "gameinput.swimup": "Naar Boven Zwemmen", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/hud/bag.ron b/assets/voxygen/i18n/nl_NL/hud/bag.ron new file mode 100644 index 0000000000..8b13457840 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/bag.ron @@ -0,0 +1,32 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + // Inventory + "hud.bag.inventory": "{playername}'s Rugzak", + "hud.bag.stats_title": "{playername}'s Stats", + "hud.bag.exp": "Exp", + "hud.bag.armor": "Armor", + "hud.bag.stats": "Stats", + "hud.bag.head": "Hoofd", + "hud.bag.neck": "Nek", + "hud.bag.tabard": "Tabberd", + "hud.bag.shoulders": "Schouders", + "hud.bag.chest": "Borstkas", + "hud.bag.hands": "Handen", + "hud.bag.lantern": "Lantaarn", + "hud.bag.glider": "Deltavlieger", + "hud.bag.belt": "Riem", + "hud.bag.ring": "Ring", + "hud.bag.back": "Rug", + "hud.bag.legs": "Benen", + "hud.bag.feet": "Voeten", + "hud.bag.mainhand": "Dominante hand", + "hud.bag.offhand": "Tweede hand", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/hud/char_window.ron b/assets/voxygen/i18n/nl_NL/hud/char_window.ron new file mode 100644 index 0000000000..82ccbaa673 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/char_window.ron @@ -0,0 +1,21 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + "character_window.character_name": "Karakter Naam", + // Character stats + "character_window.character_stats": r#"Uithouding + +Fitheid + +Willskracht + +Bescherming +"#, + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/hud/chat.ron b/assets/voxygen/i18n/nl_NL/hud/chat.ron new file mode 100644 index 0000000000..0db1871ffd --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/chat.ron @@ -0,0 +1,32 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + "hud.chat.online_msg": "[{name}] is nu online", + "hud.chat.offline_msg": "[{name}] is offline gegaan", + + "hud.chat.default_death_msg": "[{name}] is gestorven", + "hud.chat.environmental_kill_msg": "[{name}] is gestorven in {environment}", + "hud.chat.fall_kill_msg": "[{name}] is gestorven door te vallen", + "hud.chat.suicide_msg": "[{name}] is gestorven door zelf toegebrachte verwondingen", + + "hud.chat.pvp_melee_kill_msg": "[{attacker}] versloeg [{victim}]", + "hud.chat.pvp_ranged_kill_msg": "[{attacker}] schoot [{victim}] neer", + "hud.chat.pvp_explosion_kill_msg": "[{attacker}] blies [{victim}] op", + "hud.chat.pvp_energy_kill_msg": "[{attacker}] gebruikte magie om [{victim}] te vermoorden", + + "hud.chat.npc_melee_kill_msg": "{attacker} dode [{victim}]", + "hud.chat.npc_ranged_kill_msg": "{attacker} schoot [{victim}]", + "hud.chat.npc_explosion_kill_msg": "{attacker} blies [{victim}] op", + + "hud.chat.loot_msg": "Je raapte [{item}] op", + "hud.chat.loot_fail": "Jouw inventory is vol!", + "hud.chat.goodbye": "Vaarwel!", + "hud.chat.connection_lost": "Verbinding verloren. Je wordt in {time} seconden gekicked.", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/hud/crafting.ron b/assets/voxygen/i18n/nl_NL/hud/crafting.ron new file mode 100644 index 0000000000..551558ea69 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/crafting.ron @@ -0,0 +1,16 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + "hud.crafting": "Fabriceren", + "hud.crafting.recipes": "Recepten", + "hud.crafting.ingredients": "Ingrediënten:", + "hud.crafting.craft": "Maak", + "hud.crafting.tool_cata": "Vereisten:", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/hud/group.ron b/assets/voxygen/i18n/nl_NL/hud/group.ron new file mode 100644 index 0000000000..16be7c70b1 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/group.ron @@ -0,0 +1,23 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + "hud.group": "Groep", + "hud.group.invite_to_join": "[{name}] heeft je voor zijn/haar groep uitgenodigd!", + "hud.group.invite": "Uitnodigen", + "hud.group.kick": "Kick", + "hud.group.assign_leader": "Maak Leider", + "hud.group.leave": "Groep Verlaten", + "hud.group.dead" : "Dood", + "hud.group.out_of_range": "Buiten Bereik", + "hud.group.add_friend": "Aan Vrienden Toevoegen", + "hud.group.link_group": "Link Groups", + "hud.group.in_menu": "In Menu", + "hud.group.members": "Groepsleden", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/hud/map.ron b/assets/voxygen/i18n/nl_NL/hud/map.ron new file mode 100644 index 0000000000..48afb00453 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/map.ron @@ -0,0 +1,14 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + // Map and Questlog + "hud.map.map_title": "Kaart", + "hud.map.qlog_title": "Quests", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/hud/misc.ron b/assets/voxygen/i18n/nl_NL/hud/misc.ron new file mode 100644 index 0000000000..e4e722d883 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/misc.ron @@ -0,0 +1,78 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + /// Start HUD Section + "hud.do_not_show_on_startup": "Niet meer bij opstarten tonen", + "hud.show_tips": "Tips tonen", + "hud.quests": "Quests", + "hud.you_died": "Je bent gedood", + "hud.waypoint_saved": "Waypoint Opgeslagen", + + "hud.press_key_to_show_keybindings_fmt": "[{key}] Besturing", + "hud.press_key_to_toggle_lantern_fmt": "[{key}] Lantaarn", + "hud.press_key_to_show_debug_info_fmt": "Druk op {key} om debug info te tonen", + "hud.press_key_to_toggle_keybindings_fmt": "Druk op {key} om te de besturing aan/uit te zetten", + "hud.press_key_to_toggle_debug_info_fmt": "Druk op {key} om debug info aan/uit te zetten", + + // Chat outputs + + // Respawn message + "hud.press_key_to_respawn": r#"Druk op {key} om te respawnen bij het laatste kampvuur dat je bezocht."#, + + // Welcome message + "hud.welcome": r#"Welcome bij de Veloren Alpha! + +Wat tips voor je start: + + +Druk op F1 om de besturing te zien. + +Type /help in chat om de chat commando's te zien. + + +Er verschijnen kisten en andere objecten willekeurig in de wereld! + +Gebruik rechtermuisknop om ze te verzamelen! + +Om effectief hetgene dat je van de kisten krijgt te gebruiken, open je je inventory met 'B'. + +Dubbelklik op de voorwerpen in je rugzak om ze aan te doen of te gebruiken. + +Gooi dingen weg door ze eenmaal aan te klikken en dan buiten je rugzak te klikken. + + +Nachten kunnen vrij donker worden in Veloren. + +Gebruik je lantaarn door 'G' te drukken + + +Wil je je cursor gebruiken om dit venster te sluiten? Druk TAB! + + +Geniet van je verblijf in de wereld van Veloren."#, + +"hud.temp_quest_headline": r#"Help ons alsjeblieft, reiziger!"#, +"hud.temp_quest_text": r#"Kerkers vol met kwaaraardige sektes +komen overal tevoorschijn rond onze vreedzame dorpen! + + +Verzamel wat gezelschap, vul je voedselrantsoenen aan +en versla hun kwaadaardige leiders en volgelingen. + + +Misschien kan je zelfs een van hun +Maybe you can even obtain one of their +met magie gevulde objecten bemachtigen?"#, + + "hud.spell": "Spreuken", + + "hud.free_look_indicator": "Vrij rondkijken aan. Druk {key} om uit te zetten.", + "hud.auto_walk_indicator": "Automatisch wandelen aan", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/hud/sct.ron b/assets/voxygen/i18n/nl_NL/hud/sct.ron new file mode 100644 index 0000000000..d6b4b9ef17 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/sct.ron @@ -0,0 +1,14 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + // SCT outputs + "hud.sct.experience": "{amount} Exp", + "hud.sct.block": "GEBLOCKED", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/hud/settings.ron b/assets/voxygen/i18n/nl_NL/hud/settings.ron new file mode 100644 index 0000000000..41aba2277e --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/settings.ron @@ -0,0 +1,96 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + // Settings + "hud.settings.general": "Algemeen", + "hud.settings.none": "Geen", + "hud.settings.press_behavior.toggle": "Omschakelbaar", + "hud.settings.press_behavior.hold": "Inhouden", + "hud.settings.help_window": "Help Venster", + "hud.settings.debug_info": "Debug Informatie", + "hud.settings.tips_on_startup": "Tips-Bij-Opstarten", + "hud.settings.ui_scale": "UI-Schaal", + "hud.settings.relative_scaling": "Relatief Schalen", + "hud.settings.custom_scaling": "Aangepast Schalen", + "hud.settings.crosshair": "Richtkruis", + "hud.settings.opacity": "Doorschijnbaarheid", + "hud.settings.hotbar": "Hotbar", + "hud.settings.toggle_shortcuts": "Toon Sneltoetsen", + "hud.settings.toggle_bar_experience": "Toon Experience Balk", + "hud.settings.scrolling_combat_text": "Pop-up Gevechtstext", + "hud.settings.single_damage_number": "Aparte Schade Getallen", + "hud.settings.cumulated_damage": "Cumulatieve Schade Getallen", + "hud.settings.incoming_damage": "Inkomende Schade Getallen", + "hud.settings.cumulated_incoming_damage": "Cumulatieve Inkomende Schade Getallen", + "hud.settings.speech_bubble": "Spraakbubbel", + "hud.settings.speech_bubble_dark_mode": "Donkere Modus", + "hud.settings.speech_bubble_icon": "Spraakbubbel Icoon", + "hud.settings.energybar_numbers": "Energiebalk Getallen", + "hud.settings.values": "Waarden", + "hud.settings.percentages": "Percentages", + "hud.settings.chat": "Chat", + "hud.settings.background_opacity": "Transparantie Achtergrond", + "hud.settings.chat_character_name": "Karakternamen in de Chat", + "hud.settings.loading_tips": "Laadscherm Tips", + + "hud.settings.pan_sensitivity": "Camera-gevoeligheid", + "hud.settings.zoom_sensitivity": "Zoom-gevoeligheid", + "hud.settings.invert_scroll_zoom": "Inverteer Zoom Scrollen", + "hud.settings.invert_mouse_y_axis": "Inverteer muis Y As", + "hud.settings.enable_mouse_smoothing": "Camerabeweging gelijkmaken", + "hud.settings.free_look_behavior": "Vrije camera gedrag", + "hud.settings.auto_walk_behavior": "Automatisch wandelgedrag", + "hud.settings.stop_auto_walk_on_input": "Stop automatisch +wandelen bij bewegen", + + "hud.settings.view_distance": "Kijkafstand", + "hud.settings.sprites_view_distance": "Kijkafstand sprites", + "hud.settings.figures_view_distance": "Kijkafstand entiteiten", + "hud.settings.maximum_fps": "Maximum FPS", + "hud.settings.fov": "Gezichtsveld (graden)", + "hud.settings.gamma": "Gamma", + "hud.settings.ambiance": "Omgevingshelderheid", + "hud.settings.antialiasing_mode": "AntiAliasing Modus", + "hud.settings.cloud_rendering_mode": "Wolk Rendering Modus", + "hud.settings.fluid_rendering_mode": "Vloeistoffen Rendering Mode", + "hud.settings.fluid_rendering_mode.cheap": "Goedkoop", + "hud.settings.fluid_rendering_mode.shiny": "Schitterend", + "hud.settings.cloud_rendering_mode.regular": "Gewoon", + "hud.settings.fullscreen": "Volledig scherm", + "hud.settings.fullscreen_mode": "Volledige scherm modus", + "hud.settings.fullscreen_mode.exclusive": "Exclusief", + "hud.settings.fullscreen_mode.borderless": "Randloos", + "hud.settings.particles": "Deeltjes", + "hud.settings.resolution": "Resolutie", + "hud.settings.bit_depth": "Bit diepte", + "hud.settings.refresh_rate": "Vernieuwingssnelheid", + "hud.settings.save_window_size": "Venster grootte opslaan", + "hud.settings.lighting_rendering_mode": "Licht Rendering Modus", + "hud.settings.lighting_rendering_mode.ashikhmin": "Type A", + "hud.settings.lighting_rendering_mode.blinnphong": "Type B", + "hud.settings.lighting_rendering_mode.lambertian": "Type L", + "hud.settings.shadow_rendering_mode": "Schaduw Rendering Mode", + "hud.settings.shadow_rendering_mode.none": "Geen", + "hud.settings.shadow_rendering_mode.cheap": "Goedkoop", + "hud.settings.shadow_rendering_mode.map": "Kaart", + "hud.settings.shadow_rendering_mode.map.resolution": "Resolutie", + "hud.settings.lod_detail": "LoD Detail", + "hud.settings.save_window_size": "Venster grootte opslaan", + + + "hud.settings.music_volume": "Muziek Volume", + "hud.settings.sound_effect_volume": "Geluidseffecten Volume", + "hud.settings.audio_device": "Audio Apparaat", + + "hud.settings.awaitingkey": "Druk op een toets...", + "hud.settings.unbound": "Geen", + "hud.settings.reset_keybinds": "Standaardwaarden +herstellen", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/hud/skills.ron b/assets/voxygen/i18n/nl_NL/hud/skills.ron new file mode 100644 index 0000000000..c782b41883 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/skills.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/nl_NL/hud/social.ron b/assets/voxygen/i18n/nl_NL/hud/social.ron new file mode 100644 index 0000000000..0328b877d4 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/social.ron @@ -0,0 +1,22 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + "hud.social": "Andere spelers", + "hud.social.online": "Online:", + "hud.social.friends": "Vrienden", + "hud.social.not_yet_available": "Nog niet beschikbaar", + "hud.social.faction": "Factie", + "hud.social.play_online_fmt": "{nb_player} speler(s) online", + "hud.social.name": "Naam", + "hud.social.level": "Level", + "hud.social.zone": "Zone", + "hud.social.account": "Account", + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/nl_NL/hud/trade.ron b/assets/voxygen/i18n/nl_NL/hud/trade.ron new file mode 100644 index 0000000000..bac886e087 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/hud/trade.ron @@ -0,0 +1,12 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/nl_NL/main.ron b/assets/voxygen/i18n/nl_NL/main.ron new file mode 100644 index 0000000000..14ade0a30b --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/main.ron @@ -0,0 +1,80 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + "main.connecting": "Verbinden", + "main.creating_world": "Wereld aan het maken", + "main.tip": "Tip:", + + // Welcome notice that appears the first time Veloren is started + "main.notice": r#"Welkom bij de Alpha versie van Veloren! + +Voordat de fun begint zijn hier enkele dingen om rekening mee te houden: + +- Dit is een hele vroege alpha. Verwacht bugs, extreem onafgemaakte gameplay, ruwe spelmechanics en missende functies. + +- Als je constructieve feedback hebt of bugs wil melden, kan je ons contacteren via Reddit, Gitlab of onze community Discord. + +- Veloren is onder de GPL 3 open-source licentie gelicensieerd. Dat betekent dat je vrij bent om het spel te spelen, bewerken en doorgeven op welke manier +je ook wil (zo lang afgeleide werken ook onder GPL 3 gelicensieerd zijn) + +- Veloren is een non-profit community project, en iedereen dat er aan meehelpt is een vrijwilliger. +Als je interesse hebt kan je je aansluiten bij onze ontwikkeling- of ontwerpteams! + +Bedankt voor de tijd te nemen om deze melding te lezen, we hopen dat je van het spel zult genieten! +Thanks for taking the time to read this notice, we hope you enjoy the game! + +~ De Veloren ontwikkelaars"#, + + // Login process description + "main.login_process": r#"Information over het loginproces: + +Hou er rekening mee dat je tegenwoordig een account nodig hebt +om op servers met authenticatie te spelen + +Je kan een account maken op + +https://veloren.net/account/."#, + "main.login.server_not_found": "Server nie gevonden", + "main.login.authentication_error": "Authenticatie mislukt", + "main.login.server_full": "Server zit vol", + "main.login.untrusted_auth_server": "Onvertrouwde authenticatie server", + "main.login.outdated_client_or_server": "Versieprobleem: De versie tussen de client en de server komen niet overeen. Check of er updates zijn/", + "main.login.timeout": "Timeout: De server deed er te lang over om te antwoorden. (Overblast of netwerk problemen).", + "main.login.server_shut_down": "Server is afgesloten", + "main.login.network_error": "Network problemen", + "main.login.failed_sending_request": "Kon verzoek niet naar authenticatie server sturen", + "main.login.invalid_character": "Het geselecteerde karakter is ongeldig", + "main.login.client_crashed": "Client is gecrashed", + "main.login.not_on_whitelist": "Je moet op de whitelist staan om deze wereld te joinen", + "main.login.banned": "Je bent verbannen voor de volgende reden", + "main.login.kicked": "Je bent gekicked voor de volgende reden", + "main.login.select_language": "Kies een taal", + }, + + + vector_map: { + "loading.tips": [ + "Druk op 'G' om je lantaarn te doen schijnen.", + "Druk op 'F1' om alle standaardbesturingen te zien.", + "Je kan /say of /s typen om enkel met spelers rondom je te praten.", + "Je kan /region of /r typen om enkel met spelers binnen een hondertal blokken te praten.", + "Je kan /group of /g typen om enkel met je groepsleden te praten.", + "Om privéberichten te sturen gebruik je /tell gevolgd door de speler zijn naam en dan je bericht", + "NPCs met hetzelfde level zijn niet per sé altijd even sterk.", + "Hou je ogen open voor eten, kisten en andere buit verspreid over de wereld!", + "Zit je Inventory vol met eten? Probeer beter eten er van te craften!", + "Weet je niet wat te doen? Kerkers zijn met een bruine vlek op je kaart aangeduid!", + "Vergeet je grafische instellingen niet aan te passen naar je systeem. Druk 'N' om het instellingenmenu te openen.", + "Met anderen spelen is plezanter! Druk op 'O' om te zien wie er nog online is!", + "Een NPC met een doodshoofd onder zijn levensbalk is zeer sterk vergeleken met jezelf.", + "Druk op 'J' om te dansen. Feestje!", + "Druk op 'L-Shift' om je deltavlieger te openen en het luchtruim te veroveren.", + "Veloren is nog steeds in Pre-Alpha. We doen ons best om het elke dag te verbeteren!", + "Als je het ontwikkelingsteam wil versterken of gewoon een babbeltje wil doen met ons, kom dan langs op onze Discord-Server.", + "Je kan numerieke waarden in de levensbalk aan of uit zetten in de instellingen.", + "Om je stats te zien kan je op 'Stats' klikken in je Inventory.", + ], + } +) diff --git a/assets/voxygen/i18n/nl_NL/npc.ron b/assets/voxygen/i18n/nl_NL/npc.ron new file mode 100644 index 0000000000..7928da0392 --- /dev/null +++ b/assets/voxygen/i18n/nl_NL/npc.ron @@ -0,0 +1,93 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for netherlands Dutch +( + string_map: { + }, + + + vector_map: { + "npc.speech.villager_under_attack": [ + "Help, Ik word aangevallen!", + "Help! Ik word aangevallen!", + "Ow! Ik word aangevallen!", + "Ow! Ik word aangevallen! Help!", + "Help me! Ik word aangevallen!", + "Ik word aangevallen! Help!", + "Ik word aangevallen! Help me!", + "Help!", + "Help! Help!", + "Help! Help! Help!", + "Ik word aangevallen!", + "AAAHHH! Ik word aangevallen!", + "AAAHHH! Ik word aangevallen! Help!", + "Help! We worden aangevallen!", + "Help! Moordenaar!", + "Help! Er loopt een moordenaar rond!", + "Help! Ze proberen mij te vermoorden!", + "Wachters, Ik word aangevallen!", + "Wachters! Ik word aangevallen!", + "Ik word aangevallen! Wachters!", + "Help! Wachters! Ik word aangevallen!", + "Wachters! Kom snel!", + "Wachters! Wachters!", + "Wachters! Er is een snodaard mij aan het aanvallen!", + "Wachters, slacht deze kwaadaardige snodaard!", + "Wachters! Er is een moordenaar!", + "Wachters! Help me!", + "Hier kom je niet mee weg! Wachters!", + "Jij boosdoener!", + "Help me!", + "Help! Alsjeblieft!", + "Ow! Wachters! Help!", + "Ze zitten achter me aan!", + "Help! Help! Ik word onderdrukt!", + "Ah, nu zien we het geweld eigen aan het systeem.", + "Het is maar een schrammetje!", + "Stop daarmee!", + "Wat heb ik jouw ooit aangedaan?!", + "Alsjeblieft stop met aanvallen!", + "Hey! Pas op waar je dat ding wijst!", + "Gruwelijke ellendeling, wegwezen!", + "Stop ermee! Wegwezen!", + "Nu maak je me boos!", + "Oi! Wie denk je wel niet dat je bent?!", + "Hiervoor ga je boeten!", + "Stop, Alsjeblieft! Ik heb niks waardevol!", + "Ik stuur mijn broer op je af, hij is groter dan ik!", + "Neeeee, ik ga het tegen je mama zeggen!", + "Vervloek u!", + "Alsjeblieft niet doen.", + "Dat was niet erg vriendelijk!", + "Je wapen werkt, je mag het nu wegsteken hoor!", + "Genade!", + "Ik smeek je, ik heb een familie!", + "Ik ben te jong om te sterven!", + "Ik ben te mooi om te sterven!", + "Kunnen we erover praten?", + "Geweld is nooit het antwoord!", + "Vandaag zit vol met tegenslagen...", + "Hey, dat doet pijn!", + "Eek!", + "Hoe onbeleefd!", + "Stop, ik smeek je!", + "Ik wens je de mazelen toe!", + "Dit is niet plezant.", + "Hoe durf je?!", + "Ik zet het je betaald!", + "Blijf zo doorgaan en je zult spijt krijgen!", + "Zorg er niet voor dat ik je moet pijn doen!", + "Er moet een soort van misverstand zijn!", + "Je moet dit niet doen!", + "Vertrek, duivel!", + "Dat deed echt pijn!", + "Waarom doe je dit?", + "Bij alles dat heilig is, stop!", + "Je verward mij met iemand anders!", + "Ik verdien dit niet!", + "Doe dat alsjeblieft niet meer.", + "Wachters, gooi dit monster het meer in!", + "Ik laat mijn Tarasque op je los!", + ], + } +) diff --git a/assets/voxygen/i18n/no_NB/_manifest.ron b/assets/voxygen/i18n/no_NB/_manifest.ron new file mode 100644 index 0000000000..cf63fde6c8 --- /dev/null +++ b/assets/voxygen/i18n/no_NB/_manifest.ron @@ -0,0 +1,44 @@ +/// 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 + +/// Lokalisering for norsk bokmål +( + metadata: ( + language_name: "Norsk bokmål", + language_identifier: "no_NB", + ), + convert_utf8_to_ascii: false, + fonts: { + "opensans": Font ( + asset_key: "voxygen.font.OpenSans-Regular", + scale_ratio: 1.0, + ), + "metamorph": Font ( + asset_key: "voxygen.font.Metamorphous-Regular", + scale_ratio: 1.0, + ), + "alkhemi": Font ( + asset_key: "voxygen.font.Alkhemikal", + scale_ratio: 1.0, + ), + "wizard": Font ( + asset_key: "voxygen.font.wizard", + scale_ratio: 1.0, + ), + "cyri": Font ( + asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", + scale_ratio: 1.0, + ), + } +) diff --git a/assets/voxygen/i18n/no_NB/buff.ron b/assets/voxygen/i18n/no_NB/buff.ron new file mode 100644 index 0000000000..526893d919 --- /dev/null +++ b/assets/voxygen/i18n/no_NB/buff.ron @@ -0,0 +1,25 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + /// Buffs and Debuffs + "buff.remove": "Klikk for å fjerne", + "buff.title.missing": "Mangler tittel", + "buff.desc.missing": "Mangler beskrivelse", + // Buffs + "buff.title.heal": "Helbred", + "buff.desc.heal": "Helbred over tid.", + "buff.title.potion": "Trylledrikk", + "buff.desc.potion": "Drikker...", + "buff.title.saturation": "Metning", + "buff.desc.saturation": "Helbred over tid fra forbruksvarer.", + // Debuffs + "buff.title.bleed": "Blør", + "buff.desc.bleed": "Påfører regelmessig skade.", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/char_selection.ron b/assets/voxygen/i18n/no_NB/char_selection.ron new file mode 100644 index 0000000000..bb049b1e7b --- /dev/null +++ b/assets/voxygen/i18n/no_NB/char_selection.ron @@ -0,0 +1,32 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + "char_selection.loading_characters": "Laster inn karakterer...", + "char_selection.delete_permanently": "Slett denne karakteren permanent?", + "char_selection.deleting_character": "Sletter karakter...", + "char_selection.change_server": "Bytt server", + "char_selection.enter_world": "Gå inn i verden", + "char_selection.logout": "Logg ut", + "char_selection.create_new_character": "Lag ny karakter", + "char_selection.creating_character": "Skaper karakter...", + "char_selection.character_creation": "Karakterskaping", + + "char_selection.human_default": "Menneskestandarder", + "char_selection.level_fmt": "Nivå {level_nb}", + "char_selection.uncanny_valley": "Villmark", + "char_selection.plains_of_uncertainty": "Usikkerhetssletter", + "char_selection.beard": "Skjegg", + "char_selection.hair_style": "Hårstil", + "char_selection.hair_color": "Hårfarge", + "char_selection.eye_color": "Øyenfarge", + "char_selection.skin": "Hud", + "char_selection.eyeshape": "Øyedetaljer", + "char_selection.accessories": "Tilbehør", + "char_selection.create_info_name": "Karakteren din trenger et navn!", + }, + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/common.ron b/assets/voxygen/i18n/no_NB/common.ron new file mode 100644 index 0000000000..aec357f32a --- /dev/null +++ b/assets/voxygen/i18n/no_NB/common.ron @@ -0,0 +1,71 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + // Texts used in multiple locations with the same formatting + "common.username": "Brukernavn", + "common.singleplayer": "Enspiller", + "common.multiplayer": "Flerspiller", + "common.servers": "Servere", + "common.quit": "Avslutt", + "common.settings": "Innstillinger", + "common.languages": "Språk", + "common.interface": "Grensesnitt", + "common.gameplay": "Spillbarhet", + "common.controls": "Kontroller", + "common.video": "Grafikk", + "common.sound": "Lyd", + "common.resume": "Fortsett", + "common.characters": "Karakterer", + "common.close": "Lukk", + "common.yes": "Ja", + "common.no": "Nei", + "common.back": "Tilbake", + "common.create": "Lag", + "common.okay": "Ok", + "common.add": "Legg til", + "common.accept": "Aksepter", + "common.decline": "Avslå", + "common.disclaimer": "Bemerk", + "common.cancel": "Avbryt", + "common.none": "Ingen", + "common.error": "Feil", + "common.fatal_error": "Kritisk Feil", + "common.you": "Du", + "common.automatic": "Automatisk", + "common.random": "Tilfeldig", + // Settings Window title + "common.interface_settings": "Grensesnitt Instillinger", + "common.gameplay_settings": "Spillbarhet Innstillinger", + "common.controls_settings": "Kontroller Innstillinger", + "common.video_settings": "Grafikk Innstillinger", + "common.sound_settings": "Lyd Innstillinger", + "common.language_settings": "Språk Innstillinger", + + // Message when connection to the server is lost + "common.connection_lost": r#"Mistet forbindelsen! +Har serveren startet på nytt? +Har det kommet nye oppdateringer?"#, + + + "common.species.orc": "Ork", + "common.species.human": "Menneske", + "common.species.dwarf": "Dverg", + "common.species.elf": "Alv", + "common.species.undead": "Udødelig", + "common.species.danari": "Danari", + + "common.weapons.axe": "Øks", + "common.weapons.sword": "Sverd", + "common.weapons.staff": "Stav", + "common.weapons.bow": "Bue", + "common.weapons.hammer": "Hammer", + "common.weapons.sceptre": "Helbredings Septer", + "common.rand_appearance": "Tilfeldig utseende og navn", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/esc_menu.ron b/assets/voxygen/i18n/no_NB/esc_menu.ron new file mode 100644 index 0000000000..35f020c7e9 --- /dev/null +++ b/assets/voxygen/i18n/no_NB/esc_menu.ron @@ -0,0 +1,13 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + "esc_menu.logout": "Logg ut", + "esc_menu.quit_game": "Avslutt spillet", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/gameinput.ron b/assets/voxygen/i18n/no_NB/gameinput.ron new file mode 100644 index 0000000000..52be8f36fb --- /dev/null +++ b/assets/voxygen/i18n/no_NB/gameinput.ron @@ -0,0 +1,67 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + "gameinput.primary": "Primær Angrep", + "gameinput.secondary": "Sekundær Angrep/Blokk/Sikt", + "gameinput.slot1": "Hurtigbar Luke 1", + "gameinput.slot2": "Hurtigbar Luke 2", + "gameinput.slot3": "Hurtigbar Luke 3", + "gameinput.slot4": "Hurtigbar Luke 4", + "gameinput.slot5": "Hurtigbar Luke 5", + "gameinput.slot6": "Hurtigbar Luke 6", + "gameinput.slot7": "Hurtigbar Luke 7", + "gameinput.slot8": "Hurtigbar Luke 8", + "gameinput.slot9": "Hurtigbar Luke 9", + "gameinput.slot10": "Hurtigbar Luke 10", + "gameinput.swaploadout": "Bytt utlastning", + "gameinput.togglecursor": "Veksle Musepeker", + "gameinput.help": "Veksle hjelpevindu", + "gameinput.toggleinterface": "Veksle Grensesnitt", + "gameinput.toggledebug": "Veksle FPS og feilsøkingsinfo", + "gameinput.screenshot": "Ta skjermbilde", + "gameinput.toggleingameui": "Veksle Navneskilt", + "gameinput.fullscreen": "Veksle fullskjerm", + "gameinput.moveforward": "Beveg frem", + "gameinput.moveleft": "Beveg venstre", + "gameinput.moveright": "Beveg høyre", + "gameinput.moveback": "Beveg bakover", + "gameinput.jump": "Hopp", + "gameinput.glide": "Glider", + "gameinput.roll": "Rull", + "gameinput.climb": "Klatre", + "gameinput.climbdown": "Klatre ned", + "gameinput.wallleap": "Veggsprang", + "gameinput.togglelantern": "Veksle lykt", + "gameinput.mount": "Monter", + "gameinput.chat": "Chat", + "gameinput.command": "Kommando", + "gameinput.escape": "Røm", + "gameinput.map": "Kart", + "gameinput.bag": "Bag", + "gameinput.social": "Sosial", + "gameinput.sit": "Sitt", + "gameinput.spellbook": "Trylleformler", + "gameinput.settings": "Innstillinger", + "gameinput.respawn": "Gjennopstå", + "gameinput.charge": "Lad opp", + "gameinput.togglewield": "Veksle Våpen", + "gameinput.interact": "Interaksjon", + "gameinput.freelook": "Frimodus", + "gameinput.autowalk": "Autogåing", + "gameinput.dance": "Dans", + "gameinput.select": "Velg enhet", + "gameinput.acceptgroupinvite": "Godta gruppeinvitasjon", + "gameinput.declinegroupinvite": "Avvis gruppeinvitasjon", + "gameinput.crafting": "Håndverk", + "gameinput.fly": "Fly", + "gameinput.sneak": "Snik", + "gameinput.swimdown": "Svøm nedover", + "gameinput.swimup": "Svøm oppover", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/hud/bag.ron b/assets/voxygen/i18n/no_NB/hud/bag.ron new file mode 100644 index 0000000000..dc50db6beb --- /dev/null +++ b/assets/voxygen/i18n/no_NB/hud/bag.ron @@ -0,0 +1,32 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + // Inventory + "hud.bag.inventory": "{playername}'s Inventar", + "hud.bag.stats_title": "{playername}'s Statistikk", + "hud.bag.exp": "Exp", + "hud.bag.armor": "Utrustning", + "hud.bag.stats": "Statistikk", + "hud.bag.head": "Hode", + "hud.bag.neck": "Nakke", + "hud.bag.tabard": "Tabard", + "hud.bag.shoulders": "Skulder", + "hud.bag.chest": "Bryst", + "hud.bag.hands": "Hender", + "hud.bag.lantern": "Lykt", + "hud.bag.glider": "Glidefly", + "hud.bag.belt": "Belte", + "hud.bag.ring": "Ring", + "hud.bag.back": "Rygg", + "hud.bag.legs": "Ben", + "hud.bag.feet": "Føtter", + "hud.bag.mainhand": "Hovedhånd", + "hud.bag.offhand": "Tillegshånd", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/hud/char_window.ron b/assets/voxygen/i18n/no_NB/hud/char_window.ron new file mode 100644 index 0000000000..0d3e194674 --- /dev/null +++ b/assets/voxygen/i18n/no_NB/hud/char_window.ron @@ -0,0 +1,22 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + /// Start character window section + "character_window.character_name": "Karakternavn", + // Character stats + "character_window.character_stats": r#"Utholdenhet + +Fitness + +Viljestyrke + +Beskyttelse +"#, + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/hud/chat.ron b/assets/voxygen/i18n/no_NB/hud/chat.ron new file mode 100644 index 0000000000..605cbf9fe7 --- /dev/null +++ b/assets/voxygen/i18n/no_NB/hud/chat.ron @@ -0,0 +1,37 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + // Chat outputs + "hud.chat.online_msg": "[{name}] logget på", + "hud.chat.offline_msg": "[{name}] logget av", + + "hud.chat.default_death_msg": "[{name}] døde", + "hud.chat.environmental_kill_msg": "[{name}] døde i {environment}", + "hud.chat.fall_kill_msg": "[{name}] døde av fallskade", + "hud.chat.suicide_msg": "[{name}] døde av selvskader", + + "hud.chat.pvp_melee_kill_msg": "[{attacker}] drepte [{victim}]", + "hud.chat.pvp_ranged_kill_msg": "[{attacker}] skjøt [{victim}]", + "hud.chat.pvp_explosion_kill_msg": "[{attacker}] sprengte [{victim}]", + "hud.chat.pvp_energy_kill_msg": "[{attacker}] drepte [{victim}] med magi", + "hud.chat.pvp_buff_kill_msg": "[{attacker}] drepte [{victim}]", + + + "hud.chat.npc_melee_kill_msg": "{attacker} drepte [{victim}]", + "hud.chat.npc_ranged_kill_msg": "{attacker} skjøt [{victim}]", + "hud.chat.npc_explosion_kill_msg": "{attacker} sprengte [{victim}]", + "hud.chat.npc_energy_kill_msg": "{attacker} drepte [{victim}] med magi", + "hud.chat.npc_other_kill_msg": "{attacker} drepte [{victim}]", + + "hud.chat.loot_msg": "Du plukket opp [{item}]", + "hud.chat.loot_fail": "Ditt inventar er fullt!", + "hud.chat.goodbye": "Adjø!", + "hud.chat.connection_lost": "Forbindelse mistet. Utkastet om {time} sekunder.", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/hud/crafting.ron b/assets/voxygen/i18n/no_NB/hud/crafting.ron new file mode 100644 index 0000000000..279f6042ee --- /dev/null +++ b/assets/voxygen/i18n/no_NB/hud/crafting.ron @@ -0,0 +1,16 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + "hud.crafting": "Håndverk", + "hud.crafting.recipes": "Oppskrifter", + "hud.crafting.ingredients": "Ingredienser:", + "hud.crafting.craft": "Lag", + "hud.crafting.tool_cata": "Krever:", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/hud/group.ron b/assets/voxygen/i18n/no_NB/hud/group.ron new file mode 100644 index 0000000000..99159f78c6 --- /dev/null +++ b/assets/voxygen/i18n/no_NB/hud/group.ron @@ -0,0 +1,23 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + "hud.group": "Gruppe", + "hud.group.invite_to_join": "[{name}] inviterte deg til gruppen deres!", + "hud.group.invite": "Inviter", + "hud.group.kick": "Spark", + "hud.group.assign_leader": "Tilordne leder", + "hud.group.leave": "Forlat Gruppe", + "hud.group.dead" : "Død", + "hud.group.out_of_range": "Ute av rekkevidde", + "hud.group.add_friend": "Legg til i vennelisten", + "hud.group.link_group": "Koble til grupper", + "hud.group.in_menu": "I Meny", + "hud.group.members": "Gruppemedlemmer", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/hud/map.ron b/assets/voxygen/i18n/no_NB/hud/map.ron new file mode 100644 index 0000000000..07d5c8e761 --- /dev/null +++ b/assets/voxygen/i18n/no_NB/hud/map.ron @@ -0,0 +1,14 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + // Map and Questlog + "hud.map.map_title": "Kart", + "hud.map.qlog_title": "Oppdrag", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/hud/misc.ron b/assets/voxygen/i18n/no_NB/hud/misc.ron new file mode 100644 index 0000000000..7f2f16147f --- /dev/null +++ b/assets/voxygen/i18n/no_NB/hud/misc.ron @@ -0,0 +1,76 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + /// Start HUD Section + "hud.do_not_show_on_startup": "Ikke vis dette på oppstart", + "hud.show_tips": "Vis tips", + "hud.quests": "Oppdrag", + "hud.you_died": "Du døde", + "hud.waypoint_saved": "Veipunkt lagret", + + "hud.press_key_to_show_keybindings_fmt": "[{key}] Hurtigtaster", + "hud.press_key_to_toggle_lantern_fmt": "[{key}] Lykt", + "hud.press_key_to_show_debug_info_fmt": "Trykk {key} for å vise feilsøkingsinfo", + "hud.press_key_to_toggle_keybindings_fmt": "Trykk {key} for å skru av/på hurtigtaster", + "hud.press_key_to_toggle_debug_info_fmt": "Trykk {key} for å skru av/på feilsøkingsinformasjon", + + // Respawn message + "hud.press_key_to_respawn": r#"Trykk {key} for å gjennopstå ved det siste bålet du besøkte."#, + + // Welcome message + "hud.welcome": r#"Velkommen til Veloren Alfa! + + +Noen tips før du begynner: + + +Trykk på F1 for å se tilgjengelige hurtigtaster. + +Skriv /hjelp inn i chatten for å se chat-kommandoer + + +Det er kister og andre gjenstander som spawner tilfeldig i verden! + +Høyreklikk for å samle dem. + +For å brukte det du plyndrer fra disse kistene, åpne inventaret ditt med 'B'. + +Dobbeltklikk på varene i inventaret ditt for å bruke det eller ta det på. + +Kast dem ved å klikke og dra dem utenfor inventaret. + + +Nettene kan bli ganske mørke i Veloren. + +Tenn din lykt ved å trykke på 'G'. + + +Vil du frigjøre markøren for å lukke dette vinduet? Trykk på TAB! + + +Kos deg i verden av Veloren."#, + +"hud.temp_quest_headline": r#"Vær så snill, Hjelp oss reisende!"#, +"hud.temp_quest_text": r#"Fangehull fylt med onde kultister +har dukket opp rundt våre fredelige byer! + + +Samle noe selskap, samle mat +og bekjemp deres dårlige ledere og akolytter. + + +Kanskje du til og med kan skaffe deg en av de +magisk infunderte gjenstandene?"#, + + "hud.spell": "Trylleformel", + + "hud.free_look_indicator": "Frimodus aktiv. Trykk {key} for å skru av.", + "hud.auto_walk_indicator": "Autogåing aktiv", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/hud/sct.ron b/assets/voxygen/i18n/no_NB/hud/sct.ron new file mode 100644 index 0000000000..63d45168e1 --- /dev/null +++ b/assets/voxygen/i18n/no_NB/hud/sct.ron @@ -0,0 +1,14 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + // SCT outputs + "hud.sct.experience": "{amount} Exp", + "hud.sct.block": "BLOKERT", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/no_NB/hud/settings.ron b/assets/voxygen/i18n/no_NB/hud/settings.ron new file mode 100644 index 0000000000..b57975190c --- /dev/null +++ b/assets/voxygen/i18n/no_NB/hud/settings.ron @@ -0,0 +1,98 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + "hud.settings.general": "Generell", + "hud.settings.none": "Ingen", + "hud.settings.press_behavior.toggle": "Veksle", + "hud.settings.press_behavior.hold": "Hold", + "hud.settings.help_window": "Hjelpevindu", + "hud.settings.debug_info": "Feilsøkingsinformasjon", + "hud.settings.tips_on_startup": "Tips-På-Oppstart", + "hud.settings.ui_scale": "UI-Skalering", + "hud.settings.relative_scaling": "Relativ Skalering", + "hud.settings.custom_scaling": "Tilpasset Skalering", + "hud.settings.crosshair": "Retikkel", + "hud.settings.opacity": "Gjennomsiktighet", + "hud.settings.hotbar": "Hurtigknappsbar", + "hud.settings.toggle_shortcuts": "Veksle Snarveier", + "hud.settings.buffs_skillbar": "Ikoner på Ferdighetsbar", + "hud.settings.buffs_mmap": "Ikoner på Minimap", + "hud.settings.toggle_bar_experience": "Veksle erfaringsbar", + "hud.settings.scrolling_combat_text": "Rullende kamptekst", + "hud.settings.single_damage_number": "Enkelt skadetall", + "hud.settings.cumulated_damage": "Kumulert skade", + "hud.settings.incoming_damage": "Innkommende skade", + "hud.settings.cumulated_incoming_damage": "Kumulert innkommende skade", + "hud.settings.speech_bubble": "Snakkeboble", + "hud.settings.speech_bubble_dark_mode": "Mørk modus for snakkeboble", + "hud.settings.speech_bubble_icon": "Snakkeboble ikon", + "hud.settings.energybar_numbers": "Energibar tall", + "hud.settings.values": "Verdier", + "hud.settings.percentages": "Prosentandeler", + "hud.settings.chat": "Chat", + "hud.settings.background_opacity": "Bakgrunnsgjennomsiktighet", + "hud.settings.chat_character_name": "Karakternavn i chat", + "hud.settings.loading_tips": "Oppstartsskjerm Tips", + + "hud.settings.pan_sensitivity": "Panoreringssensitivitet", + "hud.settings.zoom_sensitivity": "Zoomingssensitivitet", + "hud.settings.invert_scroll_zoom": "Inverter skrollezoom", + "hud.settings.invert_mouse_y_axis": "Inverter mus Y Aksen", + "hud.settings.enable_mouse_smoothing": "Kamerautjevning", + "hud.settings.free_look_behavior": "Frimodus oppførsel", + "hud.settings.auto_walk_behavior": "Autogåing oppførsel", + "hud.settings.stop_auto_walk_on_input": "Stopp autogåing på bevegelse", + + "hud.settings.view_distance": "Utsiktsavstand", + "hud.settings.sprites_view_distance": "Sprites utsiktsavstand", + "hud.settings.figures_view_distance": "Enhets utsiktsavstand", + "hud.settings.maximum_fps": "Maksimum FPS", + "hud.settings.fov": "Synsfelt (deg)", + "hud.settings.gamma": "Gamma", + "hud.settings.ambiance": "Stemning Brightness", + "hud.settings.antialiasing_mode": "Kantutjevningsmodus", + "hud.settings.cloud_rendering_mode": "Tegn-skyer-modus", + "hud.settings.fluid_rendering_mode": "Tegn-væske-modus", + "hud.settings.fluid_rendering_mode.cheap": "Billig", + "hud.settings.fluid_rendering_mode.shiny": "Skinnende", + "hud.settings.cloud_rendering_mode.minimal": "Minimal", + "hud.settings.cloud_rendering_mode.low": "Lav", + "hud.settings.cloud_rendering_mode.medium": "Medium", + "hud.settings.cloud_rendering_mode.high": "Høy", + "hud.settings.fullscreen": "Full skjerm", + "hud.settings.fullscreen_mode": "Fullskjermsmodus", + "hud.settings.fullscreen_mode.exclusive": "Eksklusiv", + "hud.settings.fullscreen_mode.borderless": "Uten kanter", + "hud.settings.particles": "Partikler", + "hud.settings.resolution": "Resolusjon", + "hud.settings.bit_depth": "Bit Dybde", + "hud.settings.refresh_rate": "Oppdateringsfrekvens", + "hud.settings.save_window_size": "Spar vindusstørrelse", + "hud.settings.lighting_rendering_mode": "Tegn-belysning-modus", + "hud.settings.lighting_rendering_mode.ashikhmin": "Type A - Høy ", + "hud.settings.lighting_rendering_mode.blinnphong": "Type B - Medium", + "hud.settings.lighting_rendering_mode.lambertian": "Type L - Billing", + "hud.settings.shadow_rendering_mode": "Tegn-skygger-modus", + "hud.settings.shadow_rendering_mode.none": "Ingen", + "hud.settings.shadow_rendering_mode.cheap": "Billig", + "hud.settings.shadow_rendering_mode.map": "Kart", + "hud.settings.shadow_rendering_mode.map.resolution": "Resolusjon", + "hud.settings.lod_detail": "Nivå med detaljer", + "hud.settings.save_window_size": "Lagre vindusstørrelse", + + + "hud.settings.music_volume": "Musikkvolum", + "hud.settings.sound_effect_volume": "Lydeffektvolum", + "hud.settings.audio_device": "Lydenhet", + + "hud.settings.awaitingkey": "Trykk på en tast...", + "hud.settings.unbound": "Ingen", + "hud.settings.reset_keybinds": "Tilbakestill til standardinnstillinger", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/hu_HU/template.ron b/assets/voxygen/i18n/no_NB/hud/skills.ron similarity index 79% rename from assets/voxygen/i18n/hu_HU/template.ron rename to assets/voxygen/i18n/no_NB/hud/skills.ron index 6e34acff25..b50e50eea5 100644 --- a/assets/voxygen/i18n/hu_HU/template.ron +++ b/assets/voxygen/i18n/no_NB/hud/skills.ron @@ -1,6 +1,6 @@ /// WARNING: Localization files shall be saved in UTF-8 format without BOM -/// Localization for Hungarian +/// Localization for norsk bokmål ( string_map: { diff --git a/assets/voxygen/i18n/no_NB/hud/social.ron b/assets/voxygen/i18n/no_NB/hud/social.ron new file mode 100644 index 0000000000..6086ffd95a --- /dev/null +++ b/assets/voxygen/i18n/no_NB/hud/social.ron @@ -0,0 +1,22 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + "hud.social": "Andre spillere", + "hud.social.online": "Tilstede:", + "hud.social.friends": "Venner", + "hud.social.not_yet_available": "Ikke tilgjengelig enda", + "hud.social.faction": "Fraksjon", + "hud.social.play_online_fmt": "{nb_player} spiller(e) tilstede", + "hud.social.name": "Navn", + "hud.social.level": "Nivå", + "hud.social.zone": "Sone", + "hud.social.account": "Bruker", + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/no_NB/hud/trade.ron b/assets/voxygen/i18n/no_NB/hud/trade.ron new file mode 100644 index 0000000000..f087a37a31 --- /dev/null +++ b/assets/voxygen/i18n/no_NB/hud/trade.ron @@ -0,0 +1,12 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/no_NB/main.ron b/assets/voxygen/i18n/no_NB/main.ron new file mode 100644 index 0000000000..f20c98cbdf --- /dev/null +++ b/assets/voxygen/i18n/no_NB/main.ron @@ -0,0 +1,84 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for norsk bokmål +( + string_map: { + "main.username": "Brukernavn", + "main.server": "Server", + "main.password": "Passord", + "main.connecting": "Kobler til", + "main.creating_world": "Lager verden", + "main.tip": "Tips:", + + // Welcome notice that appears the first time Veloren is started + "main.notice": r#"Velkommen til alfaversjonen av Veloren! + +Før du dykker inn i moroa, vennligst hold et par ting i tankene: + +- Dette er en veldig tidlig alfa. Forvent feil, ekstremt uferdig spilling, upolert mekanikk og manglende funksjoner. + +- Hvis du har konstruktive tilbakemeldinger eller feilrapporter, kan du kontakte oss via Reddit, GitLab eller vår Discord-server. + +- Veloren er lisensiert under GPL 3 åpen kildekode-lisensen. Det betyr at du er fri til å spille, endre og distribuere spillet på nytt, akkurat + som du ønsker (så lenge arbeidet også er under GPL 3). + +- Veloren er et ikke-profitt basert samfunnsprosjekt, og alle som jobber på prosjektet er frivillige. +Hvis du liker det du ser, er du velkommen til å bli med i utviklings- eller kunstteamene! + +Takk for at du tar deg tid til å lese denne meldingen, vi håper at du liker spillet! + +- Veloren-utviklerne"#, + + // Login process description + "main.login_process": r#"Informasjon om påloggingsprosessen: + +Vær oppmerksom på at du nå trenger en konto +for å spille på godkjennings-aktiverte servere. + +Du kan opprette en konto på + +https://veloren.net/account/."#, + "main.login.server_not_found": "Server ikke funnet", + "main.login.authentication_error": "Innloggingsfeil på serveren", + "main.login.server_full": "Serveren er full", + "main.login.untrusted_auth_server": "Godkjenningsserver kan ikke stoles på", + "main.login.outdated_client_or_server": "Sannsynligvis er versjoner inkompatible, se etter oppdateringer.", + "main.login.timeout": "Serveren svarte ikke i tide. (Overbelastet eller nettverksproblemer).", + "main.login.server_shut_down": "Serveren stoppet", + "main.login.network_error": "Nettverksfeil", + "main.login.failed_sending_request": "Forespørsel til godkjenningsserver mislyktes", + "main.login.invalid_character": "Den valgte karakteren er ugyldig", + "main.login.client_crashed": "Klienten krasjet", + "main.login.not_on_whitelist": "Du trenger en hvitelisteoppføring av en administrator for å bli med", + "main.login.banned": "Du har blitt utestengt av følgende grunn", + "main.login.kicked": "Du har blitt sparket ut av følgende grunn", + "main.login.select_language": "Velg språk", + + "main.servers.select_server": "Velg en server", + }, + + + vector_map: { + "loading.tips": [ + "Trykk 'G' for å tenne lykten.", + "Trykk 'F1' for å se alle standard tastebindinger.", + "Du kan skrive /say eller /s for å bare chatte med spillere rett rundt deg.", + "Du kan skrive /region eller /r for å bare chatte med spillere et par hundre blokker rundt deg.", + "Du kan skrive /group eller /g for å bare chatte med spillere i din nåværende gruppe.", + "For å sende private meldinger skriv /tell etterfulgt av et spillernavn og meldingen din.", + "NPCer med samme nivå kan ha forskjellige problemer.", + "Hold øye med mat, kister og andre godsaker spredt over hele verden!", + "Inventar fylt med mat? Prøv å lage bedre mat med det!", + "Lurer du på hva du kan gjøre? Fangehull er merket med brune flekker på kartet!", + "Ikke glem å justere grafikken for systemet ditt. Trykk på 'N' for å åpne innstillingene.", + "Å spille med andre er gøy! Trykk 'O' for å se hvem som er online.", + "En NPC med en hodeskalle under helsebaren er ganske kraftig sammenlignet med deg selv.", + "Trykk 'J' for å danse. Fest!", + "Trykk 'L-Shift' for å åpne Glider og erobre himmelen.", + "Veloren er fortsatt i Pre-Alfa. Vi gjør vårt beste for å forbedre det hver dag!", + "Hvis du vil bli med i Dev-teamet eller bare ta en prat med oss, bli med i Discord-Serveren.", + "Du kan veksle for å vise mengden helse på helselinjen i innstillingene.", + "For å se statistikken din, klikk på 'Statistikk' -knappen i beholdningen.", + ], + } +) diff --git a/assets/voxygen/i18n/no_NB/npc.ron b/assets/voxygen/i18n/no_NB/npc.ron new file mode 100644 index 0000000000..b4f0ae0622 --- /dev/null +++ b/assets/voxygen/i18n/no_NB/npc.ron @@ -0,0 +1,92 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Lokalisering for norsk bokmål +( + string_map: { + }, + + + vector_map: { + "npc.speech.villager_under_attack": [ + "Hjelp, jeg er under angrep!", + "Hjelp! Jeg er under angrep!", + "Au! Jeg er under angrep!", + "Au! Jeg er under angrep! Hjelp!", + "Hjelp meg! Jeg er under angrep!", + "Jeg er under angrep! Hjelp!", + "Jeg er under angrep! Hjelp meg!", + "Hjelp!", + "Hjelp! Hjelp!", + "Hjelp! Hjelp! Hjelp!", + "Jeg er under angrep!", + "AAAHHH! Jeg er under angrep!", + "AAAHHH! Jeg er under angrep! Hjelp!", + "Hjelp! Vi er under angrep!", + "Hjelp! Morder!", + "Hjelp! Det er en morder på frifot!", + "Hjelp! De prøver å drepe meg!", + "Vakter, jeg er under angrep!", + "Vakter! Jeg er under angrep!", + "Jeg er under angrep! Vakter!", + "Hjelp! Vakter! Jeg er under angrep!", + "Vakter! Kom raskt!", + "Vakter! Vakter!", + "Vakter! Det er en skurk som angriper meg!", + "Vakter, drep denne onde skurken!", + "Vakter! Det er en morder!", + "Vakter! Hjelp meg!", + "Du kommer ikke unna med dette! Vakter!", + "Du fiende!", + "Hjelp meg!", + "Hjelp! Vœr så snill!", + "Ouch! Vakter! Hjelp!", + "De etterfølger etter meg!", + "Hjelp! Hjelp! Jeg blir undertrykt!", + "Ah, nå ser vi volden som er bygd inn i systemet.", + "Det er bare en ripe!", + "Slutt med det!", + "Hva har jeg noensinne gjort mot deg?!", + "Vær så snill slutt å angripe meg!", + "Hei! Se for deg hvor du peker den tingen!", + "Avskyelige krek, bort med deg!", + "Stop det! Gå vekk!", + "Nå gjør du meg sint!", + "Oi! Hvem tror du at du er?!", + "Jeg tar hodet ditt for det!", + "Stop, vær så snill! Jeg har ingenting av verdi på meg!", + "Jeg får broren min til å ta deg, han er større enn meg!", + "Neiii, jeg sier det til mamma!", + "Forbann deg!", + "Vœr så snill ikke gjør det.", + "Det var ikke veldig snilt!", + "Våpnet ditt funker, du kan ta det vekk nå!", + "Bespar meg!", + "Vær så snill, jeg har familie!", + "Jeg er for ung til å dø!", + "Kan vi snakke om dette?", + "Vold er aldri svaret!", + "I dag ser ut til å være en dårlig dag...", + "Hei, det kjente jeg!", + "Eek!", + "Hvor uhøflig!", + "Stop, jeg ber deg!", + "Smitte beslage deg!", + "Dette er ikke gøy.", + "Hvordan våger du?!", + "Du kommer til å betale for dette!", + "Fortsett med dette og du kommer til å be unnskyld!", + "Ikke få meg til å banke deg!", + "Dette må være en misforståelse!", + "Du trenger ikke å gjøre dette!", + "Gå vekk, skurk!", + "Det gjorde vondt!", + "Hvorfor gjorde du det?", + "Etter åndene, forsvinn!", + "Du må ha forvekslet meg med noen andre!", + "Jeg fortjener ikke dette!", + "Vær så snill ikke gjør dette igjen!", + "Vakter, kast dette mennesket i havet!", + "Jeg får min tarasque til å etterfølge deg!", + ], + } +) diff --git a/assets/voxygen/i18n/no_nb/_manifest.ron b/assets/voxygen/i18n/no_nb/_manifest.ron deleted file mode 100644 index d414498d62..0000000000 --- a/assets/voxygen/i18n/no_nb/_manifest.ron +++ /dev/null @@ -1,644 +0,0 @@ -/// 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 - -/// Lokalisering for norsk bokmål -( - metadata: ( - language_name: "Norsk bokmål", - language_identifier: "no_nb", - ), - convert_utf8_to_ascii: false, - fonts: { - "opensans": Font ( - asset_key: "voxygen.font.OpenSans-Regular", - scale_ratio: 1.0, - ), - "metamorph": Font ( - asset_key: "voxygen.font.Metamorphous-Regular", - scale_ratio: 1.0, - ), - "alkhemi": Font ( - asset_key: "voxygen.font.Alkhemikal", - scale_ratio: 1.0, - ), - "wizard": Font ( - asset_key: "voxygen.font.wizard", - scale_ratio: 1.0, - ), - "cyri": Font ( - asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", - scale_ratio: 1.0, - ), - }, - string_map: { - /// Start Common section - // Texts used in multiple locations with the same formatting - "common.username": "Brukernavn", - "common.singleplayer": "Enspiller", - "common.multiplayer": "Flerspiller", - "common.servers": "Servere", - "common.quit": "Avslutt", - "common.settings": "Innstillinger", - "common.languages": "Språk", - "common.interface": "Grensesnitt", - "common.gameplay": "Spillbarhet", - "common.controls": "Kontroller", - "common.video": "Grafikk", - "common.sound": "Lyd", - "common.resume": "Fortsett", - "common.characters": "Karakterer", - "common.close": "Lukk", - "common.yes": "Ja", - "common.no": "Nei", - "common.back": "Tilbake", - "common.create": "Lag", - "common.okay": "Ok", - "common.add": "Legg til", - "common.accept": "Aksepter", - "common.decline": "Avslå", - "common.disclaimer": "Bemerk", - "common.cancel": "Avbryt", - "common.none": "Ingen", - "common.error": "Feil", - "common.fatal_error": "Kritisk Feil", - "common.you": "Du", - "common.automatic": "Automatisk", - "common.random": "Tilfeldig", - // Settings Window title - "common.interface_settings": "Grensesnitt Instillinger", - "common.gameplay_settings": "Spillbarhet Innstillinger", - "common.controls_settings": "Kontroller Innstillinger", - "common.video_settings": "Grafikk Innstillinger", - "common.sound_settings": "Lyd Innstillinger", - "common.language_settings": "Språk Innstillinger", - - // Message when connection to the server is lost - "common.connection_lost": r#"Mistet forbindelsen! -Har serveren startet på nytt? -Har det kommet nye oppdateringer?"#, - - - "common.species.orc": "Ork", - "common.species.human": "Menneske", - "common.species.dwarf": "Dverg", - "common.species.elf": "Alv", - "common.species.undead": "Udødelig", - "common.species.danari": "Danari", - - "common.weapons.axe": "Øks", - "common.weapons.sword": "Sverd", - "common.weapons.staff": "Stav", - "common.weapons.bow": "Bue", - "common.weapons.hammer": "Hammer", - "common.weapons.sceptre": "Helbredings Septer", - "common.rand_appearance": "Tilfeldig utseende og navn", - /// End Common section - - - /// Start Main screen section - "main.username": "Brukernavn", - "main.server": "Server", - "main.password": "Passord", - "main.connecting": "Kobler til", - "main.creating_world": "Lager verden", - "main.tip": "Tips:", - - // Welcome notice that appears the first time Veloren is started - "main.notice": r#"Velkommen til alfaversjonen av Veloren! - -Før du dykker inn i moroa, vennligst hold et par ting i tankene: - -- Dette er en veldig tidlig alfa. Forvent feil, ekstremt uferdig spilling, upolert mekanikk og manglende funksjoner. - -- Hvis du har konstruktive tilbakemeldinger eller feilrapporter, kan du kontakte oss via Reddit, GitLab eller vår Discord-server. - -- Veloren er lisensiert under GPL 3 åpen kildekode-lisensen. Det betyr at du er fri til å spille, endre og distribuere spillet på nytt, akkurat - som du ønsker (så lenge arbeidet også er under GPL 3). - -- Veloren er et ikke-profitt basert samfunnsprosjekt, og alle som jobber på prosjektet er frivillige. -Hvis du liker det du ser, er du velkommen til å bli med i utviklings- eller kunstteamene! - -Takk for at du tar deg tid til å lese denne meldingen, vi håper at du liker spillet! - -- Veloren-utviklerne"#, - - // Login process description - "main.login_process": r#"Informasjon om påloggingsprosessen: - -Vær oppmerksom på at du nå trenger en konto -for å spille på godkjennings-aktiverte servere. - -Du kan opprette en konto på - -https://veloren.net/account/."#, - "main.login.server_not_found": "Server ikke funnet", - "main.login.authentication_error": "Innloggingsfeil på serveren", - "main.login.server_full": "Serveren er full", - "main.login.untrusted_auth_server": "Godkjenningsserver kan ikke stoles på", - "main.login.outdated_client_or_server": "Sannsynligvis er versjoner inkompatible, se etter oppdateringer.", - "main.login.timeout": "Serveren svarte ikke i tide. (Overbelastet eller nettverksproblemer).", - "main.login.server_shut_down": "Serveren stoppet", - "main.login.network_error": "Nettverksfeil", - "main.login.failed_sending_request": "Forespørsel til godkjenningsserver mislyktes", - "main.login.invalid_character": "Den valgte karakteren er ugyldig", - "main.login.client_crashed": "Klienten krasjet", - "main.login.not_on_whitelist": "Du trenger en hvitelisteoppføring av en administrator for å bli med", - "main.login.banned": "Du har blitt utestengt av følgende grunn", - "main.login.kicked": "Du har blitt sparket ut av følgende grunn", - "main.login.select_language": "Velg språk", - - "main.servers.select_server": "Velg en server", - - /// End Main screen section - - - /// Start HUD Section - "hud.do_not_show_on_startup": "Ikke vis dette på oppstart", - "hud.show_tips": "Vis tips", - "hud.quests": "Oppdrag", - "hud.you_died": "Du døde", - "hud.waypoint_saved": "Veipunkt lagret", - - "hud.press_key_to_show_keybindings_fmt": "[{key}] Hurtigtaster", - "hud.press_key_to_toggle_lantern_fmt": "[{key}] Lykt", - "hud.press_key_to_show_debug_info_fmt": "Trykk {key} for å vise feilsøkingsinfo", - "hud.press_key_to_toggle_keybindings_fmt": "Trykk {key} for å skru av/på hurtigtaster", - "hud.press_key_to_toggle_debug_info_fmt": "Trykk {key} for å skru av/på feilsøkingsinformasjon", - - // Chat outputs - "hud.chat.online_msg": "[{name}] logget på", - "hud.chat.offline_msg": "[{name}] logget av", - - "hud.chat.default_death_msg": "[{name}] døde", - "hud.chat.environmental_kill_msg": "[{name}] døde i {environment}", - "hud.chat.fall_kill_msg": "[{name}] døde av fallskade", - "hud.chat.suicide_msg": "[{name}] døde av selvskader", - - "hud.chat.pvp_melee_kill_msg": "[{attacker}] drepte [{victim}]", - "hud.chat.pvp_ranged_kill_msg": "[{attacker}] skjøt [{victim}]", - "hud.chat.pvp_explosion_kill_msg": "[{attacker}] sprengte [{victim}]", - "hud.chat.pvp_energy_kill_msg": "[{attacker}] drepte [{victim}] med magi", - "hud.chat.pvp_buff_kill_msg": "[{attacker}] drepte [{victim}]", - - - "hud.chat.npc_melee_kill_msg": "{attacker} drepte [{victim}]", - "hud.chat.npc_ranged_kill_msg": "{attacker} skjøt [{victim}]", - "hud.chat.npc_explosion_kill_msg": "{attacker} sprengte [{victim}]", - "hud.chat.npc_energy_kill_msg": "{attacker} drepte [{victim}] med magi", - "hud.chat.npc_other_kill_msg": "{attacker} drepte [{victim}]", - - "hud.chat.loot_msg": "Du plukket opp [{item}]", - "hud.chat.loot_fail": "Ditt inventar er fullt!", - "hud.chat.goodbye": "Adjø!", - "hud.chat.connection_lost": "Forbindelse mistet. Utkastet om {time} sekunder.", - - // SCT outputs - "hud.sct.experience": "{amount} Exp", - "hud.sct.block": "BLOKERT", - - // Respawn message - "hud.press_key_to_respawn": r#"Trykk {key} for å gjennopstå ved det siste bålet du besøkte."#, - - // Welcome message - "hud.welcome": r#"Velkommen til Veloren Alfa! - - -Noen tips før du begynner: - - -Trykk på F1 for å se tilgjengelige hurtigtaster. - -Skriv /hjelp inn i chatten for å se chat-kommandoer - - -Det er kister og andre gjenstander som spawner tilfeldig i verden! - -Høyreklikk for å samle dem. - -For å brukte det du plyndrer fra disse kistene, åpne inventaret ditt med 'B'. - -Dobbeltklikk på varene i inventaret ditt for å bruke det eller ta det på. - -Kast dem ved å klikke og dra dem utenfor inventaret. - - -Nettene kan bli ganske mørke i Veloren. - -Tenn din lykt ved å trykke på 'G'. - - -Vil du frigjøre markøren for å lukke dette vinduet? Trykk på TAB! - - -Kos deg i verden av Veloren."#, - -"hud.temp_quest_headline": r#"Vær så snill, Hjelp oss reisende!"#, -"hud.temp_quest_text": r#"Fangehull fylt med onde kultister -har dukket opp rundt våre fredelige byer! - - -Samle noe selskap, samle mat -og bekjemp deres dårlige ledere og akolytter. - - -Kanskje du til og med kan skaffe deg en av de -magisk infunderte gjenstandene?"#, - - - - // Inventory - "hud.bag.inventory": "{playername}'s Inventar", - "hud.bag.stats_title": "{playername}'s Statistikk", - "hud.bag.exp": "Exp", - "hud.bag.armor": "Utrustning", - "hud.bag.stats": "Statistikk", - "hud.bag.head": "Hode", - "hud.bag.neck": "Nakke", - "hud.bag.tabard": "Tabard", - "hud.bag.shoulders": "Skulder", - "hud.bag.chest": "Bryst", - "hud.bag.hands": "Hender", - "hud.bag.lantern": "Lykt", - "hud.bag.glider": "Glidefly", - "hud.bag.belt": "Belte", - "hud.bag.ring": "Ring", - "hud.bag.back": "Rygg", - "hud.bag.legs": "Ben", - "hud.bag.feet": "Føtter", - "hud.bag.mainhand": "Hovedhånd", - "hud.bag.offhand": "Tillegshånd", - - - // Map and Questlog - "hud.map.map_title": "Kart", - "hud.map.qlog_title": "Oppdrag", - - // Settings - "hud.settings.general": "Generell", - "hud.settings.none": "Ingen", - "hud.settings.press_behavior.toggle": "Veksle", - "hud.settings.press_behavior.hold": "Hold", - "hud.settings.help_window": "Hjelpevindu", - "hud.settings.debug_info": "Feilsøkingsinformasjon", - "hud.settings.tips_on_startup": "Tips-På-Oppstart", - "hud.settings.ui_scale": "UI-Skalering", - "hud.settings.relative_scaling": "Relativ Skalering", - "hud.settings.custom_scaling": "Tilpasset Skalering", - "hud.settings.crosshair": "Retikkel", - "hud.settings.transparency": "Gjennomsiktighet", - "hud.settings.hotbar": "Hurtigknappsbar", - "hud.settings.toggle_shortcuts": "Veksle Snarveier", - "hud.settings.buffs_skillbar": "Ikoner på Ferdighetsbar", - "hud.settings.buffs_mmap": "Ikoner på Minimap", - "hud.settings.toggle_bar_experience": "Veksle erfaringsbar", - "hud.settings.scrolling_combat_text": "Rullende kamptekst", - "hud.settings.single_damage_number": "Enkelt skadetall", - "hud.settings.cumulated_damage": "Kumulert skade", - "hud.settings.incoming_damage": "Innkommende skade", - "hud.settings.cumulated_incoming_damage": "Kumulert innkommende skade", - "hud.settings.speech_bubble": "Snakkeboble", - "hud.settings.speech_bubble_dark_mode": "Mørk modus for snakkeboble", - "hud.settings.speech_bubble_icon": "Snakkeboble ikon", - "hud.settings.energybar_numbers": "Energibar tall", - "hud.settings.values": "Verdier", - "hud.settings.percentages": "Prosentandeler", - "hud.settings.chat": "Chat", - "hud.settings.background_transparency": "Bakgrunnsgjennomsiktighet", - "hud.settings.chat_character_name": "Karakternavn i chat", - "hud.settings.loading_tips": "Oppstartsskjerm Tips", - - "hud.settings.pan_sensitivity": "Panoreringssensitivitet", - "hud.settings.zoom_sensitivity": "Zoomingssensitivitet", - "hud.settings.invert_scroll_zoom": "Inverter skrollezoom", - "hud.settings.invert_mouse_y_axis": "Inverter mus Y Aksen", - "hud.settings.enable_mouse_smoothing": "Kamerautjevning", - "hud.settings.free_look_behavior": "Frimodus oppførsel", - "hud.settings.auto_walk_behavior": "Autogåing oppførsel", - "hud.settings.stop_auto_walk_on_input": "Stopp autogåing på bevegelse", - - "hud.settings.view_distance": "Utsiktsavstand", - "hud.settings.sprites_view_distance": "Sprites utsiktsavstand", - "hud.settings.figures_view_distance": "Enhets utsiktsavstand", - "hud.settings.maximum_fps": "Maksimum FPS", - "hud.settings.fov": "Synsfelt (deg)", - "hud.settings.gamma": "Gamma", - "hud.settings.ambiance": "Stemning Brightness", - "hud.settings.antialiasing_mode": "Kantutjevningsmodus", - "hud.settings.cloud_rendering_mode": "Tegn-skyer-modus", - "hud.settings.fluid_rendering_mode": "Tegn-væske-modus", - "hud.settings.fluid_rendering_mode.cheap": "Billig", - "hud.settings.fluid_rendering_mode.shiny": "Skinnende", - "hud.settings.cloud_rendering_mode.minimal": "Minimal", - "hud.settings.cloud_rendering_mode.low": "Lav", - "hud.settings.cloud_rendering_mode.medium": "Medium", - "hud.settings.cloud_rendering_mode.high": "Høy", - "hud.settings.fullscreen": "Full skjerm", - "hud.settings.fullscreen_mode": "Fullskjermsmodus", - "hud.settings.fullscreen_mode.exclusive": "Eksklusiv", - "hud.settings.fullscreen_mode.borderless": "Uten kanter", - "hud.settings.particles": "Partikler", - "hud.settings.resolution": "Resolusjon", - "hud.settings.bit_depth": "Bit Dybde", - "hud.settings.refresh_rate": "Oppdateringsfrekvens", - "hud.settings.save_window_size": "Spar vindusstørrelse", - "hud.settings.lighting_rendering_mode": "Tegn-belysning-modus", - "hud.settings.lighting_rendering_mode.ashikhmin": "Type A - Høy ", - "hud.settings.lighting_rendering_mode.blinnphong": "Type B - Medium", - "hud.settings.lighting_rendering_mode.lambertian": "Type L - Billing", - "hud.settings.shadow_rendering_mode": "Tegn-skygger-modus", - "hud.settings.shadow_rendering_mode.none": "Ingen", - "hud.settings.shadow_rendering_mode.cheap": "Billig", - "hud.settings.shadow_rendering_mode.map": "Kart", - "hud.settings.shadow_rendering_mode.map.resolution": "Resolusjon", - "hud.settings.lod_detail": "Nivå med detaljer", - "hud.settings.save_window_size": "Lagre vindusstørrelse", - - - "hud.settings.music_volume": "Musikkvolum", - "hud.settings.sound_effect_volume": "Lydeffektvolum", - "hud.settings.audio_device": "Lydenhet", - - "hud.settings.awaitingkey": "Trykk på en tast...", - "hud.settings.unbound": "Ingen", - "hud.settings.reset_keybinds": "Tilbakestill til standardinnstillinger", - - "hud.social": "Andre spillere", - "hud.social.online": "Tilstede:", - "hud.social.friends": "Venner", - "hud.social.not_yet_available": "Ikke tilgjengelig enda", - "hud.social.faction": "Fraksjon", - "hud.social.play_online_fmt": "{nb_player} spiller(e) tilstede", - "hud.social.name": "Navn", - "hud.social.level": "Nivå", - "hud.social.zone": "Sone", - "hud.social.account": "Bruker", - - - "hud.crafting": "Håndverk", - "hud.crafting.recipes": "Oppskrifter", - "hud.crafting.ingredients": "Ingredienser:", - "hud.crafting.craft": "Lag", - "hud.crafting.tool_cata": "Krever:", - - "hud.group": "Gruppe", - "hud.group.invite_to_join": "[{name}] inviterte deg til gruppen deres!", - "hud.group.invite": "Inviter", - "hud.group.kick": "Spark", - "hud.group.assign_leader": "Tilordne leder", - "hud.group.leave": "Forlat Gruppe", - "hud.group.dead" : "Død", - "hud.group.out_of_range": "Ute av rekkevidde", - "hud.group.add_friend": "Legg til i vennelisten", - "hud.group.link_group": "Koble til grupper", - "hud.group.in_menu": "I Meny", - "hud.group.members": "Gruppemedlemmer", - - "hud.spell": "Trylleformel", - - "hud.free_look_indicator": "Frimodus aktiv. Trykk {key} for å skru av.", - "hud.auto_walk_indicator": "Autogåing aktiv", - - /// End HUD section - - - /// Start GameInput section - - "gameinput.primary": "Primær Angrep", - "gameinput.secondary": "Sekundær Angrep/Blokk/Sikt", - "gameinput.slot1": "Hurtigbar Luke 1", - "gameinput.slot2": "Hurtigbar Luke 2", - "gameinput.slot3": "Hurtigbar Luke 3", - "gameinput.slot4": "Hurtigbar Luke 4", - "gameinput.slot5": "Hurtigbar Luke 5", - "gameinput.slot6": "Hurtigbar Luke 6", - "gameinput.slot7": "Hurtigbar Luke 7", - "gameinput.slot8": "Hurtigbar Luke 8", - "gameinput.slot9": "Hurtigbar Luke 9", - "gameinput.slot10": "Hurtigbar Luke 10", - "gameinput.swaploadout": "Bytt utlastning", - "gameinput.togglecursor": "Veksle Musepeker", - "gameinput.help": "Veksle hjelpevindu", - "gameinput.toggleinterface": "Veksle Grensesnitt", - "gameinput.toggledebug": "Veksle FPS og feilsøkingsinfo", - "gameinput.screenshot": "Ta skjermbilde", - "gameinput.toggleingameui": "Veksle Navneskilt", - "gameinput.fullscreen": "Veksle fullskjerm", - "gameinput.moveforward": "Beveg frem", - "gameinput.moveleft": "Beveg venstre", - "gameinput.moveright": "Beveg høyre", - "gameinput.moveback": "Beveg bakover", - "gameinput.jump": "Hopp", - "gameinput.glide": "Glider", - "gameinput.roll": "Rull", - "gameinput.climb": "Klatre", - "gameinput.climbdown": "Klatre ned", - "gameinput.wallleap": "Veggsprang", - "gameinput.togglelantern": "Veksle lykt", - "gameinput.mount": "Monter", - "gameinput.chat": "Chat", - "gameinput.command": "Kommando", - "gameinput.escape": "Røm", - "gameinput.map": "Kart", - "gameinput.bag": "Bag", - "gameinput.social": "Sosial", - "gameinput.sit": "Sitt", - "gameinput.spellbook": "Trylleformler", - "gameinput.settings": "Innstillinger", - "gameinput.respawn": "Gjennopstå", - "gameinput.charge": "Lad opp", - "gameinput.togglewield": "Veksle Våpen", - "gameinput.interact": "Interaksjon", - "gameinput.freelook": "Frimodus", - "gameinput.autowalk": "Autogåing", - "gameinput.dance": "Dans", - "gameinput.select": "Velg enhet", - "gameinput.acceptgroupinvite": "Godta gruppeinvitasjon", - "gameinput.declinegroupinvite": "Avvis gruppeinvitasjon", - "gameinput.crafting": "Håndverk", - "gameinput.fly": "Fly", - "gameinput.sneak": "Snik", - "gameinput.swimdown": "Svøm nedover", - "gameinput.swimup": "Svøm oppover", - - /// End GameInput section - - - /// Start chracter selection section - "char_selection.loading_characters": "Laster inn karakterer...", - "char_selection.delete_permanently": "Slett denne karakteren permanent?", - "char_selection.deleting_character": "Sletter karakter...", - "char_selection.change_server": "Bytt server", - "char_selection.enter_world": "Gå inn i verden", - "char_selection.logout": "Logg ut", - "char_selection.create_new_character": "Lag ny karakter", - "char_selection.creating_character": "Skaper karakter...", - "char_selection.character_creation": "Karakterskaping", - - "char_selection.human_default": "Menneskestandarder", - "char_selection.level_fmt": "Nivå {level_nb}", - "char_selection.uncanny_valley": "Villmark", - "char_selection.plains_of_uncertainty": "Usikkerhetssletter", - "char_selection.beard": "Skjegg", - "char_selection.hair_style": "Hårstil", - "char_selection.hair_color": "Hårfarge", - "char_selection.eye_color": "Øyenfarge", - "char_selection.skin": "Hud", - "char_selection.eyeshape": "Øyedetaljer", - "char_selection.accessories": "Tilbehør", - "char_selection.create_info_name": "Karakteren din trenger et navn!", - - /// End character selection section - - - /// Start character window section - "character_window.character_name": "Karakternavn", - // Character stats - "character_window.character_stats": r#"Utholdenhet - -Fitness - -Viljestyrke - -Beskyttelse -"#, - /// End character window section - - - /// Start Escape Menu Section - "esc_menu.logout": "Logg ut", - "esc_menu.quit_game": "Avslutt spillet", - /// End Escape Menu Section - - /// Buffs and Debuffs - "buff.remove": "Klikk for å fjerne", - "buff.title.missing": "Mangler tittel", - "buff.desc.missing": "Mangler beskrivelse", - // Buffs - "buff.title.heal": "Helbred", - "buff.desc.heal": "Helbred over tid.", - "buff.title.potion": "Trylledrikk", - "buff.desc.potion": "Drikker...", - "buff.title.saturation": "Metning", - "buff.desc.saturation": "Helbred over tid fra forbruksvarer.", - // Debuffs - "buff.title.bleed": "Blør", - "buff.desc.bleed": "Påfører regelmessig skade.", - }, - - - vector_map: { - "loading.tips": [ - "Trykk 'G' for å tenne lykten.", - "Trykk 'F1' for å se alle standard tastebindinger.", - "Du kan skrive /say eller /s for å bare chatte med spillere rett rundt deg.", - "Du kan skrive /region eller /r for å bare chatte med spillere et par hundre blokker rundt deg.", - "Du kan skrive /group eller /g for å bare chatte med spillere i din nåværende gruppe.", - "For å sende private meldinger skriv /tell etterfulgt av et spillernavn og meldingen din.", - "NPCer med samme nivå kan ha forskjellige problemer.", - "Hold øye med mat, kister og andre godsaker spredt over hele verden!", - "Inventar fylt med mat? Prøv å lage bedre mat med det!", - "Lurer du på hva du kan gjøre? Fangehull er merket med brune flekker på kartet!", - "Ikke glem å justere grafikken for systemet ditt. Trykk på 'N' for å åpne innstillingene.", - "Å spille med andre er gøy! Trykk 'O' for å se hvem som er online.", - "En NPC med en hodeskalle under helsebaren er ganske kraftig sammenlignet med deg selv.", - "Trykk 'J' for å danse. Fest!", - "Trykk 'L-Shift' for å åpne Glider og erobre himmelen.", - "Veloren er fortsatt i Pre-Alfa. Vi gjør vårt beste for å forbedre det hver dag!", - "Hvis du vil bli med i Dev-teamet eller bare ta en prat med oss, bli med i Discord-Serveren.", - "Du kan veksle for å vise mengden helse på helselinjen i innstillingene.", - "For å se statistikken din, klikk på 'Statistikk' -knappen i beholdningen.", - ], - "npc.speech.villager_under_attack": [ - "Hjelp, jeg er under angrep!", - "Hjelp! Jeg er under angrep!", - "Au! Jeg er under angrep!", - "Au! Jeg er under angrep! Hjelp!", - "Hjelp meg! Jeg er under angrep!", - "Jeg er under angrep! Hjelp!", - "Jeg er under angrep! Hjelp meg!", - "Hjelp!", - "Hjelp! Hjelp!", - "Hjelp! Hjelp! Hjelp!", - "Jeg er under angrep!", - "AAAHHH! Jeg er under angrep!", - "AAAHHH! Jeg er under angrep! Hjelp!", - "Hjelp! Vi er under angrep!", - "Hjelp! Morder!", - "Hjelp! Det er en morder på frifot!", - "Hjelp! De prøver å drepe meg!", - "Vakter, jeg er under angrep!", - "Vakter! Jeg er under angrep!", - "Jeg er under angrep! Vakter!", - "Hjelp! Vakter! Jeg er under angrep!", - "Vakter! Kom raskt!", - "Vakter! Vakter!", - "Vakter! Det er en skurk som angriper meg!", - "Vakter, drep denne onde skurken!", - "Vakter! Det er en morder!", - "Vakter! Hjelp meg!", - "Du kommer ikke unna med dette! Vakter!", - "Du fiende!", - "Hjelp meg!", - "Hjelp! Vœr så snill!", - "Ouch! Vakter! Hjelp!", - "De etterfølger etter meg!", - "Hjelp! Hjelp! Jeg blir undertrykt!", - "Ah, nå ser vi volden som er bygd inn i systemet.", - "Det er bare en ripe!", - "Slutt med det!", - "Hva har jeg noensinne gjort mot deg?!", - "Vær så snill slutt å angripe meg!", - "Hei! Se for deg hvor du peker den tingen!", - "Avskyelige krek, bort med deg!", - "Stop det! Gå vekk!", - "Nå gjør du meg sint!", - "Oi! Hvem tror du at du er?!", - "Jeg tar hodet ditt for det!", - "Stop, vær så snill! Jeg har ingenting av verdi på meg!", - "Jeg får broren min til å ta deg, han er større enn meg!", - "Neiii, jeg sier det til mamma!", - "Forbann deg!", - "Vœr så snill ikke gjør det.", - "Det var ikke veldig snilt!", - "Våpnet ditt funker, du kan ta det vekk nå!", - "Bespar meg!", - "Vær så snill, jeg har familie!", - "Jeg er for ung til å dø!", - "Kan vi snakke om dette?", - "Vold er aldri svaret!", - "I dag ser ut til å være en dårlig dag...", - "Hei, det kjente jeg!", - "Eek!", - "Hvor uhøflig!", - "Stop, jeg ber deg!", - "Smitte beslage deg!", - "Dette er ikke gøy.", - "Hvordan våger du?!", - "Du kommer til å betale for dette!", - "Fortsett med dette og du kommer til å be unnskyld!", - "Ikke få meg til å banke deg!", - "Dette må være en misforståelse!", - "Du trenger ikke å gjøre dette!", - "Gå vekk, skurk!", - "Det gjorde vondt!", - "Hvorfor gjorde du det?", - "Etter åndene, forsvinn!", - "Du må ha forvekslet meg med noen andre!", - "Jeg fortjener ikke dette!", - "Vær så snill ikke gjør dette igjen!", - "Vakter, kast dette mennesket i havet!", - "Jeg får min tarasque til å etterfølge deg!", - ], - } -) diff --git a/assets/voxygen/i18n/pl_PL/_manifest.ron b/assets/voxygen/i18n/pl_PL/_manifest.ron new file mode 100644 index 0000000000..01c06b652f --- /dev/null +++ b/assets/voxygen/i18n/pl_PL/_manifest.ron @@ -0,0 +1,30 @@ +/// Localization for Polish / Tłumaczenia dla języka polskiego +( + metadata: ( + language_name: "Polish", + language_identifier: "pl_PL", + ), + convert_utf8_to_ascii: false, + fonts: { + "opensans": Font ( + asset_key: "voxygen.font.OpenSans-Regular", + scale_ratio: 1.0, + ), + "metamorph": Font ( + asset_key: "voxygen.font.Metamorphous-Regular", + scale_ratio: 1.0, + ), + "alkhemi": Font ( + asset_key: "voxygen.font.Alkhemikal", + scale_ratio: 1.0, + ), + "wizard": Font ( + asset_key: "voxygen.font.wizard", + scale_ratio: 1.0, + ), + "cyri": Font ( + asset_key: "voxygen.font.OpenSans-Regular", + scale_ratio: 1.0, + ), + } +) diff --git a/assets/voxygen/i18n/PL/buff.ron b/assets/voxygen/i18n/pl_PL/buff.ron similarity index 94% rename from assets/voxygen/i18n/PL/buff.ron rename to assets/voxygen/i18n/pl_PL/buff.ron index f0c8fd9ba8..283575691e 100644 --- a/assets/voxygen/i18n/PL/buff.ron +++ b/assets/voxygen/i18n/pl_PL/buff.ron @@ -26,7 +26,7 @@ "buff.desc.burn": "Palisz się żywcem", // Buffs stats "buff.stat.health": "Odnawia {str_total} Zdrowia", - "buff.stat.increase_max_stamina": "Podnosi Maksymalną Wytrzymałość o {strength}", + "buff.stat.increase_max_energy": "Podnosi Maksymalną Wytrzymałość o {strength}", "buff.stat.increase_max_health": "Podnosi Maksymalne Zdrowie o {strength}", "buff.stat.invulnerability": "Daje nietykalność", // Text diff --git a/assets/voxygen/i18n/PL/char_selection.ron b/assets/voxygen/i18n/pl_PL/char_selection.ron similarity index 100% rename from assets/voxygen/i18n/PL/char_selection.ron rename to assets/voxygen/i18n/pl_PL/char_selection.ron diff --git a/assets/voxygen/i18n/PL/common.ron b/assets/voxygen/i18n/pl_PL/common.ron similarity index 100% rename from assets/voxygen/i18n/PL/common.ron rename to assets/voxygen/i18n/pl_PL/common.ron diff --git a/assets/voxygen/i18n/PL/esc_menu.ron b/assets/voxygen/i18n/pl_PL/esc_menu.ron similarity index 100% rename from assets/voxygen/i18n/PL/esc_menu.ron rename to assets/voxygen/i18n/pl_PL/esc_menu.ron diff --git a/assets/voxygen/i18n/PL/gameinput.ron b/assets/voxygen/i18n/pl_PL/gameinput.ron similarity index 100% rename from assets/voxygen/i18n/PL/gameinput.ron rename to assets/voxygen/i18n/pl_PL/gameinput.ron diff --git a/assets/voxygen/i18n/PL/hud/bag.ron b/assets/voxygen/i18n/pl_PL/hud/bag.ron similarity index 97% rename from assets/voxygen/i18n/PL/hud/bag.ron rename to assets/voxygen/i18n/pl_PL/hud/bag.ron index 819ff405d4..1ef6272b84 100644 --- a/assets/voxygen/i18n/PL/hud/bag.ron +++ b/assets/voxygen/i18n/pl_PL/hud/bag.ron @@ -24,7 +24,7 @@ "hud.bag.offhand": "Ręka poboczna", "hud.bag.bag": "Torba", "hud.bag.health": "Zdrowie", - "hud.bag.stamina": "Energia", + "hud.bag.energy": "Energia", "hud.bag.combat_rating": "Combat Rating", "hud.bag.protection": "Ochrona", "hud.bag.combat_rating_desc": "Liczone na postawie Twojego\nekwipunku i zdrowia.", diff --git a/assets/voxygen/i18n/PL/hud/char_window.ron b/assets/voxygen/i18n/pl_PL/hud/char_window.ron similarity index 100% rename from assets/voxygen/i18n/PL/hud/char_window.ron rename to assets/voxygen/i18n/pl_PL/hud/char_window.ron diff --git a/assets/voxygen/i18n/PL/hud/chat.ron b/assets/voxygen/i18n/pl_PL/hud/chat.ron similarity index 100% rename from assets/voxygen/i18n/PL/hud/chat.ron rename to assets/voxygen/i18n/pl_PL/hud/chat.ron diff --git a/assets/voxygen/i18n/PL/hud/crafting.ron b/assets/voxygen/i18n/pl_PL/hud/crafting.ron similarity index 100% rename from assets/voxygen/i18n/PL/hud/crafting.ron rename to assets/voxygen/i18n/pl_PL/hud/crafting.ron diff --git a/assets/voxygen/i18n/PL/hud/group.ron b/assets/voxygen/i18n/pl_PL/hud/group.ron similarity index 100% rename from assets/voxygen/i18n/PL/hud/group.ron rename to assets/voxygen/i18n/pl_PL/hud/group.ron diff --git a/assets/voxygen/i18n/PL/hud/map.ron b/assets/voxygen/i18n/pl_PL/hud/map.ron similarity index 100% rename from assets/voxygen/i18n/PL/hud/map.ron rename to assets/voxygen/i18n/pl_PL/hud/map.ron diff --git a/assets/voxygen/i18n/PL/hud/misc.ron b/assets/voxygen/i18n/pl_PL/hud/misc.ron similarity index 100% rename from assets/voxygen/i18n/PL/hud/misc.ron rename to assets/voxygen/i18n/pl_PL/hud/misc.ron diff --git a/assets/voxygen/i18n/PL/hud/sct.ron b/assets/voxygen/i18n/pl_PL/hud/sct.ron similarity index 100% rename from assets/voxygen/i18n/PL/hud/sct.ron rename to assets/voxygen/i18n/pl_PL/hud/sct.ron diff --git a/assets/voxygen/i18n/PL/hud/hud_settings.ron b/assets/voxygen/i18n/pl_PL/hud/settings.ron similarity index 97% rename from assets/voxygen/i18n/PL/hud/hud_settings.ron rename to assets/voxygen/i18n/pl_PL/hud/settings.ron index f37f2a7957..916aeba80c 100644 --- a/assets/voxygen/i18n/PL/hud/hud_settings.ron +++ b/assets/voxygen/i18n/pl_PL/hud/settings.ron @@ -13,7 +13,7 @@ "hud.settings.relative_scaling": "Relatywne skalowanie", "hud.settings.custom_scaling": "Niestandardowe skalowanie", "hud.settings.crosshair": "Celownik", - "hud.settings.transparency": "Przezroczystość", + "hud.settings.opacity": "Przezroczystość", "hud.settings.hotbar": "Pasek skrótów", "hud.settings.toggle_shortcuts": "Przełącz skróty", "hud.settings.buffs_skillbar": "Wzmocnienia na pasku umiejętności", @@ -31,7 +31,7 @@ "hud.settings.values": "Wartości", "hud.settings.percentages": "Procenty", "hud.settings.chat": "Czat", - "hud.settings.background_transparency": "Przezroczystość tła", + "hud.settings.background_opacity": "Przezroczystość tła", "hud.settings.chat_character_name": "Imiona postaci na czacie", "hud.settings.loading_tips": "Porady na ekranie ładowania", "hud.settings.reset_interface": "Zresetuj ustawienia", diff --git a/assets/voxygen/i18n/PL/skills.ron b/assets/voxygen/i18n/pl_PL/hud/skills.ron similarity index 97% rename from assets/voxygen/i18n/PL/skills.ron rename to assets/voxygen/i18n/pl_PL/hud/skills.ron index cf9473ea72..0186674aab 100644 --- a/assets/voxygen/i18n/PL/skills.ron +++ b/assets/voxygen/i18n/pl_PL/hud/skills.ron @@ -9,8 +9,8 @@ // General "hud.skill.inc_health_title": "Zwiększ Zdrowie", "hud.skill.inc_health": "Zwiększa Maksymalne Zdrowie o {boost}{SP}", - "hud.skill.inc_stam_title": "Zwiększ Wytrzymałość", - "hud.skill.inc_stam": "Zwiększ Maksymalną Wytrzymałość o {boost}{SP}", + "hud.skill.inc_energy_title": "Zwiększ Wytrzymałość", + "hud.skill.inc_energy": "Zwiększ Maksymalną Wytrzymałość o {boost}{SP}", "hud.skill.unlck_sword_title": "Odblokuj miecz", "hud.skill.unlck_sword": "Odblokowuje umiejętności powiązane z mieczem{SP}", "hud.skill.unlck_axe_title": "Odblokuj topór", @@ -25,8 +25,8 @@ "hud.skill.unlck_sceptre": "Odblokowuje umiejętności powiązane z kosturem druida{SP}", "hud.skill.dodge_title": "Unik (przewrót)", "hud.skill.dodge": "Umożliwia unikanie ataków przeciwników (dodaje klatki nieśmiertelności){SP}", - "hud.skill.roll_stamina_title": "Przewrót - koszt", - "hud.skill.roll_stamina": "Przewrót kosztuje {boost}% mniej wytrzymałości{SP}", + "hud.skill.roll_energy_title": "Przewrót - koszt", + "hud.skill.roll_energy": "Przewrót kosztuje {boost}% mniej wytrzymałości{SP}", "hud.skill.roll_speed_title": "Przewrót - prędkość", "hud.skill.roll_speed": "Przewrót jest {boost}% szybszy{SP}", "hud.skill.roll_dur_title": "Przewrót - długość (czas)", @@ -93,8 +93,8 @@ "hud.skill.st_flamethrower_damage" : "Zwiększa obrażenia o {boost}%{SP}", "hud.skill.st_explosion_radius_title" : "Eksplozja - rozmiar", "hud.skill.st_explosion_radius" : "Rozmiar ma znaczenie, szczególnie gdy walczysz z wieloma przeciwnikami,\nzwiększa wielkość eksplozji o {boost}%{SP}", - "hud.skill.st_stamina_regen_title" : "Regeneracja Wytrzymałości", - "hud.skill.st_stamina_regen" : "Zwiększa regenerację Wytrzymałości o {boost}%{SP}", + "hud.skill.st_energy_regen_title" : "Regeneracja Wytrzymałości", + "hud.skill.st_energy_regen" : "Zwiększa regenerację Wytrzymałości o {boost}%{SP}", "hud.skill.st_fireball_title" : "Kula ognia", "hud.skill.st_fireball" : "Walisz z różdżki wybuchającymi fajerbolami", "hud.skill.st_damage_title" : "Obrażenia", diff --git a/assets/voxygen/i18n/PL/hud/social.ron b/assets/voxygen/i18n/pl_PL/hud/social.ron similarity index 100% rename from assets/voxygen/i18n/PL/hud/social.ron rename to assets/voxygen/i18n/pl_PL/hud/social.ron diff --git a/assets/voxygen/i18n/PL/hud/trade.ron b/assets/voxygen/i18n/pl_PL/hud/trade.ron similarity index 100% rename from assets/voxygen/i18n/PL/hud/trade.ron rename to assets/voxygen/i18n/pl_PL/hud/trade.ron diff --git a/assets/voxygen/i18n/PL/main.ron b/assets/voxygen/i18n/pl_PL/main.ron similarity index 69% rename from assets/voxygen/i18n/PL/main.ron rename to assets/voxygen/i18n/pl_PL/main.ron index 0da2e81d0e..86e8778198 100644 --- a/assets/voxygen/i18n/PL/main.ron +++ b/assets/voxygen/i18n/pl_PL/main.ron @@ -66,5 +66,26 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "Naciśnij 'G' by zapalić latarnię.", + "Naciśnij 'F1' by zobaczyć podstawową klawiszologię.", + "Wpisz /say lub /s by rozmawiać tylko z pobliskimi graczami.", + "Wpisz /region lub /r by ograniczyć zasięg wiadomości do kilkuset bloków od Ciebie.", + "Administratorzy mogą używać komendy /build by wejść w tryb budowania.", + "Możesz użyć /group lub /g by rozmawiać z graczami w grupie.", + "Aby wysłać wiadomość prywatną, użyj /tell po czym dodaj nazwę docelowego gracza i wiadomość.", + "Rozglądaj się za jedzeniem, skrzyniami oraz skarbami - są one rozrzucone po całym świecie!", + "Ekwipunek zapełniony jedzeniem? Ugotuj z tego lepsze jedzenie!", + "Zastanawiasz się nad kolejnym wypadem? Spróbuj któryś z wielu lochów na mapie!", + "Nie zapomnij o dostosowaniu ustawień graficznych. Wciśnij 'N' by otworzyć ustawienia.", + "Gra jest lepsza z innymi! Wciśnij 'O' by sprawdzić kto jest zalogowany.", + "Użyj 'J' by zatańczyć :)", + "Użyj 'L-Shift' by wyciągnąć lotnię! Spokojnie, nie jest lepiona woskiem.", + "Veloren dalej jest w fazie Pre-Alpha. Staramy się ulepszać go dzień w dzień!", + "Jeśli chcesz pomóc w rozwijaniu projektu, albo porozmawiać z nami - wpadnij na naszego Discorda [EN]", + "Możesz przełączać typ paska zdrowia w opcjach.", + "Usiądź obok ogniska (używając 'K') by powoli wyleczyć się z obrażeń.", + "Potrzebujesz więcej slotów w ekwipunku, albo nowego uzbrojenia? Wciśnij 'C' aby otworzyć menu tworzenia!", + ], } ) diff --git a/assets/voxygen/i18n/PL/_manifest.ron b/assets/voxygen/i18n/pl_PL/npc.ron similarity index 76% rename from assets/voxygen/i18n/PL/_manifest.ron rename to assets/voxygen/i18n/pl_PL/npc.ron index 204f497d07..d2213536c3 100644 --- a/assets/voxygen/i18n/PL/_manifest.ron +++ b/assets/voxygen/i18n/pl_PL/npc.ron @@ -1,58 +1,9 @@ /// Localization for Polish / Tłumaczenia dla języka polskiego ( - metadata: ( - language_name: "Polish", - language_identifier: "PL", - ), - convert_utf8_to_ascii: false, - fonts: { - "opensans": Font ( - asset_key: "voxygen.font.OpenSans-Regular", - scale_ratio: 1.0, - ), - "metamorph": Font ( - asset_key: "voxygen.font.Metamorphous-Regular", - scale_ratio: 1.0, - ), - "alkhemi": Font ( - asset_key: "voxygen.font.Alkhemikal", - scale_ratio: 1.0, - ), - "wizard": Font ( - asset_key: "voxygen.font.wizard", - scale_ratio: 1.0, - ), - "cyri": Font ( - asset_key: "voxygen.font.OpenSans-Regular", - scale_ratio: 1.0, - ), - }, - string_map: { }, vector_map: { - "loading.tips": [ - "Naciśnij 'G' by zapalić latarnię.", - "Naciśnij 'F1' by zobaczyć podstawową klawiszologię.", - "Wpisz /say lub /s by rozmawiać tylko z pobliskimi graczami.", - "Wpisz /region lub /r by ograniczyć zasięg wiadomości do kilkuset bloków od Ciebie.", - "Administratorzy mogą używać komendy /build by wejść w tryb budowania.", - "Możesz użyć /group lub /g by rozmawiać z graczami w grupie.", - "Aby wysłać wiadomość prywatną, użyj /tell po czym dodaj nazwę docelowego gracza i wiadomość.", - "Rozglądaj się za jedzeniem, skrzyniami oraz skarbami - są one rozrzucone po całym świecie!", - "Ekwipunek zapełniony jedzeniem? Ugotuj z tego lepsze jedzenie!", - "Zastanawiasz się nad kolejnym wypadem? Spróbuj któryś z wielu lochów na mapie!", - "Nie zapomnij o dostosowaniu ustawień graficznych. Wciśnij 'N' by otworzyć ustawienia.", - "Gra jest lepsza z innymi! Wciśnij 'O' by sprawdzić kto jest zalogowany.", - "Użyj 'J' by zatańczyć :)", - "Użyj 'L-Shift' by wyciągnąć lotnię! Spokojnie, nie jest lepiona woskiem.", - "Veloren dalej jest w fazie Pre-Alpha. Staramy się ulepszać go dzień w dzień!", - "Jeśli chcesz pomóc w rozwijaniu projektu, albo porozmawiać z nami - wpadnij na naszego Discorda [EN]", - "Możesz przełączać typ paska zdrowia w opcjach.", - "Usiądź obok ogniska (używając 'K') by powoli wyleczyć się z obrażeń.", - "Potrzebujesz więcej slotów w ekwipunku, albo nowego uzbrojenia? Wciśnij 'C' aby otworzyć menu tworzenia!", - ], "npc.speech.villager": [ "Co za piękny dzień!", "Jak się masz?", diff --git a/assets/voxygen/i18n/pt_BR/_manifest.ron b/assets/voxygen/i18n/pt_BR/_manifest.ron index dcc329c99f..9338cd156d 100644 --- a/assets/voxygen/i18n/pt_BR/_manifest.ron +++ b/assets/voxygen/i18n/pt_BR/_manifest.ron @@ -28,206 +28,5 @@ asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", scale_ratio: 1.0, ), - }, - - string_map: { - - }, - - vector_map: { - "loading.tips": [ - "Pressione 'G' para acender sua lâmpada.", - "Pressione 'F1' para visualizar suas teclas de atalho.", - "Você pode digitar /say ou /s para conversar apenas com jogadores próximos a você.", - "Você pode digitar /region ou /r para conversar apenas com jogadores a poucas centenas de blocos de você.", - "Admins podem usar o comando /build para entrar no modo construção.", - "Você pode digitar /group ou /g para conversar apenas com jogadores do seu grupo.", - "Para enviar mensagens privadas digite /tell seguido do nome do jogador desejado.", - "Busque sempre comida, baús e outros espólios espalhados pelo mundo!", - "Inventário cheio de comidas? Tente criar alimentos melhores com elas!", - "Imaginando o que há pra fazer? Cavernas estão marcadas com pontos marrons no mapa!", - "Não esqueça de ajustar as configurações gráficas. Pressione 'N' para abrir as configurações.", - "Jogar com outros é divertido! Pressione 'O' para ver quem está online.", - "Pressione 'J' para dançar. Hora da festa!", - "Pressione 'Shift Esquerdo' para abrir o Planador e conquistar os céus.", - "Veloren ainda está no Pre-Alpha. Estamos nos empenhando ao máximo para melhorar a cada dia!", - "Se quiser ingressar no time de Desenvolvedores ou apenas conversar conosco, acesse o nosso servidor do Discord.", - "Você pode exibir sua saúde em sua barra de vida nas opções.", - "Sente ao redor de uma fogueira (usando a tecla 'K') para lentamente se recuperar de lesões.", - "Precisa de uma mochila maior para sua jornada? Pressione 'C' para abrir o menu de criação!", - ], - "npc.speech.villager": [ - "Não é um dia tão lindo?", - "Como você está?", - "Uma ótima manhã para você!", - "Fico imaginando o que um Catobelpas pensa enquanto come grama.", - "O que você está achando do clima?", - "Só de imaginar o que há em todas as masmorras me dá arrepios. Espero que alguém as limpe.", - "Adoraria explorar algumas cavernas assim que eu ficar mais forte.", - "Você viu meu gato?", - "Já ouviu falar dos ferozes Land Sharks? Ouvi dizer que eles vivem nos desertos.", - "Dizem que diversos tipos de gemas brilhantes podem ser encontradas nas cavernas.", - "Sou viciado em queijo!", - "Quer entrar? Venha, vamos comer um queijo!", - "Dizem que cogumelos são bons para saude. Nunca comi.", - "Não esqueça dos biscoitos!", - "Eu adoro queijo anão(dwarven cheese). Gostaria de saber fazer.", - "Fico imaginando o que há depois das montanhas.", - "Espero um dia conseguir fazer meu planador.", - "Gostaria de ver o meu jardim? Tá bom, talvez outro dia então.", - "Um dia adoravel para um passeio na floresta!", - "Ser ou não ser? Acho que serei um fazendeiro.", - "Nossa aldeia é a melhor, não acha?.", - "O que você acha que faz os Restos Brilhantes(Glowing Remains) brilharem?.", - "Acho que está na hora dum segundo café da manhã!", - "Já capturou uma libélula(firefly)?", - "Não consigo entender de onde vem tantos Sauroks.", - "Gostaria que alguém conseguisse espantar os lobos (wolves) da aldeia.", - "Tive um sonho magnífico sobre queijo ontem. O que será que significa?", - "Deixei um pouco de queijo com meu irmão. Agora não sei se existe ou não. Eu chamo de queijo de Schrödinger.", - "Deixei um pouco de queijo com minha irmã. Agora não sei se existe ou não. Eu chamo de queijo de Schrödinger.", - "Alguém deveria fazer algo sobre esses cultistas. De preferência, não eu.", - "Espero que chova logo. Seria bom para as colheitas.", - "Eu amo mel! E eu odeio abelhas. ", - "Eu quero viajar no mundo um dia. Deve haver mais vida do que nesta aldeia. ", - ], - "npc.speech.villager_decline_trade": [ - "Desculpe, não tenho nada para trocar.", - "Troca? Como se eu tivesse algo que pode interessar a você.", - "Minha casa é minha, não vou trocá-la por nada.", - ], - "npc.speech.merchant_advertisement": [ - "Está interessado em fazer uma troca comigo?", - "Você quer negociar comigo?", - "Eu tenho muitos produtos, você quer dar uma olhada?" - ], - "npc.speech.merchant_busy": [ - "Ei, espere sua vez.", - "Espere, por favor. Sou apenas uma pessoa.", - "Você vê a outra pessoa na sua frente?", - "Só um momento, deixe-me terminar.", - "Não fure a fila.", - "Estou ocupado, volte mais tarde." - ], - "npc.speech.merchant_trade_successful": [ - "Obrigado por negociar comigo!", - "Obrigado!", - ], - "npc.speech.merchant_trade_declined": [ - "Talvez outra hora, tenha um bom dia!", - "Que pena, talvez da próxima vez, então! " - ], - "npc.speech.villager_cultist_alarm": [ - "Olhe! Há um cultista à solta!", - "Às armas! Os cultistas estão atacando!", - "Como ousam os cultistas atacar nossa aldeia!", - "Morte aos cultistas!", - "Cultistas não serão tolerados aqui!", - "Cultista assassino!", - "Prove o gume da minha espada, seu cultista sujo!", - "Nada pode limpar o sangue de suas mãos, cultista!", - "Com mil milhões de macacos e raios e coriscos! Um cultista entre nós!", - "Com um milhão de tubarões! Um cultista entre nós!", - "Os males deste cultista estão prestes a acabar!", - "Este cultista é meu!", - "Prepare-se para encontrar o seu criador, oh sórdido cultista!", - "Vejo um cultista! Pegue eles!", - "Vejo um cultista! Atacar!", - "Vejo um cultista! Não o deixe escapar!", - "O mais honrado cultista se importaria com alguma MORTE?!", - "Nunca perdoe! Nunca se esqueça! Morra, cultista!", - "Morra, cultista!", - "Seu reinado de terror chegará ao fim!", - "Você pagará por tudo que fez!", - "Não aceitamos bem seus tipos por aqui.", - "Você deveria ter ficado no subsolo!", - ], - "npc.speech.villager_under_attack": [ - "Ajuda, Estou sendo atacado!", - "Ajuda! Estou sendo atacado!", - "Ouch! Estou sendo atacado!", - "Ouch! Estou sendo atacado! Ajuda!", - "Me ajude! Estou sendo atacado!", - "Estou sendo atacado! Ajuda!", - "Estou sendo atacado! Me ajude!", - "Ajuda!", - "Ajuda! Ajuda!", - "Ajuda! Ajuda! Ajuda!", - "Estou sendo atacado!", - "AAAHHH! Estou sendo atacado!", - "AAAHHH! Estou sendo atacado! Ajuda!", - "Ajuda! Estamos sendo atacado!", - "Ajuda! Assassino!", - "Ajuda! Há um assassinado em andamento!", - "Ajuda! Estão tentando me matar!", - "Guardas, Estou sendo atacado!", - "Guardas! Estou sendo atacado!", - "Estou sendo atacado! Guardas!", - "Ajuda! Guardas! Estou sendo atacado!", - "Guardas! Depressa!", - "Guardas! Guardas!", - "Guardas! Um vilão está me atacando!", - "Guardas, acabem com este maldito vilão!", - "Guardas! Um assasino a solta!", - "Guardas! Me ajude!", - "Você não vai se safar dessa! Guardas!", - "Maldito Vilão!", - "Me ajude!", - "Ajuda! Por favor!", - "Ouch! Guardas! Ajuda!", - "Estão atrás de mim!", - "Ajuda! Ajuda! Estou sendo repreendido", - "Ah, agora vemos a violência inerente ao sistema.", - "Só um arranhão!", - "Pare com isso!", - "O que eu fiz para você?!", - "Por favor, pare de me atacar!", - "Hey! Cuida pra onde você aponta essa coisa!", - "Desgraçado hediondo, vou acabar com você!", - "Pare com isso! Vá embora!", - "Você está me deixando louco!", - "Ow! Quem você pensa que é?!", - "Arrancarei sua cabeça por isso!", - "Pare, por favor! Não levo nada de valor!", - "Vou mandar meu irmão em você, ele é maior que eu!", - "Nãooo, Vou contar pra minha mãe!", - "Te amaldiçoo!", - "Por favor não faça isso.", - "Isso não foi muito legal!", - "Sua arma funciona, pode guardar ela agora!", - "Me poupe!", - "Por favor, tenho uma família!", - "Sou muito jovem para morrer!", - "Podemos conversar sobre isso?", - "Violência nunca é a resposta!", - "Hoje foi um péssimo dia...", - "Ei, isso dói!", - "Eek!", - "Que rude!", - "Pare, eu imploro!", - "Que você adoeça!", - "Isso não é engraçado.", - "Como ousa?!", - "Você pagará por isso!", - "Continue assim e irá se arrepender!", - "Não me faça te machucar!", - "Deve haver algum engano!", - "Não precisa fazer isso!", - "Morre, Diabo!", - "Isso Dói!", - "Porque você faria isso?", - "Pelos espíritos, Pare!", - "Você deve ter me confundido com alguém!", - "Eu não mereço isso!", - "Por favor, não faça isso novamente.", - "Guardas, joguem este monstro no lago!", - "Vou mandar meu tarrasque em você!", - "Porque eeeeeeeeeeeeeu?", - ], - "npc.speech.villager_enemy_killed": [ - "Destruí meu inimigo!", - "Finalmente em paz!", - "... agora, o que eu estava fazendo?", - ] } ) diff --git a/assets/voxygen/i18n/pt_BR/buff.ron b/assets/voxygen/i18n/pt_BR/buff.ron index 41010940af..19ae344942 100644 --- a/assets/voxygen/i18n/pt_BR/buff.ron +++ b/assets/voxygen/i18n/pt_BR/buff.ron @@ -38,7 +38,7 @@ "buff.desc.ensnared": "Trepadeiras agarram suas pernas, impedindo seus movimentos.", // Buffs stats "buff.stat.health": "Restaura {str_total} de Vida", - "buff.stat.increase_max_stamina": "Aumenta a Stamina Máxima em {strength}", + "buff.stat.increase_max_energy": "Aumenta a Stamina Máxima em {strength}", "buff.stat.increase_max_health": "Aumenta a Vida Máxima em {strength}", "buff.stat.invulnerability": "Concede invulnerabilidade", // Text diff --git a/assets/voxygen/i18n/pt_BR/common.ron b/assets/voxygen/i18n/pt_BR/common.ron index 4efcb77603..eb57874367 100644 --- a/assets/voxygen/i18n/pt_BR/common.ron +++ b/assets/voxygen/i18n/pt_BR/common.ron @@ -62,6 +62,8 @@ O cliente está atualizado?"#, "common.weapons.axe": "Machado", "common.weapons.sword": "Espada", + "common.weapons.greatsword": "Espada Larga", + "common.weapons.shortswords": "Espada Curta", "common.weapons.staff": "Cajado", "common.weapons.bow": "Arco", "common.weapons.hammer": "Martelo", @@ -82,7 +84,7 @@ O cliente está atualizado?"#, "common.kind.modular_component": "Componente Modular", "common.kind.glider": "Planador", "common.kind.consumable": "Consumível", - "common.kind.throwable": "Pode ser jogado", + "common.kind.throwable": "Arremessável", "common.kind.utility": "Utilitário", "common.kind.ingredient": "Ingrediente", "common.kind.lantern": "Lanterna", diff --git a/assets/voxygen/i18n/pt_BR/gameinput.ron b/assets/voxygen/i18n/pt_BR/gameinput.ron index 275b83573a..527cad5f42 100644 --- a/assets/voxygen/i18n/pt_BR/gameinput.ron +++ b/assets/voxygen/i18n/pt_BR/gameinput.ron @@ -21,6 +21,8 @@ "gameinput.help": "Mostar/Ocultar Janela de Ajuda", "gameinput.toggleinterface": "Mostar/Ocultar Interface", "gameinput.toggledebug": "Mostar/Ocultar informações de depuração e FPS", + "gameinput.toggle_egui_debug": "Mostrar/Ocultar informações de depuração EGUI", + "gameinput.togglechat": "Mostrar/Ocultar Chat", "gameinput.screenshot": "Capturar Tela", "gameinput.toggleingameui": "Mostar/Ocultar Nametags", "gameinput.fullscreen": "Alternar Tela Cheia", @@ -65,6 +67,7 @@ "gameinput.swimup": "Emergir", "gameinput.mapzoomin": "Aumentar zoom do mapa", "gameinput.mapzoomout": "Reduzir zoom do mapa", + "gameinput.greet": "Saudação", }, diff --git a/assets/voxygen/i18n/pt_BR/hud/bag.ron b/assets/voxygen/i18n/pt_BR/hud/bag.ron index 175bc7e44e..47285c2028 100644 --- a/assets/voxygen/i18n/pt_BR/hud/bag.ron +++ b/assets/voxygen/i18n/pt_BR/hud/bag.ron @@ -30,16 +30,16 @@ "hud.bag.swap_equipped_weapons_desc": "Pressione {key}", "hud.bag.bag": "Mochila", "hud.bag.health": "Vida", - "hud.bag.stamina": "Stamina", + "hud.bag.energy": "Stamina", "hud.bag.combat_rating": "Pontuação de Combate", "hud.bag.protection": "Proteção", "hud.bag.stun_res": "Resistência à paralisia", "hud.bag.combat_rating_desc": "Calculado por seu\nequipamento e vida.", "hud.bag.protection_desc": "Redução de dano por armadura", "hud.bag.stun_res_desc": "Resistência à paralisia provocada por golpes consecutivos.\nRegenera como Stamina.", - "hud.bag.sort_by_name": "Ordenar por Nome", - "hud.bag.sort_by_quality": "Ordenar por Qualidade", - "hud.bag.sort_by_category": "Ordenar por Categoria", + "hud.bag.sort_by_name": "Ordenar por Nome", + "hud.bag.sort_by_quality": "Ordenar por Qualidade", + "hud.bag.sort_by_category": "Ordenar por Categoria", }, diff --git a/assets/voxygen/i18n/pt_BR/hud/chat.ron b/assets/voxygen/i18n/pt_BR/hud/chat.ron index 66d6abb47a..122a312578 100644 --- a/assets/voxygen/i18n/pt_BR/hud/chat.ron +++ b/assets/voxygen/i18n/pt_BR/hud/chat.ron @@ -11,6 +11,7 @@ "hud.outcome.curse": "morreu de: maldição", "hud.outcome.bleeding": "morreu de: sangramento", "hud.outcome.crippled": "morreu de: aleijamento", + "hud.outcome.frozen": "morreu de: congelamento", // Chat outputs "hud.chat.online_msg": "[{name}] está online.", diff --git a/assets/voxygen/i18n/pt_BR/hud/map.ron b/assets/voxygen/i18n/pt_BR/hud/map.ron index c8a604b1b7..3625d07f8a 100644 --- a/assets/voxygen/i18n/pt_BR/hud/map.ron +++ b/assets/voxygen/i18n/pt_BR/hud/map.ron @@ -27,6 +27,9 @@ "hud.map.recenter": "Recentralizar", "hud.map.marked_location": "Localização Marcada", "hud.map.marked_location_remove": "Clique para remover", + "hud.map.change_map_mode": "Alterar modo do mapa", + "hud.map.toggle_minimap_voxel": "Alternar Modo Voxel no Minimapa", + "hud.map.zoom_minimap_explanation": "Dê zoom no minimapa para ver\nsua área em maior detalhes", }, diff --git a/assets/voxygen/i18n/pt_BR/hud/hud_settings.ron b/assets/voxygen/i18n/pt_BR/hud/settings.ron similarity index 97% rename from assets/voxygen/i18n/pt_BR/hud/hud_settings.ron rename to assets/voxygen/i18n/pt_BR/hud/settings.ron index f4ffa2b767..cf27edaaf4 100644 --- a/assets/voxygen/i18n/pt_BR/hud/hud_settings.ron +++ b/assets/voxygen/i18n/pt_BR/hud/settings.ron @@ -17,7 +17,7 @@ "hud.settings.relative_scaling": "Escala Relativa", "hud.settings.custom_scaling": "Escala Customizada", "hud.settings.crosshair": "Mira", - "hud.settings.transparency": "Transparência", + "hud.settings.opacity": "Transparência", "hud.settings.hotbar": "Hotbar", "hud.settings.toggle_shortcuts": "Mostar/Ocultar atalhos", "hud.settings.buffs_skillbar": "Buffs na Barra de Habilidades", @@ -35,7 +35,7 @@ "hud.settings.values": "Valores", "hud.settings.percentages": "Porcentagens", "hud.settings.chat": "Chat", - "hud.settings.background_transparency": "Transparência de Fundo", + "hud.settings.background_opacity": "Transparência de Fundo", "hud.settings.chat_character_name": "Nomes de Personagem no chat", "hud.settings.loading_tips": "Dicas na Tela de Carregamento", "hud.settings.reset_interface": "Restaurar Padrões", @@ -101,7 +101,6 @@ "hud.settings.save_window_size": "Salvar Dim. da Janela", "hud.settings.reset_graphics": "Restaurar Padrões", - "hud.settings.master_volume": "Volume Principal", "hud.settings.inactive_master_volume_perc": "Volume Principal (janela inativa)", "hud.settings.music_volume": "Volume da Música", @@ -127,7 +126,6 @@ "hud.settings.world": "Mundo", "hud.settings.region": "Região", "hud.settings.say": "Fala", - "hud.settings.none": "Nenhum", "hud.settings.all": "Todos", "hud.settings.group_only": "Grupo apenas", "hud.settings.reset_chat" : "Restaurar Padrões", diff --git a/assets/voxygen/i18n/pt_BR/skills.ron b/assets/voxygen/i18n/pt_BR/hud/skills.ron similarity index 94% rename from assets/voxygen/i18n/pt_BR/skills.ron rename to assets/voxygen/i18n/pt_BR/hud/skills.ron index 493f6f139d..3b61b9b2d7 100644 --- a/assets/voxygen/i18n/pt_BR/skills.ron +++ b/assets/voxygen/i18n/pt_BR/hud/skills.ron @@ -11,8 +11,8 @@ // Geral "hud.skill.inc_health_title": "Aumentar Saúde", "hud.skill.inc_health": "Aumentar saúde máxima por {boost}{SP}", - "hud.skill.inc_stam_title": "Aumentar Stamina", - "hud.skill.inc_stam": "Aumentar stamina máxima por {boost}{SP}", + "hud.skill.inc_energy_title": "Aumentar Stamina", + "hud.skill.inc_energy": "Aumentar stamina máxima por {boost}{SP}", "hud.skill.unlck_sword_title": "Desbloquear Espada", "hud.skill.unlck_sword": "Desbloquear habilidades de espada{SP}", "hud.skill.unlck_axe_title": "Desbloquear Machado", @@ -26,9 +26,9 @@ "hud.skill.unlck_sceptre_title": "Desbloquear Cetro", "hud.skill.unlck_sceptre": "Desbloquear Habilidades de Cetro{SP}", "hud.skill.dodge_title": "Esquiva", - "hud.skill.dodge": "Esquivar evita ataques corpo a corpo{SP}", - "hud.skill.roll_stamina_title": "Custo de Stamina da Rolagem", - "hud.skill.roll_stamina": "Rolar usa {boost}% menos de stamina{SP}", + "hud.skill.dodge": "Rolagens de esquiva são ativadas com o clique do meio, garantindo imunidade temporária a ataques corpo a corpo quando rolando", + "hud.skill.roll_energy_title": "Custo de Stamina da Rolagem", + "hud.skill.roll_energy": "Rolar usa {boost}% menos de stamina{SP}", "hud.skill.roll_speed_title": "Velocidade da Rolagem", "hud.skill.roll_speed": "Rola {boost}% mais rápido{SP}", "hud.skill.roll_dur_title": "Duração da Rolagem", @@ -54,12 +54,14 @@ "hud.skill.sc_lifesteal_lifesteal": "Converta mais {boost}% de seu dano em vida{SP}", "hud.skill.sc_lifesteal_regen_title": "Regeneração de Stamina", "hud.skill.sc_lifesteal_regen": "Reabasteça sua stamina em mais {boost}%{SP}", - "hud.skill.sc_heal_title" : "Cura", - "hud.skill.sc_heal" : "Aumenta a cura da bomba em {boost}%{SP}", + "hud.skill.sc_heal_title": "Aura de Cura", + "hud.skill.sc_heal": "Cura seus aliados usando o sangue dos inimigos, requer combo para ativar", "hud.skill.sc_heal_heal_title": "Cura", "hud.skill.sc_heal_heal": "Aumenta a quantidade de cura a outros em {boost}%{SP}", - "hud.skill.sc_heal_cost_title" : "Custo da Cura", - "hud.skill.sc_heal_cost" : "Utiliza {boost}% a menos de energia durante curas{SP}", + "hud.skill.sc_heal_cost_title": "Custo da Cura", + "hud.skill.sc_heal_cost": "Utiliza {boost}% a menos de energia durante curas{SP}", + "hud.skill.sc_heal_duration_title": "Duração", + "hud.skill.sc_heal_duration": "Os efeitos da aura de cura duram {boost}% a mais{SP}", "hud.skill.sc_heal_range_title": "Alcance", "hud.skill.sc_heal_range": "Seu raio alcança {boost}% mais longe{SP}", "hud.skill.sc_wardaura_unlock_title": "Desbloquear Aura Protetora", @@ -95,8 +97,8 @@ "hud.skill.st_flamethrower_damage" : "Aumenta o dano em {boost}%{SP}", "hud.skill.st_explosion_radius_title" : "Raio da Explosão", "hud.skill.st_explosion_radius" : "Quanto maior melhor, e o raio da explosão é aumentado em {boost}%{SP}", - "hud.skill.st_stamina_regen_title" : "Regeneração de stamina", - "hud.skill.st_stamina_regen" : "Aumenta o ganho de stamina em {boost}%{SP}", + "hud.skill.st_energy_regen_title" : "Regeneração de stamina", + "hud.skill.st_energy_regen" : "Aumenta o ganho de stamina em {boost}%{SP}", "hud.skill.st_fireball_title" : "Bola de fogo", "hud.skill.st_fireball" : "Brinque de perseguir seus inimigos", "hud.skill.st_damage_title" : "Dano", diff --git a/assets/voxygen/i18n/pt_BR/hud/trade.ron b/assets/voxygen/i18n/pt_BR/hud/trade.ron index 7ccb1310ee..4c5cee2dbc 100644 --- a/assets/voxygen/i18n/pt_BR/hud/trade.ron +++ b/assets/voxygen/i18n/pt_BR/hud/trade.ron @@ -5,17 +5,17 @@ string_map: { "hud.trade.trade_window": "Janela de Troca", "hud.trade.phase1_description": "Arraste os itens que deseja trocar\n para a sua área.", - "hud.trade.phase2_description": "Troca travada para que você tenha\n tempo para revisar.", - /// Phase3 should only be visible for a few milliseconds if everything is working properly, but is included for completeness - "hud.trade.phase3_description": "Troca em processamento.", - "hud.trade.persons_offer": "Oferta de {playername}", - "hud.trade.has_accepted": "{playername}\naceitou", - "hud.trade.accept": "Aceitar", - "hud.trade.decline": "Recusar", - "hud.trade.invite_sent": "Pedido de troca enviado para {playername}.", - "hud.trade.result.completed": "Troca concluída com sucesso.", - "hud.trade.result.declined": "Troca cancelada.", - "hud.trade.result.nospace": "Espaço insuficiente para completar a troca.", + "hud.trade.phase2_description": "Troca travada para que você tenha\n tempo para revisar.", + /// Phase3 should only be visible for a few milliseconds if everything is working properly, but is included for completeness + "hud.trade.phase3_description": "Troca em processamento.", + "hud.trade.persons_offer": "Oferta de {playername}", + "hud.trade.has_accepted": "{playername}\naceitou", + "hud.trade.accept": "Aceitar", + "hud.trade.decline": "Recusar", + "hud.trade.invite_sent": "Pedido de troca enviado para {playername}.", + "hud.trade.result.completed": "Troca concluída com sucesso.", + "hud.trade.result.declined": "Troca cancelada.", + "hud.trade.result.nospace": "Espaço insuficiente para completar a troca.", "hud.trade.buy_price": "Preço de compra", "hud.trade.sell_price": "Preço de venda", "hud.trade.coin": "moeda(s)", @@ -27,3 +27,4 @@ vector_map: { } ) + diff --git a/assets/voxygen/i18n/pt_BR/main.ron b/assets/voxygen/i18n/pt_BR/main.ron index d6f5f5b64b..3b360535ee 100644 --- a/assets/voxygen/i18n/pt_BR/main.ron +++ b/assets/voxygen/i18n/pt_BR/main.ron @@ -66,5 +66,27 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "Pressione 'G' para acender sua lâmpada.", + "Pressione 'F1' para visualizar suas teclas de atalho.", + "Você pode digitar /say ou /s para conversar apenas com jogadores próximos a você.", + "Você pode digitar /region ou /r para conversar apenas com jogadores a poucas centenas de blocos de você.", + "Admins podem usar o comando /build para entrar no modo construção.", + "Você pode digitar /group ou /g para conversar apenas com jogadores do seu grupo.", + "Para enviar mensagens privadas digite /tell seguido do nome do jogador desejado.", + "Busque sempre comida, baús e outros espólios espalhados pelo mundo!", + "Inventário cheio de comidas? Tente criar alimentos melhores com elas!", + "Imaginando o que há pra fazer? Cavernas estão marcadas com pontos marrons no mapa!", + "Não esqueça de ajustar as configurações gráficas. Pressione 'N' para abrir as configurações.", + "Jogar com outros é divertido! Pressione 'O' para ver quem está online.", + "Pressione 'J' para dançar. Hora da festa!", + "Pressione 'Shift Esquerdo' para abrir o Planador e conquistar os céus.", + "Veloren ainda está no Pre-Alpha. Estamos nos empenhando ao máximo para melhorar a cada dia!", + "Se quiser ingressar no time de Desenvolvedores ou apenas conversar conosco, acesse o nosso servidor do Discord.", + "Você pode exibir sua saúde em sua barra de vida nas opções.", + "Sente ao redor de uma fogueira (usando a tecla 'K') para lentamente se recuperar de lesões.", + "Precisa de uma mochila maior para sua jornada? Pressione 'C' para abrir o menu de criação!", + "Tente pular ao rolar através de criaturas.", + ], } ) diff --git a/assets/voxygen/i18n/pt_BR/npc.ron b/assets/voxygen/i18n/pt_BR/npc.ron new file mode 100644 index 0000000000..2ddb22ea2d --- /dev/null +++ b/assets/voxygen/i18n/pt_BR/npc.ron @@ -0,0 +1,190 @@ +/// CUIDADO: Arquivos de tradução devem ser criados no formato UTF-8 sem Marca de Ordem de byte - BOM(https://pt.wikipedia.org/wiki/Marca_de_ordem_de_byte) + +/// Localization for Portuguese (Brazil) +( + string_map: { + + }, + + vector_map: { + "npc.speech.villager": [ + "Não é um dia tão lindo?", + "Como você está?", + "Uma ótima manhã para você!", + "Fico imaginando o que um Catobelpas pensa enquanto come grama.", + "O que você está achando do clima?", + "Só de imaginar o que há em todas as masmorras me dá arrepios. Espero que alguém as limpe.", + "Adoraria explorar algumas cavernas assim que eu ficar mais forte.", + "Você viu meu gato?", + "Já ouviu falar dos ferozes Land Sharks? Ouvi dizer que eles vivem nos desertos.", + "Dizem que diversos tipos de gemas brilhantes podem ser encontradas nas cavernas.", + "Sou viciado em queijo!", + "Quer entrar? Venha, vamos comer um queijo!", + "Dizem que cogumelos são bons para saude. Nunca comi.", + "Não esqueça dos biscoitos!", + "Eu adoro queijo anão(dwarven cheese). Gostaria de saber fazer.", + "Fico imaginando o que há depois das montanhas.", + "Espero um dia conseguir fazer meu planador.", + "Gostaria de ver o meu jardim? Tá bom, talvez outro dia então.", + "Um dia adoravel para um passeio na floresta!", + "Ser ou não ser? Acho que serei um fazendeiro.", + "Nossa aldeia é a melhor, não acha?.", + "O que você acha que faz os Restos Brilhantes(Glowing Remains) brilharem?.", + "Acho que está na hora dum segundo café da manhã!", + "Já capturou uma libélula(firefly)?", + "Gostaria que alguém conseguisse espantar os lobos da aldeia.", + "Tive um sonho magnífico sobre queijo ontem. O que será que significa?", + "Deixei um pouco de queijo com meu irmão. Agora não sei se existe ou não. Eu chamo de queijo de Schrödinger.", + "Deixei um pouco de queijo com minha irmã. Agora não sei se existe ou não. Eu chamo de queijo de Schrödinger.", + "Alguém deveria fazer algo sobre esses cultistas. De preferência, não eu.", + "Espero que chova logo. Seria bom para as colheitas.", + "Eu amo mel! E eu odeio abelhas. ", + "Eu quero viajar no mundo um dia. Deve haver mais vida do que nesta aldeia. ", + ], + "npc.speech.villager_decline_trade": [ + "Desculpe, não tenho nada para trocar.", + "Troca? Como se eu tivesse algo que pode interessar a você.", + "Minha casa é minha, não vou trocá-la por nada.", + ], + "npc.speech.merchant_advertisement": [ + "Está interessado em fazer uma troca comigo?", + "Você quer negociar comigo?", + "Eu tenho muitos produtos, você quer dar uma olhada?" + ], + "npc.speech.merchant_busy": [ + "Ei, espere sua vez.", + "Espere, por favor. Sou apenas uma pessoa.", + "Você vê a outra pessoa na sua frente?", + "Só um momento, deixe-me terminar.", + "Não fure a fila.", + "Estou ocupado, volte mais tarde." + ], + "npc.speech.merchant_trade_successful": [ + "Obrigado por negociar comigo!", + "Obrigado!", + ], + "npc.speech.merchant_trade_declined": [ + "Talvez outra hora, tenha um bom dia!", + "Que pena, talvez da próxima vez, então! " + ], + "npc.speech.villager_cultist_alarm": [ + "Olhe! Há um cultista à solta!", + "Às armas! Os cultistas estão atacando!", + "Como ousam os cultistas atacar nossa aldeia!", + "Morte aos cultistas!", + "Cultistas não serão tolerados aqui!", + "Cultista assassino!", + "Prove o gume da minha espada, seu cultista sujo!", + "Nada pode limpar o sangue de suas mãos, cultista!", + "Com mil milhões de macacos e raios e coriscos! Um cultista entre nós!", + "Os males deste cultista estão prestes a acabar!", + "Este cultista é meu!", + "Prepare-se para encontrar o seu criador, oh sórdido cultista!", + "Vejo um cultista! Pegue eles!", + "Vejo um cultista! Atacar!", + "Vejo um cultista! Não o deixe escapar!", + "O mais honrado cultista se importaria com alguma MORTE?!", + "Nunca perdoe! Nunca se esqueça! Morra, cultista!", + "Morra, cultista!", + "Seu reinado de terror chegará ao fim!", + "Você pagará por tudo que fez!", + "Não aceitamos bem seus tipos por aqui.", + "Você deveria ter ficado no subsolo!", + ], + "npc.speech.villager_under_attack": [ + "Ajuda, Estou sendo atacado!", + "Ajuda! Estou sendo atacado!", + "Ouch! Estou sendo atacado!", + "Ouch! Estou sendo atacado! Ajuda!", + "Me ajude! Estou sendo atacado!", + "Estou sendo atacado! Ajuda!", + "Estou sendo atacado! Me ajude!", + "Ajuda!", + "Ajuda! Ajuda!", + "Ajuda! Ajuda! Ajuda!", + "Estou sendo atacado!", + "AAAHHH! Estou sendo atacado!", + "AAAHHH! Estou sendo atacado! Ajuda!", + "Ajuda! Estamos sendo atacado!", + "Ajuda! Assassino!", + "Ajuda! Há um assassinado em andamento!", + "Ajuda! Estão tentando me matar!", + "Guardas, Estou sendo atacado!", + "Guardas! Estou sendo atacado!", + "Estou sendo atacado! Guardas!", + "Ajuda! Guardas! Estou sendo atacado!", + "Guardas! Depressa!", + "Guardas! Guardas!", + "Guardas! Um vilão está me atacando!", + "Guardas, acabem com este maldito vilão!", + "Guardas! Um assasino a solta!", + "Guardas! Me ajude!", + "Você não vai se safar dessa! Guardas!", + "Maldito Vilão!", + "Me ajude!", + "Ajuda! Por favor!", + "Ouch! Guardas! Ajuda!", + "Estão atrás de mim!", + "Ajuda! Ajuda! Estou sendo repreendido", + "Ah, agora vemos a violência inerente ao sistema.", + "Só um arranhão!", + "Pare com isso!", + "O que eu fiz para você?!", + "Por favor, pare de me atacar!", + "Hey! Cuida pra onde você aponta essa coisa!", + "Desgraçado hediondo, vou acabar com você!", + "Pare com isso! Vá embora!", + "Você está me deixando louco!", + "Ow! Quem você pensa que é?!", + "Arrancarei sua cabeça por isso!", + "Pare, por favor! Não levo nada de valor!", + "Vou mandar meu irmão em você, ele é maior que eu!", + "Nãooo, Vou contar pra minha mãe!", + "Te amaldiçoo!", + "Por favor não faça isso.", + "Isso não foi muito legal!", + "Sua arma funciona, pode guardar ela agora!", + "Me poupe!", + "Por favor, tenho uma família!", + "Sou muito jovem para morrer!", + "Podemos conversar sobre isso?", + "Violência nunca é a resposta!", + "Hoje foi um péssimo dia...", + "Ei, isso dói!", + "Eek!", + "Que rude!", + "Pare, eu imploro!", + "Que você adoeça!", + "Isso não é engraçado.", + "Como ousa?!", + "Você pagará por isso!", + "Continue assim e irá se arrepender!", + "Não me faça te machucar!", + "Deve haver algum engano!", + "Não precisa fazer isso!", + "Morre, Diabo!", + "Isso Dói!", + "Porque você faria isso?", + "Pelos espíritos, Pare!", + "Você deve ter me confundido com alguém!", + "Eu não mereço isso!", + "Por favor, não faça isso novamente.", + "Guardas, joguem este monstro no lago!", + "Vou mandar meu tarrasque em você!", + "Porque eeeeeeeeeeeeeu?", + ], + "npc.speech.villager_enemy_killed": [ + "Destruí meu inimigo!", + "Finalmente em paz!", + "... agora, o que eu estava fazendo?", + ], + "npc.speech.menacing": [ + "Estou te avisando!", + "Se chegar mais perto eu ataco!", + "Não tenho medo de você!", + "Se manda daqui!", + "Vaza daqui se deseja viver!", + "Você não é bem-vindo aqui!", + ], + } +) diff --git a/assets/voxygen/i18n/pt_BR/template.ron b/assets/voxygen/i18n/pt_BR/template.ron deleted file mode 100644 index 5d390a9c74..0000000000 --- a/assets/voxygen/i18n/pt_BR/template.ron +++ /dev/null @@ -1,12 +0,0 @@ -/// CUIDADO: Arquivos de tradução devem ser criados no formato UTF-8 sem Marca de Ordem de byte - BOM(https://pt.wikipedia.org/wiki/Marca_de_ordem_de_byte) - -/// Localization for Portuguese (Brazil) -( - string_map: { - - }, - - - vector_map: { - } -) diff --git a/assets/voxygen/i18n/pt_PT/_manifest.ron b/assets/voxygen/i18n/pt_PT/_manifest.ron index 6bdc0835b9..7ae6d68cf1 100644 --- a/assets/voxygen/i18n/pt_PT/_manifest.ron +++ b/assets/voxygen/i18n/pt_PT/_manifest.ron @@ -26,362 +26,5 @@ asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", scale_ratio: 1.0, ), - }, - string_map: { - /// Start Common section - // Texts used in multiple locations with the same formatting - "common.username": "nome de utilizador", - "common.singleplayer": "Um jogador", - "common.multiplayer": "Multijogador", - "common.servers": "Servidores", - "common.quit": "Sair", - "common.settings": "Definições", - "common.languages": "Linguagens", - "common.interface": "Interface", - "common.gameplay": "Jogabilidade", - "common.controls": "Controlos", - "common.video": "Video", - "common.sound": "Som", - "common.resume": "Resumir", - "common.characters": "Personagens", - "common.close": "Fechar", - "common.yes": "Sim", - "common.no": "Não", - "common.back": "Voltar", - "common.create": "Criar", - "common.okay": "Okay", - "common.accept": "Aceitar", - "common.disclaimer": "Aviso", - "common.cancel": "Cancelar", - "common.none": "Nenhum", - "common.error": "Erro", - "common.fatal_error": "Erro fatal", - - // Message when connection to the server is lost - "common.connection_lost": r#"Conexâo perdida! -Será que o server reiniciou? -O cliente está atualizado?"#, - - - "common.species.orc": "Ogre", - "common.species.human": "Humano", - "common.species.dwarf": "Anão", - "common.species.elf": "Elfo", - "common.species.undead": "Morto-vivo", - "common.species.danari": "Danari", - - "common.weapons.axe": "Machado", - "common.weapons.sword": "Espada", - "common.weapons.staff": "Cajado", - "common.weapons.bow": "Arco", - "common.weapons.hammer": "Martelo", - /// End Common section - - - /// Start Main screen section - "main.connecting": "Conectando", - "main.creating_world": "Criando o mundo", - - // Welcome notice that appears the first time Veloren is started - "main.notice": r#"Bem vindo a versão alpha de Veloren! - -Antes de começar a jogar, por favor tenha em mente que: - -- Isto é uma versão muito experimental. Prepare-se para defeitos, jogabilidade muito inacabada, mecanismos por polir e funcionalidades por -adicionar. -- Se tiver comentários construtivos ou defeitos para reportar, pode contactar-nos através do Reddit, GitLab ou o nosso servidor comunitário de -Discord. -- Veloren está licenciado sob a licensa código aberto GPL 3. Isto significa que pode jogar, modificar e redistribuir como quiser - (Contanto que o trabalho derivado seja também GPL 3). -- Veloren é um projeto comunitário sem lucro, e toda a gente que trabalha nele é um voluntário. Se gostar do que ve, considere juntar-se a equipa -de desenvolvimento ou a de artes! -- 'Voxel RPG' é um género em si mesmo. First-person shooters costumavam ser chamados de clones do DOOM. - -Tal como eles, nós estamos a tentar construir um género. Este jogo não é um clone e o seu desenvolvimento vai divergir de jogos existentes no -futuro. - -Obrigado por ler este aviso, nós esperamos que goste do jogo! - -~ A equipa do Veloren"#, - - // Login process description - "main.login_process": r#"Informação sobre o processo de Login: - -Se tiver problemas a logar: - -Tenha em atenção que é necessário uma conta -para jogar em servidores com autenticação. - -Para criar uma conta navegue até - -https://veloren.net/account/."#, - "main.login.server_not_found": "Servidor não encontrado", - "main.login.authentication_error": "Erro de autenticação", - "main.login.server_full": "Servidor está cheio", - "main.login.untrusted_auth_server": "Server de autenticação não confiado", - "main.login.outdated_client_or_server": "Servidor endoideceu: Provavelmente as versões são incompativéis, verifique se há versões mais recentes.", - "main.login.timeout": "Tempo esgotado: O servidor não respondeu a tempo. (Sobrecarregado ou problemas de rede).", - "main.login.server_shut_down": "O servidor encerrou", - "main.login.network_error": "Error de rede", - "main.login.failed_sending_request": "Pedido ao servidor de autenticação falhou", - "main.login.client_crashed": "O cliente crashou", - "main.login.select_language": "Seleccione uma língua", - - /// End Main screen section - - - /// Start HUD Section - "hud.do_not_show_on_startup": "Não mostre no início", - "hud.show_tips": "Mostrar dicas", - "hud.quests": "Missões", - "hud.you_died": "Você Morreu", - - "hud.press_key_to_show_keybindings_fmt": "Clique em {key} para mostrar as teclas mapeadas", - "hud.press_key_to_show_debug_info_fmt": "Clique em {key} para mostrar a informação de depuração", - "hud.press_key_to_toggle_keybindings_fmt": "Clique em {key} para mostrar/ocultar as teclas mapeadas", - "hud.press_key_to_toggle_debug_info_fmt": "Clique em {key} para mostrar/ocultar a informação de depuração", - - // Respawn message - "hud.press_key_to_respawn": r#"Clique em {key} para renascer na última fogueira visitada."#, - - // Welcome message - "hud.welcome": r#"Bem vindo a Alpha do Veloren!, - - -Algumas dicas antes de começar: - - -MAIS IMPORTANTE: Para definir o seu local de renascimento escreva /waypoint no chat. - -Isto também pode ser realizado depois de morto! - - -Clique em F1 para ver as teclas mapeadas. - -Escreva /help no chat para ver os comandos de chat - - -A muitos baús e outros objetos a aperecer no mundo aleatoriamente! - -Pressione o botão direito do mouse pare coletá-los. - -Para usar o que coletou basta abrir o inventário pressionando a tecla 'B'. - -Faça duplo clique nos items para usá-los ou equipá-los. - -Deite-os fora cliquando uma vez neles e depois outra fora do inventário. - - -As noites podem ser bastante escuras. - -Acenda a lanterna escrevendo /lantern no chat. - - -Quer libertar o mouse para fechar esta janela? Clique em TAB! - - -Aprecie a sua estadia no mundo de Veloren."#, - -"hud.temp_quest_headline": r#"Please, help us Traveller!"#, -"hud.temp_quest_text": r#"Dungeons filled with evil cultists -have emerged all around our peaceful towns! - - -Gather some company, stack up on food -and defeat their vile leaders and acolytes. - - -Maybe you can even obtain one of their -magically infused items?"#, - - "hud.settings.general": "Geral", - "hud.settings.none": "Nenhum", - "hud.settings.press_behavior.toggle": "Alternar", - "hud.settings.press_behavior.hold": "Segurar", - "hud.settings.help_window": "Janela de ajuda", - "hud.settings.debug_info": "Informação de depuração", - "hud.settings.tips_on_startup": "Dicas no início", - "hud.settings.ui_scale": "Escala da interface", - "hud.settings.relative_scaling": "Escala relativa", - "hud.settings.custom_scaling": "Escala customizada", - "hud.settings.crosshair": "Crosshair", - "hud.settings.transparency": "Transparência", - "hud.settings.hotbar": "Hotbar", - "hud.settings.toggle_shortcuts": "Mostar/Ocultar atalhos", - "hud.settings.toggle_bar_experience": "Mostar/Ocultar barra de experiência", - "hud.settings.scrolling_combat_text": "Texto de combate deslizante", - "hud.settings.single_damage_number": "Números de dano únicos", - "hud.settings.cumulated_damage": "Dano acumulado", - "hud.settings.incoming_damage": "Dano recebido", - "hud.settings.cumulated_incoming_damage": "Dano recebido acumulado", - "hud.settings.energybar_numbers": "Números da barra de energia", - "hud.settings.values": "Valores", - "hud.settings.percentages": "Percentagens", - "hud.settings.chat": "Chat", - "hud.settings.background_transparency": "Transparência do fundo", - - "hud.settings.pan_sensitivity": "Sensibilidade de rotação", - "hud.settings.zoom_sensitivity": "Sensibilidade de zoom", - "hud.settings.invert_scroll_zoom": "Inverter scroll zoom", - "hud.settings.invert_mouse_y_axis": "Inverter o eixo Y do mouse", - "hud.settings.free_look_behavior": "Ativação de rotação livre", - - "hud.settings.view_distance": "Alcance de visão", - "hud.settings.maximum_fps": "FPS máximo", - "hud.settings.fov": "Campo de visão(graus)", - "hud.settings.gamma": "Luminosidade", - "hud.settings.antialiasing_mode": "Modo de antialiasing", - "hud.settings.cloud_rendering_mode": "Modo de representação de nuvens", - "hud.settings.fluid_rendering_mode": "Modo de representação de fluídos", - "hud.settings.fluid_rendering_mode.cheap": "Barato", - "hud.settings.fluid_rendering_mode.shiny": "Brilhante", - "hud.settings.cloud_rendering_mode.regular": "Normal", - "hud.settings.fullscreen": "Tela cheia", - "hud.settings.save_window_size": "Gravar dimensões", - - "hud.settings.music_volume": "Volume da música", - "hud.settings.sound_effect_volume": "Volume dos efeitos sonoros", - "hud.settings.audio_device": "Dispositivo de aúdio", - - // Control list - "hud.settings.control_names": r#"Libertar mouse -Mostar/Ocultar janela de ajuda -Mostar/Ocultar interface -Mostar/Ocultar FPS e informação de depuração -Gravar captura de ecrã -Mostar/Ocultar nomes -Mostar/Ocultar tela cheia - - -Mover para frente -Mover para a esquerda -Mover para a direita -Mover para trás - -Saltar - -Planador - -Desviar - -Rolar - -Trepar - -Descer - -Auto caminhar - -Embainhar/sacar armas - -Equipar/remover capacete - -Sentar - -Montar - -Interagir - - -Ataque básico -Ataque/bloquear/apontar secundário - - -Habilidade 1 -Habilidade 2 -Habilidade 3 -Habilidade 4 -Habilidade 5 -Habilidade 6 -Habilidade 7 -Habilidade 8 -Habilidade 9 -Habilidade 10 - - -Menu de pausa -Definições -Social -Mapa -Livro de feitiços -Personagem -Registo de missões -Inventário - - - -Enviar mensagem de chat -Scroll chat - - -Comandos de chat: - -/alias [nome] - Mudar o seu nome de chat -/tp [nome] - Teletransporta-te para outro player -/jump - Deslocar a posição -/goto - Teletransporta-te para a posição -/kill - Suicidar -/pig - Invocar NPC de porco -/wolf - Invocar NPC do lobo -/help - Mostrar comandos de chat"#, - - "hud.social": "Social", - "hud.social.online": "Online", - "hud.social.friends": "Amigos", - "hud.social.not_yet_available": "Indisponível de momento", - "hud.social.faction": "Facção", - "hud.social.play_online_fmt": "{nb_player} jogador(es) online", - - "hud.spell": "Feitiço", - - "hud.free_look_indicator": "Rotação livre ativada", - /// End HUD section - - - /// Start chracter selection section - "char_selection.delete_permanently": "Deletar esta personagem permanentemente?", - "char_selection.change_server": "Mudar de servidor", - "char_selection.enter_world": "Entrar no mundo", - "char_selection.logout": "Desconectar", - "char_selection.create_new_character": "Criar nova personagem", - "char_selection.character_creation": "Criação de personagem", - - "char_selection.human_default": "Humano padrão", - "char_selection.level_fmt": "Nível {level_nb}", - "char_selection.uncanny_valley": "Vale da estranheza", - "char_selection.plains_of_uncertainty": "Planícies da incerteza", - "char_selection.beard": "Barba", - "char_selection.hair_style": "Estilo do cabelo", - "char_selection.hair_color": "Cor do cabelo", - "char_selection.chest_color": "Cor do peitoral", - "char_selection.eye_color": "Cor dos olhos", - "char_selection.skin": "Cor da pele", - "char_selection.eyebrows": "Pestanas", - "char_selection.accessories": "Acessórios", - - /// End chracter selection section - - - /// Start character window section - "character_window.character_name": "Nome da personagem", - // Charater stats - "character_window.character_stats": r#"Resistência - -Aptidão fisíca - -Força de vontade -"#, - - - /// Start character window section - - - /// Start Escape Menu Section - "esc_menu.logout": "Desconectar", - "esc_menu.quit_game": "Sair do jogo", - /// End Escape Menu Section - }, - - vector_map: { } ) diff --git a/assets/voxygen/i18n/pt_PT/buff.ron b/assets/voxygen/i18n/pt_PT/buff.ron new file mode 100644 index 0000000000..960b221c7d --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/buff.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/char_selection.ron b/assets/voxygen/i18n/pt_PT/char_selection.ron new file mode 100644 index 0000000000..849ac64ecf --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/char_selection.ron @@ -0,0 +1,29 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + "char_selection.delete_permanently": "Deletar esta personagem permanentemente?", + "char_selection.change_server": "Mudar de servidor", + "char_selection.enter_world": "Entrar no mundo", + "char_selection.logout": "Desconectar", + "char_selection.create_new_character": "Criar nova personagem", + "char_selection.character_creation": "Criação de personagem", + + "char_selection.human_default": "Humano padrão", + "char_selection.level_fmt": "Nível {level_nb}", + "char_selection.uncanny_valley": "Vale da estranheza", + "char_selection.plains_of_uncertainty": "Planícies da incerteza", + "char_selection.beard": "Barba", + "char_selection.hair_style": "Estilo do cabelo", + "char_selection.hair_color": "Cor do cabelo", + "char_selection.chest_color": "Cor do peitoral", + "char_selection.eye_color": "Cor dos olhos", + "char_selection.skin": "Cor da pele", + "char_selection.eyebrows": "Pestanas", + "char_selection.accessories": "Acessórios", + }, + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/common.ron b/assets/voxygen/i18n/pt_PT/common.ron new file mode 100644 index 0000000000..e9848f546a --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/common.ron @@ -0,0 +1,57 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + // Texts used in multiple locations with the same formatting + "common.username": "nome de utilizador", + "common.singleplayer": "Um jogador", + "common.multiplayer": "Multijogador", + "common.servers": "Servidores", + "common.quit": "Sair", + "common.settings": "Definições", + "common.languages": "Linguagens", + "common.interface": "Interface", + "common.gameplay": "Jogabilidade", + "common.controls": "Controlos", + "common.video": "Video", + "common.sound": "Som", + "common.resume": "Resumir", + "common.characters": "Personagens", + "common.close": "Fechar", + "common.yes": "Sim", + "common.no": "Não", + "common.back": "Voltar", + "common.create": "Criar", + "common.okay": "Okay", + "common.accept": "Aceitar", + "common.disclaimer": "Aviso", + "common.cancel": "Cancelar", + "common.none": "Nenhum", + "common.error": "Erro", + "common.fatal_error": "Erro fatal", + + // Message when connection to the server is lost + "common.connection_lost": r#"Conexâo perdida! +Será que o server reiniciou? +O cliente está atualizado?"#, + + + "common.species.orc": "Ogre", + "common.species.human": "Humano", + "common.species.dwarf": "Anão", + "common.species.elf": "Elfo", + "common.species.undead": "Morto-vivo", + "common.species.danari": "Danari", + + "common.weapons.axe": "Machado", + "common.weapons.sword": "Espada", + "common.weapons.staff": "Cajado", + "common.weapons.bow": "Arco", + "common.weapons.hammer": "Martelo", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/esc_menu.ron b/assets/voxygen/i18n/pt_PT/esc_menu.ron new file mode 100644 index 0000000000..51a99240b7 --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/esc_menu.ron @@ -0,0 +1,13 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + "esc_menu.logout": "Desconectar", + "esc_menu.quit_game": "Sair do jogo", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/gameinput.ron b/assets/voxygen/i18n/pt_PT/gameinput.ron new file mode 100644 index 0000000000..960b221c7d --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/gameinput.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/hud/bag.ron b/assets/voxygen/i18n/pt_PT/hud/bag.ron new file mode 100644 index 0000000000..960b221c7d --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/bag.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/hud/char_window.ron b/assets/voxygen/i18n/pt_PT/hud/char_window.ron new file mode 100644 index 0000000000..62bfce266c --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/char_window.ron @@ -0,0 +1,19 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + "character_window.character_name": "Nome da personagem", + // Charater stats + "character_window.character_stats": r#"Resistência + +Aptidão fisíca + +Força de vontade +"#, + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/hud/chat.ron b/assets/voxygen/i18n/pt_PT/hud/chat.ron new file mode 100644 index 0000000000..960b221c7d --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/chat.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/hud/crafting.ron b/assets/voxygen/i18n/pt_PT/hud/crafting.ron new file mode 100644 index 0000000000..960b221c7d --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/crafting.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/hud/group.ron b/assets/voxygen/i18n/pt_PT/hud/group.ron new file mode 100644 index 0000000000..960b221c7d --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/group.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/hud/map.ron b/assets/voxygen/i18n/pt_PT/hud/map.ron new file mode 100644 index 0000000000..960b221c7d --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/map.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/hud/misc.ron b/assets/voxygen/i18n/pt_PT/hud/misc.ron new file mode 100644 index 0000000000..29609b4545 --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/misc.ron @@ -0,0 +1,76 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + "hud.do_not_show_on_startup": "Não mostre no início", + "hud.show_tips": "Mostrar dicas", + "hud.quests": "Missões", + "hud.you_died": "Você Morreu", + + "hud.press_key_to_show_keybindings_fmt": "Clique em {key} para mostrar as teclas mapeadas", + "hud.press_key_to_show_debug_info_fmt": "Clique em {key} para mostrar a informação de depuração", + "hud.press_key_to_toggle_keybindings_fmt": "Clique em {key} para mostrar/ocultar as teclas mapeadas", + "hud.press_key_to_toggle_debug_info_fmt": "Clique em {key} para mostrar/ocultar a informação de depuração", + + // Respawn message + "hud.press_key_to_respawn": r#"Clique em {key} para renascer na última fogueira visitada."#, + + // Welcome message + "hud.welcome": r#"Bem vindo a Alpha do Veloren!, + + +Algumas dicas antes de começar: + + +MAIS IMPORTANTE: Para definir o seu local de renascimento escreva /waypoint no chat. + +Isto também pode ser realizado depois de morto! + + +Clique em F1 para ver as teclas mapeadas. + +Escreva /help no chat para ver os comandos de chat + + +A muitos baús e outros objetos a aperecer no mundo aleatoriamente! + +Pressione o botão direito do mouse pare coletá-los. + +Para usar o que coletou basta abrir o inventário pressionando a tecla 'B'. + +Faça duplo clique nos items para usá-los ou equipá-los. + +Deite-os fora cliquando uma vez neles e depois outra fora do inventário. + + +As noites podem ser bastante escuras. + +Acenda a lanterna escrevendo /lantern no chat. + + +Quer libertar o mouse para fechar esta janela? Clique em TAB! + + +Aprecie a sua estadia no mundo de Veloren."#, + +"hud.temp_quest_headline": r#"Please, help us Traveller!"#, +"hud.temp_quest_text": r#"Dungeons filled with evil cultists +have emerged all around our peaceful towns! + + +Gather some company, stack up on food +and defeat their vile leaders and acolytes. + + +Maybe you can even obtain one of their +magically infused items?"#, + "hud.spell": "Feitiço", + + "hud.free_look_indicator": "Rotação livre ativada", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/hud/sct.ron b/assets/voxygen/i18n/pt_PT/hud/sct.ron new file mode 100644 index 0000000000..960b221c7d --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/sct.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/hud/settings.ron b/assets/voxygen/i18n/pt_PT/hud/settings.ron new file mode 100644 index 0000000000..b99666d496 --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/settings.ron @@ -0,0 +1,141 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + "hud.settings.general": "Geral", + "hud.settings.none": "Nenhum", + "hud.settings.press_behavior.toggle": "Alternar", + "hud.settings.press_behavior.hold": "Segurar", + "hud.settings.help_window": "Janela de ajuda", + "hud.settings.debug_info": "Informação de depuração", + "hud.settings.tips_on_startup": "Dicas no início", + "hud.settings.ui_scale": "Escala da interface", + "hud.settings.relative_scaling": "Escala relativa", + "hud.settings.custom_scaling": "Escala customizada", + "hud.settings.crosshair": "Crosshair", + "hud.settings.opacity": "Transparência", + "hud.settings.hotbar": "Hotbar", + "hud.settings.toggle_shortcuts": "Mostar/Ocultar atalhos", + "hud.settings.toggle_bar_experience": "Mostar/Ocultar barra de experiência", + "hud.settings.scrolling_combat_text": "Texto de combate deslizante", + "hud.settings.single_damage_number": "Números de dano únicos", + "hud.settings.cumulated_damage": "Dano acumulado", + "hud.settings.incoming_damage": "Dano recebido", + "hud.settings.cumulated_incoming_damage": "Dano recebido acumulado", + "hud.settings.energybar_numbers": "Números da barra de energia", + "hud.settings.values": "Valores", + "hud.settings.percentages": "Percentagens", + "hud.settings.chat": "Chat", + "hud.settings.background_opacity": "Transparência do fundo", + + "hud.settings.pan_sensitivity": "Sensibilidade de rotação", + "hud.settings.zoom_sensitivity": "Sensibilidade de zoom", + "hud.settings.invert_scroll_zoom": "Inverter scroll zoom", + "hud.settings.invert_mouse_y_axis": "Inverter o eixo Y do mouse", + "hud.settings.free_look_behavior": "Ativação de rotação livre", + + "hud.settings.view_distance": "Alcance de visão", + "hud.settings.maximum_fps": "FPS máximo", + "hud.settings.fov": "Campo de visão(graus)", + "hud.settings.gamma": "Luminosidade", + "hud.settings.antialiasing_mode": "Modo de antialiasing", + "hud.settings.cloud_rendering_mode": "Modo de representação de nuvens", + "hud.settings.fluid_rendering_mode": "Modo de representação de fluídos", + "hud.settings.fluid_rendering_mode.cheap": "Barato", + "hud.settings.fluid_rendering_mode.shiny": "Brilhante", + "hud.settings.cloud_rendering_mode.regular": "Normal", + "hud.settings.fullscreen": "Tela cheia", + "hud.settings.save_window_size": "Gravar dimensões", + + "hud.settings.music_volume": "Volume da música", + "hud.settings.sound_effect_volume": "Volume dos efeitos sonoros", + "hud.settings.audio_device": "Dispositivo de aúdio", + + // Control list + "hud.settings.control_names": r#"Libertar mouse +Mostar/Ocultar janela de ajuda +Mostar/Ocultar interface +Mostar/Ocultar FPS e informação de depuração +Gravar captura de ecrã +Mostar/Ocultar nomes +Mostar/Ocultar tela cheia + + +Mover para frente +Mover para a esquerda +Mover para a direita +Mover para trás + +Saltar + +Planador + +Desviar + +Rolar + +Trepar + +Descer + +Auto caminhar + +Embainhar/sacar armas + +Equipar/remover capacete + +Sentar + +Montar + +Interagir + + +Ataque básico +Ataque/bloquear/apontar secundário + + +Habilidade 1 +Habilidade 2 +Habilidade 3 +Habilidade 4 +Habilidade 5 +Habilidade 6 +Habilidade 7 +Habilidade 8 +Habilidade 9 +Habilidade 10 + + +Menu de pausa +Definições +Social +Mapa +Livro de feitiços +Personagem +Registo de missões +Inventário + + + +Enviar mensagem de chat +Scroll chat + + +Comandos de chat: + +/alias [nome] - Mudar o seu nome de chat +/tp [nome] - Teletransporta-te para outro player +/jump - Deslocar a posição +/goto - Teletransporta-te para a posição +/kill - Suicidar +/pig - Invocar NPC de porco +/wolf - Invocar NPC do lobo +/help - Mostrar comandos de chat"#, + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/hud/skills.ron b/assets/voxygen/i18n/pt_PT/hud/skills.ron new file mode 100644 index 0000000000..960b221c7d --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/skills.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/hud/social.ron b/assets/voxygen/i18n/pt_PT/hud/social.ron new file mode 100644 index 0000000000..19cbd644cb --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/social.ron @@ -0,0 +1,18 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + "hud.social": "Social", + "hud.social.online": "Online", + "hud.social.friends": "Amigos", + "hud.social.not_yet_available": "Indisponível de momento", + "hud.social.faction": "Facção", + "hud.social.play_online_fmt": "{nb_player} jogador(es) online", + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/pt_PT/hud/trade.ron b/assets/voxygen/i18n/pt_PT/hud/trade.ron new file mode 100644 index 0000000000..3439af4b30 --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/hud/trade.ron @@ -0,0 +1,12 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/pt_PT/main.ron b/assets/voxygen/i18n/pt_PT/main.ron new file mode 100644 index 0000000000..2cb5ed45d4 --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/main.ron @@ -0,0 +1,58 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + "main.connecting": "Conectando", + "main.creating_world": "Criando o mundo", + + // Welcome notice that appears the first time Veloren is started + "main.notice": r#"Bem vindo a versão alpha de Veloren! + +Antes de começar a jogar, por favor tenha em mente que: + +- Isto é uma versão muito experimental. Prepare-se para defeitos, jogabilidade muito inacabada, mecanismos por polir e funcionalidades por +adicionar. +- Se tiver comentários construtivos ou defeitos para reportar, pode contactar-nos através do Reddit, GitLab ou o nosso servidor comunitário de +Discord. +- Veloren está licenciado sob a licensa código aberto GPL 3. Isto significa que pode jogar, modificar e redistribuir como quiser + (Contanto que o trabalho derivado seja também GPL 3). +- Veloren é um projeto comunitário sem lucro, e toda a gente que trabalha nele é um voluntário. Se gostar do que ve, considere juntar-se a equipa +de desenvolvimento ou a de artes! +- 'Voxel RPG' é um género em si mesmo. First-person shooters costumavam ser chamados de clones do DOOM. + +Tal como eles, nós estamos a tentar construir um género. Este jogo não é um clone e o seu desenvolvimento vai divergir de jogos existentes no +futuro. + +Obrigado por ler este aviso, nós esperamos que goste do jogo! + +~ A equipa do Veloren"#, + + // Login process description + "main.login_process": r#"Informação sobre o processo de Login: + +Se tiver problemas a logar: + +Tenha em atenção que é necessário uma conta +para jogar em servidores com autenticação. + +Para criar uma conta navegue até + +https://veloren.net/account/."#, + "main.login.server_not_found": "Servidor não encontrado", + "main.login.authentication_error": "Erro de autenticação", + "main.login.server_full": "Servidor está cheio", + "main.login.untrusted_auth_server": "Server de autenticação não confiado", + "main.login.outdated_client_or_server": "Servidor endoideceu: Provavelmente as versões são incompativéis, verifique se há versões mais recentes.", + "main.login.timeout": "Tempo esgotado: O servidor não respondeu a tempo. (Sobrecarregado ou problemas de rede).", + "main.login.server_shut_down": "O servidor encerrou", + "main.login.network_error": "Error de rede", + "main.login.failed_sending_request": "Pedido ao servidor de autenticação falhou", + "main.login.client_crashed": "O cliente crashou", + "main.login.select_language": "Seleccione uma língua", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/pt_PT/npc.ron b/assets/voxygen/i18n/pt_PT/npc.ron new file mode 100644 index 0000000000..d2679ab63c --- /dev/null +++ b/assets/voxygen/i18n/pt_PT/npc.ron @@ -0,0 +1,12 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for portuguese (Portugal) +( + string_map: { + + }, + + vector_map: { + + } +) diff --git a/assets/voxygen/i18n/ru_RU/_manifest.ron b/assets/voxygen/i18n/ru_RU/_manifest.ron index dc396f3f6c..4a60371877 100644 --- a/assets/voxygen/i18n/ru_RU/_manifest.ron +++ b/assets/voxygen/i18n/ru_RU/_manifest.ron @@ -28,206 +28,5 @@ asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", scale_ratio: 1.0, ), - }, - - string_map: { - - }, - - - vector_map: { - "loading.tips": [ - "Нажмите 'G', чтобы зажечь фонарь.", - "Нажмите '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": [ - "Помогите, Меня атакуют!", - "Помогите, Меня атакуют!", - "Оуч, Меня атакуют!", - "Оуч, Меня атакуют!", - "Помоги мне! Меня атакуют!", - "Меня атакуют! Помогите!", - "Меня атакуют! Помогите мне!", - "Помогите!", - "Помогите! Помогите!", - "Помогите! Помогите! Помогите!", - "Меня атакуют!", - "АААААА! Меня атакуют!", - "АААААА! Меня атакуют! Помогите!", - "Помогите! Мы атакованны!", - "Помогите! Убийца!", - "Помогите! Убийца на свободе!", - "Помогите! Они пытаются меня убить!", - "Стража, Меня атакуют!", - "Стража! Меня атакуют!", - "Меня атакуют! Стража!", - "Помогите! Стража! Меня атакуют!", - "Стража! Скорее!", - "Стража! Стража!", - "Стража! Этот злодей бьёт меня!", - "Стража, Схватите этого негодяя!", - "Стража! Здесь убийца!", - "Стража! Помогите me!", - "Тебе это не сойдет с рук! Стража!", - "Ты изверг!", - "Помогите мне!", - "Помогите! Пожалуйста!", - "Ой! Стража! Помогите!", - "Они идут за мной!", - "Помогите! Помогите! Меня постигла расплата!", - "Ах, теперь мы видим насилие, присущее системе.", - "Это всего лишь царапина!", - "Остановитесь!", - "Что я тебе сделал?!", - "Пожалуйста, не бей!", - "Эй! Смотри, куда направляешь эту штуку", - "Гнусный негодяй, проваливай отсюда!", - "Прекрати! Уходи!", - "Ты уже достал!", - "Эй! Что ты возомнил о себе?!", - "Я тебе башку оторву!", - "Остановись пожалуйста. У меня ничего нет!", - "Я позову брата, он больше меня", - "Нет! Я расскажу маме!", - "Будь ты проклят!", - "Пожалуйста, не делай этого.", - "Это не приятно!", - "Ваше оружие работает, вы можете убрать его прямо сейчас!", - "Пощади!", - "Пожалуйста, у меня семья!", - "Я слишком молод чтобы умереть!", - "Может договоримся?", - "Насилие не выход!", - "Сегодня выдался плохой день...", - "Эй, это больно!", - "Ик!", - "Как грубо!", - "Остановись, прошу тебя!", - "Чтоб ты сдох!", - "Это не смешно.", - "Как ты смеешь?!", - "Ты заплатишь за это!", - "Ты об этом пожалеешь!", - "Не заставляй меня делать тебе больно!", - "Произошла какая то ошибка!", - "Не делай этого!", - "Изыди, дьявол", - "Это очень больно!", - "Зачем ты это сделал?", - "Ради всего святого, прекрати!", - "Ты меня перепутал с кем то", - "Я не заслуживаю этого!", - "Пожалуйста, больше так не делай.", - "Стража, утопите этого монстра в озере!", - "Я натравлю своего tarasque на тебя!", - "Почему я?", - ], - "npc.speech.villager_enemy_killed": [ - "Я уничтожил врага!", - "Наконец-то мир!", - "... что же я наделал?", - ] } ) diff --git a/assets/voxygen/i18n/ru_RU/buff.ron b/assets/voxygen/i18n/ru_RU/buff.ron new file mode 100644 index 0000000000..0563b20d5c --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/buff.ron @@ -0,0 +1,52 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + // Buffs + "buff.remove": "Нажмите, чтобы удалить", + "buff.title.missing": "Отсутствует название", + "buff.desc.missing": "Отсутствует описание", + "buff.title.heal": "Лечить", + "buff.desc.heal": "Постепенное восстонавление здоровья", + "buff.title.potion": "Зелье", + "buff.desc.potion": "Питье...", + "buff.title.saturation": "Насыщение", + "buff.desc.saturation": "Восстонавление здоровья за счет расходных материалов.", + "buff.title.campfire_heal": "Исцеление у костра", + "buff.desc.campfire_heal": "Отдых у костра лечит {rate}% в секунду.", + "buff.title.invulnerability": "Неуязвимость", + "buff.desc.invulnerability": "Вы не можете быть повреждены ни одной атакой.", + "buff.title.protectingward": "Защитная Аура", + "buff.desc.protectingward": "Вы в некоторой степени защищены от нападений.", + "buff.title.frenzied": "Бешенство", + "buff.desc.frenzied": "Кровь течёт быстрее, ускоряя ваше движение и понемногу исцеляя вас.", + // Debuffs + "buff.title.bleed": "Кровотечение", + "buff.desc.bleed": "Наносит регулярный урон.", + "buff.title.cursed": "Проклятие", + "buff.desc.cursed": "Вас прокляли.", + "buff.title.burn": "В огне", + "buff.desc.burn": "Вы горите живьём", + "buff.title.crippled": "Увечье", + "buff.desc.crippled": "Ваше движение затруднено, так как ваши ноги сильно травмированы.", + "buff.title.frozen": "Обморожение", + "buff.desc.frozen": "Скорость движения и атак снижена.", + "buff.title.wet": "Влага", + "buff.desc.wet": "Земля запутывает ваши ноги мешая хотьбе.", + "buff.title.ensnared": "Ловушка", + "buff.desc.ensnared": "Лоза опутывает ваши ноги затрудняя движение.", + // Buffs stats + "buff.stat.health": "Восстанавливает {str_total} здоровья", + "buff.stat.increase_max_energy": "Повышает максимум энергии на {strength}", + "buff.stat.increase_max_health": "Повышает максимальное здоровье на {strength}", + "buff.stat.invulnerability": "Дарует неуязвимость", + // Text + "buff.text.over_seconds": "более {dur_secs} секунд(ы)", + "buff.text.for_seconds": "на {dur_secs} секунды", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/char_selection.ron b/assets/voxygen/i18n/ru_RU/char_selection.ron index a990ea7775..7b7d500e38 100644 --- a/assets/voxygen/i18n/ru_RU/char_selection.ron +++ b/assets/voxygen/i18n/ru_RU/char_selection.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: { "char_selection.loading_characters": "Загрузка персонажей...", diff --git a/assets/voxygen/i18n/ru_RU/common.ron b/assets/voxygen/i18n/ru_RU/common.ron index b72812e10c..94381d0756 100644 --- a/assets/voxygen/i18n/ru_RU/common.ron +++ b/assets/voxygen/i18n/ru_RU/common.ron @@ -30,7 +30,7 @@ "common.decline": "Отказаться", "common.disclaimer": "Предупреждение", "common.cancel": "Отменить", - "common.none": "Отключены", + "common.none": "Отключено", "common.error": "Ошибка", "common.fatal_error": "Фатальная ошибка", "common.you": "Вы", diff --git a/assets/voxygen/i18n/ru_RU/hud/bag.ron b/assets/voxygen/i18n/ru_RU/hud/bag.ron new file mode 100644 index 0000000000..0931b249f1 --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/hud/bag.ron @@ -0,0 +1,48 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + // Inventory + "hud.bag.inventory": "Инвентарь {playername}", + "hud.bag.stats_title": "Характеристики {playername}", + "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": "Вторая рука", + "hud.bag.inactive_mainhand": "Неактивная главная рука", + "hud.bag.inactive_offhand": "Неактивная вторая рука", + "hud.bag.swap_equipped_weapons_title": "Поменять местами экипированное оружие", + "hud.bag.swap_equipped_weapons_desc": "Нажмите {key}", + "hud.bag.bag": "Рюкзак", + "hud.bag.health": "Здоровье", + "hud.bag.energy": "Энергия", + "hud.bag.combat_rating": "Боевой рейтинг", + "hud.bag.protection": "Защита", + "hud.bag.stun_res": "Устойчивость к оглушению", + "hud.bag.combat_rating_desc": "Рассчитано с учетом вашего\nоборудования и здоровья.", + "hud.bag.protection_desc": "Снижение урона за счет брони", + "hud.bag.stun_res_desc": "Устойчивость к оглушению от последовательных ударов.\nВосстанавливается как энергия.", + "hud.bag.sort_by_name": "Сортировать по имени", + "hud.bag.sort_by_quality": "Сортировать по количеству", + "hud.bag.sort_by_category": "Сортировать по категории", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/hud/char_window.ron b/assets/voxygen/i18n/ru_RU/hud/char_window.ron new file mode 100644 index 0000000000..adac07b31d --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/hud/char_window.ron @@ -0,0 +1,12 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + "character_window.character_name": "Имя персонажа" + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/hud/chat.ron b/assets/voxygen/i18n/ru_RU/hud/chat.ron new file mode 100644 index 0000000000..b23dcfe50d --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/hud/chat.ron @@ -0,0 +1,49 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + "hud.chat.all": "Все", + "hud.chat.chat_tab_hover_tooltip": "ПКМ для настроек", + + // Debuff outcomes + "hud.outcome.burning": "сгорел", + "hud.outcome.curse": "умер от проклятия", + "hud.outcome.bleeding": "умер от кровотечения", + "hud.outcome.crippled": "умер от множества травм", + "hud.outcome.frozen": "замёрз насмерть", + + // 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.died_of_pvp_buff_msg": "[{victim}] {died_of_buff} вызванного [{attacker}]", + "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.died_of_buff_nonexistent_msg": "[{victim}] {died_of_buff}", + + "hud.chat.died_of_npc_buff_msg": "[{victim}] {died_of_buff} вызванного {attacker}", + "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} секунд.", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/hud/crafting.ron b/assets/voxygen/i18n/ru_RU/hud/crafting.ron new file mode 100644 index 0000000000..967030b43a --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/hud/crafting.ron @@ -0,0 +1,38 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + "hud.crafting": "Крафт ", + "hud.crafting.recipes": "Рецепты", + "hud.crafting.ingredients": "Ингредиенты :", + "hud.crafting.craft": "Создать", + "hud.crafting.tool_cata": "Необходимо:", + // Crafting Stations + "hud.crafting.req_crafting_station": "Необходимо:", + "hud.crafting.anvil": "Наковальня", + "hud.crafting.cauldron": "Котел", + "hud.crafting.cooking_pot": "Котелок", + "hud.crafting.crafting_bench": "Верстак", + "hud.crafting.forge": "Кузница", + "hud.crafting.loom": "Ткацкий станок", + "hud.crafting.spinning_wheel": "Прялка", + "hud.crafting.tanning_rack": "Стойка для дубления кожи", + // Tabs + "hud.crafting.tabs.all": "Все", + "hud.crafting.tabs.armor": "Броня", + "hud.crafting.tabs.dismantle": "Разобрать", + "hud.crafting.tabs.food": "Еда", + "hud.crafting.tabs.glider": "Делтопланы", + "hud.crafting.tabs.potion": "Зелья", + "hud.crafting.tabs.tool": "Инструменты", + "hud.crafting.tabs.utility": "Утилиты", + "hud.crafting.tabs.weapon": "Оружие", + "hud.crafting.tabs.bag": "Рюкзаки", + "hud.crafting.tabs.processed_material": "Материалы", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/hud/group.ron b/assets/voxygen/i18n/ru_RU/hud/group.ron new file mode 100644 index 0000000000..2d2276eab8 --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/hud/group.ron @@ -0,0 +1,24 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + "hud.group": "Группа", + "hud.group.invite_to_join": "[{name}] пригласил вас в свою группу!", + "hud.group.invite_to_trade": "[{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": "Члены группы", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/hud/map.ron b/assets/voxygen/i18n/ru_RU/hud/map.ron new file mode 100644 index 0000000000..bedaa4dec5 --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/hud/map.ron @@ -0,0 +1,38 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + // Map and Questlog + "hud.map.map_title": "Карта", + "hud.map.qlog_title": "Задания", + "hud.map.topo_map": "Топографическая", + "hud.map.difficulty": "Сложность", + "hud.map.towns": "Города", + "hud.map.castles": "Замки", + "hud.map.dungeons": "Подземелья", + "hud.map.caves": "Пещеры", + "hud.map.cave": "Пещера", + "hud.map.peaks": "Горы", + "hud.map.voxel_map": "Воксельная карта", + "hud.map.trees": "Гигантские\nдеревья", + "hud.map.tree": "Гигантское\nдерево", + "hud.map.town": "Городок", + "hud.map.castle": "Замок", + "hud.map.dungeon": "Подземелье", + "hud.map.difficulty_dungeon": "Подземелье\n\nСложность: {difficulty}", + "hud.map.drag": "Тащить", + "hud.map.zoom": "Увеличить", + "hud.map.mid_click": "Установить\nотметку", + "hud.map.recenter": "По центру", + "hud.map.marked_location": "Отмеченное место", + "hud.map.marked_location_remove": "Нажмите, чтобы удалить", + "hud.map.change_map_mode": "Изменить режим карты", + "hud.map.toggle_minimap_voxel": "Подробная миникарта", + "hud.map.zoom_minimap_explanation": "Увеличьте миникарту, чтобы более детально рассмотреть область вокруг вас.", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/hud/misc.ron b/assets/voxygen/i18n/ru_RU/hud/misc.ron new file mode 100644 index 0000000000..02537a223e --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/hud/misc.ron @@ -0,0 +1,54 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + "hud.do_not_show_on_startup": "Не показывать это при запуске", + "hud.show_tips": "Показать советы", + "hud.quests": "Задания", + "hud.you_died": "Вы умерли", + "hud.waypoint_saved": "Путевая точка сохранена", + "hud.sp_arrow_txt": "ОО", + "hud.inventory_full": "Инвентарь полон", + + "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}, чтобы переключить отладочную информацию", + + // Respawn message + "hud.press_key_to_respawn": r#"Нажмите {key}, чтобы возродиться у последнего костра."#, + + // Tutorial Button + "hud.tutorial_btn": r#"Руководство"#, + "hud.tutorial_click_here": r#"Нажмите [ {key} ], чтобы включить курсор и нажмите эту кнопку!"#, + "hud.tutorial_elements": r#"Крафт"#, + +"hud.temp_quest_headline": r#"Привет путешественник!"#, +"hud.temp_quest_text": r#"Для начала, вы можете осматреть эту деревню и собрать немного припасов. + +Вы можете взять с собой в путешествие все, что вам нужно! + +Посмотрите в правый нижний угол экрана, чтобы найти различные вещи, такие как ваша сумка, меню крафта и карта. + +Станки для крафта позволяют создавать доспехи, оружие, еду и многое другое! + +Дикие животные по всему городу - отличный источник шкуры животных, из которой вы можете сделать защиту от опасностей этого мира. + +Когда почувствуете себя готовым, постарайтесь получить еще лучшее снаряжение из множества испытаний, отмеченных на вашей карте! +"#, + + "hud.spell": "Заклинания", + // Diary + "hud.diary": "Дневник", + + "hud.free_look_indicator": "Активирован свободный вид. Чтобы отключить, нажмите {key}.", + "hud.camera_clamp_indicator": "Активирован вертикальный фиксатор камеры. Чтобы отключить, нажмите {key}.", + "hud.auto_walk_indicator": "Активирована автоматическая ходьба/плавание", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/hud/sct.ron b/assets/voxygen/i18n/ru_RU/hud/sct.ron new file mode 100644 index 0000000000..381a1416c2 --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/hud/sct.ron @@ -0,0 +1,14 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + // SCT outputs + "hud.sct.experience": "{amount} Опыта", + "hud.sct.block": "БЛОК", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/hud/settings.ron b/assets/voxygen/i18n/ru_RU/hud/settings.ron new file mode 100644 index 0000000000..56724a30d5 --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/hud/settings.ron @@ -0,0 +1,137 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + // 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.show_hitboxes": "Отображать хитбоксы", + "hud.settings.show_chat": "Отображать чат", + "hud.settings.tips_on_startup": "Советы при запуске", + "hud.settings.ui_scale": "Масштаб пользовательского интерфейса", + "hud.settings.relative_scaling": "Относительное масштабирование", + "hud.settings.custom_scaling": "Настраиваемое масштабирование", + "hud.settings.crosshair": "Перекрестие", + "hud.settings.opacity": "Непрозрачность", + "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_opacity": "Прозрачность фона", + "hud.settings.chat_character_name": "Имена персонажей в чате", + "hud.settings.loading_tips": "Советы при загрузке", + "hud.settings.reset_interface": "По умолчанию", + + "hud.settings.pan_sensitivity": "Чувствительность мыши", + "hud.settings.zoom_sensitivity": "Чувствительность масштабирования", + "hud.settings.camera_clamp_angle": "Угол для режима вертикального фиксатора камеры", + "hud.settings.invert_scroll_zoom": "Инвертировать масштаб прокрутки", + "hud.settings.invert_mouse_y_axis": "Инвертировать ось Y мыши", + "hud.settings.invert_controller_y_axis": "Инвертировать ось Y контроллера", + "hud.settings.enable_mouse_smoothing": "Сглаживание камеры", + "hud.settings.free_look_behavior": "Свободное поведение", + "hud.settings.auto_walk_behavior": "Поведение при автоматической ходьбе", + "hud.settings.camera_clamp_behavior": "Поведение вертикального фиксатора камеры", + "hud.settings.player_physics_behavior": "Физика игрока (экспериментальная)", + "hud.settings.stop_auto_walk_on_input": "Остановить автоходьбу при движении", + "hud.settings.auto_camera": "Авто-камера", + "hud.settings.reset_gameplay": "По умолчанию", + + "hud.settings.view_distance": "Дальность\nвидимости", + "hud.settings.sprites_view_distance": "Дальность\nвидимости спрайтов", + "hud.settings.figures_view_distance": "Дальность\nвидимости объектов", + "hud.settings.maximum_fps": "Максимальный FPS", + "hud.settings.present_mode": "Режим показа", + "hud.settings.present_mode.fifo": "Fifo", + "hud.settings.present_mode.mailbox": "Mailbox", + "hud.settings.present_mode.immediate": "Immediate", + "hud.settings.fov": "Угол обзора (град.)", + "hud.settings.gamma": "Гамма", + "hud.settings.exposure": "Экспозиция", + "hud.settings.ambiance": "Яркость окружения", + "hud.settings.antialiasing_mode": "Режим сглаживания", + "hud.settings.upscale_factor": "Внутреннее разрешение", + "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.gpu_profiler": "Замеры GPU(не везде поддерживается)", + "hud.settings.particles": "Частицы", + "hud.settings.lossy_terrain_compression": "Сжатие рельефа с потерями", + "hud.settings.resolution": "Разрешение", + "hud.settings.bit_depth": "Битовая глубина", + "hud.settings.refresh_rate": "Частота обновления", + "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": "Уровень детализации", + "hud.settings.save_window_size": "Сохранить размер окна", + "hud.settings.reset_graphics": "По умолчанию", + + "hud.settings.master_volume": "Общая громкость", + "hud.settings.inactive_master_volume_perc": "Громкость неактивного окна", + "hud.settings.music_volume": "Громкость музыки", + "hud.settings.sound_effect_volume": "Громкость эффектов", + "hud.settings.audio_device": "Аудио-устройство", + "hud.settings.reset_sound": "По умолчанию", + + "hud.settings.english_fallback": "Отображать английский для отсутствующих переводов", + + "hud.settings.awaitingkey": "Нажмите клавишу...", + "hud.settings.unbound": "Отсутствует", + "hud.settings.reset_keybinds": "По умолчанию", + + "hud.settings.chat_tabs": "Вкладки чата", + "hud.settings.label": "Ярлык:", + "hud.settings.delete": "Удалить", + "hud.settings.show_all": "Отображать все", + "hud.settings.messages": "Сообщения", + "hud.settings.activity": "Активность", + "hud.settings.death": "Смерть", + "hud.settings.group": "Группа", + "hud.settings.faction": "Фракция", + "hud.settings.world": "Мир", + "hud.settings.region": "Регион", + "hud.settings.say": "Сказать", + "hud.settings.all": "Все", + "hud.settings.group_only": "Только группа", + "hud.settings.reset_chat" : "По умолчанию", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/ru_RU/skills.ron b/assets/voxygen/i18n/ru_RU/hud/skills.ron similarity index 96% rename from assets/voxygen/i18n/ru_RU/skills.ron rename to assets/voxygen/i18n/ru_RU/hud/skills.ron index e8de3706c2..1da764c4ff 100644 --- a/assets/voxygen/i18n/ru_RU/skills.ron +++ b/assets/voxygen/i18n/ru_RU/hud/skills.ron @@ -11,8 +11,8 @@ // General "hud.skill.inc_health_title": "Повышение здоровья", "hud.skill.inc_health": "Увеличивает максимальное здоровье на {boost}{SP}", - "hud.skill.inc_stam_title": "Повышение Выносливости", - "hud.skill.inc_stam": "Увеличивает максимальную выносливость на {boost}{SP}", + "hud.skill.inc_energy_title": "Повышение Выносливости", + "hud.skill.inc_energy": "Увеличивает максимальную выносливость на {boost}{SP}", "hud.skill.unlck_sword_title": "Разблокировать меч", "hud.skill.unlck_sword": "Разблокировать древо навыков владения мечом{SP}", "hud.skill.unlck_axe_title": "Разблокировать топор", @@ -27,8 +27,8 @@ "hud.skill.unlck_sceptre": "Разблокировать древо навыков владения скипетром{SP}", "hud.skill.dodge_title": "Перекат", "hud.skill.dodge": "Перекат активируется средним щелчком мыши. Во время переката вы игнорируете урон ближнего боя.", - "hud.skill.roll_stamina_title": "Стоимость активации переката", - "hud.skill.roll_stamina": "Перекат использует на {boost}% меньше выносливости{SP}", + "hud.skill.roll_energy_title": "Стоимость активации переката", + "hud.skill.roll_energy": "Перекат использует на {boost}% меньше выносливости{SP}", "hud.skill.roll_speed_title": "Скорость переката", "hud.skill.roll_speed": "Перекат на {boost}% быстрее{SP}", "hud.skill.roll_dur_title": "Продолжительность переката", @@ -95,8 +95,8 @@ "hud.skill.st_flamethrower_damage" : "Урон больше на {boost}%{SP}", "hud.skill.st_explosion_radius_title" : "Радиус взрыва", "hud.skill.st_explosion_radius" : "Радиус взрыва больше на {boost}%{SP}", - "hud.skill.st_stamina_regen_title" : "Востановление выносливости", - "hud.skill.st_stamina_regen" : "Увеличивает прирост выносливости на {boost}%{SP}", + "hud.skill.st_energy_regen_title" : "Востановление выносливости", + "hud.skill.st_energy_regen" : "Увеличивает прирост выносливости на {boost}%{SP}", "hud.skill.st_fireball_title" : "Огненый шар", "hud.skill.st_fireball" : "Стреляет огненным шаром, который взрывается при ударе", "hud.skill.st_damage_title" : "Урон", diff --git a/assets/voxygen/i18n/ru_RU/hud/social.ron b/assets/voxygen/i18n/ru_RU/hud/social.ron new file mode 100644 index 0000000000..7daa9e8147 --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/hud/social.ron @@ -0,0 +1,22 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + "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": "Учетная запись", + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/ru_RU/hud/trade.ron b/assets/voxygen/i18n/ru_RU/hud/trade.ron new file mode 100644 index 0000000000..31de7304a2 --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/hud/trade.ron @@ -0,0 +1,30 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + "hud.trade.trade_window": "Окно торговли", + "hud.trade.phase1_description": "Перетащите предметы, которые вы хотите обменять\nв соответствующую область.", + "hud.trade.phase2_description": "Сделка теперь заблокирована, чтобы у вас было время\nеё просмотреть.", + /// Phase3 should only be visible for a few milliseconds if everything is working properly, but is included for completeness + "hud.trade.phase3_description": "Сделка обрабатывается.", + "hud.trade.persons_offer": "предложение от {playername}", + "hud.trade.has_accepted": "{playername}\nпринял", + "hud.trade.accept": "Принять", + "hud.trade.decline": "Отклонить", + "hud.trade.invite_sent": "Запрос на обмен отправлен {playername}.", + "hud.trade.result.completed": "Сделка успешно завершена.", + "hud.trade.result.declined": "Сделка отклонена.", + "hud.trade.result.nospace": "Недостаточно места для завершения сделки.", + "hud.trade.buy_price": "Цена покупки", + "hud.trade.sell_price": "Цена продажи", + "hud.trade.coin": "монеты", + "hud.trade.tooltip_hint_1": "", + "hud.trade.tooltip_hint_2": "", + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/ru_RU/main.ron b/assets/voxygen/i18n/ru_RU/main.ron index 752188d27f..f29dbf2775 100644 --- a/assets/voxygen/i18n/ru_RU/main.ron +++ b/assets/voxygen/i18n/ru_RU/main.ron @@ -65,5 +65,26 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "Нажмите 'G', чтобы зажечь фонарь.", + "Нажмите 'F1', чтобы просмотреть все клавиши по умолчанию.", + "Вы можете ввести /tell или /s, чтобы общаться только с игроками непосредственно вокруг вас.", + "Вы можете ввести /region или /r, чтобы общаться только с игроками в паре сотен блоков вокруг вас.", + "Администраторы могут использовать команду /build для входа в режим постройки.", + "Вы можете ввести /group или /g, чтобы общаться только с игроками в вашей текущей группе.", + "Чтобы отправить личные сообщения, введите /tell, а затем имя игрока и ваше сообщение.", + "Смотрите внимательно чтобы найти еду, сундуки и другие предметы, разбросанные по всему миру!", + "Инвентарь, заполненный едой? Попробуйте приготовить из нее еду получше!", + "Интересно, чем можно заняться? Попробуйте пройти одно из подземелий, отмеченных на карте!", + "Не забудьте настроить графику для вашей системы. Нажмите 'N', чтобы открыть настройки.", + "Играть с другими-это весело! Нажмите 'О', чтобы узнать, кто находится в сети.", + "Нажмите 'J', чтобы танцевать. Вечеринка!", + "Нажмите 'L-Shift', чтобы открыть свой дельтаплан и покорить небо.", + "Veloren все еще находится в Пре-Альфе. Мы делаем все возможное, чтобы улучшать его каждый день!", + "Если вы хотите присоединиться к команде разработчиков или просто пообщаться с нами, присоединяйтесь к нашему серверу Discord.", + "Вы можете переключить отображение количества здоровья на панели здоровья в настройках.", + "Сядьте у костра (с помощью клавиши 'К'), чтобы залечить свои раны.", + "Вам нужно больше сумок или лучшая броня, чтобы продолжить свое путешествие? Нажмите 'C', чтобы открыть меню крафта!", + ], } ) diff --git a/assets/voxygen/i18n/ru_RU/npc.ron b/assets/voxygen/i18n/ru_RU/npc.ron new file mode 100644 index 0000000000..5cca03026f --- /dev/null +++ b/assets/voxygen/i18n/ru_RU/npc.ron @@ -0,0 +1,184 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for RUS +( + string_map: { + + }, + + + vector_map: { + "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": [ + "Помогите, Меня атакуют!", + "Помогите, Меня атакуют!", + "Оуч, Меня атакуют!", + "Оуч, Меня атакуют!", + "Помоги мне! Меня атакуют!", + "Меня атакуют! Помогите!", + "Меня атакуют! Помогите мне!", + "Помогите!", + "Помогите! Помогите!", + "Помогите! Помогите! Помогите!", + "Меня атакуют!", + "АААААА! Меня атакуют!", + "АААААА! Меня атакуют! Помогите!", + "Помогите! Мы атакованны!", + "Помогите! Убийца!", + "Помогите! Убийца на свободе!", + "Помогите! Они пытаются меня убить!", + "Стража, Меня атакуют!", + "Стража! Меня атакуют!", + "Меня атакуют! Стража!", + "Помогите! Стража! Меня атакуют!", + "Стража! Скорее!", + "Стража! Стража!", + "Стража! Этот злодей бьёт меня!", + "Стража, Схватите этого негодяя!", + "Стража! Здесь убийца!", + "Стража! Помогите me!", + "Тебе это не сойдет с рук! Стража!", + "Ты изверг!", + "Помогите мне!", + "Помогите! Пожалуйста!", + "Ой! Стража! Помогите!", + "Они идут за мной!", + "Помогите! Помогите! Меня постигла расплата!", + "Ах, теперь мы видим насилие, присущее системе.", + "Это всего лишь царапина!", + "Остановитесь!", + "Что я тебе сделал?!", + "Пожалуйста, не бей!", + "Эй! Смотри, куда направляешь эту штуку", + "Гнусный негодяй, проваливай отсюда!", + "Прекрати! Уходи!", + "Ты уже достал!", + "Эй! Что ты возомнил о себе?!", + "Я тебе башку оторву!", + "Остановись пожалуйста. У меня ничего нет!", + "Я позову брата, он больше меня", + "Нет! Я расскажу маме!", + "Будь ты проклят!", + "Пожалуйста, не делай этого.", + "Это не приятно!", + "Ваше оружие работает, вы можете убрать его прямо сейчас!", + "Пощади!", + "Пожалуйста, у меня семья!", + "Я слишком молод чтобы умереть!", + "Может договоримся?", + "Насилие не выход!", + "Сегодня выдался плохой день...", + "Эй, это больно!", + "Ик!", + "Как грубо!", + "Остановись, прошу тебя!", + "Чтоб ты сдох!", + "Это не смешно.", + "Как ты смеешь?!", + "Ты заплатишь за это!", + "Ты об этом пожалеешь!", + "Не заставляй меня делать тебе больно!", + "Произошла какая то ошибка!", + "Не делай этого!", + "Изыди, дьявол", + "Это очень больно!", + "Зачем ты это сделал?", + "Ради всего святого, прекрати!", + "Ты меня перепутал с кем то", + "Я не заслуживаю этого!", + "Пожалуйста, больше так не делай.", + "Стража, утопите этого монстра в озере!", + "Я натравлю своего tarasque на тебя!", + "Почему я?", + ], + "npc.speech.villager_enemy_killed": [ + "Я уничтожил врага!", + "Наконец-то мир!", + "... что же я наделал?", + ] + } +) diff --git a/assets/voxygen/i18n/sv_SE/_manifest.ron b/assets/voxygen/i18n/sv_SE/_manifest.ron new file mode 100644 index 0000000000..7ecd68218b --- /dev/null +++ b/assets/voxygen/i18n/sv_SE/_manifest.ron @@ -0,0 +1,32 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for sweden Swedish +( + metadata: ( + language_name: "Svenska", + language_identifier: "sv_SE", + ), + convert_utf8_to_ascii: false, + fonts: { + "opensans": Font ( + asset_key: "voxygen.font.OpenSans-Regular", + scale_ratio: 1.0, + ), + "metamorph": Font ( + asset_key: "voxygen.font.Metamorphous-Regular", + scale_ratio: 1.0, + ), + "alkhemi": Font ( + asset_key: "voxygen.font.Alkhemikal", + scale_ratio: 1.0, + ), + "wizard": Font ( + asset_key: "voxygen.font.wizard", + scale_ratio: 1.0, + ), + "cyri": Font ( + asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", + scale_ratio: 1.0, + ), + } +) diff --git a/assets/voxygen/i18n/sv/buff.ron b/assets/voxygen/i18n/sv_SE/buff.ron similarity index 96% rename from assets/voxygen/i18n/sv/buff.ron rename to assets/voxygen/i18n/sv_SE/buff.ron index ea29b691d8..efc98bfbb6 100644 --- a/assets/voxygen/i18n/sv/buff.ron +++ b/assets/voxygen/i18n/sv_SE/buff.ron @@ -38,7 +38,7 @@ "buff.desc.ensnared": "Rankor greppar tag i dina ben vilket begränsar dina rörelser.", // Buffs stats "buff.stat.health": "Återger {str_total} hälsa", - "buff.stat.increase_max_stamina": "Ökar max-uthålligheten med {strength}", + "buff.stat.increase_max_energy": "Ökar max-uthålligheten med {strength}", "buff.stat.increase_max_health": "Ökar maxhälsan med {strength}", "buff.stat.invulnerability": "Ger osårbarhet", // Text diff --git a/assets/voxygen/i18n/sv/char_selection.ron b/assets/voxygen/i18n/sv_SE/char_selection.ron similarity index 100% rename from assets/voxygen/i18n/sv/char_selection.ron rename to assets/voxygen/i18n/sv_SE/char_selection.ron diff --git a/assets/voxygen/i18n/sv/common.ron b/assets/voxygen/i18n/sv_SE/common.ron similarity index 100% rename from assets/voxygen/i18n/sv/common.ron rename to assets/voxygen/i18n/sv_SE/common.ron diff --git a/assets/voxygen/i18n/sv/esc_menu.ron b/assets/voxygen/i18n/sv_SE/esc_menu.ron similarity index 100% rename from assets/voxygen/i18n/sv/esc_menu.ron rename to assets/voxygen/i18n/sv_SE/esc_menu.ron diff --git a/assets/voxygen/i18n/sv/gameinput.ron b/assets/voxygen/i18n/sv_SE/gameinput.ron similarity index 100% rename from assets/voxygen/i18n/sv/gameinput.ron rename to assets/voxygen/i18n/sv_SE/gameinput.ron diff --git a/assets/voxygen/i18n/sv/hud/bag.ron b/assets/voxygen/i18n/sv_SE/hud/bag.ron similarity index 97% rename from assets/voxygen/i18n/sv/hud/bag.ron rename to assets/voxygen/i18n/sv_SE/hud/bag.ron index 26be317f5e..42f8d8f7ba 100644 --- a/assets/voxygen/i18n/sv/hud/bag.ron +++ b/assets/voxygen/i18n/sv_SE/hud/bag.ron @@ -30,7 +30,7 @@ "hud.bag.swap_equipped_weapons_desc": "Tryck {key}", "hud.bag.bag": "Säck", "hud.bag.health": "Hälsa", - "hud.bag.stamina": "Uthållighet", + "hud.bag.energy": "Uthållighet", "hud.bag.combat_rating": "Stridsduglighet", "hud.bag.protection": "Skydd", "hud.bag.stun_res": "Motståndskraft mot medvetslöshet", diff --git a/assets/voxygen/i18n/sv/hud/char_window.ron b/assets/voxygen/i18n/sv_SE/hud/char_window.ron similarity index 100% rename from assets/voxygen/i18n/sv/hud/char_window.ron rename to assets/voxygen/i18n/sv_SE/hud/char_window.ron diff --git a/assets/voxygen/i18n/sv/hud/chat.ron b/assets/voxygen/i18n/sv_SE/hud/chat.ron similarity index 100% rename from assets/voxygen/i18n/sv/hud/chat.ron rename to assets/voxygen/i18n/sv_SE/hud/chat.ron diff --git a/assets/voxygen/i18n/sv/hud/crafting.ron b/assets/voxygen/i18n/sv_SE/hud/crafting.ron similarity index 100% rename from assets/voxygen/i18n/sv/hud/crafting.ron rename to assets/voxygen/i18n/sv_SE/hud/crafting.ron diff --git a/assets/voxygen/i18n/sv/hud/group.ron b/assets/voxygen/i18n/sv_SE/hud/group.ron similarity index 100% rename from assets/voxygen/i18n/sv/hud/group.ron rename to assets/voxygen/i18n/sv_SE/hud/group.ron diff --git a/assets/voxygen/i18n/sv/hud/map.ron b/assets/voxygen/i18n/sv_SE/hud/map.ron similarity index 100% rename from assets/voxygen/i18n/sv/hud/map.ron rename to assets/voxygen/i18n/sv_SE/hud/map.ron diff --git a/assets/voxygen/i18n/sv/hud/misc.ron b/assets/voxygen/i18n/sv_SE/hud/misc.ron similarity index 100% rename from assets/voxygen/i18n/sv/hud/misc.ron rename to assets/voxygen/i18n/sv_SE/hud/misc.ron diff --git a/assets/voxygen/i18n/sv/hud/sct.ron b/assets/voxygen/i18n/sv_SE/hud/sct.ron similarity index 100% rename from assets/voxygen/i18n/sv/hud/sct.ron rename to assets/voxygen/i18n/sv_SE/hud/sct.ron diff --git a/assets/voxygen/i18n/sv/hud/hud_settings.ron b/assets/voxygen/i18n/sv_SE/hud/settings.ron similarity index 98% rename from assets/voxygen/i18n/sv/hud/hud_settings.ron rename to assets/voxygen/i18n/sv_SE/hud/settings.ron index 98024b5a3e..0e69dd102c 100644 --- a/assets/voxygen/i18n/sv/hud/hud_settings.ron +++ b/assets/voxygen/i18n/sv_SE/hud/settings.ron @@ -17,7 +17,7 @@ "hud.settings.relative_scaling": "Relativ skalning", "hud.settings.custom_scaling": "Anpassad skalning", "hud.settings.crosshair": "Hårkors", - "hud.settings.transparency": "Transparens", + "hud.settings.opacity": "Transparens", "hud.settings.hotbar": "Snabbåtkomst", "hud.settings.toggle_shortcuts": "Växla genvägar", "hud.settings.buffs_skillbar": "Buffs at Skillbar", // TODO @@ -35,7 +35,7 @@ "hud.settings.values": "Värden", "hud.settings.percentages": "Procent", "hud.settings.chat": "Konversation", - "hud.settings.background_transparency": "Bakgrundstransparens", + "hud.settings.background_opacity": "Bakgrundstransparens", "hud.settings.chat_character_name": "Karaktärers namn i konverationer", "hud.settings.loading_tips": "Tips på laddningsskärmen", "hud.settings.reset_interface": "Återställ till standard", diff --git a/assets/voxygen/i18n/sv/skills.ron b/assets/voxygen/i18n/sv_SE/hud/skills.ron similarity index 97% rename from assets/voxygen/i18n/sv/skills.ron rename to assets/voxygen/i18n/sv_SE/hud/skills.ron index 2605641c13..067a7b3965 100644 --- a/assets/voxygen/i18n/sv/skills.ron +++ b/assets/voxygen/i18n/sv_SE/hud/skills.ron @@ -11,8 +11,8 @@ // General "hud.skill.inc_health_title": "Mer hälsa", "hud.skill.inc_health": "Ökar maxhälsan med {boost}{SP}", - "hud.skill.inc_stam_title": "Ökad uthållighet", - "hud.skill.inc_stam": "Ökar max-uthålligheten med {boost}{SP}", + "hud.skill.inc_energy_title": "Ökad uthållighet", + "hud.skill.inc_energy": "Ökar max-uthålligheten med {boost}{SP}", "hud.skill.unlck_sword_title": "Lås upp svärd", "hud.skill.unlck_sword": "Låser upp färdighetsträdet för svärd{SP}", "hud.skill.unlck_axe_title": "Lås upp yxa", @@ -27,8 +27,8 @@ "hud.skill.unlck_sceptre": "Låser upp färdighetsträdet för spira{SP}", "hud.skill.dodge_title": "Ducka", "hud.skill.dodge": "Rulla åt sidan genom att mittenklicka och bli immun mot närstridsattacker under tiden du rullar.", - "hud.skill.roll_stamina_title": "Uthållighetskostnad för rullning", - "hud.skill.roll_stamina": "Rullning kostar {boost}% mindre uthållighet{SP}", + "hud.skill.roll_energy_title": "Uthållighetskostnad för rullning", + "hud.skill.roll_energy": "Rullning kostar {boost}% mindre uthållighet{SP}", "hud.skill.roll_speed_title": "Rullningshastighet", "hud.skill.roll_speed": "Ralla {boost}% fortare{SP}", "hud.skill.roll_dur_title": "Rullningstid", @@ -97,8 +97,8 @@ "hud.skill.st_flamethrower_damage" : "Orsakar {boost}% mer skada{SP}", "hud.skill.st_explosion_radius_title" : "Explosionsradie", "hud.skill.st_explosion_radius" : "Större är bättre, explosionsradien ökar med {boost}%{SP}", - "hud.skill.st_stamina_regen_title" : "Engergiåterhämtning", - "hud.skill.st_stamina_regen" : "Återhämta ytterligare {boost}% energi{SP}", + "hud.skill.st_energy_regen_title" : "Engergiåterhämtning", + "hud.skill.st_energy_regen" : "Återhämta ytterligare {boost}% energi{SP}", "hud.skill.st_fireball_title" : "Eldklot", "hud.skill.st_fireball" : "Skjuter ett eldklot som exploderar vid träff", "hud.skill.st_damage_title" : "Skada", diff --git a/assets/voxygen/i18n/sv/hud/social.ron b/assets/voxygen/i18n/sv_SE/hud/social.ron similarity index 100% rename from assets/voxygen/i18n/sv/hud/social.ron rename to assets/voxygen/i18n/sv_SE/hud/social.ron diff --git a/assets/voxygen/i18n/sv/hud/trade.ron b/assets/voxygen/i18n/sv_SE/hud/trade.ron similarity index 100% rename from assets/voxygen/i18n/sv/hud/trade.ron rename to assets/voxygen/i18n/sv_SE/hud/trade.ron diff --git a/assets/voxygen/i18n/sv/main.ron b/assets/voxygen/i18n/sv_SE/main.ron similarity index 64% rename from assets/voxygen/i18n/sv/main.ron rename to assets/voxygen/i18n/sv_SE/main.ron index ce961f7526..0798d0ed22 100644 --- a/assets/voxygen/i18n/sv/main.ron +++ b/assets/voxygen/i18n/sv_SE/main.ron @@ -66,5 +66,27 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "Tryck 'G' för att tända din lykta.", + "Tryck 'F1' för att se alla standardgenvägar.", + "Du kan skriva /say eller /s för att endast prata med spelare i din närhet.", + "Du kan skriva /region eller /r to för att endast prata med spelare upp till hundra block bort.", + "Administratörer kan använda kommandot /build för att växla till byggläget.", + "Du kan skriva /group eller /g för att endast prata med spelare i din nuvarande grupp.", + "Använd /tell följt av ett spelarnamn och meddelande för att kommunicera direkt med en spelare.", + "Håll ett öga öppet för mat, kistor och andra fynd som finns utspridda över hela världen!", + "Inventory filled with food? Try crafting better food from it!", /// TODO translate crafting + "Wondering what there is to do? Try out one of the dungeons marked on the map!", /// TODO translate dungeon + "Glöm inte anpassa grafiken för din dator. Tryck 'N' för att öppna inställningarna.", + "Delad glädje är dubbel glädje! Tryck 'O' för att se vilka som spelar just nu.", + "Tryck 'J' för att dansa. Party!", + "Tryck på den vänstra Shift-tangenten för att använda din glidare och bli himlarnas härskare.", + "Veloren är fortfarande i Pre-Alpha-stadiet. Vi gör vårt yttersta för att förbättra spelet varje dag!", + "Om du vill gå med i utvecklargruppen eller bara snacka med oss får du gärna logga in på vår Discord-server.", + "Du kan välja att visa din hälsostatus i inställningarna.", + "Sitt nära en lägereld (tryck 'K') för att långsamt återhämta dig från skador.", + "Behöver du fler säckar eller bättre rustning för din fortsatta färd? Tryck 'C' för att öppna the crafting menu!", /// TODO translate crafting + "Försök att hoppa när du rullar genom kreatur.", + ], } ) diff --git a/assets/voxygen/i18n/sv/_manifest.ron b/assets/voxygen/i18n/sv_SE/npc.ron similarity index 76% rename from assets/voxygen/i18n/sv/_manifest.ron rename to assets/voxygen/i18n/sv_SE/npc.ron index 73dd4fc971..a3fda4dc96 100644 --- a/assets/voxygen/i18n/sv/_manifest.ron +++ b/assets/voxygen/i18n/sv_SE/npc.ron @@ -1,62 +1,12 @@ /// WARNING: Localization files shall be saved in UTF-8 format without BOM -/// Localization for "global" Swedish +/// Localization for sweden Swedish ( - metadata: ( - language_name: "Svenska", - language_identifier: "sv", - ), - convert_utf8_to_ascii: false, - fonts: { - "opensans": Font ( - asset_key: "voxygen.font.OpenSans-Regular", - scale_ratio: 1.0, - ), - "metamorph": Font ( - asset_key: "voxygen.font.Metamorphous-Regular", - scale_ratio: 1.0, - ), - "alkhemi": Font ( - asset_key: "voxygen.font.Alkhemikal", - scale_ratio: 1.0, - ), - "wizard": Font ( - asset_key: "voxygen.font.wizard", - scale_ratio: 1.0, - ), - "cyri": Font ( - asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", - scale_ratio: 1.0, - ), - }, - string_map: { }, vector_map: { - "loading.tips": [ - "Tryck 'G' för att tända din lykta.", - "Tryck 'F1' för att se alla standardgenvägar.", - "Du kan skriva /say eller /s för att endast prata med spelare i din närhet.", - "Du kan skriva /region eller /r to för att endast prata med spelare upp till hundra block bort.", - "Administratörer kan använda kommandot /build för att växla till byggläget.", - "Du kan skriva /group eller /g för att endast prata med spelare i din nuvarande grupp.", - "Använd /tell följt av ett spelarnamn och meddelande för att kommunicera direkt med en spelare.", - "Håll ett öga öppet för mat, kistor och andra fynd som finns utspridda över hela världen!", - "Inventory filled with food? Try crafting better food from it!", /// TODO translate crafting - "Wondering what there is to do? Try out one of the dungeons marked on the map!", /// TODO translate dungeon - "Glöm inte anpassa grafiken för din dator. Tryck 'N' för att öppna inställningarna.", - "Delad glädje är dubbel glädje! Tryck 'O' för att se vilka som spelar just nu.", - "Tryck 'J' för att dansa. Party!", - "Tryck på den vänstra Shift-tangenten för att använda din glidare och bli himlarnas härskare.", - "Veloren är fortfarande i Pre-Alpha-stadiet. Vi gör vårt yttersta för att förbättra spelet varje dag!", - "Om du vill gå med i utvecklargruppen eller bara snacka med oss får du gärna logga in på vår Discord-server.", - "Du kan välja att visa din hälsostatus i inställningarna.", - "Sitt nära en lägereld (tryck 'K') för att långsamt återhämta dig från skador.", - "Behöver du fler säckar eller bättre rustning för din fortsatta färd? Tryck 'C' för att öppna the crafting menu!", /// TODO translate crafting - "Försök att hoppa när du rullar genom kreatur.", - ], "npc.speech.villager": [ "Är inte detta en underbar dag?", "Hur står det till idag?", diff --git a/assets/voxygen/i18n/tr_TR/_manifest.ron b/assets/voxygen/i18n/tr_TR/_manifest.ron index 4584b27293..50c818fc21 100644 --- a/assets/voxygen/i18n/tr_TR/_manifest.ron +++ b/assets/voxygen/i18n/tr_TR/_manifest.ron @@ -40,113 +40,5 @@ asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", scale_ratio: 1.0, ), - }, - - string_map: { - - }, - - vector_map: { - "loading.tips": [ - "'G'ye basarak fenerini yak.", - "'F1'e basarak bütün kontrolleri görebilirsin.", - "'/say' veya '/s' yazarak sadece hemen yanındaki oyuncularla konuşabilirsin.", - "'/region' veya '/r' yazarak sadece bir kaç yüz blok içindeki oyuncularla konuşabilirsin.", - "Özel bir mesaj göndermek için '/tell' ve sonra bir oyuncu ismi ile mesajını yaz.", - "Aynı seviyedeki NPCler farklı zorluklara sahip olabilir.", - "Yemek, sandık ve diğer ganimetler için yere bak!", - "Envanterin yemekle mi dolu? Onları kullanarak daha iyi yemek yapmaya çalış!", - "Ne yapabileceğini merak mı ediyorsun? Zindanlar haritada kahverengi bölgeler olarak işaretlenmiştir!", - "Grafikleri sistemin için ayarlamayı unutma. 'N'e basarak ayarları aç.", - "Başkalarıyla oynamak eğlencelidir! 'O'ya basarak kimlerin çevirimiçi olduğunu gör.", - "Can barının yanında kurukafa olan bir NPC senden hayli bir güçlüdür.", - "'J'ye basarak dans et. Parti!", - "'L-Shift'e basarak Planörünü aç ve gökyüzünü fethet.", - "Veloren hala Pre-Alpha'da. Onu geliştirmek için her gün elimizden geleni yapıyoruz!", - "Geliştirme Takımına katılmak istiyorsan veya sadece sohbet etmek istiyorsan Discord sunucumuza katıl.", - "Can barında canı sayı olarak görmek istiyorsan, bunu ayarlardan aktifleştirebilirsin.", - "Niteliklerini görmek için envanterindeki 'Nitelikler' düğmesine tıklayabilirsin.", - ], - "npc.speech.villager_under_attack": [ - "Saldırı altındayım, yardım edin!", - "Saldırı altındayım! Yardım edin!", - "Ahhh! Saldırı altındayım!", - "Ahhh! Saldırı altındayım! Yardım edin!", - "Saldırı altındayım! Bana yardım edin!", - "Yardım edin! Saldırı altındayım!", - "Bana yardım edin! Saldırı altındayım!", - "Yardım edin!", - "Yardım edin! Yardım edin!", - "Yardım edin! Yardım edin! Yardım edin!", - "Saldırı altındayım!", - "AAAHHHH! Saldırı altındayım!", - "AAAHHHH! Yardım edin! Saldırı altındayım!", - "Saldırı altındayız! Yardım edin!", - "Katil! Yardım edin!", - "Bir katil serbestçe dolaşıyor! Yardım edin!", - "Beni öldürmeye çalışıyorlar! Yardım edin!", - "Gardiyanlar, saldırı altındayım!", - "Saldırı altındayım! Gardiyanlar!", - "Gardiyanlar! Saldırı altındayım!", - "Saldırı altındayım! Gardiyanlar! Yardım edin!", - "Gardiyanlar! Çabuk gelin!", - "Gardiyanlar! Gardiyanlar!", - "Bana saldıran bir kötü var! Yardım edin!", - "Gardiyanlar, bu pis kötüyü öldürün!", - "Gardiyanlar! Burada bir katil var!", - "Gardiyanlar! Bana yardım edin!", - "Bu yanına kalmayacak! Gardiyanlar!", - "Seni şeytan!", - "Bana yardım edin!", - "Lütfen! Yardım edin!", - "Ahhh! Gardiyanlar! Yardım edin!", - "Benim için geliyorlar!", - "Yardım edin! Yardım edin! Baskı altındayım!", - "Ah, artık sistemin doğasında var olan şiddeti görüyoruz.", - "Bu bana göre bir çizik bile değil!", - "Yapma şunu!", - "Ben sana ne yaptım ki?!", - "Lütfen bana saldırmayı kes!", - "Hey! Onu nereye yönelttiğine dikkat et!", - "Aşağılık herif, gözüm bile görmesin!", - "Durdur şunu! Git buradan!", - "Şimdi beni kızdırmaya başladın!", - "Hey! Sen kim olduğunu zannediyorsun ki?!", - "Bunun için kelleni alacağım!", - "Yapma, lütfen! Değerli hiçbir şeyim yok bile!", - "Kardeşimi üzerine salacağım, o benden bile büyük!", - "Olamaaaz, Seni anneme söyleyeceğim!", - "Lanet olsun sana!", - "Lütfen yapma şunu.", - "Bunu yapman pek kibarca değildi!", - "Evet silahın çalışıyor, şimdi kaldırabilir misin?", - "Bağışlayın beni!", - "Lütfen, benim bir ailem var!", - "Ölmek için çok gencim!", - "Bunu konuşarak çözebilir miyiz?", - "Şiddet hiçbir zaman çare değildir!", - "Günüm gittikçe kötüleşiyor...", - "Hey, bu acıttı!", - "Eek!", - "Ne kadar da kaba!", - "Dur, sana yalvarırım!", - "Lanet olsun sana!", - "Bu eğlenceli bile değil.", - "Ne cürret?!", - "Bunu sana ödeteceğim!", - "Yapmaya devam edersen bunun için pişman olacaksın!", - "Sana zarar vermek zorunda bırakma beni!", - "Bir yanlış anlaşılma olmalı!", - "Bunu yapmak zorunda değilsin!", - "Defol!", - "Bu gerçekten acıttı!", - "Bunu neden yaptın ki?", - "Ruhlar tarafından, dur!", - "Beni başkasıyla karıştırmış olmalısın!", - "Bunu haketmiyorum!", - "Lütfen bunu bir daha yapma.", - "Muhafızlar, şu canavarı göle atın!", - "Tarrasque'ımı üzerine salarım!", - ], } ) diff --git a/assets/voxygen/i18n/tr_TR/hud/hud_settings.ron b/assets/voxygen/i18n/tr_TR/hud/settings.ron similarity index 97% rename from assets/voxygen/i18n/tr_TR/hud/hud_settings.ron rename to assets/voxygen/i18n/tr_TR/hud/settings.ron index 57ae32a996..db24682753 100644 --- a/assets/voxygen/i18n/tr_TR/hud/hud_settings.ron +++ b/assets/voxygen/i18n/tr_TR/hud/settings.ron @@ -15,7 +15,7 @@ "hud.settings.relative_scaling": "Otomatik Ölçek", "hud.settings.custom_scaling": "Sabit Ölçek", "hud.settings.crosshair": "İmleç tipi", - "hud.settings.transparency": "Şeffaflık", + "hud.settings.opacity": "Şeffaflık", "hud.settings.hotbar": "Aksiyon Çubuğu", "hud.settings.toggle_shortcuts": "Kısayolları aç/kapa", "hud.settings.buffs_skillbar": "Etkiler yetenek çubuğunun üstünde", @@ -33,7 +33,7 @@ "hud.settings.values": "Sayılar", "hud.settings.percentages": "Yüzdeler", "hud.settings.chat": "Sohbet", - "hud.settings.background_transparency": "Arkaplan Şeffaflığı", + "hud.settings.background_opacity": "Arkaplan Şeffaflığı", "hud.settings.chat_character_name": "Sohbette karakter isimlerini göster", "hud.settings.loading_tips": "Yükleme ekranı ipuçları", diff --git a/assets/voxygen/i18n/tr_TR/main.ron b/assets/voxygen/i18n/tr_TR/main.ron index 5df46b198d..2f81506879 100644 --- a/assets/voxygen/i18n/tr_TR/main.ron +++ b/assets/voxygen/i18n/tr_TR/main.ron @@ -63,5 +63,25 @@ bir hesap oluşturabilirsin."#, vector_map: { + "loading.tips": [ + "'G'ye basarak fenerini yak.", + "'F1'e basarak bütün kontrolleri görebilirsin.", + "'/say' veya '/s' yazarak sadece hemen yanındaki oyuncularla konuşabilirsin.", + "'/region' veya '/r' yazarak sadece bir kaç yüz blok içindeki oyuncularla konuşabilirsin.", + "Özel bir mesaj göndermek için '/tell' ve sonra bir oyuncu ismi ile mesajını yaz.", + "Aynı seviyedeki NPCler farklı zorluklara sahip olabilir.", + "Yemek, sandık ve diğer ganimetler için yere bak!", + "Envanterin yemekle mi dolu? Onları kullanarak daha iyi yemek yapmaya çalış!", + "Ne yapabileceğini merak mı ediyorsun? Zindanlar haritada kahverengi bölgeler olarak işaretlenmiştir!", + "Grafikleri sistemin için ayarlamayı unutma. 'N'e basarak ayarları aç.", + "Başkalarıyla oynamak eğlencelidir! 'O'ya basarak kimlerin çevirimiçi olduğunu gör.", + "Can barının yanında kurukafa olan bir NPC senden hayli bir güçlüdür.", + "'J'ye basarak dans et. Parti!", + "'L-Shift'e basarak Planörünü aç ve gökyüzünü fethet.", + "Veloren hala Pre-Alpha'da. Onu geliştirmek için her gün elimizden geleni yapıyoruz!", + "Geliştirme Takımına katılmak istiyorsan veya sadece sohbet etmek istiyorsan Discord sunucumuza katıl.", + "Can barında canı sayı olarak görmek istiyorsan, bunu ayarlardan aktifleştirebilirsin.", + "Niteliklerini görmek için envanterindeki 'Nitelikler' düğmesine tıklayabilirsin.", + ], } ) diff --git a/assets/voxygen/i18n/tr_TR/npc.ron b/assets/voxygen/i18n/tr_TR/npc.ron new file mode 100644 index 0000000000..dd433e925a --- /dev/null +++ b/assets/voxygen/i18n/tr_TR/npc.ron @@ -0,0 +1,92 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Turkish (Turkey) +( + string_map: { + + }, + + vector_map: { + "npc.speech.villager_under_attack": [ + "Saldırı altındayım, yardım edin!", + "Saldırı altındayım! Yardım edin!", + "Ahhh! Saldırı altındayım!", + "Ahhh! Saldırı altındayım! Yardım edin!", + "Saldırı altındayım! Bana yardım edin!", + "Yardım edin! Saldırı altındayım!", + "Bana yardım edin! Saldırı altındayım!", + "Yardım edin!", + "Yardım edin! Yardım edin!", + "Yardım edin! Yardım edin! Yardım edin!", + "Saldırı altındayım!", + "AAAHHHH! Saldırı altındayım!", + "AAAHHHH! Yardım edin! Saldırı altındayım!", + "Saldırı altındayız! Yardım edin!", + "Katil! Yardım edin!", + "Bir katil serbestçe dolaşıyor! Yardım edin!", + "Beni öldürmeye çalışıyorlar! Yardım edin!", + "Gardiyanlar, saldırı altındayım!", + "Saldırı altındayım! Gardiyanlar!", + "Gardiyanlar! Saldırı altındayım!", + "Saldırı altındayım! Gardiyanlar! Yardım edin!", + "Gardiyanlar! Çabuk gelin!", + "Gardiyanlar! Gardiyanlar!", + "Bana saldıran bir kötü var! Yardım edin!", + "Gardiyanlar, bu pis kötüyü öldürün!", + "Gardiyanlar! Burada bir katil var!", + "Gardiyanlar! Bana yardım edin!", + "Bu yanına kalmayacak! Gardiyanlar!", + "Seni şeytan!", + "Bana yardım edin!", + "Lütfen! Yardım edin!", + "Ahhh! Gardiyanlar! Yardım edin!", + "Benim için geliyorlar!", + "Yardım edin! Yardım edin! Baskı altındayım!", + "Ah, artık sistemin doğasında var olan şiddeti görüyoruz.", + "Bu bana göre bir çizik bile değil!", + "Yapma şunu!", + "Ben sana ne yaptım ki?!", + "Lütfen bana saldırmayı kes!", + "Hey! Onu nereye yönelttiğine dikkat et!", + "Aşağılık herif, gözüm bile görmesin!", + "Durdur şunu! Git buradan!", + "Şimdi beni kızdırmaya başladın!", + "Hey! Sen kim olduğunu zannediyorsun ki?!", + "Bunun için kelleni alacağım!", + "Yapma, lütfen! Değerli hiçbir şeyim yok bile!", + "Kardeşimi üzerine salacağım, o benden bile büyük!", + "Olamaaaz, Seni anneme söyleyeceğim!", + "Lanet olsun sana!", + "Lütfen yapma şunu.", + "Bunu yapman pek kibarca değildi!", + "Evet silahın çalışıyor, şimdi kaldırabilir misin?", + "Bağışlayın beni!", + "Lütfen, benim bir ailem var!", + "Ölmek için çok gencim!", + "Bunu konuşarak çözebilir miyiz?", + "Şiddet hiçbir zaman çare değildir!", + "Günüm gittikçe kötüleşiyor...", + "Hey, bu acıttı!", + "Eek!", + "Ne kadar da kaba!", + "Dur, sana yalvarırım!", + "Lanet olsun sana!", + "Bu eğlenceli bile değil.", + "Ne cürret?!", + "Bunu sana ödeteceğim!", + "Yapmaya devam edersen bunun için pişman olacaksın!", + "Sana zarar vermek zorunda bırakma beni!", + "Bir yanlış anlaşılma olmalı!", + "Bunu yapmak zorunda değilsin!", + "Defol!", + "Bu gerçekten acıttı!", + "Bunu neden yaptın ki?", + "Ruhlar tarafından, dur!", + "Beni başkasıyla karıştırmış olmalısın!", + "Bunu haketmiyorum!", + "Lütfen bunu bir daha yapma.", + "Muhafızlar, şu canavarı göle atın!", + "Tarrasque'ımı üzerine salarım!", + ], + } +) diff --git a/assets/voxygen/i18n/uk_UA/_manifest.ron b/assets/voxygen/i18n/uk_UA/_manifest.ron index 760c92db7b..bf0d8b8600 100644 --- a/assets/voxygen/i18n/uk_UA/_manifest.ron +++ b/assets/voxygen/i18n/uk_UA/_manifest.ron @@ -28,180 +28,5 @@ asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", scale_ratio: 1.0, ), - }, - - string_map: { - - }, - - vector_map: { - "loading.tips": [ - "Натисніть 'G', щоб засвітити ліхтар.", - "Натисніть 'F1', щоб переглянути стандартні елементи керування.", - "Введіть /say чи /s, щоб написати лише гравцям поряд.", - "Введіть /region чи /r, щоб написати лише гравцям в радіусі кількох сотень блоків навколо.", - "Адміністратори можуть використовувати команду /build для переходу в режим будування", - "Введіть /group чи /g, щоб написати лише гравцям з Вашої групи.", - "Щоб надіслати приватне повідомлення, введіть /tell, ім'я гравця та Ваше повідомлення.", - "Тримайте око на вістрі - їжа, скрині та інші корисні предмети можуть бути де-завгодно!", - "Інвентар переповнений харчами? Спробуйте поєднати їх в кращу їжу!", - "Шукаєте чим би це зайнятись? Провідайте одне з позначених на мапі підземель!", - "Не забудьте налаштувати оптимальну для Вашої системи якість зображення. Натисніть 'N', щоб відкрити налаштування.", - "Грати з іншими весело! Натисніть 'O', щоб переглянути список користувачів в мережі.", - "Натисніть 'J', щоб потанцювати. Гей-Гоп!", - "Натисніть 'L-Shift', щоб дістати Дельтаплан, та підкорюйте небеса!", - "Veloren все ще на стадії ранньої альфи. Ми стараємося робити покращення кожного дня!", - "Якшо Ви хочете долучитись до розробки або ж просто поспілкуватись із нами, приєднуйтесь до нашого Discord-серверу.", - "Ви можете змінити відображення індикатора здоров'я в налаштуваннях.", - "Присядьте біля ватри (натиснувши 'K'), щоб відпочити та відновити здоров'я.", - "Потребуєте більше торбин чи кращу броню? Натисніть 'C' щоб відкрити ремісниче меню.", - ], - "npc.speech.villager": [ - "Ну хіба ж не прекрасний сьогодні день?", - "Як ся маєш?", - "Гарного тобі дня!", - "Цікаво, про що думає Катоблепас, коли їсть траву...", - "Як тобі така погода?", - "Мурашки по спині від одної тільки думки про ті підземелля! Сподіваюсь, хтось з ними розбереться.", - "Коли я посильнішаю, мені б хотілось піти в спелеопохід, досліджувати печери!", - "Ви не бачили мого кота?", - "Ви коли небудь чули про лютих Земних Акул? Кажуть, вони водяться в пустелях.", - "Кажуть, в печерах можна знайти коштовне каміння будь-яких типів.", - "Я просто шаленію від сиру!", - "Не зайдете в гості? Ми якраз збирались поласувати сиру!", - "Кажуть, гриби корисні для здоров'я. Я, правда, ніколи їх не їм.", - "Не забудь сухарики!", - "Обожнюю Дварфський Сир! Хотілось би вміти його робити...", - "Цікаво, що там по іншу сторону гір...", - "Я сподіваюсь виготовити власний дельтаплан одного дня.", - "Ви не хотіли б подивитись на мій сад? Гаразд, можливо іншим разом.", - "Прекрасний день для прогулянки в лісі!", - "Бути чи не бути? Думаю, я буду фермером.", - "Ви не думали про те, що наше селище - найкраще?", - "Як ти думаєш, чому Сяючі Залишки світяться?", - "По-моєму, час другого сніданку!", - "А ви коли небудь ловили світлячка?", - "Я не можу зрозуміти, звідки приходять ці Сауроки.", - "Хотілося б, щоб хтось захищав селище від вовків.", - "Сьогодні мені приснився прекрасний сон про сир. Що б це значило?", - ], - "npc.speech.villager_under_attack": [ - "Допоможіть, на мене напали!", - "Рятуйте! Б'ють!", - "Ай! Больно!", - "Лупцюють!", - "Допоможіть!", - "Рятуйте!", - "Хто-небудь, допоможіть!", - "Будь ласка, допоможіть!", - "АААААА! Рятуйте!", - "Рятуйте! Вбивця!", - "Рятуйте! Мене зараз вб'ють!", - "Охорона, на мене напали!", - "Охорона! На мене напали!", - "Мене атакують! Охорона!", - "Рятуйте! Охорона! На мене напали!", - "Охорона! Швидше!", - "Охорона! Охорона!", - "Охорона! Цей негідник напав на мене!", - "Охорона, розберіться з цим покидьком!", - "Охорона! Тут вбивця!", - "Охорона! Рятуйте!", - "Це тобі так просто не минеться! Охорона!", - "Ах ти ж падлюка!", - "АААААА!", - "Хто-небудь!", - "Ай! Охорона! Рятуйте!", - "Бляха... Вони прийшли за мною!", - "Рятуйте! Допоможіть! Мене катують!", - "Ага, ось вам і властива системі жорстокість.", - "Це лиш царапина!", - "Ану перестань!", - "Благаю, не бий мене!", - "Агов! Дивись куди сунеш цю штуку!", - "Котись під три чорти!", - "Перестань! Відчепись!", - "Я починаю сердитись!", - "Ким ти тут себе уявляєш?!", - "Ти за це поплатишся головою!", - "Благаю, перестань! У мене нічого немає!", - "Я розкажу моєму братові! Він набагато сильніший за мене!", - "Ааа, я розкажу мамі!", - "Пішло геть, опудало!", - "Благаю, перестань!", - "Це було не дуже етично з Вашого боку!", - "Все, все! Ти вмієш битись! Тепер перестань!", - "Пощади мене!", - "Благаю, у мене сім'я!", - "Почекайте, давайте спокійно обговоримо ситуацію?", - "Насилля - це ніколи не рішення!", - "Та що ж це за день такий...", - "Ауч! Це було больно!", - "Ого, яке нахабство!", - "Досить вже!", - "А щоб тобі!", - "Не весело.", - "Як ти смієш?!", - "Ти за це заплатиш!", - "Тільки спробуй! Ти про це пожалкуєш!", - "Не змушуй мене тебе покарати!", - "Це якесь непорозуміння!", - "В цьому немає необхідності!", - "Та відвали вже!", - "Це було дійсно больно!", - "Ай! Чому ти б'єшся?", - "Ви мене з кимось переплутали!", - "Я такого не заслужив!", - "Більше так не роби, будь ласка.", - "Охорона, викиньте цього монстра в озеро!", - "Я натравлю на тебе свою Тарраску!", - ], - "npc.speech.villager_decline_trade": [ - "Нажаль, в мене немає чим торгувати.", - "Торгівля? Ніби в мене є щось що вас цікавить.", - "Моя земля - моя. I я не продам її ні за що.", - ], - "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_enemy_killed": [ - "Помста - це страва, яку кожен подає по-своєму!", - "Нарешті заслужений спокій!", - "... так, на чому я зупинився?", - ] } ) diff --git a/assets/voxygen/i18n/uk_UA/buff.ron b/assets/voxygen/i18n/uk_UA/buff.ron index ce75cce748..ceb08de5bc 100644 --- a/assets/voxygen/i18n/uk_UA/buff.ron +++ b/assets/voxygen/i18n/uk_UA/buff.ron @@ -52,7 +52,7 @@ // Stats "buff.stat.health": "Відновлює {str_total} ОЗ", "buff.stat.increase_max_health": "Підвищує Максимальне Здоров'я на {strength}", - "buff.stat.increase_max_stamina": "Підвищує Максимальну Енергію на {strength}", + "buff.stat.increase_max_energy": "Підвищує Максимальну Енергію на {strength}", "buff.stat.invulnerability": "Дає невразливість", // Text "buff.text.for_seconds": "протягом {dur_secs} сек.", diff --git a/assets/voxygen/i18n/uk_UA/hud/bag.ron b/assets/voxygen/i18n/uk_UA/hud/bag.ron index f8c3ca571a..1a286d1a60 100644 --- a/assets/voxygen/i18n/uk_UA/hud/bag.ron +++ b/assets/voxygen/i18n/uk_UA/hud/bag.ron @@ -39,7 +39,7 @@ // Stats "hud.bag.health": "Здоров'я", - "hud.bag.stamina": "Енергія", + "hud.bag.energy": "Енергія", "hud.bag.combat_rating": "Бойовий рейтинг", "hud.bag.combat_rating_desc": "Розрахований з огляду на ваше\nспорядження і здоров'я", "hud.bag.protection": "Захист", diff --git a/assets/voxygen/i18n/uk_UA/hud/hud_settings.ron b/assets/voxygen/i18n/uk_UA/hud/settings.ron similarity index 98% rename from assets/voxygen/i18n/uk_UA/hud/hud_settings.ron rename to assets/voxygen/i18n/uk_UA/hud/settings.ron index 6189b2c334..ce0e78db63 100644 --- a/assets/voxygen/i18n/uk_UA/hud/hud_settings.ron +++ b/assets/voxygen/i18n/uk_UA/hud/settings.ron @@ -17,7 +17,7 @@ "hud.settings.relative_scaling": "Відносне", "hud.settings.custom_scaling": "Ручне", "hud.settings.crosshair": "Приціл", - "hud.settings.transparency": "Прозорість", + "hud.settings.opacity": "Прозорість", "hud.settings.hotbar": "Панель швидкого доступу", "hud.settings.toggle_shortcuts": "Гарячі клавіші", "hud.settings.buffs_skillbar": "Бафи біля Панелі Швидкого Доступу", @@ -35,7 +35,7 @@ "hud.settings.values": "Значення", "hud.settings.percentages": "Відсотки", "hud.settings.chat": "Чат", - "hud.settings.background_transparency": "Прозорість фону", + "hud.settings.background_opacity": "Прозорість фону", "hud.settings.chat_character_name": "Імена персонажів в чаті", "hud.settings.loading_tips": "Підказки на екрані завантаження", "hud.settings.reset_interface": "Значення за\n замовчуванням", diff --git a/assets/voxygen/i18n/uk_UA/skills.ron b/assets/voxygen/i18n/uk_UA/hud/skills.ron similarity index 97% rename from assets/voxygen/i18n/uk_UA/skills.ron rename to assets/voxygen/i18n/uk_UA/hud/skills.ron index ab26355d0c..3c64c7c95a 100644 --- a/assets/voxygen/i18n/uk_UA/skills.ron +++ b/assets/voxygen/i18n/uk_UA/hud/skills.ron @@ -11,8 +11,8 @@ // General "hud.skill.inc_health_title": "Збільшення здоров'я", "hud.skill.inc_health": "Збільшує максимальне здоров'я на {boost}{SP}", - "hud.skill.inc_stam_title": "Збільшення енергії", - "hud.skill.inc_stam": "Збільшує максимальну енергію на {boost}{SP}", + "hud.skill.inc_energy_title": "Збільшення енергії", + "hud.skill.inc_energy": "Збільшує максимальну енергію на {boost}{SP}", "hud.skill.unlck_sword_title": "Меч", "hud.skill.unlck_sword": "Відкриває дерево навичок володіння мечем{SP}", "hud.skill.unlck_axe_title": "Сокира", @@ -27,8 +27,8 @@ "hud.skill.unlck_sceptre": "Відкриває дерево навичок володіння цілющим скіпетром{SP}", "hud.skill.dodge_title": "Ухил", "hud.skill.dodge": "Станьте невразливим до ближніх атак під час перекиду{SP}", - "hud.skill.roll_stamina_title": "Енергоспоживання перекиду", - "hud.skill.roll_stamina": "Перекид споживає на {boost}% менше енергії{SP}", + "hud.skill.roll_energy_title": "Енергоспоживання перекиду", + "hud.skill.roll_energy": "Перекид споживає на {boost}% менше енергії{SP}", "hud.skill.roll_speed_title": "Швидкість перекиду", "hud.skill.roll_speed": "Перекид виконується на {boost}% швидше{SP}", "hud.skill.roll_dur_title": "Тривалість перекиду", @@ -116,8 +116,8 @@ "hud.skill.st_fireball_title": "Вогнений м'яч", "hud.skill.st_explosion_radius_title" : "Радіус вибуху", "hud.skill.st_explosion_radius" : "Більше - краще, збільшує радіус вибуху снарядів на {boost}%{SP}", - "hud.skill.st_stamina_regen_title" : "Відновлення енергії", - "hud.skill.st_stamina_regen" : "Збільшує відновлення енергії від заподіяних ушкоджень на {boost}%{SP}", + "hud.skill.st_energy_regen_title" : "Відновлення енергії", + "hud.skill.st_energy_regen" : "Збільшує відновлення енергії від заподіяних ушкоджень на {boost}%{SP}", "hud.skill.st_damage_title" : "Ушкодження снарядами", "hud.skill.st_damage" : "Збільшує ушкодження вогняними снарядами на {boost}%{SP}", // Bow diff --git a/assets/voxygen/i18n/uk_UA/main.ron b/assets/voxygen/i18n/uk_UA/main.ron index 8d136536ae..bae23b7623 100644 --- a/assets/voxygen/i18n/uk_UA/main.ron +++ b/assets/voxygen/i18n/uk_UA/main.ron @@ -65,5 +65,26 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "Натисніть 'G', щоб засвітити ліхтар.", + "Натисніть 'F1', щоб переглянути стандартні елементи керування.", + "Введіть /say чи /s, щоб написати лише гравцям поряд.", + "Введіть /region чи /r, щоб написати лише гравцям в радіусі кількох сотень блоків навколо.", + "Адміністратори можуть використовувати команду /build для переходу в режим будування", + "Введіть /group чи /g, щоб написати лише гравцям з Вашої групи.", + "Щоб надіслати приватне повідомлення, введіть /tell, ім'я гравця та Ваше повідомлення.", + "Тримайте око на вістрі - їжа, скрині та інші корисні предмети можуть бути де-завгодно!", + "Інвентар переповнений харчами? Спробуйте поєднати їх в кращу їжу!", + "Шукаєте чим би це зайнятись? Провідайте одне з позначених на мапі підземель!", + "Не забудьте налаштувати оптимальну для Вашої системи якість зображення. Натисніть 'N', щоб відкрити налаштування.", + "Грати з іншими весело! Натисніть 'O', щоб переглянути список користувачів в мережі.", + "Натисніть 'J', щоб потанцювати. Гей-Гоп!", + "Натисніть 'L-Shift', щоб дістати Дельтаплан, та підкорюйте небеса!", + "Veloren все ще на стадії ранньої альфи. Ми стараємося робити покращення кожного дня!", + "Якшо Ви хочете долучитись до розробки або ж просто поспілкуватись із нами, приєднуйтесь до нашого Discord-серверу.", + "Ви можете змінити відображення індикатора здоров'я в налаштуваннях.", + "Присядьте біля ватри (натиснувши 'K'), щоб відпочити та відновити здоров'я.", + "Потребуєте більше торбин чи кращу броню? Натисніть 'C' щоб відкрити ремісниче меню.", + ], } ) diff --git a/assets/voxygen/i18n/uk_UA/npc.ron b/assets/voxygen/i18n/uk_UA/npc.ron new file mode 100644 index 0000000000..2704bddf28 --- /dev/null +++ b/assets/voxygen/i18n/uk_UA/npc.ron @@ -0,0 +1,158 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Ukrainian +( + string_map: { + + }, + + vector_map: { + "npc.speech.villager": [ + "Ну хіба ж не прекрасний сьогодні день?", + "Як ся маєш?", + "Гарного тобі дня!", + "Цікаво, про що думає Катоблепас, коли їсть траву...", + "Як тобі така погода?", + "Мурашки по спині від одної тільки думки про ті підземелля! Сподіваюсь, хтось з ними розбереться.", + "Коли я посильнішаю, мені б хотілось піти в спелеопохід, досліджувати печери!", + "Ви не бачили мого кота?", + "Ви коли небудь чули про лютих Земних Акул? Кажуть, вони водяться в пустелях.", + "Кажуть, в печерах можна знайти коштовне каміння будь-яких типів.", + "Я просто шаленію від сиру!", + "Не зайдете в гості? Ми якраз збирались поласувати сиру!", + "Кажуть, гриби корисні для здоров'я. Я, правда, ніколи їх не їм.", + "Не забудь сухарики!", + "Обожнюю Дварфський Сир! Хотілось би вміти його робити...", + "Цікаво, що там по іншу сторону гір...", + "Я сподіваюсь виготовити власний дельтаплан одного дня.", + "Ви не хотіли б подивитись на мій сад? Гаразд, можливо іншим разом.", + "Прекрасний день для прогулянки в лісі!", + "Бути чи не бути? Думаю, я буду фермером.", + "Ви не думали про те, що наше селище - найкраще?", + "Як ти думаєш, чому Сяючі Залишки світяться?", + "По-моєму, час другого сніданку!", + "А ви коли небудь ловили світлячка?", + "Я не можу зрозуміти, звідки приходять ці Сауроки.", + "Хотілося б, щоб хтось захищав селище від вовків.", + "Сьогодні мені приснився прекрасний сон про сир. Що б це значило?", + ], + "npc.speech.villager_under_attack": [ + "Допоможіть, на мене напали!", + "Рятуйте! Б'ють!", + "Ай! Больно!", + "Лупцюють!", + "Допоможіть!", + "Рятуйте!", + "Хто-небудь, допоможіть!", + "Будь ласка, допоможіть!", + "АААААА! Рятуйте!", + "Рятуйте! Вбивця!", + "Рятуйте! Мене зараз вб'ють!", + "Охорона, на мене напали!", + "Охорона! На мене напали!", + "Мене атакують! Охорона!", + "Рятуйте! Охорона! На мене напали!", + "Охорона! Швидше!", + "Охорона! Охорона!", + "Охорона! Цей негідник напав на мене!", + "Охорона, розберіться з цим покидьком!", + "Охорона! Тут вбивця!", + "Охорона! Рятуйте!", + "Це тобі так просто не минеться! Охорона!", + "Ах ти ж падлюка!", + "АААААА!", + "Хто-небудь!", + "Ай! Охорона! Рятуйте!", + "Бляха... Вони прийшли за мною!", + "Рятуйте! Допоможіть! Мене катують!", + "Ага, ось вам і властива системі жорстокість.", + "Це лиш царапина!", + "Ану перестань!", + "Благаю, не бий мене!", + "Агов! Дивись куди сунеш цю штуку!", + "Котись під три чорти!", + "Перестань! Відчепись!", + "Я починаю сердитись!", + "Ким ти тут себе уявляєш?!", + "Ти за це поплатишся головою!", + "Благаю, перестань! У мене нічого немає!", + "Я розкажу моєму братові! Він набагато сильніший за мене!", + "Ааа, я розкажу мамі!", + "Пішло геть, опудало!", + "Благаю, перестань!", + "Це було не дуже етично з Вашого боку!", + "Все, все! Ти вмієш битись! Тепер перестань!", + "Пощади мене!", + "Благаю, у мене сім'я!", + "Почекайте, давайте спокійно обговоримо ситуацію?", + "Насилля - це ніколи не рішення!", + "Та що ж це за день такий...", + "Ауч! Це було больно!", + "Ого, яке нахабство!", + "Досить вже!", + "А щоб тобі!", + "Не весело.", + "Як ти смієш?!", + "Ти за це заплатиш!", + "Тільки спробуй! Ти про це пожалкуєш!", + "Не змушуй мене тебе покарати!", + "Це якесь непорозуміння!", + "В цьому немає необхідності!", + "Та відвали вже!", + "Це було дійсно больно!", + "Ай! Чому ти б'єшся?", + "Ви мене з кимось переплутали!", + "Я такого не заслужив!", + "Більше так не роби, будь ласка.", + "Охорона, викиньте цього монстра в озеро!", + "Я натравлю на тебе свою Тарраску!", + ], + "npc.speech.villager_decline_trade": [ + "Нажаль, в мене немає чим торгувати.", + "Торгівля? Ніби в мене є щось що вас цікавить.", + "Моя земля - моя. I я не продам її ні за що.", + ], + "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_enemy_killed": [ + "Помста - це страва, яку кожен подає по-своєму!", + "Нарешті заслужений спокій!", + "... так, на чому я зупинився?", + ] + } +) diff --git a/assets/voxygen/i18n/vi_VI/_manifest.ron b/assets/voxygen/i18n/vi_VI/_manifest.ron index b5ee1a57d1..a119637120 100644 --- a/assets/voxygen/i18n/vi_VI/_manifest.ron +++ b/assets/voxygen/i18n/vi_VI/_manifest.ron @@ -28,236 +28,5 @@ asset_key: "voxygen.font.OpenSans-Regular", scale_ratio: 1.0, ), - }, - - string_map: { - /// Start Common section - // Texts used in multiple locations with the same formatting - "common.username": "Tên tài khoản", - "common.singleplayer": "Chơi đơn", - "common.multiplayer": "Chơi mạng", - "common.servers": "Máy chủ", - "common.quit": "Bỏ cuộc", - "common.settings": "Cài đặt", - "common.languages": "Ngôn ngữ", - "common.interface": "Giao diện", - "common.gameplay": "Lối chơi", - "common.controls": "Điều khiển", - "common.video": "Đồ họa", - "common.sound": "Âm thanh", - "common.resume": "Tiếp tục", - "common.characters": "Nhân vật", - "common.close": "Đóng", - "common.yes": "Đúng", - "common.no": "Không", - "common.back": "Trở lại", - "common.create": "Tạo nên", - "common.okay": "Được", - "common.add": "Thêm vào", - "common.accept": "Chấp nhận", - "common.decline": "Từ chối", - "common.disclaimer": "Khước từ", - "common.cancel": "Hủy bỏ", - "common.none": "Không còn", - "common.error": "Lỗi", - "common.fatal_error": "Lỗi nghiêm trọng", - "common.you": "Bạn", - "common.automatic": "Tự động", - "common.random": "Ngẫu nhiên", - - // Settings Window title - "common.interface_settings": "Cài đặt giao diện", - "common.gameplay_settings": "Cài đặt chế độ chơi", - "common.controls_settings": "Cài đặt điều khiển", - "common.video_settings": "Cài đặt hình ảnh", - "common.sound_settings": "Cài đặt âm thanh", - "common.language_settings": "Cài đặt ngôn ngữ", - - // Message when connection to the server is lost - "common.connection_lost": r#"Mất mạng! -Máy chủ có khởi động lại không? -Trò chơi có được cập nhật không?"#, - - - "common.species.orc": "Loài orc", - "common.species.human": "Loài người", - "common.species.dwarf": "Quỷ lùn", - "common.species.elf": "Yêu tinh", - "common.species.undead": "Xác sống", - "common.species.danari": "Loài Danari", - - "common.weapons.axe": "Rìu", - "common.weapons.sword": "Kiếm", - "common.weapons.staff": "Ba toong", - "common.weapons.bow": "Cung", - "common.weapons.hammer": "Búa", - "common.weapons.general": "Chiến đấu chung", - "common.weapons.sceptre": "Quyền trượng chữa bệnh", - "common.rand_appearance": "Sự xuất hiện ngẫu nhiên và tên", - /// End Common section - - /// Start Main screen section - "main.username": "Tên tài khoản", - "main.server": "Máy chủ", - "main.password": "Mật khẩu", - "main.connecting": "Đang kết nối", - "main.creating_world": "Đang tạo thế giới mới", - "main.tip": "Lời khuyên:", - - // Welcome notice that appears the first time Veloren is started - "main.notice": r#"Chào mừng đến với phiên bản alpha của Veloren! - -Trước khi bạn nhảy trong vào Veloren, bạn phải nhớ những điều này: - -- Đây là một alpha rất sớm. Mong đợi lỗi chương trình, trò chơi cực kỳ chưa hoàn thành, cơ khí chưa hoàn thành, và tính năng bị thiếu. - -- Nếu bạn có phản hồi hoặc là báo cáo lỗi máy tính, liên hệ chúng tôi trên Reddit, GitLab, hoặc là máy chủ Discord của chúng tôi. - -- Veloren được cấp phép theo GPL 3 open-source licence. Đó có nghĩa là bạn được phép chơi miễn phí, sửa đổi, và phân phối lại trò chơi theo cách bạn muốn (miễn là công việc bắt nguồn cũng cấp phép theo GPL 3). - -- Veloren là một dự án cộng đồng phi lợi nhuận, và mọi người đang làm việc đó là một tình nguyện viên. Nếu bạn thích những gì bạn đang thấy, Chúng tôi mời bạn vô đội phát triển hoặc nghệ thuật! - -Cảm ơn bạn đã dành thời gian đọc thông báo này, chúng tôi hy vọng bạn thích trò chơi này! - -~ The Veloren Devs"#, - - // Login process description - "main.login_process": r#"Thông tin về quá trình đăng nhập: - -Xin lưu ý rằng bây giờ bạn cần một tài khoản -để chơi trên máy chủ xác thực được kích hoạt. - -Bạn có thể tạo một tài khoản ở - - -https://veloren.net/account/."#, - "main.login.server_not_found": "Không tìm thấy được may chủ", - "main.login.authentication_error": "Lỗi xác thực trên máy chủ", - "main.login.server_full": "Máy chủ đầy rồi", - "main.login.untrusted_auth_server": "máy chủ xác thực không đáng tin cậy", - "main.login.outdated_client_or_server": "Máy chủ nổi khùng: - Có thể các phiên bản không tương thích, kiểm tra xem có cập nhật không.", - "main.login.timeout": "Hết giờ: máy chủ không phản hồi kịp thời. (quá tải hoặc lỗi mạng).", - "main.login.server_shut_down": "Máy chủ ngừng hoạt động", - "main.login.already_logged_in": "Bạn đã đăng nhập vào máy chủ rồi.", - "main.login.network_error": "lỗi mạng", - "main.login.failed_sending_request": "yêu cầu máy chủ ủy quyền không thành công", - "main.login.invalid_character": "Ký tự đã chọn không hợp lệ", - "main.login.client_crashed": "Trò chơi bị rơi", - "main.login.not_on_whitelist": "Bạn cần được quản trị viên chấp thuận để tham gia", - "main.login.banned": "Bạn đã bị cấm tại vì", - "main.login.kicked": "Bạn đã bị đá bởi vì", - "main.login.select_language": "Chọn một ngôn ngữ", - - "main.servers.select_server": "Chọn một máy chủ", - /// End Main screen section - }, - - vector_map: { - "loading.tips": [ - "Nhấn 'G' bật đèn lồng của bạn.", - "Nhấn 'F1' để xem tất cả các liên kết phím mặc định.", - "Bạn có thể gõ /say hoặc là /s để nói chuyện xung quanh bạn.", - "Bạn có thể gõ /region hoặc là /r nói chuyện với người chơi vài trăm blocks xung quanh bạn.", - "Quản trị viên có thể sử dụng /build để vào chế độ xây dựng.", - "Bạn có thể gõ /group hoặc là /g để nói chuyện với những người chơi trong nhóm hiện tại của bạnto.", - "Để gửi tin nhắn riêng tư gõ /tell theo sau là tên người chơi và tin nhắn của bạn.", - "NPCs với cùng một mức độ có thể có một khó khăn khác nhau.", - "Coi chừng thức ăn, rương và các chiến lợi phẩm khác lan rộng khắp thế giới!", - "Thực phẩm đầy tồn kho? Hãy thử chế biến những món ăn ngon hơn từ nó!", - "Tự hỏi những gì ở đó để làm? Thử một trong những ngục tối được đánh dấu trên bản đồ!", - "Đừng quên điều chỉnh đồ họa cho hệ thống của bạn. Nhấn 'N' để mở cài đặt.", - "Chơi với những người khác rất vui! Nhấn 'O' để xem ai cũng đáng chơi hiện tại bây giờ.", - "Kẻ thù có đầu lâu bên dưới thanh máu của chúng mạnh mẽ hơn so với chính bạn.", - "Nhấn 'J' để nhảy múa. Buổi tiệc!", - "Nhấn 'L-Shift' để mở tàu lượn của bạn và chinh phục bầu trời.", - "Veloren vẫn đang trong giai đoạn Pre-Alpha. Chúng tôi cố gắng hết sức để cải thiện nó mỗi ngày!", - "Nếu bạn muốn tham gia nhóm nhà phát triển hoặc chỉ cần trò chuyện với chúng tôi, hãy tham gia Discord-Server.", - "Bạn có thể thay đổi hiển thị lượng máu của mình trên thanh sức khỏe trong cài đặt.", - "Để xem số liệu thống kê của bạn, hãy nhấn vào nút 'Stats' trong kho.", - "Ngồi gần lửa trại (nhấn nút 'K') để nghỉ ngơi - nhận được một sự chữa lành chậm theo thời gian.", - "Cần thêm túi hoặc áo giáp tốt hơn để tiếp tục cuộc hành trình của bạny? Nhấn 'C' để mở menu chế tạo!", - ], - "npc.speech.villager_under_attack": [ - "Giúp, mình đang bị tấn công!", - "Giúp! Mình đang bị tấn công!", - "Đau! Mình đang bị tấn công!", - "Đau! Mình đang bị tấn công! Giúp!", - "Giúp tôi! Mình đang bị tấn công!", - "Mình đang bị tấn công! Giúp!", - "Mình đang bị tấn công! Giúp tôi!", - "Giúp!", - "Giúp! Giúp!", - "Giúp! Giúp! Giúp!", - "Mình đang bị tấn công!", - "AAAHHH! Mình đang bị tấn công!", - "AAAHHH! Mình đang bị tấn công! Giúp!", - "Giúp! Chúng ta đang bị tấn công!", - "Giúp! Kẻ giết người!", - "Giúp! Có một kẻ giết người đang thả rông!", - "Giúp! Họ ráng giết tôi!", - "Người đâu, Mình đang bị tấn công!", - "Người đâu! Mình đang bị tấn công!", - "Mình đang bị tấn công! Lính canh!", - "Giúp! Lính canh! Mình đang bị tấn công!", - "Lính canh! Đến nhanh!", - "Lính canh! Lính canh!", - "Lính canh! Có một nhân vật phản diện đang tấn công tôi!", - "Lính canh, Giết kẻ xấu xa này!", - "Lính canh! Có một kẻ giết người!", - "Lính canh! Giúp tôi!", - "Mày sẽ không thoát khỏi điều này! Lính canh!", - "Mày khốn nạn!", - "Cứu tôi!", - "Giúp! Làm ơn!", - "Đau! Lính canh! Giúp!", - "Họ đang tìm kiếm tôi!", - "Giúp! Giúp! Tôi đang bị kìm nén!", - "Ah, bây giờ chúng ta thấy bạo lực vốn có trong hệ thống", - "Đây chỉ là một vết xước", - "Dừng lại!", - "Tôi đã làm gì cho bạn?!", - "Làm ơn đừng tấn công tôi nữa!", - "Chời ơi! Bạn phải để ý xem bạn đang chỉ cái đó ở đâu", - "Kẻ khốn nạn ghê tởm, biến mất chỗ khác!", - "Dừng lại! Đi chỗ khác!", - "Mày đang chọc tao điên!", - "Mày! Mày nghĩ bạn là ai?!", - "Tôi sẽ có đầu của mày cho điều đó!", - "Xin dừng lại! Tôi không mang gì co giá trị!", - "Tôi sẽ kể anh trai tôi. Anh to hơn bạn!", - "Khôngggg, tôi đi méc mẹ!", - "Ba má không biết dạy!", - "Làm ơn đừng làm vậy.", - "Điều đó không tốt lắm!", - "Vũ khí của bạn hoạt động, bạn có thể cất nó đi ngay bây giờ!", - "Tha cho tôi!", - "Làm ơn, tôi có một gia đình!", - "Tôi còn quá trẻ để chết!", - "Chúng ta có thể nói về điều này không?", - "Bạo lực không bao giờ là câu trả lời!", - "Hôm nay hóa ra là một ngày rất tồi tệ...", - "Chời ơi, đau đấy!", - "Eek!", - "Thật là thô lỗ!", - "Dừng lại, tôi cầu xin bạn!", - "Tôi ước bạn bệnh!", - "Điều này không vui.", - "Mày dám?!", - "Mày sẽ trả cho điều đó!", - "Hãy tiếp tục và bạn sẽ hối hận!", - "Đừng bắt tôi đánh bạn!", - "Phải có một số hiểu lầm!", - "Bạn không cần phải làm điều này!", - "Biến đi, tên khốn!", - "Đau quá!", - "Tại sao bạn lại làm vậy?", - "Chời ơi, dừng lại!", - "Bạn phải làm cho tôi nhầm lẫn với một người khác!", - "Tôi không xứng đáng điều này!", - "Xin đừng làm điều đó một lần nữa.", - "Các vệ binh, ném con quái vật này xuống hồ!", - "Tôi sẽ gọi con tarrasque của tôi để tấn công bạn!", - ], } ) diff --git a/assets/voxygen/i18n/vi_VI/buff.ron b/assets/voxygen/i18n/vi_VI/buff.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/buff.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/char_selection.ron b/assets/voxygen/i18n/vi_VI/char_selection.ron new file mode 100644 index 0000000000..ebc3ba6d98 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/char_selection.ron @@ -0,0 +1,10 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/common.ron b/assets/voxygen/i18n/vi_VI/common.ron new file mode 100644 index 0000000000..0081d6f554 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/common.ron @@ -0,0 +1,73 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + // Texts used in multiple locations with the same formatting + "common.username": "Tên tài khoản", + "common.singleplayer": "Chơi đơn", + "common.multiplayer": "Chơi mạng", + "common.servers": "Máy chủ", + "common.quit": "Bỏ cuộc", + "common.settings": "Cài đặt", + "common.languages": "Ngôn ngữ", + "common.interface": "Giao diện", + "common.gameplay": "Lối chơi", + "common.controls": "Điều khiển", + "common.video": "Đồ họa", + "common.sound": "Âm thanh", + "common.resume": "Tiếp tục", + "common.characters": "Nhân vật", + "common.close": "Đóng", + "common.yes": "Đúng", + "common.no": "Không", + "common.back": "Trở lại", + "common.create": "Tạo nên", + "common.okay": "Được", + "common.add": "Thêm vào", + "common.accept": "Chấp nhận", + "common.decline": "Từ chối", + "common.disclaimer": "Khước từ", + "common.cancel": "Hủy bỏ", + "common.none": "Không còn", + "common.error": "Lỗi", + "common.fatal_error": "Lỗi nghiêm trọng", + "common.you": "Bạn", + "common.automatic": "Tự động", + "common.random": "Ngẫu nhiên", + + // Settings Window title + "common.interface_settings": "Cài đặt giao diện", + "common.gameplay_settings": "Cài đặt chế độ chơi", + "common.controls_settings": "Cài đặt điều khiển", + "common.video_settings": "Cài đặt hình ảnh", + "common.sound_settings": "Cài đặt âm thanh", + "common.language_settings": "Cài đặt ngôn ngữ", + + // Message when connection to the server is lost + "common.connection_lost": r#"Mất mạng! +Máy chủ có khởi động lại không? +Trò chơi có được cập nhật không?"#, + + + "common.species.orc": "Loài orc", + "common.species.human": "Loài người", + "common.species.dwarf": "Quỷ lùn", + "common.species.elf": "Yêu tinh", + "common.species.undead": "Xác sống", + "common.species.danari": "Loài Danari", + + "common.weapons.axe": "Rìu", + "common.weapons.sword": "Kiếm", + "common.weapons.staff": "Ba toong", + "common.weapons.bow": "Cung", + "common.weapons.hammer": "Búa", + "common.weapons.general": "Chiến đấu chung", + "common.weapons.sceptre": "Quyền trượng chữa bệnh", + "common.rand_appearance": "Sự xuất hiện ngẫu nhiên và tên", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/esc_menu.ron b/assets/voxygen/i18n/vi_VI/esc_menu.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/esc_menu.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/gameinput.ron b/assets/voxygen/i18n/vi_VI/gameinput.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/gameinput.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/hud/bag.ron b/assets/voxygen/i18n/vi_VI/hud/bag.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/bag.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/hud/char_window.ron b/assets/voxygen/i18n/vi_VI/hud/char_window.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/char_window.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/hud/chat.ron b/assets/voxygen/i18n/vi_VI/hud/chat.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/chat.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/hud/crafting.ron b/assets/voxygen/i18n/vi_VI/hud/crafting.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/crafting.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/hud/group.ron b/assets/voxygen/i18n/vi_VI/hud/group.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/group.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/hud/map.ron b/assets/voxygen/i18n/vi_VI/hud/map.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/map.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/hud/misc.ron b/assets/voxygen/i18n/vi_VI/hud/misc.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/misc.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/hud/sct.ron b/assets/voxygen/i18n/vi_VI/hud/sct.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/sct.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/hud/settings.ron b/assets/voxygen/i18n/vi_VI/hud/settings.ron new file mode 100644 index 0000000000..92d3139b83 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/settings.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/hud/skills.ron b/assets/voxygen/i18n/vi_VI/hud/skills.ron new file mode 100644 index 0000000000..81584383c1 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/skills.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/vi_VI/hud/social.ron b/assets/voxygen/i18n/vi_VI/hud/social.ron new file mode 100644 index 0000000000..71161e0b3a --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/social.ron @@ -0,0 +1,12 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/vi_VI/hud/trade.ron b/assets/voxygen/i18n/vi_VI/hud/trade.ron new file mode 100644 index 0000000000..71161e0b3a --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/hud/trade.ron @@ -0,0 +1,12 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/vi_VI/main.ron b/assets/voxygen/i18n/vi_VI/main.ron new file mode 100644 index 0000000000..d79bcac846 --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/main.ron @@ -0,0 +1,88 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + "main.username": "Tên tài khoản", + "main.server": "Máy chủ", + "main.password": "Mật khẩu", + "main.connecting": "Đang kết nối", + "main.creating_world": "Đang tạo thế giới mới", + "main.tip": "Lời khuyên:", + + // Welcome notice that appears the first time Veloren is started + "main.notice": r#"Chào mừng đến với phiên bản alpha của Veloren! + +Trước khi bạn nhảy trong vào Veloren, bạn phải nhớ những điều này: + +- Đây là một alpha rất sớm. Mong đợi lỗi chương trình, trò chơi cực kỳ chưa hoàn thành, cơ khí chưa hoàn thành, và tính năng bị thiếu. + +- Nếu bạn có phản hồi hoặc là báo cáo lỗi máy tính, liên hệ chúng tôi trên Reddit, GitLab, hoặc là máy chủ Discord của chúng tôi. + +- Veloren được cấp phép theo GPL 3 open-source licence. Đó có nghĩa là bạn được phép chơi miễn phí, sửa đổi, và phân phối lại trò chơi theo cách bạn muốn (miễn là công việc bắt nguồn cũng cấp phép theo GPL 3). + +- Veloren là một dự án cộng đồng phi lợi nhuận, và mọi người đang làm việc đó là một tình nguyện viên. Nếu bạn thích những gì bạn đang thấy, Chúng tôi mời bạn vô đội phát triển hoặc nghệ thuật! + +Cảm ơn bạn đã dành thời gian đọc thông báo này, chúng tôi hy vọng bạn thích trò chơi này! + +~ The Veloren Devs"#, + + // Login process description + "main.login_process": r#"Thông tin về quá trình đăng nhập: + +Xin lưu ý rằng bây giờ bạn cần một tài khoản +để chơi trên máy chủ xác thực được kích hoạt. + +Bạn có thể tạo một tài khoản ở + + +https://veloren.net/account/."#, + "main.login.server_not_found": "Không tìm thấy được may chủ", + "main.login.authentication_error": "Lỗi xác thực trên máy chủ", + "main.login.server_full": "Máy chủ đầy rồi", + "main.login.untrusted_auth_server": "máy chủ xác thực không đáng tin cậy", + "main.login.outdated_client_or_server": "Máy chủ nổi khùng: + Có thể các phiên bản không tương thích, kiểm tra xem có cập nhật không.", + "main.login.timeout": "Hết giờ: máy chủ không phản hồi kịp thời. (quá tải hoặc lỗi mạng).", + "main.login.server_shut_down": "Máy chủ ngừng hoạt động", + "main.login.already_logged_in": "Bạn đã đăng nhập vào máy chủ rồi.", + "main.login.network_error": "lỗi mạng", + "main.login.failed_sending_request": "yêu cầu máy chủ ủy quyền không thành công", + "main.login.invalid_character": "Ký tự đã chọn không hợp lệ", + "main.login.client_crashed": "Trò chơi bị rơi", + "main.login.not_on_whitelist": "Bạn cần được quản trị viên chấp thuận để tham gia", + "main.login.banned": "Bạn đã bị cấm tại vì", + "main.login.kicked": "Bạn đã bị đá bởi vì", + "main.login.select_language": "Chọn một ngôn ngữ", + + "main.servers.select_server": "Chọn một máy chủ", + }, + + + vector_map: { + "loading.tips": [ + "Nhấn 'G' bật đèn lồng của bạn.", + "Nhấn 'F1' để xem tất cả các liên kết phím mặc định.", + "Bạn có thể gõ /say hoặc là /s để nói chuyện xung quanh bạn.", + "Bạn có thể gõ /region hoặc là /r nói chuyện với người chơi vài trăm blocks xung quanh bạn.", + "Quản trị viên có thể sử dụng /build để vào chế độ xây dựng.", + "Bạn có thể gõ /group hoặc là /g để nói chuyện với những người chơi trong nhóm hiện tại của bạnto.", + "Để gửi tin nhắn riêng tư gõ /tell theo sau là tên người chơi và tin nhắn của bạn.", + "NPCs với cùng một mức độ có thể có một khó khăn khác nhau.", + "Coi chừng thức ăn, rương và các chiến lợi phẩm khác lan rộng khắp thế giới!", + "Thực phẩm đầy tồn kho? Hãy thử chế biến những món ăn ngon hơn từ nó!", + "Tự hỏi những gì ở đó để làm? Thử một trong những ngục tối được đánh dấu trên bản đồ!", + "Đừng quên điều chỉnh đồ họa cho hệ thống của bạn. Nhấn 'N' để mở cài đặt.", + "Chơi với những người khác rất vui! Nhấn 'O' để xem ai cũng đáng chơi hiện tại bây giờ.", + "Kẻ thù có đầu lâu bên dưới thanh máu của chúng mạnh mẽ hơn so với chính bạn.", + "Nhấn 'J' để nhảy múa. Buổi tiệc!", + "Nhấn 'L-Shift' để mở tàu lượn của bạn và chinh phục bầu trời.", + "Veloren vẫn đang trong giai đoạn Pre-Alpha. Chúng tôi cố gắng hết sức để cải thiện nó mỗi ngày!", + "Nếu bạn muốn tham gia nhóm nhà phát triển hoặc chỉ cần trò chuyện với chúng tôi, hãy tham gia Discord-Server.", + "Bạn có thể thay đổi hiển thị lượng máu của mình trên thanh sức khỏe trong cài đặt.", + "Để xem số liệu thống kê của bạn, hãy nhấn vào nút 'Stats' trong kho.", + "Ngồi gần lửa trại (nhấn nút 'K') để nghỉ ngơi - nhận được một sự chữa lành chậm theo thời gian.", + "Cần thêm túi hoặc áo giáp tốt hơn để tiếp tục cuộc hành trình của bạny? Nhấn 'C' để mở menu chế tạo!", + ], + } +) diff --git a/assets/voxygen/i18n/vi_VI/npc.ron b/assets/voxygen/i18n/vi_VI/npc.ron new file mode 100644 index 0000000000..e251b2767a --- /dev/null +++ b/assets/voxygen/i18n/vi_VI/npc.ron @@ -0,0 +1,92 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Vietnamese (Vietnam) +( + string_map: { + + }, + + vector_map: { + "npc.speech.villager_under_attack": [ + "Giúp, mình đang bị tấn công!", + "Giúp! Mình đang bị tấn công!", + "Đau! Mình đang bị tấn công!", + "Đau! Mình đang bị tấn công! Giúp!", + "Giúp tôi! Mình đang bị tấn công!", + "Mình đang bị tấn công! Giúp!", + "Mình đang bị tấn công! Giúp tôi!", + "Giúp!", + "Giúp! Giúp!", + "Giúp! Giúp! Giúp!", + "Mình đang bị tấn công!", + "AAAHHH! Mình đang bị tấn công!", + "AAAHHH! Mình đang bị tấn công! Giúp!", + "Giúp! Chúng ta đang bị tấn công!", + "Giúp! Kẻ giết người!", + "Giúp! Có một kẻ giết người đang thả rông!", + "Giúp! Họ ráng giết tôi!", + "Người đâu, Mình đang bị tấn công!", + "Người đâu! Mình đang bị tấn công!", + "Mình đang bị tấn công! Lính canh!", + "Giúp! Lính canh! Mình đang bị tấn công!", + "Lính canh! Đến nhanh!", + "Lính canh! Lính canh!", + "Lính canh! Có một nhân vật phản diện đang tấn công tôi!", + "Lính canh, Giết kẻ xấu xa này!", + "Lính canh! Có một kẻ giết người!", + "Lính canh! Giúp tôi!", + "Mày sẽ không thoát khỏi điều này! Lính canh!", + "Mày khốn nạn!", + "Cứu tôi!", + "Giúp! Làm ơn!", + "Đau! Lính canh! Giúp!", + "Họ đang tìm kiếm tôi!", + "Giúp! Giúp! Tôi đang bị kìm nén!", + "Ah, bây giờ chúng ta thấy bạo lực vốn có trong hệ thống", + "Đây chỉ là một vết xước", + "Dừng lại!", + "Tôi đã làm gì cho bạn?!", + "Làm ơn đừng tấn công tôi nữa!", + "Chời ơi! Bạn phải để ý xem bạn đang chỉ cái đó ở đâu", + "Kẻ khốn nạn ghê tởm, biến mất chỗ khác!", + "Dừng lại! Đi chỗ khác!", + "Mày đang chọc tao điên!", + "Mày! Mày nghĩ bạn là ai?!", + "Tôi sẽ có đầu của mày cho điều đó!", + "Xin dừng lại! Tôi không mang gì co giá trị!", + "Tôi sẽ kể anh trai tôi. Anh to hơn bạn!", + "Khôngggg, tôi đi méc mẹ!", + "Ba má không biết dạy!", + "Làm ơn đừng làm vậy.", + "Điều đó không tốt lắm!", + "Vũ khí của bạn hoạt động, bạn có thể cất nó đi ngay bây giờ!", + "Tha cho tôi!", + "Làm ơn, tôi có một gia đình!", + "Tôi còn quá trẻ để chết!", + "Chúng ta có thể nói về điều này không?", + "Bạo lực không bao giờ là câu trả lời!", + "Hôm nay hóa ra là một ngày rất tồi tệ...", + "Chời ơi, đau đấy!", + "Eek!", + "Thật là thô lỗ!", + "Dừng lại, tôi cầu xin bạn!", + "Tôi ước bạn bệnh!", + "Điều này không vui.", + "Mày dám?!", + "Mày sẽ trả cho điều đó!", + "Hãy tiếp tục và bạn sẽ hối hận!", + "Đừng bắt tôi đánh bạn!", + "Phải có một số hiểu lầm!", + "Bạn không cần phải làm điều này!", + "Biến đi, tên khốn!", + "Đau quá!", + "Tại sao bạn lại làm vậy?", + "Chời ơi, dừng lại!", + "Bạn phải làm cho tôi nhầm lẫn với một người khác!", + "Tôi không xứng đáng điều này!", + "Xin đừng làm điều đó một lần nữa.", + "Các vệ binh, ném con quái vật này xuống hồ!", + "Tôi sẽ gọi con tarrasque của tôi để tấn công bạn!", + ], + } +) diff --git a/assets/voxygen/i18n/zh_CN/_manifest.ron b/assets/voxygen/i18n/zh_CN/_manifest.ron index f6fa399ab6..19bdca60e0 100644 --- a/assets/voxygen/i18n/zh_CN/_manifest.ron +++ b/assets/voxygen/i18n/zh_CN/_manifest.ron @@ -28,205 +28,5 @@ asset_key: "voxygen.font.WenQuanYiZenHei", scale_ratio: 1.0, ), - }, - - string_map: { - - }, - - vector_map: { - "loading.tips": [ - "按下 'G' 来点亮提灯.", - "按下 'F1' 查看所有默认快捷键.", - "你可以输入 /say 或 /s 只与您周围的玩家聊天.", - "你可以输入 /region 或 /r 只与你区域范围内的玩家聊天.", - "管理员可以输入 /build 指令来进入建造模式.", - "你可以输入 /group 或 /g 只与你的队伍的玩家聊天.", - "你可以输入 /tell 玩家名称 发送私人消息.", - "注意地面上的食物,箱子以及其他战利品!", - "背包里全是食物? 尝试使用它们制作更好的食物!", - "不知道做什么? 地图上褐色标点区域有地牢!", - "不要忘记调整图形设置. 按下 'N' 打开设置.", - "和其他人一起游玩时! 按下 'O' 查看在线玩家.", - "按下 'J' 跳舞!", - "按下 'L-Shift' 可以打开滑翔翼并立刻起飞", - "Veloren 处于Pre-Alpha阶段. 我们每天都在努力改善它!", - "如果您想加入开发团队或与我们聊天,请加入我们的Discord服务器.", - "你可以在设置中的生命栏中切换显示你的生命状态.", - "坐在篝火旁(同时按下 'K' 键),会缓慢恢复血量.", - "需要更大的背包或更好的护甲来继续你的旅程吗? 按下 'C' 打开制作菜单!", - ], - "npc.speech.villager": [ - "这不是很愉快的一天吗?", - "你今天过得怎么样?", - "早上好!", - "我不知道Catoblepas吃草时会在想什么.", - "你觉得这天气怎样?", - "一想到那些地牢就会让我感到害怕. 我希望有人能把它们清除掉.", - "当我变得更强大时,我想去山洞里兜兜风.", - "你有看见我的猫吗?", - "你听说过凶猛的陆地鲨吗?我听说它们生活在沙漠中.", - "他们说在洞穴中可以找到各种闪亮的宝石.", - "我超喜欢奶酪的!", - "你不进来吗?我们正要吃奶酪呢!", - "他们说蘑菇对你的健康有益,切勿单独食用.", - "别忘了饼干的事!", - "我只是崇拜矮人奶酪,我希望我能做到.", - "我想知道山的另一边是什么.", - "我希望有一天能制做出自己的滑翔伞.", - "你想看看我的花园吗?好吧,也许还需要再等一等.", - "在树林里漫步的美好一天!", - "成败与否?我想我会成为一名农夫.", - "你不认为我们的村庄是最好的吗?", - "你认为使尸体发光的原因是什么?", - "我认为是时候吃第二顿早餐了!", - "你曾经捉住过萤火虫吗?", - "我只是不明白那些蜥蜴人从何而来.", - "我希望有人能让狼远离村子.", - "昨晚我梦见了美妙的奶酪.这意味着什么?", - "我和哥哥一起留了一些奶酪.现在我不知道它是否还在.我称它为薛定谔的奶酪.", - "我和妹妹一起留了一些奶酪.现在我不知道它是否还在.我称它为薛定谔的奶酪.", - "有人应该对那些邪教徒做些什么.最好不是我.", - "我希望快点下雨.对农作物有好处.", - "我爱蜂蜜!但我讨厌蜜蜂.", - "我希望有一天能看到外面的世界.美好的生活不仅在这村庄里.", - ], - "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": [ - "当心!那里有一个邪教徒!", - "拿起武器!信徒们要进攻了!", - "邪教徒怎么敢攻击我们的村庄!", - "该死的邪教徒!", - "这里决不容忍邪教徒!", - "凶恶的邪教徒!", - "肮脏的邪教徒,尝尝我的剑刃!", - "你们邪教徒的手上沾满了鲜血,别想洗清你们的罪孽!", - "Billions of blistering blue barnacles! A cultist among us!", - "这个邪教徒的邪恶一生即将结束!", - "这个邪教徒是我的了!", - "准备跟你的造物主见面吧,肮脏的邪教徒!", - "我看到一个邪教徒!抓住他们!", - "我看到一个邪教徒!攻击!", - "我看到一个邪教徒!别让他跑了!", - "大多数的邪教徒都会在意死亡吗?!", - "决不原谅!永不忘记!忏悔吧,邪教徒!", - "去死吧,邪教徒!", - "你的恐怖统治即将终结!", - "你所做的这一切,罪有应得!", - "我们这里很不欢迎你这种家伙.", - "你应该下地狱!", - ], - "npc.speech.villager_under_attack": [ - "救命, 我们受到攻击!", - "救命! 我们受到攻击!", - "哎哟! 我受到攻击!", - "哎哟! 我受到攻击! 我需要帮助!", - "快救我! 我受到攻击!", - "我受到攻击! 救命!", - "我受到攻击! 我需要帮助!", - "救命!", - "救命! 救命!", - "救命! 救命! 救命!", - "我受到攻击!", - "啊啊啊啊! 我受到攻击!", - "啊啊啊啊! 我受到攻击! 救命!", - "救命! 我们受到攻击!", - "救命! 有杀人犯!", - "救命! 这里有一个杀人犯在逃跑!", - "救命! 他们想杀我!", - "守卫, 我受到攻击!", - "守卫! 我受到攻击!", - "我受到攻击! 守卫!", - "救命! 守卫! 我受到攻击!", - "守卫! 快来!", - "守卫! 守卫!", - "守卫! 这里有一个恶棍在攻击我!", - "守卫, 快杀死这个恶棍!", - "守卫! 这里有一个杀人犯!", - "守卫! 帮帮我!", - "你别想逃了! 守卫!", - "你是恶魔!", - "救命!", - "救救我! 拜托!", - "哎哟! 守卫! 救命!", - "他们会来找我的!", - "救命! 救命! 我被压迫了!", - "啊, 我们看到了这存在于系统里的暴力.", - "这只是擦伤而已!", - "停下来!", - "我曾经对你做过什么?!", - "不要攻击我了!", - "嘿! 注意你指向的东西!", - "可恶的家伙, 你们一起去死吧!", - "停下来! 然后滚蛋!", - "你现在让我很生气!", - "噢! 你以为你是谁?!", - "我会帮你的!", - "停, 请停一下! 我没有任何值钱的东西!", - "我把我的兄弟放在你身上, 他比我的大多了!", - "不, 我要告诉妈妈!", - "诅咒你!", - "请不要这么做.", - "那不是很好!", - "你的武器很好, 现在可以收起来了!", - "绕了我吧!", - "拜托了, 我还有家庭!", - "我还小就要死了!", - "我们可以谈谈这个吗?", - "暴力永远不是解决问题的方式!", - "今天真是非常糟糕的一天...", - "喂, 别打我!", - "诶!", - "真没礼貌!", - "停手, 求求你!", - "你有病啊!", - "这不好玩.", - "你怎么敢?!", - "你会为此复出代价的!", - "坚持下去你会后悔的!", - "不要让我伤害你!", - "这肯定有什么误会!", - "你没必要这样对我吧!", - "再见吧, 恶魔!", - "真的好疼!", - "为什么要这么做?", - "神经病啊, 停手!", - "你让我和别人感到很迷惑!", - "我不应该这样!", - "请不要再这样做.", - "守卫, 把这个怪物扔进湖里!", - "我会把怪兽放在你身上!", - "为什么是我...?", - ], - "npc.speech.villager_enemy_killed": [ - "我消灭了我的敌人!", - "终于和平了!", - "...现在我在做什么?", - ] } ) diff --git a/assets/voxygen/i18n/zh_CN/buff.ron b/assets/voxygen/i18n/zh_CN/buff.ron index 64b0b7df0f..7efc477e8f 100644 --- a/assets/voxygen/i18n/zh_CN/buff.ron +++ b/assets/voxygen/i18n/zh_CN/buff.ron @@ -26,7 +26,7 @@ "buff.desc.cursed": "你被诅咒了.", // Buffs stats "buff.stat.health": "总共恢复 {str_total} 点生命值", - "buff.stat.increase_max_stamina": "提高{strength}点耐力值上限 ", + "buff.stat.increase_max_energy": "提高{strength}点耐力值上限 ", "buff.stat.increase_max_health": "提高{strength}点生命值上限", "buff.stat.invulnerability": "获得无敌状态", // Text diff --git a/assets/voxygen/i18n/zh_CN/hud/bag.ron b/assets/voxygen/i18n/zh_CN/hud/bag.ron index 32a3169e2a..45e6602221 100644 --- a/assets/voxygen/i18n/zh_CN/hud/bag.ron +++ b/assets/voxygen/i18n/zh_CN/hud/bag.ron @@ -26,7 +26,7 @@ "hud.bag.offhand": "副手", "hud.bag.bag": "背包", "hud.bag.health": "血量", - "hud.bag.stamina": "耐力", + "hud.bag.energy": "耐力", "hud.bag.combat_rating": "战力", "hud.bag.protection": "防御", "hud.bag.stun_res": "韧性恢复", diff --git a/assets/voxygen/i18n/zh_CN/hud/hud_settings.ron b/assets/voxygen/i18n/zh_CN/hud/settings.ron similarity index 97% rename from assets/voxygen/i18n/zh_CN/hud/hud_settings.ron rename to assets/voxygen/i18n/zh_CN/hud/settings.ron index d01d5e4a09..64683542a4 100644 --- a/assets/voxygen/i18n/zh_CN/hud/hud_settings.ron +++ b/assets/voxygen/i18n/zh_CN/hud/settings.ron @@ -15,7 +15,7 @@ "hud.settings.relative_scaling": "相对缩放", "hud.settings.custom_scaling": "自定义缩放", "hud.settings.crosshair": "准星", - "hud.settings.transparency": "透明度", + "hud.settings.opacity": "透明度", "hud.settings.hotbar": "快捷键", "hud.settings.toggle_shortcuts": "显示快捷键", "hud.settings.buffs_skillbar": "增益效果显示在技能栏", @@ -33,7 +33,7 @@ "hud.settings.values": "数字", "hud.settings.percentages": "百分比", "hud.settings.chat": "聊天", - "hud.settings.background_transparency": "背景透明度", + "hud.settings.background_opacity": "背景透明度", "hud.settings.chat_character_name": "聊天显示人物名称", "hud.settings.loading_tips": "加载游戏时显示小提示", "hud.settings.reset_interface": "重置为默认", diff --git a/assets/voxygen/i18n/zh_CN/skills.ron b/assets/voxygen/i18n/zh_CN/hud/skills.ron similarity index 97% rename from assets/voxygen/i18n/zh_CN/skills.ron rename to assets/voxygen/i18n/zh_CN/hud/skills.ron index fd35f09502..7a3e1840bd 100644 --- a/assets/voxygen/i18n/zh_CN/skills.ron +++ b/assets/voxygen/i18n/zh_CN/hud/skills.ron @@ -11,8 +11,8 @@ // General "hud.skill.inc_health_title": "增强生命", "hud.skill.inc_health": "最大生命值提高{boost}{SP}", - "hud.skill.inc_stam_title": "增强耐力/法力", - "hud.skill.inc_stam": "最大耐力提升{boost}{SP}", + "hud.skill.inc_energy_title": "增强耐力/法力", + "hud.skill.inc_energy": "最大耐力提升{boost}{SP}", "hud.skill.unlck_sword_title": "解锁双手剑专精", "hud.skill.unlck_sword": "解锁双手剑技能树{SP}", "hud.skill.unlck_axe_title": "解锁双手斧专精", @@ -27,8 +27,8 @@ "hud.skill.unlck_sceptre": "解锁生命权杖技能树{SP}", "hud.skill.dodge_title": "闪避", "hud.skill.dodge": "翻滚可以躲避敌人的攻击{SP}", - "hud.skill.roll_stamina_title": "体术(翻滚)", - "hud.skill.roll_stamina": "减少每次翻滚需要消耗的耐力{boost}%{SP}", + "hud.skill.roll_energy_title": "体术(翻滚)", + "hud.skill.roll_energy": "减少每次翻滚需要消耗的耐力{boost}%{SP}", "hud.skill.roll_speed_title": "迅捷", "hud.skill.roll_speed": "加快{boost}%的翻滚速度{SP}", "hud.skill.roll_dur_title": "飞身跃入", @@ -95,8 +95,8 @@ "hud.skill.st_flamethrower_damage" : "吐息造成的伤害提高{boost}%{SP}", "hud.skill.st_explosion_radius_title" : "爆破专家", "hud.skill.st_explosion_radius" : "火球爆炸的半径扩大{boost}%{SP}", - "hud.skill.st_stamina_regen_title" : "法力回复", - "hud.skill.st_stamina_regen" : "每次击中敌人回复的法力增加{boost}%{SP}", + "hud.skill.st_energy_regen_title" : "法力回复", + "hud.skill.st_energy_regen" : "每次击中敌人回复的法力增加{boost}%{SP}", "hud.skill.st_fireball_title" : "火球术", "hud.skill.st_fireball" : "发射火球攻击敌人", "hud.skill.st_damage_title" : "伤害提升", diff --git a/assets/voxygen/i18n/zh_CN/main.ron b/assets/voxygen/i18n/zh_CN/main.ron index bf76304f5d..aaefadbe8a 100644 --- a/assets/voxygen/i18n/zh_CN/main.ron +++ b/assets/voxygen/i18n/zh_CN/main.ron @@ -64,5 +64,26 @@ https://veloren.net/account/."#, vector_map: { + "loading.tips": [ + "按下 'G' 来点亮提灯.", + "按下 'F1' 查看所有默认快捷键.", + "你可以输入 /say 或 /s 只与您周围的玩家聊天.", + "你可以输入 /region 或 /r 只与你区域范围内的玩家聊天.", + "管理员可以输入 /build 指令来进入建造模式.", + "你可以输入 /group 或 /g 只与你的队伍的玩家聊天.", + "你可以输入 /tell 玩家名称 发送私人消息.", + "注意地面上的食物,箱子以及其他战利品!", + "背包里全是食物? 尝试使用它们制作更好的食物!", + "不知道做什么? 地图上褐色标点区域有地牢!", + "不要忘记调整图形设置. 按下 'N' 打开设置.", + "和其他人一起游玩时! 按下 'O' 查看在线玩家.", + "按下 'J' 跳舞!", + "按下 'L-Shift' 可以打开滑翔翼并立刻起飞", + "Veloren 处于Pre-Alpha阶段. 我们每天都在努力改善它!", + "如果您想加入开发团队或与我们聊天,请加入我们的Discord服务器.", + "你可以在设置中的生命栏中切换显示你的生命状态.", + "坐在篝火旁(同时按下 'K' 键),会缓慢恢复血量.", + "需要更大的背包或更好的护甲来继续你的旅程吗? 按下 'C' 打开制作菜单!", + ], } ) diff --git a/assets/voxygen/i18n/zh_CN/npc.ron b/assets/voxygen/i18n/zh_CN/npc.ron new file mode 100644 index 0000000000..03df6f1099 --- /dev/null +++ b/assets/voxygen/i18n/zh_CN/npc.ron @@ -0,0 +1,183 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// 本地化 "Simplified Chinese" 简体中文 +( + string_map: { + + }, + + vector_map: { + "npc.speech.villager": [ + "这不是很愉快的一天吗?", + "你今天过得怎么样?", + "早上好!", + "我不知道Catoblepas吃草时会在想什么.", + "你觉得这天气怎样?", + "一想到那些地牢就会让我感到害怕. 我希望有人能把它们清除掉.", + "当我变得更强大时,我想去山洞里兜兜风.", + "你有看见我的猫吗?", + "你听说过凶猛的陆地鲨吗?我听说它们生活在沙漠中.", + "他们说在洞穴中可以找到各种闪亮的宝石.", + "我超喜欢奶酪的!", + "你不进来吗?我们正要吃奶酪呢!", + "他们说蘑菇对你的健康有益,切勿单独食用.", + "别忘了饼干的事!", + "我只是崇拜矮人奶酪,我希望我能做到.", + "我想知道山的另一边是什么.", + "我希望有一天能制做出自己的滑翔伞.", + "你想看看我的花园吗?好吧,也许还需要再等一等.", + "在树林里漫步的美好一天!", + "成败与否?我想我会成为一名农夫.", + "你不认为我们的村庄是最好的吗?", + "你认为使尸体发光的原因是什么?", + "我认为是时候吃第二顿早餐了!", + "你曾经捉住过萤火虫吗?", + "我只是不明白那些蜥蜴人从何而来.", + "我希望有人能让狼远离村子.", + "昨晚我梦见了美妙的奶酪.这意味着什么?", + "我和哥哥一起留了一些奶酪.现在我不知道它是否还在.我称它为薛定谔的奶酪.", + "我和妹妹一起留了一些奶酪.现在我不知道它是否还在.我称它为薛定谔的奶酪.", + "有人应该对那些邪教徒做些什么.最好不是我.", + "我希望快点下雨.对农作物有好处.", + "我爱蜂蜜!但我讨厌蜜蜂.", + "我希望有一天能看到外面的世界.美好的生活不仅在这村庄里.", + ], + "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": [ + "当心!那里有一个邪教徒!", + "拿起武器!信徒们要进攻了!", + "邪教徒怎么敢攻击我们的村庄!", + "该死的邪教徒!", + "这里决不容忍邪教徒!", + "凶恶的邪教徒!", + "肮脏的邪教徒,尝尝我的剑刃!", + "你们邪教徒的手上沾满了鲜血,别想洗清你们的罪孽!", + "Billions of blistering blue barnacles! A cultist among us!", + "这个邪教徒的邪恶一生即将结束!", + "这个邪教徒是我的了!", + "准备跟你的造物主见面吧,肮脏的邪教徒!", + "我看到一个邪教徒!抓住他们!", + "我看到一个邪教徒!攻击!", + "我看到一个邪教徒!别让他跑了!", + "大多数的邪教徒都会在意死亡吗?!", + "决不原谅!永不忘记!忏悔吧,邪教徒!", + "去死吧,邪教徒!", + "你的恐怖统治即将终结!", + "你所做的这一切,罪有应得!", + "我们这里很不欢迎你这种家伙.", + "你应该下地狱!", + ], + "npc.speech.villager_under_attack": [ + "救命, 我们受到攻击!", + "救命! 我们受到攻击!", + "哎哟! 我受到攻击!", + "哎哟! 我受到攻击! 我需要帮助!", + "快救我! 我受到攻击!", + "我受到攻击! 救命!", + "我受到攻击! 我需要帮助!", + "救命!", + "救命! 救命!", + "救命! 救命! 救命!", + "我受到攻击!", + "啊啊啊啊! 我受到攻击!", + "啊啊啊啊! 我受到攻击! 救命!", + "救命! 我们受到攻击!", + "救命! 有杀人犯!", + "救命! 这里有一个杀人犯在逃跑!", + "救命! 他们想杀我!", + "守卫, 我受到攻击!", + "守卫! 我受到攻击!", + "我受到攻击! 守卫!", + "救命! 守卫! 我受到攻击!", + "守卫! 快来!", + "守卫! 守卫!", + "守卫! 这里有一个恶棍在攻击我!", + "守卫, 快杀死这个恶棍!", + "守卫! 这里有一个杀人犯!", + "守卫! 帮帮我!", + "你别想逃了! 守卫!", + "你是恶魔!", + "救命!", + "救救我! 拜托!", + "哎哟! 守卫! 救命!", + "他们会来找我的!", + "救命! 救命! 我被压迫了!", + "啊, 我们看到了这存在于系统里的暴力.", + "这只是擦伤而已!", + "停下来!", + "我曾经对你做过什么?!", + "不要攻击我了!", + "嘿! 注意你指向的东西!", + "可恶的家伙, 你们一起去死吧!", + "停下来! 然后滚蛋!", + "你现在让我很生气!", + "噢! 你以为你是谁?!", + "我会帮你的!", + "停, 请停一下! 我没有任何值钱的东西!", + "我把我的兄弟放在你身上, 他比我的大多了!", + "不, 我要告诉妈妈!", + "诅咒你!", + "请不要这么做.", + "那不是很好!", + "你的武器很好, 现在可以收起来了!", + "绕了我吧!", + "拜托了, 我还有家庭!", + "我还小就要死了!", + "我们可以谈谈这个吗?", + "暴力永远不是解决问题的方式!", + "今天真是非常糟糕的一天...", + "喂, 别打我!", + "诶!", + "真没礼貌!", + "停手, 求求你!", + "你有病啊!", + "这不好玩.", + "你怎么敢?!", + "你会为此复出代价的!", + "坚持下去你会后悔的!", + "不要让我伤害你!", + "这肯定有什么误会!", + "你没必要这样对我吧!", + "再见吧, 恶魔!", + "真的好疼!", + "为什么要这么做?", + "神经病啊, 停手!", + "你让我和别人感到很迷惑!", + "我不应该这样!", + "请不要再这样做.", + "守卫, 把这个怪物扔进湖里!", + "我会把怪兽放在你身上!", + "为什么是我...?", + ], + "npc.speech.villager_enemy_killed": [ + "我消灭了我的敌人!", + "终于和平了!", + "...现在我在做什么?", + ] + } +) diff --git a/assets/voxygen/i18n/zh_CN/template.ron b/assets/voxygen/i18n/zh_CN/template.ron deleted file mode 100644 index b6fd6b234d..0000000000 --- a/assets/voxygen/i18n/zh_CN/template.ron +++ /dev/null @@ -1,12 +0,0 @@ -/// WARNING: Localization files shall be saved in UTF-8 format without BOM - -/// 本地化 "Simplified Chinese" 简体中文 -( - string_map: { - - }, - - - vector_map: { - } -) diff --git a/assets/voxygen/i18n/zh_TW/_manifest.ron b/assets/voxygen/i18n/zh_TW/_manifest.ron index fa00cc01f4..61925db51f 100644 --- a/assets/voxygen/i18n/zh_TW/_manifest.ron +++ b/assets/voxygen/i18n/zh_TW/_manifest.ron @@ -26,360 +26,5 @@ asset_key: "voxygen.font.bdfUMplus-outline", scale_ratio: 0.75, ), - }, - 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.resume": "繼續", - "common.characters": "角色", - "common.close": "關閉", - "common.yes": "是", - "common.no": "否", - "common.back": "返回", - "common.create": "建立", - "common.okay": "好", - "common.accept": "接受", - "common.disclaimer": "免責聲明", - "common.cancel": "取消", - "common.none": "無", - "common.error": "錯誤", - "common.fatal_error": "致命錯誤", - - // Message when connection to the server is lost - "common.connection_lost": r#"連線中斷! -檢查看看伺服器重啟了嗎? -客戶端有更新了嗎?"#, - - - "common.races.orc": "獸人", - "common.races.human": "人類", - "common.races.dwarf": "矮人", - "common.races.elf": "精靈", - "common.races.undead": "不死族", - "common.races.danari": "丹那利", - - "common.weapons.axe": "斧", - "common.weapons.sword": "劍", - "common.weapons.staff": "杖", - "common.weapons.bow": "弓", - "common.weapons.hammer": "鎚", - /// End Common section - - - /// Start Main screen section - "main.connecting": "連線中", - "main.creating_world": "生成世界中", - - // Welcome notice that appears the first time Veloren is started - "main.notice": r#"歡迎加入 Veloren alpha 版! - -在您開始享受遊戲之前,請注意一些事情: - -- 這是非常前期的 alpha 版本,您會遇到不少錯誤、未完成遊戲模式、未完善的遊戲機制以及缺失的功能。 -- 如鬼有建設性的意見回饋或是錯誤回報,可以上 Reddit、GitLab 或者我們的 Discord 伺服器。 -- Veloren 的授權條款是 GPL 3 open-source licence,也就是任何人都可以任意的遊玩、更改並重新發布遊戲(其衍生的專案也都是 GPL 3)。 -- Veloren 是個非營利的社群專案,任何人都是自願參預開發的。如果喜歡的話,歡迎加入開發與美術設計團隊。 -- 「Voxel RPG」是個專屬的類別,就像任何的第一人稱射擊遊戲以前都稱作 Doom 的複製品。 - -就像他們一樣,我們想要打造完美的作品,這不是款複製品遊戲,而此遊戲的開發將會讓未來的遊戲更加多元化。 - -感謝您的閱讀,我們祝您能享受遊戲! - -~ Veloren 開發人員"#, - - // Login process description - "main.login_process": r#"Information on the Login Process: - -If you are having issues signing in: - -Please note that you now need an account -to play on auth-enabled servers. - -You can create an account over at - -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": "伺服器錯誤:版本可能不相容,請檢查更新。", - "main.login.timeout": "逾時:伺服器無法即時回應(也許試過載或者網路問題)。", - "main.login.server_shut_down": "伺服器已關閉", - "main.login.network_error": "網路錯誤", - "main.login.failed_sending_request": "認證伺服器請求失敗", - "main.login.client_crashed": "客戶端崩潰", - "main.login.select_language": "选择一种语言 ", - - /// End Main screen section - - - /// Start HUD Section - "hud.do_not_show_on_startup": "開啟時不顯示這個", - "hud.show_tips": "顯示提示", - "hud.quests": "任務", - "hud.you_died": "死亡", - - "hud.press_key_to_show_keybindings_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} 以切換除錯資訊", - - // Respawn message - "hud.press_key_to_respawn": r#"按 {key} 以重生在上一個營火堆。"#, - - // Welcome message - "hud.welcome": r#"歡迎來到 Veloren Alpha 版, - - -以下是些開始前的提示: - - -最重要的提示:想設置重生點請在聊天欄輸入 /waypoint 。 - -就算死了也可以作! - - -按 F1 可以查看按鍵設置。 - -在聊天欄輸入 /help 可以查看聊天指令 - - -寶箱和物品會隨機重生在世界中! - -點擊右鍵能收集它們。 - -要真的使用收集到的物品,請按「B」開啟物品欄。 - -在背包中雙擊物品來使用或裝備它們。 - -要拋棄它們的話,可以按它們一次然後再點背包外面一次 - - -Veloren 半夜會特別暗。 - -在聊天欄輸入 /lantern 可以點亮提燈 - - -想要用滑鼠關閉這個視窗?請按 TAB! - - -祝您的 Veloren 旅途愉快。"#, - // Inventory - "hud.bag.inventory": "的物品欄", - "hud.bag.stats_title": "的狀態", - "hud.bag.exp": "經驗", - "hud.bag.armor": "護甲", - "hud.bag.stats": "狀態", - - // 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.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.energybar_numbers": "能量條", - "hud.settings.values": "數字", - "hud.settings.percentages": "百分比", - "hud.settings.chat": "聊天欄", - "hud.settings.background_transparency": "背景透明度", - "hud.settings.none": "無", - - "hud.settings.pan_sensitivity": "滑鼠靈敏度", - "hud.settings.zoom_sensitivity": "縮放靈敏度", - "hud.settings.invert_scroll_zoom": "反轉滾輪縮放", - "hud.settings.invert_mouse_y_axis": "反轉 Y 軸", - "hud.settings.free_look_behavior": "自由視角", - - "hud.settings.view_distance": "視野距離", - "hud.settings.maximum_fps": "最高 FPS", - "hud.settings.fov": "視野", - "hud.settings.gamma": "Gamma", - "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.regular": "一般", - "hud.settings.fullscreen": "全螢幕", - "hud.settings.save_window_size": "儲存螢幕大小", - - "hud.settings.music_volume": "音樂音量", - "hud.settings.sound_effect_volume": "音效音量", - "hud.settings.audio_device": "音訊設備", - - // Control list - "hud.settings.control_names": r#"顯示滑鼠 -切換協助視窗 -切換界面 -切換 FPS 與除錯資訊 -拍照 -切換名稱標籤 -切換全螢幕 - - -往前移動 -往左移動 -往右移動 -往後移動 - -跳躍 - -滑翔翼 - -閃避 - -翻滾 - -攀爬 - -往下攀爬 - -自動行走 - -收刀/拔出武器 - -戴上/卸下頭盔 - -坐下 - -騎乘 - -互動 - - -基本攻擊 -附屬攻擊/格檔/瞄準 - - -技能條欄位 1 -技能條欄位 2 -技能條欄位 3 -技能條欄位 4 -技能條欄位 5 -技能條欄位 6 -技能條欄位 7 -技能條欄位 8 -技能條欄位 9 -技能條欄位 10 - - -暫停選單 -設定 -社交 -地圖 -法術書 -角色 -任務日誌 -背包 - - - -傳送聊天訊息 -滾動聊天欄 - - -自由視角 - -聊天指令: - -/alias [Name] - 變更的聊天名稱 -/tp [Name] - 傳送到其他玩家 -/jump - 偏移自己的位置 -/goto - 傳送到指定位置 -/kill - 自殺 -/pig - 生成一隻豬 NPC -/wolf - 生成一隻狼 NPC -/help - 顯示聊天指令"#, - - "hud.social": "社交", - "hud.social.online": "線上", - "hud.social.friends": "朋友", - "hud.social.not_yet_available": "尚未開放", - "hud.social.faction": "陣營", - "hud.social.play_online_fmt": "{nb_player} 位朋友在線", - - "hud.spell": "法術", - - "hud.free_look_indicator": "進入自由視角中", - /// End HUD section - - - /// Start chracter selection section - "char_selection.delete_permanently": "永久刪除這個角色嗎?", - "char_selection.change_server": "變更伺服器", - "char_selection.enter_world": "進入世界", - "char_selection.logout": "登出", - "char_selection.create_new_charater": "建立心角色", - "char_selection.character_creation": "角色建立", - - "char_selection.human_default": "預設人類", - "char_selection.level_fmt": "等級 {level_nb}", - "char_selection.uncanny_valley": "怪異峽谷", - "char_selection.plains_of_uncertainty": "疑惑平原", - "char_selection.beard": "鬍鬚", - "char_selection.hair_style": "頭髮造型", - "char_selection.hair_color": "頭髮顏色", - "char_selection.chest_color": "上半身顏色", - "char_selection.eye_color": "眼睛顏色", - "char_selection.skin": "膚色", - "char_selection.eyebrows": "眉毛", - "char_selection.accessories": "飾品", - - /// End chracter selection section - - - /// Start character window section - "character_window.character_name": "角色名稱", - // Charater stats - "character_window.character_stats": r#"耐力 - -敏捷 - -法力 -"#, - - - /// Start character window section - - - /// Start Escape Menu Section - "esc_menu.logout": "登出", - "esc_menu.quit_game": "退出遊戲", - /// End Escape Menu Section - }, - - vector_map: { - - }, + } ) diff --git a/assets/voxygen/i18n/zh_TW/buff.ron b/assets/voxygen/i18n/zh_TW/buff.ron new file mode 100644 index 0000000000..9942ee4a90 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/buff.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/char_selection.ron b/assets/voxygen/i18n/zh_TW/char_selection.ron new file mode 100644 index 0000000000..10b01b0338 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/char_selection.ron @@ -0,0 +1,29 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + "char_selection.delete_permanently": "永久刪除這個角色嗎?", + "char_selection.change_server": "變更伺服器", + "char_selection.enter_world": "進入世界", + "char_selection.logout": "登出", + "char_selection.create_new_charater": "建立心角色", + "char_selection.character_creation": "角色建立", + + "char_selection.human_default": "預設人類", + "char_selection.level_fmt": "等級 {level_nb}", + "char_selection.uncanny_valley": "怪異峽谷", + "char_selection.plains_of_uncertainty": "疑惑平原", + "char_selection.beard": "鬍鬚", + "char_selection.hair_style": "頭髮造型", + "char_selection.hair_color": "頭髮顏色", + "char_selection.chest_color": "上半身顏色", + "char_selection.eye_color": "眼睛顏色", + "char_selection.skin": "膚色", + "char_selection.eyebrows": "眉毛", + "char_selection.accessories": "飾品", + }, + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/common.ron b/assets/voxygen/i18n/zh_TW/common.ron new file mode 100644 index 0000000000..f1c412f57d --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/common.ron @@ -0,0 +1,57 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + 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.resume": "繼續", + "common.characters": "角色", + "common.close": "關閉", + "common.yes": "是", + "common.no": "否", + "common.back": "返回", + "common.create": "建立", + "common.okay": "好", + "common.accept": "接受", + "common.disclaimer": "免責聲明", + "common.cancel": "取消", + "common.none": "無", + "common.error": "錯誤", + "common.fatal_error": "致命錯誤", + + // Message when connection to the server is lost + "common.connection_lost": r#"連線中斷! +檢查看看伺服器重啟了嗎? +客戶端有更新了嗎?"#, + + + "common.races.orc": "獸人", + "common.races.human": "人類", + "common.races.dwarf": "矮人", + "common.races.elf": "精靈", + "common.races.undead": "不死族", + "common.races.danari": "丹那利", + + "common.weapons.axe": "斧", + "common.weapons.sword": "劍", + "common.weapons.staff": "杖", + "common.weapons.bow": "弓", + "common.weapons.hammer": "鎚", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/esc_menu.ron b/assets/voxygen/i18n/zh_TW/esc_menu.ron new file mode 100644 index 0000000000..306b0e99f4 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/esc_menu.ron @@ -0,0 +1,13 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + "esc_menu.logout": "登出", + "esc_menu.quit_game": "退出遊戲", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/gameinput.ron b/assets/voxygen/i18n/zh_TW/gameinput.ron new file mode 100644 index 0000000000..9942ee4a90 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/gameinput.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/hud/bag.ron b/assets/voxygen/i18n/zh_TW/hud/bag.ron new file mode 100644 index 0000000000..b7deda04c5 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/bag.ron @@ -0,0 +1,17 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + // Inventory + "hud.bag.inventory": "的物品欄", + "hud.bag.stats_title": "的狀態", + "hud.bag.exp": "經驗", + "hud.bag.armor": "護甲", + "hud.bag.stats": "狀態", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/hud/char_window.ron b/assets/voxygen/i18n/zh_TW/hud/char_window.ron new file mode 100644 index 0000000000..2b2c284546 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/char_window.ron @@ -0,0 +1,20 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + /// Start character window section + "character_window.character_name": "角色名稱", + // Charater stats + "character_window.character_stats": r#"耐力 + +敏捷 + +法力 +"#, + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/hud/chat.ron b/assets/voxygen/i18n/zh_TW/hud/chat.ron new file mode 100644 index 0000000000..9942ee4a90 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/chat.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/hud/crafting.ron b/assets/voxygen/i18n/zh_TW/hud/crafting.ron new file mode 100644 index 0000000000..9942ee4a90 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/crafting.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/hud/group.ron b/assets/voxygen/i18n/zh_TW/hud/group.ron new file mode 100644 index 0000000000..9942ee4a90 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/group.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/hud/map.ron b/assets/voxygen/i18n/zh_TW/hud/map.ron new file mode 100644 index 0000000000..21ac3c7015 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/map.ron @@ -0,0 +1,14 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + // Map and Questlog + "hud.map.map_title": "地圖", + "hud.map.qlog_title": "任務", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/hud/misc.ron b/assets/voxygen/i18n/zh_TW/hud/misc.ron new file mode 100644 index 0000000000..2bd08e3fb7 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/misc.ron @@ -0,0 +1,65 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + "hud.do_not_show_on_startup": "開啟時不顯示這個", + "hud.show_tips": "顯示提示", + "hud.quests": "任務", + "hud.you_died": "死亡", + + "hud.press_key_to_show_keybindings_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} 以切換除錯資訊", + + // Respawn message + "hud.press_key_to_respawn": r#"按 {key} 以重生在上一個營火堆。"#, + + // Welcome message + "hud.welcome": r#"歡迎來到 Veloren Alpha 版, + + +以下是些開始前的提示: + + +最重要的提示:想設置重生點請在聊天欄輸入 /waypoint 。 + +就算死了也可以作! + + +按 F1 可以查看按鍵設置。 + +在聊天欄輸入 /help 可以查看聊天指令 + + +寶箱和物品會隨機重生在世界中! + +點擊右鍵能收集它們。 + +要真的使用收集到的物品,請按「B」開啟物品欄。 + +在背包中雙擊物品來使用或裝備它們。 + +要拋棄它們的話,可以按它們一次然後再點背包外面一次 + + +Veloren 半夜會特別暗。 + +在聊天欄輸入 /lantern 可以點亮提燈 + + +想要用滑鼠關閉這個視窗?請按 TAB! + + +祝您的 Veloren 旅途愉快。"#, + + + "hud.spell": "法術", + "hud.free_look_indicator": "進入自由視角中", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/hud/sct.ron b/assets/voxygen/i18n/zh_TW/hud/sct.ron new file mode 100644 index 0000000000..9942ee4a90 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/sct.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/hud/settings.ron b/assets/voxygen/i18n/zh_TW/hud/settings.ron new file mode 100644 index 0000000000..c1fe7bd594 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/settings.ron @@ -0,0 +1,145 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + // 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.opacity": "透明度", + "hud.settings.hotbar": "角色資訊", + "hud.settings.toggle_shortcuts": "顯示快捷鍵", + "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.energybar_numbers": "能量條", + "hud.settings.values": "數字", + "hud.settings.percentages": "百分比", + "hud.settings.chat": "聊天欄", + "hud.settings.background_opacity": "背景透明度", + "hud.settings.none": "無", + + "hud.settings.pan_sensitivity": "滑鼠靈敏度", + "hud.settings.zoom_sensitivity": "縮放靈敏度", + "hud.settings.invert_scroll_zoom": "反轉滾輪縮放", + "hud.settings.invert_mouse_y_axis": "反轉 Y 軸", + "hud.settings.free_look_behavior": "自由視角", + + "hud.settings.view_distance": "視野距離", + "hud.settings.maximum_fps": "最高 FPS", + "hud.settings.fov": "視野", + "hud.settings.gamma": "Gamma", + "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.regular": "一般", + "hud.settings.fullscreen": "全螢幕", + "hud.settings.save_window_size": "儲存螢幕大小", + + "hud.settings.music_volume": "音樂音量", + "hud.settings.sound_effect_volume": "音效音量", + "hud.settings.audio_device": "音訊設備", + + // Control list + "hud.settings.control_names": r#"顯示滑鼠 +切換協助視窗 +切換界面 +切換 FPS 與除錯資訊 +拍照 +切換名稱標籤 +切換全螢幕 + + +往前移動 +往左移動 +往右移動 +往後移動 + +跳躍 + +滑翔翼 + +閃避 + +翻滾 + +攀爬 + +往下攀爬 + +自動行走 + +收刀/拔出武器 + +戴上/卸下頭盔 + +坐下 + +騎乘 + +互動 + + +基本攻擊 +附屬攻擊/格檔/瞄準 + + +技能條欄位 1 +技能條欄位 2 +技能條欄位 3 +技能條欄位 4 +技能條欄位 5 +技能條欄位 6 +技能條欄位 7 +技能條欄位 8 +技能條欄位 9 +技能條欄位 10 + + +暫停選單 +設定 +社交 +地圖 +法術書 +角色 +任務日誌 +背包 + + + +傳送聊天訊息 +滾動聊天欄 + + +自由視角 + +聊天指令: + +/alias [Name] - 變更的聊天名稱 +/tp [Name] - 傳送到其他玩家 +/jump - 偏移自己的位置 +/goto - 傳送到指定位置 +/kill - 自殺 +/pig - 生成一隻豬 NPC +/wolf - 生成一隻狼 NPC +/help - 顯示聊天指令"#, + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/hud/skills.ron b/assets/voxygen/i18n/zh_TW/hud/skills.ron new file mode 100644 index 0000000000..9942ee4a90 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/skills.ron @@ -0,0 +1,11 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/hud/social.ron b/assets/voxygen/i18n/zh_TW/hud/social.ron new file mode 100644 index 0000000000..109a665f9e --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/social.ron @@ -0,0 +1,18 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + "hud.social": "社交", + "hud.social.online": "線上", + "hud.social.friends": "朋友", + "hud.social.not_yet_available": "尚未開放", + "hud.social.faction": "陣營", + "hud.social.play_online_fmt": "{nb_player} 位朋友在線", + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/zh_TW/hud/trade.ron b/assets/voxygen/i18n/zh_TW/hud/trade.ron new file mode 100644 index 0000000000..4feee7e25a --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/hud/trade.ron @@ -0,0 +1,12 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/zh_TW/main.ron b/assets/voxygen/i18n/zh_TW/main.ron new file mode 100644 index 0000000000..103ee284df --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/main.ron @@ -0,0 +1,53 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for Traditional Chinese +( + string_map: { + "main.connecting": "連線中", + "main.creating_world": "生成世界中", + + // Welcome notice that appears the first time Veloren is started + "main.notice": r#"歡迎加入 Veloren alpha 版! + +在您開始享受遊戲之前,請注意一些事情: + +- 這是非常前期的 alpha 版本,您會遇到不少錯誤、未完成遊戲模式、未完善的遊戲機制以及缺失的功能。 +- 如鬼有建設性的意見回饋或是錯誤回報,可以上 Reddit、GitLab 或者我們的 Discord 伺服器。 +- Veloren 的授權條款是 GPL 3 open-source licence,也就是任何人都可以任意的遊玩、更改並重新發布遊戲(其衍生的專案也都是 GPL 3)。 +- Veloren 是個非營利的社群專案,任何人都是自願參預開發的。如果喜歡的話,歡迎加入開發與美術設計團隊。 +- 「Voxel RPG」是個專屬的類別,就像任何的第一人稱射擊遊戲以前都稱作 Doom 的複製品。 + +就像他們一樣,我們想要打造完美的作品,這不是款複製品遊戲,而此遊戲的開發將會讓未來的遊戲更加多元化。 + +感謝您的閱讀,我們祝您能享受遊戲! + +~ Veloren 開發人員"#, + + // Login process description + "main.login_process": r#"Information on the Login Process: + +If you are having issues signing in: + +Please note that you now need an account +to play on auth-enabled servers. + +You can create an account over at + +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": "伺服器錯誤:版本可能不相容,請檢查更新。", + "main.login.timeout": "逾時:伺服器無法即時回應(也許試過載或者網路問題)。", + "main.login.server_shut_down": "伺服器已關閉", + "main.login.network_error": "網路錯誤", + "main.login.failed_sending_request": "認證伺服器請求失敗", + "main.login.client_crashed": "客戶端崩潰", + "main.login.select_language": "选择一种语言 ", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/zh_TW/npc.ron b/assets/voxygen/i18n/zh_TW/npc.ron new file mode 100644 index 0000000000..9f25779f55 --- /dev/null +++ b/assets/voxygen/i18n/zh_TW/npc.ron @@ -0,0 +1,10 @@ +/// Localization for Traditional Chinese +( + string_map: { + + }, + + vector_map: { + + }, +) diff --git a/assets/voxygen/item_image_manifest.ron b/assets/voxygen/item_image_manifest.ron index 303028d56e..bf44608ff0 100644 --- a/assets/voxygen/item_image_manifest.ron +++ b/assets/voxygen/item_image_manifest.ron @@ -437,6 +437,10 @@ "voxel.weapon.sword_1h.cobalt-3", (1.0, -1.0, 0.0), (-135.0, 90.0, 0.0), 1.2, ), + Tool("common.items.weapons.sword_1h.starter"): VoxTrans( + "voxel.weapon.sword_1h.starter", + (-1.0, 1.0, 0.0), (-135.0, 90.0, 0.0), 1.2, + ), Tool("common.items.weapons.sword_1h.iron-0"): VoxTrans( "voxel.weapon.sword_1h.iron-0", (1.0, -1.0, 0.0), (-135.0, 90.0, 0.0), 1.2, diff --git a/assets/voxygen/shaders/dual-downsample-filtered-frag.glsl b/assets/voxygen/shaders/dual-downsample-filtered-frag.glsl new file mode 100644 index 0000000000..9ce06098ae --- /dev/null +++ b/assets/voxygen/shaders/dual-downsample-filtered-frag.glsl @@ -0,0 +1,90 @@ +#version 420 core + +layout(set = 0, binding = 0) +uniform texture2D t_src_color; +layout(set = 0, binding = 1) +uniform sampler s_src_color; +layout(set = 0, binding = 2) + +uniform u_locals { + vec2 halfpixel; +}; + +layout(location = 0) in vec2 uv; + +layout(location = 0) out vec4 tgt_color; + +vec4 simplefetch(ivec2 uv) { + return texelFetch(sampler2D(t_src_color, s_src_color), uv, 0); +} + +// Check whether the texel color is higher than threshold, if so output as brightness color +vec4 filterDim(vec4 color) { + // constants from: https://learnopengl.com/Advanced-Lighting/Bloom + float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722)); + if(brightness > 1.00) + return vec4(color.rgb, 1.0); + else + return vec4(0.0, 0.0, 0.0, 1.0); +} + +vec4 filteredFetch(ivec2 uv) { + return filterDim(simplefetch(uv)); +} + +// Derived from: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf +vec4 filteredDownsample(vec2 uv, vec2 halfpixel) { + vec2 tex_res = 0.5 / halfpixel; + // coordinate of the top left texel + // _ _ _ _ + // |x|_|_|_| + // |_|_|_|_| + // |_|_|_|_| + // |_|_|_|_| + // + ivec2 tl_coord = ivec2(uv * tex_res + vec2(-1.5, 1.5)); + + // Fetch inner square + vec4 sum = filteredFetch(tl_coord + ivec2(1, 1)); + sum += filteredFetch(tl_coord + ivec2(2, 1)); + sum += filteredFetch(tl_coord + ivec2(1, 2)); + sum += filteredFetch(tl_coord + ivec2(2, 2)); + // Weight inner square + sum *= 5.0; + // Fetch border + sum += filteredFetch(tl_coord + ivec2(0, 0)); + sum += filteredFetch(tl_coord + ivec2(1, 0)); + sum += filteredFetch(tl_coord + ivec2(2, 0)); + sum += filteredFetch(tl_coord + ivec2(3, 0)); + sum += filteredFetch(tl_coord + ivec2(0, 1)); + sum += filteredFetch(tl_coord + ivec2(3, 1)); + sum += filteredFetch(tl_coord + ivec2(0, 2)); + sum += filteredFetch(tl_coord + ivec2(3, 2)); + sum += filteredFetch(tl_coord + ivec2(0, 3)); + sum += filteredFetch(tl_coord + ivec2(1, 3)); + sum += filteredFetch(tl_coord + ivec2(2, 3)); + sum += filteredFetch(tl_coord + ivec2(3, 3)); + + return sum / 32.0; +} + +vec4 simplesample(vec2 uv) { + return textureLod(sampler2D(t_src_color, s_src_color), uv, 0); +} + +// From: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf +vec4 downsample(vec2 uv, vec2 halfpixel) { + vec4 sum = simplesample(uv) * 4.0; + sum += simplesample(uv - halfpixel.xy); + sum += simplesample(uv + halfpixel.xy); + sum += simplesample(uv + vec2(halfpixel.x, -halfpixel.y)); + sum += simplesample(uv - vec2(halfpixel.x, -halfpixel.y)); + + return sum / 8.0; +} + +void main() { + // Uncomment to experiment with filtering out dim pixels + //tgt_color = filteredDownsample(uv, halfpixel); + tgt_color = downsample(uv, halfpixel); +} diff --git a/assets/voxygen/shaders/dual-downsample-frag.glsl b/assets/voxygen/shaders/dual-downsample-frag.glsl new file mode 100644 index 0000000000..af9fa46c09 --- /dev/null +++ b/assets/voxygen/shaders/dual-downsample-frag.glsl @@ -0,0 +1,34 @@ +#version 420 core + +layout(set = 0, binding = 0) +uniform texture2D t_src_color; +layout(set = 0, binding = 1) +uniform sampler s_src_color; +layout(set = 0, binding = 2) + +uniform u_locals { + vec2 halfpixel; +}; + +layout(location = 0) in vec2 uv; + +layout(location = 0) out vec4 tgt_color; + +vec4 simplesample(vec2 uv) { + return textureLod(sampler2D(t_src_color, s_src_color), uv, 0); +} + +// From: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf +vec4 downsample(vec2 uv, vec2 halfpixel) { + vec4 sum = simplesample(uv) * 4.0; + sum += simplesample(uv - halfpixel.xy); + sum += simplesample(uv + halfpixel.xy); + sum += simplesample(uv + vec2(halfpixel.x, -halfpixel.y)); + sum += simplesample(uv - vec2(halfpixel.x, -halfpixel.y)); + + return sum / 8.0; +} + +void main() { + tgt_color = downsample(uv, halfpixel); +} diff --git a/assets/voxygen/shaders/dual-upsample-frag.glsl b/assets/voxygen/shaders/dual-upsample-frag.glsl new file mode 100644 index 0000000000..10d1b1fdc6 --- /dev/null +++ b/assets/voxygen/shaders/dual-upsample-frag.glsl @@ -0,0 +1,35 @@ +#version 420 core + +layout(set = 0, binding = 0) +uniform texture2D t_src_color; +layout(set = 0, binding = 1) +uniform sampler s_src_color; +layout(set = 0, binding = 2) +uniform u_locals { + vec2 halfpixel; +}; + +layout(location = 0) in vec2 uv; + +layout(location = 0) out vec4 tgt_color; + +vec4 simplesample(vec2 uv) { + return textureLod(sampler2D(t_src_color, s_src_color), uv, 0); +} + +// From: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf +vec4 upsample(vec2 uv, vec2 halfpixel) { + vec4 sum = simplesample(uv + vec2(-halfpixel.x * 2.0, 0.0)); + sum += simplesample(uv + vec2(-halfpixel.x, halfpixel.y)) * 2.0; + sum += simplesample(uv + vec2(0.0, halfpixel.y * 2.0)); + sum += simplesample(uv + vec2(halfpixel.x, halfpixel.y)) * 2.0; + sum += simplesample(uv + vec2(halfpixel.x * 2.0, 0.0)); + sum += simplesample(uv + vec2(halfpixel.x, -halfpixel.y)) * 2.0; + sum += simplesample(uv + vec2(0.0, -halfpixel.y * 2.0)); + sum += simplesample(uv + vec2(-halfpixel.x, -halfpixel.y)) * 2.0; + return sum / 12.0; +} + +void main() { + tgt_color = upsample(uv, halfpixel); +} diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl index 3b084366da..0c1948023c 100644 --- a/assets/voxygen/shaders/figure-frag.glsl +++ b/assets/voxygen/shaders/figure-frag.glsl @@ -198,7 +198,7 @@ void main() { // For now, just make glowing material light be the same colour as the surface // TODO: Add a way to control this better outside the shaders if ((material & (1u << 0u)) > 0u) { - emitted_light += 1000 * surf_color; + emitted_light += 20 * surf_color; } float glow_mag = length(model_glow.xyz); diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl index 019e2b0618..67aae23766 100644 --- a/assets/voxygen/shaders/fluid-frag/shiny.glsl +++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl @@ -145,7 +145,7 @@ void main() { nmap = mix(f_norm, normalize(nmap), min(1.0 / pow(frag_dist, 0.75), 1)); //float suppress_waves = max(dot(), 0); - vec3 norm = vec3(0, 0, 1) * nmap.z + b_norm * nmap.x + c_norm * nmap.y; + vec3 norm = normalize(vec3(0, 0, 1) * nmap.z + b_norm * nmap.x + c_norm * nmap.y); // vec3 norm = f_norm; vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER; diff --git a/assets/voxygen/shaders/include/lod.glsl b/assets/voxygen/shaders/include/lod.glsl index 74f73849cf..e40da75ea8 100644 --- a/assets/voxygen/shaders/include/lod.glsl +++ b/assets/voxygen/shaders/include/lod.glsl @@ -105,10 +105,10 @@ vec2 textureBicubic16(texture2D tex, sampler sampl, vec2 texCoords) { /* // Correct for map rotaton. offset.zw = 1.0 - offset.zw; */ - vec4 sample0_v4 = texture(sampler2D(tex, sampl), offset.xz); - vec4 sample1_v4 = texture(sampler2D(tex, sampl), offset.yz); - vec4 sample2_v4 = texture(sampler2D(tex, sampl), offset.xw); - vec4 sample3_v4 = texture(sampler2D(tex, sampl), offset.yw); + vec4 sample0_v4 = textureLod(sampler2D(tex, sampl), offset.xz, 0); + vec4 sample1_v4 = textureLod(sampler2D(tex, sampl), offset.yz, 0); + vec4 sample2_v4 = textureLod(sampler2D(tex, sampl), offset.xw, 0); + vec4 sample3_v4 = textureLod(sampler2D(tex, sampl), offset.yw, 0); vec2 sample0 = sample0_v4.rb / 256.0 + sample0_v4.ga; vec2 sample1 = sample1_v4.rb / 256.0 + sample1_v4.ga; vec2 sample2 = sample2_v4.rb / 256.0 + sample2_v4.ga; diff --git a/assets/voxygen/shaders/include/shadows.glsl b/assets/voxygen/shaders/include/shadows.glsl index bd65dd3b5c..25018a00e1 100644 --- a/assets/voxygen/shaders/include/shadows.glsl +++ b/assets/voxygen/shaders/include/shadows.glsl @@ -77,7 +77,7 @@ float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, / // currentDepth = -currentDepth * 0.5 + 0.5; - float visibility = texture(samplerCubeShadow(t_point_shadow_maps, s_point_shadow_maps), vec4(fragToLight, currentDepth));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/); + float visibility = textureGrad(samplerCubeShadow(t_point_shadow_maps, s_point_shadow_maps), vec4(fragToLight, currentDepth), vec3(0), vec3(0));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/); /* if (visibility == 1.0 || visibility == 0.0) { return visibility; } */ diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 1006fc5e7c..85bbe9eb0b 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -361,7 +361,7 @@ void main() { sin(lifetime * 2.0 + rand2) + sin(lifetime * 9.0 + rand5) * 0.3 ), vec3(raise), - vec4(vec3(5, 5, 1.1), 1), + vec4(vec3(10.3, 9, 1.5), 1), spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5) ); break; @@ -392,7 +392,7 @@ void main() { attr = Attr( spiral_motion(vec3(0, 0, rand3 + 1), spiral_radius, lifetime, abs(rand0), rand1 * 2 * PI) + vec3(0, 0, rand2), vec3(6 * abs(rand4) * (1 - slow_start(2)) * pow(spiral_radius / length(inst_dir), 0.5)), - vec4(vec3(0, 1.7, 0.7), 1), + vec4(vec3(0, 1.7, 0.7) * 3, 1), spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3) ); break; @@ -403,7 +403,7 @@ void main() { attr = Attr( spiral_motion(inst_dir, 0.3 * (floor(2 * rand0 + 0.5) - 0.5) * min(linear_scale(10), 1), lifetime / inst_lifespan, 10.0, inst_time), vec3((1.7 - 0.7 * abs(floor(2 * rand0 - 0.5) + 0.5)) * (1.5 + 0.5 * sin(tick.x * 10 - lifetime * 4))), - vec4(vec3(purple_col, green_col, 0.75 * purple_col), 1), + vec4(vec3(purple_col, green_col, 0.75 * purple_col) * 3, 1), spin_in_axis(inst_dir, tick.z) ); break; diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl index aeba45f350..17b90c0895 100644 --- a/assets/voxygen/shaders/postprocess-frag.glsl +++ b/assets/voxygen/shaders/postprocess-frag.glsl @@ -27,7 +27,6 @@ uniform texture2D t_src_color; layout(set = 1, binding = 1) uniform sampler s_src_color; - layout(location = 0) in vec2 uv; layout (std140, set = 1, binding = 2) @@ -36,6 +35,11 @@ uniform u_locals { mat4 view_mat_inv; }; +#ifdef BLOOM_FACTOR +layout(set = 1, binding = 3) +uniform texture2D t_src_bloom; +#endif + layout(location = 0) out vec4 tgt_color; vec3 rgb2hsv(vec3 c) { @@ -182,6 +186,16 @@ void main() { vec4 aa_color = aa_apply(t_src_color, s_src_color, uv * screen_res.xy, screen_res.xy); + // Bloom + #ifdef BLOOM_FACTOR + vec4 bloom = textureLod(sampler2D(t_src_bloom, s_src_color), uv, 0); + #if (BLOOM_UNIFORM_BLUR == false) + // divide by 4.0 to account for adding blurred layers together + bloom /= 4.0; + #endif + aa_color = mix(aa_color, bloom, BLOOM_FACTOR); + #endif + // Tonemapping float exposure_offset = 1.0; // Adding an in-code offset to gamma and exposure let us have more precise control over the game's look diff --git a/assets/voxygen/shaders/ui-frag.glsl b/assets/voxygen/shaders/ui-frag.glsl index d9010a2c90..549f60bfb3 100644 --- a/assets/voxygen/shaders/ui-frag.glsl +++ b/assets/voxygen/shaders/ui-frag.glsl @@ -21,11 +21,11 @@ layout(location = 0) out vec4 tgt_color; void main() { // Text if (f_mode == uint(0)) { - tgt_color = f_color * vec4(1.0, 1.0, 1.0, texture(sampler2D(t_tex, s_tex), f_uv).a); + tgt_color = f_color * vec4(1.0, 1.0, 1.0, textureLod(sampler2D(t_tex, s_tex), f_uv, 0).a); // Image // HACK: bit 0 is set for both ordinary and north-facing images. } else if ((f_mode & uint(1)) == uint(1)) { - tgt_color = f_color * texture(sampler2D(t_tex, s_tex), f_uv); + tgt_color = f_color * textureLod(sampler2D(t_tex, s_tex), f_uv, 0); // 2D Geometry } else if (f_mode == uint(2)) { tgt_color = f_color; diff --git a/assets/voxygen/voxel/biped_weapon_manifest.ron b/assets/voxygen/voxel/biped_weapon_manifest.ron index 74cca7017f..ffc7128cf7 100644 --- a/assets/voxygen/voxel/biped_weapon_manifest.ron +++ b/assets/voxygen/voxel/biped_weapon_manifest.ron @@ -224,6 +224,10 @@ vox_spec: ("weapon.sword_1h.iron-4", (-2.0, -4.5, -3.0)), color: None ), + "common.items.weapons.sword_1h.starter": ( + vox_spec: ("weapon.sword_1h.starter", (-2.0, -4.5, -3.0)), + color: None + ), "common.items.weapons.sword_1h.obsidian-0": ( vox_spec: ("weapon.sword_1h.obsidian-0", (-2.0, -4.5, -3.0)), color: None diff --git a/assets/voxygen/voxel/weapon/sword_1h/starter.vox b/assets/voxygen/voxel/weapon/sword_1h/starter.vox new file mode 100644 index 0000000000..82917f17e9 --- /dev/null +++ b/assets/voxygen/voxel/weapon/sword_1h/starter.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcde6c25e14c7970ebb926528e0e0a2f715f200a6f88c65a2a29da90f62aa513 +size 1304 diff --git a/client/src/bin/bot/main.rs b/client/src/bin/bot/main.rs index c6a4f5b63a..d4362b9b12 100644 --- a/client/src/bin/bot/main.rs +++ b/client/src/bin/bot/main.rs @@ -174,6 +174,7 @@ impl BotClient { client.create_character( cred.username.clone(), Some("common.items.weapons.sword.starter".to_string()), + None, body.into(), ); client.load_character_list(); diff --git a/client/src/lib.rs b/client/src/lib.rs index 4a6a823c1e..018ca47a64 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -826,9 +826,20 @@ impl Client { } /// New character creation - pub fn create_character(&mut self, alias: String, tool: Option, body: comp::Body) { + pub fn create_character( + &mut self, + alias: String, + mainhand: Option, + offhand: Option, + body: comp::Body, + ) { self.character_list.loading = true; - self.send_msg(ClientGeneral::CreateCharacter { alias, tool, body }); + self.send_msg(ClientGeneral::CreateCharacter { + alias, + mainhand, + offhand, + body, + }); } /// Character deletion @@ -1220,7 +1231,7 @@ impl Client { .map(|cs| { matches!( cs, - comp::CharacterState::GlideWield | comp::CharacterState::Glide(_) + comp::CharacterState::GlideWield(_) | comp::CharacterState::Glide(_) ) }); diff --git a/common/Cargo.toml b/common/Cargo.toml index 98b2d0f288..c332b5f9eb 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -26,7 +26,8 @@ enum-iterator = "0.6" vek = { version = "=0.14.1", features = ["serde"] } # Strum -strum = "0.21" +strum = { version = "0.21", features = ["derive"] } +# TODO: remove this and rewrite every use of strum_macros to strum strum_macros = "0.21" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/common/assets/Cargo.toml b/common/assets/Cargo.toml index 4b46aecac6..ae7fb642c8 100644 --- a/common/assets/Cargo.toml +++ b/common/assets/Cargo.toml @@ -15,11 +15,10 @@ tracing = "0.1" # asset tweak serde = {version = "1.0", features = ["derive"], optional = true} -serial_test = {version = "0.5", optional = true} [dev-dependencies] walkdir = "2.3.2" [features] hot-reloading = ["assets_manager/hot-reloading"] -asset_tweak = ["serial_test", "serde", "hot-reloading"] +asset_tweak = ["serde", "hot-reloading"] diff --git a/common/assets/src/lib.rs b/common/assets/src/lib.rs index b23f27bb2f..fb21436cfa 100644 --- a/common/assets/src/lib.rs +++ b/common/assets/src/lib.rs @@ -305,11 +305,31 @@ mod tests { #[cfg(feature = "asset_tweak")] pub mod asset_tweak { - use super::{find_root, Asset, AssetExt, RonLoader}; + //! Set of functions and macros for easy tweaking values + //! using our asset cache machinery. + //! + //! Because of how macros works, you will not find + //! [tweak] and [tweak_from] macros in this module, + //! import it from [assets](super) crate directly. + //! + //! Will hot-reload (if corresponded feature is enabled). + // TODO: don't use the same ASSETS_PATH as game uses? + use super::{Asset, AssetExt, RonLoader, ASSETS_PATH}; use ron::ser::{to_writer_pretty, PrettyConfig}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::{fs, path::Path}; + /// Specifier to use with tweak functions in this module + /// + /// `Tweak("test")` will be interpreted as `/tweak/test.ron`. + /// + /// `Asset(&["path", "to", "file"])` will be interpreted as + /// `/path/to/file.ron` + pub enum Specifier<'a> { + Tweak(&'a str), + Asset(&'a [&'a str]), + } + #[derive(Clone, Deserialize, Serialize)] struct AssetTweakWrapper(T); @@ -322,93 +342,240 @@ pub mod asset_tweak { const EXTENSION: &'static str = "ron"; } - /// # Usage - /// Create file with content which represent tweaked value + /// Read value from file, will panic if file doesn't exist. /// - /// Example if you want to tweak integer value - /// ```no_run - /// use veloren_common_assets::asset_tweak; - /// let x: i32 = asset_tweak::tweak_expect("x"); - /// ``` - /// File needs to look like that - /// ```text - /// assets/tweak/x.ron - /// (5) - /// ``` - /// Note the parentheses. + /// If you don't have a file or its content is invalid, + /// this function will panic. + /// If you want to have some default content, + /// read documentation for [tweak_expect_or_create] for more. /// - /// # Panics - /// 1) If given `asset_specifier` does not exists - /// 2) If asseet is broken - pub fn tweak_expect(specifier: &str) -> T + /// # Examples: + /// How not to use. + /// ```should_panic + /// use veloren_common_assets::asset_tweak::{tweak_expect, Specifier}; + /// + /// // will panic if you don't have a file + /// let specifier = Specifier::Asset(&["no_way_we_have_this_directory", "x"]); + /// let x: i32 = tweak_expect(specifier); + /// ``` + /// + /// How to use. + /// ``` + /// use std::fs; + /// use veloren_common_assets::{ + /// asset_tweak::{tweak_expect, Specifier}, + /// ASSETS_PATH, + /// }; + /// + /// // you need to create file first + /// let tweak_path = ASSETS_PATH.join("tweak/year.ron"); + /// // note parentheses + /// fs::write(&tweak_path, b"(10)"); + /// + /// let y: i32 = tweak_expect(Specifier::Tweak("year")); + /// assert_eq!(y, 10); + /// + /// // Specifier::Tweak is just a shorthand + /// // for Specifier::Asset(&["tweak", ..]) + /// let y1: i32 = tweak_expect(Specifier::Asset(&["tweak", "year"])); + /// assert_eq!(y1, 10); + /// + /// // you may want to remove this file later + /// std::fs::remove_file(tweak_path); + /// ``` + pub fn tweak_expect(specifier: Specifier) -> T where T: Clone + Sized + Send + Sync + 'static + DeserializeOwned, { - let asset_specifier: &str = &format!("tweak.{}", specifier); - let handle = as AssetExt>::load_expect(asset_specifier); + let asset_specifier = match specifier { + Specifier::Tweak(specifier) => format!("tweak.{}", specifier), + Specifier::Asset(path) => path.join("."), + }; + let handle = as AssetExt>::load_expect(&asset_specifier); let AssetTweakWrapper(value) = handle.read().clone(); + value } - /// # Usage - /// Will create file "assets/tweak/{specifier}.ron" if not exists - /// and return passed `value`. - /// If file exists will read a value from such file. + // Helper function to create new file to tweak. + // + // The file will be filled with passed value + // returns passed value. + fn create_new(tweak_dir: &Path, filename: &str, value: T) -> T + where + T: Sized + Send + Sync + 'static + DeserializeOwned + Serialize, + { + fs::create_dir_all(tweak_dir).expect("failed to create directory for tweak files"); + let f = fs::File::create(tweak_dir.join(filename)).unwrap_or_else(|error| { + panic!("failed to create file {:?}. Error: {:?}", filename, error) + }); + let tweaker = AssetTweakWrapper(&value); + if let Err(e) = to_writer_pretty(f, &tweaker, PrettyConfig::new()) { + panic!("failed to write to file {:?}. Error: {:?}", filename, e); + } + + value + } + + // Helper function to get directory and file from asset list. + // + // Converts ["path", "to", "file"] to (String("path/to"), "file") + fn directory_and_name<'a>(path: &'a [&'a str]) -> (String, &'a str) { + let (file, path) = path.split_last().expect("empty asset list"); + let directory = path.join("/"); + + (directory, file) + } + + /// Read a value from asset, creating file if not exists. /// - /// In release builds (if `debug_assertions` == false) just returns passed - /// `value` + /// If file exists will read a value from such file + /// using [tweak_expect]. /// - /// Example if you want to tweak integer value - /// ```no_run - /// use veloren_common_assets::asset_tweak; - /// let x: i32 = asset_tweak::tweak_expect_or_create("x", 5); - /// ``` - /// File needs to look like that + /// File should look like that (note the parentheses). /// ```text /// assets/tweak/x.ron /// (5) /// ``` - /// Note the parentheses. /// - /// # Panics - /// 1) If asset is broken - /// 2) filesystem errors - pub fn tweak_expect_or_create(specifier: &str, value: T) -> T + /// # Example: + /// Tweaking integer value + /// ``` + /// use veloren_common_assets::{ + /// asset_tweak::{tweak_expect_or_create, Specifier}, + /// ASSETS_PATH, + /// }; + /// + /// // first time it will create the file + /// let x: i32 = tweak_expect_or_create(Specifier::Tweak("stars"), 5); + /// let file_path = ASSETS_PATH.join("tweak/stars.ron"); + /// assert!(file_path.is_file()); + /// assert_eq!(x, 5); + /// + /// // next time it will read value from file + /// // whatever you will pass as default + /// let x1: i32 = tweak_expect_or_create(Specifier::Tweak("stars"), 42); + /// assert_eq!(x1, 5); + /// + /// // you may want to remove this file later + /// std::fs::remove_file(file_path); + /// ``` + pub fn tweak_expect_or_create(specifier: Specifier, value: T) -> T where T: Clone + Sized + Send + Sync + 'static + DeserializeOwned + Serialize, { - if cfg!(not(debug_assertions)) { - return value; - } + let (dir, filename) = match specifier { + Specifier::Tweak(name) => (ASSETS_PATH.join("tweak"), format!("{}.ron", name)), + Specifier::Asset(list) => { + let (directory, name) = directory_and_name(list); + (ASSETS_PATH.join(directory), format!("{}.ron", name)) + }, + }; - let root = find_root().expect("failed to discover repository_root"); - let tweak_dir = root.join("assets/tweak/"); - let filename = format!("{}.ron", specifier); - - if Path::new(&tweak_dir.join(&filename)).is_file() { - let asset_specifier: &str = &format!("tweak.{}", specifier); - let handle = as AssetExt>::load_expect(asset_specifier); - let AssetTweakWrapper(new_value) = handle.read().clone(); - - new_value + if Path::new(&dir.join(&filename)).is_file() { + tweak_expect(specifier) } else { - fs::create_dir_all(&tweak_dir).expect("failed to create directory for tweak files"); - let f = fs::File::create(tweak_dir.join(&filename)).unwrap_or_else(|err| { - panic!("failed to create file {:?}. Error: {:?}", &filename, err) - }); - to_writer_pretty(f, &AssetTweakWrapper(value.clone()), PrettyConfig::new()) - .unwrap_or_else(|err| { - panic!("failed to write to file {:?}. Error: {:?}", &filename, err) - }); - - value + create_new(&dir, &filename, value) } } + /// Convinient macro to quickly tweak value. + /// + /// Will use [Specifier]`::Tweak` specifier and call + /// [tweak_expect] if passed only name + /// or [tweak_expect_or_create] if default is passed. + /// + /// # Examples: + /// ``` + /// // note that you need to export it from `assets` crate, + /// // not from `assets::asset_tweak` + /// use veloren_common_assets::{tweak, ASSETS_PATH}; + /// + /// // you need to create file first + /// let own_path = ASSETS_PATH.join("tweak/grizelda.ron"); + /// // note parentheses + /// std::fs::write(&own_path, b"(10)"); + /// + /// let z: i32 = tweak!("grizelda"); + /// assert_eq!(z, 10); + /// + /// // voila, you don't need to care about creating file first + /// let p: i32 = tweak!("peter", 8); + /// + /// let created_path = ASSETS_PATH.join("tweak/peter.ron"); + /// assert!(created_path.is_file()); + /// assert_eq!(p, 8); + /// + /// // will use default value only first time + /// // if file exists, will load from this file + /// let p: i32 = tweak!("peter", 50); + /// assert_eq!(p, 8); + /// + /// // you may want to remove this file later + /// std::fs::remove_file(own_path); + /// std::fs::remove_file(created_path); + /// ``` + #[macro_export] + macro_rules! tweak { + ($name:literal) => {{ + use $crate::asset_tweak::{tweak_expect, Specifier::Tweak}; + + tweak_expect(Tweak($name)) + }}; + + ($name:literal, $default:expr) => {{ + use $crate::asset_tweak::{tweak_expect_or_create, Specifier::Tweak}; + + tweak_expect_or_create(Tweak($name), $default) + }}; + } + + /// Convinient macro to quickly tweak value from some existing path. + /// + /// Will use [Specifier]`::Asset` specifier and call + /// [tweak_expect] if passed only name + /// or [tweak_expect_or_create] if default is passed. + /// + /// The main use case is when you have some object + /// which needs constant tuning of values, but you can't afford + /// loading a file. + /// So you can use tweak_from! and then just copy values from asset + /// to your object. + /// + /// # Examples: + /// ```no_run + /// // note that you need to export it from `assets` crate, + /// // not from `assets::asset_tweak` + /// use serde::{Deserialize, Serialize}; + /// use veloren_common_assets::{tweak_from, ASSETS_PATH}; + /// + /// #[derive(Clone, PartialEq, Deserialize, Serialize)] + /// struct Data { + /// x: i32, + /// y: i32, + /// } + /// + /// let default = Data { x: 5, y: 7 }; + /// let data: Data = tweak_from!(&["common", "body", "dimensions"], default); + /// ``` + #[macro_export] + macro_rules! tweak_from { + ($path:expr) => {{ + use $crate::asset_tweak::{tweak_expect, Specifier::Asset}; + + tweak_expect(Asset($path)) + }}; + + ($path:expr, $default:expr) => {{ + use $crate::asset_tweak::{tweak_expect_or_create, Specifier::Asset}; + + tweak_expect_or_create(Asset($path), $default) + }}; + } + #[cfg(test)] mod tests { - use super::{find_root, tweak_expect, tweak_expect_or_create}; - use serial_test::serial; + use super::*; use std::{ convert::AsRef, fmt::Debug, @@ -466,90 +633,160 @@ pub mod asset_tweak { P: AsRef + Debug, { fn drop(&mut self) { - fs::remove_file(&self.file) - .unwrap_or_else(|_| panic!("failed to create file {:?}", &self.file)); + fs::remove_file(&self.file).unwrap_or_else(|e| { + panic!("failed to remove file {:?}. Error: {:?}", &self.file, e) + }); } } - #[test] - #[serial] - fn test_tweaked_string() { - let root = find_root().expect("failed to discover repository_root"); - let tweak_dir = root.join("assets/tweak/"); - let _dir_guard = DirectoryGuard::create(tweak_dir.clone()); + // helper function to create environment with needed directory and file + // and responsible for cleaning + fn run_with_file(tweak_path: &[&str], test: impl Fn(&mut File)) { + let (tweak_dir, tweak_name) = directory_and_name(tweak_path); + let tweak_folder = ASSETS_PATH.join(tweak_dir); + let tweak_file = tweak_folder.join(format!("{}.ron", tweak_name)); - // define test files - let from_int = tweak_dir.join("__test_int_tweak.ron"); - let from_string = tweak_dir.join("__test_string_tweak.ron"); - let from_map = tweak_dir.join("__test_map_tweak.ron"); + let _dir_guard = DirectoryGuard::create(tweak_folder); + let (_file_guard, mut file) = FileGuard::create(tweak_file); - // setup fs guards - let (_file_guard1, mut file1) = FileGuard::create(from_int); - let (_file_guard2, mut file2) = FileGuard::create(from_string); - let (_file_guard3, mut file3) = FileGuard::create(from_map); - - // write to file and check result - file1 - .write_all(b"(5)") - .expect("failed to write to the file"); - let x = tweak_expect::("__test_int_tweak"); - assert_eq!(x, 5); - - // write to file and check result - file2 - .write_all(br#"("Hello Zest")"#) - .expect("failed to write to the file"); - let x = tweak_expect::("__test_string_tweak"); - assert_eq!(x, "Hello Zest".to_owned()); - - // write to file and check result - file3 - .write_all( - br#" - ({ - "wow": 4, - "such": 5, - }) - "#, - ) - .expect("failed to write to the file"); - let x: std::collections::HashMap = tweak_expect("__test_map_tweak"); - let mut map = std::collections::HashMap::new(); - map.insert("wow".to_owned(), 4); - map.insert("such".to_owned(), 5); - assert_eq!(x, map); + test(&mut file); } #[test] - #[serial] - fn test_tweaked_create() { - let root = find_root().expect("failed to discover repository_root"); - let tweak_dir = root.join("assets/tweak/"); + fn test_tweaked_int() { + let tweak_path = &["tweak_test_int", "tweak"]; - let test_path1 = tweak_dir.join("__test_int_create.ron"); - let _file_guard1 = FileGuard::hold(&test_path1); - let x = tweak_expect_or_create("__test_int_create", 5); - assert_eq!(x, 5); - assert!(test_path1.is_file()); - // Recheck it loads back correctly - let x = tweak_expect_or_create("__test_int_create", 5); - assert_eq!(x, 5); + run_with_file(tweak_path, |file| { + file.write_all(b"(5)").expect("failed to write to the file"); + let x: i32 = tweak_expect(Specifier::Asset(tweak_path)); + assert_eq!(x, 5); + }); + } - let test_path2 = tweak_dir.join("__test_tuple_create.ron"); - let _file_guard2 = FileGuard::hold(&test_path2); - let (x, y, z) = tweak_expect_or_create("__test_tuple_create", (5.0, 6.0, 7.0)); - assert_eq!((x, y, z), (5.0, 6.0, 7.0)); - // Recheck it loads back correctly - let (x, y, z) = tweak_expect_or_create("__test_tuple_create", (5.0, 6.0, 7.0)); - assert_eq!((x, y, z), (5.0, 6.0, 7.0)); + #[test] + fn test_tweaked_string() { + let tweak_path = &["tweak_test_string", "tweak"]; - // Test that file has stronger priority - let test_path3 = tweak_dir.join("__test_priority.ron"); - let (_file_guard3, mut file) = FileGuard::create(&test_path3); - file.write_all(b"(10)") + run_with_file(tweak_path, |file| { + file.write_all(br#"("Hello Zest")"#) + .expect("failed to write to the file"); + + let x: String = tweak_expect(Specifier::Asset(tweak_path)); + assert_eq!(x, "Hello Zest".to_owned()); + }); + } + + #[test] + fn test_tweaked_hashmap() { + type Map = std::collections::HashMap; + + let tweak_path = &["tweak_test_map", "tweak"]; + + run_with_file(tweak_path, |file| { + file.write_all( + br#" + ({ + "wow": 4, + "such": 5, + }) + "#, + ) .expect("failed to write to the file"); - let x = tweak_expect_or_create("__test_priority", 6); - assert_eq!(x, 10); + + let x: Map = tweak_expect(Specifier::Asset(tweak_path)); + + let mut map = Map::new(); + map.insert("wow".to_owned(), 4); + map.insert("such".to_owned(), 5); + assert_eq!(x, map); + }); + } + + #[test] + fn test_tweaked_with_macro_struct() { + // partial eq and debug because of assert_eq in this test + #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] + struct Wow { + such: i32, + field: f32, + } + + let tweak_path = &["tweak_test_struct", "tweak"]; + + run_with_file(tweak_path, |file| { + file.write_all( + br#" + (( + such: 5, + field: 35.752346, + )) + "#, + ) + .expect("failed to write to the file"); + + let x: Wow = crate::tweak_from!(tweak_path); + let expected = Wow { + such: 5, + field: 35.752_346, + }; + assert_eq!(x, expected); + }); + } + + fn run_with_path(tweak_path: &[&str], test: impl Fn(&Path)) { + let (tweak_dir, tweak_name) = directory_and_name(tweak_path); + + let tweak_folder = ASSETS_PATH.join(tweak_dir); + let test_path = tweak_folder.join(format!("{}.ron", tweak_name)); + + let _file_guard = FileGuard::hold(&test_path); + + test(&test_path); + } + + #[test] + fn test_create_tweak() { + let tweak_path = &["tweak_create_test", "tweak"]; + + run_with_path(tweak_path, |test_path| { + let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5); + assert_eq!(x, 5); + assert!(test_path.is_file()); + // Recheck it loads back correctly + let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5); + assert_eq!(x, 5); + }); + } + + #[test] + fn test_create_tweak_deep() { + let tweak_path = &["so_much", "deep_test", "tweak_create_test", "tweak"]; + + run_with_path(tweak_path, |test_path| { + let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5); + assert_eq!(x, 5); + assert!(test_path.is_file()); + // Recheck it loads back correctly + let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5); + assert_eq!(x, 5); + }); + } + + #[test] + fn test_create_but_prioritize_loaded() { + let tweak_path = &["tweak_create_and_prioritize_test", "tweak"]; + + run_with_path(tweak_path, |test_path| { + let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5); + assert_eq!(x, 5); + assert!(test_path.is_file()); + + // Recheck it loads back + // with content as priority + fs::write(test_path, b"(10)").expect("failed to write to the file"); + let x = tweak_expect_or_create(Specifier::Asset(tweak_path), 5); + assert_eq!(x, 10); + }); } } } diff --git a/common/frontend/src/lib.rs b/common/frontend/src/lib.rs index cf3a72766b..6b78cf750e 100644 --- a/common/frontend/src/lib.rs +++ b/common/frontend/src/lib.rs @@ -39,7 +39,10 @@ where { // To hold the guards that we create, they will cause the logs to be // flushed when they're dropped. - let mut _guards: Vec = vec![]; + #[cfg(not(feature = "tracy"))] + let mut guards: Vec = Vec::new(); + #[cfg(feature = "tracy")] + let guards: Vec = Vec::new(); // We will do lower logging than the default (INFO) by INCLUSION. This // means that if you need lower level logging for a specific module, then @@ -100,8 +103,8 @@ where let registry = registry.with(tracing_tracy::TracyLayer::new().with_stackdepth(0)); #[cfg(not(feature = "tracy"))] let registry = { - let (non_blocking, _stdio_guard) = tracing_appender::non_blocking(terminal.make_writer()); - _guards.push(_stdio_guard); + let (non_blocking, stdio_guard) = tracing_appender::non_blocking(terminal.make_writer()); + guards.push(stdio_guard); registry.with(tracing_subscriber::fmt::layer().with_writer(non_blocking)) }; @@ -111,9 +114,8 @@ where match fs::create_dir_all(path) { Ok(_) => { let file_appender = tracing_appender::rolling::daily(path, file); - let (non_blocking_file, _file_guard) = - tracing_appender::non_blocking(file_appender); - _guards.push(_file_guard); + let (non_blocking_file, file_guard) = tracing_appender::non_blocking(file_appender); + guards.push(file_guard); file_setup = true; registry .with(tracing_subscriber::fmt::layer().with_writer(non_blocking_file)) @@ -146,7 +148,7 @@ where }; // Return the guards - _guards + guards } pub fn init_stdout(log_path_file: Option<(&Path, &str)>) -> Vec { diff --git a/common/net/src/msg/client.rs b/common/net/src/msg/client.rs index 0cc811ceb9..4f7b18ffa0 100644 --- a/common/net/src/msg/client.rs +++ b/common/net/src/msg/client.rs @@ -51,7 +51,8 @@ pub enum ClientGeneral { RequestCharacterList, CreateCharacter { alias: String, - tool: Option, + mainhand: Option, + offhand: Option, body: comp::Body, }, DeleteCharacter(CharacterId), diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 190444290e..7c1a2d6ebf 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -1,6 +1,7 @@ use crate::{ assets, - comp::{self, buff::BuffKind, AdminRole as Role, Skill}, + comp::{self, buff::BuffKind, inventory::item::try_all_item_defs, AdminRole as Role, Skill}, + generation::try_all_entity_configs, npc, terrain, }; use assets::AssetExt; @@ -9,7 +10,6 @@ use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use std::{ fmt::{self, Display}, - path::Path, str::FromStr, }; use strum::IntoEnumIterator; @@ -77,6 +77,7 @@ pub enum ChatCommand { Lantern, Light, MakeBlock, + MakeNpc, MakeSprite, Motd, Object, @@ -122,6 +123,9 @@ impl assets::Asset for SkillPresetManifest { const EXTENSION: &'static str = "ron"; } +pub const KIT_MANIFEST_PATH: &str = "server.manifests.kits"; +pub const PRESET_MANIFEST_PATH: &str = "server.manifests.presets"; + lazy_static! { static ref ALIGNMENTS: Vec = vec!["wild", "enemy", "npc", "pet"] .iter() @@ -230,30 +234,27 @@ lazy_static! { static ref ROLES: Vec = ["admin", "moderator"].iter().copied().map(Into::into).collect(); /// List of item specifiers. Useful for tab completing - static ref ITEM_SPECS: Vec = { - let path = assets::ASSETS_PATH.join("common").join("items"); - let mut items = vec![]; - fn list_items (path: &Path, base: &Path, mut items: &mut Vec) -> std::io::Result<()>{ - for entry in std::fs::read_dir(path)? { - let path = entry?.path(); - if path.is_dir(){ - list_items(&path, base, &mut items)?; - } else if let Ok(path) = path.strip_prefix(base) { - let path = path.to_string_lossy().trim_end_matches(".ron").replace('/', ".").replace('\\', "."); - items.push(path); - } - } - Ok(()) - } - if list_items(&path, &assets::ASSETS_PATH, &mut items).is_err() { - warn!("There was a problem listing item assets"); - } + pub static ref ITEM_SPECS: Vec = { + let mut items = try_all_item_defs() + .unwrap_or_else(|e| { + warn!(?e, "Failed to load item specifiers"); + Vec::new() + }); items.sort(); items }; + /// List of all entity configs. Useful for tab completing + static ref ENTITY_CONFIGS: Vec = { + try_all_entity_configs() + .unwrap_or_else(|e| { + warn!(?e, "Failed to load entity configs"); + Vec::new() + }) + }; + static ref KITS: Vec = { - if let Ok(kits) = KitManifest::load("server.manifests.kits") { + if let Ok(kits) = KitManifest::load(KIT_MANIFEST_PATH) { kits.read().0.keys().cloned().collect() } else { Vec::new() @@ -261,7 +262,7 @@ lazy_static! { }; static ref PRESETS: HashMap> = { - if let Ok(presets) = SkillPresetManifest::load("server.manifests.presets") { + if let Ok(presets) = SkillPresetManifest::load(PRESET_MANIFEST_PATH) { presets.read().0.clone() } else { warn!("Error while loading presets"); @@ -462,8 +463,21 @@ impl ChatCommand { Some(Admin), ), ChatCommand::MakeBlock => cmd( - vec![Enum("block", BLOCK_KINDS.clone(), Required)], - "Make a block at your location", + vec![ + Enum("block", BLOCK_KINDS.clone(), Required), + Integer("r", 255, Optional), + Integer("g", 255, Optional), + Integer("b", 255, Optional), + ], + "Make a block at your location with a color", + Some(Admin), + ), + ChatCommand::MakeNpc => cmd( + vec![ + Enum("entity_config", ENTITY_CONFIGS.clone(), Required), + Integer("num", 1, Optional), + ], + "Spawn entity from config near you", Some(Admin), ), ChatCommand::MakeSprite => cmd( @@ -526,8 +540,8 @@ impl ChatCommand { "Set the server description", Some(Admin), ), - // Uses Message because site names can contain spaces, which would be assumed to be - // separators otherwise + // Uses Message because site names can contain spaces, + // which would be assumed to be separators otherwise ChatCommand::Site => cmd( vec![Message(Required)], "Teleport to a site", @@ -639,6 +653,7 @@ impl ChatCommand { ChatCommand::Lantern => "lantern", ChatCommand::Light => "light", ChatCommand::MakeBlock => "make_block", + ChatCommand::MakeNpc => "make_npc", ChatCommand::MakeSprite => "make_sprite", ChatCommand::Motd => "motd", ChatCommand::Object => "object", @@ -793,7 +808,6 @@ pub enum ArgumentSpec { } impl ArgumentSpec { - #[allow(clippy::nonstandard_macro_braces)] //tmp as of false positive !? pub fn usage_string(&self) -> String { match self { ArgumentSpec::PlayerName(req) => { @@ -841,9 +855,9 @@ impl ArgumentSpec { ArgumentSpec::SubCommand => "<[/]command> [args...]".to_string(), ArgumentSpec::Enum(label, _, req) => { if &Requirement::Required == req { - format! {"<{}>", label} + format!("<{}>", label) } else { - format! {"[{}]", label} + format!("[{}]", label) } }, ArgumentSpec::Boolean(label, _, req) => { @@ -860,9 +874,18 @@ impl ArgumentSpec { #[cfg(test)] mod tests { use super::*; + use crate::comp::Item; #[test] - fn test_loading_skill_presets() { - SkillPresetManifest::load_expect("server.manifests.presets"); + fn test_loading_skill_presets() { SkillPresetManifest::load_expect(PRESET_MANIFEST_PATH); } + + #[test] + fn test_load_kits() { + let kits = KitManifest::load_expect(KIT_MANIFEST_PATH).read(); + for kit in kits.0.values() { + for (item_id, _) in kit.iter() { + std::mem::drop(Item::new_from_asset_expect(item_id)); + } + } } } diff --git a/common/src/combat.rs b/common/src/combat.rs index 1bd42c2389..56f8b12dfa 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -13,7 +13,7 @@ use crate::{ poise::PoiseChange, skills::SkillGroupKind, Body, CharacterState, Combo, Energy, EnergyChange, EnergySource, Health, HealthChange, - HealthSource, Inventory, Ori, SkillSet, Stats, + HealthSource, Inventory, Ori, Player, SkillSet, Stats, }, event::ServerEvent, outcome::Outcome, @@ -71,6 +71,13 @@ pub struct TargetInfo<'a> { pub char_state: Option<&'a CharacterState>, } +#[derive(Clone, Copy)] +pub struct AttackOptions { + pub target_dodging: bool, + pub may_harm: bool, + pub target_group: GroupTarget, +} + #[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Debug, Serialize, Deserialize)] // TODO: Yeet clone derive pub struct Attack { @@ -161,25 +168,44 @@ impl Attack { #[allow(clippy::too_many_arguments)] pub fn apply_attack( &self, - target_group: GroupTarget, attacker: Option, target: TargetInfo, dir: Dir, - target_dodging: bool, - // Currently just modifies damage, maybe look into modifying strength of other effects? + options: AttackOptions, + // Currently strength_modifier just modifies damage, + // maybe look into modifying strength of other effects? strength_modifier: f32, attack_source: AttackSource, mut emit: impl FnMut(ServerEvent), mut emit_outcome: impl FnMut(Outcome), ) -> bool { - let mut is_applied = false; + let AttackOptions { + target_dodging, + may_harm, + target_group, + } = options; + + // target == OutOfGroup is basic heuristic that this + // "attack" has negative effects. + // + // so if target dodges this "attack" or we don't want to harm target, + // it should avoid such "damage" or effect + let avoid_damage = |attack_damage: &AttackDamage| { + matches!(attack_damage.target, Some(GroupTarget::OutOfGroup)) + && (target_dodging || !may_harm) + }; + let avoid_effect = |attack_effect: &AttackEffect| { + matches!(attack_effect.target, Some(GroupTarget::OutOfGroup)) + && (target_dodging || !may_harm) + }; let is_crit = thread_rng().gen::() < self.crit_chance; + let mut is_applied = false; let mut accumulated_damage = 0.0; for damage in self .damages .iter() .filter(|d| d.target.map_or(true, |t| t == target_group)) - .filter(|d| !(matches!(d.target, Some(GroupTarget::OutOfGroup)) && target_dodging)) + .filter(|d| !avoid_damage(d)) { is_applied = true; let damage_reduction = Attack::compute_damage_reduction( @@ -294,7 +320,7 @@ impl Attack { .effects .iter() .filter(|e| e.target.map_or(true, |t| t == target_group)) - .filter(|e| !(matches!(e.target, Some(GroupTarget::OutOfGroup)) && target_dodging)) + .filter(|e| !avoid_effect(e)) { if effect.requirements.iter().all(|req| match req { CombatRequirement::AnyDamage => accumulated_damage > 0.0 && target.health.is_some(), @@ -430,6 +456,17 @@ impl Attack { } } +/// Checks if we should allow negative effects from one player to another +// FIXME: handle pets? +// This code works only with players. +// You still can kill someone's pet and +// you still can be killed by someone's pet +pub fn may_harm(attacker: Option<&Player>, target: Option<&Player>) -> bool { + attacker + .zip(target) + .map_or(true, |(attacker, target)| attacker.may_harm(target)) +} + #[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Debug, Serialize, Deserialize)] pub struct AttackDamage { diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 98f5935bc7..1415a74461 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -119,6 +119,7 @@ pub enum CharacterAbility { charge_duration: f32, swing_duration: f32, recover_duration: f32, + ori_modifier: f32, charge_through: bool, is_interruptible: bool, damage_kind: DamageKind, @@ -251,7 +252,6 @@ pub enum CharacterAbility { damage_effect: Option, energy_regen: f32, energy_drain: f32, - orientation_behavior: basic_beam::OrientationBehavior, ori_rate: f32, specifier: beam::FrontendSpecifier, }, @@ -498,6 +498,7 @@ impl CharacterAbility { charge_duration: _, ref mut swing_duration, ref mut recover_duration, + ori_modifier: _, charge_through: _, is_interruptible: _, damage_kind: _, @@ -745,7 +746,6 @@ impl CharacterAbility { ref mut damage_effect, energy_regen: _, ref mut energy_drain, - orientation_behavior: _, ori_rate: _, specifier: _, } => { @@ -1021,9 +1021,9 @@ impl CharacterAbility { 0 }; *max_energy_gain = *max_energy_gain - * ((energy_level + 1) * stage_data.len() as u16 - 1) as f32 + * ((energy_level + 1) * stage_data.len() as u16 - 1).max(1) as f32 / (Axe(DsRegen).max_level().unwrap() + 1) as f32 - * (stage_data.len() - 1) as f32; + * (stage_data.len() - 1).max(1) as f32; *scales_from_combo = skillset .skill_level(Axe(DsDamage)) .unwrap_or(None) @@ -1533,6 +1533,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { charge_duration, swing_duration, recover_duration, + ori_modifier, charge_through, is_interruptible, damage_kind, @@ -1554,6 +1555,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { charge_duration: Duration::from_secs_f32(*charge_duration), swing_duration: Duration::from_secs_f32(*swing_duration), recover_duration: Duration::from_secs_f32(*recover_duration), + ori_modifier: *ori_modifier, is_interruptible: *is_interruptible, damage_effect: *damage_effect, ability_info, @@ -1878,7 +1880,6 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { damage_effect, energy_regen, energy_drain, - orientation_behavior, ori_rate, specifier, } => CharacterState::BasicBeam(basic_beam::Data { @@ -1894,7 +1895,6 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { energy_regen: *energy_regen, energy_drain: *energy_drain, ability_info, - orientation_behavior: *orientation_behavior, ori_rate: *ori_rate, specifier: *specifier, }, diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index ba42292175..72cd5bc641 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -9,13 +9,14 @@ use serde::Deserialize; use specs::{Component, Entity as EcsEntity}; use specs_idvs::IdvStorage; use std::{collections::VecDeque, fmt}; +use strum::IntoEnumIterator; +use strum_macros::EnumIter; use vek::*; use super::dialogue::Subject; -pub const DEFAULT_INTERACTION_TIME: f32 = 1.0; +pub const DEFAULT_INTERACTION_TIME: f32 = 3.0; pub const TRADE_INTERACTION_TIME: f32 = 300.0; -pub const MAX_LISTEN_DIST: f32 = 100.0; #[derive(Copy, Clone, Debug, PartialEq, Deserialize)] pub enum Alignment { @@ -43,6 +44,8 @@ impl Alignment { // Always attacks pub fn hostile_towards(self, other: Alignment) -> bool { match (self, other) { + (Alignment::Passive, _) => false, + (_, Alignment::Passive) => false, (Alignment::Enemy, Alignment::Enemy) => false, (Alignment::Enemy, Alignment::Wild) => false, (Alignment::Wild, Alignment::Enemy) => false, @@ -167,85 +170,107 @@ impl Behavior { #[derive(Clone, Debug, Default)] pub struct Psyche { - pub aggro: f32, // 0.0 = always flees, 1.0 = always attacks, 0.5 = flee at 50% health + /// The proportion of health below which entities will start fleeing. + /// 0.0 = never flees, 1.0 = always flees, 0.5 = flee at 50% health. + pub flee_health: f32, + /// The distance below which the agent will see enemies if it has line of + /// sight. + pub sight_dist: f32, + /// The distance below which the agent can hear enemies without seeing them. + pub listen_dist: f32, + /// The distance below which the agent will attack enemies. Should be lower + /// than `sight_dist`. `None` implied that the agent is always aggro + /// towards enemies that it is aware of. + pub aggro_dist: Option, } impl<'a> From<&'a Body> for Psyche { fn from(body: &'a Body) -> Self { Self { - aggro: match body { + flee_health: match body { Body::Humanoid(humanoid) => match humanoid.species { - humanoid::Species::Danari => 0.9, - humanoid::Species::Dwarf => 0.8, - humanoid::Species::Elf => 0.7, - humanoid::Species::Human => 0.6, - humanoid::Species::Orc => 0.9, - humanoid::Species::Undead => 0.9, + humanoid::Species::Danari => 0.1, + humanoid::Species::Dwarf => 0.2, + humanoid::Species::Elf => 0.3, + humanoid::Species::Human => 0.4, + humanoid::Species::Orc => 0.1, + humanoid::Species::Undead => 0.1, }, Body::QuadrupedSmall(quadruped_small) => match quadruped_small.species { quadruped_small::Species::Pig => 0.5, - quadruped_small::Species::Fox => 0.3, + quadruped_small::Species::Fox => 0.7, quadruped_small::Species::Sheep => 0.5, - quadruped_small::Species::Boar => 0.8, - quadruped_small::Species::Jackalope => 0.4, - quadruped_small::Species::Skunk => 0.6, - quadruped_small::Species::Cat => 0.2, - quadruped_small::Species::Batfox => 0.6, - quadruped_small::Species::Raccoon => 0.4, - quadruped_small::Species::Quokka => 0.4, - quadruped_small::Species::Dodarock => 0.9, - quadruped_small::Species::Holladon => 1.0, - quadruped_small::Species::Hyena => 0.4, - quadruped_small::Species::Rabbit => 0.1, - quadruped_small::Species::Truffler => 0.8, - quadruped_small::Species::Frog => 0.4, - quadruped_small::Species::Hare => 0.2, + quadruped_small::Species::Boar => 0.2, + quadruped_small::Species::Jackalope => 0.6, + quadruped_small::Species::Skunk => 0.4, + quadruped_small::Species::Cat => 0.8, + quadruped_small::Species::Batfox => 0.4, + quadruped_small::Species::Raccoon => 0.6, + quadruped_small::Species::Quokka => 0.6, + quadruped_small::Species::Dodarock => 0.1, + quadruped_small::Species::Holladon => 0.0, + quadruped_small::Species::Hyena => 0.6, + quadruped_small::Species::Rabbit => 0.9, + quadruped_small::Species::Truffler => 0.2, + quadruped_small::Species::Frog => 0.6, + quadruped_small::Species::Hare => 0.8, quadruped_small::Species::Goat => 0.5, - _ => 0.0, + _ => 1.0, }, Body::QuadrupedMedium(quadruped_medium) => match quadruped_medium.species { - quadruped_medium::Species::Tuskram => 0.7, - quadruped_medium::Species::Frostfang => 0.9, - quadruped_medium::Species::Mouflon => 0.7, - quadruped_medium::Species::Catoblepas => 0.8, - quadruped_medium::Species::Deer => 0.6, - quadruped_medium::Species::Hirdrasil => 0.7, - quadruped_medium::Species::Donkey => 0.7, - quadruped_medium::Species::Camel => 0.7, - quadruped_medium::Species::Zebra => 0.7, - quadruped_medium::Species::Antelope => 0.6, - quadruped_medium::Species::Horse => 0.7, - quadruped_medium::Species::Cattle => 0.7, - quadruped_medium::Species::Darkhound => 0.9, - quadruped_medium::Species::Dreadhorn => 0.8, - quadruped_medium::Species::Snowleopard => 0.7, - quadruped_medium::Species::Llama => 0.6, - quadruped_medium::Species::Alpaca => 0.6, + quadruped_medium::Species::Tuskram => 0.3, + quadruped_medium::Species::Frostfang => 0.1, + quadruped_medium::Species::Mouflon => 0.3, + quadruped_medium::Species::Catoblepas => 0.2, + quadruped_medium::Species::Deer => 0.4, + quadruped_medium::Species::Hirdrasil => 0.3, + quadruped_medium::Species::Donkey => 0.3, + quadruped_medium::Species::Camel => 0.3, + quadruped_medium::Species::Zebra => 0.3, + quadruped_medium::Species::Antelope => 0.4, + quadruped_medium::Species::Horse => 0.3, + quadruped_medium::Species::Cattle => 0.3, + quadruped_medium::Species::Darkhound => 0.1, + quadruped_medium::Species::Dreadhorn => 0.2, + quadruped_medium::Species::Snowleopard => 0.3, + quadruped_medium::Species::Llama => 0.4, + quadruped_medium::Species::Alpaca => 0.4, _ => 0.5, }, Body::QuadrupedLow(quadruped_low) => match quadruped_low.species { - quadruped_low::Species::Salamander => 0.7, - quadruped_low::Species::Monitor => 0.7, - quadruped_low::Species::Asp => 0.9, - quadruped_low::Species::Pangolin => 0.4, - _ => 0.6, + quadruped_low::Species::Salamander => 0.3, + quadruped_low::Species::Monitor => 0.3, + quadruped_low::Species::Asp => 0.1, + quadruped_low::Species::Pangolin => 0.6, + _ => 0.4, }, Body::BipedSmall(_) => 0.5, Body::BirdMedium(_) => 0.5, - Body::BirdLarge(_) => 0.9, - Body::FishMedium(_) => 0.15, - Body::FishSmall(_) => 0.0, - Body::BipedLarge(_) => 1.0, - Body::Object(_) => 1.0, - Body::Golem(_) => 1.0, - Body::Theropod(_) => 1.0, - Body::Dragon(_) => 1.0, - Body::Ship(_) => 1.0, + Body::BirdLarge(_) => 0.1, + Body::FishMedium(_) => 0.85, + Body::FishSmall(_) => 1.0, + Body::BipedLarge(_) => 0.0, + Body::Object(_) => 0.0, + Body::Golem(_) => 0.0, + Body::Theropod(_) => 0.0, + Body::Dragon(_) => 0.0, + Body::Ship(_) => 0.0, + }, + sight_dist: 40.0, + listen_dist: 30.0, + aggro_dist: match body { + Body::Humanoid(_) => Some(20.0), + _ => None, // Always aggressive if detected }, } } } +impl Psyche { + /// The maximum distance that targets might be detected by this agent. + pub fn search_dist(&self) -> f32 { self.sight_dist.max(self.listen_dist) } +} + #[derive(Clone, Debug)] /// Events that affect agent behavior from other entities/players/environment pub enum AgentEvent { @@ -309,12 +334,98 @@ pub enum SoundKind { #[derive(Clone, Copy, Debug)] pub struct Target { pub target: EcsEntity, + /// Whether the target is hostile pub hostile: bool, + /// The time at which the target was selected pub selected_at: f64, + /// Whether the target has come close enough to trigger aggro. + pub aggro_on: bool, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, EnumIter)] +pub enum TimerAction { + Interact, +} + +/// A time used for managing agent-related timeouts. The timer is designed to +/// keep track of the start of any number of previous actions. However, +/// starting/progressing an action will end previous actions. Therefore, the +/// timer should be used for actions that are mutually-exclusive. +#[derive(Clone, Debug)] +pub struct Timer { + action_starts: Vec>, + last_action: Option, +} + +impl Default for Timer { + fn default() -> Self { + Self { + action_starts: TimerAction::iter().map(|_| None).collect(), + last_action: None, + } + } +} + +impl Timer { + fn idx_for(action: TimerAction) -> usize { + TimerAction::iter() + .enumerate() + .find(|(_, a)| a == &action) + .unwrap() + .0 // Can't fail, EnumIter is exhaustive + } + + /// Reset the timer for the given action, returning true if the timer was + /// not already reset. + pub fn reset(&mut self, action: TimerAction) -> bool { + std::mem::replace(&mut self.action_starts[Self::idx_for(action)], None).is_some() + } + + /// Start the timer for the given action, even if it was already started. + pub fn start(&mut self, time: f64, action: TimerAction) { + self.action_starts[Self::idx_for(action)] = Some(time); + self.last_action = Some(action); + } + + /// Continue timing the given action, starting it if it was not already + /// started. + pub fn progress(&mut self, time: f64, action: TimerAction) { + if self.last_action != Some(action) { + self.start(time, action); + } + } + + /// Return the time that the given action was last performed at. + pub fn time_of_last(&self, action: TimerAction) -> Option { + self.action_starts[Self::idx_for(action)] + } + + /// Return `true` if the time since the action was last started exceeds the + /// given timeout. + pub fn time_since_exceeds(&self, time: f64, action: TimerAction, timeout: f64) -> bool { + self.time_of_last(action) + .map_or(true, |last_time| (time - last_time).max(0.0) > timeout) + } + + /// Return `true` while the time since the action was last started is less + /// than the given period. Once the time has elapsed, reset the timer. + pub fn timeout_elapsed( + &mut self, + time: f64, + action: TimerAction, + timeout: f64, + ) -> Option { + if self.time_since_exceeds(time, action, timeout) { + Some(self.reset(action)) + } else { + self.progress(time, action); + None + } + } } #[allow(clippy::type_complexity)] -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct Agent { pub rtsim_controller: RtSimController, pub patrol_origin: Option>, @@ -324,6 +435,7 @@ pub struct Agent { pub psyche: Psyche, pub inbox: VecDeque, pub action_state: ActionState, + pub timer: Timer, pub bearing: Vec2, pub sounds_heard: Vec, pub awareness: f32, @@ -339,13 +451,46 @@ pub struct ActionState { } impl Agent { + pub fn from_body(body: &Body) -> Self { + Agent { + rtsim_controller: RtSimController::default(), + patrol_origin: None, + target: None, + chaser: Chaser::default(), + behavior: Behavior::default(), + psyche: Psyche::from(body), + inbox: VecDeque::new(), + action_state: ActionState::default(), + timer: Timer::default(), + bearing: Vec2::zero(), + sounds_heard: Vec::new(), + awareness: 0.0, + position_pid_controller: None, + } + } + pub fn with_patrol_origin(mut self, origin: Vec3) -> Self { self.patrol_origin = Some(origin); self } + pub fn with_behavior(mut self, behavior: Behavior) -> Self { + self.behavior = behavior; + self + } + + pub fn with_no_flee(mut self, no_flee: bool) -> Self { + if no_flee { + self.set_no_flee(); + } + self + } + + pub fn set_no_flee(&mut self) { self.psyche.flee_health = 0.0; } + + // TODO: Get rid of this method, it does weird things pub fn with_destination(mut self, pos: Vec3) -> Self { - self.psyche = Psyche { aggro: 1.0 }; + self.psyche.flee_health = 0.0; self.rtsim_controller = RtSimController::with_destination(pos); self.behavior.allow(BehaviorCapability::SPEAK); self @@ -359,24 +504,6 @@ impl Agent { self.position_pid_controller = Some(pid); self } - - pub fn new( - patrol_origin: Option>, - body: &Body, - behavior: Behavior, - no_flee: bool, - ) -> Self { - Agent { - patrol_origin, - psyche: if no_flee { - Psyche { aggro: 1.0 } - } else { - Psyche::from(body) - }, - behavior, - ..Default::default() - } - } } impl Component for Agent { diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 0e52fc50c8..33840b2a2c 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -205,20 +205,15 @@ impl Body { Body::FishSmall(_) => 1.0, Body::Golem(_) => 10_000.0, Body::Humanoid(humanoid) => { - match (humanoid.species, humanoid.body_type) { - (humanoid::Species::Orc, humanoid::BodyType::Male) => 120.0, - (humanoid::Species::Orc, humanoid::BodyType::Female) => 120.0, - (humanoid::Species::Human, humanoid::BodyType::Male) => 77.0, // ~✅ - (humanoid::Species::Human, humanoid::BodyType::Female) => 59.0, // ~✅ - (humanoid::Species::Elf, humanoid::BodyType::Male) => 77.0, - (humanoid::Species::Elf, humanoid::BodyType::Female) => 59.0, - (humanoid::Species::Dwarf, humanoid::BodyType::Male) => 70.0, - (humanoid::Species::Dwarf, humanoid::BodyType::Female) => 70.0, - (humanoid::Species::Undead, humanoid::BodyType::Male) => 70.0, - (humanoid::Species::Undead, humanoid::BodyType::Female) => 50.0, - (humanoid::Species::Danari, humanoid::BodyType::Male) => 80.0, - (humanoid::Species::Danari, humanoid::BodyType::Female) => 60.0, - } + // Understand that chaning the mass values can have effects + // on multiple systems. + // + // If you want to change that value, consult with + // Physics and Combat teams + // + // Weight is proportional height, where + // a 1.75m character would weigh 65kg + 65.0 * humanoid.height() / 1.75f32 }, Body::Object(obj) => obj.mass().0, Body::QuadrupedLow(body) => match body.species { @@ -332,21 +327,8 @@ impl Body { Body::FishSmall(_) => Vec3::new(0.3, 1.2, 0.6), Body::Golem(_) => Vec3::new(5.0, 5.0, 7.5), Body::Humanoid(humanoid) => { - let height = match (humanoid.species, humanoid.body_type) { - (humanoid::Species::Orc, humanoid::BodyType::Male) => 2.0, - (humanoid::Species::Orc, humanoid::BodyType::Female) => 1.9, - (humanoid::Species::Human, humanoid::BodyType::Male) => 1.8, - (humanoid::Species::Human, humanoid::BodyType::Female) => 1.7, - (humanoid::Species::Elf, humanoid::BodyType::Male) => 1.9, - (humanoid::Species::Elf, humanoid::BodyType::Female) => 1.8, - (humanoid::Species::Dwarf, humanoid::BodyType::Male) => 1.6, - (humanoid::Species::Dwarf, humanoid::BodyType::Female) => 1.5, - (humanoid::Species::Undead, humanoid::BodyType::Male) => 1.9, - (humanoid::Species::Undead, humanoid::BodyType::Female) => 1.8, - (humanoid::Species::Danari, humanoid::BodyType::Male) => 1.5, - (humanoid::Species::Danari, humanoid::BodyType::Female) => 1.4, - }; - Vec3::new(1.5, 0.5, height) + let height = humanoid.height(); + Vec3::new(height / 1.3, 1.75 / 2.0, height) }, Body::Object(object) => object.dimensions(), Body::QuadrupedMedium(body) => match body.species { diff --git a/common/src/comp/body/humanoid.rs b/common/src/comp/body/humanoid.rs index 54ce043f36..d165a26644 100644 --- a/common/src/comp/body/humanoid.rs +++ b/common/src/comp/body/humanoid.rs @@ -64,6 +64,25 @@ impl Body { .accessory .min(self.species.num_accessories(self.body_type) - 1); } + + pub fn height(&self) -> f32 { (20.0 / 9.0) * self.scaler() } + + pub fn scaler(&self) -> f32 { + match (self.species, self.body_type) { + (Species::Orc, BodyType::Male) => 0.91, + (Species::Orc, BodyType::Female) => 0.81, + (Species::Human, BodyType::Male) => 0.81, + (Species::Human, BodyType::Female) => 0.76, + (Species::Elf, BodyType::Male) => 0.82, + (Species::Elf, BodyType::Female) => 0.76, + (Species::Dwarf, BodyType::Male) => 0.67, + (Species::Dwarf, BodyType::Female) => 0.62, + (Species::Undead, BodyType::Male) => 0.78, + (Species::Undead, BodyType::Female) => 0.72, + (Species::Danari, BodyType::Male) => 0.56, + (Species::Danari, BodyType::Female) => 0.56, + } + } } impl From for super::Body { diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index b452715362..45140e2a3b 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -377,6 +377,8 @@ impl Body { 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), + Body::TrainingDummy => Vec3::new(1.5, 1.5, 3.0), + // FIXME: this *must* be exhaustive match _ => Vec3::broadcast(0.5), } } diff --git a/common/src/comp/body/quadruped_low.rs b/common/src/comp/body/quadruped_low.rs index 9b39604c0a..5ddd680500 100644 --- a/common/src/comp/body/quadruped_low.rs +++ b/common/src/comp/body/quadruped_low.rs @@ -1,7 +1,7 @@ use crate::{make_case_elim, make_proj_elim}; use rand::{seq::SliceRandom, thread_rng}; use serde::{Deserialize, Serialize}; -use strum::{Display, EnumString}; +use strum_macros::{Display, EnumString}; make_proj_elim!( body, diff --git a/common/src/comp/body/quadruped_medium.rs b/common/src/comp/body/quadruped_medium.rs index 7f8d36ea0f..708c55db9e 100644 --- a/common/src/comp/body/quadruped_medium.rs +++ b/common/src/comp/body/quadruped_medium.rs @@ -1,7 +1,7 @@ use crate::{make_case_elim, make_proj_elim}; use rand::{seq::SliceRandom, thread_rng}; use serde::{Deserialize, Serialize}; -use strum::{Display, EnumString}; +use strum_macros::{Display, EnumString}; make_proj_elim!( body, diff --git a/common/src/comp/body/quadruped_small.rs b/common/src/comp/body/quadruped_small.rs index 75a5428283..fe0cdd9404 100644 --- a/common/src/comp/body/quadruped_small.rs +++ b/common/src/comp/body/quadruped_small.rs @@ -1,7 +1,7 @@ use crate::{make_case_elim, make_proj_elim}; use rand::{seq::SliceRandom, thread_rng}; use serde::{Deserialize, Serialize}; -use strum::{Display, EnumString}; +use strum_macros::{Display, EnumString}; make_proj_elim!( body, diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index 7e889c70ef..fb78ff6af1 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -29,7 +29,7 @@ pub enum BuffKind { /// Applied when sitting at a campfire /// Strength is fraction of health resotred per second CampfireHeal, - /// Raises maximum stamina + /// Raises maximum energy /// Strength should be 10x the effect to max energy IncreaseMaxEnergy, /// Raises maximum health @@ -81,22 +81,22 @@ impl BuffKind { /// Checks if buff is buff or debuff pub fn is_buff(self) -> bool { match self { - BuffKind::Regeneration => true, - BuffKind::Saturation => true, - BuffKind::Bleeding => false, - BuffKind::Cursed => false, - BuffKind::Potion => true, - BuffKind::CampfireHeal => true, - BuffKind::IncreaseMaxEnergy => true, - BuffKind::IncreaseMaxHealth => true, - BuffKind::Invulnerability => true, - BuffKind::ProtectingWard => true, - BuffKind::Burning => false, - BuffKind::Crippled => false, - BuffKind::Frenzied => true, - BuffKind::Frozen => false, - BuffKind::Wet => false, - BuffKind::Ensnared => false, + BuffKind::Regeneration + | BuffKind::Saturation + | BuffKind::Potion + | BuffKind::CampfireHeal + | BuffKind::Frenzied + | BuffKind::IncreaseMaxEnergy + | BuffKind::IncreaseMaxHealth + | BuffKind::Invulnerability + | BuffKind::ProtectingWard => true, + BuffKind::Bleeding + | BuffKind::Cursed + | BuffKind::Burning + | BuffKind::Crippled + | BuffKind::Frozen + | BuffKind::Wet + | BuffKind::Ensnared => false, } } @@ -146,7 +146,7 @@ pub enum BuffEffect { }, /// Changes maximum health by a certain amount MaxHealthModifier { value: f32, kind: ModifierKind }, - /// Changes maximum stamina by a certain amount + /// Changes maximum energy by a certain amount MaxEnergyModifier { value: f32, kind: ModifierKind }, /// Reduces damage after armor is accounted for by this fraction DamageReduction(f32), diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 8da7e8386c..e3697f8678 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -1,8 +1,13 @@ use crate::{ combat::Attack, - comp::{tool::ToolKind, Density, Energy, InputAttr, InputKind, Ori, Pos, Vel}, + comp::{tool::ToolKind, ControlAction, Density, Energy, InputAttr, InputKind, Ori, Pos, Vel}, event::{LocalEvent, ServerEvent}, - states::{behavior::JoinData, utils::StageSection, *}, + states::{ + self, + behavior::{CharacterBehavior, JoinData}, + utils::StageSection, + *, + }, }; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage, VecStorage}; @@ -54,7 +59,7 @@ pub enum CharacterState { Talk, Sneak, Glide(glide::Data), - GlideWield, + GlideWield(glide_wield::Data), /// A stunned state Stunned(stunned::Data), /// A basic blocking state @@ -184,7 +189,7 @@ impl CharacterState { | CharacterState::Equipping(_) | CharacterState::Dance | CharacterState::Glide(_) - | CharacterState::GlideWield + | CharacterState::GlideWield(_) | CharacterState::Talk | CharacterState::Roll(_), ) @@ -204,10 +209,10 @@ impl CharacterState { pub fn is_forced_movement(&self) -> bool { matches!(self, - CharacterState::ComboMelee(s) if s.stage_section == StageSection::Swing) + CharacterState::ComboMelee(s) if s.stage_section == StageSection::Action) || matches!(self, CharacterState::DashMelee(s) if s.stage_section == StageSection::Charge) || matches!(self, CharacterState::LeapMelee(s) if s.stage_section == StageSection::Movement) - || matches!(self, CharacterState::SpinMelee(s) if s.stage_section == StageSection::Swing) + || matches!(self, CharacterState::SpinMelee(s) if s.stage_section == StageSection::Action) || matches!(self, CharacterState::Roll(s) if s.stage_section == StageSection::Movement) } @@ -216,6 +221,82 @@ impl CharacterState { // Check if state is the same without looking at the inner data std::mem::discriminant(self) == std::mem::discriminant(other) } + + pub fn behavior(&self, j: &JoinData) -> StateUpdate { + match &self { + CharacterState::Idle => states::idle::Data.behavior(j), + CharacterState::Talk => states::talk::Data.behavior(j), + CharacterState::Climb(data) => data.behavior(j), + CharacterState::Glide(data) => data.behavior(j), + CharacterState::GlideWield(data) => data.behavior(j), + CharacterState::Stunned(data) => data.behavior(j), + CharacterState::Sit => states::sit::Data::behavior(&states::sit::Data, j), + CharacterState::Dance => states::dance::Data::behavior(&states::dance::Data, j), + CharacterState::Sneak => states::sneak::Data::behavior(&states::sneak::Data, j), + CharacterState::BasicBlock(data) => data.behavior(j), + CharacterState::Roll(data) => data.behavior(j), + CharacterState::Wielding => states::wielding::Data.behavior(j), + CharacterState::Equipping(data) => data.behavior(j), + CharacterState::ComboMelee(data) => data.behavior(j), + CharacterState::BasicMelee(data) => data.behavior(j), + CharacterState::BasicRanged(data) => data.behavior(j), + CharacterState::Boost(data) => data.behavior(j), + CharacterState::DashMelee(data) => data.behavior(j), + CharacterState::LeapMelee(data) => data.behavior(j), + CharacterState::SpinMelee(data) => data.behavior(j), + CharacterState::ChargedMelee(data) => data.behavior(j), + CharacterState::ChargedRanged(data) => data.behavior(j), + CharacterState::RepeaterRanged(data) => data.behavior(j), + CharacterState::Shockwave(data) => data.behavior(j), + CharacterState::BasicBeam(data) => data.behavior(j), + CharacterState::BasicAura(data) => data.behavior(j), + CharacterState::Blink(data) => data.behavior(j), + CharacterState::BasicSummon(data) => data.behavior(j), + CharacterState::SelfBuff(data) => data.behavior(j), + CharacterState::SpriteSummon(data) => data.behavior(j), + CharacterState::UseItem(data) => data.behavior(j), + } + } + + pub fn handle_event(&self, j: &JoinData, action: ControlAction) -> StateUpdate { + match &self { + CharacterState::Idle => states::idle::Data.handle_event(j, action), + CharacterState::Talk => states::talk::Data.handle_event(j, action), + CharacterState::Climb(data) => data.handle_event(j, action), + CharacterState::Glide(data) => data.handle_event(j, action), + CharacterState::GlideWield(data) => data.handle_event(j, action), + CharacterState::Stunned(data) => data.handle_event(j, action), + CharacterState::Sit => states::sit::Data::handle_event(&states::sit::Data, j, action), + CharacterState::Dance => { + states::dance::Data::handle_event(&states::dance::Data, j, action) + }, + CharacterState::Sneak => { + states::sneak::Data::handle_event(&states::sneak::Data, j, action) + }, + CharacterState::BasicBlock(data) => data.handle_event(j, action), + CharacterState::Roll(data) => data.handle_event(j, action), + CharacterState::Wielding => states::wielding::Data.handle_event(j, action), + CharacterState::Equipping(data) => data.handle_event(j, action), + CharacterState::ComboMelee(data) => data.handle_event(j, action), + CharacterState::BasicMelee(data) => data.handle_event(j, action), + CharacterState::BasicRanged(data) => data.handle_event(j, action), + CharacterState::Boost(data) => data.handle_event(j, action), + CharacterState::DashMelee(data) => data.handle_event(j, action), + CharacterState::LeapMelee(data) => data.handle_event(j, action), + CharacterState::SpinMelee(data) => data.handle_event(j, action), + CharacterState::ChargedMelee(data) => data.handle_event(j, action), + CharacterState::ChargedRanged(data) => data.handle_event(j, action), + CharacterState::RepeaterRanged(data) => data.handle_event(j, action), + CharacterState::Shockwave(data) => data.handle_event(j, action), + CharacterState::BasicBeam(data) => data.handle_event(j, action), + CharacterState::BasicAura(data) => data.handle_event(j, action), + CharacterState::Blink(data) => data.handle_event(j, action), + CharacterState::BasicSummon(data) => data.handle_event(j, action), + CharacterState::SelfBuff(data) => data.handle_event(j, action), + CharacterState::SpriteSummon(data) => data.handle_event(j, action), + CharacterState::UseItem(data) => data.handle_event(j, action), + } + } } impl Default for CharacterState { diff --git a/common/src/comp/combo.rs b/common/src/comp/combo.rs index 0de0e922c9..b04f460b86 100644 --- a/common/src/comp/combo.rs +++ b/common/src/comp/combo.rs @@ -27,11 +27,11 @@ impl Combo { pub fn reset(&mut self) { self.counter = 0; } pub fn change_by(&mut self, amount: i32, time: f64) { - if amount > 0 { - self.counter = self.counter.saturating_add(amount as u32); + self.counter = if amount > 0 { + self.counter.saturating_add(amount as u32) } else { - self.counter = self.counter.saturating_sub(amount.abs() as u32); - } + self.counter.saturating_sub(amount.abs() as u32) + }; self.last_increase = time; } } diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index f07608a361..54604d49f1 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -242,7 +242,10 @@ impl ControllerInputs { impl Controller { /// Sets all inputs to default - pub fn reset(&mut self) { *self = Self::default(); } + pub fn reset(&mut self) { + self.inputs = Default::default(); + self.queued_inputs = Default::default(); + } pub fn clear_events(&mut self) { self.events.clear(); } diff --git a/common/src/comp/energy.rs b/common/src/comp/energy.rs index d096414e0c..db3ae03eb0 100644 --- a/common/src/comp/energy.rs +++ b/common/src/comp/energy.rs @@ -116,6 +116,9 @@ impl Energy { }); } } + + /// Returns the fraction of energy an entity has remaining + pub fn fraction(&self) -> f32 { self.current as f32 / self.maximum.max(1) as f32 } } pub struct EnergyChange { diff --git a/common/src/comp/fluid_dynamics.rs b/common/src/comp/fluid_dynamics.rs index 6e233545e3..209ccfb79d 100644 --- a/common/src/comp/fluid_dynamics.rs +++ b/common/src/comp/fluid_dynamics.rs @@ -140,7 +140,6 @@ impl Body { Vec3::zero() } else { let rel_flow_dir = Dir::new(rel_flow.0 / v_sq.sqrt()); - // All the coefficients come pre-multiplied by their reference area 0.5 * fluid_density * v_sq * match wings { @@ -163,7 +162,7 @@ impl Body { let aoa = angle_of_attack(&ori, &rel_flow_dir); // c_l will be positive when aoa is positive (we have positive lift, // producing an upward force) and negative otherwise - let c_l = lift_coefficient(ar, planform_area, aoa); + let c_l = lift_coefficient(ar, aoa); // lift dir will be orthogonal to the local relative flow vector. // Local relative flow is the resulting vector of (relative) freestream @@ -187,25 +186,24 @@ impl Body { ori.pitched_down(aoa_eff).up() }; - // drag coefficient - let c_d = { + // induced drag coefficient (drag due to lift) + let cdi = { // Oswald's efficiency factor (empirically derived--very magical) // (this definition should not be used for aspect ratios > 25) let e = 1.78 * (1.0 - 0.045 * ar.powf(0.68)) - 0.64; - // induced drag coefficient (drag due to lift) - let cdi = c_l.powi(2) / (PI * e * ar); - - zero_lift_drag_coefficient(planform_area) - + self.parasite_drag_coefficient() - + cdi + c_l.powi(2) / (PI * e * ar) }; + + // drag coefficient + let c_d = zero_lift_drag_coefficient() + cdi; debug_assert!(c_d.is_sign_positive()); debug_assert!(c_l.is_sign_positive() || aoa.is_sign_negative()); - c_l * *lift_dir + c_d * *rel_flow_dir + planform_area * (c_l * *lift_dir + c_d * *rel_flow_dir) + + self.parasite_drag() * *rel_flow_dir }, - _ => self.parasite_drag_coefficient() * *rel_flow_dir, + _ => self.parasite_drag() * *rel_flow_dir, } } } @@ -214,7 +212,7 @@ impl Body { /// Skin friction is the drag arising from the shear forces between a fluid /// and a surface, while pressure drag is due to flow separation. Both are /// viscous effects. - fn parasite_drag_coefficient(&self) -> f32 { + fn parasite_drag(&self) -> f32 { // Reference area and drag coefficient assumes best-case scenario of the // orientation producing least amount of drag match self { @@ -243,8 +241,9 @@ impl Body { Body::BirdMedium(_) | Body::BirdLarge(_) | Body::Dragon(_) => { let dim = self.dimensions().map(|a| a * 0.5); let cd: f32 = match self { - // "Field Estimates of Body Drag Coefficient on the Basis of Dives in Passerine - // Birds", Anders Hedenström and Felix Liechti, 2001 + // "Field Estimates of Body Drag Coefficient + // on the Basis of Dives in Passerine Birds", + // Anders Hedenström and Felix Liechti, 2001 Body::BirdLarge(_) | Body::BirdMedium(_) => 0.2, // arbitrary _ => 0.7, @@ -329,33 +328,32 @@ pub fn angle_of_attack(ori: &Ori, rel_flow_dir: &Dir) -> f32 { /// Total lift coefficient for a finite wing of symmetric aerofoil shape and /// elliptical pressure distribution. -pub fn lift_coefficient(aspect_ratio: f32, planform_area: f32, aoa: f32) -> f32 { +pub fn lift_coefficient(aspect_ratio: f32, aoa: f32) -> f32 { let aoa_abs = aoa.abs(); let stall_angle = PI * 0.1; - planform_area - * if aoa_abs < stall_angle { - lift_slope(aspect_ratio, None) * aoa + if aoa_abs < stall_angle { + lift_slope(aspect_ratio, None) * aoa + } else { + // This is when flow separation and turbulence starts to kick in. + // Going to just make something up (based on some data), as the alternative is + // to just throw your hands up and return 0 + let aoa_s = aoa.signum(); + let c_l_max = lift_slope(aspect_ratio, None) * stall_angle; + let deg_45 = PI / 4.0; + if aoa_abs < deg_45 { + // drop directly to 0.6 * max lift at stall angle + // then climb back to max at 45° + Lerp::lerp(0.6 * c_l_max, c_l_max, aoa_abs / deg_45) * aoa_s } else { - // This is when flow separation and turbulence starts to kick in. - // Going to just make something up (based on some data), as the alternative is - // to just throw your hands up and return 0 - let aoa_s = aoa.signum(); - let c_l_max = lift_slope(aspect_ratio, None) * stall_angle; - let deg_45 = PI / 4.0; - if aoa_abs < deg_45 { - // drop directly to 0.6 * max lift at stall angle - // then climb back to max at 45° - Lerp::lerp(0.6 * c_l_max, c_l_max, aoa_abs / deg_45) * aoa_s - } else { - // let's just say lift goes down linearly again until we're at 90° - Lerp::lerp(c_l_max, 0.0, (aoa_abs - deg_45) / deg_45) * aoa_s - } + // let's just say lift goes down linearly again until we're at 90° + Lerp::lerp(c_l_max, 0.0, (aoa_abs - deg_45) / deg_45) * aoa_s } + } } /// The zero-lift profile drag coefficient is the parasite drag on the wings /// at the angle of attack which generates no lift -pub fn zero_lift_drag_coefficient(planform_area: f32) -> f32 { planform_area * 0.004 } +pub fn zero_lift_drag_coefficient() -> f32 { 0.026 } /// The change in lift over change in angle of attack¹. Multiplying by angle /// of attack gives the lift coefficient (for a finite wing, not aerofoil). @@ -370,7 +368,8 @@ pub fn zero_lift_drag_coefficient(planform_area: f32) -> f32 { planform_area * 0 /// freestream flow /// 2. up to around ~18°, at which point maximum lift has been achieved and /// thereafter falls precipitously, causing a stall (this is the stall -/// angle) 3. effective aoa, i.e. geometric aoa - induced aoa; assumes +/// angle) +/// 3. effective aoa, i.e. geometric aoa - induced aoa; assumes /// no sideslip // TODO: Look into handling tapered wings fn lift_slope(aspect_ratio: f32, sweep_angle: Option) -> f32 { diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 41863f52be..c425a8056b 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -887,15 +887,27 @@ impl<'a, T: ItemDesc + ?Sized> ItemDesc for &'a T { fn tags(&self) -> &[ItemTag] { (*self).tags() } } +/// Returns all item asset specifiers +/// +/// Panics in case of filesystem errors +pub fn all_item_defs_expect() -> Vec { + try_all_item_defs().expect("Failed to access items directory") +} + +/// Returns all item asset specifiers +pub fn try_all_item_defs() -> Result, Error> { + let defs = assets::load_dir::("common.items", true)?; + Ok(defs.ids().map(|id| id.to_owned()).collect()) +} + #[cfg(test)] mod tests { use super::*; #[test] fn test_assets_items() { - let defs = assets::load_dir::("common.items", true) - .expect("Failed to access items directory"); - for item in defs.ids().map(Item::new_from_asset_expect) { + let ids = all_item_defs_expect(); + for item in ids.iter().map(|id| Item::new_from_asset_expect(id)) { std::mem::drop(item) } } diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 84a5e2b7d7..76ea19e12b 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -320,7 +320,7 @@ impl Inventory { pub fn populated_slots(&self) -> usize { self.slots().filter_map(|slot| slot.as_ref()).count() } - fn free_slots(&self) -> usize { self.slots().filter(|slot| slot.is_none()).count() } + pub fn free_slots(&self) -> usize { self.slots().filter(|slot| slot.is_none()).count() } /// Check if an item is in this inventory. pub fn contains(&self, item: &Item) -> bool { diff --git a/common/src/comp/ori.rs b/common/src/comp/ori.rs index f6cc9b75c6..5531c94829 100644 --- a/common/src/comp/ori.rs +++ b/common/src/comp/ori.rs @@ -194,6 +194,30 @@ impl Ori { self.rotated(Quaternion::rotation_y(angle_radians)) } + /// Returns a version which is rolled such that its up points towards `dir` + /// as much as possible without pitching or yawing + pub fn rolled_towards(self, dir: Dir) -> Self { + dir.projected(&Plane::from(self.look_dir())) + .map_or(self, |dir| self.prerotated(self.up().rotation_between(dir))) + } + + /// Returns a version which has been pitched towards `dir` as much as + /// possible without yawing or rolling + pub fn pitched_towards(self, dir: Dir) -> Self { + dir.projected(&Plane::from(self.right())) + .map_or(self, |dir_| { + self.prerotated(self.look_dir().rotation_between(dir_)) + }) + } + + /// Returns a version which has been yawed towards `dir` as much as possible + /// without pitching or rolling + pub fn yawed_towards(self, dir: Dir) -> Self { + dir.projected(&Plane::from(self.up())).map_or(self, |dir_| { + self.prerotated(self.look_dir().rotation_between(dir_)) + }) + } + /// Returns a version without sideways tilt (roll) /// /// ``` diff --git a/common/src/comp/player.rs b/common/src/comp/player.rs index 7a34664569..76db8ddc55 100644 --- a/common/src/comp/player.rs +++ b/common/src/comp/player.rs @@ -3,6 +3,8 @@ use specs::{Component, DerefFlaggedStorage, NullStorage}; use specs_idvs::IdvStorage; use uuid::Uuid; +use crate::resources::BattleMode; + const MAX_ALIAS_LEN: usize = 32; #[derive(Debug)] @@ -17,11 +19,31 @@ pub enum DisconnectReason { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Player { pub alias: String, + pub battle_mode: BattleMode, uuid: Uuid, } +impl BattleMode { + pub fn may_harm(self, other: Self) -> bool { + matches!((self, other), (BattleMode::PvP, BattleMode::PvP)) + } +} + impl Player { - pub fn new(alias: String, uuid: Uuid) -> Self { Self { alias, uuid } } + pub fn new(alias: String, battle_mode: BattleMode, uuid: Uuid) -> Self { + Self { + alias, + battle_mode, + uuid, + } + } + + /// Currently we allow attacking only if both players are opt-in to PvP. + /// + /// Simple as tea, if they don't want the tea, don't make them drink the + /// tea. + /// You can make tea for yourself though. + pub fn may_harm(&self, other: &Player) -> bool { self.battle_mode.may_harm(other.battle_mode) } pub fn is_valid(&self) -> bool { Self::alias_validate(&self.alias).is_ok() } diff --git a/common/src/comp/skills.rs b/common/src/comp/skills.rs index d6ba867c29..541e8494f7 100644 --- a/common/src/comp/skills.rs +++ b/common/src/comp/skills.rs @@ -576,7 +576,7 @@ pub enum SwimSkill { impl Boost for SwimSkill { fn boost(self) -> BoostValue { match self { - Self::Speed => 40.into(), + Self::Speed => 25.into(), } } } @@ -606,18 +606,17 @@ pub enum SkillGroupKind { impl SkillGroupKind { /// Gets the cost in experience of earning a skill point - #[allow(clippy::many_single_char_names)] pub fn skill_point_cost(self, level: u16) -> u16 { - let exp_increment = 10.0; - let starting_exp = 100.0; - let exp_ceiling = 1000.0; - let scaling_factor = 0.1; - (exp_increment - * (exp_ceiling - / exp_increment + const EXP_INCREMENT: f32 = 10.0; + const STARTING_EXP: f32 = 70.0; + const EXP_CEILING: f32 = 1000.0; + const SCALING_FACTOR: f32 = 0.125; + (EXP_INCREMENT + * (EXP_CEILING + / EXP_INCREMENT / (1.0 - + std::f32::consts::E.powf(-scaling_factor * level as f32) - * (exp_ceiling / starting_exp - 1.0))) + + std::f32::consts::E.powf(-SCALING_FACTOR * level as f32) + * (EXP_CEILING / STARTING_EXP - 1.0))) .floor()) as u16 } diff --git a/common/src/effect.rs b/common/src/effect.rs index aca9cf0ca8..a1b6b599e3 100644 --- a/common/src/effect.rs +++ b/common/src/effect.rs @@ -28,6 +28,15 @@ impl Effect { } } + pub fn is_harm(&self) -> bool { + match self { + Effect::Health(c) => c.amount < 0, + Effect::PoiseChange(c) => c.amount < 0, + Effect::Damage(_) => true, + Effect::Buff(e) => !e.kind.is_buff(), + } + } + pub fn modify_strength(&mut self, modifier: f32) { match self { Effect::Health(change) => { diff --git a/common/src/generation.rs b/common/src/generation.rs index 00598e3877..2989e98249 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -1,5 +1,5 @@ use crate::{ - assets::{self, AssetExt}, + assets::{self, AssetExt, Error}, comp::{ self, agent, humanoid, inventory::loadout_builder::{ItemSpec, LoadoutBuilder}, @@ -140,11 +140,16 @@ impl EntityConfig { } } +/// Return all entity config specifiers +pub fn try_all_entity_configs() -> Result, Error> { + let configs = assets::load_dir::("common.entity", true)?; + Ok(configs.ids().map(|id| id.to_owned()).collect()) +} + #[derive(Clone)] pub struct EntityInfo { pub pos: Vec3, pub is_waypoint: bool, // Edge case, overrides everything else - pub is_giant: bool, pub has_agency: bool, pub alignment: Alignment, pub agent_mark: Option, @@ -170,7 +175,6 @@ impl EntityInfo { Self { pos, is_waypoint: false, - is_giant: false, has_agency: true, alignment: Alignment::Wild, agent_mark: None, @@ -307,11 +311,6 @@ impl EntityInfo { self } - pub fn into_giant(mut self) -> Self { - self.is_giant = true; - self - } - pub fn with_alignment(mut self, alignment: Alignment) -> Self { self.alignment = alignment; self @@ -382,7 +381,7 @@ impl EntityInfo { pub fn with_automatic_name(mut self) -> Self { let npc_names = NPC_NAMES.read(); - self.name = match &self.body { + let name = match &self.body { Body::Humanoid(body) => Some(get_npc_name(&npc_names.humanoid, body.species)), Body::QuadrupedMedium(body) => { Some(get_npc_name(&npc_names.quadruped_medium, body.species)) @@ -400,14 +399,8 @@ impl EntityInfo { Body::Golem(body) => Some(get_npc_name(&npc_names.golem, body.species)), Body::BipedLarge(body) => Some(get_npc_name(&npc_names.biped_large, body.species)), _ => None, - } - .map(|s| { - if self.is_giant { - format!("Giant {}", s) - } else { - s.to_string() - } - }); + }; + self.name = name.map(str::to_owned); self } @@ -554,10 +547,10 @@ mod tests { #[test] fn test_all_entity_assets() { - // It just load everything that could - let entity_configs = assets::load_dir::("common.entity", true) - .expect("Failed to access entity directory"); - for config_asset in entity_configs.ids() { + // Get list of entity configs, load everything, validate content. + let entity_configs = + try_all_entity_configs().expect("Failed to access entity configs directory"); + for config_asset in entity_configs { // print asset name so we don't need to find errors everywhere // it'll be ignored by default so you'll see it only in case of errors // @@ -568,7 +561,7 @@ mod tests { // 2) Add try_from_asset() for LoadoutBuilder and // SkillSet builder which will return Result and we will happily // panic in validate_meta() with the name of config_asset - println!("{}:", config_asset); + println!("{}:", &config_asset); let EntityConfig { hands, @@ -577,12 +570,12 @@ mod tests { loot, meta, alignment: _alignment, // can't fail if serialized, it's a boring enum - } = EntityConfig::from_asset_expect(config_asset); + } = EntityConfig::from_asset_expect(&config_asset); - validate_hands(hands, config_asset); - validate_body_and_name(body, name, config_asset); - validate_loot(loot, config_asset); - validate_meta(meta, config_asset); + validate_hands(hands, &config_asset); + validate_body_and_name(body, name, &config_asset); + validate_loot(loot, &config_asset); + validate_meta(meta, &config_asset); } } } diff --git a/common/src/ray.rs b/common/src/ray.rs index 2188658269..53bf7a9bc2 100644 --- a/common/src/ray.rs +++ b/common/src/ray.rs @@ -13,7 +13,12 @@ pub struct Ray<'a, V: ReadVol, F: FnMut(&V::Vox) -> bool, G: RayForEach> ignore_error: bool, } -impl<'a, V: ReadVol, F: FnMut(&V::Vox) -> bool, G: RayForEach> Ray<'a, V, F, G> { +impl<'a, V, F, G> Ray<'a, V, F, G> +where + V: ReadVol, + F: FnMut(&V::Vox) -> bool, + G: RayForEach, +{ pub fn new(vol: &'a V, from: Vec3, to: Vec3, until: F) -> Self { Self { vol, diff --git a/common/src/resources.rs b/common/src/resources.rs index e6d798ce84..2b08b6407b 100644 --- a/common/src/resources.rs +++ b/common/src/resources.rs @@ -74,3 +74,13 @@ impl PlayerPhysicsSetting { pub struct PlayerPhysicsSettings { pub settings: hashbrown::HashMap, } + +/// Describe how players interact with other players. +/// +/// May be removed when we will discover better way +/// to handle duels and murders +#[derive(Copy, Clone, Debug, Deserialize, Serialize)] +pub enum BattleMode { + PvP, + PvE, +} diff --git a/common/src/states/basic_aura.rs b/common/src/states/basic_aura.rs index ff5bf6fd61..6f7b9c9dc8 100644 --- a/common/src/states/basic_aura.rs +++ b/common/src/states/basic_aura.rs @@ -101,12 +101,12 @@ impl CharacterBehavior for Data { // Build up update.character = CharacterState::BasicAura(Data { timer: Duration::default(), - stage_section: StageSection::Cast, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Cast => { + StageSection::Action => { if self.timer < self.static_data.cast_duration { // Cast update.character = CharacterState::BasicAura(Data { diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 31080f42c0..f7f72720be 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -3,13 +3,17 @@ use crate::{ Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement, Damage, DamageKind, DamageSource, GroupTarget, }, - comp::{beam, Body, CharacterState, EnergyChange, EnergySource, Ori, Pos, StateUpdate}, + comp::{ + beam, body::biped_large, Body, CharacterState, EnergyChange, EnergySource, Ori, Pos, + StateUpdate, + }, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, }, uid::Uid, + util::Dir, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -38,8 +42,6 @@ pub struct StaticData { pub energy_regen: f32, /// Energy drained per second pub energy_drain: f32, - /// Used to dictate how orientation functions in this state - pub orientation_behavior: OrientationBehavior, /// How fast enemy can rotate with beam pub ori_rate: f32, /// What key is used to press ability @@ -64,9 +66,6 @@ impl CharacterBehavior for Data { let mut update = StateUpdate::from(data); let ori_rate = self.static_data.ori_rate; - if self.static_data.orientation_behavior == OrientationBehavior::Turret { - update.ori = Ori::from(data.inputs.look_dir); - } handle_orientation(data, &mut update, ori_rate); handle_move(data, &mut update, 0.4); @@ -90,12 +89,12 @@ impl CharacterBehavior for Data { // Build up update.character = CharacterState::BasicBeam(Data { timer: Duration::default(), - stage_section: StageSection::Cast, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Cast => { + StageSection::Action => { if input_is_pressed(data, self.static_data.ability_info.input) && (self.static_data.energy_drain <= f32::EPSILON || update.energy.current() > 0) @@ -135,41 +134,42 @@ impl CharacterBehavior for Data { owner: Some(*data.uid), specifier: self.static_data.specifier, }; + let beam_ori = { + // We want Beam to use Ori of owner. + // But we also want beam to use Z part of where owner looks. + // This means that we need to merge this data to one Ori. + // + // This code just gets look_dir without Z part + // and normalizes it. This is what `xy_dir is`. + // + // Then we find rotation between xy_dir and look_dir + // which gives us quaternion how of what rotation we need + // to do to get Z part we want. + // + // Then we construct Ori without Z part + // and applying `pitch` to get needed orientation. + let look_dir = data.inputs.look_dir; + let xy_dir = Dir::from_unnormalized(Vec3::new(look_dir.x, look_dir.y, 0.0)) + .unwrap_or_else(Dir::default); + let pitch = xy_dir.rotation_between(look_dir); + + Ori::from(Vec3::new( + update.ori.look_vec().x, + update.ori.look_vec().y, + 0.0, + )) + .prerotated(pitch) + }; // Gets offsets - let body_offsets_r = data.body.radius() + 1.0; - let body_offsets_z = match data.body { - Body::BirdLarge(_) => data.body.height() * 0.8, - Body::Golem(_) => data.body.height() * 0.9 + data.inputs.look_dir.z * 3.0, - _ => data.body.height() * 0.5, - }; - let (body_offsets, ori) = match self.static_data.orientation_behavior { - OrientationBehavior::Normal | OrientationBehavior::Turret => ( - Vec3::new( - body_offsets_r * data.inputs.look_dir.x, - body_offsets_r * data.inputs.look_dir.y, - body_offsets_z, - ), - Ori::from(data.inputs.look_dir), - ), - OrientationBehavior::FromOri => ( - Vec3::new( - body_offsets_r * update.ori.look_vec().x, - body_offsets_r * update.ori.look_vec().y, - body_offsets_z, - ), - Ori::from(Vec3::new( - update.ori.look_vec().x, - update.ori.look_vec().y, - data.inputs.look_dir.z, - )), - ), - }; + let body_offsets = + beam_offsets(data.body, data.inputs.look_dir, update.ori.look_vec()); let pos = Pos(data.pos.0 + body_offsets); + // Create beam segment update.server_events.push_front(ServerEvent::BeamSegment { properties, pos, - ori, + ori: beam_ori, }); update.character = CharacterState::BasicBeam(Data { timer: tick_attack_or_default(data, self.timer, None), @@ -219,14 +219,24 @@ impl CharacterBehavior for Data { } } -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum OrientationBehavior { - /// Uses look_dir as direction of beam - Normal, - /// Uses look_dir as direction of beam, sets orientation to same direction - /// as look_dir - Turret, - /// Uses orientation x and y and look_dir z as direction of beam (z from - /// look_dir as orientation will only go through 2d rotations naturally) - FromOri, +fn height_offset(body: &Body, look_dir: Dir) -> f32 { + match body { + Body::BirdLarge(_) => body.height() * 0.8, + Body::Golem(_) => body.height() * 0.9 + look_dir.z * 3.0, + Body::BipedLarge(b) => match b.species { + biped_large::Species::Mindflayer => body.height() * 0.6, + _ => body.height() * 0.5, + }, + _ => body.height() * 0.5, + } +} + +pub fn beam_offsets(body: &Body, look_dir: Dir, ori: Vec3) -> Vec3 { + let body_radius = body.radius(); + let body_offsets_z = height_offset(body, look_dir); + Vec3::new( + body_radius * ori.x * 1.1, + body_radius * ori.y * 1.1, + body_offsets_z, + ) } diff --git a/common/src/states/basic_block.rs b/common/src/states/basic_block.rs index f07532d934..298fc2206e 100644 --- a/common/src/states/basic_block.rs +++ b/common/src/states/basic_block.rs @@ -51,12 +51,12 @@ impl CharacterBehavior for Data { // Transitions to swing section of stage update.character = CharacterState::BasicBlock(Data { timer: Duration::default(), - stage_section: StageSection::Swing, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Swing => { + StageSection::Action => { if input_is_pressed(data, InputKind::Block) { // Block update.character = CharacterState::BasicBlock(Data { diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index 35460a510d..e32ccc5225 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -73,12 +73,12 @@ impl CharacterBehavior for Data { // Transitions to swing section of stage update.character = CharacterState::BasicMelee(Data { timer: Duration::default(), - stage_section: StageSection::Swing, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Swing => { + StageSection::Action => { if !self.exhausted { update.character = CharacterState::BasicMelee(Data { timer: Duration::default(), diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 35f012b5bf..6b2e2b943b 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -69,12 +69,12 @@ impl CharacterBehavior for Data { // Transitions to recover section of stage update.character = CharacterState::BasicSummon(Data { timer: Duration::default(), - stage_section: StageSection::Cast, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Cast => { + StageSection::Action => { if self.timer < self.static_data.cast_duration || self.summon_count < self.static_data.summon_amount { @@ -174,12 +174,11 @@ impl CharacterBehavior for Data { poise: comp::Poise::new(body), loadout, body, - agent: Some(comp::Agent::new( - None, - &body, - Behavior::from(BehaviorCapability::SPEAK), - true, - )), + agent: Some( + comp::Agent::from_body(&body) + .with_behavior(Behavior::from(BehaviorCapability::SPEAK)) + .with_no_flee(true), + ), alignment: comp::Alignment::Owned(*data.uid), scale: self .static_data diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index 7ace902d81..37691a5907 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -115,13 +115,13 @@ impl CharacterBehavior for Data { } else { // Transitions to swing update.character = CharacterState::ChargedMelee(Data { - stage_section: StageSection::Swing, + stage_section: StageSection::Action, timer: Duration::default(), ..*self }); } }, - StageSection::Swing => { + StageSection::Action => { if self.timer.as_millis() as f32 > self.static_data.hit_timing * self.static_data.swing_duration.as_millis() as f32 diff --git a/common/src/states/climb.rs b/common/src/states/climb.rs index 71d87e84ec..9719790b56 100644 --- a/common/src/states/climb.rs +++ b/common/src/states/climb.rs @@ -68,10 +68,13 @@ impl CharacterBehavior for Data { .then(|| data.body.jump_impulse()) .flatten() { + // How strong the climb boost is relative to a normal jump + const CLIMB_BOOST_JUMP_FACTOR: f32 = 0.5; // They've climbed atop something, give them a boost - update - .local_events - .push_front(LocalEvent::Jump(data.entity, 0.5 * impulse / data.mass.0)); + update.local_events.push_front(LocalEvent::Jump( + data.entity, + CLIMB_BOOST_JUMP_FACTOR * impulse / data.mass.0, + )); }; update.character = CharacterState::Idle {}; return update; diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index fb97e4bb56..affcdfa5e9 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -188,12 +188,12 @@ impl CharacterBehavior for Data { update.character = CharacterState::ComboMelee(Data { static_data: self.static_data.clone(), timer: Duration::default(), - stage_section: StageSection::Swing, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Swing => { + StageSection::Action => { if self.timer.as_secs_f32() > self.static_data.stage_data[stage_index].hit_timing * self.static_data.stage_data[stage_index] diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 33012b8aa7..fe086ec68e 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -43,6 +43,8 @@ pub struct StaticData { pub swing_duration: Duration, /// How long the state has until exiting pub recover_duration: Duration, + /// How fast can you turn during charge + pub ori_modifier: f32, /// Whether the state can be interrupted by other abilities pub is_interruptible: bool, /// Adds an effect onto the main damage of the attack @@ -107,13 +109,14 @@ impl CharacterBehavior for Data { / self.static_data.charge_duration.as_secs_f32()) .min(1.0); - handle_orientation(data, &mut update, 0.6); + handle_orientation(data, &mut update, self.static_data.ori_modifier); handle_forced_movement(data, &mut update, ForcedMovement::Forward { strength: self.static_data.forward_speed * charge_frac.sqrt(), }); - // This logic basically just decides if a charge should end, and prevents the - // character state spamming attacks while checking if it has hit something + // This logic basically just decides if a charge should end, + // and prevents the character state spamming attacks + // while checking if it has hit something. if !self.exhausted { // Hit attempt let poise = AttackEffect::new( @@ -215,7 +218,7 @@ impl CharacterBehavior for Data { // Stop charging now and go to swing stage section update.character = CharacterState::DashMelee(Data { timer: Duration::default(), - stage_section: StageSection::Swing, + stage_section: StageSection::Action, exhausted: false, ..*self }); @@ -238,13 +241,13 @@ impl CharacterBehavior for Data { // Transitions to swing section of stage update.character = CharacterState::DashMelee(Data { timer: Duration::default(), - stage_section: StageSection::Swing, + stage_section: StageSection::Action, exhausted: false, ..*self }); } }, - StageSection::Swing => { + StageSection::Action => { if self.static_data.charge_through && !self.exhausted { // If can charge through and not exhausted, do one more melee attack diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 00f9b65336..399110dc3c 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -4,7 +4,10 @@ use crate::{ fluid_dynamics::angle_of_attack, inventory::slot::EquipSlot, CharacterState, Ori, StateUpdate, Vel, }, - states::behavior::{CharacterBehavior, JoinData}, + states::{ + behavior::{CharacterBehavior, JoinData}, + glide_wield, + }, util::{Dir, Plane, Projection}, }; use serde::{Deserialize, Serialize}; @@ -77,8 +80,7 @@ impl CharacterBehavior for Data { if data.physics.on_ground.is_some() && (data.vel.0 - data.physics.ground_vel).magnitude_squared() < 2_f32.powi(2) { - update.character = CharacterState::GlideWield; - update.ori = update.ori.to_horizontal(); + update.character = CharacterState::GlideWield(glide_wield::Data::from(data)); } else if data.physics.in_liquid().is_some() || data .inventory @@ -86,7 +88,6 @@ impl CharacterBehavior for Data { .is_none() { update.character = CharacterState::Idle; - update.ori = update.ori.to_horizontal(); } else if !handle_climb(data, &mut update) { let air_flow = data .physics @@ -193,8 +194,6 @@ impl CharacterBehavior for Data { inputs_disabled, ..*self }); - } else { - update.ori = update.ori.to_horizontal(); } update @@ -203,7 +202,6 @@ impl CharacterBehavior for Data { fn unwield(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate::from(data); update.character = CharacterState::Idle; - update.ori = update.ori.to_horizontal(); update } } diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index 269d8c50b2..ade2f71b58 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -1,13 +1,35 @@ use super::utils::*; use crate::{ - comp::{slot::EquipSlot, CharacterState, InventoryAction, StateUpdate}, + comp::{slot::EquipSlot, CharacterState, InventoryAction, Ori, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, glide, }, }; +use serde::{Deserialize, Serialize}; -pub struct Data; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + pub ori: Ori, + span_length: f32, + chord_length: f32, +} + +impl From<&JoinData<'_>> for Data { + fn from(data: &JoinData) -> Self { + let scale = data.body.dimensions().z.sqrt(); + Self { + // Aspect ratio is what really matters for lift/drag ratio + // and the aerodynamics model works for ARs up to 25. + // The inflated dimensions are hopefully only a temporary + // bandaid for the poor glide ratio experienced under 2.5G. + // A span/chord ratio of 4.5 gives an AR of ~5.73. + span_length: scale * 4.5, + chord_length: scale, + ori: *data.ori, + } + } +} impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { @@ -19,25 +41,37 @@ impl CharacterBehavior for Data { handle_dodge_input(data, &mut update); handle_wield(data, &mut update); - // If not on the ground while wielding glider enter gliding state - if data.physics.on_ground.is_none() { - update.character = CharacterState::Glide(glide::Data::new(10.0, 0.6, *data.ori)); + // If still in this state, do the things + if matches!(update.character, CharacterState::GlideWield(_)) { + // If not on the ground while wielding glider enter gliding state + update.character = if data.physics.on_ground.is_none() { + CharacterState::Glide(glide::Data::new( + self.span_length, + self.chord_length, + self.ori, + )) + // make sure we have a glider and we're not (too deep) in water + } else if data + .inventory + .and_then(|inv| inv.equipped(EquipSlot::Glider)) + .is_some() + && data.physics.in_liquid().map_or(true, |depth| depth < 0.5) + { + CharacterState::GlideWield(Self { + // Glider tilt follows look dir + ori: self.ori.slerped_towards( + data.ori.slerped_towards( + Ori::from(data.inputs.look_dir).pitched_up(0.6), + (1.0 + data.inputs.look_dir.dot(*data.ori.look_dir()).max(0.0)) / 3.0, + ), + 5.0 * data.dt.0, + ), + ..*self + }) + } else { + CharacterState::Idle + }; } - if data - .physics - .in_liquid() - .map(|depth| depth > 0.5) - .unwrap_or(false) - { - update.character = CharacterState::Idle; - } - if data - .inventory - .and_then(|inv| inv.equipped(EquipSlot::Glider)) - .is_none() - { - update.character = CharacterState::Idle - }; update } diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 72124f1772..8125ec4617 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -107,12 +107,12 @@ impl CharacterBehavior for Data { // Transitions to swing portion of state upon hitting ground update.character = CharacterState::LeapMelee(Data { timer: Duration::default(), - stage_section: StageSection::Swing, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Swing => { + StageSection::Action => { if self.timer < self.static_data.swing_duration { // Swings weapons update.character = CharacterState::LeapMelee(Data { diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index 206963b5af..de55813305 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -69,12 +69,12 @@ impl CharacterBehavior for Data { // Transition to shoot update.character = CharacterState::RepeaterRanged(Data { timer: Duration::default(), - stage_section: StageSection::Shoot, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Shoot => { + StageSection::Action => { if self.timer < self.static_data.shoot_duration { // Draw projectile update.character = CharacterState::RepeaterRanged(Data { diff --git a/common/src/states/self_buff.rs b/common/src/states/self_buff.rs index c1c4edbdfb..6f60e283d7 100644 --- a/common/src/states/self_buff.rs +++ b/common/src/states/self_buff.rs @@ -75,12 +75,12 @@ impl CharacterBehavior for Data { // Build up update.character = CharacterState::SelfBuff(Data { timer: Duration::default(), - stage_section: StageSection::Cast, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Cast => { + StageSection::Action => { if self.timer < self.static_data.cast_duration { // Cast update.character = CharacterState::SelfBuff(Data { diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index c45805efe8..440fc4f834 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -126,12 +126,12 @@ impl CharacterBehavior for Data { // Transitions to swing update.character = CharacterState::Shockwave(Data { timer: Duration::default(), - stage_section: StageSection::Swing, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Swing => { + StageSection::Action => { if self.timer < self.static_data.swing_duration { // Swings update.character = CharacterState::Shockwave(Data { diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index f955cc3263..948a75537d 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -97,12 +97,12 @@ impl CharacterBehavior for Data { // Transitions to swing section of stage update.character = CharacterState::SpinMelee(Data { timer: Duration::default(), - stage_section: StageSection::Swing, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Swing => { + StageSection::Action => { if !self.exhausted { update.character = CharacterState::SpinMelee(Data { timer: Duration::default(), diff --git a/common/src/states/sprite_summon.rs b/common/src/states/sprite_summon.rs index 1a3610e8bd..df354f1013 100644 --- a/common/src/states/sprite_summon.rs +++ b/common/src/states/sprite_summon.rs @@ -62,12 +62,12 @@ impl CharacterBehavior for Data { // Transitions to recover section of stage update.character = CharacterState::SpriteSummon(Data { timer: Duration::default(), - stage_section: StageSection::Cast, + stage_section: StageSection::Action, ..*self }); } }, - StageSection::Cast => { + StageSection::Action => { if self.timer < self.static_data.cast_duration { let timer_frac = self.timer.as_secs_f32() / self.static_data.cast_duration.as_secs_f32(); diff --git a/common/src/states/use_item.rs b/common/src/states/use_item.rs index e086b8c1d1..0265c5b9f6 100644 --- a/common/src/states/use_item.rs +++ b/common/src/states/use_item.rs @@ -82,7 +82,7 @@ impl CharacterBehavior for Data { update.character = CharacterState::UseItem(Data { static_data: self.static_data.clone(), timer: Duration::default(), - stage_section: StageSection::Use, + stage_section: StageSection::Action, }); if let UsePoint::BuildupUse = use_point { // Create inventory manipulation event @@ -90,7 +90,7 @@ impl CharacterBehavior for Data { } } }, - StageSection::Use => { + StageSection::Action => { if self.timer < self.static_data.use_duration { // Item use update.character = CharacterState::UseItem(Data { diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 0275524f6f..c9be9a3605 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -184,7 +184,7 @@ impl Body { Body::FishMedium(_) => Some(50.0 * self.mass().0), Body::FishSmall(_) => Some(50.0 * self.mass().0), Body::Dragon(_) => Some(200.0 * self.mass().0), - Body::Humanoid(_) => Some(200.0 * self.mass().0), + Body::Humanoid(_) => Some(2500.0 * self.mass().0), Body::Theropod(body) => match body.species { theropod::Species::Sandraptor | theropod::Species::Snowraptor @@ -349,18 +349,17 @@ pub fn handle_forced_movement( } pub fn handle_orientation(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) { - if let Some(dir) = (is_strafing(data, update) || update.character.is_attack()) + let dir = (is_strafing(data, update) || update.character.is_attack()) .then(|| data.inputs.look_dir.to_horizontal().unwrap_or_default()) .or_else(|| Dir::from_unnormalized(data.inputs.move_dir.into())) - { - let rate = { - let angle = update.ori.look_dir().angle_between(*dir); - data.body.base_ori_rate() * efficiency * std::f32::consts::PI / angle - }; - update.ori = update - .ori - .slerped_towards(dir.into(), (data.dt.0 * rate).min(1.0)); + .unwrap_or_else(|| data.ori.to_horizontal().look_dir()); + let rate = { + let angle = update.ori.look_dir().angle_between(*dir); + data.body.base_ori_rate() * efficiency * std::f32::consts::PI / angle }; + update.ori = update + .ori + .slerped_towards(dir.into(), (data.dt.0 * rate).min(1.0)); } /// Updates components to move player as if theyre swimming @@ -376,7 +375,7 @@ fn swim_move( let mut water_accel = force / data.mass.0; if let Ok(Some(level)) = data.skill_set.skill_level(Skill::Swim(SwimSkill::Speed)) { - water_accel *= 1.4_f32.powi(level.into()); + water_accel *= 1.25_f32.powi(level.into()); } let dir = if data.body.can_strafe() { @@ -639,7 +638,7 @@ pub fn attempt_glide_wield(data: &JoinData<'_>, update: &mut StateUpdate) { .unwrap_or(false) && data.body.is_humanoid() { - update.character = CharacterState::GlideWield; + update.character = CharacterState::GlideWield(glide_wield::Data::from(data)); } } @@ -921,12 +920,7 @@ pub enum StageSection { Recover, Charge, Movement, - // TODO: Consolidate these to `Action` - // Code reviewers: comment here to remind me to open beginner issue - Swing, - Shoot, - Cast, - Use, + Action, } #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index c2822b31b5..f05974e088 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -243,6 +243,7 @@ impl Block { | SpriteKind::Loom | SpriteKind::SpinningWheel | SpriteKind::TanningRack => None, + SpriteKind::EnsnaringVines => Some(0.1), _ => Some(0.25), }), } @@ -309,15 +310,46 @@ impl Block { Block::air(SpriteKind::Empty) } } + + /// Attempt to convert a [`u32`] to a block + #[inline] + pub fn from_u32(x: u32) -> Option { + let [bk, r, g, b] = x.to_le_bytes(); + Some(Self { + kind: BlockKind::from_u8(bk)?, + attr: [r, g, b], + }) + } + + #[inline] + pub fn to_u32(&self) -> u32 { + u32::from_le_bytes([self.kind as u8, self.attr[0], self.attr[1], self.attr[2]]) + } } #[cfg(test)] mod tests { use super::*; + use strum::IntoEnumIterator; #[test] fn block_size() { assert_eq!(std::mem::size_of::(), 1); assert_eq!(std::mem::size_of::(), 4); } + + #[test] + fn convert_u32() { + for bk in BlockKind::iter() { + let block = Block::new(bk, Rgb::new(165, 90, 204)); // Pretty unique bit patterns + if bk.is_filled() { + assert_eq!(Block::from_u32(block.to_u32()), Some(block)); + } else { + assert_eq!( + Block::from_u32(block.to_u32()), + Some(Block::new(bk, Rgb::zero())), + ); + } + } + } } diff --git a/common/systems/src/aura.rs b/common/systems/src/aura.rs index da19ac5762..f9201b3d8d 100644 --- a/common/systems/src/aura.rs +++ b/common/systems/src/aura.rs @@ -1,24 +1,26 @@ use common::{ + combat, comp::{ aura::{AuraChange, AuraKey, AuraKind, AuraTarget}, - buff::{self, BuffCategory}, + buff::{Buff, BuffCategory, BuffChange, BuffSource}, group::Group, - Auras, BuffKind, Buffs, CharacterState, Health, Pos, + Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos, }, - event::{EventBus, ServerEvent}, + event::{Emitter, EventBus, ServerEvent}, resources::DeltaTime, uid::{Uid, UidAllocator}, }; use common_ecs::{Job, Origin, Phase, System}; use specs::{ - saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, SystemData, - World, WriteStorage, + saveload::MarkerAllocator, shred::ResourceId, Entities, Entity as EcsEntity, Join, Read, + ReadStorage, SystemData, World, WriteStorage, }; use std::time::Duration; #[derive(SystemData)] pub struct ReadData<'a> { entities: Entities<'a>, + players: ReadStorage<'a, Player>, dt: Read<'a, DeltaTime>, server_bus: Read<'a, EventBus>, uid_allocator: Read<'a, UidAllocator>, @@ -101,91 +103,36 @@ impl<'a> System<'a> for Sys { Some(buff) => buff, None => return, }; + // Ensure entity is within the aura radius if target_pos.0.distance_squared(pos.0) < aura.radius.powi(2) { - if let AuraTarget::GroupOf(uid) = aura.target { - let same_group = read_data + // Ensure the entity is in the group we want to target + let same_group = |uid: Uid| { + read_data .uid_allocator .retrieve_entity_internal(uid.into()) .and_then(|e| read_data.groups.get(e)) .map_or(false, |owner_group| { Some(owner_group) == read_data.groups.get(target) }) - || *target_uid == uid; + || *target_uid == uid + }; - if !same_group { - return; - } - } + let is_target = match aura.target { + AuraTarget::GroupOf(uid) => same_group(uid), + AuraTarget::NotGroupOf(uid) => !same_group(uid), + AuraTarget::All => true, + }; - // TODO: When more aura kinds (besides Buff) are - // implemented, match on them here - match aura.aura_kind { - AuraKind::Buff { - kind, - data, - category, - source, - } => { - let apply_buff = match kind { - BuffKind::CampfireHeal => { - matches!( - read_data.char_states.get(target), - Some(CharacterState::Sit) - ) && health.current() < health.maximum() - }, - // Add other specific buff conditions here - _ => true, - }; - if apply_buff { - // Checks that target is not already receiving a buff from - // an aura, where - // the buff is of the same kind, and is of at least - // the same strength and of at least the same duration - // If no such buff is present, adds the buff - let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| { - buff.cat_ids.iter().any(|cat_id| { - matches!(cat_id, BuffCategory::FromAura(_)) - }) && buff.kind == kind - && buff.data.strength >= data.strength - && buff.time.map_or(true, |dur| { - data.duration.map_or(false, |dur_2| dur >= dur_2) - }) - }); - if emit_buff { - use buff::*; - server_emitter.emit(ServerEvent::Buff { - entity: target, - buff_change: BuffChange::Add(Buff::new( - kind, - data, - vec![category, BuffCategory::FromAura(true)], - source, - )), - }); - } - // Finds all buffs on target that are from an aura, are of - // the - // same buff kind, and are of at most the same strength - // For any such buffs, marks it as recently applied - for (_, buff) in - target_buffs.buffs.iter_mut().filter(|(_, buff)| { - buff.cat_ids.iter().any(|cat_id| { - matches!(cat_id, BuffCategory::FromAura(_)) - }) && buff.kind == kind - && buff.data.strength <= data.strength - }) - { - if let Some(cat_id) = - buff.cat_ids.iter_mut().find(|cat_id| { - matches!(cat_id, BuffCategory::FromAura(false)) - }) - { - *cat_id = BuffCategory::FromAura(true); - } - } - } - }, + if is_target { + activate_aura( + aura, + target, + health, + &mut target_buffs, + &read_data, + &mut server_emitter, + ); } } }); @@ -201,3 +148,111 @@ impl<'a> System<'a> for Sys { buffs.set_event_emission(true); } } + +#[warn(clippy::pedantic)] +//#[warn(clippy::nursery)] +fn activate_aura( + aura: &Aura, + target: EcsEntity, + health: &Health, + target_buffs: &mut Buffs, + read_data: &ReadData, + server_emitter: &mut Emitter, +) { + let should_activate = match aura.aura_kind { + AuraKind::Buff { kind, source, .. } => { + let conditions_held = match kind { + BuffKind::CampfireHeal => { + let target_state = read_data.char_states.get(target); + matches!(target_state, Some(CharacterState::Sit)) + && health.current() < health.maximum() + }, + // Add other specific buff conditions here + _ => true, + }; + + // TODO: this check will disable friendly fire with PvE switch. + // + // Which means that you can't apply debuffs on you and your group + // even if it's intented mechanics. + // + // Not that we have this for now, but think about this + // when we will add this. + let may_harm = || { + let owner = match source { + BuffSource::Character { by } => { + read_data.uid_allocator.retrieve_entity_internal(by.into()) + }, + _ => None, + }; + owner.map_or(true, |attacker| { + let attacker = read_data.players.get(attacker); + let target = read_data.players.get(target); + combat::may_harm(attacker, target) + }) + }; + + conditions_held && (kind.is_buff() || may_harm()) + }, + }; + + if !should_activate { + return; + } + + // TODO: When more aura kinds (besides Buff) are + // implemented, match on them here + match aura.aura_kind { + AuraKind::Buff { + kind, + data, + category, + source, + } => { + // Checks that target is not already receiving a buff + // from an aura, where the buff is of the same kind, + // and is of at least the same strength + // and of at least the same duration. + // If no such buff is present, adds the buff. + let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| { + buff.cat_ids + .iter() + .any(|cat_id| matches!(cat_id, BuffCategory::FromAura(_))) + && buff.kind == kind + && buff.data.strength >= data.strength + && buff.time.map_or(true, |dur| { + data.duration.map_or(false, |dur_2| dur >= dur_2) + }) + }); + if emit_buff { + server_emitter.emit(ServerEvent::Buff { + entity: target, + buff_change: BuffChange::Add(Buff::new( + kind, + data, + vec![category, BuffCategory::FromAura(true)], + source, + )), + }); + } + // Finds all buffs on target that are from an aura, are of + // the same buff kind, and are of at most the same strength. + // For any such buffs, marks it as recently applied. + for (_, buff) in target_buffs.buffs.iter_mut().filter(|(_, buff)| { + buff.cat_ids + .iter() + .any(|cat_id| matches!(cat_id, BuffCategory::FromAura(_))) + && buff.kind == kind + && buff.data.strength <= data.strength + }) { + if let Some(cat_id) = buff + .cat_ids + .iter_mut() + .find(|cat_id| matches!(cat_id, BuffCategory::FromAura(false))) + { + *cat_id = BuffCategory::FromAura(true); + } + } + }, + } +} diff --git a/common/systems/src/beam.rs b/common/systems/src/beam.rs index dae48525d2..86854bd814 100644 --- a/common/systems/src/beam.rs +++ b/common/systems/src/beam.rs @@ -1,9 +1,9 @@ use common::{ - combat::{AttackSource, AttackerInfo, TargetInfo}, + combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, comp::{ agent::{Sound, SoundKind}, Beam, BeamSegment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, - Inventory, Ori, Pos, Scale, Stats, + Inventory, Ori, Player, Pos, Scale, Stats, }, event::{EventBus, ServerEvent}, outcome::Outcome, @@ -26,6 +26,7 @@ use vek::*; #[derive(SystemData)] pub struct ReadData<'a> { entities: Entities<'a>, + players: ReadStorage<'a, Player>, server_bus: Read<'a, EventBus>, time: Read<'a, Time>, dt: Read<'a, DeltaTime>, @@ -212,12 +213,23 @@ impl<'a> System<'a> for Sys { char_state: read_data.character_states.get(target), }; - beam_segment.properties.attack.apply_attack( + + let may_harm = combat::may_harm( + beam_owner.and_then(|owner| read_data.players.get(owner)), + read_data.players.get(target), + ); + let attack_options = AttackOptions { + // No luck with dodging beams + target_dodging: false, + may_harm, target_group, + }; + + beam_segment.properties.attack.apply_attack( attacker_info, target_info, ori.look_dir(), - false, + attack_options, 1.0, AttackSource::Beam, |e| server_events.push(e), diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index 80d3586d2f..930a243636 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -12,47 +12,13 @@ use common::{ event::{Emitter, EventBus, LocalEvent, ServerEvent}, outcome::Outcome, resources::DeltaTime, - states::{ - self, - behavior::{CharacterBehavior, JoinData, JoinStruct}, - }, + states::behavior::{JoinData, JoinStruct}, terrain::TerrainGrid, uid::Uid, }; use common_ecs::{Job, Origin, Phase, System}; use std::time::Duration; -fn incorporate_update( - join: &mut JoinStruct, - mut state_update: StateUpdate, - server_emitter: &mut Emitter, -) { - // TODO: if checking equality is expensive use optional field in StateUpdate - if *join.char_state != state_update.character { - *join.char_state = state_update.character - }; - *join.pos = state_update.pos; - *join.vel = state_update.vel; - *join.ori = state_update.ori; - *join.density = state_update.density; - // Note: might be changed every tick by timer anyway - if *join.energy != state_update.energy { - *join.energy = state_update.energy - }; - join.controller - .queued_inputs - .append(&mut state_update.queued_inputs); - for input in state_update.removed_inputs { - join.controller.queued_inputs.remove(&input); - } - if state_update.swap_equipped_weapons { - server_emitter.emit(ServerEvent::InventoryManip( - join.entity, - InventoryManip::SwapEquippedWeapons, - )); - } -} - #[derive(SystemData)] pub struct ReadData<'a> { entities: Entities<'a>, @@ -303,50 +269,13 @@ impl<'a> System<'a> for Sys { &read_data.dt, &read_data.msm, ); - let mut state_update = match j.character { - CharacterState::Idle => states::idle::Data.handle_event(&j, action), - CharacterState::Talk => states::talk::Data.handle_event(&j, action), - CharacterState::Climb(data) => data.handle_event(&j, action), - CharacterState::Glide(data) => data.handle_event(&j, action), - CharacterState::GlideWield => { - states::glide_wield::Data.handle_event(&j, action) - }, - CharacterState::Stunned(data) => data.handle_event(&j, action), - CharacterState::Sit => { - states::sit::Data::handle_event(&states::sit::Data, &j, action) - }, - CharacterState::Dance => { - states::dance::Data::handle_event(&states::dance::Data, &j, action) - }, - CharacterState::Sneak => { - states::sneak::Data::handle_event(&states::sneak::Data, &j, action) - }, - CharacterState::BasicBlock(data) => data.handle_event(&j, action), - CharacterState::Roll(data) => data.handle_event(&j, action), - CharacterState::Wielding => states::wielding::Data.handle_event(&j, action), - CharacterState::Equipping(data) => data.handle_event(&j, action), - CharacterState::ComboMelee(data) => data.handle_event(&j, action), - CharacterState::BasicMelee(data) => data.handle_event(&j, action), - CharacterState::BasicRanged(data) => data.handle_event(&j, action), - CharacterState::Boost(data) => data.handle_event(&j, action), - CharacterState::DashMelee(data) => data.handle_event(&j, action), - CharacterState::LeapMelee(data) => data.handle_event(&j, action), - CharacterState::SpinMelee(data) => data.handle_event(&j, action), - CharacterState::ChargedMelee(data) => data.handle_event(&j, action), - CharacterState::ChargedRanged(data) => data.handle_event(&j, action), - CharacterState::RepeaterRanged(data) => data.handle_event(&j, action), - CharacterState::Shockwave(data) => data.handle_event(&j, action), - CharacterState::BasicBeam(data) => data.handle_event(&j, action), - CharacterState::BasicAura(data) => data.handle_event(&j, action), - CharacterState::Blink(data) => data.handle_event(&j, action), - CharacterState::BasicSummon(data) => data.handle_event(&j, action), - CharacterState::SelfBuff(data) => data.handle_event(&j, action), - CharacterState::SpriteSummon(data) => data.handle_event(&j, action), - CharacterState::UseItem(data) => data.handle_event(&j, action), - }; - local_emitter.append(&mut state_update.local_events); - server_emitter.append(&mut state_update.server_events); - incorporate_update(&mut join_struct, state_update, &mut server_emitter); + let state_update = j.character.handle_event(&j, action); + Self::publish_state_update( + &mut join_struct, + state_update, + &mut local_emitter, + &mut server_emitter, + ); } // Mounted occurs after control actions have been handled @@ -366,43 +295,50 @@ impl<'a> System<'a> for Sys { &read_data.msm, ); - let mut state_update = match j.character { - CharacterState::Idle => states::idle::Data.behavior(&j), - CharacterState::Talk => states::talk::Data.behavior(&j), - CharacterState::Climb(data) => data.behavior(&j), - CharacterState::Glide(data) => data.behavior(&j), - CharacterState::GlideWield => states::glide_wield::Data.behavior(&j), - CharacterState::Stunned(data) => data.behavior(&j), - CharacterState::Sit => states::sit::Data::behavior(&states::sit::Data, &j), - CharacterState::Dance => states::dance::Data::behavior(&states::dance::Data, &j), - CharacterState::Sneak => states::sneak::Data::behavior(&states::sneak::Data, &j), - CharacterState::BasicBlock(data) => data.behavior(&j), - CharacterState::Roll(data) => data.behavior(&j), - CharacterState::Wielding => states::wielding::Data.behavior(&j), - CharacterState::Equipping(data) => data.behavior(&j), - CharacterState::ComboMelee(data) => data.behavior(&j), - CharacterState::BasicMelee(data) => data.behavior(&j), - CharacterState::BasicRanged(data) => data.behavior(&j), - CharacterState::Boost(data) => data.behavior(&j), - CharacterState::DashMelee(data) => data.behavior(&j), - CharacterState::LeapMelee(data) => data.behavior(&j), - CharacterState::SpinMelee(data) => data.behavior(&j), - CharacterState::ChargedMelee(data) => data.behavior(&j), - CharacterState::ChargedRanged(data) => data.behavior(&j), - CharacterState::RepeaterRanged(data) => data.behavior(&j), - CharacterState::Shockwave(data) => data.behavior(&j), - CharacterState::BasicBeam(data) => data.behavior(&j), - CharacterState::BasicAura(data) => data.behavior(&j), - CharacterState::Blink(data) => data.behavior(&j), - CharacterState::BasicSummon(data) => data.behavior(&j), - CharacterState::SelfBuff(data) => data.behavior(&j), - CharacterState::SpriteSummon(data) => data.behavior(&j), - CharacterState::UseItem(data) => data.behavior(&j), - }; - - local_emitter.append(&mut state_update.local_events); - server_emitter.append(&mut state_update.server_events); - incorporate_update(&mut join_struct, state_update, &mut server_emitter); + let state_update = j.character.behavior(&j); + Self::publish_state_update( + &mut join_struct, + state_update, + &mut local_emitter, + &mut server_emitter, + ); + } + } +} + +impl Sys { + fn publish_state_update( + join: &mut JoinStruct, + mut state_update: StateUpdate, + local_emitter: &mut Emitter, + server_emitter: &mut Emitter, + ) { + local_emitter.append(&mut state_update.local_events); + server_emitter.append(&mut state_update.server_events); + + // TODO: if checking equality is expensive use optional field in StateUpdate + if *join.char_state != state_update.character { + *join.char_state = state_update.character + }; + *join.pos = state_update.pos; + *join.vel = state_update.vel; + *join.ori = state_update.ori; + *join.density = state_update.density; + // Note: might be changed every tick by timer anyway + if *join.energy != state_update.energy { + *join.energy = state_update.energy + }; + join.controller + .queued_inputs + .append(&mut state_update.queued_inputs); + for input in state_update.removed_inputs { + join.controller.queued_inputs.remove(&input); + } + if state_update.swap_equipped_weapons { + server_emitter.emit(ServerEvent::InventoryManip( + join.entity, + InventoryManip::SwapEquippedWeapons, + )); } } } diff --git a/common/systems/src/melee.rs b/common/systems/src/melee.rs index 20ac8b54a9..433f6b48c5 100644 --- a/common/systems/src/melee.rs +++ b/common/systems/src/melee.rs @@ -1,9 +1,9 @@ use common::{ - combat::{AttackSource, AttackerInfo, TargetInfo}, + combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, comp::{ agent::{Sound, SoundKind}, - Body, CharacterState, Combo, Energy, Group, Health, Inventory, Melee, Ori, Pos, Scale, - Stats, + Body, CharacterState, Combo, Energy, Group, Health, Inventory, Melee, Ori, Player, Pos, + Scale, Stats, }, event::{EventBus, ServerEvent}, outcome::Outcome, @@ -22,6 +22,7 @@ use vek::*; pub struct ReadData<'a> { time: Read<'a, Time>, entities: Entities<'a>, + players: ReadStorage<'a, Player>, uids: ReadStorage<'a, Uid>, positions: ReadStorage<'a, Pos>, orientations: ReadStorage<'a, Ori>, @@ -115,7 +116,7 @@ impl<'a> System<'a> for Sys { let rad_b = body_b.radius() * scale_b; // Check if entity is dodging - let is_dodge = read_data + let target_dodging = read_data .char_states .get(target) .map_or(false, |c_s| c_s.is_melee_dodge()); @@ -161,12 +162,22 @@ impl<'a> System<'a> for Sys { char_state: read_data.char_states.get(target), }; - let is_applied = melee_attack.attack.apply_attack( + let may_harm = combat::may_harm( + read_data.players.get(attacker), + read_data.players.get(target), + ); + + let attack_options = AttackOptions { + target_dodging, + may_harm, target_group, + }; + + let is_applied = melee_attack.attack.apply_attack( attacker_info, target_info, dir, - is_dodge, + attack_options, 1.0, AttackSource::Melee, |e| server_emitter.emit(e), diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index 39a5e62fb0..e404e24c72 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -69,8 +69,8 @@ fn integrate_forces( // This way we can only ever lose velocity and will never experience a reverse // in direction from events such as falling into water at high velocities. if new_v.dot(vel.0) < 0.0 { - // Multiply by a factor to prevent full stop, as this can cause things to get - // stuck in high-density medium + // Multiply by a factor to prevent full stop, + // as this can cause things to get stuck in high-density medium vel.0 -= vel.0.projected(&impulse) * 0.9; } else { vel.0 = new_v; @@ -318,8 +318,7 @@ impl<'a> PhysicsData<'a> { char_state_maybe, )| { let is_sticky = sticky.is_some(); - // Code reviewers: remind me to check why on_ground was true instead of false here? - let is_mid_air = physics.on_wall.is_none() && physics.on_ground.is_some(); + let is_mid_air = physics.on_surface().is_none(); let mut entity_entity_collision_checks = 0; let mut entity_entity_collisions = 0; @@ -425,25 +424,43 @@ impl<'a> PhysicsData<'a> { entity_entity_collisions += 1; } - // Don't apply e2e pushback to entities that are in a forced movement state - // (e.g. roll, leapmelee). This allows leaps to work properly (since you won't - // get pushed away before delivering the hit), and allows rolling through an - // enemy when trapped (e.g. with minotaur). This allows using e2e pushback to - // gain speed by jumping out of a roll while in the middle of a collider, this - // is an intentional combat mechanic. - let forced_movement = matches!(char_state_maybe, Some(cs) if cs.is_forced_movement()); - - // Don't apply repulsive force to projectiles or if we're colliding with a - // terrain-like entity, or if we are a terrain-like entity + // Don't apply e2e pushback to entities + // that are in a forced movement state + // (e.g. roll, leapmelee). // - // Don't apply force when entity is a sticky which is on the + // This allows leaps to work properly + // (since you won't get pushed away before + // delivering the hit), and allows + // rolling through an enemy when trapped + // (e.g. with minotaur). + // + // This allows using e2e pushback to + // gain speed by jumping out of a roll + // while in the middle of a collider, this + // is an intentional combat mechanic. + let forced_movement = matches!( + char_state_maybe, + Some(cs) if cs.is_forced_movement()); + + // Don't apply repulsive force + // to projectiles + // + // or if we're colliding with a + // terrain-like entity, + // + // or if we are a terrain-like entity + // + // Don't apply force when entity + // is a sticky which is on the // ground (or on the wall) if !forced_movement - && (!is_sticky - || is_mid_air) + && (!is_sticky || is_mid_air) && diff.magnitude_squared() > 0.0 && !is_projectile - && !matches!(collider_other,Some(Collider::Voxel { .. })) + && !matches!( + collider_other, + Some(Collider::Voxel { .. }) + ) && !matches!(collider, Some(Collider::Voxel { .. })) { let force = 400.0 @@ -668,6 +685,8 @@ impl<'a> PhysicsData<'a> { ) .join() { + // Note: updating ori with the rest of the cache values above was attempted but + // it did not work (investigate root cause?) previous_phys_cache.ori = ori.to_quat(); } drop(guard); @@ -890,7 +909,8 @@ impl<'a> PhysicsData<'a> { .try_normalized() .unwrap_or_else(Vec3::zero); - // See whether we're on the top/bottom of a block, or the side + // See whether we're on the top/bottom of a block, + // or the side if block_rpos.z.abs() > block_rpos.xy().map(|e| e.abs()).reduce_partial_max() { @@ -940,11 +960,12 @@ impl<'a> PhysicsData<'a> { }, } - // Compute center and radius of tick path bounding sphere for the entity - // for broad checks of whether it will collide with a voxel collider + // Compute center and radius of tick path bounding sphere + // for the entity for broad checks of whether it will + // collide with a voxel collider let path_sphere = { - // TODO: duplicated with maintain_pushback_cache, make a common function - // to call to compute all this info? + // TODO: duplicated with maintain_pushback_cache, + // make a common function to call to compute all this info? let z_limits = calc_z_limit(character_state, Some(collider)); let z_limits = (z_limits.0 * scale, z_limits.1 * scale); let half_height = (z_limits.1 - z_limits.0) / 2.0; @@ -1538,7 +1559,10 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( .unwrap_or(0.0); vel.0.z = 0.0; pos.0.z = (pos.0.z - 0.1).floor() + snap_height; - physics_state.on_ground = on_ground; + physics_state.on_ground = terrain + .get(Vec3::new(pos.0.x, pos.0.y, pos.0.z - 0.01).map(|e| e.floor() as i32)) + .ok() + .copied(); } let player_aabb = Aabb { diff --git a/common/systems/src/projectile.rs b/common/systems/src/projectile.rs index e9d7a5a3e8..f5c4b3d4c8 100644 --- a/common/systems/src/projectile.rs +++ b/common/systems/src/projectile.rs @@ -1,11 +1,11 @@ use common::{ - combat::{AttackSource, AttackerInfo, TargetInfo}, + combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, comp::{ agent::{Sound, SoundKind}, projectile, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory, - Ori, PhysicsState, Pos, Projectile, Stats, Vel, + Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel, }, - event::{EventBus, ServerEvent}, + event::{Emitter, EventBus, ServerEvent}, outcome::Outcome, resources::{DeltaTime, Time}, uid::{Uid, UidAllocator}, @@ -15,8 +15,8 @@ use common::{ use common_ecs::{Job, Origin, Phase, System}; use rand::{thread_rng, Rng}; use specs::{ - saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, SystemData, - World, Write, WriteStorage, + saveload::MarkerAllocator, shred::ResourceId, Entities, Entity as EcsEntity, Join, Read, + ReadStorage, SystemData, World, Write, WriteStorage, }; use std::time::Duration; use vek::*; @@ -25,6 +25,7 @@ use vek::*; pub struct ReadData<'a> { time: Read<'a, Time>, entities: Entities<'a>, + players: ReadStorage<'a, Player>, dt: Read<'a, DeltaTime>, uid_allocator: Read<'a, UidAllocator>, server_bus: Read<'a, EventBus>, @@ -114,94 +115,36 @@ impl<'a> System<'a> for Sys { } let projectile = &mut *projectile; + + let entity_of = + |uid: Uid| read_data.uid_allocator.retrieve_entity_internal(uid.into()); for effect in projectile.hit_entity.drain(..) { - match effect { - projectile::Effect::Attack(attack) => { - if let Some(target) = read_data - .uid_allocator - .retrieve_entity_internal(other.into()) - { - if let (Some(pos), Some(ori)) = - (read_data.positions.get(target), orientations.get(entity)) - { - let dir = ori.look_dir(); + let owner = projectile.owner.and_then(entity_of); + let projectile_info = ProjectileInfo { + entity, + effect, + owner_uid: projectile.owner, + owner, + ori: orientations.get(entity), + pos, + }; - let owner_entity = projectile.owner.and_then(|u| { - read_data.uid_allocator.retrieve_entity_internal(u.into()) - }); + let target = entity_of(other); + let projectile_target_info = ProjectileTargetInfo { + uid: other, + entity: target, + target_group, + ori: target.and_then(|target| orientations.get(target)), + }; - let attacker_info = - owner_entity.zip(projectile.owner).map(|(entity, uid)| { - AttackerInfo { - entity, - uid, - energy: read_data.energies.get(entity), - combo: read_data.combos.get(entity), - inventory: read_data.inventories.get(entity), - } - }); - - let target_info = TargetInfo { - entity: target, - uid: other, - inventory: read_data.inventories.get(target), - stats: read_data.stats.get(target), - health: read_data.healths.get(target), - pos: pos.0, - ori: orientations.get(target), - char_state: read_data.character_states.get(target), - }; - - if let Some(&body) = read_data.bodies.get(entity) { - outcomes.push(Outcome::ProjectileHit { - pos: pos.0, - body, - vel: read_data - .velocities - .get(entity) - .map_or(Vec3::zero(), |v| v.0), - source: projectile.owner, - target: read_data.uids.get(target).copied(), - }); - } - - attack.apply_attack( - target_group, - attacker_info, - target_info, - dir, - false, - 1.0, - AttackSource::Projectile, - |e| server_emitter.emit(e), - |o| outcomes.push(o), - ); - } - } - }, - projectile::Effect::Explode(e) => { - server_emitter.emit(ServerEvent::Explosion { - pos: pos.0, - explosion: e, - owner: projectile.owner, - }); - }, - projectile::Effect::Vanish => { - server_emitter.emit(ServerEvent::Destroy { - entity, - cause: HealthSource::World, - }); - projectile_vanished = true; - }, - projectile::Effect::Possess => { - if other != projectile.owner.unwrap() { - if let Some(owner) = projectile.owner { - server_emitter.emit(ServerEvent::Possess(owner, other)); - } - } - }, - _ => {}, - } + dispatch_hit( + projectile_info, + projectile_target_info, + &read_data, + &mut projectile_vanished, + &mut outcomes, + &mut server_emitter, + ); } if projectile_vanished { @@ -214,8 +157,19 @@ impl<'a> System<'a> for Sys { for effect in projectile.hit_solid.drain(..) { match effect { projectile::Effect::Explode(e) => { + // We offset position a little back on the way, + // so if we hit non-exploadable block + // we still can affect blocks around it. + // + // TODO: orientation of fallen projectile is + // fragile heuristic for direction, find more + // robust method. + let projectile_direction = orientations + .get(entity) + .map_or_else(Vec3::zero, |ori| ori.look_vec()); + let offset = -0.2 * projectile_direction; server_emitter.emit(ServerEvent::Explosion { - pos: pos.0, + pos: pos.0 + offset, explosion: e, owner: projectile.owner, }); @@ -253,3 +207,141 @@ impl<'a> System<'a> for Sys { } } } + +struct ProjectileInfo<'a> { + entity: EcsEntity, + effect: projectile::Effect, + owner_uid: Option, + owner: Option, + ori: Option<&'a Ori>, + pos: &'a Pos, +} + +struct ProjectileTargetInfo<'a> { + uid: Uid, + entity: Option, + target_group: GroupTarget, + ori: Option<&'a Ori>, +} + +fn dispatch_hit( + projectile_info: ProjectileInfo, + projectile_target_info: ProjectileTargetInfo, + read_data: &ReadData, + projectile_vanished: &mut bool, + outcomes: &mut Vec, + server_emitter: &mut Emitter, +) { + match projectile_info.effect { + projectile::Effect::Attack(attack) => { + let target_uid = projectile_target_info.uid; + let target = if let Some(entity) = projectile_target_info.entity { + entity + } else { + return; + }; + + let (target_pos, projectile_dir) = { + let target_pos = read_data.positions.get(target); + let projectile_ori = projectile_info.ori; + match target_pos.zip(projectile_ori) { + Some((tgt_pos, proj_ori)) => { + let Pos(tgt_pos) = tgt_pos; + (*tgt_pos, proj_ori.look_dir()) + }, + None => return, + } + }; + + let owner = projectile_info.owner; + let projectile_entity = projectile_info.entity; + + let attacker_info = + owner + .zip(projectile_info.owner_uid) + .map(|(entity, uid)| AttackerInfo { + entity, + uid, + energy: read_data.energies.get(entity), + combo: read_data.combos.get(entity), + inventory: read_data.inventories.get(entity), + }); + + let target_info = TargetInfo { + entity: target, + uid: target_uid, + inventory: read_data.inventories.get(target), + stats: read_data.stats.get(target), + health: read_data.healths.get(target), + pos: target_pos, + ori: projectile_target_info.ori, + char_state: read_data.character_states.get(target), + }; + + // TODO: Is it possible to have projectile without body?? + if let Some(&body) = read_data.bodies.get(projectile_entity) { + outcomes.push(Outcome::ProjectileHit { + pos: target_pos, + body, + vel: read_data + .velocities + .get(projectile_entity) + .map_or(Vec3::zero(), |v| v.0), + source: projectile_info.owner_uid, + target: read_data.uids.get(target).copied(), + }); + } + + let may_harm = combat::may_harm( + owner.and_then(|owner| read_data.players.get(owner)), + read_data.players.get(target), + ); + + let attack_options = AttackOptions { + // They say witchers can dodge arrows, + // but we don't have witchers + target_dodging: false, + may_harm, + target_group: projectile_target_info.target_group, + }; + + attack.apply_attack( + attacker_info, + target_info, + projectile_dir, + attack_options, + 1.0, + AttackSource::Projectile, + |e| server_emitter.emit(e), + |o| outcomes.push(o), + ); + }, + projectile::Effect::Explode(e) => { + let Pos(pos) = *projectile_info.pos; + let owner_uid = projectile_info.owner_uid; + server_emitter.emit(ServerEvent::Explosion { + pos, + explosion: e, + owner: owner_uid, + }); + }, + projectile::Effect::Vanish => { + let entity = projectile_info.entity; + server_emitter.emit(ServerEvent::Destroy { + entity, + cause: HealthSource::World, + }); + *projectile_vanished = true; + }, + projectile::Effect::Possess => { + let target_uid = projectile_target_info.uid; + let owner_uid = projectile_info.owner_uid; + if let Some(owner_uid) = owner_uid { + if target_uid != owner_uid { + server_emitter.emit(ServerEvent::Possess(owner_uid, target_uid)); + } + } + }, + projectile::Effect::Stick => {}, + } +} diff --git a/common/systems/src/shockwave.rs b/common/systems/src/shockwave.rs index 2997821811..86ad74eca7 100644 --- a/common/systems/src/shockwave.rs +++ b/common/systems/src/shockwave.rs @@ -1,9 +1,9 @@ use common::{ - combat::{AttackSource, AttackerInfo, TargetInfo}, + combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, comp::{ agent::{Sound, SoundKind}, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory, Ori, - PhysicsState, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats, + PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats, }, event::{EventBus, ServerEvent}, outcome::Outcome, @@ -25,6 +25,7 @@ pub struct ReadData<'a> { entities: Entities<'a>, server_bus: Read<'a, EventBus>, time: Read<'a, Time>, + players: ReadStorage<'a, Player>, dt: Read<'a, DeltaTime>, uid_allocator: Read<'a, UidAllocator>, uids: ReadStorage<'a, Uid>, @@ -208,12 +209,22 @@ impl<'a> System<'a> for Sys { char_state: read_data.character_states.get(target), }; - shockwave.properties.attack.apply_attack( + let may_harm = combat::may_harm( + shockwave_owner.and_then(|owner| read_data.players.get(owner)), + read_data.players.get(target), + ); + let attack_options = AttackOptions { + // Trying roll during earthquake isn't the best idea + target_dodging: false, + may_harm, target_group, + }; + + shockwave.properties.attack.apply_attack( attacker_info, target_info, dir, - false, + attack_options, 1.0, AttackSource::Shockwave, |e| server_emitter.emit(e), diff --git a/flake.lock b/flake.lock index 2a87ca1208..196d6d02bb 100644 --- a/flake.lock +++ b/flake.lock @@ -24,11 +24,11 @@ "rustOverlay": "rustOverlay" }, "locked": { - "lastModified": 1625378930, - "narHash": "sha256-iBnCmrLweBmtN9VkG6AX4wSE5UUF//4VtfX6Ebhbaew=", + "lastModified": 1627940369, + "narHash": "sha256-KtY837WKsX9B/pIKFDKzN0wl1t3et1JZjMjGa7SAZxI=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "4d6efcfd0bc6c6235b180837257ab8020f108c23", + "rev": "fac8518469e226db4805ff80788979c847b0c322", "type": "github" }, "original": { @@ -39,11 +39,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1625375231, - "narHash": "sha256-dbp2RRftypbAYad/xhDBBG0N7s1WQW+T4jazimvtvRo=", + "lastModified": 1627814220, + "narHash": "sha256-P+MDgdZw2CBk9X1ZZaUgHgN+32pTfLFf3XVIBOXirI4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "8aff53d3cf23b5eb67c0ba9ba2392d694ade3930", + "rev": "ab5b6828af26215bf2646c31961da5d3749591ef", "type": "github" }, "original": { @@ -62,11 +62,11 @@ "rustOverlay": { "flake": false, "locked": { - "lastModified": 1625364738, - "narHash": "sha256-TPlpEcywGnB8jNIPOlCqFVbioskGOpasrVhxdw0BHzA=", + "lastModified": 1627870491, + "narHash": "sha256-0Myg04QOIcTN1RhgfRNx0i/iCRyVyf/Z6rJxZUmot5k=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "f08a653d9d6bbf06b5cc5dd1e570f1c449acbcf3", + "rev": "71d825269cfaa30605d058bd92381be9af87b0be", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index b6d8c63207..8f121f9fda 100644 --- a/flake.nix +++ b/flake.nix @@ -67,16 +67,10 @@ # veloren-world = oldAttrs: { # crateBin = lib.filter (bin: bin.name != "chunk_compression_benchmarks") oldAttrs.crateBin; # }; - shaderc-sys = _: - let SHADERC_LIB_DIR = "${common.pkgs.shaderc.lib}/lib"; in - { - inherit SHADERC_LIB_DIR; - propagatedEnv = { inherit SHADERC_LIB_DIR; }; - }; veloren-client = oldAttrs: { crateBin = lib.filter (bin: bin.name != "bot") oldAttrs.crateBin; }; - veloren-i18n = oldAttrs: { + veloren-voxygen-i18n = oldAttrs: { crateBin = lib.filter (bin: bin.name != "i18n-check") oldAttrs.crateBin; }; veloren-common = oldAttrs: { diff --git a/nix/README.md b/nix/README.md index 5bc94b991c..5ee218f80b 100644 --- a/nix/README.md +++ b/nix/README.md @@ -22,6 +22,11 @@ git lfs install --local && git lfs fetch && git lfs checkout ``` This should be automatically done if you use the development shell. +If you get an issue such as `WARN gfx_backend_vulkan: Unable to create Vulkan instance: VkError(ERROR_INCOMPATIBLE_DRIVER)`, +it might be that your system nixpkgs version and veloren repo nixpkgs version might be too far apart. In that case, you can try +changing your system nixpkgs to the unstable channel, or change the `nixpkgs` input in the `flake.nix` to match your system +nixpkgs. + ## Usage for players ### With flakes diff --git a/server-cli/Cargo.toml b/server-cli/Cargo.toml index bbe0bd15f4..3df973d6d9 100644 --- a/server-cli/Cargo.toml +++ b/server-cli/Cargo.toml @@ -16,9 +16,10 @@ This package includes the official server CLI. [features] worldgen = ["server/worldgen"] +persistent_world = ["server/persistent_world"] # needed to stay compatible with voxygens format default-publish = ["default"] -default = ["worldgen"] +default = ["worldgen", "persistent_world"] tracy = ["common-frontend/tracy"] plugins = ["server/plugins"] diff --git a/server-cli/src/cli.rs b/server-cli/src/cli.rs index 421ac93b5d..c416e3db45 100644 --- a/server-cli/src/cli.rs +++ b/server-cli/src/cli.rs @@ -8,15 +8,13 @@ use tracing::error; pub enum Admin { /// Adds an admin Add { - #[structopt(short, long)] /// Name of the admin to whom to assign a role username: String, /// role to assign to the admin - #[structopt(short, long, possible_values = &comp::AdminRole::variants(), case_insensitive = true)] + #[structopt(possible_values = &comp::AdminRole::variants(), case_insensitive = true)] role: comp::AdminRole, }, Remove { - #[structopt(short, long)] /// Name of the admin from whom to remove any existing roles username: String, }, diff --git a/server/Cargo.toml b/server/Cargo.toml index d43a6a0aee..a1855ae590 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -8,8 +8,9 @@ edition = "2018" worldgen = [] simd = ["vek/platform_intrinsics"] plugins = ["common-state/plugins"] +persistent_world = [] -default = ["worldgen", "plugins", "simd"] +default = ["worldgen", "plugins", "simd", "persistent_world"] [dependencies] common = { package = "veloren-common", path = "../common" } @@ -26,6 +27,7 @@ network = { package = "veloren-network", path = "../network", features = ["metri specs = { git = "https://github.com/amethyst/specs.git", features = ["shred-derive"], rev = "f985bec5d456f7b0dd8aae99848f9473c2cd9d46" } specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "8be2abcddf8f524cb5876e8dd20a7e47cfaf7573" } +bincode = "1.3.2" num_cpus = "1.0" tracing = "0.1" vek = { version = "0.14.1", features = ["serde"] } diff --git a/server/src/character_creator.rs b/server/src/character_creator.rs index 688b0829fd..9c193262df 100644 --- a/server/src/character_creator.rs +++ b/server/src/character_creator.rs @@ -4,45 +4,54 @@ use common::comp::{ }; use specs::{Entity, WriteExpect}; -const VALID_STARTER_ITEMS: [&str; 6] = [ - "common.items.weapons.hammer.starter_hammer", - "common.items.weapons.bow.starter", - "common.items.weapons.axe.starter_axe", - "common.items.weapons.staff.starter_staff", - "common.items.weapons.sword.starter", - "common.items.weapons.sceptre.starter_sceptre", +const VALID_STARTER_ITEMS: &[[Option<&str>; 2]] = &[ + [None, None], // Not used with an unmodified client but should still be allowed (zesterer) + [Some("common.items.weapons.hammer.starter_hammer"), None], + [Some("common.items.weapons.bow.starter"), None], + [Some("common.items.weapons.axe.starter_axe"), None], + [Some("common.items.weapons.staff.starter_staff"), None], + [Some("common.items.weapons.sword.starter"), None], + [ + Some("common.items.weapons.sword_1h.starter"), + Some("common.items.weapons.sword_1h.starter"), + ], ]; +#[derive(Debug)] +pub enum CreationError { + InvalidWeapon, + InvalidBody, +} + pub fn create_character( entity: Entity, player_uuid: String, character_alias: String, - character_tool: Option, + character_mainhand: Option, + character_offhand: Option, body: Body, character_updater: &mut WriteExpect<'_, CharacterUpdater>, -) { +) -> Result<(), CreationError> { // quick fix whitelist validation for now; eventually replace the // `Option` with an index into a server-provided list of starter // items, and replace `comp::body::Body` with `comp::body::humanoid::Body` // throughout the messages involved - let tool_id = match character_tool { - Some(tool_id) if VALID_STARTER_ITEMS.contains(&&*tool_id) => tool_id, - _ => return, - }; if !matches!(body, Body::Humanoid(_)) { - return; + return Err(CreationError::InvalidBody); } - - let stats = Stats::new(character_alias.to_string()); - let skill_set = SkillSet::default(); - + if !VALID_STARTER_ITEMS.contains(&[character_mainhand.as_deref(), character_offhand.as_deref()]) + { + return Err(CreationError::InvalidWeapon); + }; + // The client sends None if a weapon hand is empty let loadout = LoadoutBuilder::empty() .defaults() - .active_mainhand(Some(Item::new_from_asset_expect(&tool_id))) + .active_mainhand(character_mainhand.map(|x| Item::new_from_asset_expect(&x))) + .active_offhand(character_offhand.map(|x| Item::new_from_asset_expect(&x))) .build(); - let mut inventory = Inventory::new_with_loadout(loadout); - + let stats = Stats::new(character_alias.to_string()); + let skill_set = SkillSet::default(); // Default items for new characters inventory .push(Item::new_from_asset_expect( @@ -61,4 +70,21 @@ pub fn create_character( character_alias, (body, stats, skill_set, inventory, waypoint, Vec::new()), ); + Ok(()) +} + +// Error handling +impl core::fmt::Display for CreationError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + CreationError::InvalidWeapon => write!( + f, + "Invalid weapon.\nServer and client might be partially incompatible." + ), + CreationError::InvalidBody => write!( + f, + "Invalid Body.\nServer and client might be partially incompatible" + ), + } + } } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 2a7bb6f0a4..960044b230 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1,11 +1,12 @@ //! # Implementing new commands. -//! To implement a new command, add an instance of `ChatCommand` to -//! `CHAT_COMMANDS` and provide a handler function. +//! To implement a new command provide a handler function +//! in [do_command]. use crate::{ settings::{ Ban, BanAction, BanInfo, EditableSetting, SettingError, WhitelistInfo, WhitelistRecord, }, + sys::terrain::NpcData, wiring::{Logic, OutputFormula}, Server, SpawnPoint, StateExt, }; @@ -14,7 +15,9 @@ use authc::Uuid; use chrono::{NaiveTime, Timelike, Utc}; use common::{ assets, - cmd::{ChatCommand, BUFF_PACK, BUFF_PARSER}, + cmd::{ + ChatCommand, BUFF_PACK, BUFF_PARSER, ITEM_SPECS, KIT_MANIFEST_PATH, PRESET_MANIFEST_PATH, + }, comp::{ self, aura::{Aura, AuraKind, AuraTarget}, @@ -26,6 +29,7 @@ use common::{ depot, effect::Effect, event::{EventBus, ServerEvent}, + generation::EntityInfo, npc::{self, get_npc_name}, resources::{PlayerPhysicsSettings, TimeOfDay}, terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize}, @@ -66,6 +70,7 @@ impl ChatCommandExt for ChatCommand { } type CmdResult = Result; + /// Handler function called when the command is executed. /// # Arguments /// * `&mut Server` - the `Server` instance executing the command. @@ -138,6 +143,7 @@ fn do_command( ChatCommand::Lantern => handle_lantern, ChatCommand::Light => handle_light, ChatCommand::MakeBlock => handle_make_block, + ChatCommand::MakeNpc => handle_make_npc, ChatCommand::MakeSprite => handle_make_sprite, ChatCommand::Motd => handle_motd, ChatCommand::Object => handle_object, @@ -521,13 +527,21 @@ fn handle_make_block( args: Vec, action: &ChatCommand, ) -> CmdResult<()> { - if let Some(block_name) = parse_args!(args, String) { + if let (Some(block_name), r, g, b) = parse_args!(args, String, u8, u8, u8) { if let Ok(bk) = BlockKind::from_str(block_name.as_str()) { let pos = position(server, target, "target")?; - server.state.set_block( - pos.0.map(|e| e.floor() as i32), - Block::new(bk, Rgb::broadcast(255)), - ); + let new_block = Block::new(bk, Rgb::new(r, g, b).map(|e| e.unwrap_or(255))); + let pos = pos.0.map(|e| e.floor() as i32); + server.state.set_block(pos, new_block); + #[cfg(feature = "persistent_world")] + if let Some(terrain_persistence) = server + .state + .ecs() + .try_fetch_mut::() + .as_mut() + { + terrain_persistence.set_block(pos, new_block); + } Ok(()) } else { Err(format!("Invalid block kind: {}", block_name)) @@ -537,6 +551,93 @@ fn handle_make_block( } } +fn handle_make_npc( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: Vec, + action: &ChatCommand, +) -> CmdResult<()> { + let (entity_config, number) = parse_args!(args, String, i8); + + let entity_config = entity_config.ok_or_else(|| action.help_string())?; + let number = match number { + Some(i8::MIN..=0) => { + return Err("Number of entities should be at least 1".to_owned()); + }, + Some(50..=i8::MAX) => { + return Err("Number of entities should be less than 50".to_owned()); + }, + Some(number) => number, + None => 1, + }; + + let rng = &mut rand::thread_rng(); + for _ in 0..number { + let comp::Pos(pos) = position(server, target, "target")?; + let entity_info = EntityInfo::at(pos).with_asset_expect(&entity_config); + match NpcData::from_entity_info(entity_info, rng) { + NpcData::Waypoint(_) => { + return Err("Waypoint spawning is not implemented".to_owned()); + }, + NpcData::Data { + loadout, + pos, + stats, + skill_set, + poise, + health, + body, + agent, + alignment, + scale, + drop_item, + } => { + let inventory = Inventory::new_with_loadout(loadout); + + let mut entity_builder = server + .state + .create_npc(pos, stats, skill_set, health, poise, inventory, body) + .with(alignment) + .with(scale) + .with(comp::Vel(Vec3::new(0.0, 0.0, 0.0))) + .with(comp::MountState::Unmounted); + + if let Some(agent) = agent { + entity_builder = entity_builder.with(agent); + } + + if let Some(drop_item) = drop_item { + entity_builder = entity_builder.with(comp::ItemDrop(drop_item)); + } + + // Some would say it's a hack, some would say it's incomplete + // simulation. But this is what we do to avoid PvP between npc. + use comp::Alignment; + let npc_group = match alignment { + Alignment::Enemy => Some(comp::group::ENEMY), + Alignment::Npc | Alignment::Tame => Some(comp::group::NPC), + Alignment::Wild | Alignment::Passive | Alignment::Owned(_) => None, + }; + if let Some(group) = npc_group { + entity_builder = entity_builder.with(group); + } + entity_builder.build(); + }, + }; + } + + server.notify_client( + client, + ServerGeneral::server_msg( + ChatType::CommandInfo, + format!("Spawned {} entities from config: {}", number, entity_config), + ), + ); + + Ok(()) +} + fn handle_make_sprite( server: &mut Server, _client: EcsEntity, @@ -555,6 +656,15 @@ fn handle_make_sprite( .unwrap_or_else(|| Block::air(SpriteKind::Empty)) .with_sprite(sk); server.state.set_block(pos, new_block); + #[cfg(feature = "persistent_world")] + if let Some(terrain_persistence) = server + .state + .ecs() + .try_fetch_mut::() + .as_mut() + { + terrain_persistence.set_block(pos, new_block); + } Ok(()) } else { Err(format!("Invalid sprite kind: {}", sprite_name)) @@ -1005,11 +1115,12 @@ fn handle_spawn( let ai = opt_ai.unwrap_or(true); let pos = position(server, target, "target")?; - let agent = if let comp::Alignment::Owned(_) | comp::Alignment::Npc = alignment { - comp::Agent::default() - } else { - comp::Agent::default().with_patrol_origin(pos.0) - }; + let mut agent = comp::Agent::from_body(&body()); + + // If unowned, the agent should stay in a particular place + if !matches!(alignment, comp::Alignment::Owned(_)) { + agent = agent.with_patrol_origin(pos.0); + } for _ in 0..amount { let vel = Vec3::new( @@ -1160,7 +1271,7 @@ fn handle_spawn_airship( if let Some(pos) = destination { let (kp, ki, kd) = comp::agent::pid_coefficients(&comp::Body::Ship(ship)); fn pure_z(sp: Vec3, pv: Vec3) -> f32 { (sp - pv).z } - let agent = comp::Agent::default() + let agent = comp::Agent::from_body(&comp::Body::Ship(ship)) .with_destination(pos) .with_position_pid_controller(comp::PidController::new(kp, ki, kd, pos, 0.0, pure_z)); builder = builder.with(agent); @@ -1389,12 +1500,23 @@ fn handle_build( .write_storage::() .get_mut(target) { - let toggle_string = if can_build.enabled { "off" } else { "on" }; - let chat_msg = ServerGeneral::server_msg( - ChatType::CommandInfo, - format!("Toggled {:?} build mode!", toggle_string), - ); can_build.enabled ^= true; + + let toggle_string = if can_build.enabled { "on" } else { "off" }; + let msg = format!( + "Toggled build mode {}.{}", + toggle_string, + if !can_build.enabled { + "" + } else if server.settings().experimental_terrain_persistence { + " Experimental terrain persistence is enabled. The server will attempt to persist \ + changes, but this is not guaranteed." + } else { + " Changes will not be persisted when a chunk unloads." + }, + ); + + let chat_msg = ServerGeneral::server_msg(ChatType::CommandInfo, msg); if client != target { server.notify_client(target, chat_msg.clone()); } @@ -1567,49 +1689,112 @@ fn handle_kill_npcs( fn handle_kit( server: &mut Server, - _client: EcsEntity, + client: EcsEntity, target: EcsEntity, args: Vec, action: &ChatCommand, ) -> CmdResult<()> { - if let Some(name) = parse_args!(args, String) { - if let Ok(kits) = common::cmd::KitManifest::load("server.manifests.kits") { - let kits = kits.read(); - if let Some(kit) = kits.0.get(&name) { - if let (Some(mut target_inventory), mut target_inv_update) = ( - server - .state() - .ecs() - .write_storage::() - .get_mut(target), - server.state.ecs().write_storage::(), - ) { - for (item_id, quantity) in kit.iter() { - if let Ok(mut item) = comp::Item::new_from_asset(item_id) { - let _ = item.set_amount(*quantity); - let (_, _) = ( - target_inventory.push(item), - target_inv_update.insert( - target, - comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug), - ), - ); - } else { - warn!("Unknown item: {}", &item_id); - } - } - Ok(()) - } else { - Err("Could not get inventory".to_string()) - } - } else { - Err(format!("Kit '{}' not found", name)) + use common::cmd::KitManifest; + + let notify = |server: &mut Server, kit_name: &str| { + server.notify_client( + client, + ServerGeneral::server_msg(ChatType::CommandInfo, format!("Gave kit: {}", kit_name)), + ); + }; + let name = parse_args!(args, String).ok_or_else(|| action.help_string())?; + + match name.as_str() { + "all" => { + // TODO: we will probably want to handle modular items here too + let items = &ITEM_SPECS; + let res = push_kit( + items.iter().map(|item_id| (item_id.as_str(), 1)), + items.len(), + server, + target, + ); + if res.is_ok() { + notify(server, "all"); } - } else { - Err("Could not load manifest file 'server.manifests.kits'".to_string()) + res + }, + kit_name => { + let kits = KitManifest::load(KIT_MANIFEST_PATH) + .map(|kits| kits.read()) + .map_err(|_| format!("Could not load manifest file {}", KIT_MANIFEST_PATH))?; + + let kit = kits + .0 + .get(kit_name) + .ok_or(format!("Kit '{}' not found", kit_name))?; + + let res = push_kit( + kit.iter() + .map(|&(ref item_id, quantity)| (item_id.as_str(), quantity)), + kit.len(), + server, + target, + ); + if res.is_ok() { + notify(server, kit_name); + } + res + }, + } +} + +fn push_kit<'a, I>(kit: I, count: usize, server: &mut Server, target: EcsEntity) -> CmdResult<()> +where + I: Iterator, +{ + if let (Some(mut target_inventory), mut target_inv_update) = ( + server + .state() + .ecs() + .write_storage::() + .get_mut(target), + server.state.ecs().write_storage::(), + ) { + // TODO: implement atomic `insert_all_or_nothing` on Inventory + if target_inventory.free_slots() < count { + return Err("Inventory doesn't have enough slots".to_owned()); } + for (item_id, quantity) in kit { + let mut item = comp::Item::new_from_asset(item_id) + .map_err(|_| format!("Unknown item: {}", item_id))?; + let mut res = Ok(()); + + // Either push stack or push one by one. + if item.is_stackable() { + // FIXME: in theory, this can fail, + // but we don't have stack sizes yet. + let _ = item.set_amount(quantity); + res = target_inventory.push(item); + let _ = target_inv_update.insert( + target, + comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug), + ); + } else { + let ability_map = server.state.ecs().read_resource::(); + let msm = server.state.ecs().read_resource::(); + for _ in 0..quantity { + res = target_inventory.push(item.duplicate(&ability_map, &msm)); + let _ = target_inv_update.insert( + target, + comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug), + ); + } + } + // I think it's possible to pick-up item during this loop + // and fail into case where you had space but now you don't? + if res.is_err() { + return Err("Can't fit item to inventory".to_owned()); + } + } + Ok(()) } else { - Err(action.help_string()) + Err("Could not get inventory".to_string()) } } @@ -3057,7 +3242,7 @@ fn handle_skill_preset( fn clear_skillset(skill_set: &mut comp::SkillSet) { *skill_set = comp::SkillSet::default(); } fn set_skills(skill_set: &mut comp::SkillSet, preset: &str) -> CmdResult<()> { - let presets = match common::cmd::SkillPresetManifest::load("server.manifests.presets") { + let presets = match common::cmd::SkillPresetManifest::load(PRESET_MANIFEST_PATH) { Ok(presets) => presets.read().0.clone(), Err(err) => { warn!("Error in preset: {}", err); diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 345984e2a2..5c90d6f24e 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -34,6 +34,7 @@ use common_net::{msg::ServerGeneral, sync::WorldSyncExt}; use common_state::BlockChange; use comp::chat::GenericChatMsg; use hashbrown::HashSet; +use rand::Rng; use specs::{join::Join, saveload::MarkerAllocator, Entity as EcsEntity, WorldExt}; use tracing::error; use vek::{Vec2, Vec3}; @@ -738,6 +739,8 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o ((horiz_dist.max(vert_distance).max(0.0) / radius).min(1.0) - 1.0).abs() } + // TODO: Faster RNG? + let mut rng = rand::thread_rng(); for effect in explosion.effects { match effect { RadiusEffect::TerrainDestruction(power) => { @@ -748,17 +751,16 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o let color_range = power * 2.7; for _ in 0..RAYS { let dir = Vec3::new( - rand::random::() - 0.5, - rand::random::() - 0.5, - rand::random::() - 0.5, + rng.gen::() - 0.5, + rng.gen::() - 0.5, + rng.gen::() - 0.5, ) .normalized(); let _ = ecs .read_resource::() .ray(pos, pos + dir * color_range) - // TODO: Faster RNG - .until(|_| rand::random::() < 0.05) + .until(|_| rng.gen::() < 0.05) .for_each(|_: &Block, pos| touched_blocks.push(pos)) .cast(); } @@ -790,21 +792,31 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o // Destroy terrain for _ in 0..RAYS { let dir = Vec3::new( - rand::random::() - 0.5, - rand::random::() - 0.5, - rand::random::() - 0.15, + rng.gen::() - 0.5, + rng.gen::() - 0.5, + rng.gen::() - 0.15, ) .normalized(); let mut ray_energy = power; let terrain = ecs.read_resource::(); + let from = pos; + let to = pos + dir * power; let _ = terrain - .ray(pos, pos + dir * power) - // TODO: Faster RNG + .ray(from, to) .until(|block: &Block| { - let stop = block.is_liquid() || block.explode_power().is_none() || ray_energy <= 0.0; - ray_energy -= block.explode_power().unwrap_or(0.0) + rand::random::() * 0.1; + // Stop if: + // 1) Block is liquid + // 2) Consumed all energy + // 3) Can't explode block (for example we hit stone wall) + let stop = block.is_liquid() + || block.explode_power().is_none() + || ray_energy <= 0.0; + + ray_energy -= + block.explode_power().unwrap_or(0.0) + rng.gen::() * 0.1; + stop }) .for_each(|block: &Block, pos| { @@ -819,6 +831,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o let energies = &ecs.read_storage::(); let combos = &ecs.read_storage::(); let inventories = &ecs.read_storage::(); + let players = &ecs.read_storage::(); for ( entity_b, pos_b, @@ -887,12 +900,23 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o char_state: char_state_b_maybe, }; - attack.apply_attack( + let may_harm = combat::may_harm( + owner_entity.and_then(|owner| players.get(owner)), + players.get(entity_b), + ); + let attack_options = combat::AttackOptions { + // cool guyz maybe don't look at explosions + // but they still got hurt, it's not Hollywood + target_dodging: false, + may_harm, target_group, + }; + + attack.apply_attack( attacker_info, target_info, dir, - false, + attack_options, strength, combat::AttackSource::Explosion, |e| server_eventbus.emit_now(e), @@ -902,6 +926,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o } }, RadiusEffect::Entity(mut effect) => { + let players = &ecs.read_storage::(); for (entity_b, pos_b, body_b_maybe) in ( &ecs.entities(), &ecs.read_storage::(), @@ -916,14 +941,35 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o 1.0 - distance_squared / explosion.radius.powi(2) }; + // Player check only accounts for PvP/PvE flag. + // + // But bombs are intented to do + // friendly fire. + // + // What exactly friendly fire is subject to discussion. + // As we probably want to minimize possibility of being dick + // even to your group members, the only exception is when + // you want to harm yourself. + // + // This can be changed later. + let may_harm = || { + owner_entity.map_or(false, |attacker| { + let attacker_player = players.get(attacker); + let target_player = players.get(entity_b); + combat::may_harm(attacker_player, target_player) || attacker == entity_b + }) + }; if strength > 0.0 { let is_alive = ecs .read_storage::() .get(entity_b) .map_or(true, |h| !h.is_dead); + if is_alive { effect.modify_strength(strength); - server.state().apply_effect(entity_b, effect.clone(), owner); + if !effect.is_harm() || may_harm() { + server.state().apply_effect(entity_b, effect.clone(), owner); + } } } } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 11f1942349..4a3e6b2f0b 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, SoundKind, MAX_LISTEN_DIST}, + agent::{AgentEvent, Sound, SoundKind}, dialogue::Subject, inventory::slot::EquipSlot, item, @@ -408,7 +408,7 @@ pub fn handle_sound(server: &mut Server, sound: &Sound) { let propagated_sound = sound.with_new_vol(sound.vol - vol_dropoff); let can_hear_sound = propagated_sound.vol > 0.00; - let should_hear_sound = agent_dist_sqrd < MAX_LISTEN_DIST.powi(2); + let should_hear_sound = agent_dist_sqrd < agent.psyche.listen_dist.powi(2); if can_hear_sound && should_hear_sound { agent diff --git a/server/src/events/player.rs b/server/src/events/player.rs index e7290c33c9..667dd5fe39 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -18,6 +18,10 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { span!(_guard, "handle_exit_ingame"); let state = server.state_mut(); + // Sync the player's character data to the database. This must be done before + // removing any components from the entity + let entity = persist_entity(state, entity); + // Create new entity with just `Client`, `Uid`, `Player`, and `...Stream` // components Easier than checking and removing all other known components // Note: If other `ServerEvent`s are referring to this entity they will be @@ -83,9 +87,6 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { // Erase group component to avoid group restructure when deleting the entity state.ecs().write_storage::().remove(entity); - // Sync the player's character data to the database - let entity = persist_entity(state, entity); - // Delete old entity if let Err(e) = state.delete_entity_recorded(entity) { error!( diff --git a/server/src/lib.rs b/server/src/lib.rs index b70cc13247..10e6d06fc6 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -31,6 +31,8 @@ pub mod rtsim; pub mod settings; pub mod state_ext; pub mod sys; +#[cfg(feature = "persistent_world")] +pub mod terrain_persistence; #[cfg(not(feature = "worldgen"))] mod test_world; pub mod wiring; @@ -43,6 +45,8 @@ pub use crate::{ settings::{EditableSettings, Settings}, }; +#[cfg(feature = "persistent_world")] +use crate::terrain_persistence::TerrainPersistence; use crate::{ alias_validator::AliasValidator, chunk_generator::ChunkGenerator, @@ -173,7 +177,7 @@ impl Server { data_dir: &std::path::Path, runtime: Arc, ) -> Result { - info!("Server is data dir is: {}", data_dir.display()); + info!("Server data dir is: {}", data_dir.display()); if settings.auth_server_address.is_none() { info!("Authentication is disabled"); } @@ -216,6 +220,25 @@ impl Server { state.ecs_mut().insert(ecs_system_metrics); state.ecs_mut().insert(tick_metrics); state.ecs_mut().insert(physics_metrics); + if settings.experimental_terrain_persistence { + #[cfg(feature = "persistent_world")] + { + warn!( + "Experimental terrain persistence support is enabled. This feature may break, \ + be disabled, or otherwise change under your feet at *any time*. \ + Additionally, it is expected to be replaced in the future *without* \ + migration or warning. You have been warned." + ); + state + .ecs_mut() + .insert(TerrainPersistence::new(data_dir.to_owned())); + } + #[cfg(not(feature = "persistent_world"))] + error!( + "Experimental terrain persistence support was requested, but the server was not \ + compiled with the feature. Terrain modifications will *not* be persisted." + ); + } state .ecs_mut() .write_resource::() @@ -859,6 +882,13 @@ impl Server { pub fn cleanup(&mut self) { // Cleanup the local state self.state.cleanup(); + + // Maintain persisted terrain + #[cfg(feature = "persistent_world")] + self.state + .ecs() + .try_fetch_mut::() + .map(|mut t| t.maintain()); } fn initialize_client( @@ -1234,8 +1264,18 @@ impl Server { impl Drop for Server { fn drop(&mut self) { self.metrics_shutdown.notify_one(); + self.state .notify_players(ServerGeneral::Disconnect(DisconnectReason::Shutdown)); + + #[cfg(feature = "persistent_world")] + self.state + .ecs() + .try_fetch_mut::() + .map(|mut terrain_persistence| { + info!("Unloading terrain persistence..."); + terrain_persistence.unload_all() + }); } } diff --git a/server/src/pet.rs b/server/src/pet.rs index 1077b6fe9f..cead1eb894 100644 --- a/server/src/pet.rs +++ b/server/src/pet.rs @@ -49,9 +49,12 @@ fn tame_pet_internal(ecs: &specs::World, pet_entity: Entity, owner: Entity, pet: .write_storage() .insert(pet_entity, pet.unwrap_or_default()); - // TODO: Review whether we should be doing this or not, should the Agent always - // be overwritten when taming a pet? - let _ = ecs.write_storage().insert(pet_entity, Agent::default()); + // Create an agent for this entity using its body + if let Some(body) = ecs.read_storage().get(pet_entity) { + let _ = ecs + .write_storage() + .insert(pet_entity, Agent::from_body(body)); + } // Add to group system let clients = ecs.read_storage::(); diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index 5121a7193a..c8c9362bc3 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -103,15 +103,12 @@ impl<'a> System<'a> for Sys { .map(|e| e as f32) + Vec3::new(0.5, 0.5, body.flying_height()); let pos = comp::Pos(spawn_pos); - let agent = Some(comp::Agent::new( - None, - &body, + let agent = Some(comp::Agent::from_body(&body).with_behavior( if matches!(body, comp::Body::Humanoid(_)) { Behavior::from(BehaviorCapability::SPEAK) } else { Behavior::default() }, - false, )); let rtsim_entity = Some(RtSimEntity(id)); diff --git a/server/src/settings.rs b/server/src/settings.rs index 8df943a502..5c17adb5e1 100644 --- a/server/src/settings.rs +++ b/server/src/settings.rs @@ -14,6 +14,7 @@ pub use server_description::ServerDescription; pub use whitelist::{Whitelist, WhitelistInfo, WhitelistRecord}; use chrono::Utc; +use common::resources::BattleMode; use core::time::Duration; use portpicker::pick_unused_port; use serde::{Deserialize, Serialize}; @@ -48,7 +49,7 @@ pub struct Settings { pub quic_files: Option, pub max_players: usize, pub world_seed: u32, - //pub pvp_enabled: bool, + pub battle_mode: BattleMode, pub server_name: String, pub start_time: f64, /// When set to None, loads the default map file (if available); otherwise, @@ -61,6 +62,11 @@ pub struct Settings { pub spawn_town: Option, pub safe_spawn: bool, pub max_player_for_kill_broadcast: Option, + + /// Experimental feature. No guaranteed forwards-compatibility, may be + /// removed at *any time* with no migration. + #[serde(default, skip_serializing)] + pub experimental_terrain_persistence: bool, } impl Default for Settings { @@ -73,6 +79,7 @@ impl Default for Settings { world_seed: DEFAULT_WORLD_SEED, server_name: "Veloren Alpha".into(), max_players: 100, + battle_mode: BattleMode::PvP, start_time: 9.0 * 3600.0, map_file: None, max_view_distance: Some(65), @@ -82,6 +89,7 @@ impl Default for Settings { spawn_town: None, safe_spawn: true, max_player_for_kill_broadcast: None, + experimental_terrain_persistence: false, } } } @@ -159,6 +167,7 @@ impl Settings { max_players: 100, start_time: 9.0 * 3600.0, max_view_distance: None, + safe_spawn: false, client_timeout: Duration::from_secs(180), ..load // Fill in remaining fields from server_settings.ron. } diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 90bb228af1..9d91e90357 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -560,7 +560,6 @@ impl StateExt for State { ) .with(comp::Scale(1.0)) .with(comp::Vel(Vec3::new(0.0, 0.0, 0.0))) - .with(comp::MountState::Unmounted) .build(); restore_pet(self.ecs(), pet_entity, entity, pet); diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 0ca68b358a..be0444f0de 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -3,7 +3,7 @@ use common::{ comp::{ self, agent::{ - AgentEvent, Sound, SoundKind, Target, DEFAULT_INTERACTION_TIME, MAX_LISTEN_DIST, + AgentEvent, Sound, SoundKind, Target, TimerAction, DEFAULT_INTERACTION_TIME, TRADE_INTERACTION_TIME, }, buff::{BuffKind, Buffs}, @@ -28,7 +28,7 @@ use common::{ path::TraversalConfig, resources::{DeltaTime, Time, TimeOfDay}, rtsim::{Memory, MemoryItem, RtSimEntity, RtSimEvent}, - states::utils::StageSection, + states::{basic_beam, utils::StageSection}, terrain::{Block, TerrainGrid}, time::DayPeriod, trade::{TradeAction, TradePhase, TradeResult}, @@ -169,7 +169,6 @@ const PARTIAL_PATH_DIST: f32 = 50.0; const SEPARATION_DIST: f32 = 10.0; const SEPARATION_BIAS: f32 = 0.8; const MAX_FLEE_DIST: f32 = 20.0; -const SEARCH_DIST: f32 = 48.0; const SNEAK_COEFFICIENT: f32 = 0.25; const AVG_FOLLOW_DIST: f32 = 6.0; const RETARGETING_THRESHOLD_SECONDS: f64 = 10.0; @@ -270,7 +269,7 @@ impl<'a> System<'a> for Sys { ) }; - let event_emitter = event_bus.emitter(); + let mut event_emitter = event_bus.emitter(); if !matches!(char_state, CharacterState::LeapMelee(_)) { // Default to looking in orientation direction @@ -293,7 +292,7 @@ impl<'a> System<'a> for Sys { let is_gliding = matches!( read_data.char_states.get(entity), - Some(CharacterState::GlideWield | CharacterState::Glide(_)) + Some(CharacterState::GlideWield(_) | CharacterState::Glide(_)) ) && physics_state.on_ground.is_none(); if let Some(pid) = agent.position_pid_controller.as_mut() { @@ -381,44 +380,44 @@ impl<'a> System<'a> for Sys { // are the only parts of this tree that should provide // inputs. - let idle = |agent: &mut Agent, controller, mut event_emitter| { - data.idle_tree(agent, controller, &read_data, &mut event_emitter); - }; - - let relax = |agent: &mut Agent, controller, event_emitter| { - agent.target = None; - idle(agent, controller, event_emitter); - }; - - let react_as_pet = - |agent: &mut Agent, target: EcsEntity, controller, event_emitter| { - if let Some(tgt_pos) = read_data.positions.get(target) { - let dist_sqrd = pos.0.distance_squared(tgt_pos.0); - // If really far away drop everything and follow - if dist_sqrd > (2.0 * MAX_FOLLOW_DIST).powi(2) { - agent.bearing = Vec2::zero(); - data.follow(agent, controller, &read_data.terrain, tgt_pos); - // Attack target's attacker - } else if entity_was_attacked(target, &read_data) { - data.attack_target_attacker(agent, &read_data, controller); - // Follow owner if too far away and not - // fighting - } else if dist_sqrd > MAX_FOLLOW_DIST.powi(2) { - data.follow(agent, controller, &read_data.terrain, tgt_pos); - - // Otherwise just idle - } else { - idle(agent, controller, event_emitter); - } - } + let idle = + |agent: &mut Agent, + controller: &mut Controller, + event_emitter: &mut Emitter| { + data.idle_tree(agent, controller, &read_data, event_emitter); }; + let react_as_pet = |agent: &mut Agent, + target: EcsEntity, + controller: &mut Controller, + event_emitter| { + if let Some(tgt_pos) = read_data.positions.get(target) { + let dist_sqrd = pos.0.distance_squared(tgt_pos.0); + // If really far away drop everything and follow + if dist_sqrd > (2.0 * MAX_FOLLOW_DIST).powi(2) { + agent.bearing = Vec2::zero(); + data.follow(agent, controller, &read_data.terrain, tgt_pos); + // Attack target's attacker + } else if entity_was_attacked(target, &read_data) { + data.attack_target_attacker(agent, &read_data, controller); + // Follow owner if too far away and not + // fighting + } else if dist_sqrd > MAX_FOLLOW_DIST.powi(2) { + data.follow(agent, controller, &read_data.terrain, tgt_pos); + + // Otherwise just idle + } else { + idle(agent, controller, event_emitter); + } + } + }; + let react_to_target = |agent: &mut Agent, target: EcsEntity, hostile: bool, - controller, - mut event_emitter| { + controller: &mut Controller, + event_emitter| { if let Some(tgt_health) = read_data.healths.get(target) { // If target is dead, leave it if tgt_health.is_dead { @@ -427,69 +426,27 @@ impl<'a> System<'a> for Sys { { rtsim_forget_enemy(&tgt_stats.name, agent); } - relax(agent, controller, event_emitter); + agent.target = None; // If the target is hostile // (either based on alignment or if // the target just attacked) } else if hostile { - data.hostile_tree( - agent, - controller, - &read_data, - &mut event_emitter, - ); + data.hostile_tree(agent, controller, &read_data, event_emitter); // Target is something worth following // methinks } else if let Some(Alignment::Owned(uid)) = data.alignment { if read_data.uids.get(target) == Some(uid) { react_as_pet(agent, target, controller, event_emitter); } else { - relax(agent, controller, event_emitter); + agent.target = None; + idle(agent, controller, event_emitter); }; } else { idle(agent, controller, event_emitter); } } else { - relax(agent, controller, event_emitter); - } - }; - - let react_to_damage = - |agent: &mut Agent, by: Uid, controller, event_emitter| { - if let Some(attacker) = - read_data.uid_allocator.retrieve_entity_internal(by.id()) - { - // If the target is dead or in a safezone, remove the - // target and idle. - if should_stop_attacking(attacker, &read_data) { - relax(agent, controller, event_emitter); - } else if let Some(tgt_pos) = read_data.positions.get(attacker) { - if agent.target.is_none() { - controller.push_event(ControlEvent::Utterance( - UtteranceKind::Angry, - )); - } - - agent.target = Some(Target { - target: attacker, - hostile: true, - selected_at: read_data.time.0, - }); - let target_data = TargetData { - pos: tgt_pos, - body: read_data.bodies.get(attacker), - scale: read_data.scales.get(attacker), - }; - data.attack(agent, controller, &target_data, &read_data); - // Remember this encounter if an RtSim entity - if let Some(tgt_stats) = - data.rtsim_entity.and(read_data.stats.get(attacker)) - { - rtsim_new_enemy(&tgt_stats.name, agent, &read_data); - } - } else { - relax(agent, controller, event_emitter); - } + agent.target = None; + idle(agent, controller, event_emitter); } }; @@ -507,25 +464,85 @@ impl<'a> System<'a> for Sys { data.fly_upward(controller) } else if is_falling_dangerous && data.glider_equipped { data.glider_fall(controller); - } else if let Some(target_info) = agent.target { - let Target { - target, hostile, .. - } = target_info; - react_to_target(agent, target, hostile, controller, event_emitter); } else { // Target an entity that's attacking us if the attack // was recent and we have a health component match health { Some(health) if health.last_change.0 < DAMAGE_MEMORY_DURATION => { if let Some(by) = health.last_change.1.cause.damage_by() { - react_to_damage(agent, by, controller, event_emitter); - } else { - relax(agent, controller, event_emitter); + if let Some(attacker) = + read_data.uid_allocator.retrieve_entity_internal(by.id()) + { + // If the target is dead or in a safezone, remove the + // target and idle. + if should_stop_attacking(attacker, &read_data) { + agent.target = None; + } else if let Some(tgt_pos) = + read_data.positions.get(attacker) + { + if agent.target.is_none() { + controller.push_event(ControlEvent::Utterance( + UtteranceKind::Angry, + )); + } + + // Determine whether the new target should be a priority + // over the old one + // (i.e: because it's either close or because they + // attacked us) + let more_dangerous_than_old_target = + agent.target.map_or(true, |old_tgt| { + if let Some(old_tgt_pos) = + read_data.positions.get(old_tgt.target) + { + // Fuzzy factor that makes it harder for + // players to cheese enemies by making them + // quickly flip aggro between two players. + // It + // does this by only switching aggro if the + // new target is closer to the enemy by a + // specific proportional threshold. + const FUZZY_DIST_COMPARISON: f32 = 0.8; + // Only switch to new target if it is closer + // than the old target + tgt_pos.0.distance(pos.0) + < old_tgt_pos.0.distance(pos.0) + * FUZZY_DIST_COMPARISON + } else { + true + } + }); + + // Select the attacker as the new target + if more_dangerous_than_old_target { + agent.target = Some(Target { + target: attacker, + hostile: true, + selected_at: read_data.time.0, + aggro_on: true, + }); + } + + // Remember this attack if we're an RtSim entity + if let Some(tgt_stats) = + data.rtsim_entity.and(read_data.stats.get(attacker)) + { + rtsim_new_enemy(&tgt_stats.name, agent, &read_data); + } + } + } } }, - _ => { - idle(agent, controller, event_emitter); - }, + _ => {}, + } + + if let Some(target_info) = agent.target { + let Target { + target, hostile, .. + } = target_info; + react_to_target(agent, target, hostile, controller, &mut event_emitter); + } else { + idle(agent, controller, &mut event_emitter); } } @@ -572,7 +589,7 @@ impl<'a> AgentData<'a> { if agent.target.is_none() && thread_rng().gen_bool(0.1) { if let Some(Alignment::Owned(owner)) = self.alignment { if let Some(owner) = get_entity_by_id(owner.id(), read_data) { - agent.target = build_target(owner, false, read_data.time.0); + agent.target = build_target(owner, false, read_data.time.0, false); } } } @@ -601,27 +618,43 @@ impl<'a> AgentData<'a> { agent.action_state.timer = 0.1; } } - if agent.action_state.timer > 0.0 { - if agent.action_state.timer - < (if agent.behavior.is(BehaviorState::TRADING) { - TRADE_INTERACTION_TIME - } else { - DEFAULT_INTERACTION_TIME - }) - { - self.interact(agent, controller, read_data, event_emitter); - } else { - agent.action_state.timer = 0.0; - agent.target = None; - controller.actions.push(ControlAction::Stand); - self.idle(agent, controller, read_data); - } - } else if thread_rng().gen::() < 0.1 { - self.choose_target(agent, controller, read_data, event_emitter); - } else if agent.awareness > AWARENESS_INVESTIGATE_THRESHOLD { - self.handle_elevated_awareness(agent, controller, read_data); + + // If we receive a new interaction, start the interaction timer + if can_speak(agent) && self.recv_interaction(agent, controller, read_data, event_emitter) { + agent.timer.start(read_data.time.0, TimerAction::Interact); + } + + let timeout = if agent.behavior.is(BehaviorState::TRADING) { + TRADE_INTERACTION_TIME } else { - self.idle(agent, controller, read_data); + DEFAULT_INTERACTION_TIME + }; + + match agent + .timer + .timeout_elapsed(read_data.time.0, TimerAction::Interact, timeout as f64) + { + None => { + // Look toward the interacting entity for a while + if let Some(Target { target, .. }) = &agent.target { + self.look_toward(controller, read_data, *target); + controller.actions.push(ControlAction::Talk); + } + }, + Some(just_ended) => { + if just_ended { + agent.target = None; + controller.actions.push(ControlAction::Stand); + } + + if thread_rng().gen::() < 0.1 { + self.choose_target(agent, controller, read_data, event_emitter); + } else if agent.awareness > AWARENESS_INVESTIGATE_THRESHOLD { + self.handle_elevated_awareness(agent, controller, read_data); + } else { + self.idle(agent, controller, read_data); + } + }, } } @@ -647,19 +680,34 @@ impl<'a> AgentData<'a> { if let Some(Target { target, selected_at, + aggro_on, .. - }) = agent.target + }) = &mut agent.target { + let target = *target; + let selected_at = *selected_at; + if let Some(tgt_pos) = read_data.positions.get(target) { - let dist_sqrd = self.pos.0.distance_squared(tgt_pos.0); - // Should the agent flee? - if 1.0 - agent.psyche.aggro > self.damage && self.flees { + let dist_sq = self.pos.0.distance_squared(tgt_pos.0); + let in_aggro_range = agent + .psyche + .aggro_dist + .map_or(true, |ad| dist_sq < ad.powi(2)); + // If, at any point, the target comes closer than the aggro distance, switch to + // aggro mode + if in_aggro_range { + *aggro_on = true; + } + let aggro_on = *aggro_on; + + if self.damage.min(1.0) < agent.psyche.flee_health && self.flees { + // Should the agent flee? if agent.action_state.timer == 0.0 && can_speak(agent) { - let msg = "npc.speech.villager_under_attack".to_string(); - self.chat_general(msg, event_emitter); + self.chat_general("npc.speech.villager_under_attack", event_emitter); self.emit_villager_alarm(read_data.time.0, event_emitter); agent.action_state.timer = 0.01; - } else if agent.action_state.timer < FLEE_DURATION || dist_sqrd < MAX_FLEE_DIST + } else if agent.action_state.timer < FLEE_DURATION + || dist_sq < MAX_FLEE_DIST.powi(2) { self.flee(agent, controller, &read_data.terrain, tgt_pos); agent.action_state.timer += read_data.dt.0; @@ -668,32 +716,36 @@ impl<'a> AgentData<'a> { agent.target = None; self.idle(agent, controller, read_data); } - - // If not fleeing, attack the hostile entity! + } else if should_stop_attacking(target, read_data) { + if can_speak(agent) { + let msg = "npc.speech.villager_enemy_killed".to_string(); + event_emitter + .emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg))); + } + agent.target = None; + self.idle(agent, controller, read_data); } else { - // If the hostile entity is dead or has an invulnerability buff (eg, those - // applied in safezones), return to idle - if should_stop_attacking(target, read_data) { - if can_speak(agent) { - let msg = "npc.speech.villager_enemy_killed".to_string(); - event_emitter - .emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg))); - } - agent.target = None; - self.idle(agent, controller, read_data); - // Choose a new target every 10 seconds, but only for - // enemies - // TODO: This should be more principled. Consider factoring - // health, combat rating, wielded weapon, etc, into the - // decision to change target. - } else if read_data.time.0 - selected_at > RETARGETING_THRESHOLD_SECONDS - && matches!(self.alignment, Some(Alignment::Enemy)) + // Potentially choose a new target + if read_data.time.0 - selected_at > RETARGETING_THRESHOLD_SECONDS + && !in_aggro_range { self.choose_target(agent, controller, read_data, event_emitter); - } else { - // TODO Add utility for attacking vs leaving target alone + } + + if aggro_on { let target_data = build_target_data(target, tgt_pos, read_data); self.attack(agent, controller, &target_data, read_data); + } else { + // If we're not yet aggro-ed, strike a menacing pose + if thread_rng().gen::() < read_data.dt.0 * 0.25 { + self.chat_general_if_can_speak( + agent, + "npc.speech.menacing", + event_emitter, + ); + controller.push_event(ControlEvent::Utterance(UtteranceKind::Angry)); + } + self.menacing(agent, controller, read_data, target, tgt_pos); } } } @@ -941,13 +993,13 @@ impl<'a> AgentData<'a> { } } - fn interact( + fn recv_interaction( &self, agent: &mut Agent, controller: &mut Controller, read_data: &ReadData, event_emitter: &mut Emitter<'_, ServerEvent>, - ) { + ) -> bool { // TODO: Process group invites // TODO: Add Group AgentEvent // let accept = false; // set back to "matches!(alignment, Alignment::Npc)" @@ -969,9 +1021,9 @@ impl<'a> AgentData<'a> { Some(AgentEvent::Talk(by, subject)) => { if can_speak(agent) { if let Some(target) = get_entity_by_id(by.id(), read_data) { - agent.target = build_target(target, false, read_data.time.0); + agent.target = build_target(target, false, read_data.time.0, false); - if self.look_toward(controller, read_data, &target) { + 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)); @@ -1017,11 +1069,12 @@ impl<'a> AgentData<'a> { }; self.chat_general(msg, event_emitter); } else if agent.behavior.can_trade() { - let msg = "npc.speech.merchant_advertisement".to_string(); - self.chat_general(msg, event_emitter); + self.chat_general( + "npc.speech.merchant_advertisement", + event_emitter, + ); } else { - let msg = "npc.speech.villager".to_string(); - self.chat_general(msg, event_emitter); + self.chat_general("npc.speech.villager", event_emitter); } }, Subject::Trade => { @@ -1031,18 +1084,23 @@ impl<'a> AgentData<'a> { by, InviteKind::Trade, )); - let msg = - "npc.speech.merchant_advertisement".to_string(); - self.chat_general(msg, event_emitter); + self.chat_general( + "npc.speech.merchant_advertisement", + event_emitter, + ); } else { - let msg = "npc.speech.merchant_busy".to_string(); - self.chat_general(msg, event_emitter); + self.chat_general( + "npc.speech.merchant_busy", + event_emitter, + ); } } else { // TODO: maybe make some travellers willing to trade with // simpler goods like potions - let msg = "npc.speech.villager_decline_trade".to_string(); - self.chat_general(msg, event_emitter); + self.chat_general( + "npc.speech.villager_decline_trade", + event_emitter, + ); } }, Subject::Mood => { @@ -1156,7 +1214,7 @@ impl<'a> AgentData<'a> { controller.actions.push(ControlAction::Stand); controller.actions.push(ControlAction::Talk); if let Some(target) = get_entity_by_id(with.id(), read_data) { - agent.target = build_target(target, false, read_data.time.0); + agent.target = build_target(target, false, read_data.time.0, false); } controller .events @@ -1167,22 +1225,28 @@ impl<'a> AgentData<'a> { controller .events .push(ControlEvent::InviteResponse(InviteResponse::Decline)); - let msg = "npc.speech.merchant_busy".to_string(); - self.chat_general_if_can_speak(agent, msg, event_emitter); + self.chat_general_if_can_speak( + agent, + "npc.speech.merchant_busy", + event_emitter, + ); } } else { // TODO: Provide a hint where to find the closest merchant? controller .events .push(ControlEvent::InviteResponse(InviteResponse::Decline)); - let msg = "npc.speech.villager_decline_trade".to_string(); - self.chat_general_if_can_speak(agent, msg, event_emitter); + self.chat_general_if_can_speak( + agent, + "npc.speech.villager_decline_trade", + event_emitter, + ); } }, Some(AgentEvent::TradeAccepted(with)) => { if !agent.behavior.is(BehaviorState::TRADING) { if let Some(target) = get_entity_by_id(with.id(), read_data) { - agent.target = build_target(target, false, read_data.time.0); + agent.target = build_target(target, false, read_data.time.0, false); } agent.behavior.set(BehaviorState::TRADING); agent.behavior.set(BehaviorState::TRADING_ISSUER); @@ -1192,12 +1256,13 @@ impl<'a> AgentData<'a> { if agent.behavior.is(BehaviorState::TRADING) { match result { TradeResult::Completed => { - let msg = "npc.speech.merchant_trade_successful".to_string(); - self.chat_general(msg, event_emitter); + self.chat_general( + "npc.speech.merchant_trade_successful", + event_emitter, + ); }, _ => { - let msg = "npc.speech.merchant_trade_declined".to_string(); - self.chat_general(msg, event_emitter); + self.chat_general("npc.speech.merchant_trade_declined", event_emitter); }, } agent.behavior.unset(BehaviorState::TRADING); @@ -1263,32 +1328,22 @@ impl<'a> AgentData<'a> { } } }, - _ => { - if can_speak(agent) { - // No new events, continue looking towards the last - // interacting player for some time - if let Some(Target { target, .. }) = &agent.target { - self.look_toward(controller, read_data, target); - } else { - agent.action_state.timer = 0.0; - } - } - }, + Some(AgentEvent::ServerSound(_)) => {}, + Some(AgentEvent::Hurt) => {}, + None => return false, } + true } fn look_toward( &self, controller: &mut Controller, read_data: &ReadData, - target: &EcsEntity, + target: EcsEntity, ) -> bool { - if let Some(tgt_pos) = read_data.positions.get(*target) { + if let Some(tgt_pos) = read_data.positions.get(target) { let eye_offset = self.body.map_or(0.0, |b| b.eye_height()); - let tgt_eye_offset = read_data - .bodies - .get(*target) - .map_or(0.0, |b| b.eye_height()); + let tgt_eye_offset = read_data.bodies.get(target).map_or(0.0, |b| b.eye_height()); if let Some(dir) = Dir::from_unnormalized( Vec3::new(tgt_pos.0.x, tgt_pos.0.y, tgt_pos.0.z + tgt_eye_offset) - Vec3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z + eye_offset), @@ -1301,6 +1356,24 @@ impl<'a> AgentData<'a> { } } + fn menacing( + &self, + _agent: &mut Agent, + controller: &mut Controller, + read_data: &ReadData, + target: EcsEntity, + _tgt_pos: &Pos, + ) { + self.look_toward(controller, read_data, target); + controller.actions.push(ControlAction::Wield); + + let max_move = 0.5; + let move_dir_mag = controller.inputs.move_dir.magnitude(); + if move_dir_mag > max_move { + controller.inputs.move_dir = max_move * controller.inputs.move_dir / move_dir_mag; + } + } + fn flee( &self, agent: &mut Agent, @@ -1431,34 +1504,41 @@ impl<'a> AgentData<'a> { }) }; - let in_search_area = |e_pos: &Pos, e_char_state: Option<&CharacterState>| { - let mut search_dist = SEARCH_DIST; - if e_char_state.map_or(false, CharacterState::is_stealthy) { - // TODO: make sneak more effective based on a stat like e_stats.fitness - search_dist *= SNEAK_COEFFICIENT; - }; + let max_search_dist = agent.psyche.search_dist(); + let max_sight_dist = agent.psyche.sight_dist; + let max_listen_dist = agent.psyche.listen_dist; + let in_sight_dist = |e_pos: &Pos, e_char_state: Option<&CharacterState>| { + let search_dist = max_sight_dist + * if e_char_state.map_or(false, CharacterState::is_stealthy) { + // TODO: make sneak more effective based on a stat like e_stats.fitness + SNEAK_COEFFICIENT + } else { + 1.0 + }; e_pos.0.distance_squared(self.pos.0) < search_dist.powi(2) }; - let within_view = |e_pos: &Pos| { + let within_fov = |e_pos: &Pos| { (e_pos.0 - self.pos.0) .try_normalized() .map_or(true, |v| v.dot(*controller.inputs.look_dir) > 0.15) }; - let in_listen_area = |e_pos: &Pos, e_char_state: Option<&CharacterState>| { - let mut listen_dist = MAX_LISTEN_DIST; - if e_char_state.map_or(false, CharacterState::is_stealthy) { - // TODO: make sneak more effective based on a stat like e_stats.fitness - listen_dist *= SNEAK_COEFFICIENT; - } + let in_listen_dist = |e_pos: &Pos, e_char_state: Option<&CharacterState>| { + let listen_dist = max_listen_dist + * if e_char_state.map_or(false, CharacterState::is_stealthy) { + // TODO: make sneak more effective based on a stat like e_stats.fitness + SNEAK_COEFFICIENT + } else { + 1.0 + }; // TODO implement proper sound system for agents e_pos.0.distance_squared(self.pos.0) < listen_dist.powi(2) }; let within_reach = |e_pos: &Pos, e_char_state: Option<&CharacterState>| { - (in_search_area(e_pos, e_char_state) && within_view(e_pos)) - || in_listen_area(e_pos, e_char_state) + (in_sight_dist(e_pos, e_char_state) && within_fov(e_pos)) + || in_listen_dist(e_pos, e_char_state) }; let owners_hostile = |e_alignment: Option<&Alignment>| { @@ -1594,9 +1674,10 @@ impl<'a> AgentData<'a> { // TODO choose target by more than just distance let common::CachedSpatialGrid(grid) = self.cached_spatial_grid; let target = grid - .in_circle_aabr(self.pos.0.xy(), SEARCH_DIST) + .in_circle_aabr(self.pos.0.xy(), max_search_dist) .filter_map(worth_choosing) .filter_map(possible_target) + // TODO: This seems expensive. Cache this to avoid recomputing each tick .filter(|(_, e_pos)| can_see_them(e_pos)) .min_by_key(|(_, e_pos)| (e_pos.0.distance_squared(self.pos.0) * 100.0) as i32) .map(|(e, _)| e); @@ -1609,6 +1690,7 @@ impl<'a> AgentData<'a> { target, hostile: true, selected_at: read_data.time.0, + aggro_on: false, }); } @@ -1715,11 +1797,23 @@ impl<'a> AgentData<'a> { 0.0 }; - // FIXME: Retrieve actual projectile speed! + // FIXME: + // 1) Retrieve actual projectile speed! // We have to assume projectiles are faster than base speed because there are // skills that increase it, and in most cases this will cause agents to // overshoot + // + // 2) We use eye_offset-s which isn't actually ideal. + // Some attacks (beam for example) may use different offsets, + // we should probably use offsets from corresponding states. + // + // 3) Should we even have this big switch? + // Not all attacks may want their direction overwritten. + // And this is quite hard to debug when you don't see it in actual + // attack handler. if let Some(dir) = match tactic { + // FIXME: this code make Staff flamethrower aim flamethrower + // like a projectile. Tactic::Bow | Tactic::FixedTurret | Tactic::QuadLowRanged @@ -1809,13 +1903,25 @@ impl<'a> AgentData<'a> { Dir::from_unnormalized(Vec3::new(delta_x, delta_y, -1.0)) }, - _ => Dir::from_unnormalized( - Vec3::new( + _ => { + let aim_from = match self.char_state { + CharacterState::BasicBeam(_) => self.body.map_or(self.pos.0, |body| { + self.pos.0 + + basic_beam::beam_offsets( + body, + controller.inputs.look_dir, + self.ori.look_vec(), + ) + }), + _ => Vec3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z + eye_offset), + }; + let aim_to = Vec3::new( tgt_data.pos.0.x, tgt_data.pos.0.y, tgt_data.pos.0.z + tgt_eye_offset, - ) - Vec3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z + eye_offset), - ), + ); + Dir::from_unnormalized(aim_to - aim_from) + }, } { controller.inputs.look_dir = dir; } @@ -2380,40 +2486,80 @@ impl<'a> AgentData<'a> { tgt_data: &TargetData, read_data: &ReadData, ) { - if self.body.map(|b| b.is_humanoid()).unwrap_or(false) && attack_data.in_min_range() { + let extract_ability = |ability: &CharacterAbility| { + ability + .clone() + .adjusted_by_skills(self.skill_set, Some(ToolKind::Staff)) + }; + let (flamethrower, shockwave) = self + .inventory + .equipped(EquipSlot::ActiveMainhand) + .map(|i| &i.item_config_expect().abilities) + .map(|a| { + ( + Some(a.secondary.clone()), + a.abilities.get(0).map(|(_, s)| s), + ) + }) + .map_or( + (CharacterAbility::default(), CharacterAbility::default()), + |(s, a)| { + ( + extract_ability(&s.unwrap_or_default()), + extract_ability(a.unwrap_or(&CharacterAbility::default())), + ) + }, + ); + let flamethrower_range = match flamethrower { + CharacterAbility::BasicBeam { range, .. } => range, + _ => 20.0_f32, + }; + let shockwave_cost = shockwave.get_energy_cost(); + if self.body.map_or(false, |b| b.is_humanoid()) + && attack_data.in_min_range() + && self.energy.current() > CharacterAbility::default_roll().get_energy_cost() + && !matches!(self.char_state, CharacterState::Shockwave(_)) + { + // if a humanoid, have enough stamina, not in shockwave, and in melee range, + // emergency roll controller .actions .push(ControlAction::basic_input(InputKind::Roll)); - } else if attack_data.dist_sqrd < (5.0 * attack_data.min_attack_dist).powi(2) - && attack_data.angle < 15.0 + } else if matches!(self.char_state, CharacterState::Shockwave(_)) { + agent.action_state.condition = false; + } else if agent.action_state.condition + && matches!(self.char_state, CharacterState::Wielding) { - if agent.action_state.timer < 1.5 { - 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 agent.action_state.timer < 3.0 { - 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 { - agent.action_state.timer = 0.0; - } + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + } else if !matches!(self.char_state, CharacterState::Shockwave(c) if !matches!(c.stage_section, StageSection::Recover)) + { + // only try to use another ability unless in shockwave or recover + let target_approaching_speed = -agent + .target + .as_ref() + .map(|t| t.target) + .and_then(|e| read_data.velocities.get(e)) + .map_or(0.0, |v| v.0.dot(self.ori.look_vec())); if self .skill_set .has_skill(Skill::Staff(StaffSkill::UnlockShockwave)) - && self.energy.current() > 800 - && thread_rng().gen::() > 0.8 + && target_approaching_speed > 12.0 + && self.energy.current() > shockwave_cost + { + // if enemy is closing distance quickly, use shockwave to knock back + if matches!(self.char_state, CharacterState::Wielding) { + controller + .actions + .push(ControlAction::basic_input(InputKind::Ability(0))); + } else { + agent.action_state.condition = true; + } + } else if self.energy.current() + > shockwave_cost + CharacterAbility::default_roll().get_energy_cost() + && attack_data.dist_sqrd < flamethrower_range.powi(2) { - controller - .actions - .push(ControlAction::basic_input(InputKind::Ability(0))); - } else if self.energy.current() > 10 { controller .actions .push(ControlAction::basic_input(InputKind::Secondary)); @@ -2422,7 +2568,26 @@ impl<'a> AgentData<'a> { .actions .push(ControlAction::basic_input(InputKind::Primary)); } + } + // Logic to move. Intentionally kept separate from ability logic so duplicated + // work is less necessary. + if attack_data.dist_sqrd < (2.0 * attack_data.min_attack_dist).powi(2) { + // Attempt to move away from target if too close + if let Some((bearing, speed)) = agent.chaser.chase( + &*read_data.terrain, + self.pos.0, + self.vel.0, + tgt_data.pos.0, + TraversalConfig { + min_tgt_dist: 1.25, + ..self.traversal_config + }, + ) { + controller.inputs.move_dir = + -bearing.xy().try_normalized().unwrap_or_else(Vec2::zero) * speed; + } } else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) { + // Else attempt to circle target if neither too close nor too far if let Some((bearing, speed)) = agent.chaser.chase( &*read_data.terrain, self.pos.0, @@ -2438,7 +2603,7 @@ impl<'a> AgentData<'a> { self.pos, tgt_data.pos, attack_data.dist_sqrd, - ) && attack_data.angle < 15.0 + ) && attack_data.angle < 45.0 { controller.inputs.move_dir = bearing .xy() @@ -2446,18 +2611,18 @@ impl<'a> AgentData<'a> { .try_normalized() .unwrap_or_else(Vec2::zero) * speed; - controller - .actions - .push(ControlAction::basic_input(InputKind::Primary)); } else { + // Unless cannot see target, then move towards them controller.inputs.move_dir = bearing.xy().try_normalized().unwrap_or_else(Vec2::zero) * speed; self.jump_if(controller, bearing.z > 1.5); controller.inputs.move_z = bearing.z; } } - if self.body.map(|b| b.is_humanoid()).unwrap_or(false) + // Sometimes try to roll + if self.body.map_or(false, |b| b.is_humanoid()) && attack_data.dist_sqrd < 16.0f32.powi(2) + && !matches!(self.char_state, CharacterState::Shockwave(_)) && thread_rng().gen::() < 0.02 { controller @@ -2465,6 +2630,7 @@ impl<'a> AgentData<'a> { .push(ControlAction::basic_input(InputKind::Roll)); } } else { + // If too far, move towards target self.path_toward_target(agent, controller, tgt_data, read_data, false, false, None); } } @@ -4017,11 +4183,13 @@ impl<'a> AgentData<'a> { } else if is_village_guard { self.follow(agent, controller, &read_data.terrain, &sound_pos); } else if is_neutral { - let aggro = agent.psyche.aggro; + let flee_health = agent.psyche.flee_health; let close_enough = dist_sqrd < 35.0_f32.powi(2); let sound_was_loud = sound.vol >= 10.0; - if close_enough && (aggro <= 0.5 || (aggro <= 0.7 && sound_was_loud)) { + if close_enough + && (flee_health <= 0.7 || (flee_health <= 0.5 && sound_was_loud)) + { self.flee(agent, controller, &read_data.terrain, &sound_pos); } else { self.idle(agent, controller, read_data); @@ -4048,11 +4216,11 @@ impl<'a> AgentData<'a> { controller.push_event(ControlEvent::Utterance(UtteranceKind::Angry)); } - agent.target = build_target(attacker, true, read_data.time.0); + agent.target = build_target(attacker, true, read_data.time.0, true); if let Some(tgt_pos) = read_data.positions.get(attacker) { if should_stop_attacking(attacker, read_data) { - agent.target = build_target(target, false, read_data.time.0); + agent.target = build_target(target, false, read_data.time.0, false); self.idle(agent, controller, read_data); } else { @@ -4154,11 +4322,11 @@ impl<'a> AgentData<'a> { fn chat_general_if_can_speak( &self, agent: &Agent, - msg: String, + msg: impl ToString, event_emitter: &mut Emitter<'_, ServerEvent>, ) -> bool { if can_speak(agent) { - event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg))); + self.chat_general(msg, event_emitter); true } else { false @@ -4177,8 +4345,11 @@ impl<'a> AgentData<'a> { } } - fn chat_general(&self, msg: String, event_emitter: &mut Emitter<'_, ServerEvent>) { - event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc(*self.uid, msg))); + fn chat_general(&self, msg: impl ToString, event_emitter: &mut Emitter<'_, ServerEvent>) { + event_emitter.emit(ServerEvent::Chat(UnresolvedChatMsg::npc( + *self.uid, + msg.to_string(), + ))); } fn emit_villager_alarm(&self, time: f64, event_emitter: &mut Emitter<'_, ServerEvent>) { @@ -4304,11 +4475,12 @@ fn entity_was_attacked(entity: EcsEntity, read_data: &ReadData) -> bool { } } -fn build_target(target: EcsEntity, is_hostile: bool, time: f64) -> Option { +fn build_target(target: EcsEntity, is_hostile: bool, time: f64, aggro_on: bool) -> Option { Some(Target { target, hostile: is_hostile, selected_at: time, + aggro_on, }) } diff --git a/server/src/sys/msg/character_screen.rs b/server/src/sys/msg/character_screen.rs index 5610bfedf2..b2b2618c5a 100644 --- a/server/src/sys/msg/character_screen.rs +++ b/server/src/sys/msg/character_screen.rs @@ -115,19 +115,34 @@ impl Sys { character_loader.load_character_list(entity, player.uuid().to_string()) } }, - ClientGeneral::CreateCharacter { alias, tool, body } => { + ClientGeneral::CreateCharacter { + alias, + mainhand, + offhand, + body, + } => { if let Err(error) = alias_validator.validate(&alias) { debug!(?error, ?alias, "denied alias as it contained a banned word"); client.send(ServerGeneral::CharacterActionError(error.to_string()))?; } else if let Some(player) = players.get(entity) { - character_creator::create_character( + if let Err(error) = character_creator::create_character( entity, player.uuid().to_string(), alias, - tool, + mainhand.clone(), + offhand.clone(), body, character_updater, - ); + ) { + debug!( + ?error, + ?mainhand, + ?offhand, + ?body, + "Denied creating character because of invalid input." + ); + client.send(ServerGeneral::CharacterActionError(error.to_string()))?; + } } }, ClientGeneral::DeleteCharacter(character_id) => { diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs index 9aa4e3ee40..296864c78c 100644 --- a/server/src/sys/msg/in_game.rs +++ b/server/src/sys/msg/in_game.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "persistent_world")] +use crate::TerrainPersistence; use crate::{client::Client, presence::Presence, Settings}; use common::{ comp::{ @@ -16,6 +18,11 @@ use specs::{Entities, Join, Read, ReadExpect, ReadStorage, Write, WriteStorage}; use tracing::{debug, trace, warn}; use vek::*; +#[cfg(feature = "persistent_world")] +pub type TerrainPersistenceData<'a> = Option>; +#[cfg(not(feature = "persistent_world"))] +pub type TerrainPersistenceData<'a> = (); + impl Sys { #[allow(clippy::too_many_arguments)] fn handle_client_in_game_msg( @@ -36,6 +43,7 @@ impl Sys { settings: &Read<'_, Settings>, build_areas: &Read<'_, BuildAreas>, player_physics_settings: &mut Write<'_, PlayerPhysicsSettings>, + _terrain_persistence: &mut TerrainPersistenceData<'_>, maybe_player: &Option<&Player>, maybe_admin: &Option<&Admin>, msg: ClientGeneral, @@ -190,7 +198,7 @@ impl Sys { if let Some(comp_can_build) = can_build.get(entity) { if comp_can_build.enabled { for area in comp_can_build.build_areas.iter() { - if let Some(block) = build_areas + if let Some(old_block) = build_areas .areas() .get(*area) // TODO: Make this an exclusive check on the upper bound of the AABB @@ -198,13 +206,21 @@ impl Sys { .filter(|aabb| aabb.contains_point(pos)) .and_then(|_| terrain.get(pos).ok()) { - block_changes.set(pos, block.into_vacant()); + let new_block = old_block.into_vacant(); + let _was_set = block_changes.try_set(pos, new_block).is_some(); + #[cfg(feature = "persistent_world")] + if _was_set { + if let Some(terrain_persistence) = _terrain_persistence.as_mut() + { + terrain_persistence.set_block(pos, new_block); + } + } } } } } }, - ClientGeneral::PlaceBlock(pos, block) => { + ClientGeneral::PlaceBlock(pos, new_block) => { if let Some(comp_can_build) = can_build.get(entity) { if comp_can_build.enabled { for area in comp_can_build.build_areas.iter() { @@ -216,7 +232,14 @@ impl Sys { .filter(|aabb| aabb.contains_point(pos)) .is_some() { - block_changes.try_set(pos, block); + let _was_set = block_changes.try_set(pos, new_block).is_some(); + #[cfg(feature = "persistent_world")] + if _was_set { + if let Some(terrain_persistence) = _terrain_persistence.as_mut() + { + terrain_persistence.set_block(pos, new_block); + } + } } } } @@ -287,6 +310,7 @@ impl<'a> System<'a> for Sys { Read<'a, Settings>, Read<'a, BuildAreas>, Write<'a, PlayerPhysicsSettings>, + TerrainPersistenceData<'a>, ReadStorage<'a, Player>, ReadStorage<'a, Admin>, ); @@ -315,6 +339,7 @@ impl<'a> System<'a> for Sys { settings, build_areas, mut player_physics_settings, + mut terrain_persistence, players, admins, ): Self::SystemData, @@ -349,6 +374,7 @@ impl<'a> System<'a> for Sys { &settings, &build_areas, &mut player_physics_settings, + &mut terrain_persistence, &player, &maybe_admin, msg, diff --git a/server/src/sys/msg/register.rs b/server/src/sys/msg/register.rs index 8d3015d946..816640be05 100644 --- a/server/src/sys/msg/register.rs +++ b/server/src/sys/msg/register.rs @@ -2,7 +2,7 @@ use crate::{ client::Client, login_provider::{LoginProvider, PendingLogin}, metrics::PlayerMetrics, - EditableSettings, + EditableSettings, Settings, }; use common::{ comp::{Admin, Player, Stats}, @@ -17,7 +17,8 @@ use common_net::msg::{ use hashbrown::HashMap; use plugin_api::Health; use specs::{ - storage::StorageEntry, Entities, Join, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage, + shred::ResourceId, storage::StorageEntry, Entities, Join, Read, ReadExpect, ReadStorage, + SystemData, World, WriteExpect, WriteStorage, }; use tracing::trace; @@ -29,26 +30,32 @@ type ReadPlugin<'a> = Read<'a, PluginMgr>; #[cfg(not(feature = "plugins"))] type ReadPlugin<'a> = Option>; +#[derive(SystemData)] +pub struct ReadData<'a> { + entities: Entities<'a>, + stats: ReadStorage<'a, Stats>, + uids: ReadStorage<'a, Uid>, + clients: ReadStorage<'a, Client>, + server_event_bus: Read<'a, EventBus>, + player_metrics: ReadExpect<'a, PlayerMetrics>, + settings: ReadExpect<'a, Settings>, + editable_settings: ReadExpect<'a, EditableSettings>, + _healths: ReadStorage<'a, Health>, // used by plugin feature + _plugin_mgr: ReadPlugin<'a>, // used by plugin feature + _uid_allocator: Read<'a, UidAllocator>, // used by plugin feature +} + /// This system will handle new messages from clients #[derive(Default)] pub struct Sys; impl<'a> System<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( - Entities<'a>, - ReadExpect<'a, PlayerMetrics>, - ReadStorage<'a, Health>, - ReadStorage<'a, Uid>, - ReadStorage<'a, Client>, + ReadData<'a>, WriteStorage<'a, Player>, - WriteStorage<'a, PendingLogin>, - Read<'a, UidAllocator>, - ReadPlugin<'a>, - ReadStorage<'a, Stats>, - WriteExpect<'a, LoginProvider>, WriteStorage<'a, Admin>, - ReadExpect<'a, EditableSettings>, - Read<'a, EventBus>, + WriteStorage<'a, PendingLogin>, + WriteExpect<'a, LoginProvider>, ); const NAME: &'static str = "msg::register"; @@ -58,24 +65,21 @@ impl<'a> System<'a> for Sys { fn run( _job: &mut Job, ( - entities, - player_metrics, - _health_comp, // used by plugin feature - uids, - clients, + read_data, mut players, - mut pending_logins, - _uid_allocator, // used by plugin feature - _plugin_mgr, // used by plugin feature - stats, - mut login_provider, mut admins, - editable_settings, - server_event_bus, + mut pending_logins, + mut login_provider, ): Self::SystemData, ) { + let mut server_emitter = read_data.server_event_bus.emitter(); // Player list to send new players. - let player_list = (&uids, &players, stats.maybe(), admins.maybe()) + let player_list = ( + &read_data.uids, + &players, + read_data.stats.maybe(), + admins.maybe(), + ) .join() .map(|(uid, player, stats, admin)| { (*uid, PlayerInfo { @@ -92,7 +96,7 @@ impl<'a> System<'a> for Sys { let mut new_players = Vec::new(); // defer auth lockup - for (entity, client) in (&entities, &clients).join() { + for (entity, client) in (&read_data.entities, &read_data.clients).join() { let _ = super::try_recv_all(client, 0, |_, msg: ClientRegister| { trace!(?msg.token_or_username, "defer auth lockup"); let pending = login_provider.verify(&msg.token_or_username); @@ -103,15 +107,17 @@ impl<'a> System<'a> for Sys { let mut finished_pending = vec![]; let mut retries = vec![]; - for (entity, client, mut pending) in (&entities, &clients, &mut pending_logins).join() { + for (entity, client, mut pending) in + (&read_data.entities, &read_data.clients, &mut pending_logins).join() + { if let Err(e) = || -> std::result::Result<(), crate::error::Error> { #[cfg(feature = "plugins")] let ecs_world = EcsWorld { - entities: &entities, - health: (&_health_comp).into(), - uid: (&uids).into(), + entities: &read_data.entities, + health: (&read_data._healths).into(), + uid: (&read_data.uids).into(), player: (&players).into(), - uid_allocator: &_uid_allocator, + uid_allocator: &read_data._uid_allocator, }; let (username, uuid) = match login_provider.login( @@ -119,10 +125,10 @@ impl<'a> System<'a> for Sys { #[cfg(feature = "plugins")] &ecs_world, #[cfg(feature = "plugins")] - &_plugin_mgr, - &*editable_settings.admins, - &*editable_settings.whitelist, - &*editable_settings.banlist, + &read_data._plugin_mgr, + &*read_data.editable_settings.admins, + &*read_data.editable_settings.whitelist, + &*read_data.editable_settings.banlist, ) { None => return Ok(()), Some(r) => { @@ -130,7 +136,7 @@ impl<'a> System<'a> for Sys { trace!(?r, "pending login returned"); match r { Err(e) => { - server_event_bus.emit_now(ServerEvent::ClientDisconnect( + server_emitter.emit(ServerEvent::ClientDisconnect( entity, common::comp::DisconnectReason::Kicked, )); @@ -143,12 +149,13 @@ impl<'a> System<'a> for Sys { }; // Check if user is already logged-in - if let Some((old_entity, old_client, _)) = (&entities, &clients, &players) - .join() - .find(|(_, _, old_player)| old_player.uuid() == uuid) + if let Some((old_entity, old_client, _)) = + (&read_data.entities, &read_data.clients, &players) + .join() + .find(|(_, _, old_player)| old_player.uuid() == uuid) { // Remove old client - server_event_bus.emit_now(ServerEvent::ClientDisconnect( + server_emitter.emit(ServerEvent::ClientDisconnect( old_entity, common::comp::DisconnectReason::NewerLogin, )); @@ -166,8 +173,8 @@ impl<'a> System<'a> for Sys { return Ok(()); } - let player = Player::new(username, uuid); - let admin = editable_settings.admins.get(&uuid); + let player = Player::new(username, read_data.settings.battle_mode, uuid); + let admin = read_data.editable_settings.admins.get(&uuid); if !player.is_valid() { // Invalid player @@ -178,7 +185,7 @@ impl<'a> System<'a> for Sys { if let Ok(StorageEntry::Vacant(v)) = players.entry(entity) { // Add Player component to this client, if the entity exists. v.insert(player); - player_metrics.players_connected.inc(); + read_data.player_metrics.players_connected.inc(); // Give the Admin component to the player if their name exists in // admin list @@ -214,22 +221,24 @@ impl<'a> System<'a> for Sys { // Handle new players. // Tell all clients to add them to the player list. - for entity in new_players { - if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) { - let mut lazy_msg = None; - for (_, client) in (&players, &clients).join() { - if lazy_msg.is_none() { - lazy_msg = Some(client.prepare(ServerGeneral::PlayerListUpdate( - PlayerListUpdate::Add(*uid, PlayerInfo { - player_alias: player.alias.clone(), - is_online: true, - is_moderator: admins.get(entity).is_some(), - character: None, // new players will be on character select. - }), - ))); - } - lazy_msg.as_ref().map(|msg| client.send_prepared(msg)); + let player_info = |entity| { + let player_info = read_data.uids.get(entity).zip(players.get(entity)); + player_info.map(|(u, p)| (entity, u, p)) + }; + for (entity, uid, player) in new_players.into_iter().filter_map(player_info) { + let mut lazy_msg = None; + for (_, client) in (&players, &read_data.clients).join() { + if lazy_msg.is_none() { + lazy_msg = Some(client.prepare(ServerGeneral::PlayerListUpdate( + PlayerListUpdate::Add(*uid, PlayerInfo { + player_alias: player.alias.clone(), + is_online: true, + is_moderator: admins.get(entity).is_some(), + character: None, // new players will be on character select. + }), + ))); } + lazy_msg.as_ref().map(|msg| client.send_prepared(msg)); } } } diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 0f3ea5e3a5..de9ad0a201 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "persistent_world")] +use crate::TerrainPersistence; use crate::{ chunk_generator::ChunkGenerator, client::Client, @@ -8,10 +10,9 @@ use crate::{ SpawnPoint, Tick, }; use common::{ - comp::{self, agent, bird_medium, Alignment, BehaviorCapability, ForceUpdate, Pos, Waypoint}, + comp::{self, agent, bird_medium, BehaviorCapability, ForceUpdate, Pos, Waypoint}, event::{EventBus, ServerEvent}, - generation::{get_npc_name, EntityInfo}, - npc::NPC_NAMES, + generation::EntityInfo, resources::Time, terrain::TerrainGrid, LoadoutBuilder, SkillSetBuilder, @@ -20,10 +21,16 @@ use common_ecs::{Job, Origin, Phase, System}; use common_net::msg::{SerializedTerrainChunk, ServerGeneral}; use common_state::TerrainChanges; use comp::Behavior; +use rand::Rng; use specs::{Entities, Join, Read, ReadExpect, ReadStorage, Write, WriteExpect, WriteStorage}; use std::sync::Arc; use vek::*; +#[cfg(feature = "persistent_world")] +pub type TerrainPersistenceData<'a> = Option>; +#[cfg(not(feature = "persistent_world"))] +pub type TerrainPersistenceData<'a> = (); + pub(crate) struct LazyTerrainMessage { lazy_msg_lo: Option, lazy_msg_hi: Option, @@ -99,6 +106,7 @@ impl<'a> System<'a> for Sys { WriteExpect<'a, TerrainGrid>, Write<'a, TerrainChanges>, WriteExpect<'a, RtSim>, + TerrainPersistenceData<'a>, WriteStorage<'a, Pos>, ReadStorage<'a, Presence>, ReadStorage<'a, Client>, @@ -125,6 +133,7 @@ impl<'a> System<'a> for Sys { mut terrain, mut terrain_changes, mut rtsim, + mut _terrain_persistence, mut positions, presences, clients, @@ -141,7 +150,8 @@ impl<'a> System<'a> for Sys { // Also, send the chunk data to anybody that is close by. let mut new_chunks = Vec::new(); 'insert_terrain_chunks: while let Some((key, res)) = chunk_generator.recv_new_chunk() { - let (chunk, supplement) = match res { + #[allow(unused_mut)] + let (mut chunk, supplement) = match res { Ok((chunk, supplement)) => (chunk, supplement), Err(Some(entity)) => { if let Some(client) = clients.get(entity) { @@ -157,6 +167,12 @@ impl<'a> System<'a> for Sys { }, }; + // Apply changes from terrain persistence to this chunk + #[cfg(feature = "persistent_world")] + if let Some(terrain_persistence) = _terrain_persistence.as_mut() { + terrain_persistence.apply_changes(key, &mut chunk); + } + // Arcify the chunk let chunk = Arc::new(chunk); @@ -183,140 +199,43 @@ impl<'a> System<'a> for Sys { "Chunk spawned entity that wasn't nearby", ); - if entity.is_waypoint { - server_emitter.emit(ServerEvent::CreateWaypoint(entity.pos)); - continue; - } - - let mut body = entity.body; - let name = entity.name.unwrap_or_else(|| "Unnamed".to_string()); - let alignment = entity.alignment; - let mut stats = comp::Stats::new(name); - - let mut scale = entity.scale; - - // Replace stuff if it's a boss - if entity.is_giant { - if rand::random::() < 0.65 && entity.alignment != Alignment::Enemy { - let body_new = comp::humanoid::Body::random(); - let npc_names = NPC_NAMES.read(); - - body = comp::Body::Humanoid(body_new); - stats = comp::Stats::new(format!( - "Gentle Giant {}", - get_npc_name(&npc_names.humanoid, body_new.species) - )); - } - scale = 2.0 + rand::random::(); - } - - let EntityInfo { - skillset_asset, - main_tool, - second_tool, - loadout_asset, - make_loadout, - trading_information: economy, - .. - } = entity; - - let skill_set = { - let skillset_builder = SkillSetBuilder::default(); - if let Some(skillset_asset) = skillset_asset { - skillset_builder.with_asset_expect(&skillset_asset).build() - } else { - skillset_builder.build() - } - }; - - let loadout = { - let mut loadout_builder = LoadoutBuilder::empty(); - let rng = &mut rand::thread_rng(); - - // If main tool is passed, use it. Otherwise fallback to default tool - if let Some(main_tool) = main_tool { - loadout_builder = loadout_builder.active_mainhand(Some(main_tool)); - } else { - loadout_builder = loadout_builder.with_default_maintool(&body); - } - - // If second tool is passed, use it as well - if let Some(second_tool) = second_tool { - loadout_builder = loadout_builder.active_offhand(Some(second_tool)); - } - - // If there is config, apply it. - // If not, use default equipement for this body. - if let Some(asset) = loadout_asset { - loadout_builder = loadout_builder.with_asset_expect(&asset, rng); - } else { - loadout_builder = loadout_builder.with_default_equipment(&body); - } - - // Evaluate lazy function for loadout creation - if let Some(make_loadout) = make_loadout { - loadout_builder = - loadout_builder.with_creator(make_loadout, economy.as_ref()); - } - loadout_builder.build() - }; - - let health = Some(comp::Health::new(body, entity.level.unwrap_or(0))); - let poise = comp::Poise::new(body); - - let can_speak = match body { - comp::Body::Humanoid(_) => alignment == comp::Alignment::Npc, - comp::Body::BirdMedium(bird_medium) => match bird_medium.species { - // Parrots like to have a word in this, too... - bird_medium::Species::Parrot => alignment == comp::Alignment::Npc, - _ => false, + let rng = &mut rand::thread_rng(); + let data = NpcData::from_entity_info(entity, rng); + match data { + NpcData::Waypoint(pos) => { + server_emitter.emit(ServerEvent::CreateWaypoint(pos)); }, - _ => false, - }; - let trade_for_site = if matches!(entity.agent_mark, Some(agent::Mark::Merchant)) { - economy.map(|e| e.id) - } else { - None - }; - - // TODO: This code sets an appropriate base_damage for the enemy. This doesn't - // work because the damage is now saved in an ability - /* - if let Some(item::ItemKind::Tool(item::ToolData { base_damage, .. })) = - &mut loadout.active_item.map(|i| i.item.kind) - { - *base_damage = stats.level.level() as u32 * 3; - } - */ - server_emitter.emit(ServerEvent::CreateNpc { - pos: Pos(entity.pos), - stats, - skill_set, - health, - poise, - loadout, - agent: if entity.has_agency { - Some(comp::Agent::new( - Some(entity.pos), - &body, - Behavior::default() - .maybe_with_capabilities( - can_speak.then(|| BehaviorCapability::SPEAK), - ) - .with_trade_site(trade_for_site), - matches!(entity.agent_mark, Some(agent::Mark::Guard)), - )) - } else { - None + NpcData::Data { + pos, + stats, + skill_set, + health, + poise, + loadout, + agent, + body, + alignment, + scale, + drop_item, + } => { + server_emitter.emit(ServerEvent::CreateNpc { + pos, + stats, + skill_set, + health, + poise, + loadout, + agent, + body, + alignment, + scale, + anchor: Some(comp::Anchor::Chunk(key)), + drop_item, + rtsim_entity: None, + projectile: None, + }); }, - body, - alignment, - scale: comp::Scale(scale), - anchor: Some(comp::Anchor::Chunk(key)), - drop_item: entity.loot_drop, - rtsim_entity: None, - projectile: None, - }) + } } // Insert a safezone if chunk contains the spawn position @@ -404,6 +323,12 @@ impl<'a> System<'a> for Sys { }); for key in chunks_to_remove { + // Register the unloading of this chunk from terrain persistence + #[cfg(feature = "persistent_world")] + if let Some(terrain_persistence) = _terrain_persistence.as_mut() { + terrain_persistence.unload_chunk(key); + } + // TODO: code duplication for chunk insertion between here and state.rs if terrain.remove(key).is_some() { terrain_changes.removed_chunks.insert(key); @@ -415,6 +340,144 @@ impl<'a> System<'a> for Sys { } } +/// Convinient structure to use when you need to create new npc +/// from EntityInfo +// TODO: better name? +pub enum NpcData { + Data { + pos: Pos, + stats: comp::Stats, + skill_set: comp::SkillSet, + health: Option, + poise: comp::Poise, + loadout: comp::inventory::loadout::Loadout, + agent: Option, + body: comp::Body, + alignment: comp::Alignment, + scale: comp::Scale, + drop_item: Option, + }, + Waypoint(Vec3), +} + +impl NpcData { + pub fn from_entity_info(entity: EntityInfo, rng: &mut impl Rng) -> Self { + let EntityInfo { + // flags + is_waypoint, + has_agency, + agent_mark, + alignment, + // stats + body, + name, + scale, + pos, + level, + loot_drop, + // tools and skills + skillset_asset, + main_tool, + second_tool, + loadout_asset, + make_loadout, + trading_information: economy, + // unused + pet: _, // TODO: I had no idea we have this. + } = entity; + + if is_waypoint { + return Self::Waypoint(pos); + } + + let name = name.unwrap_or_else(|| "Unnamed".to_string()); + let stats = comp::Stats::new(name); + + let skill_set = { + let skillset_builder = SkillSetBuilder::default(); + if let Some(skillset_asset) = skillset_asset { + skillset_builder.with_asset_expect(&skillset_asset).build() + } else { + skillset_builder.build() + } + }; + + let loadout = { + let mut loadout_builder = LoadoutBuilder::empty(); + + // If main tool is passed, use it. Otherwise fallback to default tool + if let Some(main_tool) = main_tool { + loadout_builder = loadout_builder.active_mainhand(Some(main_tool)); + } else { + loadout_builder = loadout_builder.with_default_maintool(&body); + } + + // If second tool is passed, use it as well + if let Some(second_tool) = second_tool { + loadout_builder = loadout_builder.active_offhand(Some(second_tool)); + } + + // If there is config, apply it. + // If not, use default equipement for this body. + if let Some(asset) = loadout_asset { + loadout_builder = loadout_builder.with_asset_expect(&asset, rng); + } else { + loadout_builder = loadout_builder.with_default_equipment(&body); + } + + // Evaluate lazy function for loadout creation + if let Some(make_loadout) = make_loadout { + loadout_builder = loadout_builder.with_creator(make_loadout, economy.as_ref()); + } + loadout_builder.build() + }; + + let health = Some(comp::Health::new(body, level.unwrap_or(0))); + let poise = comp::Poise::new(body); + + let can_speak = match body { + comp::Body::Humanoid(_) => true, + comp::Body::BirdMedium(bird_medium) => match bird_medium.species { + // Parrots like to have a word in this, too... + bird_medium::Species::Parrot => alignment == comp::Alignment::Npc, + _ => false, + }, + _ => false, + }; + + let trade_for_site = if matches!(agent_mark, Some(agent::Mark::Merchant)) { + economy.map(|e| e.id) + } else { + None + }; + + let agent = has_agency.then(|| { + comp::Agent::from_body(&body) + .with_behavior( + Behavior::default() + .maybe_with_capabilities(can_speak.then(|| BehaviorCapability::SPEAK)) + .with_trade_site(trade_for_site), + ) + .with_patrol_origin(pos) + .with_no_flee(!matches!(agent_mark, Some(agent::Mark::Guard))) + }); + + NpcData::Data { + pos: Pos(pos), + stats, + skill_set, + health, + poise, + loadout, + agent, + body, + alignment, + scale: comp::Scale(scale), + drop_item: loot_drop, + } + } +} + pub fn chunk_in_vd( player_pos: Vec3, chunk_pos: Vec2, diff --git a/server/src/terrain_persistence.rs b/server/src/terrain_persistence.rs new file mode 100644 index 0000000000..bb61c7d213 --- /dev/null +++ b/server/src/terrain_persistence.rs @@ -0,0 +1,344 @@ +use atomicwrites::{AtomicFile, OverwriteBehavior}; +use common::{ + terrain::{Block, TerrainChunk}, + vol::{RectRasterableVol, WriteVol}, +}; +use hashbrown::HashMap; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use std::{ + any::{type_name, Any}, + fs::File, + io::{self, Read as _, Write as _}, + path::PathBuf, +}; +use tracing::{debug, error, info, warn}; +use vek::*; + +pub struct TerrainPersistence { + path: PathBuf, + chunks: HashMap, Chunk>, +} + +impl TerrainPersistence { + /// Create a new terrain persistence system using the given data directory. + /// + /// If the `VELOREN_TERRAIN` environment variable is set, this will be used + /// as the persistence directory instead. + pub fn new(mut data_dir: PathBuf) -> Self { + let path = std::env::var("VELOREN_TERRAIN") + .map(PathBuf::from) + .unwrap_or_else(|_| { + data_dir.push("terrain"); + data_dir + }); + + std::fs::create_dir_all(&path).expect("Failed to create terrain persistence directory"); + + info!("Using {:?} as the terrain persistence path", path); + + Self { + path, + chunks: HashMap::default(), + } + } + + /// Apply persistence changes to a newly generated chunk. + pub fn apply_changes(&mut self, key: Vec2, terrain_chunk: &mut TerrainChunk) { + let chunk = self.load_chunk(key); + + let mut resets = Vec::new(); + for (rpos, new_block) in chunk.blocks() { + if let Err(e) = terrain_chunk.map(rpos, |block| { + if block == new_block { + resets.push(rpos); + } + new_block + }) { + warn!( + "Could not set block in chunk {:?} with position {:?} (out of bounds?): {:?}", + key, rpos, e + ); + } + } + + // Reset any unchanged blocks (this is an optimisation only) + for rpos in resets { + chunk.reset_block(rpos); + } + } + + /// Maintain terrain persistence (writing changes changes back to + /// filesystem, etc.) + pub fn maintain(&mut self) { + // Currently, this does nothing because filesystem writeback occurs on + // chunk unload However, this is not a particularly reliable + // mechanism (it doesn't survive power loss, say). Later, a more + // reliable strategy should be implemented here. + } + + fn path_for(&self, key: Vec2) -> PathBuf { + let mut path = self.path.clone(); + path.push(format!("chunk_{}_{}.dat", key.x, key.y)); + path + } + + fn load_chunk(&mut self, key: Vec2) -> &mut Chunk { + let path = self.path_for(key); + self.chunks.entry(key).or_insert_with(|| { + File::open(&path) + .ok() + .map(|f| { + let bytes = match std::io::BufReader::new(f) + .bytes() + .collect::, _>>() + { + Ok(bytes) => bytes, + Err(err) => { + error!( + "Failed to read data for chunk {:?} from file: {:?}", + key, err + ); + return Chunk::default(); + }, + }; + match Chunk::deserialize_from(std::io::Cursor::new(bytes)) { + Some(chunk) => chunk, + None => { + // Find an untaken name for a backup + let mut backup_path = path.clone(); + backup_path.set_extension("dat_backup_0"); + let mut i = 1; + while backup_path.exists() { + backup_path.set_extension(format!("dat_backup_{}", i)); + i += 1; + } + + error!( + "Failed to load chunk {:?}, moving possibly corrupt (or too new) \ + data to {:?} for you to repair.", + key, backup_path + ); + if let Err(err) = std::fs::rename(path, backup_path) { + error!("Failed to rename invalid chunk file: {:?}", err); + } + Chunk::default() + }, + } + }) + .unwrap_or_default() + }) + } + + pub fn unload_chunk(&mut self, key: Vec2) { + if let Some(chunk) = self.chunks.remove(&key) { + // No need to write if no blocks have ever been written + if chunk.blocks.is_empty() { + return; + } + + let bytes = match bincode::serialize::(&chunk.prepare_raw()) { + Err(err) => { + error!("Failed to serialize chunk data: {:?}", err); + return; + }, + Ok(bytes) => bytes, + }; + + let atomic_file = + AtomicFile::new(self.path_for(key), OverwriteBehavior::AllowOverwrite); + if let Err(err) = atomic_file.write(|file| file.write_all(&bytes)) { + error!("Failed to write chunk data to file: {:?}", err); + } + } + } + + pub fn unload_all(&mut self) { + for key in self.chunks.keys().copied().collect::>() { + self.unload_chunk(key); + } + } + + pub fn set_block(&mut self, pos: Vec3, block: Block) { + let key = pos + .xy() + .map2(TerrainChunk::RECT_SIZE, |e, sz| e.div_euclid(sz as i32)); + self.load_chunk(key) + .blocks + .insert(pos - key * TerrainChunk::RECT_SIZE.map(|e| e as i32), block); + } +} + +impl Drop for TerrainPersistence { + fn drop(&mut self) { self.unload_all(); } +} + +#[derive(Default, Serialize, Deserialize)] +pub struct Chunk { + blocks: HashMap, Block>, +} + +impl Chunk { + fn deserialize_from(reader: R) -> Option { + version::try_load(reader) + } + + fn prepare_raw(self) -> version::Current { self.into() } + + fn blocks(&self) -> impl Iterator, Block)> + '_ { + self.blocks.iter().map(|(k, b)| (*k, *b)) + } + + fn reset_block(&mut self, rpos: Vec3) { self.blocks.remove(&rpos); } +} + +/// # Adding a new chunk format version +/// +/// Chunk formats are designed to be backwards-compatible when loading, but are +/// not required to be backwards-compatible when saving (i.e: we must always be +/// able to load old formats, but we're not required to save old formats because +/// newer formats might contain richer information that is incompatible with an +/// older format). +/// +/// The steps for doing this are as follows: +/// +/// 1. Create a new 'raw format' type that implements [`Serialize`] and +/// `Deserialize`]. Make sure to add a version field. If in doubt, copy the last +/// raw format and increment the version number wherever it appears. Don't +/// forget to increment the version number in the `serde(deserialize_with = +/// ...}` attribute! Conventionally, these types are named `V{N}` where `{N}` is +/// the number succeeding the previous raw format type. +/// +/// 2. Add an implementation of `From<{YourRawFormat}>` for `Chunk`. As before, +/// see previous versions if in doubt. +/// +/// 3. Change the type of [`version::Current`] to your new raw format type. +/// +/// 4. Add an entry for your raw format at the top of the array in +/// [`version::loaders`]. +/// +/// 5. Remove the `Serialize` implementation from the previous raw format type: +/// we don't need it any longer! +mod version { + use super::*; + + /// The newest supported raw format type. This should be changed every time + /// a new raw format is added. + // Step [3] + pub type Current = V3; + + type LoadChunkFn = fn(R) -> Result; + fn loaders<'a, R: io::Read + Clone>() -> &'a [LoadChunkFn] { + // Step [4] + &[load_raw::, load_raw::, load_raw::] + } + + // Convert back to current + + impl From for Current { + fn from(chunk: Chunk) -> Self { + Self { + version: version_magic(3), + blocks: chunk + .blocks + .into_iter() + .map(|(pos, b)| (pos.x as u8, pos.y as u8, pos.z as i16, b.to_u32())) + .collect(), + } + } + } + + /// Version 3 of the raw chunk format. + #[derive(Serialize, Deserialize)] + pub struct V3 { + #[serde(deserialize_with = "version::<_, 3>")] + pub version: u64, + pub blocks: Vec<(u8, u8, i16, u32)>, + } + + impl From for Chunk { + fn from(v3: V3) -> Self { + Self { + blocks: v3 + .blocks + .into_iter() + .map(|(x, y, z, b)| { + ( + Vec3::new(x as i32, y as i32, z as i32), + Block::from_u32(b).unwrap_or_else(Block::empty), + ) + }) + .collect(), + } + } + } + + /// Version 2 of the raw chunk format. + #[derive(Deserialize)] + pub struct V2 { + #[serde(deserialize_with = "version::<_, 2>")] + pub version: u64, + pub blocks: Vec<(u8, u8, i16, Block)>, + } + + impl From for Chunk { + fn from(v2: V2) -> Self { + Self { + blocks: v2 + .blocks + .into_iter() + .map(|(x, y, z, b)| (Vec3::new(x as i32, y as i32, z as i32), b)) + .collect(), + } + } + } + + /// Version 1 of the raw chunk format. + #[derive(Deserialize)] + pub struct V1 { + pub blocks: HashMap, Block>, + } + + impl From for Chunk { + fn from(v1: V1) -> Self { Self { blocks: v1.blocks } } + } + + // Utility things + + fn version_magic(n: u16) -> u64 { (n as u64) | (0x3352ACEEA789 << 16) } + + fn version<'de, D: serde::Deserializer<'de>, const V: u16>(de: D) -> Result { + u64::deserialize(de).and_then(|x| { + if x == version_magic(V) { + Ok(x) + } else { + Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Unsigned(x), + &"incorrect magic/version bytes", + )) + } + }) + } + + fn load_raw + DeserializeOwned, R: io::Read + Clone>( + reader: R, + ) -> Result { + bincode::deserialize_from::<_, RawChunk>(reader) + .map(Into::into) + .map_err(|e| (type_name::(), e)) + } + + pub fn try_load(reader: R) -> Option { + loaders() + .iter() + .find_map(|load_raw| match load_raw(reader.clone()) { + Ok(chunk) => Some(chunk), + Err((raw_name, e)) => { + debug!( + "Attempt to load chunk with raw format `{}` failed: {:?}", + raw_name, e + ); + None + }, + }) + } +} diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index dfa6fe47b5..0226b36a6b 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -28,6 +28,7 @@ hot-reloading = ["common/hot-reloading"] singleplayer = ["server"] simd = ["vek/platform_intrinsics"] tracy = ["profiling", "profiling/profile-with-tracy", "common-frontend/tracy", "client/tracy"] +tracy-memory = ["tracy"] # enables heap profiling with tracy plugins = ["client/plugins"] egui-ui = ["voxygen-egui", "egui", "egui_wgpu_backend", "egui_winit_platform"] @@ -46,7 +47,7 @@ common-systems = {package = "veloren-common-systems", path = "../common/systems" common-state = {package = "veloren-common-state", path = "../common/state"} anim = {package = "veloren-voxygen-anim", path = "anim"} -i18n = {package = "veloren-i18n", path = "i18n"} +i18n = {package = "veloren-voxygen-i18n", path = "i18n"} voxygen-egui = {package = "veloren-voxygen-egui", path = "egui", optional = true } # Graphics @@ -64,7 +65,7 @@ iced = {package = "iced_native", git = "https://github.com/Imberflur/iced", tag 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 = "9ae8f89014d0b0c5b61d0e821c5aeb6140c5c0dc" } +keyboard-keynames = { git = "https://gitlab.com/Frinksy/keyboard-keynames.git", rev = "721c8b301f8003332d0cc767ab2649fdd330e842" } # EGUI egui = {version = "0.12", optional = true } @@ -82,7 +83,7 @@ vek = {version = "=0.14.1", features = ["serde"]} gilrs = {version = "0.8.0", features = ["serde-serialize"]} # Singleplayer -server = {package = "veloren-server", path = "../server", optional = true} +server = { package = "veloren-server", path = "../server", optional = true, default-features = false, features = ["worldgen"] } # Utility backtrace = "0.3.40" @@ -97,7 +98,6 @@ crossbeam-channel = "0.5" directories-next = "2.0" dot_vox = "4.0" enum-iterator = "0.6" -futures-executor = "0.3" guillotiere = "0.6" hashbrown = {version = "0.11", features = ["rayon", "serde", "nightly"]} image = {version = "0.23.12", default-features = false, features = ["ico", "png"]} diff --git a/voxygen/anim/src/biped_large/alpha.rs b/voxygen/anim/src/biped_large/alpha.rs index 075cd1d758..301e787c95 100644 --- a/voxygen/anim/src/biped_large/alpha.rs +++ b/voxygen/anim/src/biped_large/alpha.rs @@ -57,7 +57,7 @@ impl Animation for AlphaAnimation { * ((acc_vel * lab + PI * 0.4).sin()); let (move1base, move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_large/beam.rs b/voxygen/anim/src/biped_large/beam.rs index c3b9e9a5c8..5d227d6aed 100644 --- a/voxygen/anim/src/biped_large/beam.rs +++ b/voxygen/anim/src/biped_large/beam.rs @@ -75,7 +75,7 @@ impl Animation for BeamAnimation { next.hand_r.orientation = Quaternion::rotation_x(0.0); let (move1base, move2shake, move2base, move3) = match stage_section { Some(StageSection::Buildup) => ((anim_time.powf(0.25)).min(1.0), 0.0, 0.0, 0.0), - Some(StageSection::Cast) => ( + Some(StageSection::Action) => ( 1.0, (anim_time * 15.0 + PI).sin(), (anim_time.powf(0.1)).min(1.0), diff --git a/voxygen/anim/src/biped_large/beta.rs b/voxygen/anim/src/biped_large/beta.rs index 7ed6c398c4..4578ef6478 100644 --- a/voxygen/anim/src/biped_large/beta.rs +++ b/voxygen/anim/src/biped_large/beta.rs @@ -55,7 +55,7 @@ impl Animation for BetaAnimation { * ((acc_vel * lab + PI * 0.4).sin()); let (move1base, move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(6)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_large/chargemelee.rs b/voxygen/anim/src/biped_large/chargemelee.rs index df141df553..4ea74651ea 100644 --- a/voxygen/anim/src/biped_large/chargemelee.rs +++ b/voxygen/anim/src/biped_large/chargemelee.rs @@ -60,7 +60,7 @@ impl Animation for ChargeMeleeAnimation { 0.0, (anim_time * 100.0).sin(), ), - Some(StageSection::Swing) => (1.0, anim_time.powf(0.25), 0.0, 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4), 0.0), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_large/dash.rs b/voxygen/anim/src/biped_large/dash.rs index ce4f84101d..d6afe76e47 100644 --- a/voxygen/anim/src/biped_large/dash.rs +++ b/voxygen/anim/src/biped_large/dash.rs @@ -73,7 +73,7 @@ impl Animation for DashAnimation { 0.0, 0.0, ), - Some(StageSection::Swing) => (1.0, 1.0, 1.0, anim_time.powf(4.0), 0.0), + Some(StageSection::Action) => (1.0, 1.0, 1.0, anim_time.powf(4.0), 0.0), Some(StageSection::Recover) => (1.1, 1.0, 1.0, 1.0, anim_time.powf(4.0)), _ => (0.0, 0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_large/leapmelee.rs b/voxygen/anim/src/biped_large/leapmelee.rs index 4880a9a6b3..9ec48da224 100644 --- a/voxygen/anim/src/biped_large/leapmelee.rs +++ b/voxygen/anim/src/biped_large/leapmelee.rs @@ -34,7 +34,7 @@ impl Animation for LeapAnimation { let (movement1, movement2, movement3, movement4) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0, 0.0), Some(StageSection::Movement) => (1.0, anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, 1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Action) => (1.0, 1.0, anim_time.powf(0.25), 0.0), Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_large/selfbuff.rs b/voxygen/anim/src/biped_large/selfbuff.rs index 68ff2031b0..3643987ee5 100644 --- a/voxygen/anim/src/biped_large/selfbuff.rs +++ b/voxygen/anim/src/biped_large/selfbuff.rs @@ -60,7 +60,7 @@ impl Animation for SelfBuffAnimation { (anim_time * 10.0).sin(), 0.0, ), - Some(StageSection::Cast) => { + Some(StageSection::Action) => { (1.0, 0.0, (anim_time * 30.0).sin(), (anim_time * 12.0).sin()) }, Some(StageSection::Recover) => (1.0, anim_time.powi(4), 1.0, 1.0), diff --git a/voxygen/anim/src/biped_large/shockwave.rs b/voxygen/anim/src/biped_large/shockwave.rs index c55a237e67..759a6c6cbd 100644 --- a/voxygen/anim/src/biped_large/shockwave.rs +++ b/voxygen/anim/src/biped_large/shockwave.rs @@ -44,7 +44,7 @@ impl Animation for ShockwaveAnimation { let (move1, move1pow, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time, anim_time.powf(0.25) as f32, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, 1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, 1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_large/shoot.rs b/voxygen/anim/src/biped_large/shoot.rs index 0c8348402b..1e69916a0a 100644 --- a/voxygen/anim/src/biped_large/shoot.rs +++ b/voxygen/anim/src/biped_large/shoot.rs @@ -94,7 +94,7 @@ impl Animation for ShootAnimation { Some(StageSection::Buildup) => { (anim_time, (anim_time * 10.0 + PI).sin(), 0.0, 0.0) }, - Some(StageSection::Swing) => (1.0, 1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Action) => (1.0, 1.0, anim_time.powf(0.25), 0.0), Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0, 0.0), }; @@ -143,7 +143,7 @@ impl Animation for ShootAnimation { Some(ToolKind::Bow) => { let (move1base, move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; @@ -193,7 +193,7 @@ impl Animation for ShootAnimation { "Wendigo Magic" => { let (move1base, _move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; @@ -223,7 +223,7 @@ impl Animation for ShootAnimation { "Yeti" => { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; @@ -266,7 +266,7 @@ impl Animation for ShootAnimation { "Harvester" => { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_large/spin.rs b/voxygen/anim/src/biped_large/spin.rs index c786303b21..213d8b943d 100644 --- a/voxygen/anim/src/biped_large/spin.rs +++ b/voxygen/anim/src/biped_large/spin.rs @@ -34,7 +34,7 @@ impl Animation for SpinAnimation { let (movement1, movement2, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powf(1.8), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(1.8), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_large/spinmelee.rs b/voxygen/anim/src/biped_large/spinmelee.rs index eee429c8d6..57e7fa07bc 100644 --- a/voxygen/anim/src/biped_large/spinmelee.rs +++ b/voxygen/anim/src/biped_large/spinmelee.rs @@ -46,7 +46,7 @@ impl Animation for SpinMeleeAnimation { let (move1base, move2, move3) = match stage_section { Some(StageSection::Buildup) => ((anim_time.powf(0.25)), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, (anim_time * 0.05).sin() - 0.05, 0.0), + Some(StageSection::Action) => (1.0, (anim_time * 0.05).sin() - 0.05, 0.0), Some(StageSection::Recover) => (1.0, 0.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_large/spritesummon.rs b/voxygen/anim/src/biped_large/spritesummon.rs index 9a7e918e8e..0b17e9f699 100644 --- a/voxygen/anim/src/biped_large/spritesummon.rs +++ b/voxygen/anim/src/biped_large/spritesummon.rs @@ -60,7 +60,7 @@ impl Animation for SpriteSummonAnimation { Some(StageSection::Buildup) => { (anim_time, anim_time.powf(0.1), 0.0, 0.0) }, - Some(StageSection::Cast) => { + Some(StageSection::Action) => { (1.0, 1.0, (anim_time.powf(4.0) * 80.0).min(1.0), 0.0) }, Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time), diff --git a/voxygen/anim/src/biped_large/summon.rs b/voxygen/anim/src/biped_large/summon.rs index 24960c3ec9..f335e6c62e 100644 --- a/voxygen/anim/src/biped_large/summon.rs +++ b/voxygen/anim/src/biped_large/summon.rs @@ -57,7 +57,7 @@ impl Animation for SummonAnimation { let (move1base, move2base, move3) = match stage_section { Some(StageSection::Buildup) => ((anim_time.powf(0.5)), 0.0, 0.0), - Some(StageSection::Cast) => (1.0, (anim_time.powi(2)), 0.0), + Some(StageSection::Action) => (1.0, (anim_time.powi(2)), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; @@ -126,7 +126,7 @@ impl Animation for SummonAnimation { "Tidal Warrior" => { let (move1base, move2base, move3) = match stage_section { Some(StageSection::Buildup) => ((anim_time.powi(2)), 0.0, 0.0), - Some(StageSection::Cast) => (1.0, (anim_time * 30.0).sin(), 0.0), + Some(StageSection::Action) => (1.0, (anim_time * 30.0).sin(), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_small/alpha.rs b/voxygen/anim/src/biped_small/alpha.rs index 0a14e920fb..db095e6f03 100644 --- a/voxygen/anim/src/biped_small/alpha.rs +++ b/voxygen/anim/src/biped_small/alpha.rs @@ -56,7 +56,7 @@ impl Animation for AlphaAnimation { let anim_time = anim_time.min(1.0); let (move1base, move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powi(4), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powi(4), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_small/dash.rs b/voxygen/anim/src/biped_small/dash.rs index da2077dccb..0d3d7292b2 100644 --- a/voxygen/anim/src/biped_small/dash.rs +++ b/voxygen/anim/src/biped_small/dash.rs @@ -55,7 +55,7 @@ impl Animation for DashAnimation { let (move1base, move2base, move3, move4) = match stage_section { Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0, 0.0), Some(StageSection::Charge) => (1.0, anim_time.powi(4), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, 1.0, anim_time.powi(4), 0.0), + Some(StageSection::Action) => (1.0, 1.0, anim_time.powi(4), 0.0), Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/biped_small/shoot.rs b/voxygen/anim/src/biped_small/shoot.rs index 4368f70a78..6604848f8c 100644 --- a/voxygen/anim/src/biped_small/shoot.rs +++ b/voxygen/anim/src/biped_small/shoot.rs @@ -82,7 +82,7 @@ impl Animation for ShootAnimation { Some(ToolKind::Bow) => { let (move1base, move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; @@ -113,7 +113,7 @@ impl Animation for ShootAnimation { Some(ToolKind::Staff) => { let (move1base, _move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/bird_large/alpha.rs b/voxygen/anim/src/bird_large/alpha.rs index fef6419431..d2e718e96a 100644 --- a/voxygen/anim/src/bird_large/alpha.rs +++ b/voxygen/anim/src/bird_large/alpha.rs @@ -25,7 +25,7 @@ impl Animation for AlphaAnimation { let (move1base, move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(4.0)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/bird_large/breathe.rs b/voxygen/anim/src/bird_large/breathe.rs index 798ad1cd47..94e6a8891a 100644 --- a/voxygen/anim/src/bird_large/breathe.rs +++ b/voxygen/anim/src/bird_large/breathe.rs @@ -35,7 +35,7 @@ impl Animation for BreatheAnimation { 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::Action) => (1.0, anim_time.min(1.0).powf(0.1), 0.0, anim_time), Some(StageSection::Recover) => (1.0, 1.0, anim_time, 1.0), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/bird_large/dash.rs b/voxygen/anim/src/bird_large/dash.rs index c177986d15..435a4aa6fb 100644 --- a/voxygen/anim/src/bird_large/dash.rs +++ b/voxygen/anim/src/bird_large/dash.rs @@ -39,7 +39,7 @@ impl Animation for DashAnimation { 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::Action) => (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), }; diff --git a/voxygen/anim/src/bird_large/shockwave.rs b/voxygen/anim/src/bird_large/shockwave.rs index ea40d09785..4bac2d216a 100644 --- a/voxygen/anim/src/bird_large/shockwave.rs +++ b/voxygen/anim/src/bird_large/shockwave.rs @@ -26,7 +26,7 @@ impl Animation for ShockwaveAnimation { let (movement1base, movement2base, movement3, _twitch) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.5), 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::Action) => (1.0, anim_time.min(1.0).powf(0.1), 0.0, anim_time), Some(StageSection::Recover) => (1.0, 1.0, anim_time, 1.0), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/bird_large/shoot.rs b/voxygen/anim/src/bird_large/shoot.rs index 2b346cc762..17bf8b54c9 100644 --- a/voxygen/anim/src/bird_large/shoot.rs +++ b/voxygen/anim/src/bird_large/shoot.rs @@ -27,7 +27,7 @@ impl Animation for ShootAnimation { 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::Action) => (1.0, anim_time.min(1.0).powf(0.1), 0.0, anim_time), Some(StageSection::Recover) => (1.0, 1.0, anim_time, 1.0), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/bird_large/summon.rs b/voxygen/anim/src/bird_large/summon.rs index c650af8fc3..551d50abad 100644 --- a/voxygen/anim/src/bird_large/summon.rs +++ b/voxygen/anim/src/bird_large/summon.rs @@ -28,7 +28,7 @@ impl Animation for SummonAnimation { 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::Action) => (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), }; diff --git a/voxygen/anim/src/character/alpha.rs b/voxygen/anim/src/character/alpha.rs index c7bcfbe010..b9c351f1c7 100644 --- a/voxygen/anim/src/character/alpha.rs +++ b/voxygen/anim/src/character/alpha.rs @@ -35,7 +35,7 @@ impl Animation for AlphaAnimation { let (move1, move2, move3, move2h) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powi(2), 0.0, anim_time.powf(0.25)), + Some(StageSection::Action) => (1.0, anim_time.powi(2), 0.0, anim_time.powf(0.25)), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4), 1.0), _ => (0.0, 0.0, 0.0, 0.0), }; @@ -63,7 +63,7 @@ impl Animation for AlphaAnimation { Some(ToolKind::Axe) => { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; @@ -79,7 +79,7 @@ impl Animation for AlphaAnimation { Some(ToolKind::Hammer) | Some(ToolKind::Pick) => { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; @@ -135,7 +135,7 @@ impl Animation for AlphaAnimation { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; @@ -156,7 +156,7 @@ impl Animation for AlphaAnimation { Some(ToolKind::Hammer) | Some(ToolKind::Pick) => { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/character/beam.rs b/voxygen/anim/src/character/beam.rs index fc6d6bbde9..fe17d583df 100644 --- a/voxygen/anim/src/character/beam.rs +++ b/voxygen/anim/src/character/beam.rs @@ -38,7 +38,7 @@ impl Animation for BeamAnimation { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Cast) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/character/beta.rs b/voxygen/anim/src/character/beta.rs index c84a4700c4..e22a9e1de3 100644 --- a/voxygen/anim/src/character/beta.rs +++ b/voxygen/anim/src/character/beta.rs @@ -37,7 +37,7 @@ impl Animation for BetaAnimation { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/character/block.rs b/voxygen/anim/src/character/block.rs index bf6edd54fc..45f96bbf60 100644 --- a/voxygen/anim/src/character/block.rs +++ b/voxygen/anim/src/character/block.rs @@ -45,7 +45,7 @@ impl Animation for BlockAnimation { let (movement1base, move2, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, (anim_time * 10.0).sin(), 0.0), + Some(StageSection::Action) => (1.0, (anim_time * 10.0).sin(), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(4.0)), _ => (0.0, 0.0, 0.0), diff --git a/voxygen/anim/src/character/chargeswing.rs b/voxygen/anim/src/character/chargeswing.rs index e054376e91..3cb36eefe2 100644 --- a/voxygen/anim/src/character/chargeswing.rs +++ b/voxygen/anim/src/character/chargeswing.rs @@ -50,7 +50,7 @@ impl Animation for ChargeswingAnimation { (anim_time * 18.0 * lab).sin(), 0.0, ), - Some(StageSection::Swing) => (1.0, anim_time.powf(0.25), 0.0, 0.0, anim_time.powi(4)), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0, 0.0, anim_time.powi(4)), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4), 0.0, 1.0), _ => (0.0, 0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/character/consume.rs b/voxygen/anim/src/character/consume.rs index 5b25c93bbc..60d4a72177 100644 --- a/voxygen/anim/src/character/consume.rs +++ b/voxygen/anim/src/character/consume.rs @@ -30,7 +30,7 @@ impl Animation for ConsumeAnimation { Some(ItemUseKind::Consumable(ConsumableKind::Drink)) => { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Use) => (1.0, (anim_time * 8.0).sin(), 0.0), + Some(StageSection::Action) => (1.0, (anim_time * 8.0).sin(), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(0.25)), _ => (0.0, 0.0, 0.0), }; @@ -59,7 +59,7 @@ impl Animation for ConsumeAnimation { Some(ItemUseKind::Consumable(ConsumableKind::Food | ConsumableKind::ComplexFood)) => { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Use) => (1.0, (anim_time * 12.0).sin(), 0.0), + Some(StageSection::Action) => (1.0, (anim_time * 12.0).sin(), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(0.25)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/character/dash.rs b/voxygen/anim/src/character/dash.rs index f8a82a9ac5..a217f3fbb6 100644 --- a/voxygen/anim/src/character/dash.rs +++ b/voxygen/anim/src/character/dash.rs @@ -38,7 +38,7 @@ impl Animation for DashAnimation { let (movement1, movement2, movement3, move4) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0, 0.0), Some(StageSection::Charge) => (1.0, anim_time, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, 1.0, anim_time.powf(0.01), 0.0), + Some(StageSection::Action) => (1.0, 1.0, anim_time.powf(0.01), 0.0), Some(StageSection::Recover) => (1.1, 1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/character/glidewield.rs b/voxygen/anim/src/character/glidewield.rs index 8118fb117b..e142946b12 100644 --- a/voxygen/anim/src/character/glidewield.rs +++ b/voxygen/anim/src/character/glidewield.rs @@ -5,8 +5,9 @@ use super::{ pub struct GlideWieldAnimation; +type GlideWieldAnimationDependency = (Quaternion, Quaternion); impl Animation for GlideWieldAnimation { - type Dependency<'a> = (); + type Dependency<'a> = GlideWieldAnimationDependency; type Skeleton = CharacterSkeleton; #[cfg(feature = "use-dyn-lib")] @@ -16,21 +17,25 @@ impl Animation for GlideWieldAnimation { fn update_skeleton_inner<'a>( skeleton: &Self::Skeleton, - _: Self::Dependency<'a>, + (orientation, glider_orientation): Self::Dependency<'a>, _anim_time: f32, rate: &mut f32, s_a: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); + let glider_ori = orientation.inverse() * glider_orientation; + let glider_pos = Vec3::new(0.0, -5.0, 13.0); *rate = 1.0; - next.hand_l.position = Vec3::new(-2.0 - s_a.hand.0, s_a.hand.1, s_a.hand.2 + 15.0); + next.hand_l.position = + glider_pos + glider_ori * Vec3::new(-s_a.hand.0 + -2.0, s_a.hand.1 + 8.0, s_a.hand.2); next.hand_l.orientation = Quaternion::rotation_x(3.35) * Quaternion::rotation_y(0.2); - next.hand_r.position = Vec3::new(2.0 + s_a.hand.0, s_a.hand.1, s_a.hand.2 + 15.0); + next.hand_r.position = + glider_pos + glider_ori * Vec3::new(s_a.hand.0 + 2.0, s_a.hand.1 + 8.0, s_a.hand.2); next.hand_r.orientation = Quaternion::rotation_x(3.35) * Quaternion::rotation_y(-0.2); next.glider.scale = Vec3::one() * 1.0; - next.glider.orientation = Quaternion::rotation_x(0.35); + next.glider.orientation = glider_ori; next.glider.position = Vec3::new(0.0, -5.0, 13.0); diff --git a/voxygen/anim/src/character/leapmelee.rs b/voxygen/anim/src/character/leapmelee.rs index b9268f368d..1c65b230b2 100644 --- a/voxygen/anim/src/character/leapmelee.rs +++ b/voxygen/anim/src/character/leapmelee.rs @@ -38,7 +38,7 @@ impl Animation for LeapAnimation { let (movement1, movement2, movement3, move4) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0, 0.0), Some(StageSection::Movement) => (1.0, anim_time.powi(2), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, 1.0, anim_time.powf(0.75), 0.0), + Some(StageSection::Action) => (1.0, 1.0, anim_time.powf(0.75), 0.0), Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time.powf(0.75)), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/character/mod.rs b/voxygen/anim/src/character/mod.rs index 6ac4026436..ae9641dbf0 100644 --- a/voxygen/anim/src/character/mod.rs +++ b/voxygen/anim/src/character/mod.rs @@ -231,21 +231,7 @@ impl<'a> From<&'a Body> for SkeletonAttr { fn from(body: &'a Body) -> Self { use comp::humanoid::{BodyType::*, Species::*}; Self { - scaler: match (body.species, body.body_type) { - // TODO : Derive scale from body proportions - (Orc, Male) => 0.91, - (Orc, Female) => 0.81, - (Human, Male) => 0.81, - (Human, Female) => 0.76, - (Elf, Male) => 0.82, - (Elf, Female) => 0.76, - (Dwarf, Male) => 0.67, - (Dwarf, Female) => 0.62, - (Undead, Male) => 0.78, - (Undead, Female) => 0.72, - (Danari, Male) => 0.56, - (Danari, Female) => 0.56, - }, + scaler: body.scaler(), head_scale: match (body.species, body.body_type) { (Orc, Male) => 0.9, (Orc, Female) => 0.9, diff --git a/voxygen/anim/src/character/repeater.rs b/voxygen/anim/src/character/repeater.rs index 6ef0f087c7..3da66f57a0 100644 --- a/voxygen/anim/src/character/repeater.rs +++ b/voxygen/anim/src/character/repeater.rs @@ -39,7 +39,7 @@ impl Animation for RepeaterAnimation { let (move1base, move2base, move3base, move4) = match stage_section { Some(StageSection::Movement) => (anim_time, 0.0, 0.0, 0.0), Some(StageSection::Buildup) => (1.0, anim_time, 0.0, 0.0), - Some(StageSection::Shoot) => (1.0, 1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, 1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.1, 1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/character/shockwave.rs b/voxygen/anim/src/character/shockwave.rs index db2c557c8f..1bb97225da 100644 --- a/voxygen/anim/src/character/shockwave.rs +++ b/voxygen/anim/src/character/shockwave.rs @@ -40,7 +40,7 @@ impl Animation for ShockwaveAnimation { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Swing) | Some(StageSection::Cast) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/character/shoot.rs b/voxygen/anim/src/character/shoot.rs index df7ca83f22..a21f623f37 100644 --- a/voxygen/anim/src/character/shoot.rs +++ b/voxygen/anim/src/character/shoot.rs @@ -69,7 +69,7 @@ impl Animation for ShootAnimation { Some(ToolKind::Staff) | Some(ToolKind::Sceptre) => { let (move1, move2, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Shoot) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; @@ -123,7 +123,7 @@ impl Animation for ShootAnimation { Some(ToolKind::Bow) => { let (_move1, move2, _move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Shoot) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/character/spin.rs b/voxygen/anim/src/character/spin.rs index a9413004db..8aa3357bbf 100644 --- a/voxygen/anim/src/character/spin.rs +++ b/voxygen/anim/src/character/spin.rs @@ -37,7 +37,7 @@ impl Animation for SpinAnimation { let (movement1base, movement2base, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; @@ -75,7 +75,7 @@ impl Animation for SpinAnimation { Some(ToolKind::Axe) => { let (movement1, movement2, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; @@ -156,7 +156,7 @@ impl Animation for SpinAnimation { Quaternion::rotation_x(s_a.ahr.3) * Quaternion::rotation_z(s_a.ahr.5); let (move1, move2, _move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/character/spinmelee.rs b/voxygen/anim/src/character/spinmelee.rs index 48106230f2..f994546c4a 100644 --- a/voxygen/anim/src/character/spinmelee.rs +++ b/voxygen/anim/src/character/spinmelee.rs @@ -35,7 +35,7 @@ impl Animation for SpinMeleeAnimation { *rate = 1.0; let (movement1, movement2, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(2.0)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/golem/alpha.rs b/voxygen/anim/src/golem/alpha.rs index b0c5e6eb37..af46debe38 100644 --- a/voxygen/anim/src/golem/alpha.rs +++ b/voxygen/anim/src/golem/alpha.rs @@ -26,7 +26,7 @@ impl Animation for AlphaAnimation { let (move1base, move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(4.0)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/golem/beam.rs b/voxygen/anim/src/golem/beam.rs index f9d8132f9b..8a3d22a82b 100644 --- a/voxygen/anim/src/golem/beam.rs +++ b/voxygen/anim/src/golem/beam.rs @@ -25,7 +25,7 @@ impl Animation for BeamAnimation { let (move1base, move1iso, move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Cast) => (1.0, 0.0, 1.0, 0.0), + Some(StageSection::Action) => (1.0, 0.0, 1.0, 0.0), Some(StageSection::Recover) => (1.0, 0.0, 1.0, anim_time.powf(4.0)), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/golem/shockwave.rs b/voxygen/anim/src/golem/shockwave.rs index 47efe596cc..d4d2a5ffaa 100644 --- a/voxygen/anim/src/golem/shockwave.rs +++ b/voxygen/anim/src/golem/shockwave.rs @@ -27,7 +27,7 @@ impl Animation for ShockwaveAnimation { let (move1base, move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(2.0)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/golem/shoot.rs b/voxygen/anim/src/golem/shoot.rs index d5ffc61f38..8c1fa16a6b 100644 --- a/voxygen/anim/src/golem/shoot.rs +++ b/voxygen/anim/src/golem/shoot.rs @@ -27,7 +27,7 @@ impl Animation for ShootAnimation { let (move1base, move2base, move3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.4), 0.0, 0.0), - Some(StageSection::Shoot) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(4.0)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/golem/spinmelee.rs b/voxygen/anim/src/golem/spinmelee.rs index aef2b59288..14542de232 100644 --- a/voxygen/anim/src/golem/spinmelee.rs +++ b/voxygen/anim/src/golem/spinmelee.rs @@ -26,7 +26,7 @@ impl Animation for SpinMeleeAnimation { let (movement1, movement2, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(4.0)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/object/shoot.rs b/voxygen/anim/src/object/shoot.rs index f3375a02db..0f08af91d4 100644 --- a/voxygen/anim/src/object/shoot.rs +++ b/voxygen/anim/src/object/shoot.rs @@ -35,7 +35,7 @@ impl Animation for ShootAnimation { let (movement1, movement2, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/quadruped_low/alpha.rs b/voxygen/anim/src/quadruped_low/alpha.rs index 1fb1812632..9ca4ebb5ab 100644 --- a/voxygen/anim/src/quadruped_low/alpha.rs +++ b/voxygen/anim/src/quadruped_low/alpha.rs @@ -26,7 +26,7 @@ impl Animation for AlphaAnimation { let (movement1base, movement2base, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powi(4), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powi(4), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/quadruped_low/beta.rs b/voxygen/anim/src/quadruped_low/beta.rs index d09eb8933f..e3b4f814f2 100644 --- a/voxygen/anim/src/quadruped_low/beta.rs +++ b/voxygen/anim/src/quadruped_low/beta.rs @@ -26,7 +26,7 @@ impl Animation for BetaAnimation { let (movement1base, movement2base, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powi(4), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powi(4), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/quadruped_low/breathe.rs b/voxygen/anim/src/quadruped_low/breathe.rs index c70f4bdb9a..5a8be10711 100644 --- a/voxygen/anim/src/quadruped_low/breathe.rs +++ b/voxygen/anim/src/quadruped_low/breathe.rs @@ -28,7 +28,7 @@ impl Animation for BreatheAnimation { let (movement1base, _movement2base, movement3, twitch) = match stage_section { Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0, 0.0), - Some(StageSection::Cast) => (1.0, anim_time.min(1.0), 0.0, anim_time), + Some(StageSection::Action) => (1.0, anim_time.min(1.0), 0.0, anim_time), Some(StageSection::Recover) => (1.0, 1.0, anim_time, 1.0), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/quadruped_low/dash.rs b/voxygen/anim/src/quadruped_low/dash.rs index c555ddee0b..aae6349ab9 100644 --- a/voxygen/anim/src/quadruped_low/dash.rs +++ b/voxygen/anim/src/quadruped_low/dash.rs @@ -28,7 +28,7 @@ impl Animation for DashAnimation { let (movement1base, chargemovementbase, movement2base, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0, 0.0), Some(StageSection::Charge) => (1.0, 1.0, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, 1.0, anim_time.powi(4), 0.0), + Some(StageSection::Action) => (1.0, 1.0, anim_time.powi(4), 0.0), Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/quadruped_low/shoot.rs b/voxygen/anim/src/quadruped_low/shoot.rs index 9f43cc2463..e827e9e50f 100644 --- a/voxygen/anim/src/quadruped_low/shoot.rs +++ b/voxygen/anim/src/quadruped_low/shoot.rs @@ -25,7 +25,7 @@ impl Animation for ShootAnimation { let (movement1, movement2, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/quadruped_low/tailwhip.rs b/voxygen/anim/src/quadruped_low/tailwhip.rs index 5cb36a14f2..e1d2dc741c 100644 --- a/voxygen/anim/src/quadruped_low/tailwhip.rs +++ b/voxygen/anim/src/quadruped_low/tailwhip.rs @@ -27,7 +27,7 @@ impl Animation for TailwhipAnimation { Some(StageSection::Charge) => { (anim_time.min(1.2), 0.0, 0.0, (anim_time * 15.0).sin(), 0.0) }, - Some(StageSection::Swing) => (1.0, anim_time.powi(4), 0.0, 1.0, 0.0), + Some(StageSection::Action) => (1.0, anim_time.powi(4), 0.0, 1.0, 0.0), Some(StageSection::Recover) => { (1.0, 1.0, anim_time.powi(6), 1.0, (anim_time * 7.0).sin()) }, diff --git a/voxygen/anim/src/quadruped_medium/alpha.rs b/voxygen/anim/src/quadruped_medium/alpha.rs index 62c920a8fc..771d7dedd0 100644 --- a/voxygen/anim/src/quadruped_medium/alpha.rs +++ b/voxygen/anim/src/quadruped_medium/alpha.rs @@ -26,7 +26,7 @@ impl Animation for AlphaAnimation { let (movement1base, movement2base, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powf(0.25), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/quadruped_medium/beta.rs b/voxygen/anim/src/quadruped_medium/beta.rs index 9ab747861c..7bf1bee59d 100644 --- a/voxygen/anim/src/quadruped_medium/beta.rs +++ b/voxygen/anim/src/quadruped_medium/beta.rs @@ -26,7 +26,7 @@ impl Animation for BetaAnimation { let (movement1base, movement2base, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.sqrt(), 0.0), + Some(StageSection::Action) => (1.0, anim_time.sqrt(), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/quadruped_medium/dash.rs b/voxygen/anim/src/quadruped_medium/dash.rs index 64e7efdd64..a3e90fbc1e 100644 --- a/voxygen/anim/src/quadruped_medium/dash.rs +++ b/voxygen/anim/src/quadruped_medium/dash.rs @@ -29,7 +29,7 @@ impl Animation for DashAnimation { 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, 1.0, anim_time.powi(4), 0.0, 1.0), + Some(StageSection::Action) => (1.0, 1.0, anim_time.powi(4), 0.0, 1.0), Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time, 1.0), _ => (0.0, 0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/quadruped_medium/hoof.rs b/voxygen/anim/src/quadruped_medium/hoof.rs index a48a2dbc06..5dbc7021cd 100644 --- a/voxygen/anim/src/quadruped_medium/hoof.rs +++ b/voxygen/anim/src/quadruped_medium/hoof.rs @@ -27,7 +27,7 @@ impl Animation for HoofAnimation { let (movement1base, movement2base, movement3, twitch) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powf(0.25), 0.0, anim_time), + Some(StageSection::Action) => (1.0, anim_time.powf(0.25), 0.0, anim_time), Some(StageSection::Recover) => (1.0, 1.0, anim_time.powi(4), 1.0), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/quadruped_medium/leapmelee.rs b/voxygen/anim/src/quadruped_medium/leapmelee.rs index 2b284869c6..f3551f264f 100644 --- a/voxygen/anim/src/quadruped_medium/leapmelee.rs +++ b/voxygen/anim/src/quadruped_medium/leapmelee.rs @@ -27,7 +27,7 @@ impl Animation for LeapMeleeAnimation { let (movement1base, movement2base, movement3base, movement4) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0, 0.0), Some(StageSection::Movement) => (1.0, anim_time, 0.0, 0.0), - Some(StageSection::Swing) => (1.0, 1.0, anim_time, 0.0), + Some(StageSection::Action) => (1.0, 1.0, anim_time, 0.0), Some(StageSection::Recover) => (0.0, 1.0, 1.0, anim_time.powi(4)), _ => (0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/quadruped_small/alpha.rs b/voxygen/anim/src/quadruped_small/alpha.rs index fd46a10240..41636c5adc 100644 --- a/voxygen/anim/src/quadruped_small/alpha.rs +++ b/voxygen/anim/src/quadruped_small/alpha.rs @@ -26,7 +26,7 @@ impl Animation for AlphaAnimation { let (movement1base, movement2base, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.sqrt(), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powi(4), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powi(4), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/theropod/alpha.rs b/voxygen/anim/src/theropod/alpha.rs index ba96806833..ead443e2c6 100644 --- a/voxygen/anim/src/theropod/alpha.rs +++ b/voxygen/anim/src/theropod/alpha.rs @@ -25,7 +25,7 @@ impl Animation for AlphaAnimation { let (movement1base, movement2base, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powi(2), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powi(4), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powi(4), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/theropod/beta.rs b/voxygen/anim/src/theropod/beta.rs index f3dba2e2bc..51e201aafc 100644 --- a/voxygen/anim/src/theropod/beta.rs +++ b/voxygen/anim/src/theropod/beta.rs @@ -26,7 +26,7 @@ impl Animation for BetaAnimation { let (movement1base, movement2base, movement3) = match stage_section { Some(StageSection::Buildup) => (anim_time.powi(2), 0.0, 0.0), - Some(StageSection::Swing) => (1.0, anim_time.powi(4), 0.0), + Some(StageSection::Action) => (1.0, anim_time.powi(4), 0.0), Some(StageSection::Recover) => (1.0, 1.0, anim_time), _ => (0.0, 0.0, 0.0), }; diff --git a/voxygen/anim/src/theropod/dash.rs b/voxygen/anim/src/theropod/dash.rs index c93d76ae5f..31af69d589 100644 --- a/voxygen/anim/src/theropod/dash.rs +++ b/voxygen/anim/src/theropod/dash.rs @@ -27,7 +27,7 @@ impl Animation for DashAnimation { 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, 1.0, anim_time.powi(4), 0.0, 1.0), + Some(StageSection::Action) => (1.0, 1.0, anim_time.powi(4), 0.0, 1.0), Some(StageSection::Recover) => (1.0, 1.0, 1.0, anim_time, 1.0), _ => (0.0, 0.0, 0.0, 0.0, 0.0), }; diff --git a/voxygen/egui/src/character_states.rs b/voxygen/egui/src/character_states.rs index 52b1c9df13..39a8660119 100644 --- a/voxygen/egui/src/character_states.rs +++ b/voxygen/egui/src/character_states.rs @@ -24,7 +24,7 @@ pub fn draw_char_state_group( CharacterState::Dance | CharacterState::Idle | CharacterState::Sit - | CharacterState::GlideWield + | CharacterState::GlideWield(_) | CharacterState::Sneak | CharacterState::Talk | CharacterState::Wielding => {}, diff --git a/voxygen/egui/src/lib.rs b/voxygen/egui/src/lib.rs index 0470c4f82d..c4249866e9 100644 --- a/voxygen/egui/src/lib.rs +++ b/voxygen/egui/src/lib.rs @@ -229,7 +229,9 @@ pub fn maintain_egui_inner( } }; + let start_pos = Pos2 { x: 300.0, y: 0.0 }; egui::Window::new("Debug Control") + .default_pos(start_pos) .default_width(200.0) .default_height(200.0) .show(&platform.context(), |ui| { diff --git a/voxygen/i18n/Cargo.toml b/voxygen/i18n/Cargo.toml index 388586679b..5ebbbb05d1 100644 --- a/voxygen/i18n/Cargo.toml +++ b/voxygen/i18n/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["juliancoffee ", "Rémy Phelipot"] edition = "2018" -name = "veloren-i18n" +name = "veloren-voxygen-i18n" description = "Crate for internalization and diagnostic of existing localizations." version = "0.10.0" diff --git a/voxygen/i18n/src/analysis.rs b/voxygen/i18n/src/analysis.rs index dcf8ec3f11..93150dbee6 100644 --- a/voxygen/i18n/src/analysis.rs +++ b/voxygen/i18n/src/analysis.rs @@ -1,648 +1,247 @@ -use ron::de::from_bytes; -use std::path::{Path, PathBuf}; - -use crate::data::{ - i18n_directories, LocalizationFragment, RawLocalization, LANG_MANIFEST_FILE, REFERENCE_LANG, +use crate::{ + gitfragments::{ + read_file_from_path, transform_fragment, LocalizationEntryState, LocalizationState, + }, + path::{BasePath, LangPath}, + raw::{self, RawFragment, RawLanguage}, + stats::{ + print_csv_stats, print_overall_stats, print_translation_stats, LocalizationAnalysis, + LocalizationStats, + }, + REFERENCE_LANG, }; -use hashbrown::{HashMap, HashSet}; +use hashbrown::{hash_map::Entry, HashMap}; +use ron::de::from_bytes; -#[derive(Copy, Clone, Eq, Hash, Debug, PartialEq)] -enum LocalizationState { - UpToDate, - NotFound, - Outdated, - Unknown, - Unused, -} - -#[derive(Debug, PartialEq)] -struct LocalizationStats { - uptodate_entries: usize, - outdated_entries: usize, - unused_entries: usize, - notfound_entries: usize, - errors: usize, - real_entry_count: usize, -} - -#[derive(Default)] -struct LocalizationAnalysis { - notfound: Vec<(String, Option)>, - unused: Vec<(String, Option)>, - outdated: Vec<(String, Option)>, - unknown: Vec<(String, Option)>, -} - -impl LocalizationAnalysis { - fn get_mut( - &mut self, - state: LocalizationState, - ) -> Option<&mut Vec<(String, Option)>> { - match state { - LocalizationState::NotFound => Some(&mut self.notfound), - LocalizationState::Unused => Some(&mut self.unused), - LocalizationState::Outdated => Some(&mut self.outdated), - LocalizationState::Unknown => Some(&mut self.unknown), - _ => None, - } - } - - fn show( - &mut self, - state: LocalizationState, - be_verbose: bool, - ref_i18n_map: &HashMap, - ) { - let entries = self - .get_mut(state) - .unwrap_or_else(|| panic!("called on invalid state: {:?}", state)); - if entries.is_empty() { - return; - } - println!("\n\t[{:?}]", state); - entries.sort(); - for (key, commit_id) in entries { - if be_verbose { - let our_commit = commit_id - .map(|s| format!("{}", s)) - .unwrap_or_else(|| "None".to_owned()); - let ref_commit = ref_i18n_map - .get(key) - .and_then(|s| s.commit_id) - .map(|s| format!("{}", s)) - .unwrap_or_else(|| "None".to_owned()); - println!("{:60}| {:40} | {:40}", key, our_commit, ref_commit,); - } else { - println!("{}", key); - } - } - } -} - -#[derive(Copy, Clone, Debug)] -struct LocalizationEntryState { - key_line: Option, - chuck_line_range: Option<(usize, usize)>, - commit_id: Option, - state: LocalizationState, -} - -impl LocalizationEntryState { - fn new() -> LocalizationEntryState { - LocalizationEntryState { - key_line: None, - chuck_line_range: None, - commit_id: None, - state: LocalizationState::Unknown, - } - } -} - -/// Returns the Git blob associated with the given reference and path -fn read_file_from_path<'a>( - repo: &'a git2::Repository, - reference: &git2::Reference, - path: &std::path::Path, -) -> git2::Blob<'a> { - let tree = reference - .peel_to_tree() - .expect("Impossible to peel HEAD to a tree object"); - tree.get_path(path) - .unwrap_or_else(|_| { - panic!( - "Impossible to find the file {:?} in reference {:?}", - path, - reference.name() - ) - }) - .to_object(repo) - .unwrap() - .peel_to_blob() - .expect("Impossible to fetch the Git object") -} - -fn correspond(line: &str, key: &str) -> bool { - let pat = { - // Get left part of split - let mut begin = line - .split(':') - .next() - .expect("split always produces value") - .trim() - .chars(); - // Remove quotes - begin.next(); - begin.next_back(); - begin.as_str() - }; - - pat == key -} - -fn generate_key_version<'a>( - repo: &'a git2::Repository, - localization: &LocalizationFragment, - path: &std::path::Path, - file_blob: &git2::Blob, -) -> HashMap { - let mut keys: HashMap = localization - .string_map - .keys() - .map(|k| (k.to_owned(), LocalizationEntryState::new())) - .collect(); - // Find key start lines - let file_content = std::str::from_utf8(file_blob.content()).expect("Got non UTF-8 file"); - let mut to_process: HashSet<&String> = localization.string_map.keys().collect(); - for (line_nb, line) in file_content.lines().enumerate() { - let mut found_key = None; - - for key in to_process.iter() { - if correspond(line, key) { - found_key = Some(key.to_owned()); - } - } - - if let Some(key) = found_key { - keys.get_mut(key).unwrap().key_line = Some(line_nb); - to_process.remove(key); - }; - } - - let mut error_check_set: Vec = vec![]; - // Find commit for each keys - repo.blame_file(path, None) - .expect("Impossible to generate the Git blame") - .iter() - .for_each(|e: git2::BlameHunk| { - for (key, state) in keys.iter_mut() { - let line = match state.key_line { - Some(l) => l, - None => { - if !error_check_set.contains(key) { - eprintln!( - "Key {} does not have a git line in it's state! Skipping key.", - key - ); - error_check_set.push(key.clone()); - } - continue; - }, - }; - - if line + 1 >= e.final_start_line() - && line + 1 < e.final_start_line() + e.lines_in_hunk() - { - state.chuck_line_range = Some(( - e.final_start_line(), - e.final_start_line() + e.lines_in_hunk(), - )); - state.commit_id = match state.commit_id { - Some(existing_commit) => { - match repo.graph_descendant_of(e.final_commit_id(), existing_commit) { - Ok(true) => Some(e.final_commit_id()), - Ok(false) => Some(existing_commit), - Err(err) => panic!("{}", err), - } - }, - None => Some(e.final_commit_id()), - }; - } - } - }); - - keys -} - -fn complete_key_versions<'a>( +/// Fill the entry State base information (except `state`) for a complete +/// language +fn gather_entry_state<'a>( repo: &'a git2::Repository, head_ref: &git2::Reference, - i18n_key_versions: &mut HashMap, - root_dir: &Path, - lang_dir: &Path, -) { - //TODO: review unwraps in this file + path: &LangPath, +) -> RawLanguage { + println!("-> {:?}", path.language_identifier()); + // load standard manifest + let manifest = raw::load_manifest(path).expect("failed to load language manifest"); + // transform language into LocalizationEntryState + let mut fragments = HashMap::new(); // For each file in directory - for i18n_file in root_dir.join(&lang_dir).read_dir().unwrap().flatten() { - if let Ok(file_type) = i18n_file.file_type() { - if file_type.is_file() { - println!("-> {:?}", i18n_file.file_name()); + let files = path + .fragments() + .expect("failed to get all files in language"); + for sub_path in files { + let fullpath = path.sub_path(&sub_path); + let gitpath = fullpath.strip_prefix(path.base().root_path()).unwrap(); + println!(" -> {:?}", &sub_path); + let i18n_blob = read_file_from_path(repo, head_ref, gitpath); + let fragment: RawFragment = from_bytes(i18n_blob.content()) + .unwrap_or_else(|e| panic!("Could not parse {:?} RON file, error: {}", sub_path, e)); + let frag = transform_fragment(repo, (gitpath, fragment), &i18n_blob); + fragments.insert(sub_path, frag); + } - 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 = - from_bytes(i18n_blob.content()).unwrap_or_else(|e| { - panic!( - "Could not parse {} RON file, skipping: {}", - i18n_file.path().to_string_lossy(), - e - ) - }); - i18n_key_versions.extend(generate_key_version(repo, &i18n, path, &i18n_blob)); - } else if file_type.is_dir() { - // If it's a directory, recursively check it - complete_key_versions( - repo, - head_ref, - i18n_key_versions, - root_dir, - &i18n_file.path(), + RawLanguage:: { + manifest, + fragments, + } +} + +/// fills in the `state` +fn compare_lang_with_reference( + current_i18n: &mut RawLanguage, + i18n_references: &RawLanguage, + repo: &git2::Repository, +) { + // git graph decendent of is slow, so we cache it + let mut graph_decendent_of_cache = HashMap::new(); + + let mut cached_graph_descendant_of = |commit, ancestor| -> bool { + let key = (commit, ancestor); + match graph_decendent_of_cache.entry(key) { + Entry::Occupied(entry) => { + return *entry.get(); + }, + Entry::Vacant(entry) => { + let value = repo.graph_descendant_of(commit, ancestor).unwrap_or(false); + *entry.insert(value) + }, + } + }; + + const MISSING: LocalizationEntryState = LocalizationEntryState { + key_line: None, + chuck_line_range: None, + commit_id: None, + state: Some(LocalizationState::NotFound), + }; + + // match files + for (ref_path, ref_fragment) in i18n_references.fragments.iter() { + let cur_fragment = match current_i18n.fragments.get_mut(ref_path) { + Some(c) => c, + None => { + eprintln!( + "language {} is missing file: {:?}", + current_i18n.manifest.metadata.language_identifier, ref_path ); + // add all keys as missing + let mut string_map = HashMap::new(); + for (ref_key, _) in ref_fragment.string_map.iter() { + string_map.insert(ref_key.to_owned(), MISSING.clone()); + } + current_i18n + .fragments + .insert(ref_path.to_owned(), RawFragment { + string_map, + vector_map: HashMap::new(), + }); + continue; + }, + }; + + for (ref_key, ref_state) in ref_fragment.string_map.iter() { + match cur_fragment.string_map.get_mut(ref_key) { + Some(state) => { + let commit_id = match state.commit_id { + Some(c) => c, + None => { + eprintln!( + "Commit ID of key {} in i18n file {} is missing! Skipping key.", + ref_key, + ref_path.to_string_lossy() + ); + continue; + }, + }; + let ref_commit_id = match ref_state.commit_id { + Some(c) => c, + None => { + eprintln!( + "Commit ID of key {} in reference i18n file is missing! Skipping \ + key.", + ref_key + ); + continue; + }, + }; + if commit_id != ref_commit_id + && !cached_graph_descendant_of(commit_id, ref_commit_id) + { + state.state = Some(LocalizationState::Outdated); + } else { + state.state = Some(LocalizationState::UpToDate); + } + }, + None => { + cur_fragment + .string_map + .insert(ref_key.to_owned(), MISSING.clone()); + }, } } - } -} -fn gather_state( - loc: &RawLocalization, - i18n_blob: &git2::Blob, - ref_manifest: &Path, - root_dir: &Path, - lang_dir: &Path, - repo: &git2::Repository, - head_ref: &git2::Reference, -) -> HashMap { - // Generate map - let mut i18n_map = generate_key_version( - repo, - &LocalizationFragment::from(loc.clone()), - ref_manifest, - i18n_blob, - ); - - // Gathering info about keys from language - complete_key_versions(repo, head_ref, &mut i18n_map, root_dir, lang_dir); - - i18n_map -} - -// Helper function to test localization directory -// `lang_dir` - path to localization directory. Relative from root of the -// repo. -// `root_dir` - absolute path to repo -// `ref_manifest` - path to reference manifest -// `i18n_references` - keys from reference language -// `repo` - git object for main repo -// `head_ref` - HEAD -fn test_localization_directory( - lang_dir: &Path, - root_dir: &Path, - ref_manifest: &Path, - i18n_references: &HashMap, - be_verbose: bool, - repo: &git2::Repository, - head_ref: &git2::Reference, -) -> Option { - let relfile = lang_dir.join(&(LANG_MANIFEST_FILE.to_string() + ".ron")); - if relfile == ref_manifest { - return None; - } - println!("\n-----------------------------------"); - println!("{:?}", relfile); - println!("-----------------------------------"); - - // Find the localization entry state - let current_blob = read_file_from_path(repo, head_ref, &relfile); - 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( - ¤t_loc, - ¤t_blob, - ref_manifest, - root_dir, - lang_dir, - repo, - head_ref, - ); - - // Comparing with reference localization - fill_info(&mut current_i18n, i18n_references, repo, &relfile); - - let mut state_map = LocalizationAnalysis::default(); - let result = gather_results(current_i18n, &mut state_map); - print_translation_stats( - i18n_references, - &result, - &mut state_map, - be_verbose, - relfile, - ref_manifest, - ); - Some(result) -} - -fn fill_info( - current_i18n: &mut HashMap, - i18n_references: &HashMap, - repo: &git2::Repository, - relfile: &Path, -) { - for (ref_key, ref_state) in i18n_references.iter() { - match current_i18n.get_mut(ref_key) { - Some(state) => { - let commit_id = match state.commit_id { - Some(c) => c, - None => { - eprintln!( - "Commit ID of key {} in i18n file {} is missing! Skipping key.", - ref_key, - relfile.to_string_lossy() - ); - continue; - }, - }; - let ref_commit_id = match ref_state.commit_id { - Some(c) => c, - None => { - eprintln!( - "Commit ID of key {} in reference i18n file is missing! Skipping key.", - ref_key - ); - continue; - }, - }; - if commit_id != ref_commit_id - && !repo - .graph_descendant_of(commit_id, ref_commit_id) - .unwrap_or(false) - { - state.state = LocalizationState::Outdated; - } else { - state.state = LocalizationState::UpToDate; - } - }, - None => { - current_i18n.insert(ref_key.to_owned(), LocalizationEntryState { - key_line: None, - chuck_line_range: None, - commit_id: None, - state: LocalizationState::NotFound, - }); - }, + for (_, state) in cur_fragment + .string_map + .iter_mut() + .filter(|&(k, _)| ref_fragment.string_map.get(k).is_none()) + { + state.state = Some(LocalizationState::Unused); } } - - let ref_keys: HashSet<&String> = i18n_references.keys().collect(); - for (_, state) in current_i18n - .iter_mut() - .filter(|&(k, _)| !ref_keys.contains(k)) - { - state.state = LocalizationState::Unused; - } } fn gather_results( - current_i18n: HashMap, - state_map: &mut LocalizationAnalysis, -) -> LocalizationStats { - let mut uptodate_entries = 0; - let mut outdated_entries = 0; - let mut unused_entries = 0; - let mut notfound_entries = 0; - let mut unknown_entries = 0; + current_i18n: &RawLanguage, +) -> (LocalizationAnalysis, LocalizationStats) { + let mut state_map = + LocalizationAnalysis::new(¤t_i18n.manifest.metadata.language_identifier); + let mut stats = LocalizationStats::default(); - let keys: Vec<&String> = current_i18n.keys().collect(); - for key in keys { - let entry = current_i18n.get(key).unwrap(); - match entry.state { - LocalizationState::Outdated => outdated_entries += 1, - LocalizationState::NotFound => notfound_entries += 1, - LocalizationState::Unknown => unknown_entries += 1, - LocalizationState::Unused => unused_entries += 1, - LocalizationState::UpToDate => uptodate_entries += 1, - }; - if entry.state != LocalizationState::UpToDate { - let state_keys = state_map - .get_mut(entry.state) - .expect("vectors must be added"); - state_keys.push((key.to_owned(), entry.commit_id)); + for (file, fragments) in ¤t_i18n.fragments { + for (key, entry) in &fragments.string_map { + match entry.state { + Some(LocalizationState::Outdated) => stats.outdated_entries += 1, + Some(LocalizationState::NotFound) => stats.notfound_entries += 1, + None => stats.errors += 1, + Some(LocalizationState::Unused) => stats.unused_entries += 1, + Some(LocalizationState::UpToDate) => stats.uptodate_entries += 1, + }; + let state_keys = state_map.data.get_mut(&entry.state).expect("prefiled"); + state_keys.push((file.clone(), key.to_owned(), entry.commit_id)); } } - // Calculate key count that actually matter for the status of the translation - // Unused entries don't break the game - let current_i18n_entry_count = current_i18n.len(); - let real_entry_count = current_i18n_entry_count - unused_entries; - - LocalizationStats { - uptodate_entries, - unused_entries, - outdated_entries, - notfound_entries, - errors: unknown_entries, - real_entry_count, - } -} - -fn print_translation_stats( - ref_i18n_map: &HashMap, - stats: &LocalizationStats, - state_map: &mut LocalizationAnalysis, - be_verbose: bool, - relfile: PathBuf, - ref_manifest: &Path, -) { - let uptodate_percent = - (stats.uptodate_entries as f32 / stats.real_entry_count as f32) * 100_f32; - let outdated_percent = - (stats.outdated_entries as f32 / stats.real_entry_count as f32) * 100_f32; - let untranslated_percent = - ((stats.errors + stats.errors) as f32 / stats.real_entry_count as f32) * 100_f32; - - // Display - if be_verbose { - println!( - "\n{:60}| {:40} | {:40}", - "Key name", - relfile.to_str().unwrap(), - ref_manifest.to_str().unwrap(), - ); - } else { - println!("\nKey name"); + for (_, entries) in state_map.data.iter_mut() { + entries.sort(); } - state_map.show(LocalizationState::NotFound, be_verbose, ref_i18n_map); - state_map.show(LocalizationState::Unused, be_verbose, ref_i18n_map); - state_map.show(LocalizationState::Outdated, be_verbose, ref_i18n_map); - state_map.show(LocalizationState::Unknown, be_verbose, ref_i18n_map); - - println!( - "\n{} up-to-date, {} outdated, {} unused, {} not found, {} unknown entries", - stats.uptodate_entries, - stats.outdated_entries, - stats.unused_entries, - stats.notfound_entries, - stats.errors, - ); - - println!( - "{:.2}% up-to-date, {:.2}% outdated, {:.2}% untranslated\n", - uptodate_percent, outdated_percent, untranslated_percent, - ); + (state_map, stats) } /// Test one language -/// `code` - name of the directory in assets (de_DE for example) -/// `root_dir` - absolute path to main repo -/// `assets_path` - relative path to asset directory (right now it is -/// 'assets/voxygen/i18n') -pub fn test_specific_localization( - code: &str, - root_dir: &Path, - assets_path: &Path, +/// - `code`: name of the directory in assets (de_DE for example) +/// - `path`: path to repo +/// - `be_verbose`: print extra info +/// - `csv_enabled`: generate csv files in target folder +pub fn test_specific_localizations( + path: &BasePath, + language_identifiers: &[&str], be_verbose: bool, + csv_enabled: bool, ) { - // Relative paths from root of repo to assets - let ref_lang_dir = assets_path.join(REFERENCE_LANG); - let ref_manifest = ref_lang_dir.join(LANG_MANIFEST_FILE.to_string() + ".ron"); - + //complete analysis + let mut analysis = HashMap::new(); // Initialize Git objects - let repo = git2::Repository::discover(&root_dir) - .unwrap_or_else(|_| panic!("Failed to open the Git repository at {:?}", &root_dir)); + let repo = git2::Repository::discover(path.root_path()) + .unwrap_or_else(|_| panic!("Failed to open the Git repository {:?}", path.root_path())); let head_ref = repo.head().expect("Impossible to get the HEAD reference"); - // Read HEAD for the reference language manifest - let ref_manifest_blob = read_file_from_path(&repo, &head_ref, &ref_manifest); - let loc: RawLocalization = from_bytes(ref_manifest_blob.content()) - .expect("Expect to parse reference i18n RON file, can't proceed without it"); + // Read Reference Language + let ref_language = gather_entry_state(&repo, &head_ref, &path.i18n_path(REFERENCE_LANG)); + for &language_identifier in language_identifiers { + let mut cur_language = + gather_entry_state(&repo, &head_ref, &path.i18n_path(language_identifier)); + compare_lang_with_reference(&mut cur_language, &ref_language, &repo); + let (state_map, stats) = gather_results(&cur_language); + analysis.insert(language_identifier.to_owned(), (state_map, stats)); + } - // Gathering info about keys from reference language - let reference_i18n = gather_state( - &loc, - &ref_manifest_blob, - &ref_manifest, - root_dir, - &ref_lang_dir, - &repo, - &head_ref, - ); + let output = path.root_path().join("translation_analysis.csv"); + let mut f = std::fs::File::create(output).expect("couldn't write csv file"); - // Testing how specific language is localized - let dir = assets_path.join(code); - test_localization_directory( - &dir, - root_dir, - &ref_manifest, - &reference_i18n, - be_verbose, - &repo, - &head_ref, - ); + use std::io::Write; + writeln!( + f, + "country_code,file_name,translation_key,status,git_commit" + ) + .unwrap(); + //printing + for (language_identifier, (state_map, stats)) in &analysis { + if csv_enabled { + print_csv_stats(state_map, &mut f); + } else { + print_translation_stats( + language_identifier, + &ref_language, + stats, + state_map, + be_verbose, + ); + } + } + if analysis.len() > 1 { + print_overall_stats(analysis); + } } /// Test all localizations -/// `root_dir` - absolute path to main repo -/// `assets_path` - relative path to asset directory (right now it is -/// 'assets/voxygen/i18n') -pub fn test_all_localizations(root_dir: &Path, assets_path: &Path, be_verbose: bool) { - let ref_lang_dir = assets_path.join(REFERENCE_LANG); - let ref_manifest = ref_lang_dir.join(LANG_MANIFEST_FILE.to_string() + ".ron"); - - if !root_dir.join(&ref_lang_dir).is_dir() { - panic!("Reference language folder not found {:?}", &ref_lang_dir) - } - if !root_dir.join(&ref_manifest).is_file() { - panic!("Reference language file not found {:?}", &ref_manifest) - } - - // Initialize Git objects - let repo = git2::Repository::discover(&root_dir) - .unwrap_or_else(|_| panic!("Failed to open the Git repository at {:?}", &root_dir)); - let head_ref = repo.head().expect("Impossible to get the HEAD reference"); - - // Read HEAD for the reference language file - let ref_manifest_blob = read_file_from_path(&repo, &head_ref, &ref_manifest); - let loc: RawLocalization = from_bytes(ref_manifest_blob.content()) - .expect("Expect to parse reference i18n RON file, can't proceed without it"); - - // Gathering info about keys from reference language - let reference_i18n = gather_state( - &loc, - &ref_manifest_blob, - &ref_manifest, - root_dir, - &ref_lang_dir, - &repo, - &head_ref, - ); - +pub fn test_all_localizations(path: &BasePath, be_verbose: bool, csv_enabled: bool) { // Compare to other reference files - let i18n_directories = i18n_directories(&root_dir.join(assets_path)); - let mut i18n_entry_counts: HashMap = HashMap::new(); - for dir in &i18n_directories { - let rel_dir = dir.strip_prefix(root_dir).unwrap(); - let result = test_localization_directory( - rel_dir, - root_dir, - &ref_manifest, - &reference_i18n, - be_verbose, - &repo, - &head_ref, - ); - if let Some(values) = result { - i18n_entry_counts.insert(dir.clone(), values); - } - } - - print_overall_stats(i18n_entry_counts); -} - -fn print_overall_stats(i18n_entry_counts: HashMap) { - let mut overall_uptodate_entry_count = 0; - let mut overall_outdated_entry_count = 0; - let mut overall_untranslated_entry_count = 0; - let mut overall_real_entry_count = 0; - - println!("-----------------------------------------------------------------------------"); - println!("Overall Translation Status"); - println!("-----------------------------------------------------------------------------"); - println!( - "{:12}| {:8} | {:8} | {:8} | {:8} | {:8}", - "", "up-to-date", "outdated", "untranslated", "unused", "errors", - ); - - let mut i18n_stats: Vec<(&PathBuf, &LocalizationStats)> = i18n_entry_counts.iter().collect(); - i18n_stats.sort_by_key(|(_, result)| result.notfound_entries); - - for (path, test_result) in i18n_stats { - let LocalizationStats { - uptodate_entries: uptodate, - outdated_entries: outdated, - unused_entries: unused, - notfound_entries: untranslated, - errors, - real_entry_count: real, - } = test_result; - overall_uptodate_entry_count += uptodate; - overall_outdated_entry_count += outdated; - overall_untranslated_entry_count += untranslated; - overall_real_entry_count += real; - - println!( - "{:12}|{:8} |{:6} |{:8} |{:6} |{:8}", - path.file_name().unwrap().to_string_lossy(), - uptodate, - outdated, - untranslated, - unused, - errors, - ); - } - - println!( - "\n{:.2}% up-to-date, {:.2}% outdated, {:.2}% untranslated", - (overall_uptodate_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, - (overall_outdated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, - (overall_untranslated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, - ); - println!("-----------------------------------------------------------------------------\n"); + let languages = path.i18n_directories(); + let language_identifiers = languages + .iter() + .map(|s| s.language_identifier()) + .collect::>(); + test_specific_localizations(path, &language_identifiers, be_verbose, csv_enabled); } diff --git a/voxygen/i18n/src/bin/i18n-check.rs b/voxygen/i18n/src/bin/i18n-check.rs index cc45d256bb..6a868a950f 100644 --- a/voxygen/i18n/src/bin/i18n-check.rs +++ b/voxygen/i18n/src/bin/i18n-check.rs @@ -1,6 +1,5 @@ use clap::{App, Arg}; -use std::path::Path; -use veloren_i18n::{analysis, verification}; +use veloren_voxygen_i18n::{analysis, verification, BasePath}; fn main() { let matches = App::new("i18n-check") @@ -28,24 +27,26 @@ fn main() { .long("verbose") .help("print additional information"), ) + .arg( + Arg::with_name("csv") + .long("csv") + .help("generate csv files per language in target folder"), + ) .get_matches(); // Generate paths - let root = common_assets::find_root().expect("Failed to find root of repository"); - let asset_path = Path::new("assets/voxygen/i18n/"); + let root_path = common_assets::find_root().expect("Failed to find root of repository"); + let path = BasePath::new(&root_path); + let be_verbose = matches.is_present("verbose"); + let csv_enabled = matches.is_present("csv"); if let Some(code) = matches.value_of("CODE") { - analysis::test_specific_localization( - code, - &root, - &asset_path, - matches.is_present("verbose"), - ); + analysis::test_specific_localizations(&path, &[code], be_verbose, csv_enabled); } if matches.is_present("test") { - analysis::test_all_localizations(&root, &asset_path, matches.is_present("verbose")); + analysis::test_all_localizations(&path, be_verbose, csv_enabled); } if matches.is_present("verify") { - verification::verify_all_localizations(&root, &asset_path); + verification::verify_all_localizations(&path); } } diff --git a/voxygen/i18n/src/data.rs b/voxygen/i18n/src/data.rs deleted file mode 100644 index 2240134fe2..0000000000 --- a/voxygen/i18n/src/data.rs +++ /dev/null @@ -1,437 +0,0 @@ -use crate::assets::{self, source::DirEntry, AssetExt, AssetGuard, AssetHandle}; -use deunicode::deunicode; -use hashbrown::{HashMap, HashSet}; -use serde::{Deserialize, Serialize}; -use std::{ - fs, io, - path::{Path, PathBuf}, -}; -use tracing::warn; - -/// The reference language, aka the more up-to-date localization data. -/// Also the default language at first startup. -pub const REFERENCE_LANG: &str = "en"; - -pub const LANG_MANIFEST_FILE: &str = "_manifest"; - -/// How a language can be described -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct LanguageMetadata { - /// A human friendly language name (e.g. "English (US)") - pub language_name: String, - - /// A short text identifier for this language (e.g. "en_US") - /// - /// On the opposite of `language_name` that can change freely, - /// `language_identifier` value shall be stable in time as it - /// is used by setting components to store the language - /// selected by the user. - pub language_identifier: String, -} - -/// Store font metadata -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Font { - /// Key to retrieve the font in the asset system - pub asset_key: String, - - /// Scale ratio to resize the UI text dynamicly - scale_ratio: f32, -} - -impl Font { - /// Scale input size to final UI size - pub fn scale(&self, value: u32) -> u32 { (value as f32 * self.scale_ratio).round() as u32 } -} - -/// Store font metadata -pub type Fonts = HashMap; - -/// Raw localization data, expect the strings to not be loaded here -/// However, metadata informations are correct -/// See `Language` for more info on each attributes -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -pub(crate) struct RawLocalization { - pub(crate) convert_utf8_to_ascii: bool, - pub(crate) fonts: Fonts, - pub(crate) metadata: LanguageMetadata, - pub(crate) string_map: HashMap, - pub(crate) vector_map: HashMap>, -} - -/// Store internationalization data -#[derive(Debug, PartialEq, Serialize, Deserialize)] -struct Language { - /// A map storing the localized texts - /// - /// Localized content can be accessed using a String key. - pub(crate) string_map: HashMap, - - /// A map for storing variations of localized texts, for example multiple - /// ways of saying "Help, I'm under attack". Used primarily for npc - /// dialogue. - pub(crate) vector_map: HashMap>, - - /// Whether to convert the input text encoded in UTF-8 - /// into a ASCII version by using the `deunicode` crate. - pub(crate) convert_utf8_to_ascii: bool, - - /// Font configuration is stored here - pub(crate) fonts: Fonts, - - pub(crate) metadata: LanguageMetadata, -} - -/// Store internationalization maps -/// These structs are meant to be merged into a Language -#[derive(Debug, PartialEq, Serialize, Deserialize)] -pub(crate) struct LocalizationFragment { - /// A map storing the localized texts - /// - /// Localized content can be accessed using a String key. - pub(crate) string_map: HashMap, - - /// A map for storing variations of localized texts, for example multiple - /// ways of saying "Help, I'm under attack". Used primarily for npc - /// dialogue. - pub(crate) vector_map: HashMap>, -} - -impl Language { - /// Get a localized text from the given key - pub fn get<'a>(&'a self, key: &'a str) -> Option<&str> { - self.string_map.get(key).map(String::as_str) - } - - /// Get a variation of localized text from the given key - /// - /// `index` should be a random number from `0` to `u16::max()` - /// - /// If the key is not present in the localization object - /// then the key is returned. - pub fn get_variation<'a>(&'a self, key: &'a str, index: u16) -> Option<&str> { - self.vector_map.get(key).and_then(|v| { - if v.is_empty() { - None - } else { - Some(v[index as usize % v.len()].as_str()) - } - }) - } -} - -impl Default for Language { - fn default() -> Self { - Self { - string_map: HashMap::default(), - vector_map: HashMap::default(), - ..Default::default() - } - } -} - -impl From for Language { - fn from(raw: RawLocalization) -> Self { - Self { - string_map: raw.string_map, - vector_map: raw.vector_map, - convert_utf8_to_ascii: raw.convert_utf8_to_ascii, - fonts: raw.fonts, - metadata: raw.metadata, - } - } -} -impl From for LocalizationFragment { - fn from(raw: RawLocalization) -> Self { - Self { - string_map: raw.string_map, - vector_map: raw.vector_map, - } - } -} - -impl assets::Asset for RawLocalization { - type Loader = assets::RonLoader; - - const EXTENSION: &'static str = "ron"; -} -impl assets::Asset for LocalizationFragment { - type Loader = assets::RonLoader; - - const EXTENSION: &'static str = "ron"; -} - -impl assets::Compound for Language { - fn load( - cache: &assets::AssetCache, - asset_key: &str, - ) -> Result { - let raw = cache - .load::(&[asset_key, ".", LANG_MANIFEST_FILE].concat())? - .cloned(); - let mut localization = Language::from(raw); - - // Walk through files in the folder, collecting localization fragment to merge - // inside the asked_localization - for localization_asset in cache - .load_dir::(asset_key, true)? - .iter() - { - localization - .string_map - .extend(localization_asset.read().string_map.clone()); - localization - .vector_map - .extend(localization_asset.read().vector_map.clone()); - } - - // Update the text if UTF-8 to ASCII conversion is enabled - if localization.convert_utf8_to_ascii { - for value in localization.string_map.values_mut() { - *value = deunicode(value); - } - - for value in localization.vector_map.values_mut() { - *value = value.iter().map(|s| deunicode(s)).collect(); - } - } - localization.metadata.language_name = deunicode(&localization.metadata.language_name); - - Ok(localization) - } -} - -/// the central data structure to handle localization in veloren -// inherit Copy+Clone from AssetHandle -#[derive(Debug, PartialEq, Copy, Clone)] -pub struct LocalizationHandle { - active: AssetHandle, - fallback: Option>, - pub use_english_fallback: bool, -} - -// RAII guard returned from Localization::read(), resembles AssetGuard -pub struct LocalizationGuard { - active: AssetGuard, - fallback: Option>, -} - -// arbitrary choice to minimize changing all of veloren -pub type Localization = LocalizationGuard; - -impl LocalizationGuard { - /// Get a localized text from the given key - /// - /// First lookup is done in the active language, second in - /// the fallback (if present). - /// If the key is not present in the localization object - /// then the key is returned. - pub fn get<'a>(&'a self, key: &'a str) -> &str { - self.active.get(key).unwrap_or_else(|| { - self.fallback - .as_ref() - .and_then(|f| f.get(key)) - .unwrap_or(key) - }) - } - - /// Get a variation of localized text from the given key - /// - /// `index` should be a random number from `0` to `u16::max()` - /// - /// If the key is not present in the localization object - /// then the key is returned. - pub fn get_variation<'a>(&'a self, key: &'a str, index: u16) -> &str { - self.active.get_variation(key, index).unwrap_or_else(|| { - self.fallback - .as_ref() - .and_then(|f| f.get_variation(key, index)) - .unwrap_or(key) - }) - } - - /// Return the missing keys compared to the reference language - fn list_missing_entries(&self) -> (HashSet, HashSet) { - if let Some(ref_lang) = &self.fallback { - let reference_string_keys: HashSet<_> = ref_lang.string_map.keys().cloned().collect(); - let string_keys: HashSet<_> = self.active.string_map.keys().cloned().collect(); - let strings = reference_string_keys - .difference(&string_keys) - .cloned() - .collect(); - - let reference_vector_keys: HashSet<_> = ref_lang.vector_map.keys().cloned().collect(); - let vector_keys: HashSet<_> = self.active.vector_map.keys().cloned().collect(); - let vectors = reference_vector_keys - .difference(&vector_keys) - .cloned() - .collect(); - - (strings, vectors) - } else { - (HashSet::default(), HashSet::default()) - } - } - - /// Log missing entries (compared to the reference language) as warnings - pub fn log_missing_entries(&self) { - let (missing_strings, missing_vectors) = self.list_missing_entries(); - for missing_key in missing_strings { - warn!( - "[{:?}] Missing string key {:?}", - self.metadata().language_identifier, - missing_key - ); - } - for missing_key in missing_vectors { - warn!( - "[{:?}] Missing vector key {:?}", - self.metadata().language_identifier, - missing_key - ); - } - } - - pub fn fonts(&self) -> &Fonts { &self.active.fonts } - - pub fn metadata(&self) -> &LanguageMetadata { &self.active.metadata } -} - -impl LocalizationHandle { - pub fn set_english_fallback(&mut self, use_english_fallback: bool) { - self.use_english_fallback = use_english_fallback; - } - - pub fn read(&self) -> LocalizationGuard { - LocalizationGuard { - active: self.active.read(), - fallback: if self.use_english_fallback { - self.fallback.map(|f| f.read()) - } else { - None - }, - } - } - - pub fn load(specifier: &str) -> Result { - let default_key = ["voxygen.i18n.", REFERENCE_LANG].concat(); - let language_key = ["voxygen.i18n.", specifier].concat(); - let is_default = language_key == default_key; - Ok(Self { - active: Language::load(&language_key)?, - fallback: if is_default { - None - } else { - Language::load(&default_key).ok() - }, - use_english_fallback: false, - }) - } - - pub fn load_expect(specifier: &str) -> Self { - Self::load(specifier).expect("Can't load language files") - } - - pub fn reloaded(&mut self) -> bool { self.active.reloaded() } -} - -struct FindManifests; - -impl assets::Compound for FindManifests { - fn load(_: &assets::AssetCache, _: &str) -> Result { - Ok(Self) - } -} - -impl assets::DirLoadable for FindManifests { - fn select_ids( - source: &S, - specifier: &str, - ) -> io::Result> { - let mut specifiers = Vec::new(); - - source.read_dir(specifier, &mut |entry| { - if let DirEntry::Directory(spec) = entry { - let manifest_spec = [spec, ".", LANG_MANIFEST_FILE].concat(); - if source.exists(DirEntry::File(&manifest_spec, "ron")) { - specifiers.push(manifest_spec.into()); - } - } - })?; - - Ok(specifiers) - } -} - -#[derive(Clone, Debug)] -struct LocalizationList(Vec); - -impl assets::Compound for LocalizationList { - fn load( - cache: &assets::AssetCache, - specifier: &str, - ) -> Result { - // List language directories - let languages = assets::load_dir::(specifier, false) - .unwrap_or_else(|e| panic!("Failed to get manifests from {}: {:?}", specifier, e)) - .ids() - .filter_map(|spec| cache.load::(spec).ok()) - .map(|localization| localization.read().metadata.clone()) - .collect(); - - Ok(LocalizationList(languages)) - } -} - -/// Load all the available languages located in the voxygen asset directory -pub fn list_localizations() -> Vec { - LocalizationList::load_expect_cloned("voxygen.i18n").0 -} - -/// List localization directories as a `PathBuf` vector -pub fn i18n_directories(i18n_dir: &Path) -> Vec { - fs::read_dir(i18n_dir) - .unwrap() - .map(|res| res.map(|e| e.path()).unwrap()) - .filter(|e| e.is_dir()) - .collect() -} - -#[cfg(test)] -mod tests { - use super::assets; - // Test that localization list is loaded (not empty) - #[test] - fn test_localization_list() { - let list = super::list_localizations(); - assert!(!list.is_empty()); - } - - // Test that reference language can be loaded - #[test] - fn test_localization_handle() { - let _ = super::LocalizationHandle::load_expect(super::REFERENCE_LANG); - } - - // Test to verify all languages that they are VALID and loadable, without - // need of git just on the local assets folder - #[test] - fn verify_all_localizations() { - // Generate paths - let i18n_asset_path = std::path::Path::new("assets/voxygen/i18n/"); - let root_dir = assets::find_root().expect("Failed to discover repository root"); - crate::verification::verify_all_localizations(&root_dir, i18n_asset_path); - } - - // Test to verify all languages and print missing and faulty localisation - #[test] - #[ignore] - fn test_all_localizations() { - // Options - let be_verbose = true; - // Generate paths - let i18n_asset_path = std::path::Path::new("assets/voxygen/i18n/"); - let root_dir = assets::find_root().expect("Failed to discover repository root"); - crate::analysis::test_all_localizations(&root_dir, i18n_asset_path, be_verbose); - } -} diff --git a/voxygen/i18n/src/gitfragments.rs b/voxygen/i18n/src/gitfragments.rs new file mode 100644 index 0000000000..ec77568837 --- /dev/null +++ b/voxygen/i18n/src/gitfragments.rs @@ -0,0 +1,157 @@ +//! fragment attached with git versioning information +use crate::raw::RawFragment; +use hashbrown::HashMap; +use std::path::Path; + +#[derive(Copy, Clone, Eq, Hash, Debug, PartialEq)] +pub(crate) enum LocalizationState { + UpToDate, + NotFound, + Outdated, + Unused, +} + +pub(crate) const ALL_LOCALIZATION_STATES: [Option; 5] = [ + Some(LocalizationState::UpToDate), + Some(LocalizationState::NotFound), + Some(LocalizationState::Outdated), + Some(LocalizationState::Unused), + None, +]; + +#[derive(Clone, Debug)] +pub(crate) struct LocalizationEntryState { + pub(crate) key_line: Option, + pub(crate) chuck_line_range: Option<(usize, usize)>, + pub(crate) commit_id: Option, + pub(crate) state: Option, +} + +impl LocalizationState { + pub(crate) fn print(this: &Option) -> String { + match this { + Some(LocalizationState::UpToDate) => "UpToDate", + Some(LocalizationState::NotFound) => "NotFound", + Some(LocalizationState::Outdated) => "Outdated", + Some(LocalizationState::Unused) => "Unused", + None => "Unknown", + } + .to_owned() + } +} + +impl LocalizationEntryState { + fn new(key_line: Option) -> LocalizationEntryState { + LocalizationEntryState { + key_line, + chuck_line_range: None, + commit_id: None, + state: None, + } + } +} + +/// Returns the Git blob associated with the given reference and path +pub(crate) fn read_file_from_path<'a>( + repo: &'a git2::Repository, + reference: &git2::Reference, + path: &std::path::Path, +) -> git2::Blob<'a> { + let tree = reference + .peel_to_tree() + .expect("Impossible to peel HEAD to a tree object"); + tree.get_path(path) + .unwrap_or_else(|_| { + panic!( + "Impossible to find the file {:?} in reference {:?}", + path, + reference.name() + ) + }) + .to_object(repo) + .unwrap() + .peel_to_blob() + .expect("Impossible to fetch the Git object") +} + +/// Extend a Fragment with historical git data +/// The actual translation gets dropped +/// TODO: transform vector_map too +pub(crate) fn transform_fragment<'a>( + repo: &'a git2::Repository, + fragment: (&Path, RawFragment), + file_blob: &git2::Blob, +) -> RawFragment { + let (path, fragment) = fragment; + // Find key start lines by searching all lines which have `:` in them (as they + // are probably keys) and getting the first part of such line trimming + // whitespace and quotes. Quite buggy heuristic + let file_content = std::str::from_utf8(file_blob.content()).expect("Got non UTF-8 file"); + // we only need the key part of the file to process + let file_content_keys = file_content.lines().enumerate().filter_map(|(no, line)| { + line.split_once(':').map(|(key, _)| { + let mut key = key.trim().chars(); + key.next(); + key.next_back(); + (no, key.as_str()) + }) + }); + //speed up the search by sorting all keys! + let mut file_content_keys_sorted = file_content_keys.into_iter().collect::>(); + file_content_keys_sorted.sort_by_key(|(_, key)| *key); + + let mut result = RawFragment:: { + string_map: HashMap::new(), + vector_map: HashMap::new(), + }; + + for (original_key, _) in fragment.string_map { + let line_nb = file_content_keys_sorted + .binary_search_by_key(&original_key.as_str(), |(_, key)| *key) + .map_or_else( + |_| { + eprintln!( + "Key {} does not have a git line in it's state!", + original_key + ); + None + }, + |id| Some(file_content_keys_sorted[id].0), + ); + + result + .string_map + .insert(original_key, LocalizationEntryState::new(line_nb)); + } + + // Find commit for each keys, THIS PART IS SLOW (2s/4s) + for e in repo + .blame_file(path, None) + .expect("Impossible to generate the Git blame") + .iter() + { + for (_, state) in result.string_map.iter_mut() { + if let Some(line) = state.key_line { + let range = ( + e.final_start_line(), + e.final_start_line() + e.lines_in_hunk(), + ); + if line + 1 >= range.0 && line + 1 < range.1 { + state.chuck_line_range = Some(range); + state.commit_id = state.commit_id.map_or_else( + || Some(e.final_commit_id()), + |existing_commit| match repo + .graph_descendant_of(e.final_commit_id(), existing_commit) + { + Ok(true) => Some(e.final_commit_id()), + Ok(false) => Some(existing_commit), + Err(err) => panic!("{}", err), + }, + ); + } + } + } + } + + result +} diff --git a/voxygen/i18n/src/lib.rs b/voxygen/i18n/src/lib.rs index 1d8462e15d..c5e09530b4 100644 --- a/voxygen/i18n/src/lib.rs +++ b/voxygen/i18n/src/lib.rs @@ -1,7 +1,368 @@ #[cfg(any(feature = "bin", test))] pub mod analysis; -mod data; +#[cfg(any(feature = "bin", test))] +mod gitfragments; +mod path; +mod raw; +#[cfg(any(feature = "bin", test))] pub mod stats; pub mod verification; -use common_assets as assets; -pub use data::*; +//reexport +pub use path::BasePath; + +use crate::path::{LANG_EXTENSION, LANG_MANIFEST_FILE}; +use common_assets::{self, source::DirEntry, AssetExt, AssetGuard, AssetHandle}; +use hashbrown::{HashMap, HashSet}; +use raw::{RawFragment, RawLanguage, RawManifest}; +use serde::{Deserialize, Serialize}; +use std::{io, path::PathBuf}; +use tracing::warn; + +/// The reference language, aka the more up-to-date localization data. +/// Also the default language at first startup. +pub const REFERENCE_LANG: &str = "en"; + +/// How a language can be described +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct LanguageMetadata { + /// A human friendly language name (e.g. "English (US)") + pub language_name: String, + + /// A short text identifier for this language (e.g. "en_US") + /// + /// On the opposite of `language_name` that can change freely, + /// `language_identifier` value shall be stable in time as it + /// is used by setting components to store the language + /// selected by the user. + pub language_identifier: String, +} + +/// Store font metadata +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Font { + /// Key to retrieve the font in the asset system + pub asset_key: String, + + /// Scale ratio to resize the UI text dynamically + scale_ratio: f32, +} + +impl Font { + /// Scale input size to final UI size + pub fn scale(&self, value: u32) -> u32 { (value as f32 * self.scale_ratio).round() as u32 } +} + +/// Store font metadata +pub type Fonts = HashMap; + +/// Store internationalization data +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct Language { + /// A map storing the localized texts + /// + /// Localized content can be accessed using a String key. + pub(crate) string_map: HashMap, + + /// A map for storing variations of localized texts, for example multiple + /// ways of saying "Help, I'm under attack". Used primarily for npc + /// dialogue. + pub(crate) vector_map: HashMap>, + + /// Whether to convert the input text encoded in UTF-8 + /// into a ASCII version by using the `deunicode` crate. + pub(crate) convert_utf8_to_ascii: bool, + + /// Font configuration is stored here + pub(crate) fonts: Fonts, + + pub(crate) metadata: LanguageMetadata, +} + +impl Language { + /// Get a localized text from the given key + pub fn get<'a>(&'a self, key: &'a str) -> Option<&str> { + self.string_map.get(key).map(String::as_str) + } + + /// Get a variation of localized text from the given key + /// + /// `index` should be a random number from `0` to `u16::max()` + /// + /// If the key is not present in the localization object + /// then the key is returned. + pub fn get_variation<'a>(&'a self, key: &'a str, index: u16) -> Option<&str> { + self.vector_map.get(key).and_then(|v| { + if v.is_empty() { + None + } else { + Some(v[index as usize % v.len()].as_str()) + } + }) + } +} + +impl common_assets::Compound for Language { + fn load( + cache: &common_assets::AssetCache, + asset_key: &str, + ) -> Result { + let manifest = cache + .load::(&[asset_key, ".", LANG_MANIFEST_FILE].concat())? + .cloned(); + + // Walk through files in the folder, collecting localization fragment to merge + // inside the asked_localization + let mut fragments = HashMap::new(); + for id in cache + .load_dir::>(asset_key, true)? + .ids() + { + // Don't try to load manifests + if id.ends_with(&[".", LANG_MANIFEST_FILE].concat()) { + continue; + } + + match cache.load(id) { + Ok(handle) => { + let fragment: &RawFragment = &*handle.read(); + + fragments.insert(PathBuf::from(id), fragment.clone()); + }, + Err(e) => { + warn!("Unable to load asset {}, error={:?}", id, e); + }, + } + } + + Ok(Language::from(RawLanguage { + manifest, + fragments, + })) + } +} + +/// the central data structure to handle localization in veloren +// inherit Copy+Clone from AssetHandle +#[derive(Debug, PartialEq, Copy, Clone)] +pub struct LocalizationHandle { + active: AssetHandle, + fallback: Option>, + pub use_english_fallback: bool, +} + +// RAII guard returned from Localization::read(), resembles AssetGuard +pub struct LocalizationGuard { + active: AssetGuard, + fallback: Option>, +} + +// arbitrary choice to minimize changing all of veloren +pub type Localization = LocalizationGuard; + +impl LocalizationGuard { + /// Get a localized text from the given key + /// + /// First lookup is done in the active language, second in + /// the fallback (if present). + /// If the key is not present in the localization object + /// then the key is returned. + pub fn get<'a>(&'a self, key: &'a str) -> &str { + self.active.get(key).unwrap_or_else(|| { + self.fallback + .as_ref() + .and_then(|f| f.get(key)) + .unwrap_or(key) + }) + } + + /// Get a variation of localized text from the given key + /// + /// `index` should be a random number from `0` to `u16::max()` + /// + /// If the key is not present in the localization object + /// then the key is returned. + pub fn get_variation<'a>(&'a self, key: &'a str, index: u16) -> &str { + self.active.get_variation(key, index).unwrap_or_else(|| { + self.fallback + .as_ref() + .and_then(|f| f.get_variation(key, index)) + .unwrap_or(key) + }) + } + + /// Return the missing keys compared to the reference language + fn list_missing_entries(&self) -> (HashSet, HashSet) { + if let Some(ref_lang) = &self.fallback { + let reference_string_keys: HashSet<_> = ref_lang.string_map.keys().cloned().collect(); + let string_keys: HashSet<_> = self.active.string_map.keys().cloned().collect(); + let strings = reference_string_keys + .difference(&string_keys) + .cloned() + .collect(); + + let reference_vector_keys: HashSet<_> = ref_lang.vector_map.keys().cloned().collect(); + let vector_keys: HashSet<_> = self.active.vector_map.keys().cloned().collect(); + let vectors = reference_vector_keys + .difference(&vector_keys) + .cloned() + .collect(); + + (strings, vectors) + } else { + (HashSet::default(), HashSet::default()) + } + } + + /// Log missing entries (compared to the reference language) as warnings + pub fn log_missing_entries(&self) { + let (missing_strings, missing_vectors) = self.list_missing_entries(); + for missing_key in missing_strings { + warn!( + "[{:?}] Missing string key {:?}", + self.metadata().language_identifier, + missing_key + ); + } + for missing_key in missing_vectors { + warn!( + "[{:?}] Missing vector key {:?}", + self.metadata().language_identifier, + missing_key + ); + } + } + + pub fn fonts(&self) -> &Fonts { &self.active.fonts } + + pub fn metadata(&self) -> &LanguageMetadata { &self.active.metadata } +} + +impl LocalizationHandle { + pub fn set_english_fallback(&mut self, use_english_fallback: bool) { + self.use_english_fallback = use_english_fallback; + } + + pub fn read(&self) -> LocalizationGuard { + LocalizationGuard { + active: self.active.read(), + fallback: if self.use_english_fallback { + self.fallback.map(|f| f.read()) + } else { + None + }, + } + } + + pub fn load(specifier: &str) -> Result { + let default_key = ["voxygen.i18n.", REFERENCE_LANG].concat(); + let language_key = ["voxygen.i18n.", specifier].concat(); + let is_default = language_key == default_key; + Ok(Self { + active: Language::load(&language_key)?, + fallback: if is_default { + None + } else { + Language::load(&default_key).ok() + }, + use_english_fallback: false, + }) + } + + pub fn load_expect(specifier: &str) -> Self { + Self::load(specifier).expect("Can't load language files") + } + + pub fn reloaded(&mut self) -> bool { self.active.reloaded() } +} + +struct FindManifests; + +impl common_assets::Compound for FindManifests { + fn load( + _: &common_assets::AssetCache, + _: &str, + ) -> Result { + Ok(Self) + } +} + +impl common_assets::DirLoadable for FindManifests { + fn select_ids( + source: &S, + specifier: &str, + ) -> io::Result> { + let mut specifiers = Vec::new(); + + source.read_dir(specifier, &mut |entry| { + if let DirEntry::Directory(spec) = entry { + let manifest_spec = [spec, ".", LANG_MANIFEST_FILE].concat(); + if source.exists(DirEntry::File(&manifest_spec, LANG_EXTENSION)) { + specifiers.push(manifest_spec.into()); + } + } + })?; + + Ok(specifiers) + } +} + +#[derive(Clone, Debug)] +struct LocalizationList(Vec); + +impl common_assets::Compound for LocalizationList { + fn load( + cache: &common_assets::AssetCache, + specifier: &str, + ) -> Result { + // List language directories + let languages = common_assets::load_dir::(specifier, false) + .unwrap_or_else(|e| panic!("Failed to get manifests from {}: {:?}", specifier, e)) + .ids() + .filter_map(|spec| cache.load::(spec).ok()) + .map(|localization| localization.read().metadata.clone()) + .collect(); + + Ok(LocalizationList(languages)) + } +} + +/// Load all the available languages located in the voxygen asset directory +pub fn list_localizations() -> Vec { + LocalizationList::load_expect_cloned("voxygen.i18n").0 +} + +#[cfg(test)] +mod tests { + use crate::path::BasePath; + + // Test that localization list is loaded (not empty) + #[test] + fn test_localization_list() { + let list = super::list_localizations(); + assert!(!list.is_empty()); + } + + // Test that reference language can be loaded + #[test] + fn test_localization_handle() { + let _ = super::LocalizationHandle::load_expect(super::REFERENCE_LANG); + } + + // Test to verify all languages that they are VALID and loadable, without + // need of git just on the local assets folder + #[test] + fn verify_all_localizations() { + // Generate paths + let root_dir = common_assets::find_root().expect("Failed to discover repository root"); + crate::verification::verify_all_localizations(&BasePath::new(&root_dir)); + } + + // Test to verify all languages and print missing and faulty localisation + #[test] + #[ignore] + fn test_all_localizations() { + // Generate paths + let root_dir = common_assets::find_root().expect("Failed to discover repository root"); + crate::analysis::test_all_localizations(&BasePath::new(&root_dir), true, true); + } +} diff --git a/voxygen/i18n/src/path.rs b/voxygen/i18n/src/path.rs new file mode 100644 index 0000000000..3f25e55adf --- /dev/null +++ b/voxygen/i18n/src/path.rs @@ -0,0 +1,141 @@ +use std::path::{Path, PathBuf}; + +pub(crate) const LANG_MANIFEST_FILE: &str = "_manifest"; +pub(crate) const LANG_EXTENSION: &str = "ron"; + +#[derive(Clone)] +pub struct BasePath { + ///repo part, git main folder + root_path: PathBuf, + ///relative path to i18n path which contains, currently + /// 'assets/voxygen/i18n' + relative_i18n_root_path: PathBuf, + ///i18n_root_folder + cache: PathBuf, +} + +impl BasePath { + pub fn new(root_path: &Path) -> Self { + let relative_i18n_root_path = Path::new("assets/voxygen/i18n").to_path_buf(); + let cache = root_path.join(&relative_i18n_root_path); + assert!( + cache.is_dir(), + "i18n_root_path folder doesn't exist, something is wrong!" + ); + Self { + root_path: root_path.to_path_buf(), + relative_i18n_root_path, + cache, + } + } + + pub fn root_path(&self) -> &Path { &self.root_path } + + pub fn relative_i18n_root_path(&self) -> &Path { &self.relative_i18n_root_path } + + /// absolute path to `relative_i18n_root_path` + pub fn i18n_root_path(&self) -> &Path { &self.cache } + + pub fn i18n_path(&self, language_identifier: &str) -> LangPath { + LangPath::new(self, language_identifier) + } + + /// List localization directories + pub fn i18n_directories(&self) -> Vec { + std::fs::read_dir(&self.cache) + .unwrap() + .map(|res| res.unwrap()) + .filter(|e| e.file_type().unwrap().is_dir()) + .map(|e| LangPath::new(self, e.file_name().to_str().unwrap())) + .collect() + } +} + +impl core::fmt::Debug for BasePath { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", &self.cache) + } +} + +#[derive(Clone)] +pub struct LangPath { + base: BasePath, + /// `en`, `de_DE`, `fr_FR`, etc.. + language_identifier: String, + /// i18n_path + cache: PathBuf, +} + +impl LangPath { + fn new(base: &BasePath, language_identifier: &str) -> Self { + let cache = base.i18n_root_path().join(language_identifier); + if !cache.is_dir() { + panic!("language folder '{}' doesn't exist", language_identifier); + } + Self { + base: base.clone(), + language_identifier: language_identifier.to_owned(), + cache, + } + } + + pub fn base(&self) -> &BasePath { &self.base } + + pub fn language_identifier(&self) -> &str { &self.language_identifier } + + ///absolute path to `i18n_root_path` + `language_identifier` + pub fn i18n_path(&self) -> &Path { &self.cache } + + /// fragment or manifest file, based on a path + pub fn sub_path(&self, sub_path: &Path) -> PathBuf { self.cache.join(sub_path) } + + /// fragment or manifest file, based on a string without extension + pub fn file(&self, name_without_extension: &str) -> PathBuf { + self.cache + .join(format!("{}.{}", name_without_extension, LANG_EXTENSION)) + } + + /// return all fragments sub_pathes + pub(crate) fn fragments(&self) -> Result, std::io::Error> { + let mut result = vec![]; + recursive_fragments_paths_in_language(self, Path::new(""), &mut result)?; + Ok(result) + } +} + +//unwraps cant fail as they are in same Path +fn recursive_fragments_paths_in_language( + lpath: &LangPath, + subfolder: &Path, + result: &mut Vec, +) -> Result<(), std::io::Error> { + let manifest_path = PathBuf::from(&format!("{}.{}", LANG_MANIFEST_FILE, LANG_EXTENSION)); + let template_path = PathBuf::from(&format!("{}.{}", "template", LANG_EXTENSION)); + let search_dir = lpath.sub_path(subfolder); + for fragment_file in search_dir.read_dir()?.flatten() { + let file_type = fragment_file.file_type()?; + let full_path = fragment_file.path(); + let relative_path = full_path.strip_prefix(lpath.i18n_path()).unwrap(); + if file_type.is_dir() { + recursive_fragments_paths_in_language(lpath, relative_path, result)?; + } else if file_type.is_file() + && relative_path != manifest_path + && relative_path != template_path + { + result.push(relative_path.to_path_buf()); + } + } + Ok(()) +} + +impl core::fmt::Debug for LangPath { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "{:?}", + self.base + .relative_i18n_root_path + .join(&self.language_identifier) + ) + } +} diff --git a/voxygen/i18n/src/raw.rs b/voxygen/i18n/src/raw.rs new file mode 100644 index 0000000000..56e4e4b1c7 --- /dev/null +++ b/voxygen/i18n/src/raw.rs @@ -0,0 +1,132 @@ +//! handle the loading of a `Language` +use crate::{ + path::{LangPath, LANG_EXTENSION, LANG_MANIFEST_FILE}, + Fonts, Language, LanguageMetadata, +}; +use deunicode::deunicode; +use hashbrown::hash_map::HashMap; +use ron::de::from_reader; +use serde::{Deserialize, Serialize}; +use std::{fs, path::PathBuf}; + +/// Raw localization metadata from LANG_MANIFEST_FILE file +/// See `Language` for more info on each attributes +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +pub(crate) struct RawManifest { + pub(crate) convert_utf8_to_ascii: bool, + pub(crate) fonts: Fonts, + pub(crate) metadata: LanguageMetadata, +} + +/// Raw localization data from one specific file +/// These structs are meant to be merged into a Language +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +pub(crate) struct RawFragment { + pub(crate) string_map: HashMap, + pub(crate) vector_map: HashMap>, +} + +pub(crate) struct RawLanguage { + pub(crate) manifest: RawManifest, + pub(crate) fragments: HashMap>, +} + +#[derive(Debug)] +pub(crate) enum RawError { + RonError(ron::Error), +} + +pub(crate) fn load_manifest(path: &LangPath) -> Result { + let manifest_file = path.file(LANG_MANIFEST_FILE); + tracing::debug!(?manifest_file, "manifest loading"); + let f = fs::File::open(&manifest_file)?; + let manifest: RawManifest = from_reader(f).map_err(RawError::RonError)?; + // verify that the folder name `de_DE` matches the value inside the metadata! + assert_eq!( + manifest.metadata.language_identifier, + path.language_identifier() + ); + Ok(manifest) +} + +pub(crate) fn load_raw_language( + path: &LangPath, + manifest: RawManifest, +) -> Result, common_assets::Error> { + //get List of files + let files = path.fragments()?; + + // Walk through each file in the directory + let mut fragments = HashMap::new(); + for sub_path in files { + let f = fs::File::open(path.sub_path(&sub_path))?; + let fragment = from_reader(f).map_err(RawError::RonError)?; + fragments.insert(sub_path, fragment); + } + + Ok(RawLanguage { + manifest, + fragments, + }) +} + +impl From> for Language { + fn from(raw: RawLanguage) -> Self { + let mut string_map = HashMap::new(); + let mut vector_map = HashMap::new(); + + for (_, fragment) in raw.fragments { + string_map.extend(fragment.string_map); + vector_map.extend(fragment.vector_map); + } + + let convert_utf8_to_ascii = raw.manifest.convert_utf8_to_ascii; + + // Update the text if UTF-8 to ASCII conversion is enabled + if convert_utf8_to_ascii { + for value in string_map.values_mut() { + *value = deunicode(value); + } + + for value in vector_map.values_mut() { + *value = value.iter().map(|s| deunicode(s)).collect(); + } + } + let mut metadata = raw.manifest.metadata; + metadata.language_name = deunicode(&metadata.language_name); + + Self { + string_map, + vector_map, + convert_utf8_to_ascii, + fonts: raw.manifest.fonts, + metadata, + } + } +} + +impl core::fmt::Display for RawError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + RawError::RonError(e) => write!(f, "{}", e), + } + } +} + +impl std::error::Error for RawError {} + +impl From for common_assets::Error { + fn from(e: RawError) -> Self { Self::Conversion(Box::new(e)) } +} + +impl common_assets::Asset for RawManifest { + type Loader = common_assets::RonLoader; + + const EXTENSION: &'static str = LANG_EXTENSION; +} + +impl common_assets::Asset for RawFragment { + type Loader = common_assets::RonLoader; + + const EXTENSION: &'static str = LANG_EXTENSION; +} diff --git a/voxygen/i18n/src/stats.rs b/voxygen/i18n/src/stats.rs new file mode 100644 index 0000000000..534b3de8cc --- /dev/null +++ b/voxygen/i18n/src/stats.rs @@ -0,0 +1,199 @@ +use crate::{ + gitfragments::{LocalizationEntryState, LocalizationState, ALL_LOCALIZATION_STATES}, + raw::RawLanguage, +}; +use hashbrown::HashMap; +use std::path::PathBuf; + +#[derive(Default, Debug, PartialEq)] +pub(crate) struct LocalizationStats { + pub(crate) uptodate_entries: usize, + pub(crate) notfound_entries: usize, + pub(crate) unused_entries: usize, + pub(crate) outdated_entries: usize, + pub(crate) errors: usize, +} + +#[allow(clippy::type_complexity)] +pub(crate) struct LocalizationAnalysis { + language_identifier: String, + pub(crate) data: HashMap, Vec<(PathBuf, String, Option)>>, +} + +impl LocalizationStats { + /// Calculate key count that actually matter for the status of the + /// translation Unused entries don't break the game + pub(crate) fn get_real_entry_count(&self) -> usize { + self.outdated_entries + self.notfound_entries + self.errors + self.uptodate_entries + } +} + +impl LocalizationAnalysis { + pub(crate) fn new(language_identifier: &str) -> Self { + let mut data = HashMap::new(); + for key in ALL_LOCALIZATION_STATES.iter() { + data.insert(*key, vec![]); + } + Self { + language_identifier: language_identifier.to_owned(), + data, + } + } + + fn show( + &self, + state: Option, + ref_language: &RawLanguage, + be_verbose: bool, + output: &mut W, + ) { + let entries = self.data.get(&state).unwrap_or_else(|| { + panic!( + "called on invalid state: {}", + LocalizationState::print(&state) + ) + }); + if entries.is_empty() { + return; + } + writeln!(output, "\n\t[{}]", LocalizationState::print(&state)).unwrap(); + for (path, key, commit_id) in entries { + if be_verbose { + let our_commit = LocalizationAnalysis::print_commit(commit_id); + let ref_commit = ref_language + .fragments + .get(path) + .and_then(|entry| entry.string_map.get(key)) + .and_then(|s| s.commit_id) + .map(|s| format!("{}", s)) + .unwrap_or_else(|| "None".to_owned()); + writeln!(output, "{:60}| {:40} | {:40}", key, our_commit, ref_commit).unwrap(); + } else { + writeln!(output, "{}", key).unwrap(); + } + } + } + + fn csv(&self, state: Option, output: &mut W) { + let entries = self + .data + .get(&state) + .unwrap_or_else(|| panic!("called on invalid state: {:?}", state)); + for (path, key, commit_id) in entries { + let our_commit = LocalizationAnalysis::print_commit(commit_id); + writeln!( + output, + "{},{:?},{},{},{}", + self.language_identifier, + path, + key, + LocalizationState::print(&state), + our_commit + ) + .unwrap(); + } + } + + fn print_commit(commit_id: &Option) -> String { + commit_id + .map(|s| format!("{}", s)) + .unwrap_or_else(|| "None".to_owned()) + } +} + +pub(crate) fn print_translation_stats( + language_identifier: &str, + ref_language: &RawLanguage, + stats: &LocalizationStats, + state_map: &LocalizationAnalysis, + be_verbose: bool, +) { + let real_entry_count = stats.get_real_entry_count() as f32; + let uptodate_percent = (stats.uptodate_entries as f32 / real_entry_count) * 100_f32; + let outdated_percent = (stats.outdated_entries as f32 / real_entry_count) * 100_f32; + let untranslated_percent = ((stats.errors + stats.errors) as f32 / real_entry_count) * 100_f32; + + // Display + if be_verbose { + println!( + "\n{:60}| {:40} | {:40}", + "Key name", language_identifier, ref_language.manifest.metadata.language_identifier, + ); + } else { + println!("\nKey name"); + } + + for state in &ALL_LOCALIZATION_STATES { + if state == &Some(LocalizationState::UpToDate) { + continue; + } + state_map.show(*state, ref_language, be_verbose, &mut std::io::stdout()); + } + + println!( + "\n{} up-to-date, {} outdated, {} unused, {} not found, {} unknown entries", + stats.uptodate_entries, + stats.outdated_entries, + stats.unused_entries, + stats.notfound_entries, + stats.errors, + ); + + println!( + "{:.2}% up-to-date, {:.2}% outdated, {:.2}% untranslated\n", + uptodate_percent, outdated_percent, untranslated_percent, + ); +} + +pub(crate) fn print_csv_stats(state_map: &LocalizationAnalysis, output: &mut W) { + for state in &ALL_LOCALIZATION_STATES { + state_map.csv(*state, output); + } +} + +pub(crate) fn print_overall_stats( + analysis: HashMap, +) { + let mut overall_uptodate_entry_count = 0; + let mut overall_outdated_entry_count = 0; + let mut overall_untranslated_entry_count = 0; + let mut overall_real_entry_count = 0; + + println!("-----------------------------------------------------------------------------"); + println!("Overall Translation Status"); + println!("-----------------------------------------------------------------------------"); + println!( + "{:12}| {:8} | {:8} | {:8} | {:8} | {:8}", + "", "up-to-date", "outdated", "untranslated", "unused", "errors", + ); + + let mut i18n_stats: Vec<(&String, &(_, LocalizationStats))> = analysis.iter().collect(); + i18n_stats.sort_by_key(|(_, (_, v))| v.notfound_entries); + + for (path, (_, test_result)) in i18n_stats { + let LocalizationStats { + uptodate_entries: uptodate, + outdated_entries: outdated, + unused_entries: unused, + notfound_entries: untranslated, + errors, + } = test_result; + overall_uptodate_entry_count += uptodate; + overall_outdated_entry_count += outdated; + overall_untranslated_entry_count += untranslated; + overall_real_entry_count += test_result.get_real_entry_count(); + + println!( + "{:12}|{:8} |{:6} |{:8} |{:6} |{:8}", + path, uptodate, outdated, untranslated, unused, errors, + ); + } + + println!( + "\n{:.2}% up-to-date, {:.2}% outdated, {:.2}% untranslated", + (overall_uptodate_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, + (overall_outdated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, + (overall_untranslated_entry_count as f32 / overall_real_entry_count as f32) * 100_f32, + ); + println!("-----------------------------------------------------------------------------\n"); +} diff --git a/voxygen/i18n/src/verification.rs b/voxygen/i18n/src/verification.rs index ad7897c386..fa784b9db5 100644 --- a/voxygen/i18n/src/verification.rs +++ b/voxygen/i18n/src/verification.rs @@ -1,52 +1,17 @@ -use ron::de::from_reader; -use std::{fs, path::Path}; +use crate::path::{BasePath, LangPath, LANG_MANIFEST_FILE}; -use crate::data::{i18n_directories, LocalizationFragment, LANG_MANIFEST_FILE, REFERENCE_LANG}; - -fn verify_localization_directory(root_dir: &Path, directory_path: &Path) { - // Walk through each file in the directory - for i18n_file in root_dir.join(&directory_path).read_dir().unwrap().flatten() { - if let Ok(file_type) = i18n_file.file_type() { - // Skip folders and the manifest file (which does not contain the same struct we - // want to load) - if file_type.is_file() { - let full_path = i18n_file.path(); - println!("-> {:?}", full_path.strip_prefix(&root_dir).unwrap()); - let f = fs::File::open(&full_path).expect("Failed opening file"); - let _loc: LocalizationFragment = match from_reader(f) { - Ok(v) => v, - Err(e) => { - panic!( - "Could not parse {} RON file, error: {}", - full_path.to_string_lossy(), - e - ); - }, - }; - } else if file_type.is_dir() { - verify_localization_directory(root_dir, &i18n_file.path()); - } - } - } -} +use crate::{raw, REFERENCE_LANG}; /// Test to verify all languages that they are VALID and loadable, without /// need of git just on the local assets folder -/// `root_dir` - absolute path to main repo -/// `asset_path` - relative path to asset directory (right now it is -/// 'assets/voxygen/i18n') -pub fn verify_all_localizations(root_dir: &Path, asset_path: &Path) { - let ref_i18n_dir_path = asset_path.join(REFERENCE_LANG); - let ref_i18n_path = ref_i18n_dir_path.join(LANG_MANIFEST_FILE.to_string() + ".ron"); +pub fn verify_all_localizations(path: &BasePath) { + let ref_i18n_path = path.i18n_path(REFERENCE_LANG); + let ref_i18n_manifest_path = ref_i18n_path.file(LANG_MANIFEST_FILE); assert!( - root_dir.join(&ref_i18n_dir_path).is_dir(), - "Reference language folder doesn't exist, something is wrong!" - ); - assert!( - root_dir.join(&ref_i18n_path).is_file(), + ref_i18n_manifest_path.is_file(), "Reference language manifest file doesn't exist, something is wrong!" ); - let i18n_directories = i18n_directories(&root_dir.join(asset_path)); + let i18n_directories = path.i18n_directories(); // This simple check ONLY guarantees that an arbitrary minimum of translation // files exists. It's just to notice unintentional deletion of all // files, or modifying the paths. In case you want to delete all @@ -57,11 +22,13 @@ pub fn verify_all_localizations(root_dir: &Path, asset_path: &Path) { folder is empty?" ); for i18n_directory in i18n_directories { - println!( - "verifying {:?}", - i18n_directory.strip_prefix(&root_dir).unwrap() - ); + println!("verifying {:?}", i18n_directory); // Walk through each files and try to load them - verify_localization_directory(root_dir, &i18n_directory); + verify_localization_directory(&i18n_directory); } } + +fn verify_localization_directory(path: &LangPath) { + let manifest = raw::load_manifest(path).expect("error accessing manifest file"); + raw::load_raw_language(path, manifest).expect("error accessing fragment file"); +} diff --git a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs index 4532c412dd..05515813d4 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs @@ -147,7 +147,7 @@ fn matches_ability_stage() { exhausted: false, stage: 1, timer: Duration::default(), - stage_section: states::utils::StageSection::Swing, + stage_section: states::utils::StageSection::Action, }), &PreviousEntityState { event: SfxEvent::Idle, @@ -160,7 +160,7 @@ fn matches_ability_stage() { assert_eq!( result, SfxEvent::Attack( - CharacterAbilityType::ComboMelee(states::utils::StageSection::Swing, 1), + CharacterAbilityType::ComboMelee(states::utils::StageSection::Action, 1), ToolKind::Sword ) ); @@ -209,7 +209,7 @@ fn ignores_different_ability_stage() { exhausted: false, stage: 1, timer: Duration::default(), - stage_section: states::utils::StageSection::Swing, + stage_section: states::utils::StageSection::Action, }), &PreviousEntityState { event: SfxEvent::Idle, @@ -222,7 +222,7 @@ fn ignores_different_ability_stage() { assert_ne!( result, SfxEvent::Attack( - CharacterAbilityType::ComboMelee(states::utils::StageSection::Swing, 2), + CharacterAbilityType::ComboMelee(states::utils::StageSection::Action, 2), ToolKind::Sword ) ); diff --git a/voxygen/src/controller.rs b/voxygen/src/controller.rs index b9eec9aa82..b64860e9e0 100644 --- a/voxygen/src/controller.rs +++ b/voxygen/src/controller.rs @@ -150,6 +150,10 @@ impl From<&crate::settings::GamepadSettings> for ControllerSettings { map.entry(settings.game_buttons.toggle_debug) .or_default() .push(GameInput::ToggleDebug); + #[cfg(feature = "egui-ui")] + map.entry(settings.game_buttons.toggle_debug) + .or_default() + .push(GameInput::ToggleEguiDebug); map.entry(settings.game_buttons.toggle_chat) .or_default() .push(GameInput::ToggleChat); diff --git a/voxygen/src/game_input.rs b/voxygen/src/game_input.rs index db75015f1b..6756330cd7 100644 --- a/voxygen/src/game_input.rs +++ b/voxygen/src/game_input.rs @@ -106,6 +106,9 @@ pub enum GameInput { Help, #[strum(serialize = "gameinput.toggledebug")] ToggleDebug, + #[cfg(feature = "egui-ui")] + #[strum(serialize = "gameinput.toggle_egui_debug")] + ToggleEguiDebug, #[strum(serialize = "gameinput.togglechat")] ToggleChat, #[strum(serialize = "gameinput.fullscreen")] diff --git a/voxygen/src/hud/animation.rs b/voxygen/src/hud/animation.rs new file mode 100644 index 0000000000..ec6a141f3e --- /dev/null +++ b/voxygen/src/hud/animation.rs @@ -0,0 +1,3 @@ +pub fn animation_timer(pulse: f32) -> f32 { + (pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8 //y=0.5cos(4x)+0.8 +} diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index 343df2f232..dcab283ce1 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -7,7 +7,6 @@ use super::{ }; use crate::{ game_input::GameInput, - i18n::Localization, ui::{ fonts::Fonts, slot::{ContentSize, SlotMaker}, @@ -31,6 +30,7 @@ use conrod_core::{ widget::{self, Button, Image, Rectangle, Scrollbar, State as ConrodState, Text}, widget_ids, Color, Colorable, Positionable, Scalar, Sizeable, UiCell, Widget, WidgetCommon, }; +use i18n::Localization; use crate::hud::slots::SlotKind; use specs::Entity as EcsEntity; @@ -56,7 +56,9 @@ widget_ids! { inventory_title, inventory_title_bg, scrollbar_bg, + second_phase_scrollbar_bg, scrollbar_slots, + left_scrollbar_slots, } } @@ -131,26 +133,55 @@ impl<'a> InventoryScroller<'a> { } fn background(&mut self, ui: &mut UiCell<'_>) { + let bg_id = if !self.on_right { + self.imgs.inv_bg_bag + } else { + self.imgs.player_inv_bg_bag + }; + + let img_id = if !self.on_right { + self.imgs.inv_frame_bag + } else { + self.imgs.player_inv_frame_bag + }; + let mut bg = Image::new(if self.show_stats { self.imgs.inv_bg_stats } else if self.show_bag_inv { - self.imgs.inv_bg_bag + bg_id } else { self.imgs.inv_bg_armor }) - .w_h(424.0, 708.0); + .w_h( + 424.0, + if self.show_bag_inv && !self.on_right { + 548.0 + } else { + 708.0 + }, + ); + if self.on_right { - bg = bg.bottom_right_with_margins_on(ui.window, 60.0, 5.0); + bg = bg.bottom_right_with_margins_on(ui.window, 70.0, 5.0); } else { - bg = bg.bottom_left_with_margins_on(ui.window, 60.0, 5.0); + bg = bg.bottom_left_with_margins_on(ui.window, 230.0, 5.0); } + bg.color(Some(UI_MAIN)).set(self.bg_ids.bg, ui); + Image::new(if self.show_bag_inv { - self.imgs.inv_frame_bag + img_id } else { self.imgs.inv_frame }) - .w_h(424.0, 708.0) + .w_h( + 424.0, + if self.show_bag_inv && !self.on_right { + 548.0 + } else { + 708.0 + }, + ) .middle_of(self.bg_ids.bg) .color(Some(UI_HIGHLIGHT_0)) .set(self.bg_ids.bg_frame, ui); @@ -187,6 +218,7 @@ impl<'a> InventoryScroller<'a> { ui: &mut UiCell<'_>, ) { let space_max = self.inventory.slots().count(); + // Slots Scrollbar if space_max > 45 && !self.show_bag_inv { // Scrollbar-BG @@ -202,7 +234,7 @@ impl<'a> InventoryScroller<'a> { .color(UI_MAIN) .middle_of(state.ids.scrollbar_bg) .set(state.ids.scrollbar_slots, ui); - } else if space_max > 135 { + } else if space_max > 135 && self.on_right { // Scrollbar-BG Image::new(self.imgs.scrollbar_bg_big) .w_h(9.0, 592.0) @@ -217,14 +249,45 @@ impl<'a> InventoryScroller<'a> { .middle_of(state.ids.scrollbar_bg) .set(state.ids.scrollbar_slots, ui); }; + + // This is just for the offeror inventory scrollbar + if space_max >= 108 && !self.on_right && self.show_bag_inv { + // Left bag scrollbar background + Image::new(self.imgs.second_phase_scrollbar_bg) + .w_h(9.0, 434.0) + .bottom_right_with_margins_on(self.bg_ids.bg_frame, 42.0, 3.0) + .color(Some(UI_HIGHLIGHT_0)) + .set(state.ids.second_phase_scrollbar_bg, ui); + // Left bag scrollbar + Scrollbar::y_axis(state.ids.inv_alignment) + .thickness(5.0) + .h(384.0) + .color(UI_MAIN) + .middle_of(state.ids.second_phase_scrollbar_bg) + .set(state.ids.left_scrollbar_slots, ui); + } + + let grid_width = if self.show_bag_inv && !self.on_right { + 440.0 // This for the left bag + } else if self.show_bag_inv && self.on_right { + 600.0 // This for the expanded right bag + } else { + 200.0 + }; + // Alignment for Grid - Rectangle::fill_with( - [362.0, if self.show_bag_inv { 600.0 } else { 200.0 }], - color::TRANSPARENT, - ) - .bottom_left_with_margins_on(self.bg_ids.bg_frame, 29.0, 46.5) - .scroll_kids_vertically() - .set(state.ids.inv_alignment, ui); + Rectangle::fill_with([362.0, grid_width], color::TRANSPARENT) + .bottom_left_with_margins_on( + self.bg_ids.bg_frame, + 29.0, + if self.show_bag_inv && !self.on_right { + 28.0 + } else { + 46.5 + }, + ) + .scroll_kids_vertically() + .set(state.ids.inv_alignment, ui); // Bag Slots // Create available inventory slot widgets @@ -533,7 +596,7 @@ impl<'a> Bag<'a> { } const STATS: [&str; 5] = [ "Health", - "Stamina", + "Energy", "Protection", "Combat Rating", "Stun Resilience", @@ -809,7 +872,7 @@ impl<'a> Widget for Bag<'a> { for i in STATS.iter().copied().enumerate() { let btn = Button::image(match i.1 { "Health" => self.imgs.health_ico, - "Stamina" => self.imgs.stamina_ico, + "Energy" => self.imgs.energy_ico, "Combat Rating" => self.imgs.combat_rating_ico, "Protection" => self.imgs.protection_ico, "Stun Resilience" => self.imgs.stun_res_ico, @@ -828,7 +891,7 @@ impl<'a> Widget for Bag<'a> { as i32 ); let health_txt = format!("{}", (self.health.maximum() as f32 / 10.0) as usize); - let stamina_txt = format!("{}", (self.energy.maximum() as f32 / 10.0) as usize); + let energy_txt = format!("{}", (self.energy.maximum() as f32 / 10.0) as usize); let combat_rating_txt = format!("{}", (combat_rating * 10.0) as usize); let stun_res_txt = format!( "{}", @@ -841,7 +904,7 @@ impl<'a> Widget for Bag<'a> { }; let tooltip_head = match i.1 { "Health" => i18n.get("hud.bag.health"), - "Stamina" => i18n.get("hud.bag.stamina"), + "Energy" => i18n.get("hud.bag.energy"), "Combat Rating" => i18n.get("hud.bag.combat_rating"), "Protection" => i18n.get("hud.bag.protection"), "Stun Resilience" => i18n.get("hud.bag.stun_res"), @@ -863,7 +926,7 @@ impl<'a> Widget for Bag<'a> { .set(state.ids.stat_icons[i.0], ui); Text::new(match i.1 { "Health" => &health_txt, - "Stamina" => &stamina_txt, + "Energy" => &energy_txt, "Combat Rating" => &combat_rating_txt, "Protection" => &protection_txt, "Stun Resilience" => &stun_res_txt, diff --git a/voxygen/src/hud/buffs.rs b/voxygen/src/hud/buffs.rs index 5fc82242df..2c3ca35b06 100644 --- a/voxygen/src/hud/buffs.rs +++ b/voxygen/src/hud/buffs.rs @@ -3,11 +3,11 @@ use super::{ BUFF_COLOR, DEBUFF_COLOR, TEXT_COLOR, }; use crate::{ - hud::{self, BuffPosition}, - i18n::Localization, + hud::{self, animation::animation_timer, BuffPosition}, ui::{fonts::Fonts, ImageFrame, Tooltip, TooltipManager, Tooltipable}, GlobalState, }; +use i18n::Localization; use common::comp::{BuffKind, Buffs, Energy, Health}; use conrod_core::{ @@ -104,7 +104,7 @@ impl<'a> Widget for BuffsBar<'a> { let mut event = Vec::new(); let localized_strings = self.localized_strings; let buffs = self.buffs; - let buff_ani = ((self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8) + 0.5; //Animation timer + let buff_ani = animation_timer(self.pulse) + 0.5; //Animation timer let pulsating_col = Color::Rgba(1.0, 1.0, 1.0, buff_ani); let norm_col = Color::Rgba(1.0, 1.0, 1.0, 1.0); let buff_position = self.global_state.settings.interface.buff_position; @@ -129,10 +129,10 @@ impl<'a> Widget for BuffsBar<'a> { let decayed_health = 1.0 - self.health.maximum() as f64 / self.health.base_max() as f64; let show_health = self.health.current() != self.health.maximum() || decayed_health > 0.0; - let show_stamina = self.energy.current() != self.energy.maximum(); - let offset = if show_stamina && show_health { + let show_energy = self.energy.current() != self.energy.maximum(); + let offset = if show_energy && show_health { 140.0 - } else if show_health || show_stamina { + } else if show_health || show_energy { 95.0 } else { 55.0 diff --git a/voxygen/src/hud/buttons.rs b/voxygen/src/hud/buttons.rs index 67384475b5..d91df8188c 100644 --- a/voxygen/src/hud/buttons.rs +++ b/voxygen/src/hud/buttons.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{ game_input::GameInput, - i18n::Localization, + hud::animation::animation_timer, ui::{fonts::Fonts, ImageFrame, Tooltip, TooltipManager, Tooltipable}, window::KeyMouse, GlobalState, @@ -12,9 +12,10 @@ use crate::{ use client::Client; use common::comp::{SkillSet, Stats}; use conrod_core::{ - widget::{self, Button, Image, Text}, + widget::{self, Button, Image, Text, UpdateArgs}, widget_ids, Color, Colorable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, }; +use i18n::Localization; widget_ids! { struct Ids { bag, @@ -123,16 +124,10 @@ impl<'a> Widget for Buttons<'a> { fn style(&self) -> Self::Style {} - fn update(self, args: widget::UpdateArgs) -> Self::Event { + fn update(self, args: UpdateArgs) -> Self::Event { common_base::prof_span!("Buttons::update"); - let widget::UpdateArgs { state, ui, .. } = args; - let invs = self.client.inventories(); - let inventory = match invs.get(self.client.entity()) { - Some(inv) => inv, - None => return None, - }; + let UpdateArgs { state, ui, .. } = args; let localized_strings = self.localized_strings; - let arrow_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8; //Animation timer let button_tooltip = Tooltip::new({ // Edge images [t, b, r, l] @@ -197,6 +192,11 @@ impl<'a> Widget for Buttons<'a> { state.ids.bag_text, ); } + let invs = self.client.inventories(); + let inventory = match invs.get(self.client.entity()) { + Some(inv) => inv, + None => return None, + }; if !self.show_bag { let space_used = inventory.populated_slots(); let space_max = inventory.slots().count(); @@ -357,6 +357,7 @@ impl<'a> Widget for Buttons<'a> { } // Unspent SP indicator if unspent_sp { + let arrow_ani = animation_timer(self.pulse); //Animation timer Image::new(self.imgs.sp_indicator_arrow) .w_h(20.0, 11.0) .graphics_for(state.ids.spellbook_button) @@ -425,9 +426,12 @@ impl<'a> Buttons<'a> { text: widget::Id, ) { let key_layout = &self.global_state.window.key_layout; + let key_desc = key_mouse + .display_shortened(key_layout) + .unwrap_or_else(|| key_mouse.display_string(key_layout)); //Create shadow - Text::new(key_mouse.display_string(key_layout).as_str()) + Text::new(&key_desc) .bottom_right_with_margins_on(button_identifier, 0.0, 0.0) .font_size(10) .font_id(self.fonts.cyri.conrod_id) @@ -435,7 +439,7 @@ impl<'a> Buttons<'a> { .set(text_background, ui); //Create button - Text::new(key_mouse.display_string(key_layout).as_str()) + Text::new(&key_desc) .bottom_right_with_margins_on(text_background, 1.0, 1.0) .font_size(10) .font_id(self.fonts.cyri.conrod_id) diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index c05177e1ca..43edfbc071 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -2,7 +2,7 @@ use super::{ img_ids::Imgs, ChatTab, ERROR_COLOR, FACTION_COLOR, GROUP_COLOR, INFO_COLOR, KILL_COLOR, OFFLINE_COLOR, ONLINE_COLOR, REGION_COLOR, SAY_COLOR, TELL_COLOR, TEXT_COLOR, WORLD_COLOR, }; -use crate::{i18n::Localization, settings::chat::MAX_CHAT_TABS, ui::fonts::Fonts, GlobalState}; +use crate::{settings::chat::MAX_CHAT_TABS, ui::fonts::Fonts, GlobalState}; use client::{cmd, Client}; use common::comp::{ chat::{KillSource, KillType}, @@ -22,6 +22,7 @@ use conrod_core::{ widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Ui, UiCell, Widget, WidgetCommon, }; +use i18n::Localization; use std::collections::{HashSet, VecDeque}; widget_ids! { @@ -374,7 +375,7 @@ impl<'a> Widget for Chat<'a> { _ => 0.0, }; Rectangle::fill([CHAT_BOX_WIDTH, y]) - .rgba(0.0, 0.0, 0.0, chat_settings.chat_transp + 0.1) + .rgba(0.0, 0.0, 0.0, chat_settings.chat_opacity + 0.1) .bottom_left_with_margins_on(ui.window, 10.0, 10.0) .w(CHAT_BOX_WIDTH) .set(state.ids.chat_input_bg, ui); @@ -392,7 +393,7 @@ impl<'a> Widget for Chat<'a> { // Message box Rectangle::fill([CHAT_BOX_WIDTH, CHAT_BOX_HEIGHT]) - .rgba(0.0, 0.0, 0.0, chat_settings.chat_transp) + .rgba(0.0, 0.0, 0.0, chat_settings.chat_opacity) .and(|r| { if input_focused { r.up_from(state.ids.chat_input_bg, 0.0) @@ -517,10 +518,10 @@ impl<'a> Widget for Chat<'a> { .filter(|t| t <= &1.5) { let alpha = 1.0 - (time_since_hover / 1.5).powi(4); - let shading = color::rgba(1.0, 0.82, 0.27, (chat_settings.chat_transp + 0.1) * alpha); + let shading = color::rgba(1.0, 0.82, 0.27, (chat_settings.chat_opacity + 0.1) * alpha); Rectangle::fill([CHAT_BOX_WIDTH, CHAT_TAB_HEIGHT]) - .rgba(0.0, 0.0, 0.0, (chat_settings.chat_transp + 0.1) * alpha) + .rgba(0.0, 0.0, 0.0, (chat_settings.chat_opacity + 0.1) * alpha) .up_from(state.ids.message_box_bg, 0.0) .set(state.ids.chat_tab_align, ui); if ui diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 79093537ac..dd2e917b9c 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -4,12 +4,9 @@ use super::{ item_imgs::{animate_by_pulse, ItemImgs, ItemKey::Tool}, Show, TEXT_COLOR, TEXT_DULL_RED_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN, }; -use crate::{ - i18n::Localization, - ui::{ - fonts::Fonts, ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable, Tooltip, - TooltipManager, Tooltipable, - }, +use crate::ui::{ + fonts::Fonts, ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable, Tooltip, + TooltipManager, Tooltipable, }; use client::{self, Client}; use common::{ @@ -29,6 +26,7 @@ use conrod_core::{ widget::{self, Button, Image, Rectangle, Scrollbar, Text, TextEdit}, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; use std::sync::Arc; use strum::IntoEnumIterator; diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index 8112d784d1..a53f4728c5 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -3,16 +3,14 @@ use super::{ item_imgs::{animate_by_pulse, ItemImgs, ItemKey::Tool}, Show, CRITICAL_HP_COLOR, HP_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, XP_COLOR, }; -use crate::{ - i18n::Localization, - ui::{fonts::Fonts, ImageFrame, Tooltip, TooltipManager, Tooltipable}, -}; +use crate::ui::{fonts::Fonts, ImageFrame, Tooltip, TooltipManager, Tooltipable}; use conrod_core::{ color, image::Id, widget::{self, button, Button, Image, Rectangle, Text}, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, }; +use i18n::Localization; use client::{self, Client}; use common::comp::{ @@ -741,9 +739,9 @@ impl<'a> Widget for Diary<'a> { ); self.create_unlock_skill_button( Skill::General(EnergyIncrease), - self.imgs.stamina_plus_skill, + self.imgs.energy_plus_skill, state.skills_top_l[1], - "inc_stam", + "inc_energy", state.skill_general_stat_1, ui, &mut events, @@ -826,7 +824,7 @@ impl<'a> Widget for Diary<'a> { Skill::Roll(RollSkill::Cost), self.imgs.utility_cost_skill, state.skills_bot_l[1], - "roll_stamina", + "roll_energy", state.skill_general_roll_1, ui, &mut events, @@ -1701,7 +1699,7 @@ impl<'a> Widget for Diary<'a> { Skill::Staff(BRegen), self.imgs.magic_energy_regen_skill, state.skills_top_l[2], - "st_stamina_regen", + "st_energy_regen", state.skill_staff_basic_2, ui, &mut events, diff --git a/voxygen/src/hud/esc_menu.rs b/voxygen/src/hud/esc_menu.rs index 5573cf3ad6..92f989ad68 100644 --- a/voxygen/src/hud/esc_menu.rs +++ b/voxygen/src/hud/esc_menu.rs @@ -1,9 +1,10 @@ use super::{img_ids::Imgs, settings_window::SettingsTab, TEXT_COLOR}; -use crate::{i18n::Localization, ui::fonts::Fonts}; +use crate::ui::fonts::Fonts; use conrod_core::{ widget::{self, Button, Image}, widget_ids, Color, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; widget_ids! { struct Ids { diff --git a/voxygen/src/hud/group.rs b/voxygen/src/hud/group.rs index 74ee3ed8b2..506501fe2b 100644 --- a/voxygen/src/hud/group.rs +++ b/voxygen/src/hud/group.rs @@ -9,7 +9,6 @@ use super::{ use crate::{ game_input::GameInput, hud, - i18n::Localization, settings::Settings, ui::{fonts::Fonts, ImageFrame, Tooltip, TooltipManager, Tooltipable}, GlobalState, @@ -27,6 +26,7 @@ use conrod_core::{ widget::{self, Button, Image, Rectangle, Scrollbar, Text}, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; use specs::{saveload::MarkerAllocator, WorldExt}; widget_ids! { @@ -53,7 +53,7 @@ widget_ids! { member_panels_txt[], member_health[], member_health_decay[], - member_stam[], + member_energy[], buffs[], buff_timers[], dead_txt[], @@ -297,10 +297,10 @@ impl<'a> Widget for Group<'a> { .resize(group_size, &mut ui.widget_id_generator()); }) }; - if state.ids.member_stam.len() < group_size { + if state.ids.member_energy.len() < group_size { state.update(|s| { s.ids - .member_stam + .member_energy .resize(group_size, &mut ui.widget_id_generator()) }) }; @@ -485,12 +485,12 @@ impl<'a> Widget for Group<'a> { .set(state.ids.member_panels_txt[i], ui); if let Some(energy) = energy { let stam_perc = energy.current() as f64 / energy.maximum() as f64; - // Stamina + // Energy Image::new(self.imgs.bar_content) .w_h(100.0 * stam_perc, 8.0) .color(Some(STAMINA_COLOR)) .top_left_with_margins_on(state.ids.member_panels_bg[i], 26.0, 2.0) - .set(state.ids.member_stam[i], ui); + .set(state.ids.member_energy[i], ui); } if let Some(buffs) = buffs { // Limit displayed buffs to 11 diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 55eaa73b5a..a6c534c9de 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -187,7 +187,7 @@ image_ids! { // Skilltree Icons health_plus_skill: "voxygen.element.skills.skilltree.health_plus", - stamina_plus_skill: "voxygen.element.skills.skilltree.stamina_plus", + energy_plus_skill: "voxygen.element.skills.skilltree.energy_plus", unlock_axe_skill: "voxygen.element.skills.skilltree.unlock_axe", unlock_bow_skill: "voxygen.element.skills.skilltree.unlock_bow", unlock_hammer_skill: "voxygen.element.skills.skilltree.unlock_hammer", @@ -318,8 +318,8 @@ image_ids! { health_bg: "voxygen.element.ui.skillbar.health_bg", health_frame: "voxygen.element.ui.skillbar.health_frame", decayed_bg: "voxygen.element.ui.skillbar.decayed_bg", - stamina_bg: "voxygen.element.ui.skillbar.stamina_bg", - stamina_frame: "voxygen.element.ui.skillbar.stamina_frame", + energy_bg: "voxygen.element.ui.skillbar.energy_bg", + energy_frame: "voxygen.element.ui.skillbar.energy_frame", m1_ico: "voxygen.element.ui.generic.icons.m1", m2_ico: "voxygen.element.ui.generic.icons.m2", m_scroll_ico: "voxygen.element.ui.generic.icons.m_scroll", @@ -384,6 +384,7 @@ image_ids! { map_dif_4: "voxygen.element.ui.map.icons.dif_4", map_dif_5: "voxygen.element.ui.map.icons.dif_5", map_dif_6: "voxygen.element.ui.map.icons.dif_6", + map_dif_unknown: "voxygen.element.ui.map.icons.dif_unknown", mmap_site_town: "voxygen.element.ui.map.buttons.town", mmap_site_town_hover: "voxygen.element.ui.map.buttons.town_hover", mmap_site_town_bg: "voxygen.element.ui.map.buttons.town_bg", @@ -441,6 +442,10 @@ image_ids! { inv_frame: "voxygen.element.ui.bag.inv_frame", inv_frame_bag: "voxygen.element.ui.bag.inv_frame_bag", inv_bg_bag: "voxygen.element.ui.bag.inv_bg_bag", + player_inv_frame_bag: "voxygen.element.ui.bag.player_inv_frame_bag", + player_inv_bg_bag: "voxygen.element.ui.bag.player_inv_bg_bag", + inv_middle_bg_bag: "voxygen.element.ui.bag.inv_middle_bg_bag", + inv_middle_frame: "voxygen.element.ui.bag.inv_middle_frame", char_art: "voxygen.element.ui.bag.icons.character", inv_slot: "voxygen.element.ui.bag.buttons.inv_slot", inv_slot_grey: "voxygen.element.ui.bag.buttons.inv_slot_grey", @@ -453,6 +458,7 @@ image_ids! { inv_slot_sel: "voxygen.element.ui.bag.buttons.inv_slot_sel", scrollbar_bg: "voxygen.element.ui.generic.slider.scrollbar", scrollbar_bg_big: "voxygen.element.ui.generic.slider.scrollbar_1", + second_phase_scrollbar_bg: "voxygen.element.ui.bag.second_phase_scrollbar_bg", inv_tab_active: "voxygen.element.ui.bag.buttons.inv_tab_active", inv_tab_inactive: "voxygen.element.ui.bag.buttons.inv_tab_inactive", inv_tab_inactive_hover: "voxygen.element.ui.bag.buttons.inv_tab_inactive", @@ -476,7 +482,7 @@ image_ids! { mainhand_bg: "voxygen.element.ui.bag.backgrounds.mainhand", bag_bg: "voxygen.element.ui.bag.backgrounds.bag", offhand_bg: "voxygen.element.ui.bag.backgrounds.offhand", - stamina_ico: "voxygen.element.ui.bag.icons.stamina", + energy_ico: "voxygen.element.ui.bag.icons.energy", health_ico: "voxygen.element.ui.bag.icons.health", protection_ico: "voxygen.element.ui.bag.icons.protection", stun_res_ico: "voxygen.element.ui.bag.icons.stun_res", diff --git a/voxygen/src/hud/loot_scroller.rs b/voxygen/src/hud/loot_scroller.rs index 676291465b..53e88d6820 100644 --- a/voxygen/src/hud/loot_scroller.rs +++ b/voxygen/src/hud/loot_scroller.rs @@ -4,10 +4,7 @@ use super::{ item_imgs::ItemImgs, Show, Windows, TEXT_COLOR, }; -use crate::{ - i18n::Localization, - ui::{fonts::Fonts, ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable}, -}; +use crate::ui::{fonts::Fonts, ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable}; use client::Client; use common::comp::inventory::item::{ItemDef, MaterialStatManifest, Quality}; use conrod_core::{ @@ -16,6 +13,7 @@ use conrod_core::{ widget::{self, Image, List, Rectangle, Scrollbar, Text}, widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; use std::{collections::VecDeque, sync::Arc}; widget_ids! { diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index 3742f2565c..8db489b8ff 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -4,7 +4,6 @@ use super::{ TEXT_BG, TEXT_BLUE_COLOR, TEXT_COLOR, TEXT_GRAY_COLOR, TEXT_VELORITE, UI_HIGHLIGHT_0, UI_MAIN, }; use crate::{ - i18n::Localization, session::settings_change::{Interface as InterfaceChange, Interface::*}, ui::{fonts::Fonts, img_ids, ImageFrame, Tooltip, TooltipManager, Tooltipable}, GlobalState, @@ -17,6 +16,7 @@ use conrod_core::{ widget::{self, Button, Image, Rectangle, Text}, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; use specs::{saveload::MarkerAllocator, WorldExt}; use vek::*; @@ -787,15 +787,15 @@ impl<'a> Widget for Map<'a> { SiteKind::Tree => i18n.get("hud.map.tree"), }); let (difficulty, desc) = match &site.kind { - SiteKind::Town => (0, i18n.get("hud.map.town").to_string()), + SiteKind::Town => (None, i18n.get("hud.map.town").to_string()), SiteKind::Dungeon { difficulty } => ( - *difficulty, + Some(*difficulty), i18n.get("hud.map.difficulty_dungeon") - .replace("{difficulty}", difficulty.to_string().as_str()), + .replace("{difficulty}", (difficulty + 1).to_string().as_str()), ), - SiteKind::Castle => (0, i18n.get("hud.map.castle").to_string()), - SiteKind::Cave => (0, i18n.get("hud.map.cave").to_string()), - SiteKind::Tree => (0, i18n.get("hud.map.tree").to_string()), + SiteKind::Castle => (None, i18n.get("hud.map.castle").to_string()), + SiteKind::Cave => (None, i18n.get("hud.map.cave").to_string()), + SiteKind::Tree => (None, i18n.get("hud.map.tree").to_string()), }; let desc = desc + &get_site_economy(site_rich); let site_btn = Button::image(match &site.kind { @@ -828,12 +828,12 @@ impl<'a> Widget for Map<'a> { SiteKind::Town => TEXT_COLOR, SiteKind::Castle => TEXT_COLOR, SiteKind::Dungeon { .. } => match difficulty { - 0 => QUALITY_LOW, - 1 => QUALITY_COMMON, - 2 => QUALITY_MODERATE, - 3 => QUALITY_HIGH, - 4 => QUALITY_EPIC, - 5 => QUALITY_DEBUG, + Some(0) => QUALITY_LOW, + Some(1) => QUALITY_COMMON, + Some(2) => QUALITY_MODERATE, + Some(3) => QUALITY_HIGH, + Some(4) => QUALITY_EPIC, + Some(5) => QUALITY_DEBUG, _ => TEXT_COLOR, }, SiteKind::Cave => TEXT_COLOR, @@ -859,34 +859,40 @@ impl<'a> Widget for Map<'a> { // Difficulty from 0-6 // 0 = towns and places without a difficulty level if show_difficulty { - let size = 1.8; // Size factor for difficulty indicators + let rsize = zoom * 1.8; // Size factor for difficulty indicators let dif_img = Image::new(match difficulty { - 1 => self.imgs.map_dif_1, - 2 => self.imgs.map_dif_2, - 3 => self.imgs.map_dif_3, - 4 => self.imgs.map_dif_4, - 5 => self.imgs.map_dif_6, - _ => self.imgs.nothing, + Some(0) => self.imgs.map_dif_1, + Some(1) => self.imgs.map_dif_2, + Some(2) => self.imgs.map_dif_3, + Some(3) => self.imgs.map_dif_4, + Some(4) => self.imgs.map_dif_5, + Some(5) => self.imgs.map_dif_6, + Some(_) => self.imgs.map_dif_unknown, + None => self.imgs.nothing, }) .mid_top_with_margin_on(state.ids.mmap_site_icons[i], match difficulty { - 5 => -2.0 * zoom * size, - _ => -1.0 * zoom * size, + Some(0 | 1) => -1.0 * rsize, + Some(_) => -2.0 * rsize, + _ => -1.0 * rsize, }) .w(match difficulty { - 5 => 2.0 * zoom * size, - _ => 1.0 * zoom * size * difficulty as f64, + Some(0) => 1.0 * rsize, + Some(1 | 2 | 5) => 2.0 * rsize, + Some(_) => 3.0 * rsize, + _ => 1.0 * rsize, }) .h(match difficulty { - 5 => 2.0 * size * zoom, - _ => 1.0 * zoom * size, + Some(0 | 1) => 1.0 * rsize, + Some(_) => 2.0 * rsize, + _ => 1.0 * rsize, }) .color(Some(match difficulty { - 0 => QUALITY_LOW, - 1 => QUALITY_COMMON, - 2 => QUALITY_MODERATE, - 3 => QUALITY_HIGH, - 4 => QUALITY_EPIC, - 5 => QUALITY_DEBUG, + Some(0) => QUALITY_LOW, + Some(1) => QUALITY_COMMON, + Some(2) => QUALITY_MODERATE, + Some(3) => QUALITY_HIGH, + Some(4) => QUALITY_EPIC, + Some(5) => QUALITY_DEBUG, _ => TEXT_COLOR, })); match &site.kind { diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 754d3cbf3a..47f89059e0 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -688,6 +688,13 @@ impl<'a> Widget for MiniMap<'a> { Some(rpos) => rpos, None => continue, }; + let difficulty = match &site.kind { + SiteKind::Town => None, + SiteKind::Dungeon { difficulty } => Some(*difficulty), + SiteKind::Castle => None, + SiteKind::Cave => None, + SiteKind::Tree => None, + }; Image::new(match &site.kind { SiteKind::Town => self.imgs.mmap_site_town_bg, @@ -702,20 +709,14 @@ impl<'a> Widget for MiniMap<'a> { position::Relative::Scalar(rpos.y as f64), ) .w_h(20.0, 20.0) - .color(Some(match &site.kind { - SiteKind::Town => Color::Rgba(1.0, 1.0, 1.0, 0.0), - SiteKind::Castle => Color::Rgba(1.0, 1.0, 1.0, 0.0), - SiteKind::Dungeon { difficulty } => match difficulty { - 0 => QUALITY_LOW, - 1 => QUALITY_COMMON, - 2 => QUALITY_MODERATE, - 3 => QUALITY_HIGH, - 4 => QUALITY_EPIC, - 5 => QUALITY_DEBUG, - _ => Color::Rgba(1.0, 1.0, 1.0, 0.0), - }, - SiteKind::Cave => Color::Rgba(1.0, 1.0, 1.0, 0.0), - SiteKind::Tree => Color::Rgba(1.0, 1.0, 1.0, 0.0), + .color(Some(match difficulty { + Some(0) => QUALITY_LOW, + Some(1) => QUALITY_COMMON, + Some(2) => QUALITY_MODERATE, + Some(3) => QUALITY_HIGH, + Some(4) => QUALITY_EPIC, + Some(5) => QUALITY_DEBUG, + _ => Color::Rgba(1.0, 1.0, 1.0, 0.0), })) .set(state.ids.mmap_site_icons_bgs[i], ui); Image::new(match &site.kind { diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 66cf68cf19..e246975721 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1,3 +1,4 @@ +mod animation; mod bag; mod buffs; mod buttons; @@ -55,7 +56,6 @@ use crate::{ ecs::{comp as vcomp, comp::HpFloaterList}, game_input::GameInput, hud::{img_ids::ImgsRot, prompt_dialog::DialogOutcomeEvent}, - i18n::Localization, render::UiDrawer, scene::camera::{self, Camera}, session::{ @@ -74,7 +74,7 @@ use client::Client; use common::{ combat, comp::{ - self, + self, fluid_dynamics, inventory::trade_pricing::TradePricing, item::{tool::ToolKind, ItemDesc, MaterialStatManifest, Quality}, skills::{Skill, SkillGroupKind}, @@ -86,7 +86,7 @@ use common::{ terrain::{SpriteKind, TerrainChunk}, trade::{ReducedInventory, TradeAction}, uid::Uid, - util::srgba_to_linear, + util::{srgba_to_linear, Dir}, vol::RectRasterableVol, }; use common_base::{prof_span, span}; @@ -100,6 +100,7 @@ use conrod_core::{ widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, }; use hashbrown::{HashMap, HashSet}; +use i18n::Localization; use rand::Rng; use specs::{Entity as EcsEntity, Join, WorldExt}; use std::{ @@ -232,7 +233,10 @@ widget_ids! { ping, coordinates, velocity, + glide_ratio, + glide_aoe, orientation, + look_direction, loaded_distance, time, entity_count, @@ -316,6 +320,88 @@ widget_ids! { } } +/// Specifier to use with `Position::position` +/// Read its documentation for more +// TODO: extend as you need it +#[derive(Clone, Copy)] +pub enum PositionSpecifier { + MidBottomWithMarginOn(widget::Id, f64), + TopRightWithMarginsOn(widget::Id, f64, f64), + BottomRightWithMarginsOn(widget::Id, f64, f64), + BottomLeftWithMarginsOn(widget::Id, f64, f64), + RightFrom(widget::Id, f64), +} + +/// Trait which enables you to declare widget position +/// to use later on widget creation. +/// It is implemented for all widgets which are implement Positionable, +/// so you can easily change your code to use this method. +/// +/// Consider this example: +/// ```text +/// let slot1 = slot_maker +/// .fabricate(hotbar::Slot::One, [40.0; 2]) +/// .filled_slot(self.imgs.skillbar_slot) +/// .bottom_left_with_margins_on(state.ids.frame, 0.0, 0.0); +/// if condition { +/// call_slot1(slot1); +/// } else { +/// call_slot2(slot1); +/// } +/// let slot2 = slot_maker +/// .fabricate(hotbar::Slot::Two, [40.0; 2]) +/// .filled_slot(self.imgs.skillbar_slot) +/// .right_from(state.ids.slot1, slot_offset); +/// if condition { +/// call_slot1(slot2); +/// } else { +/// call_slot2(slot2); +/// } +/// ``` +/// Despite being identical, you can't easily deduplicate code +/// which uses slot1 and slot2 as they are calling methods to position itself. +/// This can be solved if you declare position and use it later like so +/// ```text +/// let slots = [ +/// (hotbar::Slot::One, BottomLeftWithMarginsOn(state.ids.frame, 0.0, 0.0)), +/// (hotbar::Slot::Two, RightFrom(state.ids.slot1, slot_offset)), +/// ]; +/// for (slot, pos) in slots { +/// let slot = slot_maker +/// .fabricate(slot, [40.0; 2]) +/// .filled_slot(self.imgs.skillbar_slot) +/// .position(pos); +/// if condition { +/// call_slot1(slot); +/// } else { +/// call_slot2(slot); +/// } +/// } +/// ``` +pub trait Position { + fn position(self, request: PositionSpecifier) -> Self; +} + +impl Position for W { + fn position(self, request: PositionSpecifier) -> Self { + match request { + PositionSpecifier::MidBottomWithMarginOn(other, margin) => { + self.mid_bottom_with_margin_on(other, margin) + }, + PositionSpecifier::TopRightWithMarginsOn(other, top, right) => { + self.top_right_with_margins_on(other, top, right) + }, + PositionSpecifier::BottomRightWithMarginsOn(other, bottom, right) => { + self.bottom_right_with_margins_on(other, bottom, right) + }, + PositionSpecifier::BottomLeftWithMarginsOn(other, bottom, left) => { + self.bottom_left_with_margins_on(other, bottom, left) + }, + PositionSpecifier::RightFrom(other, offset) => self.right_from(other, offset), + } + } +} + #[derive(Clone, Copy)] pub struct BuffInfo { kind: comp::BuffKind, @@ -358,6 +444,9 @@ pub struct DebugInfo { pub coordinates: Option, pub velocity: Option, pub ori: Option, + pub character_state: Option, + pub look_dir: Dir, + pub in_fluid: Option, pub num_chunks: u32, pub num_lights: u32, pub num_visible_chunks: u32, @@ -1061,7 +1150,7 @@ impl Hud { 1.0, 1.0, 1.0, - self.crosshair_opacity * global_state.settings.interface.crosshair_transp, + self.crosshair_opacity * global_state.settings.interface.crosshair_opacity, ))) .set(self.ids.crosshair_outer, ui_widgets); Image::new(self.imgs.crosshair_inner) @@ -1576,8 +1665,7 @@ impl Hud { .set(overitem_id, ui_widgets); } else if let Some(sprite) = block.get_sprite() { overitem::Overitem::new( - format!("{:?}", sprite).into(), /* TODO: A better way to generate text - * for this */ + get_sprite_desc(sprite, i18n), overitem::TEXT_COLOR, pos.distance_squared(player_pos), &self.fonts, @@ -2067,6 +2155,8 @@ impl Hud { } // Display debug window. + // TODO: + // Make it use i18n keys. if let Some(debug_info) = debug_info { prof_span!("debug info"); // Alpha Version @@ -2109,15 +2199,31 @@ impl Hud { .font_size(self.fonts.cyri.scale(14)) .set(self.ids.coordinates, ui_widgets); // Player's velocity - let velocity_text = match debug_info.velocity { - Some(velocity) => format!( - "Velocity: ({:.1}, {:.1}, {:.1}) [{:.1} u/s]", - velocity.0.x, - velocity.0.y, - velocity.0.z, - velocity.0.magnitude() - ), - None => "Player has no Vel component".to_owned(), + let (velocity_text, glide_ratio_text) = match debug_info.velocity { + Some(velocity) => { + let velocity = velocity.0; + let velocity_text = format!( + "Velocity: ({:.1}, {:.1}, {:.1}) [{:.1} u/s]", + velocity.x, + velocity.y, + velocity.z, + velocity.magnitude() + ); + let horizontal_velocity = velocity.xy().magnitude(); + let dz = velocity.z; + // don't divide by zero + let glide_ratio_text = if dz.abs() > 0.0001 { + format!("Glide Ratio: {:.1}", (-1.0) * (horizontal_velocity / dz)) + } else { + "Glide Ratio: Altitude is constant".to_owned() + }; + + (velocity_text, glide_ratio_text) + }, + None => { + let err = "Player has no Vel component"; + (err.to_owned(), err.to_owned()) + }, }; Text::new(&velocity_text) .color(TEXT_COLOR) @@ -2125,23 +2231,54 @@ impl Hud { .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) .set(self.ids.velocity, ui_widgets); + Text::new(&glide_ratio_text) + .color(TEXT_COLOR) + .down_from(self.ids.velocity, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .set(self.ids.glide_ratio, ui_widgets); + let glide_angle_text = angle_of_attack_text( + debug_info.in_fluid, + debug_info.velocity, + debug_info.character_state.as_ref(), + ); + Text::new(&glide_angle_text) + .color(TEXT_COLOR) + .down_from(self.ids.glide_ratio, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .set(self.ids.glide_aoe, ui_widgets); // Player's orientation vector let orientation_text = match debug_info.ori { Some(ori) => { - let look_dir = ori.look_dir(); + let orientation = ori.look_dir(); format!( - "Orientation: ({:.1}, {:.1}, {:.1})", - look_dir.x, look_dir.y, look_dir.z, + "Orientation: ({:.2}, {:.2}, {:.2})", + orientation.x, orientation.y, orientation.z, ) }, None => "Player has no Ori component".to_owned(), }; Text::new(&orientation_text) .color(TEXT_COLOR) - .down_from(self.ids.velocity, 5.0) + .down_from(self.ids.glide_aoe, 5.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) .set(self.ids.orientation, ui_widgets); + let look_dir_text = { + let look_vec = debug_info.look_dir.to_vec(); + + format!( + "Look Direction: ({:.2}, {:.2}, {:.2})", + look_vec.x, look_vec.y, look_vec.z, + ) + }; + Text::new(&look_dir_text) + .color(TEXT_COLOR) + .down_from(self.ids.orientation, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .set(self.ids.look_direction, ui_widgets); // Loaded distance Text::new(&format!( "View distance: {:.2} blocks ({:.2} chunks)", @@ -2149,7 +2286,7 @@ impl Hud { client.loaded_distance() / TerrainChunk::RECT_SIZE.x as f32, )) .color(TEXT_COLOR) - .down_from(self.ids.orientation, 5.0) + .down_from(self.ids.look_direction, 5.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) .set(self.ids.loaded_distance, ui_widgets); @@ -3573,6 +3710,12 @@ impl Hud { self.show.debug = global_state.settings.interface.toggle_debug; true }, + #[cfg(feature = "egui-ui")] + GameInput::ToggleEguiDebug if state => { + global_state.settings.interface.toggle_egui_debug = + !global_state.settings.interface.toggle_egui_debug; + true + }, GameInput::ToggleChat if state => { global_state.settings.interface.toggle_chat = !global_state.settings.interface.toggle_chat; @@ -3861,7 +4004,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> &str BuffKind::Potion { .. } => localized_strings.get("buff.title.potion"), BuffKind::CampfireHeal { .. } => localized_strings.get("buff.title.campfire_heal"), BuffKind::IncreaseMaxHealth { .. } => localized_strings.get("buff.title.IncreaseMaxHealth"), - BuffKind::IncreaseMaxEnergy { .. } => localized_strings.get("buff.title.staminaup"), + BuffKind::IncreaseMaxEnergy { .. } => localized_strings.get("buff.title.energyup"), BuffKind::Invulnerability => localized_strings.get("buff.title.invulnerability"), BuffKind::ProtectingWard => localized_strings.get("buff.title.protectingward"), BuffKind::Frenzied => localized_strings.get("buff.title.frenzied"), @@ -3911,6 +4054,21 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz } } +pub fn get_sprite_desc(sprite: SpriteKind, localized_strings: &Localization) -> Cow { + let i18n_key = match sprite { + SpriteKind::Anvil => "hud.crafting.anvil", + SpriteKind::Cauldron => "hud.crafting.cauldron", + SpriteKind::CookingPot => "hud.crafting.cooking_pot", + SpriteKind::CraftingBench => "hud.crafting.crafting_bench", + SpriteKind::Forge => "hud.crafting.forge", + SpriteKind::Loom => "hud.crafting.loom", + SpriteKind::SpinningWheel => "hud.crafting.spinning_wheel", + SpriteKind::TanningRack => "hud.crafting.tanning_rack", + sprite => return Cow::Owned(format!("{:?}", sprite)), + }; + Cow::Borrowed(localized_strings.get(i18n_key)) +} + pub fn get_buff_time(buff: BuffInfo) -> String { if let Some(dur) = buff.dur { format!("{:.0}s", dur.as_secs_f32()) @@ -3918,3 +4076,39 @@ pub fn get_buff_time(buff: BuffInfo) -> String { "".to_string() } } + +pub fn angle_of_attack_text( + fluid: Option, + velocity: Option, + character_state: Option<&comp::CharacterState>, +) -> String { + use comp::CharacterState; + + let glider_ori = if let Some(CharacterState::Glide(data)) = character_state { + data.ori + } else { + return "Angle of Attack: Not gliding".to_owned(); + }; + + let fluid = if let Some(fluid) = fluid { + fluid + } else { + return "Angle of Attack: Not in fluid".to_owned(); + }; + + let velocity = if let Some(velocity) = velocity { + velocity + } else { + return "Angle of Attack: Player has no vel component".to_owned(); + }; + let rel_flow = fluid.relative_flow(&velocity).0; + let v_sq = rel_flow.magnitude_squared(); + + if v_sq.abs() > 0.0001 { + let rel_flow_dir = Dir::new(rel_flow / v_sq.sqrt()); + let aoe = fluid_dynamics::angle_of_attack(&glider_ori, &rel_flow_dir); + format!("Angle of Attack: {:.1}", aoe.to_degrees()) + } else { + "Angle of Attack: Not moving".to_owned() + } +} diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index 4d58d7aef0..c0c191e50d 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -5,7 +5,6 @@ use super::{ }; use crate::{ hud::{get_buff_image, get_buff_info}, - i18n::Localization, settings::InterfaceSettings, ui::{fonts::Fonts, Ingameable}, }; @@ -16,6 +15,7 @@ use conrod_core::{ widget::{self, Image, Rectangle, Text}, widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; const MAX_BUBBLE_WIDTH: f64 = 250.0; widget_ids! { diff --git a/voxygen/src/hud/overitem.rs b/voxygen/src/hud/overitem.rs index 368350389b..7522e04f06 100644 --- a/voxygen/src/hud/overitem.rs +++ b/voxygen/src/hud/overitem.rs @@ -1,6 +1,5 @@ use crate::{ game_input::GameInput, - i18n::Localization, settings::ControlSettings, ui::{fonts::Fonts, Ingameable}, }; @@ -9,6 +8,7 @@ use conrod_core::{ widget::{self, RoundedRectangle, Text}, widget_ids, Color, Colorable, Positionable, Widget, WidgetCommon, }; +use i18n::Localization; use std::borrow::Cow; use keyboard_keynames::key_layout::KeyLayout; diff --git a/voxygen/src/hud/popup.rs b/voxygen/src/hud/popup.rs index 843bc57baf..a87feaecb2 100644 --- a/voxygen/src/hud/popup.rs +++ b/voxygen/src/hud/popup.rs @@ -1,11 +1,12 @@ use super::Show; -use crate::{i18n::Localization, ui::fonts::Fonts}; +use crate::ui::fonts::Fonts; use client::{self, Client}; use common_net::msg::Notification; use conrod_core::{ widget::{self, Text}, widget_ids, Color, Colorable, Positionable, Widget, WidgetCommon, }; +use i18n::Localization; use std::{collections::VecDeque, time::Instant}; widget_ids! { diff --git a/voxygen/src/hud/prompt_dialog.rs b/voxygen/src/hud/prompt_dialog.rs index 599eca1953..769100fc86 100644 --- a/voxygen/src/hud/prompt_dialog.rs +++ b/voxygen/src/hud/prompt_dialog.rs @@ -2,7 +2,6 @@ use super::{img_ids::Imgs, TEXT_COLOR, UI_HIGHLIGHT_0}; use crate::{ game_input::GameInput, hud::{Event, PromptDialogSettings}, - i18n::LocalizationHandle, settings::Settings, ui::fonts::Fonts, }; @@ -10,6 +9,7 @@ use conrod_core::{ widget::{self, Button, Image, Text}, widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::LocalizationHandle; use keyboard_keynames::key_layout::KeyLayout; widget_ids! { diff --git a/voxygen/src/hud/settings_window/chat.rs b/voxygen/src/hud/settings_window/chat.rs index 60c540d8fd..4c54399464 100644 --- a/voxygen/src/hud/settings_window/chat.rs +++ b/voxygen/src/hud/settings_window/chat.rs @@ -2,7 +2,6 @@ use super::{RESET_BUTTONS_HEIGHT, RESET_BUTTONS_WIDTH}; use crate::{ hud::{img_ids::Imgs, ChatTab, Show, TEXT_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN}, - i18n::Localization, session::settings_change::{Chat as ChatChange, Chat::*}, settings::chat::MAX_CHAT_TABS, ui::{fonts::Fonts, ImageSlider, ToggleButton}, @@ -14,6 +13,7 @@ use conrod_core::{ widget::{self, Button, DropDownList, Image, Rectangle, Text, TextEdit}, widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; use std::cmp::Ordering; widget_ids! { @@ -150,7 +150,7 @@ impl<'a> Widget for Chat<'a> { // Chat Transp Text::new( self.localized_strings - .get("hud.settings.background_transparency"), + .get("hud.settings.background_opacity"), ) .down_from(state.ids.general_txt, 20.0) .font_size(self.fonts.cyri.scale(14)) @@ -158,7 +158,7 @@ impl<'a> Widget for Chat<'a> { .color(TEXT_COLOR) .set(state.ids.transp_text, ui); if let Some(new_val) = ImageSlider::continuous( - chat_settings.chat_transp, + chat_settings.chat_opacity, 0.0, 0.9, self.imgs.slider_indicator, diff --git a/voxygen/src/hud/settings_window/controls.rs b/voxygen/src/hud/settings_window/controls.rs index ee9b129ecd..005ba006b6 100644 --- a/voxygen/src/hud/settings_window/controls.rs +++ b/voxygen/src/hud/settings_window/controls.rs @@ -3,7 +3,6 @@ use super::{RESET_BUTTONS_HEIGHT, RESET_BUTTONS_WIDTH}; use crate::{ game_input::GameInput, hud::{img_ids::Imgs, ERROR_COLOR, TEXT_BIND_CONFLICT_COLOR, TEXT_COLOR}, - i18n::Localization, session::settings_change::{Control as ControlChange, Control::*}, ui::fonts::Fonts, GlobalState, @@ -14,6 +13,7 @@ use conrod_core::{ widget::{self, Button, Rectangle, Scrollbar, Text}, widget_ids, Borderable, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; use strum::IntoEnumIterator; widget_ids! { @@ -123,12 +123,19 @@ impl<'a> Widget for Controls<'a> { let (key_string, key_color) = if self.global_state.window.remapping_keybindings == Some(game_input) { ( - String::from(self.localized_strings.get("hud.settings.awaitingkey")), + self.localized_strings + .get("hud.settings.awaitingkey") + .to_owned(), TEXT_COLOR, ) } else if let Some(key) = controls.get_binding(game_input) { ( - key.display_string(key_layout), + format!( + "{} {}", + key.display_string(key_layout), + key.display_shortened(key_layout) + .map_or("".to_owned(), |short| format!("({})", short)) + ), if controls.has_conflicting_bindings(key) { TEXT_BIND_CONFLICT_COLOR } else { @@ -137,7 +144,9 @@ impl<'a> Widget for Controls<'a> { ) } else { ( - String::from(self.localized_strings.get("hud.settings.unbound")), + self.localized_strings + .get("hud.settings.unbound") + .to_owned(), ERROR_COLOR, ) }; diff --git a/voxygen/src/hud/settings_window/gameplay.rs b/voxygen/src/hud/settings_window/gameplay.rs index bb059f528d..010c71739b 100644 --- a/voxygen/src/hud/settings_window/gameplay.rs +++ b/voxygen/src/hud/settings_window/gameplay.rs @@ -2,7 +2,6 @@ use super::{RESET_BUTTONS_HEIGHT, RESET_BUTTONS_WIDTH}; use crate::{ hud::{img_ids::Imgs, PressBehavior, MENU_BG, TEXT_COLOR}, - i18n::Localization, session::settings_change::{Gameplay as GameplayChange, Gameplay::*}, ui::{fonts::Fonts, ImageSlider, ToggleButton}, GlobalState, @@ -13,6 +12,7 @@ use conrod_core::{ widget::{self, Button, DropDownList, Rectangle, Scrollbar, Text}, widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; widget_ids! { struct Ids { diff --git a/voxygen/src/hud/settings_window/interface.rs b/voxygen/src/hud/settings_window/interface.rs index f39def8fa6..8ccc2c8d34 100644 --- a/voxygen/src/hud/settings_window/interface.rs +++ b/voxygen/src/hud/settings_window/interface.rs @@ -4,7 +4,6 @@ use crate::{ hud::{ img_ids::Imgs, BarNumbers, BuffPosition, CrosshairType, ShortcutNumbers, Show, TEXT_COLOR, }, - i18n::Localization, session::settings_change::{Interface as InterfaceChange, Interface::*}, ui::{fonts::Fonts, ImageSlider, ScaleMode, ToggleButton}, GlobalState, @@ -15,6 +14,7 @@ use conrod_core::{ widget::{self, Button, Image, Rectangle, Scrollbar, Text}, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; widget_ids! { struct Ids{ @@ -159,7 +159,7 @@ impl<'a> Widget for Interface<'a> { .set(state.ids.window_scrollbar, ui); let bar_values = self.global_state.settings.interface.bar_numbers; - let crosshair_transp = self.global_state.settings.interface.crosshair_transp; + let crosshair_opacity = self.global_state.settings.interface.crosshair_opacity; let crosshair_type = self.global_state.settings.interface.crosshair_type; let ui_scale = self.global_state.settings.interface.ui_scale; @@ -441,7 +441,7 @@ impl<'a> Widget for Interface<'a> { 1.0, 1.0, 1.0, - self.global_state.settings.interface.crosshair_transp, + self.global_state.settings.interface.crosshair_opacity, ))) .graphics_for(state.ids.ch_1_bg) .set(state.ids.crosshair_outer_1, ui); @@ -484,7 +484,7 @@ impl<'a> Widget for Interface<'a> { 1.0, 1.0, 1.0, - self.global_state.settings.interface.crosshair_transp, + self.global_state.settings.interface.crosshair_opacity, ))) .graphics_for(state.ids.ch_2_bg) .set(state.ids.crosshair_outer_2, ui); @@ -527,7 +527,7 @@ impl<'a> Widget for Interface<'a> { 1.0, 1.0, 1.0, - self.global_state.settings.interface.crosshair_transp, + self.global_state.settings.interface.crosshair_opacity, ))) .graphics_for(state.ids.ch_3_bg) .set(state.ids.crosshair_outer_3, ui); @@ -544,7 +544,7 @@ impl<'a> Widget for Interface<'a> { .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) .set(state.ids.ch_title, ui); - Text::new(self.localized_strings.get("hud.settings.transparency")) + Text::new(self.localized_strings.get("hud.settings.opacity")) .right_from(state.ids.ch_3_bg, 20.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) @@ -552,7 +552,7 @@ impl<'a> Widget for Interface<'a> { .set(state.ids.ch_transp_text, ui); if let Some(new_val) = ImageSlider::continuous( - crosshair_transp, + crosshair_opacity, 0.0, 1.0, self.imgs.slider_indicator, @@ -568,7 +568,7 @@ impl<'a> Widget for Interface<'a> { events.push(CrosshairTransp(new_val)); } - Text::new(&format!("{:.2}", crosshair_transp,)) + Text::new(&format!("{:.2}", crosshair_opacity,)) .right_from(state.ids.ch_transp_slider, 8.0) .font_size(self.fonts.cyri.scale(14)) .graphics_for(state.ids.ch_transp_slider) diff --git a/voxygen/src/hud/settings_window/language.rs b/voxygen/src/hud/settings_window/language.rs index e1cb5787c7..5745b81bca 100644 --- a/voxygen/src/hud/settings_window/language.rs +++ b/voxygen/src/hud/settings_window/language.rs @@ -1,6 +1,5 @@ use crate::{ hud::{img_ids::Imgs, TEXT_COLOR}, - i18n::{list_localizations, Localization}, session::settings_change::{Language as LanguageChange, Language::*}, ui::{fonts::Fonts, ToggleButton}, GlobalState, @@ -10,6 +9,7 @@ use conrod_core::{ widget::{self, Button, Rectangle, Scrollbar, Text}, widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::{list_localizations, Localization}; widget_ids! { struct Ids { diff --git a/voxygen/src/hud/settings_window/mod.rs b/voxygen/src/hud/settings_window/mod.rs index 73de93697a..e72101755f 100644 --- a/voxygen/src/hud/settings_window/mod.rs +++ b/voxygen/src/hud/settings_window/mod.rs @@ -8,7 +8,6 @@ mod video; use crate::{ hud::{img_ids::Imgs, Show, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN}, - i18n::Localization, session::settings_change::SettingsChange, ui::fonts::Fonts, GlobalState, @@ -18,6 +17,7 @@ use conrod_core::{ widget::{self, Button, Image, Rectangle, Text}, widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; use strum::IntoEnumIterator; use strum_macros::EnumIter; diff --git a/voxygen/src/hud/settings_window/sound.rs b/voxygen/src/hud/settings_window/sound.rs index f563b48f9e..96fda99e6f 100644 --- a/voxygen/src/hud/settings_window/sound.rs +++ b/voxygen/src/hud/settings_window/sound.rs @@ -2,7 +2,6 @@ use super::{RESET_BUTTONS_HEIGHT, RESET_BUTTONS_WIDTH}; use crate::{ hud::{img_ids::Imgs, TEXT_COLOR}, - i18n::Localization, session::settings_change::{Audio as AudioChange, Audio::*}, ui::{fonts::Fonts, ImageSlider}, GlobalState, @@ -13,25 +12,28 @@ use conrod_core::{ widget::{self, Button, Rectangle, Scrollbar, Text}, widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; widget_ids! { struct Ids { window, window_r, window_scrollbar, - reset_sound_button, - audio_volume_slider, - audio_volume_text, - sfx_volume_slider, - sfx_volume_text, - master_volume_slider, master_volume_text, + master_volume_slider, master_volume_number, - inactive_master_volume_slider, inactive_master_volume_text, + inactive_master_volume_slider, inactive_master_volume_number, - audio_device_list, - audio_device_text, + music_volume_text, + music_volume_slider, + music_volume_number, + sfx_volume_text, + sfx_volume_slider, + sfx_volume_number, + //audio_device_list, + //audio_device_text, + reset_sound_button, } } @@ -99,19 +101,14 @@ impl<'a> Widget for Sound<'a> { .rgba(0.33, 0.33, 0.33, 1.0) .set(state.ids.window_scrollbar, ui); - // Master Volume ---------------------------------------------------- - let master_volume = self.global_state.settings.audio.master_volume; - let master_val = format!("{:2.0}", master_volume * 100.0); - let inactive_master_volume_perc = - self.global_state.settings.audio.inactive_master_volume_perc; - let inactive_val = format!("{:2.0}%", inactive_master_volume_perc * 100.0); + // Master Volume Text::new(self.localized_strings.get("hud.settings.master_volume")) .top_left_with_margins_on(state.ids.window, 10.0, 10.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) .set(state.ids.master_volume_text, ui); - + // Master Volume Slider if let Some(new_val) = ImageSlider::continuous( self.global_state.settings.audio.master_volume, 0.0, @@ -128,13 +125,18 @@ impl<'a> Widget for Sound<'a> { { events.push(AdjustMasterVolume(new_val)); } - Text::new(&master_val) - .right_from(state.ids.master_volume_slider, 8.0) - .font_size(self.fonts.cyri.scale(14)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.master_volume_number, ui); - // Master Volume (inactive window) ---------------------------------- + // Master Volume Number + Text::new(&format!( + "{:2.0}%", + self.global_state.settings.audio.master_volume * 100.0 + )) + .right_from(state.ids.master_volume_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.master_volume_number, ui); + + // Master Volume (inactive window) Text::new( self.localized_strings .get("hud.settings.inactive_master_volume_perc"), @@ -144,7 +146,7 @@ impl<'a> Widget for Sound<'a> { .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) .set(state.ids.inactive_master_volume_text, ui); - + // Master Volume (inactive window) Slider if let Some(new_val) = ImageSlider::continuous( self.global_state.settings.audio.inactive_master_volume_perc, 0.0, @@ -161,20 +163,25 @@ impl<'a> Widget for Sound<'a> { { events.push(AdjustInactiveMasterVolume(new_val)); } - Text::new(&inactive_val) - .right_from(state.ids.inactive_master_volume_slider, 8.0) - .font_size(self.fonts.cyri.scale(14)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.inactive_master_volume_number, ui); - // Music Volume ----------------------------------------------------- + // Master Volume (inactive window) Number + Text::new(&format!( + "{:2.0}%", + self.global_state.settings.audio.inactive_master_volume_perc * 100.0 + )) + .right_from(state.ids.inactive_master_volume_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.inactive_master_volume_number, ui); + + // Music Volume Text::new(self.localized_strings.get("hud.settings.music_volume")) .down_from(state.ids.inactive_master_volume_slider, 10.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) - .set(state.ids.audio_volume_text, ui); - + .set(state.ids.music_volume_text, ui); + // Music Volume Slider if let Some(new_val) = ImageSlider::continuous( self.global_state.settings.audio.music_volume, 0.0, @@ -183,26 +190,36 @@ impl<'a> Widget for Sound<'a> { self.imgs.slider, ) .w_h(104.0, 22.0) - .down_from(state.ids.audio_volume_text, 10.0) + .down_from(state.ids.music_volume_text, 10.0) .track_breadth(12.0) .slider_length(10.0) .pad_track((5.0, 5.0)) - .set(state.ids.audio_volume_slider, ui) + .set(state.ids.music_volume_slider, ui) { events.push(AdjustMusicVolume(new_val)); } + // Music Volume Number + Text::new(&format!( + "{:2.0}%", + self.global_state.settings.audio.music_volume * 100.0 + )) + .right_from(state.ids.music_volume_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.music_volume_number, ui); - // SFX Volume ------------------------------------------------------- + // SFX Volume Text::new( self.localized_strings .get("hud.settings.sound_effect_volume"), ) - .down_from(state.ids.audio_volume_slider, 10.0) + .down_from(state.ids.music_volume_slider, 10.0) .font_size(self.fonts.cyri.scale(14)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) .set(state.ids.sfx_volume_text, ui); - + // SFX Volume Slider if let Some(new_val) = ImageSlider::continuous( self.global_state.settings.audio.sfx_volume, 0.0, @@ -219,6 +236,16 @@ impl<'a> Widget for Sound<'a> { { events.push(AdjustSfxVolume(new_val)); } + // SFX Volume Number + Text::new(&format!( + "{:2.0}%", + self.global_state.settings.audio.sfx_volume * 100.0 + )) + .right_from(state.ids.sfx_volume_slider, 8.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.sfx_volume_number, ui); // Audio Device Selector // -------------------------------------------- diff --git a/voxygen/src/hud/settings_window/video.rs b/voxygen/src/hud/settings_window/video.rs index c638602f30..e577ecb6d6 100644 --- a/voxygen/src/hud/settings_window/video.rs +++ b/voxygen/src/hud/settings_window/video.rs @@ -5,7 +5,6 @@ use crate::{ img_ids::Imgs, CRITICAL_HP_COLOR, HP_COLOR, LOW_HP_COLOR, MENU_BG, STAMINA_COLOR, TEXT_COLOR, }, - i18n::Localization, render::{ AaMode, CloudMode, FluidMode, LightingMode, PresentMode, RenderMode, ShadowMapMode, ShadowMode, UpscaleMode, @@ -23,6 +22,7 @@ use conrod_core::{ widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; use core::convert::TryFrom; +use i18n::Localization; use itertools::Itertools; use std::iter::once; diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index fa367a3ab0..d1777b47af 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -7,8 +7,7 @@ use super::{ }; use crate::{ game_input::GameInput, - hud::ComboFloater, - i18n::Localization, + hud::{ComboFloater, Position, PositionSpecifier}, ui::{ fonts::Fonts, slot::{ContentSize, SlotMaker}, @@ -17,6 +16,7 @@ use crate::{ }, GlobalState, }; +use i18n::Localization; use client::{self, Client}; use common::comp::{ @@ -31,8 +31,9 @@ use common::comp::{ use conrod_core::{ color, widget::{self, Button, Image, Rectangle, Text}, - widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon, + widget_ids, Color, Colorable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, }; +use std::cmp; use vek::*; widget_ids! { @@ -57,8 +58,8 @@ widget_ids! { frame, bg_health, frame_health, - bg_stamina, - frame_stamina, + bg_energy, + frame_energy, m1_ico, m2_ico, // Level @@ -75,12 +76,12 @@ widget_ids! { hp_txt_bg, hp_txt, decay_overlay, - // Stamina-Bar - stamina_alignment, - stamina_filling, - stamina_txt_alignment, - stamina_txt_bg, - stamina_txt, + // Energy-Bar + energy_alignment, + energy_filling, + energy_txt_alignment, + energy_txt_bg, + energy_txt, // Combo Counter combo_align, combo_bg, @@ -131,6 +132,116 @@ widget_ids! { } } +#[derive(Clone, Copy)] +struct SlotEntry { + slot: hotbar::Slot, + widget_id: widget::Id, + position: PositionSpecifier, + game_input: GameInput, + shortcut_position: PositionSpecifier, + shortcut_position_bg: PositionSpecifier, + shortcut_widget_ids: (widget::Id, widget::Id), +} + +fn slot_entries(state: &State, slot_offset: f64) -> [SlotEntry; 10] { + use PositionSpecifier::*; + + [ + // 1th - 5th slots + SlotEntry { + slot: hotbar::Slot::One, + widget_id: state.ids.slot1, + position: BottomLeftWithMarginsOn(state.ids.frame, 0.0, 0.0), + game_input: GameInput::Slot1, + shortcut_position: BottomLeftWithMarginsOn(state.ids.slot1_text_bg, 1.0, 1.0), + shortcut_position_bg: TopRightWithMarginsOn(state.ids.slot1, 3.0, 5.0), + shortcut_widget_ids: (state.ids.slot1_text, state.ids.slot1_text_bg), + }, + SlotEntry { + slot: hotbar::Slot::Two, + widget_id: state.ids.slot2, + position: RightFrom(state.ids.slot1, slot_offset), + game_input: GameInput::Slot2, + shortcut_position: BottomLeftWithMarginsOn(state.ids.slot2_text_bg, 1.0, 1.0), + shortcut_position_bg: TopRightWithMarginsOn(state.ids.slot2, 3.0, 5.0), + shortcut_widget_ids: (state.ids.slot2_text, state.ids.slot2_text_bg), + }, + SlotEntry { + slot: hotbar::Slot::Three, + widget_id: state.ids.slot3, + position: RightFrom(state.ids.slot2, slot_offset), + game_input: GameInput::Slot3, + shortcut_position: BottomLeftWithMarginsOn(state.ids.slot3_text_bg, 1.0, 1.0), + shortcut_position_bg: TopRightWithMarginsOn(state.ids.slot3, 3.0, 5.0), + shortcut_widget_ids: (state.ids.slot3_text, state.ids.slot3_text_bg), + }, + SlotEntry { + slot: hotbar::Slot::Four, + widget_id: state.ids.slot4, + position: RightFrom(state.ids.slot3, slot_offset), + game_input: GameInput::Slot4, + shortcut_position: BottomLeftWithMarginsOn(state.ids.slot4_text_bg, 1.0, 1.0), + shortcut_position_bg: TopRightWithMarginsOn(state.ids.slot4, 3.0, 5.0), + shortcut_widget_ids: (state.ids.slot4_text, state.ids.slot4_text_bg), + }, + SlotEntry { + slot: hotbar::Slot::Five, + widget_id: state.ids.slot5, + position: RightFrom(state.ids.slot4, slot_offset), + game_input: GameInput::Slot5, + shortcut_position: BottomLeftWithMarginsOn(state.ids.slot5_text_bg, 1.0, 1.0), + shortcut_position_bg: TopRightWithMarginsOn(state.ids.slot5, 3.0, 5.0), + shortcut_widget_ids: (state.ids.slot5_text, state.ids.slot5_text_bg), + }, + // 6th - 10th slots + SlotEntry { + slot: hotbar::Slot::Six, + widget_id: state.ids.slot6, + position: RightFrom(state.ids.m2_slot_bg, slot_offset), + game_input: GameInput::Slot6, + shortcut_position: BottomLeftWithMarginsOn(state.ids.slot6_text_bg, 1.0, 1.0), + shortcut_position_bg: TopRightWithMarginsOn(state.ids.slot6, 3.0, 5.0), + shortcut_widget_ids: (state.ids.slot6_text, state.ids.slot6_text_bg), + }, + SlotEntry { + slot: hotbar::Slot::Seven, + widget_id: state.ids.slot7, + position: RightFrom(state.ids.slot6, slot_offset), + game_input: GameInput::Slot7, + shortcut_position: BottomLeftWithMarginsOn(state.ids.slot7_text_bg, 1.0, 1.0), + shortcut_position_bg: TopRightWithMarginsOn(state.ids.slot7, 3.0, 5.0), + shortcut_widget_ids: (state.ids.slot7_text, state.ids.slot7_text_bg), + }, + SlotEntry { + slot: hotbar::Slot::Eight, + widget_id: state.ids.slot8, + position: RightFrom(state.ids.slot7, slot_offset), + game_input: GameInput::Slot8, + shortcut_position: BottomLeftWithMarginsOn(state.ids.slot8_text_bg, 1.0, 1.0), + shortcut_position_bg: TopRightWithMarginsOn(state.ids.slot8, 3.0, 5.0), + shortcut_widget_ids: (state.ids.slot8_text, state.ids.slot8_text_bg), + }, + SlotEntry { + slot: hotbar::Slot::Nine, + widget_id: state.ids.slot9, + position: RightFrom(state.ids.slot8, slot_offset), + game_input: GameInput::Slot9, + shortcut_position: BottomLeftWithMarginsOn(state.ids.slot9_text_bg, 1.0, 1.0), + shortcut_position_bg: TopRightWithMarginsOn(state.ids.slot9, 3.0, 5.0), + shortcut_widget_ids: (state.ids.slot9_text, state.ids.slot9_text_bg), + }, + SlotEntry { + slot: hotbar::Slot::Ten, + widget_id: state.ids.slot10, + position: RightFrom(state.ids.slot9, slot_offset), + game_input: GameInput::Slot10, + shortcut_position: BottomLeftWithMarginsOn(state.ids.slot10_text_bg, 1.0, 1.0), + shortcut_position_bg: TopRightWithMarginsOn(state.ids.slot10, 3.0, 5.0), + shortcut_widget_ids: (state.ids.slot10_text, state.ids.slot10_text_bg), + }, + ] +} + #[derive(WidgetCommon)] pub struct Skillbar<'a> { client: &'a Client, @@ -205,101 +316,70 @@ impl<'a> Skillbar<'a> { combo, } } -} - -pub struct State { - ids: Ids, -} - -impl<'a> Widget for Skillbar<'a> { - type Event = (); - type State = State; - type Style = (); - - fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { - State { - ids: Ids::new(id_gen), - } - } - - fn style(&self) -> Self::Style {} - - fn update(self, args: widget::UpdateArgs) -> Self::Event { - common_base::prof_span!("Skillbar::update"); - let widget::UpdateArgs { state, ui, .. } = args; - - let max_hp = self.health.base_max().max(self.health.maximum()); - - let mut hp_percentage = self.health.current() as f64 / max_hp as f64 * 100.0; - let mut energy_percentage = - self.energy.current() as f64 / self.energy.maximum() as f64 * 100.0; - if self.health.is_dead { - hp_percentage = 0.0; - energy_percentage = 0.0; - }; - - let bar_values = self.global_state.settings.interface.bar_numbers; - let shortcuts = self.global_state.settings.interface.shortcut_numbers; - - let hp_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8; //Animation timer - let crit_hp_color: Color = Color::Rgba(0.79, 0.19, 0.17, hp_ani); + fn show_death_message(&self, state: &State, ui: &mut UiCell) { let localized_strings = self.localized_strings; let key_layout = &self.global_state.window.key_layout; - let slot_offset = 3.0; - - // Death message - if self.health.is_dead { - if let Some(key) = self - .global_state - .settings - .controls - .get_binding(GameInput::Respawn) - { - Text::new(localized_strings.get("hud.you_died")) - .middle_of(ui.window) - .font_size(self.fonts.cyri.scale(50)) - .font_id(self.fonts.cyri.conrod_id) - .color(Color::Rgba(0.0, 0.0, 0.0, 1.0)) - .set(state.ids.death_message_1_bg, ui); - Text::new( - &localized_strings - .get("hud.press_key_to_respawn") - .replace("{key}", key.display_string(key_layout).as_str()), - ) - .mid_bottom_with_margin_on(state.ids.death_message_1_bg, -120.0) - .font_size(self.fonts.cyri.scale(30)) + if let Some(key) = self + .global_state + .settings + .controls + .get_binding(GameInput::Respawn) + { + Text::new(localized_strings.get("hud.you_died")) + .middle_of(ui.window) + .font_size(self.fonts.cyri.scale(50)) .font_id(self.fonts.cyri.conrod_id) .color(Color::Rgba(0.0, 0.0, 0.0, 1.0)) - .set(state.ids.death_message_2_bg, ui); - Text::new(localized_strings.get("hud.you_died")) - .bottom_left_with_margins_on(state.ids.death_message_1_bg, 2.0, 2.0) - .font_size(self.fonts.cyri.scale(50)) - .font_id(self.fonts.cyri.conrod_id) - .color(CRITICAL_HP_COLOR) - .set(state.ids.death_message_1, ui); - Text::new( - &localized_strings - .get("hud.press_key_to_respawn") - .replace("{key}", key.display_string(key_layout).as_str()), - ) - .bottom_left_with_margins_on(state.ids.death_message_2_bg, 2.0, 2.0) - .font_size(self.fonts.cyri.scale(30)) + .set(state.ids.death_message_1_bg, ui); + Text::new( + &localized_strings + .get("hud.press_key_to_respawn") + .replace("{key}", key.display_string(key_layout).as_str()), + ) + .mid_bottom_with_margin_on(state.ids.death_message_1_bg, -120.0) + .font_size(self.fonts.cyri.scale(30)) + .font_id(self.fonts.cyri.conrod_id) + .color(Color::Rgba(0.0, 0.0, 0.0, 1.0)) + .set(state.ids.death_message_2_bg, ui); + Text::new(localized_strings.get("hud.you_died")) + .bottom_left_with_margins_on(state.ids.death_message_1_bg, 2.0, 2.0) + .font_size(self.fonts.cyri.scale(50)) .font_id(self.fonts.cyri.conrod_id) .color(CRITICAL_HP_COLOR) - .set(state.ids.death_message_2, ui); - } + .set(state.ids.death_message_1, ui); + Text::new( + &localized_strings + .get("hud.press_key_to_respawn") + .replace("{key}", key.display_string(key_layout).as_str()), + ) + .bottom_left_with_margins_on(state.ids.death_message_2_bg, 2.0, 2.0) + .font_size(self.fonts.cyri.scale(30)) + .font_id(self.fonts.cyri.conrod_id) + .color(CRITICAL_HP_COLOR) + .set(state.ids.death_message_2, ui); } - // Skillbar - // Alignment and BG - let alignment_size = 40.0 * 12.0 + slot_offset * 11.0; - Rectangle::fill_with([alignment_size, 80.0], color::TRANSPARENT) - .mid_bottom_with_margin_on(ui.window, 10.0) - .set(state.ids.frame, ui); - // Health and Stamina bar + } + + fn show_stat_bars(&self, state: &State, ui: &mut UiCell) { + let (hp_percentage, energy_percentage): (f64, f64) = if self.health.is_dead { + (0.0, 0.0) + } else { + let max_hp = cmp::max(self.health.base_max(), self.health.maximum()) as f64; + let current_hp = self.health.current() as f64; + ( + current_hp / max_hp * 100.0, + (self.energy.fraction() * 100.0).into(), + ) + }; + + // Animation timer + let hp_ani = (self.pulse * 4.0/* speed factor */).cos() * 0.5 + 0.8; + let crit_hp_color: Color = Color::Rgba(0.79, 0.19, 0.17, hp_ani); + let bar_values = self.global_state.settings.interface.bar_numbers; let show_health = self.health.current() != self.health.maximum(); - let show_stamina = self.energy.current() != self.energy.maximum(); + let show_energy = self.energy.current() != self.energy.maximum(); let decayed_health = 1.0 - self.health.maximum() as f64 / self.health.base_max() as f64; if show_health && !self.health.is_dead || decayed_health > 0.0 { @@ -344,48 +424,59 @@ impl<'a> Widget for Skillbar<'a> { .middle_of(state.ids.bg_health) .set(state.ids.frame_health, ui); } - if show_stamina && !self.health.is_dead { + if show_energy && !self.health.is_dead { let offset = if show_health || decayed_health > 0.0 { 34.0 } else { 1.0 }; - Image::new(self.imgs.stamina_bg) + Image::new(self.imgs.energy_bg) .w_h(323.0, 16.0) .mid_top_with_margin_on(state.ids.frame, -offset) - .set(state.ids.bg_stamina, ui); + .set(state.ids.bg_energy, ui); Rectangle::fill_with([319.0, 10.0], color::TRANSPARENT) - .top_left_with_margins_on(state.ids.bg_stamina, 2.0, 2.0) - .set(state.ids.stamina_alignment, ui); + .top_left_with_margins_on(state.ids.bg_energy, 2.0, 2.0) + .set(state.ids.energy_alignment, ui); Image::new(self.imgs.bar_content) .w_h(319.0 * energy_percentage / 100.0, 10.0) .color(Some(STAMINA_COLOR)) - .top_left_with_margins_on(state.ids.stamina_alignment, 0.0, 0.0) - .set(state.ids.stamina_filling, ui); - Image::new(self.imgs.stamina_frame) + .top_left_with_margins_on(state.ids.energy_alignment, 0.0, 0.0) + .set(state.ids.energy_filling, ui); + Image::new(self.imgs.energy_frame) .w_h(323.0, 16.0) .color(Some(UI_HIGHLIGHT_0)) - .middle_of(state.ids.bg_stamina) - .set(state.ids.frame_stamina, ui); + .middle_of(state.ids.bg_energy) + .set(state.ids.frame_energy, ui); } // Bar Text - // Values - if let BarNumbers::Values = bar_values { - let mut hp_txt = format!( - "{}/{}", - (self.health.current() / 10).max(1) as u32, /* Don't show 0 health for - * living players */ - (self.health.maximum() / 10) as u32 - ); - let mut energy_txt = format!( - "{}/{}", - (self.energy.current() / 10) as u32, - (self.energy.maximum() / 10) as u32 - ); - if self.health.is_dead { - hp_txt = self.localized_strings.get("hud.group.dead").to_string(); - energy_txt = self.localized_strings.get("hud.group.dead").to_string(); - }; + let bar_text = if self.health.is_dead { + Some(( + self.localized_strings.get("hud.group.dead").to_owned(), + self.localized_strings.get("hud.group.dead").to_owned(), + )) + } else if let BarNumbers::Values = bar_values { + Some(( + format!( + "{}/{}", + (self.health.current() / 10).max(1) as u32, /* Don't show 0 health for + * living players */ + (self.health.maximum() / 10) as u32 + ), + format!( + "{}/{}", + (self.energy.current() / 10) as u32, + (self.energy.maximum() / 10) as u32 + ), + )) + } else if let BarNumbers::Percent = bar_values { + Some(( + format!("{}%", hp_percentage as u32), + format!("{}%", energy_percentage as u32), + )) + } else { + None + }; + if let Some((hp_txt, energy_txt)) = bar_text { Text::new(&hp_txt) .middle_of(state.ids.frame_health) .font_size(self.fonts.cyri.scale(12)) @@ -398,54 +489,29 @@ impl<'a> Widget for Skillbar<'a> { .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) .set(state.ids.hp_txt, ui); + Text::new(&energy_txt) - .middle_of(state.ids.frame_stamina) + .middle_of(state.ids.frame_energy) .font_size(self.fonts.cyri.scale(12)) .font_id(self.fonts.cyri.conrod_id) .color(Color::Rgba(0.0, 0.0, 0.0, 1.0)) - .set(state.ids.stamina_txt_bg, ui); + .set(state.ids.energy_txt_bg, ui); Text::new(&energy_txt) - .bottom_left_with_margins_on(state.ids.stamina_txt_bg, 2.0, 2.0) + .bottom_left_with_margins_on(state.ids.energy_txt_bg, 2.0, 2.0) .font_size(self.fonts.cyri.scale(12)) .font_id(self.fonts.cyri.conrod_id) .color(TEXT_COLOR) - .set(state.ids.stamina_txt, ui); + .set(state.ids.energy_txt, ui); } - //Percentages - if let BarNumbers::Percent = bar_values { - let mut hp_txt = format!("{}%", hp_percentage as u32); - let mut energy_txt = format!("{}", energy_percentage as u32); - if self.health.is_dead { - hp_txt = self.localized_strings.get("hud.group.dead").to_string(); - energy_txt = self.localized_strings.get("hud.group.dead").to_string(); - }; - Text::new(&hp_txt) - .middle_of(state.ids.frame_health) - .font_size(self.fonts.cyri.scale(12)) - .font_id(self.fonts.cyri.conrod_id) - .color(Color::Rgba(0.0, 0.0, 0.0, 1.0)) - .set(state.ids.hp_txt_bg, ui); - Text::new(&hp_txt) - .bottom_left_with_margins_on(state.ids.hp_txt_bg, 2.0, 2.0) - .font_size(self.fonts.cyri.scale(12)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.hp_txt, ui); - Text::new(&energy_txt) - .middle_of(state.ids.frame_stamina) - .font_size(self.fonts.cyri.scale(12)) - .font_id(self.fonts.cyri.conrod_id) - .color(Color::Rgba(0.0, 0.0, 0.0, 1.0)) - .set(state.ids.stamina_txt_bg, ui); - Text::new(&energy_txt) - .bottom_left_with_margins_on(state.ids.stamina_txt_bg, 2.0, 2.0) - .font_size(self.fonts.cyri.scale(12)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.stamina_txt, ui); - } - // Slots - let content_source = (self.hotbar, self.inventory, self.energy, self.skillset); // TODO: avoid this + } + + fn show_slotbar(&mut self, state: &State, ui: &mut UiCell, slot_offset: f64) { + let shortcuts = self.global_state.settings.interface.shortcut_numbers; + let key_layout = &self.global_state.window.key_layout; + + // TODO: avoid this + let content_source = (self.hotbar, self.inventory, self.energy, self.skillset); + let image_source = (self.item_imgs, self.imgs); let mut slot_maker = SlotMaker { // TODO: is a separate image needed for the frame? @@ -515,141 +581,111 @@ impl<'a> Widget for Skillbar<'a> { .desc_text_color(TEXT_COLOR); let slot_content = |slot| { - content_source - .0 - .get(slot) - .and_then(|content| match content { - hotbar::SlotContents::Inventory(i) => content_source.1.get(i), - _ => None, - }) + let (hotbar, inventory, ..) = content_source; + hotbar.get(slot).and_then(|content| match content { + hotbar::SlotContents::Inventory(i) => inventory.get(i), + _ => None, + }) }; // Helper let tooltip_text = |slot| { - content_source - .0 - .get(slot) - .and_then(|content| match content { - hotbar::SlotContents::Inventory(i) => content_source - .1 - .get(i) - .map(|item| (item.name(), item.description())), - hotbar::SlotContents::Ability3 => content_source - .1 - .equipped(EquipSlot::ActiveMainhand) - .map(|i| i.kind()) - .and_then(|kind| match kind { - ItemKind::Tool(Tool { kind, .. }) => ability_description(kind), - _ => None, - }), - hotbar::SlotContents::Ability4 => { - let hands = |equip_slot| match content_source - .1 + let (hotbar, inventory, ..) = content_source; + hotbar.get(slot).and_then(|content| match content { + hotbar::SlotContents::Inventory(i) => inventory + .get(i) + .map(|item| (item.name(), item.description())), + hotbar::SlotContents::Ability3 => inventory + .equipped(EquipSlot::ActiveMainhand) + .map(|i| i.kind()) + .and_then(|kind| match kind { + ItemKind::Tool(Tool { kind, .. }) => ability_description(kind), + _ => None, + }), + hotbar::SlotContents::Ability4 => { + let hands = |equip_slot| match inventory.equipped(equip_slot).map(|i| i.kind()) + { + Some(ItemKind::Tool(tool)) => Some(tool.hands), + _ => None, + }; + + let active_tool_hands = hands(EquipSlot::ActiveMainhand); + let second_tool_hands = hands(EquipSlot::ActiveOffhand); + + let equip_slot = match (active_tool_hands, second_tool_hands) { + (Some(Hands::Two), _) => Some(EquipSlot::ActiveMainhand), + (Some(_), Some(Hands::One)) => Some(EquipSlot::ActiveOffhand), + (Some(Hands::One), _) => Some(EquipSlot::ActiveMainhand), + (None, Some(_)) => Some(EquipSlot::ActiveOffhand), + (_, _) => None, + }; + + equip_slot.and_then(|equip_slot| { + inventory .equipped(equip_slot) .map(|i| i.kind()) - { - Some(ItemKind::Tool(tool)) => Some(tool.hands), - _ => None, - }; - - let active_tool_hands = hands(EquipSlot::ActiveMainhand); - let second_tool_hands = hands(EquipSlot::ActiveOffhand); - - let equip_slot = match (active_tool_hands, second_tool_hands) { - (Some(Hands::Two), _) => Some(EquipSlot::ActiveMainhand), - (Some(_), Some(Hands::One)) => Some(EquipSlot::ActiveOffhand), - (Some(Hands::One), _) => Some(EquipSlot::ActiveMainhand), - (None, Some(_)) => Some(EquipSlot::ActiveOffhand), - (_, _) => None, - }; - - if let Some(equip_slot) = equip_slot { - content_source - .1 - .equipped(equip_slot) - .map(|i| i.kind()) - .and_then(|kind| match kind { - ItemKind::Tool(Tool { kind, .. }) => ability_description(kind), - _ => None, - }) - } else { - None - } - }, - }) + .and_then(|kind| match kind { + ItemKind::Tool(Tool { kind, .. }) => ability_description(kind), + _ => None, + }) + }) + }, + }) }; - // Slot 1-5 - // Slot 1 + slot_maker.empty_slot = self.imgs.skillbar_slot; slot_maker.selected_slot = self.imgs.skillbar_slot; - let slot = slot_maker - .fabricate(hotbar::Slot::One, [40.0; 2]) - .filled_slot(self.imgs.skillbar_slot) - .bottom_left_with_margins_on(state.ids.frame, 0.0, 0.0); - if let Some(item) = slot_content(hotbar::Slot::One) { - slot.with_item_tooltip(self.item_tooltip_manager, item, &None, &item_tooltip) - .set(state.ids.slot1, ui); - } else if let Some((title, desc)) = tooltip_text(hotbar::Slot::One) { - slot.with_tooltip(self.tooltip_manager, title, desc, &tooltip, TEXT_COLOR) - .set(state.ids.slot1, ui); - } else { - slot.set(state.ids.slot1, ui); - } - // Slot 2 - let slot = slot_maker - .fabricate(hotbar::Slot::Two, [40.0; 2]) - .filled_slot(self.imgs.skillbar_slot) - .right_from(state.ids.slot1, slot_offset); - if let Some(item) = slot_content(hotbar::Slot::Two) { - slot.with_item_tooltip(self.item_tooltip_manager, item, &None, &item_tooltip) - .set(state.ids.slot2, ui); - } else if let Some((title, desc)) = tooltip_text(hotbar::Slot::Two) { - slot.with_tooltip(self.tooltip_manager, title, desc, &tooltip, TEXT_COLOR) - .set(state.ids.slot2, ui); - } else { - slot.set(state.ids.slot2, ui); - } - // Slot 3 - let slot = slot_maker - .fabricate(hotbar::Slot::Three, [40.0; 2]) - .filled_slot(self.imgs.skillbar_slot) - .right_from(state.ids.slot2, slot_offset); - if let Some(item) = slot_content(hotbar::Slot::Three) { - slot.with_item_tooltip(self.item_tooltip_manager, item, &None, &item_tooltip) - .set(state.ids.slot3, ui); - } else if let Some((title, desc)) = tooltip_text(hotbar::Slot::Three) { - slot.with_tooltip(self.tooltip_manager, title, desc, &tooltip, TEXT_COLOR) - .set(state.ids.slot3, ui); - } else { - slot.set(state.ids.slot3, ui); - } - // Slot 4 - let slot = slot_maker - .fabricate(hotbar::Slot::Four, [40.0; 2]) - .filled_slot(self.imgs.skillbar_slot) - .right_from(state.ids.slot3, slot_offset); - if let Some(item) = slot_content(hotbar::Slot::Four) { - slot.with_item_tooltip(self.item_tooltip_manager, item, &None, &item_tooltip) - .set(state.ids.slot4, ui); - } else if let Some((title, desc)) = tooltip_text(hotbar::Slot::Four) { - slot.with_tooltip(self.tooltip_manager, title, desc, &tooltip, TEXT_COLOR) - .set(state.ids.slot4, ui); - } else { - slot.set(state.ids.slot4, ui); - } - // Slot 5 - let slot = slot_maker - .fabricate(hotbar::Slot::Five, [40.0; 2]) - .filled_slot(self.imgs.skillbar_slot) - .right_from(state.ids.slot4, slot_offset); - if let Some(item) = slot_content(hotbar::Slot::Five) { - slot.with_item_tooltip(self.item_tooltip_manager, item, &None, &item_tooltip) - .set(state.ids.slot5, ui); - } else if let Some((title, desc)) = tooltip_text(hotbar::Slot::Five) { - slot.with_tooltip(self.tooltip_manager, title, desc, &tooltip, TEXT_COLOR) - .set(state.ids.slot5, ui); - } else { - slot.set(state.ids.slot5, ui); + + let slots = slot_entries(state, slot_offset); + for entry in slots { + let slot = slot_maker + .fabricate(entry.slot, [40.0; 2]) + .filled_slot(self.imgs.skillbar_slot) + .position(entry.position); + // if there is an item attached, show item tooltip + if let Some(item) = slot_content(entry.slot) { + slot.with_item_tooltip(self.item_tooltip_manager, item, &None, &item_tooltip) + .set(entry.widget_id, ui); + // if we can gather some text to display, show it + } else if let Some((title, desc)) = tooltip_text(entry.slot) { + slot.with_tooltip(self.tooltip_manager, title, desc, &tooltip, TEXT_COLOR) + .set(entry.widget_id, ui); + // if not, just set slot + } else { + slot.set(entry.widget_id, ui); + } + + // shortcuts + if let ShortcutNumbers::On = shortcuts { + if let Some(key) = &self + .global_state + .settings + .controls + .get_binding(entry.game_input) + { + let position = entry.shortcut_position; + let position_bg = entry.shortcut_position_bg; + let (id, id_bg) = entry.shortcut_widget_ids; + + let key_desc = key + .display_shortened(key_layout) + .unwrap_or_else(|| key.display_string(key_layout)); + // shortcut text + Text::new(&key_desc) + .position(position) + .font_size(self.fonts.cyri.scale(8)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(id, ui); + // shortcut background + Text::new(&key_desc) + .position(position_bg) + .font_size(self.fonts.cyri.scale(8)) + .font_id(self.fonts.cyri.conrod_id) + .color(BLACK) + .set(id_bg, ui); + } + } } // Slot M1 Image::new(self.imgs.skillbar_slot) @@ -749,271 +785,7 @@ impl<'a> Widget for Skillbar<'a> { } }) .set(state.ids.m2_content, ui); - // Slot 6-10 - // Slot 6 - slot_maker.empty_slot = self.imgs.skillbar_slot; - slot_maker.selected_slot = self.imgs.skillbar_slot; - let slot = slot_maker - .fabricate(hotbar::Slot::Six, [40.0; 2]) - .filled_slot(self.imgs.skillbar_slot) - .right_from(state.ids.m2_slot_bg, slot_offset); - if let Some(item) = slot_content(hotbar::Slot::Six) { - slot.with_item_tooltip(self.item_tooltip_manager, item, &None, &item_tooltip) - .set(state.ids.slot6, ui); - } else if let Some((title, desc)) = tooltip_text(hotbar::Slot::Six) { - slot.with_tooltip(self.tooltip_manager, title, desc, &tooltip, TEXT_COLOR) - .set(state.ids.slot6, ui); - } else { - slot.set(state.ids.slot6, ui); - } - // Slot 7 - let slot = slot_maker - .fabricate(hotbar::Slot::Seven, [40.0; 2]) - .filled_slot(self.imgs.skillbar_slot) - .right_from(state.ids.slot6, slot_offset); - if let Some(item) = slot_content(hotbar::Slot::Seven) { - slot.with_item_tooltip(self.item_tooltip_manager, item, &None, &item_tooltip) - .set(state.ids.slot7, ui); - } else if let Some((title, desc)) = tooltip_text(hotbar::Slot::Seven) { - slot.with_tooltip(self.tooltip_manager, title, desc, &tooltip, TEXT_COLOR) - .set(state.ids.slot7, ui); - } else { - slot.set(state.ids.slot7, ui); - } - // Slot 8 - let slot = slot_maker - .fabricate(hotbar::Slot::Eight, [40.0; 2]) - .filled_slot(self.imgs.skillbar_slot) - .right_from(state.ids.slot7, slot_offset); - if let Some(item) = slot_content(hotbar::Slot::Eight) { - slot.with_item_tooltip(self.item_tooltip_manager, item, &None, &item_tooltip) - .set(state.ids.slot8, ui); - } else if let Some((title, desc)) = tooltip_text(hotbar::Slot::Eight) { - slot.with_tooltip(self.tooltip_manager, title, desc, &tooltip, TEXT_COLOR) - .set(state.ids.slot8, ui); - } else { - slot.set(state.ids.slot8, ui); - } - // Slot 9 - let slot = slot_maker - .fabricate(hotbar::Slot::Nine, [40.0; 2]) - .filled_slot(self.imgs.skillbar_slot) - .right_from(state.ids.slot8, slot_offset); - if let Some(item) = slot_content(hotbar::Slot::Nine) { - slot.with_item_tooltip(self.item_tooltip_manager, item, &None, &item_tooltip) - .set(state.ids.slot9, ui); - } else if let Some((title, desc)) = tooltip_text(hotbar::Slot::Nine) { - slot.with_tooltip(self.tooltip_manager, title, desc, &tooltip, TEXT_COLOR) - .set(state.ids.slot9, ui); - } else { - slot.set(state.ids.slot9, ui); - } - // Quickslot - slot_maker.empty_slot = self.imgs.skillbar_slot; - slot_maker.selected_slot = self.imgs.skillbar_slot; - let slot = slot_maker - .fabricate(hotbar::Slot::Ten, [40.0; 2]) - .filled_slot(self.imgs.skillbar_slot) - .right_from(state.ids.slot9, slot_offset); - if let Some((title, desc)) = tooltip_text(hotbar::Slot::Ten) { - slot.with_tooltip(self.tooltip_manager, title, desc, &tooltip, TEXT_COLOR) - .set(state.ids.slot10, ui); - } else { - slot.set(state.ids.slot10, ui); - } - // Shortcuts - if let ShortcutNumbers::On = shortcuts { - if let Some(slot1) = &self - .global_state - .settings - .controls - .get_binding(GameInput::Slot1) - { - Text::new(slot1.display_string(key_layout).as_str()) - .top_right_with_margins_on(state.ids.slot1, 3.0, 5.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(BLACK) - .set(state.ids.slot1_text_bg, ui); - Text::new(slot1.display_string(key_layout).as_str()) - .bottom_left_with_margins_on(state.ids.slot1_text_bg, 1.0, 1.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.slot1_text, ui); - } - if let Some(slot2) = &self - .global_state - .settings - .controls - .get_binding(GameInput::Slot2) - { - Text::new(slot2.display_string(key_layout).as_str()) - .top_right_with_margins_on(state.ids.slot2, 3.0, 5.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(BLACK) - .set(state.ids.slot2_text_bg, ui); - Text::new(slot2.display_string(key_layout).as_str()) - .bottom_left_with_margins_on(state.ids.slot2_text_bg, 1.0, 1.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.slot2_text, ui); - } - if let Some(slot3) = &self - .global_state - .settings - .controls - .get_binding(GameInput::Slot3) - { - Text::new(slot3.display_string(key_layout).as_str()) - .top_right_with_margins_on(state.ids.slot3, 3.0, 5.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(BLACK) - .set(state.ids.slot3_text_bg, ui); - Text::new(slot3.display_string(key_layout).as_str()) - .bottom_left_with_margins_on(state.ids.slot3_text_bg, 1.0, 1.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.slot3_text, ui); - } - if let Some(slot4) = &self - .global_state - .settings - .controls - .get_binding(GameInput::Slot4) - { - Text::new(slot4.display_string(key_layout).as_str()) - .top_right_with_margins_on(state.ids.slot4, 3.0, 5.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(BLACK) - .set(state.ids.slot4_text_bg, ui); - Text::new(slot4.display_string(key_layout).as_str()) - .bottom_left_with_margins_on(state.ids.slot4_text_bg, 1.0, 1.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.slot4_text, ui); - } - if let Some(slot5) = &self - .global_state - .settings - .controls - .get_binding(GameInput::Slot5) - { - Text::new(slot5.display_string(key_layout).as_str()) - .top_right_with_margins_on(state.ids.slot5, 3.0, 5.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(BLACK) - .set(state.ids.slot5_text_bg, ui); - Text::new(slot5.display_string(key_layout).as_str()) - .bottom_left_with_margins_on(state.ids.slot5_text_bg, 1.0, 1.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.slot5_text, ui); - } - if let Some(slot6) = &self - .global_state - .settings - .controls - .get_binding(GameInput::Slot6) - { - Text::new(slot6.display_string(key_layout).as_str()) - .top_right_with_margins_on(state.ids.slot6, 3.0, 5.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(BLACK) - .set(state.ids.slot6_text_bg, ui); - Text::new(slot6.display_string(key_layout).as_str()) - .bottom_right_with_margins_on(state.ids.slot6_text_bg, 1.0, 1.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.slot6_text, ui); - } - if let Some(slot7) = &self - .global_state - .settings - .controls - .get_binding(GameInput::Slot7) - { - Text::new(slot7.display_string(key_layout).as_str()) - .top_right_with_margins_on(state.ids.slot7, 3.0, 5.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(BLACK) - .set(state.ids.slot7_text_bg, ui); - Text::new(slot7.display_string(key_layout).as_str()) - .bottom_right_with_margins_on(state.ids.slot7_text_bg, 1.0, 1.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.slot7_text, ui); - } - if let Some(slot8) = &self - .global_state - .settings - .controls - .get_binding(GameInput::Slot8) - { - Text::new(slot8.display_string(key_layout).as_str()) - .top_right_with_margins_on(state.ids.slot8, 3.0, 5.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(BLACK) - .set(state.ids.slot8_text_bg, ui); - Text::new(slot8.display_string(key_layout).as_str()) - .bottom_right_with_margins_on(state.ids.slot8_text_bg, 1.0, 1.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.slot8_text, ui); - } - if let Some(slot9) = &self - .global_state - .settings - .controls - .get_binding(GameInput::Slot9) - { - Text::new(slot9.display_string(key_layout).as_str()) - .top_right_with_margins_on(state.ids.slot9, 3.0, 5.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(BLACK) - .set(state.ids.slot9_text_bg, ui); - Text::new(slot9.display_string(key_layout).as_str()) - .bottom_right_with_margins_on(state.ids.slot9_text_bg, 1.0, 1.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.slot9_text, ui); - } - if let Some(slot10) = &self - .global_state - .settings - .controls - .get_binding(GameInput::Slot10) - { - Text::new(slot10.display_string(key_layout).as_str()) - .top_right_with_margins_on(state.ids.slot10, 3.0, 5.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(BLACK) - .set(state.ids.slot10_text_bg, ui); - Text::new(slot10.display_string(key_layout).as_str()) - .bottom_right_with_margins_on(state.ids.slot10_text_bg, 1.0, 1.0) - .font_size(self.fonts.cyri.scale(8)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.slot10_text, ui); - } - }; + // M1 and M2 icons Image::new(self.imgs.m1_ico) .w_h(16.0, 18.0) @@ -1023,74 +795,137 @@ impl<'a> Widget for Skillbar<'a> { .w_h(16.0, 18.0) .mid_bottom_with_margin_on(state.ids.m2_content, -11.0) .set(state.ids.m2_ico, ui); + } - // Combo Counter - if let Some(combo) = self.combo { - if combo.combo > 0 { - let combo_txt = format!("{} Combo", combo.combo); - let combo_cnt = combo.combo as f32; - let time_since_last_update = comp::combo::COMBO_DECAY_START - combo.timer; - let alpha = (1.0 - time_since_last_update * 0.2).min(1.0) as f32; - let fnt_col = Color::Rgba( - // White -> Yellow -> Red text color gradient depending on count - (1.0 - combo_cnt / (combo_cnt + 20.0)).max(0.79), - (1.0 - combo_cnt / (combo_cnt + 80.0)).max(0.19), - (1.0 - combo_cnt / (combo_cnt + 5.0)).max(0.17), - alpha, - ); + fn show_combo_counter(&self, combo: ComboFloater, state: &State, ui: &mut UiCell) { + if combo.combo > 0 { + let combo_txt = format!("{} Combo", combo.combo); + let combo_cnt = combo.combo as f32; + let time_since_last_update = comp::combo::COMBO_DECAY_START - combo.timer; + let alpha = (1.0 - time_since_last_update * 0.2).min(1.0) as f32; + let fnt_col = Color::Rgba( + // White -> Yellow -> Red text color gradient depending on count + (1.0 - combo_cnt / (combo_cnt + 20.0)).max(0.79), + (1.0 - combo_cnt / (combo_cnt + 80.0)).max(0.19), + (1.0 - combo_cnt / (combo_cnt + 5.0)).max(0.17), + alpha, + ); + // Increase size for higher counts, + // "flash" on update by increasing the font size by 2. + let fnt_size = ((14.0 + combo.timer as f32 * 0.8).min(30.0)) as u32 + + if (time_since_last_update) < 0.1 { 2 } else { 0 }; - let fnt_size = ((14.0 + combo.timer as f32 * 0.8).min(30.0)) as u32 - + if (time_since_last_update) < 0.1 { 2 } else { 0 }; // Increase size for higher counts, "flash" on update by increasing the font size by 2 - Rectangle::fill_with([10.0, 10.0], color::TRANSPARENT) - .middle_of(ui.window) - .set(state.ids.combo_align, ui); - Text::new(combo_txt.as_str()) - .mid_bottom_with_margin_on( - state.ids.combo_align, - -350.0 + time_since_last_update * -8.0, - ) - .font_size(self.fonts.cyri.scale(fnt_size)) - .font_id(self.fonts.cyri.conrod_id) - .color(Color::Rgba(0.0, 0.0, 0.0, alpha)) - .set(state.ids.combo_bg, ui); - Text::new(combo_txt.as_str()) - .bottom_right_with_margins_on(state.ids.combo_bg, 1.0, 1.0) - .font_size(self.fonts.cyri.scale(fnt_size)) - .font_id(self.fonts.cyri.conrod_id) - .color(fnt_col) - .set(state.ids.combo, ui); - } + Rectangle::fill_with([10.0, 10.0], color::TRANSPARENT) + .middle_of(ui.window) + .set(state.ids.combo_align, ui); + + Text::new(combo_txt.as_str()) + .mid_bottom_with_margin_on( + state.ids.combo_align, + -350.0 + time_since_last_update * -8.0, + ) + .font_size(self.fonts.cyri.scale(fnt_size)) + .font_id(self.fonts.cyri.conrod_id) + .color(Color::Rgba(0.0, 0.0, 0.0, alpha)) + .set(state.ids.combo_bg, ui); + Text::new(combo_txt.as_str()) + .bottom_right_with_margins_on(state.ids.combo_bg, 1.0, 1.0) + .font_size(self.fonts.cyri.scale(fnt_size)) + .font_id(self.fonts.cyri.conrod_id) + .color(fnt_col) + .set(state.ids.combo, ui); } } } +pub struct State { + ids: Ids, +} + +impl<'a> Widget for Skillbar<'a> { + type Event = (); + type State = State; + type Style = (); + + fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { + State { + ids: Ids::new(id_gen), + } + } + + fn style(&self) -> Self::Style {} + + fn update(mut self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Skillbar::update"); + let widget::UpdateArgs { state, ui, .. } = args; + + let slot_offset = 3.0; + + // Death message + if self.health.is_dead { + self.show_death_message(state, ui); + } + + // Skillbar + // Alignment and BG + let alignment_size = 40.0 * 12.0 + slot_offset * 11.0; + Rectangle::fill_with([alignment_size, 80.0], color::TRANSPARENT) + .mid_bottom_with_margin_on(ui.window, 10.0) + .set(state.ids.frame, ui); + + // Health and Energy bar + self.show_stat_bars(state, ui); + + // Slots + self.show_slotbar(state, ui, slot_offset); + + // Combo Counter + if let Some(combo) = self.combo { + self.show_combo_counter(combo, state, ui); + } + } +} + +#[rustfmt::skip] fn ability_description(tool: &ToolKind) -> Option<(&str, &str)> { match tool { ToolKind::Hammer => Some(( "Smash of Doom", - "\nAn AOE attack with knockback. \nLeaps to position of cursor.", + "\n\ + An AOE attack with knockback.\n\ + Leaps to position of cursor.", + )), + ToolKind::Axe => Some(( + "Axe Jump", + "\n\ + A jump with the slashing leap to position of cursor.", )), - ToolKind::Axe => Some(("Spin Leap", "\nA slashing running spin leap.")), ToolKind::Staff => Some(( - "Firebomb", - "\nWhirls a big fireball into the air. \nExplodes the ground and does\na big amount \ - of damage", + "Ring of Fire", + "\n\ + Ignites the ground with fiery shockwave.", )), ToolKind::Sword => Some(( "Whirlwind", - "\nMove forward while spinning with \n your sword.", + "\n\ + Move forward while spinning with your sword.", )), ToolKind::Bow => Some(( "Burst", - "\nLaunches a burst of arrows at the top \nof a running leap.", - )), - ToolKind::Debug => Some(( - "Possessing Arrow", - "\nShoots a poisonous arrow.\nLets you control your target.", + "\n\ + Launches a burst of arrows", )), ToolKind::Sceptre => Some(( "Thorn Bulwark", - "\nProtects you and your group with thorns\nfor a short amount of time.", + "\n\ + Protects you and your group with thorns\n\ + for a short amount of time.", + )), + ToolKind::Debug => Some(( + "Possessing Arrow", + "\n\ + Shoots a poisonous arrow.\n\ + Lets you control your target.", )), _ => None, } diff --git a/voxygen/src/hud/social.rs b/voxygen/src/hud/social.rs index 8c81cf06a7..6af6a1494e 100644 --- a/voxygen/src/hud/social.rs +++ b/voxygen/src/hud/social.rs @@ -2,11 +2,7 @@ use super::{ img_ids::{Imgs, ImgsRot}, Show, TEXT_COLOR, TEXT_COLOR_3, UI_HIGHLIGHT_0, UI_MAIN, }; - -use crate::{ - i18n::Localization, - ui::{fonts::Fonts, ImageFrame, Tooltip, TooltipManager, Tooltipable}, -}; +use crate::ui::{fonts::Fonts, ImageFrame, Tooltip, TooltipManager, Tooltipable}; use client::{self, Client}; use common::{comp::group, uid::Uid}; use conrod_core::{ @@ -14,6 +10,7 @@ use conrod_core::{ widget::{self, Button, Image, Rectangle, Scrollbar, Text, TextEdit}, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use i18n::Localization; use itertools::Itertools; use std::time::Instant; diff --git a/voxygen/src/hud/trade.rs b/voxygen/src/hud/trade.rs index 1aa46459e9..51d5f3835f 100644 --- a/voxygen/src/hud/trade.rs +++ b/voxygen/src/hud/trade.rs @@ -1,18 +1,12 @@ -use super::{ - img_ids::{Imgs, ImgsRot}, - item_imgs::ItemImgs, - slots::{SlotManager, TradeSlot}, - TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, -}; -use crate::{ - hud::bag::{BackgroundIds, InventoryScroller}, - i18n::Localization, - ui::{ - fonts::Fonts, - slot::{ContentSize, SlotMaker}, - ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable, - }, +use conrod_core::{ + color, + position::Relative, + widget::{self, Button, Image, Rectangle, State as ConrodState, Text}, + widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, }; +use specs::Entity as EcsEntity; +use vek::*; + use client::Client; use common::{ comp::{ @@ -22,14 +16,23 @@ use common::{ trade::{PendingTrade, SitePrices, TradeAction, TradePhase}, }; use common_net::sync::WorldSyncExt; -use conrod_core::{ - color, - position::Relative, - widget::{self, Button, Image, Rectangle, State as ConrodState, Text}, - widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, +use i18n::Localization; + +use crate::{ + hud::bag::{BackgroundIds, InventoryScroller}, + ui::{ + fonts::Fonts, + slot::{ContentSize, SlotMaker}, + ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable, + }, +}; + +use super::{ + img_ids::{Imgs, ImgsRot}, + item_imgs::ItemImgs, + slots::{SlotManager, TradeSlot}, + TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, }; -use specs::Entity as EcsEntity; -use vek::*; pub struct State { ids: Ids, @@ -99,17 +102,18 @@ impl<'a> Trade<'a> { } } } + const MAX_TRADE_SLOTS: usize = 16; impl<'a> Trade<'a> { fn background(&mut self, state: &mut ConrodState<'_, State>, ui: &mut UiCell<'_>) { - Image::new(self.imgs.inv_bg_bag) - .w_h(424.0, 708.0) - .middle() + Image::new(self.imgs.inv_middle_bg_bag) + .w_h(424.0, 482.0) .color(Some(UI_MAIN)) + .mid_bottom_with_margin_on(ui.window, 295.0) .set(state.ids.bg, ui); - Image::new(self.imgs.inv_frame_bag) - .w_h(424.0, 708.0) + Image::new(self.imgs.inv_middle_frame) + .w_h(424.0, 482.0) .middle_of(state.ids.bg) .color(Some(UI_HIGHLIGHT_0)) .set(state.ids.bg_frame, ui); @@ -173,9 +177,9 @@ impl<'a> Trade<'a> { let inventory = inventories.get(entity)?; // Alignment for Grid - let mut alignment = Rectangle::fill_with([200.0, 340.0], color::TRANSPARENT); + let mut alignment = Rectangle::fill_with([200.0, 180.0], color::TRANSPARENT); if !ours { - alignment = alignment.top_left_with_margins_on(state.ids.bg, 180.0, 46.5); + alignment = alignment.top_left_with_margins_on(state.ids.bg, 180.0, 32.5); } else { alignment = alignment.right_from(state.ids.inv_alignment[1 - who], 0.0); } @@ -197,10 +201,16 @@ impl<'a> Trade<'a> { }) .unwrap_or_else(|| format!("Player {}", who)); - let offer_header = self - .localized_strings - .get("hud.trade.persons_offer") - .replace("{playername}", &name); + let offer_header = if ours { + self.localized_strings + .get("hud.trade.your_offer") + .to_owned() + } else { + self.localized_strings + .get("hud.trade.their_offer") + .to_owned() + }; + Text::new(&offer_header) .up_from(state.ids.inv_alignment[who], 20.0) .font_id(self.fonts.cyri.conrod_id) @@ -214,7 +224,7 @@ impl<'a> Trade<'a> { .get("hud.trade.has_accepted") .replace("{playername}", &name); Text::new(&accept_indicator) - .down_from(state.ids.inv_alignment[who], 20.0) + .down_from(state.ids.inv_alignment[who], 50.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(20)) .color(Color::Rgba( @@ -461,7 +471,7 @@ impl<'a> Trade<'a> { .w_h(31.0 * 5.0, 12.0 * 2.0) .hover_image(self.imgs.button_hover) .press_image(self.imgs.button_press) - .bottom_left_with_margins_on(state.ids.bg, 80.0, 60.0) + .bottom_left_with_margins_on(state.ids.bg, 90.0, 47.0) .label(self.localized_strings.get("hud.trade.accept")) .label_font_size(self.fonts.cyri.scale(14)) .label_color(TEXT_COLOR) diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index e383027404..3e368eacab 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -11,10 +11,9 @@ use common::{ effect::Effect, trade::{Good, SitePrices}, }; +use i18n::Localization; use std::{borrow::Cow, fmt::Write}; -use crate::i18n::Localization; - pub fn price_desc( prices: &Option, item_definition_id: &str, @@ -149,7 +148,7 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec { .get("buff.stat.health") .replace("{str_total}", &str_total.to_string()), BuffKind::IncreaseMaxEnergy => i18n - .get("buff.stat.increase_max_stamina") + .get("buff.stat.increase_max_energy") .replace("{strength}", &strength.to_string()), BuffKind::IncreaseMaxHealth => i18n .get("buff.stat.increase_max_health") diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index b52a809e07..a235fb2bfc 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -4,6 +4,8 @@ #![deny(clippy::clone_on_ref_ptr)] #![feature( array_map, + array_methods, + array_zip, bool_to_option, const_generics, drain_filter, @@ -56,6 +58,9 @@ use common_base::span; use i18n::LocalizationHandle; use std::path::PathBuf; +use std::sync::Arc; +use tokio::runtime::Runtime; + /// A type used to store state that is shared between all play states. pub struct GlobalState { pub userdata_dir: PathBuf, @@ -63,6 +68,7 @@ pub struct GlobalState { pub settings: Settings, pub profile: Profile, pub window: Window, + pub tokio_runtime: Arc, #[cfg(feature = "egui-ui")] pub egui_state: EguiState, pub lazy_init: scene::terrain::SpriteRenderContextLazy, diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 9550a440e6..98e97f5db7 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -2,9 +2,15 @@ #![feature(bool_to_option)] #![recursion_limit = "2048"] +// Allow profiling allocations with Tracy +#[cfg_attr(feature = "tracy-memory", global_allocator)] +#[cfg(feature = "tracy-memory")] +static GLOBAL: common_base::tracy_client::ProfiledAllocator = + common_base::tracy_client::ProfiledAllocator::new(std::alloc::System, 128); + +use i18n::{self, LocalizationHandle}; use veloren_voxygen::{ audio::AudioFrontend, - i18n::{self, LocalizationHandle}, profile::Profile, run, scene::terrain::SpriteRenderContext, @@ -92,7 +98,7 @@ fn main() { https://www.gitlab.com/veloren/veloren/issues/new\n\ \n\ If you're on the Veloren community Discord server, we'd be \ - grateful if you could also post a message in the #support channel. + grateful if you could also post a message in the #bugs-and-support channel. \n\ > What should I include?\n\ \n\ @@ -111,9 +117,7 @@ fn main() { Panic Payload: {:?}\n\ PanicInfo: {}\n\ Game version: {} [{}]", - logs_dir - .join("voxygen.log.") - .display(), + logs_dir.join("voxygen.log.").display(), reason, panic_info, common::util::GIT_HASH.to_string(), @@ -161,6 +165,29 @@ fn main() { default_hook(panic_info); })); + // Setup tokio runtime + use common::consts::MIN_RECOMMENDED_TOKIO_THREADS; + use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }; + use tokio::runtime::Builder; + + // TODO: evaluate std::thread::available_concurrency as a num_cpus replacement + let cores = num_cpus::get(); + let tokio_runtime = Arc::new( + Builder::new_multi_thread() + .enable_all() + .worker_threads((cores / 4).max(MIN_RECOMMENDED_TOKIO_THREADS)) + .thread_name_fn(|| { + static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0); + let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); + format!("tokio-voxygen-{}", id) + }) + .build() + .unwrap(), + ); + #[cfg(feature = "hot-reloading")] assets::start_hot_reloading(); @@ -205,7 +232,30 @@ fn main() { i18n.set_english_fallback(settings.language.use_english_fallback); // Create window - let (mut window, event_loop) = Window::new(&settings).expect("Failed to create window!"); + use veloren_voxygen::{error::Error, render::RenderError}; + let (mut window, event_loop) = match Window::new(&settings, &tokio_runtime) { + Ok(ok) => ok, + // Custom panic message when a graphics backend could not be found + Err(Error::RenderError(RenderError::CouldNotFindAdapter)) => { + #[cfg(target_os = "windows")] + const POTENTIAL_FIX: &str = + " Updating the graphics drivers on this system may resolve this issue."; + #[cfg(target_os = "macos")] + const POTENTIAL_FIX: &str = ""; + #[cfg(not(any(target_os = "windows", target_os = "macos")))] + const POTENTIAL_FIX: &str = + " Installing or updating vulkan drivers may resolve this issue."; + + panic!( + "Failed to select a rendering backend! No compatible backends were found. We \ + currently support vulkan, metal, dx12, and dx11.{} If the issue persists, please \ + include the operating system and GPU details in your bug report to help us \ + identify the cause.", + POTENTIAL_FIX + ); + }, + Err(error) => panic!("Failed to create window!: {:?}", error), + }; let clipboard = iced_winit::Clipboard::connect(window.window()); @@ -220,6 +270,7 @@ fn main() { audio, profile, window, + tokio_runtime, #[cfg(feature = "egui-ui")] egui_state, lazy_init, diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index e2c67b6c42..a5e96f3c20 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -109,10 +109,15 @@ impl PlayState for CharSelectionState { ui::Event::Logout => { return PlayStateResult::Pop; }, - ui::Event::AddCharacter { alias, tool, body } => { + ui::Event::AddCharacter { + alias, + mainhand, + offhand, + body, + } => { self.client .borrow_mut() - .create_character(alias, Some(tool), body); + .create_character(alias, mainhand, offhand, body); }, ui::Event::DeleteCharacter(character_id) => { self.client.borrow_mut().delete_character(character_id); @@ -252,6 +257,8 @@ impl PlayState for CharSelectionState { if let Some(mut second_pass) = drawer.second_pass() { second_pass.draw_clouds(); } + // Bloom (does nothing if bloom is disabled) + drawer.run_bloom_passes(); // PostProcess and UI let mut third_pass = drawer.third_pass(); third_pass.draw_postprocess(); diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index d48bfafe6e..2561f3bb90 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -1,5 +1,4 @@ use crate::{ - i18n::{Localization, LocalizationHandle}, render::UiDrawer, ui::{ self, @@ -26,6 +25,7 @@ use common::{ comp::{self, humanoid, inventory::slot::EquipSlot, Inventory, Item}, LoadoutBuilder, }; +use i18n::{Localization, LocalizationHandle}; //ImageFrame, Tooltip, use crate::settings::Settings; //use std::time::Duration; @@ -52,7 +52,8 @@ const STARTER_BOW: &str = "common.items.weapons.bow.starter"; const STARTER_AXE: &str = "common.items.weapons.axe.starter_axe"; const STARTER_STAFF: &str = "common.items.weapons.staff.starter_staff"; const STARTER_SWORD: &str = "common.items.weapons.sword.starter"; -const STARTER_SCEPTRE: &str = "common.items.weapons.sceptre.starter_sceptre"; +const STARTER_SWORDS: &str = "common.items.weapons.sword_1h.starter"; + // TODO: what does this comment mean? // // Use in future MR to make this a starter weapon @@ -67,9 +68,9 @@ image_ids_ice! { slider_range: "voxygen.element.ui.generic.slider.track", slider_indicator: "voxygen.element.ui.generic.slider.indicator", - selection: "voxygen.element.ui.generic.frames.selection", - selection_hover: "voxygen.element.ui.generic.frames.selection_hover", - selection_press: "voxygen.element.ui.generic.frames.selection_press", + char_selection: "voxygen.element.ui.generic.frames.selection", + char_selection_hover: "voxygen.element.ui.generic.frames.selection_hover", + char_selection_press: "voxygen.element.ui.generic.frames.selection_press", delete_button: "voxygen.element.ui.char_select.icons.bin", delete_button_hover: "voxygen.element.ui.char_select.icons.bin_hover", @@ -78,7 +79,7 @@ image_ids_ice! { name_input: "voxygen.element.ui.generic.textbox", // Tool Icons - sceptre: "voxygen.element.weapons.sceptre", + swords: "voxygen.element.weapons.swords", sword: "voxygen.element.weapons.sword", axe: "voxygen.element.weapons.axe", hammer: "voxygen.element.weapons.hammer", @@ -124,7 +125,8 @@ pub enum Event { Play(CharacterId), AddCharacter { alias: String, - tool: String, + mainhand: Option, + offhand: Option, body: comp::Body, }, DeleteCharacter(CharacterId), @@ -148,7 +150,8 @@ enum Mode { name: String, body: humanoid::Body, inventory: Box, - tool: &'static str, + mainhand: Option<&'static str>, + offhand: Option<&'static str>, body_type_buttons: [button::State; 2], species_buttons: [button::State; 6], @@ -178,11 +181,15 @@ impl Mode { } pub fn create(name: String) -> Self { - let tool = STARTER_SWORD; + // TODO: Load these from the server (presumably from a .ron) to allow for easier + // modification of custom starting weapons + let mainhand = Some(STARTER_SWORD); + let offhand = None; let loadout = LoadoutBuilder::empty() .defaults() - .active_mainhand(Some(Item::new_from_asset_expect(tool))) + .active_mainhand(mainhand.map(Item::new_from_asset_expect)) + .active_offhand(offhand.map(Item::new_from_asset_expect)) .build(); let inventory = Box::new(Inventory::new_with_loadout(loadout)); @@ -191,7 +198,8 @@ impl Mode { name, body: humanoid::Body::random(), inventory, - tool, + mainhand, + offhand, body_type_buttons: Default::default(), species_buttons: Default::default(), tool_buttons: Default::default(), @@ -244,7 +252,7 @@ enum Message { Name(String), BodyType(humanoid::BodyType), Species(humanoid::Species), - Tool(&'static str), + Tool((Option<&'static str>, Option<&'static str>)), RandomizeCharacter, RandomizeName, CancelDeletion, @@ -468,12 +476,12 @@ impl Controls { .padding(10) .style( style::button::Style::new(if Some(i) == selected { - imgs.selection_hover + imgs.char_selection_hover } else { - imgs.selection + imgs.char_selection }) - .hover_image(imgs.selection_hover) - .press_image(imgs.selection_press) + .hover_image(imgs.char_selection_hover) + .press_image(imgs.char_selection_press) .image_color(Rgba::new( select_col.0, select_col.1, @@ -485,7 +493,7 @@ impl Controls { .height(Length::Fill) .on_press(Message::Select(character_id)), ) - .ratio_of_image(imgs.selection), + .ratio_of_image(imgs.char_selection), ) .padding(0) .align_x(Align::End) @@ -514,9 +522,9 @@ impl Controls { .center_y(), ) .style( - style::button::Style::new(imgs.selection) - .hover_image(imgs.selection_hover) - .press_image(imgs.selection_press) + style::button::Style::new(imgs.char_selection) + .hover_image(imgs.char_selection_hover) + .press_image(imgs.char_selection_press) .image_color(Rgba::new(color.0, color.1, color.2, 255)) .text_color(iced::Color::from_rgb8(color.0, color.1, color.2)) .disabled_text_color(iced::Color::from_rgb8( @@ -531,7 +539,7 @@ impl Controls { button } }) - .ratio_of_image(imgs.selection) + .ratio_of_image(imgs.char_selection) .into(), ); characters @@ -710,7 +718,8 @@ impl Controls { name, body, inventory: _, - tool, + mainhand, + offhand: _, ref mut scroll, ref mut body_type_buttons, ref mut species_buttons, @@ -863,30 +872,30 @@ impl Controls { ]) .spacing(1); - let [ref mut sword_button, ref mut sceptre_button, ref mut axe_button, ref mut hammer_button, ref mut bow_button, ref mut staff_button] = + let [ref mut sword_button, ref mut swords_button, ref mut axe_button, ref mut hammer_button, ref mut bow_button, ref mut staff_button] = tool_buttons; let tool = Column::with_children(vec![ Row::with_children(vec![ icon_button_tooltip( sword_button, - *tool == STARTER_SWORD, - Message::Tool(STARTER_SWORD), + *mainhand == Some(STARTER_SWORD), + Message::Tool((Some(STARTER_SWORD), None)), imgs.sword, - "common.weapons.sword", + "common.weapons.greatsword", ) .into(), icon_button_tooltip( hammer_button, - *tool == STARTER_HAMMER, - Message::Tool(STARTER_HAMMER), + *mainhand == Some(STARTER_HAMMER), + Message::Tool((Some(STARTER_HAMMER), None)), imgs.hammer, "common.weapons.hammer", ) .into(), icon_button_tooltip( axe_button, - *tool == STARTER_AXE, - Message::Tool(STARTER_AXE), + *mainhand == Some(STARTER_AXE), + Message::Tool((Some(STARTER_AXE), None)), imgs.axe, "common.weapons.axe", ) @@ -896,25 +905,25 @@ impl Controls { .into(), Row::with_children(vec![ icon_button_tooltip( - sceptre_button, - *tool == STARTER_SCEPTRE, - Message::Tool(STARTER_SCEPTRE), - imgs.sceptre, - "common.weapons.sceptre", + swords_button, + *mainhand == Some(STARTER_SWORDS), + Message::Tool((Some(STARTER_SWORDS), Some(STARTER_SWORDS))), + imgs.swords, + "common.weapons.shortswords", ) .into(), icon_button_tooltip( bow_button, - *tool == STARTER_BOW, - Message::Tool(STARTER_BOW), + *mainhand == Some(STARTER_BOW), + Message::Tool((Some(STARTER_BOW), None)), imgs.bow, "common.weapons.bow", ) .into(), icon_button_tooltip( staff_button, - *tool == STARTER_STAFF, - Message::Tool(STARTER_STAFF), + *mainhand == Some(STARTER_STAFF), + Message::Tool((Some(STARTER_STAFF), None)), imgs.staff, "common.weapons.staff", ) @@ -1076,8 +1085,8 @@ impl Controls { let column_content = vec![ body_type.into(), - species.into(), tool.into(), + species.into(), slider_options.into(), rand_character.into(), ]; @@ -1300,12 +1309,17 @@ impl Controls { }, Message::CreateCharacter => { if let Mode::Create { - name, body, tool, .. + name, + body, + mainhand, + offhand, + .. } = &self.mode { events.push(Event::AddCharacter { alias: name.clone(), - tool: String::from(*tool), + mainhand: mainhand.map(String::from), + offhand: offhand.map(String::from), body: comp::Body::Humanoid(*body), }); self.mode = Mode::select(Some(InfoContent::CreatingCharacter)); @@ -1330,13 +1344,21 @@ impl Controls { }, Message::Tool(value) => { if let Mode::Create { - tool, inventory, .. + mainhand, + offhand, + inventory, + .. } = &mut self.mode { - *tool = value; + *mainhand = value.0; + *offhand = value.1; inventory.replace_loadout_item( EquipSlot::ActiveMainhand, - Some(Item::new_from_asset_expect(*tool)), + mainhand.map(|specifier| Item::new_from_asset_expect(specifier)), + ); + inventory.replace_loadout_item( + EquipSlot::ActiveOffhand, + offhand.map(|specifier| Item::new_from_asset_expect(specifier)), ); } }, diff --git a/voxygen/src/menu/main/client_init.rs b/voxygen/src/menu/main/client_init.rs index 784e0ecbb0..e2e173fb93 100644 --- a/voxygen/src/menu/main/client_init.rs +++ b/voxygen/src/menu/main/client_init.rs @@ -3,11 +3,10 @@ use client::{ error::{Error as ClientError, NetworkConnectError, NetworkError}, Client, ServerInfo, }; -use common::consts::MIN_RECOMMENDED_TOKIO_THREADS; use crossbeam_channel::{unbounded, Receiver, Sender, TryRecvError}; use std::{ sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, + atomic::{AtomicBool, Ordering}, Arc, }, time::Duration, @@ -47,29 +46,13 @@ impl ClientInit { connection_args: ConnectionArgs, username: String, password: String, - runtime: Option>, + runtime: Arc, ) -> Self { let (tx, rx) = unbounded(); let (trust_tx, trust_rx) = unbounded(); let cancel = Arc::new(AtomicBool::new(false)); let cancel2 = Arc::clone(&cancel); - let runtime = runtime.unwrap_or_else(|| { - // TODO: evaluate std::thread::available_concurrency as a num_cpus replacement - let cores = num_cpus::get(); - Arc::new( - runtime::Builder::new_multi_thread() - .enable_all() - .worker_threads((cores / 4).max(MIN_RECOMMENDED_TOKIO_THREADS)) - .thread_name_fn(|| { - static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0); - let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); - format!("tokio-voxygen-{}", id) - }) - .build() - .unwrap(), - ) - }); let runtime2 = Arc::clone(&runtime); runtime.spawn(async move { diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 06457e5069..5914cd5f1f 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -6,7 +6,6 @@ use super::char_selection::CharSelectionState; #[cfg(feature = "singleplayer")] use crate::singleplayer::Singleplayer; use crate::{ - i18n::LocalizationHandle, render::{Drawer, GlobalsBindGroup}, settings::Settings, window::Event, @@ -20,6 +19,7 @@ use client::{ use client_init::{ClientInit, Error as InitError, Msg as InitMsg}; use common::comp; use common_base::span; +use i18n::LocalizationHandle; use scene::Scene; use std::sync::Arc; use tokio::runtime; @@ -93,7 +93,7 @@ impl PlayState for MainMenuState { { if let Some(singleplayer) = &global_state.singleplayer { match singleplayer.receiver.try_recv() { - Ok(Ok(runtime)) => { + Ok(Ok(())) => { // Attempt login after the server is finished initializing attempt_login( &mut global_state.info_message, @@ -101,7 +101,7 @@ impl PlayState for MainMenuState { "".to_owned(), ConnectionArgs::Mpsc(14004), &mut self.init, - Some(runtime), + &global_state.tokio_runtime, ); }, Ok(Err(e)) => { @@ -261,7 +261,7 @@ impl PlayState for MainMenuState { password, connection_args, &mut self.init, - None, + &global_state.tokio_runtime, ); }, MainMenuEvent::CancelLoginAttempt => { @@ -290,7 +290,7 @@ impl PlayState for MainMenuState { }, #[cfg(feature = "singleplayer")] MainMenuEvent::StartSingleplayer => { - let singleplayer = Singleplayer::new(); + let singleplayer = Singleplayer::new(&global_state.tokio_runtime); global_state.singleplayer = Some(singleplayer); }, @@ -450,7 +450,7 @@ fn attempt_login( password: String, connection_args: ConnectionArgs, init: &mut InitState, - runtime: Option>, + runtime: &Arc, ) { if let Err(err) = comp::Player::alias_validate(&username) { *info_message = Some(err.to_string()); @@ -463,7 +463,7 @@ fn attempt_login( connection_args, username, password, - runtime, + Arc::clone(runtime), )); } } diff --git a/voxygen/src/menu/main/ui/connecting.rs b/voxygen/src/menu/main/ui/connecting.rs index 42c090a1c6..49651fdb21 100644 --- a/voxygen/src/menu/main/ui/connecting.rs +++ b/voxygen/src/menu/main/ui/connecting.rs @@ -1,13 +1,12 @@ use super::{ConnectionState, Imgs, Message}; -use crate::{ - i18n::Localization, - ui::{ - fonts::IcedFonts as Fonts, - ice::{component::neat_button, style, widget::Image, Element, IcedUi as Ui, Id}, - Graphic, - }, + +use crate::ui::{ + fonts::IcedFonts as Fonts, + ice::{component::neat_button, style, widget::Image, Element, IcedUi as Ui, Id}, + Graphic, }; use common::assets::{self, AssetExt}; +use i18n::Localization; use iced::{button, Align, Column, Container, Length, Row, Space, Text}; use serde::{Deserialize, Serialize}; diff --git a/voxygen/src/menu/main/ui/disclaimer.rs b/voxygen/src/menu/main/ui/disclaimer.rs index b7225a0edf..885b29bc15 100644 --- a/voxygen/src/menu/main/ui/disclaimer.rs +++ b/voxygen/src/menu/main/ui/disclaimer.rs @@ -1,6 +1,6 @@ use super::Message; +use i18n::{Localization}; use crate::{ - i18n::Localization, ui::{ fonts::IcedFonts as Fonts, ice::{component::neat_button, style, Element}, diff --git a/voxygen/src/menu/main/ui/login.rs b/voxygen/src/menu/main/ui/login.rs index cb8303b951..9234e713d6 100644 --- a/voxygen/src/menu/main/ui/login.rs +++ b/voxygen/src/menu/main/ui/login.rs @@ -1,19 +1,17 @@ use super::{Imgs, LoginInfo, Message, FILL_FRAC_ONE, FILL_FRAC_TWO}; -use crate::{ - i18n::Localization, - ui::{ - fonts::IcedFonts as Fonts, - ice::{ - component::neat_button, - style, - widget::{ - compound_graphic::{CompoundGraphic, Graphic}, - BackgroundContainer, Image, Padding, - }, - Element, +use crate::ui::{ + fonts::IcedFonts as Fonts, + ice::{ + component::neat_button, + style, + widget::{ + compound_graphic::{CompoundGraphic, Graphic}, + BackgroundContainer, Image, Padding, }, + Element, }, }; +use i18n::{LanguageMetadata, Localization}; use iced::{ button, scrollable, text_input, Align, Button, Column, Container, Length, Row, Scrollable, Space, Text, TextInput, @@ -61,7 +59,7 @@ impl Screen { i18n: &Localization, is_selecting_language: bool, selected_language_index: Option, - language_metadatas: &[crate::i18n::LanguageMetadata], + language_metadatas: &[LanguageMetadata], button_style: style::button::Style, version: &str, ) -> Element { @@ -223,7 +221,7 @@ impl LanguageSelectBanner { fonts: &Fonts, imgs: &Imgs, i18n: &Localization, - language_metadatas: &[crate::i18n::LanguageMetadata], + language_metadatas: &[LanguageMetadata], selected_language_index: Option, button_style: style::button::Style, ) -> Element { diff --git a/voxygen/src/menu/main/ui/mod.rs b/voxygen/src/menu/main/ui/mod.rs index 2cb8772994..a013750891 100644 --- a/voxygen/src/menu/main/ui/mod.rs +++ b/voxygen/src/menu/main/ui/mod.rs @@ -5,7 +5,6 @@ mod login; mod servers; use crate::{ - i18n::{LanguageMetadata, LocalizationHandle}, render::UiDrawer, ui::{ self, @@ -16,6 +15,7 @@ use crate::{ }, window, GlobalState, }; +use i18n::{LanguageMetadata, LocalizationHandle}; use iced::{text_input, Column, Container, HorizontalAlignment, Length, Row, Space}; //ImageFrame, Tooltip, use crate::settings::Settings; @@ -193,7 +193,7 @@ impl Controls { .iter() .position(|f| f == &login_info.server); - let language_metadatas = crate::i18n::list_localizations(); + let language_metadatas = i18n::list_localizations(); let selected_language_index = language_metadatas .iter() .position(|f| f.language_identifier == settings.language.selected_language); @@ -256,7 +256,7 @@ impl Controls { self.imgs.bg }; - let language_metadatas = crate::i18n::list_localizations(); + let language_metadatas = i18n::list_localizations(); // TODO: make any large text blocks scrollable so that if the area is to // small they can still be read @@ -315,7 +315,7 @@ impl Controls { ui: &mut Ui, ) { let servers = &settings.networking.servers; - let mut language_metadatas = crate::i18n::list_localizations(); + let mut language_metadatas = i18n::list_localizations(); match message { Message::Quit => events.push(Event::Quit), @@ -509,7 +509,7 @@ impl MainMenuUi { self.ui.clear_fonts(font); self.controls.fonts = Fonts::load(i18n.fonts(), &mut self.ui).expect("Impossible to load fonts!"); - let language_metadatas = crate::i18n::list_localizations(); + let language_metadatas = i18n::list_localizations(); self.controls.selected_language_index = language_metadatas .iter() .position(|f| f.language_identifier == settings.language.selected_language); diff --git a/voxygen/src/menu/main/ui/servers.rs b/voxygen/src/menu/main/ui/servers.rs index b07804ee63..4e4f20343f 100644 --- a/voxygen/src/menu/main/ui/servers.rs +++ b/voxygen/src/menu/main/ui/servers.rs @@ -1,11 +1,9 @@ use super::{Imgs, Message, FILL_FRAC_ONE}; -use crate::{ - i18n::Localization, - ui::{ - fonts::IcedFonts as Fonts, - ice::{component::neat_button, style, Element}, - }, +use crate::ui::{ + fonts::IcedFonts as Fonts, + ice::{component::neat_button, style, Element}, }; +use i18n::Localization; use iced::{ button, scrollable, Align, Button, Column, Container, Length, Row, Scrollable, Space, Text, }; diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index d7598d5508..26006ad843 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -265,6 +265,66 @@ impl From for wgpu::PresentMode { } } +/// Bloom factor +/// Controls fraction of output image luminosity that is blurred bloom +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +pub enum BloomFactor { + Low, + High, + /// Max valid value is 1.0 + Custom(f32), + // other variant has to be placed last + #[serde(other)] + Standard, +} + +impl Default for BloomFactor { + fn default() -> Self { Self::Standard } +} + +impl BloomFactor { + /// Fraction of output image luminosity that is blurred bloom + pub fn fraction(self) -> f32 { + match self { + Self::Low => 0.05, + Self::Standard => 0.10, + Self::High => 0.25, + Self::Custom(val) => val.max(0.0).min(1.0), + } + } +} + +/// Bloom settings +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +pub struct BloomConfig { + /// Controls fraction of output image luminosity that is blurred bloom + /// + /// Defaults to `Standard` + factor: BloomFactor, + /// Turning this on make the bloom blur less sharply concentrated around the + /// high intensity phenomena (removes adding in less blurred layers to the + /// final blur) + /// + /// Defaults to `false` + uniform_blur: bool, + // TODO: allow configuring the blur radius and/or the number of passes +} + +#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)] +pub enum BloomMode { + On(BloomConfig), + #[serde(other)] + Off, +} + +impl Default for BloomMode { + fn default() -> Self { Self::Off } +} + +impl BloomMode { + fn is_on(&self) -> bool { matches!(self, BloomMode::On(_)) } +} + /// Render modes #[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize)] #[serde(default)] @@ -274,7 +334,49 @@ pub struct RenderMode { pub fluid: FluidMode, pub lighting: LightingMode, pub shadow: ShadowMode, + pub bloom: BloomMode, + pub upscale_mode: UpscaleMode, pub present_mode: PresentMode, pub profiler_enabled: bool, } + +impl RenderMode { + fn split(self) -> (PipelineModes, OtherModes) { + ( + PipelineModes { + aa: self.aa, + cloud: self.cloud, + fluid: self.fluid, + lighting: self.lighting, + shadow: self.shadow, + bloom: self.bloom, + }, + OtherModes { + upscale_mode: self.upscale_mode, + present_mode: self.present_mode, + profiler_enabled: self.profiler_enabled, + }, + ) + } +} + +/// Render modes that require pipeline recreation (e.g. shader recompilation) +/// when changed +#[derive(PartialEq, Clone, Debug)] +pub struct PipelineModes { + aa: AaMode, + cloud: CloudMode, + fluid: FluidMode, + lighting: LightingMode, + pub shadow: ShadowMode, + bloom: BloomMode, +} + +/// Other render modes that don't effect pipelines +#[derive(PartialEq, Clone, Debug)] +struct OtherModes { + upscale_mode: UpscaleMode, + present_mode: PresentMode, + profiler_enabled: bool, +} diff --git a/voxygen/src/render/pipelines/bloom.rs b/voxygen/src/render/pipelines/bloom.rs new file mode 100644 index 0000000000..123f25680c --- /dev/null +++ b/voxygen/src/render/pipelines/bloom.rs @@ -0,0 +1,228 @@ +//! Based on: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf +//! +//! See additional details in the [NUM_SIZES] docs + +use super::super::{BloomConfig, Consts}; +use bytemuck::{Pod, Zeroable}; +use vek::*; + +// TODO: auto-tune the number of passes to maintain roughly constant blur per +// unit of FOV so changing resolution / FOV doesn't change the blur appearance +// significantly. +// +/// Blurring is performed while downsampling to the smaller sizes in steps and +/// then upsampling back up to the original resolution. Each level is half the +/// size in both dimensions from the previous. For instance with 5 distinct +/// sizes there is a total of 8 passes going from the largest to the smallest to +/// the largest again: +/// +/// 1 -> 1/2 -> 1/4 -> 1/8 -> 1/16 -> 1/8 -> 1/4 -> 1/2 -> 1 +/// ~~~~ +/// [downsampling] smallest [upsampling] +/// +/// The textures used for downsampling are re-used when upsampling. +/// +/// Additionally, instead of clearing them the colors are added together in an +/// attempt to obtain a more concentrated bloom near bright areas rather than +/// a uniform blur. In the example above, the added layers would include 1/8, +/// 1/4, and 1/2. The smallest size is not upsampled to and the original full +/// resolution has no blurring and we are already combining the bloom into the +/// full resolution image in a later step, so they are not included here. The 3 +/// extra layers added in mean the total luminosity of the final blurred bloom +/// image will be 4 times more than the input image. To account for this, we +/// divide the bloom intensity by 4 before applying it. +/// +/// Nevertheless, we have not fully evaluated how this visually compares to the +/// bloom obtained without adding with the previous layers so there is the +/// potential for further artistic investigation here. +/// +/// NOTE: This constant includes the full resolution size and it is +/// assumed that there will be at least one smaller image to downsample to and +/// upsample back from (otherwise no blurring would be done). Thus, the minimum +/// valid value is 2 and panicking indexing operations we perform assume this +/// will be at least 2. +pub const NUM_SIZES: usize = 5; + +pub struct BindGroup { + pub(in super::super) bind_group: wgpu::BindGroup, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Zeroable, Pod)] +pub struct Locals { + halfpixel: [f32; 2], +} + +impl Locals { + pub fn new(source_texture_resolution: Vec2) -> Self { + Self { + halfpixel: source_texture_resolution.map(|e| 0.5 / e).into_array(), + } + } +} + +pub struct BloomLayout { + pub layout: wgpu::BindGroupLayout, +} + +impl BloomLayout { + pub fn new(device: &wgpu::Device) -> Self { + Self { + layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: None, + entries: &[ + // Color source + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler { + filtering: true, + comparison: false, + }, + count: None, + }, + // halfpixel + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + ], + }), + } + } + + pub fn bind( + &self, + device: &wgpu::Device, + src_color: &wgpu::TextureView, + sampler: &wgpu::Sampler, + half_pixel: Consts, + ) -> BindGroup { + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &self.layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(src_color), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(sampler), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: half_pixel.buf().as_entire_binding(), + }, + ], + }); + + BindGroup { bind_group } + } +} + +pub struct BloomPipelines { + pub downsample_filtered: wgpu::RenderPipeline, + pub downsample: wgpu::RenderPipeline, + pub upsample: wgpu::RenderPipeline, +} + +impl BloomPipelines { + pub fn new( + device: &wgpu::Device, + vs_module: &wgpu::ShaderModule, + downsample_filtered_fs_module: &wgpu::ShaderModule, + downsample_fs_module: &wgpu::ShaderModule, + upsample_fs_module: &wgpu::ShaderModule, + target_format: wgpu::TextureFormat, + layout: &BloomLayout, + bloom_config: &BloomConfig, + ) -> Self { + common_base::span!(_guard, "BloomPipelines::new"); + let render_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Bloom pipelines layout"), + push_constant_ranges: &[], + bind_group_layouts: &[&layout.layout], + }); + + let create_pipeline = |label, fs_module, blend| { + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some(label), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: vs_module, + entry_point: "main", + buffers: &[], + }, + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + clamp_depth: false, + polygon_mode: wgpu::PolygonMode::Fill, + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + fragment: Some(wgpu::FragmentState { + module: fs_module, + entry_point: "main", + targets: &[wgpu::ColorTargetState { + format: target_format, + blend, + write_mask: wgpu::ColorWrite::ALL, + }], + }), + }) + }; + + let downsample_filtered_pipeline = create_pipeline( + "Bloom downsample filtered pipeline", + downsample_filtered_fs_module, + None, + ); + let downsample_pipeline = + create_pipeline("Bloom downsample pipeline", downsample_fs_module, None); + let upsample_pipeline = create_pipeline( + "Bloom upsample pipeline", + upsample_fs_module, + (!bloom_config.uniform_blur).then(|| wgpu::BlendState { + color: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::One, + operation: wgpu::BlendOperation::Add, + }, + // We don't reaaly use this but we need something here.. + alpha: wgpu::BlendComponent::REPLACE, + }), + ); + + Self { + downsample_filtered: downsample_filtered_pipeline, + downsample: downsample_pipeline, + upsample: upsample_pipeline, + } + } +} diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index 592d1da64d..d4a0ee3c46 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -1,4 +1,5 @@ pub mod blit; +pub mod bloom; pub mod clouds; pub mod debug; pub mod figure; diff --git a/voxygen/src/render/pipelines/postprocess.rs b/voxygen/src/render/pipelines/postprocess.rs index 18c982f50c..3d67c2d4d4 100644 --- a/voxygen/src/render/pipelines/postprocess.rs +++ b/voxygen/src/render/pipelines/postprocess.rs @@ -1,4 +1,4 @@ -use super::super::{Consts, GlobalsLayouts}; +use super::super::{Consts, GlobalsLayouts, PipelineModes}; use bytemuck::{Pod, Zeroable}; use vek::*; @@ -31,43 +31,61 @@ pub struct PostProcessLayout { } impl PostProcessLayout { - pub fn new(device: &wgpu::Device) -> Self { + pub fn new(device: &wgpu::Device, pipeline_modes: &PipelineModes) -> Self { + let mut bind_entries = vec![ + // src color + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler { + filtering: true, + comparison: false, + }, + count: None, + }, + // Locals + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + ]; + + if pipeline_modes.bloom.is_on() { + bind_entries.push( + // src bloom + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + ); + } + Self { layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, - entries: &[ - // src color - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - // Locals - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - ], + entries: &bind_entries, }), } } @@ -76,26 +94,42 @@ impl PostProcessLayout { &self, device: &wgpu::Device, src_color: &wgpu::TextureView, + src_bloom: Option<&wgpu::TextureView>, sampler: &wgpu::Sampler, locals: &Consts, ) -> BindGroup { + let mut entries = vec![ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(src_color), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(sampler), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: locals.buf().as_entire_binding(), + }, + ]; + // Optional bloom source + if let Some(src_bloom) = src_bloom { + entries.push( + // TODO: might be cheaper to premix bloom at lower resolution if we are doing + // extensive upscaling + // TODO: if there is no upscaling we can do the last bloom upsampling in post + // process to save a pass and the need for the final full size bloom render target + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::TextureView(src_bloom), + }, + ); + } + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, layout: &self.layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(src_color), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(sampler), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: locals.buf().as_entire_binding(), - }, - ], + entries: &entries, }); BindGroup { bind_group } diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 4b58ca095f..820b1353d2 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -21,11 +21,12 @@ use super::{ mesh::Mesh, model::{DynamicModel, Model}, pipelines::{ - blit, clouds, debug, figure, postprocess, shadow, sprite, terrain, ui, GlobalsBindGroup, - GlobalsLayouts, ShadowTexturesBindGroup, + blit, bloom, clouds, debug, figure, postprocess, shadow, sprite, terrain, ui, + GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup, }, texture::Texture, - AaMode, AddressMode, FilterMode, RenderError, RenderMode, ShadowMapMode, ShadowMode, Vertex, + AaMode, AddressMode, FilterMode, OtherModes, PipelineModes, RenderError, RenderMode, + ShadowMapMode, ShadowMode, Vertex, }; use common::assets::{self, AssetExt, AssetHandle}; use common_base::span; @@ -45,21 +46,35 @@ pub type ColLightInfo = (Vec<[u8; 4]>, Vec2); const QUAD_INDEX_BUFFER_U16_START_VERT_LEN: u16 = 3000; const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000; -/// A type that stores all the layouts associated with this renderer. -struct Layouts { +/// A type that stores all the layouts associated with this renderer that never +/// change when the RenderMode is modified. +struct ImmutableLayouts { global: GlobalsLayouts, - clouds: clouds::CloudsLayout, debug: debug::DebugLayout, figure: figure::FigureLayout, - postprocess: postprocess::PostProcessLayout, shadow: shadow::ShadowLayout, sprite: sprite::SpriteLayout, terrain: terrain::TerrainLayout, + clouds: clouds::CloudsLayout, + bloom: bloom::BloomLayout, ui: ui::UiLayout, blit: blit::BlitLayout, } +/// A type that stores all the layouts associated with this renderer. +struct Layouts { + immutable: Arc, + + postprocess: Arc, +} + +impl core::ops::Deref for Layouts { + type Target = ImmutableLayouts; + + fn deref(&self) -> &Self::Target { &self.immutable } +} + /// Render target views struct Views { // NOTE: unused for now, maybe... we will want it for something @@ -67,6 +82,8 @@ struct Views { tgt_color: wgpu::TextureView, tgt_depth: wgpu::TextureView, + + bloom_tgts: Option<[wgpu::TextureView; bloom::NUM_SIZES]>, // TODO: rename tgt_color_pp: wgpu::TextureView, } @@ -81,6 +98,7 @@ struct Shadow { /// 1. Only interface pipelines created /// 2. All of the pipelines have been created #[allow(clippy::large_enum_variant)] // They are both pretty large +#[allow(clippy::type_complexity)] enum State { // NOTE: this is used as a transient placeholder for moving things out of State temporarily Nothing, @@ -93,7 +111,19 @@ enum State { Complete { pipelines: Pipelines, shadow: Shadow, - recreating: Option>>, + recreating: Option<( + PipelineModes, + PipelineCreation< + Result< + ( + Pipelines, + ShadowPipelines, + Arc, + ), + RenderError, + >, + >, + )>, }, } @@ -112,11 +142,11 @@ pub struct Renderer { depth_sampler: wgpu::Sampler, state: State, - // true if there is a pending need to recreate the pipelines (e.g. RenderMode change or shader + // Some if there is a pending need to recreate the pipelines (e.g. RenderMode change or shader // hotloading) - recreation_pending: bool, + recreation_pending: Option, - layouts: Arc, + layouts: Layouts, // Note: we keep these here since their bind groups need to be updated if we resize the // color/depth textures locals: Locals, @@ -128,7 +158,8 @@ pub struct Renderer { shaders: AssetHandle, - mode: RenderMode, + pipeline_modes: PipelineModes, + other_modes: OtherModes, resolution: Vec2, // If this is Some then a screenshot will be taken and passed to the handler here @@ -152,7 +183,12 @@ pub struct Renderer { impl Renderer { /// Create a new `Renderer` from a variety of backend-specific components /// and the window targets. - pub fn new(window: &winit::window::Window, mut mode: RenderMode) -> Result { + pub fn new( + window: &winit::window::Window, + mode: RenderMode, + runtime: &tokio::runtime::Runtime, + ) -> Result { + let (pipeline_modes, mut other_modes) = mode.split(); // Enable seamless cubemaps globally, where available--they are essentially a // strict improvement on regular cube maps. // @@ -188,12 +224,38 @@ impl Renderer { #[allow(unsafe_code)] let surface = unsafe { instance.create_surface(window) }; - let adapter = futures_executor::block_on(instance.request_adapter( - &wgpu::RequestAdapterOptionsBase { - power_preference: wgpu::PowerPreference::HighPerformance, - compatible_surface: Some(&surface), + let adapters = instance + .enumerate_adapters(backend_bit) + .enumerate() + .collect::>(); + + for (i, adapter) in adapters.iter() { + let info = adapter.get_info(); + info!( + ?info.name, + ?info.vendor, + ?info.backend, + ?info.device, + ?info.device_type, + "graphics device #{}", i, + ); + } + + let adapter = match std::env::var("WGPU_ADAPTER").ok() { + Some(filter) if !filter.is_empty() => adapters.into_iter().find_map(|(i, adapter)| { + let info = adapter.get_info(); + + let full_name = format!("#{} {} {:?}", i, info.name, info.device_type,); + + full_name.contains(&filter).then(|| adapter) + }), + Some(_) | None => { + runtime.block_on(instance.request_adapter(&wgpu::RequestAdapterOptionsBase { + power_preference: wgpu::PowerPreference::HighPerformance, + compatible_surface: Some(&surface), + })) }, - )) + } .ok_or(RenderError::CouldNotFindAdapter)?; let info = adapter.get_info(); @@ -212,7 +274,33 @@ impl Renderer { ..Default::default() }; - let (device, queue) = futures_executor::block_on(adapter.request_device( + let trace_env = std::env::var_os("WGPU_TRACE_DIR"); + let trace_path = trace_env.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 + assert!( + path.exists(), + "WGPU_TRACE_DIR is set to the path \"{}\" which doesn't exist", + path.display() + ); + assert!( + path.is_dir(), + "WGPU_TRACE_DIR is set to the path \"{}\" which is not a directory", + path.display() + ); + assert!( + path.read_dir() + .expect("Could not read the directory that is specified by WGPU_TRACE_DIR") + .next() + .is_some(), + "WGPU_TRACE_DIR is set to the path \"{}\" which already contains other files", + path.display() + ); + + path + }); + + let (device, queue) = runtime.block_on(adapter.request_device( &wgpu::DeviceDescriptor { // TODO label: None, @@ -222,36 +310,7 @@ impl Renderer { | (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 - }), + trace_path.as_deref(), ))?; // Set error handler for wgpu errors @@ -285,7 +344,7 @@ impl Renderer { format, width: dims.width, height: dims.height, - present_mode: mode.present_mode.into(), + present_mode: other_modes.present_mode.into(), }; let swap_chain = device.create_swap_chain(&surface, &sc_desc); @@ -293,7 +352,7 @@ impl Renderer { let shadow_views = ShadowMap::create_shadow_views( &device, (dims.width, dims.height), - &ShadowMapMode::try_from(mode.shadow).unwrap_or_default(), + &ShadowMapMode::try_from(pipeline_modes.shadow).unwrap_or_default(), ) .map_err(|err| { warn!("Could not create shadow map views: {:?}", err); @@ -305,41 +364,51 @@ impl Renderer { let layouts = { let global = GlobalsLayouts::new(&device); - let clouds = clouds::CloudsLayout::new(&device); let debug = debug::DebugLayout::new(&device); let figure = figure::FigureLayout::new(&device); - let postprocess = postprocess::PostProcessLayout::new(&device); let shadow = shadow::ShadowLayout::new(&device); let sprite = sprite::SpriteLayout::new(&device); let terrain = terrain::TerrainLayout::new(&device); + let clouds = clouds::CloudsLayout::new(&device); + let bloom = bloom::BloomLayout::new(&device); + let postprocess = Arc::new(postprocess::PostProcessLayout::new( + &device, + &pipeline_modes, + )); let ui = ui::UiLayout::new(&device); let blit = blit::BlitLayout::new(&device); - Layouts { + let immutable = Arc::new(ImmutableLayouts { global, - clouds, debug, figure, - postprocess, shadow, sprite, terrain, + clouds, + bloom, ui, blit, + }); + + Layouts { + immutable, + postprocess, } }; - // Arcify the device and layouts + // Arcify the device let device = Arc::new(device); - let layouts = Arc::new(layouts); let (interface_pipelines, creating) = pipeline_creation::initial_create_pipelines( - // TODO: combine Arcs? Arc::clone(&device), - Arc::clone(&layouts), + Layouts { + immutable: Arc::clone(&layouts.immutable), + postprocess: Arc::clone(&layouts.postprocess), + }, shaders.read().clone(), - mode.clone(), + pipeline_modes.clone(), sc_desc.clone(), // Note: cheap clone shadow_views.is_some(), )?; @@ -350,7 +419,12 @@ impl Renderer { creating, }; - let views = Self::create_rt_views(&device, (dims.width, dims.height), &mode)?; + let (views, bloom_sizes) = Self::create_rt_views( + &device, + (dims.width, dims.height), + &pipeline_modes, + &other_modes, + ); let create_sampler = |filter| { device.create_sampler(&wgpu::SamplerDescriptor { @@ -389,6 +463,13 @@ impl Renderer { postprocess_locals, &views.tgt_color, &views.tgt_depth, + views.bloom_tgts.as_ref().map(|tgts| locals::BloomParams { + locals: bloom_sizes.map(|size| { + Self::create_consts_inner(&device, &queue, &[bloom::Locals::new(size)]) + }), + src_views: [&views.tgt_color_pp, &tgts[1], &tgts[2], &tgts[3], &tgts[4]], + final_tgt_view: &tgts[0], + }), &views.tgt_color_pp, &sampler, &depth_sampler, @@ -399,9 +480,9 @@ impl Renderer { let quad_index_buffer_u32 = create_quad_index_buffer_u32(&device, QUAD_INDEX_BUFFER_U32_START_VERT_LEN as usize); let mut profiler = wgpu_profiler::GpuProfiler::new(4, queue.get_timestamp_period()); - mode.profiler_enabled &= profiler_features_enabled; - profiler.enable_timer = mode.profiler_enabled; - profiler.enable_debug_marker = mode.profiler_enabled; + other_modes.profiler_enabled &= profiler_features_enabled; + profiler.enable_timer = other_modes.profiler_enabled; + profiler.enable_debug_marker = other_modes.profiler_enabled; #[cfg(feature = "egui-ui")] let egui_renderpass = @@ -415,7 +496,7 @@ impl Renderer { sc_desc, state, - recreation_pending: false, + recreation_pending: None, layouts, locals, @@ -430,7 +511,8 @@ impl Renderer { shaders, - mode, + pipeline_modes, + other_modes, resolution: Vec2::new(dims.width, dims.height), take_screenshot: None, @@ -467,7 +549,7 @@ impl Renderer { /// Returns `Some((total, complete))` if in progress pub fn pipeline_recreation_status(&self) -> Option<(usize, usize)> { if let State::Complete { recreating, .. } = &self.state { - recreating.as_ref().map(|r| r.status()) + recreating.as_ref().map(|(_, c)| c.status()) } else { None } @@ -475,37 +557,46 @@ impl Renderer { /// Change the render mode. pub fn set_render_mode(&mut self, mode: RenderMode) -> Result<(), RenderError> { - // TODO: are there actually any issues with the current mode not matching the - // pipelines (since we could previously have inconsistencies from - // pipelines failing to build due to shader editing)? - // TODO: FIXME: defer mode changing until pipelines are rebuilt to prevent - // incompatibilities as pipelines are now rebuilt in a deferred mannder in the - // background TODO: consider separating changes that don't require - // rebuilding pipelines - self.mode = mode; - self.sc_desc.present_mode = self.mode.present_mode.into(); + let (pipeline_modes, other_modes) = mode.split(); - // Only enable profiling if the wgpu features are enabled - self.mode.profiler_enabled &= self.profiler_features_enabled; - // Enable/disable profiler - if !self.mode.profiler_enabled { - // Clear the times if disabled - core::mem::take(&mut self.profile_times); + if self.other_modes != other_modes { + self.other_modes = other_modes; + + // Update present mode in swap chain descriptor + self.sc_desc.present_mode = self.other_modes.present_mode.into(); + + // Only enable profiling if the wgpu features are enabled + self.other_modes.profiler_enabled &= self.profiler_features_enabled; + // Enable/disable profiler + if !self.other_modes.profiler_enabled { + // Clear the times if disabled + core::mem::take(&mut self.profile_times); + } + self.profiler.enable_timer = self.other_modes.profiler_enabled; + self.profiler.enable_debug_marker = self.other_modes.profiler_enabled; + + // Recreate render target + self.on_resize(self.resolution); } - self.profiler.enable_timer = self.mode.profiler_enabled; - self.profiler.enable_debug_marker = self.mode.profiler_enabled; - // Recreate render target - self.on_resize(self.resolution)?; - - // Recreate pipelines with the new AA mode - self.recreate_pipelines(); + // We can't cancel the pending recreation even if the new settings are equal + // to the current ones becuase the recreation could be triggered by something + // else like shader hotloading + if self.pipeline_modes != pipeline_modes + || self + .recreation_pending + .as_ref() + .map_or(false, |modes| modes != &pipeline_modes) + { + // Recreate pipelines with new modes + self.recreate_pipelines(pipeline_modes); + } Ok(()) } - /// Get the render mode. - pub fn render_mode(&self) -> &RenderMode { &self.mode } + /// Get the pipelines mode. + pub fn pipeline_modes(&self) -> &PipelineModes { &self.pipeline_modes } /// Get the current profiling times /// Nested timings immediately follow their parent @@ -535,7 +626,7 @@ impl Renderer { } /// Resize internal render targets to match window render target dimensions. - pub fn on_resize(&mut self, dims: Vec2) -> Result<(), RenderError> { + pub fn on_resize(&mut self, dims: Vec2) { // Avoid panics when creating texture with w,h of 0,0. if dims.x != 0 && dims.y != 0 { self.is_minimized = false; @@ -546,13 +637,36 @@ impl Renderer { self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); // Resize other render targets - self.views = Self::create_rt_views(&self.device, (dims.x, dims.y), &self.mode)?; - // Rebind views to clouds/postprocess bind groups + let (views, bloom_sizes) = Self::create_rt_views( + &self.device, + (dims.x, dims.y), + &self.pipeline_modes, + &self.other_modes, + ); + self.views = views; + + // appease borrow check (TODO: remove after Rust 2021) + let device = &self.device; + let queue = &self.queue; + let views = &self.views; + let bloom_params = self + .views + .bloom_tgts + .as_ref() + .map(|tgts| locals::BloomParams { + locals: bloom_sizes.map(|size| { + Self::create_consts_inner(device, queue, &[bloom::Locals::new(size)]) + }), + src_views: [&views.tgt_color_pp, &tgts[1], &tgts[2], &tgts[3], &tgts[4]], + final_tgt_view: &tgts[0], + }); + self.locals.rebind( &self.device, &self.layouts, &self.views.tgt_color, &self.views.tgt_depth, + bloom_params, &self.views.tgt_color_pp, &self.sampler, &self.depth_sampler, @@ -576,7 +690,7 @@ impl Renderer { }; if let (Some((point_depth, directed_depth)), ShadowMode::Map(mode)) = - (shadow_views, self.mode.shadow) + (shadow_views, self.pipeline_modes.shadow) { match ShadowMap::create_shadow_views(&self.device, (dims.x, dims.y), &mode) { Ok((new_point_depth, new_directed_depth)) => { @@ -608,8 +722,6 @@ impl Renderer { } else { self.is_minimized = true; } - - Ok(()) } pub fn maintain(&self) { @@ -624,12 +736,13 @@ impl Renderer { fn create_rt_views( device: &wgpu::Device, size: (u32, u32), - mode: &RenderMode, - ) -> Result { + pipeline_modes: &PipelineModes, + other_modes: &OtherModes, + ) -> (Views, [Vec2; bloom::NUM_SIZES]) { let upscaled = Vec2::::from(size) - .map(|e| (e as f32 * mode.upscale_mode.factor) as u32) + .map(|e| (e as f32 * other_modes.upscale_mode.factor) as u32) .into_tuple(); - let (width, height, sample_count) = match mode.aa { + let (width, height, sample_count) = match pipeline_modes.aa { AaMode::None | AaMode::Fxaa => (upscaled.0, upscaled.1, 1), AaMode::MsaaX4 => (upscaled.0, upscaled.1, 4), AaMode::MsaaX8 => (upscaled.0, upscaled.1, 8), @@ -637,7 +750,7 @@ impl Renderer { }; let levels = 1; - let color_view = || { + let color_view = |width, height| { let tex = device.create_texture(&wgpu::TextureDescriptor { label: None, size: wgpu::Extent3d { @@ -665,8 +778,22 @@ impl Renderer { }) }; - let tgt_color_view = color_view(); - let tgt_color_pp_view = color_view(); + let tgt_color_view = color_view(width, height); + let tgt_color_pp_view = color_view(width, height); + + let mut size_shift = 0; + // TODO: skip creating bloom stuff when it is disabled + let bloom_sizes = [(); bloom::NUM_SIZES].map(|()| { + // .max(1) to ensure we don't create zero sized textures + let size = Vec2::new(width, height).map(|e| (e >> size_shift).max(1)); + size_shift += 1; + size + }); + + let bloom_tgt_views = pipeline_modes + .bloom + .is_on() + .then(|| bloom_sizes.map(|size| color_view(size.x, size.y))); let tgt_depth_tex = device.create_texture(&wgpu::TextureDescriptor { label: None, @@ -717,12 +844,16 @@ impl Renderer { array_layer_count: None, }); - Ok(Views { - tgt_color: tgt_color_view, - tgt_depth: tgt_depth_view, - tgt_color_pp: tgt_color_pp_view, - _win_depth: win_depth_view, - }) + ( + Views { + tgt_color: tgt_color_view, + tgt_depth: tgt_depth_view, + bloom_tgts: bloom_tgt_views, + tgt_color_pp: tgt_color_pp_view, + _win_depth: win_depth_view, + }, + bloom_sizes.map(|s| s.map(|e| e as f32)), + ) } /// Get the resolution of the render target. @@ -796,7 +927,7 @@ impl Renderer { } // Try to get the latest profiling results - if self.mode.profiler_enabled { + if self.other_modes.profiler_enabled { // Note: this lags a few frames behind if let Some(profile_times) = self.profiler.process_finished_frame() { self.profile_times = profile_times; @@ -806,6 +937,10 @@ impl Renderer { // Handle polling background pipeline creation/recreation // Temporarily set to nothing and then replace in the statement below let state = core::mem::replace(&mut self.state, State::Nothing); + // Indicator for if pipeline recreation finished and we need to recreate bind + // groups / render targets (handling defered so that State will be valid + // when calling Self::on_resize) + let mut trigger_on_resize = false; // If still creating initial pipelines, check if complete self.state = if let State::Interface { pipelines: interface, @@ -857,11 +992,11 @@ impl Renderer { } else if let State::Complete { pipelines, mut shadow, - recreating: Some(recreating), + recreating: Some((new_pipeline_modes, pipeline_creation)), } = state { - match recreating.try_complete() { - Ok(Ok((pipelines, shadow_pipelines))) => { + match pipeline_creation.try_complete() { + Ok(Ok((pipelines, shadow_pipelines, postprocess_layout))) => { if let ( Some(point_pipeline), Some(terrain_directed_pipeline), @@ -877,6 +1012,14 @@ impl Renderer { shadow_map.terrain_directed_pipeline = terrain_directed_pipeline; shadow_map.figure_directed_pipeline = figure_directed_pipeline; } + + self.pipeline_modes = new_pipeline_modes; + self.layouts.postprocess = postprocess_layout; + // TODO: we have the potential to skip recreating bindings / render targets on + // pipeline recreation trigged by shader reloading (would need to ensure new + // postprocess_layout is not created...) + trigger_on_resize = true; + State::Complete { pipelines, shadow, @@ -892,27 +1035,36 @@ impl Renderer { } }, // Not complete - Err(recreating) => State::Complete { + Err(pipeline_creation) => State::Complete { pipelines, shadow, - recreating: Some(recreating), + recreating: Some((new_pipeline_modes, pipeline_creation)), }, } } else { state }; + // Call on_resize to recreate render targets and their bind groups if the + // pipelines were recreated with a new postprocess layout and or changes in the + // render modes + if trigger_on_resize { + self.on_resize(self.resolution); + } + // If the shaders files were changed attempt to recreate the shaders if self.shaders.reloaded() { - self.recreate_pipelines(); + self.recreate_pipelines(self.pipeline_modes.clone()); } // Or if we have a recreation pending - if self.recreation_pending - && matches!(&self.state, State::Complete { recreating, .. } if recreating.is_none()) - { - self.recreation_pending = false; - self.recreate_pipelines(); + if matches!(&self.state, State::Complete { + recreating: None, + .. + }) { + if let Some(new_pipeline_modes) = self.recreation_pending.take() { + self.recreate_pipelines(new_pipeline_modes); + } } let tex = match self.swap_chain.get_current_frame() { @@ -920,7 +1072,8 @@ impl Renderer { // If lost recreate the swap chain Err(err @ wgpu::SwapChainError::Lost) => { warn!("{}. Recreating swap chain. A frame will be missed", err); - return self.on_resize(self.resolution).map(|()| None); + self.on_resize(self.resolution); + return Ok(None); }, Err(wgpu::SwapChainError::Timeout) => { // This will probably be resolved on the next frame @@ -945,29 +1098,36 @@ impl Renderer { } /// Recreate the pipelines - fn recreate_pipelines(&mut self) { + fn recreate_pipelines(&mut self, pipeline_modes: PipelineModes) { match &mut self.state { State::Complete { recreating, .. } if recreating.is_some() => { // Defer recreation so that we are not building multiple sets of pipelines in // the background at once - self.recreation_pending = true; + self.recreation_pending = Some(pipeline_modes); }, State::Complete { recreating, shadow, .. } => { - *recreating = Some(pipeline_creation::recreate_pipelines( - Arc::clone(&self.device), - Arc::clone(&self.layouts), - self.shaders.read().clone(), - self.mode.clone(), - self.sc_desc.clone(), // Note: cheap clone - shadow.map.is_enabled(), + *recreating = Some(( + pipeline_modes.clone(), + pipeline_creation::recreate_pipelines( + Arc::clone(&self.device), + Arc::clone(&self.layouts.immutable), + self.shaders.read().clone(), + pipeline_modes, + // NOTE: if present_mode starts to be used to configure pipelines then it + // needs to become a part of the pipeline modes + // (note here since the present mode is accessible + // through the swap chain descriptor) + self.sc_desc.clone(), // Note: cheap clone + shadow.map.is_enabled(), + ), )); }, State::Interface { .. } => { // Defer recreation so that we are not building multiple sets of pipelines in // the background at once - self.recreation_pending = true; + self.recreation_pending = Some(pipeline_modes); }, State::Nothing => {}, } @@ -1184,7 +1344,7 @@ impl Renderer { // Queue screenshot self.take_screenshot = Some(Box::new(screenshot_handler)); // Take profiler snapshot - if self.mode.profiler_enabled { + if self.other_modes.profiler_enabled { let file_name = format!( "frame-trace_{}.json", std::time::SystemTime::now() diff --git a/voxygen/src/render/renderer/drawer.rs b/voxygen/src/render/renderer/drawer.rs index 8034a251b5..16f67b76f2 100644 --- a/voxygen/src/render/renderer/drawer.rs +++ b/voxygen/src/render/renderer/drawer.rs @@ -4,8 +4,8 @@ use super::{ instances::Instances, model::{DynamicModel, Model, SubModel}, pipelines::{ - blit, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox, sprite, - terrain, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup, + blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox, + sprite, terrain, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup, }, }, Renderer, ShadowMap, ShadowMapRenderer, @@ -61,7 +61,7 @@ struct RendererBorrow<'frame> { pipelines: Pipelines<'frame>, locals: &'frame super::locals::Locals, views: &'frame super::Views, - mode: &'frame super::super::RenderMode, + pipeline_modes: &'frame super::super::PipelineModes, quad_index_buffer_u16: &'frame Buffer, quad_index_buffer_u32: &'frame Buffer, #[cfg(feature = "egui-ui")] @@ -112,7 +112,7 @@ impl<'frame> Drawer<'frame> { pipelines, locals: &renderer.locals, views: &renderer.views, - mode: &renderer.mode, + pipeline_modes: &renderer.pipeline_modes, quad_index_buffer_u16: &renderer.quad_index_buffer_u16, quad_index_buffer_u32: &renderer.quad_index_buffer_u32, #[cfg(feature = "egui-ui")] @@ -131,13 +131,13 @@ impl<'frame> Drawer<'frame> { } } - /// Get the render mode. - pub fn render_mode(&self) -> &super::super::RenderMode { self.borrow.mode } + /// Get the pipeline modes. + pub fn pipeline_modes(&self) -> &super::super::PipelineModes { self.borrow.pipeline_modes } /// Returns None if the shadow renderer is not enabled at some level or the /// pipelines are not available yet pub fn shadow_pass(&mut self) -> Option { - if !self.borrow.mode.shadow.is_map() { + if !self.borrow.pipeline_modes.shadow.is_map() { return None; } @@ -241,6 +241,94 @@ impl<'frame> Drawer<'frame> { }) } + /// To be ran between the second pass and the third pass + /// does nothing if the ingame pipelines are not yet ready + /// does nothing if bloom is disabled + pub fn run_bloom_passes(&mut self) { + let locals = &self.borrow.locals; + let views = &self.borrow.views; + + let bloom_pipelines = match self.borrow.pipelines.all() { + Some(super::Pipelines { bloom: Some(p), .. }) => p, + _ => return, + }; + + // TODO: consider consolidating optional bloom bind groups and optional pipeline + // into a single structure? + let (bloom_tgts, bloom_binds) = + match views.bloom_tgts.as_ref().zip(locals.bloom_binds.as_ref()) { + Some((t, b)) => (t, b), + None => return, + }; + + let device = self.borrow.device; + let mut encoder = self.encoder.as_mut().unwrap().scope("bloom", device); + + let mut run_bloom_pass = |bind, view, label: String, pipeline, load| { + let pass_label = format!("bloom {} pass", label); + let mut render_pass = + encoder.scoped_render_pass(&label, device, &wgpu::RenderPassDescriptor { + label: Some(&pass_label), + color_attachments: &[wgpu::RenderPassColorAttachment { + resolve_target: None, + view, + ops: wgpu::Operations { store: true, load }, + }], + depth_stencil_attachment: None, + }); + + render_pass.set_bind_group(0, bind, &[]); + render_pass.set_pipeline(pipeline); + render_pass.draw(0..3, 0..1); + }; + + // Downsample filter passes + (0..bloom::NUM_SIZES - 1).for_each(|index| { + let bind = &bloom_binds[index].bind_group; + let view = &bloom_tgts[index + 1]; + // Do filtering out of non-bright things during the first downsample + let (label, pipeline) = if index == 0 { + ( + format!("downsample filtered {}", index + 1), + &bloom_pipelines.downsample_filtered, + ) + } else { + ( + format!("downsample {}", index + 1), + &bloom_pipelines.downsample, + ) + }; + run_bloom_pass( + bind, + view, + label, + pipeline, + wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + ); + }); + + // Upsample filter passes + (0..bloom::NUM_SIZES - 1).for_each(|index| { + let bind = &bloom_binds[bloom::NUM_SIZES - 1 - index].bind_group; + let view = &bloom_tgts[bloom::NUM_SIZES - 2 - index]; + let label = format!("upsample {}", index + 1); + run_bloom_pass( + bind, + view, + label, + &bloom_pipelines.upsample, + if index + 2 == bloom::NUM_SIZES { + // Clear for the final image since that is just stuff from the pervious frame. + wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT) + } else { + // Add to less blurred images to get gradient of blur instead of a smudge> + // https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/ + wgpu::LoadOp::Load + }, + ); + }); + } + pub fn third_pass(&mut self) -> ThirdPassDrawer { let encoder = self.encoder.as_mut().unwrap(); let device = self.borrow.device; @@ -321,7 +409,7 @@ impl<'frame> Drawer<'frame> { chunks: impl Clone + Iterator, &'data terrain::BoundLocals)>, ) { - if !self.borrow.mode.shadow.is_map() { + if !self.borrow.pipeline_modes.shadow.is_map() { return; } diff --git a/voxygen/src/render/renderer/locals.rs b/voxygen/src/render/renderer/locals.rs index b7c94c5396..fb3b0301ac 100644 --- a/voxygen/src/render/renderer/locals.rs +++ b/voxygen/src/render/renderer/locals.rs @@ -1,15 +1,23 @@ use super::{ super::{ consts::Consts, - pipelines::{clouds, postprocess}, + pipelines::{bloom, clouds, postprocess}, }, Layouts, }; +pub struct BloomParams<'a> { + pub locals: [Consts; bloom::NUM_SIZES], + pub src_views: [&'a wgpu::TextureView; bloom::NUM_SIZES], + pub final_tgt_view: &'a wgpu::TextureView, +} + pub struct Locals { pub clouds: Consts, pub clouds_bind: clouds::BindGroup, + pub bloom_binds: Option<[bloom::BindGroup; bloom::NUM_SIZES]>, + pub postprocess: Consts, pub postprocess_bind: postprocess::BindGroup, } @@ -22,6 +30,7 @@ impl Locals { postprocess_locals: Consts, tgt_color_view: &wgpu::TextureView, tgt_depth_view: &wgpu::TextureView, + bloom: Option, tgt_color_pp_view: &wgpu::TextureView, sampler: &wgpu::Sampler, depth_sampler: &wgpu::Sampler, @@ -34,14 +43,26 @@ impl Locals { depth_sampler, &clouds_locals, ); - let postprocess_bind = - layouts - .postprocess - .bind(device, tgt_color_pp_view, sampler, &postprocess_locals); + + let postprocess_bind = layouts.postprocess.bind( + device, + tgt_color_pp_view, + bloom.as_ref().map(|b| b.final_tgt_view), + sampler, + &postprocess_locals, + ); + + let bloom_binds = bloom.map(|bloom| { + bloom + .src_views + .zip(bloom.locals) // zip arrays + .map(|(view, locals)| layouts.bloom.bind(device, view, sampler, locals)) + }); Self { clouds: clouds_locals, clouds_bind, + bloom_binds, postprocess: postprocess_locals, postprocess_bind, } @@ -55,6 +76,7 @@ impl Locals { // e.g. resizing tgt_color_view: &wgpu::TextureView, tgt_depth_view: &wgpu::TextureView, + bloom: Option, tgt_color_pp_view: &wgpu::TextureView, sampler: &wgpu::Sampler, depth_sampler: &wgpu::Sampler, @@ -67,9 +89,18 @@ impl Locals { depth_sampler, &self.clouds, ); - self.postprocess_bind = - layouts - .postprocess - .bind(device, tgt_color_pp_view, sampler, &self.postprocess); + self.postprocess_bind = layouts.postprocess.bind( + device, + tgt_color_pp_view, + bloom.as_ref().map(|b| b.final_tgt_view), + sampler, + &self.postprocess, + ); + self.bloom_binds = bloom.map(|bloom| { + bloom + .src_views + .zip(bloom.locals) // zip arrays + .map(|(view, locals)| layouts.bloom.bind(device, view, sampler, locals)) + }); } } diff --git a/voxygen/src/render/renderer/pipeline_creation.rs b/voxygen/src/render/renderer/pipeline_creation.rs index 1aca7f43f1..3ec6f4f479 100644 --- a/voxygen/src/render/renderer/pipeline_creation.rs +++ b/voxygen/src/render/renderer/pipeline_creation.rs @@ -1,13 +1,14 @@ use super::{ super::{ pipelines::{ - blit, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, - sprite, terrain, ui, + blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow, + skybox, sprite, terrain, ui, }, - AaMode, CloudMode, FluidMode, LightingMode, RenderError, RenderMode, ShadowMode, + AaMode, BloomMode, CloudMode, FluidMode, LightingMode, PipelineModes, RenderError, + ShadowMode, }, shaders::Shaders, - Layouts, + ImmutableLayouts, Layouts, }; use common_base::prof_span; use std::sync::Arc; @@ -20,6 +21,7 @@ pub struct Pipelines { pub lod_terrain: lod_terrain::LodTerrainPipeline, pub particle: particle::ParticlePipeline, pub clouds: clouds::CloudsPipeline, + pub bloom: Option, pub postprocess: postprocess::PostProcessPipeline, // Consider reenabling at some time // player_shadow: figure::FigurePipeline, @@ -39,6 +41,7 @@ pub struct IngamePipelines { lod_terrain: lod_terrain::LodTerrainPipeline, particle: particle::ParticlePipeline, clouds: clouds::CloudsPipeline, + pub bloom: Option, postprocess: postprocess::PostProcessPipeline, // Consider reenabling at some time // player_shadow: figure::FigurePipeline, @@ -74,6 +77,7 @@ impl Pipelines { lod_terrain: ingame.lod_terrain, particle: ingame.particle, clouds: ingame.clouds, + bloom: ingame.bloom, postprocess: ingame.postprocess, //player_shadow: ingame.player_shadow, skybox: ingame.skybox, @@ -107,6 +111,9 @@ struct ShaderModules { lod_terrain_frag: wgpu::ShaderModule, clouds_vert: wgpu::ShaderModule, clouds_frag: wgpu::ShaderModule, + dual_downsample_filtered_frag: wgpu::ShaderModule, + dual_downsample_frag: wgpu::ShaderModule, + dual_upsample_frag: wgpu::ShaderModule, postprocess_vert: wgpu::ShaderModule, postprocess_frag: wgpu::ShaderModule, blit_vert: wgpu::ShaderModule, @@ -120,7 +127,7 @@ impl ShaderModules { pub fn new( device: &wgpu::Device, shaders: &Shaders, - mode: &RenderMode, + pipeline_modes: &PipelineModes, has_shadow_views: bool, ) -> Result { prof_span!(_guard, "ShaderModules::new"); @@ -150,11 +157,11 @@ impl ShaderModules { &constants.0, // TODO: Configurable vertex/fragment shader preference. "VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT", - match mode.fluid { + match pipeline_modes.fluid { FluidMode::Cheap => "FLUID_MODE_CHEAP", FluidMode::Shiny => "FLUID_MODE_SHINY", }, - match mode.cloud { + match pipeline_modes.cloud { CloudMode::None => "CLOUD_MODE_NONE", CloudMode::Minimal => "CLOUD_MODE_MINIMAL", CloudMode::Low => "CLOUD_MODE_LOW", @@ -162,20 +169,38 @@ impl ShaderModules { CloudMode::High => "CLOUD_MODE_HIGH", CloudMode::Ultra => "CLOUD_MODE_ULTRA", }, - match mode.lighting { + match pipeline_modes.lighting { LightingMode::Ashikhmin => "LIGHTING_ALGORITHM_ASHIKHMIN", LightingMode::BlinnPhong => "LIGHTING_ALGORITHM_BLINN_PHONG", LightingMode::Lambertian => "LIGHTING_ALGORITHM_LAMBERTIAN", }, - match mode.shadow { + match pipeline_modes.shadow { ShadowMode::None => "SHADOW_MODE_NONE", ShadowMode::Map(_) if has_shadow_views => "SHADOW_MODE_MAP", ShadowMode::Cheap | ShadowMode::Map(_) => "SHADOW_MODE_CHEAP", }, ); + let constants = match pipeline_modes.bloom { + BloomMode::Off => constants, + BloomMode::On(config) => { + format!( + r#" +{} + +#define BLOOM_FACTOR {} +#define BLOOM_UNIFORM_BLUR {} + +"#, + constants, + config.factor.fraction(), + config.uniform_blur, + ) + }, + }; + let anti_alias = shaders - .get(match mode.aa { + .get(match pipeline_modes.aa { AaMode::None => "antialias.none", AaMode::Fxaa => "antialias.fxaa", AaMode::MsaaX4 => "antialias.msaa-x4", @@ -185,7 +210,7 @@ impl ShaderModules { .unwrap(); let cloud = shaders - .get(match mode.cloud { + .get(match pipeline_modes.cloud { CloudMode::None => "include.cloud.none", _ => "include.cloud.regular", }) @@ -228,7 +253,7 @@ impl ShaderModules { create_shader_module(device, &mut compiler, glsl, kind, &file_name, &options) }; - let selected_fluid_shader = ["fluid-frag.", match mode.fluid { + let selected_fluid_shader = ["fluid-frag.", match pipeline_modes.fluid { FluidMode::Cheap => "cheap", FluidMode::Shiny => "shiny", }] @@ -255,6 +280,12 @@ impl ShaderModules { lod_terrain_frag: create_shader("lod-terrain-frag", ShaderKind::Fragment)?, clouds_vert: create_shader("clouds-vert", ShaderKind::Vertex)?, clouds_frag: create_shader("clouds-frag", ShaderKind::Fragment)?, + dual_downsample_filtered_frag: create_shader( + "dual-downsample-filtered-frag", + ShaderKind::Fragment, + )?, + dual_downsample_frag: create_shader("dual-downsample-frag", ShaderKind::Fragment)?, + dual_upsample_frag: create_shader("dual-upsample-frag", ShaderKind::Fragment)?, postprocess_vert: create_shader("postprocess-vert", ShaderKind::Vertex)?, postprocess_frag: create_shader("postprocess-frag", ShaderKind::Fragment)?, blit_vert: create_shader("blit-vert", ShaderKind::Vertex)?, @@ -305,7 +336,7 @@ struct PipelineNeeds<'a> { device: &'a wgpu::Device, layouts: &'a Layouts, shaders: &'a ShaderModules, - mode: &'a RenderMode, + pipeline_modes: &'a PipelineModes, sc_desc: &'a wgpu::SwapChainDescriptor, } @@ -360,7 +391,7 @@ fn create_interface_pipelines( fn create_ingame_and_shadow_pipelines( needs: PipelineNeeds, pool: &rayon::ThreadPool, - tasks: [Task; 13], + tasks: [Task; 14], ) -> IngameAndShadowPipelines { prof_span!(_guard, "create_ingame_and_shadow_pipelines"); @@ -368,7 +399,7 @@ fn create_ingame_and_shadow_pipelines( device, layouts, shaders, - mode, + pipeline_modes, sc_desc, } = needs; @@ -382,6 +413,7 @@ fn create_ingame_and_shadow_pipelines( particle_task, lod_terrain_task, clouds_task, + bloom_task, postprocess_task, // TODO: if these are ever actually optionally done, counting them // as tasks to do beforehand seems kind of iffy since they will just @@ -403,7 +435,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.debug_frag, &layouts.global, &layouts.debug, - mode.aa, + pipeline_modes.aa, ) }, "debug pipeline creation", @@ -418,7 +450,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.skybox_vert, &shaders.skybox_frag, &layouts.global, - mode.aa, + pipeline_modes.aa, ) }, "skybox pipeline creation", @@ -434,7 +466,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.figure_frag, &layouts.global, &layouts.figure, - mode.aa, + pipeline_modes.aa, ) }, "figure pipeline creation", @@ -450,7 +482,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.terrain_frag, &layouts.global, &layouts.terrain, - mode.aa, + pipeline_modes.aa, ) }, "terrain pipeline creation", @@ -466,7 +498,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.fluid_frag, &layouts.global, &layouts.terrain, - mode.aa, + pipeline_modes.aa, ) }, "fluid pipeline creation", @@ -483,7 +515,7 @@ fn create_ingame_and_shadow_pipelines( &layouts.global, &layouts.sprite, &layouts.terrain, - mode.aa, + pipeline_modes.aa, ) }, "sprite pipeline creation", @@ -498,7 +530,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.particle_vert, &shaders.particle_frag, &layouts.global, - mode.aa, + pipeline_modes.aa, ) }, "particle pipeline creation", @@ -513,7 +545,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.lod_terrain_vert, &shaders.lod_terrain_frag, &layouts.global, - mode.aa, + pipeline_modes.aa, ) }, "lod terrain pipeline creation", @@ -529,12 +561,36 @@ fn create_ingame_and_shadow_pipelines( &shaders.clouds_frag, &layouts.global, &layouts.clouds, - mode.aa, + pipeline_modes.aa, ) }, "clouds pipeline creation", ) }; + // Pipelines for rendering our bloom + let create_bloom = || { + bloom_task.run( + || { + match &pipeline_modes.bloom { + BloomMode::Off => None, + BloomMode::On(config) => Some(config), + } + .map(|bloom_config| { + bloom::BloomPipelines::new( + device, + &shaders.blit_vert, + &shaders.dual_downsample_filtered_frag, + &shaders.dual_downsample_frag, + &shaders.dual_upsample_frag, + wgpu::TextureFormat::Rgba16Float, + &layouts.bloom, + bloom_config, + ) + }) + }, + "bloom pipelines creation", + ) + }; // Pipeline for rendering our post-processing let create_postprocess = || { postprocess_task.run( @@ -584,7 +640,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.point_light_shadows_vert, &layouts.global, &layouts.terrain, - mode.aa, + pipeline_modes.aa, ) }, "point shadow pipeline creation", @@ -599,7 +655,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.light_shadows_directed_vert, &layouts.global, &layouts.terrain, - mode.aa, + pipeline_modes.aa, ) }, "terrain directed shadow pipeline creation", @@ -614,7 +670,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.light_shadows_figure_vert, &layouts.global, &layouts.figure, - mode.aa, + pipeline_modes.aa, ) }, "figure directed shadow pipeline creation", @@ -622,7 +678,7 @@ fn create_ingame_and_shadow_pipelines( }; let j1 = || pool.join(create_debug, || pool.join(create_skybox, create_figure)); - let j2 = || pool.join(create_terrain, create_fluid); + let j2 = || pool.join(create_terrain, || pool.join(create_fluid, create_bloom)); let j3 = || pool.join(create_sprite, create_particle); let j4 = || pool.join(create_lod_terrain, create_clouds); let j5 = || pool.join(create_postprocess, create_point_shadow); @@ -636,7 +692,7 @@ fn create_ingame_and_shadow_pipelines( // Ignore this let ( ( - ((debug, (skybox, figure)), (terrain, fluid)), + ((debug, (skybox, figure)), (terrain, (fluid, bloom))), ((sprite, particle), (lod_terrain, clouds)), ), ((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)), @@ -653,6 +709,7 @@ fn create_ingame_and_shadow_pipelines( lod_terrain, particle, clouds, + bloom, postprocess, skybox, sprite, @@ -675,9 +732,9 @@ fn create_ingame_and_shadow_pipelines( /// NOTE: this tries to use all the CPU cores to complete as soon as possible pub(super) fn initial_create_pipelines( device: Arc, - layouts: Arc, + layouts: Layouts, shaders: Shaders, - mode: RenderMode, + pipeline_modes: PipelineModes, sc_desc: wgpu::SwapChainDescriptor, has_shadow_views: bool, ) -> Result< @@ -690,7 +747,7 @@ pub(super) fn initial_create_pipelines( prof_span!(_guard, "initial_create_pipelines"); // Process shaders into modules - let shader_modules = ShaderModules::new(&device, &shaders, &mode, has_shadow_views)?; + let shader_modules = ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views)?; // Create threadpool for parallel portion let pool = rayon::ThreadPoolBuilder::new() @@ -702,7 +759,7 @@ pub(super) fn initial_create_pipelines( device: &device, layouts: &layouts, shaders: &shader_modules, - mode: &mode, + pipeline_modes: &pipeline_modes, sc_desc: &sc_desc, }; @@ -729,7 +786,7 @@ pub(super) fn initial_create_pipelines( device: &device, layouts: &layouts, shaders: &shader_modules, - mode: &mode, + pipeline_modes: &pipeline_modes, sc_desc: &sc_desc, }; @@ -745,14 +802,24 @@ pub(super) fn initial_create_pipelines( /// Use this to recreate all the pipelines in the background. /// TODO: report progress /// NOTE: this tries to use all the CPU cores to complete as soon as possible +#[allow(clippy::type_complexity)] pub(super) fn recreate_pipelines( device: Arc, - layouts: Arc, + immutable_layouts: Arc, shaders: Shaders, - mode: RenderMode, + pipeline_modes: PipelineModes, sc_desc: wgpu::SwapChainDescriptor, has_shadow_views: bool, -) -> PipelineCreation> { +) -> PipelineCreation< + Result< + ( + Pipelines, + ShadowPipelines, + Arc, + ), + RenderError, + >, +> { prof_span!(_guard, "recreate_pipelines"); // Create threadpool for parallel portion @@ -780,20 +847,32 @@ pub(super) fn recreate_pipelines( // Process shaders into modules let guard = shader_task.start("process shaders"); - let shader_modules = match ShaderModules::new(&device, &shaders, &mode, has_shadow_views) { - Ok(modules) => modules, - Err(err) => { - result_send.send(Err(err)).expect("Channel disconnected"); - return; - }, - }; + let shader_modules = + match ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views) { + Ok(modules) => modules, + Err(err) => { + result_send.send(Err(err)).expect("Channel disconnected"); + return; + }, + }; drop(guard); + // Create new postprocess layouts + let postprocess_layouts = Arc::new(postprocess::PostProcessLayout::new( + &device, + &pipeline_modes, + )); + + let layouts = Layouts { + immutable: immutable_layouts, + postprocess: postprocess_layouts, + }; + let needs = PipelineNeeds { device: &device, layouts: &layouts, shaders: &shader_modules, - mode: &mode, + pipeline_modes: &pipeline_modes, sc_desc: &sc_desc, }; @@ -806,7 +885,11 @@ pub(super) fn recreate_pipelines( // Send them result_send - .send(Ok((Pipelines::consolidate(interface, ingame), shadow))) + .send(Ok(( + Pipelines::consolidate(interface, ingame), + shadow, + layouts.postprocess, + ))) .expect("Channel disconnected"); }); diff --git a/voxygen/src/render/renderer/shaders.rs b/voxygen/src/render/renderer/shaders.rs index eb3a148a89..2d8f32556c 100644 --- a/voxygen/src/render/renderer/shaders.rs +++ b/voxygen/src/render/renderer/shaders.rs @@ -68,6 +68,10 @@ impl assets::Compound for Shaders { "lod-terrain-frag", "clouds-vert", "clouds-frag", + "dual-downsample-filtered-frag", + "dual-downsample-frag", + "dual-upsample-frag", + "clouds-frag", "postprocess-vert", "postprocess-frag", "blit-vert", diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index ed21b503da..9ba998f158 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -196,7 +196,10 @@ fn handle_main_events_cleared( last.render(&mut drawer, &global_state.settings); #[cfg(feature = "egui-ui")] - if last.egui_enabled() && global_state.settings.interface.toggle_debug { + if last.egui_enabled() + && global_state.settings.interface.toggle_debug + && global_state.settings.interface.toggle_egui_debug + { drawer.draw_egui(&mut global_state.egui_state.platform, scale_factor); } }; diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index 87da4fbfee..b5db7df96d 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -12,6 +12,7 @@ const FIRST_PERSON_INTERP_TIME: f32 = 0.1; const THIRD_PERSON_INTERP_TIME: f32 = 0.1; const FREEFLY_INTERP_TIME: f32 = 0.0; const LERP_ORI_RATE: f32 = 15.0; +const CLIPPING_MODE_DISTANCE: f32 = 20.0; pub const MIN_ZOOM: f32 = 0.1; // Possible TODO: Add more modes @@ -347,7 +348,7 @@ impl Camera { /// Compute the transformation matrices (view matrix and projection matrix) /// and position of the camera. pub fn compute_dependents(&mut self, terrain: &TerrainGrid) { - self.compute_dependents_full(terrain, |block| !block.is_opaque()) + self.compute_dependents_full(terrain, |block| block.is_opaque()) } /// The is_fluid argument should return true for transparent voxels. @@ -357,6 +358,82 @@ impl Camera { is_transparent: fn(&V::Vox) -> bool, ) { span!(_guard, "compute_dependents", "Camera::compute_dependents"); + // TODO: More intelligent function to decide on which strategy to use + if self.tgt_dist < CLIPPING_MODE_DISTANCE { + self.compute_dependents_near(terrain, is_transparent) + } else { + self.compute_dependents_far(terrain, is_transparent) + } + } + + fn compute_dependents_near( + &mut self, + terrain: &V, + is_transparent: fn(&V::Vox) -> bool, + ) { + const FRUSTUM_PADDING: [Vec3; 4] = [ + Vec3::new(0.0, 0.0, -1.0), + Vec3::new(0.0, 0.0, 1.0), + Vec3::new(0.0, 0.0, -1.0), + Vec3::new(0.0, 0.0, 1.0), + ]; + // Calculate new frustom location as there may have been lerp towards tgt_dist + // Without this, there will be camera jumpig back and forth in some scenarios + // TODO: Optimize and fix clipping still happening if self.dist << self.tgt_dist + + // Use tgt_dist, as otherwise we end up in loop due to dist depending on frustum + // and vice versa + let local_dependents = self.compute_dependents_helper(self.tgt_dist); + let frustum = self.compute_frustum(&local_dependents); + let dist = { + frustum + .points + .iter() + .take(4) + .zip(FRUSTUM_PADDING.iter()) + .map(|(pos, padding)| { + let fwd = self.forward(); + pos + 0.6 * (fwd.cross(*padding) + fwd.cross(*padding).cross(fwd)) + }) + .chain([(self.focus - self.forward() * (self.dist + 0.5))]) // Padding to behind + .map(|pos| { + match terrain + .ray(self.focus, pos) + .ignore_error() + .max_iter(500) + .until(is_transparent) + .cast() + { + (d, Ok(Some(_))) => f32::min(d, self.tgt_dist), + (_, Ok(None)) => self.dist, + (_, Err(_)) => self.dist, + } + .max(0.0) + }) + .reduce(f32::min) + .unwrap_or(0.0) + }; + + if self.dist >= dist { + self.dist = dist; + } + + // Recompute only if needed + if (dist - self.tgt_dist).abs() > f32::EPSILON { + let dependents = self.compute_dependents_helper(dist); + self.frustum = self.compute_frustum(&dependents); + self.dependents = dependents; + } else { + self.dependents = local_dependents; + self.frustum = frustum; + } + } + + fn compute_dependents_far( + &mut self, + terrain: &V, + is_transparent: fn(&V::Vox) -> bool, + ) { let dist = { let (start, end) = (self.focus - self.forward() * self.dist, self.focus); @@ -364,7 +441,7 @@ impl Camera { .ray(start, end) .ignore_error() .max_iter(500) - .until(is_transparent) + .until(|b| !is_transparent(b)) .cast() { (d, Ok(Some(_))) => f32::min(self.dist - d - 0.03, self.dist), @@ -374,34 +451,47 @@ impl Camera { .max(0.0) }; - self.dependents.view_mat = Mat4::::identity() + let dependents = self.compute_dependents_helper(dist); + self.frustum = self.compute_frustum(&dependents); + self.dependents = dependents; + } + + fn compute_dependents_helper(&self, dist: f32) -> Dependents { + let view_mat = Mat4::::identity() * Mat4::translation_3d(-Vec3::unit_z() * dist) * Mat4::rotation_z(self.ori.z) * Mat4::rotation_x(self.ori.y) * Mat4::rotation_y(self.ori.x) * Mat4::rotation_3d(PI / 2.0, -Vec4::unit_x()) * Mat4::translation_3d(-self.focus.map(|e| e.fract())); - self.dependents.view_mat_inv = self.dependents.view_mat.inverted(); + let view_mat_inv: Mat4 = view_mat.inverted(); // NOTE: We reverse the far and near planes to produce an inverted depth // buffer (1 to 0 z planes). - self.dependents.proj_mat = + let proj_mat = perspective_rh_zo_general(self.fov, self.aspect, 1.0 / FAR_PLANE, 1.0 / NEAR_PLANE); // For treeculler, we also produce a version without inverted depth. - self.dependents.proj_mat_treeculler = + let proj_mat_treeculler = perspective_rh_zo_general(self.fov, self.aspect, 1.0 / NEAR_PLANE, 1.0 / FAR_PLANE); - self.dependents.proj_mat_inv = self.dependents.proj_mat.inverted(); - // TODO: Make this more efficient. - self.dependents.cam_pos = Vec3::from(self.dependents.view_mat_inv * Vec4::unit_w()); - self.frustum = Frustum::from_modelview_projection( - (self.dependents.proj_mat_treeculler - * self.dependents.view_mat + Dependents { + view_mat, + view_mat_inv, + proj_mat, + proj_mat_inv: proj_mat.inverted(), + proj_mat_treeculler, + cam_pos: Vec3::from(view_mat_inv * Vec4::unit_w()), + cam_dir: Vec3::from(view_mat_inv * -Vec4::unit_z()), + } + } + + fn compute_frustum(&mut self, dependents: &Dependents) -> Frustum { + Frustum::from_modelview_projection( + (dependents.proj_mat_treeculler + * dependents.view_mat * Mat4::translation_3d(-self.focus.map(|e| e.trunc()))) .into_col_arrays(), - ); - - self.dependents.cam_dir = Vec3::from(self.dependents.view_mat_inv * -Vec4::unit_z()); + ) } pub fn frustum(&self) -> &Frustum { &self.frustum } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 9fac2b5fff..92dc046bbf 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -518,7 +518,7 @@ impl FigureMgr { let ray_direction = scene_data.get_sun_dir(); let is_daylight = ray_direction.z < 0.0/*0.6*/; // Are shadows enabled at all? - let can_shadow_sun = renderer.render_mode().shadow.is_map() && is_daylight; + let can_shadow_sun = renderer.pipeline_modes().shadow.is_map() && is_daylight; let Dependents { proj_mat: _, view_mat: _, @@ -973,7 +973,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -1064,7 +1064,7 @@ impl FigureMgr { StageSection::Charge => { stage_time / s.static_data.charge_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -1094,7 +1094,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Shoot => { + StageSection::Action => { stage_time / s.static_data.shoot_duration.as_secs_f32() }, StageSection::Recover => { @@ -1151,7 +1151,7 @@ impl FigureMgr { StageSection::Charge => { stage_time / s.static_data.charge_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -1178,7 +1178,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -1206,7 +1206,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Cast => { + StageSection::Action => { stage_time / s.static_data.cast_duration.as_secs_f32() }, StageSection::Recover => { @@ -1237,7 +1237,7 @@ impl FigureMgr { StageSection::Movement => { stage_time / s.static_data.movement_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -1266,7 +1266,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -1348,7 +1348,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Cast => s.timer.as_secs_f32(), + StageSection::Action => s.timer.as_secs_f32(), StageSection::Recover => { stage_time / s.static_data.recover_duration.as_secs_f32() }, @@ -1377,7 +1377,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / stage.base_buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / stage.base_swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -1434,7 +1434,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => stage_time, + StageSection::Action => stage_time, StageSection::Recover => { stage_time / s.static_data.recover_duration.as_secs_f32() }, @@ -1462,7 +1462,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Use => stage_time, + StageSection::Action => stage_time, StageSection::Recover => { stage_time / s.static_data.recover_duration.as_secs_f32() }, @@ -1572,10 +1572,10 @@ impl FigureMgr { skeleton_attr, ) }, - CharacterState::GlideWield { .. } => { + CharacterState::GlideWield(data) => { anim::character::GlideWieldAnimation::update_skeleton( &target_base, - (), + (ori, data.ori.into()), state.state_time, &mut state_animation_rate, skeleton_attr, @@ -1711,7 +1711,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / stage.base_buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / stage.base_swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -1900,7 +1900,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -1929,7 +1929,7 @@ impl FigureMgr { stage_time / s.static_data.buildup_duration.as_secs_f32() }, StageSection::Charge => stage_time, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -1959,7 +1959,7 @@ impl FigureMgr { StageSection::Movement => { stage_time / s.static_data.movement_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -1989,7 +1989,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / stage.base_buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / stage.base_swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -2231,7 +2231,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -2261,7 +2261,7 @@ impl FigureMgr { StageSection::Charge => { stage_time / s.static_data.charge_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -2336,7 +2336,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / stage.base_buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / stage.base_swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -2392,7 +2392,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Cast => s.timer.as_secs_f32(), + StageSection::Action => s.timer.as_secs_f32(), StageSection::Recover => { stage_time / s.static_data.recover_duration.as_secs_f32() }, @@ -2418,7 +2418,7 @@ impl FigureMgr { stage_time / s.static_data.buildup_duration.as_secs_f32() }, StageSection::Charge => stage_time, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -2772,7 +2772,7 @@ impl FigureMgr { StageSection::Charge => { stage_time / s.static_data.charge_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -2907,7 +2907,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / stage.base_buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / stage.base_swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -3147,7 +3147,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / stage.base_buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / stage.base_swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -3194,7 +3194,7 @@ impl FigureMgr { StageSection::Charge => { stage_time / s.static_data.charge_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -3331,7 +3331,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Cast => s.timer.as_secs_f32(), + StageSection::Action => s.timer.as_secs_f32(), StageSection::Recover => { stage_time / s.static_data.recover_duration.as_secs_f32() }, @@ -3362,7 +3362,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / stage.base_buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / stage.base_swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -3420,7 +3420,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -3443,7 +3443,7 @@ impl FigureMgr { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Cast => { + StageSection::Action => { stage_time / s.static_data.cast_duration.as_secs_f32() }, StageSection::Recover => { @@ -3475,7 +3475,7 @@ impl FigureMgr { StageSection::Charge => { stage_time / s.static_data.charge_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -3729,7 +3729,7 @@ impl FigureMgr { StageSection::Charge => { stage_time / s.static_data.charge_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -3760,7 +3760,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Cast => { + StageSection::Action => { stage_time / s.static_data.cast_duration.as_secs_f32() }, StageSection::Recover => { @@ -3791,7 +3791,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -3951,7 +3951,7 @@ impl FigureMgr { StageSection::Charge => { stage_time / s.static_data.charge_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -3983,7 +3983,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / stage.base_buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / stage.base_swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -4047,7 +4047,7 @@ impl FigureMgr { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -4078,7 +4078,7 @@ impl FigureMgr { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Cast => { + StageSection::Action => { stage_time / s.static_data.cast_duration.as_secs_f32() }, StageSection::Recover => { @@ -4115,7 +4115,7 @@ impl FigureMgr { stage_time / s.static_data.movement_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -4148,7 +4148,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -4176,7 +4176,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Cast => s.timer.as_secs_f32(), + StageSection::Action => s.timer.as_secs_f32(), StageSection::Recover => { stage_time / s.static_data.recover_duration.as_secs_f32() }, @@ -4204,7 +4204,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Cast => { + StageSection::Action => { stage_time / s.static_data.cast_duration.as_secs_f32() }, StageSection::Recover => { @@ -4329,7 +4329,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / stage.base_buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / stage.base_swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -4378,7 +4378,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Cast => s.timer.as_secs_f32(), + StageSection::Action => s.timer.as_secs_f32(), StageSection::Recover => { stage_time / s.static_data.recover_duration.as_secs_f32() }, @@ -4400,7 +4400,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -4423,7 +4423,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -4446,7 +4446,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Swing => { + StageSection::Action => { stage_time / s.static_data.swing_duration.as_secs_f32() }, StageSection::Recover => { @@ -4563,7 +4563,7 @@ impl FigureMgr { StageSection::Buildup => { stage_time / s.static_data.buildup_duration.as_secs_f32() }, - StageSection::Cast => s.timer.as_secs_f32(), + StageSection::Action => s.timer.as_secs_f32(), StageSection::Recover => { stage_time / s.static_data.recover_duration.as_secs_f32() }, diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 2bbe317bb9..fd6eee9685 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -684,7 +684,7 @@ impl Scene { let sun_dir = scene_data.get_sun_dir(); let is_daylight = sun_dir.z < 0.0; - if renderer.render_mode().shadow.is_map() && (is_daylight || !lights.is_empty()) { + if renderer.pipeline_modes().shadow.is_map() && (is_daylight || !lights.is_empty()) { let fov = self.camera.get_fov(); let aspect_ratio = self.camera.get_aspect_ratio(); @@ -1065,7 +1065,7 @@ impl Scene { let camera_data = (&self.camera, scene_data.figure_lod_render_distance); // would instead have this as an extension. - if drawer.render_mode().shadow.is_map() && (is_daylight || !self.light_data.is_empty()) { + if drawer.pipeline_modes().shadow.is_map() && (is_daylight || !self.light_data.is_empty()) { if is_daylight { prof_span!("directed shadows"); if let Some(mut shadow_pass) = drawer.shadow_pass() { diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 709ce43777..32e072e2e7 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -621,7 +621,7 @@ impl ParticleMgr { if let Some(specifier) = spin.static_data.specifier { match specifier { states::spin_melee::FrontendSpecifier::CultistVortex => { - if matches!(spin.stage_section, StageSection::Swing) { + if matches!(spin.stage_section, StageSection::Action) { let range = spin.static_data.range; // Particles for vortex let heartbeats = @@ -730,7 +730,7 @@ impl ParticleMgr { CharacterState::SelfBuff(c) => { use buff::BuffKind; if let BuffKind::Frenzied = c.static_data.buff_kind { - if matches!(c.stage_section, StageSection::Cast) { + if matches!(c.stage_section, StageSection::Action) { self.particles.resize_with( self.particles.len() + usize::from( diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index 50c923385a..7381a920ac 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -229,7 +229,7 @@ impl Scene { scene_data.mouse_smoothing, ); - self.camera.compute_dependents_full(&VoidVol, |_| true); + self.camera.compute_dependents_full(&VoidVol, |_| false); let camera::Dependents { view_mat, proj_mat, diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index dae0847392..31d06afd4e 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -1244,7 +1244,7 @@ impl Terrain { return min.partial_cmple(&max).reduce_and(); }; let (visible_light_volume, visible_psr_bounds) = if ray_direction.z < 0.0 - && renderer.render_mode().shadow.is_map() + && renderer.pipeline_modes().shadow.is_map() { let visible_bounding_box = math::Aabb:: { min: math::Vec3::from(visible_bounding_box.min - focus_off), diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index f64aed6221..07114633fb 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -35,6 +35,7 @@ use common_net::{ use crate::{ audio::sfx::SfxEvent, + error::Error, game_input::GameInput, hud::{DebugInfo, Event as HudEvent, Hud, HudInfo, LootMessage, PromptDialogSettings}, key_state::KeyState, @@ -43,7 +44,7 @@ use crate::{ scene::{camera, terrain::Interaction, CameraMode, DebugShapeId, Scene, SceneData}, settings::Settings, window::{AnalogGameInput, Event}, - Direction, Error, GlobalState, PlayState, PlayStateResult, + Direction, GlobalState, PlayState, PlayStateResult, }; use hashbrown::HashMap; use settings_change::Language::ChangeLanguage; @@ -582,9 +583,11 @@ impl PlayState for SessionState { } }, GameInput::Fly => { - // Not sure where to put comment, but I noticed when testing flight - // Syncing of inputs between mounter and mountee broke with - // controller change + // Not sure where to put comment, but I noticed + // when testing flight. + // + // Syncing of inputs between mounter and mountee + // broke with controller change self.key_state.fly ^= state; let mut client = self.client.borrow_mut(); client.handle_input( @@ -977,40 +980,36 @@ impl PlayState for SessionState { .camera_mut() .compute_dependents(&*self.client.borrow().state().terrain()); - // Generate debug info, if needed (it iterates through enough data that we might + // Generate debug info, if needed + // (it iterates through enough data that we might // as well avoid it unless we need it). - let debug_info = global_state - .settings - .interface - .toggle_debug - .then(|| DebugInfo { + let debug_info = global_state.settings.interface.toggle_debug.then(|| { + let client = self.client.borrow(); + let ecs = client.state().ecs(); + let entity = client.entity(); + let coordinates = ecs.read_storage::().get(entity).cloned(); + let velocity = ecs.read_storage::().get(entity).cloned(); + let ori = ecs.read_storage::().get(entity).cloned(); + let look_dir = self.inputs.look_dir; + let in_fluid = ecs + .read_storage::() + .get(entity) + .and_then(|state| state.in_fluid); + let character_state = ecs + .read_storage::() + .get(entity) + .cloned(); + + DebugInfo { tps: global_state.clock.stats().average_tps, frame_time: global_state.clock.stats().average_busy_dt, ping_ms: self.client.borrow().get_ping_ms_rolling_avg(), - coordinates: self - .client - .borrow() - .state() - .ecs() - .read_storage::() - .get(self.client.borrow().entity()) - .cloned(), - velocity: self - .client - .borrow() - .state() - .ecs() - .read_storage::() - .get(self.client.borrow().entity()) - .cloned(), - ori: self - .client - .borrow() - .state() - .ecs() - .read_storage::() - .get(self.client.borrow().entity()) - .cloned(), + coordinates, + velocity, + ori, + look_dir, + character_state, + in_fluid, num_chunks: self.scene.terrain().chunk_count() as u32, num_lights: self.scene.lights().len() as u32, num_visible_chunks: self.scene.terrain().visible_chunk_count() as u32, @@ -1020,7 +1019,8 @@ impl PlayState for SessionState { num_particles: self.scene.particle_mgr().particle_count() as u32, num_particles_visible: self.scene.particle_mgr().particle_count_visible() as u32, - }); + } + }); // Extract HUD events ensuring the client borrow gets dropped. let mut hud_events = self.hud.maintain( @@ -1482,6 +1482,11 @@ impl PlayState for SessionState { second_pass.draw_clouds(); } } + // Bloom (call does nothing if bloom is off) + { + prof_span!("bloom"); + drawer.run_bloom_passes() + } // PostProcess and UI { prof_span!("post-process and ui"); diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs index 6f8239c141..7d4b63b11e 100644 --- a/voxygen/src/session/settings_change.rs +++ b/voxygen/src/session/settings_change.rs @@ -6,7 +6,6 @@ use crate::{ BarNumbers, BuffPosition, ChatTab, CrosshairType, Intro, PressBehavior, ScaleChange, ShortcutNumbers, XpBar, }, - i18n::{LanguageMetadata, LocalizationHandle}, render::RenderMode, settings::{ AudioSettings, ChatSettings, ControlSettings, Fps, GamepadSettings, GameplaySettings, @@ -15,6 +14,7 @@ use crate::{ window::FullScreenSettings, GlobalState, }; +use i18n::{LanguageMetadata, LocalizationHandle}; use vek::*; #[derive(Clone)] @@ -207,8 +207,8 @@ impl SettingsChange { SettingsChange::Chat(chat_change) => { let chat_tabs = &mut settings.chat.chat_tabs; match chat_change { - Chat::Transp(chat_transp) => { - settings.chat.chat_transp = chat_transp; + Chat::Transp(chat_opacity) => { + settings.chat.chat_opacity = chat_opacity; }, Chat::CharName(chat_char_name) => { settings.chat.chat_character_name = chat_char_name; @@ -453,8 +453,8 @@ impl SettingsChange { Interface::ToggleTips(loading_tips) => { settings.interface.loading_tips = loading_tips; }, - Interface::CrosshairTransp(crosshair_transp) => { - settings.interface.crosshair_transp = crosshair_transp; + Interface::CrosshairTransp(crosshair_opacity) => { + settings.interface.crosshair_opacity = crosshair_opacity; }, Interface::CrosshairType(crosshair_type) => { settings.interface.crosshair_type = crosshair_type; diff --git a/voxygen/src/settings/chat.rs b/voxygen/src/settings/chat.rs index 3df61e1964..ae16c71190 100644 --- a/voxygen/src/settings/chat.rs +++ b/voxygen/src/settings/chat.rs @@ -69,7 +69,7 @@ impl Default for ChatFilter { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(default)] pub struct ChatSettings { - pub chat_transp: f32, + pub chat_opacity: f32, pub chat_character_name: bool, pub chat_tabs: Vec, pub chat_tab_index: Option, @@ -78,7 +78,7 @@ pub struct ChatSettings { impl Default for ChatSettings { fn default() -> Self { Self { - chat_transp: 0.4, + chat_opacity: 0.4, chat_character_name: true, chat_tabs: vec![ChatTab::default()], chat_tab_index: Some(0), diff --git a/voxygen/src/settings/control.rs b/voxygen/src/settings/control.rs index 213a82f1f3..faa53ba8f1 100644 --- a/voxygen/src/settings/control.rs +++ b/voxygen/src/settings/control.rs @@ -144,6 +144,8 @@ impl ControlSettings { GameInput::Help => KeyMouse::Key(VirtualKeyCode::F1), GameInput::ToggleInterface => KeyMouse::Key(VirtualKeyCode::F2), GameInput::ToggleDebug => KeyMouse::Key(VirtualKeyCode::F3), + #[cfg(feature = "egui-ui")] + GameInput::ToggleEguiDebug => KeyMouse::Key(VirtualKeyCode::F7), GameInput::ToggleChat => KeyMouse::Key(VirtualKeyCode::F5), GameInput::Fullscreen => KeyMouse::Key(VirtualKeyCode::F11), GameInput::Screenshot => KeyMouse::Key(VirtualKeyCode::F4), diff --git a/voxygen/src/settings/gamepad.rs b/voxygen/src/settings/gamepad.rs index 1fe8b25739..4cc7d0e78d 100644 --- a/voxygen/src/settings/gamepad.rs +++ b/voxygen/src/settings/gamepad.rs @@ -77,6 +77,8 @@ pub mod con_settings { pub help: Button, pub toggle_interface: Button, pub toggle_debug: Button, + #[cfg(feature = "egui-ui")] + pub toggle_egui_debug: Button, pub toggle_chat: Button, pub fullscreen: Button, pub screenshot: Button, @@ -168,6 +170,8 @@ pub mod con_settings { help: Button::Simple(GilButton::Unknown), toggle_interface: Button::Simple(GilButton::Unknown), toggle_debug: Button::Simple(GilButton::Unknown), + #[cfg(feature = "egui-ui")] + toggle_egui_debug: Button::Simple(GilButton::Unknown), toggle_chat: Button::Simple(GilButton::Unknown), fullscreen: Button::Simple(GilButton::Unknown), screenshot: Button::Simple(GilButton::Unknown), diff --git a/voxygen/src/settings/interface.rs b/voxygen/src/settings/interface.rs index b85caac831..673caa753a 100644 --- a/voxygen/src/settings/interface.rs +++ b/voxygen/src/settings/interface.rs @@ -10,6 +10,7 @@ use vek::*; #[serde(default)] pub struct InterfaceSettings { pub toggle_debug: bool, + pub toggle_egui_debug: bool, pub toggle_hitboxes: bool, pub toggle_chat: bool, pub sct: bool, @@ -17,7 +18,7 @@ pub struct InterfaceSettings { pub sct_damage_batch: bool, pub speech_bubble_dark_mode: bool, pub speech_bubble_icon: bool, - pub crosshair_transp: f32, + pub crosshair_opacity: f32, pub crosshair_type: CrosshairType, pub intro_show: Intro, pub xp_bar: XpBar, @@ -46,6 +47,7 @@ impl Default for InterfaceSettings { fn default() -> Self { Self { toggle_debug: false, + toggle_egui_debug: false, toggle_hitboxes: false, toggle_chat: true, sct: true, @@ -53,7 +55,7 @@ impl Default for InterfaceSettings { sct_damage_batch: false, speech_bubble_dark_mode: false, speech_bubble_icon: true, - crosshair_transp: 0.6, + crosshair_opacity: 0.6, crosshair_type: CrosshairType::Round, intro_show: Intro::Show, xp_bar: XpBar::Always, diff --git a/voxygen/src/settings/language.rs b/voxygen/src/settings/language.rs index b8e3f8b20e..28fd1840e3 100644 --- a/voxygen/src/settings/language.rs +++ b/voxygen/src/settings/language.rs @@ -1,4 +1,3 @@ -use crate::i18n; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/voxygen/src/singleplayer.rs b/voxygen/src/singleplayer.rs index cb9fd50f20..44e0a789c3 100644 --- a/voxygen/src/singleplayer.rs +++ b/voxygen/src/singleplayer.rs @@ -1,4 +1,4 @@ -use common::{clock::Clock, consts::MIN_RECOMMENDED_TOKIO_THREADS}; +use common::clock::Clock; use crossbeam_channel::{bounded, unbounded, Receiver, Sender, TryRecvError}; use server::{ persistence::{DatabaseSettings, SqlLogMode}, @@ -6,14 +6,14 @@ use server::{ }; use std::{ sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, + atomic::{AtomicBool, Ordering}, Arc, }, thread::{self, JoinHandle}, time::Duration, }; use tokio::runtime::Runtime; -use tracing::{debug, error, info, trace, warn}; +use tracing::{error, info, trace, warn}; const TPS: u64 = 30; @@ -22,7 +22,7 @@ const TPS: u64 = 30; pub struct Singleplayer { _server_thread: JoinHandle<()>, stop_server_s: Sender<()>, - pub receiver: Receiver, ServerError>>, + pub receiver: Receiver>, // Wether the server is stopped or not paused: Arc, // Settings that the server was started with @@ -30,8 +30,7 @@ pub struct Singleplayer { } impl Singleplayer { - #[allow(clippy::new_without_default)] - pub fn new() -> Self { + pub fn new(runtime: &Arc) -> Self { let (stop_server_s, stop_server_r) = unbounded(); // Determine folder to save server data in @@ -82,22 +81,6 @@ impl Singleplayer { let settings = server::Settings::singleplayer(&server_data_dir); let editable_settings = server::EditableSettings::singleplayer(&server_data_dir); - // TODO: evaluate std::thread::available_concurrency as a num_cpus replacement - let cores = num_cpus::get(); - debug!("Creating a new runtime for server"); - let runtime = Arc::new( - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .worker_threads((cores / 4).max(MIN_RECOMMENDED_TOKIO_THREADS)) - .thread_name_fn(|| { - static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0); - let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); - format!("tokio-sp-{}", id) - }) - .build() - .unwrap(), - ); - let settings2 = settings.clone(); // Relative to data_dir @@ -117,39 +100,32 @@ impl Singleplayer { let (result_sender, result_receiver) = bounded(1); let builder = thread::Builder::new().name("singleplayer-server-thread".into()); + let runtime = Arc::clone(runtime); let thread = builder .spawn(move || { trace!("starting singleplayer server thread"); - let mut server = None; - if let Err(e) = result_sender.send( - match Server::new( - settings2, - editable_settings, - database_settings, - &server_data_dir, - Arc::clone(&runtime), - ) { - Ok(s) => { - server = Some(s); - Ok(runtime) - }, - Err(e) => Err(e), - }, + + let (server, init_result) = match Server::new( + settings2, + editable_settings, + database_settings, + &server_data_dir, + runtime, ) { - warn!( + Ok(server) => (Some(server), Ok(())), + Err(err) => (None, Err(err)), + }; + + match (result_sender.send(init_result), server) { + (Err(e), _) => warn!( ?e, "Failed to send singleplayer server initialization result. Most likely \ the channel was closed by cancelling server creation. Stopping Server" - ); - return; - }; + ), + (Ok(()), None) => (), + (Ok(()), Some(server)) => run_server(server, stop_server_r, paused1), + } - let server = match server { - Some(s) => s, - None => return, - }; - - run_server(server, stop_server_r, paused1); trace!("ending singleplayer server thread"); }) .unwrap(); diff --git a/voxygen/src/ui/cache.rs b/voxygen/src/ui/cache.rs index dcf5041b8e..fd807d48e6 100644 --- a/voxygen/src/ui/cache.rs +++ b/voxygen/src/ui/cache.rs @@ -1,7 +1,7 @@ use super::graphic::{Graphic, GraphicCache, Id as GraphicId}; use crate::{ + error::Error, render::{Mesh, Renderer, Texture, UiTextureBindGroup, UiVertex}, - Error, }; use conrod_core::{text::GlyphCache, widget::Id}; use hashbrown::HashMap; diff --git a/voxygen/src/ui/fonts.rs b/voxygen/src/ui/fonts.rs index 1bd9ac45a1..fe3716542d 100644 --- a/voxygen/src/ui/fonts.rs +++ b/voxygen/src/ui/fonts.rs @@ -1,4 +1,4 @@ -use crate::{i18n, ui::ice::RawFont}; +use crate::ui::ice::RawFont; use common::assets::{self, AssetExt}; pub struct Font { diff --git a/voxygen/src/ui/ice/cache.rs b/voxygen/src/ui/ice/cache.rs index aeb629bc88..e7631e10a4 100644 --- a/voxygen/src/ui/ice/cache.rs +++ b/voxygen/src/ui/ice/cache.rs @@ -1,7 +1,7 @@ use super::graphic::{Graphic, GraphicCache, Id as GraphicId}; use crate::{ + error::Error, render::{Renderer, Texture, UiTextureBindGroup}, - Error, }; use common::assets::{self, AssetExt}; use glyph_brush::GlyphBrushBuilder; diff --git a/voxygen/src/ui/ice/mod.rs b/voxygen/src/ui/ice/mod.rs index 91393ad0cf..d28ef981d9 100644 --- a/voxygen/src/ui/ice/mod.rs +++ b/voxygen/src/ui/ice/mod.rs @@ -15,9 +15,9 @@ use super::{ scale::{Scale, ScaleMode}, }; use crate::{ + error::Error, render::{Renderer, UiDrawer}, window::Window, - Error, }; use common::slowjob::SlowJobPool; use common_base::span; diff --git a/voxygen/src/ui/ice/renderer/mod.rs b/voxygen/src/ui/ice/renderer/mod.rs index 51f4fbf494..2c6ff6014c 100644 --- a/voxygen/src/ui/ice/renderer/mod.rs +++ b/voxygen/src/ui/ice/renderer/mod.rs @@ -14,11 +14,11 @@ use super::{ Font, FontId, RawFont, Rotation, }; use crate::{ + error::Error, render::{ create_ui_quad, create_ui_quad_vert_gradient, DynamicModel, Mesh, Renderer, UiBoundLocals, UiDrawer, UiLocals, UiMode, UiVertex, }, - Error, }; use common::{slowjob::SlowJobPool, util::srgba_to_linear}; use common_base::span; diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index 574dd6ed8c..65b57fb139 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -28,12 +28,12 @@ pub use widgets::{ }; use crate::{ + error::Error, render::{ create_ui_quad, create_ui_tri, DynamicModel, Mesh, RenderError, Renderer, UiBoundLocals, UiDrawer, UiLocals, UiMode, UiVertex, }, window::Window, - Error, }; #[rustfmt::skip] use ::image::GenericImageView; diff --git a/voxygen/src/ui/widgets/item_tooltip.rs b/voxygen/src/ui/widgets/item_tooltip.rs index 4dcd6f7cf0..86c3ef1ad4 100644 --- a/voxygen/src/ui/widgets/item_tooltip.rs +++ b/voxygen/src/ui/widgets/item_tooltip.rs @@ -1,12 +1,9 @@ use super::image_frame::ImageFrame; -use crate::{ - hud::{ - get_quality_col, - img_ids::Imgs, - item_imgs::{animate_by_pulse, ItemImgs, ItemKey}, - util, - }, - i18n::Localization, +use crate::hud::{ + get_quality_col, + img_ids::Imgs, + item_imgs::{animate_by_pulse, ItemImgs, ItemKey}, + util, }; use client::Client; use common::{ @@ -22,6 +19,7 @@ use conrod_core::{ widget, widget_ids, Color, Colorable, FontSize, Positionable, Scalar, Sizeable, Ui, UiCell, Widget, WidgetCommon, WidgetStyle, }; +use i18n::Localization; use lazy_static::lazy_static; use std::time::{Duration, Instant}; diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 6df984ff32..409407b06f 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -1,9 +1,10 @@ use crate::{ controller::*, + error::Error, game_input::GameInput, render::Renderer, settings::{ControlSettings, Settings}, - ui, Error, + ui, }; use common_base::span; use crossbeam_channel as channel; @@ -106,6 +107,7 @@ pub enum KeyMouse { } impl KeyMouse { + /// Returns key description (e.g Left Shift) pub fn display_string(&self, key_layout: &Option) -> String { use self::KeyMouse::*; use winit::event::{MouseButton, VirtualKeyCode::*}; @@ -225,6 +227,7 @@ impl KeyMouse { Key(MediaSelect) => "MediaSelect", Key(MediaStop) => "MediaStop", Key(Minus) => "-", + Key(Plus) => "+", Key(NumpadMultiply) => "Numpad *", Key(Mute) => "Mute", Key(MyComputer) => "My Computer", @@ -322,7 +325,6 @@ impl KeyMouse { Key(Paste) => "Paste", Key(Cut) => "Cut", Key(Asterisk) => "*", - Key(Plus) => "+", Mouse(MouseButton::Left) => "Left Click", Mouse(MouseButton::Right) => "Right Click", Mouse(MouseButton::Middle) => "Middle Click", @@ -339,7 +341,30 @@ impl KeyMouse { }, }; - String::from(key_string) + key_string.to_owned() + } + + /// Returns shortened key name (e.g. Left Click -> LMB) + /// + /// Use it in case if space does really matter. + pub fn display_shortened(&self, _key_layout: &Option) -> Option { + use self::KeyMouse::*; + use winit::event::{MouseButton, VirtualKeyCode::*}; + let key_string = match self { + Mouse(MouseButton::Left) => "M1", + Mouse(MouseButton::Right) => "M2", + Mouse(MouseButton::Middle) => "M3", + Mouse(MouseButton::Other(button)) => { + // Additional mouse buttons after middle click start at 1 + return Some(format!("M{}", button + 3)); + }, + Key(Back) => "Back", + Key(LShift) => "LShft", + Key(RShift) => "RShft", + _ => return None, + }; + + Some(key_string.to_owned()) } } @@ -373,7 +398,10 @@ pub struct Window { } impl Window { - pub fn new(settings: &Settings) -> Result<(Window, EventLoop), Error> { + pub fn new( + settings: &Settings, + runtime: &tokio::runtime::Runtime, + ) -> Result<(Window, EventLoop), Error> { let event_loop = EventLoop::new(); let size = settings.graphics.window_size; @@ -393,7 +421,7 @@ impl Window { let window = win_builder.build(&event_loop).unwrap(); - let renderer = Renderer::new(&window, settings.graphics.render_mode.clone())?; + let renderer = Renderer::new(&window, settings.graphics.render_mode.clone(), runtime)?; let keypress_map = HashMap::new(); @@ -766,8 +794,7 @@ impl Window { let physical = self.window.inner_size(); self.renderer - .on_resize(Vec2::new(physical.width, physical.height)) - .unwrap(); + .on_resize(Vec2::new(physical.width, physical.height)); // TODO: update users of this event with the fact that it is now the physical // size let winit::dpi::PhysicalSize { width, height } = physical; diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 3b0bbae244..3455861297 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -498,7 +498,8 @@ impl Floor { ctx.rng.gen_range(0..floor_sz + 1 - room_sz) }); let area = Rect::from((pos, Extent2::from(sz))); - let area_border = Rect::from((pos - 1, Extent2::from(sz) + 2)); // The room, but with some personal space + // The room, but with some personal space + let area_border = Rect::from((pos - 1, Extent2::from(sz) + 2)); // Ensure no overlap if self @@ -514,9 +515,8 @@ impl Floor { Some(area) => area, None => return, }; - let mut dynamic_rng = rand::thread_rng(); - match dynamic_rng.gen_range(0..5) { + match ctx.rng.gen_range(0..5) { // Miniboss room 0 => self.create_room(Room { seed: ctx.rng.gen(), @@ -1264,14 +1264,15 @@ impl Floor { min: (tile_center - Vec2::broadcast(1 + pillar_thickness - 1)) .with_z(floor_z), max: (tile_center + Vec2::broadcast(1 + pillar_thickness)) - .with_z(floor_z + 3), + .with_z(floor_z + 1), })); let scale = (pillar_thickness + 2) as f32 / pillar_thickness as f32; let mut lights = prim(Primitive::Scale(pillar, Vec2::broadcast(scale).with_z(1.0))); lights = prim(Primitive::And(lighting_plane, lights)); - // Only add the base (and shift the lights up) for boss-room pillars + // Only add the base (and shift the lights up) + // for boss-rooms pillars if room.kind == RoomKind::Boss { lights = prim(Primitive::Translate(lights, 3 * Vec3::unit_z())); pillar = prim(Primitive::Or(pillar, base));