diff --git a/.claude/skills/pixellab/SKILL.md b/.claude/skills/pixellab/SKILL.md index 4f2dee53..0d7cc78d 100644 --- a/.claude/skills/pixellab/SKILL.md +++ b/.claude/skills/pixellab/SKILL.md @@ -46,7 +46,15 @@ To add assets, add entries to `assets/pixellab/manifest.json` and re-run. - **Style suffix:** every prompt gets the shared style from the manifest `defaults.style` ("dark evil lair theme, muted palette with purple and teal accents..."). Change it in one place only. -- **Seed:** fixed at 1337 in the manifest for coherence across the set. +- **Terrain tiles are the exception:** the shared suffix's "readable + silhouette" makes the model put a focal object in every tile, which tiles + into visual noise. Terrain entries (floor/wall/rock) override `style` + per-item with a texture-swatch suffix ("...very low contrast, no focal + point, no objects, uniform"), describe content as "seamless tileable + texture of ...", and carry their own seed. Copy that pattern for any future + ground/wall-class tile. +- **Seed:** fixed at 1337 in the manifest for coherence across the set + (except terrain, above). - **Characters:** `"transparent": true`, `"view": "low top-down"`, `"direction": "south"`. Stored in `assets/pixellab/sprites/`. - **Tiles:** opaque, `"view": "high top-down"`. Stored in `assets/pixellab/tiles/`. diff --git a/assets/pixellab/manifest.json b/assets/pixellab/manifest.json index 32ed2070..b1d5f612 100644 --- a/assets/pixellab/manifest.json +++ b/assets/pixellab/manifest.json @@ -16,9 +16,9 @@ { "out": "sprites/agent_saboteur.png", "description": "saboteur agent in balaclava with explosive charges", "transparent": true, "view": "low top-down", "direction": "south" }, { "out": "sprites/agent_soldier.png", "description": "special forces soldier in green fatigues with rifle", "transparent": true, "view": "low top-down", "direction": "south" }, { "out": "sprites/agent_super.png", "description": "superhero agent in blue and gold suit with flowing cape", "transparent": true, "view": "low top-down", "direction": "south" }, - { "out": "tiles/floor.png", "description": "dark stone dungeon floor, seamless texture", "view": "high top-down" }, - { "out": "tiles/wall.png", "description": "dark metal-reinforced stone wall block", "view": "high top-down" }, - { "out": "tiles/rock.png", "description": "solid unexcavated dark rock and earth, seamless texture", "view": "high top-down" }, + { "out": "tiles/floor.png", "description": "seamless tileable texture of flat dark gray stone tiles, uniform repeating pattern, edge to edge", "style": ", 32x32 pixel art texture swatch, very low contrast, muted dark palette, no focal point, no objects, uniform", "seed": 42, "view": "high top-down" }, + { "out": "tiles/wall.png", "description": "seamless tileable texture of dark stone brick masonry, uniform repeating pattern, edge to edge", "style": ", 32x32 pixel art texture swatch, very low contrast, muted dark palette, no focal point, no objects, uniform", "seed": 43, "view": "high top-down" }, + { "out": "tiles/rock.png", "description": "seamless tileable texture of nearly black packed earth and rock, uniform repeating pattern, edge to edge", "style": ", 32x32 pixel art texture swatch, very low contrast, muted dark palette, no focal point, no objects, uniform", "seed": 44, "view": "high top-down" }, { "out": "tiles/throne.png", "description": "evil golden throne on a purple dais", "view": "high top-down" }, { "out": "tiles/power_core.png", "description": "glowing cyan reactor core machine", "view": "high top-down" }, { "out": "tiles/lab.png", "description": "laboratory bench with bubbling green chemicals", "view": "high top-down" }, diff --git a/assets/pixellab/tiles/floor.png b/assets/pixellab/tiles/floor.png index 2cd9392059ff46f4ee6a44172a0041d534c10ce3..60237049e3f3877f1a4d79315ec6c7da9e77a32b 100644 GIT binary patch delta 2586 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 alZ8J#iY@ua^^<{tfx*+&&t;ucLK6UteEBW_ delta 2779 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 V{<41K&%nUI;OXk;vd$@?2>@EiRk{EG diff --git a/assets/pixellab/tiles/rock.png b/assets/pixellab/tiles/rock.png index 5c215a2cff1d858d11529af161694a077ed16e9e..588559870abb80ab203a15549d80b15bf6aeb122 100644 GIT binary patch delta 2156 zcmX>j@Gvu&x;H-$Foo&Kj#(lq_txmW6Ldw%K6eQ$gI_uP%y z-qGm{P8?RY?Rzp6TNJvKHvL{3HaF=bmlH?LedYrOKELj9?n)D=cU5%CV3;x4s<%uy z_VUxc3SUmnX0@_Se`42QWuANad(9*jwgn*?N+GT6vuFQ#y8Hc-4+rnce!KTBcoWki z3q{YQU-#JmE`F=K@83Vpz&Q*bj_cPSpS`{P$Vmo!4#pSN>Nmo3T2^h?RMLJo{C`5u zyJNp*CeE0m)4lF>(sXZzPYZ+U70)N`J-FQ1{6_wsgWqjiE^N>iwYi^MmDTmIz+#bn zg`ND_xb*K|yS;yTt*rQG#K`ctE%DKBIs3PJ*&bXw>pyeG42Kn549C}sF}%P1+a*wC zdj9%d{0=)4dSBe0t2gic$Ey7Be-+F4AGfr=p1)uf(?Ts3&odP#H*HvdBj^5Y#V)1# zx?h#^%a&`i&RUi{-Mi(vclOhw8I!G6d!MdY&Nx?idfD>Xzn=b?>9x}1e5s(*T<$tCKb zG27~M_;bb=-)4u@OUr*Lz8o&NswqHpU+uq7&6a2`Kw`AYl zmqwO`j@NS^&1IV;|9EEMo1)I0nbPG36C5svv|e4B%E0&9wCY`h%FlZ9M2QLJyl;v+ zFFy>JC7e*2IkWNo#S)p56MW{I^G5Cu(MXa|OE%`#PJcN`%&kYgWdf^_lUtxik=Wv% zmCA0W>ZN-wJJ?NrsUPRm5x-`ATAJsi8Gic5^qvQZOzoW-T6d?mGe9(Itrh>fcMoK& zzx+{~_kN*<*1q*gPlNtGUcc{A{pRQ#MGeSr(zkNxBhSZua7yN zMw{2(+1I+L$9t3IUAe?LF-p-}!&E(!%&t$nsNTZjz5B9j@YMe+&(D0BujYAXN9@Y_ zgr`Mo^mK(?6~Bgg7g>2G`5aCuTczX~p(V1h$Z}f#J*6Jud!fO_huDS0bh+P~tW6Bp znlvYI&W7*bWbaPix5mCKH}mzIMU{F&K9a$jQDVJi#<9CTYnZB+9&yZ1vofw5 zd&(L#x?X&}^`gx%C|*~z>r*a+n4Mqe_k!2o>uWwg$@~=>c;ee$S--s#JcA#9c){Qs zdckz=&Y6)6a`it|&&Taku&Up9^tHK)<$SyU;_I$2e7|$j1ZnO5ABEqavJ2mu%H@09 z^ZWi~$|0_^UVXhM7yUZ^!?OpXXZ?bO^m&*?TxAV&S58=d_hrzQq7|zW9{d#Y_&TjQ zR!{I~=bg&O^`~ZVEh*G&NqBjTscqdt|0Uk#cd`;C)YgA5D`aQ>l*ZUlwc1TWea)SH ztPKmZRXlY*FOJGCxRd2+{QVB&tW^ft{+_lCvwStW&BcHI?p_{}&D->F!k$clHbz%% zR~`rjm(>{Uiazf-5|@wn2xS=HfwvHsbvSIxbp-&Va>{kZMi-fLey$lW5XblT?o1`1MWxoYCh!YwP7URTNL$+q`#Tq@Dy{ph&2c@!dSDg9eK-ZcI-*x<>T? zN8s-|NdrYd!5zevVpS1frl3lE{)_%$bD*Nd0C?K@v_GkUdY8=Qup+^r`11W z?R(ai!~=DY&sG*J{eFD&KaH+af38aYJl`|n`?Yt)Rx+Q~MJ=wrq%Ps6;;Hj>dHvyq zDSHK)j=nZ$;Aoe#y*~A}?+UJ|L4j3=)`e(9i6u|{xzt!OX%~OO3#+PEiStEy#D5C@gI&J%b#zk{jb=(uKRT<$6*HF z%S$a8->qBs@$BT~iwkW&*qVeMH1WyY*3)wOdU$3De`eXNlqX+m{NMfV{?+(ASkJ%k zPR@lK^LoR+rxA6Z!>!Zzy`MPe=!;ng?@mp%QeS8DR>f1NdR}jQJt@~tK9$2aj2na;BfM2;%gzka^=te&EY=bkq|-*6pzFe&crqw5zgr;6W*`rouTsyA=EKKXWtiTS+y3a&ZA_O# w24mS{Otbn9ADde(v;E%3e>*xYSALdn-o>iw_F%_-1_lNOPgg&ebxsLQ0Nb2N1ONa4 delta 2628 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 P3=9mOu6{1-oD!M%?e3@*G98$% zKmX?K+lw{081~iw_uGHpz3y_3*Xo;02dtJ~pKNjP)+B=-3#(tJ>i_*?yf&@s|BoLt z?$*tJ_)>H4T-iek3=1^4&bZDpnw`qP;&`CK<`|2cyj=hBHFnWUS7~X58_w#FxF7NS z*)O;6F-tTV)C?@1|ET=eJo~k@Pr?qZbqq7k8ZFYewD(hV<^O+wb7w6p*&Tbxfa$@L z6o>7%ulg>&*zxU~ndh7d;yc40)vpO%Qe|zu>wcuSKpV%VPiKr~1xh4dGFhtQeeChZ zdpf5ld0H(jy84K<@|^3eSJ$nd%Y_t8T-GJhuyLu9_IaO6o$Iv2bps}yV(9DJcj^82 zjprV&3FX^zJJ&U-eEa$Cv%Yh-9x3E;n4@Cxn&VJ|e|!6zd!et^EZw5IfA62U&(F_p zsdsdkqvCV!{QKyFf`S6=zo(Z;W@l$JL}Y88p0stV=~|uHX(rLWJ%)c~{a++#z9J^{ z@tG$zhpwGod#g0Lr^spD>G;hXH#*K*w&ixN)$R3DnBTt5J){tj_Pwm^Mb3UD{USfM z=BTOvw%F9)3*qCqy|#GCkmeP~Qv+@_sdSN@$d$3J6h6#s;e z-?@W=R)(xyGTSfv1jqg}MN9wI#g{&~{zv?7-t@I+*GjRk(bep7vJ_|&yf*EK=b`E6 z=f5nmVsJ@ZB2%$(C#Q~y^66#264PqR+?4a~e7}&q->2Eo?fT-20ouCjxU|AI{>lkW zEIk)kr4$k>YJa}I{>6Xs%&6xL_Wy61DCZry75rMpb523Ym#Kcs7hi8--p9wmJ}G6b znPQ@Z?JDNUB~Eub4EKGqd_Lp+^C(`2dGW8l{;XM7{nu+w@u~$~^K?UPpWba+w`cJV zEd!?X?YA#$p6HIbxp#m4fqeaX_JFzK%e<%F&GWbX@bbe1^Zb7*`bG6`E90KT#Kmb% zPk6|@R98GWV47;6h+tddTKk(Ot7E5UM$~RwWyNr1)vP)G8Ts2wn^+iUG9~-)&YYTR zKIPso+bJ&vw(+wue{E9Wk(@X0(#w(zvuLa5u7L(Jd^g{I=WJ$p9dp*XY4=9+eaF1> z8J{+74Xe&sdv#SU`<{LK;wF52Q$Ksx{6ELi)7Rv$VYYeKHrMZY*ghjap|-=pkB%++ z_0%Rng!OEix200(kIDz{_uGH`_SX7okOYhWGsf34%T{T9`mv|pUf3}v6Nq2r7`_;r;UYR({ zm$T`Cqnv;V!&&kArbEnp2mYG;ka_X;jm-tdm*0b*H~qiNn7MHtx7*@{`xLsAmfZ}A z^*+bAIkL^r?Rs6Gz|zm>SvO5fXYuXK3Ju-(>Eq+$(Vi}+tsW>R=l)Dlt9cXiVG5tk zgNFOXg|_>j-#@?a>a{wxDOx@1I*)Ygg be)4z4sHz1$ihRewz`)??>gTe~DWM4f0fqNT delta 2680 zcmaFP-5@$axt`12)5S5QVovMy$_X-GOOM;ve*bdE+PYf)(mEAyh0Io!lqU@e3du}2 zZuALSlsV1hy3tn{6+Wu!IcW>^jN~_vcz011ZL@IM)aAj!h)xWRn-q+s$_uV>g|NkquUKdI6bbIqcX~I=izU-^4IjcFkjvZCHxAEuX z6N1bQ=VLxco(i4+{?EVEf6w~#x7t-bm?^)p_;~n)NzQk^AB_Il9&h7#C+s4dy+cX{ z@6D8HF3*jQWQjH`d~2@YZB(|Gv8!!V%d*Q-ta8FqvQ)i!0b7T zZN&`s@3?Sj^5mHxj&04nwwj}pzh3djZTbJ)_wN^epLB&KZ!_zQ?)?>Q-%szqTWKw1 zCCB^7ZBknBQiES=vNPq5G*&7o-kRYFpY9gU37V?h&ZpbCC{Sx++_qV1I-a)n%m;Sw zJp7)qW5c0{qwT%3C4{P8?0fZV1Ix^B8~9hHnz%Z|RDPbXu*zy{XFX@x`#YU0PJ7Rh z*wvLe&9dT&M&8{;K{|=kbdJ^kj^=tlSpz|{3n=JU_W@qDF8kwK1=)?U>9`6#vi z*z5Vf#2BI$DBRKuQhLJYJw@>GN{-j>_RW6CpL;C7MRkY7k&pkYTJPQaXP~8g=*t(& zqT}`K4#!_@_1J4`VL0>0P8reN^?gfT?&x!!Y_s3I{A0vR#cz8{o6Ht+dtSaM!kGSs z!*_jW#_khwi?mJb_Dp*5h3VRzFG2#z`TKltx1E*v#2OH#<#|&xQH*Pe)vm%^Ky0)bwR;%=+9#>Dg_6A1A*kE?pF!ll-vvu0>sQ$p5TY$?AfyuNr;LlDuAD z_Ttxrw0rt>i#s!?876r$#N2i~ef>qmR>n1!T$2}@Xq#$nRjMx9&vP)_FzcSpk%x2t zSVUjr{rL3tJ7>MN+xl0{dh&m=Z0h<^*Is)&Kju@7o$$Ksi5{nFOt!XWxhMyi>ReX7 zdAZ;4PP;_tj@$xwZ~f_ED>X_d`Ke#Nsuj17ss84KeYLh;HI@8DM#Y*8M>}FBwFc|& z;W+T-eEgDvPo{I?_jj4wHm}#bA;?*(wY4ob%ty&PhmCkJbvF;r=dbDOeZ{6VRp>9XGsBCSjxy&R z_4JgCOh48qMkrlA|6k+_LwtYRO#$ako7B!HK0CI;= zqI0{-zVYUUsBa;jM~zgDEbg~!iQfO=(VKU7IpQ{#&OPsBcG=x_`f;<@3@Vq{ zdK-^bUE7*_sPxd*jzyEM1kE{h{-LE>>j9O0NvaG_?^vW6&Y7Te;AH);L#l$__aqht zISGAMnUo$8F~`Zz#ZX3CyM9Ba7FXO-hs}FB9^9DnDtJ$%<;_d_(cM1N&Lwd1rAtkg zF?Kg}_h(+9Iw_-5rE413>;TcyJG-vlVr*1tW$ zYQq{~@MscUWqewpBX!aBI3>+oxL|4;MT`7Y#Mt^D<@ z{bf#fLB)U1OG%Tf^KYCJ6!lwvtm5hP-7lqYp7`@cXHr^#b-B>S#oRYPN+%q?`1`$+ zXlc^+JD!`_^6b`ZP5rLNo ze^`(S73reD9_TI|~_a^TwTfAh*5RqJoxJ-6@rw~q_ID*H@7 zeT0|QUM~M@OKpwJb*%-Z-wZC9`Ya85-uYa9@#1XDSt8H%?dtejBPY+__fcilicJ-- z1Z;mPY`%I~B1>lf+S&JfXK!3Rbt%Ixw`;~lXJt=#wQOepwz2H2*PEi=)($77d3@L1 z`wQQ$TD{`A`prx7?_O6Z7}cwMWBPDH`+U&?L$PoDt1j4-G2JZ9WHHTA{8O2^yGA=` z4X>}b6T_+%kALv~TK6aU(dlW9A;&kcGQ9HbF4-l1Tjzq#OrDZ9)xucu)d7#b`GzZt z%34j5^gDBQ;>FZwDl?zoyW_lZ&T^Y73Ec@D%Y;*=uyp6OZkp(L%+dUkxAB4%^_(rs zm=AoIUpMRG^m*w&cKIu1D7~G&{=f>hId#udtYMGr1wU(j|?WQN!(@iat4z_Ufr~F zcMR11WgnJ2)0zABuu4nAr0Qvo@0W4&#uQC35j_8`@v*n@hjtg=We2ysxUpqwM3unt zwMphoCb5MF%2rwM&Y!nWF=(G(&_#{iW+vHZbmk=2JyN*pwJ_DY?}W+PXVvEp=l=iC zv_qof*1k!7jf`i!TBj&HauMkX5!)T*Xta8YHtV!<-U+5gGP3q 0) render tint-dimmed to 0.45. - Nearest-neighbor sampling is set globally via - `ImagePlugin::default_nearest()` — don't load pixel art without it. + `ImagePlugin::default_nearest()` — don't load pixel art without it. Equally + important: the camera renders at native art resolution (default ortho scale + 0.5 = 32px art in 32 screen px, following the villain/build cursor, `[`/`]` + to zoom). Nearest-neighbor *downscaling* (32px art in 16px cells) decimates + pixels into shimmer — never render below native scale. +- The floor texture is authored light for readability and tinted toward the + palette in `Art::load` (0.75, 0.72, 0.85). +- Player-facing strings must stay ASCII: Bevy's default font has no em dash + (renders as tofu). The sim's log strings are ASCII-only for this reason. - The terminal frontend uses none of this (chars + colors). +- To preview composition without launching the app: PIL mock in the style of + `scratchpad/lair-mock.png` (tile the real PNGs, alpha-composite sprites, + 2x nearest upscale) — useful because headless screenshot of the Bevy window + is blocked by macOS screen-recording permissions. ## Adding an asset, end to end diff --git a/src/bin/bevy.rs b/src/bin/bevy.rs index 19196b06..554738b1 100644 --- a/src/bin/bevy.rs +++ b/src/bin/bevy.rs @@ -49,7 +49,12 @@ impl Art { let door = tile("door"); let spike = tile("trap_spike"); - t(TileType::Floor, floor.clone(), Color::WHITE); + // Floor is authored light for readability; tint it toward the lair mood. + t( + TileType::Floor, + floor.clone(), + Color::srgb(0.75, 0.72, 0.85), + ); t(TileType::Wall, tile("wall"), Color::WHITE); t(TileType::Rock, tile("rock"), Color::WHITE); t(TileType::Throne, tile("throne"), Color::WHITE); @@ -186,6 +191,9 @@ impl Game { // ─── Components ────────────────────────────────────────────────────────────── +#[derive(Component)] +struct GameCamera; + #[derive(Component)] struct MapTile; @@ -232,6 +240,7 @@ fn main() { ( handle_input, advance_sim, + update_camera, render_map_tiles, render_units, render_ui, @@ -249,14 +258,17 @@ fn setup(mut commands: Commands, assets: Res, mut game: ResMut, + keyboard: Res>, + game: Res, + mut camera: Query<(&mut Transform, &mut Projection), With>, +) { + let Ok((mut transform, mut projection)) = camera.single_mut() else { + return; + }; + + // Zoom: ] in, [ out. Scale is world-units-per-pixel; smaller = closer. + if let Projection::Orthographic(ortho) = &mut *projection { + if keyboard.just_pressed(KeyCode::BracketRight) { + ortho.scale = (ortho.scale * 0.8).max(0.25); + } + if keyboard.just_pressed(KeyCode::BracketLeft) { + ortho.scale = (ortho.scale / 0.8).min(1.5); + } + } + + let target = if let Some(bm) = &game.build_mode { + grid_to_world(bm.cursor_x, bm.cursor_y, transform.translation.z) + } else { + grid_to_world( + game.sim.player.entity.x, + game.sim.player.entity.y, + transform.translation.z, + ) + }; + // Exponential smoothing, framerate-independent. + let t = 1.0 - (-8.0 * time.delta_secs()).exp(); + transform.translation = transform.translation.lerp(target, t); +} + // ─── Simulation clock ──────────────────────────────────────────────────────── fn advance_sim(time: Res