Compare commits
2
Commits
f8eda88bb3
...
22cc4d9446
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22cc4d9446 | ||
|
|
fdce6c282b |
No files matched your search
+41
-31
@@ -15,40 +15,40 @@ pub const STAT_NAMES: [&str; k::COUNTERS] = [
|
|||||||
"heroes", "bosses", "lost", "dug", "wood", "walls", "rooms", "spells", "slaps", "traps", "towers", "drowned", "burnt", "siege", "captured", "gold", "drawbridge",
|
"heroes", "bosses", "lost", "dug", "wood", "walls", "rooms", "spells", "slaps", "traps", "towers", "drowned", "burnt", "siege", "captured", "gold", "drawbridge",
|
||||||
];
|
];
|
||||||
|
|
||||||
// tiered achievements: id (= counter), thresholds, icon cell
|
// tiered achievements: id (= counter), thresholds, badge (badges.rs)
|
||||||
pub const TIERS: [(&str, &[f64], u32); 20] = [
|
pub const TIERS: [(&str, &[f64], u32); 20] = [
|
||||||
("heroes", &[1.0, 50.0, 250.0, 1000.0], crate::gfx::I_ARROW),
|
("heroes", &[1.0, 50.0, 250.0, 1000.0], crate::badges::HEROES),
|
||||||
("bosses", &[1.0, 5.0, 20.0], crate::gfx::I_RUNE),
|
("bosses", &[1.0, 5.0, 20.0], crate::badges::BOSSES),
|
||||||
("dug", &[100.0, 1000.0, 10000.0], crate::gfx::I_PICK),
|
("dug", &[100.0, 1000.0, 10000.0], crate::badges::DUG),
|
||||||
("gold", &[10000.0, 100000.0, 1000000.0], crate::gfx::I_SELL),
|
("gold", &[10000.0, 100000.0, 1000000.0], crate::badges::GOLD),
|
||||||
("wood", &[50.0, 500.0, 2000.0], crate::gfx::I_BRIDGE),
|
("wood", &[50.0, 500.0, 2000.0], crate::badges::WOOD),
|
||||||
("walls", &[10.0, 100.0], crate::gfx::I_WALL),
|
("walls", &[10.0, 100.0], crate::badges::WALLS),
|
||||||
("rooms", &[50.0, 500.0, 2000.0], crate::gfx::I_STEEL),
|
("rooms", &[50.0, 500.0, 2000.0], crate::badges::ROOMS),
|
||||||
("spells", &[10.0, 100.0, 500.0], crate::gfx::I_BOLT),
|
("spells", &[10.0, 100.0, 500.0], crate::badges::SPELLS),
|
||||||
("slaps", &[10.0, 100.0, 1000.0], crate::gfx::I_HAND),
|
("slaps", &[10.0, 100.0, 1000.0], crate::badges::SLAPS),
|
||||||
("traps", &[5.0, 50.0, 200.0], crate::gfx::I_TRAPS),
|
("traps", &[5.0, 50.0, 200.0], crate::badges::TRAPS),
|
||||||
("towers", &[5.0, 50.0], crate::gfx::I_CANNON),
|
("towers", &[5.0, 50.0], crate::badges::TOWERS),
|
||||||
("drowned", &[1.0, 25.0, 100.0], crate::gfx::I_FREEZE),
|
("drowned", &[1.0, 25.0, 100.0], crate::badges::DROWNED),
|
||||||
("burnt", &[5.0, 50.0], crate::gfx::I_DRAWBRIDGE),
|
("burnt", &[5.0, 50.0], crate::badges::BURNT),
|
||||||
("siege", &[1.0, 10.0], crate::gfx::I_CATAPULT),
|
("siege", &[1.0, 10.0], crate::badges::SIEGE),
|
||||||
("captured", &[1.0, 25.0, 100.0], crate::gfx::I_SUMMON),
|
("captured", &[1.0, 25.0, 100.0], crate::badges::CAPTURED),
|
||||||
("drawbridge", &[1.0, 50.0], crate::gfx::I_DRAWBRIDGE),
|
("drawbridge", &[1.0, 50.0], crate::badges::DRAWBRIDGE),
|
||||||
("wins", &[1.0, 10.0, 50.0], crate::gfx::I_FLAG),
|
("wins", &[1.0, 10.0, 50.0], crate::badges::WINS),
|
||||||
("missions", &[1.0, 10.0, 30.0], crate::gfx::I_MAGIC),
|
("missions", &[1.0, 10.0, 30.0], crate::badges::MISSIONS),
|
||||||
("coop", &[1.0, 10.0, 20.0], crate::gfx::I_HEAL),
|
("coop", &[1.0, 10.0, 20.0], crate::badges::COOP),
|
||||||
("minutes", &[60.0, 600.0, 3000.0], crate::gfx::I_SPELLS),
|
("minutes", &[60.0, 600.0, 3000.0], crate::badges::MINUTES),
|
||||||
];
|
];
|
||||||
|
|
||||||
// special achievements: id, hidden until unlocked, icon cell
|
// special achievements: id, hidden until unlocked, badge (badges.rs)
|
||||||
pub const SPECIAL: [(&str, bool, u32); 8] = [
|
pub const SPECIAL: [(&str, bool, u32); 8] = [
|
||||||
("flawless", false, crate::gfx::I_HEAL),
|
("flawless", false, crate::badges::FLAWLESS),
|
||||||
("speedrun", false, crate::gfx::I_BOLT),
|
("speedrun", false, crate::badges::SPEEDRUN),
|
||||||
("campaign", false, crate::gfx::I_RUNE),
|
("campaign", false, crate::badges::CAMPAIGN),
|
||||||
("surface", false, crate::gfx::I_WALL),
|
("surface", false, crate::badges::SURFACE),
|
||||||
("defeat", true, crate::gfx::I_SELL),
|
("defeat", true, crate::badges::DEFEAT),
|
||||||
("legend", false, crate::gfx::I_ARROW),
|
("legend", false, crate::badges::LEGEND),
|
||||||
("wyrm", true, crate::gfx::I_SUMMON),
|
("wyrm", true, crate::badges::WYRM),
|
||||||
("fullhand", true, crate::gfx::I_HAND),
|
("fullhand", true, crate::badges::FULLHAND),
|
||||||
];
|
];
|
||||||
|
|
||||||
// number of achievements in all
|
// number of achievements in all
|
||||||
@@ -244,7 +244,7 @@ pub fn fmt_count(v: f64) -> String {
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
// every achievement in display order: (id, hidden, icon, tier 0..)
|
// every achievement in display order: (id, hidden, badge, tier 0..)
|
||||||
pub fn all() -> Vec<(String, bool, u32, usize)> {
|
pub fn all() -> Vec<(String, bool, u32, usize)> {
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
for (id, steps, icon) in TIERS.iter() {
|
for (id, steps, icon) in TIERS.iter() {
|
||||||
@@ -268,6 +268,16 @@ mod tests {
|
|||||||
assert_eq!(all().len(), count());
|
assert_eq!(all().len(), count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn every_achievement_has_its_own_badge() {
|
||||||
|
let mut seen = std::collections::BTreeSet::new();
|
||||||
|
for b in TIERS.iter().map(|t| t.2).chain(SPECIAL.iter().map(|s| s.2)) {
|
||||||
|
assert!(b < crate::badges::COUNT, "badge {} out of range", b);
|
||||||
|
assert!(seen.insert(b), "badge {} used twice", b);
|
||||||
|
}
|
||||||
|
assert_eq!(seen.len() as u32, crate::badges::COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn the_tracker_counts_a_game_and_unlocks() {
|
fn the_tracker_counts_a_game_and_unlocks() {
|
||||||
let mut g = Game::new(3, 64, 64);
|
let mut g = Game::new(3, 64, 64);
|
||||||
|
|||||||
+929
@@ -0,0 +1,929 @@
|
|||||||
|
// Achievement badges: one 32x32 pixel-art symbol per achievement, generated at start into a
|
||||||
|
// texture of its own (the main atlas is full). Each symbol is painted from simple shapes in
|
||||||
|
// materials (steel, gold, wood, ...); every material has a ramp of four shades, and the shapes
|
||||||
|
// are bevelled automatically (lit from the top left) and get a dark outline.
|
||||||
|
|
||||||
|
use macroquad::prelude::*;
|
||||||
|
|
||||||
|
pub const SIZE: u32 = 32;
|
||||||
|
const COLS: u32 = 8;
|
||||||
|
|
||||||
|
// badge indices (the icon field of achievements::TIERS / SPECIAL)
|
||||||
|
pub const HEROES: u32 = 0;
|
||||||
|
pub const BOSSES: u32 = 1;
|
||||||
|
pub const DUG: u32 = 2;
|
||||||
|
pub const GOLD: u32 = 3;
|
||||||
|
pub const WOOD: u32 = 4;
|
||||||
|
pub const WALLS: u32 = 5;
|
||||||
|
pub const ROOMS: u32 = 6;
|
||||||
|
pub const SPELLS: u32 = 7;
|
||||||
|
pub const SLAPS: u32 = 8;
|
||||||
|
pub const TRAPS: u32 = 9;
|
||||||
|
pub const TOWERS: u32 = 10;
|
||||||
|
pub const DROWNED: u32 = 11;
|
||||||
|
pub const BURNT: u32 = 12;
|
||||||
|
pub const SIEGE: u32 = 13;
|
||||||
|
pub const CAPTURED: u32 = 14;
|
||||||
|
pub const DRAWBRIDGE: u32 = 15;
|
||||||
|
pub const WINS: u32 = 16;
|
||||||
|
pub const MISSIONS: u32 = 17;
|
||||||
|
pub const COOP: u32 = 18;
|
||||||
|
pub const MINUTES: u32 = 19;
|
||||||
|
pub const FLAWLESS: u32 = 20;
|
||||||
|
pub const SPEEDRUN: u32 = 21;
|
||||||
|
pub const CAMPAIGN: u32 = 22;
|
||||||
|
pub const SURFACE: u32 = 23;
|
||||||
|
pub const DEFEAT: u32 = 24;
|
||||||
|
pub const LEGEND: u32 = 25;
|
||||||
|
pub const WYRM: u32 = 26;
|
||||||
|
pub const FULLHAND: u32 = 27;
|
||||||
|
pub const COUNT: u32 = 28;
|
||||||
|
|
||||||
|
// the source rectangle of a badge in the badge texture
|
||||||
|
pub fn badge_rect(i: u32) -> Rect {
|
||||||
|
Rect::new(((i % COLS) * SIZE) as f32, ((i / COLS) * SIZE) as f32, SIZE as f32, SIZE as f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ materials
|
||||||
|
|
||||||
|
const EMPTY: u8 = 0;
|
||||||
|
const STEEL: u8 = 1;
|
||||||
|
const GOLDM: u8 = 2;
|
||||||
|
const WOODM: u8 = 3;
|
||||||
|
const STONE: u8 = 4;
|
||||||
|
const RED: u8 = 5;
|
||||||
|
const FIRE: u8 = 6;
|
||||||
|
const WATER: u8 = 7;
|
||||||
|
const CRYSTAL: u8 = 8;
|
||||||
|
const GREEN: u8 = 9;
|
||||||
|
const BONE: u8 = 10;
|
||||||
|
const PURPLE: u8 = 11;
|
||||||
|
const IRON: u8 = 12; // dark iron, also slits and holes
|
||||||
|
const BRICK: u8 = 13;
|
||||||
|
const SKIN: u8 = 14; // imp green (the keeper's hand)
|
||||||
|
const DKWOOD: u8 = 15;
|
||||||
|
const GLASS: u8 = 16;
|
||||||
|
const CYAN: u8 = 17; // magic glow
|
||||||
|
const SUN: u8 = 18;
|
||||||
|
const BLUE: u8 = 19;
|
||||||
|
const SPARK: u8 = 20; // white sparkles and motion lines, no outline
|
||||||
|
const EMBER: u8 = 21; // floating embers and steam, no outline
|
||||||
|
const TEA: u8 = 22;
|
||||||
|
|
||||||
|
fn ramp(m: u8) -> [(u8, u8, u8); 4] {
|
||||||
|
match m {
|
||||||
|
STEEL => [(66, 72, 90), (116, 124, 144), (166, 174, 190), (222, 228, 238)],
|
||||||
|
GOLDM => [(124, 76, 22), (196, 138, 38), (238, 192, 70), (255, 240, 150)],
|
||||||
|
WOODM => [(78, 46, 26), (120, 76, 42), (156, 106, 62), (196, 146, 92)],
|
||||||
|
STONE => [(58, 54, 64), (90, 84, 96), (124, 118, 130), (160, 154, 166)],
|
||||||
|
RED => [(96, 18, 24), (156, 34, 36), (204, 62, 50), (240, 122, 92)],
|
||||||
|
FIRE => [(196, 54, 18), (240, 124, 30), (255, 192, 60), (255, 244, 170)],
|
||||||
|
WATER => [(22, 44, 96), (38, 84, 148), (66, 134, 198), (150, 200, 240)],
|
||||||
|
CRYSTAL => [(60, 34, 120), (110, 66, 196), (164, 120, 244), (226, 206, 255)],
|
||||||
|
GREEN => [(34, 66, 30), (58, 108, 42), (98, 150, 58), (152, 202, 92)],
|
||||||
|
BONE => [(120, 104, 84), (178, 160, 128), (216, 202, 170), (244, 236, 214)],
|
||||||
|
PURPLE => [(50, 26, 60), (88, 42, 100), (128, 68, 140), (176, 110, 182)],
|
||||||
|
IRON => [(22, 18, 24), (40, 34, 42), (62, 54, 64), (88, 80, 90)],
|
||||||
|
BRICK => [(88, 36, 30), (134, 60, 44), (170, 88, 62), (204, 126, 90)],
|
||||||
|
SKIN => [(56, 84, 44), (98, 138, 72), (140, 184, 102), (184, 222, 140)],
|
||||||
|
DKWOOD => [(50, 30, 20), (78, 48, 30), (104, 68, 42), (134, 94, 60)],
|
||||||
|
GLASS => [(92, 128, 150), (136, 176, 196), (180, 214, 228), (232, 246, 252)],
|
||||||
|
CYAN => [(30, 110, 150), (60, 180, 220), (130, 230, 250), (220, 255, 255)],
|
||||||
|
SUN => [(220, 120, 30), (250, 180, 50), (255, 222, 90), (255, 250, 200)],
|
||||||
|
BLUE => [(24, 30, 80), (40, 52, 128), (66, 84, 172), (110, 132, 214)],
|
||||||
|
SPARK => [(200, 200, 210), (236, 236, 244), (255, 255, 255), (255, 255, 255)],
|
||||||
|
EMBER => [(200, 80, 30), (255, 150, 50), (255, 210, 110), (255, 250, 220)],
|
||||||
|
TEA => [(70, 36, 20), (104, 58, 30), (134, 82, 44), (170, 112, 66)],
|
||||||
|
_ => [(0, 0, 0); 4],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outlined(m: u8) -> bool {
|
||||||
|
m != EMPTY && m != SPARK && m != EMBER
|
||||||
|
}
|
||||||
|
|
||||||
|
const OUTLINE: (u8, u8, u8) = (18, 12, 16);
|
||||||
|
const AUTO: u8 = 255;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct P {
|
||||||
|
m: u8,
|
||||||
|
s: u8, // shade 0..3, or AUTO (bevelled)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn a(m: u8) -> P {
|
||||||
|
P { m, s: AUTO }
|
||||||
|
}
|
||||||
|
fn f(m: u8, s: u8) -> P {
|
||||||
|
P { m, s }
|
||||||
|
}
|
||||||
|
const CUT: P = P { m: EMPTY, s: AUTO };
|
||||||
|
|
||||||
|
// one badge being painted
|
||||||
|
struct Pad {
|
||||||
|
m: [u8; 1024],
|
||||||
|
s: [u8; 1024],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pad {
|
||||||
|
fn new() -> Pad {
|
||||||
|
Pad { m: [EMPTY; 1024], s: [AUTO; 1024] }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn px(&mut self, x: i32, y: i32, p: P) {
|
||||||
|
if (0..32).contains(&x) && (0..32).contains(&y) {
|
||||||
|
let i = (y * 32 + x) as usize;
|
||||||
|
self.m[i] = p.m;
|
||||||
|
self.s[i] = p.s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, x: i32, y: i32) -> u8 {
|
||||||
|
if (0..32).contains(&x) && (0..32).contains(&y) {
|
||||||
|
self.m[(y * 32 + x) as usize]
|
||||||
|
} else {
|
||||||
|
EMPTY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// every pixel whose centre passes the test
|
||||||
|
fn fill(&mut self, p: P, test: impl Fn(f32, f32) -> bool) {
|
||||||
|
for y in 0..32 {
|
||||||
|
for x in 0..32 {
|
||||||
|
if test(x as f32 + 0.5, y as f32 + 0.5) {
|
||||||
|
self.px(x, y, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// inclusive pixel rectangle
|
||||||
|
fn rect(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, p: P) {
|
||||||
|
for y in y0..=y1 {
|
||||||
|
for x in x0..=x1 {
|
||||||
|
self.px(x, y, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disc(&mut self, cx: f32, cy: f32, r: f32, p: P) {
|
||||||
|
self.fill(p, |x, y| (x - cx).powi(2) + (y - cy).powi(2) <= r * r);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ring(&mut self, cx: f32, cy: f32, r0: f32, r1: f32, p: P) {
|
||||||
|
self.fill(p, |x, y| {
|
||||||
|
let d = (x - cx).powi(2) + (y - cy).powi(2);
|
||||||
|
d <= r1 * r1 && d >= r0 * r0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ellipse turned by `ang` (radians)
|
||||||
|
fn oval(&mut self, cx: f32, cy: f32, rx: f32, ry: f32, ang: f32, p: P) {
|
||||||
|
let (s, c) = ang.sin_cos();
|
||||||
|
self.fill(p, |x, y| {
|
||||||
|
let (dx, dy) = (x - cx, y - cy);
|
||||||
|
let u = dx * c + dy * s;
|
||||||
|
let v = -dx * s + dy * c;
|
||||||
|
(u / rx).powi(2) + (v / ry).powi(2) <= 1.0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// a thick line (round ends)
|
||||||
|
fn line(&mut self, x0: f32, y0: f32, x1: f32, y1: f32, w: f32, p: P) {
|
||||||
|
let (dx, dy) = (x1 - x0, y1 - y0);
|
||||||
|
let len2 = (dx * dx + dy * dy).max(1e-6);
|
||||||
|
let r = w * 0.5;
|
||||||
|
self.fill(p, |x, y| {
|
||||||
|
let t = (((x - x0) * dx + (y - y0) * dy) / len2).clamp(0.0, 1.0);
|
||||||
|
let (qx, qy) = (x0 + t * dx - x, y0 + t * dy - y);
|
||||||
|
qx * qx + qy * qy <= r * r
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// a line through several points
|
||||||
|
fn path(&mut self, pts: &[(f32, f32)], w: f32, p: P) {
|
||||||
|
for s in pts.windows(2) {
|
||||||
|
self.line(s[0].0, s[0].1, s[1].0, s[1].1, w, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poly(&mut self, pts: &[(f32, f32)], p: P) {
|
||||||
|
self.fill(p, |x, y| {
|
||||||
|
let mut inside = false;
|
||||||
|
let n = pts.len();
|
||||||
|
let mut j = n - 1;
|
||||||
|
for i in 0..n {
|
||||||
|
let (xi, yi) = pts[i];
|
||||||
|
let (xj, yj) = pts[j];
|
||||||
|
if (yi > y) != (yj > y) && x < (xj - xi) * (y - yi) / (yj - yi) + xi {
|
||||||
|
inside = !inside;
|
||||||
|
}
|
||||||
|
j = i;
|
||||||
|
}
|
||||||
|
inside
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// a five-pointed star
|
||||||
|
fn star(&mut self, cx: f32, cy: f32, r0: f32, r1: f32, p: P) {
|
||||||
|
let pts: Vec<(f32, f32)> = (0..10)
|
||||||
|
.map(|k| {
|
||||||
|
let ang = -std::f32::consts::FRAC_PI_2 + k as f32 * std::f32::consts::PI / 5.0;
|
||||||
|
let r = if k % 2 == 0 { r1 } else { r0 };
|
||||||
|
(cx + r * ang.cos(), cy + r * ang.sin())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
self.poly(&pts, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a small four-pointed sparkle
|
||||||
|
fn sparkle(&mut self, x: i32, y: i32) {
|
||||||
|
let p = f(SPARK, 2);
|
||||||
|
self.px(x, y, p);
|
||||||
|
for (dx, dy) in [(1, 0), (-1, 0), (0, 1), (0, -1)] {
|
||||||
|
self.px(x + dx, y + dy, f(SPARK, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bevel, outline and write into the image at cell `i`
|
||||||
|
fn blit(&self, img: &mut Image, i: u32) {
|
||||||
|
let (ox, oy) = ((i % COLS) * SIZE, (i / COLS) * SIZE);
|
||||||
|
for y in 0..32i32 {
|
||||||
|
for x in 0..32i32 {
|
||||||
|
let m = self.get(x, y);
|
||||||
|
let col = if m == EMPTY {
|
||||||
|
let near = [(1, 0), (-1, 0), (0, 1), (0, -1)].iter().any(|(dx, dy)| outlined(self.get(x + dx, y + dy)));
|
||||||
|
if !near {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
OUTLINE
|
||||||
|
} else {
|
||||||
|
let mut s = self.s[(y * 32 + x) as usize];
|
||||||
|
if s == AUTO {
|
||||||
|
let lit = self.get(x - 1, y) != m || self.get(x, y - 1) != m;
|
||||||
|
let dark = self.get(x + 1, y) != m || self.get(x, y + 1) != m;
|
||||||
|
s = match (lit, dark) {
|
||||||
|
(true, false) => 2,
|
||||||
|
(false, true) => 0,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
// the top-left corner catches the light
|
||||||
|
if lit && !dark && self.get(x - 1, y) != m && self.get(x, y - 1) != m {
|
||||||
|
s = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ramp(m)[s.min(3) as usize]
|
||||||
|
};
|
||||||
|
img.set_pixel(ox + x as u32, oy + y as u32, Color::from_rgba(col.0, col.1, col.2, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ the symbols
|
||||||
|
|
||||||
|
fn heroes(p: &mut Pad) {
|
||||||
|
// a knight's great helm, split by a blow
|
||||||
|
p.poly(&[(16.0, 1.5), (21.0, 4.0), (19.0, 7.0), (16.0, 6.0)], a(RED)); // plume
|
||||||
|
p.disc(16.0, 13.0, 8.5, a(STEEL));
|
||||||
|
p.rect(8, 13, 24, 25, a(STEEL));
|
||||||
|
p.rect(9, 14, 23, 15, f(IRON, 0)); // visor slit
|
||||||
|
p.line(16.0, 6.0, 16.0, 25.0, 1.0, f(STEEL, 0)); // ridge
|
||||||
|
for (x, y) in [(11, 19), (13, 21), (11, 22), (20, 19), (22, 21)] {
|
||||||
|
p.px(x, y, f(IRON, 1));
|
||||||
|
}
|
||||||
|
// the crack and the broken-off piece
|
||||||
|
p.poly(&[(19.0, 3.0), (26.0, 3.0), (26.0, 12.0), (21.0, 9.0)], CUT);
|
||||||
|
p.path(&[(21.0, 9.0), (18.0, 13.0), (20.0, 17.0), (17.5, 22.0)], 1.0, f(IRON, 0));
|
||||||
|
p.poly(&[(23.0, 26.0), (28.0, 24.0), (29.5, 29.5), (24.0, 29.5)], a(STEEL));
|
||||||
|
p.line(24.0, 26.5, 28.0, 25.0, 0.8, f(STEEL, 3));
|
||||||
|
p.px(27, 7, f(STEEL, 2));
|
||||||
|
p.px(29, 11, f(STEEL, 1));
|
||||||
|
p.sparkle(28, 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bosses(p: &mut Pad) {
|
||||||
|
// a skull wearing a cracked crown
|
||||||
|
p.disc(16.0, 19.0, 9.0, a(BONE));
|
||||||
|
p.rect(11, 24, 21, 29, a(BONE));
|
||||||
|
p.disc(12.0, 19.0, 2.6, f(IRON, 0));
|
||||||
|
p.disc(20.0, 19.0, 2.6, f(IRON, 0));
|
||||||
|
p.px(12, 19, f(RED, 3));
|
||||||
|
p.px(20, 19, f(RED, 3));
|
||||||
|
p.poly(&[(16.0, 21.5), (14.5, 24.5), (17.5, 24.5)], f(IRON, 0));
|
||||||
|
for x in [12, 14, 16, 18, 20] {
|
||||||
|
p.line(x as f32 + 0.5, 26.0, x as f32 + 0.5, 29.0, 0.8, f(BONE, 0));
|
||||||
|
}
|
||||||
|
p.poly(&[(6.0, 13.0), (6.0, 3.0), (10.5, 8.0), (16.0, 1.0), (21.5, 8.0), (26.0, 3.0), (26.0, 13.0)], a(GOLDM));
|
||||||
|
p.rect(6, 11, 25, 13, f(GOLDM, 0));
|
||||||
|
p.disc(16.0, 8.5, 1.8, a(RED));
|
||||||
|
p.disc(9.0, 10.0, 1.2, f(CRYSTAL, 2));
|
||||||
|
p.disc(23.0, 10.0, 1.2, f(CRYSTAL, 2));
|
||||||
|
p.path(&[(13.0, 3.0), (14.5, 7.0), (12.5, 10.0), (13.5, 13.5)], 1.2, CUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dug(p: &mut Pad) {
|
||||||
|
// a pickaxe struck into a rock
|
||||||
|
p.poly(&[(2.0, 30.0), (4.0, 21.0), (10.0, 16.0), (18.0, 17.0), (25.0, 20.0), (30.0, 30.0)], a(STONE));
|
||||||
|
p.path(&[(8.0, 22.0), (12.0, 25.0), (11.0, 29.0)], 1.0, f(STONE, 0));
|
||||||
|
p.path(&[(21.0, 23.0), (24.0, 27.0)], 1.0, f(STONE, 0));
|
||||||
|
p.rect(5, 26, 6, 27, a(GOLDM));
|
||||||
|
p.rect(16, 27, 17, 28, a(GOLDM));
|
||||||
|
p.px(26, 26, f(GOLDM, 2));
|
||||||
|
p.line(25.0, 5.0, 13.0, 23.0, 2.6, a(WOODM));
|
||||||
|
p.path(&[(10.0, 7.0), (16.0, 3.0), (23.0, 4.0)], 3.2, a(STEEL));
|
||||||
|
p.path(&[(23.0, 4.0), (28.0, 9.0), (29.0, 15.0)], 3.0, a(STEEL));
|
||||||
|
p.line(9.0, 7.5, 10.0, 7.0, 1.6, a(STEEL));
|
||||||
|
p.disc(24.0, 5.5, 2.4, a(IRON));
|
||||||
|
// chips flying
|
||||||
|
p.rect(5, 12, 6, 13, a(STONE));
|
||||||
|
p.rect(8, 9, 8, 10, a(STONE));
|
||||||
|
p.px(3, 16, f(STONE, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gold(p: &mut Pad) {
|
||||||
|
// a pile of coins
|
||||||
|
p.poly(&[(3.0, 29.0), (7.0, 21.0), (13.0, 17.0), (20.0, 17.0), (26.0, 21.0), (29.0, 29.0)], a(GOLDM));
|
||||||
|
for (x, y) in [(8, 25), (14, 22), (20, 25), (24, 23), (11, 27), (17, 27)] {
|
||||||
|
p.oval(x as f32 + 0.5, y as f32, 2.6, 1.2, 0.0, f(GOLDM, 0));
|
||||||
|
p.oval(x as f32 + 0.5, y as f32 - 0.5, 2.0, 0.8, 0.0, f(GOLDM, 2));
|
||||||
|
}
|
||||||
|
// a standing coin
|
||||||
|
p.disc(12.0, 13.0, 7.0, a(GOLDM));
|
||||||
|
p.ring(12.0, 13.0, 4.6, 5.6, f(GOLDM, 0));
|
||||||
|
p.rect(11, 10, 12, 16, f(GOLDM, 0));
|
||||||
|
p.rect(10, 10, 13, 10, f(GOLDM, 0));
|
||||||
|
p.rect(10, 16, 13, 16, f(GOLDM, 0));
|
||||||
|
// another coin flying
|
||||||
|
p.oval(24.0, 9.0, 4.0, 4.0, 0.0, a(GOLDM));
|
||||||
|
p.ring(24.0, 9.0, 2.0, 2.9, f(GOLDM, 0));
|
||||||
|
p.sparkle(27, 16);
|
||||||
|
p.sparkle(5, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wood(p: &mut Pad) {
|
||||||
|
// an axe buried in a log
|
||||||
|
p.rect(3, 18, 23, 27, a(WOODM));
|
||||||
|
for (x0, x1, y) in [(5, 12, 20), (9, 19, 23), (4, 9, 25), (14, 21, 26)] {
|
||||||
|
p.rect(x0, y, x1, y, f(WOODM, 0));
|
||||||
|
}
|
||||||
|
p.oval(24.5, 22.5, 4.0, 5.2, 0.0, f(BONE, 1));
|
||||||
|
p.ring(24.5, 22.5, 1.5, 2.3, f(WOODM, 2));
|
||||||
|
p.ring(24.5, 22.5, 3.2, 3.9, f(WOODM, 2));
|
||||||
|
p.px(24, 22, f(WOODM, 1));
|
||||||
|
p.line(5.0, 3.0, 15.0, 17.0, 2.6, a(DKWOOD));
|
||||||
|
p.poly(&[(10.0, 14.0), (17.0, 11.0), (20.5, 17.5), (13.0, 19.0)], a(STEEL));
|
||||||
|
p.line(12.0, 18.6, 20.0, 17.6, 1.0, f(STEEL, 3));
|
||||||
|
p.rect(9, 17, 21, 18, a(WOODM));
|
||||||
|
p.rect(12, 17, 19, 17, f(IRON, 0));
|
||||||
|
// chips
|
||||||
|
p.rect(23, 9, 24, 10, f(WOODM, 2));
|
||||||
|
p.rect(26, 13, 26, 14, f(WOODM, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walls(p: &mut Pad) {
|
||||||
|
// a brick wall with battlements
|
||||||
|
p.rect(3, 9, 28, 28, f(IRON, 1));
|
||||||
|
for (x0, x1) in [(3, 8), (13, 18), (23, 28)] {
|
||||||
|
p.rect(x0, 3, x1, 8, f(IRON, 1));
|
||||||
|
}
|
||||||
|
for (row, y) in [8, 13, 18, 23].iter().enumerate() {
|
||||||
|
let off = if row % 2 == 0 { 0 } else { 4 };
|
||||||
|
let mut x = 3 - off;
|
||||||
|
while x <= 28 {
|
||||||
|
p.rect(x.max(3), y + 1, (x + 6).min(28), y + 4, a(BRICK));
|
||||||
|
x += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (x0, x1) in [(3, 8), (13, 18), (23, 28)] {
|
||||||
|
p.rect(x0, 4, x1, 7, a(BRICK));
|
||||||
|
}
|
||||||
|
p.rect(3, 9, 28, 9, f(IRON, 1));
|
||||||
|
// a crack and a loose brick
|
||||||
|
p.path(&[(19.0, 14.0), (17.0, 18.0), (19.0, 21.0)], 1.0, f(IRON, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rooms(p: &mut Pad) {
|
||||||
|
// a stone arch with a barred wooden door
|
||||||
|
p.disc(16.0, 15.0, 13.0, a(STONE));
|
||||||
|
p.rect(3, 15, 28, 29, a(STONE));
|
||||||
|
p.disc(16.0, 15.0, 8.5, f(IRON, 0));
|
||||||
|
p.rect(8, 15, 23, 29, f(IRON, 0));
|
||||||
|
for k in 0..7 {
|
||||||
|
let ang = std::f32::consts::PI * (1.0 + k as f32 / 6.0);
|
||||||
|
p.line(16.0 + 9.0 * ang.cos(), 15.0 + 9.0 * ang.sin(), 16.0 + 13.0 * ang.cos(), 15.0 + 13.0 * ang.sin(), 0.9, f(STONE, 0));
|
||||||
|
}
|
||||||
|
p.rect(3, 22, 7, 22, f(STONE, 0));
|
||||||
|
p.rect(24, 22, 28, 22, f(STONE, 0));
|
||||||
|
p.disc(16.0, 15.5, 7.5, a(WOODM));
|
||||||
|
p.rect(9, 15, 22, 29, a(WOODM));
|
||||||
|
for x in [12, 16, 19] {
|
||||||
|
p.line(x as f32 + 0.5, 8.5, x as f32 + 0.5, 29.0, 0.9, f(WOODM, 0));
|
||||||
|
}
|
||||||
|
p.rect(9, 13, 22, 14, a(IRON));
|
||||||
|
p.rect(9, 23, 22, 24, a(IRON));
|
||||||
|
p.ring(20.0, 19.0, 1.2, 2.2, a(GOLDM));
|
||||||
|
p.rect(2, 29, 29, 30, a(STONE));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spells(p: &mut Pad) {
|
||||||
|
// an open spell book with a swirl of magic
|
||||||
|
p.poly(&[(2.0, 15.0), (16.0, 19.0), (30.0, 15.0), (30.0, 28.0), (16.0, 31.0), (2.0, 28.0)], a(PURPLE));
|
||||||
|
p.poly(&[(3.5, 13.0), (15.5, 17.0), (15.5, 28.5), (3.5, 25.5)], a(BONE));
|
||||||
|
p.poly(&[(16.5, 17.0), (28.5, 13.0), (28.5, 25.5), (16.5, 28.5)], a(BONE));
|
||||||
|
for k in 0..3 {
|
||||||
|
let y = 18.0 + k as f32 * 3.0;
|
||||||
|
p.line(5.5, y, 13.5, y + 2.4, 0.8, f(BONE, 0));
|
||||||
|
p.line(18.5, y + 2.4, 26.5, y, 0.8, f(BONE, 0));
|
||||||
|
}
|
||||||
|
p.px(5, 16, f(RED, 2));
|
||||||
|
p.px(6, 16, f(RED, 2));
|
||||||
|
// the swirl rising from the pages
|
||||||
|
let mut prev = (16.0, 15.0);
|
||||||
|
for k in 1..22 {
|
||||||
|
let t = k as f32 / 21.0;
|
||||||
|
let ang = t * 9.0;
|
||||||
|
let r = 1.0 + t * 6.5;
|
||||||
|
let pt = (16.0 + r * ang.cos(), 14.0 - t * 8.0 + r * 0.5 * ang.sin());
|
||||||
|
p.line(prev.0, prev.1, pt.0, pt.1, 1.0 + t * 1.2, f(CYAN, if t > 0.6 { 1 } else { 2 }));
|
||||||
|
prev = pt;
|
||||||
|
}
|
||||||
|
p.disc(16.0, 9.0, 1.4, f(CYAN, 3));
|
||||||
|
p.sparkle(5, 4);
|
||||||
|
p.sparkle(27, 5);
|
||||||
|
p.sparkle(26, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slaps(p: &mut Pad) {
|
||||||
|
// the keeper's open hand, swinging
|
||||||
|
let tilt = 0.25f32;
|
||||||
|
let (s, c) = tilt.sin_cos();
|
||||||
|
let rot = |x: f32, y: f32| (19.0 + (x - 19.0) * c - (y - 19.0) * s, 19.0 + (x - 19.0) * s + (y - 19.0) * c);
|
||||||
|
let fingers = [(13.5, 6.5), (17.0, 4.5), (20.5, 5.0), (24.0, 8.0)];
|
||||||
|
for (x, y) in fingers {
|
||||||
|
let (x0, y0) = rot(x, 18.0);
|
||||||
|
let (x1, y1) = rot(x, y);
|
||||||
|
p.line(x0, y0, x1, y1, 3.2, a(SKIN));
|
||||||
|
p.px((x1 - 0.5) as i32, (y1 - 1.6) as i32, f(BONE, 2));
|
||||||
|
}
|
||||||
|
let (px0, py0) = rot(19.0, 20.0);
|
||||||
|
p.oval(px0, py0, 6.5, 7.0, tilt, a(SKIN));
|
||||||
|
let (tx0, ty0) = rot(13.5, 21.0);
|
||||||
|
let (tx1, ty1) = rot(8.5, 15.0);
|
||||||
|
p.line(tx0, ty0, tx1, ty1, 3.2, a(SKIN));
|
||||||
|
p.px((tx1 - 1.0) as i32, (ty1 - 1.0) as i32, f(BONE, 2));
|
||||||
|
let (wx0, wy0) = rot(17.0, 26.0);
|
||||||
|
let (wx1, wy1) = rot(17.0, 31.0);
|
||||||
|
p.line(wx0, wy0, wx1, wy1, 6.0, a(SKIN));
|
||||||
|
p.line(wx0 - 3.0, wy0 + 2.5, wx0 + 3.5, wy0 + 1.0, 1.6, a(PURPLE)); // sleeve cuff
|
||||||
|
p.line(15.5, 15.0, 19.5, 22.0, 0.8, f(SKIN, 0));
|
||||||
|
// motion lines
|
||||||
|
for (x0, y, x1) in [(1, 9, 6), (2, 14, 7), (1, 19, 5)] {
|
||||||
|
p.rect(x0, y, x1, y, f(SPARK, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn traps(p: &mut Pad) {
|
||||||
|
// spikes shooting from a floor slab, one of them bloodied
|
||||||
|
p.rect(2, 23, 29, 29, a(STONE));
|
||||||
|
p.rect(2, 26, 29, 26, f(STONE, 0));
|
||||||
|
p.rect(15, 23, 15, 26, f(STONE, 0));
|
||||||
|
p.rect(8, 26, 8, 29, f(STONE, 0));
|
||||||
|
p.rect(23, 26, 23, 29, f(STONE, 0));
|
||||||
|
for (cx, tip) in [(6.0, 11.0), (11.0, 7.0), (16.0, 3.0), (21.0, 7.0), (26.0, 11.0)] {
|
||||||
|
p.poly(&[(cx - 2.6, 23.0), (cx, tip), (cx + 2.6, 23.0)], a(STEEL));
|
||||||
|
p.line(cx - 0.6, tip + 3.0, cx - 1.6, 22.0, 0.8, f(STEEL, 3));
|
||||||
|
p.rect(cx as i32 - 3, 22, cx as i32 + 2, 23, f(IRON, 1));
|
||||||
|
}
|
||||||
|
p.poly(&[(14.6, 8.0), (16.0, 3.0), (17.4, 8.0)], a(RED));
|
||||||
|
p.rect(16, 9, 16, 11, f(RED, 1));
|
||||||
|
p.px(16, 13, f(RED, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn towers(p: &mut Pad) {
|
||||||
|
// a dark tower with a horned roof and a glowing window
|
||||||
|
p.poly(&[(9.0, 30.0), (11.5, 12.0), (20.5, 12.0), (23.0, 30.0)], a(IRON));
|
||||||
|
for y in [16, 21, 26] {
|
||||||
|
p.line(11.0, y as f32 + 0.5, 21.5, y as f32 + 0.5, 0.8, f(IRON, 0));
|
||||||
|
}
|
||||||
|
p.rect(8, 9, 23, 12, a(PURPLE));
|
||||||
|
for x in [8, 13, 18, 22] {
|
||||||
|
p.rect(x, 7, x + 1, 8, a(PURPLE));
|
||||||
|
}
|
||||||
|
p.poly(&[(9.0, 8.0), (16.0, 0.5), (23.0, 8.0)], a(IRON));
|
||||||
|
p.path(&[(8.5, 10.0), (5.0, 7.0), (4.0, 2.0)], 1.6, a(BONE));
|
||||||
|
p.path(&[(23.5, 10.0), (27.0, 7.0), (28.0, 2.0)], 1.6, a(BONE));
|
||||||
|
p.disc(16.0, 18.0, 2.2, f(FIRE, 1));
|
||||||
|
p.rect(14, 18, 18, 22, f(FIRE, 1));
|
||||||
|
p.rect(15, 17, 17, 21, f(FIRE, 2));
|
||||||
|
p.px(16, 18, f(FIRE, 3));
|
||||||
|
p.rect(14, 27, 18, 30, f(IRON, 0));
|
||||||
|
p.disc(16.0, 27.0, 2.4, f(IRON, 0));
|
||||||
|
p.rect(3, 30, 28, 30, a(STONE));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drowned(p: &mut Pad) {
|
||||||
|
// a helmet sinking between waves
|
||||||
|
p.poly(&[(17.0, 2.0), (22.0, 4.0), (20.0, 6.5), (16.5, 6.0)], a(RED));
|
||||||
|
p.disc(15.5, 14.0, 8.0, a(STEEL));
|
||||||
|
p.rect(9, 12, 21, 13, f(IRON, 0));
|
||||||
|
p.line(15.5, 6.0, 15.5, 20.0, 1.0, f(STEEL, 0));
|
||||||
|
p.fill(a(WATER), |x, y| y >= 18.0 + 1.6 * (x * 0.55).sin() && y < 31.0 && (1.0..31.0).contains(&x));
|
||||||
|
for (x0, y0) in [(4.0, 23.0), (14.0, 25.0), (22.0, 22.0), (9.0, 28.0), (20.0, 28.5)] {
|
||||||
|
p.line(x0, y0, x0 + 3.5, y0, 1.0, f(WATER, 3));
|
||||||
|
}
|
||||||
|
p.ring(25.0, 12.0, 1.0, 2.0, f(GLASS, 3));
|
||||||
|
p.ring(27.0, 6.0, 0.6, 1.5, f(GLASS, 3));
|
||||||
|
p.px(23, 2, f(GLASS, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn burnt(p: &mut Pad) {
|
||||||
|
// a burning bridge plank
|
||||||
|
p.poly(&[(2.0, 23.0), (28.0, 16.5), (30.0, 22.0), (4.0, 28.5)], a(WOODM));
|
||||||
|
p.line(5.0, 25.5, 28.0, 19.5, 0.8, f(WOODM, 0));
|
||||||
|
p.poly(&[(19.0, 18.8), (28.0, 16.5), (30.0, 22.0), (21.0, 24.3)], a(IRON));
|
||||||
|
p.rect(6, 23, 7, 24, f(IRON, 2));
|
||||||
|
p.rect(13, 21, 14, 22, f(IRON, 2));
|
||||||
|
for (x, y) in [(20, 20), (23, 21), (25, 19), (27, 20)] {
|
||||||
|
p.px(x, y, f(FIRE, 1));
|
||||||
|
}
|
||||||
|
// flames
|
||||||
|
p.poly(&[(14.0, 21.0), (13.0, 14.0), (16.0, 9.0), (17.0, 13.0), (20.0, 4.0), (22.0, 11.0), (24.5, 8.0), (25.5, 14.0), (28.0, 17.0)], f(FIRE, 0));
|
||||||
|
p.poly(&[(16.0, 20.0), (16.0, 15.0), (18.0, 12.5), (19.0, 15.0), (20.5, 9.0), (22.0, 14.0), (24.0, 12.0), (24.5, 16.0), (26.0, 18.0)], f(FIRE, 1));
|
||||||
|
p.poly(&[(18.5, 19.0), (19.0, 16.0), (20.5, 13.5), (21.5, 16.0), (23.0, 15.5), (23.5, 18.0)], f(FIRE, 2));
|
||||||
|
p.px(21, 17, f(FIRE, 3));
|
||||||
|
for (x, y) in [(11, 6), (14, 3), (26, 4), (29, 10), (8, 12)] {
|
||||||
|
p.px(x, y, f(EMBER, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn siege(p: &mut Pad) {
|
||||||
|
// a catapult with a boulder
|
||||||
|
p.line(13.0, 24.0, 17.0, 13.0, 2.0, a(DKWOOD));
|
||||||
|
p.line(21.0, 24.0, 17.0, 13.0, 2.0, a(DKWOOD));
|
||||||
|
p.line(6.0, 22.0, 24.0, 7.0, 2.2, a(WOODM));
|
||||||
|
p.oval(24.5, 6.5, 3.2, 2.0, -0.7, a(DKWOOD));
|
||||||
|
p.disc(25.0, 3.5, 2.8, a(STONE));
|
||||||
|
p.rect(3, 22, 28, 24, a(WOODM));
|
||||||
|
p.rect(3, 23, 28, 23, f(WOODM, 0));
|
||||||
|
p.disc(17.0, 13.0, 1.4, a(IRON));
|
||||||
|
p.line(6.0, 22.0, 10.0, 22.0, 1.0, f(IRON, 1)); // the rope
|
||||||
|
for cx in [8.0, 23.0] {
|
||||||
|
p.disc(cx, 25.5, 4.0, a(DKWOOD));
|
||||||
|
p.ring(cx, 25.5, 2.6, 3.3, f(DKWOOD, 0));
|
||||||
|
p.disc(cx, 25.5, 1.2, a(STEEL));
|
||||||
|
}
|
||||||
|
p.rect(1, 30, 30, 30, a(STONE));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn captured(p: &mut Pad) {
|
||||||
|
// two iron shackles joined by a chain
|
||||||
|
p.ring(9.0, 20.0, 3.5, 6.5, a(STEEL));
|
||||||
|
p.ring(23.0, 20.0, 3.5, 6.5, a(STEEL));
|
||||||
|
p.rect(7, 13, 11, 15, a(IRON));
|
||||||
|
p.rect(21, 13, 25, 15, a(IRON));
|
||||||
|
p.disc(9.0, 24.5, 1.2, f(IRON, 0));
|
||||||
|
p.disc(23.0, 24.5, 1.2, f(IRON, 0));
|
||||||
|
// the chain, links turned alternately
|
||||||
|
let links = [(9.0, 11.0, true), (12.0, 7.5, false), (16.0, 6.0, true), (20.0, 7.5, false), (23.0, 11.0, true)];
|
||||||
|
for (x, y, upright) in links {
|
||||||
|
if upright {
|
||||||
|
p.oval(x, y, 1.6, 2.6, 0.0, a(STEEL));
|
||||||
|
p.rect(x as i32, y as i32 - 1, x as i32, y as i32, f(IRON, 0));
|
||||||
|
} else {
|
||||||
|
p.oval(x, y, 2.6, 1.6, 0.0, a(IRON));
|
||||||
|
p.rect(x as i32 - 1, y as i32, x as i32, y as i32, f(IRON, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drawbridge(p: &mut Pad) {
|
||||||
|
// a gatehouse with its bridge hanging on chains above the moat
|
||||||
|
p.rect(14, 4, 29, 29, a(STONE));
|
||||||
|
for x in [14, 19, 24] {
|
||||||
|
p.rect(x, 1, x + 2, 3, a(STONE));
|
||||||
|
}
|
||||||
|
for y in [9, 15, 21] {
|
||||||
|
p.rect(14, y, 29, y, f(STONE, 0));
|
||||||
|
}
|
||||||
|
p.disc(22.0, 16.0, 4.5, f(IRON, 0));
|
||||||
|
p.rect(18, 16, 26, 28, f(IRON, 0));
|
||||||
|
for x in [19, 21, 23, 25] {
|
||||||
|
p.rect(x, 13, x, 22, f(IRON, 2));
|
||||||
|
}
|
||||||
|
p.fill(a(WATER), |x, y| y >= 26.5 + 0.8 * (x * 1.1).sin() && y < 31.0 && (1.0..18.0).contains(&x));
|
||||||
|
// the bridge, half raised
|
||||||
|
p.line(18.0, 27.0, 4.0, 17.0, 4.0, a(WOODM));
|
||||||
|
for k in 1..4 {
|
||||||
|
let t = k as f32 / 4.0;
|
||||||
|
let (x, y) = (18.0 - 14.0 * t, 27.0 - 10.0 * t);
|
||||||
|
p.line(x - 1.2, y - 1.7, x + 1.2, y + 1.7, 0.8, f(WOODM, 0));
|
||||||
|
}
|
||||||
|
p.disc(18.0, 27.0, 1.3, a(IRON));
|
||||||
|
for k in 0..6 {
|
||||||
|
let t = k as f32 / 5.0;
|
||||||
|
let (x, y) = (4.5 + 9.5 * t, 15.0 - 7.0 * t);
|
||||||
|
p.disc(x, y, 1.1, if k % 2 == 0 { a(STEEL) } else { a(IRON) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wins(p: &mut Pad) {
|
||||||
|
// a banner planted in the dungeon heart
|
||||||
|
p.disc(10.5, 20.0, 6.0, a(RED));
|
||||||
|
p.disc(21.5, 20.0, 6.0, a(RED));
|
||||||
|
p.poly(&[(4.7, 21.5), (27.3, 21.5), (16.0, 31.0)], a(RED));
|
||||||
|
p.oval(9.0, 18.5, 2.0, 1.4, -0.6, f(RED, 3));
|
||||||
|
p.line(12.0, 2.0, 12.0, 21.0, 1.8, a(DKWOOD));
|
||||||
|
p.disc(12.0, 2.0, 1.3, a(GOLDM));
|
||||||
|
p.poly(&[(13.0, 3.5), (27.0, 4.5), (23.5, 8.0), (27.0, 11.5), (13.0, 12.5)], a(GOLDM));
|
||||||
|
p.line(14.5, 6.0, 21.5, 6.5, 0.8, f(GOLDM, 0));
|
||||||
|
p.line(14.5, 9.5, 21.5, 9.5, 0.8, f(GOLDM, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn missions(p: &mut Pad) {
|
||||||
|
// a crystal shard on a rock
|
||||||
|
p.poly(&[(5.0, 30.0), (8.0, 25.0), (14.0, 24.0), (22.0, 25.0), (27.0, 30.0)], a(STONE));
|
||||||
|
p.poly(&[(16.0, 1.5), (22.0, 9.0), (19.5, 27.0), (12.5, 27.0), (10.0, 9.0)], f(CRYSTAL, 1));
|
||||||
|
p.poly(&[(16.0, 1.5), (10.0, 9.0), (12.5, 27.0), (14.5, 27.0), (14.0, 9.0)], f(CRYSTAL, 2));
|
||||||
|
p.poly(&[(16.0, 1.5), (22.0, 9.0), (19.5, 27.0), (18.5, 27.0), (18.5, 9.0)], f(CRYSTAL, 0));
|
||||||
|
p.line(12.0, 6.5, 13.0, 18.0, 0.9, f(CRYSTAL, 3));
|
||||||
|
p.poly(&[(6.0, 14.0), (9.5, 18.0), (10.5, 27.0), (6.5, 27.0), (4.5, 19.0)], f(CRYSTAL, 1));
|
||||||
|
p.poly(&[(6.0, 14.0), (4.5, 19.0), (6.5, 27.0), (7.5, 27.0), (6.5, 18.0)], f(CRYSTAL, 2));
|
||||||
|
p.poly(&[(26.0, 16.0), (28.0, 21.0), (26.5, 27.0), (22.0, 27.0), (23.0, 20.0)], f(CRYSTAL, 0));
|
||||||
|
p.sparkle(24, 5);
|
||||||
|
p.sparkle(4, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coop(p: &mut Pad) {
|
||||||
|
// two claws clasped: an imp's and a demon's
|
||||||
|
p.line(0.0, 25.0, 11.0, 19.5, 6.0, a(SKIN));
|
||||||
|
p.line(32.0, 25.0, 21.0, 19.5, 6.0, a(RED));
|
||||||
|
p.line(3.0, 21.0, 6.0, 27.5, 2.0, a(PURPLE)); // cuffs
|
||||||
|
p.line(29.0, 21.0, 26.0, 27.5, 2.0, a(IRON));
|
||||||
|
p.oval(19.5, 18.5, 5.5, 4.5, 0.0, a(RED));
|
||||||
|
p.oval(12.5, 18.5, 5.0, 4.8, 0.0, a(SKIN));
|
||||||
|
// the imp's fingers curl over the back of the demon's hand
|
||||||
|
for k in 0..4 {
|
||||||
|
let x = 15.0 + k as f32 * 2.4;
|
||||||
|
p.path(&[(x - 0.5, 15.0), (x + 1.0, 17.5), (x + 0.6, 21.0)], 2.0, a(SKIN));
|
||||||
|
p.px((x + 0.6) as i32, 22, f(BONE, 3));
|
||||||
|
}
|
||||||
|
// the demon's thumb over the imp's hand
|
||||||
|
p.line(19.0, 14.5, 12.0, 14.0, 2.2, a(RED));
|
||||||
|
p.px(10, 13, f(BONE, 3));
|
||||||
|
p.px(11, 13, f(BONE, 2));
|
||||||
|
p.sparkle(16, 6);
|
||||||
|
p.sparkle(8, 9);
|
||||||
|
p.sparkle(25, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hourglass(p: &mut Pad, cx: f32, top: i32, bottom: i32, half: f32, frame: u8) {
|
||||||
|
let (t, b) = (top as f32, bottom as f32);
|
||||||
|
let mid = (t + b) * 0.5 + 0.5;
|
||||||
|
p.poly(&[(cx - half + 1.0, t + 2.0), (cx + half - 1.0, t + 2.0), (cx + 1.0, mid), (cx + half - 1.0, b - 1.0), (cx - half + 1.0, b - 1.0), (cx - 1.0, mid)], f(GLASS, 1));
|
||||||
|
p.poly(&[(cx - half + 2.0, t + 2.5), (cx - half + 3.0, t + 2.5), (cx - 0.5, mid - 1.0)], f(GLASS, 3));
|
||||||
|
// sand: a little left above, a heap below, the stream
|
||||||
|
p.poly(&[(cx - half * 0.45, mid - 4.0), (cx + half * 0.45, mid - 4.0), (cx, mid - 0.5)], f(SUN, 1));
|
||||||
|
p.poly(&[(cx - half + 1.5, b - 1.0), (cx, b - 6.0), (cx + half - 1.5, b - 1.0)], f(SUN, 1));
|
||||||
|
p.poly(&[(cx - 2.0, b - 3.5), (cx, b - 6.0), (cx + 0.5, b - 3.5)], f(SUN, 2));
|
||||||
|
p.line(cx, mid, cx, b - 5.0, 0.9, f(SUN, 2));
|
||||||
|
p.rect((cx - half) as i32, top, (cx + half) as i32 - 1, top + 1, a(frame));
|
||||||
|
p.rect((cx - half) as i32, bottom, (cx + half) as i32 - 1, bottom + 1, a(frame));
|
||||||
|
p.line(cx - half + 0.5, t + 1.0, cx - half + 0.5, b, 1.3, a(frame));
|
||||||
|
p.line(cx + half - 0.5, t + 1.0, cx + half - 0.5, b, 1.3, a(frame));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn minutes(p: &mut Pad) {
|
||||||
|
// an hourglass with a candle beside it
|
||||||
|
hourglass(p, 13.0, 3, 27, 8.0, WOODM);
|
||||||
|
p.rect(23, 15, 27, 28, a(BONE));
|
||||||
|
p.rect(22, 28, 28, 29, a(IRON));
|
||||||
|
p.line(24.0, 17.0, 24.0, 21.0, 0.9, f(BONE, 3));
|
||||||
|
p.px(25, 14, f(IRON, 0));
|
||||||
|
p.poly(&[(23.5, 13.0), (25.0, 7.0), (26.6, 13.0)], f(FIRE, 1));
|
||||||
|
p.poly(&[(24.4, 13.0), (25.0, 9.5), (25.8, 13.0)], f(FIRE, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flawless(p: &mut Pad) {
|
||||||
|
// a gleaming, untouched shield
|
||||||
|
let rim = [(5.0, 3.0), (27.0, 3.0), (27.0, 15.0), (24.0, 22.0), (16.0, 30.0), (8.0, 22.0), (5.0, 15.0)];
|
||||||
|
p.poly(&rim, a(GOLDM));
|
||||||
|
let field = [(7.5, 5.5), (24.5, 5.5), (24.5, 15.0), (22.0, 21.0), (16.0, 27.0), (10.0, 21.0), (7.5, 15.0)];
|
||||||
|
p.poly(&field, a(BLUE));
|
||||||
|
p.poly(&[(7.5, 5.5), (16.0, 5.5), (16.0, 27.0), (10.0, 21.0), (7.5, 15.0)], f(BLUE, 2));
|
||||||
|
p.rect(15, 7, 16, 23, a(GOLDM));
|
||||||
|
p.rect(10, 12, 21, 13, a(GOLDM));
|
||||||
|
p.disc(16.0, 12.5, 2.0, a(CRYSTAL));
|
||||||
|
p.line(9.0, 7.0, 12.0, 7.0, 0.9, f(BLUE, 3));
|
||||||
|
p.line(9.0, 7.0, 9.0, 10.0, 0.9, f(BLUE, 3));
|
||||||
|
p.sparkle(26, 4);
|
||||||
|
p.sparkle(4, 20);
|
||||||
|
p.px(28, 9, f(SPARK, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn speedrun(p: &mut Pad) {
|
||||||
|
// a winged hourglass
|
||||||
|
for side in [-1.0f32, 1.0] {
|
||||||
|
let x = |dx: f32| 16.0 + side * dx;
|
||||||
|
p.poly(&[(x(6.0), 10.0), (x(13.0), 4.0), (x(15.5), 6.0), (x(14.5), 10.0), (x(15.0), 13.0), (x(13.0), 17.0), (x(6.0), 19.0)], a(BONE));
|
||||||
|
for (dx, y) in [(10.0, 9.0), (11.0, 12.0), (10.0, 15.0)] {
|
||||||
|
p.line(x(6.5), y + 1.5, x(dx + 4.0), y, 0.8, f(BONE, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hourglass(p, 16.0, 4, 27, 6.0, GOLDM);
|
||||||
|
p.path(&[(27.0, 20.0), (25.0, 24.0), (28.0, 24.0), (25.5, 29.5)], 1.2, f(SUN, 2));
|
||||||
|
p.path(&[(6.0, 20.0), (4.0, 24.0), (7.0, 24.0), (4.5, 29.5)], 1.2, f(SUN, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn campaign(p: &mut Pad) {
|
||||||
|
// the crown with the core made whole at its heart
|
||||||
|
p.poly(&[(4.0, 22.0), (3.0, 8.0), (10.0, 15.0), (16.0, 5.0), (22.0, 15.0), (29.0, 8.0), (28.0, 22.0)], a(GOLDM));
|
||||||
|
p.rect(4, 21, 27, 27, a(GOLDM));
|
||||||
|
p.rect(4, 22, 27, 22, f(GOLDM, 0));
|
||||||
|
p.rect(4, 26, 27, 26, f(GOLDM, 0));
|
||||||
|
for (x, y) in [(3.0, 7.0), (16.0, 3.5), (29.0, 7.0)] {
|
||||||
|
p.disc(x, y, 1.8, a(GOLDM));
|
||||||
|
}
|
||||||
|
p.disc(8.5, 24.0, 1.3, a(RED));
|
||||||
|
p.disc(23.5, 24.0, 1.3, a(RED));
|
||||||
|
// the core
|
||||||
|
p.poly(&[(16.0, 11.0), (20.5, 17.5), (16.0, 26.0), (11.5, 17.5)], f(CRYSTAL, 1));
|
||||||
|
p.poly(&[(16.0, 11.0), (11.5, 17.5), (16.0, 26.0), (15.0, 17.5)], f(CRYSTAL, 2));
|
||||||
|
p.px(14, 15, f(CRYSTAL, 3));
|
||||||
|
p.px(14, 16, f(CRYSTAL, 3));
|
||||||
|
p.sparkle(26, 14);
|
||||||
|
p.sparkle(7, 13);
|
||||||
|
p.sparkle(16, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn surface(p: &mut Pad) {
|
||||||
|
// the sun rising over grassy hills
|
||||||
|
for k in 0..8 {
|
||||||
|
let ang = k as f32 * std::f32::consts::PI / 4.0;
|
||||||
|
p.line(16.0 + 9.0 * ang.cos(), 14.0 + 9.0 * ang.sin(), 16.0 + 13.0 * ang.cos(), 14.0 + 13.0 * ang.sin(), 2.0, f(SUN, 1));
|
||||||
|
}
|
||||||
|
p.disc(16.0, 14.0, 7.0, f(SUN, 2));
|
||||||
|
p.disc(14.5, 12.5, 4.0, f(SUN, 3));
|
||||||
|
p.fill(a(GREEN), |x, y| y >= 21.0 + 2.5 * (x * 0.22 + 0.4).sin() && y < 31.0 && (1.0..31.0).contains(&x));
|
||||||
|
for x in [3, 7, 12, 19, 24, 28] {
|
||||||
|
let base = 21.0 + 2.5 * ((x as f32 + 0.5) * 0.22 + 0.4).sin();
|
||||||
|
p.line(x as f32 + 0.5, base, x as f32 - 0.5, base - 3.0, 1.0, f(GREEN, 2));
|
||||||
|
}
|
||||||
|
p.px(10, 26, f(RED, 2));
|
||||||
|
p.px(22, 27, f(SUN, 2));
|
||||||
|
p.px(16, 28, f(SPARK, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn defeat(p: &mut Pad) {
|
||||||
|
// a cup of tea, to soothe the defeat
|
||||||
|
p.oval(16.0, 27.0, 14.0, 2.8, 0.0, a(BONE));
|
||||||
|
p.oval(16.0, 26.5, 9.0, 1.5, 0.0, f(BONE, 0));
|
||||||
|
p.ring(24.5, 18.5, 2.0, 4.0, a(BONE));
|
||||||
|
p.poly(&[(6.0, 13.0), (26.0, 13.0), (25.0, 20.0), (21.0, 26.0), (11.0, 26.0), (7.0, 20.0)], a(BONE));
|
||||||
|
p.rect(7, 16, 25, 17, f(BLUE, 2));
|
||||||
|
p.px(12, 16, f(BLUE, 0));
|
||||||
|
p.px(16, 17, f(BLUE, 0));
|
||||||
|
p.px(20, 16, f(BLUE, 0));
|
||||||
|
p.oval(16.0, 13.5, 9.5, 1.8, 0.0, f(TEA, 1));
|
||||||
|
p.oval(14.0, 13.0, 3.0, 0.6, 0.0, f(TEA, 3));
|
||||||
|
// steam
|
||||||
|
for x0 in [11.0f32, 16.0, 21.0] {
|
||||||
|
let mut prev = (x0, 10.0);
|
||||||
|
for k in 1..6 {
|
||||||
|
let y = 10.0 - k as f32 * 1.5;
|
||||||
|
let pt = (x0 + (k as f32 * 1.4).sin() * 1.3, y);
|
||||||
|
p.line(prev.0, prev.1, pt.0, pt.1, 0.9, f(EMBER, 3));
|
||||||
|
prev = pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn legend(p: &mut Pad) {
|
||||||
|
// a star crowned with laurels
|
||||||
|
for side in [-1.0f32, 1.0] {
|
||||||
|
let mut prev = (16.0 + side * 3.0, 29.0);
|
||||||
|
for k in 0..7 {
|
||||||
|
let ang = std::f32::consts::FRAC_PI_2 + side * (0.35 + k as f32 * 0.33);
|
||||||
|
let pt = (16.0 + 12.0 * ang.cos(), 16.0 + 12.0 * ang.sin());
|
||||||
|
p.line(prev.0, prev.1, pt.0, pt.1, 1.0, f(GREEN, 0));
|
||||||
|
let out = (16.0 + 14.2 * ang.cos(), 16.0 + 14.2 * ang.sin());
|
||||||
|
let inn = (16.0 + 10.0 * ang.cos(), 16.0 + 10.0 * ang.sin());
|
||||||
|
p.oval(out.0, out.1, 2.2, 1.1, ang + side * 0.6, a(GREEN));
|
||||||
|
if k < 6 {
|
||||||
|
p.oval(inn.0, inn.1, 2.0, 1.0, ang - side * 0.6, a(GREEN));
|
||||||
|
}
|
||||||
|
prev = pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.line(13.0, 29.0, 19.0, 29.0, 1.4, a(RED));
|
||||||
|
p.star(16.0, 15.5, 4.2, 10.0, a(GOLDM));
|
||||||
|
p.star(16.0, 15.5, 1.8, 4.0, f(GOLDM, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wyrm(p: &mut Pad) {
|
||||||
|
// the ember wyrm's head
|
||||||
|
p.path(&[(10.0, 9.0), (6.0, 5.0), (2.0, 4.0)], 2.2, a(BONE));
|
||||||
|
p.path(&[(13.0, 8.0), (11.0, 3.0), (7.0, 0.5)], 2.0, a(BONE));
|
||||||
|
// neck and spines
|
||||||
|
p.poly(&[(2.0, 31.0), (4.0, 18.0), (9.0, 10.0), (14.0, 22.0), (12.0, 31.0)], a(RED));
|
||||||
|
for (x, y) in [(3.0, 22.0), (3.5, 27.0), (5.5, 16.0)] {
|
||||||
|
p.poly(&[(x, y), (x - 3.0, y + 0.5), (x + 0.5, y + 3.0)], a(FIRE));
|
||||||
|
}
|
||||||
|
// the head, snout to the right, jaw open
|
||||||
|
p.poly(&[(6.0, 12.0), (12.0, 7.0), (21.0, 9.0), (29.0, 12.0), (29.5, 15.0), (19.0, 16.5), (12.0, 20.0)], a(RED));
|
||||||
|
p.poly(&[(12.0, 19.0), (19.0, 18.0), (27.0, 21.0), (26.0, 23.5), (16.0, 24.0), (10.0, 22.0)], a(RED));
|
||||||
|
p.poly(&[(19.0, 16.4), (29.0, 15.0), (27.0, 20.5), (19.0, 18.0)], f(IRON, 0));
|
||||||
|
for x in [21, 24, 27] {
|
||||||
|
p.px(x, 16, f(BONE, 3));
|
||||||
|
p.px(x - 1, 19, f(BONE, 3));
|
||||||
|
}
|
||||||
|
p.oval(16.0, 11.5, 2.4, 1.4, 0.2, f(SUN, 2));
|
||||||
|
p.rect(16, 11, 16, 12, f(IRON, 0));
|
||||||
|
p.line(13.0, 9.6, 19.0, 10.0, 0.9, f(RED, 0));
|
||||||
|
p.px(27, 11, f(IRON, 0));
|
||||||
|
p.line(16.0, 22.0, 25.0, 21.5, 0.8, f(RED, 3));
|
||||||
|
// embers from the jaws
|
||||||
|
for (x, y) in [(30, 18), (28, 25), (30, 27), (24, 27), (29, 22)] {
|
||||||
|
p.px(x, y, f(EMBER, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fullhand(p: &mut Pad) {
|
||||||
|
// the keeper's hand, full of creatures
|
||||||
|
// horns and ears behind the heads
|
||||||
|
p.poly(&[(12.5, 7.5), (11.0, 2.5), (14.5, 6.0)], a(BONE));
|
||||||
|
p.poly(&[(19.5, 7.5), (21.0, 2.5), (17.5, 6.0)], a(BONE));
|
||||||
|
p.poly(&[(6.0, 14.0), (3.5, 10.0), (8.0, 12.5)], a(GREEN));
|
||||||
|
p.poly(&[(26.0, 14.0), (28.5, 10.0), (24.0, 12.5)], a(PURPLE));
|
||||||
|
let heads: [(f32, f32, u8); 6] = [(16.0, 10.0, RED), (9.0, 16.5, GREEN), (23.0, 16.5, PURPLE), (16.0, 16.0, BONE), (10.5, 22.0, STEEL), (21.5, 22.0, BRICK)];
|
||||||
|
for (x, y, m) in heads {
|
||||||
|
p.disc(x, y, 4.3, f(IRON, 0));
|
||||||
|
p.disc(x, y, 3.6, a(m));
|
||||||
|
let (ex, ey) = (x as i32, y as i32);
|
||||||
|
p.px(ex - 2, ey, f(SUN, 2));
|
||||||
|
p.px(ex + 1, ey, f(SUN, 2));
|
||||||
|
p.px(ex - 1, ey + 2, f(IRON, 0));
|
||||||
|
p.px(ex, ey + 2, f(IRON, 0));
|
||||||
|
}
|
||||||
|
// the cupped hand below
|
||||||
|
p.poly(&[(2.0, 22.0), (6.0, 25.0), (26.0, 25.0), (30.0, 21.0), (29.0, 27.0), (22.0, 30.5), (9.0, 30.5), (3.0, 27.0)], a(SKIN));
|
||||||
|
p.line(30.0, 21.0, 29.0, 15.0, 3.0, a(SKIN));
|
||||||
|
p.px(28, 13, f(BONE, 3));
|
||||||
|
p.line(2.0, 22.0, 3.0, 17.0, 3.0, a(SKIN));
|
||||||
|
p.px(3, 15, f(BONE, 3));
|
||||||
|
p.line(8.0, 27.5, 24.0, 27.5, 0.8, f(SKIN, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// every badge in order of its index
|
||||||
|
pub fn gen_badges() -> Image {
|
||||||
|
let rows = (COUNT + COLS - 1) / COLS;
|
||||||
|
let mut img = Image::gen_image_color((COLS * SIZE) as u16, (rows * SIZE) as u16, Color::new(0.0, 0.0, 0.0, 0.0));
|
||||||
|
let painters: [fn(&mut Pad); COUNT as usize] = [
|
||||||
|
heroes, bosses, dug, gold, wood, walls, rooms, spells, slaps, traps, towers, drowned, burnt, siege, captured, drawbridge, wins, missions, coop, minutes, flawless, speedrun, campaign, surface, defeat, legend, wyrm, fullhand,
|
||||||
|
];
|
||||||
|
for (i, paint) in painters.iter().enumerate() {
|
||||||
|
let mut p = Pad::new();
|
||||||
|
paint(&mut p);
|
||||||
|
p.blit(&mut img, i as u32);
|
||||||
|
}
|
||||||
|
img
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn every_badge_is_drawn() {
|
||||||
|
let img = gen_badges();
|
||||||
|
for i in 0..COUNT {
|
||||||
|
let r = badge_rect(i);
|
||||||
|
let mut opaque = 0;
|
||||||
|
for y in 0..SIZE {
|
||||||
|
for x in 0..SIZE {
|
||||||
|
if img.get_pixel(r.x as u32 + x, r.y as u32 + y).a > 0.5 {
|
||||||
|
opaque += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(opaque > 150, "badge {} has only {} pixels", i, opaque);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn export_badges_png() {
|
||||||
|
// look at the art: cargo test export_badges_png -- --ignored
|
||||||
|
let img = gen_badges();
|
||||||
|
let mut big = Image::gen_image_color(img.width * 4, img.height * 4, Color::new(0.1, 0.07, 0.07, 1.0));
|
||||||
|
for y in 0..img.height as u32 * 4 {
|
||||||
|
for x in 0..img.width as u32 * 4 {
|
||||||
|
let c = img.get_pixel(x / 4, img.height as u32 - 1 - y / 4); // export_png flips
|
||||||
|
if c.a > 0.0 {
|
||||||
|
big.set_pixel(x, y, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
big.export_png("/tmp/badges.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-1
@@ -99,6 +99,7 @@ pub struct Gfx {
|
|||||||
pub atlas: Texture2D,
|
pub atlas: Texture2D,
|
||||||
pub sprites: Texture2D,
|
pub sprites: Texture2D,
|
||||||
pub cursors: Texture2D, // mouse pointer frames, 16x16 each (see CURSOR_*)
|
pub cursors: Texture2D, // mouse pointer frames, 16x16 each (see CURSOR_*)
|
||||||
|
pub badges: Texture2D, // achievement badges, 32x32 each (see badges.rs)
|
||||||
pub font: Texture2D, // Dinowyde (glyphs of 6x8 px ordered by Unicode, see font_slot) and DINOBYTE
|
pub font: Texture2D, // Dinowyde (glyphs of 6x8 px ordered by Unicode, see font_slot) and DINOBYTE
|
||||||
pub font_map: FontMap, // which glyphs the two fonts hold
|
pub font_map: FontMap, // which glyphs the two fonts hold
|
||||||
}
|
}
|
||||||
@@ -2106,7 +2107,9 @@ impl Gfx {
|
|||||||
font.set_filter(FilterMode::Nearest);
|
font.set_filter(FilterMode::Nearest);
|
||||||
let cursors = Texture2D::from_image(&gen_cursors());
|
let cursors = Texture2D::from_image(&gen_cursors());
|
||||||
cursors.set_filter(FilterMode::Nearest);
|
cursors.set_filter(FilterMode::Nearest);
|
||||||
Gfx { atlas, sprites, cursors, font, font_map }
|
let badges = Texture2D::from_image(&crate::badges::gen_badges());
|
||||||
|
badges.set_filter(FilterMode::Nearest);
|
||||||
|
Gfx { atlas, sprites, cursors, badges, font, font_map }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cell(id: u32) -> Rect {
|
pub fn cell(id: u32) -> Rect {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ mod achievements;
|
|||||||
mod ai;
|
mod ai;
|
||||||
mod armory;
|
mod armory;
|
||||||
mod audio;
|
mod audio;
|
||||||
|
mod badges;
|
||||||
mod campaign;
|
mod campaign;
|
||||||
mod campaign_doc;
|
mod campaign_doc;
|
||||||
mod cloud;
|
mod cloud;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ impl Menu {
|
|||||||
draw_rectangle(ix, ry, pic, pic, Color::new(0.08, 0.05, 0.05, 1.0));
|
draw_rectangle(ix, ry, pic, pic, Color::new(0.08, 0.05, 0.05, 1.0));
|
||||||
draw_rectangle_lines(ix, ry, pic, pic, 3.0, bc);
|
draw_rectangle_lines(ix, ry, pic, pic, 3.0, bc);
|
||||||
let tint = if got { WHITE } else { Color::new(0.3, 0.3, 0.3, 1.0) };
|
let tint = if got { WHITE } else { Color::new(0.3, 0.3, 0.3, 1.0) };
|
||||||
draw_texture_ex(&gfx.atlas, ix + 4.0, ry + 4.0, tint, DrawTextureParams { source: Some(crate::gfx::Gfx::cell(*icon)), dest_size: Some(vec2(pic - 8.0, pic - 8.0)), ..Default::default() });
|
draw_texture_ex(&gfx.badges, ix + 4.0, ry + 4.0, tint, DrawTextureParams { source: Some(crate::badges::badge_rect(*icon)), dest_size: Some(vec2(pic - 8.0, pic - 8.0)), ..Default::default() });
|
||||||
let tx = ix + pic + CW();
|
let tx = ix + pic + CW();
|
||||||
let (name, desc) = if *hidden && !got { ("???".to_string(), tr("ach.hidden")) } else { texts(id) };
|
let (name, desc) = if *hidden && !got { ("???".to_string(), tr("ach.hidden")) } else { texts(id) };
|
||||||
gfx.text(&name, tx, ry + CH() * 0.1, S(), if got { GOLD } else { TXT });
|
gfx.text(&name, tx, ry + CH() * 0.1, S(), if got { GOLD } else { TXT });
|
||||||
|
|||||||
Reference in new issue
Block a user