From 9599042661b468aae7bd34dca05441c0ebc93ad7 Mon Sep 17 00:00:00 2001 From: Matthias Braun Date: Fri, 25 Mar 2005 20:39:56 +0000 Subject: [PATCH] Had a bit of time today and worked on supertux: - Commited some old changes I had lying around here (improved mrtree images? and improvement to the collision grid which still isn't enabled by default) - Did a bit more cleanups (eleminated defines.h and renamed scene.* to player_status.* as that is waht ti really contained) - Cleaned up PlayerStatus code a bit (refactored loading/saving out of worldmap into PlayerStatus) - Implemented LevelTransformer interface and did a first test with a flip level transformer - Removed explicit LevelEndSequence attribute (can be done by placing aprorpiate SequenceTrigger objects) SVN-Revision: 2293 --- data/images/shared/mrtree-small-left-0.png | Bin 9835 -> 9908 bytes data/images/shared/mrtree-small-left-1.png | Bin 9776 -> 9918 bytes data/images/shared/mrtree-small-left-2.png | Bin 9482 -> 9606 bytes data/images/shared/mrtree-squished-left.png | Bin 5935 -> 5933 bytes data/images/shared/mrtree-walk-left-0.png | Bin 10207 -> 10253 bytes data/images/shared/mrtree-walk-left-1.png | Bin 10231 -> 10352 bytes data/images/shared/mrtree-walk-left-2.png | Bin 9863 -> 9987 bytes data/levels/test/bonusblock.stl | 2 +- lib/gui/menu.cpp | 2 +- lib/special/game_object.h | 3 +- lib/special/moving_object.h | 2 + lib/special/object_remove_listener.h | 3 + lib/utils/configfile.h | 4 +- src/badguy/badguy.h | 10 + src/badguy/mrtree.cpp | 9 +- src/collision_grid.cpp | 66 +- src/collision_grid.h | 4 +- src/collision_grid_iterator.h | 8 +- src/defines.h | 50 -- src/{intro.h => direction.h} | 22 +- src/flip_level_transformer.cpp | 78 ++ src/flip_level_transformer.h | 29 + src/gameloop.cpp | 286 +++---- src/gameloop.h | 1 + src/intro.cpp | 34 - src/level.cpp | 91 +- src/level.h | 21 +- src/level_transformer.cpp | 17 + src/level_transformer.h | 25 + src/leveleditor.cpp | 1199 ++++++++++++++------------- src/leveleditor.h | 8 +- src/object/background.h | 2 + src/object/block.cpp | 4 +- src/object/bullet.cpp | 1 - src/object/camera.cpp | 4 +- src/object/camera.h | 1 - src/object/coin.cpp | 2 +- src/object/flower.cpp | 1 - src/object/gameobjs.h | 1 - src/object/growup.cpp | 1 - src/object/oneup.cpp | 2 +- src/object/player.cpp | 145 ++-- src/object/player.h | 52 +- src/object/specialriser.cpp | 1 - src/object/star.cpp | 2 +- src/object/tilemap.cpp | 23 +- src/object/tilemap.h | 16 +- src/object_factory.h | 3 + src/{scene.cpp => player_status.cpp} | 69 +- src/{scene.h => player_status.h} | 37 +- src/resources.cpp | 1 - src/sector.cpp | 18 +- src/sector.h | 12 +- src/serializable.h | 5 +- src/supertux.cpp | 2 - src/tile.cpp | 2 +- src/tile_manager.cpp | 1 - src/tile_manager.h | 10 + src/title.cpp | 9 +- src/worldmap.cpp | 16 +- 60 files changed, 1266 insertions(+), 1151 deletions(-) delete mode 100644 src/defines.h rename src/{intro.h => direction.h} (71%) create mode 100644 src/flip_level_transformer.cpp create mode 100644 src/flip_level_transformer.h delete mode 100644 src/intro.cpp create mode 100644 src/level_transformer.cpp create mode 100644 src/level_transformer.h rename src/{scene.cpp => player_status.cpp} (58%) rename src/{scene.h => player_status.h} (72%) diff --git a/data/images/shared/mrtree-small-left-0.png b/data/images/shared/mrtree-small-left-0.png index 4842eaf8d00450acbfca3e068c21d04791ce6296..9bfec1f4a1656c458362b43eacb7d0835ba3235b 100644 GIT binary patch literal 9908 zcmV;lCQI3gP)=O_3;K9CRO1NlHckPqYo z`9S`Xq}gnqa`vUmt~zn~oU0yM`_L7CuFoO7*EewQEB8+O@eMz&Tz=kiY&Un~(C~}1 z8z)QTlTo+6e$l3YQuU<;D;BPqugx!h0P35_1Ft<0&gD{HzW>hKKNBb$Zn6soMkK{) zD+4G256>0o*zA!@)}yJDez$blif^x+dETZEKs}7yy!z%Zx3sMJMk>9wdcg{XXjCPi zE8zS7A^&~LT94gr(Ooi)-~Y`IZu`XtpdP55v+wxLFF%XY{kMyjg~chyt9ZUoCY8hU zyd(Z@CMmOXoA1Q?%DYn|+Lz|cpa0nPS6^@cp9OXG*42UE{`^;STt9Vh{glD+^OtJG zDns}lBoi52*FEab8tzr;X&2bl##6D#8BFAzvhu&cA6;db={XOQn#h zLt@g8V&!$;{<{mlTKKaEb=4KC#z!l(C&E>oGtR!Gj9@TOGE>hZmCRBsnD5F%4R!^| zXPXB5+Jeh(zTxI~J+~dRKy~*^cYoc8*lQ-u9++|N$D^Zwx~@wio_^N=1*mSy6KiO# z)}kAq{^Iwp{I6GrUYYr4r>XyT>%V?x)4FGVF@Kq@&6p=6Nz$l>CgWL5(|j+i5W6VS zzD2;v%tIa%7Jpj;=q11Z^s0AZ~d8+Dc z%IeoPwYR+T@O`h|_u2QorhfguzP7S;`>PLL_*p@%s?tZlZ<+R6PU??^WD^4YeM5Mj zhv@6{byT}^=Y8aRU%cpR-#M1omEP_zp8bjCXAbmlyW{-7mcsRo2Eb^9PbIU*EW*DP zQFV|o$mjF8o{6d_#qFE7En0H+%Jdu0Zdmia0`-EwxnN#-rFj2}Pxv)WQ}zLxPUXmE z@`oix-U|sBC14AM0)pfdjuqveo}ROtrY_pjx}j~``vTNk9=l~$I@R@u^RKk(s_W#@ z2w(D_*87q*3K$U3LRhAQ>$#Lyx?<1HkxQl?zhGVarj9WWUT-N-t=_tN;+hwqfAqLT z@fsuKjrz~{aQcuHQhY!`5F`ZQJ?FwvRH&#ZClClAh%kGxjbgO_zkj^>+H>DCP+yI| zTKVF$&psNd>1>)jr=+3ze33*veb|ySoAA+f?L7z7%t^Fuw#XzcdOP6ByNdpXm$bgb zNJiS-I&w?1**tqJuO}bvKYZrVr>}3>@zBgGzSM{+3D~y%raY*oX#_(GiY8Iamt2Dy zkjZC@?`meC87{7C)4F2>&z6{b(meOnC6|6`$%+QHtuv}uPMX86crfb78h!o`<;Zsn4OrO&?WdFtxTtC!#QzdyTk-s#E8Ig3KL zu1hkJIU;T6xdPjoO(e-A9?v655_R>p2qKu~2;pc9*AfY60E9@3K}Ox2^cPL z^yY6BN=f`)-z|wURgI-vFJuIIyNWoDha#)=4gPcW52y3Kew{aPa*`t3_P50^6}LYqO#LzRqiaF$49GdFo)t;5TKXbHSWbWDtBh zx24$G(a&Ii63>0_6Zv=i(2`G8qfC8Mkg4+m^!09DF_yXNAwiAR#Y=ffY~Hf!z)GrD`9DnD*s8M^WQ)4+Th3OSE}ArLMz z2n1Au!2pV;AW8z+luch(j$*NZB+3{@ki=k-T-v3w-Y1_m>E7Y&_}jDp?!I>=`M>_= z>nHy4iQE5p@zt?O^%E3w+5G#`v^`fKG2oKV=Me+}RZ|F+sr2q1E>%>DhL49RNYsoo zkQJYnwH_U<3G{%<;I8U*kNx(gF&oG@qCLOx)H5HQvoJhq(sYA#I{Us3JQejK5j%`y zK_+Q2(3b$9rmg}#?Bjdj`$8#0021*$R%-goJJxUeiV?I%-W8T=_14u|FzkN*#HBjE z@4smk^?ndp@d+3{RSgQY^)c!isxTrxqF74t_5uoU{QNNWQ-@Aju=vyqulu*}cD^eW z)$|jlU*ihfKe6D95b0DF*BzVoh?$dE^9PTi9+gm3!U&6pWV+rd2dikH1`hvoRR>wJ z85zu<+P*3C-0RQ1-g(S5b)cvF+b5o`QY@H9Bu^Y$V5HBbm=mx~;Q&9UY6_yb?-GN% zRPOxG9zJ=+4>|YTZ}Z5Fan?WQBFp={P(CTM{v{jJDz&=JoWw&n5A)3Z8HRh+G1cr) zM5D4kPCV7}AI)ZS<1y9L>uL^hyu)azV+#y)=SUaNF)=Z-}~LK6C_$}2HGs@rm2V`-2c-)Y+J)A>cn5|ia^Co5Um@*b3{zjq<8l)BfTDGP9T}c;Q2PP zW`5zAn13*;UXaf0K2aAIq8lbN=gZ`>ds`3|>2&&#AJnzYWO_J&p-I#Q73NjzNP>VO z34{X@V=e2G4z-Kx>1<2Uvol9^olg5Uhv~D!_`Z+lc?|9LP!y4I(>%saQ80oLw!D!? z5JZAupIknNZ8`MrD)7pa8C=JpedEsWj=83ubR0O}?vR5ui8Hk*$db4`8Z_D9yd@%*|w`Um@Go^nofr4g}64=Q9c zMe>Ei*Zw`N7EOwZt{$7+jJ)lUuv~OWM-6whPnmy zEdG#!<2VQT_7j#UY=1-IvAg>Sg%zgEC_CWiRzV<@PEuJbGHF^FuRk4R$L4-cUZzt~ z<*~E(u47kG)r_mTdE5GAxN|GvGnRd<>C%t?;MN&aKDFL17^5UFKq2eVy2)k2adq@J zhFLGm^obIwqRZxRkczV>usP+Bvs|7YvFWS~QLHaxd(xrJ6G^K_$I;5F%={Hq3{8&E zFg|d^VT{Sg%S@dWVZj-dOrCRK&bqT>aJ1J}C={4FVx_Ht)Lg17W-nX4 zb#>V>qyF}nr`n&I^y?dMcBe1M{dlfVU$;$aNN3e2$6MUad1B3pkKXra z-|Aae?>c7G-+qjh@YEB3_*$eo-DsOWzVEZU!y+9Iaru{~;JXD11&2H(4QQMxaq)Fi zas2|>tjB~oGNGFBC6_RqM%5JRCyNvd7C}SjW7kcfWo?$`r}~&elBjM}IqviTn_tYbz%~df2YRNg?IB1mo(288@;Ht| ztRlkTzzDgFN!w--Sd#DbwASrMq(kXRXVukbb~DtW^}w z`s*0+KA-;neh_>lNy2qq+P2snKW7q6vkuRu<+3unwwY{MKS*Vb&f;YiXokCI1zyD~ z%-xYq*w0L#JsV9kzIfHSSH0~~pf^n}Um1C2#?z}G`PW6uyl{JK4@3P9!+iplZPU6n zM_Hsah7*b^gd;&jQJ{ZUk!Y2Ou4@eS<-wCs_rqwY*CLa&(KH2F^%>|%kxhCfHcWJA zZMPV&$VmHfs{OW0ph_m57{TAu%lCaB+w{pN1FZO14Tb#S&sk7akE#Zdsk0(Xn5v`d zrP5mvAzI~;O!QXx+U}EHec`#2Lg892yY@3Le*H&ZFF$4ZDZ<0IJ#3Gu`EPlBOU(1_ zx`*%H$@rQx>Djps#pq-6+8lEiL{VfF+qMw|pZHLYOx&Zgp)|zcc^;xDl1UYiRh@uQ z(#%vUh39z`%>rfN2;p#;k&zKJO~bVXrkoU|{goWY?Z*RXmPvna94+{#jX#fx(`t{N zybgGWY{yKQThg&`RH9|wBmY>)pIf*7wHKF0s%q8)9~*0*`r0Qy^VuuE^~EngFzuq) zSEIGb>nf`?6h)?KY8Clh4ncC+-I=0TFfp?MHa=9O&{v@Pj9Mm4ml*C#(9&GswDT+Q zJ)d;SrGKr3W_V~dIjYAgh`z>SHxFar6O1Z&7VKZzGz1lTvJP5M-or!qv~RU=ZHCT<#?+^--nv@NrBdJd^^HIO}obX`t8-wq#nZ9fp*D8X{~UKx@C(;2zt0~NpL)wPaU}K6XttU9H0iqRO^sbIfuynhXU|EWN9s<&kO2QZLAiD^rL%E<;;V zNJ$^ff=W%MMpuYA5?gjyggu}7u!1BA7=Kdk%7TDfEg(gOJv}TaW@VP1SBYs_W44to ztB_7uqs2uyBG9^RaKQx^f1>TTKYo1UJF2MmN+L1x)i++d=Le@>AQG!nm^N2JE#JiP z3uH9aBNVE{E*L~jnTD`(Ae|SInN}4bqLMaT3es`?&%kVa7=+vuna*EsH&A|*}R+nZkLmm>MU9wM6A^*I>&Y)O-TzlcF6)$ zahScR3{etr?6KP)P?VBl=(Ro~`kSxUpM31L|5&_& zS&fq<3ON_cbW2NKqJWP_Vo+e*ykD_ z*T)bA}pKY!rVhoilT_?a8$jk!-A^H z2(m+ePl8}Dgl?#0GC2YPpPj8jY{}WH?rna)*?U`>no5n_HG6@4UgKneq(@qc=x1xcLrZC3Y6FB{I_1GuYBb`=Gvm&s$Oyi_aPo!*FEpNC! z1KEA4-}bD-)(EWW&S5);j|CA`VzFYN?H2gZMKwsWNFisA0va@Q;{DFs8uyeW30anL zY!@w1YINpvCPu_38Yw4QEu)tWt{OkJ>bkdO{@34p{qY@LPo6vXRDt#_35q#^{@(bg z^Y&byzFiKYAmRA?9$@kuIPIJu$Z z*9esb2!%^ij(ewaVznx&s!}__q_h2{Z~gYE-=6oD&3`04@`V{Ev&o!LON(*BbUCisqu5rurlLs1n1hL5=4l{CX+@~kM`8EEa!Q!%ZaK*Yp| z`gjq6mOhJtv`cT%r^EHx4fN$4eAA;d<*@W`#}kgoq*MEj4p9`Do{j?fd>#-ei@o*1 z3(KX z$3c=Mq7{L+H(;U3q!M|G#Ui0lh+w#6%z+SCc?n-AkV=g-wQk6EuYF;|>ql+=H{Wpc zn#@4vXH65wr9+44f`-c_Y&S*obNe33F$G*(zzFN3N%)bbu611>otGNv-DB>8k01!R zj)x@5*p|ace3*fO0bYD^kl_)N3$Jd(b=^abb?((#SyJA9pwfKmCq$^NsoQrTge#C7 zEDekZf$mTM5u1_W*5l;-Lzy9g3FL+C!wr+pzi_>T6sw|6C z)`Smep_DExP&ZLv+lInu-#3K4 zCxGtu0`nGD(JC1BH?;D_H!wAE@ zhfZ+>K_FIHMkeVTacQ`}$E3WfoC(vV(#aP`*yU;%AET;K9@AwHBjQuvP(!3FN>Deb zET4d5$*bRZ@{PL=cRbTUpx*TRn<5+6KKsZi%gSX*l2B9-1RvoL_zNO5G>NQxrL=?` zDT`87T{)_8sbrdTI*nr;45(?EBoi4jnZqwId#+FS4mjhy>Z2MuR`p}EITI@PsTrpe zDL1H`&`#g*Lsx(F+T}ZcbmxyQY&M&fHwAV5Yp-6Dww?`5IzB|9P(V_AJP$no(50Xx zGg>;v>I5Q{V3f&dnnqb!8MO@&WX(UQYEe~{zOLdCPXpT3W-;&NiIkV`8$EijM3j7D zbpln@F(yoRrP%n*;~#tU)(5t)*>-)i*{mKG)a!4)zNWi(?d9`NtwEMWQUgK>G*iH` z4&HY5J!n~PF>hhyz{noV$phE z9CWi+aVlb!RMtq$TsXkP5B&GHc5mJF_lE`56Ee3nOis-jKc$TLND5_7s+UQbhkW=# z)+1IOB~4zUt&1J04#;bykE-I*;!`!0;(#ghLLc`5yFiILw+?`Ic{LsJB@9O#jeP z+BSX6g3c+c!W0VT`$kLmrEO4NBT-#dgBFk}uj>?7|Nf?by5spf&K(7{*=$z&26ue6 zyf$4{Q70Wh_!hF>=+}ZE5R1hK>IT`Ai>w(mPIZq^Pjf&A)pG^134wvWk6oDKKuSWG+mTRxomB1HKB-t8r>Sa^?!f(xm8!JD$U$%dSTPYq`=^38k>UT z)1c}CxnwE&#uFn4!VmT8xAJn-C|H zmdT{Eqci?{{~XHnEend4cYr1i_TzQ^d-e_BKwQ;`>7k_N-ee0bKMq7oXg*b$D{i zrnDBTM9~fDh!>G$0bTdlzA-^!NJiH~6mvGIbh0!T8V(^z(!Qy@{aojfZaklHam_Lo zFRvh-%8^f(PM&zjeJ5EV6xIm@G!T3gRYKGhWT`~9OOlLbTG&OAw?i9vUdc*L)26)A zJFuI2->Y~1e)SXIUb*rU)ofc=I^S zrBaNaT#ocM7R56o0)2h^I{MPiYnqcBAImlO?{fCizG)x<}Rop zS}m}@bq90}*C}oE9gT17ZuhWlmzV!IKqw**DT^}cIPD$#Rp0mN>nzgSGqlg&?*YK~ zeR8=RdLT%&Qa$WTIn>(IC<&DENhd72wn+?h)$Idy*+q+P3s-h7ojx~AGLb=2d;~X0 zC=|xBt;0T8f45C_Z6!@J4lZLQ1;SAU3F81J>ab3~3|yf|LzBc{pUKkm8&Oq%Oxn+a zAfZS)<&|M{Ekq!o6AqW5sT!)P5vwvrk0h}zu<`;%nL;QWL{>$H`z%KKizte6&_0$P z7RaT7gaeC5=AO9tOc~&F-~GEK8`j@<+IgQUBbiK7$oQx_L~BYbG3kta$cSwfd@{)Z zXRHd02CGAn8NPS4cP9_L000I@NklZC~dnU{BP&I+}Eh+N(H$PB!@7XMZAYj=RI6C+eV0FYU)GOHHK(x3K6UG80>fP&G_oW@~N*r{_^7X z_Qy^+@w6!ERF2BZN+wOKB^W%qzE4Aw%G|{|YhTJy$g1z?Fa+E5@dfvYW$mA~K8iV^ z)Y(0R)KB!u+pQzhrY~Jk9GUy^*B`I9I=4e@T?LKftC1Cfpl%TF50V*}&S1xh&rEB& z=9cYii_!kh^60iV#ORtwQqct`Uq1QF)Bo}IWZ&Z3zk5~p?Pg+Xs*us19#yveFS>f` z>cFix-E`-Ivxh&@FxjVlbCRlt($H9Zz$6}zzolTa@l}h-GlRrxd?ZQ0a}9F&EQaA9 zb~Hy2O7l{MtVcQ_ASphRrj;>nN(j#_^|baLdAC1NF3F{1=#r@ff*_!&0d!p_6crEX z2y|^xP=zzMU-zl+A9u&|cP!Y^+Wg(#o>xw5n&E|Nnk*i_D?d1S;)i}WZv2ejEIMb= zw)xupH%&htY<}>B@yCzvn6J%G9R%w4Z~gx9ZS7BN`smd%o8Rb16eKF@MFw_Rqktam zoRaUs&Q_C6F9)5)%W5RYEl^*({EnUNEy0eiSI<`zji#n1B*i6fb>P@OnKY^OtTQw?d;m|Ysw#C26(w$X z57oH4)o|N4R9$r2kMH{(z)v6i>8iD_J$YR^+jjDCGcWk@4WIq@ufLcuxm?+&mw)=7_rKl?3>@aR`haIx`KgsRM55x#LZLuDR~VfO5RmEH zWg_?nRz8ZZhp8V|Lm>gX+I6z|-Dtr)Wl_jvOxo5(2RI#lZmZ=-QSu+kMRJO|eNA%hz9P&A*R{w!8BSzlbfa^>?+KVG}IwxQ~> z>gw9=JAQTF&(rg%ZzTo1EX=`cumu;Or{aRfO6ULod(!#cv*wvC^*?Ft3ZQtHD zE2xJV9?X5|;?G?f|KA%f>fO0<_c7;M4>12nHav35wV$}Q_PmdtcWK(c|6+jvhIH~9 ziypiW@V zJoC)go7n{?#wef;!eaXaCQW zzqp6wdcU;fViy^#_QSGFWHK3;rg_Ytm1P4x?W!Id2!xVR+t=pJUG(xdzxhq=|179Y zO-6RGvVhM}t23j*+ZU9c<*sZVQwdRu*JL-l3r@44qIxBbbBnhG@9ycj{R7)mQCCAOJ{iJndcYJ6dj-&eEhtAG3T*1rf(UAJ!CmG8X2{=b)Aq1o!E zd&U47kHvI(T zT~*bSCiSPoWJN+WI+Rb0mK_6aWyai@H~-5WU-;4wPUn58kNSh3`qZ^6h9Y~Ox_XVv zSyJj8<&F6apH8PwTZTUwqiF^V!$Oion5F`o5GN1p-*^6si>^<<+q8Y#CkoUxx2&1# zD+4WpWu*OvYG?U3d6vYXJk&nX|h6Tg0kYx!ZX~N~t(L+O_%j&9@?>}&$x%m?T z>biC7W~Ic=%~##bmIaF0LWci1Iskg}o%f}lVUAG;O~ilX6h2f*3vAV^^D1qy7g zwkLk_)9+mIv4Ps&-X3^+%j+AxFj*p`xQb0#m0jZ{9*<>7v^|cfqncv>ixK1&}%8lP*R!)e$!78VrH%;Yl! zk|aS<6m(sOWmzCe5-iIi9uv{i(+yrQL6LT(`p=iveEPmCXV0GV)}*tJ<9*>X?997Ld)#*#7=*B;q+x6os;~a*!k-YZ16z0jLTIn~i|W!=8Y| zXF^p~sHzOz(BN`xMiGw=pT z*f-GJBY1wjySIyne}06Q<_*JB#ctr#54^eJz@we6`h5>sD!<&|3={ml=5yZma4 zX_#IHNs=H*CoWG~mI>3cK#>#-!+>cTplRl`(ks(6VOb_9ih^O9$mKHN?Ly(62fqkW zOd}c_hGkg@HBS}i&${)}JMX-6+iBhYp-{;2jr+d*wOI=#rXpB8n(5Q9EDH?7p4hiV zkf4Y|o_0`ZIj!mtNfuZlMN)7Io^e0ta5!O_1~grTzdR!h4t3oPVA4Pxm#6mk_y0ZT zZa;6v91lPMgM)n-80UvBG|XPAI*CcAW0HDFMJ}ZqmN?wBxiwT*}|^rZcpQxUk^i*oMrEAd;9c)I(6#Q zr>lYuzwYgzFx1wF{qMWLvNi|~*C!6FgQ0~a$pS|uNfHD>9QJnt(bzD;kwL`bBOpnD z=Xn&DIZ$3+jup$U{pR_Lmc4%3%T$`Cw|?>S-}vfPSFGu-n4-et@fQO9C&^@4f+UF` zNxqO|b|^Mi7n+!R2O9IW>(PJKw1Z27@DK zq@r58cI}i+Z~W%9HGfl78z^!io6UYAx3*O!X@4!fSFkewIHkg(P zlBD4C7K0>7bhoF`)e(oy#$u$qeCLK|Uz@anjAN4jH{aTP!@To_+KS2|WHOmgY`i20 z0)pTM&pTn5I&wJ?k!arW0{&94tPPfB!LklA1Rx&IA{U+2bf9nFHyxZBJ0mJpQ&W>o zaOrdg|J>YkfU~Z+!Rs=nkM#ckKw&1yCCZX zW-chjhwlv|wsi!~sDj;l5^$E<;qtPR^Y9c6f>JR&7%xjj9lbBT^zydT25M<(>33%= z$gL+5}Z`MG}36jSh0(=-u{ z4Ic$40FVSA*ho}5Sx{|2`#TDz7uzx2M&ca8LXmK6J-o<4qr%O=MxR*LCu9S=U+hQg z{v<*@BKGcx9VLE8!xAoDRe?*ds(~gu(Y6OLbPB2}BODokD5jukDw2r|02M5!eD$u#>a71vi`<{U40fxrh%gRt4`u-R;Id051whekMyO6^#&suDEi!hszm zq-=h2#l`^@RV+$^S%gBN8I$g*8#erY+q>_+|J2CH$joI|c&_B^7P6uSNlrtO#R)Go z(4nGKW5Bbg=WIq+Gm%gX@HC07X$_*lfGcKDQ6qq6^7jlBbBK)O(6T=UUw}sjxub!6Lf6baTc=yd$;r4i; z$pVtO1E5(Qa~AO9CI-U-vZ@LHq4gu4)6o-GF{j)Pnmn}%Y1ja)20zT!80s&s#)8G( zLL2&Un>E*m&ek+u{Zkhl0)wg3-ACxEI>bx@ff5o^rh4$+W&te+hOp#P2Ydk?q2b3* zUqw}3UjD0nyOPe1eWde}i*BvC^}7H1pJ^3e+9hY5V_G9p)e}dCM!IEDWHcnmcCM zTwN!ieuf8&mX@Guif|Zbl`%Bj3rWt6%7!^K)X%|VkG$|InO(VFiY|oDx!{tfrlwO8 zj!)QqKK$^*+D9M%-rGwq&sGi&^}sYO423krM;y4~+L_=SI(VK3EE0X~DQx)J4&43Y z3t;0&bTy~&?uHgr&MU!XH#We~3H0}-V0TcU4Fb5{+*4W znB%wTyV}y|YEOfv2~4SXp|V;4MG=T6!ejD6PBak>RimM5^(8mnc;nW$-+p`k3opDd zv|+=BzSAcC9i~_jn>TOzmbWBTX&CwHq_*Uz`u!$U%}cGyK@jbHxHmDX(7NJy)1P^95@C{5-^Y4T}-MDdMO*Gzh_qCrbLtFc9B;z_X$&K6ZnhV2Jkxa%%V;f&+V-REx zrpXoJn4Lc~wto1b3Q-h6Q4|8j0%k6^qh(75_7n*RGd42i3=;%t0I(2?hoS2lJnjHu zv0-GhGTQc1pePQ}5gCh@m4LHT04OXtzX*w#f;V632gQo0n@%H>HlfR2T=A(2^mU17 zJ+SlYP$)D#7!0a94e!j*;O(bTsRjyI?A8_8bJkiEv~XeqRX~ zhJ_#qh$kY@R2_ogf-L7i)9e^2?_hEv2m*>KgCz28pkV|7R8C<)d$%?? zO16FJr8j=`rM~{w@2y+6jyZ7PfdAQNpXDcg`|sMd%V%0@*^AG0qTII2 zA`t=0a?msa4d?pM^->Zu{D(Y`%E=fRih(=g5yiBL+WN8+e_)y%M-T_9Yx69@=}V(| z$A<68(x=L{?|5^W&s(_*!0O44sqf!@+r3}>)_3lHuI`$^eSwPDcYVb+undFR`eNjA zA_&sJNO%CUEJIH7=-WCBaWIFe*9K8DokDadftC+sEV?8J)3T6G>F8}zz`ITG6(UN4 z3@nqw^S=y(k`1^6EL6q9;iXNbgF!Z{gX8JZ4uXY_W)+%Z$ATLJg(P{Z=5Ys5)zFXj z{lEF5zr;XSe_dHBGdRobEZ=|1?UaMIe*Jp$t6#l$|HUgV|A*J#df~Q?j&6^coeho$ zy4y3*%?vo+KrXAIV}}fLw~7XmK#hmR{%#Sx-;Sa8Qc&Au>~7D%MbXw;4iV@5JPW$3LLtH3Kx^tSdS=oX_5d@Mu7DniiMI$WAR0``MK3eI+ab56tcMt3?qL% zmxn-W^T>JEU3EvtqrZG^&!i-(cp`e=2YdeKUzc8PN1%ko^m9l!N69RTlOCw0DyBuRKYey}VDo)^F{BwW5M9)I#jcfHnhzw4Aied?*FR<`xL za{sbbJ{X1p2cJJ?9w?#Fa-a|Kcp9_k7h(RQVhA+?B<=JbNYld>3_TBEB1&M^oHCFk zIjQdsV3@pP*zG+0z7jZiC#E!T7#WEz-Tr>(qEp)bYuB!|zw-KTe!ApRa#l&H6N;=r z*HuVzF2B7A6R|LX@?bt|(gK46yW?<>1d_UixQB zCZ^p)PaEaCc-al>-+S*p^CN+}==_DhFRSalreTJ=a0=z{QA&;{(7ap3w3z{r6akOV z23v&#VF!)ig_T%zO)W-1ft${tQ#6n%vSaC*8qie^?C%yqiW&r4{`Py)8ZvbZdRwz7 z74k84JmV-5WhR4AkA_P=KL;+4y|A|YR3eXfJOY-roiwlq2i;851VxdE$0ML<1`bF5 z1v(_9K+4GtQ)@0VUwQepw?0aXy755wvp>D)(jsbjq#qQ;AQm4kv{ekl!eF0{Y=%O^ ztN^Id>0Y;&MP;=EvKoP+D5z=h!y+=6Illt6(_FA|6g)l~Drb7Ivs;9gP{C6Kq;e-_ zEwdrRsfa{0xGiczsZi2PWa_k* zB$IIf1bAM6jkQ6NvamT64EBi^eCNKO?p(KSUE4{4noK6YI%{EOxuS_swJc;M2lcS3 zC_JbGh8IhgSAsvHF_-sb0ydi+iY$Ys+0iX^2qcCjQ9H|nsA=Iqmxzja#o)a%1dj!# zltTNEivEO-A=N^NBrwd72&FWbiiw_#j*IVTgy3Yyoa15`2GQsUa*_xT1RT8cq(N0x z72)s@5{VdeT?5DQ`4fT!35uejs#%ysjA}g)t@w|%kNxqn#~zbT7}T3?x+(kkBk%3v zZMBPN`v`C2%p*FkfUa?vI?X$#%bI^U7Ir%i)3iVk!mY^hPM>IMN zfPmNMLQx%w-~tD#&vl`0z8h2Kx>3Kl3>_&AB28h%RrQc0X^b$VX&T~*eCbP36a>Nj z(G(_{MMg|QmL)hGPS{wk@LYyr0Avp7Or&Q2E-|!i+x8DmIQu{J&_mne@%Y0v)m7;u zJu$z{Dd=Vbdv-(*Z*9XgO*kBmF@niNX1Ci5|1XNEf}BSZ1PsFfMbgkU4e>-6k;o95 z-s(pdAxR>fP60GMvAjSK1WeO_Ea%3=WXrOk>)NR2S?KGMFn2*I0>yUdx<26%c#rNQdb?eqGA5Q#vVX(Rg6iI`o z@}6>}c>@%>JBOgDd2iJX9j0kQ*ENiU2hh{ofpjJ@u2&HR0k7AKoFo?F@wg4Y0R=w4 zA7z!U!kaH9vf`o&xLn>zJ?z1<97>8S;c|MxaSr(1HPAKsrL9}HJ~PqlnT`VW>8GD| z@7S~XPZwU|qYut_=I5BmaXbU4s3EX-M}DTtQ~o? zLLuh3(%<$STklqw%|c~xAw)4 zhQZKq{}`%vr1f;Dn75z-Za0TSGJ%iN2!bd)A4!F&$negp7dM5UD=RPkVJH;3KNt+E z<9h1awQEcIhqr%r;rZpD>HJ$G34+Wk?Apk zKl{gx8#i8205uc}F~iZ;djjQ2m&@rHBc7)s#i-oFZk;x=rV>vh+&M_~C85@fyj*r=l0bTas z+{IPMWJJiaR*)4>#b}y_!{LC@e&tZ`y37@YB z>2wk$CBbovoa31+=Matz7AiyfFpgUG%ya)*dF?IrqcyZ2;mFW&FX;ACaJp>pcm>#w z7(@4Zi$IbjBsq6X`gF)sNwl>LqyN<;%7Z)t0UuZ!d&;l7GYkX6!vlyX!uj^LX%u#l zVHk)+hT(9yCIs}L?lDn(Hw+z$M5Cvbfh?9C0+nKvUo^}y=D0lqVzDR~#s;q^kQc2} z(Fv2wU`WH1y3(TnJs3ZIz7j}M7U9U?F}FSd)XyTIs&UL-NTH`)#NsO&L6Vcx)U$DR z_#w8%;P3wKTT6GfJaOR_*HP!tWG&r4sFNCP+;R7JLsN!YREnm{22 zk0)lFXaW#iR6zu1XctP#9Ee7vlOv2UHn5CsG_lymWeA0Y8xJF!%YftTaPR^$Vj7Af zj}|T}I2#X!Wx?Bp0%vdCYnlsY&UpNe&)@St4FKCk^ke`41?)*gK~yfUwvGy<{OJr2 zaWI48;)+p9l|R8@j^p-aTO$4*0F!j2#QMuFf0j& z;6XN<8eK>}BhoZeC>~BYms$j(VHtaN4xp@(14R+1?cFUDMZs>jBbkh$r?(TDrk+p* zXIU84j6F~l6P(@NHl|1Q(|>z-@2qJ{H-rbRXSTe#_cBYVfuhB2mDO%Ya&Fw4Fbsj} zItBzOBN|O1nGn&wUxAd>!8yUDh>ns9>LYbWkF-omLwj=; zZZAFh>zOPVh6aXVFfh;yNy@=I&W@4qeMoJDpx1o9)1YKA2+YkofN zwd|IlD)vJBIwerkI&$H8=ul)R#BR(z*AK%m(b~2Tx~`+Bs0`8ANMVjjmP{m~I!1;x zc%9WqBt#^W36zuxD6e#)qFR99a3C_2fuY4;noy*E>#a8yw})O>GHZ5zYT4&2MrB1E z?Di9DQB>Bjn7_o151X>cWo#gbkAAL7RV|RD0aZJ(-?3sC`e++weCktNW`kt5#-`O? za$a_1!J6%F`qbV|4SruaN=t$uND3S$AUY@@IWP-@&F8;aSM<$C_wSTE{T+VK-uD^L z>o3MV=Qe(+Zu!C={x06V;&=Cdz4>?9$n=yXvTq#bYlp0M60mduIVl5&)0}X;had>(8juwYq!T1) z+CuF#7mAC@VObVT(-`HMhy4sm<`bhNBAt$dAPBH52hPTWv-6-2D~$(w6)@_mmV3YQ z-5DDyKi@O3yntwI7`m<=R+rmQ6etHpF{APWf#H7AXx&lr z>0kZwi9Z0qZ-4vStKQ%7;&(;4_1qb?tN;Bw-~QgWPpK>(!xIJWCz8>Mc)SjYL=+@R zLCT3p#535xgMyb|0)>1B{t^{pnm{(qB3P%v?WM+mIoPMc%*})nuXy;@HFJN^*x2~l zY&Pq<;f5O?7<>DH36>QC@G@?^@x}+8Zt_Z5&J`F%o{=yZ>{DQw0_2PjcFu*0@(PFm zdU`oz$m{M_3{_E)6g6`%tF1zg0|F6BHWfpIDA~BNv+Rb<06aU%T zm4j_Ey^MO R%)S5s002ovPDHLkV1f))g;M|k diff --git a/data/images/shared/mrtree-small-left-1.png b/data/images/shared/mrtree-small-left-1.png index f1110088b49486acc342e088a222c4d41bd36dd2..a773cc81ae6dc823eab2a542af47b18de41ff8fa 100644 GIT binary patch literal 9918 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 literal 9776 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

xn!Aflr+cY<3<)vbQ^sW%=TOY=W1P=9dm>dzl+ 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 KMNUMnLSTYr25ag7 diff --git a/data/images/shared/mrtree-small-left-2.png b/data/images/shared/mrtree-small-left-2.png index 618fd916d0ec5a8cba3e7f1ef5c0636f1bef526a..63abe698dfaedb2bee76246eecadf079727501d3 100644 GIT binary patch literal 9606 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 literal 9482 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 literal 5935 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 diff --git a/data/images/shared/mrtree-walk-left-0.png b/data/images/shared/mrtree-walk-left-0.png index f39c65880bd76531d61d86901d262ab19899eaf0..035547c85043a425a1107b82665d8c1601496189 100644 GIT binary patch literal 10253 zcmV+oDDu~dP)o&NJqpPg-5 zZBdtO8CQ%A1{Z8&ifOXpLP;Zma7j!EDct4KbGb9b_emk)l8{^uOn!tU7(xk2FftGz z1`IABSJ{@VZk1h??K?Yt=J&^nwS_I?CPQ+3|67%rd7t_A@AJM3d?5e$(zvhDzj=7` z1%JvF+P&}>eRERCRX3J+W7#~6Ofy}eA9KR zp82=h`m@m4_U1Wr7Av0Wvj|#tn#hd=5j)7-{co$k-F#8iMNfPH>buCA->klT@2=PX zU9-FA&tDo6TK1$dWrl>xDQ59+bcM!nAA{~zNp-ZycW=1)6YCmgHVk|K>R2-;2bvE| zO=bJltJ6(IG&0Kijicfy%>3RITkq9u|AvRNxaYc=HptfBL@{M;1T&e-_kd zc0Kb?>z{h+JHzSM=Uw;DC6w1EffqRB^C~i_+_)M#o+Jl2?AlE1T*|w8_PL9Gz52G* z1OI10ZPJ>`e*Cq2t~c!1kEYC4r1LJ~;PwijC<>aYBb~~@vaFN3ww7bi+ex8wuhYTP z)%PvFQS>z0~m4RiFRr>_zk?*WOwN zw~GS+IF5r%T0U+cdZL=U>3IuAuHyU48?OD?s;gHG{=0pNT86F}6Sf#rr_gtR!9ZtVu)6NNHUIJHHNW_~2DM3Ra{unXA6iQDgAdoA zHxR0=7eUhn@1ko4QpxN|`_B`Vq4(R z^V(Wo8vfXf2T)-JYUomf>?hV`FtKUO=F~= zMRK6-$@z<~{?yk#{- z>68sBdwJ@kpZ@HX%c_@cd(SJXkAL}NA7MPbzqoLv?GiWw$8n(K)uUEOra?f_?>(qA z3kWV2#NnZ78aNi{iyja*J=f}uOe*Q_bg9+a@&&^u6yXd#;ZSN@IJ3+ z)Qoaa6>a>q#-QQxbC9!2(Jc@{&S=gG0Gdo==avi@PDM|f1~WU&Vp#FfeY;*mBpjR7 zxUX?)!_0;QXVj-YvH6LLmp5!^Dy`j9cHvci5JKKHWs-9Yh|OX0bQet1gr;erC_z;H}rvQ1U5weKb@R;8+n}p9F7-gJ6XK_5|=$WrFyi3{6*ISr*DG z!f=;3qlE^6oaW&PF!1^a-gqGekB5cm63}~a05oNS&@5<*Lte?kV_*bhJUFw3bM1-nn?+WPK5(t{GY!0GJfUfJXZ5_d&AEgy1 zo*L7vQ*;%C5NL`8cS+$omO-FobVU0S$fXKyhT#ch#R0>b5Zw%HTLqxPD*zNl!?G+; zv;$8lx}d#v*HQp~J|ihr)W@#(LLclwX>yjv!#R7;^qau_tlAe&(!2m&hWMVx;Ti+q}$RbBD%;CF8QkG2U1 z_2$pt{OE9W_|Ka+ZoDs2mqGPZ4{Y=Or-7zM5F64!C_r=rf*TM-9+F!?e|HY?;WTtz zhh^HJcn86V2aXL4bZBsSXoM?)Y zv`zw!nf&kHwdo$Po3B_ouZv%W-5Z@faM+d%7)IP82uM#1XRi6e0|Om z269e4=7>a<3HrOT7#T2;h&nLz!q1f~0n4&rSi0C`dGl`yVlNHgiUkV{LccJdWN9s8uIx(ajTl`H*Q_g0#-tEdGT7E`W9EDhOw+y!@vaW*HV{h%^TF zrm%I7jvzFBRtCp$;5Zh# zeu!9>OA-vl5>VAczIN+hawyYSusQ)1reGTmI$w}6&~IXR5C{zBP~l<0OurnrT=zxr zgh!f5qG|mTm=P54`qm+I?@eI_Poc!cfM+O(IUThg240>XZTkTpjz+++ad23NJM0B7 zjqeha41?ZI4awmQOgoRfA|szOKpX*r3&nx~7K`4VL6?a7f%WUwZ$9msdhWux%MH5^ z`J{rDw`{m19)@m?0W>-=1Wg+|oAP@p%&&4mWJ4J23}f3y6*aX}5RNN|ZtBPMG694T z^dIboio(li-LV%wkp{~UTvjIF$`Fs5l0zC8bTcRwkMvL;o+od3ra-nrMkV!jGRTZ)5Acp%5*ml9rA}*;)|C}NS-=4G;mc}wS+%D zl!YoAn17iMhG7(gQY<=r)W8)_TgV4&)Xh8_E5G!wXl-sr*QQNy_Y7ldfCEhkKmaz^ zizE^la0ambg{Lt|IysJp< zcIR;MRbjljF#}bjA&3RWc4>si$dC$(b`Y%4amk8O_yYlS?4<#VhpMWGCq`gf4l05d zCpufBS5Ame{psUBSE{RK?0Ed)Cto@LBG*+Vp+eA6vIN7!3tFi0O6c-3co+(K4}+N(3!ouj{kneC)cX+#@fhqkA!iNL zOeq9fhI3F}OM|05c=Oc^6q&$Z>Oj>jxVf3Br|*CMjtMdU#gWB-1@PC3=@nlvEf%fY z9Yp&SlvP{D)%mXybi}v(1|AUjUDUXydpg9j9JI?OymJDDd!J?DO0fzr{b>LdHExW= zRFs5{%v@?`Sg5Rzpmk>+Z@m_UWmzaI9XW0EpK(`r@9*xd-Mw|7R?C(33-q-9XaBgV zzv&enlcsWB{wQGew-q#1D?ru{>`~INgffbwmzH1 zGSQ8I#NbRwG;6?g07~KzT3Lf&)yXjpRhgi#djv{qvhtCezkkERz`|250FHZJ+|$~# zJg4rPTRDk+UoiXH^ER@x!bnY$;BxDy)O-mBgRn@O_|)$n`ND+O{)bEBzDCz0zkc|R z^OkVPB(-r$zB5Zgo9k!5*uatoH(dS`@17rCS$Ts%An3x#$XhF#w5Ayo=2ORXp=fJ1 z*#4hf*g(+JF?7^`H8Nx(s59{J6T_X%8#a=<1(BxkN>WEsV88}wR83(|_*$vKLPf~Z zE>H{sA%La;X}J&a9-sQs(;HuyuqJi!AyTpLENu|bx+^xm=~F;fwNUOkw8(^PqC25u zPK9{NnjEu%RM-Q1FpKi}6f!6_7L_orLnKcfoU9Ch?29>w#P!GER1BW7dsgSQHn-dwb1Nwq4bJc zG^b6-x`mhH2D*Z7s1;tcq)fEi6f)fL1vZ9bFz?C`hUuhoC5hv5=xD;Mi*1bn_2i=-+q#6GnOt!lkn>Yuwl9ols59q%uk_WAEDhyo3*b z^3L)%?U63pbXSrzjxbfYG;I^@Nr3-+DV<~9oJ`;VNIYfJ9 zOg_tlx*0SW&PFz^jCmQEq=r;_3sv<#eEMr=!Lbx5ic;J%Nf8>!xC6J>gl*ek7zXjd!mUa|7C4>- zMNv={4K7K9X_{!=p2LopqBsXEg8Y&1x(zfIe!c>RS!l-CHb7AYbGA%?6#&N~uq}eW ztxsU-6+vh^aA0=^sf3Ot%Y%p~Mv7Y6->oAP=h54l$8{h6!u|j8={2{XF8_Z*N#()z zgWKw-FVYq)S;kkEJ&!AIGGW;oqP;2{o4`|Qf)E0Va^P4tY`dUNrfI@giXs??v#)bm z88l5JT*YC=MFN^PyL(XbwZ>GSoJVs(F z7F`^MRAN8^u<&vbu^um8ULONtWlWmJBAc-R!-s1==|^X?jkf)JRyJu()93N?TFz+l z|F%4{>zPRh4s7|W=KQUm6J_|r6b8C;h*WqXNCY0Q3q#Q?)EtFC8HHTR2Jga{dC}W5 zEsAy^rwIsg;0rS7ZO@^%D-LtJg;{QK%zd}BEH3?Y0EwuBSS$t*2Q*!vMYiuXaP~Qq zQ8#P+&BSt!!NL6+_U;@)Fd|~nWu@RH3${hj+04OK<{Zc+jlWHsJsUhP-MQ-ORoWQ? z6#%aP;{1JqvdoOcNFL`c7C{jjp-K_{fD1HD(6Kj-Kp6wcE#P2#2BM2Al5_!JsINe7 zD2<39w1a`3G&q(oY|O}JvHQh5E_J)W9X9;iVL6!I;72->8Z+57GzSBn9B%qTJ>ny& zch65%3WO@07jXDX?ra8B-9*cdG|s=^!)vOlrrtf{+!@}Moh`~|ulno>m1`%JR9cWs zpL^=e(yC-hFkFbl9zTnG&cNV76~Rgxgep+Ob7>QbtQCfU93ZC&MurnmH5K`s4p=;R zfk!%>0!33$^A>y|2_tPPLasyJDXD4Dy!sKdsa(=Vc~upv>$!KG`T_+y<~ygXgaQI( zK)8~E`ULk!TXFO$wG=Qwqm+kV#vJZP&p|Hu#8)a5W3c;j!U? z5lC?0^0TmY;PBF>(#4`bXM!IE+B6(=?9*WxJQiHWLq1IhGn54Us5&c#_U6Yv;}6?N zbk>#++wHSL)Reua)%+XxHF9z~{hwVuyDsv2$~W(Rv$aRo-u%T#d>EmsJR(&L49!8; zE)7+A9+SAjpzigYiJAo^$n1{;!wz~l1{Yi!Mmm>*u_upen?+AvLmy>e`Vv1zUX*c8 zNGND#-a`1W=1f+zu-~Jh>RcWaMZq*Iys|!zYd&%|<}T$CA5Nc^&BdxraIkBz=wdos zOq2!A|HX{+U0=Rq`IF9RG5@t2){>sC)~~$s%G$dXJB?2&m>5EL$0brs?BOBu+RT;($z2FXpozA3@vxq50R| zcvJhs_dd7#v{Y2dWc;gdZF~Gj7hOvsP|jlVEC;NAC(fSFfah$u-9Z?N1i!|h(lfS# z3A!0f4GG1jbVy<`Gb~^V&!EoD;q5pSkQgiv@t9L4j{o*)g23om0*>P#)@z_@asZNt zE%s7RONWV~P*PF?p64M+5?GFcp&BZrwPr;FQdN^#iNL7dHI<$BVAStg6X06xo8VSx^)Ogd%WYBRND+ zT|c_dpLU_MB@KxtNSY23B`&CG4Y8aFjwVoT2Rl**RLg-x5io}}Sn`IAq-sHAC>(1W zYd&G4vtL7I*aFRZvE+&{G);%5>P0QH%+Xo?$%4u;AdHDve-bX28=}M^o0TC54tm;1 zVCfYro@jct$v!DfO{e3J&7RM!tg0tS#T7UQwAe6IuDFMlMC-<>bV5fS5~64 z&I9R+T3qzS3K-{AfL>C9CYGSt%j5i8C&7DJ8Q!uS4CW3c>Mc1F`+UIGUKxftek|~D zG#0TE_&$QmZ-{_qC@8X41k@#oNW{!>33JEPE}90zFfa`ZygS zf#@AtQFB)4J11rSs~=c>c4zksSI=2Y(6KjJnD-APiq6}%97GSApa>1qIkbTKbATmR zxd0}Cu9ghuUCAO+Md9jO%dq^TB?y%1m@?qP)fpRndyiB)SCT6&Z`~G>!M*S>5db2=VZyu#nOCb0(NPY*lkDxWGV=!Z3Ky}b*Ip_lr zl}$LBjjptbWuL2o$HyR(&KFZ3mSxc2sUV-v10e7QPMrU$>jrw;Wei7U7={7SB|`Gj z1tOc}f~w_VItjXCU%c|a?*G|e9{J@X%5j7Gp_@M>Kl(r0b_%ZA1%w-Mal%-WbZo#f zc+5QiP#$UM033i97`RJl05&L^fFEZs6T{i4n#N)35-%$1X~gOn~zL2#ys zvU4QV%=V(@Y%eA)@MBNEiG)RP#fK}Amo=#Bp%-l!CWd2Kn5GGuq2VtTPRgfpJcD#H z4^>s+cDv#7P@`=}K+n@~h=O!FUe~rO*Sr0-U2h(@`9HAcfvwrW>@VvkRcGApBa1Fi z2@S(aqv@4Hi!wC=%OH?EVlfFn7U@aNTWHuq^AS=d`@7wG7Rke4yeTRMh%V7AY?tR%TlSsUg6!ED%CaR%60M_q~n# z8t0!-p!(_KKmAxnZLX}Y3qx|bkNAD0ia}TFa4}Hvk^^0HpzAua$->Q#bs_~t7k+PB z1hOn2>L%rL002!_q3J3pib6iefMz&cdQBxt%UNX8<8OC>b!r6w>%bpz;13rp zFxn$yaBvVgISbozkWJD^BnP>N?tAFk69ToZW!s(8W{DibPzXjm#kY3o#suY)2=?z% zMi&DlhO^k)6or-tQo{~BJ`oHvetUrs0#gSR^^n#%jswRjkj&oJyxtB4a~FhAS#QDA z?IRe6hhMM6AAq;?^i6hkg+R_1_T^dz!GT>OeBw1d109}^mz0+L;kZFvyJ79c(UC3Z zPd#jIP29BQM5JC_LdXY_;$K0e5 z>(@{cDnacux^M`NGJfNvvL=8~6?0m5B}zUA6_pYAyneVu3Bi(Dm^#z=)(dYv_Q0A4 z_8b?e_dk2TZ};|%&s=y}2}9E~SPme>0XYi(gaRt-DD2prE##42KSJSPQRC973^JJv zO#MheHBCb*nMF369Y4*n9P}OlE?yZP!;u{~$cPiOBhg*rAW|*DS0W);+kxoFpKiPU zj^#Z+dGsgOHEB)kyMnrN+m_oi`bPKUv)xb>W%K|8z;>*0`jaS@j)8IlUl5SI40xW0 z*Xu=Dr4J1698tA!9EWJPI<6@f#y2|VUN8wIC2Wx+`(6n}I|!5$gu(&TPP6DhO>@nL z^}qdN%hvs?o3tiwTu@g(usYH^u>IzFiz8qd3h6;o08Jy%^&_b@jt#W#)G>E~Z){}u zIG8l81QnA+aH0e19j{Jw2`~){>Gb&RI@544*h^#SwSK6oe#TD-8czb{1f_u>f)N@s z7YyPrfBgBk`}Q6D%DAA~B)hh|jvl2A&4H$fxNwCBilV)5 zwDeHg29!i-ghLVV0)vwBE~@d_`~PFztLv^Vg4(1tvC*LepD!uPcuULaF%4fOXBR&c zLJ$Z9;1VU|(iRwALe*K;G3sfI8LrtDK`u!!7>yUMNsey%{hcPxKDQbi1LSk>FD*UX z$ND1<%F4oU`&e-PeXigB`kpVY_|%F5`(e*(dp=AHL;qY==R!UMa3X=6Dn#EzGCn4U zOe7L84F|Dag2fk?1JDoCFoNW;R;+Gp%K^u;ux%T3odpc zla@~lZIci(VduDdN*z#42!xElm=SGAbeBKP0KlIf|Lx2`Z35X0gKQ=TilT~1_|YE5 zu>oBNRDJB2cc@=RHaBY4qgqIE1dfg3=XVwnNBGiy2bW$GfoWK$&6lzq1KV;y(FBqt zA}?#O9Sa=C6;6X2EqPBCc**A=Vh7{x`l;~sL zgk}gt(MHSeB$C4nM9~dZHjvJw!1FxZ9ye&3KE%p9%yk~?!Sh)QmgdEx<)wwA>obMT z52rkLVpzC6A_Rd4hy#|RLGdgYy3lN=X$HEc!%!)lOxwV=3s$OW21@kyzVXbpd7F;Q|EU(dWWgU|i>IUYb;!uc*wBod01xQOq1jBjlCT<48y`3FAl=(Bk+0sm^_0&Wxwh;4x(Ku2Kt8&`TJ1- z3g>6b5Cs?fL2lfa^0rGnn#gGfnWT>H{WJ!<%MXEi*$oSS?+JD-n>NRTR5A;ibwDf^ z+-?taLmzixv0ek=vLNcFA6drIEa368pdpRvM2+k8y?{kjRMycLifUMPO%*uKIW6O7 zLTIqGh?1ZOBJYMEi12v4;5i-~$0HDu@b(UQLkILcf#hZ3_PD@s6h@*t;xV99M z5Xfm4Ji?;*obwi4!T`V*@A|^hT|1v#vho%$QmG7-Y~frIf26Q~Ka*vS8nJc7K{h4e z;uS(MSRJjI;n>IP@M%b+H9yCCP*Ejdc<}TbZz6geh#soYN%tLYq$~h-bl_OX5(ax^ z$oU+Em;gzVkj-X`<+Ney5O@(B&%z%*6r|glZF}Ci(|_~j>%P8?0RVTY_J^=LTN)p5 zA(Kg=vd)iOvJkbi*`ud5hUQ@RHXRMu)dJ9wi~&Q#F?c*4genR|wbYOe-bEpmPM%O? zRdP0}>jKc!(?^mxR=Op*U2uCup`=@p=A3I&>VChU+gnx^5Bcq9@DwC;+-FplrA zT*q;cNtg&$(oi)Ef+!vsGoxDbi+lFWoVn=9_<;S`rkD0!0kaC4p5m)&eNYtTs5fC) zf~v_Z2+=S+ltCsbqivrCRkk3y9bl9-V%zo!pGi#!(A$;9!Hy&b`yB+s0tD&Q0Za~X zXxU-F=VQSNHf)PP*KKfof&Q7#zx$Ux6iCg5vte{Y2TT!gXjIo+Skd_C#`}w!dc%Sn zid$Tow5F*~t^LD`Lg3{K-5$GG_aE(Mgc|jOqja2#NkD(Mg{D_j%w8ZubW*hU_^Xgg%19(qh*Y>yUgbtrog0C28$&S*j+ST~x1YM@xi=PdbZofryd{2Q(lUa< zASO>OgUfY%bDzpO4s#ZX*#5c@`D-c zUR^i;KwHyY1O1zq)J?bDkvbjEJ(eG;pLF@3t81qJX5m!}_s`?!y=(gMbko!4)|_3_ zIgg*0J_6Jq{PqWDw|6|h=la_iG`|%CMQD^(P#8R@7Xdw9o|0n&J#8BHyx}qzT^6BD zOF>21@^w8Ot**}QE!VItkGi@#(5!{L-U-uikj(%bBOnxL?2m%=a%B$peKN5t^Z$e2DY$8jjHEG_hgM_Y}3ZIacoD|Ex}?|t&m z0C3;a_pR8z?S=1Ta_tw)n0f8JYd-(OZ=6zDJZ2W-;eROW?HCyjA(2RcqA19+fK(!f zeLDz(-uY1NZSaN+s96Fz%c5$s2~P<<2Fz%$0isWZl_~q>M{b_|pR;Dqx>=TG@2$&k zz5DR{y@v6=F)j+=Z)musVU5pEH7LsH*MN@l0tgsH4{9Ke1U>JED0)y)9f6Vr4t9vh z<@><9^6>hBY*s`24j)`%W&4YN+1)s6?m2HKN5z!nOV@qrSpd%#KJ7pCUc=ZXp$^*w zw*EB>&Sit;9Sp~E&{NZuS<`3TxxAs_)t8?uTU1sV`eZm<*1PWiJ^4SG81*F3TtiVn zV#G#nq)c9P(aM+OgT~h`z4_7u4Ko`Y06#sMhZ6(=*`x*2aiC@iXoPXFLvH2RP~WvT z-iY?r*6;1_>W}f|5!6;ME@)x<>p0k&#$_v4+|kj}KFcL~Fft_n%Z*?BOybvTZW!p< z-8bRA)-mS)%&uoHyyK=j%2r;#^5YrjsT&CdNc077En0fbzu&t2*5{9Qfv2`U^`B2a zvG(>{HjC=&83!tBrmXqqE#Lg>>3F!Owm$WZKmYN6|0NXR(bJLs$)qU@J1@ENlAR4R z8#?j#CJ#RM;3c2^?ls1@e)+HWHfc>&_&`pN-1F!?rE52=r9VL81NlJyQRV*wpy1EN Tc;Gs300000NkvXXu0mjf+HZfc literal 10207 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 Z`Twv;{s>ka!V3TZ002ovPDHLkV1k2yVx9m1 diff --git a/data/images/shared/mrtree-walk-left-1.png b/data/images/shared/mrtree-walk-left-1.png index d1bde106a0fe874a50ec2a76f712b239e0ced5c6..d814da55869efab8cf08d9f6d03f8f648d516cf1 100644 GIT binary patch literal 10352 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 O0000v19o_+cKal?g>fPe|o8Q;H>%gs68dCvQN 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 diff --git a/data/images/shared/mrtree-walk-left-2.png b/data/images/shared/mrtree-walk-left-2.png index 6abbe659356c63a46fdce7bd2cc8813fa40a0f34..e762155056abca474a428e8e0cb78060d79c7878 100644 GIT binary patch literal 9987 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 N002ovPDHLkV1mPV*S`P& literal 9863 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 diff --git a/data/levels/test/bonusblock.stl b/data/levels/test/bonusblock.stl index d392e932b..7db74262c 100644 --- a/data/levels/test/bonusblock.stl +++ b/data/levels/test/bonusblock.stl @@ -9,7 +9,7 @@ (gravity 10.000000) (background (image "arctis.jpg") (speed 0.5)) - (music "supertux-2.ogg") + (music "theme.mod") (spawnpoint (name "main") (x 100) diff --git a/lib/gui/menu.cpp b/lib/gui/menu.cpp index ba7b749a6..57643efdd 100644 --- a/lib/gui/menu.cpp +++ b/lib/gui/menu.cpp @@ -746,7 +746,7 @@ Menu::event(SDL_Event& event) { case SDL_KEYDOWN: { - SDLKey key = key = event.key.keysym.sym; + SDLKey key = event.key.keysym.sym; SDLMod keymod; char ch[2]; keymod = SDL_GetModState(); diff --git a/lib/special/game_object.h b/lib/special/game_object.h index b235c01ac..a657d4984 100644 --- a/lib/special/game_object.h +++ b/lib/special/game_object.h @@ -80,8 +80,7 @@ namespace SuperTux remove_listeners = entry; } - - + // flags enum { /// the tile so you can stand on it diff --git a/lib/special/moving_object.h b/lib/special/moving_object.h index b5c476496..0ac684421 100644 --- a/lib/special/moving_object.h +++ b/lib/special/moving_object.h @@ -25,6 +25,7 @@ #include "math/vector.h" #include "math/rectangle.h" +class FlipLevelTransformer; class Sector; class CollisionGrid; @@ -65,6 +66,7 @@ namespace SuperTux protected: friend class Sector; friend class CollisionGrid; + friend class FlipLevelTransformer; /** The bounding box of the object (as used for collision detection, this * isn't necessarily the bounding box for graphics) diff --git a/lib/special/object_remove_listener.h b/lib/special/object_remove_listener.h index fdcb769bc..502db1ce5 100644 --- a/lib/special/object_remove_listener.h +++ b/lib/special/object_remove_listener.h @@ -9,6 +9,9 @@ class GameObject; class ObjectRemoveListener { public: + virtual ~ObjectRemoveListener() + { } + virtual void object_removed(GameObject* object) = 0; }; diff --git a/lib/utils/configfile.h b/lib/utils/configfile.h index 5c2de647c..90862da82 100644 --- a/lib/utils/configfile.h +++ b/lib/utils/configfile.h @@ -27,7 +27,9 @@ namespace SuperTux { FILE * opendata(const std::string& filename, const char * mode); class Config { - public: +public: + virtual ~Config() + { } void load (); void save (); virtual void customload(const lisp::Lisp* ) diff --git a/src/badguy/badguy.h b/src/badguy/badguy.h index c7c24e101..92bd0104e 100644 --- a/src/badguy/badguy.h +++ b/src/badguy/badguy.h @@ -11,6 +11,7 @@ #include "serializable.h" #include "resources.h" #include "sector.h" +#include "direction.h" #include "object_factory.h" #include "lisp/parser.h" #include "lisp/lisp.h" @@ -35,6 +36,15 @@ public: virtual void kill_fall(); + Vector get_start_position() const + { + return start_position; + } + void set_start_position(const Vector& vec) + { + start_position = vec; + } + protected: enum State { STATE_INIT, diff --git a/src/badguy/mrtree.cpp b/src/badguy/mrtree.cpp index efe080de9..d94471790 100644 --- a/src/badguy/mrtree.cpp +++ b/src/badguy/mrtree.cpp @@ -3,6 +3,7 @@ #include "mrtree.h" static const float WALKSPEED = 50; +static const float WALKSPEED_SMALL = 30; MrTree::MrTree(const lisp::Lisp& reader) : mystate(STATE_BIG) @@ -27,11 +28,13 @@ MrTree::write(lisp::Writer& writer) void MrTree::activate() { - physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED); - if(mystate == STATE_BIG) + if(mystate == STATE_BIG) { + physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED); sprite->set_action(dir == LEFT ? "left" : "right"); - else + } else { + physic.set_velocity_x(dir == LEFT ? -WALKSPEED_SMALL : WALKSPEED_SMALL); sprite->set_action(dir == LEFT ? "small-left" : "small-right"); + } } bool diff --git a/src/collision_grid.cpp b/src/collision_grid.cpp index c3b27698b..311fc78af 100644 --- a/src/collision_grid.cpp +++ b/src/collision_grid.cpp @@ -78,7 +78,14 @@ CollisionGrid::remove_object(MovingObject* object) break; } } +#ifdef DEBUG assert(wrapper != 0); +#else + if(wrapper == 0) { + std::cerr << "Tried to remove nonexistant object!\n"; + return; + } +#endif const Rectangle& bbox = wrapper->dest; for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) { @@ -90,7 +97,7 @@ CollisionGrid::remove_object(MovingObject* object) std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n"; continue; } - remove_object_from_gridcell(gridy*cells_x + gridx, object); + remove_object_from_gridcell(gridy*cells_x + gridx, wrapper); } } @@ -98,11 +105,13 @@ CollisionGrid::remove_object(MovingObject* object) } void -CollisionGrid::move_object(MovingObject* object) +CollisionGrid::move_object(ObjectWrapper* wrapper) { - const Rectangle& bbox = object->bbox; - for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) { - for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) { + // FIXME not optimal yet... should leave the gridcells untouched that don't + // need to be changed. + const Rectangle& obbox = wrapper->dest; + for(float y = obbox.p1.y; y < obbox.p2.y; y += cell_height) { + for(float x = obbox.p1.x; x < obbox.p2.x; x += cell_width) { int gridx = int(x / cell_width); int gridy = int(y / cell_height); if(gridx < 0 || gridy < 0 @@ -110,14 +119,36 @@ CollisionGrid::move_object(MovingObject* object) std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n"; continue; } - // TODO + remove_object_from_gridcell(gridy*cells_x + gridx, wrapper); } } + + const Rectangle& nbbox = wrapper->object->bbox; + for(float y = nbbox.p1.y; y < nbbox.p2.y; y += cell_height) { + for(float x = nbbox.p1.x; x < nbbox.p2.x; x += cell_width) { + int gridx = int(x / cell_width); + int gridy = int(y / cell_height); + if(gridx < 0 || gridy < 0 + || gridx >= int(cells_x) || gridy >= int(cells_y)) { + std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n"; + continue; + } + + GridEntry* entry = new GridEntry; + entry->object_wrapper = wrapper; + entry->next = grid[gridy*cells_x + gridx]; + grid[gridy*cells_x + gridx] = entry; + } + } + + wrapper->dest = nbbox; } void CollisionGrid::check_collisions() { + std::vector moved_objects; + CollisionGridIterator iter(*this, Sector::current()->get_active_region()); while(ObjectWrapper* wrapper = iter.next_wrapper()) { MovingObject* object = wrapper->object; @@ -126,6 +157,7 @@ CollisionGrid::check_collisions() if(object->get_flags() & GameObject::FLAG_NO_COLLDET) { object->bbox.move(object->movement); object->movement = Vector(0, 0); + moved_objects.push_back(wrapper); continue; } @@ -134,8 +166,16 @@ CollisionGrid::check_collisions() collide_object(wrapper); - object->bbox.move(object->get_movement()); - object->movement = Vector(0, 0); + if(object->movement != Vector(0, 0)) { + object->bbox.move(object->movement); + object->movement = Vector(0, 0); + moved_objects.push_back(wrapper); + } + } + + for(std::vector::iterator i = moved_objects.begin(); + i != moved_objects.end(); ++i) { + move_object(*i); } } @@ -145,13 +185,13 @@ CollisionGrid::collide_object(ObjectWrapper* wrapper) iterator_timestamp++; const Rectangle& bbox = wrapper->object->bbox; - for(float y = bbox.p1.y; y < bbox.p2.y; y += cell_height) { - for(float x = bbox.p1.x; x < bbox.p2.x; x += cell_width) { + for(float y = bbox.p1.y - cell_height; y < bbox.p2.y + cell_height; y += cell_height) { + for(float x = bbox.p1.x - cell_width; x < bbox.p2.x + cell_width; x += cell_width) { int gridx = int(x / cell_width); int gridy = int(y / cell_height); if(gridx < 0 || gridy < 0 || gridx >= int(cells_x) || gridy >= int(cells_y)) { - std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n"; + //std::cerr << "Object out of range: " << gridx << ", " << gridy << "\n"; continue; } @@ -209,13 +249,13 @@ CollisionGrid::collide_object_object(ObjectWrapper* wrapper, } void -CollisionGrid::remove_object_from_gridcell(int gridcell, MovingObject* object) +CollisionGrid::remove_object_from_gridcell(int gridcell, ObjectWrapper* wrapper) { GridEntry* lastentry = 0; GridEntry* entry = grid[gridcell]; while(entry) { - if(entry->object_wrapper->object == object) { + if(entry->object_wrapper == wrapper) { if(lastentry == 0) { grid[gridcell] = entry->next; } else { diff --git a/src/collision_grid.h b/src/collision_grid.h index 8a507d142..7822c31e3 100644 --- a/src/collision_grid.h +++ b/src/collision_grid.h @@ -20,7 +20,6 @@ public: void add_object(MovingObject* object); void remove_object(MovingObject* object); - void move_object(MovingObject* object); void check_collisions(); @@ -47,9 +46,10 @@ private: ObjectWrapper* object_wrapper; }; - void remove_object_from_gridcell(int gridcell, MovingObject* object); + void remove_object_from_gridcell(int gridcell, ObjectWrapper* wrapper); void collide_object(ObjectWrapper* wrapper); void collide_object_object(ObjectWrapper* wrapper, ObjectWrapper* wrapper2); + void move_object(ObjectWrapper* wrapper); typedef std::vector GridEntries; GridEntries grid; diff --git a/src/collision_grid_iterator.h b/src/collision_grid_iterator.h index 9e22f629f..efed0b4ef 100644 --- a/src/collision_grid_iterator.h +++ b/src/collision_grid_iterator.h @@ -13,20 +13,20 @@ public: CollisionGridIterator(CollisionGrid& newgrid, const Rectangle& bbox) : grid(newgrid) { - start_x = int(bbox.p1.x / grid.cell_width); + start_x = int(bbox.p1.x / grid.cell_width) - 2; if(start_x < 0) start_x = 0; x = start_x; - y = int(bbox.p1.y / grid.cell_height); + y = int(bbox.p1.y / grid.cell_height) - 2; if(y < 0) y = 0; - end_x = int(bbox.p2.x / grid.cell_width) + 1; + end_x = int(bbox.p2.x / grid.cell_width) + 2; if(end_x > (int) grid.cells_x) end_x = grid.cells_x; - end_y = int(bbox.p2.y / grid.cell_height) + 1; + end_y = int(bbox.p2.y / grid.cell_height) + 2; if(end_y > (int) grid.cells_y) end_y = grid.cells_y; diff --git a/src/defines.h b/src/defines.h deleted file mode 100644 index c408b688a..000000000 --- a/src/defines.h +++ /dev/null @@ -1,50 +0,0 @@ -// $Id$ -// -// SuperTux -// Copyright (C) 2000 Bill Kendrick -// Copyright (C) 2004 Tobias Glaesser -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA -// 02111-1307, USA. -#ifndef SUPERTUX_DEFINES_H -#define SUPERTUX_DEFINES_H - -#include - -enum Direction { LEFT = 0, RIGHT = 1 }; - -/* keyboard/joystick states: */ -#define UP 0 -#define DOWN 1 - -/* Dying types: */ -enum DyingType { - DYING_NOT = 0, - DYING_SQUISHED = 1, - DYING_FALLING = 2 -}; - -/* Speed constraints: */ -#define MAX_LIVES 99 - -/* gameplay related defines */ -#define START_LIVES 4 - -#define MAX_FIRE_BULLETS 2 -#define MAX_ICE_BULLETS 1 -#define FROZEN_TIME 3.0 - -#endif /*SUPERTUX_DEFINES_H*/ - diff --git a/src/intro.h b/src/direction.h similarity index 71% rename from src/intro.h rename to src/direction.h index 87b34e22f..5ecc417bc 100644 --- a/src/intro.h +++ b/src/direction.h @@ -1,7 +1,8 @@ // $Id$ -// -// SuperTux - A Jump'n Run +// +// SuperTux // Copyright (C) 2000 Bill Kendrick +// Copyright (C) 2004 Tobias Glaesser // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -12,18 +13,15 @@ // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef SUPERTUX_INTRO_H -#define SUPERTUX_INTRO_H +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +#ifndef SUPERTUX_DIRECTION_H +#define SUPERTUX_DIRECTION_H -void draw_intro(); +enum Direction { LEFT = 0, RIGHT = 1 }; -#endif /*SUPERTUX_INTRO_H*/ +#endif /*SUPERTUX_DEFINES_H*/ -/* Local Variables: */ -/* mode:c++ */ -/* End: */ diff --git a/src/flip_level_transformer.cpp b/src/flip_level_transformer.cpp new file mode 100644 index 000000000..db5a693a4 --- /dev/null +++ b/src/flip_level_transformer.cpp @@ -0,0 +1,78 @@ +#include + +#include "flip_level_transformer.h" +#include "object/tilemap.h" +#include "badguy/badguy.h" +#include "sector.h" +#include "tile_manager.h" + +void +FlipLevelTransformer::transform_sector(Sector* sector) +{ + float height = sector->solids->get_height() + * sector->solids->get_tilemanager()->get_default_height(); + + for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); + i != sector->gameobjects.end(); ++i) { + GameObject* object = *i; + + TileMap* tilemap = dynamic_cast (object); + if(tilemap) { + transform_tilemap(tilemap); + } + BadGuy* badguy = dynamic_cast (object); + if(badguy) { + transform_badguy(height, badguy); + } else { + MovingObject* mobject = dynamic_cast (object); + if(mobject) { + transform_moving_object(height, mobject); + } + } + } + for(Sector::SpawnPoints::iterator i = sector->spawnpoints.begin(); + i != sector->spawnpoints.end(); ++i) { + transform_spawnpoint(height, *i); + } +} + +void +FlipLevelTransformer::transform_tilemap(TileMap* tilemap) +{ + for(size_t x = 0; x < tilemap->get_width(); ++x) { + for(size_t y = 0; y < tilemap->get_height()/2; ++y) { + // swap tiles + int y2 = tilemap->get_height()-1-y; + const Tile* t1 = tilemap->get_tile(x, y); + const Tile* t2 = tilemap->get_tile(x, y2); + tilemap->change(x, y, t2->getID()); + tilemap->change(x, y2, t1->getID()); + } + } + tilemap->set_drawing_effect(VERTICAL_FLIP); +} + +void +FlipLevelTransformer::transform_badguy(float height, BadGuy* badguy) +{ + Vector pos = badguy->get_start_position(); + pos.y = height - pos.y; + badguy->set_start_position(pos); +} + +void +FlipLevelTransformer::transform_spawnpoint(float height, SpawnPoint* spawn) +{ + Vector pos = spawn->pos; + pos.y = height - pos.y; + spawn->pos = pos; +} + +void +FlipLevelTransformer::transform_moving_object(float height, MovingObject*object) +{ + Vector pos = object->bbox.p1; + pos.y = height - pos.y; + object->bbox.set_pos(pos); +} + diff --git a/src/flip_level_transformer.h b/src/flip_level_transformer.h new file mode 100644 index 000000000..f3cc8422f --- /dev/null +++ b/src/flip_level_transformer.h @@ -0,0 +1,29 @@ +#ifndef __FLIP_LEVEL_TRANSFORMER_H__ +#define __FLIP_LEVEL_TRANSFORMER_H__ + +#include "level_transformer.h" + +class TileMap; +class BadGuy; +class SpawnPoint; +namespace SuperTux { +class MovingObject; +} + +using namespace SuperTux; + +/** Vertically or horizontally flip a level */ +class FlipLevelTransformer : public LevelTransformer +{ +public: + virtual void transform_sector(Sector* sector); + +private: + void transform_tilemap(TileMap* tilemap); + void transform_moving_object(float height, MovingObject* object); + void transform_badguy(float height, BadGuy* badguy); + void transform_spawnpoint(float height, SpawnPoint* spawnpoint); +}; + +#endif + diff --git a/src/gameloop.cpp b/src/gameloop.cpp index 275c42fa6..3a33cdbbc 100644 --- a/src/gameloop.cpp +++ b/src/gameloop.cpp @@ -32,14 +32,13 @@ #include #include -#include "SDL.h" +#include #ifndef WIN32 #include #include #endif -#include "defines.h" #include "app/globals.h" #include "gameloop.h" #include "video/screen.h" @@ -47,8 +46,8 @@ #include "gui/menu.h" #include "sector.h" #include "level.h" -#include "scene.h" #include "tile.h" +#include "player_status.h" #include "object/particlesystem.h" #include "object/background.h" #include "object/tilemap.h" @@ -59,7 +58,6 @@ #include "resources.h" #include "app/gettext.h" #include "worldmap.h" -#include "intro.h" #include "misc.h" #include "statistics.h" #include "timer.h" @@ -225,32 +223,18 @@ GameSession::start_timers() void GameSession::on_escape_press() { - if(currentsector->player->dying || end_sequence != NO_ENDSEQUENCE) + if(currentsector->player->is_dying() || end_sequence != NO_ENDSEQUENCE) return; // don't let the player open the menu, when he is dying if(game_pause) return; - if(st_gl_mode == ST_GL_TEST) - { - exit_status = ES_LEVEL_ABORT; - } - else if (!Menu::current()) - { - /* Tell Tux that the keys are all down, otherwise - it could have nasty bugs, like going allways to the right - or whatever that key does */ - Player& tux = *(currentsector->player); - tux.key_event((SDLKey)keymap.up, UP); - tux.key_event((SDLKey)keymap.down, UP); - tux.key_event((SDLKey)keymap.left, UP); - tux.key_event((SDLKey)keymap.right, UP); - tux.key_event((SDLKey)keymap.jump, UP); - tux.key_event((SDLKey)keymap.power, UP); - - Menu::set_current(game_menu); - Ticks::pause_start(); - } + if(st_gl_mode == ST_GL_TEST) { + exit_status = ES_LEVEL_ABORT; + } else if (!Menu::current()) { + Menu::set_current(game_menu); + Ticks::pause_start(); + } } void @@ -260,15 +244,15 @@ GameSession::process_events() { Player& tux = *currentsector->player; - tux.input.fire = UP; - tux.input.left = UP; - tux.input.right = DOWN; - tux.input.down = UP; + tux.input.fire = false; + tux.input.left = false; + tux.input.right = true; + tux.input.down = false; if (int(last_x_pos) == int(tux.get_pos().x)) - tux.input.up = DOWN; + tux.input.up = true; else - tux.input.up = UP; + tux.input.up = false; last_x_pos = tux.get_pos().x; @@ -339,7 +323,7 @@ GameSession::process_events() { SDLKey key = event.key.keysym.sym; - if(tux.key_event(key,DOWN)) + if(tux.key_event(key, true)) break; switch(key) @@ -356,7 +340,7 @@ GameSession::process_events() { SDLKey key = event.key.keysym.sym; - if(tux.key_event(key, UP)) + if(tux.key_event(key, false)) break; switch(key) @@ -407,93 +391,7 @@ GameSession::process_events() ch[1] = '\0'; } last_keys.append(ch); // add to cheat keys - - // Cheating words (the goal of this is really for debugging, - // but could be used for some cheating, nothing wrong with that) - if(compare_last(last_keys, "grow")) - { - tux.grow(false); - last_keys.clear(); - } - if(compare_last(last_keys, "fire")) - { - tux.grow(false); - tux.got_power = tux.FIRE_POWER; - last_keys.clear(); - } - if(compare_last(last_keys, "ice")) - { - tux.grow(false); - tux.got_power = tux.ICE_POWER; - last_keys.clear(); - } - if(compare_last(last_keys, "lifeup")) - { - player_status.lives++; - last_keys.clear(); - } - if(compare_last(last_keys, "lifedown")) - { - player_status.lives--; - last_keys.clear(); - } - if(compare_last(last_keys, "grease")) - { - tux.physic.set_velocity_x(tux.physic.get_velocity_x()*3); - last_keys.clear(); - } - if(compare_last(last_keys, "invincible")) - { // be invincle for the rest of the level - tux.invincible_timer.start(10000); - last_keys.clear(); - } - if(compare_last(last_keys, "shrink")) - { // remove powerups - tux.kill(tux.SHRINK); - last_keys.clear(); - } - if(compare_last(last_keys, "kill")) - { // kill Tux, but without losing a life - player_status.lives++; - tux.kill(tux.KILL); - last_keys.clear(); - } - if(compare_last(last_keys, "grid")) - { // toggle debug grid - debug_grid = !debug_grid; - last_keys.clear(); - } - if(compare_last(last_keys, "hover")) - { // toggle hover ability on/off - tux.enable_hover = !tux.enable_hover; - last_keys.clear(); - } - if(compare_last(last_keys, "gotoend")) - { // goes to the end of the level - tux.move(Vector( - (currentsector->solids->get_width()*32) - - (screen->w*2), - 0)); - currentsector->camera->reset( - Vector(tux.get_pos().x, tux.get_pos().y)); - last_keys.clear(); - } - // temporary to help player's choosing a flapping - if(compare_last(last_keys, "marek")) - { - tux.flapping_mode = Player::MAREK_FLAP; - last_keys.clear(); - } - if(compare_last(last_keys, "ricardo")) - { - tux.flapping_mode = Player::RICARDO_FLAP; - last_keys.clear(); - } - if(compare_last(last_keys, "ryan")) - { - tux.flapping_mode = Player::RYAN_FLAP; - last_keys.clear(); - } + handle_cheats(); break; case SDL_JOYAXISMOTION: @@ -501,74 +399,75 @@ GameSession::process_events() { if (event.jaxis.value < -joystick_keymap.dead_zone) { - tux.input.left = DOWN; - tux.input.right = UP; + tux.input.left = true; + tux.input.right = false; } else if (event.jaxis.value > joystick_keymap.dead_zone) { - tux.input.left = UP; - tux.input.right = DOWN; + tux.input.left = false; + tux.input.right = true; } else { - tux.input.left = DOWN; - tux.input.right = DOWN; + tux.input.left = false; + tux.input.right = false; } } else if (event.jaxis.axis == joystick_keymap.y_axis) { if (event.jaxis.value > joystick_keymap.dead_zone) { - tux.input.up = DOWN; - tux.input.down = UP; + tux.input.up = true; + tux.input.down = false; } else if (event.jaxis.value < -joystick_keymap.dead_zone) { - tux.input.up = UP; - tux.input.down = DOWN; + tux.input.up = false; + tux.input.down = true; } else { - tux.input.up = DOWN; - tux.input.down = DOWN; + tux.input.up = false; + tux.input.down = false; } } break; case SDL_JOYHATMOTION: if(event.jhat.value & SDL_HAT_UP) { - tux.input.up = DOWN; - tux.input.down = UP; + tux.input.up = true; + tux.input.down = false; } else if(event.jhat.value & SDL_HAT_DOWN) { - tux.input.up = UP; - tux.input.down = DOWN; + tux.input.up = false; + tux.input.down = true; } else if(event.jhat.value & SDL_HAT_LEFT) { - tux.input.left = DOWN; - tux.input.right = UP; + tux.input.left = true; + tux.input.right = false; } else if(event.jhat.value & SDL_HAT_RIGHT) { - tux.input.left = UP; - tux.input.right = DOWN; + tux.input.left = false; + tux.input.right = true; } else if(event.jhat.value == SDL_HAT_CENTERED) { - tux.input.left = UP; - tux.input.right = UP; - tux.input.up = UP; - tux.input.down = UP; + tux.input.left = false; + tux.input.right = false; + tux.input.up = false; + tux.input.down = false; } break; case SDL_JOYBUTTONDOWN: + // FIXME: I assume we have to set old_jump and stuff here?!? if (event.jbutton.button == joystick_keymap.a_button) - tux.input.jump = DOWN; + tux.input.jump = true; else if (event.jbutton.button == joystick_keymap.b_button) - tux.input.fire = DOWN; + tux.input.fire = true; else if (event.jbutton.button == joystick_keymap.start_button) on_escape_press(); break; case SDL_JOYBUTTONUP: if (event.jbutton.button == joystick_keymap.a_button) - tux.input.jump = UP; + tux.input.jump = false; else if (event.jbutton.button == joystick_keymap.b_button) - tux.input.fire = UP; + tux.input.fire = false; break; default: @@ -580,6 +479,93 @@ GameSession::process_events() } void +GameSession::handle_cheats() +{ + Player& tux = *currentsector->player; + + // Cheating words (the goal of this is really for debugging, + // but could be used for some cheating, nothing wrong with that) + if(compare_last(last_keys, "grow")) { + tux.grow(false); + last_keys.clear(); + } + if(compare_last(last_keys, "fire")) { + tux.grow(false); + tux.got_power = tux.FIRE_POWER; + last_keys.clear(); + } + if(compare_last(last_keys, "ice")) { + tux.grow(false); + tux.got_power = tux.ICE_POWER; + last_keys.clear(); + } + if(compare_last(last_keys, "lifeup")) { + player_status.lives++; + last_keys.clear(); + } + if(compare_last(last_keys, "lifedown")) { + player_status.lives--; + last_keys.clear(); + } + if(compare_last(last_keys, "grease")) { + tux.physic.set_velocity_x(tux.physic.get_velocity_x()*3); + last_keys.clear(); + } + if(compare_last(last_keys, "invincible")) { + // be invincle for the rest of the level + tux.invincible_timer.start(10000); + last_keys.clear(); + } + if(compare_last(last_keys, "shrink")) { + // remove powerups + tux.kill(tux.SHRINK); + last_keys.clear(); + } + if(compare_last(last_keys, "kill")) { + // kill Tux, but without losing a life + player_status.lives++; + tux.kill(tux.KILL); + last_keys.clear(); + } + if(compare_last(last_keys, "grid")) { + // toggle debug grid + debug_grid = !debug_grid; + last_keys.clear(); + } + if(compare_last(last_keys, "hover")) { + // toggle hover ability on/off + tux.enable_hover = !tux.enable_hover; + last_keys.clear(); + } + if(compare_last(last_keys, "gotoend")) { + // goes to the end of the level + tux.move(Vector( + (currentsector->solids->get_width()*32) - (screen->w*2), 0)); + currentsector->camera->reset( + Vector(tux.get_pos().x, tux.get_pos().y)); + last_keys.clear(); + } + if(compare_last(last_keys, "finish")) { + // finish current sector + exit_status = ES_LEVEL_FINISHED; + // don't add points to stats though... + } + // temporary to help player's choosing a flapping + if(compare_last(last_keys, "marek")) { + tux.flapping_mode = Player::MAREK_FLAP; + last_keys.clear(); + } + if(compare_last(last_keys, "ricardo")) { + tux.flapping_mode = Player::RICARDO_FLAP; + last_keys.clear(); + } + if(compare_last(last_keys, "ryan")) { + tux.flapping_mode = Player::RYAN_FLAP; + last_keys.clear(); + } +} + +void GameSession::check_end_conditions() { Player* tux = currentsector->player; @@ -811,7 +797,7 @@ GameSession::respawn(const std::string& sector, const std::string& spawnpoint) void GameSession::start_sequence(const std::string& sequencename) { - if(sequencename == "endsequence") { + if(sequencename == "endsequence" || sequencename == "fireworks") { if(end_sequence) return; @@ -825,7 +811,7 @@ GameSession::start_sequence(const std::string& sequencename) global_stats.set_points(TIME_NEEDED_STAT, int(time_left.get_period() - time_left.get_timeleft())); - if(level->get_end_sequence_type() == Level::FIREWORKS_ENDSEQ_ANIM) { + if(sequencename == "fireworks") { currentsector->add_object(new Fireworks()); } } else { diff --git a/src/gameloop.h b/src/gameloop.h index a63a24b05..ea1df562a 100644 --- a/src/gameloop.h +++ b/src/gameloop.h @@ -129,6 +129,7 @@ private: void check_end_conditions(); void start_timers(); void process_events(); + void handle_cheats(); void levelintro(); void drawstatus(DrawingContext& context); diff --git a/src/intro.cpp b/src/intro.cpp deleted file mode 100644 index d23e600d7..000000000 --- a/src/intro.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// $Id$ -// -// SuperTux - A Jump'n Run -// Copyright (C) 2000 Bill Kendrick -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include - -#include "intro.h" -#include "app/globals.h" -#include "defines.h" -#include "video/font.h" -#include "video/screen.h" -#include "resources.h" - -using namespace SuperTux; - -void draw_intro() -{ - display_text_file("intro.txt", 1, white_big_text , white_text, white_small_text, blue_text ); -} - diff --git a/src/level.cpp b/src/level.cpp index 8e572a05e..21ce9eb53 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -38,7 +38,6 @@ #include "lisp/writer.h" #include "level.h" #include "math/physic.h" -#include "scene.h" #include "sector.h" #include "tile.h" #include "resources.h" @@ -47,11 +46,13 @@ #include "object/tilemap.h" #include "object/coin.h" +// test +#include "flip_level_transformer.h" + using namespace std; Level::Level() - : name("noname"), author("mr. x"), timelimit(500), - end_sequence_type(NONE_ENDSEQ_ANIM) + : name("noname"), author("Mr. X"), timelimit(500) { } @@ -70,6 +71,13 @@ Level::load(const std::string& filepath) level->get("version", version); if(version == 1) { load_old_format(*level); + +#if 0 + // test for now + FlipLevelTransformer* transformer = new FlipLevelTransformer(); + transformer->transform(this); +#endif + return; } @@ -91,20 +99,12 @@ Level::load(const std::string& filepath) Sector* sector = new Sector; sector->parse(*(iter.lisp())); add_sector(sector); - } else if(token == "end-sequence-animation") { - std::string endsequencename; - iter.value()->get(endsequencename); - if(endsequencename == "fireworks") { - end_sequence_type = FIREWORKS_ENDSEQ_ANIM; - } else { - std::cout << "Unknown endsequence type: '" << endsequencename << - "'.\n"; - } } else { std::cerr << "Unknown token '" << token << "' in level file.\n"; continue; } } + } catch(std::exception& e) { std::stringstream msg; msg << "Problem when reading level '" << filepath << "': " << e.what(); @@ -144,12 +144,11 @@ Level::save(const std::string& filename) writer->write_string("name", name, true); writer->write_string("author", author); writer->write_int("time", timelimit); - writer->write_string("end-sequence-animation", - end_sequence_type == FIREWORKS_ENDSEQ_ANIM ? "fireworks" : "none"); for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) { + Sector* sector = *i; writer->start_list("sector"); - i->second->write(*writer); + sector->write(*writer); writer->end_list("sector"); } @@ -162,63 +161,41 @@ Level::save(const std::string& filename) Level::~Level() { for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) - delete i->second; + delete *i; } void Level::add_sector(Sector* sector) { - sectors.insert(std::make_pair(sector->get_name(), sector)); + Sector* test = get_sector(sector->get_name()); + if(test != 0) { + throw std::runtime_error("Trying to add 2 sectors with same name"); + } + sectors.push_back(sector); } Sector* Level::get_sector(const std::string& name) { - Sectors::iterator i = sectors.find(name); - if(i == sectors.end()) - return 0; + for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) { + Sector* sector = *i; + if(sector->get_name() == name) + return sector; + } - return i->second; + return 0; } -Sector* -Level::get_next_sector(const Sector* sector) +size_t +Level::get_sector_count() { - for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) - { - if(i->second == sector) - { - i++; - if(i == sectors.end()) - return NULL; - return i->second; - } - } - std::cerr << "Warning: Sector not found on level\n"; - return NULL; + return sectors.size(); } Sector* -Level::get_previous_sector(const Sector* sector) +Level::get_sector(size_t num) { - for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) - { - if(i->second == sector) - { - if(i == sectors.begin()) - return NULL; - i--; - return i->second; - } - } - std::cerr << "Warning: Sector not found on level\n"; - return NULL; -} - -int -Level::get_total_sectors() -{ -return sectors.size(); + return sectors.at(num); } int @@ -226,16 +203,17 @@ Level::get_total_badguys() { int total_badguys = 0; for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) - total_badguys += i->second->get_total_badguys(); + total_badguys += (*i)->get_total_badguys(); return total_badguys; } int Level::get_total_coins() { + // FIXME not really correct as coins can also be inside blocks... int total_coins = 0; for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) { - Sector* sector = i->second; + Sector* sector = *i; for(Sector::GameObjects::iterator o = sector->gameobjects.begin(); o != sector->gameobjects.end(); ++o) { Coin* coin = dynamic_cast (*o); @@ -245,3 +223,4 @@ Level::get_total_coins() } return total_coins; } + diff --git a/src/level.h b/src/level.h index 7eb024807..555138ae1 100644 --- a/src/level.h +++ b/src/level.h @@ -21,11 +21,9 @@ #ifndef SUPERTUX_LEVEL_H #define SUPERTUX_LEVEL_H -#include +#include #include -using namespace SuperTux; - class Sector; namespace lisp { @@ -35,17 +33,11 @@ class Lisp; class Level { public: - enum EndSequenceType{ - NONE_ENDSEQ_ANIM, - FIREWORKS_ENDSEQ_ANIM - }; - std::string name; std::string author; int timelimit; - typedef std::map Sectors; + typedef std::vector Sectors; Sectors sectors; - EndSequenceType end_sequence_type; public: Level(); @@ -55,9 +47,6 @@ public: void load(const std::string& filename); void save(const std::string& filename); - EndSequenceType get_end_sequence_type() const - { return end_sequence_type; } - const std::string& get_name() const { return name; } @@ -68,10 +57,8 @@ public: Sector* get_sector(const std::string& name); - Sector* get_next_sector(const Sector* sector); - Sector* get_previous_sector(const Sector* sector); - - int get_total_sectors(); + size_t get_sector_count(); + Sector* get_sector(size_t num); int get_total_badguys(); int get_total_coins(); diff --git a/src/level_transformer.cpp b/src/level_transformer.cpp new file mode 100644 index 000000000..4735dd247 --- /dev/null +++ b/src/level_transformer.cpp @@ -0,0 +1,17 @@ +#include + +#include "level_transformer.h" +#include "level.h" + +LevelTransformer::~LevelTransformer() +{ +} + +void +LevelTransformer::transform(Level* level) +{ + for(size_t i = 0; i < level->get_sector_count(); ++i) { + transform_sector(level->get_sector(i)); + } +} + diff --git a/src/level_transformer.h b/src/level_transformer.h new file mode 100644 index 000000000..c587acf9e --- /dev/null +++ b/src/level_transformer.h @@ -0,0 +1,25 @@ +#ifndef __LEVEL_TRANSFORMER_H__ +#define __LEVEL_TRANSFORMER_H__ + +class Level; +class Sector; + +/** + * This class is an abstract interface for algorithms that transform levels in + * some way before they are played. + */ +class LevelTransformer +{ +public: + virtual ~LevelTransformer(); + + /** transform a complete Level, the standard implementation just calls + * transformSector on each sector in the level. + */ + virtual void transform(Level* level); + + virtual void transform_sector(Sector* sector) = 0; +}; + +#endif + diff --git a/src/leveleditor.cpp b/src/leveleditor.cpp index 1f1692def..9ae0dc173 100644 --- a/src/leveleditor.cpp +++ b/src/leveleditor.cpp @@ -201,622 +201,625 @@ delete img_next_sector_bt; void LevelEditor::run(const std::string filename) { -SoundManager::get()->halt_music(); -Menu::set_current(0); + SoundManager::get()->halt_music(); + Menu::set_current(0); -DrawingContext context; + DrawingContext context; -if(!filename.empty()) - { - level_nb = -1; - load_level(filename); - } -else - Menu::set_current(main_menu); + if(!filename.empty()) + { + level_nb = -1; + load_level(filename); + } + else + Menu::set_current(main_menu); -mouse_cursor->set_state(MC_NORMAL); + mouse_cursor->set_state(MC_NORMAL); -done = false; -while(!done) - { - events(); - action(); - draw(context); - } + done = false; + while(!done) + { + events(); + action(); + draw(context); + } -if(level_changed) - if(confirm_dialog(NULL, _("Level not saved. Wanna to?"))) - save_level(); + if(level_changed) + if(confirm_dialog(NULL, _("Level not saved. Wanna to?"))) + save_level(); } void LevelEditor::events() { -mouse_moved = false; + mouse_moved = false; -while(SDL_PollEvent(&event)) - { - Menu* menu = Menu::current(); - if(menu) + SDL_Event event; + while(SDL_PollEvent(&event)) { - menu->event(event); - menu->action(); - if(menu == main_menu) + Menu* menu = Menu::current(); + if(menu) { - switch (main_menu->check()) + menu->event(event); + menu->action(); + if(menu == main_menu) { - case MN_ID_RETURN: - Menu::set_current(0); - break; - case MN_ID_QUIT: - done = true; - break; + switch (main_menu->check()) + { + case MN_ID_RETURN: + Menu::set_current(0); + break; + case MN_ID_QUIT: + done = true; + break; + } } - } - else if(menu == create_subset_menu) - { - // activate or deactivate Create button if any filename as been specified - if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0') - create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE; - else - create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION; - - if(create_subset_menu->check() == MN_ID_CREATE_SUBSET) - { // applying settings: - std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input; - LevelSubset::create(subset_name); - - delete level_subset; - level_subset = new LevelSubset(); - level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input); - - level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input; - level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input; - //FIXME: generate better level filenames - level_subset->add_level(subset_name+'/'+"new_level.stl"); - Level* newlevel = new Level(); - newlevel->add_sector(create_sector("main", 25, 19)); - newlevel->save(level_subset->get_level_filename(0)); - level_subset->save(); - - load_level(0); - - create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input(""); - create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input(""); - create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input(""); + else if(menu == create_subset_menu) + { + // activate or deactivate Create button if any filename as been specified + if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0') + create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE; + else + create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION; + + if(create_subset_menu->check() == MN_ID_CREATE_SUBSET) + { // applying settings: + std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input; + LevelSubset::create(subset_name); + + delete level_subset; + level_subset = new LevelSubset(); + level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input); + + level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input; + level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input; + //FIXME: generate better level filenames + level_subset->add_level(subset_name+'/'+"new_level.stl"); + Level* newlevel = new Level(); + newlevel->add_sector(create_sector("main", 25, 19)); + newlevel->save(level_subset->get_level_filename(0)); + level_subset->save(); + + load_level(0); + + create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input(""); + create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input(""); + create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input(""); + } } - } - else if(menu == subset_menu) - { - int i = subset_menu->check(); - if(i >= 0) + else if(menu == subset_menu) { - std::set::iterator it = level_subsets.begin(); - for(int t = 0; t < i; t++) - it++; - load_level_subset(*it); - Menu::set_current(0); + int i = subset_menu->check(); + if(i >= 0) + { + std::set::iterator it = level_subsets.begin(); + for(int t = 0; t < i; t++) + it++; + load_level_subset(*it); + Menu::set_current(0); + } } - } - else if(menu == settings_menu) - { - if(settings_menu->check() == MN_ID_APPLY_SETTINGS) - { // applying settings: - level_changed = true; + else if(menu == settings_menu) + { + if(settings_menu->check() == MN_ID_APPLY_SETTINGS) + { // applying settings: + level_changed = true; - level->name = settings_menu->get_item_by_id(MN_ID_NAME).input; - level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input; + level->name = settings_menu->get_item_by_id(MN_ID_NAME).input; + level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input; - solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()), - atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str())); - foregrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()), - atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str())); - backgrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()), - atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str())); + solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()), + atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str())); + foregrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()), + atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str())); + backgrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()), + atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str())); - Menu::set_current(0); + Menu::set_current(0); + } } } - } - // check for events in buttons - else if(tiles_board->event(event)) - { - std::vector vector; - vector.push_back(tiles_board->selected_id()); + // check for events in buttons + else if(tiles_board->event(event)) + { + std::vector vector; + vector.push_back(tiles_board->selected_id()); - selection.clear(); - selection.push_back(vector); - continue; - } - else if(tiles_layer->event(event)) - { - cur_layer = tiles_layer->selected_id(); - continue; - } - else if(level_options->event(event)) - { - switch(level_options->selected_id()) + selection.clear(); + selection.push_back(vector); + continue; + } + else if(tiles_layer->event(event)) { - case BT_LEVEL_SAVE: - save_level(); - break; - case BT_LEVEL_TEST: - test_level(); - break; - case BT_LEVEL_SETUP: - Menu::set_current(settings_menu); - break; - case BT_NEXT_LEVEL: - if(level_nb + 1 < level_subset->get_num_levels()) - load_level(level_nb + 1); - else - { - char str[1024]; - sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2); - if(confirm_dialog(NULL, str)) - { - level_subset->add_level("new_level.stl"); - Level* newlevel = new Level(); - newlevel->add_sector(create_sector("main", 25, 19)); - newlevel->save(level_subset->get_level_filename(level_nb + 1)); - level_subset->save(); + cur_layer = tiles_layer->selected_id(); + continue; + } + else if(level_options->event(event)) + { + switch(level_options->selected_id()) + { + case BT_LEVEL_SAVE: + save_level(); + break; + case BT_LEVEL_TEST: + test_level(); + break; + case BT_LEVEL_SETUP: + Menu::set_current(settings_menu); + break; + case BT_NEXT_LEVEL: + if(level_nb + 1 < level_subset->get_num_levels()) load_level(level_nb + 1); + else + { + char str[1024]; + sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2); + if(confirm_dialog(NULL, str)) + { + level_subset->add_level("new_level.stl"); + Level* newlevel = new Level(); + newlevel->add_sector(create_sector("main", 25, 19)); + newlevel->save(level_subset->get_level_filename(level_nb + 1)); + level_subset->save(); + load_level(level_nb + 1); + } } - } - break; - case BT_PREVIOUS_LEVEL: - if(level_nb - 1 >= 0) - load_level(level_nb - 1); - break; - case BT_NEXT_SECTOR: -std::cerr << "next sector.\n"; -std::cerr << "total sectors: " << level->get_total_sectors() << std::endl; - load_sector(level->get_next_sector(sector)); - break; - case BT_PREVIOUS_SECTOR: -std::cerr << "previous sector.\n"; - load_sector(level->get_previous_sector(sector)); - break; + break; + case BT_PREVIOUS_LEVEL: + if(level_nb - 1 >= 0) + load_level(level_nb - 1); + break; + case BT_NEXT_SECTOR: + std::cerr << "next sector.\n"; + load_sector(sectornum+1); + break; + case BT_PREVIOUS_SECTOR: + std::cerr << "previous sector.\n"; + if(sectornum > 0) + load_sector(sectornum-1); + break; + } + level_options->set_unselected(); + continue; } - level_options->set_unselected(); - continue; - } - else - { - switch(event.type) + else { - case SDL_MOUSEMOTION: - mouse_moved = true; - if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT)) - { // movement like in strategy games - scroll.x += -1 * event.motion.xrel; - scroll.y += -1 * event.motion.yrel; - } - break; - - case SDL_MOUSEBUTTONDOWN: - mouse_moved = true; - if(event.button.button == SDL_BUTTON_LEFT) - left_button = true; - else if(event.button.button == SDL_BUTTON_MIDDLE) - { - middle_button = true; - selection_ini = Vector(event.button.x, event.button.y); - } - break; - - case SDL_MOUSEBUTTONUP: - mouse_moved = true; - if(event.button.button == SDL_BUTTON_LEFT) - left_button = false; - else if(event.button.button == SDL_BUTTON_MIDDLE) - { - middle_button = false; - selection_end = Vector(event.button.x, event.button.y); + switch(event.type) + { + case SDL_MOUSEMOTION: + mouse_moved = true; + mouse_x = event.motion.x; + mouse_y = event.motion.y; + if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT)) + { // movement like in strategy games + scroll.x += -1 * event.motion.xrel; + scroll.y += -1 * event.motion.yrel; + } + break; - if(selection_end.x < selection_ini.x) + case SDL_MOUSEBUTTONDOWN: + mouse_moved = true; + mouse_x = event.motion.x; + mouse_y = event.motion.y; + if(event.button.button == SDL_BUTTON_LEFT) + left_button = true; + else if(event.button.button == SDL_BUTTON_MIDDLE) { - float t = selection_ini.x; - selection_ini.x = selection_end.x; - selection_end.x = t; + middle_button = true; + selection_ini = Vector(event.button.x, event.button.y); } - if(selection_end.y < selection_ini.y) + break; + + case SDL_MOUSEBUTTONUP: + mouse_moved = true; + mouse_x = event.motion.x; + mouse_y = event.motion.y; + if(event.button.button == SDL_BUTTON_LEFT) + left_button = false; + else if(event.button.button == SDL_BUTTON_MIDDLE) { - float t = selection_ini.y; - selection_ini.y = selection_end.y; - selection_end.y = t; - } + middle_button = false; + selection_end = Vector(event.button.x, event.button.y); - selection.clear(); - std::vector vector; + if(selection_end.x < selection_ini.x) + { + float t = selection_ini.x; + selection_ini.x = selection_end.x; + selection_end.x = t; + } + if(selection_end.y < selection_ini.y) + { + float t = selection_ini.y; + selection_ini.y = selection_end.y; + selection_end.y = t; + } - TileMap* tilemap = 0; - if(cur_layer == LAYER_FOREGROUNDTILES) - tilemap = foregrounds; - else if(cur_layer == LAYER_TILES) - tilemap = solids; - else if(cur_layer == LAYER_BACKGROUNDTILES) - tilemap = backgrounds; + selection.clear(); + std::vector vector; - for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++) - { - vector.clear(); - for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++) + TileMap* tilemap = 0; + if(cur_layer == LAYER_FOREGROUNDTILES) + tilemap = foregrounds; + else if(cur_layer == LAYER_TILES) + tilemap = solids; + else if(cur_layer == LAYER_BACKGROUNDTILES) + tilemap = backgrounds; + + for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++) { - vector.push_back(tilemap->get_tile(x + - (int)(((selection_ini.x+scroll.x)*zoom)/32), - y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->getID()); + vector.clear(); + for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++) + { + vector.push_back(tilemap->get_tile(x + + (int)(((selection_ini.x+scroll.x)*zoom)/32), + y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->getID()); + } + selection.push_back(vector); } - selection.push_back(vector); } - } - break; + break; - case SDL_KEYDOWN: // key pressed - switch(event.key.keysym.sym) - { - case SDLK_ESCAPE: - Menu::set_current(main_menu); - break; - /* scrolling related events: */ - case SDLK_HOME: - scroll.x = 0; - break; - case SDLK_END: - scroll.x = sector->solids->get_height()*32 - screen->w; - break; - case SDLK_LEFT: - scroll.x -= 80; - break; - case SDLK_RIGHT: - scroll.x += 80; - break; - case SDLK_UP: - scroll.y -= 80; - break; - case SDLK_DOWN: - scroll.y += 80; - break; - case SDLK_PAGEUP: - scroll.x -= 450; - break; - case SDLK_PAGEDOWN: - scroll.x += 450; - break; - case SDLK_PLUS: - case SDLK_KP_PLUS: - zoom += 0.10; - break; - case SDLK_MINUS: - case SDLK_KP_MINUS: - zoom -= 0.10; - break; + case SDL_KEYDOWN: // key pressed + switch(event.key.keysym.sym) + { + case SDLK_ESCAPE: + Menu::set_current(main_menu); + break; + /* scrolling related events: */ + case SDLK_HOME: + scroll.x = 0; + break; + case SDLK_END: + scroll.x = sector->solids->get_height()*32 - screen->w; + break; + case SDLK_LEFT: + scroll.x -= 80; + break; + case SDLK_RIGHT: + scroll.x += 80; + break; + case SDLK_UP: + scroll.y -= 80; + break; + case SDLK_DOWN: + scroll.y += 80; + break; + case SDLK_PAGEUP: + scroll.x -= 450; + break; + case SDLK_PAGEDOWN: + scroll.x += 450; + break; + case SDLK_PLUS: + case SDLK_KP_PLUS: + zoom += 0.10; + break; + case SDLK_MINUS: + case SDLK_KP_MINUS: + zoom -= 0.10; + break; + + case SDLK_F1: + show_help(); + break; + case SDLK_F2: + show_grid = !show_grid; + break; + default: + break; + } + break; + + case SDL_QUIT: // window closed + done = true; + break; - case SDLK_F1: - show_help(); - break; - case SDLK_F2: - show_grid = !show_grid; - break; default: break; - } - break; - - case SDL_QUIT: // window closed - done = true; - break; - - default: - break; + } } } - } } void LevelEditor::action() { -mouse_cursor->set_state(MC_NORMAL); -if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover()) - mouse_cursor->set_state(MC_LINK); + mouse_cursor->set_state(MC_NORMAL); + if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover()) + mouse_cursor->set_state(MC_LINK); -if(sector) - { - // don't scroll before the start or after the level's end - float width = sector->solids->get_width() * 32; - float height = sector->solids->get_height() * 32; - - if(scroll.x < -screen->w/2) - scroll.x = -screen->w/2; - if(scroll.x > width - screen->w/2) - scroll.x = width - screen->w/2; - if(scroll.y < -screen->h/2) - scroll.y = -screen->h/2; - if(scroll.y > height - screen->h/2) - scroll.y = height - screen->h/2; - - // set camera translation, since BadGuys like it - sector->camera->set_scrolling((int)scroll.x, (int)scroll.y); - - if(left_button && mouse_moved) - for(unsigned int x = 0; x < selection.size(); x++) - for(unsigned int y = 0; y < selection[x].size(); y++) - change((int)(scroll.x + event.button.x) + (x*32), - (int)(scroll.y + event.button.y) + (y*32), selection[x][y], - cur_layer); - } + if(sector) + { + // don't scroll before the start or after the level's end + float width = sector->solids->get_width() * 32; + float height = sector->solids->get_height() * 32; + + if(scroll.x < -screen->w/2) + scroll.x = -screen->w/2; + if(scroll.x > width - screen->w/2) + scroll.x = width - screen->w/2; + if(scroll.y < -screen->h/2) + scroll.y = -screen->h/2; + if(scroll.y > height - screen->h/2) + scroll.y = height - screen->h/2; + + // set camera translation, since BadGuys like it + sector->camera->set_scrolling((int)scroll.x, (int)scroll.y); + + if(left_button && mouse_moved) + for(unsigned int x = 0; x < selection.size(); x++) + for(unsigned int y = 0; y < selection[x].size(); y++) + change((int)(scroll.x + mouse_x) + (x*32), + (int)(scroll.y + mouse_y) + (y*32), selection[x][y], + cur_layer); + } } #define FADING_TIME .6 void LevelEditor::draw(DrawingContext& context) { -context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI); -mouse_cursor->draw(context); + context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI); + mouse_cursor->draw(context); -// draw a filled background -context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h), Color(60,60,60), LAYER_BACKGROUND0-1); - -if(level_name_timer.check()) - { - context.push_transform(); - if(level_name_timer.get_timeleft() < FADING_TIME) - context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME)); + // draw a filled background + context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h), Color(60,60,60), LAYER_BACKGROUND0-1); - context.draw_text(gold_text, level->name, Vector(screen->w/2, 30), CENTER_ALLIGN, LAYER_GUI); - if(level_nb != -1) + if(level_name_timer.check()) { - char str[128]; - sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels()); - context.draw_text(gold_text, str, Vector(screen->w/2, 50), CENTER_ALLIGN, LAYER_GUI); - } + context.push_transform(); + if(level_name_timer.get_timeleft() < FADING_TIME) + context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME)); - context.pop_transform(); - } -if(sector) - context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10); -else - context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10); - -Menu* menu = Menu::current(); -if(menu) - menu->draw(context); -else - { - tiles_board->draw(context); - tiles_layer->draw(context); - level_options->draw(context); - } + context.draw_text(gold_text, level->name, Vector(screen->w/2, 30), CENTER_ALLIGN, LAYER_GUI); + if(level_nb != -1) + { + char str[128]; + sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels()); + context.draw_text(gold_text, str, Vector(screen->w/2, 50), CENTER_ALLIGN, LAYER_GUI); + } -// draw selection -if(sector) - { - if(!middle_button) + context.pop_transform(); + } + if(sector) + context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10); + else + context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10); + + Menu* menu = Menu::current(); + if(menu) + menu->draw(context); + else { - context.set_drawing_effect(SEMI_TRANSPARENT); + tiles_board->draw(context); + tiles_layer->draw(context); + level_options->draw(context); + } - if(selection.size()) + // draw selection + if(sector) + { + if(!middle_button) { - if(selection[0][0] == 0 && selection.size() == 1) - context.draw_surface(img_rubber_bt, Vector(event.button.x - 8, - event.button.y - 8), LAYER_GUI-2); - else if(selection[0][0] >= gameobjs_first_id || selection[0][0] < 0) + context.set_drawing_effect(SEMI_TRANSPARENT); + + if(selection.size()) { -// FIXME: this should draw an object image near cursor -#if 0 - int id = selection[0][0]; - - if(id == OBJ_TRAMPOLINE) - context.draw_surface(img_trampoline[0].get_frame(0), Vector(event.button.x - 8, - event.button.y - 8), LAYER_GUI-2); - else if(id == OBJ_FLYING_PLATFORM) - context.draw_surface(img_flying_platform->get_frame(0), Vector(event.button.x - 8, - event.button.y - 8), LAYER_GUI-2); - else - if(id == OBJ_DOOR) - /*context.draw_surface(door->get_frame(0), Vector(event.button.x - 8, - event.button.y - 8), LAYER_GUI-2);*/ - ; - else + if(selection[0][0] == 0 && selection.size() == 1) + context.draw_surface(img_rubber_bt, Vector(mouse_x - 8, + mouse_y - 8), LAYER_GUI-2); + else if(selection[0][0] >= gameobjs_first_id || selection[0][0] < 0) { - BadGuyKind kind = BadGuyKind((-id)-1); - BadGuy badguy(kind, 0,0); - badguy.activate(LEFT); - Surface *img = badguy.get_image(); + // FIXME: this should draw an object image near cursor + #if 0 + int id = selection[0][0]; + + if(id == OBJ_TRAMPOLINE) + context.draw_surface(img_trampoline[0].get_frame(0), Vector(mouse_x - 8, + mouse_y - 8), LAYER_GUI-2); + else if(id == OBJ_FLYING_PLATFORM) + context.draw_surface(img_flying_platform->get_frame(0), Vector(mouse_x - 8, + mouse_y - 8), LAYER_GUI-2); + else + if(id == OBJ_DOOR) + /*context.draw_surface(door->get_frame(0), Vector(mouse_x - 8, + mouse_y - 8), LAYER_GUI-2);*/ + ; + else + { + BadGuyKind kind = BadGuyKind((-id)-1); + BadGuy badguy(kind, 0,0); + badguy.activate(LEFT); + Surface *img = badguy.get_image(); - context.draw_surface(img, Vector(event.button.x - 8, - event.button.y - 8), LAYER_GUI-2); + context.draw_surface(img, Vector(mouse_x - 8, + mouse_y - 8), LAYER_GUI-2); + } + #endif } -#endif - } - else - { - for(unsigned int x = 0; x < selection.size(); x++) - for(unsigned int y = 0; y < selection[x].size(); y++) { - const Tile* tile = tile_manager->get(selection[x][y]); - tile->draw(context, - Vector(event.button.x + x*32 - 8, event.button.y + y*32 - 8), - LAYER_GUI-2); + else + { + for(unsigned int x = 0; x < selection.size(); x++) + for(unsigned int y = 0; y < selection[x].size(); y++) { + const Tile* tile = tile_manager->get(selection[x][y]); + tile->draw(context, + Vector(mouse_x + x*32 - 8, mouse_y + y*32 - 8), + LAYER_GUI-2); + } } } + context.set_drawing_effect(NONE_EFFECT); } - context.set_drawing_effect(NONE_EFFECT); - } - else - context.draw_filled_rect(Vector(std::min((int)selection_ini.x, (int)event.button.x)*zoom, - std::min((int)selection_ini.y, (int)event.button.y))*zoom, - Vector(abs(event.button.x - (int)selection_ini.x)*zoom, - abs(event.button.y - (int)selection_ini.y)*zoom), - Color(170,255,170,128), LAYER_GUI-2); + else + context.draw_filled_rect(Vector(std::min((int)selection_ini.x, mouse_x)*zoom, + std::min((int)selection_ini.y, mouse_y))*zoom, + Vector(abs(mouse_x - (int)selection_ini.x)*zoom, + abs(mouse_y - (int)selection_ini.y)*zoom), + Color(170,255,170,128), LAYER_GUI-2); - if(show_grid) - { - for(int x = 0; x < screen->w / (32*zoom); x++) + if(show_grid) { - int pos = (int)(x*32*zoom) - ((int)scroll.x % 32); - context.draw_filled_rect(Vector (pos, 0), Vector(1, screen->h), - Color(225, 225, 225), LAYER_GUI-50); - } - for(int y = 0; y < screen->h / (32*zoom); y++) - { - int pos = (int)(y*32*zoom) - ((int)scroll.y % 32); - context.draw_filled_rect(Vector (0, pos), Vector(screen->w, 1), - Color(225, 225, 225), LAYER_GUI-50); + for(int x = 0; x < screen->w / (32*zoom); x++) + { + int pos = (int)(x*32*zoom) - ((int)scroll.x % 32); + context.draw_filled_rect(Vector (pos, 0), Vector(1, screen->h), + Color(225, 225, 225), LAYER_GUI-50); + } + for(int y = 0; y < screen->h / (32*zoom); y++) + { + int pos = (int)(y*32*zoom) - ((int)scroll.y % 32); + context.draw_filled_rect(Vector (0, pos), Vector(screen->w, 1), + Color(225, 225, 225), LAYER_GUI-50); + } } - } - context.push_transform(); - context.set_translation(scroll); - context.set_zooming(zoom); + context.push_transform(); + context.set_translation(scroll); + context.set_zooming(zoom); - for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i) - { - TileMap* tilemap = dynamic_cast (*i); - if(tilemap) - { // draw the non-selected tiles semi-transparently - context.push_transform(); + for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i) + { + TileMap* tilemap = dynamic_cast (*i); + if(tilemap) + { // draw the non-selected tiles semi-transparently + context.push_transform(); - if(tilemap->get_layer() != cur_layer) - context.set_drawing_effect(SEMI_TRANSPARENT); - (*i)->draw(context); + if(tilemap->get_layer() != cur_layer) + context.set_drawing_effect(SEMI_TRANSPARENT); + (*i)->draw(context); - context.pop_transform(); - continue; - } - Background* background = dynamic_cast (*i); - if(background) - { // don't resize background - context.push_transform(); - context.set_zooming(1.0); - (*i)->draw(context); - context.pop_transform(); + context.pop_transform(); + continue; + } + Background* background = dynamic_cast (*i); + if(background) + { // don't resize background + context.push_transform(); + context.set_zooming(1.0); + (*i)->draw(context); + context.pop_transform(); + } + else + (*i)->draw(context); } - else - (*i)->draw(context); - } - context.pop_transform(); - } -else - context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h),Color(0,0,0), LAYER_BACKGROUND0); + context.pop_transform(); + } + else + context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h),Color(0,0,0), LAYER_BACKGROUND0); -context.do_drawing(); + context.do_drawing(); } void LevelEditor::load_level_subset(std::string filename) { -delete level_subset; -level_subset = new LevelSubset(); -level_subset->load(filename.c_str()); -load_level(0); + delete level_subset; + level_subset = new LevelSubset(); + level_subset->load(filename.c_str()); + load_level(0); } void LevelEditor::load_level(std::string filename) { -if(level_changed) - { - if(confirm_dialog(NULL, _("Level not saved. Wanna to?"))) - save_level(); - else - return; - } - -level_filename = filename; + if(level_changed) + { + if(confirm_dialog(NULL, _("Level not saved. Wanna to?"))) + save_level(); + else + return; + } -delete level; -level = new Level(); -level->load(filename); + level_filename = filename; -load_sector("main"); -level_name_timer.start(3000); -scroll.x = scroll.y = 0; -level_changed = false; + delete level; + level = new Level(); + level->load(filename); + + sectornum = 0; + load_sector(0); + level_name_timer.start(3000); + scroll.x = scroll.y = 0; + level_changed = false; -settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str()); -settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str()); + settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str()); + settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str()); } void LevelEditor::load_level(int nb) { -if(level_changed) - { - if(confirm_dialog(NULL, _("Level not saved. Wanna to?"))) - save_level(); - else - return; - } - -level_nb = nb; -level_filename = level_subset->get_level_filename(level_nb); - -load_level(level_filename); -} + if(level_changed) + { + if(confirm_dialog(NULL, _("Level not saved. Wanna to?"))) + save_level(); + else + return; + } -void LevelEditor::load_sector(std::string name) -{ -sector_name = name; -sector = level->get_sector(sector_name); -if(!sector) - Termination::abort("Level has no " + sector_name + " sector.", ""); + level_nb = nb; + level_filename = level_subset->get_level_filename(level_nb); -load_sector(sector); + load_level(level_filename); } -void LevelEditor::load_sector(Sector* sector_) +void LevelEditor::load_sector(size_t num) { -if(sector_ == NULL) + assert(num <= level->get_sector_count()); + + if(num >= level->get_sector_count()) { - if(!confirm_dialog(NULL, _("No more sectors exist. Create another?"))) - return; - sector_ = create_sector("new_sector",25,19); - level->add_sector(sector_); + if(!confirm_dialog(NULL, _("No more sectors exist. Create another?"))) + return; + Sector* sector_ = create_sector("new_sector",25,19); + level->add_sector(sector_); + num = level->get_sector_count()-1; } -sector = sector_; + sector = level->get_sector(num); -/* Load sector stuff */ + /* Load sector stuff */ -sector->update_game_objects(); + sector->update_game_objects(); -foregrounds = solids = backgrounds = 0; -/* Point foregrounds, backgrounds, solids to its layer */ -for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++) - { - TileMap* tilemap = dynamic_cast (*i); - if(tilemap) + foregrounds = solids = backgrounds = 0; + /* Point foregrounds, backgrounds, solids to its layer */ + for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++) { - if(tilemap->get_layer() == LAYER_FOREGROUNDTILES) - foregrounds = tilemap; - else if(tilemap->get_layer() == LAYER_TILES) - solids = tilemap; - else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES) - backgrounds = tilemap; + TileMap* tilemap = dynamic_cast (*i); + if(tilemap) + { + if(tilemap->get_layer() == LAYER_FOREGROUNDTILES) + foregrounds = tilemap; + else if(tilemap->get_layer() == LAYER_TILES) + solids = tilemap; + else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES) + backgrounds = tilemap; + } } - } -if(!foregrounds) - { - TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height()); - sector->add_object(tilemap); - sector->update_game_objects(); - } -if(!backgrounds) - { - TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height()); - sector->add_object(tilemap); - sector->update_game_objects(); - } + if(!foregrounds) + { + TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height()); + sector->add_object(tilemap); + sector->update_game_objects(); + } + if(!backgrounds) + { + TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height()); + sector->add_object(tilemap); + sector->update_game_objects(); + } + + char str[64]; + sprintf(str, "%i", solids->get_width()); + settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str); + sprintf(str, "%i", solids->get_height()); + settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str); -char str[64]; -sprintf(str, "%i", solids->get_width()); -settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str); -sprintf(str, "%i", solids->get_height()); -settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str); + sectornum = num; } void LevelEditor::save_level() { -level->save(level_filename); -level_changed = false; + level->save(level_filename); + level_changed = false; } void LevelEditor::test_level() @@ -907,111 +910,111 @@ void LevelEditor::change(int x, int y, int newtile, int layer) void LevelEditor::show_help() { -DrawingContext context; - -bool show_grid_t = show_grid; -show_grid = false; -mouse_cursor->set_state(MC_HIDE); - - -char str[1024]; -const char *text1[] = { - _("This is the built-in level editor. Its aim is to be intuitive\n" - "and simple to use, so it should be pretty straightforward.\n" - "\n" - "To open a level, first you'll have to select a level subset from\n" - "the menu (or create your own).\n" - "A level subset is basically a collection of levels.\n" - "They can then be played from the Contrib menu.\n" - "\n" - "To access the menu from the level editor, just press Esc.\n" - "\n" - "You are currently looking at the level. To scroll it, just\n" - "press the right mouse button and drag the mouse. It will move like\n" - "a strategy game.\n" - "You can also use the arrow keys and Page Up/Down.\n" - "\n" - "'+' and '-' keys can be used to zoom the level in/out.\n" - "\n" - "You probably already noticed those floating groups of buttons.\n" - "Each one serves a different purpose. To select a certain button\n" - "just press the Left mouse button on it. A few buttons have key\n" - "shortcuts. You can find them by pressing the Right mouse button on\n" - "a button. That will also show what that button does.\n" - "Groups of buttons can also be moved around by just dragging them,\n" - "while pressing the Left mouse button.\n" - "\n" - "Let's learn a bit of what each group of buttons does, shall we?\n" - "\n" - "To starting putting tiles and objects around use the bigger group\n" - "of buttons. Each button is a different tile. To put it on the level,\n" - "just press it and then left click in the level.\n" - "You can also copy tiles from the level by using the middle mouse button.\n" - "Use the mouse wheel to scroll that group of buttons. You will find\n" - "enemies and game objects in the bottom.\n") - }; - -const char *text2[] = { - _("The Foreground/Interactive/Background buttons may be used to\n" - "see and edit the respective layer. Levels have three tiles layers:\n" - "Foreground - tiles are drawn on top of everything and have no contact\n" - "with the player.\n" - "Interactive - these are the tiles that have contact with the player.\n" - "Background - tiles are drawn underneath everything and have no contact\n" - "with the player.\n" - "The unselected layers will be drawn semi-transparently.\n" - "\n" - "Last, but not least, the group of buttons that's left serves\n" - "to do related actions with the level.\n" - "From left to right:\n" - "Mini arrows - can be used to choose other sectors.\n" - "Sectors are mini-levels, so to speak, that can be accessed using a door.\n" - "Big arrows - choose other level in the same level subset.\n" - "Diskette - save the level\n" - "Tux - test the level\n" - "Tools - set a few settings for the level, including resizing it.\n" - "\n" - "We have reached the end of this Howto.\n" - "\n" - "Don't forget to send us a few cool levels. :)\n" - "\n" - "Enjoy,\n" - " SuperTux development team\n" - "\n" - "PS: If you are looking for something more powerful, you might like to\n" - "try FlexLay. FlexLay is a level editor that supports several games,\n" - "including SuperTux. It is an independent project.\n" - "Webpage: http://pingus.seul.org/~grumbel/flexlay/") - }; - -const char **text[] = { text1, text2 }; - - -bool done; -for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++) - { - draw(context); + DrawingContext context; + + bool show_grid_t = show_grid; + show_grid = false; + mouse_cursor->set_state(MC_HIDE); + + + char str[1024]; + const char *text1[] = { + _("This is the built-in level editor. Its aim is to be intuitive\n" + "and simple to use, so it should be pretty straightforward.\n" + "\n" + "To open a level, first you'll have to select a level subset from\n" + "the menu (or create your own).\n" + "A level subset is basically a collection of levels.\n" + "They can then be played from the Contrib menu.\n" + "\n" + "To access the menu from the level editor, just press Esc.\n" + "\n" + "You are currently looking at the level. To scroll it, just\n" + "press the right mouse button and drag the mouse. It will move like\n" + "a strategy game.\n" + "You can also use the arrow keys and Page Up/Down.\n" + "\n" + "'+' and '-' keys can be used to zoom the level in/out.\n" + "\n" + "You probably already noticed those floating groups of buttons.\n" + "Each one serves a different purpose. To select a certain button\n" + "just press the Left mouse button on it. A few buttons have key\n" + "shortcuts. You can find them by pressing the Right mouse button on\n" + "a button. That will also show what that button does.\n" + "Groups of buttons can also be moved around by just dragging them,\n" + "while pressing the Left mouse button.\n" + "\n" + "Let's learn a bit of what each group of buttons does, shall we?\n" + "\n" + "To starting putting tiles and objects around use the bigger group\n" + "of buttons. Each button is a different tile. To put it on the level,\n" + "just press it and then left click in the level.\n" + "You can also copy tiles from the level by using the middle mouse button.\n" + "Use the mouse wheel to scroll that group of buttons. You will find\n" + "enemies and game objects in the bottom.\n") + }; + + const char *text2[] = { + _("The Foreground/Interactive/Background buttons may be used to\n" + "see and edit the respective layer. Levels have three tiles layers:\n" + "Foreground - tiles are drawn on top of everything and have no contact\n" + "with the player.\n" + "Interactive - these are the tiles that have contact with the player.\n" + "Background - tiles are drawn underneath everything and have no contact\n" + "with the player.\n" + "The unselected layers will be drawn semi-transparently.\n" + "\n" + "Last, but not least, the group of buttons that's left serves\n" + "to do related actions with the level.\n" + "From left to right:\n" + "Mini arrows - can be used to choose other sectors.\n" + "Sectors are mini-levels, so to speak, that can be accessed using a door.\n" + "Big arrows - choose other level in the same level subset.\n" + "Diskette - save the level\n" + "Tux - test the level\n" + "Tools - set a few settings for the level, including resizing it.\n" + "\n" + "We have reached the end of this Howto.\n" + "\n" + "Don't forget to send us a few cool levels. :)\n" + "\n" + "Enjoy,\n" + " SuperTux development team\n" + "\n" + "PS: If you are looking for something more powerful, you might like to\n" + "try FlexLay. FlexLay is a level editor that supports several games,\n" + "including SuperTux. It is an independent project.\n" + "Webpage: http://pingus.seul.org/~grumbel/flexlay/") + }; + + const char **text[] = { text1, text2 }; + + + bool done; + for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++) + { + draw(context); - context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI); + context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI); - context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI); + context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI); - sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0])); - context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI); + sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0])); + context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI); - context.do_drawing(); + context.do_drawing(); - done = false; + done = false; - while(!done) - { - done = wait_for_event(event); - SDL_Delay(50); + while(!done) { + SDL_Event event; + done = wait_for_event(event); + SDL_Delay(50); } } -show_grid = show_grid_t; -mouse_cursor->set_state(MC_NORMAL); + show_grid = show_grid_t; + mouse_cursor->set_state(MC_NORMAL); } Sector* diff --git a/src/leveleditor.h b/src/leveleditor.h index 5fef8801b..912f8ea33 100644 --- a/src/leveleditor.h +++ b/src/leveleditor.h @@ -18,8 +18,6 @@ #ifndef SUPERTUX_LEVELEDITOR_H #define SUPERTUX_LEVELEDITOR_H -#include "SDL.h" - #include #include @@ -91,8 +89,7 @@ private: void load_level_subset(std::string filename); void load_level(std::string filename); void load_level(int nb); - void load_sector(std::string name); - void load_sector(Sector* sector); + void load_sector(size_t num); void save_level(); void test_level(); @@ -108,6 +105,7 @@ private: Level* level; std::string level_filename; + size_t sectornum; // number of current sector Sector* sector; // current sector TileMap *solids, *foregrounds, *backgrounds; std::string sector_name; @@ -122,13 +120,13 @@ private: Menu* settings_menu; bool left_button, middle_button, mouse_moved; + int mouse_x, mouse_y; bool done; bool show_grid; Vector scroll; float zoom; - SDL_Event event; Timer2 level_name_timer; Surface *img_background_bt, *img_foreground_bt, *img_interactive_bt; diff --git a/src/object/background.h b/src/object/background.h index 0bc3ca199..dd849b40d 100644 --- a/src/object/background.h +++ b/src/object/background.h @@ -25,6 +25,8 @@ #include "special/game_object.h" #include "serializable.h" +using namespace SuperTux; + class DisplayManager; namespace lisp { diff --git a/src/object/block.cpp b/src/object/block.cpp index b3c6fccb6..3bd32096d 100644 --- a/src/object/block.cpp +++ b/src/object/block.cpp @@ -13,6 +13,7 @@ #include "flower.h" #include "oneup.h" #include "star.h" +#include "player_status.h" #include "badguy/badguy.h" #include "coin.h" #include "object_factory.h" @@ -25,7 +26,7 @@ Block::Block(const Vector& pos, Sprite* newsprite) : sprite(newsprite), bouncing(false), bounce_dir(0), bounce_offset(0) { bbox.set_pos(pos); - bbox.set_size(32, 32); + bbox.set_size(32, 32.1); flags |= FLAG_SOLID; original_y = pos.y; } @@ -88,6 +89,7 @@ Block::draw(DrawingContext& context) void Block::start_bounce() { + original_y = bbox.p1.y; bouncing = true; bounce_dir = -BOUNCY_BRICK_SPEED; bounce_offset = 0; diff --git a/src/object/bullet.cpp b/src/object/bullet.cpp index bceed3ccb..e80491a29 100644 --- a/src/object/bullet.cpp +++ b/src/object/bullet.cpp @@ -2,7 +2,6 @@ #include #include "bullet.h" -#include "defines.h" #include "resources.h" #include "camera.h" #include "sector.h" diff --git a/src/object/camera.cpp b/src/object/camera.cpp index 246207f55..1b6038673 100644 --- a/src/object/camera.cpp +++ b/src/object/camera.cpp @@ -178,7 +178,7 @@ Camera::scroll_normal(float elapsed_time) /****** Vertical Scrolling part ******/ bool do_y_scrolling = true; - if(player->dying || sector->solids->get_height() == 19) + if(player->is_dying() || sector->solids->get_height() == 19) do_y_scrolling = false; if(do_y_scrolling) { @@ -260,7 +260,7 @@ Camera::scroll_autoscroll(float elapsed_time) { Player* player = sector->player; - if(player->dying) + if(player->is_dying()) return; if(auto_t - elapsed_time >= 0) { diff --git a/src/object/camera.h b/src/object/camera.h index 45351b6af..028671add 100644 --- a/src/object/camera.h +++ b/src/object/camera.h @@ -23,7 +23,6 @@ #include #include -#include "defines.h" #include "math/vector.h" #include "special/game_object.h" #include "video/drawing_context.h" diff --git a/src/object/coin.cpp b/src/object/coin.cpp index c0910183e..c9b38ba54 100644 --- a/src/object/coin.cpp +++ b/src/object/coin.cpp @@ -6,7 +6,7 @@ #include "special/sprite_manager.h" #include "player.h" #include "sector.h" -#include "scene.h" +#include "player_status.h" #include "gameobjs.h" #include "statistics.h" #include "object_factory.h" diff --git a/src/object/flower.cpp b/src/object/flower.cpp index 435be0725..d4805553b 100644 --- a/src/object/flower.cpp +++ b/src/object/flower.cpp @@ -2,7 +2,6 @@ #include #include "flower.h" -#include "defines.h" #include "resources.h" #include "camera.h" #include "sector.h" diff --git a/src/object/gameobjs.h b/src/object/gameobjs.h index 6b884d737..d5c958365 100644 --- a/src/object/gameobjs.h +++ b/src/object/gameobjs.h @@ -23,7 +23,6 @@ #include "video/surface.h" #include "timer.h" -#include "scene.h" #include "math/physic.h" #include "special/game_object.h" #include "special/moving_object.h" diff --git a/src/object/growup.cpp b/src/object/growup.cpp index deaaff168..4ed6e7231 100644 --- a/src/object/growup.cpp +++ b/src/object/growup.cpp @@ -2,7 +2,6 @@ #include #include "growup.h" -#include "defines.h" #include "resources.h" #include "camera.h" #include "sector.h" diff --git a/src/object/oneup.cpp b/src/object/oneup.cpp index a576bcfe3..324f8381f 100644 --- a/src/object/oneup.cpp +++ b/src/object/oneup.cpp @@ -3,7 +3,7 @@ #include "oneup.h" #include "resources.h" #include "player.h" -#include "scene.h" +#include "player_status.h" #include "sector.h" #include "special/sprite_manager.h" #include "video/drawing_context.h" diff --git a/src/object/player.cpp b/src/object/player.cpp index ec971654f..93fd5c425 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -27,9 +27,8 @@ #include "app/gettext.h" #include "special/sprite_manager.h" #include "player.h" -#include "defines.h" -#include "scene.h" #include "tile.h" +#include "player_status.h" #include "special/sprite.h" #include "sector.h" #include "resources.h" @@ -79,18 +78,24 @@ PlayerKeymap::PlayerKeymap() keymap.jump = SDLK_SPACE; } -void player_input_init(player_input_type* pplayer_input) +PlayerInputType::PlayerInputType() { - pplayer_input->up = UP; - pplayer_input->old_up = UP; - pplayer_input->down = UP; - pplayer_input->fire = UP; - pplayer_input->left = UP; - pplayer_input->old_fire = UP; - pplayer_input->right = UP; - pplayer_input->jump = UP; - pplayer_input->old_jump = UP; - pplayer_input->activate = UP; + reset(); +} + +void +PlayerInputType::reset() +{ + up = false; + old_up = false; + down = false; + fire = false; + old_fire = false; + left = false; + right = false; + jump = false; + old_jump = false; + activate = false; } void @@ -149,7 +154,7 @@ Player::init() duck = false; dead = false; - dying = DYING_NOT; + dying = false; last_ground_y = 0; fall_mode = ON_GROUND; jumping = false; @@ -171,13 +176,12 @@ Player::init() on_ground_flag = false; grabbed_object = 0; - player_input_init(&input); - + input.reset(); physic.reset(); } -int -Player::key_event(SDLKey key, int state) +bool +Player::key_event(SDLKey key, bool state) { idle_timer.start(IDLE_TIME, true); @@ -193,8 +197,8 @@ Player::key_event(SDLKey key, int state) } else if(key == keymap.up) { - if(state == UP) - input.old_up = UP; + if(state == false) + input.old_up = false; input.up = state; /* Up key also opens activates stuff */ input.activate = state; @@ -207,16 +211,16 @@ Player::key_event(SDLKey key, int state) } else if(key == keymap.power) { - if (state == UP) - input.old_fire = UP; + if(state == false) + input.old_fire = false; input.fire = state; return true; } else if(key == keymap.jump) { - if (state == UP) - input.old_jump = UP; + if(state == false) + input.old_jump = false; input.jump = state; return true; } @@ -230,9 +234,9 @@ Player::level_begin() move(Vector(100, 170)); duck = false; - dying = DYING_NOT; + dying = false; - player_input_init(&input); + input.reset(); on_ground_flag = false; @@ -253,11 +257,11 @@ Player::action(float elapsed_time) return; } - if(input.fire == UP) + if(input.fire == false) grabbed_object = 0; - if(dying == DYING_NOT) + if(!dying) handle_input(); movement = physic.get_movement(elapsed_time); @@ -296,36 +300,38 @@ Player::handle_horizontal_input() float ay = physic.get_acceleration_y(); float dirsign = 0; - if(input.left == DOWN && input.right == UP && (!duck || physic.get_velocity_y() != 0)) { + if(!duck || physic.get_velocity_y() != 0) { + if(input.left && !input.right) { old_dir = dir; dir = LEFT; dirsign = -1; - } else if(input.left == UP && input.right == DOWN && (!duck || physic.get_velocity_y() != 0)) { + } else if(!input.left && input.right) { old_dir = dir; dir = RIGHT; dirsign = 1; + } } - if (input.fire == UP) { - ax = dirsign * WALK_ACCELERATION_X; - // limit speed - if(vx >= MAX_WALK_XM && dirsign > 0) { - vx = MAX_WALK_XM; - ax = 0; - } else if(vx <= -MAX_WALK_XM && dirsign < 0) { - vx = -MAX_WALK_XM; - ax = 0; - } + if (!input.fire) { + ax = dirsign * WALK_ACCELERATION_X; + // limit speed + if(vx >= MAX_WALK_XM && dirsign > 0) { + vx = MAX_WALK_XM; + ax = 0; + } else if(vx <= -MAX_WALK_XM && dirsign < 0) { + vx = -MAX_WALK_XM; + ax = 0; + } } else { - ax = dirsign * RUN_ACCELERATION_X; - // limit speed - if(vx >= MAX_RUN_XM && dirsign > 0) { - vx = MAX_RUN_XM; - ax = 0; - } else if(vx <= -MAX_RUN_XM && dirsign < 0) { - vx = -MAX_RUN_XM; - ax = 0; - } + ax = dirsign * RUN_ACCELERATION_X; + // limit speed + if(vx >= MAX_RUN_XM && dirsign > 0) { + vx = MAX_RUN_XM; + ax = 0; + } else if(vx <= -MAX_RUN_XM && dirsign < 0) { + vx = -MAX_RUN_XM; + ax = 0; + } } // we can reach WALK_SPEED without any acceleration @@ -419,7 +425,7 @@ Player::handle_vertical_input() } // Press jump key - if(input.jump == DOWN && can_jump && on_ground()) + if(input.jump && can_jump && on_ground()) { if(duck) { // only jump a little bit when in duck mode { physic.set_velocity_y(300); @@ -443,7 +449,7 @@ Player::handle_vertical_input() SoundManager::get()->play_sound(IDToSound(SND_BIGJUMP)); } // Let go of jump key - else if(input.jump == UP) + else if(!input.jump) { if (!flapping && !duck && !falling_from_flap && !on_ground()) { @@ -461,8 +467,7 @@ Player::handle_vertical_input() { // Flapping, Ricardo's version // similar to SM3 Fox - if(input.jump == DOWN && input.old_jump == UP && can_flap && - flaps_nb < 3) + if(input.jump && !input.old_jump && can_flap && flaps_nb < 3) { physic.set_velocity_y(350); physic.set_velocity_x(physic.get_velocity_x() * 35); @@ -472,7 +477,7 @@ Player::handle_vertical_input() else if(flapping_mode == MAREK_FLAP) { // Flapping, Marek's version - if (input.jump == DOWN && can_flap) + if (input.jump && can_flap) { if (!flapping_timer.started()) { @@ -504,7 +509,7 @@ Player::handle_vertical_input() else if(flapping_mode == RYAN_FLAP) { // Flapping, Ryan's version - if (input.jump == DOWN && can_flap) + if (input.jump && can_flap) { if (!flapping_timer.started()) { @@ -549,13 +554,13 @@ Player::handle_vertical_input() #if 0 /* In case the player has pressed Down while in a certain range of air, enable butt jump action */ - if (input.down == DOWN && !butt_jump && !duck) + if (input.down && !butt_jump && !duck) if(tiles_on_air(TILES_FOR_BUTTJUMP) && jumping) butt_jump = true; #endif /* When Down is not held anymore, disable butt jump */ - if(butt_jump && input.down == UP) + if(butt_jump && !input.down) butt_jump = false; // Do butt jump @@ -617,13 +622,14 @@ Player::handle_vertical_input() get_pos().y + bbox.get_height() + 64)) && jumping == false && can_jump == false - && input.jump == DOWN - && input.old_jump == UP) + && input.jump && !input.old_jump) { can_jump = true; } #endif + // FIXME: why the heck is this here and not somewhere where the keys are + // checked?!? input.old_jump = input.jump; } @@ -634,30 +640,31 @@ Player::handle_input() handle_horizontal_input(); /* Jump/jumping? */ - if (on_ground() && input.jump == UP) + if (on_ground() && !input.jump) can_jump = true; handle_vertical_input(); /* Shoot! */ - if (input.fire == DOWN && input.old_fire == UP && got_power != NONE_POWER) { + if (input.fire && !input.old_fire && got_power != NONE_POWER) { if(Sector::current()->add_bullet( // get_pos() + Vector(0, bbox.get_height()/2), get_pos() + ((dir == LEFT)? Vector(0, bbox.get_height()/2) : Vector(32, bbox.get_height()/2)), physic.get_velocity_x(), dir)) shooting_timer.start(SHOOTING_TIME); - input.old_fire = DOWN; + // FIXME: why the heck is this here + input.old_fire = false; } /* Duck! */ - if (input.down == DOWN && size == BIG && !duck + if (input.down && size == BIG && !duck && physic.get_velocity_y() == 0 && on_ground()) { duck = true; bbox.move(Vector(0, 32)); bbox.set_height(31.8); } - else if(input.down == UP && size == BIG && duck) + else if(!input.down && size == BIG && duck) { // try if we can really unduck bbox.move(Vector(0, -32)); @@ -794,8 +801,8 @@ Player::draw(DrawingContext& context) } /* Draw Tux */ - if (dying == DYING_SQUISHED) { - smalltux_gameover->draw(context, get_pos(), LAYER_FOREGROUNDTILES+1); + if(dying) { + smalltux_gameover->draw(context, get_pos(), layer); } else if(growing_timer.get_timeleft() > 0) { if(size == SMALL) { @@ -848,7 +855,7 @@ HitResponse Player::collision(GameObject& other, const CollisionHit& hit) { Portable* portable = dynamic_cast (&other); - if(portable && grabbed_object == 0 && input.fire == DOWN + if(portable && grabbed_object == 0 && input.fire && fabsf(hit.normal.x) > .9) { grabbed_object = portable; return CONTINUE; @@ -872,7 +879,7 @@ Player::collision(GameObject& other, const CollisionHit& hit) TriggerBase* trigger = dynamic_cast (&other); if(trigger) { - if(input.up == DOWN && input.old_up == UP) + if(input.up && !input.old_up) trigger->event(*this, TriggerBase::EVENT_ACTIVATE); } @@ -923,7 +930,7 @@ Player::kill(HurtMode mode) physic.set_acceleration(0, 0); physic.set_velocity(0, 700); --player_status.lives; - dying = DYING_SQUISHED; + dying = true; dying_timer.start(3.0); flags |= FLAG_NO_COLLDET; } diff --git a/src/object/player.h b/src/object/player.h index 0a179a425..91e14b511 100644 --- a/src/object/player.h +++ b/src/object/player.h @@ -20,14 +20,14 @@ #define SUPERTUX_PLAYER_H #include -#include "SDL.h" +#include #include "timer.h" +#include "direction.h" #include "video/surface.h" #include "special/moving_object.h" #include "special/sprite.h" #include "math/physic.h" -#include "defines.h" using namespace SuperTux; @@ -61,28 +61,33 @@ public: extern PlayerKeymap keymap; -struct player_input_type +/** Contains a field of booleans that indicate wheter a button is pressed or + * released. The old_ fields contain the state of the button at the previous + * frame. + */ +struct PlayerInputType { - int right; - int left; - int up; - int old_up; - int down; - int fire; - int old_fire; - int activate; - int jump; - int old_jump; +public: + PlayerInputType(); + void reset(); + + bool left; + bool right; + bool up; + bool old_up; + bool down; + bool fire; + bool old_fire; + bool activate; + bool jump; + bool old_jump; }; -void player_input_init(player_input_type* pplayer_input); - class Camera; class PlayerStatus; extern Surface* tux_life; - #define GROWING_TIME 1.0 #define GROWING_FRAMES 7 extern Surface* growingtux_left[GROWING_FRAMES]; @@ -124,12 +129,15 @@ public: enum Power { NONE_POWER, FIRE_POWER, ICE_POWER }; enum FallMode { ON_GROUND, JUMPING, TRAMPOLINE_JUMP, FALLING }; - player_input_type input; + PlayerInputType input; int got_power; int size; bool duck; bool dead; - DyingType dying; + +private: + bool dying; +public: Direction dir; Direction old_dir; @@ -170,7 +178,7 @@ public: Player(); virtual ~Player(); - int key_event(SDLKey key, int state); + bool key_event(SDLKey key, bool state); void level_begin(); void handle_input(); @@ -183,7 +191,11 @@ public: void make_invincible(); bool is_invincible() const { - return invincible_timer.started(); + return invincible_timer.started(); + } + bool is_dying() const + { + return dying; } void kill(HurtMode mode); diff --git a/src/object/specialriser.cpp b/src/object/specialriser.cpp index 8d84f6af7..af0440efd 100644 --- a/src/object/specialriser.cpp +++ b/src/object/specialriser.cpp @@ -2,7 +2,6 @@ #include #include "specialriser.h" -#include "defines.h" #include "resources.h" #include "camera.h" #include "sector.h" diff --git a/src/object/star.cpp b/src/object/star.cpp index 522c08af4..8bf4a4d2f 100644 --- a/src/object/star.cpp +++ b/src/object/star.cpp @@ -3,7 +3,7 @@ #include "star.h" #include "resources.h" #include "player.h" -#include "scene.h" +#include "player_status.h" #include "special/sprite_manager.h" #include "video/drawing_context.h" diff --git a/src/object/tilemap.cpp b/src/object/tilemap.cpp index fb0cb63d7..59557d162 100644 --- a/src/object/tilemap.cpp +++ b/src/object/tilemap.cpp @@ -37,7 +37,7 @@ TileMap::TileMap() : solid(false), speed(1), width(0), height(0), layer(LAYER_TILES), - vertical_flip(false) + drawing_effect(0) { tilemanager = tile_manager; @@ -47,7 +47,7 @@ TileMap::TileMap() TileMap::TileMap(const lisp::Lisp& reader) : solid(false), speed(1), width(0), height(0), layer(LAYER_TILES), - vertical_flip(false) + drawing_effect(0) { tilemanager = tile_manager; @@ -87,7 +87,7 @@ TileMap::TileMap(const lisp::Lisp& reader) TileMap::TileMap(int layer_, bool solid_, size_t width_, size_t height_) : solid(solid_), speed(1), width(0), height(0), layer(layer_), - vertical_flip(false) + drawing_effect(0) { tilemanager = tile_manager; @@ -136,8 +136,8 @@ TileMap::draw(DrawingContext& context) { context.push_transform(); - if(vertical_flip) - context.set_drawing_effect(VERTICAL_FLIP); + if(drawing_effect != 0) + context.set_drawing_effect(drawing_effect); float trans_x = roundf(context.get_translation().x); float trans_y = roundf(context.get_translation().y); context.set_translation(Vector(trans_x * speed, trans_y * speed)); @@ -234,19 +234,6 @@ TileMap::resize(int new_width, int new_height) width = new_width; } -void -TileMap::do_vertical_flip() -{ - // remap tiles vertically flipped - for(int y = 0; y < height / 2; ++y) { - for(int x = 0; x < width; ++x) { - std::swap(tiles[y*width + x], tiles[(((height-1)*width) - (y*width)) + x]); - } - } - - vertical_flip = true; -} - const Tile* TileMap::get_tile(int x, int y) const { diff --git a/src/object/tilemap.h b/src/object/tilemap.h index ecf8d1030..2c1960a9f 100644 --- a/src/object/tilemap.h +++ b/src/object/tilemap.h @@ -61,10 +61,6 @@ public: */ void resize(int newwidth, int newheight); - /** Flip the all tile map vertically. The purpose of this is to let - player to play the same level in a different way :) */ - void do_vertical_flip(); - size_t get_width() const { return width; } @@ -86,6 +82,16 @@ public: void change_at(const Vector& pos, uint32_t newtile); + TileManager* get_tilemanager() const + { + return tilemanager; + } + + void set_drawing_effect(int effect) + { + drawing_effect = effect; + } + private: std::vector tiles; @@ -96,7 +102,7 @@ private: int width, height; int layer; - bool vertical_flip; + int drawing_effect; }; #endif /*SUPERTUX_TILEMAP_H*/ diff --git a/src/object_factory.h b/src/object_factory.h index c9a57e099..7ff6028f2 100644 --- a/src/object_factory.h +++ b/src/object_factory.h @@ -31,6 +31,9 @@ using namespace SuperTux; class Factory { public: + virtual ~Factory() + { } + /** Creates a new gameobject from a lisp node. * Remember to delete the objects later */ diff --git a/src/scene.cpp b/src/player_status.cpp similarity index 58% rename from src/scene.cpp rename to src/player_status.cpp index 429abd07d..ba8b633c7 100644 --- a/src/scene.cpp +++ b/src/player_status.cpp @@ -16,15 +16,16 @@ // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - #include -#include - -#include "scene.h" -#include "defines.h" +#include "lisp/writer.h" +#include "lisp/lisp.h" +#include "player_status.h" #include "resources.h" +static const int START_LIVES = 4; +static const int MAX_LIVES = 99; + PlayerStatus player_status; PlayerStatus::PlayerStatus() @@ -64,34 +65,50 @@ PlayerStatus::incCoins() SoundManager::get()->play_sound(IDToSound(SND_DISTRO)); } -std::string bonus_to_string(PlayerStatus::BonusType b) +void +PlayerStatus::write(lisp::Writer& writer) { - switch (b) - { + switch(bonus) { case PlayerStatus::NO_BONUS: - return "none"; + writer.write_string("bonus", "none"); + break; case PlayerStatus::GROWUP_BONUS: - return "growup"; + writer.write_string("bonus", "growup"); + break; case PlayerStatus::FLOWER_BONUS: - return "iceflower"; + writer.write_string("bonus", "fireflower"); + break; default: - return "none"; - } -} + std::cerr << "Unknown bonus type.\n"; + writer.write_string("bonus", "none"); + } -PlayerStatus::BonusType string_to_bonus(const std::string& str) -{ - if (str == "none") - return PlayerStatus::NO_BONUS; - else if (str == "growup") - return PlayerStatus::GROWUP_BONUS; - else if (str == "iceflower") - return PlayerStatus::FLOWER_BONUS; - else - return PlayerStatus::NO_BONUS; + writer.write_int("lives", lives); + writer.write_int("distros", distros); + writer.write_int("max-score-multiplier", max_score_multiplier); } -unsigned int global_frame_counter; +void +PlayerStatus::read(const lisp::Lisp& lisp) +{ + reset(); + + std::string bonusname; + if(lisp.get("bonus", bonusname)) { + if(bonusname == "none") { + bonus = NO_BONUS; + } else if(bonusname == "growup") { + bonus = GROWUP_BONUS; + } else if(bonusname == "fireflower") { + bonus = FLOWER_BONUS; + } else { + std::cerr << "Unknown bonus '" << bonusname << "' in savefile.\n"; + bonus = NO_BONUS; + } + } -// EOF // + lisp.get("lives", lives); + lisp.get("distros", distros); + lisp.get("max-score-multiplier", max_score_multiplier); +} diff --git a/src/scene.h b/src/player_status.h similarity index 72% rename from src/scene.h rename to src/player_status.h index b8d55963c..760ae9212 100644 --- a/src/scene.h +++ b/src/player_status.h @@ -16,18 +16,28 @@ // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef SUPERTUX_PLAYERSTATUS_H +#define SUPERTUX_PLAYERSTATUS_H -#ifndef SUPERTUX_SCENE_H -#define SUPERTUX_SCENE_H - -#include "video/surface.h" +#include "lisp/lisp.h" #include "timer.h" +#include "serializable.h" -#define FRAME_RATE 10 // 100 Frames per second (10ms) - -// Player stats -struct PlayerStatus +/** + * This class memorizes player status between different game sessions (for + * example when switching maps in the worldmap) + */ +class PlayerStatus : public Serializable { +public: + PlayerStatus(); + void reset(); + void incLives(); + void incCoins(); + + void write(lisp::Writer& writer); + void read(const lisp::Lisp& lisp); + int distros; int lives; enum BonusType { NO_BONUS, GROWUP_BONUS, FLOWER_BONUS }; @@ -35,16 +45,9 @@ struct PlayerStatus int score_multiplier; int max_score_multiplier; - - PlayerStatus(); - void reset(); - void incLives(); - void incCoins(); }; -std::string bonus_to_string(PlayerStatus::BonusType b); -PlayerStatus::BonusType string_to_bonus(const std::string& str); - +// global player state extern PlayerStatus player_status; -#endif /*SUPERTUX_SCENE_H*/ +#endif diff --git a/src/resources.cpp b/src/resources.cpp index fc8c038cb..f2c3c0a17 100644 --- a/src/resources.cpp +++ b/src/resources.cpp @@ -24,7 +24,6 @@ #include "app/setup.h" #include "gui/menu.h" #include "gui/button.h" -#include "scene.h" #include "resources.h" #include "tile_manager.h" #include "object/gameobjs.h" diff --git a/src/sector.cpp b/src/sector.cpp index 655eb22a3..4037f35fb 100644 --- a/src/sector.cpp +++ b/src/sector.cpp @@ -28,6 +28,7 @@ #include "app/globals.h" #include "sector.h" +#include "player_status.h" #include "object/gameobjs.h" #include "object/camera.h" #include "object/background.h" @@ -56,6 +57,8 @@ #include "badguy/spike.h" #include "trigger/sequence_trigger.h" +//#define USE_GRID + Sector* Sector::_current = 0; Sector::Sector() @@ -694,7 +697,7 @@ Sector::collision_object(MovingObject* object1, MovingObject* object2) void Sector::collision_handler() { -#if 0 +#ifdef USE_GRID grid->check_collisions(); #else for(std::vector::iterator i = gameobjects.begin(); @@ -738,6 +741,10 @@ Sector::collision_handler() bool Sector::add_bullet(const Vector& pos, float xm, Direction dir) { + // TODO remove this function and move these checks elsewhere... + static const size_t MAX_FIRE_BULLETS = 2; + static const size_t MAX_ICE_BULLETS = 1; + if(player->got_power == Player::FIRE_POWER) { if(bullets.size() > MAX_FIRE_BULLETS-1) return false; @@ -826,14 +833,13 @@ int Sector::get_total_badguys() { int total_badguys = 0; -#if 0 - for(GameObjects::iterator i = gameobjects_new.begin(); i != gameobjects_new.end(); ++i) - { + for(GameObjects::iterator i = gameobjects.begin(); + i != gameobjects.end(); ++i) { BadGuy* badguy = dynamic_cast (*i); if(badguy) total_badguys++; - } -#endif + } + return total_badguys; } diff --git a/src/sector.h b/src/sector.h index 5f31dc96e..39b13bce4 100644 --- a/src/sector.h +++ b/src/sector.h @@ -23,10 +23,10 @@ #include #include +#include "direction.h" #include "math/vector.h" #include "audio/musicref.h" #include "video/drawing_context.h" -#include "defines.h" using namespace SuperTux; @@ -46,8 +46,9 @@ class TileMap; class Bullet; class CollisionGrid; -struct SpawnPoint +class SpawnPoint { +public: std::string name; Vector pos; }; @@ -100,7 +101,7 @@ public: bool add_smoke_cloud(const Vector& pos); void add_floating_text(const Vector& pos, const std::string& text); - /** @evil@ but can#t always be avoided in current design... */ + /** @evil@ but can't always be avoided in current design... */ static Sector* current() { return _current; } @@ -138,6 +139,8 @@ private: public: // TODO make this private again typedef std::vector GameObjects; GameObjects gameobjects; + typedef std::vector SpawnPoints; + SpawnPoints spawnpoints; Rectangle get_active_region(); @@ -147,9 +150,6 @@ private: /// container for newly created objects, they'll be added in Sector::action GameObjects gameobjects_new; - typedef std::vector SpawnPoints; - SpawnPoints spawnpoints; - int currentmusic; CollisionGrid* grid; diff --git a/src/serializable.h b/src/serializable.h index 3fc702004..06a8234e3 100644 --- a/src/serializable.h +++ b/src/serializable.h @@ -19,8 +19,6 @@ #ifndef SUPERTUX_SERIALIZABLE_H #define SUPERTUX_SERIALIZABLE_H -using namespace SuperTux; - namespace lisp { class Writer; } @@ -28,6 +26,9 @@ class Writer; class Serializable { public: + virtual ~Serializable() + { } + virtual void write(lisp::Writer& writer) = 0; }; diff --git a/src/supertux.cpp b/src/supertux.cpp index 81856f6e6..06cbbe733 100644 --- a/src/supertux.cpp +++ b/src/supertux.cpp @@ -25,10 +25,8 @@ #include #include -#include "defines.h" #include "app/globals.h" #include "app/setup.h" -#include "intro.h" #include "title.h" #include "gameloop.h" #include "leveleditor.h" diff --git a/src/tile.cpp b/src/tile.cpp index a57d3043f..0edef84d9 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -27,8 +27,8 @@ #include "app/globals.h" #include "lisp/lisp.h" #include "tile.h" -#include "scene.h" #include "resources.h" +#include "timer.h" #include "math/vector.h" #include "video/drawing_context.h" diff --git a/src/tile_manager.cpp b/src/tile_manager.cpp index f2c95e09d..e3a5b59a6 100644 --- a/src/tile_manager.cpp +++ b/src/tile_manager.cpp @@ -31,7 +31,6 @@ #include "tile.h" #include "tile_manager.h" #include "resources.h" -#include "scene.h" TileManager::TileManager(const std::string& filename) { diff --git a/src/tile_manager.h b/src/tile_manager.h index db8a70ccb..961e07751 100644 --- a/src/tile_manager.h +++ b/src/tile_manager.h @@ -81,6 +81,16 @@ public: { return tiles.size(); } + + int get_default_width() const + { + return 32; + } + + int get_default_height() const + { + return 32; + } }; #endif diff --git a/src/title.cpp b/src/title.cpp index 3f0f3a5e0..514479d77 100644 --- a/src/title.cpp +++ b/src/title.cpp @@ -36,7 +36,6 @@ #include #endif -#include "defines.h" #include "app/globals.h" #include "title.h" #include "video/screen.h" @@ -52,7 +51,7 @@ #include "gameloop.h" #include "worldmap.h" #include "leveleditor.h" -#include "scene.h" +#include "player_status.h" #include "tile.h" #include "sector.h" #include "object/tilemap.h" @@ -246,16 +245,16 @@ void draw_demo(float elapsed_time) world->play_music(LEVEL_MUSIC); - tux->key_event((SDLKey) keymap.right,DOWN); + tux->key_event((SDLKey) keymap.right, true); if(random_timer.check()) { random_timer.start(float(rand() % 3000 + 3000) / 1000.); walking = !walking; } else { if(walking) - tux->key_event((SDLKey) keymap.jump,UP); + tux->key_event((SDLKey) keymap.jump, false); else - tux->key_event((SDLKey) keymap.jump,DOWN); + tux->key_event((SDLKey) keymap.jump, true); } // Wrap around at the end of the level back to the beginnig diff --git a/src/worldmap.cpp b/src/worldmap.cpp index af6742148..80a21d773 100644 --- a/src/worldmap.cpp +++ b/src/worldmap.cpp @@ -43,7 +43,7 @@ #include "worldmap.h" #include "resources.h" #include "misc.h" -#include "scene.h" +#include "player_status.h" #define map_message_TIME 2.8 @@ -1087,8 +1087,6 @@ WorldMap::savegame(const std::string& filename) if(filename == "") return; - std::cout << "savegame: " << filename << std::endl; - std::ofstream file(filename.c_str(), std::ios::out); lisp::Writer writer(file); @@ -1111,16 +1109,14 @@ WorldMap::savegame(const std::string& filename) std::string(name + " - " + nb_solved_levels_str+"/"+total_levels_str)); writer.write_string("map", map_filename); writer.write_bool("intro-displayed", intro_displayed); - writer.write_int("lives", player_status.lives); - writer.write_int("distros", player_status.lives); - writer.write_int("max-score-multiplier", player_status.max_score_multiplier); writer.start_list("tux"); writer.write_float("x", tux->get_tile_pos().x); writer.write_float("y", tux->get_tile_pos().y); writer.write_string("back", direction_to_string(tux->back_direction)); - writer.write_string("bonus", bonus_to_string(player_status.bonus)); + player_status.write(writer); + writer.write_string("back", direction_to_string(tux->back_direction)); writer.end_list("tux"); @@ -1169,21 +1165,19 @@ WorldMap::loadgame(const std::string& filename) savegame->get("distros", player_status.distros); savegame->get("max-score-multiplier", player_status.max_score_multiplier); if (player_status.lives < 0) - player_status.lives = START_LIVES; + player_status.reset(); const lisp::Lisp* tux_lisp = savegame->get_lisp("tux"); if(tux) { Vector p; std::string back_str = "none"; - std::string bonus_str = "none"; tux_lisp->get("x", p.x); tux_lisp->get("y", p.y); tux_lisp->get("back", back_str); - tux_lisp->get("bonus", bonus_str); + player_status.read(*tux_lisp); - player_status.bonus = string_to_bonus(bonus_str); tux->back_direction = string_to_direction(back_str); tux->set_tile_pos(p); } -- 2.11.0