Rank badges and level equipment; no edge scrolling before the mouse has moved

- Military rank badges instead of level numbers: Lv 2-4 bronze chevrons, Lv 5-7 silver bars,
  Lv 8-9 gold stars, Lv 10 gold star in a wreath; rank names in the unit info (EN/DE)
- Equipment grows with the level: shoulder pieces (bronze/silver/gold), helmets with horns
  (creatures) or plumes (heroes), capes from Lv 7, glowing ring from Lv 9, aura at Lv 10
- Fix: before the first mouse move the pointer counts as (0, 0) and the edge scrolling
  moved the camera at the start and after loading a savegame

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 12:47:34 +01:00
1 parent 52d49b0243
commit 729dd4fe4c
6 files changed
+170 -5

No files matched your search

+79 -3
View File
@@ -1020,6 +1020,7 @@ impl R3d {
}
draw_landmarks_solid(g, t);
draw_towers_solid(g);
draw_gear(g, cam);
crate::props::draw_props_solid(g, cam, t);
// ---- unlit glow / overlays
@@ -1052,6 +1053,7 @@ impl R3d {
// translucent glows and effects last (they write depth, see glow_material)
draw_landmarks_glow(g, t);
draw_towers_glow(g, t);
draw_gear_glow(g, cam, t);
crate::props::draw_props_glow(g, cam, t);
for f in &g.fx {
draw_fx(f, t);
@@ -1176,9 +1178,11 @@ impl R3d {
if e.guard {
gfx.glyph(0x0F, head.x - (GW * small * 0.5).floor(), head.y - 2.0 * GH * small, small, Color::from_rgba(255, 220, 90, 255));
}
if e.level > 1 && px_per_tile >= 24.0 * F() as f32 {
let x = head.x - (cols as f32 * cw * 0.5).floor() - 2.0 * cw;
gfx.text_sh(&e.level.to_string(), x, head.y - GH * small, small, Color::from_rgba(255, 220, 90, 255));
// rank badge left of the health bar
if e.level > 1 && px_per_tile >= 20.0 * F() as f32 {
let k = 2.0 * F() as f32;
let x = head.x - (cols as f32 * cw * 0.5).floor() - 12.0 * k;
gfx.badge(x, head.y - GH * small - 4.0 * k, e.level, k);
}
if matches!(e.job, Job::Sleep(_)) && !e.moving {
let k = (t * 1.5 + e.anim) % 1.0;
@@ -1216,6 +1220,78 @@ fn shade_c(r: u8, g: u8, b: u8, f: f32) -> Color {
Color::new(r as f32 / 255.0 * f, g as f32 / 255.0 * f, b as f32 / 255.0 * f, 1.0)
}
// size of a unit's sprite (same as in the billboards)
fn sprite_size(k: EK) -> f32 {
if k == EK::Chainbreaker {
1.0
} else if matches!(k, EK::Ogre | EK::Paladin | EK::Jailer) {
0.8
} else {
0.62
}
}
fn gear_shown(e: &Ent) -> bool {
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 bob = if e.moving { (e.anim * 12.0).sin().abs() * 0.06 } else { 0.0 };
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
fn draw_gear_glow(g: &Game, cam: &Cam, t: f32) {
let toward = cam.eye_dir();
for e in g.ents.iter().filter(|e| gear_shown(e) && e.level >= 9) {
let w = sprite_size(e.kind);
let base = vec3(e.x, 0.0, 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 y = w * 1.12 + (t * 2.0 + e.anim).sin() * 0.02;
for k in 0..8 {
let a0 = k as f32 * 0.785 + t;
let a1 = a0 + 0.785;
line3(base + vec3(a0.cos() * 0.12, y, a0.sin() * 0.12), base + vec3(a1.cos() * 0.12, y, a1.sin() * 0.12), 0.025, c);
}
if e.level >= 10 {
disc(vec3(e.x, 0.015, e.y), 0.42 + 0.03 * (t * 3.0).sin(), Color::new(c.r, c.g, c.b, 0.18), 16);
}
}
}
fn draw_towers_solid(g: &Game) {
for tw in &g.towers {
let (x, z) = tw.pos();