diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 813eabf4c0..0a93f8de07 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,7 +8,8 @@ variables: stages: - optional-builds - check-compile - - post + - build-post + - publish before_script: - source $HOME/.cargo/env @@ -90,30 +91,30 @@ security: # -- -# -- post build +# -- build-post unittests: - stage: post + stage: build-post when: delayed start_in: 5 seconds tags: - veloren-docker script: - echo "Workaround, cargo tests fails due some rust files are already deleted, so we just stack cargo test. if its the os error, it wont appear on them all, if its a real error, it will retry and then fail" - - cargo test || cargo test || cargo test || cargo test + - cargo test || ( sleep 10 && cargo test ) || ( sleep 10 && cargo test ) || cargo test || cargo test || cargo test || cargo test || cargo test || cargo test || cargo test || cargo test || cargo test || cargo test coverage: - stage: post + stage: build-post when: delayed start_in: 5 seconds tags: - veloren-docker script: - echo "Workaround, tarpaulin fails due some rust files are already deleted, so we just stack tarpaulin. if its the os error, it wont appear on them all, if its a real error, it will retry and then fail" - - cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v + - cargo tarpaulin -v || ( sleep 10 && cargo tarpaulin -v ) || ( sleep 10 && cargo tarpaulin -v ) || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v benchmarks: - stage: post + stage: build-post when: delayed start_in: 5 seconds tags: @@ -125,7 +126,7 @@ benchmarks: localization-status: variables: GIT_DEPTH: 0 - stage: post + stage: build-post when: delayed start_in: 5 seconds allow_failure: true @@ -135,13 +136,13 @@ localization-status: - cargo test -q test_all_localizations -- --nocapture --ignored linux: - stage: post + stage: build-post when: delayed start_in: 5 seconds only: refs: - /^r[0-9]+\.[0-9]+\.[0-9]+/ - - /^v[0-9]+\.[0-9]+\.[0-9]+/ + - /^v[0-9]+\.[0-9]+/ - /^master$/ tags: - veloren-docker @@ -160,13 +161,13 @@ linux: expire_in: 1 week windows: - stage: post + stage: build-post when: delayed start_in: 5 seconds only: refs: - /^r[0-9]+\.[0-9]+\.[0-9]+/ - - /^v[0-9]+\.[0-9]+\.[0-9]+/ + - /^v[0-9]+\.[0-9]+/ - /^master$/ tags: - veloren-docker @@ -181,4 +182,53 @@ windows: - assets/ - LICENSE expire_in: 1 week + +macos: + stage: build-post + when: delayed + start_in: 5 seconds + only: + refs: + - /^r[0-9]+\.[0-9]+\.[0-9]+/ + - /^v[0-9]+\.[0-9]+/ + - /^master$/ + tags: + - veloren-docker + script: + - PATH="/dockercache/osxcross/target/bin:$PATH" COREAUDIO_SDK_PATH=/dockercache/osxcross/target/SDK/MacOSX10.13.sdk CC=o64-clang CXX=o64-clang++ cargo build --target x86_64-apple-darwin --release + - cp -r target/x86_64-apple-darwin/release/veloren-server-cli $CI_PROJECT_DIR + - cp -r target/x86_64-apple-darwin/release/veloren-voxygen $CI_PROJECT_DIR + artifacts: + paths: + - veloren-server-cli + - veloren-voxygen + - assets/ + - LICENSE + expire_in: 1 week +# -- + +# -- publish + +docker: + stage: publish + when: delayed + start_in: 5 seconds + image: + name: gcr.io/kaniko-project/executor:debug + entrypoint: [""] + dependencies: + - linux + before_script: + - ls "$CI_PROJECT_DIR/server-cli/" + only: + refs: + - /^r[0-9]+\.[0-9]+\.[0-9]+/ + - /^v[0-9]+\.[0-9]+/ + - /^master$/ + tags: + - veloren-docker + script: + - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json + - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/server-cli/Dockerfile --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}-server" + # -- diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS new file mode 100644 index 0000000000..c1aa0c58d8 --- /dev/null +++ b/.gitlab/CODEOWNERS @@ -0,0 +1,28 @@ +# Defines people who should approve to certain parts of the codebase + +/assets/ @assetsandvisualdesign @frontend +/chat-cli/ @frontend +/client/ @backend @networking +/common/ @backend @networking +/server/ @backend @networking +/server-cli/ @frontend +#/voxygen/ @someone +/voxygen/anim/ @animation +/voxygen/audio/ @audio +#/voxygen/hud/ @someone +#/voxygen/menu / @someone +#/voxygen/mesh/ @someone +/voxygen/render/ @rendering +#/voxygen/scene/ @someone +#/voxygen/ui/ @someone +/world/ @worldgen + +# All files related to documentation or game unrelated content needs to be approved by the meta group +*.md @meta +*.nix @meta +.gitignore @meta +.gitattributes @meta +.gitlab-ci.yml @meta +rust-toolchain @meta +LICENSE @meta +.cargo/ @meta \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 055492a259..cd4f119c21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added gamma setting - Added new orc hairstyles - Added sfx for wielding/unwielding weapons +- Fixed NPCs attacking the player forever after killing them +- Added sfx for collecting, dropping and using inventory items +- New attack animation +- weapon control system +- Game pauses when in singleplayer and pause menu ### Changed diff --git a/Cargo.lock b/Cargo.lock index 399d5b6a2f..083ae3808f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3377,6 +3377,7 @@ dependencies = [ "specs 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", "specs-idvs 0.1.0 (git+https://gitlab.com/veloren/specs-idvs.git)", "treeculler 0.1.0 (git+https://gitlab.com/yusdacra/treeculler.git)", + "uvth 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "vek 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)", "veloren-client 0.5.0", "veloren-common 0.5.0", diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index 53dbe54287..6c17493431 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -35,5 +35,59 @@ ], threshold: 1.0, ), + Inventory(Collected): ( + files: [ + "voxygen.audio.sfx.inventory.add_item", + ], + threshold: 0.5, + ), + Inventory(Swapped): ( + files: [ + "voxygen.audio.sfx.inventory.add_item", + ], + threshold: 0.5, + ), + Inventory(Given): ( + files: [ + "voxygen.audio.sfx.inventory.add_item", + ], + threshold: 0.5, + ), + Inventory(Dropped): ( + files: [ + "voxygen.audio.sfx.footsteps.stepgrass_4", + ], + threshold: 0.5, + ), + Inventory(Consumed(Potion)): ( + files: [ + "voxygen.audio.sfx.inventory.consumable.liquid", + ], + threshold: 0.3, + ), + Inventory(Consumed(PotionMinor)): ( + files: [ + "voxygen.audio.sfx.inventory.consumable.liquid", + ], + threshold: 0.3, + ), + Inventory(Consumed(Apple)): ( + files: [ + "voxygen.audio.sfx.inventory.consumable.apple", + ], + threshold: 0.3, + ), + Inventory(Consumed(Mushroom)): ( + files: [ + "voxygen.audio.sfx.inventory.consumable.food", + ], + threshold: 0.3, + ), + Inventory(Consumed(Cheese)): ( + files: [ + "voxygen.audio.sfx.inventory.consumable.food", + ], + threshold: 0.3, + ) } ) \ No newline at end of file diff --git a/assets/voxygen/audio/sfx/inventory/add_item.wav b/assets/voxygen/audio/sfx/inventory/add_item.wav new file mode 100644 index 0000000000..3c6f68dd07 Binary files /dev/null and b/assets/voxygen/audio/sfx/inventory/add_item.wav differ diff --git a/assets/voxygen/audio/sfx/inventory/consumable/apple.wav b/assets/voxygen/audio/sfx/inventory/consumable/apple.wav new file mode 100644 index 0000000000..4478493a55 Binary files /dev/null and b/assets/voxygen/audio/sfx/inventory/consumable/apple.wav differ diff --git a/assets/voxygen/audio/sfx/inventory/consumable/food.wav b/assets/voxygen/audio/sfx/inventory/consumable/food.wav new file mode 100644 index 0000000000..13f5fdcf1b Binary files /dev/null and b/assets/voxygen/audio/sfx/inventory/consumable/food.wav differ diff --git a/assets/voxygen/audio/sfx/inventory/consumable/liquid.wav b/assets/voxygen/audio/sfx/inventory/consumable/liquid.wav new file mode 100644 index 0000000000..6d048a79c5 Binary files /dev/null and b/assets/voxygen/audio/sfx/inventory/consumable/liquid.wav differ diff --git a/assets/voxygen/element/frames/banner_top.png b/assets/voxygen/element/frames/banner_top.png index d3561d3a9c..e75a275660 100644 Binary files a/assets/voxygen/element/frames/banner_top.png and b/assets/voxygen/element/frames/banner_top.png differ diff --git a/assets/voxygen/element/misc_bg/textbox_bot.png b/assets/voxygen/element/misc_bg/textbox_bot.png index c28435b703..330ab52d80 100644 Binary files a/assets/voxygen/element/misc_bg/textbox_bot.png and b/assets/voxygen/element/misc_bg/textbox_bot.png differ diff --git a/assets/voxygen/element/misc_bg/textbox_mid.png b/assets/voxygen/element/misc_bg/textbox_mid.png index 8c09cd780b..f1998299bc 100644 Binary files a/assets/voxygen/element/misc_bg/textbox_mid.png and b/assets/voxygen/element/misc_bg/textbox_mid.png differ diff --git a/assets/voxygen/element/misc_bg/textbox_top.png b/assets/voxygen/element/misc_bg/textbox_top.png index 8431fc58ce..8666ed9d1f 100644 Binary files a/assets/voxygen/element/misc_bg/textbox_top.png and b/assets/voxygen/element/misc_bg/textbox_top.png differ diff --git a/assets/voxygen/net.veloren.veloren.appdata.xml b/assets/voxygen/net.veloren.veloren.appdata.xml new file mode 100644 index 0000000000..1192abe8fc --- /dev/null +++ b/assets/voxygen/net.veloren.veloren.appdata.xml @@ -0,0 +1,61 @@ + + + + net.veloren.veloren.desktop + CC0-1.0 + GPL-3.0-or-later + + intense + mild + mild + mild + + Veloren + + Veloren is a multiplayer voxel RPG written in Rust. It is inspired + by games such as Cube World, Legend of Zelda: Breath of the Wild, + Dwarf Fortress and Minecraft. + + +

+ Welcome To Veloren! +

+ Veloren is a multiplayer voxel RPG written in Rust. Veloren takes + inspiration from games such as Cube World, Minecraft and Dwarf + Fortress. The game is currently under heavy development, but is + playable. +

+ Development +

