| 1 | // Zeekling - flyer that swoops down when she spots the player |
| 2 | // Copyright (C) 2005 Matthias Braun <matze@braunis.de> |
| 3 | // Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de> |
| 4 | // |
| 5 | // This program is free software: you can redistribute it and/or modify |
| 6 | // it under the terms of the GNU General Public License as published by |
| 7 | // the Free Software Foundation, either version 3 of the License, or |
| 8 | // (at your option) any later version. |
| 9 | // |
| 10 | // This program is distributed in the hope that it will be useful, |
| 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | // GNU General Public License for more details. |
| 14 | // |
| 15 | // You should have received a copy of the GNU General Public License |
| 16 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 17 | |
| 18 | #include "badguy/zeekling.hpp" |
| 19 | |
| 20 | #include <math.h> |
| 21 | |
| 22 | #include "math/random.hpp" |
| 23 | #include "object/player.hpp" |
| 24 | #include "sprite/sprite.hpp" |
| 25 | |
| 26 | Zeekling::Zeekling(const ReaderMapping& reader) : |
| 27 | BadGuy(reader, "images/creatures/zeekling/zeekling.sprite" ), |
| 28 | speed(gameRandom.randf(130.0f, 171.0f)), |
| 29 | diveRecoverTimer(), |
| 30 | state(FLYING), |
| 31 | last_player(nullptr), |
| 32 | last_player_pos(), |
| 33 | last_self_pos() |
| 34 | { |
| 35 | m_physic.enable_gravity(false); |
| 36 | } |
| 37 | |
| 38 | void |
| 39 | Zeekling::initialize() |
| 40 | { |
| 41 | m_physic.set_velocity_x(m_dir == Direction::LEFT ? -speed : speed); |
| 42 | m_sprite->set_action(m_dir == Direction::LEFT ? "left" : "right" ); |
| 43 | } |
| 44 | |
| 45 | bool |
| 46 | Zeekling::collision_squished(GameObject& object) |
| 47 | { |
| 48 | m_sprite->set_action(m_dir == Direction::LEFT ? "squished-left" : "squished-right" ); |
| 49 | kill_squished(object); |
| 50 | return true; |
| 51 | } |
| 52 | |
| 53 | void |
| 54 | Zeekling::onBumpHorizontal() |
| 55 | { |
| 56 | if (m_frozen) |
| 57 | { |
| 58 | m_physic.set_velocity_x(0); |
| 59 | return; |
| 60 | } |
| 61 | if (state == FLYING) { |
| 62 | m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT); |
| 63 | m_sprite->set_action(m_dir == Direction::LEFT ? "left" : "right" ); |
| 64 | m_physic.set_velocity_x(m_dir == Direction::LEFT ? -speed : speed); |
| 65 | } else |
| 66 | if (state == DIVING) { |
| 67 | m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT); |
| 68 | state = FLYING; |
| 69 | m_sprite->set_action(m_dir == Direction::LEFT ? "left" : "right" ); |
| 70 | m_physic.set_velocity_x(m_dir == Direction::LEFT ? -speed : speed); |
| 71 | m_physic.set_velocity_y(0); |
| 72 | } else |
| 73 | if (state == CLIMBING) { |
| 74 | m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT); |
| 75 | m_sprite->set_action(m_dir == Direction::LEFT ? "left" : "right" ); |
| 76 | m_physic.set_velocity_x(m_dir == Direction::LEFT ? -speed : speed); |
| 77 | } else { |
| 78 | assert(false); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | void |
| 83 | Zeekling::onBumpVertical() |
| 84 | { |
| 85 | if (m_frozen || BadGuy::get_state() == STATE_BURNING) |
| 86 | { |
| 87 | m_physic.set_velocity_y(0); |
| 88 | m_physic.set_velocity_x(0); |
| 89 | return; |
| 90 | } |
| 91 | if (state == FLYING) { |
| 92 | m_physic.set_velocity_y(0); |
| 93 | } else |
| 94 | if (state == DIVING) { |
| 95 | state = CLIMBING; |
| 96 | m_physic.set_velocity_y(-speed); |
| 97 | m_sprite->set_action(m_dir == Direction::LEFT ? "left" : "right" ); |
| 98 | } else |
| 99 | if (state == CLIMBING) { |
| 100 | state = FLYING; |
| 101 | m_physic.set_velocity_y(0); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | void |
| 106 | Zeekling::collision_solid(const CollisionHit& hit) |
| 107 | { |
| 108 | if (m_sprite->get_action() == "squished-left" || |
| 109 | m_sprite->get_action() == "squished-right" ) |
| 110 | { |
| 111 | return; |
| 112 | } |
| 113 | |
| 114 | if (hit.top || hit.bottom) { |
| 115 | onBumpVertical(); |
| 116 | } else if (hit.left || hit.right) { |
| 117 | onBumpHorizontal(); |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | /** linear prediction of player and badguy positions to decide if we should enter the DIVING state */ |
| 122 | bool |
| 123 | Zeekling::should_we_dive() |
| 124 | { |
| 125 | if (m_frozen) |
| 126 | return false; |
| 127 | |
| 128 | const auto player = get_nearest_player(); |
| 129 | if (player && last_player && (player == last_player)) { |
| 130 | |
| 131 | // get positions, calculate movement |
| 132 | const Vector& player_pos = player->get_pos(); |
| 133 | const Vector player_mov = (player_pos - last_player_pos); |
| 134 | const Vector self_pos = m_col.m_bbox.p1(); |
| 135 | const Vector self_mov = (self_pos - last_self_pos); |
| 136 | |
| 137 | // new vertical speed to test with |
| 138 | float vy = 2*fabsf(self_mov.x); |
| 139 | |
| 140 | // do not dive if we are not above the player |
| 141 | float height = player_pos.y - self_pos.y; |
| 142 | if (height <= 0) return false; |
| 143 | |
| 144 | // do not dive if we are too far above the player |
| 145 | if (height > 512) return false; |
| 146 | |
| 147 | // do not dive if we would not descend faster than the player |
| 148 | float relSpeed = vy - player_mov.y; |
| 149 | if (relSpeed <= 0) return false; |
| 150 | |
| 151 | // guess number of frames to descend to same height as player |
| 152 | float estFrames = height / relSpeed; |
| 153 | |
| 154 | // guess where the player would be at this time |
| 155 | float estPx = (player_pos.x + (estFrames * player_mov.x)); |
| 156 | |
| 157 | // guess where we would be at this time |
| 158 | float estBx = (self_pos.x + (estFrames * self_mov.x)); |
| 159 | |
| 160 | // near misses are OK, too |
| 161 | if (fabsf(estPx - estBx) < 8) return true; |
| 162 | } |
| 163 | |
| 164 | // update last player tracked, as well as our positions |
| 165 | last_player = player; |
| 166 | if (player) { |
| 167 | last_player_pos = player->get_pos(); |
| 168 | last_self_pos = m_col.m_bbox.p1(); |
| 169 | } |
| 170 | |
| 171 | return false; |
| 172 | } |
| 173 | |
| 174 | void |
| 175 | Zeekling::active_update(float dt_sec) { |
| 176 | if (state == FLYING) { |
| 177 | if (should_we_dive()) { |
| 178 | state = DIVING; |
| 179 | m_physic.set_velocity_y(2*fabsf(m_physic.get_velocity_x())); |
| 180 | m_sprite->set_action(m_dir == Direction::LEFT ? "diving-left" : "diving-right" ); |
| 181 | } |
| 182 | BadGuy::active_update(dt_sec); |
| 183 | return; |
| 184 | } else if (state == DIVING) { |
| 185 | BadGuy::active_update(dt_sec); |
| 186 | return; |
| 187 | } else if (state == CLIMBING) { |
| 188 | // stop climbing when we're back at initial height |
| 189 | if (get_pos().y <= m_start_position.y) { |
| 190 | state = FLYING; |
| 191 | m_physic.set_velocity_y(0); |
| 192 | } |
| 193 | BadGuy::active_update(dt_sec); |
| 194 | return; |
| 195 | } else { |
| 196 | assert(false); |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | void |
| 201 | Zeekling::freeze() |
| 202 | { |
| 203 | BadGuy::freeze(); |
| 204 | m_physic.enable_gravity(true); |
| 205 | } |
| 206 | |
| 207 | void |
| 208 | Zeekling::unfreeze() |
| 209 | { |
| 210 | BadGuy::unfreeze(); |
| 211 | m_physic.enable_gravity(false); |
| 212 | state = FLYING; |
| 213 | initialize(); |
| 214 | } |
| 215 | |
| 216 | bool |
| 217 | Zeekling::is_freezable() const |
| 218 | { |
| 219 | return true; |
| 220 | } |
| 221 | |
| 222 | /* EOF */ |
| 223 | |