Add pixel art sprites for charger, spawner, and laser turret

Replace colored-rect placeholder rendering with proper 16x16
spritesheet art. Charger: orange armored rhino with horn, eyes,
walk cycle (row 7). Spawner: purple hive pod with glowing core
and tendrils (row 8). Laser turret: angular metallic housing
with red lens that brightens when firing (row 8). All three
retain colored-rect fallback when spritesheet is unavailable.
This commit is contained in:
Thomas
2026-03-02 20:45:21 +00:00
parent 7b1d39f522
commit 5f899f61c6
4 changed files with 461 additions and 65 deletions

View File

@@ -291,6 +291,8 @@ static void charger_update(Entity *self, float dt, const Tilemap *map) {
/* Death sequence */
if (self->flags & ENTITY_DEAD) {
animation_set(&self->anim, &anim_charger_death);
animation_update(&self->anim, dt);
cd->death_timer -= dt;
body->vel.x = 0;
if (cd->death_timer <= 0) {
@@ -394,43 +396,63 @@ static void charger_update(Entity *self, float dt, const Tilemap *map) {
}
}
/* Animation — use grunt anims as placeholder */
if (cd->state == CHARGER_CHARGE) {
animation_set(&self->anim, &anim_grunt_walk);
/* Animation */
if (cd->state == CHARGER_CHARGE || cd->state == CHARGER_PATROL) {
animation_set(&self->anim, &anim_charger_walk);
} else {
animation_set(&self->anim, &anim_grunt_idle);
animation_set(&self->anim, &anim_charger_idle);
}
animation_update(&self->anim, dt);
}
static void charger_render(Entity *self, const Camera *cam) {
(void)cam;
Body *body = &self->body;
ChargerData *cd = (ChargerData *)self->data;
/* Colored rect fallback — orange/amber color scheme */
SDL_Color color;
if (cd && cd->state == CHARGER_ALERT) {
/* Flash during telegraph */
float blink = sinf(cd->state_timer * 30.0f);
uint8_t r = (blink > 0) ? 255 : 200;
color = (SDL_Color){r, 160, 40, 255};
} else if (cd && cd->state == CHARGER_STUNNED) {
color = (SDL_Color){160, 160, 100, 255};
} else if (cd && cd->state == CHARGER_CHARGE) {
color = (SDL_Color){255, 120, 30, 255};
} else {
color = (SDL_Color){220, 140, 40, 255};
}
renderer_draw_rect(body->pos, body->size, color, LAYER_ENTITIES, cam);
if (g_spritesheet && self->anim.def) {
SDL_Rect src = animation_current_rect(&self->anim);
/* Direction indicator: small triangle in facing direction */
float cx = body->pos.x + body->size.x * 0.5f;
float cy = body->pos.y + 2.0f;
bool left = (self->flags & ENTITY_FACING_LEFT) != 0;
float arrow_x = left ? body->pos.x - 3.0f : body->pos.x + body->size.x + 1.0f;
renderer_draw_rect(vec2(arrow_x, cy), vec2(2, 3),
(SDL_Color){255, 200, 60, 255}, LAYER_ENTITIES, cam);
(void)cx;
/* Center the 16x16 sprite over the 14x16 hitbox */
Vec2 render_pos = vec2(
body->pos.x - 1.0f,
body->pos.y
);
/* Flash white during alert telegraph */
uint8_t alpha = 255;
if (cd && cd->state == CHARGER_ALERT) {
float blink = sinf(cd->state_timer * 30.0f);
alpha = (blink > 0) ? 180 : 255;
}
Sprite spr = {
.texture = g_spritesheet,
.src = src,
.pos = render_pos,
.size = vec2(SPRITE_CELL, SPRITE_CELL),
.flip_x = (self->flags & ENTITY_FACING_LEFT) != 0,
.flip_y = false,
.layer = LAYER_ENTITIES,
.alpha = alpha,
};
renderer_submit(&spr);
} else {
/* Colored rect fallback */
SDL_Color color;
if (cd && cd->state == CHARGER_ALERT) {
float blink = sinf(cd->state_timer * 30.0f);
uint8_t r = (blink > 0) ? 255 : 200;
color = (SDL_Color){r, 160, 40, 255};
} else if (cd && cd->state == CHARGER_STUNNED) {
color = (SDL_Color){160, 160, 100, 255};
} else if (cd && cd->state == CHARGER_CHARGE) {
color = (SDL_Color){255, 120, 30, 255};
} else {
color = (SDL_Color){220, 140, 40, 255};
}
renderer_draw_rect(body->pos, body->size, color, LAYER_ENTITIES, cam);
}
}
static void charger_destroy(Entity *self) {
@@ -494,6 +516,8 @@ static void spawner_update(Entity *self, float dt, const Tilemap *map) {
/* Death sequence */
if (self->flags & ENTITY_DEAD) {
animation_set(&self->anim, &anim_spawner_death);
animation_update(&self->anim, dt);
sd->death_timer -= dt;
if (sd->death_timer <= 0) {
particle_emit_death_puff(self->body.pos, (SDL_Color){180, 60, 180, 255});
@@ -511,6 +535,10 @@ static void spawner_update(Entity *self, float dt, const Tilemap *map) {
sd->pulse_timer = 0;
}
/* Animation */
animation_set(&self->anim, &anim_spawner_idle);
animation_update(&self->anim, dt);
/* Spawn a grunt when timer expires */
if (sd->spawn_timer <= 0) {
sd->spawn_timer = SPAWNER_INTERVAL;
@@ -530,26 +558,46 @@ static void spawner_update(Entity *self, float dt, const Tilemap *map) {
}
static void spawner_render(Entity *self, const Camera *cam) {
(void)cam;
Body *body = &self->body;
SpawnerData *sd = (SpawnerData *)self->data;
/* Pulsing purple color when about to spawn */
SDL_Color color;
if (sd && sd->pulse_timer > 0) {
float pulse = sinf(sd->pulse_timer * 12.0f) * 0.5f + 0.5f;
uint8_t r = (uint8_t)(140 + 80 * pulse);
uint8_t b = (uint8_t)(180 + 60 * pulse);
color = (SDL_Color){r, 50, b, 255};
} else {
color = (SDL_Color){140, 50, 160, 255};
}
renderer_draw_rect(body->pos, body->size, color, LAYER_ENTITIES, cam);
if (g_spritesheet && self->anim.def) {
SDL_Rect src = animation_current_rect(&self->anim);
/* Small inner dot to distinguish from other hazards */
float cx = body->pos.x + body->size.x * 0.5f - 2.0f;
float cy = body->pos.y + body->size.y * 0.5f - 2.0f;
SDL_Color dot = {255, 200, 255, 255};
renderer_draw_rect(vec2(cx, cy), vec2(4, 4), dot, LAYER_ENTITIES, cam);
Vec2 render_pos = vec2(body->pos.x, body->pos.y);
/* Pulse brightness when about to spawn */
uint8_t alpha = 255;
if (sd && sd->pulse_timer > 0) {
float pulse = sinf(sd->pulse_timer * 12.0f);
alpha = (pulse > 0) ? 200 : 255;
}
Sprite spr = {
.texture = g_spritesheet,
.src = src,
.pos = render_pos,
.size = vec2(SPRITE_CELL, SPRITE_CELL),
.flip_x = false,
.flip_y = false,
.layer = LAYER_ENTITIES,
.alpha = alpha,
};
renderer_submit(&spr);
} else {
/* Colored rect fallback */
SDL_Color color;
if (sd && sd->pulse_timer > 0) {
float pulse = sinf(sd->pulse_timer * 12.0f) * 0.5f + 0.5f;
uint8_t r = (uint8_t)(140 + 80 * pulse);
uint8_t b = (uint8_t)(180 + 60 * pulse);
color = (SDL_Color){r, 50, b, 255};
} else {
color = (SDL_Color){140, 50, 160, 255};
}
renderer_draw_rect(body->pos, body->size, color, LAYER_ENTITIES, cam);
}
}
static void spawner_destroy(Entity *self) {

View File

@@ -199,6 +199,14 @@ static void laser_turret_update(Entity *self, float dt, const Tilemap *map) {
(SDL_Color){255, 100, 50, 255});
}
}
/* Animation */
if (ld->state == LASER_CHARGING || ld->state == LASER_FIRING) {
animation_set(&self->anim, &anim_laser_turret_fire);
} else {
animation_set(&self->anim, &anim_laser_turret_idle);
}
animation_update(&self->anim, dt);
}
/* ── Render ──────────────────────────────────────── */
@@ -281,24 +289,47 @@ static void laser_turret_render(Entity *self, const Camera *cam) {
(int)(3.0f * fade), beam_col);
}
/* Draw turret body — colored rect fallback (no sprite yet). */
SDL_Color base_col;
if (ld->state == LASER_FIRING) {
base_col = (SDL_Color){200, 80, 50, 255};
} else if (ld->state == LASER_CHARGING) {
base_col = (SDL_Color){180, 100, 60, 255};
} else {
base_col = (SDL_Color){140, 100, 80, 255};
}
renderer_draw_rect(body->pos, body->size, base_col, LAYER_ENTITIES, cam);
/* Draw turret body */
if (g_spritesheet && self->anim.def) {
SDL_Rect src = animation_current_rect(&self->anim);
/* Small dot indicating aim direction */
float dot_dist = 10.0f;
float dot_x = cx + cosf(ld->aim_angle) * dot_dist - 1.0f;
float dot_y = cy + sinf(ld->aim_angle) * dot_dist - 1.0f;
SDL_Color dot_col = {255, 50, 30, 255};
renderer_draw_rect(vec2(dot_x, dot_y), vec2(2, 2), dot_col,
LAYER_ENTITIES, cam);
/* Center the 16x16 sprite over the 14x14 hitbox */
Vec2 render_pos = vec2(
body->pos.x - 1.0f,
body->pos.y - 1.0f
);
Sprite spr = {
.texture = g_spritesheet,
.src = src,
.pos = render_pos,
.size = vec2(SPRITE_CELL, SPRITE_CELL),
.flip_x = (self->flags & ENTITY_FACING_LEFT) != 0,
.flip_y = false,
.layer = LAYER_ENTITIES,
.alpha = 255,
};
renderer_submit(&spr);
} else {
/* Colored rect fallback */
SDL_Color base_col;
if (ld->state == LASER_FIRING) {
base_col = (SDL_Color){200, 80, 50, 255};
} else if (ld->state == LASER_CHARGING) {
base_col = (SDL_Color){180, 100, 60, 255};
} else {
base_col = (SDL_Color){140, 100, 80, 255};
}
renderer_draw_rect(body->pos, body->size, base_col, LAYER_ENTITIES, cam);
/* Small dot indicating aim direction (only needed for fallback) */
float dot_dist = 10.0f;
float dot_x = cx + cosf(ld->aim_angle) * dot_dist - 1.0f;
float dot_y = cy + sinf(ld->aim_angle) * dot_dist - 1.0f;
SDL_Color dot_col = {255, 50, 30, 255};
renderer_draw_rect(vec2(dot_x, dot_y), vec2(2, 2), dot_col,
LAYER_ENTITIES, cam);
}
}
/* ── Destroy ─────────────────────────────────────── */

View File

@@ -993,6 +993,233 @@ static const uint32_t asteroid2[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
};
/* ── Charger enemy sprites ──────────────────────────── */
/* Charger: Orange armored rhino-like ground charger. 14x16 hitbox.
* Stocky body, forward horn, angry eyes, stubby legs. */
#define CHG 0xd98a30FF /* charger orange */
#define CHD 0xa86620FF /* charger dark */
#define CHL 0xf0b050FF /* charger light */
#define CHK 0x7a4a14FF /* charger darkest (outline) */
static const uint32_t charger_idle1[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, CHK, T, T, T, T, T, CHK, CHK, CHK, T, T, T, T,
T, T, CHK, YLW, CHK, T, T, T, CHK, CHL, CHG, CHG, CHK, T, T, T,
T, T, T, CHK, CHK, CHK, CHK, CHK, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, WHT, BLK, CHG, CHG, WHT, BLK, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, T, CHK, CHG, CHG, CHD, CHD, CHG, CHG, CHG, CHK, T, T, T,
T, T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T, T,
T, T, T, T, CHK, CHD, CHG, CHG, CHG, CHG, CHD, CHK, T, T, T, T,
T, T, T, T, T, CHK, CHD, CHG, CHG, CHD, CHK, T, T, T, T, T,
T, T, T, T, CHK, CHK, T, T, T, T, CHK, CHK, T, T, T, T,
T, T, T, T, CHK, CHD, T, T, T, T, CHD, CHK, T, T, T, T,
T, T, T, T, CHK, CHK, T, T, T, T, CHK, CHK, T, T, T, T,
};
static const uint32_t charger_idle2[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, CHK, T, T, T, T, T, CHK, CHK, CHK, T, T, T, T,
T, T, CHK, YLW, CHK, T, T, T, CHK, CHL, CHG, CHG, CHK, T, T, T,
T, T, T, CHK, CHK, CHK, CHK, CHK, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, WHT, BLK, CHG, CHG, WHT, BLK, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, T, CHK, CHG, CHG, CHD, CHD, CHG, CHG, CHG, CHK, T, T, T,
T, T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T, T,
T, T, T, T, CHK, CHD, CHG, CHG, CHG, CHG, CHD, CHK, T, T, T, T,
T, T, T, T, T, CHK, CHK, T, T, CHK, CHK, T, T, T, T, T,
T, T, T, T, T, CHD, CHK, T, T, CHK, CHD, T, T, T, T, T,
T, T, T, T, T, CHK, CHK, T, T, CHK, CHK, T, T, T, T, T,
};
/* Charger walk frames — legs alternate */
static const uint32_t charger_walk1[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, CHK, T, T, T, T, T, CHK, CHK, CHK, T, T, T, T,
T, T, CHK, YLW, CHK, T, T, T, CHK, CHL, CHG, CHG, CHK, T, T, T,
T, T, T, CHK, CHK, CHK, CHK, CHK, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, WHT, BLK, CHG, CHG, WHT, BLK, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, T, CHK, CHG, CHG, CHD, CHD, CHG, CHG, CHG, CHK, T, T, T,
T, T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T, T,
T, T, T, T, CHK, CHD, CHG, CHG, CHG, CHG, CHD, CHK, T, T, T, T,
T, T, T, T, T, CHK, CHD, CHG, CHG, CHD, CHK, T, T, T, T, T,
T, T, T, T, T, CHK, T, T, T, CHK, T, T, T, T, T, T,
T, T, T, T, CHK, T, T, T, T, T, CHK, T, T, T, T, T,
T, T, T, T, CHK, T, T, T, T, T, CHK, T, T, T, T, T,
};
static const uint32_t charger_walk2[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, CHK, T, T, T, T, T, CHK, CHK, CHK, T, T, T, T,
T, T, CHK, YLW, CHK, T, T, T, CHK, CHL, CHG, CHG, CHK, T, T, T,
T, T, T, CHK, CHK, CHK, CHK, CHK, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, WHT, BLK, CHG, CHG, WHT, BLK, CHG, CHG, CHK, T, T,
T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, T, T, CHK, CHG, CHG, CHD, CHD, CHG, CHG, CHG, CHK, T, T, T,
T, T, T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T, T,
T, T, T, T, CHK, CHD, CHG, CHG, CHG, CHG, CHD, CHK, T, T, T, T,
T, T, T, T, T, CHK, CHD, CHG, CHG, CHD, CHK, T, T, T, T, T,
T, T, T, T, CHK, T, T, T, T, T, CHK, T, T, T, T, T,
T, T, T, T, T, CHK, T, T, T, CHK, T, T, T, T, T, T,
T, T, T, T, T, CHK, T, T, T, CHK, T, T, T, T, T, T,
};
/* Charger death — flattened, X eyes */
static const uint32_t charger_death1[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, CHK, CHK, CHK, CHK, CHK, CHK, CHK, CHK, CHK, CHK, CHK, CHK, T, T,
T, T, CHK, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHG, CHK, T, T,
T, T, CHK, CHG, BLK, CHG, BLK, CHG, BLK, CHG, BLK, CHG, CHG, CHK, T, T,
T, T, CHK, CHG, CHG, BLK, CHG, CHG, CHG, BLK, CHG, CHG, CHG, CHK, T, T,
T, T, CHK, CHD, CHG, CHG, CHD, CHD, CHD, CHG, CHG, CHD, CHG, CHK, T, T,
T, T, T, CHK, CHK, CHK, CHK, CHK, CHK, CHK, CHK, CHK, CHK, T, T, T,
};
/* ── Spawner enemy sprites ─────────────────────────── */
/* Spawner: Purple alien hive pod. 16x16 hitbox.
* Organic, bulbous body with glowing core and tendrils at base. */
#define SPW 0xaa44ccFF /* spawner purple */
#define SPD 0x7b1fa2FF /* spawner dark purple */
#define SPL 0xce93d8FF /* spawner light purple */
#define SPK 0x4a1066FF /* spawner outline */
static const uint32_t spawner_idle1[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, SPK, SPK, SPK, SPK, T, T, T, T, T, T,
T, T, T, T, T, SPK, SPW, SPW, SPW, SPW, SPK, T, T, T, T, T,
T, T, T, T, SPK, SPW, SPW, SPL, SPL, SPW, SPW, SPK, T, T, T, T,
T, T, T, SPK, SPW, SPW, SPL, PRL, PRL, SPL, SPW, SPW, SPK, T, T, T,
T, T, T, SPK, SPW, SPL, PRL, WHT, WHT, PRL, SPL, SPW, SPK, T, T, T,
T, T, SPK, SPW, SPW, SPL, PRL, WHT, WHT, PRL, SPL, SPW, SPW, SPK, T, T,
T, T, SPK, SPW, SPW, SPL, PRL, WHT, WHT, PRL, SPL, SPW, SPW, SPK, T, T,
T, T, T, SPK, SPW, SPW, SPL, PRL, PRL, SPL, SPW, SPW, SPK, T, T, T,
T, T, T, SPK, SPW, SPW, SPW, SPD, SPD, SPW, SPW, SPW, SPK, T, T, T,
T, T, T, T, SPK, SPD, SPW, SPW, SPW, SPW, SPD, SPK, T, T, T, T,
T, T, T, T, SPK, SPK, SPD, SPD, SPD, SPD, SPK, SPK, T, T, T, T,
T, T, T, SPK, SPD, T, SPK, T, T, SPK, T, SPD, SPK, T, T, T,
T, T, SPK, SPD, T, T, T, T, T, T, T, T, SPD, SPK, T, T,
T, T, SPK, T, T, T, T, T, T, T, T, T, T, SPK, T, T,
};
/* Spawner idle frame 2 — core pulses brighter */
static const uint32_t spawner_idle2[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, SPK, SPK, SPK, SPK, T, T, T, T, T, T,
T, T, T, T, T, SPK, SPW, SPW, SPW, SPW, SPK, T, T, T, T, T,
T, T, T, T, SPK, SPW, SPL, SPL, SPL, SPL, SPW, SPK, T, T, T, T,
T, T, T, SPK, SPW, SPL, PRL, WHT, WHT, PRL, SPL, SPW, SPK, T, T, T,
T, T, T, SPK, SPW, PRL, WHT, WHT, WHT, WHT, PRL, SPW, SPK, T, T, T,
T, T, SPK, SPW, SPL, PRL, WHT, WHT, WHT, WHT, PRL, SPL, SPW, SPK, T, T,
T, T, SPK, SPW, SPL, PRL, WHT, WHT, WHT, WHT, PRL, SPL, SPW, SPK, T, T,
T, T, T, SPK, SPW, SPL, PRL, WHT, WHT, PRL, SPL, SPW, SPK, T, T, T,
T, T, T, SPK, SPW, SPW, SPL, SPD, SPD, SPL, SPW, SPW, SPK, T, T, T,
T, T, T, T, SPK, SPD, SPW, SPW, SPW, SPW, SPD, SPK, T, T, T, T,
T, T, T, T, SPK, SPK, SPD, SPD, SPD, SPD, SPK, SPK, T, T, T, T,
T, T, T, SPK, SPD, T, SPK, T, T, SPK, T, SPD, SPK, T, T, T,
T, T, SPK, SPD, T, T, T, T, T, T, T, T, SPD, SPK, T, T,
T, T, SPK, T, T, T, T, T, T, T, T, T, T, SPK, T, T,
};
/* Spawner death — cracked open, dark */
static const uint32_t spawner_death1[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, SPK, T, T, T, T, SPK, T, T, T, T, T,
T, T, T, T, SPK, SPD, SPK, T, T, SPK, SPD, SPK, T, T, T, T,
T, T, T, SPK, SPD, SPW, SPD, SPK, SPK, SPD, SPW, SPD, SPK, T, T, T,
T, T, SPK, SPD, SPW, SPW, SPW, SPD, SPD, SPW, SPW, SPW, SPD, SPK, T, T,
T, T, SPK, SPD, SPW, SPD, SPK, BLK, BLK, SPK, SPD, SPW, SPD, SPK, T, T,
T, T, T, SPK, SPD, SPD, SPK, SPK, SPK, SPK, SPD, SPD, SPK, T, T, T,
T, T, T, T, SPK, SPK, T, T, T, T, SPK, SPK, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
};
/* ── Laser turret sprites ──────────────────────────── */
/* Laser turret: Angular metallic housing with red lens. 14x14 hitbox.
* Wall-mounted feel, darker/more angular than regular turret. */
#define LTG 0x707070FF /* laser turret grey */
#define LTD 0x484848FF /* laser turret dark */
#define LTL 0x989898FF /* laser turret light */
#define LTK 0x303030FF /* laser turret outline */
#define LTR 0xcc3020FF /* laser turret red lens */
#define LRD 0x881818FF /* laser turret red dark */
#define LRL 0xff5040FF /* laser turret red light */
/* Laser turret idle — angular housing, dim red lens */
static const uint32_t laser_idle1[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, LTK, LTK, LTK, LTK, LTK, LTK, LTK, LTK, T, T, T, T,
T, T, T, LTK, LTL, LTL, LTG, LTG, LTG, LTG, LTL, LTL, LTK, T, T, T,
T, T, T, LTK, LTL, LTG, LTG, LTG, LTG, LTG, LTG, LTL, LTK, T, T, T,
T, T, T, LTK, LTG, LTG, LTD, LTD, LTD, LTD, LTG, LTG, LTK, T, T, T,
T, T, T, LTK, LTG, LTD, LRD, LTR, LTR, LRD, LTD, LTG, LTK, T, T, T,
T, T, T, LTK, LTG, LTD, LTR, LRD, LRD, LTR, LTD, LTG, LTK, T, T, T,
T, T, T, LTK, LTG, LTD, LTR, LRD, LRD, LTR, LTD, LTG, LTK, T, T, T,
T, T, T, LTK, LTG, LTD, LRD, LTR, LTR, LRD, LTD, LTG, LTK, T, T, T,
T, T, T, LTK, LTG, LTG, LTD, LTD, LTD, LTD, LTG, LTG, LTK, T, T, T,
T, T, T, LTK, LTL, LTG, LTG, LTG, LTG, LTG, LTG, LTL, LTK, T, T, T,
T, T, T, LTK, LTL, LTL, LTG, LTG, LTG, LTG, LTL, LTL, LTK, T, T, T,
T, T, T, T, LTK, LTK, LTK, LTK, LTK, LTK, LTK, LTK, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
};
/* Laser turret firing — lens glows bright, ring highlight */
static const uint32_t laser_fire1[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, LTK, LTK, LTK, LTK, LTK, LTK, LTK, LTK, T, T, T, T,
T, T, T, LTK, LTL, LTL, LTG, LTG, LTG, LTG, LTL, LTL, LTK, T, T, T,
T, T, T, LTK, LTL, LTG, LTG, LTG, LTG, LTG, LTG, LTL, LTK, T, T, T,
T, T, T, LTK, LTG, LTG, LRL, LRL, LRL, LRL, LTG, LTG, LTK, T, T, T,
T, T, T, LTK, LTG, LRL, LTR, WHT, WHT, LTR, LRL, LTG, LTK, T, T, T,
T, T, T, LTK, LTG, LRL, WHT, WHT, WHT, WHT, LRL, LTG, LTK, T, T, T,
T, T, T, LTK, LTG, LRL, WHT, WHT, WHT, WHT, LRL, LTG, LTK, T, T, T,
T, T, T, LTK, LTG, LRL, LTR, WHT, WHT, LTR, LRL, LTG, LTK, T, T, T,
T, T, T, LTK, LTG, LTG, LRL, LRL, LRL, LRL, LTG, LTG, LTK, T, T, T,
T, T, T, LTK, LTL, LTG, LTG, LTG, LTG, LTG, LTG, LTL, LTK, T, T, T,
T, T, T, LTK, LTL, LTL, LTG, LTG, LTG, LTG, LTL, LTL, LTK, T, T, T,
T, T, T, T, LTK, LTK, LTK, LTK, LTK, LTK, LTK, LTK, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
};
/* ── Spritesheet generation ────────────────────────── */
/* All sprite definitions for the sheet - row, column, pixel data */
@@ -1068,10 +1295,26 @@ static const SpriteDef s_sprite_defs[] = {
{6, 5, asteroid2},
{6, 6, powerup_fuel1},
{6, 7, powerup_fuel2},
/* Row 7: Charger */
{7, 0, charger_idle1},
{7, 1, charger_idle2},
{7, 2, charger_walk1},
{7, 3, charger_walk2},
{7, 4, charger_walk1}, /* reuse frame */
{7, 5, charger_walk2}, /* reuse frame */
{7, 6, charger_death1},
/* Row 8: Spawner + Laser Turret */
{8, 0, spawner_idle1},
{8, 1, spawner_idle2},
{8, 2, spawner_death1},
{8, 3, laser_idle1},
{8, 4, laser_fire1},
};
#define SHEET_COLS 8
#define SHEET_ROWS 7
#define SHEET_ROWS 9
SDL_Texture *sprites_generate(SDL_Renderer *renderer) {
int w = SHEET_COLS * SPRITE_CELL;
@@ -1278,6 +1521,42 @@ static AnimFrame s_drone_frames[] = {
FRAME(1, 6, 0.2f),
};
/* Charger */
static AnimFrame s_charger_idle_frames[] = {
FRAME(0, 7, 0.5f),
FRAME(1, 7, 0.5f),
};
static AnimFrame s_charger_walk_frames[] = {
FRAME(2, 7, 0.12f),
FRAME(3, 7, 0.12f),
FRAME(4, 7, 0.12f),
FRAME(5, 7, 0.12f),
};
static AnimFrame s_charger_death_frames[] = {
FRAME(6, 7, 0.3f),
};
/* Spawner */
static AnimFrame s_spawner_idle_frames[] = {
FRAME(0, 8, 0.6f),
FRAME(1, 8, 0.6f),
};
static AnimFrame s_spawner_death_frames[] = {
FRAME(2, 8, 0.3f),
};
/* Laser Turret */
static AnimFrame s_laser_turret_idle_frames[] = {
FRAME(3, 8, 1.0f),
};
static AnimFrame s_laser_turret_fire_frames[] = {
FRAME(4, 8, 0.15f),
};
/* Exported animation definitions */
AnimDef anim_player_idle;
AnimDef anim_player_run;
@@ -1313,6 +1592,16 @@ AnimDef anim_asteroid;
AnimDef anim_drone;
AnimDef anim_charger_idle;
AnimDef anim_charger_walk;
AnimDef anim_charger_death;
AnimDef anim_spawner_idle;
AnimDef anim_spawner_death;
AnimDef anim_laser_turret_idle;
AnimDef anim_laser_turret_fire;
void sprites_init_anims(void) {
anim_player_idle = (AnimDef){s_player_idle_frames, 2, true, NULL};
anim_player_run = (AnimDef){s_player_run_frames, 4, true, NULL};
@@ -1347,4 +1636,14 @@ void sprites_init_anims(void) {
anim_asteroid = (AnimDef){s_asteroid_frames, 2, true, NULL};
anim_drone = (AnimDef){s_drone_frames, 2, true, NULL};
anim_charger_idle = (AnimDef){s_charger_idle_frames, 2, true, NULL};
anim_charger_walk = (AnimDef){s_charger_walk_frames, 4, true, NULL};
anim_charger_death = (AnimDef){s_charger_death_frames, 1, false, NULL};
anim_spawner_idle = (AnimDef){s_spawner_idle_frames, 2, true, NULL};
anim_spawner_death = (AnimDef){s_spawner_death_frames, 1, false, NULL};
anim_laser_turret_idle = (AnimDef){s_laser_turret_idle_frames, 1, true, NULL};
anim_laser_turret_fire = (AnimDef){s_laser_turret_fire_frames, 1, false, NULL};
}

View File

@@ -8,10 +8,15 @@
* Each sprite cell is 16x16 pixels.
* The sheet is organized in rows:
*
* Row 0: Player idle (2 frames), run (4 frames), jump (1), fall (1)
* Row 1: Grunt idle (2 frames), walk (4 frames), death (2 frames)
* Row 2: Flyer idle (2 frames), fly (4 frames), death (2 frames)
* Row 3: Projectiles: bullet (2 frames), impact (3 frames), enemy bullet (2)
* Row 0: Player idle (2), run (4), jump (1), fall (1)
* Row 1: Grunt idle (2), walk (4), death (1)
* Row 2: Flyer idle (2), fly (4), death (1)
* Row 3: Projectiles: bullet (2), impact (3), enemy bullet (2)
* Row 4: Turret (2), platform (1), flame vent (3), force field (2)
* Row 5: Force field off (1), powerups: health (2), jetpack (2), drone (2)
* Row 6: Drone companion (2), gun powerup (2), asteroid (2), fuel (2)
* Row 7: Charger idle (2), walk (4), death (1)
* Row 8: Spawner idle (2), death (1), laser turret idle (1), fire (1)
*/
#define SPRITE_CELL 16
@@ -62,6 +67,19 @@ extern AnimDef anim_asteroid;
/* ── Drone animation ───────────────────────────── */
extern AnimDef anim_drone;
/* ── Charger animations ────────────────────────── */
extern AnimDef anim_charger_idle;
extern AnimDef anim_charger_walk;
extern AnimDef anim_charger_death;
/* ── Spawner animations ────────────────────────── */
extern AnimDef anim_spawner_idle;
extern AnimDef anim_spawner_death;
/* ── Laser turret animations ───────────────────── */
extern AnimDef anim_laser_turret_idle;
extern AnimDef anim_laser_turret_fire;
/* Initialize all animation definitions */
void sprites_init_anims(void);