Add height zones, jetpack boost particles, and TigerStyle guidelines

Level generator: add vertical variety with tall levels (46 tiles).
Segment generators accept ground_row parameter, SEG_CLIMB connects
height zones, transitions inherit predecessor ground row to prevent
walkability gaps. Climb segments respect traversal direction.

Jetpack boost: add blue flame particles during dash (burst + trail)
and continuous idle glow from player back while boost timer is active.

Camera: add 30px vertical look-ahead when player velocity exceeds
50 px/s.

Fix flame vent pedestal in gen_pit to use ground-relative position
instead of map bottom (broken in tall HIGH-zone levels).

Add TigerStyle coding guidelines to AGENTS.md adapted for C11.
Add tall_test.lvl (40x46) for height zone validation.
This commit is contained in:
Thomas
2026-03-01 14:57:53 +00:00
parent 9d828c47b1
commit ad2d68a8b4
8 changed files with 813 additions and 192 deletions

View File

@@ -301,6 +301,137 @@ void particle_emit_jetpack_trail(Vec2 pos, Vec2 dash_dir) {
particle_emit(&smoke);
}
void particle_emit_jetpack_boost_burst(Vec2 pos, Vec2 dash_dir) {
/* Blue flame accents mixed into the regular jetpack burst */
float exhaust_angle = atan2f(-dash_dir.y, -dash_dir.x);
/* Hot blue core — bright electric blue */
ParticleBurst core = {
.origin = pos,
.count = 10,
.speed_min = 90.0f,
.speed_max = 220.0f,
.life_min = 0.15f,
.life_max = 0.35f,
.size_min = 2.0f,
.size_max = 4.5f,
.spread = 0.4f,
.direction = exhaust_angle,
.drag = 2.5f,
.gravity_scale = 0.05f,
.color = {80, 160, 255, 255}, /* electric blue */
.color_vary = true,
};
particle_emit(&core);
/* Outer blue-white flare */
ParticleBurst flare = {
.origin = pos,
.count = 8,
.speed_min = 40.0f,
.speed_max = 130.0f,
.life_min = 0.2f,
.life_max = 0.4f,
.size_min = 1.5f,
.size_max = 3.0f,
.spread = 0.7f,
.direction = exhaust_angle,
.drag = 3.0f,
.gravity_scale = 0.1f,
.color = {140, 200, 255, 255}, /* light blue */
.color_vary = true,
};
particle_emit(&flare);
}
void particle_emit_jetpack_boost_trail(Vec2 pos, Vec2 dash_dir) {
/* Continuous blue flame trail while dashing with boost active */
float exhaust_angle = atan2f(-dash_dir.y, -dash_dir.x);
/* Blue flame sparks */
ParticleBurst sparks = {
.origin = pos,
.count = 2,
.speed_min = 50.0f,
.speed_max = 140.0f,
.life_min = 0.1f,
.life_max = 0.25f,
.size_min = 1.5f,
.size_max = 3.0f,
.spread = 0.35f,
.direction = exhaust_angle,
.drag = 3.0f,
.gravity_scale = 0.05f,
.color = {60, 140, 255, 255}, /* bright blue */
.color_vary = true,
};
particle_emit(&sparks);
/* Blue-white wisps */
ParticleBurst wisps = {
.origin = pos,
.count = 1,
.speed_min = 20.0f,
.speed_max = 50.0f,
.life_min = 0.15f,
.life_max = 0.35f,
.size_min = 2.0f,
.size_max = 3.5f,
.spread = 0.5f,
.direction = exhaust_angle,
.drag = 3.5f,
.gravity_scale = -0.05f,
.color = {160, 210, 255, 200}, /* pale blue */
.color_vary = true,
};
particle_emit(&wisps);
}
void particle_emit_jetpack_boost_idle(Vec2 pos, bool facing_left) {
/* Ambient blue flame simmering from the player's back while boost is
* active but the player isn't dashing. Exhaust drifts backward and
* slightly downward — a subtle idle glow effect. */
float exhaust_angle = facing_left ? 0.0f : (float)M_PI; /* away from facing */
/* Small blue sparks drifting backward */
ParticleBurst sparks = {
.origin = pos,
.count = 1,
.speed_min = 15.0f,
.speed_max = 45.0f,
.life_min = 0.12f,
.life_max = 0.3f,
.size_min = 1.0f,
.size_max = 2.5f,
.spread = 0.8f,
.direction = exhaust_angle,
.drag = 4.0f,
.gravity_scale = 0.15f,
.color = {50, 130, 255, 220}, /* medium blue */
.color_vary = true,
};
particle_emit(&sparks);
/* Faint blue-white wisps floating up */
ParticleBurst wisps = {
.origin = pos,
.count = 1,
.speed_min = 8.0f,
.speed_max = 25.0f,
.life_min = 0.15f,
.life_max = 0.35f,
.size_min = 1.5f,
.size_max = 2.5f,
.spread = 1.0f,
.direction = exhaust_angle - 0.3f, /* slightly upward */
.drag = 4.5f,
.gravity_scale = -0.1f,
.color = {140, 200, 255, 160}, /* pale blue, translucent */
.color_vary = true,
};
particle_emit(&wisps);
}
void particle_emit_muzzle_flash(Vec2 pos, Vec2 shoot_dir) {
float angle = atan2f(shoot_dir.y, shoot_dir.x);