feat: explosions, particle system, electricity, structural integrity

Explosions:
- trigger_explosion(x, y, radius, damage) in Game
- Destroys non-static solids, ignites empty cells, pressure spike
- Knockback to entities (velocity away from center + upward pop)
- Particle burst on explosion (fire + smoke + debris)

Particle system:
- ParticleManager in src/physics/particle.rs (capacity 2000)
- CPU-side: position, velocity, life, color, gravity, size
- spawn_burst() for radial bursts, individual spawn() for ambient
- Rendered as third draw call in graphics.rs (is_ui: 2 in shader)
- ParticleInstance = ColorInstance compatible (12 bytes)
- upload_particles() via GpuRenderer trait
- Ambient: fire sparks, lava bubbles — scanned around player

Electricity:
- chunk.electricity: Vec<u8> (4KB/chunk) — current strength 0-255
- Material.conductive + conductivity fields (water = conductive)
- electricity_step() in CA: propagates current through conductive
  neighbors, decays over time, skips non-conductive cells
- cells_swap swaps electricity, serialization saves/loads it

Structural integrity:
- Material.structural: bool (stone, wood = true)
- structural_step() in CA (every 30 ticks, infinite mode only):
  checks if structural cells have support (below, below-left, below-right)
  converts unsupported to Sand (falls)
- Only active in infinite mode to avoid breaking test grids

Graphics shader: is_ui==2 branch renders particles in world-space
All 185 tests + 14 scenarios pass. 143 FPS benchmark.
This commit is contained in:
Emil
2026-06-22 20:13:07 +03:00
parent 4a89612081
commit 0d54d987ac
13 changed files with 631 additions and 21 deletions
+22 -10
View File
@@ -71,17 +71,29 @@ vec3 compute_light(ivec2 world_pos) {
}
void main() {
vec2 pixel = (in_grid + in_pos) * pc.cell_size;
gl_Position = vec4(
2.0 * pixel.x / pc.screen_size.x - 1.0,
2.0 * pixel.y / pc.screen_size.y - 1.0,
0.0, 1.0
);
out_color = in_color;
if (pc.is_ui != 0u) {
if (pc.is_ui == 2u) {
vec2 world_pos = in_grid;
vec2 screen = (world_pos - vec2(pc.cam_pos)) * pc.cell_size + in_pos * pc.cell_size;
gl_Position = vec4(
2.0 * screen.x / pc.screen_size.x - 1.0,
2.0 * screen.y / pc.screen_size.y - 1.0,
0.0, 1.0
);
out_color = in_color;
out_light = vec3(1.0);
} else {
ivec2 world_pos = ivec2(in_grid + vec2(pc.cam_pos));
out_light = compute_light(world_pos);
vec2 pixel = (in_grid + in_pos) * pc.cell_size;
gl_Position = vec4(
2.0 * pixel.x / pc.screen_size.x - 1.0,
2.0 * pixel.y / pc.screen_size.y - 1.0,
0.0, 1.0
);
out_color = in_color;
if (pc.is_ui != 0u) {
out_light = vec3(1.0);
} else {
ivec2 world_pos = ivec2(in_grid + vec2(pc.cam_pos));
out_light = compute_light(world_pos);
}
}
}
Binary file not shown.