From bbe2fa3553428eff7b1b5c89be27f7b0f42efa93 Mon Sep 17 00:00:00 2001 From: Andrew Kloet Date: Mon, 7 Jul 2025 12:28:38 -0400 Subject: [PATCH] add whale --- src/Aquarium.cpp | 15 +++++++--- src/Aquarium.h | 1 + src/Whale.cpp | 60 ++++++++++++++++++++++++++++++++++++++++ src/Whale.h | 34 +++++++++++++++++++++++ src/assets/WhaleAssets.h | 53 +++++++++++++++++++++++++++++++++++ 5 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 src/Whale.cpp create mode 100644 src/Whale.h create mode 100644 src/assets/WhaleAssets.h diff --git a/src/Aquarium.cpp b/src/Aquarium.cpp index 9d12401..1ec7be5 100644 --- a/src/Aquarium.cpp +++ b/src/Aquarium.cpp @@ -6,6 +6,7 @@ #include "Seaweed.h" #include "Ship.h" #include "Waterline.h" +#include "Whale.h" #include #include @@ -135,20 +136,26 @@ void Aquarium::addWaterline() { addEntityImpl(); } void Aquarium::addCastle() { addEntityImpl(); } void Aquarium::addShip() { addEntityImpl(); } void Aquarium::addSeaMonster() { addEntityImpl(); } +void Aquarium::addWhale() { addEntityImpl(); } + void Aquarium::ensureBigEntityExists() { // Check if any big entities exist on screen for (const auto &entity : entities) { if (dynamic_cast(entity.get()) || - dynamic_cast(entity.get())) { + dynamic_cast(entity.get()) || + dynamic_cast(entity.get())) { return; // Big entity found, do nothing } } - // No big entity found, spawn next in cycle - if (big_entity_index % 2 == 0) { + // No big entity found, spawn next in cycle (Ship, SeaMonster, Whale) + int entity_type = big_entity_index % 3; + if (entity_type == 0) { addEntityImpl(); - } else { + } else if (entity_type == 1) { addEntityImpl(); + } else { + addEntityImpl(); } ++big_entity_index; } diff --git a/src/Aquarium.h b/src/Aquarium.h index 3ad11d5..80e00a8 100644 --- a/src/Aquarium.h +++ b/src/Aquarium.h @@ -52,6 +52,7 @@ public: void addCastle(); void addShip(); void addSeaMonster(); + void addWhale(); void redraw(); void initColors(); diff --git a/src/Whale.cpp b/src/Whale.cpp new file mode 100644 index 0000000..ac716f9 --- /dev/null +++ b/src/Whale.cpp @@ -0,0 +1,60 @@ +#include "Whale.h" +#include "Aquarium.h" +#include "Random.h" +#include "assets/WhaleAssets.h" + +Whale::Whale() : Whale(getRandomDirection()) {} + +Whale::Whale(int asset_index) + : Entity(), frames(whaleAssets[asset_index].frames), + mask(whaleAssets[asset_index].mask), speed(WHALE_SPEED), + moving_right(asset_index == 0) { + + const auto &aquarium = Aquarium::getInstance(); + y = 0; + + // Use first frame for positioning calculations + const auto &first_frame = frames[0]; + if (moving_right) { + x = -static_cast(first_frame[6].length()); + } else { + x = static_cast(aquarium.getWidth()); + } + + current_image = first_frame; +} + +int Whale::getRandomDirection() { return Random::intInRange(0, 1); } + +void Whale::update() noexcept { + x += moving_right ? speed : -speed; + + ++animation_counter; + + // Use longer delay for first frame (no water spout) + int current_delay = + (current_frame_index == 0) ? FIRST_FRAME_PAUSE : ANIMATION_DELAY; + + if (animation_counter >= current_delay) { + current_frame_index = (current_frame_index + 1) % frames.size(); + animation_counter = 0; + } +} + +const std::vector &Whale::getImage() const { + current_image = frames[current_frame_index]; + return current_image; +} + +bool Whale::shouldBeRemoved() const noexcept { + const auto &aquarium = Aquarium::getInstance(); + const auto &first_frame = frames[0]; + + if (moving_right) { + return x > static_cast(aquarium.getWidth()); + } else { + return (x + static_cast(first_frame[0].length())) < 0; + } +} + +int Whale::getPreferredLayer() const noexcept { return 8; } diff --git a/src/Whale.h b/src/Whale.h new file mode 100644 index 0000000..f053545 --- /dev/null +++ b/src/Whale.h @@ -0,0 +1,34 @@ +#pragma once +#include "Entity.h" +#include "assets/WhaleAssets.h" + +class Whale : public Entity { +private: + static constexpr float WHALE_SPEED = 1.0f; + + const std::vector> frames; + const std::vector &mask; + const float speed; + const bool moving_right; + + int current_frame_index = 0; + int animation_counter = 0; + mutable std::vector current_image; + + static constexpr int ANIMATION_DELAY = 1; + static constexpr int FIRST_FRAME_PAUSE = 10; + + explicit Whale(int asset_index); + static int getRandomDirection(); + +public: + Whale(); + + void update() noexcept override; + const std::vector &getImage() const override; + const std::vector &getMask() const override { return mask; } + char getDefaultColor() const noexcept override { return 'B'; } + + bool shouldBeRemoved() const noexcept override; + int getPreferredLayer() const noexcept override; +}; diff --git a/src/assets/WhaleAssets.h b/src/assets/WhaleAssets.h new file mode 100644 index 0000000..fbc658f --- /dev/null +++ b/src/assets/WhaleAssets.h @@ -0,0 +1,53 @@ +#pragma once +#include "../Entity.h" +#include + +struct WhaleAsset { + std::vector> frames; + std::vector mask; +}; + +inline const std::vector whaleAssets = { + {{{{R"()", R"()", R"()", R"( .-----.)", R"( .' `.)", + R"(,????/ (o) \)", R"(\`._/ ,__)"}, + {R"()", R"()", R"( :)", R"( .-----.)", + R"( .' `.)", R"(,????/ (o) \)", + R"(\`._/ ,__)"}, + {R"()", R"( :)", R"( :)", R"( .-----.)", + R"( .' `.)", R"(,????/ (o) \)", + R"(\`._/ ,__)"}, + {R"( . .)", R"( -:-)", R"( :)", + R"( .-----.)", R"( .' `.)", R"(,????/ (o) \)", + R"(\`._/ ,__)"}, + {R"( . .)", R"( .-.-.)", R"( :)", + R"( .-----.)", R"( .' `.)", R"(,????/ (o) \)", + R"(\`._/ ,__)"}, + {R"( . .)", R"( '.-:-.')", R"( ' : ')", + R"( .-----.)", R"( .' `.)", R"(,????/ (o) \)", + R"(\`._/ ,__)"}, + {R"()", R"( .- -.)", R"( ; : ;)", R"( .-----.)", + R"( .' `.)", R"(,????/ (o) \)", + R"(\`._/ ,__)"}, + {R"()", R"()", R"( ; ;)", R"( .-----.)", + R"( .' `.)", R"(,????/ (o) \)", + R"(\`._/ ,__)"}}, + {R"( C C)", R"( CCCCCCC)", R"( C C C)", R"()", + R"()", R"( W)", R"()"}}, + {{{R"()", R"()", R"()", R"( .-----.)", R"( .' `.)", + R"( / (o) \????)", R"((__, \_.'/)"}, + {R"()", R"()", R"( :)", R"( .-----.)", R"( .' `.)", + R"( / (o) \????)", R"((__, \_.'/)"}, + {R"()", R"( :)", R"( :)", R"( .-----.)", + R"( .' `.)", R"( / (o) \????)", R"((__, \_.'/)"}, + {R"( . .)", R"( -:-)", R"( :)", R"( .-----.)", + R"( .' `.)", R"( / (o) \????)", R"((__, \_.'/)"}, + {R"( . .)", R"( .-.-.)", R"( :)", R"( .-----.)", + R"( .' `.)", R"( / (o) \????)", R"((__, \_.'/)"}, + {R"( . .)", R"( '.-:-.')", R"( ' : ')", R"( .-----.)", + R"( .' `.)", R"( / (o) \????)", R"((__, \_.'/)"}, + {R"()", R"( .- -.)", R"( ; : ;)", R"( .-----.)", + R"( .' `.)", R"( / (o) \????)", R"((__, \_.'/)"}, + {R"()", R"()", R"( ; ;)", R"( .-----.)", R"( .' `.)", + R"( / (o) \????)", R"((__, \_.'/)"}}, + {R"( C C)", R"( CCCCCCC)", R"( C C C)", R"()", R"()", + R"( W)", R"()"}}}};