forked from tas/major_tom
Add in-game level editor with auto-discovered tile/entity palettes
Implements a full level editor that runs inside the game engine as an alternative mode, accessible via --edit flag or E key during gameplay. The editor auto-discovers available tiles from the tileset texture and entities from a new central registry, so adding new game content automatically appears in the editor without any editor-specific changes. Editor features: tile painting (pencil/eraser/flood fill) across 3 layers, entity placement with drag-to-move, player spawn point tool, camera pan/zoom, grid overlay, .lvl save/load, map resize, and test play (P to play, ESC to return to editor). Supporting changes: - Entity registry centralizes spawn functions (replaces strcmp chain) - Mouse input + raw keyboard access added to input system - Camera zoom support for editor overview - Zoom-aware rendering in tilemap, renderer, and sprite systems - Powerup and drone sprites/animations wired up (were defined but unused) - Bitmap font renderer for editor UI (4x6 pixel glyphs, no dependencies)
This commit is contained in:
@@ -231,6 +231,491 @@ void parallax_generate_nebula(Parallax *p, SDL_Renderer *renderer) {
|
||||
p->near_layer.owns_texture = true;
|
||||
}
|
||||
|
||||
/* ── Themed: Alien Sky (planet surface) ──────────────── */
|
||||
|
||||
static void generate_alien_sky_far(Parallax *p, SDL_Renderer *renderer) {
|
||||
int w = SCREEN_WIDTH;
|
||||
int h = SCREEN_HEIGHT;
|
||||
|
||||
SDL_Texture *tex = SDL_CreateTexture(renderer,
|
||||
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
|
||||
if (!tex) return;
|
||||
|
||||
SDL_SetRenderTarget(renderer, tex);
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
unsigned int saved_seed = (unsigned int)rand();
|
||||
srand(55);
|
||||
|
||||
/* Fewer stars than deep space — atmosphere scatters light */
|
||||
for (int i = 0; i < 60; i++) {
|
||||
int x = (int)(randf() * w);
|
||||
int y = (int)(randf() * h * 0.6f); /* stars only in upper portion */
|
||||
uint8_t brightness = (uint8_t)(60 + (int)(randf() * 80));
|
||||
/* Warm-tinted: alien atmosphere filters starlight */
|
||||
uint8_t r = clamp_u8(brightness + 20);
|
||||
uint8_t g = clamp_u8(brightness - 10);
|
||||
uint8_t b = clamp_u8(brightness - 30);
|
||||
uint8_t a = (uint8_t)(100 + (int)(randf() * 80));
|
||||
SDL_SetRenderDrawColor(renderer, r, g, b, a);
|
||||
SDL_Rect dot = {x, y, 1, 1};
|
||||
SDL_RenderFillRect(renderer, &dot);
|
||||
}
|
||||
|
||||
/* A few brighter stars peeking through */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int x = (int)(randf() * w);
|
||||
int y = (int)(randf() * h * 0.4f);
|
||||
SDL_SetRenderDrawColor(renderer, 255, 220, 180, 200);
|
||||
SDL_Rect dot = {x, y, 1, 1};
|
||||
SDL_RenderFillRect(renderer, &dot);
|
||||
}
|
||||
|
||||
/* Distant mountain/terrain silhouette at bottom */
|
||||
int terrain_base = (int)(h * 0.70f);
|
||||
for (int x = 0; x < w; x++) {
|
||||
/* Jagged terrain profile using multiple sine waves */
|
||||
float t = (float)x / (float)w;
|
||||
float h1 = sinf(t * 6.28f * 2.0f) * 15.0f;
|
||||
float h2 = sinf(t * 6.28f * 5.3f + 1.2f) * 8.0f;
|
||||
float h3 = sinf(t * 6.28f * 11.7f + 3.1f) * 4.0f;
|
||||
int peak = terrain_base - (int)(h1 + h2 + h3);
|
||||
if (peak < terrain_base - 30) peak = terrain_base - 30;
|
||||
|
||||
/* Dark terrain silhouette */
|
||||
for (int y = peak; y < h; y++) {
|
||||
/* Gradient: darker at bottom, slightly lighter at peaks */
|
||||
int depth = y - peak;
|
||||
uint8_t r = clamp_u8(8 - depth / 8);
|
||||
uint8_t g = clamp_u8(5 - depth / 10);
|
||||
uint8_t b = clamp_u8(12 - depth / 6);
|
||||
uint8_t a = (uint8_t)(depth < 3 ? 120 : 180);
|
||||
SDL_SetRenderDrawColor(renderer, r, g, b, a);
|
||||
SDL_Rect px = {x, y, 1, 1};
|
||||
SDL_RenderFillRect(renderer, &px);
|
||||
}
|
||||
}
|
||||
|
||||
srand(saved_seed);
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
|
||||
p->far_layer.texture = tex;
|
||||
p->far_layer.tex_w = w;
|
||||
p->far_layer.tex_h = h;
|
||||
p->far_layer.scroll_x = 0.03f; /* very slow — distant landscape */
|
||||
p->far_layer.scroll_y = 0.03f;
|
||||
p->far_layer.active = true;
|
||||
p->far_layer.owns_texture = true;
|
||||
}
|
||||
|
||||
static void generate_alien_sky_near(Parallax *p, SDL_Renderer *renderer) {
|
||||
int w = SCREEN_WIDTH;
|
||||
int h = SCREEN_HEIGHT;
|
||||
|
||||
SDL_Texture *tex = SDL_CreateTexture(renderer,
|
||||
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
|
||||
if (!tex) return;
|
||||
|
||||
SDL_SetRenderTarget(renderer, tex);
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
unsigned int saved_seed = (unsigned int)rand();
|
||||
srand(188);
|
||||
|
||||
/* Atmospheric haze / dust clouds — warm oranges and reds */
|
||||
typedef struct { uint8_t r, g, b; } HazeColor;
|
||||
HazeColor haze_palette[] = {
|
||||
{ 80, 30, 15}, /* rusty orange */
|
||||
{ 60, 20, 25}, /* dark red */
|
||||
{ 50, 35, 20}, /* brown dust */
|
||||
{ 70, 25, 40}, /* murky purple */
|
||||
{ 45, 40, 30}, /* sandy */
|
||||
};
|
||||
int haze_count = sizeof(haze_palette) / sizeof(haze_palette[0]);
|
||||
|
||||
/* Haze bands across the sky */
|
||||
for (int band = 0; band < 4; band++) {
|
||||
float cy = randf() * h * 0.7f + h * 0.15f;
|
||||
HazeColor col = haze_palette[band % haze_count];
|
||||
int blobs = 20 + (int)(randf() * 15);
|
||||
for (int b = 0; b < blobs; b++) {
|
||||
float angle = randf() * (float)(2.0 * M_PI);
|
||||
float dist = randf() * 100.0f;
|
||||
int bx = (int)(randf() * w);
|
||||
int by = (int)(cy + sinf(angle) * dist * 0.3f);
|
||||
int bw = 15 + (int)(randf() * 30);
|
||||
int bh = 5 + (int)(randf() * 10);
|
||||
uint8_t br = clamp_u8(col.r + (int)(randf() * 20 - 10));
|
||||
uint8_t bg = clamp_u8(col.g + (int)(randf() * 20 - 10));
|
||||
uint8_t bb = clamp_u8(col.b + (int)(randf() * 20 - 10));
|
||||
SDL_SetRenderDrawColor(renderer, br, bg, bb, (uint8_t)(6 + (int)(randf() * 12)));
|
||||
SDL_Rect rect = {bx - bw / 2, by - bh / 2, bw, bh};
|
||||
SDL_RenderFillRect(renderer, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
/* Floating dust particles */
|
||||
for (int i = 0; i < 40; i++) {
|
||||
int x = (int)(randf() * w);
|
||||
int y = (int)(randf() * h);
|
||||
HazeColor col = haze_palette[(int)(randf() * haze_count)];
|
||||
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b,
|
||||
(uint8_t)(25 + (int)(randf() * 35)));
|
||||
SDL_Rect dot = {x, y, 1, 1};
|
||||
SDL_RenderFillRect(renderer, &dot);
|
||||
}
|
||||
|
||||
srand(saved_seed);
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
|
||||
p->near_layer.texture = tex;
|
||||
p->near_layer.tex_w = w;
|
||||
p->near_layer.tex_h = h;
|
||||
p->near_layer.scroll_x = 0.10f;
|
||||
p->near_layer.scroll_y = 0.06f;
|
||||
p->near_layer.active = true;
|
||||
p->near_layer.owns_texture = true;
|
||||
}
|
||||
|
||||
/* ── Themed: Interior (planet base) ─────────────────── */
|
||||
|
||||
static void generate_interior_far(Parallax *p, SDL_Renderer *renderer) {
|
||||
int w = SCREEN_WIDTH;
|
||||
int h = SCREEN_HEIGHT;
|
||||
|
||||
SDL_Texture *tex = SDL_CreateTexture(renderer,
|
||||
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
|
||||
if (!tex) return;
|
||||
|
||||
SDL_SetRenderTarget(renderer, tex);
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
unsigned int saved_seed = (unsigned int)rand();
|
||||
srand(73);
|
||||
|
||||
/* Wall panel grid — subtle horizontal and vertical lines */
|
||||
/* Horizontal beams */
|
||||
for (int i = 0; i < 6; i++) {
|
||||
int y = (int)(randf() * h);
|
||||
uint8_t a = (uint8_t)(15 + (int)(randf() * 20));
|
||||
SDL_SetRenderDrawColor(renderer, 25, 30, 40, a);
|
||||
SDL_Rect beam = {0, y, w, 2};
|
||||
SDL_RenderFillRect(renderer, &beam);
|
||||
}
|
||||
|
||||
/* Vertical structural columns */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int x = (int)(randf() * w);
|
||||
int col_w = 2 + (int)(randf() * 3);
|
||||
uint8_t a = (uint8_t)(12 + (int)(randf() * 18));
|
||||
SDL_SetRenderDrawColor(renderer, 20, 25, 35, a);
|
||||
SDL_Rect col_rect = {x, 0, col_w, h};
|
||||
SDL_RenderFillRect(renderer, &col_rect);
|
||||
}
|
||||
|
||||
/* Recessed wall panels (darker rectangles) */
|
||||
for (int i = 0; i < 12; i++) {
|
||||
int px = (int)(randf() * w);
|
||||
int py = (int)(randf() * h);
|
||||
int pw = 20 + (int)(randf() * 40);
|
||||
int ph = 15 + (int)(randf() * 30);
|
||||
SDL_SetRenderDrawColor(renderer, 8, 10, 18, (uint8_t)(20 + (int)(randf() * 15)));
|
||||
SDL_Rect panel = {px, py, pw, ph};
|
||||
SDL_RenderFillRect(renderer, &panel);
|
||||
/* Panel border highlight (top/left edge) */
|
||||
SDL_SetRenderDrawColor(renderer, 30, 35, 50, 20);
|
||||
SDL_Rect edge_h = {px, py, pw, 1};
|
||||
SDL_Rect edge_v = {px, py, 1, ph};
|
||||
SDL_RenderFillRect(renderer, &edge_h);
|
||||
SDL_RenderFillRect(renderer, &edge_v);
|
||||
}
|
||||
|
||||
srand(saved_seed);
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
|
||||
p->far_layer.texture = tex;
|
||||
p->far_layer.tex_w = w;
|
||||
p->far_layer.tex_h = h;
|
||||
p->far_layer.scroll_x = 0.02f; /* very slow — background wall */
|
||||
p->far_layer.scroll_y = 0.02f;
|
||||
p->far_layer.active = true;
|
||||
p->far_layer.owns_texture = true;
|
||||
}
|
||||
|
||||
static void generate_interior_near(Parallax *p, SDL_Renderer *renderer) {
|
||||
int w = SCREEN_WIDTH;
|
||||
int h = SCREEN_HEIGHT;
|
||||
|
||||
SDL_Texture *tex = SDL_CreateTexture(renderer,
|
||||
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
|
||||
if (!tex) return;
|
||||
|
||||
SDL_SetRenderTarget(renderer, tex);
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
unsigned int saved_seed = (unsigned int)rand();
|
||||
srand(209);
|
||||
|
||||
/* Pipes running along ceiling/floor areas */
|
||||
for (int i = 0; i < 5; i++) {
|
||||
int py = (int)(randf() * h);
|
||||
int pipe_h = 2 + (int)(randf() * 2);
|
||||
/* Pipe body */
|
||||
uint8_t r = (uint8_t)(20 + (int)(randf() * 15));
|
||||
uint8_t g = (uint8_t)(25 + (int)(randf() * 15));
|
||||
uint8_t b = (uint8_t)(35 + (int)(randf() * 15));
|
||||
SDL_SetRenderDrawColor(renderer, r, g, b, 25);
|
||||
SDL_Rect pipe = {0, py, w, pipe_h};
|
||||
SDL_RenderFillRect(renderer, &pipe);
|
||||
/* Pipe highlight (top edge) */
|
||||
SDL_SetRenderDrawColor(renderer, r + 15, g + 15, b + 15, 18);
|
||||
SDL_Rect highlight = {0, py, w, 1};
|
||||
SDL_RenderFillRect(renderer, &highlight);
|
||||
}
|
||||
|
||||
/* Small indicator lights / LEDs scattered on walls */
|
||||
for (int i = 0; i < 15; i++) {
|
||||
int x = (int)(randf() * w);
|
||||
int y = (int)(randf() * h);
|
||||
float r_chance = randf();
|
||||
if (r_chance < 0.4f) {
|
||||
/* Green status light */
|
||||
SDL_SetRenderDrawColor(renderer, 30, 180, 60, 60);
|
||||
} else if (r_chance < 0.65f) {
|
||||
/* Amber warning */
|
||||
SDL_SetRenderDrawColor(renderer, 200, 150, 30, 50);
|
||||
} else if (r_chance < 0.80f) {
|
||||
/* Red alert */
|
||||
SDL_SetRenderDrawColor(renderer, 200, 40, 30, 45);
|
||||
} else {
|
||||
/* Blue data */
|
||||
SDL_SetRenderDrawColor(renderer, 40, 100, 200, 50);
|
||||
}
|
||||
SDL_Rect led = {x, y, 1, 1};
|
||||
SDL_RenderFillRect(renderer, &led);
|
||||
/* Tiny glow around the LED */
|
||||
SDL_SetRenderDrawColor(renderer, 40, 60, 80, 8);
|
||||
SDL_Rect glow = {x - 1, y - 1, 3, 3};
|
||||
SDL_RenderFillRect(renderer, &glow);
|
||||
}
|
||||
|
||||
/* Ventilation grate patterns */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int gx = (int)(randf() * w);
|
||||
int gy = (int)(randf() * h);
|
||||
int gw = 8 + (int)(randf() * 12);
|
||||
int gh = 4 + (int)(randf() * 6);
|
||||
/* Grate slots (horizontal lines within a rectangle) */
|
||||
for (int s = 0; s < gh; s += 2) {
|
||||
SDL_SetRenderDrawColor(renderer, 15, 18, 28, 25);
|
||||
SDL_Rect slot = {gx, gy + s, gw, 1};
|
||||
SDL_RenderFillRect(renderer, &slot);
|
||||
}
|
||||
}
|
||||
|
||||
srand(saved_seed);
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
|
||||
p->near_layer.texture = tex;
|
||||
p->near_layer.tex_w = w;
|
||||
p->near_layer.tex_h = h;
|
||||
p->near_layer.scroll_x = 0.08f;
|
||||
p->near_layer.scroll_y = 0.05f;
|
||||
p->near_layer.active = true;
|
||||
p->near_layer.owns_texture = true;
|
||||
}
|
||||
|
||||
/* ── Themed: Deep Space (space station viewports) ───── */
|
||||
|
||||
static void generate_deep_space_far(Parallax *p, SDL_Renderer *renderer) {
|
||||
int w = SCREEN_WIDTH;
|
||||
int h = SCREEN_HEIGHT;
|
||||
|
||||
SDL_Texture *tex = SDL_CreateTexture(renderer,
|
||||
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
|
||||
if (!tex) return;
|
||||
|
||||
SDL_SetRenderTarget(renderer, tex);
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
unsigned int saved_seed = (unsigned int)rand();
|
||||
srand(99);
|
||||
|
||||
/* Dense starfield — station viewports show deep space clearly */
|
||||
/* Many small stars */
|
||||
for (int i = 0; i < 180; i++) {
|
||||
int x = (int)(randf() * w);
|
||||
int y = (int)(randf() * h);
|
||||
uint8_t brightness = (uint8_t)(120 + (int)(randf() * 100));
|
||||
uint8_t r = brightness, g = brightness, b = brightness;
|
||||
float tint = randf();
|
||||
if (tint < 0.3f) {
|
||||
b = clamp_u8(brightness + 50);
|
||||
g = clamp_u8(brightness + 10); /* cool blue-white */
|
||||
} else if (tint < 0.45f) {
|
||||
r = clamp_u8(brightness + 30); /* warm */
|
||||
}
|
||||
SDL_SetRenderDrawColor(renderer, r, g, b, (uint8_t)(180 + (int)(randf() * 75)));
|
||||
SDL_Rect dot = {x, y, 1, 1};
|
||||
SDL_RenderFillRect(renderer, &dot);
|
||||
}
|
||||
|
||||
/* Medium stars with halos — more prominent than default */
|
||||
for (int i = 0; i < 40; i++) {
|
||||
int x = (int)(randf() * w);
|
||||
int y = (int)(randf() * h);
|
||||
uint8_t brightness = (uint8_t)(200 + (int)(randf() * 55));
|
||||
uint8_t r = brightness, g = brightness, b = 255;
|
||||
SDL_SetRenderDrawColor(renderer, r, g, b, 255);
|
||||
SDL_Rect dot = {x, y, 1, 1};
|
||||
SDL_RenderFillRect(renderer, &dot);
|
||||
if (randf() < 0.5f) {
|
||||
SDL_SetRenderDrawColor(renderer, r, g, b, 90);
|
||||
SDL_Rect hx = {x - 1, y, 3, 1};
|
||||
SDL_Rect hy = {x, y - 1, 1, 3};
|
||||
SDL_RenderFillRect(renderer, &hx);
|
||||
SDL_RenderFillRect(renderer, &hy);
|
||||
}
|
||||
}
|
||||
|
||||
/* Bright feature stars */
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int x = (int)(randf() * w);
|
||||
int y = (int)(randf() * h);
|
||||
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
|
||||
SDL_Rect core = {x, y, 2, 2};
|
||||
SDL_RenderFillRect(renderer, &core);
|
||||
/* Cyan-white glow */
|
||||
SDL_SetRenderDrawColor(renderer, 150, 220, 255, (uint8_t)(100 + (int)(randf() * 80)));
|
||||
SDL_Rect ch = {x - 1, y, 4, 2};
|
||||
SDL_Rect cv = {x, y - 1, 2, 4};
|
||||
SDL_RenderFillRect(renderer, &ch);
|
||||
SDL_RenderFillRect(renderer, &cv);
|
||||
}
|
||||
|
||||
srand(saved_seed);
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
|
||||
p->far_layer.texture = tex;
|
||||
p->far_layer.tex_w = w;
|
||||
p->far_layer.tex_h = h;
|
||||
p->far_layer.scroll_x = 0.05f;
|
||||
p->far_layer.scroll_y = 0.05f;
|
||||
p->far_layer.active = true;
|
||||
p->far_layer.owns_texture = true;
|
||||
}
|
||||
|
||||
static void generate_deep_space_near(Parallax *p, SDL_Renderer *renderer) {
|
||||
int w = SCREEN_WIDTH;
|
||||
int h = SCREEN_HEIGHT;
|
||||
|
||||
SDL_Texture *tex = SDL_CreateTexture(renderer,
|
||||
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
|
||||
if (!tex) return;
|
||||
|
||||
SDL_SetRenderTarget(renderer, tex);
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
unsigned int saved_seed = (unsigned int)rand();
|
||||
srand(251);
|
||||
|
||||
/* Vivid nebula clouds — cyan, electric blue, violet */
|
||||
typedef struct { uint8_t r, g, b; } SpaceColor;
|
||||
SpaceColor palette[] = {
|
||||
{ 20, 60, 140}, /* electric blue */
|
||||
{ 30, 90, 130}, /* cyan-blue */
|
||||
{ 50, 30, 120}, /* deep violet */
|
||||
{ 15, 80, 100}, /* teal */
|
||||
{ 70, 20, 100}, /* purple */
|
||||
};
|
||||
int palette_count = sizeof(palette) / sizeof(palette[0]);
|
||||
|
||||
for (int cloud = 0; cloud < 6; cloud++) {
|
||||
float cx = randf() * w;
|
||||
float cy = randf() * h;
|
||||
SpaceColor col = palette[cloud % palette_count];
|
||||
int blobs = 35 + (int)(randf() * 25);
|
||||
for (int b = 0; b < blobs; b++) {
|
||||
float angle = randf() * (float)(2.0 * M_PI);
|
||||
float dist = randf() * 90.0f + randf() * 50.0f;
|
||||
int bx = (int)(cx + cosf(angle) * dist);
|
||||
int by = (int)(cy + sinf(angle) * dist);
|
||||
int bw = 10 + (int)(randf() * 24);
|
||||
int bh = 8 + (int)(randf() * 18);
|
||||
uint8_t br = clamp_u8(col.r + (int)(randf() * 30 - 15));
|
||||
uint8_t bg = clamp_u8(col.g + (int)(randf() * 30 - 15));
|
||||
uint8_t bb = clamp_u8(col.b + (int)(randf() * 30 - 15));
|
||||
SDL_SetRenderDrawColor(renderer, br, bg, bb, (uint8_t)(10 + (int)(randf() * 20)));
|
||||
SDL_Rect rect = {bx - bw / 2, by - bh / 2, bw, bh};
|
||||
SDL_RenderFillRect(renderer, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
/* Scattered bright dust */
|
||||
for (int i = 0; i < 50; i++) {
|
||||
int x = (int)(randf() * w);
|
||||
int y = (int)(randf() * h);
|
||||
SpaceColor col = palette[(int)(randf() * palette_count)];
|
||||
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b,
|
||||
(uint8_t)(35 + (int)(randf() * 45)));
|
||||
SDL_Rect dot = {x, y, 2, 2};
|
||||
SDL_RenderFillRect(renderer, &dot);
|
||||
}
|
||||
|
||||
srand(saved_seed);
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND);
|
||||
|
||||
p->near_layer.texture = tex;
|
||||
p->near_layer.tex_w = w;
|
||||
p->near_layer.tex_h = h;
|
||||
p->near_layer.scroll_x = 0.15f;
|
||||
p->near_layer.scroll_y = 0.10f;
|
||||
p->near_layer.active = true;
|
||||
p->near_layer.owns_texture = true;
|
||||
}
|
||||
|
||||
/* ── Themed parallax dispatcher ─────────────────────── */
|
||||
|
||||
void parallax_generate_themed(Parallax *p, SDL_Renderer *renderer, ParallaxStyle style) {
|
||||
switch (style) {
|
||||
case PARALLAX_STYLE_ALIEN_SKY:
|
||||
generate_alien_sky_far(p, renderer);
|
||||
generate_alien_sky_near(p, renderer);
|
||||
break;
|
||||
case PARALLAX_STYLE_INTERIOR:
|
||||
generate_interior_far(p, renderer);
|
||||
generate_interior_near(p, renderer);
|
||||
break;
|
||||
case PARALLAX_STYLE_DEEP_SPACE:
|
||||
generate_deep_space_far(p, renderer);
|
||||
generate_deep_space_near(p, renderer);
|
||||
break;
|
||||
case PARALLAX_STYLE_DEFAULT:
|
||||
default:
|
||||
parallax_generate_stars(p, renderer);
|
||||
parallax_generate_nebula(p, renderer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Render ──────────────────────────────────────────── */
|
||||
|
||||
static void render_layer(const ParallaxLayer *layer, const Camera *cam,
|
||||
|
||||
Reference in New Issue
Block a user