From 8b2a4ea0268c103404387366078c1e57b9fba70f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Sun, 19 Feb 2017 22:33:07 +0100 Subject: [PATCH] Switched to individual power points display in actionbar --- TODO | 2 +- graphics/ui/battle.svg | 37 +++-- .../images/battle/actionpointsempty.png | Bin 1020 -> 0 bytes out/assets/images/battle/actionpointsfull.png | Bin 1160 -> 0 bytes out/assets/images/battle/actionpointsnone.png | Bin 401 -> 0 bytes out/assets/images/battle/actionpointspart.png | Bin 1171 -> 0 bytes out/assets/images/battle/power-available.png | Bin 0 -> 1009 bytes out/assets/images/battle/power-used.png | Bin 0 -> 1078 bytes out/assets/images/battle/power-using.png | Bin 0 -> 406 bytes src/core/BattleLog.ts | 13 +- src/core/events/ShipChangeEvent.ts | 5 + src/ui/Preload.ts | 7 +- src/ui/battle/ActionBar.spec.ts | 79 ++++++++--- src/ui/battle/ActionBar.ts | 130 ++++++++++++------ src/ui/battle/ActionIcon.ts | 6 +- src/ui/battle/ActionTooltip.ts | 2 +- src/ui/battle/BattleView.ts | 9 +- src/ui/battle/LogProcessor.ts | 21 ++- 18 files changed, 222 insertions(+), 89 deletions(-) delete mode 100644 out/assets/images/battle/actionpointsempty.png delete mode 100644 out/assets/images/battle/actionpointsfull.png delete mode 100644 out/assets/images/battle/actionpointsnone.png delete mode 100644 out/assets/images/battle/actionpointspart.png create mode 100644 out/assets/images/battle/power-available.png create mode 100644 out/assets/images/battle/power-used.png create mode 100644 out/assets/images/battle/power-using.png diff --git a/TODO b/TODO index 4262f03..46e9b8f 100644 --- a/TODO +++ b/TODO @@ -2,7 +2,7 @@ * Highlight ships that will be included as target of current action * Do not focus on ship while targetting for area effects (dissociate hover and target) * Active effects are not enough visible in ship list (maybe better in arena ?) -* Discrete power display, instead of the continuous power bar +* All things displayed in battle should be updated from BattleLog, not from current state * Drones: add tooltip * Drones: add hull points and take area damage * More sound effects diff --git a/graphics/ui/battle.svg b/graphics/ui/battle.svg index 5df9076..6b68664 100644 --- a/graphics/ui/battle.svg +++ b/graphics/ui/battle.svg @@ -890,11 +890,11 @@ borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:zoom="0.79411404" - inkscape:cx="713.78747" - inkscape:cy="706.929" + inkscape:zoom="3.1764562" + inkscape:cx="257.62353" + inkscape:cy="1025.9327" inkscape:document-units="px" - inkscape:current-layer="layer32" + inkscape:current-layer="layer8" showgrid="false" units="px" showguides="true" @@ -1789,13 +1789,34 @@ style="display:inline"> + inkscape:export-ydpi="90" + transform="matrix(0.98149613,0,0,1.5780874,-2.5439867,-65.79016)" /> + + _bFcx)Hy&{xffQSkx4R3&e-K=-cll(X2xoyu zWHC_Hbr5EB(wJQV6l5>)^mS!_$SA_a%n)7zsii%G|+??-=~ zw?3S(s;;_p;+wsEYaDo()@dJ5O&=!M z?B66Gy`y7yJMVytLTLT>*2pxge8%-m-Umz*HtjX#*uLt|)}5(4RH6Quv?TLb zRMeMK%fe(Bb008dF!?LUGw&AQZF+y=&!!tY?o08^lybZJe$AaFr*%7|r#!r-RKWU4 z$Ytj&fvlcGYd3xilPZ9jq{7*8a#|Sw3kKoU>^GZhTqLvpvVCNozI~r=SK{f_bIR_d zzsf#;U!l63GcM`MJ*jBl6Rd7;*LD`1M)>EFWqHhst87tIbl27{sQ1>E7O)rE+u!~5 z?c7Y}|Aq~()~nUlg_>pmg&&_*M`*)kJ-C6qRCOkTR-Iv_5Rc%((%~$K? zWLlzlAA~=M+xYSHv;Dlbg+D^#qhs6dwF~G6w?%gYLm@#ov3Jwfr+al_b}uP@onO9n z*A#P*5j(j*GEe=nV#oHawTn(J|0$ikn)Cj$Jv&Q9cd%IU8|JDmgvZ~x`I*`r{2KXL zZy9&H)VO}T*4_Ji?PNaNV33VFIq$Sqv;}5o^Q~|^=MZ9W1MXI<^+KE2FSfpM_;NdH zC;vx*q`fz8{Cc*<`MiwX)%P~rUCN);o)DK@r+h~0NP_Ic;Iq?V7GA2qbfb3d8>gQ2 zihnw1{#f#(vH3QhZ7@xf&dt2oyLO5Dg+`mE mQ_Q!{o_+NBe*ZbEAO4Sw#eT7Kd&B|rDTAl0pUXO@geCw$xXmE| diff --git a/out/assets/images/battle/actionpointsfull.png b/out/assets/images/battle/actionpointsfull.png deleted file mode 100644 index 9c4442bc4516468a0038c0910ec74d5f76485b95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1160 zcmeAS@N?(olHy`uVBq!ia0y~yVA}>_bFcx)Hy&{xffQSkx4R3&e-K=-cll(X2xoyu zWHC_Hbr5EB(wJQV6l5>)^mS!_$SA_aEV^UEPDTa>7EwF!Z||P&PWdW%?Bo02 ztG(l|%zW2pm7q{D+t;P(g<6xMb)d?uqdI4_>Z8h<9Qg+^TATxZM$>R+Gl^cb96`Ug{y4L z*F1Vx@^Ct@lBzZJU<}ZiH%Y}4f-b$~h};vB@zS%($^B~2DMi`fOY#O1elLs{rXR}K z63y`Q$IjmpeD(g*WmnyAvhpqDf2g<0%;Q={k4IZTuMYD%iF~L*lT=>LU=nHMQVFqg za?fJ(S$U^rm*f2GEw|X$UAV5d_@n#R2R~1*j?kLFe~YX6U*)xR-uJ~A!?y-c*v&b6 z!q=m0;?9Q}79G^OW^@nXHvf~aB&)jm43-yhn5xbUD3yJs^@J;Q_mcf*9LqP#RcDLu z6Zd#}X{X`q$@YPN7;h-%+~2f!YEFPmYqYb|;=m3^{4~Mf< zZ%uji=>~7O+cvC_VNnqITNIq_7ZJigY4(ylhVaatPB)alnqK{P^sxB7^-;N%y379F zxc_AG{(JuR)ibBLue`;$O>w!#w5u&!`6PwgPDeSd=2V3_u;j_LfYtGBQCAdGxJ(;k z+HbYTG`_jC{k+84xRvbpcl~i@t~Y8pwX`_-*kp0Z4?Lm!7oO%jsu>X`e8}pkMBlr- zwOX9K+hDOe$#0X=5+|W9z8S$XOzbV3M@O? zTYq(Ne$1bG=YCncrdngXPnti! z{5<}$a^|Y)=~G)~n*bBPhy8+8iJ}YPPVp4gvdxtJQoKs6eCwpwA-~yP)z}t2+iEOpeKlq6FMr595$y6kVRf=&72}1vcQvpeQTeM>TOF_ca>Xo1*#IApeF=;AM)Ak| zc;?6Uf6W8U_x6^LzA*bcm$TjKuyby^8fMy(x&aZ#i)3PNEIMRkyY|_I^9dj1zwO@@ g;s3X~Z298<>>{q+(J^-YpfZEO)78&qol`;+06Vx4-~a#s diff --git a/out/assets/images/battle/actionpointsnone.png b/out/assets/images/battle/actionpointsnone.png deleted file mode 100644 index b93a437416e2ecd5b7c359497ffda793310fdfda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 401 zcmeAS@N?(olHy`uVBq!ia0y~yVA}>_bFcx)Hy&{xffQSkx4R3&e-K=-cll(X2xoyu zWHC_Hbr5EB(wJQV6l5>)^mS!_$SA_aETzG}WC8;NqnxLUV@SoVw|5Qw4jYKDJ-C1J z0Pl?(+NI}xABfoc@iE$ZeJc>~JJ6uynBg3=GIzqh{LYl)+n#)V`CYqxI?zZUIPh$$ zO5hzPFgxM&ujET=Ck@o@22Wz24iP)B?aT4?`7`}J@)^Nu3d&Vq-ml}^`nkT%@MW2& z)OS{}@B!H^p8tPU@6cI0@g79ugEYS-ThcC{7TFF~z_6or<@9ToXIJsc#i~F=!asd1 z57dsFc;nZ*4>7wTx){zy2Va=A#Z;RAz32ARU30`CN_8@({mz_QRQS*9y)Hy#)zn{2 w*Ou)4awgGdSFMdN2Uzq#mZ?8K)ZP2PaJH4n`Y$uEJq?ocboFyt=akR{0M#jqJ^%m! diff --git a/out/assets/images/battle/actionpointspart.png b/out/assets/images/battle/actionpointspart.png deleted file mode 100644 index 7bd47d67aaca9c2481d7065b110e9e6ac5bd73ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1171 zcmeAS@N?(olHy`uVBq!ia0y~yVA}>_bFcx)Hy&{xffQSkx4R3&e-K=-cll(X2xoyu zWHC_Hbr5EB(wJQV6l5>)^mS!_$SA_aEFX~?AkV$r zTUSq1eZNk&dxFt)r39ZYj~gc@&a`@s|nW;@sy5ByCryz1#JkD2vU`J zd`fBKp4C@Vw-{SL-=mjSmctq!R+RewbMwPFuV!D~U-LeEvHkqpPf~Ut&DrK?;kf_W z%dEL=p(_}d@PAn-6~w!QLF1Xf=Oh&fdYRaACSEJV%ELWlrB#E^%NY~iTz=jA(nzi6 z<=1kqgfG{P?H64CZ7F*@WIN|qi=&>0I9FZs4ATsIrg&YPX`Mtq!k`W&kwz|+kSHhb zR}3=(cJi0@>y>9@@$YaAxBl|`zg%3!^J7NSr1sY3P0stJ z>*WvrGp)S1^fNQtSoDC zu!eEeN=bb>`c{`&5ANTCdx``a0!q4l*GeYF*?PR2yo>$LvXi=p^j7+>sOpxF`~AFN z$DL2J{C}_7FS)nso#v&u1)qdhZEMs$q1okXnz+`Ht>xdMX&17b;3mpe8C-cKwouHp z)n~!63A+QTCqz$>-Sy1**JkrJ@pboqY-)^`cIefcD}Q1B9*_R#3+F93ZFW>MB1HI* z)KLb$_j$V7oV?rMf!DWLX^E3im)MLzpT^olGam76aKF*|hT(nC*OY5sbAJ1_7tFR1VM1Q82z_~Dr32Fz5#ylX6SWWY9-1B13w?iKTX>uPesjxD zSKNP2xtN_kf7xTEP=7rwPVZD}smO1P z%IB*ue*0`O&%dyPmtXkXM?X9weBQBKYL@6EyS-V{Oj}YnsKUauWQ|PRjYUqg`&LPo quwVEyy?TC}?7mO)o84vqO7c3ioPGRC-wjxNFnGH9xvXb@sx_@OaQ!`i1%6T(Txni zolWx;m^?S9Tjs$*)U}|`zx)isUF=QJ2vP%7Cjh;vyG5rR zEf>~&y>RAGe-y8@ybK*l6Q2h0d+NCv4zUL&S|0Qy1IeT1})c?;M^WKdIM2Lzbq zlL~{4h`=0}mWZ2!$5;bW{i+w#`vd}?5ZOj-gSN3ZkQ#6egv;M78`6MCKm=MuMIvHP z2n>)S-+uSHU(v5We}|--z}D5Pb?jkIMg>$$S2X)mBuocXphWPL6l!n){i%IYKd6WI z2x$YnfwYm;WQ1uD3m#p1+~%;&YIdb2wpk1-d)L2T%>F-Ul|BX5LF>Q>v4&YIpaF_U zm(OW(8j1wKMFhE&8+GXHMK7qIe6d6Fdx#Nejl>Yq00Tsg36~z#lC!O4a3Ww#z^K4y zNT=U)=V02eXw%$9aR_0EJirWcxKb6suoCWEs4W?gg%YDV(31S~;_Np;gYSVF^DB_Fqq88E&Gh?VT%{2H3D zGN5P?Gwc~?0=(4i?~nQw4Z2Ozp!l#DnuzHZ%)b0S*MN#YAtGAL6fpsxBb^id=M^a1hv?QOH z)nqkVzr~IysLQoeW8f4xCHNKJJpQX+(XaO2My^3~0@J)hRHYaXN=KxX=x?vmQ37|x zo9DnWfn$<>y6A>_LO0z+p~af!MH(Qxob76)9JZ@+3YAOd{Y69Okxe$DCTi(XJ4?hQc|c#`8>BGBbs3Ij;t z(w{-AqiBZBhix$dosrU^K7Q0**D;EN2NheVVzevn1JBAmQ9W6mE0ezQ2k>sWCu%p% feWLUz_D%g4nHkRc9C*XE00000NkvXXu0mjfYxl!+ literal 0 HcmV?d00001 diff --git a/out/assets/images/battle/power-used.png b/out/assets/images/battle/power-used.png new file mode 100644 index 0000000000000000000000000000000000000000..37f0d07612ba6f40e8b3c6ded36c3fd60e8e8e89 GIT binary patch literal 1078 zcmV-61j+k}P))yS48OYbJ zy%fOl@kDg|T3sC)U}NKwA3b`^l`BiW{$$moot@^~#eehlXP*O`$d}|awB2_i%gB;a z2fzX#f|L~40h)nJlTHvv5C?KP(6IMoagI0p_WlP+tVeJec_`G!Q6NdS0xsOc0+VFh z1~nFytci(&kVz^N{`>y@@5+UsZ(lyTsibu~)|GS_ST-IYGA2n#9r37)F=4sJsRZ}c z`k<0oBC`ju5LBuDP-xB8TfiD<89X3VAd-D*&<=PM8>tbVS)KyIo`Ft~d!=J{A?RpC z+I_B%)x;Xo8nA|0k?30#Kr2sQFsShSC4dH0$z<#XF#&o3&GqX$`wKyLpMG(JqP}Xh zDp)nFBDbN7@)@Wu+@nTq*a9_>4IycYU4RmepN>Xt&U!O+Ro{}ef><%EU{;WZh(2Zx z>JCVBu1;v?pnxiHo>v9uBL8tZ`VG*W^hpED!poo`Vo9*%pietLbwHk|6{o??>C)S^ zL4vTUkILDzg`nSl_2qi0`W*#B#1b$7_YFNnG5=8)N+<>+hl~vgBel8>i`o&tKYX|| zT?jf=vze`drY+EvRBUF(*!Mv=+Z`<43)j0-2qdRx<^UY%& w987il_DAgP{r}F1Uc9(upxNHuZWfLE2TpwziYZ}-0{{R307*qoM6N<$g4JN^WB>pF literal 0 HcmV?d00001 diff --git a/out/assets/images/battle/power-using.png b/out/assets/images/battle/power-using.png new file mode 100644 index 0000000000000000000000000000000000000000..a4695bafed5c5273c851f4c223a7e707003d463f GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^RzNJu!3HG79siyNQfx`y?k)`fL2$v|<&%LToCO|{ z#Xud`L734=V|E2lkiEpy*OmPtlL(iF$iH+xJq89wWltB!kch)iui5H12Z|i{_x|g}j*zWW8{Sm}X2~fX_;SC!ZuuVGGe1vw zSL*4NtUoKSS}w9}QQ(r9Hv<+vW1E%z=O&-y<>{A86RvV@o?nytTrH&e-EYlpRf?y3 z*YSqRY!&U?@6Y_=cyJ7(*`1Q9K7GC!8Xn1amV9|Xr64$Njopj@8DD3|w!>u%SC5~L zOgQaZ_AK&t%QDY=aShADd*WJOqlLRx%+Fd~q@rZS6q@V1GU%(xx0#i{e_Czk)Uiw~ y>-zjYJeg|>P(^{-B_Cf63#s2d+l&fdOW$}Ms any; + // Log of a battle // This keeps track of all events in a battle // It also allows to register a callback to receive these events @@ -7,7 +12,7 @@ module TS.SpaceTac { events: BaseLogEvent[]; // List of subscribers - private subscribers: Function[]; + private subscribers: LogSubscriber[]; // List of event codes to ignore private filters: string[]; @@ -43,7 +48,7 @@ module TS.SpaceTac { this.events.push(event); - this.subscribers.forEach((subscriber: Function) => { + this.subscribers.forEach(subscriber => { subscriber(event); }); } @@ -54,14 +59,14 @@ module TS.SpaceTac { } // Subscribe a callback to receive further events - subscribe(callback: (event: BaseLogEvent) => void): Function { + subscribe(callback: LogSubscriber): LogSubscriber { this.subscribers.push(callback); return callback; } // Unsubscribe a callback // Pass the value returned by 'subscribe' as argument - unsubscribe(callback: Function): void { + unsubscribe(callback: LogSubscriber): void { var index = this.subscribers.indexOf(callback); if (index >= 0) { this.subscribers.splice(index, 1); diff --git a/src/core/events/ShipChangeEvent.ts b/src/core/events/ShipChangeEvent.ts index 5df7973..8f1f848 100644 --- a/src/core/events/ShipChangeEvent.ts +++ b/src/core/events/ShipChangeEvent.ts @@ -3,8 +3,13 @@ module TS.SpaceTac { // Battle event, when a ship turn ended, and advanced to a new one export class ShipChangeEvent extends BaseLogEvent { + // Ship that starts playing + new_ship: Ship; + constructor(ship: Ship, new_ship: Ship) { super("ship_change", ship, new_ship ? Target.newFromShip(new_ship) : null); + + this.new_ship = new_ship; } } } diff --git a/src/ui/Preload.ts b/src/ui/Preload.ts index 501eccc..df140e7 100644 --- a/src/ui/Preload.ts +++ b/src/ui/Preload.ts @@ -36,10 +36,9 @@ module TS.SpaceTac.UI { this.loadImage("battle/action-active.png"); this.loadImage("battle/action-selected.png"); this.loadImage("battle/action-tooltip.png"); - this.loadImage("battle/actionpointsnone.png"); - this.loadImage("battle/actionpointsempty.png"); - this.loadImage("battle/actionpointsfull.png"); - this.loadImage("battle/actionpointspart.png"); + this.loadImage("battle/power-available.png"); + this.loadImage("battle/power-using.png"); + this.loadImage("battle/power-used.png"); this.loadImage("battle/ship-tooltip-own.png"); this.loadImage("battle/ship-tooltip-enemy.png"); this.loadImage("battle/ship-tooltip-effect.png"); diff --git a/src/ui/battle/ActionBar.spec.ts b/src/ui/battle/ActionBar.spec.ts index 16ba480..61d8623 100644 --- a/src/ui/battle/ActionBar.spec.ts +++ b/src/ui/battle/ActionBar.spec.ts @@ -8,25 +8,25 @@ module TS.SpaceTac.UI.Specs { // Ship not owned by current battleview player var ship = new Ship(); bar.setShip(ship); - expect(bar.actions.length).toBe(0); + expect(bar.action_icons.length).toBe(0); // Ship with no equipment (only endturn action) battleview.player = ship.getPlayer(); bar.setShip(ship); - expect(bar.actions.length).toBe(1); - expect(bar.actions[0].action.code).toEqual("endturn"); + expect(bar.action_icons.length).toBe(1); + expect(bar.action_icons[0].action.code).toEqual("endturn"); // Add an engine, with move action ship.addSlot(SlotType.Engine).attach((new Equipments.ConventionalEngine()).generate()); bar.setShip(ship); - expect(bar.actions.length).toBe(2); - expect(bar.actions[0].action.code).toEqual("move"); + expect(bar.action_icons.length).toBe(2); + expect(bar.action_icons[0].action.code).toEqual("move"); // Add a weapon, with fire action ship.addSlot(SlotType.Weapon).attach((new Equipments.GatlingGun()).generate()); bar.setShip(ship); - expect(bar.actions.length).toBe(3); - expect(bar.actions[1].action.code).toEqual("fire-gatlinggun"); + expect(bar.action_icons.length).toBe(3); + expect(bar.action_icons[1].action.code).toEqual("fire-gatlinggun"); }); inbattleview_it("mark actions that would become unavailable after use", (battleview: BattleView) => { @@ -51,54 +51,95 @@ module TS.SpaceTac.UI.Specs { ship.setValue("power", 9); bar.setShip(ship); - expect(bar.actions.length).toBe(4); + expect(bar.action_icons.length).toBe(4); var checkFading = (fading: number[], available: number[]) => { fading.forEach((index: number) => { - var icon = bar.actions[index]; + var icon = bar.action_icons[index]; expect(icon.fading || !icon.active).toBe(true); }); available.forEach((index: number) => { - var icon = bar.actions[index]; + var icon = bar.action_icons[index]; expect(icon.fading).toBe(false); }); }; // Weapon 1 leaves all choices open - bar.actions[1].processClick(); + bar.action_icons[1].processClick(); checkFading([], [0, 1, 2, 3]); bar.actionEnded(); // Weapon 2 can't be fired twice - bar.actions[2].processClick(); + bar.action_icons[2].processClick(); checkFading([2], [0, 1, 3]); bar.actionEnded(); // Not enough AP for both weapons ship.setValue("power", 7); - bar.actions[2].processClick(); + bar.action_icons[2].processClick(); checkFading([1, 2], [0, 3]); bar.actionEnded(); // Not enough AP to move ship.setValue("power", 3); - bar.actions[1].processClick(); + bar.action_icons[1].processClick(); checkFading([0, 1, 2], [3]); bar.actionEnded(); // Dynamic AP usage for move actions ship.setValue("power", 6); - bar.actions[0].processClick(); + bar.action_icons[0].processClick(); checkFading([], [0, 1, 2, 3]); - bar.actions[0].processHover(Target.newFromLocation(2, 8)); + bar.action_icons[0].processHover(Target.newFromLocation(2, 8)); checkFading([2], [0, 1, 3]); - bar.actions[0].processHover(Target.newFromLocation(3, 8)); + bar.action_icons[0].processHover(Target.newFromLocation(3, 8)); checkFading([1, 2], [0, 3]); - bar.actions[0].processHover(Target.newFromLocation(4, 8)); + bar.action_icons[0].processHover(Target.newFromLocation(4, 8)); checkFading([0, 1, 2], [3]); - bar.actions[0].processHover(Target.newFromLocation(5, 8)); + bar.action_icons[0].processHover(Target.newFromLocation(5, 8)); checkFading([0, 1, 2], [3]); bar.actionEnded(); }); + + inbattleview_it("updates power points display", battleview => { + let bar = battleview.action_bar; + + function check(available = 0, using = 0, used = 0) { + expect(bar.power.children.length).toBe(available + using + used); + bar.power.children.forEach((child, idx) => { + let img = child; + if (idx < available) { + expect(img.name).toEqual("battle-power-available"); + } else if (idx < available + using) { + expect(img.name).toEqual("battle-power-using"); + } else { + expect(img.name).toEqual("battle-power-used"); + } + }); + } + + // not owned ship + let ship = new Ship(); + TestTools.setShipAP(ship, 8); + bar.setShip(ship); + check(); + + // owned ship + ship.fleet = battleview.player.fleet; + bar.setShip(ship); + check(8); + + // used points + ship.setValue("power", 6); + check(6, 0, 2); + + // using points + bar.updatePower(5); + check(1, 5, 2); + + // decrease + ship.setAttribute("power_capacity", 3); + check(3); + }); }); } diff --git a/src/ui/battle/ActionBar.ts b/src/ui/battle/ActionBar.ts index e90ef80..fb4c710 100644 --- a/src/ui/battle/ActionBar.ts +++ b/src/ui/battle/ActionBar.ts @@ -5,12 +5,11 @@ module TS.SpaceTac.UI { battleview: BattleView; // List of action icons - group: Phaser.Group; - actions: ActionIcon[]; + actions: Phaser.Group; + action_icons: ActionIcon[]; - // Progress bar displaying action points - actionpoints: ValueBar; - actionpointstemp: ValueBar; + // Power bar + power: Phaser.Group; // Tooltip to display hovered action info tooltip: ActionTooltip; @@ -20,6 +19,8 @@ module TS.SpaceTac.UI { // Current ship, whose actions are displayed ship: Ship; + ship_power_capacity: number; + ship_power_value: number; // Interactivity interactive = false; @@ -29,7 +30,7 @@ module TS.SpaceTac.UI { super(battleview.game); this.battleview = battleview; - this.actions = []; + this.action_icons = []; this.ship = null; battleview.ui.add(this); @@ -37,17 +38,13 @@ module TS.SpaceTac.UI { // Background this.addChild(new Phaser.Image(this.game, 0, 0, "battle-actionbar", 0)); - // Action points progress bar - this.actionpoints = new ValueBar(this.game, 190, 108, "battle-actionpointsempty"); - this.actionpoints.setBarImage("battle-actionpointspart"); - this.addChild(this.actionpoints); - this.actionpointstemp = new ValueBar(this.game, 190, 108, "battle-actionpointsnone"); - this.actionpointstemp.setBarImage("battle-actionpointsfull"); - this.addChild(this.actionpointstemp); + // Power bar + this.power = this.game.add.group(); + this.addChild(this.power); // Group for actions - this.group = new Phaser.Group(this.game); - this.addChild(this.group); + this.actions = new Phaser.Group(this.game); + this.addChild(this.actions); // Waiting icon this.icon_waiting = new Phaser.Image(this.game, this.width / 2, 50, "battle-waiting", 0); @@ -73,6 +70,23 @@ module TS.SpaceTac.UI { battleview.inputs.bind(Phaser.Keyboard.EIGHT, "Action 8", () => this.keyActionPressed(7)); battleview.inputs.bind(Phaser.Keyboard.NINE, "Action 9", () => this.keyActionPressed(8)); battleview.inputs.bind(Phaser.Keyboard.ZERO, "Action 10", () => this.keyActionPressed(9)); + + // Log processing + battleview.log_processor.register(event => { + if (event instanceof ShipChangeEvent) { + this.setShip(event.new_ship); + } else if (event instanceof ValueChangeEvent) { + if (event.ship == this.ship) { + if (event.value.name == SHIP_ATTRIBUTES.power_capacity.name) { + this.ship_power_capacity = event.value.get(); + this.updatePower(); + } else if (event.value.name == SHIP_VALUES.power.name) { + this.ship_power_value = event.value.get(); + this.updatePower(); + } + } + } + }); } /** @@ -90,60 +104,86 @@ module TS.SpaceTac.UI { keyActionPressed(position: number) { if (this.interactive) { if (position < 0) { - this.actions[this.actions.length - 1].processClick(); - } else if (position < this.actions.length) { - this.actions[position].processClick(); + this.action_icons[this.action_icons.length - 1].processClick(); + } else if (position < this.action_icons.length) { + this.action_icons[position].processClick(); } } } // Clear the action icons clearAll(): void { - this.actions.forEach((action: ActionIcon) => { + this.action_icons.forEach((action: ActionIcon) => { action.destroy(); }); - this.actions = []; + this.action_icons = []; this.tooltip.setAction(null); } // Add an action icon addAction(ship: Ship, action: BaseAction): ActionIcon { - var icon = new ActionIcon(this, 192 + this.actions.length * 88, 8, ship, action); - this.actions.push(icon); + var icon = new ActionIcon(this, 192 + this.action_icons.length * 88, 8, ship, action); + this.action_icons.push(icon); this.tooltip.bringToTop(); return icon; } - // Update the action points indicator - updateActionPoints(): void { - if (this.ship) { - this.actionpoints.setValue(this.ship.values.power.get(), this.ship.attributes.power_capacity.get()); - this.actionpointstemp.setValue(this.ship.values.power.get(), this.ship.attributes.power_capacity.get()); - this.actionpoints.visible = true; - this.actionpointstemp.visible = true; - } else { - this.actionpoints.visible = false; - this.actionpointstemp.visible = false; + /** + * Update the power indicator + */ + updatePower(selected_action = 0): void { + let current_power = this.power.children.length; + let power_capacity = this.ship_power_capacity; + + if (current_power > power_capacity) { + range(current_power - power_capacity).forEach(i => this.power.removeChildAt(current_power - 1 - i)); + //this.power.removeChildren(ship_power, current_power); // TODO bugged in phaser 2.6 + } else if (power_capacity > current_power) { + range(power_capacity - current_power).forEach(i => this.game.add.image(190 + (current_power + i) * 56, 104, "battle-power-used", 0, this.power)); } + + let power_value = this.ship_power_value; + let remaining_power = power_value - selected_action; + this.power.children.forEach((obj, idx) => { + let img = obj; + let key: string; + if (idx < remaining_power) { + key = "battle-power-available"; + } else if (idx < power_value) { + key = "battle-power-using"; + } else { + key = "battle-power-used" + } + img.name = key; + img.loadTexture(key); + }); } - // Update fading flags - // ap_usage is the consumption of currently selected action - updateFadings(ap_usage: number): void { - var remaining_ap = this.ship.values.power.get() - ap_usage; + /** + * Set current action power usage. + * + * When an action is selected, this will fade the icons not available after the action would be done. + * This will also highlight power usage in the power bar. + * + * *power_usage* is the consumption of currently selected action. + */ + updateSelectedActionPower(power_usage: number): void { + var remaining_ap = this.ship.values.power.get() - power_usage; if (remaining_ap < 0) { remaining_ap = 0; } - this.actions.forEach((icon: ActionIcon) => { + this.action_icons.forEach((icon: ActionIcon) => { icon.updateFadingStatus(remaining_ap); }); - this.actionpointstemp.setValue(remaining_ap, this.ship.attributes.power_capacity.get()); + this.updatePower(power_usage); } - // Set action icons from selected ship + /** + * Set the bar to display a given ship + */ setShip(ship: Ship): void { this.clearAll(); @@ -154,13 +194,18 @@ module TS.SpaceTac.UI { }); this.ship = ship; + this.ship_power_capacity = ship.getAttribute("power_capacity"); + this.ship_power_value = ship.getValue("power"); this.game.tweens.create(this).to({ "alpha": 1 }, 400).start(); } else { this.ship = null; + this.ship_power_capacity = 0; + this.ship_power_value = 0; this.game.tweens.create(this).to({ "alpha": 0.5 }, 400).start(); } - this.updateActionPoints(); + this.updatePower(); + this.setInteractive(this.ship != null); } // Called by an action icon when the action is selected @@ -169,8 +214,9 @@ module TS.SpaceTac.UI { // Called by an action icon when the action has been applied actionEnded(): void { - this.updateActionPoints(); - this.actions.forEach((action: ActionIcon) => { + // TODO Lock interactivity until animation is ended + this.updatePower(); + this.action_icons.forEach((action: ActionIcon) => { action.resetState(); }); this.battleview.exitTargettingMode(); diff --git a/src/ui/battle/ActionIcon.ts b/src/ui/battle/ActionIcon.ts index 76585c6..2b88a2f 100644 --- a/src/ui/battle/ActionIcon.ts +++ b/src/ui/battle/ActionIcon.ts @@ -43,7 +43,7 @@ module TS.SpaceTac.UI { this.ship = ship; this.action = action; - bar.group.addChild(this); + bar.actions.addChild(this); // Active layer this.active = false; @@ -104,7 +104,7 @@ module TS.SpaceTac.UI { this.battleview.arena.range_hint.setPrimary(this.ship, this.action); // Update fading statuses - this.bar.updateFadings(this.action.getActionPointsUsage(this.battleview.battle, this.ship, null)); + this.bar.updateSelectedActionPower(this.action.getActionPointsUsage(this.battleview.battle, this.ship, null)); // Set the selected state this.setSelected(true); @@ -129,7 +129,7 @@ module TS.SpaceTac.UI { processHover(target: Target): void { target = this.action.checkTarget(this.battleview.battle, this.ship, target); this.targetting.setTarget(target, false, this.action.getBlastRadius(this.ship)); - this.bar.updateFadings(this.action.getActionPointsUsage(this.battleview.battle, this.ship, target)); + this.bar.updateSelectedActionPower(this.action.getActionPointsUsage(this.battleview.battle, this.ship, target)); } // Called when a target is selected diff --git a/src/ui/battle/ActionTooltip.ts b/src/ui/battle/ActionTooltip.ts index 8b3d149..c5712c0 100644 --- a/src/ui/battle/ActionTooltip.ts +++ b/src/ui/battle/ActionTooltip.ts @@ -59,7 +59,7 @@ module TS.SpaceTac.UI { this.cost.setText(cost); this.description.setText(action.action.equipment ? action.action.equipment.getActionDescription() : ""); - let position = this.bar.actions.indexOf(action); + let position = this.bar.action_icons.indexOf(action); if (action.action instanceof EndTurnAction) { this.shortcut.setText("[ space ]"); } else if (position == 9) { diff --git a/src/ui/battle/BattleView.ts b/src/ui/battle/BattleView.ts index 35e1ede..bcc32a0 100644 --- a/src/ui/battle/BattleView.ts +++ b/src/ui/battle/BattleView.ts @@ -52,6 +52,8 @@ module TS.SpaceTac.UI { this.background = null; this.battle.timer = this.timer; + + this.log_processor = new LogProcessor(this); } // Create view graphics @@ -79,9 +81,6 @@ module TS.SpaceTac.UI { this.ship_tooltip = new ShipTooltip(this); this.add.existing(this.ship_tooltip); - // Start processing the battle log - this.log_processor = new LogProcessor(this); - // "Battle" animation this.displayFightMessage(); @@ -92,6 +91,9 @@ module TS.SpaceTac.UI { this.inputs.bindCheat(Phaser.Keyboard.W, "Win current battle", () => { this.battle.endBattle(this.player.fleet); }); + + // Inject initial events in battle to populate the LogProcessor + this.battle.injectInitialEvents(); } // Leaving the view, we unbind the battle @@ -180,7 +182,6 @@ module TS.SpaceTac.UI { // Disable interaction when it is the AI turn, or when the current ship can't play setInteractionEnabled(enabled: boolean): void { this.exitTargettingMode(); - this.action_bar.setInteractive(enabled); this.interacting = enabled; } diff --git a/src/ui/battle/LogProcessor.ts b/src/ui/battle/LogProcessor.ts index 8e02025..2f1c282 100644 --- a/src/ui/battle/LogProcessor.ts +++ b/src/ui/battle/LogProcessor.ts @@ -20,13 +20,25 @@ module TS.SpaceTac.UI { // Processing queue, when delay is active private queue: BaseLogEvent[] = []; + // Forward events to other subscribers + private forwarding: LogSubscriber[] = []; + constructor(view: BattleView) { this.view = view; this.battle = view.battle; this.log = view.battle.log; this.subscription = this.log.subscribe(event => this.processBattleEvent(event)); - this.battle.injectInitialEvents(); + } + + /** + * Register a sub-subscriber. + * + * The difference with registering directly to the BattleLog is that events may be delayed + * for animations. + */ + register(callback: LogSubscriber) { + this.forwarding.push(callback); } /** @@ -61,6 +73,8 @@ module TS.SpaceTac.UI { console.log("Battle event", event); + this.forwarding.forEach(subscriber => subscriber(event)); + if (event instanceof ShipChangeEvent) { this.processShipChangeEvent(event); } else if (event instanceof MoveEvent) { @@ -98,7 +112,6 @@ module TS.SpaceTac.UI { private processShipChangeEvent(event: ShipChangeEvent): void { this.view.arena.setShipPlaying(event.target.ship); this.view.ship_list.setPlaying(event.target.ship); - this.view.action_bar.setShip(event.target.ship); if (this.battle.canPlay(this.view.player)) { // Player turn @@ -137,7 +150,9 @@ module TS.SpaceTac.UI { // Ship value changed private processValueChangedEvent(event: ValueChangeEvent): void { var sprite = this.view.arena.findShipSprite(event.ship); - sprite.displayValueChanged(event); + if (sprite) { + sprite.displayValueChanged(event); + } var item = this.view.ship_list.findItem(event.ship); if (item) {