initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
build
|
||||||
|
bin
|
||||||
43
Makefile
Normal file
43
Makefile
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Compiler and flags
|
||||||
|
CXX = g++
|
||||||
|
CXXFLAGS = -std=c++17 -Wall -Wextra -O3
|
||||||
|
LDFLAGS = -lncurses -ltinfo
|
||||||
|
|
||||||
|
# Directories
|
||||||
|
SRC_DIR = src
|
||||||
|
OBJ_DIR = build
|
||||||
|
BIN_DIR = bin
|
||||||
|
|
||||||
|
# File extensions
|
||||||
|
SRC_EXT = cpp
|
||||||
|
OBJ_EXT = o
|
||||||
|
|
||||||
|
# Find all source files and generate object files
|
||||||
|
SOURCES = $(wildcard $(SRC_DIR)/*.$(SRC_EXT))
|
||||||
|
OBJECTS = $(SOURCES:$(SRC_DIR)/%.$(SRC_EXT)=$(OBJ_DIR)/%.$(OBJ_EXT))
|
||||||
|
|
||||||
|
# Output executable
|
||||||
|
EXEC = $(BIN_DIR)/fissh
|
||||||
|
|
||||||
|
# Default target: build everything
|
||||||
|
all: $(EXEC)
|
||||||
|
|
||||||
|
# Rule to link the object files into the final executable
|
||||||
|
$(EXEC): $(OBJECTS)
|
||||||
|
$(CXX) $(OBJECTS) -o $(EXEC) $(LDFLAGS)
|
||||||
|
|
||||||
|
# Rule to compile .cpp files into .o files
|
||||||
|
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.$(SRC_EXT)
|
||||||
|
@mkdir -p $(OBJ_DIR) # Make sure the obj dir exists
|
||||||
|
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
# Clean up the build directory
|
||||||
|
clean:
|
||||||
|
rm -rf $(OBJ_DIR) $(BIN_DIR)
|
||||||
|
|
||||||
|
# Run the program
|
||||||
|
run: $(EXEC)
|
||||||
|
./$(EXEC)
|
||||||
|
|
||||||
|
# Phony targets
|
||||||
|
.PHONY: all clean run
|
||||||
263
src/Aquarium.cpp
Normal file
263
src/Aquarium.cpp
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
#include "Aquarium.h"
|
||||||
|
#include "Random.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
int g_maxCells = 0;
|
||||||
|
Aquarium::Aquarium() {
|
||||||
|
initscr();
|
||||||
|
noecho();
|
||||||
|
cbreak();
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
short colorLookup[256];
|
||||||
|
void Aquarium::initColorLookup() {
|
||||||
|
for (int i = 0; i < 256; ++i)
|
||||||
|
colorLookup[i] = 8; // default to 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::resize() {
|
||||||
|
clear();
|
||||||
|
getmaxyx(stdscr, height, width);
|
||||||
|
|
||||||
|
if (g_maxCells && height * width > g_maxCells) {
|
||||||
|
endwin(); // Cleanly shut down ncurses
|
||||||
|
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));
|
||||||
|
|
||||||
|
fishes.clear();
|
||||||
|
bubbles.clear();
|
||||||
|
seaweeds.clear();
|
||||||
|
|
||||||
|
addWaterline();
|
||||||
|
addCastle();
|
||||||
|
for (int i = 0; i < width / 15; i++)
|
||||||
|
addSeaweed();
|
||||||
|
for (int i = 0; i < width * (height - 9) / 350; i++)
|
||||||
|
addFish();
|
||||||
|
}
|
||||||
|
|
||||||
|
Aquarium::~Aquarium() { endwin(); }
|
||||||
|
|
||||||
|
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->getY() < 9)
|
||||||
|
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();) { // use an iterator
|
||||||
|
auto &fish = *it;
|
||||||
|
fish->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 (fx > width || fx < -30) {
|
||||||
|
it = fishes.erase(it); // erase and update iterator
|
||||||
|
addFish();
|
||||||
|
} else {
|
||||||
|
++it; // only increment if not erasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waterline->draw();
|
||||||
|
waterline->update();
|
||||||
|
|
||||||
|
applyBackBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aquarium::addBubble(float x, float 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::clearBackBuffer() {
|
||||||
|
for (auto &row : backBuffer)
|
||||||
|
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) {
|
||||||
|
if (y < 0 || y >= height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const size_t len = std::min(line.size(), colorLine.size());
|
||||||
|
|
||||||
|
for (size_t j = 0; j < len; ++j) {
|
||||||
|
int cx = x + static_cast<int>(j);
|
||||||
|
if (cx < 0 || cx >= width)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char ch = line[j];
|
||||||
|
const char colorChar = colorLine[j];
|
||||||
|
const bool isBold = fastIsUpper(static_cast<unsigned char>(colorChar));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::applyBackBuffer() {
|
||||||
|
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];
|
||||||
|
|
||||||
|
if (newCell != oldCell) {
|
||||||
|
oldCell = newCell;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
addch(newCell.ch);
|
||||||
|
} else {
|
||||||
|
// Still move the cursor to stay aligned
|
||||||
|
move(y, x + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
96
src/Aquarium.h
Normal file
96
src/Aquarium.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Bubble.h"
|
||||||
|
#include "Castle.h"
|
||||||
|
#include "Fish.h"
|
||||||
|
#include "Seaweed.h"
|
||||||
|
#include "Waterline.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
extern int g_maxCells;
|
||||||
|
class Aquarium {
|
||||||
|
private:
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
|
||||||
|
struct Cell {
|
||||||
|
char ch = ' ';
|
||||||
|
char colorChar = 'k'; // Default to black
|
||||||
|
bool bold = false;
|
||||||
|
|
||||||
|
bool operator==(const Cell &other) const {
|
||||||
|
return ch == other.ch && colorChar == other.colorChar &&
|
||||||
|
bold == other.bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Cell &other) const { return !(*this == other); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LayeredCell {
|
||||||
|
std::vector<std::pair<int, Cell>> layers; // Sorted by layer (ascending)
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::vector<Cell>> frontBuffer;
|
||||||
|
std::vector<std::vector<Cell>> backBuffer;
|
||||||
|
std::vector<std::vector<LayeredCell>> layeredMap;
|
||||||
|
|
||||||
|
inline static const std::unordered_map<char, int> colorCharToPair = {
|
||||||
|
{'r', 1}, // Red
|
||||||
|
{'g', 2}, // Green
|
||||||
|
{'y', 3}, // Yellow
|
||||||
|
{'b', 4}, // Blue
|
||||||
|
{'m', 5}, // Magenta
|
||||||
|
{'c', 6}, // Cyan
|
||||||
|
{'w', 7}, // White
|
||||||
|
{'k', 8} // Black
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void applyColorAttr(char colorChar, bool enable) const {
|
||||||
|
bool bold = std::isupper(colorChar);
|
||||||
|
char lowerChar = std::tolower(static_cast<unsigned char>(colorChar));
|
||||||
|
|
||||||
|
auto it = colorCharToPair.find(lowerChar);
|
||||||
|
if (it != colorCharToPair.end()) {
|
||||||
|
int colorPairId = it->second;
|
||||||
|
if (enable) {
|
||||||
|
attron(COLOR_PAIR(colorPairId));
|
||||||
|
if (bold)
|
||||||
|
attron(A_BOLD);
|
||||||
|
} else {
|
||||||
|
attroff(COLOR_PAIR(colorPairId));
|
||||||
|
if (bold)
|
||||||
|
attroff(A_BOLD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<Fish>> fishes;
|
||||||
|
std::vector<std::unique_ptr<Bubble>> bubbles;
|
||||||
|
std::vector<std::unique_ptr<Seaweed>> seaweeds;
|
||||||
|
std::unique_ptr<Waterline> waterline;
|
||||||
|
std::unique_ptr<Castle> castle;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Aquarium();
|
||||||
|
~Aquarium();
|
||||||
|
static Aquarium &getInstance() {
|
||||||
|
static Aquarium instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
[[nodiscard]] int getWidth() const { return width; }
|
||||||
|
[[nodiscard]] int getHeight() const { return height; }
|
||||||
|
void addFish();
|
||||||
|
void addBubble(float x, float y);
|
||||||
|
void addSeaweed();
|
||||||
|
void addWaterline();
|
||||||
|
void addCastle();
|
||||||
|
void redraw();
|
||||||
|
void initColors();
|
||||||
|
void initColorLookup();
|
||||||
|
void resize();
|
||||||
|
void clearBackBuffer();
|
||||||
|
void drawToBackBuffer(int y, int x, int layer, const std::string &line,
|
||||||
|
const std::string &colorLine);
|
||||||
|
void removeFromBackBuffer(int y, int x, int layer, const std::string &line);
|
||||||
|
void applyBackBuffer();
|
||||||
|
};
|
||||||
26
src/Bubble.cpp
Normal file
26
src/Bubble.cpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#include "Bubble.h"
|
||||||
|
#include "Aquarium.h"
|
||||||
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
Bubble::Bubble(float x, float y) : Entity() {
|
||||||
|
this->x = x;
|
||||||
|
this->y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bubble::update() { y -= 1; }
|
||||||
|
|
||||||
|
void Bubble::draw() {
|
||||||
|
lifetime++;
|
||||||
|
|
||||||
|
// Determine the frame based on lifetime
|
||||||
|
int frameNumber = lifetime / 9;
|
||||||
|
if (frameNumber > 2)
|
||||||
|
frameNumber = 2;
|
||||||
|
|
||||||
|
char frame = bubbleChars[frameNumber];
|
||||||
|
|
||||||
|
std::string line(1, frame);
|
||||||
|
std::string colorLine(1, 'c');
|
||||||
|
|
||||||
|
Aquarium::getInstance().drawToBackBuffer(y, x, 0, line, colorLine);
|
||||||
|
}
|
||||||
14
src/Bubble.h
Normal file
14
src/Bubble.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Entity.h"
|
||||||
|
|
||||||
|
class Bubble : public Entity {
|
||||||
|
private:
|
||||||
|
static constexpr char bubbleChars[3] = {'.', 'o', 'O'};
|
||||||
|
int lifetime = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Bubble(float x, float y);
|
||||||
|
|
||||||
|
void update();
|
||||||
|
void draw();
|
||||||
|
};
|
||||||
52
src/Castle.cpp
Normal file
52
src/Castle.cpp
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#include "Castle.h"
|
||||||
|
#include "Aquarium.h"
|
||||||
|
|
||||||
|
const std::vector<std::string> Castle::image = {
|
||||||
|
R"( T~~ )", R"( | )",
|
||||||
|
R"( /^\ )", R"( / \ )",
|
||||||
|
R"( _ _ _ / \ _ _ _ )", R"([ ]_[ ]_[ ]/ _ _ \[ ]_[ ]_[ ])",
|
||||||
|
R"(|_=__-_ =_|_[ ]_[ ]_|_=-___-__|)", R"( | _- = | =_ = _ |= _= | )",
|
||||||
|
R"( |= -[] |- = _ = |_-=_[] | )", R"( | =_ |= - ___ | =_ = | )",
|
||||||
|
R"( |= []- |- /| |\ |=_ =[] | )", R"( |- =_ | =| | | | |- = - | )",
|
||||||
|
R"( |_______|__|_|_|_|__|_______| )"};
|
||||||
|
|
||||||
|
const std::vector<std::string> Castle::mask = {
|
||||||
|
R"( RR )", R"( )",
|
||||||
|
R"( yyy )", R"( y y )",
|
||||||
|
R"( y y )", R"( y y )",
|
||||||
|
R"( )", R"( )",
|
||||||
|
R"( )", R"( yyy )",
|
||||||
|
R"( yy yy )", R"( y y y y )",
|
||||||
|
R"( yyyyyyy )"};
|
||||||
|
|
||||||
|
Castle::Castle() : Entity() {
|
||||||
|
x = Aquarium::getInstance().getWidth() - 32;
|
||||||
|
y = Aquarium::getInstance().getHeight() - 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Castle::draw() {
|
||||||
|
for (size_t i = 0; i < image.size(); ++i) {
|
||||||
|
std::string currentLine;
|
||||||
|
std::string colorLine;
|
||||||
|
|
||||||
|
// Iterate over characters in the current line
|
||||||
|
for (size_t j = 0; j < image[i].size(); ++j) {
|
||||||
|
char ch = image[i][j];
|
||||||
|
if (ch == '?')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char colorChar = 'K'; // default to black
|
||||||
|
if (i < mask.size() && j < mask[i].size() && mask[i][j] != ' ')
|
||||||
|
colorChar = mask[i][j];
|
||||||
|
|
||||||
|
// Store the character and color
|
||||||
|
currentLine += ch;
|
||||||
|
colorLine += colorChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aquarium::getInstance().drawToBackBuffer(y + i, x, 0, currentLine,
|
||||||
|
colorLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Castle::update() { return; }
|
||||||
14
src/Castle.h
Normal file
14
src/Castle.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Entity.h"
|
||||||
|
|
||||||
|
class Castle : public Entity {
|
||||||
|
private:
|
||||||
|
static const std::vector<std::string> image;
|
||||||
|
static const std::vector<std::string> mask;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Castle();
|
||||||
|
|
||||||
|
void draw();
|
||||||
|
void update();
|
||||||
|
};
|
||||||
19
src/Entity.h
Normal file
19
src/Entity.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cctype>
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Entity {
|
||||||
|
protected:
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Entity() : x(0), y(0) {}
|
||||||
|
virtual ~Entity() {}
|
||||||
|
|
||||||
|
inline float getX() const { return x; }
|
||||||
|
inline float getY() const { return y; }
|
||||||
|
};
|
||||||
105
src/Fish.cpp
Normal file
105
src/Fish.cpp
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#include "Fish.h"
|
||||||
|
#include "Aquarium.h"
|
||||||
|
#include "FishAssets.h"
|
||||||
|
#include "Random.h"
|
||||||
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
Fish::Fish() : Fish(getRandomFishPair()) {}
|
||||||
|
|
||||||
|
Fish::Fish(const FishAssetPair &pair)
|
||||||
|
: Entity(),
|
||||||
|
speed((pair.index % 2 == 0) ? Random::floatInRange(0.25, 2.25)
|
||||||
|
: -Random::floatInRange(0.25, 2.25)),
|
||||||
|
image(*pair.image), refMask(*pair.mask) {
|
||||||
|
y = Random::intInRange(image.size() + 6,
|
||||||
|
Aquarium::getInstance().getHeight() - image.size());
|
||||||
|
|
||||||
|
x = (speed < 0) ? Aquarium::getInstance().getWidth() : -20;
|
||||||
|
|
||||||
|
randomizeMask();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::vector<std::string>, std::vector<std::string>>>
|
||||||
|
Fish::fishPairs;
|
||||||
|
|
||||||
|
bool Fish::initialized = false;
|
||||||
|
|
||||||
|
void Fish::initializeFishAssets() {
|
||||||
|
if (initialized)
|
||||||
|
return;
|
||||||
|
fishPairs = fishAssetPairs;
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fish::randomizeMask() {
|
||||||
|
// Create a mapping of digit to color
|
||||||
|
std::unordered_map<char, char> colorMap;
|
||||||
|
mask = refMask;
|
||||||
|
|
||||||
|
// For each digit 1-9, assign a random color
|
||||||
|
for (char digit = '1'; digit <= '9'; ++digit) {
|
||||||
|
colorMap[digit] =
|
||||||
|
availableColors[Random::intInRange(0, availableColors.size() - 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special case: '4' always maps to 'W'
|
||||||
|
colorMap['4'] = 'W';
|
||||||
|
|
||||||
|
// Apply the color mapping to each character in the mask
|
||||||
|
for (auto &line : mask) {
|
||||||
|
for (auto &ch : line) {
|
||||||
|
if (ch >= '1' && ch <= '9') {
|
||||||
|
ch = colorMap[ch];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Fish::FishAssetPair Fish::getRandomFishPair() {
|
||||||
|
if (!initialized)
|
||||||
|
initializeFishAssets();
|
||||||
|
|
||||||
|
int index = Random::intInRange(0, fishPairs.size() - 1);
|
||||||
|
|
||||||
|
return FishAssetPair{index, &fishPairs[index].first,
|
||||||
|
&fishPairs[index].second};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fish::update() { x += speed; }
|
||||||
|
|
||||||
|
void Fish::draw(int layer) {
|
||||||
|
Aquarium &aq = Aquarium::getInstance();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < image.size(); ++i) {
|
||||||
|
const std::string &row = image[i];
|
||||||
|
const std::string &maskRow = (i < mask.size()) ? mask[i] : "";
|
||||||
|
|
||||||
|
int baseY = y + static_cast<int>(i);
|
||||||
|
int cursorX = static_cast<int>(x);
|
||||||
|
std::string currentSegment;
|
||||||
|
std::string currentColors;
|
||||||
|
|
||||||
|
for (size_t j = 0; j < row.size(); ++j) {
|
||||||
|
char ch = row[j];
|
||||||
|
|
||||||
|
if (ch == '?') {
|
||||||
|
if (!currentSegment.empty()) {
|
||||||
|
aq.drawToBackBuffer(baseY, cursorX, layer, currentSegment,
|
||||||
|
currentColors);
|
||||||
|
cursorX += currentSegment.size();
|
||||||
|
currentSegment.clear();
|
||||||
|
currentColors.clear();
|
||||||
|
}
|
||||||
|
cursorX += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSegment.push_back(ch);
|
||||||
|
currentColors.push_back((j < maskRow.size()) ? maskRow[j] : 'k');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentSegment.empty()) {
|
||||||
|
aq.drawToBackBuffer(baseY, cursorX, layer, currentSegment, currentColors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/Fish.h
Normal file
37
src/Fish.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Entity.h"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
class Fish : public Entity {
|
||||||
|
private:
|
||||||
|
struct FishAssetPair {
|
||||||
|
int index;
|
||||||
|
const std::vector<std::string> *image;
|
||||||
|
const std::vector<std::string> *mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
Fish(const FishAssetPair &pair);
|
||||||
|
|
||||||
|
static std::vector<
|
||||||
|
std::pair<std::vector<std::string>, std::vector<std::string>>>
|
||||||
|
fishPairs;
|
||||||
|
static bool initialized;
|
||||||
|
|
||||||
|
const std::vector<std::string> ℑ
|
||||||
|
const std::vector<std::string> &refMask;
|
||||||
|
std::vector<std::string> mask;
|
||||||
|
|
||||||
|
static constexpr std::array<char, 12> availableColors = {
|
||||||
|
'c', 'C', 'r', 'R', 'y', 'Y', 'b', 'B', 'g', 'G', 'm', 'M'};
|
||||||
|
|
||||||
|
const float speed;
|
||||||
|
static FishAssetPair getRandomFishPair();
|
||||||
|
|
||||||
|
static void initializeFishAssets();
|
||||||
|
void randomizeMask();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Fish();
|
||||||
|
void update();
|
||||||
|
void draw(int layer);
|
||||||
|
};
|
||||||
238
src/FishAssets.h
Normal file
238
src/FishAssets.h
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using FishAssetPairRaw =
|
||||||
|
std::pair<std::vector<std::string>, std::vector<std::string>>;
|
||||||
|
|
||||||
|
inline std::vector<FishAssetPairRaw> fishAssetPairs = {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
R"(???\)",
|
||||||
|
R"(??/ \)",
|
||||||
|
R"(>=_('>)",
|
||||||
|
R"(??\_/)",
|
||||||
|
R"(???/)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
R"( 1)",
|
||||||
|
R"( 1 1)",
|
||||||
|
R"(663745)",
|
||||||
|
R"( 111)",
|
||||||
|
R"( 3)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
R"(??/)",
|
||||||
|
R"(?/ \)",
|
||||||
|
R"(<')_=<)",
|
||||||
|
R"(?\_/)",
|
||||||
|
R"(??\)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
R"( 2)",
|
||||||
|
R"( 111)",
|
||||||
|
R"(547366)",
|
||||||
|
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"(?/' `. /)",
|
||||||
|
R"(<6 ) >{{)",
|
||||||
|
R"(?`. ,' \)",
|
||||||
|
R"(???\{)",
|
||||||
|
R"(????`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
R"( 2)",
|
||||||
|
R"( 22)",
|
||||||
|
R"( 11 11 6)",
|
||||||
|
R"(54 7 166)",
|
||||||
|
R"( 11 11 6)",
|
||||||
|
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"(/o \\ "-.' /)",
|
||||||
|
R"(\ // _.-'._\)",
|
||||||
|
R"(?`"\)--"`???????)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
R"( 22222 )",
|
||||||
|
R"( 112111121 66)",
|
||||||
|
R"(14 77 1116 6)",
|
||||||
|
R"(1 77 1111666)",
|
||||||
|
R"( 11331111 )"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
R"(??__)",
|
||||||
|
R"(><_'>)",
|
||||||
|
R"(???')"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
R"( 11)",
|
||||||
|
R"(61145)",
|
||||||
|
R"( 3)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
R"(?__)",
|
||||||
|
R"(<'_><)",
|
||||||
|
R"(?`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
R"( 11)",
|
||||||
|
R"(54116)",
|
||||||
|
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"( 22222 )",
|
||||||
|
R"( 211111222 )",
|
||||||
|
R"( 1111 7 11116666)",
|
||||||
|
R"(1 4 7777 1 6 )",
|
||||||
|
R"( 1111 7711111126666)",
|
||||||
|
R"( 333 222222 )"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
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"(?,-' `-:.,-'/)",
|
||||||
|
R"(> o )<) _ ( )",
|
||||||
|
R"(?`-._ _.:'?`-.\)",
|
||||||
|
R"(?????``?\;??????)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
R"( 22 )",
|
||||||
|
R"( 1212 )",
|
||||||
|
R"( 111 11116666)",
|
||||||
|
R"(1 4 777 1 6 )",
|
||||||
|
R"( 1111 1111 6666)",
|
||||||
|
R"( 33 22 )",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
R"(_?????????_.*"\??????)",
|
||||||
|
R"(\'-._..-*` `'*-.??)",
|
||||||
|
R"(?) , (( o >)",
|
||||||
|
R"(/.`"*--.__)_.`_.-*`??)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
R"(6 11222 )",
|
||||||
|
R"(6661111111 11111 )",
|
||||||
|
R"( 6 3 77 4 1)",
|
||||||
|
R"(6661111111311311111 )",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
R"(??????/"*._?????????_)",
|
||||||
|
R"(??.-*'` `*-.._.-'/)",
|
||||||
|
R"(< o )) , ( )",
|
||||||
|
R"(??`*-._`._(__.--*"`.\)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
R"( 22211 6)",
|
||||||
|
R"( 11111 1111111666)",
|
||||||
|
R"(1 4 77 3 6 )",
|
||||||
|
R"( 1111131131111111666)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
19
src/Random.h
Normal file
19
src/Random.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <random>
|
||||||
|
class Random {
|
||||||
|
public:
|
||||||
|
static std::mt19937 &engine() {
|
||||||
|
static std::mt19937 gen(std::random_device{}());
|
||||||
|
return gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intInRange(int min, int max) {
|
||||||
|
std::uniform_int_distribution<int> dist(min, max);
|
||||||
|
return dist(engine());
|
||||||
|
}
|
||||||
|
|
||||||
|
static float floatInRange(float min, float max) {
|
||||||
|
std::uniform_real_distribution<float> dist(min, max);
|
||||||
|
return dist(engine());
|
||||||
|
}
|
||||||
|
};
|
||||||
41
src/Seaweed.cpp
Normal file
41
src/Seaweed.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include "Seaweed.h"
|
||||||
|
#include "Aquarium.h"
|
||||||
|
#include "Random.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
Seaweed::Seaweed() : Entity() {
|
||||||
|
speed = Random::floatInRange(0.1f, 0.3f);
|
||||||
|
height = Random::intInRange(SEAWEED_MIN_HEIGHT, SEAWEED_MAX_HEIGHT);
|
||||||
|
x = Random::intInRange(0, Aquarium::getInstance().getWidth());
|
||||||
|
y = Aquarium::getInstance().getHeight() - 1;
|
||||||
|
lifetime = Random::intInRange(SEAWEED_MIN_LIFETIME, SEAWEED_MAX_LIFETIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Seaweed::update() {
|
||||||
|
frame += speed;
|
||||||
|
if (frame >= 1.0f) {
|
||||||
|
std::swap(pattern[0], pattern[1]);
|
||||||
|
frame -= 1.0f;
|
||||||
|
}
|
||||||
|
--lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Seaweed::draw() {
|
||||||
|
std::string line;
|
||||||
|
std::string colorLine;
|
||||||
|
|
||||||
|
for (int i = 0; i < height; ++i) {
|
||||||
|
line.clear();
|
||||||
|
char ch = (pattern[i % 2] == '(') ? '(' : ')';
|
||||||
|
|
||||||
|
// Adjust x and y based on the pattern
|
||||||
|
int drawX = (ch == '(') ? x : x + 1;
|
||||||
|
int drawY = y - i;
|
||||||
|
|
||||||
|
line.push_back(ch);
|
||||||
|
colorLine.push_back('g');
|
||||||
|
|
||||||
|
Aquarium::getInstance().drawToBackBuffer(drawY, drawX, 0, line, colorLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Seaweed.h
Normal file
18
src/Seaweed.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Entity.h"
|
||||||
|
|
||||||
|
class Seaweed : public Entity {
|
||||||
|
private:
|
||||||
|
char pattern[2] = {'(', ')'};
|
||||||
|
|
||||||
|
float speed, frame = 0;
|
||||||
|
int lifetime;
|
||||||
|
int height;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Seaweed();
|
||||||
|
|
||||||
|
int getLifetime() { return lifetime; };
|
||||||
|
void update();
|
||||||
|
void draw();
|
||||||
|
};
|
||||||
53
src/Waterline.cpp
Normal file
53
src/Waterline.cpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#include "Waterline.h"
|
||||||
|
#include "Aquarium.h"
|
||||||
|
#include "Random.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
Waterline::Waterline() {
|
||||||
|
std::vector<std::string> baseShape = {
|
||||||
|
"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", "^^^^ ^^^ ^^^ ^^^ ^^^^ ",
|
||||||
|
"^^^^ ^^^^ ^^^ ^^ ", "^^ ^^^^ ^^^ ^^^^^^ "};
|
||||||
|
|
||||||
|
int seg_len = baseShape[0].size();
|
||||||
|
int repeat = Aquarium::getInstance().getWidth() / seg_len + 1;
|
||||||
|
|
||||||
|
for (auto &line : baseShape) {
|
||||||
|
std::string original = line;
|
||||||
|
while (line.size() < Aquarium::getInstance().getWidth()) {
|
||||||
|
line += original;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shape = std::move(baseShape);
|
||||||
|
y = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Waterline::draw() {
|
||||||
|
for (size_t i = 0; i < shape.size(); ++i) {
|
||||||
|
Aquarium::getInstance().drawToBackBuffer(i + y, x, 0, shape[i],
|
||||||
|
std::string(shape[i].size(), 'c'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Waterline::update() {
|
||||||
|
for (size_t i = 1; i < shape.size(); ++i) {
|
||||||
|
// Probability increases with line index (later lines = higher chance)
|
||||||
|
float chance = static_cast<float>(i) / shape.size();
|
||||||
|
if (Random::floatInRange(0.0f, 1.0f) < chance * (1.0f / WAVE_MOVE_CHANCE)) {
|
||||||
|
int direction = Random::intInRange(0, 1) == 0 ? -1 : 1;
|
||||||
|
shiftString(shape[i], direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Waterline::shiftString(std::string &str, int direction) {
|
||||||
|
if (str.empty() || (direction != 1 && direction != -1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (direction == 1) {
|
||||||
|
std::rotate(str.rbegin(), str.rbegin() + 1, str.rend());
|
||||||
|
} else {
|
||||||
|
std::rotate(str.begin(), str.begin() + 1, str.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/Waterline.h
Normal file
13
src/Waterline.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Entity.h"
|
||||||
|
|
||||||
|
class Waterline : public Entity {
|
||||||
|
private:
|
||||||
|
std::vector<std::string> shape;
|
||||||
|
void shiftString(std::string &, int direction);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Waterline();
|
||||||
|
void draw();
|
||||||
|
void update();
|
||||||
|
};
|
||||||
8
src/defs.h
Normal file
8
src/defs.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define BUBBLE_SPAWN_CHANCE 0.02f
|
||||||
|
#define SEAWEED_MIN_HEIGHT 3
|
||||||
|
#define SEAWEED_MAX_HEIGHT 7
|
||||||
|
#define SEAWEED_MIN_LIFETIME 5000
|
||||||
|
#define SEAWEED_MAX_LIFETIME 15000
|
||||||
|
#define WAVE_MOVE_CHANCE 15
|
||||||
32
src/main.cpp
Normal file
32
src/main.cpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#include "Aquarium.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "r:")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'r':
|
||||||
|
g_maxCells = std::atoi(optarg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Usage: %s [-r max_cells]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Aquarium &aquarium = Aquarium::getInstance();
|
||||||
|
aquarium.resize();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
aquarium.redraw();
|
||||||
|
int ch = getch();
|
||||||
|
if (ch == 'q')
|
||||||
|
break;
|
||||||
|
if (ch == 'r')
|
||||||
|
aquarium.resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user