+ Currently the communication of contributors happens mainly on our + official Discord server (https://discord.gg/kjwJwjK). You can join + it to keep up with the development, talk to us or contribute + something yourself. Anyone who shows genuine effort to help is + welcome in our team. You don't have to know how to program to + contribute! +

+
+ + + https://media.discordapp.net/attachments/634860358623821835/643034796548947968/screenshot_1573381825305.png + + + https://media.discordapp.net/attachments/597826574095613962/643102462781423616/screenshot_1573397958545.png + + + https://cdn.discordapp.com/attachments/634860358623821835/646518917577310219/screenshot_1574211401431.png + + + + sandbox + world + multiplayer + + https://veloren.net + https://gitlab.com/veloren/veloren/issues + https://gitlab.com/veloren/veloren#faq + https://book.veloren.net/ + + veloren-voxygen + +
diff --git a/assets/voxygen/net.veloren.veloren.desktop b/assets/voxygen/net.veloren.veloren.desktop new file mode 100644 index 0000000000..5e4f2cd02e --- /dev/null +++ b/assets/voxygen/net.veloren.veloren.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=Veloren +Comment=Veloren is a multiplayer voxel RPG written in Rust +Exec=veloren-voxygen +Categories=Game;Simulation; +Keywords=veloren;sandbox;world;blocks;nodes;multiplayer;roleplaying; +Icon=net.veloren.veloren.png +Terminal=false diff --git a/assets/voxygen/net.veloren.veloren.png b/assets/voxygen/net.veloren.veloren.png new file mode 100644 index 0000000000..356c33aabc Binary files /dev/null and b/assets/voxygen/net.veloren.veloren.png differ diff --git a/client/src/lib.rs b/client/src/lib.rs index b5b6ab32bc..68284d841b 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -14,6 +14,7 @@ pub use specs::{ use byteorder::{ByteOrder, LittleEndian}; use common::{ comp::{self, ControlEvent, Controller, ControllerInputs, InventoryManip}, + event::{EventBus, SfxEvent, SfxEventItem}, msg::{ validate_chat_msg, ChatMsgValidationError, ClientMsg, ClientState, PlayerListUpdate, RequestStateError, ServerError, ServerInfo, ServerMsg, MAX_BYTES_CHAT_MSG, @@ -39,11 +40,11 @@ use vek::*; // The duration of network inactivity until the player is kicked // @TODO: in the future, this should be configurable on the server // and be provided to the client -const SERVER_TIMEOUT: Duration = Duration::from_secs(20); +const SERVER_TIMEOUT: f64 = 20.0; // After this duration has elapsed, the user will begin getting kick warnings in // their chat window -const SERVER_TIMEOUT_GRACE_PERIOD: Duration = Duration::from_secs(14); +const SERVER_TIMEOUT_GRACE_PERIOD: f64 = 14.0; pub enum Event { Chat { @@ -63,8 +64,8 @@ pub struct Client { postbox: PostBox, - last_server_ping: Instant, - last_server_pong: Instant, + last_server_ping: f64, + last_server_pong: f64, last_ping_delta: f64, tick: u64, @@ -151,8 +152,8 @@ impl Client { postbox, - last_server_ping: Instant::now(), - last_server_pong: Instant::now(), + last_server_ping: 0.0, + last_server_pong: 0.0, last_ping_delta: 0.0, tick: 0, @@ -377,6 +378,7 @@ impl Client { } } } + // Handle new messages from the server. frontend_events.append(&mut self.handle_new_messages()?); @@ -479,9 +481,9 @@ impl Client { } // Send a ping to the server once every second - if Instant::now().duration_since(self.last_server_ping) > Duration::from_secs(1) { + if self.state.get_time() - self.last_server_ping > 1. { self.postbox.send_message(ClientMsg::Ping); - self.last_server_ping = Instant::now(); + self.last_server_ping = self.state.get_time(); } // 6) Update the server about the player's physics attributes. @@ -526,16 +528,14 @@ impl Client { // Check that we have an valid connection. // Use the last ping time as a 1s rate limiter, we only notify the user once per // second - if Instant::now().duration_since(self.last_server_ping) > Duration::from_secs(1) { - let duration_since_last_pong = Instant::now().duration_since(self.last_server_pong); + if self.state.get_time() - self.last_server_ping > 1. { + let duration_since_last_pong = self.state.get_time() - self.last_server_pong; // Dispatch a notification to the HUD warning they will be kicked in {n} seconds - if duration_since_last_pong.as_secs() >= SERVER_TIMEOUT_GRACE_PERIOD.as_secs() { - if let Some(seconds_until_kick) = - SERVER_TIMEOUT.checked_sub(duration_since_last_pong) - { + if duration_since_last_pong >= SERVER_TIMEOUT_GRACE_PERIOD { + if self.state.get_time() - duration_since_last_pong > 0. { frontend_events.push(Event::DisconnectionNotification( - seconds_until_kick.as_secs(), + (self.state.get_time() - duration_since_last_pong).round() as u64, )); } } @@ -589,11 +589,10 @@ impl Client { ServerMsg::Ping => self.postbox.send_message(ClientMsg::Pong), ServerMsg::Pong => { - self.last_server_pong = Instant::now(); + self.last_server_pong = self.state.get_time(); - self.last_ping_delta = Instant::now() - .duration_since(self.last_server_ping) - .as_secs_f64(); + self.last_ping_delta = + (self.state.get_time() - self.last_server_ping).round(); }, ServerMsg::ChatMsg { message, chat_type } => { frontend_events.push(Event::Chat { message, chat_type }) @@ -669,8 +668,14 @@ impl Client { self.state.write_component(entity, character_state); } }, - ServerMsg::InventoryUpdate(inventory) => { - self.state.write_component(self.entity, inventory) + ServerMsg::InventoryUpdate(inventory, event) => { + self.state.write_component(self.entity, inventory); + + self.state + .ecs() + .read_resource::>() + .emitter() + .emit(SfxEventItem::at_player_position(SfxEvent::Inventory(event))); }, ServerMsg::TerrainChunkUpdate { key, chunk } => { if let Ok(chunk) = chunk { @@ -704,7 +709,7 @@ impl Client { } else if let Some(err) = self.postbox.error() { return Err(err.into()); // We regularily ping in the tick method - } else if Instant::now().duration_since(self.last_server_pong) > SERVER_TIMEOUT { + } else if self.state.get_time() - self.last_server_pong > SERVER_TIMEOUT { return Err(Error::ServerTimeout); } Ok(frontend_events) diff --git a/common/src/comp/inventory/item.rs b/common/src/comp/inventory/item.rs index 8d5b8e91ab..27549b101e 100644 --- a/common/src/comp/inventory/item.rs +++ b/common/src/comp/inventory/item.rs @@ -70,8 +70,7 @@ pub enum Armor { Necklace, } -//TODO: Do we even need this? -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Consumable { Apple, Cheese, diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 2c37c7331d..f192208b5c 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -1,10 +1,11 @@ pub mod item; // Reexports -pub use item::{Debug, Item, ItemKind, SwordKind, ToolData, ToolKind}; +pub use item::{Consumable, Debug, Item, ItemKind, Tool}; use crate::assets; -use specs::{Component, HashMapStorage, NullStorage}; +use specs::{Component, FlaggedStorage, HashMapStorage}; +use specs_idvs::IDVStorage; use std::ops::Not; #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -136,12 +137,37 @@ impl Component for Inventory { type Storage = HashMapStorage; } -// ForceUpdate +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub enum InventoryUpdateEvent { + Init, + Used, + Consumed(Consumable), + Gave, + Given, + Swapped, + Dropped, + Collected, + Possession, + Debug, +} + +impl Default for InventoryUpdateEvent { + fn default() -> Self { Self::Init } +} + #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] -pub struct InventoryUpdate; +pub struct InventoryUpdate { + event: InventoryUpdateEvent, +} + +impl InventoryUpdate { + pub fn new(event: InventoryUpdateEvent) -> Self { Self { event } } + + pub fn event(&self) -> InventoryUpdateEvent { self.event } +} impl Component for InventoryUpdate { - type Storage = NullStorage; + type Storage = FlaggedStorage>; } #[cfg(test)] mod test; diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 0d08898d7e..940c8f4130 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -31,7 +31,8 @@ pub use controller::{ pub use energy::{Energy, EnergySource}; pub use inputs::CanBuild; pub use inventory::{ - item, Inventory, InventoryUpdate, Item, ItemKind, SwordKind, ToolData, ToolKind, + item, Inventory, InventoryUpdate, InventoryUpdateEvent, Item, ItemKind, SwordKind, ToolData, + ToolKind, }; pub use last::Last; pub use location::{Waypoint, WaypointArea}; diff --git a/common/src/event.rs b/common/src/event.rs index 53ac827c01..188c994ae8 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -1,4 +1,5 @@ use crate::{comp, sync::Uid}; +use comp::{item::Tool, InventoryUpdateEvent}; use parking_lot::Mutex; use serde::Deserialize; use specs::Entity as EcsEntity; @@ -39,8 +40,9 @@ pub enum SfxEvent { Fall, ExperienceGained, LevelUp, - Wield(comp::item::ToolKind), - Unwield(comp::item::ToolKind), + Wield(Tool), + Unwield(Tool), + Inventory(InventoryUpdateEvent), } pub enum LocalEvent { diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index a9244b15f6..0a7b84ae33 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -71,7 +71,7 @@ pub enum ServerMsg { entity: u64, character_state: comp::CharacterState, }, - InventoryUpdate(comp::Inventory), + InventoryUpdate(comp::Inventory, comp::InventoryUpdateEvent), TerrainChunkUpdate { key: Vec2, chunk: Result, ()>, diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index f2268d439c..9d8415ea28 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -153,7 +153,7 @@ impl<'a> System<'a> for Sys { been_close, .. } => { - if let (Some(tgt_pos), _tgt_stats, tgt_alignment) = ( + if let (Some(tgt_pos), Some(tgt_stats), tgt_alignment) = ( positions.get(*target), stats.get(*target), alignments @@ -164,7 +164,8 @@ impl<'a> System<'a> for Sys { // Don't attack entities we are passive towards // TODO: This is here, it's a bit of a hack if let Some(alignment) = alignment { - if (*alignment).passive_towards(tgt_alignment) { + if (*alignment).passive_towards(tgt_alignment) || tgt_stats.is_dead + { do_idle = true; break 'activity; } diff --git a/server-cli/Dockerfile b/server-cli/Dockerfile new file mode 100644 index 0000000000..eacecc7846 --- /dev/null +++ b/server-cli/Dockerfile @@ -0,0 +1,8 @@ +FROM debian:stable-slim + +ARG PROJECTNAME=server-cli + +COPY ./server-cli/docker-run.sh /opt/docker-run.sh +COPY ./veloren-server-cli /opt/veloren-server-cli +COPY ./assets/common /opt/assets/common +COPY ./assets/world /opt/assets/world diff --git a/server-cli/docker-compose.yml b/server-cli/docker-compose.yml new file mode 100644 index 0000000000..ba9badf7de --- /dev/null +++ b/server-cli/docker-compose.yml @@ -0,0 +1,17 @@ +version: "3.7" + +services: + game-server: + image: registry.gitlab.com/veloren/veloren:master-server + ports: + - "14004:14004" + - "14005:14005" + deploy: + replicas: 1 + update_config: + parallelism: 2 + delay: 10s + order: stop-first + failure_action: rollback + restart_policy: + condition: on-failure diff --git a/server-cli/docker-run.sh b/server-cli/docker-run.sh new file mode 100755 index 0000000000..e965090fdb --- /dev/null +++ b/server-cli/docker-run.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd /opt +RUST_LOG=info,common=debug,common::net=info RUST_BACKTRACE=1 /opt/veloren-server-cli diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 1f46109481..b4a2e3e5dd 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -262,6 +262,7 @@ lazy_static! { ), ]; } + fn handle_give(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) { if let Ok(item) = assets::load_cloned(&args) { server @@ -274,7 +275,10 @@ fn handle_give(server: &mut Server, entity: EcsEntity, args: String, _action: &C .state .ecs() .write_storage::() - .insert(entity, comp::InventoryUpdate); + .insert( + entity, + comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Given), + ); } else { server.notify_client(entity, ServerMsg::private(String::from("Invalid item!"))); } @@ -1113,7 +1117,10 @@ fn handle_debug(server: &mut Server, entity: EcsEntity, _args: String, _action: .state .ecs() .write_storage::() - .insert(entity, comp::InventoryUpdate); + .insert( + entity, + comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug), + ); } else { server.notify_client( entity, diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 96be66e52b..7d7e3fd124 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -106,7 +106,10 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { } } ecs.write_storage::() - .insert(possesse, comp::InventoryUpdate) + .insert( + possesse, + comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Possession), + ) .err() .map(|e| { error!( diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 4485a62fb7..09998ca6aa 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -48,7 +48,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv } } - state.write_component(entity, comp::InventoryUpdate); + state.write_component( + entity, + comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Collected), + ); }, comp::InventoryManip::Collect(pos) => { @@ -76,6 +79,8 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv .get_mut(entity) .and_then(|inv| inv.remove(slot)); + let mut event = comp::InventoryUpdateEvent::Used; + if let Some(item) = item_opt { match item.kind { comp::ItemKind::Tool { .. } => { @@ -94,7 +99,8 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv stats.equipment.main = Some(item); } }, - comp::ItemKind::Consumable { effect, .. } => { + comp::ItemKind::Consumable { kind, effect } => { + event = comp::InventoryUpdateEvent::Consumed(kind); state.apply_effect(entity, effect); }, comp::ItemKind::Utility { kind } => match kind { @@ -168,7 +174,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv } } - state.write_component(entity, comp::InventoryUpdate); + state.write_component(entity, comp::InventoryUpdate::new(event)); }, comp::InventoryManip::Swap(a, b) => { @@ -177,7 +183,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv .write_storage::() .get_mut(entity) .map(|inv| inv.swap_slots(a, b)); - state.write_component(entity, comp::InventoryUpdate); + state.write_component( + entity, + comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Swapped), + ); }, comp::InventoryManip::Drop(slot) => { @@ -201,7 +210,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv item, )); } - state.write_component(entity, comp::InventoryUpdate); + state.write_component( + entity, + comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Dropped), + ); }, } diff --git a/server/src/lib.rs b/server/src/lib.rs index 089713a679..637b11d5ec 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -282,7 +282,10 @@ impl Server { state.write_component(entity, comp::CharacterState::default()); state.write_component(entity, comp::Alignment::Owned(entity)); state.write_component(entity, comp::Inventory::default()); - state.write_component(entity, comp::InventoryUpdate); + state.write_component( + entity, + comp::InventoryUpdate::new(comp::InventoryUpdateEvent::default()), + ); state.write_component(entity, comp::AbilityPool::default()); // Make sure physics are accepted. state.write_component(entity, comp::ForceUpdate); @@ -576,6 +579,8 @@ impl Server { .get(entity) .is_some() } + + pub fn number_of_players(&self) -> i64 { self.metrics.player_online.get() } } impl Drop for Server { @@ -607,7 +612,10 @@ impl StateExt for State { .map(|inv| inv.push(item).is_none()) .unwrap_or(false); if success { - self.write_component(entity, comp::InventoryUpdate); + self.write_component( + entity, + comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Collected), + ); } success } diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index 35c7d591fc..d32eb3e1d2 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -328,8 +328,11 @@ impl<'a> System<'a> for Sys { // TODO: Sync clients that don't have a position? // Sync inventories - for (client, inventory, _) in (&mut clients, &inventories, &inventory_updates).join() { - client.notify(ServerMsg::InventoryUpdate(inventory.clone())); + for (client, inventory, update) in (&mut clients, &inventories, &inventory_updates).join() { + client.notify(ServerMsg::InventoryUpdate( + inventory.clone(), + update.event(), + )); } // Remove all force flags. diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 7c3e6debb0..9ae1608213 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -63,6 +63,7 @@ chrono = "0.4.9" rust-argon2 = "0.5" bincode = "1.2" deunicode = "1.0" +uvth = "3.1.1" [target.'cfg(target_os = "macos")'.dependencies] dispatch = "0.1.4" @@ -74,6 +75,7 @@ winres = "0.1" criterion = "0.3" git2 = "0.10" world = { package = "veloren-world", path = "../world" } +gfx_window_glutin = { version = "0.31.0", features = ["headless"] } [[bench]] name = "meshing_benchmark" diff --git a/voxygen/examples/character_renderer.rs b/voxygen/examples/character_renderer.rs new file mode 100644 index 0000000000..ae07183f5e --- /dev/null +++ b/voxygen/examples/character_renderer.rs @@ -0,0 +1,58 @@ +use common::{assets, comp}; +use gfx_window_glutin::init_headless; +use vek::*; +use veloren_voxygen::{render, scene::simple as scene}; + +fn main() { + // Setup renderer + let dim = (200u16, 300u16, 1, gfx::texture::AaMode::Single); + let events_loop = glutin::EventsLoop::new(); + let context = glutin::ContextBuilder::new() + .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2))) + .build_headless(&events_loop, (dim.0 as u32, dim.1 as u32).into()) + .expect("Failed to build headless context"); + + let (_context, device, factory, color_view, depth_view) = init_headless(context, dim); + + let mut renderer = render::Renderer::new( + device, + factory, + color_view, + depth_view, + render::AaMode::SsaaX4, + render::CloudMode::Regular, + render::FluidMode::Shiny, + ) + .unwrap(); + + // Create character + let body = comp::humanoid::Body::random(); + const STARTER_BOW: &str = "common.items.weapons.starter_bow"; + let equipment = comp::Equipment { + main: assets::load_cloned(STARTER_BOW).ok(), + alt: None, + }; + + // Setup scene (using the character selection screen `Scene`) + let mut scene = scene::Scene::new(&mut renderer, None); + let scene_data = scene::SceneData { + time: 1.0, + delta_time: 1.0, + tick: 0, + body: Some(body.clone()), + gamma: 1.0, + }; + scene.camera_mut().set_focus_pos(Vec3::unit_z() * 0.8); + scene.camera_mut().set_distance(1.5); + scene.camera_mut().update(0.0); + scene.maintain(&mut renderer, scene_data); + + // Render + renderer.clear(); + scene.render(&mut renderer, 0, Some(body), &equipment); + + renderer.flush(); + // Get image + let img = renderer.create_screenshot().unwrap(); + img.save("character.png").unwrap(); +} diff --git a/voxygen/src/anim/biped_large/mod.rs b/voxygen/src/anim/biped_large/mod.rs index 85611e944e..282c094876 100644 --- a/voxygen/src/anim/biped_large/mod.rs +++ b/voxygen/src/anim/biped_large/mod.rs @@ -47,7 +47,7 @@ impl BipedLargeSkeleton { impl Skeleton for BipedLargeSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { let upper_torso_mat = self.upper_torso.compute_base_matrix(); let shoulder_l_mat = self.shoulder_l.compute_base_matrix(); let shoulder_r_mat = self.shoulder_r.compute_base_matrix(); @@ -78,6 +78,8 @@ impl Skeleton for BipedLargeSkeleton { FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), ] } diff --git a/voxygen/src/anim/bird_medium/idle.rs b/voxygen/src/anim/bird_medium/idle.rs index 82cd25c2bc..86b776720e 100644 --- a/voxygen/src/anim/bird_medium/idle.rs +++ b/voxygen/src/anim/bird_medium/idle.rs @@ -33,7 +33,7 @@ impl Animation for IdleAnimation { * 0.25, ); - next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1) / 11.0; + next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1); next.head.ori = Quaternion::rotation_z(duck_head_look.x) * Quaternion::rotation_x(-duck_head_look.y.abs() + wave_slow_cos * 0.03); next.head.scale = Vec3::one(); @@ -46,7 +46,7 @@ impl Animation for IdleAnimation { next.torso.ori = Quaternion::rotation_y(wave_slow * 0.03); next.torso.scale = Vec3::one() / 11.0; - next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1) / 11.0; + next.tail.offset = Vec3::new(0.0, skeleton_attr.tail.0, skeleton_attr.tail.1); next.tail.ori = Quaternion::rotation_x(wave_slow_cos * 0.03); next.tail.scale = Vec3::one(); @@ -54,7 +54,7 @@ impl Animation for IdleAnimation { -skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, - ) / 11.0; + ); next.wing_l.ori = Quaternion::rotation_z(0.0); next.wing_l.scale = Vec3::one() * 1.05; @@ -62,7 +62,7 @@ impl Animation for IdleAnimation { skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, - ) / 11.0; + ); next.wing_r.ori = Quaternion::rotation_y(0.0); next.wing_r.scale = Vec3::one() * 1.05; diff --git a/voxygen/src/anim/bird_medium/mod.rs b/voxygen/src/anim/bird_medium/mod.rs index f1330d82c8..d5e7832e74 100644 --- a/voxygen/src/anim/bird_medium/mod.rs +++ b/voxygen/src/anim/bird_medium/mod.rs @@ -27,15 +27,15 @@ impl BirdMediumSkeleton { impl Skeleton for BirdMediumSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { let torso_mat = self.torso.compute_base_matrix(); [ - FigureBoneData::new(self.head.compute_base_matrix() * torso_mat), + FigureBoneData::new(torso_mat * self.head.compute_base_matrix()), FigureBoneData::new(torso_mat), - FigureBoneData::new(self.tail.compute_base_matrix() * torso_mat), - FigureBoneData::new(self.wing_l.compute_base_matrix() * torso_mat), - FigureBoneData::new(self.wing_r.compute_base_matrix() * torso_mat), + FigureBoneData::new(torso_mat * self.tail.compute_base_matrix()), + FigureBoneData::new(torso_mat * self.wing_l.compute_base_matrix()), + FigureBoneData::new(torso_mat * self.wing_r.compute_base_matrix()), FigureBoneData::new(self.leg_l.compute_base_matrix()), FigureBoneData::new(self.leg_r.compute_base_matrix()), FigureBoneData::default(), @@ -47,6 +47,8 @@ impl Skeleton for BirdMediumSkeleton { FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), ] } diff --git a/voxygen/src/anim/bird_medium/run.rs b/voxygen/src/anim/bird_medium/run.rs index 36cb77cc50..ea73668941 100644 --- a/voxygen/src/anim/bird_medium/run.rs +++ b/voxygen/src/anim/bird_medium/run.rs @@ -28,7 +28,7 @@ impl Animation for RunAnimation { 0.0, skeleton_attr.head.0, skeleton_attr.head.1 + center * 0.5, - ) / 11.0; + ); next.head.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0 + center * 0.03); next.head.scale = Vec3::one(); @@ -44,7 +44,7 @@ impl Animation for RunAnimation { 0.0, skeleton_attr.tail.0, skeleton_attr.tail.1 + centeroffset * 0.6, - ) / 11.0; + ); next.tail.ori = Quaternion::rotation_x(center * 0.03); next.tail.scale = Vec3::one(); @@ -52,16 +52,16 @@ impl Animation for RunAnimation { -skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, - ) / 11.0; - next.wing_l.ori = Quaternion::rotation_y(footl * 0.3); + ); + next.wing_l.ori = Quaternion::rotation_y(footl * 0.1); next.wing_l.scale = Vec3::one() * 1.05; next.wing_r.offset = Vec3::new( skeleton_attr.wing.0, skeleton_attr.wing.1, skeleton_attr.wing.2, - ) / 11.0; - next.wing_r.ori = Quaternion::rotation_y(footr * 0.3); + ); + next.wing_r.ori = Quaternion::rotation_y(footr * 0.1); next.wing_r.scale = Vec3::one() * 1.05; next.leg_l.offset = Vec3::new( diff --git a/voxygen/src/anim/bird_small/mod.rs b/voxygen/src/anim/bird_small/mod.rs index 610b63122a..763bb2fac8 100644 --- a/voxygen/src/anim/bird_small/mod.rs +++ b/voxygen/src/anim/bird_small/mod.rs @@ -31,7 +31,7 @@ impl BirdSmallSkeleton { impl Skeleton for BirdSmallSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { let torso_mat = self.torso.compute_base_matrix(); [ @@ -51,6 +51,8 @@ impl Skeleton for BirdSmallSkeleton { FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), ] } diff --git a/voxygen/src/anim/character/attack.rs b/voxygen/src/anim/character/attack.rs index e52cc502a0..e9701476d4 100644 --- a/voxygen/src/anim/character/attack.rs +++ b/voxygen/src/anim/character/attack.rs @@ -21,60 +21,65 @@ impl Animation for AttackAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - let slow = (anim_time as f32 * 9.0).sin(); - let accel_med = 1.0 - (anim_time as f32 * 16.0).cos(); - let accel_slow = 1.0 - (anim_time as f32 * 12.0).cos(); - let accel_fast = 1.0 - (anim_time as f32 * 24.0).cos(); - let decel = (anim_time as f32 * 16.0).min(PI / 2.0).sin(); + let lab = 1.0; + let accel_med = 1.0 - (anim_time as f32 * 16.0 * lab as f32).cos(); + let accel_slow = 1.0 - (anim_time as f32 * 12.0 * lab as f32).cos(); + let accel_fast = 1.0 - (anim_time as f32 * 24.0 * lab as f32).cos(); + let decel = (anim_time as f32 * 16.0 * lab as f32).min(PI / 2.0).sin(); + + let slow = (((5.0) + / (1.1 + 3.9 * ((anim_time as f32 * lab as f32 * 12.4).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * lab as f32 * 12.4).sin()); next.head.offset = Vec3::new( 0.0 + skeleton_attr.neck_right, - -2.0 + skeleton_attr.neck_forward, + -2.0 + skeleton_attr.neck_forward + decel * 0.8, skeleton_attr.neck_height + 21.0, ); - next.head.ori = Quaternion::rotation_z(decel * -0.25) - * Quaternion::rotation_x(0.0 + decel * -0.1) - * Quaternion::rotation_y(decel * 0.1); + next.head.ori = Quaternion::rotation_z(decel * 0.25) + * Quaternion::rotation_x(0.0 + decel * 0.1) + * Quaternion::rotation_y(decel * -0.1); next.head.scale = Vec3::one() * skeleton_attr.head_scale; next.chest.offset = Vec3::new(0.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_x(0.0); + next.chest.ori = Quaternion::rotation_z(decel * -0.2) + * Quaternion::rotation_x(0.0 + decel * -0.2) + * Quaternion::rotation_y(decel * 0.2); next.chest.scale = Vec3::one(); next.belt.offset = Vec3::new(0.0, 0.0, 5.0); - next.belt.ori = Quaternion::rotation_x(0.0); + next.belt.ori = Quaternion::rotation_z(decel * -0.1) + * Quaternion::rotation_x(0.0 + decel * -0.1) + * Quaternion::rotation_y(decel * 0.1); next.belt.scale = Vec3::one(); next.shorts.offset = Vec3::new(0.0, 0.0, 2.0); - next.shorts.ori = Quaternion::rotation_x(0.0); + next.belt.ori = Quaternion::rotation_z(decel * -0.08) + * Quaternion::rotation_x(0.0 + decel * -0.08) + * Quaternion::rotation_y(decel * 0.08); next.shorts.scale = Vec3::one(); match active_tool_kind { //TODO: Inventory - Some(ToolKind::Sword(_)) => { - next.l_hand.offset = - Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, 0.0); - next.l_hand.ori = Quaternion::rotation_z(-0.8) - * Quaternion::rotation_x(0.0 + accel_med * -0.8) - * Quaternion::rotation_y(0.0 + accel_med * -0.4); - next.l_hand.scale = Vec3::one() * 1.01; - - next.r_hand.offset = - Vec3::new(-8.0 + accel_slow * 10.0, 8.0 + accel_fast * 3.0, -2.0); - next.r_hand.ori = Quaternion::rotation_z(-0.8) - * Quaternion::rotation_x(0.0 + accel_med * -0.8) - * Quaternion::rotation_y(0.0 + accel_med * -0.4); - next.r_hand.scale = Vec3::one() * 1.01; - - next.main.offset = Vec3::new( - -8.0 + accel_slow * 10.0 + skeleton_attr.weapon_x, - 8.0 + accel_fast * 3.0, - 0.0, - ); - next.main.ori = Quaternion::rotation_z(-0.8) - * Quaternion::rotation_x(0.0 + accel_med * -0.8) - * Quaternion::rotation_y(0.0 + accel_med * -0.4); + Some(Tool::Sword(_)) => { + next.l_hand.offset = Vec3::new(0.0, 1.0, 0.0); + next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.scale = Vec3::one() * 1.04; + next.r_hand.offset = Vec3::new(0.0, 0.0, -3.0); + next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.scale = Vec3::one() * 1.05; + next.main.offset = Vec3::new(0.0, 6.0, -1.0); + next.main.ori = Quaternion::rotation_x(-0.3) + * Quaternion::rotation_y(0.0) + * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); + + next.control.offset = Vec3::new(-8.0 - slow * 1.0, 3.0 - slow * 5.0, 0.0); + next.control.ori = Quaternion::rotation_x(-1.2) + * Quaternion::rotation_y(slow * 1.5) + * Quaternion::rotation_z(1.4 + slow * 0.5); + next.control.scale = Vec3::one(); }, Some(ToolKind::Axe) => { next.l_hand.offset = @@ -266,10 +271,17 @@ impl Animation for AttackAnimation { next.lantern.scale = Vec3::one() * 0.0; next.torso.offset = Vec3::new(0.0, -0.2, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z(decel * -0.2) - * Quaternion::rotation_x(0.0 + decel * -0.2) - * Quaternion::rotation_y(decel * 0.2); + next.torso.ori = + Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + + next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.scale = Vec3::one(); + + next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.scale = Vec3::one(); next } } diff --git a/voxygen/src/anim/character/block.rs b/voxygen/src/anim/character/block.rs index 6aeff58972..6c55878e96 100644 --- a/voxygen/src/anim/character/block.rs +++ b/voxygen/src/anim/character/block.rs @@ -62,22 +62,24 @@ impl Animation for BlockAnimation { match active_tool_kind { //TODO: Inventory - Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0 + wave_ultra_slow * 1.0); - next.l_hand.ori = Quaternion::rotation_x(-0.3); - next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(-6.0, 3.0, -2.0); - next.r_hand.ori = Quaternion::rotation_x(-0.3); - next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new( - -6.0 + skeleton_attr.weapon_x, - 4.5 + skeleton_attr.weapon_y, - 0.0, - ); + Some(Tool::Sword(_)) => { + next.l_hand.offset = Vec3::new(0.0, -5.0, -5.0); + next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.scale = Vec3::one() * 1.04; + next.r_hand.offset = Vec3::new(0.0, -6.0, -8.0); + next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.scale = Vec3::one() * 1.05; + next.main.offset = Vec3::new(0.0, 0.0, -6.0); next.main.ori = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); + + next.control.offset = Vec3::new(-8.0, 13.0, 8.0); + next.control.ori = Quaternion::rotation_x(0.2) + * Quaternion::rotation_y(0.4) + * Quaternion::rotation_z(-1.57); + next.control.scale = Vec3::one(); }, Some(ToolKind::Axe) => { next.l_hand.offset = Vec3::new( diff --git a/voxygen/src/anim/character/blockidle.rs b/voxygen/src/anim/character/blockidle.rs index cdc889a554..94f18c2571 100644 --- a/voxygen/src/anim/character/blockidle.rs +++ b/voxygen/src/anim/character/blockidle.rs @@ -61,22 +61,24 @@ impl Animation for BlockIdleAnimation { match active_tool_kind { //TODO: Inventory - Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0 + wave_ultra_slow * 1.0); - next.l_hand.ori = Quaternion::rotation_x(-0.3); - next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(-6.0, 3.0, -2.0); - next.r_hand.ori = Quaternion::rotation_x(-0.3); - next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new( - -6.0 + skeleton_attr.weapon_x, - 4.5 + skeleton_attr.weapon_y, - 0.0, - ); + Some(Tool::Sword(_)) => { + next.l_hand.offset = Vec3::new(0.0, -5.0, -5.0); + next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.scale = Vec3::one() * 1.04; + next.r_hand.offset = Vec3::new(0.0, -6.0, -8.0); + next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.scale = Vec3::one() * 1.05; + next.main.offset = Vec3::new(0.0, 0.0, -6.0); next.main.ori = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); + + next.control.offset = Vec3::new(-8.0, 13.0, 8.0); + next.control.ori = Quaternion::rotation_x(0.2) + * Quaternion::rotation_y(0.4) + * Quaternion::rotation_z(-1.57); + next.control.scale = Vec3::one(); }, Some(ToolKind::Axe) => { next.l_hand.offset = Vec3::new( diff --git a/voxygen/src/anim/character/charge.rs b/voxygen/src/anim/character/charge.rs index 288b340361..fccf834c3e 100644 --- a/voxygen/src/anim/character/charge.rs +++ b/voxygen/src/anim/character/charge.rs @@ -103,6 +103,11 @@ impl Animation for ChargeAnimation { next.torso.ori = Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + + next.control.offset = Vec3::new(0.0, 0.0, 0.0); + next.control.ori = Quaternion::rotation_x(0.0); + next.control.scale = Vec3::one(); + next } } diff --git a/voxygen/src/anim/character/cidle.rs b/voxygen/src/anim/character/cidle.rs index ad268f1f39..71e61a55cc 100644 --- a/voxygen/src/anim/character/cidle.rs +++ b/voxygen/src/anim/character/cidle.rs @@ -22,7 +22,7 @@ impl Animation for CidleAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - let wave_ultra_slow = (anim_time as f32 * 3.0 + PI).sin(); + let wave_ultra_slow = (anim_time as f32 * 0.5 + PI).sin(); let wave_ultra_slow_cos = (anim_time as f32 * 3.0 + PI).cos(); let wave_slow_cos = (anim_time as f32 * 6.0 + PI).cos(); let wave_slow = (anim_time as f32 * 6.0 + PI).sin(); @@ -62,30 +62,24 @@ impl Animation for CidleAnimation { match active_tool_kind { //TODO: Inventory - Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new( - -6.0 + wave_ultra_slow_cos * 1.0, - -2.0 + wave_ultra_slow_cos * 0.5, - 1.0 + wave_ultra_slow * 1.0, - ); - next.l_hand.ori = Quaternion::rotation_x(1.27); - next.l_hand.scale = Vec3::one() * 1.0; - next.r_hand.offset = Vec3::new( - -6.0 + wave_ultra_slow_cos * 1.0, - -2.5 + wave_ultra_slow_cos * 0.5, - -1.0 + wave_ultra_slow * 1.0, - ); - next.r_hand.ori = Quaternion::rotation_x(1.27); - next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new( - -6.0 + skeleton_attr.weapon_x + wave_ultra_slow_cos * 1.0, - 5.5 + skeleton_attr.weapon_y + wave_ultra_slow_cos * 0.5, - 1.0 + wave_ultra_slow * 1.0, - ); - next.main.ori = Quaternion::rotation_x(-0.3) + Some(Tool::Sword(_)) => { + next.l_hand.offset = Vec3::new(-0.25, -5.0, -5.0); + next.l_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2); + next.l_hand.scale = Vec3::one() * 1.04; + next.r_hand.offset = Vec3::new(1.25, -5.5, -8.0); + next.r_hand.ori = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3); + next.r_hand.scale = Vec3::one() * 1.05; + next.main.offset = Vec3::new(0.0, 0.0, -6.0); + next.main.ori = Quaternion::rotation_x(-0.1) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); + + next.control.offset = Vec3::new(-7.0, 6.0, 6.0); + next.control.ori = Quaternion::rotation_x(0.0) + * Quaternion::rotation_y(0.0) + * Quaternion::rotation_z(0.0); + next.control.scale = Vec3::one(); }, Some(ToolKind::Axe) => { next.l_hand.offset = Vec3::new( diff --git a/voxygen/src/anim/character/climb.rs b/voxygen/src/anim/character/climb.rs index 5f1825f831..8cb25ce70c 100644 --- a/voxygen/src/anim/character/climb.rs +++ b/voxygen/src/anim/character/climb.rs @@ -59,11 +59,11 @@ impl Animation for ClimbAnimation { * Quaternion::rotation_y(wave_test * 0.10); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new(-6.0, -0.25 + wave_testc * 1.5, 5.0 - wave_test * 4.0); + next.l_hand.offset = Vec3::new(-6.0, -0.25 + wave_testc * 1.5, 6.0 - wave_test * 4.0); next.l_hand.ori = Quaternion::rotation_x(2.2 + wave_testc * 0.5); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new(6.0, -0.25 - wave_testc * 1.5, 5.0 + wave_test * 4.0); + next.r_hand.offset = Vec3::new(6.0, -0.25 - wave_testc * 1.5, 6.0 + wave_test * 4.0); next.r_hand.ori = Quaternion::rotation_x(2.2 - wave_testc * 0.5); next.r_hand.scale = Vec3::one(); @@ -75,15 +75,6 @@ impl Animation for ClimbAnimation { next.r_foot.ori = Quaternion::rotation_x(0.2 + wave_testc * 0.5); next.r_foot.scale = Vec3::one(); - next.main.offset = Vec3::new( - -7.0 + skeleton_attr.weapon_x, - -5.0 + skeleton_attr.weapon_y, - 15.0, - ); - next.main.ori = - Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + wave_cos * 0.25); - next.main.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); next.l_shoulder.ori = Quaternion::rotation_x(wave_cos * 0.15); next.l_shoulder.scale = Vec3::one() * 1.1; @@ -96,6 +87,23 @@ impl Animation for ClimbAnimation { next.glider.ori = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; + next.main.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 18.0, + ); + next.main.ori = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + wave_cos * 0.25); + next.main.scale = Vec3::one(); + + next.second.offset = Vec3::new( + 0.0 + skeleton_attr.weapon_x, + 0.0 + skeleton_attr.weapon_y, + 0.0, + ); + next.second.ori = Quaternion::rotation_y(0.0); + next.second.scale = Vec3::one() * 0.0; + next.lantern.offset = Vec3::new(0.0, 0.0, 0.0); next.lantern.ori = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one() * 0.0; @@ -104,6 +112,17 @@ impl Animation for ClimbAnimation { next.torso.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + next.control.offset = Vec3::new(0.0, 0.0, 0.0); + next.control.ori = Quaternion::rotation_x(0.0); + next.control.scale = Vec3::one(); + + next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.scale = Vec3::one(); + + next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.scale = Vec3::one(); next } } diff --git a/voxygen/src/anim/character/crun.rs b/voxygen/src/anim/character/crun.rs index 8297b6dbde..bc6448ac5b 100644 --- a/voxygen/src/anim/character/crun.rs +++ b/voxygen/src/anim/character/crun.rs @@ -1,17 +1,13 @@ -use super::{ - super::{Animation, }, - CharacterSkeleton,SkeletonAttr -}; +use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; use common::comp::item::ToolKind; -use std::f32::consts::PI; -use std::ops::Mul; +use std::{f32::consts::PI, ops::Mul}; use vek::*; pub struct WieldAnimation; impl Animation for WieldAnimation { - type Skeleton = CharacterSkeleton; type Dependency = (f32, f64); + type Skeleton = CharacterSkeleton; fn update_skeleton( skeleton: &Self::Skeleton, @@ -43,23 +39,25 @@ impl Animation for WieldAnimation { match Tool::Bow { //TODO: Inventory - Tool::Sword(_) => { - next.l_hand.offset = Vec3::new(-6.0, 3.75, 0.25); - next.l_hand.ori = Quaternion::rotation_x(-0.3); - next.l_hand.scale = Vec3::one() * 1.01; - next.r_hand.offset = Vec3::new(-6.0, 3.0, -2.0); - next.r_hand.ori = Quaternion::rotation_x(-0.3); - next.r_hand.scale = Vec3::one() * 1.01; - next.weapon.offset = Vec3::new( - -6.0 + skeleton_attr.weapon_x, - 4.0 + skeleton_attr.weapon_y, - 0.0, - ); - next.weapon.ori = Quaternion::rotation_x(-0.3) + Some(Tool::Sword(_)) => { + next.l_hand.offset = Vec3::new(0.0, -5.0, -5.0); + next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.scale = Vec3::one() * 1.04; + next.r_hand.offset = Vec3::new(0.0, -6.0, -8.0); + next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.scale = Vec3::one() * 1.05; + next.main.offset = Vec3::new(0.0, 0.0, -6.0); + next.main.ori = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); - next.weapon.scale = Vec3::one(); - } + next.main.scale = Vec3::one(); + + next.control.offset = Vec3::new(-8.0, 4.0, 6.0); + next.control.ori = Quaternion::rotation_x(0.0) + * Quaternion::rotation_y(0.0) + * Quaternion::rotation_z(0.0); + next.control.scale = Vec3::one(); + }, Tool::Axe => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); @@ -76,7 +74,7 @@ impl Animation for WieldAnimation { * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); - } + }, Tool::Hammer => { next.l_hand.offset = Vec3::new(-7.0, 8.25, 3.0); next.l_hand.ori = Quaternion::rotation_x(-0.3) @@ -97,7 +95,7 @@ impl Animation for WieldAnimation { * Quaternion::rotation_y(-1.2) * Quaternion::rotation_z(wave * -0.25); next.weapon.scale = Vec3::one(); - } + }, Tool::Staff => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); @@ -114,7 +112,7 @@ impl Animation for WieldAnimation { * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); - } + }, Tool::SwordShield => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); @@ -131,7 +129,7 @@ impl Animation for WieldAnimation { * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); - } + }, Tool::Bow => { next.l_hand.offset = Vec3::new(-4.0, 5.0, 0.0); next.l_hand.ori = Quaternion::rotation_x(0.0) @@ -152,7 +150,7 @@ impl Animation for WieldAnimation { * Quaternion::rotation_y(-1.7) * Quaternion::rotation_z(0.85 + 3.14); next.weapon.scale = Vec3::one(); - } + }, Tool::Daggers => { next.l_hand.offset = Vec3::new(-6.0, 3.5, 0.0); next.l_hand.ori = Quaternion::rotation_x(-0.3); @@ -169,7 +167,7 @@ impl Animation for WieldAnimation { * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.weapon.scale = Vec3::one(); - } + }, } next diff --git a/voxygen/src/anim/character/gliding.rs b/voxygen/src/anim/character/gliding.rs index d3c6e18552..ab25059863 100644 --- a/voxygen/src/anim/character/gliding.rs +++ b/voxygen/src/anim/character/gliding.rs @@ -105,14 +105,6 @@ impl Animation for GlidingAnimation { ); next.r_foot.scale = Vec3::one(); - next.main.offset = Vec3::new( - -7.0 + skeleton_attr.weapon_x, - -5.0 + skeleton_attr.weapon_y, - 15.0, - ); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); - next.main.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); next.l_shoulder.ori = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; @@ -126,6 +118,22 @@ impl Animation for GlidingAnimation { Quaternion::rotation_x(1.0) * Quaternion::rotation_y(wave_very_slow_cos * 0.04); next.glider.scale = Vec3::one(); + next.main.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 15.0, + ); + next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.scale = Vec3::one(); + + next.second.offset = Vec3::new( + 0.0 + skeleton_attr.weapon_x, + 0.0 + skeleton_attr.weapon_y, + 0.0, + ); + next.second.ori = Quaternion::rotation_y(0.0); + next.second.scale = Vec3::one() * 0.0; + next.lantern.offset = Vec3::new(0.0, 0.0, 0.0); next.lantern.ori = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one() * 0.0; @@ -135,6 +143,17 @@ impl Animation for GlidingAnimation { * Quaternion::rotation_y(tilt * 16.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + next.control.offset = Vec3::new(0.0, 0.0, 0.0); + next.control.ori = Quaternion::rotation_x(0.0); + next.control.scale = Vec3::one(); + + next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.scale = Vec3::one(); + + next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.scale = Vec3::one(); next } } diff --git a/voxygen/src/anim/character/idle.rs b/voxygen/src/anim/character/idle.rs index 87a709ab50..57949cb125 100644 --- a/voxygen/src/anim/character/idle.rs +++ b/voxygen/src/anim/character/idle.rs @@ -56,7 +56,7 @@ impl Animation for IdleAnimation { next.shorts.scale = Vec3::one(); next.l_hand.offset = Vec3::new( - -6.0, + -7.0, -0.25 + wave_ultra_slow_cos * 0.15, 5.0 + wave_ultra_slow * 0.5, ); @@ -65,7 +65,7 @@ impl Animation for IdleAnimation { next.l_hand.scale = Vec3::one(); next.r_hand.offset = Vec3::new( - 6.0, + 7.0, -0.25 + wave_ultra_slow_cos * 0.15, 5.0 + wave_ultra_slow * 0.5 + wave_ultra_slow_abs * -0.05, ); @@ -80,14 +80,6 @@ impl Animation for IdleAnimation { next.r_foot.ori = Quaternion::identity(); next.r_foot.scale = Vec3::one(); - next.main.offset = Vec3::new( - -7.0 + skeleton_attr.weapon_x, - -5.0 + skeleton_attr.weapon_y, - 15.0, - ); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); - next.main.scale = Vec3::one() + wave_ultra_slow_abs * -0.05; - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 5.0); next.l_shoulder.ori = Quaternion::rotation_x(0.0); next.l_shoulder.scale = (Vec3::one() + wave_ultra_slow_abs * -0.05) * 1.15; @@ -100,6 +92,22 @@ impl Animation for IdleAnimation { next.glider.ori = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; + next.main.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 18.0, + ); + next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.scale = Vec3::one() + wave_ultra_slow_abs * -0.05; + + next.second.offset = Vec3::new( + 0.0 + skeleton_attr.weapon_x, + 0.0 + skeleton_attr.weapon_y, + 0.0, + ); + next.second.ori = Quaternion::rotation_y(0.0); + next.second.scale = Vec3::one() * 0.0; + next.lantern.offset = Vec3::new(0.0, 0.0, 0.0); next.lantern.ori = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one() * 0.0; @@ -108,6 +116,17 @@ impl Animation for IdleAnimation { next.torso.ori = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + next.control.offset = Vec3::new(0.0, 0.0, 0.0); + next.control.ori = Quaternion::rotation_x(0.0); + next.control.scale = Vec3::one(); + + next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.scale = Vec3::one(); + + next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.scale = Vec3::one(); next } } diff --git a/voxygen/src/anim/character/jump.rs b/voxygen/src/anim/character/jump.rs index b9ecdd2a29..a06bad0f04 100644 --- a/voxygen/src/anim/character/jump.rs +++ b/voxygen/src/anim/character/jump.rs @@ -68,14 +68,6 @@ impl Animation for JumpAnimation { next.r_foot.ori = Quaternion::rotation_x(wave_stop * 1.2 + wave_slow * 0.2); next.r_foot.scale = Vec3::one(); - next.main.offset = Vec3::new( - -7.0 + skeleton_attr.weapon_x, - -5.0 + skeleton_attr.weapon_y, - 15.0, - ); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); - next.main.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); next.l_shoulder.ori = Quaternion::rotation_x(wave_stop_alt * 0.3); next.l_shoulder.scale = Vec3::one() * 1.1; @@ -88,6 +80,22 @@ impl Animation for JumpAnimation { next.glider.ori = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; + next.main.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 15.0, + ); + next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.scale = Vec3::one(); + + next.second.offset = Vec3::new( + 0.0 + skeleton_attr.weapon_x, + 0.0 + skeleton_attr.weapon_y, + 0.0, + ); + next.second.ori = Quaternion::rotation_y(0.0); + next.second.scale = Vec3::one() * 0.0; + next.lantern.offset = Vec3::new(0.0, 0.0, 0.0); next.lantern.ori = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one() * 0.0; @@ -96,6 +104,17 @@ impl Animation for JumpAnimation { next.torso.ori = Quaternion::rotation_x(-0.2); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + next.control.offset = Vec3::new(0.0, 0.0, 0.0); + next.control.ori = Quaternion::rotation_x(0.0); + next.control.scale = Vec3::one(); + + next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.scale = Vec3::one(); + + next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.scale = Vec3::one(); next } } diff --git a/voxygen/src/anim/character/mod.rs b/voxygen/src/anim/character/mod.rs index d7b3bf68eb..1fb83a2cbb 100644 --- a/voxygen/src/anim/character/mod.rs +++ b/voxygen/src/anim/character/mod.rs @@ -37,12 +37,16 @@ pub struct CharacterSkeleton { r_hand: Bone, l_foot: Bone, r_foot: Bone, - main: Bone, l_shoulder: Bone, r_shoulder: Bone, glider: Bone, + main: Bone, + second: Bone, lantern: Bone, torso: Bone, + control: Bone, + l_control: Bone, + r_control: Bone, } impl CharacterSkeleton { @@ -52,11 +56,16 @@ impl CharacterSkeleton { impl Skeleton for CharacterSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { let chest_mat = self.chest.compute_base_matrix(); let torso_mat = self.torso.compute_base_matrix(); let l_hand_mat = self.l_hand.compute_base_matrix(); + let r_hand_mat = self.r_hand.compute_base_matrix(); + let control_mat = self.control.compute_base_matrix(); + let l_control_mat = self.l_control.compute_base_matrix(); + let r_control_mat = self.r_control.compute_base_matrix(); let main_mat = self.main.compute_base_matrix(); + let second_mat = self.second.compute_base_matrix(); let head_mat = self.head.compute_base_matrix(); [ @@ -64,18 +73,20 @@ impl Skeleton for CharacterSkeleton { FigureBoneData::new(torso_mat * chest_mat), FigureBoneData::new(torso_mat * self.belt.compute_base_matrix()), FigureBoneData::new(torso_mat * self.shorts.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * l_hand_mat), - FigureBoneData::new(torso_mat * chest_mat * self.r_hand.compute_base_matrix()), + FigureBoneData::new(torso_mat * chest_mat * control_mat * l_control_mat * l_hand_mat), + FigureBoneData::new(torso_mat * chest_mat * control_mat * r_control_mat * r_hand_mat), FigureBoneData::new(torso_mat * self.l_foot.compute_base_matrix()), FigureBoneData::new(torso_mat * self.r_foot.compute_base_matrix()), - FigureBoneData::new(torso_mat * chest_mat * main_mat), FigureBoneData::new(torso_mat * chest_mat * self.l_shoulder.compute_base_matrix()), FigureBoneData::new(torso_mat * chest_mat * self.r_shoulder.compute_base_matrix()), FigureBoneData::new(torso_mat * self.glider.compute_base_matrix()), + FigureBoneData::new(torso_mat * chest_mat * control_mat * l_control_mat * main_mat), + FigureBoneData::new(torso_mat * chest_mat * control_mat * r_control_mat * second_mat), FigureBoneData::new(torso_mat * chest_mat * self.lantern.compute_base_matrix()), FigureBoneData::new(torso_mat), - FigureBoneData::default(), - FigureBoneData::default(), + FigureBoneData::new(control_mat), + FigureBoneData::new(l_control_mat), + FigureBoneData::new(r_control_mat), ] } @@ -88,12 +99,16 @@ impl Skeleton for CharacterSkeleton { self.r_hand.interpolate(&target.r_hand, dt); self.l_foot.interpolate(&target.l_foot, dt); self.r_foot.interpolate(&target.r_foot, dt); - self.main.interpolate(&target.main, dt); self.l_shoulder.interpolate(&target.l_shoulder, dt); self.r_shoulder.interpolate(&target.r_shoulder, dt); self.glider.interpolate(&target.glider, dt); + self.main.interpolate(&target.main, dt); + self.second.interpolate(&target.second, dt); self.lantern.interpolate(&target.lantern, dt); self.torso.interpolate(&target.torso, dt); + self.control.interpolate(&target.control, dt); + self.l_control.interpolate(&target.l_control, dt); + self.r_control.interpolate(&target.r_control, dt); } } diff --git a/voxygen/src/anim/character/roll.rs b/voxygen/src/anim/character/roll.rs index 0f7b731811..734f83ecd4 100644 --- a/voxygen/src/anim/character/roll.rs +++ b/voxygen/src/anim/character/roll.rs @@ -86,16 +86,6 @@ impl Animation for RollAnimation { next.r_foot.ori = Quaternion::rotation_x(wave * -0.4); next.r_foot.scale = Vec3::one(); - next.main.offset = Vec3::new( - -7.0 + skeleton_attr.weapon_x, - -5.0 + skeleton_attr.weapon_y, - 15.0, - ); - next.main.ori = Quaternion::rotation_y(2.5) - * Quaternion::rotation_z(1.57) - * Quaternion::rotation_x(0.0); - next.main.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); next.l_shoulder.ori = Quaternion::rotation_x(0.0); next.l_shoulder.scale = Vec3::one() * 1.1; @@ -108,6 +98,24 @@ impl Animation for RollAnimation { next.glider.ori = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; + next.main.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 15.0, + ); + next.main.ori = Quaternion::rotation_y(2.5) + * Quaternion::rotation_z(1.57) + * Quaternion::rotation_x(0.0); + next.main.scale = Vec3::one(); + + next.second.offset = Vec3::new( + 0.0 + skeleton_attr.weapon_x, + 0.0 + skeleton_attr.weapon_y, + 0.0, + ); + next.second.ori = Quaternion::rotation_y(0.0); + next.second.scale = Vec3::one() * 0.0; + next.lantern.offset = Vec3::new(0.0, 0.0, 0.0); next.lantern.ori = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one() * 0.0; @@ -116,6 +124,18 @@ impl Animation for RollAnimation { Vec3::new(0.0, 0.0, 0.1 + wave_dub * 16.0) / 11.0 * skeleton_attr.scaler; next.torso.ori = Quaternion::rotation_x(wave_slow * 6.5) * Quaternion::rotation_y(tilt); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + + next.control.offset = Vec3::new(0.0, 0.0, 0.0); + next.control.ori = Quaternion::rotation_x(0.0); + next.control.scale = Vec3::one(); + + next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.scale = Vec3::one(); + + next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.scale = Vec3::one(); next } } diff --git a/voxygen/src/anim/character/run.rs b/voxygen/src/anim/character/run.rs index 2fa33fa78b..35ad488bdb 100644 --- a/voxygen/src/anim/character/run.rs +++ b/voxygen/src/anim/character/run.rs @@ -21,29 +21,38 @@ impl Animation for RunAnimation { let speed = Vec2::::from(velocity).magnitude(); *rate = speed; - let constant = 1.0; - let wave = (((5.0) - / (1.1 + 3.9 * ((anim_time as f32 * constant as f32 * 4.2).sin()).powf(2.0 as f32))) + let lab = 1.0; + let long = (((5.0) + / (1.5 + 3.5 * ((anim_time as f32 * lab as f32 * 0.66).sin()).powf(2.0 as f32))) .sqrt()) - * ((anim_time as f32 * constant as f32 * 1.2).sin()); - let wave_cos = (((5.0) - / (1.1 + 3.9 * ((anim_time as f32 * constant as f32 * 2.4).sin()).powf(2.0 as f32))) - .sqrt()) - * ((anim_time as f32 * constant as f32 * 1.5).sin()); - let wave_cos_dub = (((5.0) - / (1.1 + 3.9 * ((anim_time as f32 * constant as f32 * 4.8).sin()).powf(2.0 as f32))) - .sqrt()) - * ((anim_time as f32 * constant as f32 * 1.5).sin()); + * ((anim_time as f32 * lab as f32 * 0.66).sin()); + + let short = (((5.0) + / (1.5 + 3.5 * ((anim_time as f32 * lab as f32 * 1.32).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * lab as f32 * 1.32).sin()); + + let shortalt = (((5.0) + / (1.5 + + 3.5 + * ((anim_time as f32 * lab as f32 * 1.32 + PI / 2.0).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * lab as f32 * 1.32 + PI / 2.0).sin()); + + let foot = (((5.0) + / (1.1 + 3.9 * ((anim_time as f32 * lab as f32 * 1.32).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * lab as f32 * 1.32).sin()); + + let wave_stop = (anim_time as f32 * 2.6).min(PI / 2.0 / 2.0).sin(); - let wave_diff = (anim_time as f32 * 0.6).sin(); - let wave_stop = (anim_time as f32 * 2.6).min(PI / 2.0).sin(); let head_look = Vec2::new( - ((global_time + anim_time) as f32 / 4.0) + ((global_time + anim_time) as f32 / 18.0) .floor() .mul(7331.0) .sin() * 0.2, - ((global_time + anim_time) as f32 / 4.0) + ((global_time + anim_time) as f32 / 18.0) .floor() .mul(1337.0) .sin() @@ -67,81 +76,100 @@ impl Animation for RunAnimation { next.head.offset = Vec3::new( 0.0, -3.0 + skeleton_attr.neck_forward, - skeleton_attr.neck_height + 20.0 + wave_cos * 1.3, + skeleton_attr.neck_height + 20.0 + short * 1.3, ); - next.head.ori = Quaternion::rotation_z(head_look.x + wave * 0.1) + next.head.ori = Quaternion::rotation_z(head_look.x + long * 0.1) * Quaternion::rotation_x(head_look.y + 0.35); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + wave_cos * 1.1); - next.chest.ori = Quaternion::rotation_z(wave * 0.2); + next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + short * 1.1); + next.chest.ori = Quaternion::rotation_z(short * 0.2); next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, 0.0, 5.0 + wave_cos * 1.1); - next.belt.ori = Quaternion::rotation_z(wave * 0.35); + next.belt.offset = Vec3::new(0.0, 0.0, 5.0 + short * 1.1); + next.belt.ori = Quaternion::rotation_z(short * 0.35); next.belt.scale = Vec3::one(); - next.shorts.offset = Vec3::new(0.0, 0.0, 2.0 + wave_cos * 1.1); - next.shorts.ori = Quaternion::rotation_z(wave * 0.6); + next.shorts.offset = Vec3::new(0.0, 0.0, 2.0 + short * 1.1); + next.shorts.ori = Quaternion::rotation_z(short * 0.6); next.shorts.scale = Vec3::one(); next.l_hand.offset = Vec3::new( -6.0 + wave_stop * -1.0, - -0.25 + wave_cos * 2.0, - 5.0 - wave * 1.5, + -0.25 + short * 2.0, + 5.0 + short * -1.5, ); next.l_hand.ori = - Quaternion::rotation_x(0.8 + wave_cos * 1.2) * Quaternion::rotation_y(wave_stop * 0.1); + Quaternion::rotation_x(0.8 + short * 1.2) * Quaternion::rotation_y(wave_stop * 0.1); next.l_hand.scale = Vec3::one(); next.r_hand.offset = Vec3::new( 6.0 + wave_stop * 1.0, - -0.25 - wave_cos * 2.0, - 5.0 + wave * 1.5, + -0.25 + short * -2.0, + 5.0 + short * 1.5, ); - next.r_hand.ori = Quaternion::rotation_x(0.8 + wave_cos * -1.2) - * Quaternion::rotation_y(wave_stop * -0.1); + next.r_hand.ori = + Quaternion::rotation_x(0.8 + short * -1.2) * Quaternion::rotation_y(wave_stop * -0.1); next.r_hand.scale = Vec3::one(); - next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave_cos * 1.0, 6.0 - wave_cos_dub * 0.7); - next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.2); + next.l_foot.offset = Vec3::new(-3.4, foot * 1.0, 6.0); + next.l_foot.ori = Quaternion::rotation_x(foot * -1.2); next.l_foot.scale = Vec3::one(); - next.r_foot.offset = Vec3::new(3.4, 0.0 - wave_cos * 1.0, 6.0 - wave_cos_dub * 0.7); - next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.2); + next.r_foot.offset = Vec3::new(3.4, foot * -1.0, 6.0); + next.r_foot.ori = Quaternion::rotation_x(foot * 1.2); next.r_foot.scale = Vec3::one(); - next.main.offset = Vec3::new( - -7.0 + skeleton_attr.weapon_x, - -5.0 + skeleton_attr.weapon_y, - 15.0, - ); - next.main.ori = - Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + wave_cos * 0.25); - next.main.scale = Vec3::one(); - - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); - next.l_shoulder.ori = Quaternion::rotation_x(wave_cos * 0.15); + next.l_shoulder.offset = Vec3::new(-5.0, -1.0, 4.7); + next.l_shoulder.ori = Quaternion::rotation_x(short * 0.15); next.l_shoulder.scale = Vec3::one() * 1.1; - next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); - next.r_shoulder.ori = Quaternion::rotation_x(wave * 0.15); + next.r_shoulder.offset = Vec3::new(5.0, -1.0, 4.7); + next.r_shoulder.ori = Quaternion::rotation_x(short * -0.15); next.r_shoulder.scale = Vec3::one() * 1.1; next.glider.offset = Vec3::new(0.0, 5.0, 0.0); next.glider.ori = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; + next.main.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 15.0, + ); + next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + short * 0.25); + next.main.scale = Vec3::one(); + + next.second.offset = Vec3::new( + 0.0 + skeleton_attr.weapon_x, + 0.0 + skeleton_attr.weapon_y, + 0.0, + ); + next.second.ori = Quaternion::rotation_y(0.0); + next.second.scale = Vec3::one() * 0.0; + next.lantern.offset = Vec3::new(0.0, 5.0, 0.0); next.lantern.ori = Quaternion::rotation_y(0.0); next.lantern.scale = Vec3::one() * 0.0; - next.torso.offset = Vec3::new(0.0, -0.3 + wave * -0.08, 0.4) * skeleton_attr.scaler; + next.torso.offset = Vec3::new(0.0, -0.3 + shortalt * -0.065, 0.4) * skeleton_attr.scaler; next.torso.ori = - Quaternion::rotation_x(wave_stop * speed * -0.06 + wave_diff * speed * -0.005) + Quaternion::rotation_x(wave_stop * speed * -0.05 + wave_stop * speed * -0.005) * Quaternion::rotation_y(tilt); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + next.control.offset = Vec3::new(0.0, 0.0, 0.0); + next.control.ori = Quaternion::rotation_x(0.0); + next.control.scale = Vec3::one(); + + next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.scale = Vec3::one(); + + next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.scale = Vec3::one(); + next } } diff --git a/voxygen/src/anim/character/sit.rs b/voxygen/src/anim/character/sit.rs index b3eed80df2..c777bd71a3 100644 --- a/voxygen/src/anim/character/sit.rs +++ b/voxygen/src/anim/character/sit.rs @@ -95,14 +95,6 @@ impl Animation for SitAnimation { ); next.r_foot.scale = Vec3::one(); - next.main.offset = Vec3::new( - -7.0 + skeleton_attr.weapon_x, - -5.0 + skeleton_attr.weapon_y, - 15.0, - ); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); - next.main.scale = Vec3::one() + wave_slow_abs * -0.05; - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); next.l_shoulder.ori = Quaternion::rotation_x(0.0); next.l_shoulder.scale = (Vec3::one() + wave_slow_abs * -0.05) * 1.15; @@ -115,6 +107,22 @@ impl Animation for SitAnimation { next.glider.ori = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; + next.main.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 15.0, + ); + next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.scale = Vec3::one() + wave_slow_abs * -0.05; + + next.second.offset = Vec3::new( + 0.0 + skeleton_attr.weapon_x, + 0.0 + skeleton_attr.weapon_y, + 0.0, + ); + next.second.ori = Quaternion::rotation_y(0.0); + next.second.scale = Vec3::one() * 0.0; + next.lantern.offset = Vec3::new(0.0, 0.0, 0.0); next.lantern.ori = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one() * 0.0; @@ -122,6 +130,18 @@ impl Animation for SitAnimation { next.torso.offset = Vec3::new(0.0, -0.2, wave_stop * -0.16) * skeleton_attr.scaler; next.torso.ori = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + + next.control.offset = Vec3::new(0.0, 0.0, 0.0); + next.control.ori = Quaternion::rotation_x(0.0); + next.control.scale = Vec3::one(); + + next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.scale = Vec3::one(); + + next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.scale = Vec3::one(); next } } diff --git a/voxygen/src/anim/character/stand.rs b/voxygen/src/anim/character/stand.rs index f795dd20e0..6e2cb811e8 100644 --- a/voxygen/src/anim/character/stand.rs +++ b/voxygen/src/anim/character/stand.rs @@ -1,6 +1,6 @@ use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; -use common::comp::item::ToolKind; -use std::{f32::consts::PI, ops::Mul}; +use common::comp::item::Tool; +use std::ops::Mul; use vek::*; pub struct StandAnimation; @@ -18,9 +18,8 @@ impl Animation for StandAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - let wave_ultra_slow = (anim_time as f32 * 1.0 + PI).sin(); - let wave_ultra_slow_cos = (anim_time as f32 * 1.0 + PI).cos(); - let wave_ultra_slow_abs = ((anim_time as f32 * 0.5 + PI).sin()) + 1.0; + let slow = (anim_time as f32 * 1.0).sin(); + let breathe = ((anim_time as f32 * 0.5).sin()).abs(); let head_look = Vec2::new( ((global_time + anim_time) as f32 / 12.0) @@ -37,40 +36,32 @@ impl Animation for StandAnimation { next.head.offset = Vec3::new( 0.0 + skeleton_attr.neck_right, -3.0 + skeleton_attr.neck_forward, - skeleton_attr.neck_height + 21.0 + wave_ultra_slow * 0.3, + skeleton_attr.neck_height + 21.0 + slow * 0.3, ); next.head.ori = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y.abs()); next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + wave_ultra_slow * 0.3); - next.chest.ori = Quaternion::rotation_x(0.0); - next.chest.scale = Vec3::one() * 1.01 + wave_ultra_slow_abs * 0.05; + next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + slow * 0.3); + next.chest.ori = Quaternion::rotation_z(head_look.x * 0.6); + next.chest.scale = Vec3::one() * 1.01; - next.belt.offset = Vec3::new(0.0, 0.0, 5.0 + wave_ultra_slow * 0.3); - next.belt.ori = Quaternion::rotation_x(0.0); - next.belt.scale = Vec3::one() + wave_ultra_slow_abs * 0.05; + next.belt.offset = Vec3::new(0.0, 0.0, 5.0 + slow * 0.3); + next.belt.ori = Quaternion::rotation_z(head_look.x * 0.4); + next.belt.scale = Vec3::one() + breathe * 0.05; - next.shorts.offset = Vec3::new(0.0, 0.0, 2.0 + wave_ultra_slow * 0.3); + next.shorts.offset = Vec3::new(0.0, 0.0, 2.0 + slow * 0.3); next.shorts.ori = Quaternion::rotation_x(0.0); next.shorts.scale = Vec3::one(); - next.l_hand.offset = Vec3::new( - -6.0, - -0.25 + wave_ultra_slow_cos * 0.15, - 5.0 + wave_ultra_slow * 0.5, - ); + next.l_hand.offset = Vec3::new(-7.0, -0.25 + slow * 0.15, 5.0 + slow * 0.5); - next.l_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); + next.l_hand.ori = Quaternion::rotation_x(0.0 + slow * -0.06); next.l_hand.scale = Vec3::one(); - next.r_hand.offset = Vec3::new( - 6.0, - -0.25 + wave_ultra_slow_cos * 0.15, - 5.0 + wave_ultra_slow * 0.5 + wave_ultra_slow_abs * -0.05, - ); - next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); - next.r_hand.scale = Vec3::one() + wave_ultra_slow_abs * -0.05; + next.r_hand.offset = Vec3::new(7.0, -0.25 + slow * 0.15, 5.0 + slow * 0.5); + next.r_hand.ori = Quaternion::rotation_x(0.0 + slow * -0.06); + next.r_hand.scale = Vec3::one(); next.l_foot.offset = Vec3::new(-3.4, -0.1, 8.0); next.l_foot.ori = Quaternion::identity(); @@ -80,26 +71,34 @@ impl Animation for StandAnimation { next.r_foot.ori = Quaternion::identity(); next.r_foot.scale = Vec3::one(); - next.main.offset = Vec3::new( - -7.0 + skeleton_attr.weapon_x, - -5.0 + skeleton_attr.weapon_y, - 15.0, - ); - next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); - next.main.scale = Vec3::one() + wave_ultra_slow_abs * -0.05; - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 5.0); next.l_shoulder.ori = Quaternion::rotation_x(0.0); - next.l_shoulder.scale = (Vec3::one() + wave_ultra_slow_abs * -0.05) * 1.15; + next.l_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; next.r_shoulder.offset = Vec3::new(5.0, 0.0, 5.0); next.r_shoulder.ori = Quaternion::rotation_x(0.0); - next.r_shoulder.scale = (Vec3::one() + wave_ultra_slow_abs * -0.05) * 1.15; + next.r_shoulder.scale = (Vec3::one() + breathe * -0.05) * 1.15; next.glider.offset = Vec3::new(0.0, 5.0, 0.0); next.glider.ori = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; + next.main.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 18.0, + ); + next.main.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.main.scale = Vec3::one(); + + next.second.offset = Vec3::new( + 0.0 + skeleton_attr.weapon_x, + 0.0 + skeleton_attr.weapon_y, + 0.0, + ); + next.second.ori = Quaternion::rotation_y(0.0); + next.second.scale = Vec3::one() * 0.0; + next.lantern.offset = Vec3::new(0.0, 0.0, 0.0); next.lantern.ori = Quaternion::rotation_x(0.0); next.lantern.scale = Vec3::one() * 0.0; @@ -108,6 +107,17 @@ impl Animation for StandAnimation { next.torso.ori = Quaternion::rotation_x(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + next.control.offset = Vec3::new(0.0, 0.0, 0.0); + next.control.ori = Quaternion::rotation_x(0.0); + next.control.scale = Vec3::one(); + + next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.scale = Vec3::one(); + + next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.scale = Vec3::one(); next } } diff --git a/voxygen/src/anim/character/swim.rs b/voxygen/src/anim/character/swim.rs index 8aed2c03dc..eae0613728 100644 --- a/voxygen/src/anim/character/swim.rs +++ b/voxygen/src/anim/character/swim.rs @@ -95,15 +95,6 @@ impl Animation for SwimAnimation { next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.5); next.r_foot.scale = Vec3::one(); - next.main.offset = Vec3::new( - -7.0 + skeleton_attr.weapon_x, - -5.0 + skeleton_attr.weapon_y, - 15.0, - ); - next.main.ori = - Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + wave_cos * 0.25); - next.main.scale = Vec3::one(); - next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); next.l_shoulder.ori = Quaternion::rotation_x(wave_cos * 0.15); next.l_shoulder.scale = Vec3::one() * 1.1; @@ -116,6 +107,23 @@ impl Animation for SwimAnimation { next.glider.ori = Quaternion::rotation_y(0.0); next.glider.scale = Vec3::one() * 0.0; + next.main.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 18.0, + ); + next.main.ori = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + wave_cos * 0.25); + next.main.scale = Vec3::one(); + + next.second.offset = Vec3::new( + 0.0 + skeleton_attr.weapon_x, + 0.0 + skeleton_attr.weapon_y, + 0.0, + ); + next.second.ori = Quaternion::rotation_y(0.0); + next.second.scale = Vec3::one() * 0.0; + next.lantern.offset = Vec3::new(0.0, 5.0, 0.0); next.lantern.ori = Quaternion::rotation_y(0.0); next.lantern.scale = Vec3::one() * 0.0; @@ -126,6 +134,17 @@ impl Animation for SwimAnimation { * Quaternion::rotation_y(0.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + next.control.offset = Vec3::new(0.0, 0.0, 0.0); + next.control.ori = Quaternion::rotation_x(0.0); + next.control.scale = Vec3::one(); + + next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.scale = Vec3::one(); + + next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.scale = Vec3::one(); next } } diff --git a/voxygen/src/anim/character/wield.rs b/voxygen/src/anim/character/wield.rs index 874e003de2..0a9384f270 100644 --- a/voxygen/src/anim/character/wield.rs +++ b/voxygen/src/anim/character/wield.rs @@ -29,22 +29,24 @@ impl Animation for WieldAnimation { let wave_stop = (anim_time as f32 * 2.6).min(PI / 2.0).sin(); match active_tool_kind { //TODO: Inventory - Some(ToolKind::Sword(_)) => { - next.l_hand.offset = Vec3::new(-6.0, -2.0, 1.0); + Some(Tool::Sword(_)) => { + next.l_hand.offset = Vec3::new(0.0, -5.0, -5.0); next.l_hand.ori = Quaternion::rotation_x(1.27); - next.l_hand.scale = Vec3::one() * 1.00; - next.r_hand.offset = Vec3::new(-6.0, -2.5, -1.0); + next.l_hand.scale = Vec3::one() * 1.04; + next.r_hand.offset = Vec3::new(0.0, -6.0, -8.0); next.r_hand.ori = Quaternion::rotation_x(1.27); - next.r_hand.scale = Vec3::one() * 1.01; - next.main.offset = Vec3::new( - -6.0 + skeleton_attr.weapon_x, - 5.5 + skeleton_attr.weapon_y, - 1.0, - ); + next.r_hand.scale = Vec3::one() * 1.05; + next.main.offset = Vec3::new(0.0, 0.0, -6.0); next.main.ori = Quaternion::rotation_x(-0.3) * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(0.0); next.main.scale = Vec3::one(); + + next.control.offset = Vec3::new(-8.0, 4.0, 6.0); + next.control.ori = Quaternion::rotation_x(0.0) + * Quaternion::rotation_y(0.0) + * Quaternion::rotation_z(0.0); + next.control.scale = Vec3::one(); }, Some(ToolKind::Axe) => { next.l_hand.offset = Vec3::new(-6.5, -0.5, 6.0); @@ -200,6 +202,14 @@ impl Animation for WieldAnimation { next.torso.offset = Vec3::new(0.0, 0.3 + wave * -0.08, 0.4) * skeleton_attr.scaler; next.torso.ori = Quaternion::rotation_x(wave_stop * -0.2); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + + next.l_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.l_control.ori = Quaternion::rotation_x(0.0); + next.l_control.scale = Vec3::one(); + + next.r_control.offset = Vec3::new(0.0, 0.0, 0.0); + next.r_control.ori = Quaternion::rotation_x(0.0); + next.r_control.scale = Vec3::one(); next } } diff --git a/voxygen/src/anim/critter/mod.rs b/voxygen/src/anim/critter/mod.rs index 9677461239..4a2c05ef6d 100644 --- a/voxygen/src/anim/critter/mod.rs +++ b/voxygen/src/anim/critter/mod.rs @@ -32,7 +32,7 @@ impl CritterSkeleton { impl Skeleton for CritterSkeleton { type Attr = CritterAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { [ FigureBoneData::new(self.head.compute_base_matrix()), FigureBoneData::new(self.chest.compute_base_matrix()), @@ -50,6 +50,8 @@ impl Skeleton for CritterSkeleton { FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), ] } diff --git a/voxygen/src/anim/dragon/mod.rs b/voxygen/src/anim/dragon/mod.rs index c3bfec7e61..131d78dc24 100644 --- a/voxygen/src/anim/dragon/mod.rs +++ b/voxygen/src/anim/dragon/mod.rs @@ -49,7 +49,7 @@ impl DragonSkeleton { impl Skeleton for DragonSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { let chest_front_mat = self.chest_front.compute_base_matrix(); let wing_in_l_mat = self.wing_in_l.compute_base_matrix(); let wing_in_r_mat = self.wing_in_r.compute_base_matrix(); @@ -72,6 +72,8 @@ impl Skeleton for DragonSkeleton { FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), ] } diff --git a/voxygen/src/anim/fish_medium/mod.rs b/voxygen/src/anim/fish_medium/mod.rs index b085ed7220..5912719299 100644 --- a/voxygen/src/anim/fish_medium/mod.rs +++ b/voxygen/src/anim/fish_medium/mod.rs @@ -35,7 +35,7 @@ impl FishMediumSkeleton { impl Skeleton for FishMediumSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { let torso_mat = self.torso.compute_base_matrix(); let rear_mat = self.rear.compute_base_matrix(); @@ -56,6 +56,8 @@ impl Skeleton for FishMediumSkeleton { FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), ] } diff --git a/voxygen/src/anim/fish_small/mod.rs b/voxygen/src/anim/fish_small/mod.rs index ed26ef0247..e1b47513f7 100644 --- a/voxygen/src/anim/fish_small/mod.rs +++ b/voxygen/src/anim/fish_small/mod.rs @@ -27,7 +27,7 @@ impl FishSmallSkeleton { impl Skeleton for FishSmallSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { let torso_mat = self.torso.compute_base_matrix(); [ @@ -47,6 +47,8 @@ impl Skeleton for FishSmallSkeleton { FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), ] } diff --git a/voxygen/src/anim/fixture/mod.rs b/voxygen/src/anim/fixture/mod.rs index 92a68d0125..a243b923a4 100644 --- a/voxygen/src/anim/fixture/mod.rs +++ b/voxygen/src/anim/fixture/mod.rs @@ -13,7 +13,7 @@ impl FixtureSkeleton { impl Skeleton for FixtureSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { [ FigureBoneData::new(vek::Mat4::identity()), FigureBoneData::new(vek::Mat4::identity()), @@ -31,6 +31,8 @@ impl Skeleton for FixtureSkeleton { FigureBoneData::new(vek::Mat4::identity()), FigureBoneData::new(vek::Mat4::identity()), FigureBoneData::new(vek::Mat4::identity()), + FigureBoneData::new(vek::Mat4::identity()), + FigureBoneData::new(vek::Mat4::identity()), ] } diff --git a/voxygen/src/anim/mod.rs b/voxygen/src/anim/mod.rs index 607ff5f731..590f67abb1 100644 --- a/voxygen/src/anim/mod.rs +++ b/voxygen/src/anim/mod.rs @@ -52,7 +52,7 @@ impl Bone { pub trait Skeleton: Send + Sync + 'static { type Attr; - fn compute_matrices(&self) -> [FigureBoneData; 16]; + fn compute_matrices(&self) -> [FigureBoneData; 18]; /// Change the current skeleton to be more like `target`. fn interpolate(&mut self, target: &Self, dt: f32); diff --git a/voxygen/src/anim/object/mod.rs b/voxygen/src/anim/object/mod.rs index d6f3ac510e..b9befc2522 100644 --- a/voxygen/src/anim/object/mod.rs +++ b/voxygen/src/anim/object/mod.rs @@ -15,7 +15,7 @@ const SCALE: f32 = 1.0 / 11.0; impl Skeleton for ObjectSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { [ FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), @@ -33,6 +33,8 @@ impl Skeleton for ObjectSkeleton { FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), + FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), + FigureBoneData::new(Mat4::scaling_3d(Vec3::broadcast(SCALE))), ] } diff --git a/voxygen/src/anim/quadruped_medium/mod.rs b/voxygen/src/anim/quadruped_medium/mod.rs index 57f36c9c1f..a30eaa0203 100644 --- a/voxygen/src/anim/quadruped_medium/mod.rs +++ b/voxygen/src/anim/quadruped_medium/mod.rs @@ -31,7 +31,7 @@ impl QuadrupedMediumSkeleton { impl Skeleton for QuadrupedMediumSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { let ears_mat = self.ears.compute_base_matrix(); let head_upper_mat = self.head_upper.compute_base_matrix(); let head_lower_mat = self.head_lower.compute_base_matrix(); @@ -53,6 +53,8 @@ impl Skeleton for QuadrupedMediumSkeleton { FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), ] } diff --git a/voxygen/src/anim/quadruped_small/idle.rs b/voxygen/src/anim/quadruped_small/idle.rs index 9293d7cec7..59a3c2fc0c 100644 --- a/voxygen/src/anim/quadruped_small/idle.rs +++ b/voxygen/src/anim/quadruped_small/idle.rs @@ -17,11 +17,10 @@ impl Animation for IdleAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - let wave = (anim_time as f32 * 14.0).sin(); - let wave_slow = (anim_time as f32 * 3.5 + PI).sin(); - let wave_slow_cos = (anim_time as f32 * 3.5 + PI).cos(); + let slow = (anim_time as f32 * 3.5).sin(); + let slow_alt = (anim_time as f32 * 3.5 + PI).sin(); - let pig_head_look = Vec2::new( + let head_look = Vec2::new( ((global_time + anim_time) as f32 / 8.0) .floor() .mul(7331.0) @@ -35,17 +34,17 @@ impl Animation for IdleAnimation { ); next.head.offset = - Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1 + wave * 0.2) / 11.0; - next.head.ori = Quaternion::rotation_z(pig_head_look.x) - * Quaternion::rotation_x(pig_head_look.y + wave_slow_cos * 0.03); + Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1 + slow * 0.2) / 11.0; + next.head.ori = Quaternion::rotation_z(head_look.x) + * Quaternion::rotation_x(head_look.y + slow_alt * 0.03); next.head.scale = Vec3::one() / 10.5; next.chest.offset = Vec3::new( - wave_slow * 0.05, + slow * 0.05, skeleton_attr.chest.0, - skeleton_attr.chest.1 + wave_slow_cos * 0.2, + skeleton_attr.chest.1 + slow_alt * 0.2, ) / 11.0; - next.chest.ori = Quaternion::rotation_y(wave_slow * 0.05); + next.chest.ori = Quaternion::rotation_y(slow * 0.05); next.chest.scale = Vec3::one() / 11.0; next.leg_lf.offset = Vec3::new( @@ -53,7 +52,7 @@ impl Animation for IdleAnimation { skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ) / 11.0; - next.leg_lf.ori = Quaternion::rotation_x(wave_slow * 0.08); + next.leg_lf.ori = Quaternion::rotation_x(slow * 0.08); next.leg_lf.scale = Vec3::one() / 11.0; next.leg_rf.offset = Vec3::new( @@ -61,7 +60,7 @@ impl Animation for IdleAnimation { skeleton_attr.feet_f.1, skeleton_attr.feet_f.2, ) / 11.0; - next.leg_rf.ori = Quaternion::rotation_x(wave_slow_cos * 0.08); + next.leg_rf.ori = Quaternion::rotation_x(slow_alt * 0.08); next.leg_rf.scale = Vec3::one() / 11.0; next.leg_lb.offset = Vec3::new( @@ -69,7 +68,7 @@ impl Animation for IdleAnimation { skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ) / 11.0; - next.leg_lb.ori = Quaternion::rotation_x(wave_slow_cos * 0.08); + next.leg_lb.ori = Quaternion::rotation_x(slow_alt * 0.08); next.leg_lb.scale = Vec3::one() / 11.0; next.leg_rb.offset = Vec3::new( @@ -77,7 +76,7 @@ impl Animation for IdleAnimation { skeleton_attr.feet_b.1, skeleton_attr.feet_b.2, ) / 11.0; - next.leg_rb.ori = Quaternion::rotation_x(wave_slow * 0.08); + next.leg_rb.ori = Quaternion::rotation_x(slow * 0.08); next.leg_rb.scale = Vec3::one() / 11.0; next diff --git a/voxygen/src/anim/quadruped_small/mod.rs b/voxygen/src/anim/quadruped_small/mod.rs index fb4c559fbf..a277b5aff8 100644 --- a/voxygen/src/anim/quadruped_small/mod.rs +++ b/voxygen/src/anim/quadruped_small/mod.rs @@ -26,7 +26,7 @@ impl QuadrupedSmallSkeleton { impl Skeleton for QuadrupedSmallSkeleton { type Attr = SkeletonAttr; - fn compute_matrices(&self) -> [FigureBoneData; 16] { + fn compute_matrices(&self) -> [FigureBoneData; 18] { [ FigureBoneData::new(self.head.compute_base_matrix()), FigureBoneData::new(self.chest.compute_base_matrix()), @@ -44,6 +44,8 @@ impl Skeleton for QuadrupedSmallSkeleton { FigureBoneData::default(), FigureBoneData::default(), FigureBoneData::default(), + FigureBoneData::default(), + FigureBoneData::default(), ] } diff --git a/voxygen/src/anim/quadruped_small/run.rs b/voxygen/src/anim/quadruped_small/run.rs index 38a7cfeb58..801c66ddb4 100644 --- a/voxygen/src/anim/quadruped_small/run.rs +++ b/voxygen/src/anim/quadruped_small/run.rs @@ -1,4 +1,5 @@ use super::{super::Animation, QuadrupedSmallSkeleton, SkeletonAttr}; +use std::f32::consts::PI; use vek::*; pub struct RunAnimation; @@ -16,55 +17,55 @@ impl Animation for RunAnimation { ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - let wave = (anim_time as f32 * 14.0).sin(); - let wave_quick = (anim_time as f32 * 20.0).sin(); - let wave_quick_cos = (anim_time as f32 * 20.0).cos(); - let wave_cos = (anim_time as f32 * 14.0).cos(); + let slow = (anim_time as f32 * 14.0).sin(); + let fast = (anim_time as f32 * 20.0).sin(); + let fast_alt = (anim_time as f32 * 20.0 + PI / 2.0).sin(); + let slow_alt = (anim_time as f32 * 14.0 + PI / 2.0).sin(); next.head.offset = - Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1 + wave * 1.5) / 11.0; + Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1 + slow * 1.5) / 11.0; next.head.ori = - Quaternion::rotation_x(0.2 + wave * 0.05) * Quaternion::rotation_y(wave_cos * 0.03); + Quaternion::rotation_x(0.2 + slow * 0.05) * Quaternion::rotation_y(slow_alt * 0.03); next.head.scale = Vec3::one() / 10.5; next.chest.offset = Vec3::new( 0.0, skeleton_attr.chest.0, - skeleton_attr.chest.1 + wave_cos * 1.2, + skeleton_attr.chest.1 + slow_alt * 1.2, ) / 11.0; - next.chest.ori = Quaternion::rotation_x(wave * 0.1); + next.chest.ori = Quaternion::rotation_x(slow * 0.1); next.chest.scale = Vec3::one() / 11.0; next.leg_lf.offset = Vec3::new( -skeleton_attr.feet_f.0, - skeleton_attr.feet_f.1 + wave_quick * 0.8, - skeleton_attr.feet_f.2 + wave_quick_cos * 1.5, + skeleton_attr.feet_f.1 + fast * 0.8, + skeleton_attr.feet_f.2 + fast_alt * 1.5, ) / 11.0; - next.leg_lf.ori = Quaternion::rotation_x(wave_quick * 0.3); + next.leg_lf.ori = Quaternion::rotation_x(fast * 0.3); next.leg_lf.scale = Vec3::one() / 11.0; next.leg_rf.offset = Vec3::new( skeleton_attr.feet_f.0, - skeleton_attr.feet_f.1 - wave_quick_cos * 0.8, - skeleton_attr.feet_f.2 + wave_quick * 1.5, + skeleton_attr.feet_f.1 + fast_alt * -0.8, + skeleton_attr.feet_f.2 + fast * 1.5, ) / 11.0; - next.leg_rf.ori = Quaternion::rotation_x(wave_quick_cos * -0.3); + next.leg_rf.ori = Quaternion::rotation_x(fast_alt * -0.3); next.leg_rf.scale = Vec3::one() / 11.0; next.leg_lb.offset = Vec3::new( -skeleton_attr.feet_b.0, - skeleton_attr.feet_b.1 - wave_quick_cos * 0.8, - skeleton_attr.feet_b.2 + wave_quick * 1.5, + skeleton_attr.feet_b.1 + fast_alt * -0.8, + skeleton_attr.feet_b.2 + fast * 1.5, ) / 11.0; - next.leg_lb.ori = Quaternion::rotation_x(wave_quick_cos * -0.3); + next.leg_lb.ori = Quaternion::rotation_x(fast_alt * -0.3); next.leg_lb.scale = Vec3::one() / 11.0; next.leg_rb.offset = Vec3::new( skeleton_attr.feet_b.0, - skeleton_attr.feet_b.1 + wave_quick * 0.8, - skeleton_attr.feet_b.2 + wave_quick_cos * 1.5, + skeleton_attr.feet_b.1 + fast * 0.8, + skeleton_attr.feet_b.2 + fast_alt * 1.5, ) / 11.0; - next.leg_rb.ori = Quaternion::rotation_x(wave_quick * 0.3); + next.leg_rb.ori = Quaternion::rotation_x(fast * 0.3); next.leg_rb.scale = Vec3::one() / 11.0; next diff --git a/voxygen/src/audio/music.rs b/voxygen/src/audio/music.rs index 7b46abd016..397472472f 100644 --- a/voxygen/src/audio/music.rs +++ b/voxygen/src/audio/music.rs @@ -1,6 +1,5 @@ use crate::audio::AudioFrontend; -use client::Client; -use common::assets; +use common::{assets, state::State}; use rand::{seq::IteratorRandom, thread_rng}; use serde::Deserialize; use std::time::Instant; @@ -44,18 +43,18 @@ impl MusicMgr { } } - pub fn maintain(&mut self, audio: &mut AudioFrontend, client: &Client) { + pub fn maintain(&mut self, audio: &mut AudioFrontend, state: &State) { if audio.music_enabled() && self.began_playing.elapsed().as_secs_f64() > self.next_track_change { - self.play_random_track(audio, client); + self.play_random_track(audio, state); } } - fn play_random_track(&mut self, audio: &mut AudioFrontend, client: &Client) { + fn play_random_track(&mut self, audio: &mut AudioFrontend, state: &State) { const SILENCE_BETWEEN_TRACKS_SECONDS: f64 = 45.0; - let game_time = (client.state().get_time_of_day() as u64 % 86400) as u32; + let game_time = (state.get_time_of_day() as u64 % 86400) as u32; let current_period_of_day = Self::get_current_day_period(game_time); let mut rng = thread_rng(); diff --git a/voxygen/src/audio/sfx/event_mapper/mod.rs b/voxygen/src/audio/sfx/event_mapper/mod.rs index e195d1cba2..a745b4de67 100644 --- a/voxygen/src/audio/sfx/event_mapper/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/mod.rs @@ -1,11 +1,12 @@ -pub mod movement; -pub mod progression; +mod movement; +mod progression; + +use common::state::State; use movement::MovementEventMapper; use progression::ProgressionEventMapper; use super::SfxTriggers; -use client::Client; pub struct SfxEventMapper { progression_event_mapper: ProgressionEventMapper, @@ -20,8 +21,15 @@ impl SfxEventMapper { } } - pub fn maintain(&mut self, client: &Client, triggers: &SfxTriggers) { - self.progression_event_mapper.maintain(client, triggers); - self.movement_event_mapper.maintain(client, triggers); + pub fn maintain( + &mut self, + state: &State, + player_entity: specs::Entity, + triggers: &SfxTriggers, + ) { + self.progression_event_mapper + .maintain(state, player_entity, triggers); + self.movement_event_mapper + .maintain(state, player_entity, triggers); } } diff --git a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs index 025a1ae72f..494477af45 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/mod.rs @@ -3,10 +3,10 @@ /// from use crate::audio::sfx::{SfxTriggerItem, SfxTriggers}; -use client::Client; use common::{ comp::{Body, CharacterState, Item, ItemKind, PhysicsState, Pos, Stats, ToolData, Vel}, event::{EventBus, SfxEvent, SfxEventItem}, + state::State, }; use hashbrown::HashMap; use specs::{Entity as EcsEntity, Join, WorldExt}; @@ -43,13 +43,13 @@ impl MovementEventMapper { } } - pub fn maintain(&mut self, client: &Client, triggers: &SfxTriggers) { + pub fn maintain(&mut self, state: &State, player_entity: EcsEntity, triggers: &SfxTriggers) { const SFX_DIST_LIMIT_SQR: f32 = 20000.0; - let ecs = client.state().ecs(); + let ecs = state.ecs(); let player_position = ecs .read_storage::() - .get(client.entity()) + .get(player_entity) .map_or(Vec3::zero(), |pos| pos.0); for (entity, pos, vel, body, stats, physics, character) in ( @@ -108,7 +108,7 @@ impl MovementEventMapper { } } - self.cleanup(client.entity()); + self.cleanup(player_entity); } /// As the player explores the world, we track the last event of the nearby @@ -167,31 +167,42 @@ impl MovementEventMapper { }) = stats.equipment.main { if let Some(wield_event) = match ( - previous_state.weapon_drawn, - Self::weapon_drawn(character_state), + previous_event.weapon_drawn, + current_event.action.is_roll(), + Self::has_weapon_drawn(current_event.action), ) { - (false, true) => Some(SfxEvent::Wield(kind)), - (true, false) => Some(SfxEvent::Unwield(kind)), + (false, false, true) => Some(SfxEvent::Wield(kind)), + (true, false, false) => Some(SfxEvent::Unwield(kind)), _ => None, } { return wield_event; } } - // Match the fall/land and jump states based on the on_ground status - // They may also have landed on the ground with the glider (!jump) - if let Some(jump_or_fall_event) = match (physics_state.on_ground, previous_state.on_ground) - { - (true, false) => { - if previous_state.event == SfxEvent::Glide { - Some(SfxEvent::GliderClose) + // Match all other Movemement and Action states + match ( + current_event.movement, + current_event.action, + previous_event.event.clone(), + ) { + (_, ActionState::Roll { .. }, _) => SfxEvent::Roll, + (MovementState::Climb, ..) => SfxEvent::Climb, + (MovementState::Swim, ..) => SfxEvent::Swim, + (MovementState::Run, ..) => { + // If the entitys's velocity is very low, they may be stuck, or walking into a + // solid object. We should not trigger the run SFX in this case, + // even if their move state indicates running. The 0.1 value is + // an approximation from playtesting scenarios where this can occur. + if vel.magnitude() > 0.1 { + SfxEvent::Run } else { Some(SfxEvent::Run) } }, (false, true) => Some(SfxEvent::Jump), _ => None, - } { + } + { return jump_or_fall_event; } diff --git a/voxygen/src/audio/sfx/event_mapper/progression.rs b/voxygen/src/audio/sfx/event_mapper/progression.rs index 3edfcf1f7b..7c05db0360 100644 --- a/voxygen/src/audio/sfx/event_mapper/progression.rs +++ b/voxygen/src/audio/sfx/event_mapper/progression.rs @@ -2,10 +2,10 @@ /// and experience and emits associated SFX use crate::audio::sfx::SfxTriggers; -use client::Client; use common::{ comp::Stats, event::{EventBus, SfxEvent, SfxEventItem}, + state::State, }; use specs::WorldExt; @@ -30,13 +30,18 @@ impl ProgressionEventMapper { } } - pub fn maintain(&mut self, client: &Client, triggers: &SfxTriggers) { - let ecs = client.state().ecs(); + pub fn maintain( + &mut self, + state: &State, + player_entity: specs::Entity, + triggers: &SfxTriggers, + ) { + let ecs = state.ecs(); // level and exp changes let next_state = ecs.read_storage::() - .get(client.entity()) + .get(player_entity) .map_or(self.state.clone(), |stats| ProgressionState { level: stats.level.level(), exp: stats.exp.current(), diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 2af778a0ef..86771e86c2 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -4,11 +4,11 @@ mod event_mapper; use crate::audio::AudioFrontend; -use client::Client; use common::{ assets, comp::{Ori, Pos}, event::{EventBus, SfxEvent, SfxEventItem}, + state::State, }; use event_mapper::SfxEventMapper; use hashbrown::HashMap; @@ -50,23 +50,29 @@ impl SfxMgr { } } - pub fn maintain(&mut self, audio: &mut AudioFrontend, client: &Client) { + pub fn maintain( + &mut self, + audio: &mut AudioFrontend, + state: &State, + player_entity: specs::Entity, + ) { if !audio.sfx_enabled() { return; } - self.event_mapper.maintain(client, &self.triggers); + self.event_mapper + .maintain(state, player_entity, &self.triggers); - let ecs = client.state().ecs(); + let ecs = state.ecs(); let player_position = ecs .read_storage::() - .get(client.entity()) + .get(player_entity) .map_or(Vec3::zero(), |pos| pos.0); let player_ori = ecs .read_storage::() - .get(client.entity()) + .get(player_entity) .map_or(Vec3::zero(), |pos| pos.0); audio.set_listener_pos(&player_position, &player_ori); diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index 22d763ed42..70e27498de 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -258,6 +258,7 @@ impl<'a> Widget for Bag<'a> { if let Some(to_drop) = state.selected_slot { if ui.widget_input(ui.window).clicks().left().next().is_some() { event = Some(Event::HudEvent(HudEvent::DropInventorySlot(to_drop))); + state.update(|s| s.selected_slot = None); } } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 760bd4a2ef..f5a0a89ad8 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -38,7 +38,7 @@ use crate::{ ecs::comp as vcomp, i18n::{i18n_asset_key, LanguageMetadata, VoxygenLocalization}, render::{AaMode, CloudMode, Consts, FluidMode, Globals, Renderer}, - scene::camera::Camera, + scene::camera::{self, Camera}, ui::{fonts::ConrodVoxygenFonts, Graphic, Ingameable, ScaleMode, Ui}, window::{Event as WinEvent, GameInput}, GlobalState, @@ -357,7 +357,7 @@ impl Show { fn toggle_ui(&mut self) { self.ui = !self.ui; } - fn toggle_windows(&mut self) { + fn toggle_windows(&mut self, global_state: &mut GlobalState) { if self.bag || self.esc_menu || self.map @@ -379,9 +379,19 @@ impl Show { self.character_window = false; self.open_windows = Windows::None; self.want_grab = true; + + // Unpause the game if we are on singleplayer + if let Some(singleplayer) = global_state.singleplayer.as_ref() { + singleplayer.pause(false); + }; } else { self.esc_menu = true; self.want_grab = false; + + // Pause the game if we are on singleplayer + if let Some(singleplayer) = global_state.singleplayer.as_ref() { + singleplayer.pause(true); + }; } } @@ -1725,7 +1735,14 @@ impl Hud { settings_window::Event::ToggleHelp => self.show.help = !self.show.help, settings_window::Event::ToggleDebug => self.show.debug = !self.show.debug, settings_window::Event::ChangeTab(tab) => self.show.open_setting_tab(tab), - settings_window::Event::Close => self.show.settings(false), + settings_window::Event::Close => { + // Unpause the game if we are on singleplayer so that we can logout + if let Some(singleplayer) = global_state.singleplayer.as_ref() { + singleplayer.pause(false); + }; + + self.show.settings(false) + }, settings_window::Event::AdjustMousePan(sensitivity) => { events.push(Event::AdjustMousePan(sensitivity)); }, @@ -1917,12 +1934,29 @@ impl Hud { self.show.esc_menu = false; self.show.want_grab = false; self.force_ungrab = true; + + // Unpause the game if we are on singleplayer + if let Some(singleplayer) = global_state.singleplayer.as_ref() { + singleplayer.pause(false); + }; }, Some(esc_menu::Event::Logout) => { + // Unpause the game if we are on singleplayer so that we can logout + if let Some(singleplayer) = global_state.singleplayer.as_ref() { + singleplayer.pause(false); + }; + events.push(Event::Logout); }, Some(esc_menu::Event::Quit) => events.push(Event::Quit), - Some(esc_menu::Event::CharacterSelection) => events.push(Event::CharacterSelection), + Some(esc_menu::Event::CharacterSelection) => { + // Unpause the game if we are on singleplayer so that we can logout + if let Some(singleplayer) = global_state.singleplayer.as_ref() { + singleplayer.pause(false); + }; + + events.push(Event::CharacterSelection) + }, None => {}, } } @@ -1992,7 +2026,7 @@ impl Hud { self.ui.focus_widget(None); } else { // Close windows on esc - self.show.toggle_windows(); + self.show.toggle_windows(global_state); } true }, @@ -2078,9 +2112,13 @@ impl Hud { self.ui.focus_widget(maybe_id); } let events = self.update_layout(client, global_state, debug_info, dt); - let (v_mat, p_mat, _) = camera.compute_dependents(client); - self.ui - .maintain(&mut global_state.window.renderer_mut(), Some(p_mat * v_mat)); + let camera::Dependents { + view_mat, proj_mat, .. + } = camera.dependents(); + self.ui.maintain( + &mut global_state.window.renderer_mut(), + Some(proj_mat * view_mat), + ); // Check if item images need to be reloaded self.item_imgs.reload_if_changed(&mut self.ui); diff --git a/voxygen/src/lib.rs b/voxygen/src/lib.rs index 301aab5d19..56877b2131 100644 --- a/voxygen/src/lib.rs +++ b/voxygen/src/lib.rs @@ -1,6 +1,82 @@ -/// Used by benchmarks -pub mod mesh; -pub mod render; +#![deny(unsafe_code)] +#![feature(drain_filter)] +#![recursion_limit = "2048"] -// Used by tests +#[macro_use] +pub mod ui; +pub mod anim; +pub mod audio; +mod ecs; +pub mod error; +pub mod hud; pub mod i18n; +pub mod key_state; +pub mod logging; +pub mod menu; +pub mod mesh; +pub mod meta; +pub mod render; +pub mod scene; +pub mod session; +pub mod settings; +#[cfg(feature = "singleplayer")] +pub mod singleplayer; +pub mod window; + +// Reexports +pub use crate::error::Error; + +use crate::{ + audio::AudioFrontend, meta::Meta, settings::Settings, singleplayer::Singleplayer, + window::Window, +}; + +/// A type used to store state that is shared between all play states. +pub struct GlobalState { + pub settings: Settings, + pub meta: Meta, + pub window: Window, + pub audio: AudioFrontend, + pub info_message: Option, + pub singleplayer: Option, +} + +impl GlobalState { + /// Called after a change in play state has occurred (usually used to + /// reverse any temporary effects a state may have made). + pub fn on_play_state_changed(&mut self) { + self.window.grab_cursor(false); + self.window.needs_refresh_resize(); + } + + pub fn maintain(&mut self, dt: f32) { self.audio.maintain(dt); } +} + +pub enum Direction { + Forwards, + Backwards, +} + +/// States can either close (and revert to a previous state), push a new state +/// on top of themselves, or switch to a totally different state. +pub enum PlayStateResult { + /// Pop all play states in reverse order and shut down the program. + Shutdown, + /// Close the current play state and pop it from the play state stack. + Pop, + /// Push a new play state onto the play state stack. + Push(Box), + /// Switch the current play state with a new play state. + Switch(Box), +} + +/// A trait representing a playable game state. This may be a menu, a game +/// session, the title screen, etc. +pub trait PlayState { + /// Play the state until some change of state is required (i.e: a menu is + /// opened or the game is closed). + fn play(&mut self, direction: Direction, global_state: &mut GlobalState) -> PlayStateResult; + + /// Get a descriptive name for this state type. + fn name(&self) -> &'static str; +} diff --git a/voxygen/src/logging.rs b/voxygen/src/logging.rs index 4085b3c30f..50ecbe6481 100644 --- a/voxygen/src/logging.rs +++ b/voxygen/src/logging.rs @@ -1,4 +1,5 @@ use fern::colors::{Color, ColoredLevelConfig}; +use std::fs; use crate::settings::Settings; @@ -39,12 +40,20 @@ pub fn init( )) }); - // Try to create the log file. - // Incase of it failing we simply print it out to the console. - let mut log_file_created = Ok(()); - match fern::log_file(&format!("voxygen-{}.log", time.format("%Y-%m-%d-%H"))) { - Ok(log_file) => file_cfg = file_cfg.chain(log_file), - Err(e) => log_file_created = Err(e), + // Try to create the logs file parent directories. + let mut log_file_created = fs::create_dir_all(&settings.log.logs_path); + + if log_file_created.is_ok() { + // Try to create the log file. + match fern::log_file( + settings + .log + .logs_path + .join(&format!("voxygen-{}.log", time.format("%Y-%m-%d-%H"))), + ) { + Ok(log_file) => file_cfg = file_cfg.chain(log_file), + Err(e) => log_file_created = Err(e), + } } let stdout_cfg = fern::Dispatch::new() @@ -65,6 +74,7 @@ pub fn init( .apply() .expect("Failed to setup logging!"); + // Incase that the log file creation failed simply print it to the console if let Err(e) = log_file_created { log::error!("Failed to create log file! {}", e); } diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 53536055ce..f2a091daf0 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -1,92 +1,21 @@ #![deny(unsafe_code)] -#![feature(drain_filter)] #![recursion_limit = "2048"] -#[macro_use] -pub mod ui; -pub mod anim; -pub mod audio; -mod ecs; -pub mod error; -pub mod hud; -pub mod i18n; -pub mod key_state; -mod logging; -pub mod menu; -pub mod mesh; -pub mod meta; -pub mod render; -pub mod scene; -pub mod session; -pub mod settings; -#[cfg(feature = "singleplayer")] -pub mod singleplayer; -pub mod window; - -// Reexports -pub use crate::error::Error; - -use crate::{ - audio::AudioFrontend, - i18n::{i18n_asset_key, VoxygenLocalization}, +use veloren_voxygen::{ + audio::{self, AudioFrontend}, + i18n::{self, i18n_asset_key, VoxygenLocalization}, + logging, menu::main::MainMenuState, meta::Meta, settings::Settings, window::Window, + Direction, GlobalState, PlayState, PlayStateResult, }; + use common::assets::{load, load_expect}; use log::{debug, error}; use std::{mem, panic, str::FromStr}; -/// A type used to store state that is shared between all play states. -pub struct GlobalState { - settings: Settings, - meta: Meta, - window: Window, - audio: AudioFrontend, - info_message: Option, -} - -impl GlobalState { - /// Called after a change in play state has occurred (usually used to - /// reverse any temporary effects a state may have made). - pub fn on_play_state_changed(&mut self) { - self.window.grab_cursor(false); - self.window.needs_refresh_resize(); - } - - pub fn maintain(&mut self, dt: f32) { self.audio.maintain(dt); } -} - -pub enum Direction { - Forwards, - Backwards, -} - -/// States can either close (and revert to a previous state), push a new state -/// on top of themselves, or switch to a totally different state. -pub enum PlayStateResult { - /// Pop all play states in reverse order and shut down the program. - Shutdown, - /// Close the current play state and pop it from the play state stack. - Pop, - /// Push a new play state onto the play state stack. - Push(Box), - /// Switch the current play state with a new play state. - Switch(Box), -} - -/// A trait representing a playable game state. This may be a menu, a game -/// session, the title screen, etc. -pub trait PlayState { - /// Play the state until some change of state is required (i.e: a menu is - /// opened or the game is closed). - fn play(&mut self, direction: Direction, global_state: &mut GlobalState) -> PlayStateResult; - - /// Get a descriptive name for this state type. - fn name(&self) -> &'static str; -} - fn main() { // Initialize logging. let term_log_level = std::env::var_os("VOXYGEN_LOG") @@ -135,6 +64,7 @@ fn main() { settings, meta, info_message: None, + singleplayer: None, }; // Try to load the localization and log missing entries diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 6dc28fb820..f7eaae27c2 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -1,16 +1,16 @@ -mod scene; mod ui; use crate::{ i18n::{i18n_asset_key, VoxygenLocalization}, + scene::simple::{self as scene, Scene}, session::SessionState, window::Event as WinEvent, Direction, GlobalState, PlayState, PlayStateResult, }; use client::{self, Client}; -use common::{assets, clock::Clock, comp, msg::ClientState}; +use common::{assets, clock::Clock, comp, msg::ClientState, state::DeltaTime}; use log::error; -use scene::Scene; +use specs::WorldExt; use std::{cell::RefCell, rc::Rc, time::Duration}; use ui::CharSelectionUi; @@ -26,7 +26,10 @@ impl CharSelectionState { Self { char_selection_ui: CharSelectionUi::new(global_state), client, - scene: Scene::new(global_state.window.renderer_mut()), + scene: Scene::new( + global_state.window.renderer_mut(), + Some("fixture.selection_bg"), + ), } } } @@ -95,17 +98,23 @@ impl PlayState for CharSelectionState { }); // Maintain the scene. - self.scene.maintain( - global_state.window.renderer_mut(), - &self.client.borrow(), - humanoid_body.clone(), - global_state.settings.graphics.gamma, - ); + { + let client = self.client.borrow(); + let scene_data = scene::SceneData { + time: client.state().get_time(), + delta_time: client.state().ecs().read_resource::().0, + tick: client.get_tick(), + body: humanoid_body.clone(), + gamma: global_state.settings.graphics.gamma, + }; + self.scene + .maintain(global_state.window.renderer_mut(), scene_data); + } // Render the scene. self.scene.render( global_state.window.renderer_mut(), - &self.client.borrow(), + self.client.borrow().get_tick(), humanoid_body.clone(), &comp::Equipment { main: self diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 9ce56b48dc..504e3d6dc9 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -18,7 +18,6 @@ use ui::{Event as MainMenuEvent, MainMenuUi}; pub struct MainMenuState { main_menu_ui: MainMenuUi, - singleplayer: Option, } impl MainMenuState { @@ -26,7 +25,6 @@ impl MainMenuState { pub fn new(global_state: &mut GlobalState) -> Self { Self { main_menu_ui: MainMenuUi::new(global_state), - singleplayer: None, } } } @@ -47,7 +45,7 @@ impl PlayState for MainMenuState { } // Reset singleplayer server if it was running already - self.singleplayer = None; + global_state.singleplayer = None; loop { // Handle window events. @@ -119,7 +117,7 @@ impl PlayState for MainMenuState { // client_init contains Some(ClientInit), which spawns a thread which // contains a TcpStream::connect() call This call is // blocking TODO fix when the network rework happens - self.singleplayer = None; + global_state.singleplayer = None; client_init = None; self.main_menu_ui.cancel_connection(); }, @@ -127,7 +125,7 @@ impl PlayState for MainMenuState { MainMenuEvent::StartSingleplayer => { let (singleplayer, server_settings) = Singleplayer::new(None); // TODO: Make client and server use the same thread pool - self.singleplayer = Some(singleplayer); + global_state.singleplayer = Some(singleplayer); attempt_login( global_state, diff --git a/voxygen/src/scene/camera.rs b/voxygen/src/scene/camera.rs index a0d0d59e54..9eb279d3c5 100644 --- a/voxygen/src/scene/camera.rs +++ b/voxygen/src/scene/camera.rs @@ -1,4 +1,3 @@ -use client::Client; use common::vol::{ReadVol, Vox}; use std::f32::consts::PI; use treeculler::Frustum; @@ -22,6 +21,13 @@ impl Default for CameraMode { fn default() -> Self { Self::ThirdPerson } } +#[derive(Clone)] +pub struct Dependents { + pub view_mat: Mat4, + pub proj_mat: Mat4, + pub cam_pos: Vec3, +} + pub struct Camera { tgt_focus: Vec3, focus: Vec3, @@ -33,6 +39,8 @@ pub struct Camera { mode: CameraMode, last_time: Option, + + dependents: Dependents, } impl Camera { @@ -49,12 +57,18 @@ impl Camera { mode, last_time: None, + + dependents: Dependents { + view_mat: Mat4::identity(), + proj_mat: Mat4::identity(), + cam_pos: Vec3::zero(), + }, } } /// Compute the transformation matrices (view matrix and projection matrix) /// and position of the camera. - pub fn compute_dependents(&self, client: &Client) -> (Mat4, Mat4, Vec3) { + pub fn compute_dependents(&mut self, terrain: &impl ReadVol) { let dist = { let (start, end) = ( self.focus @@ -66,9 +80,7 @@ impl Camera { self.focus, ); - match client - .state() - .terrain() + match terrain .ray(start, end) .ignore_error() .max_iter(500) @@ -82,7 +94,7 @@ impl Camera { .max(0.0) }; - let view_mat = Mat4::::identity() + self.dependents.view_mat = Mat4::::identity() * Mat4::translation_3d(-Vec3::unit_z() * dist) * Mat4::rotation_z(self.ori.z) * Mat4::rotation_x(self.ori.y) @@ -90,20 +102,21 @@ impl Camera { * Mat4::rotation_3d(PI / 2.0, -Vec4::unit_x()) * Mat4::translation_3d(-self.focus); - let proj_mat = Mat4::perspective_rh_no(self.fov, self.aspect, NEAR_PLANE, FAR_PLANE); + self.dependents.proj_mat = + Mat4::perspective_rh_no(self.fov, self.aspect, NEAR_PLANE, FAR_PLANE); // TODO: Make this more efficient. - let cam_pos = Vec3::from(view_mat.inverted() * Vec4::unit_w()); - - (view_mat, proj_mat, cam_pos) + self.dependents.cam_pos = Vec3::from(self.dependents.view_mat.inverted() * Vec4::unit_w()); } - pub fn frustum(&self, client: &Client) -> Frustum { - let (view_mat, proj_mat, _) = self.compute_dependents(client); - - Frustum::from_modelview_projection((proj_mat * view_mat).into_col_arrays()) + pub fn frustum(&self) -> Frustum { + Frustum::from_modelview_projection( + (self.dependents.proj_mat * self.dependents.view_mat).into_col_arrays(), + ) } + pub fn dependents(&self) -> Dependents { self.dependents.clone() } + /// Rotate the camera about its focus by the given delta, limiting the input /// accordingly. pub fn rotate_by(&mut self, delta: Vec3) { diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 35bdf5cbcc..01b9323f8f 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -167,21 +167,6 @@ impl FigureModelCache { }, CameraMode::FirstPerson => None, }, - if camera_mode != CameraMode::FirstPerson - || character_state - .map(|cs| match cs { - CharacterState::BasicAttack { .. } - | CharacterState::BasicBlock { .. } - | CharacterState::Equipping { .. } - | CharacterState::Wielding { .. } => true, - _ => false, - }) - .unwrap_or_default() - { - Some(mesh_main(equipment.and_then(|e| e.main.as_ref()))) - } else { - None - }, match camera_mode { CameraMode::ThirdPerson => Some( humanoid_armor_shoulder_spec.mesh_left_shoulder(&body), @@ -195,6 +180,19 @@ impl FigureModelCache { CameraMode::FirstPerson => None, }, Some(mesh_glider()), + if camera_mode != CameraMode::FirstPerson + || character_state + .map(|cs| { + cs.action.is_attack() + || cs.action.is_block() + || cs.action.is_wield() + }) + .unwrap_or_default() + { + Some(mesh_main(equipment.and_then(|e| e.main.as_ref()))) + } else { + None + }, Some(mesh_lantern()), None, None, diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 80eea9d095..1bcfd294c4 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -1,5 +1,5 @@ mod cache; -mod load; +pub mod load; pub use cache::FigureModelCache; pub use load::load_mesh; // TODO: Don't make this public. @@ -13,13 +13,16 @@ use crate::{ quadruped_small::QuadrupedSmallSkeleton, Animation, Skeleton, }, render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer, Shadow}, - scene::camera::{Camera, CameraMode}, + scene::{ + camera::{Camera, CameraMode}, + SceneData, + }, }; -use client::Client; use common::{ comp::{ Body, CharacterState, ItemKind, Last, Ori, PhysicsState, Pos, Scale, Stats, ToolData, Vel, }, + state::State, terrain::TerrainChunk, vol::RectRasterableVol, }; @@ -95,17 +98,18 @@ impl FigureMgr { self.biped_large_model_cache.clean(tick); } - pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client, camera: &Camera) { - let time = client.state().get_time(); - let tick = client.get_tick(); - let ecs = client.state().ecs(); - let view_distance = client.view_distance().unwrap_or(1); - let dt = client.state().get_delta_time(); - let frustum = camera.frustum(client); + pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData, camera: &Camera) { + let state = scene_data.state; + let time = state.get_time(); + let tick = scene_data.tick; + let ecs = state.ecs(); + let view_distance = scene_data.view_distance; + let dt = state.get_delta_time(); + let frustum = camera.frustum(); // Get player position. let player_pos = ecs .read_storage::() - .get(client.entity()) + .get(scene_data.player_entity) .map_or(Vec3::zero(), |pos| pos.0); for (entity, pos, vel, ori, scale, body, character, last_character, physics, stats) in ( @@ -1252,7 +1256,7 @@ impl FigureMgr { } } - // Clear states that have dead entities. + // Clear states that have deleted entities. self.character_states .retain(|entity, _| ecs.entities().is_alive(*entity)); self.quadruped_small_states @@ -1280,19 +1284,18 @@ impl FigureMgr { pub fn render( &mut self, renderer: &mut Renderer, - client: &mut Client, + state: &State, + player_entity: EcsEntity, + tick: u64, globals: &Consts, lights: &Consts, shadows: &Consts, camera: &Camera, ) { - let tick = client.get_tick(); - let ecs = client.state().ecs(); + let ecs = state.ecs(); - let character_state_storage = client - .state() - .read_storage::(); - let character_state = character_state_storage.get(client.entity()); + let character_state_storage = state.read_storage::(); + let character_state = character_state_storage.get(player_entity); for (entity, _, _, body, stats, _) in ( &ecs.entities(), @@ -1306,7 +1309,7 @@ impl FigureMgr { // Don't render dead entities .filter(|(_, _, _, _, stats, _)| stats.map_or(true, |s| !s.is_dead)) { - let is_player = entity == client.entity(); + let is_player = entity == player_entity; let player_camera_mode = if is_player { camera.get_mode() } else { diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 160384e1bc..84c6aa2f3e 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -1,29 +1,29 @@ pub mod camera; pub mod figure; +pub mod simple; pub mod terrain; use self::{ camera::{Camera, CameraMode}, figure::FigureMgr, - music::MusicMgr, terrain::Terrain, }; use crate::{ anim::character::SkeletonAttr, - audio::{music, sfx::SfxMgr, AudioFrontend}, + audio::{music::MusicMgr, sfx::SfxMgr, AudioFrontend}, render::{ create_pp_mesh, create_skybox_mesh, Consts, Globals, Light, Model, PostProcessLocals, PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline, }, window::Event, }; -use client::Client; use common::{ comp, + state::State, terrain::{BlockKind, TerrainChunk}, vol::ReadVol, }; -use specs::{Join, WorldExt}; +use specs::{Entity as EcsEntity, Join, WorldExt}; use vek::*; // TODO: Don't hard-code this. @@ -62,6 +62,15 @@ pub struct Scene { music_mgr: MusicMgr, } +pub struct SceneData<'a> { + pub state: &'a State, + pub player_entity: specs::Entity, + pub loaded_distance: f32, + pub view_distance: u32, + pub tick: u64, + pub thread_pool: &'a uvth::ThreadPool, +} + impl Scene { /// Create a new `Scene` with default parameters. pub fn new(renderer: &mut Renderer) -> Self { @@ -148,29 +157,29 @@ impl Scene { &mut self, renderer: &mut Renderer, audio: &mut AudioFrontend, - client: &Client, + scene_data: &SceneData, gamma: f32, ) { // Get player position. - let player_pos = client - .state() + let player_pos = scene_data + .state .ecs() .read_storage::() - .get(client.entity()) + .get(scene_data.player_entity) .map_or(Vec3::zero(), |pos| pos.0); - let player_rolling = client - .state() + let player_rolling = scene_data + .state .ecs() .read_storage::() - .get(client.entity()) - .map_or(false, |cs| cs.is_dodge()); + .get(scene_data.player_entity) + .map_or(false, |cs| cs.action.is_roll()); - let player_scale = match client - .state() + let player_scale = match scene_data + .state .ecs() .read_storage::() - .get(client.entity()) + .get(scene_data.player_entity) { Some(comp::Body::Humanoid(body)) => SkeletonAttr::calculate_scale(body), _ => 1_f32, @@ -196,25 +205,30 @@ impl Scene { ); // Tick camera for interpolation. - self.camera.update(client.state().get_time()); + self.camera.update(scene_data.state.get_time()); // Compute camera matrices. - let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client); + self.camera.compute_dependents(&*scene_data.state.terrain()); + let camera::Dependents { + view_mat, + proj_mat, + cam_pos, + } = self.camera.dependents(); // Update chunk loaded distance smoothly for nice shader fog - let loaded_distance = client.loaded_distance(); - self.loaded_distance = (0.98 * self.loaded_distance + 0.02 * loaded_distance).max(0.01); + self.loaded_distance = + (0.98 * self.loaded_distance + 0.02 * scene_data.loaded_distance).max(0.01); // Update light constants let mut lights = ( - &client.state().ecs().read_storage::(), - client.state().ecs().read_storage::().maybe(), - client - .state() + &scene_data.state.ecs().read_storage::(), + scene_data.state.ecs().read_storage::().maybe(), + scene_data + .state .ecs() .read_storage::() .maybe(), - &client.state().ecs().read_storage::(), + &scene_data.state.ecs().read_storage::(), ) .join() .filter(|(pos, _, _, _)| { @@ -247,15 +261,15 @@ impl Scene { // Update shadow constants let mut shadows = ( - &client.state().ecs().read_storage::(), - client - .state() + &scene_data.state.ecs().read_storage::(), + scene_data + .state .ecs() .read_storage::() .maybe(), - client.state().ecs().read_storage::().maybe(), - &client.state().ecs().read_storage::(), - &client.state().ecs().read_storage::(), + scene_data.state.ecs().read_storage::().maybe(), + &scene_data.state.ecs().read_storage::(), + &scene_data.state.ecs().read_storage::(), ) .join() .filter(|(_, _, _, _, stats)| !stats.is_dead) @@ -285,13 +299,13 @@ impl Scene { cam_pos, self.camera.get_focus_pos(), self.loaded_distance, - client.state().get_time_of_day(), - client.state().get_time(), + scene_data.state.get_time_of_day(), + scene_data.state.get_time(), renderer.get_resolution(), lights.len(), shadows.len(), - client - .state() + scene_data + .state .terrain() .get(cam_pos.map(|e| e.floor() as i32)) .map(|b| b.kind()) @@ -304,7 +318,7 @@ impl Scene { // Maintain the terrain. self.terrain.maintain( renderer, - client, + &scene_data, self.camera.get_focus_pos(), self.loaded_distance, view_mat, @@ -312,22 +326,31 @@ impl Scene { ); // Maintain the figures. - self.figure_mgr.maintain(renderer, client, &self.camera); + self.figure_mgr.maintain(renderer, scene_data, &self.camera); // Remove unused figures. - self.figure_mgr.clean(client.get_tick()); + self.figure_mgr.clean(scene_data.tick); // Maintain audio - self.sfx_mgr.maintain(audio, client); - self.music_mgr.maintain(audio, client); + self.sfx_mgr + .maintain(audio, scene_data.state, scene_data.player_entity); + self.music_mgr.maintain(audio, scene_data.state); } /// Render the scene using the provided `Renderer`. - pub fn render(&mut self, renderer: &mut Renderer, client: &mut Client) { + pub fn render( + &mut self, + renderer: &mut Renderer, + state: &State, + player_entity: EcsEntity, + tick: u64, + ) { // Render terrain and figures. self.figure_mgr.render( renderer, - client, + state, + player_entity, + tick, &self.globals, &self.lights, &self.shadows, diff --git a/voxygen/src/menu/char_selection/scene.rs b/voxygen/src/scene/simple.rs similarity index 66% rename from voxygen/src/menu/char_selection/scene.rs rename to voxygen/src/scene/simple.rs index ef8a5ba82a..a0f6d159e7 100644 --- a/voxygen/src/menu/char_selection/scene.rs +++ b/voxygen/src/scene/simple.rs @@ -9,21 +9,37 @@ use crate::{ PostProcessLocals, PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline, }, scene::{ - camera::{Camera, CameraMode}, + camera::{self, Camera, CameraMode}, figure::{load_mesh, FigureModelCache, FigureState}, }, window::{Event, PressState}, }; -use client::Client; use common::{ comp::{humanoid, Body, Equipment}, - state::DeltaTime, terrain::BlockKind, + vol::{BaseVol, ReadVol, Vox}, }; use log::error; -use specs::WorldExt; use vek::*; +#[derive(PartialEq, Eq, Copy, Clone)] +struct VoidVox; +impl Vox for VoidVox { + fn empty() -> Self { VoidVox } + + fn is_empty(&self) -> bool { true } + + fn or(self, _other: Self) -> Self { VoidVox } +} +struct VoidVol; +impl BaseVol for VoidVol { + type Error = (); + type Vox = VoidVox; +} +impl ReadVol for VoidVol { + fn get<'a>(&'a self, _pos: Vec3) -> Result<&'a Self::Vox, Self::Error> { Ok(&VoidVox) } +} + struct Skybox { model: Model, locals: Consts, @@ -42,8 +58,7 @@ pub struct Scene { skybox: Skybox, postprocess: PostProcess, - backdrop_model: Model, - backdrop_state: FigureState, + backdrop: Option<(Model, FigureState)>, figure_model_cache: FigureModelCache, figure_state: FigureState, @@ -52,15 +67,28 @@ pub struct Scene { char_ori: f32, } +pub struct SceneData { + pub time: f64, + pub delta_time: f32, + pub tick: u64, + pub body: Option, + pub gamma: f32, +} + impl Scene { - pub fn new(renderer: &mut Renderer) -> Self { + pub fn new(renderer: &mut Renderer, backdrop: Option<&str>) -> Self { let resolution = renderer.get_resolution().map(|e| e as f32); + let mut camera = Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson); + camera.set_focus_pos(Vec3::unit_z() * 1.5); + camera.set_distance(3.0); // 4.2 + camera.set_orientation(Vec3::new(0.0, 0.0, 0.0)); + Self { globals: renderer.create_consts(&[Globals::default()]).unwrap(), lights: renderer.create_consts(&[Light::default(); 32]).unwrap(), shadows: renderer.create_consts(&[Shadow::default(); 32]).unwrap(), - camera: Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson), + camera, skybox: Skybox { model: renderer.create_model(&create_skybox_mesh()).unwrap(), @@ -75,13 +103,14 @@ impl Scene { figure_model_cache: FigureModelCache::new(), figure_state: FigureState::new(renderer, CharacterSkeleton::new()), - backdrop_model: renderer - .create_model(&load_mesh( - "fixture.selection_bg", - Vec3::new(-55.0, -49.5, -2.0), - )) - .unwrap(), - backdrop_state: FigureState::new(renderer, FixtureSkeleton::new()), + backdrop: backdrop.map(|specifier| { + ( + renderer + .create_model(&load_mesh(specifier, Vec3::new(-55.0, -49.5, -2.0))) + .unwrap(), + FigureState::new(renderer, FixtureSkeleton::new()), + ) + }), turning: false, char_ori: 0.0, @@ -90,6 +119,8 @@ impl Scene { pub fn globals(&self) -> &Consts { &self.globals } + pub fn camera_mut(&mut self) -> &mut Camera { &mut self.camera } + /// Handle an incoming user input event (e.g.: cursor moved, key pressed, /// window closed). /// @@ -114,22 +145,17 @@ impl Scene { } } - pub fn maintain( - &mut self, - renderer: &mut Renderer, - client: &Client, - body: Option, - gamma: f32, - ) { - self.camera.set_focus_pos(Vec3::unit_z() * 1.5); - self.camera.update(client.state().get_time()); - self.camera.set_distance(3.0); // 4.2 - self.camera - .set_orientation(Vec3::new(client.state().get_time() as f32 * 0.0, 0.0, 0.0)); + pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: SceneData) { + self.camera.update(scene_data.time); - let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client); - const VD: f32 = 115.0; //View Distance - const TIME: f64 = 43200.0; // hours*3600 seconds + self.camera.compute_dependents(&VoidVol); + let camera::Dependents { + view_mat, + proj_mat, + cam_pos, + } = self.camera.dependents(); + const VD: f32 = 115.0; // View Distance + const TIME: f64 = 43200.0; // 12 hours*3600 seconds if let Err(err) = renderer.update_consts(&mut self.globals, &[Globals::new( view_mat, proj_mat, @@ -137,31 +163,30 @@ impl Scene { self.camera.get_focus_pos(), VD, TIME, - client.state().get_time(), + scene_data.time, renderer.get_resolution(), 0, 0, BlockKind::Air, None, - gamma, + scene_data.gamma, )]) { error!("Renderer failed to update: {:?}", err); } - self.figure_model_cache.clean(client.get_tick()); + self.figure_model_cache.clean(scene_data.tick); - if let Some(body) = body { + if let Some(body) = scene_data.body { let tgt_skeleton = IdleAnimation::update_skeleton( self.figure_state.skeleton_mut(), - client.state().get_time(), - client.state().get_time(), + scene_data.time, + scene_data.time, &mut 0.0, &SkeletonAttr::from(&body), ); - self.figure_state.skeleton_mut().interpolate( - &tgt_skeleton, - client.state().ecs().read_resource::().0, - ); + self.figure_state + .skeleton_mut() + .interpolate(&tgt_skeleton, scene_data.delta_time); } self.figure_state.update( @@ -181,7 +206,7 @@ impl Scene { pub fn render( &mut self, renderer: &mut Renderer, - client: &Client, + tick: u64, body: Option, equipment: &Equipment, ) { @@ -194,7 +219,7 @@ impl Scene { renderer, Body::Humanoid(body), Some(equipment), - client.get_tick(), + tick, CameraMode::default(), None, ) @@ -210,14 +235,16 @@ impl Scene { ); } - renderer.render_figure( - &self.backdrop_model, - &self.globals, - self.backdrop_state.locals(), - self.backdrop_state.bone_consts(), - &self.lights, - &self.shadows, - ); + if let Some((model, state)) = &self.backdrop { + renderer.render_figure( + model, + &self.globals, + state.locals(), + state.bone_consts(), + &self.lights, + &self.shadows, + ); + } renderer.render_post_process( &self.postprocess.model, diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index c6c41f4928..be5f7c40c3 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -6,7 +6,7 @@ use crate::{ }, }; -use client::Client; +use super::SceneData; use common::{ assets, figure::Segment, @@ -1083,26 +1083,26 @@ impl Terrain { pub fn maintain( &mut self, renderer: &mut Renderer, - client: &Client, + scene_data: &SceneData, focus_pos: Vec3, loaded_distance: f32, view_mat: Mat4, proj_mat: Mat4, ) { - let current_tick = client.get_tick(); - let current_time = client.state().get_time(); + let current_tick = scene_data.tick; + let current_time = scene_data.state.get_time(); // Add any recently created or changed chunks to the list of chunks to be // meshed. - for (modified, pos) in client - .state() + for (modified, pos) in scene_data + .state .terrain_changes() .modified_chunks .iter() .map(|c| (true, c)) .chain( - client - .state() + scene_data + .state .terrain_changes() .new_chunks .iter() @@ -1121,8 +1121,8 @@ impl Terrain { let mut neighbours = true; for i in -1..2 { for j in -1..2 { - neighbours &= client - .state() + neighbours &= scene_data + .state .terrain() .get_key(pos + Vec2::new(i, j)) .is_some(); @@ -1143,20 +1143,20 @@ impl Terrain { // Add the chunks belonging to recently changed blocks to the list of chunks to // be meshed - for pos in client - .state() + for pos in scene_data + .state .terrain_changes() .modified_blocks .iter() .map(|(p, _)| *p) { - let chunk_pos = client.state().terrain().pos_key(pos); + let chunk_pos = scene_data.state.terrain().pos_key(pos); // Only mesh if this chunk has all its neighbors let mut neighbours = true; for i in -1..2 { for j in -1..2 { - neighbours &= client - .state() + neighbours &= scene_data + .state .terrain() .get_key(chunk_pos + Vec2::new(i, j)) .is_some(); @@ -1178,15 +1178,15 @@ impl Terrain { for x in -1..2 { for y in -1..2 { let neighbour_pos = pos + Vec3::new(x, y, 0); - let neighbour_chunk_pos = client.state().terrain().pos_key(neighbour_pos); + let neighbour_chunk_pos = scene_data.state.terrain().pos_key(neighbour_pos); if neighbour_chunk_pos != chunk_pos { // Only remesh if this chunk has all its neighbors let mut neighbours = true; for i in -1..2 { for j in -1..2 { - neighbours &= client - .state() + neighbours &= scene_data + .state .terrain() .get_key(neighbour_chunk_pos + Vec2::new(i, j)) .is_some(); @@ -1205,7 +1205,7 @@ impl Terrain { } // Remove any models for chunks that have been recently removed. - for pos in &client.state().terrain_changes().removed_chunks { + for pos in &scene_data.state.terrain_changes().removed_chunks { self.chunks.remove(pos); self.mesh_todo.remove(pos); } @@ -1220,7 +1220,7 @@ impl Terrain { }) .min_by_key(|todo| todo.active_worker.unwrap_or(todo.started_tick)) { - if client.thread_pool().queued_jobs() > 0 { + if scene_data.thread_pool.queued_jobs() > 0 { break; } @@ -1239,7 +1239,7 @@ impl Terrain { // Copy out the chunk data we need to perform the meshing. We do this by taking // a sample of the terrain that includes both the chunk we want and // its neighbours. - let volume = match client.state().terrain().sample(aabr) { + let volume = match scene_data.state.terrain().sample(aabr) { Ok(sample) => sample, // Either this chunk or its neighbours doesn't yet exist, so we keep it in the // queue to be processed at a later date when we have its neighbours. @@ -1266,7 +1266,7 @@ impl Terrain { // Queue the worker thread. let started_tick = todo.started_tick; - client.thread_pool().execute(move || { + scene_data.thread_pool.execute(move || { let _ = send.send(mesh_worker( pos, (min_z as f32, max_z as f32), diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 1a2be92867..d156fb6708 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -4,7 +4,7 @@ use crate::{ i18n::{i18n_asset_key, VoxygenLocalization}, key_state::KeyState, render::Renderer, - scene::Scene, + scene::{camera, Scene, SceneData}, window::{Event, GameInput}, Direction, Error, GlobalState, PlayState, PlayStateResult, }; @@ -108,7 +108,11 @@ impl SessionState { renderer.clear(); // Render the screen using the global renderer - self.scene.render(renderer, &mut self.client.borrow_mut()); + { + let client = self.client.borrow(); + self.scene + .render(renderer, client.state(), client.entity(), client.get_tick()); + } // Draw the UI to the screen self.hud.render(renderer, self.scene.globals()); @@ -145,10 +149,12 @@ impl PlayState for SessionState { let mut current_client_state = self.client.borrow().get_client_state(); while let ClientState::Pending | ClientState::Character = current_client_state { // Compute camera data - let (view_mat, _, cam_pos) = self - .scene - .camera() - .compute_dependents(&self.client.borrow()); + self.scene + .camera_mut() + .compute_dependents(&*self.client.borrow().state().terrain()); + let camera::Dependents { + view_mat, cam_pos, .. + } = self.scene.camera().dependents(); let cam_dir: Vec3 = Vec3::from(view_mat.inverted() * -Vec4::unit_z()); // Check to see whether we're aiming at anything @@ -368,18 +374,27 @@ impl PlayState for SessionState { self.inputs.look_dir = cam_dir; - // Perform an in-game tick. - if let Err(err) = self.tick(clock.get_avg_delta()) { - global_state.info_message = - Some(localized_strings.get("common.connection_lost").to_owned()); - error!("[session] Failed to tick the scene: {:?}", err); + // Runs if either in a multiplayer server or the singleplayer server is unpaused + if global_state.singleplayer.is_none() + || !global_state.singleplayer.as_ref().unwrap().is_paused() + { + // Perform an in-game tick. + if let Err(err) = self.tick(clock.get_avg_delta()) { + global_state.info_message = + Some(localized_strings.get("common.connection_lost").to_owned()); + error!("[session] Failed to tick the scene: {:?}", err); - return PlayStateResult::Pop; + return PlayStateResult::Pop; + } } // Maintain global state. global_state.maintain(clock.get_last_delta().as_secs_f32()); + // Recompute dependents just in case some input modified the camera + self.scene + .camera_mut() + .compute_dependents(&*self.client.borrow().state().terrain()); // Extract HUD events ensuring the client borrow gets dropped. let mut hud_events = self.hud.maintain( &self.client.borrow(), @@ -539,6 +554,9 @@ impl PlayState for SessionState { global_state.settings.graphics.fov = new_fov; global_state.settings.save_to_file_warn(); self.scene.camera_mut().set_fov_deg(new_fov); + self.scene + .camera_mut() + .compute_dependents(&*self.client.borrow().state().terrain()); }, HudEvent::ChangeGamma(new_gamma) => { global_state.settings.graphics.gamma = new_gamma; @@ -598,13 +616,26 @@ impl PlayState for SessionState { } } - // Maintain the scene. - self.scene.maintain( - global_state.window.renderer_mut(), - &mut global_state.audio, - &self.client.borrow(), - global_state.settings.graphics.gamma, - ); + // Runs if either in a multiplayer server or the singleplayer server is unpaused + if global_state.singleplayer.is_none() + || !global_state.singleplayer.as_ref().unwrap().is_paused() + { + let client = self.client.borrow(); + let scene_data = SceneData { + state: client.state(), + player_entity: client.entity(), + loaded_distance: client.loaded_distance(), + view_distance: client.view_distance().unwrap_or(1), + tick: client.get_tick(), + thread_pool: client.thread_pool(), + }; + self.scene.maintain( + global_state.window.renderer_mut(), + &mut global_state.audio, + &scene_data, + global_state.settings.graphics.gamma, + ); + } // Render the session. self.render(global_state.window.renderer_mut()); diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 8d339f2837..5bae056d85 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -5,7 +5,7 @@ use crate::{ ui::ScaleMode, window::KeyMouse, }; -use directories::ProjectDirs; +use directories::{ProjectDirs, UserDirs}; use glutin::{MouseButton, VirtualKeyCode}; use log::warn; use serde_derive::{Deserialize, Serialize}; @@ -177,10 +177,28 @@ pub struct Log { // Whether to create a log file or not. // Default is to create one. pub log_to_file: bool, + // The path on which the logs will be stored + pub logs_path: PathBuf, } impl Default for Log { - fn default() -> Self { Self { log_to_file: true } } + fn default() -> Self { + let proj_dirs = ProjectDirs::from("net", "veloren", "voxygen") + .expect("System's $HOME directory path not found!"); + + // Chooses a path to store the logs by the following order: + // - The VOXYGEN_LOGS environment variable + // - The ProjectsDirs data local directory + // This only selects if there isn't already an entry in the settings file + let logs_path = std::env::var_os("VOXYGEN_LOGS") + .map(PathBuf::from) + .unwrap_or(proj_dirs.data_local_dir().join("logs")); + + Self { + log_to_file: true, + logs_path, + } + } } /// `GraphicsSettings` contains settings related to framerate and in-game @@ -273,10 +291,26 @@ pub struct Settings { // TODO: Remove at a later date, for dev testing pub logon_commands: Vec, pub language: LanguageSettings, + pub screenshots_path: PathBuf, } impl Default for Settings { fn default() -> Self { + let user_dirs = UserDirs::new().expect("System's $HOME directory path not found!"); + + // Chooses a path to store the screenshots by the following order: + // - The VOXYGEN_SCREENSHOT environment variable + // - The user's picture directory + // - The executable's directory + // This only selects if there isn't already an entry in the settings file + let screenshots_path = std::env::var_os("VOXYGEN_SCREENSHOT") + .map(PathBuf::from) + .or(user_dirs.picture_dir().map(|dir| dir.join("veloren"))) + .or(std::env::current_exe() + .ok() + .and_then(|dir| dir.parent().map(|val| PathBuf::from(val)))) + .expect("Couldn't choose a place to store the screenshots"); + Settings { controls: ControlSettings::default(), gameplay: GameplaySettings::default(), @@ -288,6 +322,7 @@ impl Default for Settings { send_logon_commands: false, logon_commands: Vec::new(), language: LanguageSettings::default(), + screenshots_path, } } } diff --git a/voxygen/src/singleplayer.rs b/voxygen/src/singleplayer.rs index a1b3cec031..a5e9e5a87d 100644 --- a/voxygen/src/singleplayer.rs +++ b/voxygen/src/singleplayer.rs @@ -4,6 +4,10 @@ use crossbeam::channel::{unbounded, Receiver, Sender, TryRecvError}; use log::info; use server::{Event, Input, Server, ServerSettings}; use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, thread::{self, JoinHandle}, time::Duration, }; @@ -19,6 +23,8 @@ enum Msg { pub struct Singleplayer { _server_thread: JoinHandle<()>, sender: Sender, + // Wether the server is stopped or not + paused: Arc, } impl Singleplayer { @@ -31,6 +37,9 @@ impl Singleplayer { let thread_pool = client.map(|c| c.thread_pool().clone()); let settings2 = settings.clone(); + let paused = Arc::new(AtomicBool::new(false)); + let paused1 = paused.clone(); + let thread = thread::spawn(move || { let server = Server::new(settings2).expect("Failed to create server instance!"); @@ -39,17 +48,25 @@ impl Singleplayer { None => server, }; - run_server(server, receiver); + run_server(server, receiver, paused1); }); ( Singleplayer { _server_thread: thread, sender, + paused, }, settings, ) } + + /// Returns wether or not the server is paused + pub fn is_paused(&self) -> bool { self.paused.load(Ordering::SeqCst) } + + /// Pauses if true is passed and unpauses if false (Does nothing if in that + /// state already) + pub fn pause(&self, state: bool) { self.paused.store(state, Ordering::SeqCst); } } impl Drop for Singleplayer { @@ -59,13 +76,34 @@ impl Drop for Singleplayer { } } -fn run_server(mut server: Server, rec: Receiver) { +fn run_server(mut server: Server, rec: Receiver, paused: Arc) { info!("Starting server-cli..."); // Set up an fps clock let mut clock = Clock::start(); loop { + // Check any event such as stopping and pausing + match rec.try_recv() { + Ok(msg) => match msg { + Msg::Stop => break, + }, + Err(err) => match err { + TryRecvError::Empty => (), + TryRecvError::Disconnected => break, + }, + } + + // Wait for the next tick. + clock.tick(Duration::from_millis(1000 / TPS)); + + // Skip updating the server if it's paused + if paused.load(Ordering::SeqCst) && server.number_of_players() < 2 { + continue; + } else if server.number_of_players() > 1 { + paused.store(false, Ordering::SeqCst); + } + let events = server .tick(Input::default(), clock.get_last_delta()) .expect("Failed to tick server!"); @@ -80,16 +118,5 @@ fn run_server(mut server: Server, rec: Receiver) { // Clean up the server after a tick. server.cleanup(); - - match rec.try_recv() { - Ok(_msg) => break, - Err(err) => match err { - TryRecvError::Empty => (), - TryRecvError::Disconnected => break, - }, - } - - // Wait for the next tick. - clock.tick(Duration::from_millis(1000 / TPS)); } } diff --git a/voxygen/src/ui/img_ids.rs b/voxygen/src/ui/img_ids.rs index f3cfc0f65f..ea5a8ac294 100644 --- a/voxygen/src/ui/img_ids.rs +++ b/voxygen/src/ui/img_ids.rs @@ -114,7 +114,12 @@ pub struct Rotations { /// corresponding ImgIds and create a struct with all of them. /// /// Example usage: -/// ``` +/// ```ignore +/// use veloren_voxygen::{ +/// image_ids, +/// ui::img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic}, +/// }; +/// /// image_ids! { /// pub struct Imgs { /// diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 1b43c15787..59c9b6c4ed 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -594,7 +594,7 @@ impl Window { }); if take_screenshot { - self.take_screenshot(); + self.take_screenshot(&settings); } if toggle_fullscreen { @@ -659,15 +659,16 @@ impl Window { pub fn send_supplement_event(&mut self, event: Event) { self.supplement_events.push(event) } - pub fn take_screenshot(&mut self) { + pub fn take_screenshot(&mut self, settings: &Settings) { match self.renderer.create_screenshot() { Ok(img) => { + let mut path = settings.screenshots_path.clone(); + std::thread::spawn(move || { - use std::{path::PathBuf, time::SystemTime}; + use std::time::SystemTime; // Check if folder exists and create it if it does not - let mut path = PathBuf::from("./screenshots"); if !path.exists() { - if let Err(err) = std::fs::create_dir(&path) { + if let Err(err) = std::fs::create_dir_all(&path) { warn!("Couldn't create folder for screenshot: {:?}", err); } }