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
+38
-10
@@ -379,6 +379,7 @@ pub struct Fx {
|
||||
pub h: f32, // height of the start point (3D)
|
||||
pub h2: f32, // height of the end point (projectiles)
|
||||
pub size: f32, // 1 = normal; attacks of higher levels look bigger
|
||||
pub style: u8, // projectiles: arrow, cannonball, fireball, ... (see particles.rs)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -446,6 +447,12 @@ pub struct Game {
|
||||
#[serde(skip)]
|
||||
pub springs: usize, // claimed mana springs (recounted)
|
||||
#[serde(skip)]
|
||||
pub parts: Vec<crate::particles::Part>, // visual particles (never saved)
|
||||
#[serde(skip)]
|
||||
pub stuck: Vec<crate::particles::Part>, // arrows stuck in walls and floors, scorch marks
|
||||
#[serde(skip, default = "crate::particles::vrng")]
|
||||
pub vrng: Rng, // random numbers for the particles only
|
||||
#[serde(skip)]
|
||||
pub demo: Option<crate::demo::Demo>, // the endless bridge battle behind the title menu
|
||||
#[serde(default)]
|
||||
pub dwarf_caves: Vec<crate::dwarves::DwarfCave>,
|
||||
@@ -606,6 +613,9 @@ impl Game {
|
||||
springs: 0,
|
||||
freed: 0,
|
||||
demo: None,
|
||||
parts: Vec::new(),
|
||||
stuck: Vec::new(),
|
||||
vrng: crate::particles::vrng(),
|
||||
dwarf_caves: Vec::new(),
|
||||
dwarf_t: 30.0,
|
||||
rune_free: 0,
|
||||
@@ -922,8 +932,9 @@ impl Game {
|
||||
|
||||
pub fn fx(&mut self, kind: u8, x: f32, y: f32, x2: f32, y2: f32, max: f32, col: u32) {
|
||||
let h = if kind == 0 { 0.45 } else { 0.4 };
|
||||
self.fx_particles(kind, x, y, h, col);
|
||||
if self.fx.len() < 400 {
|
||||
self.fx.push(Fx { kind, x, y, x2, y2, t: 0.0, max, col, h, h2: 0.45, size: 1.0 });
|
||||
self.fx.push(Fx { kind, x, y, x2, y2, t: 0.0, max, col, h, h2: 0.45, size: 1.0, style: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -931,7 +942,7 @@ impl Game {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn shot(&mut self, x: f32, y: f32, h: f32, x2: f32, y2: f32, h2: f32, max: f32, col: u32) {
|
||||
if self.fx.len() < 400 {
|
||||
self.fx.push(Fx { kind: 0, x, y, x2, y2, t: 0.0, max, col, h, h2, size: 1.0 });
|
||||
self.fx.push(Fx { kind: 0, x, y, x2, y2, t: 0.0, max, col, h, h2, size: 1.0, style: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -944,8 +955,9 @@ impl Game {
|
||||
|
||||
// effect at a given height
|
||||
pub fn fx_at(&mut self, kind: u8, x: f32, y: f32, h: f32, max: f32, col: u32) {
|
||||
self.fx_particles(kind, x, y, h, col);
|
||||
if self.fx.len() < 400 {
|
||||
self.fx.push(Fx { kind, x, y, x2: 0.0, y2: 0.0, t: 0.0, max, col, h, h2: h, size: 1.0 });
|
||||
self.fx.push(Fx { kind, x, y, x2: 0.0, y2: 0.0, t: 0.0, max, col, h, h2: h, size: 1.0, style: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -961,6 +973,7 @@ impl Game {
|
||||
self.step(dt);
|
||||
}
|
||||
self.sync_treasury();
|
||||
self.update_particles(dt);
|
||||
for f in self.fx.iter_mut() {
|
||||
f.t += dt;
|
||||
}
|
||||
@@ -1875,6 +1888,15 @@ impl Game {
|
||||
let h = if flies(e.kind) { 0.8 } else { 0.45 };
|
||||
self.shot(e.x, e.y, h, tx, ty, 0.45, 0.25, col);
|
||||
self.grow_last_fx(lv);
|
||||
use crate::particles::*;
|
||||
self.style_last_fx(match e.kind {
|
||||
EK::Archer => ST_ARROW,
|
||||
EK::Mage => ST_SHADOW,
|
||||
EK::Priest => ST_HOLY,
|
||||
EK::Priestess | EK::Wyrm => ST_FIRE,
|
||||
EK::Owl => ST_ARCANE,
|
||||
_ => ST_PLAIN,
|
||||
});
|
||||
self.sfx(Sfx::Shoot, e.x, e.y);
|
||||
} else {
|
||||
self.fx(2, tx, ty, 0.0, 0.0, 0.25, if e.level >= 9 { 0xff60e0ff } else { 0xffffffff });
|
||||
@@ -1904,8 +1926,6 @@ impl Game {
|
||||
}
|
||||
if e.kind == EK::Priestess || e.kind == EK::Wyrm {
|
||||
// fireball / fire breath: half damage to enemies around the target
|
||||
self.fx(3, tx, ty, 0.0, 0.0, 0.5, 0xff3080ff);
|
||||
self.grow_last_fx(lv);
|
||||
let me = e.side();
|
||||
let splash: Vec<usize> = self
|
||||
.ents
|
||||
@@ -4050,7 +4070,12 @@ impl Game {
|
||||
TowerKind::Rune => 0xff40c0ff,
|
||||
};
|
||||
self.shot(mx, my, mh, ex, ey, eh, 0.3, col);
|
||||
self.fx_at(2, ex, ey, eh, 0.3, 0xffffffff);
|
||||
self.style_last_fx(match t.kind {
|
||||
TowerKind::Arrow => crate::particles::ST_ARROW,
|
||||
TowerKind::Cannon => crate::particles::ST_CANNON,
|
||||
TowerKind::Magic => crate::particles::ST_MAGIC,
|
||||
TowerKind::Rune => crate::particles::ST_RUNE,
|
||||
});
|
||||
self.sfx(match t.kind {
|
||||
TowerKind::Arrow => Sfx::Shoot,
|
||||
TowerKind::Cannon => Sfx::Cannon,
|
||||
@@ -4123,8 +4148,9 @@ impl Game {
|
||||
TowerKind::Arrow => {
|
||||
// the arrow pierces up to two more heroes behind the target
|
||||
let dir = norm(ex - px, ey - py);
|
||||
let end = (px + dir.0 * t.range(), py + dir.1 * t.range());
|
||||
self.shot(mx, my, mh, end.0, end.1, 0.45, 0.3, 0xffd0e0f0);
|
||||
let (endx, endy, wall) = self.arrow_end(px, py, dir.0, dir.1, t.range());
|
||||
self.shot(mx, my, mh, endx, endy, 0.45, 0.3, 0xffd0e0f0);
|
||||
self.style_last_fx(if wall { crate::particles::ST_ARROW_WALL } else { crate::particles::ST_ARROW });
|
||||
self.sfx(Sfx::Shoot, px, py);
|
||||
let mut hits = vec![(j, dmg)];
|
||||
for (o, e) in self.ents.iter().enumerate() {
|
||||
@@ -4147,8 +4173,7 @@ impl Game {
|
||||
TowerKind::Cannon => {
|
||||
// splash damage and knockback around the impact
|
||||
self.shot(mx, my, mh, ex, ey, 0.4, 0.35, 0xff303030);
|
||||
self.fx(3, ex, ey, 0.0, 0.0, 0.6, 0xff4070a0);
|
||||
self.fx(2, ex, ey, 0.0, 0.0, 0.3, 0xff60c0ff);
|
||||
self.style_last_fx(crate::particles::ST_CANNON);
|
||||
self.sfx(Sfx::Cannon, px, py);
|
||||
let mut hits = Vec::new();
|
||||
for (o, e) in self.ents.iter().enumerate() {
|
||||
@@ -4176,6 +4201,7 @@ impl Game {
|
||||
TowerKind::Magic => {
|
||||
// sure hit, slows, jumps to a second hero
|
||||
self.shot(mx, my, mh, ex, ey, 0.45, 0.2, 0xffff60d0);
|
||||
self.style_last_fx(crate::particles::ST_MAGIC);
|
||||
self.sfx(Sfx::Magic, px, py);
|
||||
self.ents[j].slow = 2.0;
|
||||
let chain = self
|
||||
@@ -4202,6 +4228,7 @@ impl Game {
|
||||
TowerKind::Rune => {
|
||||
// a rune bolt that jumps on to up to three more enemies nearby
|
||||
self.shot(mx, my, mh, ex, ey, 0.45, 0.25, 0xff40c0ff);
|
||||
self.style_last_fx(crate::particles::ST_RUNE);
|
||||
self.fx(2, ex, ey, 0.0, 0.0, 0.35, 0xffffc040);
|
||||
self.sfx(Sfx::Magic, px, py);
|
||||
let mut hit = vec![j];
|
||||
@@ -4255,6 +4282,7 @@ impl Game {
|
||||
let dmg = e.atk() * (0.85 + self.rng.f() * 0.3);
|
||||
if s.range > 1.5 {
|
||||
self.shot(e.x, e.y, 0.45, tx, ty, hit_h, 0.25, 0xff70b0d0);
|
||||
self.style_last_fx(if e.kind == EK::Archer { crate::particles::ST_ARROW } else { crate::particles::ST_PLAIN });
|
||||
} else {
|
||||
self.fx_at(2, tx, ty, hit_h, 0.25, 0xffffffff);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user