diff --git a/data/images/creatures/dive_mine/dive_mine.sprite b/data/images/creatures/dive_mine/dive_mine.sprite
new file mode 100644
index 00000000000..8fc2fea1016
--- /dev/null
+++ b/data/images/creatures/dive_mine/dive_mine.sprite
@@ -0,0 +1,58 @@
+(supertux-sprite
+ (action
+ (name "left")
+ (fps 12.0)
+ (hitbox 14 19 32 32)
+ (images "left-0.png"
+ "left-1.png"
+ "left-2.png"
+ "left-3.png"
+ "left-4.png"
+ "left-5.png"
+ "left-6.png"
+ "left-7.png"
+ "left-8.png"
+ "left-9.png"
+ "left-10.png"
+ "left-11.png"))
+
+ (action
+ (name "right")
+ (fps 12.0)
+ (hitbox 14 19 32 32)
+ (mirror-action "left"))
+
+ (action
+ (name "iced-left")
+ (hitbox 5 8 32 32)
+ (images "left-0.png"))
+
+ (action
+ (name "iced-right")
+ (hitbox 5 8 32 32)
+ (mirror-action "iced-left"))
+
+ (action
+ (name "ticking-left")
+ (fps 15.0)
+ (hitbox 14 19 32 32)
+ (images "ticking-0.png"
+ "ticking-1.png"
+ "ticking-2.png"
+ "ticking-3.png"
+ "ticking-4.png"
+ "ticking-5.png"
+ "ticking-6.png"
+ "ticking-7.png"
+ "ticking-8.png"
+ "ticking-9.png"
+ ))
+
+
+ (action
+ (name "ticking-right")
+ (fps 15.0)
+ (hitbox 14 19 32 32)
+ (mirror-action "ticking-left"))
+
+)
diff --git a/data/images/creatures/dive_mine/left-0.png b/data/images/creatures/dive_mine/left-0.png
new file mode 100644
index 00000000000..fcc72f8f1c8
Binary files /dev/null and b/data/images/creatures/dive_mine/left-0.png differ
diff --git a/data/images/creatures/dive_mine/left-1.png b/data/images/creatures/dive_mine/left-1.png
new file mode 100644
index 00000000000..ef5eacd38c4
Binary files /dev/null and b/data/images/creatures/dive_mine/left-1.png differ
diff --git a/data/images/creatures/dive_mine/left-10.png b/data/images/creatures/dive_mine/left-10.png
new file mode 100644
index 00000000000..a5d993172d5
Binary files /dev/null and b/data/images/creatures/dive_mine/left-10.png differ
diff --git a/data/images/creatures/dive_mine/left-11.png b/data/images/creatures/dive_mine/left-11.png
new file mode 100644
index 00000000000..8759577feae
Binary files /dev/null and b/data/images/creatures/dive_mine/left-11.png differ
diff --git a/data/images/creatures/dive_mine/left-2.png b/data/images/creatures/dive_mine/left-2.png
new file mode 100644
index 00000000000..4e4a429c633
Binary files /dev/null and b/data/images/creatures/dive_mine/left-2.png differ
diff --git a/data/images/creatures/dive_mine/left-3.png b/data/images/creatures/dive_mine/left-3.png
new file mode 100644
index 00000000000..24883d82fb7
Binary files /dev/null and b/data/images/creatures/dive_mine/left-3.png differ
diff --git a/data/images/creatures/dive_mine/left-4.png b/data/images/creatures/dive_mine/left-4.png
new file mode 100644
index 00000000000..65aec8d71fa
Binary files /dev/null and b/data/images/creatures/dive_mine/left-4.png differ
diff --git a/data/images/creatures/dive_mine/left-5.png b/data/images/creatures/dive_mine/left-5.png
new file mode 100644
index 00000000000..e4c7a7e0130
Binary files /dev/null and b/data/images/creatures/dive_mine/left-5.png differ
diff --git a/data/images/creatures/dive_mine/left-6.png b/data/images/creatures/dive_mine/left-6.png
new file mode 100644
index 00000000000..62e24139e21
Binary files /dev/null and b/data/images/creatures/dive_mine/left-6.png differ
diff --git a/data/images/creatures/dive_mine/left-7.png b/data/images/creatures/dive_mine/left-7.png
new file mode 100644
index 00000000000..b8636dd363b
Binary files /dev/null and b/data/images/creatures/dive_mine/left-7.png differ
diff --git a/data/images/creatures/dive_mine/left-8.png b/data/images/creatures/dive_mine/left-8.png
new file mode 100644
index 00000000000..0841717904a
Binary files /dev/null and b/data/images/creatures/dive_mine/left-8.png differ
diff --git a/data/images/creatures/dive_mine/left-9.png b/data/images/creatures/dive_mine/left-9.png
new file mode 100644
index 00000000000..190dff94907
Binary files /dev/null and b/data/images/creatures/dive_mine/left-9.png differ
diff --git a/data/images/creatures/dive_mine/ticking-0.png b/data/images/creatures/dive_mine/ticking-0.png
new file mode 100644
index 00000000000..ea1476b28fc
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking-0.png differ
diff --git a/data/images/creatures/dive_mine/ticking-1.png b/data/images/creatures/dive_mine/ticking-1.png
new file mode 100644
index 00000000000..79b3b6278a7
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking-1.png differ
diff --git a/data/images/creatures/dive_mine/ticking-2.png b/data/images/creatures/dive_mine/ticking-2.png
new file mode 100644
index 00000000000..499e6c73265
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking-2.png differ
diff --git a/data/images/creatures/dive_mine/ticking-3.png b/data/images/creatures/dive_mine/ticking-3.png
new file mode 100644
index 00000000000..7adf77f9535
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking-3.png differ
diff --git a/data/images/creatures/dive_mine/ticking-4.png b/data/images/creatures/dive_mine/ticking-4.png
new file mode 100644
index 00000000000..0243d2299e3
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking-4.png differ
diff --git a/data/images/creatures/dive_mine/ticking-5.png b/data/images/creatures/dive_mine/ticking-5.png
new file mode 100644
index 00000000000..059dbb976f8
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking-5.png differ
diff --git a/data/images/creatures/dive_mine/ticking-6.png b/data/images/creatures/dive_mine/ticking-6.png
new file mode 100644
index 00000000000..4127d03d921
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking-6.png differ
diff --git a/data/images/creatures/dive_mine/ticking-7.png b/data/images/creatures/dive_mine/ticking-7.png
new file mode 100644
index 00000000000..69b61bd7c74
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking-7.png differ
diff --git a/data/images/creatures/dive_mine/ticking-8.png b/data/images/creatures/dive_mine/ticking-8.png
new file mode 100644
index 00000000000..e1bccbda977
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking-8.png differ
diff --git a/data/images/creatures/dive_mine/ticking-9.png b/data/images/creatures/dive_mine/ticking-9.png
new file mode 100644
index 00000000000..1a2cc3851bf
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking-9.png differ
diff --git a/data/images/creatures/dive_mine/ticking_glow/ticking_glow-0.png b/data/images/creatures/dive_mine/ticking_glow/ticking_glow-0.png
new file mode 100644
index 00000000000..ffbecf47554
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking_glow/ticking_glow-0.png differ
diff --git a/data/images/creatures/dive_mine/ticking_glow/ticking_glow-1.png b/data/images/creatures/dive_mine/ticking_glow/ticking_glow-1.png
new file mode 100644
index 00000000000..978ac9e9c02
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking_glow/ticking_glow-1.png differ
diff --git a/data/images/creatures/dive_mine/ticking_glow/ticking_glow-2.png b/data/images/creatures/dive_mine/ticking_glow/ticking_glow-2.png
new file mode 100644
index 00000000000..e257091d111
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking_glow/ticking_glow-2.png differ
diff --git a/data/images/creatures/dive_mine/ticking_glow/ticking_glow-3.png b/data/images/creatures/dive_mine/ticking_glow/ticking_glow-3.png
new file mode 100644
index 00000000000..93d58e23848
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking_glow/ticking_glow-3.png differ
diff --git a/data/images/creatures/dive_mine/ticking_glow/ticking_glow-4.png b/data/images/creatures/dive_mine/ticking_glow/ticking_glow-4.png
new file mode 100644
index 00000000000..8e9527cb346
Binary files /dev/null and b/data/images/creatures/dive_mine/ticking_glow/ticking_glow-4.png differ
diff --git a/data/images/creatures/dive_mine/ticking_glow/ticking_glow.sprite b/data/images/creatures/dive_mine/ticking_glow/ticking_glow.sprite
new file mode 100644
index 00000000000..e1336b7e598
--- /dev/null
+++ b/data/images/creatures/dive_mine/ticking_glow/ticking_glow.sprite
@@ -0,0 +1,19 @@
+(supertux-sprite
+ (action
+ (name "idle")
+ (fps 15)
+ (images "ticking_glow-4.png")
+ (hitbox 48 48 0 0)
+ )
+
+ (action
+ (name "ticking")
+ (fps 15)
+ (images "ticking_glow-0.png"
+ "ticking_glow-1.png"
+ "ticking_glow-2.png"
+ "ticking_glow-3.png"
+ "ticking_glow-4.png")
+ (hitbox 48 48 0 0)
+ )
+)
diff --git a/data/images/engine/editor/objects.stoi b/data/images/engine/editor/objects.stoi
index 1db914bb094..5aaf6e284e4 100644
--- a/data/images/engine/editor/objects.stoi
+++ b/data/images/engine/editor/objects.stoi
@@ -165,6 +165,9 @@
(object
(class "ghoul")
(icon "images/creatures/ghoul/g1.png"))
+ (object
+ (class "dive-mine")
+ (icon "images/creatures/dive_mine/left-0.png"))
)
(objectgroup
diff --git a/src/badguy/dive_mine.cpp b/src/badguy/dive_mine.cpp
new file mode 100644
index 00000000000..8b99adcb5c7
--- /dev/null
+++ b/src/badguy/dive_mine.cpp
@@ -0,0 +1,207 @@
+// SuperTux
+// Copyright (C) 2023 Vankata453
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "badguy/dive_mine.hpp"
+
+#include "editor/editor.hpp"
+#include "object/explosion.hpp"
+#include "object/player.hpp"
+#include "sprite/sprite.hpp"
+#include "sprite/sprite_manager.hpp"
+#include "supertux/sector.hpp"
+
+const float DiveMine::s_trigger_radius = 100.f;
+const float DiveMine::s_swim_speed = 20.f;
+const float DiveMine::s_max_float_acceleration = 15.f;
+
+DiveMine::DiveMine(const ReaderMapping& reader) :
+ BadGuy(reader, "images/creatures/dive_mine/dive_mine.sprite"),
+ m_ticking_glow(SpriteManager::current()->create("images/creatures/dive_mine/ticking_glow/ticking_glow.sprite")),
+ m_chasing(true)
+{
+ reset_sprites();
+}
+
+void
+DiveMine::reset_sprites()
+{
+ set_action(m_dir);
+ m_ticking_glow->set_action("idle");
+}
+
+void
+DiveMine::stop_chasing()
+{
+ if (!m_chasing)
+ return;
+
+ m_physic.reset();
+ m_physic.set_velocity_y(1.f);
+ m_physic.set_acceleration_y(s_max_float_acceleration);
+
+ reset_sprites();
+ m_chasing = false;
+}
+
+void
+DiveMine::explode()
+{
+ remove_me();
+ Sector::get().add(m_col.m_bbox.get_middle(), EXPLOSION_STRENGTH_DEFAULT);
+ run_dead_script();
+}
+
+void
+DiveMine::collision_solid(const CollisionHit& hit)
+{
+ if (m_in_water)
+ {
+ if (hit.left || hit.right)
+ turn_around();
+ }
+ else
+ {
+ explode();
+ }
+}
+
+HitResponse
+DiveMine::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
+{
+ if (!m_frozen &&
+ ((hit.left && (m_dir == Direction::LEFT)) || (hit.right && (m_dir == Direction::RIGHT))))
+ {
+ turn_around();
+ }
+
+ BadGuy::collision_badguy(badguy, hit);
+ return CONTINUE;
+}
+
+HitResponse
+DiveMine::collision_player(Player& player, const CollisionHit& hit)
+{
+ if (!m_frozen)
+ explode();
+
+ return ABORT_MOVE;
+}
+
+void
+DiveMine::draw(DrawingContext& context)
+{
+ BadGuy::draw(context);
+
+ if (m_frozen || Editor::is_active())
+ return;
+
+ m_ticking_glow->set_blend(Blend::ADD);
+ m_ticking_glow->draw(context.light(),
+ Vector(m_col.m_bbox.get_left() + m_col.m_bbox.get_width() / 2,
+ m_col.m_bbox.get_top() - 8.f),
+ m_layer, m_flip);
+}
+
+void
+DiveMine::active_update(float dt_sec)
+{
+ BadGuy::active_update(dt_sec);
+
+ if (m_frozen || !m_in_water)
+ {
+ m_physic.enable_gravity(true);
+ return;
+ }
+ m_physic.enable_gravity(false);
+
+ // Update float cycles
+ if (!m_chasing)
+ {
+ if (std::abs(m_physic.get_acceleration_y()) > s_max_float_acceleration * 3)
+ m_physic.inverse_velocity_y();
+
+ m_physic.set_acceleration_y(m_physic.get_acceleration_y() + (s_max_float_acceleration / 25) * (m_physic.get_velocity_y() < 0.f ? 1 : -1));
+ }
+
+ // Detect if player is near
+ auto player = get_nearest_player();
+ if (player)
+ {
+ // Face the player
+ m_dir = (player->get_pos().x > get_pos().x) ? Direction::RIGHT : Direction::LEFT;
+ }
+
+ if (!player || !player->is_swimming())
+ {
+ stop_chasing();
+ return;
+ }
+
+ Vector dist = player->get_bbox().get_middle() - m_col.m_bbox.get_middle();
+ if (m_chasing)
+ {
+ if (glm::length(dist) > s_trigger_radius) // Player is out of trigger radius
+ {
+ stop_chasing();
+ return;
+ }
+
+ set_action("ticking", m_dir);
+ m_ticking_glow->set_action("ticking");
+
+ m_physic.set_velocity(glm::normalize(dist) * s_swim_speed);
+ }
+ else
+ {
+ set_action(m_dir);
+ m_chasing = (glm::length(dist) <= s_trigger_radius);
+ }
+}
+
+void
+DiveMine::ignite()
+{
+ explode();
+}
+
+void
+DiveMine::freeze()
+{
+ BadGuy::freeze();
+
+ m_physic.reset();
+ m_physic.set_velocity_y(1.f);
+}
+
+void
+DiveMine::unfreeze(bool melt)
+{
+ BadGuy::unfreeze();
+
+ m_chasing = true; // Ensure stop_chasing() will be executed
+ stop_chasing();
+}
+
+void
+DiveMine::turn_around()
+{
+ if (m_frozen || m_chasing)
+ return;
+
+ m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT);
+}
+
+/* EOF */
diff --git a/src/badguy/dive_mine.hpp b/src/badguy/dive_mine.hpp
new file mode 100644
index 00000000000..6fe8f50a9c7
--- /dev/null
+++ b/src/badguy/dive_mine.hpp
@@ -0,0 +1,70 @@
+// SuperTux
+// Copyright (C) 2023 Vankata453
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#ifndef HEADER_SUPERTUX_BADGUY_DIVEMINE_HPP
+#define HEADER_SUPERTUX_BADGUY_DIVEMINE_HPP
+
+#include "badguy/badguy.hpp"
+
+#include "sprite/sprite_ptr.hpp"
+
+class DiveMine final : public BadGuy
+{
+private:
+ static const float s_trigger_radius;
+ static const float s_swim_speed;
+ static const float s_max_float_acceleration;
+
+public:
+ DiveMine(const ReaderMapping& reader);
+
+ virtual void collision_solid(const CollisionHit& hit) override;
+ virtual HitResponse collision_badguy(BadGuy& badguy, const CollisionHit& hit) override;
+ virtual HitResponse collision_player(Player& player, const CollisionHit& hit) override;
+
+ virtual void draw(DrawingContext& context) override;
+ virtual void active_update(float dt_sec) override;
+
+ virtual void ignite() override;
+ virtual void freeze() override;
+ virtual void unfreeze(bool melt = true) override;
+ virtual bool is_freezable() const override { return true; }
+
+ static std::string class_name() { return "dive-mine"; }
+ virtual std::string get_class_name() const override { return class_name(); }
+ static std::string display_name() { return _("Dive Mine"); }
+ virtual std::string get_display_name() const override { return display_name(); }
+
+private:
+ void reset_sprites();
+ void stop_chasing();
+
+ void explode();
+ void turn_around();
+
+private:
+ SpritePtr m_ticking_glow;
+
+ bool m_chasing;
+
+private:
+ DiveMine(const DiveMine&) = delete;
+ DiveMine& operator=(const DiveMine&) = delete;
+};
+
+#endif
+
+/* EOF */
diff --git a/src/supertux/game_object_factory.cpp b/src/supertux/game_object_factory.cpp
index 9d5e5273038..f1c1a611fdd 100644
--- a/src/supertux/game_object_factory.cpp
+++ b/src/supertux/game_object_factory.cpp
@@ -25,6 +25,7 @@
#include "badguy/dart.hpp"
#include "badguy/darttrap.hpp"
#include "badguy/dispenser.hpp"
+#include "badguy/dive_mine.hpp"
#include "badguy/fish_chasing.hpp"
#include "badguy/fish_harmless.hpp"
#include "badguy/fish_jumping.hpp"
@@ -169,6 +170,7 @@ GameObjectFactory::init_factories()
add_factory("dart", OBJ_PARAM_DISPENSABLE);
add_factory("darttrap");
add_factory("dispenser", OBJ_PARAM_DISPENSABLE);
+ add_factory("dive-mine", OBJ_PARAM_DISPENSABLE);
add_factory("fish-chasing", OBJ_PARAM_DISPENSABLE);
add_factory("fish-harmless", OBJ_PARAM_DISPENSABLE);
add_factory("fish"); // backward compatibility