1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
use crate::wiring::{Circuit, WiringElement};
use common::{
comp::{LightEmitter, PhysicsState, Pos},
event, event_emitters,
resources::EntitiesDiedLastTick,
};
use common_ecs::{Job, Origin, Phase, System};
use common_state::BlockChange;
use hashbrown::HashMap;
use specs::{
shred, Entities, Entity, Join, LendJoin, Read, ReadStorage, SystemData, 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>,
}
event_emitters! {
struct Events[Emitters] {
shoot: event::ShootEvent,
}
}
/// 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>,
Events<'a>,
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<Self>,
(read_data, events, mut wiring_elements, mut light_emitters, mut block_change): Self::SystemData,
) {
let mut emitters = events.get_emitters();
// 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<Entity, HashMap<String, f32>> = (
&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::<HashMap<_, _>>(),
)
})
.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(),
)
.lend_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 emitters,
pos,
&mut block_change,
light_emitter.as_deref_mut(),
);
})
},
)
}
}