diff --git a/data/images/creatures/gold_bomb/flee-0.png b/data/images/creatures/gold_bomb/flee-0.png new file mode 100644 index 00000000000..a27b3b37e54 Binary files /dev/null and b/data/images/creatures/gold_bomb/flee-0.png differ diff --git a/data/images/creatures/gold_bomb/flee-1.png b/data/images/creatures/gold_bomb/flee-1.png new file mode 100644 index 00000000000..44dfa633edb Binary files /dev/null and b/data/images/creatures/gold_bomb/flee-1.png differ diff --git a/data/images/creatures/gold_bomb/flee-2.png b/data/images/creatures/gold_bomb/flee-2.png new file mode 100644 index 00000000000..142a6f33960 Binary files /dev/null and b/data/images/creatures/gold_bomb/flee-2.png differ diff --git a/data/images/creatures/gold_bomb/flee-3.png b/data/images/creatures/gold_bomb/flee-3.png new file mode 100644 index 00000000000..205b4161c1b Binary files /dev/null and b/data/images/creatures/gold_bomb/flee-3.png differ diff --git a/data/images/creatures/gold_bomb/flee-4.png b/data/images/creatures/gold_bomb/flee-4.png new file mode 100644 index 00000000000..7fe9153b0ae Binary files /dev/null and b/data/images/creatures/gold_bomb/flee-4.png differ diff --git a/data/images/creatures/gold_bomb/flee-5.png b/data/images/creatures/gold_bomb/flee-5.png new file mode 100644 index 00000000000..26f767a5d9a Binary files /dev/null and b/data/images/creatures/gold_bomb/flee-5.png differ diff --git a/data/images/creatures/gold_bomb/flee-6.png b/data/images/creatures/gold_bomb/flee-6.png new file mode 100644 index 00000000000..5bec6cdad68 Binary files /dev/null and b/data/images/creatures/gold_bomb/flee-6.png differ diff --git a/data/images/creatures/gold_bomb/flee-7.png b/data/images/creatures/gold_bomb/flee-7.png new file mode 100644 index 00000000000..3123bed2ba2 Binary files /dev/null and b/data/images/creatures/gold_bomb/flee-7.png differ diff --git a/data/images/creatures/gold_bomb/gold_bomb.sprite b/data/images/creatures/gold_bomb/gold_bomb.sprite index 4393ab319f2..ef98138f079 100644 --- a/data/images/creatures/gold_bomb/gold_bomb.sprite +++ b/data/images/creatures/gold_bomb/gold_bomb.sprite @@ -18,14 +18,83 @@ (hitbox 14 19 32 32) (mirror-action "left")) + (action + (name "flee-left") + (fps 25.0) + (hitbox 14 19 32 32) + (images "flee-0.png" + "flee-1.png" + "flee-2.png" + "flee-3.png" + "flee-4.png" + "flee-5.png" + "flee-6.png" + "flee-7.png")) + + (action + (name "flee-right") + (fps 25.0) + (hitbox 14 19 32 32) + (mirror-action "flee-left")) + + (action + (name "scared-left") + (fps 25.0) + (hitbox 14 19 32 32) + (images "scared-0.png" + "scared-1.png")) + + (action + (name "scared-right") + (fps 25.0) + (hitbox 14 19 32 32) + (mirror-action "scared-left")) + + (action + (name "recover-left") + (fps 20.0) + (loops 1) + (hitbox 14 19 32 32) + (images "scared-2.png" + "scared-3.png" + "scared-4.png" + "scared-5.png" + "scared-6.png" + "scared-7.png" + "scared-4.png" + "scared-5.png" + "scared-6.png" + "scared-7.png" + "scared-8.png" + "scared-9.png" + "scared-9.png" + "scared-9.png" + "scared-9.png" + "scared-9.png" + "scared-10.png" + "scared-11.png" + "scared-11.png" + "scared-11.png" + "scared-11.png" + "scared-11.png" + "scared-10.png" + "scared-7.png")) + + (action + (name "recover-right") + (fps 20.0) + (loops 1) + (hitbox 14 19 32 32) + (mirror-action "recover-left")) + (action (name "iced-left") - (hitbox 5 8 32 32) + (hitbox 14 19 32 32) (images "left-0.png")) (action (name "iced-right") - (hitbox 5 8 32 32) + (hitbox 14 19 32 32) (mirror-action "iced-left")) (action diff --git a/data/images/creatures/gold_bomb/scared-0.png b/data/images/creatures/gold_bomb/scared-0.png new file mode 100644 index 00000000000..6a80218971a Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-0.png differ diff --git a/data/images/creatures/gold_bomb/scared-1.png b/data/images/creatures/gold_bomb/scared-1.png new file mode 100644 index 00000000000..90668610e60 Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-1.png differ diff --git a/data/images/creatures/gold_bomb/scared-10.png b/data/images/creatures/gold_bomb/scared-10.png new file mode 100644 index 00000000000..d8d033d82cd Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-10.png differ diff --git a/data/images/creatures/gold_bomb/scared-11.png b/data/images/creatures/gold_bomb/scared-11.png new file mode 100644 index 00000000000..93812a5c732 Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-11.png differ diff --git a/data/images/creatures/gold_bomb/scared-2.png b/data/images/creatures/gold_bomb/scared-2.png new file mode 100644 index 00000000000..82e1065f88b Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-2.png differ diff --git a/data/images/creatures/gold_bomb/scared-3.png b/data/images/creatures/gold_bomb/scared-3.png new file mode 100644 index 00000000000..5cb6ca278dd Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-3.png differ diff --git a/data/images/creatures/gold_bomb/scared-4.png b/data/images/creatures/gold_bomb/scared-4.png new file mode 100644 index 00000000000..3cec6da43d5 Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-4.png differ diff --git a/data/images/creatures/gold_bomb/scared-5.png b/data/images/creatures/gold_bomb/scared-5.png new file mode 100644 index 00000000000..7886085e09c Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-5.png differ diff --git a/data/images/creatures/gold_bomb/scared-6.png b/data/images/creatures/gold_bomb/scared-6.png new file mode 100644 index 00000000000..a1d827a6dad Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-6.png differ diff --git a/data/images/creatures/gold_bomb/scared-7.png b/data/images/creatures/gold_bomb/scared-7.png new file mode 100644 index 00000000000..3e440549d75 Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-7.png differ diff --git a/data/images/creatures/gold_bomb/scared-8.png b/data/images/creatures/gold_bomb/scared-8.png new file mode 100644 index 00000000000..789d87919d2 Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-8.png differ diff --git a/data/images/creatures/gold_bomb/scared-9.png b/data/images/creatures/gold_bomb/scared-9.png new file mode 100644 index 00000000000..a3465714a03 Binary files /dev/null and b/data/images/creatures/gold_bomb/scared-9.png differ diff --git a/src/badguy/goldbomb.cpp b/src/badguy/goldbomb.cpp index 671fab7c535..971852fc39c 100644 --- a/src/badguy/goldbomb.cpp +++ b/src/badguy/goldbomb.cpp @@ -19,6 +19,8 @@ #include "audio/sound_manager.hpp" #include "audio/sound_source.hpp" +#include "badguy/bomb.hpp" +#include "badguy/haywire.hpp" #include "badguy/owl.hpp" #include "object/coin_explode.hpp" #include "object/explosion.hpp" @@ -29,14 +31,29 @@ #include "supertux/sector.hpp" #include "util/reader_mapping.hpp" +static const float HOP_HEIGHT = -250.f; +static const float REALIZE_TIME = 0.5f; + +// SAFE_DIST >= REALIZE_DIST +static const float REALIZE_DIST = 32.f * 8.f; +static const float SAFE_DIST = 32.f * 10.f; + +static const float NORMAL_WALK_SPEED = 80.0f; +static const float FLEEING_WALK_SPEED = 180.0f; +static const int NORMAL_MAX_DROP_HEIGHT = 16; +static const int FLEEING_MAX_DROP_HEIGHT = 600; + GoldBomb::GoldBomb(const ReaderMapping& reader) : WalkingBadguy(reader, "images/creatures/gold_bomb/gold_bomb.sprite", "left", "right"), tstate(STATE_NORMAL), + m_realize_timer(), ticking(), m_exploding_sprite(SpriteManager::current()->create("images/creatures/mr_bomb/ticking_glow/ticking_glow.sprite")) { - walk_speed = 80; - max_drop_height = 16; + assert(SAFE_DIST >= REALIZE_DIST); + + walk_speed = NORMAL_WALK_SPEED; + max_drop_height = NORMAL_MAX_DROP_HEIGHT; SoundManager::current()->preload("sounds/explosion.wav"); @@ -54,7 +71,11 @@ GoldBomb::collision_solid(const CollisionHit& hit) update_on_ground_flag(hit); return; + } else if (tstate != STATE_NORMAL && (hit.left || hit.right)) { + cornered(); + return; } + WalkingBadguy::collision_solid(hit); } @@ -94,9 +115,10 @@ GoldBomb::collision_badguy(BadGuy& badguy, const CollisionHit& hit) { if (tstate == STATE_TICKING) { - if (m_physic.get_velocity() != Vector()) - kill_fall(); + if (m_physic.get_velocity() != Vector()) kill_fall(); return ABORT_MOVE; + } else if (tstate != STATE_NORMAL) { + return FORCE_MOVE; } return WalkingBadguy::collision_badguy(badguy, hit); } @@ -113,7 +135,7 @@ GoldBomb::collision_squished(GameObject& object) kill_fall(); return true; } - if (is_valid() && tstate == STATE_NORMAL) { + if (is_valid() && tstate != STATE_TICKING) { tstate = STATE_TICKING; m_frozen = false; set_action(m_dir == Direction::LEFT ? "ticking-left" : "ticking-right", 1); @@ -147,15 +169,122 @@ GoldBomb::active_update(float dt_sec) } return; } - if (is_grabbed()) + + if ((tstate == STATE_FLEEING || tstate == STATE_CORNERED) && on_ground() && might_fall(FLEEING_MAX_DROP_HEIGHT+1)) + { + // also check for STATE_CORNERED just so + // the bomb doesnt automatically turn around + cornered(); return; + } WalkingBadguy::active_update(dt_sec); + + if (m_frozen) return; + + // Look for any of these in safe distance: + // Player, ticking Haywire, ticking Bomb or ticking GoldBomb + MovingObject* obj = nullptr; + std::vector objs = Sector::get().get_nearby_objects(get_bbox().get_middle(), SAFE_DIST); + for (MovingObject* currobj : objs) + { + obj = currobj; + + auto player = dynamic_cast(obj); + if (player && !player->get_ghost_mode()) break; + + auto haywire = dynamic_cast(obj); + if (haywire && haywire->is_exploding()) break; + + auto bomb = dynamic_cast(obj); + if (bomb) break; + + auto goldbomb = dynamic_cast(obj); + if (goldbomb && goldbomb->is_ticking()) break; + + obj = nullptr; + } + + if (!obj) + { + // Everybody's outside of safe distance. Am I cornered? + + if (tstate == STATE_CORNERED) + { + // Look back to check. + set_action("recover", m_dir); + if (!m_sprite->animation_done()) return; + } + + // Finally, when done recovering, go back to normal. + if (tstate == STATE_NORMAL) return; + + tstate = STATE_NORMAL; + m_physic.set_velocity_x(NORMAL_WALK_SPEED * (m_dir == Direction::LEFT ? -1 : 1)); + m_physic.set_acceleration_x(0); + set_action(m_dir); + max_drop_height = NORMAL_MAX_DROP_HEIGHT; + set_walk_speed(NORMAL_WALK_SPEED); + return; + } + + // Someone's in safe distance + const Vector p1 = get_bbox().get_middle(); + const Vector p2 = obj->get_bbox().get_middle(); + const Vector vecdist = p2-p1; + + // But I only react to those who are in realize distance + if (glm::length(vecdist) > REALIZE_DIST && tstate == STATE_NORMAL) return; + + // Someone's around! + switch (tstate) + { + case STATE_FLEEING: + // They popped up from the other side! Turn around! + if (m_dir == (vecdist.x > 0 ? Direction::LEFT : Direction::RIGHT)) return; + [[fallthrough]]; + + case STATE_NORMAL: + { + if (!on_ground()) break; + + // Gold bomb is solid therefore raycast from + // one of the upper corners of the hitbox. + // (grown 1 just to make sure it doesnt interfere.) + const Rectf eye = get_bbox().grown(1.f); + if (!Sector::get().free_line_of_sight( + vecdist.x <= 0 ? eye.p1() : Vector(eye.get_right(), eye.get_top()), + obj->get_bbox().get_middle(), + false, + obj + )) break; + + // Hop before fleeing. + set_walk_speed(0); + m_physic.set_velocity_y(HOP_HEIGHT); + m_physic.set_velocity_x(0); + m_physic.set_acceleration_x(0); + m_dir = vecdist.x > 0 ? Direction::RIGHT : Direction::LEFT; + m_sprite->set_action("flee", m_dir); + tstate = STATE_REALIZING; + m_realize_timer.start(REALIZE_TIME); + break; + } + + case STATE_REALIZING: + if (!m_realize_timer.check()) break; + + flee(vecdist.x > 0 ? Direction::LEFT : Direction::RIGHT); + break; + + default: break; + } } void GoldBomb::draw(DrawingContext& context) { m_sprite->draw(context.color(), get_pos(), m_layer, m_flip); + if (tstate == STATE_TICKING) { m_exploding_sprite->set_blend(Blend::ADD); @@ -267,7 +396,8 @@ GoldBomb::ungrab(MovingObject& object, Direction dir_) void GoldBomb::freeze() { - if (tstate == STATE_NORMAL) { + if (tstate != STATE_TICKING) { + tstate = STATE_NORMAL; WalkingBadguy::freeze(); } } @@ -298,4 +428,37 @@ void GoldBomb::play_looping_sounds() } } +void +GoldBomb::flee(Direction dir) +{ + set_walk_speed(FLEEING_WALK_SPEED); + max_drop_height = FLEEING_MAX_DROP_HEIGHT; + m_dir = dir; + + const float speed = FLEEING_WALK_SPEED * (m_dir == Direction::LEFT ? -1 : 1); + m_physic.set_acceleration_x(speed); + m_physic.set_velocity_x(speed); + + if (get_action() == dir_to_string(m_dir)) + m_sprite->set_animation_loops(-1); + else + set_action("flee", m_dir); + + tstate = STATE_FLEEING; +} + +void +GoldBomb::cornered() +{ + if (tstate == STATE_CORNERED) return; + + set_walk_speed(0); + m_physic.set_velocity_x(0); + m_physic.set_acceleration_x(0); + + set_action("scared", m_dir); + + tstate = STATE_CORNERED; +} + /* EOF */ diff --git a/src/badguy/goldbomb.hpp b/src/badguy/goldbomb.hpp index 877b0bfb20e..c9e56f0d2a2 100644 --- a/src/badguy/goldbomb.hpp +++ b/src/badguy/goldbomb.hpp @@ -54,17 +54,26 @@ class GoldBomb final : public WalkingBadguy virtual void stop_looping_sounds() override; virtual void play_looping_sounds() override; + bool is_ticking() const { return tstate == STATE_TICKING; } + protected: virtual bool collision_squished(GameObject& object) override; +private: + void flee(Direction dir); + void cornered(); + private: enum Ticking_State { STATE_NORMAL, - STATE_TICKING + STATE_TICKING, + STATE_REALIZING, + STATE_FLEEING, + STATE_CORNERED }; -private: Ticking_State tstate; + Timer m_realize_timer; std::unique_ptr ticking; SpritePtr m_exploding_sprite; diff --git a/src/badguy/haywire.cpp b/src/badguy/haywire.cpp index 39cc560df69..a60a23a798b 100644 --- a/src/badguy/haywire.cpp +++ b/src/badguy/haywire.cpp @@ -39,7 +39,7 @@ const float SKID_TIME = 0.3f; Haywire::Haywire(const ReaderMapping& reader) : WalkingBadguy(reader, "images/creatures/haywire/haywire.sprite", "left", "right"), - is_exploding(false), + m_is_exploding(false), time_until_explosion(0.0f), is_stunned(false), time_stunned(0.0f), @@ -86,7 +86,7 @@ Haywire::collision_squished(GameObject& object) WalkingBadguy::unfreeze(); } - if (!is_exploding) { + if (!m_is_exploding) { m_last_player_direction = m_dir; start_exploding(); stomped_timer.start(STOMPED_TIME); @@ -108,7 +108,7 @@ Haywire::active_update(float dt_sec) { auto* player = get_nearest_player(); - if (is_exploding) { + if (m_is_exploding) { ticking->set_position(get_pos()); grunting->set_position(get_pos()); if (dt_sec >= time_until_explosion) { @@ -129,7 +129,7 @@ Haywire::active_update(float dt_sec) } } - if (is_exploding) + if (m_is_exploding) { if (on_ground() && std::abs(m_physic.get_velocity_x()) > 40.f && player) { @@ -230,7 +230,7 @@ void Haywire::draw(DrawingContext& context) { m_sprite->draw(context.color(), get_pos(), m_layer, m_flip); - if (stomped_timer.get_timeleft() < 0.05f && is_exploding) + if (stomped_timer.get_timeleft() < 0.05f && m_is_exploding) { m_exploding_sprite->set_blend(Blend::ADD); m_exploding_sprite->draw(context.light(), @@ -242,7 +242,7 @@ Haywire::draw(DrawingContext& context) void Haywire::kill_fall() { - if (is_exploding) { + if (m_is_exploding) { ticking->stop(); grunting->stop(); } @@ -276,7 +276,7 @@ Haywire::ignite() void Haywire::freeze() { BadGuy::freeze(); - if (is_exploding) { + if (m_is_exploding) { stop_exploding(); } } @@ -287,7 +287,7 @@ Haywire::start_exploding() set_walk_speed (EXPLODING_WALK_SPEED); max_drop_height = -1; time_until_explosion = TIME_EXPLOSION; - is_exploding = true; + m_is_exploding = true; ticking = SoundManager::current()->create_sound_source("sounds/fizz.wav"); ticking->set_position(get_pos()); @@ -309,7 +309,7 @@ Haywire::stop_exploding() set_walk_speed(NORMAL_WALK_SPEED); max_drop_height = 16; time_until_explosion = 0.0f; - is_exploding = false; + m_is_exploding = false; if (ticking) ticking->stop(); @@ -330,7 +330,7 @@ void Haywire::stop_looping_sounds() void Haywire::play_looping_sounds() { - if (is_exploding) { + if (m_is_exploding) { if (ticking) { ticking->play(); } @@ -350,18 +350,15 @@ Haywire::collision_solid(const CollisionHit& hit) HitResponse Haywire::collision_badguy(BadGuy& badguy, const CollisionHit& hit) { - if (is_exploding) + if (m_is_exploding) { badguy.kill_fall(); return FORCE_MOVE; } if (m_frozen) return FORCE_MOVE; - else - { - WalkingBadguy::collision_badguy(badguy, hit); - } - return ABORT_MOVE; + + return WalkingBadguy::collision_badguy(badguy, hit); } /* EOF */ diff --git a/src/badguy/haywire.hpp b/src/badguy/haywire.hpp index 5eef8664f64..bca973206a4 100644 --- a/src/badguy/haywire.hpp +++ b/src/badguy/haywire.hpp @@ -47,6 +47,8 @@ class Haywire final : public WalkingBadguy virtual std::string get_display_name() const override { return display_name(); } virtual bool is_snipable() const override { return true; } + inline bool is_exploding() const { return m_is_exploding; } + protected: virtual bool collision_squished(GameObject& object) override; virtual void collision_solid(const CollisionHit& hit) override; @@ -58,7 +60,7 @@ class Haywire final : public WalkingBadguy void stop_exploding(); private: - bool is_exploding; + bool m_is_exploding; float time_until_explosion; bool is_stunned; float time_stunned;