#include "enity.hpp"
bool Enity::update_physic(float dt, uint update_idx) {
  #ifdef DEBUG
  ++debuger::update_physics;
  #endif
  if (flags & EF_NO_OFFSET)
    return check_hit(update_idx);
  cnauto check_dist = (p_info ? p_info->check_dist : 4.0f);
  // глобальное смещение:
  phys.pos += -g_offset.dir * g_offset.spd * dt;
  // доп функции движения:
  if (pf0 and !(this->*pf0)(dt)) return false;
  if (pf1 and !(this->*pf1)(dt)) return false;
  if (flags & EF_NO_MOVE) // без самостоятельного движения
    return check_hit(update_idx);
  phys.update_speed(dt); // обновление скорости
  // движение:
  return checkmove(phys.dir, phys.spd, check_dist, dt, update_idx);
} // update_physic
bool Enity::checkmove(CN(Vec) dir, float spd, float check_dist, float dt,
uint update_idx) {
  #ifdef DEBUG
  ++debuger::check_moves;
  #endif
  /* растояние делится на длинну проверки хитбокса и определяется dt
   * для пошагового движения с проверками коллизий */
  // глобальное смещение:
  cnauto pos0 = phys.pos;
  cauto pos_end = phys.pos + dir * spd * dt;
  // применить алгоритм деления пути
  auto len = distance(phys.pos, pos_end);
  if (len > 0 and len > check_dist) {
    const int cnt = std::floor(len / check_dist);
    cauto dt2 = (spd * cnt) / len;
    FOR (i, cnt) {
      phys.set_pos(pos0 + dir * (spd * dt2 * i));
      if ( !check_hit(update_idx))
        return false;
    }
  } // if len >= check_dist
  if ( !check_hit(update_idx))
    return false;
  phys.set_pos(pos_end);
  return true;
} // checkmove
bool Enity::check_hit(uint update_idx) {
  #ifdef DEBUG
  ++debuger::check_hits;
  #endif
  if ( !is_collidable) // если сущность не способна сталкиваться, то скип
    return true;
  if ( !p_info) // нужны данные о столкновении
    return true;
  auto p_cld_info = rcast(const Cld_info*, p_info);
  if (flags & EF_NO_COLLIDE) // скип проверки столкновений
    return true;
  // проверка столкновений себя со всеми
  for (uint i = update_idx + 1; i < vp_enity.size(); ++i) {
    nauto other = vp_enity[i];
    #ifdef DEBUG
    ++debuger::collide_checks; // collide_circ_point считается
    #endif
    // исключения. для оптимизации самые редкие ставь сверху
    if ( ( ((flags & EF_IGNORE_MASTER) // игнор родителя
        or (other->flags & EF_IGNORE_MASTER))
      && (master == other or other->master == this) )
    || (other->flags & EF_NO_COLLIDE) // не хочет врезаться, и не надо
    || ((flags & EF_IGNORE_SELF_ID)
      && id == other->id) // себе подобных не трогать
    || !other->p_info // пустые пули не трогать
    || !collide_circ_point(phys.pos, p_cld_info->cld_dist,
      other->phys.pos) // если хитбоксы далеко друг от друга, то не проверять
    || other->is_dead()
    || !other->is_collidable) // если можно сталкиваться
      continue;
    // проверка столкновений
    bool collided = false; // 1, если один раз столкнулись
    cnauto p_other_cld_info = rcast(CP(Cld_info), other->p_info);
    for (cnauto self_hit: get_hits()) { // свои хитбоксы
      for (cnauto other_hit: other->get_hits()) { // чужие хитбоксы
        #ifdef DEBUG
        ++debuger::collide_checks;
        #endif
        if (self_hit->collide(phys.pos, *other_hit, other->phys.pos)) {
          // дамажимся
          damage(p_other_cld_info->dmg, *other);
          other->damage(p_cld_info->dmg, *this);
          other->flags |= EF_COLLIDED;
          flags |= EF_COLLIDED;
          if (is_dead())
            return false;
          collided = true;
          break;
        } // if collided
      } // for other_hits
      if (collided)
        break;
    } // for self_hits
  } // for vp_enity size
  return true;
} // check_hit