Added /explosion command

This commit is contained in:
Joshua Barretto
2019-08-07 18:17:04 +01:00
parent 192f5d355f
commit 5b62531da3
6 changed files with 84 additions and 13 deletions

View File

@ -4,6 +4,7 @@ use vek::*;
pub enum Event { pub enum Event {
LandOnGround { entity: EcsEntity, vel: Vec3<f32> }, LandOnGround { entity: EcsEntity, vel: Vec3<f32> },
Explosion { pos: Vec3<f32>, radius: f32 },
} }
#[derive(Default)] #[derive(Default)]
@ -19,6 +20,10 @@ impl EventBus {
} }
} }
pub fn emit(&self, event: Event) {
self.queue.lock().unwrap().push_front(event);
}
pub fn recv_all(&self) -> impl ExactSizeIterator<Item = Event> { pub fn recv_all(&self) -> impl ExactSizeIterator<Item = Event> {
std::mem::replace(self.queue.lock().unwrap().deref_mut(), VecDeque::new()).into_iter() std::mem::replace(self.queue.lock().unwrap().deref_mut(), VecDeque::new()).into_iter()
} }

View File

@ -2,32 +2,47 @@ use crate::vol::{ReadVol, Vox};
use vek::*; use vek::*;
pub trait RayUntil<V: Vox> = FnMut(&V) -> bool; pub trait RayUntil<V: Vox> = FnMut(&V) -> bool;
pub trait RayForEach = FnMut(Vec3<i32>);
pub struct Ray<'a, V: ReadVol, F: RayUntil<V::Vox>> { pub struct Ray<'a, V: ReadVol, F: RayUntil<V::Vox>, G: RayForEach> {
vol: &'a V, vol: &'a V,
from: Vec3<f32>, from: Vec3<f32>,
to: Vec3<f32>, to: Vec3<f32>,
until: F, until: F,
for_each: Option<G>,
max_iter: usize, max_iter: usize,
ignore_error: bool, ignore_error: bool,
} }
impl<'a, V: ReadVol, F: RayUntil<V::Vox>> Ray<'a, V, F> { impl<'a, V: ReadVol, F: RayUntil<V::Vox>, G: RayForEach> Ray<'a, V, F, G> {
pub fn new(vol: &'a V, from: Vec3<f32>, to: Vec3<f32>, until: F) -> Self { pub fn new(vol: &'a V, from: Vec3<f32>, to: Vec3<f32>, until: F) -> Self {
Self { Self {
vol, vol,
from, from,
to, to,
until, until,
for_each: None,
max_iter: 100, max_iter: 100,
ignore_error: false, ignore_error: false,
} }
} }
pub fn until(self, f: F) -> Ray<'a, V, F> { pub fn until(self, f: F) -> Ray<'a, V, F, G> {
Ray { until: f, ..self } Ray { until: f, ..self }
} }
pub fn for_each<H: RayForEach>(self, f: H) -> Ray<'a, V, F, H> {
Ray {
for_each: Some(f),
vol: self.vol,
from: self.from,
to: self.to,
until: self.until,
max_iter: self.max_iter,
ignore_error: self.ignore_error,
}
}
pub fn max_iter(mut self, max_iter: usize) -> Self { pub fn max_iter(mut self, max_iter: usize) -> Self {
self.max_iter = max_iter; self.max_iter = max_iter;
self self
@ -56,6 +71,11 @@ impl<'a, V: ReadVol, F: RayUntil<V::Vox>> Ray<'a, V, F> {
break; break;
} }
// for_each
if let Some(g) = &mut self.for_each {
g(ipos);
}
match self.vol.get(ipos).map(|vox| (vox, (self.until)(vox))) { match self.vol.get(ipos).map(|vox| (vox, (self.until)(vox))) {
Ok((vox, true)) => return (dist, Ok(Some(vox))), Ok((vox, true)) => return (dist, Ok(Some(vox))),
Err(err) if !self.ignore_error => return (dist, Err(err)), Err(err) if !self.ignore_error => return (dist, Err(err)),

View File

@ -315,16 +315,15 @@ impl State {
.for_each(|(pos, block)| { .for_each(|(pos, block)| {
let _ = terrain.set(*pos, *block); let _ = terrain.set(*pos, *block);
}); });
std::mem::swap( self.ecs.write_resource::<TerrainChanges>().modified_blocks = std::mem::replace(
&mut self.ecs.write_resource::<BlockChange>().blocks, &mut self.ecs.write_resource::<BlockChange>().blocks,
&mut self.ecs.write_resource::<TerrainChanges>().modified_blocks, Default::default(),
) );
} }
/// 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.ecs.write_resource::<TerrainChanges>().clear(); self.ecs.write_resource::<TerrainChanges>().clear();
self.ecs.write_resource::<BlockChange>().clear();
} }
} }

