Beispiel #1
0
void
BadGuy::try_activate()
{
  // Don't activate if player is dying
  auto player = get_nearest_player();
  if (!player) return;

  if (!is_offscreen()) {
    set_state(STATE_ACTIVE);
    if (!m_is_initialized) {

      // if starting direction was set to AUTO, this is our chance to re-orient the badguy
      if (m_start_dir == Direction::AUTO) {
        auto player_ = get_nearest_player();
        if (player_ && (player_->get_bbox().get_left() > m_col.m_bbox.get_right())) {
          m_dir = Direction::RIGHT;
        } else {
          m_dir = Direction::LEFT;
        }
      }

      initialize();
      m_is_initialized = true;
    }
    activate();
  }
}
Beispiel #2
0
void
Kugelblitz::try_activate()
{
  // Much smaller offscreen distances to pop out of nowhere and surprise Tux
  float X_OFFSCREEN_DISTANCE = 400;
  float Y_OFFSCREEN_DISTANCE = 600;

  Player* player = get_nearest_player();
  if (!player) return;
  Vector dist = player->get_bbox().get_middle() - get_bbox().get_middle();
  if ((fabsf(dist.x) <= X_OFFSCREEN_DISTANCE) && (fabsf(dist.y) <= Y_OFFSCREEN_DISTANCE)) {
    set_state(STATE_ACTIVE);
    if (!is_initialized) {

      // if starting direction was set to AUTO, this is our chance to re-orient the badguy
      if (start_dir == AUTO) {
        Player* player = get_nearest_player();
        if (player && (player->get_bbox().p1.x > get_bbox().p2.x)) {
          dir = RIGHT;
        } else {
          dir = LEFT;
        }
      }

      initialize();
      is_initialized = true;
    }
    activate();
  }
}
Beispiel #3
0
void
BadGuy::try_activate()
{
  // Don't activate if player is dying
  Player* player = get_nearest_player();
  if (!player) return;

  if (!is_offscreen()) {
    set_state(STATE_ACTIVE);
    if (!is_initialized) {

      // if starting direction was set to AUTO, this is our chance to re-orient the badguy
      if (start_dir == AUTO) {
        Player* player_ = get_nearest_player();
        if (player_ && (player_->get_bbox().p1.x > get_bbox().p2.x)) {
          dir = RIGHT;
        } else {
          dir = LEFT;
        }
      }

      initialize();
      is_initialized = true;
    }
    activate();
  }
}
Beispiel #4
0
void
Plant::active_update(float elapsed_time) {
  BadGuy::active_update(elapsed_time);

  if(state == PLANT_SLEEPING) {

    auto player = get_nearest_player();
    if (player) {
      Rectf pb = player->get_bbox();

      bool inReach_left = (pb.p2.x >= bbox.p2.x-((dir == LEFT) ? 256 : 0));
      bool inReach_right = (pb.p1.x <= bbox.p1.x+((dir == RIGHT) ? 256 : 0));
      bool inReach_top = (pb.p2.y >= bbox.p2.y);
      bool inReach_bottom = (pb.p1.y <= bbox.p1.y);

      if (inReach_left && inReach_right && inReach_top && inReach_bottom) {
        // wake up
        sprite->set_action(dir == LEFT ? "waking-left" : "waking-right");
        if(!timer.started()) timer.start(WAKE_TIME);
        state = PLANT_WAKING;
      }
    }
  }

  if(state == PLANT_WAKING) {
    if(timer.check()) {
      // start walking
      sprite->set_action(dir == LEFT ? "left" : "right");
      physic.set_velocity_x(dir == LEFT ? -PLANT_SPEED : PLANT_SPEED);
      state = PLANT_WALKING;
    }
  }

}
Beispiel #5
0
void
Yeti::drop_stalactite()
{
  // make a stalactite falling down and shake camera a bit
  Sector::current()->camera->shake(.1f, 0, 10);

  auto player = get_nearest_player();
  if (!player) return;

  Sector* sector = Sector::current();
  for(const auto& obj : sector->gameobjects) {
    auto stalactite = dynamic_cast<YetiStalactite*>(obj.get());
    if(stalactite && stalactite->is_hanging()) {
      if (hit_points >= 3) {
        // drop stalactites within 3 of player, going out with each jump
        float distancex = fabsf(stalactite->get_bbox().get_middle().x - player->get_bbox().get_middle().x);
        if(distancex < stomp_count*32) {
          stalactite->start_shaking();
        }
      }
      else { /* if (hitpoints < 3) */
        // drop every 3rd pair of stalactites
        if(((((int)stalactite->get_pos().x + 16) / 64) % 3) == (stomp_count % 3)) {
          stalactite->start_shaking();
        }
      }
    } /* if(stalactite && stalactite->is_hanging()) */
  }
}
Beispiel #6
0
void
WillOWisp::active_update(float elapsed_time)
{
  Player* player = get_nearest_player();
  if (!player) return;
  Vector p1 = bbox.get_middle();
  Vector p2 = player->get_bbox().get_middle();
  Vector dist = (p2 - p1);

  switch(mystate) {
    case STATE_STOPPED:
      break;

    case STATE_IDLE:
      if (dist.norm() <= track_range) {
        mystate = STATE_TRACKING;
      }
      break;

    case STATE_TRACKING:
      if (dist.norm() > vanish_range) {
        vanish();
      } else if (dist.norm() >= 1) {
        Vector dir_ = dist.unit();
        movement = dir_ * elapsed_time * flyspeed;
      } else {
        /* We somehow landed right on top of the player without colliding.
         * Sit tight and avoid a division by zero. */
      }
      sound_source->set_position(get_pos());
      break;

    case STATE_WARPING:
      if(sprite->animation_done()) {
        remove_me();
      }

    case STATE_VANISHING: {
      Vector dir_ = dist.unit();
      movement = dir_ * elapsed_time * flyspeed;
      if(sprite->animation_done()) {
        remove_me();
      }
      break;
    }

    case STATE_PATHMOVING:
    case STATE_PATHMOVING_TRACK:
      if(walker.get() == NULL)
        return;
      movement = walker->advance(elapsed_time) - get_pos();
      if(mystate == STATE_PATHMOVING_TRACK && dist.norm() <= track_range) {
        mystate = STATE_TRACKING;
      }
      break;

    default:
      assert(false);
  }
}
Beispiel #7
0
void
Toad::set_state(ToadState newState)
{
  if (newState == IDLE) {
    m_physic.set_velocity_x(0);
    m_physic.set_velocity_y(0);
    if (!m_frozen)
      m_sprite->set_action(m_dir == Direction::LEFT ? "idle-left" : "idle-right");

    recover_timer.start(TOAD_RECOVER_TIME);
  } else
    if (newState == JUMPING) {
      m_sprite->set_action(m_dir == Direction::LEFT ? "jumping-left" : "jumping-right");
      m_physic.set_velocity_x(m_dir == Direction::LEFT ? -HORIZONTAL_SPEED : HORIZONTAL_SPEED);
      m_physic.set_velocity_y(VERTICAL_SPEED);
      SoundManager::current()->play( HOP_SOUND, get_pos());
    } else
      if (newState == FALLING) {
        Player* player = get_nearest_player();
        // face player
        if (player && (player->get_bbox().get_right() < m_col.m_bbox.get_left()) && (m_dir == Direction::RIGHT)) m_dir = Direction::LEFT;
        if (player && (player->get_bbox().get_left() > m_col.m_bbox.get_right()) && (m_dir == Direction::LEFT)) m_dir = Direction::RIGHT;
        m_sprite->set_action(m_dir == Direction::LEFT ? "idle-left" : "idle-right");
      }

  state = newState;
}
Beispiel #8
0
void
Jumpy::active_update(float dt_sec)
{
  BadGuy::active_update(dt_sec);

  if (m_frozen)
    return;

  auto player = get_nearest_player();
  if (player)
  {
    m_dir = (player->get_pos().x > get_pos().x) ? Direction::RIGHT : Direction::LEFT;
  }

  if (!groundhit_pos_set)
  {
    m_sprite->set_action(m_dir == Direction::LEFT ? "left-middle" : "right-middle");
    return;
  }

  if ( get_pos().y < (pos_groundhit.y - JUMPY_MID_TOLERANCE ) )
    m_sprite->set_action(m_dir == Direction::LEFT ? "left-up" : "right-up");
  else if ( get_pos().y >= (pos_groundhit.y - JUMPY_MID_TOLERANCE) &&
            get_pos().y < (pos_groundhit.y - JUMPY_LOW_TOLERANCE) )
    m_sprite->set_action(m_dir == Direction::LEFT ? "left-middle" : "right-middle");
  else
    m_sprite->set_action(m_dir == Direction::LEFT ? "left-down" : "right-down");
}
Beispiel #9
0
void
SpiderMite::active_update(float elapsed_time)
{
  if(frozen)
  {
    BadGuy::active_update(elapsed_time);
    return;
  }
  if(timer.check()) {
    if(mode == FLY_UP) {
      mode = FLY_DOWN;
      physic.set_velocity_y(-MOVE_SPEED);
    } else if(mode == FLY_DOWN) {
      mode = FLY_UP;
      physic.set_velocity_y(MOVE_SPEED);
    }
    timer.start(FLYTIME);
  }
  movement=physic.get_movement(elapsed_time);

  Player* player = get_nearest_player();
  if (player) {
    dir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
    sprite->set_action(dir == LEFT ? "left" : "right");
  }
}
Beispiel #10
0
void
InfoBlock::update(float delta)
{
  Block::update(delta);

  if (delta == 0) return;

  // hide message if player is too far away
  if (dest_pct > 0) {
    Player* player = get_nearest_player();
    if (player) {
      Vector p1 = bbox.get_middle();
      Vector p2 = player->get_bbox().get_middle();
      Vector dist = (p2 - p1);
      float d = dist.norm();
      if (d > 128) dest_pct = 0;
    }
  }

  // handle soft fade-in and fade-out
  if (shown_pct != dest_pct) {
    if (dest_pct > shown_pct) shown_pct = std::min(shown_pct + 2*delta, dest_pct);
    if (dest_pct < shown_pct) shown_pct = std::max(shown_pct - 2*delta, dest_pct);
  }
}
Beispiel #11
0
void
Toad::set_state(ToadState newState)
{

  if (newState == IDLE) {
    physic.set_velocity_x(0);
    physic.set_velocity_y(0);
    if (!frozen)
      sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");

    recover_timer.start(TOAD_RECOVER_TIME);
  } else
    if (newState == JUMPING) {
      sprite->set_action(dir == LEFT ? "jumping-left" : "jumping-right");
      physic.set_velocity_x(dir == LEFT ? -HORIZONTAL_SPEED : HORIZONTAL_SPEED);
      physic.set_velocity_y(VERTICAL_SPEED);
      SoundManager::current()->play( HOP_SOUND, get_pos());
    } else
      if (newState == FALLING) {
        Player* player = get_nearest_player();
        // face player
        if (player && (player->get_bbox().p2.x < get_bbox().p1.x) && (dir == RIGHT)) dir = LEFT;
        if (player && (player->get_bbox().p1.x > get_bbox().p2.x) && (dir == LEFT)) dir = RIGHT;
        sprite->set_action(dir == LEFT ? "idle-left" : "idle-right");
      }

  state = newState;
}
Beispiel #12
0
void
Dispenser::active_update(float )
{
  if (dispense_timer.check()) {
    // auto always shoots in Tux's direction
    if( autotarget ){
      if( sprite->animation_done()) {
        sprite->set_action(dir == LEFT ? "working-left" : "working-right");
        swivel = false;
      }

      Player* player = get_nearest_player();
      if( player && !swivel ){
        Direction targetdir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
        if( dir != targetdir ){ // no target: swivel cannon
          swivel = true;
          dir = targetdir;
          sprite->set_action(dir == LEFT ? "swivel-left" : "swivel-right", 1);
        } else { // tux in sight: shoot
          launch_badguy();
        }
      }
    } else {
      launch_badguy();
    }
  }
}
Beispiel #13
0
void
Stalactite::active_update(float elapsed_time)
{
  if(state == STALACTITE_HANGING) {
    auto player = get_nearest_player();
    if (player && !player->get_ghost_mode()) {
      if(player->get_bbox().p2.x > bbox.p1.x - SHAKE_RANGE_X
         && player->get_bbox().p1.x < bbox.p2.x + SHAKE_RANGE_X
         && player->get_bbox().p2.y > bbox.p1.y
         && player->get_bbox().p1.y < bbox.p2.y + SHAKE_RANGE_Y
         && Sector::current()->can_see_player(bbox.get_middle())) {
        timer.start(SHAKE_TIME);
        state = STALACTITE_SHAKING;
        SoundManager::current()->play("sounds/cracking.wav", get_pos());
      }
    }
  } else if(state == STALACTITE_SHAKING) {
    shake_delta = Vector(static_cast<float>(graphicsRandom.rand(-3, 3)), 0.0f);
    if(timer.check()) {
      state = STALACTITE_FALLING;
      physic.enable_gravity(true);
      set_colgroup_active(COLGROUP_MOVING);
    }
  } else if(state == STALACTITE_FALLING) {
    movement = physic.get_movement(elapsed_time);
  }
}
bool
BadGuy::is_offscreen()
{
  Player* player = get_nearest_player();
  if (!player) return false;
  Vector dist = player->get_bbox().get_middle() - get_bbox().get_middle();
  // In SuperTux 0.1.x, Badguys were activated when Tux<->Badguy center distance was approx. <= ~668px
  // This doesn't work for wide-screen monitors which give us a virt. res. of approx. 1066px x 600px
  if ((fabsf(dist.x) <= X_OFFSCREEN_DISTANCE) && (fabsf(dist.y) <= Y_OFFSCREEN_DISTANCE)) {
    return false;
  }
  return true;
}
Beispiel #15
0
/** linear prediction of player and badguy positions to decide if we should enter the DIVING state */
bool
Zeekling::should_we_dive()
{
  if (m_frozen)
    return false;

  const auto player = get_nearest_player();
  if (player && last_player && (player == last_player)) {

    // get positions, calculate movement
    const Vector& player_pos = player->get_pos();
    const Vector player_mov = (player_pos - last_player_pos);
    const Vector self_pos = m_col.m_bbox.p1();
    const Vector self_mov = (self_pos - last_self_pos);

    // new vertical speed to test with
    float vy = 2*fabsf(self_mov.x);

    // do not dive if we are not above the player
    float height = player_pos.y - self_pos.y;
    if (height <= 0) return false;

    // do not dive if we are too far above the player
    if (height > 512) return false;

    // do not dive if we would not descend faster than the player
    float relSpeed = vy - player_mov.y;
    if (relSpeed <= 0) return false;

    // guess number of frames to descend to same height as player
    float estFrames = height / relSpeed;

    // guess where the player would be at this time
    float estPx = (player_pos.x + (estFrames * player_mov.x));

    // guess where we would be at this time
    float estBx = (self_pos.x + (estFrames * self_mov.x));

    // near misses are OK, too
    if (fabsf(estPx - estBx) < 8) return true;
  }

  // update last player tracked, as well as our positions
  last_player = player;
  if (player) {
    last_player_pos = player->get_pos();
    last_self_pos = m_col.m_bbox.p1();
  }

  return false;
}
Beispiel #16
0
void
Dispenser::activate()
{
  if( broken ){
    return;
  }
  if( autotarget && !swivel ){ // auto cannon sprite might be wrong
    Player* player = get_nearest_player();
    if( player ){
      dir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
      sprite->set_action(dir == LEFT ? "working-left" : "working-right");
    }
  }
  dispense_timer.start(cycle, true);
  launch_badguy();
}
Beispiel #17
0
void
Dispenser::activate()
{
  if (m_broken){
    return;
  }
  if (m_autotarget && !m_swivel){ // auto cannon sprite might be wrong
    auto* player = get_nearest_player();
    if (player) {
      m_dir = (player->get_pos().x > get_pos().x) ? Direction::RIGHT : Direction::LEFT;
      m_sprite->set_action(m_dir == Direction::LEFT ? "working-left" : "working-right");
    }
  }
  m_dispense_timer.start(m_cycle, true);
  launch_badguy();
}
Beispiel #18
0
void
Haywire::active_update(float elapsed_time)
{
  if (is_exploding) {
    ticking->set_position(get_pos());
    grunting->set_position(get_pos());
    if (elapsed_time >= time_until_explosion) {
      kill_fall ();
      return;
    }
    else
      time_until_explosion -= elapsed_time;
  }

  if (is_stunned) {
    if (time_stunned > elapsed_time) {
      time_stunned -= elapsed_time;
    }
    else { /* if (time_stunned <= elapsed_time) */
      time_stunned = 0.f;
      is_stunned = false;
    }
  }

  if (is_exploding) {
    auto p = get_nearest_player ();
    float target_velocity = 0.f;

    if (p && time_stunned == 0.f) {
      /* Player is on the right */
      if (p->get_pos ().x > get_pos ().x)
        target_velocity = walk_speed;
      else /* player in on the left */
        target_velocity = (-1.f) * walk_speed;
    } /* if (player) */

    WalkingBadguy::active_update(elapsed_time, target_velocity);
  }
  else {
    WalkingBadguy::active_update(elapsed_time);
  }
}
Beispiel #19
0
bool
BadGuy::is_offscreen() const
{
  Vector dist;
  if (Editor::is_active()) {
    Camera& cam = Sector::get().get_camera();
    dist = cam.get_center() - m_col.m_bbox.get_middle();
  }
  auto player = get_nearest_player();
  if (!player)
    return false;
  if (!Editor::is_active()) {
    dist = player->get_bbox().get_middle() - m_col.m_bbox.get_middle();
  }
  // In SuperTux 0.1.x, Badguys were activated when Tux<->Badguy center distance was approx. <= ~668px
  // This doesn't work for wide-screen monitors which give us a virt. res. of approx. 1066px x 600px
  if ((fabsf(dist.x) <= X_OFFSCREEN_DISTANCE) && (fabsf(dist.y) <= Y_OFFSCREEN_DISTANCE)) {
    return false;
  }
  return true;
}
void
WillOWisp::active_update(float elapsed_time)
{
  Player* player = get_nearest_player();
  if (!player) return;
  Vector p1 = this->get_pos() + (this->get_bbox().p2 - this->get_bbox().p1) / 2;
  Vector p2 = player->get_pos() + (player->get_bbox().p2 - player->get_bbox().p1) / 2;
  Vector dist = (p2 - p1);

  if (mystate == STATE_IDLE) {
    if (dist.norm() <= TRACK_RANGE) {
      mystate = STATE_TRACKING;
    }
  }
  
  if (mystate == STATE_TRACKING) {
    if (dist.norm() <= VANISH_RANGE) {
      Vector dir = dist.unit();
      movement = dir*elapsed_time*FLYSPEED;
    } else {
      mystate = STATE_VANISHING;
      sprite->set_action("vanishing", 1);
    }
    soundSource->set_position(get_pos());
  }

  if (mystate == STATE_WARPING) {
    if(sprite->animation_done()) {
      remove_me();
    }
  }

  if (mystate == STATE_VANISHING) {
    if(sprite->animation_done()) {
      remove_me();
    }
  }
  
}
Beispiel #21
0
void
AngryStone::active_update(float elapsed_time) {
  BadGuy::active_update(elapsed_time);

  if (frozen) {
    return;
  }

  switch (state) {


    case IDLE: {
      MovingObject* player = get_nearest_player();
      if(player) {
        MovingObject* badguy = this;
        const Vector playerPos = player->get_pos();
        const Vector badguyPos = badguy->get_pos();
        float dx = (playerPos.x - badguyPos.x);
        float dy = (playerPos.y - badguyPos.y);

        float playerHeight = player->get_bbox().get_height();
        float badguyHeight = badguy->get_bbox().get_height();

        float playerWidth = player->get_bbox().get_width();
        float badguyWidth = badguy->get_bbox().get_width();

        if ((dx > -playerWidth) && (dx < badguyWidth)) {
          if (dy > 0) {
            attackDirection.x = 0;
            attackDirection.y = 1;
          } else {
            attackDirection.x = 0;
            attackDirection.y = -1;
          }
          if ((attackDirection.x != oldWallDirection.x) || (attackDirection.y != oldWallDirection.y)) {
            sprite->set_action("charging");
            timer.start(CHARGE_TIME);
            state = CHARGING;
          }
        } else if ((dy > -playerHeight) && (dy < badguyHeight)) {
          if (dx > 0) {
            attackDirection.x = 1;
            attackDirection.y = 0;
          } else {
            attackDirection.x = -1;
            attackDirection.y = 0;
          }
          if ((attackDirection.x != oldWallDirection.x) || (attackDirection.y != oldWallDirection.y)) {
            sprite->set_action("charging");
            timer.start(CHARGE_TIME);
            state = CHARGING;
          }
        }
      }
    } break;

    case CHARGING: {
      if (timer.check()) {
        sprite->set_action("attacking");
        timer.start(ATTACK_TIME);
        state = ATTACKING;
        physic.enable_gravity(false);
        physic.set_velocity_x(CHARGE_SPEED * attackDirection.x);
        physic.set_velocity_y(CHARGE_SPEED * attackDirection.y);
        oldWallDirection.x = 0;
        oldWallDirection.y = 0;
      }
    } break;

    case ATTACKING: {
      if (timer.check()) {
        timer.start(RECOVER_TIME);
        state = RECOVERING;
        sprite->set_action("idle");
        physic.enable_gravity(true);
        physic.set_velocity_x(0);
        physic.set_velocity_y(0);
      }
    } break;

    case RECOVERING: {
      if (timer.check()) {
        state = IDLE;
        sprite->set_action("idle");
        physic.enable_gravity(true);
        physic.set_velocity_x(0);
        physic.set_velocity_y(0);
      }
    } break;
  }

}
Beispiel #22
0
void
Dispenser::launch_badguy()
{
  if (badguys.empty()) return;
  if (frozen) return;

  //FIXME: Does is_offscreen() work right here?
  if (!is_offscreen() && !Editor::is_active()) {
    Direction launchdir = dir;
    if( !autotarget && start_dir == AUTO ){
      Player* player = get_nearest_player();
      if( player ){
        launchdir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
      }
    }

    if (badguys.size() > 1) {
      if (random) {
        next_badguy = gameRandom.rand(badguys.size());
      }
      else {
        next_badguy++;

        if (next_badguy >= badguys.size())
          next_badguy = 0;
      }
    }

    std::string badguy = badguys[next_badguy];

    if(badguy == "random") {
      log_warning << "random is outdated; use a list of badguys to select from." << std::endl;
      return;
    }
    if(badguy == "goldbomb") {
      log_warning << "goldbomb is not allowed to be dispensed" << std::endl;
      return;
    }

    try {
      GameObjectPtr game_object;
      Vector spawnpoint;
      Rectf object_bbox;

      /* Need to allocate the badguy first to figure out its bounding box. */
      game_object = ObjectFactory::instance().create(badguy, get_pos(), launchdir);
      if (game_object == NULL)
        throw std::runtime_error("Creating " + badguy + " object failed.");

      BadGuy& bad_guy = dynamic_cast<BadGuy&>(*game_object);

      object_bbox = bad_guy.get_bbox();

      switch (type) {
        case DT_DROPPER:
          spawnpoint = get_anchor_pos (bbox, ANCHOR_BOTTOM);
          spawnpoint.x -= 0.5 * object_bbox.get_width();
          break;
        case DT_ROCKETLAUNCHER:
        case DT_CANNON:
          spawnpoint = get_pos(); /* top-left corner of the cannon */
          if (launchdir == LEFT)
            spawnpoint.x -= object_bbox.get_width() + 1;
          else
            spawnpoint.x += bbox.get_width() + 1;
          break;
        case DT_POINT:
          spawnpoint = bbox.p1;
        default:
          break;
      }

      /* Now we set the real spawn position */
      bad_guy.set_pos(spawnpoint);

      /* We don't want to count dispensed badguys in level stats */
      bad_guy.countMe = false;

      Sector::current()->add_object(game_object);
    } catch(const std::exception& e) {
      log_warning << "Error dispensing badguy: " << e.what() << std::endl;
      return;
    }
  }
}
Beispiel #23
0
void
Yeti::kill_fall()
{
  // shooting bullets or being invincible won't work :)
  take_hit(*get_nearest_player()); // FIXME: debug only(?)
}
Beispiel #24
0
 Player* get_nearest_player (const Rectf& pos) const {
   return (get_nearest_player (get_anchor_pos (pos, ANCHOR_MIDDLE)));
 }
