Bestiary portraits in the high-res style: a painted pixel-art bust for every creature
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a24fc51605
commit
8d2362fc11
41 files changed
+308
-1
No files matched your search
@@ -28,6 +28,7 @@ mod menu;
|
||||
mod netplay;
|
||||
mod personality;
|
||||
mod platform;
|
||||
mod portraits;
|
||||
mod orders;
|
||||
mod particles;
|
||||
mod props;
|
||||
|
||||
+25
-1
@@ -174,7 +174,19 @@ impl Menu {
|
||||
_ => level,
|
||||
};
|
||||
match entry {
|
||||
Entry::Unit(kind, _) => draw_sprite(gfx, kind, show_level, x + k, y + k, pic),
|
||||
Entry::Unit(kind, _) => {
|
||||
// the high-res style shows the creature's portrait filling the box
|
||||
let box_size = pic + 2.0 * k;
|
||||
match crate::portraits::rect(kind).filter(|_| crate::gfx::high_res_ui()) {
|
||||
Some(src) => {
|
||||
let m = 2.0;
|
||||
draw_texture_ex(&crate::portraits::atlas(), x + m, y + m, WHITE, DrawTextureParams { source: Some(src), dest_size: Some(vec2(box_size - 2.0 * m, box_size - 2.0 * m)), ..Default::default() });
|
||||
draw_rectangle_lines(x + m, y + m, box_size - 2.0 * m, box_size - 2.0 * m, 2.0, Color::new(0.0, 0.0, 0.0, 0.8));
|
||||
draw_rectangle_lines(x, y, box_size, box_size, 2.0, FRAME);
|
||||
}
|
||||
None => draw_sprite(gfx, kind, show_level, x + k, y + k, pic),
|
||||
}
|
||||
}
|
||||
Entry::Rank(l) => draw_sprite(gfx, EK::Ogre, l, x + k, y + k, pic),
|
||||
Entry::Building(i) => {
|
||||
let icon = tool_info(TOOLS[i]).icon;
|
||||
@@ -321,3 +333,15 @@ fn draw_sprite(gfx: &Gfx, kind: EK, level: u32, x: f32, y: f32, size: f32) {
|
||||
let src = Gfx::sprite_cell(kind, false, level);
|
||||
draw_texture_ex(&gfx.sprites, x, y, WHITE, DrawTextureParams { source: Some(src), dest_size: Some(vec2(size, size)), ..Default::default() });
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn every_bestiary_unit_has_a_portrait() {
|
||||
for &k in CREATURES.iter().chain(HUMANS.iter()) {
|
||||
assert!(crate::portraits::rect(k).is_some(), "no portrait for {:?}", k);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Bestiary portraits for the high-res style: one 128x128 pixel-art bust per creature kind in one
|
||||
// atlas (8 columns), generated from the creatures' sprites (see art/generated/portraits/).
|
||||
|
||||
use crate::game::EK;
|
||||
use macroquad::prelude::*;
|
||||
|
||||
const PNG: &[u8] = include_bytes!("../assets/portraits/portraits.png");
|
||||
pub const CELL: f32 = 128.0;
|
||||
const COLS: usize = 8;
|
||||
|
||||
// the atlas order, the same as assets/portraits/order.txt
|
||||
const ORDER: [EK; 31] = [
|
||||
EK::Worker, EK::Kobold, EK::Spider, EK::Mage, EK::Ogre, EK::Knight, EK::Archer, EK::Priest,
|
||||
EK::Dwarf, EK::Paladin, EK::Jailer, EK::Priestess, EK::Chainbreaker, EK::Bat, EK::Gargoyle, EK::Owl,
|
||||
EK::Wyrm, EK::Cogling, EK::Revenant, EK::Giant, EK::Fairy, EK::BattleMage, EK::Ram, EK::Catapult,
|
||||
EK::CaveBear, EK::Beetle, EK::Trader, EK::Thief, EK::Monk, EK::Ranger, EK::Inquisitor,
|
||||
];
|
||||
|
||||
thread_local! {
|
||||
static ATLAS: Texture2D = {
|
||||
let t = Texture2D::from_file_with_format(PNG, Some(ImageFormat::Png));
|
||||
t.set_filter(FilterMode::Nearest);
|
||||
t
|
||||
};
|
||||
}
|
||||
|
||||
// the atlas texture (loaded on first use)
|
||||
pub fn atlas() -> Texture2D {
|
||||
ATLAS.with(|t| t.clone())
|
||||
}
|
||||
|
||||
// the source rect of a kind's portrait in the atlas, None for kinds without one
|
||||
pub fn rect(kind: EK) -> Option<Rect> {
|
||||
let i = ORDER.iter().position(|&k| k == kind)?;
|
||||
Some(Rect::new((i % COLS) as f32 * CELL, (i / COLS) as f32 * CELL, CELL, CELL))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn atlas_decodes_with_room_for_every_portrait() {
|
||||
let img = Image::from_file_with_format(PNG, Some(ImageFormat::Png)).expect("atlas decodes");
|
||||
for &k in ORDER.iter() {
|
||||
let r = rect(k).unwrap();
|
||||
assert!(r.x + r.w <= img.width as f32 && r.y + r.h <= img.height as f32, "{:?} outside the atlas", k);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn order_matches_list() {
|
||||
let list: Vec<&str> = include_str!("../assets/portraits/order.txt").lines().collect();
|
||||
let ours: Vec<String> = ORDER.iter().map(|k| format!("{:?}", k)).collect();
|
||||
assert_eq!(list, ours);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user