mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add fundamentals for wiring system.
This commit is contained in:
parent
247ed4d78a
commit
7faa0d3cd9
@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Lift is now calculated for gliders based on dimensions (currently same for all)
|
- Lift is now calculated for gliders based on dimensions (currently same for all)
|
||||||
- Specific music tracks can now play exclusively in towns.
|
- Specific music tracks can now play exclusively in towns.
|
||||||
- Custom map markers can be placed now
|
- Custom map markers can be placed now
|
||||||
|
- Fundamentals/prototype for wiring system
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -94,6 +94,7 @@ pub enum ChatCommand {
|
|||||||
Unban,
|
Unban,
|
||||||
Version,
|
Version,
|
||||||
Waypoint,
|
Waypoint,
|
||||||
|
Wiring,
|
||||||
Whitelist,
|
Whitelist,
|
||||||
World,
|
World,
|
||||||
}
|
}
|
||||||
@ -157,6 +158,7 @@ pub static CHAT_COMMANDS: &[ChatCommand] = &[
|
|||||||
ChatCommand::Unban,
|
ChatCommand::Unban,
|
||||||
ChatCommand::Version,
|
ChatCommand::Version,
|
||||||
ChatCommand::Waypoint,
|
ChatCommand::Waypoint,
|
||||||
|
ChatCommand::Wiring,
|
||||||
ChatCommand::Whitelist,
|
ChatCommand::Whitelist,
|
||||||
ChatCommand::World,
|
ChatCommand::World,
|
||||||
];
|
];
|
||||||
@ -529,6 +531,7 @@ impl ChatCommand {
|
|||||||
ChatCommand::Waypoint => {
|
ChatCommand::Waypoint => {
|
||||||
cmd(vec![], "Set your waypoint to your current position", Admin)
|
cmd(vec![], "Set your waypoint to your current position", Admin)
|
||||||
},
|
},
|
||||||
|
ChatCommand::Wiring => cmd(vec![], "Create wiring element", Admin),
|
||||||
ChatCommand::Whitelist => cmd(
|
ChatCommand::Whitelist => cmd(
|
||||||
vec![Any("add/remove", Required), Any("username", Required)],
|
vec![Any("add/remove", Required), Any("username", Required)],
|
||||||
"Adds/removes username to whitelist",
|
"Adds/removes username to whitelist",
|
||||||
@ -602,6 +605,7 @@ impl ChatCommand {
|
|||||||
ChatCommand::Unban => "unban",
|
ChatCommand::Unban => "unban",
|
||||||
ChatCommand::Version => "version",
|
ChatCommand::Version => "version",
|
||||||
ChatCommand::Waypoint => "waypoint",
|
ChatCommand::Waypoint => "waypoint",
|
||||||
|
ChatCommand::Wiring => "wiring",
|
||||||
ChatCommand::Whitelist => "whitelist",
|
ChatCommand::Whitelist => "whitelist",
|
||||||
ChatCommand::World => "world",
|
ChatCommand::World => "world",
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,13 @@ use core::{convert::TryFrom, ops::Not, time::Duration};
|
|||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
|
use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
|
||||||
|
use wiring::{Circuit, Wire, WiringAction, WiringActionEffect, WiringElement};
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use world::util::Sampler;
|
use world::util::Sampler;
|
||||||
|
|
||||||
use crate::{client::Client, login_provider::LoginProvider};
|
use crate::{client::Client, login_provider::LoginProvider, wiring};
|
||||||
use scan_fmt::{scan_fmt, scan_fmt_some};
|
use scan_fmt::{scan_fmt, scan_fmt_some};
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
@ -149,6 +151,7 @@ fn get_handler(cmd: &ChatCommand) -> CommandHandler {
|
|||||||
ChatCommand::Unban => handle_unban,
|
ChatCommand::Unban => handle_unban,
|
||||||
ChatCommand::Version => handle_version,
|
ChatCommand::Version => handle_version,
|
||||||
ChatCommand::Waypoint => handle_waypoint,
|
ChatCommand::Waypoint => handle_waypoint,
|
||||||
|
ChatCommand::Wiring => handle_spawn_wiring,
|
||||||
ChatCommand::Whitelist => handle_whitelist,
|
ChatCommand::Whitelist => handle_whitelist,
|
||||||
ChatCommand::World => handle_world,
|
ChatCommand::World => handle_world,
|
||||||
}
|
}
|
||||||
@ -1702,6 +1705,102 @@ fn handle_waypoint(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_spawn_wiring(
|
||||||
|
server: &mut Server,
|
||||||
|
client: EcsEntity,
|
||||||
|
target: EcsEntity,
|
||||||
|
_args: String,
|
||||||
|
_action: &ChatCommand,
|
||||||
|
) -> CmdResult<()> {
|
||||||
|
// Obviously it is a WIP - use it for debug
|
||||||
|
|
||||||
|
let mut pos = position(server, target, "target")?;
|
||||||
|
pos.0.z += 1.0;
|
||||||
|
pos.0.x += 3.0;
|
||||||
|
|
||||||
|
let mut outputs1 = HashMap::new();
|
||||||
|
outputs1.insert(String::from("color"), wiring::OutputFormula::OnCollide {
|
||||||
|
value: 1.0,
|
||||||
|
});
|
||||||
|
|
||||||
|
let builder1 = server
|
||||||
|
.state
|
||||||
|
.create_wiring(pos, comp::object::Body::Coins, WiringElement {
|
||||||
|
actions: vec![WiringAction {
|
||||||
|
formula: wiring::OutputFormula::Constant { value: 1.0 },
|
||||||
|
threshold: 1.0,
|
||||||
|
effects: vec![WiringActionEffect::SetLight {
|
||||||
|
r: wiring::OutputFormula::Input {
|
||||||
|
name: String::from("color"),
|
||||||
|
},
|
||||||
|
g: wiring::OutputFormula::Input {
|
||||||
|
name: String::from("color"),
|
||||||
|
},
|
||||||
|
b: wiring::OutputFormula::Input {
|
||||||
|
name: String::from("color"),
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
inputs: HashMap::new(),
|
||||||
|
outputs: outputs1,
|
||||||
|
});
|
||||||
|
let ent1 = builder1.build();
|
||||||
|
|
||||||
|
pos.0.x += 3.0;
|
||||||
|
let builder2 = server
|
||||||
|
.state
|
||||||
|
.create_wiring(pos, comp::object::Body::Coins, WiringElement {
|
||||||
|
actions: vec![WiringAction {
|
||||||
|
formula: wiring::OutputFormula::Input {
|
||||||
|
name: String::from("color"),
|
||||||
|
},
|
||||||
|
threshold: 1.0,
|
||||||
|
effects: vec![
|
||||||
|
// Another demo:
|
||||||
|
// WiringActionEffect::SetLight {
|
||||||
|
// r: wiring::OutputFormula::Input { name: String::from("color") },
|
||||||
|
// g: wiring::OutputFormula::Input { name: String::from("color") },
|
||||||
|
// b: wiring::OutputFormula::Input { name: String::from("color") }
|
||||||
|
// },
|
||||||
|
WiringActionEffect::SpawnProjectile {
|
||||||
|
constr: comp::ProjectileConstructor::Arrow {
|
||||||
|
damage: 1.0,
|
||||||
|
energy_regen: 0.0,
|
||||||
|
knockback: 0.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
inputs: HashMap::new(),
|
||||||
|
outputs: HashMap::new(),
|
||||||
|
});
|
||||||
|
let ent2 = builder2.build();
|
||||||
|
|
||||||
|
pos.0.x += 3.0;
|
||||||
|
let builder3 = server
|
||||||
|
.state
|
||||||
|
.create_wiring(pos, comp::object::Body::TrainingDummy, WiringElement {
|
||||||
|
actions: vec![],
|
||||||
|
inputs: HashMap::new(),
|
||||||
|
outputs: HashMap::new(),
|
||||||
|
})
|
||||||
|
.with(Circuit {
|
||||||
|
wires: vec![Wire {
|
||||||
|
input_entity: ent1,
|
||||||
|
input_field: String::from("color"),
|
||||||
|
output_entity: ent2,
|
||||||
|
output_field: String::from("color"),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
builder3.build();
|
||||||
|
|
||||||
|
server.notify_client(
|
||||||
|
client,
|
||||||
|
ServerGeneral::server_msg(ChatType::CommandInfo, "Wire"),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::useless_conversion)] // TODO: Pending review in #587
|
#[allow(clippy::useless_conversion)] // TODO: Pending review in #587
|
||||||
fn handle_adminify(
|
fn handle_adminify(
|
||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
|
@ -30,6 +30,7 @@ pub mod settings;
|
|||||||
pub mod state_ext;
|
pub mod state_ext;
|
||||||
pub mod sys;
|
pub mod sys;
|
||||||
#[cfg(not(feature = "worldgen"))] mod test_world;
|
#[cfg(not(feature = "worldgen"))] mod test_world;
|
||||||
|
pub mod wiring;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
@ -242,6 +243,8 @@ impl Server {
|
|||||||
state.ecs_mut().register::<RegionSubscription>();
|
state.ecs_mut().register::<RegionSubscription>();
|
||||||
state.ecs_mut().register::<Client>();
|
state.ecs_mut().register::<Client>();
|
||||||
state.ecs_mut().register::<Presence>();
|
state.ecs_mut().register::<Presence>();
|
||||||
|
state.ecs_mut().register::<wiring::WiringElement>();
|
||||||
|
state.ecs_mut().register::<wiring::Circuit>();
|
||||||
state.ecs_mut().register::<comp::HomeChunk>();
|
state.ecs_mut().register::<comp::HomeChunk>();
|
||||||
state.ecs_mut().register::<login_provider::PendingLogin>();
|
state.ecs_mut().register::<login_provider::PendingLogin>();
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
client::Client, persistence::PersistedComponents, presence::Presence, settings::Settings,
|
client::Client, persistence::PersistedComponents, presence::Presence, settings::Settings,
|
||||||
sys::sentinel::DeletedEntities, SpawnPoint,
|
sys::sentinel::DeletedEntities, wiring, SpawnPoint,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
character::CharacterId,
|
character::CharacterId,
|
||||||
@ -76,6 +76,12 @@ pub trait StateExt {
|
|||||||
) -> EcsEntityBuilder;
|
) -> EcsEntityBuilder;
|
||||||
/// Creates a safezone
|
/// Creates a safezone
|
||||||
fn create_safezone(&mut self, range: Option<f32>, pos: comp::Pos) -> EcsEntityBuilder;
|
fn create_safezone(&mut self, range: Option<f32>, pos: comp::Pos) -> EcsEntityBuilder;
|
||||||
|
fn create_wiring(
|
||||||
|
&mut self,
|
||||||
|
pos: comp::Pos,
|
||||||
|
object: comp::object::Body,
|
||||||
|
wiring_element: wiring::WiringElement,
|
||||||
|
) -> EcsEntityBuilder;
|
||||||
// NOTE: currently only used for testing
|
// NOTE: currently only used for testing
|
||||||
/// Queues chunk generation in the view distance of the persister, this
|
/// Queues chunk generation in the view distance of the persister, this
|
||||||
/// entity must be built before those chunks are received (the builder
|
/// entity must be built before those chunks are received (the builder
|
||||||
@ -347,6 +353,34 @@ impl StateExt for State {
|
|||||||
)]))
|
)]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_wiring(
|
||||||
|
&mut self,
|
||||||
|
pos: comp::Pos,
|
||||||
|
object: comp::object::Body,
|
||||||
|
wiring_element: wiring::WiringElement,
|
||||||
|
) -> EcsEntityBuilder {
|
||||||
|
self.ecs_mut()
|
||||||
|
.create_entity_synced()
|
||||||
|
.with(pos)
|
||||||
|
.with(comp::Vel(Vec3::zero()))
|
||||||
|
.with(comp::Ori::default())
|
||||||
|
.with(comp::Collider::Box {
|
||||||
|
radius: comp::Body::Object(object).radius(),
|
||||||
|
z_min: 0.0,
|
||||||
|
z_max: comp::Body::Object(object).height()
|
||||||
|
})
|
||||||
|
.with(comp::Body::Object(object))
|
||||||
|
.with(comp::Mass(10.0))
|
||||||
|
// .with(comp::Sticky)
|
||||||
|
.with(wiring_element)
|
||||||
|
.with(comp::LightEmitter {
|
||||||
|
col: Rgb::new(0.0, 0.0, 0.0),
|
||||||
|
strength: 2.0,
|
||||||
|
flicker: 1.0,
|
||||||
|
animated: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: currently only used for testing
|
// NOTE: currently only used for testing
|
||||||
/// Queues chunk generation in the view distance of the persister, this
|
/// Queues chunk generation in the view distance of the persister, this
|
||||||
/// entity must be built before those chunks are received (the builder
|
/// entity must be built before those chunks are received (the builder
|
||||||
|
@ -10,6 +10,7 @@ pub mod subscription;
|
|||||||
pub mod terrain;
|
pub mod terrain;
|
||||||
pub mod terrain_sync;
|
pub mod terrain_sync;
|
||||||
pub mod waypoint;
|
pub mod waypoint;
|
||||||
|
pub mod wiring;
|
||||||
|
|
||||||
use common_ecs::{dispatch, run_now, System};
|
use common_ecs::{dispatch, run_now, System};
|
||||||
use common_systems::{melee, projectile};
|
use common_systems::{melee, projectile};
|
||||||
@ -30,6 +31,7 @@ pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) {
|
|||||||
dispatch::<invite_timeout::Sys>(dispatch_builder, &[]);
|
dispatch::<invite_timeout::Sys>(dispatch_builder, &[]);
|
||||||
dispatch::<persistence::Sys>(dispatch_builder, &[]);
|
dispatch::<persistence::Sys>(dispatch_builder, &[]);
|
||||||
dispatch::<object::Sys>(dispatch_builder, &[]);
|
dispatch::<object::Sys>(dispatch_builder, &[]);
|
||||||
|
dispatch::<wiring::Sys>(dispatch_builder, &[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_sync_systems(ecs: &mut specs::World) {
|
pub fn run_sync_systems(ecs: &mut specs::World) {
|
||||||
|
75
server/src/sys/wiring.rs
Normal file
75
server/src/sys/wiring.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
use crate::wiring::{Circuit, WiringElement};
|
||||||
|
use common::{
|
||||||
|
comp::{LightEmitter, PhysicsState},
|
||||||
|
event::{EventBus, ServerEvent},
|
||||||
|
};
|
||||||
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use specs::{
|
||||||
|
join::Join, shred::ResourceId, Entities, Entity, Read, ReadStorage, SystemData, World,
|
||||||
|
WriteStorage,
|
||||||
|
};
|
||||||
|
mod compute_outputs;
|
||||||
|
use compute_outputs::compute_outputs;
|
||||||
|
mod dispatch_actions;
|
||||||
|
use dispatch_actions::dispatch_actions;
|
||||||
|
|
||||||
|
#[derive(SystemData)]
|
||||||
|
pub struct WiringData<'a> {
|
||||||
|
pub entities: Entities<'a>,
|
||||||
|
pub event_bus: Read<'a, EventBus<ServerEvent>>,
|
||||||
|
pub circuits: ReadStorage<'a, Circuit>,
|
||||||
|
pub wiring_elements: WriteStorage<'a, WiringElement>,
|
||||||
|
pub light_emitters: WriteStorage<'a, LightEmitter>,
|
||||||
|
pub physics_states: ReadStorage<'a, PhysicsState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This system is responsible for handling wiring (signals and wiring systems)
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Sys;
|
||||||
|
impl<'a> System<'a> for Sys {
|
||||||
|
type SystemData = WiringData<'a>;
|
||||||
|
|
||||||
|
const NAME: &'static str = "wiring";
|
||||||
|
const ORIGIN: Origin = Origin::Server;
|
||||||
|
const PHASE: Phase = Phase::Create;
|
||||||
|
|
||||||
|
fn run(_job: &mut Job<Self>, mut system_data: Self::SystemData) {
|
||||||
|
// Calculate new outputs using inputs (those inputs are calculated and populated
|
||||||
|
// in previous tick) Take inputs and wiring_element.outputs and with
|
||||||
|
// that compute new outputs
|
||||||
|
let computed_outputs = compute_outputs(&system_data);
|
||||||
|
// Pass new outputs as inputs for the next tick
|
||||||
|
dispatch_circuit_transport(&computed_outputs, &mut system_data);
|
||||||
|
// Using inputs dispatch actions
|
||||||
|
dispatch_actions(&mut system_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_circuit_transport<'a>(
|
||||||
|
computed_outputs: &HashMap<Entity, HashMap<String, f32>>,
|
||||||
|
system_data: &mut WiringData<'a>,
|
||||||
|
) {
|
||||||
|
let WiringData {
|
||||||
|
circuits,
|
||||||
|
wiring_elements,
|
||||||
|
..
|
||||||
|
} = system_data;
|
||||||
|
|
||||||
|
(circuits)
|
||||||
|
.join()
|
||||||
|
.map(|circuit| circuit.wires.iter())
|
||||||
|
.flatten()
|
||||||
|
.for_each(|wire| {
|
||||||
|
let input_value = computed_outputs
|
||||||
|
.get(&wire.input_entity)
|
||||||
|
.and_then(|e| e.get(&wire.input_field))
|
||||||
|
.unwrap_or(&0.0);
|
||||||
|
|
||||||
|
if let Some(wiring_element) = wiring_elements.get_mut(wire.output_entity) {
|
||||||
|
wiring_element
|
||||||
|
.inputs
|
||||||
|
.insert(wire.output_field.clone(), *input_value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
94
server/src/sys/wiring/compute_outputs.rs
Normal file
94
server/src/sys/wiring/compute_outputs.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use super::WiringData;
|
||||||
|
use crate::wiring::OutputFormula;
|
||||||
|
use common::comp::PhysicsState;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use specs::{join::Join, Entity, ReadStorage};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
pub fn compute_outputs(system_data: &WiringData) -> HashMap<Entity, HashMap<String, f32>> {
|
||||||
|
let WiringData {
|
||||||
|
entities,
|
||||||
|
wiring_elements,
|
||||||
|
physics_states,
|
||||||
|
..
|
||||||
|
} = system_data;
|
||||||
|
(&*entities, wiring_elements)
|
||||||
|
.join()
|
||||||
|
.map(|(entity, wiring_element)| {
|
||||||
|
(
|
||||||
|
entity,
|
||||||
|
wiring_element
|
||||||
|
.outputs
|
||||||
|
.iter()
|
||||||
|
.map(
|
||||||
|
|(key, output_formula)| {
|
||||||
|
compute_output_with_key(
|
||||||
|
key,
|
||||||
|
output_formula,
|
||||||
|
&wiring_element.inputs,
|
||||||
|
entity,
|
||||||
|
physics_states,
|
||||||
|
)
|
||||||
|
}, // (String, f32)
|
||||||
|
)
|
||||||
|
.collect::<HashMap<_, _>>(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_output_with_key(
|
||||||
|
// yes, this function is defined only to make one place
|
||||||
|
// look a bit nicer
|
||||||
|
// Don't discuss.
|
||||||
|
key: &str,
|
||||||
|
output_formula: &OutputFormula,
|
||||||
|
inputs: &HashMap<String, f32>,
|
||||||
|
entity: Entity,
|
||||||
|
physics_states: &ReadStorage<PhysicsState>,
|
||||||
|
) -> (String, f32) {
|
||||||
|
(
|
||||||
|
key.to_string(),
|
||||||
|
compute_output(output_formula, inputs, entity, physics_states),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_output(
|
||||||
|
output_formula: &OutputFormula,
|
||||||
|
inputs: &HashMap<String, f32>,
|
||||||
|
entity: Entity,
|
||||||
|
physics_states: &ReadStorage<PhysicsState>,
|
||||||
|
) -> f32 {
|
||||||
|
match output_formula {
|
||||||
|
OutputFormula::Constant { value } => *value,
|
||||||
|
OutputFormula::Input { name } => *inputs.get(name).unwrap_or(&0.0),
|
||||||
|
OutputFormula::Logic(_logic) => {
|
||||||
|
warn!("Not implemented OutputFormula::Logic");
|
||||||
|
0.0
|
||||||
|
},
|
||||||
|
OutputFormula::SineWave { .. } => {
|
||||||
|
warn!("Not implemented OutputFormula::SineWave");
|
||||||
|
0.0
|
||||||
|
},
|
||||||
|
OutputFormula::OnCollide { value } => {
|
||||||
|
output_formula_on_collide(value, entity, physics_states)
|
||||||
|
},
|
||||||
|
OutputFormula::OnInteract { .. } => {
|
||||||
|
warn!("Not implemented OutputFormula::OnInteract");
|
||||||
|
0.0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_formula_on_collide(
|
||||||
|
value: &f32,
|
||||||
|
entity: Entity,
|
||||||
|
physics_states: &ReadStorage<PhysicsState>,
|
||||||
|
) -> f32 {
|
||||||
|
if let Some(ps) = physics_states.get(entity) {
|
||||||
|
if !ps.touch_entities.is_empty() {
|
||||||
|
return *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0.0
|
||||||
|
}
|
111
server/src/sys/wiring/dispatch_actions.rs
Normal file
111
server/src/sys/wiring/dispatch_actions.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
use super::{compute_outputs::compute_output, WiringData};
|
||||||
|
use crate::wiring::{OutputFormula, WiringActionEffect};
|
||||||
|
use common::{
|
||||||
|
comp::{object, Body, LightEmitter, PhysicsState, ProjectileConstructor},
|
||||||
|
event::{Emitter, ServerEvent},
|
||||||
|
util::Dir,
|
||||||
|
};
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use specs::{join::Join, Entity, ReadStorage, WriteStorage};
|
||||||
|
use tracing::warn;
|
||||||
|
use vek::Rgb;
|
||||||
|
|
||||||
|
pub fn dispatch_actions(system_data: &mut WiringData) {
|
||||||
|
let WiringData {
|
||||||
|
entities,
|
||||||
|
event_bus,
|
||||||
|
wiring_elements,
|
||||||
|
light_emitters,
|
||||||
|
physics_states,
|
||||||
|
..
|
||||||
|
} = system_data;
|
||||||
|
let mut server_emitter = event_bus.emitter();
|
||||||
|
|
||||||
|
(&*entities, wiring_elements)
|
||||||
|
.join()
|
||||||
|
.for_each(|(entity, wiring_element)| {
|
||||||
|
wiring_element
|
||||||
|
.actions
|
||||||
|
.iter()
|
||||||
|
.filter(|wiring_action| {
|
||||||
|
compute_output(
|
||||||
|
&wiring_action.formula,
|
||||||
|
&wiring_element.inputs,
|
||||||
|
entity,
|
||||||
|
physics_states,
|
||||||
|
) >= wiring_action.threshold
|
||||||
|
})
|
||||||
|
.for_each(|wiring_action| {
|
||||||
|
dispatch_action(
|
||||||
|
entity,
|
||||||
|
&wiring_element.inputs,
|
||||||
|
&wiring_action.effects,
|
||||||
|
&mut server_emitter,
|
||||||
|
light_emitters,
|
||||||
|
physics_states,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_action(
|
||||||
|
entity: Entity,
|
||||||
|
source: &HashMap<String, f32>,
|
||||||
|
action_effects: &[WiringActionEffect],
|
||||||
|
|
||||||
|
server_emitter: &mut Emitter<ServerEvent>,
|
||||||
|
light_emitters: &mut WriteStorage<LightEmitter>,
|
||||||
|
physics_states: &ReadStorage<PhysicsState>,
|
||||||
|
) {
|
||||||
|
action_effects
|
||||||
|
.iter()
|
||||||
|
.for_each(|action_effect| match action_effect {
|
||||||
|
WiringActionEffect::SetBlockCollidability { .. } => {
|
||||||
|
warn!("Not implemented WiringActionEffect::SetBlockCollidability")
|
||||||
|
},
|
||||||
|
WiringActionEffect::SpawnProjectile { constr } => {
|
||||||
|
dispatch_action_spawn_projectile(entity, constr, server_emitter)
|
||||||
|
},
|
||||||
|
WiringActionEffect::SetLight { r, g, b } => {
|
||||||
|
dispatch_action_set_light(entity, source, r, g, b, light_emitters, physics_states)
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_action_spawn_projectile(
|
||||||
|
entity: Entity,
|
||||||
|
constr: &ProjectileConstructor,
|
||||||
|
server_emitter: &mut Emitter<ServerEvent>,
|
||||||
|
) {
|
||||||
|
// Use match here if there will be more options
|
||||||
|
// NOTE: constr in RFC is about Arrow projectile
|
||||||
|
server_emitter.emit(ServerEvent::Shoot {
|
||||||
|
entity,
|
||||||
|
dir: Dir::forward(),
|
||||||
|
body: Body::Object(object::Body::Arrow),
|
||||||
|
projectile: constr.create_projectile(None, 1.0, 1.0),
|
||||||
|
light: None,
|
||||||
|
speed: 5.0,
|
||||||
|
object: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_action_set_light(
|
||||||
|
entity: Entity,
|
||||||
|
source: &HashMap<String, f32>,
|
||||||
|
r: &OutputFormula,
|
||||||
|
g: &OutputFormula,
|
||||||
|
b: &OutputFormula,
|
||||||
|
|
||||||
|
light_emitters: &mut WriteStorage<LightEmitter>,
|
||||||
|
physics_states: &ReadStorage<PhysicsState>,
|
||||||
|
) {
|
||||||
|
let mut light_emitter = light_emitters.get_mut(entity).unwrap();
|
||||||
|
|
||||||
|
// TODO: make compute_output accept multiple formulas
|
||||||
|
let computed_r = compute_output(r, source, entity, physics_states);
|
||||||
|
let computed_g = compute_output(g, source, entity, physics_states);
|
||||||
|
let computed_b = compute_output(b, source, entity, physics_states);
|
||||||
|
|
||||||
|
light_emitter.col = Rgb::new(computed_r, computed_g, computed_b);
|
||||||
|
}
|
74
server/src/wiring.rs
Normal file
74
server/src/wiring.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
use common::comp::ProjectileConstructor;
|
||||||
|
use core::f32;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use specs::{Component, Entity};
|
||||||
|
use specs_idvs::IdvStorage;
|
||||||
|
use vek::Vec3;
|
||||||
|
|
||||||
|
pub struct Logic {
|
||||||
|
pub kind: LogicKind,
|
||||||
|
pub left: OutputFormula,
|
||||||
|
pub right: OutputFormula,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WiringElement {
|
||||||
|
pub inputs: HashMap<String, f32>,
|
||||||
|
pub outputs: HashMap<String, OutputFormula>,
|
||||||
|
pub actions: Vec<WiringAction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Circuit {
|
||||||
|
pub wires: Vec<Wire>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum OutputFormula {
|
||||||
|
Constant { value: f32 },
|
||||||
|
Input { name: String },
|
||||||
|
Logic(Box<Logic>),
|
||||||
|
|
||||||
|
SineWave { amplitude: f32, frequency: f32 },
|
||||||
|
OnCollide { value: f32 },
|
||||||
|
OnInteract { value: f32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum LogicKind {
|
||||||
|
Min, // acts like And
|
||||||
|
Max, // acts like Or
|
||||||
|
Sub, // `|x| { 5.0 - x }` acts like Not, depending on reference voltages
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WiringAction {
|
||||||
|
pub formula: OutputFormula,
|
||||||
|
pub threshold: f32,
|
||||||
|
pub effects: Vec<WiringActionEffect>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum WiringActionEffect {
|
||||||
|
SpawnProjectile {
|
||||||
|
constr: ProjectileConstructor,
|
||||||
|
},
|
||||||
|
SetBlockCollidability {
|
||||||
|
coords: Vec3<i32>,
|
||||||
|
collidable: bool,
|
||||||
|
},
|
||||||
|
SetLight {
|
||||||
|
r: OutputFormula,
|
||||||
|
g: OutputFormula,
|
||||||
|
b: OutputFormula,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Wire {
|
||||||
|
pub input_field: String,
|
||||||
|
pub output_field: String,
|
||||||
|
pub input_entity: Entity,
|
||||||
|
pub output_entity: Entity,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for WiringElement {
|
||||||
|
type Storage = IdvStorage<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Circuit {
|
||||||
|
type Storage = IdvStorage<Self>;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user