mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'master' of gitlab.com:veloren/veloren into sharp/small-fixes
This commit is contained in:
commit
6332cbe006
@ -44,8 +44,6 @@ coverage:
|
|||||||
- cargo build --release
|
- cargo build --release
|
||||||
- cp -r target/release/veloren-server-cli $CI_PROJECT_DIR
|
- cp -r target/release/veloren-server-cli $CI_PROJECT_DIR
|
||||||
- cp -r target/release/veloren-voxygen $CI_PROJECT_DIR
|
- cp -r target/release/veloren-voxygen $CI_PROJECT_DIR
|
||||||
- strip --strip-all veloren-server-cli
|
|
||||||
- strip --strip-all veloren-voxygen
|
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- veloren-server-cli
|
- veloren-server-cli
|
||||||
|
@ -40,6 +40,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Better pathfinding
|
- Better pathfinding
|
||||||
- Bombs
|
- Bombs
|
||||||
- Training dummy items
|
- Training dummy items
|
||||||
|
- Added spin attack for axe
|
||||||
|
- Creature specific stats
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3717,7 +3717,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "specs-idvs"
|
name = "specs-idvs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://gitlab.com/veloren/specs-idvs.git#530ebb00b60685d6d1f47a3bc16c5b1f25c5e530"
|
source = "git+https://gitlab.com/veloren/specs-idvs.git?branch=specs-git#fcb0b2306b571f62f9f85d89e79e087454d95efd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"specs",
|
"specs",
|
||||||
]
|
]
|
||||||
|
@ -72,7 +72,3 @@ debug = false
|
|||||||
[profile.releasedebuginfo]
|
[profile.releasedebuginfo]
|
||||||
inherits = 'release'
|
inherits = 'release'
|
||||||
debug = 1
|
debug = 1
|
||||||
|
|
||||||
# living on the edge
|
|
||||||
[patch.crates-io]
|
|
||||||
specs = { git = "https://github.com/amethyst/specs.git", rev = "7a2e348ab2223818bad487695c66c43db88050a5" }
|
|
||||||
|
BIN
assets/voxygen/element/icons/2haxe_m1.png
(Stored with Git LFS)
BIN
assets/voxygen/element/icons/2haxe_m1.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/icons/skill_axespin.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/skill_axespin.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/weapon/axe/rusty_2h.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/weapon/axe/rusty_2h.vox
(Stored with Git LFS)
Binary file not shown.
@ -19,7 +19,7 @@ num = "0.2.0"
|
|||||||
num_cpus = "1.10.1"
|
num_cpus = "1.10.1"
|
||||||
tracing = { version = "0.1", default-features = false }
|
tracing = { version = "0.1", default-features = false }
|
||||||
rayon = "^1.3.0"
|
rayon = "^1.3.0"
|
||||||
specs = "0.16.1"
|
specs = { git = "https://github.com/amethyst/specs.git", rev = "7a2e348ab2223818bad487695c66c43db88050a5" }
|
||||||
vek = { version = "0.11.0", features = ["serde"] }
|
vek = { version = "0.11.0", features = ["serde"] }
|
||||||
hashbrown = { version = "0.7.2", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.7.2", features = ["rayon", "serde", "nightly"] }
|
||||||
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "223a4097f7ebc8d451936dccb5e6517194bbf086" }
|
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "223a4097f7ebc8d451936dccb5e6517194bbf086" }
|
||||||
|
@ -39,6 +39,7 @@ use network::{Address, Network, Participant, Pid, Stream, PROMISES_CONSISTENCY,
|
|||||||
use num::traits::FloatConst;
|
use num::traits::FloatConst;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
@ -58,6 +59,7 @@ const SERVER_TIMEOUT: f64 = 20.0;
|
|||||||
// After this duration has elapsed, the user will begin getting kick warnings in
|
// After this duration has elapsed, the user will begin getting kick warnings in
|
||||||
// their chat window
|
// their chat window
|
||||||
const SERVER_TIMEOUT_GRACE_PERIOD: f64 = 14.0;
|
const SERVER_TIMEOUT_GRACE_PERIOD: f64 = 14.0;
|
||||||
|
const PING_ROLLING_AVERAGE_SECS: usize = 10;
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Chat(comp::ChatMsg),
|
Chat(comp::ChatMsg),
|
||||||
@ -102,6 +104,7 @@ pub struct Client {
|
|||||||
last_server_ping: f64,
|
last_server_ping: f64,
|
||||||
last_server_pong: f64,
|
last_server_pong: f64,
|
||||||
last_ping_delta: f64,
|
last_ping_delta: f64,
|
||||||
|
ping_deltas: VecDeque<f64>,
|
||||||
|
|
||||||
tick: u64,
|
tick: u64,
|
||||||
state: State,
|
state: State,
|
||||||
@ -344,6 +347,7 @@ impl Client {
|
|||||||
last_server_ping: 0.0,
|
last_server_ping: 0.0,
|
||||||
last_server_pong: 0.0,
|
last_server_pong: 0.0,
|
||||||
last_ping_delta: 0.0,
|
last_ping_delta: 0.0,
|
||||||
|
ping_deltas: VecDeque::new(),
|
||||||
|
|
||||||
tick: 0,
|
tick: 0,
|
||||||
state,
|
state,
|
||||||
@ -1030,8 +1034,15 @@ impl Client {
|
|||||||
},
|
},
|
||||||
ServerMsg::Pong => {
|
ServerMsg::Pong => {
|
||||||
self.last_server_pong = self.state.get_time();
|
self.last_server_pong = self.state.get_time();
|
||||||
|
self.last_ping_delta = self.state.get_time() - self.last_server_ping;
|
||||||
|
|
||||||
self.last_ping_delta = (self.state.get_time() - self.last_server_ping).round();
|
// Maintain the correct number of deltas for calculating the rolling average
|
||||||
|
// ping. The client sends a ping to the server every second so we should be
|
||||||
|
// receiving a pong reply roughly every second.
|
||||||
|
while self.ping_deltas.len() > PING_ROLLING_AVERAGE_SECS - 1 {
|
||||||
|
self.ping_deltas.pop_front();
|
||||||
|
}
|
||||||
|
self.ping_deltas.push_back(self.last_ping_delta);
|
||||||
},
|
},
|
||||||
ServerMsg::ChatMsg(m) => frontend_events.push(Event::Chat(m)),
|
ServerMsg::ChatMsg(m) => frontend_events.push(Event::Chat(m)),
|
||||||
ServerMsg::SetPlayerEntity(uid) => {
|
ServerMsg::SetPlayerEntity(uid) => {
|
||||||
@ -1174,6 +1185,22 @@ impl Client {
|
|||||||
|
|
||||||
pub fn get_ping_ms(&self) -> f64 { self.last_ping_delta * 1000.0 }
|
pub fn get_ping_ms(&self) -> f64 { self.last_ping_delta * 1000.0 }
|
||||||
|
|
||||||
|
pub fn get_ping_ms_rolling_avg(&self) -> f64 {
|
||||||
|
let mut total_weight = 0.;
|
||||||
|
let pings = self.ping_deltas.len() as f64;
|
||||||
|
(self
|
||||||
|
.ping_deltas
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.fold(0., |acc, (i, ping)| {
|
||||||
|
let weight = i as f64 + 1. / pings;
|
||||||
|
total_weight += weight;
|
||||||
|
acc + (weight * ping)
|
||||||
|
})
|
||||||
|
/ total_weight)
|
||||||
|
* 1000.0
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a reference to the client's worker thread pool. This pool should be
|
/// Get a reference to the client's worker thread pool. This pool should be
|
||||||
/// used for any computationally expensive operations that run outside
|
/// used for any computationally expensive operations that run outside
|
||||||
/// of the main thread (i.e., threads that block on I/O operations are
|
/// of the main thread (i.e., threads that block on I/O operations are
|
||||||
@ -1197,6 +1224,18 @@ impl Client {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true if this client is an admin on the server
|
||||||
|
pub fn is_admin(&self) -> bool {
|
||||||
|
let client_uid = self
|
||||||
|
.state
|
||||||
|
.read_component_cloned::<Uid>(self.entity)
|
||||||
|
.expect("Client doesn't have a Uid!!!");
|
||||||
|
|
||||||
|
self.player_list
|
||||||
|
.get(&client_uid)
|
||||||
|
.map_or(false, |info| info.is_admin)
|
||||||
|
}
|
||||||
|
|
||||||
/// Clean client ECS state
|
/// Clean client ECS state
|
||||||
fn clean_state(&mut self) {
|
fn clean_state(&mut self) {
|
||||||
let client_uid = self
|
let client_uid = self
|
||||||
|
@ -8,9 +8,9 @@ edition = "2018"
|
|||||||
no-assets = []
|
no-assets = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git" }
|
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", branch = "specs-git" }
|
||||||
|
|
||||||
specs = { version = "0.16.1", features = ["serde", "storage-event-control"] }
|
specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control"], rev = "7a2e348ab2223818bad487695c66c43db88050a5" }
|
||||||
vek = { version = "0.11.0", features = ["serde"] }
|
vek = { version = "0.11.0", features = ["serde"] }
|
||||||
dot_vox = "4.0"
|
dot_vox = "4.0"
|
||||||
image = { version = "0.22.5", default-features = false, features = ["png"] }
|
image = { version = "0.22.5", default-features = false, features = ["png"] }
|
||||||
|
@ -20,6 +20,7 @@ pub enum CharacterAbilityType {
|
|||||||
BasicBlock,
|
BasicBlock,
|
||||||
TripleStrike(Stage),
|
TripleStrike(Stage),
|
||||||
LeapMelee,
|
LeapMelee,
|
||||||
|
SpinMelee,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&CharacterState> for CharacterAbilityType {
|
impl From<&CharacterState> for CharacterAbilityType {
|
||||||
@ -32,6 +33,7 @@ impl From<&CharacterState> for CharacterAbilityType {
|
|||||||
CharacterState::BasicBlock => Self::BasicBlock,
|
CharacterState::BasicBlock => Self::BasicBlock,
|
||||||
CharacterState::LeapMelee(_) => Self::LeapMelee,
|
CharacterState::LeapMelee(_) => Self::LeapMelee,
|
||||||
CharacterState::TripleStrike(data) => Self::TripleStrike(data.stage),
|
CharacterState::TripleStrike(data) => Self::TripleStrike(data.stage),
|
||||||
|
CharacterState::SpinMelee(_) => Self::SpinMelee,
|
||||||
_ => Self::BasicMelee,
|
_ => Self::BasicMelee,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,6 +82,12 @@ pub enum CharacterAbility {
|
|||||||
recover_duration: Duration,
|
recover_duration: Duration,
|
||||||
base_damage: u32,
|
base_damage: u32,
|
||||||
},
|
},
|
||||||
|
SpinMelee {
|
||||||
|
energy_cost: u32,
|
||||||
|
buildup_duration: Duration,
|
||||||
|
recover_duration: Duration,
|
||||||
|
base_damage: u32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CharacterAbility {
|
impl CharacterAbility {
|
||||||
@ -117,6 +125,10 @@ impl CharacterAbility {
|
|||||||
.energy
|
.energy
|
||||||
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
|
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
|
||||||
.is_ok(),
|
.is_ok(),
|
||||||
|
CharacterAbility::SpinMelee { energy_cost, .. } => update
|
||||||
|
.energy
|
||||||
|
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
|
||||||
|
.is_ok(),
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,6 +251,26 @@ impl From<&CharacterAbility> for CharacterState {
|
|||||||
recover_duration: *recover_duration,
|
recover_duration: *recover_duration,
|
||||||
base_damage: *base_damage,
|
base_damage: *base_damage,
|
||||||
}),
|
}),
|
||||||
|
CharacterAbility::SpinMelee {
|
||||||
|
energy_cost,
|
||||||
|
buildup_duration,
|
||||||
|
recover_duration,
|
||||||
|
base_damage,
|
||||||
|
} => CharacterState::SpinMelee(spin_melee::Data {
|
||||||
|
exhausted: false,
|
||||||
|
energy_cost: *energy_cost,
|
||||||
|
buildup_duration: *buildup_duration,
|
||||||
|
buildup_duration_default: *buildup_duration,
|
||||||
|
recover_duration: *recover_duration,
|
||||||
|
recover_duration_default: *recover_duration,
|
||||||
|
base_damage: *base_damage,
|
||||||
|
// This isn't needed for it's continuous implementation, but is left in should this
|
||||||
|
// skill be moved to the skillbar
|
||||||
|
hits_remaining: 1,
|
||||||
|
hits_remaining_default: 1, /* Should be the same value as hits_remaining, also
|
||||||
|
* this value can be removed if ability moved to
|
||||||
|
* skillbar */
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,8 @@ pub enum CharacterState {
|
|||||||
TripleStrike(triple_strike::Data),
|
TripleStrike(triple_strike::Data),
|
||||||
/// A leap followed by a small aoe ground attack
|
/// A leap followed by a small aoe ground attack
|
||||||
LeapMelee(leap_melee::Data),
|
LeapMelee(leap_melee::Data),
|
||||||
|
/// Spin around, dealing damage to enemies surrounding you
|
||||||
|
SpinMelee(spin_melee::Data),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CharacterState {
|
impl CharacterState {
|
||||||
@ -75,7 +77,8 @@ impl CharacterState {
|
|||||||
| CharacterState::DashMelee(_)
|
| CharacterState::DashMelee(_)
|
||||||
| CharacterState::TripleStrike(_)
|
| CharacterState::TripleStrike(_)
|
||||||
| CharacterState::BasicBlock
|
| CharacterState::BasicBlock
|
||||||
| CharacterState::LeapMelee(_) => true,
|
| CharacterState::LeapMelee(_)
|
||||||
|
| CharacterState::SpinMelee(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,7 +89,8 @@ impl CharacterState {
|
|||||||
| CharacterState::BasicRanged(_)
|
| CharacterState::BasicRanged(_)
|
||||||
| CharacterState::DashMelee(_)
|
| CharacterState::DashMelee(_)
|
||||||
| CharacterState::TripleStrike(_)
|
| CharacterState::TripleStrike(_)
|
||||||
| CharacterState::LeapMelee(_) => true,
|
| CharacterState::LeapMelee(_)
|
||||||
|
| CharacterState::SpinMelee(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,13 +221,11 @@ impl Tool {
|
|||||||
base_damage: 7,
|
base_damage: 7,
|
||||||
needs_timing: true,
|
needs_timing: true,
|
||||||
},
|
},
|
||||||
BasicMelee {
|
SpinMelee {
|
||||||
energy_cost: 100,
|
energy_cost: 100,
|
||||||
buildup_duration: Duration::from_millis(700),
|
buildup_duration: Duration::from_millis(125),
|
||||||
recover_duration: Duration::from_millis(100),
|
recover_duration: Duration::from_millis(125),
|
||||||
base_healthchange: -12,
|
base_damage: 5,
|
||||||
range: 3.5,
|
|
||||||
max_angle: 30.0,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
Hammer(_) => vec![
|
Hammer(_) => vec![
|
||||||
|
@ -131,6 +131,81 @@ pub struct Stats {
|
|||||||
pub fitness: u32,
|
pub fitness: u32,
|
||||||
pub willpower: u32,
|
pub willpower: u32,
|
||||||
pub is_dead: bool,
|
pub is_dead: bool,
|
||||||
|
pub body_type: Body,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Body {
|
||||||
|
pub fn base_health(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Body::Humanoid(_) => 52,
|
||||||
|
Body::QuadrupedSmall(_) => 44,
|
||||||
|
Body::QuadrupedMedium(_) => 72,
|
||||||
|
Body::BirdMedium(_) => 36,
|
||||||
|
Body::FishMedium(_) => 32,
|
||||||
|
Body::Dragon(_) => 256,
|
||||||
|
Body::BirdSmall(_) => 24,
|
||||||
|
Body::FishSmall(_) => 20,
|
||||||
|
Body::BipedLarge(_) => 144,
|
||||||
|
Body::Object(_) => 100,
|
||||||
|
Body::Golem(_) => 168,
|
||||||
|
Body::Critter(_) => 32,
|
||||||
|
Body::QuadrupedLow(_) => 64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_health_increase(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Body::Humanoid(_) => 5,
|
||||||
|
Body::QuadrupedSmall(_) => 4,
|
||||||
|
Body::QuadrupedMedium(_) => 7,
|
||||||
|
Body::BirdMedium(_) => 4,
|
||||||
|
Body::FishMedium(_) => 3,
|
||||||
|
Body::Dragon(_) => 26,
|
||||||
|
Body::BirdSmall(_) => 2,
|
||||||
|
Body::FishSmall(_) => 2,
|
||||||
|
Body::BipedLarge(_) => 14,
|
||||||
|
Body::Object(_) => 0,
|
||||||
|
Body::Golem(_) => 17,
|
||||||
|
Body::Critter(_) => 3,
|
||||||
|
Body::QuadrupedLow(_) => 6,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_exp(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Body::Humanoid(_) => 15,
|
||||||
|
Body::QuadrupedSmall(_) => 12,
|
||||||
|
Body::QuadrupedMedium(_) => 28,
|
||||||
|
Body::BirdMedium(_) => 10,
|
||||||
|
Body::FishMedium(_) => 8,
|
||||||
|
Body::Dragon(_) => 160,
|
||||||
|
Body::BirdSmall(_) => 5,
|
||||||
|
Body::FishSmall(_) => 4,
|
||||||
|
Body::BipedLarge(_) => 75,
|
||||||
|
Body::Object(_) => 0,
|
||||||
|
Body::Golem(_) => 75,
|
||||||
|
Body::Critter(_) => 8,
|
||||||
|
Body::QuadrupedLow(_) => 24,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_exp_increase(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Body::Humanoid(_) => 3,
|
||||||
|
Body::QuadrupedSmall(_) => 2,
|
||||||
|
Body::QuadrupedMedium(_) => 6,
|
||||||
|
Body::BirdMedium(_) => 2,
|
||||||
|
Body::FishMedium(_) => 2,
|
||||||
|
Body::Dragon(_) => 32,
|
||||||
|
Body::BirdSmall(_) => 1,
|
||||||
|
Body::FishSmall(_) => 1,
|
||||||
|
Body::BipedLarge(_) => 15,
|
||||||
|
Body::Object(_) => 0,
|
||||||
|
Body::Golem(_) => 15,
|
||||||
|
Body::Critter(_) => 2,
|
||||||
|
Body::QuadrupedLow(_) => 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stats {
|
impl Stats {
|
||||||
@ -143,7 +218,10 @@ impl Stats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Delete this once stat points will be a thing
|
// TODO: Delete this once stat points will be a thing
|
||||||
pub fn update_max_hp(&mut self) { self.health.set_maximum(52 + 3 * self.level.amount); }
|
pub fn update_max_hp(&mut self, body: Body) {
|
||||||
|
self.health
|
||||||
|
.set_maximum(body.base_health() + body.base_health_increase() * self.level.amount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stats {
|
impl Stats {
|
||||||
@ -187,9 +265,10 @@ impl Stats {
|
|||||||
fitness,
|
fitness,
|
||||||
willpower,
|
willpower,
|
||||||
is_dead: false,
|
is_dead: false,
|
||||||
|
body_type: body,
|
||||||
};
|
};
|
||||||
|
|
||||||
stats.update_max_hp();
|
stats.update_max_hp(body);
|
||||||
stats
|
stats
|
||||||
.health
|
.health
|
||||||
.set_to(stats.health.maximum(), HealthSource::Revive);
|
.set_to(stats.health.maximum(), HealthSource::Revive);
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
assets,
|
assets,
|
||||||
comp::{
|
comp::{
|
||||||
item::{Item, ItemKind},
|
item::{Item, ItemKind},
|
||||||
CharacterAbility, ItemConfig, Loadout,
|
Body, CharacterAbility, ItemConfig, Loadout,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -24,6 +24,44 @@ use std::time::Duration;
|
|||||||
/// ```
|
/// ```
|
||||||
pub struct LoadoutBuilder(Loadout);
|
pub struct LoadoutBuilder(Loadout);
|
||||||
|
|
||||||
|
impl Body {
|
||||||
|
pub fn base_dmg(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Body::Humanoid(_) => 8,
|
||||||
|
Body::QuadrupedSmall(_) => 7,
|
||||||
|
Body::QuadrupedMedium(_) => 10,
|
||||||
|
Body::BirdMedium(_) => 6,
|
||||||
|
Body::FishMedium(_) => 5,
|
||||||
|
Body::Dragon(_) => 75,
|
||||||
|
Body::BirdSmall(_) => 4,
|
||||||
|
Body::FishSmall(_) => 3,
|
||||||
|
Body::BipedLarge(_) => 30,
|
||||||
|
Body::Object(_) => 0,
|
||||||
|
Body::Golem(_) => 30,
|
||||||
|
Body::Critter(_) => 6,
|
||||||
|
Body::QuadrupedLow(_) => 9,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn base_range(&self) -> f32 {
|
||||||
|
match self {
|
||||||
|
Body::Humanoid(_) => 5.0,
|
||||||
|
Body::QuadrupedSmall(_) => 4.5,
|
||||||
|
Body::QuadrupedMedium(_) => 5.5,
|
||||||
|
Body::BirdMedium(_) => 3.5,
|
||||||
|
Body::FishMedium(_) => 3.5,
|
||||||
|
Body::Dragon(_) => 12.5,
|
||||||
|
Body::BirdSmall(_) => 3.0,
|
||||||
|
Body::FishSmall(_) => 3.0,
|
||||||
|
Body::BipedLarge(_) => 10.0,
|
||||||
|
Body::Object(_) => 3.0,
|
||||||
|
Body::Golem(_) => 7.5,
|
||||||
|
Body::Critter(_) => 3.0,
|
||||||
|
Body::QuadrupedLow(_) => 4.5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LoadoutBuilder {
|
impl LoadoutBuilder {
|
||||||
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
|
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@ -63,7 +101,7 @@ impl LoadoutBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Default animal configuration
|
/// Default animal configuration
|
||||||
pub fn animal() -> Self {
|
pub fn animal(body: Body) -> Self {
|
||||||
Self(Loadout {
|
Self(Loadout {
|
||||||
active_item: Some(ItemConfig {
|
active_item: Some(ItemConfig {
|
||||||
item: assets::load_expect_cloned("common.items.weapons.empty"),
|
item: assets::load_expect_cloned("common.items.weapons.empty"),
|
||||||
@ -71,8 +109,8 @@ impl LoadoutBuilder {
|
|||||||
energy_cost: 10,
|
energy_cost: 10,
|
||||||
buildup_duration: Duration::from_millis(600),
|
buildup_duration: Duration::from_millis(600),
|
||||||
recover_duration: Duration::from_millis(100),
|
recover_duration: Duration::from_millis(100),
|
||||||
base_healthchange: -6,
|
base_healthchange: -(body.base_dmg() as i32),
|
||||||
range: 5.0,
|
range: body.base_range(),
|
||||||
max_angle: 80.0,
|
max_angle: 80.0,
|
||||||
}),
|
}),
|
||||||
ability2: None,
|
ability2: None,
|
||||||
|
@ -12,6 +12,7 @@ pub mod idle;
|
|||||||
pub mod leap_melee;
|
pub mod leap_melee;
|
||||||
pub mod roll;
|
pub mod roll;
|
||||||
pub mod sit;
|
pub mod sit;
|
||||||
|
pub mod spin_melee;
|
||||||
pub mod triple_strike;
|
pub mod triple_strike;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod wielding;
|
pub mod wielding;
|
||||||
|
146
common/src/states/spin_melee.rs
Normal file
146
common/src/states/spin_melee.rs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
use crate::{
|
||||||
|
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
|
||||||
|
sys::character_behavior::*,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::time::Duration;
|
||||||
|
use vek::Vec3;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
||||||
|
pub struct Data {
|
||||||
|
/// How long until the state attacks
|
||||||
|
pub buildup_duration: Duration,
|
||||||
|
/// Allows for buildup_duration to be reset to default value
|
||||||
|
pub buildup_duration_default: Duration,
|
||||||
|
/// How long until state ends
|
||||||
|
pub recover_duration: Duration,
|
||||||
|
/// Allows for recover_duration to be reset to default value
|
||||||
|
pub recover_duration_default: Duration,
|
||||||
|
/// Base damage
|
||||||
|
pub base_damage: u32,
|
||||||
|
/// Whether the attack can deal more damage
|
||||||
|
pub exhausted: bool,
|
||||||
|
/// How many hits it can do before ending
|
||||||
|
pub hits_remaining: u32,
|
||||||
|
/// Allows for hits_remaining to be reset to default value
|
||||||
|
pub hits_remaining_default: u32,
|
||||||
|
/// Energy cost per attack
|
||||||
|
pub energy_cost: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const MOVE_SPEED: f32 = 5.0;
|
||||||
|
|
||||||
|
impl CharacterBehavior for Data {
|
||||||
|
fn behavior(&self, data: &JoinData) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
|
||||||
|
if self.buildup_duration != Duration::default() {
|
||||||
|
// Allows for moving
|
||||||
|
update.vel.0 =
|
||||||
|
Vec3::new(data.inputs.move_dir.x, data.inputs.move_dir.y, 0.0) * MOVE_SPEED;
|
||||||
|
|
||||||
|
update.character = CharacterState::SpinMelee(Data {
|
||||||
|
buildup_duration: self
|
||||||
|
.buildup_duration
|
||||||
|
.checked_sub(Duration::from_secs_f32(data.dt.0))
|
||||||
|
.unwrap_or_default(),
|
||||||
|
buildup_duration_default: self.buildup_duration_default,
|
||||||
|
recover_duration: self.recover_duration,
|
||||||
|
recover_duration_default: self.recover_duration_default,
|
||||||
|
base_damage: self.base_damage,
|
||||||
|
exhausted: self.exhausted,
|
||||||
|
hits_remaining: self.hits_remaining,
|
||||||
|
hits_remaining_default: self.hits_remaining_default,
|
||||||
|
energy_cost: self.energy_cost,
|
||||||
|
});
|
||||||
|
} else if !self.exhausted {
|
||||||
|
//Hit attempt
|
||||||
|
data.updater.insert(data.entity, Attacking {
|
||||||
|
base_healthchange: -(self.base_damage as i32),
|
||||||
|
range: 3.5,
|
||||||
|
max_angle: 360_f32.to_radians(),
|
||||||
|
applied: false,
|
||||||
|
hit_count: 0,
|
||||||
|
knockback: 0.0,
|
||||||
|
});
|
||||||
|
|
||||||
|
update.character = CharacterState::SpinMelee(Data {
|
||||||
|
buildup_duration: self.buildup_duration,
|
||||||
|
buildup_duration_default: self.buildup_duration_default,
|
||||||
|
recover_duration: self.recover_duration,
|
||||||
|
recover_duration_default: self.recover_duration_default,
|
||||||
|
base_damage: self.base_damage,
|
||||||
|
exhausted: true,
|
||||||
|
hits_remaining: self.hits_remaining - 1,
|
||||||
|
hits_remaining_default: self.hits_remaining_default,
|
||||||
|
energy_cost: self.energy_cost,
|
||||||
|
});
|
||||||
|
} else if self.recover_duration != Duration::default() {
|
||||||
|
// Allows for moving
|
||||||
|
update.vel.0 =
|
||||||
|
Vec3::new(data.inputs.move_dir.x, data.inputs.move_dir.y, 0.0) * MOVE_SPEED;
|
||||||
|
|
||||||
|
update.character = CharacterState::SpinMelee(Data {
|
||||||
|
buildup_duration: self.buildup_duration,
|
||||||
|
buildup_duration_default: self.buildup_duration_default,
|
||||||
|
recover_duration: self
|
||||||
|
.recover_duration
|
||||||
|
.checked_sub(Duration::from_secs_f32(data.dt.0))
|
||||||
|
.unwrap_or_default(),
|
||||||
|
recover_duration_default: self.recover_duration_default,
|
||||||
|
base_damage: self.base_damage,
|
||||||
|
exhausted: self.exhausted,
|
||||||
|
hits_remaining: self.hits_remaining,
|
||||||
|
hits_remaining_default: self.hits_remaining_default,
|
||||||
|
energy_cost: self.energy_cost,
|
||||||
|
});
|
||||||
|
} else if self.hits_remaining != 0 {
|
||||||
|
// Allows for one ability usage to have multiple hits
|
||||||
|
// This isn't needed for it's continuous implementation, but is left in should
|
||||||
|
// this skill be moved to the skillbar
|
||||||
|
update.character = CharacterState::SpinMelee(Data {
|
||||||
|
buildup_duration: self.buildup_duration_default,
|
||||||
|
buildup_duration_default: self.buildup_duration_default,
|
||||||
|
recover_duration: self.recover_duration_default,
|
||||||
|
recover_duration_default: self.recover_duration_default,
|
||||||
|
base_damage: self.base_damage,
|
||||||
|
exhausted: false,
|
||||||
|
hits_remaining: self.hits_remaining,
|
||||||
|
hits_remaining_default: self.hits_remaining_default,
|
||||||
|
energy_cost: self.energy_cost,
|
||||||
|
});
|
||||||
|
} else if update.energy.current() >= self.energy_cost && data.inputs.secondary.is_pressed()
|
||||||
|
{
|
||||||
|
update.character = CharacterState::SpinMelee(Data {
|
||||||
|
buildup_duration: self.buildup_duration_default,
|
||||||
|
buildup_duration_default: self.buildup_duration_default,
|
||||||
|
recover_duration: self.recover_duration_default,
|
||||||
|
recover_duration_default: self.recover_duration_default,
|
||||||
|
base_damage: self.base_damage,
|
||||||
|
exhausted: false,
|
||||||
|
hits_remaining: self.hits_remaining_default,
|
||||||
|
hits_remaining_default: self.hits_remaining_default,
|
||||||
|
energy_cost: self.energy_cost,
|
||||||
|
});
|
||||||
|
// Consumes energy if there's enough left and RMB is held down
|
||||||
|
update
|
||||||
|
.energy
|
||||||
|
.change_by(-(self.energy_cost as i32), EnergySource::Ability);
|
||||||
|
} else {
|
||||||
|
// Done
|
||||||
|
update.character = CharacterState::Wielding;
|
||||||
|
// Make sure attack component is removed
|
||||||
|
data.updater.remove::<Attacking>(data.entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grant energy on successful hit
|
||||||
|
if let Some(attack) = data.attacking {
|
||||||
|
if attack.applied && attack.hit_count > 0 {
|
||||||
|
data.updater.remove::<Attacking>(data.entity);
|
||||||
|
update.energy.change_by(10, EnergySource::HitEnemy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update
|
||||||
|
}
|
||||||
|
}
|
@ -244,6 +244,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
CharacterState::Boost(data) => data.handle_event(&j, action),
|
CharacterState::Boost(data) => data.handle_event(&j, action),
|
||||||
CharacterState::DashMelee(data) => data.handle_event(&j, action),
|
CharacterState::DashMelee(data) => data.handle_event(&j, action),
|
||||||
CharacterState::LeapMelee(data) => data.handle_event(&j, action),
|
CharacterState::LeapMelee(data) => data.handle_event(&j, action),
|
||||||
|
CharacterState::SpinMelee(data) => data.handle_event(&j, action),
|
||||||
};
|
};
|
||||||
local_emitter.append(&mut state_update.local_events);
|
local_emitter.append(&mut state_update.local_events);
|
||||||
server_emitter.append(&mut state_update.server_events);
|
server_emitter.append(&mut state_update.server_events);
|
||||||
@ -269,6 +270,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
CharacterState::Boost(data) => data.behavior(&j),
|
CharacterState::Boost(data) => data.behavior(&j),
|
||||||
CharacterState::DashMelee(data) => data.behavior(&j),
|
CharacterState::DashMelee(data) => data.behavior(&j),
|
||||||
CharacterState::LeapMelee(data) => data.behavior(&j),
|
CharacterState::LeapMelee(data) => data.behavior(&j),
|
||||||
|
CharacterState::SpinMelee(data) => data.behavior(&j),
|
||||||
};
|
};
|
||||||
|
|
||||||
local_emitter.append(&mut state_update.local_events);
|
local_emitter.append(&mut state_update.local_events);
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{CharacterState, ControlEvent, Controller},
|
comp::{
|
||||||
|
slot::{EquipSlot, Slot},
|
||||||
|
CharacterState, ControlEvent, Controller, InventoryManip,
|
||||||
|
},
|
||||||
event::{EventBus, LocalEvent, ServerEvent},
|
event::{EventBus, LocalEvent, ServerEvent},
|
||||||
state::DeltaTime,
|
state::DeltaTime,
|
||||||
sync::{Uid, UidAllocator},
|
sync::{Uid, UidAllocator},
|
||||||
@ -79,7 +82,18 @@ impl<'a> System<'a> for Sys {
|
|||||||
server_emitter.emit(ServerEvent::ToggleLantern(entity))
|
server_emitter.emit(ServerEvent::ToggleLantern(entity))
|
||||||
},
|
},
|
||||||
ControlEvent::InventoryManip(manip) => {
|
ControlEvent::InventoryManip(manip) => {
|
||||||
*character_state = CharacterState::Idle;
|
// Unwield if a wielded equipment slot is being modified, to avoid entering
|
||||||
|
// a barehanded wielding state.
|
||||||
|
if character_state.is_wield() {
|
||||||
|
match manip {
|
||||||
|
InventoryManip::Drop(Slot::Equip(EquipSlot::Mainhand))
|
||||||
|
| InventoryManip::Swap(_, Slot::Equip(EquipSlot::Mainhand))
|
||||||
|
| InventoryManip::Swap(Slot::Equip(EquipSlot::Mainhand), _) => {
|
||||||
|
*character_state = CharacterState::Idle;
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
server_emitter.emit(ServerEvent::InventoryManip(entity, manip))
|
server_emitter.emit(ServerEvent::InventoryManip(entity, manip))
|
||||||
},
|
},
|
||||||
ControlEvent::Respawn => server_emitter.emit(ServerEvent::Respawn(entity)),
|
ControlEvent::Respawn => server_emitter.emit(ServerEvent::Respawn(entity)),
|
||||||
|
@ -22,7 +22,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&mut self,
|
&mut self,
|
||||||
(entities, dt, server_event_bus, character_states, mut stats,mut energies): Self::SystemData,
|
(entities, dt, server_event_bus, character_states, mut stats, mut energies): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let mut server_event_emitter = server_event_bus.emitter();
|
let mut server_event_emitter = server_event_bus.emitter();
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
server_event_emitter.emit(ServerEvent::LevelUp(entity, stat.level.level()));
|
server_event_emitter.emit(ServerEvent::LevelUp(entity, stat.level.level()));
|
||||||
}
|
}
|
||||||
|
|
||||||
stat.update_max_hp();
|
stat.update_max_hp(stat.body_type);
|
||||||
stat.health
|
stat.health
|
||||||
.set_to(stat.health.maximum(), HealthSource::LevelUp);
|
.set_to(stat.health.maximum(), HealthSource::LevelUp);
|
||||||
}
|
}
|
||||||
@ -103,6 +103,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
CharacterState::BasicMelee { .. }
|
CharacterState::BasicMelee { .. }
|
||||||
| CharacterState::DashMelee { .. }
|
| CharacterState::DashMelee { .. }
|
||||||
| CharacterState::LeapMelee { .. }
|
| CharacterState::LeapMelee { .. }
|
||||||
|
| CharacterState::SpinMelee { .. }
|
||||||
| CharacterState::TripleStrike { .. }
|
| CharacterState::TripleStrike { .. }
|
||||||
| CharacterState::BasicRanged { .. } => {
|
| CharacterState::BasicRanged { .. } => {
|
||||||
if energy.get_unchecked().regen_rate != 0.0 {
|
if energy.get_unchecked().regen_rate != 0.0 {
|
||||||
|
@ -13,10 +13,10 @@ common = { package = "veloren-common", path = "../common" }
|
|||||||
world = { package = "veloren-world", path = "../world" }
|
world = { package = "veloren-world", path = "../world" }
|
||||||
network = { package = "veloren_network", path = "../network", default-features = false }
|
network = { package = "veloren_network", path = "../network", default-features = false }
|
||||||
|
|
||||||
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git" }
|
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", branch = "specs-git" }
|
||||||
|
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
specs = { version = "0.16.1", features = ["shred-derive"] }
|
specs = { git = "https://github.com/amethyst/specs.git", features = ["shred-derive"], rev = "7a2e348ab2223818bad487695c66c43db88050a5" }
|
||||||
vek = { version = "0.11.0", features = ["serde"] }
|
vek = { version = "0.11.0", features = ["serde"] }
|
||||||
uvth = "3.1.1"
|
uvth = "3.1.1"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
|
@ -544,7 +544,7 @@ fn handle_spawn(
|
|||||||
.create_npc(
|
.create_npc(
|
||||||
pos,
|
pos,
|
||||||
comp::Stats::new(get_npc_name(id).into(), body),
|
comp::Stats::new(get_npc_name(id).into(), body),
|
||||||
LoadoutBuilder::animal().build(),
|
LoadoutBuilder::animal(body).build(),
|
||||||
body,
|
body,
|
||||||
)
|
)
|
||||||
.with(comp::Vel(vel))
|
.with(comp::Vel(vel))
|
||||||
@ -1568,7 +1568,7 @@ fn handle_set_level(
|
|||||||
{
|
{
|
||||||
stats.level.set_level(lvl);
|
stats.level.set_level(lvl);
|
||||||
|
|
||||||
stats.update_max_hp();
|
stats.update_max_hp(stats.body_type);
|
||||||
stats
|
stats
|
||||||
.health
|
.health
|
||||||
.set_to(stats.health.maximum(), comp::HealthSource::LevelUp);
|
.set_to(stats.health.maximum(), comp::HealthSource::LevelUp);
|
||||||
|
@ -63,9 +63,12 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
if let Some(attacker_stats) = stats.get_mut(attacker) {
|
if let Some(attacker_stats) = stats.get_mut(attacker) {
|
||||||
// TODO: Discuss whether we should give EXP by Player
|
// TODO: Discuss whether we should give EXP by Player
|
||||||
// Killing or not.
|
// Killing or not.
|
||||||
attacker_stats
|
attacker_stats.exp.change_by(
|
||||||
.exp
|
(entity_stats.body_type.base_exp()
|
||||||
.change_by((entity_stats.level.level() * 10) as i64);
|
+ entity_stats.level.level()
|
||||||
|
* entity_stats.body_type.base_exp_increase())
|
||||||
|
as i64,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
-- Nothing to undo since up.sql only creates missing inventory/loadout records
|
@ -0,0 +1,15 @@
|
|||||||
|
-- This migration was added in conjunction with a change that no longer creates missing inventory/loadout records
|
||||||
|
-- for characters that don't have one, to prevent such characters from being unable to log in.
|
||||||
|
|
||||||
|
-- Create a default loadout for any characters that don't have one
|
||||||
|
INSERT INTO loadout (character_id, items)
|
||||||
|
SELECT c."id", "{""active_item"":{""item"":{""name"":""Battered Sword"",""description"":""Two-Hand Sword\n\nPower: 2-10\n\nHeld together by Rust and hope.\n\n<Right-Click to use>"",""kind"":{""Tool"":{""kind"":{""Sword"":""BasicSword""},""equip_time_millis"":300}}},""ability1"":{""TripleStrike"":{""base_damage"":5,""needs_timing"":false}},""ability2"":{""DashMelee"":{""energy_cost"":700,""buildup_duration"":{""secs"":0,""nanos"":500000000},""recover_duration"":{""secs"":0,""nanos"":500000000},""base_damage"":10}},""ability3"":null,""block_ability"":""BasicBlock"",""dodge_ability"":""Roll""},""second_item"":null,""shoulder"":null,""chest"":{""name"":""Rugged Shirt"",""description"":""Chest\n\nArmor: 0\n\nSmells like Adventure.\n\n<Right-Click to use>"",""kind"":{""Armor"":{""kind"":{""Chest"":""Rugged0""},""stats"":20}}},""belt"":null,""hand"":null,""pants"":{""name"":""Rugged Commoner's Pants"",""description"":""Legs\n\nArmor: 0\n\nThey remind you of the old days.\n\n<Right-Click to use>"",""kind"":{""Armor"":{""kind"":{""Pants"":""Rugged0""},""stats"":20}}},""foot"":{""name"":""Worn out Sandals"",""description"":""Feet\n\nArmor: 0\n\nLoyal companions.\n\n<Right-Click to use>"",""kind"":{""Armor"":{""kind"":{""Foot"":""Sandal0""},""stats"":20}}},""back"":null,""ring"":null,""neck"":null,""lantern"":{""name"":""Black Lantern"",""description"":""Used by city guards."",""kind"":{""Lantern"":{""kind"":""Black0"",""color"":{""r"":255,""g"":190,""b"":75},""strength_thousandths"":3000,""flicker_thousandths"":300}}},""head"":null,""tabard"":null}"
|
||||||
|
FROM character c
|
||||||
|
WHERE (SELECT COUNT(1) FROM loadout WHERE character_id = c.id) = 0;
|
||||||
|
|
||||||
|
|
||||||
|
-- Create a default inventory for any characters that don't have one
|
||||||
|
INSERT INTO inventory (character_id, items)
|
||||||
|
SELECT c."id", "{""slots"":[{""name"":""Dwarven Cheese"",""description"":""Restores 15 Health\n\nAromatic and nutritious\n\n<Right-Click to use>"",""kind"":{""Consumable"":{""kind"":""Cheese"",""effect"":{""Health"":{""amount"":15,""cause"":""Item""}},""amount"":1}}},{""name"":""Apple"",""description"":""Restores 20 Health\n\nRed and juicy\n\n<Right-Click to use>"",""kind"":{""Consumable"":{""kind"":""Apple"",""effect"":{""Health"":{""amount"":20,""cause"":""Item""}},""amount"":1}}},null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null],""amount"":2}"
|
||||||
|
FROM character c
|
||||||
|
WHERE (SELECT COUNT(1) FROM inventory WHERE character_id = c.id) = 0;
|
@ -0,0 +1 @@
|
|||||||
|
-- Nothing to undo since up.sql only fixes corrupt JSON in loadouts
|
@ -0,0 +1,7 @@
|
|||||||
|
-- This is a migration to fix loadouts that contain old versions of items with the DashMelee skill before the
|
||||||
|
-- energy_cost field was added to it in https://gitlab.com/veloren/veloren/-/merge_requests/1140
|
||||||
|
-- This missing field in the JSON prevents accounts with affected characters from being able log in due to JSON
|
||||||
|
-- deserialization failure.
|
||||||
|
UPDATE loadout
|
||||||
|
SET items = REPLACE(items, '{"DashMelee":{"buildup_duration"','{"DashMelee":{"energy_cost":700,"buildup_duration"')
|
||||||
|
WHERE items LIKE '%DashMelee%';
|
@ -236,71 +236,35 @@ impl Drop for CharacterLoader {
|
|||||||
fn load_character_data(player_uuid: &str, character_id: i32, db_dir: &str) -> CharacterDataResult {
|
fn load_character_data(player_uuid: &str, character_id: i32, db_dir: &str) -> CharacterDataResult {
|
||||||
let connection = establish_connection(db_dir);
|
let connection = establish_connection(db_dir);
|
||||||
|
|
||||||
let (character_data, body_data, stats_data, maybe_inventory, maybe_loadout) =
|
let result = schema::character::dsl::character
|
||||||
schema::character::dsl::character
|
.filter(schema::character::id.eq(character_id))
|
||||||
.filter(schema::character::id.eq(character_id))
|
.filter(schema::character::player_uuid.eq(player_uuid))
|
||||||
.filter(schema::character::player_uuid.eq(player_uuid))
|
.inner_join(schema::body::table)
|
||||||
.inner_join(schema::body::table)
|
.inner_join(schema::stats::table)
|
||||||
.inner_join(schema::stats::table)
|
.inner_join(schema::inventory::table)
|
||||||
.left_join(schema::inventory::table)
|
.inner_join(schema::loadout::table)
|
||||||
.left_join(schema::loadout::table)
|
.first::<(Character, Body, Stats, Inventory, Loadout)>(&connection);
|
||||||
.first::<(Character, Body, Stats, Option<Inventory>, Option<Loadout>)>(&connection)?;
|
|
||||||
|
|
||||||
Ok((
|
match result {
|
||||||
comp::Body::from(&body_data),
|
Ok((character_data, body_data, stats_data, inventory, loadout)) => Ok((
|
||||||
comp::Stats::from(StatsJoinData {
|
comp::Body::from(&body_data),
|
||||||
alias: &character_data.alias,
|
comp::Stats::from(StatsJoinData {
|
||||||
body: &comp::Body::from(&body_data),
|
alias: &character_data.alias,
|
||||||
stats: &stats_data,
|
body: &comp::Body::from(&body_data),
|
||||||
}),
|
stats: &stats_data,
|
||||||
maybe_inventory.map_or_else(
|
}),
|
||||||
|| {
|
comp::Inventory::from(inventory),
|
||||||
// If no inventory record was found for the character, create it now
|
comp::Loadout::from(&loadout),
|
||||||
let row = Inventory::from((character_data.id, comp::Inventory::default()));
|
)),
|
||||||
|
Err(e) => {
|
||||||
if let Err(error) = diesel::insert_into(schema::inventory::table)
|
error!(
|
||||||
.values(&row)
|
?e,
|
||||||
.execute(&connection)
|
?character_id,
|
||||||
{
|
"Failed to load character data for character"
|
||||||
warn!(
|
);
|
||||||
"Failed to create an inventory record for character {}: {}",
|
Err(Error::CharacterDataError)
|
||||||
&character_data.id, error
|
},
|
||||||
)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
comp::Inventory::default()
|
|
||||||
},
|
|
||||||
comp::Inventory::from,
|
|
||||||
),
|
|
||||||
maybe_loadout.map_or_else(
|
|
||||||
|| {
|
|
||||||
// Create if no record was found
|
|
||||||
let default_loadout = LoadoutBuilder::new()
|
|
||||||
.defaults()
|
|
||||||
.active_item(LoadoutBuilder::default_item_config_from_str(
|
|
||||||
character_data.tool.as_deref(),
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let row = NewLoadout::from((character_data.id, &default_loadout));
|
|
||||||
|
|
||||||
if let Err(e) = diesel::insert_into(schema::loadout::table)
|
|
||||||
.values(&row)
|
|
||||||
.execute(&connection)
|
|
||||||
{
|
|
||||||
let char_id = character_data.id;
|
|
||||||
warn!(
|
|
||||||
?e,
|
|
||||||
?char_id,
|
|
||||||
"Failed to create an loadout record for character",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
default_loadout
|
|
||||||
},
|
|
||||||
|data| comp::Loadout::from(&data),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a list of characters belonging to the player. This data is a small
|
/// Loads a list of characters belonging to the player. This data is a small
|
||||||
@ -311,40 +275,36 @@ fn load_character_data(player_uuid: &str, character_id: i32, db_dir: &str) -> Ch
|
|||||||
/// stats, body, etc...) the character is skipped, and no entry will be
|
/// stats, body, etc...) the character is skipped, and no entry will be
|
||||||
/// returned.
|
/// returned.
|
||||||
fn load_character_list(player_uuid: &str, db_dir: &str) -> CharacterListResult {
|
fn load_character_list(player_uuid: &str, db_dir: &str) -> CharacterListResult {
|
||||||
let data = schema::character::dsl::character
|
let result = schema::character::dsl::character
|
||||||
.filter(schema::character::player_uuid.eq(player_uuid))
|
.filter(schema::character::player_uuid.eq(player_uuid))
|
||||||
.order(schema::character::id.desc())
|
.order(schema::character::id.desc())
|
||||||
.inner_join(schema::body::table)
|
.inner_join(schema::body::table)
|
||||||
.inner_join(schema::stats::table)
|
.inner_join(schema::stats::table)
|
||||||
.left_join(schema::loadout::table)
|
.inner_join(schema::loadout::table)
|
||||||
.load::<(Character, Body, Stats, Option<Loadout>)>(&establish_connection(db_dir))?;
|
.load::<(Character, Body, Stats, Loadout)>(&establish_connection(db_dir));
|
||||||
|
|
||||||
Ok(data
|
match result {
|
||||||
.iter()
|
Ok(data) => Ok(data
|
||||||
.map(|(character_data, body_data, stats_data, maybe_loadout)| {
|
.iter()
|
||||||
let character = CharacterData::from(character_data);
|
.map(|(character_data, body_data, stats_data, loadout)| {
|
||||||
let body = comp::Body::from(body_data);
|
let character = CharacterData::from(character_data);
|
||||||
let level = stats_data.level as usize;
|
let body = comp::Body::from(body_data);
|
||||||
let loadout = maybe_loadout.as_ref().map_or_else(
|
let level = stats_data.level as usize;
|
||||||
|| {
|
let loadout = comp::Loadout::from(loadout);
|
||||||
LoadoutBuilder::new()
|
|
||||||
.defaults()
|
|
||||||
.active_item(LoadoutBuilder::default_item_config_from_str(
|
|
||||||
character.tool.as_deref(),
|
|
||||||
))
|
|
||||||
.build()
|
|
||||||
},
|
|
||||||
comp::Loadout::from,
|
|
||||||
);
|
|
||||||
|
|
||||||
CharacterItem {
|
CharacterItem {
|
||||||
character,
|
character,
|
||||||
body,
|
body,
|
||||||
level,
|
level,
|
||||||
loadout,
|
loadout,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect())
|
.collect()),
|
||||||
|
Err(e) => {
|
||||||
|
error!(?e, ?player_uuid, "Failed to load character list for player");
|
||||||
|
Err(Error::CharacterDataError)
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new character with provided comp::Character and comp::Body data.
|
/// Create a new character with provided comp::Character and comp::Body data.
|
||||||
|
@ -2,7 +2,7 @@ extern crate serde_json;
|
|||||||
|
|
||||||
use super::schema::{body, character, inventory, loadout, stats};
|
use super::schema::{body, character, inventory, loadout, stats};
|
||||||
use crate::comp;
|
use crate::comp;
|
||||||
use common::{character::Character as CharacterData, LoadoutBuilder};
|
use common::character::Character as CharacterData;
|
||||||
use diesel::sql_types::Text;
|
use diesel::sql_types::Text;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
@ -106,7 +106,7 @@ impl From<StatsJoinData<'_>> for comp::Stats {
|
|||||||
|
|
||||||
base_stats.exp.set_current(data.stats.exp as u32);
|
base_stats.exp.set_current(data.stats.exp as u32);
|
||||||
|
|
||||||
base_stats.update_max_hp();
|
base_stats.update_max_hp(base_stats.body_type);
|
||||||
base_stats
|
base_stats
|
||||||
.health
|
.health
|
||||||
.set_to(base_stats.health.maximum(), comp::HealthSource::Revive);
|
.set_to(base_stats.health.maximum(), comp::HealthSource::Revive);
|
||||||
@ -212,14 +212,7 @@ where
|
|||||||
bytes: Option<&<DB as diesel::backend::Backend>::RawValue>,
|
bytes: Option<&<DB as diesel::backend::Backend>::RawValue>,
|
||||||
) -> diesel::deserialize::Result<Self> {
|
) -> diesel::deserialize::Result<Self> {
|
||||||
let t = String::from_sql(bytes)?;
|
let t = String::from_sql(bytes)?;
|
||||||
|
serde_json::from_str(&t).map_err(Box::from)
|
||||||
match serde_json::from_str(&t) {
|
|
||||||
Ok(data) => Ok(Self(data)),
|
|
||||||
Err(e) => {
|
|
||||||
warn!(?e, "Failed to deserialise inventory data");
|
|
||||||
Ok(Self(comp::Inventory::default()))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,23 +291,7 @@ where
|
|||||||
bytes: Option<&<DB as diesel::backend::Backend>::RawValue>,
|
bytes: Option<&<DB as diesel::backend::Backend>::RawValue>,
|
||||||
) -> diesel::deserialize::Result<Self> {
|
) -> diesel::deserialize::Result<Self> {
|
||||||
let t = String::from_sql(bytes)?;
|
let t = String::from_sql(bytes)?;
|
||||||
|
serde_json::from_str(&t).map_err(Box::from)
|
||||||
match serde_json::from_str(&t) {
|
|
||||||
Ok(data) => Ok(Self(data)),
|
|
||||||
Err(e) => {
|
|
||||||
warn!(?e, "Failed to deserialise loadout data");
|
|
||||||
|
|
||||||
// We don't have a weapon reference here, so we default to sword
|
|
||||||
let loadout = LoadoutBuilder::new()
|
|
||||||
.defaults()
|
|
||||||
.active_item(LoadoutBuilder::default_item_config_from_str(Some(
|
|
||||||
"common.items.weapons.sword.starter_sword",
|
|
||||||
)))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Ok(Self(loadout))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +215,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
head: None,
|
head: None,
|
||||||
tabard: None,
|
tabard: None,
|
||||||
},
|
},
|
||||||
_ => LoadoutBuilder::animal().build(),
|
_ => LoadoutBuilder::animal(entity.body).build(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut scale = entity.scale;
|
let mut scale = entity.scale;
|
||||||
@ -296,7 +296,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
scale = 2.0 + rand::random::<f32>();
|
scale = 2.0 + rand::random::<f32>();
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.update_max_hp();
|
stats.update_max_hp(stats.body_type);
|
||||||
|
|
||||||
stats
|
stats
|
||||||
.health
|
.health
|
||||||
|
@ -34,8 +34,8 @@ conrod_winit = { git = "https://gitlab.com/veloren/conrod.git", branch = "pre-wi
|
|||||||
euc = { git = "https://github.com/zesterer/euc.git" }
|
euc = { git = "https://github.com/zesterer/euc.git" }
|
||||||
|
|
||||||
# ECS
|
# ECS
|
||||||
specs = "0.16.1"
|
specs = { git = "https://github.com/amethyst/specs.git", rev = "7a2e348ab2223818bad487695c66c43db88050a5" }
|
||||||
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git" }
|
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", branch = "specs-git" }
|
||||||
|
|
||||||
# Mathematics
|
# Mathematics
|
||||||
vek = { version = "0.11.0", features = [
|
vek = { version = "0.11.0", features = [
|
||||||
|
@ -17,6 +17,7 @@ pub mod run;
|
|||||||
pub mod shoot;
|
pub mod shoot;
|
||||||
pub mod sit;
|
pub mod sit;
|
||||||
pub mod spin;
|
pub mod spin;
|
||||||
|
pub mod spinmelee;
|
||||||
pub mod stand;
|
pub mod stand;
|
||||||
pub mod swim;
|
pub mod swim;
|
||||||
pub mod wield;
|
pub mod wield;
|
||||||
@ -28,8 +29,8 @@ pub use self::{
|
|||||||
dance::DanceAnimation, dash::DashAnimation, equip::EquipAnimation,
|
dance::DanceAnimation, dash::DashAnimation, equip::EquipAnimation,
|
||||||
glidewield::GlideWieldAnimation, gliding::GlidingAnimation, idle::IdleAnimation,
|
glidewield::GlideWieldAnimation, gliding::GlidingAnimation, idle::IdleAnimation,
|
||||||
jump::JumpAnimation, leapmelee::LeapAnimation, roll::RollAnimation, run::RunAnimation,
|
jump::JumpAnimation, leapmelee::LeapAnimation, roll::RollAnimation, run::RunAnimation,
|
||||||
shoot::ShootAnimation, sit::SitAnimation, spin::SpinAnimation, stand::StandAnimation,
|
shoot::ShootAnimation, sit::SitAnimation, spin::SpinAnimation, spinmelee::SpinMeleeAnimation,
|
||||||
swim::SwimAnimation, wield::WieldAnimation,
|
stand::StandAnimation, swim::SwimAnimation, wield::WieldAnimation,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Bone, FigureBoneData, Skeleton};
|
use super::{Bone, FigureBoneData, Skeleton};
|
||||||
|
170
voxygen/src/anim/src/character/spinmelee.rs
Normal file
170
voxygen/src/anim/src/character/spinmelee.rs
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
use super::{super::Animation, CharacterSkeleton, SkeletonAttr};
|
||||||
|
use common::comp::item::{Hands, ToolKind};
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
pub struct SpinMeleeAnimation;
|
||||||
|
|
||||||
|
impl Animation for SpinMeleeAnimation {
|
||||||
|
type Dependency = (Option<ToolKind>, Option<ToolKind>, Vec3<f32>, f64);
|
||||||
|
type Skeleton = CharacterSkeleton;
|
||||||
|
|
||||||
|
#[cfg(feature = "use-dyn-lib")]
|
||||||
|
const UPDATE_FN: &'static [u8] = b"character_spinmelee\0";
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "be-dyn-lib", export_name = "character_spinmelee")]
|
||||||
|
#[allow(clippy::approx_constant)] // TODO: Pending review in #587
|
||||||
|
fn update_skeleton_inner(
|
||||||
|
skeleton: &Self::Skeleton,
|
||||||
|
(active_tool_kind, second_tool_kind, velocity, _global_time): Self::Dependency,
|
||||||
|
anim_time: f64,
|
||||||
|
rate: &mut f32,
|
||||||
|
skeleton_attr: &SkeletonAttr,
|
||||||
|
) -> Self::Skeleton {
|
||||||
|
*rate = 1.0;
|
||||||
|
let lab = 1.0;
|
||||||
|
let speed = Vec2::<f32>::from(velocity).magnitude();
|
||||||
|
let mut next = (*skeleton).clone();
|
||||||
|
//torso movement
|
||||||
|
let xshift = if velocity.z.abs() < 0.1 {
|
||||||
|
((anim_time as f32 - 1.1) * lab as f32 * 3.0).sin()
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
let yshift = if velocity.z.abs() < 0.1 {
|
||||||
|
((anim_time as f32 - 1.1) * lab as f32 * 3.0 + PI / 2.0).sin()
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
let spin = if anim_time < 1.1 && velocity.z.abs() < 0.1 {
|
||||||
|
0.5 * ((anim_time as f32).powf(2.0))
|
||||||
|
} else {
|
||||||
|
lab as f32 * anim_time as f32 * 0.9
|
||||||
|
};
|
||||||
|
|
||||||
|
//feet
|
||||||
|
let slowersmooth = (anim_time as f32 * lab as f32 * 4.0).sin();
|
||||||
|
let quick = (anim_time as f32 * lab as f32 * 8.0).sin();
|
||||||
|
|
||||||
|
if let Some(ToolKind::Axe(_)) = active_tool_kind {
|
||||||
|
next.l_hand.offset = Vec3::new(-0.5, 0.0, 4.0);
|
||||||
|
next.l_hand.ori = Quaternion::rotation_x(PI / 2.0)
|
||||||
|
* Quaternion::rotation_z(0.0)
|
||||||
|
* Quaternion::rotation_y(PI);
|
||||||
|
next.l_hand.scale = Vec3::one() * 1.08;
|
||||||
|
next.r_hand.offset = Vec3::new(0.5, 0.0, -2.5);
|
||||||
|
next.r_hand.ori = Quaternion::rotation_x(PI / 2.0)
|
||||||
|
* Quaternion::rotation_z(0.0)
|
||||||
|
* Quaternion::rotation_y(0.0);
|
||||||
|
next.r_hand.scale = Vec3::one() * 1.06;
|
||||||
|
next.main.offset = Vec3::new(-0.0, -2.0, -1.0);
|
||||||
|
next.main.ori = Quaternion::rotation_x(0.0)
|
||||||
|
* Quaternion::rotation_y(0.0)
|
||||||
|
* Quaternion::rotation_z(0.0);
|
||||||
|
|
||||||
|
next.control.offset = Vec3::new(0.0, 16.0, 3.0);
|
||||||
|
next.control.ori = Quaternion::rotation_x(-1.4)
|
||||||
|
* Quaternion::rotation_y(0.0)
|
||||||
|
* Quaternion::rotation_z(1.4);
|
||||||
|
next.control.scale = Vec3::one();
|
||||||
|
|
||||||
|
next.head.offset = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1);
|
||||||
|
next.head.ori = Quaternion::rotation_z(0.0)
|
||||||
|
* Quaternion::rotation_x(-0.15)
|
||||||
|
* Quaternion::rotation_y(0.08);
|
||||||
|
next.chest.offset = Vec3::new(
|
||||||
|
0.0,
|
||||||
|
skeleton_attr.chest.0 - 3.0,
|
||||||
|
skeleton_attr.chest.1 - 2.0,
|
||||||
|
);
|
||||||
|
next.chest.ori = Quaternion::rotation_z(0.0)
|
||||||
|
* Quaternion::rotation_x(-0.1)
|
||||||
|
* Quaternion::rotation_y(0.3);
|
||||||
|
next.chest.scale = Vec3::one();
|
||||||
|
|
||||||
|
next.belt.offset = Vec3::new(0.0, 1.0, -1.0);
|
||||||
|
next.belt.ori = Quaternion::rotation_z(0.0)
|
||||||
|
* Quaternion::rotation_x(0.4)
|
||||||
|
* Quaternion::rotation_y(0.0);
|
||||||
|
next.belt.scale = Vec3::one() * 0.98;
|
||||||
|
next.shorts.offset = Vec3::new(0.0, 3.0, -2.5);
|
||||||
|
next.shorts.ori = Quaternion::rotation_z(0.0)
|
||||||
|
* Quaternion::rotation_x(0.7)
|
||||||
|
* Quaternion::rotation_y(0.0);
|
||||||
|
next.shorts.scale = Vec3::one();
|
||||||
|
next.torso.offset = Vec3::new(
|
||||||
|
-xshift * (anim_time as f32).min(0.6),
|
||||||
|
-yshift * (anim_time as f32).min(0.6),
|
||||||
|
0.0,
|
||||||
|
) * skeleton_attr.scaler;
|
||||||
|
next.torso.ori = Quaternion::rotation_z(spin * -16.0)
|
||||||
|
* Quaternion::rotation_x(0.0)
|
||||||
|
* Quaternion::rotation_y(0.0);
|
||||||
|
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;
|
||||||
|
}
|
||||||
|
if velocity.z.abs() > 0.1 {
|
||||||
|
next.l_foot.offset = Vec3::new(-skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0);
|
||||||
|
next.l_foot.ori = Quaternion::rotation_x(1.0) * Quaternion::rotation_z(0.0);
|
||||||
|
next.l_foot.scale = Vec3::one();
|
||||||
|
|
||||||
|
next.r_foot.offset = Vec3::new(skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0);
|
||||||
|
next.r_foot.ori = Quaternion::rotation_x(1.0);
|
||||||
|
next.r_foot.scale = Vec3::one();
|
||||||
|
} else if speed < 0.5 {
|
||||||
|
next.l_foot.offset = Vec3::new(
|
||||||
|
-skeleton_attr.foot.0,
|
||||||
|
2.0 + quick * -6.0,
|
||||||
|
skeleton_attr.foot.2,
|
||||||
|
);
|
||||||
|
next.l_foot.ori =
|
||||||
|
Quaternion::rotation_x(0.5 + slowersmooth * 0.2) * Quaternion::rotation_z(0.0);
|
||||||
|
next.l_foot.scale = Vec3::one();
|
||||||
|
|
||||||
|
next.r_foot.offset = Vec3::new(skeleton_attr.foot.0, 4.0, skeleton_attr.foot.2);
|
||||||
|
next.r_foot.ori =
|
||||||
|
Quaternion::rotation_x(0.5 - slowersmooth * 0.2) * Quaternion::rotation_y(-0.4);
|
||||||
|
next.r_foot.scale = Vec3::one();
|
||||||
|
} else {
|
||||||
|
next.l_foot.offset = Vec3::new(
|
||||||
|
-skeleton_attr.foot.0,
|
||||||
|
2.0 + quick * -6.0,
|
||||||
|
skeleton_attr.foot.2,
|
||||||
|
);
|
||||||
|
next.l_foot.ori =
|
||||||
|
Quaternion::rotation_x(0.5 + slowersmooth * 0.2) * Quaternion::rotation_z(0.0);
|
||||||
|
next.l_foot.scale = Vec3::one();
|
||||||
|
|
||||||
|
next.r_foot.offset = Vec3::new(
|
||||||
|
skeleton_attr.foot.0,
|
||||||
|
2.0 + quick * 6.0,
|
||||||
|
skeleton_attr.foot.2,
|
||||||
|
);
|
||||||
|
next.r_foot.ori =
|
||||||
|
Quaternion::rotation_x(0.5 - slowersmooth * 0.2) * Quaternion::rotation_z(0.0);
|
||||||
|
next.r_foot.scale = Vec3::one();
|
||||||
|
};
|
||||||
|
next.lantern.offset = Vec3::new(
|
||||||
|
skeleton_attr.lantern.0,
|
||||||
|
skeleton_attr.lantern.1,
|
||||||
|
skeleton_attr.lantern.2,
|
||||||
|
);
|
||||||
|
next.lantern.ori = Quaternion::rotation_z(0.0)
|
||||||
|
* Quaternion::rotation_x(0.7)
|
||||||
|
* Quaternion::rotation_y(-0.8);
|
||||||
|
next.glider.offset = Vec3::new(0.0, 0.0, 10.0);
|
||||||
|
next.glider.scale = Vec3::one() * 0.0;
|
||||||
|
next.l_control.scale = Vec3::one();
|
||||||
|
next.r_control.scale = Vec3::one();
|
||||||
|
|
||||||
|
next.second.scale = match (
|
||||||
|
active_tool_kind.map(|tk| tk.into_hands()),
|
||||||
|
second_tool_kind.map(|tk| tk.into_hands()),
|
||||||
|
) {
|
||||||
|
(Some(Hands::OneHand), Some(Hands::OneHand)) => Vec3::one(),
|
||||||
|
(_, _) => Vec3::zero(),
|
||||||
|
};
|
||||||
|
|
||||||
|
next
|
||||||
|
}
|
||||||
|
}
|
@ -131,7 +131,7 @@ impl Animation for WieldAnimation {
|
|||||||
let hand_scale = 1.12;
|
let hand_scale = 1.12;
|
||||||
|
|
||||||
next.control.offset = Vec3::new(0.0, 0.0, 0.0);
|
next.control.offset = Vec3::new(0.0, 0.0, 0.0);
|
||||||
// next.control.ori = Quaternion::rotation_x(u_slow * 0.15 + 1.0)
|
//next.control.ori = Quaternion::rotation_x(slow * 1.0);
|
||||||
// * Quaternion::rotation_y(0.0)
|
// * Quaternion::rotation_y(0.0)
|
||||||
// * Quaternion::rotation_z(u_slowalt * 0.08);
|
// * Quaternion::rotation_z(u_slowalt * 0.08);
|
||||||
// next.control.scale = Vec3::one();
|
// next.control.scale = Vec3::one();
|
||||||
@ -172,26 +172,52 @@ impl Animation for WieldAnimation {
|
|||||||
// next.r_control.scale = Vec3::one();
|
// next.r_control.scale = Vec3::one();
|
||||||
},
|
},
|
||||||
Some(ToolKind::Axe(_)) => {
|
Some(ToolKind::Axe(_)) => {
|
||||||
next.l_hand.offset = Vec3::new(-4.0, 3.0, 6.0);
|
if velocity < 0.5 {
|
||||||
next.l_hand.ori = Quaternion::rotation_x(-0.3)
|
next.head.offset = Vec3::new(
|
||||||
* Quaternion::rotation_z(3.14 - 0.3)
|
0.0,
|
||||||
* Quaternion::rotation_y(-0.8);
|
-3.5 + skeleton_attr.head.0,
|
||||||
|
skeleton_attr.head.1 + u_slow * 0.1,
|
||||||
|
);
|
||||||
|
next.head.ori = Quaternion::rotation_z(head_look.x)
|
||||||
|
* Quaternion::rotation_x(0.35 + head_look.y.abs());
|
||||||
|
next.head.scale = Vec3::one() * skeleton_attr.head_scale;
|
||||||
|
next.chest.ori = Quaternion::rotation_x(-0.35)
|
||||||
|
* Quaternion::rotation_y(u_slowalt * 0.04)
|
||||||
|
* Quaternion::rotation_z(0.15);
|
||||||
|
next.belt.offset =
|
||||||
|
Vec3::new(0.0, 1.0 + skeleton_attr.belt.0, skeleton_attr.belt.1);
|
||||||
|
next.belt.ori = Quaternion::rotation_x(0.15)
|
||||||
|
* Quaternion::rotation_y(u_slowalt * 0.03)
|
||||||
|
* Quaternion::rotation_z(0.15);
|
||||||
|
next.shorts.offset =
|
||||||
|
Vec3::new(0.0, 1.0 + skeleton_attr.shorts.0, skeleton_attr.shorts.1);
|
||||||
|
next.shorts.ori = Quaternion::rotation_x(0.15) * Quaternion::rotation_z(0.25);
|
||||||
|
next.control.ori = Quaternion::rotation_x(1.8)
|
||||||
|
* Quaternion::rotation_y(-0.5)
|
||||||
|
* Quaternion::rotation_z(PI - 0.2);
|
||||||
|
next.control.scale = Vec3::one();
|
||||||
|
} else {
|
||||||
|
next.control.ori = Quaternion::rotation_x(2.1)
|
||||||
|
* Quaternion::rotation_y(-0.4)
|
||||||
|
* Quaternion::rotation_z(PI - 0.2);
|
||||||
|
next.control.scale = Vec3::one();
|
||||||
|
}
|
||||||
|
next.l_hand.offset = Vec3::new(-0.5, 0.0, 4.0);
|
||||||
|
next.l_hand.ori = Quaternion::rotation_x(PI / 2.0)
|
||||||
|
* Quaternion::rotation_z(0.0)
|
||||||
|
* Quaternion::rotation_y(0.0);
|
||||||
next.l_hand.scale = Vec3::one() * 1.08;
|
next.l_hand.scale = Vec3::one() * 1.08;
|
||||||
next.r_hand.offset = Vec3::new(-2.5, 9.0, 4.0);
|
next.r_hand.offset = Vec3::new(0.5, 0.0, -2.5);
|
||||||
next.r_hand.ori = Quaternion::rotation_x(-0.3)
|
next.r_hand.ori = Quaternion::rotation_x(PI / 2.0)
|
||||||
* Quaternion::rotation_z(3.14 - 0.3)
|
* Quaternion::rotation_z(0.0)
|
||||||
* Quaternion::rotation_y(-0.8);
|
* Quaternion::rotation_y(0.0);
|
||||||
next.r_hand.scale = Vec3::one() * 1.06;
|
next.r_hand.scale = Vec3::one() * 1.06;
|
||||||
next.main.offset = Vec3::new(-6.0, 10.0, -1.0);
|
next.main.offset = Vec3::new(-0.0, -2.0, -1.0);
|
||||||
next.main.ori = Quaternion::rotation_x(1.27)
|
next.main.ori = Quaternion::rotation_x(0.0)
|
||||||
* Quaternion::rotation_y(-0.3)
|
* Quaternion::rotation_y(0.0)
|
||||||
* Quaternion::rotation_z(-0.8);
|
* Quaternion::rotation_z(0.0);
|
||||||
|
|
||||||
next.control.offset = Vec3::new(0.0, 0.0, 0.0);
|
next.control.offset = Vec3::new(-3.0, 11.0, 3.0);
|
||||||
next.control.ori = Quaternion::rotation_x(u_slowalt * 0.1 + 0.2)
|
|
||||||
* Quaternion::rotation_y(-0.3)
|
|
||||||
* Quaternion::rotation_z(u_slow * 0.1 + 0.0);
|
|
||||||
next.control.scale = Vec3::one();
|
|
||||||
},
|
},
|
||||||
Some(ToolKind::Hammer(_)) => {
|
Some(ToolKind::Hammer(_)) => {
|
||||||
next.l_hand.offset = Vec3::new(-12.0, 0.0, 0.0);
|
next.l_hand.offset = Vec3::new(-12.0, 0.0, 0.0);
|
||||||
|
@ -117,6 +117,7 @@ image_ids! {
|
|||||||
flyingrod_m2: "voxygen.element.icons.debug_wand_m2",
|
flyingrod_m2: "voxygen.element.icons.debug_wand_m2",
|
||||||
charge: "voxygen.element.icons.skill_charge_3",
|
charge: "voxygen.element.icons.skill_charge_3",
|
||||||
hammerleap: "voxygen.element.icons.skill_hammerleap",
|
hammerleap: "voxygen.element.icons.skill_hammerleap",
|
||||||
|
axespin: "voxygen.element.icons.skill_axespin",
|
||||||
|
|
||||||
// Skillbar
|
// Skillbar
|
||||||
level_up: "voxygen.element.misc_bg.level_up",
|
level_up: "voxygen.element.misc_bg.level_up",
|
||||||
|
@ -708,7 +708,7 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
Some(ToolKind::Dagger(_)) => self.imgs.onehdagger_m2,
|
Some(ToolKind::Dagger(_)) => self.imgs.onehdagger_m2,
|
||||||
Some(ToolKind::Shield(_)) => self.imgs.onehshield_m2,
|
Some(ToolKind::Shield(_)) => self.imgs.onehshield_m2,
|
||||||
Some(ToolKind::Hammer(_)) => self.imgs.hammerleap,
|
Some(ToolKind::Hammer(_)) => self.imgs.hammerleap,
|
||||||
Some(ToolKind::Axe(_)) => self.imgs.nothing,
|
Some(ToolKind::Axe(_)) => self.imgs.axespin,
|
||||||
Some(ToolKind::Bow(_)) => self.imgs.bow_m2,
|
Some(ToolKind::Bow(_)) => self.imgs.bow_m2,
|
||||||
Some(ToolKind::Staff(StaffKind::Sceptre)) => self.imgs.heal_0,
|
Some(ToolKind::Staff(StaffKind::Sceptre)) => self.imgs.heal_0,
|
||||||
Some(ToolKind::Staff(_)) => self.imgs.staff_m2,
|
Some(ToolKind::Staff(_)) => self.imgs.staff_m2,
|
||||||
|
@ -8,6 +8,7 @@ pub const FAR_PLANE: f32 = 100000.0;
|
|||||||
|
|
||||||
const FIRST_PERSON_INTERP_TIME: f32 = 0.1;
|
const FIRST_PERSON_INTERP_TIME: f32 = 0.1;
|
||||||
const THIRD_PERSON_INTERP_TIME: f32 = 0.1;
|
const THIRD_PERSON_INTERP_TIME: f32 = 0.1;
|
||||||
|
const FREEFLY_INTERP_TIME: f32 = 0.0;
|
||||||
const LERP_ORI_RATE: f32 = 15.0;
|
const LERP_ORI_RATE: f32 = 15.0;
|
||||||
pub const MIN_ZOOM: f32 = 0.1;
|
pub const MIN_ZOOM: f32 = 0.1;
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ pub const MIN_ZOOM: f32 = 0.1;
|
|||||||
pub enum CameraMode {
|
pub enum CameraMode {
|
||||||
FirstPerson = 0,
|
FirstPerson = 0,
|
||||||
ThirdPerson = 1,
|
ThirdPerson = 1,
|
||||||
|
Freefly = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CameraMode {
|
impl Default for CameraMode {
|
||||||
@ -75,15 +77,7 @@ impl Camera {
|
|||||||
/// and position of the camera.
|
/// and position of the camera.
|
||||||
pub fn compute_dependents(&mut self, terrain: &impl ReadVol) {
|
pub fn compute_dependents(&mut self, terrain: &impl ReadVol) {
|
||||||
let dist = {
|
let dist = {
|
||||||
let (start, end) = (
|
let (start, end) = (self.focus - self.forward() * self.dist, self.focus);
|
||||||
self.focus
|
|
||||||
+ (Vec3::new(
|
|
||||||
-f32::sin(self.ori.x) * f32::cos(self.ori.y),
|
|
||||||
-f32::cos(self.ori.x) * f32::cos(self.ori.y),
|
|
||||||
f32::sin(self.ori.y),
|
|
||||||
) * self.dist),
|
|
||||||
self.focus,
|
|
||||||
);
|
|
||||||
|
|
||||||
match terrain
|
match terrain
|
||||||
.ray(start, end)
|
.ray(start, end)
|
||||||
@ -161,13 +155,10 @@ impl Camera {
|
|||||||
|
|
||||||
/// Zoom the camera by the given delta, limiting the input accordingly.
|
/// Zoom the camera by the given delta, limiting the input accordingly.
|
||||||
pub fn zoom_by(&mut self, delta: f32) {
|
pub fn zoom_by(&mut self, delta: f32) {
|
||||||
match self.mode {
|
if self.mode == CameraMode::ThirdPerson {
|
||||||
CameraMode::ThirdPerson => {
|
// Clamp camera dist to the 2 <= x <= infinity range
|
||||||
// Clamp camera dist to the 2 <= x <= infinity range
|
self.tgt_dist = (self.tgt_dist + delta).max(2.0);
|
||||||
self.tgt_dist = (self.tgt_dist + delta).max(2.0);
|
}
|
||||||
},
|
|
||||||
CameraMode::FirstPerson => {},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Zoom with the ability to switch between first and third-person mode.
|
/// Zoom with the ability to switch between first and third-person mode.
|
||||||
@ -187,6 +178,7 @@ impl Camera {
|
|||||||
self.set_mode(CameraMode::ThirdPerson);
|
self.set_mode(CameraMode::ThirdPerson);
|
||||||
self.tgt_dist = MIN_THIRD_PERSON;
|
self.tgt_dist = MIN_THIRD_PERSON;
|
||||||
},
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -250,6 +242,7 @@ impl Camera {
|
|||||||
match self.mode {
|
match self.mode {
|
||||||
CameraMode::FirstPerson => FIRST_PERSON_INTERP_TIME,
|
CameraMode::FirstPerson => FIRST_PERSON_INTERP_TIME,
|
||||||
CameraMode::ThirdPerson => THIRD_PERSON_INTERP_TIME,
|
CameraMode::ThirdPerson => THIRD_PERSON_INTERP_TIME,
|
||||||
|
CameraMode::Freefly => FREEFLY_INTERP_TIME,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,6 +286,9 @@ impl Camera {
|
|||||||
CameraMode::FirstPerson => {
|
CameraMode::FirstPerson => {
|
||||||
self.set_distance(MIN_ZOOM);
|
self.set_distance(MIN_ZOOM);
|
||||||
},
|
},
|
||||||
|
CameraMode::Freefly => {
|
||||||
|
self.zoom_by(0.0);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,4 +303,45 @@ impl Camera {
|
|||||||
mode => mode,
|
mode => mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cycle the camera to its next valid mode. If is_admin is false then only
|
||||||
|
/// modes which are accessible without admin access will be cycled to.
|
||||||
|
pub fn next_mode(&mut self, is_admin: bool) {
|
||||||
|
self.set_mode(match self.mode {
|
||||||
|
CameraMode::ThirdPerson => CameraMode::FirstPerson,
|
||||||
|
CameraMode::FirstPerson => {
|
||||||
|
if is_admin {
|
||||||
|
CameraMode::Freefly
|
||||||
|
} else {
|
||||||
|
CameraMode::ThirdPerson
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CameraMode::Freefly => CameraMode::ThirdPerson,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a unit vector in the forward direction for the current camera
|
||||||
|
/// orientation
|
||||||
|
pub fn forward(&self) -> Vec3<f32> {
|
||||||
|
Vec3::new(
|
||||||
|
f32::sin(self.ori.x) * f32::cos(self.ori.y),
|
||||||
|
f32::cos(self.ori.x) * f32::cos(self.ori.y),
|
||||||
|
-f32::sin(self.ori.y),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a unit vector in the right direction for the current camera
|
||||||
|
/// orientation
|
||||||
|
pub fn right(&self) -> Vec3<f32> {
|
||||||
|
const UP: Vec3<f32> = Vec3::new(0.0, 0.0, 1.0);
|
||||||
|
self.forward().cross(UP).normalized()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a unit vector in the forward direction on the XY plane for
|
||||||
|
/// the current camera orientation
|
||||||
|
pub fn forward_xy(&self) -> Vec2<f32> { Vec2::new(f32::sin(self.ori.x), f32::cos(self.ori.x)) }
|
||||||
|
|
||||||
|
/// Return a unit vector in the right direction on the XY plane for
|
||||||
|
/// the current camera orientation
|
||||||
|
pub fn right_xy(&self) -> Vec2<f32> { Vec2::new(f32::cos(self.ori.x), -f32::sin(self.ori.x)) }
|
||||||
}
|
}
|
||||||
|
@ -122,42 +122,50 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
|
|||||||
|
|
||||||
[
|
[
|
||||||
match camera_mode {
|
match camera_mode {
|
||||||
CameraMode::ThirdPerson => Some(
|
CameraMode::ThirdPerson | CameraMode::Freefly => Some(
|
||||||
humanoid_head_spec
|
humanoid_head_spec
|
||||||
.mesh_head(&body, |segment, offset| generate_mesh(segment, offset)),
|
.mesh_head(&body, |segment, offset| generate_mesh(segment, offset)),
|
||||||
),
|
),
|
||||||
CameraMode::FirstPerson => None,
|
CameraMode::FirstPerson => None,
|
||||||
},
|
},
|
||||||
match camera_mode {
|
match camera_mode {
|
||||||
CameraMode::ThirdPerson => Some(humanoid_armor_chest_spec.mesh_chest(
|
CameraMode::ThirdPerson | CameraMode::Freefly => {
|
||||||
&body,
|
Some(humanoid_armor_chest_spec.mesh_chest(
|
||||||
loadout,
|
&body,
|
||||||
|segment, offset| generate_mesh(segment, offset),
|
loadout,
|
||||||
)),
|
|segment, offset| generate_mesh(segment, offset),
|
||||||
|
))
|
||||||
|
},
|
||||||
CameraMode::FirstPerson => None,
|
CameraMode::FirstPerson => None,
|
||||||
},
|
},
|
||||||
match camera_mode {
|
match camera_mode {
|
||||||
CameraMode::ThirdPerson => Some(humanoid_armor_belt_spec.mesh_belt(
|
CameraMode::ThirdPerson | CameraMode::Freefly => {
|
||||||
&body,
|
Some(humanoid_armor_belt_spec.mesh_belt(
|
||||||
loadout,
|
&body,
|
||||||
|segment, offset| generate_mesh(segment, offset),
|
loadout,
|
||||||
)),
|
|segment, offset| generate_mesh(segment, offset),
|
||||||
|
))
|
||||||
|
},
|
||||||
CameraMode::FirstPerson => None,
|
CameraMode::FirstPerson => None,
|
||||||
},
|
},
|
||||||
match camera_mode {
|
match camera_mode {
|
||||||
CameraMode::ThirdPerson => Some(humanoid_armor_back_spec.mesh_back(
|
CameraMode::ThirdPerson | CameraMode::Freefly => {
|
||||||
&body,
|
Some(humanoid_armor_back_spec.mesh_back(
|
||||||
loadout,
|
&body,
|
||||||
|segment, offset| generate_mesh(segment, offset),
|
loadout,
|
||||||
)),
|
|segment, offset| generate_mesh(segment, offset),
|
||||||
|
))
|
||||||
|
},
|
||||||
CameraMode::FirstPerson => None,
|
CameraMode::FirstPerson => None,
|
||||||
},
|
},
|
||||||
match camera_mode {
|
match camera_mode {
|
||||||
CameraMode::ThirdPerson => Some(humanoid_armor_pants_spec.mesh_pants(
|
CameraMode::ThirdPerson | CameraMode::Freefly => {
|
||||||
&body,
|
Some(humanoid_armor_pants_spec.mesh_pants(
|
||||||
loadout,
|
&body,
|
||||||
|segment, offset| generate_mesh(segment, offset),
|
loadout,
|
||||||
)),
|
|segment, offset| generate_mesh(segment, offset),
|
||||||
|
))
|
||||||
|
},
|
||||||
CameraMode::FirstPerson => None,
|
CameraMode::FirstPerson => None,
|
||||||
},
|
},
|
||||||
Some(humanoid_armor_hand_spec.mesh_left_hand(
|
Some(humanoid_armor_hand_spec.mesh_left_hand(
|
||||||
@ -181,7 +189,7 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
|
|||||||
|segment, offset| generate_mesh(segment, offset),
|
|segment, offset| generate_mesh(segment, offset),
|
||||||
)),
|
)),
|
||||||
match camera_mode {
|
match camera_mode {
|
||||||
CameraMode::ThirdPerson => {
|
CameraMode::ThirdPerson | CameraMode::Freefly => {
|
||||||
Some(humanoid_armor_shoulder_spec.mesh_left_shoulder(
|
Some(humanoid_armor_shoulder_spec.mesh_left_shoulder(
|
||||||
&body,
|
&body,
|
||||||
loadout,
|
loadout,
|
||||||
@ -191,7 +199,7 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
|
|||||||
CameraMode::FirstPerson => None,
|
CameraMode::FirstPerson => None,
|
||||||
},
|
},
|
||||||
match camera_mode {
|
match camera_mode {
|
||||||
CameraMode::ThirdPerson => {
|
CameraMode::ThirdPerson | CameraMode::Freefly => {
|
||||||
Some(humanoid_armor_shoulder_spec.mesh_right_shoulder(
|
Some(humanoid_armor_shoulder_spec.mesh_right_shoulder(
|
||||||
&body,
|
&body,
|
||||||
loadout,
|
loadout,
|
||||||
|
@ -715,6 +715,15 @@ impl FigureMgr {
|
|||||||
skeleton_attr,
|
skeleton_attr,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
CharacterState::SpinMelee(_) => {
|
||||||
|
anim::character::SpinMeleeAnimation::update_skeleton(
|
||||||
|
&target_base,
|
||||||
|
(active_tool_kind, second_tool_kind, vel.0, time),
|
||||||
|
state.state_time,
|
||||||
|
&mut state_animation_rate,
|
||||||
|
skeleton_attr,
|
||||||
|
)
|
||||||
|
},
|
||||||
CharacterState::TripleStrike(s) => match s.stage {
|
CharacterState::TripleStrike(s) => match s.stage {
|
||||||
triple_strike::Stage::First => {
|
triple_strike::Stage::First => {
|
||||||
anim::character::AlphaAnimation::update_skeleton(
|
anim::character::AlphaAnimation::update_skeleton(
|
||||||
|
@ -395,10 +395,17 @@ impl Scene {
|
|||||||
},
|
},
|
||||||
CameraMode::ThirdPerson if scene_data.is_aiming => player_scale * 2.1,
|
CameraMode::ThirdPerson if scene_data.is_aiming => player_scale * 2.1,
|
||||||
CameraMode::ThirdPerson => player_scale * 1.65,
|
CameraMode::ThirdPerson => player_scale * 1.65,
|
||||||
|
CameraMode::Freefly => 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.camera
|
match self.camera.get_mode() {
|
||||||
.set_focus_pos(player_pos + Vec3::unit_z() * (up - tilt.min(0.0).sin() * dist * 0.6));
|
CameraMode::FirstPerson | CameraMode::ThirdPerson => {
|
||||||
|
self.camera.set_focus_pos(
|
||||||
|
player_pos + Vec3::unit_z() * (up - tilt.min(0.0).sin() * dist * 0.6),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
CameraMode::Freefly => {},
|
||||||
|
};
|
||||||
|
|
||||||
// Tick camera for interpolation.
|
// Tick camera for interpolation.
|
||||||
self.camera.update(
|
self.camera.update(
|
||||||
|
@ -177,7 +177,9 @@ impl PlayState for SessionState {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut ori = self.scene.camera().get_orientation();
|
let mut walk_forward_dir = self.scene.camera().forward_xy();
|
||||||
|
let mut walk_right_dir = self.scene.camera().right_xy();
|
||||||
|
let mut freefly_vel = Vec3::zero();
|
||||||
let mut free_look = false;
|
let mut free_look = false;
|
||||||
let mut auto_walk = false;
|
let mut auto_walk = false;
|
||||||
|
|
||||||
@ -526,6 +528,14 @@ impl PlayState for SessionState {
|
|||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Event::InputUpdate(GameInput::CycleCamera, true) => {
|
||||||
|
// Prevent accessing camera modes which aren't available in multiplayer
|
||||||
|
// unless you are an admin. This is an easily bypassed clientside check.
|
||||||
|
// The server should do its own filtering of which entities are sent to
|
||||||
|
// clients to prevent abuse.
|
||||||
|
let camera = self.scene.camera_mut();
|
||||||
|
camera.next_mode(self.client.borrow().is_admin());
|
||||||
|
},
|
||||||
Event::AnalogGameInput(input) => match input {
|
Event::AnalogGameInput(input) => match input {
|
||||||
AnalogGameInput::MovementX(v) => {
|
AnalogGameInput::MovementX(v) => {
|
||||||
self.key_state.analog_matrix.x = v;
|
self.key_state.analog_matrix.x = v;
|
||||||
@ -552,17 +562,60 @@ impl PlayState for SessionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !free_look {
|
if !free_look {
|
||||||
ori = self.scene.camera().get_orientation();
|
walk_forward_dir = self.scene.camera().forward_xy();
|
||||||
|
walk_right_dir = self.scene.camera().right_xy();
|
||||||
self.inputs.look_dir = Dir::from_unnormalized(cam_dir + aim_dir_offset).unwrap();
|
self.inputs.look_dir = Dir::from_unnormalized(cam_dir + aim_dir_offset).unwrap();
|
||||||
}
|
}
|
||||||
// Calculate the movement input vector of the player from the current key
|
|
||||||
// presses and the camera direction.
|
// Get the current state of movement related inputs
|
||||||
let unit_vecs = (
|
let input_vec = self.key_state.dir_vec();
|
||||||
Vec2::new(ori[0].cos(), -ori[0].sin()),
|
let (axis_right, axis_up) = (input_vec[0], input_vec[1]);
|
||||||
Vec2::new(ori[0].sin(), ori[0].cos()),
|
|
||||||
);
|
match self.scene.camera().get_mode() {
|
||||||
let dir_vec = self.key_state.dir_vec();
|
camera::CameraMode::FirstPerson | camera::CameraMode::ThirdPerson => {
|
||||||
self.inputs.move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
|
// Move the player character based on their walking direction.
|
||||||
|
// This could be different from the camera direction if free look is enabled.
|
||||||
|
self.inputs.move_dir = walk_right_dir * axis_right + walk_forward_dir * axis_up;
|
||||||
|
freefly_vel = Vec3::zero();
|
||||||
|
},
|
||||||
|
|
||||||
|
camera::CameraMode::Freefly => {
|
||||||
|
// Move the camera freely in 3d space. Apply acceleration so that
|
||||||
|
// the movement feels more natural and controlled.
|
||||||
|
const FREEFLY_ACCEL: f32 = 120.0;
|
||||||
|
const FREEFLY_DAMPING: f32 = 80.0;
|
||||||
|
const FREEFLY_MAX_SPEED: f32 = 50.0;
|
||||||
|
|
||||||
|
let forward = self.scene.camera().forward();
|
||||||
|
let right = self.scene.camera().right();
|
||||||
|
let dir = right * axis_right + forward * axis_up;
|
||||||
|
|
||||||
|
let dt = clock.get_last_delta().as_secs_f32();
|
||||||
|
if freefly_vel.magnitude_squared() > 0.01 {
|
||||||
|
let new_vel =
|
||||||
|
freefly_vel - freefly_vel.normalized() * (FREEFLY_DAMPING * dt);
|
||||||
|
if freefly_vel.dot(new_vel) > 0.0 {
|
||||||
|
freefly_vel = new_vel;
|
||||||
|
} else {
|
||||||
|
freefly_vel = Vec3::zero();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dir.magnitude_squared() > 0.01 {
|
||||||
|
freefly_vel += dir * (FREEFLY_ACCEL * dt);
|
||||||
|
if freefly_vel.magnitude() > FREEFLY_MAX_SPEED {
|
||||||
|
freefly_vel = freefly_vel.normalized() * FREEFLY_MAX_SPEED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let pos = self.scene.camera().get_focus_pos();
|
||||||
|
self.scene
|
||||||
|
.camera_mut()
|
||||||
|
.set_focus_pos(pos + freefly_vel * dt);
|
||||||
|
|
||||||
|
// Do not apply any movement to the player character
|
||||||
|
self.inputs.move_dir = Vec2::zero();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
self.inputs.climb = self.key_state.climb();
|
self.inputs.climb = self.key_state.climb();
|
||||||
|
|
||||||
@ -597,7 +650,7 @@ impl PlayState for SessionState {
|
|||||||
global_state,
|
global_state,
|
||||||
DebugInfo {
|
DebugInfo {
|
||||||
tps: clock.get_tps(),
|
tps: clock.get_tps(),
|
||||||
ping_ms: self.client.borrow().get_ping_ms(),
|
ping_ms: self.client.borrow().get_ping_ms_rolling_avg(),
|
||||||
coordinates: self
|
coordinates: self
|
||||||
.client
|
.client
|
||||||
.borrow()
|
.borrow()
|
||||||
|
@ -142,6 +142,7 @@ impl ControlSettings {
|
|||||||
//GameInput::Charge => KeyMouse::Key(VirtualKeyCode::Key1),
|
//GameInput::Charge => KeyMouse::Key(VirtualKeyCode::Key1),
|
||||||
GameInput::FreeLook => KeyMouse::Key(VirtualKeyCode::L),
|
GameInput::FreeLook => KeyMouse::Key(VirtualKeyCode::L),
|
||||||
GameInput::AutoWalk => KeyMouse::Key(VirtualKeyCode::Period),
|
GameInput::AutoWalk => KeyMouse::Key(VirtualKeyCode::Period),
|
||||||
|
GameInput::CycleCamera => KeyMouse::Key(VirtualKeyCode::Key0),
|
||||||
GameInput::Slot1 => KeyMouse::Key(VirtualKeyCode::Key1),
|
GameInput::Slot1 => KeyMouse::Key(VirtualKeyCode::Key1),
|
||||||
GameInput::Slot2 => KeyMouse::Key(VirtualKeyCode::Key2),
|
GameInput::Slot2 => KeyMouse::Key(VirtualKeyCode::Key2),
|
||||||
GameInput::Slot3 => KeyMouse::Key(VirtualKeyCode::Key3),
|
GameInput::Slot3 => KeyMouse::Key(VirtualKeyCode::Key3),
|
||||||
@ -204,6 +205,7 @@ impl Default for ControlSettings {
|
|||||||
//GameInput::Charge,
|
//GameInput::Charge,
|
||||||
GameInput::FreeLook,
|
GameInput::FreeLook,
|
||||||
GameInput::AutoWalk,
|
GameInput::AutoWalk,
|
||||||
|
GameInput::CycleCamera,
|
||||||
GameInput::Slot1,
|
GameInput::Slot1,
|
||||||
GameInput::Slot2,
|
GameInput::Slot2,
|
||||||
GameInput::Slot3,
|
GameInput::Slot3,
|
||||||
|
@ -47,14 +47,14 @@ impl Singleplayer {
|
|||||||
let paused = Arc::new(AtomicBool::new(false));
|
let paused = Arc::new(AtomicBool::new(false));
|
||||||
let paused1 = paused.clone();
|
let paused1 = paused.clone();
|
||||||
|
|
||||||
let server = Server::new(settings2).expect("Failed to create server instance!");
|
|
||||||
|
|
||||||
let server = match thread_pool {
|
|
||||||
Some(pool) => server.with_thread_pool(pool),
|
|
||||||
None => server,
|
|
||||||
};
|
|
||||||
|
|
||||||
let thread = thread::spawn(move || {
|
let thread = thread::spawn(move || {
|
||||||
|
let server = Server::new(settings2).expect("Failed to create server instance!");
|
||||||
|
|
||||||
|
let server = match thread_pool {
|
||||||
|
Some(pool) => server.with_thread_pool(pool),
|
||||||
|
None => server,
|
||||||
|
};
|
||||||
|
|
||||||
run_server(server, receiver, paused1);
|
run_server(server, receiver, paused1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ pub enum GameInput {
|
|||||||
SwapLoadout,
|
SwapLoadout,
|
||||||
FreeLook,
|
FreeLook,
|
||||||
AutoWalk,
|
AutoWalk,
|
||||||
|
CycleCamera,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameInput {
|
impl GameInput {
|
||||||
@ -89,6 +90,7 @@ impl GameInput {
|
|||||||
GameInput::Mount => "gameinput.mount",
|
GameInput::Mount => "gameinput.mount",
|
||||||
GameInput::Enter => "gameinput.enter",
|
GameInput::Enter => "gameinput.enter",
|
||||||
GameInput::Command => "gameinput.command",
|
GameInput::Command => "gameinput.command",
|
||||||
|
GameInput::CycleCamera => "gameinput.cyclecamera",
|
||||||
GameInput::Escape => "gameinput.escape",
|
GameInput::Escape => "gameinput.escape",
|
||||||
GameInput::Map => "gameinput.map",
|
GameInput::Map => "gameinput.map",
|
||||||
GameInput::Bag => "gameinput.bag",
|
GameInput::Bag => "gameinput.bag",
|
||||||
|
Loading…
Reference in New Issue
Block a user