Dark demonic dungeon towers, a beating crystal heart and demon gate portals
- own towers in black obsidian and dark iron with red glowing joints, bone spikes, skulls, chains; arrow spire with a ballista, hellmouth cannon with a demon-head barrel, dark spire with horns and a crystal; every level adds parts - soul core: a crystal heart beating lub-dub (faster when hurt, cracked at low health) held by chains on an obsidian throne, veins with a pulse across the floor, rising embers - portals: an arch of stone and bone with a skull and horns, a vertical vortex with runes and falling sparks; grey and dead until claimed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
90a05a86d8
commit
33b9879b4c
1 file changed
+367
-71
+367
-71
@@ -1055,7 +1055,7 @@ impl R3d {
|
|||||||
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);
|
||||||
// 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);
|
draw_landmarks_glow(g, t, cam.basis().1);
|
||||||
draw_towers_glow(g, t);
|
draw_towers_glow(g, t);
|
||||||
draw_gear_glow(g, cam, t);
|
draw_gear_glow(g, cam, t);
|
||||||
crate::props::draw_props_glow(g, cam, t);
|
crate::props::draw_props_glow(g, cam, t);
|
||||||
@@ -1357,8 +1357,154 @@ fn draw_gear_glow(g: &Game, cam: &Cam, t: f32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the dungeon's own towers: black obsidian and dark iron, bone spikes, skulls and chains (the
|
||||||
|
// red glowing joints are drawn in the glow pass); every level adds visible parts
|
||||||
|
fn draw_dark_tower(tw: &Tower) {
|
||||||
|
let (x, z) = tw.pos();
|
||||||
|
let c = vec3(x, 0.0, z);
|
||||||
|
let h = tower_height(tw);
|
||||||
|
let f = 0.8 + tw.flash * 1.5;
|
||||||
|
let l = tw.level;
|
||||||
|
let obs = |k: u8| shade_c(30 + k, 24 + k, 34 + k, f);
|
||||||
|
let iron = shade_c(58, 56, 64, f);
|
||||||
|
let bone = shade_c(214, 204, 178, f);
|
||||||
|
let blood = shade_c(128, 18, 26, f);
|
||||||
|
let bx = |p: Vec3, s: Vec3, cl: Color| draw_cube(c + p, s, None, cl);
|
||||||
|
let skull = |p: Vec3, dir: Vec3| {
|
||||||
|
bx(p, vec3(0.15, 0.14, 0.15), bone);
|
||||||
|
let side = vec3(dir.z, 0.0, -dir.x);
|
||||||
|
for sgn in [-1.0f32, 1.0] {
|
||||||
|
bx(p + dir * 0.076 + side * (0.035 * sgn) + vec3(0.0, 0.015, 0.0), vec3(0.04, 0.04, 0.04), shade_c(20, 8, 10, 1.0));
|
||||||
|
}
|
||||||
|
bx(p + vec3(0.0, -0.08, 0.0) + dir * 0.02, vec3(0.1, 0.04, 0.1), bone);
|
||||||
|
};
|
||||||
|
let faces = [vec3(0.0, 0.0, 1.0), vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, -1.0), vec3(-1.0, 0.0, 0.0)];
|
||||||
|
match tw.kind {
|
||||||
|
TowerKind::Arrow => {
|
||||||
|
// spike spire: stepped obsidian foot, a tapering keep, a crown of bone spikes
|
||||||
|
bx(vec3(0.0, 0.07, 0.0), vec3(0.92, 0.14, 0.92), obs(4));
|
||||||
|
bx(vec3(0.0, 0.19, 0.0), vec3(0.78, 0.1, 0.78), obs(10));
|
||||||
|
let segs = 4;
|
||||||
|
for k in 0..segs {
|
||||||
|
let y0 = 0.24 + (h - 0.24) * k as f32 / segs as f32;
|
||||||
|
let hh = (h - 0.24) / segs as f32;
|
||||||
|
let w = 0.64 - k as f32 * 0.04;
|
||||||
|
bx(vec3(0.0, y0 + hh * 0.5, 0.0), vec3(w, hh - 0.03, w), obs((k * 4) as u8 + 8));
|
||||||
|
}
|
||||||
|
// buttress spikes on the corners
|
||||||
|
for (dx, dz) in [(0.36f32, 0.36f32), (-0.36, 0.36), (0.36, -0.36), (-0.36, -0.36)] {
|
||||||
|
bx(vec3(dx, 0.35, dz), vec3(0.12, 0.5, 0.12), obs(6));
|
||||||
|
line3(c + vec3(dx, 0.58, dz), c + vec3(dx * 1.45, 0.95, dz * 1.45), 0.06, bone);
|
||||||
|
}
|
||||||
|
bx(vec3(0.0, h + 0.03, 0.0), vec3(0.86, 0.07, 0.86), iron);
|
||||||
|
// crown of bone spikes (more with every level)
|
||||||
|
let n = 4 + 2 * l as usize;
|
||||||
|
for k in 0..n {
|
||||||
|
let a = k as f32 / n as f32 * std::f32::consts::TAU;
|
||||||
|
let p = c + vec3(a.cos() * 0.4, h + 0.06, a.sin() * 0.4);
|
||||||
|
line3(p, p + vec3(a.cos() * 0.08, 0.26 + 0.06 * (k % 2) as f32, a.sin() * 0.08), 0.05, bone);
|
||||||
|
}
|
||||||
|
// a small ballista turned towards the last target
|
||||||
|
let (ax, az) = (tw.aim.cos(), tw.aim.sin());
|
||||||
|
line3(c + vec3(-ax * 0.25, h + 0.16, -az * 0.25), c + vec3(ax * 0.32, h + 0.16, az * 0.32), 0.06, iron);
|
||||||
|
line3(c + vec3(-az * 0.22, h + 0.2, ax * 0.22), c + vec3(az * 0.22, h + 0.2, -ax * 0.22), 0.04, shade_c(90, 60, 40, f));
|
||||||
|
skull(vec3(0.0, h * 0.72, 0.33), faces[0]);
|
||||||
|
if l >= 2 {
|
||||||
|
// banner of the dungeon
|
||||||
|
bx(vec3(0.34, h + 0.5, -0.34), vec3(0.03, 0.9, 0.03), iron);
|
||||||
|
bx(vec3(0.46, h + 0.72, -0.34), vec3(0.22, 0.34, 0.02), blood);
|
||||||
|
skull(vec3(0.33, h * 0.72, 0.0), faces[1]);
|
||||||
|
}
|
||||||
|
if l >= 3 {
|
||||||
|
skull(vec3(0.0, h * 0.72, -0.33), faces[2]);
|
||||||
|
skull(vec3(-0.33, h * 0.72, 0.0), faces[3]);
|
||||||
|
for sgn in [-1.0f32, 1.0] {
|
||||||
|
line3(c + vec3(sgn * 0.3, h + 0.08, 0.0), c + vec3(sgn * 0.55, h + 0.55, 0.0), 0.08, bone);
|
||||||
|
line3(c + vec3(sgn * 0.55, h + 0.55, 0.0), c + vec3(sgn * 0.45, h + 0.75, 0.0), 0.05, bone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TowerKind::Cannon => {
|
||||||
|
// hellmouth: a riveted iron drum on an obsidian foot, a demon-head barrel on top
|
||||||
|
bx(vec3(0.0, 0.07, 0.0), vec3(0.96, 0.14, 0.96), obs(4));
|
||||||
|
for k in 0..3 {
|
||||||
|
let y = 0.14 + (h - 0.14) * (k as f32 + 0.5) / 3.0;
|
||||||
|
let r = 0.86 - k as f32 * 0.04;
|
||||||
|
bx(vec3(0.0, y, 0.0), vec3(r, (h - 0.14) / 3.0 - 0.02, r * 0.8), obs(14));
|
||||||
|
bx(vec3(0.0, y, 0.0), vec3(r * 0.8, (h - 0.14) / 3.0 - 0.02, r), obs(10));
|
||||||
|
// rivets
|
||||||
|
for q in 0..8 {
|
||||||
|
let a = q as f32 * 0.785;
|
||||||
|
bx(vec3(a.cos() * r * 0.46, y, a.sin() * r * 0.46), Vec3::splat(0.045), iron);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bx(vec3(0.0, h + 0.04, 0.0), vec3(0.66, 0.08, 0.66), iron);
|
||||||
|
let (ax, az) = (tw.aim.cos(), tw.aim.sin());
|
||||||
|
let along = |d: f32, y: f32| c + vec3(ax * d, h + y, az * d);
|
||||||
|
// the demon head: a heavy block with horns, its open jaw is the muzzle
|
||||||
|
draw_cube(along(0.0, 0.24), Vec3::splat(0.34), None, iron);
|
||||||
|
for k in 1..4 {
|
||||||
|
draw_cube(along(k as f32 * 0.14, 0.24), Vec3::splat(0.3 - k as f32 * 0.02), None, shade_c(44, 42, 50, f));
|
||||||
|
}
|
||||||
|
draw_cube(along(0.56, 0.24), Vec3::splat(0.26), None, obs(20));
|
||||||
|
for sgn in [-1.0f32, 1.0] {
|
||||||
|
let side = vec3(-az * sgn, 0.0, ax * sgn);
|
||||||
|
line3(along(0.05, 0.4) + side * 0.14, along(-0.1, 0.72) + side * 0.26, 0.07, bone);
|
||||||
|
}
|
||||||
|
if l >= 2 {
|
||||||
|
// chains around the drum
|
||||||
|
for k in 0..8 {
|
||||||
|
let a0 = k as f32 * 0.785;
|
||||||
|
let a1 = a0 + 0.785;
|
||||||
|
line3(c + vec3(a0.cos() * 0.47, h * 0.55, a0.sin() * 0.47), c + vec3(a1.cos() * 0.47, h * 0.5 - 0.04, a1.sin() * 0.47), 0.035, iron);
|
||||||
|
}
|
||||||
|
skull(vec3(0.0, h * 0.5, 0.45), faces[0]);
|
||||||
|
}
|
||||||
|
if l >= 3 {
|
||||||
|
for k in 0..8 {
|
||||||
|
let a = k as f32 * 0.785 + 0.39;
|
||||||
|
let p = c + vec3(a.cos() * 0.44, h * 0.85, a.sin() * 0.44);
|
||||||
|
line3(p, p + vec3(a.cos() * 0.25, 0.08, a.sin() * 0.25), 0.05, bone);
|
||||||
|
}
|
||||||
|
skull(vec3(0.0, h * 0.5, -0.45), faces[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// dark spire: an obsidian needle between curved horns, claws hold the crystal
|
||||||
|
bx(vec3(0.0, 0.06, 0.0), vec3(0.86, 0.12, 0.86), obs(4));
|
||||||
|
bx(vec3(0.0, 0.17, 0.0), vec3(0.6, 0.1, 0.6), obs(12));
|
||||||
|
bx(vec3(0.0, h * 0.5, 0.0), vec3(0.34, h, 0.34), obs(8));
|
||||||
|
bx(vec3(0.0, h * 0.3, 0.0), vec3(0.44, h * 0.3, 0.44), obs(14));
|
||||||
|
let horns = 2 + l as usize;
|
||||||
|
for k in 0..horns {
|
||||||
|
let a = k as f32 / horns as f32 * std::f32::consts::TAU + 0.4;
|
||||||
|
let mut prev = c + vec3(a.cos() * 0.4, 0.2, a.sin() * 0.4);
|
||||||
|
for q in 1..=5 {
|
||||||
|
let fq = q as f32 / 5.0;
|
||||||
|
let r = 0.4 + (fq * 3.14).sin() * 0.18 - fq * 0.22;
|
||||||
|
let next = c + vec3(a.cos() * r, 0.2 + fq * (h - 0.1), a.sin() * r);
|
||||||
|
line3(prev, next, 0.09 - fq * 0.05, if q == 5 { bone } else { obs(18) });
|
||||||
|
prev = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k in 0..4 {
|
||||||
|
let a = k as f32 * 1.57 + 0.78;
|
||||||
|
line3(c + vec3(a.cos() * 0.14, h, a.sin() * 0.14), c + vec3(a.cos() * 0.24, h + 0.42, a.sin() * 0.24), 0.05, bone);
|
||||||
|
}
|
||||||
|
skull(vec3(0.0, h * 0.35, 0.24), faces[0]);
|
||||||
|
if l >= 3 {
|
||||||
|
skull(vec3(0.0, h * 0.35, -0.24), faces[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_towers_solid(g: &Game) {
|
fn draw_towers_solid(g: &Game) {
|
||||||
for tw in &g.towers {
|
for tw in &g.towers {
|
||||||
|
if !tw.hero && tw.kind != TowerKind::Rune {
|
||||||
|
draw_dark_tower(tw);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let (x, z) = tw.pos();
|
let (x, z) = tw.pos();
|
||||||
let c = vec3(x, 0.0, z);
|
let c = vec3(x, 0.0, z);
|
||||||
let h = tower_height(tw);
|
let h = tower_height(tw);
|
||||||
@@ -1477,6 +1623,55 @@ fn draw_towers_solid(g: &Game) {
|
|||||||
|
|
||||||
fn draw_towers_glow(g: &Game, t: f32) {
|
fn draw_towers_glow(g: &Game, t: f32) {
|
||||||
for (k, tw) in g.towers.iter().enumerate() {
|
for (k, tw) in g.towers.iter().enumerate() {
|
||||||
|
if !tw.hero && tw.kind != TowerKind::Rune {
|
||||||
|
// red glowing joints, arrow slits and the hellmouth's fire
|
||||||
|
let (x, z) = tw.pos();
|
||||||
|
let c = vec3(x, 0.0, z);
|
||||||
|
let h = tower_height(tw);
|
||||||
|
let glow = 0.55 + 0.35 * (t * 2.2 + k as f32).sin() + tw.flash * 2.0;
|
||||||
|
let ember = Color::new(1.0, 0.22, 0.08, glow.min(1.0));
|
||||||
|
match tw.kind {
|
||||||
|
TowerKind::Arrow => {
|
||||||
|
for q in 1..4 {
|
||||||
|
let y = 0.24 + (h - 0.24) * q as f32 / 4.0;
|
||||||
|
let w = 0.64 - q as f32 * 0.04 + 0.01;
|
||||||
|
draw_cube(c + vec3(0.0, y - 0.015, 0.0), vec3(w, 0.025, w), None, ember);
|
||||||
|
}
|
||||||
|
for d in [vec3(0.0, 0.0, 0.31), vec3(0.31, 0.0, 0.0), vec3(0.0, 0.0, -0.31), vec3(-0.31, 0.0, 0.0)] {
|
||||||
|
draw_cube(c + d + vec3(0.0, h * 0.45, 0.0), vec3(if d.x == 0.0 { 0.05 } else { 0.02 }, 0.18, if d.z == 0.0 { 0.05 } else { 0.02 }), None, Color::new(1.0, 0.35, 0.1, 0.9));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TowerKind::Cannon => {
|
||||||
|
let (ax, az) = (tw.aim.cos(), tw.aim.sin());
|
||||||
|
let m = c + vec3(ax * 0.7, h + 0.24, az * 0.7);
|
||||||
|
draw_sphere(m, 0.1 * (1.0 + tw.flash * 3.0), None, Color::new(1.0, 0.5, 0.1, 0.95));
|
||||||
|
draw_sphere(m, 0.2 * (1.0 + tw.flash * 3.0), None, Color::new(1.0, 0.3, 0.05, 0.3));
|
||||||
|
for q in 1..3 {
|
||||||
|
let y = 0.14 + (h - 0.14) * q as f32 / 3.0;
|
||||||
|
draw_cube(c + vec3(0.0, y, 0.0), vec3(0.84, 0.02, 0.84), None, ember);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// the crystal in the claws and shards around it
|
||||||
|
let cc = vec3(x, h + 0.42 + (t * 2.0 + k as f32).sin() * 0.05, z);
|
||||||
|
let pulse = 1.0 + (t * 4.0 + k as f32).sin() * 0.1 + tw.flash * 2.0;
|
||||||
|
draw_sphere(cc, 0.15 * pulse, None, Color::new(0.85, 0.3, 1.0, 0.95));
|
||||||
|
draw_sphere(cc, 0.32 * pulse, None, Color::new(0.7, 0.2, 1.0, 0.22));
|
||||||
|
for q in 0..(1 + tw.level) {
|
||||||
|
let a = t * 1.5 + q as f32 * 2.1;
|
||||||
|
draw_cube(cc + vec3(a.cos() * 0.42, (t * 2.0 + q as f32).sin() * 0.12, a.sin() * 0.42), vec3(0.05, 0.12, 0.05), None, Color::new(0.85, 0.4, 1.0, 0.9));
|
||||||
|
}
|
||||||
|
for q in 0..3 {
|
||||||
|
let y = h * (0.45 + q as f32 * 0.17);
|
||||||
|
let gl = 0.4 + 0.6 * ((t * 2.5 - q as f32 * 0.8 + k as f32).sin() * 0.5 + 0.5);
|
||||||
|
for (dx, dz) in [(0.175f32, 0.0f32), (-0.175, 0.0), (0.0, 0.175), (0.0, -0.175)] {
|
||||||
|
draw_cube(c + vec3(dx, y, dz), vec3(if dx == 0.0 { 0.08 } else { 0.02 }, 0.07, if dz == 0.0 { 0.08 } else { 0.02 }), None, Color::new(1.0, 0.25, 0.4, gl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if tw.kind == TowerKind::Rune {
|
if tw.kind == TowerKind::Rune {
|
||||||
// forge glow on the crown, runes on the keep and three rune stones circling it
|
// forge glow on the crown, runes on the keep and three rune stones circling it
|
||||||
let (x, z) = tw.pos();
|
let (x, z) = tw.pos();
|
||||||
@@ -1551,6 +1746,21 @@ pub fn disc(c: Vec3, r: f32, color: Color, seg: usize) {
|
|||||||
draw_mesh(&Mesh { vertices: v, indices: i, texture: None });
|
draw_mesh(&Mesh { vertices: v, indices: i, texture: None });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// an upright ellipse in the x-y plane (portal vortex)
|
||||||
|
fn vdisc(c: Vec3, rx: f32, ry: f32, color: Color, seg: usize) {
|
||||||
|
let cc = [(color.r * 255.0) as u8, (color.g * 255.0) as u8, (color.b * 255.0) as u8, (color.a * 255.0) as u8];
|
||||||
|
let mut v = vec![Vertex { position: c, uv: Vec2::ZERO, color: cc, normal: Vec4::Z }];
|
||||||
|
for k in 0..seg {
|
||||||
|
let a = k as f32 / seg as f32 * std::f32::consts::TAU;
|
||||||
|
v.push(Vertex { position: c + vec3(a.cos() * rx, a.sin() * ry, 0.0), uv: Vec2::ZERO, color: cc, normal: Vec4::Z });
|
||||||
|
}
|
||||||
|
let mut i = Vec::with_capacity(seg * 3);
|
||||||
|
for k in 0..seg as u16 {
|
||||||
|
i.extend_from_slice(&[0, 1 + k, 1 + (k + 1) % seg as u16]);
|
||||||
|
}
|
||||||
|
draw_mesh(&Mesh { vertices: v, indices: i, texture: None });
|
||||||
|
}
|
||||||
|
|
||||||
fn ring(c: Vec3, r: f32, color: Color) {
|
fn ring(c: Vec3, r: f32, color: Color) {
|
||||||
let seg = 32;
|
let seg = 32;
|
||||||
for k in 0..seg {
|
for k in 0..seg {
|
||||||
@@ -1561,19 +1771,37 @@ fn ring(c: Vec3, r: f32, color: Color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_landmarks_solid(g: &Game, t: f32) {
|
fn draw_landmarks_solid(g: &Game, t: f32) {
|
||||||
// soul core: stepped obsidian pedestal with four spikes holding the orb
|
// soul core: an obsidian throne whose four curved spikes hold the crystal heart in chains
|
||||||
let (cx, cy) = g.map.core;
|
let (cx, cy) = g.map.core;
|
||||||
let c = vec3(cx as f32 + 1.5, 0.0, cy as f32 + 1.5);
|
let c = vec3(cx as f32 + 1.5, 0.0, cy as f32 + 1.5);
|
||||||
draw_cube(c + vec3(0.0, 0.08, 0.0), vec3(2.8, 0.16, 2.8), None, Color::from_rgba(34, 14, 18, 255));
|
let obs = |k: u8| Color::from_rgba(28 + k, 14 + k / 2, 20 + k / 2, 255);
|
||||||
draw_cube(c + vec3(0.0, 0.22, 0.0), vec3(2.1, 0.14, 2.1), None, Color::from_rgba(48, 18, 24, 255));
|
draw_cube(c + vec3(0.0, 0.08, 0.0), vec3(2.9, 0.16, 2.9), None, obs(6));
|
||||||
draw_cube(c + vec3(0.0, 0.35, 0.0), vec3(1.3, 0.14, 1.3), None, Color::from_rgba(66, 24, 30, 255));
|
draw_cube(c + vec3(0.0, 0.22, 0.0), vec3(2.3, 0.14, 2.3), None, obs(14));
|
||||||
for (k, (dx, dz)) in [(-1.15f32, -1.15f32), (1.15, -1.15), (1.15, 1.15), (-1.15, 1.15)].iter().enumerate() {
|
draw_cube(c + vec3(0.0, 0.36, 0.0), vec3(1.5, 0.14, 1.5), None, obs(24));
|
||||||
|
draw_cube(c + vec3(0.0, 0.48, 0.0), vec3(0.8, 0.1, 0.8), None, obs(32));
|
||||||
|
let heart = c + vec3(0.0, 1.55, 0.0);
|
||||||
|
let iron = Color::from_rgba(70, 64, 72, 255);
|
||||||
|
for (k, (dx, dz)) in [(-1.2f32, -1.2f32), (1.2, -1.2), (1.2, 1.2), (-1.2, 1.2)].iter().enumerate() {
|
||||||
let b = c + vec3(*dx, 0.0, *dz);
|
let b = c + vec3(*dx, 0.0, *dz);
|
||||||
draw_cube(b + vec3(0.0, 0.45, 0.0), vec3(0.26, 0.9, 0.26), None, Color::from_rgba(28, 20, 26, 255));
|
draw_cube(b + vec3(0.0, 0.3, 0.0), vec3(0.36, 0.6, 0.36), None, obs(4));
|
||||||
draw_cube(b + vec3(0.0, 1.05, 0.0), vec3(0.16, 0.3, 0.16), None, Color::from_rgba(40, 28, 36, 255));
|
// a curved spike leaning towards the heart
|
||||||
draw_cube(b + vec3(0.0, 1.26, 0.0), vec3(0.08, 0.14, 0.08), None, Color::from_rgba(180, 140, 60, 255));
|
let mut prev = b + vec3(0.0, 0.55, 0.0);
|
||||||
// claws reaching for the orb
|
for q in 1..=5 {
|
||||||
line3(b + vec3(0.0, 1.0, 0.0), c + vec3(dx * 0.45, 1.55 - (k % 2) as f32 * 0.1, dz * 0.45), 0.07, Color::from_rgba(36, 24, 30, 255));
|
let f = q as f32 / 5.0;
|
||||||
|
let next = b + vec3(dx * 0.22 * f * f, 0.55 + f * 0.9, dz * 0.22 * f * f);
|
||||||
|
line3(prev, next, 0.2 - f * 0.14, if q == 5 { Color::from_rgba(200, 180, 150, 255) } else { obs(10) });
|
||||||
|
prev = next;
|
||||||
|
}
|
||||||
|
// a sagging chain from the spike to the heart (links alternate)
|
||||||
|
let top = b + vec3(dx * 0.12, 1.3, dz * 0.12);
|
||||||
|
for q in 0..9 {
|
||||||
|
let f0 = q as f32 / 9.0;
|
||||||
|
let f1 = (q + 1) as f32 / 9.0;
|
||||||
|
let sag = |f: f32| -0.18 * (f * std::f32::consts::PI).sin() + (t * 1.3 + k as f32).sin() * 0.02;
|
||||||
|
let p0 = top.lerp(heart, f0 * 0.82) + vec3(0.0, sag(f0), 0.0);
|
||||||
|
let p1 = top.lerp(heart, f1 * 0.82) + vec3(0.0, sag(f1), 0.0);
|
||||||
|
line3(p0, p1, if q % 2 == 0 { 0.06 } else { 0.035 }, iron);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// rival masters' cores: obsidian pedestal (the orb glows in the glow pass)
|
// rival masters' cores: obsidian pedestal (the orb glows in the glow pass)
|
||||||
for rv in g.rivals.iter().filter(|r| r.alive) {
|
for rv in g.rivals.iter().filter(|r| r.alive) {
|
||||||
@@ -1585,15 +1813,44 @@ fn draw_landmarks_solid(g: &Game, t: f32) {
|
|||||||
draw_cube(c + vec3(dx, 0.5, dz), vec3(0.22, 1.0, 0.22), None, Color::from_rgba(30, 28, 38, 255));
|
draw_cube(c + vec3(dx, 0.5, dz), vec3(0.22, 1.0, 0.22), None, Color::from_rgba(30, 28, 38, 255));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// portal circles: eight standing stones of different height
|
// portals: a demon gate - an arch of stone blocks with bone ribs and horns on a stepped
|
||||||
for &(px, py) in &g.map.portals {
|
// platform (the vortex glows in the glow pass); grey and dead until claimed
|
||||||
|
for (pk, &(px, py)) in g.map.portals.iter().enumerate() {
|
||||||
|
let owned = g.map.portal_owned(pk);
|
||||||
let pc = vec3(px as f32 + 1.5, 0.0, py as f32 + 1.5);
|
let pc = vec3(px as f32 + 1.5, 0.0, py as f32 + 1.5);
|
||||||
for k in 0..8 {
|
let stone = |k: u8| if owned { Color::from_rgba(46 + k, 30 + k / 2, 58 + k, 255) } else { Color::from_rgba(56 + k, 54 + k, 60 + k, 255) };
|
||||||
let a = k as f32 * 0.785 + 0.39;
|
let bone = if owned { Color::from_rgba(212, 200, 176, 255) } else { Color::from_rgba(150, 146, 140, 255) };
|
||||||
let h = 0.8 + ((k * 5 + px as usize) % 4) as f32 * 0.15;
|
draw_cube(pc + vec3(0.0, 0.05, 0.0), vec3(2.9, 0.1, 2.9), None, stone(0));
|
||||||
let p = pc + vec3(a.cos() * 1.5, h * 0.5, a.sin() * 1.5);
|
draw_cube(pc + vec3(0.0, 0.14, 0.0), vec3(2.4, 0.08, 1.4), None, stone(8));
|
||||||
draw_cube(p, vec3(0.28, h, 0.28), None, Color::from_rgba(52 + (k * 3) as u8, 44, 62, 255));
|
let r = 1.25;
|
||||||
draw_cube(p + vec3(0.0, h * 0.5 + 0.04, 0.0), vec3(0.22, 0.08, 0.22), None, Color::from_rgba(70, 60, 82, 255));
|
for side in [-1.0f32, 1.0] {
|
||||||
|
// pillars of stacked, slightly offset blocks
|
||||||
|
for k in 0..5 {
|
||||||
|
let off = if k % 2 == 0 { 0.03 } else { -0.03 };
|
||||||
|
draw_cube(pc + vec3(side * (r + off), 0.32 + k as f32 * 0.36, 0.0), vec3(0.42, 0.34, 0.5), None, stone((k * 5) as u8 + 6));
|
||||||
|
}
|
||||||
|
// a bone rib along the inner edge
|
||||||
|
line3(pc + vec3(side * (r - 0.27), 0.2, 0.26), pc + vec3(side * (r - 0.27), 1.95, 0.26), 0.06, bone);
|
||||||
|
line3(pc + vec3(side * (r - 0.27), 0.2, -0.26), pc + vec3(side * (r - 0.27), 1.95, -0.26), 0.06, bone);
|
||||||
|
}
|
||||||
|
// the arch: blocks on a half circle
|
||||||
|
for k in 0..=8 {
|
||||||
|
let a = k as f32 / 8.0 * std::f32::consts::PI;
|
||||||
|
let p = pc + vec3(a.cos() * r, 2.0 + a.sin() * r * 0.9, 0.0);
|
||||||
|
draw_cube(p, vec3(0.4, 0.34, 0.52), None, stone(16 + (k % 3) as u8 * 4));
|
||||||
|
}
|
||||||
|
// keystone skull and horns
|
||||||
|
let top = pc + vec3(0.0, 2.0 + r * 0.9 + 0.12, 0.0);
|
||||||
|
draw_cube(top, vec3(0.3, 0.28, 0.3), None, bone);
|
||||||
|
for sgn in [-1.0f32, 1.0] {
|
||||||
|
draw_cube(top + vec3(sgn * 0.07, 0.03, 0.155), vec3(0.07, 0.07, 0.02), None, Color::from_rgba(20, 8, 16, 255));
|
||||||
|
let mut prev = top + vec3(sgn * 0.15, 0.08, 0.0);
|
||||||
|
for q in 1..=4 {
|
||||||
|
let f = q as f32 / 4.0;
|
||||||
|
let next = top + vec3(sgn * (0.15 + f * 0.55), 0.08 + (f * 2.2).sin() * 0.45, 0.0);
|
||||||
|
line3(prev, next, 0.1 - f * 0.07, bone);
|
||||||
|
prev = next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// gate pillars
|
// gate pillars
|
||||||
@@ -1636,34 +1893,68 @@ fn draw_landmarks_solid(g: &Game, t: f32) {
|
|||||||
let _ = t;
|
let _ = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_landmarks_glow(g: &Game, t: f32) {
|
fn draw_landmarks_glow(g: &Game, t: f32, cam_r: Vec3) {
|
||||||
// soul core: pulsing orb (faster and more unsteady when hurt), energy threads to the
|
// soul core: a faceted crystal heart beating lub-dub (faster when hurt, cracked when badly
|
||||||
// spikes, runes circling around it
|
// hurt), glowing veins across the floor, rising embers
|
||||||
let (cx, cy) = g.map.core;
|
let (cx, cy) = g.map.core;
|
||||||
let hp = (g.core_hp / CORE_HP).clamp(0.0, 1.0);
|
let hp = (g.core_hp / CORE_HP).clamp(0.0, 1.0);
|
||||||
let beat = t * (2.0 + (1.0 - hp) * 6.0);
|
let rate = 0.9 + (1.0 - hp) * 1.6;
|
||||||
let pulse = 1.0 + beat.sin() * 0.06 + if hp < 0.4 { (t * 31.0).sin() * 0.03 } else { 0.0 };
|
let ph = (t * rate).fract();
|
||||||
|
let beat = (-((ph - 0.02) / 0.06).powi(2)).exp() + 0.65 * (-((ph - 0.2) / 0.06).powi(2)).exp();
|
||||||
|
let pulse = 1.0 + beat * 0.09;
|
||||||
let base = vec3(cx as f32 + 1.5, 0.0, cy as f32 + 1.5);
|
let base = vec3(cx as f32 + 1.5, 0.0, cy as f32 + 1.5);
|
||||||
let c = base + vec3(0.0, 1.6 + (t * 1.3).sin() * 0.06, 0.0);
|
let hc = base + vec3(0.0, 1.55 + (t * 1.1).sin() * 0.03, 0.0);
|
||||||
draw_sphere(c, 0.62 * pulse, None, Color::new(0.4 + 0.5 * hp, 0.04, 0.07, 1.0));
|
let right = vec3(cam_r.x, 0.0, cam_r.z).normalize_or_zero();
|
||||||
draw_sphere(c, 0.32 * pulse, None, Color::new(1.0, 0.5 + 0.3 * hp, 0.4, 0.55));
|
let up = Vec3::Y;
|
||||||
draw_sphere(c, 0.85 * pulse, None, Color::new(1.0, 0.2, 0.15, 0.22));
|
let eye = EYE.with(|e| e.get());
|
||||||
draw_sphere(c, 1.15 * pulse, None, Color::new(1.0, 0.1, 0.1, 0.08));
|
let front = vec3(eye.x, 0.0, eye.z).normalize_or_zero(); // towards the viewer
|
||||||
draw_sphere(c + vec3(-0.18, 0.2, 0.18), 0.13, None, Color::new(1.0, 0.85, 0.7, 0.8));
|
let red = Color::new(0.38 + 0.38 * hp + 0.2 * beat, 0.02 + 0.04 * beat, 0.07, 1.0);
|
||||||
for (k, (dx, dz)) in [(-1.15f32, -1.15f32), (1.15, -1.15), (1.15, 1.15), (-1.15, 1.15)].iter().enumerate() {
|
let hot = Color::new(1.0, 0.35 + 0.25 * beat, 0.3, 0.7);
|
||||||
let tip = base + vec3(*dx, 1.33, *dz);
|
// two lobes and the point below, then the facets as bright chips
|
||||||
let flick = 0.5 + 0.5 * (t * 5.0 + k as f32 * 1.7).sin();
|
let s0 = 0.36 * pulse;
|
||||||
let mid = (tip + c) * 0.5 + vec3((t * 7.0 + k as f32).sin() * 0.08, 0.15, (t * 6.0 + k as f32).cos() * 0.08);
|
for sgn in [-1.0f32, 1.0] {
|
||||||
let col = Color::new(1.0, 0.25 + 0.3 * flick, 0.2, 0.35 + 0.45 * flick * hp.max(0.3));
|
draw_sphere(hc + right * (sgn * 0.21 * pulse) + up * (0.12 * pulse), s0 * 0.85, None, red);
|
||||||
line3(tip, mid, 0.04, col);
|
|
||||||
line3(mid, c, 0.04, col);
|
|
||||||
draw_sphere(tip + vec3(0.0, 0.05, 0.0), 0.07, None, Color::new(1.0, 0.6, 0.3, 0.6 + 0.4 * flick));
|
|
||||||
}
|
}
|
||||||
for k in 0..6 {
|
for (k, (y, r)) in [(-0.05f32, 0.36f32), (-0.22, 0.28), (-0.36, 0.18), (-0.46, 0.09)].iter().enumerate() {
|
||||||
let a = t * 0.7 + k as f32 * 1.047;
|
draw_sphere(hc + up * (y * pulse), r * pulse, None, if k == 0 { red } else { Color::new(red.r * 0.9, red.g, red.b, 1.0) });
|
||||||
let p = c + vec3(a.cos() * 1.05, (t * 1.1 + k as f32).sin() * 0.25, a.sin() * 1.05);
|
|
||||||
draw_cube(p, vec3(0.1, 0.16, 0.04), None, Color::new(1.0, 0.7, 0.3, 0.85));
|
|
||||||
}
|
}
|
||||||
|
draw_sphere(hc + right * (-0.12 * pulse) + up * (0.2 * pulse), 0.1 * pulse, None, Color::new(1.0, 0.75, 0.7, 0.75));
|
||||||
|
for k in 0..7 {
|
||||||
|
let a = k as f32 * 0.9 + 0.4;
|
||||||
|
let p = hc + right * (a.cos() * 0.22 * pulse) + up * (a.sin() * 0.2 * pulse - 0.05) + front * 0.3;
|
||||||
|
draw_cube(p, Vec3::splat(0.05), None, Color::new(1.0, 0.5 + 0.3 * ((t * 3.0 + k as f32).sin() * 0.5 + 0.5), 0.45, 0.8));
|
||||||
|
}
|
||||||
|
draw_sphere(hc, 0.75 * pulse, None, Color::new(1.0, 0.15, 0.15, 0.16 + 0.12 * beat));
|
||||||
|
draw_sphere(hc, 1.1 * pulse, None, Color::new(1.0, 0.1, 0.1, 0.06 + 0.05 * beat));
|
||||||
|
// cracks when hurt
|
||||||
|
let cracks = ((1.0 - hp) * 7.0) as usize;
|
||||||
|
for k in 0..cracks.min(6) {
|
||||||
|
let a = k as f32 * 2.3;
|
||||||
|
let p0 = hc + right * (a.cos() * 0.08) + up * (a.sin() * 0.1);
|
||||||
|
let p1 = p0 + right * (a.cos() * 0.25) + up * ((a * 1.7).sin() * 0.22);
|
||||||
|
line3(p0 + front * 0.37, p1 + front * 0.33, 0.025, Color::new(0.08, 0.0, 0.02, 1.0));
|
||||||
|
}
|
||||||
|
// veins across the floor, a pulse running outwards with every beat
|
||||||
|
for k in 0..8 {
|
||||||
|
let a = k as f32 * 0.785 + 0.2;
|
||||||
|
let mut prev = base + vec3(a.cos() * 1.3, 0.02, a.sin() * 1.3);
|
||||||
|
for q in 1..=6 {
|
||||||
|
let f = q as f32 / 6.0;
|
||||||
|
let wig = ((k * 7 + q * 3) as f32).sin() * 0.18;
|
||||||
|
let next = base + vec3(a.cos() * (1.3 + f * 2.2) - a.sin() * wig, 0.02, a.sin() * (1.3 + f * 2.2) + a.cos() * wig);
|
||||||
|
let wave = (-((f - ph * 1.6) / 0.18).powi(2)).exp();
|
||||||
|
line3(prev, next, 0.05, Color::new(1.0, 0.15 + 0.3 * wave, 0.1, (0.25 + 0.7 * wave) * (1.0 - f * 0.5) * (0.4 + 0.6 * hp)));
|
||||||
|
prev = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// embers rising from the heart
|
||||||
|
for k in 0..10 {
|
||||||
|
let e = (t * 0.35 + k as f32 * 0.137).fract();
|
||||||
|
let a = k as f32 * 2.4;
|
||||||
|
let p = hc + vec3(a.cos() * (0.2 + e * 0.5), -0.2 + e * 2.0, a.sin() * (0.2 + e * 0.5));
|
||||||
|
draw_cube(p, Vec3::splat(0.04 * (1.0 - e) + 0.01), None, Color::new(1.0, 0.45 + 0.4 * (1.0 - e), 0.15, 1.0 - e));
|
||||||
|
}
|
||||||
|
let _ = hot;
|
||||||
// rival masters' cores in their colours
|
// rival masters' cores in their colours
|
||||||
for rv in g.rivals.iter().filter(|r| r.alive) {
|
for rv in g.rivals.iter().filter(|r| r.alive) {
|
||||||
let (rx, rz) = rv.centre();
|
let (rx, rz) = rv.centre();
|
||||||
@@ -1678,42 +1969,47 @@ fn draw_landmarks_glow(g: &Game, t: f32) {
|
|||||||
draw_cube(c + vec3(a.cos() * 0.9, (t + k as f32).sin() * 0.2, a.sin() * 0.9), vec3(0.08, 0.14, 0.04), None, Color::new(col.x, col.y, col.z, 0.8));
|
draw_cube(c + vec3(a.cos() * 0.9, (t + k as f32).sin() * 0.2, a.sin() * 0.9), vec3(0.08, 0.14, 0.04), None, Color::new(col.x, col.y, col.z, 0.8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// portals: a circle of standing stones with runes, the vortex, a column of light, sparks
|
// portals: runes on the arch and a vertical vortex inside it, sparks pouring out and falling
|
||||||
for (pk, &(px, py)) in g.map.portals.iter().enumerate() {
|
for (pk, &(px, py)) in g.map.portals.iter().enumerate() {
|
||||||
let owned = g.map.portal_owned(pk);
|
let owned = g.map.portal_owned(pk);
|
||||||
let pc = vec3(px as f32 + 1.5, 0.03, py as f32 + 1.5);
|
let pc = vec3(px as f32 + 1.5, 0.0, py as f32 + 1.5);
|
||||||
let arm = if owned { Color::new(0.8, 0.4, 1.0, 1.0) } else { Color::new(0.45, 0.42, 0.5, 1.0) };
|
let arm = if owned { Color::new(0.8, 0.35, 1.0, 1.0) } else { Color::new(0.45, 0.42, 0.5, 1.0) };
|
||||||
disc(pc, 1.35, if owned { Color::new(0.2, 0.05, 0.3, 0.9) } else { Color::new(0.1, 0.1, 0.12, 0.9) }, 32);
|
let mid = pc + vec3(0.0, 1.3, 0.0);
|
||||||
let speed = if owned { 1.6 } else { 0.3 };
|
let (rx, ry) = (0.95, 1.55);
|
||||||
for a in 0..3 {
|
// the dark heart of the vortex: a vertical disc in the arch
|
||||||
for k in 0..18 {
|
vdisc(mid, rx, ry, if owned { Color::new(0.16, 0.02, 0.26, 0.92) } else { Color::new(0.08, 0.08, 0.1, 0.85) }, 28);
|
||||||
let f = k as f32 / 18.0;
|
let speed = if owned { 2.0 } else { 0.25 };
|
||||||
let ang = t * speed + a as f32 * 2.094 + f * 4.0;
|
for armk in 0..3 {
|
||||||
let r = f * 1.3;
|
for k in 0..22 {
|
||||||
let p = pc + vec3(ang.cos() * r, 0.1 + (1.0 - f) * 0.5, ang.sin() * r);
|
let f = k as f32 / 22.0;
|
||||||
draw_cube(p, Vec3::splat(0.05 + 0.06 * (1.0 - f)), None, Color::new(arm.r, arm.g, arm.b, 0.4 + 0.6 * (1.0 - f)));
|
let ang = t * speed + armk as f32 * 2.094 + f * 5.0;
|
||||||
|
let p = mid + vec3(ang.cos() * rx * f, ang.sin() * ry * f * 0.9, 0.0);
|
||||||
|
draw_cube(p, Vec3::splat(0.05 + 0.05 * f), None, Color::new(arm.r, arm.g, arm.b, if owned { 0.3 + 0.7 * f } else { 0.25 }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// runes on the stones
|
// runes on the pillars and the arch
|
||||||
for k in 0..8 {
|
for k in 0..10 {
|
||||||
let a = k as f32 * 0.785 + 0.39;
|
let glow = 0.5 + 0.5 * (t * 2.0 - k as f32 * 0.6).sin();
|
||||||
let glow = 0.5 + 0.5 * (t * 2.0 + k as f32 * 0.8).sin();
|
let p = if k < 6 {
|
||||||
let h = 0.45 + (k % 3) as f32 * 0.12;
|
let side = if k % 2 == 0 { -1.0 } else { 1.0 };
|
||||||
let p = pc + vec3(a.cos() * 1.4, h, a.sin() * 1.4);
|
pc + vec3(side * 1.25, 0.4 + (k / 2) as f32 * 0.6, 0.27)
|
||||||
draw_cube(p, vec3(0.08, 0.14, 0.08), None, Color::new(arm.r, arm.g, arm.b, if owned { 0.4 + 0.6 * glow } else { 0.25 }));
|
} else {
|
||||||
|
let a = (k - 6) as f32 / 3.0 * std::f32::consts::PI * 0.7 + 0.47;
|
||||||
|
pc + vec3(a.cos() * 1.25, 2.0 + a.sin() * 1.12, 0.27)
|
||||||
|
};
|
||||||
|
draw_cube(p, vec3(0.12, 0.14, 0.02), None, Color::new(arm.r, arm.g, arm.b, if owned { 0.35 + 0.65 * glow } else { 0.2 }));
|
||||||
}
|
}
|
||||||
if owned {
|
if owned {
|
||||||
// column of light and rising sparks
|
draw_sphere(mid, 0.35 + 0.05 * (t * 3.0).sin(), None, Color::new(0.95, 0.75, 1.0, 0.5));
|
||||||
for k in 0..5 {
|
// sparks pour out of the vortex and fall to the floor
|
||||||
let y = 0.4 + k as f32 * 0.5;
|
for k in 0..14 {
|
||||||
draw_sphere(pc + vec3(0.0, y, 0.0), 0.35 - k as f32 * 0.05, None, Color::new(0.75, 0.45, 1.0, 0.1));
|
let e = (t * 0.5 + k as f32 * 0.071).fract();
|
||||||
}
|
let a = k as f32 * 2.4;
|
||||||
for k in 0..10 {
|
let dir = if k % 2 == 0 { 1.0 } else { -1.0 };
|
||||||
let ph = (t * 0.6 + k as f32 * 0.37).fract();
|
let p = mid + vec3(a.cos() * 0.5 * (1.0 - e), a.sin() * 0.6 + e * 0.4 - e * e * 1.9, dir * e * 1.1);
|
||||||
let a = k as f32 * 2.4 + t * 0.5;
|
draw_cube(p, Vec3::splat(0.045 * (1.0 - e) + 0.015), None, Color::new(0.95, 0.65, 1.0, 1.0 - e));
|
||||||
let p = pc + vec3(a.cos() * (0.3 + 0.5 * ph), 0.2 + ph * 2.4, a.sin() * (0.3 + 0.5 * ph));
|
|
||||||
draw_cube(p, Vec3::splat(0.05), None, Color::new(0.9, 0.7, 1.0, 1.0 - ph));
|
|
||||||
}
|
}
|
||||||
|
disc(pc + vec3(0.0, 0.2, 0.0), 1.1, Color::new(0.6, 0.25, 0.9, 0.12 + 0.05 * (t * 2.0).sin()), 24);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// gate light
|
// gate light
|
||||||
|
|||||||
Reference in new issue
Block a user