View File

@ -76,7 +76,11 @@ pub trait ReadVol: BaseVol {
self.get(pos).unwrap() self.get(pos).unwrap()
} }
fn ray(&self, from: Vec3<f32>, to: Vec3<f32>) -> Ray<Self, fn(&Self::Vox) -> bool> fn ray(
&self,
from: Vec3<f32>,
to: Vec3<f32>,
) -> Ray<Self, fn(&Self::Vox) -> bool, fn(Vec3<i32>)>
where where
Self: Sized, Self: Sized,
{ {

View File

@ -6,6 +6,7 @@ use crate::Server;
use chrono::{NaiveTime, Timelike}; use chrono::{NaiveTime, Timelike};
use common::{ use common::{
comp, comp,
event::{Event as GameEvent, EventBus},
msg::ServerMsg, msg::ServerMsg,
npc::{get_npc_name, NpcKind}, npc::{get_npc_name, NpcKind},
state::TimeOfDay, state::TimeOfDay,
@ -147,10 +148,16 @@ lazy_static! {
), ),
ChatCommand::new( ChatCommand::new(
"lantern", "lantern",
"{} ", "{}",
"/lantern : adds/remove light near player", "/lantern : adds/remove light near player",
handle_lantern, handle_lantern,
), ),
ChatCommand::new(
"explosion",
"{}",
"/explosion <radius> : Explodes the ground around you",
handle_explosion,
),
]; ];
} }
@ -669,6 +676,22 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
} }
} }
fn handle_explosion(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
if let Ok(radius) = scan_fmt!(&args, action.arg_fmt, f32) {
match server.state.read_component_cloned::<comp::Pos>(entity) {
Some(pos) => server
.state
.ecs()
.read_resource::<EventBus>()
.emit(GameEvent::Explosion { pos: pos.0, radius }),
None => server.clients.notify(
entity,
ServerMsg::private(String::from("You have no position!")),
),
}
}
}
fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
let ecs = server.state.ecs(); let ecs = server.state.ecs();

View File

@ -18,7 +18,7 @@ use common::{
event::{Event as GameEvent, EventBus}, event::{Event as GameEvent, EventBus},
msg::{ClientMsg, ClientState, RequestStateError, ServerError, ServerInfo, ServerMsg}, msg::{ClientMsg, ClientState, RequestStateError, ServerError, ServerInfo, ServerMsg},
net::PostOffice, net::PostOffice,
state::{State, TimeOfDay, Uid}, state::{BlockChange, State, TimeOfDay, Uid},
terrain::{block::Block, TerrainChunk, TerrainChunkSize, TerrainMap}, terrain::{block::Block, TerrainChunk, TerrainChunkSize, TerrainMap},
vol::Vox, vol::Vox,
vol::{ReadVol, VolSize}, vol::{ReadVol, VolSize},
@ -203,6 +203,8 @@ impl Server {
/// Handle events coming through via the event bus /// Handle events coming through via the event bus
fn handle_events(&mut self) { fn handle_events(&mut self) {
let terrain = self.state.ecs().read_resource::<TerrainMap>();
let mut block_change = self.state.ecs().write_resource::<BlockChange>();
let mut stats = self.state.ecs().write_storage::<comp::Stats>(); let mut stats = self.state.ecs().write_storage::<comp::Stats>();
for event in self.state.ecs().read_resource::<EventBus>().recv_all() { for event in self.state.ecs().read_resource::<EventBus>().recv_all() {
@ -215,6 +217,24 @@ impl Server {
} }
} }
} }
GameEvent::Explosion { pos, radius } => {
const RAYS: usize = 500;
for _ in 0..RAYS {
let dir = Vec3::new(
rand::random::<f32>() - 0.5,
rand::random::<f32>() - 0.5,
rand::random::<f32>() - 0.5,
)
.normalized();
let _ = terrain
.ray(pos, pos + dir * radius)
.until(|_| rand::random::<f32>() < 0.05)
.for_each(|pos| block_change.set(pos, Block::empty()))
.cast();
}
}
} }
} }
} }
@ -249,6 +269,9 @@ impl Server {
frontend_events.append(&mut self.handle_new_connections()?); frontend_events.append(&mut self.handle_new_connections()?);
frontend_events.append(&mut self.handle_new_messages()?); frontend_events.append(&mut self.handle_new_messages()?);
// Handle game events
self.handle_events();
// 4) Tick the client's LocalState. // 4) Tick the client's LocalState.
self.state.tick(dt); self.state.tick(dt);
@ -371,9 +394,6 @@ impl Server {
self.state.remove_chunk(key); self.state.remove_chunk(key);
} }
// Handle events
self.handle_events();
// 6) Synchronise clients with the new state of the world. // 6) Synchronise clients with the new state of the world.
self.sync_clients(); self.sync_clients();