From 3785885abe73c3675fa88707d5fadd6063581c77 Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Sun, 4 Jan 2026 15:34:56 +1100 Subject: [PATCH] Add character (warrior) and rat/bat sprites --- public/bat.png | Bin 0 -> 3456 bytes public/rat.png | Bin 0 -> 6493 bytes public/warrior.png | Bin 0 -> 29322 bytes src/game/generator.ts | 4 +- src/game/simulation.ts | 9 ++- src/game/types.ts | 4 +- src/scenes/DungeonRenderer.ts | 137 +++++++++++++++++++++++++++++++++- src/scenes/GameScene.ts | 9 +++ 8 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 public/bat.png create mode 100644 public/rat.png create mode 100644 public/warrior.png diff --git a/public/bat.png b/public/bat.png new file mode 100644 index 0000000000000000000000000000000000000000..d1104c422c99820d44d2c2297aea7eeea1f7ed0f GIT binary patch literal 3456 zcmV-`4S({9P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z00083Nkl#mS z|C5n120K29PX_!k>BR7*N-l!PS?=g<*uaCNsxVWz~QvG?vL zwTK|@Oe$tmQKW2+cpg#x06-!V+T@i8;zW{jS@JdUISZsFl5#h03w{B2tp1drMTa2F zP!tQ^^S>>C+`QI)an*(MDgRnBOV};sr$~^jQrLU(Tgowba|EI{Wi7(*BGkq{yv3}g;h)a=P5<*R|pW!i(T8Pgi zfMUh_W2*U8z$mwQ)Z)|F6dhZ1H|?64$5x(;GqsYv2q%$0n5{&AkJ|*Q5s)P}ck})+ zZR!n+!?_r9tv4c1>+{*_J+IX3`wY zsb};Dc&$a=w|zz!6Kg@|IWB zB0_!NW9<>}sRgjKm_p*0j2pgl1R|$?pICZ5RRIu@EiA@Wl6bdo&cDTPu77UbYu#_F i2-YV5)d60HzXJfGpWC;y^>CU10000l zpsG4VrYQ+%2?Y^WKUE4fV892E+o=wS0*gEVpR&H467Z!N=$Ql&)C0JL0H0P=I46MV3s?*@ zGkXDHnE<)MnZCs7^IFgj7YeD&dWlvRQTbqFTpo8EeSKbfj!9*z=j6hc=$2VBynWu8 z)GvYsNq(J=13*C%1ueylW_gY5JReV_?-dJJfgn5OPwoI-kP<)#p+P4;*)ge%hh>0(BTo#a@GIWAHTJ{miZp6HO zdM(6~ZY2LKar1gPI)d>qWruOeEE$)h(5zw%nB#G&GQ+PKdzCOrKeB*FqR6$FRFj01 z__bqn!Foz`XOeHxMI%iV+LFYFffpfn5gm&BDZ%wxl0OL>VXfsNG+_!w1V3y%__Goy z3$lN_{YE1hD_pqqqspFSIYA0G#MiU?6h~m-4Il4dKlJNMZoFjcv9CI<6eBN^P59^)KP_c3$!J;g>9ShLRRl{ zmiB6Sq3#o*1S4ooVUu>9R%AJu;QK7miBL*q)smW`)L9yPD*G#kx?PX9C#$LS8tFgu zXU6svchh$s4T5qt0d4n*BjGKs4&y5YuL&#GD)?%?(J=@nu%>SC;%0Ors)~3YEHuFObz;fy z16O;-Jze`p$;ygCQJ8>=fPRDad5WDh&PIm&b&iJy-VR4u`guUi8uf@-b& zwkA%MY3XSORZ3M_Rf-3l!nH?bxuu=*?S{9(#Ii#w>?Z7r@PtXM+#Q`Ioz*h@GD>wx zvCCTCN~e-%h3#^l-7aOv8F^i1L3wJfWozD@JO60?A@{oX&i~3T6$uTn8wgpj5rmUZ zCB6%}yGqbOh7hM(Fw=e+Sczq-W%_zpQtPzD=Q)Kq1(H~YL6Z%Z4d+4U!Ov{iY>aHG z>ig=)S=#EYrJSYWYT{}`6XO#_6Z4ru*^>MVFLttbvKO+E%@#&pM%+#1O)a*{^_xbe zhR01EF5yO^h9w4iMy3r4HMupc^X)b5WlzdlHIg-|^5)_FUrw7wnkt&9zicgHBWjzR zoBS=|R#foj@3sBM$(_mmZT@lhk~l<>OT<-lMsxx1zP%+Edo8w>HJNiP?f?BEZ4UxB zm;y~L3T!lyjd2b*NMVDFG}|K!r-*0SOJnxb_) zDneXRmAsA;%OY)pZPJ6hc823~2Dt{xZ&YbTdAVwZUEXvd?~cwRf9L)t*zl#wfDD_u zc^6LhU4)lWR`8N?i>1k^MQOZD`6CPBDyNKPJtOmEjmP)yqw9k+L8gVLqZVN%T=W`e zN?htbpAM5cguGe43zTqgwT7`*BzC9~sjX&<1{-FIv~))-wO9XSXnqS)ii(@A{#CLMdUt{qKJ;EB}Y)D zQv@z@taxobi*z0B&Rj|PR#7$3YBOawW4E`x#*=4EoK~5>olfjRw6y$<2>JTk(dA!x z1SEZe1JNGZ1Z(}=Ez#0pTQ)K|Aug?3tb3vRPgnSOX0_3B;o4>T_?d6VU%|sUgmK5! zz0^n-LJv((ea>#q_E!0};YzwTx^nM2?bg|}M#I_tXb58l;~s~ZxM`vF=N#RPq^@?t z{PP1v;vt5vhujlS9wK2Py||pg-^^}u-kW<@c*rYyBC+KUvA)&oII|2N=-_k^M`+er zmO{2p)~&IXahvA2)b#xRn$Mjgf`KGQP{Pn#mR^-vtofL zYVzH&ocq^Q_dWI{9+GD@huU-6 zvuaySoLY~4IvaiMuJ)egiA=6ubb6msKF-hOiAt_^p8L37eZRUyQmzKPWxt;sYhUtY zKd$M}>@dHOIg(w1twzpC8$YZpo~#5q_IMVYPp>FmVTXrKJtSQwkif7KG84ukh$0U1 zUP_6I{+51wzjs##o3fc=%cFWIdKjyvqJ#uo&mAmGPG?LPrk|xJcRTx)-0$}sP}d;_ z`v&Q|M!W?0%`RTw9`$;zc+#DGMv`c5`hf#E9_??wUh4cPyG^D}PJYD1$T+{V7T`hr zC;7Bi(pComA65Ve3I%|hM^w890Ppz#VBZ1-SY%Jq}^cLe|-j#8GB)%99FGzom8 zvq16SV)a8w5sOF}k1;Q#(ykDX#lcF24AU0&y!s_Yaj1k2~zrdy)#j+0+tYJ1CgSl*==YBiR#t(h~_-a{dgtSNU zjUPxNkTYZzE8Ocr8?#CXvlq1|I&E=@?G~tC>uyPyYh5!Q2Fl`ZJ^8JKNz}NtOGNC* zq_OSl>INceJauZ($nRpO2%X^Y6~JQ?Nf=7*E4X_fe?gpdglJ~Zm7d;Yhrm+rM3`*7 zZ+~ASsCTC7^5?;eAtsAxhrApEzgffELfys^T4i3wtjaI{c@2uvvxD%4J&i9aY%*a2 zH@;bWMw{XWxSfs?nT|RYabRa_{ncp3^Tyudhw%7Qiu({7v8@B;^;L#U{GlS^A4(59 z{ck_0vw%gKGLPhMDtBrfy)t;sX26!43qPEEa0d>T9ZpU2at}shcGnu8^}7B24c%+6 zJ*GEl@yoSedQNz9_#pGO*x~`nv)?03c{t&^$RoX7^sSl}brf4|m3bd$T;2O86Qi#L zygnQ7EE&gP&S0amJJVH3(0=~F6T%AkRFR*u!W9-yad*}F1ZmVXrp@3LQRH8X(;_V8 zz=Qqc-mJkAkRsE?G=sudA1y8maQy_rOk$GVkt|@{P+V8PT=}cw& z^Zm4W?>8&aVs6W{aiUXn@?L!;r9uFF)0_`#V|Qw`nPpkMbePMF3lu4M$H&JeYcGsuxGruC@51DGNu}^>TMsy;hdLOF z+C8$X-89;;47m|FJ-2}H%JsJLmHyQKwHa*|mE`2$i+7jz?pU%~iO_l)qk-DiRd)T( z`R086@Bs(|q_b`M&&+uSibs-*<&8@k0))7v=WvxZ>6|ECv7K0a;rJm!ajIHwylUom ze2J&@s0}%IJ~EKx$Bh}?e!>RReoy&_U)|=NTInn&AZyQs=5QfdQmZr`4ubv*!5}YB z3=wrZ=TaFP<@fu*SG!Ak1(Raaq&d8v$zdYQ-<3z*US;ez>-Vy_saIxL)n>X1?*j86 z6YiGISg7USihl)s(@#SltCDY7qewP~UCN36V{{Es#h~13pnhekt<9IpTU{eJQjHEH z&YYlE@#@MG&(FpIIH=<|Nuz1N3CiDVO_qabFjjJ6Fk^_ehnwPgEhPR$FlmKnlVrdFL#y@>D(KZ@SuTVp$lvro%x7V z(BC3I!hE`cky(bSf38`;HCn_JvDNhc|PtJ&Jk zYe#e=9yE4*GalT$wR4}2Lh`EQdgAA>6cxO*-nv1n^2@f86Jrh5h3aVQ@9yVD3rP|H z)x$N;z=8eIMs{X5MOPUk+UR`4`~jQV+1N?GpVT4JzZY$pO082weztUWvFm==$y9Oi z#&mUAB`PqG>U*el1u$-xz~9?I6V+ce=jNIExDkXDYY216HH0o8^EI71T`JTZMCV{( z|JX|oQ);gg7uw=C8*7;xgqzELZg>2)8ge|SgZp*)@x2IK^Stq#Q+$+7iJuzczVLe~ z{ZGcR=cfmQh9lTH*RY5HqASg`ikmQGZSVKMre@=(s2F*CnOxVMG*olw%Oh=T*Gc(z zQI7zv{7#J-iviJ`Pm|gG>CtCM13u2qYV~zS1^>9L(VD0aip;fHmkdeKcTK$AOPIjS2wnAJFmIESdoC8mH_OaOB>Q(A7)ES zOH(i_|B~&BtAk%(Pa+NlU_Fx8TyAN=vE@c+LH#+F-%=Aa99ObS^DC5gq4cw4|=$Dz@A>f`p4=A}j?Z^Gc^#wUCLSg2^ z36_kN6&FwZ(6fb8pU$wr`}Y#8D+a@_SRdn*4A&GC6$K`xSDh9!Lu5>ru*HKQhz~_0 zGN7*RdLod#4D%X3`q$CQ%1UqyQ;tTNoQ&g=2?!Gp2iTd)UnuEd`(*Wn+VX_(kt&GE zPBJsBc~^amB_~ZG{{IE^=+sLvDQIQUt_g8exzk2wEf9N02!c&evDm z-+fSx&qPlz^&)p_Clrh;H)dw(WzJD-pw0Jq!uH;xA*=I-==Nb$_$4iI2Lw4)TzxoqnCqrcd=bxMa=wT?^YPvNAGVMX@K zcArS2p>hl;lzVSPm9T79+1#v^a6)`7aeH-sUIFQ|?dh2+hO*DK1TQu|ex|gP%`ckG zVZ+Gse>L-=qn0LqU~m?OlFey*@_dN73$Bx}Zy5nPfQyL^!VJcx*|(4~FjV*%BBBbm zBTdvoii?XYL-%#k$5`)k>GbvVdh>R1TF4~HTk&o0(d~#MnJZ!wYxkt(evaa#KmKS* zZ4-X}U-h!FQI8^8co}}SIbzWChC1fs#}X{}gKG1*QB5J}jLuNj_v~Jpk+sZ_o<>Wu?bVoR z{WbBtQ+m19coAbyHtTH9j^G>`X+1=gUC0kL^qGBM71Q2M{2cI8&Wv}C^pXARGGI_p z|9awaq@P-z;Nq$EGvVHotNwL2CD!md2c9jxAQYvN*)`L)GhU_(3kzGV)nsgTueuDO zsFK!&p92MqmfAnRc#qL~7a29XS4=?3-E((yP9$nA?8~u!<8ypv9!LaSBVZ)|r-eKQ zRrSqj;95rlkKa^`1t9M75VX+t= z?nx8bj#v-HGtv@Uuhl%w(O|z6s+qXs`hwnt0zM@r1-TeG~7^b6q zJd#8|q6|;v0Xc}+cy`*kk)}F)I^e|f`I@_!9apUC;78h1Uv1>jOsOrGf>v}(9`hf% z0zpavp{}J%fz9@D(w$eWK2;1QbS-(z7?b>{z0f5c4h*t>9tRC|6=y zOnMp^I^Xlh>VYwFq>8#s73(j#USs;6XhIEZ{uGZ6Zhr`%EL4Sx5Ow2_);wm+pf9cJ z_~@Oo_Vy32uk(U2QCE32bNW_$Zv-iQ!k09-IHLPLq?;(-*Obt@pf6RZ6_)8dITYEw|4^_hIrFy zV5Ke_3poy{4@lJNsUMuFX)`;> ze?P5ZZOy!;JiF&Hqqbk`wTuCd)(_qqaEJx!>oDLvPSS>BBfPTn^?zp@A4z~dp>nOk TeB5l*{}oV{*O04)T7>)$BiLpl literal 0 HcmV?d00001 diff --git a/public/warrior.png b/public/warrior.png new file mode 100644 index 0000000000000000000000000000000000000000..17c417e589971ea35753586a2ade9543013e5406 GIT binary patch literal 29322 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 P-y-FuRisMp7zO+vvxll) literal 0 HcmV?d00001 diff --git a/src/game/generator.ts b/src/game/generator.ts index edd35a7..65369b5 100644 --- a/src/game/generator.ts +++ b/src/game/generator.ts @@ -114,6 +114,7 @@ export function generateWorld(level: number, runState: RunState): { world: World actors.set(playerId, { id: playerId, isPlayer: true, + type: "player", pos: { x: playerX, y: playerY }, speed: GAME_CONFIG.player.speed, energy: 0, @@ -139,6 +140,7 @@ export function generateWorld(level: number, runState: RunState): { world: World actors.set(enemyId, { id: enemyId, isPlayer: false, + type: random() < 0.5 ? "rat" : "bat", pos: { x: enemyX, y: enemyY }, speed: GAME_CONFIG.enemy.minSpeed + Math.floor(random() * (GAME_CONFIG.enemy.maxSpeed - GAME_CONFIG.enemy.minSpeed)), energy: 0, @@ -155,7 +157,5 @@ export function generateWorld(level: number, runState: RunState): { world: World return { world: { width, height, tiles, actors, exit }, playerId }; } -// Backward compatibility - will be removed in Phase 2 -/** @deprecated Use generateWorld instead */ export const makeTestWorld = generateWorld; diff --git a/src/game/simulation.ts b/src/game/simulation.ts index 45ad357..020a93f 100644 --- a/src/game/simulation.ts +++ b/src/game/simulation.ts @@ -41,8 +41,15 @@ export function applyAction(w: World, actorId: EntityId, action: Action): SimEve }); if (target.stats.hp <= 0) { + events.push({ + type: "killed", + targetId: target.id, + killerId: actorId, + x: target.pos.x, + y: target.pos.y, + victimType: target.type + }); w.actors.delete(target.id); - events.push({ type: "killed", targetId: target.id, killerId: actorId }); } } else { events.push({ type: "waited", actorId }); // Missed or invalid target diff --git a/src/game/types.ts b/src/game/types.ts index 2322dd5..aac4f5b 100644 --- a/src/game/types.ts +++ b/src/game/types.ts @@ -10,11 +10,10 @@ export type Action = | { type: "wait" }; export type SimEvent = - | { type: "moved"; actorId: EntityId; from: Vec2; to: Vec2 } | { type: "moved"; actorId: EntityId; from: Vec2; to: Vec2 } | { type: "attacked"; attackerId: EntityId; targetId: EntityId } | { type: "damaged"; targetId: EntityId; amount: number; hp: number; x: number; y: number } - | { type: "killed"; targetId: EntityId; killerId: EntityId } + | { type: "killed"; targetId: EntityId; killerId: EntityId; x: number; y: number; victimType?: "player" | "rat" | "bat" } | { type: "waited"; actorId: EntityId }; export type Stats = { @@ -37,6 +36,7 @@ export type RunState = { export type Actor = { id: EntityId; isPlayer: boolean; + type?: "player" | "rat" | "bat"; pos: Vec2; speed: number; energy: number; diff --git a/src/scenes/DungeonRenderer.ts b/src/scenes/DungeonRenderer.ts index 5ded101..db4d9d0 100644 --- a/src/scenes/DungeonRenderer.ts +++ b/src/scenes/DungeonRenderer.ts @@ -7,6 +7,8 @@ import { GAME_CONFIG } from "../game/config/GameConfig"; export class DungeonRenderer { private scene: Phaser.Scene; private gfx: Phaser.GameObjects.Graphics; + private playerSprite?: Phaser.GameObjects.Sprite; + private enemySprites: Map = new Map(); // FOV private fov!: any; @@ -60,6 +62,84 @@ export class DungeonRenderer { this.visible = new Uint8Array(this.world.width * this.world.height); this.visibleStrength = new Float32Array(this.world.width * this.world.height); + // Setup player sprite + if (!this.playerSprite) { + this.playerSprite = this.scene.add.sprite(0, 0, "warrior", 0); + this.playerSprite.setDepth(100); + + // Calculate display size to fit within tile while maintaining 12:15 aspect ratio + const scale = TILE_SIZE / 15; // Fit height to tile size + this.playerSprite.setScale(scale); + + // Simple animations from PD source + this.scene.anims.create({ + key: 'warrior-idle', + frames: this.scene.anims.generateFrameNumbers('warrior', { frames: [0, 0, 0, 1, 0, 0, 1, 1] }), + frameRate: 2, + repeat: -1 + }); + + this.scene.anims.create({ + key: 'warrior-run', + frames: this.scene.anims.generateFrameNumbers('warrior', { frames: [2, 3, 4, 5, 6, 7] }), + frameRate: 15, + repeat: -1 + }); + + this.scene.anims.create({ + key: 'warrior-die', + frames: this.scene.anims.generateFrameNumbers('warrior', { frames: [8, 9, 10, 11, 12] }), + frameRate: 10, + repeat: 0 + }); + + this.playerSprite.play('warrior-idle'); + } + + // Rat animations + if (!this.scene.anims.exists('rat-idle')) { + this.scene.anims.create({ + key: 'rat-idle', + frames: this.scene.anims.generateFrameNumbers('rat', { frames: [0, 0, 0, 1] }), + frameRate: 4, + repeat: -1 + }); + this.scene.anims.create({ + key: 'rat-run', + frames: this.scene.anims.generateFrameNumbers('rat', { frames: [6, 7, 8, 9, 10] }), + frameRate: 10, + repeat: -1 + }); + this.scene.anims.create({ + key: 'rat-die', + frames: this.scene.anims.generateFrameNumbers('rat', { frames: [11, 12, 13, 14] }), + frameRate: 10, + repeat: 0 + }); + } + + // Bat animations + if (!this.scene.anims.exists('bat-idle')) { + this.scene.anims.create({ + key: 'bat-idle', + frames: this.scene.anims.generateFrameNumbers('bat', { frames: [0, 1] }), + frameRate: 8, + repeat: -1 + }); + this.scene.anims.create({ + key: 'bat-run', + frames: this.scene.anims.generateFrameNumbers('bat', { frames: [0, 1] }), + frameRate: 12, + repeat: -1 + }); + this.scene.anims.create({ + key: 'bat-die', + frames: this.scene.anims.generateFrameNumbers('bat', { frames: [4, 5, 6] }), + frameRate: 10, + repeat: 0 + }); + } + this.fov = new FOV.PreciseShadowcasting((x: number, y: number) => { if (!inBounds(this.world, x, y)) return false; return !isWall(this.world, x, y); @@ -176,14 +256,50 @@ export class DungeonRenderer { } // Actors (enemies only if visible) + const activeEnemyIds = new Set(); + for (const a of this.world.actors.values()) { const i = idx(this.world, a.pos.x, a.pos.y); const isVis = this.visible[i] === 1; - if (!a.isPlayer && !isVis) continue; + + if (a.isPlayer) { + if (this.playerSprite) { + this.playerSprite.setPosition(a.pos.x * TILE_SIZE + TILE_SIZE / 2, a.pos.y * TILE_SIZE + TILE_SIZE / 2); + this.playerSprite.setVisible(true); + } + continue; + } - const color = a.isPlayer ? GAME_CONFIG.rendering.playerColor : GAME_CONFIG.rendering.enemyColor; - this.gfx.fillStyle(color, 1); - this.gfx.fillRect(a.pos.x * TILE_SIZE + 4, a.pos.y * TILE_SIZE + 4, TILE_SIZE - 8, TILE_SIZE - 8); + if (!isVis) continue; + + activeEnemyIds.add(a.id); + let sprite = this.enemySprites.get(a.id); + + const textureKey = a.type === "bat" ? "bat" : "rat"; + + if (!sprite) { + sprite = this.scene.add.sprite(0, 0, textureKey, 0); + sprite.setDepth(99); + const scale = TILE_SIZE / 15; + sprite.setScale(scale); + sprite.play(`${textureKey}-idle`); + this.enemySprites.set(a.id, sprite); + } + + sprite.setPosition(a.pos.x * TILE_SIZE + TILE_SIZE / 2, a.pos.y * TILE_SIZE + TILE_SIZE / 2); + sprite.setVisible(true); + } + + // Hide/Cleanup inactive/non-visible enemy sprites + for (const [id, sprite] of this.enemySprites.entries()) { + if (!activeEnemyIds.has(id)) { + sprite.setVisible(false); + // We could also destroy if they are dead, but hide is safer for now + if (!this.world.actors.has(id)) { + sprite.destroy(); + this.enemySprites.delete(id); + } + } } // Render minimap @@ -295,4 +411,17 @@ export class DungeonRenderer { onComplete: () => text.destroy() }); } + + spawnCorpse(x: number, y: number, type: "player" | "rat" | "bat") { + const textureKey = type === "player" ? "warrior" : type; + const corpse = this.scene.add.sprite( + x * TILE_SIZE + TILE_SIZE / 2, + y * TILE_SIZE + TILE_SIZE / 2, + textureKey, + 0 + ); + corpse.setDepth(50); + corpse.setScale(TILE_SIZE / 15); + corpse.play(`${textureKey}-die`); + } } diff --git a/src/scenes/GameScene.ts b/src/scenes/GameScene.ts index a724fad..f7c4491 100644 --- a/src/scenes/GameScene.ts +++ b/src/scenes/GameScene.ts @@ -38,6 +38,12 @@ export class GameScene extends Phaser.Scene { super("GameScene"); } + preload() { + this.load.spritesheet("warrior", "warrior.png", { frameWidth: 12, frameHeight: 15 }); + this.load.spritesheet("rat", "rat.png", { frameWidth: 16, frameHeight: 15 }); + this.load.spritesheet("bat", "bat.png", { frameWidth: 15, frameHeight: 15 }); + } + create() { this.cursors = this.input.keyboard!.createCursorKeys(); @@ -217,6 +223,9 @@ export class GameScene extends Phaser.Scene { if (ev.type === "damaged") { console.log("Showing damage:", ev.amount, "at", ev.x, ev.y); this.dungeonRenderer.showDamage(ev.x, ev.y, ev.amount); + } else if (ev.type === "killed") { + console.log("Showing corpse for:", ev.victimType, "at", ev.x, ev.y); + this.dungeonRenderer.spawnCorpse(ev.x, ev.y, ev.victimType || "rat"); } }