commit 754643ba2253abf2bcb716fbe6514aefb4ca836d
parent 6409ce0b4b938e5be1bc5d9b9d5eda40996836c2
Author: amrfti <andrew@kloet.net>
Date: Mon, 7 Jul 2025 17:25:29 -0400
finish mirroring. abstract entity removal
Diffstat:
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 ¤t_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> ℑ
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> ℑ
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