From f2f399c296efc7ee18bd4d934e5333780c8788fa Mon Sep 17 00:00:00 2001 From: Marti Vich Date: Wed, 7 Jan 2026 22:42:34 +0100 Subject: [PATCH] feat: magic system visuals, audio sfx, and ui polish --- DEVLOG.md | 36 ++++- implementación/task.md | 4 +- public/assets/sfx/arrow.mp3 | Bin 0 -> 9744 bytes public/assets/sfx/footsteps.mp3 | Bin 0 -> 33144 bytes public/assets/sfx/sword1.mp3 | Bin 0 -> 60864 bytes src/engine/data/Spells.js | 24 ++++ src/engine/dungeon/DungeonDeck.js | 8 +- src/engine/game/CombatSystem.js | 101 ++++++++++++++ src/engine/game/GameEngine.js | 162 ++++++++++++++-------- src/engine/game/MagicSystem.js | 159 ++++++++++++++++++++++ src/engine/game/TurnManager.js | 4 + src/main.js | 23 ++-- src/view/GameRenderer.js | 138 ++++++++++++++++--- src/view/ParticleManager.js | 216 ++++++++++++++++++++++++++++++ src/view/UIManager.js | 73 ++++++++-- 15 files changed, 841 insertions(+), 107 deletions(-) create mode 100644 public/assets/sfx/arrow.mp3 create mode 100644 public/assets/sfx/footsteps.mp3 create mode 100644 public/assets/sfx/sword1.mp3 create mode 100644 src/engine/data/Spells.js create mode 100644 src/engine/game/CombatSystem.js create mode 100644 src/engine/game/MagicSystem.js create mode 100644 src/view/ParticleManager.js diff --git a/DEVLOG.md b/DEVLOG.md index aa4f7b1..015aaec 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -1,7 +1,41 @@ # Devlog - Warhammer Quest (Versión Web 3D) -## Sesión 7: Vista Táctica 2D y Refinamiento LOS (6 Enero 2026) +## Sesión 8: Sistema de Magia, Audio y Pulido UI (7 Enero 2026) + +### Objetivos Completados +1. **Sistema de Audio Inmersivo**: + - Implementada reproducción de efectos de sonido (SFX). + - Pasos en bucle al mover entidades. + - Sonidos de combate: Espadazos, flechas. + - Sonido ambiental al abrir puertas. + +2. **Sistema de Magia Avanzado (Bola de Fuego)**: + - Implementada mecánica de selección de área de efecto (2x2). + - **Feedback Visual**: Visualización de rango y línea de visión (Verde/Rojo) en tiempo real al apuntar. + - **Secuencia de Ataque Completa**: Proyectil físico ➔ Impacto ➔ Explosión Central ➔ Daño en área. + - Daño individual calculado para cada monstruo afectado. + - Cancelación de hechizo mediante clic derecho. + +3. **Feedback de Combate Unificado**: + - Centralizada la lógica de visualización de daño en `showCombatFeedback`. + - Muestra claramente: Daño (Rojo + Temblor), Bloqueos (Amarillo), Fallos (Gris). + - Aplicado tanto a magia como a ataques físicos. + +4. **Mejoras de UI**: + - Las estadísticas de las cartas de personaje ahora usan abreviaturas en español claras (H.C, Fuer, Res, etc.) en lugar de siglas en inglés crípticas. + +### Estado Actual +El juego dispone de un sistema de combate rico visual y auditivamente. La magia se siente poderosa "gameplay-wise". La interfaz es más amigable para el usuario hispanohablante. + +### Tareas Pendientes / Known Issues +1. **Sincronización de Audio**: Los SFX de pasos a veces continúan un instante tras acabar la animación. +2. **Animación Doble**: Ocasionalmente se reproducen dos animaciones de ataque o feedback superpuestos. +3. **Interfaz de Hechizos**: Actualmente lista todos los hechizos en botones; se necesitará un seleccionador tipo "Libro de Hechizos" cuando el Mago tenga más opciones. + +--- + + ### Objetivos Completados 1. **Vista Táctica (Toggle 2D/3D)**: diff --git a/implementación/task.md b/implementación/task.md index ed1f34e..932439c 100644 --- a/implementación/task.md +++ b/implementación/task.md @@ -39,7 +39,9 @@ - [x] Implement Monster AI (Sequential Movement, Pathfinding, Attack Approach) - [x] Implement Combat Logic (Melee Attack Rolls, Damage, Death State) - [x] Implement Game Loop Rules (Exploration Stop, Continuous Combat, Phase Skipping) - - [ ] Refine Combat System (Ranged weapons, Special Monster Rules, Magic) + - [x] Refine Combat System (Ranged weapons, Area Magic, Damage Feedback) + - [x] Implement Audio System (SFX, Footsteps, Ambience) + - [x] UI Improvements (Spanish Stats, Tooltips) ## Phase 4: Campaign System - [ ] **Campaign Manager** diff --git a/public/assets/sfx/arrow.mp3 b/public/assets/sfx/arrow.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e76c0fc568c0ad0d33dca1eceea86c089689e2ef GIT binary patch literal 9744 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 literal 0 HcmV?d00001 diff --git a/public/assets/sfx/footsteps.mp3 b/public/assets/sfx/footsteps.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f3223c921162e91fdd4d2ce113a01f7bf9b27ded GIT binary patch literal 33144 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 W-v9Xz7+Ft1Gok;RkN+>c^8W`I8WNZQ literal 0 HcmV?d00001 diff --git a/public/assets/sfx/sword1.mp3 b/public/assets/sfx/sword1.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f295303f96115c8bad7bfab18850f78b1ffba189 GIT binary patch literal 60864 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

