Skip to content

Commit db256c3

Browse files
authored
Fix CorruptedCrusher's behaviour (#3277)
* Fix `CorruptedCrusher`'s behaviour * Make roots quicker * Remove unnecessary namespace
1 parent 9ff3bbc commit db256c3

File tree

4 files changed

+342
-180
lines changed

4 files changed

+342
-180
lines changed

src/badguy/crusher.cpp

Lines changed: 135 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,24 @@
4141
#include "util/reader_mapping.hpp"
4242
#include "video/surface.hpp"
4343

44-
namespace
45-
{
46-
/* Maximum movement speed in pixels per LOGICAL_FPS. */
47-
constexpr float RECOVER_SPEED_NORMAL = -3.125f;
48-
constexpr float RECOVER_SPEED_LARGE = -2.0f;
49-
constexpr float PAUSE_TIME_NORMAL = 0.5f;
50-
constexpr float PAUSE_TIME_LARGE = 1.0f;
51-
constexpr float DETECT_RANGE = 1000.f;
44+
/* Maximum movement speed in pixels per LOGICAL_FPS. */
45+
constexpr float RECOVER_SPEED_NORMAL = -3.125f;
46+
constexpr float RECOVER_SPEED_LARGE = -2.0f;
47+
constexpr float PAUSE_TIME_NORMAL = 0.5f;
48+
constexpr float PAUSE_TIME_LARGE = 1.0f;
49+
constexpr float DETECT_RANGE = 1000.f;
5250

53-
constexpr float MAX_CRUSH_SPEED = 700.f;
51+
constexpr float MAX_CRUSH_SPEED = 700.f;
5452

55-
constexpr float RECOVER_SPEED_MULTIPLIER_NORMAL = 1.125f;
56-
constexpr float RECOVER_SPEED_MULTIPLIER_LARGE = 1.0f;
53+
constexpr float RECOVER_SPEED_MULTIPLIER_NORMAL = 1.125f;
54+
constexpr float RECOVER_SPEED_MULTIPLIER_LARGE = 1.0f;
5755

58-
constexpr float BRICK_BREAK_PROBE_DISTANCE = 12.f;
59-
constexpr float RECOVERY_PATH_PROBE_DISTANCE = 1.0f;
60-
}
56+
constexpr float BRICK_BREAK_PROBE_DISTANCE = 12.f;
57+
constexpr float RECOVERY_PATH_PROBE_DISTANCE = 1.0f;
58+
59+
// Visual offset of the roots from the crusher.
60+
constexpr float ROOT_OFFSET_X = 2.5f;
61+
constexpr float ROOT_OFFSET_Y = 5.5f;
6162

6263
Crusher::CrusherDirection
6364
Crusher::CrusherDirection_from_string(std::string_view str)
@@ -507,83 +508,136 @@ Crusher::direction_from_vector(const Vector& vec)
507508
}
508509

