mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Pedantic comment and language fixes.
Former-commit-id: eb49765c911aaa97a9c8ed351216a7a6f8411213
This commit is contained in:
parent
96cdbdb881
commit
6b09fd7c53
@ -4,8 +4,8 @@ stages:
|
|||||||
- post-build
|
- post-build
|
||||||
- executable
|
- executable
|
||||||
|
|
||||||
# our own git fetch command like https://gitlab.com/gitlab-org/gitlab-runner/blob/master/shells/abstract.go
|
# Our own git fetch command like https://gitlab.com/gitlab-org/gitlab-runner/blob/master/shells/abstract.go
|
||||||
# speeds up building because we skip the git clean and dont need any gitlab caches
|
# speeds up building because we skip the git clean and don't need any gitlab caches.
|
||||||
variables:
|
variables:
|
||||||
GIT_STRATEGY: none
|
GIT_STRATEGY: none
|
||||||
before_script:
|
before_script:
|
||||||
|
@ -6,15 +6,15 @@ use std::time::Duration;
|
|||||||
const FPS: u64 = 60;
|
const FPS: u64 = 60;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Init logging
|
// Initialize logging.
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
info!("Starting chat-cli...");
|
info!("Starting chat-cli...");
|
||||||
|
|
||||||
// Set up an fps clock
|
// Set up an fps clock.
|
||||||
let mut clock = Clock::new();
|
let mut clock = Clock::new();
|
||||||
|
|
||||||
// Create client
|
// Create a client.
|
||||||
let mut client =
|
let mut client =
|
||||||
Client::new(([127, 0, 0, 1], 59003), 300).expect("Failed to create client instance");
|
Client::new(([127, 0, 0, 1], 59003), 300).expect("Failed to create client instance");
|
||||||
|
|
||||||
@ -38,10 +38,10 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the server after a tick
|
// Clean up the server after a tick.
|
||||||
client.cleanup();
|
client.cleanup();
|
||||||
|
|
||||||
// Wait for the next tick
|
// Wait for the next tick.
|
||||||
clock.tick(Duration::from_millis(1000 / FPS));
|
clock.tick(Duration::from_millis(1000 / FPS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the client's worker thread pool. This pool should be used for any
|
/// Get a reference to the client's worker thread pool. This pool should be used for any
|
||||||
/// computationally expensive operations that run outside of the main thread (i.e: threads that
|
/// computationally expensive operations that run outside of the main thread (i.e., threads that
|
||||||
/// block on I/O operations are exempt).
|
/// block on I/O operations are exempt).
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn thread_pool(&self) -> &threadpool::ThreadPool {
|
pub fn thread_pool(&self) -> &threadpool::ThreadPool {
|
||||||
@ -114,7 +114,7 @@ impl Client {
|
|||||||
&mut self.state
|
&mut self.state
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the player's entity
|
/// Get the player's entity.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn entity(&self) -> EcsEntity {
|
pub fn entity(&self) -> EcsEntity {
|
||||||
self.entity
|
self.entity
|
||||||
@ -126,13 +126,13 @@ impl Client {
|
|||||||
self.tick
|
self.tick
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a chat message to the server
|
/// Send a chat message to the server.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn send_chat(&mut self, msg: String) {
|
pub fn send_chat(&mut self, msg: String) {
|
||||||
self.postbox.send_message(ClientMsg::Chat(msg))
|
self.postbox.send_message(ClientMsg::Chat(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a single client tick, handle input and update the game state by the given duration
|
/// Execute a single client tick, handle input and update the game state by the given duration.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn tick(&mut self, input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
|
pub fn tick(&mut self, input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
|
||||||
// This tick function is the centre of the Veloren universe. Most client-side things are
|
// This tick function is the centre of the Veloren universe. Most client-side things are
|
||||||
@ -147,13 +147,13 @@ impl Client {
|
|||||||
// 4) Go through the terrain update queue and apply all changes to the terrain
|
// 4) Go through the terrain update queue and apply all changes to the terrain
|
||||||
// 5) Finish the tick, passing control of the main thread back to the frontend
|
// 5) Finish the tick, passing control of the main thread back to the frontend
|
||||||
|
|
||||||
// Build up a list of events for this frame, to be passed to the frontend
|
// Build up a list of events for this frame, to be passed to the frontend.
|
||||||
let mut frontend_events = Vec::new();
|
let mut frontend_events = Vec::new();
|
||||||
|
|
||||||
// Handle new messages from the server
|
// Handle new messages from the server.
|
||||||
frontend_events.append(&mut self.handle_new_messages()?);
|
frontend_events.append(&mut self.handle_new_messages()?);
|
||||||
|
|
||||||
// Pass character control from frontend input to the player's entity
|
// Pass character control from frontend input to the player's entity.
|
||||||
// TODO: Only do this if the entity already has a Control component!
|
// TODO: Only do this if the entity already has a Control component!
|
||||||
self.state.write_component(
|
self.state.write_component(
|
||||||
self.entity,
|
self.entity,
|
||||||
@ -164,10 +164,10 @@ impl Client {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Tick the client's LocalState (step 3)
|
// Tick the client's LocalState (step 3).
|
||||||
self.state.tick(dt);
|
self.state.tick(dt);
|
||||||
|
|
||||||
// Update the server about the player's physics attributes
|
// Update the server about the player's physics attributes.
|
||||||
match (
|
match (
|
||||||
self.state.read_storage().get(self.entity).cloned(),
|
self.state.read_storage().get(self.entity).cloned(),
|
||||||
self.state.read_storage().get(self.entity).cloned(),
|
self.state.read_storage().get(self.entity).cloned(),
|
||||||
@ -180,7 +180,7 @@ impl Client {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the server about the player's currently playing animation and the previous one
|
// Update the server about the player's currently playing animation and the previous one.
|
||||||
if let Some(animation_history) = self
|
if let Some(animation_history) = self
|
||||||
.state
|
.state
|
||||||
.read_storage::<comp::AnimationHistory>()
|
.read_storage::<comp::AnimationHistory>()
|
||||||
@ -201,7 +201,7 @@ impl Client {
|
|||||||
if let Some(pos) = pos {
|
if let Some(pos) = pos {
|
||||||
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
|
let chunk_pos = self.state.terrain().pos_key(pos.0.map(|e| e as i32));
|
||||||
|
|
||||||
// Remove chunks that are too far from the player
|
// Remove chunks that are too far from the player.
|
||||||
let mut chunks_to_remove = Vec::new();
|
let mut chunks_to_remove = Vec::new();
|
||||||
self.state.terrain().iter().for_each(|(key, _)| {
|
self.state.terrain().iter().for_each(|(key, _)| {
|
||||||
if (Vec2::from(chunk_pos) - Vec2::from(key))
|
if (Vec2::from(chunk_pos) - Vec2::from(key))
|
||||||
@ -216,8 +216,8 @@ impl Client {
|
|||||||
self.state.remove_chunk(key);
|
self.state.remove_chunk(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request chunks from the server
|
// Request chunks from the server.
|
||||||
// TODO: This is really not very efficient
|
// TODO: This is really inefficient.
|
||||||
'outer: for dist in 0..10 {
|
'outer: for dist in 0..10 {
|
||||||
for i in chunk_pos.x - dist..chunk_pos.x + dist + 1 {
|
for i in chunk_pos.x - dist..chunk_pos.x + dist + 1 {
|
||||||
for j in chunk_pos.y - dist..chunk_pos.y + dist + 1 {
|
for j in chunk_pos.y - dist..chunk_pos.y + dist + 1 {
|
||||||
@ -237,25 +237,25 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If chunks are taking too long, assume they're no longer pending
|
// If chunks are taking too long, assume they're no longer pending.
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
self.pending_chunks
|
self.pending_chunks
|
||||||
.retain(|_, created| now.duration_since(*created) < Duration::from_secs(10));
|
.retain(|_, created| now.duration_since(*created) < Duration::from_secs(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish the tick, pass control back to the frontend (step 6)
|
// Finish the tick, pass control back to the frontend (step 6).
|
||||||
self.tick += 1;
|
self.tick += 1;
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clean up the client after a tick
|
/// Clean up the client after a tick.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
// Cleanup the local state
|
// Cleanup the local state
|
||||||
self.state.cleanup();
|
self.state.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle new server messages
|
/// Handle new server messages.
|
||||||
fn handle_new_messages(&mut self) -> Result<Vec<Event>, Error> {
|
fn handle_new_messages(&mut self) -> Result<Vec<Event>, Error> {
|
||||||
let mut frontend_events = Vec::new();
|
let mut frontend_events = Vec::new();
|
||||||
|
|
||||||
@ -324,7 +324,7 @@ impl Client {
|
|||||||
} else if self.state.get_time() - self.last_ping > SERVER_TIMEOUT {
|
} else if self.state.get_time() - self.last_ping > SERVER_TIMEOUT {
|
||||||
return Err(Error::ServerTimeout);
|
return Err(Error::ServerTimeout);
|
||||||
} else if self.state.get_time() - self.last_ping > SERVER_TIMEOUT * 0.5 {
|
} else if self.state.get_time() - self.last_ping > SERVER_TIMEOUT * 0.5 {
|
||||||
// Try pinging the server if the timeout is nearing
|
// Try pinging the server if the timeout is nearing.
|
||||||
self.postbox.send_message(ClientMsg::Ping);
|
self.postbox.send_message(ClientMsg::Ping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@ use std::{
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// An asset of a different type has already been loaded with this specifier
|
/// An asset of a different type has already been loaded with this specifier.
|
||||||
InvalidType,
|
InvalidType,
|
||||||
/// Asset does not exist
|
/// Asset does not exist.
|
||||||
NotFound(String),
|
NotFound(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,8 +35,8 @@ lazy_static! {
|
|||||||
RwLock::new(HashMap::new());
|
RwLock::new(HashMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function used to load assets
|
/// Function used to load assets.
|
||||||
/// loaded assets are cached in a global singleton hashmap
|
/// Loaded assets are cached in a global singleton hashmap.
|
||||||
/// Example usage:
|
/// Example usage:
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use image::DynamicImage;
|
/// use image::DynamicImage;
|
||||||
@ -54,9 +54,9 @@ pub fn load<A: Asset + 'static>(specifier: &str) -> Result<Arc<A>, Error> {
|
|||||||
.downcast()?)
|
.downcast()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function used to load assets that will panic if the asset is not found
|
/// Function used to load assets that will panic if the asset is not found.
|
||||||
/// Use this to load essential assets
|
/// Use this to load essential assets.
|
||||||
/// loaded assets are cached in a global singleton hashmap
|
/// Loaded assets are cached in a global singleton hashmap.
|
||||||
/// Example usage:
|
/// Example usage:
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use image::DynamicImage;
|
/// use image::DynamicImage;
|
||||||
@ -89,11 +89,11 @@ impl Asset for DotVoxData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: System to load file from specifiers (eg "core.ui.backgrounds.city")
|
// TODO: System to load file from specifiers (e.g.: "core.ui.backgrounds.city").
|
||||||
fn try_open_with_path(name: &str) -> Option<File> {
|
fn try_open_with_path(name: &str) -> Option<File> {
|
||||||
debug!("Trying to access \"{}\"", name);
|
debug!("Trying to access \"{}\"", name);
|
||||||
// TODO: don't do this?
|
// TODO: Don't do this?
|
||||||
// if it's stupid and it works..,
|
// If it's stupid but it works...
|
||||||
[
|
[
|
||||||
"assets".to_string(),
|
"assets".to_string(),
|
||||||
"../assets".to_string(), /* optimizations */
|
"../assets".to_string(), /* optimizations */
|
||||||
|
@ -43,7 +43,7 @@ impl Clock {
|
|||||||
.duration_since(self.last_sys_time)
|
.duration_since(self.last_sys_time)
|
||||||
.expect("Time went backwards!");
|
.expect("Time went backwards!");
|
||||||
|
|
||||||
// Attempt to sleep to fill the gap
|
// Attempt to sleep to fill the gap.
|
||||||
if let Some(sleep_dur) = tgt.checked_sub(delta) {
|
if let Some(sleep_dur) = tgt.checked_sub(delta) {
|
||||||
let adjustment = if self.running_tps_average == 0.0 {
|
let adjustment = if self.running_tps_average == 0.0 {
|
||||||
1.0
|
1.0
|
||||||
|
@ -71,24 +71,23 @@ pub enum Draw {
|
|||||||
}
|
}
|
||||||
////
|
////
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub enum Pighead {
|
pub enum PigHead {
|
||||||
Default,
|
Default,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub enum Pigchest {
|
pub enum PigChest {
|
||||||
Default,
|
Default,
|
||||||
}
|
}
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub enum Pigleg_l {
|
pub enum PigLegL {
|
||||||
Default,
|
Default,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub enum Pigleg_r {
|
pub enum PigLegR {
|
||||||
Default,
|
Default,
|
||||||
}
|
}
|
||||||
////
|
|
||||||
|
|
||||||
const ALL_RACES: [Race; 6] = [
|
const ALL_RACES: [Race; 6] = [
|
||||||
Race::Danari,
|
Race::Danari,
|
||||||
@ -158,19 +157,19 @@ const ALL_QRACES: [Race; 6] = [
|
|||||||
Race::Undead,
|
Race::Undead,
|
||||||
];
|
];
|
||||||
const ALL_QBODY_TYPES: [BodyType; 3] = [BodyType::Female, BodyType::Male, BodyType::Unspecified];
|
const ALL_QBODY_TYPES: [BodyType; 3] = [BodyType::Female, BodyType::Male, BodyType::Unspecified];
|
||||||
const ALL_QHEADS: [Pighead; 1] = [Pighead::Default];
|
const ALL_QPIG_HEADS: [PigHead; 1] = [PigHead::Default];
|
||||||
const ALL_QCHESTS: [Pigchest; 1] = [Pigchest::Default];
|
const ALL_QPIG_CHESTS: [PigChest; 1] = [PigChest::Default];
|
||||||
const ALL_QPIGLEG_LS: [Pigleg_l; 1] = [Pigleg_l::Default];
|
const ALL_QPIG_LEG_LS: [PigLegL; 1] = [PigLegL::Default];
|
||||||
const ALL_QPIGLEG_RS: [Pigleg_r; 1] = [Pigleg_r::Default];
|
const ALL_QPIG_LEG_RS: [PigLegR; 1] = [PigLegR::Default];
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub struct QuadrupedBody {
|
pub struct QuadrupedBody {
|
||||||
pub race: Race,
|
pub race: Race,
|
||||||
pub body_type: BodyType,
|
pub body_type: BodyType,
|
||||||
pub pighead: Pighead,
|
pub pig_head: PigHead,
|
||||||
pub pigchest: Pigchest,
|
pub pig_chest: PigChest,
|
||||||
pub pigleg_l: Pigleg_l,
|
pub pig_leg_l: PigLegL,
|
||||||
pub pigleg_r: Pigleg_r,
|
pub pig_leg_r: PigLegR,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QuadrupedBody {
|
impl QuadrupedBody {
|
||||||
@ -178,10 +177,10 @@ impl QuadrupedBody {
|
|||||||
Self {
|
Self {
|
||||||
race: *thread_rng().choose(&ALL_QRACES).unwrap(),
|
race: *thread_rng().choose(&ALL_QRACES).unwrap(),
|
||||||
body_type: *thread_rng().choose(&ALL_QBODY_TYPES).unwrap(),
|
body_type: *thread_rng().choose(&ALL_QBODY_TYPES).unwrap(),
|
||||||
pighead: *thread_rng().choose(&ALL_QHEADS).unwrap(),
|
pig_head: *thread_rng().choose(&ALL_QPIG_HEADS).unwrap(),
|
||||||
pigchest: *thread_rng().choose(&ALL_QCHESTS).unwrap(),
|
pig_chest: *thread_rng().choose(&ALL_QPIG_CHESTS).unwrap(),
|
||||||
pigleg_l: *thread_rng().choose(&ALL_QPIGLEG_LS).unwrap(),
|
pig_leg_l: *thread_rng().choose(&ALL_QPIG_LEG_LS).unwrap(),
|
||||||
pigleg_r: *thread_rng().choose(&ALL_QPIGLEG_RS).unwrap(),
|
pig_leg_r: *thread_rng().choose(&ALL_QPIG_LEG_RS).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use specs::{Component, FlaggedStorage, NullStorage, VecStorage};
|
use specs::{Component, FlaggedStorage, NullStorage, VecStorage};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
// Pos
|
// Position
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Pos(pub Vec3<f32>);
|
pub struct Pos(pub Vec3<f32>);
|
||||||
@ -10,7 +10,7 @@ impl Component for Pos {
|
|||||||
type Storage = VecStorage<Self>;
|
type Storage = VecStorage<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vel
|
// Velocity
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Vel(pub Vec3<f32>);
|
pub struct Vel(pub Vec3<f32>);
|
||||||
@ -19,7 +19,7 @@ impl Component for Vel {
|
|||||||
type Storage = VecStorage<Self>;
|
type Storage = VecStorage<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dir
|
// Direction
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Dir(pub Vec3<f32>);
|
pub struct Dir(pub Vec3<f32>);
|
||||||
@ -28,7 +28,7 @@ impl Component for Dir {
|
|||||||
type Storage = VecStorage<Self>;
|
type Storage = VecStorage<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dir
|
// Update
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct ForceUpdate;
|
pub struct ForceUpdate;
|
||||||
|
@ -4,7 +4,7 @@ use vek::*;
|
|||||||
// Crate
|
// Crate
|
||||||
use crate::vol::Vox;
|
use crate::vol::Vox;
|
||||||
|
|
||||||
/// A type representing a single voxel in a figure
|
/// A type representing a single voxel in a figure.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum Cell {
|
pub enum Cell {
|
||||||
Filled([u8; 3]),
|
Filled([u8; 3]),
|
||||||
|
@ -23,7 +23,7 @@ pub mod util;
|
|||||||
pub mod vol;
|
pub mod vol;
|
||||||
pub mod volumes;
|
pub mod volumes;
|
||||||
|
|
||||||
/// The networking module containing high-level wrappers of `TcpListener` and `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by both the server and client
|
/// The networking module containing high-level wrappers of `TcpListener` and `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by both the server and client.
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::net::SocketAddr;
|
/// use std::net::SocketAddr;
|
||||||
|
@ -2,7 +2,8 @@ use crate::{comp, state};
|
|||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
// Automatically derive From<T> for EcsResPacket for each variant EcsResPacket::T(T)
|
// Automatically derive From<T> for EcsResPacket
|
||||||
|
// for each variant EcsResPacket::T(T).
|
||||||
sphynx::sum_type! {
|
sphynx::sum_type! {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum EcsResPacket {
|
pub enum EcsResPacket {
|
||||||
@ -11,7 +12,8 @@ sphynx::sum_type! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl sphynx::ResPacket for EcsResPacket {}
|
impl sphynx::ResPacket for EcsResPacket {}
|
||||||
// Automatically derive From<T> for EcsCompPacket for each variant EcsCompPacket::T(T)
|
// Automatically derive From<T> for EcsCompPacket
|
||||||
|
// for each variant EcsCompPacket::T(T.)
|
||||||
sphynx::sum_type! {
|
sphynx::sum_type! {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum EcsCompPacket {
|
pub enum EcsCompPacket {
|
||||||
@ -23,7 +25,8 @@ sphynx::sum_type! {
|
|||||||
Stats(comp::Stats),
|
Stats(comp::Stats),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Automatically derive From<T> for EcsCompPhantom for each variant EcsCompPhantom::T(PhantomData<T>)
|
// Automatically derive From<T> for EcsCompPhantom
|
||||||
|
// for each variant EcsCompPhantom::T(PhantomData<T>).
|
||||||
sphynx::sum_type! {
|
sphynx::sum_type! {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum EcsCompPhantom {
|
pub enum EcsCompPhantom {
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
/// Messages server sends to client
|
/// Messages server sends to client.
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub enum ServerMsg {
|
pub enum ServerMsg {
|
||||||
// VersionInfo MUST always stay first in this struct
|
// VersionInfo MUST always stay first in this struct.
|
||||||
VersionInfo {},
|
VersionInfo {},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Messages client sends to server
|
/// Messages client sends to server.
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub enum ClientMsg {
|
pub enum ClientMsg {
|
||||||
// VersionInfo MUST always stay first in this struct
|
// VersionInfo MUST always stay first in this struct.
|
||||||
VersionInfo {},
|
VersionInfo {},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Control message type, used in [PostBox](super::PostBox) and [PostOffice](super::PostOffice) to control threads
|
/// Control message type, used in [PostBox](super::PostBox) and [PostOffice](super::PostOffice) to control threads.
|
||||||
pub enum ControlMsg {
|
pub enum ControlMsg {
|
||||||
Shutdown,
|
Shutdown,
|
||||||
}
|
}
|
||||||
|
@ -253,7 +253,7 @@ impl<S: PostSend, R: PostRecv> PostBox<S, R> {
|
|||||||
let _ = self.send_tx.send(data);
|
let _ = self.send_tx.send(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This method is super messy
|
// TODO: This method is super messy.
|
||||||
pub fn next_message(&mut self) -> Option<R> {
|
pub fn next_message(&mut self) -> Option<R> {
|
||||||
if self.err.is_some() {
|
if self.err.is_some() {
|
||||||
return None;
|
return None;
|
||||||
@ -528,14 +528,14 @@ fn connect() {
|
|||||||
|
|
||||||
let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
|
let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
|
||||||
|
|
||||||
// We should start off with 0 incoming connections
|
// We should start off with 0 incoming connections.
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
assert_eq!(postoffice.new_connections().len(), 0);
|
assert_eq!(postoffice.new_connections().len(), 0);
|
||||||
assert_eq!(postoffice.error(), None);
|
assert_eq!(postoffice.error(), None);
|
||||||
|
|
||||||
let postbox = PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap();
|
let postbox = PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap();
|
||||||
|
|
||||||
// Now a postbox has been created, we should have 1 new
|
// Now a postbox has been created, we should have 1 new.
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
let incoming = postoffice.new_connections();
|
let incoming = postoffice.new_connections();
|
||||||
assert_eq!(incoming.len(), 1);
|
assert_eq!(incoming.len(), 1);
|
||||||
@ -549,7 +549,7 @@ fn connect_fail() {
|
|||||||
|
|
||||||
let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(listen_addr).unwrap();
|
let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(listen_addr).unwrap();
|
||||||
|
|
||||||
// We should start off with 0 incoming connections
|
// We should start off with 0 incoming connections.
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
assert_eq!(postoffice.new_connections().len(), 0);
|
assert_eq!(postoffice.new_connections().len(), 0);
|
||||||
assert_eq!(postoffice.error(), None);
|
assert_eq!(postoffice.error(), None);
|
||||||
@ -564,7 +564,7 @@ fn connection_count() {
|
|||||||
let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
|
let mut postoffice = PostOffice::<TestMsg<u32>, TestMsg<f32>>::bind(srv_addr).unwrap();
|
||||||
let mut postboxes = Vec::new();
|
let mut postboxes = Vec::new();
|
||||||
|
|
||||||
// We should start off with 0 incoming connections
|
// We should start off with 0 incoming connections.
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(250));
|
||||||
assert_eq!(postoffice.new_connections().len(), 0);
|
assert_eq!(postoffice.new_connections().len(), 0);
|
||||||
assert_eq!(postoffice.error(), None);
|
assert_eq!(postoffice.error(), None);
|
||||||
@ -573,7 +573,7 @@ fn connection_count() {
|
|||||||
postboxes.push(PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap());
|
postboxes.push(PostBox::<TestMsg<f32>, TestMsg<u32>>::to_server(srv_addr).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5 postboxes created, we should have 5
|
// 5 postboxes created, we should have 5.
|
||||||
thread::sleep(Duration::from_millis(3500));
|
thread::sleep(Duration::from_millis(3500));
|
||||||
let incoming = postoffice.new_connections();
|
let incoming = postoffice.new_connections();
|
||||||
assert_eq!(incoming.len(), 5);
|
assert_eq!(incoming.len(), 5);
|
||||||
@ -597,7 +597,7 @@ fn disconnect() {
|
|||||||
incoming.next().unwrap()
|
incoming.next().unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
// The client postbox has since been disconnected
|
// The client postbox has since been disconnected.
|
||||||
thread::sleep(Duration::from_millis(2050));
|
thread::sleep(Duration::from_millis(2050));
|
||||||
let incoming_msgs = server_postbox.new_messages();
|
let incoming_msgs = server_postbox.new_messages();
|
||||||
assert_eq!(incoming_msgs.len(), 0);
|
assert_eq!(incoming_msgs.len(), 0);
|
||||||
|
@ -188,7 +188,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
|||||||
Ok(None) => {}
|
Ok(None) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try getting messages from the send channel
|
// Try getting messages from the send channel.
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
match send_rx.try_recv() {
|
match send_rx.try_recv() {
|
||||||
Ok(send_msg) => {
|
Ok(send_msg) => {
|
||||||
@ -202,12 +202,12 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Assemble into packet
|
// Assemble into packet.
|
||||||
let mut packet_bytes = msg_bytes.len().to_le_bytes().as_ref().to_vec();
|
let mut packet_bytes = msg_bytes.len().to_le_bytes().as_ref().to_vec();
|
||||||
packet_bytes.push(msg_bytes.iter().fold(0, |a, x| a ^ *x));
|
packet_bytes.push(msg_bytes.iter().fold(0, |a, x| a ^ *x));
|
||||||
packet_bytes.append(&mut msg_bytes);
|
packet_bytes.append(&mut msg_bytes);
|
||||||
|
|
||||||
// Split packet into chunks
|
// Split packet into chunks.
|
||||||
packet_bytes
|
packet_bytes
|
||||||
.chunks(4096)
|
.chunks(4096)
|
||||||
.map(|chunk| chunk.to_vec())
|
.map(|chunk| chunk.to_vec())
|
||||||
@ -222,7 +222,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try sending bytes through the TCP stream
|
// Try sending bytes through the TCP stream.
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
match outgoing_chunks.pop_front() {
|
match outgoing_chunks.pop_front() {
|
||||||
Some(mut chunk) => match stream.write(&chunk) {
|
Some(mut chunk) => match stream.write(&chunk) {
|
||||||
@ -232,7 +232,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||||
// Return chunk to the queue to try again later
|
// Return chunk to the queue to try again later.
|
||||||
outgoing_chunks.push_front(chunk);
|
outgoing_chunks.push_front(chunk);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -246,7 +246,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try receiving bytes from the TCP stream
|
// Try receiving bytes from the TCP stream.
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
let mut buf = [0; 4096];
|
let mut buf = [0; 4096];
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try turning bytes into messages
|
// Try turning bytes into messages.
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
match incoming_buf.get(0..9) {
|
match incoming_buf.get(0..9) {
|
||||||
Some(len_bytes) => {
|
Some(len_bytes) => {
|
||||||
|
@ -54,7 +54,7 @@ impl<'a, V: ReadVol, F: RayUntil<V::Vox>> Ray<'a, V, F> {
|
|||||||
pos = self.from + dir * dist;
|
pos = self.from + dir * dist;
|
||||||
ipos = pos.map(|e| e.floor() as i32);
|
ipos = pos.map(|e| e.floor() as i32);
|
||||||
|
|
||||||
// Allow one iteration above max
|
// Allow one iteration above max.
|
||||||
if dist > max {
|
if dist > max {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -20,25 +20,25 @@ use std::{collections::HashSet, sync::Arc, time::Duration};
|
|||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
/// How much faster should an in-game day be compared to a real day?
|
/// How much faster should an in-game day be compared to a real day?
|
||||||
// TODO: Don't hard-code this
|
// TODO: Don't hard-code this.
|
||||||
const DAY_CYCLE_FACTOR: f64 = 24.0 * 60.0;
|
const DAY_CYCLE_FACTOR: f64 = 24.0 * 60.0;
|
||||||
|
|
||||||
/// A resource to store the time of day
|
/// A resource that stores the time of day.
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct TimeOfDay(f64);
|
pub struct TimeOfDay(f64);
|
||||||
|
|
||||||
/// A resource to store the tick (i.e: physics) time
|
/// A resource that stores the tick (i.e: physics) time.
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Time(f64);
|
pub struct Time(f64);
|
||||||
|
|
||||||
/// A resource used to store the time since the last tick
|
/// A resource that stores the time since the previous tick.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct DeltaTime(pub f32);
|
pub struct DeltaTime(pub f32);
|
||||||
|
|
||||||
/// At what point should we stop speeding up physics to compensate for lag? If we speed physics up
|
/// At what point should we stop speeding up physics to compensate for lag? If we speed physics up
|
||||||
/// too fast, we'd skip important physics events like collisions. This constant determines what
|
/// too fast, we'd skip important physics events like collisions. This constant determines the
|
||||||
/// the upper limit is. If delta time exceeds this value, the game's physics will begin to produce
|
/// upper limit. If delta time exceeds this value, the game's physics will begin to produce time
|
||||||
/// time lag. Ideally, we'd avoid such a situation.
|
/// lag. Ideally, we'd avoid such a situation.
|
||||||
const MAX_DELTA_TIME: f32 = 0.15;
|
const MAX_DELTA_TIME: f32 = 0.15;
|
||||||
|
|
||||||
pub struct Changes {
|
pub struct Changes {
|
||||||
@ -64,7 +64,7 @@ impl Changes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A type used to represent game state stored on both the client and the server. This includes
|
/// A type used to represent game state stored on both the client and the server. This includes
|
||||||
/// things like entity components, terrain data, and global state like weather, time of day, etc.
|
/// things like entity components, terrain data, and global states like weather, time of day, etc.
|
||||||
pub struct State {
|
pub struct State {
|
||||||
ecs: sphynx::World<EcsCompPacket, EcsResPacket>,
|
ecs: sphynx::World<EcsCompPacket, EcsResPacket>,
|
||||||
// Avoid lifetime annotation by storing a thread pool instead of the whole dispatcher
|
// Avoid lifetime annotation by storing a thread pool instead of the whole dispatcher
|
||||||
@ -82,7 +82,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `State` from an ECS state package
|
/// Create a new `State` from an ECS state package.
|
||||||
pub fn from_state_package(
|
pub fn from_state_package(
|
||||||
state_package: sphynx::StatePackage<EcsCompPacket, EcsResPacket>,
|
state_package: sphynx::StatePackage<EcsCompPacket, EcsResPacket>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -97,14 +97,14 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new Sphynx ECS world
|
// Create a new Sphynx ECS world.
|
||||||
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsCompPacket, EcsResPacket>) {
|
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsCompPacket, EcsResPacket>) {
|
||||||
// Register synced components
|
// Register synced components.
|
||||||
ecs.register_synced::<comp::Actor>();
|
ecs.register_synced::<comp::Actor>();
|
||||||
ecs.register_synced::<comp::Player>();
|
ecs.register_synced::<comp::Player>();
|
||||||
ecs.register_synced::<comp::Stats>();
|
ecs.register_synced::<comp::Stats>();
|
||||||
|
|
||||||
// Register unsynched (or synced by other means) components
|
// Register unsynced (or synced by other means) components.
|
||||||
ecs.register::<comp::phys::Pos>();
|
ecs.register::<comp::phys::Pos>();
|
||||||
ecs.register::<comp::phys::Vel>();
|
ecs.register::<comp::phys::Vel>();
|
||||||
ecs.register::<comp::phys::Dir>();
|
ecs.register::<comp::phys::Dir>();
|
||||||
@ -112,16 +112,16 @@ impl State {
|
|||||||
ecs.register::<comp::Agent>();
|
ecs.register::<comp::Agent>();
|
||||||
ecs.register::<comp::Control>();
|
ecs.register::<comp::Control>();
|
||||||
|
|
||||||
// Register synced resources used by the ECS
|
// Register synced resources used by the ECS.
|
||||||
ecs.add_resource_synced(TimeOfDay(0.0));
|
ecs.add_resource_synced(TimeOfDay(0.0));
|
||||||
|
|
||||||
// Register unsynced resources used by the ECS
|
// Register unsynced resources used by the ECS.
|
||||||
ecs.add_resource(Time(0.0));
|
ecs.add_resource(Time(0.0));
|
||||||
ecs.add_resource(DeltaTime(0.0));
|
ecs.add_resource(DeltaTime(0.0));
|
||||||
ecs.add_resource(TerrainMap::new().unwrap());
|
ecs.add_resource(TerrainMap::new().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a component with the state's ECS
|
/// Register a component with the state's ECS.
|
||||||
pub fn with_component<T: Component>(mut self) -> Self
|
pub fn with_component<T: Component>(mut self) -> Self
|
||||||
where
|
where
|
||||||
<T as Component>::Storage: Default,
|
<T as Component>::Storage: Default,
|
||||||
@ -130,27 +130,27 @@ impl State {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a component attributed to a particular entity
|
/// Write a component attributed to a particular entity.
|
||||||
pub fn write_component<C: Component>(&mut self, entity: EcsEntity, comp: C) {
|
pub fn write_component<C: Component>(&mut self, entity: EcsEntity, comp: C) {
|
||||||
let _ = self.ecs.write_storage().insert(entity, comp);
|
let _ = self.ecs.write_storage().insert(entity, comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a component attributed to a particular entity
|
/// Read a component attributed to a particular entity.
|
||||||
pub fn read_component_cloned<C: Component + Clone>(&self, entity: EcsEntity) -> Option<C> {
|
pub fn read_component_cloned<C: Component + Clone>(&self, entity: EcsEntity) -> Option<C> {
|
||||||
self.ecs.read_storage().get(entity).cloned()
|
self.ecs.read_storage().get(entity).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a read-only reference to the storage of a particular component type
|
/// Get a read-only reference to the storage of a particular component type.
|
||||||
pub fn read_storage<C: Component>(&self) -> EcsStorage<C, Fetch<EcsMaskedStorage<C>>> {
|
pub fn read_storage<C: Component>(&self) -> EcsStorage<C, Fetch<EcsMaskedStorage<C>>> {
|
||||||
self.ecs.read_storage::<C>()
|
self.ecs.read_storage::<C>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the internal ECS world
|
/// Get a reference to the internal ECS world.
|
||||||
pub fn ecs(&self) -> &sphynx::World<EcsCompPacket, EcsResPacket> {
|
pub fn ecs(&self) -> &sphynx::World<EcsCompPacket, EcsResPacket> {
|
||||||
&self.ecs
|
&self.ecs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to the internal ECS world
|
/// Get a mutable reference to the internal ECS world.
|
||||||
pub fn ecs_mut(&mut self) -> &mut sphynx::World<EcsCompPacket, EcsResPacket> {
|
pub fn ecs_mut(&mut self) -> &mut sphynx::World<EcsCompPacket, EcsResPacket> {
|
||||||
&mut self.ecs
|
&mut self.ecs
|
||||||
}
|
}
|
||||||
@ -194,7 +194,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the chunk with the given key from this state's terrain, if it exists
|
/// Remove the chunk with the given key from this state's terrain, if it exists.
|
||||||
pub fn remove_chunk(&mut self, key: Vec2<i32>) {
|
pub fn remove_chunk(&mut self, key: Vec2<i32>) {
|
||||||
if self
|
if self
|
||||||
.ecs
|
.ecs
|
||||||
@ -208,27 +208,27 @@ impl State {
|
|||||||
|
|
||||||
/// Execute a single tick, simulating the game state by the given duration.
|
/// Execute a single tick, simulating the game state by the given duration.
|
||||||
pub fn tick(&mut self, dt: Duration) {
|
pub fn tick(&mut self, dt: Duration) {
|
||||||
// Change the time accordingly
|
// Change the time accordingly.
|
||||||
self.ecs.write_resource::<TimeOfDay>().0 += dt.as_secs_f64() * DAY_CYCLE_FACTOR;
|
self.ecs.write_resource::<TimeOfDay>().0 += dt.as_secs_f64() * DAY_CYCLE_FACTOR;
|
||||||
self.ecs.write_resource::<Time>().0 += dt.as_secs_f64();
|
self.ecs.write_resource::<Time>().0 += dt.as_secs_f64();
|
||||||
|
|
||||||
// Update delta time
|
// Update delta time.
|
||||||
// Above a delta time of MAX_DELTA_TIME, start lagging to avoid skipping important physics events
|
// Beyond a delta time of MAX_DELTA_TIME, start lagging to avoid skipping important physics events.
|
||||||
self.ecs.write_resource::<DeltaTime>().0 = dt.as_secs_f32().min(MAX_DELTA_TIME);
|
self.ecs.write_resource::<DeltaTime>().0 = dt.as_secs_f32().min(MAX_DELTA_TIME);
|
||||||
|
|
||||||
// Run systems to update the world
|
// Run systems to update the world.
|
||||||
// Create and run dispatcher for ecs systems
|
// Create and run a dispatcher for ecs systems.
|
||||||
let mut dispatch_builder = DispatcherBuilder::new().with_pool(self.thread_pool.clone());
|
let mut dispatch_builder = DispatcherBuilder::new().with_pool(self.thread_pool.clone());
|
||||||
sys::add_local_systems(&mut dispatch_builder);
|
sys::add_local_systems(&mut dispatch_builder);
|
||||||
// This dispatches all the systems in parallel
|
// This dispatches all the systems in parallel.
|
||||||
dispatch_builder.build().dispatch(&self.ecs.res);
|
dispatch_builder.build().dispatch(&self.ecs.res);
|
||||||
|
|
||||||
self.ecs.maintain();
|
self.ecs.maintain();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clean up the state after a tick
|
/// Clean up the state after a tick.
|
||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
// Clean up data structures from the last tick
|
// Clean up data structures from the last tick.
|
||||||
self.changes.cleanup();
|
self.changes.cleanup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
Some(tgt_pos) => {
|
Some(tgt_pos) => {
|
||||||
let tgt_pos = tgt_pos.0 + *offset;
|
let tgt_pos = tgt_pos.0 + *offset;
|
||||||
|
|
||||||
// Jump with target
|
// Jump with target.
|
||||||
control.jumping = tgt_pos.z > pos.0.z + 1.0;
|
control.jumping = tgt_pos.z > pos.0.z + 1.0;
|
||||||
|
|
||||||
// Move towards the target
|
// Move towards the target.
|
||||||
let dist = tgt_pos.distance(pos.0);
|
let dist = tgt_pos.distance(pos.0);
|
||||||
control.move_dir = if dist > 5.0 {
|
control.move_dir = if dist > 5.0 {
|
||||||
Vec2::from(tgt_pos - pos.0).normalized()
|
Vec2::from(tgt_pos - pos.0).normalized()
|
||||||
@ -50,7 +50,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
_ => control.move_dir = Vec2::zero(),
|
_ => control.move_dir = Vec2::zero(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change offset occasionally
|
// Change offset occasionally.
|
||||||
if rand::random::<f32>() < 0.003 {
|
if rand::random::<f32>() < 0.003 {
|
||||||
*offset =
|
*offset =
|
||||||
Vec2::new(rand::random::<f32>() - 0.5, rand::random::<f32>() - 0.5)
|
Vec2::new(rand::random::<f32>() - 0.5, rand::random::<f32>() - 0.5)
|
||||||
|
@ -42,8 +42,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
&& vel.0.z <= 0.0;
|
&& vel.0.z <= 0.0;
|
||||||
|
|
||||||
let (gliding, friction) = if on_ground {
|
let (gliding, friction) = if on_ground {
|
||||||
// TODO: Don't hard-code this
|
// TODO: Don't hard-code this.
|
||||||
// Apply physics to the player: acceleration and non-linear decceleration
|
// Apply physics to the player: acceleration and non-linear deceleration.
|
||||||
vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 200.0;
|
vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 200.0;
|
||||||
|
|
||||||
if control.jumping {
|
if control.jumping {
|
||||||
@ -52,12 +52,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
(false, 0.15)
|
(false, 0.15)
|
||||||
} else {
|
} else {
|
||||||
// TODO: Don't hard-code this
|
// TODO: Don't hard-code this.
|
||||||
// Apply physics to the player: acceleration and non-linear decceleration
|
// Apply physics to the player: acceleration and non-linear deceleration.
|
||||||
vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 10.0;
|
vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 10.0;
|
||||||
|
|
||||||
if control.gliding && vel.0.z < 0.0 {
|
if control.gliding && vel.0.z < 0.0 {
|
||||||
// TODO: Don't hard-code this
|
// TODO: Don't hard-code this.
|
||||||
let anti_grav = 9.81 * 3.95 + vel.0.z.powf(2.0) * 0.2;
|
let anti_grav = 9.81 * 3.95 + vel.0.z.powf(2.0) * 0.2;
|
||||||
vel.0.z +=
|
vel.0.z +=
|
||||||
dt.0 * anti_grav * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
|
dt.0 * anti_grav * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
|
||||||
|
@ -28,8 +28,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Movement
|
// Movement
|
||||||
pos.0 += vel.0 * dt.0;
|
pos.0 += vel.0 * dt.0;
|
||||||
|
|
||||||
// Don't fall into the void
|
// Don't fall into the void.
|
||||||
// TODO: This shouldn't be needed when we have proper physics and chunk loading
|
// TODO: This shouldn't be needed when we have proper physics and chunk loading.
|
||||||
if pos.0.z < 0.0 {
|
if pos.0.z < 0.0 {
|
||||||
pos.0.z = 0.0;
|
pos.0.z = 0.0;
|
||||||
vel.0.z = 0.0;
|
vel.0.z = 0.0;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::ray::{Ray, RayUntil};
|
use crate::ray::{Ray, RayUntil};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
/// A voxel
|
/// A voxel.
|
||||||
pub trait Vox {
|
pub trait Vox {
|
||||||
fn empty() -> Self;
|
fn empty() -> Self;
|
||||||
fn is_empty(&self) -> bool;
|
fn is_empty(&self) -> bool;
|
||||||
@ -50,7 +50,7 @@ pub trait SizedVol: BaseVol {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_size(&self) -> Vec3<u32>;
|
fn get_size(&self) -> Vec3<u32>;
|
||||||
|
|
||||||
/// Iterate through all potential voxel positions in this volume
|
/// Iterate through all potential voxel positions in this volume.
|
||||||
fn iter_positions(&self) -> VoxPosIter {
|
fn iter_positions(&self) -> VoxPosIter {
|
||||||
VoxPosIter {
|
VoxPosIter {
|
||||||
pos: Vec3::zero(),
|
pos: Vec3::zero(),
|
||||||
@ -73,7 +73,7 @@ pub trait ReadVol: BaseVol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A volume that provides the ability to sample (i.e: clone a section of) its voxel data.
|
/// A volume that provides the ability to sample (i.e., clone a section of) its voxel data.
|
||||||
pub trait SampleVol<I>: BaseVol {
|
pub trait SampleVol<I>: BaseVol {
|
||||||
type Sample: BaseVol + ReadVol;
|
type Sample: BaseVol + ReadVol;
|
||||||
/// Take a sample of the volume by cloning voxels within the provided range.
|
/// Take a sample of the volume by cloning voxels within the provided range.
|
||||||
|
@ -25,8 +25,8 @@ pub struct Chunk<V: Vox, S: VolSize, M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
||||||
/// Used to transform a voxel position in the volume into its corresponding index in the voxel
|
/// Used to transform a voxel position in the volume into its corresponding index
|
||||||
// array.
|
/// in the voxel array.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn idx_for(pos: Vec3<i32>) -> Option<usize> {
|
fn idx_for(pos: Vec3<i32>) -> Option<usize> {
|
||||||
if pos.map(|e| e >= 0).reduce_and()
|
if pos.map(|e| e >= 0).reduce_and()
|
||||||
|
@ -20,8 +20,8 @@ pub struct Dyna<V: Vox, M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Vox, M> Dyna<V, M> {
|
impl<V: Vox, M> Dyna<V, M> {
|
||||||
/// Used to transform a voxel position in the volume into its corresponding index in the voxel
|
/// Used to transform a voxel position in the volume into its corresponding index
|
||||||
// array.
|
/// in the voxel array.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn idx_for(sz: Vec3<u32>, pos: Vec3<i32>) -> Option<usize> {
|
fn idx_for(sz: Vec3<u32>, pos: Vec3<i32>) -> Option<usize> {
|
||||||
if pos.map(|e| e >= 0).reduce_and() && pos.map2(sz, |e, lim| e < lim as i32).reduce_and() {
|
if pos.map(|e| e >= 0).reduce_and() && pos.map2(sz, |e, lim| e < lim as i32).reduce_and() {
|
||||||
|
0
common/src/volumes/vol_map.rs
Normal file
0
common/src/volumes/vol_map.rs
Normal file
@ -15,7 +15,7 @@ fn main() {
|
|||||||
let mut clock = Clock::new();
|
let mut clock = Clock::new();
|
||||||
|
|
||||||
// Create server
|
// Create server
|
||||||
let mut server = Server::new().expect("Failed to create server instance");
|
let mut server = Server::new().expect("Failed to create server instance!");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let events = server
|
let events = server
|
||||||
@ -30,10 +30,10 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the server after a tick
|
// Clean up the server after a tick.
|
||||||
server.cleanup();
|
server.cleanup();
|
||||||
|
|
||||||
// Wait for the next tick
|
// Wait for the next tick.
|
||||||
clock.tick(Duration::from_millis(1000 / TPS));
|
clock.tick(Duration::from_millis(1000 / TPS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! # Implementing new commands
|
//! # Implementing new commands.
|
||||||
//! To implement a new command, add an instance of `ChatCommand` to `CHAT_COMMANDS`
|
//! To implement a new command, add an instance of `ChatCommand` to `CHAT_COMMANDS`
|
||||||
//! and provide a handler function.
|
//! and provide a handler function.
|
||||||
|
|
||||||
@ -9,26 +9,26 @@ use vek::*;
|
|||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use scan_fmt::scan_fmt;
|
use scan_fmt::scan_fmt;
|
||||||
/// Struct representing a command that a user can run from server chat
|
/// Struct representing a command that a user can run from server chat.
|
||||||
pub struct ChatCommand {
|
pub struct ChatCommand {
|
||||||
/// The keyword used to invoke the command, omitting the leading '/'
|
/// The keyword used to invoke the command, omitting the leading '/'.
|
||||||
pub keyword: &'static str,
|
pub keyword: &'static str,
|
||||||
/// A format string for parsing arguments
|
/// A format string for parsing arguments.
|
||||||
arg_fmt: &'static str,
|
arg_fmt: &'static str,
|
||||||
/// message to explain how the command is used
|
/// A message that explains how the command is used.
|
||||||
help_string: &'static str,
|
help_string: &'static str,
|
||||||
/// Handler function called when the command is executed
|
/// Handler function called when the command is executed.
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `&mut Server` - the `Server` instance executing the command
|
/// * `&mut Server` - the `Server` instance executing the command.
|
||||||
/// * `EcsEntity` - an `Entity` corresponding to the player that invoked the command
|
/// * `EcsEntity` - an `Entity` corresponding to the player that invoked the command.
|
||||||
/// * `String` - a `String` containing the part of the command after the keyword
|
/// * `String` - a `String` containing the part of the command after the keyword.
|
||||||
/// * `&ChatCommand` - the command to execute with the above arguments
|
/// * `&ChatCommand` - the command to execute with the above arguments.
|
||||||
/// Handler functions must parse arguments from the the given `String` (`scan_fmt!` is included for this purpose)
|
/// Handler functions must parse arguments from the the given `String` (`scan_fmt!` is included for this purpose).
|
||||||
handler: fn(&mut Server, EcsEntity, String, &ChatCommand),
|
handler: fn(&mut Server, EcsEntity, String, &ChatCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChatCommand {
|
impl ChatCommand {
|
||||||
/// Creates a new chat command
|
/// Creates a new chat command.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
keyword: &'static str,
|
keyword: &'static str,
|
||||||
arg_fmt: &'static str,
|
arg_fmt: &'static str,
|
||||||
@ -42,14 +42,14 @@ impl ChatCommand {
|
|||||||
handler,
|
handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Calls the contained handler function, passing `&self` as the last argument
|
/// Calls the contained handler function, passing `&self` as the last argument.
|
||||||
pub fn execute(&self, server: &mut Server, entity: EcsEntity, args: String) {
|
pub fn execute(&self, server: &mut Server, entity: EcsEntity, args: String) {
|
||||||
(self.handler)(server, entity, args, self);
|
(self.handler)(server, entity, args, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Static list of chat commands available to the server
|
/// Static list of chat commands available to the server.
|
||||||
pub static ref CHAT_COMMANDS: Vec<ChatCommand> = vec![
|
pub static ref CHAT_COMMANDS: Vec<ChatCommand> = vec![
|
||||||
ChatCommand::new(
|
ChatCommand::new(
|
||||||
"jump",
|
"jump",
|
||||||
@ -81,7 +81,8 @@ lazy_static! {
|
|||||||
"/pet : Spawn a test pet NPC",
|
"/pet : Spawn a test pet NPC",
|
||||||
handle_pet
|
handle_pet
|
||||||
),
|
),
|
||||||
ChatCommand::new("help", "", "/help: Display this message", handle_help)
|
ChatCommand::new(
|
||||||
|
"help", "", "/help: Display this message", handle_help)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
|
|||||||
}
|
}
|
||||||
None => server.clients.notify(
|
None => server.clients.notify(
|
||||||
entity,
|
entity,
|
||||||
ServerMsg::Chat(String::from("Command 'jump' invalid in current state")),
|
ServerMsg::Chat(String::from("Command 'jump' invalid in current state.")),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,7 +166,7 @@ fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &Chat
|
|||||||
}
|
}
|
||||||
None => server.clients.notify(
|
None => server.clients.notify(
|
||||||
entity,
|
entity,
|
||||||
ServerMsg::Chat(format!("Unable to teleport to player '{}'", alias)),
|
ServerMsg::Chat(format!("Unable to teleport to player '{}'!", alias)),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
pub struct Input {
|
pub struct Input {
|
||||||
// TODO: Use this type to manage server input
|
// TODO: Use this type to manage server input.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Input {
|
impl Default for Input {
|
||||||
|
@ -56,7 +56,7 @@ impl Server {
|
|||||||
Self::bind(SocketAddr::from(([0; 4], 59003)))
|
Self::bind(SocketAddr::from(([0; 4], 59003)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new server bound to the given socket
|
/// Create a new server bound to the given socket.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn bind<A: Into<SocketAddr>>(addrs: A) -> Result<Self, Error> {
|
pub fn bind<A: Into<SocketAddr>>(addrs: A) -> Result<Self, Error> {
|
||||||
let (chunk_tx, chunk_rx) = mpsc::channel();
|
let (chunk_tx, chunk_rx) = mpsc::channel();
|
||||||
@ -114,7 +114,7 @@ impl Server {
|
|||||||
&mut self.world
|
&mut self.world
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a non-player character
|
/// Build a non-player character.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn create_npc(&mut self, name: String, body: comp::Body) -> EcsEntityBuilder {
|
pub fn create_npc(&mut self, name: String, body: comp::Body) -> EcsEntityBuilder {
|
||||||
self.state
|
self.state
|
||||||
@ -140,18 +140,18 @@ impl Server {
|
|||||||
state.write_component(entity, comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0)));
|
state.write_component(entity, comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0)));
|
||||||
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
|
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
|
||||||
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
|
state.write_component(entity, comp::phys::Dir(Vec3::unit_y()));
|
||||||
// Make sure everything is accepted
|
// Make sure everything is accepted.
|
||||||
state.write_component(entity, comp::phys::ForceUpdate);
|
state.write_component(entity, comp::phys::ForceUpdate);
|
||||||
|
|
||||||
// Set initial animation
|
// Set initial animation.
|
||||||
state.write_component(entity, comp::AnimationHistory::new(comp::Animation::Idle));
|
state.write_component(entity, comp::AnimationHistory::new(comp::Animation::Idle));
|
||||||
|
|
||||||
// Tell the client his request was successful
|
// Tell the client its request was successful.
|
||||||
client.notify(ServerMsg::StateAnswer(Ok(ClientState::Character)));
|
client.notify(ServerMsg::StateAnswer(Ok(ClientState::Character)));
|
||||||
client.client_state = ClientState::Character;
|
client.client_state = ClientState::Character;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a single server tick, handle input and update the game state by the given duration
|
/// Execute a single server tick, handle input and update the game state by the given duration.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn tick(&mut self, input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
|
pub fn tick(&mut self, input: Input, dt: Duration) -> Result<Vec<Event>, Error> {
|
||||||
// This tick function is the centre of the Veloren universe. Most server-side things are
|
// This tick function is the centre of the Veloren universe. Most server-side things are
|
||||||
@ -168,27 +168,27 @@ impl Server {
|
|||||||
// 6) Send relevant state updates to all clients
|
// 6) Send relevant state updates to all clients
|
||||||
// 7) Finish the tick, passing control of the main thread back to the frontend
|
// 7) Finish the tick, passing control of the main thread back to the frontend
|
||||||
|
|
||||||
// Build up a list of events for this frame, to be passed to the frontend
|
// Build up a list of events for this frame, to be passed to the frontend.
|
||||||
let mut frontend_events = Vec::new();
|
let mut frontend_events = Vec::new();
|
||||||
|
|
||||||
// If networking has problems, handle them
|
// If networking has problems, handle them.
|
||||||
if let Some(err) = self.postoffice.error() {
|
if let Some(err) = self.postoffice.error() {
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle new client connections (step 2)
|
// Handle new client connections (step 2).
|
||||||
frontend_events.append(&mut self.handle_new_connections()?);
|
frontend_events.append(&mut self.handle_new_connections()?);
|
||||||
|
|
||||||
// Handle new messages from clients
|
// Handle new messages from clients
|
||||||
frontend_events.append(&mut self.handle_new_messages()?);
|
frontend_events.append(&mut self.handle_new_messages()?);
|
||||||
|
|
||||||
// Tick the client's LocalState (step 3)
|
// Tick the client's LocalState (step 3).
|
||||||
self.state.tick(dt);
|
self.state.tick(dt);
|
||||||
|
|
||||||
// Fetch any generated `TerrainChunk`s and insert them into the terrain
|
// Fetch any generated `TerrainChunk`s and insert them into the terrain.
|
||||||
// Also, send the chunk data to anybody that is close by
|
// Also, send the chunk data to anybody that is close by.
|
||||||
if let Ok((key, chunk)) = self.chunk_rx.try_recv() {
|
if let Ok((key, chunk)) = self.chunk_rx.try_recv() {
|
||||||
// Send the chunk to all nearby players
|
// Send the chunk to all nearby players.
|
||||||
for (entity, player, pos) in (
|
for (entity, player, pos) in (
|
||||||
&self.state.ecs().entities(),
|
&self.state.ecs().entities(),
|
||||||
&self.state.ecs().read_storage::<comp::Player>(),
|
&self.state.ecs().read_storage::<comp::Player>(),
|
||||||
@ -216,12 +216,12 @@ impl Server {
|
|||||||
self.pending_chunks.remove(&key);
|
self.pending_chunks.remove(&key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove chunks that are too far from players
|
// Remove chunks that are too far from players.
|
||||||
let mut chunks_to_remove = Vec::new();
|
let mut chunks_to_remove = Vec::new();
|
||||||
self.state.terrain().iter().for_each(|(key, _)| {
|
self.state.terrain().iter().for_each(|(key, _)| {
|
||||||
let mut min_dist = i32::MAX;
|
let mut min_dist = i32::MAX;
|
||||||
|
|
||||||
// For each player with a position, calculate the distance
|
// For each player with a position, calculate the distance.
|
||||||
for (_, pos) in (
|
for (_, pos) in (
|
||||||
&self.state.ecs().read_storage::<comp::Player>(),
|
&self.state.ecs().read_storage::<comp::Player>(),
|
||||||
&self.state.ecs().read_storage::<comp::phys::Pos>(),
|
&self.state.ecs().read_storage::<comp::phys::Pos>(),
|
||||||
@ -243,21 +243,21 @@ impl Server {
|
|||||||
self.state.remove_chunk(key);
|
self.state.remove_chunk(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synchronise clients with the new state of the world
|
// Synchronise clients with the new state of the world.
|
||||||
self.sync_clients();
|
self.sync_clients();
|
||||||
|
|
||||||
// Finish the tick, pass control back to the frontend (step 6)
|
// Finish the tick, pass control back to the frontend (step 6).
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clean up the server after a tick
|
/// Clean up the server after a tick.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
// Cleanup the local state
|
// Cleanup the local state
|
||||||
self.state.cleanup();
|
self.state.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle new client connections
|
/// Handle new client connections.
|
||||||
fn handle_new_connections(&mut self) -> Result<Vec<Event>, Error> {
|
fn handle_new_connections(&mut self) -> Result<Vec<Event>, Error> {
|
||||||
let mut frontend_events = Vec::new();
|
let mut frontend_events = Vec::new();
|
||||||
|
|
||||||
@ -269,11 +269,10 @@ impl Server {
|
|||||||
last_ping: self.state.get_time(),
|
last_ping: self.state.get_time(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return the state of the current world
|
// Return the state of the current world (all of the components that Sphynx tracks).
|
||||||
// (All components Sphynx tracks)
|
|
||||||
client.notify(ServerMsg::InitialSync {
|
client.notify(ServerMsg::InitialSync {
|
||||||
ecs_state: self.state.ecs().gen_state_package(),
|
ecs_state: self.state.ecs().gen_state_package(),
|
||||||
entity_uid: self.state.ecs().uid_from_entity(entity).unwrap().into(), // Can't fail
|
entity_uid: self.state.ecs().uid_from_entity(entity).unwrap().into(), // Can't fail.
|
||||||
});
|
});
|
||||||
|
|
||||||
self.clients.add(entity, client);
|
self.clients.add(entity, client);
|
||||||
@ -284,7 +283,7 @@ impl Server {
|
|||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle new client messages
|
/// Handle new client messages.
|
||||||
fn handle_new_messages(&mut self) -> Result<Vec<Event>, Error> {
|
fn handle_new_messages(&mut self) -> Result<Vec<Event>, Error> {
|
||||||
let mut frontend_events = Vec::new();
|
let mut frontend_events = Vec::new();
|
||||||
|
|
||||||
@ -297,17 +296,17 @@ impl Server {
|
|||||||
let mut disconnect = false;
|
let mut disconnect = false;
|
||||||
let new_msgs = client.postbox.new_messages();
|
let new_msgs = client.postbox.new_messages();
|
||||||
|
|
||||||
// Update client ping
|
// Update client ping.
|
||||||
if new_msgs.len() > 0 {
|
if new_msgs.len() > 0 {
|
||||||
client.last_ping = state.get_time();
|
client.last_ping = state.get_time();
|
||||||
|
|
||||||
// Process incoming messages
|
// Process incoming messages.
|
||||||
for msg in new_msgs {
|
for msg in new_msgs {
|
||||||
match msg {
|
match msg {
|
||||||
ClientMsg::RequestState(requested_state) => match requested_state {
|
ClientMsg::RequestState(requested_state) => match requested_state {
|
||||||
ClientState::Connected => disconnect = true, // Default state
|
ClientState::Connected => disconnect = true, // Default state
|
||||||
ClientState::Registered => match client.client_state {
|
ClientState::Registered => match client.client_state {
|
||||||
// Use ClientMsg::Register instead
|
// Use ClientMsg::Register instead.
|
||||||
ClientState::Connected => {
|
ClientState::Connected => {
|
||||||
client.error_state(RequestStateError::WrongMessage)
|
client.error_state(RequestStateError::WrongMessage)
|
||||||
}
|
}
|
||||||
@ -319,7 +318,7 @@ impl Server {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientState::Spectator => match requested_state {
|
ClientState::Spectator => match requested_state {
|
||||||
// Become Registered first
|
// Become Registered first.
|
||||||
ClientState::Connected => {
|
ClientState::Connected => {
|
||||||
client.error_state(RequestStateError::Impossible)
|
client.error_state(RequestStateError::Impossible)
|
||||||
}
|
}
|
||||||
@ -330,7 +329,7 @@ impl Server {
|
|||||||
client.allow_state(ClientState::Spectator)
|
client.allow_state(ClientState::Spectator)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Use ClientMsg::Character instead
|
// Use ClientMsg::Character instead.
|
||||||
ClientState::Character => {
|
ClientState::Character => {
|
||||||
client.error_state(RequestStateError::WrongMessage)
|
client.error_state(RequestStateError::WrongMessage)
|
||||||
}
|
}
|
||||||
@ -339,11 +338,11 @@ impl Server {
|
|||||||
ClientState::Connected => {
|
ClientState::Connected => {
|
||||||
Self::initialize_player(state, entity, client, player)
|
Self::initialize_player(state, entity, client, player)
|
||||||
}
|
}
|
||||||
// Use RequestState instead (No need to send `player` again)
|
// Use RequestState instead (No need to send `player` again).
|
||||||
_ => client.error_state(RequestStateError::Impossible),
|
_ => client.error_state(RequestStateError::Impossible),
|
||||||
},
|
},
|
||||||
ClientMsg::Character { name, body } => match client.client_state {
|
ClientMsg::Character { name, body } => match client.client_state {
|
||||||
// Become Registered first
|
// Become Registered first.
|
||||||
ClientState::Connected => {
|
ClientState::Connected => {
|
||||||
client.error_state(RequestStateError::Impossible)
|
client.error_state(RequestStateError::Impossible)
|
||||||
}
|
}
|
||||||
@ -367,7 +366,7 @@ impl Server {
|
|||||||
ClientState::Character => {
|
ClientState::Character => {
|
||||||
state.write_component(entity, animation_history)
|
state.write_component(entity, animation_history)
|
||||||
}
|
}
|
||||||
// Only characters can send animations
|
// Only characters can send animations.
|
||||||
_ => client.error_state(RequestStateError::Impossible),
|
_ => client.error_state(RequestStateError::Impossible),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -377,7 +376,7 @@ impl Server {
|
|||||||
state.write_component(entity, vel);
|
state.write_component(entity, vel);
|
||||||
state.write_component(entity, dir);
|
state.write_component(entity, dir);
|
||||||
}
|
}
|
||||||
// Only characters send their position
|
// Only characters can send positions.
|
||||||
_ => client.error_state(RequestStateError::Impossible),
|
_ => client.error_state(RequestStateError::Impossible),
|
||||||
},
|
},
|
||||||
ClientMsg::TerrainChunkRequest { key } => match client.client_state {
|
ClientMsg::TerrainChunkRequest { key } => match client.client_state {
|
||||||
@ -396,7 +395,7 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Always possible
|
// Always possible.
|
||||||
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
|
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
|
||||||
ClientMsg::Pong => {}
|
ClientMsg::Pong => {}
|
||||||
ClientMsg::Disconnect => disconnect = true,
|
ClientMsg::Disconnect => disconnect = true,
|
||||||
@ -408,7 +407,7 @@ impl Server {
|
|||||||
{
|
{
|
||||||
disconnect = true;
|
disconnect = true;
|
||||||
} else if state.get_time() - client.last_ping > CLIENT_TIMEOUT * 0.5 {
|
} else if state.get_time() - client.last_ping > CLIENT_TIMEOUT * 0.5 {
|
||||||
// Try pinging the client if the timeout is nearing
|
// Try pinging the client if the timeout is nearing.
|
||||||
client.postbox.send_message(ServerMsg::Ping);
|
client.postbox.send_message(ServerMsg::Ping);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,9 +420,9 @@ impl Server {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle new chat messages
|
// Handle new chat messages.
|
||||||
for (entity, msg) in new_chat_msgs {
|
for (entity, msg) in new_chat_msgs {
|
||||||
// Handle chat commands
|
// Handle chat commands.
|
||||||
if msg.starts_with("/") && msg.len() > 1 {
|
if msg.starts_with("/") && msg.len() > 1 {
|
||||||
let argv = String::from(&msg[1..]);
|
let argv = String::from(&msg[1..]);
|
||||||
self.process_chat_cmd(entity, argv);
|
self.process_chat_cmd(entity, argv);
|
||||||
@ -439,14 +438,14 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle client disconnects
|
// Handle client disconnects.
|
||||||
for entity in disconnected_clients {
|
for entity in disconnected_clients {
|
||||||
self.state.ecs_mut().delete_entity_synced(entity);
|
self.state.ecs_mut().delete_entity_synced(entity);
|
||||||
|
|
||||||
frontend_events.push(Event::ClientDisconnected { entity });
|
frontend_events.push(Event::ClientDisconnected { entity });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate requested chunks
|
// Generate requested chunks.
|
||||||
for key in requested_chunks {
|
for key in requested_chunks {
|
||||||
self.generate_chunk(key);
|
self.generate_chunk(key);
|
||||||
}
|
}
|
||||||
@ -454,17 +453,17 @@ impl Server {
|
|||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize a new client states with important information
|
/// Initialize a new client states with important information.
|
||||||
fn initialize_player(
|
fn initialize_player(
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
entity: specs::Entity,
|
entity: specs::Entity,
|
||||||
client: &mut Client,
|
client: &mut Client,
|
||||||
player: comp::Player,
|
player: comp::Player,
|
||||||
) {
|
) {
|
||||||
// Save player metadata (for example the username)
|
// Save player metadata (for example the username).
|
||||||
state.write_component(entity, player);
|
state.write_component(entity, player);
|
||||||
|
|
||||||
// Sync logical information other players have authority over, not the server
|
// Sync logical information other players have authority over, not the server.
|
||||||
for (other_entity, &uid, &animation_history) in (
|
for (other_entity, &uid, &animation_history) in (
|
||||||
&state.ecs().entities(),
|
&state.ecs().entities(),
|
||||||
&state.ecs().read_storage::<common::state::Uid>(),
|
&state.ecs().read_storage::<common::state::Uid>(),
|
||||||
@ -479,17 +478,17 @@ impl Server {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell the client his request was successful
|
// Tell the client its request was successful.
|
||||||
client.allow_state(ClientState::Registered);
|
client.allow_state(ClientState::Registered);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sync client states with the most up to date information
|
/// Sync client states with the most up to date information.
|
||||||
fn sync_clients(&mut self) {
|
fn sync_clients(&mut self) {
|
||||||
// Sync 'logical' state using Sphynx
|
// Sync 'logical' state using Sphynx.
|
||||||
self.clients
|
self.clients
|
||||||
.notify_registered(ServerMsg::EcsSync(self.state.ecs_mut().next_sync_package()));
|
.notify_registered(ServerMsg::EcsSync(self.state.ecs_mut().next_sync_package()));
|
||||||
|
|
||||||
// Sync 'physical' state
|
// Sync 'physical' state.
|
||||||
for (entity, &uid, &pos, &vel, &dir, force_update) in (
|
for (entity, &uid, &pos, &vel, &dir, force_update) in (
|
||||||
&self.state.ecs().entities(),
|
&self.state.ecs().entities(),
|
||||||
&self.state.ecs().read_storage::<Uid>(),
|
&self.state.ecs().read_storage::<Uid>(),
|
||||||
@ -516,7 +515,7 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync animation states
|
// Sync animation states.
|
||||||
for (entity, &uid, &animation_history) in (
|
for (entity, &uid, &animation_history) in (
|
||||||
&self.state.ecs().entities(),
|
&self.state.ecs().entities(),
|
||||||
&self.state.ecs().read_storage::<Uid>(),
|
&self.state.ecs().read_storage::<Uid>(),
|
||||||
@ -524,7 +523,7 @@ impl Server {
|
|||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
// Check if we need to sync
|
// Check if we need to sync.
|
||||||
if Some(animation_history.current) == animation_history.last {
|
if Some(animation_history.current) == animation_history.last {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -538,7 +537,7 @@ impl Server {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update animation last/current state
|
// Update animation last/current state.
|
||||||
for (entity, mut animation_history) in (
|
for (entity, mut animation_history) in (
|
||||||
&self.state.ecs().entities(),
|
&self.state.ecs().entities(),
|
||||||
&mut self.state.ecs().write_storage::<comp::AnimationHistory>(),
|
&mut self.state.ecs().write_storage::<comp::AnimationHistory>(),
|
||||||
@ -548,7 +547,7 @@ impl Server {
|
|||||||
animation_history.last = Some(animation_history.current);
|
animation_history.last = Some(animation_history.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all force flags
|
// Remove all force flags.
|
||||||
self.state
|
self.state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.write_storage::<comp::phys::ForceUpdate>()
|
.write_storage::<comp::phys::ForceUpdate>()
|
||||||
@ -564,18 +563,18 @@ impl Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn process_chat_cmd(&mut self, entity: EcsEntity, cmd: String) {
|
fn process_chat_cmd(&mut self, entity: EcsEntity, cmd: String) {
|
||||||
// separate string into keyword and arguments
|
// Separate string into keyword and arguments.
|
||||||
let sep = cmd.find(' ');
|
let sep = cmd.find(' ');
|
||||||
let (kwd, args) = match sep {
|
let (kwd, args) = match sep {
|
||||||
Some(i) => (cmd[..i].to_string(), cmd[(i + 1)..].to_string()),
|
Some(i) => (cmd[..i].to_string(), cmd[(i + 1)..].to_string()),
|
||||||
None => (cmd, "".to_string()),
|
None => (cmd, "".to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// find command object and run its handler
|
// Find the command object and run its handler.
|
||||||
let action_opt = CHAT_COMMANDS.iter().find(|x| x.keyword == kwd);
|
let action_opt = CHAT_COMMANDS.iter().find(|x| x.keyword == kwd);
|
||||||
match action_opt {
|
match action_opt {
|
||||||
Some(action) => action.execute(self, entity, args),
|
Some(action) => action.execute(self, entity, args),
|
||||||
// unknown command
|
// Unknown command
|
||||||
None => {
|
None => {
|
||||||
self.clients.notify(
|
self.clients.notify(
|
||||||
entity,
|
entity,
|
||||||
|
@ -6,7 +6,6 @@ use vek::*;
|
|||||||
|
|
||||||
// Local
|
// Local
|
||||||
use super::{super::Animation, CharacterSkeleton, SCALE};
|
use super::{super::Animation, CharacterSkeleton, SCALE};
|
||||||
//
|
|
||||||
|
|
||||||
pub struct GlidingAnimation;
|
pub struct GlidingAnimation;
|
||||||
|
|
||||||
@ -21,19 +20,19 @@ impl Animation for GlidingAnimation {
|
|||||||
) -> Self::Skeleton {
|
) -> Self::Skeleton {
|
||||||
let mut next = (*skeleton).clone();
|
let mut next = (*skeleton).clone();
|
||||||
let wave = (anim_time as f32 * 14.0).sin();
|
let wave = (anim_time as f32 * 14.0).sin();
|
||||||
let waveslow = (anim_time as f32 * 7.0).sin();
|
let wave_slow = (anim_time as f32 * 7.0).sin();
|
||||||
let wavecos_slow = (anim_time as f32 * 7.0).cos();
|
let wave_slow_cos = (anim_time as f32 * 7.0).cos();
|
||||||
let arcwave = (1.0f32.ln_1p() - 1.5).abs();
|
let arc_wave = (1.0f32.ln_1p() - 1.5).abs();
|
||||||
let wavetest = (wave.cbrt());
|
let wave_test = (wave.cbrt());
|
||||||
let fuzzwave = (anim_time as f32 * 12.0).sin();
|
let fuzz_wave = (anim_time as f32 * 12.0).sin();
|
||||||
let wavecos = (anim_time as f32 * 14.0).cos();
|
let wave_cos = (anim_time as f32 * 14.0).cos();
|
||||||
let wave_stop = (anim_time as f32 * 1.5).min(PI / 2.0).sin();
|
let wave_stop = (anim_time as f32 * 1.5).min(PI / 2.0).sin();
|
||||||
let wave_stopalt = (anim_time as f32 * 5.0).min(PI / 2.0).sin();
|
let wave_stop_alt = (anim_time as f32 * 5.0).min(PI / 2.0).sin();
|
||||||
let waveveryslow = (anim_time as f32 * 3.0).sin();
|
let wave_very_slow = (anim_time as f32 * 3.0).sin();
|
||||||
let waveveryslowalt = (anim_time as f32 * 2.5).sin();
|
let wave_very_slow_alt = (anim_time as f32 * 2.5).sin();
|
||||||
let waveveryslowcos = (anim_time as f32 * 3.0).cos();
|
let wave_very_slow_cos = (anim_time as f32 * 3.0).cos();
|
||||||
|
|
||||||
let wave_slowtest = (anim_time as f32).min(PI / 2.0).sin();
|
let wave_slow_test = (anim_time as f32).min(PI / 2.0).sin();
|
||||||
|
|
||||||
let head_look = Vec2::new(
|
let head_look = Vec2::new(
|
||||||
((global_time + anim_time) as f32 / 4.0)
|
((global_time + anim_time) as f32 / 4.0)
|
||||||
@ -48,38 +47,48 @@ impl Animation for GlidingAnimation {
|
|||||||
* 0.25,
|
* 0.25,
|
||||||
);
|
);
|
||||||
next.head.offset = Vec3::new(5.5, 2.0, 12.0);
|
next.head.offset = Vec3::new(5.5, 2.0, 12.0);
|
||||||
next.head.ori = Quaternion::rotation_x(0.35 - waveveryslow * 0.10 + head_look.y)
|
next.head.ori = Quaternion::rotation_x(0.35 - wave_very_slow * 0.10 + head_look.y)
|
||||||
* Quaternion::rotation_z(head_look.x + waveveryslowcos * 0.15);
|
* Quaternion::rotation_z(head_look.x + wave_very_slow_cos * 0.15);
|
||||||
next.head.scale = Vec3::one();
|
next.head.scale = Vec3::one();
|
||||||
|
|
||||||
next.chest.offset = Vec3::new(5.5, 0.0, 8.0);
|
next.chest.offset = Vec3::new(5.5, 0.0, 8.0);
|
||||||
next.chest.ori = Quaternion::rotation_z(waveveryslowcos * 0.15);
|
next.chest.ori = Quaternion::rotation_z(wave_very_slow_cos * 0.15);
|
||||||
next.chest.scale = Vec3::one();
|
next.chest.scale = Vec3::one();
|
||||||
|
|
||||||
next.belt.offset = Vec3::new(5.5, 0.0, 6.0);
|
next.belt.offset = Vec3::new(5.5, 0.0, 6.0);
|
||||||
next.belt.ori = Quaternion::rotation_z(waveveryslowcos * 0.20);
|
next.belt.ori = Quaternion::rotation_z(wave_very_slow_cos * 0.20);
|
||||||
next.belt.scale = Vec3::one();
|
next.belt.scale = Vec3::one();
|
||||||
|
|
||||||
next.shorts.offset = Vec3::new(5.5, 0.0, 3.0);
|
next.shorts.offset = Vec3::new(5.5, 0.0, 3.0);
|
||||||
next.shorts.ori = Quaternion::rotation_z(waveveryslowcos * 0.25);
|
next.shorts.ori = Quaternion::rotation_z(wave_very_slow_cos * 0.25);
|
||||||
next.shorts.scale = Vec3::one();
|
next.shorts.scale = Vec3::one();
|
||||||
|
|
||||||
next.l_hand.offset = Vec3::new(-8.0, -10.0 + waveveryslow * 2.5, 18.5 + waveveryslow * 1.0);
|
next.l_hand.offset = Vec3::new(
|
||||||
next.l_hand.ori = Quaternion::rotation_x(0.9 - waveveryslow * 0.10);
|
-8.0,
|
||||||
|
-10.0 + wave_very_slow * 2.5,
|
||||||
|
18.5 + wave_very_slow * 1.0,
|
||||||
|
);
|
||||||
|
next.l_hand.ori = Quaternion::rotation_x(0.9 - wave_very_slow * 0.10);
|
||||||
next.l_hand.scale = Vec3::one();
|
next.l_hand.scale = Vec3::one();
|
||||||
|
|
||||||
next.r_hand.offset = Vec3::new(11.0, -10.0 + waveveryslow * 2.5, 18.5 + waveveryslow * 1.0);
|
next.r_hand.offset = Vec3::new(
|
||||||
next.r_hand.ori = Quaternion::rotation_x(0.9 - waveveryslow * 0.10);
|
11.0,
|
||||||
|
-10.0 + wave_very_slow * 2.5,
|
||||||
|
18.5 + wave_very_slow * 1.0,
|
||||||
|
);
|
||||||
|
next.r_hand.ori = Quaternion::rotation_x(0.9 - wave_very_slow * 0.10);
|
||||||
next.r_hand.scale = Vec3::one();
|
next.r_hand.scale = Vec3::one();
|
||||||
|
|
||||||
next.l_foot.offset = Vec3::new(-3.4, 1.0, 8.0);
|
next.l_foot.offset = Vec3::new(-3.4, 1.0, 8.0);
|
||||||
next.l_foot.ori =
|
next.l_foot.ori = Quaternion::rotation_x(
|
||||||
Quaternion::rotation_x(wave_stop * -0.7 - wavecos_slow * -0.21 + waveveryslow * 0.19);
|
wave_stop * -0.7 - wave_slow_cos * -0.21 + wave_very_slow * 0.19,
|
||||||
|
);
|
||||||
next.l_foot.scale = Vec3::one();
|
next.l_foot.scale = Vec3::one();
|
||||||
|
|
||||||
next.r_foot.offset = Vec3::new(3.4, 1.0, 8.0);
|
next.r_foot.offset = Vec3::new(3.4, 1.0, 8.0);
|
||||||
next.r_foot.ori =
|
next.r_foot.ori = Quaternion::rotation_x(
|
||||||
Quaternion::rotation_x(wave_stop * -0.8 + waveslow * -0.25 + waveveryslowalt * 0.13);
|
wave_stop * -0.8 + wave_slow * -0.25 + wave_very_slow_alt * 0.13,
|
||||||
|
);
|
||||||
next.r_foot.scale = Vec3::one();
|
next.r_foot.scale = Vec3::one();
|
||||||
|
|
||||||
next.weapon.offset = Vec3::new(-5.0, -6.0, 19.0);
|
next.weapon.offset = Vec3::new(-5.0, -6.0, 19.0);
|
||||||
@ -95,11 +104,11 @@ impl Animation for GlidingAnimation {
|
|||||||
next.r_shoulder.scale = Vec3::one();
|
next.r_shoulder.scale = Vec3::one();
|
||||||
|
|
||||||
next.torso.offset = Vec3::new(-0.5, -0.2, 0.0);
|
next.torso.offset = Vec3::new(-0.5, -0.2, 0.0);
|
||||||
next.torso.ori = Quaternion::rotation_x(-0.8 + waveveryslow * 0.10);
|
next.torso.ori = Quaternion::rotation_x(-0.8 + wave_very_slow * 0.10);
|
||||||
next.torso.scale = Vec3::one() / 11.0;
|
next.torso.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.draw.offset = Vec3::new(13.5, 3.0, -1.0);
|
next.draw.offset = Vec3::new(13.5, 3.0, -1.0);
|
||||||
next.draw.ori = Quaternion::rotation_y(waveveryslowcos * 0.05);
|
next.draw.ori = Quaternion::rotation_y(wave_very_slow_cos * 0.05);
|
||||||
next.draw.scale = Vec3::one();
|
next.draw.scale = Vec3::one();
|
||||||
|
|
||||||
next
|
next
|
||||||
|
@ -24,11 +24,11 @@ impl Animation for IdleAnimation {
|
|||||||
let mut next = (*skeleton).clone();
|
let mut next = (*skeleton).clone();
|
||||||
|
|
||||||
let wave = (anim_time as f32 * 12.0).sin();
|
let wave = (anim_time as f32 * 12.0).sin();
|
||||||
let wavecos = (anim_time as f32 * 12.0).cos();
|
let wave_cos = (anim_time as f32 * 12.0).cos();
|
||||||
let wave_slow = (anim_time as f32 * 6.0 + PI).sin();
|
let wave_slow = (anim_time as f32 * 6.0 + PI).sin();
|
||||||
let wavecos_slow = (anim_time as f32 * 6.0 + PI).cos();
|
let wave_slow_cos = (anim_time as f32 * 6.0 + PI).cos();
|
||||||
let waveultra_slow = (anim_time as f32 * 1.0 + PI).sin();
|
let wave_ultra_slow = (anim_time as f32 * 1.0 + PI).sin();
|
||||||
let waveultracos_slow = (anim_time as f32 * 1.0 + PI).cos();
|
let wave_ultra_slow_cos = (anim_time as f32 * 1.0 + PI).cos();
|
||||||
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
||||||
|
|
||||||
let head_look = Vec2::new(
|
let head_look = Vec2::new(
|
||||||
@ -43,37 +43,37 @@ impl Animation for IdleAnimation {
|
|||||||
.sin()
|
.sin()
|
||||||
* 0.25,
|
* 0.25,
|
||||||
);
|
);
|
||||||
next.head.offset = Vec3::new(5.5, 2.0, 11.0 + waveultra_slow * 0.3);
|
next.head.offset = Vec3::new(5.5, 2.0, 11.0 + wave_ultra_slow * 0.3);
|
||||||
next.head.ori = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y);
|
next.head.ori = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y);
|
||||||
next.head.scale = Vec3::one();
|
next.head.scale = Vec3::one();
|
||||||
|
|
||||||
next.chest.offset = Vec3::new(5.5, 0.0, 7.0 + waveultra_slow * 0.3);
|
next.chest.offset = Vec3::new(5.5, 0.0, 7.0 + wave_ultra_slow * 0.3);
|
||||||
next.chest.ori = Quaternion::rotation_x(0.0);
|
next.chest.ori = Quaternion::rotation_x(0.0);
|
||||||
next.chest.scale = Vec3::one();
|
next.chest.scale = Vec3::one();
|
||||||
|
|
||||||
next.belt.offset = Vec3::new(5.5, 0.0, 5.0 + waveultra_slow * 0.3);
|
next.belt.offset = Vec3::new(5.5, 0.0, 5.0 + wave_ultra_slow * 0.3);
|
||||||
next.belt.ori = Quaternion::rotation_x(0.0);
|
next.belt.ori = Quaternion::rotation_x(0.0);
|
||||||
next.belt.scale = Vec3::one();
|
next.belt.scale = Vec3::one();
|
||||||
|
|
||||||
next.shorts.offset = Vec3::new(5.5, 0.0, 2.0 + waveultra_slow * 0.3);
|
next.shorts.offset = Vec3::new(5.5, 0.0, 2.0 + wave_ultra_slow * 0.3);
|
||||||
next.shorts.ori = Quaternion::rotation_x(0.0);
|
next.shorts.ori = Quaternion::rotation_x(0.0);
|
||||||
next.shorts.scale = Vec3::one();
|
next.shorts.scale = Vec3::one();
|
||||||
|
|
||||||
next.l_hand.offset = Vec3::new(
|
next.l_hand.offset = Vec3::new(
|
||||||
-6.0,
|
-6.0,
|
||||||
-2.0 + waveultracos_slow * 0.15,
|
-2.0 + wave_ultra_slow_cos * 0.15,
|
||||||
11.5 + waveultra_slow * 0.5,
|
11.5 + wave_ultra_slow * 0.5,
|
||||||
);
|
);
|
||||||
|
|
||||||
next.l_hand.ori = Quaternion::rotation_x(0.0 + waveultra_slow * 0.06);
|
next.l_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * 0.06);
|
||||||
next.l_hand.scale = Vec3::one();
|
next.l_hand.scale = Vec3::one();
|
||||||
|
|
||||||
next.r_hand.offset = Vec3::new(
|
next.r_hand.offset = Vec3::new(
|
||||||
9.0,
|
9.0,
|
||||||
-2.0 + waveultracos_slow * 0.15,
|
-2.0 + wave_ultra_slow_cos * 0.15,
|
||||||
11.5 + waveultra_slow * 0.5,
|
11.5 + wave_ultra_slow * 0.5,
|
||||||
);
|
);
|
||||||
next.r_hand.ori = Quaternion::rotation_x(0.0 + waveultra_slow * 0.06);
|
next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * 0.06);
|
||||||
next.r_hand.scale = Vec3::one();
|
next.r_hand.scale = Vec3::one();
|
||||||
|
|
||||||
next.l_foot.offset = Vec3::new(-3.3, -0.1, 8.0);
|
next.l_foot.offset = Vec3::new(-3.3, -0.1, 8.0);
|
||||||
|
@ -6,7 +6,6 @@ use vek::*;
|
|||||||
|
|
||||||
// Local
|
// Local
|
||||||
use super::{super::Animation, CharacterSkeleton, SCALE};
|
use super::{super::Animation, CharacterSkeleton, SCALE};
|
||||||
//
|
|
||||||
|
|
||||||
pub struct JumpAnimation;
|
pub struct JumpAnimation;
|
||||||
|
|
||||||
@ -21,16 +20,16 @@ impl Animation for JumpAnimation {
|
|||||||
) -> Self::Skeleton {
|
) -> Self::Skeleton {
|
||||||
let mut next = (*skeleton).clone();
|
let mut next = (*skeleton).clone();
|
||||||
let wave = (anim_time as f32 * 14.0).sin();
|
let wave = (anim_time as f32 * 14.0).sin();
|
||||||
let waveslow = (anim_time as f32 * 7.0).sin();
|
let wave_slow = (anim_time as f32 * 7.0).sin();
|
||||||
let arcwave = (1.0f32.ln_1p() - 1.5).abs();
|
let arc_wave = (1.0f32.ln_1p() - 1.5).abs();
|
||||||
let wavetest = (wave.cbrt());
|
let wave_test = (wave.cbrt());
|
||||||
let fuzzwave = (anim_time as f32 * 12.0).sin();
|
let fuzz_wave = (anim_time as f32 * 12.0).sin();
|
||||||
let wavecos = (anim_time as f32 * 14.0).cos();
|
let wave_cos = (anim_time as f32 * 14.0).cos();
|
||||||
let wave_stop = (anim_time as f32 * 4.5).min(PI / 2.0).sin();
|
let wave_stop = (anim_time as f32 * 4.5).min(PI / 2.0).sin();
|
||||||
let wave_stopalt = (anim_time as f32 * 5.0).min(PI / 2.0).sin();
|
let wave_stop_alt = (anim_time as f32 * 5.0).min(PI / 2.0).sin();
|
||||||
|
|
||||||
let wave_slowtest = (anim_time as f32).min(PI / 2.0).sin();
|
let wave_slow_test = (anim_time as f32).min(PI / 2.0).sin();
|
||||||
let wavecos_slow = (anim_time as f32 * 8.0 + PI).cos();
|
let wave_slow_cos = (anim_time as f32 * 8.0 + PI).cos();
|
||||||
|
|
||||||
next.head.offset = Vec3::new(5.5, 2.0, 12.0);
|
next.head.offset = Vec3::new(5.5, 2.0, 12.0);
|
||||||
next.head.ori = Quaternion::rotation_x(0.25);
|
next.head.ori = Quaternion::rotation_x(0.25);
|
||||||
@ -49,19 +48,19 @@ impl Animation for JumpAnimation {
|
|||||||
next.shorts.scale = Vec3::one();
|
next.shorts.scale = Vec3::one();
|
||||||
|
|
||||||
next.l_hand.offset = Vec3::new(-6.0, 0.0, 12.0);
|
next.l_hand.offset = Vec3::new(-6.0, 0.0, 12.0);
|
||||||
next.l_hand.ori = Quaternion::rotation_x(wave_stopalt * 1.2 - waveslow * 0.15);
|
next.l_hand.ori = Quaternion::rotation_x(wave_stop_alt * 1.2 - wave_slow * 0.15);
|
||||||
next.l_hand.scale = Vec3::one();
|
next.l_hand.scale = Vec3::one();
|
||||||
|
|
||||||
next.r_hand.offset = Vec3::new(9.0, 0.0, 12.0);
|
next.r_hand.offset = Vec3::new(9.0, 0.0, 12.0);
|
||||||
next.r_hand.ori = Quaternion::rotation_x(wave_stopalt * -1.2 + waveslow * 0.15);
|
next.r_hand.ori = Quaternion::rotation_x(wave_stop_alt * -1.2 + wave_slow * 0.15);
|
||||||
next.r_hand.scale = Vec3::one();
|
next.r_hand.scale = Vec3::one();
|
||||||
|
|
||||||
next.l_foot.offset = Vec3::new(-3.4, 1.0, 6.0);
|
next.l_foot.offset = Vec3::new(-3.4, 1.0, 6.0);
|
||||||
next.l_foot.ori = Quaternion::rotation_x(wave_stop * -1.2 - waveslow * 0.2);
|
next.l_foot.ori = Quaternion::rotation_x(wave_stop * -1.2 - wave_slow * 0.2);
|
||||||
next.l_foot.scale = Vec3::one();
|
next.l_foot.scale = Vec3::one();
|
||||||
|
|
||||||
next.r_foot.offset = Vec3::new(3.4, -1.0, 6.0);
|
next.r_foot.offset = Vec3::new(3.4, -1.0, 6.0);
|
||||||
next.r_foot.ori = Quaternion::rotation_x(wave_stop * 1.2 + waveslow * 0.2);
|
next.r_foot.ori = Quaternion::rotation_x(wave_stop * 1.2 + wave_slow * 0.2);
|
||||||
next.r_foot.scale = Vec3::one();
|
next.r_foot.scale = Vec3::one();
|
||||||
|
|
||||||
next.weapon.offset = Vec3::new(-5.0, -6.0, 19.0);
|
next.weapon.offset = Vec3::new(-5.0, -6.0, 19.0);
|
||||||
|
@ -21,35 +21,35 @@ impl Animation for RunAnimation {
|
|||||||
let mut next = (*skeleton).clone();
|
let mut next = (*skeleton).clone();
|
||||||
|
|
||||||
let wave = (anim_time as f32 * 14.0).sin();
|
let wave = (anim_time as f32 * 14.0).sin();
|
||||||
let wavetest = (wave.cbrt());
|
let wave_test = (wave.cbrt());
|
||||||
let fuzzwave = (anim_time as f32 * 12.0).sin();
|
let fuzz_wave = (anim_time as f32 * 12.0).sin();
|
||||||
let wavecos = (anim_time as f32 * 14.0).cos();
|
let wave_cos = (anim_time as f32 * 14.0).cos();
|
||||||
let wave_slow = (anim_time as f32 * 7.0 + PI).sin();
|
let wave_slow = (anim_time as f32 * 7.0 + PI).sin();
|
||||||
let wavecos_slow = (anim_time as f32 * 8.0 + PI).cos();
|
let wave_slow_cos = (anim_time as f32 * 8.0 + PI).cos();
|
||||||
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
||||||
|
|
||||||
next.head.offset = Vec3::new(5.5, 2.0, 11.0 + wavecos * 1.3);
|
next.head.offset = Vec3::new(5.5, 2.0, 11.0 + wave_cos * 1.3);
|
||||||
next.head.ori = Quaternion::rotation_x(0.15);
|
next.head.ori = Quaternion::rotation_x(0.15);
|
||||||
next.head.scale = Vec3::one();
|
next.head.scale = Vec3::one();
|
||||||
|
|
||||||
next.chest.offset = Vec3::new(5.5, 0.0, 7.0 + wavecos * 1.1);
|
next.chest.offset = Vec3::new(5.5, 0.0, 7.0 + wave_cos * 1.1);
|
||||||
next.chest.ori = Quaternion::rotation_z(wave * 0.1);
|
next.chest.ori = Quaternion::rotation_z(wave * 0.1);
|
||||||
next.chest.scale = Vec3::one();
|
next.chest.scale = Vec3::one();
|
||||||
|
|
||||||
next.belt.offset = Vec3::new(5.5, 0.0, 5.0 + wavecos * 1.1);
|
next.belt.offset = Vec3::new(5.5, 0.0, 5.0 + wave_cos * 1.1);
|
||||||
next.belt.ori = Quaternion::rotation_z(wave * 0.25);
|
next.belt.ori = Quaternion::rotation_z(wave * 0.25);
|
||||||
next.belt.scale = Vec3::one();
|
next.belt.scale = Vec3::one();
|
||||||
|
|
||||||
next.shorts.offset = Vec3::new(5.5, 0.0, 2.0 + wavecos * 1.1);
|
next.shorts.offset = Vec3::new(5.5, 0.0, 2.0 + wave_cos * 1.1);
|
||||||
next.shorts.ori = Quaternion::rotation_z(wave * 0.6);
|
next.shorts.ori = Quaternion::rotation_z(wave * 0.6);
|
||||||
next.shorts.scale = Vec3::one();
|
next.shorts.scale = Vec3::one();
|
||||||
|
|
||||||
next.l_hand.offset = Vec3::new(-6.0, 0.0 + wavecos * 2.5, 11.0 - wave * 1.5);
|
next.l_hand.offset = Vec3::new(-6.0, 0.0 + wave_cos * 2.5, 11.0 - wave * 1.5);
|
||||||
next.l_hand.ori = Quaternion::rotation_x(wavecos * 0.9);
|
next.l_hand.ori = Quaternion::rotation_x(wave_cos * 0.9);
|
||||||
next.l_hand.scale = Vec3::one();
|
next.l_hand.scale = Vec3::one();
|
||||||
|
|
||||||
next.r_hand.offset = Vec3::new(9.0, 0.0 - wavecos * 2.5, 11.0 + wave * 1.5);
|
next.r_hand.offset = Vec3::new(9.0, 0.0 - wave_cos * 2.5, 11.0 + wave * 1.5);
|
||||||
next.r_hand.ori = Quaternion::rotation_x(wavecos * -0.9);
|
next.r_hand.ori = Quaternion::rotation_x(wave_cos * -0.9);
|
||||||
next.r_hand.scale = Vec3::one();
|
next.r_hand.scale = Vec3::one();
|
||||||
|
|
||||||
next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave * 1.0, 6.0);
|
next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave * 1.0, 6.0);
|
||||||
@ -73,7 +73,7 @@ impl Animation for RunAnimation {
|
|||||||
next.r_shoulder.scale = Vec3::one();
|
next.r_shoulder.scale = Vec3::one();
|
||||||
|
|
||||||
next.torso.offset = Vec3::new(-0.5, -0.2, 0.4);
|
next.torso.offset = Vec3::new(-0.5, -0.2, 0.4);
|
||||||
next.torso.ori = Quaternion::rotation_x(-velocity * 0.05 - wavecos * 0.1);
|
next.torso.ori = Quaternion::rotation_x(-velocity * 0.05 - wave_cos * 0.1);
|
||||||
next.torso.scale = Vec3::one() / 11.0;
|
next.torso.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.draw.offset = Vec3::new(13.5, 0.0, 0.0);
|
next.draw.offset = Vec3::new(13.5, 0.0, 0.0);
|
||||||
|
@ -28,9 +28,9 @@ impl Bone {
|
|||||||
* Mat4::from(self.ori)
|
* Mat4::from(self.ori)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the current bone to be more like `target`
|
/// Change the current bone to be more like `target`.
|
||||||
fn interpolate(&mut self, target: &Bone) {
|
fn interpolate(&mut self, target: &Bone) {
|
||||||
// TODO: Make configurable
|
// TODO: Make configurable.
|
||||||
let factor = 0.3;
|
let factor = 0.3;
|
||||||
self.offset += (target.offset - self.offset) * factor;
|
self.offset += (target.offset - self.offset) * factor;
|
||||||
self.ori = vek::ops::Slerp::slerp(self.ori, target.ori, factor);
|
self.ori = vek::ops::Slerp::slerp(self.ori, target.ori, factor);
|
||||||
@ -41,7 +41,7 @@ impl Bone {
|
|||||||
pub trait Skeleton: Send + Sync + 'static {
|
pub trait Skeleton: Send + Sync + 'static {
|
||||||
fn compute_matrices(&self) -> [FigureBoneData; 16];
|
fn compute_matrices(&self) -> [FigureBoneData; 16];
|
||||||
|
|
||||||
/// Change the current skeleton to be more like `target`
|
/// Change the current skeleton to be more like `target`.
|
||||||
fn interpolate(&mut self, target: &Self);
|
fn interpolate(&mut self, target: &Self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ pub trait Animation {
|
|||||||
type Skeleton;
|
type Skeleton;
|
||||||
type Dependency;
|
type Dependency;
|
||||||
|
|
||||||
/// Returns a new skeleton that is generated by the animation
|
/// Returns a new skeleton that is generated by the animation.
|
||||||
fn update_skeleton(
|
fn update_skeleton(
|
||||||
skeleton: &Self::Skeleton,
|
skeleton: &Self::Skeleton,
|
||||||
dependency: Self::Dependency,
|
dependency: Self::Dependency,
|
||||||
|
@ -21,16 +21,16 @@ impl Animation for IdleAnimation {
|
|||||||
let mut next = (*skeleton).clone();
|
let mut next = (*skeleton).clone();
|
||||||
|
|
||||||
let wave = (anim_time as f32 * 14.0).sin();
|
let wave = (anim_time as f32 * 14.0).sin();
|
||||||
let wavetest = (wave.cbrt());
|
let wave_test = (wave.cbrt());
|
||||||
let waveultra_slow = (anim_time as f32 * 1.0 + PI).sin();
|
let wave_ultra_slow = (anim_time as f32 * 1.0 + PI).sin();
|
||||||
let waveultracos_slow = (anim_time as f32 * 1.0 + PI).cos();
|
let wave_ultra_slow_cos = (anim_time as f32 * 1.0 + PI).cos();
|
||||||
let fuzzwave = (anim_time as f32 * 12.0).sin();
|
let fuzz_wave = (anim_time as f32 * 12.0).sin();
|
||||||
let wavecos = (anim_time as f32 * 14.0).cos();
|
let wave_cos = (anim_time as f32 * 14.0).cos();
|
||||||
let wave_slow = (anim_time as f32 * 3.5 + PI).sin();
|
let wave_slow = (anim_time as f32 * 3.5 + PI).sin();
|
||||||
let wavecos_slow = (anim_time as f32 * 3.5 + PI).cos();
|
let wave_slow_cos = (anim_time as f32 * 3.5 + PI).cos();
|
||||||
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
||||||
|
|
||||||
let pighead_look = Vec2::new(
|
let pig_head_look = Vec2::new(
|
||||||
((global_time + anim_time) as f32 / 8.0)
|
((global_time + anim_time) as f32 / 8.0)
|
||||||
.floor()
|
.floor()
|
||||||
.mul(7331.0)
|
.mul(7331.0)
|
||||||
@ -43,30 +43,30 @@ impl Animation for IdleAnimation {
|
|||||||
* 0.25,
|
* 0.25,
|
||||||
);
|
);
|
||||||
|
|
||||||
next.pighead.offset = Vec3::new(0.0, -2.0, -1.5 + wave * 0.2) / 11.0;
|
next.pig_head.offset = Vec3::new(0.0, -2.0, -1.5 + wave * 0.2) / 11.0;
|
||||||
next.pighead.ori = Quaternion::rotation_z(pighead_look.x)
|
next.pig_head.ori = Quaternion::rotation_z(pig_head_look.x)
|
||||||
* Quaternion::rotation_x(pighead_look.y + wavecos_slow * 0.03);
|
* Quaternion::rotation_x(pig_head_look.y + wave_slow_cos * 0.03);
|
||||||
next.pighead.scale = Vec3::one() / 10.5;
|
next.pig_head.scale = Vec3::one() / 10.5;
|
||||||
|
|
||||||
next.pigchest.offset = Vec3::new(wave_slow * 0.05, -9.0, 1.5 + wavecos_slow * 0.4) / 11.0;
|
next.pig_chest.offset = Vec3::new(wave_slow * 0.05, -9.0, 1.5 + wave_slow_cos * 0.4) / 11.0;
|
||||||
next.pigchest.ori = Quaternion::rotation_y(wave_slow * 0.05);
|
next.pig_chest.ori = Quaternion::rotation_y(wave_slow * 0.05);
|
||||||
next.pigchest.scale = Vec3::one() / 11.0;
|
next.pig_chest.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.piglf_leg.offset = Vec3::new(-4.5, 2.0, 1.5) / 11.0;
|
next.pig_leg_lf.offset = Vec3::new(-4.5, 2.0, 1.5) / 11.0;
|
||||||
next.piglf_leg.ori = Quaternion::rotation_x(wave_slow * 0.08);
|
next.pig_leg_lf.ori = Quaternion::rotation_x(wave_slow * 0.08);
|
||||||
next.piglf_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_lf.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.pigrf_leg.offset = Vec3::new(2.5, 2.0, 1.5) / 11.0;
|
next.pig_leg_rf.offset = Vec3::new(2.5, 2.0, 1.5) / 11.0;
|
||||||
next.pigrf_leg.ori = Quaternion::rotation_x(wavecos_slow * 0.08);
|
next.pig_leg_rf.ori = Quaternion::rotation_x(wave_slow_cos * 0.08);
|
||||||
next.pigrf_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_rf.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.piglb_leg.offset = Vec3::new(-4.5, -3.0, 1.5) / 11.0;
|
next.pig_leg_lb.offset = Vec3::new(-4.5, -3.0, 1.5) / 11.0;
|
||||||
next.piglb_leg.ori = Quaternion::rotation_x(wavecos_slow * 0.08);
|
next.pig_leg_lb.ori = Quaternion::rotation_x(wave_slow_cos * 0.08);
|
||||||
next.piglb_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_lb.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.pigrb_leg.offset = Vec3::new(2.5, -3.0, 1.5) / 11.0;
|
next.pig_leg_rb.offset = Vec3::new(2.5, -3.0, 1.5) / 11.0;
|
||||||
next.pigrb_leg.ori = Quaternion::rotation_x(wave_slow * 0.08);
|
next.pig_leg_rb.ori = Quaternion::rotation_x(wave_slow * 0.08);
|
||||||
next.pigrb_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_rb.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next
|
next
|
||||||
}
|
}
|
||||||
|
@ -21,37 +21,37 @@ impl Animation for JumpAnimation {
|
|||||||
let mut next = (*skeleton).clone();
|
let mut next = (*skeleton).clone();
|
||||||
|
|
||||||
let wave = (anim_time as f32 * 14.0).sin();
|
let wave = (anim_time as f32 * 14.0).sin();
|
||||||
let wavetest = (wave.cbrt());
|
let wave_test = (wave.cbrt());
|
||||||
let fuzzwave = (anim_time as f32 * 12.0).sin();
|
let fuzz_wave = (anim_time as f32 * 12.0).sin();
|
||||||
let wavecos = (anim_time as f32 * 14.0).cos();
|
let wave_cos = (anim_time as f32 * 14.0).cos();
|
||||||
let wave_slow = (anim_time as f32 * 7.0 + PI).sin();
|
let wave_slow = (anim_time as f32 * 7.0 + PI).sin();
|
||||||
let wavecos_slow = (anim_time as f32 * 8.0 + PI).cos();
|
let wave_slow_cos = (anim_time as f32 * 8.0 + PI).cos();
|
||||||
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
||||||
let wave_stop = (anim_time as f32 * 4.5).min(PI / 2.0).sin();
|
let wave_stop = (anim_time as f32 * 4.5).min(PI / 2.0).sin();
|
||||||
|
|
||||||
next.pighead.offset = Vec3::new(0.0, 0.0, -1.5) / 11.0;
|
next.pig_head.offset = Vec3::new(0.0, 0.0, -1.5) / 11.0;
|
||||||
next.pighead.ori = Quaternion::rotation_x(wave_stop * 0.4);
|
next.pig_head.ori = Quaternion::rotation_x(wave_stop * 0.4);
|
||||||
next.pighead.scale = Vec3::one() / 10.5;
|
next.pig_head.scale = Vec3::one() / 10.5;
|
||||||
|
|
||||||
next.pigchest.offset = Vec3::new(0.0, -9.0, 1.5) / 11.0;
|
next.pig_chest.offset = Vec3::new(0.0, -9.0, 1.5) / 11.0;
|
||||||
next.pigchest.ori = Quaternion::rotation_x(0.0);
|
next.pig_chest.ori = Quaternion::rotation_x(0.0);
|
||||||
next.pigchest.scale = Vec3::one() / 11.0;
|
next.pig_chest.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.piglf_leg.offset = Vec3::new(-4.5, 3.0, 1.5) / 11.0;
|
next.pig_leg_lf.offset = Vec3::new(-4.5, 3.0, 1.5) / 11.0;
|
||||||
next.piglf_leg.ori = Quaternion::rotation_x(wave_stop * 0.6 - wave_slow * 0.3);
|
next.pig_leg_lf.ori = Quaternion::rotation_x(wave_stop * 0.6 - wave_slow * 0.3);
|
||||||
next.piglf_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_lf.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.pigrf_leg.offset = Vec3::new(2.5, 3.0, 1.5) / 11.0;
|
next.pig_leg_rf.offset = Vec3::new(2.5, 3.0, 1.5) / 11.0;
|
||||||
next.pigrf_leg.ori = Quaternion::rotation_x(wave_stop * 0.6 - wave_slow * 0.3);
|
next.pig_leg_rf.ori = Quaternion::rotation_x(wave_stop * 0.6 - wave_slow * 0.3);
|
||||||
next.pigrf_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_rf.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.piglb_leg.offset = Vec3::new(-4.5, -4.0, 2.0) / 11.0;
|
next.pig_leg_lb.offset = Vec3::new(-4.5, -4.0, 2.0) / 11.0;
|
||||||
next.piglb_leg.ori = Quaternion::rotation_x(wave_stop * -0.6 + wave_slow * 0.3);
|
next.pig_leg_lb.ori = Quaternion::rotation_x(wave_stop * -0.6 + wave_slow * 0.3);
|
||||||
next.piglb_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_lb.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.pigrb_leg.offset = Vec3::new(2.5, -4.0, 2.0) / 11.0;
|
next.pig_leg_rb.offset = Vec3::new(2.5, -4.0, 2.0) / 11.0;
|
||||||
next.pigrb_leg.ori = Quaternion::rotation_x(wave_stop * -0.6 + wave_slow * 0.3);
|
next.pig_leg_rb.ori = Quaternion::rotation_x(wave_stop * -0.6 + wave_slow * 0.3);
|
||||||
next.pigrb_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_rb.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next
|
next
|
||||||
}
|
}
|
||||||
|
@ -17,23 +17,23 @@ const SCALE: f32 = 11.0;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct QuadrupedSkeleton {
|
pub struct QuadrupedSkeleton {
|
||||||
pighead: Bone,
|
pig_head: Bone,
|
||||||
pigchest: Bone,
|
pig_chest: Bone,
|
||||||
piglf_leg: Bone,
|
pig_leg_lf: Bone,
|
||||||
pigrf_leg: Bone,
|
pig_leg_rf: Bone,
|
||||||
piglb_leg: Bone,
|
pig_leg_lb: Bone,
|
||||||
pigrb_leg: Bone,
|
pig_leg_rb: Bone,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl QuadrupedSkeleton {
|
impl QuadrupedSkeleton {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
pighead: Bone::default(),
|
pig_head: Bone::default(),
|
||||||
pigchest: Bone::default(),
|
pig_chest: Bone::default(),
|
||||||
piglf_leg: Bone::default(),
|
pig_leg_lf: Bone::default(),
|
||||||
pigrf_leg: Bone::default(),
|
pig_leg_rf: Bone::default(),
|
||||||
piglb_leg: Bone::default(),
|
pig_leg_lb: Bone::default(),
|
||||||
pigrb_leg: Bone::default(),
|
pig_leg_rb: Bone::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,12 +41,12 @@ impl QuadrupedSkeleton {
|
|||||||
impl Skeleton for QuadrupedSkeleton {
|
impl Skeleton for QuadrupedSkeleton {
|
||||||
fn compute_matrices(&self) -> [FigureBoneData; 16] {
|
fn compute_matrices(&self) -> [FigureBoneData; 16] {
|
||||||
[
|
[
|
||||||
FigureBoneData::new(self.pighead.compute_base_matrix()),
|
FigureBoneData::new(self.pig_head.compute_base_matrix()),
|
||||||
FigureBoneData::new(self.pigchest.compute_base_matrix()),
|
FigureBoneData::new(self.pig_chest.compute_base_matrix()),
|
||||||
FigureBoneData::new(self.piglf_leg.compute_base_matrix()),
|
FigureBoneData::new(self.pig_leg_lf.compute_base_matrix()),
|
||||||
FigureBoneData::new(self.pigrf_leg.compute_base_matrix()),
|
FigureBoneData::new(self.pig_leg_rf.compute_base_matrix()),
|
||||||
FigureBoneData::new(self.piglb_leg.compute_base_matrix()),
|
FigureBoneData::new(self.pig_leg_lb.compute_base_matrix()),
|
||||||
FigureBoneData::new(self.pigrb_leg.compute_base_matrix()),
|
FigureBoneData::new(self.pig_leg_rb.compute_base_matrix()),
|
||||||
FigureBoneData::default(),
|
FigureBoneData::default(),
|
||||||
FigureBoneData::default(),
|
FigureBoneData::default(),
|
||||||
FigureBoneData::default(),
|
FigureBoneData::default(),
|
||||||
@ -61,11 +61,11 @@ impl Skeleton for QuadrupedSkeleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn interpolate(&mut self, target: &Self) {
|
fn interpolate(&mut self, target: &Self) {
|
||||||
self.pighead.interpolate(&target.pighead);
|
self.pig_head.interpolate(&target.pig_head);
|
||||||
self.pigchest.interpolate(&target.pigchest);
|
self.pig_chest.interpolate(&target.pig_chest);
|
||||||
self.piglf_leg.interpolate(&target.piglf_leg);
|
self.pig_leg_lf.interpolate(&target.pig_leg_lf);
|
||||||
self.pigrf_leg.interpolate(&target.pigrf_leg);
|
self.pig_leg_rf.interpolate(&target.pig_leg_rf);
|
||||||
self.piglb_leg.interpolate(&target.piglb_leg);
|
self.pig_leg_lb.interpolate(&target.pig_leg_lb);
|
||||||
self.pigrb_leg.interpolate(&target.pigrb_leg);
|
self.pig_leg_rb.interpolate(&target.pig_leg_rb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,43 +21,43 @@ impl Animation for RunAnimation {
|
|||||||
let mut next = (*skeleton).clone();
|
let mut next = (*skeleton).clone();
|
||||||
|
|
||||||
let wave = (anim_time as f32 * 14.0).sin();
|
let wave = (anim_time as f32 * 14.0).sin();
|
||||||
let wavequick = (anim_time as f32 * 20.0).sin();
|
let wave_quick = (anim_time as f32 * 20.0).sin();
|
||||||
let wavequickcos = (anim_time as f32 * 20.0).cos();
|
let wave_quick_cos = (anim_time as f32 * 20.0).cos();
|
||||||
let wavetest = (wave.cbrt());
|
let wave_test = (wave.cbrt());
|
||||||
let fuzzwave = (anim_time as f32 * 12.0).sin();
|
let fuzz_wave = (anim_time as f32 * 12.0).sin();
|
||||||
let wavecos = (anim_time as f32 * 14.0).cos();
|
let wave_cos = (anim_time as f32 * 14.0).cos();
|
||||||
let wave_slow = (anim_time as f32 * 7.0 + PI).sin();
|
let wave_slow = (anim_time as f32 * 7.0 + PI).sin();
|
||||||
let wavecos_slow = (anim_time as f32 * 8.0 + PI).cos();
|
let wave_slow_cos = (anim_time as f32 * 8.0 + PI).cos();
|
||||||
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
let wave_dip = (wave_slow.abs() - 0.5).abs();
|
||||||
|
|
||||||
next.pighead.offset = Vec3::new(0.0, 0.0, -1.5 + wave * 1.5) / 11.0;
|
next.pig_head.offset = Vec3::new(0.0, 0.0, -1.5 + wave * 1.5) / 11.0;
|
||||||
next.pighead.ori =
|
next.pig_head.ori =
|
||||||
Quaternion::rotation_x(0.2 + wave * 0.05) * Quaternion::rotation_y(wavecos * 0.03);
|
Quaternion::rotation_x(0.2 + wave * 0.05) * Quaternion::rotation_y(wave_cos * 0.03);
|
||||||
next.pighead.scale = Vec3::one() / 10.5;
|
next.pig_head.scale = Vec3::one() / 10.5;
|
||||||
|
|
||||||
next.pigchest.offset = Vec3::new(0.0, -9.0, 1.5 + wavecos * 1.2) / 11.0;
|
next.pig_chest.offset = Vec3::new(0.0, -9.0, 1.5 + wave_cos * 1.2) / 11.0;
|
||||||
next.pigchest.ori = Quaternion::rotation_x(wave * 0.1);
|
next.pig_chest.ori = Quaternion::rotation_x(wave * 0.1);
|
||||||
next.pigchest.scale = Vec3::one() / 11.0;
|
next.pig_chest.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.piglf_leg.offset =
|
next.pig_leg_lf.offset =
|
||||||
Vec3::new(-4.5, 2.0 + wavequick * 0.8, 2.5 + wavequickcos * 1.5) / 11.0;
|
Vec3::new(-4.5, 2.0 + wave_quick * 0.8, 2.5 + wave_quick_cos * 1.5) / 11.0;
|
||||||
next.piglf_leg.ori = Quaternion::rotation_x(wavequick * 0.3);
|
next.pig_leg_lf.ori = Quaternion::rotation_x(wave_quick * 0.3);
|
||||||
next.piglf_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_lf.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.pigrf_leg.offset =
|
next.pig_leg_rf.offset =
|
||||||
Vec3::new(2.5, 2.0 - wavequickcos * 0.8, 2.5 + wavequick * 1.5) / 11.0;
|
Vec3::new(2.5, 2.0 - wave_quick_cos * 0.8, 2.5 + wave_quick * 1.5) / 11.0;
|
||||||
next.pigrf_leg.ori = Quaternion::rotation_x(wavequickcos * -0.3);
|
next.pig_leg_rf.ori = Quaternion::rotation_x(wave_quick_cos * -0.3);
|
||||||
next.pigrf_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_rf.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.piglb_leg.offset =
|
next.pig_leg_lb.offset =
|
||||||
Vec3::new(-4.5, -3.0 - wavequickcos * 0.8, 2.5 + wavequick * 1.5) / 11.0;
|
Vec3::new(-4.5, -3.0 - wave_quick_cos * 0.8, 2.5 + wave_quick * 1.5) / 11.0;
|
||||||
next.piglb_leg.ori = Quaternion::rotation_x(wavequickcos * -0.3);
|
next.pig_leg_lb.ori = Quaternion::rotation_x(wave_quick_cos * -0.3);
|
||||||
next.piglb_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_lb.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next.pigrb_leg.offset =
|
next.pig_leg_rb.offset =
|
||||||
Vec3::new(2.5, -3.0 + wavequick * 0.8, 2.5 + wavequickcos * 1.5) / 11.0;
|
Vec3::new(2.5, -3.0 + wave_quick * 0.8, 2.5 + wave_quick_cos * 1.5) / 11.0;
|
||||||
next.pigrb_leg.ori = Quaternion::rotation_x(wavequick * 0.3);
|
next.pig_leg_rb.ori = Quaternion::rotation_x(wave_quick * 0.3);
|
||||||
next.pigrb_leg.scale = Vec3::one() / 11.0;
|
next.pig_leg_rb.scale = Vec3::one() / 11.0;
|
||||||
|
|
||||||
next
|
next
|
||||||
}
|
}
|
||||||
|
@ -7,16 +7,16 @@ use client;
|
|||||||
// Crate
|
// Crate
|
||||||
use crate::render::RenderError;
|
use crate::render::RenderError;
|
||||||
|
|
||||||
/// Represents any error that may be triggered by Voxygen
|
/// Represents any error that may be triggered by Voxygen.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// An error relating to the internal client
|
/// An error relating to the internal client.
|
||||||
ClientError(client::Error),
|
ClientError(client::Error),
|
||||||
/// A miscellaneous error relating to a backend dependency
|
/// A miscellaneous error relating to a backend dependency.
|
||||||
BackendError(Box<any::Any>),
|
BackendError(Box<any::Any>),
|
||||||
/// An error relating the rendering subsystem
|
/// An error relating the rendering subsystem.
|
||||||
RenderError(RenderError),
|
RenderError(RenderError),
|
||||||
// A miscellaneous error with an unknown or unspecified source
|
/// A miscellaneous error with an unknown or unspecified source.
|
||||||
Other(failure::Error),
|
Other(failure::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,9 +150,9 @@ impl<'a> Widget for Buttons<'a> {
|
|||||||
return Some(Event::ToggleMap);
|
return Some(Event::ToggleMap);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Other Windows can only be accessed, when Settings are closed.
|
// Other Windows can only be accessed when `Settings` is closed.
|
||||||
// Opening Settings will close all other Windows including the Bag.
|
// Opening `Settings` will close all other Windows, including the `Bag`.
|
||||||
// Opening the Map won't close the windows displayed before.
|
// Opening the `Map` won't close the previously displayed windows.
|
||||||
Image::new(self.imgs.social_button)
|
Image::new(self.imgs.social_button)
|
||||||
.w_h(25.0, 25.0)
|
.w_h(25.0, 25.0)
|
||||||
.left_from(state.ids.settings_button, 10.0)
|
.left_from(state.ids.settings_button, 10.0)
|
||||||
|
@ -104,7 +104,7 @@ impl<'a> Widget for CharacterWindow<'a> {
|
|||||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||||
let widget::UpdateArgs { id, state, ui, .. } = args;
|
let widget::UpdateArgs { id, state, ui, .. } = args;
|
||||||
|
|
||||||
// TODO: Read from parameter / character struct
|
// TODO: Read from parameter/character struct.
|
||||||
let xp_percentage = 0.4;
|
let xp_percentage = 0.4;
|
||||||
|
|
||||||
// Frame
|
// Frame
|
||||||
@ -133,7 +133,8 @@ impl<'a> Widget for CharacterWindow<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
Text::new("Character Name") // Add in actual Character Name
|
// TODO: Use an actual character name.
|
||||||
|
Text::new("Character Name")
|
||||||
.mid_top_with_margin_on(state.charwindow_frame, 6.0)
|
.mid_top_with_margin_on(state.charwindow_frame, 6.0)
|
||||||
.font_id(self.fonts.metamorph)
|
.font_id(self.fonts.metamorph)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
@ -153,7 +154,7 @@ impl<'a> Widget for CharacterWindow<'a> {
|
|||||||
|
|
||||||
// Contents
|
// Contents
|
||||||
|
|
||||||
//Head
|
// Head
|
||||||
Image::new(self.imgs.head_bg)
|
Image::new(self.imgs.head_bg)
|
||||||
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
.w_h(28.0 * 1.8, 28.0 * 1.8)
|
||||||
.mid_top_with_margin_on(state.content_align, 5.0)
|
.mid_top_with_margin_on(state.content_align, 5.0)
|
||||||
@ -319,7 +320,8 @@ impl<'a> Widget for CharacterWindow<'a> {
|
|||||||
.top_left_with_margins_on(state.charwindow_tab_bg, 7.0 * 4.0, 4.0 * 4.0)
|
.top_left_with_margins_on(state.charwindow_tab_bg, 7.0 * 4.0, 4.0 * 4.0)
|
||||||
.set(state.charwindow_rectangle, ui);
|
.set(state.charwindow_rectangle, ui);
|
||||||
|
|
||||||
// Tab Button -> Add that back in when we have multiple tabs
|
// TODO: Add this back in when we have multiple tabs.
|
||||||
|
// Tab Button ->
|
||||||
// Button::image(self.imgs.charwindow_tab)
|
// Button::image(self.imgs.charwindow_tab)
|
||||||
//.w_h(65.0, 23.0)
|
//.w_h(65.0, 23.0)
|
||||||
//.top_left_with_margins_on(state.charwindow_tab_bg, -18.0, 1.8)
|
//.top_left_with_margins_on(state.charwindow_tab_bg, -18.0, 1.8)
|
||||||
@ -328,7 +330,8 @@ impl<'a> Widget for CharacterWindow<'a> {
|
|||||||
//.label_font_size(14)
|
//.label_font_size(14)
|
||||||
//.set(state.charwindow_tab1, ui);
|
//.set(state.charwindow_tab1, ui);
|
||||||
|
|
||||||
Text::new("1") //Add in actual Character Level
|
// TODO: Use an actual character level.
|
||||||
|
Text::new("1")
|
||||||
.mid_top_with_margin_on(state.charwindow_rectangle, 10.0)
|
.mid_top_with_margin_on(state.charwindow_rectangle, 10.0)
|
||||||
.font_id(self.fonts.opensans)
|
.font_id(self.fonts.opensans)
|
||||||
.font_size(30)
|
.font_size(30)
|
||||||
@ -352,7 +355,8 @@ impl<'a> Widget for CharacterWindow<'a> {
|
|||||||
.set(state.charwindow_tab1_expbar, ui);
|
.set(state.charwindow_tab1_expbar, ui);
|
||||||
|
|
||||||
// Exp-Text
|
// Exp-Text
|
||||||
Text::new("120/170") // Shows the Exp / Exp to reach the next level
|
// TODO: Shows current Exp over the next threshold Exp.
|
||||||
|
Text::new("120/170")
|
||||||
.mid_top_with_margin_on(state.charwindow_tab1_expbar, 10.0)
|
.mid_top_with_margin_on(state.charwindow_tab1_expbar, 10.0)
|
||||||
.font_id(self.fonts.opensans)
|
.font_id(self.fonts.opensans)
|
||||||
.font_size(15)
|
.font_size(15)
|
||||||
@ -382,6 +386,7 @@ impl<'a> Widget for CharacterWindow<'a> {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(state.charwindow_tab1_statnames, ui);
|
.set(state.charwindow_tab1_statnames, ui);
|
||||||
|
|
||||||
|
// TODO: Shows actual stat points.
|
||||||
Text::new(
|
Text::new(
|
||||||
"1234\n\
|
"1234\n\
|
||||||
\n\
|
\n\
|
||||||
|
@ -39,7 +39,8 @@ impl<'a> Chat<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scrolled_to_bottom(state: &State, ui: &UiCell) -> bool {
|
fn scrolled_to_bottom(state: &State, ui: &UiCell) -> bool {
|
||||||
// could be more efficient to cache result and update it when a scroll event has occurred instead of every frame
|
// Might be more efficient to cache result and update it when a scroll event has occurred
|
||||||
|
// instead of every frame.
|
||||||
if let Some(scroll) = ui
|
if let Some(scroll) = ui
|
||||||
.widget_graph()
|
.widget_graph()
|
||||||
.widget(state.ids.message_box)
|
.widget(state.ids.message_box)
|
||||||
@ -84,7 +85,7 @@ impl<'a> Widget for Chat<'a> {
|
|||||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||||
let widget::UpdateArgs { id, state, ui, .. } = args;
|
let widget::UpdateArgs { id, state, ui, .. } = args;
|
||||||
|
|
||||||
// Maintain scrolling
|
// Maintain scrolling.
|
||||||
if !self.new_messages.is_empty() {
|
if !self.new_messages.is_empty() {
|
||||||
state.update(|s| s.messages.extend(self.new_messages.drain(..)));
|
state.update(|s| s.messages.extend(self.new_messages.drain(..)));
|
||||||
ui.scroll_widget(state.ids.message_box, [0.0, std::f64::MAX]);
|
ui.scroll_widget(state.ids.message_box, [0.0, std::f64::MAX]);
|
||||||
@ -93,8 +94,8 @@ impl<'a> Widget for Chat<'a> {
|
|||||||
let keyboard_capturer = ui.global_input().current.widget_capturing_keyboard;
|
let keyboard_capturer = ui.global_input().current.widget_capturing_keyboard;
|
||||||
let input_focused = keyboard_capturer == Some(state.ids.input);
|
let input_focused = keyboard_capturer == Some(state.ids.input);
|
||||||
|
|
||||||
// Only show if it has the keyboard captured
|
// Only show if it has the keyboard captured.
|
||||||
// Chat input with rectangle as background
|
// Chat input uses a rectangle as its background.
|
||||||
if input_focused {
|
if input_focused {
|
||||||
let text_edit = TextEdit::new(&state.input)
|
let text_edit = TextEdit::new(&state.input)
|
||||||
.w(460.0)
|
.w(460.0)
|
||||||
@ -139,7 +140,7 @@ impl<'a> Widget for Chat<'a> {
|
|||||||
.scroll_kids_vertically()
|
.scroll_kids_vertically()
|
||||||
.set(state.ids.message_box, ui);
|
.set(state.ids.message_box, ui);
|
||||||
while let Some(item) = items.next(ui) {
|
while let Some(item) = items.next(ui) {
|
||||||
// This would be easier if conrod used the v-metrics from rusttype
|
// This would be easier if conrod used the v-metrics from rusttype.
|
||||||
let widget = if item.i < state.messages.len() {
|
let widget = if item.i < state.messages.len() {
|
||||||
let text = Text::new(&state.messages[item.i])
|
let text = Text::new(&state.messages[item.i])
|
||||||
.font_size(15)
|
.font_size(15)
|
||||||
@ -147,15 +148,15 @@ impl<'a> Widget for Chat<'a> {
|
|||||||
.w(470.0)
|
.w(470.0)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.line_spacing(2.0);
|
.line_spacing(2.0);
|
||||||
// Add space between messages
|
// Add space between messages.
|
||||||
let y = match text.get_y_dimension(ui) {
|
let y = match text.get_y_dimension(ui) {
|
||||||
Dimension::Absolute(y) => y + 2.0,
|
Dimension::Absolute(y) => y + 2.0,
|
||||||
_ => 0.0,
|
_ => 0.0,
|
||||||
};
|
};
|
||||||
text.h(y)
|
text.h(y)
|
||||||
} else {
|
} else {
|
||||||
// Spacer at bottom of the last message so that it is not cut off
|
// Spacer at bottom of the last message so that it is not cut off.
|
||||||
// Needs to be larger than the space above
|
// Needs to be larger than the space above.
|
||||||
Text::new("")
|
Text::new("")
|
||||||
.font_size(6)
|
.font_size(6)
|
||||||
.font_id(self.fonts.opensans)
|
.font_id(self.fonts.opensans)
|
||||||
@ -165,7 +166,7 @@ impl<'a> Widget for Chat<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Chat Arrow
|
// Chat Arrow
|
||||||
// Check if already at bottom
|
// Check if already at bottom.
|
||||||
if !Self::scrolled_to_bottom(state, ui) {
|
if !Self::scrolled_to_bottom(state, ui) {
|
||||||
if Button::image(self.imgs.chat_arrow)
|
if Button::image(self.imgs.chat_arrow)
|
||||||
.w_h(20.0, 20.0)
|
.w_h(20.0, 20.0)
|
||||||
@ -179,11 +180,11 @@ impl<'a> Widget for Chat<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the chat widget is focused return a focus event to pass the focus to the input box
|
// If the chat widget is focused, return a focus event to pass the focus to the input box.
|
||||||
if keyboard_capturer == Some(id) {
|
if keyboard_capturer == Some(id) {
|
||||||
Some(Event::Focus(state.ids.input))
|
Some(Event::Focus(state.ids.input))
|
||||||
}
|
}
|
||||||
// If enter is pressed and the input box is not empty send the current message
|
// If enter is pressed and the input box is not empty, send the current message.
|
||||||
else if ui
|
else if ui
|
||||||
.widget_input(state.ids.input)
|
.widget_input(state.ids.input)
|
||||||
.presses()
|
.presses()
|
||||||
|
@ -103,7 +103,7 @@ impl<'a> Widget for EscMenu<'a> {
|
|||||||
.set(state.ids.menu_button_2, ui)
|
.set(state.ids.menu_button_2, ui)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
{
|
{
|
||||||
// TODO: Show controls window
|
// TODO: Show controls window.
|
||||||
};
|
};
|
||||||
// Servers
|
// Servers
|
||||||
if Button::image(self.imgs.button)
|
if Button::image(self.imgs.button)
|
||||||
@ -118,7 +118,7 @@ impl<'a> Widget for EscMenu<'a> {
|
|||||||
.set(state.ids.menu_button_3, ui)
|
.set(state.ids.menu_button_3, ui)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
{
|
{
|
||||||
// TODO: Show servers window (is needed in-game?)
|
// TODO: Show servers window (needed in-game?).
|
||||||
};
|
};
|
||||||
// Logout
|
// Logout
|
||||||
if Button::image(self.imgs.button)
|
if Button::image(self.imgs.button)
|
||||||
|
@ -103,7 +103,7 @@ impl<'a> Widget for MiniMap<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
// Make it display the actual location
|
// TODO: Make it display the actual location.
|
||||||
Text::new("Uncanny Valley")
|
Text::new("Uncanny Valley")
|
||||||
.mid_top_with_margin_on(state.ids.mmap_frame, 3.0)
|
.mid_top_with_margin_on(state.ids.mmap_frame, 3.0)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
|
@ -94,13 +94,13 @@ pub enum Event {
|
|||||||
Quit,
|
Quit,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: are these the possible layouts we want?
|
// TODO: Are these the possible layouts we want?
|
||||||
// TODO: maybe replace this with bitflags
|
// TODO: Maybe replace this with bitflags.
|
||||||
// map not here because it currently is displayed over the top of other open windows
|
// `map` is not here because it currently is displayed over the top of other open windows.
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum Windows {
|
pub enum Windows {
|
||||||
Settings, // display settings window
|
Settings, // Display settings window.
|
||||||
CharacterAnd(Option<SmallWindowType>), // show character window + optionally another
|
CharacterAnd(Option<SmallWindowType>), // Show character window + optionally another.
|
||||||
Small(SmallWindowType),
|
Small(SmallWindowType),
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
@ -146,7 +146,7 @@ impl Show {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_charwindow(&mut self) {
|
fn toggle_char_window(&mut self) {
|
||||||
self.open_windows = match self.open_windows {
|
self.open_windows = match self.open_windows {
|
||||||
Windows::CharacterAnd(small) => match small {
|
Windows::CharacterAnd(small) => match small {
|
||||||
Some(small) => Windows::Small(small),
|
Some(small) => Windows::Small(small),
|
||||||
@ -212,14 +212,14 @@ pub struct Hud {
|
|||||||
impl Hud {
|
impl Hud {
|
||||||
pub fn new(window: &mut Window, settings: Settings) -> Self {
|
pub fn new(window: &mut Window, settings: Settings) -> Self {
|
||||||
let mut ui = Ui::new(window).unwrap();
|
let mut ui = Ui::new(window).unwrap();
|
||||||
// TODO: adjust/remove this, right now it is used to demonstrate window scaling functionality
|
// TODO: Adjust/remove this, right now it is used to demonstrate window scaling functionality.
|
||||||
ui.scaling_mode(ScaleMode::RelativeToWindow([1920.0, 1080.0].into()));
|
ui.scaling_mode(ScaleMode::RelativeToWindow([1920.0, 1080.0].into()));
|
||||||
// Generate ids
|
// Generate ids.
|
||||||
let ids = Ids::new(ui.id_generator());
|
let ids = Ids::new(ui.id_generator());
|
||||||
// Load images
|
// Load images.
|
||||||
let imgs = Imgs::load(&mut ui).expect("Failed to load images");
|
let imgs = Imgs::load(&mut ui).expect("Failed to load images!");
|
||||||
// Load fonts
|
// Load fonts.
|
||||||
let fonts = Fonts::load(&mut ui).expect("Failed to load fonts");
|
let fonts = Fonts::load(&mut ui).expect("Failed to load fonts!");
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
ui,
|
ui,
|
||||||
@ -251,12 +251,12 @@ impl Hud {
|
|||||||
let ref mut ui_widgets = self.ui.set_widgets();
|
let ref mut ui_widgets = self.ui.set_widgets();
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
// Don't show anything if the UI is toggled off
|
// Don't show anything if the UI is toggled off.
|
||||||
if !self.show.ui {
|
if !self.show.ui {
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display debug window
|
// Display debug window.
|
||||||
if self.show.debug {
|
if self.show.debug {
|
||||||
// Alpha Version
|
// Alpha Version
|
||||||
Text::new(version)
|
Text::new(version)
|
||||||
@ -273,7 +273,7 @@ impl Hud {
|
|||||||
.set(self.ids.fps_counter, ui_widgets);
|
.set(self.ids.fps_counter, ui_widgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Bag-Space Button
|
// Add Bag-Space Button.
|
||||||
if self.show.inventory_test_button {
|
if self.show.inventory_test_button {
|
||||||
if Button::image(self.imgs.grid_button)
|
if Button::image(self.imgs.grid_button)
|
||||||
.w_h(100.0, 100.0)
|
.w_h(100.0, 100.0)
|
||||||
@ -314,7 +314,7 @@ impl Hud {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bag button and icons near it
|
// Bag button and nearby icons
|
||||||
match Buttons::new(
|
match Buttons::new(
|
||||||
&self.show.open_windows,
|
&self.show.open_windows,
|
||||||
self.show.map,
|
self.show.map,
|
||||||
@ -326,7 +326,7 @@ impl Hud {
|
|||||||
{
|
{
|
||||||
Some(buttons::Event::ToggleBag) => self.show.toggle_bag(),
|
Some(buttons::Event::ToggleBag) => self.show.toggle_bag(),
|
||||||
Some(buttons::Event::ToggleSettings) => self.show.toggle_settings(),
|
Some(buttons::Event::ToggleSettings) => self.show.toggle_settings(),
|
||||||
Some(buttons::Event::ToggleCharacter) => self.show.toggle_charwindow(),
|
Some(buttons::Event::ToggleCharacter) => self.show.toggle_char_window(),
|
||||||
Some(buttons::Event::ToggleSmall(small)) => self.show.toggle_small(small),
|
Some(buttons::Event::ToggleSmall(small)) => self.show.toggle_small(small),
|
||||||
Some(buttons::Event::ToggleMap) => self.show.toggle_map(),
|
Some(buttons::Event::ToggleMap) => self.show.toggle_map(),
|
||||||
None => {}
|
None => {}
|
||||||
@ -365,10 +365,10 @@ impl Hud {
|
|||||||
}
|
}
|
||||||
self.new_messages = VecDeque::new();
|
self.new_messages = VecDeque::new();
|
||||||
|
|
||||||
//Windows
|
// Windows
|
||||||
|
|
||||||
//Char Window will always appear at the left side. Other Windows either appear at the left side,
|
// Char Window will always appear at the left side. Other Windows default to the
|
||||||
//or when the Char Window is opened they will appear right from it.
|
// left side, but when the Char Window is opened they will appear to the right of it.
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
if let Windows::Settings = self.show.open_windows {
|
if let Windows::Settings = self.show.open_windows {
|
||||||
@ -447,7 +447,7 @@ impl Hud {
|
|||||||
self.new_messages.push_back(msg);
|
self.new_messages.push_back(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if a TextEdit widget has the keyboard captured
|
// Checks if a TextEdit widget has the keyboard captured.
|
||||||
fn typing(&self) -> bool {
|
fn typing(&self) -> bool {
|
||||||
if let Some(id) = self.ui.widget_capturing_keyboard() {
|
if let Some(id) = self.ui.widget_capturing_keyboard() {
|
||||||
self.ui
|
self.ui
|
||||||
@ -515,7 +515,7 @@ impl Hud {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key::CharacterWindow => {
|
Key::CharacterWindow => {
|
||||||
self.show.toggle_charwindow();
|
self.show.toggle_char_window();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Key::Social => {
|
Key::Social => {
|
||||||
@ -547,7 +547,7 @@ impl Hud {
|
|||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
// Handle cursor grab
|
// Handle cursor grab.
|
||||||
if !self.force_ungrab {
|
if !self.force_ungrab {
|
||||||
if cursor_grabbed != self.show.want_grab {
|
if cursor_grabbed != self.show.want_grab {
|
||||||
global_state.window.grab_cursor(self.show.want_grab);
|
global_state.window.grab_cursor(self.show.want_grab);
|
||||||
@ -571,8 +571,8 @@ impl Hud {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get the text to show in the help window, along with the
|
// Get the text to show in the help window and use the
|
||||||
//length of the longest line in order to resize the window
|
// length of the longest line to resize the window.
|
||||||
fn get_help_text(cs: &ControlSettings) -> String {
|
fn get_help_text(cs: &ControlSettings) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{free_cursor:?} = Free cursor\n\
|
"{free_cursor:?} = Free cursor\n\
|
||||||
|
@ -237,7 +237,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
.set(state.ids.debug_button_label, ui);
|
.set(state.ids.debug_button_label, ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2 Gameplay////////////////
|
// 2 Gameplay
|
||||||
if Button::image(if let SettingsTab::Gameplay = state.settings_tab {
|
if Button::image(if let SettingsTab::Gameplay = state.settings_tab {
|
||||||
self.imgs.settings_button_pressed
|
self.imgs.settings_button_pressed
|
||||||
} else {
|
} else {
|
||||||
@ -264,7 +264,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
state.update(|s| s.settings_tab = SettingsTab::Gameplay);
|
state.update(|s| s.settings_tab = SettingsTab::Gameplay);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3 Controls/////////////////////
|
// 3 Controls
|
||||||
if Button::image(if let SettingsTab::Controls = state.settings_tab {
|
if Button::image(if let SettingsTab::Controls = state.settings_tab {
|
||||||
self.imgs.settings_button_pressed
|
self.imgs.settings_button_pressed
|
||||||
} else {
|
} else {
|
||||||
@ -356,7 +356,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
.font_id(self.fonts.opensans)
|
.font_id(self.fonts.opensans)
|
||||||
.font_size(18)
|
.font_size(18)
|
||||||
.set(state.ids.controls_text, ui);
|
.set(state.ids.controls_text, ui);
|
||||||
// TODO: Replace with buttons that show the actual keybind and allow the user to change it.
|
// TODO: Replace with buttons that show actual keybinds and allow the user to change them.
|
||||||
Text::new(
|
Text::new(
|
||||||
"TAB\n\
|
"TAB\n\
|
||||||
F1\n\
|
F1\n\
|
||||||
@ -423,7 +423,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
.font_size(18)
|
.font_size(18)
|
||||||
.set(state.ids.controls_controls, ui);
|
.set(state.ids.controls_controls, ui);
|
||||||
}
|
}
|
||||||
// 4 Video////////////////////////////////
|
// 4 Video
|
||||||
if Button::image(if let SettingsTab::Video = state.settings_tab {
|
if Button::image(if let SettingsTab::Video = state.settings_tab {
|
||||||
self.imgs.settings_button_pressed
|
self.imgs.settings_button_pressed
|
||||||
} else {
|
} else {
|
||||||
@ -451,7 +451,7 @@ impl<'a> Widget for SettingsWindow<'a> {
|
|||||||
state.update(|s| s.settings_tab = SettingsTab::Video);
|
state.update(|s| s.settings_tab = SettingsTab::Video);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5 Sound///////////////////////////////
|
// 5 Sound
|
||||||
if Button::image(if let SettingsTab::Sound = state.settings_tab {
|
if Button::image(if let SettingsTab::Sound = state.settings_tab {
|
||||||
self.imgs.settings_button_pressed
|
self.imgs.settings_button_pressed
|
||||||
} else {
|
} else {
|
||||||
|
@ -68,12 +68,13 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
|
||||||
let widget::UpdateArgs { state, ui, .. } = args;
|
let widget::UpdateArgs { state, ui, .. } = args;
|
||||||
|
|
||||||
// TODO: Read from parameter / character struct
|
// TODO: Read from parameter/character struct
|
||||||
let xp_percentage = 0.4;
|
let xp_percentage = 0.4;
|
||||||
let hp_percentage = 1.0;
|
let hp_percentage = 1.0;
|
||||||
let mana_percentage = 1.0;
|
let mana_percentage = 1.0;
|
||||||
|
|
||||||
// Crosshair TODO: Only show while aiming with a bow or when casting a spell
|
// TODO: Only show while aiming with a bow or when casting a spell.
|
||||||
|
// Crosshair
|
||||||
// Image::new(self.imgs.crosshair)
|
// Image::new(self.imgs.crosshair)
|
||||||
// .w_h(101.0 * 0.5, 101.0 * 0.5)
|
// .w_h(101.0 * 0.5, 101.0 * 0.5)
|
||||||
// .mid_top_with_margin_on(ui.window, 500.0)
|
// .mid_top_with_margin_on(ui.window, 500.0)
|
||||||
@ -158,14 +159,14 @@ impl<'a> Widget for Skillbar<'a> {
|
|||||||
|
|
||||||
// Level Display
|
// Level Display
|
||||||
|
|
||||||
// Insert actual Level here
|
// TODO: Insert actual Level here.
|
||||||
Text::new("1")
|
Text::new("1")
|
||||||
.left_from(state.ids.xp_bar, -15.0)
|
.left_from(state.ids.xp_bar, -15.0)
|
||||||
.font_size(10)
|
.font_size(10)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(state.ids.level_text, ui);
|
.set(state.ids.level_text, ui);
|
||||||
|
|
||||||
// Insert next Level here
|
// TODO: Insert next Level here.
|
||||||
Text::new("2")
|
Text::new("2")
|
||||||
.right_from(state.ids.xp_bar, -15.0)
|
.right_from(state.ids.xp_bar, -15.0)
|
||||||
.font_size(10)
|
.font_size(10)
|
||||||
|
@ -24,17 +24,17 @@ use log;
|
|||||||
use simplelog::{CombinedLogger, Config, TermLogger, WriteLogger};
|
use simplelog::{CombinedLogger, Config, TermLogger, WriteLogger};
|
||||||
use std::{fs::File, mem, panic, str::FromStr, thread};
|
use std::{fs::File, mem, panic, str::FromStr, thread};
|
||||||
|
|
||||||
/// The URL of the default public server that Voxygen will connect to
|
/// The URL of the default public server that Voxygen will connect to.
|
||||||
const DEFAULT_PUBLIC_SERVER: &'static str = "server.veloren.net";
|
const DEFAULT_PUBLIC_SERVER: &'static str = "server.veloren.net";
|
||||||
|
|
||||||
/// A type used to store state that is shared between all play states
|
/// A type used to store state that is shared between all play states.
|
||||||
pub struct GlobalState {
|
pub struct GlobalState {
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
window: Window,
|
window: Window,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalState {
|
impl GlobalState {
|
||||||
/// Called after a change in play state has occured (usually used to reverse any temporary
|
/// Called after a change in play state has occurred (usually used to reverse any temporary
|
||||||
/// effects a state may have made).
|
/// effects a state may have made).
|
||||||
pub fn on_play_state_changed(&mut self) {
|
pub fn on_play_state_changed(&mut self) {
|
||||||
self.window.grab_cursor(false);
|
self.window.grab_cursor(false);
|
||||||
@ -47,16 +47,16 @@ pub enum Direction {
|
|||||||
Backwards,
|
Backwards,
|
||||||
}
|
}
|
||||||
|
|
||||||
// States can either close (and revert to a previous state), push a new state on top of themselves,
|
/// 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
|
/// or switch to a totally different state.
|
||||||
pub enum PlayStateResult {
|
pub enum PlayStateResult {
|
||||||
/// Pop all play states in reverse order and shut down the program
|
/// Pop all play states in reverse order and shut down the program.
|
||||||
Shutdown,
|
Shutdown,
|
||||||
/// Close the current play state and pop it from the play state stack
|
/// Close the current play state and pop it from the play state stack.
|
||||||
Pop,
|
Pop,
|
||||||
/// Push a new play state onto the play state stack
|
/// Push a new play state onto the play state stack.
|
||||||
Push(Box<dyn PlayState>),
|
Push(Box<dyn PlayState>),
|
||||||
/// Switch the current play state with a new play state
|
/// Switch the current play state with a new play state.
|
||||||
Switch(Box<dyn PlayState>),
|
Switch(Box<dyn PlayState>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,16 +67,16 @@ pub trait PlayState {
|
|||||||
/// is closed).
|
/// is closed).
|
||||||
fn play(&mut self, direction: Direction, global_state: &mut GlobalState) -> PlayStateResult;
|
fn play(&mut self, direction: Direction, global_state: &mut GlobalState) -> PlayStateResult;
|
||||||
|
|
||||||
/// Get a descriptive name for this state type
|
/// Get a descriptive name for this state type.
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Set up the global state
|
// Set up the global state.
|
||||||
let settings = Settings::load();
|
let settings = Settings::load();
|
||||||
let window = Window::new(&settings).expect("Failed to create window");
|
let window = Window::new(&settings).expect("Failed to create window!");
|
||||||
|
|
||||||
// Init logging
|
// Initialize logging.
|
||||||
let term_log_level = std::env::var_os("VOXYGEN_LOG")
|
let term_log_level = std::env::var_os("VOXYGEN_LOG")
|
||||||
.and_then(|env| env.to_str().map(|s| s.to_owned()))
|
.and_then(|env| env.to_str().map(|s| s.to_owned()))
|
||||||
.and_then(|s| log::LevelFilter::from_str(&s).ok())
|
.and_then(|s| log::LevelFilter::from_str(&s).ok())
|
||||||
@ -91,7 +91,7 @@ fn main() {
|
|||||||
])
|
])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Set up panic handler to relay swish panic messages to the user
|
// Set up panic handler to relay swish panic messages to the user.
|
||||||
let settings_clone = settings.clone();
|
let settings_clone = settings.clone();
|
||||||
let default_hook = panic::take_hook();
|
let default_hook = panic::take_hook();
|
||||||
panic::set_hook(Box::new(move |panic_info| {
|
panic::set_hook(Box::new(move |panic_info| {
|
||||||
@ -155,7 +155,7 @@ fn main() {
|
|||||||
|
|
||||||
let mut global_state = GlobalState { settings, window };
|
let mut global_state = GlobalState { settings, window };
|
||||||
|
|
||||||
// Set up the initial play state
|
// Set up the initial play state.
|
||||||
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(MainMenuState::new(&mut global_state))];
|
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(MainMenuState::new(&mut global_state))];
|
||||||
states
|
states
|
||||||
.last()
|
.last()
|
||||||
@ -173,14 +173,14 @@ fn main() {
|
|||||||
.last_mut()
|
.last_mut()
|
||||||
.map(|last| last.play(direction, &mut global_state))
|
.map(|last| last.play(direction, &mut global_state))
|
||||||
{
|
{
|
||||||
// Implement state transfer logic
|
// Implement state transfer logic.
|
||||||
match state_result {
|
match state_result {
|
||||||
PlayStateResult::Shutdown => {
|
PlayStateResult::Shutdown => {
|
||||||
direction = Direction::Backwards;
|
direction = Direction::Backwards;
|
||||||
log::info!("Shutting down all states...");
|
log::info!("Shutting down all states...");
|
||||||
while states.last().is_some() {
|
while states.last().is_some() {
|
||||||
states.pop().map(|old_state| {
|
states.pop().map(|old_state| {
|
||||||
log::info!("Popped state '{}'", old_state.name());
|
log::info!("Popped state '{}'.", old_state.name());
|
||||||
global_state.on_play_state_changed();
|
global_state.on_play_state_changed();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -188,13 +188,13 @@ fn main() {
|
|||||||
PlayStateResult::Pop => {
|
PlayStateResult::Pop => {
|
||||||
direction = Direction::Backwards;
|
direction = Direction::Backwards;
|
||||||
states.pop().map(|old_state| {
|
states.pop().map(|old_state| {
|
||||||
log::info!("Popped state '{}'", old_state.name());
|
log::info!("Popped state '{}'.", old_state.name());
|
||||||
global_state.on_play_state_changed();
|
global_state.on_play_state_changed();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
PlayStateResult::Push(new_state) => {
|
PlayStateResult::Push(new_state) => {
|
||||||
direction = Direction::Forwards;
|
direction = Direction::Forwards;
|
||||||
log::info!("Pushed state '{}'", new_state.name());
|
log::info!("Pushed state '{}'.", new_state.name());
|
||||||
states.push(new_state);
|
states.push(new_state);
|
||||||
global_state.on_play_state_changed();
|
global_state.on_play_state_changed();
|
||||||
}
|
}
|
||||||
@ -202,7 +202,7 @@ fn main() {
|
|||||||
direction = Direction::Forwards;
|
direction = Direction::Forwards;
|
||||||
states.last_mut().map(|old_state| {
|
states.last_mut().map(|old_state| {
|
||||||
log::info!(
|
log::info!(
|
||||||
"Switching to state '{}' from state '{}'",
|
"Switching to state '{}' from state '{}'.",
|
||||||
new_state.name(),
|
new_state.name(),
|
||||||
old_state.name()
|
old_state.name()
|
||||||
);
|
);
|
||||||
|
@ -22,7 +22,7 @@ pub struct CharSelectionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CharSelectionState {
|
impl CharSelectionState {
|
||||||
/// Create a new `CharSelectionState`
|
/// Create a new `CharSelectionState`.
|
||||||
pub fn new(window: &mut Window, client: Rc<RefCell<Client>>) -> Self {
|
pub fn new(window: &mut Window, client: Rc<RefCell<Client>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
char_selection_ui: CharSelectionUi::new(window),
|
char_selection_ui: CharSelectionUi::new(window),
|
||||||
@ -32,7 +32,7 @@ impl CharSelectionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The background colour
|
// Background colour
|
||||||
const BG_COLOR: Rgba<f32> = Rgba {
|
const BG_COLOR: Rgba<f32> = Rgba {
|
||||||
r: 0.0,
|
r: 0.0,
|
||||||
g: 0.3,
|
g: 0.3,
|
||||||
@ -42,28 +42,28 @@ const BG_COLOR: Rgba<f32> = Rgba {
|
|||||||
|
|
||||||
impl PlayState for CharSelectionState {
|
impl PlayState for CharSelectionState {
|
||||||
fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult {
|
fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult {
|
||||||
// Set up an fps clock
|
// Set up an fps clock.
|
||||||
let mut clock = Clock::new();
|
let mut clock = Clock::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Handle window events
|
// Handle window events.
|
||||||
for event in global_state.window.fetch_events() {
|
for event in global_state.window.fetch_events() {
|
||||||
match event {
|
match event {
|
||||||
Event::Close => {
|
Event::Close => {
|
||||||
return PlayStateResult::Shutdown;
|
return PlayStateResult::Shutdown;
|
||||||
}
|
}
|
||||||
// Pass events to ui
|
// Pass events to ui.
|
||||||
Event::Ui(event) => {
|
Event::Ui(event) => {
|
||||||
self.char_selection_ui.handle_event(event);
|
self.char_selection_ui.handle_event(event);
|
||||||
}
|
}
|
||||||
// Ignore all other events
|
// Ignore all other events.
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global_state.window.renderer_mut().clear(BG_COLOR);
|
global_state.window.renderer_mut().clear(BG_COLOR);
|
||||||
|
|
||||||
// Maintain the UI
|
// Maintain the UI.
|
||||||
for event in self
|
for event in self
|
||||||
.char_selection_ui
|
.char_selection_ui
|
||||||
.maintain(global_state.window.renderer_mut())
|
.maintain(global_state.window.renderer_mut())
|
||||||
@ -89,33 +89,33 @@ impl PlayState for CharSelectionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maintain the scene
|
// Maintain the scene.
|
||||||
self.scene
|
self.scene
|
||||||
.maintain(global_state.window.renderer_mut(), &self.client.borrow());
|
.maintain(global_state.window.renderer_mut(), &self.client.borrow());
|
||||||
|
|
||||||
// Render the scene
|
// Render the scene.
|
||||||
self.scene
|
self.scene
|
||||||
.render(global_state.window.renderer_mut(), &self.client.borrow());
|
.render(global_state.window.renderer_mut(), &self.client.borrow());
|
||||||
|
|
||||||
// Draw the UI to the screen
|
// Draw the UI to the screen.
|
||||||
self.char_selection_ui
|
self.char_selection_ui
|
||||||
.render(global_state.window.renderer_mut());
|
.render(global_state.window.renderer_mut());
|
||||||
|
|
||||||
// Tick the client (currently only to keep the connection alive)
|
// Tick the client (currently only to keep the connection alive).
|
||||||
self.client
|
self.client
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.tick(client::Input::default(), clock.get_last_delta())
|
.tick(client::Input::default(), clock.get_last_delta())
|
||||||
.expect("Failed to tick the client");
|
.expect("Failed to tick the client");
|
||||||
self.client.borrow_mut().cleanup();
|
self.client.borrow_mut().cleanup();
|
||||||
|
|
||||||
// Finish the frame
|
// Finish the frame.
|
||||||
global_state.window.renderer_mut().flush();
|
global_state.window.renderer_mut().flush();
|
||||||
global_state
|
global_state
|
||||||
.window
|
.window
|
||||||
.swap_buffers()
|
.swap_buffers()
|
||||||
.expect("Failed to swap window buffers");
|
.expect("Failed to swap window buffers");
|
||||||
|
|
||||||
// Wait for the next tick
|
// Wait for the next tick.
|
||||||
clock.tick(Duration::from_millis(1000 / FPS));
|
clock.tick(Duration::from_millis(1000 / FPS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,16 +276,16 @@ pub struct CharSelectionUi {
|
|||||||
impl CharSelectionUi {
|
impl CharSelectionUi {
|
||||||
pub fn new(window: &mut Window) -> Self {
|
pub fn new(window: &mut Window) -> Self {
|
||||||
let mut ui = Ui::new(window).unwrap();
|
let mut ui = Ui::new(window).unwrap();
|
||||||
// TODO: adjust/remove this, right now it is used to demonstrate window scaling functionality
|
// TODO: Adjust/remove this, right now it is used to demonstrate window scaling functionality.
|
||||||
ui.scaling_mode(ScaleMode::RelativeToWindow([1920.0, 1080.0].into()));
|
ui.scaling_mode(ScaleMode::RelativeToWindow([1920.0, 1080.0].into()));
|
||||||
// Generate ids
|
// Generate ids
|
||||||
let ids = Ids::new(ui.id_generator());
|
let ids = Ids::new(ui.id_generator());
|
||||||
// Load images
|
// Load images
|
||||||
let imgs = Imgs::load(&mut ui).expect("Failed to load images");
|
let imgs = Imgs::load(&mut ui).expect("Failed to load images!");
|
||||||
// Load fonts
|
// Load fonts
|
||||||
let fonts = Fonts::load(&mut ui).expect("Failed to load fonts");
|
let fonts = Fonts::load(&mut ui).expect("Failed to load fonts!");
|
||||||
|
|
||||||
// TODO: Randomize initial values
|
// TODO: Randomize initial values.
|
||||||
Self {
|
Self {
|
||||||
ui,
|
ui,
|
||||||
ids,
|
ids,
|
||||||
@ -299,20 +299,20 @@ impl CharSelectionUi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: split this up into multiple modules or functions
|
// TODO: Split this into multiple modules or functions.
|
||||||
fn update_layout(&mut self) -> Vec<Event> {
|
fn update_layout(&mut self) -> Vec<Event> {
|
||||||
let mut events = Vec::new();
|
let mut events = Vec::new();
|
||||||
let ref mut ui_widgets = self.ui.set_widgets();
|
let ref mut ui_widgets = self.ui.set_widgets();
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
// Character Selection /////////////////
|
// Character Selection
|
||||||
// Supposed functionality:
|
// Supposed functionality:
|
||||||
// 3d rendered characters have to be clicked for selection
|
// 3d rendered characters have to be clicked for selection.
|
||||||
// Selected characters will appear in the selection window
|
// Selected characters will appear in the selection window.
|
||||||
// the selection window is only active when there are >0 characters on the server
|
// The selection window is only active when there are >0 characters on the server.
|
||||||
// after logging into the server the character that was played last will be selected automatically
|
// After logging into the server the character that was played last will be selected automatically.
|
||||||
// if >1 characters are on the server but none of them was logged in last the one that was created last will be selected
|
// If >1 characters are on the server but none of them was logged in last the one that was created last will be selected.
|
||||||
// if the no. of characters = character_limit the "Create Character" button won't be clickable anymore
|
// If the no. of characters = character_limit the "Create Character" button won't be clickable anymore.
|
||||||
|
|
||||||
// Background Image
|
// Background Image
|
||||||
if !self.character_creation {
|
if !self.character_creation {
|
||||||
@ -336,7 +336,7 @@ impl CharSelectionUi {
|
|||||||
events.push(Event::Logout);
|
events.push(Event::Logout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Character Button
|
// Create Character Button.
|
||||||
if Button::image(self.imgs.button)
|
if Button::image(self.imgs.button)
|
||||||
.mid_bottom_with_margin_on(ui_widgets.window, 10.0)
|
.mid_bottom_with_margin_on(ui_widgets.window, 10.0)
|
||||||
.w_h(270.0, 50.0)
|
.w_h(270.0, 50.0)
|
||||||
@ -374,7 +374,7 @@ impl CharSelectionUi {
|
|||||||
.font_size(14)
|
.font_size(14)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(self.ids.version, ui_widgets);
|
.set(self.ids.version, ui_widgets);
|
||||||
// Click Character to Login <-- Temporary!
|
// Click Character to Login TODO: <-- Temporary!
|
||||||
Image::new(self.imgs.window_frame_2)
|
Image::new(self.imgs.window_frame_2)
|
||||||
.mid_top_with_margin_on(ui_widgets.window, 60.0)
|
.mid_top_with_margin_on(ui_widgets.window, 60.0)
|
||||||
.w_h(700.0, 70.0)
|
.w_h(700.0, 70.0)
|
||||||
@ -443,7 +443,7 @@ impl CharSelectionUi {
|
|||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Character_Creation //////////////
|
// Character_Creation
|
||||||
else {
|
else {
|
||||||
// Background
|
// Background
|
||||||
//Image::new(self.imgs.bg_creation)
|
//Image::new(self.imgs.bg_creation)
|
||||||
@ -478,7 +478,7 @@ impl CharSelectionUi {
|
|||||||
.set(self.ids.create_button, ui_widgets)
|
.set(self.ids.create_button, ui_widgets)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
{
|
{
|
||||||
// TODO: Save character
|
// TODO: Save character.
|
||||||
self.character_creation = false;
|
self.character_creation = false;
|
||||||
}
|
}
|
||||||
// Character Name Input
|
// Character Name Input
|
||||||
@ -519,7 +519,7 @@ impl CharSelectionUi {
|
|||||||
.set(self.ids.creation_window, ui_widgets);
|
.set(self.ids.creation_window, ui_widgets);
|
||||||
|
|
||||||
// Arrows
|
// Arrows
|
||||||
// TODO: lower the resolution of the arrow images & use non decimal sizes below
|
// TODO: Lower the resolution of the arrow images & use non decimal sizes below.
|
||||||
const ARROW_WH: [f64; 2] = [986.0 * 0.03, 1024.0 * 0.03];
|
const ARROW_WH: [f64; 2] = [986.0 * 0.03, 1024.0 * 0.03];
|
||||||
match self.creation_state {
|
match self.creation_state {
|
||||||
CreationState::Race => {
|
CreationState::Race => {
|
||||||
@ -586,7 +586,7 @@ impl CharSelectionUi {
|
|||||||
|
|
||||||
// Body
|
// Body
|
||||||
|
|
||||||
//Race Selection
|
// Race Selection
|
||||||
if let CreationState::Race = self.creation_state {
|
if let CreationState::Race = self.creation_state {
|
||||||
Text::new("Choose your Race")
|
Text::new("Choose your Race")
|
||||||
.mid_top_with_margin_on(self.ids.creation_window, 74.0)
|
.mid_top_with_margin_on(self.ids.creation_window, 74.0)
|
||||||
@ -595,7 +595,7 @@ impl CharSelectionUi {
|
|||||||
.set(self.ids.select_window_title, ui_widgets);
|
.set(self.ids.select_window_title, ui_widgets);
|
||||||
|
|
||||||
// Male/Female/Race Icons
|
// Male/Female/Race Icons
|
||||||
// for alignment
|
// Alignment
|
||||||
Rectangle::fill_with([151.0, 68.0], color::TRANSPARENT)
|
Rectangle::fill_with([151.0, 68.0], color::TRANSPARENT)
|
||||||
.mid_top_with_margin_on(self.ids.creation_window, 210.0)
|
.mid_top_with_margin_on(self.ids.creation_window, 210.0)
|
||||||
.set(self.ids.body_type_bg, ui_widgets);
|
.set(self.ids.body_type_bg, ui_widgets);
|
||||||
@ -636,11 +636,11 @@ impl CharSelectionUi {
|
|||||||
{
|
{
|
||||||
self.character_body.body_type = BodyType::Female;
|
self.character_body.body_type = BodyType::Female;
|
||||||
}
|
}
|
||||||
// for alignment
|
// Alignment
|
||||||
Rectangle::fill_with([458.0, 68.0], color::TRANSPARENT)
|
Rectangle::fill_with([458.0, 68.0], color::TRANSPARENT)
|
||||||
.mid_top_with_margin_on(self.ids.creation_window, 120.0)
|
.mid_top_with_margin_on(self.ids.creation_window, 120.0)
|
||||||
.set(self.ids.races_bg, ui_widgets);
|
.set(self.ids.races_bg, ui_widgets);
|
||||||
// TODO: If races where in some sort of array format we could do this in a loop
|
// TODO: If races were in some sort of array format, we could do this in a loop.
|
||||||
// Human
|
// Human
|
||||||
Image::new(if let BodyType::Male = self.character_body.body_type {
|
Image::new(if let BodyType::Male = self.character_body.body_type {
|
||||||
self.imgs.human_m
|
self.imgs.human_m
|
||||||
@ -777,7 +777,7 @@ impl CharSelectionUi {
|
|||||||
|
|
||||||
// Description Headline and Text
|
// Description Headline and Text
|
||||||
|
|
||||||
// TODO: Load these from files (or from the server???)
|
// TODO: Load these from files (or from the server???).
|
||||||
const HUMAN_DESC: &str =
|
const HUMAN_DESC: &str =
|
||||||
"The former nomads were only recently able to gain a foothold in the world of Veloren. \n\
|
"The former nomads were only recently able to gain a foothold in the world of Veloren. \n\
|
||||||
\n\
|
\n\
|
||||||
@ -854,7 +854,6 @@ impl CharSelectionUi {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.wrap_by_word()
|
.wrap_by_word()
|
||||||
.set(self.ids.race_description, ui_widgets);
|
.set(self.ids.race_description, ui_widgets);
|
||||||
// Races Descriptions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let CreationState::Weapon = self.creation_state {
|
if let CreationState::Weapon = self.creation_state {
|
||||||
@ -863,7 +862,7 @@ impl CharSelectionUi {
|
|||||||
.font_size(28)
|
.font_size(28)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(self.ids.select_window_title, ui_widgets);
|
.set(self.ids.select_window_title, ui_widgets);
|
||||||
// BG for Alignment
|
// Alignment
|
||||||
Rectangle::fill_with([470.0, 60.0], color::TRANSPARENT)
|
Rectangle::fill_with([470.0, 60.0], color::TRANSPARENT)
|
||||||
.mid_top_with_margin_on(self.ids.creation_window, 180.0)
|
.mid_top_with_margin_on(self.ids.creation_window, 180.0)
|
||||||
.set(self.ids.weapon_bg, ui_widgets);
|
.set(self.ids.weapon_bg, ui_widgets);
|
||||||
@ -997,7 +996,7 @@ impl CharSelectionUi {
|
|||||||
self.character_body.weapon = Weapon::Staff;
|
self.character_body.weapon = Weapon::Staff;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Load these from files (or from the server???)
|
// TODO: Load these from files (or from the server???).
|
||||||
const SWORDSHIELD_DESC: &str = " MISSING ";
|
const SWORDSHIELD_DESC: &str = " MISSING ";
|
||||||
const DAGGERS_DESC: &str = " MISSING ";
|
const DAGGERS_DESC: &str = " MISSING ";
|
||||||
const SWORD_DESC: &str = " MISSING ";
|
const SWORD_DESC: &str = " MISSING ";
|
||||||
@ -1028,12 +1027,11 @@ impl CharSelectionUi {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.wrap_by_word()
|
.wrap_by_word()
|
||||||
.set(self.ids.race_description, ui_widgets);
|
.set(self.ids.race_description, ui_widgets);
|
||||||
// Races Descriptions
|
|
||||||
}
|
}
|
||||||
// 3 states/windows: 1.Skin & Eyes 2.Hair 3.Accessories
|
// 3 states/windows: 1: Skin & Eyes 2: Hair 3: Accessories
|
||||||
// If one state is activated the other ones collapse
|
// If one state is activated, the other ones collapse.
|
||||||
// The title bar is the button to unfold/collapse the windows
|
// The title bar is the button to unfold/collapse the windows.
|
||||||
// The BG Frame can be stretched to the needed size
|
// The BG Frame can be stretched to the needed size.
|
||||||
|
|
||||||
// Window BG
|
// Window BG
|
||||||
if let CreationState::Body(state) = self.creation_state {
|
if let CreationState::Body(state) = self.creation_state {
|
||||||
@ -1093,7 +1091,7 @@ impl CharSelectionUi {
|
|||||||
{
|
{
|
||||||
self.creation_state = CreationState::Body(BodyPart::Accessories);
|
self.creation_state = CreationState::Body(BodyPart::Accessories);
|
||||||
}
|
}
|
||||||
} // State 1 fin
|
}
|
||||||
|
|
||||||
// Hair Open
|
// Hair Open
|
||||||
BodyPart::Hair => {
|
BodyPart::Hair => {
|
||||||
@ -1144,7 +1142,7 @@ impl CharSelectionUi {
|
|||||||
{
|
{
|
||||||
self.creation_state = CreationState::Body(BodyPart::Accessories);
|
self.creation_state = CreationState::Body(BodyPart::Accessories);
|
||||||
}
|
}
|
||||||
} // State 2 fin
|
}
|
||||||
|
|
||||||
// Open: Accessories
|
// Open: Accessories
|
||||||
BodyPart::Accessories => {
|
BodyPart::Accessories => {
|
||||||
@ -1195,10 +1193,10 @@ impl CharSelectionUi {
|
|||||||
{
|
{
|
||||||
self.creation_state = CreationState::Body(BodyPart::Accessories);
|
self.creation_state = CreationState::Body(BodyPart::Accessories);
|
||||||
}
|
}
|
||||||
} // State 3 fin
|
}
|
||||||
} // match fin
|
}
|
||||||
|
|
||||||
// Body Customization Window Contents ////////////////////////
|
// Body Customization Window Contents
|
||||||
match state {
|
match state {
|
||||||
BodyPart::SkinEyes => {
|
BodyPart::SkinEyes => {
|
||||||
// Skin Color: Text, Brightness Slider, Picker
|
// Skin Color: Text, Brightness Slider, Picker
|
||||||
@ -1207,18 +1205,18 @@ impl CharSelectionUi {
|
|||||||
.font_size(25)
|
.font_size(25)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(self.ids.skin_color_text, ui_widgets);
|
.set(self.ids.skin_color_text, ui_widgets);
|
||||||
// TODO: Align Buttons here
|
// TODO: Align Buttons here.
|
||||||
// They set an i32 to a value from 0-14
|
// Users set a variable to a value from 0-14.
|
||||||
// Depending on the race another color will be chosen
|
// Depending on the race another color will be chosen.
|
||||||
// Here only the BG image changes depending on the race.
|
// Only the BG image (190x114 -> 2px border!) changes depending on the race.
|
||||||
Rectangle::fill_with([192.0, 116.0], color::WHITE)
|
Rectangle::fill_with([192.0, 116.0], color::WHITE)
|
||||||
.top_right_with_margins_on(self.ids.skin_eyes_window, 60.0, 30.0)
|
.top_right_with_margins_on(self.ids.skin_eyes_window, 60.0, 30.0)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(self.ids.skin_rect, ui_widgets);
|
.set(self.ids.skin_rect, ui_widgets);
|
||||||
|
|
||||||
// TODO:Slider
|
// TODO: Slider
|
||||||
// Sliders actually change the Alpha-Level of the main colour chosen above
|
// Sliders actually change the Alpha-Level of the main colour chosen above.
|
||||||
// -> They will appear "brighter", therefore the sliders are labeled "Brightness"
|
// -> They will appear "brighter", therefore the sliders are labeled "Brightness".
|
||||||
Image::new(self.imgs.slider_range)
|
Image::new(self.imgs.slider_range)
|
||||||
.w_h(208.0, 12.0)
|
.w_h(208.0, 12.0)
|
||||||
.bottom_left_with_margins_on(self.ids.skin_rect, 10.0, -255.0)
|
.bottom_left_with_margins_on(self.ids.skin_rect, 10.0, -255.0)
|
||||||
@ -1241,17 +1239,16 @@ impl CharSelectionUi {
|
|||||||
.font_size(25)
|
.font_size(25)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(self.ids.eye_color_text, ui_widgets);
|
.set(self.ids.eye_color_text, ui_widgets);
|
||||||
// TODO: Align 16 Buttons here
|
// TODO: Align 16 Buttons here.
|
||||||
//
|
// Users set a variable to a value from 0-14.
|
||||||
// They set a variable to a value from 0-14
|
// Depending on the race another color will be chosen.
|
||||||
// Depending on the race another color will be chosen
|
|
||||||
// Only the BG image (190x114 -> 2px border!) changes depending on the race.
|
// Only the BG image (190x114 -> 2px border!) changes depending on the race.
|
||||||
Rectangle::fill_with([192.0, 116.0], color::WHITE)
|
Rectangle::fill_with([192.0, 116.0], color::WHITE)
|
||||||
.top_right_with_margins_on(self.ids.skin_eyes_window, 186.0, 30.0)
|
.top_right_with_margins_on(self.ids.skin_eyes_window, 186.0, 30.0)
|
||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(self.ids.eyes_rect, ui_widgets);
|
.set(self.ids.eyes_rect, ui_widgets);
|
||||||
|
|
||||||
// TODO:Slider
|
// TODO: Slider
|
||||||
|
|
||||||
Image::new(self.imgs.slider_range)
|
Image::new(self.imgs.slider_range)
|
||||||
.w_h(208.0, 12.0)
|
.w_h(208.0, 12.0)
|
||||||
@ -1270,7 +1267,7 @@ impl CharSelectionUi {
|
|||||||
.set(self.ids.eye_color_slider_text, ui_widgets);
|
.set(self.ids.eye_color_slider_text, ui_widgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hair ///////////////////////////////////////////////////////
|
// Hair
|
||||||
|
|
||||||
// Hair Styles -> Arrows
|
// Hair Styles -> Arrows
|
||||||
// Hair Color -> Picker
|
// Hair Color -> Picker
|
||||||
@ -1352,7 +1349,7 @@ impl CharSelectionUi {
|
|||||||
.set(self.ids.eyebrow_arrow_l, ui_widgets)
|
.set(self.ids.eyebrow_arrow_l, ui_widgets)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
{};
|
{};
|
||||||
// Beard -> Only active when "male" was chosen
|
// Beard -> Only active if "male" was chosen.
|
||||||
if let BodyType::Male = self.character_body.body_type {
|
if let BodyType::Male = self.character_body.body_type {
|
||||||
Text::new("Beard Style")
|
Text::new("Beard Style")
|
||||||
.mid_top_with_margin_on(self.ids.hair_window, 340.0)
|
.mid_top_with_margin_on(self.ids.hair_window, 340.0)
|
||||||
@ -1378,9 +1375,9 @@ impl CharSelectionUi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessories ///////////////////////////////
|
// Accessories
|
||||||
|
|
||||||
// Accessory Picker -> Arrows (Name Changes with race!)
|
// Accessory Picker -> Arrows (Name changes with race!)
|
||||||
// Color -> Picker
|
// Color -> Picker
|
||||||
// Brightness -> Slider
|
// Brightness -> Slider
|
||||||
BodyPart::Accessories => {
|
BodyPart::Accessories => {
|
||||||
@ -1450,7 +1447,7 @@ impl CharSelectionUi {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.set(self.ids.warpaint_slider_text, ui_widgets);
|
.set(self.ids.warpaint_slider_text, ui_widgets);
|
||||||
} // Human
|
}
|
||||||
Race::Orc => {
|
Race::Orc => {
|
||||||
Text::new("Head Band")
|
Text::new("Head Band")
|
||||||
.mid_top_with_margin_on(self.ids.accessories_window, 60.0)
|
.mid_top_with_margin_on(self.ids.accessories_window, 60.0)
|
||||||
@ -1516,7 +1513,7 @@ impl CharSelectionUi {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.set(self.ids.warpaint_slider_text, ui_widgets);
|
.set(self.ids.warpaint_slider_text, ui_widgets);
|
||||||
} // Orc
|
}
|
||||||
Race::Elf => {
|
Race::Elf => {
|
||||||
Text::new("Tribe Markings")
|
Text::new("Tribe Markings")
|
||||||
.mid_top_with_margin_on(self.ids.accessories_window, 60.0)
|
.mid_top_with_margin_on(self.ids.accessories_window, 60.0)
|
||||||
@ -1582,7 +1579,7 @@ impl CharSelectionUi {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.set(self.ids.warpaint_slider_text, ui_widgets);
|
.set(self.ids.warpaint_slider_text, ui_widgets);
|
||||||
} // Elf
|
}
|
||||||
Race::Dwarf => {
|
Race::Dwarf => {
|
||||||
Text::new("War Paint")
|
Text::new("War Paint")
|
||||||
.mid_top_with_margin_on(self.ids.accessories_window, 60.0)
|
.mid_top_with_margin_on(self.ids.accessories_window, 60.0)
|
||||||
@ -1648,7 +1645,7 @@ impl CharSelectionUi {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.set(self.ids.warpaint_slider_text, ui_widgets);
|
.set(self.ids.warpaint_slider_text, ui_widgets);
|
||||||
} // Dwarf
|
}
|
||||||
Race::Undead => {
|
Race::Undead => {
|
||||||
Text::new("Teeth")
|
Text::new("Teeth")
|
||||||
.mid_top_with_margin_on(self.ids.accessories_window, 60.0)
|
.mid_top_with_margin_on(self.ids.accessories_window, 60.0)
|
||||||
@ -1714,7 +1711,7 @@ impl CharSelectionUi {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.set(self.ids.warpaint_slider_text, ui_widgets);
|
.set(self.ids.warpaint_slider_text, ui_widgets);
|
||||||
} // Undead
|
}
|
||||||
Race::Danari => {
|
Race::Danari => {
|
||||||
Text::new("Horns")
|
Text::new("Horns")
|
||||||
.mid_top_with_margin_on(self.ids.accessories_window, 60.0)
|
.mid_top_with_margin_on(self.ids.accessories_window, 60.0)
|
||||||
@ -1780,12 +1777,12 @@ impl CharSelectionUi {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.font_size(14)
|
.font_size(14)
|
||||||
.set(self.ids.warpaint_slider_text, ui_widgets);
|
.set(self.ids.warpaint_slider_text, ui_widgets);
|
||||||
} // Danari
|
}
|
||||||
} // match Race fin
|
} // match Race fin
|
||||||
} // Accessories fin
|
}
|
||||||
} // Body Customization Fin
|
}
|
||||||
} // CreationState::Body Fin
|
}
|
||||||
} // Char Creation fin
|
}
|
||||||
|
|
||||||
events
|
events
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,17 @@ use std::{
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
// Error parsing input string or error resolving host name
|
// Error parsing input string or error resolving host name.
|
||||||
BadAddress(std::io::Error),
|
BadAddress(std::io::Error),
|
||||||
// Parsing yielded an empty iterator (specifically to_socket_addrs())
|
// Parsing yielded an empty iterator (specifically to_socket_addrs()).
|
||||||
NoAddress,
|
NoAddress,
|
||||||
// Parsing/host name resolution successful but could not connect
|
// Parsing/host name resolution successful but could not connect.
|
||||||
ConnectionFailed(ClientError),
|
ConnectionFailed(ClientError),
|
||||||
ClientCrashed,
|
ClientCrashed,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to asynchronusly parse the server address, resolve host names, and create the client (which involves establishing a connection to the server)
|
// Used to asynchronously parse the server address, resolve host names,
|
||||||
|
// and create the client (which involves establishing a connection to the server).
|
||||||
pub struct ClientInit {
|
pub struct ClientInit {
|
||||||
rx: Receiver<Result<Client, Error>>,
|
rx: Receiver<Result<Client, Error>>,
|
||||||
}
|
}
|
||||||
@ -34,19 +35,20 @@ impl ClientInit {
|
|||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
// Sleep the thread to wait for the single-player server to start up
|
// Sleep the thread to wait for the single-player server to start up.
|
||||||
if wait {
|
if wait {
|
||||||
thread::sleep(Duration::from_millis(500));
|
thread::sleep(Duration::from_millis(500));
|
||||||
}
|
}
|
||||||
// Parses ip address or resolves hostname
|
// Parse ip address or resolves hostname.
|
||||||
// Note: if you use an ipv6 address the number after the last colon will be used as the port unless you use [] around the address
|
// Note: if you use an ipv6 address, the number after the last colon will be used
|
||||||
|
// as the port unless you use [] around the address.
|
||||||
match server_address
|
match server_address
|
||||||
.to_socket_addrs()
|
.to_socket_addrs()
|
||||||
.or((server_address.as_ref(), default_port).to_socket_addrs())
|
.or((server_address.as_ref(), default_port).to_socket_addrs())
|
||||||
{
|
{
|
||||||
Ok(socket_adders) => {
|
Ok(socket_address) => {
|
||||||
let (first_addrs, second_addrs) =
|
let (first_addrs, second_addrs) =
|
||||||
socket_adders.partition::<Vec<_>, _>(|a| a.is_ipv6() == prefer_ipv6);
|
socket_address.partition::<Vec<_>, _>(|a| a.is_ipv6() == prefer_ipv6);
|
||||||
|
|
||||||
let mut last_err = None;
|
let mut last_err = None;
|
||||||
|
|
||||||
@ -59,11 +61,11 @@ impl ClientInit {
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
match err {
|
match err {
|
||||||
// assume connection failed and try next address
|
// Assume the connection failed and try next address.
|
||||||
ClientError::Network(_) => {
|
ClientError::Network(_) => {
|
||||||
last_err = Some(Error::ConnectionFailed(err))
|
last_err = Some(Error::ConnectionFailed(err))
|
||||||
}
|
}
|
||||||
// TODO: handle error?
|
// TODO: Handle errors?
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"Unexpected non-network error when creating client: {:?}",
|
"Unexpected non-network error when creating client: {:?}",
|
||||||
err
|
err
|
||||||
@ -72,11 +74,11 @@ impl ClientInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Parsing/host name resolution successful but no connection succeeded
|
// Parsing/host name resolution successful but no connection succeeded.
|
||||||
let _ = tx.send(Err(last_err.unwrap_or(Error::NoAddress)));
|
let _ = tx.send(Err(last_err.unwrap_or(Error::NoAddress)));
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// Error parsing input string or error resolving host name
|
// Error parsing input string or error resolving host name.
|
||||||
let _ = tx.send(Err(Error::BadAddress(err)));
|
let _ = tx.send(Err(Error::BadAddress(err)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,8 +86,8 @@ impl ClientInit {
|
|||||||
|
|
||||||
ClientInit { rx }
|
ClientInit { rx }
|
||||||
}
|
}
|
||||||
// Returns None is the thread is still running
|
/// Poll if the thread is complete.
|
||||||
// Otherwise returns the Result of client creation
|
/// Returns None if the thread is still running, otherwise returns the Result of client creation.
|
||||||
pub fn poll(&self) -> Option<Result<Client, Error>> {
|
pub fn poll(&self) -> Option<Result<Client, Error>> {
|
||||||
match self.rx.try_recv() {
|
match self.rx.try_recv() {
|
||||||
Ok(result) => Some(result),
|
Ok(result) => Some(result),
|
||||||
|
@ -21,7 +21,7 @@ pub struct MainMenuState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MainMenuState {
|
impl MainMenuState {
|
||||||
/// Create a new `MainMenuState`
|
/// Create a new `MainMenuState`.
|
||||||
pub fn new(global_state: &mut GlobalState) -> Self {
|
pub fn new(global_state: &mut GlobalState) -> Self {
|
||||||
Self {
|
Self {
|
||||||
main_menu_ui: MainMenuUi::new(global_state),
|
main_menu_ui: MainMenuUi::new(global_state),
|
||||||
@ -41,29 +41,29 @@ const BG_COLOR: Rgba<f32> = Rgba {
|
|||||||
|
|
||||||
impl PlayState for MainMenuState {
|
impl PlayState for MainMenuState {
|
||||||
fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult {
|
fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult {
|
||||||
// Set up an fps clock
|
// Set up an fps clock.
|
||||||
let mut clock = Clock::new();
|
let mut clock = Clock::new();
|
||||||
|
|
||||||
// Used for client creation
|
// Used for client creation.
|
||||||
let mut client_init: Option<ClientInit> = None;
|
let mut client_init: Option<ClientInit> = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Handle window events
|
// Handle window events.
|
||||||
for event in global_state.window.fetch_events() {
|
for event in global_state.window.fetch_events() {
|
||||||
match event {
|
match event {
|
||||||
Event::Close => return PlayStateResult::Shutdown,
|
Event::Close => return PlayStateResult::Shutdown,
|
||||||
// Pass events to ui
|
// Pass events to ui.
|
||||||
Event::Ui(event) => {
|
Event::Ui(event) => {
|
||||||
self.main_menu_ui.handle_event(event);
|
self.main_menu_ui.handle_event(event);
|
||||||
}
|
}
|
||||||
// Ignore all other events
|
// Ignore all other events.
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global_state.window.renderer_mut().clear(BG_COLOR);
|
global_state.window.renderer_mut().clear(BG_COLOR);
|
||||||
|
|
||||||
// Poll client creation
|
// Poll client creation.
|
||||||
match client_init.as_ref().and_then(|init| init.poll()) {
|
match client_init.as_ref().and_then(|init| init.poll()) {
|
||||||
Some(Ok(client)) => {
|
Some(Ok(client)) => {
|
||||||
self.main_menu_ui.connected();
|
self.main_menu_ui.connected();
|
||||||
@ -86,7 +86,7 @@ impl PlayState for MainMenuState {
|
|||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maintain the UI
|
// Maintain the UI.
|
||||||
for event in self.main_menu_ui.maintain(global_state) {
|
for event in self.main_menu_ui.maintain(global_state) {
|
||||||
match event {
|
match event {
|
||||||
MainMenuEvent::LoginAttempt {
|
MainMenuEvent::LoginAttempt {
|
||||||
@ -98,9 +98,9 @@ impl PlayState for MainMenuState {
|
|||||||
if !net_settings.servers.contains(&server_address) {
|
if !net_settings.servers.contains(&server_address) {
|
||||||
net_settings.servers.push(server_address.clone());
|
net_settings.servers.push(server_address.clone());
|
||||||
}
|
}
|
||||||
// TODO: Handle this result
|
// TODO: Handle this result.
|
||||||
global_state.settings.save_to_file();
|
global_state.settings.save_to_file();
|
||||||
// Don't try to connect if there is already a connection in progress
|
// Don't try to connect if there is already a connection in progress.
|
||||||
client_init = client_init.or(Some(ClientInit::new(
|
client_init = client_init.or(Some(ClientInit::new(
|
||||||
(server_address, DEFAULT_PORT, false),
|
(server_address, DEFAULT_PORT, false),
|
||||||
(comp::Player::new(username.clone()), 300),
|
(comp::Player::new(username.clone()), 300),
|
||||||
@ -114,17 +114,17 @@ impl PlayState for MainMenuState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the UI to the screen
|
// Draw the UI to the screen.
|
||||||
self.main_menu_ui.render(global_state.window.renderer_mut());
|
self.main_menu_ui.render(global_state.window.renderer_mut());
|
||||||
|
|
||||||
// Finish the frame
|
// Finish the frame.
|
||||||
global_state.window.renderer_mut().flush();
|
global_state.window.renderer_mut().flush();
|
||||||
global_state
|
global_state
|
||||||
.window
|
.window
|
||||||
.swap_buffers()
|
.swap_buffers()
|
||||||
.expect("Failed to swap window buffers");
|
.expect("Failed to swap window buffers!");
|
||||||
|
|
||||||
// Wait for the next tick
|
// Wait for the next tick.
|
||||||
clock.tick(Duration::from_millis(1000 / FPS));
|
clock.tick(Duration::from_millis(1000 / FPS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ pub struct StartSingleplayerState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StartSingleplayerState {
|
impl StartSingleplayerState {
|
||||||
/// Create a new `MainMenuState`
|
/// Create a new `MainMenuState`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let (singleplayer, sock) = Singleplayer::new();
|
let (singleplayer, sock) = Singleplayer::new();
|
||||||
|
|
||||||
@ -34,13 +34,12 @@ impl PlayState for StartSingleplayerState {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Client creation
|
// Create the client.
|
||||||
let client = loop {
|
let client = loop {
|
||||||
match client_init.poll() {
|
match client_init.poll() {
|
||||||
Some(Ok(client)) => break client,
|
Some(Ok(client)) => break client,
|
||||||
// An error occured!
|
|
||||||
Some(Err(err)) => {
|
Some(Err(err)) => {
|
||||||
warn!("Failed to start singleplayer server: {:?}", err);
|
warn!("Failed to start single-player server: {:?}", err);
|
||||||
return PlayStateResult::Pop;
|
return PlayStateResult::Pop;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -52,7 +51,7 @@ impl PlayState for StartSingleplayerState {
|
|||||||
if !net_settings.servers.contains(&server_address) {
|
if !net_settings.servers.contains(&server_address) {
|
||||||
net_settings.servers.push(server_address.clone());
|
net_settings.servers.push(server_address.clone());
|
||||||
}
|
}
|
||||||
// TODO: Handle this result
|
// TODO: Handle this result.
|
||||||
global_state.settings.save_to_file();
|
global_state.settings.save_to_file();
|
||||||
|
|
||||||
PlayStateResult::Push(Box::new(CharSelectionState::new(
|
PlayStateResult::Push(Box::new(CharSelectionState::new(
|
||||||
@ -65,6 +64,6 @@ impl PlayState for StartSingleplayerState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"Starting Singleplayer"
|
"Starting Single-Player"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ widget_ids! {
|
|||||||
singleplayer_text,
|
singleplayer_text,
|
||||||
usrnm_bg,
|
usrnm_bg,
|
||||||
srvr_bg,
|
srvr_bg,
|
||||||
// Serverlist
|
// Server list
|
||||||
servers_button,
|
servers_button,
|
||||||
servers_frame,
|
servers_frame,
|
||||||
servers_text,
|
servers_text,
|
||||||
@ -237,7 +237,7 @@ impl MainMenuUi {
|
|||||||
|
|
||||||
let netsettings = &global_state.settings.networking;
|
let netsettings = &global_state.settings.networking;
|
||||||
|
|
||||||
// TODO: draw scroll bar or remove it
|
// TODO: Draw scroll bar or remove it.
|
||||||
let (mut items, scrollbar) = List::flow_down(netsettings.servers.len())
|
let (mut items, scrollbar) = List::flow_down(netsettings.servers.len())
|
||||||
.top_left_with_margins_on(self.ids.servers_frame, 0.0, 5.0)
|
.top_left_with_margins_on(self.ids.servers_frame, 0.0, 5.0)
|
||||||
.w_h(400.0, 300.0)
|
.w_h(400.0, 300.0)
|
||||||
|
@ -7,7 +7,7 @@ use crate::render::{
|
|||||||
Pipeline,
|
Pipeline,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Given a volume, a position and the cardinal directions, compute each vertex' AO value
|
/// Given volume, position, and cardinal directions, compute each vertex's AO value.
|
||||||
/// `dirs` should be a slice of length 5 so that the sliding window of size 2 over the slice
|
/// `dirs` should be a slice of length 5 so that the sliding window of size 2 over the slice
|
||||||
/// yields each vertex' adjacent positions.
|
/// yields each vertex' adjacent positions.
|
||||||
fn get_ao_quad<V: ReadVol>(vol: &V, pos: Vec3<i32>, dirs: &[Vec3<i32>]) -> Vec4<f32> {
|
fn get_ao_quad<V: ReadVol>(vol: &V, pos: Vec3<i32>, dirs: &[Vec3<i32>]) -> Vec4<f32> {
|
||||||
@ -29,7 +29,7 @@ fn get_ao_quad<V: ReadVol>(vol: &V, pos: Vec3<i32>, dirs: &[Vec3<i32>]) -> Vec4<
|
|||||||
.get(pos + offs[0] + offs[1])
|
.get(pos + offs[0] + offs[1])
|
||||||
.map(|v| !v.is_empty())
|
.map(|v| !v.is_empty())
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
// Map both 1 and 2 neighbors to 0.5 occlusion
|
// Map both 1 and 2 neighbors to 0.5 occlusion.
|
||||||
if s1 || s2 || corner {
|
if s1 || s2 || corner {
|
||||||
0.5
|
0.5
|
||||||
} else {
|
} else {
|
||||||
|
@ -12,7 +12,7 @@ pub struct Consts<T: Copy + gfx::traits::Pod> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy + gfx::traits::Pod> Consts<T> {
|
impl<T: Copy + gfx::traits::Pod> Consts<T> {
|
||||||
/// Create a new `Const<T>`
|
/// Create a new `Const<T>`.
|
||||||
pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Self {
|
pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
buf: factory.create_constant_buffer(len),
|
buf: factory.create_constant_buffer(len),
|
||||||
|
@ -8,12 +8,12 @@ pub struct Mesh<P: Pipeline> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Pipeline> Mesh<P> {
|
impl<P: Pipeline> Mesh<P> {
|
||||||
/// Create a new `Mesh`
|
/// Create a new `Mesh`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { verts: vec![] }
|
Self { verts: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear vertices, allows reusing allocated memory of the underlying Vec
|
/// Clear vertices, allows reusing allocated memory of the underlying Vec.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.verts.clear();
|
self.verts.clear();
|
||||||
}
|
}
|
||||||
@ -50,12 +50,12 @@ impl<P: Pipeline> Mesh<P> {
|
|||||||
self.verts.push(quad.a);
|
self.verts.push(quad.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push the vertices of another mesh onto the end of this mesh
|
/// Push the vertices of another mesh onto the end of this mesh.
|
||||||
pub fn push_mesh(&mut self, other: &Mesh<P>) {
|
pub fn push_mesh(&mut self, other: &Mesh<P>) {
|
||||||
self.verts.extend_from_slice(other.vertices());
|
self.verts.extend_from_slice(other.vertices());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push the vertices of another mesh onto the end of this mesh
|
/// Map and push the vertices of another mesh onto the end of this mesh.
|
||||||
pub fn push_mesh_map<F: FnMut(P::Vertex) -> P::Vertex>(&mut self, other: &Mesh<P>, mut f: F) {
|
pub fn push_mesh_map<F: FnMut(P::Vertex) -> P::Vertex>(&mut self, other: &Mesh<P>, mut f: F) {
|
||||||
// Reserve enough space in our Vec. This isn't necessary, but it tends to reduce the number
|
// Reserve enough space in our Vec. This isn't necessary, but it tends to reduce the number
|
||||||
// of required (re)allocations.
|
// of required (re)allocations.
|
||||||
|
@ -33,7 +33,7 @@ use gfx_device_gl as gfx_backend;
|
|||||||
// Library
|
// Library
|
||||||
use gfx;
|
use gfx;
|
||||||
|
|
||||||
/// Used to represent one of many possible errors that may be omitted by the rendering subsystem
|
/// Used to represent one of many possible errors that may be omitted by the rendering subsystem.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RenderError {
|
pub enum RenderError {
|
||||||
PipelineError(gfx::PipelineStateError<String>),
|
PipelineError(gfx::PipelineStateError<String>),
|
||||||
|
@ -28,7 +28,7 @@ impl<P: Pipeline> Model<P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a mesh on the GPU which can be updated dynamically
|
/// Represents a mesh on the GPU which can be updated dynamically.
|
||||||
pub struct DynamicModel<P: Pipeline> {
|
pub struct DynamicModel<P: Pipeline> {
|
||||||
pub vbuf: gfx::handle::Buffer<gfx_backend::Resources, P::Vertex>,
|
pub vbuf: gfx::handle::Buffer<gfx_backend::Resources, P::Vertex>,
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ impl<P: Pipeline> DynamicModel<P> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a model with a slice of a portion of this model to send to the renderer
|
/// Create a model with a slice of a portion of this model to send to the renderer.
|
||||||
pub fn submodel(&self, range: Range<usize>) -> Model<P> {
|
pub fn submodel(&self, range: Range<usize>) -> Model<P> {
|
||||||
Model {
|
Model {
|
||||||
vbuf: self.vbuf.clone(),
|
vbuf: self.vbuf.clone(),
|
||||||
|
@ -23,9 +23,9 @@ gfx_defines! {
|
|||||||
proj_mat: [[f32; 4]; 4] = "proj_mat",
|
proj_mat: [[f32; 4]; 4] = "proj_mat",
|
||||||
cam_pos: [f32; 4] = "cam_pos",
|
cam_pos: [f32; 4] = "cam_pos",
|
||||||
focus_pos: [f32; 4] = "focus_pos",
|
focus_pos: [f32; 4] = "focus_pos",
|
||||||
// TODO: Fix whatever alignment issue requires these uniforms to be aligned
|
// TODO: Fix whatever alignment issue requires these uniforms to be aligned.
|
||||||
view_distance: [f32; 4] = "view_distance",
|
view_distance: [f32; 4] = "view_distance",
|
||||||
time_of_day: [f32; 4] = "time_of_day", // TODO: Make this f64
|
time_of_day: [f32; 4] = "time_of_day", // TODO: Make this f64.
|
||||||
tick: [f32; 4] = "tick",
|
tick: [f32; 4] = "tick",
|
||||||
screen_res: [f32; 4] = "screen_res",
|
screen_res: [f32; 4] = "screen_res",
|
||||||
}
|
}
|
||||||
|
@ -67,8 +67,7 @@ pub struct Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
/// Create a new `Renderer` from a variety of backend-specific components and the window
|
/// Create a new `Renderer` from a variety of backend-specific components and the window targets.
|
||||||
/// targets.
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
device: gfx_backend::Device,
|
device: gfx_backend::Device,
|
||||||
mut factory: gfx_backend::Factory,
|
mut factory: gfx_backend::Factory,
|
||||||
@ -186,11 +185,11 @@ impl Renderer {
|
|||||||
(&mut self.win_color_view, &mut self.win_depth_view)
|
(&mut self.win_color_view, &mut self.win_depth_view)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resize internal render targets to match window render target dimensions
|
/// Resize internal render targets to match window render target dimensions.
|
||||||
pub fn on_resize(&mut self) -> Result<(), RenderError> {
|
pub fn on_resize(&mut self) -> Result<(), RenderError> {
|
||||||
let dims = self.win_color_view.get_dimensions();
|
let dims = self.win_color_view.get_dimensions();
|
||||||
|
|
||||||
// Panics when creating texture with w,h of 0,0
|
// Avoid panics when creating texture with w,h of 0,0.
|
||||||
if dims.0 != 0 && dims.1 != 0 {
|
if dims.0 != 0 && dims.1 != 0 {
|
||||||
let (tgt_color_view, tgt_depth_view, tgt_color_res) =
|
let (tgt_color_view, tgt_depth_view, tgt_color_res) =
|
||||||
Self::create_rt_views(&mut self.factory, (dims.0, dims.1))?;
|
Self::create_rt_views(&mut self.factory, (dims.0, dims.1))?;
|
||||||
@ -224,7 +223,7 @@ impl Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Queue the clearing of the color and depth targets ready for a new frame to be rendered.
|
/// Queue the clearing of the color and depth targets ready for a new frame to be rendered.
|
||||||
/// TODO: Make a version of this that doesn't clear the colour target for speed
|
/// TODO: Make a version of this that doesn't clear the colour target for speed.
|
||||||
pub fn clear(&mut self, col: Rgba<f32>) {
|
pub fn clear(&mut self, col: Rgba<f32>) {
|
||||||
self.encoder.clear(&self.tgt_color_view, col.into_array());
|
self.encoder.clear(&self.tgt_color_view, col.into_array());
|
||||||
self.encoder.clear_depth(&self.tgt_depth_view, 1.0);
|
self.encoder.clear_depth(&self.tgt_depth_view, 1.0);
|
||||||
@ -262,7 +261,7 @@ impl Renderer {
|
|||||||
Ok(Model::new(&mut self.factory, mesh))
|
Ok(Model::new(&mut self.factory, mesh))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new dynamic model with the specified size
|
/// Create a new dynamic model with the specified size.
|
||||||
pub fn create_dynamic_model<P: Pipeline>(
|
pub fn create_dynamic_model<P: Pipeline>(
|
||||||
&mut self,
|
&mut self,
|
||||||
size: usize,
|
size: usize,
|
||||||
@ -270,7 +269,7 @@ impl Renderer {
|
|||||||
DynamicModel::new(&mut self.factory, size)
|
DynamicModel::new(&mut self.factory, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update a dynamic model with a mesh and a offset
|
/// Update a dynamic model with a mesh and a offset.
|
||||||
pub fn update_model<P: Pipeline>(
|
pub fn update_model<P: Pipeline>(
|
||||||
&mut self,
|
&mut self,
|
||||||
model: &DynamicModel<P>,
|
model: &DynamicModel<P>,
|
||||||
@ -288,7 +287,7 @@ impl Renderer {
|
|||||||
Texture::new(&mut self.factory, image)
|
Texture::new(&mut self.factory, image)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new dynamic texture (gfx::memory::Usage::Dynamic) with the specified dimensions
|
/// Create a new dynamic texture (gfx::memory::Usage::Dynamic) with the specified dimensions.
|
||||||
pub fn create_dynamic_texture<P: Pipeline>(
|
pub fn create_dynamic_texture<P: Pipeline>(
|
||||||
&mut self,
|
&mut self,
|
||||||
dims: Vec2<u16>,
|
dims: Vec2<u16>,
|
||||||
@ -296,7 +295,7 @@ impl Renderer {
|
|||||||
Texture::new_dynamic(&mut self.factory, dims.x, dims.y)
|
Texture::new_dynamic(&mut self.factory, dims.x, dims.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update a texture with the provided offset, size, and data
|
/// Update a texture with the provided offset, size, and data.
|
||||||
pub fn update_texture<P: Pipeline>(
|
pub fn update_texture<P: Pipeline>(
|
||||||
&mut self,
|
&mut self,
|
||||||
texture: &Texture<P>,
|
texture: &Texture<P>,
|
||||||
@ -449,7 +448,7 @@ fn create_pipeline<'a, P: gfx::pso::PipelineInit>(
|
|||||||
},
|
},
|
||||||
pipe,
|
pipe,
|
||||||
)
|
)
|
||||||
// Do some funky things to work around an oddity in gfx's error ownership rules
|
// Do some funky things to work around an oddity in gfx's error ownership rules.
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
RenderError::PipelineError(match err {
|
RenderError::PipelineError(match err {
|
||||||
gfx::PipelineStateError::Program(err) => gfx::PipelineStateError::Program(err),
|
gfx::PipelineStateError::Program(err) => gfx::PipelineStateError::Program(err),
|
||||||
|
@ -89,7 +89,7 @@ impl<P: Pipeline> Texture<P> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates a texture with the given data (used for updating the glyph cache texture)
|
/// Update a texture with the given data (used for updating the glyph cache texture).
|
||||||
pub fn update(
|
pub fn update(
|
||||||
&self,
|
&self,
|
||||||
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
|
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
|
||||||
@ -113,7 +113,7 @@ impl<P: Pipeline> Texture<P> {
|
|||||||
)
|
)
|
||||||
.map_err(|err| RenderError::TexUpdateError(err))
|
.map_err(|err| RenderError::TexUpdateError(err))
|
||||||
}
|
}
|
||||||
/// Get dimensions of the represented image
|
/// Get dimensions of the represented image.
|
||||||
pub fn get_dimensions(&self) -> Vec2<u16> {
|
pub fn get_dimensions(&self) -> Vec2<u16> {
|
||||||
let (w, h, ..) = self.tex.get_info().kind.get_dimensions();
|
let (w, h, ..) = self.tex.get_info().kind.get_dimensions();
|
||||||
Vec2::new(w, h)
|
Vec2::new(w, h)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// TODO: Get rid of this ugliness
|
// TODO: Get rid of this ugliness.
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub fn arr_to_mat(arr: [f32; 16]) -> [[f32; 4]; 4] {
|
pub fn arr_to_mat(arr: [f32; 16]) -> [[f32; 4]; 4] {
|
||||||
[
|
[
|
||||||
|
@ -79,7 +79,7 @@ impl Camera {
|
|||||||
|
|
||||||
let proj_mat = Mat4::perspective_rh_no(self.fov, self.aspect, NEAR_PLANE, FAR_PLANE);
|
let proj_mat = Mat4::perspective_rh_no(self.fov, self.aspect, NEAR_PLANE, FAR_PLANE);
|
||||||
|
|
||||||
// TODO: Make this more efficient
|
// TODO: Make this more efficient.
|
||||||
let cam_pos = Vec3::from(view_mat.inverted() * Vec4::unit_w());
|
let cam_pos = Vec3::from(view_mat.inverted() * Vec4::unit_w());
|
||||||
|
|
||||||
(view_mat, proj_mat, cam_pos)
|
(view_mat, proj_mat, cam_pos)
|
||||||
@ -95,7 +95,7 @@ impl Camera {
|
|||||||
self.ori.z = (self.ori.z + delta.z) % (2.0 * PI);
|
self.ori.z = (self.ori.z + delta.z) % (2.0 * PI);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the orientation of the camera about its focus
|
/// Set the orientation of the camera about its focus.
|
||||||
pub fn set_orientation(&mut self, orientation: Vec3<f32>) {
|
pub fn set_orientation(&mut self, orientation: Vec3<f32>) {
|
||||||
// Wrap camera yaw
|
// Wrap camera yaw
|
||||||
self.ori.x = orientation.x % (2.0 * PI);
|
self.ori.x = orientation.x % (2.0 * PI);
|
||||||
@ -111,7 +111,7 @@ impl Camera {
|
|||||||
self.tgt_dist = (self.tgt_dist + delta).max(0.0);
|
self.tgt_dist = (self.tgt_dist + delta).max(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the distance of the camera from the target (i.e: zoom)
|
/// Set the distance of the camera from the target (i.e., zoom).
|
||||||
pub fn set_distance(&mut self, dist: f32) {
|
pub fn set_distance(&mut self, dist: f32) {
|
||||||
self.tgt_dist = dist;
|
self.tgt_dist = dist;
|
||||||
}
|
}
|
||||||
@ -146,7 +146,7 @@ impl Camera {
|
|||||||
self.aspect = if aspect.is_normal() { aspect } else { 1.0 };
|
self.aspect = if aspect.is_normal() { aspect } else { 1.0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the orientation of the camera
|
/// Get the orientation of the camera.
|
||||||
pub fn get_orientation(&self) -> Vec3<f32> {
|
pub fn get_orientation(&self) -> Vec3<f32> {
|
||||||
self.ori
|
self.ori
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ use common::{
|
|||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
actor::{
|
actor::{
|
||||||
Belt, Chest, Draw, Foot, Hand, Head, Pants, Pigchest, Pighead, Pigleg_l, Pigleg_r,
|
Belt, Chest, Draw, Foot, Hand, Head, Pants, PigChest, PigHead, PigLegL, PigLegR,
|
||||||
Shoulder, Weapon,
|
Shoulder, Weapon,
|
||||||
},
|
},
|
||||||
Body, HumanoidBody, QuadrupedBody,
|
Body, HumanoidBody, QuadrupedBody,
|
||||||
@ -75,12 +75,12 @@ impl FigureModelCache {
|
|||||||
None,
|
None,
|
||||||
],
|
],
|
||||||
Body::Quadruped(body) => [
|
Body::Quadruped(body) => [
|
||||||
Some(Self::load_pighead(body.pighead)),
|
Some(Self::load_pig_head(body.pig_head)),
|
||||||
Some(Self::load_pigchest(body.pigchest)),
|
Some(Self::load_pig_chest(body.pig_chest)),
|
||||||
Some(Self::load_piglf_leg(body.pigleg_l)),
|
Some(Self::load_pig_leg_lf(body.pig_leg_l)),
|
||||||
Some(Self::load_pigrf_leg(body.pigleg_r)),
|
Some(Self::load_pig_leg_rf(body.pig_leg_r)),
|
||||||
Some(Self::load_piglb_leg(body.pigleg_l)),
|
Some(Self::load_pig_leg_lb(body.pig_leg_l)),
|
||||||
Some(Self::load_pigrb_leg(body.pigleg_r)),
|
Some(Self::load_pig_leg_rb(body.pig_leg_r)),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
@ -117,15 +117,15 @@ impl FigureModelCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean(&mut self, tick: u64) {
|
pub fn clean(&mut self, tick: u64) {
|
||||||
// TODO: Don't hard-code this
|
// TODO: Don't hard-code this.
|
||||||
self.models
|
self.models
|
||||||
.retain(|_, (_, last_used)| *last_used + 60 > tick);
|
.retain(|_, (_, last_used)| *last_used + 60 > tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Don't make this public
|
// TODO: Don't make this public.
|
||||||
pub fn load_mesh(filename: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
pub fn load_mesh(filename: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
||||||
let fullpath: String = ["/voxygen/voxel/", filename].concat();
|
let full_path: String = ["/voxygen/voxel/", filename].concat();
|
||||||
Segment::from(assets::load_expect::<DotVoxData>(fullpath.as_str()).as_ref())
|
Segment::from(assets::load_expect::<DotVoxData>(full_path.as_str()).as_ref())
|
||||||
.generate_mesh(position)
|
.generate_mesh(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +205,7 @@ impl FigureModelCache {
|
|||||||
Self::load_mesh(
|
Self::load_mesh(
|
||||||
match weapon {
|
match weapon {
|
||||||
Weapon::Sword => "sword.vox",
|
Weapon::Sword => "sword.vox",
|
||||||
// TODO actually match against other weapons and set the right model
|
// TODO actually match against other weapons and set the right model.
|
||||||
_ => "sword.vox",
|
_ => "sword.vox",
|
||||||
},
|
},
|
||||||
Vec3::new(0.0, 0.0, -4.0),
|
Vec3::new(0.0, 0.0, -4.0),
|
||||||
@ -238,55 +238,55 @@ impl FigureModelCache {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_pighead(pighead: Pighead) -> Mesh<FigurePipeline> {
|
fn load_pig_head(pig_head: PigHead) -> Mesh<FigurePipeline> {
|
||||||
Self::load_mesh(
|
Self::load_mesh(
|
||||||
match pighead {
|
match pig_head {
|
||||||
Pighead::Default => "pighead.vox",
|
PigHead::Default => "pighead.vox",
|
||||||
},
|
},
|
||||||
Vec3::new(-6.0, 4.5, 3.0),
|
Vec3::new(-6.0, 4.5, 3.0),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_pigchest(pigchest: Pigchest) -> Mesh<FigurePipeline> {
|
fn load_pig_chest(pig_chest: PigChest) -> Mesh<FigurePipeline> {
|
||||||
Self::load_mesh(
|
Self::load_mesh(
|
||||||
match pigchest {
|
match pig_chest {
|
||||||
Pigchest::Default => "pigchest.vox",
|
PigChest::Default => "pigchest.vox",
|
||||||
},
|
},
|
||||||
Vec3::new(-5.0, 4.5, 0.0),
|
Vec3::new(-5.0, 4.5, 0.0),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_piglf_leg(pigleg_l: Pigleg_l) -> Mesh<FigurePipeline> {
|
fn load_pig_leg_lf(pig_leg_l: PigLegL) -> Mesh<FigurePipeline> {
|
||||||
|
Self::load_mesh(
|
||||||
|
match pig_leg_l {
|
||||||
|
PigLegL::Default => "pigleg_l.vox",
|
||||||
|
},
|
||||||
|
Vec3::new(0.0, -1.0, -1.5),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_pig_leg_rf(pig_leg_r: PigLegR) -> Mesh<FigurePipeline> {
|
||||||
|
Self::load_mesh(
|
||||||
|
match pig_leg_r {
|
||||||
|
PigLegR::Default => "pigleg_r.vox",
|
||||||
|
},
|
||||||
|
Vec3::new(0.0, -1.0, -1.5),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_pig_leg_lb(pigleg_l: PigLegL) -> Mesh<FigurePipeline> {
|
||||||
Self::load_mesh(
|
Self::load_mesh(
|
||||||
match pigleg_l {
|
match pigleg_l {
|
||||||
Pigleg_l::Default => "pigleg_l.vox",
|
PigLegL::Default => "pigleg_l.vox",
|
||||||
},
|
},
|
||||||
Vec3::new(0.0, -1.0, -1.5),
|
Vec3::new(0.0, -1.0, -1.5),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_pigrf_leg(pigleg_r: Pigleg_r) -> Mesh<FigurePipeline> {
|
fn load_pig_leg_rb(pig_leg_r: PigLegR) -> Mesh<FigurePipeline> {
|
||||||
Self::load_mesh(
|
Self::load_mesh(
|
||||||
match pigleg_r {
|
match pig_leg_r {
|
||||||
Pigleg_r::Default => "pigleg_r.vox",
|
PigLegR::Default => "pigleg_r.vox",
|
||||||
},
|
|
||||||
Vec3::new(0.0, -1.0, -1.5),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_piglb_leg(pigleg_l: Pigleg_l) -> Mesh<FigurePipeline> {
|
|
||||||
Self::load_mesh(
|
|
||||||
match pigleg_l {
|
|
||||||
Pigleg_l::Default => "pigleg_l.vox",
|
|
||||||
},
|
|
||||||
Vec3::new(0.0, -1.0, -1.5),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_pigrb_leg(pigleg_r: Pigleg_r) -> Mesh<FigurePipeline> {
|
|
||||||
Self::load_mesh(
|
|
||||||
match pigleg_r {
|
|
||||||
Pigleg_r::Default => "pigleg_r.vox",
|
|
||||||
},
|
},
|
||||||
Vec3::new(0.0, -1.0, -1.5),
|
Vec3::new(0.0, -1.0, -1.5),
|
||||||
)
|
)
|
||||||
@ -411,7 +411,7 @@ impl FigureMgr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear states that have dead entities
|
// Clear states that have dead entities.
|
||||||
self.character_states
|
self.character_states
|
||||||
.retain(|entity, _| ecs.entities().is_alive(*entity));
|
.retain(|entity, _| ecs.entities().is_alive(*entity));
|
||||||
self.quadruped_states
|
self.quadruped_states
|
||||||
|
@ -93,15 +93,15 @@ impl FigureModelCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean(&mut self, tick: u64) {
|
pub fn clean(&mut self, tick: u64) {
|
||||||
// TODO: Don't hard-code this
|
// TODO: Don't hard-code this.
|
||||||
self.models
|
self.models
|
||||||
.retain(|_, (_, last_used)| *last_used + 60 > tick);
|
.retain(|_, (_, last_used)| *last_used + 60 > tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Don't make this public
|
// TODO: Don't make this public.
|
||||||
pub fn load_mesh(filename: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
pub fn load_mesh(filename: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
||||||
let fullpath: String = ["/voxygen/voxel/", filename].concat();
|
let full_path: String = ["/voxygen/voxel/", filename].concat();
|
||||||
Segment::from(assets::load_expect::<DotVoxData>(fullpath.as_str()).as_ref())
|
Segment::from(assets::load_expect::<DotVoxData>(full_path.as_str()).as_ref())
|
||||||
.generate_mesh(position)
|
.generate_mesh(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ impl FigureModelCache {
|
|||||||
Self::load_mesh(
|
Self::load_mesh(
|
||||||
match weapon {
|
match weapon {
|
||||||
Weapon::Sword => "sword.vox",
|
Weapon::Sword => "sword.vox",
|
||||||
// TODO actually match against other weapons and set the right model
|
// TODO actually match against other weapons and set the right model.
|
||||||
_ => "sword.vox",
|
_ => "sword.vox",
|
||||||
},
|
},
|
||||||
Vec3::new(0.0, 0.0, -4.0),
|
Vec3::new(0.0, 0.0, -4.0),
|
||||||
@ -276,9 +276,9 @@ impl FigureMgr {
|
|||||||
state.skeleton.interpolate(&target_skeleton);
|
state.skeleton.interpolate(&target_skeleton);
|
||||||
|
|
||||||
state.update(renderer, pos.0, dir.0);
|
state.update(renderer, pos.0, dir.0);
|
||||||
} // TODO: Non-humanoid bodies
|
} // TODO: Non-humanoid bodies.
|
||||||
},
|
},
|
||||||
// TODO: Non-character actors
|
// TODO: Non-character actors.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,9 +308,9 @@ impl FigureMgr {
|
|||||||
state.bone_consts(),
|
state.bone_consts(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} // TODO: Non-humanoid bodies
|
} // TODO: Non-humanoid bodies.
|
||||||
},
|
},
|
||||||
// TODO: Non-character actors
|
// TODO: Non-character actors.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,15 +93,15 @@ impl FigureModelCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean(&mut self, tick: u64) {
|
pub fn clean(&mut self, tick: u64) {
|
||||||
// TODO: Don't hard-code this
|
// TODO: Don't hard-code this.
|
||||||
self.models
|
self.models
|
||||||
.retain(|_, (_, last_used)| *last_used + 60 > tick);
|
.retain(|_, (_, last_used)| *last_used + 60 > tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Don't make this public
|
// TODO: Don't make this public.
|
||||||
pub fn load_mesh(filename: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
pub fn load_mesh(filename: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
||||||
let fullpath: String = ["/voxygen/voxel/", filename].concat();
|
let full_path: String = ["/voxygen/voxel/", filename].concat();
|
||||||
Segment::from(assets::load_expect::<DotVoxData>(fullpath.as_str()).as_ref())
|
Segment::from(assets::load_expect::<DotVoxData>(full_path.as_str()).as_ref())
|
||||||
.generate_mesh(position)
|
.generate_mesh(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ impl FigureModelCache {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_lf_leg(leg_l: Leg_l) -> Mesh<FigurePipeline> {
|
fn load_leg_lf(leg_l: Leg_l) -> Mesh<FigurePipeline> {
|
||||||
Self::load_mesh(
|
Self::load_mesh(
|
||||||
match belt {
|
match belt {
|
||||||
Belt::Default => "pigleg_l.vox",
|
Belt::Default => "pigleg_l.vox",
|
||||||
@ -132,7 +132,7 @@ impl FigureModelCache {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_rf_leg(leg_R: Leg_r) -> Mesh<FigurePipeline> {
|
fn load_leg_rf(leg_r: Leg_r) -> Mesh<FigurePipeline> {
|
||||||
Self::load_mesh(
|
Self::load_mesh(
|
||||||
match pants {
|
match pants {
|
||||||
Pants::Default => "pigleg_r.vox",
|
Pants::Default => "pigleg_r.vox",
|
||||||
@ -141,7 +141,7 @@ impl FigureModelCache {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_lb_leg(leg_l: Leg_l) -> Mesh<FigurePipeline> {
|
fn load_leg_lb(leg_l: Leg_l) -> Mesh<FigurePipeline> {
|
||||||
Self::load_mesh(
|
Self::load_mesh(
|
||||||
match hand {
|
match hand {
|
||||||
Hand::Default => "pigleg_l.vox",
|
Hand::Default => "pigleg_l.vox",
|
||||||
@ -150,7 +150,7 @@ impl FigureModelCache {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_rb_leg(leg_R: Leg_r) -> Mesh<FigurePipeline> {
|
fn load_leg_rb(leg_r: Leg_r) -> Mesh<FigurePipeline> {
|
||||||
Self::load_mesh(
|
Self::load_mesh(
|
||||||
match hand {
|
match hand {
|
||||||
Hand::Default => "pigleg_r.vox",
|
Hand::Default => "pigleg_r.vox",
|
||||||
@ -214,7 +214,7 @@ impl FigureMgr {
|
|||||||
|
|
||||||
self.states
|
self.states
|
||||||
.retain(|entity, _| ecs.entities().is_alive(*entity));
|
.retain(|entity, _| ecs.entities().is_alive(*entity));
|
||||||
}
|
} // TODO: Place `render` into above impl and fix `maintain`.
|
||||||
|
|
||||||
pub fn render(
|
pub fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -20,7 +20,7 @@ use common::{comp, figure::Segment};
|
|||||||
use dot_vox;
|
use dot_vox;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
// TODO: Don't hard-code this
|
// TODO: Don't hard-code this.
|
||||||
const CURSOR_PAN_SCALE: f32 = 0.005;
|
const CURSOR_PAN_SCALE: f32 = 0.005;
|
||||||
|
|
||||||
struct Skybox {
|
struct Skybox {
|
||||||
@ -78,9 +78,9 @@ impl Scene {
|
|||||||
&mut self.camera
|
&mut self.camera
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle an incoming user input event (i.e: cursor moved, key pressed, window closed, etc.).
|
/// Handle an incoming user input event (e.g.: cursor moved, key pressed, window closed).
|
||||||
///
|
///
|
||||||
/// If the event is handled, return true
|
/// If the event is handled, return true.
|
||||||
pub fn handle_input_event(&mut self, event: Event) -> bool {
|
pub fn handle_input_event(&mut self, event: Event) -> bool {
|
||||||
match event {
|
match event {
|
||||||
// When the window is resized, change the camera's aspect ratio
|
// When the window is resized, change the camera's aspect ratio
|
||||||
@ -105,7 +105,7 @@ impl Scene {
|
|||||||
|
|
||||||
/// Maintain data such as GPU constant buffers, models, etc. To be called once per tick.
|
/// Maintain data such as GPU constant buffers, models, etc. To be called once per tick.
|
||||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||||
// Get player position
|
// Get player position.
|
||||||
let player_pos = client
|
let player_pos = client
|
||||||
.state()
|
.state()
|
||||||
.ecs()
|
.ecs()
|
||||||
@ -114,16 +114,16 @@ impl Scene {
|
|||||||
.map(|pos| pos.0)
|
.map(|pos| pos.0)
|
||||||
.unwrap_or(Vec3::zero());
|
.unwrap_or(Vec3::zero());
|
||||||
|
|
||||||
// Alter camera position to match player
|
// Alter camera position to match player.
|
||||||
self.camera.set_focus_pos(player_pos + Vec3::unit_z() * 2.1);
|
self.camera.set_focus_pos(player_pos + Vec3::unit_z() * 2.1);
|
||||||
|
|
||||||
// Tick camera for interpolation
|
// Tick camera for interpolation.
|
||||||
self.camera.update(client.state().get_time());
|
self.camera.update(client.state().get_time());
|
||||||
|
|
||||||
// Compute camera matrices
|
// Compute camera matrices.
|
||||||
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client);
|
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client);
|
||||||
|
|
||||||
// Update global constants
|
// Update global constants.
|
||||||
renderer
|
renderer
|
||||||
.update_consts(
|
.update_consts(
|
||||||
&mut self.globals,
|
&mut self.globals,
|
||||||
@ -140,22 +140,22 @@ impl Scene {
|
|||||||
)
|
)
|
||||||
.expect("Failed to update global constants");
|
.expect("Failed to update global constants");
|
||||||
|
|
||||||
// Maintain the terrain
|
// Maintain the terrain.
|
||||||
self.terrain.maintain(renderer, client);
|
self.terrain.maintain(renderer, client);
|
||||||
|
|
||||||
// Maintain the figures
|
// Maintain the figures.
|
||||||
self.figure_mgr.maintain(renderer, client);
|
self.figure_mgr.maintain(renderer, client);
|
||||||
|
|
||||||
// Remove unused figures
|
// Remove unused figures.
|
||||||
self.figure_mgr.clean(client.get_tick());
|
self.figure_mgr.clean(client.get_tick());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the scene using the provided `Renderer`
|
/// 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, client: &mut Client) {
|
||||||
// Render the skybox first (it appears over everything else so must be rendered first)
|
// Render the skybox first (it appears over everything else so must be rendered first).
|
||||||
renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals);
|
renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals);
|
||||||
|
|
||||||
// Render terrain and figures
|
// Render terrain and figures.
|
||||||
self.terrain.render(renderer, &self.globals);
|
self.terrain.render(renderer, &self.globals);
|
||||||
self.figure_mgr.render(renderer, client, &self.globals);
|
self.figure_mgr.render(renderer, client, &self.globals);
|
||||||
|
|
||||||
|
@ -19,14 +19,14 @@ struct ChunkMeshState {
|
|||||||
active_worker: bool,
|
active_worker: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type produced by mesh worker threads corresponding to the position and mesh of a chunk
|
/// A type produced by mesh worker threads corresponding to the position and mesh of a chunk.
|
||||||
struct MeshWorkerResponse {
|
struct MeshWorkerResponse {
|
||||||
pos: Vec2<i32>,
|
pos: Vec2<i32>,
|
||||||
mesh: Mesh<TerrainPipeline>,
|
mesh: Mesh<TerrainPipeline>,
|
||||||
started_tick: u64,
|
started_tick: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function executed by worker threads dedicated to chunk meshing
|
/// Function executed by worker threads dedicated to chunk meshing.
|
||||||
fn mesh_worker(
|
fn mesh_worker(
|
||||||
pos: Vec2<i32>,
|
pos: Vec2<i32>,
|
||||||
started_tick: u64,
|
started_tick: u64,
|
||||||
@ -44,7 +44,7 @@ pub struct Terrain {
|
|||||||
chunks: HashMap<Vec2<i32>, TerrainChunk>,
|
chunks: HashMap<Vec2<i32>, TerrainChunk>,
|
||||||
|
|
||||||
// The mpsc sender and receiver used for talking to meshing worker threads.
|
// The mpsc sender and receiver used for talking to meshing worker threads.
|
||||||
// We keep the sender component for no reason othe than to clone it and send it to new workers.
|
// We keep the sender component for no reason other than to clone it and send it to new workers.
|
||||||
mesh_send_tmp: mpsc::Sender<MeshWorkerResponse>,
|
mesh_send_tmp: mpsc::Sender<MeshWorkerResponse>,
|
||||||
mesh_recv: mpsc::Receiver<MeshWorkerResponse>,
|
mesh_recv: mpsc::Receiver<MeshWorkerResponse>,
|
||||||
mesh_todo: HashMap<Vec2<i32>, ChunkMeshState>,
|
mesh_todo: HashMap<Vec2<i32>, ChunkMeshState>,
|
||||||
@ -69,7 +69,7 @@ impl Terrain {
|
|||||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||||
let current_tick = client.get_tick();
|
let current_tick = client.get_tick();
|
||||||
|
|
||||||
// Add any recently created or changed chunks to the list of chunks to be meshed
|
// Add any recently created or changed chunks to the list of chunks to be meshed.
|
||||||
for pos in client
|
for pos in client
|
||||||
.state()
|
.state()
|
||||||
.changes()
|
.changes()
|
||||||
@ -108,7 +108,7 @@ impl Terrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remove any models for chunks that have been recently removed
|
// Remove any models for chunks that have been recently removed.
|
||||||
for pos in &client.state().changes().removed_chunks {
|
for pos in &client.state().changes().removed_chunks {
|
||||||
self.chunks.remove(pos);
|
self.chunks.remove(pos);
|
||||||
self.mesh_todo.remove(pos);
|
self.mesh_todo.remove(pos);
|
||||||
@ -117,11 +117,11 @@ impl Terrain {
|
|||||||
for todo in self
|
for todo in self
|
||||||
.mesh_todo
|
.mesh_todo
|
||||||
.values_mut()
|
.values_mut()
|
||||||
// Only spawn workers for meshing jobs without an active worker already
|
// Only spawn workers for meshing jobs without an active worker already.
|
||||||
.filter(|todo| !todo.active_worker)
|
.filter(|todo| !todo.active_worker)
|
||||||
{
|
{
|
||||||
// Find the area of the terrain we want. Because meshing needs to compute things like
|
// Find the area of the terrain we want. Because meshing needs to compute things like
|
||||||
// ambient occlusion and edge elision, we also need to borders of the chunk's
|
// ambient occlusion and edge elision, we also need the borders of the chunk's
|
||||||
// neighbours too (hence the `- 1` and `+ 1`).
|
// neighbours too (hence the `- 1` and `+ 1`).
|
||||||
let aabr = Aabr {
|
let aabr = Aabr {
|
||||||
min: todo
|
min: todo
|
||||||
@ -138,40 +138,40 @@ impl Terrain {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Copy out the chunk data we need to perform the meshing. We do this by taking a
|
// 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
|
// 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 client.state().terrain().sample(aabr) {
|
||||||
Ok(sample) => sample,
|
Ok(sample) => sample,
|
||||||
// If either this chunk or its neighbours doesn't yet exist, so we keep it in the
|
// Either this chunk or its neighbours doesn't yet exist, so we keep it in the
|
||||||
// todo queue to be processed at a later date when we have its neighbours.
|
// queue to be processed at a later date when we have its neighbours.
|
||||||
Err(VolMap2dErr::NoSuchChunk) => return,
|
Err(VolMap2dErr::NoSuchChunk) => return,
|
||||||
_ => panic!("Unhandled edge case"),
|
_ => panic!("Unhandled edge case"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Clone various things to that they can be moved into the thread
|
// Clone various things so that they can be moved into the thread.
|
||||||
let send = self.mesh_send_tmp.clone();
|
let send = self.mesh_send_tmp.clone();
|
||||||
let pos = todo.pos;
|
let pos = todo.pos;
|
||||||
|
|
||||||
// Queue the worker thread
|
// Queue the worker thread.
|
||||||
client.thread_pool().execute(move || {
|
client.thread_pool().execute(move || {
|
||||||
let _ = send.send(mesh_worker(pos, current_tick, volume, aabb));
|
let _ = send.send(mesh_worker(pos, current_tick, volume, aabb));
|
||||||
});
|
});
|
||||||
todo.active_worker = true;
|
todo.active_worker = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receive a chunk mesh from a worker thread, upload it to the GPU and then store it
|
// Receive a chunk mesh from a worker thread and upload it to the GPU, then store it.
|
||||||
// Only pull out one chunk per frame to avoid an unacceptable amount of blocking lag due
|
// Only pull out one chunk per frame to avoid an unacceptable amount of blocking lag due
|
||||||
// to the GPU upload. That still gives us a 60 chunks / second budget to play with.
|
// to the GPU upload. That still gives us a 60 chunks / second budget to play with.
|
||||||
if let Ok(response) = self.mesh_recv.recv_timeout(Duration::new(0, 0)) {
|
if let Ok(response) = self.mesh_recv.recv_timeout(Duration::new(0, 0)) {
|
||||||
match self.mesh_todo.get(&response.pos) {
|
match self.mesh_todo.get(&response.pos) {
|
||||||
// It's the mesh we want, insert the newly finished model into the terrain model
|
// It's the mesh we want, insert the newly finished model into the terrain model
|
||||||
// data structure (convert the mesh to a model first of course)
|
// data structure (convert the mesh to a model first of course).
|
||||||
Some(todo) if response.started_tick == todo.started_tick => {
|
Some(todo) if response.started_tick == todo.started_tick => {
|
||||||
self.chunks.insert(
|
self.chunks.insert(
|
||||||
response.pos,
|
response.pos,
|
||||||
TerrainChunk {
|
TerrainChunk {
|
||||||
model: renderer
|
model: renderer
|
||||||
.create_model(&response.mesh)
|
.create_model(&response.mesh)
|
||||||
.expect("Failed to upload chunk mesh to the GPU"),
|
.expect("Failed to upload chunk mesh to the GPU!"),
|
||||||
locals: renderer
|
locals: renderer
|
||||||
.create_consts(&[TerrainLocals {
|
.create_consts(&[TerrainLocals {
|
||||||
model_offs: Vec3::from(
|
model_offs: Vec3::from(
|
||||||
@ -181,12 +181,12 @@ impl Terrain {
|
|||||||
)
|
)
|
||||||
.into_array(),
|
.into_array(),
|
||||||
}])
|
}])
|
||||||
.expect("Failed to upload chunk locals to the GPU"),
|
.expect("Failed to upload chunk locals to the GPU!"),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Chunk must have been removed, or it was spawned on an old tick. Drop the mesh
|
// Chunk must have been removed, or it was spawned on an old tick. Drop the mesh
|
||||||
// since it's either out of date or no longer needed
|
// since it's either out of date or no longer needed.
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,11 @@ pub struct SessionState {
|
|||||||
hud: Hud,
|
hud: Hud,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an active game session (i.e: one that is being played)
|
/// Represents an active game session (i.e., the one being played).
|
||||||
impl SessionState {
|
impl SessionState {
|
||||||
/// Create a new `SessionState`
|
/// Create a new `SessionState`.
|
||||||
pub fn new(window: &mut Window, client: Rc<RefCell<Client>>, settings: Settings) -> Self {
|
pub fn new(window: &mut Window, client: Rc<RefCell<Client>>, settings: Settings) -> Self {
|
||||||
// Create a scene for this session. The scene handles visible elements of the game world
|
// Create a scene for this session. The scene handles visible elements of the game world.
|
||||||
let scene = Scene::new(window.renderer_mut(), &client.borrow());
|
let scene = Scene::new(window.renderer_mut(), &client.borrow());
|
||||||
Self {
|
Self {
|
||||||
scene,
|
scene,
|
||||||
@ -38,7 +38,7 @@ impl SessionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The background colour
|
// Background colour
|
||||||
const BG_COLOR: Rgba<f32> = Rgba {
|
const BG_COLOR: Rgba<f32> = Rgba {
|
||||||
r: 0.0,
|
r: 0.0,
|
||||||
g: 0.3,
|
g: 0.3,
|
||||||
@ -47,9 +47,10 @@ const BG_COLOR: Rgba<f32> = Rgba {
|
|||||||
};
|
};
|
||||||
|
|
||||||
impl SessionState {
|
impl SessionState {
|
||||||
/// Tick the session (and the client attached to it)
|
/// Tick the session (and the client attached to it).
|
||||||
pub fn tick(&mut self, dt: Duration) -> Result<(), Error> {
|
pub fn tick(&mut self, dt: Duration) -> Result<(), Error> {
|
||||||
// Calculate the movement input vector of the player from the current key presses and the camera direction
|
// Calculate the movement input vector of the player from the current key presses
|
||||||
|
// and the camera direction.
|
||||||
let ori = self.scene.camera().get_orientation();
|
let ori = self.scene.camera().get_orientation();
|
||||||
let unit_vecs = (
|
let unit_vecs = (
|
||||||
Vec2::new(ori[0].cos(), -ori[0].sin()),
|
Vec2::new(ori[0].cos(), -ori[0].sin()),
|
||||||
@ -58,7 +59,7 @@ impl SessionState {
|
|||||||
let dir_vec = self.key_state.dir_vec();
|
let dir_vec = self.key_state.dir_vec();
|
||||||
let move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
|
let move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
|
||||||
|
|
||||||
// Take the input events
|
// Take the input events.
|
||||||
let mut input_events = Vec::new();
|
let mut input_events = Vec::new();
|
||||||
mem::swap(&mut self.input_events, &mut input_events);
|
mem::swap(&mut self.input_events, &mut input_events);
|
||||||
|
|
||||||
@ -84,7 +85,7 @@ impl SessionState {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clean up the session (and the client attached to it) after a tick
|
/// Clean up the session (and the client attached to it) after a tick.
|
||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
self.client.borrow_mut().cleanup();
|
self.client.borrow_mut().cleanup();
|
||||||
}
|
}
|
||||||
@ -108,13 +109,13 @@ impl SessionState {
|
|||||||
|
|
||||||
impl PlayState for SessionState {
|
impl PlayState for SessionState {
|
||||||
fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult {
|
fn play(&mut self, _: Direction, global_state: &mut GlobalState) -> PlayStateResult {
|
||||||
// Trap the cursor
|
// Trap the cursor.
|
||||||
global_state.window.grab_cursor(true);
|
global_state.window.grab_cursor(true);
|
||||||
|
|
||||||
// Set up an fps clock
|
// Set up an fps clock.
|
||||||
let mut clock = Clock::new();
|
let mut clock = Clock::new();
|
||||||
|
|
||||||
// Load a few chunks TODO: Remove this
|
// Load a few chunks. TODO: Remove this.
|
||||||
/*
|
/*
|
||||||
for x in -6..7 {
|
for x in -6..7 {
|
||||||
for y in -6..7 {
|
for y in -6..7 {
|
||||||
@ -127,9 +128,9 @@ impl PlayState for SessionState {
|
|||||||
|
|
||||||
// Game loop
|
// Game loop
|
||||||
loop {
|
loop {
|
||||||
// Handle window events
|
// Handle window events.
|
||||||
for event in global_state.window.fetch_events() {
|
for event in global_state.window.fetch_events() {
|
||||||
// Pass all events to the ui first
|
// Pass all events to the ui first.
|
||||||
if self.hud.handle_event(event.clone(), global_state) {
|
if self.hud.handle_event(event.clone(), global_state) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -162,16 +163,16 @@ impl PlayState for SessionState {
|
|||||||
// TODO: Do something if the event wasn't handled?
|
// TODO: Do something if the event wasn't handled?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform an in-game tick
|
// Perform an in-game tick.
|
||||||
self.tick(clock.get_last_delta())
|
self.tick(clock.get_last_delta())
|
||||||
.expect("Failed to tick the scene");
|
.expect("Failed to tick the scene!");
|
||||||
|
|
||||||
// Maintain the scene
|
// Maintain the scene.
|
||||||
self.scene.maintain(
|
self.scene.maintain(
|
||||||
global_state.window.renderer_mut(),
|
global_state.window.renderer_mut(),
|
||||||
&mut self.client.borrow_mut(),
|
&mut self.client.borrow_mut(),
|
||||||
);
|
);
|
||||||
// Maintain the UI
|
// Maintain the UI.
|
||||||
for event in self
|
for event in self
|
||||||
.hud
|
.hud
|
||||||
.maintain(global_state.window.renderer_mut(), clock.get_tps())
|
.maintain(global_state.window.renderer_mut(), clock.get_tps())
|
||||||
@ -188,19 +189,19 @@ impl PlayState for SessionState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the session
|
// Render the session.
|
||||||
self.render(global_state.window.renderer_mut());
|
self.render(global_state.window.renderer_mut());
|
||||||
|
|
||||||
// Display the frame on the window
|
// Display the frame on the window.
|
||||||
global_state
|
global_state
|
||||||
.window
|
.window
|
||||||
.swap_buffers()
|
.swap_buffers()
|
||||||
.expect("Failed to swap window buffers");
|
.expect("Failed to swap window buffers!");
|
||||||
|
|
||||||
// Wait for the next tick
|
// Wait for the next tick.
|
||||||
clock.tick(Duration::from_millis(1000 / FPS));
|
clock.tick(Duration::from_millis(1000 / FPS));
|
||||||
|
|
||||||
// Clean things up after the tick
|
// Clean things up after the tick.
|
||||||
self.cleanup();
|
self.cleanup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use serde_derive::{Deserialize, Serialize};
|
|||||||
use std::{fs::File, io::prelude::*, path::PathBuf};
|
use std::{fs::File, io::prelude::*, path::PathBuf};
|
||||||
use toml;
|
use toml;
|
||||||
|
|
||||||
/// Settings contains everything that can be configured in the Settings.toml file
|
/// `Settings` contains everything that can be configured in the Settings.toml file.
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
@ -14,7 +14,7 @@ pub struct Settings {
|
|||||||
pub log: Log,
|
pub log: Log,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ControlSettings contains keybindings
|
/// `ControlSettings` contains keybindings.
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct ControlSettings {
|
pub struct ControlSettings {
|
||||||
pub toggle_cursor: VirtualKeyCode,
|
pub toggle_cursor: VirtualKeyCode,
|
||||||
@ -95,20 +95,20 @@ impl Settings {
|
|||||||
config
|
config
|
||||||
.merge(
|
.merge(
|
||||||
Config::try_from(&default_settings)
|
Config::try_from(&default_settings)
|
||||||
.expect("Default settings struct could not be converted to Config"),
|
.expect("Default settings struct could not be converted to Config!"),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// TODO: log errors here
|
// TODO: Log errors here.
|
||||||
// If merge or try_into fail use the default settings
|
// If merge or try_into fail, use the default settings.
|
||||||
match config.merge::<config::File<_>>(path.into()) {
|
match config.merge::<config::File<_>>(path.into()) {
|
||||||
Ok(_) => match config.try_into() {
|
Ok(_) => match config.try_into() {
|
||||||
Ok(settings) => settings,
|
Ok(settings) => settings,
|
||||||
Err(_) => default_settings,
|
Err(_) => default_settings,
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// Maybe the file didn't exist
|
// Maybe the file didn't exist.
|
||||||
// TODO: Handle this result
|
// TODO: Handle this result.
|
||||||
default_settings.save_to_file();
|
default_settings.save_to_file();
|
||||||
default_settings
|
default_settings
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ impl Settings {
|
|||||||
|
|
||||||
fn get_settings_path() -> PathBuf {
|
fn get_settings_path() -> PathBuf {
|
||||||
let proj_dirs =
|
let proj_dirs =
|
||||||
ProjectDirs::from("net", "veloren", "voxygen").expect("No home directory defined.");
|
ProjectDirs::from("net", "veloren", "voxygen").expect("No home directory defined!");
|
||||||
let path = proj_dirs.config_dir();
|
let path = proj_dirs.config_dir();
|
||||||
path.join("settings");
|
path.join("settings");
|
||||||
let path = path.with_extension("toml");
|
let path = path.with_extension("toml");
|
||||||
|
@ -28,7 +28,7 @@ impl Singleplayer {
|
|||||||
|
|
||||||
let sock = SocketAddr::from((
|
let sock = SocketAddr::from((
|
||||||
[127, 0, 0, 1],
|
[127, 0, 0, 1],
|
||||||
pick_unused_port().expect("Failed to find unused port"),
|
pick_unused_port().expect("Failed to find unused port!"),
|
||||||
));
|
));
|
||||||
|
|
||||||
let sock2 = sock.clone();
|
let sock2 = sock.clone();
|
||||||
@ -60,12 +60,12 @@ fn run_server(sock: SocketAddr, rec: Receiver<Msg>) {
|
|||||||
let mut clock = Clock::new();
|
let mut clock = Clock::new();
|
||||||
|
|
||||||
// Create server
|
// Create server
|
||||||
let mut server = Server::bind(sock).expect("Failed to create server instance");
|
let mut server = Server::bind(sock).expect("Failed to create server instance!");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let events = server
|
let events = server
|
||||||
.tick(Input::default(), clock.get_last_delta())
|
.tick(Input::default(), clock.get_last_delta())
|
||||||
.expect("Failed to tick server");
|
.expect("Failed to tick server!");
|
||||||
|
|
||||||
for event in events {
|
for event in events {
|
||||||
match event {
|
match event {
|
||||||
@ -75,7 +75,7 @@ fn run_server(sock: SocketAddr, rec: Receiver<Msg>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up the server after a tick
|
// Clean up the server after a tick.
|
||||||
server.cleanup();
|
server.cleanup();
|
||||||
|
|
||||||
match rec.try_recv() {
|
match rec.try_recv() {
|
||||||
@ -86,7 +86,7 @@ fn run_server(sock: SocketAddr, rec: Receiver<Msg>) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the next tick
|
// Wait for the next tick.
|
||||||
clock.tick(Duration::from_millis(1000 / TPS));
|
clock.tick(Duration::from_millis(1000 / TPS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,12 @@ impl Event {
|
|||||||
pub fn try_from(event: glutin::Event, window: &glutin::GlWindow) -> Option<Self> {
|
pub fn try_from(event: glutin::Event, window: &glutin::GlWindow) -> Option<Self> {
|
||||||
use conrod_winit::*;
|
use conrod_winit::*;
|
||||||
use winit;
|
use winit;
|
||||||
// A wrapper around the winit window that allows us to implement the trait necessary for enabling
|
// A wrapper around the winit window that allows us to implement the trait necessary for
|
||||||
// the winit <-> conrod conversion functions.
|
// enabling the winit <-> conrod conversion functions.
|
||||||
struct WindowRef<'a>(&'a winit::Window);
|
struct WindowRef<'a>(&'a winit::Window);
|
||||||
|
|
||||||
// Implement the `WinitWindow` trait for `WindowRef` to allow for generating compatible conversion
|
// Implement the `WinitWindow` trait for `WindowRef` to allow for generating compatible
|
||||||
// functions.
|
// conversion functions.
|
||||||
impl<'a> conrod_winit::WinitWindow for WindowRef<'a> {
|
impl<'a> conrod_winit::WinitWindow for WindowRef<'a> {
|
||||||
fn get_inner_size(&self) -> Option<(u32, u32)> {
|
fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||||
winit::Window::get_inner_size(&self.0).map(Into::into)
|
winit::Window::get_inner_size(&self.0).map(Into::into)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/// This macro will automatically load all specified assets, get the corresponding FontIds and
|
/// This macro will automatically load all specified assets, get the corresponding FontIds and
|
||||||
/// create a struct with all of them
|
/// create a struct with all of them.
|
||||||
///
|
///
|
||||||
/// Example usage:
|
/// Example usage:
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -59,12 +59,12 @@ impl GraphicCache {
|
|||||||
{
|
{
|
||||||
match self
|
match self
|
||||||
.rect_map
|
.rect_map
|
||||||
.get(&(graphic_id, dims, source.map(|e| e.to_bits()))) //<-------- TODO: Replace this with rounded representation of source
|
.get(&(graphic_id, dims, source.map(|e| e.to_bits()))) // TODO: Replace this with rounded representation of source
|
||||||
{
|
{
|
||||||
Some(aabr) => Some(*aabr),
|
Some(aabr) => Some(*aabr),
|
||||||
None => match self.graphic_map.get(&graphic_id) {
|
None => match self.graphic_map.get(&graphic_id) {
|
||||||
Some(graphic) => {
|
Some(graphic) => {
|
||||||
// Allocate rectangle
|
// Allocate rectangle.
|
||||||
let aabr = match self
|
let aabr = match self
|
||||||
.atlas
|
.atlas
|
||||||
.allocate(size2(i32::from(dims.x), i32::from(dims.y)))
|
.allocate(size2(i32::from(dims.x), i32::from(dims.y)))
|
||||||
@ -76,13 +76,16 @@ impl GraphicCache {
|
|||||||
max: Vec2::new(max.x as u16, max.y as u16),
|
max: Vec2::new(max.x as u16, max.y as u16),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Out of room
|
// Out of room.
|
||||||
// TODO: make more room by 1. expanding cache size, 2. removing unused allocations, 3. rearranging rectangles
|
// TODO: Make more room.
|
||||||
|
// 1) Expand cache size
|
||||||
|
// 2) Remove unused allocations
|
||||||
|
// 3) Rearrange rectangles
|
||||||
None => return None,
|
None => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render image
|
// Render image.
|
||||||
// TODO: use source
|
// TODO: Use source.
|
||||||
let data = match graphic {
|
let data = match graphic {
|
||||||
Graphic::Image(ref image) => image
|
Graphic::Image(ref image) => image
|
||||||
.resize_exact(
|
.resize_exact(
|
||||||
@ -99,14 +102,14 @@ impl GraphicCache {
|
|||||||
Graphic::Blank => return None,
|
Graphic::Blank => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Draw to allocated area
|
// Draw to allocated area.
|
||||||
cacher(aabr, data);
|
cacher(aabr, data);
|
||||||
|
|
||||||
// Insert area into map for retrieval
|
// Insert area into map for retrieval.
|
||||||
self.rect_map
|
self.rect_map
|
||||||
.insert((graphic_id, dims, source.map(|e| e.to_bits())), aabr);
|
.insert((graphic_id, dims, source.map(|e| e.to_bits())), aabr);
|
||||||
|
|
||||||
// Return area
|
// Return area.
|
||||||
Some(aabr)
|
Some(aabr)
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
|
@ -109,7 +109,7 @@ pub fn draw_vox(
|
|||||||
.map(|p| p.data)
|
.map(|p| p.data)
|
||||||
.collect::<Vec<[u8; 4]>>()
|
.collect::<Vec<[u8; 4]>>()
|
||||||
} else {
|
} else {
|
||||||
// TODO: remove clone
|
// TODO: Remove clone.
|
||||||
color.as_ref().to_vec()
|
color.as_ref().to_vec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,7 +121,7 @@ fn ao_level(side1: bool, corner: bool, side2: bool) -> u8 {
|
|||||||
3 - [side1, corner, side2].iter().filter(|e| **e).count() as u8
|
3 - [side1, corner, side2].iter().filter(|e| **e).count() as u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: generalize meshing code....
|
// TODO: Generalize meshing code.
|
||||||
fn create_quad(
|
fn create_quad(
|
||||||
origin: Vec3<f32>,
|
origin: Vec3<f32>,
|
||||||
unit_x: Vec3<f32>,
|
unit_x: Vec3<f32>,
|
||||||
@ -140,7 +140,7 @@ fn create_quad(
|
|||||||
let c = Vert::new(origin + unit_x + unit_y, col, norm, c_ao);
|
let c = Vert::new(origin + unit_x + unit_y, col, norm, c_ao);
|
||||||
let d = Vert::new(origin + unit_y, col, norm, d_ao);
|
let d = Vert::new(origin + unit_y, col, norm, d_ao);
|
||||||
|
|
||||||
// Flip to fix anisotropy
|
// Flip to fix anisotropy.
|
||||||
let (a, b, c, d) = if a_ao + c_ao > b_ao + d_ao {
|
let (a, b, c, d) = if a_ao + c_ao > b_ao + d_ao {
|
||||||
(d, a, b, c)
|
(d, a, b, c)
|
||||||
} else {
|
} else {
|
||||||
@ -163,7 +163,7 @@ fn generate_mesh(segment: &Segment, offs: Vec3<f32>) -> Vec<Vert> {
|
|||||||
let is_empty = |pos| segment.get(pos).map(|v| v.is_empty()).unwrap_or(true);
|
let is_empty = |pos| segment.get(pos).map(|v| v.is_empty()).unwrap_or(true);
|
||||||
|
|
||||||
let occluders = |unit_x, unit_y, dir| {
|
let occluders = |unit_x, unit_y, dir| {
|
||||||
// would be nice to generate unit_x and unit_y from a given direction
|
// Would be nice to generate unit_x and unit_y from a given direction.
|
||||||
[
|
[
|
||||||
!is_empty(pos + dir - unit_x),
|
!is_empty(pos + dir - unit_x),
|
||||||
!is_empty(pos + dir - unit_x - unit_y),
|
!is_empty(pos + dir - unit_x - unit_y),
|
||||||
|
@ -57,7 +57,7 @@ impl<'a> GraphicCreator<'a> for VoxelMs9Graphic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This macro will automatically load all specified assets, get the corresponding ImgIds and
|
/// This macro will automatically load all specified assets, get the corresponding ImgIds and
|
||||||
/// create a struct with all of them
|
/// create a struct with all of them.
|
||||||
///
|
///
|
||||||
/// Example usage:
|
/// Example usage:
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -88,7 +88,7 @@ pub struct Ui {
|
|||||||
draw_commands: Vec<DrawCommand>,
|
draw_commands: Vec<DrawCommand>,
|
||||||
// Model for drawing the ui
|
// Model for drawing the ui
|
||||||
model: DynamicModel<UiPipeline>,
|
model: DynamicModel<UiPipeline>,
|
||||||
// Stores new window size for updating scaling
|
// Window size for updating scaling
|
||||||
window_resized: Option<Vec2<f64>>,
|
window_resized: Option<Vec2<f64>>,
|
||||||
// Scaling of the ui
|
// Scaling of the ui
|
||||||
scale: Scale,
|
scale: Scale,
|
||||||
@ -110,10 +110,10 @@ impl Ui {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the scaling mode of the ui
|
// Set the scaling mode of the ui.
|
||||||
pub fn scaling_mode(&mut self, mode: ScaleMode) {
|
pub fn scaling_mode(&mut self, mode: ScaleMode) {
|
||||||
self.scale.scaling_mode(mode);
|
self.scale.scaling_mode(mode);
|
||||||
// Give conrod the new size
|
// Give conrod the new size.
|
||||||
let (w, h) = self.scale.scaled_window_size().into_tuple();
|
let (w, h) = self.scale.scaled_window_size().into_tuple();
|
||||||
self.ui.handle_event(Input::Resize(w, h));
|
self.ui.handle_event(Input::Resize(w, h));
|
||||||
}
|
}
|
||||||
@ -134,7 +134,7 @@ impl Ui {
|
|||||||
self.ui.set_widgets()
|
self.ui.set_widgets()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accepts Option so widget can be unfocused
|
// Accepts Option so widget can be unfocused.
|
||||||
pub fn focus_widget(&mut self, id: Option<WidgId>) {
|
pub fn focus_widget(&mut self, id: Option<WidgId>) {
|
||||||
self.ui.keyboard_capture(match id {
|
self.ui.keyboard_capture(match id {
|
||||||
Some(id) => id,
|
Some(id) => id,
|
||||||
@ -142,12 +142,12 @@ impl Ui {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get id of current widget capturing keyboard
|
// Get id of current widget capturing keyboard.
|
||||||
pub fn widget_capturing_keyboard(&self) -> Option<WidgId> {
|
pub fn widget_capturing_keyboard(&self) -> Option<WidgId> {
|
||||||
self.ui.global_input().current.widget_capturing_keyboard
|
self.ui.global_input().current.widget_capturing_keyboard
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get whether a widget besides the window is capturing the mouse
|
// Get whether a widget besides the window is capturing the mouse.
|
||||||
pub fn no_widget_capturing_mouse(&self) -> bool {
|
pub fn no_widget_capturing_mouse(&self) -> bool {
|
||||||
self.ui
|
self.ui
|
||||||
.global_input()
|
.global_input()
|
||||||
@ -157,7 +157,7 @@ impl Ui {
|
|||||||
.is_none()
|
.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the widget graph
|
// Get the widget graph.
|
||||||
pub fn widget_graph(&self) -> &Graph {
|
pub fn widget_graph(&self) -> &Graph {
|
||||||
self.ui.widget_graph()
|
self.ui.widget_graph()
|
||||||
}
|
}
|
||||||
@ -203,8 +203,8 @@ impl Ui {
|
|||||||
self.draw_commands.clear();
|
self.draw_commands.clear();
|
||||||
let mut mesh = Mesh::new();
|
let mut mesh = Mesh::new();
|
||||||
|
|
||||||
// TODO: this could be removed entirely if the draw call just used both textures
|
// TODO: this could be removed entirely if the draw call just used both textures,
|
||||||
// however this allows for flexibility if we want to interleave other draw calls later
|
// however this allows for flexibility if we want to interweave other draw calls later.
|
||||||
enum State {
|
enum State {
|
||||||
Image,
|
Image,
|
||||||
Plain,
|
Plain,
|
||||||
@ -213,8 +213,8 @@ impl Ui {
|
|||||||
let mut current_state = State::Plain;
|
let mut current_state = State::Plain;
|
||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
|
|
||||||
let window_scizzor = default_scissor(renderer);
|
let window_scissor = default_scissor(renderer);
|
||||||
let mut current_scizzor = window_scizzor;
|
let mut current_scissor = window_scissor;
|
||||||
|
|
||||||
// Switches to the `Plain` state and completes the previous `Command` if not already in the
|
// Switches to the `Plain` state and completes the previous `Command` if not already in the
|
||||||
// `Plain` state.
|
// `Plain` state.
|
||||||
@ -239,12 +239,12 @@ impl Ui {
|
|||||||
..
|
..
|
||||||
} = prim;
|
} = prim;
|
||||||
|
|
||||||
// Check for a change in the scizzor
|
// Check for a change in the scissor.
|
||||||
let new_scizzor = {
|
let new_scissor = {
|
||||||
let (l, b, w, h) = scizzor.l_b_w_h();
|
let (l, b, w, h) = scizzor.l_b_w_h();
|
||||||
// Calculate minimum x and y coordinates while
|
// Calculate minimum x and y coordinates while
|
||||||
// - flipping y axis (from +up to +down)
|
// flipping y axis (from +up to +down) and
|
||||||
// - moving origin to top-left corner (from middle)
|
// moving origin to top-left corner (from middle).
|
||||||
let min_x = self.ui.win_w / 2.0 + l;
|
let min_x = self.ui.win_w / 2.0 + l;
|
||||||
let min_y = self.ui.win_h / 2.0 - b - h;
|
let min_y = self.ui.win_h / 2.0 - b - h;
|
||||||
Aabr {
|
Aabr {
|
||||||
@ -257,22 +257,22 @@ impl Ui {
|
|||||||
y: ((min_y + h) * p_scale_factor) as u16,
|
y: ((min_y + h) * p_scale_factor) as u16,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
.intersection(window_scizzor)
|
.intersection(window_scissor)
|
||||||
};
|
};
|
||||||
if new_scizzor != current_scizzor {
|
if new_scissor != current_scissor {
|
||||||
// Finish the current command
|
// Finish the current command.
|
||||||
self.draw_commands.push(match current_state {
|
self.draw_commands.push(match current_state {
|
||||||
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
||||||
State::Image => DrawCommand::image(start..mesh.vertices().len()),
|
State::Image => DrawCommand::image(start..mesh.vertices().len()),
|
||||||
});
|
});
|
||||||
start = mesh.vertices().len();
|
start = mesh.vertices().len();
|
||||||
|
|
||||||
// Update the scizzor and produce a command.
|
// Update the scissor and produce a command.
|
||||||
current_scizzor = new_scizzor;
|
current_scissor = new_scissor;
|
||||||
self.draw_commands.push(DrawCommand::Scissor(new_scizzor));
|
self.draw_commands.push(DrawCommand::Scissor(new_scissor));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0)
|
// Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0).
|
||||||
let ui_win_w = self.ui.win_w;
|
let ui_win_w = self.ui.win_w;
|
||||||
let ui_win_h = self.ui.win_h;
|
let ui_win_h = self.ui.win_h;
|
||||||
let vx = |x: f64| (x / ui_win_w * 2.0) as f32;
|
let vx = |x: f64| (x / ui_win_w * 2.0) as f32;
|
||||||
@ -303,7 +303,7 @@ impl Ui {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch to the image state if we are not in it already
|
// Switch to the image state if we are not in it already.
|
||||||
if let State::Plain = current_state {
|
if let State::Plain = current_state {
|
||||||
self.draw_commands
|
self.draw_commands
|
||||||
.push(DrawCommand::plain(start..mesh.vertices().len()));
|
.push(DrawCommand::plain(start..mesh.vertices().len()));
|
||||||
@ -318,8 +318,8 @@ impl Ui {
|
|||||||
(rect.w() * p_scale_factor).round() as u16,
|
(rect.w() * p_scale_factor).round() as u16,
|
||||||
(rect.h() * p_scale_factor).round() as u16,
|
(rect.h() * p_scale_factor).round() as u16,
|
||||||
);
|
);
|
||||||
// Transform the source rectangle into uv coordinate
|
// Transform the source rectangle into uv coordinate.
|
||||||
// TODO: make sure this is right
|
// TODO: Make sure this is right.
|
||||||
let source_aabr = {
|
let source_aabr = {
|
||||||
let (uv_l, uv_r, uv_b, uv_t) = (0.0, 1.0, 0.0, 1.0); /*match source_rect {
|
let (uv_l, uv_r, uv_b, uv_t) = (0.0, 1.0, 0.0, 1.0); /*match source_rect {
|
||||||
Some(src_rect) => {
|
Some(src_rect) => {
|
||||||
@ -339,7 +339,7 @@ impl Ui {
|
|||||||
let (cache_w, cache_h) =
|
let (cache_w, cache_h) =
|
||||||
cache_tex.get_dimensions().map(|e| e as f32).into_tuple();
|
cache_tex.get_dimensions().map(|e| e as f32).into_tuple();
|
||||||
|
|
||||||
// Cache graphic at particular resolution
|
// Cache graphic at particular resolution.
|
||||||
let uv_aabr = match graphic_cache.cache_res(
|
let uv_aabr = match graphic_cache.cache_res(
|
||||||
*graphic_id,
|
*graphic_id,
|
||||||
resolution,
|
resolution,
|
||||||
@ -371,15 +371,15 @@ impl Ui {
|
|||||||
font_id,
|
font_id,
|
||||||
} => {
|
} => {
|
||||||
switch_to_plain_state!();
|
switch_to_plain_state!();
|
||||||
// Get screen width and height
|
// Get screen width and height.
|
||||||
let (screen_w, screen_h) =
|
let (screen_w, screen_h) =
|
||||||
renderer.get_resolution().map(|e| e as f32).into_tuple();
|
renderer.get_resolution().map(|e| e as f32).into_tuple();
|
||||||
// Calculate dpi factor
|
// Calculate dpi factor.
|
||||||
let dpi_factor = screen_w / ui_win_w as f32;
|
let dpi_factor = screen_w / ui_win_w as f32;
|
||||||
|
|
||||||
let positioned_glyphs = text.positioned_glyphs(dpi_factor);
|
let positioned_glyphs = text.positioned_glyphs(dpi_factor);
|
||||||
let (glyph_cache, cache_tex) = self.cache.glyph_cache_mut_and_tex();
|
let (glyph_cache, cache_tex) = self.cache.glyph_cache_mut_and_tex();
|
||||||
// Queue the glyphs to be cached
|
// Queue the glyphs to be cached.
|
||||||
for glyph in positioned_glyphs {
|
for glyph in positioned_glyphs {
|
||||||
glyph_cache.queue_glyph(font_id.index(), glyph.clone());
|
glyph_cache.queue_glyph(font_id.index(), glyph.clone());
|
||||||
}
|
}
|
||||||
@ -424,7 +424,7 @@ impl Ui {
|
|||||||
}
|
}
|
||||||
PrimitiveKind::Rectangle { color } => {
|
PrimitiveKind::Rectangle { color } => {
|
||||||
let color = srgb_to_linear(color.to_fsa().into());
|
let color = srgb_to_linear(color.to_fsa().into());
|
||||||
// Don't draw a transparent rectangle
|
// Don't draw a transparent rectangle.
|
||||||
if color[3] == 0.0 {
|
if color[3] == 0.0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -442,7 +442,7 @@ impl Ui {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
PrimitiveKind::TrianglesSingleColor { color, triangles } => {
|
PrimitiveKind::TrianglesSingleColor { color, triangles } => {
|
||||||
// Don't draw transparent triangle or switch state if there are actually no triangles
|
// Don't draw transparent triangle or switch state if there are actually no triangles.
|
||||||
let color = srgb_to_linear(Rgba::from(Into::<[f32; 4]>::into(color)));
|
let color = srgb_to_linear(Rgba::from(Into::<[f32; 4]>::into(color)));
|
||||||
if triangles.is_empty() || color[3] == 0.0 {
|
if triangles.is_empty() || color[3] == 0.0 {
|
||||||
continue;
|
continue;
|
||||||
@ -454,7 +454,7 @@ impl Ui {
|
|||||||
let p1 = Vec2::new(vx(tri[0][0]), vy(tri[0][1]));
|
let p1 = Vec2::new(vx(tri[0][0]), vy(tri[0][1]));
|
||||||
let p2 = Vec2::new(vx(tri[1][0]), vy(tri[1][1]));
|
let p2 = Vec2::new(vx(tri[1][0]), vy(tri[1][1]));
|
||||||
let p3 = Vec2::new(vx(tri[2][0]), vy(tri[2][1]));
|
let p3 = Vec2::new(vx(tri[2][0]), vy(tri[2][1]));
|
||||||
// If triangle is clockwise reverse it
|
// If triangle is clockwise, reverse it.
|
||||||
let (v1, v2): (Vec3<f32>, Vec3<f32>) = ((p2 - p1).into(), (p3 - p1).into());
|
let (v1, v2): (Vec3<f32>, Vec3<f32>) = ((p2 - p1).into(), (p3 - p1).into());
|
||||||
let triangle = if v1.cross(v2).z > 0.0 {
|
let triangle = if v1.cross(v2).z > 0.0 {
|
||||||
[p1.into_array(), p2.into_array(), p3.into_array()]
|
[p1.into_array(), p2.into_array(), p3.into_array()]
|
||||||
@ -469,49 +469,49 @@ impl Ui {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {} // TODO: Add this
|
_ => {} // TODO: Add this.
|
||||||
//PrimitiveKind::TrianglesMultiColor {..} => {println!("primitive kind multicolor with id {:?}", id);}
|
//PrimitiveKind::TrianglesMultiColor {..} => {println!("primitive kind multicolor with id {:?}", id);}
|
||||||
// Other uneeded for now
|
// Other unneeded for now.
|
||||||
//PrimitiveKind::Other {..} => {println!("primitive kind other with id {:?}", id);}
|
//PrimitiveKind::Other {..} => {println!("primitive kind other with id {:?}", id);}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Enter the final command
|
// Enter the final command.
|
||||||
self.draw_commands.push(match current_state {
|
self.draw_commands.push(match current_state {
|
||||||
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
|
||||||
State::Image => DrawCommand::image(start..mesh.vertices().len()),
|
State::Image => DrawCommand::image(start..mesh.vertices().len()),
|
||||||
});
|
});
|
||||||
|
|
||||||
// create a larger dynamic model if the mesh is larger than the current model size
|
// Create a larger dynamic model if the mesh is larger than the current model size.
|
||||||
if self.model.vbuf.len() < mesh.vertices().len() {
|
if self.model.vbuf.len() < mesh.vertices().len() {
|
||||||
self.model = renderer
|
self.model = renderer
|
||||||
.create_dynamic_model(mesh.vertices().len() * 4 / 3)
|
.create_dynamic_model(mesh.vertices().len() * 4 / 3)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
renderer.update_model(&self.model, &mesh, 0).unwrap();
|
renderer.update_model(&self.model, &mesh, 0).unwrap();
|
||||||
// Update model with new mesh
|
// Update model with new mesh.
|
||||||
|
|
||||||
// Handle window resizing
|
// Handle window resizing.
|
||||||
if let Some(new_dims) = self.window_resized.take() {
|
if let Some(new_dims) = self.window_resized.take() {
|
||||||
self.scale.window_resized(new_dims, renderer);
|
self.scale.window_resized(new_dims, renderer);
|
||||||
let (w, h) = self.scale.scaled_window_size().into_tuple();
|
let (w, h) = self.scale.scaled_window_size().into_tuple();
|
||||||
self.ui.handle_event(Input::Resize(w, h));
|
self.ui.handle_event(Input::Resize(w, h));
|
||||||
|
|
||||||
let res = renderer.get_resolution();
|
let res = renderer.get_resolution();
|
||||||
// Avoid panic in graphic cache when minimizing
|
// Avoid panic in graphic cache when minimizing.
|
||||||
if res.x > 0 && res.y > 0 {
|
if res.x > 0 && res.y > 0 {
|
||||||
self.cache
|
self.cache
|
||||||
.clear_graphic_cache(renderer, renderer.get_resolution().map(|e| e * 4));
|
.clear_graphic_cache(renderer, renderer.get_resolution().map(|e| e * 4));
|
||||||
}
|
}
|
||||||
// TODO: probably need to resize glyph cache, see conrod's gfx backend for reference
|
// TODO: Probably need to resize glyph cache, see conrod's gfx backend for reference.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, renderer: &mut Renderer) {
|
pub fn render(&self, renderer: &mut Renderer) {
|
||||||
let mut scissor = default_scissor(renderer);
|
let mut scissor_to_render = default_scissor(renderer);
|
||||||
for draw_command in self.draw_commands.iter() {
|
for draw_command in self.draw_commands.iter() {
|
||||||
match draw_command {
|
match draw_command {
|
||||||
DrawCommand::Scissor(scizzor) => {
|
DrawCommand::Scissor(scissor) => {
|
||||||
scissor = *scizzor;
|
scissor_to_render = *scissor;
|
||||||
}
|
}
|
||||||
DrawCommand::Draw { kind, verts } => {
|
DrawCommand::Draw { kind, verts } => {
|
||||||
let tex = match kind {
|
let tex = match kind {
|
||||||
@ -519,7 +519,7 @@ impl Ui {
|
|||||||
DrawKind::Plain => self.cache.glyph_cache_tex(),
|
DrawKind::Plain => self.cache.glyph_cache_tex(),
|
||||||
};
|
};
|
||||||
let model = self.model.submodel(verts.clone());
|
let model = self.model.submodel(verts.clone());
|
||||||
renderer.render_ui_element(&model, &tex, scissor);
|
renderer.render_ui_element(&model, &tex, scissor_to_render);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
use crate::{render::Renderer, window::Window};
|
use crate::{render::Renderer, window::Window};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
// How the ui is scaled
|
/// Type of scaling to use.
|
||||||
pub enum ScaleMode {
|
pub enum ScaleMode {
|
||||||
// Scale against physical size
|
// Scale against physical size.
|
||||||
Absolute(f64),
|
Absolute(f64),
|
||||||
// Use the dpi factor provided by the windowing system (i.e. use logical size)
|
// Use the dpi factor provided by the windowing system (i.e. use logical size).
|
||||||
DpiFactor,
|
DpiFactor,
|
||||||
// Scale based on the window's physical size, but maintain aspect ratio of widgets
|
// Scale based on the window's physical size, but maintain aspect ratio of widgets.
|
||||||
// Contains width and height of the "default" window size (ie where there should be no scaling)
|
// Contains width and height of the "default" window size (ie where there should be no scaling).
|
||||||
RelativeToWindow(Vec2<f64>),
|
RelativeToWindow(Vec2<f64>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Scale {
|
pub struct Scale {
|
||||||
// Type of scaling to use
|
|
||||||
mode: ScaleMode,
|
mode: ScaleMode,
|
||||||
// Current dpi factor
|
// Current dpi factor
|
||||||
dpi_factor: f64,
|
dpi_factor: f64,
|
||||||
@ -31,11 +30,11 @@ impl Scale {
|
|||||||
window_dims,
|
window_dims,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Change the scaling mode
|
// Change the scaling mode.
|
||||||
pub fn scaling_mode(&mut self, mode: ScaleMode) {
|
pub fn scaling_mode(&mut self, mode: ScaleMode) {
|
||||||
self.mode = mode;
|
self.mode = mode;
|
||||||
}
|
}
|
||||||
// Calculate factor to transform between logical coordinates and our scaled coordinates
|
// Calculate factor to transform between logical coordinates and our scaled coordinates.
|
||||||
pub fn scale_factor_logical(&self) -> f64 {
|
pub fn scale_factor_logical(&self) -> f64 {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ScaleMode::Absolute(scale) => scale / self.dpi_factor,
|
ScaleMode::Absolute(scale) => scale / self.dpi_factor,
|
||||||
@ -45,20 +44,20 @@ impl Scale {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Calculate factor to transform between physical coordinates and our scaled coordinates
|
// Calculate factor to transform between physical coordinates and our scaled coordinates.
|
||||||
pub fn scale_factor_physical(&self) -> f64 {
|
pub fn scale_factor_physical(&self) -> f64 {
|
||||||
self.scale_factor_logical() * self.dpi_factor
|
self.scale_factor_logical() * self.dpi_factor
|
||||||
}
|
}
|
||||||
// Updates internal window size (and/or dpi_factor)
|
// Updates internal window size (and/or dpi_factor).
|
||||||
pub fn window_resized(&mut self, new_dims: Vec2<f64>, renderer: &Renderer) {
|
pub fn window_resized(&mut self, new_dims: Vec2<f64>, renderer: &Renderer) {
|
||||||
self.dpi_factor = renderer.get_resolution().x as f64 / new_dims.x;
|
self.dpi_factor = renderer.get_resolution().x as f64 / new_dims.x;
|
||||||
self.window_dims = new_dims;
|
self.window_dims = new_dims;
|
||||||
}
|
}
|
||||||
// Get scaled window size
|
// Get scaled window size.
|
||||||
pub fn scaled_window_size(&self) -> Vec2<f64> {
|
pub fn scaled_window_size(&self) -> Vec2<f64> {
|
||||||
self.window_dims / self.scale_factor_logical()
|
self.window_dims / self.scale_factor_logical()
|
||||||
}
|
}
|
||||||
// Transform point from logical to scaled coordinates
|
// Transform point from logical to scaled coordinates.
|
||||||
pub fn scale_point(&self, point: Vec2<f64>) -> Vec2<f64> {
|
pub fn scale_point(&self, point: Vec2<f64>) -> Vec2<f64> {
|
||||||
point / self.scale_factor_logical()
|
point / self.scale_factor_logical()
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ pub trait ValueFromPercent<T> {
|
|||||||
|
|
||||||
/// Linear value selection.
|
/// Linear value selection.
|
||||||
///
|
///
|
||||||
/// If the slider's width is greater than it's height, it will automatically become a horizontal
|
/// If the slider's width is greater than its height, it will automatically become a horizontal
|
||||||
/// slider, otherwise it will be a vertical slider.
|
/// slider, otherwise it will be a vertical slider.
|
||||||
///
|
///
|
||||||
/// Its reaction is triggered if the value is updated or if the mouse button is released while
|
/// Its reaction is triggered if the value is updated or if the mouse button is released while
|
||||||
@ -31,9 +31,9 @@ pub struct ImageSlider<T, K> {
|
|||||||
max: T,
|
max: T,
|
||||||
/// The amount in which the slider's display should be skewed.
|
/// The amount in which the slider's display should be skewed.
|
||||||
///
|
///
|
||||||
/// Higher skew amounts (above 1.0) will weight lower values.
|
/// Higher skew amounts (above 1.0) will weigh lower values.
|
||||||
///
|
///
|
||||||
/// Lower skew amounts (below 1.0) will weight heigher values.
|
/// Lower skew amounts (below 1.0) will weigh higher values.
|
||||||
///
|
///
|
||||||
/// All skew amounts should be greater than 0.0.
|
/// All skew amounts should be greater than 0.0.
|
||||||
skew: f32,
|
skew: f32,
|
||||||
@ -47,7 +47,7 @@ struct Track {
|
|||||||
color: Option<Color>,
|
color: Option<Color>,
|
||||||
src_rect: Option<Rect>,
|
src_rect: Option<Rect>,
|
||||||
breadth: Option<f32>,
|
breadth: Option<f32>,
|
||||||
// Padding on the ends of the track constraining the slider to a smaller area
|
// Padding on the ends of the track constraining the slider to a smaller area.
|
||||||
padding: (f32, f32),
|
padding: (f32, f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,13 +202,13 @@ where
|
|||||||
if mouse.buttons.left().is_down() {
|
if mouse.buttons.left().is_down() {
|
||||||
let mouse_abs_xy = mouse.abs_xy();
|
let mouse_abs_xy = mouse.abs_xy();
|
||||||
let (mouse_offset, track_length) = if is_horizontal {
|
let (mouse_offset, track_length) = if is_horizontal {
|
||||||
// Horizontal.
|
// Horizontal
|
||||||
(
|
(
|
||||||
mouse_abs_xy[0] - rect.x.start - start_pad,
|
mouse_abs_xy[0] - rect.x.start - start_pad,
|
||||||
rect.w() - start_pad - end_pad,
|
rect.w() - start_pad - end_pad,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Vertical.
|
// Vertical
|
||||||
(
|
(
|
||||||
mouse_abs_xy[1] - rect.y.start - start_pad,
|
mouse_abs_xy[1] - rect.y.start - start_pad,
|
||||||
rect.h() - start_pad - end_pad,
|
rect.h() - start_pad - end_pad,
|
||||||
@ -264,7 +264,7 @@ where
|
|||||||
})
|
})
|
||||||
.unwrap_or(slider.image_id);
|
.unwrap_or(slider.image_id);
|
||||||
|
|
||||||
// The rectangle for positioning and sizing the slider.
|
// A rectangle for positioning and sizing the slider.
|
||||||
let value_perc = utils::map_range(new_value, min, max, 0.0, 1.0);
|
let value_perc = utils::map_range(new_value, min, max, 0.0, 1.0);
|
||||||
let unskewed_perc = value_perc.powf(1.0 / skew as f64);
|
let unskewed_perc = value_perc.powf(1.0 / skew as f64);
|
||||||
let slider_rect = if is_horizontal {
|
let slider_rect = if is_horizontal {
|
||||||
|
@ -105,8 +105,10 @@ impl Widget for ToggleButton {
|
|||||||
t_image,
|
t_image,
|
||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
// Check if button was clicked
|
// Check if the button was clicked.
|
||||||
// (can't use .set().was_clicked() because we are changing the image and this is after setting the widget which causes flickering as it takes a frame to change after the mouse button is lifted)
|
// (Can't use `.set().was_clicked()` because we are changing the image after setting the
|
||||||
|
// widget, which causes flickering since it takes a frame to change after the mouse button
|
||||||
|
// is lifted).
|
||||||
if ui.widget_input(state.ids.button).clicks().left().count() % 2 == 1 {
|
if ui.widget_input(state.ids.button).clicks().left().count() % 2 == 1 {
|
||||||
value = !value;
|
value = !value;
|
||||||
}
|
}
|
||||||
|
@ -85,15 +85,15 @@ impl Window {
|
|||||||
self.needs_refresh_resize = false;
|
self.needs_refresh_resize = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy data that is needed by the events closure to avoid lifetime errors
|
// Copy data that is needed by the events closure to avoid lifetime errors.
|
||||||
// TODO: Remove this if/when the compiler permits it
|
// TODO: Remove this if/when the compiler permits it.
|
||||||
let cursor_grabbed = self.cursor_grabbed;
|
let cursor_grabbed = self.cursor_grabbed;
|
||||||
let renderer = &mut self.renderer;
|
let renderer = &mut self.renderer;
|
||||||
let window = &mut self.window;
|
let window = &mut self.window;
|
||||||
let key_map = &self.key_map;
|
let key_map = &self.key_map;
|
||||||
|
|
||||||
self.events_loop.poll_events(|event| {
|
self.events_loop.poll_events(|event| {
|
||||||
// Get events for ui
|
// Get events for ui.
|
||||||
if let Some(event) = ui::Event::try_from(event.clone(), &window) {
|
if let Some(event) = ui::Event::try_from(event.clone(), &window) {
|
||||||
events.push(Event::Ui(event));
|
events.push(Event::Ui(event));
|
||||||
}
|
}
|
||||||
@ -174,7 +174,7 @@ impl Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a key that the game recognises after keyboard mapping
|
/// Represents a key that the game recognises after keyboard mapping.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum Key {
|
pub enum Key {
|
||||||
ToggleCursor,
|
ToggleCursor,
|
||||||
@ -197,12 +197,12 @@ pub enum Key {
|
|||||||
Help,
|
Help,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an incoming event from the window
|
/// Represents an incoming event from the window.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
/// The window has been requested to close.
|
/// The window has been requested to close.
|
||||||
Close,
|
Close,
|
||||||
/// The window has been resized
|
/// The window has been resized.
|
||||||
Resize(Vec2<u32>),
|
Resize(Vec2<u32>),
|
||||||
/// A key has been typed that corresponds to a specific character.
|
/// A key has been typed that corresponds to a specific character.
|
||||||
Char(char),
|
Char(char),
|
||||||
@ -210,12 +210,12 @@ pub enum Event {
|
|||||||
CursorPan(Vec2<f32>),
|
CursorPan(Vec2<f32>),
|
||||||
/// The camera has been requested to zoom.
|
/// The camera has been requested to zoom.
|
||||||
Zoom(f32),
|
Zoom(f32),
|
||||||
/// A key that the game recognises has been pressed down
|
/// A key that the game recognises has been pressed down.
|
||||||
KeyDown(Key),
|
KeyDown(Key),
|
||||||
/// A key that the game recognises has been released down
|
/// A key that the game recognises has been released down.
|
||||||
KeyUp(Key),
|
KeyUp(Key),
|
||||||
/// Event that the ui uses
|
/// Event that the ui uses.
|
||||||
Ui(ui::Event),
|
Ui(ui::Event),
|
||||||
/// Game settings have changed
|
/// Game settings have changed.
|
||||||
SettingsChanged,
|
SettingsChanged,
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_chunk(chunk_pos: Vec2<i32>) -> TerrainChunk {
|
pub fn generate_chunk(chunk_pos: Vec2<i32>) -> TerrainChunk {
|
||||||
// TODO: This is all test code, remove/improve this later
|
// TODO: This is all test code, remove/improve this later.
|
||||||
|
|
||||||
let air = Block::empty();
|
let air = Block::empty();
|
||||||
let stone = Block::new(1, Rgb::new(200, 220, 255));
|
let stone = Block::new(1, Rgb::new(200, 220, 255));
|
||||||
|
Loading…
Reference in New Issue
Block a user