X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=src%2Fbadguy%2Fzeekling.cpp;h=ffea907c013c67c9ec77d9c9cd8ad6d1bd5f6cc8;hb=013a5ca196545a094f27c1b708facd0084d58d55;hp=41c45903e315a2670a20b823b1fc737eb7ca1e3e;hpb=354da395bda1f813280b26cc8d0c0547bf187c7d;p=supertux.git diff --git a/src/badguy/zeekling.cpp b/src/badguy/zeekling.cpp index 41c45903e..ffea907c0 100644 --- a/src/badguy/zeekling.cpp +++ b/src/badguy/zeekling.cpp @@ -1,7 +1,8 @@ // $Id$ -// -// SuperTux +// +// Zeekling - flyer that swoops down when she spots the player // Copyright (C) 2005 Matthias Braun +// Copyright (C) 2006 Christoph Sommer // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -12,36 +13,32 @@ // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // 02111-1307, USA. #include +#include -#include "zeekling.h" - -static const float SPEED = 150; +#include "zeekling.hpp" +#include "random_generator.hpp" -//TODO: Make the Zeekling behave more interesting Zeekling::Zeekling(const lisp::Lisp& reader) + : BadGuy(reader, "images/creatures/zeekling/zeekling.sprite"), last_player(0) { - reader.get("x", start_position.x); - reader.get("y", start_position.y); - bbox.set_size(31.8, 31.8); - sprite = sprite_manager->create("zeekling"); - set_direction = false; + state = FLYING; + speed = systemRandom.rand(130, 171); + physic.enable_gravity(false); } -Zeekling::Zeekling(float pos_x, float pos_y, Direction d) +Zeekling::Zeekling(const Vector& pos, Direction d) + : BadGuy(pos, d, "images/creatures/zeekling/zeekling.sprite"), last_player(0) { - start_position.x = pos_x; - start_position.y = pos_y; - bbox.set_size(63.8, 50.8); - sprite = sprite_manager->create("zeekling"); - set_direction = true; - initial_direction = d; + state = FLYING; + speed = systemRandom.rand(130, 171); + physic.enable_gravity(false); } void @@ -56,35 +53,146 @@ Zeekling::write(lisp::Writer& writer) } void -Zeekling::activate() +Zeekling::initialize() { - if (set_direction) {dir = initial_direction;} - physic.set_velocity_x(dir == LEFT ? -SPEED : SPEED); - physic.enable_gravity(false); + physic.set_velocity_x(dir == LEFT ? -speed : speed); sprite->set_action(dir == LEFT ? "left" : "right"); } bool -Zeekling::collision_squished(Player& player) +Zeekling::collision_squished(GameObject& object) { sprite->set_action(dir == LEFT ? "squished-left" : "squished-right"); - kill_squished(player); + kill_squished(object); kill_fall(); return true; } -HitResponse -Zeekling::collision_solid(GameObject& , const CollisionHit& hit) -{ - if(fabsf(hit.normal.y) > .5) { // hit floor or roof? +void +Zeekling::onBumpHorizontal() { + if (state == FLYING) { + dir = (dir == LEFT ? RIGHT : LEFT); + sprite->set_action(dir == LEFT ? "left" : "right"); + physic.set_velocity_x(dir == LEFT ? -speed : speed); + } else + if (state == DIVING) { + dir = (dir == LEFT ? RIGHT : LEFT); + state = FLYING; + sprite->set_action(dir == LEFT ? "left" : "right"); + physic.set_velocity_x(dir == LEFT ? -speed : speed); physic.set_velocity_y(0); - } else { // hit right or left + } else + if (state == CLIMBING) { dir = (dir == LEFT ? RIGHT : LEFT); sprite->set_action(dir == LEFT ? "left" : "right"); - physic.set_velocity_x(dir == LEFT ? -SPEED : SPEED); + physic.set_velocity_x(dir == LEFT ? -speed : speed); + } else { + assert(false); + } +} + +void +Zeekling::onBumpVertical() { + if (state == FLYING) { + physic.set_velocity_y(0); + } else + if (state == DIVING) { + state = CLIMBING; + physic.set_velocity_y(-speed); + sprite->set_action(dir == LEFT ? "left" : "right"); + } else + if (state == CLIMBING) { + state = FLYING; + physic.set_velocity_y(0); + } +} + +void +Zeekling::collision_solid(const CollisionHit& hit) +{ + if(hit.top || hit.bottom) { + onBumpVertical(); + } else if(hit.left || hit.right) { + onBumpHorizontal(); + } +} + +/** + * linear prediction of player and badguy positions to decide if we should enter the DIVING state + */ +bool +Zeekling::should_we_dive() { + + const MovingObject* player = this->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 = this->get_pos(); + 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; } - return CONTINUE; + // update last player tracked, as well as our positions + last_player = player; + if (player) { + last_player_pos = player->get_pos(); + last_self_pos = this->get_pos(); + } + + return false; +} + +void +Zeekling::active_update(float elapsed_time) { + if (state == FLYING) { + if (should_we_dive()) { + state = DIVING; + physic.set_velocity_y(2*fabsf(physic.get_velocity_x())); + sprite->set_action(dir == LEFT ? "diving-left" : "diving-right"); + } + BadGuy::active_update(elapsed_time); + return; + } else if (state == DIVING) { + BadGuy::active_update(elapsed_time); + return; + } else if (state == CLIMBING) { + // stop climbing when we're back at initial height + if (get_pos().y <= start_position.y) { + state = FLYING; + physic.set_velocity_y(0); + } + BadGuy::active_update(elapsed_time); + return; + } else { + assert(false); + } } IMPLEMENT_FACTORY(Zeekling, "zeekling")