+/**
+ * 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) return false;
+
+ const MovingObject* badguy = this;
+
+ const Vector playerPos = player->get_pos();
+ const Vector playerMov = player->get_movement();
+
+ const Vector badguyPos = badguy->get_pos();
+ const Vector badguyMov = badguy->get_movement();
+
+ // new vertical speed to test with
+ float vy = -2*fabsf(badguyMov.x);
+
+ // do not dive if we are not above the player
+ float height = playerPos.y - badguyPos.y;
+ if (height <= 0) return false;
+
+ // do not dive if we would not descend faster than the player
+ float relSpeed = -vy + playerMov.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 = (playerPos.x + (estFrames * playerMov.x));
+
+ // guess where we would be at this time
+ float estBx = (badguyPos.x + (estFrames * badguyMov.x));
+
+ // near misses are OK, too
+ if (fabsf(estPx - estBx) < 32) return true;
+
+ return false;
+}
+
+void
+Zeekling::active_update(float elapsed_time) {
+ BadGuy::active_update(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");
+ }
+ return;
+ }
+
+ if (state == DIVING) {
+ return;
+ }
+
+ 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);
+ }
+ return;
+ }
+
+}
+