From cd8549349525e6cf62360208b80aff26a257c7b2 Mon Sep 17 00:00:00 2001 From: kts Date: Sat, 27 Dec 2014 19:48:24 -0800 Subject: [PATCH] Initial commit of LD31 release of Petite Juliet. --- Makefile | 45 ++++++ gfx/doubt.ase | Bin 0 -> 5132 bytes gfx/doubt.png | Bin 0 -> 1304 bytes gfx/health.png | Bin 0 -> 215 bytes gfx/juliet.ase | Bin 0 -> 12832 bytes gfx/juliet.png | Bin 0 -> 2393 bytes gfx/juliet_palette.ase | Bin 0 -> 1246 bytes gfx/screens.png | Bin 0 -> 4239 bytes gfx/waves.ase | Bin 0 -> 3029 bytes gfx/waves.png | Bin 0 -> 1834 bytes gfx/world.png | Bin 0 -> 403 bytes sfx/hit.wav | Bin 0 -> 25388 bytes sfx/jump.wav | Bin 0 -> 25388 bytes sfx/kick.wav | Bin 0 -> 4516 bytes sfx/punch.wav | Bin 0 -> 2456 bytes sfx/whoosh.wav | Bin 0 -> 7616 bytes sfx/wiff.wav | Bin 0 -> 7616 bytes shaders/sprite_fs.glsl | 9 ++ shaders/sprite_vs.glsl | 15 ++ shaders/world_fs.glsl | 23 +++ shaders/world_vs.glsl | 12 ++ src/Enemy.cpp | 127 +++++++++++++++ src/Enemy.hpp | 14 ++ src/Entity.cpp | 57 +++++++ src/Entity.hpp | 54 +++++++ src/Juliet.cpp | 275 +++++++++++++++++++++++++++++++++ src/Juliet.hpp | 39 +++++ src/Sprite.cpp | 80 ++++++++++ src/Sprite.hpp | 39 +++++ src/World.cpp | 12 ++ src/World.hpp | 17 ++ src/a_common.cpp | 16 ++ src/a_common.hpp | 19 +++ src/g_common.cpp | 20 +++ src/g_common.hpp | 20 +++ src/game.cpp | 342 +++++++++++++++++++++++++++++++++++++++++ src/game.hpp | 4 + src/main.cpp | 126 +++++++++++++++ src/shader.cpp | 58 +++++++ src/shader.hpp | 17 ++ src/texture.cpp | 51 ++++++ src/texture.hpp | 12 ++ src/v_common.cpp | 39 +++++ src/v_common.hpp | 40 +++++ todo.ktx | 20 +++ 45 files changed, 1602 insertions(+) create mode 100644 Makefile create mode 100644 gfx/doubt.ase create mode 100644 gfx/doubt.png create mode 100644 gfx/health.png create mode 100644 gfx/juliet.ase create mode 100644 gfx/juliet.png create mode 100644 gfx/juliet_palette.ase create mode 100644 gfx/screens.png create mode 100644 gfx/waves.ase create mode 100644 gfx/waves.png create mode 100644 gfx/world.png create mode 100644 sfx/hit.wav create mode 100644 sfx/jump.wav create mode 100644 sfx/kick.wav create mode 100644 sfx/punch.wav create mode 100644 sfx/whoosh.wav create mode 100644 sfx/wiff.wav create mode 100644 shaders/sprite_fs.glsl create mode 100644 shaders/sprite_vs.glsl create mode 100644 shaders/world_fs.glsl create mode 100644 shaders/world_vs.glsl create mode 100644 src/Enemy.cpp create mode 100644 src/Enemy.hpp create mode 100644 src/Entity.cpp create mode 100644 src/Entity.hpp create mode 100644 src/Juliet.cpp create mode 100644 src/Juliet.hpp create mode 100644 src/Sprite.cpp create mode 100644 src/Sprite.hpp create mode 100644 src/World.cpp create mode 100644 src/World.hpp create mode 100644 src/a_common.cpp create mode 100644 src/a_common.hpp create mode 100644 src/g_common.cpp create mode 100644 src/g_common.hpp create mode 100644 src/game.cpp create mode 100644 src/game.hpp create mode 100644 src/main.cpp create mode 100644 src/shader.cpp create mode 100644 src/shader.hpp create mode 100644 src/texture.cpp create mode 100644 src/texture.hpp create mode 100644 src/v_common.cpp create mode 100644 src/v_common.hpp create mode 100644 todo.ktx diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..26a1f50 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +BITS ?= $(shell getconf LONG_BIT) +ifeq ($(BITS),64) + CXXFLAGS=-m64 + LDFLAGS=-m64 + LIB_DIR=lib64 +else + CXXFLAGS=-m32 + LDFLAGS=-m32 + LIB_DIR=lib +endif +CXX=g++ +DEBUG=-g +CXXFLAGS+=$(DEBUG) -Wall `sdl2-config --cflags` -c +#LDFLAGS+=-Wall `sdl2-config --libs` -lSDL2_image -lSDL2_mixer -lGLEW -lGL +#LDFLAGS+= -Wall -L/usr/$(LIB_DIR) -Wl,-rpath=$(LIB_DIR)/ -lSDL2 -lpthread -Wl,--no-undefined -lm -ldl -lpthread -lrt -Wl,-Bstatic -lSDL2_image -lSDL2_mixer -lGLEW -Wl,-Bdynamic -lGL +LDFLAGS+= -Wall -L/export/home/kts/Devel/sdl/$(LIB_DIR) -Wl,-rpath=$(LIB_DIR)/ -lSDL2 -lpthread -Wl,--no-undefined -lm -ldl -lpthread -lrt -Wl,-Bstatic -lSDL2_image -lSDL2_mixer -lGLEW -Wl,-Bdynamic -lGL +VPATH=src +BINARY=juliet$(BITS) +OBJ=main.o v_common.o a_common.o g_common.o shader.o texture.o World.o Sprite.o Entity.o Juliet.o Enemy.o game.o +OBJ_DIR=src/obj + +$(BINARY): $(patsubst %,$(OBJ_DIR)/%,$(OBJ)) + $(CXX) $^ -o $@ $(LDFLAGS) + +release: + make clean + BITS=32 make install + make clean + BITS=64 make install + +install: $(BINARY) + mkdir -p build/ + mkdir -p build/$(LIB_DIR)/ + cp -f /usr/$(LIB_DIR)/libpng14.so.14.12.0 build/$(LIB_DIR)/libpng14.so.14 + cp -f /usr/$(LIB_DIR)/libSDL2-2.0.so.0.2.1 build/$(LIB_DIR)/libSDL2-2.0.so.0 + cp -p $(BINARY) build/ + cp -rf gfx/ build/ + cp -rf sfx/ build/ + cp -rf shaders build/ + +$(OBJ_DIR)/%.o: %.cpp + $(CXX) $(CXXFLAGS) $< -o $@ + +clean: + rm -f $(OBJ_DIR)/*.o && rm -f $(BINARY) diff --git a/gfx/doubt.ase b/gfx/doubt.ase new file mode 100644 index 0000000000000000000000000000000000000000..30a85c5528719bf1fe21eb224ea4a8d222f0e3a0 GIT binary patch literal 5132 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 literal 0 HcmV?d00001 diff --git a/gfx/doubt.png b/gfx/doubt.png new file mode 100644 index 0000000000000000000000000000000000000000..b3288de088b5f9aca9f435e172112e56458417e2 GIT binary patch literal 1304 zcmV+z1?T#SP) zy>=Q&5QVGvIN&i@NZ6ds*pPi_E3`6`TM3xQAS(w_?G|GOP5nT3!~GKb_~=$ucUSj} zq)VF!00000002NlM2q>Fd)C*k;aPJxRmXO<2?dDgYvyjDhMsdb6Qgz$^}LABuD@3e ze|+`@kz;&+I(eljdZNA`#l7qAQNw5hZ&w?Bd3!C9b9AY|# zw}zLu*D~c0_;M}4V!mbxu$Zs;aNJEUM81Bh24c}lUaOXwdHLtxL`1Zl%}jdH9*(=R zn6FKBYc0|H7-RvMnM*0Axg&ZX`@etvGr0z-qm=Sr{gx-Gu2VMRayBzH;4p@%_(A=y z-eYE#KlM(w6eBT2@6k&B8t<^t6Evci@k#%N*QB5|VQ2LJ#700000&=V~fIWl70TVK20 zmg3YjQV~%bmbyrc8Xk_`=PWzct+kWPJT=L3#Q`6!&;L#Rz*#YBJc_iN%A$$n%!)zu z?f{Ga8eV95{ch?9j>d@SwIy(>Moc84o#N-^52W$DKH!WP8qfGI#i+S{Q?xht18ZBH z2alUeYb^RwN_lyEE!uUKxs=;OWc-(i>Z9xbSq&E(c+3xMO{z~4 zuOmrfOx0g%1Wz*^j=N%cg^)(pCUDQ<>)c*_bo@mA`PX{y`iJ^2(+{i-7+ybdhNJ1; zR?euEF~bU}_0};&=RdOk*8cnL1Qs{FS)JDFMs-^c5o&U)CXY}70 zwN33=dYr=k@egb2NuTO#t*Q6=N#&;uHS$LE*>!kh)Ybz400000008KL7W|~G#Hf9_ z3K(b93voE^QhtaY{XKX_jO}V;x)v)lzgL?z|BueY`+;lg!EL#zAK2rm9n!NtWBhoP zxX#V|z*&CcUiB@^-!stU{lH=>kJu6;`i%U;cs2Ep%*7pk$&YDq_eb>1=l9Ec@=FZu zNxf(On)CPcME$^H<_qsVu@QaLPaN^HM)eUt_A`<02i7{TA2>@ttmhv2qvKEa!#+E; z{hZL_6b@0_&j~&JumAu6000000G@@~UfkAVfX-;)uG$uA0G(31`C4mUoZ@Y9)s6ND ze%`DYMDb5{fP8L#KX6TbRt$-gF&{X=?}WZn%j>80>ZAEHV+hg4%iKuV1pkwMKQJ@% z%iC*-+%GIKB1RT%qZB`|7pWt%kuqLmWL=`KF^{wfz6}3C8@l&Dzmqn1PYI}PfFAu= z49_Li+tr4>dLi0;!`sq5uOa%f=|~M??)TP^r6(eDj4}PFADF0rKW|j8-Cv$lT=d?? z6I1*CbaMT_8Cp+dAA{(JPoAkK_?Vt(+JQDjfAyyj`Z0bu?#!oH?|RvQ`=8%kO{h1I zX9HyFiR>RsI(rhT+*4m;uIZ!sr|5~OzMr+GJ}WnW(5rs<_g?e>G|w;T1du{=ZFj@~ O0000LfY#_jTVZMd} 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 literal 0 HcmV?d00001 diff --git a/gfx/juliet.png b/gfx/juliet.png new file mode 100644 index 0000000000000000000000000000000000000000..ad0573b474ca65042ea139152fa85c7ebb32a050 GIT binary patch literal 2393 zcmV-f38wamP) zv2qhh5QclG3tWVhow|ga=+c$QbfNAApm-Dto&btB;2p#Z+=X`;w$mdKxSZNWL{W-7 z7wB3HdskoYDetCQ>D#Cf^EqRJD#^GQR`FH1?FOlDP z1|AM3F@zA_KDr|!qThf0Ei1Y6&Rg<71e%Se;_Ddao!>X#c@4~`@5OQ{x(;#P`9Ap0 zWi&p0pI=|oo7>xvTY&St}{c}w2qa=BbCmrJS|{GiVN+`RjNoj*vPi0bfzI{$O??gw`MAbE2?DADR`1o;%B zeEd%cFJ(1&%l@m{4@^XK_V@`Q{BSUdZy()>y`U&hw5mIVkZ&ZR)db}Icr4D1@@VjdL zq}Dvzf7ZNp|KWa2!X`ml6R2AKtk(UH@_n_x^9;-rh*u{klKkr!=hxRFHiQsf9v{cy zU=q)cj_A*ie`kFPnU_w4xZSn#9Ak2ND&~>nhmIe3{Goo%l#^b_OY>^QKqevDeppEz z90P3!!lGkGeEia)r42x4pc=e&45SZhZ@UJMu0pw7E|<&Ya^(v3a%#jlgc_We^SiK} zUWmnVN$L#UxNK6gT7$-~DVbwX*xD6Iy`jM%yltZ%3wytwW2rin4eh}=U* zW9*}b+yR*KlFro3ufgZal817mUQUe|ZC-lz)2cxtBtLs`r9A(CY5gVeqT|mHUpyms zfYJ-_-0PnytJ}Ek7}CoCEq-5Y=PCpd z<|Qq`PitKR!fUtRn%`IZH@eYaFQ!6_L#P4aQ}*@E4ZRUO8MOHPVeKfB)Q|FM8xS@- zx0V+biE#)uIFB!(iE_h_G%Lh7gc_We^D6rTqh8ugzgzH<7|xf&Z)3Oa4~*=~YP~;f zo5_vgd^x=D5A1TeT&|v_@iTtcKGb;)T!PR$V2kAvx)r=wF6qS=Sr?UNrRzkWw zmWb%n)p>aF4m z0yTJK0#q|^J$~Q=XgvUHwh|r!Z#{mBXaJI*2;ZU;;Y+fUTJU|1pU(TFd<*r>?QM`W z;Qe?^&n_8T-yd4V)U0Jpm*z)UG`I8vVEMsGdCR_o$DW3%S)<7mQp_^0m9< zP7u;=^xzZW*U?DZIWHeK>8?EJ*joofCj)39v(XfKWoe(LZXP)8Gw81Mr!4x4*ILc+ zIWg{Cg&@NAsMRp0vnM#GhV<+)zWoT9!pFBCJO3XKIDU`D(~ArGrNkKCLY z>AY#;(~AotTD|c+H!pvE^V`yM`1T;mVPlL@l9R>lPT%sL8>c;Bq{mFka||ME?EP@< zhD_pkMk|TI3_6j@{W{}=*yk!jI&jd5ue63VhBcr56e+!iLpxI%BlVR^1|s^R8002~ zBp<@FxSuU!Xe`5}b%@ly{*hh}A?9eY7qkFAmu-JoER5*dG4@@<)=3*}|Nt=ZcT|0(l4KA0<<#M@PQdw@2Y?WKX16^~!)f8jniw;?9PbPt$P62O~ z8_lZ`!?NEFEr#hP$yT}1yc#hq`|V(2Xm~O$*l^wLIJ?uMq=o z3+%}x+>a~an3d*6t&AFW$Xu55Y2=pV-&PY+!+qng^G3#SfNzmG98BWdM|UE2Hk!uw z<8d~}EY~i1Bt^<;p)h%P^;X1d0F?xmr#FS zG4|>QMtG~-8XjoP+WTZT#jxswU1MxrM&r}B)RT$u8oAk%n!~4gC0b4XWVzEC);z${ z@p0_4@7ghlFh4IG1N8%=o=(J5in8g?K69h_(cGFb(w|6-ZE?1qfzu25)ZxFYn3u53 zOV5NMgdqLu;(DbMnO7$#G&wyLv1wjOzl9J2x&(;UPlO-)G=?}1>IX)Br5c`6OHVE8 zCANH!HT{XiK;rB&{`Tqz*4ktl@swJ6YEdsSl3Ti?5A*3yBnA>k!;kKAxm+%n%ayBI zJfZuJQKP3>gRi0X&SkVx{G8S?YV}J zgRSvxn>E)?L^@tzj)Cs#V--V-P5)g3kLG5^jAszp7PTECw}?&KEcF=!&EIZ6QC>e< z^4hs`$B)MNTL;qeb82~8HT=D~2Se+PlM>bJEAE5e`a}kio}Y8u9=ka;cO&D=N`+t38hTF=;%)r3H0JT&}!NgU=Ce6~Y zKg{}mq1w#_Y3~lt`u)jl=F@uA!-=t)r`_Z(wL-Y+`C=ZeeL?c?j`9}pN691Er&m?_!SRDw|^rh0@%y zY0eLgMy?#o)xP@Y_xSu?@5lS`I{tV)-mmBD{dgS-mKL{pc|be>0Du=_YHW4Pu>b%N zdy?~5a)o$CA2Uv8=xt*F>(41^ulVQK!tHNrj|Kq#68v+4fV=|HVJt0It$+LKh}-D+%rr1HT&Wav z*)SeVrpt#EEuA|E_IeOH4u9jGTilsnCUvv)%eo@V51%KSNup?!eU?#KS}C;CiyKYA z=O5&Q-icJ(1{^@6M3o}eS_lhhhN-!sg{6vhz#U1eozasq-5ePNRwiiYB0i#MX)jFS zp}t$b#;&^KyBtaL$hv|7HOAD}9~-_~Z03<_(Z&Y}zlK~8l35nDh7G~6Jxbf#cr9?P3~7>{K`wDxJ?v%ulqt=DJ)_`-`H5E<$(TpaX`q;lZYkd& z9y^3uHLNc?a}2KzENx7EYDwonhHtmqgp=%3(h&^5&EJX3tXqouG4Ol}%9l-WUqGdWjBXdc=2inT#QbsT^z#)pApekwf_$S$IgXV z<;%j1p^QO`=UQ=zhrQ+%7LouNrzf|NwPPM+O_Cd{&8_<&daXsOVJ0Z|Eli8m)j+@e@#hY%PnNdn%I&Czhq7zsKsWHLjVBA~aS9Tb zI2L-%T(?xOxUd@l*l0{J^G)yN@KJ3IW6S;FtqM?=$TmKDe>udJBk%20R$>l%5cVMHycVIP`%kNWz}8z)WG71dgA^X(r~ zs;K^i{x-A80a|9{h+LtKFFo>a3i>JsDwY42yykfBMSL-|0|78|X1rUlac99+<%32# zigKHqy?$tEMb}`6qIdunF?E*2DScdou7arsZU2T9$qu0zc2~6<8MET01BlOF2t=LR zZj-U3Gox&VoGC?>go~K%=21-@zT=2>EAfB7FLsQ{r%@TLZ$4EWAjnoN}D{d z4*`?}sFL)N!y|>-h`Qp&2jgt0ph(Y^P63dJhiR(&>Y+8UhZ9&lvne(9*9=UraO6LE z7Favb4~sSt-TOLUQ=X=glo_VBtxYx=$kep-{duSiRwc^>cOLW-ssA8X>`^j-dx zYu$nNLRTN3l$pZ^mL}{&3+I5AT|EL4{ZNmD3|;jzA*_f#Eb$bYe2z>-=Fh$t#oD-` zClu36>z6?7a=ddV2USAVr2Jo9j+=?*HvELI{>03}kb)1}t8<${f8G9J$7C2Jd!$WD z;2*dY;#@NdM3W253u11;P0RYU@5^f8pI>3uqt2T}3|BU;JH(YJOKrt42-6UMTU8enJ^ zea#I=&+<08VBe8v10d~jH4crxlp3@Bz%}#tK2J%YxXa5d!O7avwb_DyZ zQxX_oYC@be$tRW7rvE(fUB37uvj4*C0KaFXp60c$mbxvh-kG86R6-2K*1tB=Y6u!E z8GHp8V_9j>bTcZA_W!^M+8KKs3mfF4F5Jp;@+HlV-+Sc*TiY}wp1^lo6Jw7wy+0kT zz~L*DJ1tIykMDjgm4_I7^WE8MW)GHy-xl97@2nP2e&M&gR;w!_iC+OSJ=2I+m!2iH ztKlX%|dGL^A zIfzeT@X}O@7P|`ON12J0N1-b3uTdYTK8(Min`_ z+JM)@<@&i-pVmNMF4&A7akWg`#y^3DXU;aVS^AG7>(f-m9TcmEV+2_36WPteX?loi zCfT|18_(;Y$=<2gc!uI?=*S=)4&vrI9#=Zs-h?183fvGMO~cOhuX0sB>bE|ONynwH z+(_!y;OBBNL_QXU;8bVt?~l$RTbgs(0c@5}f+=&oO1r~4onSo|v|fyW6}Gdo)`6{nFBEmg+M z=W{*P9vuR_Fy&sggixuf$|e-Th6d!+g|y6%AZ{+bz3$L>=hSii0 zN1d{AI9tK%T%sJ|KZ}wc++g&5%Z07y9z{84d#xlaZk&`7_Ef+a%A_rE1+DTJ)fO}` zab1cUg|qvTeZwcE;6|sc^t@HPJ$yE}VhnS7_$mEBaS7wtQd>SW2M~aIp$^0XIzzHZ z>Ni%xlJz_dsPK6H8J;< zhp?H6Uh{nJ$qhut?1LzYs3i$Cz9WlQ18x&9wB}t>75%#f_{KJ-E75v)b@D8X4v4P2 zsJibUs9QC*T{`!56#O*0W=`pd z7xA#>NiOFW zV4%?utFjfD!R;}C=Dc$ngtpO^w{{O66}PIgv|I9>4BwEHfsnxArFyvMN~l97lT z-`OkM`4o4x$rknC>he3ZeN_h6bCn^_QBQjc?U|!Wt$?9^^cNrDU5iN#)s6@#%pT{P6G!ofjIMMu%M;jw&A-4TjG-@<3ca8pr=SRN+7G8 z`4+U3=dY^b-C&c-_3kS^Sm>I~V7la)hVA%%U?Z>dc)cbgCGSI`r#1{&Io*y#Tr#(O znqJr`Gub5_a^e`C*LFSM-wiVR+OFe_g7y4$@c?z0LwxnPDMaUrY;y9MY`?0NiPyeo z-0c#`!Eqh$tsM}phAs?5Ycfv06HMCt)Z9e{--ug%(9O}Wm4ebwe^d-F)pjL16NZ|i ze{U5aHQa9F7+#H#6e6b?nVWAjT)7%(SaA!0;=+S;)A%}FNQ^13D+o4t5qM_tveqQw_kjkLBjPiFlZjVUE2cAg$ z!v~UkNe{JuzECKu6_6R+KSKCex8^r>|Ax+=zw+of&K$3=`;Ftn0p9c692@|X1E7_c zJF5kw?VU1BP`TedX$&7}b#|ZhK_a6bokR2>LA5}5p3shBbl=D7+dPI|BAajMOa}^l zMBnV{?Zh()HDj&z$Tb39xx;|gRKVW&`+B^Qa2~sd`!sz0KiT(wkI_f}ebwOZJgsJn z#ayZT7$EksxY!_iC4}|W;)eekWv}CC3v+n*f$w97L8Q*_!^6E_)Vl_eA-e~&(SD`e zgsHQK)PsY~kJ)!;b)(&D9h83mQ783?^GyO+KL!7eJYcrNIEx?;(9#4-snl3I+fG literal 0 HcmV?d00001 diff --git a/gfx/waves.ase b/gfx/waves.ase new file mode 100644 index 0000000000000000000000000000000000000000..3a7a980de17a8360344dfc157982d9158caf87b6 GIT binary patch literal 3029 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 literal 0 HcmV?d00001 diff --git a/sfx/hit.wav b/sfx/hit.wav new file mode 100644 index 0000000000000000000000000000000000000000..a9583995dc722279a3b936755804836660198453 GIT binary patch literal 25388 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%0Dgpe(#UG287q2XS zQ&?2kxX`Mwdw%zPzg*wk==h3wdNeKi8S%ayYN9L6HK_T@ej;|OLwql+75SOi8xMDf zxY>xLD6LEWoNSb|DsNnlN`+EQ!+H(-)%UEQQ}*2xeQL%r6cALG;?gsuo8a*B@M0-7&1GXL$ zO|?_(OXel>6!z~P6YoB{Fem(9H9~e)IrbnAZ8%I*9yovGc%ogSr)EC>K?y7g|TywuZ zDvlxkS5%#oP`eMZgYBl#X2||CAh&hAN8BYwB8mD%$3~~x0d`x=pY3M3Bho|Dv|KJP zM+|o^bu4|<@KM9OhIblTl{P9pRJy;^uY7EIv!reEMedt^QIG&o}IFHRj-S#ai z>t(23*O-+!w0hKWZEP=lu)PLZ<{kUCt%+)*eWM=HkZ5RhS#)VMI=U*lCb}AJbTlR! zizvA@x;MHfdH}yCL^npKM5jegE>4o)!-BP+$GzwhU#f#*dRyED^a>9S-= za%XaDG9nqCj8AS%{wMh+8I+!!Hgh%b#9M9*^2!e2<&$U;@?>Z1S$jcZd%LAw1J2I^ zAFno7;e>GlP9%pQryYix@OWg>(~`?mw zY}+RKOLT5@Vf0w^O!RK_Ni;9|FnTbW9QBS4i{8ZZjqE0NKjh>-@WWttvb#LJJe`U7 z{BzPSnS(v}p7I0b8ReJC4oHtkE=XLGBooqG()QR3pF-Wbt=}Fw@+SWkn&jrz*xNV6 z2{gtje+ACyIN>1+%mAyd$2s6coC0>hp9nbA{EOVj?o*upzD6!hTxY)+S7 zhZFZ#sOL|$r`a{O4DL;cHbtD>8Q&Av$7#HKu3N5G?(p2sx!rQhRbdSBm?uMP5i)a?e#*IerRqg`x4|Qj_{_aF~B-%mlV7IsHjyk@p>*RKE zJG&iSXSWmXySM{fPj`|VjQL}6+Ibn#Uhlf#oO}*eo`TchhiFR?`SQL*-iU09^YgYi zHT1yg@D*QBMp*j862o;6(kFZvoHr z2g{!^&zfeo#-3sa+G%#WU4{C%bF@3+@{H()Xks)qdJ2{9OytMu(Imv<1=tIAi@HRM z>`!*29fJtk(B1&en&adrd$w~W?Bo}s60J|iqWwADDxH_SpNvI23H9S)$x+BK15qW7 zNnTCfO18j=iOBmKx#sRpRIKgrcH}<16KIKd2j`f-HP$OXnO{tTXw9LrNlY0C{9@iR z^KfpTV#c5iK_p7;aUfbZ#Fg-FZ?psP(+l41i8$_QdZQBRg_CWs#+dGd-~CV(4MvS~ zCZhY&#`g&~n7bO!e2<$KkO$tx8uAY4Ycn67U4*wRzo0E`T>m@lYhU5r%iDPSF%9ib zyiXVf8FIFhcR72SJ;1FFIFU=`y$OD|YP{YYd3Z~_6Wbckwm?>Hjov)CxD32}7q#TQ z$k4;U*ger=oZa6?ZFz^g$DNKC=;8KvTe+>>dZ_J-uHZIwE!_@oJJ;78g|`S}Q6tTD zHNMdJmg_#``Jch|&N$PKM3a4X4XVlQ!Iop}@%B=*+w4SCfDhY;>?8JGRD;*s8|<0b zHxEJE9BnCVeGcc|OU;#yDAs~kU*Lp373T@5sr#e#z&WC`-xY65b^zNuBEI(l*ZN|o zyAUVChwv8Y3%rYLj(0Q%<1O1&koF>QTxR}cH?{kq*6iE(He(>Td?Ma39Eu#W6Dp;g z{Q|fPrhRZ`sR6fM@w2g0p4+&Oe}g=Bm%G~ya3{d}t=(F1?Su5obUI>dO8P`PIej#J zDt$VACw((rlKv~*6gAK>?r6-p18-S=YRm~YA`X55Lh|nDF0Au2wCarfH3Ife10DRJyzLFn_BBVt z`o5^#`vAX#%x-2E)H7|c%R0>Y9<|=Xe<4nu|^NH)6lL% zd`-n^V2*hO7Jh~*|3~cT%kU;)71*>6*DDbdKO+|AV;6i2Z;YNWH(*yh8|`rHogL7M zX!1_@Ein8sSS|1TuEaUvJnRR<8Z*=ocy=f%qT#@26sqxCv0FTbcWrOr4d5bF=nXhu bwlea5rVrW~kUa`|-3Gj;;tkCVu=M`{!XB>5 literal 0 HcmV?d00001 diff --git a/sfx/jump.wav b/sfx/jump.wav new file mode 100644 index 0000000000000000000000000000000000000000..8b0bed05633c5770af09485ae4e5e52b3dbf5ca2 GIT binary patch literal 25388 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 Qyh{3$S4qZPO}m@_1NrAm3IG5A literal 0 HcmV?d00001 diff --git a/sfx/kick.wav b/sfx/kick.wav new file mode 100644 index 0000000000000000000000000000000000000000..6da4c060a530044c4a2be88e89ffbcaba7616df2 GIT binary patch literal 4516 zcmW+(2Y6J)+MaUSo__YMgoucA1A+>n1dtj~xDZ4jbOGgtB7{V^5V#@`^a>J0P>P{n zz#zT%E`ktEkR~FbD3Hdc?Cv>dPuuhF{b!zc-f!AB&-2YZ@65c{w@;t-3IM};r1#F4 zHuDu300033sk;G?4Ib9_#GjO!ubc_5DeB>-X`1Wvro^>Cc=|MUN5c)N62LaF^ZZ!h+z^ieqC1?U(2T7nEcnx$0Z-O46BS;0^ zz(3;UV9*zg0PlivU?`Xpw`PE`U@n*p7J@9W1mu8~AQ!9!d0-1z1$KcA;3)VWTmXB) zuizNC2hM?K;2Nj_cYz6>0s@AB1U>K)D_xqSYuD z6`)n<6xxak(E)T1T|iIJ4OERDAsy8q2YCo%Hl7nOkDJE%4J_i0Sin6ojni-p4Z~qH z23MjFaS57)i_u5;GRnkf<2a16Z~@B3-^J}^@xBFU9G;2N@rP(2PD6cgXVeKdMXzB6 zy@+kd;J;uPU4xI%L0E*=!c*u|Sb)aEZ&5Ee4>g1TL<}5&%0OFm1=K|az=Hn)CGbOV z7Ip)hU?NxuYrXOCs@ENE@#@1lo&g4VH$hWxA24H!L2+y>*cM9$b7MY`7Ax@@#14Da z?w8&fcdWP6P4-5*gxAu&8FQR1v0t3&v3#dnEW;6FZ#oZL$KK^$wP(BE*nQm5w(PdB z?>bRyvvbLs2_o-3d<#?FzRK z<%V%+c(|yxZFqex7oJvI8tPJeEyUFB4&A9)71~_$X(+R1a;RI)h>%$GPUvBE=g^+& zcASZw7~luLr*fmjsW6gTbd^JNQB*q3WGTa@E|($g2I3`Bl#%d#W00kE+IMa`i^7 zWA!6#Om$=Zi|Q%*k?Mo`(`r`_)buyrsmV8TYs!o>HJzh&&9Z2x+9%O}*LE?_*M4g< zp=$HJP+x09sK6=@v3AFBhP^y|$$k=U?xaMPIIAL+PIY9UJ3u?)?$qkVSUop3O)rmK z*9Up+jFVoz(FCCAaxf`sfm_iJU{A9U?lwE21Zz9`!V<7$&B9aczwkXfjp*+jCC)ny z$+m7Dx!pC$gxCaXNvxQ1V;$(3-UhnTBbjkvDsvm$VEV(3zVmR4uMMKv9cVJ!02i{~ z;?^8Ztl&N&YPlL>ApaqGfWJ-h!at~q!ddEq(2Dkp>*#SJ$Ltm7G7+(q=_C#I&66(p z&PhqEE3IaeWynpHCvscmBJPIVh>yv0_*TkMeu!f7GnIbA*UEfhhjL1|pi~NXltl4w zrLS0}WQvBeOf;1}alR(jDv!mWQXxKyqgV-wrVF7g?pRp#hL6Ab{Ct=j$-SxEPKp%*f-EO#`lb&d^yY>Mqv6g z+vw+XJ9;Vo6V;p^M*T=VCC5Gs!J(iREb(rD54`c9ELP=Z$8tQ|McyKJWsGne$Cfz<-Gd- zD~vsQ!{~i|cvLjLjrKHdMCTZac_gkMwZ>}mjpze2E1F~-h-*wFdf7@b6YNFiB)ix= z5!WNp$+SLjE?Os?7wmvL$mC|{tddg1Ii#aqF)IbwLDqj_C`Gzo~+0)Efwu$d$Zn19x7xX>m2D07x zBkX#<9v9{3aYKds+##VeFNhoXiK5M)7t@6V>98ZS zZR;=hBY&a)y0Xo`L7C?trwsQuQC{;`$WdjFd`X!mFISq$>B=LiuCiMCUG68XlpV33 zyi3G#x_C@-g^AMlf-0p6XT@rMikQc%Vk`cbaGe__WN{X+aBKKOY#TnEE#wSe7Ps2> z3fIARioL~*VrRymCW*{e-x+$4Zye4082U7`GaltMstIF|ztdadksm}3p*^A@y^n}e z9};J%7l^e~5gti>f!k2MZ~}$!-{d89h|EI^$)RW{*%&n^P3RDX@FuYp?j`2KWyB~r zjpza2C0f8_LV@*(TEG#%0Sg}oWq3QdgTDdSa4xurXMj_95;%-M0Q>R#U>_b8=TY(c zSa2L?fO9xAJ}w9Rju*w}tpFzeFA#{Mpb=3BQi!Ku5CPy+;w8A0NP#=!YhEQ5!ct-f zP&O$4c}C1s&7BF+ow?#zRq+KyMUg|UZHoeGE>Tq zWSVhXnGEhZ^L;$lPq_uYB>rdLR9<0s^P|`x|2^AE2(upv-MKx&7hH9Guig;r@!8@8 z{-C&zuMthYwUjDkN=t=(QlU^Iv0`KSZSfs>q4xrJ=Xij0-0Bq)DKb(B+5 zg0e<>QOTBGQhLX^uB0kuaqT`NI&r-Y%Du&VGAmw^i-cYBa$%)BK$s=d!U*{+-$l;m z>&cDzh;*B~Dt*rVS8C18mhQ5Bq+C{&nzMh2g}&Y5bl<;3#n)Zj&mb|4`BA8(bA|uV z-Gx`^7=M&H#rLB=;UAFg`I%%n=OZ_8>xdy-OG4mI;tOm)Jd?eLUS%`TJzofZ>01a> zd=e~U)`2xlbMQ8E#M9{>-VXX&Y!p2>#?TMl6(}2GRhJI2N=k=Wg{7md6Qz@^O{KG}Z%RM2a!bFozA9aBttvfWEh)Wd zEi1ig<(K|#9Vzv!OQi|+-O|_Xn$k3zFPm<+DqCTvmmRR@m))`tltt~*vIb7w@_x>H z<#U~{%71W*%Wpdg6_opaMY6lUVzP@Xx445UZ@F75)mW%9E!OM#;@IZrS7O$47@K;P;`;9&J-(4fW#zpm*I-I}#*j;*d6Q6w|P@#KDSD_JYvBil$XQrXgA zYQHp}dM@ptlH>=}6q%>D$t~!|au>R;GJx)@jHeeWIrLs-CH+V#pauUCy1oA_J=uSm z{@VX5ecXSYzV9!hJ^xj@k$Rr)rXHlns+;I}>QZ{GnnUkX$I^vr5BiDPn2xB3CIcl@ z-M~?*Wgw606nLNNA81Vt3&hCLfkHAP@GUttFov8KXi8=V%8BWLUBt(M$wW?|F)=&v z0M7{I5x5Jx2A0Csfljbtpd847HNaE*fLgWIyRUBa zuBrXKqiQ6!SzRB?Q&VHJ)GBwZn(y{hd%I25pu?-*IM4lWIYs_w_96drdzC-M&hh_k zrTg=&j{bI59sff!q%1Xyl@8`km*E&dTHCcL5s}yS^m&89J8^!C9+2WDN zKyh27nYb?E3ExHT3)>>cgsqX~!urT0VQr*m94`y&B2j)*`Hwidrt4e*62y>%Z9}EHzK}y#-F|e#yQ_pW4kXgy3{u)I^DN8I^1_Y+TCYHU-h*y z72jmjWVV|n%v19M)7aX{jJ8%WYpey#O=~jaw+At4b{A%u-Guqo78usC=~U+#z0moM zzTh0EfxDIN>gLg(xHIX~?pWG#`_k>>|A(wt6Z(gkL_dp}R1@znYMl2A^__Q;y6zQF z3|L8Z1M{g_U<$Pd456Nax2XEC88sBj)M99pCtxL63V$aP(Rs2T+D+!5d~!QlMBYJ} zB*J6JW_SQO0H=^MaU*g)=E+OgB%a}C1Wnu`l86gLKVl~_jaWr2CO#)N5i^OC#8~1E z(VwU$IuJD3hHd~-c0tx)#OOL Pk8FZnG6nxclKB4sckz^r literal 0 HcmV?d00001 diff --git a/sfx/punch.wav b/sfx/punch.wav new file mode 100644 index 0000000000000000000000000000000000000000..4f8bdd7277f22819d3ac7ac06f79ae3ad34735e7 GIT binary patch literal 2456 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 KkoYLTk^ckTB5^YS literal 0 HcmV?d00001 diff --git a/sfx/whoosh.wav b/sfx/whoosh.wav new file mode 100644 index 0000000000000000000000000000000000000000..3517aa9fe10b7abcf3dbe90685ca079310f716c5 GIT binary patch literal 7616 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 z

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 literal 0 HcmV?d00001 diff --git a/sfx/wiff.wav b/sfx/wiff.wav new file mode 100644 index 0000000000000000000000000000000000000000..de942a04f69f99fac1ce4bc27ae455f12c7aa859 GIT binary patch literal 7616 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 literal 0 HcmV?d00001 diff --git a/shaders/sprite_fs.glsl b/shaders/sprite_fs.glsl new file mode 100644 index 0000000..dc07dbc --- /dev/null +++ b/shaders/sprite_fs.glsl @@ -0,0 +1,9 @@ +#version 120 +// uniforms +uniform sampler2D texture_sampler; +// input +varying vec2 frag_uv; + +void main() { + gl_FragColor = texture2D(texture_sampler, frag_uv); +} diff --git a/shaders/sprite_vs.glsl b/shaders/sprite_vs.glsl new file mode 100644 index 0000000..1fb868d --- /dev/null +++ b/shaders/sprite_vs.glsl @@ -0,0 +1,15 @@ +#version 120 +// input +uniform vec2 resolution; // display resolution +attribute vec3 vp; // vertex position +uniform vec2 vp_offset; // vertex offset (draw position) +attribute vec2 uv; // UV map +uniform vec2 uv_offset; // offset within the UV map +// output +varying vec2 frag_uv; + +void main() { + vec2 pos = vp.xy + vp_offset; + gl_Position = vec4(pos/resolution, 1.0, 1.0); + frag_uv = vec2(uv.s+uv_offset.x, 1.0 - uv.t+uv_offset.y); +} diff --git a/shaders/world_fs.glsl b/shaders/world_fs.glsl new file mode 100644 index 0000000..9c8d84e --- /dev/null +++ b/shaders/world_fs.glsl @@ -0,0 +1,23 @@ +#version 120 +varying vec2 frag; +// input +uniform sampler2D texture_sampler; +uniform vec2 seed; +uniform int iteration; + +void main() { + vec2 z = frag; + int i; + for (i = 0; i < iteration; i++) { + float x = (z.x * z.x - z.y * z.y) + seed.x; + float y = (z.y * z.x + z.x * z.y) + seed.y; + if ((x * x + y * y) > 4.0) break; + z.x = x; + z.y = y; + } + if (i == iteration) { + discard; + } else { + gl_FragColor = texture2D(texture_sampler, vec2(float(i)) / 100.0, 0.25); + } +} diff --git a/shaders/world_vs.glsl b/shaders/world_vs.glsl new file mode 100644 index 0000000..9acf958 --- /dev/null +++ b/shaders/world_vs.glsl @@ -0,0 +1,12 @@ +#version 120 +// input +attribute vec2 vp; +uniform vec2 vp_offset; +uniform vec2 size; +// output +varying vec2 frag; + +void main() { + gl_Position = vec4(vp+vp_offset, 0.5, 1.0); + frag = vp/size; +} diff --git a/src/Enemy.cpp b/src/Enemy.cpp new file mode 100644 index 0000000..ece9ce3 --- /dev/null +++ b/src/Enemy.cpp @@ -0,0 +1,127 @@ +#include "Enemy.hpp" +#include "g_common.hpp" +#include "a_common.hpp" +#include + +Enemy::Enemy() { + target = NULL; + type = ENTITY_ENEMY; + flags = ENTITY_LEFT; + max_force = 3.0f; + speed = 1.0f; + damage = 1; +} +Enemy::~Enemy() { + +} +void Enemy::doThink(int delta) { + if (target == NULL) { + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + if ((*entity_it)->type == ENTITY_JULIET) { + target = (*entity_it); + break; + } + } + } else { + if (flags & ENTITY_DIE) { + if (sprite.frame >= 10) { + flags |= ENTITY_DESTROY; + } + } else if (flags & ENTITY_HURT) { + if (!(flags & ENTITY_HAS_HURT)) { + sprite.frame = 0; + sprite.setAnimation(2); + flags |= ENTITY_HAS_HURT; + } else { + if (sprite.frame >= 6) { + sprite.frame = 0; + sprite.setAnimation(0); + flags &= ~ENTITY_HAS_HURT; + flags &= ~ENTITY_HURT; + } + } + } else if (flags & ENTITY_ATTACK) { + if (sprite.frame == 6) { // frame 6 is hit + if (!(flags & ENTITY_HAS_HIT)) { + float diff_x = target->x - x; + float diff_y = target->y - y; + if (diff_y > -48 && diff_y < 48) { + if (diff_x > 0 && diff_x < 32) { + Mix_PlayChannel(-1, a_sfx[SFX_HIT], 0); + target->takeDamage(damage); + flags |= ENTITY_HAS_HIT; + target->addForce((float)damage*3, (float)damage*3); + } else if (diff_x <= 0 && diff_x > -32) { + Mix_PlayChannel(-1, a_sfx[SFX_HIT], 0); + target->takeDamage(damage); + flags |= ENTITY_HAS_HIT; + target->addForce(-(float)damage*3, (float)damage*3); + } + } + } + } else if (sprite.frame >= 10) { + sprite.setAnimation(0); + sprite.frame = 0; + flags &= ~ENTITY_ATTACK; + flags &= ~ENTITY_HAS_HIT; + frame_time = 100; + } + } else { + float diff_x = target->x - x; + float diff_y = target->y - y; + if (diff_x > -48 && diff_x < 48 && diff_y > -48 && diff_y < 48) { + sprite.setAnimation(1); + sprite.frame = 0; + frame_time = 75; + flags |= ENTITY_ATTACK; + } + if (target->x > x) { + if (!(flags & ENTITY_RIGHT)) { + flags |= ENTITY_RIGHT; + flags &= ~ENTITY_LEFT; + sprite.flipSprite(); + } + if (vel_x > -max_force && vel_x < max_force) { + if (sprite.frame == 4 || sprite.frame == 9) { + addForce(speed, 0); + } else { + addForce(speed/2.0f, 0); + } + flags |= ENTITY_WALK; + } + } else if (target->x < x) { + if (!(flags & ENTITY_LEFT)) { + flags |= ENTITY_LEFT; + flags &= ~ENTITY_RIGHT; + sprite.flipSprite(); + } + if (vel_x > -max_force && vel_x < max_force) { + if (sprite.frame == 4 || sprite.frame == 9) { + addForce(-speed, 0); + } else { + addForce(-(speed/2.0f), 0); + } + flags |= ENTITY_WALK; + } + } + if (target->y > y+32) { + if (!(flags & ENTITY_FALLING)) { + flags |= ENTITY_JUMP; + addForce(0, 2); + } + } + } + } + frame_elapsed += delta; + if (frame_elapsed >= frame_time) { + frame_elapsed = 0; + sprite.loopFrame(); + } + if (health <= 0) { + sprite.frame = 0; + sprite.setAnimation(3); + flags |= ENTITY_DIE; + health = 100; // cheat + } +} diff --git a/src/Enemy.hpp b/src/Enemy.hpp new file mode 100644 index 0000000..9db3b20 --- /dev/null +++ b/src/Enemy.hpp @@ -0,0 +1,14 @@ +#ifndef ENEMY_HPP +#define ENEMY_HPP +#include "Entity.hpp" + +class Enemy : public Entity { + public: + Enemy(); + ~Enemy(); + void doThink(int delta); + private: + Entity *target; +}; + +#endif diff --git a/src/Entity.cpp b/src/Entity.cpp new file mode 100644 index 0000000..ec8aa7c --- /dev/null +++ b/src/Entity.cpp @@ -0,0 +1,57 @@ +#include "Entity.hpp" +#include + +Entity::Entity() { + frame_time = 100; + frame_elapsed = 0; + x = y = 0; + vel_x = vel_y = 0.0f; + max_force = 5.0f; + col_box = NULL; + type = ENTITY_NONE; + flags = 0; + speed = 5.0f; + health = 10.0f; + damage = 0; +} +Entity::~Entity() { + if (col_box != NULL) free(col_box); +} +void Entity::setSprite(Sprite new_sprite) { + sprite = new_sprite; + if (col_box != NULL) free(col_box); + col_box = (float*)malloc(4*sprite.width*sprite.height*sizeof(float)); +} +void Entity::addForce(float x_, float y_) { + //if (vel_x + x_ <= max_force) { + vel_x += x_; + //} else { + //vel_x = max_force; + //} +// if (vel_y + y_ <= max_force) { + vel_y += y_; +// } else { +// vel_y = max_force; +// } +} +void Entity::setPosition(float x_, float y_) { + x = x_; + y = y_; +} +void Entity::doThink(int delta) { +} +float Entity::checkCol(int x, int y, int component) { + if (x < 0 || y < 0) return 0.0f; + int pos = y*sprite.height+x+component; + return(col_box[pos]); +} +void Entity::takeDamage(int damage) { + health -= damage; + flags |= ENTITY_HURT; + sprite.frame = 0; +} + +bool destroyEntity(Entity *entity) { + if (entity->flags & ENTITY_DESTROY) return true; + return false; +} diff --git a/src/Entity.hpp b/src/Entity.hpp new file mode 100644 index 0000000..89f827d --- /dev/null +++ b/src/Entity.hpp @@ -0,0 +1,54 @@ +#ifndef ENTITY_HPP +#define ENTITY_HPP +#include "Sprite.hpp" + +#define ENTITY_NONE 0 +#define ENTITY_JULIET 1 +#define ENTITY_ENEMY 2 + +#define ENTITY_NONE 0 +#define ENTITY_STAND 1 +#define ENTITY_WALK 2 +#define ENTITY_LEFT 4 +#define ENTITY_RIGHT 8 +#define ENTITY_ATTACK 16 +#define ENTITY_JUMP 32 +#define ENTITY_ATTACK2 64 +#define ENTITY_FALLING 128 +#define ENTITY_HAS_HIT 256 +#define ENTITY_HURT 512 +#define ENTITY_HAS_HURT 1024 +#define ENTITY_CAN_JUMP 2048 +#define ENTITY_DESTROY 4096 +#define ENTITY_DIE 8192 + +class Entity { + protected: + public: + int frame_time; + int frame_elapsed; + Sprite sprite; + Entity(); + ~Entity(); + int type; + int flags; + void setSprite(Sprite new_sprite); + virtual void doThink(int delta); + void setPosition(float x, float y); + void addForce(float x_, float y_); + float checkCol(int x, int y, int component); + virtual void takeDamage(int damage); + float vel_x; + float vel_y; + float x; + float y; + float pos[18]; // okay, this is what we get for not using matrices. + float *col_box; + float max_force; // self-applied force only applies up to this + // + float speed; + float health; + int damage; +}; +bool destroyEntity(Entity *entity); +#endif diff --git a/src/Juliet.cpp b/src/Juliet.cpp new file mode 100644 index 0000000..ea0dbe0 --- /dev/null +++ b/src/Juliet.cpp @@ -0,0 +1,275 @@ +#include "Juliet.hpp" +#include "Entity.hpp" +#include "g_common.hpp" +#include "a_common.hpp" +#include + +Juliet::Juliet() { + frame_time = 75; + state = 0; + flags = ENTITY_LEFT; + state = ENTITY_STAND; + max_force = 4.0f; + speed = 4.0f; + damage = 2; + type = ENTITY_JULIET; + health = 20.0f; +} +Juliet::~Juliet() { + +} + +void Juliet::setState(int new_state) { +} +void Juliet::removeState(int rem_state) { +} +void Juliet::doThink(int delta) { + frame_elapsed += delta; + if (frame_elapsed >= frame_time) { + frame_elapsed = 0; + sprite.loopFrame(); + } + if (state & JULIET_VICTORY) { + sprite.setAnimation(JULIET_ANIM_VICTORY); + return; + } + + if (flags & ENTITY_ATTACK) { + if (flags & ENTITY_ATTACK2) { + flags &= ~ENTITY_ATTACK2; + state &= ~JULIET_KICKING; + sprite.setAnimation(JULIET_ANIM_PUNCH); + sprite.frame = 0; + flags &= ~ENTITY_HAS_HIT; + frame_time = 75; + } + if (state & JULIET_PUNCHING) { + if (sprite.frame == 4) { + if (!(flags & ENTITY_HAS_HIT)) { + Mix_PlayChannel(-1, a_sfx[SFX_WIFF], 0); + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + Entity *entity = (*entity_it); + if (entity->type == ENTITY_ENEMY) { + float diff_x = entity->x - x; + float diff_y = entity->y - y; + if (diff_y > -48 && diff_y < 48) { + float force = 0; + if (diff_x > 0 && diff_x < 48) { + Mix_PlayChannel(-1, a_sfx[SFX_PUNCH], 0); + entity->addForce((float)damage*2, (float)damage*2); + entity->takeDamage(damage/2); + } else if (diff_x <= 0 && diff_x > -48) { + Mix_PlayChannel(-1, a_sfx[SFX_PUNCH], 0); + entity->addForce(-(float)damage*2, (float)damage*2); + entity->takeDamage(damage/2); + } + } + } + } + flags |= ENTITY_HAS_HIT; + } + } else if (sprite.frame == 5) { + flags &= ~ENTITY_HAS_HIT; + } else if (sprite.frame == 7) { + if (!(flags & ENTITY_HAS_HIT)) { + Mix_PlayChannel(-1, a_sfx[SFX_WIFF], 0); + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + Entity *entity = (*entity_it); + if (entity->type == ENTITY_ENEMY) { + float diff_x = entity->x - x; + float diff_y = entity->y - y; + if (diff_y > -48 && diff_y < 48) { + float force = 0; + if (diff_x > 0 && diff_x < 48) { + Mix_PlayChannel(-1, a_sfx[SFX_PUNCH], 0); + entity->addForce((float)damage*2, (float)damage*2); + entity->takeDamage(damage/2); + } else if (diff_x <= 0 && diff_x > -48) { + Mix_PlayChannel(-1, a_sfx[SFX_PUNCH], 0); + entity->addForce(-(float)damage*2, (float)damage*2); + entity->takeDamage(damage/2); + } + } + } + } + flags |= ENTITY_HAS_HIT; + } + } else if (sprite.frame >= 10) { + state &= ~JULIET_PUNCHING; + flags &= ~ENTITY_ATTACK; + flags &= ~ENTITY_HAS_HIT; + frame_time = 100; + } + } else { + state |= JULIET_PUNCHING; + sprite.setAnimation(JULIET_ANIM_PUNCH); + sprite.frame = 0; + frame_time = 75; + } + } + if (flags & ENTITY_ATTACK2) { + if (flags & ENTITY_ATTACK) { + flags &= ~ENTITY_ATTACK; + state &= ~JULIET_PUNCHING; + sprite.setAnimation(JULIET_ANIM_KICK); + sprite.frame = 0; + frame_time = 75; + flags &= ~ENTITY_HAS_HIT; + } + if (state & JULIET_KICKING) { + if (sprite.frame == 5) { + if (!(flags & ENTITY_HAS_HIT)) { + Mix_PlayChannel(-1, a_sfx[SFX_WHOOSH], 0); + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + Entity *entity = (*entity_it); + if (entity->type == ENTITY_ENEMY) { + float diff_x = entity->x - x; + float diff_y = entity->y - y; + if (diff_y > -48 && diff_y < 48) { + float force = 0; + if (diff_x > 0 && diff_x < 60) { + Mix_PlayChannel(-1, a_sfx[SFX_KICK], 0); + entity->addForce((float)damage*4, (float)damage*4); + entity->takeDamage(damage); + } else if (diff_x <= 0 && diff_x > -60) { + Mix_PlayChannel(-1, a_sfx[SFX_KICK], 0); + entity->addForce(-(float)damage*4, (float)damage*4); + entity->takeDamage(damage); + } + } + } + } + flags |= ENTITY_HAS_HIT; + } + } else if (sprite.frame >= 10) { + state &= ~JULIET_KICKING; + flags &= ~ENTITY_ATTACK2; + flags &= ~ENTITY_HAS_HIT; + frame_time = 100; + } + } else { + state |= JULIET_KICKING; + sprite.setAnimation(JULIET_ANIM_KICK); + sprite.frame = 0; + frame_time = 75; + } + } + + if ((flags & ENTITY_JUMP) && (flags & ENTITY_CAN_JUMP)) { + if (!(state & JULIET_PUNCHING) && !(state & JULIET_KICKING)) { + sprite.setAnimation(JULIET_ANIM_JUMP); + } + if (!(state & JULIET_HOLD_JUMP)) { + if (sprite.frame == 2) { + addForce(0, speed*3); + Mix_PlayChannel(-1, a_sfx[SFX_JUMP], 0); + if (state & JULIET_LEFT) { + addForce(-speed/2, 0); + } else if (state & JULIET_RIGHT) { + addForce(speed/2, 0); + } + flags &= ~ENTITY_CAN_JUMP; + flags &= ~ENTITY_JUMP; + flags |= ENTITY_FALLING; + } else if (sprite.frame >= 9) { + flags &= ~ENTITY_JUMP; + flags &= ~ENTITY_CAN_JUMP; + } + } + } else if (flags & ENTITY_FALLING) { + if (!(state & JULIET_PUNCHING) && !(state & JULIET_KICKING)) { + sprite.setAnimation(JULIET_ANIM_JUMP); + if (sprite.frame < 4) { + sprite.frame = 4; + } else if (sprite.frame > 7) { + sprite.frame = 4; + } + } + flags &= ~ENTITY_JUMP; + if (state & JULIET_LEFT) { + addForce(-speed/2, 0); + } else if (state & JULIET_RIGHT) { + addForce(speed/2, 0); + } + } else if (flags & ENTITY_HURT) { + flags &= ~ENTITY_JUMP; + flags &= ~ENTITY_HAS_HIT; + flags &= ~ENTITY_ATTACK; + flags &= ~ENTITY_ATTACK2; + state &= ~JULIET_PUNCHING; + state &= ~JULIET_KICKING; + sprite.setAnimation(JULIET_ANIM_HURT); + frame_time = 25; + if (sprite.frame >= 6) { + flags &= ~ENTITY_HURT; + sprite.frame = 0; + frame_time = 100; + } + } else if (flags & ENTITY_WALK) { + if (!(state & JULIET_PUNCHING) && !(state & JULIET_KICKING)) { + sprite.setAnimation(JULIET_ANIM_WALK); + } + if (flags & ENTITY_LEFT) { + if (vel_x > -max_force && vel_x < max_force) { + if (sprite.frame == 4 || sprite.frame == 9) { + addForce(-speed, 0); + } else { + addForce(-(speed/2), 0); + } + } + } else if (flags & ENTITY_RIGHT) { + if (vel_x > -max_force && vel_x < max_force) { + if (sprite.frame == 4 || sprite.frame == 9) { + addForce(speed, 0); + } else { + addForce((speed/2), 0); + } + } + } + } else { + if (!(state & JULIET_PUNCHING) && !(state & JULIET_KICKING)) { + sprite.setAnimation(JULIET_ANIM_STAND); + } + } + if (state & JULIET_JUMP && flags & ENTITY_CAN_JUMP) { + flags |= ENTITY_JUMP; + sprite.frame = 0; + if (!(state & JULIET_HOLD_JUMP)) { + state |= JULIET_HOLD_JUMP; + } + } else if (state & JULIET_HOLD_JUMP && (flags & ENTITY_CAN_JUMP)) { + state &= ~JULIET_HOLD_JUMP; + } + if (state & JULIET_LEFT) { + if (!(flags & ENTITY_LEFT)) { + sprite.flipSprite(); + flags |= ENTITY_LEFT; + flags &= ~ENTITY_RIGHT; + } + if (!(flags & ENTITY_FALLING)) { + flags |= ENTITY_WALK; + } + } else { + if (!(state & JULIET_RIGHT)) flags &= ~ENTITY_WALK; + } + if (state & JULIET_RIGHT) { + if (!(flags & ENTITY_RIGHT)) { + sprite.flipSprite(); + flags |= ENTITY_RIGHT; + flags &= ~ENTITY_LEFT; + } + if (!(flags & ENTITY_FALLING)) { + flags |= ENTITY_WALK; + } + } else { + if (!(state & JULIET_LEFT)) flags &= ~ENTITY_WALK; + } + if (state & JULIET_PUNCH) { + flags |= ENTITY_ATTACK; + } else if (state & JULIET_KICK) { + flags |= ENTITY_ATTACK2; + } +} diff --git a/src/Juliet.hpp b/src/Juliet.hpp new file mode 100644 index 0000000..8c76b5f --- /dev/null +++ b/src/Juliet.hpp @@ -0,0 +1,39 @@ +#ifndef JULIET_HPP +#define JULIET_HPP +#include "Entity.hpp" + +#define JULIET_STAND 0 +#define JULIET_LEFT 1 +#define JULIET_RIGHT 2 +#define JULIET_WALK 4 +#define JULIET_JUMP 8 +#define JULIET_PUNCH 16 +#define JULIET_KICK 32 +#define JULIET_BUSY 64 +#define JULIET_ACTIVE 128 +#define JULIET_FALLING 256 +#define JULIET_HOLD_JUMP 512 +#define JULIET_KICKING 1024 +#define JULIET_PUNCHING 2048 +#define JULIET_VICTORY 4096 + +#define JULIET_ANIM_STAND 0 +#define JULIET_ANIM_WALK 1 +#define JULIET_ANIM_VICTORY 2 +#define JULIET_ANIM_JUMP 3 +#define JULIET_ANIM_KICK 4 +#define JULIET_ANIM_PUNCH 5 +#define JULIET_ANIM_HURT 6 + +class Juliet : public Entity { + public: + Juliet(); + ~Juliet(); + void setState(int new_state); + void removeState(int rem_state); + void doThink(int delta); + int state; + private: +}; + +#endif diff --git a/src/Sprite.cpp b/src/Sprite.cpp new file mode 100644 index 0000000..a88eb71 --- /dev/null +++ b/src/Sprite.cpp @@ -0,0 +1,80 @@ +#include +#include "Sprite.hpp" + +Sprite::Sprite(unsigned int s_cols, unsigned int s_rows, unsigned int s_width, unsigned int s_height) { + cols = s_cols; + rows = s_rows; + width = s_width; + height = s_height; + frame = animation = 0; + x = y = 0; + offset[0] = offset[1] = texture = 0; + // create the sprite quad + float fwidth = s_width; + float fheight = s_height; + quad[0] = -fwidth; quad[1] = -fheight; quad[2] = 0.0f; + quad[3] = fwidth; quad[4] = -fheight; quad[5] = 0.0f; + quad[6] = -fwidth; quad[7] = fheight; quad[8] = 0.0f; + quad[9] = fwidth; quad[10] = fheight; quad[11] = 0.0f; + quad[12] = fwidth; quad[13] = -fheight; quad[14] = 0.0f; + quad[15] = -fwidth; quad[16] = fheight; quad[17] = 0.0f; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW); + // get our sprite texture UV out of spritesheet + sprite_w = fwidth/(float)(width*cols); + sprite_h = fheight/(float)(height*rows); + quad_uv[0] = 0.0; quad_uv[1] = 0.0; + quad_uv[2] = sprite_w; quad_uv[3] = 0.0; + quad_uv[4] = 0.0; quad_uv[5] = sprite_h; + quad_uv[6] = sprite_w; quad_uv[7] = sprite_h; + quad_uv[8] = sprite_w; quad_uv[9] = 0.0; + quad_uv[10] = 0.0; quad_uv[11] = sprite_h; + glGenBuffers(1, &uv); + glBindBuffer(GL_ARRAY_BUFFER, uv); + glBufferData(GL_ARRAY_BUFFER, sizeof(quad_uv), quad_uv, GL_STATIC_DRAW); +} +Sprite::~Sprite() { + +} +void Sprite::setFrame(unsigned int new_frame) { + if (new_frame > cols) { + new_frame = cols; + } else if (new_frame < 0) { + new_frame = 0; + } + frame = new_frame; + offset[0] = (float)frame * sprite_w; +} +void Sprite::incFrame() { + if (frame+1 < cols) frame++; + offset[0] = (float)frame * sprite_w; +} +void Sprite::decFrame() { + if ((int)frame-1 >= 0) frame--; + offset[0] = (float)frame * sprite_w; +} +void Sprite::loopFrame() { + if (frame+1 < cols) frame++; + else frame = 0; + offset[0] = (float)frame * sprite_w; +} +void Sprite::setAnimation(unsigned int new_animation) { + if (new_animation >= rows) { + new_animation = rows; + } else if ((int)new_animation <= 0) { + new_animation = 0; + } + animation = new_animation+1; + offset[1] = (float)animation * sprite_h; +} +void Sprite::flipSprite() { + quad[0] = -quad[0]; + quad[3] = -quad[3]; + quad[6] = -quad[6]; + quad[9] = -quad[9]; + quad[12] = -quad[12]; + quad[15] = -quad[15]; + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW); +} diff --git a/src/Sprite.hpp b/src/Sprite.hpp new file mode 100644 index 0000000..ceea894 --- /dev/null +++ b/src/Sprite.hpp @@ -0,0 +1,39 @@ +#ifndef SPRITE_HPP +#define SPRITE_HPP +#ifdef __APPLE__ +#include +#else +#include +#endif +class Sprite { + private: + unsigned int x; // selected col (x * width) + unsigned int y; // selected row (y * height) + float sprite_w; // sprite's width in UV terms + float sprite_h; // sprite's height in UV terms + // this is dumb, but let's have mock vec2s for GL + float quad[18]; // quad dimensions (width/max_width) + float quad_uv[12]; // uv map + public: + unsigned int width; // width of individual sprite + unsigned int height; // height of individual sprite + unsigned int rows; // rows in sheet + unsigned int cols; // cols in sheet + Sprite(unsigned int s_cols = 0, unsigned int s_rows = 0, unsigned int s_width= 0, unsigned int s_height = 0); + ~Sprite(); + void setFrame(unsigned int frame); + void setAnimation(unsigned int animation); + void incFrame(); + void decFrame(); + void loopFrame(); + void flipSprite(); + GLuint vbo; // vertex buffer object for quad + GLuint uv; + GLuint uv_offset; + GLuint texture; // texture + float offset[2]; // x and y + unsigned int frame; + unsigned int animation; +}; + +#endif diff --git a/src/World.cpp b/src/World.cpp new file mode 100644 index 0000000..42b7094 --- /dev/null +++ b/src/World.cpp @@ -0,0 +1,12 @@ +#include "World.hpp" + +World::World(int iter, float size_x_, float size_y_, float offset_x_, float offset_y_) { + iteration = iter; + size_x = size_x_; + size_y = size_y_; + offset_x = offset_x_; + offset_y = offset_y_; +} +World::~World() { + +} diff --git a/src/World.hpp b/src/World.hpp new file mode 100644 index 0000000..e4eb8a7 --- /dev/null +++ b/src/World.hpp @@ -0,0 +1,17 @@ +#ifndef WORLD_HPP +#define WORLD_HPP + +class World { + protected: + public: + World(int iter, float size_x_, float size_y_, float offset_x_, float offset_y_); + ~World(); + float seed; + int iteration; + float size_x; + float size_y; + float offset_x; + float offset_y; +}; + +#endif diff --git a/src/a_common.cpp b/src/a_common.cpp new file mode 100644 index 0000000..d7d7ffa --- /dev/null +++ b/src/a_common.cpp @@ -0,0 +1,16 @@ +#include "a_common.hpp" + +int a_rate = 22050; +Uint16 a_format = AUDIO_S16SYS; +int a_channels = 2; +int a_buffers = 4096; + +Mix_Chunk *a_sfx[SFX_COUNT] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + diff --git a/src/a_common.hpp b/src/a_common.hpp new file mode 100644 index 0000000..904ed84 --- /dev/null +++ b/src/a_common.hpp @@ -0,0 +1,19 @@ +#ifndef A_COMMON_HPP +#define A_COMMON_HPP +#include "SDL_mixer.h" + +extern int a_rate; +extern Uint16 a_format; +extern int a_channels; +extern int a_buffers; + +#define SFX_KICK 0 +#define SFX_PUNCH 1 +#define SFX_WHOOSH 2 +#define SFX_JUMP 3 +#define SFX_WIFF 4 +#define SFX_HIT 5 +#define SFX_COUNT 6 +extern Mix_Chunk *a_sfx[SFX_COUNT]; + +#endif diff --git a/src/g_common.cpp b/src/g_common.cpp new file mode 100644 index 0000000..b56017a --- /dev/null +++ b/src/g_common.cpp @@ -0,0 +1,20 @@ +#include "g_common.hpp" + +int g_running = 0; +char g_keystate[256]; + +int g_wave = 0; +int g_waves[8] = { + 5, + 10, + 20, + 25, + 30, + 35, + 40, + 45 +}; + +int g_victory = 0; + +std::vector g_entities; diff --git a/src/g_common.hpp b/src/g_common.hpp new file mode 100644 index 0000000..a348e15 --- /dev/null +++ b/src/g_common.hpp @@ -0,0 +1,20 @@ +#ifndef G_COMMON_HPP +#define G_COMMON_HPP +#include +#include "Entity.hpp" +extern int g_running; +extern char g_keystate[256]; + +extern int g_wave; +extern int g_waves[8]; + +extern int g_victory; + +extern std::vector g_entities; + +#define DRAG 0.25 +#define GRAVITY 0.5 +#define MAX_FALL 10 +#define MAX_VELOCITY 4 + +#endif diff --git a/src/game.cpp b/src/game.cpp new file mode 100644 index 0000000..87f9d37 --- /dev/null +++ b/src/game.cpp @@ -0,0 +1,342 @@ +#ifdef _WIN32 +#include +#else +#include +#endif +#include "game.hpp" +#include +#include +#include "g_common.hpp" +#include "v_common.hpp" +#include "World.hpp" +#include "Sprite.hpp" +#include "Entity.hpp" +#include "Juliet.hpp" +#include "Enemy.hpp" + +int runGame() { + g_running = 1; + std::vector world; + world.push_back(new World(64, 0.40, 0.40, 0, 0)); + world.push_back(new World(16, 1.20, 1.00, 0, 0)); + Juliet juliet; + juliet.setSprite(Sprite(11, 7, 32, 32)); + juliet.sprite.texture = v_texture_juliet; + + g_entities.push_back(&juliet); + + Sprite waves(1, 8, 128, 32); + waves.texture = v_texture_waves; + waves.setAnimation(0); + + Sprite health(2, 2, 16, 16); + health.texture = v_texture_health; + health.setAnimation(0); + + Sprite screens(1, 3, 128, 128); + screens.texture = v_texture_screens; + screens.setAnimation(0); + + float max_x = (float)(v_width-16); + float min_x = -(float)(v_width-16); + float max_y = -(float)(v_height-32); + float min_y = (float)(v_height+16); + + unsigned int last_time = SDL_GetTicks(); + unsigned int current_time = last_time; + unsigned int delta_time = 0; + unsigned int accumulator = 0; + + unsigned int last_wave = SDL_GetTicks(); + unsigned int next_wave = SDL_GetTicks(); + int spawn_timer = 0; + int spawn_count = 0; + + while(g_running) { + last_time = current_time; + current_time = SDL_GetTicks(); + delta_time = current_time - last_time; + accumulator += delta_time; + // event handling + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) { + g_keystate[event.key.keysym.scancode] = event.key.state; + } else if (event.type == SDL_QUIT) { + g_running = 0; + } else if (event.type == SDL_WINDOWEVENT) { + if (event.window.event == SDL_WINDOWEVENT_CLOSE) { + g_running = 0; + } + } + } + // world processing + while (accumulator >= 16) { + if (juliet.health <= 0) { + juliet.flags |= ENTITY_DESTROY; + std::vector::iterator entity_it, entity_end; + entity_it = std::remove_if(g_entities.begin(), g_entities.end(), destroyEntity); + g_entities.erase(entity_it, g_entities.end()); + + screens.setAnimation(1); + if (g_keystate[SDL_SCANCODE_SPACE] == SDL_PRESSED) { + g_wave = 0; + waves.setAnimation(0); + spawn_timer = 0; + spawn_count = 0; + g_entities.clear(); + juliet = Juliet(); + juliet.setSprite(Sprite(11, 7, 32, 32)); + juliet.sprite.texture = v_texture_juliet; + g_entities.push_back(&juliet); + screens.setAnimation(0); + } + } else if (g_victory == 1) { + screens.setAnimation(2); + juliet.state = JULIET_VICTORY; + juliet.frame_elapsed += 16; + juliet.frame_time = 200; + if (juliet.frame_elapsed >= juliet.frame_time) { + juliet.frame_elapsed = 0; + juliet.sprite.loopFrame(); + } + if (g_keystate[SDL_SCANCODE_SPACE] == SDL_PRESSED) { + g_victory = 0; + g_wave = 0; + waves.setAnimation(0); + spawn_timer = 0; + spawn_count = 0; + g_entities.clear(); + juliet = Juliet(); + juliet.setSprite(Sprite(11, 7, 32, 32)); + juliet.sprite.texture = v_texture_juliet; + g_entities.push_back(&juliet); + screens.setAnimation(0); + } + } else { + if (g_keystate[SDL_SCANCODE_SPACE] == SDL_PRESSED) { + juliet.state |= JULIET_JUMP; + } else { + juliet.state &= ~JULIET_JUMP; + } + if (g_keystate[SDL_SCANCODE_Z] == SDL_PRESSED) { + juliet.state |= JULIET_KICK; + } else { + juliet.state &= ~JULIET_KICK; + } + if (g_keystate[SDL_SCANCODE_X] == SDL_PRESSED) { + juliet.state |= JULIET_PUNCH; + } else { + juliet.state &= ~JULIET_PUNCH; + } + if (g_keystate[SDL_SCANCODE_LEFT] == SDL_PRESSED) { + juliet.state |= JULIET_LEFT; + } else { + juliet.state &= ~JULIET_LEFT; + } + if (g_keystate[SDL_SCANCODE_RIGHT] == SDL_PRESSED) { + juliet.state |= JULIET_RIGHT; + } else { + juliet.state &= ~JULIET_RIGHT; + } + + spawn_timer += 16; + if (spawn_timer >= 4000) { + if (spawn_count < g_waves[g_wave]) { + spawn_timer = 0; + Enemy *new_enemy = new Enemy(); + new_enemy->setSprite(Sprite(11, 4, 32, 32)); + new_enemy->sprite.texture = v_texture_doubt; + new_enemy->sprite.setAnimation(1); + g_entities.push_back(new_enemy); + spawn_count++; + } else { + if (g_wave+1 >= 8) { + if (g_entities.size() == 1) { + g_victory = 1; + } + } else { + spawn_timer = 0; + spawn_count = 0; + g_wave++; + waves.setAnimation(g_wave); + } + } + } + } + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + Entity *entity = *entity_it; + float l = (entity->checkCol(12, 4, 0)+entity->checkCol(12, 28, 0))/2.0f; + float r = (entity->checkCol(20, 4, 0)+entity->checkCol(20, 28, 0))/2.0f; + float t = (entity->checkCol(12, 28, 0)+entity->checkCol(20, 28, 0))/2.0f; + float b = (entity->checkCol(12, 4, 0)+entity->checkCol(20, 4, 0))/2.0f; + if (l > r) entity->vel_x += r*2; + else if (r > l) entity->vel_x -= r*2; + if (b > 0.00) { + entity->vel_y += b; + } + if (b <= 0.03) { + entity->vel_y -= GRAVITY; + if (b > 0.0 && b < 0.01) { + entity->flags |= ENTITY_FALLING; + entity->flags &= ~ENTITY_CAN_JUMP; + } else { + entity->flags &= ~ENTITY_FALLING; + } + } else if (b > 0.05 && b <= 0.1) { + if (entity->vel_y < 0.0f) { + entity->vel_y /= 2; + } + entity->flags &= ~ENTITY_FALLING; + } else { + if (entity->vel_y < 0.05) entity->vel_y = 0; + entity->flags &= ~ENTITY_FALLING; + entity->flags |= ENTITY_CAN_JUMP; + } + if (entity->vel_y < 0) { + entity->vel_y = (entity->vel_y < -MAX_FALL ? -MAX_FALL : entity->vel_y); + } + if (entity->vel_x > 0) { + entity->vel_x = (entity->vel_x > MAX_VELOCITY ? MAX_VELOCITY : entity->vel_x-DRAG); + } else if (entity->vel_x < 0) { + entity->vel_x = (entity->vel_x < -MAX_VELOCITY ? -MAX_VELOCITY : entity->vel_x+DRAG); + } else { + entity->vel_x = 0; + } + float x = entity->x; + float y = entity->y; + //printf("%fvs%f %fvs%f\n", entity->y+entity->vel_y, min_y, entity->y+entity->vel_y, max_y); + if (entity->y+entity->vel_y >= max_y && entity->y+entity->vel_y <= min_y) { + y = entity->y + entity->vel_y; + } + if (entity->x+entity->vel_x >= min_x && entity->x+entity->vel_x <= max_x) { + x = entity->x + entity->vel_x; + } + entity->setPosition(x, y); + + entity->doThink(16); + } + entity_it = std::remove_if(g_entities.begin(), g_entities.end(), destroyEntity); + g_entities.erase(entity_it, g_entities.end()); + accumulator -= 16; + } + // 1. drawing + glViewport(0, 0, v_width, v_height); + glUniform2f(v_unif_sprite_resolution, (float)v_width, (float)v_height); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // 1.1. draw world + glUseProgram(v_program_world); + glEnableVertexAttribArray(v_attr_world_vp); + // send quad + glBindBuffer(GL_ARRAY_BUFFER, v_quad_vbo); + glVertexAttribPointer(v_attr_world_vp, 3, GL_FLOAT, GL_FALSE, 0, NULL); + std::vector::iterator world_it, world_end; + for (world_it = world.begin(), world_end = world.end(); world_it != world_end; ++world_it) { + World *werld = *world_it; + // send seed + float t = (float)SDL_GetTicks() / 10000.0f; + float seed_x = (sin(cos(t/10)*10) + cos(t*2.0)/4.0+sin(t*3.0)/6.0)*0.25; + float seed_y = (cos(sin(t/10)*10) + sin(t*2.0)/4.0+cos(t*3.0)/6.0)*0.25; + int iteration = werld->iteration; + glUniform2f(v_unif_world_seed, seed_x, seed_y); + glUniform2f(v_unif_world_size, werld->size_x, werld->size_y); + glUniform2f(v_unif_world_vp_offset, werld->offset_x, werld->offset_y); + // send iteration + glUniform1i(v_unif_world_iteration, iteration); + // send texture palette + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, v_texture_world); + glUniform1i(v_unif_world_texture, 0); + // draw quad + glDrawArrays(GL_TRIANGLES, 0, 6); + } + glDisableVertexAttribArray(v_attr_world_vp); + // okay, this is weird, but for our entities we grab sections of the world render for pixel-based collision detection on next iteration + std::vector::iterator entity_it, entity_end; + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + Entity *entity = *entity_it; + int p_x = (int)((entity->x+((float)v_width))/2.0f); + int p_y = (int)((entity->y+((float)v_height)-(float)entity->sprite.height)/2.0f); + glReadPixels(p_x, p_y, (int)entity->sprite.width, (int)entity->sprite.height, GL_RGBA, GL_FLOAT, entity->col_box); + } + // 1.2. draw sprites + glUseProgram(v_program_sprite); + glEnableVertexAttribArray(v_attr_sprite_vp); + glEnableVertexAttribArray(v_attr_sprite_uv); + for (entity_it = g_entities.begin(), entity_end = g_entities.end(); entity_it != entity_end; ++entity_it) { + //send mesh + glBindBuffer(GL_ARRAY_BUFFER, (*entity_it)->sprite.vbo); + glVertexAttribPointer(v_attr_sprite_vp, 3, GL_FLOAT, GL_FALSE, 0, NULL); + // send uvs + glBindBuffer(GL_ARRAY_BUFFER, (*entity_it)->sprite.uv); + glVertexAttribPointer(v_attr_sprite_uv, 2, GL_FLOAT, GL_FALSE, 0, NULL); + // send uv offset + glUniform2f(v_unif_sprite_uv_offset, (*entity_it)->sprite.offset[0], (*entity_it)->sprite.offset[1]); + // set draw offset + glUniform2f(v_unif_sprite_vp_offset, (float)(int)(*entity_it)->x, (float)(int)(*entity_it)->y); + // bind texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, (*entity_it)->sprite.texture); + glUniform1i(v_unif_sprite_texture, 0); + // draw quad + glDrawArrays(GL_TRIANGLES, 0, 6); + } + // 1.3. draw waves UI + glBindBuffer(GL_ARRAY_BUFFER, waves.vbo); + glVertexAttribPointer(v_attr_sprite_vp, 3, GL_FLOAT, GL_FALSE, 0, NULL); + // send uvs + glBindBuffer(GL_ARRAY_BUFFER, waves.uv); + glVertexAttribPointer(v_attr_sprite_uv, 2, GL_FLOAT, GL_FALSE, 0, NULL); + // send uv offset + glUniform2f(v_unif_sprite_uv_offset, waves.offset[0], waves.offset[1]); + // set draw offset + glUniform2f(v_unif_sprite_vp_offset, 640-128, 480-32); + // bind texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, waves.texture); + glUniform1i(v_unif_sprite_texture, 0); + // draw quad + glDrawArrays(GL_TRIANGLES, 0, 6); + // 1.4. draw player health + glBindBuffer(GL_ARRAY_BUFFER, health.vbo); + glVertexAttribPointer(v_attr_sprite_vp, 3, GL_FLOAT, GL_FALSE, 0, NULL); + // send uvs + glBindBuffer(GL_ARRAY_BUFFER, health.uv); + glVertexAttribPointer(v_attr_sprite_uv, 2, GL_FLOAT, GL_FALSE, 0, NULL); + // send uv offset + glUniform2f(v_unif_sprite_uv_offset, health.offset[0], health.offset[1]); + // bind texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, health.texture); + glUniform1i(v_unif_sprite_texture, 0); + // draw quad + float i = 0; + for (i = 0; i < juliet.health; i++) { + // set draw offset + glUniform2f(v_unif_sprite_vp_offset, -((juliet.health*32)/2)+(i*32), (-480+20)); + glDrawArrays(GL_TRIANGLES, 0, 6); + } + // 1.5 draw instructions + glBindBuffer(GL_ARRAY_BUFFER, screens.vbo); + glVertexAttribPointer(v_attr_sprite_vp, 3, GL_FLOAT, GL_FALSE, 0, NULL); + // send uvs + glBindBuffer(GL_ARRAY_BUFFER, screens.uv); + glVertexAttribPointer(v_attr_sprite_uv, 2, GL_FLOAT, GL_FALSE, 0, NULL); + // send uv offset + glUniform2f(v_unif_sprite_uv_offset, screens.offset[0], screens.offset[1]); + // bind texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, screens.texture); + glUniform1i(v_unif_sprite_texture, 0); + // draw quad + glUniform2f(v_unif_sprite_vp_offset, 0, (480-150)); + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableVertexAttribArray(v_attr_sprite_uv); + glDisableVertexAttribArray(v_attr_sprite_vp); + + SDL_GL_SwapWindow(v_window); + } + return 0; +} diff --git a/src/game.hpp b/src/game.hpp new file mode 100644 index 0000000..904f15d --- /dev/null +++ b/src/game.hpp @@ -0,0 +1,4 @@ +#ifndef GAME_HPP +#define GAME_HPP +int runGame(); +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..763457f --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,126 @@ +#ifdef __APPLE__ +#include +#include "CoreFoundation/CoreFoundation.h" +#else +#include +#endif +#include +#include +#include +#include +#include "v_common.hpp" +#include "a_common.hpp" +#include "shader.hpp" +#include "texture.hpp" +#include "game.hpp" + +int main(int argc, char *argv[]) { +#ifdef __APPLE__ + char path[PATH_MAX]; + CFURLRef res = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle()); + CFURLGetFileSystemRepresentation(res, TRUE, (UInt8 *)path, PATH_MAX); + CFRelease(res); + chdir(path); +#endif + printf("Petite Juliet...!\n"); + + if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SDL_Init failure", SDL_GetError(), NULL); + return 1; + } + // load sdl image + int flags = IMG_INIT_PNG; + if (!(IMG_Init(flags) & flags)) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "IMG_Init failure", IMG_GetError(), NULL); + return 1; + } + // load sdl mixer + flags = MIX_INIT_OGG; + if (!(Mix_Init(flags) & flags)) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Mix_Init failure", Mix_GetError(), NULL); + // let it run, I guess; + } + if (Mix_OpenAudio(a_rate, a_format, a_channels, a_buffers) != 0) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Mix_OpenAudio failure", Mix_GetError(), NULL); + // let it run, I guess; + } + // open audio files + a_sfx[SFX_KICK] = Mix_LoadWAV("sfx/kick.wav"); + a_sfx[SFX_PUNCH] = Mix_LoadWAV("sfx/punch.wav"); + a_sfx[SFX_WHOOSH] = Mix_LoadWAV("sfx/whoosh.wav"); + a_sfx[SFX_WIFF] = Mix_LoadWAV("sfx/wiff.wav"); + a_sfx[SFX_HIT] = Mix_LoadWAV("sfx/hit.wav"); + a_sfx[SFX_JUMP] = Mix_LoadWAV("sfx/jump.wav"); + + if ((v_window = SDL_CreateWindow("Petite Juliet", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, v_width, v_height, SDL_WINDOW_OPENGL)) == NULL) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SDL_CreateWindow failure", SDL_GetError(), NULL); + return 1; + } + SDL_ShowCursor(0); + if ((v_glcontext = SDL_GL_CreateContext(v_window)) == NULL) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SDL_GL_CreateContext failure", SDL_GetError(), NULL); + return 1; + } + #ifndef __APPLE__ + glewExperimental = GL_TRUE; + glewInit(); + #endif + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + SDL_GL_MakeCurrent(v_window, v_glcontext); + + glViewport(0, 0, v_width, v_height); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + SDL_GL_SwapWindow(v_window); + // compile shaders + GLuint vs, fs; + vs = compileShader("shaders/sprite_vs.glsl", GL_VERTEX_SHADER); + fs = compileShader("shaders/sprite_fs.glsl", GL_FRAGMENT_SHADER); + v_program_sprite = glCreateProgram(); + glAttachShader(v_program_sprite, vs); + glAttachShader(v_program_sprite, fs); + linkProgram(v_program_sprite); + glDeleteShader(vs); + glDeleteShader(fs); + v_attr_sprite_vp = glGetAttribLocation(v_program_sprite, "vp"); + v_unif_sprite_vp_offset = glGetUniformLocation(v_program_sprite, "vp_offset"); + v_attr_sprite_uv = glGetAttribLocation(v_program_sprite, "uv"); + v_unif_sprite_uv_offset = glGetUniformLocation(v_program_sprite, "uv_offset"); + v_unif_sprite_texture = glGetUniformLocation(v_program_sprite, "texture_sampler"); + v_unif_sprite_resolution = glGetUniformLocation(v_program_sprite, "resolution"); + // world shader + vs = compileShader("shaders/world_vs.glsl", GL_VERTEX_SHADER); + fs = compileShader("shaders/world_fs.glsl", GL_FRAGMENT_SHADER); + v_program_world = glCreateProgram(); + glAttachShader(v_program_world, vs); + glAttachShader(v_program_world, fs); + linkProgram(v_program_world); + glDeleteShader(vs); + glDeleteShader(fs); + v_attr_world_vp = glGetAttribLocation(v_program_world, "vp"); + v_unif_world_vp_offset = glGetUniformLocation(v_program_world, "vp_offset"); + v_unif_world_size = glGetUniformLocation(v_program_world, "size"); + v_unif_world_texture = glGetUniformLocation(v_program_world, "texture_sampler"); + v_unif_world_seed = glGetUniformLocation(v_program_world, "seed"); + v_unif_world_iteration = glGetUniformLocation(v_program_world, "iteration"); + // create our quad vertex buffer object for sprite drawing + glGenBuffers(1, &v_quad_vbo); + glBindBuffer(GL_ARRAY_BUFFER, v_quad_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(v_quad), v_quad, GL_STATIC_DRAW); + + // load graphics + v_texture_juliet = loadTexture("gfx/juliet.png"); + v_texture_doubt = loadTexture("gfx/doubt.png"); + v_texture_world = loadTexture("gfx/world.png"); + v_texture_waves = loadTexture("gfx/waves.png"); + v_texture_health = loadTexture("gfx/health.png"); + v_texture_screens = loadTexture("gfx/screens.png"); + // load sound + // load music + + runGame(); + + return 0; +} diff --git a/src/shader.cpp b/src/shader.cpp new file mode 100644 index 0000000..c10770f --- /dev/null +++ b/src/shader.cpp @@ -0,0 +1,58 @@ +#include "shader.hpp" +#include +#include +#include +#include +GLuint compileShader(const char *filename, GLenum type) { + std::string data; + std::ifstream filestream(filename, std::ios::in); + if(!filestream.is_open()) { + std::cerr << "compileShader: " << filename << " does not exist." << std::endl; + return 0; + } + std::string line; + while(!filestream.eof()) { + std::getline(filestream, line); + data.append(line + "\n"); + } + filestream.close(); + const char *buffer = data.c_str(); + // create and compile shader + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &buffer, NULL); + glCompileShader(shader); + // let's do some error checking + int params = -1; + glGetShaderiv(shader, GL_COMPILE_STATUS, ¶ms); + if (params != GL_TRUE) { + // TODO: replace with throw? + std::cout << "compileShader: shader " << shader << " failed to compile" << std::endl; + char log[2048]; + int len = 0; + glGetShaderInfoLog(shader, 2048, &len, log); + std::cout << log << std::endl; + return 0; + } + // free buffer and return our shader index + std::cout << "compileShader: built " << filename << " as " << shader << std::endl; + return shader; +} +/* ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, + linkProgram(GLuint program) + GLSL Shader program linking. It is left to the user to first create the program and attach the desired shaders. + ```````````````````````````````````````````````````````````````` */ +GLuint linkProgram(GLuint program) { + int params = -1; + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, ¶ms); + if (params != GL_TRUE) { + std::cout << "linkProgram: program " << program << " failed to link" << std::endl; + char log[2048]; + int len = 0; + glGetProgramInfoLog(program, 2048, &len, log); + std::cout << log << std::endl; + return 0; + } + std::cout << "linkProgram: successfully linked " << program << std::endl; + return program; +} diff --git a/src/shader.hpp b/src/shader.hpp new file mode 100644 index 0000000..7168493 --- /dev/null +++ b/src/shader.hpp @@ -0,0 +1,17 @@ +/* ================================================================ +Shader-related functions +```````````````` +This header file and accompanying source provides functions for reading, compiling, and linking GLSL shaders. +*/ +#ifndef SHADER_HPP +#define SHADER_HPP +#include +#include +#ifdef __APPLE__ +#include +#else +#include +#endif +GLuint compileShader(const char *filename, GLenum type); +GLuint linkProgram(GLuint program); +#endif diff --git a/src/texture.cpp b/src/texture.cpp new file mode 100644 index 0000000..d13c404 --- /dev/null +++ b/src/texture.cpp @@ -0,0 +1,51 @@ +#include "texture.hpp" +#if defined(_WIN32) || defined(__APPLE__) +#include +#include +#else +#include +#include +#endif + +GLuint loadTexture(const char *filename) { + // load image + SDL_Surface *image; + if ((image = IMG_Load(filename)) == NULL) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "loadTexture:IMG_Load", IMG_GetError(), NULL); + return 0; + } + // generate texture + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + // apply filtering (?) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + // + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); + SDL_FreeSurface(image); + return texture; +} + +GLuint loadTexture1D(const char *filename) { + // load image + SDL_Surface *image; + if ((image = IMG_Load(filename)) == NULL) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "loadTexture:IMG_Load", IMG_GetError(), NULL); + return 0; + } + // generate texture + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_1D, texture); + // apply filtering (?) + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); + // + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, image->w, 0, GL_RGB, GL_UNSIGNED_BYTE, image->pixels); + SDL_FreeSurface(image); + return texture; + +} diff --git a/src/texture.hpp b/src/texture.hpp new file mode 100644 index 0000000..efbe98a --- /dev/null +++ b/src/texture.hpp @@ -0,0 +1,12 @@ +#ifndef TEXTURE_HPP +#define TEXTURE_HPP +#ifdef __APPLE__ +#include +#else +#include +#endif + +GLuint loadTexture(const char *filename); +GLuint loadTexture1D(const char *filename); + +#endif diff --git a/src/v_common.cpp b/src/v_common.cpp new file mode 100644 index 0000000..30aef81 --- /dev/null +++ b/src/v_common.cpp @@ -0,0 +1,39 @@ +#include "v_common.hpp" + +SDL_Window *v_window = NULL; +SDL_GLContext v_glcontext = 0; +unsigned int v_width = 640; +unsigned int v_height = 480; + +GLuint v_program_world = 0; +GLuint v_attr_world_vp = 0; +GLuint v_unif_world_texture = 0; +GLuint v_unif_world_seed = 0; +GLuint v_unif_world_iteration = 0; +GLuint v_unif_world_size = 0; +GLuint v_unif_world_vp_offset = 0; + +GLuint v_program_sprite = 0; +GLuint v_attr_sprite_vp = 0; +GLuint v_unif_sprite_vp_offset = 0; +GLuint v_attr_sprite_uv = 0; +GLuint v_unif_sprite_uv_offset = 0; +GLuint v_unif_sprite_texture = 0; +GLuint v_unif_sprite_resolution = 0; + +GLfloat v_quad[18] = { + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, +}; +GLuint v_quad_vbo = 0; + +GLuint v_texture_world = 0; +GLuint v_texture_juliet = 0; +GLuint v_texture_doubt = 0; +GLuint v_texture_waves = 0; +GLuint v_texture_health = 0; +GLuint v_texture_screens = 0; diff --git a/src/v_common.hpp b/src/v_common.hpp new file mode 100644 index 0000000..a5fad6d --- /dev/null +++ b/src/v_common.hpp @@ -0,0 +1,40 @@ +#ifndef V_COMMON_HPP +#define V_COMMON_HPP +#ifdef __APPLE__ +#include +#else +#include +#endif +#include + +extern SDL_Window *v_window; +extern SDL_GLContext v_glcontext; +extern unsigned int v_width; +extern unsigned int v_height; + +extern GLuint v_program_world; +extern GLuint v_attr_world_vp; +extern GLuint v_unif_world_texture; +extern GLuint v_unif_world_seed; +extern GLuint v_unif_world_iteration; +extern GLuint v_unif_world_size; +extern GLuint v_unif_world_vp_offset; +extern GLuint v_program_sprite; +extern GLuint v_attr_sprite_vp; +extern GLuint v_unif_sprite_vp_offset; +extern GLuint v_attr_sprite_uv; +extern GLuint v_unif_sprite_uv_offset; +extern GLuint v_unif_sprite_texture; +extern GLuint v_unif_sprite_resolution; + +extern GLfloat v_quad[18]; +extern GLuint v_quad_vbo; + +extern GLuint v_texture_world; +extern GLuint v_texture_juliet; +extern GLuint v_texture_doubt; +extern GLuint v_texture_waves; +extern GLuint v_texture_health; +extern GLuint v_texture_screens; + +#endif diff --git a/todo.ktx b/todo.ktx new file mode 100644 index 0000000..f98f8e4 --- /dev/null +++ b/todo.ktx @@ -0,0 +1,20 @@ +1. frame buffer coloring object - additive layer that fades out. colors are added by players and special fx(blood,etc.) + +2. throwing entities (can throw players!) + +3. multiplayer, _NETWORKED_ and local + +4. game is in: stages->waves + each stage is a separate color, is shown as a separate FBO in the background. when current stage end, it zooms up to replace the current stage + waves then occur + +5. explosion/gore + +6. multiple enemy types + also boss - each boss is a shadow of the player, probably + +7. some sort of block/counter attack + +8. items (parasole, etc.) + +9. music and more sfx