Merge branch 'build-mode' into 'master'

Build mode and crosshair

See merge request veloren/veloren!290
This commit is contained in:
Timo Koesters 2019-07-03 20:50:28 +00:00
commit 4de3a64d7b
16 changed files with 169 additions and 64 deletions

Binary file not shown.

View File

@ -12,7 +12,7 @@ use common::{
msg::{ClientMsg, ClientState, ServerInfo, ServerMsg},
net::PostBox,
state::State,
terrain::{chonk::ChonkMetrics, TerrainChunk, TerrainChunkSize},
terrain::{block::Block, chonk::ChonkMetrics, TerrainChunk, TerrainChunkSize},
vol::VolSize,
};
use log::{info, log_enabled, warn};
@ -185,6 +185,14 @@ impl Client {
self.pending_chunks.clear();
}
pub fn place_block(&mut self, pos: Vec3<i32>, block: Block) {
self.postbox.send_message(ClientMsg::PlaceBlock(pos, block));
}
pub fn remove_block(&mut self, pos: Vec3<i32>) {
self.postbox.send_message(ClientMsg::BreakBlock(pos));
}
/// Execute a single client tick, handle input and update the game state by the given duration.
#[allow(dead_code)]
pub fn tick(

View File

@ -28,6 +28,9 @@ pub struct Rolling {
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct OnGround;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct CanBuild;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Jumping;
@ -85,6 +88,10 @@ impl Component for OnGround {
type Storage = NullStorage<Self>;
}
impl Component for CanBuild {
type Storage = FlaggedStorage<Self, NullStorage<Self>>;
}
impl Component for Jumping {
type Storage = NullStorage<Self>;
}

View File

@ -15,7 +15,9 @@ pub use agent::Agent;
pub use animation::{Animation, AnimationInfo};
pub use body::{humanoid, quadruped, quadruped_medium, Body};
pub use controller::Controller;
pub use inputs::{Attacking, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling, Wielding};
pub use inputs::{
Attacking, CanBuild, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling, Wielding,
};
pub use inventory::{item, Inventory};
pub use phys::{ForceUpdate, Ori, Pos, Vel};
pub use player::Player;

View File

@ -1,5 +1,6 @@
use super::ClientState;
use crate::comp;
use crate::terrain::block::Block;
use vek::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -14,6 +15,8 @@ pub enum ClientMsg {
Controller(comp::Controller),
RequestState(ClientState),
SetViewDistance(u32),
BreakBlock(Vec3<i32>),
PlaceBlock(Vec3<i32>, Block),
Ping,
Pong,
Chat(String),

View File

@ -22,6 +22,7 @@ sphynx::sum_type! {
Ori(comp::Ori),
Body(comp::Body),
Player(comp::Player),
CanBuild(comp::CanBuild),
Stats(comp::Stats),
}
}
@ -35,6 +36,7 @@ sphynx::sum_type! {
Ori(PhantomData<comp::Ori>),
Body(PhantomData<comp::Body>),
Player(PhantomData<comp::Player>),
CanBuild(PhantomData<comp::CanBuild>),
Stats(PhantomData<comp::Stats>),
}
}

View File

@ -129,6 +129,7 @@ impl State {
ecs.register_synced::<comp::Body>();
ecs.register_synced::<comp::Player>();
ecs.register_synced::<comp::Stats>();
ecs.register_synced::<comp::CanBuild>();
// Register components synced by other means
ecs.register::<comp::Pos>();

View File

@ -2,7 +2,7 @@ use crate::vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol};
use serde_derive::{Deserialize, Serialize};
use vek::*;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum DynaErr {
OutOfBounds,
}

View File

@ -6,7 +6,7 @@ use fxhash::FxHashMap;
use std::{collections::hash_map, fmt::Debug, marker::PhantomData, sync::Arc};
use vek::*;
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum VolMap2dErr<V: BaseVol> {
NoSuchChunk,
ChunkErr(V::Err),

View File

@ -7,9 +7,7 @@ use common::{
comp,
msg::ServerMsg,
npc::{get_npc_name, NpcKind},
state::{TerrainChange, TimeOfDay},
terrain::Block,
vol::Vox,
state::TimeOfDay,
};
use specs::{Builder, Entity as EcsEntity, Join};
use vek::*;
@ -106,18 +104,6 @@ lazy_static! {
"/players : Show the online players list",
handle_players,
),
ChatCommand::new(
"solid",
"{}",
"/solid : Make the blocks around you solid",
handle_solid,
),
ChatCommand::new(
"empty",
"{}",
"/empty : Make the blocks around you empty",
handle_empty,
),
ChatCommand::new(
"help", "", "/help: Display this message", handle_help),
ChatCommand::new(
@ -125,7 +111,13 @@ lazy_static! {
"{}",
"/health : Set your current health",
handle_health,
)
),
ChatCommand::new(
"build",
"",
"/build : Toggles build mode on and off",
handle_build,
),
];
}
@ -359,41 +351,32 @@ fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action
}
}
fn handle_solid(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) {
match server.state.read_component_cloned::<comp::Pos>(entity) {
Some(current_pos) => {
server.state.ecs().write_resource::<TerrainChange>().set(
current_pos.0.map(|e| e.floor() as i32),
Block::new(1, Rgb::broadcast(255)),
);
}
None => server.clients.notify(
fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) {
if server
.state
.read_storage::<comp::CanBuild>()
.get(entity)
.is_some()
{
server
.state
.ecs()
.write_storage::<comp::CanBuild>()
.remove(entity);
server.clients.notify(
entity,
ServerMsg::Chat(String::from("You have no position!")),
),
}
}
fn handle_empty(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) {
match server.state.read_component_cloned::<comp::Pos>(entity) {
Some(current_pos) => {
let mut terrain_change = server.state.ecs().write_resource::<TerrainChange>();
for i in -1..2 {
for j in -1..2 {
for k in -2..1 {
terrain_change.set(
current_pos.0.map(|e| e.floor() as i32) + Vec3::new(i, j, k),
Block::empty(),
);
}
}
}
}
None => server.clients.notify(
ServerMsg::Chat(String::from("Toggled off build mode!")),
);
} else {
let _ = server
.state
.ecs()
.write_storage::<comp::CanBuild>()
.insert(entity, comp::CanBuild);
server.clients.notify(
entity,
ServerMsg::Chat(String::from("You have no position!")),
),
ServerMsg::Chat(String::from("Toggled on build mode!")),
);
}
}

View File

@ -16,9 +16,10 @@ use common::{
comp,
msg::{ClientMsg, ClientState, RequestStateError, ServerInfo, ServerMsg},
net::PostOffice,
state::{State, Uid},
terrain::{TerrainChunk, TerrainChunkSize, TerrainMap},
state::{State, TerrainChange, Uid},
terrain::{block::Block, TerrainChunk, TerrainChunkSize, TerrainMap},
vol::VolSize,
vol::Vox,
};
use log::debug;
use specs::{join::Join, world::EntityBuilder as EcsEntityBuilder, Builder, Entity as EcsEntity};
@ -500,6 +501,30 @@ impl Server {
// Only characters can send positions.
_ => client.error_state(RequestStateError::Impossible),
},
ClientMsg::BreakBlock(pos) => {
if state
.ecs_mut()
.read_storage::<comp::CanBuild>()
.get(entity)
.is_some()
{
let mut terrain_change =
state.ecs().write_resource::<TerrainChange>();
terrain_change.set(pos, Block::empty());
}
}
ClientMsg::PlaceBlock(pos, block) => {
if state
.ecs_mut()
.read_storage::<comp::CanBuild>()
.get(entity)
.is_some()
{
let mut terrain_change =
state.ecs().write_resource::<TerrainChange>();
terrain_change.set(pos, block);
}
}
ClientMsg::TerrainChunkRequest { key } => match client.client_state {
ClientState::Connected
| ClientState::Registered

View File

@ -49,9 +49,6 @@ image_ids! {
chat_arrow_mo: "voxygen/element/buttons/arrow_down_hover.vox",
chat_arrow_press: "voxygen/element/buttons/arrow_down_press.vox",
// Crosshair
crosshair: "voxygen/element/misc_bg/crosshair.vox",
////////////////////////////////////////////////////////////////////////
<VoxelMs9Graphic>
@ -122,6 +119,9 @@ image_ids! {
button_hover: "voxygen/element/buttons/button_hover.vox",
button_press: "voxygen/element/buttons/button_press.vox",
// Crosshair
crosshair: "voxygen/element/misc_bg/crosshair.vox",
//////////////////////////////////////////////////////////////////////////////////////////////////////
<ImageGraphic>

View File

@ -48,6 +48,9 @@ const MANA_COLOR: Color = Color::Rgba(0.42, 0.41, 0.66, 1.0);
widget_ids! {
struct Ids {
// Crosshair
crosshair,
// Character Names
name_tags[],
// Health Bars
@ -330,6 +333,13 @@ impl Hud {
let mut health_id_walker = self.ids.health_bars.walk();
let mut health_back_id_walker = self.ids.health_bar_backs.walk();
// Crosshair
Image::new(self.imgs.crosshair)
.w_h(21.0 * 2.0, 21.0 * 2.0)
.middle_of(ui_widgets.window)
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
.set(self.ids.crosshair, ui_widgets);
// Render Name Tags
for (pos, name) in (&entities, &pos, &stats, player.maybe())
.join()

View File

@ -8,7 +8,9 @@ use crate::{
Direction, Error, GlobalState, PlayState, PlayStateResult,
};
use client::{self, Client};
use common::{clock::Clock, comp, comp::Pos, msg::ClientState};
use common::{
clock::Clock, comp, comp::Pos, msg::ClientState, ray::Ray, terrain::block::Block, vol::ReadVol,
};
use log::{error, warn};
use std::{cell::RefCell, rc::Rc, time::Duration};
use vek::*;
@ -108,17 +110,75 @@ impl PlayState for SessionState {
return PlayStateResult::Shutdown;
}
Event::InputUpdate(GameInput::Attack, state) => {
// Check the existence of CanBuild component. If it's here, use LMB to
// place blocks, if not, use it to attack
if state {
if let ClientState::Character = current_client_state {
self.controller.attack = state;
let mut client = self.client.borrow_mut();
if client
.state()
.read_storage::<comp::CanBuild>()
.get(client.entity())
.is_some()
{
let (view_mat, _, cam_pos) =
self.scene.camera().compute_dependents(&client);
let cam_dir =
(self.scene.camera().get_focus_pos() - cam_pos).normalized();
let (d, b) = {
let terrain = client.state().terrain();
let ray =
terrain.ray(cam_pos, cam_pos + cam_dir * 100.0).cast();
(ray.0, if let Ok(Some(_)) = ray.1 { true } else { false })
};
if b {
let pos =
(cam_pos + cam_dir * (d - 0.01)).map(|e| e.floor() as i32);
client.place_block(pos, Block::new(1, Rgb::broadcast(255))); // TODO: Handle block color with a command
}
} else {
self.controller.respawn = state; // TODO: Don't do both
if let ClientState::Character = current_client_state {
self.controller.attack = state
} else {
self.controller.respawn = state; // TODO: Don't do both
}
}
} else {
self.controller.attack = state;
self.controller.respawn = state;
}
}
Event::InputUpdate(GameInput::SecondAttack, state) => {
if state {
let mut client = self.client.borrow_mut();
if client
.state()
.read_storage::<comp::CanBuild>()
.get(client.entity())
.is_some()
{
let (view_mat, _, cam_pos) =
self.scene.camera().compute_dependents(&client);
let cam_dir =
(self.scene.camera().get_focus_pos() - cam_pos).normalized();
let (d, b) = {
let terrain = client.state().terrain();
let ray =
terrain.ray(cam_pos, cam_pos + cam_dir * 100.0).cast();
(ray.0, if let Ok(Some(_)) = ray.1 { true } else { false })
};
if b {
let pos = (cam_pos + cam_dir * d).map(|e| e.floor() as i32);
client.remove_block(pos);
}
} else {
// TODO: Handle secondary attack
}
}
}
Event::InputUpdate(GameInput::Roll, state) => {
self.controller.roll = state;
}

View File

@ -31,6 +31,7 @@ pub struct ControlSettings {
pub screenshot: KeyMouse,
pub toggle_ingame_ui: KeyMouse,
pub attack: KeyMouse,
pub second_attack: KeyMouse,
pub roll: KeyMouse,
}
@ -60,6 +61,7 @@ impl Default for ControlSettings {
screenshot: KeyMouse::Key(VirtualKeyCode::F4),
toggle_ingame_ui: KeyMouse::Key(VirtualKeyCode::F6),
attack: KeyMouse::Mouse(MouseButton::Left),
second_attack: KeyMouse::Mouse(MouseButton::Right),
roll: KeyMouse::Mouse(MouseButton::Middle),
}
}

View File

@ -34,6 +34,7 @@ pub enum GameInput {
Screenshot,
ToggleIngameUi,
Attack,
SecondAttack,
Roll,
Respawn,
}
@ -138,6 +139,7 @@ impl Window {
GameInput::ToggleIngameUi,
);
key_map.insert(settings.controls.attack, GameInput::Attack);
key_map.insert(settings.controls.second_attack, GameInput::SecondAttack);
key_map.insert(settings.controls.roll, GameInput::Roll);
let keypress_map = HashMap::new();