From c1be08256bded55be035d3178db49dc59ec55a52 Mon Sep 17 00:00:00 2001 From: KalebHawkins Date: Sat, 22 Jun 2024 23:44:04 -0500 Subject: [PATCH] initial commit --- assets/EmpireStateNF.ttf | Bin 0 -> 77608 bytes assets/background.png | Bin 0 -> 2757 bytes assets/embed.go | 13 +++++ docs/GameDesignDoc.md | 54 ++++++++++++++++++ docs/GameObjects.md | 47 ++++++++++++++++ docs/GameStates.md | 31 ++++++++++ go.mod | 17 ++++++ go.sum | 24 ++++++++ main.go | 14 +++++ pong/pong.go | 118 +++++++++++++++++++++++++++++++++++++++ pong/states.go | 11 ++++ 11 files changed, 329 insertions(+) create mode 100644 assets/EmpireStateNF.ttf create mode 100644 assets/background.png create mode 100644 assets/embed.go create mode 100644 docs/GameDesignDoc.md create mode 100644 docs/GameObjects.md create mode 100644 docs/GameStates.md create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 pong/pong.go create mode 100644 pong/states.go diff --git a/assets/EmpireStateNF.ttf b/assets/EmpireStateNF.ttf new file mode 100644 index 0000000000000000000000000000000000000000..20369a9bd8275a700b1b317343f3d53c0a8375ac GIT binary patch literal 77608 zcmeFa349#IwJ+S&ty!ee%xHR6X|&9y8STrAMkCphtktq4%ljr9BN?!<9lRhLhb7n! z5+-(l1QJY0atSvJmP1H<@4MmJ76eGZz|8_#AckxtH{k_my;-=|iD&%%PghUR$Toza zyx;fx-iPcnb-KE{y8G0rQ>V^3RV6S%5DM^!a&_ zZhTx2*avVtcICDWJFi}|?w5j~y9}`D%00V%?0=-?f?)RGeCej0S8vmgd@>;jdAOhL zx_ZN|oq}7)MS1^5Pr3S<8#aBj<}<5teT%^IiZ^fEaMk}_cjgqX&ByuX&B(}&=(gav z7ssK^+jd`XUUdF1IL0%j-S62rzMTy+ykB7foxf(sl^ebtEQkusy#m)fx^2VtJ0-92 zOTahdyl?x4Z5umJe&-0TZxRIift@>c?S854*v|x3^C+(Q@15ftcea?H*(3=0Ex7*2 z0?~pA6OkQNzZtc*{aGlK@LWN7a_^VEqNEdVEI)tv{BGT0=`@aW1(9;`hx??{=XVP} z-C>-6zwWS75v1FQ5cL7z8MPiw1gzgigU1kfqD;4)gGCx8nqTiy(ef@*(lz_V*&| zbI7>`@Lqw5zeWCwfJqLlALpOvd8}MbH{jYIBYzs-=LbkQ{uv+%+=BFBq@>1$Y{`326%O81M+L#dT~7xEXn4NMlI%1E|(fwD@bJJpepQ z{375K05vMjK?=?iw*oid!tbCQyt9)2NiFx7!07q^C!WV%!ZYYOYD(WPT`8Cu-UnRD z;TZCQz)>gC5xg7Goalu5Q-Ej%x{K7xjtTR$>%NUV)B_`$9RjQaj%R_l=(;uHN09yj z`BXOUXa5^O?}Ba+EA>uwe<7g$i3ifaR-b=B;eplXU!+vr4?x{t z2ul4^-DksA90RESFM5vpT;jX*v(hP^dH5glOrqs$C?E7w=m|OqM8iR($l@S9$lIe( zj2wouuL*kgA2{a|UKL&uejxl1xgyTxKmzCmgJ6Vo$rDUMzF-zCf>kIGY>*mu!67&W z7i6kOC=`kWFUl(sN+DBxf?o&-K_MiR3t=H5R0x$ql~66z2(?0;P%ktHjgTWTp-G4f z%|b$G5n6>g!d#(EXcsz!dBT0d$AwP_pM)Ig61s&Rp;zb=<_rD80$~7>W=L2l3=4~d z#ljL{sjy5~F02q%3afi zDtt|NT6ju0B|IZMEBuG>obYwwo5DAQ7lh|QsqYAv2{#Jcgzdt(ut~UCI4E2%>=y12 z-Y4ur*%QKT_yxb=Z>R7fCFMWY3HyP(Bz#}EMYu*Fs;^Z}>=E8C+$nra_$T23$jJ@D zdxWcn&B9f}M&SzKO5qT$`JV9a_`O{i6Tmn4TQBStMuc_3sPHM_bHe??gTiNoDdDK_ zdEtogumE`@d_nkM!oLas3hZ)Wi*N(oB-|^YW#I3yki(up+KGQUqyap0BT9S#cl`^# z_<7+q;djD$ww!HX*R#FsZt)rM74e7CqdteP$XDtM_`<#h-yGi;{DFXc{ya+Xp&qXQ zEgk@6zlf(k3rhWO;rDDA8$$^# z8s*~e{5(NOJu3*w$C7uP`|hhNc7D$PAc#Bh=TH8LODODIqGA=hlRUPn;$~m?gh>6M!mgX_z23o2Q~Lkp!cVgFa0#? zdQ!L*T=WHn>R$pk9YF2>t3vxP3y%mN0B@WCe@%n89s_s1B)kmTJO_^YI=JbZsN3&@ z+AkX~Z)UxaT7$02bqM@iZ%XYog-ci-8eEb2j%2YIk*Lv z9~wakTh$VfDp~bOT%@ZR@*R`HOV%u1b8LUF_gH7|h}ZA;^&NX+$(mzN^m_dxBe+Vh zy(!-K<}Dueoeg+reHG3dc`3`+9P9KR6GjdlqB57S3Hp!iKXk}@2;ZR|9~YjuAcF}P zW^}3j0*kX0gjSi+VA0`}*nl7bTMz6qU=5A-Si*0J*@6~kW&T7k5pxEeF+(iT!4k1}Fb+uIoFV9r zB`6!<3@U(p10Wtt(7B)#_dCl~U|D#Z=$)C$|Xl!g;mc{L9OhDzLUc_5aFA{W~5$G0s zgO{H*X$jJj`r|?X zcp30A;9lTWz$vYJ;UpBTd?8?yTj`DyP_PWBnB_RviE9=hU4nE8?wbdE1+KqByM7&V zw&Ltsq}L#AN4gv7ZuNNzBY7ya z8imql)gsj)tyf-K5ccD>PlA^?#;;k9Yv`V}%B!ZYA?=K=ZfJBWT%j^mys0_SA<==K z7|t1UxSS5X!6F%OKp6}S*ui2^*Ib5+g3e&TB9<_}g#`oQpr7uDcd&LQ;auDwZBF>( zO&ubx#R;cBpf~uP4vT0=bE0@la_?XM@|WM`7)*6-1L41jx4im@lZpD=P}987tFOMA zYRfM!u$EcN+DbqE$fT!XaZ4Hdl%ui8;jpDW8_?2jfh zV@#Y}yttxkWo@&=#3nAg-0|M48&h{(Hd!@#fa z1;3V|<*$Xdx?9nY9)L#u2sGzspxrz#d>6XS4}||h?Y{_CP-MurPzc&t*AbEG~=%ITMK zQh&WTFKz}I@(k1ZJVTDvT#d(W(CPKM=4ZQ}9l!Lc)HULs@ES~mHLF=Dt&?heZ3DO;9>ODBN!FXNNcO`=V>%OgWB0P1LM7dA6S{v zcov~oXaNR^4^eoHCWkjuquZR-3NI~(p^2h^Y&m=qdV?TrR$8G)!@L?+u3;4#)}&#u ze5v<#YFM9!Ezq!K8n!~i)~i@ra`XN@;`F0Aq4D^g{!$k6J0t!;IO31{OLcCrlRqA# zD@b;07tLkPYDOYC(!qd7fX1?!Ui@uR=CCpO8QJ5DRt0LxJQlGl^{O-OVfk|Ed}L8E z>rI|9$pvyzsHiFyC@K&e`&K9aqR-PgmL-CXA#XlAlX@_)vGtdqThz8C$XvfKTT;^_ zS1t^GELE0yf+C|y7x?Y7b;Es~{*sQK!HTJT^VH!$@kB5h@%zI?ftI%Z#%JVbtiFP_ z4P~yTx!vK>x~8#2gIrqP(`anD>qNW{9?c% z=?gNj0tIsbvnZGum{Y0l0&q*U#w`)zmdM-YmT6di^h%ML{+nto=8UKykt*kmC_Qb2 z<5_24q`#fRN92j&=FsryCzNB2QtYaQNB;QJiG}jQ(T9GaeL0MJ`0h!3H?XRk$P_NSV?r4>jS1c!M_Ly99g>r8#O!bdYdNbfA2qgB~f-fsTm|CZt3M z%FocjMs%?K9dyuZbTH4L1K0Iz*>uqO19Z^v@$tz?@I3wA4l+zBbWjnZgVGLIKls58 zeR{??hdI;Q=m@>_NY-2TQbsS=MFvn;3lyTQ1V&>Q8skXn7$-flA+4lW7*XV*q+xmu z%hj-a4YO)kk%sv-ETmx(4Xe|zMh%N^-d+U+Z)p=L5{?v(!LF_B>TDF_>x0xxm zMENhN?>x+x(y_9;r^~t4|>~2+_c^*7tg8T?V z;#I=}5JRs!7dAOG7a9Y%5{tBE@l1#^LY!wR!6_9Ar^JCZDOfFdrbfXUfi(a-E`&h! za)th_z*-dgGmsrp5j3S%uZ%H(#o|;Q>HO?&Y6d>{!JBV>aId`gmYZ+BMZWyzn=jv+ zdJlU()p#*5|K7cHu}qil#Vvd3@>KmLNMfAPmt^-Ow_Vti@cx5(60U)mfVq@UwLnh1 zRakSuFRdFls@!yo@! z{_c05{L-(~Lu6-qS=W&x_kfiyXHktH7wQ(-^!VIG^e|x5=768bq0mL*bfL@wWELQ^ zK#=}V(#MQ0FVnxK>676IMHkWNlgU@KjmMyTOk8`R>Je6HA_ivCgl)JakL;UQHc9{+$^<{G$P>2WBYfMaB%PRnPy0i4XIBt4qrYzb$H+8 z?UUPX8NKB*Y%KKv2aP4dVl??J(U<(AOyij>8t`$-k2M;NUmJ~a*3o&iok!a^!_sADz4@<@5hfzgPi%a8gCL5PH4Zw;;6)*r80c-)> z05}M^4{#K40`MH*Wx(rzvjF9Da)A^ADgmv40l)}g3*ZL8LBM^0qkt2D=KwDQUI&~7 z6wv470x1Mk0$KqBfDynJzzu+dfcpSP0Ve>@0bT~Y4mb-a81K@-^EwP)BdosbyX+`& z7$pv)#9@>;j1q@Y;xI}aMv22HaahQu3-zdA7a$C12FwSn0&D{80UQ9}LLJ;}%i-2s zPQ?=^?7&?+aMuppwF7tUz+F3V*ACpZ19$BJC+q+x>;Nb104MAKC+q+x>;Nb1NOQt0 z3*nqF%R{*Ktp3>`+=Pa!(YYD#8i_M zG&%Jm41mc<&lHV)w=N7mvqOa6vEO1+szcxE9Mt#2BEJ{qw&BsA$QKvcIoS(YB5J_&HoozGa z=9)w%>2y-+VWYvymM4Fad59)`D(2rh9O>`v36}Tt^jAbT*E`A`VrlpL2EyI#9f7vn zdds`!&GV=9{zPJKxx1^U-6l#d^#HJRo_uU`q)Z$u=*#5BAx26VtAuUx?R(5@I5*Mmxd z@vbKyRZXL)Qly1Q9Y_;e)yJsnW3#V%WnAR9QGahkj|T9U$B_VFxz6%_u<2Bv3ZpmP z$b#GD?dOqhOMULf8(-Oxx*>C3qkH)1t5d1#kKW5ZFp>J}BrL|2qp9o1vZTLCE6K0X zs#54w{Yh7{vg5*4(2o-JCx~XydPs${BIhblq5^mY&QvRBqBx4uwK&rSysMr}`W1pl z>FQRf*m1#w^Q&=wHLfLmqejmSM9&THfSxB&U4pPdd5~MfiZm>uVKo{S*RU21>(sCT z4O^&TWXs~uSgm3+o3MmNi~srneqhcbtCd9m{c*PwEs@UAnYele-ie^%S%3vBrnnqX z>lr;y@}K(&2dCL-4&HfrS-HQwlpRktn;m9b%Ys$)_VV(OoO+MUrc3%FSKJ$OdoAKW zrRqusYnNQNqQzNnYg;rHZ@<#Vev!Phpmq5*eJz*!QVrs0GLk7<;}dbUzt_L!+5(+q zu^$Cmv$(z5D2b9GcS@cpDHL;7t_&})n)HXooVumE29`#bwU*6^jkRxE-cVOJI!9Be zKpCa&Ns`7-LHID&EAc5?g!Aw;q26tPt*HC7{_O@okdEE0a8e9MWI|}eQCjo5nt0>t zcf=cH@w{5$4brVRR;Xb?4GU>lrG`Z{jQEA$+o@q>`{a2`G;Fzst{NAO5SXr6jdge8lQ0tqw$@p(0D6xi}mThvpU-Ek-FvN?ylKx*KANvJ>ZOSNKv< zJNsGSJ#)_-3w6zHE=)CwqxnUlXfV=JSz=E1X3E#tCVaGP%4nO44ED?`DedZCRBNvq z>YZCuG*s!Vbl?#a?!tU=GU$WkX-JIe9Ff3ixMyz6<%-W+P?;r#6rK@(rtwT6M(=T` zBJGF_`}b)K+|m~DuqvCUp@SMAlSqaKfN@)>6ByY?;W-6&ksXXAI1M+$NWDl)kOq;G ztC!zC`z+h~ zOS8Q7nnVFBP{Y~4j|RZMDh_n|*q02|p7Sj?W`8a}r-lVR4cP@8HX z?Z-C4G)L4XA2d04tI@8B((S<8ffKb?X;fZDR9^PBshpuLm+#AzHcV2TVJ%q0=PVQW}F_HQO^LCGgQ?=sK zl=<_SqO_`G_juz)tz&(WqYjyEp7P0k6RAf76_X>2Ab{n9GHcVy_P|(cocTt#oXf6D zWgI72w7LvJfG&k~a160$n-Q(JlWm=bDJ*T{sZra?G>lpX*fgjmEyd`qAJ|E>DYuXh zf-Bm41JtAjXzvZs-W#C3H$Z!DfcD-1?Y#lodjquh259dM(B2!My*EI6Z-DmR0PVdY zt-a5(5U#z?vJhzR#FA9$`a=p~9B@RRyZ%tcZi zYJe`$pvv*InlH680|hPMWO^{*^y>{y2!E9)ksm2TTuru&kSyo^z@b!UEpjElSWqbE zmKt3~ac2r@xhPg8pM-Xv!`Rv67mEXmdOycmXqMz$n;t6vkBdr;R)<+^N|_3Dx&rnY zqe*h)==G@^bOzC>XWRA3Co<2-aOn+mmi8}D%+%q?wc1V z+I-_+-*8+nw`^&KvfsA1J2WSe@QpS$jW#tFMvLsWvVwv#n_OJXZqeA7_X7`WJe+N{ zggu7(foyxs-|g9Qsa7{CN>Yl-mNKLjL^LVGu@W_JrW`2`+95S`PQMh(WeT3HyNM4c zmvcxx{@6EC9~W;*-pj_n@lhtH{_~n|BEX|K`B3H>jizCC+mwAGb)%S@%4G*e5%8h; z1rg_cez&C8=n_ENUZS+^t-`g4L*A#vEPqh=!8C?A>3%T?jLYnp*21Av!Ky_vI|in= zgZb@{(~ZEXcl1JM@4oshjiX9jHxaSV7(Hx{kbMPHCnjTR2phERQ4ZB{$ zDBhXQUO;~yD^Z7-9lz6o_zbCpMKBgd1Hu0w#quEE<))a7gc4SPCgn%#!j(?Cgxa9e z8sQtkjqz|Z#?c1561Snq|6s(8QwZ`%WY?^uKlc|7$$ZJ;_4xHVF`OUtc+EMolxuLB zB)wQ`G$vCzQ;nk#nK}6`xy52+KQ>reY_5FW6{6Xgy4;vAVtkv!K9G|ux+IZ_sgLAH z4LSDT;DkfW&H0|iDN5pTK&mwJ$PA<8wXR&eG+x%_ThQMB9k+m@jb5j)NrYOu!QSea|2COIySWy_TqA*}ZVZe&QfE9%SE1EW?&9V?~ zN}FXN=uxQmn2W|U2k8)^)hK?m7#w?CC`Qh9t%+=9-ep_#GSjAvbiP7RPSL+H=>kD*t%phYQ z&>2M+%Mnu_1yjG6DL%u~N5OKowxt8ZEq(7_xafoP#j?hM`RG4#><(F)?7nqD|DMj2 zeyp)6Qa*3)+(7$HJ;Aw&)-o1qYHVx*JKL;*0@-LL&V5DWT!nA5;wiFy9^60p_2WVV zBw%_Z3Qy3iJs-pn9xw!Xh>DQXz@QkZjI;zPS&@85-ADsS>AFUZO=^ivYTp5ykc7{ylTo|@a)zV!CJj6|_gl@df5q7+evC`F?+ zQrgJcM3njm*x$Gp^6&FTz7-9#Jd>9sW0r&R_ZJh=Ky+lUK(-b@=`Mxp9mXuGI?NY> zZbClm8sdnWERIms9wlx&1Wz>ud`~03OjN_h;4Oo|pgaRG3LW7AAGwfD`50N(DAI|6 zm=@&7YR*Z-x$tu*@g8Z+qhUoFR-$6Zg$T?v6^bXk23WP?#YERz9xsP_ga{ae8$((t zu5;?*k)RGC-Q`MHH)IlWc(5XSD7BS+G_{(&Qt;Yq1%cEKcGv#|S#IhhuxqYmucX2m z$diA!|2MMy;0JCyBD3$_`RmQUx%2K%Zv6hLmo|QRZ0y{*v9XXWOCOhIOs!RDdQj`H zE^ha?@))X&oi_yjBEGA9Ax-zU>Fp~4-Fdf{j<+MmmsI0KC9qnh1~ReGe$Wb$;um>* zDg2FWzZoka;Y6%-K0BCtuL>Vx>*UmPECF}0J@qedazYcYY?;b>D!HFLMM}95CiNuw8 z@{!v<@SrUJX8-SG)_M1xzuEljJM%WawCejCKgpI|z>kWo6jy8f$fHw+Foxc$%t1u7 z8^!jK=ahR``J91j=u8xlf{=M|CTZv`NQqOck*-Brf%GzsV|S9I*qJ3M-Z}~)d+dW2 zBkWWt>({V44I9$1g&KC1ip|u_VSu2xdkZUJK}9nO#_ef0J&k-3yJ-=fu{H+PMfp$m zPguznn+sXUd1!tRN;|iVW~(LQ>f})lwN_vK{ARP?Uu+P?Bl$&DCHtqy4fvlSR zAZ2`z$0>an66Zq-t9%mJClu_XXk{N!u#W+|SHX5d_HP1qT-XFQsRz5%V}M7o$5hkD zg%3gw-L6RQ2Y`J_kw^@KB*cvc19c)*Uo2WG8sJN$S|N8N)d?)rFjQdX1g2fUHtoFN za+)fElM!5zF2kL`V2ljcLt;YJaaLyt14F& z^@bx=gIBgBt{AKeb&WJNE^G5w^sZ})U)EdU_w{;K7!0{`UY?w5c&wn6`z=}fG9xl>EvM*~Muj~{L#61@ zI;1pbtRCrNq+}OZfz*t26;dnG%aD>j_78|gpD8)0q4QWda)`OyHVFe0um=V#N-S** z=MfI2U4-d8aF5ef!GM9fO)#UI2KVGvo7s&@o^!wCa4Pk!BUfKtH!y#3(~3_I$?hum zmL1g#7xYK2zIuYKnjB9Z7`^)HzB`60`uloAW3^)qW9!+CsgEsPGqOT19}Y)`!z`No zVx~MzUPPXG=9#Hmo1GzT0H8sSpvsrzM zWQt`Bm`r*31*g+BjjAI%&rMqW7DIRFL=6vNti1|z?D)Ka?P9|;G7O5XcAEUJs^^+w zbNFbR<^k3zJV4$NvhMPMYQYQ2_&b2^CV=sG0ORif#@_*qzXKS52QdB)VEi4x_&b2{ zcL3w>0LI?|jK2dIe+MxB4y4E5vn+&cBNfWTO(WaG+dNM1R1L_ZyxwcJQ%@w;4AT<<$Az<9`v{#_)0wRm3YwCd*Cba zz*pjdufzjii3h$C4}2w_w6A2Ag>YZVEDJ%)B2Ok6E+R;2cur2!a-`+;id(1_pIVDg zt;MI-;!|t!skQjjT6}6PKD8E~T8mGu#i!QdQ)}_5wfNLpe5x7`rw8H!gaOTf`G8e` zO@KXs1AxPT2LX=&P61v5yasp!pv1%Jfw%x+Kr>)IU=?5!U=QE`;4t7pz$1WDfR_NT z0p0*09*+FET_~grg><2iE)>#*Lb^~$7YgY@AzkUnwk|}rbs@5i6KJ_6Di)KdSo{t| z#RbiiygxA6k+w`WY#RNkS=4Bita9=uk^cxDA`0|#e3u)e|ON|;hPM0baaRN_jbyN+iDBByZU+x#}*7PTresIw6>+_EaKBz+w#C( zQHKW8k)FLSVxBZ2Kj6}!*3=J-kArAL#M>HqpLxF)MO&*>u1f$5@m80M-i-cL0$KqB zfDynJzzu+dfcpSP0Ve>@0bT~Y4mb-?Lo*A3&Y}?BpUtum9-0|NXl67Wn)$aa)QGsG zsL*b_eK&~E4I*@d2;Cq;H;B*;B6NcY-5^3Yh|mopbb|=pAVN2Y&Mb@Pa*{_%epqHp9GEX-Fc?!Wd?RyvrfG={ zp`M=k6_xY5yUSVORhQfRw&b6>-}eRf%f$QIgB@*ieQE$?>iaU&b#--j$;GYI41aoY z%%fz|+L;%M;ao(u(CjJ1a!?zhIUU(nS&HE1qk;-xJo>RpYiq4&VKulTtvMnF5*+}I z6Ua_RmhBKy>L6%5;73}8lsbqSqzy=0kv1Wv>j$*j?4#Q3n|*C^WyJ{@sQja8qx_$u z%V0pDs5#pC>>;IOI`?M|$(wiY7I&mt*!xnG>{&UDB)^R8q2x38jc9qbncFh8_tex= zQ^ivyNABsG?3{dR@+tYL(Wl2Yj&95rfjlNoOlfs~(%PZ^8R%sz`LROX{M#aaoI4`Q2? z`Y4}qF5O8{DyEVmhg3D&nEJ%ju3h4CIr;5fyC&%nmf;>sM^OoR$t9VqHR7{l#Zyl| zjS4vY)a27oPj<>1N1uKg~Y`_%N$BU-qg|6U*uHc2P;DxT>g|6U*uHc2P;Dy5Bg~H*5!r_I&;f2EC zg~H*5!r@J8d9y5pYk9LQ1fn+WhN8iKh=zxHICouuFrXPQAFv9r39tun0B{)aAm9T}$VVy{?`YD7Ff+kWTok_R$HY0abpVLJ0GE9Uofmru2{&S@^2 zO5M#ao9yv-1^k^ph^_u`XIpy!-cw`!bup}yGp2;vZyrs(lBLHfV0B&_k&td1i3He=#0V!59ZG{Tsn0gah^B{thdKa=v6(N1cy~s2a zqYG!0@s5rBfR!jZbYaXUBvg9tCSo&fk`l9dDN__`{et?GBIjP_@XgfAN5{vHvIu5* zr@nma`1q}C2xg|cHjIyNU^~W=fy|{^eMO#zPy^6>EhG?aPs@POQ>RX)Tw*XwxG8nQ zGJb_@FDzzxUTT#9n(@GO=|c1&tX%YpW%Q%g0N>VzqH|r-NnAN}=iY-R? z%vQiwr;c)XdLN6(spP)NYxn;obtaRaArU(`m3&?-n@qh|W(UVGp)H#%>imx>jaN(1 zS$1OnG{v@J9feF(3iYTwnw9!n-t&?!#JRQ&6e3G1XOu;n6d5E92H{cKN?Pwsicnyn z64Bk9Xl{qQ#BU%8;xOoypl?{qBk2n|L^>bK?mXF4Y7K{y%D$2MTldI*bIHAIo}Bu{ zy*^*v#%pVqcShu3`?9*_%ZuuR&X4_f>b}f98X;L({YYQc5eovn4}wJx*ZKSt;m+kX z`E^U%gQJdMo%iV29F60+3@~eSrAW|Sh(EhY*vDR(hVwdYlf?Q{>Nss7utKE=T?CBx zuVh8(Qu11XaUGM43?vOGr6x(UOlaPX6F5l-(rKuf)+234+QcoHtth+|k7`x^4d8bK zumx}f;2_{Wz)`>nz;l3?0j~qj0@RV;EDPa-o>>-xu4)lJYXG0uk90B8E~Kkqo7_pm zQpKW41bDy3hBp%%-u$-Na2l0$;iO~E*)BY!6}G~gm4~dtLl!BIx25@k0Y*|+b=&ccSOvRtEdq_ufogUn=qO;Nl{ViVrjK<%E1efuURs(b4T zZDq{^RrieU+c$bo)j)IE=={!k{mF(*iCPPYm|UjPmg*wd@4xo7g5nB~tKJ>xpQvxS zZE3!2gq5VHJ?^Nuq1&#@)zuC*m3-}MFzNf67gWkrE)4r$`x-rJXri)eVkk=tahu|p z)>g7*Gh`5XX}EPGBc(I4*$c@-vm0hDn`tc<68CQ=k;`o{guzGrHq1ug_zb;dEct-Q z;rJB$u$5NJyaKh@W}Kn44`;4a&s@-CY!sFUcX*wMPR|x4uc@0>FM>`1 zUsWCb22l@WahIIMOz*(hiI%iu3F;;T2EB4OoME_IyEnU@q}aJ{a(KF=G3YpI4>guf zVd2B2A8Ky6bznlicJQ`Z|G?6t-qe)AqIIU_U?0~^n(_H$Km&;n}eFj7+H^PD6(ng(dPg`}&YhcMgvJZ%b4o5|G zt;c8&wKsl0Jmj{y#MJfZqVa&~>E#{mh+lbqsJnY;W{=I=%6^SE{1~GR!{W0N()`!l z{+{t>`IMf6?C-o^p)nc{ff_=qS*S2O>5kN!P-;a=y-5%_StFWMJOMnR;&Xrx;d~oX z8bPnqIOwgr_G!4A(#lU6FnW(nQ{yZ{v4Hd*WHBjHpGW#Ey-y9&8syCZUVEVe+N3N3 zS0@qEP_=U&3=s)q2uX z#{)G*dBg$-L-STgZ2_CD!Y8lm-oCV!YJ9Ng&Ka63w+a7Jt8ZF!faa2qVYauTPtckJ z+fc8owYtuXD7e>U5OqznkH{1H?wSK{GSieOy{+nriQ9?k5QV3<&2$N$!v1AlYD0mXEV z-c4~?xo6nRf0^`%9JasB1qWa|x4zYC1Ljp(x^_If^C zcPdW(M1xI-_c8b@JJ_J+IGM8E#^x?xMP zqa*x9`$MwdlKcg~n@Z5wMqFbpv7#@m>~9X}C5#5XDt{H4zQppc9`@9`Ln+;}qhD=^ zuIUOtc}2Wd4iqDXz-r3G5GZSNi@#QQp#sC(RS;!2V^4{@uo%jH;(4y&@|vcZGPF`7 zt^a9+#3F1I*nm>|6p}(U9YF09PF8%y<(>;+Ne)W-M`PXrzzARq;0C}!zxgaOTf`G8e`O@KXs1AxPT2LX=&P61v5yasp!phghPvXF5K zEpjT;ew4L!)}o(Vo9^et-5?xjr+pS@HRg6oKPiXD_UTXLE{*88@ z={ESXnqjaZEw4Ej0);}1?p0bpjd(b=O~ZC;*j^RWBmztWep<)?Aq|LNK$HVtum-<; z!Ayx3w87jw*vZLuPWiaFgHbGC+O)3SqVziI`ZqWkj7MDw7!iOup)Cigd!q*!Xd;wy znH$sXqplgc@voB(EbN{9i-=vW#MBxxzK6=hR$s{L$uZ9B=&16SB!2?uZU777SlCgW zaZ=P9JjM62JVR<#Q3;Iixn8V6qstx2HF}g~aabs@ymrx@4U%rop=H4)-z&6C&i1@q zr^u|SFIufVfu6}!u1F77KoLMXqfXZ4>&kn3`YRVs^v(NF|GXPpEdeVn@_lUZrXKi& zH(XKPjWu%Qu%oDM&=Cn3{vVp}kqaNyr_hUU;KNc1`!Fv>MsE*K?os7E%cy~xv z(b$XkUY)?gN*>8HZry7F#--Z=O{UR$4257IvgnZ|&kNbs1e~mS14xI#RLgOH7)-_2 zt6z_6` zr#LrwEmf5V z7O{iWCzh36&?jTB6g17LfNWIOsUU{{}#?vnTCUmK2i=rpDf7 zwd{YTR>=Vi`)l&YwuFz}ml~~I-c@6OD3on&fz(p=?@gEWMUtcJ-soa$@)zOmHBoj; zYH$6JRVW_EYX!BVjLr2c}yq4C742j3ue&oRC11}fWLBhcxi3z z(uKp7N5aFA@NhZ2S*up9S~1c6@uB+R{sq-?_eTcW?j9apzhT3=vE)8}8{Mu^_iDM= zZZbKFZR`)J_vh!COl+T=y0xf?-IS{FhY;sw%lG+8g6xshA)k-!Y0dPY%38Z(lS=nS z^qo6l)xKVMAA5p(VQEzrK0}^IlhE1>G}fEOsEv;fPhy30t7_S%u^!FxqBUv>uL52L zSyr!J0p&Rtx@#ez63_}50E_^(0B!&r1l$KW3OE6H4)8MIb--DGIu@8^A$%+_%R*q^ zh~gvT_{cauxF2{g@Lu4QzXJFQl@NP1R=kl|@y5T&ii=gDJq@`aspG5kL`yH<&ahqpV_3LvVsdqF{PPnQUdl_8{cZiX~AX%PAIQf)Cr=TwgVw27nd;pV7*o(q=GoQ}P=kfa!ya`)a2gUa#Kd$din@MltsYwt1=TSrTN1CN|+N3*_aM zmKI{=h>6<1rNO}BK5Q*wubwwpRyNpKJz5lwSj-jWMWZP@mB%5?<*vC4AouH2FXk2( zLv5AwBvXDK%gHses@5H?jSqqnRqUsr4|Y0bMM^~>6& z{d1hoIeklN)$@Hne*eWahxq1F@rOfnMts}iA+#F3V(W0dr!K>S_ zddXj>agdfqG*5{VNcF?Q}eFN?}dbCw*zdP9z~3fH`5 zF`By3P*#&D{mJ+8-KNF`CUyXwRZeMrd+_w>P@l(HD6)geKlIM+Z;^+(yB1=%jXm%X zJ$id-tggn@&JNA+7Nm87f7AFUfdK*S()mHmgM4`!q2zof+cdPjbZ@o{7?122N1MJ@ zsn~H;MY`I^`?3YqvjuW>JFaY0s%F26@wsmkI6r|ZrqH;%G#bfhdx@$dhX}rI(a(=vY z%n_zFNk*NKIiZSc^(%Cx@3H7%IBh4)~ zf5C!@=DBx|$SV)U;_n~w_0J9GoBZqIgSW&RZeOCut$u%EK}4&Y+6I2$`&jhHK z2WjsIwO+A#m6a9_4nm9=XsN0++u+2Eu4ilVu&6-l(_g!7XaiRCoie&B{kxtzu_w^z zE10}u?EUw#w$#ZTH`NX{muzrXwg#`?k^C9Ihwjzttl}q;Lo4iIhi^`_!u*8%R9;0> zhkw&l)r(?&O8Ne#iZi_eUyYE93(VJ41qZF= z&hzXVM(d68JQ^o*jMgvY811^mv0@D?)3AVsm8;lHc~FiKAH%>9YmiaG*OpaQo%P4F zr_4)+b7wiE4zil$2)iov8}W|ht*k2bN%5N0W0=ys?$`)hoBY<=V{21S9Lij;)#&p_ z*as+j@%jl2Ij(r>%F!#nc4byf7}grW-aX$z-H{^e7BJ4BD0UvVk=et?j22^5q%c`- zGJDT3st5Ka^zqa!w)Z7)Yev2b-VNh`P}t#NV;k)dD+;wxL;0wmS%0L@q-&Qe#~A!3SYno(YV|g8FJHa^VotLk5chNit6-CTytaYm2_4lp;Rmk%@b1e6+zxN`4ugY8S_kx#b z-<5ZLng6f&GSzy3FBA2E1*udS0N)Lcu?voR9=3QKH*59uuFdpao2iHuUdc?9wv@bU zGkr^&DSHXyK{%d-1>*2D!ZZxZDq<99Vo)rl)0i1+ROTcr!Llx#aAB10LaTEL^8u>> zn*e(N2LOiw4+0(moC3TAcn$CdK-KqLKxa`1*Y{>w2w!fi2JcaW&#A%Z)ZlY!@HsX3 zoEm&i4L+wP9qd|zVAq=Tq>dik)q}fwa90oR>cL$-xT^zwW9}_TJdG^&_io zA=~I@Z<@d0eV@H-?frXtd-vSGw*SV8xiX2igl z5k_D}42+p#`Dl6!VqnaOfiWWn#*7#kGh$%Oh=DO92F8pS7;`!XW|oEU7?@cW!ly@t z5jGx9hmD8vZDD*{7~dAgw}tU-VSHN{-xkKVg{gs1AW$1FY{P|ZxUdZuw&B7yT-b&S z+i+nUh|mTiw1Eh1AVM36&;}y3fe3ABBFwT7PJ~$&qO}P-F}OXe3AiZ3m6f4-9Iyob z2^ji`=^6RFBgF0$?hR*oM43)YK{p5t$6^YU7l(;g3B!q~IrWOwm%TK&|MMG|Yx#kB z3l{a?_}R-DIDhp=wzh?Oulv+m;@GBjH}w+>k4IPU?q(g^mo+pj+t$I1o#QJT*@Dr} z?;o^@`gm*i)D`O=ylH`qd6xMr_TIm8?fuvFj>a#$v48$em&M1Z{q*iy*(k{k%eQyT z+qSfx$xR~^Ew8M&)`kZ z;7!lqP0!#>&)`kZ;7!lqP0!#>&)`kZ;7!k<+dPAA^9;JpGwE(~mWA+cbC!i5R`3kC z?x#v1z|TRrpM!8`K)Bxn|2?WL7uALd1`HDn6ddc8veNpKjP@AcPgTfPrpXK02@WU( zR03K71Ar007QhXFgMj-0M*$}Q&jDTrybd@E;6ao?3IUaXR=@yY1h55g1K=RwKEP4H z3BYrJmjSN>&I0(J-arZgm4H^j0AK{L1#ko4AmBd0QNRhnbAXotuLI5k3dWDKEAi>q z;&ZQ6x%WEY*8%?o@(%z%pe)-8c6R~7fM&paz$(Bdz#hN>z+u3HfJXqQ051Vv1H1v? zdL$4RAPi^*%m=IjYy#{78~_{!JP3FMa0>7e;5EP-06ysohzk$~Gy~=XRsl8v_5cn5 z4g($pJOVfccnR1NFSL&bPA|IrU2KFASG>xoXYYk_@Qkq6g;v5LW1#bB}!@Cx7+Dn1wZTos>(2hg?s z$m>`0_5M6$LrvfT6h>&EHCAvD zg%B#*p3)0aoEWZA0#>yv-B@*kO2nh_09uEA=CQKqzp}`G7#%HzZsQpgt4OF4v5mbp z5xuCr5k)28jd6*x-7$Y7b?q2@2x1yB|D_l+zk&hyVrl0p++bl0WntbZawGV!#Iz~V zZ@7Y{j`C-!vuE&K9Z+Xg0^nUJpO=*;C<>(|H~7lBL~M>J1i&j1^_B3+OJ>k{9&r~Z z+GjluB>s03hN3A>OEDZR=b!d z$pxha1*KI*Zl6Ik>7~4Q&{Ae%R);$etIaUI&S2C*ueLiFlkNHWPOGcXlvn7o*ev-D zyC_P^d67B79-Gx_^Ar|Xtp$&G3h)OUfBP&|Hd~b?P^mZRP4z|YDzj)1^(?>IUD%MT z%hNj|548-6CL_x!E_9UY@+3XWaTfb+WvzK)ZZ6B4<16qNJ8~G_Ojl$tDiJYngo#}Z zr7bpdCEHsxP*EApuc)f3$ZxEU3>1Z1d_`tUdvpAQOO|{v9`CT4i~KEj6xR41UdfQ7 z%dy&WB%3SXkyvqSV{CHa@U4x}R;w9n`}kcpDaR)o@+_8sqg8LQ%XXdKY|OJ5 zb8?K9Jfm5!vpMV*LqV>^lZ(sDMt89(&uqaAfn2L47i;({c%H?aXF6og%QF`-R)~dA zbfV;N6c#dOW{LbnqtTk1pI0DsEnw!T$q3Y^TXsp@WQsnuSa_D{v*QWa$kkWBfJ#JEuo7LkMq*Ol5zo+XnJ4h!;ola!w*HOxdUZ`JV z!5V>L%xP;5zAh6DZT0E|rA`znpy((*k_mczBx&G!0G%-)m2jp&IYYX50!K2AXsjgS zY6{9KLO%2#1D)x^?D)z_L;pJ-htpdToEu2o{Z z#HUfkfi)@hSqr71M!_0^H2@=(fyP`kK57Nl!iTA}h7%2sKo0|!JfMXT5UGzStxW!m z=sr+Jiy-^lIISf)j$Od9jwBLVL~=Z}k3E{|y%^XkYzj)20bRW%?Q2v?=+C z^u(nZF=rXN(9T=wm7|BSmc{Z6T`pRQyN&45#_57Z@m1~~2DVV4Mjxort6&4b7ASOS z12tf`0$r8@TcXkh90F>nj5C8i>BS=GT!gsig6rObUPLbZsWc<1je~APHs$BdY3Bjq zj?fQLuWf>UX&y zj8@I$m`BAL8tpN65bBR1n4mpqDT>ccIfmfJ_TBfXIhGR3{ocKnfMwp^_rF*Fo~`S9 zy4OqJyuZOzWXjJkF*PB&FOh%!TMB~L%P=)!4d&d8W3S-bF-&|&cuaUw_?GYk z;g`a%h2IN*68=Xx#{_m_8k3%l5K1JZ9s_nWu#?!P>Ip?gGaNsrygn?H!et638OTz;6S78*qy1 zJ_-CJ#_3Fb`2}luyPHf8F;*un1<1Mlss=x!-h2MD;oBMiX9hTN4q(rw4fBQ zq|$=su^>0*CSp+a6ETCjwgRoTfVJ$U1nnikZDSFC0y`tqEH&C7nWhWH4COctR>O*o zk)YlH`l+;-t>%t6lmI@0#o3jC#RPhqV1O*dMmTXOiWqaEXeh5UG_mR z`{2#m_P$LI=UVf0I+HE`KTTGB@Xk$a)Z%s8Q&;6%b-{d{wZNj23)lgxJtx*6;U5-B2WPCoCG&>-VZo-s%v62BKIY;V~q?~>!C-v8h^VknB&ikbqG|V$#i}XB0 zj@6ueA3vVvhpV3Lde(&(+@RBAlj!O+wk(aL-XjIi{pez7JnN2xLJmtsb&btgksw?>}6#E_gPCY9fijcLTU;vp=9ZkjD1#!%S+pczqXCQ!zh|@X zEHw4nC3+j)*W3=d#N(T@<4mtUl86}G2}6YN860JZU5K15mNg*pkT8`Ammvpa%1#lKywJO@tsc%NMbUDHG*opUZPm26I4N!MEG_P>r&Z3Z`>atE)i(jJ|12Khx#`gs zrED1Xmn**={5P3CNdMC<>!a3QhBLe3!IkzZ?+60qO^%}IJQ zT47a_V$guETS8GFoI)9@WoSkG!e@sTgGMxjpB1g9VL{Ld%*=-c>|AVzGq^lg;2Wt}|rz z^ezK`^9QW6WhT;Ukfa;I$f)f5)i`t5dqZDC=J z&yJ0)wQa>SjJzVNkitR~S`rPz4oGG2vS8Ll5hw~pph#G#Gn?-4x_Vxd3EA@}S7&x! zPg`65?)j54XvsyzrMqRK4c6UJ_EMigr{`0RjKcu?XE`;&Zcg39MpExlGjTFQ0oRcu z_b9tPN3|BLe3$t6CBJLO+xnU!w6pZg$7~B=W)7rIvFPQ%W_JSC0(;GXb?Vfqs&h`AT0G=^ z@E7Z=6)Lq3(XIVH*bVLNwO<6Iz~)sDu^Ih?@8U&=(;)aE1VEi-hM(%lv`7dp92PK( zv_v`@0QZ%b8!tEN3oaL2)?c<>wram`iMrJiskc~0qb-K$*v-+{8qMkEKgqfPs{yPE z___e_5;{`X1&h-M_D^5AvUBP{*QHCf2c~K+V8l?Ls>p4gJ}^1`?u%WM2X?;no!SGF zwdX}IRVg;MSQlslAZwFl>cR^v(i!cGKk4m5+&elSGb~sUa6%$)XyM?7CTb;IX>=fQ zFrB0Ul{oX7lB15S@<8f6&m@C}+6*oqNyhq$b1+)1@gq{KfJj6Hi7{w1olT3)7#HBz zl?Mnj;cU@P_gZY-wcy_O`3vmd0(O-j8}ByL&uJE;l@unD3a8?QtovcJ^1R9OpxEu% zt@gtJ8b>uL3_rP$DzbBtHTRP@cEwtT&OI(->!eb7tXvSBjErgRL?;XIJiR;XSubQqi6WC3VTt5ZXBl$nc{^?5c{Zd@>aIM324X?Hn)pnxVPE^~8 zYLhcaC#vm4wVkN86V-O2+D=s4iE2AhZ6~VjM75ntwXZW1UhV751l2A>wdzs5W?VaP zZNzm8u7r+%_ZjBFpnKhtF&rd=jA#O;!=OrT8Xw7R6>s4(6K_;xW2Q zVyi#yIh(^YvC=BuPLTpLE-ofx^DVGa{ys!>;H*%$@P}NBLh~+7%Hscc?X}l#i%$#+ zj|#o*HBHKcr#xSUE>qli>cIyeOz=D!pz{wDH=LTk(~>Af-8p~iZ1>%fu`yA1cc1b+ z76gY=VxuR7a?zwP4rVfyuD+=&?a<1GJTnQeBv`+vUzi` zzDvGD=^T^=s8qP0Y;wO5CqzRK3w<3>8v_)_K=zyD6HA&m@(bcr;~J5w5d#$Q#qBEJ z+d^z^%m2lSg8Km3A`86*fkR2)+#Dj$j~sH!&^2>COMnfg2Rgp;XXn9@+a3qxvyk;i zGQmH!EE*pqf=G(tiP89uB1m(UWs3M|&nOShOpU)L*%`Igb<*=xbhdRz-5(il8V_B$Um1g*`Mhr<$JeTsw^XD2} zb~CIP!ptFwQ`?XI^jPDu97lMtaeZOWaL-SBG8)Qr4Fx?t<0cayyEV6I6`5WfG+sL} z&mMrsRCqxrn?)L>ptVIC>`~ibklJAIue5=8&U_K$jvx#&%tN~&L*zSy$C1w!4v|+C z4%t-5u0qKwgneytx*`>#eJ%XmQWaXOLbWQ?rb1g(h1j6s45*_+G9@*(27#(?V!c>;MTSVt6F^v$L+A1`lxovSj z53fGtX$_CpZOYAS4hf7kh%b27ghvL2J92ZXbg^OLNzc>%;ytN4#~;K$ZQA)~e?QH; z3BM4ZO8mSg&`(_Dxjih#uOJa;4kknfz`*AK#$YvPl|267cQ&OZy>U9Z)^3s-?3-}( zQR=)VLQ|pNr13vC9ugUn7Z4hd50_2dsp~WI0q5E4lls)Lo6n(dQ0XKF6Ak%nHgR?V zsz);Hq#jSwPwxDM&g0}~C6R2DOF!u(Xd2(iaSHTKj3A;BQ^=q=6-rc}W~xxG3YDnP z8WmbAzsq$?{{g4IacL=?@RI%Q}n&1#9`Fn*I(#5_qs2`Ml79rhC9M~o-&6yQoMl|qwj zAi4po4Hk~Z9eCtG+^!eu*S+veTk98gZ@1HA|Kn+BY_rnhxYdjfcJ zdQBxb@sY>RoO%4V?rziGy|>+W=FEM!-JfU8J97pJ(iEzv+uJO%9EJtj)oMIhdG6|*Bn0AU=03&ztalntu z@J|5$M23G2_}6360=5{{E@Rqy5k#n$PSYAz)0A8CeaUV?um!1 zH|BPn!LWE_*%i@2VmAD+i6e|ZVXyDN@WO5#=)pSpAD7RXWH0bc{GqeOCs!3Ce+3ee zC%ple<NDV@OuY_L{=dK;)FWYSl8sevB|>5`~rlxKv_l;zQQ z#sBWPIg-Ksa7*!*vgU)N_$gCS{9~V)GFX$$X+i$mi(d^&bvW%}c2LTy>g2S#qJ+=K z=dJd|7 z+HTd{VUNaT@sBYMOU+NT=A4wHSHEer-fWB(N7%)4K_43&Y%y3ObYYex>tn{y4pV6O z*&1WEMJx+Sk2zeC+^ZQaHO7T3ZVJ{^g=&Adv*eNB;DrSWmc-!TM9ad}b5G?+7DWBa zN69f66&Y32!G?!bQw78WR2q>vCiKXFrWe~WlN#1!y^Av)3YzypAOP?&t?>S_6P z&2MW0<71LibF5N%Ombwd=W>|UWq7Hmb8R{L&oA&v$O5_(eCj0)F}5; zV&B|9g+=ZxerV2awI^F`{MWNxiY+f&Bi>~!+!Ft7EL5yUBmed63XKa1v_@c+7vj|E z4dKDfM!m@xYV5Z}C59TG8?%)~JeKAUNlVmaE;F?^NQ%^Jpja^fQF_^;VaA3kZX%#A&1Uz-=(l_M4FBjdvz*Tm&Y zSfUBrwB=&I_BpmL4#9NIP1}FT9U0Bw$mbsy)9RV_lc@$@hd95@8Y;Xw|`X@ipiF|^(e!+P3`Nii*;v0lQIIMTjN4DtLy|NGzT zdwLG_^k|>EQ-1(p4{k!W<0d*m4f0A{g5KBZKM@Qqt6PYnIf9G^s5df;`VD^ z>o%x?wmI}F=%vH z!K?P=*sKQDZ6UUYdXiR{ukT3;sfHj&XLX9ba!b)inv)+DZK+I7souUy3|u^!+q$>B zd~a*6ay^!nC60K0XflZvi@(vFTpad>U{gqG+dye?Ut4LY$rM`J)>m9Q&{i5^603Z3 zKu#a*=<3?pSXx$A(x?gQ>A89pSl(|kT^+>Y5C1}#ZtH#EIUI}ebXZR+Coc09zG~O8 zY}H*w_^#skt_QyB7%glB)XtzLV3|a83$T>XvSUEW4EF*oCkU%s0BvSiO<|)VI5l(T z7G*J}8M(PXSUOi#r4?Fqa2`u?X*Ooc2_xD5hR7v)_a)-qu1_2(D=X^HE7(zDDs0co z-kwudw*4EOX(^hM8vDtYip$DU+p=;RGmDE#ONw6d9DxIQ!}9RazJ2?S)LN`oi|3~% zjGIvwbJKRj{W{QO3j76t;x?n%Z2aRdOs4F`FDjByUn5PXKh%&tAGe!Xs%up-{5NV! z3xHv=RQ2xIr=wlC9jkZ64X3HV@G6GkrIs^fFQZt`ko}BoL6ayhv!l`27hmJy6YXuk zz0%e;J$2yk_II_ly?CjueWz#PLhb(Pu7&fpQ`4R_Z(4N)(A?hsFYn?dW?9qT{^Cn* zZL)RE3N|)ehd{a~f6ye^q+8MRldzo3@Y=C1vn?_a4VpL&T7VaR5Q5jXh}`k=BPhML zMJC|I%xKVSTLkM4bZK;D5FFVTz%?s+tgHU5hWQL=v*j>%0~WkYs86OqHFg+~sfdf2r71dFkOIc?FS| zJnK}bNri5Zp`~ldB{iq%5j31^Rs@Y_3FCU0jX~q79uY@VO>7CWz!;G8Ld;b$V-w%< zSXT%|w(_=|oVIeCay3aflew@XE-gGZC33K!tPC4yViQxf2^5cmo27%hpxsd^%-hL4 z2PqqS`KJ7=4P`7lN}Zh&nGzR1IGEobn&gO#s7TH$&TcN1kXdSBEU8^cuWvvbn6U=+ zwJHDjKIyvyHw6^OHlJaHQu<^DD2`j1d;6q!$E~+dnrX_pL~o4ET4Y`XbZI|^_Woc@ zNRP6lCzwnL>6WPU1Y>%7d^~)@$EW{PGn)C0fLoZn#GbHZ#2fITjJ@J^z_9u zD-k8Pb9f&32xIu*wjhd4xr&oI>U4EJBX2%M)*SV8ADWqY_=xGq z+|0}&apvf|V&~DWnVGI5P#Qn)DO?`Jq=ttdZg?0knefWo(A?b6t6g2cJ#>iP^5m_A zF4X1Y{k=jHEC_5>8=ykuQThqVYuw> zmD`_a3K*TnGaDU;YU$rBJgUrymbHtomA^<|WNnZGD4Rh#)WDBHRe<;^o%9}|nE&|U z8iuQgE4l5ads0CrV8u&usetL4iP@6_ZCc0bS}a32k{PXv*7j-NiL;+=bpo@;9hG_LA{L!EnU$dbkATpo>`fF7e;3D`{Z5! zDjybCFuDLhGDLXk&=>x^~GCCarG|Zqu zP^^nVdjPos(WwjBJY$#5>LJ;nO{z(<0euO~HZfGepdLrG_!!T_ z$lliwUVe+rJfoJJldF&-%@pcGj3EI5db2^?W{$N+gcu{O2G2qWnY#xYg4P9m%|GC4 z8W^nm|HR)v2q8UC9|Y~5K1j^d>4W^uhSWe^pze*ZK;1l1-SQv`Fm>**O^oOG-*=)C190Yjkw1J|@&CMVRejNj8&hhV09u3~3oD`Uok> z9+g^SFNw&GjLM0~$c@U3^jKHqZU5=!tZdebEfLJN!&2f8<8RDj*{^Rac;}&iXltK7 zFg4xQb_FxnuKfpgwzpmK++H(1ReL_BW_o|^1v<=Lnc%9Vj7cjQeYCZ``(k@L8Gf|2 z$)+F64M5o1N#pfZ&Swzop%>eOlR;?D4(4Cv`e&J?#u!oe7^m*Npy?q-$-6+wTs*DE zLq1X@sZg#8tyiID6&jGC^THTte^ee%lN>l{fCV#@kMy6IC|kCobJH>$D=IT7FxQB* ztK1$Zq9?HbBwWKz3B1Ao%SV>5X-fcR$`(0e`haHZ;v-7P36t{bi$jx=Vgogrv-+6K z_@n2DWz2it2~UYMl-4He=iz5a4D{2PqcWT0V{GUvR=;5XKto`Ht1c$NpzRJ%uXEV* zjbeJ8H(#%j$>O=({;f3ztyUDZewoIv{VX1bRs^3D?gofaJBC@^lbi20(eP z(L^W26qi*bnG7)obE>JTBq$<2(p1!#8x@z(v)We=!TqXx?cY){CT@q7%5K)0`*Cp9 z=>-_bavRPCER_|z*foH-c~~VNE;|Vam!_Sq@x-lh&BHYf*EP5n<64R9E?o7vx^bm+ zhgGhAfC%J(&m3zdt6A^te(9{;XGM)&`=o}H|GZ}I*Q~Iimt4BGR^Gs9n`(m>0U8cS zbejgMh+J|AiS}n#3wT)km;25Q4rVs5Z_BH$6c2j7DQZ27Vv9)uJohA4NiC<>4GtcA zTzt{ve4}S@u>RA{3Dp*Ho0?{$f4HS<`x`HA+!kd@Z%Mt+Q|gVTxmgDUKDUI98bASfT5(DyBHsnF%+= zxz0>ribLB|;xNmjLt1!M?J~rPI+a@=IZ$N>s_a0O9jLMcRd%4t4piBJDmzeR2deBq zl^v+E166jQ$_`Z7p;Y-gGvQUf&P;GB^cuCQ7ge%!D(Q$aB zHQveQ<5+76{tT*Cp&I$A!ZWb=APWYX@vsdqTxx08fdwFVEt-@$6O129im;^>uaI`B z1nInjPDNyb!)hkp34H%*@HhDG7fmcqX$}+5y&fV4dfp2bWAZ#Zf`Z|~;tL)vZGs8H zIn7TL_7%SJ>w>bvU&O@6#Ebvz>5YyS&n$k6^5TIp0Fx;yr5Xn=H+-V%9Bp4Y8$}jP zc~W(f%_$wTRXS@@lB?I^^di~>T)wZm@@QF4!-nRD?j`4xa$cX!`aYf25erk7)mYVT zR8G33JzZqi&-*@EHE?T9vUDI0zkuaUAR3*ZIzZf?7jg4&>Ja2G!HST9uo|Hep%cN4 za1h~EgwqI*AY4Fr7U30yw-G)>;7*DFSP?Q1RwFbbbRxJB4kFx&a2nwegbN7IBD{j| zHo}Jp+({7tD?$dsYJ^6FP6Ri?L4;coP9r>mZ~@_2gjW#WM)(i`9(&l%Y^Nxo6-rVS1i)c=(sjRd=dF;um181M?ekyWp zO_jC$W=D5QQ)*L_tJ`I&I_6BMkg%Q96t_A)t~568T>SDqT;lIR+y6g&C7Ai7oJ7nz z2PWytoZaCQ?~7Ys_&1%Co!ch2b<+PGo!jXDw$2@$ozC-+Mxr2T{PQ zcyAm1M&p$o+weNxjls)!a~u77R`#L5=kaecUQhxiy$kc_X`EDen{bzKzwlsyZUJT~ zIviR^eSHSd1cRCY@i_v``naXj32=fw_H+-Rt$^4lu?M5X9*h!uz%ce;l-Pq&Vh>v< z?8o&40=z=f#>YJvCHA0K(ad72u!phkXyl#Apg0*~9q;paY&j2Rwlecmf}|2;=5H;RMU6K+V&kLZ#}{)iT89GL$@1N(Jmf`Pq8MQW`KF;?9K z>@@*N?<{&|L0 znrA*{rCAHv1eXvi^G`a$T?+(}yiM7~#g$Y6zZJq-+|{lQKlI7yi3h# zRE4Hh=%@oKlT>O;%ti282g8FB2r^;I zxmKhRRP*LyOuoLn5h9yFe=#C1SrTW$JW1ladQXt%%~E}BTbN&xJ;Vf=nvfVR9?b8~&yPva86pjyQtU?0f96H2 zS+9F`!5@+pU1nqIrReDVXOg}i6QS|PR|5k5K67ud|Kcl}H&kDftv^z^Ze8`Erj)A6$|O$!;*L~RkCx+lQ*~_R+BMPQ zEo|eunBjT5qSjt%Gijod(j#I^Vv5ICd;Z+8VZ%DlKbl1Us%nGXo)mfvn-?xyE5+By z!hz58!!Wj!eFOP8TMKPd19TDGzF~(~O1GVQ!gimYAS+au_b7NH04tygz!pH88Kl@X zEPy(-%<6$0nAHi}86DCoDjcenA+GDHXA2!xTLP)XV`SH!&c%EOltt#~gP_``oqatVVs%Yi5&{U{vt~9}kxz-ruTrF=#85!c`x?Sb2Hxo_W>_by4 zt?s>?l&T;IH_KH}pawOrMTL4)XtxRts}S)!p399YG$TU_ALBHmjf$Wp5!gn6jSN(2 zl|*zHCX!Q59Qn+2bF#-zSUpzCY>QLKee_nep;&u_(&6^4O<;EW|BhmLElxy$lRWa_JGpbOYsS=_*M9S z;j51M=!4FVR=3dMp^3N-9V=a?<0CEl5-x#zrFHH8uC6i2-&aCo^+b01q=k%xB|ozGc;g;Tbns~KXx&JcxMNLL z_Ua~6Xq`lq;~yJ&#yI~U34uE z;+9W_nnp*>A)k2Qfd@uXQ#~J;eq(`pvkAAK4T-bt+-Xb>`mE`J=xF=;^|om115YNz z#3Ve4r>UunJ64+K;Cy-Lm2Oh!IbL3ujAf9I8YBiGq40H0|Y5FAo3Vcsm?lQ3g0kfvJU29$3h51ZG@lZrP9o#v~~S zW{ORrAF|@{XTuIpNvSnAEzW9g`r+Bdi)S;)$mHkq^WvHL`P{|Xm&7mP=OvzC@riEH z+3k6;TXP>bX1?bv`*&)N{%O{k%`nWl`VO_ZX3N!LX*d(`u zhB5mh9epcdq}2ySWfW1yQB;Y3MCbJ^z;lfTf);{mfGlWL0IdU559lI1CzT1U=uy0B z*07$^3It)LK(;7FM($}Hu44_@(nA|mU<)tD+2uI+T_`fb46wu}6^+@-80|ZDC!bAe zOG$1^KG*ip2HOV0+p10Wru&+XWo*mH=**lqb?@x$y`iW2WMgJ`QFnvpM~h|4gQnl} zXox-}L~QX?rKReEYD|s#V7p!Xyyx3`LzrG{G75%c(@Q0vFI?oW!=#K+_(mSjZd;(SajF{my>K6No;E|)D#DfPJy zD_q(us%TLaC{>N4eWCnmwhVb|OYN`Jn8PriqnS3_5d{eY+1-jmp1W2EXMblbj-T3Q zUt@hs%nM1fr=^6dvtsWG&C`<~9hVD#b*7!G|Z*ONkdKaKU26dxv zcQL3R5Q+9L;>%lvy=>eRRYfDsvHH$$x3_4eSot7XF+DLN_~e$Mn$aY#IVsR=X73ZL)tc^9gx#1R5?RbIpaf>yD4Hf zAA5#Di`+i!AfN+`7U{5)F$PTmnv|(>w=g49r2-j{36Bd?p=cF~SD{oDN>ia#D&$b1 zVij7eLQWN`QK1bg)TTmPRcMsxi$V+D!RhQ@K^E46V@jJzR zK`F)SGBYZo#CA`6NV20Lw<*i>Ym?_Ny2fC*m3qWO+WxEeFAs8$aNgqlTy4KIQM|=8 z5MfL#>u`JyQM^BhLHf|>%u?ym_L$hn3Fsrq zkcIBT*mh8n!eImbr^O0|Ha|tA_$eRSypbYqpgI3RS95g9@!zp=K3oRiW)Fv{Qxp zRA`q9kqsa(^8pz;FWd+!J;dnsQ-D6n=rslU0A^RfM`cE{3RmSE84p(A$*HhFfI$tm zRbxjDEs3!4L5IO!9_UH(<^b*M610}TomGqdnVwrPf%}Rv6V|^$5or-B(>BC~`mpWJ zxTf@+Fwf!3#>%v&ec`JtSHvTkP`LYB8q&H;qqEYJjeioi1)8FhEHNpu5kcZ*U(Rm# z6Rtv8bZuaC+|U&lQJC0pPlLkVi*^;7#Ja~72LEXP>e$WB0i)g(8bN$sN~zMq+}>%; zNwhRsI0~RYkm2{FCXS}w2&d|C;`G>I((1YL>W~GCiZe1Iq;;XSk91- z7w{UkJp;>F=F6Y%AHY`MvNNY|@|&JLQVDPVN8)!@*X-Q0SNl^q^3TVvja4D>dVNXB z2#SQSPiX!qmS`84JTMVvLM1{C*%p;z4B#6wa3mzWSyCs0|0A}{U~0&<&}gm2dT}l0 zHfyn3!9arGMmUIYE5d1nM-VO`Jd5xO!rKTRBFOeb*O>`dLtbYl(8*rE@H5&_MY*trX&~C(pFKuQBRw8^~UVX zL@6svO3ajKtGU%=jMi@BFO+5^7w~tCc!Q@l>UrYKL}N5d5BibM`NkYr696Jo&iRI~ zF5JVjm3;2^Q|#V`7ua4EZ3y^2--{Xxid1%ZJ-vV|bigywqSy0rGUB-Z-*iCS3Xc3j zaVxmhkP~DxEOOg7uI?qvXOc;fpRZ(G<8UPlXEIYH{WDoaXXBcW>uOwA;Y#TnRT;*z z-AwF5Vc_MpREBAn35H$FI@!l4QEtm@UDSw{N#Lr$)@TNJcB|Ol2IzVs_{Ys7h4a+Bof7hwzqw&TA&%z&- z@-K<+67KVfr~D}&!t>`8JR?1ohsRF{cdK{~VBalZ>F*Z4q~`M@7Ej*HfQL2S@_6H^ zJilBaJ+=GoKJoNj%7+Zqjw|2Q>|yznZz;;>w3>boD<{QctlFyLO{-(+OIdoKa>{gA z$>P@vCzO1a#2*%OSBzgypFAHo=wT^+^7skiS4#PjKhYD6-j#&-#C`NR`GdHM6XVY zr`wKe%c+I#5o6-KLLVB~d*c3PAO*H?Dq``2~^cr}76`_=YJ*wP?LR(5on z?Krgz_gYAKNcu+MW8C_xqgSe?aPfKWRQu1K854Z^J}BO!YP|p3KmPUj#|L={&uNmi zzw~?0f5`v6fJxmaVOV~5P(skT;7t8pp_zt_VMF2RMuTb0bTy(p@}8*rsCO)PMQ2C9 zYJELch0KE^nFHC+eS^IZK^RXM?k?YNTp^ zbzyZ`b#--9O+d{Xbt82n^;hbztUJ-%wBhqDxh>DPMz;^NzrJb9rsq4FI$qy=WJ|}E z$G4Vl9ou?k>j&G8ZST3EWk=u6J9pmM+t7P&UrWEx|91ny1J4aS=jw1>8I%U!+jV%? z;oaMJUvXdB^W4ynM@mP2KK{LlW0U_nH8=I!>8R;H9eDiU_`#QN+;QWV51pTBI`XX} zo};<=D?YmRX#H%&tTdZFTRK}i+cvvncK7V$?9tgfX78E(!tB>)zcc&H?4{Y)XRplu zY0hshd@gP-eQwp9bFO)A+uXq1*xcc{TjuVX``p}@=g!YPIrsCqSLc2+_rbg{ub+>e zPoB@m(Qys)9rHc&L-PmbkImmXf8YGM`ESiXKK~5VQZLWHKL7f$vEv4`oQQyf2r!q{ z<2L}ZltyT2E1~akLG0WF$((=#q6=9%fRN+ycjz|^cl17aPtrkxz+o*+p5|oO4{5$2 z!~XEM^r#F60DfGC$yL)&WjF|&>J=Fd!36#d8P-D#z9_??ev4v^44Z_o01L`4`URmB zF9+b9B?gN^LV!nxHG)AGC&M@sP?ssgenNt7M27u^7~LH*902%X84eVpb&tz%kPxMN zS%yP|Lfvm=STB?X(prSd5E}Gvf!ovjGo-@&{QPYCEtXo`{d=XFsqsnoM6OgdG$d_w z@7g^%A#HR`xW@Op26ClZ_r&D5yKidJH6Trm47kRnkfJ?%m>fk4sc#gRBMeRMMwDxOc!FMFIag7# zVb{oHuGB$aQtzeyQ53>G3B*sZ_fY<^Ar~;oa;8_1_2jh6HNwi$i!b=%BR4cSG`O>M zv$UbAvy9-JO)ezqMZRMP>GQoK2ba9yt)$dDG>V!}yC-+^S`Cc$PtgZakWrSy@UUyV z-`zVTjg7lUP$9zEC@MED5!rwuq`s+fSML<1ca8M+4WZhox@*8aDPx$u$Y2?B$~RcK zb2fvbmSUb|HQWa(WX;J+=N^&TMkgjW^^d#9CZ$355Kx7ZwAI$jH-oqvPzV?_lr8Od zyQcBkY*c?hau4_JVr@K19~&Q##(L2cToWsmfwCF{C611;YLD)5^#dP>WDmyWd%Bt4 z?e1rYlX^k@W4&&CXawD5fPEJ|2|Z{M{btxreQI(PWf`BG9v$DCEe(zC8l5~i=9 zzvaFWiF(ISGS^VHgk-oIpJc_KcJ)cS#(T%`JziwFxuurY4&LM&MhD!32l0V{-m%GS zX>z=GWMT-tFomzk3{%ioQG24^sFmSRbG2>vN6gAocJVNPcX0FZvTO zKj3!d^rOf=QAnDDdiunm^aJi)AZKbGPLb61z}fhy8<;wX%1EKXEsZtn(|F-GNt+s~ znwzO{r0P1Uxv{FcxenFZC{?v|O0|s}>uNf(rN$Nos%dT6RM)<_uBD@~s#!v^#+I7K zTHNBUwrWGwy1GqL)yBq6jV*3BJKYrWLbP$zBPR7W67Usc`M+}P16b+oc*dc34S zDo(Fu)i7r1hwx5<$TXk*Wvg&8pcbse`=RfXFbYq>6T>9#Ct&X)LG(9-KM5AwZfHGr zPaxRpDqw) z{L(g|7MNrfPkAsSnr45wc!Rg!gfOVSp9^e`0z1Q~yBpY@#9bL$kTm^-;UT1T{>3+| zSqtzs8UY?9$jn(TAOJ%IWml^?4>UHeh&5UlyKMu`78jw!H7wAirGsSxI zr#dvU+H|mzci@>fwet4IV`w>@(#a|s60||K|14&{EB$*7kz^?n2n~5Kt2bo#|w6v0t1c{v;lD`xfC!}FEm;n>|EZ{f?Et-ef zasj?r1oJ?0j8r0&!kVKD8uxNop|6Fc&3EvX_0a}f3LR$EW@O|MM z!rwzHc3k)_cJTiOW)h!-rPlqh8u@~7o2Y>{tMx7jM98z$M`&^ul*xlJO=+L^npHVf0K|N9{xZ zB<@W;aX&^FvamV`x#$eEN56)C?}%_zm=&%F7lpTk&j}CW*pqi4H-vwqfZ^W$@zD_j zgdgtF0SNmb?{N+I)lH3$QcG*mrW&-P2>ll1jED(F%Vi59tT orL-cR5#5N05os$bU@dZrLhFlYuMzx`q*r8;UXlVQqeW}}57ZP@0RR91 literal 0 HcmV?d00001 diff --git a/assets/background.png b/assets/background.png new file mode 100644 index 0000000000000000000000000000000000000000..d9924d6bf300a0237b367258a9ef86852a5a8016 GIT binary patch literal 2757 zcmex=GWD^cd zWLGK_F>0K+kVDyN<3Z7&iyu^slZu)+xx~aJB&Af<)HO7j7{Xt-8JvMhf->+QXb5E{0CoSr%fP~* z@c$MA4>Kb$F)<4=*facEwEuSVUjr@%4Tc3w4J1$p058 z)PM1>#rm%bzsN{?pd|4B5@p&i|BYDx<-yMnq^Eqa?N=z%e)Vs``Y#QC=8zlq*C^C~ z{cp$mFAD$i$xV9~DAa%P?~3*BnSb0N*Z-F&)PMQ!iS_T8|NJMr@VP>%0ayP%SpTN^ z&wg@a;2MScum5Y%{&nC#gW@4lOMY+x%>4fXr3PI5*P;EZ;Xi{Yxh}XwsR5V&^=bc7 e_|L#ft_!YEYQWWh)3m=c|0^fg0oN$he-i);TkN&~ literal 0 HcmV?d00001 diff --git a/assets/embed.go b/assets/embed.go new file mode 100644 index 0000000..cdaf2cb --- /dev/null +++ b/assets/embed.go @@ -0,0 +1,13 @@ +package assets + +import ( + _ "embed" +) + +var ( + //go:embed EmpireStateNF.ttf + EmpireStateNF_ttf []byte + + //go:embed background.png + Background []byte +) diff --git a/docs/GameDesignDoc.md b/docs/GameDesignDoc.md new file mode 100644 index 0000000..991e0db --- /dev/null +++ b/docs/GameDesignDoc.md @@ -0,0 +1,54 @@ +# Pong: Game Design Document + +## Introduction + +This document outlines the design for a classic Pong game. Pong is a simple yet addictive two-dimensional table tennis video game. The objective is to defeat your opponent (or the AI) by deflecting a ball with a paddle and preventing it from reaching your goal. + +## Gameplay + +**Core Mechanic:** Players control paddles on opposite sides of a rectangular playing field. They use the paddles to deflect a moving ball and prevent it from entering their goal area. + +**Scoring:** A point is awarded to the player who fails to deflect the ball, allowing it to pass their paddle and reach the goal area. + +**Winning:** The first player to reach a predetermined score wins the game. + +## Game Features + +**Singleplayer:** Play against a computer-controlled opponent with adjustable difficulty levels. + +**Multiplayer:** Option for two players to compete locally on the same device. + +**Controls:** Players use keyboards or joysticks to move their paddles up and down. Simple one-button controls (up/down or left/right for paddles) are ideal. + +**Visuals:** Simple 2D graphics with a clean and minimalist aesthetic. The playing field, paddles, and ball will be represented by basic shapes. + +**Audio:** Basic sound effects for ball collisions with paddles and walls, and scoring events. Optional background music with low intensity to avoid distraction. + +**Difficulty:** Adjustable AI difficulty for the single-player mode, allowing players of various skill levels to enjoy the game. + +## Technical Specifications + +**Platform:** This game can be developed for various platforms, including PC, mobile devices (iOS and Android), and web browsers. The chosen platform will determine the specific programming languages and tools used. + +**Graphics:** 2D vector graphics are ideal for a clean and scalable visual style. +Physics: Simple collision detection between the ball, paddles, and walls will be implemented to govern ball movement. + +## Target Audience + +Pong is a casual game targeted towards a broad audience. Its simple mechanics and easy-to-understand gameplay make it enjoyable for players of all ages and gaming experience levels. + +## Monetization (Optional) + +This version of Pong can be completely free-to-play. + +For future versions, optional in-app purchases for cosmetic customization of paddles or visual themes can be considered. + +## Future Considerations + +**Power-Ups:** Introduce temporary power-ups that appear on the field during gameplay, granting players temporary advantages like paddle size increase, speed boost, or ball deflection manipulation. + +**Game Modes:** Include additional game modes like variations in field size, multiple balls, or wrapped edges where the ball can bounce off the sides and continue play. + +**Online Multiplayer:** Implement online multiplayer functionality for players to compete across a network. + +This Game Design Document provides a basic framework for the development of a Pong game. The features and technical specifications can be expanded upon to create a more engaging and feature-rich experience. \ No newline at end of file diff --git a/docs/GameObjects.md b/docs/GameObjects.md new file mode 100644 index 0000000..b26c264 --- /dev/null +++ b/docs/GameObjects.md @@ -0,0 +1,47 @@ +```mermaid +--- +title: Game Objects +--- +classDiagram + class Game { + + Cfg Cfg + + + state state + + playerOne paddle + + playerTwo paddle + + ball ball + + + background *ebiten.Image + + + Update() error + + Draw(*ebiten.Image) + + Layout(int, int) int int + + Run() error + + Reset() + + drawMainMenu(screen *ebiten.Image) + } + + class Cfg { + +screenWidth int + +screenHeight int + +WindowTitle string + faceSource *text.GoTextFaceSource + } + + class paddle { + + int x + + int y + + int dx + + *ebiten.Image + + int score + } + + class ball { + + int x + + int y + + int dx + + int dy + + *ebiten.Image + } + +``` \ No newline at end of file diff --git a/docs/GameStates.md b/docs/GameStates.md new file mode 100644 index 0000000..e8d3da1 --- /dev/null +++ b/docs/GameStates.md @@ -0,0 +1,31 @@ +```mermaid +--- +title: Game State Document +--- +stateDiagram-v2 + state "Main Menu" as main + state "Game Loop" as gameLoop + state "Paused" as pause + state "Game Over" as gameOver + state numPlayers <> + state restartQuit <> + + [*] --> main + + main --> numPlayers + numPlayers --> OnePlayer + numPlayers --> TwoPlayer + + OnePlayer --> gameLoop + TwoPlayer --> gameLoop + + gameLoop --> pause + pause --> gameLoop + gameLoop --> gameOver + gameOver --> restartQuit + + restartQuit --> Quit + restartQuit --> Restart + + Restart --> main + Quit --> [*] \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b4c1225 --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module gtihub.com/KalebHawkins/pong + +go 1.22.3 + +require github.com/hajimehoshi/ebiten/v2 v2.7.5 + +require ( + github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 // indirect + github.com/ebitengine/hideconsole v1.0.0 // indirect + github.com/ebitengine/purego v0.7.0 // indirect + github.com/go-text/typesetting v0.1.1-0.20240325125605-c7936fe59984 // indirect + github.com/jezek/xgb v1.1.1 // indirect + golang.org/x/image v0.16.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f294f1d --- /dev/null +++ b/go.sum @@ -0,0 +1,24 @@ +github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z0UovDfvpCn7wZ0GUZ+yosIteNDthn3FU= +github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M= +github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= +github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= +github.com/ebitengine/purego v0.7.0 h1:HPZpl61edMGCEW6XK2nsR6+7AnJ3unUxpTZBkkIXnMc= +github.com/ebitengine/purego v0.7.0/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= +github.com/go-text/typesetting v0.1.1-0.20240325125605-c7936fe59984 h1:NwCC36eQsDf1xVZG9jD7ngXNNjsvk8KXky15ogA1Vo0= +github.com/go-text/typesetting v0.1.1-0.20240325125605-c7936fe59984/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= +github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= +github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= +github.com/hajimehoshi/bitmapfont/v3 v3.0.0 h1:r2+6gYK38nfztS/et50gHAswb9hXgxXECYgE8Nczmi4= +github.com/hajimehoshi/bitmapfont/v3 v3.0.0/go.mod h1:+CxxG+uMmgU4mI2poq944i3uZ6UYFfAkj9V6WqmuvZA= +github.com/hajimehoshi/ebiten/v2 v2.7.5 h1:jN6FnhCd9NGYCsm5GtrweuikrlyVGCSUpH5YgL+7UKA= +github.com/hajimehoshi/ebiten/v2 v2.7.5/go.mod h1:H2pHVgq29rfm5yeQ7jzWOM3VHsjo7/AyucODNLOhsVY= +github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= +github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw= +golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/main.go b/main.go new file mode 100644 index 0000000..f936058 --- /dev/null +++ b/main.go @@ -0,0 +1,14 @@ +package main + +import "gtihub.com/KalebHawkins/pong/pong" + +func main() { + cfg := &pong.Cfg{ + ScreenWidth: 640, + ScreenHeight: 480, + WindowTitle: "Pong", + } + + g := pong.NewGame(cfg) + g.Run() +} diff --git a/pong/pong.go b/pong/pong.go new file mode 100644 index 0000000..78bebba --- /dev/null +++ b/pong/pong.go @@ -0,0 +1,118 @@ +package pong + +import ( + "bytes" + "fmt" + "image/color" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" + "github.com/hajimehoshi/ebiten/v2/text/v2" + "gtihub.com/KalebHawkins/pong/assets" +) + +const ( + fontSize = 48 + titleFontSize = fontSize * 1.5 +) + +// Game is a structure containing the game data and configuration. +type Game struct { + *Cfg + state +} + +// Cfg contains the Game's configuration data. +type Cfg struct { + // ScreenWidth represents the width of the window + ScreenWidth int + // ScreenHeight represents the height of the window + ScreenHeight int + // WindowTitle is the title displayed in the window's title bar + WindowTitle string + // faceSource is the font face used for the menu, scoreboard, etc + faceSource *text.GoTextFaceSource + // backgroundImage is the background image + backgroundImage *ebiten.Image +} + +// NewGame returns a Game instance to be ran. +func NewGame(cfg *Cfg) *Game { + fs, err := text.NewGoTextFaceSource(bytes.NewReader(assets.EmpireStateNF_ttf)) + if err != nil { + panic(fmt.Sprintf("failed to load font file: %v", err)) + } + + bgImg, _, err := ebitenutil.NewImageFromReader(bytes.NewReader(assets.Background)) + if err != nil { + panic(fmt.Sprintf("failed to background texture: %v", err)) + } + + cfg.faceSource = fs + cfg.backgroundImage = bgImg + + return &Game{ + Cfg: cfg, + state: mainMenu, + } +} + +// Update manages user input, handles physics processes and updates game states. +func (g *Game) Update() error { + switch g.state { + case mainMenu: + + } + + return nil +} + +// Draw draws the appropriate images to the screen based on game state. +func (g *Game) Draw(screen *ebiten.Image) { + switch g.state { + case mainMenu: + g.drawMainMenu(screen) + } +} + +// Layout returns the screen's logical width and height. +func (g *Game) Layout(w, h int) (int, int) { + return w, h +} + +// Run runs begins the game loop. +func (g *Game) Run() error { + ebiten.SetWindowSize(g.Cfg.ScreenWidth, g.Cfg.ScreenHeight) + ebiten.SetWindowTitle(g.Cfg.WindowTitle) + + return ebiten.RunGame(g) +} + +func (g *Game) drawMainMenu(screen *ebiten.Image) { + imgOpts := &ebiten.DrawImageOptions{} + imgOpts.GeoM.Scale(3, 2) + screen.DrawImage(g.backgroundImage, imgOpts) + + titleTextFace := &text.GoTextFace{ + Source: g.Cfg.faceSource, + Size: titleFontSize, + } + menuTextFace := &text.GoTextFace{ + Source: g.Cfg.faceSource, + Size: fontSize, + } + + opts := &text.DrawOptions{} + opts.GeoM.Translate(float64(g.ScreenWidth)/2, titleFontSize) + opts.PrimaryAlign = text.AlignCenter + opts.LineSpacing = titleFontSize + opts.ColorScale.ScaleWithColor(color.Black) + text.Draw(screen, "Pong", titleTextFace, opts) + + opts = &text.DrawOptions{} + opts.GeoM.Translate(float64(g.ScreenWidth)/2, 3*titleFontSize) + opts.LineSpacing = fontSize + opts.PrimaryAlign = text.AlignCenter + opts.ColorScale.ScaleWithColor(color.Black) + text.Draw(screen, "Single Player\nMultiplayer", menuTextFace, opts) +} diff --git a/pong/states.go b/pong/states.go new file mode 100644 index 0000000..cdaa335 --- /dev/null +++ b/pong/states.go @@ -0,0 +1,11 @@ +package pong + +// state represents the games state +type state int + +const ( + mainMenu state = iota + gameLoop + paused + gameOver +)