Add moon surface intro level with asteroid hazards and unarmed mechanics

Introduce moon01.lvl as the starting level — a pure jump-and-run intro
with no gun and no enemies, just platforming over gaps and dodging falling
asteroids. The player picks up their gun upon transitioning to level01.

New features:
- Moon tileset and PARALLAX_STYLE_MOON with crater terrain backgrounds
- Asteroid entity (ENT_ASTEROID): falls from sky, damages on contact,
  explodes on ground with particles, respawns after delay
- PLAYER_UNARMED directive disables gun for the level
- Pit rescue mechanic: falling costs 1 HP and auto-dashes upward
- Gun powerup entity type for future armed-pickup levels
- Segment-based procedural level generator with themed rooms
- Extended editor with entity palette and improved tile cycling
- Web shell improvements for Emscripten builds
This commit is contained in:
Thomas
2026-03-01 09:20:49 +00:00
parent ea6e16358f
commit fac7085056
30 changed files with 2139 additions and 83 deletions

View File

@@ -860,6 +860,90 @@ static const uint32_t drone_frame2[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
};
/* ── Gun powerup sprite ────────────────────────────── */
/* Gun powerup — metallic gun icon (grey/cyan) */
static const uint32_t powerup_gun1[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, GYD, GYD, GYD, GYD, GYD, GYD, GYD, T, T, T,
T, T, T, T, GYD, GYD, GRY, GRY, GYL, GYL, GYL, GRY, GRY, GYD, T, T,
T, T, T, GYD, GRY, GRY, GYL, WHT, WHT, WHT, GYL, GRY, GRY, GRY, GYD, T,
T, T, T, GYD, GRY, GYL, WHT, WHT, WHT, WHT, GYL, GRY, GRY, GRY, GYD, T,
T, T, T, T, GYD, GRY, GRY, GYL, GYL, GRY, GRY, GYD, GYD, GYD, T, T,
T, T, T, T, T, T, GYD, GRY, GRY, GYD, T, T, T, T, T, T,
T, T, T, T, T, T, T, GYD, GYD, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
};
/* Gun powerup frame 2 — brighter glow */
static const uint32_t powerup_gun2[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, GYD, GYL, GYL, GYL, GYL, GYL, GYL, GYD, T, T, T,
T, T, T, GYD, GYD, GYL, WHT, WHT, WHT, WHT, WHT, GYL, GYL, GYD, T, T,
T, T, T, GYD, GYL, WHT, WHT, WHT, WHT, WHT, WHT, GYL, GYL, GYL, GYD, T,
T, T, T, GYD, GYL, WHT, WHT, WHT, WHT, WHT, WHT, GYL, GYL, GYL, GYD, T,
T, T, T, T, GYD, GYL, GYL, WHT, WHT, GYL, GYL, GYD, GYD, GYD, T, T,
T, T, T, T, T, T, GYD, GYL, GYL, GYD, T, T, T, T, T, T,
T, T, T, T, T, T, T, GYD, GYD, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
};
/* ── Asteroid sprite ────────────────────────────────── */
/* Asteroid frame 1 — jagged rocky boulder */
static const uint32_t asteroid1[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, BRN, BRN, BRN, T, T, T, T, T, T, T,
T, T, T, T, T, BRN, BRD, BRN, BRD, BRN, T, T, T, T, T, T,
T, T, T, T, BRN, BRD, GYD, BRD, GYD, BRD, BRN, T, T, T, T, T,
T, T, T, BRN, BRD, GYD, GRY, GYD, GRY, GYD, BRD, BRN, T, T, T, T,
T, T, BRN, BRD, GYD, GRY, GYL, GRY, GRY, GYD, GYD, BRD, BRN, T, T, T,
T, T, BRN, BRD, GYD, GRY, GRY, GYD, GRY, GYL, GYD, BRD, BRN, T, T, T,
T, T, BRD, BRD, GYD, GRY, GYD, BRD, GYD, GRY, GYD, BRD, BRD, T, T, T,
T, T, BRN, BRD, GYD, GYD, GRY, GYD, GRY, GYD, GYD, BRD, BRN, T, T, T,
T, T, BRN, BRD, GYD, GRY, GYL, GRY, GYD, GRY, GYD, BRD, BRN, T, T, T,
T, T, T, BRN, BRD, GYD, GRY, GYD, GRY, GYD, BRD, BRN, T, T, T, T,
T, T, T, T, BRN, BRD, GYD, BRD, GYD, BRD, BRN, T, T, T, T, T,
T, T, T, T, T, BRN, BRD, BRN, BRD, BRN, T, T, T, T, T, T,
T, T, T, T, T, T, BRN, BRN, BRN, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
};
/* Asteroid frame 2 — rotated highlights */
static const uint32_t asteroid2[16*16] = {
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
T, T, T, T, T, T, BRN, BRN, BRN, T, T, T, T, T, T, T,
T, T, T, T, T, BRN, BRD, BRN, BRD, BRN, T, T, T, T, T, T,
T, T, T, T, BRN, BRD, GYD, BRD, GYD, BRD, BRN, T, T, T, T, T,
T, T, T, BRN, BRD, GYD, GRY, GYD, GYL, GYD, BRD, BRN, T, T, T, T,
T, T, BRN, BRD, GYD, GRY, GYD, GRY, GRY, GYL, GYD, BRD, BRN, T, T, T,
T, T, BRN, BRD, GYD, GYD, GRY, GYD, GYL, GRY, GYD, BRD, BRN, T, T, T,
T, T, BRD, BRD, GYD, GRY, GYD, BRD, GYD, GYD, GYD, BRD, BRD, T, T, T,
T, T, BRN, BRD, GYD, GYL, GRY, GYD, GRY, GYD, GYD, BRD, BRN, T, T, T,
T, T, BRN, BRD, GYD, GRY, GRY, GYD, GYD, GRY, GYD, BRD, BRN, T, T, T,
T, T, T, BRN, BRD, GYD, GYL, GYD, GRY, GYD, BRD, BRN, T, T, T, T,
T, T, T, T, BRN, BRD, GYD, BRD, GYD, BRD, BRN, T, T, T, T, T,
T, T, T, T, T, BRN, BRD, BRN, BRD, BRN, T, T, T, T, T, T,
T, T, T, T, T, T, BRN, BRN, BRN, T, T, T, T, T, T, T,
T, T, T, T, T, T, T, T, 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 */
@@ -926,9 +1010,13 @@ static const SpriteDef s_sprite_defs[] = {
{5, 5, powerup_drone1},
{5, 6, powerup_drone2},
/* Row 6: Drone companion */
/* Row 6: Drone companion + Gun powerup + Asteroid */
{6, 0, drone_frame1},
{6, 1, drone_frame2},
{6, 2, powerup_gun1},
{6, 3, powerup_gun2},
{6, 4, asteroid1},
{6, 5, asteroid2},
};
#define SHEET_COLS 8
@@ -1115,6 +1203,18 @@ static AnimFrame s_powerup_drone_frames[] = {
FRAME(6, 5, 0.3f),
};
/* Gun powerup */
static AnimFrame s_powerup_gun_frames[] = {
FRAME(2, 6, 0.4f),
FRAME(3, 6, 0.4f),
};
/* Asteroid */
static AnimFrame s_asteroid_frames[] = {
FRAME(4, 6, 0.15f),
FRAME(5, 6, 0.15f),
};
/* Drone companion */
static AnimFrame s_drone_frames[] = {
FRAME(0, 6, 0.2f),
@@ -1150,6 +1250,8 @@ AnimDef anim_force_field_off;
AnimDef anim_powerup_health;
AnimDef anim_powerup_jetpack;
AnimDef anim_powerup_drone;
AnimDef anim_powerup_gun;
AnimDef anim_asteroid;
AnimDef anim_drone;
@@ -1182,6 +1284,8 @@ void sprites_init_anims(void) {
anim_powerup_health = (AnimDef){s_powerup_health_frames, 2, true, NULL};
anim_powerup_jetpack = (AnimDef){s_powerup_jetpack_frames, 2, true, NULL};
anim_powerup_drone = (AnimDef){s_powerup_drone_frames, 2, true, NULL};
anim_powerup_gun = (AnimDef){s_powerup_gun_frames, 2, true, NULL};
anim_asteroid = (AnimDef){s_asteroid_frames, 2, true, NULL};
anim_drone = (AnimDef){s_drone_frames, 2, true, NULL};
}