Particle effects: falling trails behind projectiles, exploding fireballs and cannonballs, arrows stuck in walls and floors
- projectiles get styles: arrows (shaft and fletching), cannonballs and fireballs fly in an arc, shadow, holy, arcane, rune and magic bolts with their own glow - trails of embers, sparks, motes or smoke that drift down to the floor - fireballs explode on the ground (flash, shock wave, embers, smoke, scorch mark), cannonballs burst into stones, dirt, sparks and smoke - arrows stay stuck in walls (or glance off into the floor) for five minutes, at most 400; they drop out when the wall is dug away - spells: lightning sprays sparks and scorches the floor, healing lifts green motes, level-ups and summoning rain sparkles; digging throws earth, gold pops coins - particles are never saved and use their own random numbers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
33b9879b4c
commit
80982b71f7
4 files changed
+487
-10
No files matched your search
@@ -1026,6 +1026,7 @@ impl R3d {
|
||||
draw_landmarks_solid(g, t);
|
||||
draw_towers_solid(g);
|
||||
crate::props::draw_props_solid(g, cam, t);
|
||||
draw_parts_solid(g);
|
||||
|
||||
// ---- unlit glow / overlays
|
||||
gl_use_material(&self.glow);
|
||||
@@ -1062,6 +1063,7 @@ impl R3d {
|
||||
for f in &g.fx {
|
||||
draw_fx(f, t);
|
||||
}
|
||||
draw_parts_glow(g);
|
||||
gl_use_default_material();
|
||||
crate::ui::begin_ui();
|
||||
if let (true, Some((rt, tw, th))) = (div > 1, self.target.as_ref()) {
|
||||
@@ -2070,12 +2072,105 @@ fn draw_flag(f: usize, t: f32) {
|
||||
draw_mesh(&Mesh { vertices: v, indices: vec![0, 1, 2], texture: None });
|
||||
}
|
||||
|
||||
// projectiles with a look of their own (styles in particles.rs)
|
||||
fn draw_projectile(f: &Fx, p: f32, t: f32) {
|
||||
use crate::particles::*;
|
||||
let at = |q: f32| {
|
||||
let (x, y, h) = fx_pos(f, q);
|
||||
vec3(x, h, y)
|
||||
};
|
||||
let head = at(p);
|
||||
let c = rgba(f.col);
|
||||
let sz = f.size;
|
||||
match f.style {
|
||||
ST_ARROW | ST_ARROW_WALL => {
|
||||
let back = at((p - 0.08).max(0.0));
|
||||
let dir = (head - back).normalize_or_zero();
|
||||
line3(head - dir * 0.3, head, 0.03, Color::from_rgba(120, 84, 48, 255));
|
||||
draw_cube(head, Vec3::splat(0.035), None, Color::from_rgba(200, 200, 210, 255));
|
||||
draw_cube(head - dir * 0.28, Vec3::splat(0.05), None, Color::from_rgba(230, 230, 230, 255));
|
||||
}
|
||||
ST_CANNON => {
|
||||
draw_sphere(head, 0.1 * sz, None, Color::from_rgba(24, 24, 28, 255));
|
||||
draw_sphere(head, 0.14 * sz, None, Color::new(1.0, 0.6, 0.2, 0.15));
|
||||
}
|
||||
ST_FIRE => {
|
||||
let flick = 1.0 + (t * 30.0 + f.x).sin() * 0.12;
|
||||
draw_sphere(head, 0.08 * sz * flick, None, Color::new(1.0, 0.95, 0.6, 1.0));
|
||||
draw_sphere(head, 0.14 * sz * flick, None, Color::new(1.0, 0.55, 0.12, 0.8));
|
||||
draw_sphere(head, 0.24 * sz * flick, None, Color::new(1.0, 0.3, 0.05, 0.25));
|
||||
}
|
||||
_ => {
|
||||
let tail = at((p - 0.3).max(0.0));
|
||||
line3(tail, head, 0.06 * sz, Color::new(c.r, c.g, c.b, 0.7));
|
||||
draw_sphere(head, 0.08 * sz, None, c);
|
||||
draw_sphere(head, 0.17 * sz, None, Color::new(c.r, c.g, c.b, 0.25));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// particles that glow (embers, sparks, smoke, flashes, shock waves)
|
||||
fn draw_parts_glow(g: &Game) {
|
||||
use crate::particles::*;
|
||||
for p in &g.parts {
|
||||
let c = rgba(p.col);
|
||||
let life = (p.t / p.max).clamp(0.0, 1.0);
|
||||
let at = vec3(p.x, p.h, p.y);
|
||||
match p.kind {
|
||||
EMBER | SPARK | MOTE => {
|
||||
let s = p.size * (1.0 - life * 0.6);
|
||||
draw_cube(at, Vec3::splat(s), None, Color::new(c.r, c.g, c.b, 1.0 - life * life));
|
||||
}
|
||||
SMOKE => draw_sphere(at, p.size, None, Color::new(c.r, c.g, c.b, 0.35 * (1.0 - life))),
|
||||
FLASH => {
|
||||
let r = p.size * (0.3 + life * 0.9);
|
||||
draw_sphere(at, r * 0.6, None, Color::new(1.0, 0.95, 0.75, 1.0 - life));
|
||||
draw_sphere(at, r, None, Color::new(c.r, c.g, c.b, 0.6 * (1.0 - life)));
|
||||
}
|
||||
RING => ring(at, p.size * (0.2 + life), Color::new(c.r, c.g, c.b, 0.8 * (1.0 - life))),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// scorch marks on the floor
|
||||
for p in g.stuck.iter().filter(|p| p.kind == SCORCH) {
|
||||
let fade = 1.0 - (p.t / p.max).clamp(0.0, 1.0);
|
||||
disc(vec3(p.x, 0.015, p.y), p.size, Color::new(0.03, 0.02, 0.02, 0.55 * fade), 14);
|
||||
}
|
||||
}
|
||||
|
||||
// solid particles: debris, coins and the arrows stuck in walls and floors
|
||||
fn draw_parts_solid(g: &Game) {
|
||||
use crate::particles::*;
|
||||
for p in &g.parts {
|
||||
let c = rgba(p.col);
|
||||
let at = vec3(p.x, p.h, p.y);
|
||||
match p.kind {
|
||||
DEBRIS | COIN => {
|
||||
let life = (p.t / p.max).clamp(0.0, 1.0);
|
||||
let s = p.size * if life > 0.75 { (1.0 - life) * 4.0 } else { 1.0 };
|
||||
draw_cube(at, vec3(s, if p.kind == COIN { s * 0.4 } else { s }, s), None, c);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
for p in g.stuck.iter().filter(|p| p.kind == ARROW) {
|
||||
let left = p.max - p.t;
|
||||
let k = if left < 10.0 { left / 10.0 } else { 1.0 };
|
||||
let tip = vec3(p.x, p.h, p.y);
|
||||
let dir = vec3(p.vx, p.vh, p.vy).normalize_or_zero();
|
||||
let tail = tip - dir * (0.32 * k);
|
||||
line3(tail, tip + dir * 0.03, 0.03, Color::from_rgba(116, 80, 46, 255));
|
||||
draw_cube(tail, vec3(0.05, 0.05, 0.05) * k, None, Color::from_rgba(226, 226, 226, 255));
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_fx(f: &Fx, t: f32) {
|
||||
let p = (f.t / f.max).clamp(0.0, 1.0);
|
||||
let c = rgba(f.col);
|
||||
let a = |alpha: f32| Color::new(c.r, c.g, c.b, alpha);
|
||||
let at = vec3(f.x, f.h, f.y);
|
||||
match f.kind {
|
||||
0 if f.style != crate::particles::ST_PLAIN => draw_projectile(f, p, t),
|
||||
0 => {
|
||||
let from = vec3(f.x, f.h, f.y);
|
||||
let to = vec3(f.x2, f.h2, f.y2);
|
||||
|
||||
Reference in new issue
Block a user