commit 039d38da9df30a76921275780419b05e1ea18b65
parent 04133e71173affddbf4b53562da9795d77c9dcef
Author: amrfti <andrew@kloet.net>
Date: Mon, 26 May 2025 06:41:38 -0400
add sea monster
Diffstat:
13 files changed, 181 insertions(+), 49 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,7 +1,7 @@
# Compiler and flags
CXX = g++
-CXXFLAGS = -std=c++17 -Wall -Wextra -O3
-LDFLAGS = -lncurses -ltinfo
+CXXFLAGS = -std=c++17 -Wall -Wextra -O3 -fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables
+LDFLAGS = -lncurses -ltinfo -Wl,--gc-sections -s
# Directories
SRC_DIR = src
diff --git a/src/Aquarium.cpp b/src/Aquarium.cpp
@@ -2,6 +2,7 @@
#include "Bubble.h"
#include "Castle.h"
#include "Fish.h"
+#include "SeaMonster.h"
#include "Seaweed.h"
#include "Ship.h"
#include "Waterline.h"
@@ -46,6 +47,8 @@ void Aquarium::ensureEntitiesSorted() {
void Aquarium::redraw() {
clearCurrentFrame();
+ ensureBigEntityExists();
+
std::vector<std::unique_ptr<Entity>> newEntities;
bool entities_modified = false;
@@ -93,7 +96,7 @@ void Aquarium::redraw() {
// Draw all entities
for (const auto &entity : entities) {
- entity->draw(0);
+ entity->draw();
}
renderToScreen();
@@ -119,7 +122,6 @@ void Aquarium::resize() {
addWaterline();
addCastle();
- addShip();
for (int i = 0; i < width / 15; i++)
addSeaweed();
for (int i = 0; i < width * (height - 9) / 350; i++)
@@ -132,6 +134,24 @@ void Aquarium::addSeaweed() { addEntityImpl<Seaweed>(); }
void Aquarium::addWaterline() { addEntityImpl<Waterline>(); }
void Aquarium::addCastle() { addEntityImpl<Castle>(); }
void Aquarium::addShip() { addEntityImpl<Ship>(); }
+void Aquarium::addSeaMonster() { addEntityImpl<SeaMonster>(); }
+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())) {
+ return; // Big entity found, do nothing
+ }
+ }
+
+ // No big entity found, spawn next in cycle
+ if (big_entity_index % 2 == 0) {
+ addEntityImpl<Ship>();
+ } else {
+ addEntityImpl<SeaMonster>();
+ }
+ ++big_entity_index;
+}
void Aquarium::clearCurrentFrame() {
for (auto &row : currentFrame) {
diff --git a/src/Aquarium.h b/src/Aquarium.h
@@ -26,6 +26,8 @@ private:
std::vector<std::vector<Cell>> currentFrame;
std::vector<std::vector<Cell>> previousFrame;
std::vector<std::unique_ptr<Entity>> entities;
+ size_t big_entity_index = 0;
+ void ensureBigEntityExists();
bool entities_need_sorting = true;
static inline short colorLookup[256] = {0};
@@ -49,6 +51,7 @@ public:
void addWaterline();
void addCastle();
void addShip();
+ void addSeaMonster();
void redraw();
void initColors();
diff --git a/src/Entity.cpp b/src/Entity.cpp
@@ -1,7 +1,7 @@
#include "Entity.h"
#include "Aquarium.h"
-void Entity::draw(int layer) const {
+void Entity::draw() const {
auto &aquarium = Aquarium::getInstance();
const auto &image = getImage();
diff --git a/src/Entity.h b/src/Entity.h
@@ -31,8 +31,8 @@ public:
virtual char getDefaultColor() const noexcept = 0;
virtual bool shouldBeRemoved() const noexcept = 0;
- virtual std::unique_ptr<Entity> createReplacement() const = 0;
+ virtual std::unique_ptr<Entity> createReplacement() const { return nullptr; }
virtual int getPreferredLayer() const noexcept = 0;
- void draw(int) const;
+ void draw() const;
};
diff --git a/src/Fish.cpp b/src/Fish.cpp
@@ -4,24 +4,7 @@
#include "assets/FishAssets.h"
#include "defs.h"
-std::array<std::unordered_map<char, char>, 20> Fish::color_map_cache;
-bool Fish::color_maps_initialized = false;
-
-// Pre-generate 20 different color mappings at startup
-void Fish::initializeColorMaps() {
- for (auto &color_map : color_map_cache) {
- color_map.clear();
- color_map['4'] = 'W'; // White is always '4'
-
- for (char digit = '1'; digit <= '9'; ++digit) {
- if (digit != '4') {
- color_map[digit] = AVAILABLE_COLORS[Random::intInRange(
- 0, static_cast<int>(AVAILABLE_COLORS.size()) - 1)];
- }
- }
- }
- color_maps_initialized = true;
-}
+std::unordered_map<char, char> Fish::color_map;
Fish::Fish() : Fish(getRandomAssetIndex()) {}
@@ -31,24 +14,30 @@ Fish::Fish(int asset_index)
speed(Random::floatInRange(0.25f, 2.25f)),
moving_right(asset_index % 2 == 0) {
- if (!color_maps_initialized) {
- initializeColorMaps();
- }
-
const auto &aquarium = Aquarium::getInstance();
y = Random::intInRange(static_cast<int>(image.size()) + 6,
aquarium.getHeight() - static_cast<int>(image.size()));
x = moving_right ? -20.0f : static_cast<float>(aquarium.getWidth());
- applyRandomColorMapping();
+ randomizeMask();
}
-// Pick one of the pre-generated color mappings
-void Fish::applyRandomColorMapping() {
- const auto &selected_map = color_map_cache[Random::intInRange(0, 9)];
+void Fish::randomizeMask() {
+ // Clear and rebuild color map with fresh random colors each time
+ color_map.clear();
+ color_map['4'] = 'W'; // White is always '4'
+
+ // Assign random colors to digits 1-3, 5-9
+ for (char digit = '1'; digit <= '9'; ++digit) {
+ if (digit != '4') {
+ color_map[digit] = AVAILABLE_COLORS[Random::intInRange(
+ 0, static_cast<int>(AVAILABLE_COLORS.size()) - 1)];
+ }
+ }
+ // Apply color mapping to mask
for (auto &line : mask) {
for (char &ch : line) {
- if (auto it = selected_map.find(ch); it != selected_map.end()) {
+ if (auto it = color_map.find(ch); it != color_map.end()) {
ch = it->second;
}
}
diff --git a/src/Fish.h b/src/Fish.h
@@ -14,13 +14,11 @@ private:
const float speed;
const bool moving_right;
- static std::array<std::unordered_map<char, char>, 20> color_map_cache;
- static bool color_maps_initialized;
- static void initializeColorMaps();
+ static std::unordered_map<char, char> color_map;
explicit Fish(int asset_index);
static int getRandomAssetIndex();
- void applyRandomColorMapping();
+ void randomizeMask();
public:
Fish();
diff --git a/src/SeaMonster.cpp b/src/SeaMonster.cpp
@@ -0,0 +1,56 @@
+#include "SeaMonster.h"
+#include "Aquarium.h"
+#include "Random.h"
+#include "assets/SeaMonsterAssets.h"
+
+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) {
+
+ const auto &aquarium = Aquarium::getInstance();
+ y = WATER_SURFACE_OFFSET;
+
+ if (moving_right) {
+ x = -static_cast<float>(frame1[0].length());
+ } else {
+ x = static_cast<float>(aquarium.getWidth());
+ }
+
+ current_image = frame1;
+}
+
+int SeaMonster::getRandomDirection() { return Random::intInRange(0, 1); }
+
+void SeaMonster::update() noexcept {
+ x += moving_right ? speed : -speed;
+
+ ++animation_counter;
+ if (animation_counter >= ANIMATION_DELAY) {
+ current_frame = !current_frame;
+ animation_counter = 0;
+ }
+}
+
+const std::vector<std::string> &SeaMonster::getImage() const {
+ if (current_frame) {
+ current_image = frame2;
+ } else {
+ current_image = frame1;
+ }
+ 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
@@ -0,0 +1,35 @@
+#pragma once
+#include "Entity.h"
+#include "assets/SeaMonsterAssets.h"
+
+class SeaMonster : public Entity {
+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::string> &mask;
+ const float speed;
+ const bool moving_right;
+
+ bool current_frame = false;
+ int animation_counter = 0;
+ mutable std::vector<std::string> current_image;
+
+ static constexpr int ANIMATION_DELAY = 5;
+
+ explicit SeaMonster(int asset_index);
+ static int getRandomDirection();
+
+public:
+ SeaMonster();
+
+ 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 'G'; }
+
+ bool shouldBeRemoved() const noexcept override;
+ int getPreferredLayer() const noexcept override;
+};
diff --git a/src/Ship.cpp b/src/Ship.cpp
@@ -6,8 +6,8 @@
Ship::Ship() : Ship(getRandomDirection()) {}
Ship::Ship(int asset_index)
- : Entity(), image(pirateShipAssets[asset_index].image),
- mask(pirateShipAssets[asset_index].mask), speed(SHIP_SPEED),
+ : Entity(), image(shipAssets[asset_index].image),
+ mask(shipAssets[asset_index].mask), speed(SHIP_SPEED),
moving_right(asset_index == 0) {
const auto &aquarium = Aquarium::getInstance();
@@ -32,10 +32,4 @@ bool Ship::shouldBeRemoved() const noexcept {
}
}
-std::unique_ptr<Entity> Ship::createReplacement() const {
- return std::make_unique<Ship>();
-}
-
-int Ship::getPreferredLayer() const noexcept {
- return 9; // Ships on layer 9
-}
+int Ship::getPreferredLayer() const noexcept { return 9; }
diff --git a/src/Ship.h b/src/Ship.h
@@ -24,6 +24,5 @@ public:
char getDefaultColor() const noexcept override { return 'W'; }
bool shouldBeRemoved() const noexcept override;
- std::unique_ptr<Entity> createReplacement() const override;
int getPreferredLayer() const noexcept override;
};
diff --git a/src/assets/SeaMonsterAssets.h b/src/assets/SeaMonsterAssets.h
@@ -0,0 +1,38 @@
+#pragma once
+#include "../Entity.h"
+#include <vector>
+
+struct SeaMonsterAsset {
+ std::vector<std::string> frame1;
+ std::vector<std::string> frame2;
+ 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"()"}}};
diff --git a/src/assets/ShipAssets.h b/src/assets/ShipAssets.h
@@ -2,7 +2,7 @@
#include "../Entity.h"
#include <vector>
-inline std::vector<AssetPair> pirateShipAssets = {
+inline std::vector<AssetPair> shipAssets = {
{{
R"( | | |)",
R"( )_) )_) )_))",