Beispiel #25
0
void
Dispenser::launch_badguy()
{
  if (m_badguys.empty()) return;
  if (m_frozen) return;
  if (m_limit_dispensed_badguys &&
      m_current_badguys >= m_max_concurrent_badguys)
      return;

  //FIXME: Does is_offscreen() work right here?
  if (!is_offscreen() && !Editor::is_active()) {
    Direction launchdir = m_dir;
    if ( !m_autotarget && m_start_dir == Direction::AUTO ){
      Player* player = get_nearest_player();
      if ( player ){
        launchdir = (player->get_pos().x > get_pos().x) ? Direction::RIGHT : Direction::LEFT;
      }
    }

    if (m_badguys.size() > 1) {
      if (m_random) {
        m_next_badguy = static_cast<unsigned int>(gameRandom.rand(static_cast<int>(m_badguys.size())));
      }
      else {
        m_next_badguy++;

        if (m_next_badguy >= m_badguys.size())
          m_next_badguy = 0;
      }
    }

    std::string badguy = m_badguys[m_next_badguy];

    if (badguy == "random") {
      log_warning << "random is outdated; use a list of badguys to select from." << std::endl;
      return;
    }
    if (badguy == "goldbomb") {
      log_warning << "goldbomb is not allowed to be dispensed" << std::endl;
      return;
    }

    try {
      /* Need to allocate the badguy first to figure out its bounding box. */
      auto game_object = GameObjectFactory::instance().create(badguy, get_pos(), launchdir);
      if (game_object == nullptr)
        throw std::runtime_error("Creating " + badguy + " object failed.");

      auto& bad_guy = dynamic_cast<BadGuy&>(*game_object);

      Rectf object_bbox = bad_guy.get_bbox();

      Vector spawnpoint;
      switch (m_type)
      {
        case DispenserType::DROPPER:
          spawnpoint = get_anchor_pos (m_col.m_bbox, ANCHOR_BOTTOM);
          spawnpoint.x -= 0.5f * object_bbox.get_width();
          break;

        case DispenserType::ROCKETLAUNCHER:
        case DispenserType::CANNON:
          spawnpoint = get_pos(); /* top-left corner of the cannon */
          if (launchdir == Direction::LEFT)
            spawnpoint.x -= object_bbox.get_width() + 1;
          else
            spawnpoint.x += m_col.m_bbox.get_width() + 1;
          break;

        case DispenserType::POINT:
          spawnpoint = m_col.m_bbox.p1();
          break;

        default:
          break;
      }

      /* Now we set the real spawn position */
      bad_guy.set_pos(spawnpoint);

      /* We don't want to count dispensed badguys in level stats */
      bad_guy.m_countMe = false;

      /* Set reference to dispenser in badguy itself */
      if (m_limit_dispensed_badguys)
      {
        bad_guy.set_parent_dispenser(this);
        m_current_badguys++;
      }

      Sector::get().add_object(std::move(game_object));
    } catch(const std::exception& e) {
      log_warning << "Error dispensing badguy: " << e.what() << std::endl;
      return;
    }
  }
}
Beispiel #26
0
void
GhostTree::active_update(float elapsed_time)
{
  (void) elapsed_time;

  if (mystate == STATE_IDLE) {
    if(colorchange_timer.check()) {
      SoundManager::current()->play("sounds/tree_howling.ogg", get_pos());
      suck_timer.start(3);
      treecolor = (treecolor + 1) % 3;

      Color col;
      switch(treecolor) {
        case 0: col = Color(1, 0, 0); break;
        case 1: col = Color(0, 1, 0); break;
        case 2: col = Color(0, 0, 1); break;
        case 3: col = Color(1, 1, 0); break;
        case 4: col = Color(1, 0, 1); break;
        case 5: col = Color(0, 1, 1); break;
        default: assert(false);
      }
      glow_sprite->set_color(col);
    }

    if(suck_timer.check()) {
      Color col = glow_sprite->get_color();
      SoundManager::current()->play("sounds/tree_suck.ogg", get_pos());
      for(auto iter = willowisps.begin(); iter != willowisps.end(); ++iter) {
        TreeWillOWisp& willo = **iter;
        if(willo.get_color() == col) {
          willo.start_sucking(get_bbox().get_middle() + SUCK_TARGET_OFFSET + Vector(gameRandom.randf(-SUCK_TARGET_SPREAD, SUCK_TARGET_SPREAD), gameRandom.randf(-SUCK_TARGET_SPREAD, SUCK_TARGET_SPREAD)));
        }
      }
      mystate = STATE_SUCKING;
    }

    if(willowisp_timer.check()) {
      if(willowisps.size() < WILLOWISP_COUNT) {
        Vector pos = Vector(bbox.get_width() / 2, bbox.get_height() / 2 + willo_spawn_y + WILLOWISP_TOP_OFFSET);
        auto willowisp = std::make_shared<TreeWillOWisp>(this, pos, 200 + willo_radius, willo_speed);

        Sector::current()->add_object(willowisp);
        willowisps.push_back(willowisp);

        willo_spawn_y -= 40;
        if(willo_spawn_y < -160)
          willo_spawn_y = 0;

        willo_radius += 20;
        if(willo_radius > 120)
          willo_radius = 0;

        if(willo_speed == 1.8f) {
          willo_speed = 1.5f;
        } else {
          willo_speed = 1.8f;
        }

        do {
          willo_color = (willo_color + 1) % 3;
        } while(willo_color == treecolor);

        switch(willo_color) {
          case 0: willowisp->set_color(Color(1, 0, 0)); break;
          case 1: willowisp->set_color(Color(0, 1, 0)); break;
          case 2: willowisp->set_color(Color(0, 0, 1)); break;
          case 3: willowisp->set_color(Color(1, 1, 0)); break;
          case 4: willowisp->set_color(Color(1, 0, 1)); break;
          case 5: willowisp->set_color(Color(0, 1, 1)); break;
          default: assert(false);
        }
      }
    }

    if(root_timer.check()) {
      /* TODO indicate root with an animation */
      Player* player = get_nearest_player();
      if (player) {
        auto root = std::make_shared<Root>(Vector(player->get_bbox().get_left(), get_bbox().get_bottom()+ROOT_TOP_OFFSET));
        Sector::current()->add_object(root);
      }
    }
  } else if (mystate == STATE_SWALLOWING) {
    if (suck_lantern) {
      // suck in lantern
      assert (suck_lantern);
      Vector pos = suck_lantern->get_pos();
      Vector delta = get_bbox().get_middle() + SUCK_TARGET_OFFSET - pos;
      Vector dir_ = delta.unit();
      if (delta.norm() < 1) {
        dir_ = delta;
        suck_lantern->ungrab(*this, RIGHT);
        suck_lantern->remove_me();
        suck_lantern = 0;
        sprite->set_action("swallow", 1);
      } else {
        pos += dir_;
        suck_lantern->grab(*this, pos, RIGHT);
      }
    } else {
      // wait until lantern is swallowed
      if (sprite->animation_done()) {
        if (is_color_deadly(suck_lantern_color)) {
          die();
        } else {
          sprite->set_action("default");
          mystate = STATE_IDLE;
          spawn_lantern();
        }
      }
    }
  }
}