use super::*;
fn draw_fireplace(buf: &mut PixBuf, pw: usize, ph: usize) {
let side_w = (pw * 19 / 100).max(4);
let mantel_h = (ph * 11 / 100).max(2);
let hearth_y = ph.saturating_sub((ph * 9 / 100).max(2));
let fire_x0 = side_w;
let fire_x1 = pw.saturating_sub(side_w);
let mortar = Color::Rgb(52, 47, 42);
let brick_h = (ph / 18).max(2);
let brick_w = (side_w / 3).max(3);
for side in 0..2usize {
let xs = if side == 0 { 0 } else { pw.saturating_sub(side_w) };
for py in mantel_h..hearth_y {
for ppx in xs..(xs + side_w).min(pw) {
let row = (py - mantel_h) / brick_h;
let off = if row % 2 == 0 { 0 } else { brick_w / 2 };
let col_b = (ppx - xs + off) % brick_w;
let row_b = (py - mantel_h) % brick_h;
let v = hash(((row * 13 + (ppx - xs + off) / brick_w) as u64).wrapping_add(77));
let c = if row_b == 0 || col_b == 0 {
mortar
} else if row_b == 1 {
Color::Rgb(
90u8.saturating_add((v % 15) as u8),
45u8.saturating_add(((v >> 4) % 10) as u8),
30u8.saturating_add(((v >> 8) % 8) as u8),
)
} else {
Color::Rgb(
135u8.saturating_add((v % 25) as u8),
72u8.saturating_add(((v >> 4) % 18) as u8),
52u8.saturating_add(((v >> 8) % 14) as u8),
)
};
if py < buf.len() && ppx < buf[0].len() { buf[py][ppx] = Some(c); }
}
}
}
for py in 0..mantel_h {
for ppx in 0..pw {
if py >= buf.len() || ppx >= buf[0].len() { continue; }
let grain = hash((ppx as u64 / 2).wrapping_add((py as u64 * 3).wrapping_mul(17)));
let grain_line = (py * pw + ppx / 3) % 7 == 0;
let (r, g, b) = if py == mantel_h - 1 {
(52u8, 31u8, 12u8) } else if grain_line {
(72u8.saturating_add((grain % 10) as u8), 43u8.saturating_add((grain % 8) as u8), 16u8.saturating_add((grain % 5) as u8))
} else {
(88u8.saturating_add((grain % 18) as u8), 54u8.saturating_add((grain % 13) as u8), 22u8.saturating_add((grain % 9) as u8))
};
buf[py][ppx] = Some(Color::Rgb(r, g, b));
}
}
let carpet_x0 = fire_x0.saturating_sub(fire_x0 / 3);
let carpet_x1 = (fire_x1 + (pw - fire_x1) / 3).min(pw);
for py in hearth_y..ph {
for ppx in 0..pw {
if py >= buf.len() || ppx >= buf[0].len() { continue; }
let on_carpet = ppx >= carpet_x0 && ppx < carpet_x1;
if on_carpet {
let cx = ppx - carpet_x0;
let cy = py - hearth_y;
let cw = carpet_x1 - carpet_x0;
let ch = ph - hearth_y;
let border = cx == 0 || cx == cw.saturating_sub(1) || cy == 0 || cy == ch.saturating_sub(1);
let inner_border = cx == 2 || cx == cw.saturating_sub(3) || cy == 2 || cy == ch.saturating_sub(3);
let v = hash(ppx as u64 * 7 + py as u64 * 13 + 8888);
let noise = (v % 12) as u8;
buf[py][ppx] = Some(if border || inner_border {
Color::Rgb(130u8.saturating_add(noise / 2), 40u8.saturating_add(noise / 4), 18u8.saturating_add(noise / 4))
} else {
Color::Rgb(105u8.saturating_add(noise), 28u8.saturating_add(noise / 3), 18u8.saturating_add(noise / 3))
});
} else {
let v = hash(((ppx / 5) as u64).wrapping_add((py * 53) as u64));
buf[py][ppx] = Some(Color::Rgb(
78u8.saturating_add((v % 18) as u8),
76u8.saturating_add(((v >> 4) % 14) as u8),
72u8.saturating_add(((v >> 8) % 11) as u8),
));
}
}
}
let log_h = (ph * 5 / 100).max(2);
let log_y0 = hearth_y.saturating_sub(log_h + 1);
let fire_w = fire_x1 - fire_x0;
let log_hw = fire_w / 2;
let logs: &[(isize, u8, u8, u8)] = &[
(fire_x0 as isize + fire_w as isize / 3, 58, 32, 12),
(fire_x0 as isize + fire_w as isize * 2 / 3, 48, 26, 10),
];
for &(cx, lr, lg, lb) in logs {
for dy in 0..log_h {
let py = log_y0 + dy;
if py >= ph { continue; }
let ry = dy as f64 / log_h as f64;
let half_w = (log_hw as f64 * (1.0 - (ry * 2.0 - 1.0).powi(2)).sqrt()) as isize;
for dx in -half_w..=half_w {
let px = cx + dx;
if px < fire_x0 as isize || px as usize >= fire_x1 { continue; }
let v = hash(px as u64 * 5 + py as u64 * 11 + 3030);
let bark = (v % 3 == 0) as u8 * 12;
let ember = if dy == 0 { 30u8 } else { 0u8 };
buf[py][px as usize] = Some(Color::Rgb(
lr.saturating_add(bark).saturating_add(ember),
lg.saturating_add(bark / 2),
lb.saturating_add(bark / 3),
));
}
}
}
let grate_h = (ph * 10 / 100).max(3);
let grate_y0 = hearth_y.saturating_sub(grate_h);
let bar_w = 1usize;
let gap_w = (fire_x1 - fire_x0) / 10;
let gap_w = gap_w.max(2);
let iron_hi = Color::Rgb(48, 45, 42);
let iron_dk = Color::Rgb(22, 20, 18);
for ppx in fire_x0..fire_x1 {
if grate_y0 < buf.len() { buf[grate_y0][ppx] = Some(iron_hi); }
if hearth_y < buf.len() && hearth_y < ph { buf[hearth_y.min(ph-1)][ppx] = Some(iron_dk); }
}
let mut bx = fire_x0 + gap_w / 2;
while bx + bar_w <= fire_x1 {
for py in grate_y0..hearth_y {
if py >= buf.len() { continue; }
for dx in 0..bar_w {
let px = bx + dx;
if px < buf[0].len() {
buf[py][px] = Some(if dx == 0 { iron_hi } else { iron_dk });
}
}
}
bx += gap_w;
}
}
pub(super) fn fill_fire(buf: &mut PixBuf, pw: usize, ph: usize, tick: u64) {
let side_w = (pw * 19 / 100).max(4);
let mantel_h = (ph * 11 / 100).max(2);
let hearth_y = ph.saturating_sub((ph * 9 / 100).max(2));
let fire_x0 = side_w;
let fire_x1 = pw.saturating_sub(side_w);
let fire_pw = fire_x1.saturating_sub(fire_x0);
let fire_ph = hearth_y.saturating_sub(mantel_h);
for py in 0..ph { for ppx in 0..pw { buf[py][ppx] = Some(Color::Rgb(5, 4, 3)); } }
for py in mantel_h..hearth_y {
for ppx in fire_x0..fire_x1 {
let v = hash(ppx as u64 * 3 + py as u64 * 7 + 999);
buf[py][ppx] = Some(Color::Rgb(
11u8.saturating_add((v % 4) as u8),
9u8.saturating_add(((v >> 4) % 3) as u8),
8u8.saturating_add(((v >> 8) % 3) as u8),
));
}
}
let crackle_t = tick / 4;
let blend_f = (tick % 4) as f64 / 4.0;
for ppx in fire_x0..fire_x1 {
let local_x = ppx - fire_x0;
let cx = (local_x as f64 - fire_pw as f64 / 2.0).abs() / (fire_pw as f64 / 2.0);
let arch = (1.0 - cx * 0.65).max(0.0);
let ha = hash(ppx as u64 * 11 + crackle_t * 37 + 101);
let hb = hash(ppx as u64 * 11 + (crackle_t + 1) * 37 + 101);
let ca = (ha % 100) as f64 / 100.0 * 0.30 - 0.09;
let cb = (hb % 100) as f64 / 100.0 * 0.30 - 0.09;
let hn = hash((ppx as u64 + 1) * 11 + crackle_t * 37 + 101);
let cn = (hn % 100) as f64 / 100.0 * 0.30 - 0.09;
let crackle = (ca * (1.0 - blend_f) + cb * blend_f) * 0.75 + cn * 0.25;
let height = ((arch + crackle).clamp(0.1, 1.0) * 0.58 * fire_ph as f64) as usize;
let top = hearth_y.saturating_sub(height);
for py in top..hearth_y {
let f = (py - top) as f64 / height.max(1) as f64;
buf[py][ppx] = Some(
if f < 0.20 { let v = f/0.20; Color::Rgb(255, (210.0*v+45.0) as u8, (90.0*(1.0-v)) as u8) }
else if f < 0.60 { let v = (f-0.20)/0.40; Color::Rgb(255, (45.0*(1.0-v)) as u8, 0) }
else { Color::Rgb(((1.0-(f-0.60)/0.40)*240.0).max(0.0) as u8, 0, 0) }
);
}
}
let n_sparks = (fire_pw / 2).max(3);
for i in 0..n_sparks {
let hs = hash(i as u64 * 17 + tick / 5 * 31 + 555);
if hs % 4 != 0 { continue; }
let sx = fire_x0 + (hs >> 10) as usize % fire_pw;
let sy = mantel_h + (hs >> 20) as usize % (fire_ph * 2 / 5);
set_px(buf, sx as isize, sy as isize, match (hs >> 5) % 3 {
0 => Color::Rgb(255, 210, 60),
1 => Color::Rgb(255, 150, 25),
_ => Color::Rgb(255, 255, 190),
});
}
let shadow_w = (fire_pw / 6).max(3);
for py in mantel_h..hearth_y {
for dx in 0..shadow_w {
let alpha = (1.0 - dx as f64 / shadow_w as f64) * 0.78;
for &px in &[fire_x0 + dx, fire_x1.saturating_sub(1 + dx)] {
if px < buf[0].len() {
if let Some(Color::Rgb(r, g, b)) = buf[py][px] {
buf[py][px] = Some(Color::Rgb(
(r as f64 * (1.0 - alpha)) as u8,
(g as f64 * (1.0 - alpha)) as u8,
(b as f64 * (1.0 - alpha)) as u8,
));
}
}
}
}
}
draw_fireplace(buf, pw, ph);
}