diff --git a/src/gfx.rs b/src/gfx.rs index 55f1be3..27b7887 100644 --- a/src/gfx.rs +++ b/src/gfx.rs @@ -1935,13 +1935,39 @@ fn gear_anchor(g: &Cell) -> GearAnchor { // the gear's parts in their texture (grey, tinted by the metal or cloth colour at draw time): // (x, y, w, h) in texels of GEAR_TEX -pub const GEAR_TEX: u32 = 64; +pub const GEAR_TEX: u32 = 96; pub const GEAR_HELMET: (u32, u32, u32, u32) = (0, 0, 24, 12); pub const GEAR_PAULDRON: (u32, u32, u32, u32) = (24, 0, 12, 9); pub const GEAR_CROWN: (u32, u32, u32, u32) = (36, 0, 20, 10); pub const GEAR_CAPE: (u32, u32, u32, u32) = (0, 14, 28, 40); pub const GEAR_HORN: (u32, u32, u32, u32) = (28, 14, 7, 10); pub const GEAR_PLUME: (u32, u32, u32, u32) = (36, 14, 7, 12); +// the holidays' headwear (in its own colours, not tinted): a Christmas hat, a carved pumpkin, bunny ears +pub const GEAR_SANTA: (u32, u32, u32, u32) = (44, 14, 20, 20); +pub const GEAR_PUMPKIN: (u32, u32, u32, u32) = (66, 14, 22, 20); +pub const GEAR_EARS: (u32, u32, u32, u32) = (44, 36, 18, 18); + +// where the gear of a 10x10 sprite goes (its cell, standing; per kind and view), for the +// holidays' headwear in the pixel-art style +static SMALL_ANCHORS: std::sync::OnceLock> = std::sync::OnceLock::new(); + +pub fn small_anchor(k: EK, view: u32) -> GearAnchor { + let i = sprite_index(k) as usize * VIEWS as usize + view.min(VIEWS - 1) as usize; + SMALL_ANCHORS + .get_or_init(|| { + let mut v = Vec::new(); + for &k in SPRITE_KINDS.iter() { + for view in 0..VIEWS { + let g: Cell = sprite_with_gear(k, 0, view).iter().map(|r| r.to_vec()).collect(); + v.push(gear_anchor(&g)); + } + } + v + }) + .get(i) + .copied() + .unwrap_or_default() +} // kinds that carry treasure in a sack on the back (instead of the coin sign over the head); // their 10x10 sprite with the sack gets an own atlas column after SPRITE_KINDS @@ -2842,6 +2868,60 @@ fn gen_gear() -> Image { shape(GEAR_HORN, &|x, y| (x - (1.0 - y) * 0.4 - 0.3).abs() < 0.16 + 0.3 * y, &|x, y| 0.95 - y * 0.25 - x * 0.1); // plume: a feather bush shape(GEAR_PLUME, &|x, y| (x - 0.5).powi(2) * 3.2 + (y - 0.5).powi(2) * 1.1 <= 0.26 + 0.04 * (y * 14.0).sin(), &|_x, y| 0.95 - y * 0.3); + // the holidays' headwear, coloured: a shape, outlined, its colour per texel + let mut cshape = |(ox, oy, w, h): (u32, u32, u32, u32), inside: &dyn Fn(f32, f32) -> bool, paint: &dyn Fn(f32, f32) -> Color| { + for y in 0..h { + for x in 0..w { + let (fx, fy) = ((x as f32 + 0.5) / w as f32, (y as f32 + 0.5) / h as f32); + if !inside(fx, fy) { + continue; + } + let edge = [(-1i32, 0i32), (1, 0), (0, -1), (0, 1)].iter().any(|&(dx, dy)| { + let (nx, ny) = (x as i32 + dx, y as i32 + dy); + nx < 0 || ny < 0 || nx >= w as i32 || ny >= h as i32 || !inside((nx as f32 + 0.5) / w as f32, (ny as f32 + 0.5) / h as f32) + }); + img.set_pixel(ox + x, oy + y, if edge { outline } else { paint(fx, fy) }); + } + } + }; + // Christmas: a red hat bending to the side, white brim and bobble + let santa_in = |x: f32, y: f32| { + let bobble = (x - 0.85).powi(2) + (y - 0.12).powi(2) < 0.018; + let brim = y > 0.72 && (x - 0.45).abs() < 0.45; + let cone = y > 0.08 && y <= 0.75 && (x - (0.45 + (0.75 - y) * 0.5)).abs() < (y - 0.05) * 0.5; + bobble || brim || cone + }; + cshape(GEAR_SANTA, &santa_in, &|x, y| { + if y > 0.72 || (x - 0.85).powi(2) + (y - 0.12).powi(2) < 0.018 { + Color::new(0.95, 0.95, 0.97, 1.0) + } else { + Color::new(0.78 - x * 0.15, 0.1, 0.12, 1.0) + } + }); + // Halloween: a carved pumpkin over the head, a glowing face, a stalk + cshape(GEAR_PUMPKIN, &|x, y| (x - 0.5).powi(2) * 3.6 + (y - 0.56).powi(2) * 4.4 <= 0.85 || ((x - 0.5).abs() < 0.07 && y < 0.25), &|x, y| { + let eye = (y - 0.48).abs() < 0.08 && ((x - 0.33).abs() < 0.08 || (x - 0.67).abs() < 0.08); + let mouth = (y - 0.72).abs() < 0.05 && (x - 0.5).abs() < 0.25 && ((x * 12.0) as i32 % 2 == 0 || (y - 0.72).abs() < 0.025); + if y < 0.2 { + Color::new(0.3, 0.5, 0.15, 1.0) + } else if eye || mouth { + Color::new(1.0, 0.85, 0.3, 1.0) + } else if ((x - 0.5) * 6.0).fract().abs() < 0.12 { + Color::new(0.75, 0.35, 0.05, 1.0) + } else { + Color::new(0.95, 0.5, 0.1, 1.0) + } + }); + // Easter: two upright bunny ears, pink inside + let ear = |x: f32, y: f32, cx: f32, k: f32| (x - cx - (0.5 - y) * 0.12 * (cx - 0.5).signum()).powi(2) * k + (y - 0.5).powi(2) * 4.4 <= 1.0; + cshape(GEAR_EARS, &|x, y| ear(x, y, 0.3, 70.0) || ear(x, y, 0.7, 70.0), &|x, y| { + let inner = ear(x, y, 0.3, 260.0) || ear(x, y, 0.7, 260.0); + if inner { + Color::new(1.0, 0.65, 0.75, 1.0) + } else { + Color::new(0.97, 0.96, 0.94, 1.0) + } + }); img } diff --git a/src/render3d.rs b/src/render3d.rs index a084d3c..71a3aaa 100644 --- a/src/render3d.rs +++ b/src/render3d.rs @@ -2048,6 +2048,36 @@ impl R3d { } } } + // the holidays' headwear on everybody (both styles): a Christmas hat, a carved + // pumpkin, bunny ears + let holiday = crate::holiday::current(); + if holiday != crate::holiday::NONE && !white && e.captive == 0 && !sleeping { + let an = match pic { + Some(p) => Gfx::hd_anchor(p), + None => crate::gfx::small_anchor(e.kind, v), + }; + let at = |fx: f32, fy: f32| { + let fx = if mirror { 1.0 - fx } else { fx }; + corner((fx - 0.5) * 1.2 * w, (1.0 - fy) * 1.2 * h - 0.1 * h) + toward * 0.01 + }; + let mid = (an.head.0 + an.head.1) * 0.5; + let hw = (an.head.1 - an.head.0).max(0.12); + let neck = an.top + (an.shoulder - an.top) * 0.75; + let (x0, y0, x1, y1, part) = match holiday { + crate::holiday::CHRISTMAS => (mid - hw * 0.62, an.top - hw * 0.75, mid + hw * 0.72, an.top + hw * 0.2, crate::gfx::GEAR_SANTA), + crate::holiday::HALLOWEEN => (mid - hw * 0.68, an.top - hw * 0.25, mid + hw * 0.68, neck, crate::gfx::GEAR_PUMPKIN), + _ => (mid - hw * 0.5, an.top - hw * 0.95, mid + hw * 0.5, an.top + hw * 0.05, crate::gfx::GEAR_EARS), + }; + let gt = crate::gfx::GEAR_TEX as f32; + let (mut a0, b0) = (part.0 as f32 / gt, part.1 as f32 / gt); + let (mut a1, b1) = ((part.0 + part.2) as f32 / gt, (part.1 + part.3) as f32 / gt); + if mirror { + std::mem::swap(&mut a0, &mut a1); + } + let k = (a * 1.3).min(1.0); + let hc = [(255.0 * k) as u8, (255.0 * k) as u8, (255.0 * k) as u8, 255]; + mb_gear.quad([at(x0, y0), at(x1, y0), at(x1, y1), at(x0, y1)], [vec2(a0, b0), vec2(a1, b0), vec2(a1, b1), vec2(a0, b1)], [hc; 4], [toward; 4]); + } let mb = match pic { Some(_) if fine => &mut mb_hd, Some(_) => &mut mb_hd32,