-
-
Notifications
You must be signed in to change notification settings - Fork 522
Zeekling behaviour improvements #2624
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
41eff8f
1037d4e
29fcf4e
8e0a31d
ae544c8
35e9477
95020a8
0b69aa7
37391e1
a25356e
4f0d527
1713e75
a2a1123
434998d
0304ee8
e59ec8b
5e23863
11312a6
97bb941
c6d7055
9c82746
62035d0
0fd1cee
56a3d9b
e88dd1b
b90c9df
ebf5feb
f54a040
1fafb26
a35d19b
94023dd
6b55ea5
b28e5ae
bd0c031
158d0ac
3826309
3f2d596
00ccad9
1b559ed
f31b140
aa12984
3aa39b9
98124ca
a388580
78c1588
5842192
ccef82e
c28cb48
f508da0
15f4294
94dd97d
caef364
024469d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,28 +17,43 @@ | |
|
||
#include "badguy/zeekling.hpp" | ||
|
||
#include <math.h> | ||
|
||
#include "math/easing.hpp" | ||
#include "math/random.hpp" | ||
#include "math/util.hpp" | ||
#include "math/vector.hpp" | ||
#include "object/player.hpp" | ||
#include "sprite/sprite.hpp" | ||
#include "supertux/sector.hpp" | ||
|
||
static const float FLYING_SPEED = 180.f; | ||
static const float CHARGING_SPEED = 150.f; | ||
static const float DIVING_SPEED = 280.f; | ||
|
||
static const float CHARGING_DURATION = 0.3f; | ||
static const float DIVING_DURATION = 1.5f; | ||
static const float RECOVER_DURATION = 2.8f; | ||
|
||
static const float MIN_DETECT_RANGE_Y = 32.f * 4.5f; | ||
static const float MAX_DETECT_RANGE_Y = 512.f; | ||
static const float MIN_DETECT_RANGE_X = 10.f; | ||
static const float MAX_DETECT_RANGE_X = 32.f * 15.f; | ||
|
||
static const float CATCH_OFFSET = -10.f; | ||
|
||
Zeekling::Zeekling(const ReaderMapping& reader) : | ||
BadGuy(reader, "images/creatures/zeekling/zeekling.sprite"), | ||
speed(gameRandom.randf(130.0f, 171.0f)), | ||
diveRecoverTimer(), | ||
state(FLYING), | ||
last_player(nullptr), | ||
last_player_pos(0.0f, 0.0f), | ||
last_self_pos(0.0f, 0.0f) | ||
m_catch_pos(0.f), | ||
m_timer(), | ||
m_state(FLYING) | ||
{ | ||
m_physic.enable_gravity(false); | ||
m_physic.set_velocity_x(FLYING_SPEED); | ||
} | ||
|
||
void | ||
Zeekling::initialize() | ||
{ | ||
m_physic.set_velocity_x(m_dir == Direction::LEFT ? -speed : speed); | ||
m_physic.set_velocity_x(m_physic.get_velocity_x() * (m_dir == Direction::LEFT ? -1 : 1)); | ||
set_action(m_dir); | ||
} | ||
|
||
|
@@ -54,149 +69,202 @@ Zeekling::collision_squished(GameObject& object) | |
} | ||
|
||
void | ||
Zeekling::onBumpHorizontal() | ||
{ | ||
if (state == FLYING) { | ||
m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT); | ||
set_action(m_dir); | ||
m_physic.set_velocity_x(m_dir == Direction::LEFT ? -speed : speed); | ||
} else | ||
if (state == DIVING) { | ||
m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT); | ||
state = FLYING; | ||
set_action(m_dir); | ||
m_physic.set_velocity(m_dir == Direction::LEFT ? -speed : speed, 0); | ||
} else | ||
if (state == CLIMBING) { | ||
m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT); | ||
set_action(m_dir); | ||
m_physic.set_velocity_x(m_dir == Direction::LEFT ? -speed : speed); | ||
} else { | ||
assert(false); | ||
} | ||
Zeekling::on_bump_horizontal() | ||
{ | ||
m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT); | ||
set_action(m_dir); | ||
m_physic.set_velocity_x(std::abs(m_physic.get_velocity_x()) * (m_dir == Direction::LEFT ? -1 : 1)); | ||
|
||
if (m_state == DIVING) | ||
{ | ||
MatusGuy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Diving attempt failed. So sad. Return to base. | ||
recover(); | ||
} | ||
} | ||
|
||
void | ||
Zeekling::onBumpVertical() | ||
Zeekling::on_bump_vertical() | ||
{ | ||
if (BadGuy::get_state() == STATE_BURNING) | ||
{ | ||
m_physic.set_velocity(0, 0); | ||
return; | ||
switch (m_state) { | ||
case DIVING: | ||
// Diving attempt failed. So sad. Return to base. | ||
recover(); | ||
break; | ||
|
||
case RECOVERING: | ||
// I guess this is my new home now. | ||
MatusGuy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Set start position to the current position. | ||
fly(); | ||
break; | ||
|
||
default: | ||
break; | ||
} | ||
if (state == FLYING) { | ||
m_physic.set_velocity_y(0); | ||
} else | ||
if (state == DIVING) { | ||
state = CLIMBING; | ||
m_physic.set_velocity_y(-speed); | ||
set_action(m_dir); | ||
} else | ||
if (state == CLIMBING) { | ||
state = FLYING; | ||
m_physic.set_velocity_y(0); | ||
} | ||
} | ||
|
||
void | ||
Zeekling::collision_solid(const CollisionHit& hit) | ||
{ | ||
if (m_frozen) | ||
BadGuy::collision_solid(hit); | ||
else | ||
{ | ||
if (m_sprite->get_action() == "squished-left" || | ||
m_sprite->get_action() == "squished-right") | ||
{ | ||
return; | ||
} | ||
BadGuy::collision_solid(hit); | ||
return; | ||
} | ||
|
||
if (hit.top || hit.bottom) { | ||
onBumpVertical(); | ||
} | ||
else if (hit.left || hit.right) { | ||
onBumpHorizontal(); | ||
} | ||
if (BadGuy::get_state() == STATE_SQUISHED || | ||
BadGuy::get_state() == STATE_BURNING) | ||
{ | ||
return; | ||
} | ||
|
||
if (hit.top || hit.bottom) | ||
on_bump_vertical(); | ||
else if (hit.left || hit.right) | ||
on_bump_horizontal(); | ||
} | ||
|
||
/** Linear prediction of player and badguy positions to decide if we should enter the DIVING state. */ | ||
bool | ||
Zeekling::should_we_dive() | ||
MatusGuy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
using RaycastResult = CollisionSystem::RaycastResult; | ||
|
||
if (m_frozen) | ||
return false; | ||
|
||
const auto player = get_nearest_player(); | ||
if (player && last_player && (player == last_player)) { | ||
Player* player = get_nearest_player(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to keep the usage of auto to a bare minimum, unless it's a long type or the same type is referenced twice in the same line. |
||
if (!player) | ||
return false; | ||
|
||
// Get positions and 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); | ||
// Left/rightmost point of the hitbox. | ||
Vector eye; | ||
const Rectf& bbox = get_bbox().grown(1.f); | ||
eye = bbox.get_middle(); | ||
eye.x = m_dir == Direction::LEFT ? bbox.get_left() : bbox.get_right(); | ||
|
||
// New vertical speed to test with. | ||
float vy = 2*fabsf(self_mov.x); | ||
const Vector& playermiddle = player->get_bbox().get_middle(); | ||
|
||
// 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 close to the player. | ||
float height = player->get_bbox().get_top() - get_bbox().get_top(); | ||
if (height <= MIN_DETECT_RANGE_Y) | ||
return false; | ||
|
||
// Do not dive if we are too far above the player. | ||
if (height > 512) return false; | ||
// Do not dive if we are too far above the player. | ||
if (height > MAX_DETECT_RANGE_Y) | ||
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; | ||
float xdist = std::abs(eye.x - playermiddle.x); | ||
if (!math::in_bounds(xdist, MIN_DETECT_RANGE_X, MAX_DETECT_RANGE_X)) | ||
return false; | ||
|
||
// Guess number of frames to descend to same height as player. | ||
float estFrames = height / relSpeed; | ||
RaycastResult result = Sector::get().get_first_line_intersection(eye, playermiddle, false, nullptr); | ||
|
||
// Guess where the player would be at this time. | ||
float estPx = (player_pos.x + (estFrames * player_mov.x)); | ||
auto* resultobj = std::get_if<CollisionObject*>(&result.hit); | ||
|
||
if (result.is_valid && resultobj && | ||
*resultobj == player->get_collision_object()) | ||
{ | ||
m_target_y = player->get_bbox().get_top() + CATCH_OFFSET; | ||
return true; | ||
} | ||
|
||
// Guess where we would be at this time. | ||
float estBx = (self_pos.x + (estFrames * self_mov.x)); | ||
return false; | ||
} | ||
|
||
// Allow for slight inaccuracies. | ||
if (fabsf(estPx - estBx) < 8) return true; | ||
} | ||
void | ||
Zeekling::set_speed(float speed) | ||
{ | ||
m_physic.set_velocity_x(speed * (m_dir == Direction::LEFT ? -1 : 1)); | ||
} | ||
|
||
// Update the 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(); | ||
} | ||
void Zeekling::fly() | ||
{ | ||
m_state = FLYING; | ||
set_speed(FLYING_SPEED); | ||
m_start_position.y = get_pos().y; | ||
set_action(m_dir); | ||
} | ||
|
||
return false; | ||
void Zeekling::charge() | ||
{ | ||
m_state = CHARGING; | ||
m_timer.start(CHARGING_DURATION); | ||
set_speed(CHARGING_SPEED); | ||
set_action("charge", m_dir); | ||
} | ||
|
||
void Zeekling::dive() | ||
{ | ||
m_state = DIVING; | ||
m_timer.start(DIVING_DURATION); | ||
set_speed(DIVING_SPEED); | ||
set_action("dive", m_dir); | ||
} | ||
|
||
void Zeekling::recover() | ||
{ | ||
m_state = RECOVERING; | ||
m_catch_pos = get_pos().y; | ||
m_timer.start(RECOVER_DURATION); | ||
set_action(m_dir); | ||
} | ||
|
||
void | ||
Zeekling::active_update(float dt_sec) { | ||
if (state == FLYING) { | ||
if (should_we_dive()) { | ||
state = DIVING; | ||
m_physic.set_velocity_y(2*fabsf(m_physic.get_velocity_x())); | ||
set_action("dive", m_dir); | ||
switch (m_state) { | ||
case FLYING: | ||
if (!should_we_dive()) | ||
break; | ||
|
||
charge(); | ||
|
||
break; | ||
|
||
case CHARGING: | ||
if (m_timer.check()) | ||
{ | ||
dive(); | ||
} | ||
break; | ||
|
||
case DIVING: | ||
{ | ||
if (m_timer.check()) | ||
{ | ||
recover(); | ||
break; | ||
} | ||
|
||
const float dist = m_target_y - m_start_position.y; | ||
const double progress = CubicEaseIn(static_cast<double>(1.f - m_timer.get_progress())); | ||
const float value = m_target_y - (static_cast<float>(progress) * dist); | ||
const Vector pos(get_pos().x, value); | ||
set_pos(pos); | ||
|
||
MatusGuy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
break; | ||
} | ||
BadGuy::active_update(dt_sec); | ||
return; | ||
} else if (state == DIVING) { | ||
BadGuy::active_update(dt_sec); | ||
return; | ||
} else if (state == CLIMBING) { | ||
// Stop climbing when we're back at initial height. | ||
if (get_pos().y <= m_start_position.y) { | ||
state = FLYING; | ||
m_physic.set_velocity_y(0); | ||
|
||
case RECOVERING: | ||
{ | ||
if (m_timer.check()) | ||
{ | ||
fly(); | ||
break; | ||
} | ||
|
||
const float dist = m_catch_pos - m_start_position.y; | ||
const double progress = QuadraticEaseInOut(static_cast<double>(m_timer.get_progress())); | ||
const float value = m_catch_pos - (static_cast<float>(progress) * dist); | ||
const Vector pos(get_pos().x, value); | ||
set_pos(pos); | ||
|
||
MatusGuy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
break; | ||
} | ||
BadGuy::active_update(dt_sec); | ||
return; | ||
} else { | ||
assert(false); | ||
|
||
default: | ||
break; | ||
} | ||
|
||
BadGuy::active_update(dt_sec); | ||
} | ||
|
||
void | ||
|
@@ -211,7 +279,7 @@ Zeekling::unfreeze(bool melt) | |
{ | ||
BadGuy::unfreeze(melt); | ||
m_physic.enable_gravity(false); | ||
state = FLYING; | ||
m_state = FLYING; | ||
initialize(); | ||
} | ||
|
||
|
@@ -221,4 +289,10 @@ Zeekling::is_freezable() const | |
return true; | ||
} | ||
|
||
void Zeekling::on_flip(float height) | ||
{ | ||
BadGuy::on_flip(height); | ||
m_start_position.y = get_pos().y; | ||
} | ||
|
||
/* EOF */ |
Uh oh!
There was an error while loading. Please reload this page.