REBASE AWESOME
This commit is contained in:
331
src/Aquarium.cpp
331
src/Aquarium.cpp
@@ -1,12 +1,15 @@
|
||||
#include "Aquarium.h"
|
||||
#include "Random.h"
|
||||
#include "defs.h"
|
||||
#include "Bubble.h"
|
||||
#include "Castle.h"
|
||||
#include "Fish.h"
|
||||
#include "Seaweed.h"
|
||||
#include "Ship.h"
|
||||
#include "Waterline.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <ncurses.h>
|
||||
|
||||
int g_maxCells = 0;
|
||||
|
||||
Aquarium::Aquarium() {
|
||||
initscr();
|
||||
noecho();
|
||||
@@ -14,41 +17,86 @@ Aquarium::Aquarium() {
|
||||
nodelay(stdscr, TRUE);
|
||||
curs_set(0);
|
||||
initColors();
|
||||
initColorLookup();
|
||||
timeout(100);
|
||||
getmaxyx(stdscr, height, width);
|
||||
frontBuffer.assign(height, std::vector<Cell>(width));
|
||||
backBuffer.assign(height, std::vector<Cell>(width));
|
||||
layeredMap.assign(height, std::vector<LayeredCell>(width));
|
||||
}
|
||||
|
||||
void Aquarium::initColors() {
|
||||
if (has_colors()) {
|
||||
start_color();
|
||||
init_pair(1, COLOR_RED, COLOR_BLACK); // 'r'
|
||||
init_pair(2, COLOR_GREEN, COLOR_BLACK); // 'g'
|
||||
init_pair(3, COLOR_YELLOW, COLOR_BLACK); // 'y'
|
||||
init_pair(4, COLOR_BLUE, COLOR_BLACK); // 'b'
|
||||
init_pair(5, COLOR_MAGENTA, COLOR_BLACK); // 'm'
|
||||
init_pair(6, COLOR_CYAN, COLOR_BLACK); // 'c'
|
||||
init_pair(7, COLOR_WHITE, COLOR_BLACK); // 'w'
|
||||
init_pair(8, COLOR_BLACK, COLOR_BLACK); // 'k'
|
||||
currentFrame.assign(height, std::vector<Cell>(width));
|
||||
previousFrame.assign(height, std::vector<Cell>(width));
|
||||
|
||||
if (!colorLookupInitialized) {
|
||||
initColorLookup();
|
||||
colorLookupInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
short colorLookup[256];
|
||||
void Aquarium::initColorLookup() {
|
||||
for (int i = 0; i < 256; ++i)
|
||||
colorLookup[i] = 8; // default to black
|
||||
void Aquarium::ensureEntitiesSorted() {
|
||||
if (entities_need_sorting) {
|
||||
std::sort(entities.begin(), entities.end(),
|
||||
[](const auto &a, const auto &b) {
|
||||
int layerA = a->getPreferredLayer();
|
||||
int layerB = b->getPreferredLayer();
|
||||
if (layerA != layerB)
|
||||
return layerA < layerB;
|
||||
return a->getId() < b->getId();
|
||||
});
|
||||
entities_need_sorting = false;
|
||||
}
|
||||
}
|
||||
|
||||
colorLookup['r'] = 1;
|
||||
colorLookup['g'] = 2;
|
||||
colorLookup['y'] = 3;
|
||||
colorLookup['b'] = 4;
|
||||
colorLookup['m'] = 5;
|
||||
colorLookup['c'] = 6;
|
||||
colorLookup['w'] = 7;
|
||||
colorLookup['k'] = 8;
|
||||
void Aquarium::redraw() {
|
||||
clearCurrentFrame();
|
||||
|
||||
std::vector<std::unique_ptr<Entity>> newEntities;
|
||||
bool entities_modified = false;
|
||||
|
||||
// Update and check for removal/replacement
|
||||
for (auto it = entities.begin(); it != entities.end();) {
|
||||
auto &entity = *it;
|
||||
entity->update();
|
||||
|
||||
// Handle fish bubble spawning
|
||||
if (auto *fish = dynamic_cast<Fish *>(entity.get())) {
|
||||
if (fish->shouldSpawnBubble()) {
|
||||
newEntities.emplace_back(
|
||||
std::make_unique<Bubble>(fish->getX(), fish->getY()));
|
||||
}
|
||||
}
|
||||
|
||||
if (entity->shouldBeRemoved()) {
|
||||
auto replacement = entity->createReplacement();
|
||||
if (replacement) {
|
||||
*it = std::move(replacement);
|
||||
entities_modified = true;
|
||||
++it;
|
||||
} else {
|
||||
it = entities.erase(it);
|
||||
entities_modified = true;
|
||||
}
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new entities if any
|
||||
if (!newEntities.empty()) {
|
||||
for (auto &newEntity : newEntities) {
|
||||
entities.emplace_back(std::move(newEntity));
|
||||
}
|
||||
entities_modified = true;
|
||||
}
|
||||
|
||||
// Only sort if entities were modified
|
||||
if (entities_modified) {
|
||||
entities_need_sorting = true;
|
||||
}
|
||||
ensureEntitiesSorted();
|
||||
|
||||
// Draw all entities
|
||||
for (const auto &entity : entities) {
|
||||
entity->draw(0);
|
||||
}
|
||||
|
||||
renderToScreen();
|
||||
}
|
||||
|
||||
void Aquarium::resize() {
|
||||
@@ -56,20 +104,18 @@ void Aquarium::resize() {
|
||||
getmaxyx(stdscr, height, width);
|
||||
|
||||
if (g_maxCells && height * width > g_maxCells) {
|
||||
endwin(); // Cleanly shut down ncurses
|
||||
endwin();
|
||||
std::cerr << "Error: Terminal too large. Maximum allowed area is "
|
||||
<< g_maxCells << " cells, but current size is "
|
||||
<< (height * width) << ".\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
frontBuffer.assign(height, std::vector<Cell>(width));
|
||||
backBuffer.assign(height, std::vector<Cell>(width));
|
||||
layeredMap.assign(height, std::vector<LayeredCell>(width));
|
||||
currentFrame.assign(height, std::vector<Cell>(width));
|
||||
previousFrame.assign(height, std::vector<Cell>(width));
|
||||
|
||||
fishes.clear();
|
||||
bubbles.clear();
|
||||
seaweeds.clear();
|
||||
entities.clear();
|
||||
entities_need_sorting = true;
|
||||
|
||||
addWaterline();
|
||||
addCastle();
|
||||
@@ -80,101 +126,21 @@ void Aquarium::resize() {
|
||||
addFish();
|
||||
}
|
||||
|
||||
Aquarium::~Aquarium() { endwin(); }
|
||||
void Aquarium::addFish() { addEntityImpl<Fish>(); }
|
||||
void Aquarium::addBubble(float x, float y) { addEntityImpl<Bubble>(x, y); }
|
||||
void Aquarium::addSeaweed() { addEntityImpl<Seaweed>(); }
|
||||
void Aquarium::addWaterline() { addEntityImpl<Waterline>(); }
|
||||
void Aquarium::addCastle() { addEntityImpl<Castle>(); }
|
||||
void Aquarium::addShip() { addEntityImpl<Ship>(); }
|
||||
|
||||
void Aquarium::redraw() {
|
||||
clearBackBuffer();
|
||||
|
||||
for (auto &row : layeredMap)
|
||||
for (auto &cell : row)
|
||||
cell.layers.clear();
|
||||
|
||||
for (auto it = bubbles.begin(); it != bubbles.end();) {
|
||||
auto &bubble = *it;
|
||||
bubble->draw();
|
||||
bubble->update();
|
||||
if (bubble->isOutOfWater())
|
||||
it = bubbles.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
castle->draw();
|
||||
|
||||
for (auto it = seaweeds.begin(); it != seaweeds.end();) {
|
||||
auto &seaweed = *it;
|
||||
seaweed->draw();
|
||||
seaweed->update();
|
||||
if (seaweed->getLifetime() < 0) {
|
||||
it = seaweeds.erase(it);
|
||||
addSeaweed();
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
int baseFishLayer = 10;
|
||||
for (auto it = fishes.begin(); it != fishes.end();) {
|
||||
auto &fish = *it;
|
||||
|
||||
static_cast<const Entity *>(fish.get())
|
||||
->draw(baseFishLayer +
|
||||
static_cast<int>(std::distance(fishes.begin(), it)));
|
||||
|
||||
fish->update();
|
||||
|
||||
float fx = fish->getX();
|
||||
if (Random::floatInRange(0, 1) < BUBBLE_SPAWN_CHANCE) {
|
||||
addBubble(fx, fish->getY());
|
||||
}
|
||||
|
||||
if (fish->isOffScreen()) {
|
||||
it = fishes.erase(it);
|
||||
addFish();
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
waterline->draw();
|
||||
waterline->update();
|
||||
|
||||
static_cast<const Entity *>(ship.get())->draw(9);
|
||||
if (ship->isOffScreen())
|
||||
addShip();
|
||||
ship->update();
|
||||
|
||||
applyBackBuffer();
|
||||
}
|
||||
|
||||
void Aquarium::addBubble(size_t x, size_t y) {
|
||||
bubbles.emplace_back(std::make_unique<Bubble>(x, y));
|
||||
}
|
||||
|
||||
void Aquarium::addWaterline() { waterline = std::make_unique<Waterline>(); };
|
||||
|
||||
void Aquarium::addSeaweed() {
|
||||
seaweeds.emplace_back(std::make_unique<Seaweed>());
|
||||
}
|
||||
|
||||
void Aquarium::addCastle() { castle = std::make_unique<Castle>(); }
|
||||
|
||||
void Aquarium::addFish() { fishes.emplace_back(std::make_unique<Fish>()); }
|
||||
|
||||
void Aquarium::addShip() { ship = std::make_unique<Ship>(); }
|
||||
|
||||
void Aquarium::clearBackBuffer() {
|
||||
for (auto &row : backBuffer)
|
||||
void Aquarium::clearCurrentFrame() {
|
||||
for (auto &row : currentFrame) {
|
||||
std::fill(row.begin(), row.end(), Cell());
|
||||
}
|
||||
}
|
||||
|
||||
inline char fastToLower(char c) { return (c >= 'A' && c <= 'Z') ? c + 32 : c; }
|
||||
|
||||
inline bool fastIsUpper(char c) { return (c >= 'A' && c <= 'Z'); }
|
||||
|
||||
void Aquarium::drawToBackBuffer(int y, int x, int layer,
|
||||
const std::string &line,
|
||||
const std::string &colorLine) {
|
||||
void Aquarium::drawToFrame(int y, int x, const std::string &line,
|
||||
const std::string &colorLine) {
|
||||
if (y < 0 || y >= height)
|
||||
return;
|
||||
|
||||
@@ -187,89 +153,58 @@ void Aquarium::drawToBackBuffer(int y, int x, int layer,
|
||||
|
||||
const char ch = line[j];
|
||||
const char colorChar = colorLine[j];
|
||||
const bool isBold = fastIsUpper(static_cast<unsigned char>(colorChar));
|
||||
const bool isBold = (colorChar >= 'A' && colorChar <= 'Z');
|
||||
|
||||
Cell cell{
|
||||
ch,
|
||||
static_cast<char>(fastToLower(static_cast<unsigned char>(colorChar))),
|
||||
isBold};
|
||||
|
||||
auto &cellLayers = layeredMap[y][cx].layers;
|
||||
|
||||
bool replaced = false;
|
||||
for (auto &p : cellLayers) {
|
||||
if (p.first == layer) {
|
||||
p.second = cell;
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!replaced) {
|
||||
cellLayers.emplace_back(layer, cell);
|
||||
}
|
||||
|
||||
// Set back buffer to topmost layer (assume highest layer is last)
|
||||
if (!cellLayers.empty()) {
|
||||
backBuffer[y][cx] = cellLayers.back().second;
|
||||
}
|
||||
currentFrame[y][cx] = {
|
||||
ch, static_cast<char>(isBold ? colorChar + 32 : colorChar), isBold};
|
||||
}
|
||||
}
|
||||
|
||||
void Aquarium::removeFromBackBuffer(int y, int x, int layer,
|
||||
const std::string &line) {
|
||||
for (size_t j = 0; j < line.size(); ++j) {
|
||||
int cx = x + static_cast<int>(j);
|
||||
if (y < 0 || y >= height || cx < 0 || cx >= width)
|
||||
continue;
|
||||
|
||||
auto &cellLayers = layeredMap[y][cx].layers;
|
||||
|
||||
auto it = std::find_if(cellLayers.begin(), cellLayers.end(),
|
||||
[layer](const auto &p) { return p.first == layer; });
|
||||
|
||||
if (it != cellLayers.end())
|
||||
cellLayers.erase(it);
|
||||
|
||||
if (!cellLayers.empty())
|
||||
backBuffer[y][cx] = cellLayers.back().second;
|
||||
else
|
||||
backBuffer[y][cx] = Cell(); // Clear
|
||||
}
|
||||
void Aquarium::initColorLookup() {
|
||||
for (int i = 0; i < 256; ++i)
|
||||
colorLookup[i] = 8; // Default black
|
||||
colorLookup['r'] = 1;
|
||||
colorLookup['g'] = 2;
|
||||
colorLookup['y'] = 3;
|
||||
colorLookup['b'] = 4;
|
||||
colorLookup['m'] = 5;
|
||||
colorLookup['c'] = 6;
|
||||
colorLookup['w'] = 7;
|
||||
colorLookup['k'] = 8;
|
||||
}
|
||||
|
||||
void Aquarium::applyBackBuffer() {
|
||||
void Aquarium::renderToScreen() {
|
||||
for (int y = 0; y < height; ++y) {
|
||||
std::string rowStr;
|
||||
int lastPair = -1;
|
||||
bool lastBold = false;
|
||||
|
||||
move(y, 0);
|
||||
|
||||
for (int x = 0; x < width; ++x) {
|
||||
const Cell &newCell = backBuffer[y][x];
|
||||
Cell &oldCell = frontBuffer[y][x];
|
||||
const Cell &newCell = currentFrame[y][x];
|
||||
Cell &oldCell = previousFrame[y][x];
|
||||
|
||||
if (newCell != oldCell) {
|
||||
oldCell = newCell;
|
||||
move(y, x);
|
||||
|
||||
int pairId = colorLookup[static_cast<unsigned char>(newCell.colorChar)];
|
||||
bool bold = newCell.bold;
|
||||
|
||||
// Change attr if needed
|
||||
if (pairId != lastPair || bold != lastBold) {
|
||||
attrset(COLOR_PAIR(pairId) | (bold ? A_BOLD : A_NORMAL));
|
||||
lastPair = pairId;
|
||||
lastBold = bold;
|
||||
}
|
||||
|
||||
int colorPair =
|
||||
colorLookup[static_cast<unsigned char>(newCell.colorChar)];
|
||||
attrset(COLOR_PAIR(colorPair) | (newCell.bold ? A_BOLD : A_NORMAL));
|
||||
addch(newCell.ch);
|
||||
} else {
|
||||
// Still move the cursor to stay aligned
|
||||
move(y, x + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Aquarium::initColors() {
|
||||
if (has_colors()) {
|
||||
start_color();
|
||||
init_pair(1, COLOR_RED, COLOR_BLACK);
|
||||
init_pair(2, COLOR_GREEN, COLOR_BLACK);
|
||||
init_pair(3, COLOR_YELLOW, COLOR_BLACK);
|
||||
init_pair(4, COLOR_BLUE, COLOR_BLACK);
|
||||
init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
|
||||
init_pair(6, COLOR_CYAN, COLOR_BLACK);
|
||||
init_pair(7, COLOR_WHITE, COLOR_BLACK);
|
||||
init_pair(8, COLOR_BLACK, COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
Aquarium::~Aquarium() { endwin(); }
|
||||
|
||||
Reference in New Issue
Block a user