use crate::wiring::{Circuit, WiringElement}; use common::{ comp::{LightEmitter, PhysicsState, Pos}, event::{EventBus, ServerEvent}, resources::EntitiesDiedLastTick, }; use common_ecs::{Job, Origin, Phase, System}; use common_state::BlockChange; use hashbrown::HashMap; use specs::{ join::Join, shred::ResourceId, Entities, Entity, Read, ReadStorage, SystemData, World, Write, WriteStorage, }; #[derive(SystemData)] pub struct ReadData<'a> { entities: Entities<'a>, circuits: ReadStorage<'a, Circuit>, pos: ReadStorage<'a, Pos>, physics_states: ReadStorage<'a, PhysicsState>, entities_died_last_tick: Read<'a, EntitiesDiedLastTick>, } /// This system is responsible for handling wiring (signals and wiring systems) #[derive(Default)] pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( ReadData<'a>, Read<'a, EventBus>, WriteStorage<'a, WiringElement>, WriteStorage<'a, LightEmitter>, // maybe Write<'a, BlockChange>, ); const NAME: &'static str = "wiring"; const ORIGIN: Origin = Origin::Server; const PHASE: Phase = Phase::Create; fn run( _job: &mut Job, (read_data, event_bus, mut wiring_elements, mut light_emitters, mut block_change): Self::SystemData, ) { let mut server_emitter = event_bus.emitter(); // Compute the output for each wiring element by computing // the output for each `OutputFormula` and store each value per output per // entity. let computed_outputs: HashMap> = ( &read_data.entities, &wiring_elements, read_data.physics_states.maybe(), read_data.pos.maybe(), ) .join() .map(|(entity, wiring_element, physics_state, pos)| { ( entity, wiring_element .outputs .iter() .map(|(key, output_formula)| { ( // Output name/key key.to_string(), // Output value output_formula.compute_output( &wiring_element.inputs, physics_state, &read_data.entities_died_last_tick.0, pos, ), ) }) .collect::>(), ) }) .collect(); // Pass new outputs as inputs for the next tick to the proper elements in the // circuit. (read_data.circuits) .join() .flat_map(|circuit| circuit.wires.iter()) .for_each(|wire| { // The current output values becomes input values for the next tick let input_value = computed_outputs .get(&wire.input.entity) .and_then(|e| e.get(&wire.input.name)) .unwrap_or(&0.0); // Push the current output value into the inputs for the proper element to be // used next tick. if let Some(wiring_element) = wiring_elements.get_mut(wire.output.entity) { wiring_element .inputs .insert(wire.output.name.clone(), *input_value); } }); // Use inputs to dispatch actions and apply effects ( &read_data.entities, &mut wiring_elements, read_data.physics_states.maybe(), (&mut light_emitters).maybe(), read_data.pos.maybe(), ) .join() .for_each( |(entity, wiring_element, physics_state, mut light_emitter, pos)| { wiring_element .actions .iter() .filter(|wiring_action| { // Filter out any wiring actions with a total output less than the // threshold wiring_action.formula.compute_output( &wiring_element.inputs, physics_state, &read_data.entities_died_last_tick.0, pos, ) >= wiring_action.threshold }) .for_each(|wiring_action| { // Apply world effects of each wiring action wiring_action.apply_effects( entity, &wiring_element.inputs, physics_state, &read_data.entities_died_last_tick.0, &mut server_emitter, pos, &mut block_change, light_emitter.as_deref_mut(), ); }) }, ) } }