use std::collections::HashMap; use shrev::ReaderId; use space_crush_common::{ components::{FleetOwned, PlayerOwned, Ship}, misc::ComponentEvent, }; use specs::{ hibitset::BitSetLike, prelude::*, world::Index, Entities, ReadExpect, ReadStorage, System, World, WriteStorage, }; use crate::{components::FleetInfo, resources::PlayerState}; pub struct FleetInfoUpdate { modified: HashMap, need_update: BitSet, fleet_owned_id: ReaderId>, player_owned_id: ReaderId>, } #[derive(Default)] struct Modified { old_fleet_id: Option, old_player_id: Option, } impl FleetInfoUpdate { pub fn new(world: &mut World) -> Self { let modified = HashMap::new(); let need_update = BitSet::new(); let fleet_owned_id = unsafe { WriteStorage::::setup(world); let mut fleet_owned = world.system_data::>(); fleet_owned .unprotected_storage_mut() .channel_mut() .register_reader() }; let player_owned_id = unsafe { WriteStorage::::setup(world); let mut player_owned = world.system_data::>(); player_owned .unprotected_storage_mut() .channel_mut() .register_reader() }; Self { modified, need_update, fleet_owned_id, player_owned_id, } } } #[derive(SystemData)] pub struct FleetInfoUpdateData<'a> { entities: Entities<'a>, player_state: ReadExpect<'a, PlayerState>, ships: ReadStorage<'a, Ship>, fleet_infos: WriteStorage<'a, FleetInfo>, fleet_owned: ReadStorage<'a, FleetOwned>, player_owned: ReadStorage<'a, PlayerOwned>, } impl<'a> System<'a> for FleetInfoUpdate { type SystemData = FleetInfoUpdateData<'a>; fn run(&mut self, data: Self::SystemData) { let FleetInfoUpdateData { entities, player_state, ships, mut fleet_infos, fleet_owned, player_owned, } = data; self.modified.clear(); self.need_update.clear(); /* player owned events */ let events = player_owned .unprotected_storage() .channel() .read(&mut self.player_owned_id); for event in events { match event { ComponentEvent::Inserted(id) => { self.need_update.add(*id); } ComponentEvent::Modified(id, player_owned) | ComponentEvent::Removed(id, player_owned) => { self.need_update.add(*id); self.modified.entry(*id).or_default().old_player_id = Some(player_owned.owner); } } } /* fleet owned events */ let events = fleet_owned .unprotected_storage() .channel() .read(&mut self.fleet_owned_id); for event in events { match event { ComponentEvent::Inserted(id) => { self.need_update.add(*id); } ComponentEvent::Modified(id, fleet_owned) | ComponentEvent::Removed(id, fleet_owned) => { self.need_update.add(*id); self.modified.entry(*id).or_default().old_fleet_id = Some(fleet_owned.owner); } } } if self.need_update.is_empty() { return; } /* update fleets */ let player_id = player_state.player_id; for (fleet_id, fleet_info) in (&entities, &mut fleet_infos).join() { for (ship_id, ship, fleet_owned, player_owned, _) in ( &entities, &ships, &fleet_owned, &player_owned, &self.need_update, ) .join() { let new_match = fleet_owned.owner == fleet_id && player_owned.owner == player_id; let old_match = match self.modified.get(&ship_id.id()) { None => false, Some(modified) => match (modified.old_fleet_id, modified.old_player_id) { (Some(old_fleet_id), Some(old_player_id)) => { old_fleet_id != fleet_id && old_player_id == player_id } (Some(old_fleet_id), None) => { old_fleet_id != fleet_id && player_owned.owner == player_id } (None, Some(old_player_id)) => { fleet_owned.owner != fleet_id && old_player_id == player_id } (None, None) => false, }, }; if old_match && !new_match { fleet_info.count[ship.type_] = fleet_info.count[ship.type_].saturating_sub(1); } else if !old_match && new_match { fleet_info.count[ship.type_] += 1; } } } } }