Ragged scorch marks with fresh embers; explosions, projectiles and spells cast light
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2a6892f9a6
commit
730ad08d7a
1 file changed
+80
-8
+80
-8
@@ -1148,11 +1148,42 @@ impl R3d {
|
|||||||
push(vec3(x, tower_height(tw) + 0.6, z), 4.5, vec3(0.75, 0.4, 1.0), 0.8 + tw.flash * 3.0, 10.0);
|
push(vec3(x, tower_height(tw) + 0.6, z), 4.5, vec3(0.75, 0.4, 1.0), 0.8 + tw.flash * 3.0, 10.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// effects light up their surroundings: lightning, explosions, glowing projectiles, spells
|
||||||
|
let tint = |c: u32| {
|
||||||
|
let c = rgba(c);
|
||||||
|
vec3(c.r, c.g, c.b)
|
||||||
|
};
|
||||||
for f in &g.fx {
|
for f in &g.fx {
|
||||||
if f.kind == 1 {
|
let left = 1.0 - (f.t / f.max).clamp(0.0, 1.0);
|
||||||
push(vec3(f.x, 2.0, f.y), 8.0, vec3(1.0, 1.0, 0.85), 3.0 * (1.0 - f.t / f.max), 200.0);
|
match f.kind {
|
||||||
|
1 => push(vec3(f.x, 2.0, f.y), 8.0, vec3(1.0, 1.0, 0.85), 3.0 * left, 200.0),
|
||||||
|
0 => {
|
||||||
|
use crate::particles::*;
|
||||||
|
let c = match f.style {
|
||||||
|
ST_FIRE => Some(vec3(1.0, 0.55, 0.15)),
|
||||||
|
ST_SHADOW => Some(vec3(0.75, 0.3, 1.0)),
|
||||||
|
ST_HOLY => Some(vec3(1.0, 0.92, 0.6)),
|
||||||
|
ST_ARCANE => Some(vec3(1.0, 0.7, 0.4)),
|
||||||
|
ST_RUNE => Some(vec3(0.4, 0.8, 1.0)),
|
||||||
|
ST_MAGIC => Some(vec3(1.0, 0.4, 0.9)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let (Some(c), true) = (c, left > 0.0) {
|
||||||
|
let (x, z, h) = fx_pos(f, 1.0 - left);
|
||||||
|
push(vec3(x, h, z), 3.0 + f.size, c, 1.4, 120.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// healing and spells (level ups, specials): a glow in the effect's colour
|
||||||
|
4 => push(vec3(f.x, 0.8, f.y), 4.0, vec3(0.5, 1.0, 0.55), 1.6 * left, 130.0),
|
||||||
|
7 => push(vec3(f.x, 0.9, f.y), 5.0, tint(f.col), 2.0 * left, 130.0),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// explosions (fireballs, cannonballs, rune strikes): a bright, short flash of light
|
||||||
|
for p in g.parts.iter().filter(|p| p.kind == crate::particles::FLASH) {
|
||||||
|
let left = 1.0 - (p.t / p.max).clamp(0.0, 1.0);
|
||||||
|
push(vec3(p.x, p.h + 0.4, p.y), 3.0 + 4.0 * p.size, tint(p.col), 3.5 * left * left.sqrt(), 180.0);
|
||||||
|
}
|
||||||
l.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
|
l.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
|
||||||
l.truncate(self.max_lights.min(MAX_LIGHTS));
|
l.truncate(self.max_lights.min(MAX_LIGHTS));
|
||||||
let mut pos = vec![Vec4::ZERO; MAX_LIGHTS];
|
let mut pos = vec![Vec4::ZERO; MAX_LIGHTS];
|
||||||
@@ -2620,11 +2651,52 @@ fn draw_traps_glow(g: &Game, t: f32) {
|
|||||||
// particles that glow (embers, sparks, smoke, flashes, shock waves)
|
// particles that glow (embers, sparks, smoke, flashes, shock waves)
|
||||||
fn draw_scorch_glow(g: &Game) {
|
fn draw_scorch_glow(g: &Game) {
|
||||||
use crate::particles::*;
|
use crate::particles::*;
|
||||||
|
// all marks go into one mesh (one draw call)
|
||||||
|
let mut v: Vec<Vertex> = Vec::new();
|
||||||
|
let mut ix: Vec<u16> = Vec::new();
|
||||||
|
let mut disc = |c: Vec3, r: f32, color: Color, seg: usize| {
|
||||||
|
if v.len() + seg + 1 > 60000 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cc = [(color.r * 255.0) as u8, (color.g * 255.0) as u8, (color.b * 255.0) as u8, (color.a * 255.0) as u8];
|
||||||
|
let b = v.len() as u16;
|
||||||
|
v.push(Vertex { position: c, uv: Vec2::ZERO, color: cc, normal: Vec4::Y });
|
||||||
|
for k in 0..seg {
|
||||||
|
let a = k as f32 / seg as f32 * std::f32::consts::TAU;
|
||||||
|
v.push(Vertex { position: c + vec3(a.cos() * r, 0.0, a.sin() * r), uv: Vec2::ZERO, color: cc, normal: Vec4::Y });
|
||||||
|
}
|
||||||
|
for k in 0..seg as u16 {
|
||||||
|
ix.extend_from_slice(&[b, b + 1 + k, b + 1 + (k + 1) % seg as u16]);
|
||||||
|
}
|
||||||
|
};
|
||||||
for p in g.stuck.iter().filter(|p| p.kind == SCORCH) {
|
for p in g.stuck.iter().filter(|p| p.kind == SCORCH) {
|
||||||
let fade = 1.0 - (p.t / p.max).clamp(0.0, 1.0);
|
let life = (p.t / p.max).clamp(0.0, 1.0);
|
||||||
let at = vec3(p.x, floor_y(g, p.x, p.y) + 0.018, p.y);
|
// stays dark for most of its time, then fades
|
||||||
disc(at, p.size, Color::new(0.02, 0.017, 0.017, 0.78 * fade), 14);
|
let fade = if life < 0.6 { 1.0 } else { 1.0 - (life - 0.6) / 0.4 };
|
||||||
ring(at + Vec3::Y * 0.002, p.size * 1.06, Color::new(0.55, 0.22, 0.06, 0.22 * fade));
|
let base = vec3(p.x, floor_y(g, p.x, p.y) + 0.016, p.y);
|
||||||
|
// a ragged blotch of soot: overlapping soft spots around the centre, their places
|
||||||
|
// fixed by the position (so the mark keeps its shape)
|
||||||
|
let mut h = ((p.x * 73.0) as i32 as u32).wrapping_mul(2654435761) ^ ((p.y * 91.0) as i32 as u32).wrapping_mul(2246822519);
|
||||||
|
let mut next = || {
|
||||||
|
h ^= h >> 13;
|
||||||
|
h = h.wrapping_mul(1274126177);
|
||||||
|
(h >> 8) as f32 / 16777216.0
|
||||||
|
};
|
||||||
|
for k in 0..6 {
|
||||||
|
let (a, d) = (next() * std::f32::consts::TAU, if k == 0 { 0.0 } else { p.size * (0.25 + 0.3 * next()) });
|
||||||
|
let r = p.size * if k == 0 { 0.65 } else { 0.3 + 0.25 * next() };
|
||||||
|
let at = base + vec3(a.cos() * d, 0.0005 * k as f32, a.sin() * d);
|
||||||
|
disc(at, r, Color::new(0.03, 0.025, 0.022, 0.32 * fade), 10);
|
||||||
|
disc(at, r * 0.6, Color::new(0.015, 0.012, 0.01, 0.3 * fade), 8);
|
||||||
|
}
|
||||||
|
// freshly burnt: embers glow in the middle for a few seconds
|
||||||
|
if p.t < 4.0 {
|
||||||
|
let k = 1.0 - p.t / 4.0;
|
||||||
|
disc(base + Vec3::Y * 0.004, p.size * 0.45, Color::new(1.0, 0.38, 0.08, 0.45 * k * k), 12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ix.is_empty() {
|
||||||
|
draw_mesh(&Mesh { vertices: v, indices: ix, texture: None });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2642,8 +2714,8 @@ fn draw_parts_glow(g: &Game) {
|
|||||||
SMOKE => draw_sphere(at, p.size, None, Color::new(c.r, c.g, c.b, 0.14 * (1.0 - life))),
|
SMOKE => draw_sphere(at, p.size, None, Color::new(c.r, c.g, c.b, 0.14 * (1.0 - life))),
|
||||||
FLASH => {
|
FLASH => {
|
||||||
let r = p.size * (0.3 + life * 0.9);
|
let r = p.size * (0.3 + life * 0.9);
|
||||||
draw_sphere(at, r * 0.6, None, Color::new(1.0, 0.9, 0.7, 0.7 * (1.0 - life)));
|
draw_sphere(at, r * 0.6, None, Color::new(1.0, 0.95, 0.8, 0.95 * (1.0 - life)));
|
||||||
draw_sphere(at, r, None, Color::new(c.r, c.g, c.b, 0.35 * (1.0 - life)));
|
draw_sphere(at, r, None, Color::new(c.r, c.g, c.b, 0.55 * (1.0 - life)));
|
||||||
}
|
}
|
||||||
RING => ring(at, p.size * (0.2 + life), Color::new(c.r, c.g, c.b, 0.8 * (1.0 - life))),
|
RING => ring(at, p.size * (0.2 + life), Color::new(c.r, c.g, c.b, 0.8 * (1.0 - life))),
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|||||||
Reference in new issue
Block a user