Eight new spells: seer's eye, cave-in, haste, stoneskin, gold rain, invisibility, plague, quake

- researched in the library (and unlocked in the campaign), in the spells menu with own icons and a radius under the pointer
- haste speeds up everything a creature does, stoneskin halves damage, invisible creatures are ignored by enemies and towers until they attack (drawn see-through), plague spreads among enemies, quake hurts and stuns enemies only, cave-in turns floor back into earth
- particles for every spell

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-04 07:07:52 +01:00
1 parent a54316c86e
commit 7f4d5ca7b4
11 files changed
+532 -12

No files matched your search

+94
View File
@@ -72,6 +72,7 @@ pub const T_RIVAL2: u32 = 139; // 4 variants: floor of the blue master
pub const I_RUNE: u32 = 143; // rune forge tower
pub const T_FORGE: u32 = 144; // 4 variants: sooty flagstones with glowing cracks
pub const I_TRAPS: u32 = 148; // 10 icons in TRAP_KINDS order
pub const I_SPELLS: u32 = 158; // 8 icons in SPELLS order
pub struct Gfx {
pub atlas: Texture2D,
@@ -863,6 +864,99 @@ fn gen_atlas() -> Image {
for (k, f) in icons.iter().enumerate() {
cv.cell(I_TRAPS + k as u32, |x, y| f(x, y));
}
// spell icons
let spells: [fn(i32, i32) -> Option<Color>; 8] = [
// seer's eye
|x, y| {
let (dx, dy) = ((x - 8) as f32, (y - 8) as f32 * 1.8);
let d = (dx * dx + dy * dy).sqrt();
if d < 2.5 {
Some(c(40, 10, 60))
} else if d < 4.5 {
Some(c(200, 120, 255))
} else if d < 7.0 {
Some(c(235, 225, 245))
} else {
None
}
},
// cave-in
|x, y| {
if y >= 9 && (x + y * 3) % 5 != 0 {
Some(c(110, 80, 56))
} else if y < 9 && ((x * 7 + y * 5) % 9 == 0) {
Some(c(140, 110, 80))
} else if y < 3 {
Some(c(70, 60, 56))
} else {
None
}
},
// haste
|x, y| {
let bolt = (y < 8 && (x - (12 - y / 2)).abs() <= 1) || (y >= 8 && (x - (10 - (y - 8) / 2) + 4).abs() <= 1) || (y == 8 && (5..=11).contains(&x));
if bolt {
Some(c(255, 230, 90))
} else if (y == 4 || y == 11) && x < 4 {
Some(c(255, 200, 80))
} else {
None
}
},
// stoneskin
|x, y| {
if (3..=12).contains(&x) && (2..=13).contains(&y) && !(y > 10 && (x - 8).abs() + (y - 10) > 5) {
Some(if (x + y) % 4 == 0 { c(110, 110, 104) } else { c(170, 168, 160) })
} else {
None
}
},
// gold rain
|x, y| {
if y % 4 == (x / 3) % 4 && x % 3 != 2 {
Some(c(255, 210, 60))
} else {
None
}
},
// invisibility
|x, y| {
let d = (x - 8).pow(2) + (y - 7).pow(2);
if (d <= 20 && (x + y) % 2 == 0) || (y >= 11 && (x - 8).abs() <= 4 && (x + y) % 2 == 0) {
Some(c(200, 220, 255))
} else {
None
}
},
// plague
|x, y| {
let d = |cx: i32, cy: i32, r: i32| (x - cx).pow(2) + (y - cy).pow(2) <= r * r;
if d(8, 8, 5) && !d(8, 8, 2) {
Some(c(140, 210, 60))
} else if d(8, 8, 1) || d(3, 4, 1) || d(13, 12, 1) {
Some(c(90, 150, 40))
} else {
None
}
},
// quake
|x, y| {
if y >= 7 && y <= 9 && (x + (y - 8)) % 4 != 0 {
Some(c(140, 110, 80))
} else if (x - 8 + (y - 8).abs() * 0).abs() <= 0 && y > 9 {
Some(c(60, 40, 30))
} else if y > 9 && (x + y) % 3 == 0 {
Some(c(100, 76, 56))
} else if y < 6 && (x * 5 + y) % 7 == 0 {
Some(c(170, 140, 100))
} else {
None
}
},
];
for (k, f) in spells.iter().enumerate() {
cv.cell(I_SPELLS + k as u32, |x, y| f(x, y));
}
// aerie: straw and twigs on stone, a few feathers
for v in 0..4u32 {
cv.cell(T_AERIE + v, |x, y| {