use ::bracket_lib::prelude::*; use ::specs::prelude::*; use crate::components::{ Attributes, DamageOverTime, Duration, EquipmentChanged, Initiative, MyTurn, Pools, Position, StatusEffect, }; use crate::rng::roll_dice; use crate::RunState; pub struct InitiativeSystem {} impl<'a> System<'a> for InitiativeSystem { #[allow(clippy::type_complexity)] type SystemData = ( WriteStorage<'a, Initiative>, ReadStorage<'a, Position>, WriteStorage<'a, MyTurn>, Entities<'a>, ReadStorage<'a, Attributes>, WriteExpect<'a, RunState>, ReadExpect<'a, Entity>, ReadExpect<'a, Point>, ReadStorage<'a, Pools>, WriteStorage<'a, Duration>, WriteStorage<'a, EquipmentChanged>, ReadStorage<'a, StatusEffect>, ReadStorage<'a, DamageOverTime>, ); fn run(&mut self, data: Self::SystemData) { let ( mut initiatives, positions, mut turns, entities, attributes, mut runstate, player, player_pos, pools, mut durations, mut dirty, statuses, dots, ) = data; if *runstate != RunState::Ticking { return; } // Clear any remaining MyTurn we left by mistake turns.clear(); // Roll initiative for (entity, initiative, pos) in (&entities, &mut initiatives, &positions).join() { initiative.current -= 1; if initiative.current < 1 { let mut myturn = true; // Re-roll initiative.current = 6 + roll_dice(1, 6); // Give a bonus for quickness if let Some(attr) = attributes.get(entity) { initiative.current -= attr.quickness.bonus; } // Apply pool penalty if let Some(pools) = pools.get(entity) { initiative.current += f32::floor(pools.total_initiative_penalty) as i32; } // TODO: More initiative granting boosts/penalties will go here later // if its the player, we want to go to an AwaitingInput state if entity == *player { *runstate = RunState::AwaitingInput; } else { let distance = DistanceAlg::Pythagoras.distance2d(*player_pos, Point::from(*pos)); if distance > 20.0 { myturn = false; } } // It's my turn if myturn { turns .insert(entity, MyTurn {}) .expect("Unable to insert turn"); } } } // Handle durations if *runstate == RunState::AwaitingInput { use crate::effects::*; for (effect_entity, duration, status) in (&entities, &mut durations, &statuses).join() { if entities.is_alive(status.target) { duration.turns -= 1; if let Some(dot) = dots.get(effect_entity) { add_effect( None, EffectType::Damage { amount: dot.damage }, Targets::Single { target: status.target, }, ); } if duration.turns < 1 { dirty .insert(status.target, EquipmentChanged {}) .expect("Unable to insert EquipmentChanged tag"); entities .delete(effect_entity) .expect("Unable to delete status effect tag entity"); } } } } } }