fissh

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

commit 754643ba2253abf2bcb716fbe6514aefb4ca836d
parent 6409ce0b4b938e5be1bc5d9b9d5eda40996836c2
Author: amrfti <andrew@kloet.net>
Date:   Mon,  7 Jul 2025 17:25:29 -0400

finish mirroring. abstract entity removal

Diffstat:
MMakefile | 4++--
Msrc/Castle.h | 1-
Msrc/Entity.cpp | 25+++++--------------------
Msrc/Entity.h | 18+++++++++---------
Msrc/Fish.cpp | 14++------------
Msrc/Fish.h | 2--
Msrc/SeaMonster.cpp | 29++++++++---------------------
Msrc/SeaMonster.h | 7++-----
Msrc/Ship.cpp | 16+++-------------
Msrc/Ship.h | 3---
Asrc/SpriteUtils.h | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Waterline.h | 1-
Msrc/Whale.cpp | 16++--------------
Msrc/Whale.h | 3---
Msrc/assets/FishAssets.h | 136++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/assets/SeaMonsterAssets.h | 52++++++++++++++++++++++------------------------------
Msrc/assets/ShipAssets.h | 44+++++++++++---------------------------------
Dsrc/assets/SpriteUtils.h | 68--------------------------------------------------------------------
18 files changed, 241 insertions(+), 279 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ # Compiler and flags CXX = g++ -CXXFLAGS = -std=c++17 -Wall -Wextra -Os -fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables -LDFLAGS = -lncurses -ltinfo -Wl,--gc-sections -s +CXXFLAGS = -std=c++17 -Wall -Wextra -O3 +LDFLAGS = -lncurses -ltinfo # Directories SRC_DIR = src diff --git a/src/Castle.h b/src/Castle.h @@ -14,7 +14,6 @@ public: const std::vector<std::string> &getMask() const override { return mask; } char getDefaultColor() const noexcept override { return 'K'; } - bool shouldBeRemoved() const noexcept override { return false; } std::unique_ptr<Entity> createReplacement() const override { return nullptr; } int getPreferredLayer() const noexcept override { return 0; } }; diff --git a/src/Entity.cpp b/src/Entity.cpp @@ -1,6 +1,5 @@ #include "Entity.h" #include "Aquarium.h" -#include <algorithm> void Entity::draw() const { auto &aquarium = Aquarium::getInstance(); @@ -57,25 +56,11 @@ void Entity::draw() const { } } -size_t Entity::getMaxRowWidth(const std::vector<std::string> &image) noexcept { - if (image.empty()) { - return 0; - } - - size_t max_width = 0; - for (const auto &row : image) { - max_width = std::max(max_width, row.length()); - } - - return max_width; -} - bool Entity::shouldBeRemoved() const noexcept { const auto &aquarium = Aquarium::getInstance(); - const auto &current_image = getImage(); - const float entity_width = static_cast<float>(getMaxRowWidth(current_image)); - - // Default implementation: remove if completely off-screen - return (x + entity_width < 0) || - (x > static_cast<float>(aquarium.getWidth())); + if (moving_right) { + return x > static_cast<float>(aquarium.getWidth()); + } else { + return (x + static_cast<float>(getWidth())) < 0; + } } diff --git a/src/Entity.h b/src/Entity.h @@ -10,33 +10,33 @@ struct AssetPair { class Entity { protected: + const bool moving_right; float x; float y; static inline size_t next_id = 0; const size_t entity_id; + virtual const std::vector<std::string> &getImage() const = 0; + virtual const std::vector<std::string> &getMask() const = 0; + virtual char getDefaultColor() const noexcept = 0; public: - Entity() : x(0.0f), y(0.0f), entity_id(++next_id) {} + Entity() : moving_right(false), x(0.0f), y(0.0f), entity_id(++next_id) {} + Entity(bool moving_right) + : moving_right(moving_right), x(0.0f), y(0.0f), entity_id(++next_id) {} Entity(float init_x, float init_y) - : x(init_x), y(init_y), entity_id(++next_id) {} + : moving_right(false), x(init_x), y(init_y), entity_id(++next_id) {} virtual ~Entity() = default; float getX() const noexcept { return x; } float getY() const noexcept { return y; } + virtual size_t getWidth() const noexcept { return getImage()[0].length(); } size_t getId() const noexcept { return entity_id; } virtual void update() noexcept = 0; - virtual const std::vector<std::string> &getImage() const = 0; - virtual const std::vector<std::string> &getMask() const = 0; - virtual char getDefaultColor() const noexcept = 0; virtual bool shouldBeRemoved() const noexcept; virtual std::unique_ptr<Entity> createReplacement() const { return nullptr; } virtual int getPreferredLayer() const noexcept = 0; void draw() const; - -protected: - // Helper function to get the maximum width of any row in an image - static size_t getMaxRowWidth(const std::vector<std::string> &image) noexcept; }; diff --git a/src/Fish.cpp b/src/Fish.cpp @@ -9,10 +9,9 @@ std::unordered_map<char, char> Fish::color_map; Fish::Fish() : Fish(getRandomAssetIndex()) {} Fish::Fish(int asset_index) - : Entity(), image(fishAssetPairs[asset_index].image), + : Entity(asset_index % 2 == 0), image(fishAssetPairs[asset_index].image), mask(fishAssetPairs[asset_index].mask), - speed(Random::floatInRange(0.25f, 2.25f)), - moving_right(asset_index % 2 == 0) { + speed(Random::floatInRange(0.25f, 2.25f)) { const auto &aquarium = Aquarium::getInstance(); y = Random::intInRange(static_cast<int>(image.size()) + 6, @@ -50,15 +49,6 @@ int Fish::getRandomAssetIndex() { void Fish::update() noexcept { x += moving_right ? speed : -speed; } -bool Fish::shouldBeRemoved() const noexcept { - const auto &aquarium = Aquarium::getInstance(); - if (moving_right) { - return x > static_cast<float>(aquarium.getWidth()); - } else { - return (x + static_cast<float>(image[0].length())) < 0; - } -} - std::unique_ptr<Entity> Fish::createReplacement() const { return std::make_unique<Fish>(); } diff --git a/src/Fish.h b/src/Fish.h @@ -12,7 +12,6 @@ private: const std::vector<std::string> &image; std::vector<std::string> mask; const float speed; - const bool moving_right; static std::unordered_map<char, char> color_map; @@ -28,7 +27,6 @@ public: const std::vector<std::string> &getMask() const override { return mask; } char getDefaultColor() const noexcept override { return 'k'; } - bool shouldBeRemoved() const noexcept override; std::unique_ptr<Entity> createReplacement() const override; int getPreferredLayer() const noexcept override; diff --git a/src/SeaMonster.cpp b/src/SeaMonster.cpp @@ -6,21 +6,21 @@ SeaMonster::SeaMonster() : SeaMonster(getRandomDirection()) {} SeaMonster::SeaMonster(int asset_index) - : Entity(), frame1(seaMonsterAssets[asset_index].frame1), - frame2(seaMonsterAssets[asset_index].frame2), - mask(seaMonsterAssets[asset_index].mask), speed(SEAMONSTER_SPEED), - moving_right(asset_index == 0) { + : Entity(asset_index == 0), frames(seaMonsterAssets[asset_index].frames), + mask(seaMonsterAssets[asset_index].mask), speed(SEAMONSTER_SPEED) { const auto &aquarium = Aquarium::getInstance(); y = WATER_SURFACE_OFFSET; + // Use first frame for positioning calculations + const auto &first_frame = frames[0]; if (moving_right) { - x = -static_cast<float>(frame1[0].length()); + x = -static_cast<float>(first_frame[0].length()); } else { x = static_cast<float>(aquarium.getWidth()); } - current_image = frame1; + current_image = first_frame; } int SeaMonster::getRandomDirection() { return Random::intInRange(0, 1); } @@ -30,27 +30,14 @@ void SeaMonster::update() noexcept { ++animation_counter; if (animation_counter >= ANIMATION_DELAY) { - current_frame = !current_frame; + current_frame_index = (current_frame_index + 1) % frames.size(); animation_counter = 0; } } const std::vector<std::string> &SeaMonster::getImage() const { - if (current_frame) { - current_image = frame2; - } else { - current_image = frame1; - } + current_image = frames[current_frame_index]; return current_image; } -bool SeaMonster::shouldBeRemoved() const noexcept { - const auto &aquarium = Aquarium::getInstance(); - if (moving_right) { - return x > static_cast<float>(aquarium.getWidth()); - } else { - return (x + static_cast<float>(frame1[0].length())) < 0; - } -} - int SeaMonster::getPreferredLayer() const noexcept { return 8; } diff --git a/src/SeaMonster.h b/src/SeaMonster.h @@ -7,13 +7,11 @@ private: static constexpr float SEAMONSTER_SPEED = 0.8f; static constexpr int WATER_SURFACE_OFFSET = 2; - const std::vector<std::string> &frame1; - const std::vector<std::string> &frame2; + const std::vector<std::vector<std::string>> frames; const std::vector<std::string> &mask; const float speed; - const bool moving_right; - bool current_frame = false; + int current_frame_index = 0; int animation_counter = 0; mutable std::vector<std::string> current_image; @@ -30,6 +28,5 @@ public: const std::vector<std::string> &getMask() const override { return mask; } char getDefaultColor() const noexcept override { return 'G'; } - bool shouldBeRemoved() const noexcept override; int getPreferredLayer() const noexcept override; }; diff --git a/src/Ship.cpp b/src/Ship.cpp @@ -6,12 +6,11 @@ Ship::Ship() : Ship(getRandomDirection()) {} Ship::Ship(int asset_index) - : Entity(), image(shipAssets[asset_index].image), - mask(shipAssets[asset_index].mask), speed(SHIP_SPEED), - moving_right(asset_index == 0) { + : Entity(asset_index == 0), image(shipAssets[asset_index].image), + mask(shipAssets[asset_index].mask), speed(SHIP_SPEED) { const auto &aquarium = Aquarium::getInstance(); - y = WATER_SURFACE_OFFSET; + y = 0; if (moving_right) { x = -static_cast<float>(image[0].length()); } else { @@ -23,13 +22,4 @@ int Ship::getRandomDirection() { return Random::intInRange(0, 1); } void Ship::update() noexcept { x += moving_right ? speed : -speed; } -bool Ship::shouldBeRemoved() const noexcept { - const auto &aquarium = Aquarium::getInstance(); - if (moving_right) { - return x > static_cast<float>(aquarium.getWidth()); - } else { - return (x + static_cast<float>(image[0].length())) < 0; - } -} - int Ship::getPreferredLayer() const noexcept { return 9; } diff --git a/src/Ship.h b/src/Ship.h @@ -5,12 +5,10 @@ class Ship : public Entity { private: static constexpr float SHIP_SPEED = 1.0f; - static constexpr int WATER_SURFACE_OFFSET = 0; const std::vector<std::string> &image; const std::vector<std::string> &mask; const float speed; - const bool moving_right; explicit Ship(int asset_index); static int getRandomDirection(); @@ -23,6 +21,5 @@ public: const std::vector<std::string> &getMask() const override { return mask; } char getDefaultColor() const noexcept override { return 'W'; } - bool shouldBeRemoved() const noexcept override; int getPreferredLayer() const noexcept override; }; diff --git a/src/SpriteUtils.h b/src/SpriteUtils.h @@ -0,0 +1,81 @@ +// SpriteUtils.h - Unified mirroring for all asset types +#pragma once +#include <algorithm> +#include <string> +#include <unordered_map> +#include <vector> + +struct AssetPair; + +// Character mapping for directional elements +const std::unordered_map<char, char> charFlipMap = { + {'(', ')'}, {')', '('}, {'[', ']'}, {']', '['}, {'{', '}'}, + {'}', '{'}, {'<', '>'}, {'>', '<'}, {'/', '\\'}, {'\\', '/'}}; + +inline char mirrorChar(char c) { + auto it = charFlipMap.find(c); + return (it != charFlipMap.end()) ? it->second : c; +} + +inline std::string mirrorRow(std::string row) { + std::reverse(row.begin(), row.end()); + for (char &c : row) { + c = mirrorChar(c); + } + return row; +} + +// Mirror an entire sprite (vector of strings) +inline std::vector<std::string> +mirrorSprite(const std::vector<std::string> &sprite) { + std::vector<std::string> mirrored; + mirrored.reserve(sprite.size()); + for (const auto &row : sprite) { + mirrored.push_back(mirrorRow(row)); + } + return mirrored; +} + +inline AssetPair mirrorAssetPair(const AssetPair &asset) { + return {mirrorSprite(asset.image), mirrorSprite(asset.mask)}; +} + +// Create bidirectional assets from simple AssetPair vector +inline std::vector<AssetPair> +createBidirectionalAssets(const std::vector<AssetPair> &rightFacingAssets) { + std::vector<AssetPair> result; + result.reserve(rightFacingAssets.size() * 2); + + for (const auto &asset : rightFacingAssets) { + result.push_back(asset); // Right-facing + result.push_back(mirrorAssetPair(asset)); // Left-facing (mirrored) + } + + return result; +} + +// Generic template for any asset type with frames and mask +template <typename AssetType> +AssetType mirrorFramedAsset(const AssetType &asset) { + AssetType mirrored; + + // Mirror all frames + for (const auto &frame : asset.frames) { + mirrored.frames.push_back(mirrorSprite(frame)); + } + + // Mirror mask + mirrored.mask = mirrorSprite(asset.mask); + + return mirrored; +} + +// Create bidirectional assets from single framed asset +template <typename AssetType> +std::vector<AssetType> +createBidirectionalFramedAssets(const AssetType &rightFacingAsset) { + return { + rightFacingAsset, // [0] = Right-facing + mirrorFramedAsset(rightFacingAsset) // [1] = Left-facing (mirrored) + }; +} diff --git a/src/Waterline.h b/src/Waterline.h @@ -24,7 +24,6 @@ public: const std::vector<std::string> &getMask() const override; char getDefaultColor() const noexcept override { return WATERLINE_COLOR; } - bool shouldBeRemoved() const noexcept override { return false; } std::unique_ptr<Entity> createReplacement() const override { return nullptr; } int getPreferredLayer() const noexcept override { return 0; } diff --git a/src/Whale.cpp b/src/Whale.cpp @@ -6,9 +6,8 @@ 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) { + : Entity((asset_index = 0)), frames(whaleAssets[asset_index].frames), + mask(whaleAssets[asset_index].mask), speed(WHALE_SPEED) { const auto &aquarium = Aquarium::getInstance(); y = 0; @@ -46,15 +45,4 @@ const std::vector<std::string> &Whale::getImage() const { 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[6].length())) < 0; - } -} - int Whale::getPreferredLayer() const noexcept { return 8; } diff --git a/src/Whale.h b/src/Whale.h @@ -9,7 +9,6 @@ private: 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; @@ -28,7 +27,5 @@ public: 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/FishAssets.h b/src/assets/FishAssets.h @@ -1,53 +1,105 @@ #pragma once #include "../Entity.h" -#include "SpriteUtils.h" +#include "../SpriteUtils.h" #include <vector> -// Store only right-facing fish assets (half the data!) -const std::vector<AssetPair> rightFacingFishAssets = { - {{R"(???\??)", R"(??/ \?)", R"(>=_('>)", R"(??\_/?)", R"(???/??)"}, - {R"( 1 )", R"( 1 1 )", R"(663745)", R"( 111 )", R"( 3 )"}}, - {{R"(?????,????)", R"(?????}\???)", R"(\??.' `\?)", R"(}}< ( 6>)", - R"(/??`, .'?)", R"(?????}/???)", R"(?????'????)"}, - {R"( 2 )", R"( 22 )", R"(6 11 11 )", R"(661 7 45)", - R"(6 11 11 )", R"( 33 )", R"( 3 )"}}, - {{R"(???????,--,_????)", R"(__????_\.---'-.?)", R"(\ '.-" // o\)", - R"(/_.'-._ \\ /)", R"(???????`"--(/"`?)"}, - {R"( 22222 )", R"(66 121111211 )", R"(6 6111 77 41)", - R"(6661111 77 1)", R"( 11113311 )"}}, - {{R"(??__?)", R"(><_'>)", R"(???'?)"}, - {R"( 11 )", R"(61145)", R"( 3 )"}}, - {{R"(????????_.-`\??????)", R"(?????-:`_..,_\?????)", - R"(('-..:-` , '-.,?)", R"(?} _ ;':( o :)", - R"((.-`/'-.,__'` _.-`?)", R"(???`'-.,/??//`?????)"}, - {R"( 22222 )", R"( 222111112 )", - R"(66661111 7 1111 )", R"( 6 1 7777 4 1)", - R"(6666211111177 1111 )", R"( 222222 333 )"}}, - {{ - R"(????????/\??????)", - R"(????????\.\_????)", - R"(\'-,.:-` '-,?)", - R"( ) _ (>( o <)", - R"(/.-`?':._ _.-`?)", - R"(??????;/?``?????)", +const std::vector<AssetPair> fishAssets = { + { + { + R"(???\??)", + R"(??/ \?)", + R"(>=_('>)", + R"(??\_/?)", + R"(???/??)"}, + { + R"( 1 )", + R"( 1 1 )", + R"(663745)", + R"( 111 )", + R"( 3 )"}}, + { + { + R"(?????,????)", + R"(?????}\???)", + R"(\??.' `\?)", + R"(}}< ( 6>)", + R"(/??`, .'?)", + R"(?????}/???)", + R"(?????'????)"}, + { + R"( 2 )", + R"( 22 )", + R"(6 11 11 )", + R"(661 7 45)", + R"(6 11 11 )", + R"( 33 )", + R"( 3 )"}}, + { + { + R"(???????,--,_????)", + R"(__????_\.---'-.?)", + R"(\ '.-" // o\)", + R"(/_.'-._ \\ /)", + R"(???????`"--(/"`?)"}, + { + R"( 22222 )", + R"(66 121111211 )", + R"(6 6111 77 41)", + R"(6661111 77 1)", + R"( 11113311 )"}}, + { + { + R"(??__?)", + R"(><_'>)", + R"(???'?)"}, + { + R"( 11 )", + R"(61145)", + R"( 3 )"}}, + { + { + R"(????????_.-`\??????)", + R"(?????-:`_..,_\?????)", + R"(('-..:-` , '-.,?)", + R"(?} _ ;':( o :)", + R"((.-`/'-.,__'` _.-`?)", + R"(???`'-.,/??//`?????)"}, + { + R"( 22222 )", + R"( 222111112 )", + R"(66661111 7 1111 )", + R"( 6 1 7777 4 1)", + R"(6666211111177 1111 )", + R"( 222222 333 )"}}, + { + { + R"(????????/\??????)", + R"(????????\.\_????)", + R"(\'-,.:-` '-,?)", + R"( ) _ (>( o <)", + R"(/.-`?':._ _.-`?)", + R"(??????;/?``?????)", }, { - R"( 22 )", - R"( 2121 )", - R"(66661111 111 )", - R"( 6 1 777 4 1)", - R"(6666 1111 1111 )", - R"( 22 33 )", - }}, - {{R"(_?????????_.*"\??????)", R"(\'-._..-*` `'*-.??)", - R"(?) , (( o >)", R"(/.`"*--.__)_.`_.-*`??)"}, + R"( 22 )", + R"( 2121 )", + R"(66661111 111 )", + R"( 6 1 777 4 1)", + R"(6666 1111 1111 )", + R"( 22 33 )",}}, + { + { + R"(_?????????_.*"\??????)", + R"(\'-._..-*` `'*-.??)", + R"(?) , (( o >)", + R"(/.`"*--.__)_.`_.-*`??)"}, { - R"(6 11222 )", - R"(6661111111 11111 )", - R"( 6 3 77 4 1)", - R"(6661111111311311111 )", + R"(6 11222 )", + R"(6661111111 11111 )", + R"( 6 3 77 4 1)", + R"(6661111111311311111 )", }}}; inline const std::vector<AssetPair> fishAssetPairs = - SpriteUtils::createBidirectionalAssets(rightFacingFishAssets); + createBidirectionalAssets(fishAssets); diff --git a/src/assets/SeaMonsterAssets.h b/src/assets/SeaMonsterAssets.h @@ -1,38 +1,30 @@ #pragma once #include "../Entity.h" +#include "../SpriteUtils.h" #include <vector> struct SeaMonsterAsset { - std::vector<std::string> frame1; - std::vector<std::string> frame2; + std::vector<std::vector<std::string>> frames; std::vector<std::string> mask; }; -inline const std::vector<SeaMonsterAsset> seaMonsterAssets = { - {{ - R"( _???_?????????????????????_???_???????_a_a)", - R"( _{.`=`.}_??????_???_??????_{.`=`.}_????{/ ''\_)", - R"(?_????{.' _ '.}????{.`'`.}????{.' _ '.}??{| ._oo))", - R"({ \??{/ .'?'. \}??{/ .-. \}??{/ .'?'. \}?{/ |)", - }, - { - R"( _???_????????????????????_a_a)", - R"(??_??????_???_??????_{.`=`.}_??????_???_??????{/ ''\_)", - R"(?{ \????{.`'`.}????{.' _ '.}????{.`'`.}????{| ._oo))", - R"(??\ \??{/ .-. \}??{/ .'?'. \}??{/ .-. \}???{/ |)", - }, - { - R"( W W)", - R"()", - R"()", - R"()", - }}, - {{R"( a_a_???????_???_?????????????????????_???_)", - R"( _/'' \}????_{.`=`.}_??????_???_??????_{.`=`.}_)", - R"((oo_. |}??{.' _ '.}????{.`'`.}????{.' _ '.}????_)", - R"(????| \}?{/ .'?'. \}??{/ .-. \}??{/ .'?'. \}??/ })"}, - {R"( a_a_????????????????????_ _)", - R"( _/'' \}??????_???_??????_{.`=`.}_??????_???_??????_)", - R"((oo_. |}????{.`'`.}????{.' _ '.}????{.`'`.}????/ })", - R"(????| \}???{/ .-. \}??{/ .'?'. \}??{/ .-. \}??/ /)"}, - {R"( W W)", R"()", R"()", R"()"}}}; +const SeaMonsterAsset seaMonster = { + {// Frame 1 + { + R"(?????????_???_?????????????????????_???_???????_a_a???)", + R"(???????_{.`=`.}_??????_???_??????_{.`=`.}_????{/ ''\_?)", + R"(?_????{.' _ '.}????{.`'`.}????{.' _ '.}??{| ._oo))", + R"({ \??{/ .'?'. \}??{/ .-. \}??{/ .'?'. \}?{/ |????)"}, + { + R"(??????????????????????_???_????????????????????_a_a???)", + R"(??_??????_???_??????_{.`=`.}_??????_???_??????{/ ''\_?)", + R"(?{ \????{.`'`.}????{.' _ '.}????{.`'`.}????{| ._oo))", + R"(??\ \??{/ .-. \}??{/ .'?'. \}??{/ .-. \}???{/ |????)"}}, + { + R"( W W )", + R"()", + R"()", + R"()"}}; + +inline const std::vector<SeaMonsterAsset> seaMonsterAssets = + createBidirectionalFramedAssets(seaMonster); diff --git a/src/assets/ShipAssets.h b/src/assets/ShipAssets.h @@ -1,37 +1,15 @@ #pragma once #include "../Entity.h" +#include "../SpriteUtils.h" #include <vector> -inline std::vector<AssetPair> shipAssets = { - {{ - R"( | | |)", - R"( )_) )_) )_))", - R"( )___))___))___)\)", - R"( )____)____)_____)\\)", - R"(_____|____|____|____\\\__)", - R"(\ /)", - }, - { - R"( y y y)", - R"()", - R"( w)", - R"( ww)", - R"(yyyyyyyyyyyyyyyyyyyywwwyy)", - R"(y y)", - }}, - {{ - R"( | | |)", - R"( (_( (_( (_()", - R"( /(___((___((___()", - R"( //(_____(____(____()", - R"(__///____|____|____|_____)", - R"(????\ /)", - }, - { - R"( y y y)", - R"()", - R"( w)", - R"( ww)", - R"(yywwwyyyyyyyyyyyyyyyyyyyy)", - R"( y y)", - }}}; +const AssetPair ship = { + {R"( | | | )", R"( )_) )_) )_) )", + R"( )___))___))___)\ )", R"( )____)____)_____)\\ )", + R"(_____|____|____|____\\\__)", R"(\ /????)"}, + {R"( y y y )", R"()", R"( w )", + R"( ww )", R"(yyyyyyyyyyyyyyyyyyyywwwyy)", + R"(y y )"}}; + +inline const std::vector<AssetPair> shipAssets = + createBidirectionalAssets({ship}); diff --git a/src/assets/SpriteUtils.h b/src/assets/SpriteUtils.h @@ -1,68 +0,0 @@ -#pragma once -#include <algorithm> -#include <string> -#include <unordered_map> -#include <vector> - -struct AssetPair; - -namespace SpriteUtils { -// Character mapping for directional elements -const std::unordered_map<char, char> charFlipMap = { - {'(', ')'}, {')', '('}, {'[', ']'}, {']', '['}, {'{', '}'}, {'}', '{'}, - {'<', '>'}, {'>', '<'}, {'/', '\\'}, {'\\', '/'}, {'`', '\''}, {'\'', '`'}, -}; - -// Mirror a single character (handle directional flipping) -inline char mirrorChar(char c) { - auto it = charFlipMap.find(c); - return (it != charFlipMap.end()) ? it->second : c; -} - -// Mirror a single row with proper character flipping -inline std::string mirrorRow(std::string row) { - // First reverse the string - std::reverse(row.begin(), row.end()); - - // Then flip directional characters - for (char &c : row) { - c = mirrorChar(c); - } - - return row; -} - -// Mirror an entire sprite -inline std::vector<std::string> -mirrorSprite(const std::vector<std::string> &sprite) { - std::vector<std::string> mirrored; - mirrored.reserve(sprite.size()); - - for (const auto &row : sprite) { - mirrored.push_back(mirrorRow(row)); - } - - return mirrored; -} - -// Mirror an AssetPair -inline AssetPair mirrorAssetPair(const AssetPair &asset) { - return {mirrorSprite(asset.image), mirrorSprite(asset.mask)}; -} - -// Create bidirectional asset pairs from right-facing assets -inline std::vector<AssetPair> -createBidirectionalAssets(const std::vector<AssetPair> &rightFacingAssets) { - std::vector<AssetPair> result; - result.reserve(rightFacingAssets.size() * 2); - - for (const auto &asset : rightFacingAssets) { - // Add right-facing version - result.push_back(asset); - // Add left-facing (mirrored) version - result.push_back(mirrorAssetPair(asset)); - } - - return result; -} -} // namespace SpriteUtils