509510
void
510-
Crusher::spawn_roots()
511+
Crusher::spawn_roots(const CollisionHit& hit_info)
511512
{
513+
constexpr float TILE_SIZE = 32.0f;
514+
512515
Vector pos(0.f, 0.f);
513516
const Direction dir = direction_from_vector(m_dir_vector);
514517
float* axis{};
515518
float origin{}, pos1{}, pos2{};
516519

517-
switch (dir)
520+
// Snap the impact origin to the tile grid.
521+
if (hit_info.bottom || hit_info.top)
518522
{
519-
case Direction::UP:
520-
origin = get_bbox().get_top();
521-
axis = &pos.x;
522-
pos1 = get_bbox().get_left();
523-
pos2 = get_bbox().get_right();
524-
break;
525-
526-
case Direction::DOWN:
527-
origin = get_bbox().get_bottom();
528-
axis = &pos.x;
529-
pos1 = get_bbox().get_left();
530-
pos2 = get_bbox().get_right();
531-
break;
532-
533-
case Direction::LEFT:
534-
origin = get_bbox().get_left();
535-
axis = &pos.y;
536-
pos1 = get_bbox().get_top();
537-
pos2 = get_bbox().get_bottom();
538-
break;
539-
540-
case Direction::RIGHT:
541-
origin = get_bbox().get_right();
542-
axis = &pos.y;
543-
pos1 = get_bbox().get_top();
544-
pos2 = get_bbox().get_bottom();
545-
break;
546-
case Direction::AUTO:
547-
case Direction::NONE:
548-
default:
549-
return;
523+
origin = round((hit_info.bottom ? get_bbox().get_bottom() : get_bbox().get_top()) / TILE_SIZE) * TILE_SIZE;
524+
axis = &pos.x;
525+
pos1 = get_bbox().get_left();
526+
pos2 = get_bbox().get_right();
527+
}
528+
else if (hit_info.left || hit_info.right)
529+
{
530+
origin = round((hit_info.left ? get_bbox().get_left() : get_bbox().get_right()) / TILE_SIZE) * TILE_SIZE;
531+
axis = &pos.y;
532+
pos1 = get_bbox().get_top();
533+
pos2 = get_bbox().get_bottom();
534+
}
535+
else
536+
{
537+
return;
550538
}
551539

552540
*(axis == &pos.y ? &pos.x : &pos.y) = origin;
553541

554-
for (int i = 0; i < 3; ++i)
555-
{
556-
const float delay = (static_cast<float>(i) + 1.f) * 0.6f;
557-
Root& root = Sector::get().add<Root>(Vector(0.f, 0.f), invert_dir(dir),
558-
"images/creatures/mole/corrupted/root.sprite",
559-
delay, false, false);
560-
const float dimension = (axis == &pos.y ? root.get_height() : root.get_width());
542+
const Direction root_direction = invert_dir(dir);
543+
544+
auto spawn_roots_on_side = [&](float start, float sign) {
545+
for (int i = 0; i < 3; ++i)
546+
{
547+
const float hatch_delay = 0.05f;
548+
const float delay = (static_cast<float>(i) + 1.f) * 0.22f;
549+
Root& root = Sector::get().add<Root>(Vector(0.f, 0.f),
550+
root_direction,
551+
"images/creatures/mole/corrupted/root.sprite",
552+
hatch_delay, false, false, delay);
553+
554+
const float dimension = (axis == &pos.y ? root.get_height() : root.get_width());
555+
556+
if (axis != nullptr)
557+
*axis = start + sign * (10.f + ((dimension / 2.f) + 50.f) * static_cast<float>(i));
558+
559+
// Ensure the root's base is on a solid surface.
560+
Rectf base_check_box(pos - Vector(2.0f, 2.0f), Sizef(4.0f, 4.0f));
561+
if (hit_info.bottom)
562+
base_check_box.move(Vector(0.f, 2.0f));
563+
else if (hit_info.top)
564+
base_check_box.move(Vector(0.f, -2.0f));
565+
else if (hit_info.left)
566+
base_check_box.move(Vector(-2.0f, 0.f));
567+
else if (hit_info.right)
568+
base_check_box.move(Vector(2.0f, 0.f));
569+
570+
if (Sector::get().is_free_of_tiles(base_check_box, false, Tile::SOLID))
571+
{
572+
root.remove_me();
573+
continue;
574+
}
561575

562-
if (axis != nullptr)
563-
*axis = pos1 - 10.f - (((dimension / 2.f) + 50.f) * static_cast<float>(i));
576+
// Ensure the root's exit path is clear.
577+
Rectf exit_path_box;
578+
const Sizef root_size = root.get_bbox().get_size();
579+
const float clearance = 1.0f;
564580

565-
root.set_pos(pos);
566-
root.construct();
567-
}
581+
switch (root_direction)
582+
{
583+
case Direction::UP:
584+
exit_path_box = Rectf(pos.x - root_size.width / 2.0f, pos.y - root_size.height - clearance,
585+
pos.x + root_size.width / 2.0f, pos.y - clearance);
586+
break;
587+
case Direction::DOWN:
588+
exit_path_box = Rectf(pos.x - root_size.width / 2.0f, pos.y + clearance,
589+
pos.x + root_size.width / 2.0f, pos.y + root_size.height + clearance);
590+
break;
591+
case Direction::LEFT:
592+
exit_path_box = Rectf(pos.x - root_size.width - clearance, pos.y - root_size.height / 2.0f,
593+
pos.x - clearance, pos.y + root_size.height / 2.0f);
594+
break;
595+
case Direction::RIGHT:
596+
exit_path_box = Rectf(pos.x + clearance, pos.y - root_size.height / 2.0f,
597+
pos.x + root_size.width + clearance, pos.y + root_size.height / 2.0f);
598+
break;
599+
default:
600+
break;
601+
}
568602

569-
for (int i = 0; i < 3; ++i)
570-
{
571-
const float delay = (static_cast<float>(i) + 1.f) * 0.6f;
572-
Root& root = Sector::get().add<Root>(Vector(0.f, 0.f), invert_dir(dir),
573-
"images/creatures/mole/corrupted/root.sprite",
574-
delay, false, false);
575-
const float dimension = (axis == &pos.y ? root.get_height() : root.get_width());
603+
if (!exit_path_box.empty() && !Sector::get().is_free_of_tiles(exit_path_box, false, Tile::SOLID))
604+
{
605+
root.remove_me();
606+
continue;
607+
}
608+
609+
Vector spawn_pos = pos;
610+
switch (root_direction)
611+
{
612+
case Direction::UP:
613+
// Addtional offset to account for grass.
614+
spawn_pos.y += ROOT_OFFSET_Y - 4.8f;
615+
break;
616+
case Direction::DOWN:
617+
spawn_pos.y -= ROOT_OFFSET_Y;
618+
break;
619+
case Direction::LEFT:
620+
spawn_pos.x += ROOT_OFFSET_X;
621+
break;
622+
case Direction::RIGHT:
623+
spawn_pos.x -= ROOT_OFFSET_X;
624+
break;
625+
default:
626+
break;
627+
}
576628

577-
if (axis != nullptr)
578-
*axis = pos2 + 10.f + (((dimension / 2.f) + 50.f) * static_cast<float>(i));
629+
root.set_pos(spawn_pos);
630+
root.construct();
631+
root.initialize();
632+
}
633+
};
579634

580-
root.set_pos(pos);
581-
root.construct();
582-
}
635+
spawn_roots_on_side(pos1, -1.f);
636+
spawn_roots_on_side(pos2, +1.f);
583637
}
584638

