2044 lines
89 KiB
C++
2044 lines
89 KiB
C++
#include "CyberRenderer.hpp"
|
|
#include "hub/HubRoom.hpp"
|
|
#include "hub/RestRoom.hpp"
|
|
#include "combat/CombatSystem.hpp"
|
|
#include "rlgl.h"
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <cstdio>
|
|
|
|
namespace dnr {
|
|
|
|
namespace {
|
|
|
|
// Draw a textured quad in 3D using rlgl
|
|
void drawTexturedQuad(Texture2D tex, Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3, float uTiles = 1.0f, float vTiles = 1.0f, Color tint = WHITE) {
|
|
rlDisableBackfaceCulling();
|
|
rlSetTexture(tex.id);
|
|
rlBegin(RL_QUADS);
|
|
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
|
|
|
|
rlTexCoord2f(0.0f, 0.0f);
|
|
rlVertex3f(v0.x, v0.y, v0.z);
|
|
|
|
rlTexCoord2f(0.0f, vTiles);
|
|
rlVertex3f(v1.x, v1.y, v1.z);
|
|
|
|
rlTexCoord2f(uTiles, vTiles);
|
|
rlVertex3f(v2.x, v2.y, v2.z);
|
|
|
|
rlTexCoord2f(uTiles, 0.0f);
|
|
rlVertex3f(v3.x, v3.y, v3.z);
|
|
|
|
rlEnd();
|
|
rlSetTexture(0);
|
|
rlEnableBackfaceCulling();
|
|
}
|
|
|
|
// Draw a textured 3D box using rlgl
|
|
void drawTexturedBox(Texture2D tex, Vector3 pos, Vector3 size, float uTile = 1.0f, float vTile = 1.0f, Color tint = WHITE) {
|
|
float x = pos.x - size.x * 0.5f;
|
|
float y = pos.y - size.y * 0.5f;
|
|
float z = pos.z - size.z * 0.5f;
|
|
float w = size.x;
|
|
float h = size.y;
|
|
float d = size.z;
|
|
|
|
rlDisableBackfaceCulling();
|
|
rlSetTexture(tex.id);
|
|
rlBegin(RL_QUADS);
|
|
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
|
|
|
|
// Front face (+Z)
|
|
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x, y + h, z + d);
|
|
rlTexCoord2f(0.0f, vTile); rlVertex3f(x, y, z + d);
|
|
rlTexCoord2f(uTile, vTile); rlVertex3f(x + w, y, z + d);
|
|
rlTexCoord2f(uTile, 0.0f); rlVertex3f(x + w, y + h, z + d);
|
|
|
|
// Back face (-Z)
|
|
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + w, y + h, z);
|
|
rlTexCoord2f(0.0f, vTile); rlVertex3f(x + w, y, z);
|
|
rlTexCoord2f(uTile, vTile); rlVertex3f(x, y, z);
|
|
rlTexCoord2f(uTile, 0.0f); rlVertex3f(x, y + h, z);
|
|
|
|
// Top face (+Y)
|
|
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x, y + h, z);
|
|
rlTexCoord2f(0.0f, vTile); rlVertex3f(x, y + h, z + d);
|
|
rlTexCoord2f(uTile, vTile); rlVertex3f(x + w, y + h, z + d);
|
|
rlTexCoord2f(uTile, 0.0f); rlVertex3f(x + w, y + h, z);
|
|
|
|
// Bottom face (-Y)
|
|
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x, y, z + d);
|
|
rlTexCoord2f(0.0f, vTile); rlVertex3f(x, y, z);
|
|
rlTexCoord2f(uTile, vTile); rlVertex3f(x + w, y, z);
|
|
rlTexCoord2f(uTile, 0.0f); rlVertex3f(x + w, y, z + d);
|
|
|
|
// Right face (+X)
|
|
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + w, y + h, z + d);
|
|
rlTexCoord2f(0.0f, vTile); rlVertex3f(x + w, y, z + d);
|
|
rlTexCoord2f(uTile, vTile); rlVertex3f(x + w, y, z);
|
|
rlTexCoord2f(uTile, 0.0f); rlVertex3f(x + w, y + h, z);
|
|
|
|
// Left face (-X)
|
|
rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x, y + h, z);
|
|
rlTexCoord2f(0.0f, vTile); rlVertex3f(x, y, z);
|
|
rlTexCoord2f(uTile, vTile); rlVertex3f(x, y, z + d);
|
|
rlTexCoord2f(uTile, 0.0f); rlVertex3f(x, y + h, z + d);
|
|
|
|
rlEnd();
|
|
rlSetTexture(0);
|
|
rlEnableBackfaceCulling();
|
|
}
|
|
|
|
// Procedural generator: Warm Oak Parquet Floor (256x256)
|
|
Texture2D createParquetTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 145, 95, 52, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
const int plankHeight = 32;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
int row = y / plankHeight;
|
|
int rowY = y % plankHeight;
|
|
bool isHorizontalSeam = (rowY == 0 || rowY == plankHeight - 1);
|
|
|
|
for (int x = 0; x < w; ++x) {
|
|
int shift = (row % 2 == 0) ? 0 : 48;
|
|
int plankW = 96;
|
|
int plankCol = (x + shift) / plankW;
|
|
int plankX = (x + shift) % plankW;
|
|
bool isVerticalSeam = (plankX == 0 || plankX == plankW - 1);
|
|
|
|
if (isHorizontalSeam || isVerticalSeam) {
|
|
pixels[y * w + x] = Color{ 48, 28, 14, 255 };
|
|
continue;
|
|
}
|
|
|
|
int plankSeed = (row * 17 + plankCol * 31);
|
|
int toneOffset = (plankSeed % 31) - 15;
|
|
|
|
int baseR = 158 + toneOffset;
|
|
int baseG = 108 + (toneOffset * 3) / 4;
|
|
int baseB = 60 + toneOffset / 2;
|
|
|
|
float grainSin = std::sin((y * 4.0f + std::sin(x * 0.05f) * 6.0f) * 0.4f);
|
|
int grainVal = static_cast<int>(grainSin * 14.0f);
|
|
int fiberNoise = ((x * 13 + y * 29) % 9) - 4;
|
|
|
|
int finalR = std::clamp(baseR + grainVal + fiberNoise, 40, 240);
|
|
int finalG = std::clamp(baseG + (grainVal * 3) / 4 + fiberNoise, 25, 200);
|
|
int finalB = std::clamp(baseB + grainVal / 2 + fiberNoise, 10, 160);
|
|
|
|
pixels[y * w + x] = Color{ (unsigned char)finalR, (unsigned char)finalG, (unsigned char)finalB, 255 };
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Luxury Royal Navy Persian/Cyber Rug (256x256)
|
|
Texture2D createRugTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 22, 32, 54, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
int distEdge = std::min({ x, w - 1 - x, y, h - 1 - y });
|
|
int weave = ((x + y) % 2 == 0) ? 6 : -6;
|
|
|
|
Color c = Color{ 24, 35, 58, 255 };
|
|
|
|
if (distEdge < 4) {
|
|
c = Color{ 210, 185, 130, 255 }; // Fringes
|
|
} else if (distEdge >= 6 && distEdge <= 22) {
|
|
// Wide ornate gold border
|
|
int pattern = ((x / 6 + y / 6) % 2 == 0) ? 35 : -15;
|
|
c = Color{ (unsigned char)std::clamp(185 + pattern, 0, 255),
|
|
(unsigned char)std::clamp(150 + pattern, 0, 255),
|
|
(unsigned char)std::clamp(75 + pattern, 0, 255), 255 };
|
|
} else if (distEdge == 24 || distEdge == 25) {
|
|
c = Color{ 165, 35, 45, 255 }; // Crimson accent piping
|
|
} else if (distEdge >= 28 && distEdge <= 38) {
|
|
if ((x / 4) % 2 == (y / 4) % 2) {
|
|
c = Color{ 125, 98, 58, 255 };
|
|
} else {
|
|
c = Color{ 36, 52, 84, 255 };
|
|
}
|
|
} else {
|
|
int cx = w / 2;
|
|
int cy = h / 2;
|
|
int dx = std::abs(x - cx);
|
|
int dy = std::abs(y - cy);
|
|
int manhattanDist = dx + dy;
|
|
int eucDist = static_cast<int>(std::sqrt(dx * dx + dy * dy));
|
|
|
|
if (manhattanDist < 60 && manhattanDist > 54) {
|
|
c = Color{ 215, 175, 85, 255 }; // Gold diamond outline
|
|
} else if (manhattanDist <= 54 && manhattanDist >= 40) {
|
|
c = Color{ 150, 32, 48, 255 }; // Crimson medallion body
|
|
} else if (eucDist < 25) {
|
|
if (eucDist < 8) {
|
|
c = Color{ 245, 215, 125, 255 };
|
|
} else if ((dx == dy) || dx == 0 || dy == 0) {
|
|
c = Color{ 215, 175, 85, 255 };
|
|
} else {
|
|
c = Color{ 20, 32, 54, 255 };
|
|
}
|
|
} else if (manhattanDist < 85 && (dx < 12 || dy < 12)) {
|
|
c = Color{ 175, 145, 75, 255 };
|
|
}
|
|
}
|
|
|
|
int r = std::clamp(c.r + weave, 0, 255);
|
|
int g = std::clamp(c.g + weave, 0, 255);
|
|
int b = std::clamp(c.b + weave, 0, 255);
|
|
pixels[y * w + x] = Color{ (unsigned char)r, (unsigned char)g, (unsigned char)b, 255 };
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Textured Apartment Stucco/Wallpaper (256x256)
|
|
Texture2D createWallTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 78, 84, 94, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
float bottomShadow = (y > 220) ? (1.0f - (y - 220) / 36.0f * 0.35f) : 1.0f;
|
|
float topShadow = (y < 30) ? (0.82f + (y / 30.0f) * 0.18f) : 1.0f;
|
|
float vLighting = bottomShadow * topShadow;
|
|
|
|
for (int x = 0; x < w; ++x) {
|
|
int seamMod = x % 64;
|
|
int seamShade = 0;
|
|
if (seamMod == 0) seamShade = -22;
|
|
else if (seamMod == 1) seamShade = +16;
|
|
|
|
int stipple = ((x * 19 + y * 37 + (x ^ y) * 11) % 15) - 7;
|
|
|
|
int baseR = static_cast<int>((82 + stipple + seamShade) * vLighting);
|
|
int baseG = static_cast<int>((88 + stipple + seamShade) * vLighting);
|
|
int baseB = static_cast<int>((98 + stipple + seamShade) * vLighting);
|
|
|
|
pixels[y * w + x] = Color{
|
|
(unsigned char)std::clamp(baseR, 0, 255),
|
|
(unsigned char)std::clamp(baseG, 0, 255),
|
|
(unsigned char)std::clamp(baseB, 0, 255),
|
|
255
|
|
};
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Cyberpunk Synthwave Neon Wall Art (128x256)
|
|
Texture2D createPosterTexture() {
|
|
const int w = 128;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 15, 10, 24, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
float ny = (float)y / h;
|
|
for (int x = 0; x < w; ++x) {
|
|
float nx = (float)x / w;
|
|
|
|
if (x < 4 || x >= w - 4 || y < 4 || y >= h - 4) {
|
|
pixels[y * w + x] = Color{ 25, 25, 30, 255 }; // Metal frame
|
|
continue;
|
|
}
|
|
|
|
Color c;
|
|
if (ny < 0.65f) {
|
|
int r = static_cast<int>(20 + ny * 140);
|
|
int g = static_cast<int>(10 + ny * 20);
|
|
int b = static_cast<int>(40 + ny * 70);
|
|
c = Color{ (unsigned char)r, (unsigned char)g, (unsigned char)b, 255 };
|
|
|
|
// Retro Neon Sun
|
|
float sdx = (float)(x - 64);
|
|
float sdy = (float)(y - 110);
|
|
float sDist = std::sqrt(sdx * sdx + sdy * sdy);
|
|
if (sDist < 38.0f) {
|
|
bool isCutout = (y > 105 && (y % 6 < 2));
|
|
if (!isCutout) {
|
|
float sunT = (y - 72) / 76.0f;
|
|
int sr = 255;
|
|
int sg = static_cast<int>(220 - sunT * 180);
|
|
int sb = static_cast<int>(40 + sunT * 120);
|
|
c = Color{ (unsigned char)sr, (unsigned char)sg, (unsigned char)sb, 255 };
|
|
}
|
|
}
|
|
} else {
|
|
float gridY = (ny - 0.65f) / 0.35f;
|
|
c = Color{ 10, 12, 22, 255 };
|
|
|
|
int lineIndex = static_cast<int>(std::pow(gridY, 0.6f) * 10.0f);
|
|
int prevIndex = static_cast<int>(std::pow(std::max(0.0f, gridY - 0.015f), 0.6f) * 10.0f);
|
|
if (lineIndex != prevIndex) {
|
|
c = Color{ 0, 230, 255, 255 };
|
|
}
|
|
|
|
float rayAngle = std::atan2(gridY, nx - 0.5f);
|
|
int rayId = static_cast<int>((rayAngle + 3.14159f) * 6.0f);
|
|
int rayIdNext = static_cast<int>((std::atan2(gridY, (nx + 0.03f) - 0.5f) + 3.14159f) * 6.0f);
|
|
if (rayId != rayIdNext) {
|
|
c = Color{ 255, 0, 160, 255 };
|
|
}
|
|
}
|
|
|
|
pixels[y * w + x] = c;
|
|
}
|
|
}
|
|
|
|
ImageDrawText(&img, "NETRUNNER", 14, 12, 12, Color{ 0, 240, 255, 255 });
|
|
ImageDrawText(&img, "2088", 50, 26, 10, Color{ 255, 50, 150, 255 });
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Polished Dark Walnut Desk Wood (256x256)
|
|
Texture2D createDeskWoodTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 90, 54, 32, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
float grain1 = std::sin((x * 0.4f + std::sin(y * 0.08f) * 4.0f));
|
|
float grain2 = std::sin((x * 1.2f));
|
|
int gVal = static_cast<int>(grain1 * 12.0f + grain2 * 6.0f);
|
|
|
|
int r = std::clamp(92 + gVal, 30, 200);
|
|
int g = std::clamp(56 + (gVal * 2) / 3, 20, 150);
|
|
int b = std::clamp(34 + gVal / 2, 10, 100);
|
|
|
|
int sheen = static_cast<int>(std::sin((x + y * 0.5f) * 0.05f) * 10.0f);
|
|
if (sheen > 0) {
|
|
r = std::clamp(r + sheen, 0, 255);
|
|
g = std::clamp(g + sheen, 0, 255);
|
|
b = std::clamp(b + sheen, 0, 255);
|
|
}
|
|
|
|
pixels[y * w + x] = Color{ (unsigned char)r, (unsigned char)g, (unsigned char)b, 255 };
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Code Editor / IDE Monitor Screen (512x256)
|
|
Texture2D createMonitorCodeTexture() {
|
|
const int w = 512;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 18, 20, 28, 255 });
|
|
|
|
ImageDrawRectangle(&img, 0, 0, w, 22, Color{ 28, 32, 44, 255 });
|
|
ImageDrawCircle(&img, 12, 11, 4, Color{ 255, 95, 86, 255 });
|
|
ImageDrawCircle(&img, 26, 11, 4, Color{ 255, 189, 46, 255 });
|
|
ImageDrawCircle(&img, 40, 11, 4, Color{ 39, 201, 63, 255 });
|
|
|
|
ImageDrawText(&img, "neural_daemon.cpp - [ACTIVE SESSION]", 60, 6, 10, Color{ 160, 175, 200, 255 });
|
|
ImageDrawRectangle(&img, 0, 22, 38, h - 38, Color{ 22, 25, 34, 255 });
|
|
|
|
ImageDrawRectangle(&img, 0, h - 16, w, 16, Color{ 0, 122, 204, 255 });
|
|
ImageDrawText(&img, " UTF-8 C++20 CYBER-LINK: 10 Gbps ICE_SECURITY: BYPASS READY", 8, h - 13, 10, WHITE);
|
|
ImageDrawRectangle(&img, w - 45, 22, 45, h - 38, Color{ 22, 24, 32, 255 });
|
|
|
|
const char* codeLines[] = {
|
|
"#include <cyberspace/neural_link.h>",
|
|
"namespace netrunner {",
|
|
" // Primary ICE Breaker protocol",
|
|
" class NeuralExploit : public IDaemon {",
|
|
" public:",
|
|
" bool injectPayload(uint64_t targetNode) {",
|
|
" CipherBuffer buffer = decryptChamber(6);",
|
|
" if (!buffer.isValid()) {",
|
|
" triggerAlarm(ICE_WARNING);",
|
|
" return false; // lockout",
|
|
" }",
|
|
" log(\"JACK IN SEQUENCE CONFIRMED\");",
|
|
" return m_revolver.spinAndFire();",
|
|
" }",
|
|
" private:",
|
|
" CyberRevolver m_revolver{ 6 };",
|
|
" };",
|
|
"} // namespace netrunner",
|
|
"// Awaiting Neural Sync command..."
|
|
};
|
|
|
|
int lineCount = sizeof(codeLines) / sizeof(codeLines[0]);
|
|
for (int i = 0; i < lineCount; ++i) {
|
|
int lineY = 28 + i * 11;
|
|
char numBuf[8];
|
|
snprintf(numBuf, sizeof(numBuf), "%02d", i + 1);
|
|
ImageDrawText(&img, numBuf, 8, lineY, 10, Color{ 85, 95, 115, 255 });
|
|
|
|
Color syntaxCol = Color{ 220, 225, 235, 255 };
|
|
std::string s = codeLines[i];
|
|
if (s.rfind("#include", 0) == 0) syntaxCol = Color{ 210, 140, 230, 255 };
|
|
else if (s.find("//") != std::string::npos) syntaxCol = Color{ 100, 210, 115, 255 };
|
|
else if (s.find("class") != std::string::npos || s.find("namespace") != std::string::npos) syntaxCol = Color{ 86, 156, 214, 255 };
|
|
else if (s.find("bool") != std::string::npos || s.find("uint64_t") != std::string::npos) syntaxCol = Color{ 78, 201, 176, 255 };
|
|
else if (s.find("return") != std::string::npos) syntaxCol = Color{ 197, 134, 192, 255 };
|
|
else if (s.find("\"") != std::string::npos) syntaxCol = Color{ 206, 145, 120, 255 };
|
|
else if (s.find("injectPayload") != std::string::npos || s.find("decryptChamber") != std::string::npos) syntaxCol = Color{ 220, 220, 130, 255 };
|
|
|
|
ImageDrawText(&img, codeLines[i], 46, lineY, 10, syntaxCol);
|
|
ImageDrawRectangle(&img, w - 40, lineY, static_cast<int>(s.length() * 0.8f), 2, syntaxCol);
|
|
}
|
|
|
|
ImageDrawRectangle(&img, 46 + 220, 28 + 18 * 11, 7, 10, Color{ 0, 240, 255, 255 });
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Cyberspace Telemetry Radar Monitor (256x256)
|
|
Texture2D createMonitorRadarTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 10, 16, 26, 255 });
|
|
|
|
ImageDrawRectangle(&img, 0, 0, w, 20, Color{ 16, 26, 42, 255 });
|
|
ImageDrawText(&img, "NET SCAN // SUBNET 192.168.0", 8, 5, 10, Color{ 0, 220, 255, 255 });
|
|
|
|
int cx = w / 2;
|
|
int cy = h / 2 + 10;
|
|
ImageDrawCircleLines(&img, cx, cy, 30, Color{ 0, 140, 180, 180 });
|
|
ImageDrawCircleLines(&img, cx, cy, 60, Color{ 0, 140, 180, 180 });
|
|
ImageDrawCircleLines(&img, cx, cy, 90, Color{ 0, 140, 180, 180 });
|
|
|
|
ImageDrawLine(&img, cx - 100, cy, cx + 100, cy, Color{ 0, 180, 220, 120 });
|
|
ImageDrawLine(&img, cx, cy - 100, cx, cy + 100, Color{ 0, 180, 220, 120 });
|
|
ImageDrawLine(&img, cx, cy, cx + 65, cy - 65, Color{ 0, 255, 180, 255 });
|
|
|
|
Vector2 nodes[] = {
|
|
{ 70.0f, 90.0f },
|
|
{ 180.0f, 80.0f },
|
|
{ 160.0f, 190.0f },
|
|
{ 90.0f, 180.0f },
|
|
{ 128.0f, 138.0f }
|
|
};
|
|
for (int i = 0; i < 4; ++i) {
|
|
ImageDrawLine(&img, (int)nodes[i].x, (int)nodes[i].y, (int)nodes[4].x, (int)nodes[4].y, Color{ 0, 200, 255, 160 });
|
|
ImageDrawCircle(&img, (int)nodes[i].x, (int)nodes[i].y, 4, Color{ 255, 190, 0, 255 });
|
|
}
|
|
ImageDrawCircle(&img, (int)nodes[4].x, (int)nodes[4].y, 6, Color{ 0, 255, 120, 255 });
|
|
|
|
ImageDrawText(&img, "GATEWAY STATUS: STANDBY", 10, h - 26, 10, Color{ 0, 255, 120, 255 });
|
|
ImageDrawText(&img, "ICE BREAKER: READY", 10, h - 14, 10, Color{ 255, 200, 60, 255 });
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Diamond-Quilted Bedspread (256x256)
|
|
Texture2D createBedQuiltTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 42, 54, 76, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
const int gridSize = 32;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
int d1 = (x + y) % gridSize;
|
|
int d2 = (x - y + 1000 * gridSize) % gridSize;
|
|
|
|
bool isSeam = (d1 <= 1 || d2 <= 1);
|
|
int distToSeam = std::min({ d1, gridSize - d1, d2, gridSize - d2 });
|
|
|
|
float puff = std::clamp(distToSeam / (gridSize * 0.5f), 0.0f, 1.0f);
|
|
int puffShade = static_cast<int>(puff * 22.0f);
|
|
int weave = ((x ^ y) % 3) * 3 - 3;
|
|
|
|
int r = 40 + puffShade + weave;
|
|
int g = 52 + puffShade + weave;
|
|
int b = 74 + puffShade + weave;
|
|
|
|
if (isSeam) {
|
|
bool isStitch = ((x / 4 + y / 4) % 2 == 0);
|
|
if (isStitch) {
|
|
r = 110; g = 135; b = 175;
|
|
} else {
|
|
r = 24; g = 32; b = 46;
|
|
}
|
|
}
|
|
|
|
pixels[y * w + x] = Color{
|
|
(unsigned char)std::clamp(r, 0, 255),
|
|
(unsigned char)std::clamp(g, 0, 255),
|
|
(unsigned char)std::clamp(b, 0, 255),
|
|
255
|
|
};
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Megacity Night Skyline Window Backdrop (512x256)
|
|
Texture2D createCityNightTexture() {
|
|
const int w = 512;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 8, 12, 24, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
float t = (float)y / h;
|
|
int r = static_cast<int>(6 + t * 24);
|
|
int g = static_cast<int>(10 + t * 16);
|
|
int b = static_cast<int>(20 + t * 35);
|
|
for (int x = 0; x < w; ++x) {
|
|
pixels[y * w + x] = Color{ (unsigned char)r, (unsigned char)g, (unsigned char)b, 255 };
|
|
}
|
|
}
|
|
|
|
// Stars
|
|
for (int i = 0; i < 70; ++i) {
|
|
int sx = (i * 73 + 17) % w;
|
|
int sy = (i * 37 + 5) % (h / 2);
|
|
int brightness = 140 + (i * 29) % 115;
|
|
ImageDrawPixel(&img, sx, sy, Color{ (unsigned char)brightness, (unsigned char)brightness, 255, 255 });
|
|
}
|
|
|
|
// Distant dark silhouettes
|
|
for (int b = 0; b < 24; ++b) {
|
|
int bx = b * 22;
|
|
int bw = 18 + (b * 7) % 15;
|
|
int bh = 70 + (b * 31) % 80;
|
|
int by = h - bh;
|
|
ImageDrawRectangle(&img, bx, by, bw, bh, Color{ 14, 18, 32, 255 });
|
|
}
|
|
|
|
// Foreground lit skyscrapers
|
|
for (int b = 0; b < 16; ++b) {
|
|
int bx = b * 32 + (b % 3) * 4;
|
|
int bw = 24 + (b * 11) % 18;
|
|
int bh = 110 + (b * 47) % 115;
|
|
int by = h - bh;
|
|
|
|
Color bldgCol = Color{ 20, 26, 42, 255 };
|
|
ImageDrawRectangle(&img, bx, by, bw, bh, bldgCol);
|
|
|
|
if (bh > 160) {
|
|
ImageDrawLine(&img, bx + bw / 2, by - 25, bx + bw / 2, by, Color{ 60, 70, 90, 255 });
|
|
ImageDrawCircle(&img, bx + bw / 2, by - 26, 2, Color{ 255, 40, 40, 255 });
|
|
}
|
|
|
|
for (int wy = by + 12; wy < h - 8; wy += 8) {
|
|
for (int wx = bx + 4; wx < bx + bw - 4; wx += 5) {
|
|
int litSeed = (wx * 17 + wy * 31 + b * 53);
|
|
if (litSeed % 5 == 0) {
|
|
Color winCol = Color{ 255, 230, 130, 255 };
|
|
if (litSeed % 3 == 0) winCol = Color{ 140, 230, 255, 255 };
|
|
else if (litSeed % 7 == 0) winCol = Color{ 255, 120, 160, 255 };
|
|
ImageDrawRectangle(&img, wx, wy, 3, 4, winCol);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Traffic streaks
|
|
ImageDrawLine(&img, 0, h - 35, w, h - 25, Color{ 255, 140, 0, 120 });
|
|
ImageDrawLine(&img, 0, h - 33, w, h - 23, Color{ 0, 230, 255, 100 });
|
|
ImageDrawLine(&img, 0, h - 18, w, h - 12, Color{ 255, 50, 80, 140 });
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Brushed Steel & Hazard Stripe Cyber Gateway (256x256)
|
|
Texture2D createCyberGateTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 28, 35, 48, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
int metalNoise = ((y * 11 + (x ^ y) * 7) % 9) - 4;
|
|
|
|
Color c = Color{
|
|
(unsigned char)std::clamp(32 + metalNoise, 0, 255),
|
|
(unsigned char)std::clamp(38 + metalNoise, 0, 255),
|
|
(unsigned char)std::clamp(50 + metalNoise, 0, 255),
|
|
255
|
|
};
|
|
|
|
int borderDist = std::min({ x, w - 1 - x, y, h - 1 - y });
|
|
if (borderDist < 20) {
|
|
bool isYellow = (((x + y) / 14) % 2 == 0);
|
|
if (isYellow) {
|
|
c = Color{ 240, 195, 25, 255 };
|
|
} else {
|
|
c = Color{ 22, 22, 26, 255 };
|
|
}
|
|
} else if (borderDist >= 20 && borderDist <= 23) {
|
|
c = Color{ 12, 16, 24, 255 };
|
|
} else if (borderDist == 26) {
|
|
if ((x % 24 == 12 && (y <= 28 || y >= h - 28)) ||
|
|
(y % 24 == 12 && (x <= 28 || x >= w - 28))) {
|
|
c = Color{ 190, 205, 225, 255 };
|
|
}
|
|
}
|
|
|
|
pixels[y * w + x] = c;
|
|
}
|
|
}
|
|
|
|
ImageDrawRectangle(&img, 32, h / 2 - 12, w - 64, 24, Color{ 20, 26, 38, 255 });
|
|
ImageDrawRectangleLines(&img, Rectangle{ 32.0f, h / 2.0f - 12.0f, (float)(w - 64), 24.0f }, 2, Color{ 0, 180, 220, 200 });
|
|
|
|
ImageDrawText(&img, "NEURAL DIVER GATEWAY", 46, h / 2 - 8, 12, Color{ 0, 240, 255, 255 });
|
|
ImageDrawText(&img, "CYBERSPACE ICE LEVEL 1+", 54, 40, 10, Color{ 240, 195, 25, 255 });
|
|
ImageDrawText(&img, "AUTHORIZED RUNNERS ONLY", 50, h - 50, 10, Color{ 220, 50, 60, 255 });
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: High-Tech Gunmetal Dungeon Wall (256x256)
|
|
Texture2D createDungeonWallTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 18, 24, 36, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
int n = ((x * 13 + y * 29) % 17) - 8;
|
|
int r = std::clamp(22 + n, 0, 255);
|
|
int g = std::clamp(28 + n, 0, 255);
|
|
int b = std::clamp(42 + n, 0, 255);
|
|
Color c = Color{ (unsigned char)r, (unsigned char)g, (unsigned char)b, 255 };
|
|
|
|
// Panel bevel seams
|
|
if (x < 4 || x >= w - 4 || y < 4 || y >= h - 4 ||
|
|
y == 85 || y == 86 || y == 170 || y == 171 ||
|
|
(x == 128 && (y < 85 || y > 170))) {
|
|
c = Color{ 8, 12, 18, 255 };
|
|
}
|
|
|
|
// Hex bolts in corners
|
|
for (int bx : { 16, w - 16 }) {
|
|
for (int by : { 16, 96, 182, h - 16 }) {
|
|
if (std::abs(x - bx) <= 3 && std::abs(y - by) <= 3) {
|
|
c = Color{ 130, 160, 200, 255 };
|
|
}
|
|
}
|
|
}
|
|
|
|
// Vertical glowing cyan data fiber conduit
|
|
if (x >= 124 && x <= 132) {
|
|
int dist = std::abs(x - 128);
|
|
if (dist <= 1) {
|
|
c = Color{ 0, 240, 255, 255 };
|
|
} else if (dist <= 3) {
|
|
c = Color{ 0, 160, 220, 255 };
|
|
} else {
|
|
c = Color{ 0, 80, 140, 255 };
|
|
}
|
|
}
|
|
|
|
// Hazard warning stripes at bottom
|
|
if (y >= h - 22 && y < h - 4) {
|
|
bool isYellow = (((x + y) / 10) % 2 == 0);
|
|
c = isYellow ? Color{ 230, 180, 25, 255 } : Color{ 15, 18, 24, 255 };
|
|
}
|
|
|
|
// Server status LEDs
|
|
if (x >= 40 && x <= 85 && y >= 110 && y <= 145) {
|
|
if (x % 12 < 4 && y % 10 < 4) {
|
|
c = ((x + y) % 3 == 0) ? CyberColors::NeonGreen :
|
|
(((x + y) % 3 == 1) ? CyberColors::NeonCyan : CyberColors::NeonOrange);
|
|
} else {
|
|
c = Color{ 12, 16, 24, 255 };
|
|
}
|
|
}
|
|
|
|
pixels[y * w + x] = c;
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Industrial Tech Floor Grating (256x256)
|
|
Texture2D createDungeonFloorTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 12, 16, 24, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
bool isMeshLine = (((x + y) % 16 == 0) || ((x - y + 512) % 16 == 0));
|
|
bool isGrateHole = (((x + y) % 16 >= 6 && (x + y) % 16 <= 10) &&
|
|
((x - y + 512) % 16 >= 6 && (x - y + 512) % 16 <= 10));
|
|
|
|
Color c = Color{ 20, 28, 40, 255 };
|
|
|
|
if (isMeshLine) {
|
|
c = Color{ 70, 90, 120, 255 };
|
|
} else if (isGrateHole) {
|
|
bool isCircuit = (x % 64 < 6 || y % 64 < 6);
|
|
c = isCircuit ? Color{ 0, 200, 240, 255 } : Color{ 6, 8, 14, 255 };
|
|
}
|
|
|
|
if (x < 6 || x >= w - 6 || y < 6 || y >= h - 6) {
|
|
c = Color{ 35, 45, 60, 255 };
|
|
if ((x % 32 < 4) && (y % 32 < 4)) {
|
|
c = Color{ 140, 165, 195, 255 };
|
|
}
|
|
}
|
|
|
|
pixels[y * w + x] = c;
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Industrial Grid Ceiling with Neon Fixture (256x256)
|
|
Texture2D createDungeonCeilTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 10, 14, 20, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
Color c = Color{ 12, 16, 24, 255 };
|
|
|
|
if (x % 64 < 8 || y % 64 < 8) {
|
|
c = Color{ 30, 40, 55, 255 };
|
|
}
|
|
|
|
if (x >= 96 && x <= 160 && y >= 32 && y <= 224) {
|
|
int distFromCenter = std::abs(x - 128);
|
|
if (distFromCenter <= 12) {
|
|
c = Color{ 245, 252, 255, 255 };
|
|
} else if (distFromCenter <= 22) {
|
|
c = Color{ 0, 220, 255, 255 };
|
|
} else {
|
|
c = Color{ 0, 80, 140, 255 };
|
|
}
|
|
}
|
|
|
|
pixels[y * w + x] = c;
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Armored Bulkhead Sliding Door (256x256)
|
|
Texture2D createBulkheadDoorTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 22, 30, 44, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
Color c = Color{ 26, 36, 52, 255 };
|
|
|
|
if (x < 6 || x >= w - 6 || y < 6 || y >= h - 6 || std::abs(x - 128) <= 2) {
|
|
c = Color{ 12, 16, 24, 255 };
|
|
}
|
|
|
|
if ((y >= 8 && y <= 32) || (y >= h - 34 && y <= h - 10)) {
|
|
bool isYellow = (((x + y) / 14) % 2 == 0);
|
|
c = isYellow ? Color{ 235, 185, 20, 255 } : Color{ 18, 20, 26, 255 };
|
|
}
|
|
|
|
if (x == 50 || x == 51 || x == 205 || x == 206) {
|
|
c = Color{ 60, 80, 110, 255 };
|
|
}
|
|
|
|
if (x >= 88 && x <= 168 && y >= 96 && y <= 160) {
|
|
int dx = std::abs(x - 128);
|
|
int dy = std::abs(y - 128);
|
|
if (dx <= 32 && dy <= 24) {
|
|
c = Color{ 10, 14, 20, 255 };
|
|
if (dx <= 20 && dy <= 12) {
|
|
c = Color{ 255, 60, 40, 255 };
|
|
}
|
|
}
|
|
}
|
|
|
|
pixels[y * w + x] = c;
|
|
}
|
|
}
|
|
|
|
ImageDrawText(&img, "RESTRICTED ICE", 70, 44, 12, Color{ 0, 220, 255, 255 });
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Structural Door Frame (128x256)
|
|
Texture2D createDoorFrameTexture() {
|
|
const int w = 128;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 30, 40, 58, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
Color c = Color{ 28, 38, 54, 255 };
|
|
|
|
if (x < 6 || x >= w - 6) {
|
|
c = Color{ 14, 18, 26, 255 };
|
|
}
|
|
|
|
if (x >= 40 && x <= 88) {
|
|
c = Color{ 55, 75, 100, 255 };
|
|
if (x >= 56 && x <= 72) {
|
|
c = Color{ 160, 185, 215, 255 };
|
|
}
|
|
}
|
|
|
|
if (x >= w - 18 && x <= w - 6) {
|
|
bool isYellow = (((x + y) / 10) % 2 == 0);
|
|
c = isYellow ? Color{ 230, 180, 25, 255 } : Color{ 16, 20, 28, 255 };
|
|
}
|
|
|
|
pixels[y * w + x] = c;
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Ancient Palace Ashlar Sandstone Wall with Greek Key Meander (256x256)
|
|
Texture2D createAncientStoneWallTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 185, 170, 142, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
const int rowH = 64;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
int row = y / rowH;
|
|
int rowY = y % rowH;
|
|
bool isHorizMortar = (rowY <= 2 || rowY >= rowH - 2);
|
|
|
|
for (int x = 0; x < w; ++x) {
|
|
int shift = (row % 2 == 0) ? 0 : 128;
|
|
int blockCol = (x + shift) / 128;
|
|
int blockX = (x + shift) % 128;
|
|
bool isVertMortar = (blockX <= 2 || blockX >= 126);
|
|
|
|
if (isHorizMortar || isVertMortar) {
|
|
pixels[y * w + x] = Color{ 88, 76, 62, 255 };
|
|
continue;
|
|
}
|
|
|
|
int blockSeed = (row * 23 + blockCol * 41);
|
|
int blockTone = (blockSeed % 25) - 12;
|
|
int chiselNoise = ((x * 17 + y * 31 + (x ^ y) * 7) % 17) - 8;
|
|
|
|
int r = std::clamp(188 + blockTone + chiselNoise, 0, 255);
|
|
int g = std::clamp(172 + blockTone + chiselNoise, 0, 255);
|
|
int b = std::clamp(144 + blockTone + chiselNoise, 0, 255);
|
|
|
|
if (row == 0 && rowY >= 14 && rowY <= 48) {
|
|
int fretX = x % 32;
|
|
bool isFret = (rowY >= 18 && rowY <= 22) || (rowY >= 40 && rowY <= 44) ||
|
|
(fretX >= 14 && fretX <= 18 && rowY >= 22 && rowY <= 40);
|
|
if (isFret) {
|
|
r = std::clamp(r - 45, 0, 255);
|
|
g = std::clamp(g - 50, 0, 255);
|
|
b = std::clamp(b - 55, 0, 255);
|
|
}
|
|
}
|
|
|
|
pixels[y * w + x] = Color{ (unsigned char)r, (unsigned char)g, (unsigned char)b, 255 };
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Ancient Roman Opus Sectile Floor (Terracotta & Marble Flagstones) (256x256)
|
|
Texture2D createAncientFloorTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 175, 100, 65, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
const int tileSize = 32;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
int tY = y / tileSize;
|
|
int py = y % tileSize;
|
|
bool isHorizGrout = (py <= 1 || py >= tileSize - 1);
|
|
|
|
for (int x = 0; x < w; ++x) {
|
|
int tX = x / tileSize;
|
|
int px = x % tileSize;
|
|
bool isVertGrout = (px <= 1 || px >= tileSize - 1);
|
|
|
|
if (isHorizGrout || isVertGrout) {
|
|
pixels[y * w + x] = Color{ 58, 48, 40, 255 };
|
|
continue;
|
|
}
|
|
|
|
bool isMarble = ((tX + tY) % 2 == 0);
|
|
int stoneNoise = ((x * 19 + y * 23) % 11) - 5;
|
|
|
|
if (isMarble) {
|
|
int vein = ((x + y * 2) % 31 == 0 || (x * 2 - y + 500) % 37 == 0) ? -28 : 0;
|
|
int r = std::clamp(224 + stoneNoise + vein, 0, 255);
|
|
int g = std::clamp(218 + stoneNoise + vein, 0, 255);
|
|
int b = std::clamp(205 + stoneNoise + vein, 0, 255);
|
|
pixels[y * w + x] = Color{ (unsigned char)r, (unsigned char)g, (unsigned char)b, 255 };
|
|
} else {
|
|
int r = std::clamp(178 + stoneNoise, 0, 255);
|
|
int g = std::clamp(96 + stoneNoise / 2, 0, 255);
|
|
int b = std::clamp(62 + stoneNoise / 3, 0, 255);
|
|
pixels[y * w + x] = Color{ (unsigned char)r, (unsigned char)g, (unsigned char)b, 255 };
|
|
}
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Ancient Timber Ceiling Beams (256x256)
|
|
Texture2D createAncientCeilTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 70, 42, 22, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
const int beamSpan = 64;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
int bY = y % beamSpan;
|
|
bool isBeam = (bY >= 14 && bY <= 50);
|
|
|
|
for (int x = 0; x < w; ++x) {
|
|
if (isBeam) {
|
|
int grain = static_cast<int>(std::sin((x * 0.08f + std::sin(y * 0.3f)) * 4.0f) * 12.0f);
|
|
int fiber = ((x * 13 + y * 29) % 7) - 3;
|
|
int r = std::clamp(76 + grain + fiber, 0, 255);
|
|
int g = std::clamp(46 + grain / 2 + fiber, 0, 255);
|
|
int b = std::clamp(26 + grain / 3 + fiber, 0, 255);
|
|
|
|
if ((x % 48 >= 22 && x % 48 <= 26) && (bY >= 30 && bY <= 34)) {
|
|
r = 35; g = 35; b = 40;
|
|
}
|
|
pixels[y * w + x] = Color{ (unsigned char)r, (unsigned char)g, (unsigned char)b, 255 };
|
|
} else {
|
|
int stipple = ((x * 19 + y * 23) % 15) - 7;
|
|
int r = std::clamp(142 + stipple, 0, 255);
|
|
int g = std::clamp(132 + stipple, 0, 255);
|
|
int b = std::clamp(116 + stipple, 0, 255);
|
|
pixels[y * w + x] = Color{ (unsigned char)r, (unsigned char)g, (unsigned char)b, 255 };
|
|
}
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// Procedural generator: Weathered Ancient Storage Wood & Chest Planks (256x256)
|
|
Texture2D createAncientWoodTexture() {
|
|
const int w = 256;
|
|
const int h = 256;
|
|
Image img = GenImageColor(w, h, Color{ 105, 68, 38, 255 });
|
|
Color* pixels = (Color*)img.data;
|
|
|
|
const int plankW = 42;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
for (int x = 0; x < w; ++x) {
|
|
int px = x % plankW;
|
|
bool isSeam = (px == 0 || px == plankW - 1);
|
|
if (isSeam) {
|
|
pixels[y * w + x] = Color{ 42, 26, 14, 255 };
|
|
continue;
|
|
}
|
|
|
|
int grain = static_cast<int>(std::sin((y * 0.12f + std::sin(x * 0.05f)) * 5.0f) * 14.0f);
|
|
int noise = ((x * 17 + y * 29) % 9) - 4;
|
|
|
|
int r = std::clamp(115 + grain + noise, 0, 255);
|
|
int g = std::clamp(75 + (grain * 2) / 3 + noise, 0, 255);
|
|
int b = std::clamp(45 + grain / 2 + noise, 0, 255);
|
|
|
|
pixels[y * w + x] = Color{ (unsigned char)r, (unsigned char)g, (unsigned char)b, 255 };
|
|
}
|
|
}
|
|
|
|
Texture2D tex = LoadTextureFromImage(img);
|
|
SetTextureFilter(tex, TEXTURE_FILTER_BILINEAR);
|
|
UnloadImage(img);
|
|
return tex;
|
|
}
|
|
|
|
// 3D Ancient Prop Helpers
|
|
void drawAncientAmphora(Vector3 pos, float height, Color clayColor) {
|
|
DrawCylinder({ pos.x, pos.y + height * 0.05f, pos.z }, height * 0.14f, height * 0.18f, height * 0.10f, 12, clayColor);
|
|
DrawCylinder({ pos.x, pos.y + height * 0.40f, pos.z }, height * 0.18f, height * 0.38f, height * 0.60f, 12, clayColor);
|
|
DrawCylinder({ pos.x, pos.y + height * 0.72f, pos.z }, height * 0.38f, height * 0.16f, height * 0.20f, 12, clayColor);
|
|
DrawCylinder({ pos.x, pos.y + height * 0.88f, pos.z }, height * 0.16f, height * 0.18f, height * 0.20f, 12, clayColor);
|
|
DrawCircle3D({ pos.x, pos.y + height, pos.z }, height * 0.20f, { 1.0f, 0.0f, 0.0f }, 90.0f, clayColor);
|
|
|
|
// Twin loop handles
|
|
DrawCube({ pos.x - height * 0.26f, pos.y + height * 0.62f, pos.z }, height * 0.06f, height * 0.28f, height * 0.06f, clayColor);
|
|
DrawCube({ pos.x + height * 0.26f, pos.y + height * 0.62f, pos.z }, height * 0.06f, height * 0.28f, height * 0.06f, clayColor);
|
|
}
|
|
|
|
void drawAncientCrate(Texture2D woodTex, Vector3 pos, Vector3 size) {
|
|
drawTexturedBox(woodTex, pos, size, 1.0f, 1.0f);
|
|
DrawCubeWires(pos, size.x + 0.015f, size.y + 0.015f, size.z + 0.015f, Color{ 145, 105, 55, 255 });
|
|
}
|
|
|
|
void drawMarbleColumn(Vector3 pos, float height, float radius) {
|
|
DrawCube({ pos.x, pos.y + 0.12f, pos.z }, radius * 2.4f, 0.24f, radius * 2.4f, Color{ 215, 210, 200, 255 });
|
|
DrawCylinder({ pos.x, pos.y + height * 0.5f, pos.z }, radius, radius * 0.95f, height - 0.48f, 16, Color{ 232, 226, 216, 255 });
|
|
DrawCylinderWires({ pos.x, pos.y + height * 0.5f, pos.z }, radius + 0.01f, radius * 0.95f + 0.01f, height - 0.48f, 16, Color{ 185, 180, 170, 255 });
|
|
DrawCube({ pos.x, pos.y + height - 0.12f, pos.z }, radius * 2.4f, 0.24f, radius * 2.4f, Color{ 215, 210, 200, 255 });
|
|
}
|
|
|
|
void drawMarbleNectarFountain(Vector3 pos, bool recharged, float animTime) {
|
|
// Stepped octagonal marble dais
|
|
DrawCylinder({ pos.x, pos.y + 0.08f, pos.z }, 1.6f, 1.7f, 0.16f, 16, Color{ 210, 205, 195, 255 });
|
|
DrawCylinder({ pos.x, pos.y + 0.24f, pos.z }, 1.3f, 1.4f, 0.16f, 16, Color{ 225, 220, 210, 255 });
|
|
|
|
// Basin pedestal and upper marble bowl
|
|
DrawCylinder({ pos.x, pos.y + 0.65f, pos.z }, 1.0f, 0.45f, 0.65f, 16, Color{ 240, 235, 225, 255 });
|
|
DrawCylinderWires({ pos.x, pos.y + 0.65f, pos.z }, 1.02f, 0.47f, 0.66f, 16, Color{ 185, 155, 95, 255 });
|
|
|
|
// Shimmering restorative nectar pool
|
|
float pulse = 0.5f + 0.5f * std::sin(animTime * 3.5f);
|
|
Color nectarCol = recharged ? Color{ 80, 230, 160, 235 } :
|
|
Color{ 255, 195, static_cast<unsigned char>(60 + pulse * 45), 235 };
|
|
DrawCylinder({ pos.x, pos.y + 0.88f, pos.z }, 0.88f, 0.88f, 0.12f, 16, nectarCol);
|
|
|
|
// Spouting urn in center
|
|
DrawCylinder({ pos.x, pos.y + 1.25f, pos.z }, 0.22f, 0.14f, 0.55f, 12, Color{ 185, 110, 70, 255 });
|
|
DrawCircle3D({ pos.x, pos.y + 1.05f + pulse * 0.12f, pos.z }, 0.55f, { 1.0f, 0.0f, 0.0f }, 90.0f, nectarCol);
|
|
DrawSphere({ pos.x, pos.y + 1.55f, pos.z }, 0.12f + pulse * 0.04f, WHITE);
|
|
}
|
|
|
|
void drawScribeStele(Vector3 pos) {
|
|
// Stele base plinth
|
|
DrawCube({ pos.x, pos.y + 0.15f, pos.z }, 0.90f, 0.30f, 0.70f, Color{ 195, 185, 170, 255 });
|
|
// Inscribed marble monolith
|
|
DrawCube({ pos.x, pos.y + 1.15f, pos.z }, 0.60f, 1.70f, 0.30f, Color{ 228, 222, 210, 255 });
|
|
DrawCubeWires({ pos.x, pos.y + 1.15f, pos.z }, 0.62f, 1.72f, 0.32f, Color{ 170, 135, 75, 255 });
|
|
|
|
// Engraved glowing inscription lines on East face (facing room center)
|
|
for (float ly = 0.55f; ly <= 1.75f; ly += 0.22f) {
|
|
DrawLine3D({ pos.x + 0.31f, pos.y + ly, pos.z - 0.22f },
|
|
{ pos.x + 0.31f, pos.y + ly, pos.z + 0.22f },
|
|
Color{ 240, 195, 60, 220 });
|
|
}
|
|
|
|
// Bronze reading lectern
|
|
DrawCube({ pos.x + 0.26f, pos.y + 0.88f, pos.z }, 0.26f, 0.06f, 0.55f, Color{ 145, 105, 45, 255 });
|
|
// Wax tablet ledger
|
|
DrawCube({ pos.x + 0.28f, pos.y + 0.93f, pos.z }, 0.18f, 0.02f, 0.38f, Color{ 210, 165, 95, 255 });
|
|
}
|
|
|
|
void drawCraftsmanWorkbench(Texture2D woodTex, Vector3 pos) {
|
|
// Heavy timber workbench top
|
|
drawTexturedBox(woodTex, { pos.x, pos.y + 0.75f, pos.z }, { 1.0f, 0.14f, 2.6f });
|
|
// 4 Heavy timber legs
|
|
DrawCube({ pos.x - 0.35f, 0.37f, pos.z - 1.05f }, 0.14f, 0.74f, 0.14f, Color{ 65, 42, 22, 255 });
|
|
DrawCube({ pos.x + 0.35f, 0.37f, pos.z - 1.05f }, 0.14f, 0.74f, 0.14f, Color{ 65, 42, 22, 255 });
|
|
DrawCube({ pos.x - 0.35f, 0.37f, pos.z + 1.05f }, 0.14f, 0.74f, 0.14f, Color{ 65, 42, 22, 255 });
|
|
DrawCube({ pos.x + 0.35f, 0.37f, pos.z + 1.05f }, 0.14f, 0.74f, 0.14f, Color{ 65, 42, 22, 255 });
|
|
|
|
// Bronze gunsmith anvil
|
|
DrawCube({ pos.x - 0.15f, 0.92f, pos.z - 0.60f }, 0.32f, 0.20f, 0.48f, Color{ 135, 95, 45, 255 });
|
|
DrawCylinder({ pos.x - 0.15f, 0.96f, pos.z - 0.90f }, 0.02f, 0.12f, 0.24f, 8, Color{ 165, 120, 60, 255 });
|
|
|
|
// Cartridge vise and bullet mold
|
|
DrawCube({ pos.x - 0.12f, 0.88f, pos.z + 0.20f }, 0.22f, 0.12f, 0.36f, Color{ 65, 70, 80, 255 });
|
|
DrawCube({ pos.x - 0.14f, 0.96f, pos.z + 0.20f }, 0.04f, 0.08f, 0.20f, Color{ 180, 140, 60, 255 });
|
|
|
|
// Terracotta crucible with glowing bronze / embers
|
|
DrawCylinder({ pos.x - 0.08f, 0.88f, pos.z + 0.75f }, 0.16f, 0.12f, 0.16f, 10, Color{ 180, 85, 45, 255 });
|
|
DrawSphere({ pos.x - 0.08f, 0.94f, pos.z + 0.75f }, 0.11f, Color{ 255, 160, 45, 255 });
|
|
|
|
// Ancient clay oil lamp with flame
|
|
DrawCylinder({ pos.x - 0.10f, 0.86f, pos.z - 0.05f }, 0.12f, 0.08f, 0.08f, 8, Color{ 190, 100, 55, 255 });
|
|
DrawSphere({ pos.x - 0.10f, 0.93f, pos.z - 0.05f }, 0.05f, Color{ 255, 225, 110, 255 });
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
CyberRenderer::CyberRenderer() = default;
|
|
|
|
CyberRenderer::~CyberRenderer() {
|
|
cleanup();
|
|
}
|
|
|
|
void CyberRenderer::init() {
|
|
m_camera.position = { 0.0f, 0.8f, 0.0f };
|
|
m_camera.target = { 0.0f, 0.8f, -1.0f };
|
|
m_camera.up = { 0.0f, 1.0f, 0.0f };
|
|
m_camera.fovy = 70.0f;
|
|
m_camera.projection = CAMERA_PERSPECTIVE;
|
|
|
|
// 1. Offscreen Render Target for 3D Post-Processing
|
|
m_renderTarget = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
|
|
SetTextureFilter(m_renderTarget.texture, TEXTURE_FILTER_BILINEAR);
|
|
|
|
// 2. Post-Processing GLSL Shader
|
|
std::vector<std::pair<std::string, std::string>> shaderPaths = {
|
|
{ "assets/shaders/postprocess.vs", "assets/shaders/postprocess.fs" },
|
|
{ "../assets/shaders/postprocess.vs", "../assets/shaders/postprocess.fs" }
|
|
};
|
|
for (const auto& sp : shaderPaths) {
|
|
if (FileExists(sp.first.c_str()) && FileExists(sp.second.c_str())) {
|
|
m_shaderPostProcess = LoadShader(sp.first.c_str(), sp.second.c_str());
|
|
if (m_shaderPostProcess.id != 0) {
|
|
m_locTime = GetShaderLocation(m_shaderPostProcess, "u_time");
|
|
m_locGlitch = GetShaderLocation(m_shaderPostProcess, "u_glitchIntensity");
|
|
m_locRes = GetShaderLocation(m_shaderPostProcess, "u_resolution");
|
|
m_postProcessLoaded = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
generateTextures();
|
|
}
|
|
|
|
void CyberRenderer::cleanup() {
|
|
if (m_postProcessLoaded) {
|
|
UnloadShader(m_shaderPostProcess);
|
|
UnloadRenderTexture(m_renderTarget);
|
|
m_postProcessLoaded = false;
|
|
}
|
|
unloadTextures();
|
|
}
|
|
|
|
void CyberRenderer::generateTextures() {
|
|
if (m_texturesLoaded) return;
|
|
|
|
m_texDungeonWall = createDungeonWallTexture();
|
|
m_texDungeonFloor = createDungeonFloorTexture();
|
|
m_texDungeonCeil = createDungeonCeilTexture();
|
|
m_texDungeonDoor = createBulkheadDoorTexture();
|
|
m_texDungeonDoorFrame = createDoorFrameTexture();
|
|
|
|
m_texParquet = createParquetTexture();
|
|
m_texRug = createRugTexture();
|
|
m_texPlasterWall = createWallTexture();
|
|
m_texPoster = createPosterTexture();
|
|
m_texDeskWood = createDeskWoodTexture();
|
|
m_texMonitorCode = createMonitorCodeTexture();
|
|
m_texMonitorRadar = createMonitorRadarTexture();
|
|
m_texBedQuilt = createBedQuiltTexture();
|
|
m_texCityNight = createCityNightTexture();
|
|
m_texCyberGate = createCyberGateTexture();
|
|
|
|
m_texAncientWall = createAncientStoneWallTexture();
|
|
m_texAncientFloor = createAncientFloorTexture();
|
|
m_texAncientCeil = createAncientCeilTexture();
|
|
m_texAncientWood = createAncientWoodTexture();
|
|
|
|
m_texturesLoaded = true;
|
|
}
|
|
|
|
void CyberRenderer::unloadTextures() {
|
|
if (!m_texturesLoaded) return;
|
|
|
|
UnloadTexture(m_texDungeonWall);
|
|
UnloadTexture(m_texDungeonFloor);
|
|
UnloadTexture(m_texDungeonCeil);
|
|
UnloadTexture(m_texDungeonDoor);
|
|
UnloadTexture(m_texDungeonDoorFrame);
|
|
|
|
UnloadTexture(m_texParquet);
|
|
UnloadTexture(m_texRug);
|
|
UnloadTexture(m_texPlasterWall);
|
|
UnloadTexture(m_texPoster);
|
|
UnloadTexture(m_texDeskWood);
|
|
UnloadTexture(m_texMonitorCode);
|
|
UnloadTexture(m_texMonitorRadar);
|
|
UnloadTexture(m_texBedQuilt);
|
|
UnloadTexture(m_texCityNight);
|
|
UnloadTexture(m_texCyberGate);
|
|
|
|
UnloadTexture(m_texAncientWall);
|
|
UnloadTexture(m_texAncientFloor);
|
|
UnloadTexture(m_texAncientCeil);
|
|
UnloadTexture(m_texAncientWood);
|
|
|
|
m_texturesLoaded = false;
|
|
}
|
|
|
|
|
|
void CyberRenderer::beginPostProcess() {
|
|
if (m_postProcessLoaded) {
|
|
BeginTextureMode(m_renderTarget);
|
|
ClearBackground(Color{ 4, 6, 12, 255 });
|
|
}
|
|
}
|
|
|
|
void CyberRenderer::endPostProcess(float animTime) {
|
|
if (m_postProcessLoaded) {
|
|
EndTextureMode();
|
|
|
|
float dt = GetFrameTime();
|
|
m_glitchIntensity = std::max(0.0f, m_glitchIntensity - dt * 2.5f);
|
|
|
|
SetShaderValue(m_shaderPostProcess, m_locTime, &animTime, SHADER_UNIFORM_FLOAT);
|
|
SetShaderValue(m_shaderPostProcess, m_locGlitch, &m_glitchIntensity, SHADER_UNIFORM_FLOAT);
|
|
float res[2] = { (float)GetScreenWidth(), (float)GetScreenHeight() };
|
|
SetShaderValue(m_shaderPostProcess, m_locRes, res, SHADER_UNIFORM_VEC2);
|
|
|
|
BeginShaderMode(m_shaderPostProcess);
|
|
DrawTextureRec(m_renderTarget.texture,
|
|
Rectangle{ 0.0f, 0.0f, (float)m_renderTarget.texture.width, -(float)m_renderTarget.texture.height },
|
|
Vector2{ 0.0f, 0.0f }, WHITE);
|
|
EndShaderMode();
|
|
}
|
|
}
|
|
|
|
void CyberRenderer::triggerGlitch(float intensity) {
|
|
m_glitchIntensity = std::max(m_glitchIntensity, intensity);
|
|
}
|
|
|
|
void CyberRenderer::renderCyberDrone(Vector3 pos, Color coreColor, float scale, float animTime, bool isHit, bool isAttacking, bool isElite) {
|
|
float bob = std::sin(animTime * 3.5f) * 0.12f;
|
|
Vector3 basePos = { pos.x, pos.y + bob, pos.z };
|
|
|
|
if (isHit) {
|
|
float shakeX = ((std::rand() % 100) / 100.0f - 0.5f) * 0.18f;
|
|
float shakeY = ((std::rand() % 100) / 100.0f - 0.5f) * 0.18f;
|
|
basePos.x += shakeX;
|
|
basePos.y += shakeY;
|
|
}
|
|
|
|
// Elite Aura and Orbiting Corona
|
|
if (isElite) {
|
|
float haloY = basePos.y + 0.48f * scale + 0.04f * std::sin(animTime * 4.0f);
|
|
DrawCircle3D({ basePos.x, haloY, basePos.z }, 0.55f * scale, { 1.0f, 0.0f, 0.0f }, 90.0f, CyberColors::NeonPurple);
|
|
DrawCircle3D({ basePos.x, haloY, basePos.z }, 0.40f * scale, { 1.0f, 0.0f, 0.0f }, 90.0f, CyberColors::NeonYellow);
|
|
for (int k = 0; k < 4; ++k) {
|
|
float spAngle = animTime * 2.5f + k * (3.14159265f * 0.5f);
|
|
Vector3 spPos = { basePos.x + std::cos(spAngle) * 0.48f * scale, haloY, basePos.z + std::sin(spAngle) * 0.48f * scale };
|
|
DrawCube(spPos, 0.08f * scale, 0.18f * scale, 0.08f * scale, CyberColors::NeonYellow);
|
|
DrawCubeWires(spPos, 0.09f * scale, 0.19f * scale, 0.09f * scale, CyberColors::NeonPurple);
|
|
}
|
|
}
|
|
|
|
// 1. Armored Faceted Chassis
|
|
DrawSphere(basePos, 0.42f * scale, Color{ 22, 28, 42, 255 });
|
|
DrawSphereWires(basePos, 0.44f * scale, 8, 8, Color{ 45, 60, 90, 255 });
|
|
|
|
// 2. Glowing Elemental Core / Reactor
|
|
float pulse = 0.5f + 0.5f * std::sin(animTime * (isAttacking ? 14.0f : 5.0f));
|
|
Color pulseCol = isHit ? WHITE : (isAttacking ? RED : coreColor);
|
|
DrawSphere(basePos, 0.28f * scale, pulseCol);
|
|
|
|
// 3. Forward Sensory Laser Visor
|
|
Vector3 visorPos = { basePos.x, basePos.y + 0.05f * scale, basePos.z - 0.38f * scale };
|
|
DrawCube(visorPos, 0.38f * scale, 0.12f * scale, 0.12f * scale, Color{ 14, 18, 26, 255 });
|
|
DrawCube(visorPos, 0.34f * scale, 0.08f * scale, 0.14f * scale, isAttacking ? RED : coreColor);
|
|
DrawCubeWires(visorPos, 0.36f * scale, 0.10f * scale, 0.14f * scale, WHITE);
|
|
|
|
// 4. Orbiting Deflection Shields (4 plates rotating around core)
|
|
float rot = animTime * 1.6f;
|
|
for (int i = 0; i < 4; ++i) {
|
|
float angle = rot + i * (3.14159265f * 0.5f);
|
|
float dist = 0.68f * scale;
|
|
Vector3 shieldPos = {
|
|
basePos.x + std::cos(angle) * dist,
|
|
basePos.y + std::sin(angle * 2.0f) * 0.10f * scale,
|
|
basePos.z + std::sin(angle) * dist
|
|
};
|
|
DrawCube(shieldPos, 0.18f * scale, 0.38f * scale, 0.06f * scale, Color{ 30, 42, 62, 255 });
|
|
DrawCubeWires(shieldPos, 0.19f * scale, 0.39f * scale, 0.07f * scale, coreColor);
|
|
}
|
|
|
|
// 5. Plasma Thruster & Exhaust Plume
|
|
Vector3 nozzlePos = { basePos.x, basePos.y - 0.40f * scale, basePos.z };
|
|
DrawCylinder(nozzlePos, 0.14f * scale, 0.18f * scale, 0.15f * scale, 8, Color{ 35, 45, 60, 255 });
|
|
float flameH = 0.25f + pulse * 0.15f;
|
|
Vector3 flamePos = { basePos.x, basePos.y - (0.45f + flameH * 0.5f) * scale, basePos.z };
|
|
DrawCylinder(flamePos, 0.10f * scale, 0.01f * scale, flameH * scale, 8, CyberColors::NeonCyan);
|
|
DrawSphere({ basePos.x, basePos.y - 0.45f * scale, basePos.z }, 0.08f * scale, WHITE);
|
|
}
|
|
|
|
void CyberRenderer::render3D(const Dungeon& dungeon, const Player& player, float animTime) {
|
|
if (!m_texturesLoaded) {
|
|
generateTextures();
|
|
}
|
|
|
|
beginPostProcess();
|
|
|
|
Vector3 eye = player.getVisualPosition();
|
|
m_camera.position = eye;
|
|
|
|
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
|
|
float dirX = std::sin(rad);
|
|
float dirZ = -std::cos(rad);
|
|
|
|
m_camera.target = { eye.x + dirX, eye.y, eye.z + dirZ };
|
|
|
|
BeginMode3D(m_camera);
|
|
|
|
const float tileSize = 2.0f;
|
|
const float wallHeight = 2.0f;
|
|
|
|
int pX = player.getGridX();
|
|
int pY = player.getGridY();
|
|
int renderDist = 7;
|
|
|
|
int minX = std::max(0, pX - renderDist);
|
|
int maxX = std::min(dungeon.getWidth() - 1, pX + renderDist);
|
|
int minY = std::max(0, pY - renderDist);
|
|
int maxY = std::min(dungeon.getHeight() - 1, pY + renderDist);
|
|
|
|
for (int y = minY; y <= maxY; ++y) {
|
|
for (int x = minX; x <= maxX; ++x) {
|
|
Vector3 center = { x * tileSize, 0.0f, y * tileSize };
|
|
TileType tile = dungeon.getTile(x, y);
|
|
|
|
// Atmospheric depth fog
|
|
float dist = std::sqrt(static_cast<float>((x - pX) * (x - pX) + (y - pY) * (y - pY)));
|
|
float fog = std::clamp((dist - 1.2f) / (renderDist - 1.2f), 0.0f, 1.0f);
|
|
float b = 1.0f - fog * 0.85f;
|
|
Color tileTint = Color{ static_cast<unsigned char>(255 * b), static_cast<unsigned char>(255 * b), static_cast<unsigned char>(255 * b), 255 };
|
|
|
|
if (tile == TileType::WALL) {
|
|
Vector3 wallCenter = { center.x, wallHeight * 0.5f, center.z };
|
|
Vector3 wallSize = { tileSize, wallHeight, tileSize };
|
|
|
|
drawTexturedBox(m_texDungeonWall, wallCenter, wallSize, 1.0f, 1.0f, tileTint);
|
|
|
|
Vector3 accentCenter = { center.x, wallHeight * 0.5f, center.z };
|
|
DrawCubeWires(accentCenter, wallSize.x * 1.008f, 0.06f, wallSize.z * 1.008f,
|
|
Color{ 0, static_cast<unsigned char>(220 * b), static_cast<unsigned char>(255 * b), 255 });
|
|
} else {
|
|
// Textured Floor
|
|
drawTexturedQuad(m_texDungeonFloor,
|
|
{ center.x - tileSize * 0.5f, -0.01f, center.z - tileSize * 0.5f },
|
|
{ center.x - tileSize * 0.5f, -0.01f, center.z + tileSize * 0.5f },
|
|
{ center.x + tileSize * 0.5f, -0.01f, center.z + tileSize * 0.5f },
|
|
{ center.x + tileSize * 0.5f, -0.01f, center.z - tileSize * 0.5f },
|
|
1.0f, 1.0f, tileTint);
|
|
|
|
// Textured Ceiling
|
|
drawTexturedQuad(m_texDungeonCeil,
|
|
{ center.x + tileSize * 0.5f, wallHeight, center.z - tileSize * 0.5f },
|
|
{ center.x + tileSize * 0.5f, wallHeight, center.z + tileSize * 0.5f },
|
|
{ center.x - tileSize * 0.5f, wallHeight, center.z + tileSize * 0.5f },
|
|
{ center.x - tileSize * 0.5f, wallHeight, center.z - tileSize * 0.5f },
|
|
1.0f, 1.0f, tileTint);
|
|
|
|
if (tile == TileType::DOOR_LOCKED || tile == TileType::DOOR_UNLOCKED) {
|
|
bool locked = (tile == TileType::DOOR_LOCKED);
|
|
bool wallLeftRight = (dungeon.getTile(x - 1, y) == TileType::WALL && dungeon.getTile(x + 1, y) == TileType::WALL);
|
|
|
|
if (wallLeftRight) {
|
|
Vector3 leftPost = { center.x - 0.9f, wallHeight * 0.5f, center.z };
|
|
drawTexturedBox(m_texDungeonDoorFrame, leftPost, { 0.2f, wallHeight, 0.35f }, 1.0f, 1.0f, tileTint);
|
|
|
|
Vector3 rightPost = { center.x + 0.9f, wallHeight * 0.5f, center.z };
|
|
drawTexturedBox(m_texDungeonDoorFrame, rightPost, { 0.2f, wallHeight, 0.35f }, 1.0f, 1.0f, tileTint);
|
|
|
|
Vector3 topLintel = { center.x, wallHeight - 0.1f, center.z };
|
|
drawTexturedBox(m_texDungeonDoorFrame, topLintel, { 1.8f, 0.2f, 0.35f }, 2.0f, 1.0f, tileTint);
|
|
|
|
if (locked) {
|
|
Vector3 doorBody = { center.x, (wallHeight - 0.2f) * 0.5f, center.z };
|
|
drawTexturedBox(m_texDungeonDoor, doorBody, { 1.6f, wallHeight - 0.2f, 0.2f }, 1.0f, 1.0f, tileTint);
|
|
|
|
float pulse = 0.5f + 0.5f * std::sin(animTime * 6.0f);
|
|
Color lockCol = (pulse > 0.5f) ? CyberColors::NeonOrange : RED;
|
|
Vector3 lockCore = { center.x, 0.9f, center.z };
|
|
DrawCube(lockCore, 0.4f, 0.25f, 0.24f, lockCol);
|
|
DrawCubeWires(lockCore, 0.42f, 0.27f, 0.26f, CyberColors::NeonYellow);
|
|
} else {
|
|
Vector3 leftSlab = { center.x - 0.7f, (wallHeight - 0.2f) * 0.5f, center.z };
|
|
Vector3 rightSlab = { center.x + 0.7f, (wallHeight - 0.2f) * 0.5f, center.z };
|
|
drawTexturedBox(m_texDungeonDoor, leftSlab, { 0.25f, wallHeight - 0.2f, 0.18f }, 0.5f, 1.0f, tileTint);
|
|
drawTexturedBox(m_texDungeonDoor, rightSlab, { 0.25f, wallHeight - 0.2f, 0.18f }, 0.5f, 1.0f, tileTint);
|
|
}
|
|
} else {
|
|
Vector3 northPost = { center.x, wallHeight * 0.5f, center.z - 0.9f };
|
|
drawTexturedBox(m_texDungeonDoorFrame, northPost, { 0.35f, wallHeight, 0.2f }, 1.0f, 1.0f, tileTint);
|
|
|
|
Vector3 southPost = { center.x, wallHeight * 0.5f, center.z + 0.9f };
|
|
drawTexturedBox(m_texDungeonDoorFrame, southPost, { 0.35f, wallHeight, 0.2f }, 1.0f, 1.0f, tileTint);
|
|
|
|
Vector3 topLintel = { center.x, wallHeight - 0.1f, center.z };
|
|
drawTexturedBox(m_texDungeonDoorFrame, topLintel, { 0.35f, 0.2f, 1.8f }, 1.0f, 2.0f, tileTint);
|
|
|
|
if (locked) {
|
|
Vector3 doorBody = { center.x, (wallHeight - 0.2f) * 0.5f, center.z };
|
|
drawTexturedBox(m_texDungeonDoor, doorBody, { 0.2f, wallHeight - 0.2f, 1.6f }, 1.0f, 1.0f, tileTint);
|
|
|
|
float pulse = 0.5f + 0.5f * std::sin(animTime * 6.0f);
|
|
Color lockCol = (pulse > 0.5f) ? CyberColors::NeonOrange : RED;
|
|
Vector3 lockCore = { center.x, 0.9f, center.z };
|
|
DrawCube(lockCore, 0.24f, 0.25f, 0.4f, lockCol);
|
|
DrawCubeWires(lockCore, 0.26f, 0.27f, 0.42f, CyberColors::NeonYellow);
|
|
} else {
|
|
Vector3 northSlab = { center.x, (wallHeight - 0.2f) * 0.5f, center.z - 0.7f };
|
|
Vector3 southSlab = { center.x, (wallHeight - 0.2f) * 0.5f, center.z + 0.7f };
|
|
drawTexturedBox(m_texDungeonDoor, northSlab, { 0.18f, wallHeight - 0.2f, 0.25f }, 0.5f, 1.0f, tileTint);
|
|
drawTexturedBox(m_texDungeonDoor, southSlab, { 0.18f, wallHeight - 0.2f, 0.25f }, 0.5f, 1.0f, tileTint);
|
|
}
|
|
}
|
|
} else if (tile == TileType::LOOT_CACHE) {
|
|
float floatY = 0.6f + std::sin(animTime * 3.0f + x) * 0.12f;
|
|
Vector3 lootPos = { center.x, floatY, center.z };
|
|
DrawCubeWires(lootPos, 0.6f, 0.6f, 0.6f, CyberColors::NeonYellow);
|
|
DrawCube(lootPos, 0.35f, 0.35f, 0.35f, Color{ 255, 230, 0, 180 });
|
|
} else if (tile == TileType::EXIT_PORTAL) {
|
|
float pulse = 0.5f + 0.5f * std::sin(animTime * 4.0f);
|
|
|
|
// 1. Raised Octagonal Platform Dais with hazard glow
|
|
Vector3 basePos = { center.x, 0.10f, center.z };
|
|
DrawCylinder(basePos, 0.95f, 1.0f, 0.20f, 8, Color{ 22, 30, 44, 255 });
|
|
DrawCylinderWires(basePos, 0.96f, 1.01f, 0.20f, 8, CyberColors::NeonCyan);
|
|
DrawCircle3D({ center.x, 0.21f, center.z }, 0.90f, { 1.0f, 0.0f, 0.0f }, 90.0f, Color{ 0, 240, 255, 120 });
|
|
|
|
// 2. Four Corner Energy Emitter Pylons with glowing caps
|
|
float pylonDist = 0.72f;
|
|
Vector3 pylons[4] = {
|
|
{ center.x - pylonDist, 0.42f, center.z - pylonDist },
|
|
{ center.x + pylonDist, 0.42f, center.z - pylonDist },
|
|
{ center.x - pylonDist, 0.42f, center.z + pylonDist },
|
|
{ center.x + pylonDist, 0.42f, center.z + pylonDist }
|
|
};
|
|
for (int p = 0; p < 4; ++p) {
|
|
DrawCylinder(pylons[p], 0.06f, 0.08f, 0.65f, 8, Color{ 36, 46, 66, 255 });
|
|
DrawCylinderWires(pylons[p], 0.065f, 0.085f, 0.65f, 8, CyberColors::NeonCyan);
|
|
DrawSphere({ pylons[p].x, 0.76f, pylons[p].z }, 0.08f, Color{ 0, 255, 200, 255 });
|
|
}
|
|
|
|
// 3. Three Concentric Levitating Counter-Rotating Neon Rings
|
|
float ring1Y = 0.42f + 0.05f * std::sin(animTime * 3.0f);
|
|
float ring2Y = 0.88f + 0.07f * std::sin(animTime * 3.5f + 1.2f);
|
|
float ring3Y = 1.34f + 0.05f * std::sin(animTime * 2.8f + 2.4f);
|
|
|
|
DrawCircle3D({ center.x, ring1Y, center.z }, 0.75f, { 1.0f, 0.0f, 0.0f }, 90.0f,
|
|
Color{ 0, 255, 180, static_cast<unsigned char>(160 + pulse * 95) });
|
|
DrawCircle3D({ center.x, ring2Y, center.z }, 0.55f, { 1.0f, 0.0f, 0.0f }, 90.0f,
|
|
Color{ 0, 240, 255, static_cast<unsigned char>(180 + pulse * 75) });
|
|
DrawCircle3D({ center.x, ring3Y, center.z }, 0.35f, { 1.0f, 0.0f, 0.0f }, 90.0f,
|
|
Color{ 180, 0, 255, static_cast<unsigned char>(160 + pulse * 95) });
|
|
|
|
// 4. Central Swirling Singularity Core
|
|
Vector3 corePos = { center.x, 0.85f + pulse * 0.05f, center.z };
|
|
DrawCylinder(corePos, 0.20f + pulse * 0.03f, 0.20f + pulse * 0.03f, 1.3f, 16,
|
|
Color{ 0, 230, 255, static_cast<unsigned char>(130 + pulse * 80) });
|
|
DrawCylinderWires(corePos, 0.23f, 0.23f, 1.32f, 8, CyberColors::NeonGreen);
|
|
DrawSphere(corePos, 0.18f + pulse * 0.04f, Color{ 255, 255, 255, 220 });
|
|
|
|
// 5. Overhead Holographic Beacon Frame
|
|
Vector3 signPos = { center.x, wallHeight - 0.20f, center.z };
|
|
DrawCube(signPos, 1.3f, 0.18f, 0.06f, Color{ 10, 16, 26, 240 });
|
|
DrawCubeWires(signPos, 1.32f, 0.20f, 0.08f, CyberColors::NeonCyan);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Render Laser Barriers
|
|
for (const auto& l : dungeon.getLasers()) {
|
|
if (l.disarmed) continue;
|
|
if (std::abs(l.x - pX) > renderDist || std::abs(l.y - pY) > renderDist) continue;
|
|
Vector3 lCenter = { l.x * tileSize, 0.9f, l.y * tileSize };
|
|
|
|
if (l.vertical) {
|
|
DrawCube({ lCenter.x, lCenter.y, lCenter.z - 0.95f }, 0.25f, 0.25f, 0.10f, Color{ 60, 65, 80, 255 });
|
|
DrawCube({ lCenter.x, lCenter.y, lCenter.z + 0.95f }, 0.25f, 0.25f, 0.10f, Color{ 60, 65, 80, 255 });
|
|
if (l.active) {
|
|
float pulse = 0.5f + 0.5f * std::sin(animTime * 12.0f);
|
|
DrawCylinderEx({ lCenter.x, lCenter.y, lCenter.z - 0.95f }, { lCenter.x, lCenter.y, lCenter.z + 0.95f }, 0.04f, 0.04f, 8, RED);
|
|
DrawCylinderEx({ lCenter.x, lCenter.y, lCenter.z - 0.95f }, { lCenter.x, lCenter.y, lCenter.z + 0.95f }, 0.12f, 0.12f, 8, Color{ 255, 0, 50, static_cast<unsigned char>(70 + pulse * 60) });
|
|
} else {
|
|
DrawCylinderEx({ lCenter.x, lCenter.y, lCenter.z - 0.95f }, { lCenter.x, lCenter.y, lCenter.z + 0.95f }, 0.02f, 0.02f, 6, Color{ 0, 255, 120, 90 });
|
|
}
|
|
} else {
|
|
DrawCube({ lCenter.x - 0.95f, lCenter.y, lCenter.z }, 0.10f, 0.25f, 0.25f, Color{ 60, 65, 80, 255 });
|
|
DrawCube({ lCenter.x + 0.95f, lCenter.y, lCenter.z }, 0.10f, 0.25f, 0.25f, Color{ 60, 65, 80, 255 });
|
|
if (l.active) {
|
|
float pulse = 0.5f + 0.5f * std::sin(animTime * 12.0f);
|
|
DrawCylinderEx({ lCenter.x - 0.95f, lCenter.y, lCenter.z }, { lCenter.x + 0.95f, lCenter.y, lCenter.z }, 0.04f, 0.04f, 8, RED);
|
|
DrawCylinderEx({ lCenter.x - 0.95f, lCenter.y, lCenter.z }, { lCenter.x + 0.95f, lCenter.y, lCenter.z }, 0.12f, 0.12f, 8, Color{ 255, 0, 50, static_cast<unsigned char>(70 + pulse * 60) });
|
|
} else {
|
|
DrawCylinderEx({ lCenter.x - 0.95f, lCenter.y, lCenter.z }, { lCenter.x + 0.95f, lCenter.y, lCenter.z }, 0.02f, 0.02f, 6, Color{ 0, 255, 120, 90 });
|
|
}
|
|
}
|
|
}
|
|
|
|
// Render Proximity Mines
|
|
for (const auto& m : dungeon.getMines()) {
|
|
if (m.defused || m.triggered) continue;
|
|
if (std::abs(m.x - pX) > renderDist || std::abs(m.y - pY) > renderDist) continue;
|
|
Vector3 mCenter = { m.x * tileSize, 0.04f, m.y * tileSize };
|
|
|
|
DrawCylinder(mCenter, 0.38f, 0.42f, 0.08f, 16, Color{ 32, 38, 50, 255 });
|
|
DrawCylinderWires(mCenter, 0.39f, 0.43f, 0.08f, 16, Color{ 80, 90, 110, 255 });
|
|
|
|
float blink = 0.5f + 0.5f * std::sin(animTime * 10.0f);
|
|
Color ledCol = (blink > 0.4f) ? RED : Color{ 120, 20, 30, 255 };
|
|
DrawSphere({ mCenter.x, 0.12f, mCenter.z }, 0.10f, ledCol);
|
|
DrawCircle3D({ mCenter.x, 0.09f, mCenter.z }, 0.28f, { 1.0f, 0.0f, 0.0f }, 90.0f, Color{ 255, 120, 0, static_cast<unsigned char>(100 + blink * 100) });
|
|
}
|
|
|
|
// Render Cyber Shrines
|
|
for (const auto& s : dungeon.getShrines()) {
|
|
if (std::abs(s.x - pX) > renderDist || std::abs(s.y - pY) > renderDist) continue;
|
|
Vector3 sCenter = { s.x * tileSize, 0.0f, s.y * tileSize };
|
|
|
|
DrawCylinder({ sCenter.x, 0.10f, sCenter.z }, 0.9f, 1.0f, 0.20f, 8, Color{ 20, 14, 30, 255 });
|
|
DrawCylinderWires({ sCenter.x, 0.10f, sCenter.z }, 0.92f, 1.02f, 0.20f, 8, CyberColors::NeonPurple);
|
|
|
|
DrawCube({ sCenter.x, 0.95f, sCenter.z }, 0.55f, 1.50f, 0.55f, Color{ 14, 10, 22, 255 });
|
|
DrawCubeWires({ sCenter.x, 0.95f, sCenter.z }, 0.57f, 1.52f, 0.57f, s.used ? GRAY : CyberColors::NeonPurple);
|
|
|
|
float pulse = 0.5f + 0.5f * std::sin(animTime * 3.5f);
|
|
Vector3 runePos = { sCenter.x, 1.95f + pulse * 0.10f, sCenter.z };
|
|
Color runeCol = s.used ? GRAY : Color{ 255, 40, 160, static_cast<unsigned char>(180 + pulse * 75) };
|
|
DrawSphere(runePos, 0.18f + pulse * 0.04f, runeCol);
|
|
DrawCircle3D(runePos, 0.40f, { 1.0f, 0.0f, 0.0f }, 90.0f, runeCol);
|
|
}
|
|
|
|
// Render Corridor Patrol Enemies as 3D Cyber-Drones
|
|
auto& enemies = const_cast<Dungeon&>(dungeon).getEnemies();
|
|
for (const auto& me : enemies) {
|
|
if (!me.alive) continue;
|
|
if (std::abs(me.x - pX) > renderDist || std::abs(me.y - pY) > renderDist) continue;
|
|
|
|
Vector3 ePos = { me.x * tileSize, 0.9f, me.y * tileSize };
|
|
renderCyberDrone(ePos, me.enemy.getColor(), 1.0f, animTime, false, false, me.enemy.isElite());
|
|
}
|
|
|
|
EndMode3D();
|
|
|
|
endPostProcess(animTime);
|
|
}
|
|
|
|
void CyberRenderer::renderCombat3D(const Dungeon& dungeon, const Player& player, const CombatSystem& combat, float animTime) {
|
|
if (!m_texturesLoaded) {
|
|
generateTextures();
|
|
}
|
|
|
|
beginPostProcess();
|
|
|
|
Vector3 eye = player.getVisualPosition();
|
|
m_camera.position = eye;
|
|
|
|
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
|
|
float dirX = std::sin(rad);
|
|
float dirZ = -std::cos(rad);
|
|
|
|
m_camera.target = { eye.x + dirX, eye.y, eye.z + dirZ };
|
|
|
|
BeginMode3D(m_camera);
|
|
|
|
const float tileSize = 2.0f;
|
|
const float wallHeight = 2.0f;
|
|
int pX = player.getGridX();
|
|
int pY = player.getGridY();
|
|
int renderDist = 5;
|
|
|
|
int minX = std::max(0, pX - renderDist);
|
|
int maxX = std::min(dungeon.getWidth() - 1, pX + renderDist);
|
|
int minY = std::max(0, pY - renderDist);
|
|
int maxY = std::min(dungeon.getHeight() - 1, pY + renderDist);
|
|
|
|
// 1. Render surrounding corridor geometry
|
|
for (int y = minY; y <= maxY; ++y) {
|
|
for (int x = minX; x <= maxX; ++x) {
|
|
Vector3 center = { x * tileSize, 0.0f, y * tileSize };
|
|
TileType tile = dungeon.getTile(x, y);
|
|
|
|
float dist = std::sqrt(static_cast<float>((x - pX) * (x - pX) + (y - pY) * (y - pY)));
|
|
float fog = std::clamp((dist - 1.0f) / 4.0f, 0.0f, 1.0f);
|
|
float b = 1.0f - fog * 0.85f;
|
|
Color tileTint = Color{ static_cast<unsigned char>(255 * b), static_cast<unsigned char>(255 * b), static_cast<unsigned char>(255 * b), 255 };
|
|
|
|
if (tile == TileType::WALL) {
|
|
Vector3 wallCenter = { center.x, wallHeight * 0.5f, center.z };
|
|
drawTexturedBox(m_texDungeonWall, wallCenter, { tileSize, wallHeight, tileSize }, 1.0f, 1.0f, tileTint);
|
|
} else {
|
|
drawTexturedQuad(m_texDungeonFloor,
|
|
{ center.x - tileSize * 0.5f, -0.01f, center.z - tileSize * 0.5f },
|
|
{ center.x - tileSize * 0.5f, -0.01f, center.z + tileSize * 0.5f },
|
|
{ center.x + tileSize * 0.5f, -0.01f, center.z + tileSize * 0.5f },
|
|
{ center.x + tileSize * 0.5f, -0.01f, center.z - tileSize * 0.5f },
|
|
1.0f, 1.0f, tileTint);
|
|
|
|
drawTexturedQuad(m_texDungeonCeil,
|
|
{ center.x + tileSize * 0.5f, wallHeight, center.z - tileSize * 0.5f },
|
|
{ center.x + tileSize * 0.5f, wallHeight, center.z + tileSize * 0.5f },
|
|
{ center.x - tileSize * 0.5f, wallHeight, center.z + tileSize * 0.5f },
|
|
{ center.x - tileSize * 0.5f, wallHeight, center.z - tileSize * 0.5f },
|
|
1.0f, 1.0f, tileTint);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. STAGE ENEMY 3D DRONE IN FRONT OF PLAYER
|
|
const auto& enemy = combat.getEnemy();
|
|
if (enemy && enemy->isAlive()) {
|
|
Vector3 combatDronePos = {
|
|
eye.x + dirX * 2.5f,
|
|
eye.y + 0.02f,
|
|
eye.z + dirZ * 2.5f
|
|
};
|
|
|
|
bool isHit = (combat.getPhase() == CombatPhase::PLAYER_ACTION_ANIM);
|
|
bool isAttacking = (combat.getPhase() == CombatPhase::ENEMY_ACTION_ANIM);
|
|
|
|
renderCyberDrone(combatDronePos, enemy->getColor(), 1.5f, animTime, isHit, isAttacking, enemy->isElite());
|
|
}
|
|
|
|
EndMode3D();
|
|
|
|
endPostProcess(animTime);
|
|
}
|
|
|
|
void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float animTime) {
|
|
if (!m_texturesLoaded) {
|
|
generateTextures();
|
|
}
|
|
|
|
Vector3 eye = player.getVisualPosition();
|
|
m_camera.position = eye;
|
|
|
|
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
|
|
float dirX = std::sin(rad);
|
|
float dirZ = -std::cos(rad);
|
|
|
|
m_camera.target = { eye.x + dirX, eye.y, eye.z + dirZ };
|
|
|
|
BeginMode3D(m_camera);
|
|
|
|
const float tileSize = 2.0f;
|
|
const float roomHeight = 2.8f;
|
|
const float minCoord = 0.6f;
|
|
const float maxCoord = 11.4f;
|
|
|
|
// 1. FLOOR: Warm Oak Parquet Floor (snug room = 10.8m x 10.8m)
|
|
drawTexturedQuad(m_texParquet,
|
|
{ minCoord, 0.0f, minCoord },
|
|
{ minCoord, 0.0f, maxCoord },
|
|
{ maxCoord, 0.0f, maxCoord },
|
|
{ maxCoord, 0.0f, minCoord },
|
|
5.5f, 5.5f);
|
|
|
|
// 2. LUXURY RUG: Centered in room (6.0m, 6.0m), size 4.0m x 4.0m
|
|
Vector3 rugCenter = { 6.0f, 0.02f, 6.0f };
|
|
float rw = 2.0f;
|
|
float rl = 2.0f;
|
|
drawTexturedQuad(m_texRug,
|
|
{ rugCenter.x - rw, 0.02f, rugCenter.z - rl },
|
|
{ rugCenter.x - rw, 0.02f, rugCenter.z + rl },
|
|
{ rugCenter.x + rw, 0.02f, rugCenter.z + rl },
|
|
{ rugCenter.x + rw, 0.02f, rugCenter.z - rl },
|
|
1.0f, 1.0f);
|
|
|
|
// 3. CEILING: Solid ceiling covering the entire room and walls
|
|
DrawCube({ 6.0f, roomHeight + 0.10f, 6.0f }, 11.6f, 0.20f, 11.6f, Color{ 32, 36, 44, 255 });
|
|
DrawCube({ 6.0f, roomHeight - 0.02f, 6.0f }, 2.4f, 0.04f, 2.4f, Color{ 255, 245, 220, 255 }); // Warm LED ceiling lamp
|
|
DrawCubeWires({ 6.0f, roomHeight - 0.02f, 6.0f }, 2.42f, 0.04f, 2.42f, Color{ 180, 140, 80, 255 }); // Brass fixture
|
|
|
|
// 4. SURROUNDING WALLS (Solid textured boxes with 0.24m thickness)
|
|
const float wallThick = 0.24f;
|
|
const float halfThick = wallThick * 0.5f;
|
|
|
|
// South Wall (Z = maxCoord = 11.4f)
|
|
drawTexturedBox(m_texPlasterWall,
|
|
{ 6.0f, roomHeight * 0.5f, maxCoord + halfThick },
|
|
{ 10.8f + wallThick * 2.0f, roomHeight, wallThick },
|
|
5.5f, 1.4f);
|
|
|
|
// West Wall (X = minCoord = 0.6f)
|
|
drawTexturedBox(m_texPlasterWall,
|
|
{ minCoord - halfThick, roomHeight * 0.5f, 6.0f },
|
|
{ wallThick, roomHeight, 10.8f + wallThick * 2.0f },
|
|
5.5f, 1.4f);
|
|
|
|
// North Wall: Split around Gateway (Gateway is at X = 6.0f, width 2.6f: X in [4.7, 7.3])
|
|
// Left Segment: X from minCoord (0.6f) to 4.7f => length 4.1f, center 2.65f
|
|
drawTexturedBox(m_texPlasterWall,
|
|
{ 2.65f, roomHeight * 0.5f, minCoord - halfThick },
|
|
{ 4.1f + wallThick, roomHeight, wallThick },
|
|
2.1f, 1.4f);
|
|
// Right Segment: X from 7.3f to maxCoord (11.4f) => length 4.1f, center 9.35f
|
|
drawTexturedBox(m_texPlasterWall,
|
|
{ 9.35f, roomHeight * 0.5f, minCoord - halfThick },
|
|
{ 4.1f + wallThick, roomHeight, wallThick },
|
|
2.1f, 1.4f);
|
|
// Lintel above Gateway: X in [4.7f, 7.3f], Y from 2.4f to roomHeight (0.4f height)
|
|
drawTexturedBox(m_texPlasterWall,
|
|
{ 6.0f, 2.6f, minCoord - halfThick },
|
|
{ 2.6f, 0.4f, wallThick },
|
|
1.3f, 0.2f);
|
|
|
|
// East Wall: Split around Window (Window at Z in [3.6f, 8.4f], Y in [0.8f, 2.4f])
|
|
// North Segment: Z from minCoord (0.6f) to 3.6f => length 3.0f, center 2.1f
|
|
drawTexturedBox(m_texPlasterWall,
|
|
{ maxCoord + halfThick, roomHeight * 0.5f, 2.1f },
|
|
{ wallThick, roomHeight, 3.0f + wallThick },
|
|
1.5f, 1.4f);
|
|
// South Segment: Z from 8.4f to maxCoord (11.4f) => length 3.0f, center 9.9f
|
|
drawTexturedBox(m_texPlasterWall,
|
|
{ maxCoord + halfThick, roomHeight * 0.5f, 9.9f },
|
|
{ wallThick, roomHeight, 3.0f + wallThick },
|
|
1.5f, 1.4f);
|
|
// Below Window: Z in [3.6f, 8.4f] (length 4.8f), Y from 0.0f to 0.8f (height 0.8f, center 0.4f)
|
|
drawTexturedBox(m_texPlasterWall,
|
|
{ maxCoord + halfThick, 0.4f, 6.0f },
|
|
{ wallThick, 0.8f, 4.8f },
|
|
2.4f, 0.4f);
|
|
// Above Window: Z in [3.6f, 8.4f] (length 4.8f), Y from 2.4f to roomHeight (height 0.4f, center 2.6f)
|
|
drawTexturedBox(m_texPlasterWall,
|
|
{ maxCoord + halfThick, 2.6f, 6.0f },
|
|
{ wallThick, 0.4f, 4.8f },
|
|
2.4f, 0.2f);
|
|
|
|
// Baseboards along bottom of walls inside room
|
|
DrawCube({ 6.0f, 0.08f, maxCoord - 0.04f }, 10.8f, 0.16f, 0.08f, Color{ 75, 48, 30, 255 });
|
|
DrawCube({ minCoord + 0.04f, 0.08f, 6.0f }, 0.08f, 0.16f, 10.8f, Color{ 75, 48, 30, 255 });
|
|
DrawCube({ 2.65f, 0.08f, minCoord + 0.04f }, 4.1f, 0.16f, 0.08f, Color{ 75, 48, 30, 255 });
|
|
DrawCube({ 9.35f, 0.08f, minCoord + 0.04f }, 4.1f, 0.16f, 0.08f, Color{ 75, 48, 30, 255 });
|
|
DrawCube({ maxCoord - 0.04f, 0.08f, 2.1f }, 0.08f, 0.16f, 3.0f, Color{ 75, 48, 30, 255 });
|
|
DrawCube({ maxCoord - 0.04f, 0.08f, 9.9f }, 0.08f, 0.16f, 3.0f, Color{ 75, 48, 30, 255 });
|
|
|
|
// 5. FRAMED SYNTHWAVE POSTER: South Wall (Z = maxCoord - 0.01f, centered at X = 3.8f, Y = 1.55f)
|
|
drawTexturedQuad(m_texPoster,
|
|
{ 3.0f, 2.35f, maxCoord - 0.01f },
|
|
{ 3.0f, 0.75f, maxCoord - 0.01f },
|
|
{ 4.6f, 0.75f, maxCoord - 0.01f },
|
|
{ 4.6f, 2.35f, maxCoord - 0.01f },
|
|
1.0f, 1.0f);
|
|
DrawCubeWires({ 3.8f, 1.55f, maxCoord - 0.005f }, 1.62f, 1.62f, 0.02f, Color{ 20, 20, 25, 255 });
|
|
|
|
// 6. PANORAMIC WINDOW & NIGHT CITY SKYLINE (East Wall, X = maxCoord = 11.4f)
|
|
// Step A: Outdoor Megacity Backdrop Quad (rendered first so depth test passes for everything in front)
|
|
drawTexturedQuad(m_texCityNight,
|
|
{ 16.5f, 6.0f, -2.0f },
|
|
{ 16.5f, -2.0f, -2.0f },
|
|
{ 16.5f, -2.0f, 14.0f },
|
|
{ 16.5f, 6.0f, 14.0f },
|
|
1.0f, 1.0f);
|
|
|
|
// Step B: 3D Skyscraper buildings outside window (X = 13.5 .. 14.8)
|
|
Vector3 bldPositions[4] = {
|
|
{ 14.0f, 1.2f, 4.4f },
|
|
{ 14.6f, 1.7f, 5.8f },
|
|
{ 14.2f, 2.1f, 7.0f },
|
|
{ 14.5f, 1.1f, 8.0f }
|
|
};
|
|
for (int i = 0; i < 4; ++i) {
|
|
float bldH = 3.4f + (i % 3) * 1.2f;
|
|
Vector3 bldPos = { bldPositions[i].x, bldH * 0.5f - 0.5f, bldPositions[i].z };
|
|
DrawCube(bldPos, 1.6f, bldH, 1.4f, Color{ 12, 16, 28, 255 });
|
|
DrawCubeWires(bldPos, 1.6f, bldH, 1.4f, (i % 2 == 0) ? CyberColors::NeonCyan : CyberColors::NeonPink);
|
|
// Roof antenna beacon
|
|
DrawLine3D({ bldPos.x, bldH - 0.5f, bldPos.z }, { bldPos.x, bldH + 0.45f, bldPos.z }, Color{ 140, 150, 170, 255 });
|
|
DrawSphere({ bldPos.x, bldH + 0.48f, bldPos.z }, 0.06f, Color{ 255, 40, 40, 255 });
|
|
}
|
|
|
|
// Step C: Window frame, mullions, and wooden sill in the wall opening
|
|
DrawCubeWires({ maxCoord, 1.6f, 6.0f }, 0.28f, 1.62f, 4.82f, Color{ 15, 20, 30, 255 });
|
|
DrawCube({ maxCoord, 1.6f, 6.0f }, 0.08f, 1.6f, 0.06f, Color{ 22, 28, 40, 255 }); // Center vertical mullion
|
|
DrawCube({ maxCoord, 1.6f, 6.0f }, 0.08f, 0.06f, 4.8f, Color{ 22, 28, 40, 255 }); // Center horizontal mullion
|
|
DrawCube({ maxCoord - 0.12f, 0.78f, 6.0f }, 0.32f, 0.06f, 5.0f, Color{ 85, 55, 35, 255 }); // Interior wooden sill
|
|
|
|
// Step D: Translucent Glass Reflections (depth mask DISABLED so city behind is NEVER occluded!)
|
|
rlDisableDepthMask();
|
|
DrawCube({ maxCoord, 1.6f, 6.0f }, 0.01f, 1.56f, 4.74f, Color{ 0, 160, 230, 20 });
|
|
DrawLine3D({ maxCoord - 0.01f, 2.25f, 4.3f }, { maxCoord - 0.01f, 0.95f, 5.6f }, Color{ 255, 255, 255, 55 });
|
|
DrawLine3D({ maxCoord - 0.01f, 2.15f, 6.5f }, { maxCoord - 0.01f, 0.95f, 7.7f }, Color{ 255, 255, 255, 45 });
|
|
rlEnableDepthMask();
|
|
|
|
// 7. COMPUTER WORKSTATION DESK (West Wall: against X = minCoord = 0.6f, Z = 5.0f)
|
|
Vector3 deskPos = { 1.05f, 0.75f, 5.0f };
|
|
Vector3 deskSize = { 0.85f, 0.08f, 2.2f };
|
|
drawTexturedBox(m_texDeskWood, deskPos, deskSize, 1.0f, 2.0f);
|
|
|
|
// Metal legs
|
|
DrawCube({ 0.70f, 0.36f, 4.0f }, 0.08f, 0.72f, 0.08f, Color{ 40, 44, 52, 255 });
|
|
DrawCube({ 1.40f, 0.36f, 4.0f }, 0.08f, 0.72f, 0.08f, Color{ 40, 44, 52, 255 });
|
|
DrawCube({ 0.70f, 0.36f, 6.0f }, 0.08f, 0.72f, 0.08f, Color{ 40, 44, 52, 255 });
|
|
DrawCube({ 1.40f, 0.36f, 6.0f }, 0.08f, 0.72f, 0.08f, Color{ 40, 44, 52, 255 });
|
|
|
|
// Primary Monitor (IDE code editor): facing East (+X) toward the player
|
|
Vector3 mon1Pos = { 0.90f, 1.25f, 4.5f };
|
|
DrawCube(mon1Pos, 0.08f, 0.75f, 0.95f, Color{ 18, 22, 30, 255 });
|
|
DrawCube({ mon1Pos.x, 0.92f, mon1Pos.z }, 0.08f, 0.32f, 0.12f, Color{ 35, 40, 50, 255 });
|
|
DrawCube({ mon1Pos.x + 0.05f, 0.79f, mon1Pos.z }, 0.22f, 0.02f, 0.32f, Color{ 35, 40, 50, 255 });
|
|
|
|
// Primary Monitor Screen Quad with C++ Code
|
|
drawTexturedQuad(m_texMonitorCode,
|
|
{ mon1Pos.x + 0.045f, mon1Pos.y + 0.34f, mon1Pos.z + 0.44f },
|
|
{ mon1Pos.x + 0.045f, mon1Pos.y - 0.34f, mon1Pos.z + 0.44f },
|
|
{ mon1Pos.x + 0.045f, mon1Pos.y - 0.34f, mon1Pos.z - 0.44f },
|
|
{ mon1Pos.x + 0.045f, mon1Pos.y + 0.34f, mon1Pos.z - 0.44f },
|
|
1.0f, 1.0f);
|
|
|
|
// Secondary Monitor (Cyberspace Radar): angled slightly
|
|
Vector3 mon2Pos = { 0.92f, 1.25f, 5.5f };
|
|
DrawCube(mon2Pos, 0.08f, 0.75f, 0.85f, Color{ 18, 22, 30, 255 });
|
|
DrawCube({ mon2Pos.x, 0.92f, mon2Pos.z }, 0.08f, 0.32f, 0.12f, Color{ 35, 40, 50, 255 });
|
|
DrawCube({ mon2Pos.x + 0.05f, 0.79f, mon2Pos.z }, 0.22f, 0.02f, 0.32f, Color{ 35, 40, 50, 255 });
|
|
|
|
// Secondary Monitor Screen Quad with Radar
|
|
drawTexturedQuad(m_texMonitorRadar,
|
|
{ mon2Pos.x + 0.045f, mon2Pos.y + 0.34f, mon2Pos.z + 0.40f },
|
|
{ mon2Pos.x + 0.045f, mon2Pos.y - 0.34f, mon2Pos.z + 0.40f },
|
|
{ mon2Pos.x + 0.045f, mon2Pos.y - 0.34f, mon2Pos.z - 0.40f },
|
|
{ mon2Pos.x + 0.045f, mon2Pos.y + 0.34f, mon2Pos.z - 0.40f },
|
|
1.0f, 1.0f);
|
|
|
|
// Keyboard & Mouse
|
|
DrawCube({ 1.20f, 0.80f, 4.7f }, 0.30f, 0.02f, 0.70f, Color{ 25, 28, 36, 255 });
|
|
DrawCubeWires({ 1.20f, 0.80f, 4.7f }, 0.30f, 0.02f, 0.70f, Color{ 0, 180, 220, 160 });
|
|
DrawCube({ 1.20f, 0.80f, 5.30f }, 0.14f, 0.025f, 0.10f, Color{ 30, 35, 45, 255 });
|
|
|
|
// PC Tower on floor under desk with pulsing cyan LED
|
|
Vector3 pcPos = { 0.95f, 0.35f, 3.6f };
|
|
DrawCube(pcPos, 0.40f, 0.70f, 0.60f, Color{ 14, 18, 26, 255 });
|
|
DrawCubeWires(pcPos, 0.40f, 0.70f, 0.60f, Color{ 35, 45, 60, 255 });
|
|
float ledPulse = 0.5f + 0.5f * std::sin(animTime * 3.0f);
|
|
DrawCube({ pcPos.x + 0.21f, pcPos.y + 0.25f, pcPos.z }, 0.02f, 0.03f, 0.30f,
|
|
Color{ 0, static_cast<unsigned char>(180 + ledPulse * 75), 255, 255 });
|
|
|
|
// 8. BED (South-East corner: X in [8.5f, 11.2f], Z in [8.8f, 11.2f])
|
|
Vector3 bedPos = { 9.8f, 0.30f, 10.0f };
|
|
Vector3 bedSize = { 2.3f, 0.45f, 2.2f };
|
|
DrawCube({ bedPos.x, 0.08f, bedPos.z }, bedSize.x + 0.1f, 0.16f, bedSize.z + 0.1f, Color{ 55, 35, 20, 255 });
|
|
drawTexturedBox(m_texBedQuilt, bedPos, bedSize, 2.0f, 2.0f);
|
|
|
|
// Bed Headboard against South wall
|
|
DrawCube({ bedPos.x, 0.75f, maxCoord - 0.08f }, bedSize.x + 0.1f, 1.1f, 0.10f, Color{ 55, 35, 20, 255 });
|
|
|
|
// Two soft pillows at head of bed
|
|
Vector3 pillow1 = { 9.2f, 0.58f, 10.7f };
|
|
Vector3 pillow2 = { 10.4f, 0.58f, 10.7f };
|
|
DrawCube(pillow1, 0.9f, 0.12f, 0.50f, Color{ 235, 238, 246, 255 });
|
|
DrawCube(pillow2, 0.9f, 0.12f, 0.50f, Color{ 235, 238, 246, 255 });
|
|
|
|
// 9. CYBERSPACE GATEWAY DOOR (North wall at X = 6.0f, Z = minCoord)
|
|
Vector3 gateFramePos = { 6.0f, roomHeight * 0.5f, minCoord + 0.05f };
|
|
drawTexturedBox(m_texCyberGate, gateFramePos, { 2.6f, roomHeight, 0.30f }, 1.0f, 1.0f);
|
|
|
|
float pulse = 0.5f + 0.5f * std::sin(animTime * 4.0f);
|
|
Vector3 portalField = { 6.0f, (roomHeight - 0.4f) * 0.5f, minCoord + 0.06f };
|
|
DrawCube(portalField, 1.8f, roomHeight - 0.4f, 0.08f, Color{ 0, 230, 160, 140 });
|
|
DrawCubeWires(portalField, 1.82f, roomHeight - 0.38f, 0.10f,
|
|
Color{ 0, 255, 120, static_cast<unsigned char>(180 + pulse * 75) });
|
|
|
|
Vector3 signPos = { 6.0f, roomHeight - 0.22f, minCoord + 0.24f };
|
|
DrawCube(signPos, 1.6f, 0.25f, 0.06f, Color{ 10, 16, 26, 255 });
|
|
DrawCubeWires(signPos, 1.6f, 0.25f, 0.06f, CyberColors::NeonCyan);
|
|
|
|
EndMode3D();
|
|
}
|
|
|
|
void CyberRenderer::renderRestRoom(const RestRoom& restRoom, const Player& player, float animTime) {
|
|
if (!m_texturesLoaded) {
|
|
generateTextures();
|
|
}
|
|
|
|
beginPostProcess();
|
|
|
|
Vector3 eye = player.getVisualPosition();
|
|
m_camera.position = eye;
|
|
|
|
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
|
|
float dirX = std::sin(rad);
|
|
float dirZ = -std::cos(rad);
|
|
|
|
m_camera.target = { eye.x + dirX, eye.y, eye.z + dirZ };
|
|
|
|
BeginMode3D(m_camera);
|
|
|
|
const float roomHeight = 3.2f;
|
|
const float minCoord = 0.6f;
|
|
const float maxCoord = 11.4f;
|
|
|
|
// 1. Ancient Terracotta & Marble Floor
|
|
drawTexturedQuad(m_texAncientFloor,
|
|
{ minCoord, -0.01f, minCoord },
|
|
{ minCoord, -0.01f, maxCoord },
|
|
{ maxCoord, -0.01f, maxCoord },
|
|
{ maxCoord, -0.01f, minCoord },
|
|
5.0f, 5.0f);
|
|
|
|
// 2. Ancient Cedar Timber Ceiling Beams
|
|
drawTexturedQuad(m_texAncientCeil,
|
|
{ maxCoord, roomHeight, minCoord },
|
|
{ maxCoord, roomHeight, maxCoord },
|
|
{ minCoord, roomHeight, maxCoord },
|
|
{ minCoord, roomHeight, minCoord },
|
|
5.0f, 5.0f);
|
|
|
|
// 3. Ancient Ashlar Stone Walls
|
|
// South Wall (Z = maxCoord)
|
|
drawTexturedBox(m_texAncientWall, { 6.0f, roomHeight * 0.5f, maxCoord }, { 11.0f, roomHeight, 0.3f }, 5.5f, 1.0f);
|
|
// East Wall (X = maxCoord)
|
|
drawTexturedBox(m_texAncientWall, { maxCoord, roomHeight * 0.5f, 6.0f }, { 0.3f, roomHeight, 11.0f }, 5.5f, 1.0f);
|
|
// West Wall (X = minCoord)
|
|
drawTexturedBox(m_texAncientWall, { minCoord, roomHeight * 0.5f, 6.0f }, { 0.3f, roomHeight, 11.0f }, 5.5f, 1.0f);
|
|
|
|
// North Wall: Split around Monumental Palace Portal (X = 6.0f, width 2.6f)
|
|
drawTexturedBox(m_texAncientWall, { 2.7f, roomHeight * 0.5f, minCoord }, { 4.2f, roomHeight, 0.3f }, 2.0f, 1.0f);
|
|
drawTexturedBox(m_texAncientWall, { 9.3f, roomHeight * 0.5f, minCoord }, { 4.2f, roomHeight, 0.3f }, 2.0f, 1.0f);
|
|
drawTexturedBox(m_texAncientWall, { 6.0f, roomHeight - 0.3f, minCoord }, { 2.6f, 0.6f, 0.3f }, 1.3f, 0.3f);
|
|
|
|
// 4. Classical Fluted Marble Columns in 4 corners
|
|
drawMarbleColumn({ minCoord + 0.5f, 0.0f, minCoord + 0.5f }, roomHeight, 0.24f);
|
|
drawMarbleColumn({ maxCoord - 0.5f, 0.0f, minCoord + 0.5f }, roomHeight, 0.24f);
|
|
drawMarbleColumn({ minCoord + 0.5f, 0.0f, maxCoord - 0.5f }, roomHeight, 0.24f);
|
|
drawMarbleColumn({ maxCoord - 0.5f, 0.0f, maxCoord - 0.5f }, roomHeight, 0.24f);
|
|
|
|
// 5. Wall Torches / Sconces with flickering warm flame
|
|
auto drawTorchSconce = [&](Vector3 wallPos, Vector3 outDir) {
|
|
// Bronze mount bracket
|
|
DrawCube(wallPos, 0.08f, 0.28f, 0.08f, Color{ 145, 105, 45, 255 });
|
|
Vector3 torchPos = { wallPos.x + outDir.x * 0.22f, wallPos.y + 0.10f, wallPos.z + outDir.z * 0.22f };
|
|
DrawCylinder(torchPos, 0.04f, 0.05f, 0.28f, 8, Color{ 80, 50, 25, 255 });
|
|
|
|
float flick1 = std::sin(animTime * 14.0f + wallPos.x * 3.5f + wallPos.z * 4.2f) * 0.025f;
|
|
float flick2 = std::cos(animTime * 18.0f + wallPos.z * 2.8f) * 0.02f;
|
|
Vector3 flamePos = { torchPos.x, torchPos.y + 0.16f + flick1, torchPos.z };
|
|
DrawSphere(flamePos, 0.11f + flick2, Color{ 255, 160, 45, 240 });
|
|
DrawSphere({ flamePos.x, flamePos.y + 0.04f, flamePos.z }, 0.06f, Color{ 255, 230, 110, 255 });
|
|
};
|
|
|
|
// Torch sconces on walls
|
|
drawTorchSconce({ 3.2f, 1.7f, minCoord + 0.16f }, { 0.0f, 0.0f, 1.0f });
|
|
drawTorchSconce({ 8.8f, 1.7f, minCoord + 0.16f }, { 0.0f, 0.0f, 1.0f });
|
|
drawTorchSconce({ 3.5f, 1.7f, maxCoord - 0.16f }, { 0.0f, 0.0f, -1.0f });
|
|
drawTorchSconce({ 8.5f, 1.7f, maxCoord - 0.16f }, { 0.0f, 0.0f, -1.0f });
|
|
drawTorchSconce({ minCoord + 0.16f, 1.7f, 4.0f }, { 1.0f, 0.0f, 0.0f });
|
|
drawTorchSconce({ minCoord + 0.16f, 1.7f, 8.0f }, { 1.0f, 0.0f, 0.0f });
|
|
drawTorchSconce({ maxCoord - 0.16f, 1.7f, 4.0f }, { -1.0f, 0.0f, 0.0f });
|
|
drawTorchSconce({ maxCoord - 0.16f, 1.7f, 8.0f }, { -1.0f, 0.0f, 0.0f });
|
|
|
|
// 6. Monumental Palace Portal to Next Floor (North: X = 6.0f, Z = minCoord)
|
|
drawMarbleColumn({ 4.7f, 0.0f, minCoord + 0.16f }, roomHeight - 0.2f, 0.22f);
|
|
drawMarbleColumn({ 7.3f, 0.0f, minCoord + 0.16f }, roomHeight - 0.2f, 0.22f);
|
|
// Classical marble lintel archway
|
|
DrawCube({ 6.0f, roomHeight - 0.40f, minCoord + 0.16f }, 3.0f, 0.32f, 0.38f, Color{ 220, 215, 205, 255 });
|
|
DrawCubeWires({ 6.0f, roomHeight - 0.40f, minCoord + 0.16f }, 3.02f, 0.34f, 0.40f, Color{ 175, 140, 80, 255 });
|
|
|
|
// Dark descending stairway passage into catacombs
|
|
DrawCube({ 6.0f, (roomHeight - 0.6f) * 0.5f, minCoord + 0.06f }, 2.0f, roomHeight - 0.6f, 0.10f, Color{ 18, 14, 22, 255 });
|
|
float portalPulse = 0.5f + 0.5f * std::sin(animTime * 3.2f);
|
|
DrawCube({ 6.0f, (roomHeight - 0.6f) * 0.5f, minCoord + 0.08f }, 1.5f, roomHeight - 0.8f, 0.04f,
|
|
Color{ 230, 190, 80, static_cast<unsigned char>(110 + portalPulse * 70) });
|
|
|
|
// 7. Corner Storeroom Clusters (Terracotta Amphorae & Weathered Crates)
|
|
// Northwest Cluster at tile (1, 1) -> (2.0f, 0.0f, 2.0f)
|
|
drawAncientCrate(m_texAncientWood, { 2.1f, 0.45f, 2.1f }, { 0.90f, 0.90f, 0.90f });
|
|
drawAncientCrate(m_texAncientWood, { 2.1f, 1.15f, 2.1f }, { 0.65f, 0.50f, 0.65f });
|
|
drawAncientAmphora({ 1.5f, 0.0f, 1.6f }, 1.15f, Color{ 188, 102, 58, 255 });
|
|
drawAncientAmphora({ 2.5f, 0.0f, 1.5f }, 0.95f, Color{ 168, 88, 48, 255 });
|
|
drawAncientAmphora({ 1.6f, 0.0f, 2.4f }, 1.05f, Color{ 198, 115, 68, 255 });
|
|
|
|
// Northeast Cluster at tile (5, 1) -> (10.0f, 0.0f, 2.0f)
|
|
drawAncientCrate(m_texAncientWood, { 9.9f, 0.50f, 1.8f }, { 1.00f, 1.00f, 1.00f });
|
|
drawAncientCrate(m_texAncientWood, { 10.4f, 0.40f, 2.4f }, { 0.80f, 0.80f, 0.80f });
|
|
drawAncientAmphora({ 9.4f, 0.0f, 2.3f }, 1.20f, Color{ 180, 95, 52, 255 });
|
|
drawAncientAmphora({ 10.4f, 0.0f, 1.4f }, 0.90f, Color{ 205, 120, 72, 255 });
|
|
|
|
// Additional storeroom props along south walls
|
|
drawAncientAmphora({ 1.4f, 0.0f, 10.2f }, 1.10f, Color{ 175, 92, 50, 255 });
|
|
drawAncientAmphora({ 2.2f, 0.0f, 10.5f }, 0.95f, Color{ 192, 108, 62, 255 });
|
|
drawAncientCrate(m_texAncientWood, { 10.1f, 0.40f, 10.2f }, { 0.80f, 0.80f, 0.80f });
|
|
drawAncientAmphora({ 9.4f, 0.0f, 10.4f }, 1.15f, Color{ 185, 100, 56, 255 });
|
|
|
|
// 8. Stations:
|
|
// West: Scribe's Inscribed Stele / Archive at tile (1, 3) -> (2.0f, 0.0f, 6.0f)
|
|
drawScribeStele({ 2.0f, 0.0f, 6.0f });
|
|
|
|
// Center: Marble Nectar Fountain at tile (3, 3) -> (6.0f, 0.0f, 6.0f)
|
|
drawMarbleNectarFountain({ 6.0f, 0.0f, 6.0f }, restRoom.isRecharged(), animTime);
|
|
|
|
// East: Craftsman's Workbench at tile (5, 3) -> (10.0f, 0.0f, 6.0f)
|
|
drawCraftsmanWorkbench(m_texAncientWood, { 10.0f, 0.0f, 6.0f });
|
|
// Water quenching urn & parts basket beside workbench
|
|
drawAncientAmphora({ 10.0f, 0.0f, 7.6f }, 0.85f, Color{ 140, 110, 85, 255 });
|
|
drawAncientCrate(m_texAncientWood, { 10.0f, 0.30f, 4.4f }, { 0.65f, 0.60f, 0.65f });
|
|
|
|
EndMode3D();
|
|
|
|
endPostProcess(animTime);
|
|
}
|
|
|
|
} // namespace dnr
|