Splash arrows in lava
This commit is contained in:
1 parent
3b710516d8
commit
ee88fc04a5
3 files changed
+116
-7
No files matched your search
+13
@@ -73,6 +73,19 @@ impl Game {
|
|||||||
g.map.t[idx(x, y)].owner = Owner::Player;
|
g.map.t[idx(x, y)].owner = Owner::Player;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// lava pools in front of the human arrow towers, with gaps so the path to the bridge stays open
|
||||||
|
for (x0, y0, x1, y1) in [(RIVER_X + 5, BRIDGE_Y - 4, RIVER_X + 6, BRIDGE_Y - 3), (RIVER_X + 9, BRIDGE_Y - 1, RIVER_X + 10, BRIDGE_Y)] {
|
||||||
|
for y in y0..=y1 {
|
||||||
|
for x in x0..=x1 {
|
||||||
|
let t = &mut g.map.t[idx(x, y)];
|
||||||
|
if t.kind == Kind::Floor && t.owner == Owner::Hero {
|
||||||
|
t.kind = Kind::Lava;
|
||||||
|
t.owner = Owner::None;
|
||||||
|
t.gold = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// a few rooms on the dungeon side for the look of it
|
// a few rooms on the dungeon side for the look of it
|
||||||
for (x0, y0, x1, y1, k) in [(8, 13, 13, 17, Kind::Lair), (8, 30, 13, 34, Kind::Training), (17, 12, 21, 15, Kind::Farm)] {
|
for (x0, y0, x1, y1, k) in [(8, 13, 13, 17, Kind::Lair), (8, 30, 13, 34, Kind::Training), (17, 12, 21, 15, Kind::Farm)] {
|
||||||
for y in y0..=y1 {
|
for y in y0..=y1 {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ pub const STUCK_MAX: usize = 400; // arrows and scorch marks
|
|||||||
pub const STUCK_LIFE: f32 = 300.0; // seconds an arrow stays
|
pub const STUCK_LIFE: f32 = 300.0; // seconds an arrow stays
|
||||||
const PARTS_MAX: usize = 900;
|
const PARTS_MAX: usize = 900;
|
||||||
const GRAVITY: f32 = 6.0;
|
const GRAVITY: f32 = 6.0;
|
||||||
|
const LIQ_H: f32 = -0.3; // river surface, matching the depth used in render3d.rs
|
||||||
|
|
||||||
// particle kinds
|
// particle kinds
|
||||||
pub const EMBER: u8 = 0; // glowing, falls
|
pub const EMBER: u8 = 0; // glowing, falls
|
||||||
@@ -22,6 +23,7 @@ pub const ARROW: u8 = 6; // stuck arrow (vx, vy, vh = its direction)
|
|||||||
pub const SCORCH: u8 = 7; // dark mark on the floor
|
pub const SCORCH: u8 = 7; // dark mark on the floor
|
||||||
pub const FLASH: u8 = 8; // expanding fireball
|
pub const FLASH: u8 = 8; // expanding fireball
|
||||||
pub const RING: u8 = 9; // shock wave
|
pub const RING: u8 = 9; // shock wave
|
||||||
|
pub const SPLASH: u8 = 10; // liquid droplets, bounce on the river surface
|
||||||
|
|
||||||
// projectile styles (Fx::style)
|
// projectile styles (Fx::style)
|
||||||
pub const ST_PLAIN: u8 = 0;
|
pub const ST_PLAIN: u8 = 0;
|
||||||
@@ -186,6 +188,7 @@ impl Game {
|
|||||||
for f in impacts {
|
for f in impacts {
|
||||||
self.impact(&f);
|
self.impact(&f);
|
||||||
}
|
}
|
||||||
|
self.spawn_lava_particles(dt);
|
||||||
// particles
|
// particles
|
||||||
for p in self.parts.iter_mut() {
|
for p in self.parts.iter_mut() {
|
||||||
p.t += dt;
|
p.t += dt;
|
||||||
@@ -204,6 +207,24 @@ impl Game {
|
|||||||
p.h += p.vh * dt;
|
p.h += p.vh * dt;
|
||||||
}
|
}
|
||||||
FLASH | RING => {}
|
FLASH | RING => {}
|
||||||
|
SPLASH => {
|
||||||
|
p.vh -= GRAVITY * 0.75 * dt;
|
||||||
|
p.x += p.vx * dt;
|
||||||
|
p.y += p.vy * dt;
|
||||||
|
p.h += p.vh * dt;
|
||||||
|
if p.h <= LIQ_H {
|
||||||
|
p.h = LIQ_H;
|
||||||
|
if p.vh < -0.6 {
|
||||||
|
p.vh = -p.vh * 0.30;
|
||||||
|
p.vx *= 0.55;
|
||||||
|
p.vy *= 0.55;
|
||||||
|
} else {
|
||||||
|
p.vh = 0.0;
|
||||||
|
p.vx *= 0.65;
|
||||||
|
p.vy *= 0.65;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// falling: embers drift down slower than sparks and debris
|
// falling: embers drift down slower than sparks and debris
|
||||||
let g = if p.kind == EMBER { GRAVITY * 0.35 } else { GRAVITY };
|
let g = if p.kind == EMBER { GRAVITY * 0.35 } else { GRAVITY };
|
||||||
@@ -267,6 +288,10 @@ impl Game {
|
|||||||
self.stick(Part { kind: SCORCH, x, y, h: 0.0, vx: 0.0, vy: 0.0, vh: 0.0, t: 0.0, max: 20.0, col: 0, size: 0.6 });
|
self.stick(Part { kind: SCORCH, x, y, h: 0.0, vx: 0.0, vy: 0.0, vh: 0.0, t: 0.0, max: 20.0, col: 0, size: 0.6 });
|
||||||
}
|
}
|
||||||
ST_ARROW | ST_ARROW_WALL => {
|
ST_ARROW | ST_ARROW_WALL => {
|
||||||
|
if let Some(kind) = self.liquid_kind_at(x, y) {
|
||||||
|
self.liquid_impact(kind, x, y, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
let (dx, dy) = (f.x2 - f.x, f.y2 - f.y);
|
let (dx, dy) = (f.x2 - f.x, f.y2 - f.y);
|
||||||
let l = (dx * dx + dy * dy).sqrt().max(0.01);
|
let l = (dx * dx + dy * dy).sqrt().max(0.01);
|
||||||
let (dx, dy) = (dx / l, dy / l);
|
let (dx, dy) = (dx / l, dy / l);
|
||||||
@@ -294,6 +319,71 @@ impl Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn liquid_kind_at(&self, x: f32, y: f32) -> Option<Kind> {
|
||||||
|
self.map.get(x as i32, y as i32).filter(|t| t.liquid()).map(|t| t.kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn liquid_impact(&mut self, kind: Kind, x: f32, y: f32, size: f32) {
|
||||||
|
let at = (x, y, LIQ_H + 0.04);
|
||||||
|
match kind {
|
||||||
|
Kind::Lava => {
|
||||||
|
self.burst(SPLASH, at, 8.max((10.0 * size) as usize), 1.3, 1.9, 0.38, abgr(255, 110, 25), 0.045);
|
||||||
|
self.burst(MOTE, at, 4, 0.7, 1.2, 0.7, abgr(255, 150, 45), 0.035);
|
||||||
|
self.part(FLASH, at, (0.0, 0.0, 0.0), 0.12, abgr(255, 155, 45), 0.18 * size.max(0.7));
|
||||||
|
// short burning arrow smoke: small, quickly fading
|
||||||
|
self.part(SMOKE, (x, y, LIQ_H + 0.10), (0.0, 0.0, 0.70), 0.65, abgr(115, 55, 25), 0.09);
|
||||||
|
self.burst(SMOKE, (x, y, LIQ_H + 0.18), 1, 0.1, 0.35, 0.45, abgr(85, 42, 20), 0.06);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.burst(SPLASH, at, 10.max((12.0 * size) as usize), 1.2, 1.8, 0.42, abgr(140, 220, 255), 0.04);
|
||||||
|
self.part(RING, (x, y, LIQ_H), (0.0, 0.0, 0.0), 0.25, abgr(150, 225, 255), 0.35 * size.max(0.7));
|
||||||
|
self.part(SMOKE, (x, y, LIQ_H + 0.08), (0.0, 0.0, 0.35), 0.45, abgr(180, 210, 235), 0.07);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_lava_particles(&mut self, dt: f32) {
|
||||||
|
if self.parts.len() >= PARTS_MAX {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let rate = 7.0;
|
||||||
|
let mut count = (rate * dt).floor() as usize;
|
||||||
|
if self.vrng.f() < (rate * dt).fract() {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let max_x = self.map.w as i32 - 1;
|
||||||
|
let max_y = self.map.h as i32 - 1;
|
||||||
|
if max_x <= 1 || max_y <= 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for _ in 0..count {
|
||||||
|
if self.parts.len() >= PARTS_MAX {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let mut spot = None;
|
||||||
|
for _ in 0..16 {
|
||||||
|
let x = self.vrng.range(1, max_x - 1);
|
||||||
|
let y = self.vrng.range(1, max_y - 1);
|
||||||
|
let i = idx(x, y);
|
||||||
|
if self.map.shown(i) && self.map.t[i].kind == Kind::Lava {
|
||||||
|
spot = Some((x as f32 + 0.5, y as f32 + 0.5));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some((cx, cy)) = spot {
|
||||||
|
let at = (cx, cy, LIQ_H + 0.03);
|
||||||
|
self.burst(MOTE, at, 1, 0.15, 0.40, 1.40, abgr(255, 115, 30), 0.035);
|
||||||
|
let spark = self.vrng.f() < 0.25;
|
||||||
|
if spark {
|
||||||
|
self.burst(SPLASH, at, 1, 0.70, 1.10, 0.38, abgr(255, 170, 45), 0.028);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// the last projectile gets a style
|
// the last projectile gets a style
|
||||||
pub(crate) fn style_last_fx(&mut self, style: u8) {
|
pub(crate) fn style_last_fx(&mut self, style: u8) {
|
||||||
if let Some(f) = self.fx.last_mut() {
|
if let Some(f) = self.fx.last_mut() {
|
||||||
|
|||||||
+13
-7
@@ -1124,6 +1124,7 @@ impl R3d {
|
|||||||
draw_marks(g, t);
|
draw_marks(g, t);
|
||||||
draw_overlay(&g.map, ov, self.wall_sub);
|
draw_overlay(&g.map, ov, self.wall_sub);
|
||||||
draw_entry_warnings(g, t, self.wall_sub);
|
draw_entry_warnings(g, t, self.wall_sub);
|
||||||
|
draw_scorch_glow(g);
|
||||||
// translucent glows and effects last (they write depth, see glow_material)
|
// translucent glows and effects last (they write depth, see glow_material)
|
||||||
draw_landmarks_glow(g, t, cam.basis().1);
|
draw_landmarks_glow(g, t, cam.basis().1);
|
||||||
draw_towers_glow(g, t);
|
draw_towers_glow(g, t);
|
||||||
@@ -2455,27 +2456,32 @@ 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_parts_glow(g: &Game) {
|
fn draw_scorch_glow(g: &Game) {
|
||||||
use crate::particles::*;
|
use crate::particles::*;
|
||||||
// scorch marks on the floor: drawn before smoke/flash so they are not depth-occluded
|
|
||||||
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 fade = 1.0 - (p.t / p.max).clamp(0.0, 1.0);
|
||||||
disc(vec3(p.x, floor_y(g, p.x, p.y) + 0.02, p.y), p.size, Color::new(0.025, 0.018, 0.018, 0.70 * fade), 14);
|
let at = vec3(p.x, floor_y(g, p.x, p.y) + 0.018, p.y);
|
||||||
|
disc(at, p.size, Color::new(0.02, 0.017, 0.017, 0.78 * fade), 14);
|
||||||
|
ring(at + Vec3::Y * 0.002, p.size * 1.06, Color::new(0.55, 0.22, 0.06, 0.22 * fade));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_parts_glow(g: &Game) {
|
||||||
|
use crate::particles::*;
|
||||||
for p in &g.parts {
|
for p in &g.parts {
|
||||||
let c = rgba(p.col);
|
let c = rgba(p.col);
|
||||||
let life = (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, p.h, p.y);
|
let at = vec3(p.x, p.h, p.y);
|
||||||
match p.kind {
|
match p.kind {
|
||||||
EMBER | SPARK | MOTE => {
|
EMBER | SPARK | MOTE | SPLASH => {
|
||||||
let s = p.size * (1.0 - life * 0.6);
|
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));
|
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.18 * (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.95, 0.75, 1.0 - life));
|
draw_sphere(at, r * 0.6, None, Color::new(1.0, 0.9, 0.7, 0.7 * (1.0 - life)));
|
||||||
draw_sphere(at, r, None, Color::new(c.r, c.g, c.b, 0.6 * (1.0 - life)));
|
draw_sphere(at, r, None, Color::new(c.r, c.g, c.b, 0.35 * (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