585639
void
586-
Crusher::crushed(const CollisionHit& hit_info)
640+
Crusher::crushed(const CollisionHit& hit_info, bool allow_root_spawn)
587641
{
588642
m_state = DELAY;
589643
m_state_timer.start(m_ic_size == NORMAL ? PAUSE_TIME_NORMAL : PAUSE_TIME_LARGE, true);
@@ -599,8 +653,8 @@ Crusher::crushed(const CollisionHit& hit_info)
599653

600654
spawn_particles(hit_info);
601655

602-
if (m_ic_type == CORRUPTED)
603-
spawn_roots();
656+
if (m_ic_type == CORRUPTED && allow_root_spawn)
657+
spawn_roots(hit_info);
604658

605659
run_crush_script();
606660
}
@@ -807,7 +861,7 @@ Crusher::collision(MovingObject& other, const CollisionHit& hit)
807861
if (m_dir_vector.y > 0.5f && hit.bottom) // Down, hit bottom
808862
is_crushing_hit = true;
809863
else if (m_dir_vector.y < -0.5f && hit.top) // Up, hit top
810-
is_crushing_hit = true;
864+
is_crushing_hit = true;
811865
else if (m_dir_vector.x < -0.5f && hit.left) // Left, hit left
812866
is_crushing_hit = true;
813867
else if (m_dir_vector.x > 0.5f && hit.right) // Right, hit right
@@ -828,7 +882,7 @@ Crusher::collision(MovingObject& other, const CollisionHit& hit)
828882
if (player_vulnerable)
829883
{
830884
SoundManager::current()->play("sounds/brick.wav", get_pos());
831-
crushed(hit);
885+
crushed(hit, false);
832886

833887
if (!player->is_invincible())
834888
{
@@ -841,7 +895,7 @@ Crusher::collision(MovingObject& other, const CollisionHit& hit)
841895

842896
if (other_crusher)
843897
{
844-
crushed(hit);
898+
crushed(hit, false);
845899
return ABORT_MOVE;
846900
}
847901

@@ -858,7 +912,7 @@ Crusher::collision(MovingObject& other, const CollisionHit& hit)
858912
if (m_dir != CrusherDirection::HORIZONTAL && m_dir != CrusherDirection::UP && m_dir != CrusherDirection::ALL)
859913
{
860914
SoundManager::current()->play("sounds/brick.wav", get_pos());
861-
crushed(hit);
915+
crushed(hit, true);
862916
return ABORT_MOVE;
863917
}
864918
else // Ensure the rock does not get stuck in a wall when pushed by the crusher.
@@ -904,7 +958,7 @@ Crusher::collision(MovingObject& other, const CollisionHit& hit)
904958

905959
if (rock_would_hit_wall)
906960
{
907-
crushed(hit);
961+
crushed(hit, true);
908962
return ABORT_MOVE;
909963
}
910964
else
@@ -928,7 +982,7 @@ Crusher::collision_solid(const CollisionHit& hit)
928982
{
929983
if (m_state == CRUSHING && should_finish_crushing(hit))
930984
{
931-
crushed(hit);
985+
crushed(hit, true);
932986
}
933987
else if (m_state == RECOVERING)
934988
{
@@ -1009,7 +1063,7 @@ Crusher::update(float dt_sec)
10091063
}
10101064
break;
10111065
}
1012-
1066+
10131067
case DELAY:
10141068
if (m_state_timer.check())
10151069
{

src/badguy/crusher.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,13 @@ class Crusher final : public MovingSprite
105105
static Direction direction_from_vector(const Vector& vec);
106106

107107
void crush();
108-
void crushed(const CollisionHit& hit_info);
108+
void crushed(const CollisionHit& hit_info, bool allow_root_spawn);
109109
void run_crush_script();
110110
void recover();
111111
void idle();
112112
bool is_recovery_path_clear_of_crushers() const;
113113

114-
void spawn_roots();
114+
void spawn_roots(const CollisionHit& hit_info);
115115
void spawn_particles(const CollisionHit& hit_info);
116116

117117
inline std::string get_crush_sound() const;

0 commit comments

Comments
 (0)