$}G zmJa2wCmo6xFA`o;5oM=rK>gEH_YOkWO^z-(gNh+_hDv;|uq;QW)Q|+e@=WVvRhs)& z#Zl~O9E^f6(Qly9W%Ws+AsWq9oDfUKG8D`{yWh-XmSpwin>HWwFjvvL2_fGM$xmpr zW4r1%4toc$8kR7qVZ^m@ywlMsG{i+d5F6tkCfgojokA?;j)?``{^C!Sf#GYq|Fq*T z{%jyDc2q}+FR)t)%w=oKuGm42?3B#{hSD;qoQL~#;ss#%HYPm7P&>!0l(_L3VA*a} zy8!I)LX$gDc&;RQ@EZIeTu?p-4lyYQeu|i&F)3Mk2iB<2@0M$ixg`zVEwm9e(~CtX z@`~qnlpx$_F7;MWX0pm`7ix5XeN#`@;FFl3tgq*TP+#1;lzLR6M8b8M-9l8YPE5h< z?7(=wzE6Bv^{Fp6*H3i6Wj>sLwKwOp-MJYQ`*97WSsMJ>^BRrT zLSGCmwzfoi_513I!U`ztLMUryxy0#XsS?cDQ8NM5MTv&k;y%p8Ru{WeMW{+lPSsMXrf*zmdiw_f3j@pAUw1!3XW<#@YlU-Bn zr?5`B`ONG6M=Rw|bTuzB{yl@0E@bx{e!Zz^Dbv>ht7@nX4_WiL13pM%_#6ss7XWx$ z76z}J;s4=}k$Nf@Ki!p74D$aw-#=LmX@DW0pXn9$gysXFV#OLW6hiC;XQW5#vc^`_ z1*PJG;&xe96tL`du2DMAvnC=46szJ$`5^0ftI<9w58ViZYbS9G}~Vg_buiU z*teVTEoItYabOXW>8seDYognACNOyZylbndBr|;D{c(2bbey~??XA9ImN;8U!)UfN zzAW2;@hpqJW19hhz0obo&X-=(poRv3X6$%p>c+k)&Y;oo9 z6lpa>UzExnS%Z`e&UfHCT+E`^FAY<{$Dg9ealg5teqsaMp7I943&PuFLU+vP((w6k z%%*?&bp09KY?!60MZd_jmHb)N66P}8Je*>ub=sm{{NMYdzao1v?&OT(XPd7Kw*kbzs zuU{>`DlKjQ+h5G5jN|6drXUqEu_i^VVXGPcNed%2EmzH+kQM>&cE=qJh10NmK#BgX zPcEDSqLg;UOXnlC5t4ZNc07CjcgIJt1zqe5$4H->d^BM3eQW+fw`y3^*ronAuitD| z>T-UqQgXY{WVhbWeFoP`2y=?kJF$d{p(uI^Kfa?st?CRPnrTb1C%T`m7`>K`wmV7G zbCsiM_`{p^2%)q=7+e2*WG;`ynwvyt|4PO0=l}4yS$)PvYpuGexLD5pw&kPX>O-#B zGDBMk7yy2kK8Gp$?@(P9 ztZgnm-p~SOq11#UmP!*&joP*)7I|0enLE$9X(l3~f@WIAur18qIeiv6m3{p_t_SNI zRKe-4;(_+OGTS{&qWA-d*tg7dv&HMOSWgbKe^`_%CSbUn_GS>A`lGCD*Ou;3Uqgx# zRb}mqNh$Z)9Fk{G3Ydq6yzcPn#m2r@=BtG?E>4rFx%<%b$ws>s-P+GzSikiw_0W%R zH}t=G2O4%S;}MzDj=$%ZF~=P#q4cDSly*u#tnhDMF><|q*On*h9i@>Ps^8OO83cWGDaP-5f)9>etY%S#QbjjD`y=f0D)N}jchH?|>el44{f$$TP+&Gd;`NHGh` z4UeZggQ2fl_3fYl-g|G3-8$gJKa++_AoR}%sEiAYMIxK>`+oOs`jS#~G9jQ@HoJ-!EkJA95i&)dgcpG2U44pD5BV3T@6@L=+qIjew~ z(QP%77DDgrep#>MS1)8HL5kSnh0r+Id2@4FT@)uW7DYddBVjzh=}uf=x)O=*`jB(b z_KenS-5s2Hfrw{L+Q~gHNp25@NZLQqGI&={7~7QFR3_Y2N_8snHZUV9%xf<<@<2e# zaq#8$qMPzAVnUILW7A9!_%+6X(gdp=V}XM#ortNPv+;cDOINbSh#t3M)gur^U{ljb zE++#tA8DPo;K_#f^-05g;6>-TrL(@CyUU8hU_-bXL73W(TV7z}f+P&bT$~RKQ~4z| zFUKmov0;M|>Z{fAZh_IF+x9_K3*G0d5o&<5Gh@=^j@>r!2ecdsFcib=D>R zC$#6G+lhN~+jmg_-cLgg^>s0ye%F%@Jjo~el!3dl(U*=r%15pdf)zYVugefRPthoa z>`{X5KQvDs60);CnD)4Dk(C(f-)Ntjl*a1<7ZzA9@;G&EN_JoFewL=UpdfWNkgu;8 zBT+m3#VEqGvTEMrUQ7799Q4X*>E+$khu@}|>V|kH@;YQgG6O*I({uJTT-^UH>?`TB z%WNcwjaeSh*#753$a6h7L5qgH(^`D$W_)P_zi5~U(r7N1N{EPAgZnU3 zom(`2>rLM}kM6de^~s<3R{PSJ!M#{;x_jcY<;s?FwhC5PX9^2uSvlk+7jkQCfSWo> zFMV)XU;FgMg29R!6Xsjo_ zx|_BY)Oz>YnL;f!%6XeYZED==M(3GcooL*n)rS}9_1(%yS;9%4@CZlkS)1^(SDQ3WT;cJI5ub@)u~0kk zv8WI(xKKGXBI={=S>wJ6|H0j4dNpitdOUsFw_=bV&K{EovW)Qwba;}?cbpdgy~t9U zQI9{#C~Rb~ga`*HC1&?VMEXq27NP$*MVg7`&BHDZ@h7%I2#2T52>%nW7t^vyf)QQL zq3SlyUn@u#%2yfk;kMfrdMS6da@0?%%VNr46Sw(oG~mSZ(X=1I{RtcZ-6g%ko0i zFS}IFhRt?yh-jIKcR&=51miQ~vlUre0(4$vHi7ww!-KF2<|ye zw>ErHzX&x*A*Pn9qa|=~A>*!Y>fzHg-zwBCF>agpAe&pq)uDW^6O*Pwol@FbR!pUA zw`YuyO+(MUys2a0i<0`Fg%%CM3hWYL)GTZjgn?rV*a)3eueg|^T&a=+-({{jadqF@=x`+b z*AIiKr?aYkzT#DU|2Mt^OTc6gU}QUg&&N?Lu(st-%fhT!dPM@aK0BQx@MC#;9!{Da z_z<;3V6H7LtmN%6+Xz@S*wP+&KcexmJOvhG%m5UdlO7E1O)NwOcct^#o9J7mo8aH9 zixX=^M-j^75B3T)Pn3}f4db609}m>p+fPKRm9r~vhpSJz1-{|7;y2;tE-({iirUy1 zYimIyIGH_udHiq>MkzjSHaKl5jn$Al#YByx9m`w@tA{6@A+Mzi zg{x{)+L7}N#joAHPwW1Q~WAIO7h{NZMKX)JZW-I^M`O=f&7vp4`)U0;$fIgAK z5v&)T-|vJFwWW57C|3F_ZyXw$R|26%Z9r~-HO?8wlfdL{6|q;v547-IGsgwGFg#^+^E7l;n=g-XgUk{XREdATcxJiu zN)bE8Il8#Jn(3@gWT}3#zCMFwPVm7a{+^4s(rL$ED=b`RYrTELUymiTdNH4g6Q?qo z4VTf!3rQy9>Z1CvO^S78nWmRP?vGQw#1-(mMC%#kjbq2;W5fA^Yn<$C!&8(urp@9v zaja6uWflv29eIrHa#1PCARC2We$_m7MrV3^_*AJ`41CiOT&4T+XU3Oykr}c#KS!}I zRDJevdP8#PEIn-!&I9EqY_EnWmCfE5wiHQ-fwC)a@%e)~9#9{>A`n~qhtIAA2A^@v z87Yp!*uxTlk7llE@_dioHXt*A+xI17ty8A|gN`+zPYlS3w#os0^AbX^z9Ck`D%t`r zJhU=@Kpo|C-9XO)FWE$eQi%Ct{hIKQ z-~U-IZS59Jyf5s(%tZ>|a)AljBT-?4Fa@_Q$ z6j5o#rJ+P8@I`O^dyibIlyahC)py)vpD_q+%FQH;N#0_?noB&lIE2M0G1R2C4kmG1 z&%FU0w`h!Bk%_|p=_CC;7>2I^^YuS`?q-!M$bo}ml8JNW$Z(Wue*P)(mkUye^3vYv zAXWXzO}#*|tlUGG7>cHLetLxsPjI8v4gB%Z?_@dgKut%4mA zzZ`a#%XfduS34_pbErJsW+);u4{T;v{99Gby#9!O_ZKPGm9^=kgbScbElpR&`d$h$ zS#B4}rs8OvN#`Yq~1JL32363JXFg+LPd2L4penb1*$gk=<3Pq;0J;g|xF5h?bx&&@`} zw>a80+O*8tXZAfV(7x!`Z^cZwl)EgZYdKTfPXMGwjnGrlTtH8Dzx8D>K;wP#+RExpVH0ILVvm)k5kh$Uh}t@`jp9VHm1d9rPu~9X2lf#wJ^dN>Lx6=$s7Fdtxoyhw%SHKJW`iER zC%ZoJBn$d$VIiiJd4o2jkpy3VU!2&TSweG(r&?V^luC7Db<3tN## ztmbM0bUJV2-9pQfU0Xfvhq&P%Ow8rT(UuW`b`1ghd6GBMW%{E&(G(e*TLW@R5(H*-#km{Ga`);kg%g zR*)Eh(1V|`d*BG*%?y=TKUXf9vhY!QF+LX4S$U5{lLUWoDN#!)A73`5SZOxynzKEy zbn1>L>1KE1qtNMk@QxCfcp+TmJMDtF{_nMEu{LXk$a3!AbsCM& zkvLvj#O>Z)e|%#5x|lxeNoe(>Eu5*O%maS~23r>jg#@aFTJZymw>7r*)MD;KA!b68 z+D|dl>>paf#pJP;RYXW$z;nY@#0_EH3mgYR2z6Zj7S zQi;t7#pr?vfsA%(YRRzH`>~sC&9BWJwm@ecTWNasc$%L9syqXxZnt*zH|jJ_r*EH^ z76Zut>_7kx_lV3m>MuSoRBA)i-fd4~_w=dMyb-7kFci}C8cN|nAV~pE&%3orDV}~9AC1I@NUC^Z*oe>_J+TEahrnlEIAK5lgY|R#K}9Po zo)?6HK`NzpI`j3pV|z0K5Zav!)f4ixzEIcXAc^z@4ypKn#_nS%l+8Xvn#w&z|M-_= zzz%!mx8RMs?=2cHpx+4H6y!XQ^i@Wr3&G+ zuR{W+j~B!1i#idN9rY6y^Z9p96-wX3%-yX0#WQ2( z0;(ueMgCVYieGRsSfcq=bu35F4~{-=;k1$#z>y_CLk6V>ST{~xckT|fnFwBnUe?J2CLMZ|rn)`I|9hGn;Ev(mUL7@N^+bte| zD43FzRm@G^8c(bdTYy)5Dv7sf#8q890DllK*D`**ADb~{Bv=QQ=dj>>%$z%7jfBB_ zzydTB=}Zv&)_J{MD8)Lk<%d+B4n{qK`$_u?Q(evpaT0?xan+ttROA9 z-S#+gaqws}L3|s!bP+6)ln8YmK_K0lHrr(foteEVRK)0s0-*>PnVxHH4HRYTv@VRa zwsa3wX4@c$Rwz6bIq$*zb4E&HJsEr{_++A0IEeUDSH3(4<{C$F3S2r7DX57ozLB^! zO2s}mF8~-3q$Ej0FCb*o;T}1%?MsRRDFLa{Wy~xgzJaPr`~YbyD`03KM9GE-I~Wz& zaah&BW;A>xFh|empB^i=w^R(-@fKQ+tB;Tn^$^s!M^5)~DhX4geatY-6jHpmy zCE%7M2QR}Jq-Y=fA>G7WCVJAfRqHL4>_#JXn!?V8x6rvxO+Fgc1JvVb?xTn7L=0#E zD%D`3doC-T$OF9qg{jnvufi_Sk_6j0Nn>%n48^ND2^^0XrOsDoCCjplrN!G+?)5pR zyL;s)p6sQJ+5KrlYD$e`YpMc#8A!e;C0N;?g3abEjQVSD{~St?$Z#_nzf#Mb zmo!UM_u_yRm`yetB`hrp`4e=R=3GX^q)sIbh4t5G(Q~OZQgo_2PM zSc+32o@}~2RL1-QIOKZD2+Nr#j<)O-*hTqI%Lh5`3lptX_9tV1;7uP?a&{DK4cr(m zHL_@yH5G6XVo?`ab*`r|WQ;Peo5c)>rj(>Es+GJNy*Kgv_^kcRdvPwU+2rcZQmy6} zrc{A$>g=|osmA&#<4<3-PJ{Zt6h0Sfm^-_;cb(pPS0^=d95yvGI9wh6{z+5E(2%*9?!+p;;7fFF&`*Uo2q8qYottF zg{--gNSe`WAi%^D>%rCZy>;sFcU!^$^DX{MfRjv<6-=Ct@st1aA1GeBd!42NFh*27 z5Cs5TySFYEISsFVtNr@)i<5UdQ-t_7;LwA`nSLf=r2D5#;n86UCGj?wE$#l;a*Vu4 zv~ULrWIEu>qaOtlY(;iN7;5ZADhZiuB}nzhz>lv|dvG`Tl4^x>GLpxZQo411gc`($ zGqnn{);iI*Xz=b$q}-h-X;|7hc}Rbjg+_lyUlyb53iTAhaP?hmTxn~11lVEPRz5ud z-iO>KcRA{=+hTc9NGuAhc`a#n6N5E6MisbNUeA&s&su;N{D4_+5fC z!l@@~^5xNin^^|y7&Qvm)83)5Ts ze~W(yrtVDr$Z;j@|J+L}Hk!u5D!ToUv5S@0wW*&&v!8FirlvJBzY^42JIO5Utf-&G z(HA7EtAy93XfpHaF@xp&qL%SFkI7j}A7J~Y?8A+rY1ZP!#~E2&cwz)?D(T#F3who| zN1pD-OtAwpRts2AQ3rq&psU9Wn^Th&H$JTD&t8$Ev05)5K3=|?u8zN2ot3?1TRH=m z!epch(Jl_S7G$_S?(bh;jzeMPEx^Uy=PCUF(twCrRn!w(Lh|bpZcqB&wgO(VqI->< z(4|j5QIi8W?p{_lNshCNQ2ccAOv>IYB(N=y=Np!4jrr4Js|!saDaI}P$*j&{(hL~l zAg~Ho6V0H^0@K(Ci z+LCjQaP0XAlD^(|CY&qdN`v+@J~P=LyO*p6kGF^~ryXAKNnck_X22ESUg#H#x$Dz2 zscHFVolofUjnC+bl1GA)%?mr3tkHl9CD`W}kaT6drE}Z!E*4Ht68XK96zw6o0)$*d zO+DMhaX}vMg#>qj3ExOzl7@AOS`1~b!0&MyK~4R1fjujuOD^AvcNqQpZ%)L@aLG?V z-twQnbZBRD@Rc32)BnYx&_No`>k2DF5AjLG*XZB+Z@Edmjl0n`HLJC zb)6BpAWXl=d?3Ccf|q0a**;F+@%>MdGEH^L!myhUg3aHq*DI(v+isSA*NA?(m@B)R z^-%mUO>%F;9*GPG+dl;bQ|e% z-F|5k@%kGnXZ_9|A!BR#`yfg#0FDI~j~x~>k<-XT4THQY5~_k%Dw!PpkpV9ZH9a9+ ziy>eF7a3^^nE4TY{O$sDyh>;{C1hOYT>f^wrJDc-BRt%UH~fiZ zPy<})7UDn1lSJY1SPHx%nPUzF;fUk^AKEwCOX+G?tG?1v2YH zr%E+j>cT|(sS6~Y`u68rXCvS03~%M_9B^W9q=z|AF~0GC_MF_#RQsS{s^l-8y`)6X zQ(OwQR=wlyDzlXW&ohfMAoGGQ`Wq@4ISh9BB&ubdmG6BP`j*<#$^D5H4;L~$p%g%! zRkA{jyF^48d1MM8xGfESUg=Kz=T2esmFaXlHfVu%y5d4pnOuf`fP_UHovJV{> z)<;vj?Ln*5c(ejZf{e^l#%_2kh>r}1t8ws-=#D({jW}F#-~mP-gKhT!CphX;m^`G@ zzxZzDp4uUdeC;njaxw4rGW-<7N%1&(v5@_O9RhDx!?pNz^YK(z8XX_8Q9AO7eF-w62i7t$6L@`^c-dK zEAAPJ6UdZQe5^(uoyfzOG+HQuH%1?EG#Uz+vTRV0N6@Kw*FT{!hI~KPefvW#;r-^H z78R=y-Ol^J^>WA`m;NA$JEhp*X5=CRvA+H@9Fx6V76^d=2p@o?g{3qsLapy8Y!f!p zca>qwu&Xj6H8oV#Uvp?6Kg6m2 zv!H`+L-CVCXJSkn6K1f#rDUR0Cm5S)G6ZBLlj%9Q#V^q}C$9C|r5BwSPDjp`r$Nqt zD{p@QC$d^PSG?lqfAJ(!VO7ESZy0kYj?rOIOFf@_EemnU(3iu-=MV^i$`FZoHxJZi zeL<3XP&YS#5?S~EG4+-KQMGN?@Q~6BJ(SYj-3`*+-QC>`NSAbjbcdv*ba#rBq%=rM zi1UrttM~WJ4}K1N)?UXMN32C=R)#Xr%hZ1XUZ0~LP~UkR#}ZnhQKy@xQ*N`rzmnaX zBcBeJ+m|;CCE9i4u&qEWT#8PaQp%I*bd#puj``79by347zoM-7%6cZ<97CS-xFvD_ zfSJVgnyc`qm&hV}F1N+nLh^MPhYz`!DO_g8&B^gxbT+IYH?nJrSPKhsG$L%1Dfekg zVf%^UplJjyA!ORW6h6SFa54RQjxR+NA9hUS~F21Ru1;e|$Z9@EN+X z<~dc)<>b9wcuP}7dwhYN#T5^f!;dtlqrr+UPbo}7L$c06(_YaZtJ+PhT~$E1pu@L~ zku?u`)L2(7KJ+cyEHgD+r^R=x5qcf5kdV0W)6mjCG4;K!1Jf#IgnS#aW0eRpiCR<& zjctuyN=+4&E~H0OYvEhW-hqwsXDY;)8mP{;se;aW{(`L(GRHKf1bUYsTej&6gE(Rb zHvAB;^wwmydiI35gTBC)I*Z>P(opzG(`fo4hKCbtd@7$ebCNmEBnNu&|I1(9YOBmW z>&pI%M_h3e(KGh{_3hBgl%_Bu#YSkPk;3OnB3v{CeB++$+x>yNk%;V0CD3h~_6|3X3tj2d`swuWWMvY|MQTfsl1;qSb$jhy? zFa=QG_fyaeko)dpP&`Fk&?}mS<6g#s-fcp4#4ZXO9X2pNB1!Cc;^3p~#9*7PGIZ{a zJF8z#4w=x2i_I`*Y8s%21B#a)aMx;)m7#5`t=6l$iB47l=W6(EYUUZ#*hK#jBD1sAsCQlT-_z#MQlyBb^m#4kP{S zqq;Z1Zu2DLk1;3E>bDu=DFLIchO{e6JP3GShYp@6jCJSs9w?Z_F;V?VNt1FZf=QEv z8fWi&H(p?BWKAj8;o3+q0fWWIVR<(8)Xl~Bt?*Q@c-9 zMQ5n}4Eu7}K8Cn0yb?!RNA!&0&$4ONGC(kaMthc{6se4!=`b@K+GpliUDF6`TxxB< z`-sJZiJ}8wx7O#@PYbzu&XM2APfI5zjtg0&F?k;!-wv4-t={&s#n+n zCXoOzRa(ouZ0FQErcUlT*6ODTurY?ip`FGRwB2|%+4GC=} zVK}Kq_0m+yFp}tMTPyvNnTp))+S2MBu85Sf_sp?I>{A_bd--^BW=`+dZ-dG=ry&GB zDwil!Ix|s6>OZ$a=%;smgPHw2VPGoeLwmP+T zY+#15TEMco<+<6_e6WT}-NR@HO({R`!>a#g%L6hNgRZq#gcUuEGZ%)6;vz$mAbl=@ zMdJAicp(|&2QL=mIM?bzPrOD|md9TbQ;40B*RDm6IbJfJIeV1KPG(t+jNt#_IYhwA zBQ#T?A~of}08cpv=M@ud-kFDN)$F5fTK6M$VKc^!R@ZY*Op}(-Njp_wf>aJrM;mN9o=0Va7r&%4O8L}37bK!6-RmXCq+WVEDx#3JU5b{#2 zRxQ!of;~(0A)rUfUAO_#k+7odC0N>aN-{q8P4U|o$Q=U!fcgMmy$mJ|P6CQk^^bw*0-zoit#m~Y<69Cs*jxO7a0!3x1-<+X!t_zE%L96p5#%@;-qvP##ZAAQ zmjr*TUE+H;+dp;ibHFW*itIBQ2lTx9UmXe=6hvGR-VgYDKe~j)`F-}z2=(Xm%aD%8 zWaJ?h_T@LJikHCCXdo{Y@z1!W#;dB!UInMFkn=jgGFtYJDQU%tyz}Xy@Es54lkBm@uF3WS|tDx7?vSdIgAO8Ej z@N;n3gF3gk@}1N7eh!jioTueT{I<@!9ApFzBR70MO%#pszd* zOJAW(5*b{QE;d{^Ij`EE=lL0W#S0vUKF^Q8{CxEQherlQ|1bVPT;@(-L}|H#gsN4z zLdHhtKDKUOHj_>lu& zPSJ>=&+{KXByQ})bqAc#gzbO#W$vV*Od+mv`X8HL-}S<^vuecOi(EXTI(WD=7hH`O z1E)GcwJ(zNp%L?;xxb6l(Pr**m=Z|N`b!fkDst6PGAYb-WG7ljTms|0& zX-$q=sT2|}r>rx+v2YPp}N!;xnUX)_ZNk=hmcQS#ML zPVk*SpKzGjD6DEgj1))K`1roC8pT8iY<}%I+Sfav?l|X`XMd|3G0Ay)HWK(##i=dC zrlRd>kw>?etW>Es^tdEj>4O6QwI4!f>T~0=57kG!WdgJtqH`)fM9q3F9W5p<55U8~5<&r5hHuj>pz#+&4G+h#T!+G5 ziztM1h5W()8Sqn2XEMN`FZwSZu*8Kqq4_}Q=f8X)Fewl3;|FQ5j3u9kGP0O5>@4X7 zjS4P8Z6Y&p!O|y98thH#W{Ype!7+vbyPA(p9T$G}S|(8y>PK}>F(hO$#@0v>UMMXa z<7qW2nOebqn-(=coF4h&D?}_y|(K_ zA_{^0zt8ap`S^|uYE?~Yie#F+3`Wu=uXauGLK<_;bX>wDcnUBm6?}ohRz4l$iq&_z z-5;Y4Wb(rTH)hS@&Q&t$y%HaqimDgV2PDaYOp{gB?Xx727B%DR(npu3(trm`?AV&~ zV~V$k%)0NA@=okPG5rk|^&of{pi3SK_0jpKD{f6IhDG_yu2T_=D9|Zmv`Eq~Ow!QZ zWI^ZWpPXodli0|y&6TI{)-wt-d*2U6_MFUqg^+X?8-!jg#|vMao>NsUl^GCH4Pv&+ z{MJ4fQI8Q4MOU!;PZt`1BnF-eb{2yI{$DJjVH(I|0}*Cn@Qts4vDNZ+-e57x2R15Kr<9I{j|eAy-)`_y?j%tidt5<<5`E$D zNHF0&NSZeA$(5L}1&BRbQ;Q2@t){@KMs!%zH3_j48oDa{Ycgi>#kwtTc(N)=kl60z zm_Qc2rvN)X)hC=hc#1-A8cNK;$S(&N;p?wF-*}5%59RF1hMjJi+Kz1vq?Ult0q0$G z8nOc2L%5F&m(PC5vQA$K>bf>;QlVe(o%bRdQteYW@nwCh-tVs3fbN z4SuNi8?ATp_t8rln_w0gfONM3^=YP;0nW~q-PN0!(+CN2@wT!JHk@wYceEYFX2rd* znw-AUN1!cYW0BNvBTykDcDD23Q;f+}hpGzD3Eo z;-0165IRp6tbrLpiS@^2lDiARlkBg>XM@A7tIT>$?!$`YJ;cw`3;R6&zcx{<$~PYJ z6U%tEEVOuCbkyeWE^|*uP#FgA6l|uRs4A>q2zW0zrf4-w(Y3In{W>Bp-D2;&eqVHY zk^xhk_+y|tA*Ims?W3WoPKW~;(jWe817Prqe0inS7m)a26I>QwE`g6_8b+-`8s07UH_bbmJiQr9i571nMv9poUERjJh z*;l13IVEumaiwYvKx$S`sG-~<*kfKxQb0xEwM-TbT?+M8TSD5Axo?D! zz+eKp5f$M^kIvB805!EGQ)9j7yk&^l@q_$)x590er{ZdQD~CLMnL z$?d+?Xf@DUc^QvFvctQoI!RD*(k3Bjm*G3J>j1yl62j4vhi|z-SFg*X64yb4_X6mH zS7n>7k5K6rKf|y=6elz)VR&Vk>E)zHOk&_vzhQZ{#`q zC*BuyP=EL?aH2gCbdvmMeTM3SCK_%9AWQzM*MROqET)9hp%3xj`=#U<(Ux$DBRoC$ zeC3*05`SxMYU!HiO=n`@R99}kOqaNMEK3jtGl@o2p2jKNdX5q#@%h_sFY!x|(JL2n zvBbP3hLGRoE4Xgl*Vi~-)8`y#7fZ*{#3j|C{{VJJOd+;UDn>D4jHu@OZI};P zaz?so`u^IB8}9N^K~%oHxU##At8%sSyBtQnG?GFynhtA`spb<4+!r%`m13a*<|Xe^ zL-Ogt1xez#N&YA6(v(?+YjtF2pl2GMM}l6)-oz&qwAI9|#{ie=FUZ+3`-nP()kV>~ znrv@TV12kH$2C&o`uTZT?c7R773&Qrli#FTc$`9PH=7eWJFTrs-L&QtB_?_9t*sqq zs@PlcV>Eg%egY!^R6yuYc*PZgejrWR8zx^}bzCj!M0;LVYK_PfrIcB{fGJ9Gy%i2s#1Oj!LL%3k)Le zhNdq0t{I^(Iwt1sddpSxd~@NtPRTAwsuj*zqvh>rK6ANX2Fd#LP~}!85s_Pl7Qe>{ zxzQ0uzA*5qM^leYpss;&({Yk|daQw6>f>iZ z&QE@~^Y-_jk;WQ(+VEaEi|W1m4r>a*?Mlft{@sdFJaPUx!-qVKzUezYt-i&=snWtn z=edVThz>seqsz~(so_&JFU*~dM-&{-Vagz5p{k7Y|hfcU|2Jo|(>!*ypQ2*dR2e@<4U-eM=U*9!$ zkAu@W+v3q*{CUC)s<`Q1C+VBrIPhaQck#9o6sObjw8Uy1N7Y;2)gKTu(WE3+@Zv2n z$M~}xX!jVcy^XtXIl_g3w15S>^Rw(yVb`LfkK3XmfTuHnqC(2u{B*;km>LGvx zQqikHwW^VBT|JrhH`A+$Ma@(ztJlxSo!VpP2hBS8_q@gKkaZUhpo+)-l=Q|4xI#?JW<|CNeO`eccidTAbwC+UV0j@L@!)+og#&PI6U)p(lf2xS`B zIKzZ!c4>VhL{b=R{I(0TNmbptJlHceL)N6_B~WBV3-!l(9f$*9zm zu?eMCXsR`!CRCBbJX+9MEg}wu%xRo~Ng4ecl|~Yys9N0x+39?qbjjz)y`-U``pC_-v;tkt;9JdZy@0kL)nk?R zw5tcZrP#R+_m?1j14Z6Z;^7|+-uU-vIW}8g1`Y$y&0IPt3UX)GL2wGc*CDXKrk$c@ z>6?*he{1An^#4#!Rl6LucTCLYd#0a-8U!2eX*XpDZu0X}C6K1&=yYpa(!?Z3z>{ay zgZ|dbG7NQ}ksV$Cu0J?#HYR7(H~;WAOAyO}af?rq45I;PyDE$I_YL8HpfN;&C7UJ4 z1$ghNuJXb>T`Y9lNq-;o0z1B*CMVG)(357zKXn7WXr*gsV9?R-nR;s`1^w|X|&;9i=({&n)j;b;fdodo@ zcFgKvboQM1>z0@LHVG9gJQy-$B#Elw}Ie|VbN8Jv}U68pQJP-NZKg2U^YRw>{~8B5gn zWc$4%^CY8p>8Zp!yBlLl1d%9&*C--irN-NWRZC~KgyobIgTl__6_f(+ey;QOoV0`p z<(g)$g~>rxvwyzG#!5G+44HppevyV$oQN{5_nhVUXveyRMkGmTyPC@qj*qe7&Qf9L zcUfun9WNy)D$RhYKi;f^yvpu$X(VoGZxxU&ZHSFQ!O5?cI@J3fjEN&vxf;h_bcw=9 zBdFtP3q)^AdiOq95e29zd9H`;R5vWX|F{_PLPYCA`b&%orT_G@hJ9e=h?i5e$RqAt z=|})bB6N&d(J=05Wl5~=dyj`8u_$|BOMyj3vJFB;Q4A;cB8+w6Q!v^HbrfA7kS7m9 z$03r4p>}F{W4VE+ERD}eAffA-|08R+qz2z*%yNf;cAn2)a&%ew>%?`?J!n|>25Qw+ z9C4z=W@{HRul|CZ5^c>!LuXP2)m?%1X@hThv~-@4HUHs{ao}QcMs@XXKc)GYf?|71 z?m(Oqwn(4pX(cTj9mmg=?zPt06UoT~lkg@HgIyOFP%Y+$fyI#Khe5;MO_!3)(mXW* zs_D7c8=K5|HJU;$`o%=8NW-#qmwFCvbTFg0lJaoZ-SJI~&Ki?GPGqfX3_qp*=FPbT z+xP-sPaK^G3tdj_)l?<&C8TCAof6`9DagZ4`pyeue&aq2+YERsIFdR*k5=Ue2v^n%wK+sGLDsm4MXl|C5?ZzWs!0HBzWP%FEJ@r`M94mvom zG>C0KS0qnN06;)2^`&ip4~4>B1@+0>2IlX-_L+w;Z6>H`S@V(=C+LRj8IB%yikR!m63qoP z3+H`9OWCqW%+I=N@7BC3qLskp9K_@j97uS$(CjyL5lC2Dl>#9dJ|tnKIGz)K0h{%l zTa}wed(gJifN0v#@QN4jHX{xn3)j2EN>!4PYI&)8)qYPqGFR;lY#FnANq<%C1GzFy zd2O2lC2Q67N`w zHGx0tAyj9w&j332XZVjl*i~*S|F6G-3yKlE2~T6(-W@&@Hc5}}(c#gegclrg_EyHd zY6I-fQH6m>;DH8(?3%7}Cd&yazGGVL3Ag0UtA>qhE+jMoCi@38hg5ky7i){&rH}V4 zwdHtkPfQ!JvNVO&#Mzcx8z9sgky`g=tq;q!ac32tG~)4cVM*+kNMU6r6w09rQ8{W- zaco+YRMY^OTcHl1yJ04MjK3%3tzdX}*Sb;aqkZ_&e2-C>Bk4pKEhJALe`W*6@O3*U zUph$QXiQ`PCufK(cL)bH9u`5J7GqU85e|%fEQu;r0f-1r07u>DJWhaeYTD9_^ zB>_crq0egk>apsu%%3-sIds*NlcVr0g*;VdWj}8Xa-?{=J9m8lO2m^C+6S9pcS;u;(7_`XX$&o#E zzm&!Bx$DGBgKJfaR~!{X4s3lL#nw^VZPwRRa4SX~`nJWxSRst#WqL{o%MLI8AT>#| zx2;COEneN;nxCN+Q&HPCiCls+etA+2IYtYAbEE!K zlfj3>FK}62D*;Lq(bo8c!p*;n_HG=#zSdyo6rbbLOODqnxj_22g-#)lnx3@>LBm5i zlH?0IN%9jz)!xS`N=imD> z&$-0~#(~NAjan5Y!2G&T861cbRjZ2zanf&EyYm6xGas5A;#=Yl{_?X(Z$1Fg_~So* zN~?&U3r+vqFEWRVC)N7gV4EM)BC)tESC344ZtbV#evBk5y!*<0!(DL|fHpuQ780Mu z^=R1A((jJ*YI5cxWL~P#+&M$=r;3~A=y~vQ*sDoz0z_5|t@oF$y#fMh+Tk)rLrKy@ zTBEUL5^Mkf0C^xHJCUOgh$zdk-;Z_TmMCU~643BSv>ljJ!wFL*5d4;H)U8ulk-V?v?s;S=CA3|bPXO^07h6jrT2uh`T_Inf2^S4%v>N9A zAnfDE3u)@^1isIpXyzd=U3m z>L%03^3ep~kcXqbu88W*slbOt4js|=E*zh+LvXXJ<07yCC_mT+#9+`uF`2^psiF0k zaLYUJt-R90Gd)krUp%pc_?6Dsz99YOCk#0lzkMENKuped;lOPRp#nAOzi>=_*0$%)< zkbcMWvELepYr+bV)jmR{!m90v{Or1*l5erkQ=L{4Kp=o%lEN<9GZFV;mu!%!6EWHhuAOem zK$b?JF8*pYq{?W#a?06k{moD)OPsPB+QWUeY4F{>=$FiwW zE7Bvkq&i#N5)8GFLi?}MEPsXFjp-~tvs&f+i}yM&y|YH2!oPS11dX;y8YwhEIRyAd z#!dnX^?nZngD91$`Wjx04mI(hGKF%iU^znTEe}JG`I`9v*7)@h8wOuN0mc-}&-w6I z*4x-v09qCt_-evFp{NWIwPQ!d z=ClOY=IaodUHKq7 zmnG{WRq%AfUXRl!su*3?{z%5nQx<#C{LdRBNjRz8ZZ#zVg-xR{ava=U6YYjc%i zC$ZGfA|d5xvew$8o0)!nj`i<;#0-6f zO_S%WOhS!i5yOm$)8;&^L)yYjuU+tM&cokJwICes2ej2s+mf%WE7Kacw6Rvlm4&Y;8$`6`;IUiiD^uJ35lW%To<#AB zfS(aKohp@SA>xE;@e!T>ZJMZRaRxz_l&`-J>b35{J@ehlz@u5)3W+pc7%s6T2MGt8 zSB5=fQae>dlH&OOn#XNk+(8MQ@9=4cbx*x_bH{N-&2V{hnpdw+I8PW7GQJcz4vwrtUpnY8^Qm^|9sSM}0TOib#b0LzX-D zZcF1gw&`FJvQ*rvY;VEqM73t9(o)ep5=vyEn2yOLk&$xW#EQRyE>-}nKA3VjjTjk4 zW3vi^bhQL##(A?u)-vx|Jtk3L$hr1!z3uRGIr}&b{<}Wde>rQsSzl^$ZsEw}ZhdP(f_bKA^5Tk+D;A&sL82=ze}JfNm(z+j^Mxc0zC#sd*)cI`+rGF_LFKg~ zccP&i(rhYRdRI;3yhWAkj4X_W;=(dq2eZC8;d^M`WEAQG1MF{AZ&W8MR+^0c)wg_% zHhduML|$YJ>Hb56?rhH1`Z$zxGqDNs*pl!nS|oLMEi0sORNx9}oC3Gz5cmZYBSn|) z@zzw9r|qp0L!OIgcFIVLZmLtA&s)WV;|7Q4=|FqrFwvp~;^E5aQKnPJe49n#ECeuo zQ{XVp)#LV@82Dsn@aqz=dM))5+6D$~q`+iCW?+(fL!Q7fAdz5&NDIU`VbYN&)^`%{ zzQL(KB5(s>ek4=)Q|~y5ytg*~VnG<-`WH`P{s0ZMo_y%na6vJJSAITYL0KT+2YYt2 z9AeSohFN2HlBy-=tjdCjCVr)0F2MBf6Hu~O8CY3alu@<{#cdXGxJ zNn})F?ei`23CNV%(+7k5!0Pd5GOZw94F`iR7bJqZ-^vk^hOxPmsLX4t)4tSNIcXUEnvC)cbu(Km!r}BO3bAZ~K+NL9S@AV`;v!I&W z5ZPgNv78`QYqAA$mEukBb$AJ01g=cxS%)g$X-fxrnE1CPo+`vYDPr3>QCT8c1s)oF z)O9Dn;hn5^ws2dfpO5C8iH!4Ij_qa8pBuemB~E`ov1PtlPX ze@;mNPIXp3KuH=x763_h=vp69k1kRF?iG05$63Gm!)n-u?7oXPj5KV79;3c6OygZ( z-5C~}X3>CddOBlq0s5c)^bYne&RF*pQwjQg|K;0XOE(xy?ffs_-XvOwhY@f#kp*;} zluP11!k|}U?VGHPcCYyfM=E7~v`vLsX?e}RGfzVOjUtP*#HpDN6;>~f*LhcV%u}ja zM4J=M*3(qr%?Bw{U3KlM0qmiukr52BM!82}r2gOThmT6Sak=Y|)!R$4$~7rwQwfou z+XJF+#f73w4o1^VLLioG;6hlk93&>Egjdm*AB7Zw6sN){cu2Vi^1qvWNf$zD+vO** z_k7*>M~?=AZEJMC(2{6L*Q#}{Cl;FX4;FXgh@{)?xJ*vM4_(Q5R_Ygb&7d#!bet|& zHR4=WhZzLy7VtKcMP1dsWmCbrq9&k^sr+dT1`wxE{bgzIjcGm5`@m0mO zp+Z5${9bY~QEj3~rY$RkQ=|4|%g5_Jm_VEI%#>6OcV&@ll;qNg|M~|Sz0>!N_vXK< zAe>6RB?Ji&I|WgT8LGATuOB_^o;36PX^K(iGT<{Vd)6|@@x`cck~hrYR(R>))1#?m zz+t#BbX@ccT5}<+wTy>wzEg#Up5w{(U!i%^s$7~VRAyKpwc4Iw^37dn~&ZiY$!WI*}C=I^-lx`E{ z;Z zLLyj(Qf6NDLj#o+qk*bu043jm%3E564J+s@<~Am&8BCvsZ@eUaVE^)-Nvz<<+#eq& znqO(nuj$PQO(U>9uL2kHnE9}nqURnGNDMhr3P^@W<>t#w^GRmK=zHE`(Uaekr-|-0 zB$IpTky?pFu1&kPXCb0MThG|QK$_U2-yzdrG_)+p!h##}ML+UKaf?2r`&Z}kTzjwI z4BJy!po3FmHQMrkgvhT=FW53AyTOgY*kqZ!J;^+Iw2&`{5tE*TZrgOKfABv5-*Fr2 zcrfYw<3DzShs6}tufD(hAJkJq*n!Nbqmwv4evOjmE(~SImTZis=E5T*PsfomgRMLA z?^n(FU1PrbmZ0Ov+{sFT>g+RIfqt1;!`pcX_(PLwA+qH=w-2L%hi@FxzD%i?G9&X1 z%MCPQ+r+-LL3w_@koWyYWFv!3g-&5n)@Q7MNIY;}#0M8qDa(_&ae55ebaa9IihpB` zc~nd?Zvy!p@=MdppS=xbS}RWg36K&Pl1A-5u{AqxAxR65L(e3`cLF|u&v}JgC+bStbma<4P57dNm@UG4>!vc&Z93vY>SPX6R$|9tj(>MOCDh4H zYc>uAIc2|Qvj4n)@C;!oLrZ9)^cLfV;ea!wOBRdokA=ghS|#09qmda|l-KPlFt-EL z=~ezYFWm!fmyGqEF@@*;@*gJ%>WkNdKlT^@U4er37Z5}A$6q`eNIAT1=e4CSK45eVh za!iuwoa*m`TNys1TNWUtU~U?(UWiHcrDb0ykO{W<&M0(^GjpWleMaD!O%q}g3Y@lg zHIhz6Gaid&^*K?umBHQQevhZY;VKY13`dcD$MxtMucx<2Dl2*?uq-E+MJ%=IT~IuB z_lBOT#WSu~>R6gO>y9A7&HMiT_i1rir9X1HJ8%euhaBKAjYkN&pSNIF*vUQrckKN#aD zv}vt}isZDKoKt1AlOvxW#HH+q4B2J(WtWEbGvAlu8`%TKgwwl%0=9l`W^6m0TtwmS zH3Z+{ViUg>hc{P&oGg4h-?Ghu8-FvP<(D&4YgWWf?NCc`ii$*a10#$G1L%{$ltM%? z{z#?-)3K_-@!WfTuH^uFxWe^xvNF};%Xi&YT*Q;<{O`UAz72)?45Izd7Z2OYMB_|Y z;~zgIspGf52eWlC$1};bDU9RYmVLaHSaQh=85Rvfsi}KNc{)ASGx|;vFgi#HFHu#Y z_&s0CtrZi+&S3ef$^Xl*>&MaeY^7IZ(lC2rBQFXac`|53A}=a0$XYXy^G{y#{dMaJ z;ua_ZwLFSG1F6Z^%20fbrZV+Z?z=(U%rYGnfADK zw}&&SG1x<%TenK%qDmSbu|7Q9P+ro2#>4@H^kR(oY^(^yujv9qp<;|+5^B!iBxzV7 z92Ccs&r8bG`u4J#!~!Lxq(kbTHZO8^B^GX{ka-C~z+3t&igyZ1l&K`p{@DL~S3Pvq zpIL=|{Nrbb(c7u+n}7V28&P5J*(77sqO-!X)wJj#)?;j_Sr2gjX}6l~_h^wa}PfSz(JR2B)A1{)(4ww_u83s`p!rNs8&wGG?6b zPJ04*oaUE51Nxo!S7b6;OoZ-FMKxGb%RzIH7pbm;LuU!HRC3!|i#-SA2?9cXoH{%t zk^;+jtRHdX`Gt%t%{0r4v1B<^>Wsm5bep+&pGg|SOHk9AL8}|qDWWE%_R|JG&(!RP zNxEWll>XF@pt^`0I+_8{pjoZx?z9B2(R`jViTQoY zP>gR1LJD7E;(M1BbH7Z-og3N99}cm(2{tQWvgnlP=c;2GI0oaze}m{TO`RP_YT&}y zGKA#0@areK(yymrdaWNFY{KR<*cF4k+e)zh(Ghw=hf%s z9EEr<7VXcSG!G1Ky3~!M49^zZ>+51<+{W_Hhq;cdsCVW1ae2Fchir6F3MhRLlAwIE zyo>fLZDRlTi95?JFf-7;pgrm``swK*eA1^7<16pibauZ_ZP#H`a3!B#Q^3PVsi{Sk zpkNmc0K^7+4xZj~u7>r~rT$u&i};Uk7ts8?&g?U?OW{9!2#rMlk1w8YbwXkBPvA2t zH(3m-&2D*F#v6>2QXI{m{H>1A4AWm);+jdtt_$Hjn@w?NuR0s41;;XP14MTGCo6OP zP?F7uvok$^-^X!_SUvP6=MuaRsXDE(dggL!n{D@6VedU{*s^a68$q2E?3u;__uw^b zZbeGrVrNf*ztRDLG9U3H$i?KW2dyc@Kvqj(da(VU_-bGaefF+$^kuOpfkbnaacXH7+kccC0Ww)As%ZdmH&xNi(xEj*^x2tPyAkkZ=+OZpAkbU|KT|g^%*R? z6#Khh564-_FA;vYM`>*72_hZq0zPY_axyX=e)`fpX5lrt0w-)Kd7;3t8WnH$gGFj8n3V{11DwF%KRF&@ zMj;kW1#n}{mX0ipsX`pEl!0lb4plF2-^{U;?zQ?tO?RS%98t9!aX@3wZ@BZ_aHy+J z$jnq2d-k-Vev5yhwP84_^{79Ji0;iGtz4S^q!J-g_7%M8O3Qu>T#E4KROUF`BkcyY zUS3FnL)xZa&1i*R~;54E*80T3fOlNRj@^` zuxw~c-CANezXrOa)fNvg>1&_No4p~Rw@K?Nnhvvk3ie0*!ZR3vaF-scJ#iAI#rCEy>duG}D`sT=FCsG+r341F?*NJVlQ6l;2uSYaDq?$+-4vLF}{n~n6 zV>C^}yP@VsMN#xec9m1Io3kbHZuHob7CWZ)S6CLAM3(I7+WP1Uq4@FNGxSei91f9L z0=g$pqcoivskd+30Pt{JOy6;E-epA=f{UT`>$G1z@p$;6h|lyM;StgUWltHCl?n6Y z2dAqp>9gC@wrm5ycqjk9&Jgs^H0Qa;P)8LD4!w^zGz;45K4GwW{1?p0sphjip0fol z1pW=s`|N-9bav(G0L=gOj{)8IdC%D9|E-6b6~m_{O!lpLLXJVh==XdFkf$1npWHgV zrx}a!plBiEe=K|uk9GG zTXR6{a%bt(a4{*re^3qxe2=PHB}&P%gi{n7zJw`c>u8pkV9KGko?o}PY@Y2s3u6Fs zEuiV;$eJFO$>iV9YVxgkM)O!JAhG3fS0)<$6t_7HX%B|nm_VMv)K!IVPU_ZrGAsq4 zHq$T|PD7DAss|JSpp;KV;tc?^#hpEQ96qE!_(1DHBq~ZbXj1-v;}=`sU$N(md%+l~!I&fHmdA^8peoZGZE+5sOQ5emJ1_lw4R8!axUs>|RJCwL^|$ zGNUxHfhhGdFr_%0ERJr{)!zrSD7DsnSlX=u`%i2bqWxLconPv9<}Moc>WeHe-O?vX z^o1j^G>NkYx@OV$${cmBD>PwR*PPykX0s)DVfA23XeqPd$<%HPRF9NOKfCagA(I$d zzf4hxU%0Dz$cEy{u^IFGWRw35ul$a=MS zEKw;AAPIU-9z1K)+2DmsAUjomg{dd%oX+qa^63L#*J2iZ{S%MS{H>{~!ZYUoUmquz zgzB1h0si8n|9gpL2m7}zFsFEp`%}BBw$hUj>%5jU%x59h_Tw^EQN#=-J=u+}`=y&38Mm7DAIC%_P>pnZ0UiIxDoe^6q(F zSA_n;Lh_j3up5(s;=o8E~)^QI}Oe4 zWK52e&Sgds2ey`hUMeZgT++L_eTOf2lBdHlME>7VYIGNae$BcOzB3phq0bNbeJW&k zxes*_eh3(8?zz1DH5gaTK2r9(LF7u%@S61tq#-a6@*etDoeYhIH?+DWN^2c@?!J=& z0p*Fnv7(*2&31SsIzSaZuCJ?JQ+yT-4PA1qs!hrpc!@sqfk*~4JW>}$5TFpr_YdEo zdTTn$6aOb3vHTPm&xB_G@r|^g=_8%4CO)4_l;&~p9sYM1=|d2g4H@|M^lK1K=C=6<>t6k^am>6+(Rpx#{^I>%su=0E$5<=>Us_f=fM}k*}DG z0Na)6yn3pielppVVPza?^4I_pMl8hXdw_k zmT`X!!Mf#UHX6_k9Ya~jT9ZKKx-Zw}1?#S96=8&#iYH9#p?|m4OITXJVMC2(eL~dn zfYmv>K><%r*L-r_@ar#Yucyy4=xqDR=vrUd+injXqd!YJ zf;Fz<0M&qs0GN&jkzRi(I7ty&Ux@m35ThLEe;U`H@D97p+w?Mh9f?xPPaVOpOMh;6z%bGW?8Xh{!r2m@iU?+!% zi+P`sU1R}5iKUYR(|anoqA}qXjv+lm^UtN0-@y->CMqEgVkm#`-v+?o>iJ2Y3;Tor z#(OPQddFLmb{4B|p05>f)PlIP1TsD#oj?2Sd8FOhots5n)Rtm@CBt(K9_23Bk>7Xi z9{GfVU!6D^U1DKbMqmBQ85Er)sM2s!*Xgs?R-?sFP=tBX3psrcdGxp;jR77=Zet>U zXXM@0q%8BA+)16zorSEAgb@N#mi-|oC-G_1z+IWSMzeZS;s)f&B#^5r5}77f-vFAb z<^))x1eR0V_tSj8LMFoqY80%DHemzpgC-IPF+y3MNY-Hx2Z)-ty3LT(?zIcnFD2~= z<~dPB3VWOAXS}$GeuWK8{$WhN`=KBHl^`7b=Qw?x30|~}tfVT8hW?FZeE4QwQ9sf+ zV>?bhvTWr}gsfEVHz!`FyZ1BunMU_H*t zsG<4e*@1pxQs&rEVPZeT(aD!9HdZ>^w7%#oYB$oQC=M~qjxddu&M_KB?DP2#V<2WcSwCE*SB%H?k@Y2-#Z82t{E%@uo@Tt-R}tc9n{Wy z1PcGY-z&Q426pQIX%K zbhGnl`^hA?d}PCAAZ=q}VB5}$uF|NbP0Yx&zhB>Y!Bz`EZ=HGlTJZ{ftM;iiHkj*t zCPha#Mqlb-+8OqWs^Z!i?|?1G&8l4rNr?mGNqBn+`PF_FMKiqy2uk)}fZvlNSx{0+ zoc?ekDFFLD36IhCCG)QNcfyAk>-Zb{s?1N403Z@|ot0@6U_!WfC3?#E3J$|d3=3## znJLG+f#~|3GQ};vx$=GxZnWea!&GO0<8{K#luo`f>ZGiLT=KhJ*B?AOMobo*FcfJO zyKc-{3t}gPZVPvy@E33mrKKALL`4v(AyNj2{O0r1 z=lu`f&&xl+_jB!>bDwjc>%J~3E8`33{eS%iZLTj8r!d<~2e8NI7~s+T6x+F2&v9P) z`O18<>~e?#bmtT6Gs5kGl0~z=V@$43y%FK3<{3n=L09g_4OOC{y)WAs-B;@v01;pv z0GRcUAy)th@$2MRlknpvb72s6B0c`qU5EZbB|u6HB_CLZB`vrvhYZ+bV;m2?V*5&- zHvIsRRWTw79^y}#W^=~jBoWLaI|@%WICj7T9MqCv8<3}6Qsa9?mpN#wB?(}M^3!k#KEL7X%v&>EW1*uaVACF3Sm6{E zxRmO2nxKIYB>p(78O@gqVGs7&W8;{qQeFDhonZ&41m8JtP)vZME>>MQ@S9X(#_V9? zXyQyYm3AHZUP2;uSJ;4Xnn)bTn|}hYcZ%Pi;w9fvZ!WGmYSBi8BmvZ;Ss?x{xUKaa zUW_ZALILmt_K}yfFU7D8*SxSYpFEP&WpTFfY3vejE<{`K6+LPPin>iMcgWqw25XIO zq|2^5$P>TUr7tvPtd(QI=E)h|ED{t41O1ZJ^Qif>-_{2%SqpQ78HdOQ;9hoZfW|B< z4Nm;2mdB^HJ&~hrhws+<<`Obl+_v6tJKCZ@Y#SVW&y$^DW_xO4lq!#>KCZH+kEdVY zYMk+(2JDEX6rmHUN#cKxfr6_+)_Qo&(^eWVQhBK`wKBa0LVQ!<>ia@(3yHX8KuB_6g>r3a;7{fMa8ZYXEB?)4oEc!VS^i|&JV!kE|c*Ci8 zfp=nCacpCx)b%@E3(kBY_ip|w)Vy;yX4g9H%Rx17Zjn}Ve*S8ANEiSxAbUWNj@aqVi>Dn6uKmKdDuM{POo;5Wv^moD`;IHuyzn= zPJ;l{Di5x>_l-{r5<5X>6JVZ2Peq&5N$#d7bN{}QEZjOfW_!8Gt6^TDQMm2URKF08 zH~l)G@&58*V`N0@X!EC^Ci-{XZlEYWnz>_4H0g?oUKrHSQITObh(KT+&GvjwwZUcC zFUM`Go2l%g zcPvtyu6savbNBM^apZ_7Z2VLihuW1(F8w^ai||O;L@*wgLJ>py*=a~(A zn@RDUqx_Kl#O|o{bt>TyWpbqb(sj1JE6MW>&L`--%b;KtT3{p@W1j&v4+ysJO+pU! zG<3&ur>D4e5zNs_1!c%yd5V0|8k;G;Z$=m}FxREsMJ=oz^d`nGA*Q!2898zfcFpKD z71hIyn3?rJkW0W!Kzj2r%25*$w4HYQ3BaACozRYj9wml-_7NX$DhkhIuGR@ zh!xO{s$EdmXgGM(7OgeE9SMtjtJkdf^3_o$GIh?adlPlip{%B)j8`q8qsi0B0dsj+ za(Vv2^9XbL+F}{6KfM0$e(B811y@0f&p8PUz9xu7IfWj)2M=NZ1i}GaqBSBIzAbGX zT4Fe-po}Oq%78JwT*ArJruxT0A2>_h5)$8A{}s;>4wX%1_jfj6y6sx0D7paaim4bg z(k#tiiT5zp45km8nurbLtmhULgnas%hSG#uxZi}qedOw?4_w}q8WiSgI1LYhoAk~fb586g_PL5d)tG37kITQY>@?+$3ID5D&#Hcy~rRgLaPLXYSWD)J?BRdz;f z3C}vlfmT|<7btuGIz6PY5u0jf-o&w)>(rZlsp{I6RF)e*799c14+-i zy_0c9FO_y%k(l)0tC3b#>!12?2so>?!Rr(1;rrYF?1Sj}!~JnhBGrHKw0eE>*nSLb zAs*%~km{6oBPm}(WMBDGDbT#IF$(X}ZJ3|u+ih={Vi=7T21kNvqJd1k4UI?%LtmDE+5=I)6pV4TaHwxF8J-PN&73b42V zNu~;EkI*V3DpfjXYyRqImdPUHNe@)Cv^>dJZk)JR+HuVG>!vD41ZH%fJZUfs7|mi`Wn)eD1bU+kNNU*JTxQ z^E%B#P5@7qQh>bOk;<>~ybl*w?SqZj{<8GLOuoFKJX16VT!Bi0yN>j491w}A+&9!n zA;)`gNxpv|tfnb0IjhWbg|!2TuF48s-7W`YyDWHSr_bY_ zo}Id2)QsmS`WFFtiQ9Ge*am*y`0_k0#(VGPL7(9@db-fA1MS9W+u_rSJHj^BnMXy_ z7vtq(_-E!!59P?`$$X$tAiy71%-;p}C_qS&9m$No2_Fk{(0@Td^{NpilETE2+Tag< z8>^!bNu@|2#pzR8xLaST`W78y{Nt>pn-J@tDR&iDA&*STL`Q462q1k3)t#?4drT2Zj3krP~OZeL7 z%QIpsl|* zxy2sW+_Kt8fGM-a?#zhR;^ptv3Ndy7kTF$uqH%!0iidz5tW^zWC{K!cM9Fd!Vu>TU z=6%;OFtmhb&ut_Woq6-YbTQLpZ${+uEUeqUqEm`yYsH*<{`39c%NGJnVvw0tIK?Rb zRltW^Y>c|sor~rG1Ax3!-(n<>g{c#0 z0M}L7Yv^j&A-B&{MNBPN-Svzv?ic@jO-{_dN4Jb}YO0JH9Lh6@fpChDn9$_|g=ng& zQQ6lzI`o_*k@>U?j3`T%gs)S=) zSiK}Q0QtHizicK9)tM4Y-NS|x3{{FO_*n$iOX)T9osgVS-HC64Eaw~{VHZA8b9pmQ zEbcToY`z^}?EXF1p*P_He>kAxB^0RFrM?~7E=qWRcP+?FvZ5mP21AXd>a(8>5m=JX zAEG5+ou6G)#diN1t}$c>;PWoSO6i1PnB6#m(zmIU;Vba?oU66#Ub9&Yb2Wp$6Y$}@ zKvqHulBHZ`nOgE21pfI~fnXO0-)X-l6~FnI>F!o&_jo(18sWV-IhCESWhhptQ$o@| zmQrCBq?tH^<{6uGe9UPSkDE1F6-N%S|E|Fv??3TUj6OB_ahkc~@_bP}JoxDwmj}vJ zE?fR4Zz^dAMkp`}8mt$G&)k_t|IT#(d-4P zy=s_-VuJ*MZUDYr2}M0}S4UJ6IhyXMMn z@$=Mquf}?nkG5(yo+P-w6tsyK;Mfp?dsw9G(JIr=aJsSblcOjUCmEoY+F7aBG^Y7GVGt1fk+{HDAiShWQU0|S z@sR@WQ?Gh-5}U#!Y=ymgRX(%DSKap(A&j!-Y67zw0$-%|!?a4jzv?RPjn57dch!vX zWQU|RD(x0$(cn@WsNN@eRP9w}B5#5gRcN0KeHWHC>!RGVk-z%DX0aR`)|kr2Ri1u} z#}2FeYe9t!-x^dBn}`{60@+zg;?pRXRDFh!EE+?9@Z1HQk!#N5=f{8f4k4Ds^B?&B z#ix{?#V^Xs2Boy+EWbTR^-0SvyIu~qanWlfNz61yE6bTut>yD^bNzH4ch%`n4p*qs z75y9+8u*L<#~Xv;b~`5oP&q5p2Q zW>jWEZn2@eD^`ief`1Jt`8ZC2F6wIzW;>_IR>Dn51~VG^rT)5J_OyZl%XM=31hO|w zGj6e;QivjbIo(pX5kzEQ_Up;^jKa@}XS-*m#3-)TIu_D(-bsT_8O{8ou&IupUCL^& zAxU2>S#|9BlAhAG@*jOI@A!UDvo~#RDPh#3^6u89^{FTUKvTeuqrlR^VaUL#^gSR( z9ZCcvj3T8F^8dZK1bP{so98@`HKjkL{V73b`RU9CosysIz?+%<82@_oA3X8;wC4K8 zVb`?&;R)VS)WnJW_deYE*+3w+?Z8Tqxs92o?Ku$d4RX+jip|OQKPisg)PCR%tY`t< z=&APmF61;`&@zdg171iy|JH9$!tyze<2SC-)MTuFq8o1vp$SqTNRt|bmn6`mA_)l} z^?ss_{U$_7GQQ|(Luwio5~AG^`B7A5WD>ys4O4xawU_mQwOq%FO7Il1K~0d9*U`kO z2D0;PTsqK0y8iu9BSNsa0L7hy?V`_pnmyIVds!kk-eMUrpYbr%v}pF}ho$e6A$-{q z%oEM%1fe!qv)JBIR!R+7l?XUX4}*47Qsyx8Tg{`UV|C=H4YWcqQW6`}@lzNm%H-~I z5kH{ z1-t)$zi@J?YlPQd#S=IG)8B$?WHk;%rvJs~A~x5FriFs{_mp$1(UMqL3+e57>Cl%t zfjsbBZ#R3da4wON`l$)5JxjE(7L_`JtGm36&?~-OQ)*$tn3o(Z5yxlxLV;WySEP{K zdCxX4zf_b2H|j#$Xy4e<*;w~Pl&e*{wBn&ss7op*kDt6H2jS6TV=8?ve2%$0Y^UB} z-!EA|b50sTuQO8QB=_QJox2}S@8j&$TRm$N+v|0<1NZF`Yq|w(8Kj422; z4T+f3!P0i?|K4bFx?tce(~|E)JM-uf*zn{-GeyXV}V~L#Vw+Z#Z6kC z>k9Pdk`*^s5PQ`}hhW(F^A$U|?4W}@U`CAWV5}->;bjfQ` zY8Lwbj1;R^=;3sfNy`Fw?JTuWO*X-qwcI`(+%ki*QTqp&LBQJ#Noq8m`+CV|O7_K=}XgV2b9- z%eCM1|29O~qCV)xA@;F)&$7h~hBQ<+8cHph3$7*YAFw-SOuP`ubJ&T{2@fmTGfpB0 zz0Man?KYQwzcUYh_00dQY4O#)hhuJp?z+8ztv7xeBF;GygRuMXI03M@QlL?=m)gwT z1ome|H_OJzKc;G}w^WSU%6*iFwlccTd)RpD)n@5^(_m@-n~y5m2KagN-~0|%WL3n$ zmJxsPnZ1+yWp&(ZI|48^n3-9@!6YcBjDq(NGK4~?NJ6D)`NfTgY!|pT_)uJ+$Sew> zL?)QI3-^MTLYJb;)iKpi$-JKWY&ajY&`VITL|N}FnS5WWSgORJQ!q)2jVYDyxc%qm z^Or~Hoyn7gJMG6knvy~xr8CAND|1yR{noYe0ZsNCz915JCmopa#?fAGXPv~nW%vgF z0EEm^Tvwo9r+sXEa!B`%cT^K_Up<*+lK#yz-X~J~gxNo3ekTC0=#E(hbdB|_76sCp zL0Sebf+Gt19BlDgFihb1NGa%da{Z6)1PTnZ|M3^Q`Ol@k+Gmxwu^IbU1h<9ebO`7r ze=ctc)_VK&`o0LY8EeVj*2T+HCtk}}N>ZC`eV<&UrvZNm%QQDg+#|vTA(A6h3^_Ly^%~Gbio9*w_h8LZ2;vdxu>V@s>Fk`5=WKx!1X&HKT@);6! zr+Bdjg?mqMlyN6-rnD21&D_uo+p(v0Cf5+T=+mD|lM-$s3MB~;!gM6UlD)*@PW6#* zy;9$ePj`XbfABfMoO0_w*8E1}W30lMxShYBy=Kb*2n^-UQSnW}mORQ9np z*f7_Ysfylu{1ler>PfrXmaai{)VSnJ~Pw1IX~>TlNCWTr`bBo}2iW+&C^@ zT9V8n6QgG1{p~%qmr$dk>4op|)}%Xhq0`OY4LJe3b>&|V@R3j7xz2)#U>pFr^sA4e1# z!fR_X2ivG4eJyFF*0hv3hpb_x_5s;|$hK8UsR^6ZD8y?l({v^L;_D1v*hK(YIyV_G z$-Npcs=>M}&(^Pg_ZYG}U!B(`KYx)EaGMEENTQ=7PLP>3Z_Q3u^IXhure?lD%UbPb z1e3g@J-CC7+(YD@md>V0vFWaB`(oA)VO^vV=NK6=qunRkj=4IJclzV?!O5&rb48f{ zlLc#Nex7-l+=rIk)poTG^rdcdxT);t8{nX-VSzVS^?@HXwi$~eU;F!>I}v}2@-jbc zHM$xB=XH0YjRinXbd%{1;A4$(h9876uwwf<_l_t)C%v`ya`f@U`P=8|&=(;3#KFxo z7SJT*ZM7dXh?l?uq9hvFEIvW8g(f zK>sZe-)G~kfB_FJOK@3~gEuWAURodB)A1#6ZRlmZS%pRR2F=?*1~G|ij+kChGA-lV zhRSKxA3bqQ&$y%|1R|7)SA0c3@xV1$eVH(6p{0^)zO{oJh9ervI~k@oZ^;CTMzy?) zjJmnrYdwADLbf8;*zAJP9JL1SGh90oKqE##mX(lU*+p63lu!&M8InZaB#x)HHaAF= z*^rEG@K3zq`GM+sx?vE>1R|wBNk~9TZ!eCo|NrnYOrq%n@U|*vs>DH6a~#P?gcI?D z(%HRAopD^poPu+AReq|6AxURt{ZYMnNXE<1%W;+mHoMnbGvOrP|F1MA9IqC@eK};52bZou(sZb%#94JIw_TajCpg> z(}zC1zmDA`Ts~G5)OE(f`Ltj$Wax*k?}PFybB4()#N?O&?de@zauq{&bcp5Fd$a~? zRo>P%U^#1~It4RaH$|vOawHc_^5JhimyCJdXiqJ}A8~NGR%H#Je?M=!CK9?i6it+7SE47A&i(MG_WJh{_Q<-AOF1>!NZR#c0Dyr`&&Mv= zxQ{?bnn19y)ys^z{-Dfgxg^r3umSDeFzPJCU%=T#lf8MgxgR~o1LL(JBi4Qrbl7%KsGv_%ZFp0YyEb@AP}a3& zSt++QPt_68dOlzN*1}UW7w$))W?$Jxa9xDs!x{?19Y|}J>!cmxV*cUs_-jN>$+DznPZBfXi|iz02{wRn`+c4p~d;sDHMhHXmA174JOKUL}@w+Vu9?) zl~*-^%U!asIW$s%mw=V*=Y)g(-2I}U$FkoHcD}`(nN>@o?3iM(X(bL>{eSR01)Mq= zj$hnT`TDmW(z3b<;b7+YA4LKmqrh1a#=EYgRuEv9RVAt09hN#uM!I3VH{5`B$j3k!fN1<$`Q*z82S05PZBZ&G5OdM+C6B2S` z4f5L&5MCWCQI6RuV=1x6omIe { diff --git a/src/engine/game/CombatSystem.js b/src/engine/game/CombatSystem.js new file mode 100644 index 0000000..9c40c4a --- /dev/null +++ b/src/engine/game/CombatSystem.js @@ -0,0 +1,101 @@ +import { CombatMechanics } from './CombatMechanics.js'; + +export class CombatSystem { + constructor(gameEngine) { + this.game = gameEngine; + } + + /** + * Handles the complete flow of a Melee Attack request + * @param {Object} attacker + * @param {Object} defender + * @returns {Object} Result object { success: boolean, result: logObject, reason: string } + */ + handleMeleeAttack(attacker, defender) { + // 1. Validations + if (!attacker || !defender) return { success: false, reason: 'invalid_target' }; + + // Check Phase (Hero Phase for heroes) + // Note: Monsters use this too, but their phase check is in AI loop. + // We might want to enforce "Monster Phase" check here later if we pass 'source' context. + if (attacker.type === 'hero' && this.game.turnManager.currentPhase !== 'hero') { + return { success: false, reason: 'phase' }; + } + + // Check Action Economy (Cooldown) + if (attacker.hasAttacked) { + return { success: false, reason: 'cooldown' }; + } + + // Check Adjacency (Melee Range) + // Logic: Manhattan distance == 1 + const dx = Math.abs(attacker.x - defender.x); + const dy = Math.abs(attacker.y - defender.y); + if (dx + dy !== 1) { + return { success: false, reason: 'range' }; + } + + // 2. Execution (Math) + // Calls the pure math module + const result = CombatMechanics.resolveMeleeAttack(attacker, defender, this.game); + + // 3. Update State + attacker.hasAttacked = true; + + // 4. Side Effects (Sound, UI Events) + if (window.SOUND_MANAGER) { + // Logic to choose sound could be expanded here based on Weapon Type + window.SOUND_MANAGER.playSound('sword'); + } + + if (this.game.onCombatResult) { + this.game.onCombatResult(result); + } + + return { success: true, result }; + } + + /** + * Handles the complete flow of a Ranged Attack request + * @param {Object} attacker + * @param {Object} defender + * @returns {Object} Result object + */ + handleRangedAttack(attacker, defender) { + if (!attacker || !defender) return { success: false, reason: 'invalid_target' }; + + // 1. Validations + if (attacker.type === 'hero' && this.game.turnManager.currentPhase !== 'hero') { + return { success: false, reason: 'phase' }; + } + + if (attacker.hasAttacked) { + return { success: false, reason: 'cooldown' }; + } + + // Check "Pinned" Status (Can't shoot if enemies are adjacent) + // Using GameEngine's helper for now as it holds entity lists + if (this.game.isEntityPinned(attacker)) { + return { success: false, reason: 'pinned' }; + } + + // Line of Sight is assumed checked by UI/Input, but we could enforce it here if strict. + + // 2. Execution (Math) + const result = CombatMechanics.resolveRangedAttack(attacker, defender, this.game); + + // 3. Update State + attacker.hasAttacked = true; + + // 4. Side Effects + if (window.SOUND_MANAGER) { + window.SOUND_MANAGER.playSound('arrow'); + } + + if (this.game.onCombatResult) { + this.game.onCombatResult(result); + } + + return { success: true, result }; + } +} diff --git a/src/engine/game/GameEngine.js b/src/engine/game/GameEngine.js index 8f100af..60936c0 100644 --- a/src/engine/game/GameEngine.js +++ b/src/engine/game/GameEngine.js @@ -1,6 +1,8 @@ import { DungeonGenerator } from '../dungeon/DungeonGenerator.js'; import { TurnManager } from './TurnManager.js'; import { MonsterAI } from './MonsterAI.js'; +import { MagicSystem } from './MagicSystem.js'; +import { CombatSystem } from './CombatSystem.js'; import { CombatMechanics } from './CombatMechanics.js'; import { HERO_DEFINITIONS } from '../data/Heroes.js'; import { MONSTER_DEFINITIONS } from '../data/Monsters.js'; @@ -14,6 +16,8 @@ export class GameEngine { this.dungeon = new DungeonGenerator(); this.turnManager = new TurnManager(); this.ai = new MonsterAI(this); // Init AI + this.magicSystem = new MagicSystem(this); // Init Magic + this.combatSystem = new CombatSystem(this); // Init Combat this.player = null; this.selectedEntity = null; this.isRunning = false; @@ -147,7 +151,73 @@ export class GameEngine { return monster; } + onCellHover(x, y) { + if (this.targetingMode === 'spell' && this.currentSpell) { + const area = this.currentSpell.area || 1; + const cells = []; + + if (area === 2) { + cells.push({ x: x, y: y }); + cells.push({ x: x + 1, y: y }); + cells.push({ x: x, y: y + 1 }); + cells.push({ x: x + 1, y: y + 1 }); + } else { + cells.push({ x: x, y: y }); + } + + // LOS Check for Color + let color = 0xffffff; // Default White + const caster = this.selectedEntity; + if (caster) { + // Check LOS to the center/anchor cell (x,y) + const targetObj = { x: x, y: y }; + const los = this.checkLineOfSightStrict(caster, targetObj); + + if (los && los.clear) { + color = 0x00ff00; // Green (Good) + } else { + color = 0xff0000; // Red (Blocked) + } + } + + // Show Preview + if (window.RENDERER) { + window.RENDERER.showAreaPreview(cells, color); + } + } else { + if (window.RENDERER) window.RENDERER.hideAreaPreview(); + } + } + onCellClick(x, y) { + // SPELL TARGETING LOGIC + if (this.targetingMode === 'spell' && this.currentSpell) { + const area = this.currentSpell.area || 1; + const targetCells = []; + + if (area === 2) { + targetCells.push({ x: x, y: y }); + targetCells.push({ x: x + 1, y: y }); + targetCells.push({ x: x, y: y + 1 }); + targetCells.push({ x: x + 1, y: y + 1 }); + } else { + targetCells.push({ x: x, y: y }); + } + + // Execute Spell + const result = this.executeSpell(this.currentSpell, targetCells); + + if (result.success) { + // Success + } else { + if (this.onShowMessage) this.onShowMessage('Fallo', result.reason || 'No se pudo lanzar el hechizo.'); + } + + this.cancelTargeting(); + if (window.RENDERER) window.RENDERER.hideAreaPreview(); + return; + } + // RANGED TARGETING LOGIC if (this.targetingMode === 'ranged') { const clickedMonster = this.monsters ? this.monsters.find(m => m.x === x && m.y === y && !m.isDead) : null; @@ -236,29 +306,38 @@ export class GameEngine { performHeroAttack(targetMonsterId) { const hero = this.selectedEntity; const monster = this.monsters.find(m => m.id === targetMonsterId); + return this.combatSystem.handleMeleeAttack(hero, monster); + } - if (!hero || !monster) return null; + performRangedAttack(targetMonsterId) { + const hero = this.selectedEntity; + const monster = this.monsters.find(m => m.id === targetMonsterId); + return this.combatSystem.handleRangedAttack(hero, monster); + } - // Check Phase - if (this.turnManager.currentPhase !== 'hero') return { success: false, reason: 'phase' }; + canCastSpell(spell) { + return this.magicSystem.canCastSpell(this.selectedEntity, spell); + } - // Check Adjacency - const dx = Math.abs(hero.x - monster.x); - const dy = Math.abs(hero.y - monster.y); - if (dx + dy !== 1) return { success: false, reason: 'range' }; + executeSpell(spell, targetCells = []) { + if (!this.selectedEntity) return { success: false, reason: 'no_caster' }; + return this.magicSystem.executeSpell(this.selectedEntity, spell, targetCells); + } - // Check Action Economy - if (hero.hasAttacked) return { success: false, reason: 'cooldown' }; + deselectEntity() { + if (!this.selectedEntity) return; + const id = this.selectedEntity.id; + this.selectedEntity = null; + this.plannedPath = []; + if (this.onEntitySelect) this.onEntitySelect(id, false); + if (this.onPathChange) this.onPathChange([]); - // Execute Attack - const result = CombatMechanics.resolveMeleeAttack(hero, monster, this); - hero.hasAttacked = true; - - if (window.SOUND_MANAGER) window.SOUND_MANAGER.playSound('sword'); - - if (this.onCombatResult) this.onCombatResult(result); - - return { success: true, result }; + // Also deselect monster if selected + if (this.selectedMonster) { + const monsterId = this.selectedMonster.id; + this.selectedMonster = null; + if (this.onEntitySelect) this.onEntitySelect(monsterId, false); + } } isEntityPinned(entity) { @@ -295,45 +374,6 @@ export class GameEngine { }); } - performRangedAttack(targetMonsterId) { - const hero = this.selectedEntity; - const monster = this.monsters.find(m => m.id === targetMonsterId); - - if (!hero || !monster) return null; - - if (this.turnManager.currentPhase !== 'hero') return { success: false, reason: 'phase' }; - if (hero.hasAttacked) return { success: false, reason: 'cooldown' }; - if (this.isEntityPinned(hero)) return { success: false, reason: 'pinned' }; - - // LOS Check should be done before calling this, but we can double check or assume UI did it. - // For simplicity, we execute the attack here assuming validation passed. - - const result = CombatMechanics.resolveRangedAttack(hero, monster, this); - hero.hasAttacked = true; - - if (window.SOUND_MANAGER) window.SOUND_MANAGER.playSound('arrow'); - - if (this.onCombatResult) this.onCombatResult(result); - - return { success: true, result }; - } - - deselectEntity() { - if (!this.selectedEntity) return; - const id = this.selectedEntity.id; - this.selectedEntity = null; - this.plannedPath = []; - if (this.onEntitySelect) this.onEntitySelect(id, false); - if (this.onPathChange) this.onPathChange([]); - - // Also deselect monster if selected - if (this.selectedMonster) { - const monsterId = this.selectedMonster.id; - this.selectedMonster = null; - if (this.onEntitySelect) this.onEntitySelect(monsterId, false); - } - } - // Alias for legacy calls if any deselectPlayer() { this.deselectEntity(); @@ -646,8 +686,16 @@ export class GameEngine { console.log("Ranged Targeting Mode ON"); } + startSpellTargeting(spell) { + this.targetingMode = 'spell'; + this.currentSpell = spell; + console.log(`Spell Targeting Mode ON: ${spell.name}`); + if (this.onShowMessage) this.onShowMessage(spell.name, 'Selecciona el objetivo (Monstruo o Casilla).'); + } + cancelTargeting() { this.targetingMode = null; + this.currentSpell = null; if (this.onRangedTarget) { this.onRangedTarget(null, null); } diff --git a/src/engine/game/MagicSystem.js b/src/engine/game/MagicSystem.js new file mode 100644 index 0000000..4c17133 --- /dev/null +++ b/src/engine/game/MagicSystem.js @@ -0,0 +1,159 @@ +import { CombatMechanics } from './CombatMechanics.js'; + +export class MagicSystem { + constructor(gameEngine) { + this.game = gameEngine; + } + + canCastSpell(caster, spell) { + if (!caster || !spell) return false; + + // 1. Check Class/Role Restriction + // For now hardcoded validation, but could be part of Spell definition (e.g. spell.classes.includes(caster.key)) + if (caster.key !== 'wizard') return false; + + // 2. Check Phase + if (this.game.turnManager.currentPhase !== 'hero') return false; + + // 3. Check Cost vs Power + // Assuming TurnManager has a way to check available power + const availablePower = this.game.turnManager.power; + if (availablePower < spell.cost) return false; + + return true; + } + + executeSpell(caster, spell, targetCells = []) { + if (!this.canCastSpell(caster, spell)) { + return { success: false, reason: 'validation_failed' }; + } + + console.log(`[MagicSystem] Casting ${spell.name} by ${caster.name}`); + + // Dispatch based on Spell Type + // We could also look up a specific handler function map if this grows + if (spell.type === 'heal') { + return this.resolveHeal(caster, spell); + } else if (spell.type === 'attack') { + return this.resolveAttack(caster, spell, targetCells); + } + + return { success: false, reason: 'unknown_spell_type' }; + } + + resolveHeal(caster, spell) { + // Default Logic: Heal all heroes in same section (simplified to all heroes) + let totalHealed = 0; + + this.game.heroes.forEach(h => { + // Check if wounded + if (h.currentWounds < h.stats.wounds) { + const amount = spell.healAmount || 1; + const oldWounds = h.currentWounds; + h.currentWounds = Math.min(h.currentWounds + amount, h.stats.wounds); + + const healed = h.currentWounds - oldWounds; + if (healed > 0) { + totalHealed += healed; + if (this.game.onShowMessage) { + this.game.onShowMessage('Curación', `${h.name} recupera ${healed} herida(s).`); + } + // Visuals + if (window.RENDERER) { + window.RENDERER.triggerVisualEffect('heal', h.x, h.y); + } + } + } + }); + + return { success: true, type: 'heal', healedCount: totalHealed }; + } + + resolveAttack(caster, spell, targetCells) { + const level = caster.level || 1; + + // 1. Calculate Center of Impact + let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity; + targetCells.forEach(c => { + if (c.x < minX) minX = c.x; + if (c.x > maxX) maxX = c.x; + if (c.y < minY) minY = c.y; + if (c.y > maxY) maxY = c.y; + }); + + // Exact center of the group + const centerX = (minX + maxX) / 2; + const centerY = (minY + maxY) / 2; + + // 2. Launch Projectile + if (window.RENDERER) { + window.RENDERER.triggerProjectile(caster.x, caster.y, centerX, centerY, () => { + + // --- IMPACT CALLBACK --- + + // 3. Central Explosion + window.RENDERER.triggerVisualEffect('fireball', centerX, centerY); + + // 4. Apply Damage to all targets + let hits = 0; + targetCells.forEach(cell => { + const monster = this.game.monsters.find(m => m.x === cell.x && m.y === cell.y && !m.isDead); + + if (monster) { + const damageDice = spell.damageDice || 1; + let damageTotal = level; + for (let i = 0; i < damageDice; i++) { + damageTotal += Math.floor(Math.random() * 6) + 1; + } + + // Apply Damage + CombatMechanics.applyDamage(monster, damageTotal, this.game); + hits++; + + // Feedback + if (this.game.onEntityHit) { + this.game.onEntityHit(monster.id); + } + + // Use Centralized Combat Feedback + window.RENDERER.showCombatFeedback(monster.x, monster.y, damageTotal, true); + + console.log(`[MagicSystem] ${spell.name} hit ${monster.name} for ${damageTotal} damage.`); + + // Check Death (Handled by events usually, but ensuring cleanup if needed) + if (monster.currentWounds <= 0 && !monster.isDead) { + monster.isDead = true; + if (this.game.onEntityDeath) this.game.onEntityDeath(monster.id); + } + } + }); + }); + } else { + // Fallback for no renderer (tests?) or race condition + // Just apply damage immediately logic (duplicated for brevity check) + let hits = 0; + targetCells.forEach(cell => { + const monster = this.game.monsters.find(m => m.x === cell.x && m.y === cell.y && !m.isDead); + if (monster) { + const damageDice = spell.damageDice || 1; + let damageTotal = level; + for (let i = 0; i < damageDice; i++) { + damageTotal += Math.floor(Math.random() * 6) + 1; + } + CombatMechanics.applyDamage(monster, damageTotal, this.game); + hits++; + if (this.game.onEntityHit) { + this.game.onEntityHit(monster.id); + } + console.log(`[MagicSystem] ${spell.name} hit ${monster.name} for ${damageTotal} damage (no renderer).`); + if (monster.currentWounds <= 0 && !monster.isDead) { + monster.isDead = true; + if (this.game.onEntityDeath) this.game.onEntityDeath(monster.id); + } + } + }); + } + + return { success: true, type: 'attack', hits: 1 }; // Return success immediately + } +} diff --git a/src/engine/game/TurnManager.js b/src/engine/game/TurnManager.js index 7c1fb9a..ef5a130 100644 --- a/src/engine/game/TurnManager.js +++ b/src/engine/game/TurnManager.js @@ -11,6 +11,10 @@ export class TurnManager { this.eventsTriggered = []; } + get power() { + return this.currentPowerRoll; + } + startGame() { this.currentTurn = 1; console.log(`--- TURN ${this.currentTurn} START ---`); diff --git a/src/main.js b/src/main.js index 6eb60a3..66c7350 100644 --- a/src/main.js +++ b/src/main.js @@ -116,19 +116,7 @@ game.onCombatResult = (log) => { const defender = game.heroes.find(h => h.id === log.defenderId) || game.monsters.find(m => m.id === log.defenderId); if (defender) { setTimeout(() => { // Slight delay for cause-effect - if (log.hitSuccess) { - if (log.woundsCaused > 0) { - // HIT and WOUND - renderer.triggerDamageEffect(log.defenderId); - renderer.showFloatingText(defender.x, defender.y, `💥 -${log.woundsCaused}`, '#ff0000'); - } else { - // BLOCKED (Hit but Toughness saved) - renderer.showFloatingText(defender.x, defender.y, `🛡️ Block`, '#ffff00'); - } - } else { - // MISS - renderer.showFloatingText(defender.x, defender.y, `💨 Miss`, '#aaaaaa'); - } + renderer.showCombatFeedback(defender.x, defender.y, log.woundsCaused, log.hitSuccess); }, 500); } }; @@ -294,7 +282,16 @@ renderer.setupInteraction( handleClick, () => { // Right Click Handler + if (game.targetingMode === 'spell' || game.targetingMode === 'ranged') { + game.cancelTargeting(); + if (window.RENDERER) window.RENDERER.hideAreaPreview(); + ui.showTemporaryMessage('Cancelado', 'Lanzamiento de hechizo cancelado.', 1000); + return; + } game.executeMovePath(); + }, + (x, y) => { + if (game.onCellHover) game.onCellHover(x, y); } ); diff --git a/src/view/GameRenderer.js b/src/view/GameRenderer.js index 23ae0b3..6c3f203 100644 --- a/src/view/GameRenderer.js +++ b/src/view/GameRenderer.js @@ -1,13 +1,20 @@ import * as THREE from 'three'; +import { DIRECTIONS } from '../engine/dungeon/Constants.js'; +import { ParticleManager } from './ParticleManager.js'; export class GameRenderer { constructor(containerId) { this.container = document.getElementById(containerId) || document.body; + this.width = this.container.clientWidth; + this.height = this.container.clientHeight; - // 1. Scene + // Scene Setup this.scene = new THREE.Scene(); - this.scene.background = new THREE.Color(0x1a1a1a); + this.scene.background = new THREE.Color(0x111111); // Dark dungeon bg + this.particleManager = new ParticleManager(this.scene); // Init Particles + + this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 0.1, 1000); // 2. Renderer this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false }); this.renderer.setSize(window.innerWidth, window.innerHeight); @@ -49,6 +56,10 @@ export class GameRenderer { this.tokensGroup = new THREE.Group(); this.scene.add(this.tokensGroup); + + this.spellPreviewGroup = new THREE.Group(); + this.scene.add(this.spellPreviewGroup); + this.tokens = new Map(); this.entities = new Map(); @@ -70,7 +81,7 @@ export class GameRenderer { this.scene.add(dirLight); } - setupInteraction(cameraGetter, onClick, onRightClick) { + setupInteraction(cameraGetter, onClick, onRightClick, onHover = null) { const getMousePos = (event) => { const rect = this.renderer.domElement.getBoundingClientRect(); return { @@ -79,6 +90,21 @@ export class GameRenderer { }; }; + const handleHover = (event) => { + if (!onHover) return; + this.mouse.set(getMousePos(event).x, getMousePos(event).y); + this.raycaster.setFromCamera(this.mouse, cameraGetter()); + const intersects = this.raycaster.intersectObject(this.interactionPlane); + if (intersects.length > 0) { + const p = intersects[0].point; + const x = Math.round(p.x); + const y = Math.round(-p.z); + onHover(x, y); + } + }; + + this.renderer.domElement.addEventListener('mousemove', handleHover); + this.renderer.domElement.addEventListener('click', (event) => { this.mouse.set(getMousePos(event).x, getMousePos(event).y); this.raycaster.setFromCamera(this.mouse, cameraGetter()); @@ -167,6 +193,30 @@ export class GameRenderer { }); } + showAreaPreview(cells, color = 0xffffff) { + this.spellPreviewGroup.clear(); // Ensure cleared first + if (!cells) return; + + const geometry = new THREE.PlaneGeometry(0.9, 0.9); + const material = new THREE.MeshBasicMaterial({ + color: color, + transparent: true, + opacity: 0.5, + side: THREE.DoubleSide + }); + + cells.forEach(cell => { + const mesh = new THREE.Mesh(geometry, material); + mesh.rotation.x = -Math.PI / 2; + mesh.position.set(cell.x, 0.06, -cell.y); // Slightly above floor/highlights + this.spellPreviewGroup.add(mesh); + }); + } + + hideAreaPreview() { + this.spellPreviewGroup.clear(); + } + addEntity(entity) { if (this.entities.has(entity.id)) return; @@ -292,6 +342,25 @@ export class GameRenderer { }; } + triggerVisualEffect(type, x, y) { + if (this.particleManager) { + if (type === 'fireball') { + this.particleManager.spawnFireballExplosion(x, -y); + } else if (type === 'heal') { + this.particleManager.spawnHealEffect(x, -y); + } + } + } + + triggerProjectile(startX, startY, endX, endY, onHitCallback) { + if (this.particleManager) { + // Map Grid Y to World -Z + this.particleManager.spawnProjectile(startX, -startY, endX, -endY, onHitCallback); + } else { + if (onHitCallback) onHitCallback(); + } + } + showFloatingText(x, y, text, color = "#ffffff") { const canvas = document.createElement('canvas'); canvas.width = 256; @@ -329,6 +398,33 @@ export class GameRenderer { this.floatingTextGroup.add(sprite); } + showCombatFeedback(x, y, damage, isHit, defenseText = 'Block') { + const entityKey = `${x},${y}`; // Approximate lookup if needed, but we pass coords. + // Actually to trigger shake we need entity ID. + // We can find entity at X,Y? + let entityId = null; + for (const [id, mesh] of this.entities.entries()) { + // Check approximate position + if (Math.abs(mesh.position.x - x) < 0.1 && Math.abs(mesh.position.z - (-y)) < 0.1) { + entityId = id; + break; + } + } + + if (isHit) { + if (damage > 0) { + // HIT and DAMAGE + this.showFloatingText(x, y, `💥 -${damage}`, '#ff0000'); + if (entityId) this.triggerDamageEffect(entityId); + } else { + // HIT but NO DAMAGE (Blocked) + this.showFloatingText(x, y, `🛡️ ${defenseText}`, '#ffff00'); + } + } else { + // MISS + this.showFloatingText(x, y, `💨 Miss`, '#aaaaaa'); + } + } triggerDeathAnimation(entityId) { const mesh = this.entities.get(entityId); if (!mesh) return; @@ -355,6 +451,7 @@ export class GameRenderer { }, duration); } + moveEntityAlongPath(entity, path) { const mesh = this.entities.get(entity.id); if (mesh) { @@ -381,6 +478,15 @@ export class GameRenderer { } updateAnimations(time) { + // Calculate Delta (Approx) + if (!this.lastTime) this.lastTime = time; + const delta = (time - this.lastTime) / 1000; + this.lastTime = time; + + if (this.particleManager) { + this.particleManager.update(delta); + } + let isAnyMoving = false; this.entities.forEach((mesh, id) => { @@ -553,13 +659,13 @@ export class GameRenderer { this.exitGroup.children.forEach(child => { if (child.userData.isDoor) { child.userData.cells.forEach(cell => { - existingDoorCells.add(`${cell.x},${cell.y}`); + existingDoorCells.add(`${cell.x},${cell.y} `); }); } }); // Filter out exits that already have doors - const newExits = exits.filter(ex => !existingDoorCells.has(`${ex.x},${ex.y}`)); + const newExits = exits.filter(ex => !existingDoorCells.has(`${ex.x},${ex.y} `)); if (newExits.length === 0) { @@ -598,7 +704,7 @@ export class GameRenderer { }; newExits.forEach((ex, i) => { - const key = `${ex.x},${ex.y}`; + const key = `${ex.x},${ex.y} `; const exDir = normalizeDir(ex.direction); if (processed.has(key)) { @@ -608,7 +714,7 @@ export class GameRenderer { let partner = null; for (let j = i + 1; j < newExits.length; j++) { const other = newExits[j]; - const otherKey = `${other.x},${other.y}`; + const otherKey = `${other.x},${other.y} `; const otherDir = normalizeDir(other.direction); if (processed.has(otherKey)) continue; @@ -635,7 +741,7 @@ export class GameRenderer { if (partner) { doors.push([ex, partner]); processed.add(key); - processed.add(`${partner.x},${partner.y}`); + processed.add(`${partner.x},${partner.y} `); } else { doors.push([ex]); processed.add(key); @@ -691,7 +797,7 @@ export class GameRenderer { direction: dirMap[dir] || 'N' } }; - mesh.name = `door_${idx}`; + mesh.name = `door_${idx} `; this.exitGroup.add(mesh); }); @@ -749,7 +855,7 @@ export class GameRenderer { }, undefined, // onProgress (err) => { - console.error(`[GameRenderer] Failed to load texture: ${path}`, err); + console.error(`[GameRenderer] Failed to load texture: ${path} `, err); const callbacks = this._pendingTextureRequests.get(path); if (callbacks) { this._pendingTextureRequests.delete(path); @@ -837,7 +943,7 @@ export class GameRenderer { }); } else { - console.warn(`[GameRenderer] details missing for texture render. def: ${!!tileDef}, inst: ${!!tileInstance}`); + console.warn(`[GameRenderer] details missing for texture render.def: ${!!tileDef}, inst: ${!!tileInstance} `); } } @@ -1111,12 +1217,12 @@ export class GameRenderer { preview.variant.exits.forEach(ex => { const gx = x + ex.x; const gy = y + ex.y; - exitKeys.add(`${gx},${gy}`); + exitKeys.add(`${gx},${gy} `); }); } cells.forEach(cell => { - const key = `${cell.x},${cell.y}`; + const key = `${cell.x},${cell.y} `; let color = baseColor; // If this cell is an exit, color it Blue @@ -1257,9 +1363,9 @@ export class GameRenderer { const filename = subType; if (type === 'hero') { - path = `/assets/images/dungeon1/tokens/heroes/${filename}.png`; + path = `/ assets / images / dungeon1 / tokens / heroes / ${filename}.png`; } else { - path = `/assets/images/dungeon1/tokens/enemies/${filename}.png`; + path = `/ assets / images / dungeon1 / tokens / enemies / ${filename}.png`; } this.getTexture(path, (texture) => { @@ -1267,7 +1373,7 @@ export class GameRenderer { token.material.color.setHex(0xFFFFFF); // Reset to white to show texture token.material.needsUpdate = true; }, undefined, (err) => { - console.warn(`[GameRenderer] Token texture missing: ${path}`); + console.warn(`[GameRenderer] Token texture missing: ${path} `); }); }; diff --git a/src/view/ParticleManager.js b/src/view/ParticleManager.js new file mode 100644 index 0000000..19bdc71 --- /dev/null +++ b/src/view/ParticleManager.js @@ -0,0 +1,216 @@ + +import * as THREE from 'three'; + +export class ParticleManager { + constructor(scene) { + this.scene = scene; + this.particles = []; + // Optional: Preload textures here if needed, or create them procedurally on canvas + } + + createTexture(color, type = 'circle') { + const canvas = document.createElement('canvas'); + canvas.width = 32; + canvas.height = 32; + const ctx = canvas.getContext('2d'); + + if (type === 'circle') { + const grad = ctx.createRadialGradient(16, 16, 0, 16, 16, 16); + grad.addColorStop(0, color); + grad.addColorStop(1, 'rgba(0,0,0,0)'); + ctx.fillStyle = grad; + ctx.fillRect(0, 0, 32, 32); + } else if (type === 'star') { + ctx.fillStyle = color; + ctx.beginPath(); + ctx.moveTo(16, 0); ctx.lineTo(20, 12); + ctx.lineTo(32, 16); ctx.lineTo(20, 20); + ctx.lineTo(16, 32); ctx.lineTo(12, 20); + ctx.lineTo(0, 16); ctx.lineTo(12, 12); + ctx.fill(); + } + + const tex = new THREE.CanvasTexture(canvas); + return tex; + } + + // Generic Emitter + emit(x, y, z, options = {}) { + const count = options.count || 10; + const color = options.color || '#ffaa00'; + const speed = options.speed || 0.1; + const life = options.life || 1.0; // seconds + const type = options.type || 'circle'; + + const material = new THREE.SpriteMaterial({ + map: this.createTexture(color, type), + transparent: true, + blending: THREE.AdditiveBlending, + depthWrite: false + }); + + for (let i = 0; i < count; i++) { + const sprite = new THREE.Sprite(material); + sprite.position.set(x, y, z); + + // Random velocity + const theta = Math.random() * Math.PI * 2; + const phi = Math.random() * Math.PI; + const v = (Math.random() * 0.5 + 0.5) * speed; + + sprite.userData = { + velocity: new THREE.Vector3( + Math.cos(theta) * Math.sin(phi) * v, + Math.cos(phi) * v, // Upward bias? + Math.sin(theta) * Math.sin(phi) * v + ), + life: life, + maxLife: life, + scaleSpeed: options.scaleSpeed || 0 + }; + + // Scale variation + const startScale = options.scale || 0.5; + sprite.scale.setScalar(startScale); + sprite.userData.startScale = startScale; + + this.scene.add(sprite); + this.particles.push(sprite); + } + } + + spawnFireballExplosion(x, y) { + // World coordinates: x, 0.5, y (assuming y is vertical, wait, 3D grid y is usually z?) + // In our game: x is x, y is z (flat), y-up is height. + // Let's check coordinates. Usually map x,y maps to 3D x,0,z or x,z, (-y). + // GameRenderer uses x, 0, y for positions typically. + + // Emitter + this.emit(x, 0.5, y, { + count: 20, + color: '#ff4400', + speed: 0.15, + life: 0.8, + type: 'circle', + scale: 0.8, + scaleSpeed: -1.0 // Shrink + }); + this.emit(x, 0.5, y, { + count: 10, + color: '#ffff00', + speed: 0.1, + life: 0.5, + type: 'circle', + scale: 0.5 + }); + } + + spawnHealEffect(x, y) { + // Upward floating particles + const count = 15; + const material = new THREE.SpriteMaterial({ + map: this.createTexture('#00ff00', 'star'), + transparent: true, + blending: THREE.AdditiveBlending + }); + + for (let i = 0; i < count; i++) { + const sprite = new THREE.Sprite(material); + // Random spread around center + const ox = (Math.random() - 0.5) * 0.6; + const oy = (Math.random() - 0.5) * 0.6; + + sprite.position.set(x + ox, 0.2, y + oy); + + sprite.userData = { + velocity: new THREE.Vector3(0, 0.05 + Math.random() * 0.05, 0), // Up only + life: 1.5, + maxLife: 1.5 + }; + sprite.scale.setScalar(0.3); + + this.scene.add(sprite); + this.particles.push(sprite); + } + } + + spawnProjectile(startX, startZ, endX, endZ, onHit) { + // Simple Projectile (a sprite that moves) + const material = new THREE.SpriteMaterial({ + map: this.createTexture('#ffaa00', 'circle'), + transparent: true, + blending: THREE.AdditiveBlending + }); + + const sprite = new THREE.Sprite(material); + sprite.scale.setScalar(0.4); + // Start height 1.5 (caster head level) + sprite.position.set(startX, 1.5, startZ); + + const speed = 15.0; // Units per second + const dist = Math.sqrt((endX - startX) ** 2 + (endZ - startZ) ** 2); + const duration = dist / speed; + + sprite.userData = { + isProjectile: true, + startPos: new THREE.Vector3(startX, 1.5, startZ), + targetPos: new THREE.Vector3(endX, 0.5, endZ), // Target floor/center + time: 0, + duration: duration, + onHit: onHit + }; + + this.scene.add(sprite); + this.particles.push(sprite); + } + + update(dt) { + for (let i = this.particles.length - 1; i >= 0; i--) { + const p = this.particles[i]; + + if (p.userData.isProjectile) { + p.userData.time += dt; + const t = Math.min(1, p.userData.time / p.userData.duration); + + p.position.lerpVectors(p.userData.startPos, p.userData.targetPos, t); + + // Trail effect + if (Math.random() > 0.5) { + this.emit(p.position.x, p.position.y, p.position.z, { + count: 1, color: '#ff4400', life: 0.3, scale: 0.2, speed: 0.05 + }); + } + + if (t >= 1) { + // Hit! + if (p.userData.onHit) p.userData.onHit(); + this.scene.remove(p); + this.particles.splice(i, 1); + } + continue; + } + + // Normal Particle Update + // Move + p.position.add(p.userData.velocity); + + // Life + p.userData.life -= dt; + const progress = 1 - (p.userData.life / p.userData.maxLife); + + // Opacity Fade + p.material.opacity = p.userData.life / p.userData.maxLife; + + // Scale Change + if (p.userData.scaleSpeed) { + const s = Math.max(0.01, p.userData.startScale + p.userData.scaleSpeed * progress); + p.scale.setScalar(s); + } + + if (p.userData.life <= 0) { + this.scene.remove(p); + this.particles.splice(i, 1); + } + } + } +} diff --git a/src/view/UIManager.js b/src/view/UIManager.js index 0ce87ec..8e63609 100644 --- a/src/view/UIManager.js +++ b/src/view/UIManager.js @@ -1,4 +1,5 @@ import { DIRECTIONS } from '../engine/dungeon/Constants.js'; +import { SPELLS } from '../engine/data/Spells.js'; export class UIManager { constructor(cameraManager, gameEngine) { @@ -532,14 +533,14 @@ export class UIManager { statsGrid.style.marginBottom = '8px'; const stats = [ - { label: 'WS', value: hero.stats.ws || 0 }, - { label: 'BS', value: hero.stats.bs || 0 }, - { label: 'S', value: hero.stats.str || 0 }, - { label: 'T', value: hero.stats.toughness || 0 }, - { label: 'W', value: `${hero.currentWounds || hero.stats.wounds}/${hero.stats.wounds}` }, - { label: 'I', value: hero.stats.initiative || 0 }, - { label: 'A', value: hero.stats.attacks || 0 }, - { label: 'Mov', value: `${hero.currentMoves || 0}/${hero.stats.move}` } + { label: 'H.C', value: hero.stats.ws || 0 }, // Hab. Combate + { label: 'H.P', value: hero.stats.bs || 0 }, // Hab. Proyectiles + { label: 'Fuer', value: hero.stats.str || 0 }, // Fuerza + { label: 'Res', value: hero.stats.toughness || 0 }, // Resistencia + { label: 'Her', value: `${hero.currentWounds || hero.stats.wounds}/${hero.stats.wounds}` }, // Heridas + { label: 'Ini', value: hero.stats.initiative || 0 },// Iniciativa + { label: 'Ata', value: hero.stats.attacks || 0 }, // Ataques + { label: 'Mov', value: `${hero.currentMoves || 0}/${hero.stats.move}` } // Movimiento ]; stats.forEach(stat => { @@ -599,6 +600,50 @@ export class UIManager { }; } card.appendChild(bowBtn); + } else if (hero.key === 'wizard') { + // SPELLS UI + const spellsTitle = document.createElement('div'); + spellsTitle.textContent = "HECHIZOS (Poder: " + (this.game.turnManager.power || 0) + ")"; + spellsTitle.style.marginTop = '10px'; + spellsTitle.style.fontSize = '12px'; + spellsTitle.style.fontWeight = 'bold'; + spellsTitle.style.color = '#aa88ff'; + spellsTitle.style.borderBottom = '1px solid #aa88ff'; + card.appendChild(spellsTitle); + + SPELLS.forEach(spell => { + const btn = document.createElement('button'); + btn.textContent = `${spell.name} (${spell.cost})`; + btn.title = spell.description; + btn.style.width = '100%'; + btn.style.padding = '5px'; + btn.style.marginTop = '4px'; + btn.style.fontSize = '11px'; + btn.style.fontFamily = '"Cinzel", serif'; + + const canCast = this.game.canCastSpell(spell); + + btn.style.backgroundColor = canCast ? '#4b0082' : '#333'; + btn.style.color = canCast ? '#fff' : '#888'; + btn.style.border = '1px solid #666'; + btn.style.cursor = canCast ? 'pointer' : 'not-allowed'; + + if (canCast) { + btn.onclick = (e) => { + e.stopPropagation(); + + if (spell.type === 'attack') { + // Use Targeting Mode + this.game.startSpellTargeting(spell); + } else { + // Healing is instant/global (no target needed for 'healing_hands') + this.game.executeSpell(spell); + } + }; + } + + card.appendChild(btn); + }); } card.dataset.heroId = hero.id; @@ -714,12 +759,12 @@ export class UIManager { statsGrid.style.fontSize = '12px'; const stats = [ - { label: 'WS', value: monster.stats.ws || 0 }, - { label: 'S', value: monster.stats.str || 0 }, - { label: 'T', value: monster.stats.toughness || 0 }, - { label: 'W', value: `${monster.currentWounds || monster.stats.wounds}/${monster.stats.wounds}` }, - { label: 'I', value: monster.stats.initiative || 0 }, - { label: 'A', value: monster.stats.attacks || 0 } + { label: 'H.C', value: monster.stats.ws || 0 }, // Hab. Combate + { label: 'Fuer', value: monster.stats.str || 0 }, // Fuerza + { label: 'Res', value: monster.stats.toughness || 0 }, // Resistencia + { label: 'Her', value: `${monster.currentWounds || monster.stats.wounds}/${monster.stats.wounds}` }, // Heridas + { label: 'Ini', value: monster.stats.initiative || 0 }, // Iniciativa + { label: 'Ata', value: monster.stats.attacks || 0 } // Ataques ]; stats.forEach(stat => {