fissh

termios terminal aquarium. demo at ssh://fish@kloet.net
Download | Log | Files | Refs

commit 878039a6d5d52fefcc19421aac7771875f403886
parent 039d38da9df30a76921275780419b05e1ea18b65
Author: amrfti <andrew@kloet.net>
Date:   Mon,  7 Jul 2025 12:28:38 -0400

add whale

Diffstat:
Msrc/Aquarium.cpp | 15+++++++++++----
Msrc/Aquarium.h | 1+
Asrc/Whale.cpp | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Whale.h | 34++++++++++++++++++++++++++++++++++
Asrc/assets/WhaleAssets.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 159 insertions(+), 4 deletions(-)

diff --git 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 <algorithm> #include <iostream> @@ -135,20 +136,26 @@ void Aquarium::addWaterline() { addEntityImpl<Waterline>(); } void Aquarium::addCastle() { addEntityImpl<Castle>(); } void Aquarium::addShip() { addEntityImpl<Ship>(); } void Aquarium::addSeaMonster() { addEntityImpl<SeaMonster>(); } +void Aquarium::addWhale() { addEntityImpl<Whale>(); } + void Aquarium::ensureBigEntityExists() { // Check if any big entities exist on screen for (const auto &entity : entities) { if (dynamic_cast<Ship *>(entity.get()) || - dynamic_cast<SeaMonster *>(entity.get())) { + dynamic_cast<SeaMonster *>(entity.get()) || + dynamic_cast<Whale *>(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<Ship>(); - } else { + } else if (entity_type == 1) { addEntityImpl<SeaMonster>(); + } else { + addEntityImpl<Whale>(); } ++big_entity_index; } diff --git 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 @@ -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<float>(first_frame[6].length()); + } else { + x = static_cast<float>(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<std::string> &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<float>(aquarium.getWidth()); + } else { + return (x + static_cast<float>(first_frame[0].length())) < 0; + } +} + +int Whale::getPreferredLayer() const noexcept { return 8; } diff --git a/src/Whale.h 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<std::vector<std::string>> frames; + const std::vector<std::string> &mask; + const float speed; + const bool moving_right; + + int current_frame_index = 0; + int animation_counter = 0; + mutable std::vector<std::string> 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<std::string> &getImage() const override; + const std::vector<std::string> &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 @@ -0,0 +1,53 @@ +#pragma once +#include "../Entity.h" +#include <vector> + +struct WhaleAsset { + std::vector<std::vector<std::string>> frames; + std::vector<std::string> mask; +}; + +inline const std::vector<WhaleAsset> 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"()"}}}};