add sea monster

This commit is contained in:
2025-05-26 06:41:38 -04:00
parent 593bbd6786
commit 90ae6678c5
13 changed files with 181 additions and 49 deletions

View File

@@ -1,7 +1,7 @@
# Compiler and flags # Compiler and flags
CXX = g++ CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -O3 CXXFLAGS = -std=c++17 -Wall -Wextra -O3 -fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables
LDFLAGS = -lncurses -ltinfo LDFLAGS = -lncurses -ltinfo -Wl,--gc-sections -s
# Directories # Directories
SRC_DIR = src SRC_DIR = src

View File

@@ -2,6 +2,7 @@
#include "Bubble.h" #include "Bubble.h"
#include "Castle.h" #include "Castle.h"
#include "Fish.h" #include "Fish.h"
#include "SeaMonster.h"
#include "Seaweed.h" #include "Seaweed.h"
#include "Ship.h" #include "Ship.h"
#include "Waterline.h" #include "Waterline.h"
@@ -46,6 +47,8 @@ void Aquarium::ensureEntitiesSorted() {
void Aquarium::redraw() { void Aquarium::redraw() {
clearCurrentFrame(); clearCurrentFrame();
ensureBigEntityExists();
std::vector<std::unique_ptr<Entity>> newEntities; std::vector<std::unique_ptr<Entity>> newEntities;
bool entities_modified = false; bool entities_modified = false;
@@ -93,7 +96,7 @@ void Aquarium::redraw() {
// Draw all entities // Draw all entities
for (const auto &entity : entities) { for (const auto &entity : entities) {
entity->draw(0); entity->draw();
} }
renderToScreen(); renderToScreen();
@@ -119,7 +122,6 @@ void Aquarium::resize() {
addWaterline(); addWaterline();
addCastle(); addCastle();
addShip();
for (int i = 0; i < width / 15; i++) for (int i = 0; i < width / 15; i++)
addSeaweed(); addSeaweed();
for (int i = 0; i < width * (height - 9) / 350; i++) 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::addWaterline() { addEntityImpl<Waterline>(); }
void Aquarium::addCastle() { addEntityImpl<Castle>(); } void Aquarium::addCastle() { addEntityImpl<Castle>(); }
void Aquarium::addShip() { addEntityImpl<Ship>(); } 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() { void Aquarium::clearCurrentFrame() {
for (auto &row : currentFrame) { for (auto &row : currentFrame) {

View File

@@ -26,6 +26,8 @@ private:
std::vector<std::vector<Cell>> currentFrame; std::vector<std::vector<Cell>> currentFrame;
std::vector<std::vector<Cell>> previousFrame; std::vector<std::vector<Cell>> previousFrame;
std::vector<std::unique_ptr<Entity>> entities; std::vector<std::unique_ptr<Entity>> entities;
size_t big_entity_index = 0;
void ensureBigEntityExists();
bool entities_need_sorting = true; bool entities_need_sorting = true;
static inline short colorLookup[256] = {0}; static inline short colorLookup[256] = {0};
@@ -49,6 +51,7 @@ public:
void addWaterline(); void addWaterline();
void addCastle(); void addCastle();
void addShip(); void addShip();
void addSeaMonster();
void redraw(); void redraw();
void initColors(); void initColors();

View File

@@ -1,7 +1,7 @@
#include "Entity.h" #include "Entity.h"
#include "Aquarium.h" #include "Aquarium.h"
void Entity::draw(int layer) const { void Entity::draw() const {
auto &aquarium = Aquarium::getInstance(); auto &aquarium = Aquarium::getInstance();
const auto &image = getImage(); const auto &image = getImage();

View File

@@ -31,8 +31,8 @@ public:
virtual char getDefaultColor() const noexcept = 0; virtual char getDefaultColor() const noexcept = 0;
virtual bool shouldBeRemoved() 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; virtual int getPreferredLayer() const noexcept = 0;
void draw(int) const; void draw() const;
}; };

View File

@@ -4,24 +4,7 @@
#include "assets/FishAssets.h" #include "assets/FishAssets.h"
#include "defs.h" #include "defs.h"
std::array<std::unordered_map<char, char>, 20> Fish::color_map_cache; std::unordered_map<char, char> Fish::color_map;
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;
}
Fish::Fish() : Fish(getRandomAssetIndex()) {} Fish::Fish() : Fish(getRandomAssetIndex()) {}
@@ -31,24 +14,30 @@ Fish::Fish(int asset_index)
speed(Random::floatInRange(0.25f, 2.25f)), speed(Random::floatInRange(0.25f, 2.25f)),
moving_right(asset_index % 2 == 0) { moving_right(asset_index % 2 == 0) {
if (!color_maps_initialized) {
initializeColorMaps();
}
const auto &aquarium = Aquarium::getInstance(); const auto &aquarium = Aquarium::getInstance();
y = Random::intInRange(static_cast<int>(image.size()) + 6, y = Random::intInRange(static_cast<int>(image.size()) + 6,
aquarium.getHeight() - static_cast<int>(image.size())); aquarium.getHeight() - static_cast<int>(image.size()));
x = moving_right ? -20.0f : static_cast<float>(aquarium.getWidth()); x = moving_right ? -20.0f : static_cast<float>(aquarium.getWidth());
applyRandomColorMapping(); randomizeMask();
} }
// Pick one of the pre-generated color mappings void Fish::randomizeMask() {
void Fish::applyRandomColorMapping() { // Clear and rebuild color map with fresh random colors each time
const auto &selected_map = color_map_cache[Random::intInRange(0, 9)]; 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 (auto &line : mask) {
for (char &ch : line) { 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; ch = it->second;
} }
} }

View File

@@ -14,13 +14,11 @@ private:
const float speed; const float speed;
const bool moving_right; const bool moving_right;
static std::array<std::unordered_map<char, char>, 20> color_map_cache; static std::unordered_map<char, char> color_map;
static bool color_maps_initialized;
static void initializeColorMaps();
explicit Fish(int asset_index); explicit Fish(int asset_index);
static int getRandomAssetIndex(); static int getRandomAssetIndex();
void applyRandomColorMapping(); void randomizeMask();
public: public:
Fish(); Fish();

56
src/SeaMonster.cpp Normal file
View File

@@ -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; }

35
src/SeaMonster.h Normal file
View File

@@ -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;
};

View File

@@ -6,8 +6,8 @@
Ship::Ship() : Ship(getRandomDirection()) {} Ship::Ship() : Ship(getRandomDirection()) {}
Ship::Ship(int asset_index) Ship::Ship(int asset_index)
: Entity(), image(pirateShipAssets[asset_index].image), : Entity(), image(shipAssets[asset_index].image),
mask(pirateShipAssets[asset_index].mask), speed(SHIP_SPEED), mask(shipAssets[asset_index].mask), speed(SHIP_SPEED),
moving_right(asset_index == 0) { moving_right(asset_index == 0) {
const auto &aquarium = Aquarium::getInstance(); const auto &aquarium = Aquarium::getInstance();
@@ -32,10 +32,4 @@ bool Ship::shouldBeRemoved() const noexcept {
} }
} }
std::unique_ptr<Entity> Ship::createReplacement() const { int Ship::getPreferredLayer() const noexcept { return 9; }
return std::make_unique<Ship>();
}
int Ship::getPreferredLayer() const noexcept {
return 9; // Ships on layer 9
}

View File

@@ -24,6 +24,5 @@ public:
char getDefaultColor() const noexcept override { return 'W'; } char getDefaultColor() const noexcept override { return 'W'; }
bool shouldBeRemoved() const noexcept override; bool shouldBeRemoved() const noexcept override;
std::unique_ptr<Entity> createReplacement() const override;
int getPreferredLayer() const noexcept override; int getPreferredLayer() const noexcept override;
}; };

View File

@@ -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"()"}}};

View File

@@ -2,7 +2,7 @@
#include "../Entity.h" #include "../Entity.h"
#include <vector> #include <vector>
inline std::vector<AssetPair> pirateShipAssets = { inline std::vector<AssetPair> shipAssets = {
{{ {{
R"( | | |)", R"( | | |)",
R"( )_) )_) )_))", R"( )_) )_) )_))",