From 765b64c29c4d2ead3de682d58481cfd8b651350c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Valor=20Mart=C3=ADnez?= Date: Fri, 26 Feb 2021 17:51:46 +0100 Subject: [PATCH] code reorganized --- .vscode/c_cpp_properties.json | 19 + .vscode/launch.json | 21 + .vscode/settings.json | 19 + .vscode/tasks.json | 31 + media/.DS_Store | Bin 10244 -> 10244 bytes media/gfx/font_black_x2.png | Bin 0 -> 2294 bytes media/gfx/font_white_x2.png | Bin 0 -> 2303 bytes media/gfx/game_bg.png | Bin 10222 -> 10231 bytes media/sound/credits.txt | 3 +- media/sound/stage_change.wav | Bin 75598 -> 338732 bytes source/balloon.cpp | 96 +- source/balloon.h | 118 +- source/bullet.h | 2 +- source/coffeedrop.cpp | 89 + source/coffeedrop.h | 50 + source/const.h | 338 +-- source/gamedirector.cpp | 3742 +++++++++++++++++---------------- source/gamedirector.h | 556 ++--- source/ifdefs.h | 4 +- source/item.h | 2 +- source/ltexture.cpp | 15 +- source/ltexture.h | 4 +- source/main.cpp | 105 +- source/movingsprite.cpp | 31 +- source/movingsprite.h | 50 +- source/player.cpp | 25 +- source/player.h | 91 +- source/text.cpp | 1 - source/text.h | 35 +- source/utils.cpp | 112 + source/{struct.h => utils.h} | 99 +- 31 files changed, 3111 insertions(+), 2547 deletions(-) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 media/gfx/font_black_x2.png create mode 100644 media/gfx/font_white_x2.png create mode 100644 source/coffeedrop.cpp create mode 100644 source/coffeedrop.h create mode 100644 source/utils.cpp rename source/{struct.h => utils.h} (51%) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c492c00 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,19 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "macFrameworkPath": [ + "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks" + ], + "compilerPath": "/usr/bin/clang", + "intelliSenseMode": "macos-clang-x64", + "cStandard": "c11", + "cppStandard": "c++11" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a65266f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "g++ - Build and debug", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/coffee_crisis_macos_debug", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin", + "environment": [], + "externalConsole": false, + "MIMode": "lldb", + "preLaunchTask": "C/C++: g++ build" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0801ee1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,19 @@ +{ + "files.associations": { + "cstddef": "cpp", + "__tuple": "cpp", + "array": "cpp", + "algorithm": "cpp", + "__bit_reference": "cpp", + "__hash_table": "cpp", + "__split_buffer": "cpp", + "initializer_list": "cpp", + "iterator": "cpp", + "string": "cpp", + "string_view": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "iosfwd": "cpp", + "stdexcept": "cpp" + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..f74ab57 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,31 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "C/C++: g++ build", + "command": "/usr/bin/g++", + "args": [ + "-g", + "${workspaceFolder}/source/*.cpp", + "-std=c++11", + "-Wall", + "-O2", + "-lSDL2", + "-o", + "${workspaceFolder}/bin/coffee_crisis_macos_debug" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "compiler: /usr/bin/g++" + } + ] +} \ No newline at end of file diff --git a/media/.DS_Store b/media/.DS_Store index 3b4105f7595101ef6f00e956c2f34a7bed630413..913ab0a8e5f95c4bbb2002ff2e0a46991f1c10d5 100644 GIT binary patch delta 39 vcmZn(XbIS`RDi?C)IvwW(8O}`ItjL?f)8EjrB5ylSy{mBdq delta 39 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 E0tLyNnI3=X^c6!Je7Uw&4>=^jkQC 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 literal 10222 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⪚|&A7ag^D{RI_{gK<#v(%Tz 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 zKm;NXfe1t(0uhKn1R@ZD2t*(P5r{wpA`pQHL?8kYh(H7)5P=9pAOaDHKm;NXfe1t( z0uhKn1R@ZD2t*(P5r{wpA`pQHL?8kYh(H7)5P=9pAOaDHKm;NXfe1t(0uhKn1R@ZD z2t*(P5r{wpA`pQHL?8kYh(H7)5P=9pAOaDHKm;NXfe1t(0uhKn1R@ZD2t*(P5r{wp zA`pQHL?8kYh(H7)5P=9pAOaDHKm;NXfe1t(0uhKn1R@ZD2t*(P5r{wpA`pQHL?8kY zh(H7)5P=9pAOaDHKm;NXfe1t(0uhKn1R@ZD2t*(P5r{wpA`pQHL?8kYh(H7)5P=9p zAOaDHKm;NXfe1t(0uhKn1R@ZD2t*(P5r{wpA`pQHL?8kYh(H7)5P=9pAOaDHKm;NX zfe1t(0uhKn1R@ZD2t*(P5r{wpA`pQHL?8kYh(H7)5P=9pAOaDHKm;NXfe1t(0uhKn z1R@ZD2t*(P5r{wpA`pQHL?8kYh(H7)5P=9pAOaDHKm;NXfe1t(0uhKn1R@ZD2t*(P z5r{wpA`pQHL?8kYh(H7)5P=9pAOaDHKm;NXfe1t(0uhKn1R@ZD2t*(P5r{wpA`pQH zL?8kYh(H7)5P=9pAOaDHKm;NXfe1t(0uhKn1R@ZD2t*(P5r{wpA`pQHL?8kYh(H7) z5P=9pAOaDHKm;NXfe1t(0uhKn1R@ZD2t*(P5r{wpA`pQHL?8kYh(H7)5P=9p;7#BU DZjPI9 literal 75598 zcmeFZXLwar*EYJ^uIasz5FoVBd+)s?y-BeEf`|o0KtPlxq9USF1O!n56%c9CTj(XU zK!8wENiVytzP@{8Jvlz__nvc|@4Uaxk0<-u$=++uIm$imIp&yiEg06XckkYHS!Q&% zQN5b_u{}BR)@jn{&e>8}yHS_2H zPRqZy{_pMo`}Zn2X8rfyTg@x~UL6|vp;1*8W|ai`Th;dO*Zyl=Nn@+V|JPpT_033vs?GGW>Kpwvd(bDt`0vupE2fwK z_{mZCN^kT^|Np_RN`mPtwVE1CEtO8-+CTlN{lEX1{r=HMJ^r!5lvQc6>8O%l<%8)z z$uta8BH2c0Q485&zNwXZQHv?j>`!B8Y}LC8i+{#f`KwpjZQ5miHpiNlnZF4U(=zI3 zN;cqui53g5O?%8%>Pap1L*q!E@Go4*2f9j9OlD9k|1Td*G-)(>RV9^Vm@-W(s&tvX z|B*;isVCV-J52MN=~LC`D&I_rG@|OK${y1{x=L3}8%UG#FPo~au#8%XIrZc5=Fq#! zBN}b`ZX!}8vkG$)JCaJaR$*T?hWw{%WUG0N#?y}(Pl~+BRQgWX@rZrZDASfInbeoA z{3FS{LI{(VfB0kCOZ*kk+iay?CfcSQCbDL$$w!iF+DYD+{?HZk8-1b|>7grRCFwD- zu0o5nsAyFy@gwP^hpf{ott4s0ZhBYwVR9YH(c84#>_cb>c+*&tNRr3`YOO-m)JtQl z;!!1sWSJhDGEMGQolcm3Ky2j?$)g_=0g_3!5bsQ1)k-@oxJp;(no{W@*<-_4o>4G{ zVn;Hly$b99I%iD`P4vhEGnyokw2==ABop_{D3cu|mF65-LsDt9ivENR>8Bq;h+*W) z7V?2+sCkZKS|dZA_Al$mHqtCqj*%dX;#C#hDlKM=OkJ8GSL&nFC&Hg{1x2I^U$VfQ zxm9RU57Q2l%VZIqr4c^VMj8oalY`XPWC!6$tr~pefh>;% zhP)zsNV*wg;)AJ&I-5NR6C1{uaUu(-KmCv`B#V$HW{Ma~qX`f4+KM+1i45$Zzhp7t zLY|X8l3`+CdS&_7I0mzUvWv-M(nT^TYQ!chqzILsQ8X!LB!~KtOq1*Mttvj$rz$6q zz2*<0NIl46(@!%;o7^C8bc5_Cw8$2dEyQ6NFUou**N$EmM#4A3lX{X5gXO5{n0X#nRxn_q2g(m_n;U@gs49?`Ht zAE8g46C35q>jEU1tAGt{7Vwl ze3K`HhuPE2iKLDCP)?vMLUM?^nl$5iDoTV-EU64^)5tlJ(nvFt(MZxrxRG`hk$lr8bH)x4@L%z}#l1ElZ=tp^# zB_1Xr+upzKHPM^ozWqxG}g!nNGwA(;_p5W{fGSlqc+HBR&)I zlo=^MR;~HehuCT|#l)J{DiM8%1H^e3tf1UiwIUEIChI8Ykmt1elXRZ(p~Zzyl-YU4 z585d@9IpFt&0_=tp-{{o%F?1i9{MU}*kC0gE1{RUiU^1`ag11Eg@r6ekhO$B73auC z89q{_M>EgtLAiq1Xx1xc{-8KeJ|^CowFxndeuz_)vxo~E17tPzA-pI) z#Aj1JkG`5w3T%i!7A62VSa6NvK}b+WC;VuIChg{or&yWaNjI_ERUBCOo#MT{kWn*LBMDLz$fF!>5G=ue258IC+Lxnkx#`fko<4SFcM zQQjd7sh^o?%$!J^qCVs^vDuC{VMIC=T&FnL7=PGlWysh3N=yu35`w;jzY5=o&%_7% zO%^!tn`g=}z6`B2)($U;MG_EmS6-D2!j#rS;wiD;T+6KJM|shVAK6SrWcuI4LDmMq-{%BQf4r!EGG`S@P+bBN)@+Hh!#fetgnyWy8yxt0ZByc0N5EP~J;2K>C} z?}8pbe7E8)LI%yw0QB?+yLj}V*+y%D9TLmYJ0IU2h($0YP%fhwI`DF#t;~16y{agax=PbvE5 zK?k)3VhqJM81FDxZ2{+KO(ag5bAaYQp0~Knr)QBMCdPu z{ggY2Z&i8O?3xSA`0bTX+R=>c{@Of{w+QwMLVGxC6d6H=&R%7q*eQKvbs z!vF4&Gl*#gZIo5gq1!NMhWSBS5ZD$E%xc4rF!&Y(`x1aNWyoj71GEcFTZ~OWJZJ@` z>(TgJA2KQPXMkUMxGqCGajIHn&qPQG1|JyMMQC{|$24GC!-rbnKma%q2_1FtH;#D* zHkJeNKw!*6w}@C%Uabb-;~>?^6v68v*vvq>06STD9mBN8?+ECj8lV)6dInyUBMP;_ zsfMsI5RvtRWRejL$qq0lfGGs-Jme_Q@f0%ijOOsW2`~v}9w1(+Mm^xv4puaR&K6)x zJ6IaVWP-Wr80#~t!v-hXtymp|%I|SZN2U?1^{=#rvM#NlML^35Ov0hf&JbG~AwqR9 zx58jm03p_%s3!2Xz)l&FumUey zZSxSpLZdPIGzOZ{z^oqLji9{-W=u2K*#ugHnCIY79B zT}4J0$gEwdy8>+*;>~~^w1OB|If{XZqmr*7kVSc=BhwBT)PeO>qj@lMXiZjO3(X)F zY73P62bTBR7$}1iS(ury-DzMB8;_ zZx@(e8}b97Jqm4s@G%oO(VnOpT4;rdK{P`#)`Q9<7Nc$Wd=C-veg(h^hnxGSEK?*AjvIBjgl{e`Cbhf!3z@-33UinBV>w zkpPw}m?=r{rV%hILY|`)s0Y&)f_&wgN+Hy zrAqXB0-GMtOLMRx-nEd6X|GZXxHUpNsDjeb(g+AOf>l&^&^qXZws`1}jC;m);Nvk` z1M6D2N)??8Hqw5jDI(jB=>R7AG1=hS6G+Pf;itYt`(zyIX=;K+FuzMMb%?CB%8Ym7pjX4#D`{52pAI!Bv_!CZ#;&4+R;Dp zsf>9v^b5#l8Q_@*J*i?Ug*C(i+Qrf?y#)3afLU31(HTe*;!gSgmT@1RXX082=0GT_ z2M;jzKpPKu9$tcmV)Tl}`XFOw~}~rgA09z~5}3 zpAIimVaW^3Av#qm1xCg2LBj7MVDl7mN)byh{ubkJIk2YLngb-W!Be`EqvKmXEH1=2 z+I!Rh8$%F%E23h7ehK)Oq7R*0L?KGSu)u&cTI+*>ZxDQUR_4(Q@Gl)bDW4Vq_owhL z57#Qtn@%ukZB$^12Nuv?oz^JY;nPV2S$Ye-^MF$Ydb{wZ)22+w$*3G*K_(#X)4i`a z=#BuZdEiR*Rs`}p?TF|!GXyqCh{9cbzYA;`rYqL08knDX#sx&~4m8r9GY<$;y=y^# z+JzKjZcrYfy}AoyGhq20U{a2~i33dX0;^(F8&vty{+vz{3D+`wrnTP#&vU>WKSYx% zB|2ZDGYTtm1nm}SeTu^xTn5cn^yv%+Gy%p|@FvsP3+7Or-x}*lJnVged~?%CK@1-w ze^Esf%REG^OMq%~@TY#|6({Ds4jX8H>xOM~Zdr`zX+SX-GwULdv0!y-3#!gIp( zE_{6q`>c>l=Sx%*RsiS6=-ZeX$czFDsCRm09i0evHU*n{B15p149|g_SX^b9JLn}N zQo%sJ5u(`&G3LQ;$^fyDOZ#R!{N~W^M(tv)tl_%Aide*h&e>`AM-@jl;!fuVPQ=3j zE=NOS1B~qm8@r+<8nJ$Y8U7e}^QaQ3{#T5;sCyHD57}RaSor~0suCpdz8bJ>4^550 z&PYV9I@nkT8tAko1RRb6O0)uBK@Qcyqa=K%eY6u}6lk(S4#}po+(LNi2Oq*wt zoh8v7V;0s{M{MdKhLi;)M3+u7C|8z&fi_@b1?LR-PE`zxeirmru;M+!%zXqad1ffC z55@e6X3m1ynb4{mtuU`zBRWB-K_6ls7go-nFl0|!J*Wms!i!EN-~u$#-j;Ss0bqI* z{0l}4oldA|qmu+$8|~m6olzAa+fY3!Vm4`rQ7HO_!ACk<^oMESw&7dn9GAL_O zj?YBRq8V*~ZFR7O!}=ZtyTj3oR$w{-j>X)IW4hyNO)$%gwTJ462-Iv1(T`57T|ifb zkFLs^s}@jT5&IY5OBige2hLfLIVD7jDzGQW^Q0x7>4SOR7PTp@>_tGDhaa>Lr(JMe z;AcfFvM}RlO^=4I`oOgwu10{%0^YP5(CLH&)dV~1raW2!+Y)iDK4MC9Bn$lahkv!!1X%#+zogQ#r%ndy?0Rgl_SR`RdT?^6eo9S5uE zES648X_;nIVrHxM}NrAMAT?iqpa&e94UX;p@Vi{bdJ~(>?(&1&%k&YvAGZV z+3=dqtFy3HT|re*T3Pd4N2H$viz56!h?@Fqw0({p*%C1FZ=mJE^&B8Vr%NH=OeU-i zg!Fe1qgQ~5Li2Y_ zH|7&&EVGlrKgeXiU>?D)708Zrac(dTf2T7enU65bh9UxV&PDeO@`3m{V0|BRlxpP+ z*prG{=U4Fk6xJg;t)X4C9no$MU+ROwbh;i1+=_tW6R=K(H`$2YUD#FuL`qRZ&^p-w zENP1QYUb=>AVxbq3ub39xK@DA)Q|23(3wgp;+F$7X|H6d{3d*P1t)bojQy}RiRGBdY*S`C+l4vICNZC|am-*A znUXb(9IO+|nah}!zZ$Z)ed6->5>X@6CeS-h=sHc}rgO^~Syk z_72MX+tWTb-1BYrVE2!ii(M<8eN++lWJdYIM@!18r!OvDm)5G}vpeph#i>sUQ?4H> zP_8VO6>|1&7p6~x+&xN`9KU`J{ zhFs4odUPwc)N%iv^2Lu+T_Kst-i>*)#Db#FEYPMti@Ae>JkE4q)**QnB`F$z8?Kel5<=0neO5{G&y+Rulbu$P8iveynz zx6cZHVLulh=ok>u!jT@)%F!e86-Pu=nPY$SHs_w$S$=Z0AN+$8n+AGn1qCmuH!3u} zQFug1%fZntI@qgq=#h|^J>box%n>K+U3_^{!{1+D)@1oiYqQaB9cVgc)`rF#r`KqZ zJ9%%Nu+cx(C?7DST8FM&)QXmoq3i133COSB&^bRU&ekpXS7DHI2RGbO&eUdi7zNrM z{Uaq?`&81b-mnATd17YwyS;gtJ*>RFTI*E+vslmVTfScCVht9RG+I&(=wDzY9l#DeIfp! z?Di!oA9+v93p^v`dG2`mtV@yVyS|h{D!NFC28rPohnW&VZ=qn zXMMMd=J}$)#Xk#I`lQ0Ye4~qUeHlfg#GA!O#qA|FsdZ@+X;$fT@j}UPaYOODn_w}5U6%U?&Ue+L^Z%K~_-ohq#>gEr*-Y&Q8#rLy@pIQ1W`q*z7fd@xC3f*@h z{nqb)r~mYO^GEylZp_dR1V3MJd_c~El#lbjxshI+cE4SP{w%|D^Mx+1DLtc1_k63p zCtcUut7*nm{WWH_@e6YA70mW6$hhl}jUF<~7@jR)`ml@HQ|x3;WFK;&TryvudzW{y zZTVl>RBi_QDraMVVt;3{kuR$=Cye^WEX>*A#yP!~k*+t#?El*M0un3OG$xxr$l9&5 zIiGC|Z+FHBKlqIm;{EpuulfgCKJ<53Tz)Z@Eq=W$OZ~zu9_MwTqw^i1vtuv6-R8$P zvhL)9Es@*}ek=QwTga~9-ehNT%h-L~dA1ktVN3a!xd@AmKWg32e{J80d2-#dCLr4O zUhqEq?XYj12cy>c7sovb{3CH>$f??U!|o(^iEPs}E+(f{QQT)8iW1)J)~Dw0eRkBo zH7K>-tHWnBSTXuw!+K-iZ}?>N@#MY3H`FZ{_*tzbJp-$c>M%04tl5zWvEIiaj}kWr zd>_-;IWp``Tc^MtmSpD|zRX&O8!h+&uLaBiwlQ;=`O_H5G{h=*S$}T4s~N7G|MIawrvhy$G(JXWb4h2w)|@B;o|jdLsLJ} zij{Usr1FY%LmnYsmiPM3%T0XyaUcgdV{R)`v0G^$kol{TS0k|ATp5FJt-}udw@>Lu?DKH|HlL^0C&Ze5P%lFv6h= z?>Jw#H1k_)S>`vu(#UU@(9T)L7ugLi!SgY2zMQ4gdpc0 zp|10H)KF!*xxuw4=rrwWTp!*`-&PXX_)R*7~=irsep&+NZu}T1($yEx@-y>+Bn*t?-qr zIle{eATdRGS+px{#8>4k-wMguTdPGV@u0AKT>Zr)}EPET3hao2*J5fv9QyO%Y8`a$sz4<{8&y|+KN*Ug%lj!UC5 zzC5izc<}e?dshyexwCYC&AW&GnwM7dNUw+Cr-GmUb#YvF$gOYlyFct*vNCgU#g_c% zp8chE>7je2;u2riVpOlbUmt+Fur51=`<)%f*W(<*BIJPYxMsp$?iqibTg)HiT-+S) zRjvsa%&}Yz&dTlJ!nt95344oAVOtBS?5{!^TO{mbp9u@t--RyhYeEHM6MkiO^D~*D z{BTC*K4Fe=>C9{{nJr*{XXmi*ab4I%K8W4KzhIn#mF*)8XNL<{F!Bp-h%l5d;Zyl_ z{2*Z>pDNVg>selKuUW2g8!YKux+Q{dYn{MvwqD?C*xnEdZIW=vKHsv`(b&4pnPNTV z*TQzu|Cuc|aE^Us&~|&hV8PKcWS`@k&|XeYShU~8h}ZpJimn~FHf~#RPW97a?P^bq zn$e&%&ewcmjT0Th>K638*|2EPnPwl1+}kp3Y}ZyFj9c7d#OS}8JQ?~|^3c9zNgsFK zlMvYQR&O#_1G8=35*2_u->$>zRk#PJM$Bs%5Fyl+&_Jc}-d`Zx+WQ zcQuixi4Eiw@jZEkbXx8!yXDjJOG;zqJ7u(TSZS@aRo_%n)W4Kc^+%SpB~0$?U6?k4- zT&Se_j?1^bkEE|WQBrqzh`6+(vF}0I8tXFaEigFQ`)mbp_4&bk`q*L3}q_d$h` z6JNeMYgOr~XP1jNWh533PEXHExSN#St_tH~Qqo-)~yf{OPW;$+_*_!9{g_>&wG%3}&C{SDB~!1ZEgcF@D7U4o^?$ zPnmQ2yXZb^hTW`OMhLf2$X=#oA{~b1l&LNxiIfRxhi2m19a5r9du}hswj` zMN+zSU%V!56HiI+hPV>Zjb3a+HtccT_*6LS3qSr-iDm^%d#~{gK+q zXr^s87HLGIqO1A_eW3oc{T+otlD4Oq`V8pPgXi9lBCN+L_x{* z4pE9dpD7JIXO-jbn(9gSOf|>-n)s9tjat$gEdrVMeflUcVY{psp2^>Do_uC4gq zcd-1j_gGoBXHluulTh-K`%KZtuKI;vRgB3$U%n}?sO)6UiPHI5gG=(BeOpwKF{@zO z!vlG7X-%^)r0P#Uxw7g}=DE20ADwvb&g>)CZuLL(-tEk2}=YfCqaEnL&(4W123fN!#z>pQK!BtB5T7JaIp)JrRuj%dx5 z!TM1()R?QQ#vn#yV!1r_5WknRTgLLy)-?W+wT!Q2JIr(7$v$fiSKr!``^0jOjkRoG zi-iuzsRx<1LSyDUpJI&Xzc6_I4dZjJw{e?&)re!WjP=atI6-L6o-@v|^Na*;gfWC$ zV7$ROnJHWj(}v4uOV|=_6>@Y3_Gh6k`ziKSldS96R<>9!#D1GI>=*bzXOYm-FU;D} zKg=fkKd_Atc+K#(&u@C)Z3fi?V=27To>D!97;^pF|>QDL73 zz7jDwcx?0^p-I&~k2sMS5HqaKm+=D|W!IeFYJ0ujo#!`>>+@@i>w`nuqzw;h+hgQ} z*2jmv+WhLkW{s}*>{)k1haVG{H}jAEp>EgkiwU7YM&u{XvB3|l(SBau&vuD*2qPF5 z+shch%+xRG>Dv3+EUmFBXnFDu^@P+khT8?xtmGj*@Wzn-se&#(SU-YHQ--!FnW6$ zihnA*WV;rnDjK6r(1&Qt^v|{5bwSV6&*`t>yydy^5^ALO+#;qf9#Qu2E!Y~uWZ=Jw zJ~y9LyMrlW{$?7m8O&z38TL+ZvbFer?1%hI$fqB%vxTYbB4H`} zned!lD}-}N!l&G6z6-yGKg(C+>j?+BIl>a|RiQK2Ss2Lq_|4pCR2TW&0RDGQ=i76W zg!&w3*~i6L8}lCPY5ufrmGFzbiKVmSy5+EAnAPqaXFcKQV(sQ=V$HJWSYEeZwsf&` z)@}BN)=0-etKrD8t?^58BnGT@J_!8Ezjuf`uvhq@kg}-2h&9zRqir?E#V@P(bImqQ zm)ASq_DJKM-S)Rk>OZ;NpF=NoaE_SNVcGDhZKn*m+~Ry+YvZ|H2iGlZJt{Gx(H}8? z)mk1F5!WnmXV`AXy8#z0o$cqj9>P{;3M&|K#xrfG#%mXq*VP&FFm;Xeis}*@t6jvG z)EeS*^^DkBTO`fU-jt7NzbZM}RW(pA)CTCH-c5hoP_&ng?OKVRtVQWp)%x0(YEQg6 zD#_~Waxe9v)LR`ajaPZ_IZ&E{yb`R&N*9&!QcvY=X`lSAG(cV~6-xu8gOUgN`A6}B zWDy@oBYaNzrZ*USBUL)%DVElFewLL?nZ9xE4W1|Et6c+1-zYy+B$cG(_bHm4yCL6|c`R3W z+CQt$!`4qv-re@_^Bb4$MO+HKuQWV5hZVRZ&hDz={IV0% zm;Hd@xsgmKt})Y*tHH3Gz)WQAIMJ+wv)A@aHDeC5RKLKq)PvaweGNNLug$eFzUNdU zju+T1d_%5)ug*6X9Kt~1xR4}pmd3(R%WPqTWuY*^@}6*2m?Z=WeT7KAtI!vbJ;b_r zJG+8k#x&+9F^9O}%p`6tGo9-~kKT z4(^@QVqKSG4S#8KuXd{@iPgEfe@C~e{$1$(s73*kLT=bY{3lp$+75GzguToIw!X2? z=&sk&Z-J$2G=Fu8HVkX^ZTU}asVr$f$>a3u$`bu|Wv)I%ouvC~b@b!fMXkR+Ok1yC zRKL{gs_pbol%F+T(Xh)JuTGJ_QKIFea)R7Q9xvaQzL7tdF3D4+3VE88plp<;DoOHk zWslrdd4c>HtsIvpC~M?dN*}qcqDfcfD^dq}m$XA#C~;CVX{dNv{KYq1jP{lIQoO(T zj(ey1ZhG7MJl;0GKE6S|^}e@!$9*6BcKas#I{13{KKFL?o$_4qE_P>o?o?Fsyk0)o zUB9fX;=7Ut<=2Y_l$I5=Dn5-z+z03GdhvDEsT{v&LguDNdo#XH%XtuS>++o$SIcko zy)gainX^e((#}k|dhXn=8{)-TcP?E|PXFfa(5JsV`XJ}q%+7^OexI_rC9B+8g}=Dp z`xWY)E^3tWh4!I3N}sH))>E{-dSm^jzDGZYv&Wdf*smCaeTf;tbzt(j)y&6y0DFahixv61Y*S%0H%hq6%@zjovxMz@ zb72Rs^I!7isA$jf7JeB&jr*9-Wk2O7vG4MCn2-3C%zFME=4bvxW-I>-vzX6g-bWTb z!H00kg2;U*r1D9YKZVmA4JEuB~GH=REF z*M8j{Z}~5Gv_i`Fll5?|p8k%S zf_yn!vnYMEld>PUdP`j*|E^w^C#s4ZqIOX-mA%RuB}*BuM5tp>NA^&bs&$li)F#S& zwYf4%oumv=&nTeOr0x-sj1Ry z$_VM6oG;dpcZ%;wpNU0c6S0oy@h$WDi{pIPeLnB2zSZ8LzHQ!yzJ1S$LG1~9_0DV{lwkc-OYW{^?_@E>vF~HiUZ}H%6pXwW$7hvl(>rK z7kyu_upr@u3lAUe%8q+}{Mp{eDUSx-SMIgC)jD zko%f_#&%;LvCY{yZVBsS-^3Z}U^b6k&wAP4SQopU4dPC)PVRfQ9V*QI+yyp+`<3PR zmF#N%5v%f1TnC{iH$(W68zXE+PQ8dVzaDS!1HtJnd@Z4+5GBkOx(g{nsxZS6W!Ykx zZn$7l9x&J6ovXLmrEPV$7tt`U#PVuhdGbJ+OXaLwnO|EylIk+&-^U&2B$*|E1Ti zo{Rdt-^1PObeAjLrnFZ&jA`+4%QqX;ZTMHM;G~Y#T2<>F@jQHN(8<90&aIB0te;v^ z`IQ`Yhj<30JLYa%lN zlGaMQr|wg~Q{&VQ>U+v1mMOzAnX!s>p~F#1!9I-!DGF_r5RC8|G`_x#XSYUg-VK<@CBM4tqYYc;91` z=em2AhqukW0?`^&>dk2gP`kyZai^McTlA!UMV zvPbdGlq#gzYL@b<-a|WsJ7Sj^r!f(`mILf#LuLCht+~6*a;_J9mivOe!8tgNU(0pp z|Kw`$+qevF3%8H^hlLifzn(Zs7_Nis&A`yZLzvs`&_-R%~gF` zceNqTmp;&&sf+b$>Pvc|(oj!VWbLN1OFN9byh*vFzM`~KLzL6X6?wGssjQ%uye)I` zJvmpp0=90KM@XCGNGU}QlJ3ficu>A0Zji5vZ^@U%TJl-(qI6BHEB!3C5?hK5eTRLD z$LagY-O#(zwb1iZMXq~ad6N5d*=X03Qk*cAEG(~A@_AXU;#W$q7d|PjSrA?HH1Arz zUyhu6;`xQF$r+oTUbx@;;r3e#?!Iw#(~Y|+uU~%Y?2rrJoXJg@bB?|A*Ts*o?YO?- z_Lp}Tr+Xhgc&2Bb%WING%6eFq?ZA~XB1roJ|t0vfYI`+=oI(Zlo}V>ng0} zCJI})fx;hLKVb(qNNB>9^3Sm!6Sy!zV;c!)*hpbF8!zl-qp{bjFEr(b3a@ePgmYXQ zVIIE>b#8_*%JQ~lBi8YZ(yk?K_TkWXpKifGe;9I{jfy?~g4q6)!6Vf8c z2<1b%L{twOA8n0nR&8a>(ZpZk2h~Zb`MBZ5dI>GxZSqTpnXTXJzO&<`KH=Tg_ABjv zungrggcw-v~|j6 z?K|Zy?S#@r^Hb|;b<`VbQ{>QDYBhDOdQRD_PEqcty%kPtszhqxN`0-m;?x|vp8a*sF9j)uX{G(jr@5bd z>WP!2BDs$`MVqPj$6el6ne%uSVLcnnrLi3DZ9Zm)b3d_zIDwtWtz;Hrw=|R6V=Un2 z;?(F(<2`P$v58w}q;OW|cdi?AiA!P1aR>A+m%`?A!#O{`jJwYz@n^YN*p*-7H*l%^ zV&uLhoWC%Ld(L;~Zt~N)a(*_KgEpB@mWe_e%S^#)`CcdyWFf{f)-u3y)AFO`V{2FIFq>|zXFq1kwa>I4 zbR;@5oT1J{|2V&X0XF|Lfk^@9gAWF_4%-pjDv}M$i_s$HB>WWny`}XPN?h(_jU8i9!nzj1A;a3gk)K054IpN#5 z^r$}J)k4|8ngQqSdG@tJU&~8uGtSU27;kHmHbVVb{YlAH1}VD~e`UXtBMYie{z{Ed z;&Gz4Q=6bf=}VQd`bK4e{-rWWpQQxpqm@fq1LYO%iF{K%i4)PIa((p&`J!@2{zLHt zx9cl^Ds7bQIGw+u{H)|dvaEcjc$8+!-^ykA1Lb46w=z_Y$B7fG6iFxLZPLr~hf;01 zy>w6dTkJ0N5pRi4eD8@9eSTs+{L|-JzWQRWH(tEuZ7VkN{UCnsdoG^ysbZF|Ks@jJ zOnl#G7yW#1`(E|_9p+3^x4nq-E}-JxnBSN#f!ae|9ZC5jX|fjUY&GOzS86L;cM&8PfXo(WzfBY zxBMT~PtSU`=GlVW!+CcKqe?y~o961`e(d|oH(Qx1N9e~@f7~&i$7Y;B|H*--z4Ix5m3Kw}~IfE#tdzAK>#$zBjj)pTxQNV_Y4flq+CsaEq$a&CzpkJrJq5eIBe+}3j zIxuK#M1zq1(O-pCi@z25M$MD4)9NKw@7SbiQby~N`iDC|XmYMs%T|y3C$;M`i0yD_ z;J)^q`gLv_+v84)`W>n@p5DBm?zsBj)tHz#CN3;`Ys91A?}LvzYx`fbykxJ-#amVx zuW&zWVayn{i@rf&wSLMJHB5VU&$2$tyzru5e$V1XrHjfp zxNV-FP!|VbrP-p7)uuC3j8wJ>yM`Oh6>WniKp6HFjsMZc?Wx$na@SDuk)YbB=s=vj2^|^ zPz#qM#B*1KGB(lDfZJkO$?dm%&7HSw;fgKiINth93C^juo6b@8 zG5BW~r~GC(_WIXyrUzs>EkVou&IYgZ_k>CTb0U5SS|5ESq)W9C;ad~OL`|wQA}*m( zVfDo=ZzcWJv0w6~p8J}-G9aPlnISdX_=bkIJvsPv>%0Aqw^-S|W|IZ&M%UMy^sBY9 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%0DlH41w1WjO#D= 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 NBc6xkd6}d3{{f{$KzslI diff --git a/source/balloon.cpp b/source/balloon.cpp index e9da6e5..01a3e2e 100644 --- a/source/balloon.cpp +++ b/source/balloon.cpp @@ -5,7 +5,7 @@ Balloon::Balloon() { mSprite = new AnimatedSprite(); - init(0, 0, NO_KIND, BALLON_VELX_POSITIVE, 0, nullptr, nullptr); + init(0, 0, NO_KIND, BALLOON_VELX_POSITIVE, 0, nullptr, nullptr); } // Destructor @@ -227,13 +227,21 @@ void Balloon::init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, // Inicializa variables mStopped = true; - mStoppedTimer = 0; + mStoppedCounter = 0; mBlinking = false; mVisible = true; mInvulnerable = false; mBeingCreated = true; - mCreationTimer = creationtimer; + mCreationCounter = creationtimer; + mCreationCounterIni = creationtimer; mPopping = false; + mBouncing.enabled = false; + mBouncing.counter = 0; + mBouncing.speed = 2; + mBouncing.zoomW = 1; + mBouncing.zoomH = 1; + mBouncing.despX = 0; + mBouncing.despY = 0; // Tipo mKind = kind; @@ -288,7 +296,24 @@ void Balloon::render() { if (mVisible) { - mSprite->render(); + if (mBouncing.enabled) + { + mSprite->setPosX(getPosX() + mBouncing.despX); + mSprite->setPosY(getPosY() + mBouncing.despY); + mSprite->render(); + mSprite->setPosX(getPosX() - mBouncing.despX); + mSprite->setPosY(getPosY() - mBouncing.despY); + } + else if (isBeingCreated()) + { + mSprite->getTexture()->setAlpha(255 - (int)((float)mCreationCounter * (255.0f/(float)mCreationCounterIni))); + mSprite->render(); + mSprite->getTexture()->setAlpha(255); + } + else + { + mSprite->render(); + } } } @@ -328,10 +353,13 @@ void Balloon::move() if (mPosY + mHeight > PLAY_AREA_BOTTOM) { // Corregimos - mPosY -= int(mVelY); + //mPosY -= int(mVelY); + mPosY = PLAY_AREA_BOTTOM - mHeight; // Invertimos colocando una velocidad por defecto mVelY = -mDefaultVelY; + + bounceStart(); } // Aplica gravedad al objeto, sin pasarse de un limite establecido @@ -357,10 +385,10 @@ void Balloon::move() setInvulnerable(true); // Todavia tiene tiempo en el contador - if (mCreationTimer > 0) + if (mCreationCounter > 0) { // Desplaza lentamente el globo hacia abajo y hacia un lado - if (mCreationTimer % 10 == 0) + if (mCreationCounter % 10 == 0) { ++mPosY; mPosX += mVelX; @@ -374,16 +402,16 @@ void Balloon::move() } // Hace visible el globo de forma intermitente - if (mCreationTimer > 100) + if (mCreationCounter > 100) { - setVisible(mCreationTimer / 10 % 2 == 0); + setVisible(mCreationCounter / 10 % 2 == 0); } else { - setVisible(mCreationTimer / 5 % 2 == 0); + setVisible(mCreationCounter / 5 % 2 == 0); } - --mCreationTimer; + --mCreationCounter; } // El contador ha llegado a cero else @@ -398,9 +426,9 @@ void Balloon::move() else if (isStopped() == true) { // Si todavía está detenido, reduce el contador - if (mStoppedTimer > 0) + if (mStoppedCounter > 0) { - --mStoppedTimer; + --mStoppedCounter; } // Si el contador ha llegado a cero, ya no está detenido else { @@ -433,6 +461,7 @@ void Balloon::update() move(); setAnimation(); shiftColliders(); + bounceUpdate(); if (isPopping()) { setInvulnerable(true); @@ -608,13 +637,13 @@ Uint16 Balloon::getTimeToLive() // Establece el valor de la variable void Balloon::setStoppedTimer(Uint16 time) { - mStoppedTimer = time; + mStoppedCounter = time; } // Obtiene del valor de la variable Uint16 Balloon::getStoppedTimer() { - return mStoppedTimer; + return mStoppedCounter; } // Obtiene del valor de la variable @@ -641,4 +670,41 @@ void Balloon::shiftColliders() Uint8 Balloon::getMenace() { return mMenace; +} + +void Balloon::bounceStart() +{ + mBouncing.enabled = true; + mBouncing.zoomW = 1; + mBouncing.zoomH = 1; + mSprite->setZoomW(mBouncing.zoomW); + mSprite->setZoomH(mBouncing.zoomH); + mBouncing.despX = 0; + mBouncing.despY = 0; +} +void Balloon::bounceStop() +{ + mBouncing.enabled = false; + mBouncing.counter = 0; + mBouncing.zoomW = 1; + mBouncing.zoomH = 1; + mSprite->setZoomW(mBouncing.zoomW); + mSprite->setZoomH(mBouncing.zoomH); + mBouncing.despX = 0; + mBouncing.despY = 0; +} +void Balloon::bounceUpdate() +{ + if (mBouncing.enabled) + { + mBouncing.zoomW = mBouncing.w[mBouncing.counter / mBouncing.speed]; + mBouncing.zoomH = mBouncing.h[mBouncing.counter / mBouncing.speed]; + mSprite->setZoomW(mBouncing.zoomW); + mSprite->setZoomH(mBouncing.zoomH); + mBouncing.despX = (mSprite->getSpriteClip().w - (mSprite->getSpriteClip().w * mBouncing.zoomW)); + mBouncing.despY = (mSprite->getSpriteClip().h - (mSprite->getSpriteClip().h * mBouncing.zoomH)); + mBouncing.counter++; + if ((mBouncing.counter / mBouncing.speed) > (MAX_BOUNCE - 1)) + bounceStop(); + } } \ No newline at end of file diff --git a/source/balloon.h b/source/balloon.h index bd3f8e0..b46531d 100644 --- a/source/balloon.h +++ b/source/balloon.h @@ -1,13 +1,66 @@ #pragma once -#include "struct.h" +#include "utils.h" #include "animatedsprite.h" #ifndef BALLOON_H #define BALLOON_H +#define MAX_BOUNCE 10 + // Clase globo class Balloon { +private: + float mPosX; // Posición en el eje X + int mPosY; // Posición en el eje Y + + Uint8 mWidth; // Ancho + Uint8 mHeight; // Alto + + float mVelX; // Velocidad en el eje X. Cantidad de pixeles a desplazarse + float mVelY; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse + float mGravity; // Aceleración en el eje Y. Modifica la velocidad + float mDefaultVelY; // Velocidad inicial que tienen al rebotar contra el suelo + int mMaxVelY; // Máxima velocidad que puede alcanzar el objeto en el eje Y + + AnimatedSprite *mSprite; // Sprite del objeto globo + bool mBeingCreated; // Indica si el globo se está creando + bool mBlinking; // Indica si el globo está intermitente + bool mInvulnerable; // Indica si el globo es invulnerable + bool mPopping; // Indica si el globo está explotando + bool mStopped; // Indica si el globo está parado + bool mVisible; // Indica si el globo es visible + Circle mCollider; // Circulo de colisión del objeto + Uint16 mCreationCounter; // Temporizador para controlar el estado "creandose" + Uint16 mCreationCounterIni; // Valor inicial para el temporizador para controlar el estado "creandose" + Uint16 mScore; // Puntos que da el globo al ser destruido + Uint16 mStoppedCounter; // Contador para controlar el estado "parado" + Uint16 mTimeToLive; // Indica el tiempo de vida que le queda al globo + Uint8 mKind; // Tipo de globo + Uint8 mMenace; // Cantidad de amenaza que genera el globo + + struct bouncing // Estructura para las variables para el efecto de los rebotes + { + bool enabled; // Si el efecto está activo + Uint8 counter; // Countador para el efecto + Uint8 speed; // Velocidad a la que transcurre el efecto + float zoomW; // Zoom aplicado a la anchura + float zoomH; // Zoom aplicado a la altura + float despX; // Desplazamiento de pixeles en el eje X antes de pintar el objeto con zoom + float despY; // idem + + // Vector con los valores de zoom para el ancho y alto del globo + float w[MAX_BOUNCE] = {1.10f, 1.05f, 1.00f, 0.95f, 0.90f, 0.95f, 1.00f, 1.02f, 1.05f, 1.02f}; + float h[MAX_BOUNCE] = {0.90f, 0.95f, 1.00f, 1.05f, 1.10f, 1.05f, 1.00f, 0.98f, 0.95f, 0.98f}; + }; + + bouncing mBouncing; + + void shiftColliders(); // Alinea el circulo de colisión con la posición del objeto globo + void bounceStart(); // Activa el efecto + void bounceStop(); // Detiene el efecto + void bounceUpdate(); // Aplica el efecto + public: // Constructor Balloon(); @@ -16,7 +69,7 @@ public: ~Balloon(); // Inicializador - void init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, LTexture* texture, SDL_Renderer *renderer); + void init(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, LTexture *texture, SDL_Renderer *renderer); // Centra el globo en la posición X void allignTo(int x); @@ -119,67 +172,6 @@ public: // Obtiene le valor de la variable Uint8 getMenace(); - -private: - // Posición X,Y del objeto globo - float mPosX; - int mPosY; - - // Alto y ancho del objeto globo - Uint8 mWidth; - Uint8 mHeight; - - // Variables para controlar la velocidad del globo - float mVelX; - float mVelY; - float mGravity; - float mDefaultVelY; - int mMaxVelY; - - // Puntos que da el globo al ser destruido - Uint16 mScore; - - // Nivel de amenaza del globo - Uint8 mMenace; - - // Indica si el globo está parado - bool mStopped; - - // Temporizador para controlar el estado "parado" - Uint16 mStoppedTimer; - - // Indica si el globo está intermitente - bool mBlinking; - - // Indica si el globo es visible - bool mVisible; - - // Indica si el globo es invulnerable - bool mInvulnerable; - - // Indica si el globo se está creando - bool mBeingCreated; - - // Indica si el globo está explotando - bool mPopping; - - // Indica el tiempo de vida que le queda al globo - Uint16 mTimeToLive; - - // Temporizador para controlar el estado "creandose" - Uint16 mCreationTimer; - - // Tipo de globo - Uint8 mKind; - - // Sprite del objeto globo - AnimatedSprite *mSprite; - - // Circulo de colisión del objeto - Circle mCollider; - - // Alinea el circulo de colisión con la posición del objeto globo - void shiftColliders(); }; #endif diff --git a/source/bullet.h b/source/bullet.h index 48d744d..26c5d24 100644 --- a/source/bullet.h +++ b/source/bullet.h @@ -1,5 +1,5 @@ #pragma once -#include "struct.h" +#include "utils.h" #include "sprite.h" #ifndef BULLET_H diff --git a/source/coffeedrop.cpp b/source/coffeedrop.cpp new file mode 100644 index 0000000..0211825 --- /dev/null +++ b/source/coffeedrop.cpp @@ -0,0 +1,89 @@ +#include "const.h" +#include "coffeedrop.h" + +#ifndef UNUSED + +// Constructor +CoffeeDrop::CoffeeDrop() +{ + mPosX = 0; + mPosY = 0; + mWidth = 8; + mHeight = 8; + mVelX = 0; + mVelY = 0; + mGravity = 0.1f; + mFloor = 0; + mEnabled = false; + mSprite = new Sprite(); + mAlpha = 128; +} + +// Destructor +CoffeeDrop::~CoffeeDrop() +{ + delete mSprite; +} + +// Inicializador +void CoffeeDrop::init(LTexture *texture, SDL_Renderer *renderer, float x, float y, float velX, float velY, int floor) +{ + mEnabled = true; + mPosX = x; + mPosY = y; + mVelX = velX; + mVelY = velY; + mFloor = floor; + + mSprite->setRenderer(renderer); + mSprite->setTexture(texture); + mSprite->setSpriteClip(256, 97, mWidth, mHeight); + mSprite->setPosX(x); + mSprite->setPosY(y); +} + +// Actualiza las variables del objeto +void CoffeeDrop::update() +{ + if (mEnabled) + { + mVelY += mGravity; + mPosX += mVelX; + mPosY += mVelY; + + mSprite->setPosX((int)mPosX); + mSprite->setPosY((int)mPosY); + + if ((mPosY > mFloor) || (mPosX > PLAY_AREA_RIGHT) || ((mPosX - mWidth) < PLAY_AREA_LEFT)) + mEnabled = false; + + mAlpha -= 2; + if (mAlpha == 0) + mEnabled = false; + } +} + +// Pinta el objeto +void CoffeeDrop::render() +{ + if (mEnabled) + { + mSprite->getTexture()->setAlpha(mAlpha); + mSprite->render(); + mSprite->getTexture()->setAlpha(255); + } +} + +// Deshabilita el objeto +void CoffeeDrop::disable() +{ + mEnabled = false; +} + +// Comprueba si està habilitado +bool CoffeeDrop::isEnabled() +{ + return mEnabled; +} + +#endif \ No newline at end of file diff --git a/source/coffeedrop.h b/source/coffeedrop.h new file mode 100644 index 0000000..0bf9c7a --- /dev/null +++ b/source/coffeedrop.h @@ -0,0 +1,50 @@ +#pragma once +#include "sprite.h" + +#ifndef COFFEEDROP_H +#define COFFEEDROP_H + +#ifndef UNUSED + +// Clase CoffeeDrop. Gotas de café que aparecen al explotar un globo +class CoffeeDrop +{ +private: + float mPosX; // Posicion en el eje X del objeto + float mPosY; // Posicion en el eje Y del objeto + Uint8 mWidth; // Ancho del sprite + Uint8 mHeight; // Alto del sprite + float mVelX; // Velocidad en el eje X + float mVelY; // Velocidad en el eje Y + float mGravity; // Aceleración en el eje Y + int mFloor; // Punto donde se encuentra el suelo y desaparecen + bool mEnabled; // Si esta habilitado. Sirve para saber si se pinta, se actualiza o se puede usar + Sprite *mSprite; // Puntero al sprite con los graficos + Uint8 mAlpha; + +public: + // Constructor + CoffeeDrop(); + + // Destructor + ~CoffeeDrop(); + + // Inicializador + void init(LTexture *texture, SDL_Renderer *renderer, float x, float y, float velX, float velY, int floor); + + // Actualiza las variables del objeto + void update(); + + // Pinta el objeto + void render(); + + // Deshabilita el objeto + void disable(); + + // Comprueba si està habilitado + bool isEnabled(); +}; + +#endif + +#endif diff --git a/source/const.h b/source/const.h index c64146a..c547b54 100644 --- a/source/const.h +++ b/source/const.h @@ -7,73 +7,75 @@ // Textos #define WINDOW_CAPTION "Coffee Crisis" -#define TEXT_COPYRIGHT "@2020,2021 JAILDESIGNER (V1.3*)" +#define TEXT_COPYRIGHT "@2020,2021 JAILDESIGNER (V1.4)" // Recursos -const Uint8 BINFILE_SCORE = 0; -const Uint8 BINFILE_DEMO = 1; -const Uint8 BINFILE_CONFIG = 2; +#define BINFILE_SCORE 0 +#define BINFILE_DEMO 1 +#define BINFILE_CONFIG 2 -const Uint8 TOTAL_BINFILE = 3; +#define TOTAL_BINFILE 3 -const Uint8 MUSIC_INTRO = 0; -const Uint8 MUSIC_PLAYING = 1; -const Uint8 MUSIC_TITLE = 2; +#define MUSIC_INTRO 0 +#define MUSIC_PLAYING 1 +#define MUSIC_TITLE 2 -const Uint8 TOTAL_MUSIC = 3; +#define TOTAL_MUSIC 3 -const Uint8 SOUND_BALLON = 0; -const Uint8 SOUND_BULLET = 1; -const Uint8 SOUND_MENU_SELECT = 2; -const Uint8 SOUND_MENU_CANCEL = 3; -const Uint8 SOUND_MENU_MOVE = 4; -const Uint8 SOUND_TITLE = 5; -const Uint8 SOUND_PLAYER_COLLISION = 6; -const Uint8 SOUND_HISCORE = 7; -const Uint8 SOUND_ITEM_DROP = 8; -const Uint8 SOUND_ITEM_PICKUP = 9; -const Uint8 SOUND_COFFEE_OUT = 10; -const Uint8 SOUND_STAGE_CHANGE = 11; -const Uint8 SOUND_BUBBLE1 = 12; -const Uint8 SOUND_BUBBLE2 = 13; -const Uint8 SOUND_BUBBLE3 = 14; -const Uint8 SOUND_BUBBLE4 = 15; +#define SOUND_BALLOON 0 +#define SOUND_BUBBLE1 1 +#define SOUND_BUBBLE2 2 +#define SOUND_BUBBLE3 3 +#define SOUND_BUBBLE4 4 +#define SOUND_BULLET 5 +#define SOUND_COFFEE_OUT 6 +#define SOUND_HISCORE 7 +#define SOUND_ITEM_DROP 8 +#define SOUND_ITEM_PICKUP 9 +#define SOUND_MENU_CANCEL 10 +#define SOUND_MENU_MOVE 11 +#define SOUND_MENU_SELECT 12 +#define SOUND_PLAYER_COLLISION 13 +#define SOUND_STAGE_CHANGE 14 +#define SOUND_TITLE 15 -const Uint8 TOTAL_SOUND = 16; +#define TOTAL_SOUND 16 -const Uint8 TEXTURE_BALLOON = 0; -const Uint8 TEXTURE_BULLET = 1; -const Uint8 TEXTURE_FONT_BLACK = 2; -const Uint8 TEXTURE_FONT_NOKIA = 3; -const Uint8 TEXTURE_FONT_WHITE = 4; -const Uint8 TEXTURE_GAME_BG = 5; -const Uint8 TEXTURE_GAME_TEXT = 6; -const Uint8 TEXTURE_INTRO = 7; -const Uint8 TEXTURE_ITEMS = 8; -const Uint8 TEXTURE_LOGO = 9; -const Uint8 TEXTURE_MENU = 10; -const Uint8 TEXTURE_PLAYER_BODY = 11; -const Uint8 TEXTURE_PLAYER_LEGS = 12; -const Uint8 TEXTURE_PLAYER_DEATH = 13; -const Uint8 TEXTURE_TITLE = 14; +#define TEXTURE_BALLOON 0 +#define TEXTURE_BULLET 1 +#define TEXTURE_FONT_BLACK 2 +#define TEXTURE_FONT_BLACK_X2 3 +#define TEXTURE_FONT_NOKIA 4 +#define TEXTURE_FONT_WHITE 5 +#define TEXTURE_FONT_WHITE_X2 6 +#define TEXTURE_GAME_BG 7 +#define TEXTURE_GAME_TEXT 8 +#define TEXTURE_INTRO 9 +#define TEXTURE_ITEMS 10 +#define TEXTURE_LOGO 11 +#define TEXTURE_MENU 12 +#define TEXTURE_PLAYER_BODY 13 +#define TEXTURE_PLAYER_DEATH 14 +#define TEXTURE_PLAYER_LEGS 15 +#define TEXTURE_TITLE 16 -const Uint8 TOTAL_TEXTURE = 15; +#define TOTAL_TEXTURE 17 // Tamaño de bloque -const Uint8 BLOCK = 8; -const Uint8 HALF_BLOCK = BLOCK / 2; +#define BLOCK 8 +#define HALF_BLOCK BLOCK / 2 // Tamaño de la pantalla real -const int SCREEN_WIDTH = 256; -const int SCREEN_HEIGHT = SCREEN_WIDTH * 3 / 4; // 192 +#define SCREEN_WIDTH 256 +const int SCREEN_HEIGHT = SCREEN_WIDTH * 3 / 4; // Tamaño de la pantalla que se muestra -const int VIEW_WIDTH = SCREEN_WIDTH * 3; // 768 -const int VIEW_HEIGHT = SCREEN_HEIGHT * 3; // 576 +const int VIEW_WIDTH = SCREEN_WIDTH * 3; +const int VIEW_HEIGHT = SCREEN_HEIGHT * 3; // Cantidad de enteros a escribir en los ficheros de datos -const Uint8 TOTAL_SCORE_DATA = 3; -const Uint16 TOTAL_DEMO_DATA = 2000; +#define TOTAL_SCORE_DATA 3 +#define TOTAL_DEMO_DATA 2000 // Zona de juego const int PLAY_AREA_TOP = (0 * BLOCK); @@ -82,7 +84,7 @@ const int PLAY_AREA_LEFT = (0 * BLOCK); const int PLAY_AREA_RIGHT = SCREEN_WIDTH - (0 * BLOCK); const int PLAY_AREA_WIDTH = PLAY_AREA_RIGHT - PLAY_AREA_LEFT; const int PLAY_AREA_HEIGHT = PLAY_AREA_BOTTOM - PLAY_AREA_TOP; -const int PLAY_AREA_CENTER_X = PLAY_AREA_LEFT + (PLAY_AREA_WIDTH / 2); +const int PLAY_AREA_CENTER_X = PLAY_AREA_LEFT + (PLAY_AREA_WIDTH / 2); const int PLAY_AREA_CENTER_Y = PLAY_AREA_TOP + (PLAY_AREA_HEIGHT / 2); const int PLAY_AREA_FIRST_QUARTER_Y = PLAY_AREA_HEIGHT / 4; const int PLAY_AREA_THIRD_QUARTER_Y = (PLAY_AREA_HEIGHT / 4) * 3; @@ -95,95 +97,92 @@ const int SCREEN_CENTER_Y = SCREEN_HEIGHT / 2; const int SCREEN_FIRST_QUARTER_Y = SCREEN_HEIGHT / 4; const int SCREEN_THIRD_QUARTER_Y = (SCREEN_HEIGHT / 4) * 3; -// Color transparente para los sprites -const Uint8 COLOR_KEY_R = 0xff; -const Uint8 COLOR_KEY_G = 0x00; -const Uint8 COLOR_KEY_B = 0xff; - // Opciones de menu -const int MENU_NO_OPTION = -1; -const int MENU_OPTION_START = 0; -const int MENU_OPTION_QUIT = 1; -const int MENU_OPTION_TOTAL = 2; - -// Selector de menu -const int MENU_SELECTOR_BLACK = (BLOCK * 0); -const int MENU_SELECTOR_WHITE = (BLOCK * 1); +#define MENU_NO_OPTION -1 +#define MENU_OPTION_START 0 +#define MENU_OPTION_QUIT 1 +#define MENU_OPTION_TOTAL 2 // Tipos de fondos para el menu -const int MENU_BACKGROUND_TRANSPARENT = 0; -const int MENU_BACKGROUND_SOLID = 1; +#define MENU_BACKGROUND_TRANSPARENT 0 +#define MENU_BACKGROUND_SOLID 1 // Estados del jugador -const Uint8 PLAYER_STATUS_WALKING_LEFT = 0; -const Uint8 PLAYER_STATUS_WALKING_RIGHT = 1; -const Uint8 PLAYER_STATUS_WALKING_STOP = 2; +#define PLAYER_STATUS_WALKING_LEFT 0 +#define PLAYER_STATUS_WALKING_RIGHT 1 +#define PLAYER_STATUS_WALKING_STOP 2 -const Uint8 PLAYER_STATUS_FIRING_UP = 0; -const Uint8 PLAYER_STATUS_FIRING_LEFT = 1; -const Uint8 PLAYER_STATUS_FIRING_RIGHT = 2; -const Uint8 PLAYER_STATUS_FIRING_NO = 3; +#define PLAYER_STATUS_FIRING_UP 0 +#define PLAYER_STATUS_FIRING_LEFT 1 +#define PLAYER_STATUS_FIRING_RIGHT 2 +#define PLAYER_STATUS_FIRING_NO 3 -const Uint8 PLAYER_ANIMATION_LEGS_WALKING_LEFT = 0; -const Uint8 PLAYER_ANIMATION_LEGS_WALKING_RIGHT = 1; -const Uint8 PLAYER_ANIMATION_LEGS_WALKING_STOP = 2; +#define PLAYER_ANIMATION_LEGS_WALKING_LEFT 0 +#define PLAYER_ANIMATION_LEGS_WALKING_RIGHT 1 +#define PLAYER_ANIMATION_LEGS_WALKING_STOP 2 -const Uint8 PLAYER_ANIMATION_BODY_WALKING_LEFT = 0; -const Uint8 PLAYER_ANIMATION_BODY_FIRING_LEFT = 1; -const Uint8 PLAYER_ANIMATION_BODY_WALKING_RIGHT = 2; -const Uint8 PLAYER_ANIMATION_BODY_FIRING_RIGHT = 3; -const Uint8 PLAYER_ANIMATION_BODY_WALKING_STOP = 4; -const Uint8 PLAYER_ANIMATION_BODY_FIRING_UP = 5; -const Uint8 PLAYER_ANIMATION_BODY_WALKING_LEFT_EXTRA_HIT = 6; -const Uint8 PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT = 7; -const Uint8 PLAYER_ANIMATION_BODY_WALKING_RIGHT_EXTRA_HIT = 8; -const Uint8 PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT = 9; -const Uint8 PLAYER_ANIMATION_BODY_WALKING_STOP_EXTRA_HIT = 10; -const Uint8 PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT = 11; +#define PLAYER_ANIMATION_BODY_WALKING_LEFT 0 +#define PLAYER_ANIMATION_BODY_FIRING_LEFT 1 +#define PLAYER_ANIMATION_BODY_WALKING_RIGHT 2 +#define PLAYER_ANIMATION_BODY_FIRING_RIGHT 3 +#define PLAYER_ANIMATION_BODY_WALKING_STOP 4 +#define PLAYER_ANIMATION_BODY_FIRING_UP 5 +#define PLAYER_ANIMATION_BODY_WALKING_LEFT_EXTRA_HIT 6 +#define PLAYER_ANIMATION_BODY_FIRING_LEFT_EXTRA_HIT 7 +#define PLAYER_ANIMATION_BODY_WALKING_RIGHT_EXTRA_HIT 8 +#define PLAYER_ANIMATION_BODY_FIRING_RIGHT_EXTRA_HIT 9 +#define PLAYER_ANIMATION_BODY_WALKING_STOP_EXTRA_HIT 10 +#define PLAYER_ANIMATION_BODY_FIRING_UP_EXTRA_HIT 11 // Variables del jugador -const Uint16 PLAYER_INVULNERABLE_TIMER = 200; +#define PLAYER_INVULNERABLE_COUNTER 200 -// Estados del juego -const Uint8 GAME_STATE_TITLE = 0; -const Uint8 GAME_STATE_PLAYING = 1; -const Uint8 GAME_STATE_QUIT = 2; -const Uint8 GAME_STATE_GAME_OVER_SCREEN = 3; -const Uint8 GAME_STATE_INTRO = 4; -const Uint8 GAME_STATE_DEMO = 5; -const Uint8 GAME_STATE_INSTRUCTIONS = 6; -const Uint8 GAME_STATE_LOGO = 7; -const Uint8 GAME_STATE_INIT = 8; +// Secciones del programa +#define PROG_SECTION_LOGO 0 +#define PROG_SECTION_INTRO 1 +#define PROG_SECTION_TITLE 2 +#define PROG_SECTION_GAME 3 + +// Secciones del juego +#define GAME_SECTION_PLAY 0 +#define GAME_SECTION_PAUSE 1 +#define GAME_SECTION_GAMEOVER 2 + +// Secciones del titulo +#define TITLE_SECTION_1 0 +#define TITLE_SECTION_2 1 +#define TITLE_SECTION_3 2 +#define TITLE_SECTION_INSTRUCTIONS 3 // Estados de cada elemento que pertenece a un evento -const Uint8 EVENT_WAITING = 1; -const Uint8 EVENT_RUNNING = 2; -const Uint8 EVENT_COMPLETED = 3; +#define EVENT_WAITING 1 +#define EVENT_RUNNING 2 +#define EVENT_COMPLETED 3 // Cantidad de eventos de la intro -const Uint8 INTRO_TOTAL_BITMAPS = 6; -const Uint8 INTRO_TOTAL_TEXTS = 9; -const Uint8 INTRO_TOTAL_EVENTS = INTRO_TOTAL_BITMAPS + INTRO_TOTAL_TEXTS; +#define INTRO_TOTAL_BITMAPS 6 +#define INTRO_TOTAL_TEXTS 9 +const int INTRO_TOTAL_EVENTS = INTRO_TOTAL_BITMAPS + INTRO_TOTAL_TEXTS; // Cantidad de eventos de la pantalla de titulo -const Uint8 TITLE_TOTAL_EVENTS = 2; +#define TITLE_TOTAL_EVENTS 2 // Relaciones de Id con nomnbres -const Uint8 BITMAP0 = 0; -const Uint8 BITMAP1 = 1; -const Uint8 BITMAP2 = 2; -const Uint8 BITMAP3 = 3; -const Uint8 BITMAP4 = 4; -const Uint8 BITMAP5 = 5; -const Uint8 TEXT0 = 6; -const Uint8 TEXT1 = 7; -const Uint8 TEXT2 = 8; -const Uint8 TEXT3 = 9; -const Uint8 TEXT4 = 10; -const Uint8 TEXT5 = 11; -const Uint8 TEXT6 = 12; -const Uint8 TEXT7 = 13; -const Uint8 TEXT8 = 14; +#define BITMAP0 0 +#define BITMAP1 1 +#define BITMAP2 2 +#define BITMAP3 3 +#define BITMAP4 4 +#define BITMAP5 5 +#define TEXT0 6 +#define TEXT1 7 +#define TEXT2 8 +#define TEXT3 9 +#define TEXT4 10 +#define TEXT5 11 +#define TEXT6 12 +#define TEXT7 13 +#define TEXT8 14 // Anclajes para el marcador de puntos const int SCORE_WORD_X = (SCREEN_WIDTH / 4) - ((5 * BLOCK) / 2); @@ -198,85 +197,88 @@ const int HISCORE_NUMBER_Y = SCREEN_HEIGHT - (2 * BLOCK) + 2; const int MULTIPLIER_WORD_X = (SCREEN_WIDTH / 2) - ((4 * BLOCK) / 2); const int MULTIPLIER_WORD_Y = SCREEN_HEIGHT - (3 * BLOCK) + 2; -const int MULTIPLIER_NUMBER_X = (SCREEN_WIDTH / 2) - ((3 * BLOCK) / 2);; +const int MULTIPLIER_NUMBER_X = (SCREEN_WIDTH / 2) - ((3 * BLOCK) / 2); const int MULTIPLIER_NUMBER_Y = SCREEN_HEIGHT - (2 * BLOCK) + 2; // Ningun tipo -const Uint8 NO_KIND = 0; +#define NO_KIND 0 // Tipos de globo -const Uint8 BALLOON_1 = 1; -const Uint8 BALLOON_2 = 2; -const Uint8 BALLOON_3 = 3; -const Uint8 BALLOON_4 = 4; +#define BALLOON_1 1 +#define BALLOON_2 2 +#define BALLOON_3 3 +#define BALLOON_4 4 // Velocidad del globo -const float BALLON_VELX_POSITIVE = 0.7f; -const float BALLON_VELX_NEGATIVE = -0.7f; +#define BALLOON_VELX_POSITIVE 0.7f +#define BALLOON_VELX_NEGATIVE -0.7f // Indice para las animaciones de los globos -const Uint8 BALLOON_MOVING_ANIMATION = 0; -const Uint8 BALLOON_POP_ANIMATION = 1; -const Uint8 BALLOON_BORN_ANIMATION = 2; +#define BALLOON_MOVING_ANIMATION 0 +#define BALLOON_POP_ANIMATION 1 +#define BALLOON_BORN_ANIMATION 2 // Cantidad posible de globos -const Uint8 MAX_BALLOONS = 75; +#define MAX_BALLOONS 75 // Tipos de bala -const Uint8 BULLET_UP = 1; -const Uint8 BULLET_LEFT = 2; -const Uint8 BULLET_RIGHT = 3; +#define BULLET_UP 1 +#define BULLET_LEFT 2 +#define BULLET_RIGHT 3 // Cantidad posible de globos -const Uint8 MAX_BULLETS = 50; +#define MAX_BULLETS 50 // Tipos de objetos -const Uint8 ITEM_POINTS_1_DISK = 1; -const Uint8 ITEM_POINTS_2_GAVINA = 2; -const Uint8 ITEM_POINTS_3_PACMAR = 3; -const Uint8 ITEM_CLOCK = 4; -const Uint8 ITEM_TNT = 5; -const Uint8 ITEM_COFFEE = 6; +#define ITEM_POINTS_1_DISK 1 +#define ITEM_POINTS_2_GAVINA 2 +#define ITEM_POINTS_3_PACMAR 3 +#define ITEM_CLOCK 4 +#define ITEM_TNT 5 +#define ITEM_COFFEE 6 // Cantidad de objetos simultaneos -const Uint8 MAX_ITEMS = 5; +#define MAX_ITEMS 5 // Valores para las variables asociadas a los objetos -const Uint8 REMAINING_EXPLOSIONS = 3; -const Uint8 REMAINING_EXPLOSIONS_TIMER = 50; -const Uint16 TIME_STOPPED_TIMER = 300; +#define REMAINING_EXPLOSIONS 3 +#define REMAINING_EXPLOSIONS_COUNTER 50 +#define TIME_STOPPED_COUNTER 300 // Estados de entrada -const Uint8 NO_INPUT = 0; -const Uint8 INPUT_UP = 1; -const Uint8 INPUT_DOWN = 2; -const Uint8 INPUT_LEFT = 3; -const Uint8 INPUT_RIGHT = 4; -const Uint8 INPUT_ACCEPT = 5; -const Uint8 INPUT_CANCEL = 6; -const Uint8 INPUT_FIRE_UP = 7; -const Uint8 INPUT_FIRE_LEFT = 8; -const Uint8 INPUT_FIRE_RIGHT = 9; -const Uint8 INPUT_PAUSE = 10; +#define NO_INPUT 0 +#define INPUT_UP 1 +#define INPUT_DOWN 2 +#define INPUT_LEFT 3 +#define INPUT_RIGHT 4 +#define INPUT_ACCEPT 5 +#define INPUT_CANCEL 6 +#define INPUT_FIRE_UP 7 +#define INPUT_FIRE_LEFT 8 +#define INPUT_FIRE_RIGHT 9 +#define INPUT_PAUSE 10 // Zona muerta del mando analógico -const int JOYSTICK_DEAD_ZONE = 8000; +#define JOYSTICK_DEAD_ZONE 8000 // Tipos de mensajes para el retorno de las funciones -const Uint8 MSG_OK = 0; -const Uint8 MSG_BULLET_OUT = 1; +#define MSG_OK 0 +#define MSG_BULLET_OUT 1 // Tipos de texto -const Uint8 TEXT_FIXED = 0; -const Uint8 TEXT_VARIABLE = 1; +#define TEXT_FIXED 0 +#define TEXT_VARIABLE 1 // Cantidad de elementos del vector de SmartSprites -const Uint8 MAX_SMART_SPRITES = 10; +#define MAX_SMART_SPRITES 10 + +// Cantidad máxima de gotas de café +#define MAX_COFFEE_DROPS 100 // Contadores -const Uint16 TITLE_TIMER = 800; -const Uint8 STAGE_COUNTER = 200; -const Uint16 INSTRUCTIONS_COUNTER = 600; -const Uint16 DEATH_COUNTER = 350; +#define TITLE_COUNTER 800 +#define STAGE_COUNTER 200 +#define INSTRUCTIONS_COUNTER 600 +#define DEATH_COUNTER 350 #endif \ No newline at end of file diff --git a/source/gamedirector.cpp b/source/gamedirector.cpp index 233a724..4e848e8 100644 --- a/source/gamedirector.cpp +++ b/source/gamedirector.cpp @@ -4,114 +4,59 @@ #include const Uint8 *keystates; - -// Calcula el cuadrado de la distancia entre dos puntos -double distanceSquared(int x1, int y1, int x2, int y2) -{ - int deltaX = x2 - x1; - int deltaY = y2 - y1; - return deltaX * deltaX + deltaY * deltaY; -} - -// Detector de colisiones entre dos circulos -bool checkCollision(Circle &a, Circle &b) -{ - // Calcula el radio total al cuadrado - int totalRadiusSquared = a.r + b.r; - totalRadiusSquared = totalRadiusSquared * totalRadiusSquared; - - // Si la distancia entre el centro de los circulos es inferior a la suma de sus radios - if (distanceSquared(a.x, a.y, b.x, b.y) < (totalRadiusSquared)) - { - // Los circulos han colisionado - return true; - } - - // En caso contrario - return false; -} +float mSin[360]; // Vector con los valores del seno para 360 grados // Constructor -GameDirector::GameDirector() +GameDirector::GameDirector(std::string path) { - // Texturas - mTexture[TEXTURE_BALLOON].texture = new LTexture(); - mTexture[TEXTURE_BULLET].texture = new LTexture(); - mTexture[TEXTURE_FONT_BLACK].texture = new LTexture(); - mTexture[TEXTURE_FONT_NOKIA].texture = new LTexture(); - mTexture[TEXTURE_FONT_WHITE].texture = new LTexture(); - mTexture[TEXTURE_GAME_BG].texture = new LTexture(); - mTexture[TEXTURE_GAME_TEXT].texture = new LTexture(); - mTexture[TEXTURE_INTRO].texture = new LTexture(); - mTexture[TEXTURE_ITEMS].texture = new LTexture(); - mTexture[TEXTURE_LOGO].texture = new LTexture(); - mTexture[TEXTURE_MENU].texture = new LTexture(); - mTexture[TEXTURE_PLAYER_BODY].texture = new LTexture(); - mTexture[TEXTURE_PLAYER_LEGS].texture = new LTexture(); - mTexture[TEXTURE_PLAYER_DEATH].texture = new LTexture(); - mTexture[TEXTURE_TITLE].texture = new LTexture(); - - // Manejador de eventos + // Crea los objetos mEventHandler = new SDL_Event(); - // Objetos para el juego - mPlayer = new Player(); - for (Uint8 i = 0; i < MAX_BALLOONS; i++) - { - mBalloon[i] = new Balloon(); - } - for (Uint8 i = 0; i < MAX_BULLETS; i++) - { - mBullet[i] = new Bullet(); - } - for (Uint8 i = 0; i < MAX_ITEMS; i++) - { - mItem[i] = new Item(); - } - for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++) - { - mSmartSprite[i] = new SmartSprite(); - } - mGameBackgroundFront = new Sprite(); - mGameBackgroundSky = new Sprite(); - mGBClouds1 = new MovingSprite(); - mGBClouds1b = new MovingSprite(); - mGBClouds2 = new MovingSprite(); - mGBClouds2b = new MovingSprite(); - mGrass = new Sprite(); - mTitleTile = new Sprite(); - mText.white = new Text(); - mText.black = new Text(); - mText.nokia = new Text(); - mMenu.title = new Menu(); - mMenu.pause = new Menu(); + mTexture[TEXTURE_MENU].texture = new LTexture(); + mTexture[TEXTURE_FONT_WHITE].texture = new LTexture(); + mTexture[TEXTURE_FONT_WHITE_X2].texture = new LTexture(); + mTexture[TEXTURE_FONT_BLACK].texture = new LTexture(); + mTexture[TEXTURE_FONT_BLACK_X2].texture = new LTexture(); + mTexture[TEXTURE_FONT_NOKIA].texture = new LTexture(); + mMenu.gameOver = new Menu(); mMenu.options = new Menu(); - mMenu.active = new Menu(); - mGetReadyBitmap = new SmartSprite(); - mCoffeeBitmap = new SmartSprite(); - mCrisisBitmap = new SmartSprite(); - mDustSpriteLeft = new AnimatedSprite(); - mDustSpriteRight = new AnimatedSprite(); - m1000Bitmap = new SmartSprite(); - m2500Bitmap = new SmartSprite(); - m5000Bitmap = new SmartSprite(); - mInstructions = new Sprite(); - for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) - { - mIntroBitmap[i] = new SmartSprite(); - } - for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) - { - mIntroText[i] = new Text2(); - } - mLogo.sprite = new Sprite(); + mMenu.pause = new Menu(); + mMenu.title = new Menu(); + + mText.black = new Text(); + mText.blackX2 = new Text(); + mText.nokia = new Text(); + mText.white = new Text(); + mText.whiteX2 = new Text(); + + // Inicializa variables + setExecutablePath(path); + setFileList(); + checkFileList(); + + // Carga recursos + loadMediaProg(); + loadScoreFile(); + loadConfigFile(); + loadDemoFile(); + + // Inicializa JailAudio + initJailAudio(); + + // Inicializa SDL + initSDL(); + + // Inicializa el resto de variables + initProg(); } GameDirector::~GameDirector() { - // Libera los recursos globales - unLoadMedia(GAME_STATE_INIT); + quitProg(); + + // Destuye el manejador de eventos + delete mEventHandler; // Libera el mando if (mGameControllerFound) @@ -120,559 +65,196 @@ GameDirector::~GameDirector() mGameController = NULL; } - // Destuye el manejador de eventos - delete mEventHandler; - // Destruye los objetos - delete mTexture[TEXTURE_BALLOON].texture; - delete mTexture[TEXTURE_BULLET].texture; - delete mTexture[TEXTURE_FONT_BLACK].texture; - delete mTexture[TEXTURE_FONT_NOKIA].texture; - delete mTexture[TEXTURE_FONT_WHITE].texture; - delete mTexture[TEXTURE_GAME_BG].texture; - delete mTexture[TEXTURE_GAME_TEXT].texture; - delete mTexture[TEXTURE_INTRO].texture; - delete mTexture[TEXTURE_ITEMS].texture; - delete mTexture[TEXTURE_LOGO].texture; delete mTexture[TEXTURE_MENU].texture; - delete mTexture[TEXTURE_PLAYER_BODY].texture; - delete mTexture[TEXTURE_PLAYER_LEGS].texture; - delete mTexture[TEXTURE_PLAYER_DEATH].texture; - delete mTexture[TEXTURE_TITLE].texture; + delete mTexture[TEXTURE_FONT_WHITE].texture; + delete mTexture[TEXTURE_FONT_WHITE_X2].texture; + delete mTexture[TEXTURE_FONT_BLACK].texture; + delete mTexture[TEXTURE_FONT_BLACK_X2].texture; + delete mTexture[TEXTURE_FONT_NOKIA].texture; - delete mPlayer; - for (Uint8 i = 0; i < MAX_BALLOONS; i++) - { - delete mBalloon[i]; - } - for (Uint8 i = 0; i < MAX_BULLETS; i++) - { - delete mBullet[i]; - } - for (Uint8 i = 0; i < MAX_ITEMS; i++) - { - delete mItem[i]; - } - for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++) - { - delete mSmartSprite[i]; - } - delete mGameBackgroundFront; - delete mGameBackgroundSky; - delete mGBClouds1; - delete mGBClouds1b; - delete mGBClouds2; - delete mGBClouds2b; - delete mGrass; - delete mTitleTile; - delete mText.white; - delete mText.black; - delete mText.nokia; - delete mMenu.title; - delete mMenu.pause; delete mMenu.gameOver; delete mMenu.options; + delete mMenu.pause; + delete mMenu.title; mMenu.active = nullptr; - delete mGetReadyBitmap; - delete mCoffeeBitmap; - delete mCrisisBitmap; - delete mDustSpriteLeft; - delete mDustSpriteRight; - delete m1000Bitmap; - delete m2500Bitmap; - delete m5000Bitmap; - delete mInstructions; - for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) - { - delete mIntroBitmap[i]; - } - for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) - { - delete mIntroText[i]; - } - delete mLogo.sprite; - // Destruye la ventana + delete mText.black; + delete mText.blackX2; + delete mText.nokia; + delete mText.white; + delete mText.whiteX2; + SDL_DestroyTexture(mBackbuffer); - SDL_DestroyTexture(mTitleSurface); - SDL_DestroyTexture(mInstructionsSurface); SDL_DestroyRenderer(mRenderer); SDL_DestroyWindow(mWindow); mBackbuffer = nullptr; - mTitleSurface = nullptr; - mInstructionsSurface = nullptr; - mWindow = nullptr; mRenderer = nullptr; + mWindow = nullptr; - // Sal del subsistema SDL SDL_Quit(); } -// Iniciador -void GameDirector::init(bool reset) +// Inicia las variables necesarias para arrancar el programa +void GameDirector::initProg() { - if (true) - { - // Variables - mTicks = 0; - mTicksSpeed = 15; - mMenaceLevel = 0; - mMenaceLevelThreshold = 0; - mGame.score = 0; - mGame.hiScoreAchieved = false; - mGame.stage = 1; - mGame.stageCounter = 0; - mGame.paused = false; - mGame.deathCounter = DEATH_COUNTER; - for (Uint8 i = 0; i < STAGE_COUNTER; i++) - { - mGame.stagePath[i] = (sin(double(i * 180.0 / double(STAGE_COUNTER)) * 3.14 / 180.0) * (BLOCK * 4)) - BLOCK; - } - mPlayFieldDrawOnly = true; - mDebug = false; - mTitleStatus = 0; - mTitleTimer = TITLE_TIMER; - mExplosionTime = false; - mRemainingExplosions = REMAINING_EXPLOSIONS; - mRemainingExplosionsTimer = REMAINING_EXPLOSIONS_TIMER; - mTimeStopped = false; - mTimeStoppedTimer = TIME_STOPPED_TIMER; - mInstructionsCounter = INSTRUCTIONS_COUNTER; - mDemo = false; - mDemoRecording = false; - mDemoCounter = 0; - mGameCounter = 0; - mMenuKeyPressed = false; - mMenu.active = mMenu.title; - for (int i = 0; i < 360; i++) - { - mSen[i] = sin(i * 3.14 / 180); - } - mTitleBackgroundTimer = 0; - mTitleBackgroundMode = rand() % 2; - mTitleMenuVisible = false; - mLogo.counter = 0; + // Variables + mProg.quit = false; + mProg.debug = false; + mProg.ticks = 0; + mProg.ticksSpeed = 15; + mProg.section = PROG_SECTION_LOGO; + initSin(); + initPaths(); - // Fondo animado - mGBClouds1->init(0, 0, 256, 52, -0.1f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); - mGBClouds1->setSpriteClip(256, 0, 256, 52); + mOptions.displayCoffeeDrops = false; - mGBClouds1b->init(256, 0, 256, 52, -0.1f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); - mGBClouds1b->setSpriteClip(256, 0, 256, 52); + mDemo.enabled = false; + mDemo.recording = false; + mDemo.counter = 0; - mGBClouds2->init(0, 52, 256, 32, -0.05f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); - mGBClouds2->setSpriteClip(256, 52, 256, 32); + mMenu.keyPressed = false; + mMenu.active = mMenu.title; - mGBClouds2b->init(256, 52, 256, 32, -0.05f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); - mGBClouds2b->setSpriteClip(256, 52, 256, 32); + // Teclado + mProg.keyboard.up = SDL_SCANCODE_UP; + mProg.keyboard.down = SDL_SCANCODE_DOWN; + mProg.keyboard.left = SDL_SCANCODE_LEFT; + mProg.keyboard.right = SDL_SCANCODE_RIGHT; + mProg.keyboard.accept = SDL_SCANCODE_RETURN; + mProg.keyboard.cancel = SDL_SCANCODE_ESCAPE; + mProg.keyboard.fire = SDL_SCANCODE_W; + mProg.keyboard.fireLeft = SDL_SCANCODE_Q; + mProg.keyboard.fireRight = SDL_SCANCODE_E; + mProg.keyboard.pause = SDL_SCANCODE_ESCAPE; + mProg.keyboard.escape = SDL_SCANCODE_ESCAPE; - mGrass->init(0, 85, SCREEN_WIDTH, 6, mTexture[TEXTURE_GAME_BG].texture, mRenderer); - mGrass->setPosY(154); + // Buffer de teclado + mProg.keyboardBuffer.up = 0; + mProg.keyboardBuffer.down = 0; + mProg.keyboardBuffer.left = 0; + mProg.keyboardBuffer.right = 0; + mProg.keyboardBuffer.accept = 0; + mProg.keyboardBuffer.cancel = 0; + mProg.keyboardBuffer.fire = 0; + mProg.keyboardBuffer.fireLeft = 0; + mProg.keyboardBuffer.fireRight = 0; + mProg.keyboardBuffer.pause = 0; + mProg.keyboardBuffer.escape = 0; - // Objeto jugador - mPlayer->init(PLAY_AREA_CENTER_X - 12, PLAY_AREA_BOTTOM - 24, mTexture[TEXTURE_PLAYER_LEGS].texture, mTexture[TEXTURE_PLAYER_BODY].texture, mRenderer); + // Inicializa los objetos de texto + mText.white->init(mTexture[TEXTURE_FONT_WHITE].texture, mRenderer, TEXT_FIXED, BLOCK); + mText.whiteX2->init(mTexture[TEXTURE_FONT_WHITE_X2].texture, mRenderer, TEXT_FIXED, BLOCK * 2); + mText.black->init(mTexture[TEXTURE_FONT_BLACK].texture, mRenderer, TEXT_FIXED, BLOCK); + mText.blackX2->init(mTexture[TEXTURE_FONT_BLACK_X2].texture, mRenderer, TEXT_FIXED, BLOCK * 2); + mText.nokia->init(mTexture[TEXTURE_FONT_NOKIA].texture, mRenderer, TEXT_VARIABLE, 10); - // Establece a cero todos los valores del vector de objetos globo - resetBalloons(); + // Inicializa el objeto con el menu del titulo + mMenu.title->init("TITLE", 0, 15 * BLOCK, MENU_BACKGROUND_SOLID, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); + mMenu.title->addItem("PLAY"); + mMenu.title->addItem("OPTIONS", 0, 5); + mMenu.title->addItem("QUIT"); + mMenu.title->setDefaultActionWhenCancel(2); + mMenu.title->setBackgroundColor(0x30, 0x30, 0x40, 192); + mMenu.title->setSelectorColor(0xe5, 0x1c, 0x23, 255); + mMenu.title->setSelectorTextColor(0xFF, 0xF1, 0x76); + mMenu.title->centerMenu(SCREEN_CENTER_X); + mMenu.title->centerMenuElements(); - // Crea objetos globo y los centra en el area de juego - mBalloon[0]->init(0, BLOCK, BALLOON_4, BALLON_VELX_POSITIVE, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); - mBalloon[0]->allignTo(PLAY_AREA_CENTER_X); + // Inicializa el objeto con el menu de pausa + mMenu.pause->init("PAUSE", 0, 12 * BLOCK, MENU_BACKGROUND_SOLID, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); + mMenu.pause->addItem("CONTINUE"); + mMenu.pause->addItem("EXIT TO TITLE"); + mMenu.pause->setDefaultActionWhenCancel(0); + mMenu.pause->setBackgroundColor(0x29, 0x39, 0x41, 240); + mMenu.pause->setSelectorColor(0xFF, 0x7A, 0x00, 255); + mMenu.pause->setSelectorTextColor(0xFF, 0xFF, 0xFF); + mMenu.pause->centerMenu(SCREEN_CENTER_X); + mMenu.pause->centerMenuElements(); - // Con los globos creados, calcula el nivel de amenaza - calculateMenaceLevel(); + // Inicializa el objeto con el menu de la pantalla de game over + mMenu.gameOver->init("GAME OVER", 0, PLAY_AREA_CENTER_Y + BLOCK * 4, MENU_BACKGROUND_TRANSPARENT, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); + mMenu.gameOver->addItem("YES"); + mMenu.gameOver->addItem("NO"); + mMenu.gameOver->setDefaultActionWhenCancel(1); + mMenu.gameOver->setBackgroundColor(0, 0, 0, 255); + mMenu.gameOver->setSelectorColor(0x54, 0x6e, 0x7a, 255); + mMenu.gameOver->setSelectorColor(0x54, 0x6e, 0x7a, 0); + mMenu.gameOver->setSelectorTextColor(0xFF, 0xFF, 0xFF); + mMenu.gameOver->setSelectorTextColor(0xFF, 0xF1, 0x76); + mMenu.gameOver->centerMenu(SCREEN_CENTER_X); + mMenu.gameOver->centerMenuElements(); - // Establece a cero todos los valores del vector de objetos bala - resetBullets(); + // Inicializa el objeto con el menu de las opciones + mMenu.options->init("OPTIONS", 0, 15 * BLOCK, MENU_BACKGROUND_SOLID, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); + mMenu.options->addItem("FULLSCREEN"); + mMenu.options->addItem("WINDOWS SIZE", 0, 5); + mMenu.options->addItem("[OK]"); + mMenu.options->addItem("[CANCEL]"); + mMenu.options->setDefaultActionWhenCancel(3); + mMenu.options->setBackgroundColor(0x30, 0x30, 0x40, 192); + mMenu.options->setSelectorColor(0xe5, 0x1c, 0x23, 255); + mMenu.options->setSelectorTextColor(0xFF, 0xF1, 0x76); + mMenu.options->centerMenu(SCREEN_CENTER_X); + mMenu.options->centerMenuElements(); - // Establece a cero todos los valores del vector de objetos item - resetItems(); + // Actualiza los elementos del menu de opciones con los valores correspondientes + updateOptionsMenu(); +} - // Establece a cero todos los valores del vector de objetos SmafrtSprite - resetSmartSprites(); +// Carga los recursos necesarios para el programa +bool GameDirector::loadMediaProg() +{ + bool success = true; - // Inicializa el sprite del logo - mLogo.sprite->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_LOGO].texture, mRenderer); + // Texturas + success &= loadTextureFromFile(mTexture[TEXTURE_MENU].texture, mTexture[TEXTURE_MENU].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_FONT_WHITE].texture, mTexture[TEXTURE_FONT_WHITE].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_FONT_WHITE_X2].texture, mTexture[TEXTURE_FONT_WHITE_X2].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_FONT_BLACK].texture, mTexture[TEXTURE_FONT_BLACK].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_FONT_BLACK_X2].texture, mTexture[TEXTURE_FONT_BLACK_X2].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_FONT_NOKIA].texture, mTexture[TEXTURE_FONT_NOKIA].file, mRenderer); - // Inicializa el bitmap de GetReady! - mGetReadyBitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); - mGetReadyBitmap->setPosX(-113); - mGetReadyBitmap->setPosY(PLAY_AREA_CENTER_Y - 10); - mGetReadyBitmap->setWidth(109); - mGetReadyBitmap->setHeight(20); - mGetReadyBitmap->setVelX(2.0f); - mGetReadyBitmap->setVelY(0.0f); - mGetReadyBitmap->setAccelX(0.00f); - mGetReadyBitmap->setAccelY(0.0f); - mGetReadyBitmap->setSpriteClip(0, 0, 109, 20); - mGetReadyBitmap->setEnabled(true); - mGetReadyBitmap->setEnabledTimer(0); - mGetReadyBitmap->setDestX(PLAY_AREA_RIGHT); - mGetReadyBitmap->setDestY(PLAY_AREA_CENTER_Y - 10); + // Sonidos + mSound[SOUND_MENU_SELECT].sound = JA_LoadSound(mSound[SOUND_MENU_SELECT].file.c_str()); + mSound[SOUND_MENU_CANCEL].sound = JA_LoadSound(mSound[SOUND_MENU_CANCEL].file.c_str()); + mSound[SOUND_MENU_MOVE].sound = JA_LoadSound(mSound[SOUND_MENU_MOVE].file.c_str()); - // Inicializa el bitmap de Coffee - mCoffeeBitmap->init(mTexture[TEXTURE_TITLE].texture, mRenderer); - mCoffeeBitmap->setId(0); - mCoffeeBitmap->setIntroEvents(&mTitleEvents[0]); - mCoffeeBitmap->setPosX(45); - mCoffeeBitmap->setPosY(11 - 200); - mCoffeeBitmap->setWidth(167); - mCoffeeBitmap->setHeight(46); - mCoffeeBitmap->setVelX(0.0f); - mCoffeeBitmap->setVelY(2.5f); - mCoffeeBitmap->setAccelX(0.0f); - mCoffeeBitmap->setAccelY(0.1f); - //mCoffeeBitmap->setSpriteClip(45, 11, 167, 46); - mCoffeeBitmap->setSpriteClip(0, 0, 167, 46); - mCoffeeBitmap->setEnabled(true); - mCoffeeBitmap->setEnabledTimer(0); - mCoffeeBitmap->setDestX(45); - mCoffeeBitmap->setDestY(11); + // Musicas + mMusic[MUSIC_TITLE].music = JA_LoadMusic(mMusic[MUSIC_TITLE].file.c_str()); - // Inicializa el bitmap de Crisis - mCrisisBitmap->init(mTexture[TEXTURE_TITLE].texture, mRenderer); - mCrisisBitmap->setId(1); - mCrisisBitmap->setIntroEvents(&mTitleEvents[0]); - mCrisisBitmap->setPosX(60); - mCrisisBitmap->setPosY(57 + 200); - mCrisisBitmap->setWidth(137); - mCrisisBitmap->setHeight(46); - mCrisisBitmap->setVelX(0.0f); - mCrisisBitmap->setVelY(-2.5f); - mCrisisBitmap->setAccelX(0.0f); - mCrisisBitmap->setAccelY(-0.1f); - //mCrisisBitmap->setSpriteClip(60, 57, 137, 46); - mCrisisBitmap->setSpriteClip(0, 46, 137, 46); - mCrisisBitmap->setEnabled(true); - mCrisisBitmap->setEnabledTimer(0); - mCrisisBitmap->setDestX(60); - mCrisisBitmap->setDestY(57); + return success; +} - // Inicializa el bitmap de DustRight - mDustSpriteRight->init(mTexture[TEXTURE_TITLE].texture, mRenderer); - mDustSpriteRight->setPosX(218); - mDustSpriteRight->setPosY(47); - mDustSpriteRight->setWidth(16); - mDustSpriteRight->setHeight(14); - mDustSpriteRight->setCurrentFrame(0); - mDustSpriteRight->setAnimationCounter(0); - mDustSpriteRight->setAnimationNumFrames(0, 7); - mDustSpriteRight->setAnimationSpeed(0, 8); - mDustSpriteRight->setAnimationLoop(0, false); - mDustSpriteRight->setAnimationFrames(0, 0, 160 + (mDustSpriteRight->getWidth() * 0), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); - mDustSpriteRight->setAnimationFrames(0, 1, 160 + (mDustSpriteRight->getWidth() * 1), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); - mDustSpriteRight->setAnimationFrames(0, 2, 160 + (mDustSpriteRight->getWidth() * 2), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); - mDustSpriteRight->setAnimationFrames(0, 3, 160 + (mDustSpriteRight->getWidth() * 3), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); - mDustSpriteRight->setAnimationFrames(0, 4, 160 + (mDustSpriteRight->getWidth() * 4), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); - mDustSpriteRight->setAnimationFrames(0, 5, 160 + (mDustSpriteRight->getWidth() * 5), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); - mDustSpriteRight->setAnimationFrames(0, 6, 160 + (mDustSpriteRight->getWidth() * 6), 80, mDustSpriteRight->getWidth(), mDustSpriteRight->getHeight()); +// Libera las variables del programa +void GameDirector::quitProg() +{ + // Guarda los ficheros + saveScoreFile(); + saveConfigFile(); + saveDemoFile(); - // Inicializa el bitmap de DustLeft - mDustSpriteLeft->init(mTexture[TEXTURE_TITLE].texture, mRenderer); - mDustSpriteLeft->setPosX(33); - mDustSpriteLeft->setPosY(47); - mDustSpriteLeft->setWidth(16); - mDustSpriteLeft->setHeight(14); - mDustSpriteLeft->setCurrentFrame(0); - mDustSpriteLeft->setAnimationCounter(0); - mDustSpriteLeft->setAnimationNumFrames(0, 7); - mDustSpriteLeft->setAnimationSpeed(0, 8); - mDustSpriteLeft->setAnimationLoop(0, false); - mDustSpriteLeft->setAnimationFrames(0, 0, 160 + (mDustSpriteLeft->getWidth() * 0), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); - mDustSpriteLeft->setAnimationFrames(0, 1, 160 + (mDustSpriteLeft->getWidth() * 1), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); - mDustSpriteLeft->setAnimationFrames(0, 2, 160 + (mDustSpriteLeft->getWidth() * 2), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); - mDustSpriteLeft->setAnimationFrames(0, 3, 160 + (mDustSpriteLeft->getWidth() * 3), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); - mDustSpriteLeft->setAnimationFrames(0, 4, 160 + (mDustSpriteLeft->getWidth() * 4), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); - mDustSpriteLeft->setAnimationFrames(0, 5, 160 + (mDustSpriteLeft->getWidth() * 5), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); - mDustSpriteLeft->setAnimationFrames(0, 6, 160 + (mDustSpriteLeft->getWidth() * 6), 66, mDustSpriteLeft->getWidth(), mDustSpriteLeft->getHeight()); + // Texturas + mTexture[TEXTURE_MENU].texture->unload(); + mTexture[TEXTURE_FONT_WHITE].texture->unload(); + mTexture[TEXTURE_FONT_WHITE_X2].texture->unload(); + mTexture[TEXTURE_FONT_BLACK].texture->unload(); + mTexture[TEXTURE_FONT_BLACK_X2].texture->unload(); + mTexture[TEXTURE_FONT_NOKIA].texture->unload(); - // Inicializa el bitmap de 1000 puntos - m1000Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); - m1000Bitmap->setPosX(0); - m1000Bitmap->setPosY(0); - m1000Bitmap->setWidth(26); - m1000Bitmap->setHeight(9); - m1000Bitmap->setVelX(0.0f); - m1000Bitmap->setVelY(-0.5f); - m1000Bitmap->setAccelX(0.0f); - m1000Bitmap->setAccelY(-0.1f); - m1000Bitmap->setSpriteClip(0, 20, 26, 9); - m1000Bitmap->setEnabled(false); - m1000Bitmap->setEnabledTimer(0); - m1000Bitmap->setDestX(0); - m1000Bitmap->setDestY(0); + // Sonidos + JA_DeleteSound(mSound[SOUND_MENU_SELECT].sound); + JA_DeleteSound(mSound[SOUND_MENU_CANCEL].sound); + JA_DeleteSound(mSound[SOUND_MENU_MOVE].sound); - // Inicializa el bitmap de 2500 puntos - m2500Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); - m2500Bitmap->setPosX(0); - m2500Bitmap->setPosY(0); - m2500Bitmap->setWidth(28); - m2500Bitmap->setHeight(9); - m2500Bitmap->setVelX(0.0f); - m2500Bitmap->setVelY(-0.5f); - m2500Bitmap->setAccelX(0.0f); - m2500Bitmap->setAccelY(-0.1f); - m2500Bitmap->setSpriteClip(26, 20, 28, 9); - m2500Bitmap->setEnabled(false); - m2500Bitmap->setEnabledTimer(0); - m2500Bitmap->setDestX(0); - m2500Bitmap->setDestY(0); + // Musicas + JA_DeleteMusic(mMusic[MUSIC_TITLE].music); +} - // Inicializa el bitmap de 5000 puntos - m5000Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); - m5000Bitmap->setPosX(0); - m5000Bitmap->setPosY(0); - m5000Bitmap->setWidth(28); - m5000Bitmap->setHeight(9); - m5000Bitmap->setVelX(0.0f); - m5000Bitmap->setVelY(-0.5f); - m5000Bitmap->setAccelX(0.0f); - m5000Bitmap->setAccelY(-0.1f); - m5000Bitmap->setSpriteClip(54, 20, 28, 9); - m5000Bitmap->setEnabled(false); - m5000Bitmap->setEnabledTimer(0); - m5000Bitmap->setDestX(0); - m5000Bitmap->setDestY(0); - - // Inicializa el vector de eventos de la intro - for (Uint8 i = 0; i < INTRO_TOTAL_EVENTS; i++) - { - mIntroEvents[i] = EVENT_WAITING; - } - - // Inicializa el vector de eventos de la pantalla de titulo - for (Uint8 i = 0; i < TITLE_TOTAL_EVENTS; i++) - { - mTitleEvents[i] = EVENT_WAITING; - } - - // Inicializa los bitmaps de la intro - for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) - { - mIntroBitmap[i]->init(mTexture[TEXTURE_INTRO].texture, mRenderer); - mIntroBitmap[i]->setId(i); - mIntroBitmap[i]->setIntroEvents(&mIntroEvents[0]); - mIntroBitmap[i]->setWidth(128); - mIntroBitmap[i]->setHeight(96); - mIntroBitmap[i]->setEnabled(false); - mIntroBitmap[i]->setEnabledTimer(20); - mIntroBitmap[i]->setDestX(SCREEN_CENTER_X - 64); - mIntroBitmap[i]->setDestY(SCREEN_FIRST_QUARTER_Y - 24); - } - - mIntroBitmap[0]->setPosX(-128); - mIntroBitmap[0]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); - mIntroBitmap[0]->setVelX(0.0f); - mIntroBitmap[0]->setVelY(0.0f); - mIntroBitmap[0]->setAccelX(0.6f); - mIntroBitmap[0]->setAccelY(0.0f); - mIntroBitmap[0]->setSpriteClip(0, 0, 128, 96); - - mIntroBitmap[1]->setPosX(SCREEN_WIDTH); - mIntroBitmap[1]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); - mIntroBitmap[1]->setVelX(-1.0f); - mIntroBitmap[1]->setVelY(0.0f); - mIntroBitmap[1]->setAccelX(-0.3f); - mIntroBitmap[1]->setAccelY(0.0f); - mIntroBitmap[1]->setSpriteClip(128, 0, 128, 96); - - mIntroBitmap[2]->setPosX(SCREEN_CENTER_X - 64); - mIntroBitmap[2]->setPosY(-96); - mIntroBitmap[2]->setVelX(0.0f); - mIntroBitmap[2]->setVelY(3.0f); - mIntroBitmap[2]->setAccelX(0.1f); - mIntroBitmap[2]->setAccelY(0.3f); - mIntroBitmap[2]->setSpriteClip(0, 96, 128, 96); - - mIntroBitmap[2]->setEnabledTimer(250); - - mIntroBitmap[3]->setPosX(SCREEN_CENTER_X - 64); - mIntroBitmap[3]->setPosY(SCREEN_HEIGHT); - mIntroBitmap[3]->setVelX(0.0f); - mIntroBitmap[3]->setVelY(-0.7f); - mIntroBitmap[3]->setAccelX(0.0f); - mIntroBitmap[3]->setAccelY(0.0f); - mIntroBitmap[3]->setSpriteClip(128, 96, 128, 96); - - mIntroBitmap[4]->setPosX(SCREEN_CENTER_X - 64); - mIntroBitmap[4]->setPosY(-96); - mIntroBitmap[4]->setVelX(0.0f); - mIntroBitmap[4]->setVelY(3.0f); - mIntroBitmap[4]->setAccelX(0.1f); - mIntroBitmap[4]->setAccelY(0.3f); - mIntroBitmap[4]->setSpriteClip(0, 192, 128, 96); - - mIntroBitmap[5]->setPosX(SCREEN_WIDTH); - mIntroBitmap[5]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); - mIntroBitmap[5]->setVelX(-0.7f); - mIntroBitmap[5]->setVelY(0.0f); - mIntroBitmap[5]->setAccelX(0.0f); - mIntroBitmap[5]->setAccelY(0.0f); - mIntroBitmap[5]->setSpriteClip(128, 192, 128, 96); - - // Inicializa los textos de la intro - for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) - { - mIntroText[i]->init(mTexture[TEXTURE_FONT_NOKIA].texture, mRenderer, TEXT_VARIABLE, 10); - mIntroText[i]->setId(6 + i); - mIntroText[i]->setIntroEvents(&mIntroEvents[0]); - mIntroText[i]->setPosX(BLOCK * 0); - mIntroText[i]->setPosY(SCREEN_HEIGHT - (BLOCK * 6)); - mIntroText[i]->setKerning(-1); - mIntroText[i]->setEnabled(false); - mIntroText[i]->setEnabledTimer(180); - } - - mIntroText[0]->setCaption("Un dia qualsevol de l'any 2000"); - mIntroText[0]->setWrittingSpeed(10); - - mIntroText[1]->setCaption("Tot esta tranquil a la UPV"); - mIntroText[1]->setWrittingSpeed(10); - - mIntroText[2]->setCaption("Fins que un desaprensiu..."); - mIntroText[2]->setWrittingSpeed(15); - - mIntroText[3]->setCaption("HEY! ME ANE A FERME UN CORTAET..."); - mIntroText[3]->setWrittingSpeed(10); - - mIntroText[4]->setCaption("UAAAAAAAAAAAAA!!!"); - mIntroText[4]->setWrittingSpeed(1); - - mIntroText[5]->setCaption("Espera un moment..."); - mIntroText[5]->setWrittingSpeed(20); - - mIntroText[6]->setCaption("Si resulta que no tinc solt!"); - mIntroText[6]->setWrittingSpeed(2); - - mIntroText[7]->setCaption("MERDA DE MAQUINA!"); - mIntroText[7]->setWrittingSpeed(3); - - mIntroText[8]->setCaption("Blop... blop... blop..."); - mIntroText[8]->setWrittingSpeed(20); - - for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) - { - mIntroText[i]->center(SCREEN_CENTER_X); - } - } - - // Esta parte solo se ejecuta al arrancar la aplicación - if (reset == false) - { - // Variables - mGame.status = GAME_STATE_LOGO; - mTitleStatus = 0; - mTiteNextGS = GAME_STATE_PLAYING; - - // Teclado - mKeyboard.up = SDL_SCANCODE_UP; - mKeyboard.down = SDL_SCANCODE_DOWN; - mKeyboard.left = SDL_SCANCODE_LEFT; - mKeyboard.right = SDL_SCANCODE_RIGHT; - mKeyboard.accept = SDL_SCANCODE_RETURN; - mKeyboard.cancel = SDL_SCANCODE_ESCAPE; - mKeyboard.fire = SDL_SCANCODE_W; - mKeyboard.fireLeft = SDL_SCANCODE_Q; - mKeyboard.fireRight = SDL_SCANCODE_E; - mKeyboard.pause = SDL_SCANCODE_ESCAPE; - mKeyboard.escape = SDL_SCANCODE_ESCAPE; - - // Buffer de teclado - mKeyboardBuffer.up = 0; - mKeyboardBuffer.down = 0; - mKeyboardBuffer.left = 0; - mKeyboardBuffer.right = 0; - mKeyboardBuffer.accept = 0; - mKeyboardBuffer.cancel = 0; - mKeyboardBuffer.fire = 0; - mKeyboardBuffer.fireLeft = 0; - mKeyboardBuffer.fireRight = 0; - mKeyboardBuffer.pause = 0; - mKeyboardBuffer.escape = 0; - - // Carga la máxima puntuación a partir del fichero de datos - if (mScoreDataFile[0] == 0) - { - mGame.hiScore = 10000; - } - // Comprueba el checksum para ver si se ha modificado el fichero - else if (mScoreDataFile[0] % 43 == mScoreDataFile[1]) - { - mGame.hiScore = mScoreDataFile[0]; - } - else - { - mGame.hiScore = 10000; - } - - // Los fondos - mGameBackgroundFront->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_GAME_BG].texture, mRenderer); - mGameBackgroundSky->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_GAME_BG].texture, mRenderer); - - // El tile de fondo - mTitleTile->init(0, 0, 64, 64, mTexture[TEXTURE_TITLE].texture, mRenderer); - mTitleTile->setSpriteClip(192, 0, 64, 64); - - // Las instrucciones - //mInstructions->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture.instructions, mRenderer); - - // Objetos texto, uno de cada color - mText.white->init(mTexture[TEXTURE_FONT_WHITE].texture, mRenderer, TEXT_FIXED, BLOCK); - mText.black->init(mTexture[TEXTURE_FONT_BLACK].texture, mRenderer, TEXT_FIXED, BLOCK); - mText.nokia->init(mTexture[TEXTURE_FONT_NOKIA].texture, mRenderer, TEXT_VARIABLE, 10); - - // Inicializa el objeto con el menu del titulo - mMenu.title->init("TITLE", 0, 15 * BLOCK, MENU_BACKGROUND_SOLID, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); - mMenu.title->addItem("PLAY"); - mMenu.title->addItem("OPTIONS", 0, 5); - mMenu.title->addItem("QUIT"); - mMenu.title->setDefaultActionWhenCancel(2); - mMenu.title->setBackgroundColor(0x30, 0x30, 0x40, 192); - mMenu.title->setSelectorColor(0xe5, 0x1c, 0x23, 255); - mMenu.title->setSelectorTextColor(0xFF, 0xF1, 0x76); - mMenu.title->centerMenu(SCREEN_CENTER_X); - mMenu.title->centerMenuElements(); - - // Inicializa el objeto con el menu de pausa - mMenu.pause->init("PAUSE", 0, 12 * BLOCK, MENU_BACKGROUND_SOLID, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); - mMenu.pause->addItem("CONTINUE"); - mMenu.pause->addItem("EXIT TO TITLE"); - mMenu.pause->setDefaultActionWhenCancel(0); - mMenu.pause->setBackgroundColor(0x29, 0x39, 0x41, 240); - mMenu.pause->setSelectorColor(0xFF, 0x7A, 0x00, 255); - mMenu.pause->setSelectorTextColor(0xFF, 0xFF, 0xFF); - mMenu.pause->centerMenu(SCREEN_CENTER_X); - mMenu.pause->centerMenuElements(); - - // Inicializa el objeto con el menu de la pantalla de game over - mMenu.gameOver->init("GAME OVER", 0, PLAY_AREA_CENTER_Y + BLOCK * 4, MENU_BACKGROUND_TRANSPARENT, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); - mMenu.gameOver->addItem("YES"); - mMenu.gameOver->addItem("NO"); - mMenu.gameOver->setDefaultActionWhenCancel(1); - mMenu.gameOver->setBackgroundColor(0, 0, 0, 255); - mMenu.gameOver->setSelectorColor(0x54, 0x6e, 0x7a, 255); - mMenu.gameOver->setSelectorColor(0x54, 0x6e, 0x7a, 0); - mMenu.gameOver->setSelectorTextColor(0xFF, 0xFF, 0xFF); - mMenu.gameOver->setSelectorTextColor(0xFF, 0xF1, 0x76); - mMenu.gameOver->centerMenu(SCREEN_CENTER_X); - mMenu.gameOver->centerMenuElements(); - - // Inicializa el objeto con el menu de las opciones - mMenu.options->init("OPTIONS", 0, 15 * BLOCK, MENU_BACKGROUND_SOLID, mTexture[TEXTURE_MENU].texture, mRenderer, mText.white); - mMenu.options->addItem("FULLSCREEN"); - mMenu.options->addItem("WINDOWS SIZE", 0, 5); - mMenu.options->addItem("[OK]"); - mMenu.options->addItem("[CANCEL]"); - mMenu.options->setDefaultActionWhenCancel(3); - mMenu.options->setBackgroundColor(0x30, 0x30, 0x40, 192); - mMenu.options->setSelectorColor(0xe5, 0x1c, 0x23, 255); - mMenu.options->setSelectorTextColor(0xFF, 0xF1, 0x76); - mMenu.options->centerMenu(SCREEN_CENTER_X); - mMenu.options->centerMenuElements(); - - // Actualiza los elementos del menu de opciones con los valores correspondientes - updateOptionsMenu(); - } +// Inicializa JailAudio +void GameDirector::initJailAudio() +{ + JA_Init(48000, AUDIO_S16, 2); } // Arranca SDL y crea la ventana @@ -695,9 +277,6 @@ bool GameDirector::initSDL() printf("Warning: Nearest texture filtering not enabled!\n"); } - // Inicializa jail_audio - JA_Init(48000, AUDIO_S16, 2); - // Comprueba si hay algun mando conectado if (SDL_NumJoysticks() < 1) { @@ -720,7 +299,7 @@ bool GameDirector::initSDL() printf("%i joysticks were found.\n", SDL_NumJoysticks()); std::cout << SDL_JoystickNumButtons(mGameController) << " buttons\n"; - //Get controller haptic device + // Obtiene el dispositivo de control háptico mControllerHaptic = SDL_HapticOpenFromJoystick(mGameController); if (mControllerHaptic == NULL) { @@ -730,7 +309,7 @@ bool GameDirector::initSDL() { printf("Haptics detected\n"); - //Get initialize rumble + // Inicializa la vibración if (SDL_HapticRumbleInit(mControllerHaptic) < 0) { printf("Warning: Unable to initialize rumble!\nSDL Error: %s\n", SDL_GetError()); @@ -774,22 +353,6 @@ bool GameDirector::initSDL() printf("Backbuffer could not be created!\nSDL Error: %s\n", SDL_GetError()); success = false; } - - // Crea una textura para dibujar el fondo del titulo - mTitleSurface = SDL_CreateTexture(mRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2); - if (mTitleSurface == NULL) - { - printf("TitleSurface could not be created!\nSDL Error: %s\n", SDL_GetError()); - success = false; - } - - // Crea una textura para dibujar las instrucciones - mInstructionsSurface = SDL_CreateTexture(mRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH, SCREEN_HEIGHT); - if (mInstructionsSurface == NULL) - { - printf("InstructionsSurface could not be created!\nSDL Error: %s\n", SDL_GetError()); - success = false; - } } } @@ -797,53 +360,770 @@ bool GameDirector::initSDL() return success; } +// Inicializa el vector con los valores del seno +void GameDirector::initSin() +{ + // Vector con los valores del seno para 360 grados + for (int i = 0; i < 360; i++) + { + mSin[i] = sin((float)i * 3.14f / 180.0f); + } +} + +// Inicializa las variables que contienen puntos de ruta para mover objetos +void GameDirector::initPaths() +{ + int firstPart = STAGE_COUNTER / 4; // 50 + int secondPart = firstPart * 3; // 150 + int centerPoint = PLAY_AREA_CENTER_Y - (BLOCK * 2); + int distance = (PLAY_AREA_BOTTOM) - (PLAY_AREA_CENTER_Y - 16); + + for (int i = 0; i < STAGE_COUNTER; i++) + { + if (i < firstPart) + mGame.stagePath[i] = (mSin[(int)((i * 1.8f) + 90)] * (distance) + centerPoint); + else if (i < secondPart) + mGame.stagePath[i] = (int)centerPoint; + else + mGame.stagePath[i] = (mSin[(int)(((i - 149) * 1.8f) + 90)] * (centerPoint + 17) - 17); + } +} + +// Inicializa las variables necesarias para la sección 'Logo' +void GameDirector::initLogo() +{ + // Reserva memoria para los punteros + mTexture[TEXTURE_LOGO].texture = new LTexture(); + mLogo.sprite = new Sprite(); + + // Carga los recursos + loadMediaLogo(); + + // Inicializa variables + mLogo.counter = 0; + mLogo.sprite->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_LOGO].texture, mRenderer); +} + +// Carga los recursos necesarios para la sección 'Logo' +bool GameDirector::loadMediaLogo() +{ + bool success = true; + + success &= loadTextureFromFile(mTexture[TEXTURE_LOGO].texture, mTexture[TEXTURE_LOGO].file, mRenderer); + + return success; +} + +// Libera las variables necesarias para la sección 'Logo' +void GameDirector::quitLogo() +{ + // Texturas + mTexture[TEXTURE_LOGO].texture->unload(); + + // Libera la memoria de los punteros + delete mTexture[TEXTURE_LOGO].texture; + delete mLogo.sprite; + mTexture[TEXTURE_LOGO].texture = nullptr; + mLogo.sprite = nullptr; +} + +// Inicializa las variables necesarias para la sección 'Intro' +void GameDirector::initIntro() +{ + // Reserva memoria para los punteros + mTexture[TEXTURE_INTRO].texture = new LTexture(); + + for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) + mIntro.bitmap[i] = new SmartSprite(); + + for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) + mIntro.text[i] = new Text2(); + + // Carga los recursos + loadMediaIntro(); + + // Inicializa el vector de eventos de la intro + for (Uint8 i = 0; i < INTRO_TOTAL_EVENTS; i++) + mIntro.events[i] = EVENT_WAITING; + + // Inicializa los bitmaps de la intro + for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) + { + mIntro.bitmap[i]->init(mTexture[TEXTURE_INTRO].texture, mRenderer); + mIntro.bitmap[i]->setId(i); + mIntro.bitmap[i]->setIntroEvents(&mIntro.events[0]); + mIntro.bitmap[i]->setWidth(128); + mIntro.bitmap[i]->setHeight(96); + mIntro.bitmap[i]->setEnabled(false); + mIntro.bitmap[i]->setEnabledTimer(20); + mIntro.bitmap[i]->setDestX(SCREEN_CENTER_X - 64); + mIntro.bitmap[i]->setDestY(SCREEN_FIRST_QUARTER_Y - 24); + } + + mIntro.bitmap[0]->setPosX(-128); + mIntro.bitmap[0]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); + mIntro.bitmap[0]->setVelX(0.0f); + mIntro.bitmap[0]->setVelY(0.0f); + mIntro.bitmap[0]->setAccelX(0.6f); + mIntro.bitmap[0]->setAccelY(0.0f); + mIntro.bitmap[0]->setSpriteClip(0, 0, 128, 96); + + mIntro.bitmap[1]->setPosX(SCREEN_WIDTH); + mIntro.bitmap[1]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); + mIntro.bitmap[1]->setVelX(-1.0f); + mIntro.bitmap[1]->setVelY(0.0f); + mIntro.bitmap[1]->setAccelX(-0.3f); + mIntro.bitmap[1]->setAccelY(0.0f); + mIntro.bitmap[1]->setSpriteClip(128, 0, 128, 96); + + mIntro.bitmap[2]->setPosX(SCREEN_CENTER_X - 64); + mIntro.bitmap[2]->setPosY(-96); + mIntro.bitmap[2]->setVelX(0.0f); + mIntro.bitmap[2]->setVelY(3.0f); + mIntro.bitmap[2]->setAccelX(0.1f); + mIntro.bitmap[2]->setAccelY(0.3f); + mIntro.bitmap[2]->setSpriteClip(0, 96, 128, 96); + + mIntro.bitmap[2]->setEnabledTimer(250); + + mIntro.bitmap[3]->setPosX(SCREEN_CENTER_X - 64); + mIntro.bitmap[3]->setPosY(SCREEN_HEIGHT); + mIntro.bitmap[3]->setVelX(0.0f); + mIntro.bitmap[3]->setVelY(-0.7f); + mIntro.bitmap[3]->setAccelX(0.0f); + mIntro.bitmap[3]->setAccelY(0.0f); + mIntro.bitmap[3]->setSpriteClip(128, 96, 128, 96); + + mIntro.bitmap[4]->setPosX(SCREEN_CENTER_X - 64); + mIntro.bitmap[4]->setPosY(-96); + mIntro.bitmap[4]->setVelX(0.0f); + mIntro.bitmap[4]->setVelY(3.0f); + mIntro.bitmap[4]->setAccelX(0.1f); + mIntro.bitmap[4]->setAccelY(0.3f); + mIntro.bitmap[4]->setSpriteClip(0, 192, 128, 96); + + mIntro.bitmap[5]->setPosX(SCREEN_WIDTH); + mIntro.bitmap[5]->setPosY(SCREEN_FIRST_QUARTER_Y - 24); + mIntro.bitmap[5]->setVelX(-0.7f); + mIntro.bitmap[5]->setVelY(0.0f); + mIntro.bitmap[5]->setAccelX(0.0f); + mIntro.bitmap[5]->setAccelY(0.0f); + mIntro.bitmap[5]->setSpriteClip(128, 192, 128, 96); + + // Inicializa los textos de la intro + for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) + { + mIntro.text[i]->init(mTexture[TEXTURE_FONT_NOKIA].texture, mRenderer, TEXT_VARIABLE, 10); + mIntro.text[i]->setId(6 + i); + mIntro.text[i]->setIntroEvents(&mIntro.events[0]); + mIntro.text[i]->setPosX(BLOCK * 0); + mIntro.text[i]->setPosY(SCREEN_HEIGHT - (BLOCK * 6)); + mIntro.text[i]->setKerning(-1); + mIntro.text[i]->setEnabled(false); + mIntro.text[i]->setEnabledTimer(180); + } + + mIntro.text[0]->setCaption("Un dia qualsevol de l'any 2000"); + mIntro.text[0]->setWrittingSpeed(10); + + mIntro.text[1]->setCaption("Tot esta tranquil a la UPV"); + mIntro.text[1]->setWrittingSpeed(10); + + mIntro.text[2]->setCaption("Fins que un desaprensiu..."); + mIntro.text[2]->setWrittingSpeed(15); + + mIntro.text[3]->setCaption("HEY! ME ANE A FERME UN CORTAET..."); + mIntro.text[3]->setWrittingSpeed(10); + + mIntro.text[4]->setCaption("UAAAAAAAAAAAAA!!!"); + mIntro.text[4]->setWrittingSpeed(1); + + mIntro.text[5]->setCaption("Espera un moment..."); + mIntro.text[5]->setWrittingSpeed(20); + + mIntro.text[6]->setCaption("Si resulta que no tinc solt!"); + mIntro.text[6]->setWrittingSpeed(2); + + mIntro.text[7]->setCaption("MERDA DE MAQUINA!"); + mIntro.text[7]->setWrittingSpeed(3); + + mIntro.text[8]->setCaption("Blop... blop... blop..."); + mIntro.text[8]->setWrittingSpeed(20); + + for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) + { + mIntro.text[i]->center(SCREEN_CENTER_X); + } +} + +// Carga los recursos necesarios para la sección 'Intro' +bool GameDirector::loadMediaIntro() +{ + bool success = true; + + // Texturas + success &= loadTextureFromFile(mTexture[TEXTURE_INTRO].texture, mTexture[TEXTURE_INTRO].file, mRenderer); + + // Musicas + mMusic[MUSIC_INTRO].music = JA_LoadMusic(mMusic[MUSIC_INTRO].file.c_str()); + + return success; +} + +// Libera las variables necesarias para la sección 'Intro' +void GameDirector::quitIntro() +{ + // Texturas + mTexture[TEXTURE_INTRO].texture->unload(); + + // Musicas + JA_DeleteMusic(mMusic[MUSIC_INTRO].music); + + // Libera la memoria de los punteros + delete mTexture[TEXTURE_INTRO].texture; + mTexture[TEXTURE_INTRO].texture = nullptr; + + for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) + { + delete mIntro.bitmap[i]; + mIntro.bitmap[i] = nullptr; + } + for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) + { + delete mIntro.text[i]; + mIntro.text[i] = nullptr; + } +} + +// Inicializa las variables necesarias para la sección 'Title' +void GameDirector::initTitle(Uint8 section) +{ + // Reserva memoria para los punteros + mTexture[TEXTURE_TITLE].texture = new LTexture(); + mTexture[TEXTURE_ITEMS].texture = new LTexture(); + mTitle.coffeeBitmap = new SmartSprite(); + mTitle.crisisBitmap = new SmartSprite(); + mTitle.dustBitmapL = new AnimatedSprite(); + mTitle.dustBitmapR = new AnimatedSprite(); + mTitle.tile = new Sprite(); + + // Carga los recursos + loadMediaTitle(); + + // Inicializa variables + mTitle.section = section; + mTitle.counter = TITLE_COUNTER; + mTitle.backgroundCounter = 0; + mTitle.backgroundMode = rand() % 2; + mTitle.menuVisible = false; + mTitle.nextProgSection = PROG_SECTION_GAME; + mTitle.instructionsCounter = INSTRUCTIONS_COUNTER; + + // Inicializa el bitmap de Coffee + mTitle.coffeeBitmap->init(mTexture[TEXTURE_TITLE].texture, mRenderer); + mTitle.coffeeBitmap->setId(0); + mTitle.coffeeBitmap->setIntroEvents(&mTitle.events[0]); + mTitle.coffeeBitmap->setPosX(45); + mTitle.coffeeBitmap->setPosY(11 - 200); + mTitle.coffeeBitmap->setWidth(167); + mTitle.coffeeBitmap->setHeight(46); + mTitle.coffeeBitmap->setVelX(0.0f); + mTitle.coffeeBitmap->setVelY(2.5f); + mTitle.coffeeBitmap->setAccelX(0.0f); + mTitle.coffeeBitmap->setAccelY(0.1f); + mTitle.coffeeBitmap->setSpriteClip(0, 0, 167, 46); + mTitle.coffeeBitmap->setEnabled(true); + mTitle.coffeeBitmap->setEnabledTimer(0); + mTitle.coffeeBitmap->setDestX(45); + mTitle.coffeeBitmap->setDestY(11); + + // Inicializa el bitmap de Crisis + mTitle.crisisBitmap->init(mTexture[TEXTURE_TITLE].texture, mRenderer); + mTitle.crisisBitmap->setId(1); + mTitle.crisisBitmap->setIntroEvents(&mTitle.events[0]); + mTitle.crisisBitmap->setPosX(60); + mTitle.crisisBitmap->setPosY(57 + 200); + mTitle.crisisBitmap->setWidth(137); + mTitle.crisisBitmap->setHeight(46); + mTitle.crisisBitmap->setVelX(0.0f); + mTitle.crisisBitmap->setVelY(-2.5f); + mTitle.crisisBitmap->setAccelX(0.0f); + mTitle.crisisBitmap->setAccelY(-0.1f); + mTitle.crisisBitmap->setSpriteClip(0, 46, 137, 46); + mTitle.crisisBitmap->setEnabled(true); + mTitle.crisisBitmap->setEnabledTimer(0); + mTitle.crisisBitmap->setDestX(60); + mTitle.crisisBitmap->setDestY(57); + + // Inicializa el bitmap de DustRight + mTitle.dustBitmapR->init(mTexture[TEXTURE_TITLE].texture, mRenderer); + mTitle.dustBitmapR->setPosX(218); + mTitle.dustBitmapR->setPosY(47); + mTitle.dustBitmapR->setWidth(16); + mTitle.dustBitmapR->setHeight(14); + mTitle.dustBitmapR->setCurrentFrame(0); + mTitle.dustBitmapR->setAnimationCounter(0); + mTitle.dustBitmapR->setAnimationNumFrames(0, 7); + mTitle.dustBitmapR->setAnimationSpeed(0, 8); + mTitle.dustBitmapR->setAnimationLoop(0, false); + mTitle.dustBitmapR->setAnimationFrames(0, 0, 160 + (mTitle.dustBitmapR->getWidth() * 0), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); + mTitle.dustBitmapR->setAnimationFrames(0, 1, 160 + (mTitle.dustBitmapR->getWidth() * 1), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); + mTitle.dustBitmapR->setAnimationFrames(0, 2, 160 + (mTitle.dustBitmapR->getWidth() * 2), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); + mTitle.dustBitmapR->setAnimationFrames(0, 3, 160 + (mTitle.dustBitmapR->getWidth() * 3), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); + mTitle.dustBitmapR->setAnimationFrames(0, 4, 160 + (mTitle.dustBitmapR->getWidth() * 4), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); + mTitle.dustBitmapR->setAnimationFrames(0, 5, 160 + (mTitle.dustBitmapR->getWidth() * 5), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); + mTitle.dustBitmapR->setAnimationFrames(0, 6, 160 + (mTitle.dustBitmapR->getWidth() * 6), 80, mTitle.dustBitmapR->getWidth(), mTitle.dustBitmapR->getHeight()); + + // Inicializa el bitmap de DustLeft + mTitle.dustBitmapL->init(mTexture[TEXTURE_TITLE].texture, mRenderer); + mTitle.dustBitmapL->setPosX(33); + mTitle.dustBitmapL->setPosY(47); + mTitle.dustBitmapL->setWidth(16); + mTitle.dustBitmapL->setHeight(14); + mTitle.dustBitmapL->setCurrentFrame(0); + mTitle.dustBitmapL->setAnimationCounter(0); + mTitle.dustBitmapL->setAnimationNumFrames(0, 7); + mTitle.dustBitmapL->setAnimationSpeed(0, 8); + mTitle.dustBitmapL->setAnimationLoop(0, false); + mTitle.dustBitmapL->setAnimationFrames(0, 0, 160 + (mTitle.dustBitmapL->getWidth() * 0), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); + mTitle.dustBitmapL->setAnimationFrames(0, 1, 160 + (mTitle.dustBitmapL->getWidth() * 1), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); + mTitle.dustBitmapL->setAnimationFrames(0, 2, 160 + (mTitle.dustBitmapL->getWidth() * 2), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); + mTitle.dustBitmapL->setAnimationFrames(0, 3, 160 + (mTitle.dustBitmapL->getWidth() * 3), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); + mTitle.dustBitmapL->setAnimationFrames(0, 4, 160 + (mTitle.dustBitmapL->getWidth() * 4), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); + mTitle.dustBitmapL->setAnimationFrames(0, 5, 160 + (mTitle.dustBitmapL->getWidth() * 5), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); + mTitle.dustBitmapL->setAnimationFrames(0, 6, 160 + (mTitle.dustBitmapL->getWidth() * 6), 66, mTitle.dustBitmapL->getWidth(), mTitle.dustBitmapL->getHeight()); + + // Inicializa el vector de eventos de la pantalla de titulo + for (Uint8 i = 0; i < TITLE_TOTAL_EVENTS; i++) + mTitle.events[i] = EVENT_WAITING; + + // El tile de fondo + mTitle.tile->init(0, 0, 64, 64, mTexture[TEXTURE_TITLE].texture, mRenderer); + mTitle.tile->setSpriteClip(192, 0, 64, 64); +} + +// Carga los recursos necesarios para la sección 'Title' +bool GameDirector::loadMediaTitle() +{ + // Indicador de éxito en la carga + bool success = true; + + // Texturas + success &= loadTextureFromFile(mTexture[TEXTURE_TITLE].texture, mTexture[TEXTURE_TITLE].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_ITEMS].texture, mTexture[TEXTURE_ITEMS].file, mRenderer); + + // Crea el mosaico de fondo del titulo + mTitleSurface = SDL_CreateTexture(mRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2); + if (mTitleSurface == NULL) + { + printf("TitleSurface could not be created!\nSDL Error: %s\n", SDL_GetError()); + success = false; + } + else + { + SDL_SetRenderTarget(mRenderer, mTitleSurface); + SDL_SetRenderDrawColor(mRenderer, 0x43, 0x43, 0x4F, 0xFF); + SDL_RenderClear(mRenderer); + for (Uint8 i = 0; i < 8; i++) + for (Uint8 j = 0; j < 6; j++) + { + mTitle.tile->setPosX(i * 64); + mTitle.tile->setPosY(j * 64); + mTitle.tile->render(); + } + mTitle.backgroundWindow.x = 0; + mTitle.backgroundWindow.y = 0; + mTitle.backgroundWindow.w = SCREEN_WIDTH; + mTitle.backgroundWindow.h = SCREEN_HEIGHT; + SDL_SetRenderTarget(mRenderer, nullptr); + } + + // Crea una textura para dibujar las instrucciones + mInstructionsSurface = SDL_CreateTexture(mRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH, SCREEN_HEIGHT); + if (mInstructionsSurface == NULL) + { + printf("InstructionsSurface could not be created!\nSDL Error: %s\n", SDL_GetError()); + success = false; + } + + // Sonidos + mSound[SOUND_TITLE].sound = JA_LoadSound(mSound[SOUND_TITLE].file.c_str()); + + return success; +} + +// Libera las variables necesarias para la sección 'Title' +void GameDirector::quitTitle() +{ + // Texturas + mTexture[TEXTURE_TITLE].texture->unload(); + mTexture[TEXTURE_ITEMS].texture->unload(); + + // Sonidos + JA_DeleteSound(mSound[SOUND_TITLE].sound); + + // Libera la memoria de los punteros + delete mTexture[TEXTURE_TITLE].texture; + delete mTexture[TEXTURE_ITEMS].texture; + delete mTitle.coffeeBitmap; + delete mTitle.crisisBitmap; + delete mTitle.dustBitmapL; + delete mTitle.dustBitmapR; + delete mTitle.tile; + mTexture[TEXTURE_TITLE].texture = nullptr; + mTexture[TEXTURE_ITEMS].texture = nullptr; + mTitle.coffeeBitmap = nullptr; + mTitle.crisisBitmap = nullptr; + mTitle.dustBitmapL = nullptr; + mTitle.dustBitmapR = nullptr; + mTitle.tile = nullptr; + + SDL_DestroyTexture(mTitleSurface); + SDL_DestroyTexture(mInstructionsSurface); + mTitleSurface = nullptr; + mInstructionsSurface = nullptr; +} + +// Inicializa las variables necesarias para la sección 'Game' +void GameDirector::initGame() +{ + // Reserva memoria para los punteros + for (Uint8 i = 0; i < MAX_BALLOONS; i++) + mGame.balloon[i] = new Balloon(); + for (Uint8 i = 0; i < MAX_BULLETS; i++) + mGame.bullet[i] = new Bullet(); + for (Uint8 i = 0; i < MAX_ITEMS; i++) + mGame.item[i] = new Item(); + for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++) + mGame.smartSprite[i] = new SmartSprite(); + mTexture[TEXTURE_PLAYER_LEGS].texture = new LTexture(); + mTexture[TEXTURE_PLAYER_BODY].texture = new LTexture(); + mTexture[TEXTURE_PLAYER_DEATH].texture = new LTexture(); + mTexture[TEXTURE_BALLOON].texture = new LTexture(); + mTexture[TEXTURE_BULLET].texture = new LTexture(); + mTexture[TEXTURE_GAME_BG].texture = new LTexture(); + mTexture[TEXTURE_GAME_TEXT].texture = new LTexture(); + mTexture[TEXTURE_ITEMS].texture = new LTexture(); + mGame.player = new Player(); + mGame._1000Bitmap = new SmartSprite(); + mGame._2500Bitmap = new SmartSprite(); + mGame._5000Bitmap = new SmartSprite(); + mGame.background = new Sprite(); + mGame.clouds1a = new MovingSprite(); + mGame.clouds1b = new MovingSprite(); + mGame.clouds2a = new MovingSprite(); + mGame.clouds2b = new MovingSprite(); + mGame.getReadyBitmap = new SmartSprite(); + mGame.gradient = new Sprite(); + mGame.grass = new Sprite(); + + // Carga los recursos + loadMediaGame(); + + // Inicializa variables + mGame.section = GAME_SECTION_PLAY; + mGame.menaceLevelCurrent = 0; + mGame.menaceLevelThreshold = 0; + mGame.score = 0; + mGame.hiScoreAchieved = false; + mGame.stage = 1; + mGame.stageCounter = STAGE_COUNTER; + mGame.deathCounter = DEATH_COUNTER; + mGame.explosionTime = false; + mGame.remainingExplosions = REMAINING_EXPLOSIONS; + mGame.remainingExplosionsCounter = REMAINING_EXPLOSIONS_COUNTER; + mGame.timeStopped = false; + mGame.timeStoppedCounter = TIME_STOPPED_COUNTER; + mGame.playFieldDrawOnly = true; + mProg.debug = false; + mGame.counter = 0; + + // Fondo animado + mGame.clouds1a->init(0, 0, 256, 52, -0.4f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); + mGame.clouds1a->setSpriteClip(256, 0, 256, 52); + + mGame.clouds1b->init(256, 0, 256, 52, -0.4f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); + mGame.clouds1b->setSpriteClip(256, 0, 256, 52); + + mGame.clouds2a->init(0, 52, 256, 32, -0.2f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); + mGame.clouds2a->setSpriteClip(256, 52, 256, 32); + + mGame.clouds2b->init(256, 52, 256, 32, -0.2f, 0.0f, 0.0f, 0.0f, mTexture[TEXTURE_GAME_BG].texture, mRenderer); + mGame.clouds2b->setSpriteClip(256, 52, 256, 32); + + mGame.grass->init(0, 85, SCREEN_WIDTH, 6, mTexture[TEXTURE_GAME_BG].texture, mRenderer); + mGame.grass->setPosY(154); + + // Objeto jugador + mGame.player->init(PLAY_AREA_CENTER_X - 12, PLAY_AREA_BOTTOM - 24, mTexture[TEXTURE_PLAYER_LEGS].texture, mTexture[TEXTURE_PLAYER_BODY].texture, mRenderer); + + // Establece a cero todos los valores del vector de objetos globo + resetBalloons(); + + // Crea objetos globo y los centra en el area de juego + mGame.balloon[0]->init(0, BLOCK, BALLOON_4, BALLOON_VELX_POSITIVE, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); + mGame.balloon[0]->allignTo(PLAY_AREA_CENTER_X); + + // Con los globos creados, calcula el nivel de amenaza + setMenaceLevel(); + + // Establece a cero todos los valores del vector de objetos bala + resetBullets(); + + // Establece a cero todos los valores del vector de objetos item + resetItems(); + + // Establece a cero todos los valores del vector de objetos SmafrtSprite + resetSmartSprites(); + + // Inicializa el bitmap de GetReady! + mGame.getReadyBitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); + mGame.getReadyBitmap->setPosX(-113); + mGame.getReadyBitmap->setPosY(PLAY_AREA_CENTER_Y - 10); + mGame.getReadyBitmap->setWidth(109); + mGame.getReadyBitmap->setHeight(20); + mGame.getReadyBitmap->setVelX(2.0f); + mGame.getReadyBitmap->setVelY(0.0f); + mGame.getReadyBitmap->setAccelX(0.00f); + mGame.getReadyBitmap->setAccelY(0.0f); + mGame.getReadyBitmap->setSpriteClip(0, 0, 109, 20); + mGame.getReadyBitmap->setEnabled(true); + mGame.getReadyBitmap->setEnabledTimer(0); + mGame.getReadyBitmap->setDestX(PLAY_AREA_RIGHT); + mGame.getReadyBitmap->setDestY(PLAY_AREA_CENTER_Y - 10); + + // Inicializa el bitmap de 1000 puntos + mGame._1000Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); + mGame._1000Bitmap->setPosX(0); + mGame._1000Bitmap->setPosY(0); + mGame._1000Bitmap->setWidth(26); + mGame._1000Bitmap->setHeight(9); + mGame._1000Bitmap->setVelX(0.0f); + mGame._1000Bitmap->setVelY(-0.5f); + mGame._1000Bitmap->setAccelX(0.0f); + mGame._1000Bitmap->setAccelY(-0.1f); + mGame._1000Bitmap->setSpriteClip(0, 20, 26, 9); + mGame._1000Bitmap->setEnabled(false); + mGame._1000Bitmap->setEnabledTimer(0); + mGame._1000Bitmap->setDestX(0); + mGame._1000Bitmap->setDestY(0); + + // Inicializa el bitmap de 2500 puntos + mGame._2500Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); + mGame._2500Bitmap->setPosX(0); + mGame._2500Bitmap->setPosY(0); + mGame._2500Bitmap->setWidth(28); + mGame._2500Bitmap->setHeight(9); + mGame._2500Bitmap->setVelX(0.0f); + mGame._2500Bitmap->setVelY(-0.5f); + mGame._2500Bitmap->setAccelX(0.0f); + mGame._2500Bitmap->setAccelY(-0.1f); + mGame._2500Bitmap->setSpriteClip(26, 20, 28, 9); + mGame._2500Bitmap->setEnabled(false); + mGame._2500Bitmap->setEnabledTimer(0); + mGame._2500Bitmap->setDestX(0); + mGame._2500Bitmap->setDestY(0); + + // Inicializa el bitmap de 5000 puntos + mGame._5000Bitmap->init(mTexture[TEXTURE_GAME_TEXT].texture, mRenderer); + mGame._5000Bitmap->setPosX(0); + mGame._5000Bitmap->setPosY(0); + mGame._5000Bitmap->setWidth(28); + mGame._5000Bitmap->setHeight(9); + mGame._5000Bitmap->setVelX(0.0f); + mGame._5000Bitmap->setVelY(-0.5f); + mGame._5000Bitmap->setAccelX(0.0f); + mGame._5000Bitmap->setAccelY(-0.1f); + mGame._5000Bitmap->setSpriteClip(54, 20, 28, 9); + mGame._5000Bitmap->setEnabled(false); + mGame._5000Bitmap->setEnabledTimer(0); + mGame._5000Bitmap->setDestX(0); + mGame._5000Bitmap->setDestY(0); + + // Los fondos + mGame.background->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_GAME_BG].texture, mRenderer); + mGame.gradient->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTexture[TEXTURE_GAME_BG].texture, mRenderer); +} + +// Carga los recursos necesarios para la sección 'Game' +bool GameDirector::loadMediaGame() +{ + bool success = true; + + // Texturas + success &= loadTextureFromFile(mTexture[TEXTURE_PLAYER_LEGS].texture, mTexture[TEXTURE_PLAYER_LEGS].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_PLAYER_BODY].texture, mTexture[TEXTURE_PLAYER_BODY].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_PLAYER_DEATH].texture, mTexture[TEXTURE_PLAYER_DEATH].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_BALLOON].texture, mTexture[TEXTURE_BALLOON].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_BULLET].texture, mTexture[TEXTURE_BULLET].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_GAME_BG].texture, mTexture[TEXTURE_GAME_BG].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_GAME_TEXT].texture, mTexture[TEXTURE_GAME_TEXT].file, mRenderer); + success &= loadTextureFromFile(mTexture[TEXTURE_ITEMS].texture, mTexture[TEXTURE_ITEMS].file, mRenderer); + + // Sonidos + mSound[SOUND_BALLOON].sound = JA_LoadSound(mSound[SOUND_BALLOON].file.c_str()); + mSound[SOUND_BULLET].sound = JA_LoadSound(mSound[SOUND_BULLET].file.c_str()); + mSound[SOUND_PLAYER_COLLISION].sound = JA_LoadSound(mSound[SOUND_PLAYER_COLLISION].file.c_str()); + mSound[SOUND_HISCORE].sound = JA_LoadSound(mSound[SOUND_HISCORE].file.c_str()); + mSound[SOUND_ITEM_DROP].sound = JA_LoadSound(mSound[SOUND_ITEM_DROP].file.c_str()); + mSound[SOUND_ITEM_PICKUP].sound = JA_LoadSound(mSound[SOUND_ITEM_PICKUP].file.c_str()); + mSound[SOUND_COFFEE_OUT].sound = JA_LoadSound(mSound[SOUND_COFFEE_OUT].file.c_str()); + mSound[SOUND_STAGE_CHANGE].sound = JA_LoadSound(mSound[SOUND_STAGE_CHANGE].file.c_str()); + mSound[SOUND_TITLE].sound = JA_LoadSound(mSound[SOUND_TITLE].file.c_str()); + mSound[SOUND_BUBBLE1].sound = JA_LoadSound(mSound[SOUND_BUBBLE1].file.c_str()); + mSound[SOUND_BUBBLE2].sound = JA_LoadSound(mSound[SOUND_BUBBLE2].file.c_str()); + mSound[SOUND_BUBBLE3].sound = JA_LoadSound(mSound[SOUND_BUBBLE3].file.c_str()); + mSound[SOUND_BUBBLE4].sound = JA_LoadSound(mSound[SOUND_BUBBLE4].file.c_str()); + + // Musicas + mMusic[MUSIC_PLAYING].music = JA_LoadMusic(mMusic[MUSIC_PLAYING].file.c_str()); + + return success; +} + +// Libera las variables necesarias para la sección 'Game' +void GameDirector::quitGame() +{ + // Texturas + mTexture[TEXTURE_PLAYER_LEGS].texture->unload(); + mTexture[TEXTURE_PLAYER_BODY].texture->unload(); + mTexture[TEXTURE_PLAYER_DEATH].texture->unload(); + mTexture[TEXTURE_BALLOON].texture->unload(); + mTexture[TEXTURE_BULLET].texture->unload(); + mTexture[TEXTURE_GAME_BG].texture->unload(); + mTexture[TEXTURE_GAME_TEXT].texture->unload(); + mTexture[TEXTURE_ITEMS].texture->unload(); + + // Sonidos + JA_DeleteSound(mSound[SOUND_BALLOON].sound); + JA_DeleteSound(mSound[SOUND_BULLET].sound); + JA_DeleteSound(mSound[SOUND_PLAYER_COLLISION].sound); + JA_DeleteSound(mSound[SOUND_HISCORE].sound); + JA_DeleteSound(mSound[SOUND_ITEM_DROP].sound); + JA_DeleteSound(mSound[SOUND_ITEM_PICKUP].sound); + JA_DeleteSound(mSound[SOUND_COFFEE_OUT].sound); + JA_DeleteSound(mSound[SOUND_STAGE_CHANGE].sound); + JA_DeleteSound(mSound[SOUND_TITLE].sound); + JA_DeleteSound(mSound[SOUND_BUBBLE1].sound); + JA_DeleteSound(mSound[SOUND_BUBBLE2].sound); + JA_DeleteSound(mSound[SOUND_BUBBLE3].sound); + JA_DeleteSound(mSound[SOUND_BUBBLE4].sound); + + // Musicas + JA_DeleteMusic(mMusic[MUSIC_PLAYING].music); + + // Libera la memoria de los punteros + for (Uint8 i = 0; i < MAX_BALLOONS; i++) + { + delete mGame.balloon[i]; + mGame.balloon[i] = nullptr; + } + for (Uint8 i = 0; i < MAX_BULLETS; i++) + { + delete mGame.bullet[i]; + mGame.bullet[i] = nullptr; + } + for (Uint8 i = 0; i < MAX_ITEMS; i++) + { + delete mGame.item[i]; + mGame.item[i] = nullptr; + } + for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++) + { + delete mGame.smartSprite[i]; + mGame.smartSprite[i] = nullptr; + } + delete mTexture[TEXTURE_PLAYER_LEGS].texture; + delete mTexture[TEXTURE_PLAYER_BODY].texture; + delete mTexture[TEXTURE_PLAYER_DEATH].texture; + delete mTexture[TEXTURE_BALLOON].texture; + delete mTexture[TEXTURE_BULLET].texture; + delete mTexture[TEXTURE_GAME_BG].texture; + delete mTexture[TEXTURE_GAME_TEXT].texture; + delete mTexture[TEXTURE_ITEMS].texture; + delete mGame.player; + delete mGame._1000Bitmap; + delete mGame._2500Bitmap; + delete mGame._5000Bitmap; + delete mGame.background; + delete mGame.clouds1a; + delete mGame.clouds1b; + delete mGame.clouds2a; + delete mGame.clouds2b; + delete mGame.getReadyBitmap; + delete mGame.gradient; + delete mGame.grass; + mTexture[TEXTURE_PLAYER_LEGS].texture = nullptr; + mTexture[TEXTURE_PLAYER_BODY].texture = nullptr; + mTexture[TEXTURE_PLAYER_DEATH].texture = nullptr; + mTexture[TEXTURE_BALLOON].texture = nullptr; + mTexture[TEXTURE_BULLET].texture = nullptr; + mTexture[TEXTURE_GAME_BG].texture = nullptr; + mTexture[TEXTURE_GAME_TEXT].texture = nullptr; + mTexture[TEXTURE_ITEMS].texture = nullptr; + mGame.player = nullptr; + mGame._1000Bitmap = nullptr; + mGame._2500Bitmap = nullptr; + mGame._5000Bitmap = nullptr; + mGame.background = nullptr; + mGame.clouds1a = nullptr; + mGame.clouds1b = nullptr; + mGame.clouds2a = nullptr; + mGame.clouds2b = nullptr; + mGame.getReadyBitmap = nullptr; + mGame.gradient = nullptr; + mGame.grass = nullptr; +} + // Crea el indice de ficheros void GameDirector::setFileList() { // Ficheros binarios - mBinFile[BINFILE_SCORE].file = mExecutablePath + "/" + "../data/score.bin"; - mBinFile[BINFILE_DEMO].file = mExecutablePath + "/" + "../data/demo.bin"; - mBinFile[BINFILE_CONFIG].file = mExecutablePath + "/" + "../data/config.bin"; + mBinFile[BINFILE_SCORE].file = mProg.executablePath + "/" + "../data/score.bin"; + mBinFile[BINFILE_DEMO].file = mProg.executablePath + "/" + "../data/demo.bin"; + mBinFile[BINFILE_CONFIG].file = mProg.executablePath + "/" + "../data/config.bin"; // Musicas - mMusic[MUSIC_INTRO].file = mExecutablePath + "/" + "../media/music/intro.ogg"; - mMusic[MUSIC_PLAYING].file = mExecutablePath + "/" + "../media/music/playing.ogg"; - mMusic[MUSIC_TITLE].file = mExecutablePath + "/" + "../media/music/title.ogg"; + mMusic[MUSIC_INTRO].file = mProg.executablePath + "/" + "../media/music/intro.ogg"; + mMusic[MUSIC_PLAYING].file = mProg.executablePath + "/" + "../media/music/playing.ogg"; + mMusic[MUSIC_TITLE].file = mProg.executablePath + "/" + "../media/music/title.ogg"; // Sonidos - mSound[SOUND_BALLON].file = mExecutablePath + "/" + "../media/sound/balloon.wav"; - mSound[SOUND_BULLET].file = mExecutablePath + "/" + "../media/sound/bullet.wav"; - mSound[SOUND_MENU_SELECT].file = mExecutablePath + "/" + "../media/sound/menu_select.wav"; - mSound[SOUND_MENU_CANCEL].file = mExecutablePath + "/" + "../media/sound/menu_cancel.wav"; - mSound[SOUND_MENU_MOVE].file = mExecutablePath + "/" + "../media/sound/menu_move.wav"; - mSound[SOUND_TITLE].file = mExecutablePath + "/" + "../media/sound/title.wav"; - mSound[SOUND_PLAYER_COLLISION].file = mExecutablePath + "/" + "../media/sound/player_collision.wav"; - mSound[SOUND_HISCORE].file = mExecutablePath + "/" + "../media/sound/hiscore.wav"; - mSound[SOUND_ITEM_DROP].file = mExecutablePath + "/" + "../media/sound/itemdrop.wav"; - mSound[SOUND_ITEM_PICKUP].file = mExecutablePath + "/" + "../media/sound/itempickup.wav"; - mSound[SOUND_COFFEE_OUT].file = mExecutablePath + "/" + "../media/sound/coffeeout.wav"; - mSound[SOUND_STAGE_CHANGE].file = mExecutablePath + "/" + "../media/sound/stage_change.wav"; - mSound[SOUND_BUBBLE1].file = mExecutablePath + "/" + "../media/sound/bubble1.wav"; - mSound[SOUND_BUBBLE2].file = mExecutablePath + "/" + "../media/sound/bubble2.wav"; - mSound[SOUND_BUBBLE3].file = mExecutablePath + "/" + "../media/sound/bubble3.wav"; - mSound[SOUND_BUBBLE4].file = mExecutablePath + "/" + "../media/sound/bubble4.wav"; + mSound[SOUND_BALLOON].file = mProg.executablePath + "/" + "../media/sound/balloon.wav"; + mSound[SOUND_BUBBLE1].file = mProg.executablePath + "/" + "../media/sound/bubble1.wav"; + mSound[SOUND_BUBBLE2].file = mProg.executablePath + "/" + "../media/sound/bubble2.wav"; + mSound[SOUND_BUBBLE3].file = mProg.executablePath + "/" + "../media/sound/bubble3.wav"; + mSound[SOUND_BUBBLE4].file = mProg.executablePath + "/" + "../media/sound/bubble4.wav"; + mSound[SOUND_BULLET].file = mProg.executablePath + "/" + "../media/sound/bullet.wav"; + mSound[SOUND_COFFEE_OUT].file = mProg.executablePath + "/" + "../media/sound/coffeeout.wav"; + mSound[SOUND_HISCORE].file = mProg.executablePath + "/" + "../media/sound/hiscore.wav"; + mSound[SOUND_ITEM_DROP].file = mProg.executablePath + "/" + "../media/sound/itemdrop.wav"; + mSound[SOUND_ITEM_PICKUP].file = mProg.executablePath + "/" + "../media/sound/itempickup.wav"; + mSound[SOUND_MENU_CANCEL].file = mProg.executablePath + "/" + "../media/sound/menu_cancel.wav"; + mSound[SOUND_MENU_MOVE].file = mProg.executablePath + "/" + "../media/sound/menu_move.wav"; + mSound[SOUND_MENU_SELECT].file = mProg.executablePath + "/" + "../media/sound/menu_select.wav"; + mSound[SOUND_PLAYER_COLLISION].file = mProg.executablePath + "/" + "../media/sound/player_collision.wav"; + mSound[SOUND_STAGE_CHANGE].file = mProg.executablePath + "/" + "../media/sound/stage_change.wav"; + mSound[SOUND_TITLE].file = mProg.executablePath + "/" + "../media/sound/title.wav"; // Texturas - mTexture[TEXTURE_BALLOON].file = mExecutablePath + "/" + "../media/gfx/balloon.png"; - mTexture[TEXTURE_BULLET].file = mExecutablePath + "/" + "../media/gfx/bullet.png"; - mTexture[TEXTURE_FONT_BLACK].file = mExecutablePath + "/" + "../media/gfx/font_black.png"; - mTexture[TEXTURE_FONT_NOKIA].file = mExecutablePath + "/" + "../media/gfx/font_nokia.png"; - mTexture[TEXTURE_FONT_WHITE].file = mExecutablePath + "/" + "../media/gfx/font_white.png"; - mTexture[TEXTURE_GAME_BG].file = mExecutablePath + "/" + "../media/gfx/game_bg.png"; - mTexture[TEXTURE_GAME_TEXT].file = mExecutablePath + "/" + "../media/gfx/game_text.png"; - mTexture[TEXTURE_INTRO].file = mExecutablePath + "/" + "../media/gfx/intro.png"; - mTexture[TEXTURE_ITEMS].file = mExecutablePath + "/" + "../media/gfx/items.png"; - mTexture[TEXTURE_LOGO].file = mExecutablePath + "/" + "../media/gfx/logo.png"; - mTexture[TEXTURE_MENU].file = mExecutablePath + "/" + "../media/gfx/menu.png"; - mTexture[TEXTURE_PLAYER_LEGS].file = mExecutablePath + "/" + "../media/gfx/player_legs.png"; - mTexture[TEXTURE_PLAYER_BODY].file = mExecutablePath + "/" + "../media/gfx/player_body.png"; - mTexture[TEXTURE_PLAYER_DEATH].file = mExecutablePath + "/" + "../media/gfx/player_death.png"; - mTexture[TEXTURE_TITLE].file = mExecutablePath + "/" + "../media/gfx/title.png"; + mTexture[TEXTURE_BALLOON].file = mProg.executablePath + "/" + "../media/gfx/balloon.png"; + mTexture[TEXTURE_BULLET].file = mProg.executablePath + "/" + "../media/gfx/bullet.png"; + mTexture[TEXTURE_FONT_BLACK_X2].file = mProg.executablePath + "/" + "../media/gfx/font_black_x2.png"; + mTexture[TEXTURE_FONT_BLACK].file = mProg.executablePath + "/" + "../media/gfx/font_black.png"; + mTexture[TEXTURE_FONT_NOKIA].file = mProg.executablePath + "/" + "../media/gfx/font_nokia.png"; + mTexture[TEXTURE_FONT_WHITE_X2].file = mProg.executablePath + "/" + "../media/gfx/font_white_x2.png"; + mTexture[TEXTURE_FONT_WHITE].file = mProg.executablePath + "/" + "../media/gfx/font_white.png"; + mTexture[TEXTURE_GAME_BG].file = mProg.executablePath + "/" + "../media/gfx/game_bg.png"; + mTexture[TEXTURE_GAME_TEXT].file = mProg.executablePath + "/" + "../media/gfx/game_text.png"; + mTexture[TEXTURE_INTRO].file = mProg.executablePath + "/" + "../media/gfx/intro.png"; + mTexture[TEXTURE_ITEMS].file = mProg.executablePath + "/" + "../media/gfx/items.png"; + mTexture[TEXTURE_LOGO].file = mProg.executablePath + "/" + "../media/gfx/logo.png"; + mTexture[TEXTURE_MENU].file = mProg.executablePath + "/" + "../media/gfx/menu.png"; + mTexture[TEXTURE_PLAYER_BODY].file = mProg.executablePath + "/" + "../media/gfx/player_body.png"; + mTexture[TEXTURE_PLAYER_DEATH].file = mProg.executablePath + "/" + "../media/gfx/player_death.png"; + mTexture[TEXTURE_PLAYER_LEGS].file = mProg.executablePath + "/" + "../media/gfx/player_legs.png"; + mTexture[TEXTURE_TITLE].file = mProg.executablePath + "/" + "../media/gfx/title.png"; } // Comprueba que todos los ficheros existen @@ -856,69 +1136,330 @@ bool GameDirector::checkFileList() // Comprueba los ficheros de musica printf("\n>> MUSIC FILES\n"); - for (Uint8 i = 0; i < TOTAL_MUSIC; i++) - { - p = mMusic[i].file.c_str(); - filename = p.substr(p.find_last_of("\\/") + 1); - file = SDL_RWFromFile(p.c_str(), "r+b"); - if (file != NULL) + if (success) + for (Uint8 i = 0; i < TOTAL_MUSIC; i++) { - printf("Checking file %-20s [OK]\n", filename.c_str()); + p = mMusic[i].file.c_str(); + filename = p.substr(p.find_last_of("\\/") + 1); + file = SDL_RWFromFile(p.c_str(), "r+b"); + if (file != NULL) + { + printf("Checking file %-20s [OK]\n", filename.c_str()); + } + else + { + printf("Checking file %-20s [ERROR]\n", filename.c_str()); + success = false; + break; + } + SDL_RWclose(file); } - else - { - success = false; - printf("Checking file %-20s [ERROR]\n", filename.c_str()); - } - SDL_RWclose(file); - } // Comprueba los ficheros de sonidos printf("\n>> SOUND FILES\n"); - for (Uint8 i = 0; i < TOTAL_SOUND; i++) - { - p = mSound[i].file.c_str(); - filename = p.substr(p.find_last_of("\\/") + 1); - file = SDL_RWFromFile(p.c_str(), "r+b"); - if (file != NULL) + if (success) + for (Uint8 i = 0; i < TOTAL_SOUND; i++) { - printf("Checking file %-20s [OK]\n", filename.c_str()); + p = mSound[i].file.c_str(); + filename = p.substr(p.find_last_of("\\/") + 1); + file = SDL_RWFromFile(p.c_str(), "r+b"); + if (file != NULL) + { + printf("Checking file %-20s [OK]\n", filename.c_str()); + } + else + { + printf("Checking file %-20s [ERROR]\n", filename.c_str()); + success = false; + break; + } + SDL_RWclose(file); } - else - { - success = false; - printf("Checking file %-20s [ERROR]\n", filename.c_str()); - } - SDL_RWclose(file); - } // Comprueba los ficheros con texturas printf("\n>> TEXTURE FILES\n"); - for (Uint8 i = 0; i < TOTAL_TEXTURE; i++) - { - p = mTexture[i].file.c_str(); - filename = p.substr(p.find_last_of("\\/") + 1); - file = SDL_RWFromFile(p.c_str(), "r+b"); - if (file != NULL) + if (success) + for (Uint8 i = 0; i < TOTAL_TEXTURE; i++) { - printf("Checking file %-20s [OK]\n", filename.c_str()); + p = mTexture[i].file.c_str(); + filename = p.substr(p.find_last_of("\\/") + 1); + file = SDL_RWFromFile(p.c_str(), "r+b"); + if (file != NULL) + { + printf("Checking file %-20s [OK]\n", filename.c_str()); + } + else + { + printf("Checking file %-20s [ERROR]\n", filename.c_str()); + success = false; + break; + } + SDL_RWclose(file); } - else - { - success = false; - printf("Checking file %-20s [ERROR]\n", filename.c_str()); - } - SDL_RWclose(file); - } // Resultado if (success) - { printf("\n** All files OK.\n\n"); + else + printf("\n** A file is missing. Exiting.\n\n"); + + return success; +} + +// Carga el fichero de puntos +bool GameDirector::loadScoreFile() +{ + // Indicador de éxito en la carga + bool success = true; + std::string path = mProg.executablePath + "/"; + std::string p = mBinFile[BINFILE_SCORE].file.c_str(); + std::string filename = p.substr(p.find_last_of("\\/") + 1); + SDL_RWops *file = SDL_RWFromFile(p.c_str(), "r+b"); + + // El fichero no existe + if (file == NULL) + { + printf("Warning: Unable to open %s file\n", filename.c_str()); + + // Creamos el fichero para escritura + file = SDL_RWFromFile(p.c_str(), "w+b"); + if (file != NULL) + { + printf("New file (%s) created!\n", filename.c_str()); + + // Inicializamos los datos + for (int i = 0; i < TOTAL_SCORE_DATA; ++i) + { + mGame.scoreDataFile[i] = 0; + SDL_RWwrite(file, &mGame.scoreDataFile[i], sizeof(Uint32), 1); + } + + // Cerramos el fichero + SDL_RWclose(file); + } + else + { + printf("Error: Unable to create file %s\n", filename.c_str()); + success = false; + } + } + // El fichero existe + else + { + // Cargamos los datos + printf("Reading file %s\n", filename.c_str()); + for (int i = 0; i < TOTAL_SCORE_DATA; ++i) + SDL_RWread(file, &mGame.scoreDataFile[i], sizeof(Uint32), 1); + + // Cierra el fichero + SDL_RWclose(file); + } + + // Establece el valor de la máxima puntuación a partir del vector con los datos + if (mGame.scoreDataFile[0] == 0) + mGame.hiScore = 10000; + // Comprueba el checksum para ver si se ha modificado el fichero + else if (mGame.scoreDataFile[0] % 43 == mGame.scoreDataFile[1]) + mGame.hiScore = mGame.scoreDataFile[0]; + else + mGame.hiScore = 10000; + + return success; +} + +// Carga el fichero de configuración +bool GameDirector::loadConfigFile() +{ + // Indicador de éxito en la carga + bool success = true; + std::string path = mProg.executablePath + "/"; + std::string p = mBinFile[BINFILE_CONFIG].file.c_str(); + std::string filename = p.substr(p.find_last_of("\\/") + 1); + SDL_RWops *file = SDL_RWFromFile(p.c_str(), "r+b"); + + // El fichero no existe + if (file == NULL) + { + printf("Warning: Unable to open %s file\n", filename.c_str()); + + // Creamos el fichero para escritura + file = SDL_RWFromFile(p.c_str(), "w+b"); + if (file != NULL) + { + printf("New file (%s) created!\n", filename.c_str()); + + // Inicializamos los datos + mOptions.fullScreenMode = 0; + SDL_RWwrite(file, &mOptions.fullScreenMode, sizeof(mOptions.fullScreenMode), 1); + + mOptions.windowSize = 3; + SDL_RWwrite(file, &mOptions.windowSize, sizeof(mOptions.windowSize), 1); + + // Cerramos el fichero + SDL_RWclose(file); + } + else + { + printf("Error: Unable to create file %s\n", filename.c_str()); + success = false; + } + } + // El fichero existe + else + { + // Cargamos los datos + printf("Reading file %s\n", filename.c_str()); + SDL_RWread(file, &mOptions.fullScreenMode, sizeof(mOptions.fullScreenMode), 1); + SDL_SetWindowFullscreen(mWindow, mOptions.fullScreenMode); + SDL_RWread(file, &mOptions.windowSize, sizeof(mOptions.windowSize), 1); + SDL_SetWindowSize(mWindow, SCREEN_WIDTH * mOptions.windowSize, SCREEN_HEIGHT * mOptions.windowSize); + + // Cierra el fichero + SDL_RWclose(file); + } + + return success; +} + +// Carga el fichero de datos para la demo +bool GameDirector::loadDemoFile() +{ + // Indicador de éxito en la carga + bool success = true; + std::string path = mProg.executablePath + "/"; + std::string p = mBinFile[BINFILE_DEMO].file.c_str(); + std::string filename = p.substr(p.find_last_of("\\/") + 1); + SDL_RWops *file = SDL_RWFromFile(p.c_str(), "r+b"); + + // El fichero no existe + if (file == NULL) + { + printf("Warning: Unable to open %s file\n", filename.c_str()); + + // Creamos el fichero para escritura + file = SDL_RWFromFile(p.c_str(), "w+b"); + if (file != NULL) + { + printf("New file (%s) created!\n", filename.c_str()); + + // Inicializamos los datos + for (int i = 0; i < TOTAL_DEMO_DATA; ++i) + { + mDemo.keys.left = 0; + mDemo.keys.right = 0; + mDemo.keys.noInput = 0; + mDemo.keys.fire = 0; + mDemo.keys.fireLeft = 0; + mDemo.keys.fireRight = 0; + mDemo.dataFile[i] = mDemo.keys; + SDL_RWwrite(file, &mDemo.dataFile[i], sizeof(DemoKeys), 1); + } + + // Cerramos el fichero + SDL_RWclose(file); + } + else + { + printf("Error: Unable to create file %s\n", filename.c_str()); + success = false; + } + } + // El fichero existe + else + { + // Cargamos los datos + printf("Reading file %s\n", filename.c_str()); + for (int i = 0; i < TOTAL_DEMO_DATA; ++i) + SDL_RWread(file, &mDemo.dataFile[i], sizeof(DemoKeys), 1); + + // Cierra el fichero + SDL_RWclose(file); + } + + return success; +} + +// Guarda el fichero de puntos +bool GameDirector::saveScoreFile() +{ + bool success = true; + std::string path = mProg.executablePath + "/"; + std::string p = mBinFile[BINFILE_SCORE].file; + std::string filename = p.substr(p.find_last_of("\\/") + 1); + SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b"); + if (file != NULL) + { + // Guardamos los datos + for (int i = 0; i < TOTAL_SCORE_DATA; ++i) + { + SDL_RWwrite(file, &mGame.scoreDataFile[i], sizeof(Uint32), 1); + } + + printf("Writing file %s\n", filename.c_str()); + + // Cerramos el fichero + SDL_RWclose(file); } else { - printf("\n** File is missing. Exiting.\n\n"); + printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError()); + } + return success; +} + +// Guarda el fichero de configuración +bool GameDirector::saveConfigFile() +{ + bool success = true; + std::string path = mProg.executablePath + "/"; + std::string p = mBinFile[BINFILE_CONFIG].file; + std::string filename = p.substr(p.find_last_of("\\/") + 1); + SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b"); + if (file != NULL) + { + // Guarda los datos + SDL_RWwrite(file, &mOptions.fullScreenMode, sizeof(Uint32), 1); + SDL_RWwrite(file, &mOptions.windowSize, sizeof(Uint8), 1); + + printf("Writing file %s\n", filename.c_str()); + + // Cierra el fichero + SDL_RWclose(file); + } + else + { + printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError()); + } + return success; +} + +// Guarda el fichero de datos para la demo +bool GameDirector::saveDemoFile() +{ + bool success = true; + std::string path = mProg.executablePath + "/"; + std::string p = mBinFile[BINFILE_DEMO].file; + std::string filename = p.substr(p.find_last_of("\\/") + 1); + if (mDemo.recording) + { + SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b"); + if (file != NULL) + { + // Guardamos los datos + for (int i = 0; i < TOTAL_DEMO_DATA; ++i) + { + SDL_RWwrite(file, &mDemo.dataFile[i], sizeof(DemoKeys), 1); + } + + printf("Writing file %s\n", filename.c_str()); + + // Cerramos el fichero + SDL_RWclose(file); + } + else + { + printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError()); + } } return success; } @@ -935,466 +1476,16 @@ bool GameDirector::loadTextureFromFile(LTexture *texture, std::string path, SDL_ return success; } -// Carga los recursos necesarios -bool GameDirector::loadMedia(Uint8 section) +// Comprueba el valor de la variable 'quit' +bool GameDirector::exit() { - // Indicador de éxito en la carga - bool success = true; - std::string path = mExecutablePath + "/"; - std::string p; - - switch (section) - { - case GAME_STATE_INIT: - { - p = mBinFile[BINFILE_SCORE].file.c_str(); - std::string filename = p.substr(p.find_last_of("\\/") + 1); - // Abrimos el fichero con la puntuacion para leer en binario - SDL_RWops *file = SDL_RWFromFile(p.c_str(), "r+b"); - - // El fichero no existe - if (file == NULL) - { - printf("Warning: Unable to open %s file\n", filename.c_str()); - - // Creamos el fichero para escritura - file = SDL_RWFromFile(p.c_str(), "w+b"); - if (file != NULL) - { - printf("New file (%s) created!\n", filename.c_str()); - - // Inicializamos los datos - for (int i = 0; i < TOTAL_SCORE_DATA; ++i) - { - mScoreDataFile[i] = 0; - SDL_RWwrite(file, &mScoreDataFile[i], sizeof(Uint32), 1); - } - - // Cerramos el fichero - SDL_RWclose(file); - } - else - { - printf("Error: Unable to create file %s\n", filename.c_str()); - success = false; - } - } - // El fichero existe - else - { - // Cargamos los datos - printf("Reading file %s\n", filename.c_str()); - for (int i = 0; i < TOTAL_SCORE_DATA; ++i) - { - SDL_RWread(file, &mScoreDataFile[i], sizeof(Uint32), 1); - } - - // Cierra el fichero - SDL_RWclose(file); - } - - p = mBinFile[BINFILE_DEMO].file.c_str(); - filename = p.substr(p.find_last_of("\\/") + 1); - // Abrimos el fichero con los datos para el modo demo para leer en binario - file = SDL_RWFromFile(p.c_str(), "r+b"); - - // El fichero no existe - if (file == NULL) - { - printf("Warning: Unable to open %s file\n", filename.c_str()); - - // Creamos el fichero para escritura - file = SDL_RWFromFile(p.c_str(), "w+b"); - if (file != NULL) - { - printf("New file (%s) created!\n", filename.c_str()); - - // Inicializamos los datos - for (int i = 0; i < TOTAL_DEMO_DATA; ++i) - { - mDemoKeys.left = 0; - mDemoKeys.right = 0; - mDemoKeys.noInput = 0; - mDemoKeys.fire = 0; - mDemoKeys.fireLeft = 0; - mDemoKeys.fireRight = 0; - mDemoDataFile[i] = mDemoKeys; - SDL_RWwrite(file, &mDemoDataFile[i], sizeof(DemoKeys), 1); - } - - // Cerramos el fichero - SDL_RWclose(file); - } - else - { - printf("Error: Unable to create file %s\n", filename.c_str()); - success = false; - } - } - // El fichero existe - else - { - // Cargamos los datos - printf("Reading file %s\n", filename.c_str()); - for (int i = 0; i < TOTAL_DEMO_DATA; ++i) - { - SDL_RWread(file, &mDemoDataFile[i], sizeof(DemoKeys), 1); - } - - // Cierra el fichero - SDL_RWclose(file); - } - - p = mBinFile[BINFILE_CONFIG].file.c_str(); - filename = p.substr(p.find_last_of("\\/") + 1); - // Abrimos el fichero con la configuracion de las opciones leer en binario - file = SDL_RWFromFile(p.c_str(), "r+b"); - - // El fichero no existe - if (file == NULL) - { - printf("Warning: Unable to open %s file\n", filename.c_str()); - - // Creamos el fichero para escritura - file = SDL_RWFromFile(p.c_str(), "w+b"); - if (file != NULL) - { - printf("New file (%s) created!\n", filename.c_str()); - - // Inicializamos los datos - mFullScreenMode = 0; - SDL_RWwrite(file, &mFullScreenMode, sizeof(mFullScreenMode), 1); - - mWindowSize = 3; - SDL_RWwrite(file, &mWindowSize, sizeof(mWindowSize), 1); - - // Cerramos el fichero - SDL_RWclose(file); - } - else - { - printf("Error: Unable to create file %s\n", filename.c_str()); - success = false; - } - } - // El fichero existe - else - { - // Cargamos los datos - printf("Reading file %s\n", filename.c_str()); - SDL_RWread(file, &mFullScreenMode, sizeof(mFullScreenMode), 1); - SDL_SetWindowFullscreen(mWindow, mFullScreenMode); - SDL_RWread(file, &mWindowSize, sizeof(mWindowSize), 1); - SDL_SetWindowSize(mWindow, SCREEN_WIDTH * mWindowSize, SCREEN_HEIGHT * mWindowSize); - - // Cierra el fichero - SDL_RWclose(file); - } - printf("\n"); - - // Texturas - loadTextureFromFile(mTexture[TEXTURE_MENU].texture, mTexture[TEXTURE_MENU].file, mRenderer); - loadTextureFromFile(mTexture[TEXTURE_FONT_WHITE].texture, mTexture[TEXTURE_FONT_WHITE].file, mRenderer); - loadTextureFromFile(mTexture[TEXTURE_FONT_BLACK].texture, mTexture[TEXTURE_FONT_BLACK].file, mRenderer); - loadTextureFromFile(mTexture[TEXTURE_FONT_NOKIA].texture, mTexture[TEXTURE_FONT_NOKIA].file, mRenderer); - - // Sonidos - mSound[SOUND_MENU_SELECT].sound = JA_LoadSound(mSound[SOUND_MENU_SELECT].file.c_str()); - mSound[SOUND_MENU_CANCEL].sound = JA_LoadSound(mSound[SOUND_MENU_CANCEL].file.c_str()); - mSound[SOUND_MENU_MOVE].sound = JA_LoadSound(mSound[SOUND_MENU_MOVE].file.c_str()); - - // Musicas - mMusic[MUSIC_TITLE].music = JA_LoadMusic(mMusic[MUSIC_TITLE].file.c_str()); - } - break; - - case GAME_STATE_TITLE: - { - // Texturas - loadTextureFromFile(mTexture[TEXTURE_TITLE].texture, mTexture[TEXTURE_TITLE].file, mRenderer); - - // Crea el mosaico de fondo del titulo - SDL_SetRenderTarget(mRenderer, mTitleSurface); - SDL_SetRenderDrawColor(mRenderer, 0x43, 0x43, 0x4F, 0xFF); - SDL_RenderClear(mRenderer); - for (Uint8 i = 0; i < 8; i++) - for (Uint8 j = 0; j < 6; j++) - { - mTitleTile->setPosX(i * 64); - mTitleTile->setPosY(j * 64); - mTitleTile->render(); - } - mBackgroundWindow.x = 0; - mBackgroundWindow.y = 0; - mBackgroundWindow.w = SCREEN_WIDTH; - mBackgroundWindow.h = SCREEN_HEIGHT; - SDL_SetRenderTarget(mRenderer, nullptr); - - // Sonidos - mSound[SOUND_TITLE].sound = JA_LoadSound(mSound[SOUND_TITLE].file.c_str()); - } - break; - - case GAME_STATE_PLAYING: - { - // Texturas - loadTextureFromFile(mTexture[TEXTURE_PLAYER_LEGS].texture, mTexture[TEXTURE_PLAYER_LEGS].file, mRenderer); - loadTextureFromFile(mTexture[TEXTURE_PLAYER_BODY].texture, mTexture[TEXTURE_PLAYER_BODY].file, mRenderer); - loadTextureFromFile(mTexture[TEXTURE_PLAYER_DEATH].texture, mTexture[TEXTURE_PLAYER_DEATH].file, mRenderer); - loadTextureFromFile(mTexture[TEXTURE_BALLOON].texture, mTexture[TEXTURE_BALLOON].file, mRenderer); - loadTextureFromFile(mTexture[TEXTURE_BULLET].texture, mTexture[TEXTURE_BULLET].file, mRenderer); - loadTextureFromFile(mTexture[TEXTURE_GAME_BG].texture, mTexture[TEXTURE_GAME_BG].file, mRenderer); - loadTextureFromFile(mTexture[TEXTURE_GAME_TEXT].texture, mTexture[TEXTURE_GAME_TEXT].file, mRenderer); - loadTextureFromFile(mTexture[TEXTURE_ITEMS].texture, mTexture[TEXTURE_ITEMS].file, mRenderer); - - // Sonidos - mSound[SOUND_BALLON].sound = JA_LoadSound(mSound[SOUND_BALLON].file.c_str()); - mSound[SOUND_BULLET].sound = JA_LoadSound(mSound[SOUND_BULLET].file.c_str()); - mSound[SOUND_PLAYER_COLLISION].sound = JA_LoadSound(mSound[SOUND_PLAYER_COLLISION].file.c_str()); - mSound[SOUND_HISCORE].sound = JA_LoadSound(mSound[SOUND_HISCORE].file.c_str()); - mSound[SOUND_ITEM_DROP].sound = JA_LoadSound(mSound[SOUND_ITEM_DROP].file.c_str()); - mSound[SOUND_ITEM_PICKUP].sound = JA_LoadSound(mSound[SOUND_ITEM_PICKUP].file.c_str()); - mSound[SOUND_COFFEE_OUT].sound = JA_LoadSound(mSound[SOUND_COFFEE_OUT].file.c_str()); - mSound[SOUND_STAGE_CHANGE].sound = JA_LoadSound(mSound[SOUND_STAGE_CHANGE].file.c_str()); - mSound[SOUND_TITLE].sound = JA_LoadSound(mSound[SOUND_TITLE].file.c_str()); - mSound[SOUND_BUBBLE1].sound = JA_LoadSound(mSound[SOUND_BUBBLE1].file.c_str()); - mSound[SOUND_BUBBLE2].sound = JA_LoadSound(mSound[SOUND_BUBBLE2].file.c_str()); - mSound[SOUND_BUBBLE3].sound = JA_LoadSound(mSound[SOUND_BUBBLE3].file.c_str()); - mSound[SOUND_BUBBLE4].sound = JA_LoadSound(mSound[SOUND_BUBBLE4].file.c_str()); - - // Musicas - mMusic[MUSIC_PLAYING].music = JA_LoadMusic(mMusic[MUSIC_PLAYING].file.c_str()); - } - break; - - case GAME_STATE_GAME_OVER_SCREEN: - { - } - break; - - case GAME_STATE_INTRO: - { - // Texturas - loadTextureFromFile(mTexture[TEXTURE_INTRO].texture, mTexture[TEXTURE_INTRO].file, mRenderer); - - // Musicas - mMusic[MUSIC_INTRO].music = JA_LoadMusic(mMusic[MUSIC_INTRO].file.c_str()); - } - break; - - case GAME_STATE_DEMO: - { - } - break; - - case GAME_STATE_INSTRUCTIONS: - { - // Texturas - loadTextureFromFile(mTexture[TEXTURE_ITEMS].texture, mTexture[TEXTURE_ITEMS].file, mRenderer); - } - break; - - case GAME_STATE_LOGO: - { - // Texturas - loadTextureFromFile(mTexture[TEXTURE_LOGO].texture, mTexture[TEXTURE_LOGO].file, mRenderer); - } - break; - - default: - { - } - break; - } - - return success; -} - -// Descrga los recursos necesarios -bool GameDirector::unLoadMedia(Uint8 section) -{ - // Indicador de éxito en la carga - bool success = true; - std::string path = mExecutablePath + "/"; - std::string p; - - switch (section) - { - case GAME_STATE_INIT: - { - p = mBinFile[BINFILE_SCORE].file; - std::string filename = p.substr(p.find_last_of("\\/") + 1); - // Abre el fichero de puntuación para escribir - SDL_RWops *file = SDL_RWFromFile(p.c_str(), "w+b"); - if (file != NULL) - { - // Guardamos los datos - for (int i = 0; i < TOTAL_SCORE_DATA; ++i) - { - SDL_RWwrite(file, &mScoreDataFile[i], sizeof(Uint32), 1); - } - - printf("Writing file %s\n", filename.c_str()); - - // Cerramos el fichero - SDL_RWclose(file); - } - else - { - printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError()); - } - - p = mBinFile[BINFILE_CONFIG].file; - filename = p.substr(p.find_last_of("\\/") + 1); - // Abre el fichero de puntuación para escribir - SDL_RWops *file2 = SDL_RWFromFile(p.c_str(), "w+b"); - if (file != NULL) - { - // Guardamos los datos - SDL_RWwrite(file2, &mFullScreenMode, sizeof(Uint32), 1); - SDL_RWwrite(file2, &mWindowSize, sizeof(Uint8), 1); - - printf("Writing file %s\n", filename.c_str()); - - // Cerramos el fichero - SDL_RWclose(file2); - } - else - { - printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError()); - } - - p = mBinFile[BINFILE_DEMO].file; - filename = p.substr(p.find_last_of("\\/") + 1); - // Abre el fichero de demo para escribir - if (mDemoRecording) - { - SDL_RWops *file2 = SDL_RWFromFile(p.c_str(), "w+b"); - if (file2 != NULL) - { - // Guardamos los datos - for (int i = 0; i < TOTAL_DEMO_DATA; ++i) - { - SDL_RWwrite(file2, &mDemoDataFile[i], sizeof(DemoKeys), 1); - } - - printf("Writing file %s\n", filename.c_str()); - - // Cerramos el fichero - SDL_RWclose(file2); - } - else - { - printf("Error: Unable to save %s file! %s\n", filename.c_str(), SDL_GetError()); - } - } - printf("\n"); - - // Texturas - mTexture[TEXTURE_MENU].texture->free(); - mTexture[TEXTURE_FONT_WHITE].texture->free(); - mTexture[TEXTURE_FONT_BLACK].texture->free(); - mTexture[TEXTURE_FONT_NOKIA].texture->free(); - - // Sonidos - JA_DeleteSound(mSound[SOUND_MENU_SELECT].sound); - JA_DeleteSound(mSound[SOUND_MENU_CANCEL].sound); - JA_DeleteSound(mSound[SOUND_MENU_MOVE].sound); - - // Musicas - JA_DeleteMusic(mMusic[MUSIC_TITLE].music); - } - break; - - case GAME_STATE_TITLE: - { - // Texturas - mTexture[TEXTURE_TITLE].texture->free(); - - // Sonidos - JA_DeleteSound(mSound[SOUND_TITLE].sound); - } - break; - - case GAME_STATE_PLAYING: - { - // Texturas - mTexture[TEXTURE_PLAYER_LEGS].texture->free(); - mTexture[TEXTURE_PLAYER_BODY].texture->free(); - mTexture[TEXTURE_PLAYER_DEATH].texture->free(); - mTexture[TEXTURE_BALLOON].texture->free(); - mTexture[TEXTURE_BULLET].texture->free(); - mTexture[TEXTURE_GAME_BG].texture->free(); - mTexture[TEXTURE_GAME_TEXT].texture->free(); - mTexture[TEXTURE_ITEMS].texture->free(); - - // Sonidos - JA_DeleteSound(mSound[SOUND_BALLON].sound); - JA_DeleteSound(mSound[SOUND_BULLET].sound); - JA_DeleteSound(mSound[SOUND_PLAYER_COLLISION].sound); - JA_DeleteSound(mSound[SOUND_HISCORE].sound); - JA_DeleteSound(mSound[SOUND_ITEM_DROP].sound); - JA_DeleteSound(mSound[SOUND_ITEM_PICKUP].sound); - JA_DeleteSound(mSound[SOUND_COFFEE_OUT].sound); - JA_DeleteSound(mSound[SOUND_STAGE_CHANGE].sound); - JA_DeleteSound(mSound[SOUND_TITLE].sound); - JA_DeleteSound(mSound[SOUND_BUBBLE1].sound); - JA_DeleteSound(mSound[SOUND_BUBBLE2].sound); - JA_DeleteSound(mSound[SOUND_BUBBLE3].sound); - JA_DeleteSound(mSound[SOUND_BUBBLE4].sound); - - // Musicas - JA_DeleteMusic(mMusic[MUSIC_PLAYING].music); - } - break; - - case GAME_STATE_GAME_OVER_SCREEN: - { - } - break; - - case GAME_STATE_INTRO: - { - // Texturas - mTexture[TEXTURE_INTRO].texture->free(); - - // Musicas - JA_DeleteMusic(mMusic[MUSIC_INTRO].music); - } - break; - - case GAME_STATE_DEMO: - { - } - break; - - case GAME_STATE_INSTRUCTIONS: - { - // Texturas - mTexture[TEXTURE_ITEMS].texture->free(); - } - break; - - case GAME_STATE_LOGO: - { - // Texturas - mTexture[TEXTURE_LOGO].texture->free(); - } - break; - - default: - { - } - break; - } - - return success; + return mProg.quit; } // Establece el valor de la variable void GameDirector::setExecutablePath(std::string path) { - mExecutablePath = path.substr(0, path.find_last_of("\\/")); + mProg.executablePath = path.substr(0, path.find_last_of("\\/")); } // Establece el valor de la variable @@ -1419,8 +1510,8 @@ void GameDirector::updateHiScore() mGame.hiScore = mGame.score; // Almacena la máxima puntuación en el fichero junto con un checksum - mScoreDataFile[0] = mGame.hiScore; - mScoreDataFile[1] = mGame.hiScore % 43; + mGame.scoreDataFile[0] = mGame.hiScore; + mGame.scoreDataFile[1] = mGame.hiScore % 43; // Si superamos la máxima puntuación if (mGame.hiScoreAchieved == false) @@ -1474,7 +1565,7 @@ void GameDirector::renderScoreBoard() mText.white->write(SCORE_NUMBER_X, SCORE_NUMBER_Y, updateScoreText(mGame.score), 0); mText.white->write(MULTIPLIER_WORD_X, MULTIPLIER_WORD_Y, "MULT", 0); - mText.white->write(MULTIPLIER_NUMBER_X, MULTIPLIER_NUMBER_Y, std::to_string(mPlayer->getScoreMultiplier()), 0, 3); + mText.white->write(MULTIPLIER_NUMBER_X, MULTIPLIER_NUMBER_Y, std::to_string(mGame.player->getScoreMultiplier()), 0, 3); mText.white->write(HISCORE_WORD_X, HISCORE_WORD_Y, "HI-SCORE", 0); mText.white->write(HISCORE_NUMBER_X, HISCORE_NUMBER_Y, updateScoreText(mGame.hiScore), 0); @@ -1489,19 +1580,19 @@ void GameDirector::updateStage() { mGame.stage = realStage; JA_PlaySound(mSound[SOUND_STAGE_CHANGE].sound); - mGame.stageCounter = STAGE_COUNTER; + mGame.stageCounter = 0; } - if (mGame.stageCounter > 0) + if (mGame.stageCounter < STAGE_COUNTER) { - mGame.stageCounter--; + mGame.stageCounter++; } } // Actualiza el estado de muerte void GameDirector::updateDeath() { - if (!mPlayer->isAlive()) + if (!mGame.player->isAlive()) { if (mGame.deathCounter > 0) { @@ -1532,36 +1623,36 @@ void GameDirector::updateDeath() // Animación if ((mGame.deathCounter / 5) % 2 == 0) { - mSmartSprite[mGame.deathIndex]->setSpriteClip(0, 0, 24, 24); + mGame.smartSprite[mGame.deathIndex]->setSpriteClip(0, 0, 24, 24); } else { - mSmartSprite[mGame.deathIndex]->setSpriteClip(24, 0, 24, 24); + mGame.smartSprite[mGame.deathIndex]->setSpriteClip(24, 0, 24, 24); } // Rebote en los laterales - if (mSmartSprite[mGame.deathIndex]->getVelX() > 0) + if (mGame.smartSprite[mGame.deathIndex]->getVelX() > 0) { - if (mSmartSprite[mGame.deathIndex]->getPosX() > (SCREEN_WIDTH - mSmartSprite[mGame.deathIndex]->getWidth())) + if (mGame.smartSprite[mGame.deathIndex]->getPosX() > (SCREEN_WIDTH - mGame.smartSprite[mGame.deathIndex]->getWidth())) { - mSmartSprite[mGame.deathIndex]->setPosX(SCREEN_WIDTH - mSmartSprite[mGame.deathIndex]->getWidth()); - mSmartSprite[mGame.deathIndex]->setVelX(mSmartSprite[mGame.deathIndex]->getVelX() * (-1)); - mSmartSprite[mGame.deathIndex]->setDestX(mSmartSprite[mGame.deathIndex]->getDestX() * (-1)); + mGame.smartSprite[mGame.deathIndex]->setPosX(SCREEN_WIDTH - mGame.smartSprite[mGame.deathIndex]->getWidth()); + mGame.smartSprite[mGame.deathIndex]->setVelX(mGame.smartSprite[mGame.deathIndex]->getVelX() * (-1)); + mGame.smartSprite[mGame.deathIndex]->setDestX(mGame.smartSprite[mGame.deathIndex]->getDestX() * (-1)); } } else { - if (mSmartSprite[mGame.deathIndex]->getPosX() < 0) + if (mGame.smartSprite[mGame.deathIndex]->getPosX() < 0) { - mSmartSprite[mGame.deathIndex]->setPosX(0); - mSmartSprite[mGame.deathIndex]->setVelX(mSmartSprite[mGame.deathIndex]->getVelX() * (-1)); - mSmartSprite[mGame.deathIndex]->setDestX(mSmartSprite[mGame.deathIndex]->getDestX() * (-1)); + mGame.smartSprite[mGame.deathIndex]->setPosX(0); + mGame.smartSprite[mGame.deathIndex]->setVelX(mGame.smartSprite[mGame.deathIndex]->getVelX() * (-1)); + mGame.smartSprite[mGame.deathIndex]->setDestX(mGame.smartSprite[mGame.deathIndex]->getDestX() * (-1)); } } } else { - setGameStatus(GAME_STATE_GAME_OVER_SCREEN); + mGame.section = GAME_SECTION_GAMEOVER; } } } @@ -1569,7 +1660,7 @@ void GameDirector::updateDeath() // Renderiza el fade final cuando se acaba la partida void GameDirector::renderDeathFade() { - if (!mPlayer->isAlive() && (mGame.deathCounter < 150)) + if (!mGame.player->isAlive() && (mGame.deathCounter < 150)) { // 192 / 6 = 32, 6 cuadrados de 32 pixeles SDL_Rect rect[12]; @@ -1594,9 +1685,9 @@ void GameDirector::moveBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - if (mBalloon[i]->isActive()) + if (mGame.balloon[i]->isActive()) { - mBalloon[i]->update(); + mGame.balloon[i]->update(); } } } @@ -1606,23 +1697,23 @@ void GameDirector::renderBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - if (mBalloon[i]->isActive()) + if (mGame.balloon[i]->isActive()) { - mBalloon[i]->render(); - if ((mDebug) && (mBalloon[i]->isPopping() == false)) - mText.white->write(mBalloon[i]->getPosX() + (mBalloon[i]->getWidth() / 2) - 4, mBalloon[i]->getPosY() - 8, std::to_string(i)); + mGame.balloon[i]->render(); + if ((mProg.debug) && (mGame.balloon[i]->isPopping() == false)) + mText.white->write(mGame.balloon[i]->getPosX() + (mGame.balloon[i]->getWidth() / 2) - 4, mGame.balloon[i]->getPosY() - 8, std::to_string(i)); } } } // Devuelve el primer indice no activo del vector de globos -Uint8 GameDirector::getBallonFreeIndex() +Uint8 GameDirector::getBalloonFreeIndex() { int index = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - if (mBalloon[i]->isActive() == false) + if (mGame.balloon[i]->isActive() == false) { index = i; break; @@ -1635,8 +1726,8 @@ Uint8 GameDirector::getBallonFreeIndex() // Crea un globo nuevo en el vector de globos Uint8 GameDirector::createNewBalloon(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, LTexture *texture) { - Uint8 index = getBallonFreeIndex(); - mBalloon[index]->init(x, y, kind, velx, creationtimer, texture, mRenderer); + Uint8 index = getBalloonFreeIndex(); + mGame.balloon[index]->init(x, y, kind, velx, creationtimer, texture, mRenderer); return index; } @@ -1645,63 +1736,63 @@ void GameDirector::resetBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - mBalloon[i]->erase(); + mGame.balloon[i]->erase(); } } // Explosiona un globo. Lo destruye y crea otros dos si es el caso void GameDirector::popBalloon(Uint8 index) { - if (mBalloon[index]->isActive()) + if (mGame.balloon[index]->isActive()) { // Otorga los puntos correspondientes al globo - mPlayer->addScore(Uint32(mBalloon[index]->getScore() * mPlayer->getScoreMultiplier())); - setScore(mPlayer->getScore()); + mGame.player->addScore(Uint32(mGame.balloon[index]->getScore() * mGame.player->getScoreMultiplier())); + setScore(mGame.player->getScore()); updateHiScore(); - Uint8 kind = mBalloon[index]->getKind(); + Uint8 kind = mGame.balloon[index]->getKind(); Uint8 freeIndex = 0; switch (kind) { // Si es del tipo más pequeño, simplemente elimina el globo case BALLOON_1: - mBalloon[index]->pop(); + mGame.balloon[index]->pop(); break; // En cualquier otro caso, crea dos globos de un tipo inferior default: - freeIndex = getBallonFreeIndex(); - mBalloon[freeIndex]->init(0, mBalloon[index]->getPosY(), mBalloon[index]->getKind() - 1, BALLON_VELX_NEGATIVE, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); - mBalloon[freeIndex]->allignTo(mBalloon[index]->getPosX() + (mBalloon[index]->getWidth() / 2)); - mBalloon[freeIndex]->setVelY(-2.5); - // if (isTimeStopped()) mBalloon[freeIndex]->setStop(true); + freeIndex = getBalloonFreeIndex(); + mGame.balloon[freeIndex]->init(0, mGame.balloon[index]->getPosY(), mGame.balloon[index]->getKind() - 1, BALLOON_VELX_NEGATIVE, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); + mGame.balloon[freeIndex]->allignTo(mGame.balloon[index]->getPosX() + (mGame.balloon[index]->getWidth() / 2)); + mGame.balloon[freeIndex]->setVelY(-2.5); + // if (isTimeStopped()) mGame.balloon[freeIndex]->setStop(true); - freeIndex = getBallonFreeIndex(); - mBalloon[freeIndex]->init(0, mBalloon[index]->getPosY(), mBalloon[index]->getKind() - 1, BALLON_VELX_POSITIVE, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); - mBalloon[freeIndex]->allignTo(mBalloon[index]->getPosX() + (mBalloon[index]->getWidth() / 2)); - mBalloon[freeIndex]->setVelY(-2.5); - // if (isTimeStopped()) mBalloon[freeIndex]->setStop(true); + freeIndex = getBalloonFreeIndex(); + mGame.balloon[freeIndex]->init(0, mGame.balloon[index]->getPosY(), mGame.balloon[index]->getKind() - 1, BALLOON_VELX_POSITIVE, 0, mTexture[TEXTURE_BALLOON].texture, mRenderer); + mGame.balloon[freeIndex]->allignTo(mGame.balloon[index]->getPosX() + (mGame.balloon[index]->getWidth() / 2)); + mGame.balloon[freeIndex]->setVelY(-2.5); + // if (isTimeStopped()) mGame.balloon[freeIndex]->setStop(true); // Elimina el globo - mBalloon[index]->pop(); + mGame.balloon[index]->pop(); break; } // Reproduce el sonido de explotar el globo // JA_PlaySound(mFX.popBalloon); // Recalcula el nivel de amenaza - calculateMenaceLevel(); + setMenaceLevel(); } } // Explosiona todos los globos -void GameDirector::popAllBallons() +void GameDirector::popAllBalloons() { int candidate[MAX_BALLOONS]; Uint8 j = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - if ((mBalloon[i]->isActive()) && (mBalloon[i]->isPopping() == false) && (mBalloon[i]->isBeingCreated() == false)) + if ((mGame.balloon[i]->isActive()) && (mGame.balloon[i]->isPopping() == false) && (mGame.balloon[i]->isBeingCreated() == false)) { candidate[j] = i; } @@ -1718,7 +1809,7 @@ void GameDirector::popAllBallons() popBalloon(i); } } - JA_PlaySound(mSound[SOUND_BALLON].sound); + JA_PlaySound(mSound[SOUND_BALLOON].sound); } // Detiene todos los globos @@ -1726,10 +1817,10 @@ void GameDirector::stopAllBalloons(Uint16 time) { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - if (mBalloon[i]->isActive()) + if (mGame.balloon[i]->isActive()) { - mBalloon[i]->setStop(true); - mBalloon[i]->setStoppedTimer(time); + mGame.balloon[i]->setStop(true); + mGame.balloon[i]->setStoppedTimer(time); } } } @@ -1739,10 +1830,10 @@ void GameDirector::startAllBalloons() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - if ((mBalloon[i]->isActive()) && (mBalloon[i]->isBeingCreated() == false)) + if ((mGame.balloon[i]->isActive()) && (mGame.balloon[i]->isBeingCreated() == false)) { - mBalloon[i]->setStop(false); - mBalloon[i]->setStoppedTimer(0); + mGame.balloon[i]->setStop(false); + mGame.balloon[i]->setStoppedTimer(0); } } } @@ -1753,7 +1844,7 @@ Uint8 GameDirector::countBalloons() Uint8 num = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - if (mBalloon[i]->isActive()) + if (mGame.balloon[i]->isActive()) { ++num; } @@ -1762,16 +1853,16 @@ Uint8 GameDirector::countBalloons() } // Comprueba la colisión entre el jugador y los globos activos -bool GameDirector::checkPlayerBallonCollision() +bool GameDirector::checkPlayerBalloonCollision() { bool result = false; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - //if ((mBalloon[i]->isActive()) && (mBalloon[i]->isStopped() == false)) - if ((mBalloon[i]->isActive()) && !(mBalloon[i]->isStopped()) && !(mBalloon[i]->isInvulnerable())) + //if ((mGame.balloon[i]->isActive()) && (mGame.balloon[i]->isStopped() == false)) + if ((mGame.balloon[i]->isActive()) && !(mGame.balloon[i]->isStopped()) && !(mGame.balloon[i]->isInvulnerable())) { - if (checkCollision(mPlayer->getCollider(), mBalloon[i]->getCollider())) + if (checkCollision(mGame.player->getCollider(), mGame.balloon[i]->getCollider())) { result = true; break; @@ -1784,34 +1875,34 @@ bool GameDirector::checkPlayerBallonCollision() // Comprueba la colisión entre el jugador y los items void GameDirector::checkPlayerItemCollision() { - if (mPlayer->isAlive()) + if (mGame.player->isAlive()) for (Uint8 i = 0; i < MAX_ITEMS; i++) { - if (mItem[i]->isEnabled()) + if (mGame.item[i]->isEnabled()) { - if (checkCollision(mPlayer->getCollider(), mItem[i]->getCollider())) + if (checkCollision(mGame.player->getCollider(), mGame.item[i]->getCollider())) { - switch (mItem[i]->getClass()) + switch (mGame.item[i]->getClass()) { case ITEM_POINTS_1_DISK: - mPlayer->addScore(1000); - setScore(mPlayer->getScore()); + mGame.player->addScore(1000); + setScore(mGame.player->getScore()); updateHiScore(); - createItemScoreSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m1000Bitmap->getWidth() / 2), mItem[i]->getPosY(), m1000Bitmap); + createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._1000Bitmap->getWidth() / 2), mGame.item[i]->getPosY(), mGame._1000Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_POINTS_2_GAVINA: - mPlayer->addScore(2500); - setScore(mPlayer->getScore()); + mGame.player->addScore(2500); + setScore(mGame.player->getScore()); updateHiScore(); - createItemScoreSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m2500Bitmap->getWidth() / 2), mItem[i]->getPosY(), m2500Bitmap); + createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._2500Bitmap->getWidth() / 2), mGame.item[i]->getPosY(), mGame._2500Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_POINTS_3_PACMAR: - mPlayer->addScore(5000); - setScore(mPlayer->getScore()); + mGame.player->addScore(5000); + setScore(mGame.player->getScore()); updateHiScore(); - createItemScoreSprite(mItem[i]->getPosX() + (mItem[i]->getWidth() / 2) - (m5000Bitmap->getWidth() / 2), mItem[i]->getPosY(), m5000Bitmap); + createItemScoreSprite(mGame.item[i]->getPosX() + (mGame.item[i]->getWidth() / 2) - (mGame._5000Bitmap->getWidth() / 2), mGame.item[i]->getPosY(), mGame._5000Bitmap); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_CLOCK: @@ -1819,45 +1910,45 @@ void GameDirector::checkPlayerItemCollision() JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; case ITEM_TNT: - popAllBallons(); + popAllBalloons(); setExplosionTime(true); JA_PlaySound(mSound[SOUND_TITLE].sound); break; case ITEM_COFFEE: - mPlayer->giveExtraHit(); + mGame.player->giveExtraHit(); JA_PlaySound(mSound[SOUND_ITEM_PICKUP].sound); break; default: break; } - mItem[i]->erase(); + mGame.item[i]->erase(); } } } } // Comprueba y procesa la colisión entre las balas y los globos -void GameDirector::checkBulletBallonCollision() +void GameDirector::checkBulletBalloonCollision() { for (Uint8 i = 0; i < MAX_BALLOONS; i++) { for (Uint8 j = 0; j < MAX_BULLETS; j++) { - if (mBalloon[i]->isActive() && !(mBalloon[i]->isInvulnerable()) && mBullet[j]->isActive()) + if (mGame.balloon[i]->isActive() && !(mGame.balloon[i]->isInvulnerable()) && mGame.bullet[j]->isActive()) { - if (checkCollision(mBalloon[i]->getCollider(), mBullet[j]->getCollider())) + if (checkCollision(mGame.balloon[i]->getCollider(), mGame.bullet[j]->getCollider())) { - mPlayer->incScoreMultiplier(); + mGame.player->incScoreMultiplier(); popBalloon(i); - if (mDemo == false) - JA_PlaySound(mSound[SOUND_BALLON].sound); - mBullet[j]->erase(); - calculateMenaceLevel(); + if (mDemo.enabled == false) + JA_PlaySound(mSound[SOUND_BALLOON].sound); + mGame.bullet[j]->erase(); + setMenaceLevel(); Uint8 droppeditem = dropItem(); - if ((droppeditem != NO_KIND) && (mDemo == false) && (mDemoRecording == false)) + if ((droppeditem != NO_KIND) && (mDemo.enabled == false) && (mDemo.recording == false)) { - createItem(mBalloon[i]->getPosX(), mBalloon[i]->getPosY(), droppeditem); + createItem(mGame.balloon[i]->getPosX(), mGame.balloon[i]->getPosY(), droppeditem); JA_PlaySound(mSound[SOUND_ITEM_DROP].sound); } break; @@ -1872,11 +1963,11 @@ void GameDirector::moveBullets() { for (Uint8 i = 0; i < MAX_BULLETS; i++) { - if (mBullet[i]->isActive()) + if (mGame.bullet[i]->isActive()) { - if (mBullet[i]->move() == MSG_BULLET_OUT) + if (mGame.bullet[i]->move() == MSG_BULLET_OUT) { - mPlayer->decScoreMultiplier(); + mGame.player->decScoreMultiplier(); } } } @@ -1887,9 +1978,9 @@ void GameDirector::renderBullets() { for (Uint8 i = 0; i < MAX_BULLETS; i++) { - if (mBullet[i]->isActive()) + if (mGame.bullet[i]->isActive()) { - mBullet[i]->render(); + mGame.bullet[i]->render(); } } } @@ -1901,7 +1992,7 @@ Uint8 GameDirector::getBulletFreeIndex() for (int i = 0; i < MAX_BULLETS; i++) { - if (mBullet[i]->isActive() == false) + if (mGame.bullet[i]->isActive() == false) { index = i; break; @@ -1916,14 +2007,14 @@ void GameDirector::resetBullets() { for (Uint8 i = 0; i < MAX_BULLETS; i++) { - mBullet[i]->erase(); + mGame.bullet[i]->erase(); } } // Crea un objeto bala void GameDirector::createBullet(int x, int y, Uint8 kind) { - mBullet[getBulletFreeIndex()]->init(x, y, kind, mTexture[TEXTURE_BULLET].texture, mRenderer); + mGame.bullet[getBulletFreeIndex()]->init(x, y, kind, mTexture[TEXTURE_BULLET].texture, mRenderer); } // Actualiza los items @@ -1931,7 +2022,7 @@ void GameDirector::updateItems() { for (Uint8 i = 0; i < MAX_ITEMS; i++) { - mItem[i]->update(); + mGame.item[i]->update(); } } @@ -1940,7 +2031,7 @@ void GameDirector::renderItems() { for (Uint8 i = 0; i < MAX_ITEMS; i++) { - mItem[i]->render(); + mGame.item[i]->render(); } } @@ -1951,7 +2042,7 @@ Uint8 GameDirector::getItemFreeIndex() for (int i = 0; i < MAX_ITEMS; i++) { - if (mItem[i]->getClass() == NO_KIND) + if (mGame.item[i]->getClass() == NO_KIND) { index = i; break; @@ -1966,7 +2057,7 @@ void GameDirector::resetItems() { for (Uint8 i = 0; i < MAX_ITEMS; i++) { - mItem[i]->erase(); + mGame.item[i]->erase(); } } @@ -2023,40 +2114,40 @@ Uint8 GameDirector::dropItem() // Crea un objeto item void GameDirector::createItem(int x, int y, Uint8 kind) { - mItem[getItemFreeIndex()]->init(kind, x, y, mTexture[TEXTURE_ITEMS].texture, mRenderer); + mGame.item[getItemFreeIndex()]->init(kind, x, y, mTexture[TEXTURE_ITEMS].texture, mRenderer); } // Crea un objeto SmartSprite para mostrar la puntuación al coger un objeto void GameDirector::createItemScoreSprite(int x, int y, SmartSprite *sprite) { Uint8 index = getSmartSpriteFreeIndex(); - *mSmartSprite[index] = *sprite; - mSmartSprite[index]->setPosX(x); - mSmartSprite[index]->setPosY(y); - mSmartSprite[index]->setDestX(x); - mSmartSprite[index]->setDestY(y - 15); - mSmartSprite[index]->setEnabled(true); - mSmartSprite[index]->setEnabledTimer(100); + *mGame.smartSprite[index] = *sprite; + mGame.smartSprite[index]->setPosX(x); + mGame.smartSprite[index]->setPosY(y); + mGame.smartSprite[index]->setDestX(x); + mGame.smartSprite[index]->setDestY(y - 15); + mGame.smartSprite[index]->setEnabled(true); + mGame.smartSprite[index]->setEnabledTimer(100); } // Crea un SmartSprite para arrojar el item café al recibir un impacto void GameDirector::throwCoffee(int x, int y) { Uint8 index = getSmartSpriteFreeIndex(); - mSmartSprite[index]->init(mTexture[TEXTURE_ITEMS].texture, mRenderer); - mSmartSprite[index]->setPosX(x - 8); - mSmartSprite[index]->setPosY(y - 8); - mSmartSprite[index]->setWidth(16); - mSmartSprite[index]->setHeight(16); - mSmartSprite[index]->setVelX(-1.0f + ((rand() % 5) * 0.5f)); - mSmartSprite[index]->setVelY(-4.0f); - mSmartSprite[index]->setAccelX(0.0f); - mSmartSprite[index]->setAccelY(0.2f); - mSmartSprite[index]->setDestX(x + (mSmartSprite[index]->getVelX() * 50)); - mSmartSprite[index]->setDestY(SCREEN_HEIGHT + 1); - mSmartSprite[index]->setEnabled(true); - mSmartSprite[index]->setEnabledTimer(1); - mSmartSprite[index]->setSpriteClip(80, 16, 16, 16); + mGame.smartSprite[index]->init(mTexture[TEXTURE_ITEMS].texture, mRenderer); + mGame.smartSprite[index]->setPosX(x - 8); + mGame.smartSprite[index]->setPosY(y - 8); + mGame.smartSprite[index]->setWidth(16); + mGame.smartSprite[index]->setHeight(16); + mGame.smartSprite[index]->setVelX(-1.0f + ((rand() % 5) * 0.5f)); + mGame.smartSprite[index]->setVelY(-4.0f); + mGame.smartSprite[index]->setAccelX(0.0f); + mGame.smartSprite[index]->setAccelY(0.2f); + mGame.smartSprite[index]->setDestX(x + (mGame.smartSprite[index]->getVelX() * 50)); + mGame.smartSprite[index]->setDestY(SCREEN_HEIGHT + 1); + mGame.smartSprite[index]->setEnabled(true); + mGame.smartSprite[index]->setEnabledTimer(1); + mGame.smartSprite[index]->setSpriteClip(80, 16, 16, 16); } // Crea un SmartSprite para arrojar el item café al recibir un impacto @@ -2064,20 +2155,20 @@ void GameDirector::throwPlayer(int x, int y) { int sentit = ((rand() % 2) ? 1 : -1); mGame.deathIndex = getSmartSpriteFreeIndex(); - mSmartSprite[mGame.deathIndex]->init(mTexture[TEXTURE_PLAYER_DEATH].texture, mRenderer); - mSmartSprite[mGame.deathIndex]->setPosX(x); - mSmartSprite[mGame.deathIndex]->setPosY(y); - mSmartSprite[mGame.deathIndex]->setWidth(24); - mSmartSprite[mGame.deathIndex]->setHeight(24); - mSmartSprite[mGame.deathIndex]->setVelX(2.0f * sentit); - mSmartSprite[mGame.deathIndex]->setVelY(-5.0f); - mSmartSprite[mGame.deathIndex]->setAccelX(0.0f); - mSmartSprite[mGame.deathIndex]->setAccelY(0.2f); - mSmartSprite[mGame.deathIndex]->setDestX(SCREEN_WIDTH * sentit); - mSmartSprite[mGame.deathIndex]->setDestY(SCREEN_HEIGHT + 1); - mSmartSprite[mGame.deathIndex]->setEnabled(true); - mSmartSprite[mGame.deathIndex]->setEnabledTimer(1); - mSmartSprite[mGame.deathIndex]->setSpriteClip(0, 0, 24, 24); + mGame.smartSprite[mGame.deathIndex]->init(mTexture[TEXTURE_PLAYER_DEATH].texture, mRenderer); + mGame.smartSprite[mGame.deathIndex]->setPosX(x); + mGame.smartSprite[mGame.deathIndex]->setPosY(y); + mGame.smartSprite[mGame.deathIndex]->setWidth(24); + mGame.smartSprite[mGame.deathIndex]->setHeight(24); + mGame.smartSprite[mGame.deathIndex]->setVelX(2.0f * sentit); + mGame.smartSprite[mGame.deathIndex]->setVelY(-5.0f); + mGame.smartSprite[mGame.deathIndex]->setAccelX(0.0f); + mGame.smartSprite[mGame.deathIndex]->setAccelY(0.2f); + mGame.smartSprite[mGame.deathIndex]->setDestX(SCREEN_WIDTH * sentit); + mGame.smartSprite[mGame.deathIndex]->setDestY(SCREEN_HEIGHT + 1); + mGame.smartSprite[mGame.deathIndex]->setEnabled(true); + mGame.smartSprite[mGame.deathIndex]->setEnabledTimer(1); + mGame.smartSprite[mGame.deathIndex]->setSpriteClip(0, 0, 24, 24); } // Actualiza los SmartSprites @@ -2085,7 +2176,7 @@ void GameDirector::updateSmartSprites() { for (int i = 0; i < MAX_SMART_SPRITES; i++) { - mSmartSprite[i]->update(); + mGame.smartSprite[i]->update(); } } @@ -2094,7 +2185,7 @@ void GameDirector::renderSmartSprites() { for (int i = 0; i < MAX_SMART_SPRITES; i++) { - mSmartSprite[i]->render(); + mGame.smartSprite[i]->render(); } } @@ -2105,7 +2196,7 @@ Uint8 GameDirector::getSmartSpriteFreeIndex() for (int i = 0; i < MAX_SMART_SPRITES; i++) { - if (mSmartSprite[i]->isEnabled() == false) + if (mGame.smartSprite[i]->isEnabled() == false) { index = i; break; @@ -2120,20 +2211,74 @@ void GameDirector::resetSmartSprites() { for (Uint8 i = 0; i < MAX_SMART_SPRITES; i++) { - mSmartSprite[i]->erase(); + mGame.smartSprite[i]->erase(); } } +#ifndef UNUSED +// Deshabilita todas las gotas de café +void GameDirector::resetCoffeeDrops() +{ + for (Uint8 i = 0; i < MAX_COFFEE_DROPS; i++) + { + mCoffeeDrop[i]->disable(); + } +} + +// Actualiza las gotas de cafe +void GameDirector::updateCoffeeDrops() +{ + for (Uint8 i = 0; i < MAX_COFFEE_DROPS; i++) + { + mCoffeeDrop[i]->update(); + } +} + +// Dibuja las gotas de cafe +void GameDirector::renderCoffeeDrops() +{ + for (Uint8 i = 0; i < MAX_COFFEE_DROPS; i++) + { + mCoffeeDrop[i]->render(); + } +} + +// Devuelve el primer indice libre del vector de CoffeeDrops +Uint8 GameDirector::getCoffeDropFreeIndex() +{ + Uint8 index = 0; + + for (Uint8 i = 0; i < MAX_COFFEE_DROPS; i++) + { + if (!(mCoffeeDrop[i]->isEnabled())) + { + index = i; + break; + } + } + return index; +} + +// Crea un numero de gotas de cafe +void GameDirector::createCoffeDrops(Uint8 num, int x, int y) +{ + for (Uint8 i = 0; i < num; i++) + { + mCoffeeDrop[getCoffeDropFreeIndex()]->init(mTexture[TEXTURE_GAME_BG].texture, mRenderer, x, y, ((rand() % 7) * 0.5f) - 1.5f, (rand() % 7) * (-0.5f) + 1.0f, PLAY_AREA_BOTTOM); + } +} +#endif + // Acciones a realizar cuando el jugador muere void GameDirector::killPlayer() { - if (mPlayer->hasExtraHit()) + if (mGame.player->hasExtraHit()) { - mPlayer->removeExtraHit(); - throwCoffee(mPlayer->getPosX() + (mPlayer->getWidth() / 2), mPlayer->getPosY() + (mPlayer->getHeight() / 2)); + mGame.player->removeExtraHit(); + throwCoffee(mGame.player->getPosX() + (mGame.player->getWidth() / 2), mGame.player->getPosY() + (mGame.player->getHeight() / 2)); JA_PlaySound(mSound[SOUND_COFFEE_OUT].sound); } - else if (!mPlayer->isInvulnerable()) + else if (!mGame.player->isInvulnerable()) { stopAllBalloons(10); // 5000 JA_StopMusic(); @@ -2141,66 +2286,72 @@ void GameDirector::killPlayer() shakeScreen(); SDL_Delay(500); // 1000 JA_PlaySound(mSound[SOUND_COFFEE_OUT].sound); - throwPlayer(mPlayer->getPosX(), mPlayer->getPosY()); - mPlayer->setAlive(false); + throwPlayer(mGame.player->getPosX(), mGame.player->getPosY()); + mGame.player->setAlive(false); } } -// Calcula y establece el valor de amenaza en funcion de los globos activos -void GameDirector::calculateMenaceLevel() +// Obtiene el valor de la variable +Uint8 GameDirector::getSubsection() { - mMenaceLevel = 0; + return mProg.subsection; +} + +// Calcula y establece el valor de amenaza en funcion de los globos activos +void GameDirector::setMenaceLevel() +{ + mGame.menaceLevelCurrent = 0; for (Uint8 i = 0; i < MAX_BALLOONS; i++) { - mMenaceLevel += mBalloon[i]->getMenace(); + mGame.menaceLevelCurrent += mGame.balloon[i]->getMenace(); } } // Obtiene el valor de la variable Uint8 GameDirector::getMenaceLevel() { - return mMenaceLevel; + return mGame.menaceLevelCurrent; } // Obtiene el valor de la variable bool GameDirector::isPlayFieldDrawOnly() { - return mPlayFieldDrawOnly; + return mGame.playFieldDrawOnly; } // Establece el valor de la variable void GameDirector::setPlayFieldDrawOnly(bool state) { - mPlayFieldDrawOnly = state; + mGame.playFieldDrawOnly = state; } // Establece el valor de la variable void GameDirector::setTimeStopped(bool value) { - mTimeStopped = value; + mGame.timeStopped = value; } // Obtiene el valor de la variable bool GameDirector::isTimeStopped() { - return mTimeStopped; + return mGame.timeStopped; } // Establece el valor de la variable -void GameDirector::setTimeStoppedTimer(Uint16 value) +void GameDirector::setTimeStoppedCounter(Uint16 value) { - mTimeStoppedTimer = value; + mGame.timeStoppedCounter = value; } // Actualiza y comprueba el valor de la variable -void GameDirector::updateTimeStoppedTimer() +void GameDirector::updateTimeStoppedCounter() { if (isTimeStopped()) { - if (mTimeStoppedTimer > 0) + if (mGame.timeStoppedCounter > 0) { - mTimeStoppedTimer--; - stopAllBalloons(TIME_STOPPED_TIMER); + mGame.timeStoppedCounter--; + stopAllBalloons(TIME_STOPPED_COUNTER); } else { @@ -2212,41 +2363,41 @@ void GameDirector::updateTimeStoppedTimer() // Establece el valor de la variable void GameDirector::setExplosionTime(bool value) { - mExplosionTime = value; + mGame.explosionTime = value; } // Obtiene el valor de la variable bool GameDirector::isExplosionTime() { - return mExplosionTime; + return mGame.explosionTime; } // Establece el valor de la variable void GameDirector::setRemainingExplosions(Uint8 value) { - mRemainingExplosions = value; + mGame.remainingExplosions = value; } // Actualiza y comprueba el valor de la variable -void GameDirector::updateRemainingExplosionsTimer() +void GameDirector::updateRemainingExplosionsCounter() { if (isExplosionTime()) { - if (mRemainingExplosionsTimer > 0) + if (mGame.remainingExplosionsCounter > 0) { - --mRemainingExplosionsTimer; + --mGame.remainingExplosionsCounter; } - else if (mRemainingExplosions > 0) + else if (mGame.remainingExplosions > 0) { - popAllBallons(); - --mRemainingExplosions; - mRemainingExplosionsTimer = REMAINING_EXPLOSIONS_TIMER; + popAllBalloons(); + --mGame.remainingExplosions; + mGame.remainingExplosionsCounter = REMAINING_EXPLOSIONS_COUNTER; } else { - mExplosionTime = false; - mRemainingExplosions = REMAINING_EXPLOSIONS; - mRemainingExplosionsTimer = REMAINING_EXPLOSIONS_TIMER; + mGame.explosionTime = false; + mGame.remainingExplosions = REMAINING_EXPLOSIONS; + mGame.remainingExplosionsCounter = REMAINING_EXPLOSIONS_COUNTER; } } } @@ -2260,7 +2411,7 @@ void GameDirector::updatePlayField() checkGameInput(); // Actualiza el jugador - mPlayer->update(); + mGame.player->update(); // Mueve los globos moveBalloons(); @@ -2281,65 +2432,61 @@ void GameDirector::updatePlayField() updateSmartSprites(); // Actualiza los contadores de estado - updateTimeStoppedTimer(); - updateRemainingExplosionsTimer(); + updateTimeStoppedCounter(); + updateRemainingExplosionsCounter(); // Comprueba las colisiones entre globos y balas - checkBulletBallonCollision(); + checkBulletBalloonCollision(); // Comprueba las colisiones entre elk jugador y los items checkPlayerItemCollision(); // Comprueba el nivel de amenaza - checkMenaceLevel(); + updateMenaceLevel(); // Comprueba la colisión entre el jugador y los globos - if (checkPlayerBallonCollision()) + if (checkPlayerBalloonCollision()) { - if (mPlayer->isAlive()) + if (mGame.player->isAlive()) { - if (mDemo) - { - mGame.status = GAME_STATE_INSTRUCTIONS; - } + if (mDemo.enabled) + setProgSection(PROG_SECTION_TITLE, TITLE_SECTION_INSTRUCTIONS); else - { killPlayer(); - } } } } else { // Actualiza el mensaje de GetReady - mGetReadyBitmap->update(); + mGame.getReadyBitmap->update(); } } // Actualiza el fondo void GameDirector::updateBackground() { - mGBClouds1->move(); - mGBClouds1b->move(); - mGBClouds2->move(); - mGBClouds2b->move(); - if (mGBClouds1->getPosX() < -mGBClouds1->getWidth()) + mGame.clouds1a->move(); + mGame.clouds1b->move(); + mGame.clouds2a->move(); + mGame.clouds2b->move(); + if (mGame.clouds1a->getPosX() < -mGame.clouds1a->getWidth()) { - mGBClouds1->setPosX(mGBClouds1->getWidth()); + mGame.clouds1a->setPosX(mGame.clouds1a->getWidth()); } - if (mGBClouds1b->getPosX() < -mGBClouds1b->getWidth()) + if (mGame.clouds1b->getPosX() < -mGame.clouds1b->getWidth()) { - mGBClouds1b->setPosX(mGBClouds1b->getWidth()); + mGame.clouds1b->setPosX(mGame.clouds1b->getWidth()); } - if (mGBClouds2->getPosX() < -mGBClouds2->getWidth()) + if (mGame.clouds2a->getPosX() < -mGame.clouds2a->getWidth()) { - mGBClouds2->setPosX(mGBClouds2->getWidth()); + mGame.clouds2a->setPosX(mGame.clouds2a->getWidth()); } - if (mGBClouds2b->getPosX() < -mGBClouds2b->getWidth()) + if (mGame.clouds2b->getPosX() < -mGame.clouds2b->getWidth()) { - mGBClouds2b->setPosX(mGBClouds2b->getWidth()); + mGame.clouds2b->setPosX(mGame.clouds2b->getWidth()); } - mGrass->setSpriteClip(256, 85 + (6 * (mGameCounter / 20 % 2)), SCREEN_WIDTH, 6); + mGame.grass->setSpriteClip(256, 85 + (6 * (mGame.counter / 20 % 2)), SCREEN_WIDTH, 6); } // Dibuja el fondo @@ -2353,21 +2500,21 @@ void GameDirector::renderBackground() } updateBackground(); - mGameBackgroundSky->setSpriteClip(SCREEN_WIDTH, 192, SCREEN_WIDTH, SCREEN_HEIGHT); + mGame.gradient->setSpriteClip(SCREEN_WIDTH, 192, SCREEN_WIDTH, SCREEN_HEIGHT); mTexture[TEXTURE_GAME_BG].texture->setAlpha(255); - mGameBackgroundSky->render(); + mGame.gradient->render(); - mGameBackgroundSky->setSpriteClip(0, 192, SCREEN_WIDTH, SCREEN_HEIGHT); + mGame.gradient->setSpriteClip(0, 192, SCREEN_WIDTH, SCREEN_HEIGHT); mTexture[TEXTURE_GAME_BG].texture->setAlpha(alpha); - mGameBackgroundSky->render(); + mGame.gradient->render(); mTexture[TEXTURE_GAME_BG].texture->setAlpha(255); - mGBClouds1->render(); - mGBClouds1b->render(); - mGBClouds2->render(); - mGBClouds2b->render(); - mGameBackgroundFront->render(); - mGrass->render(); + mGame.clouds1a->render(); + mGame.clouds1b->render(); + mGame.clouds2a->render(); + mGame.clouds2b->render(); + mGame.background->render(); + mGame.grass->render(); } // Dibuja el campo de juego @@ -2378,9 +2525,9 @@ void GameDirector::renderPlayField() renderBullets(); renderItems(); renderSmartSprites(); - mPlayer->render(); + mGame.player->render(); renderDeathFade(); - if (!mDemo) + if (!mDemo.enabled) { renderScoreBoard(); } @@ -2396,32 +2543,32 @@ void GameDirector::renderPlayField() } // Gestiona el nivel de amenaza -void GameDirector::checkMenaceLevel() +void GameDirector::updateMenaceLevel() { // Aumenta el nivel de amenaza en función de la puntuación - mMenaceLevelThreshold = 7 + (4 * (mGame.score / 10000)); + mGame.menaceLevelThreshold = 7 + (4 * (mGame.score / 10000)); // Si el nivel de amenza es inferior al umbral - if (mMenaceLevel < mMenaceLevelThreshold) + if (mGame.menaceLevelCurrent < mGame.menaceLevelThreshold) { Uint8 index = 0; // Obtiene el centro del jugador en el eje X - int x = mPlayer->getPosX() + (mPlayer->getWidth() / 2); + int x = mGame.player->getPosX() + (mGame.player->getWidth() / 2); // Crea un globo sobre el jugador en dirección hacia el centro if (x < (PLAY_AREA_WIDTH / 2)) { - index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_POSITIVE, 400, mTexture[TEXTURE_BALLOON].texture); + index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLOON_VELX_POSITIVE, 400, mTexture[TEXTURE_BALLOON].texture); } else { - index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_NEGATIVE, 400, mTexture[TEXTURE_BALLOON].texture); + index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLOON_VELX_NEGATIVE, 400, mTexture[TEXTURE_BALLOON].texture); } - mBalloon[index]->allignTo(x); + mGame.balloon[index]->allignTo(x); // Recalcula el nivel de amenaza con el nuevo globo - calculateMenaceLevel(); + setMenaceLevel(); } } @@ -2489,175 +2636,170 @@ void GameDirector::checkGameInput() // Obtiene el estado de las teclas pulsadas del teclado keystates = SDL_GetKeyboardState(NULL); - mDemoKeys.left = 0; - mDemoKeys.right = 0; - mDemoKeys.noInput = 0; - mDemoKeys.fire = 0; - mDemoKeys.fireLeft = 0; - mDemoKeys.fireRight = 0; + mDemo.keys.left = 0; + mDemo.keys.right = 0; + mDemo.keys.noInput = 0; + mDemo.keys.fire = 0; + mDemo.keys.fireLeft = 0; + mDemo.keys.fireRight = 0; // Modo Demo activo - if (mDemo) + if (mDemo.enabled) { - if (mDemoDataFile[mDemoCounter].left == 1) - mPlayer->setInput(INPUT_LEFT); - if (mDemoDataFile[mDemoCounter].right == 1) - mPlayer->setInput(INPUT_RIGHT); - if (mDemoDataFile[mDemoCounter].noInput == 1) - mPlayer->setInput(NO_INPUT); - if (mDemoDataFile[mDemoCounter].fire == 1) - if (mPlayer->canFire()) + if (mDemo.dataFile[mDemo.counter].left == 1) + mGame.player->setInput(INPUT_LEFT); + if (mDemo.dataFile[mDemo.counter].right == 1) + mGame.player->setInput(INPUT_RIGHT); + if (mDemo.dataFile[mDemo.counter].noInput == 1) + mGame.player->setInput(NO_INPUT); + if (mDemo.dataFile[mDemo.counter].fire == 1) + if (mGame.player->canFire()) { - mPlayer->setInput(INPUT_FIRE_UP); - createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP); - mPlayer->setFireCooldown(10); + mGame.player->setInput(INPUT_FIRE_UP); + createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_UP); + mGame.player->setFireCooldown(10); } - if (mDemoDataFile[mDemoCounter].fireLeft == 1) - if (mPlayer->canFire()) + if (mDemo.dataFile[mDemo.counter].fireLeft == 1) + if (mGame.player->canFire()) { - mPlayer->setInput(INPUT_FIRE_LEFT); - createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP); - mPlayer->setFireCooldown(10); + mGame.player->setInput(INPUT_FIRE_LEFT); + createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_UP); + mGame.player->setFireCooldown(10); } - if (mDemoDataFile[mDemoCounter].fireRight == 1) - if (mPlayer->canFire()) + if (mDemo.dataFile[mDemo.counter].fireRight == 1) + if (mGame.player->canFire()) { - mPlayer->setInput(INPUT_FIRE_RIGHT); - createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP); - mPlayer->setFireCooldown(10); + mGame.player->setInput(INPUT_FIRE_RIGHT); + createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_UP); + mGame.player->setFireCooldown(10); } // Comprobamos la tecla o el botón de pausa/menu - if ((keystates[mKeyboard.accept] != 0) || (keystates[mKeyboard.cancel] != 0) || (checkGameController(INPUT_PAUSE)) || (checkGameController(INPUT_ACCEPT)) || (checkGameController(INPUT_CANCEL))) + if ((keystates[mProg.keyboard.accept] != 0) || (keystates[mProg.keyboard.cancel] != 0) || (checkGameController(INPUT_PAUSE)) || (checkGameController(INPUT_ACCEPT)) || (checkGameController(INPUT_CANCEL))) { - setGameStatus(GAME_STATE_TITLE); + setProgSection(PROG_SECTION_TITLE); } - if (mDemoCounter < TOTAL_DEMO_DATA) - { - mDemoCounter++; - } + if (mDemo.counter < TOTAL_DEMO_DATA) + mDemo.counter++; else - { - mGame.status = GAME_STATE_INSTRUCTIONS; - //mDemoCounter = 0; - } + setProgSection(PROG_SECTION_TITLE, TITLE_SECTION_INSTRUCTIONS); } // Modo Demo no activo - else if (mPlayer->isAlive()) + else if (mGame.player->isAlive()) { // Tecla izquierda o el mando hacia la izquierda - if ((keystates[mKeyboard.left] != 0) || (checkGameController(INPUT_LEFT))) + if ((keystates[mProg.keyboard.left] != 0) || (checkGameController(INPUT_LEFT))) { - mPlayer->setInput(INPUT_LEFT); - mDemoKeys.left = 1; + mGame.player->setInput(INPUT_LEFT); + mDemo.keys.left = 1; } else { // Tecla derecha o el mando hacia la derecha - if ((keystates[mKeyboard.right] != 0) || (checkGameController(INPUT_RIGHT))) + if ((keystates[mProg.keyboard.right] != 0) || (checkGameController(INPUT_RIGHT))) { - mPlayer->setInput(INPUT_RIGHT); - mDemoKeys.right = 1; + mGame.player->setInput(INPUT_RIGHT); + mDemo.keys.right = 1; } else { // Ninguna de las dos direcciones pulsadas - mPlayer->setInput(NO_INPUT); - mDemoKeys.noInput = 1; + mGame.player->setInput(NO_INPUT); + mDemo.keys.noInput = 1; } } // Comprueba la tecla o el botón de disparo central - if ((keystates[mKeyboard.fire] != 0) || (checkGameController(INPUT_FIRE_UP))) + if ((keystates[mProg.keyboard.fire] != 0) || (checkGameController(INPUT_FIRE_UP))) { - if (mPlayer->canFire()) + if (mGame.player->canFire()) { - mPlayer->setInput(INPUT_FIRE_UP); - createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_UP); - mPlayer->setFireCooldown(10); + mGame.player->setInput(INPUT_FIRE_UP); + createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_UP); + mGame.player->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSound[SOUND_BULLET].sound); - mDemoKeys.fire = 1; + mDemo.keys.fire = 1; } } // Comprueba la tecla o el botón de disparo izquierdo - if ((keystates[mKeyboard.fireLeft] != 0) || (checkGameController(INPUT_FIRE_LEFT))) + if ((keystates[mProg.keyboard.fireLeft] != 0) || (checkGameController(INPUT_FIRE_LEFT))) { - if (mPlayer->canFire()) + if (mGame.player->canFire()) { - mPlayer->setInput(INPUT_FIRE_LEFT); - createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_LEFT); - mPlayer->setFireCooldown(10); + mGame.player->setInput(INPUT_FIRE_LEFT); + createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_LEFT); + mGame.player->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSound[SOUND_BULLET].sound); - mDemoKeys.fireLeft = 1; + mDemo.keys.fireLeft = 1; } } // Comprueba la tecla o el botón de disparo derecho - if ((keystates[mKeyboard.fireRight] != 0) || (checkGameController(INPUT_FIRE_RIGHT))) + if ((keystates[mProg.keyboard.fireRight] != 0) || (checkGameController(INPUT_FIRE_RIGHT))) { - if (mPlayer->canFire()) + if (mGame.player->canFire()) { - mPlayer->setInput(INPUT_FIRE_RIGHT); - createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY() + (mPlayer->getHeight() / 2), BULLET_RIGHT); - mPlayer->setFireCooldown(10); + mGame.player->setInput(INPUT_FIRE_RIGHT); + createBullet(mGame.player->getPosX() + (mGame.player->getWidth() / 2) - 4, mGame.player->getPosY() + (mGame.player->getHeight() / 2), BULLET_RIGHT); + mGame.player->setFireCooldown(10); // Reproduce el sonido de disparo JA_PlaySound(mSound[SOUND_BULLET].sound); - mDemoKeys.fireRight = 1; + mDemo.keys.fireRight = 1; } } // Para la pausa actuaremos cuando se suelte la tecla de pausa // Comprueba la tecla o el botón de pausa/menu y su buffer - if (((keystates[mKeyboard.pause] != 0) || (checkGameController(INPUT_PAUSE))) && (mKeyboardBuffer.pause == 0)) //|| ((mGameControllerFound) && (SDL_JoystickGetButton(mGameController, BUTTON_START)))) + if (((keystates[mProg.keyboard.pause] != 0) || (checkGameController(INPUT_PAUSE))) && (mProg.keyboardBuffer.pause == 0)) //|| ((mGameControllerFound) && (SDL_JoystickGetButton(mGameController, BUTTON_START)))) { // Se acaba de pulsar la tecla. Lo anotamos en su buffer - mKeyboardBuffer.pause = 1; + mProg.keyboardBuffer.pause = 1; } else { // Aqui sigue la tecla pulsada, mantenemos el buffer activo - if (((keystates[mKeyboard.pause] != 0) || (checkGameController(INPUT_PAUSE))) && (mKeyboardBuffer.pause != 0)) + if (((keystates[mProg.keyboard.pause] != 0) || (checkGameController(INPUT_PAUSE))) && (mProg.keyboardBuffer.pause != 0)) { - mKeyboardBuffer.pause = 1; + mProg.keyboardBuffer.pause = 1; } else { // Se ha soltado la tecla, procedemos a ejecutar la accion - if (((keystates[mKeyboard.pause] == 0) || (!checkGameController(INPUT_PAUSE))) && (mKeyboardBuffer.pause != 0)) + if (((keystates[mProg.keyboard.pause] == 0) || (!checkGameController(INPUT_PAUSE))) && (mProg.keyboardBuffer.pause != 0)) { - //setGameStatus(GAME_STATE_PAUSED); - mGame.paused = true; + mGame.section = GAME_SECTION_PAUSE; if (JA_GetMusicState() == JA_MUSIC_PLAYING) { JA_PauseMusic(); } - mKeyboardBuffer.pause = 0; + mProg.keyboardBuffer.pause = 0; } } } - if (mDemoCounter < TOTAL_DEMO_DATA) + if (mDemo.counter < TOTAL_DEMO_DATA) { - if (mDemoRecording) + if (mDemo.recording) { - mDemoDataFile[mDemoCounter] = mDemoKeys; + mDemo.dataFile[mDemo.counter] = mDemo.keys; } - mDemoCounter++; + mDemo.counter++; } - else if (mDemoRecording) + else if (mDemo.recording) { - mGame.status = GAME_STATE_QUIT; + mProg.quit = true; + ; } } } @@ -2668,61 +2810,62 @@ void GameDirector::checkMenuInput(Menu *menu) // Obtiene el estado de las teclas pulsadas del teclado keystates = SDL_GetKeyboardState(NULL); - if (!mMenuKeyPressed) + if (!mMenu.keyPressed) { // Tecla arriba o el mando hacia arriba - if ((keystates[mKeyboard.up] != 0) || (checkGameController(INPUT_UP))) + if ((keystates[mProg.keyboard.up] != 0) || (checkGameController(INPUT_UP))) { if (menu->checkInput(INPUT_UP)) { - mMenuKeyPressed = true; + mMenu.keyPressed = true; JA_PlaySound(mSound[SOUND_MENU_MOVE].sound); } } // Tecla abajo o el mando hacia abajo - if ((keystates[mKeyboard.down] != 0) || (checkGameController(INPUT_DOWN))) + if ((keystates[mProg.keyboard.down] != 0) || (checkGameController(INPUT_DOWN))) { if (menu->checkInput(INPUT_DOWN)) { - mMenuKeyPressed = true; + mMenu.keyPressed = true; JA_PlaySound(mSound[SOUND_MENU_MOVE].sound); } } // Tecla o el botón de aceptar - if ((keystates[mKeyboard.accept] != 0) || (checkGameController(INPUT_ACCEPT))) + if ((keystates[mProg.keyboard.accept] != 0) || (checkGameController(INPUT_ACCEPT))) { menu->checkInput(INPUT_ACCEPT); - mMenuKeyPressed = true; + mMenu.keyPressed = true; } // Tecla o el botón de cancelar - if ((keystates[mKeyboard.cancel] != 0) || (checkGameController(INPUT_CANCEL))) + if ((keystates[mProg.keyboard.cancel] != 0) || (checkGameController(INPUT_CANCEL))) { menu->checkInput(INPUT_CANCEL); - mMenuKeyPressed = true; + mMenu.keyPressed = true; } } // Ningún botón, libera la variable - if ((keystates[mKeyboard.up] == 0) && (keystates[mKeyboard.down] == 0) && (keystates[mKeyboard.accept] == 0) && - (keystates[mKeyboard.cancel] == 0) && (checkGameController(NO_INPUT))) + if ((keystates[mProg.keyboard.up] == 0) && (keystates[mProg.keyboard.down] == 0) && (keystates[mProg.keyboard.accept] == 0) && + (keystates[mProg.keyboard.cancel] == 0) && (checkGameController(NO_INPUT))) { - mMenuKeyPressed = false; + mMenu.keyPressed = false; } } // Obtiene el valor de la variable -Uint8 GameDirector::getGameStatus() +Uint8 GameDirector::getProgSection() { - return mGame.status; + return mProg.section; } // Establece el valor de la variable -void GameDirector::setGameStatus(Uint8 status) +void GameDirector::setProgSection(Uint8 section, Uint8 subsection) { - mGame.status = status; + mProg.section = section; + mProg.subsection = subsection; } // Pinta una transición en pantalla @@ -2859,42 +3002,45 @@ void GameDirector::renderFade(Uint8 index) // Pinta diferentes mensajes en la pantalla void GameDirector::renderMessages() { - //mGetReadyBitmap->update(); - mGetReadyBitmap->render(); - if (!mGetReadyBitmap->isEnabled()) + // GETREADY! + mGame.getReadyBitmap->render(); + if (!mGame.getReadyBitmap->isEnabled()) setPlayFieldDrawOnly(false); - if (mTimeStopped) + // Time Stopped + if (mGame.timeStopped) { - if ((mTimeStoppedTimer > 100) || (mTimeStoppedTimer % 10 > 4)) + if ((mGame.timeStoppedCounter > 100) || (mGame.timeStoppedCounter % 10 > 4)) { - mText.black->writeCentered(PLAY_AREA_CENTER_X + 1, PLAY_AREA_FIRST_QUARTER_Y + 1, "Time Stopped: " + std::to_string(mTimeStoppedTimer / 10), 0); - mText.white->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, "Time Stopped: " + std::to_string(mTimeStoppedTimer / 10), 0); + mText.black->writeCentered(PLAY_AREA_CENTER_X + 1, PLAY_AREA_FIRST_QUARTER_Y + 1, "Time Stopped: " + std::to_string(mGame.timeStoppedCounter / 10)); + mText.white->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, "Time Stopped: " + std::to_string(mGame.timeStoppedCounter / 10)); } } - if (mDemo) + // D E M O + if (mDemo.enabled) { - if (mDemoCounter % 30 > 14) + if (mDemo.counter % 30 > 14) { - mText.black->writeCentered(PLAY_AREA_CENTER_X + 1, PLAY_AREA_FIRST_QUARTER_Y + 1, "D E M O", 0); - mText.white->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, "D E M O", 0); + mText.black->writeCentered(PLAY_AREA_CENTER_X + 1, PLAY_AREA_FIRST_QUARTER_Y + 1, "D E M O"); + mText.white->writeCentered(PLAY_AREA_CENTER_X, PLAY_AREA_FIRST_QUARTER_Y, "D E M O"); } } - if (mGame.stageCounter > 0) + // STAGE NUMBER + if (mGame.stageCounter < STAGE_COUNTER) { - mText.black->writeCentered(PLAY_AREA_CENTER_X + 1, int(mGame.stagePath[mGame.stageCounter - 1]) + 1, "STAGE " + std::to_string(mGame.stage), 0); - mText.white->writeCentered(PLAY_AREA_CENTER_X, int(mGame.stagePath[mGame.stageCounter - 1]), "STAGE " + std::to_string(mGame.stage), 0); + mText.blackX2->writeCentered(PLAY_AREA_CENTER_X + 2, mGame.stagePath[mGame.stageCounter] + 2, "STAGE " + std::to_string(mGame.stage)); + mText.whiteX2->writeCentered(PLAY_AREA_CENTER_X, mGame.stagePath[mGame.stageCounter], "STAGE " + std::to_string(mGame.stage)); } } // Habilita el efecto del item de detener el tiempo void GameDirector::enableTimeStopItem() { - stopAllBalloons(TIME_STOPPED_TIMER); + stopAllBalloons(TIME_STOPPED_COUNTER); setTimeStopped(true); - setTimeStoppedTimer(TIME_STOPPED_TIMER); + setTimeStoppedCounter(TIME_STOPPED_COUNTER); if (JA_GetMusicState() == JA_MUSIC_PLAYING) { JA_PauseMusic(); @@ -2904,8 +3050,8 @@ void GameDirector::enableTimeStopItem() // Deshabilita el efecto del item de detener el tiempo void GameDirector::disableTimeStopItem() { - mTimeStopped = false; - mTimeStoppedTimer = TIME_STOPPED_TIMER; + mGame.timeStopped = false; + mGame.timeStoppedCounter = TIME_STOPPED_COUNTER; startAllBalloons(); if (JA_GetMusicState() == JA_MUSIC_PAUSED) { @@ -2916,20 +3062,20 @@ void GameDirector::disableTimeStopItem() // Cambia el valor de la variable de modo de pantalla completa void GameDirector::changeFullScreenMode() { - switch (mFullScreenMode) + switch (mOptions.fullScreenMode) { case 0: - mFullScreenMode = SDL_WINDOW_FULLSCREEN; + mOptions.fullScreenMode = SDL_WINDOW_FULLSCREEN; break; case SDL_WINDOW_FULLSCREEN: - mFullScreenMode = SDL_WINDOW_FULLSCREEN_DESKTOP; + mOptions.fullScreenMode = SDL_WINDOW_FULLSCREEN_DESKTOP; break; case SDL_WINDOW_FULLSCREEN_DESKTOP: - mFullScreenMode = 0; + mOptions.fullScreenMode = 0; break; default: - mFullScreenMode = 0; + mOptions.fullScreenMode = 0; break; } } @@ -2937,7 +3083,7 @@ void GameDirector::changeFullScreenMode() // Actualiza los elementos del menu de opciones void GameDirector::updateOptionsMenu() { - switch (mFullScreenMode) + switch (mOptions.fullScreenMode) { case 0: mMenu.options->setItemCaption(0, "WINDOWED"); @@ -2953,7 +3099,7 @@ void GameDirector::updateOptionsMenu() mMenu.options->setItemCaption(0, "WINDOWED"); break; } - mMenu.options->setItemCaption(1, "WINDOWS SIZE " + std::to_string(mWindowSize)); + mMenu.options->setItemCaption(1, "WINDOWS SIZE " + std::to_string(mOptions.windowSize)); mMenu.options->centerMenu(SCREEN_CENTER_X); mMenu.options->centerMenuElements(); } @@ -2969,25 +3115,25 @@ void GameDirector::shakeScreen() SDL_RenderClear(mRenderer); // Dibuja los objetos - mGameBackgroundFront->setPosX(0); - mGameBackgroundFront->setWidth(1); - mGameBackgroundFront->setSpriteClip(0, 0, 1, 192); + mGame.background->setPosX(0); + mGame.background->setWidth(1); + mGame.background->setSpriteClip(0, 0, 1, 192); renderBackground(); - mGameBackgroundFront->setPosX(255); - mGameBackgroundFront->setSpriteClip(255, 0, 1, 192); - mGameBackgroundFront->render(); + mGame.background->setPosX(255); + mGame.background->setSpriteClip(255, 0, 1, 192); + mGame.background->render(); - mGameBackgroundFront->setPosX(v[n]); - mGameBackgroundFront->setWidth(256); - mGameBackgroundFront->setSpriteClip(0, 0, 256, 192); - mGameBackgroundFront->render(); + mGame.background->setPosX(v[n]); + mGame.background->setWidth(256); + mGame.background->setSpriteClip(0, 0, 256, 192); + mGame.background->render(); - mGrass->render(); + mGame.grass->render(); renderBalloons(); renderBullets(); renderItems(); - mPlayer->render(); + mGame.player->render(); renderScoreBoard(); // Actualiza la pantalla @@ -2999,7 +3145,9 @@ void GameDirector::shakeScreen() // Bucle para el logo del juego void GameDirector::runLogo() { - while (mGame.status == GAME_STATE_LOGO) + initLogo(); + + while (mProg.section == PROG_SECTION_LOGO) { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) @@ -3007,14 +3155,14 @@ void GameDirector::runLogo() // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { - setGameStatus(GAME_STATE_QUIT); + mProg.quit = true; break; } // Cualquier tecla pulsada if ((mEventHandler->type == SDL_KEYDOWN) || (mEventHandler->type == SDL_JOYBUTTONDOWN)) { - setGameStatus(GAME_STATE_TITLE); + setProgSection(PROG_SECTION_TITLE); } } @@ -3054,10 +3202,10 @@ void GameDirector::runLogo() SDL_RenderPresent(mRenderer); // Comprueba si ha terminado el logo - if (SDL_GetTicks() - mTicks > mTicksSpeed) + if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks - mTicks = SDL_GetTicks(); + mProg.ticks = SDL_GetTicks(); if (mLogo.counter == 0) { @@ -3070,7 +3218,7 @@ void GameDirector::runLogo() if (mLogo.counter == 500) // minimo 200 + 255 { mLogo.counter = 0; - setGameStatus(GAME_STATE_INTRO); + setProgSection(PROG_SECTION_INTRO); } else { @@ -3078,11 +3226,15 @@ void GameDirector::runLogo() } } } + + quitLogo(); } // Bucle para la intro del juego void GameDirector::runIntro() { + initIntro(); + // Si la música no está sonando if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED)) { @@ -3090,12 +3242,12 @@ void GameDirector::runIntro() JA_PlayMusic(mMusic[MUSIC_INTRO].music, false); } - while (mGame.status == GAME_STATE_INTRO) + while (mProg.section == PROG_SECTION_INTRO) { - if (SDL_GetTicks() - mTicks > mTicksSpeed) + if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks - mTicks = SDL_GetTicks(); + mProg.ticks = SDL_GetTicks(); // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) @@ -3103,137 +3255,137 @@ void GameDirector::runIntro() // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { - setGameStatus(GAME_STATE_QUIT); + mProg.quit = true; break; } if ((mEventHandler->type == SDL_KEYDOWN) || (mEventHandler->type == SDL_JOYBUTTONDOWN)) { JA_StopMusic(); - setGameStatus(GAME_STATE_TITLE); + setProgSection(PROG_SECTION_TITLE); } } // Actualiza los objetos for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) { - mIntroBitmap[i]->update(); + mIntro.bitmap[i]->update(); } for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) { - mIntroText[i]->update(); + mIntro.text[i]->update(); } // Guión de eventos // Primera imagen - UPV - if (mIntroEvents[BITMAP0] == EVENT_WAITING) + if (mIntro.events[BITMAP0] == EVENT_WAITING) { - mIntroBitmap[0]->setEnabled(true); - mIntroEvents[BITMAP0] = EVENT_RUNNING; + mIntro.bitmap[0]->setEnabled(true); + mIntro.events[BITMAP0] = EVENT_RUNNING; } // Primer texto de la primera imagen - if ((mIntroEvents[BITMAP0] == EVENT_COMPLETED) && (mIntroEvents[TEXT0] == EVENT_WAITING)) + if ((mIntro.events[BITMAP0] == EVENT_COMPLETED) && (mIntro.events[TEXT0] == EVENT_WAITING)) { - mIntroText[0]->setEnabled(true); - mIntroEvents[TEXT0] = EVENT_RUNNING; + mIntro.text[0]->setEnabled(true); + mIntro.events[TEXT0] = EVENT_RUNNING; } // Segundo texto de la primera imagen - if ((mIntroEvents[TEXT0] == EVENT_COMPLETED) && (mIntroEvents[TEXT1] == EVENT_WAITING)) + if ((mIntro.events[TEXT0] == EVENT_COMPLETED) && (mIntro.events[TEXT1] == EVENT_WAITING)) { - mIntroText[0]->setEnabled(false); - mIntroText[1]->setEnabled(true); - mIntroEvents[TEXT1] = EVENT_RUNNING; + mIntro.text[0]->setEnabled(false); + mIntro.text[1]->setEnabled(true); + mIntro.events[TEXT1] = EVENT_RUNNING; } // Tercer texto de la primera imagen - if ((mIntroEvents[TEXT1] == EVENT_COMPLETED) && (mIntroEvents[TEXT2] == EVENT_WAITING)) + if ((mIntro.events[TEXT1] == EVENT_COMPLETED) && (mIntro.events[TEXT2] == EVENT_WAITING)) { - mIntroText[1]->setEnabled(false); - mIntroText[2]->setEnabled(true); - mIntroEvents[TEXT2] = EVENT_RUNNING; + mIntro.text[1]->setEnabled(false); + mIntro.text[2]->setEnabled(true); + mIntro.events[TEXT2] = EVENT_RUNNING; } // Segunda imagen - Máquina - if ((mIntroEvents[TEXT2] == EVENT_COMPLETED) && (mIntroEvents[BITMAP1] == EVENT_WAITING)) + if ((mIntro.events[TEXT2] == EVENT_COMPLETED) && (mIntro.events[BITMAP1] == EVENT_WAITING)) { - mIntroBitmap[0]->setEnabled(false); - mIntroText[2]->setEnabled(false); - mIntroBitmap[1]->setEnabled(true); - mIntroEvents[BITMAP1] = EVENT_RUNNING; + mIntro.bitmap[0]->setEnabled(false); + mIntro.text[2]->setEnabled(false); + mIntro.bitmap[1]->setEnabled(true); + mIntro.events[BITMAP1] = EVENT_RUNNING; } // Primer texto de la segunda imagen - if ((mIntroEvents[BITMAP1] == EVENT_COMPLETED) && (mIntroEvents[TEXT3] == EVENT_WAITING)) + if ((mIntro.events[BITMAP1] == EVENT_COMPLETED) && (mIntro.events[TEXT3] == EVENT_WAITING)) { - mIntroText[3]->setEnabled(true); - mIntroEvents[TEXT3] = EVENT_RUNNING; + mIntro.text[3]->setEnabled(true); + mIntro.events[TEXT3] = EVENT_RUNNING; } // Tercera imagen junto con primer texto - GRITO - if ((mIntroEvents[TEXT3] == EVENT_COMPLETED) && (mIntroEvents[BITMAP2] == EVENT_WAITING) && (mIntroEvents[TEXT4] == EVENT_WAITING)) + if ((mIntro.events[TEXT3] == EVENT_COMPLETED) && (mIntro.events[BITMAP2] == EVENT_WAITING) && (mIntro.events[TEXT4] == EVENT_WAITING)) { - mIntroBitmap[1]->setEnabled(false); - mIntroText[3]->setEnabled(false); - mIntroBitmap[2]->setEnabled(true); - mIntroText[4]->setEnabled(true); - mIntroEvents[BITMAP2] = EVENT_RUNNING; - mIntroEvents[TEXT4] = EVENT_RUNNING; + mIntro.bitmap[1]->setEnabled(false); + mIntro.text[3]->setEnabled(false); + mIntro.bitmap[2]->setEnabled(true); + mIntro.text[4]->setEnabled(true); + mIntro.events[BITMAP2] = EVENT_RUNNING; + mIntro.events[TEXT4] = EVENT_RUNNING; } // Cuarta imagen junto con primer texto - Reflexión - if ((mIntroEvents[TEXT4] == EVENT_COMPLETED) && (mIntroEvents[BITMAP3] == EVENT_WAITING) && (mIntroEvents[TEXT5] == EVENT_WAITING)) + if ((mIntro.events[TEXT4] == EVENT_COMPLETED) && (mIntro.events[BITMAP3] == EVENT_WAITING) && (mIntro.events[TEXT5] == EVENT_WAITING)) { - mIntroBitmap[2]->setEnabled(false); - mIntroText[4]->setEnabled(false); - mIntroBitmap[3]->setEnabled(true); - mIntroText[5]->setEnabled(true); - mIntroEvents[BITMAP3] = EVENT_RUNNING; - mIntroEvents[TEXT5] = EVENT_RUNNING; + mIntro.bitmap[2]->setEnabled(false); + mIntro.text[4]->setEnabled(false); + mIntro.bitmap[3]->setEnabled(true); + mIntro.text[5]->setEnabled(true); + mIntro.events[BITMAP3] = EVENT_RUNNING; + mIntro.events[TEXT5] = EVENT_RUNNING; } // Segundo texto de la cuarta imagen - if ((mIntroEvents[TEXT5] == EVENT_COMPLETED) && (mIntroEvents[TEXT6] == EVENT_WAITING)) + if ((mIntro.events[TEXT5] == EVENT_COMPLETED) && (mIntro.events[TEXT6] == EVENT_WAITING)) { - mIntroText[5]->setEnabled(false); - mIntroText[6]->setEnabled(true); - mIntroEvents[TEXT6] = EVENT_RUNNING; + mIntro.text[5]->setEnabled(false); + mIntro.text[6]->setEnabled(true); + mIntro.events[TEXT6] = EVENT_RUNNING; } // Quinta imagen - Patada - if ((mIntroEvents[TEXT6] == EVENT_COMPLETED) && (mIntroEvents[BITMAP4] == EVENT_WAITING)) + if ((mIntro.events[TEXT6] == EVENT_COMPLETED) && (mIntro.events[BITMAP4] == EVENT_WAITING)) { - mIntroBitmap[3]->setEnabled(false); - mIntroText[6]->setEnabled(false); - mIntroBitmap[4]->setEnabled(true); - mIntroEvents[BITMAP4] = EVENT_RUNNING; + mIntro.bitmap[3]->setEnabled(false); + mIntro.text[6]->setEnabled(false); + mIntro.bitmap[4]->setEnabled(true); + mIntro.events[BITMAP4] = EVENT_RUNNING; } // Primer texto de la quinta imagen - if ((mIntroEvents[BITMAP4] == EVENT_COMPLETED) && (mIntroEvents[TEXT7] == EVENT_WAITING)) + if ((mIntro.events[BITMAP4] == EVENT_COMPLETED) && (mIntro.events[TEXT7] == EVENT_WAITING)) { - mIntroText[7]->setEnabled(true); - mIntroEvents[TEXT7] = EVENT_RUNNING; + mIntro.text[7]->setEnabled(true); + mIntro.events[TEXT7] = EVENT_RUNNING; } // Sexta imagen junto con texto - Globos de café - if ((mIntroEvents[TEXT7] == EVENT_COMPLETED) && (mIntroEvents[BITMAP5] == EVENT_WAITING) && (mIntroEvents[TEXT8] == EVENT_WAITING)) + if ((mIntro.events[TEXT7] == EVENT_COMPLETED) && (mIntro.events[BITMAP5] == EVENT_WAITING) && (mIntro.events[TEXT8] == EVENT_WAITING)) { - mIntroBitmap[4]->setEnabled(false); - mIntroText[7]->setEnabled(false); - mIntroBitmap[5]->setEnabled(true); - mIntroText[8]->setEnabled(true); - mIntroEvents[BITMAP5] = EVENT_RUNNING; - mIntroEvents[TEXT8] = EVENT_RUNNING; + mIntro.bitmap[4]->setEnabled(false); + mIntro.text[7]->setEnabled(false); + mIntro.bitmap[5]->setEnabled(true); + mIntro.text[8]->setEnabled(true); + mIntro.events[BITMAP5] = EVENT_RUNNING; + mIntro.events[TEXT8] = EVENT_RUNNING; } // Acaba el último texto - if (mIntroEvents[TEXT8] == EVENT_COMPLETED) + if (mIntro.events[TEXT8] == EVENT_COMPLETED) { - mIntroText[8]->setEnabled(false); + mIntro.text[8]->setEnabled(false); JA_StopMusic(); - mGame.status = GAME_STATE_TITLE; + setProgSection(PROG_SECTION_TITLE); } } @@ -3243,31 +3395,30 @@ void GameDirector::runIntro() // Dibuja los objetos for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++) - { - mIntroBitmap[i]->render(); - } + mIntro.bitmap[i]->render(); for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++) - { - mIntroText[i]->render(); - } + mIntro.text[i]->render(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); } + + quitIntro(); } // Bucle para el titulo del juego -void GameDirector::runTitle() +void GameDirector::runTitle(Uint8 section) { - Uint8 R, G, B; - R = 0x27; - G = 0x27; - B = 0x36; + Uint8 R = 0x27; + Uint8 G = 0x27; + Uint8 B = 0x36; - while (mGame.status == GAME_STATE_TITLE) + initTitle(); + + while (mProg.section == PROG_SECTION_TITLE) { - // Estado 0 - Titulo desplazandose - if (mTitleStatus == 0) + // Sección 1 - Titulo desplazandose + if (mTitle.section == TITLE_SECTION_1) { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) @@ -3275,20 +3426,20 @@ void GameDirector::runTitle() // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { - setGameStatus(GAME_STATE_QUIT); + mProg.quit = true; break; } } // Calcula la lógica de los objetos - if (SDL_GetTicks() - mTicks > mTicksSpeed) + if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks - mTicks = SDL_GetTicks(); + mProg.ticks = SDL_GetTicks(); // Actualiza los objetos - mCoffeeBitmap->update(); - mCrisisBitmap->update(); + mTitle.coffeeBitmap->update(); + mTitle.crisisBitmap->update(); } // Limpia la pantalla @@ -3296,16 +3447,16 @@ void GameDirector::runTitle() SDL_RenderClear(mRenderer); // Dibuja los objetos - mCoffeeBitmap->render(); - mCrisisBitmap->render(); + mTitle.coffeeBitmap->render(); + mTitle.crisisBitmap->render(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); - // Si los objetos han llegado a su destino, cambiamos de estado - if ((mTitleEvents[0] == EVENT_COMPLETED) && (mTitleEvents[0] == EVENT_COMPLETED)) + // Si los objetos han llegado a su destino, cambiamos de Sección + if ((mTitle.events[0] == EVENT_COMPLETED) && (mTitle.events[0] == EVENT_COMPLETED)) { - mTitleStatus = 1; + mTitle.section = TITLE_SECTION_2; // Pantallazo blanco SDL_SetRenderDrawColor(mRenderer, 0xFF, 0xFF, 0xFF, 0xFF); @@ -3314,8 +3465,8 @@ void GameDirector::runTitle() } } - // Estado 1 - Titulo vibrando - if (mTitleStatus == 1) + // Sección 2 - Titulo vibrando + if (mTitle.section == TITLE_SECTION_2) { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) @@ -3323,7 +3474,7 @@ void GameDirector::runTitle() // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { - setGameStatus(GAME_STATE_QUIT); + mProg.quit = true; break; } } @@ -3333,8 +3484,8 @@ void GameDirector::runTitle() // Agita la pantalla int v[] = {-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 0}; - int a = mCoffeeBitmap->getPosX(); - int b = mCrisisBitmap->getPosX(); + int a = mTitle.coffeeBitmap->getPosX(); + int b = mTitle.crisisBitmap->getPosX(); for (Uint8 n = 0; n < 11 * 3; n++) { // Limpia la pantalla @@ -3342,26 +3493,26 @@ void GameDirector::runTitle() SDL_RenderClear(mRenderer); // Dibuja los objetos - mCoffeeBitmap->setPosX(a + v[n / 3]); - mCrisisBitmap->setPosX(b + v[n / 3]); - mCoffeeBitmap->render(); - mCrisisBitmap->render(); - mDustSpriteRight->animate(0); - mDustSpriteLeft->animate(0); - mDustSpriteRight->render(); - mDustSpriteLeft->render(); + mTitle.coffeeBitmap->setPosX(a + v[n / 3]); + mTitle.crisisBitmap->setPosX(b + v[n / 3]); + mTitle.coffeeBitmap->render(); + mTitle.crisisBitmap->render(); + mTitle.dustBitmapR->animate(0); + mTitle.dustBitmapL->animate(0); + mTitle.dustBitmapR->render(); + mTitle.dustBitmapL->render(); // Actualiza la pantalla SDL_RenderPresent(mRenderer); } - mTitleStatus = 2; + mTitle.section = 2; } - // Estado 2 - La pantalla de titulo con el menú y la música - if (mTitleStatus == 2) + // Sección 3 - La pantalla de titulo con el menú y la música + if (mTitle.section == TITLE_SECTION_3) { - if (mTitleTimer > 0) + if (mTitle.counter > 0) { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) @@ -3369,14 +3520,14 @@ void GameDirector::runTitle() // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { - setGameStatus(GAME_STATE_QUIT); + mProg.quit = true; break; } if ((mEventHandler->type == SDL_KEYUP) || (mEventHandler->type == SDL_JOYBUTTONUP)) { - mTitleMenuVisible = true; - mTitleTimer = TITLE_TIMER; + mTitle.menuVisible = true; + mTitle.counter = TITLE_COUNTER; } } // Si la música no está sonando @@ -3387,10 +3538,10 @@ void GameDirector::runTitle() } // Calcula la lógica de los objetos - if (SDL_GetTicks() - mTicks > mTicksSpeed) + if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks - mTicks = SDL_GetTicks(); + mProg.ticks = SDL_GetTicks(); // Actualiza la lógica del menu mMenu.active->update(); @@ -3404,46 +3555,47 @@ void GameDirector::runTitle() SDL_RenderClear(mRenderer); // Pinta el tileado de fondo - switch (mTitleBackgroundMode) + switch (mTitle.backgroundMode) { - case 0: - mBackgroundWindow.x++; - mBackgroundWindow.x %= 64; - mBackgroundWindow.y++; - mBackgroundWindow.y %= 64; + case 0: // El tileado de fondo se desplaza en diagonal + mTitle.backgroundWindow.x++; + mTitle.backgroundWindow.x %= 64; + mTitle.backgroundWindow.y++; + mTitle.backgroundWindow.y %= 64; break; - case 1: - mTitleBackgroundTimer++; - mTitleBackgroundTimer %= 360; - mBackgroundWindow.x = 128 + (int(mSen[(mTitleBackgroundTimer + 270) % 360] * 128)); - mBackgroundWindow.y = 96 + (int(mSen[(360 - mTitleBackgroundTimer) % 360] * 96)); + case 1: // El tileado de fondo se desplaza en circulo + ++mTitle.backgroundCounter %= 360; + mTitle.backgroundWindow.x = 128 + (int(mSin[(mTitle.backgroundCounter + 270) % 360] * 128)); + mTitle.backgroundWindow.y = 96 + (int(mSin[(360 - mTitle.backgroundCounter) % 360] * 96)); break; default: break; } - SDL_RenderCopy(mRenderer, mTitleSurface, &mBackgroundWindow, NULL); + SDL_RenderCopy(mRenderer, mTitleSurface, &mTitle.backgroundWindow, NULL); // Dibuja los objetos - mCoffeeBitmap->render(); - mCrisisBitmap->render(); - if (mTitleMenuVisible == true) + mTitle.coffeeBitmap->render(); + mTitle.crisisBitmap->render(); + if (mTitle.menuVisible == true) { mMenu.active->render(); } - mDustSpriteRight->animate(0); - mDustSpriteLeft->animate(0); - mDustSpriteRight->render(); - mDustSpriteLeft->render(); + mTitle.dustBitmapR->animate(0); + mTitle.dustBitmapL->animate(0); + mTitle.dustBitmapR->render(); + mTitle.dustBitmapL->render(); - if ((mTitleTimer % 50 > 14) && (mTitleMenuVisible == false)) + // PRESS ANY KEY! + if ((mTitle.counter % 50 > 14) && (mTitle.menuVisible == false)) { mText.black->writeCentered(SCREEN_CENTER_X + 1, PLAY_AREA_THIRD_QUARTER_Y + BLOCK + 1, "PRESS ANY KEY!", 0); mText.white->writeCentered(SCREEN_CENTER_X, PLAY_AREA_THIRD_QUARTER_Y + BLOCK, "PRESS ANY KEY!", 0); } + // texto con el copyright y versión mText.black->writeCentered(SCREEN_CENTER_X + 1, SCREEN_HEIGHT - (BLOCK * 2) + 1, TEXT_COPYRIGHT, 0); mText.white->writeCentered(SCREEN_CENTER_X, SCREEN_HEIGHT - (BLOCK * 2), TEXT_COPYRIGHT, 0); @@ -3457,7 +3609,7 @@ void GameDirector::runTitle() SDL_RenderPresent(mRenderer); // Comprueba las entradas para el menu - if (mTitleMenuVisible == true) + if (mTitle.menuVisible == true) { checkMenuInput(mMenu.active); } @@ -3468,26 +3620,25 @@ void GameDirector::runTitle() switch (mMenu.active->getItemSelected()) { case 0: - setGameStatus(GAME_STATE_PLAYING); + setProgSection(PROG_SECTION_GAME); mMenu.active->reset(); - mMenuKeyPressed = false; + mMenu.keyPressed = false; JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); renderFade(1); JA_StopMusic(); - init(true); disableDemoMode(); break; case 1: JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); mMenu.active->reset(); mMenu.active = mMenu.options; - mFullScreenModePrevious = mFullScreenMode; - mWindowSizePrevious = mWindowSize; + mOptions.fullScreenModePrevious = mOptions.fullScreenMode; + mOptions.windowSizePrevious = mOptions.windowSize; break; case 2: - setGameStatus(GAME_STATE_QUIT); + mProg.quit = true; mMenu.active->reset(); - mMenuKeyPressed = false; + mMenu.keyPressed = false; JA_PlaySound(mSound[SOUND_MENU_CANCEL].sound); renderFade(1); JA_StopMusic(); @@ -3511,23 +3662,23 @@ void GameDirector::runTitle() break; case 1: // Windows size JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); - mWindowSize++; - if (mWindowSize == 5) - mWindowSize = 1; + mOptions.windowSize++; + if (mOptions.windowSize == 5) + mOptions.windowSize = 1; updateOptionsMenu(); mMenu.active->deselectItem(); break; case 2: // OK JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); - SDL_SetWindowFullscreen(mWindow, mFullScreenMode); - SDL_SetWindowSize(mWindow, SCREEN_WIDTH * mWindowSize, SCREEN_HEIGHT * mWindowSize); + SDL_SetWindowFullscreen(mWindow, mOptions.fullScreenMode); + SDL_SetWindowSize(mWindow, SCREEN_WIDTH * mOptions.windowSize, SCREEN_HEIGHT * mOptions.windowSize); mMenu.active->reset(); mMenu.active = mMenu.title; break; case 3: // CANCEL JA_PlaySound(mSound[SOUND_MENU_CANCEL].sound); - mFullScreenMode = mFullScreenModePrevious; - mWindowSize = mWindowSizePrevious; + mOptions.fullScreenMode = mOptions.fullScreenModePrevious; + mOptions.windowSize = mOptions.windowSizePrevious; updateOptionsMenu(); mMenu.active->reset(); mMenu.active = mMenu.title; @@ -3540,94 +3691,125 @@ void GameDirector::runTitle() if (mMenu.active->getName() == "TITLE") { - mTitleTimer--; + mTitle.counter--; } } - else if (mTitleTimer == 0) + else if (mTitle.counter == 0) { - mTitleTimer = TITLE_TIMER; - mGame.status = mTiteNextGS; + mTitle.counter = TITLE_COUNTER; + setProgSection(mTitle.nextProgSection); mMenu.active->reset(); toogleTitleNextGS(); renderFade(1); - if (mGame.status == GAME_STATE_INTRO) + if (mProg.section == PROG_SECTION_INTRO) { JA_StopMusic(); } - init(true); enableDemoMode(); } } + + // Sección Instrucciones + if (mTitle.section == TITLE_SECTION_INSTRUCTIONS) + runInstructions(); } + + quitTitle(); } // Bucle para el juego void GameDirector::runGame() { - while (mGame.status == GAME_STATE_PLAYING) - { - // Si la música no está sonando - if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED)) - { - // Reproduce la música - if (mPlayer->isAlive()) - JA_PlayMusic(mMusic[MUSIC_PLAYING].music, true); - } + initGame(); - // Lógica del juego - if (mGame.paused) + while (mProg.section == PROG_SECTION_GAME) + { + // Sección juego en pausa + if (mGame.section == GAME_SECTION_PAUSE) runPausedGame(); - // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego - if (SDL_GetTicks() - mTicks > mTicksSpeed) + // Sección Game Over + if (mGame.section == GAME_SECTION_GAMEOVER) + runGameOverScreen(); + + // Sección juego jugando + if (mGame.section == GAME_SECTION_PLAY) { - // Actualiza el contador de ticks - mTicks = SDL_GetTicks(); - - // Actualiza el contador de juego - mGameCounter++; - - // Comprueba los eventos que hay en la cola - while (SDL_PollEvent(mEventHandler) != 0) + // Si la música no está sonando + if ((JA_GetMusicState() == JA_MUSIC_INVALID) || (JA_GetMusicState() == JA_MUSIC_STOPPED)) { - // Evento de salida de la aplicación - if (mEventHandler->type == SDL_QUIT) - { - setGameStatus(GAME_STATE_QUIT); - break; - } + // Reproduce la música + if (mGame.player->isAlive()) + JA_PlayMusic(mMusic[MUSIC_PLAYING].music, true); } - // Actualiza la lógica del juego - updatePlayField(); + // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego + if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) + { + // Actualiza el contador de ticks + mProg.ticks = SDL_GetTicks(); + + // Actualiza el contador de juego + mGame.counter++; + + // Comprueba los eventos que hay en la cola + while (SDL_PollEvent(mEventHandler) != 0) + { + // Evento de salida de la aplicación + if (mEventHandler->type == SDL_QUIT) + { + mProg.quit = true; + break; + } + else if ((mEventHandler->type == SDL_KEYDOWN) && (mEventHandler->key.repeat == 0)) + { + switch (mEventHandler->key.keysym.scancode) + { + case SDL_SCANCODE_T: + mProg.ticksSpeed *= 2; + break; + case SDL_SCANCODE_G: + mProg.ticksSpeed /= 2; + break; + default: + break; + } + } + } + + // Actualiza la lógica del juego + updatePlayField(); + } + + // Cambia el destino donde se pinta todo + SDL_SetRenderTarget(mRenderer, mBackbuffer); + + // Limpia la pantalla + SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF); + SDL_RenderClear(mRenderer); + + // Dibuja los objetos + renderPlayField(); + + // Vuelve a usar el renderizador como destino + SDL_SetRenderTarget(mRenderer, NULL); + + // Volca el contenido de la textura en el renderizador + SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); + + // Actualiza la pantalla + if (mProg.section == PROG_SECTION_GAME) + SDL_RenderPresent(mRenderer); } - - // Cambia el destino donde se pinta todo - SDL_SetRenderTarget(mRenderer, mBackbuffer); - - // Limpia la pantalla - SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF); - SDL_RenderClear(mRenderer); - - // Dibuja los objetos - renderPlayField(); - - // Vuelve a usar el renderizador como destino - SDL_SetRenderTarget(mRenderer, NULL); - - // Volca el contenido de la textura en el renderizador - SDL_RenderCopy(mRenderer, mBackbuffer, NULL, NULL); - - // Actualiza la pantalla - if (mGame.status == GAME_STATE_PLAYING) - SDL_RenderPresent(mRenderer); } + + quitGame(); } // Bucle para el menu de pausa del juego void GameDirector::runPausedGame() { - while (mGame.paused) + while (mGame.section == GAME_SECTION_PAUSE) { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) @@ -3635,16 +3817,16 @@ void GameDirector::runPausedGame() // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { - setGameStatus(GAME_STATE_QUIT); + mProg.quit = true; break; } } // Calcula la lógica de los objetos - if (SDL_GetTicks() - mTicks > mTicksSpeed) + if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks - mTicks = SDL_GetTicks(); + mProg.ticks = SDL_GetTicks(); // Actualiza la lógica del menu mMenu.pause->update(); @@ -3657,7 +3839,7 @@ void GameDirector::runPausedGame() renderBackground(); renderBalloons(); renderBullets(); - mPlayer->render(); + mGame.player->render(); renderScoreBoard(); mMenu.pause->render(); @@ -3678,10 +3860,9 @@ void GameDirector::runPausedGame() { case 0: JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); - //setGameStatus(GAME_STATE_PLAYING); - mGame.paused = false; + mGame.section = GAME_SECTION_PLAY; mMenu.pause->reset(); - mMenuKeyPressed = false; + mMenu.keyPressed = false; if (JA_GetMusicState() == JA_MUSIC_PAUSED) { JA_ResumeMusic(); @@ -3689,12 +3870,11 @@ void GameDirector::runPausedGame() break; case 1: JA_PlaySound(mSound[SOUND_MENU_CANCEL].sound); - setGameStatus(GAME_STATE_TITLE); + setProgSection(PROG_SECTION_TITLE); mMenu.pause->reset(); - mMenuKeyPressed = false; + mMenu.keyPressed = false; JA_StopMusic(); renderFade(1); - init(true); break; default: break; @@ -3707,16 +3887,16 @@ void GameDirector::runInstructions() { SDL_Rect window = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT}; Sprite *sprite = new Sprite(); - SDL_Rect rect1 = {60, 88, 16, 16}; - SDL_Rect rect2 = {60, 104, 16, 16}; - SDL_Rect rect3 = {60, 120, 16, 16}; - SDL_Rect rect4 = {60, 136, 16, 16}; - SDL_Rect rect5 = {60, 152, 16, 16}; - SDL_Rect rect6 = {60, 168, 16, 16}; + SDL_Rect rect1 = {60, 88, 16, 16}; // Disquito + SDL_Rect rect2 = {60, 104, 16, 16}; // Gavineixon + SDL_Rect rect3 = {60, 120, 16, 16}; // Pacmar + SDL_Rect rect4 = {60, 136, 16, 16}; // Time Stopper + SDL_Rect rect5 = {60, 152, 16, 16}; // Explosive + SDL_Rect rect6 = {60, 168, 16, 16}; // Coffee SDL_Rect rect = {0, 0, 16, 16}; - sprite->init(rect1, mTexture[TEXTURE_ITEMS].texture, mRenderer); - while (mGame.status == GAME_STATE_INSTRUCTIONS) + + while (mTitle.section == TITLE_SECTION_INSTRUCTIONS) { int y = 0; // Comprueba los eventos que hay en la cola @@ -3725,14 +3905,14 @@ void GameDirector::runInstructions() // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { - setGameStatus(GAME_STATE_QUIT); + mProg.quit = true; break; } if ((mEventHandler->type == SDL_KEYDOWN) || (mEventHandler->type == SDL_JOYBUTTONDOWN)) { JA_StopMusic(); - setGameStatus(GAME_STATE_TITLE); + setProgSection(PROG_SECTION_TITLE); } } @@ -3756,32 +3936,32 @@ void GameDirector::runInstructions() sprite->init(rect1, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; - rect.y = 16 * (((mInstructionsCounter + 3) / 36) % 2); + rect.y = 16 * (((mTitle.instructionsCounter + 3) / 36) % 2); sprite->render(); sprite->init(rect2, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; - rect.y = 16 * (((mInstructionsCounter + 6) / 36) % 2); + rect.y = 16 * (((mTitle.instructionsCounter + 6) / 36) % 2); sprite->render(); sprite->init(rect3, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; - rect.y = 16 * (((mInstructionsCounter + 9) / 36) % 2); + rect.y = 16 * (((mTitle.instructionsCounter + 9) / 36) % 2); sprite->render(); sprite->init(rect4, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; - rect.y = 16 * (((mInstructionsCounter + 12) / 36) % 2); + rect.y = 16 * (((mTitle.instructionsCounter + 12) / 36) % 2); sprite->render(); sprite->init(rect5, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x += rect.w; - rect.y = 16 * (((mInstructionsCounter + 15) / 36) % 2); + rect.y = 16 * (((mTitle.instructionsCounter + 15) / 36) % 2); sprite->render(); sprite->init(rect6, mTexture[TEXTURE_ITEMS].texture, mRenderer); sprite->setSpriteClip(rect); rect.x = 0; - rect.y = 16 * (((mInstructionsCounter + 0) / 36) % 2); + rect.y = 16 * (((mTitle.instructionsCounter + 0) / 36) % 2); sprite->render(); SDL_SetRenderTarget(mRenderer, nullptr); @@ -3791,7 +3971,7 @@ void GameDirector::runInstructions() SDL_RenderClear(mRenderer); // Dibuja los objetos - y = SCREEN_HEIGHT - (INSTRUCTIONS_COUNTER - mInstructionsCounter) + 100; + y = SCREEN_HEIGHT - (INSTRUCTIONS_COUNTER - mTitle.instructionsCounter) + 100; if (y < 0) { y = 0; @@ -3803,12 +3983,12 @@ void GameDirector::runInstructions() // Muestra la pantalla SDL_RenderPresent(mRenderer); - mInstructionsCounter--; + mTitle.instructionsCounter--; - if (mInstructionsCounter == 0) + if (mTitle.instructionsCounter == 0) { - mInstructionsCounter = INSTRUCTIONS_COUNTER; - setGameStatus(GAME_STATE_TITLE); + mTitle.instructionsCounter = INSTRUCTIONS_COUNTER; + mTitle.section = TITLE_SECTION_1; } } delete sprite; @@ -3817,7 +3997,7 @@ void GameDirector::runInstructions() // Bucle para la pantalla de game over void GameDirector::runGameOverScreen() { - while (mGame.status == GAME_STATE_GAME_OVER_SCREEN) + while (mGame.section == GAME_SECTION_GAMEOVER) { // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) @@ -3825,16 +4005,16 @@ void GameDirector::runGameOverScreen() // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { - setGameStatus(GAME_STATE_QUIT); + mProg.quit = true; break; } } // Calcula la lógica de los objetos - if (SDL_GetTicks() - mTicks > mTicksSpeed) + if (SDL_GetTicks() - mProg.ticks > mProg.ticksSpeed) { // Actualiza el contador de ticks - mTicks = SDL_GetTicks(); + mProg.ticks = SDL_GetTicks(); // Actualiza la lógica del menu mMenu.gameOver->update(); @@ -3869,24 +4049,22 @@ void GameDirector::runGameOverScreen() switch (mMenu.gameOver->getItemSelected()) { case 0: - setGameStatus(GAME_STATE_PLAYING); + setProgSection(PROG_SECTION_GAME); mMenu.gameOver->reset(); - mMenuKeyPressed = false; + mMenu.keyPressed = false; JA_PlaySound(mSound[SOUND_MENU_SELECT].sound); JA_StopMusic(); - init(true); renderFade(1); disableDemoMode(); break; case 1: - setGameStatus(GAME_STATE_TITLE); + setProgSection(PROG_SECTION_TITLE); mMenu.gameOver->reset(); - mMenuKeyPressed = false; + mMenu.keyPressed = false; JA_PlaySound(mSound[SOUND_MENU_CANCEL].sound); JA_StopMusic(); - init(true); renderFade(1); - mTiteNextGS = GAME_STATE_PLAYING; + mTitle.nextProgSection = PROG_SECTION_GAME; enableDemoMode(); break; default: @@ -3898,12 +4076,12 @@ void GameDirector::runGameOverScreen() // Dibuja la informacion de debug en pantalla void GameDirector::renderDebugInfo() { - if (mDebug) + if (mProg.debug) { - mText.white->write(0, 0 * BLOCK, "menace: " + std::to_string(mMenaceLevelThreshold) + "/" + std::to_string(mMenaceLevel)); - mText.white->write(0, 1 * BLOCK, "item[0].TTL: " + std::to_string(mItem[0]->mTimeToLive)); + mText.white->write(0, 0 * BLOCK, "menace: " + std::to_string(mGame.menaceLevelThreshold) + "/" + std::to_string(mGame.menaceLevelCurrent)); + mText.white->write(0, 1 * BLOCK, "item[0].TTL: " + std::to_string(mGame.item[0]->mTimeToLive)); mText.white->write(0, 2 * BLOCK, "mGame.stage: " + std::to_string(mGame.stage)); - mText.white->write(0, 3 * BLOCK, "mGameCounter : " + std::to_string(mGameCounter)); + mText.white->write(0, 3 * BLOCK, "mGame.counter : " + std::to_string(mGame.counter)); mText.white->write(0, 4 * BLOCK, ": "); mText.white->write(0, 5 * BLOCK, ": "); } @@ -3912,26 +4090,26 @@ void GameDirector::renderDebugInfo() // Activa el modo Demo void GameDirector::enableDemoMode() { - mDemo = true; - mGetReadyBitmap->setEnabled(false); + mDemo.enabled = true; + mGame.getReadyBitmap->setEnabled(false); } // Desactiva el modo Demo void GameDirector::disableDemoMode() { - mDemo = false; - mGetReadyBitmap->setEnabled(true); + mDemo.enabled = false; + mGame.getReadyBitmap->setEnabled(true); } // Actualiza el proximo estado del juego despues del titulo void GameDirector::toogleTitleNextGS() { - if (mTiteNextGS == GAME_STATE_LOGO) + if (mTitle.nextProgSection == PROG_SECTION_LOGO) { - mTiteNextGS = GAME_STATE_PLAYING; + mTitle.nextProgSection = PROG_SECTION_GAME; } else { - mTiteNextGS = GAME_STATE_LOGO; + mTitle.nextProgSection = PROG_SECTION_LOGO; } } \ No newline at end of file diff --git a/source/gamedirector.h b/source/gamedirector.h index a2b5b43..5821539 100644 --- a/source/gamedirector.h +++ b/source/gamedirector.h @@ -6,12 +6,14 @@ #include "player.h" #include "balloon.h" #include "bullet.h" +#include "coffeedrop.h" #include "item.h" #include "text.h" #include "text2.h" #include "menu.h" #include "const.h" #include "jail_audio.h" +#include "utils.h" #include #ifndef GAMEDIRECTOR_H @@ -20,33 +22,272 @@ // GameDirector class GameDirector { +private: + SDL_Window *mWindow; // La ventana donde dibujamos + SDL_Renderer *mRenderer; // El renderizador de la ventana + SDL_Event *mEventHandler; // Manejador de eventos + + SDL_Texture *mBackbuffer; // Textura para usar como backbuffer + SDL_Texture *mTitleSurface; // Textura para dibujar el fondo de la pantalla de título + SDL_Texture *mInstructionsSurface; // Textura donde dibujar las instrucciones + + SDL_Joystick *mGameController; // Manejador para el mando 1 + SDL_Haptic *mControllerHaptic; // Manejador para el mando con vibración + bool mGameControllerFound; // Variable para saber si hay un mando conectado + +#ifndef UNUSED + CoffeeDrop *mCoffeeDrop[MAX_COFFEE_DROPS]; // Vector con todas ls gotas de café; +#endif + + struct text + { + Text *white; // Texto blanco de 8x8 + Text *whiteX2; // Texto blanco de 16x16 + Text *black; // Texto negro de 8x8 + Text *blackX2; // Texto negro de 16x16 + Text *nokia; // Texto de anchura variable y 10px de alto + }; + text mText; // Variable con todos los objetos de texto + + struct menu + { + Menu *title; // Menu de la pantalla de título + Menu *pause; // Menú de la pantalla de pausa + Menu *gameOver; // Menú de la pantalla de game over + Menu *options; // Menú de la pantalla de opciones + Menu *active; // Menu activo (de momento para la pantalla del titulo) + bool keyPressed; // Variable para evitar la repetición de teclas en los menus + }; + menu mMenu; // Variable con todos los objetos menus y sus variables + + struct intro + { + SmartSprite *bitmap[INTRO_TOTAL_BITMAPS]; // Vector con los sprites inteligentes para los dibujos de la intro + Text2 *text[INTRO_TOTAL_TEXTS]; // Textos de la intro + Uint8 events[INTRO_TOTAL_EVENTS]; // Vector para coordinar los eventos de la intro + }; + intro mIntro; // Contiene todas las variables de la sección 'Intro' + + struct game + { + Uint32 score; // Puntuación actual + Uint32 hiScore; // Puntuación máxima + Uint8 section; // Seccion actual dentro del juego + bool hiScoreAchieved; // Indica si se ha superado la puntuación máxima + Uint8 stage; // Pantalla actual + Uint8 stageCounter; // Contador para el tiempo visible del texto de Stage + float stagePath[STAGE_COUNTER]; // Vector con los puntos Y por donde pasará la etiqueta + Uint16 deathCounter; // Contador para la animación de muerte del jugador + Uint8 deathIndex; // Indice del vector de smartsprites que contiene el sprite del jugador + Uint8 menaceLevelCurrent; // Nivel de amenaza actual + Uint8 menaceLevelThreshold; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el numero de globos + bool playFieldDrawOnly; // Indica si el bucle de juego avanza o solo pinta + bool timeStopped; // Indica si el tiempo está detenido + Uint16 timeStoppedCounter; // Temporizador para llevar la cuenta del tiempo detenido + Uint8 remainingExplosions; // Cantidad de explosiones restantes + Uint16 remainingExplosionsCounter; // Temporizador para la cantidad de explosiones restantes + bool explosionTime; // Indica si las explosiones estan en marcha + Uint32 counter; // Contador para el juego + Uint32 scoreDataFile[TOTAL_SCORE_DATA]; // Datos del fichero de puntos + SmartSprite *getReadyBitmap; // Sprite para el texto de GetReady del principio de la partida + SmartSprite *_1000Bitmap; // Sprite con el texto 1.000 + SmartSprite *_2500Bitmap; // Sprite con el texto 2.500 + SmartSprite *_5000Bitmap; // Sprite con el texto 5.000 + Sprite *background; // Sprite con los graficos frontales del fondo + Sprite *gradient; // Sprite con los graficos del degradado de color de fondo + MovingSprite *clouds1a; // Sprite para las nubes superiores + MovingSprite *clouds1b; // Sprite para las nubes superiores + MovingSprite *clouds2a; // Sprite para las nubes inferiores + MovingSprite *clouds2b; // Sprite para las nubes inferiores + Sprite *grass; // Sprite para la hierba + Player *player; // El jugador + Balloon *balloon[MAX_BALLOONS]; // Vector con los objetos globo + Bullet *bullet[MAX_BULLETS]; // Vector con los objetos bala + Item *item[MAX_ITEMS]; // Vector con los objetos item + SmartSprite *smartSprite[MAX_SMART_SPRITES]; // Vector para almacenar y gestionar SmartSprites + }; + game mGame; // Variable con todas las variables usadas durante el juego + + struct title + { + Uint16 counter; // Temporizador para la pantalla de titulo + Uint16 backgroundCounter; // Temporizador para el fondo de tiles de la pantalla de titulo + Uint8 backgroundMode; // Variable para almacenar el tipo de efecto que hará el fondo de la pantalla de titulo + bool menuVisible; // Indicador para saber si se muestra el menu del titulo o la frase intermitente + Sprite *tile; // Sprite para dibujar el fondo de pantalla del título + SDL_Rect backgroundWindow; // Ventana visible para la textura de fondo del titulo + Uint8 section; // Indicador para el bucle del titulo + Uint8 nextProgSection; // Indica cual es la siguiente sección a cargar cuando termine el contador del titulo + Uint8 events[TITLE_TOTAL_EVENTS]; // Vector para coordinar los eventos de la pantalla de titulo + Uint16 instructionsCounter; // Contador para las instrucciones + SmartSprite *coffeeBitmap; // Sprite con la palabra COFFEE para la pantalla de titulo + SmartSprite *crisisBitmap; // Sprite con la palabra CRISIS para la pantalla de titulo + AnimatedSprite *dustBitmapL; // Sprite con la el polvo que aparece al colisionar el texto de la pantalla de titulo + AnimatedSprite *dustBitmapR; // Sprite con la el polvo que aparece al colisionar el texto de la pantalla de titulo + }; + title mTitle; // Variable con todas las variables de la pantalla de titulo + + struct demo + { + bool enabled; // Indica si está activo el modo demo + bool recording; // Indica si está activado el modo para grabar la demo + Uint16 counter; // Contador para el modo demo + DemoKeys keys; // Variable con las pulsaciones de teclas del modo demo + DemoKeys dataFile[TOTAL_DEMO_DATA]; // Datos del fichero con los movimientos para la demo + }; + demo mDemo; // Variable con todas las variables relacionadas con el modo demo + + struct options + { + Uint32 fullScreenMode; // Contiene el valor del modo de pantalla completa + Uint32 fullScreenModePrevious; + Uint8 windowSize; // Contiene el valor del tamaño de la ventana + Uint8 windowSizePrevious; + bool displayCoffeeDrops; // Indica si se han de mostar las gotas de cafe + }; + options mOptions; // Variable con todas las variables de las opciones del programa + + struct logo + { + Uint16 counter; // Contador + Sprite *sprite; // Sprite con la textura del logo + }; + logo mLogo; // Variable con las variables para el logo + + struct prog + { + bool debug; // Indica si se va a mostrar la información de debug + bool quit; // Indica si hay que salir del programa + Input keyboard; // Almacena los códigos de teclado correspondientes + Input keyboardBuffer; // Buffer para teclas pulsadas + std::string executablePath; // Path del ejecutable + Uint32 ticks; // Contador de ticks para ajustar la velocidad del programa + Uint8 section; // Sección actual del programa; + Uint8 subsection; // Subseccion dentro de la sección; + Uint8 ticksSpeed; // Velocidad a la que se repiten los bucles del programa + }; + prog mProg; // Contiene todas las variables globales del programa + + struct resourceBinFile + { + std::string file; // Ruta al fichero + }; + resourceBinFile mBinFile[TOTAL_BINFILE]; // Todos los ficheros binarios + + struct resourceSound + { + std::string file; // Ruta al fichero + JA_Sound sound; // Variable con el sonido + }; + resourceSound mSound[TOTAL_SOUND]; // Todos los sonidos + + struct resourceMusic + { + std::string file; // Ruta al fichero + JA_Music music; // Variable con la música + }; + resourceMusic mMusic[TOTAL_MUSIC]; // Todas las músicas + + struct resourceTexture + { + std::string file; // Ruta al fichero + LTexture *texture; // Variable con la textura + }; + resourceTexture mTexture[TOTAL_TEXTURE]; // Todos los gráficos + public: // Constructor - GameDirector(); + GameDirector(std::string path); // Destructor ~GameDirector(); - // Iniciador - void init(bool reset); + // Inicia las variables necesarias para arrancar el programa + void initProg(); + + // Carga los recursos necesarios para el programa + bool loadMediaProg(); + + // Libera las variables del programa + void quitProg(); + + // Inicializa jail_audio + void initJailAudio(); // Arranca SDL y crea la ventana bool initSDL(); + // Inicializa el vector con los valores del seno + void initSin(); + + // Inicializa las variables que contienen puntos de ruta para mover objetos + void initPaths(); + + // Inicializa las variables necesarias para la sección 'Logo' + void initLogo(); + + // Carga los recursos necesarios para la sección 'Logo' + bool loadMediaLogo(); + + // Libera las variables necesarias para la sección 'Logo' + void quitLogo(); + + // Inicializa las variables necesarias para la sección 'Intro' + void initIntro(); + + // Carga los recursos necesarios para la sección 'Intro' + bool loadMediaIntro(); + + // Libera las variables necesarias para la sección 'Intro' + void quitIntro(); + + // Inicializa las variables necesarias para la sección 'Title' + void initTitle(Uint8 section = TITLE_SECTION_1); + + // Carga los recursos necesarios para la sección 'Title' + bool loadMediaTitle(); + + // Libera las variables necesarias para la sección 'Title' + void quitTitle(); + + // Inicializa las variables necesarias para la sección 'Game' + void initGame(); + + // Carga los recursos necesarios para la sección 'Game' + bool loadMediaGame(); + + // Libera las variables necesarias para la sección 'Game' + void quitGame(); + // Crea el indice de ficheros void setFileList(); // Comprueba que todos los ficheros existen bool checkFileList(); + // Carga el fichero de puntos + bool loadScoreFile(); + + // Carga el fichero de configuración + bool loadConfigFile(); + + // Carga el fichero de datos para la demo + bool loadDemoFile(); + + // Guarda el fichero de puntos + bool saveScoreFile(); + + // Guarda el fichero de configuración + bool saveConfigFile(); + + // Guarda el fichero de datos para la demo + bool saveDemoFile(); + // Carga un archivo de imagen en una textura bool loadTextureFromFile(LTexture *texture, std::string path, SDL_Renderer *renderer); - // Carga los recursos necesarios - bool loadMedia(Uint8 section); - - // Descrga los recursos necesarios - bool unLoadMedia(Uint8 section); + // Comprueba el valor de la variable 'quit' + bool exit(); // Establece el valor de la variable void setExecutablePath(std::string path); @@ -82,10 +323,10 @@ public: void renderBalloons(); // Devuelve el primer indice no activo del vector de globos - Uint8 getBallonFreeIndex(); + Uint8 getBalloonFreeIndex(); // Crea un globo nuevo en el vector de globos - Uint8 createNewBalloon(float x, int y, Uint8 kind, float velx, Uint16 stoppedtimer, LTexture *texture); + Uint8 createNewBalloon(float x, int y, Uint8 kind, float velx, Uint16 stoppedcounter, LTexture *texture); // Establece a cero todos los valores del vector de objetos globo void resetBalloons(); @@ -94,7 +335,7 @@ public: void popBalloon(Uint8 index); // Explosiona todos los globos - void popAllBallons(); + void popAllBalloons(); // Detiene todos los globos void stopAllBalloons(Uint16 time); @@ -106,13 +347,13 @@ public: Uint8 countBalloons(); // Comprueba la colisión entre el jugador y los globos activos - bool checkPlayerBallonCollision(); + bool checkPlayerBalloonCollision(); // Comprueba la colisión entre el jugador y los items void checkPlayerItemCollision(); // Comprueba la colisión entre las balas y los globos - void checkBulletBallonCollision(); + void checkBulletBalloonCollision(); // Mueve las balas activas void moveBullets(); @@ -168,11 +409,30 @@ public: // Establece a cero todos los valores del vector de objetos SmafrtSprite void resetSmartSprites(); +#ifndef UNUSED + // Deshabilita todas las gotas de café + void resetCoffeeDrops(); + + // Actualiza las gotas de cafe + void updateCoffeeDrops(); + + // Dibuja las gotas de cafe + void renderCoffeeDrops(); + + // Devuelve el primer indice libre del vector de CoffeeDrops + Uint8 getCoffeDropFreeIndex(); + + // Crea un numero de gotas de cafe + void createCoffeDrops(Uint8 num, int x, int y); +#endif // Acciones a realizar cuando el jugador muere void killPlayer(); + // Obtiene el valor de la variable + Uint8 getSubsection(); + // Calcula y establece el valor de amenaza en funcion de los globos activos - void calculateMenaceLevel(); + void setMenaceLevel(); // Obtiene el valor de la variable Uint8 getMenaceLevel(); @@ -190,10 +450,10 @@ public: bool isTimeStopped(); // Establece el valor de la variable - void setTimeStoppedTimer(Uint16 value); + void setTimeStoppedCounter(Uint16 value); // Actualiza y comprueba el valor de la variable - void updateTimeStoppedTimer(); + void updateTimeStoppedCounter(); // Establece el valor de la variable void setExplosionTime(bool value); @@ -205,10 +465,10 @@ public: void setRemainingExplosions(Uint8 value); // Actualiza y comprueba el valor de la variable - void updateRemainingExplosionsTimer(); + void updateRemainingExplosionsCounter(); // Gestiona el nivel de amenaza - void checkMenaceLevel(); + void updateMenaceLevel(); // Actualiza el campo de juego void updatePlayField(); @@ -232,10 +492,10 @@ public: void checkMenuInput(Menu *menu); // Obtiene el valor de la variable - Uint8 getGameStatus(); + Uint8 getProgSection(); // Establece el valor de la variable - void setGameStatus(Uint8 status); + void setProgSection(Uint8 section, Uint8 subsection = 0); // Pinta una transición en pantalla void renderFade(Uint8 index); @@ -265,7 +525,7 @@ public: void runIntro(); // Bucle para el titulo del juego - void runTitle(); + void runTitle(Uint8 section = TITLE_SECTION_1); // Bucle para el juego void runGame(); @@ -290,260 +550,6 @@ public: // Intercambia el proximo estado del juego despues del titulo void toogleTitleNextGS(); - -private: - // La ventana donde dibujamos - SDL_Window *mWindow; - - // El renderizador de la ventana - SDL_Renderer *mRenderer; - - // Texturas donde dibujar - SDL_Texture *mBackbuffer; - SDL_Texture *mTitleSurface; - SDL_Texture *mInstructionsSurface; - - // Manejador para el mando 1 - SDL_Joystick *mGameController; - bool mGameControllerFound; - SDL_Haptic *mControllerHaptic; - - // Datos del fichero - Uint32 mScoreDataFile[TOTAL_SCORE_DATA]; - DemoKeys mDemoDataFile[TOTAL_DEMO_DATA]; - - // Manejador de eventos - SDL_Event *mEventHandler; - - // El jugador - Player *mPlayer; - - // Vector con los objetos globo - Balloon *mBalloon[MAX_BALLOONS]; - - // Vector con los objetos bala - Bullet *mBullet[MAX_BULLETS]; - - // Vector con los objetos item - Item *mItem[MAX_ITEMS]; - - // Fondo del juego - Sprite *mGameBackgroundFront; - Sprite *mGameBackgroundSky; - MovingSprite *mGBClouds1; - MovingSprite *mGBClouds1b; - MovingSprite *mGBClouds2; - MovingSprite *mGBClouds2b; - Sprite *mGrass; - - // Instrucciones - Sprite *mInstructions; - - // Fondo de la pantalla de titulo - Sprite *mTitleTile; - - // Ventana visible de la textura de fondo del titulo - SDL_Rect mBackgroundWindow; - - // Vector con los valores del seno para 360 grados - double mSen[360]; - - // Texto - struct text - { - Text *white; - Text *black; - Text *nokia; - }; - - text mText; - - // Variable con lkos menus del juego - struct menu - { - Menu *title; // Menu de la pantalla de título - Menu *pause; // Menú de la pantalla de pausa - Menu *gameOver; // Menú de la pantalla de game over - Menu *options; // Menú de la pantalla de opciones - Menu *active; // Menu activo (de momento para la pantalla del titulo) - }; - - menu mMenu; - - // Notificación GetReady! - SmartSprite *mGetReadyBitmap; - - // Dibujos de la intro - SmartSprite *mIntroBitmap[INTRO_TOTAL_BITMAPS]; - - // Sprites con el titulo del juego para la pantalla de titulo - SmartSprite *mCoffeeBitmap; - SmartSprite *mCrisisBitmap; - AnimatedSprite *mDustSpriteLeft; - AnimatedSprite *mDustSpriteRight; - - // Sprites con los puntos de algunos objetos - SmartSprite *m1000Bitmap; - SmartSprite *m2500Bitmap; - SmartSprite *m5000Bitmap; - - // Vector para almacenar y gestionar SmartSprites - SmartSprite *mSmartSprite[MAX_SMART_SPRITES]; - - // Textos de la intro - Text2 *mIntroText[INTRO_TOTAL_TEXTS]; - - // Vector para coordinar los eventos de la intro - Uint8 mIntroEvents[INTRO_TOTAL_EVENTS]; - - // Vector para coordinar los eventos de la pantalla de titulo - Uint8 mTitleEvents[TITLE_TOTAL_EVENTS]; - - // Indicador para el bucle del titulo - Uint8 mTitleStatus; - - struct game - { - Uint32 score; // Puntuación actual - Uint32 hiScore; // Puntuación máxima - Uint8 status; // Indicador para el bucle principal - bool paused; // Idica si el juego está en pausa - bool hiScoreAchieved; // Indica si se ha superado la puntuación máxima - Uint8 stage; // Pantalla actual - Uint8 stageCounter; // Contador para el tiempo visible del texto de Stage - double stagePath[STAGE_COUNTER]; // Vector con los puntos Y por donde pasará la etiqueta - Uint16 deathCounter; // Contador para la animación de muerte del jugador - Uint8 deathIndex; // Indice del vector de smartsprites que contiene el sprite del jugador - }; - - game mGame; - - // Contador de ticks para ajustar la velocidad del juego - Uint32 mTicks; - - // Velocidad a la que se repite el bucle de juego - Uint8 mTicksSpeed; - - // Nivel de amenaza actual - Uint8 mMenaceLevel; - - // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, - // se generan más globos. Si el umbral aumenta, aumenta el numero de globos - Uint8 mMenaceLevelThreshold; - - // Indica si el bucle de juego avanza o solo pinta - bool mPlayFieldDrawOnly; - - // Indica si se va a mostrar la información de debug - bool mDebug; - - // Almacena los códigos de teclado correspondientes - Input mKeyboard; - - // Buffer para teclas pulsadas - Input mKeyboardBuffer; - - // Indica si el tiempo está detenido - bool mTimeStopped; - - // Temporizador para llevar la cuenta del tiempo detenido - Uint16 mTimeStoppedTimer; - - // Cantidad de explosiones restantes - Uint8 mRemainingExplosions; - - // Temporizador para la cantidad de explosiones restantes - Uint16 mRemainingExplosionsTimer; - - // Indica si las explosiones estan en marcha - bool mExplosionTime; - - // Contador para las instrucciones - Uint16 mInstructionsCounter; - - // Temporizador para la pantalla de titulo - Uint16 mTitleTimer; - - // Temporizador para el fondo de tiles de la pantalla de titulo - Uint16 mTitleBackgroundTimer; - - // Variable para almacenar el tipo de efecto que hará el foindo del titulo - Uint8 mTitleBackgroundMode; - - // Indicador para saber si se muestra el menu del titulo o la frase intermitente - bool mTitleMenuVisible; - - // Indica si está activo el modo demo - bool mDemo; - - // Indica si está activado el modo para grabar la demo - bool mDemoRecording; - - // Contador para el modo demo - Uint16 mDemoCounter; - DemoKeys mDemoKeys; - - // Indica a que estado pasara el juego cuando acabe el temporizador del titulo - Uint8 mTiteNextGS; - - // Contador para el juego - Uint32 mGameCounter; - - // Variable para evitar la repetición de teclas en los menus - bool mMenuKeyPressed; - - // Variables para el tamaño y modo de la ventana y variables para almacenar el valor si cancelamos en el menu de opciones - Uint32 mFullScreenMode; - Uint32 mFullScreenModePrevious; - Uint8 mWindowSize; - Uint8 mWindowSizePrevious; - - // Variables para el logo - struct logo - { - Uint16 counter; - Sprite *sprite; - }; - logo mLogo; - - // Path del ejecutable - std::string mExecutablePath; - - // Recursos - struct resourceBinFile - { - std::string file; - bool loaded; - }; - - resourceBinFile mBinFile[TOTAL_BINFILE]; - - struct resourceSound - { - std::string file; - bool loaded; - JA_Sound sound; - }; - - resourceSound mSound[TOTAL_SOUND]; - - struct resourceMusic - { - std::string file; - bool loaded; - JA_Music music; - }; - - resourceMusic mMusic[TOTAL_MUSIC]; - - struct resourceTexture - { - std::string file; - bool loaded; - LTexture *texture; - }; - - resourceTexture mTexture[TOTAL_TEXTURE]; }; #endif diff --git a/source/ifdefs.h b/source/ifdefs.h index c0a80c0..14980c7 100644 --- a/source/ifdefs.h +++ b/source/ifdefs.h @@ -12,4 +12,6 @@ #ifdef __linux__ #include "/usr/include/SDL2/SDL.h" -#endif \ No newline at end of file +#endif + +#define UNUSED \ No newline at end of file diff --git a/source/item.h b/source/item.h index 2f0706a..b554a3f 100644 --- a/source/item.h +++ b/source/item.h @@ -1,7 +1,7 @@ #pragma once #include "ifdefs.h" #include "animatedsprite.h" -#include "struct.h" +#include "utils.h" #ifndef ITEM_H #define ITEM_H diff --git a/source/ltexture.cpp b/source/ltexture.cpp index 3b44063..cc2d146 100644 --- a/source/ltexture.cpp +++ b/source/ltexture.cpp @@ -16,7 +16,7 @@ LTexture::LTexture() LTexture::~LTexture() { // Deallocate - free(); + unload(); } bool LTexture::loadFromFile(std::string path, SDL_Renderer *renderer) @@ -46,14 +46,14 @@ bool LTexture::loadFromFile(std::string path, SDL_Renderer *renderer) } // Get rid of preexisting texture - free(); + unload(); // The final texture SDL_Texture *newTexture = NULL; // Load image at specified path //SDL_Surface *loadedSurface = IMG_Load(path.c_str()); - SDL_Surface *loadedSurface = SDL_CreateRGBSurfaceWithFormatFrom((void*)data, width, height, depth, pitch, pixel_format); + SDL_Surface *loadedSurface = SDL_CreateRGBSurfaceWithFormatFrom((void *)data, width, height, depth, pitch, pixel_format); if (loadedSurface == NULL) { printf("Unable to load image %s!\n", path.c_str()); @@ -61,7 +61,7 @@ bool LTexture::loadFromFile(std::string path, SDL_Renderer *renderer) else { // Color key image - SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, COLOR_KEY_R, COLOR_KEY_G, COLOR_KEY_B)); + //SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, COLOR_KEY_R, COLOR_KEY_G, COLOR_KEY_B)); // Create texture from surface pixels newTexture = SDL_CreateTextureFromSurface(renderer, loadedSurface); @@ -102,7 +102,7 @@ bool LTexture::createBlank(SDL_Renderer *renderer, int width, int height, SDL_Te return mTexture != NULL; } -void LTexture::free() +void LTexture::unload() { // Free texture if it exists if (mTexture != NULL) @@ -132,7 +132,7 @@ void LTexture::setAlpha(Uint8 alpha) SDL_SetTextureAlphaMod(mTexture, alpha); } -void LTexture::render(SDL_Renderer *renderer, int x, int y, SDL_Rect *clip, double angle, SDL_Point *center, SDL_RendererFlip flip) +void LTexture::render(SDL_Renderer *renderer, int x, int y, SDL_Rect *clip, float zoomW, float zoomH, double angle, SDL_Point *center, SDL_RendererFlip flip) { // Set rendering space and render to screen SDL_Rect renderQuad = {x, y, mWidth, mHeight}; @@ -144,6 +144,9 @@ void LTexture::render(SDL_Renderer *renderer, int x, int y, SDL_Rect *clip, doub renderQuad.h = clip->h; } + renderQuad.w = renderQuad.w * zoomW; + renderQuad.h = renderQuad.h * zoomH; + // Render to screen SDL_RenderCopyEx(renderer, mTexture, clip, &renderQuad, angle, center, flip); } diff --git a/source/ltexture.h b/source/ltexture.h index 3321666..e533620 100644 --- a/source/ltexture.h +++ b/source/ltexture.h @@ -23,7 +23,7 @@ public: bool createBlank(SDL_Renderer *renderer, int width, int height, SDL_TextureAccess = SDL_TEXTUREACCESS_STREAMING); // Deallocates texture - void free(); + void unload(); // Set color modulation void setColor(Uint8 red, Uint8 green, Uint8 blue); @@ -35,7 +35,7 @@ public: void setAlpha(Uint8 alpha); // Renders texture at given point - void render(SDL_Renderer *renderer, int x, int y, SDL_Rect *clip = NULL, double angle = 0.0, SDL_Point *center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE); + void render(SDL_Renderer *renderer, int x, int y, SDL_Rect *clip = NULL, float zoomW = 1, float zoomH = 1, double angle = 0.0, SDL_Point *center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE); // Set self as render target void setAsRenderTarget(SDL_Renderer *renderer); diff --git a/source/main.cpp b/source/main.cpp index 4d42b08..24f7ec3 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,10 +1,10 @@ /* This source code copyrighted by JailDesigner (2020) started on Castalla 15-07-2020. -Using some sample source code from Lazy Foo' Productions */ -/*Descripción del enfoque utilizado para crear el juego. +/* +Descripción del enfoque utilizado para crear el juego. El programa contine una serie de clases/objetos básicos: la clase sprite permite dibujar partes de un fichero png en pantalla. La clase AnimatedSprite @@ -37,11 +37,11 @@ un tipo asociado diferente a NO_KIND */ #include "ifdefs.h" +#include "const.h" +#include "gamedirector.h" #include #include #include -#include "const.h" -#include "gamedirector.h" int main(int argc, char *args[]) { @@ -49,82 +49,33 @@ int main(int argc, char *args[]) srand(time(nullptr)); // Crea el objeto gameDirector - GameDirector *gameDirector = new GameDirector(); + GameDirector *gameDirector = new GameDirector(args[0]); + printf("Starting the game...\n\n"); - // Establece el valor de la variable con el path del ejecutable - gameDirector->setExecutablePath(args[0]); - - // Inicializa la lista de ficheros - gameDirector->setFileList(); - - // Comprueba que existen todos los ficheros - if (!gameDirector->checkFileList()) + // Mientras no se quiera salir del juego + while (!(gameDirector->exit())) { - return -1; - } - - // Arranca SDL y crea la ventana - if (!gameDirector->initSDL()) - { - printf("Failed to initialize!\n"); - return -1; - } - else - { - // Carga los recursos - if (!gameDirector->loadMedia(GAME_STATE_INIT)) + switch (gameDirector->getProgSection()) { - printf("Failed to load media!\n"); + case PROG_SECTION_LOGO: + gameDirector->runLogo(); + break; + case PROG_SECTION_INTRO: + gameDirector->runIntro(); + break; + case PROG_SECTION_TITLE: + gameDirector->runTitle(gameDirector->getSubsection()); + break; + case PROG_SECTION_GAME: + gameDirector->runGame(); + break; } - else - { - // Inicializa el objeto gameDirector - gameDirector->init(false); - printf("Starting the game...\n\n"); - - // Mientras no se quiera salir del juego - while (!(gameDirector->getGameStatus() == GAME_STATE_QUIT)) - { - switch (gameDirector->getGameStatus()) - { - case GAME_STATE_LOGO: - gameDirector->loadMedia(GAME_STATE_LOGO); - gameDirector->runLogo(); - gameDirector->unLoadMedia(GAME_STATE_LOGO); - break; - case GAME_STATE_INTRO: - gameDirector->loadMedia(GAME_STATE_INTRO); - gameDirector->runIntro(); - gameDirector->unLoadMedia(GAME_STATE_INTRO); - break; - case GAME_STATE_TITLE: - gameDirector->loadMedia(GAME_STATE_TITLE); - gameDirector->runTitle(); - gameDirector->unLoadMedia(GAME_STATE_TITLE); - break; - case GAME_STATE_PLAYING: - gameDirector->loadMedia(GAME_STATE_PLAYING); - gameDirector->runGame(); - gameDirector->unLoadMedia(GAME_STATE_PLAYING); - break; - case GAME_STATE_GAME_OVER_SCREEN: - gameDirector->loadMedia(GAME_STATE_GAME_OVER_SCREEN); - gameDirector->runGameOverScreen(); - gameDirector->unLoadMedia(GAME_STATE_GAME_OVER_SCREEN); - break; - case GAME_STATE_INSTRUCTIONS: - gameDirector->loadMedia(GAME_STATE_INSTRUCTIONS); - gameDirector->runInstructions(); - gameDirector->unLoadMedia(GAME_STATE_INSTRUCTIONS); - break; - } - } - } - - // Libera todos los recursos y cierra SDL - delete gameDirector; - printf("Shutting down the game...\n"); - - return 0; } + + // Libera todos los recursos y cierra SDL + delete gameDirector; + gameDirector = nullptr; + printf("Shutting down the game...\n"); + + return 0; } diff --git a/source/movingsprite.cpp b/source/movingsprite.cpp index 8954677..63ec6a5 100644 --- a/source/movingsprite.cpp +++ b/source/movingsprite.cpp @@ -33,6 +33,10 @@ void MovingSprite::init(float x, float y, int w, int h, float velx, float vely, setAccelX(accelx); setAccelY(accely); + // Establece el zoom W,H del sprite + setZoomW(1); + setZoomH(1); + // Establece la textura donde están los gráficos para el sprite setTexture(texture); @@ -56,7 +60,8 @@ void MovingSprite::move() // Muestra el sprite por pantalla void MovingSprite::render() { - mTexture->render(mRenderer, (int)mPosX, (int)mPosY, &mSpriteClip); + //mTexture->render(mRenderer, (int)mPosX, (int)mPosY, &mSpriteClip); + mTexture->render(mRenderer, (int)mPosX, (int)mPosY, &mSpriteClip, mZoomW, mZoomH); } // Establece el valor de la variable @@ -95,6 +100,18 @@ float MovingSprite::getAccelY() return mAccelY; } +// Establece el valor de la variable +float MovingSprite::getZoomW() +{ + return mZoomW; +} + +// Establece el valor de la variable +float MovingSprite::getZoomH() +{ + return mZoomH; +} + // Establece el valor de la variable void MovingSprite::setPosX(float x) { @@ -129,4 +146,16 @@ void MovingSprite::setAccelX(float x) void MovingSprite::setAccelY(float y) { mAccelY = y; +} + +// Establece el valor de la variable +void MovingSprite::setZoomW(float w) +{ + mZoomW = w; +} + +// Establece el valor de la variable +void MovingSprite::setZoomH(float h) +{ + mZoomH = h; } \ No newline at end of file diff --git a/source/movingsprite.h b/source/movingsprite.h index f4bdcd6..d609fca 100644 --- a/source/movingsprite.h +++ b/source/movingsprite.h @@ -8,6 +8,19 @@ // Clase MovingSprite. Añade posicion y velocidad en punto flotante class MovingSprite : public Sprite { +private: + float mPosX; // Posición en el eje X + float mPosY; // Posición en el eje Y + + float mVelX; // Velocidad en el eje X. Cantidad de pixeles a desplazarse + float mVelY; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse + + float mAccelX; // Aceleración en el eje X. Variación de la velocidad + float mAccelY; // Aceleración en el eje Y. Variación de la velocidad + + float mZoomW; // Zoom aplicado a la anchura + float mZoomH; // Zoom aplicado a la altura + public: // Constructor MovingSprite(); @@ -26,52 +39,51 @@ public: // Obten el valor de la variable float getPosX(); - + // Obten el valor de la variable float getPosY(); - + // Obten el valor de la variable float getVelX(); - + // Obten el valor de la variable float getVelY(); // Obten el valor de la variable float getAccelX(); - + // Obten el valor de la variable float getAccelY(); - + + // Obten el valor de la variable + float getZoomW(); + + // Obten el valor de la variable + float getZoomH(); + // Establece el valor de la variable void setPosX(float x); - + // Establece el valor de la variable void setPosY(float y); // Establece el valor de la variable void setVelX(float x); - + // Establece el valor de la variable void setVelY(float y); // Establece el valor de la variable void setAccelX(float x); - + // Establece el valor de la variable void setAccelY(float y); -private: - // Posición - float mPosX; - float mPosY; + // Establece el valor de la variable + void setZoomW(float w); - // Velocidad - float mVelX; - float mVelY; - - // Aceleración - float mAccelX; - float mAccelY; + // Establece el valor de la variable + void setZoomH(float h); }; #endif diff --git a/source/player.cpp b/source/player.cpp index a0f86d4..83a0cf1 100644 --- a/source/player.cpp +++ b/source/player.cpp @@ -6,6 +6,7 @@ Player::Player() { mSpriteLegs = new AnimatedSprite(); mSpriteBody = new AnimatedSprite(); + mSpriteHead = new AnimatedSprite(); init(0, 0, nullptr, nullptr, nullptr); } @@ -17,6 +18,7 @@ Player::~Player() mSpriteBody = nullptr; delete mSpriteLegs; delete mSpriteBody; + delete mSpriteHead; } // Iniciador @@ -27,8 +29,9 @@ void Player::init(float x, int y, LTexture *textureLegs, LTexture *textureBody, mStatusWalking = PLAYER_STATUS_WALKING_STOP; mStatusFiring = PLAYER_STATUS_FIRING_NO; mInvulnerable = false; - mInvulnerableTimer = PLAYER_INVULNERABLE_TIMER; + mInvulnerableCounter = PLAYER_INVULNERABLE_COUNTER; mExtraHit = false; + mCoffees = 0; // Establece la altura y el ancho del jugador mWidth = 3 * BLOCK; @@ -51,12 +54,8 @@ void Player::init(float x, int y, LTexture *textureLegs, LTexture *textureBody, // Establece la velocidad base mBaseSpeed = 1.5; - // Establece el numero inicial de vidas - mStartingLives = 3; - mLives = mStartingLives; - // Establece la puntuación inicial - mScore = 0; + mScore = 9500; // Establece el multiplicador de puntos inicial mScoreMultiplier = 1.0f; @@ -331,7 +330,7 @@ void Player::render() { if (mInvulnerable) { - if ((mInvulnerableTimer % 10) > 4) + if ((mInvulnerableCounter % 10) > 4) { mSpriteLegs->render(); mSpriteBody->render(); @@ -694,26 +693,26 @@ void Player::setInvulnerable(bool value) // Obtiene el valor de la variable bool Player::getInvulnerableTimer() { - return mInvulnerableTimer; + return mInvulnerableCounter; } // Establece el valor de la variable void Player::setInvulnerableTimer(Uint16 value) { - mInvulnerableTimer = value; + mInvulnerableCounter = value; } // Actualiza el valor de la variable void Player::updateInvulnerableTimer() { - if (mInvulnerableTimer > 0) + if (mInvulnerableCounter > 0) { - --mInvulnerableTimer; + --mInvulnerableCounter; } else { mInvulnerable = false; - mInvulnerableTimer = PLAYER_INVULNERABLE_TIMER; + mInvulnerableCounter = PLAYER_INVULNERABLE_COUNTER; } } @@ -734,7 +733,7 @@ void Player::removeExtraHit() { mExtraHit = false; mInvulnerable = true; - mInvulnerableTimer = PLAYER_INVULNERABLE_TIMER; + mInvulnerableCounter = PLAYER_INVULNERABLE_COUNTER; } // Obtiene el circulo de colisión diff --git a/source/player.h b/source/player.h index 0a9148c..322b67d 100644 --- a/source/player.h +++ b/source/player.h @@ -1,5 +1,5 @@ #pragma once -#include "struct.h" +#include "utils.h" #include "animatedsprite.h" #ifndef PLAYER_H @@ -8,6 +8,38 @@ // The player class Player { +private: + float mPosX; // Posicion en el eje X + int mPosY; // Posicion en el eje Y + + Uint8 mWidth; // Anchura + Uint8 mHeight; // Altura + + float mVelX; // Cantidad de pixeles a desplazarse en el eje X + int mVelY; // Cantidad de pixeles a desplazarse en el eje Y + + float mBaseSpeed; // Velocidad base del jugador + int mCooldown; // Contador durante el cual no puede disparar + + Uint32 mScore; // Puntos del jugador + float mScoreMultiplier; // Multiplicador de puntos + + Uint8 mStatusWalking; // Estado del jugador + Uint8 mStatusFiring; // Estado del jugador + + bool mAlive; // Indica si el jugador está vivo + bool mInvulnerable; // Indica si el jugador es invulnerable + Uint16 mInvulnerableCounter; // Temporizador para la invulnerabilidad + bool mExtraHit; // Indica si el jugador tiene un toque extra + Uint8 mCoffees; // Indica cuantos cafes lleva acumulados + + AnimatedSprite *mSpriteLegs; // Sprite para dibujar las piernas + AnimatedSprite *mSpriteBody; // Sprite para dibujar el cuerpo + AnimatedSprite *mSpriteHead; // Sprite para dibujar la cabeza + + Circle mCollider; // Circulo de colisión del jugador + void shiftColliders(); // Actualiza el circulo de colisión a la posición del jugador + public: // Constructor Player(); @@ -111,63 +143,6 @@ public: // Obtiene el circulo de colisión Circle &getCollider(); - -private: - // Posición X, Y del jugador - float mPosX; - int mPosY; - - // Altura y anchura del jugador - Uint8 mWidth; - Uint8 mHeight; - - // Velocidad X, Y del jugador - float mVelX; - int mVelY; - - // Velocidad base del jugador - float mBaseSpeed; - - // Contador durante el cual no puede disparar - int mCooldown; - - // Vidas actuales del jugador - Uint8 mLives; - - // Vidas iniciales del jugador - Uint8 mStartingLives; - - // Puntos del jugador - Uint32 mScore; - - // Multiplicador de puntos - float mScoreMultiplier; - - // Estado del jugador - Uint8 mStatusWalking; - Uint8 mStatusFiring; - - // Indica si el jugador está vivo - bool mAlive; - - // Indica si el jugador es invulnerable - bool mInvulnerable; - - // Temporizador para la invulnerabilidad - Uint16 mInvulnerableTimer; - - // Indica si el jugador tiene un toque extra - bool mExtraHit; - - // Sprite para dibujar al jugador en pantalla - AnimatedSprite *mSpriteLegs; - AnimatedSprite *mSpriteBody; - - // Circulo de colisión del jugador - Circle mCollider; - - // Actualiza el circulo de colisión a la posición del jugador - void shiftColliders(); }; #endif diff --git a/source/text.cpp b/source/text.cpp index b4b8cce..dbc8ba8 100644 --- a/source/text.cpp +++ b/source/text.cpp @@ -31,7 +31,6 @@ void Text::init(LTexture *texture, SDL_Renderer *renderer, Uint8 type, Uint8 siz mSprite->setSpriteClip(0, 0, mSprite->getWidth(), mSprite->getHeight()); // Cadena con los caracteres ascii que se van a inicializar - // std::string text = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-/().:#!"; std::string text = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ{\\[]]^_`abcdefghijklmnopqrstuvwxyz"; // Inicializa a cero el vector con las coordenadas diff --git a/source/text.h b/source/text.h index f651c59..8f6f86b 100644 --- a/source/text.h +++ b/source/text.h @@ -7,6 +7,20 @@ // Clase texto. Pinta texto en pantalla a partir de un bitmap class Text { + private: + Sprite *mSprite;// Objeto con los graficos para el texto + + struct Offset + { + int x; + int y; + Uint8 w; + }; + Offset mOffset[255];// Vector con las posiciones y ancho de cada letra + + Uint8 mType;// Indica si el texto es de anchura fija o variable + Uint8 mSize;// Altura del texto + public: // Constructor Text(); @@ -26,7 +40,7 @@ public: void writeColored(int x, int y, std::string text, Uint8 R, Uint8 G, Uint8 B); // Escribe el texto centrado en un punto x y con kerning - void writeCentered(int x, int y, std::string text, int kerning); + void writeCentered(int x, int y, std::string text, int kerning = 0); // Obtiene la longitud en pixels de una cadena Uint16 lenght(std::string text, int kerning); @@ -42,25 +56,6 @@ public: // Establece el valor de la variable void setSize(Uint8 size); - -private: - // Objeto con los graficos para el texto - Sprite *mSprite; - - // Coordenadas dentro del PNG para cada código ascii y su anchura - struct Offset - { - int x; - int y; - Uint8 w; - }; - - // Vector con las posiciones y ancho de cada letra - Offset mOffset[255]; - - // Indica si el texto es de anchura fija o variable - Uint8 mType; - Uint8 mSize; }; #endif diff --git a/source/utils.cpp b/source/utils.cpp new file mode 100644 index 0000000..ca82b32 --- /dev/null +++ b/source/utils.cpp @@ -0,0 +1,112 @@ +#include "utils.h" + +// Calcula el cuadrado de la distancia entre dos puntos +double distanceSquared(int x1, int y1, int x2, int y2) +{ + int deltaX = x2 - x1; + int deltaY = y2 - y1; + return deltaX * deltaX + deltaY * deltaY; +} + +// Detector de colisiones entre dos circulos +bool checkCollision(Circle &a, Circle &b) +{ + // Calcula el radio total al cuadrado + int totalRadiusSquared = a.r + b.r; + totalRadiusSquared = totalRadiusSquared * totalRadiusSquared; + + // Si la distancia entre el centro de los circulos es inferior a la suma de sus radios + if (distanceSquared(a.x, a.y, b.x, b.y) < (totalRadiusSquared)) + { + // Los circulos han colisionado + return true; + } + + // En caso contrario + return false; +} + +// Detector de colisiones entre un circulo y un rectangulo +bool checkCollision(Circle &a, SDL_Rect &b) +{ + //Closest point on collision box + int cX, cY; + + //Find closest x offset + if (a.x < b.x) + { + cX = b.x; + } + else if (a.x > b.x + b.w) + { + cX = b.x + b.w; + } + else + { + cX = a.x; + } + + //Find closest y offset + if (a.y < b.y) + { + cY = b.y; + } + else if (a.y > b.y + b.h) + { + cY = b.y + b.h; + } + else + { + cY = a.y; + } + + //If the closest point is inside the circle + if (distanceSquared(a.x, a.y, cX, cY) < a.r * a.r) + { + //This box and the circle have collided + return true; + } + + //If the shapes have not collided + return false; +} + +// Detector de colisiones entre un dos rectangulos +bool checkCollision(SDL_Rect &a, SDL_Rect &b) +{ + //Calculate the sides of rect A + int leftA = a.x; + int rightA = a.x + a.w; + int topA = a.y; + int bottomA = a.y + a.h; + + //Calculate the sides of rect B + int leftB = b.x; + int rightB = b.x + b.w; + int topB = b.y; + int bottomB = b.y + b.h; + + //If any of the sides from A are outside of B + if (bottomA <= topB) + { + return false; + } + + if (topA >= bottomB) + { + return false; + } + + if (rightA <= leftB) + { + return false; + } + + if (leftA >= rightB) + { + return false; + } + + //If none of the sides from A are outside B + return true; +} \ No newline at end of file diff --git a/source/struct.h b/source/utils.h similarity index 51% rename from source/struct.h rename to source/utils.h index bfde6fc..2efa4e9 100644 --- a/source/struct.h +++ b/source/utils.h @@ -1,42 +1,57 @@ -#pragma once -#include "ifdefs.h" - -#ifndef STRUCT_H -#define STRUCT_H - -// Estructura para definir un circulo -struct Circle -{ - Uint16 x; - Uint16 y; - Uint8 r; -}; - -// Estructura para definir todas las entradas que aceptará el programa -struct Input -{ - Uint8 up; - Uint8 down; - Uint8 left; - Uint8 right; - Uint8 escape; - Uint8 pause; - Uint8 fire; - Uint8 fireLeft; - Uint8 fireRight; - Uint8 accept; - Uint8 cancel; -}; - -// Estructura para mapear el teclado usado en la demo -struct DemoKeys -{ - Uint8 left; - Uint8 right; - Uint8 noInput; - Uint8 fire; - Uint8 fireLeft; - Uint8 fireRight; -}; - -#endif +#pragma once +#include "ifdefs.h" + +#ifndef UTILS_H +#define UTILS_H + +// Estructura para definir un circulo +struct Circle +{ + Uint16 x; + Uint16 y; + Uint8 r; +}; + +// Estructura para definir todas las entradas que aceptará el programa +struct Input +{ + Uint8 up; + Uint8 down; + Uint8 left; + Uint8 right; + Uint8 escape; + Uint8 pause; + Uint8 fire; + Uint8 fireLeft; + Uint8 fireRight; + Uint8 accept; + Uint8 cancel; +}; + +// Estructura para mapear el teclado usado en la demo +struct DemoKeys +{ + Uint8 left; + Uint8 right; + Uint8 noInput; + Uint8 fire; + Uint8 fireLeft; + Uint8 fireRight; +}; + +// Calcula el cuadrado de la distancia entre dos puntos +double distanceSquared(int x1, int y1, int x2, int y2); + +// Detector de colisiones entre dos circulos +bool checkCollision(Circle &a, Circle &b); + +// Detector de colisiones entre un circulo y un rectangulo +bool checkCollision(Circle &a, SDL_Rect &b); + +// Detector de colisiones entre un dos rectangulos +bool checkCollision(SDL_Rect a, SDL_Rect b); + +// Inicializa el vector con los valores del seno +void initSin(); + +#endif \ No newline at end of file