Gear is part of the sprites; more detailed towers and gates
- Level gear is painted into five sprite variants per kind (shoulder pieces, helmet with horns or plume, cape, gold with crown), derived from each silhouette, so it moves, sways and flips with the unit; billboards use the 12x12 cell with the gear border - Arrow tower: plinth, masonry courses, arrow slits, corbels, wooden platform, merlons, pennant and ladder; cannon tower: round body with iron bands, carriage, barrel with muzzle ring, cannon balls; magic tower: stepped base, buttresses, glowing rune bands - Hero gate / gate of the order: plinths, banded pillars, capitals, stepped arch with keystone, half-raised portcullis, banners of the order Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c9848c8948
commit
359000afa3
2 files changed
+223
-73
No files matched your search
+114
-10
@@ -1062,29 +1062,125 @@ pub const SPRITE_KINDS: [EK; 17] = [
|
|||||||
];
|
];
|
||||||
// width of the sprite texture in pixels
|
// width of the sprite texture in pixels
|
||||||
pub const SPRITE_TEX_W: f32 = (17 * 12) as f32;
|
pub const SPRITE_TEX_W: f32 = (17 * 12) as f32;
|
||||||
pub const SPRITE_TEX_H: f32 = 24.0;
|
pub const SPRITE_TEX_H: f32 = (2 * GEAR_TIERS * SPR_CELL) as f32;
|
||||||
|
|
||||||
pub fn sprite_index(k: EK) -> u32 {
|
pub fn sprite_index(k: EK) -> u32 {
|
||||||
SPRITE_KINDS.iter().position(|&s| s == k).unwrap_or(0) as u32
|
SPRITE_KINDS.iter().position(|&s| s == k).unwrap_or(0) as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
// row 0: normal sprites, row 1: white silhouettes (hit flash)
|
// Equipment tier of a level: 0 (Lv 1-2) none, 1 (3-4) shoulder pieces, 2 (5-6) + helmet,
|
||||||
|
// 3 (7-8) + cape, 4 (9-10) all in gold with a crown
|
||||||
|
pub const GEAR_TIERS: u32 = 5;
|
||||||
|
pub fn gear_tier(level: u32) -> u32 {
|
||||||
|
((level.max(1) - 1) / 2).min(GEAR_TIERS - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_hero_kind(k: EK) -> bool {
|
||||||
|
matches!(k, EK::Knight | EK::Archer | EK::Priest | EK::Dwarf | EK::Paladin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// the 12x12 cell of a kind with its gear painted around and onto the silhouette
|
||||||
|
fn sprite_with_gear(k: EK, tier: u32) -> [[Option<Color>; 12]; 12] {
|
||||||
|
let mut g = [[None; 12]; 12];
|
||||||
|
for (y, row) in sprite_rows(k).iter().enumerate() {
|
||||||
|
for (x, ch) in row.chars().enumerate().take(SPR) {
|
||||||
|
g[y + 1][x + 1] = pal(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tier == 0 {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
let metal = match tier {
|
||||||
|
1 => c(196, 126, 60),
|
||||||
|
4 => c(250, 206, 72),
|
||||||
|
_ => c(208, 214, 228),
|
||||||
|
};
|
||||||
|
let dark = Color::new(metal.r * 0.6, metal.g * 0.6, metal.b * 0.6, 1.0);
|
||||||
|
let span = |g: &[[Option<Color>; 12]; 12], y: usize| -> Option<(usize, usize)> {
|
||||||
|
let xs: Vec<usize> = (0..12).filter(|&x| g[y][x].is_some()).collect();
|
||||||
|
Some((*xs.first()?, *xs.last()?))
|
||||||
|
};
|
||||||
|
let top = (0..12).find(|&y| g[y].iter().any(|p| p.is_some())).unwrap_or(1);
|
||||||
|
// cape (behind the figure: only where the sprite is empty)
|
||||||
|
if tier >= 3 {
|
||||||
|
let cape = if is_hero_kind(k) { c(44, 74, 156) } else { c(128, 22, 34) };
|
||||||
|
for y in (top + 3).min(11)..=(top + 8).min(11) {
|
||||||
|
if let Some((l, r)) = span(&g, y) {
|
||||||
|
for x in [l.wrapping_sub(1), r + 1] {
|
||||||
|
if x < 12 && g[y][x].is_none() {
|
||||||
|
g[y][x] = Some(cape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// shoulder pieces at the widest row of the upper body
|
||||||
|
let shoulder = (top + 2..(top + 6).min(11)).max_by_key(|&y| span(&g, y).map_or(0, |(l, r)| r - l)).unwrap_or(top + 3);
|
||||||
|
if let Some((l, r)) = span(&g, shoulder) {
|
||||||
|
for x in [l, r] {
|
||||||
|
g[shoulder][x] = Some(metal);
|
||||||
|
if shoulder + 1 < 12 && g[shoulder + 1][x].is_some() {
|
||||||
|
g[shoulder + 1][x] = Some(dark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// helmet: the top row in metal, horns (creatures) or a plume (heroes)
|
||||||
|
if tier >= 2 {
|
||||||
|
if let Some((l, r)) = span(&g, top) {
|
||||||
|
for x in l..=r {
|
||||||
|
if g[top][x].is_some() {
|
||||||
|
g[top][x] = Some(metal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if top >= 1 {
|
||||||
|
if is_hero_kind(k) {
|
||||||
|
g[top - 1][(l + r) / 2] = Some(c(200, 40, 40));
|
||||||
|
} else {
|
||||||
|
let bone = c(232, 222, 200);
|
||||||
|
if l >= 1 {
|
||||||
|
g[top - 1][l - 1] = Some(bone);
|
||||||
|
g[top][l - 1] = Some(bone);
|
||||||
|
}
|
||||||
|
if r + 1 < 12 {
|
||||||
|
g[top - 1][r + 1] = Some(bone);
|
||||||
|
g[top][r + 1] = Some(bone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// crown
|
||||||
|
if tier >= 4 && top >= 1 {
|
||||||
|
if let Some((l, r)) = span(&g, top) {
|
||||||
|
let m = (l + r) / 2;
|
||||||
|
for x in [m.saturating_sub(1), m + 1] {
|
||||||
|
if x < 12 {
|
||||||
|
g[top - 1][x] = Some(c(255, 230, 90));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g
|
||||||
|
}
|
||||||
|
|
||||||
|
// rows 2*tier: sprites with the gear of the tier, rows 2*tier+1: white silhouettes (hit flash)
|
||||||
fn gen_sprites() -> Image {
|
fn gen_sprites() -> Image {
|
||||||
let n = SPRITE_KINDS.len() as u32;
|
let n = SPRITE_KINDS.len() as u32;
|
||||||
let mut img = Image::gen_image_color((n * SPR_CELL) as u16, (2 * SPR_CELL) as u16, Color::new(0.0, 0.0, 0.0, 0.0));
|
let mut img = Image::gen_image_color((n * SPR_CELL) as u16, (2 * GEAR_TIERS * SPR_CELL) as u16, Color::new(0.0, 0.0, 0.0, 0.0));
|
||||||
for (i, &k) in SPRITE_KINDS.iter().enumerate() {
|
for (i, &k) in SPRITE_KINDS.iter().enumerate() {
|
||||||
let rows = sprite_rows(k);
|
for tier in 0..GEAR_TIERS {
|
||||||
for (y, row) in rows.iter().enumerate() {
|
let g = sprite_with_gear(k, tier);
|
||||||
for (x, ch) in row.chars().enumerate().take(SPR) {
|
for (y, row) in g.iter().enumerate() {
|
||||||
if let Some(col) = pal(ch) {
|
for (x, px) in row.iter().enumerate() {
|
||||||
let px = i as u32 * SPR_CELL + 1 + x as u32;
|
if let Some(col) = px {
|
||||||
let py = 1 + y as u32;
|
let (px, py) = (i as u32 * SPR_CELL + x as u32, tier * 2 * SPR_CELL + y as u32);
|
||||||
img.set_pixel(px, py, col);
|
img.set_pixel(px, py, *col);
|
||||||
img.set_pixel(px, py + SPR_CELL, WHITE);
|
img.set_pixel(px, py + SPR_CELL, WHITE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
img
|
img
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1214,11 +1310,19 @@ impl Gfx {
|
|||||||
Rect::new((id % ATLAS_COLS) as f32 * CELL, (id / ATLAS_COLS) as f32 * CELL, CELL, CELL)
|
Rect::new((id % ATLAS_COLS) as f32 * CELL, (id / ATLAS_COLS) as f32 * CELL, CELL, CELL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the plain 10x10 sprite (interface icons)
|
||||||
pub fn sprite_rect(k: EK, white: bool) -> Rect {
|
pub fn sprite_rect(k: EK, white: bool) -> Rect {
|
||||||
let i = sprite_index(k);
|
let i = sprite_index(k);
|
||||||
Rect::new((i * SPR_CELL + 1) as f32, (1 + if white { SPR_CELL } else { 0 }) as f32, SPR as f32, SPR as f32)
|
Rect::new((i * SPR_CELL + 1) as f32, (1 + if white { SPR_CELL } else { 0 }) as f32, SPR as f32, SPR as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the whole 12x12 cell with the gear of a level (billboards)
|
||||||
|
pub fn sprite_cell(k: EK, white: bool, level: u32) -> Rect {
|
||||||
|
let i = sprite_index(k);
|
||||||
|
let row = gear_tier(level) * 2 + white as u32;
|
||||||
|
Rect::new((i * SPR_CELL) as f32, (row * SPR_CELL) as f32, SPR_CELL as f32, SPR_CELL as f32)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tile_tex(kind: Kind, claimed: bool, hero: bool, var: u8, reinforced: bool) -> u32 {
|
pub fn tile_tex(kind: Kind, claimed: bool, hero: bool, var: u8, reinforced: bool) -> u32 {
|
||||||
let v = var as u32 & 3;
|
let v = var as u32 & 3;
|
||||||
match kind {
|
match kind {
|
||||||
|
|||||||
+109
-63
@@ -1020,7 +1020,6 @@ impl R3d {
|
|||||||
}
|
}
|
||||||
draw_landmarks_solid(g, t);
|
draw_landmarks_solid(g, t);
|
||||||
draw_towers_solid(g);
|
draw_towers_solid(g);
|
||||||
draw_gear(g, cam);
|
|
||||||
crate::props::draw_props_solid(g, cam, t);
|
crate::props::draw_props_solid(g, cam, t);
|
||||||
|
|
||||||
// ---- unlit glow / overlays
|
// ---- unlit glow / overlays
|
||||||
@@ -1108,7 +1107,7 @@ impl R3d {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
let white = e.flash > 0.07;
|
let white = e.flash > 0.07;
|
||||||
let src = Gfx::sprite_rect(e.kind, white);
|
let src = Gfx::sprite_cell(e.kind, white, e.level);
|
||||||
let (mut u0, v0) = (src.x / sw, src.y / sh);
|
let (mut u0, v0) = (src.x / sw, src.y / sh);
|
||||||
let (mut u1, v1) = ((src.x + src.w) / sw, (src.y + src.h) / sh);
|
let (mut u1, v1) = ((src.x + src.w) / sw, (src.y + src.h) / sh);
|
||||||
if e.face < 0.0 {
|
if e.face < 0.0 {
|
||||||
@@ -1131,8 +1130,10 @@ impl R3d {
|
|||||||
let y = dx * sr + dy * cr;
|
let y = dx * sr + dy * cr;
|
||||||
base + r * x + Vec3::Y * y
|
base + r * x + Vec3::Y * y
|
||||||
};
|
};
|
||||||
|
// the 12x12 cell (sprite with a one pixel border for the gear): the sprite's feet
|
||||||
|
// (bottom of its 11th pixel row) stay on the ground
|
||||||
mb.quad(
|
mb.quad(
|
||||||
[corner(-w * 0.5, h), corner(w * 0.5, h), corner(w * 0.5, 0.0), corner(-w * 0.5, 0.0)],
|
[corner(-w * 0.6, h * 1.1), corner(w * 0.6, h * 1.1), corner(w * 0.6, -h * 0.1), corner(-w * 0.6, -h * 0.1)],
|
||||||
[vec2(u0, v0), vec2(u1, v0), vec2(u1, v1), vec2(u0, v1)],
|
[vec2(u0, v0), vec2(u1, v0), vec2(u1, v1), vec2(u0, v1)],
|
||||||
[c; 4],
|
[c; 4],
|
||||||
[toward; 4],
|
[toward; 4],
|
||||||
@@ -1239,53 +1240,13 @@ fn gear_shown(e: &Ent) -> bool {
|
|||||||
e.alive && !e.held && e.captive == 0 && e.level >= 3 && !(matches!(e.job, Job::Sleep(_)) && !e.moving)
|
e.alive && !e.held && e.captive == 0 && e.level >= 3 && !(matches!(e.job, Job::Sleep(_)) && !e.moving)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equipment that grows with the level: shoulder pieces (Lv 3+: bronze, 5+: silver, 8+: gold),
|
|
||||||
// a helmet (Lv 5+: horns for creatures, a plume for heroes) and a cape (Lv 7+).
|
|
||||||
fn draw_gear(g: &Game, cam: &Cam) {
|
|
||||||
let (_, r, _) = cam.basis();
|
|
||||||
let toward = cam.eye_dir();
|
|
||||||
for e in g.ents.iter().filter(|e| gear_shown(e)) {
|
|
||||||
let w = sprite_size(e.kind);
|
|
||||||
let mut bob = if e.moving { (e.anim * 12.0).sin().abs() * 0.06 } else { 0.0 };
|
|
||||||
if flies(e.kind) {
|
|
||||||
bob = 0.45;
|
|
||||||
}
|
|
||||||
let base = vec3(e.x, bob, e.y) + toward * 0.5;
|
|
||||||
let at = |dx: f32, dy: f32| base + r * (dx * w) + Vec3::Y * (dy * w);
|
|
||||||
let metal = if e.level >= 8 {
|
|
||||||
Color::from_rgba(240, 200, 70, 255)
|
|
||||||
} else if e.level >= 5 {
|
|
||||||
Color::from_rgba(200, 206, 220, 255)
|
|
||||||
} else {
|
|
||||||
Color::from_rgba(190, 120, 60, 255)
|
|
||||||
};
|
|
||||||
if e.level >= 7 {
|
|
||||||
// cape behind the figure
|
|
||||||
let back = base - toward * 0.1;
|
|
||||||
draw_cube(back + Vec3::Y * (0.5 * w), vec3(0.42 * w, 0.55 * w, 0.42 * w), None, if e.hero { Color::from_rgba(40, 70, 150, 255) } else { Color::from_rgba(120, 20, 30, 255) });
|
|
||||||
}
|
|
||||||
for side in [-1.0f32, 1.0] {
|
|
||||||
draw_cube(at(side * 0.26, 0.6), Vec3::splat(0.09 * w + 0.02), None, metal);
|
|
||||||
}
|
|
||||||
if e.level >= 5 {
|
|
||||||
draw_cube(at(0.0, 0.93), vec3(0.2 * w, 0.06 * w, 0.2 * w), None, metal);
|
|
||||||
if e.hero {
|
|
||||||
draw_cube(at(0.0, 1.02), vec3(0.04, 0.1 * w, 0.04), None, Color::from_rgba(200, 40, 40, 255));
|
|
||||||
} else {
|
|
||||||
for side in [-1.0f32, 1.0] {
|
|
||||||
draw_cube(at(side * 0.16, 1.0), vec3(0.035, 0.09 * w, 0.035), None, Color::from_rgba(230, 220, 200, 255));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lv 9+: a glowing ring above the head, Lv 10 also a faint aura at the feet
|
// Lv 9+: a glowing ring above the head, Lv 10 also a faint aura at the feet
|
||||||
fn draw_gear_glow(g: &Game, cam: &Cam, t: f32) {
|
fn draw_gear_glow(g: &Game, cam: &Cam, t: f32) {
|
||||||
let toward = cam.eye_dir();
|
let toward = cam.eye_dir();
|
||||||
for e in g.ents.iter().filter(|e| gear_shown(e) && e.level >= 9) {
|
for e in g.ents.iter().filter(|e| gear_shown(e) && e.level >= 9) {
|
||||||
let w = sprite_size(e.kind);
|
let w = sprite_size(e.kind);
|
||||||
let base = vec3(e.x, 0.0, e.y) + toward * 0.5;
|
let lift = if flies(e.kind) { 0.45 } else { 0.0 };
|
||||||
|
let base = vec3(e.x, lift, e.y) + toward * 0.5;
|
||||||
let c = if e.hero { Color::new(0.8, 0.9, 1.0, 0.8) } else { Color::new(1.0, 0.8, 0.3, 0.8) };
|
let c = if e.hero { Color::new(0.8, 0.9, 1.0, 0.8) } else { Color::new(1.0, 0.8, 0.3, 0.8) };
|
||||||
let y = w * 1.12 + (t * 2.0 + e.anim).sin() * 0.02;
|
let y = w * 1.12 + (t * 2.0 + e.anim).sin() * 0.02;
|
||||||
for k in 0..8 {
|
for k in 0..8 {
|
||||||
@@ -1302,34 +1263,87 @@ fn draw_gear_glow(g: &Game, cam: &Cam, t: f32) {
|
|||||||
fn draw_towers_solid(g: &Game) {
|
fn draw_towers_solid(g: &Game) {
|
||||||
for tw in &g.towers {
|
for tw in &g.towers {
|
||||||
let (x, z) = tw.pos();
|
let (x, z) = tw.pos();
|
||||||
|
let c = vec3(x, 0.0, z);
|
||||||
let h = tower_height(tw);
|
let h = tower_height(tw);
|
||||||
let f = 0.75 + tw.flash * 1.5; // brightens briefly when firing
|
let f = 0.75 + tw.flash * 1.5; // brightens briefly when firing
|
||||||
// knight towers: pale stone with blue tones
|
// knight towers: pale stone with blue tones
|
||||||
let shade_c = |r: u8, g: u8, b: u8, f: f32| if tw.hero { shade_c(r / 3 + 110, g / 3 + 115, b / 3 + 135, f) } else { shade_c(r, g, b, f) };
|
let col = |r: u8, g: u8, b: u8| if tw.hero { shade_c(r / 3 + 110, g / 3 + 115, b / 3 + 135, f) } else { shade_c(r, g, b, f) };
|
||||||
|
let bx = |p: Vec3, s: Vec3, cl: Color| draw_cube(c + p, s, None, cl);
|
||||||
match tw.kind {
|
match tw.kind {
|
||||||
TowerKind::Arrow => {
|
TowerKind::Arrow => {
|
||||||
draw_cube(vec3(x, h * 0.5, z), vec3(0.7, h, 0.7), None, shade_c(110, 75, 45, f));
|
// stone plinth, masonry body with courses and arrow slits
|
||||||
draw_cube(vec3(x, h + 0.06, z), vec3(0.92, 0.12, 0.92), None, shade_c(130, 90, 55, f));
|
bx(vec3(0.0, 0.08, 0.0), vec3(0.86, 0.16, 0.86), col(86, 80, 76));
|
||||||
for (dx, dz) in [(-0.38, -0.38), (0.38, -0.38), (0.38, 0.38), (-0.38, 0.38)] {
|
bx(vec3(0.0, h * 0.5, 0.0), vec3(0.66, h, 0.66), col(118, 108, 98));
|
||||||
draw_cube(vec3(x + dx, h + 0.2, z + dz), vec3(0.15, 0.18, 0.15), None, shade_c(130, 90, 55, f));
|
let courses = (h / 0.22) as i32;
|
||||||
|
for k in 1..courses {
|
||||||
|
bx(vec3(0.0, k as f32 * 0.22, 0.0), vec3(0.68, 0.025, 0.68), col(80, 72, 66));
|
||||||
}
|
}
|
||||||
|
for (dx, dz) in [(0.0f32, 0.34f32), (0.34, 0.0), (0.0, -0.34), (-0.34, 0.0)] {
|
||||||
|
bx(vec3(dx, h * 0.62, dz), vec3(if dx == 0.0 { 0.06 } else { 0.02 }, 0.2, if dz == 0.0 { 0.06 } else { 0.02 }), col(24, 18, 16));
|
||||||
|
}
|
||||||
|
// corbels and the wooden platform
|
||||||
|
for (dx, dz) in [(-0.3f32, -0.3f32), (0.3, -0.3), (0.3, 0.3), (-0.3, 0.3)] {
|
||||||
|
bx(vec3(dx * 1.2, h - 0.08, dz * 1.2), vec3(0.12, 0.12, 0.12), col(96, 88, 80));
|
||||||
|
}
|
||||||
|
bx(vec3(0.0, h + 0.04, 0.0), vec3(0.94, 0.08, 0.94), col(120, 84, 50));
|
||||||
|
// battlements: merlons with gaps
|
||||||
|
for k in 0..4 {
|
||||||
|
let d = -0.39 + k as f32 * 0.26;
|
||||||
|
for (px, pz) in [(d, -0.42), (d, 0.42), (-0.42, d), (0.42, d)] {
|
||||||
|
bx(vec3(px, h + 0.17, pz), vec3(0.11, 0.18, 0.11), col(128, 118, 106));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// pennant and ladder
|
||||||
|
bx(vec3(0.3, h + 0.45, 0.3), vec3(0.03, 0.7, 0.03), col(140, 140, 150));
|
||||||
|
bx(vec3(0.42, h + 0.7, 0.3), vec3(0.22, 0.12, 0.02), if tw.hero { shade_c(60, 90, 180, f) } else { shade_c(170, 30, 40, f) });
|
||||||
|
for k in 0..((h / 0.15) as i32) {
|
||||||
|
bx(vec3(-0.2, 0.1 + k as f32 * 0.15, 0.35), vec3(0.2, 0.025, 0.03), col(110, 76, 44));
|
||||||
|
}
|
||||||
|
bx(vec3(-0.29, h * 0.5, 0.35), vec3(0.025, h, 0.03), col(100, 70, 40));
|
||||||
|
bx(vec3(-0.11, h * 0.5, 0.35), vec3(0.025, h, 0.03), col(100, 70, 40));
|
||||||
}
|
}
|
||||||
TowerKind::Cannon => {
|
TowerKind::Cannon => {
|
||||||
draw_cube(vec3(x, h * 0.5, z), vec3(0.88, h, 0.88), None, shade_c(100, 100, 110, f));
|
// squat round tower (stacked rings), iron bands, carriage, barrel, cannon balls
|
||||||
draw_cube(vec3(x, h + 0.08, z), vec3(0.5, 0.16, 0.5), None, shade_c(70, 70, 78, f));
|
|
||||||
let (ax, az) = (tw.aim.cos(), tw.aim.sin());
|
|
||||||
for k in 0..4 {
|
for k in 0..4 {
|
||||||
let d = 0.1 + k as f32 * 0.14;
|
let r = 0.92 - k as f32 * 0.03;
|
||||||
draw_cube(vec3(x + ax * d, h + 0.2, z + az * d), Vec3::splat(0.2 - k as f32 * 0.015), None, shade_c(40, 40, 46, f));
|
let y = h * (k as f32 + 0.5) / 4.0;
|
||||||
|
bx(vec3(0.0, y, 0.0), vec3(r, h / 4.0 + 0.01, r * 0.82), col(104, 104, 112));
|
||||||
|
bx(vec3(0.0, y, 0.0), vec3(r * 0.82, h / 4.0 + 0.01, r), col(96, 96, 104));
|
||||||
|
}
|
||||||
|
bx(vec3(0.0, h * 0.35, 0.0), vec3(0.94, 0.05, 0.94), col(54, 54, 60));
|
||||||
|
bx(vec3(0.0, h * 0.75, 0.0), vec3(0.9, 0.05, 0.9), col(54, 54, 60));
|
||||||
|
bx(vec3(0.0, h + 0.05, 0.0), vec3(0.62, 0.1, 0.62), col(80, 62, 44));
|
||||||
|
let (ax, az) = (tw.aim.cos(), tw.aim.sin());
|
||||||
|
// carriage cheeks beside the barrel
|
||||||
|
for side in [-1.0f32, 1.0] {
|
||||||
|
bx(vec3(-az * 0.14 * side, h + 0.17, ax * 0.14 * side), vec3(0.14, 0.16, 0.14), col(96, 70, 44));
|
||||||
|
}
|
||||||
|
for k in 0..5 {
|
||||||
|
let d = 0.0 + k as f32 * 0.13;
|
||||||
|
let r = 0.2 - k as f32 * 0.012;
|
||||||
|
bx(vec3(ax * d, h + 0.22, az * d), Vec3::splat(r), col(40, 40, 46));
|
||||||
|
}
|
||||||
|
bx(vec3(ax * 0.58, h + 0.22, az * 0.58), Vec3::splat(0.2), col(70, 70, 78));
|
||||||
|
for (dx, dz, y) in [(0.3f32, -0.28f32, 0.0f32), (0.38, -0.2, 0.0), (0.34, -0.24, 0.08)] {
|
||||||
|
bx(vec3(dx, h + 0.15 + y, dz), Vec3::splat(0.07), col(30, 30, 34));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TowerKind::Magic => {
|
TowerKind::Magic => {
|
||||||
draw_cube(vec3(x, h * 0.5, z), vec3(0.42, h, 0.42), None, shade_c(84, 74, 104, f));
|
// rune-cut obelisk on a stepped base with buttresses (crystal and runes glow)
|
||||||
draw_cube(vec3(x, 0.1, z), vec3(0.75, 0.2, 0.75), None, shade_c(70, 62, 86, f));
|
bx(vec3(0.0, 0.06, 0.0), vec3(0.82, 0.12, 0.82), col(62, 56, 76));
|
||||||
draw_cube(vec3(x, h, z), vec3(0.6, 0.1, 0.6), None, shade_c(110, 96, 130, f));
|
bx(vec3(0.0, 0.17, 0.0), vec3(0.62, 0.1, 0.62), col(72, 64, 90));
|
||||||
|
bx(vec3(0.0, h * 0.5, 0.0), vec3(0.4, h, 0.4), col(84, 74, 104));
|
||||||
|
bx(vec3(0.0, h * 0.75, 0.0), vec3(0.32, h * 0.5, 0.46), col(92, 82, 114));
|
||||||
|
for (dx, dz) in [(0.25f32, 0.0f32), (-0.25, 0.0), (0.0, 0.25), (0.0, -0.25)] {
|
||||||
|
bx(vec3(dx, h * 0.22, dz), vec3(if dx == 0.0 { 0.12 } else { 0.1 }, h * 0.44, if dz == 0.0 { 0.12 } else { 0.1 }), col(70, 62, 88));
|
||||||
|
}
|
||||||
|
bx(vec3(0.0, h, 0.0), vec3(0.58, 0.08, 0.58), col(110, 96, 130));
|
||||||
|
for (dx, dz) in [(-0.24f32, -0.24f32), (0.24, -0.24), (0.24, 0.24), (-0.24, 0.24)] {
|
||||||
|
bx(vec3(dx, h + 0.12, dz), vec3(0.06, 0.18, 0.06), col(120, 104, 140));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// level pips
|
}
|
||||||
|
// level pips on the plinth
|
||||||
for k in 0..tw.level {
|
for k in 0..tw.level {
|
||||||
draw_cube(vec3(x - 0.2 + k as f32 * 0.2, 0.25, z + 0.46), Vec3::splat(0.09), None, Color::from_rgba(240, 200, 70, 255));
|
draw_cube(vec3(x - 0.2 + k as f32 * 0.2, 0.25, z + 0.46), Vec3::splat(0.09), None, Color::from_rgba(240, 200, 70, 255));
|
||||||
}
|
}
|
||||||
@@ -1346,6 +1360,15 @@ fn draw_towers_glow(g: &Game, t: f32) {
|
|||||||
let pulse = 1.0 + (t * 4.0 + k as f32).sin() * 0.1 + tw.flash * 2.0;
|
let pulse = 1.0 + (t * 4.0 + k as f32).sin() * 0.1 + tw.flash * 2.0;
|
||||||
draw_sphere(c, 0.16 * pulse, None, Color::new(0.85, 0.5, 1.0, 0.95));
|
draw_sphere(c, 0.16 * pulse, None, Color::new(0.85, 0.5, 1.0, 0.95));
|
||||||
draw_sphere(c, 0.3 * pulse, None, Color::new(0.7, 0.3, 1.0, 0.25));
|
draw_sphere(c, 0.3 * pulse, None, Color::new(0.7, 0.3, 1.0, 0.25));
|
||||||
|
// runes on the four faces of the obelisk, glowing one after another
|
||||||
|
let h = tower_height(tw);
|
||||||
|
for r in 0..3 {
|
||||||
|
let y = h * (0.35 + r as f32 * 0.22);
|
||||||
|
let glow = 0.4 + 0.6 * ((t * 2.5 - r as f32 * 0.8 + k as f32).sin() * 0.5 + 0.5);
|
||||||
|
for (dx, dz) in [(0.21f32, 0.0f32), (-0.21, 0.0), (0.0, 0.21), (0.0, -0.21)] {
|
||||||
|
draw_cube(vec3(x + dx, y, z + dz), vec3(if dx == 0.0 { 0.1 } else { 0.02 }, 0.08, if dz == 0.0 { 0.1 } else { 0.02 }), None, Color::new(0.8, 0.45, 1.0, glow));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1419,10 +1442,33 @@ fn draw_landmarks_solid(g: &Game, t: f32) {
|
|||||||
let (gx, gz) = g.gate_center();
|
let (gx, gz) = g.gate_center();
|
||||||
if g.gate_hp > 0.0 {
|
if g.gate_hp > 0.0 {
|
||||||
let stone = Color::from_rgba(150, 155, 172, 255);
|
let stone = Color::from_rgba(150, 155, 172, 255);
|
||||||
draw_cube(vec3(gx - 1.1, 1.0, gz), vec3(0.45, 2.0, 0.45), None, stone);
|
let dark = Color::from_rgba(112, 116, 132, 255);
|
||||||
draw_cube(vec3(gx + 1.1, 1.0, gz), vec3(0.45, 2.0, 0.45), None, stone);
|
let gold = Color::from_rgba(230, 200, 100, 255);
|
||||||
draw_cube(vec3(gx, 2.1, gz), vec3(2.8, 0.35, 0.55), None, stone);
|
let iron = Color::from_rgba(56, 58, 66, 255);
|
||||||
draw_cube(vec3(gx, 2.35, gz), vec3(0.5, 0.2, 0.5), None, Color::from_rgba(230, 200, 100, 255));
|
for side in [-1.1f32, 1.1] {
|
||||||
|
draw_cube(vec3(gx + side, 0.12, gz), vec3(0.6, 0.24, 0.6), None, dark); // plinth
|
||||||
|
draw_cube(vec3(gx + side, 1.0, gz), vec3(0.45, 1.8, 0.45), None, stone);
|
||||||
|
for y in [0.6f32, 1.2] {
|
||||||
|
draw_cube(vec3(gx + side, y, gz), vec3(0.48, 0.05, 0.48), None, dark); // bands
|
||||||
|
}
|
||||||
|
draw_cube(vec3(gx + side, 1.92, gz), vec3(0.56, 0.12, 0.56), None, dark); // capital
|
||||||
|
// banner of the order: white with a golden sun
|
||||||
|
draw_cube(vec3(gx + side, 1.35, gz + 0.25), vec3(0.32, 0.7, 0.02), None, Color::from_rgba(232, 234, 242, 255));
|
||||||
|
draw_cube(vec3(gx + side, 1.45, gz + 0.265), vec3(0.14, 0.14, 0.01), None, gold);
|
||||||
|
draw_cube(vec3(gx + side, 1.02, gz + 0.26), vec3(0.32, 0.05, 0.02), None, Color::from_rgba(60, 90, 170, 255));
|
||||||
|
}
|
||||||
|
// stepped arch with a keystone
|
||||||
|
draw_cube(vec3(gx, 2.1, gz), vec3(2.8, 0.3, 0.55), None, stone);
|
||||||
|
draw_cube(vec3(gx, 1.88, gz), vec3(1.7, 0.14, 0.5), None, dark);
|
||||||
|
draw_cube(vec3(gx, 2.32, gz), vec3(2.2, 0.14, 0.5), None, dark);
|
||||||
|
draw_cube(vec3(gx, 2.2, gz), vec3(0.34, 0.5, 0.58), None, gold);
|
||||||
|
// portcullis (iron bars) in the opening, half raised
|
||||||
|
for k in 0..5 {
|
||||||
|
draw_cube(vec3(gx - 0.6 + k as f32 * 0.3, 1.45, gz), vec3(0.05, 0.85, 0.05), None, iron);
|
||||||
|
}
|
||||||
|
for y in [1.15f32, 1.5, 1.8] {
|
||||||
|
draw_cube(vec3(gx, y, gz), vec3(1.7, 0.05, 0.05), None, iron);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for k in 0..10 {
|
for k in 0..10 {
|
||||||
let a = k as f32 * 1.7;
|
let a = k as f32 * 1.7;
|
||||||
|
|||||||
Reference in new issue
Block a user