/// Creates the correct actions for enemies after the player turn has ended
Creates the correct actions for enemies after the player turn has ended
Creates the correct actions for enemies after the player turn has ended
pub fn enemy_ai(
  run_ai: Query<&TurnDisplayer, Added<EnemyTurnAnimating>>,
  mut enemies: Query<(
    &mut EntityPendingAction,
    &Enemy,
    &EntityHealth,
    &TilePos,
    &TileParent,
  )>,
  players: Query<(Entity, &TilePos, &PlayerStatus)>,
  tiles: Query<(&TilePos, &DataLayer)>,
  mut map_q: MapQuery,
) {
  // An enemy turn has started when there is both a TurnDisplayer and a EnemyTurnAnimating component added
  if let Err(_) = run_ai.get_single() {
    return;
  }
  // ok, the enemy turn has started
  // make a list of all the blocked points to use in pathfinding. This seems inefficient,
  // but the borrows on MapQuery and Query<&TilePos> are really hard to figure out.
  let mut blocked_points: Vec<TilePos> = tiles
    .iter()
    .filter_map(|(tp, dl)| match dl.kind {
      TileKind::Floor => None,
      _ => Some(tp.to_owned()),
    })
    .collect();
  let mut warrior_pos: (Entity, TilePos) = (Entity::from_raw(0), Default::default());
  let mut wizard_pos: (Entity, TilePos) = (Entity::from_raw(0), Default::default());
  for p in players.iter() {
    blocked_points.push(p.1.to_owned());
    if p.2.magika.is_some() {
      wizard_pos = (p.0, p.1.to_owned());
    } else {
      warrior_pos = (p.0, p.1.to_owned());
    }
  }
  for (mut queued_action, enemy_data, enemy_health, enemy_pos, parent) in enemies.iter_mut() {
    let layer = map_q.get_layer(parent.map_id, parent.layer_id).unwrap().1;
    let ms = layer.get_layer_size_in_tiles();
    let map_dim = (ms.0, ms.1);
    let mut ai_type = enemy_data.ai_type;
    if ai_type == EnemyAIType::AttackUntilWeak {
      if enemy_health.health < 20. {
        ai_type = EnemyAIType::RunAway;
      } else {
        ai_type = EnemyAIType::AttackClosest;
      }
    }
    let closest_player = if utils::tile_distance(enemy_pos, &warrior_pos.1)
      > utils::tile_distance(enemy_pos, &wizard_pos.1)
    {
      wizard_pos
    } else {
      warrior_pos
    };
    queued_action.is_ready = true;
    // If an enemy is next to a player, the enemy will always attack
    if utils::tile_distance(&closest_player.1, enemy_pos) == 1 {
      queued_action.action = EntityAction::Attack(PendingAttack::new(
        closest_player.0,
        closest_player.1.to_owned(),
        enemy_pos.to_owned(),
        enemy_pos.to_owned(),
      ));
    } else {
      match ai_type {
        EnemyAIType::AttackClosest => {
          if let Some((new_pos, reached_target)) = get_move(
            enemy_pos,
            &closest_player.1,
            enemy_data.speed,
            &blocked_points,
            map_dim,
          ) {
            blocked_points.push(new_pos);
            if reached_target {
              queued_action.action = EntityAction::Attack(PendingAttack::new(
                closest_player.0,
                closest_player.1.clone(),
                new_pos,
                enemy_pos.to_owned(),
              ));
            } else {
              queued_action.action = EntityAction::Move((enemy_pos.to_owned(), new_pos));
            }
          } else {
            // a path was not found, so just wait
            queued_action.action = EntityAction::Wait;
          }
        }
        EnemyAIType::RunAway => {
          let warrior_pos = warrior_pos.1;
          // just run away from warrior for now
          if warrior_pos.1 > enemy_pos.1 {
            queued_action.action = run_in_direction(
              enemy_pos,
              enemy_data.speed,
              map_dim,
              (0, -1),
              &blocked_points,
            );
          } else if warrior_pos.1 < enemy_pos.1 {
            queued_action.action = run_in_direction(
              enemy_pos,
              enemy_data.speed,
              map_dim,
              (0, 1),
              &blocked_points,
            );
          } else if warrior_pos.0 > enemy_pos.0 {
            queued_action.action = run_in_direction(
              enemy_pos,
              enemy_data.speed,
              map_dim,
              (-1, 0),
              &blocked_points,
            );
          } else if warrior_pos.0 < enemy_pos.0 {
            queued_action.action = run_in_direction(
              enemy_pos,
              enemy_data.speed,
              map_dim,
              (1, 0),
              &blocked_points,
            );
          } else {
            queued_action.action = EntityAction::Wait;
          }
        }
        _ => {
          queued_action.action = EntityAction::Wait;
        }
      }
    }
    if !(queued_action.is_move() || queued_action.is_attack()) {
      blocked_points.push(enemy_pos.to_owned());
    }
  }
}
pub fn enemy_ai ( run_ai : Query < & TurnDisplayer , Added < EnemyTurnAnimating > > , mut enemies : Query < ( & mut EntityPendingAction , & Enemy , & EntityHealth , & TilePos , & TileParent , ) > , players : Query < ( Entity , & TilePos , & PlayerStatus ) > , tiles : Query < ( & TilePos , & DataLayer ) > , mut map_q : MapQuery , ) { // An enemy turn has started when there is both a TurnDisplayer and a EnemyTurnAnimating component added if let Err ( _ ) = run_ai . get_single ( ) { return ; } // ok, the enemy turn has started // make a list of all the blocked points to use in pathfinding. This seems inefficient, // but the borrows on MapQuery and Query<&TilePos> are really hard to figure out. let mut blocked_points : Vec < TilePos > = tiles . iter ( ) . filter_map ( | ( tp , dl ) | match dl . kind { TileKind :: Floor => None , _ => Some ( tp . to_owned ( ) ) , } ) . collect ( ) ; let mut warrior_pos : ( Entity , TilePos ) = ( Entity :: from_raw ( 0 ) , Default :: default ( ) ) ; let mut wizard_pos : ( Entity , TilePos ) = ( Entity :: from_raw ( 0 ) , Default :: default ( ) ) ; for p in players . iter ( ) { blocked_points . push ( p . 1 . to_owned ( ) ) ; if p . 2 . magika . is_some ( ) { wizard_pos = ( p . 0 , p . 1 . to_owned ( ) ) ; } else { warrior_pos = ( p . 0 , p . 1 . to_owned ( ) ) ; } } for ( mut queued_action , enemy_data , enemy_health , enemy_pos , parent ) in enemies . iter_mut ( ) { let layer = map_q . get_layer ( parent . map_id , parent . layer_id ) . unwrap ( ) . 1 ; let ms = layer . get_layer_size_in_tiles ( ) ; let map_dim = ( ms . 0 , ms . 1 ) ; let mut ai_type = enemy_data . ai_type ; if ai_type == EnemyAIType :: AttackUntilWeak { if enemy_health . health < 20. { ai_type = EnemyAIType :: RunAway ; } else { ai_type = EnemyAIType :: AttackClosest ; } } let closest_player = if utils :: tile_distance ( enemy_pos , & warrior_pos . 1 ) > utils :: tile_distance ( enemy_pos , & wizard_pos . 1 ) { wizard_pos } else { warrior_pos } ; queued_action . is_ready = true ; // If an enemy is next to a player, the enemy will always attack if utils :: tile_distance ( & closest_player . 1 , enemy_pos ) == 1 { queued_action . action = EntityAction :: Attack ( PendingAttack :: new ( closest_player . 0 , closest_player . 1 . to_owned ( ) , enemy_pos . to_owned ( ) , enemy_pos . to_owned ( ) , ) ) ; } else { match ai_type { EnemyAIType :: AttackClosest => { if let Some ( ( new_pos , reached_target ) ) = get_move ( enemy_pos , & closest_player . 1 , enemy_data . speed , & blocked_points , map_dim , ) { blocked_points . push ( new_pos ) ; if reached_target { queued_action . action = EntityAction :: Attack ( PendingAttack :: new ( closest_player . 0 , closest_player . 1 . clone ( ) , new_pos , enemy_pos . to_owned ( ) , ) ) ; } else { queued_action . action = EntityAction :: Move ( ( enemy_pos . to_owned ( ) , new_pos ) ) ; } } else { // a path was not found, so just wait queued_action . action = EntityAction :: Wait ; } } EnemyAIType :: RunAway => { let warrior_pos = warrior_pos . 1 ; // just run away from warrior for now if warrior_pos . 1 > enemy_pos . 1 { queued_action . action = run_in_direction ( enemy_pos , enemy_data . speed , map_dim , ( 0 , - 1 ) , & blocked_points , ) ; } else if warrior_pos . 1 < enemy_pos . 1 { queued_action . action = run_in_direction ( enemy_pos , enemy_data . speed , map_dim , ( 0 , 1 ) , & blocked_points , ) ; } else if warrior_pos . 0 > enemy_pos . 0 { queued_action . action = run_in_direction ( enemy_pos , enemy_data . speed , map_dim , ( - 1 , 0 ) , & blocked_points , ) ; } else if warrior_pos . 0 < enemy_pos . 0 { queued_action . action = run_in_direction ( enemy_pos , enemy_data . speed , map_dim , ( 1 , 0 ) , & blocked_points , ) ; } else { queued_action . action = EntityAction :: Wait ; } } _ => { queued_action . action = EntityAction :: Wait ; } } } if ! ( queued_action . is_move ( ) || queued_action . is_attack ( ) ) { blocked_points . push ( enemy_pos . to_owned ( ) ) ; } } }
Creates the correct actions for enemies after the player turn has ended
Creates the correct actions for enemies after the player turn has ended
run_ai
Query<&TurnDisplayer, Added<EnemyTurnAnimating>>
enemies
Query<(
    &mut EntityPendingAction,
    &Enemy,
    &EntityHealth,
    &TilePos,
    &TileParent,
  )>
players
Query<(Entity, &TilePos, &PlayerStatus)>
tiles
Query<(&TilePos, &DataLayer)>
map_q
MapQuery