mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 23:27:49 +01:00
craptrix 2
This commit is contained in:
@@ -27,7 +27,8 @@
|
||||
namespace cfg {
|
||||
constexpr int BoardW = 10;
|
||||
constexpr int BoardH = 20;
|
||||
constexpr int CellPx = 12; // 10x20 -> 120x240
|
||||
// Reduce CellPx to 11 (was 12) so BoardH * CellPx = 220, leaving 10px vertical margin top & bottom (240-220)/2
|
||||
constexpr int CellPx = 11; // creates vertical margins when centered
|
||||
constexpr int FrameW = 400;
|
||||
constexpr int FrameH = 240;
|
||||
|
||||
@@ -188,7 +189,7 @@ public:
|
||||
if (cell_of(TET[pidx], rot, xx, yy)) {
|
||||
int x = px + xx, y = py + yy;
|
||||
if (x >= 0 && x < cfg::BoardW && y >= 0 && y < cfg::BoardH)
|
||||
set(x, y, 1);
|
||||
set(x, y, static_cast<uint8_t>(pidx + 1)); // store piece id (1..7)
|
||||
}
|
||||
}
|
||||
int clearLines() {
|
||||
@@ -305,150 +306,261 @@ const uint8_t Font5x7::data[22][5] = {
|
||||
class Renderer {
|
||||
public:
|
||||
Renderer(IFramebuffer& fb) : fb(fb) {
|
||||
bw = cfg::BoardW * cfg::CellPx; // 120
|
||||
bh = cfg::BoardH * cfg::CellPx; // 240
|
||||
ox = (fb.width() - bw) / 2; // 140
|
||||
oy = (fb.height() - bh) / 2; // 0
|
||||
bw = cfg::BoardW * cfg::CellPx; // 10 * 11 = 110
|
||||
bh = cfg::BoardH * cfg::CellPx; // 20 * 11 = 220 (leaves 10px margins vertically)
|
||||
ox = (fb.width() - bw) / 2; // centered horizontally
|
||||
oy = (fb.height() - bh) / 2; // centered vertically with margin
|
||||
}
|
||||
|
||||
void render(const Board& b, int px, int py, int prot, int pidx, int score, int level, int lines, int nextIdx,
|
||||
bool ghost) {
|
||||
fb.clear(false); // white bg (off)
|
||||
fb.clear(false); // white background
|
||||
|
||||
// playfield frame
|
||||
// Outer playfield frame
|
||||
rect(ox - 2, oy - 2, bw + 4, bh + 4, true);
|
||||
|
||||
// settled
|
||||
// Draw settled cells (always full square borders for consistent look)
|
||||
for (int y = 0; y < cfg::BoardH; ++y)
|
||||
for (int x = 0; x < cfg::BoardW; ++x)
|
||||
if (b.get(x, y))
|
||||
drawCell(x, y, true);
|
||||
for (int x = 0; x < cfg::BoardW; ++x) {
|
||||
int v = b.get(x, y);
|
||||
if (v)
|
||||
drawCellFull(x, y, v, false);
|
||||
}
|
||||
|
||||
// ghost
|
||||
// Ghost (classic outline per cell) BEFORE active so active isn't obscured
|
||||
if (ghost)
|
||||
drawGhost(b, px, py, prot, pidx);
|
||||
|
||||
// active
|
||||
// Active piece overlay
|
||||
for (int yy = 0; yy < 4; ++yy)
|
||||
for (int xx = 0; xx < 4; ++xx)
|
||||
if (cell_of(TET[pidx], prot, xx, yy)) {
|
||||
int gx = px + xx, gy = py + yy;
|
||||
if (gx >= 0 && gx < cfg::BoardW && gy >= 0 && gy < cfg::BoardH)
|
||||
drawCell(gx, gy, true);
|
||||
drawCellFull(gx, gy, pidx + 1, true);
|
||||
}
|
||||
|
||||
// HUD (right side)
|
||||
// HUD
|
||||
int hudX = ox + bw + 16;
|
||||
int y = oy + 9; // +1 padding to avoid 1px visual cutoff
|
||||
drawLabel(hudX, y, "SCORE");
|
||||
y += 12;
|
||||
drawNumber(hudX, y, score);
|
||||
y += 19;
|
||||
drawLabel(hudX, y, "LEVEL");
|
||||
y += 12;
|
||||
drawNumber(hudX, y, level);
|
||||
y += 19;
|
||||
drawLabel(hudX, y, "LINES");
|
||||
y += 12;
|
||||
drawNumber(hudX, y, lines);
|
||||
y += 19;
|
||||
drawLabel(hudX, y, "NEXT");
|
||||
y += 7;
|
||||
int yHUD = oy + 9;
|
||||
drawLabel(hudX, yHUD, "SCORE"); yHUD += 12; drawNumber(hudX, yHUD, score); yHUD += 19;
|
||||
drawLabel(hudX, yHUD, "LEVEL"); yHUD += 12; drawNumber(hudX, yHUD, level); yHUD += 19;
|
||||
drawLabel(hudX, yHUD, "LINES"); yHUD += 12; drawNumber(hudX, yHUD, lines); yHUD += 19;
|
||||
drawLabel(hudX, yHUD, "NEXT"); yHUD += 7;
|
||||
|
||||
const int p = cfg::CellPx;
|
||||
const int nx = hudX, ny = y + 4;
|
||||
const int nx = hudX, ny = yHUD + 4;
|
||||
rect(nx - 2, ny - 2, 4 * p + 4, 4 * p + 4, true);
|
||||
for (int yy = 0; yy < 4; ++yy)
|
||||
for (int xx = 0; xx < 4; ++xx)
|
||||
if (cell_of(TET[nextIdx], 0, xx, yy))
|
||||
fillRect(nx + xx * p, ny + yy * p, p, p, true);
|
||||
|
||||
// removed surrounding HUD border rectangle that caused panel outline
|
||||
// rect(hudX - 8, oy, cfg::FrameW - (hudX - 8) - 8, cfg::FrameH - oy * 2, true);
|
||||
drawCellFullPreview(nx + xx * p, ny + yy * p, nextIdx + 1);
|
||||
|
||||
DispTools::get().draw_to_display();
|
||||
}
|
||||
|
||||
// Consistent full bordered cell drawing (square regardless of neighbors)
|
||||
void drawCellFull(int cx, int cy, int type, bool active) {
|
||||
int p = cfg::CellPx;
|
||||
int x0 = ox + cx * p;
|
||||
int y0 = oy + cy * p;
|
||||
rect(x0, y0, p, p, true); // outer border
|
||||
int ix0 = x0 + 1, iy0 = y0 + 1;
|
||||
int w = p - 2, h = p - 2;
|
||||
if (w <= 0 || h <= 0) return;
|
||||
for (int yy = 0; yy < h; ++yy)
|
||||
for (int xx = 0; xx < w; ++xx)
|
||||
if (patternOn(type, w, h, xx, yy))
|
||||
putPixel(ix0 + xx, iy0 + yy, true);
|
||||
if (active && p >= 8)
|
||||
rect(x0 + 2, y0 + 2, p - 4, p - 4, true); // subtle highlight
|
||||
}
|
||||
|
||||
// Preview cell
|
||||
void drawCellFullPreview(int x0, int y0, int type) {
|
||||
int p = cfg::CellPx;
|
||||
rect(x0, y0, p, p, true);
|
||||
int ix0 = x0 + 1, iy0 = y0 + 1, w = p - 2, h = p - 2;
|
||||
if (w <= 0 || h <= 0) return;
|
||||
for (int yy = 0; yy < h; ++yy)
|
||||
for (int xx = 0; xx < w; ++xx)
|
||||
if (patternOn(type, w, h, xx, yy))
|
||||
putPixel(ix0 + xx, iy0 + yy, true);
|
||||
}
|
||||
|
||||
private:
|
||||
IFramebuffer& fb;
|
||||
int ox = 0, oy = 0, bw = 0, bh = 0;
|
||||
|
||||
// Pattern helper: returns true if pixel (xx,yy) inside w x h interior should be filled for piece type
|
||||
bool patternOn(int type, int w, int h, int xx, int yy) const {
|
||||
switch (type) {
|
||||
case 1: { // I — symmetric vertical stripes (centered for even widths)
|
||||
int spacing = 3;
|
||||
// center stripes by choosing an offset that balances margins
|
||||
int offset = ((w - 1) % spacing) / 2; // distributes leftover space equally
|
||||
return ((xx - offset) % spacing) == 0;
|
||||
}
|
||||
case 2: return (yy < h / 2) || (xx == 0); // J
|
||||
case 3: return (yy >= h / 2) || (xx == w - 1); // L
|
||||
case 4: return (((xx + yy) & 1) == 0); // O
|
||||
case 5: return (((xx + yy) % 3) == 0); // S
|
||||
case 6: return ((((xx - yy) % 3) + 3) % 3 == 0); // Z
|
||||
case 7: { // T — centered diamond (handles even dimensions without bias)
|
||||
bool evenW = (w % 2) == 0;
|
||||
bool evenH = (h % 2) == 0;
|
||||
int dx, dy, radius;
|
||||
if (evenW) {
|
||||
int midLx = w / 2 - 1;
|
||||
int midRx = w / 2;
|
||||
dx = std::min(std::abs(xx - midLx), std::abs(xx - midRx));
|
||||
} else {
|
||||
int mid = (w - 1) / 2;
|
||||
dx = std::abs(xx - mid);
|
||||
}
|
||||
if (evenH) {
|
||||
int midLy = h / 2 - 1;
|
||||
int midRy = h / 2;
|
||||
dy = std::min(std::abs(yy - midLy), std::abs(yy - midRy));
|
||||
} else {
|
||||
int mid = (h - 1) / 2;
|
||||
dy = std::abs(yy - mid);
|
||||
}
|
||||
if (evenW) {
|
||||
radius = std::max(1, w / 2 - 2);
|
||||
} else {
|
||||
radius = std::max(1, (w - 1) / 2 - 1);
|
||||
}
|
||||
return (dx + dy) <= radius;
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void putPixel(int x, int y, bool on) { fb.drawPixel(x, y, on); }
|
||||
void hline(int x, int y, int w, bool on) {
|
||||
for (int i = 0; i < w; ++i)
|
||||
putPixel(x + i, y, on);
|
||||
}
|
||||
void vline(int x, int y, int h, bool on) {
|
||||
for (int i = 0; i < h; ++i)
|
||||
putPixel(x, y + i, on);
|
||||
}
|
||||
void hline(int x, int y, int w, bool on) { for (int i = 0; i < w; ++i) putPixel(x + i, y, on); }
|
||||
void vline(int x, int y, int h, bool on) { for (int i = 0; i < h; ++i) putPixel(x, y + i, on); }
|
||||
void rect(int x, int y, int w, int h, bool on) {
|
||||
hline(x, y, w, on);
|
||||
hline(x, y + h - 1, w, on);
|
||||
vline(x, y, h, on);
|
||||
vline(x + w - 1, y, h, on);
|
||||
}
|
||||
void fillRect(int x, int y, int w, int h, bool on) {
|
||||
for (int yy = 0; yy < h; ++yy)
|
||||
for (int xx = 0; xx < w; ++xx)
|
||||
putPixel(x + xx, y + yy, on);
|
||||
}
|
||||
|
||||
void drawCell(int cx, int cy, bool on) {
|
||||
int x0 = ox + cx * cfg::CellPx;
|
||||
int y0 = oy + cy * cfg::CellPx;
|
||||
fillRect(x0, y0, cfg::CellPx, cfg::CellPx, on);
|
||||
}
|
||||
void dashedH(int x, int y, int w, bool on) { for (int i = 0; i < w; ++i) if ((i & 1) == 0) putPixel(x + i, y, on); }
|
||||
void dashedV(int x, int y, int h, bool on) { for (int i = 0; i < h; ++i) if ((i & 1) == 0) putPixel(x, y + i, on); }
|
||||
|
||||
void drawGhost(const Board& b, int px, int py, int prot, int pidx) {
|
||||
// Determine landing position (same as before)
|
||||
int gy = py;
|
||||
while (true) {
|
||||
bool col = false;
|
||||
for (int yy = 0; yy < 4; ++yy)
|
||||
for (int yy = 0; yy < 4 && !col; ++yy)
|
||||
for (int xx = 0; xx < 4; ++xx)
|
||||
if (cell_of(TET[pidx], prot, xx, yy)) {
|
||||
int nx = px + xx, ny = gy + yy + 1;
|
||||
if (ny >= cfg::BoardH || (ny >= 0 && nx >= 0 && nx < cfg::BoardW && b.get(nx, ny)))
|
||||
col = true;
|
||||
if (ny >= cfg::BoardH || (ny >= 0 && nx >= 0 && nx < cfg::BoardW && b.get(nx, ny))) { col = true; break; }
|
||||
}
|
||||
if (col)
|
||||
break;
|
||||
if (col) break;
|
||||
++gy;
|
||||
}
|
||||
// Build occupancy mask of the piece (4x4 local)
|
||||
bool occ[4][4] = {};
|
||||
int minCol = 4, maxCol = -1, minRow = 4, maxRow = -1;
|
||||
for (int yy = 0; yy < 4; ++yy)
|
||||
for (int xx = 0; xx < 4; ++xx)
|
||||
if (cell_of(TET[pidx], prot, xx, yy)) {
|
||||
int gx = px + xx, gy2 = gy + yy;
|
||||
if (gx < 0 || gx >= cfg::BoardW || gy2 < 0 || gy2 >= cfg::BoardH)
|
||||
continue;
|
||||
int x0 = ox + gx * cfg::CellPx, y0 = oy + gy2 * cfg::CellPx;
|
||||
rect(x0, y0, cfg::CellPx, cfg::CellPx, true);
|
||||
occ[yy][xx] = true;
|
||||
minCol = std::min(minCol, xx); maxCol = std::max(maxCol, xx);
|
||||
minRow = std::min(minRow, yy); maxRow = std::max(maxRow, yy);
|
||||
}
|
||||
}
|
||||
if (maxCol < 0) return; // nothing
|
||||
int psize = cfg::CellPx;
|
||||
|
||||
// 5x7 text — LSB = top, so rows draw y..y+6. No baseline offset needed.
|
||||
void drawChar5x7(int x, int y, char c, bool on) {
|
||||
uint8_t gi = Font5x7::glyph(c);
|
||||
if (gi == 255)
|
||||
return;
|
||||
for (int col = 0; col < 5; ++col) {
|
||||
uint8_t bits = Font5x7::data[gi][col];
|
||||
for (int row = 0; row < 8; row++) {
|
||||
if (bits & (1u << row)) {
|
||||
putPixel(x + col, y + row, on);
|
||||
}
|
||||
auto drawHorizRun = [&](int boardY, int boardXCellStart, int cells, bool topEdge) {
|
||||
int yPix = oy + boardY * psize + (topEdge ? 0 : (psize - 1));
|
||||
int xPixStart = ox + boardXCellStart * psize;
|
||||
int totalPixels = cells * psize;
|
||||
for (int i = 0; i < totalPixels; ++i) {
|
||||
int xPix = xPixStart + i;
|
||||
// Parity-based sparse edge: ensures corners never double-thicken
|
||||
if (((xPix + yPix) & 1) == 0) putPixel(xPix, yPix, true);
|
||||
}
|
||||
};
|
||||
auto drawVertRun = [&](int boardX, int boardYCellStart, int cells, bool leftEdge) {
|
||||
int xPix = ox + boardX * psize + (leftEdge ? 0 : (psize - 1));
|
||||
int yPixStart = oy + boardYCellStart * psize;
|
||||
int totalPixels = cells * psize;
|
||||
for (int i = 0; i < totalPixels; ++i) {
|
||||
int yPix = yPixStart + i;
|
||||
if (((xPix + yPix) & 1) == 0) putPixel(xPix, yPix, true);
|
||||
}
|
||||
};
|
||||
|
||||
// Horizontal top edges
|
||||
for (int r = minRow; r <= maxRow; ++r) {
|
||||
int c = minCol;
|
||||
while (c <= maxCol) {
|
||||
if (occ[r][c] && (r == minRow || !occ[r-1][c])) {
|
||||
int runStart = c; int end = c;
|
||||
while (end <= maxCol && occ[r][end] && (r == minRow || !occ[r-1][end])) ++end;
|
||||
drawHorizRun(gy + r, px + runStart, end - runStart, true);
|
||||
c = end;
|
||||
} else ++c;
|
||||
}
|
||||
}
|
||||
// Horizontal bottom edges
|
||||
for (int r = minRow; r <= maxRow; ++r) {
|
||||
int c = minCol;
|
||||
while (c <= maxCol) {
|
||||
if (occ[r][c] && (r == maxRow || !occ[r+1][c])) {
|
||||
int runStart = c; int end = c;
|
||||
while (end <= maxCol && occ[r][end] && (r == maxRow || !occ[r+1][end])) ++end;
|
||||
drawHorizRun(gy + r, px + runStart, end - runStart, false);
|
||||
c = end;
|
||||
} else ++c;
|
||||
}
|
||||
}
|
||||
// Vertical left edges
|
||||
for (int c = minCol; c <= maxCol; ++c) {
|
||||
int r = minRow;
|
||||
while (r <= maxRow) {
|
||||
if (occ[r][c] && (c == minCol || !occ[r][c-1])) {
|
||||
int runStart = r; int end = r;
|
||||
while (end <= maxRow && occ[end][c] && (c == minCol || !occ[end][c-1])) ++end;
|
||||
drawVertRun(px + c, gy + runStart, end - runStart, true);
|
||||
r = end;
|
||||
} else ++r;
|
||||
}
|
||||
}
|
||||
// Vertical right edges
|
||||
for (int c = minCol; c <= maxCol; ++c) {
|
||||
int r = minRow;
|
||||
while (r <= maxRow) {
|
||||
if (occ[r][c] && (c == maxCol || !occ[r][c+1])) {
|
||||
int runStart = r; int end = r;
|
||||
while (end <= maxRow && occ[end][c] && (c == maxCol || !occ[end][c+1])) ++end;
|
||||
drawVertRun(px + c, gy + runStart, end - runStart, false);
|
||||
r = end;
|
||||
} else ++r;
|
||||
}
|
||||
}
|
||||
}
|
||||
void drawLabel(int x, int y, const char* s) {
|
||||
for (int i = 0; s[i]; ++i)
|
||||
drawChar5x7(x + i * 6, y, s[i], true);
|
||||
|
||||
void drawChar5x7(int x, int y, char c, bool on) {
|
||||
uint8_t gi = Font5x7::glyph(c);
|
||||
if (gi == 255) return;
|
||||
for (int col = 0; col < 5; ++col) {
|
||||
uint8_t bits = Font5x7::data[gi][col];
|
||||
for (int row = 0; row < 8; row++)
|
||||
if (bits & (1u << row)) putPixel(x + col, y + row, on);
|
||||
}
|
||||
}
|
||||
void drawLabel(int x, int y, const char* s) { for (int i = 0; s[i]; ++i) drawChar5x7(x + i * 6, y, s[i], true); }
|
||||
void drawNumber(int x, int y, int n) {
|
||||
char buf[16];
|
||||
int len = snprintf(buf, sizeof(buf), "%d", n);
|
||||
for (int i = 0; i < len; ++i)
|
||||
drawChar5x7(x + i * 6, y, buf[i], true);
|
||||
for (int i = 0; i < len; ++i) drawChar5x7(x + i * 6, y, buf[i], true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -683,8 +795,6 @@ extern "C" void app_main() {
|
||||
static PlatformFramebuffer fb;
|
||||
static PlatformInput input;
|
||||
static PlatformClock clock;
|
||||
|
||||
static App* app = nullptr;
|
||||
app = new App(fb, input, clock);
|
||||
app->runForever();
|
||||
static App app(fb, input, clock);
|
||||
app.runForever();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user