pub fn spawn_action_choosers(
  mut commands: Commands,
  mut select_events: EventReader<TileSelectedEvent>,
  tile_q: Query<(&TilePos, &TileParent, &DataLayer)>,
  mut player_q: Query<
    (
      &TilePos,
      &TileParent,
      &MapEntityType,
      &mut EntityPendingAction,
    ),
    With<PlayerStatus>,
  >,
  enemy_q: Query<(Entity, &TilePos, &TileParent), (With<Enemy>, Without<PlayerStatus>)>,
  map_marker_q: Query<(Entity, &MapActionChooser)>,
  mut map_query: MapQuery,
) {
  // there really shouldn't be more than one tile click event per frame
  let click_event = select_events.iter().next();

  let mut build_markers: Option<(TilePos, TileParent, MapEntityType)> = None;

  // A new click has happened. Click events override UI state
  if let Some(TileSelectedEvent(Some(click_pos))) = click_event {
    if let Some((player_pos, player_tile_parent, player_type, mut player_pending_action)) =
      player_q.iter_mut().find(|dp| dp.0 == click_pos)
    {
      // The pending action is not yet ready, because we don't know which square we want to move to
      player_pending_action.is_ready = false;
      player_pending_action.action = EntityAction::Move((TilePos::default(), TilePos::default()));
      build_markers = Some((
        player_pos.to_owned(),
        player_tile_parent.to_owned(),
        player_type.to_owned(),
      ));

      // Update the other player. If they were busy selecting a move, they can't anymore
      if let Some((_, _, _, mut other_player_action)) = player_q
        .iter_mut()
        .find(|(pos, _, _, status)| *pos != click_pos && !status.is_ready)
      {
        other_player_action.action = EntityAction::Wait;
        other_player_action.is_ready = true;
      }
    }
  }

  // Or, move has been selected in the UI
  for (player_pos, player_tile_parent, player_type, player_pending_action) in player_q.iter() {
    match (
      player_pending_action.is_ready,
      &player_pending_action.action,
    ) {
      (false, EntityAction::Move(_)) | (false, EntityAction::Attack(_)) => {
        if map_marker_q.is_empty() {
          build_markers = Some((
            player_pos.to_owned(),
            player_tile_parent.to_owned(),
            player_type.to_owned(),
          ));
        }
      }
      _ => {}
    };
  }

  // And finally spawn new markers
  if let Some((origin, parent, player_type)) = build_markers {
    let layer = parent.layer_id;
    let map = parent.map_id;

    let max_move_distance: i32 = constants::PLAYER_DEFAULT_MOVE_SPEED as i32;

    /*
    Finding possible move tiles:
    1. Make a list of rows that are `max_move_distance` or less from the player
    2. Make a list of columns on those rows that are less than `max_move_distance - $row_distance_from_player`
    3. Turn the row, column pairs into `TilePos`'s
    4. For each TilePos, check that the tile type is correct, and the there are no entities in the way
    5. For each TilePos left over, create a MapActionChooserBundle to put a chooser component on that square
    */

    ((max_move_distance * -1 + 1)..max_move_distance)
      .map(|i| {
        let moves_left = max_move_distance - i.abs();
        let row = origin.1 as i32 + i;
        ((moves_left * -1 + 1)..(moves_left)).map(move |j| {
          let column = origin.0 as i32 + j;
          (column, row)
        })
      })
      .flatten()
      .filter_map(|(x, y)| {
        if x < 0 || y < 0 {
          None
        } else {
          Some(TilePos(x as u32, y as u32))
        }
      })
      .filter_map(move |pos| {
        // Check if a player is blocking the move
        if player_q.iter().any(|pd| {
          match pd.3.action {
            EntityAction::Move((_, b)) => {
              if b == pos {
                return true;
              }
            }
            _ => {}
          };
          *pd.0 == pos
        }) {
          return None;
        }

        if let Ok(tile_entity) = map_query.get_tile_entity(pos, map, layer) {
          if let Ok((dp, parent, data)) = tile_q.get(tile_entity) {
            assert!(*dp == pos);
            if data.kind == TileKind::Floor {
              return Some((pos, parent.to_owned()));
            }
          }
        }
        None
      })
      .for_each(|(marker_pos, marker_parent)| {
        if let Some(enemy) = enemy_q.iter().find(|(_, pos, _)| **pos == marker_pos) {
          commands
            .spawn()
            .insert_bundle(PlayerAttackChooserBundle::new(
              &marker_pos,
              &marker_parent,
              &origin.clone(),
              &player_type,
              enemy.1,
              enemy.0,
            ));
        } else {
          commands.spawn().insert_bundle(PlayerMoveChooserBundle::new(
            &marker_pos,
            &marker_parent,
            &origin.clone(),
            &player_type,
          ));
        }
      });
  }
}
/// If a player has been clicked during the PlayerChoose phase of a turn,
/// or "Move" has been selected in the left UI,
/// this will spawn markers to show possible moves.
If a player has been clicked during the PlayerChoose phase of a turn,
or "Move" has been selected in the left UI,
this will spawn markers to show possible moves.
If a player has been clicked during the PlayerChoose phase of a turn or " Move " has been selected in the left UI this will spawn markers to show possible moves .
pub fn spawn_action_choosers(
  mut commands: Commands,
  mut select_events: EventReader<TileSelectedEvent>,
  tile_q: Query<(&TilePos, &TileParent, &DataLayer)>,
  mut player_q: Query<
    (
      &TilePos,
      &TileParent,
      &MapEntityType,
      &mut EntityPendingAction,
    ),
    With<PlayerStatus>,
  >,
  enemy_q: Query<(Entity, &TilePos, &TileParent), (With<Enemy>, Without<PlayerStatus>)>,
  map_marker_q: Query<(Entity, &MapActionChooser)>,
  mut map_query: MapQuery,
) {
  // there really shouldn't be more than one tile click event per frame
  let click_event = select_events.iter().next();
  let mut build_markers: Option<(TilePos, TileParent, MapEntityType)> = None;
  // A new click has happened. Click events override UI state
  if let Some(TileSelectedEvent(Some(click_pos))) = click_event {
    if let Some((player_pos, player_tile_parent, player_type, mut player_pending_action)) =
      player_q.iter_mut().find(|dp| dp.0 == click_pos)
    {
      // The pending action is not yet ready, because we don't know which square we want to move to
      player_pending_action.is_ready = false;
      player_pending_action.action = EntityAction::Move((TilePos::default(), TilePos::default()));
      build_markers = Some((
        player_pos.to_owned(),
        player_tile_parent.to_owned(),
        player_type.to_owned(),
      ));
      // Update the other player. If they were busy selecting a move, they can't anymore
      if let Some((_, _, _, mut other_player_action)) = player_q
        .iter_mut()
        .find(|(pos, _, _, status)| *pos != click_pos && !status.is_ready)
      {
        other_player_action.action = EntityAction::Wait;
        other_player_action.is_ready = true;
      }
    }
  }
  // Or, move has been selected in the UI
  for (player_pos, player_tile_parent, player_type, player_pending_action) in player_q.iter() {
    match (
      player_pending_action.is_ready,
      &player_pending_action.action,
    ) {
      (false, EntityAction::Move(_)) | (false, EntityAction::Attack(_)) => {
        if map_marker_q.is_empty() {
          build_markers = Some((
            player_pos.to_owned(),
            player_tile_parent.to_owned(),
            player_type.to_owned(),
          ));
        }
      }
      _ => {}
    };
  }
  // And finally spawn new markers
  if let Some((origin, parent, player_type)) = build_markers {
    let layer = parent.layer_id;
    let map = parent.map_id;
    let max_move_distance: i32 = constants::PLAYER_DEFAULT_MOVE_SPEED as i32;
    /*
    Finding possible move tiles:
    1. Make a list of rows that are `max_move_distance` or less from the player
    2. Make a list of columns on those rows that are less than `max_move_distance - $row_distance_from_player`
    3. Turn the row, column pairs into `TilePos`'s
    4. For each TilePos, check that the tile type is correct, and the there are no entities in the way
    5. For each TilePos left over, create a MapActionChooserBundle to put a chooser component on that square
    */
    ((max_move_distance * -1 + 1)..max_move_distance)
      .map(|i| {
        let moves_left = max_move_distance - i.abs();
        let row = origin.1 as i32 + i;
        ((moves_left * -1 + 1)..(moves_left)).map(move |j| {
          let column = origin.0 as i32 + j;
          (column, row)
        })
      })
      .flatten()
      .filter_map(|(x, y)| {
        if x < 0 || y < 0 {
          None
        } else {
          Some(TilePos(x as u32, y as u32))
        }
      })
      .filter_map(move |pos| {
        // Check if a player is blocking the move
        if player_q.iter().any(|pd| {
          match pd.3.action {
            EntityAction::Move((_, b)) => {
              if b == pos {
                return true;
              }
            }
            _ => {}
          };
          *pd.0 == pos
        }) {
          return None;
        }
        if let Ok(tile_entity) = map_query.get_tile_entity(pos, map, layer) {
          if let Ok((dp, parent, data)) = tile_q.get(tile_entity) {
            assert!(*dp == pos);
            if data.kind == TileKind::Floor {
              return Some((pos, parent.to_owned()));
            }
          }
        }
        None
      })
      .for_each(|(marker_pos, marker_parent)| {
        if let Some(enemy) = enemy_q.iter().find(|(_, pos, _)| **pos == marker_pos) {
          commands
            .spawn()
            .insert_bundle(PlayerAttackChooserBundle::new(
              &marker_pos,
              &marker_parent,
              &origin.clone(),
              &player_type,
              enemy.1,
              enemy.0,
            ));
        } else {
          commands.spawn().insert_bundle(PlayerMoveChooserBundle::new(
            &marker_pos,
            &marker_parent,
            &origin.clone(),
            &player_type,
          ));
        }
      });
  }
}
pub fn spawn_action_choosers ( mut commands : Commands , mut select_events : EventReader < TileSelectedEvent > , tile_q : Query < ( & TilePos , & TileParent , & DataLayer ) > , mut player_q : Query < ( & TilePos , & TileParent , & MapEntityType , & mut EntityPendingAction , ) , With < PlayerStatus > , > , enemy_q : Query < ( Entity , & TilePos , & TileParent ) , ( With < Enemy > , Without < PlayerStatus > ) > , map_marker_q : Query < ( Entity , & MapActionChooser ) > , mut map_query : MapQuery , ) { // there really shouldn't be more than one tile click event per frame let click_event = select_events . iter ( ) . next ( ) ; let mut build_markers : Option < ( TilePos , TileParent , MapEntityType ) > = None ; // A new click has happened. Click events override UI state if let Some ( TileSelectedEvent ( Some ( click_pos ) ) ) = click_event { if let Some ( ( player_pos , player_tile_parent , player_type , mut player_pending_action ) ) = player_q . iter_mut ( ) . find ( | dp | dp . 0 == click_pos ) { // The pending action is not yet ready, because we don't know which square we want to move to player_pending_action . is_ready = false ; player_pending_action . action = EntityAction :: Move ( ( TilePos :: default ( ) , TilePos :: default ( ) ) ) ; build_markers = Some ( ( player_pos . to_owned ( ) , player_tile_parent . to_owned ( ) , player_type . to_owned ( ) , ) ) ; // Update the other player. If they were busy selecting a move, they can't anymore if let Some ( ( _ , _ , _ , mut other_player_action ) ) = player_q . iter_mut ( ) . find ( | ( pos , _ , _ , status ) | * pos != click_pos && ! status . is_ready ) { other_player_action . action = EntityAction :: Wait ; other_player_action . is_ready = true ; } } } // Or, move has been selected in the UI for ( player_pos , player_tile_parent , player_type , player_pending_action ) in player_q . iter ( ) { match ( player_pending_action . is_ready , & player_pending_action . action , ) { ( false , EntityAction :: Move ( _ ) ) | ( false , EntityAction :: Attack ( _ ) ) => { if map_marker_q . is_empty ( ) { build_markers = Some ( ( player_pos . to_owned ( ) , player_tile_parent . to_owned ( ) , player_type . to_owned ( ) , ) ) ; } } _ => { } } ; } // And finally spawn new markers if let Some ( ( origin , parent , player_type ) ) = build_markers { let layer = parent . layer_id ; let map = parent . map_id ; let max_move_distance : i32 = constants :: PLAYER_DEFAULT_MOVE_SPEED as i32 ; /*
    Finding possible move tiles:
    1. Make a list of rows that are `max_move_distance` or less from the player
    2. Make a list of columns on those rows that are less than `max_move_distance - $row_distance_from_player`
    3. Turn the row, column pairs into `TilePos`'s
    4. For each TilePos, check that the tile type is correct, and the there are no entities in the way
    5. For each TilePos left over, create a MapActionChooserBundle to put a chooser component on that square
    */ ( ( max_move_distance * - 1 + 1 ) .. max_move_distance ) . map ( | i | { let moves_left = max_move_distance - i . abs ( ) ; let row = origin . 1 as i32 + i ; ( ( moves_left * - 1 + 1 ) .. ( moves_left ) ) . map ( move | j | { let column = origin . 0 as i32 + j ; ( column , row ) } ) } ) . flatten ( ) . filter_map ( | ( x , y ) | { if x < 0 || y < 0 { None } else { Some ( TilePos ( x as u32 , y as u32 ) ) } } ) . filter_map ( move | pos | { // Check if a player is blocking the move if player_q . iter ( ) . any ( | pd | { match pd . 3 . action { EntityAction :: Move ( ( _ , b ) ) => { if b == pos { return true ; } } _ => { } } ; * pd . 0 == pos } ) { return None ; } if let Ok ( tile_entity ) = map_query . get_tile_entity ( pos , map , layer ) { if let Ok ( ( dp , parent , data ) ) = tile_q . get ( tile_entity ) { assert ! ( dp pos ) ; if data . kind == TileKind :: Floor { return Some ( ( pos , parent . to_owned ( ) ) ) ; } } } None } ) . for_each ( | ( marker_pos , marker_parent ) | { if let Some ( enemy ) = enemy_q . iter ( ) . find ( | ( _ , pos , _ ) | * * pos == marker_pos ) { commands . spawn ( ) . insert_bundle ( PlayerAttackChooserBundle :: new ( & marker_pos , & marker_parent , & origin . clone ( ) , & player_type , enemy . 1 , enemy . 0 , ) ) ; } else { commands . spawn ( ) . insert_bundle ( PlayerMoveChooserBundle :: new ( & marker_pos , & marker_parent , & origin . clone ( ) , & player_type , ) ) ; } } ) ; } }
If a player has been clicked during the PlayerChoose phase of a turn,
or "Move" has been selected in the left UI,
this will spawn markers to show possible moves.
If a player has been clicked during the PlayerChoose phase of a turn or " Move " has been selected in the left UI this will spawn markers to show possible moves .
commands
Commands
select_events
EventReader<TileSelectedEvent>
tile_q
Query<(&TilePos, &TileParent, &DataLayer)>
player_q
Query<
    (
      &TilePos,
      &TileParent,
      &MapEntityType,
      &mut EntityPendingAction,
    ),
    With<PlayerStatus>,
  >
enemy_q
Query<(Entity, &TilePos, &TileParent), (With<Enemy>, Without<PlayerStatus>)>
map_marker_q
Query<(Entity, &MapActionChooser)>
map_query
MapQuery
commands
Commands
select_events
EventReader<TileSelectedEvent>
tile_q
Query<(&TilePos, &TileParent, &DataLayer)>
player_q
Query<
    (
      &TilePos,
      &TileParent,
      &MapEntityType,
      &mut EntityPendingAction,
    ),
    With<PlayerStatus>,
  >
enemy_q
Query<(Entity, &TilePos, &TileParent), (With<Enemy>, Without<PlayerStatus>)>
map_marker_q
Query<(Entity, &MapActionChooser)>
map_query
MapQuery