From 0f8f76554bdb7ab163c211152e2b8ccb4cb996e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Sat, 5 Aug 2023 04:10:40 +0200 Subject: [PATCH 01/14] only show allowed directions for all objects instead of all merge all switch sprites into one with directions add up and down switches --- data/images/objects/switch/down-0.png | Bin 0 -> 10406 bytes data/images/objects/switch/down-1.png | Bin 0 -> 11221 bytes data/images/objects/switch/down-2.png | Bin 0 -> 11605 bytes data/images/objects/switch/down-3.png | Bin 0 -> 11695 bytes data/images/objects/switch/down-4.png | Bin 0 -> 11604 bytes data/images/objects/switch/left.sprite | 37 ------ data/images/objects/switch/right-0.png | Bin 6170 -> 0 bytes data/images/objects/switch/right-1.png | Bin 7240 -> 0 bytes data/images/objects/switch/right-2.png | Bin 7716 -> 0 bytes data/images/objects/switch/right-3.png | Bin 7772 -> 0 bytes data/images/objects/switch/right-4.png | Bin 7657 -> 0 bytes data/images/objects/switch/right.sprite | 37 ------ data/images/objects/switch/switch.sprite | 137 ++++++++++++++++++++--- src/badguy/angrystone.cpp | 1 + src/badguy/badguy.cpp | 27 ++++- src/badguy/badguy.hpp | 6 + src/badguy/dart.cpp | 4 + src/badguy/darttrap.cpp | 1 + src/badguy/dive_mine.cpp | 1 + src/badguy/fish_jumping.cpp | 1 + src/badguy/flame.cpp | 1 + src/badguy/flyingsnowball.cpp | 1 + src/badguy/ghostflame.cpp | 1 + src/badguy/ghosttree.cpp | 2 + src/badguy/ghoul.cpp | 2 + src/badguy/iceflame.cpp | 1 + src/badguy/jumpy.cpp | 2 + src/badguy/kugelblitz.cpp | 1 + src/badguy/mole.cpp | 2 + src/badguy/skydive.cpp | 1 + src/badguy/spidermite.cpp | 1 + src/badguy/stalactite.cpp | 2 + src/badguy/willowisp.cpp | 3 +- src/badguy/yeti.cpp | 1 + src/badguy/yeti_stalactite.cpp | 1 + src/editor/object_settings.cpp | 39 ++++++- src/editor/object_settings.hpp | 5 +- src/object/conveyor_belt.cpp | 15 ++- src/object/conveyor_belt.hpp | 2 + src/object/ispy.cpp | 16 ++- src/object/ispy.hpp | 2 + src/supertux/direction.cpp | 6 +- src/supertux/direction.hpp | 2 +- src/trigger/switch.cpp | 85 +++++++++----- src/trigger/switch.hpp | 13 ++- 45 files changed, 326 insertions(+), 133 deletions(-) create mode 100644 data/images/objects/switch/down-0.png create mode 100644 data/images/objects/switch/down-1.png create mode 100644 data/images/objects/switch/down-2.png create mode 100644 data/images/objects/switch/down-3.png create mode 100644 data/images/objects/switch/down-4.png delete mode 100644 data/images/objects/switch/left.sprite delete mode 100644 data/images/objects/switch/right-0.png delete mode 100644 data/images/objects/switch/right-1.png delete mode 100644 data/images/objects/switch/right-2.png delete mode 100644 data/images/objects/switch/right-3.png delete mode 100644 data/images/objects/switch/right-4.png delete mode 100644 data/images/objects/switch/right.sprite diff --git a/data/images/objects/switch/down-0.png b/data/images/objects/switch/down-0.png new file mode 100644 index 0000000000000000000000000000000000000000..c83a2e694db09cf18bf48f143b9ef06bf8e3ccb0 GIT binary patch literal 10406 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

93Z+8Q z#pVN(v>z^iLB|N5dYGP>X}u_y*yto-ZItrfMyzSOE1arR+-L`lqar1lUicKBr4$Go zot%uz$;o+4yl#M!qCJ&po@ZXg{TC>!0*$bv#Z zjQyUe1VN#~X8iIn1Jcz%CR5tX%xr0SnMxi8d$qm0TbC*8QB?QF6?`^f_^>yl>a(jH z=i74;Up1-oCK?X|rOqnu`6%7c0F`ko2Pya;eEl@xtB{zKlw|+X%uKY@&XSc4iEW$S z+`HC%Z4Wmn^;G=Zw{MX&`0HwUtY&6akUn9O127Eczg8Gf7JspeL~hahc7=6QP~L}c zEhfeK(rXh9v^5rVNFWBbN@Z(P&czHc58qSlSB zGff`0ja60Ro}05>Q*YY?D(2=8n2aIvqcg7eb}$UgK<$cevB?IUElimhyQlU3S>Yw$ubX+} z)d=+uMCf^2!wf+5Vx^3;i%Vda-R5gU!IagtwI?GZBSUqLBZEsz4sXWCof?{&Hhiy} z?alY5YKFS0sPL`c&&m!QS;r+ICM9zl>z$^jr`gV)RfA=-HZ(NIS6*q8*{tOo>0I2d zrLHHHzaY2q{ry1F#HrFU62OgI#j*qMW&MEXb3KFemO_5}JL{o7>(RBXqvtg)zRpws zm=L5)#duSf^u5$M^D0(bA1{IApkRHSmh;><4BA~t(R*Wh1?AiRd-K6OX=y4Q#Z)ir z>f$3d-!x9Qb3VS~#ygxAj$(@LCjr@E8S1i-K{zJacj>8Q#O88x9XxXuMT4=x1;cgRR&zhF3yLI%0?e;-*8QR KjY>7Uu>S(u+g@`3 literal 0 HcmV?d00001 diff --git a/data/images/objects/switch/down-1.png b/data/images/objects/switch/down-1.png new file mode 100644 index 0000000000000000000000000000000000000000..7eaf5584f8e031c2f49c3a86fb7bc48c83020c20 GIT binary patch literal 11221 zcmeHrby(Eh*7nfdDWO9*4Bg$`NXyJHzz_q%AR!=~lF|(lf`o)1AdP|`NGghybPFOV z`40G;=kfX8^PTrQ*Y&;sow*qJ?Y-}HueI;J*8Z_~jDemy5k4J0001D;)KD=({RN@E zKwNCpZ22=j$I006$9R>C8vp2lKUGJ6_Gpw`P8h0v(9;b?4Q?04eHxscEYa~^wu^N;Dn)#-ayw5NX71$mQe{p%mHIyRn*0;f3uw679CYz-@!l|im9D-Bkx}ILR@a>Mw>>?*j*=-Ye+M%FwBhbnB z`5zY(AtQ!wq|5D&f}pQjU&bcEIK57WOKexZ}Gi#V4j8+~~p6OYu~D9e}id$%#SPczeXvThRaUB$s0o znwK5@Qyr}l&q3N74`+TNKi}RBvfnL#nMHePICsY3rB-q3Za>25{UD`9esxFO!YXtD z9@cWhh@Sh2*l3XTGneBu9z9na^vW96V5e7%7L@yUz11Ot_j4aD>*hSV`&7!|Er0cM z%PhpLdJ)$l_*>Pd)uujrz$t*isXbRujd9fc^PB95{xq*Ak5z@H6WnB5Tl`0Jpc#)n z>-vxC^mL2%M2me~_9&X0S)x9DwpdF*s0;U(&0>V09hxicR#5eS`QX75-e4v_iKE3* zs;4P&XJqhbJ5xQLha9ok<14BupXZ#_)mq7SUGm&MjO$Xy9E6*?P+e4 zx@~K5RTsioP&WP1gne4hR_KxuvreO{B&=VUDoy%=^Wh2}R5E93e@*J{-N1u$6ACS6 zj(aCEBA=683zMCEJV_`1$JZ_bYQeXsPnpCf67$iv#GX;CVgm6_AcEYG}T{LAf0DQEIn z{7;)3r##M5|;X)@l%GC*4&Wa^gG}ksq zs0zOulOgQYiLA(vk(+qoLr1ze!>~t~d{gS&XAy7~)6FQq+S|lIyDyuGxutMi@Pd{5 zR2*#i>dC<1?9VY`Ir#H)v%4=E&g&9R>3#1VCpe`N=^iYVzGH1xH5uHBF}@Ha@C0(- zh?&Z_5$XI{`ORkPM%cQnpdpr^a{l7|V@`t|P1QTHB zNT=g8_jv8&&Pvj1Y8nC!SUBE_PoKYrhM^TiPOg5zN9a80+KFz25I1iHtgZ9ltZ7Dv z${Mc<=RYr_o=Q!4>OqR24hF@6(dt~$vir8fKVcZ*b-Ca@ESGkP?L|7p@={ulE!sSn zp&O;~wO4|*<+W--e^hM7oW8faE6y*7H(lfdoLjHBv&9?FF^lx2ZXKzgXfcA~Aog-4iI9S(=!q ze_<^cabqEIW--uoab<_$AaWo$Rv9Y-pOPlMPCrlR!BdQ^AOrO3_eoiueK@R>D$ju9 zkyJe`Us!?P`etI5)&qO7@VjukYht5|F;^9s6y6@v(~sZ5zWtPyu$Rl(S~F=#*{gTunC;t3?3C(y*{%x}IJLw5 zU}&v&THn!^Gf$ogW3R)&Fwb2qk?P`ipEe?-ViS2W!U^%=sm@MayNMa&YT(7paSeh% zgrcG^rtFI__;(jI=V~Rb`(l%jQ8vl^8h}&$_ICy4k$90r@!sog|A_J*hr=e+Hl zxGT6WUDtcU7IU#DtN6y@%r}Q0lG&?P5)`lP8r2dFX4#uv$RR9H5+}(`k_ze z;~7>j^yK~Qd*XM&jJ7_Tp7w^`SxFh%8sv`iT2nT(49bvh0sU9R4Yv}=<701Y?-q^m zDq$Ymk$WW%K8r}@03U8iQVw8TB5kk_B1EG}MX>hooeWb}di>9@sO(-8)X?i+p ztJJwj+9sDYGg|A&U3HI6@}Wp3JqgqK=ACFej2x2>gZ1?76JiSl9UAZQd=TM!SaH-N z;A{s0s1t-bf{FnIc#?QLeR@wn0z3Jq&D)*V$2*^ASrhp=&T(t%94sEz!f| zzgJ9_Ec7Mf<_-2aBWh|(CQ!&!WhmhTHUhYu5$ptAP%beWN(Xkjf%T(zHj`x_l6R|H+?gt?prG06CBsq?lOHY>ZHr{o>qIisIg6^=V$@K zHZ3=GS#s7jVEJ=L%%=qO=B8RY+zGq5|7DE-Ji$aGsEHo+p8SxlpAh;ncySQYqd1#fcS6yvTKK-y@Nnfd zg&tuw0lxnYlao@G89~tf6O+K#OKY4TpI4jo;nGh67)yleg=8BgK#$CtYAo*Mge>bQ zVp3S9NMepuNAIyqC|uar7}7qpzHimHkT|8nKknfaA3Myv^NIvnI@%qQ zJ!_gFm7#QIzQEGbQZ7Ox6LP7H^ATs_XHn>AcA89fPn1_L6?!|N?Yg>{>b9XGi5~$G z4Yu4+-m5Y*7Sl}iA^|o4!IKY7?sUb7At}oQrCSFdUJ?@=sI*Yk9%a$k>obSqRTxra zONhr^aCu^8F7EtPwW}g}I{R)9cN4 z-gUrjwR3M16KXZz*6@5@OgGB6UZzAm{5*~hz!~Jon%ydyWS0(~R6b-P(yH~M!iwPP zxm}L;DFi1D#w_e;w#IBl!5A|QUMrv7kt(^b?{Lg48oqf4iT!6;_ zUiipkq>DyVZ=e`bdanqF^3iJ%Tx$!#OmBofMHsz%xlB%JEjNk=l?|?G2lX z6FPIn+4&2=#OcO023QUil@R2!wCZ-W|VFF^Rj&p4scHv zB@AuzSDG9`Pi{_j-(SCYb&xO5JxyHKq_Ni@iuOto?As=@RONd+2{z84603*(Q{%E} zUHdb#r)SwnmRI>SN~DmD0L`x(pw_dsz}wp*te^Id+R1GkD^)3a1cW=rkaiZT5?{I> z!=6&Tj&V|f_8JgA;wNnU5mVrPZ0~$n9d|BE^9<<*_K~5i^L>0OJgs7nKIFMA`7>*B z1!I$F3}o=UHS5sS0xcEe#mY3H!46M-NFi1X%fS2bR7Lg%|Lk_+oCOjNpEH$|LB04D zy^TV_4cXCdh0aljV6jl6dGeoKcpDdJf}bWf0uXhp#$;KD4Thv82s*2`Fz8TeRfM``vz$3(wSrZNBlomrre0gbb zn&rxD_C_Ob7_>vWhbalZIXa(auP*a!cB~%F-h?Xu{8UN&K?4iGUojKM#iMPI7o;(t z7WNsSo0Gg7$`DOi_Ijm|o?L88ca=i;#YIR&ecq z{Ysskj+9lFqeje_ajZFUk^1v4X|i{vCkioOEKhqs6Mn@RE==G?X1I*( zSKf*0MKX433pu27MjbSS`(Yi7^4Ov>AhYIlSJA?cQ~pg1(SZB9)BE=J2Gg~fw1lIM z+d{cW3d!?x^>5<*smpyY^3chHk>FkuV0E*9bi~v<=UsYM`5=^vd`-sUW>^56`urcD78IIzR_`h#X3X z7!A1Za~ga~^?i%BSYVfG{8B+-N3p4nZ%li#;`t%Tc2ixO=XmPee)0fKiNpq@h7Auf zKi{i7ourFkKFu3!=#yr&={=F-Pzx%X+5+T4xy)|9>dXoNur z_v`my7;>`k@KP~X5$*fty9DODzHz-5D*||SW9>$*89M6{&1w^*DM$i&vX3=kwX4Vl zQ)~$958lU-0~M1lgE5~HAA#g%t@7+8v+*9mfkj`Wo0_7P4(^k;*(s&Uxw2NYZ{+em z3Db6I4+yiKYPq94HHA9M^)``dNp*}8#a6ODkNqN-_O=9if48->Nl*3mk|X5(Y};@j zr?rSc0B+{6eL|H&nQAi-%zX`%VJLRi>}Gf zjY;j%c*KBox#b%ufMTJ7sVW|NVak(r*;syIQ%g1ZB3?sXOFEc|^~W-yl~ITWB`y^V zs~&HVG@*Dp!a}i5^dTR31O${xAKnxb1e6g!Mk2F5{3umYcr zZRON9=WIbjax-W$#?_wg-tyL)YUpR@(FGE$I-S?FDHE**JHoE zZ@H98=R?H1q>$%YP+qopR3?S(|0VVIh^lu4tqYE*iUSw@ighu3#28k^P@fb={nu zqzXM9Y}J0;_UJ^1?&h=68x&{ZGHMj#2NLx)m$F*84B76=Kfe+uz|FZsb;ms$Db=zz zUC7ul1{Blawl46vH%3Wl#+1QdANPP=+6&%P-r|i=+c-M8Y4cIuPvl5||Ho7HDD}Q$ zOJNs?b1-3~QNpuP?aP4Bx7`>U1!_wDCgCxjVGqL2nr|0vEo14)@3v65AjP(hU(qu9 zT32n54$Dw(ycHMahfeOD(;a;-Edy?md3P@5zWPLp6**SZOSZZM$J-x+pIh+S?N@{) z=`boxRjV?lvNGN)>f8#O$0P+1NYURN0L&H~g9DZYxWc0LLmL)r4bAa-GYY0L8h6E$ z#VFK^+Xvw{rYpWTWo)91s!e<2P4phduyl?^nNmH%-|iMpH24NR8S=ry2>ylhIR zu8^2k>|;{)u3sP^3v0A4dj>s)Vd4=T)Voam=< zkBZ|`eGx`ZWJ@7K=Rfv-KG>C|`6|w>Big2hX4>!&Da2&&^;Bt{3XHkfOY6Yuu<2Et z8R{4~Ys@ialk4brd~PADHD@2J56p+Pg2~)T_1!F`)&jq9@@V4kOYTQ2(y#_Cw{9QM zuNC(O<=+q$+VK0rS!b|q*Aiy&#vq0x1{zyw*df1F-{Tk8U|CW3UiK#ei=P`TYpW4E zxsN#=s1*E=ZtzK$QtrKYN(#7?zV8nlhE5IsENd9ZleZ9`DrE`hGHkvii^90KHTc|;MUB&Z$lo# zBv2Nv2v7ze3!juRjQS8DYg@cr@|F(K21cul#JjqgK-MMO-iba@(rZWV`Yu881@WFO z2^W^9002oG9CdAMuB#&jb$8BOD>iyw}#r z420XuGT#=}1?hSy!<^t6{+=*ne?1eZzYA2-o>^WFU&dDoMc@WQf`Ptnt_UwFUs>jB zUMbY`Rkr{$@EU@2k!3d5H2^BRd%}Ps{384yK2=}%Jt1Z}e4vb{y@QmIirOC(sFp0V z6B6kmB_QDAIwHi z!rc+TD^4)P-5V*(%#0cb{y~l!g{u0ZI{$ER)xM_pLfQ*xq8cKo`Jey-LLiU?A4rH# zNK)W;d(^0|?w{5OuRl~o=_%j~_7D){2MM^j{msG)se11(fB)3N%LH|MFJJ`oa`*Ox z!c_0U5J;BaoqD)>d;RXy+Y5Hpbse{>y@LQMsB6!^+o)^m8vJQ5VZUPv$k3cI30k@Le*JoduklEPqnNj?}9BFHBK0!#2gL`A`TPzON?aS#Lq z5(YtjqtN$+qbdRH`g>GYRQ4z;K?w;-2SHI`J{U|A6^$5JkPi$M0;8Bgl462F!ccJ` zk!z}}b&ygt(3E8s;s^cJW8ex#I=Fkf$ujG}5#GLk4Vb{)V8%%Bm1u%ul7gr-6cQ2> zkrbB{ll%*02J`enCHWPnAc$Y+*SzhaQfeqhFe=&LZeT~4fCs|y+T?0kq)?MV2@Afe z4HWZrKWZ*g%APPV(%sX<-Q87|`6^c673FnF0cHLu7AYNf=(XWB7-oM}Z+}#r64+7T zx+x>@ufYF}$=J!=2l4;L^B43_7DZ2_kGrRfzNbFK83slEYo31w{*%cFwbyweJ^eKQ zhe`b(IGJDRs)4d~_w@VCzcK9Yuhy>($rXMr6%cs62}psVzvTA<--Fp-F9Ax&UqetQ zFv1ar+HLf}wJR2P7l~0x|zuUZ9M?)rR_y>B(Ge zIl8)1zY8F9wIxYuUS*!Kw}*!-9On6VVg8XQ{}H8nK{v+4FrNF-h{!e!ON3MTMfqx79pX~boCKvu+Cu|r3^^cwp>ij%u zDoBVrdt*bi)m5%f;eemYZ4XdScpe%SUH||;%heanw^aTfsuKsPsjG^!hKq$JL^rl| zZVCWkrE01unwaa;#+cO7N(|CiIHH=bQn{{kRmP`7h5q5ckHcb$qf)pik|BQdTmFGjkHl`f27F%2#pCdwm8@bUkR79lz8+& zC4yAXP-{>7-O5F(5r>&no|;^Nx~?u!WMt&~;;|=&gBDykU9=OtL+AjfZ0ugpGZb-b zDc6C`0AWAx zF`U%EbV@VU%a%#Jd4@;{S7I&6s7FNCK&z;d+r$)W?K4{TyR~G!@~a;mc5pU3H4RPm z;v#rx>w6GoyxM-xP0k}lT_7oK;w#AB^0%Lz2Hg10&dvY@^K_WdYc11M{){*SEj*@h zUS3|O`1tr0)9_UCR3kvJ+YHr-!^03Gd{slz#Qw$a{te+tGJuZO*48|)8Y(sip4j@- zp%s624vqp7&?$3kpj-ih_Vr-|0Zl+NVzy^}-HAC_1Bn^defTPlM!Pr-Fc3q*;RGZF z0#faTepa=$-9P>OnISzR13c3-5e;Af=6;LCV=JZf*_#X0;5rB83};tXa=W;=P^`Tx zaHqd@tGwBLUJusran(tr^+X)rq0>FNBYrs|xfQ-GTA z-bMF2(^j=0PFlc4rEzI_dHL-R-+9(X@}zyYrv>{Uf-1~pl=xVn*TC@Lnk^>;BC>y} zJ4@Ix-EHRL@Z|fJj~L{Vs1EgM8X3|1I5)>%URG8D&rb2$tup6j59==UTOTcwKRG=; zuWg|DxW0TIbV&o?r{T~_BW1=fj1a`O`QQaG!(+OuJ24WeR3u$%WdozYHOG2Z{q$*E zX4xAK4vrbL02<<`&N;%S4fZ})14^vCyu9L$j(l`rvc__b?k57*uoY#rv!IP}HbYLz z>91cwC_TWc72{u+Uy+?T2re9-5&@(E8nqvW5^l=kkF*%wbNKp&+&wq^Ea-tOKt31~ zfVyM|_K@I3)99R^ugjuHbR`XT0buLp%VcS?KbkSBpj0YKTF&KQNgfy(#w>JII0yus zpe4=C&ABzXEp>VbyUt(7n|CZbUF?znGQjj6;$H#!I w5>n&DV1aK643~nB=>xd}T-DgA|b>5RhJy&;v-9-mCNw0)#F}=prCUuL4q~cae?=ND*lwNK=}q zH0gqZpn!OT?z6Z1yXQOiJJ0joe+8jJdK_5@VpJPD#d01^@skH8oU? zaGzk@ivviEdpDUnVFdsfr2TYMi2Du;Bx_p2weE=EfcYk@t*%qgH8pj2*X+3b&RN01*&cF(WmWNM8FI}dlw zIPB-~zPkG3`1_7~N7ruGUSM3;E&qkDM=ba6Jv~a7=z4go>tiRe|7Fp5Ywt~)*KQ6L8%v+JzORUY{*DYM|eq)W?vqFU`8PP>NsA#u`^I1M{cS($FPAe{`9Y@}CaFTo+(z$aEEiclY+pXSnd{l` zZ2gg_T%f|u*4kGF!TVwsD$wGPH=nLF)eRO<-#s9<{bJ{K!|$zcdCo)VCm-t#w%ao4 z;p4SG241pLVG_08$`Caku?W6iHH{Eb$(rrFPu_05!nTq@if)Z248j#X7V@-+XDfA$u zJQq(f%uclMS*n&u>C}W)ZDD`5>%&qrW2>E!M*!)v3@cP~^K03=zSlkmx1p7Gdy~LR z8eB=xO_yT*=g$3EPWO>!V)NU>wRPPw>E_q5&95HKr%fcv?sN=4Y~KmtYo^na+gLHy zlixY0eIT=6^5tH>LcYY&Xm(fDMC7WVTX0j_5sjXAYML1ILowH~+NFYmCq7QqapNqd zdNPfanFR9|NF$#N2gtojyl-kJ&#vIh?8o?C-v~RQmBpicIm;ccl*@BlBJj zErdOj#AvY+NfQH)g72;N(;o%Rb7!asO>8s)1Wjt{JGuIo7ew2V&4)3Zw=^ z)K?vnb`W{l%J1A%cVEBd_4HDwYJS`}%C?Z`l&ITIf={qsAMwUmFtM+8(G2KZ6H-B) zc1=iP^y~gGVAj^(OE#i@6?cKn6BSPvU>R?c4_H&<)o4) zO;vCEAi$-SYgUo#)ocBt^hEi}fmN&LBfX{}T;<|c0@W`<>#AsM-Fw`fEkwN98nSZ_Szf z+_a8dEhPCG(q;8iI_KrEA0r}-HOuj`I@M)S@&u*Vvu%NXT&s+wHAJi4b~ZQD%?*<2 zuD*+Px?dc?bSI0#Sf1`l`HfnXVnmXRt(a=y`oX(Mf<0bhztc?W`TO?IoWZ28!#=*d zp=omH^|UG0FeSZxznHCK()FZf$=ms)Iq^hrZ~LP}k)fpc(2@48re|D5+B9AHsXVz4 zhrp$nSNCniyS`QJ+q}ATdtDx4NCZ*2zt|W=UvXGcR9kFOdgBQ3_Pd4cca7CIj}>@A z0gRv6hHqOfKK76J$QoZ#!bek)Lia&%=6gTlHs0=?sSitJo;tU>ccPmds2*(vkBpmB zjozgp3MzZyEa#>xT*c33zR2-L2moj7#dny+7mt}GZ6+zX5ITK(l#;srrYi5UCJRcEmdU8Ag)DC5nPJyer$ z>_h~dALFT5H2)lb%W`!~Zk0FYTjDb%?2$2s%o|%zAzZVc%)^Lj5ilNr1 zh_P2Hp%jcj_h~AAhs7EO8*@M&uHCcBx}ROV%aVo`EkH5ce61%sWf<&uiNn!Aqu4}+z_-gzizPA= zp~WS9@R*2bk@ny{RoScBOO?%J{5!J-aZ2}A{M+ygBpPm6wYkMDgo;w7x$h+C1~HdR zzbOpv=L8r6S@s0@G*V1WZ2i&N$R0v`L|vM$-@fTul7YheFAF*}_}g6V(rfDc zJ36Q3(Xx+~u3s|Q@^p)G?xw1h#mY2PwCM~EU*V2vOfte=Bpyh%*c&#k@7<0nRxMt7 zl^W!7@SLZ=wlcp^s9B;|03x6Mb*iw1L^BU{s(YF2xqQ%T(9Bt5#4ZtqgI{?r*F>AX zr7R~+tPp#3K*4EW9x13+f5!>iL-uq&$zVY56Y_oUr!Xcpy^{yC_secx(Dn;8I=Rs6 zXVC1*J{LFUCH?Fn^@`-dRu)-x-Zd&N$`Ac28ePX1RqMiZtK5*Y32#v>dqj36Yol(S z5qd;%jHB>uCsBlp17jouD;Q9bbTo6~s~<_6^3CSOPXC(kN6%FCdv`+!r!5+V`YrU2 z2wR;hA)Z?WgRBM}#0-qxvS-Be3xh-)FF;fij5L;^^b%5ciQd=K=ZKJr(IxmH3yY(V zwW1{@!%1mJl=P-(D=5gt-c|K>)2!bHh9-ZTneHcjT-dG5OKTZL)1p6OWY0{D>{{&= z(1~8;W2ha7dwW3o{oty870FB)KccYNpK}TGj#UgcmXn zhQCiRFC2%k60tDk^bHvT@ngbuJPbjRYG$18$8<=ITY9cIFi_5nCL-vMzFj;rwHN1p zPSEJ5Sa4g`Ta2EXM&_Y&idBaHP(aV_a1#8o|2-*w*pF4NDF_<8U4G2n>?|C=O_;a>gBtC;kvxY0$Q zPxOez+)Jrs_%@)oj?J3s&&a;eg5z0Td4=|bnR%kVdbI>x$Wag3QEM{O>S zeO;^YHi1NYwFp)u@@sv!x(`|(CE}0&BZ{hlx@eks-S|Xtxg&8l+n_>zH%4O_q-V z@|0ZDby<2NbX-12YoEwo9pxj>$72-R0_r0tvC|h^5iVs~?1}A+A*mtors5PfN=mOM z<#U(D{^-x*Tn~S;GdH;IP|r2=D%Mr;Q9%xwjQF&OE1=+OUW&`+2gUH~Wroz_QQ~}0 zMosDI%Yb%yJT4@cg)(AZ`##!A{wSZd5I03rb!+_66r$G3o)FCW zu=1insMZWEENP;KbC&98c5J63>au|j^CX8ivd=!7o-IKpJTXhLRbJ$Qf!$Ot_+-pd zHHGQQJ(@z&=#tMz%lJBWq)EgMKOz)IGzDxt6Zq&&eq`YrV2N}ejJ}YR1x;EQZuj2` zjPCjjJYJMGw7tLD;uL;-d8#L9{jBfveFcFTs`?g<-GOjCBCWtT$+c(@ep-orofBB# z6JqACY_81g3jw&=%+_i~)2K%lSmy!eHcWU4$Bi!0*T^ zsjjluL~%=gW!AV1i(e5PRC3Tj0@+S(0wMi@m@&p=rJxyi>w;iQZH)pE<@Y`>OsN`t z$sczNa^~k&aV4Q#-cyoSYlww1ZQrTtUUMM+K(oP|+q$Wk<1M2$F-lPoV%n^mM`Vyn zK6w*B7+B*&*+&A?ih&1((OTwMG6D2Tq&A6znM|DDu5N^UZiH~MVEdkjiP;dA&_WEj zD(nF!1V@$8v9TJUgbrWzU^Ct;oc`+lZFQnt{3e6))<;+N!jr}YXY$`ZP-;?fSQJPa zCq%HQC+MiQ8GHl0Y9+~gV)CA9WD`S2EY~lAzvL)Wn6ILyDm}ed5J^J=|Ey!hscEl8 zMo6Czdz$qA9W6Cg!v}6fh@n3aM?1){N{v*OmEj1nyiez$-jk6Lu1Cz8cgbGcTB;W` zmE2EyS24fnDEB3$l7R{VqYie?6TeB}VDO#4_)EH`^4p8_7YL2a**yYD#2ku@vh2<9JiVa0nRQcb-=j2GX!lB(FeR0U8_(2L^Hg0F;9S#f1N4C` z*(sL^wfRjl8QJa?C5MFnAnaYLF^N5txkMU*+>B;iCPk8FCEv;73R1M{Cbzi624G^c zJi=p!B6;S5)(GP)92bdCIMpfvY4_VTfY(Zi5K6lhqT;@9o`rDL=n!A^rGRBR~lCsS9X3fS#A_fhUeK*?6(fA70eZzp;plr{P5h^U~5KagP4R4^_mEY zAYKYzaF%bd{EC2>qF|kw1+5A``z9Z=3?;^ut?*WT*v74#YadZ&!YMnR!M9uywn<8p zMxd~{TnRF~9%dIjF{n4I%L$qOQ~xkBH8ki)nbsTL1tHfy8p*v2+k~cxTd*1>Q^L3_ zYJ3XLW$w(}N0J-Y7y+^=iUn~bueOwEwkop__;K{_i9mhz9C()FmvZ$QA2P|OP^yT6LIdZlil(!O*vuRmx-W*N2hyz9Nwa(S@L)#NPA%+(yUFgiRgg2GIYA?F zhkPOgs_$QI;#KhoOybi|<&d#oF-I_1TZgk?+MSi&`x{FLjtLA7bD zzSJ^#6-=qS&SUki<1u>KWp=Z*vgo(2y({zv`ZES{B=jnEE}lbF-n3sm zr8N|b7J@Vd3N-~G?L)AqBoGd(I`TV0-B%d-tS3VaG#yUDq*Qn}lJEI;k8^E0okC@R zvW-_7qxp@jd6h8CMRrnI-jRIXj8ri0?u?=cJ3`i(OS!C?$*i1BMZOLFwjjn0>zym+ z%BtIE!@~O{Pg-7Pwt%kkx#Trp<&{Z`&L!Auq~VcWEcu*HNjSm!JV8>OAWBGQ+rLyC zIoSP4?+i{5Z=Ow@A-8lso2NJtr5_=;XNTd_TCe96CpSDAg_J7< zDO%|G2{3aUCysrI4!>U8e*|>Comb9wBgT!YrY3p~%0T)72!}*{re_i@asSj=9drAm zV9`~cstipofy;ePDcfm;r-=^w(2EbbPDIJ3`g2pbQ?r6*67-ogj7mOh?6KBCDq0zf zQ1nEJ!<2{WZ)MGCR#NdUQ=l$2#V_2qeWpW_b>#+xyqzoydV4=Rar%YuSDn@B+o5`8 zqsEcBv#WZSRmYix9|<g{O7OSKwRlZyY#i>+>$(>OgPNc4jo`xdS7Jv_C5ehyiuq zOPr2|wp)xp$iO%jTnO6Eh)JypqUuA~O+&K-8l7HuM@`4CkAoh3g{CKS|^ zk_=mkZ%Wm^bmIgDb_mU2fqtL|IsucCL*U^%!HhY}a?IMiVc*uqUXsdsYKE-NefA+YQEwLt8YYiMuXPSGsKI`1Lo2 zv=6!$jGsglv-`Y$udcegc&*ZUiS#b@l%?`F`xZpL=ERQehukYXEdQ9stlPmhT~7mY7gwJ1(ljdLnp|J6PxXE4#=Q@ePG+1{E;zVJ zZ_Fp%LDrw-r+Gq3CpZ0=#)481ZjlikIk?HgpH4;91A)bc<_I-M)v#t}hu)+xz-MZ& zT~(bjk{6=$AFqG%P~Mz8g;QL8QnPe=+_~NEQrLq~W8?fQTH>1=B(4*TZ-lS7zF2lA z3sf9%9#cy(?fb@xPmg^|j5ib3?tT23pP6t^sDKzMIP~H61G=a?b8@iDf<|)|681J} zlTJvUmf2l{u4H~q;yc6x!27A~k2HJ-qx~I7K8Ze%Yo&p3%ZtH|pR@Ro%h?pP z&P21;-Wo8-ytSlycBshI?sP_u-23`;FZ(+i0Du>XQc*F`R8jfIEo9t{xNC^Ovo=7KTrE1O*BfTQo%<+a{AhU#IJib3Z`3Pd-;Q9iw$iw;U4if8+ zfbaTkjv9&d1*@X;p+{oJWvpX<AIu3=qr*EOF14DRuh`=4a9FQXZ9^SZn{s4fSg1N5r2vZ>*2|PmCi%6zPuizy*TFF+=`_$Do}4TA;tp=X~V91%h+;C;#8j|Iq7a zEI)OHsd^!>=T0?Mp@=@|+4sE`q}GIEu?aB^;5`!VXeUCt-0U6f7()CJh&Ml9HBk zfXg^YOT(ppqtN$3;VJ>{{(DsCRE{_*aS5=LBLXTWjBr9Ag~grV(!w%MGLFL1P)R5P zAt?>aV%gbFJbRH|mIpxoi;*k5JSYSF{h@XZ(!AQsRdi$f| zl;O^zKbvx*{|fxyn2cS#{5=2Pc>aR^$)eR=)A!MLa77|8|C;BYf&XMO!tHfv zj8A~(|1hcl11I+@T{UpFUOoZ8`8P)1_|^KgA-SV|O2xtPa}$8U5x?X|!+nvCKbHWf z*{@_)hoX8%W0{!iw=!+u$-czFll ziq-|A@8|h1-TxEdF9sbH0_lnN`d6X<4*4a^Z|5Cc%)k0@=QG^tQS{IA;~!~pUOE4Z zk3Z7wf6)Vu`ae$oEq(tZ*MH>tw-ord!2ijv|H$=kDe!NB|C3$+-{d0u>k1p`iTk4G zhr52Rh@@!4U3(KdXsfIKyb1?=Q)$12J0kVgxQ+$@M7Yjhc>bjdzPL^jjHa#{$r_Ld z4|>HpVax>pAo9~xRWh;Y-7(kKp*Lqe3)vk3QwY8AOz|ux2epSUI9wsJ%5kmdp}fcQ z#6=FiZQ2+mxluUtb?tQ!bqxdpDTP!@aPP!u$I!+b%DIFqc{>wbOx9i9J943fMjR-no19`Hn%v>({UK1qB2`Bq>ztJy^1Ih?RjF{G1U00Pek1 zC|E_61Ej&Pq}n&k@F6yT|Ay$Xhyo7?$Z5@w%!tliohjwjzmHKb9%m=})W>qCcTxSx zlPB{z+1X%O*|>JU-KE2wd2dz{07pdVR@?jBUNFUaTcU|Tq=^6#D7=WzK0YkEn@NyH zSkDA#$fp%@RY4@tIiBIQ7SG*5r^Y>hxmN#AOGT*cYge61GXZPXjUFt30u(bvZ#RzD z+{APmONfsL=w4ZIR4QGo?lPmV{+3Ioti|s!|6v2etgWpLrXa}Ok6oH!AdH~-`0N=W zGYd;$l`AKN|BAS7Z-%gyft6L9wMdkO&>bC~M&-jU<$4c#wd;o8IzZpB8Sfw<{54i2 z61f-n1Q$Ej=1dvC?cpu|jfv924HA;n{L@(TnVOCcR#n1jEN5v1^16MzlBsD$71=bFT(xmlD0PS%_q-58C^Ywb7iTPOR2o=%I1 zkB%-YD{GpYn_Ci4)*|g^0(9TVWjOngdmBQmW(bBr@VegR^ArgI{NV7yIjoJxtV3cE zUt^X|*-d}{ub;?}=67Xjfa`bQ@%QC}B)xa$Bo2;_^l^{fSXIyvdTn%7R}gP3g-hK+ znw1Bj1xm?8xw%y*4lHNpEs4%QvBkX+3Fsn^jeeCUqVx@sPB8_j-4amY=5!k`6aqWoBY(&9|%W>^x&LH4VCi zNC$|-CnVf#cJ-=REO-xi(|liGXJ==3qb+3dU}LiVX!l(bZjoKYW35Fjq;mu?Du`EN1h8adz%j# z^78VwAKy>K)k>PMurQQ~;&|GP{jdRe@a0R4L+K?1Fsc_2>LLy^{!D15!bgu!%gIC` zkQ1v2X5t1t9nF*FEL9a}#!BYk69eAhs>;sp`0So>g+>^U&9r)SbOeo$zrX(i*?<(s zHz|OebnsVjZDnQ3C;!~4sw#Xm8r|i|NIqg}Ztlj$#@6JwYrDC=&H=FLvG=x_GM5bR z{8EEg$2GR1fcJWGiK3!*87djg7N6z2qmMh~eIGbgPm|N&85_H3 zmyV%NrB4)1ZwUn8uc|($%1;vg(F`y)9y~nil~s7D5q+4ti#M7XH>7DFe42%?qLR7B z8aFw~!GE(d(1;rba`kl+TV54RVRn{(HH_f1 z(#S{_b z>|YS}10*KIe(R5(G6MkgL4Nwi7#Q3eh<10iLpdRV7+*9Ji1b0(0RTQzV_7C%Ot&(W ze}H4I;)cX}i91X@yE3}#m`nUFU*y!U8cKp zQRd=Frr(C!ZsEz%yiX8eZcgr??CG11o#r>&ixW)AnOm#r8>YXRN zvw6_@{V+a9W~pYL@SCQsX|iFZEZ*Uu9R2*wR{M}Wm)^{|jU$1+1DWlL>{mzA>s=O= zo7GbDM?pWN4GL;R4vgcv!hC~NVs~&g=U1F|3#Da682m(X>{+9@9COpDl4Lu2g3Z!T zrTJuY&N9*~%FlCX`F7|M7=p;{PWFzwCKS&-c4ui8cRXLp3@%nwbTRGkOrLyf^1kW4 z96MbCj4E>{#b5$vlGzfl7VZCYm7Oh1)NfHFiZiuj0BneXqq}CH?t}h+vV|=O+(c z>keN}?;IyrFU-98R+FDB6}ymM9)QgRY!s z)yUd9lZ9p8JhD}Dewyd^_^fu})nz85nNcD9QH6fMu-1)A?nRm@^>&;1SM)t*O|}eP zWPQd8bN)y5t#!7jZn+Gz#=Sa`h83rG-q6&9>G)}+P+|!S0#0% z9C>&!TI#+xkyz*2D>jhB6B#%Fz46VY>=1nvsB%=*uz7Gr#Tp=;p`)@b`r=r+O{~E` z@pfPy#GN%wDf)1GaYr5b`W4N`@f?H~*YoM}Y{c{giSDQ+_epYv;cG#jdhZ+=H=f<=IXb4^NIUvwfTNs7-z}&*kf;O>=(lEn?n@oR`JXksunQ#`ZRwP#%E^ zOJeq-sWiKiP-n=FXqn6z;*?n{E?gOP6~h{poH+ z_BT1Gg6@;@IkzAs=;re&bZt0&-R8^ko9liIl1Qr(d~#Gxe1%+x{gNkg(WIHcb=|WI z?f;$mbC;%+YmeGv;a9!ili659f3yk4x*iPJ-=CX#Q5E;T%tC0gD)}32k?UdHjqk6! zp6BNIXdCCm*yWV^GDbdN)@!5*gwn{2gb>fK9QP%y<=;A|rD(}zlv*lw|L_%;qlqZh z+G6dTPw>IJbH)2!p1d!=ZGFE(WQ96hDzWjmJ9T79)L!^7Yp+!7gLjdk%yZ1J+%Lo6xSC?_)3JbZZo+dkP(roRuFNNS(k(cxrIm4g(y!{;U$|eCl z^{o%=RY|2D9oFdnQ6#x~C1*4(v=mJRHwbwV4accm#L4R0ijc&GU1{085?6f7OlySP zA%#R(f+t-Lw&X6?bV#xLVoM0xlo_t;ytoWkNP=)X7CgX zn)KT4QV7Z2Ypgv?Wjmr9TOYq%+F%7X;#PcUfI1#g6YK&~2gf)v>jEwT>~nqH*}8rE zEm!1dyL{|RMP@ERZQrZ&x*B)M2U#1&CwY+;$)hpK{G$@L9!ZSBT4}&Z>WyIc9Q2KZ z9#VDup{^28?rpe2!Hu}I&;T!|aFcIbQTXR6x#74)%(%zjFqtj!&t3R+jN7Ye!`6s* zh0f|CT(n(-uPwM2JM}Ik7Pob|wDuNzbThHz@xCS`HQ*k2ec+lc9PCQ-AV?RtQIxOG zYJ`mX4!bcGs*Uucxf^wo@5so#gN?v00@4=9K?7R>cc`c?F_p+!XXI_aBmWX^yoKpP zHER8C4yoY*b3nvvFQZ5%@U@#2XMJyLA3swcRmlycj=VxE`kb@-u&D0JyH&2ECc>1@ ze(WvhDkuXx|InqaDBgRY!w+t4DQK5V4A7*Vn#+ywek;qibg1fV7nW1gQ%luXB=QPw zN~0tt>q@hqTl1hKZRJ>di+EmhO$6ph4wEc>Ob^XW!$jfciM|ZBQeAsE8zO|0wsl7K zaKK%y!m}*M|3nI2)o)5M2KB!jK{P-M9U1I?_|*|ITZ6WXd%Km*njh4%X)kgeU);#c zt=zQ;L%L_hzRi3cxvW!Oyo1;Y!Ly{?aJSVZyPGr-mBcyFlzzv@8m)EjZY>Ahtyy0Q z4G6DgInub;hyr}Bc}B5ZQl74V@1(rFhN4{8Au-|&JV#)mjZ6Db^ZPuV{@dD~|Dlm%D2_QHo4ghi`kJR}e)}ZFw-?QY6 z`bK}W*QwrcI8$8H7H@wp^vUbXrUhxKl@meey98E+ ztK0T25n6b$VgvBH?Q5Mw?fj&C;&6b@wH>#C5oz+PN-LCL@B8yL2V2)wF%s*l=>EnH zySOv2#J8em@q4c*V)y|a{xCS<+}^E8R=W}a{IXk;n4$v?Y;@)wYe*N2xxU+w?W$G! z-30cHTO3o9#MhM|L=w8HdBXgC0S!P!wa@ zSu&*~h%NH5*c(f`Z&_tMsk48|WF zs2RVnh>IFu8?Wdxsnjj;f5dzjnDn%JG=~#rY)gZy)t?4N5}C6b_Jn@k66bQW`i{23 zQxVW(z28WNYcV5lY{(%;TbrZIYO*M>O3=I`UhMzTVq?o0hlhWr*B3+i@%7&DLRp)) z4TY*rB3g1f92}i8m7uFCp2?}`pec+absu>&N!5{LS8y-%(Hnj!RW#wHTEoePMRQR3 zt^x(mZ83?-;oRi2^=d)RfzZOm_HMtE&rMndpdMD043q<5LD!oHezHuxcb(gS#SwIO znKoPC@kXkbNGTZ99toz!(*PP5uA^&vMH2Q{m-^*=+tmX@EDLG3yI!YXVbF9{EV>FK zt~HnHm=U*+EDV#&jJWwBP?xJjJ`n#BN7Pg$c@tOHrx!9oiCbTAn_f1%=c9GJ>C4U) z(3hW{4RL?lUm#(K6uTd0c_aONDES)W{VE%m%7wk#DO-}3yf#xfqH;tfaRr*%0u+6P z>49rGv-H3^9jcWw`#Mw|m*NULeH3zvLH^eL1>BR^Iy)rDoh6ViyxEYZPOU9p^r~3w zs|oSpbcHCuorEC;R&&xlL}>TF(UpV>{E2@ z18t=$LE|R@$PD8#yLU#tX}mt1^=?xdfIrXjR;6RKm(=DO7fE2ZRiY$6H`%h`<0*S- zpufoFKDGFa;r1f`FWTuGEzHe^l*@j)nuWB^$}u-7nr1C~%m#SghLZ=T3YrovJW?<% zWaUH9=uBSPI1d+59C{EuRh>HAZ)2ZPN;ekb^5h|Dhu<3I9YEf3_5;)gt6Z2trXJ;R z6ok8Rz<`$a39(f+g98z(Q^vhMbLH$1b+9PNA8=DbA5EQ;>8NtBz()FB=-!VV(5|H(mnTr3svQKv||^HTnp2 z`rcG2nQ=Mka{iU*!u`Qb+?mNMiG(&kBITsjc%^5ubhw|MH{j=OkY*WYn*@+3J*g^y zn1A19J5N^~qU^f2AH!ecZNK5KL$s5B%5Y012xWb%eTt?rmzR*1`^vgp6I(Q~PD7?roc#BI=IwG zgXZ~4UK>9jSERlbK+dVnqO#BQ2?#C}j!0Oli(o`Z6FHD|=Sc^+z2(lG6QOp9rmAK3 zFwh)#;mEux@hSDTCoPzqE ze@M#A{cSSfkYl_fx6F{P#-(YpxS^@hR(v#d<1ocSTb|^>bJwHP+_-ieLVXE3kJRZ+ zd5q>;8=_+`mU;^q<+I>-fwza?s;YVFbD{57IjxI3eDIPtJ+0YlPI-TXol7aGIj#Cd zWZrPjxBbrcMh8VSN=2W!Cbm^VssFX)nJM!CZw>PaYhJNptJbDyOBgH^2QyCffIZ($ zm=WJXD-`%f=~2BF^zIc&vV#<{16tR7c@AQ)unb@|0fgEo#9>1RIFvRTpI{a0a9gIX%2_ zWK>pnK3K32so=>4QJ*yL-M7%p8IU9yjOg#fXJT`H`6c|mfRZx7K8JAele9(NI>vWT z^x741nW_3@S{+0CVY{Q1at@p3Vm5Rg65_$MpwBZmBa1$!q|6QU#WKb+1zUMWg7aT& z*r+&CoeZ3Vn|&eKf(l%mq%aGq{4`w`5(2x+ZV`T;pIw6_UQ!6|Prze#U~@81?vEh1 zXKj5FX;v$mt!cg|>Z=++vbVgQoLiZ(svql@T?a9w#cX%l;#?tXBiAZ#r{VNw& zOZZ*&Vs9lJCZw`hecw#w!979(`^9Uh0;a|7$+7U*fx9;DVWBGg^$!@6UW+^EgnNIa z(w~iR(UGcHla&m_>p3-dM$l7j_d9e+6TVSF4hcX=3%N>@9FF%xG-!5K5{}eOlG8|u z1)^ShDLzT((jN&Wd<$SRi;>(x5eyR*F1!j+n0<_M+Y_x!zSc*~6794>U^Z5wza^KJ z4ya_^SqK7C{+Qutb?&{aeWid5nkPz;ZXv>6ZKgEd*g8R0PRT}jd_S7opZTpKVVVM{ zeaB+5Z4_h|-Oo3?z96+_)Rq+*9ApAv8CVbM(ggL~3Ds8mBub_JU{z^wXmn|VM=SMH zo)a~=l|hjID!}9gDRkij>DJW+Ki?OHL2XQ&=3~pskSn9IL{$Q`q8c(E4a3i`+aw<4 z5G#v0*s*vJ%?eLxR=ZJ>z)ta)BS+RgNDfQA|4aO(aeewQ+>{O^Wx+K zttG>~n2d+?V>Q-ng$f1<>yz3)9CK=1diHvv7jQp5g^#Q!MG(=8$EXz^@lC#-JGD=6 z$denZtt}TQ5C2YU(K6Fpv*MXCtq$({bQ#$|?1B=VRmIQOFtONfw|I-&DY_7)8x?)0 z%wnJguMSXvo|W9$s`%IzF2bkyz?A30kF5fOXtU|$Q>@A+DG=?P*H>I($z-B-2TMqDO5M2 zy{7ZTNFAJKbJZ|4jG$)3ZO~V5;=`=@OT!0Q%!~=~gF*~EIB)2(dxoENzYa4N^JA_i zrq?26{;IMr8gN92=xoZr`<~;im@#_k@n!X6DFRZ5cY5qUK8)qLuPtQp_1 zw?g;?3zQZEhI$z!*{`%z3qMNC*q5@1APb_}LRRT5UI z#grEZfV3m%$y>@mUhOUw%xXU*c}cz{XF=chKWz#J1-hpmGQgT;sFUswO=aH~K4_)s z!ZJef-gAFaIgzwWIv-O!Ns>~XLj%o|btC$n7Yc+wF=Onz$N?I+9f^`` z`*vrc*72{>lyfYbuG+s&{w^6)@GWE&u{#x^FO;M^8ezHWnz*5v(px+dF*&;`vmiw) z?^r*ZUj;~Qf5kDgE+gsqDa2BVFEnL$gL7vD5%yBlHaUc{1JgSG`6+F+P9MmSt!(hD z#oD)({ZiP5RV}4DFUwO{5>poiJI%KR&-<6;aNt+8#XizfMonGi^j@1|W!#99pA$%teVM@z zkRLLKI|C2BebZZq@q>{kMPWd>5kH+ARCMJW$J#vJ6%A#Hmcl5#zw^!`MJ+RFg(u5F z*Q-Ome;6a;CGVW%FtVR?=-W~}v^JjYi8ra^!4DvMaqjS7wjwMcPj4;Cl(YVQLz|3- zdhiK|%81o4TqH`|d0ji>PM8ru?2Lds#hhQe^A6A8=lt7q!vb;USEu$R;w33#*x+2z zoT$88>{oQJ@43`C-K1lA>VBBX2CxwKVFG>7j1vdCnHMDpewSNhYJHjZWu|nC4$m7t z6L#0frHAej{ra5{u5WefUnn25QYEuIR&!q5rxGS{Chqg3#ossUO1iGwy3I1yph_}C z;KPEE6Mw-b&Sy*OA5)(z7ellsd`#p9lud;*+K+*g?i$sjBZjp^YcW60^_lou z>j6R)54B+q`4n2^_RxI5R|*&V1tVzU*|2<7oQG0x=9A$m53OQ7eYCRb0tr#lH)!Y6U7pE!#zt9M=h4D>`Y`0xbLfpRtwsKa;}I9ulp`?SL$q8*M*z(=-j(;4-xZB zw@G&MWvg#&m1#-kxbz7bhp#j8!R9N%n48rE zADCm0C;WM&bSXp$M9FBDTwt6y_*PD*2x8*Txc&9c!Plm}4)BSk@Qh5St(*ZP zxrGzcrdz~ovxI`rr?cJ=nQsHmwCiov5@_?5g=j;LM7}*?9`Yu`RMdGmW-oog=^iSB z-E4Ww1~M<)@=WxOy0!Y%RogL0HaFhAF5%qt6ab)%MPXkJ7(+EB5w6aHa9dX!q@a&8 z8vE)10FaXPLBkP_NDR;hX^(P|X5VROW(T5drP+-{p&%$)5$S+Z^K(b)`Dy7R{2UPy zw(PPpq*6YTSORAx1`hObc5?BM^pR%&$t#JyzUUTW2mXX$9HrTfp*lcCS9c^3A_x%# z2`KxZyoA|hNP$xBwsw**C6zxYuq|nJ2Mh)+DJ10W?JejnBIxRFF9ep5kPre13keGg zU=acyzAhNJkARB@#|6bN4ke@q!X1Uipj=&m7o2b#S5J&IJ3Dq9_y;+56rt>c?fk>R zMf)ec2gX)N9ov9l=Ys_Z34=i50w7@lVF{t%?Xja!=%3au9)GBa)lle#Y%&YbS&a z>Zj-5ZB*5vI)B<+$Y_spM*p<9K>v=kMf{0Fd%8RQ#MmN)kWNTvY#<(3X7Jze7?j;# z3-q`7T#Wp;K(Oxq zCL78bZjTf~yV(CUxmXrS>}0UQ!Y^tA%lxw+I~PescO)F+>aOqV>Lks65i9V5@@Gi_ zrT!=uNljP8Ps5*Jr0qq${ZVlWaC@PjO(~&&1^#bLdJeAMF8^;le?k9bk$1;=foKQcd z0s?<-0+MjVFZn&-UP#-YOMun!*AT)1?qZL`?zVrF+CTbH|0IniL`8(b5D-|v&IW8J z0J#Z52-w^NOJH+PSX9goBy1~!{FM=ZvU|ANVZ7n)NO^l~4q(?ATUtNY8OZ%J`*{8y zi?;(3nSD`*LM49}KFuB8)mznP`=m07n=^ssjlJ00?5#mE`q}+ddmXHJMZy14XJfkvNs& za&a->h;;OZnCEC-nuhvq4g>QfKMnEAB`}Ic<7d2wI^Bc`okf9|m@NqwMg^0xK$U&>25+Nw=ypihs1(pV z$Q-IgS{uF6($er`wNDMOfRnILxrO1iH3u_$`x|0nVjvNb@YAE6HaBKK2o>%l4^XEy zBO%5@^?IuO^;8@-{Fs6e7^wm=cM(^?G83IbS_+I=XfCr$Pvf>n(Fp3(f$!+}xQ&B@ z!;P!)S39Knmhl-8Sr|f#rh%Fk z>$R&=D}sS8;l)*M>`zMLM^FWzP$VoYEIJ0&ufRN95U3&o!I71f^`fC6)|fxiT(Cy< zYqMgVGp+K6)RO}C4qwtiZZ`ZlaILxE=)^=gm~cSCpBKksV!pxmVCB{Pj`zgqXkJkf zL3w$(bzCTzu*)q;t2nvGc4%&n0y?5GPOTtkF!uh5_^SVwp@9K`06yUOSt`Roc)O^)9og!4!zudV70K_4M?7>Dh|#i)aB0;sdO~BAs$PcMG_U zO-;kPmU^fa_5sIVu`d`_&H0S`bZLqM%JJm}a-?tLg#vK$I8%G7X>MqZb}YF$xwy2tJ%X{}a$o-V zXlGpqrU4ceJxqdq3le)+t#4r9<>mE6$f{`u`@lyX%bWSmuBv?40dp!e9*|{^7uurE z)}E+9Jm&VuwP1KHb8z36oA#NRvGM(D%*;}6YHOqG>gr(Q1_lBp*3)9#MNgkS!_MSw z_AT#}3}he(4BmSoo;kScZ)Py2tAQd9VLCWCu#&2LO)?{G|k^=EseZu&-VePDo`5Pa%5w}N8W;HHn`|RVvZ1?ZqUt?rs z?9P_(=*-E^PR~H95BRRfbl3>flA64=-c39|4?4&#Q?|T*xp{sbG5ysDU}+y$hp`y^ z@LGVos5Aq4Qk3>VVeLk;n(eXgxQ^xciod@3e4GvvAI5GZk3?G5SYjA}GdZKAV;<}b9nu~p^U z)DMZMl?iuJ9339+nG1HYbg>~22-1)pj^u7RWj-D56+6J%_V$XjwDi-`(#LlrzCL{N z!E6tobpDzah%sV*=f)-(+1A=uukFdHED!FhXDB;A(^3aAIwwfc``?+L+4f%Ry080Q*Shbu_u3O{sIN&y!Ab!D0I0OJ z)Q$1~LHI9_j0Ar)9XetM0GKb_G_}MT!+n9CSd1gu4F$ycd!m3SKeQtN;5V_EZRNu& z@1yY(OhH2sD8XLi>QBPiystuZ3Dq26hH6yI(}`ugsP2_B2tl7+18SFRSKM* zeeHclaMkON@&@IBCwoVO<9@+2!SjLfv%&t;2Yqb$nXmTKC0j;=ZGG(s6=?FDKD|-) zZpI|o%{qVKXgQJUmpAHjN`wD4bATA^sCY$1^dhMj!z&OBic@KQT|rvLHi-Oumew+f)gtV?W*&&p+; zlp5E;3?Gg?)`aDx51i<}v24A8*hOx4e8sk~+6^1NOs&tz^Z6`!-7CL_{lYYMnxk%e z5tn-DN=xAUz|p7nz{gPwIR$O3I}CfviQ@MnHAn*QmPmgt|7jiH__bDk8kZiMxu~Qf%fkmr4myGlQe0fh zR+13to-`n+a2gxRtP( zXfAk1@)b_h(7=^~w7vw1UWJ}VjjXgi=X5z0UBrvm}SFTw|6p1@{(ZE}E7xrFzr?j{0 zg&Bv0W9EqUW8FF$E7sa&=*Obrk#c$dBp1NLz-mVLXj%KVH4Q`EVz=zj0MA9YH?^G8 zHn7#+sve{IF~pJOgVO$e)`7<7`>~Ug$!hLfkGwSNin9=dTcNFhnjJ&`2liic5tBkBe%v%!gSQC zbS@P(Ay9vkSNyt>xqV&PUCy2^bai*HciRm?y35wJxz&qVaorrjjGa3XRk^)u2i|!v zUML<0m6KmeW{?l$WZlw|>eqU{m+E{UVX$`NFa!J5Gkx~0U~fjZkOc6NP{f66pqwnt z^Sb8TZyHF*G`%OrCv4r8on0rLZZ3{B=wm?4>+^XhO%gGttevB38`*L3JDY)fy7lk1 zB7=QgI3&q6h~4*ge=tctZgg(n!dWl9Yp=~OS--xT7WjGtij;XCd~SKWvq!h%*)iG7 z(0bIo`A^PG*UR~%ELz2onw^7g+B>a39W%#AZgg^#nhmz+hkg*z6OU+2#>Rgb3B$g4 z`lPv=jd3ZWudXO-#hEh-e!1PWPrU#=B~KOe8IkB4rA3o>^1u^t(lM9F^%XwJkZ^EO zH_W^6$iRj^{YNzDv4bgcdF$Ajw4i1ps6u9H{KWdr4=RPmTbj`BsLO{zpCr26>C$&@ zE~K?MsZgt5;;ou^7XMM&&bzrn4ng~p|8hCd@vX%11}AYjtZa2%z#KjGpb3ZZ8d-9~bf`Sx8E=v)IB#pCvZlJlrqzN0Fw%ea*UT`Fvznk~T z+hz}3o>L$m^5q3ClHAWM~q8KX*s>p_8SKU^tH@U8rOO2JyVMA$HraIF9#?AFUYN*!nefr)YT*{i2g=XckF08ZdTaqSpm9?XY*x=LQqK`ic~n^L zM`wSWdey$u5M)ZbU!5nfWamXa)e@;A6AfB3QX$){G=y3a*w*j(j~ zNd)|y+PZ|h74ZEQVWw~?i17G^eA82*rlvf#8QP16Hy#!mhAzBlQFALRI18Yw96_U!Awm7 zbL)j#Jf?4X6^!=p0wQXC9K+baT>QP>EA~^iLwqj9z6B9*8I9Y*u>yhKS8g<(8{TCpW45_=9;llxiOm?p_b)?A&33KiUm9wDNk)Yvsc_#mrlK1A z5hUo&$ebv6yQY7InK44MKV1H+1n%d>{$#wtFD)P(?lSGZ`T6|Vy!Qh!O=h!9a{u}Wz8l|)O{XKu7qZ^Z|W=+|V~*nZ)guE;W^Ur#@?<$>5C7FGEx| zw$i-Rt@pKE`H57Mml2XQX)RM(z@6_y9_AMw0h@l%t&IsbpAV2xq%D4nE|8oYtTAx- zL~ZC|=Wsw>Zoa8@i~L8F+QxTf54D#3vdRhc66k}yheE_|MKE_5m7DQ(;U14&{mBsV z10I;~Og)_(lZdrUG#a0r_hPLW%{^NIg^DwX?MWCDtNKS@a2=acotJN_LPPE@?~BfC z6u9$d1AnXJ)!R3&k!pMlS&)2K37GjJKely63;<`okyIPff3D|mjT;_1q^O|LTFx%ZygVhK7{uB}1c1xa6cgHdhb5$$){KzsA^MED60rB#3$vO>E zW&Whf61%O;k=2pDMS-!0VG7h^cTUS10n7r9VN70%9U z&gdb+5k1VsF(4>o1H2(;D5VeB4a;erzuY%=SqVNSq6YP*E|*lHSf({zdluOteapFPP+jWv#r?PXJ#)fGJ){(*-kP0j z21FM^tuwC-WU?8^fNR7AR#AqZG85h2v~GMd*}HhBpcpw?YfQ)MW=M!j>VDh3+rQAN zYdzlJIPOl8B0)^2>#Xfz?I8&CqKk*|O<_xl$y`_=Gc@-wj&ez5KZXwx{ zSw5sCs`%@d8m68*JM}Lov{oGM^LBlRa|)>~l&6q|j*1~e^6%y)udaR!3AMVMnyt+6 zArC5NVWcO+qwAfm%wb=7<(l)mtl?Vc5q0?@+}Jm?5S8gIgT@P%B{-s=MS=paZRPm~5wR}4OmUxu}q+H>f;C)eH~-X@RReKCK1rGp?w zi`%}p)udeBILxOnHmV~zJ(oeJz+k%gMb=N)*_YR!Y}=@d@XI1A5_96+dG1|R1+R>augEVC2`2bD(3F;M?bJ`?%dZzPs1g^H4RH2_#ze(#6SUscttLJwHIy`TU#z+|YCpIU z6Y7RbT$o@A7r`1pEvnsy1}~?7PcYF$5=y?C`U1;Ic0J%Rofj}Sh2bFNXq?a*y+{_f z36Prgt&70-^NNFqfRae#b3Fnjrp7F(C(#^MCsJz+{TIQO1c6buocA+adg<=z)fk;% zIhi`1YH3+m@wgVjZ!|F4dh)s7dv@<*N)A@r9p>U0Xw2P}Tw=O~RcA>*09O`>* zvsqi2>l%VU(KG{VHuSrr*pp~}vggluY{zO%=5||hw3^6pruQeci{{@BjIW{YJn!^F zVGZ6p#@px%8tszNHe8Sjde5)o3K}gMy?D_*`OEfY6I%A)G72QCz@FQw%`y>#c>*hNhRGLoWZm_v}=cSiE6^*gSf^$ag zws8oQp2BtO6zbf?SW}7pDo(dzqwC8sRknG| zx%jQ>D`I3U;WyhL9E$@BrYw%co7Jyq-(dJS3RDXHg+!lb43v=mcp|4Mq&1nNQ&X5r z?|m%AA@$S=DxJdM^2kd}7s0=iUuu5AHL!>+DgV3fLxo!MVYYxdvDc%_$yX(0&s8)6 zW2pHE6>rbQe=oGN7tV}J&uYl4T-$N~G(xx>wk%18;mA7Z%Su|8SbT!(zVLtvMl#)} z%0a%#p7eoEOwO;E_3~$ixF$D9Kx%VTR%lQdHQV?Nz)q{QoAJA^ZzX)CXwqI8zp?r3e=g{U^C&Mahv<_->!*A>`;{2(PKEX=XqtJG0F(wkut&2@MLv?^q7voI<1$@hY>jTW`YumDhvFdA zG(<}HpImEdx=+P0^C~Y&thkmvug}_&px|EOyi8u0f>W73q?OwtTSZ`eCATs{+l+AP>obRf9K}{v-7Jm6X#GOx2HLz{q<_D{p#u@#)Xe} zC_*_E<-Qy~c~Yddhwdq{Ck{4pQfy7NpGjB|kgX%)=6*x2t@AycnImIyEaI8rqZ3c! zZIIKYZ0H`~4u|@D)uwad2o(>(`Q|sP1ii96eBco80Q#))hHK~J{T;LAsFzaRb!1bL z6HRzKj;z6i9_i;y@2kf9as!%$%!Dv#WZQsGxz>s^>-WjCjKXl6Q2Tv2p~O zw({_=^rtM(d>8o;4$h-cpGo-Gw0LXwn_npGG1MG6@HK2c)a>e}%YLzl;w zRgp47i5}B;(a0SiX6{&xlEkL>ujGpl*EuR4@Wt_OTsMsg-qS6kxJWWmSF!rUVk72G zH?;sHDKg~4%HjKRuM;wf)wf*9l+O6)HI~MWrVAzh;b;=|31cG2m1!Jx)!1R z9ig-6pFvOBEpyB#-uhiBc_75^uXS{zTxWMXp zbCqxRb%MZ?Vg|^?py*L+5MQM|J;hT6&U~q8)&_lbo#aJ3iGGX#3JI#_E$tmP>aAF0 ze*R!xG=b&ZR?K}hRXLj1axUTDd!QQESyPBv#T&G%P7fbFJuOP8aT!%9sDHxkp<~c|JXvHs!zfm z($`A^01!l?)zl2N)ztp+iWvW5I5#j^UaQ@ZG3sY+26LIrm`Vq?ns@aaB_01`RXU*v z>w9LTsX5Js+G;5a3j!~g*#kt(Ic##hy*-(T+DKz6;!}&_)BHS)o#_@sdwv{j9|mv1 zE_{|2(v@tyLSXhR0SDnmdR3^dGQo-F+gTj>9M`;x?uI!9PMh!#U(a>&-~V9+)0shr z7y$E84RAUPt%19>+)ChrfUq{j7uhc{Di`>ImKrv8*j9?#gYqv*LO%H~2viwv*w=?! zbs5I;$0Fj&jhdC#t6%xYKeH~Y8i5^B^7^}@ves)zk~?@ZfT|%0tlbY=RdaoAGcus% z4EzpA*;};4vTRTwtVkgRQ_3Rg&+yr2l9UXK9>#nGM0RdR2r{zGbG;4j!CaF*Ux~;R z2raB9?c4!_1tp`C)*EE2(uJHiHwEI;*25t65mVN`+X^KPwTy0 z^A>jr-u6E-mTqn12HBKsc;E4jmS6dXF>no1D!7fUPdu?c0sv^@(fD`vmU_B!2#mWJ z9Eov2iTSyE;@{~5018Teo^XUK3I}vRIiWpZJll;eJU}!O#$zF=2h#IYLph_hZemd; zH}y>sH(e33NFF6c3I#toJb^n32M7AOyLouY`N4S3c;)ctr_JI#z%vNW6~<$!X9!fo zU{OG*7*q@-s^N$Bf$%6&02Q!EM>%8lD}PYnuV6gRIGm@PxVW#cub8id7zXPk4wjXb z6$e4YArMhKLe$IO0|)mL_3+|7rTE36j`Bia(VjRo#shfD33tGF<6t~I_-MG0F?$jYXg|d{7=Z-rt>ix_Nv3?$g@~b=q_m zw;R$?93Rw~=ihBKwe<}Dv^ka03GMEAW^s!C9f?HziSzWvx}9N=2yv7f${inw7oHjX zH#`pQ_}2veZ9Jzv|1A)_yFdB=hW>|MXR(~=DyNP?c%M4eR)_JN#w&-!Akav;U#C*C za3~0Z6oooE$cjQ`WFew(qys`!66_!ek&=>>K}vvsqtf>9!ofWds8cFDxfmMHgOWnZ zqM%Tes3RN%7KOqQQlbtZDTJs47>baAJ3^%qG7`U07+}%(N`Sll9@Qxo@{|fJ1wzVz zWJP5i@CPUuj%PuDk)m)Zu%sjiA`Nkn{Y7;;4{|Do+Ato780d`rti{j`j&sCd-C;bs zXb*3{zdB6O?kE!+{8Tislq?vZh7c(b1Omk${{oq#uwM8iKjj32#2}Ef9wb8U3Z4;; zPd2nW+zBP_>EU$NaXKw>_`%?Xg`d_2p82dDKNdMPEDDapU`;U?HyF=pWWZC(vyuWT z{821&x){Wn;Taf(Jgv7sDoz#dBz|_OApWnw|BcDS8RP5m|Hktd^iLKQEY25$bv3{m zIJlq?xPQ&_&%l2&8ROSFFC5li`+peJ|AABZm9AQNTMX9!H~%K68^5l8El6(YGpT^U zvqeA-j`$_N7u*MhJevZ%j=#DP&TtPW6n?e+qtyP~2S6K_vXH#7?ScVYgKDE}AS zZ}xvA<^N>HePpe=+Ey5hxEY%)bi#cgQbUe!K7BWB%2K zzn|f6kK%vcAOA>;)5`f@{QQw_|BD{*)crl8M&vH6~0G|_w-BP zcVEc|-$;tn*3%$eAtNS`07#}tdH?{#8QSV9rk3rS=CcNj7K|rbFD2el(>h5mxLjve zX|Hu1rM;w)Aoi{8vvGSWI1r+j5KD7RAbGnx{@Zj1vo5q$DzGw6>bAC>PJ+EN_oWmq zqUq~aR!MuLl=4RiAIGiD@C{_9D!BCdWW+YwXDiFA&$oFyS44#_J3CuWNLV;{2&xCs zV;Fa1&h;YWZj~hkkZ}VeJe#>ALTJx%6OnPh{*?P{VK(wbdb6+sgKDd6T_mlnYRq$% zoSP@dhh?5J$9W8P0dOF#-^AqPwb8M$n9-+pi*4bgEUc_yC@TQqy{y|L3F3Tqsfw9d z$sij4N2?0drWd#mj0iDaqZJiP^V)*m_q6pBOMH+fSC6}{DVC|`?JX&0*W5%Kt6rnp zYYZmnyz7vC8K7*$=$swZ(9n?bq`aKs=_n;2=FMWF&eg`BwF2f|u@p9B+`5CvEM1Z^ zQ;O`;P_Vfb`!&@NFON6)%^?)3oPl}(;wEC$XzRSqW@KU#9-w%~85-v%b&cm}_THFO z3ST;|xloI{Z$Vy%i~w<-iHXUxaoRqit=7GkhrrBC&Elyo$G4*&>lNkYmEP;-Ln9+$ zV3JN2k)E(8>sk*6$7z+B*yNnH`JvUj47Zc7>n5LS6dRu*d zvX-N$-iEs2iQ1?)A3ZrsR4AS}OWI=!1mo;$)8Y$5IoaQ!E+-6aRfe)or}r$+RHpHn zT38SQr1b{v6POwVgoK31?x)eJzTMreF*Y$VnbIKRRu&`%SZ0_lU^-a@?kDk)S6QCt zyu+_X)ZOd*#`sdbzM$Be(XquJ7*c z^}c)eE?{hYyv*0zn@Lv~ki@H**sly!257~qJNWeF!`4enOF`1oq&?NSDnxU%Rql;Q zHFEBQxw$!(`!%AeT`biiS1Bne+nwI$qFGp23ir3Buc9DX6Iy(bpBG%8ER8DzKa;N< z^S`fmvl@|3)>^6<4wxAquixrQ<-cQ)C-)qU#nLk{FzAn>tb9yCdJJjCFKop86gX{K zf?#l#&Idn!{6Iw7C>CJqM0#GPeGmd30_mX>>s zjYn_=&P*}eCV}D6QTZHFfI}6_l`B^^=I7@(R#ye$-z>WG^75)}V@F$53jr;&!Tr$Q zrvw2VEER@kl9cxOwDdj8*qrb-`^7JdZw7{k@4s36Qfn#ljkKG1b8{1y{5CP@!iS=Mz8=nzaAdP zmX(#w_Gr*n%Ospvj)ib=a2y>6wTur9@!h<6v*XpPuos^5VV@5J5YL}Khh3|6`|>?N zaa1Sv(=8q#vvllB)5ys>%yuH~}==C+9?atmDX|R-3RD)ArxzGFeK{F{jFc)3E zpdgUK#)-ISjnKJJWf4Y-;Wcqv`?ZnBOZ)r=4N9IZWrtlonKCz)C<}FNRv| zg*Xp%lc@*MUE0_OQC03M8zLZC@;t8uD`xrufofIHG9BrRdyL*XQ1Mj z$3-#bz~|GO9>SBNrU}5x8NIhp>ACmllk`1LoY@a}NIO0&5sE(0tB;a=1gCPfY*$KD ZBG_Ah3USL8<33%}wKeqBpQzf0{TG$oi{1bL literal 0 HcmV?d00001 diff --git a/data/images/objects/switch/left.sprite b/data/images/objects/switch/left.sprite deleted file mode 100644 index 1dd45e140ba..00000000000 --- a/data/images/objects/switch/left.sprite +++ /dev/null @@ -1,37 +0,0 @@ -(supertux-sprite - (action - (name "off") - (hitbox 11 2 20 44) - (images - "left-0.png" - ) - ) - (action - (name "turnon") - (fps 18) - (hitbox 11 2 20 44) - (images - "left-1.png" - "left-2.png" - "left-3.png" - ) - ) - (action - (name "on") - (hitbox 11 2 20 44) - (images - "left-4.png" - ) - ) - (action - (name "turnoff") - (fps 18) - (hitbox 11 2 20 44) - (images - "left-3.png" - "left-2.png" - "left-1.png" - ) - ) -) - diff --git a/data/images/objects/switch/right-0.png b/data/images/objects/switch/right-0.png deleted file mode 100644 index 03973d7e69310976c428acb23aa8ec4924001c37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6170 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 diff --git a/data/images/objects/switch/right-1.png b/data/images/objects/switch/right-1.png deleted file mode 100644 index 596528075aba65f0bd3f1a6730f5c2dad5fddaf2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7240 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 WWbr7bcI}}60000 zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3*rlH9r#h5us}UIKDh4q}ye;N|-}VD}`+ul!_d zB+U$)O#(NZaM@=6>z~K`gFh+fYGNuix1241VvEgpeyH~O^L%$U-v93(|9Xkvza2N> z{etI^et8|p`*nWjc=~)piLc}1`*Bm|>pJyyA!oncI_S>&`E_1>T`2hHxO@F>+UtEi ze%a3JpP#SGzPI(`_gDzVN?b2^<9Beu@_*G`!t<~5FY&(%jgwr+{Om)#$NlePXL|pA z@IHR}5j)#o$HVtf7@J=wWxfjk5&Pz5L`?Q8D>vaJ_#Jv-Xae)lj>_T$c+2Vb1jyP8G(^)yC?gtrg zDf#8hAvYmpl2g6z&3lTw=lbNI!$1p(NHj_zH7#_K6=R})O03jSPa#E9e3J!mtA+;{ZMNsoOsg7 zr<{7)=^t5hueSY`HTNTPf1Ne=%9=73?|J1jYaA}+eF-NxNy-@+^U;ybUOBVX z#psne<;*ruQzTE;TvBqDQ^v?(+|H-tK63Y&xxdVtN%3#x&Hty&8Kv(3kvXH(y)yS} z-hPv{)i0xjn;>KLFo|a_?SY$U_K*K}okIg5nF)rh^y?a=G)U~o9D{aRyqj#G- zB)G7J-OK7@_pEvXrsG~z&Z(RI<FVUJ?hSYwiT zAJXWo7xD$+N2?`UR!|8ab=F5IDfeb$2GC{@NclKQB@H?kkQmTz6 zQxUQ;TTW@6-6g6uh01x+_dZRvHP7RDU7?C)rrjDD1olumgde2hCa96v5ByB{Y8Dyh z!aBSkleqdCOlaJrw36t+Qsv0Ow? zC5L0N#LRD=x*lhbDy3s@vKqJ)Mh2@-U*wNkM%uJ)*7PxRvz(LDcNtnO|pqy-rPPc%#v z{6HA9H%H<7br((0cWhK=>Tu$WV^i#Nk(-*}{>7d1AjhjxPoBVn1qC2Sc?A$ZASyHb z?f1?Qi34dlaoy6WjoKdxdDslO9~*hO3ycP9&Kx_)f&_J&YER%O!7H^#)|PH<#D)A6 zgr)(!&T(2U&FsmUosMOVBnrS|`}GXPtG6|`@2$oJAz84Swrmz%?cx-!#SW?=-3~0$ zJ48Cuq6VSx4sSykPw97x^A~<8tke7Lf{=@jroJOB2RNYO3iV#V5?~-K^iu2;#yrHHLzOa{ zAR-OR8=v;3_$f{DPth3Ng(XH3bNpJU1$qk$-F8LyP(MD+Ips6q5fGy9S|R6p5PlMA z)B%cNMboFfxf$%Y)69c9V=E|6d=+KDx~SzUVTB58Yxjp1q2*A2A;`Vkv2}18DifF$ zF0%MtizqJbfhDFHqCj+mH>zFw`al+XPvSiWxD3ymauYPeH_#8{$+5n{TL1=$HjzD} zHYtf|!N?9%Hf@4aLMLKr_%aK?K~@I2|JQeTB@rVRl!f`j?7ewGd46EQBR>O8R!bQK%|5m>}pwBiu#EPSK>M#zbt1zz)sY^e-lJ<#zh0|#s)wN7hq ztumvcVaZKyo+$YQjG*tNkU@OntIHf{0)rkK7>`bO6W2dtmOL6bNqn~(5!W8;JX9o| zb4pj%e38;xb@Cz+Vbu*K<#J_4FRzc!$8QpLz>BYHPrM4^06P7 zs-4_|bK_LD1MF{)0+UzS5ou(B;xLhFt2X7+hT;^-fEuB|Pp;g9E7D5^nCa+Bxu>>r zL_{pFUcgzje0w5k{`CDj2-(y`uDvS#DfG`Ww<-zA?8T1`&7}uFXDWOYO2ca^(xpoaMXZP+VlDTd zL<$ru3kl|@v;iP?)K*EBXu?1+C&-bEBrpD=HWH4)p$>f{#fclDynqY51y~9ObruFA z6bX5ljfzV0p-mjcil{eOWD3Gurk~jWZPJenNCK*8q%BodxGO8%C=Ictuy|1EP#PN8 zjb;WmrA^j3$~6b<;6$LQ_Ur{7IGBP5CUjuckOZ`TY4orFg8_p=QVWeTmOFE+ z(V&pE1UG$xa^+2MO5U5o1~9Wmh#6{}d#6k72xg}Xn?R@~<6DVyA;S+#+IHfE9pVtp zgN^4ml5^-q6tT@2aHBx+xpUD=brUl1$OSnj!peXnSdIKF5;dtW^blg8 zXUrNFX2kwq2PlI0GI!Xifj$_B`-OqC!G zkqV-3Z=wre0^Gp8Ie`{*f5%}xywU15VJ13KMJQ&wknc!z4?sr==ppg`i#H_AyLcm} zNf%%i+kFS_vKt_U?4ePHTrq(?PgfCC_~Bl-rZ?{$pu^Ek7!H{G^2}S27)g?NSqyHb z3y+Y7n-|R!5DFpv)c(ZU?zQ6;2o?UMRy&nVAZtfXIV=B-awzC;bT^%hJ4}#lru0?M z+0MYmQE67v%mMHKHNpX_wTy@!5qj05Mj|J1L%K#(b8y4(6oe(TgyAUx)ER{czJaP! zK-5%LXBr~pI8}uWIImgx)w)5WrCV8m`95qlq0HI0s(sR#5k-Tp0xQr6G8jhkaZu!a zAxUY$J-ccF9ZI-iYx-{> zSj7!OqOCj1(Pk-7Qbv>FrhJKXAQ&l!Iz8rY6GURL_8kDlsu2lZT{8{((TFg5uU9d6 zszx}|eTx!^Q>G5F+dQ_x1LPPLa z?=GC_3CoAnpgUl@XUWul$P3k?3)wjWf*4=7@UcZ>fyPS-8TV|A%>p}+r{|3+h_2XG zI#!CQ#*AKFI1&T46E^gM9~4Ic?>ijmk>jlqK1k?}4KkM5iY7yh9uka-9k>f$Ksk9Y zgCGh-32?(!;G|bt6`kOasJLwJMKRalJCvZ*kg{+E5XKaY+ShWSAw_)P>eBO)O#2Qz zm!ue72^vjJB@ht~N=K>t4K4X+j;mC279Av&03CIfqCp<-p}s^$eMD*xf)(kY=sfaJ z+QgSB!j&@9fSDMV48LPQYjei6*!WWs4gf`#o^K#>q3U{xYeSPcLxE$+(&HMrMPtiR z&RH;`M}NCU19_p-hzj?DM~6m^2!}q&D~HQ`7$wax3* zA&^eztjlEvctKK^l4236uI8a`_sm8q4?O`ORA6WUg5m6@kxR>-aePndph++Kg4QX_egW#u8^~yry1fBxw2-00FG5^y^F}oNK$}WD z6IY3ws?jv21T`wpvbSwA`p7aVCr!s(sbDtTUa7#yDH*mSw9k|Qjg0CSWnl?UrE@Zk zMK=wpgIVD~wFdp!ibqu$#$PE^pmM^@={6^+Itt+8OFP^Cf#1*gP-U#KLwZq!24M(U;@ z55zapJH47YF}US@X?#St-p?f4@FFaJV%w&Kmjt}t(Ns4_ItIvNK&VggH?Wps~^1D=WN zNWB@*CE92DIG_j_?1B`a2{s5zk<)V_10Rsp>ls5rV6=r6MncpeJxOO76l`v-`|z0jNUztP3X21*R#Q z833QbKO?vjrkYGci$kZbiyA@0VRg8CzpcYbI*y43?5vih#I-JNBMUf)w&`NinKv0Lc0s8gT-hA zrL5-;6O26aX3yC$E!gOjo)=YWHsYXUCPv1WBJXma(>$C^UAzbHVd)b=ThAW9m)yJJ zDoFis&1){Jj0Z!7)n`aI@-yp7PZpo&b2tDE!VpuR74-NEO@QQMeq~yOofX;of#nC; zz{#UQ(KR~P<=69LNEWu&2#dvoZv!GLEUjn61N@TPcv`Ks5P&LHj0p* zGaktfTA(`!4Po{kxLCwc(B(zsdaSz;?=ZifL3s%iR&?R#Lp}l1u>V^=-JC9jjXqdc zx}G06naoFU&FAqrq9rdA*;16HtQUNy9%so9fu1{HX958ho9dAqH^nls)N*GNsPodP zM~Ed=SR@hZ>t@~kL_`e#VXphv-_4f+Firy1(QXLII@Egs7>VqpafYV4^j>4L4W1Mc zQ#c7nfg~`XaAZqiK{g0!i}9TEvBf~&o?Sv-lhh?|)al{PN&4i$eIB;&Gd)K^?);K^ z0Y20RFulmxr|KqeoX(uY2rI-qZS0ma;Q;xZn5%zDrqq_xw1a5joG##w6_WJrV^ zs(>rV98#3}cnYdDuc*7WD1PcG$YUbA^f^k_gNn!p)f8NX5s%4|31~UQ=l>LU^T!GM zuP;QKEKHL5H@7kIMnK2cYXATM24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f| zc%?sf00007bV*G`2jm447Z?M{Huxg|00#(3L_t(o!|j+&Y*g13$A9;}dGldAo-uB1 zJTo@9V~jhY1ZsyYN(xC+7C^*`luZ_qQlu=5RJD~X5Ez!Is!-K}T~r+;Ufrb{Vhdpw>WMr$QxIs@(52;j&TrS7ny?cX7slP5XfJ18yz`(!&QcB!zH$n(HJ3CQInScS< z3kERvL@CA4&=68eE?l^9*Rrf5PZMtjq!74;z>pFHkcPqEvf1YY0LW&ul$Djy*VorH zIyyT3q!-r!C5FN8H*coi?S?>r+h2XfFOMJR2SCp}0c0@*a{;v0s5T6UdJ;Pu+5`l>cZr#7n-ArcQ0FL5plgT7LpAW+@ zFbqQj;&Bj8r<1m}HX4>Jp?%XPezkNd6ScLxQd7e}-EKbo_+!4*`q==KQe-k291aJj zY2t7=#MZ4_<>}L>AG-v%ZQI6)6DL^l!3X^2=uvb$jv)loN^!^MV>*?3HUQhU(ONSx zF@eM301)AD_>n{i27_p=Id<$A(P$J)DH4GImSvGhBxW9Z25~4+0(8dcEw}v4evL4>B<^fy?DWYyGwObC?uD6lXm{eZ_W1On{ezn`wIE;5-6RaI5=_VyBw$Ddn#7GSOkA*igZq`A4d{MM~oeJw35;Xoij zdwV+_9UWY~dX=W8Cc3-3Ur2Zspy>L!004V~4uzdM) z&YU^(jfg{`7Zmpd0J2ahg3c$__Z_Pp28(J>K?Mn4RNLYzH&_8SwQ4Z&Ohtu{#nFt%-z&*$-YJjCO1lu}=0GMP=WSZpX13OQo2 z*ld{&g+ia4J9qAVm&;{lwEq4dv)S~H+S;;UeLdwhHQe#})US^oRfn%#D|@Y_<jB%gky0XrKuSq%Z7l~59LRY*o?rCz^b7&a6)&9sJQNCT&t|ho!+7Oi6%}>AO(g!| zNhS>~L`q8;xt!0x_ptbE4m@l{0JqysMMVW&U0oB`uV25yQ!EyX70c;3@MQwNL$Siz zPu|r(uqiFB|6)k0tE<_yYgekNsp$@rDeR|lktj(uRor*1lI21x) zSzm`#Yt7xecS)zyy-ug|XUC5p?_DVIS%A4-&|0%-(V{8aw%1*{bZK-U#i0;_hr=MH zoSvGRn*aCJivnZ{+vqtkwrQG<7aDoK08&cQ=`?^UTwr{BynK9oJW^Lz_rnDT&|2H+ zbQ)b)kA`88&*xq3?d{()P4h1c4v@>`riX`zQA!y~DO@fWQc5mgzAUAbrOf;vSTJ*F zt*w!f5t7LyPNx&YFpyGO7cXAS0H3gcKPRBD40E|0ckbMwq@)BO?{qp-qobpZ{r&x8 z3n>mDl~NOxm6Zen0c_i*tE(%Y$z)nyhB$y|Y;1hrvaCNChEZbMwozSO{YH0p_qCTH e{;mCA*na?7W*pr&zKbRR0000 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 diff --git a/data/images/objects/switch/right-4.png b/data/images/objects/switch/right-4.png deleted file mode 100644 index bd87bf6d28ec25e6dd6f385e799f6274f534e7ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7657 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 X`Qj>$D0F|Q00000NkvXXu0mjfc6NrA diff --git a/data/images/objects/switch/right.sprite b/data/images/objects/switch/right.sprite deleted file mode 100644 index 57c0e36e632..00000000000 --- a/data/images/objects/switch/right.sprite +++ /dev/null @@ -1,37 +0,0 @@ -(supertux-sprite - (action - (name "off") - (hitbox 0 2 20 43) - (images - "right-0.png" - ) - ) - (action - (name "turnon") - (fps 18) - (hitbox 0 2 20 43) - (images - "right-1.png" - "right-2.png" - "right-3.png" - ) - ) - (action - (name "on") - (hitbox 0 2 20 43) - (images - "right-4.png" - ) - ) - (action - (name "turnoff") - (fps 18) - (hitbox 0 2 20 43) - (images - "right-3.png" - "right-2.png" - "right-1.png" - ) - ) -) - diff --git a/data/images/objects/switch/switch.sprite b/data/images/objects/switch/switch.sprite index 8e3c03a57de..f215f0956a9 100644 --- a/data/images/objects/switch/switch.sprite +++ b/data/images/objects/switch/switch.sprite @@ -1,37 +1,148 @@ (supertux-sprite (action - (name "off") + (name "off-none") (hitbox 3 4 25 39) - (images + (images "switch-0.png" ) ) (action - (name "turnon") - (fps 18) + (name "turnon-none") + (fps 18) (hitbox 3 4 25 39) - (images - "switch-1.png" + (images + "switch-1.png" "switch-2.png" - "switch-3.png" + "switch-3.png" ) ) (action - (name "on") + (name "on-none") (hitbox 3 4 25 39) - (images + (images "switch-4.png" ) ) (action - (name "turnoff") - (fps 18) + (name "turnoff-none") + (fps 18) (hitbox 3 4 25 39) (images "switch-3.png" "switch-2.png" - "switch-1.png" + "switch-1.png" + ) + ) + (action + (name "off-left") + (hitbox 11 2 20 44) + (images + "left-0.png" + ) + ) + (action + (name "turnon-left") + (fps 18) + (hitbox 11 2 20 44) + (images + "left-1.png" + "left-2.png" + "left-3.png" + ) + ) + (action + (name "on-left") + (hitbox 11 2 20 44) + (images + "left-4.png" ) ) + (action + (name "turnoff-left") + (fps 18) + (hitbox 11 2 20 44) + (images + "left-3.png" + "left-2.png" + "left-1.png" + ) + ) + (action + (name "off-right") + (hitbox 0 2 20 43) + (mirror-action "off-left") + ) + (action + (name "turnon-right") + (fps 18) + (hitbox 0 2 20 43) + (mirror-action "turnon-left") + ) + (action + (name "on-right") + (hitbox 0 2 20 43) + (mirror-action "on-left") + ) + (action + (name "turnoff-right") + (fps 18) + (hitbox 0 2 20 43) + (mirror-action "turnoff-left") + ) + (action + (name "off-down") + (hitbox 2 0 43 20) + (images + "down-0.png" + ) + ) + (action + (name "turnon-down") + (fps 18) + (hitbox 2 0 43 20) + (images + "down-1.png" + "down-2.png" + "down-3.png" + ) + ) + (action + (name "on-down") + (hitbox 2 0 43 20) + (images + "down-4.png" + ) + ) + (action + (name "turnoff-down") + (fps 18) + (hitbox 2 0 43 20) + (images + "down-3.png" + "down-2.png" + "down-1.png" + ) + ) + (action + (name "off-up") + (hitbox 2 11 44 20) + (flip-action "off-down") + ) + (action + (name "turnon-up") + (fps 18) + (hitbox 2 11 44 20) + (flip-action "turnon-down") + ) + (action + (name "on-up") + (hitbox 2 11 44 20) + (flip-action "on-down") + ) + (action + (name "turnoff-up") + (fps 18) + (hitbox 2 11 44 20) + (flip-action "turnoff-down") + ) ) - diff --git a/src/badguy/angrystone.cpp b/src/badguy/angrystone.cpp index b81c1c9c98e..b5bdc3bc300 100644 --- a/src/badguy/angrystone.cpp +++ b/src/badguy/angrystone.cpp @@ -36,6 +36,7 @@ AngryStone::AngryStone(const ReaderMapping& reader) : m_physic.set_velocity_x(0); m_physic.set_velocity_y(0); m_physic.enable_gravity(true); + m_allowed_directions = {}; set_action("idle"); } diff --git a/src/badguy/badguy.cpp b/src/badguy/badguy.cpp index 2f56b9b4b9c..a6d9a3cbbab 100644 --- a/src/badguy/badguy.cpp +++ b/src/badguy/badguy.cpp @@ -57,6 +57,8 @@ BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite m_start_position(m_col.m_bbox.p1()), m_dir(direction), m_start_dir(direction), + m_dir_in_allowed(0), + m_allowed_directions({Direction::AUTO, Direction::LEFT, Direction::RIGHT}), m_frozen(false), m_ignited(false), m_in_water(false), @@ -81,6 +83,15 @@ BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite m_dir = (m_start_dir == Direction::AUTO) ? Direction::LEFT : m_start_dir; m_lightsprite->set_blend(Blend::ADD); + + for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + { + if (m_allowed_directions[i] == m_start_dir) + { + m_dir_in_allowed = i; + break; + } + } } BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name_, int layer_, @@ -93,6 +104,8 @@ BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name_, int m_start_position(m_col.m_bbox.p1()), m_dir(Direction::LEFT), m_start_dir(Direction::AUTO), + m_dir_in_allowed(0), + m_allowed_directions({Direction::AUTO, Direction::LEFT, Direction::RIGHT}), m_frozen(false), m_ignited(false), m_in_water(false), @@ -124,6 +137,15 @@ BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name_, int m_dir = (m_start_dir == Direction::AUTO) ? Direction::LEFT : m_start_dir; m_lightsprite->set_blend(Blend::ADD); + + for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + { + if (m_allowed_directions[i] == m_start_dir) + { + m_dir_in_allowed = i; + break; + } + } } void @@ -1038,7 +1060,8 @@ BadGuy::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_direction(_("Direction"), &m_start_dir, Direction::AUTO, "direction"); + if (!m_allowed_directions.empty()) + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::AUTO, "direction", 0, m_allowed_directions); result.add_script(_("Death script"), &m_dead_script, "dead-script"); result.reorder({"direction", "sprite", "x", "y"}); @@ -1051,6 +1074,8 @@ BadGuy::after_editor_set() { MovingSprite::after_editor_set(); + m_start_dir = m_allowed_directions[m_dir_in_allowed]; + if (m_dir == Direction::AUTO) { if (m_sprite->has_action("editor-left")) { diff --git a/src/badguy/badguy.hpp b/src/badguy/badguy.hpp index 2bcc9ff3dc3..1a32fba3c03 100644 --- a/src/badguy/badguy.hpp +++ b/src/badguy/badguy.hpp @@ -250,6 +250,12 @@ class BadGuy : public MovingSprite, /** The direction we initially faced in */ Direction m_start_dir; + /** The order of the direction in the allowed directions, + does not correspond to the actual direction!*/ + int m_dir_in_allowed; + + std::vector m_allowed_directions; + bool m_frozen; bool m_ignited; /**< true if this badguy is currently on fire */ bool m_in_water; /** < true if the badguy is currently in water */ diff --git a/src/badguy/dart.cpp b/src/badguy/dart.cpp index 0cb9d8e2552..9aa9a909fcb 100644 --- a/src/badguy/dart.cpp +++ b/src/badguy/dart.cpp @@ -34,6 +34,8 @@ Dart::Dart(const ReaderMapping& reader) : { m_physic.enable_gravity(false); m_countMe = false; + m_allowed_directions = {Direction::AUTO, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; + SoundManager::current()->preload(DART_SOUND); SoundManager::current()->preload("sounds/darthit.wav"); SoundManager::current()->preload("sounds/stomp.wav"); @@ -48,6 +50,8 @@ Dart::Dart(const Vector& pos, Direction d, const BadGuy* parent_, const std::str { m_physic.enable_gravity(false); m_countMe = false; + m_allowed_directions = {Direction::AUTO, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; + SoundManager::current()->preload(DART_SOUND); SoundManager::current()->preload("sounds/darthit.wav"); SoundManager::current()->preload("sounds/stomp.wav"); diff --git a/src/badguy/darttrap.cpp b/src/badguy/darttrap.cpp index dac6a70fd49..0b0c401add0 100644 --- a/src/badguy/darttrap.cpp +++ b/src/badguy/darttrap.cpp @@ -42,6 +42,7 @@ DartTrap::DartTrap(const ReaderMapping& reader) : reader.get("ammo", m_ammo, -1); reader.get("dart-sprite", m_dart_sprite, "images/creatures/darttrap/skull_dart.sprite"); m_countMe = false; + m_allowed_directions = {Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; SoundManager::current()->preload("sounds/dartfire.wav"); if (m_start_dir == Direction::AUTO) { log_warning << "Setting a DartTrap's direction to AUTO is no good idea" << std::endl; } m_state = IDLE; diff --git a/src/badguy/dive_mine.cpp b/src/badguy/dive_mine.cpp index 8b99adcb5c7..8778574b39f 100644 --- a/src/badguy/dive_mine.cpp +++ b/src/badguy/dive_mine.cpp @@ -38,6 +38,7 @@ DiveMine::DiveMine(const ReaderMapping& reader) : void DiveMine::reset_sprites() { + m_allowed_directions = {}; set_action(m_dir); m_ticking_glow->set_action("idle"); } diff --git a/src/badguy/fish_jumping.cpp b/src/badguy/fish_jumping.cpp index 58f08ca764b..3b656ca0436 100644 --- a/src/badguy/fish_jumping.cpp +++ b/src/badguy/fish_jumping.cpp @@ -33,6 +33,7 @@ FishJumping::FishJumping(const ReaderMapping& reader) : m_stop_y(0) { m_physic.enable_gravity(true); + m_allowed_directions = {}; } void diff --git a/src/badguy/flame.cpp b/src/badguy/flame.cpp index 69b3527ecda..ad072af4059 100644 --- a/src/badguy/flame.cpp +++ b/src/badguy/flame.cpp @@ -49,6 +49,7 @@ Flame::Flame(const ReaderMapping& reader, const std::string& sprite) : m_lightsprite->set_color(Color(0.21f, 0.13f, 0.08f)); m_glowing = true; + m_allowed_directions = {}; } ObjectSettings diff --git a/src/badguy/flyingsnowball.cpp b/src/badguy/flyingsnowball.cpp index 66dc7110f74..b4a7a60f07a 100644 --- a/src/badguy/flyingsnowball.cpp +++ b/src/badguy/flyingsnowball.cpp @@ -35,6 +35,7 @@ FlyingSnowBall::FlyingSnowBall(const ReaderMapping& reader) : puff_timer() { m_physic.enable_gravity(false); + m_allowed_directions = {}; } void diff --git a/src/badguy/ghostflame.cpp b/src/badguy/ghostflame.cpp index f5b1abe03f3..18fc2bddd29 100644 --- a/src/badguy/ghostflame.cpp +++ b/src/badguy/ghostflame.cpp @@ -20,6 +20,7 @@ Ghostflame::Ghostflame(const ReaderMapping& reader) : Flame(reader, "images/creatures/flame/ghostflame.sprite") { m_lightsprite->set_color(Color(0.21f, 0.00f, 0.21f)); + m_allowed_directions = {}; } bool diff --git a/src/badguy/ghosttree.cpp b/src/badguy/ghosttree.cpp index 506f16aa919..daa6589abef 100644 --- a/src/badguy/ghosttree.cpp +++ b/src/badguy/ghosttree.cpp @@ -57,6 +57,8 @@ GhostTree::GhostTree(const ReaderMapping& mapping) : set_colgroup_active(COLGROUP_TOUCHABLE); SoundManager::current()->preload("sounds/tree_howling.ogg"); SoundManager::current()->preload("sounds/tree_suck.ogg"); + + m_allowed_directions = {}; } void diff --git a/src/badguy/ghoul.cpp b/src/badguy/ghoul.cpp index 9b7df79ea6f..11b220e8e65 100644 --- a/src/badguy/ghoul.cpp +++ b/src/badguy/ghoul.cpp @@ -40,6 +40,8 @@ Ghoul::Ghoul(const ReaderMapping& reader) : reader.get("running", running, false); init_path(reader, running); + + m_allowed_directions = {}; set_action(m_dir); } diff --git a/src/badguy/iceflame.cpp b/src/badguy/iceflame.cpp index b6865a4abdd..ddc41823bd3 100644 --- a/src/badguy/iceflame.cpp +++ b/src/badguy/iceflame.cpp @@ -29,6 +29,7 @@ Iceflame::Iceflame(const ReaderMapping& reader) : Flame(reader, "images/creatures/flame/iceflame.sprite") { m_lightsprite->set_color(Color(0.00f, 0.13f, 0.18f)); + m_allowed_directions = {}; } void diff --git a/src/badguy/jumpy.cpp b/src/badguy/jumpy.cpp index 05e9f94c213..e8c21522252 100644 --- a/src/badguy/jumpy.cpp +++ b/src/badguy/jumpy.cpp @@ -33,6 +33,8 @@ Jumpy::Jumpy(const ReaderMapping& reader) : set_action(m_dir, "middle"); // TODO create a nice sound for this... //SoundManager::current()->preload("sounds/skid.wav"); + + m_allowed_directions = {}; } void diff --git a/src/badguy/kugelblitz.cpp b/src/badguy/kugelblitz.cpp index 50bc6c1daf8..2cce77f111e 100644 --- a/src/badguy/kugelblitz.cpp +++ b/src/badguy/kugelblitz.cpp @@ -49,6 +49,7 @@ Kugelblitz::Kugelblitz(const ReaderMapping& reader) : set_action("falling"); m_physic.enable_gravity(false); m_countMe = false; + m_allowed_directions = {}; lightsprite->set_blend(Blend::ADD); lightsprite->set_color(Color(0.2f, 0.1f, 0.0f)); diff --git a/src/badguy/mole.cpp b/src/badguy/mole.cpp index 610d5d6b7c3..0c1add77692 100644 --- a/src/badguy/mole.cpp +++ b/src/badguy/mole.cpp @@ -41,6 +41,8 @@ Mole::Mole(const ReaderMapping& reader) : SoundManager::current()->preload("sounds/fall.wav"); SoundManager::current()->preload("sounds/squish.wav"); SoundManager::current()->preload("sounds/dartfire.wav"); + + m_allowed_directions = {}; } void diff --git a/src/badguy/skydive.cpp b/src/badguy/skydive.cpp index efd9e8973e1..a85d175c565 100644 --- a/src/badguy/skydive.cpp +++ b/src/badguy/skydive.cpp @@ -28,6 +28,7 @@ SkyDive::SkyDive(const ReaderMapping& reader) : BadGuy(reader, "images/creatures/skydive/skydive.sprite") { + m_allowed_directions = {}; SoundManager::current()->preload("sounds/explosion.wav"); set_action("normal", 1); } diff --git a/src/badguy/spidermite.cpp b/src/badguy/spidermite.cpp index f6099a955db..f725e6c3258 100644 --- a/src/badguy/spidermite.cpp +++ b/src/badguy/spidermite.cpp @@ -28,6 +28,7 @@ SpiderMite::SpiderMite(const ReaderMapping& reader) : timer() { m_physic.enable_gravity(false); + m_allowed_directions = {}; } void diff --git a/src/badguy/stalactite.cpp b/src/badguy/stalactite.cpp index aa66150cd8b..d5f3ee21856 100644 --- a/src/badguy/stalactite.cpp +++ b/src/badguy/stalactite.cpp @@ -46,6 +46,8 @@ Stalactite::Stalactite(const ReaderMapping& mapping) : SoundManager::current()->preload("sounds/cracking.wav"); SoundManager::current()->preload("sounds/sizzle.ogg"); SoundManager::current()->preload("sounds/icecrash.ogg"); + + m_allowed_directions = {}; } void diff --git a/src/badguy/willowisp.cpp b/src/badguy/willowisp.cpp index 83a36c40544..90d29d90e6b 100644 --- a/src/badguy/willowisp.cpp +++ b/src/badguy/willowisp.cpp @@ -85,6 +85,8 @@ WillOWisp::WillOWisp(const ReaderMapping& reader) : m_sprite->set_color(m_color); m_glowing = true; + m_allowed_directions = {}; + set_action("idle"); } @@ -307,7 +309,6 @@ WillOWisp::get_settings() { ObjectSettings result = BadGuy::get_settings(); - result.add_direction(_("Direction"), &m_dir); result.add_text(_("Sector"), &m_target_sector, "sector"); result.add_text(_("Spawnpoint"), &m_target_spawnpoint, "spawnpoint"); result.add_text(_("Hit script"), &m_hit_script, "hit-script"); diff --git a/src/badguy/yeti.cpp b/src/badguy/yeti.cpp index 319ef588e42..96be13b0d38 100644 --- a/src/badguy/yeti.cpp +++ b/src/badguy/yeti.cpp @@ -76,6 +76,7 @@ Yeti::Yeti(const ReaderMapping& reader) : reader.get("hud-icon", hud_icon, "images/creatures/yeti/hudlife.png"); hud_head = Surface::from_file(hud_icon); + m_allowed_directions = {}; initialize(); diff --git a/src/badguy/yeti_stalactite.cpp b/src/badguy/yeti_stalactite.cpp index c09a003faa6..e3b9a4d8440 100644 --- a/src/badguy/yeti_stalactite.cpp +++ b/src/badguy/yeti_stalactite.cpp @@ -24,6 +24,7 @@ static const float YT_SHAKE_TIME = .8f; YetiStalactite::YetiStalactite(const ReaderMapping& mapping) : Stalactite(mapping) { + m_allowed_directions = {}; } void diff --git a/src/editor/object_settings.cpp b/src/editor/object_settings.cpp index 931720169b9..0099f4847d6 100644 --- a/src/editor/object_settings.cpp +++ b/src/editor/object_settings.cpp @@ -21,6 +21,7 @@ #include "util/gettext.hpp" #include "video/color.hpp" +#include "supertux/direction.hpp" ObjectSettings::ObjectSettings(const std::string& name) : m_name(name), @@ -113,12 +114,40 @@ ObjectSettings::add_rectf(const std::string& text, Rectf* value_ptr, void ObjectSettings::add_direction(const std::string& text, Direction* value_ptr, std::optional default_value, - const std::string& key, unsigned int flags) -{ + const std::string& key, unsigned int flags, + std::vector possible_directions) +{ + std::vector direction_labels, direction_symbols; + if (!possible_directions.empty()) + { + for (Direction direction : possible_directions) + { + direction_labels.push_back(_(dir_to_string(direction))); + direction_symbols.push_back(dir_to_string(direction)); + } + } + else + { + direction_labels = {_("auto"), _("none"), _("left"), _("right"), _("up"), _("down")}; + direction_symbols = {"auto", "none", "left", "right", "up", "down"}; + } + + int new_default_value = -1; + if (default_value) + { + for (int i = 0; i < static_cast(direction_symbols.size()); ++i) + { + if (string_to_dir(direction_symbols[i]) == *default_value) + { + new_default_value = i; + break; + } + } + } + add_enum(text, reinterpret_cast(value_ptr), - {_("auto"), _("left"), _("right"), _("up"), _("down")}, - {"auto", "left", "right", "up", "down"}, - default_value ? static_cast(*default_value) : std::optional(), + direction_labels, direction_symbols, + default_value ? new_default_value : std::optional(), key, flags); } diff --git a/src/editor/object_settings.hpp b/src/editor/object_settings.hpp index a0482e24b6d..91917f3fa18 100644 --- a/src/editor/object_settings.hpp +++ b/src/editor/object_settings.hpp @@ -23,8 +23,6 @@ #include "editor/object_option.hpp" #include "object/path_walker.hpp" -#include - class Color; enum class Direction; class GameObject; @@ -66,7 +64,8 @@ class ObjectSettings final const std::string& key = {}, unsigned int flags = 0); void add_direction(const std::string& text, Direction* value_ptr, std::optional default_value = {}, - const std::string& key = {}, unsigned int flags = 0); + const std::string& key = {}, unsigned int flags = 0, + std::vector possible_directions = {}); void add_walk_mode(const std::string& text, WalkMode* value_ptr, const std::optional& default_value = {}, const std::string& key = {}, unsigned int flags = 0); diff --git a/src/object/conveyor_belt.cpp b/src/object/conveyor_belt.cpp index 97af510e8a3..b98af43d092 100644 --- a/src/object/conveyor_belt.cpp +++ b/src/object/conveyor_belt.cpp @@ -28,6 +28,8 @@ ConveyorBelt::ConveyorBelt(const ReaderMapping &reader) : ExposedObject(this), m_running(true), m_dir(Direction::LEFT), + m_dir_in_allowed(0), + m_allowed_directions({Direction::LEFT, Direction::RIGHT}), m_length(1), m_speed(1.0f), m_frame(0.0f), @@ -50,6 +52,15 @@ ConveyorBelt::ConveyorBelt(const ReaderMapping &reader) : set_action("stopped"); else set_action(m_dir); + + for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + { + if (m_allowed_directions[i] == m_dir) + { + m_dir_in_allowed = i; + break; + } + } } ObjectSettings @@ -57,7 +68,7 @@ ConveyorBelt::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_direction(_("Direction"), &m_dir, Direction::LEFT, "direction"); + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::LEFT, "direction", 0, m_allowed_directions); result.add_float(_("Speed"), &m_speed, "speed", 1.0f); result.add_bool(_("Running"), &m_running, "running", true); result.add_int(_("Length"), &m_length, "length", 3); @@ -126,6 +137,8 @@ ConveyorBelt::after_editor_set() { MovingSprite::after_editor_set(); + m_dir = m_allowed_directions[m_dir_in_allowed]; + if (m_length <= 0) m_length = 1; set_action(dir_to_string(m_dir)); diff --git a/src/object/conveyor_belt.hpp b/src/object/conveyor_belt.hpp index c3228811b1e..7c7e0c740cc 100644 --- a/src/object/conveyor_belt.hpp +++ b/src/object/conveyor_belt.hpp @@ -69,6 +69,8 @@ class ConveyorBelt final : public MovingSprite, private: bool m_running; Direction m_dir; + int m_dir_in_allowed; + std::vector m_allowed_directions; int m_length; float m_speed; diff --git a/src/object/ispy.cpp b/src/object/ispy.cpp index cd0fef1b0a1..d0e3430b044 100644 --- a/src/object/ispy.cpp +++ b/src/object/ispy.cpp @@ -29,7 +29,9 @@ Ispy::Ispy(const ReaderMapping& reader) : MovingSprite(reader, "images/objects/ispy/ispy.sprite", LAYER_TILES + 5, COLGROUP_DISABLED), m_state(ISPYSTATE_IDLE), m_script(), - m_dir(Direction::AUTO) + m_dir(Direction::LEFT), + m_allowed_directions({Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}), + m_dir_in_allowed(0) { reader.get("script", m_script); @@ -42,6 +44,15 @@ Ispy::Ispy(const ReaderMapping& reader) : if (m_dir == Direction::AUTO) log_warning << "Setting an Ispy's direction to AUTO is no good idea." << std::endl; + for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + { + if (m_allowed_directions[i] == m_dir) + { + m_dir_in_allowed = i; + break; + } + } + set_sprite_action("idle"); } @@ -51,7 +62,7 @@ Ispy::get_settings() ObjectSettings result = MovingSprite::get_settings(); result.add_script(_("Script"), &m_script, "script"); - result.add_direction(_("Direction"), &m_dir, Direction::AUTO, "direction"); + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::LEFT, "direction", 0, m_allowed_directions); result.reorder({"script", "facing-down", "direction", "x", "y"}); @@ -62,6 +73,7 @@ void Ispy::after_editor_set() { MovingSprite::after_editor_set(); + m_dir = m_allowed_directions[m_dir_in_allowed]; set_sprite_action("idle"); } diff --git a/src/object/ispy.hpp b/src/object/ispy.hpp index dd57209eac4..2f3c7736996 100644 --- a/src/object/ispy.hpp +++ b/src/object/ispy.hpp @@ -56,6 +56,8 @@ class Ispy final : public MovingSprite std::string m_script; /**< script to execute when Tux is spotted */ Direction m_dir; + std::vector m_allowed_directions; + int m_dir_in_allowed; private: Ispy(const Ispy&) = delete; diff --git a/src/supertux/direction.cpp b/src/supertux/direction.cpp index 18413d6f3f9..08441cedbbf 100644 --- a/src/supertux/direction.cpp +++ b/src/supertux/direction.cpp @@ -32,6 +32,8 @@ dir_to_string(const Direction& dir) { switch (dir) { + case Direction::NONE: + return "none"; case Direction::LEFT: return "left"; case Direction::RIGHT: @@ -53,7 +55,9 @@ dir_to_string(const Direction& dir) Direction string_to_dir(const std::string& dir_str) { - if (dir_str == "left") + if (dir_str == "none") + return Direction::NONE; + else if (dir_str == "left") return Direction::LEFT; else if (dir_str == "right") return Direction::RIGHT; diff --git a/src/supertux/direction.hpp b/src/supertux/direction.hpp index 7346026f41d..6bac98d2e84 100644 --- a/src/supertux/direction.hpp +++ b/src/supertux/direction.hpp @@ -21,7 +21,7 @@ class ObjectOption; -enum class Direction { AUTO, LEFT, RIGHT, UP, DOWN }; +enum class Direction { AUTO, NONE, LEFT, RIGHT, UP, DOWN }; std::ostream& operator<<(std::ostream& o, const Direction& dir); diff --git a/src/trigger/switch.cpp b/src/trigger/switch.cpp index 4c0fba4ccb3..b31b51cedd3 100644 --- a/src/trigger/switch.cpp +++ b/src/trigger/switch.cpp @@ -29,14 +29,34 @@ namespace { } // namespace Switch::Switch(const ReaderMapping& reader) : - SpritedTrigger(reader, "images/objects/switch/left.sprite"), - script(), - off_script(), - state(OFF), - bistable() + SpritedTrigger(reader, "images/objects/switch/switch.sprite"), + m_script(), + m_off_script(), + m_state(OFF), + m_bistable(), + m_dir(Direction::NONE), + m_dir_in_allowed(0), + m_allowed_directions({Direction::NONE, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}) { - reader.get("script", script); - bistable = reader.get("off-script", off_script); + std::string dir_str; + if (reader.get("direction", dir_str)) + m_dir = string_to_dir(dir_str); + else + m_dir = Direction::NONE; + + for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + { + if (m_allowed_directions[i] == m_dir) + { + m_dir_in_allowed = i; + break; + } + } + + set_action("off", m_dir); + + reader.get("script", m_script); + m_bistable = reader.get("off-script", m_off_script); SoundManager::current()->preload(SWITCH_SOUND); } @@ -50,10 +70,12 @@ Switch::get_settings() { ObjectSettings result = SpritedTrigger::get_settings(); - result.add_script(_("Turn on script"), &script, "script"); - result.add_script(_("Turn off script"), &off_script, "off-script"); + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), m_allowed_directions[0], "direction", 0, m_allowed_directions); + + result.add_script(_("Turn on script"), &m_script, "script"); + result.add_script(_("Turn off script"), &m_off_script, "off-script"); - result.reorder({"script", "off-script", "sprite", "x", "y"}); + result.reorder({"direction", "script", "off-script", "sprite", "x", "y"}); return result; } @@ -61,35 +83,35 @@ Switch::get_settings() void Switch::update(float ) { - switch (state) { + switch (m_state) { case OFF: break; case TURN_ON: if (m_sprite->animation_done()) { std::ostringstream location; location << "switch" << m_col.m_bbox.p1(); - Sector::get().run_script(script, location.str()); + Sector::get().run_script(m_script, location.str()); - set_action("on", 1); - state = ON; + set_action("on", m_dir, 1); + m_state = ON; } break; case ON: - if (m_sprite->animation_done() && !bistable) { - set_action("turnoff", 1); - state = TURN_OFF; + if (m_sprite->animation_done() && !m_bistable) { + set_action("turnoff", m_dir, 1); + m_state = TURN_OFF; } break; case TURN_OFF: if (m_sprite->animation_done()) { - if (bistable) { + if (m_bistable) { std::ostringstream location; location << "switch" << m_col.m_bbox.p1(); - Sector::get().run_script(off_script, location.str()); + Sector::get().run_script(m_off_script, location.str()); } - set_action("off"); - state = OFF; + set_action("off", m_dir); + m_state = OFF; } break; } @@ -100,19 +122,19 @@ Switch::event(Player& , EventType type) { if (type != EVENT_ACTIVATE) return; - switch (state) { + switch (m_state) { case OFF: - set_action("turnon", 1); + set_action("turnon", m_dir, 1); SoundManager::current()->play(SWITCH_SOUND, get_pos()); - state = TURN_ON; + m_state = TURN_ON; break; case TURN_ON: break; case ON: - if (bistable) { - set_action("turnoff", 1); + if (m_bistable) { + set_action("turnoff", m_dir, 1); SoundManager::current()->play(SWITCH_SOUND, get_pos()); - state = TURN_OFF; + m_state = TURN_OFF; } break; case TURN_OFF: @@ -120,6 +142,15 @@ Switch::event(Player& , EventType type) } } +void +Switch::after_editor_set() +{ + SpritedTrigger::after_editor_set(); + + m_dir = m_allowed_directions[m_dir_in_allowed]; + set_action("off", m_dir); +} + void Switch::on_flip(float height) { diff --git a/src/trigger/switch.hpp b/src/trigger/switch.hpp index 19a5d728c33..fc21eda7f9a 100644 --- a/src/trigger/switch.hpp +++ b/src/trigger/switch.hpp @@ -18,6 +18,7 @@ #define HEADER_SUPERTUX_TRIGGER_SWITCH_HPP #include "trigger/trigger_base.hpp" +#include "supertux/direction.hpp" class Switch final : public SpritedTrigger { @@ -35,6 +36,7 @@ class Switch final : public SpritedTrigger virtual void update(float dt_sec) override; virtual void event(Player& player, EventType type) override; + virtual void after_editor_set() override; virtual void on_flip(float height) override; private: @@ -46,10 +48,13 @@ class Switch final : public SpritedTrigger }; private: - std::string script; - std::string off_script; - SwitchState state; - bool bistable; + std::string m_script; + std::string m_off_script; + SwitchState m_state; + bool m_bistable; + Direction m_dir; + int m_dir_in_allowed; + std::vector m_allowed_directions; private: Switch(const Switch&) = delete; From 1b61e53e27e88560e57a2012304b1061b9cf0e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Sat, 5 Aug 2023 11:43:31 +0200 Subject: [PATCH 02/14] code quality --- src/badguy/yeti.cpp | 124 ++++++++++++++++----------------- src/badguy/yeti.hpp | 31 ++++----- src/editor/object_settings.cpp | 2 +- src/object/ispy.cpp | 25 ++----- src/object/ispy.hpp | 3 - 5 files changed, 84 insertions(+), 101 deletions(-) diff --git a/src/badguy/yeti.cpp b/src/badguy/yeti.cpp index 96be13b0d38..d8f721c53fb 100644 --- a/src/badguy/yeti.cpp +++ b/src/badguy/yeti.cpp @@ -56,36 +56,36 @@ const float SNOW_EXPLOSIONS_VY = -200; /**< Speed of snowballs */ Yeti::Yeti(const ReaderMapping& reader) : BadGuy(reader, "images/creatures/yeti/yeti.sprite"), - state(), - state_timer(), - safe_timer(), - stomp_count(), - hit_points(), - hud_head(), - left_stand_x(), - right_stand_x(), - left_jump_x(), - right_jump_x(), - fixed_pos(), - hud_icon() + m_state(), + m_state_timer(), + m_safe_timer(), + m_stomp_count(), + m_hit_points(), + m_hud_head(), + m_left_stand_x(), + m_right_stand_x(), + m_left_jump_x(), + m_right_jump_x(), + m_fixed_pos(), + m_hud_icon() { - reader.get("lives", hit_points, INITIAL_HITPOINTS); + reader.get("lives", m_hit_points, INITIAL_HITPOINTS); m_countMe = true; SoundManager::current()->preload("sounds/yeti_gna.wav"); SoundManager::current()->preload("sounds/yeti_roar.wav"); - reader.get("hud-icon", hud_icon, "images/creatures/yeti/hudlife.png"); - hud_head = Surface::from_file(hud_icon); + reader.get("hud-icon", m_hud_icon, "images/creatures/yeti/hudlife.png"); + m_hud_head = Surface::from_file(m_hud_icon); m_allowed_directions = {}; initialize(); - reader.get("fixed-pos", fixed_pos, false); - if (fixed_pos) { - left_stand_x = 80; - right_stand_x = 1140; - left_jump_x = 528; - right_jump_x = 692; + reader.get("fixed-pos", m_fixed_pos, false); + if (m_fixed_pos) { + m_left_stand_x = 80; + m_right_stand_x = 1140; + m_left_jump_x = 528; + m_right_jump_x = 692; } else { recalculate_pos(); } @@ -102,22 +102,22 @@ void Yeti::recalculate_pos() { if (m_dir == Direction::RIGHT) { - left_stand_x = m_col.m_bbox.get_left(); - right_stand_x = left_stand_x + RUN_DISTANCE; + m_left_stand_x = m_col.m_bbox.get_left(); + m_right_stand_x = m_left_stand_x + RUN_DISTANCE; } else { - right_stand_x = m_col.m_bbox.get_left(); - left_stand_x = right_stand_x - RUN_DISTANCE; + m_right_stand_x = m_col.m_bbox.get_left(); + m_left_stand_x = m_right_stand_x - RUN_DISTANCE; } - left_jump_x = left_stand_x + JUMP_SPACE; - right_jump_x = right_stand_x - JUMP_SPACE; + m_left_jump_x = m_left_stand_x + JUMP_SPACE; + m_right_jump_x = m_right_stand_x - JUMP_SPACE; } void Yeti::draw(DrawingContext& context) { // we blink when we are safe - if (safe_timer.started() && size_t(g_game_time * 40) % 2) + if (m_safe_timer.started() && size_t(g_game_time * 40) % 2) return; draw_hit_points(context); @@ -128,15 +128,15 @@ Yeti::draw(DrawingContext& context) void Yeti::draw_hit_points(DrawingContext& context) { - if (hud_head) + if (m_hud_head) { context.push_transform(); context.set_translation(Vector(0, 0)); context.transform().scale = 1.f; - for (int i = 0; i < hit_points; ++i) + for (int i = 0; i < m_hit_points; ++i) { - context.color().draw_surface(hud_head, Vector(BORDER_X + (static_cast(i * hud_head->get_width())), BORDER_Y + 1), LAYER_FOREGROUND1); + context.color().draw_surface(m_hud_head, Vector(BORDER_X + (static_cast(i * m_hud_head->get_width())), BORDER_Y + 1), LAYER_FOREGROUND1); } context.pop_transform(); @@ -146,20 +146,20 @@ Yeti::draw_hit_points(DrawingContext& context) void Yeti::active_update(float dt_sec) { - switch (state) { + switch (m_state) { case JUMP_DOWN: m_physic.set_velocity_x((m_dir==Direction::RIGHT)?+JUMP_DOWN_VX:-JUMP_DOWN_VX); break; case RUN: m_physic.set_velocity_x((m_dir==Direction::RIGHT)?+RUN_VX:-RUN_VX); - if (((m_dir == Direction::RIGHT) && (get_pos().x >= right_jump_x)) || ((m_dir == Direction::LEFT) && (get_pos().x <= left_jump_x))) jump_up(); + if (((m_dir == Direction::RIGHT) && (get_pos().x >= m_right_jump_x)) || ((m_dir == Direction::LEFT) && (get_pos().x <= m_left_jump_x))) jump_up(); break; case JUMP_UP: m_physic.set_velocity_x((m_dir==Direction::RIGHT)?+JUMP_UP_VX:-JUMP_UP_VX); - if (((m_dir == Direction::RIGHT) && (get_pos().x >= right_stand_x)) || ((m_dir == Direction::LEFT) && (get_pos().x <= left_stand_x))) be_angry(); + if (((m_dir == Direction::RIGHT) && (get_pos().x >= m_right_stand_x)) || ((m_dir == Direction::LEFT) && (get_pos().x <= m_left_stand_x))) be_angry(); break; case BE_ANGRY: - if (state_timer.check() && on_ground()) { + if (m_state_timer.check() && on_ground()) { m_physic.set_velocity_y(STOMP_VY); set_action("stomp", m_dir); SoundManager::current()->play("sounds/yeti_gna.wav", get_pos()); @@ -167,7 +167,7 @@ Yeti::active_update(float dt_sec) break; case SQUISHED: { - Direction newdir = (int(state_timer.get_timeleft() * SNOW_EXPLOSIONS_FREQUENCY) % 2) ? Direction::LEFT : Direction::RIGHT; + Direction newdir = (int(m_state_timer.get_timeleft() * SNOW_EXPLOSIONS_FREQUENCY) % 2) ? Direction::LEFT : Direction::RIGHT; if (m_dir != newdir && m_dir == Direction::RIGHT) { SoundManager::current()->play("sounds/stomp.wav", get_pos()); add_snow_explosions(); @@ -176,9 +176,9 @@ Yeti::active_update(float dt_sec) m_dir = newdir; set_action("jump", m_dir); } - if (state_timer.check()) { + if (m_state_timer.check()) { BadGuy::kill_fall(); - state = FALLING; + m_state = FALLING; m_physic.set_velocity_y(JUMP_UP_VY / 2); // Move up a bit before falling // Add some extra explosions for (int i = 0; i < 10; i++) { @@ -200,7 +200,7 @@ Yeti::jump_down() set_action("jump", m_dir); m_physic.set_velocity_x((m_dir==Direction::RIGHT)?(+JUMP_DOWN_VX):(-JUMP_DOWN_VX)); m_physic.set_velocity_y(JUMP_DOWN_VY); - state = JUMP_DOWN; + m_state = JUMP_DOWN; } void @@ -209,7 +209,7 @@ Yeti::run() set_action("walking", m_dir); m_physic.set_velocity_x((m_dir==Direction::RIGHT)?(+RUN_VX):(-RUN_VX)); m_physic.set_velocity_y(0); - state = RUN; + m_state = RUN; } void @@ -218,7 +218,7 @@ Yeti::jump_up() set_action("jump", m_dir); m_physic.set_velocity_x((m_dir==Direction::RIGHT)?(+JUMP_UP_VX):(-JUMP_UP_VX)); m_physic.set_velocity_y(JUMP_UP_VY); - state = JUMP_UP; + m_state = JUMP_UP; } void @@ -229,9 +229,9 @@ Yeti::be_angry() set_action("stand", m_dir); m_physic.set_velocity_x(0); - stomp_count = 0; - state = BE_ANGRY; - state_timer.start(STOMP_WAIT); + m_stomp_count = 0; + m_state = BE_ANGRY; + m_state_timer.start(STOMP_WAIT); } bool @@ -254,13 +254,13 @@ Yeti::kill_squished(GameObject& object) void Yeti::take_hit(Player& ) { - if (safe_timer.started()) + if (m_safe_timer.started()) return; SoundManager::current()->play("sounds/yeti_roar.wav", get_pos()); - hit_points--; + m_hit_points--; - if (hit_points <= 0) { + if (m_hit_points <= 0) { // We're dead m_physic.set_velocity_x(((m_dir==Direction::RIGHT)?+RUN_VX:-RUN_VX)/5); m_physic.set_velocity_y(0); @@ -268,13 +268,13 @@ void Yeti::take_hit(Player& ) // Set the badguy layer to be above the foremost, so that // this does not reveal secret tilemaps: m_layer = Sector::get().get_foremost_layer() + 1; - state = SQUISHED; - state_timer.start(YETI_SQUISH_TIME); + m_state = SQUISHED; + m_state_timer.start(YETI_SQUISH_TIME); set_colgroup_active(COLGROUP_MOVING_ONLY_STATIC); //sprite->set_action("dead"); // This sprite does not look very good } else { - safe_timer.start(SAFE_TIME); + m_safe_timer.start(SAFE_TIME); } } @@ -296,16 +296,16 @@ Yeti::drop_stalactite() for (auto& stalactite : Sector::get().get_objects_by_type()) { if (stalactite.is_hanging()) { - if (hit_points >= 3) { + if (m_hit_points >= 3) { // drop stalactites within 3 of player, going out with each jump float distancex = fabsf(stalactite.get_bbox().get_middle().x - player->get_bbox().get_middle().x); - if (distancex < static_cast(stomp_count) * 32.0f) { + if (distancex < static_cast(m_stomp_count) * 32.0f) { stalactite.start_shaking(); } } else { /* if (hitpoints < 3) */ // drop every 3rd pair of stalactites - if ((((static_cast(stalactite.get_pos().x) + 16) / 64) % 3) == (stomp_count % 3)) { + if ((((static_cast(stalactite.get_pos().x) + 16) / 64) % 3) == (m_stomp_count % 3)) { stalactite.start_shaking(); } } @@ -320,7 +320,7 @@ Yeti::collision_solid(const CollisionHit& hit) if (hit.top || hit.bottom) { // hit floor or roof m_physic.set_velocity_y(0); - switch (state) { + switch (m_state) { case JUMP_DOWN: run(); break; @@ -330,17 +330,17 @@ Yeti::collision_solid(const CollisionHit& hit) break; case BE_ANGRY: // we just landed - if (!state_timer.started()) { + if (!m_state_timer.started()) { set_action("stand", m_dir); - stomp_count++; + m_stomp_count++; drop_stalactite(); // go to other side after 3 jumps - if (stomp_count == 3) { + if (m_stomp_count == 3) { jump_down(); } else { // jump again - state_timer.start(STOMP_WAIT); + m_state_timer.start(STOMP_WAIT); } } break; @@ -351,7 +351,7 @@ Yeti::collision_solid(const CollisionHit& hit) } } else if (hit.left || hit.right) { // hit wall - if(state != SQUISHED && state != FALLING) + if(m_state != SQUISHED && m_state != FALLING) jump_up(); } } @@ -367,9 +367,9 @@ Yeti::get_settings() { ObjectSettings result = BadGuy::get_settings(); - result.add_text("hud-icon", &hud_icon, "hud-icon", std::string("images/creatures/yeti/hudlife.png"), OPTION_HIDDEN); - result.add_bool(_("Fixed position"), &fixed_pos, "fixed-pos", false); - result.add_int(_("Lives"), &hit_points, "lives", 5); + result.add_text("hud-icon", &m_hud_icon, "hud-icon", std::string("images/creatures/yeti/hudlife.png"), OPTION_HIDDEN); + result.add_bool(_("Fixed position"), &m_fixed_pos, "fixed-pos", false); + result.add_int(_("Lives"), &m_hit_points, "lives", 5); return result; } diff --git a/src/badguy/yeti.hpp b/src/badguy/yeti.hpp index fe6435b1ad2..4fcb8e33d09 100644 --- a/src/badguy/yeti.hpp +++ b/src/badguy/yeti.hpp @@ -54,6 +54,7 @@ class Yeti final : public BadGuy void take_hit(Player& player); void add_snow_explosions(); + void recalculate_pos(); private: enum YetiState { @@ -66,22 +67,20 @@ class Yeti final : public BadGuy }; private: - YetiState state; - Timer state_timer; - Timer safe_timer; - int stomp_count; - int hit_points; - SurfacePtr hud_head; - - float left_stand_x; - float right_stand_x; - float left_jump_x; - float right_jump_x; - - void recalculate_pos(); - - bool fixed_pos; - std::string hud_icon; + YetiState m_state; + Timer m_state_timer; + Timer m_safe_timer; + int m_stomp_count; + int m_hit_points; + SurfacePtr m_hud_head; + + float m_left_stand_x; + float m_right_stand_x; + float m_left_jump_x; + float m_right_jump_x; + + bool m_fixed_pos; + std::string m_hud_icon; class SnowExplosionParticle: public BadGuy { diff --git a/src/editor/object_settings.cpp b/src/editor/object_settings.cpp index 0099f4847d6..c2622ce09b3 100644 --- a/src/editor/object_settings.cpp +++ b/src/editor/object_settings.cpp @@ -19,9 +19,9 @@ #include #include +#include "supertux/direction.hpp" #include "util/gettext.hpp" #include "video/color.hpp" -#include "supertux/direction.hpp" ObjectSettings::ObjectSettings(const std::string& name) : m_name(name), diff --git a/src/object/ispy.cpp b/src/object/ispy.cpp index d0e3430b044..1f5e71899ea 100644 --- a/src/object/ispy.cpp +++ b/src/object/ispy.cpp @@ -53,7 +53,7 @@ Ispy::Ispy(const ReaderMapping& reader) : } } - set_sprite_action("idle"); + set_action("idle", m_dir); } ObjectSettings @@ -74,7 +74,7 @@ Ispy::after_editor_set() { MovingSprite::after_editor_set(); m_dir = m_allowed_directions[m_dir_in_allowed]; - set_sprite_action("idle"); + set_action("idle", m_dir); } HitResponse @@ -103,7 +103,7 @@ Ispy::update(float dt_sec) if (Sector::get().can_see_player(eye)) { - set_sprite_action("alert", 1); + set_action("alert", m_dir, 1); m_state = ISPYSTATE_ALERT; } } @@ -111,7 +111,7 @@ Ispy::update(float dt_sec) { if (m_sprite->animation_done()) { - set_sprite_action("hiding", 1); + set_action("hiding", m_dir, 1); m_state = ISPYSTATE_HIDING; Sector::get().run_script(m_script, "Ispy"); @@ -121,7 +121,7 @@ Ispy::update(float dt_sec) { if (m_sprite->animation_done()) { - set_sprite_action("showing", 1); + set_action("showing", m_dir, 1); m_state = ISPYSTATE_SHOWING; } } @@ -129,25 +129,12 @@ Ispy::update(float dt_sec) { if (m_sprite->animation_done()) { - set_sprite_action("idle"); + set_action("idle", m_dir); m_state = ISPYSTATE_IDLE; } } } -void -Ispy::set_sprite_action(const std::string& action, int loops) -{ - switch (m_dir) - { - case Direction::DOWN: set_action(action + "-down", loops); break; - case Direction::UP: set_action(action + "-up", loops); break; - case Direction::LEFT: set_action(action + "-left", loops); break; - case Direction::RIGHT: set_action(action + "-right", loops); break; - default: break; - } -} - void Ispy::on_flip(float height) { diff --git a/src/object/ispy.hpp b/src/object/ispy.hpp index 2f3c7736996..33255819ec7 100644 --- a/src/object/ispy.hpp +++ b/src/object/ispy.hpp @@ -40,9 +40,6 @@ class Ispy final : public MovingSprite virtual void on_flip(float height) override; -private: - void set_sprite_action(const std::string& action, int loops = -1); - private: enum IspyState { ISPYSTATE_IDLE, From 106c2cf409b358a7ce2a22bf3d1a4406f2fbd070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Sat, 5 Aug 2023 12:42:38 +0200 Subject: [PATCH 03/14] replace direction bools with Direction for bumpers and pushbuttons code quality --- .../objects/pushbutton/pushbutton.sprite | 23 ++++++-- data/images/objects/trampoline/bumper.sprite | 12 ++-- src/object/bumper.cpp | 39 ++++++++---- src/object/bumper.hpp | 6 +- src/object/pushbutton.cpp | 59 ++++++++++++------- src/object/pushbutton.hpp | 11 +++- 6 files changed, 102 insertions(+), 48 deletions(-) diff --git a/data/images/objects/pushbutton/pushbutton.sprite b/data/images/objects/pushbutton/pushbutton.sprite index 3d8a98ef0cc..22ab5c23d6d 100644 --- a/data/images/objects/pushbutton/pushbutton.sprite +++ b/data/images/objects/pushbutton/pushbutton.sprite @@ -1,16 +1,27 @@ (supertux-sprite (action - (name "off") + (name "off-up") (hitbox 0 0 30 16) (images "pushbutton-0.png") ) (action - (name "on") + (name "on-up") (hitbox 0 0 30 10) - (loops 1) - (fps 20) + (loops 1) + (fps 20) (images "pushbutton-1.png" - "pushbutton-2.png" - "pushbutton-3.png") + "pushbutton-2.png" + "pushbutton-3.png" + ) + ) + (action + (name "off-down") + (hitbox 0 0 30 16) + (flip-action "off-up") + ) + (action + (name "on-down") + (hitbox 0 0 30 10) + (flip-action "on-up") ) ) diff --git a/data/images/objects/trampoline/bumper.sprite b/data/images/objects/trampoline/bumper.sprite index d19eb96a14b..6f42d0e6f48 100644 --- a/data/images/objects/trampoline/bumper.sprite +++ b/data/images/objects/trampoline/bumper.sprite @@ -1,11 +1,11 @@ (supertux-sprite (action - (name "right-normal") + (name "normal-right") (hitbox 0 2 18 27) (images "right-0.png") ) (action - (name "right-swinging") + (name "swinging-right") (fps 24) (hitbox 0 2 18 27) (images "right-0.png" @@ -17,14 +17,14 @@ ) ) (action - (name "left-normal") + (name "normal-left") (hitbox 14 2 18 27) - (mirror-action "right-normal") + (mirror-action "normal-right") ) (action - (name "left-swinging") + (name "swinging-left") (fps 24) (hitbox 14 2 18 27) - (mirror-action "right-swinging") + (mirror-action "swinging-right") ) ) diff --git a/src/object/bumper.cpp b/src/object/bumper.cpp index 0fd0caa3fd2..ba65455189a 100644 --- a/src/object/bumper.cpp +++ b/src/object/bumper.cpp @@ -32,11 +32,28 @@ const float BOUNCE_X = 700.0f; Bumper::Bumper(const ReaderMapping& reader) : MovingSprite(reader, "images/objects/trampoline/bumper.sprite", LAYER_OBJECTS, COLGROUP_MOVING), m_physic(), - m_facing_left() + m_dir(Direction::RIGHT), + m_dir_in_allowed(0), + m_allowed_directions({Direction::LEFT, Direction::RIGHT}) { - reader.get("left", m_facing_left); - set_action(m_facing_left ? "left-normal" : "right-normal"); + std::string dir_str; + bool old_facing_left = false; + + if (reader.get("direction", dir_str)) + m_dir = string_to_dir(dir_str); + else if (reader.get("left", old_facing_left) && old_facing_left) + m_dir = Direction::LEFT; + set_action("normal", m_dir); m_physic.enable_gravity(false); + + for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + { + if (m_allowed_directions[i] == m_dir) + { + m_dir_in_allowed = i; + break; + } + } } ObjectSettings @@ -44,8 +61,8 @@ Bumper::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_bool(_("Facing Left"), &m_facing_left, "left", false); - result.reorder({"left", "sprite", "x", "y"}); + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::RIGHT, "direction", 0, m_allowed_directions); + result.reorder({"direction", "sprite", "x", "y"}); return result; } @@ -53,9 +70,7 @@ void Bumper::update(float dt_sec) { if (m_sprite->animation_done()) - { - set_action(m_facing_left ? "left-normal" : "right-normal"); - } + set_action("normal", m_dir); m_col.set_movement(m_physic.get_movement (dt_sec)); } @@ -65,11 +80,11 @@ Bumper::collision(GameObject& other, const CollisionHit& hit) auto player = dynamic_cast (&other); if (player) { - float BOUNCE_DIR = m_facing_left ? -BOUNCE_X : BOUNCE_X; + float BOUNCE_DIR = m_dir == Direction::LEFT ? -BOUNCE_X : BOUNCE_X; player->get_physic().set_velocity(0.f, player->is_swimming() ? 0.f : BOUNCE_Y); player->sideways_push(BOUNCE_DIR); SoundManager::current()->play(TRAMPOLINE_SOUND, get_pos()); - set_action((m_facing_left ? "left-swinging" : "right-swinging"), 1); + set_action("swinging", m_dir, 1); } auto bumper = dynamic_cast (&other); if (bumper) @@ -90,7 +105,9 @@ void Bumper::after_editor_set() { MovingSprite::after_editor_set(); - set_action(m_facing_left ? "left-normal" : "right-normal"); + + m_dir = m_allowed_directions[m_dir_in_allowed]; + set_action("normal", m_dir); } void diff --git a/src/object/bumper.hpp b/src/object/bumper.hpp index 65258cffac7..42038540223 100644 --- a/src/object/bumper.hpp +++ b/src/object/bumper.hpp @@ -20,6 +20,7 @@ #include "supertux/physic.hpp" +enum class Direction; class Player; class Bumper final : public MovingSprite @@ -44,7 +45,10 @@ class Bumper final : public MovingSprite private: Physic m_physic; - bool m_facing_left; + + Direction m_dir; + int m_dir_in_allowed; + std::vector m_allowed_directions; private: Bumper(const Bumper&) = delete; diff --git a/src/object/pushbutton.cpp b/src/object/pushbutton.cpp index c4c49325dbf..4e13f579ab9 100644 --- a/src/object/pushbutton.cpp +++ b/src/object/pushbutton.cpp @@ -20,6 +20,7 @@ #include "object/player.hpp" #include "object/rock.hpp" #include "sprite/sprite.hpp" +#include "supertux/direction.hpp" #include "supertux/flip_level_transformer.hpp" #include "supertux/sector.hpp" #include "util/reader_mapping.hpp" @@ -31,21 +32,37 @@ const std::string BUTTON_SOUND = "sounds/switch.ogg"; PushButton::PushButton(const ReaderMapping& mapping) : MovingSprite(mapping, "images/objects/pushbutton/pushbutton.sprite", LAYER_BACKGROUNDTILES+1, COLGROUP_MOVING), - script(), - state(OFF), - m_upside_down(false) + m_script(), + m_state(OFF), + m_dir(Direction::UP), + m_dir_in_allowed(0), + m_allowed_directions({Direction::UP, Direction::DOWN}) { SoundManager::current()->preload(BUTTON_SOUND); - set_action("off", -1); - if (!mapping.get("script", script)) + if (!mapping.get("script", m_script)) { log_warning << "No script set for pushbutton." << std::endl; } - mapping.get("upside-down", m_upside_down); - if (m_upside_down) - FlipLevelTransformer::transform_flip(m_flip); + bool old_upside_down; + std::string dir_str; + + if (mapping.get("direction", dir_str)) + m_dir = string_to_dir(dir_str); + else if (mapping.get("upside-down", old_upside_down) && old_upside_down) + m_dir = Direction::DOWN; + + for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + { + if (m_allowed_directions[i] == m_dir) + { + m_dir_in_allowed = i; + break; + } + } + + set_action("off", m_dir, -1); } ObjectSettings @@ -53,10 +70,10 @@ PushButton::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_script(_("Script"), &script, "script"); - result.add_bool(_("Upside down"), &m_upside_down, "upside-down"); + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::UP, "direction", 0, m_allowed_directions); + result.add_script(_("Script"), &m_script, "script"); - result.reorder({"script", "upside-down", "x", "y"}); + result.reorder({"direction", "script", "x", "y"}); return result; } @@ -65,8 +82,8 @@ void PushButton::after_editor_set() { MovingSprite::after_editor_set(); - if ((m_upside_down && m_flip == NO_FLIP) || (!m_upside_down && m_flip == VERTICAL_FLIP)) - FlipLevelTransformer::transform_flip(m_flip); + m_dir = m_allowed_directions[m_dir_in_allowed]; + set_action("off", m_dir); } void @@ -85,7 +102,7 @@ PushButton::collision(GameObject& other, const CollisionHit& hit) { float vy = player->get_physic().get_velocity_y(); - if (m_upside_down) + if (m_dir == Direction::DOWN) { if (vy >= 0) return FORCE_MOVE; @@ -106,22 +123,22 @@ PushButton::collision(GameObject& other, const CollisionHit& hit) } } - if (state != OFF || !(m_upside_down ? hit.bottom : hit.top)) + if (m_state != OFF || !(m_dir == Direction::DOWN ? hit.bottom : hit.top)) return FORCE_MOVE; // change appearance - state = ON; + m_state = ON; float old_bbox_height = m_col.m_bbox.get_height(); - set_action("on", -1); + set_action("on", m_dir, -1); float new_bbox_height = m_col.m_bbox.get_height(); Vector delta(0, old_bbox_height - new_bbox_height); - set_pos(get_pos() + delta * (m_upside_down ? 0 : 1.f)); + set_pos(get_pos() + delta * (m_dir == Direction::DOWN ? 0 : 1.f)); // play sound SoundManager::current()->play(BUTTON_SOUND, get_pos()); // run script - Sector::get().run_script(script, "PushButton"); + Sector::get().run_script(m_script, "PushButton"); return FORCE_MOVE; } @@ -130,8 +147,8 @@ void PushButton::on_flip(float height) { MovingSprite::on_flip(height); - FlipLevelTransformer::transform_flip(m_flip); - m_upside_down = !m_upside_down; + m_dir = m_dir == Direction::UP ? Direction::DOWN : Direction::UP; + set_action(m_state == OFF ? "off" : "on", m_dir); } /* EOF */ diff --git a/src/object/pushbutton.hpp b/src/object/pushbutton.hpp index 5e29aa0902e..dacd2da24c6 100644 --- a/src/object/pushbutton.hpp +++ b/src/object/pushbutton.hpp @@ -19,6 +19,8 @@ #include "object/moving_sprite.hpp" +enum class Direction; + /** PushButton - jump on it to run a script */ class PushButton final : public MovingSprite { @@ -43,9 +45,12 @@ class PushButton final : public MovingSprite ON }; - std::string script; - PushButtonState state; - bool m_upside_down; + std::string m_script; + PushButtonState m_state; + + Direction m_dir; + int m_dir_in_allowed; + std::vector m_allowed_directions; private: PushButton(const PushButton&) = delete; From 967c56531e6d43d0e622311b97a9a76e8af3cb71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Sat, 5 Aug 2023 20:10:18 +0200 Subject: [PATCH 04/14] replace m_allowed_directions with a virtual getter --- src/badguy/angrystone.cpp | 7 ++++++- src/badguy/angrystone.hpp | 3 +++ src/badguy/badguy.cpp | 28 +++++++++++++++++++--------- src/badguy/badguy.hpp | 7 ++++--- src/badguy/dart.cpp | 8 ++++++-- src/badguy/dart.hpp | 3 +++ src/badguy/darttrap.cpp | 7 ++++++- src/badguy/darttrap.hpp | 3 +++ src/badguy/dive_mine.cpp | 7 ++++++- src/badguy/dive_mine.hpp | 3 +++ src/badguy/fish_jumping.cpp | 7 ++++++- src/badguy/fish_jumping.hpp | 3 +++ src/badguy/flame.cpp | 7 ++++++- src/badguy/flame.hpp | 3 +++ src/badguy/flyingsnowball.cpp | 7 ++++++- src/badguy/flyingsnowball.hpp | 1 + src/badguy/ghostflame.cpp | 7 ++++++- src/badguy/ghostflame.hpp | 3 +++ src/badguy/ghosttree.cpp | 8 ++++++-- src/badguy/ghosttree.hpp | 3 +++ src/badguy/ghoul.cpp | 8 ++++++-- src/badguy/ghoul.hpp | 3 ++- src/badguy/iceflame.cpp | 7 ++++++- src/badguy/iceflame.hpp | 3 +++ src/badguy/jumpy.cpp | 8 ++++++-- src/badguy/jumpy.hpp | 3 +++ src/badguy/kugelblitz.cpp | 7 ++++++- src/badguy/kugelblitz.hpp | 3 +++ src/badguy/mole.cpp | 8 ++++++-- src/badguy/mole.hpp | 3 +++ src/badguy/skydive.cpp | 7 ++++++- src/badguy/skydive.hpp | 3 +++ src/badguy/spidermite.cpp | 7 ++++++- src/badguy/spidermite.hpp | 3 +++ src/badguy/stalactite.cpp | 8 ++++++-- src/badguy/stalactite.hpp | 1 + src/badguy/willowisp.cpp | 8 ++++++-- src/badguy/willowisp.hpp | 3 +++ src/badguy/yeti.cpp | 7 ++++++- src/badguy/yeti.hpp | 3 +++ src/badguy/yeti_stalactite.cpp | 7 ++++++- src/badguy/yeti_stalactite.hpp | 3 +++ src/object/bumper.cpp | 19 +++++++++++++------ src/object/bumper.hpp | 4 +++- src/object/conveyor_belt.cpp | 17 ++++++++++++----- src/object/conveyor_belt.hpp | 2 +- src/object/ispy.cpp | 17 ++++++++++++----- src/object/ispy.hpp | 4 +++- src/object/pushbutton.cpp | 19 +++++++++++++------ src/object/pushbutton.hpp | 4 +++- src/trigger/switch.cpp | 19 +++++++++++++------ src/trigger/switch.hpp | 4 +++- 52 files changed, 274 insertions(+), 73 deletions(-) diff --git a/src/badguy/angrystone.cpp b/src/badguy/angrystone.cpp index b5bdc3bc300..d03beafb2f4 100644 --- a/src/badguy/angrystone.cpp +++ b/src/badguy/angrystone.cpp @@ -36,7 +36,6 @@ AngryStone::AngryStone(const ReaderMapping& reader) : m_physic.set_velocity_x(0); m_physic.set_velocity_y(0); m_physic.enable_gravity(true); - m_allowed_directions = {}; set_action("idle"); } @@ -198,4 +197,10 @@ AngryStone::is_flammable() const return false; } +std::vector +AngryStone::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/angrystone.hpp b/src/badguy/angrystone.hpp index d11c69259dc..22dbccb787b 100644 --- a/src/badguy/angrystone.hpp +++ b/src/badguy/angrystone.hpp @@ -39,6 +39,9 @@ class AngryStone final : public BadGuy static std::string display_name() { return _("Angry Stone"); } virtual std::string get_display_name() const override { return display_name(); } +protected: + virtual std::vector get_allowed_directions() const override; + protected: enum AngryStoneState { IDLE, diff --git a/src/badguy/badguy.cpp b/src/badguy/badguy.cpp index a6d9a3cbbab..84cdeb216af 100644 --- a/src/badguy/badguy.cpp +++ b/src/badguy/badguy.cpp @@ -58,7 +58,6 @@ BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite m_dir(direction), m_start_dir(direction), m_dir_in_allowed(0), - m_allowed_directions({Direction::AUTO, Direction::LEFT, Direction::RIGHT}), m_frozen(false), m_ignited(false), m_in_water(false), @@ -84,9 +83,11 @@ BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite m_dir = (m_start_dir == Direction::AUTO) ? Direction::LEFT : m_start_dir; m_lightsprite->set_blend(Blend::ADD); - for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + auto allowed_directions = get_allowed_directions(); + + for (int i = 0; i < static_cast(allowed_directions.size()); ++i) { - if (m_allowed_directions[i] == m_start_dir) + if (allowed_directions[i] == m_start_dir) { m_dir_in_allowed = i; break; @@ -105,7 +106,6 @@ BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name_, int m_dir(Direction::LEFT), m_start_dir(Direction::AUTO), m_dir_in_allowed(0), - m_allowed_directions({Direction::AUTO, Direction::LEFT, Direction::RIGHT}), m_frozen(false), m_ignited(false), m_in_water(false), @@ -138,9 +138,11 @@ BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name_, int m_dir = (m_start_dir == Direction::AUTO) ? Direction::LEFT : m_start_dir; m_lightsprite->set_blend(Blend::ADD); - for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + auto allowed_directions = get_allowed_directions(); + + for (int i = 0; i < static_cast(allowed_directions.size()); ++i) { - if (m_allowed_directions[i] == m_start_dir) + if (allowed_directions[i] == m_start_dir) { m_dir_in_allowed = i; break; @@ -327,6 +329,12 @@ BadGuy::deactivate() { } +std::vector +BadGuy::get_allowed_directions() const +{ + return {Direction::AUTO, Direction::LEFT, Direction::RIGHT}; +} + void BadGuy::active_update(float dt_sec) { @@ -1060,8 +1068,8 @@ BadGuy::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - if (!m_allowed_directions.empty()) - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::AUTO, "direction", 0, m_allowed_directions); + if (!get_allowed_directions().empty()) + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::AUTO, "direction", 0, get_allowed_directions()); result.add_script(_("Death script"), &m_dead_script, "dead-script"); result.reorder({"direction", "sprite", "x", "y"}); @@ -1074,7 +1082,9 @@ BadGuy::after_editor_set() { MovingSprite::after_editor_set(); - m_start_dir = m_allowed_directions[m_dir_in_allowed]; + if (!get_allowed_directions().empty()) + m_start_dir = get_allowed_directions()[m_dir_in_allowed]; + else m_start_dir = Direction::AUTO; if (m_dir == Direction::AUTO) { diff --git a/src/badguy/badguy.hpp b/src/badguy/badguy.hpp index 1a32fba3c03..b267edb32e1 100644 --- a/src/badguy/badguy.hpp +++ b/src/badguy/badguy.hpp @@ -22,10 +22,10 @@ #include "object/portable.hpp" #include "scripting/badguy.hpp" #include "squirrel/exposed_object.hpp" -#include "supertux/direction.hpp" #include "supertux/physic.hpp" #include "supertux/timer.hpp" +enum class Direction; class Player; class Bullet; @@ -184,6 +184,9 @@ class BadGuy : public MovingSprite, /** called when the badguy has been deactivated */ virtual void deactivate(); + /** returns a vector of dorections the BadGuy can be set to */ + virtual std::vector get_allowed_directions() const; + void kill_squished(GameObject& object); void set_state(State state); @@ -254,8 +257,6 @@ class BadGuy : public MovingSprite, does not correspond to the actual direction!*/ int m_dir_in_allowed; - std::vector m_allowed_directions; - bool m_frozen; bool m_ignited; /**< true if this badguy is currently on fire */ bool m_in_water; /** < true if the badguy is currently in water */ diff --git a/src/badguy/dart.cpp b/src/badguy/dart.cpp index 9aa9a909fcb..a9d42b96ac5 100644 --- a/src/badguy/dart.cpp +++ b/src/badguy/dart.cpp @@ -34,7 +34,6 @@ Dart::Dart(const ReaderMapping& reader) : { m_physic.enable_gravity(false); m_countMe = false; - m_allowed_directions = {Direction::AUTO, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; SoundManager::current()->preload(DART_SOUND); SoundManager::current()->preload("sounds/darthit.wav"); @@ -50,7 +49,6 @@ Dart::Dart(const Vector& pos, Direction d, const BadGuy* parent_, const std::str { m_physic.enable_gravity(false); m_countMe = false; - m_allowed_directions = {Direction::AUTO, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; SoundManager::current()->preload(DART_SOUND); SoundManager::current()->preload("sounds/darthit.wav"); @@ -157,6 +155,12 @@ Dart::set_flip(Flip flip) m_flip = flip; } +std::vector +Dart::get_allowed_directions() const +{ + return {Direction::AUTO, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; +} + void Dart::on_flip(float height) { diff --git a/src/badguy/dart.hpp b/src/badguy/dart.hpp index 9512126eb34..c9cbf317e2e 100644 --- a/src/badguy/dart.hpp +++ b/src/badguy/dart.hpp @@ -55,6 +55,9 @@ class Dart final : public BadGuy void set_flip(Flip flip); +protected: + virtual std::vector get_allowed_directions() const override; + protected: const BadGuy* parent; /**< collisions with this BadGuy will be ignored */ std::unique_ptr sound_source; /**< SoundSource for ambient sound */ diff --git a/src/badguy/darttrap.cpp b/src/badguy/darttrap.cpp index 0b0c401add0..8e1dd7d71a9 100644 --- a/src/badguy/darttrap.cpp +++ b/src/badguy/darttrap.cpp @@ -42,7 +42,6 @@ DartTrap::DartTrap(const ReaderMapping& reader) : reader.get("ammo", m_ammo, -1); reader.get("dart-sprite", m_dart_sprite, "images/creatures/darttrap/skull_dart.sprite"); m_countMe = false; - m_allowed_directions = {Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; SoundManager::current()->preload("sounds/dartfire.wav"); if (m_start_dir == Direction::AUTO) { log_warning << "Setting a DartTrap's direction to AUTO is no good idea" << std::endl; } m_state = IDLE; @@ -154,6 +153,12 @@ DartTrap::get_settings() return result; } +std::vector +DartTrap::get_allowed_directions() const +{ + return {Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; +} + void DartTrap::on_flip(float height) { diff --git a/src/badguy/darttrap.hpp b/src/badguy/darttrap.hpp index 8944c998e2a..43cbb7ce7fd 100644 --- a/src/badguy/darttrap.hpp +++ b/src/badguy/darttrap.hpp @@ -39,6 +39,9 @@ class DartTrap final : public BadGuy virtual void on_flip(float height) override; +protected: + virtual std::vector get_allowed_directions() const override; + protected: enum State { IDLE, LOADING diff --git a/src/badguy/dive_mine.cpp b/src/badguy/dive_mine.cpp index 8778574b39f..883dd3b3568 100644 --- a/src/badguy/dive_mine.cpp +++ b/src/badguy/dive_mine.cpp @@ -38,7 +38,6 @@ DiveMine::DiveMine(const ReaderMapping& reader) : void DiveMine::reset_sprites() { - m_allowed_directions = {}; set_action(m_dir); m_ticking_glow->set_action("idle"); } @@ -205,4 +204,10 @@ DiveMine::turn_around() m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT); } +std::vector +DiveMine::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/dive_mine.hpp b/src/badguy/dive_mine.hpp index 6fe8f50a9c7..c27b6f62cb3 100644 --- a/src/badguy/dive_mine.hpp +++ b/src/badguy/dive_mine.hpp @@ -48,6 +48,9 @@ class DiveMine final : public BadGuy static std::string display_name() { return _("Dive Mine"); } virtual std::string get_display_name() const override { return display_name(); } +protected: + virtual std::vector get_allowed_directions() const override; + private: void reset_sprites(); void stop_chasing(); diff --git a/src/badguy/fish_jumping.cpp b/src/badguy/fish_jumping.cpp index 3b656ca0436..c9ddf65c124 100644 --- a/src/badguy/fish_jumping.cpp +++ b/src/badguy/fish_jumping.cpp @@ -33,7 +33,6 @@ FishJumping::FishJumping(const ReaderMapping& reader) : m_stop_y(0) { m_physic.enable_gravity(true); - m_allowed_directions = {}; } void @@ -172,4 +171,10 @@ FishJumping::is_freezable() const return true; } +std::vector +FishJumping::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/fish_jumping.hpp b/src/badguy/fish_jumping.hpp index 7ebe1834941..ed97fe28182 100644 --- a/src/badguy/fish_jumping.hpp +++ b/src/badguy/fish_jumping.hpp @@ -41,6 +41,9 @@ class FishJumping final : public BadGuy static std::string display_name() { return _("Jumping Fish"); } virtual std::string get_display_name() const override { return display_name(); } +protected: + virtual std::vector get_allowed_directions() const override; + private: HitResponse hit(const CollisionHit& ); void start_waiting(); diff --git a/src/badguy/flame.cpp b/src/badguy/flame.cpp index ad072af4059..e9b93ea26d4 100644 --- a/src/badguy/flame.cpp +++ b/src/badguy/flame.cpp @@ -49,7 +49,6 @@ Flame::Flame(const ReaderMapping& reader, const std::string& sprite) : m_lightsprite->set_color(Color(0.21f, 0.13f, 0.08f)); m_glowing = true; - m_allowed_directions = {}; } ObjectSettings @@ -145,6 +144,12 @@ void Flame::play_looping_sounds() } } +std::vector +Flame::get_allowed_directions() const +{ + return {}; +} + void Flame::on_flip(float height) { diff --git a/src/badguy/flame.hpp b/src/badguy/flame.hpp index e0ca267873f..080551bae94 100644 --- a/src/badguy/flame.hpp +++ b/src/badguy/flame.hpp @@ -48,6 +48,9 @@ class Flame : public BadGuy virtual void on_flip(float height) override; +protected: + virtual std::vector get_allowed_directions() const override; + protected: float angle; float radius; diff --git a/src/badguy/flyingsnowball.cpp b/src/badguy/flyingsnowball.cpp index b4a7a60f07a..1a8b317a57a 100644 --- a/src/badguy/flyingsnowball.cpp +++ b/src/badguy/flyingsnowball.cpp @@ -35,7 +35,6 @@ FlyingSnowBall::FlyingSnowBall(const ReaderMapping& reader) : puff_timer() { m_physic.enable_gravity(false); - m_allowed_directions = {}; } void @@ -107,4 +106,10 @@ FlyingSnowBall::active_update(float dt_sec) } +std::vector +FlyingSnowBall::get_allowed_directions() const +{ + return std::vector(); +} + /* EOF */ diff --git a/src/badguy/flyingsnowball.hpp b/src/badguy/flyingsnowball.hpp index 65b98f8f075..6665ce29ab6 100644 --- a/src/badguy/flyingsnowball.hpp +++ b/src/badguy/flyingsnowball.hpp @@ -36,6 +36,7 @@ class FlyingSnowBall final : public BadGuy protected: virtual bool collision_squished(GameObject& object) override; + virtual std::vector get_allowed_directions() const override; private: float total_time_elapsed; diff --git a/src/badguy/ghostflame.cpp b/src/badguy/ghostflame.cpp index 18fc2bddd29..7b69516fe6b 100644 --- a/src/badguy/ghostflame.cpp +++ b/src/badguy/ghostflame.cpp @@ -20,7 +20,6 @@ Ghostflame::Ghostflame(const ReaderMapping& reader) : Flame(reader, "images/creatures/flame/ghostflame.sprite") { m_lightsprite->set_color(Color(0.21f, 0.00f, 0.21f)); - m_allowed_directions = {}; } bool @@ -35,4 +34,10 @@ Ghostflame::is_freezable() const return false; } +std::vector +Ghostflame::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/ghostflame.hpp b/src/badguy/ghostflame.hpp index 112c9308518..93fb3be7922 100644 --- a/src/badguy/ghostflame.hpp +++ b/src/badguy/ghostflame.hpp @@ -31,6 +31,9 @@ class Ghostflame final : public Flame static std::string display_name() { return _("Ghost Flame"); } virtual std::string get_display_name() const override { return display_name(); } +protected: + virtual std::vector get_allowed_directions() const override; + private: Ghostflame(const Ghostflame&) = delete; Ghostflame& operator=(const Ghostflame&) = delete; diff --git a/src/badguy/ghosttree.cpp b/src/badguy/ghosttree.cpp index daa6589abef..641b146f612 100644 --- a/src/badguy/ghosttree.cpp +++ b/src/badguy/ghosttree.cpp @@ -57,8 +57,6 @@ GhostTree::GhostTree(const ReaderMapping& mapping) : set_colgroup_active(COLGROUP_TOUCHABLE); SoundManager::current()->preload("sounds/tree_howling.ogg"); SoundManager::current()->preload("sounds/tree_suck.ogg"); - - m_allowed_directions = {}; } void @@ -264,6 +262,12 @@ GhostTree::spawn_lantern() Sector::get().add(m_col.m_bbox.get_middle() + SUCK_TARGET_OFFSET); } +std::vector +GhostTree::get_allowed_directions() const +{ + return {}; +} + void GhostTree::on_flip(float height) { diff --git a/src/badguy/ghosttree.hpp b/src/badguy/ghosttree.hpp index 21dd3d93477..caad21d9ca9 100644 --- a/src/badguy/ghosttree.hpp +++ b/src/badguy/ghosttree.hpp @@ -48,6 +48,9 @@ class GhostTree final : public BadGuy void willowisp_died(TreeWillOWisp* willowisp); void die(); +protected: + virtual std::vector get_allowed_directions() const override; + private: enum MyState { STATE_IDLE, STATE_SUCKING, STATE_SWALLOWING, STATE_DYING diff --git a/src/badguy/ghoul.cpp b/src/badguy/ghoul.cpp index 11b220e8e65..a4030dafd74 100644 --- a/src/badguy/ghoul.cpp +++ b/src/badguy/ghoul.cpp @@ -41,8 +41,6 @@ Ghoul::Ghoul(const ReaderMapping& reader) : init_path(reader, running); - m_allowed_directions = {}; - set_action(m_dir); } @@ -209,4 +207,10 @@ Ghoul::move_to(const Vector& pos) set_pos(pos); } +std::vector +Ghoul::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/ghoul.hpp b/src/badguy/ghoul.hpp index aa8de211424..c8cf1a22192 100644 --- a/src/badguy/ghoul.hpp +++ b/src/badguy/ghoul.hpp @@ -31,7 +31,7 @@ class Ghoul final : public BadGuy, std::string get_class_name() const override { return class_name(); } std::string get_display_name() const override { return display_name(); } bool is_freezable() const override; - bool is_flammable() const override; + bool is_flammable() const override; virtual bool is_snipable() const override { return true; } void finish_construction() override; @@ -49,6 +49,7 @@ class Ghoul final : public BadGuy, protected: bool collision_squished(GameObject& object) override; + std::vector get_allowed_directions() const override; private: enum MyState { diff --git a/src/badguy/iceflame.cpp b/src/badguy/iceflame.cpp index ddc41823bd3..d76f4808c2c 100644 --- a/src/badguy/iceflame.cpp +++ b/src/badguy/iceflame.cpp @@ -29,7 +29,6 @@ Iceflame::Iceflame(const ReaderMapping& reader) : Flame(reader, "images/creatures/flame/iceflame.sprite") { m_lightsprite->set_color(Color(0.00f, 0.13f, 0.18f)); - m_allowed_directions = {}; } void @@ -67,4 +66,10 @@ Iceflame::is_freezable() const return false; } +std::vector +Iceflame::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/iceflame.hpp b/src/badguy/iceflame.hpp index e45963d30ba..462106f46ce 100644 --- a/src/badguy/iceflame.hpp +++ b/src/badguy/iceflame.hpp @@ -34,6 +34,9 @@ class Iceflame final : public Flame static std::string display_name() { return _("Ice Flame"); } virtual std::string get_display_name() const override { return display_name(); } +protected: + virtual std::vector get_allowed_directions() const override; + private: Iceflame(const Iceflame&) = delete; Iceflame& operator=(const Iceflame&) = delete; diff --git a/src/badguy/jumpy.cpp b/src/badguy/jumpy.cpp index e8c21522252..c05bad04929 100644 --- a/src/badguy/jumpy.cpp +++ b/src/badguy/jumpy.cpp @@ -33,8 +33,6 @@ Jumpy::Jumpy(const ReaderMapping& reader) : set_action(m_dir, "middle"); // TODO create a nice sound for this... //SoundManager::current()->preload("sounds/skid.wav"); - - m_allowed_directions = {}; } void @@ -127,4 +125,10 @@ Jumpy::is_flammable() const return true; } +std::vector +Jumpy::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/jumpy.hpp b/src/badguy/jumpy.hpp index 39c6b2edfba..853ff044a11 100644 --- a/src/badguy/jumpy.hpp +++ b/src/badguy/jumpy.hpp @@ -40,6 +40,9 @@ class Jumpy final : public BadGuy static std::string display_name() { return _("Jumpy"); } virtual std::string get_display_name() const override { return display_name(); } +protected: + virtual std::vector get_allowed_directions() const override; + private: HitResponse hit(const CollisionHit& hit); diff --git a/src/badguy/kugelblitz.cpp b/src/badguy/kugelblitz.cpp index 2cce77f111e..e90530683af 100644 --- a/src/badguy/kugelblitz.cpp +++ b/src/badguy/kugelblitz.cpp @@ -49,7 +49,6 @@ Kugelblitz::Kugelblitz(const ReaderMapping& reader) : set_action("falling"); m_physic.enable_gravity(false); m_countMe = false; - m_allowed_directions = {}; lightsprite->set_blend(Blend::ADD); lightsprite->set_color(Color(0.2f, 0.1f, 0.0f)); @@ -212,4 +211,10 @@ Kugelblitz::is_flammable() const return false; } +std::vector +Kugelblitz::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/kugelblitz.hpp b/src/badguy/kugelblitz.hpp index 6d6348ceb48..a3fee55dc97 100644 --- a/src/badguy/kugelblitz.hpp +++ b/src/badguy/kugelblitz.hpp @@ -42,6 +42,9 @@ class Kugelblitz final : public BadGuy void explode(); +protected: + virtual std::vector get_allowed_directions() const override; + private: void try_activate(); HitResponse hit(const CollisionHit& hit); diff --git a/src/badguy/mole.cpp b/src/badguy/mole.cpp index 0c1add77692..d58d3f1e34c 100644 --- a/src/badguy/mole.cpp +++ b/src/badguy/mole.cpp @@ -41,8 +41,6 @@ Mole::Mole(const ReaderMapping& reader) : SoundManager::current()->preload("sounds/fall.wav"); SoundManager::current()->preload("sounds/squish.wav"); SoundManager::current()->preload("sounds/dartfire.wav"); - - m_allowed_directions = {}; } void @@ -172,6 +170,12 @@ Mole::ignite() { SoundManager::current()->play("sounds/fire.ogg", get_pos()); } +std::vector +Mole::get_allowed_directions() const +{ + return {}; +} + void Mole::on_flip(float height) { diff --git a/src/badguy/mole.hpp b/src/badguy/mole.hpp index 64191a7f847..8f92c982df1 100644 --- a/src/badguy/mole.hpp +++ b/src/badguy/mole.hpp @@ -40,6 +40,9 @@ class Mole final : public BadGuy virtual void on_flip(float height) override; +protected: + virtual std::vector get_allowed_directions() const override; + private: enum MoleState { PRE_THROWING, diff --git a/src/badguy/skydive.cpp b/src/badguy/skydive.cpp index a85d175c565..9d76be0e349 100644 --- a/src/badguy/skydive.cpp +++ b/src/badguy/skydive.cpp @@ -28,7 +28,6 @@ SkyDive::SkyDive(const ReaderMapping& reader) : BadGuy(reader, "images/creatures/skydive/skydive.sprite") { - m_allowed_directions = {}; SoundManager::current()->preload("sounds/explosion.wav"); set_action("normal", 1); } @@ -196,4 +195,10 @@ SkyDive::is_portable() const return true; } +std::vector +SkyDive::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/skydive.hpp b/src/badguy/skydive.hpp index 7a12c7d803d..1a718f203d3 100644 --- a/src/badguy/skydive.hpp +++ b/src/badguy/skydive.hpp @@ -43,6 +43,9 @@ class SkyDive final : public BadGuy virtual std::string get_display_name() const override { return display_name(); } virtual bool is_snipable() const override { return true; } +protected: + virtual std::vector get_allowed_directions() const override; + private: virtual HitResponse collision_player(Player& player, const CollisionHit& hit) override; virtual bool collision_squished (GameObject& obj) override; diff --git a/src/badguy/spidermite.cpp b/src/badguy/spidermite.cpp index f725e6c3258..2bb995d091a 100644 --- a/src/badguy/spidermite.cpp +++ b/src/badguy/spidermite.cpp @@ -28,7 +28,6 @@ SpiderMite::SpiderMite(const ReaderMapping& reader) : timer() { m_physic.enable_gravity(false); - m_allowed_directions = {}; } void @@ -109,4 +108,10 @@ SpiderMite::is_freezable() const return true; } +std::vector +SpiderMite::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/spidermite.hpp b/src/badguy/spidermite.hpp index 264df91a9f7..6061e5e0f01 100644 --- a/src/badguy/spidermite.hpp +++ b/src/badguy/spidermite.hpp @@ -39,6 +39,9 @@ class SpiderMite final : public BadGuy virtual std::string get_display_name() const override { return display_name(); } virtual bool is_snipable() const override { return true; } +protected: + virtual std::vector get_allowed_directions() const override; + protected: enum SpiderMiteMode { FLY_UP, diff --git a/src/badguy/stalactite.cpp b/src/badguy/stalactite.cpp index d5f3ee21856..82afe0c9540 100644 --- a/src/badguy/stalactite.cpp +++ b/src/badguy/stalactite.cpp @@ -46,8 +46,6 @@ Stalactite::Stalactite(const ReaderMapping& mapping) : SoundManager::current()->preload("sounds/cracking.wav"); SoundManager::current()->preload("sounds/sizzle.ogg"); SoundManager::current()->preload("sounds/icecrash.ogg"); - - m_allowed_directions = {}; } void @@ -200,6 +198,12 @@ Stalactite::deactivate() remove_me(); } +std::vector +Stalactite::get_allowed_directions() const +{ + return {}; +} + void Stalactite::on_flip(float height) { diff --git a/src/badguy/stalactite.hpp b/src/badguy/stalactite.hpp index ec73a7fc645..23f86a0fdca 100644 --- a/src/badguy/stalactite.hpp +++ b/src/badguy/stalactite.hpp @@ -47,6 +47,7 @@ class Stalactite : public BadGuy protected: void on_type_change(int old_type) override; + std::vector get_allowed_directions() const override; protected: enum StalactiteType { diff --git a/src/badguy/willowisp.cpp b/src/badguy/willowisp.cpp index 90d29d90e6b..2163ecaac1d 100644 --- a/src/badguy/willowisp.cpp +++ b/src/badguy/willowisp.cpp @@ -85,8 +85,6 @@ WillOWisp::WillOWisp(const ReaderMapping& reader) : m_sprite->set_color(m_color); m_glowing = true; - m_allowed_directions = {}; - set_action("idle"); } @@ -353,6 +351,12 @@ WillOWisp::move_to(const Vector& pos) set_pos(pos); } +std::vector +WillOWisp::get_allowed_directions() const +{ + return {}; +} + void WillOWisp::on_flip(float height) { diff --git a/src/badguy/willowisp.hpp b/src/badguy/willowisp.hpp index 8f356e3919c..1b2a90fef7d 100644 --- a/src/badguy/willowisp.hpp +++ b/src/badguy/willowisp.hpp @@ -77,6 +77,9 @@ class WillOWisp final : Color get_color() const { return m_color; } +protected: + virtual std::vector get_allowed_directions() const override; + private: virtual bool collides(GameObject& other, const CollisionHit& hit) const override; virtual HitResponse collision_player(Player& player, const CollisionHit& hit) override; diff --git a/src/badguy/yeti.cpp b/src/badguy/yeti.cpp index d8f721c53fb..6f3eeec0d5c 100644 --- a/src/badguy/yeti.cpp +++ b/src/badguy/yeti.cpp @@ -76,7 +76,6 @@ Yeti::Yeti(const ReaderMapping& reader) : reader.get("hud-icon", m_hud_icon, "images/creatures/yeti/hudlife.png"); m_hud_head = Surface::from_file(m_hud_icon); - m_allowed_directions = {}; initialize(); @@ -399,4 +398,10 @@ Yeti::SnowExplosionParticle::SnowExplosionParticle(const Vector& pos, const Vect m_layer = Sector::get().get_foremost_layer() + 1; } +std::vector +Yeti::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/yeti.hpp b/src/badguy/yeti.hpp index 4fcb8e33d09..68aea592bb5 100644 --- a/src/badguy/yeti.hpp +++ b/src/badguy/yeti.hpp @@ -42,6 +42,9 @@ class Yeti final : public BadGuy void kill_squished(GameObject& object); +protected: + virtual std::vector get_allowed_directions() const override; + private: void run(); void jump_up(); diff --git a/src/badguy/yeti_stalactite.cpp b/src/badguy/yeti_stalactite.cpp index e3b9a4d8440..e8f642041a4 100644 --- a/src/badguy/yeti_stalactite.cpp +++ b/src/badguy/yeti_stalactite.cpp @@ -24,7 +24,6 @@ static const float YT_SHAKE_TIME = .8f; YetiStalactite::YetiStalactite(const ReaderMapping& mapping) : Stalactite(mapping) { - m_allowed_directions = {}; } void @@ -92,4 +91,10 @@ YetiStalactite::is_flammable() const return false; } +std::vector +YetiStalactite::get_allowed_directions() const +{ + return {}; +} + /* EOF */ diff --git a/src/badguy/yeti_stalactite.hpp b/src/badguy/yeti_stalactite.hpp index c3f4c943c34..70a4bd01460 100644 --- a/src/badguy/yeti_stalactite.hpp +++ b/src/badguy/yeti_stalactite.hpp @@ -37,6 +37,9 @@ class YetiStalactite final : public Stalactite void start_shaking(); bool is_hanging() const; +protected: + virtual std::vector get_allowed_directions() const override; + private: YetiStalactite(const YetiStalactite&) = delete; YetiStalactite& operator=(const YetiStalactite&) = delete; diff --git a/src/object/bumper.cpp b/src/object/bumper.cpp index ba65455189a..b7567a890f8 100644 --- a/src/object/bumper.cpp +++ b/src/object/bumper.cpp @@ -33,8 +33,7 @@ Bumper::Bumper(const ReaderMapping& reader) : MovingSprite(reader, "images/objects/trampoline/bumper.sprite", LAYER_OBJECTS, COLGROUP_MOVING), m_physic(), m_dir(Direction::RIGHT), - m_dir_in_allowed(0), - m_allowed_directions({Direction::LEFT, Direction::RIGHT}) + m_dir_in_allowed(0) { std::string dir_str; bool old_facing_left = false; @@ -46,9 +45,11 @@ Bumper::Bumper(const ReaderMapping& reader) : set_action("normal", m_dir); m_physic.enable_gravity(false); - for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + auto allowed_directions = get_allowed_directions(); + + for (int i = 0; i < static_cast(allowed_directions.size()); ++i) { - if (m_allowed_directions[i] == m_dir) + if (allowed_directions[i] == m_dir) { m_dir_in_allowed = i; break; @@ -61,7 +62,7 @@ Bumper::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::RIGHT, "direction", 0, m_allowed_directions); + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::RIGHT, "direction", 0, get_allowed_directions()); result.reorder({"direction", "sprite", "x", "y"}); return result; } @@ -101,12 +102,18 @@ Bumper::get_physic() return m_physic; } +std::vector +Bumper::get_allowed_directions() const +{ + return {Direction::LEFT, Direction::RIGHT}; +} + void Bumper::after_editor_set() { MovingSprite::after_editor_set(); - m_dir = m_allowed_directions[m_dir_in_allowed]; + m_dir = get_allowed_directions()[m_dir_in_allowed]; set_action("normal", m_dir); } diff --git a/src/object/bumper.hpp b/src/object/bumper.hpp index 42038540223..c5da8ffaad0 100644 --- a/src/object/bumper.hpp +++ b/src/object/bumper.hpp @@ -43,12 +43,14 @@ class Bumper final : public MovingSprite Physic& get_physic(); +private: + virtual std::vector get_allowed_directions() const; + private: Physic m_physic; Direction m_dir; int m_dir_in_allowed; - std::vector m_allowed_directions; private: Bumper(const Bumper&) = delete; diff --git a/src/object/conveyor_belt.cpp b/src/object/conveyor_belt.cpp index b98af43d092..6498ba04b05 100644 --- a/src/object/conveyor_belt.cpp +++ b/src/object/conveyor_belt.cpp @@ -29,7 +29,6 @@ ConveyorBelt::ConveyorBelt(const ReaderMapping &reader) : m_running(true), m_dir(Direction::LEFT), m_dir_in_allowed(0), - m_allowed_directions({Direction::LEFT, Direction::RIGHT}), m_length(1), m_speed(1.0f), m_frame(0.0f), @@ -53,9 +52,11 @@ ConveyorBelt::ConveyorBelt(const ReaderMapping &reader) : else set_action(m_dir); - for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + auto allowed_directions = get_allowed_directions(); + + for (int i = 0; i < static_cast(allowed_directions.size()); ++i) { - if (m_allowed_directions[i] == m_dir) + if (allowed_directions[i] == m_dir) { m_dir_in_allowed = i; break; @@ -68,7 +69,7 @@ ConveyorBelt::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::LEFT, "direction", 0, m_allowed_directions); + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::LEFT, "direction", 0, get_allowed_directions()); result.add_float(_("Speed"), &m_speed, "speed", 1.0f); result.add_bool(_("Running"), &m_running, "running", true); result.add_int(_("Length"), &m_length, "length", 3); @@ -137,7 +138,7 @@ ConveyorBelt::after_editor_set() { MovingSprite::after_editor_set(); - m_dir = m_allowed_directions[m_dir_in_allowed]; + m_dir = get_allowed_directions()[m_dir_in_allowed]; if (m_length <= 0) m_length = 1; @@ -181,4 +182,10 @@ ConveyorBelt::set_speed(float target_speed) m_speed = target_speed; } +std::vector +ConveyorBelt::get_allowed_directions() const +{ + return {Direction::LEFT, Direction::RIGHT}; +} + /* EOF */ diff --git a/src/object/conveyor_belt.hpp b/src/object/conveyor_belt.hpp index 7c7e0c740cc..ad240022498 100644 --- a/src/object/conveyor_belt.hpp +++ b/src/object/conveyor_belt.hpp @@ -65,12 +65,12 @@ class ConveyorBelt final : public MovingSprite, private: void update_hitbox() override; + virtual std::vector get_allowed_directions() const; private: bool m_running; Direction m_dir; int m_dir_in_allowed; - std::vector m_allowed_directions; int m_length; float m_speed; diff --git a/src/object/ispy.cpp b/src/object/ispy.cpp index 1f5e71899ea..4b91ea69d3d 100644 --- a/src/object/ispy.cpp +++ b/src/object/ispy.cpp @@ -30,7 +30,6 @@ Ispy::Ispy(const ReaderMapping& reader) : m_state(ISPYSTATE_IDLE), m_script(), m_dir(Direction::LEFT), - m_allowed_directions({Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}), m_dir_in_allowed(0) { reader.get("script", m_script); @@ -44,9 +43,11 @@ Ispy::Ispy(const ReaderMapping& reader) : if (m_dir == Direction::AUTO) log_warning << "Setting an Ispy's direction to AUTO is no good idea." << std::endl; - for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + auto allowed_directions = get_allowed_directions(); + + for (int i = 0; i < static_cast(allowed_directions.size()); ++i) { - if (m_allowed_directions[i] == m_dir) + if (allowed_directions[i] == m_dir) { m_dir_in_allowed = i; break; @@ -62,7 +63,7 @@ Ispy::get_settings() ObjectSettings result = MovingSprite::get_settings(); result.add_script(_("Script"), &m_script, "script"); - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::LEFT, "direction", 0, m_allowed_directions); + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::LEFT, "direction", 0, get_allowed_directions()); result.reorder({"script", "facing-down", "direction", "x", "y"}); @@ -73,7 +74,7 @@ void Ispy::after_editor_set() { MovingSprite::after_editor_set(); - m_dir = m_allowed_directions[m_dir_in_allowed]; + m_dir = get_allowed_directions()[m_dir_in_allowed]; set_action("idle", m_dir); } @@ -135,6 +136,12 @@ Ispy::update(float dt_sec) } } +std::vector +Ispy::get_allowed_directions() const +{ + return {Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; +} + void Ispy::on_flip(float height) { diff --git a/src/object/ispy.hpp b/src/object/ispy.hpp index 33255819ec7..2474c3f9f8f 100644 --- a/src/object/ispy.hpp +++ b/src/object/ispy.hpp @@ -40,6 +40,9 @@ class Ispy final : public MovingSprite virtual void on_flip(float height) override; +private: + virtual std::vector get_allowed_directions() const; + private: enum IspyState { ISPYSTATE_IDLE, @@ -53,7 +56,6 @@ class Ispy final : public MovingSprite std::string m_script; /**< script to execute when Tux is spotted */ Direction m_dir; - std::vector m_allowed_directions; int m_dir_in_allowed; private: diff --git a/src/object/pushbutton.cpp b/src/object/pushbutton.cpp index 4e13f579ab9..d691a8dbfa6 100644 --- a/src/object/pushbutton.cpp +++ b/src/object/pushbutton.cpp @@ -35,8 +35,7 @@ PushButton::PushButton(const ReaderMapping& mapping) : m_script(), m_state(OFF), m_dir(Direction::UP), - m_dir_in_allowed(0), - m_allowed_directions({Direction::UP, Direction::DOWN}) + m_dir_in_allowed(0) { SoundManager::current()->preload(BUTTON_SOUND); @@ -53,9 +52,11 @@ PushButton::PushButton(const ReaderMapping& mapping) : else if (mapping.get("upside-down", old_upside_down) && old_upside_down) m_dir = Direction::DOWN; - for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + auto allowed_directions = get_allowed_directions(); + + for (int i = 0; i < static_cast(allowed_directions.size()); ++i) { - if (m_allowed_directions[i] == m_dir) + if (allowed_directions[i] == m_dir) { m_dir_in_allowed = i; break; @@ -70,7 +71,7 @@ PushButton::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::UP, "direction", 0, m_allowed_directions); + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::UP, "direction", 0, get_allowed_directions()); result.add_script(_("Script"), &m_script, "script"); result.reorder({"direction", "script", "x", "y"}); @@ -82,7 +83,7 @@ void PushButton::after_editor_set() { MovingSprite::after_editor_set(); - m_dir = m_allowed_directions[m_dir_in_allowed]; + m_dir = get_allowed_directions()[m_dir_in_allowed]; set_action("off", m_dir); } @@ -143,6 +144,12 @@ PushButton::collision(GameObject& other, const CollisionHit& hit) return FORCE_MOVE; } +std::vector +PushButton::get_allowed_directions() const +{ + return {Direction::UP, Direction::DOWN}; +} + void PushButton::on_flip(float height) { diff --git a/src/object/pushbutton.hpp b/src/object/pushbutton.hpp index dacd2da24c6..b0800dac96a 100644 --- a/src/object/pushbutton.hpp +++ b/src/object/pushbutton.hpp @@ -39,6 +39,9 @@ class PushButton final : public MovingSprite virtual void on_flip(float height) override; +private: + virtual std::vector get_allowed_directions() const; + private: enum PushButtonState { OFF, @@ -50,7 +53,6 @@ class PushButton final : public MovingSprite Direction m_dir; int m_dir_in_allowed; - std::vector m_allowed_directions; private: PushButton(const PushButton&) = delete; diff --git a/src/trigger/switch.cpp b/src/trigger/switch.cpp index b31b51cedd3..dd164e673e1 100644 --- a/src/trigger/switch.cpp +++ b/src/trigger/switch.cpp @@ -35,8 +35,7 @@ Switch::Switch(const ReaderMapping& reader) : m_state(OFF), m_bistable(), m_dir(Direction::NONE), - m_dir_in_allowed(0), - m_allowed_directions({Direction::NONE, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}) + m_dir_in_allowed(0) { std::string dir_str; if (reader.get("direction", dir_str)) @@ -44,9 +43,11 @@ Switch::Switch(const ReaderMapping& reader) : else m_dir = Direction::NONE; - for (int i = 0; i < static_cast(m_allowed_directions.size()); ++i) + auto allowed_directions = get_allowed_directions(); + + for (int i = 0; i < static_cast(allowed_directions.size()); ++i) { - if (m_allowed_directions[i] == m_dir) + if (allowed_directions[i] == m_dir) { m_dir_in_allowed = i; break; @@ -70,7 +71,7 @@ Switch::get_settings() { ObjectSettings result = SpritedTrigger::get_settings(); - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), m_allowed_directions[0], "direction", 0, m_allowed_directions); + result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::NONE, "direction", 0, get_allowed_directions()); result.add_script(_("Turn on script"), &m_script, "script"); result.add_script(_("Turn off script"), &m_off_script, "off-script"); @@ -142,12 +143,18 @@ Switch::event(Player& , EventType type) } } +std::vector +Switch::get_allowed_directions() const +{ + return {Direction::NONE, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; +} + void Switch::after_editor_set() { SpritedTrigger::after_editor_set(); - m_dir = m_allowed_directions[m_dir_in_allowed]; + m_dir = get_allowed_directions()[m_dir_in_allowed]; set_action("off", m_dir); } diff --git a/src/trigger/switch.hpp b/src/trigger/switch.hpp index fc21eda7f9a..267032d629d 100644 --- a/src/trigger/switch.hpp +++ b/src/trigger/switch.hpp @@ -39,6 +39,9 @@ class Switch final : public SpritedTrigger virtual void after_editor_set() override; virtual void on_flip(float height) override; +private: + virtual std::vector get_allowed_directions() const; + private: enum SwitchState { OFF, @@ -54,7 +57,6 @@ class Switch final : public SpritedTrigger bool m_bistable; Direction m_dir; int m_dir_in_allowed; - std::vector m_allowed_directions; private: Switch(const Switch&) = delete; From 04efaf249290248fb1b64040c5da5e1faae64ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Sat, 5 Aug 2023 23:44:46 +0200 Subject: [PATCH 05/14] fix darttrap segfault --- src/badguy/darttrap.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/badguy/darttrap.cpp b/src/badguy/darttrap.cpp index 8e1dd7d71a9..892cfcb35d3 100644 --- a/src/badguy/darttrap.cpp +++ b/src/badguy/darttrap.cpp @@ -50,6 +50,17 @@ DartTrap::DartTrap(const ReaderMapping& reader) : if (!Editor::is_active()) { if (m_initial_delay == 0) m_initial_delay = 0.1f; } + + auto allowed_directions = get_allowed_directions(); + + for (int i = 0; i < static_cast(allowed_directions.size()); ++i) + { + if (allowed_directions[i] == m_start_dir) + { + m_dir_in_allowed = i; + break; + } + } } void From 8d97dca313c0790c272b22ac4d85da1e854004a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Sun, 6 Aug 2023 02:33:40 +0200 Subject: [PATCH 06/14] consistency [ci skip] --- src/badguy/flyingsnowball.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/badguy/flyingsnowball.cpp b/src/badguy/flyingsnowball.cpp index 1a8b317a57a..3cc71ceb2e8 100644 --- a/src/badguy/flyingsnowball.cpp +++ b/src/badguy/flyingsnowball.cpp @@ -109,7 +109,7 @@ FlyingSnowBall::active_update(float dt_sec) std::vector FlyingSnowBall::get_allowed_directions() const { - return std::vector(); + return {}; } /* EOF */ From 26b5821bdd0b52c86338377af8447a41972293b3 Mon Sep 17 00:00:00 2001 From: Vankata453 <78196474+Vankata453@users.noreply.github.com> Date: Tue, 8 Aug 2023 02:50:34 +0300 Subject: [PATCH 07/14] Use `DirectionOption` to manage custom directions, code fixes --- src/badguy/badguy.cpp | 32 ++-------------------- src/badguy/badguy.hpp | 6 +--- src/badguy/dart.cpp | 2 +- src/badguy/darttrap.cpp | 13 +-------- src/editor/object_option.cpp | 50 ++++++++++++++++++++++++++++++++++ src/editor/object_option.hpp | 23 +++++++++++++++- src/editor/object_settings.cpp | 42 ++++------------------------ src/editor/object_settings.hpp | 5 ++-- src/gui/item_stringselect.cpp | 17 ++++++++++++ src/gui/item_stringselect.hpp | 4 +++ src/gui/menu.cpp | 9 ++++++ src/gui/menu.hpp | 1 + src/object/bumper.cpp | 19 ++----------- src/object/bumper.hpp | 3 +- src/object/conveyor_belt.cpp | 18 ++---------- src/object/conveyor_belt.hpp | 4 +-- src/object/ispy.cpp | 19 ++----------- src/object/ispy.hpp | 3 +- src/object/pushbutton.cpp | 20 ++------------ src/object/pushbutton.hpp | 5 +--- src/supertux/direction.cpp | 25 +++++++++++++++++ src/supertux/direction.hpp | 1 + src/trigger/switch.cpp | 19 ++----------- src/trigger/switch.hpp | 4 +-- 24 files changed, 161 insertions(+), 183 deletions(-) diff --git a/src/badguy/badguy.cpp b/src/badguy/badguy.cpp index 84cdeb216af..2b6ba4bc81c 100644 --- a/src/badguy/badguy.cpp +++ b/src/badguy/badguy.cpp @@ -57,7 +57,6 @@ BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite m_start_position(m_col.m_bbox.p1()), m_dir(direction), m_start_dir(direction), - m_dir_in_allowed(0), m_frozen(false), m_ignited(false), m_in_water(false), @@ -82,17 +81,6 @@ BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite m_dir = (m_start_dir == Direction::AUTO) ? Direction::LEFT : m_start_dir; m_lightsprite->set_blend(Blend::ADD); - - auto allowed_directions = get_allowed_directions(); - - for (int i = 0; i < static_cast(allowed_directions.size()); ++i) - { - if (allowed_directions[i] == m_start_dir) - { - m_dir_in_allowed = i; - break; - } - } } BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name_, int layer_, @@ -105,7 +93,6 @@ BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name_, int m_start_position(m_col.m_bbox.p1()), m_dir(Direction::LEFT), m_start_dir(Direction::AUTO), - m_dir_in_allowed(0), m_frozen(false), m_ignited(false), m_in_water(false), @@ -137,17 +124,6 @@ BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name_, int m_dir = (m_start_dir == Direction::AUTO) ? Direction::LEFT : m_start_dir; m_lightsprite->set_blend(Blend::ADD); - - auto allowed_directions = get_allowed_directions(); - - for (int i = 0; i < static_cast(allowed_directions.size()); ++i) - { - if (allowed_directions[i] == m_start_dir) - { - m_dir_in_allowed = i; - break; - } - } } void @@ -332,7 +308,7 @@ BadGuy::deactivate() std::vector BadGuy::get_allowed_directions() const { - return {Direction::AUTO, Direction::LEFT, Direction::RIGHT}; + return { Direction::AUTO, Direction::LEFT, Direction::RIGHT }; } void @@ -1069,7 +1045,7 @@ BadGuy::get_settings() ObjectSettings result = MovingSprite::get_settings(); if (!get_allowed_directions().empty()) - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::AUTO, "direction", 0, get_allowed_directions()); + result.add_direction(_("Direction"), &m_start_dir, get_allowed_directions(), "direction"); result.add_script(_("Death script"), &m_dead_script, "dead-script"); result.reorder({"direction", "sprite", "x", "y"}); @@ -1082,10 +1058,6 @@ BadGuy::after_editor_set() { MovingSprite::after_editor_set(); - if (!get_allowed_directions().empty()) - m_start_dir = get_allowed_directions()[m_dir_in_allowed]; - else m_start_dir = Direction::AUTO; - if (m_dir == Direction::AUTO) { if (m_sprite->has_action("editor-left")) { diff --git a/src/badguy/badguy.hpp b/src/badguy/badguy.hpp index b267edb32e1..acbed144992 100644 --- a/src/badguy/badguy.hpp +++ b/src/badguy/badguy.hpp @@ -184,7 +184,7 @@ class BadGuy : public MovingSprite, /** called when the badguy has been deactivated */ virtual void deactivate(); - /** returns a vector of dorections the BadGuy can be set to */ + /** Returns a vector of directions the badguy can be set to. */ virtual std::vector get_allowed_directions() const; void kill_squished(GameObject& object); @@ -253,10 +253,6 @@ class BadGuy : public MovingSprite, /** The direction we initially faced in */ Direction m_start_dir; - /** The order of the direction in the allowed directions, - does not correspond to the actual direction!*/ - int m_dir_in_allowed; - bool m_frozen; bool m_ignited; /**< true if this badguy is currently on fire */ bool m_in_water; /** < true if the badguy is currently in water */ diff --git a/src/badguy/dart.cpp b/src/badguy/dart.cpp index a9d42b96ac5..d59e5e2c76e 100644 --- a/src/badguy/dart.cpp +++ b/src/badguy/dart.cpp @@ -158,7 +158,7 @@ Dart::set_flip(Flip flip) std::vector Dart::get_allowed_directions() const { - return {Direction::AUTO, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; + return { Direction::AUTO, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN }; } void diff --git a/src/badguy/darttrap.cpp b/src/badguy/darttrap.cpp index 892cfcb35d3..47f2819f470 100644 --- a/src/badguy/darttrap.cpp +++ b/src/badguy/darttrap.cpp @@ -50,17 +50,6 @@ DartTrap::DartTrap(const ReaderMapping& reader) : if (!Editor::is_active()) { if (m_initial_delay == 0) m_initial_delay = 0.1f; } - - auto allowed_directions = get_allowed_directions(); - - for (int i = 0; i < static_cast(allowed_directions.size()); ++i) - { - if (allowed_directions[i] == m_start_dir) - { - m_dir_in_allowed = i; - break; - } - } } void @@ -167,7 +156,7 @@ DartTrap::get_settings() std::vector DartTrap::get_allowed_directions() const { - return {Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; + return { Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN }; } void diff --git a/src/editor/object_option.cpp b/src/editor/object_option.cpp index f4b4d48900a..c1799423385 100644 --- a/src/editor/object_option.cpp +++ b/src/editor/object_option.cpp @@ -23,10 +23,12 @@ #include #include "editor/object_menu.hpp" +#include "gui/item_stringselect.hpp" #include "gui/menu.hpp" #include "gui/menu_manager.hpp" #include "gui/menu_object_select.hpp" #include "object/tilemap.hpp" +#include "supertux/direction.hpp" #include "supertux/moving_object.hpp" #include "util/gettext.hpp" #include "util/writer.hpp" @@ -769,4 +771,52 @@ ListOption::add_to_menu(Menu& menu) const { menu.add_list(get_text(), m_items, m_value_ptr); } + +DirectionOption::DirectionOption(const std::string& text, Direction* value_ptr, + std::vector possible_directions, + const std::string& key, unsigned int flags) : + ObjectOption(text, key, flags), + m_value_ptr(value_ptr), + m_possible_directions(std::move(possible_directions)) +{ + if (m_possible_directions.empty()) + m_possible_directions = { Direction::AUTO, Direction::NONE, Direction::LEFT, + Direction::RIGHT, Direction::UP, Direction::DOWN }; +} + +void +DirectionOption::save(Writer& writer) const +{ + if (*m_value_ptr == m_possible_directions.at(0)) + return; + + writer.write(get_key(), dir_to_string(*m_value_ptr)); +} + +std::string +DirectionOption::to_string() const +{ + return dir_to_translated_string(*m_value_ptr); +} + +void +DirectionOption::add_to_menu(Menu& menu) const +{ + int selected = 0; + std::vector labels; + for (size_t i = 0; i < m_possible_directions.size(); i++) + { + const auto& dir = m_possible_directions.at(i); + labels.push_back(dir_to_translated_string(dir)); + + if (dir == *m_value_ptr) + selected = static_cast(i); + } + + menu.add_string_select(-1, get_text(), selected, labels) + .set_callback([value_ptr = m_value_ptr, possible_directions = m_possible_directions](int selected) { + *value_ptr = possible_directions.at(selected); + }); +} + /* EOF */ diff --git a/src/editor/object_option.hpp b/src/editor/object_option.hpp index 5258588fe44..756163b357b 100644 --- a/src/editor/object_option.hpp +++ b/src/editor/object_option.hpp @@ -40,8 +40,9 @@ namespace sexp { class Value; } // namespace sexp class Color; -class Menu; +enum class Direction; class GameObject; +class Menu; class Path; class PathObject; class Rectf; @@ -521,6 +522,26 @@ class ListOption : public ObjectOption ListOption& operator=(const ListOption&) = delete; }; +class DirectionOption : public ObjectOption +{ +public: + DirectionOption(const std::string& text, Direction* value_ptr, + std::vector possible_directions, + const std::string& key, unsigned int flags); + + virtual void save(Writer& write) const override; + virtual std::string to_string() const override; + virtual void add_to_menu(Menu& menu) const override; + +private: + Direction* m_value_ptr; + std::vector m_possible_directions; + +private: + DirectionOption(const DirectionOption&) = delete; + DirectionOption& operator=(const DirectionOption&) = delete; +}; + #endif /* EOF */ diff --git a/src/editor/object_settings.cpp b/src/editor/object_settings.cpp index c2622ce09b3..73a8f1462a4 100644 --- a/src/editor/object_settings.cpp +++ b/src/editor/object_settings.cpp @@ -19,7 +19,6 @@ #include #include -#include "supertux/direction.hpp" #include "util/gettext.hpp" #include "video/color.hpp" @@ -113,42 +112,11 @@ ObjectSettings::add_rectf(const std::string& text, Rectf* value_ptr, void ObjectSettings::add_direction(const std::string& text, Direction* value_ptr, - std::optional default_value, - const std::string& key, unsigned int flags, - std::vector possible_directions) -{ - std::vector direction_labels, direction_symbols; - if (!possible_directions.empty()) - { - for (Direction direction : possible_directions) - { - direction_labels.push_back(_(dir_to_string(direction))); - direction_symbols.push_back(dir_to_string(direction)); - } - } - else - { - direction_labels = {_("auto"), _("none"), _("left"), _("right"), _("up"), _("down")}; - direction_symbols = {"auto", "none", "left", "right", "up", "down"}; - } - - int new_default_value = -1; - if (default_value) - { - for (int i = 0; i < static_cast(direction_symbols.size()); ++i) - { - if (string_to_dir(direction_symbols[i]) == *default_value) - { - new_default_value = i; - break; - } - } - } - - add_enum(text, reinterpret_cast(value_ptr), - direction_labels, direction_symbols, - default_value ? new_default_value : std::optional(), - key, flags); + std::vector possible_directions, + const std::string& key, unsigned int flags) +{ + add_option(std::make_unique(text, value_ptr, std::move(possible_directions), + key, flags)); } void diff --git a/src/editor/object_settings.hpp b/src/editor/object_settings.hpp index 91917f3fa18..b8f60845993 100644 --- a/src/editor/object_settings.hpp +++ b/src/editor/object_settings.hpp @@ -63,9 +63,8 @@ class ObjectSettings final std::optional default_value = {}, const std::string& key = {}, unsigned int flags = 0); void add_direction(const std::string& text, Direction* value_ptr, - std::optional default_value = {}, - const std::string& key = {}, unsigned int flags = 0, - std::vector possible_directions = {}); + std::vector possible_directions = {}, + const std::string& key = {}, unsigned int flags = 0); void add_walk_mode(const std::string& text, WalkMode* value_ptr, const std::optional& default_value = {}, const std::string& key = {}, unsigned int flags = 0); diff --git a/src/gui/item_stringselect.cpp b/src/gui/item_stringselect.cpp index f81784c5ae3..288a80c21ad 100644 --- a/src/gui/item_stringselect.cpp +++ b/src/gui/item_stringselect.cpp @@ -28,11 +28,28 @@ ItemStringSelect::ItemStringSelect(const std::string& text, std::vector items, int default_item, int id) : + MenuItem(text, id), + m_items(std::move(items)), + m_selected(new int(default_item)), + m_pointer_provided(false), + m_callback(), + m_width(calculate_width()) +{ +} + +ItemStringSelect::~ItemStringSelect() +{ + if (!m_pointer_provided) + delete m_selected; +} + void ItemStringSelect::draw(DrawingContext& context, const Vector& pos, int menu_width, bool active) { diff --git a/src/gui/item_stringselect.hpp b/src/gui/item_stringselect.hpp index e4478515f70..46504274507 100644 --- a/src/gui/item_stringselect.hpp +++ b/src/gui/item_stringselect.hpp @@ -25,6 +25,8 @@ class ItemStringSelect final : public MenuItem { public: ItemStringSelect(const std::string& text, std::vector items, int* selected, int id = -1); + ItemStringSelect(const std::string& text, std::vector items, int default_item, int id = -1); + ~ItemStringSelect() override; /** Draws the menu item. */ virtual void draw(DrawingContext&, const Vector& pos, int menu_width, bool active) override; @@ -49,6 +51,8 @@ class ItemStringSelect final : public MenuItem private: std::vector m_items; // list of values for a STRINGSELECT item int* m_selected; // currently selected item + const bool m_pointer_provided; // Indicates whether a pointer has been provided to the item. + std::function m_callback; float m_width; diff --git a/src/gui/menu.cpp b/src/gui/menu.cpp index e51eb8606d4..347b9413b53 100644 --- a/src/gui/menu.cpp +++ b/src/gui/menu.cpp @@ -272,6 +272,15 @@ Menu::add_string_select(int id, const std::string& text, int* selected, const st return *item_ptr; } +ItemStringSelect& +Menu::add_string_select(int id, const std::string& text, int default_item, const std::vector& strings) +{ + auto item = std::make_unique(text, strings, default_item, id); + auto item_ptr = item.get(); + add_item(std::move(item)); + return *item_ptr; +} + ItemFile& Menu::add_file(const std::string& text, std::string* input, const std::vector& extensions, const std::string& basedir, bool path_relative_to_basedir, int id) diff --git a/src/gui/menu.hpp b/src/gui/menu.hpp index 448562b661b..0f55350b43e 100644 --- a/src/gui/menu.hpp +++ b/src/gui/menu.hpp @@ -90,6 +90,7 @@ class Menu ItemGoTo& add_submenu(const std::string& text, int submenu, int id = -1); ItemControlField& add_controlfield(int id, const std::string& text, const std::string& mapping = ""); ItemStringSelect& add_string_select(int id, const std::string& text, int* selected, const std::vector& strings); + ItemStringSelect& add_string_select(int id, const std::string& text, int default_item, const std::vector& strings); ItemTextField& add_textfield(const std::string& text, std::string* input, int id = -1); ItemScript& add_script(const std::string& text, std::string* script, int id = -1); ItemScriptLine& add_script_line(std::string* input, int id = -1); diff --git a/src/object/bumper.cpp b/src/object/bumper.cpp index b7567a890f8..f2c59aea1f6 100644 --- a/src/object/bumper.cpp +++ b/src/object/bumper.cpp @@ -32,8 +32,7 @@ const float BOUNCE_X = 700.0f; Bumper::Bumper(const ReaderMapping& reader) : MovingSprite(reader, "images/objects/trampoline/bumper.sprite", LAYER_OBJECTS, COLGROUP_MOVING), m_physic(), - m_dir(Direction::RIGHT), - m_dir_in_allowed(0) + m_dir(Direction::RIGHT) { std::string dir_str; bool old_facing_left = false; @@ -44,17 +43,6 @@ Bumper::Bumper(const ReaderMapping& reader) : m_dir = Direction::LEFT; set_action("normal", m_dir); m_physic.enable_gravity(false); - - auto allowed_directions = get_allowed_directions(); - - for (int i = 0; i < static_cast(allowed_directions.size()); ++i) - { - if (allowed_directions[i] == m_dir) - { - m_dir_in_allowed = i; - break; - } - } } ObjectSettings @@ -62,7 +50,7 @@ Bumper::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::RIGHT, "direction", 0, get_allowed_directions()); + result.add_direction(_("Direction"), &m_dir, get_allowed_directions(), "direction"); result.reorder({"direction", "sprite", "x", "y"}); return result; } @@ -105,7 +93,7 @@ Bumper::get_physic() std::vector Bumper::get_allowed_directions() const { - return {Direction::LEFT, Direction::RIGHT}; + return { Direction::LEFT, Direction::RIGHT }; } void @@ -113,7 +101,6 @@ Bumper::after_editor_set() { MovingSprite::after_editor_set(); - m_dir = get_allowed_directions()[m_dir_in_allowed]; set_action("normal", m_dir); } diff --git a/src/object/bumper.hpp b/src/object/bumper.hpp index c5da8ffaad0..854d0d63cff 100644 --- a/src/object/bumper.hpp +++ b/src/object/bumper.hpp @@ -44,13 +44,12 @@ class Bumper final : public MovingSprite Physic& get_physic(); private: - virtual std::vector get_allowed_directions() const; + std::vector get_allowed_directions() const; private: Physic m_physic; Direction m_dir; - int m_dir_in_allowed; private: Bumper(const Bumper&) = delete; diff --git a/src/object/conveyor_belt.cpp b/src/object/conveyor_belt.cpp index 6498ba04b05..d832fc500da 100644 --- a/src/object/conveyor_belt.cpp +++ b/src/object/conveyor_belt.cpp @@ -28,7 +28,6 @@ ConveyorBelt::ConveyorBelt(const ReaderMapping &reader) : ExposedObject(this), m_running(true), m_dir(Direction::LEFT), - m_dir_in_allowed(0), m_length(1), m_speed(1.0f), m_frame(0.0f), @@ -51,17 +50,6 @@ ConveyorBelt::ConveyorBelt(const ReaderMapping &reader) : set_action("stopped"); else set_action(m_dir); - - auto allowed_directions = get_allowed_directions(); - - for (int i = 0; i < static_cast(allowed_directions.size()); ++i) - { - if (allowed_directions[i] == m_dir) - { - m_dir_in_allowed = i; - break; - } - } } ObjectSettings @@ -69,7 +57,7 @@ ConveyorBelt::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::LEFT, "direction", 0, get_allowed_directions()); + result.add_direction(_("Direction"), &m_dir, get_allowed_directions(), "direction"); result.add_float(_("Speed"), &m_speed, "speed", 1.0f); result.add_bool(_("Running"), &m_running, "running", true); result.add_int(_("Length"), &m_length, "length", 3); @@ -138,8 +126,6 @@ ConveyorBelt::after_editor_set() { MovingSprite::after_editor_set(); - m_dir = get_allowed_directions()[m_dir_in_allowed]; - if (m_length <= 0) m_length = 1; set_action(dir_to_string(m_dir)); @@ -185,7 +171,7 @@ ConveyorBelt::set_speed(float target_speed) std::vector ConveyorBelt::get_allowed_directions() const { - return {Direction::LEFT, Direction::RIGHT}; + return { Direction::LEFT, Direction::RIGHT }; } /* EOF */ diff --git a/src/object/conveyor_belt.hpp b/src/object/conveyor_belt.hpp index ad240022498..f0a42c4fc94 100644 --- a/src/object/conveyor_belt.hpp +++ b/src/object/conveyor_belt.hpp @@ -65,12 +65,12 @@ class ConveyorBelt final : public MovingSprite, private: void update_hitbox() override; - virtual std::vector get_allowed_directions() const; + + std::vector get_allowed_directions() const; private: bool m_running; Direction m_dir; - int m_dir_in_allowed; int m_length; float m_speed; diff --git a/src/object/ispy.cpp b/src/object/ispy.cpp index 4b91ea69d3d..62d755cedb5 100644 --- a/src/object/ispy.cpp +++ b/src/object/ispy.cpp @@ -29,8 +29,7 @@ Ispy::Ispy(const ReaderMapping& reader) : MovingSprite(reader, "images/objects/ispy/ispy.sprite", LAYER_TILES + 5, COLGROUP_DISABLED), m_state(ISPYSTATE_IDLE), m_script(), - m_dir(Direction::LEFT), - m_dir_in_allowed(0) + m_dir(Direction::LEFT) { reader.get("script", m_script); @@ -43,17 +42,6 @@ Ispy::Ispy(const ReaderMapping& reader) : if (m_dir == Direction::AUTO) log_warning << "Setting an Ispy's direction to AUTO is no good idea." << std::endl; - auto allowed_directions = get_allowed_directions(); - - for (int i = 0; i < static_cast(allowed_directions.size()); ++i) - { - if (allowed_directions[i] == m_dir) - { - m_dir_in_allowed = i; - break; - } - } - set_action("idle", m_dir); } @@ -63,7 +51,7 @@ Ispy::get_settings() ObjectSettings result = MovingSprite::get_settings(); result.add_script(_("Script"), &m_script, "script"); - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::LEFT, "direction", 0, get_allowed_directions()); + result.add_direction(_("Direction"), &m_dir, get_allowed_directions(), "direction"); result.reorder({"script", "facing-down", "direction", "x", "y"}); @@ -74,7 +62,6 @@ void Ispy::after_editor_set() { MovingSprite::after_editor_set(); - m_dir = get_allowed_directions()[m_dir_in_allowed]; set_action("idle", m_dir); } @@ -139,7 +126,7 @@ Ispy::update(float dt_sec) std::vector Ispy::get_allowed_directions() const { - return {Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; + return { Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN }; } void diff --git a/src/object/ispy.hpp b/src/object/ispy.hpp index 2474c3f9f8f..8d635fb0516 100644 --- a/src/object/ispy.hpp +++ b/src/object/ispy.hpp @@ -41,7 +41,7 @@ class Ispy final : public MovingSprite virtual void on_flip(float height) override; private: - virtual std::vector get_allowed_directions() const; + std::vector get_allowed_directions() const; private: enum IspyState { @@ -56,7 +56,6 @@ class Ispy final : public MovingSprite std::string m_script; /**< script to execute when Tux is spotted */ Direction m_dir; - int m_dir_in_allowed; private: Ispy(const Ispy&) = delete; diff --git a/src/object/pushbutton.cpp b/src/object/pushbutton.cpp index d691a8dbfa6..52bd1659537 100644 --- a/src/object/pushbutton.cpp +++ b/src/object/pushbutton.cpp @@ -20,7 +20,6 @@ #include "object/player.hpp" #include "object/rock.hpp" #include "sprite/sprite.hpp" -#include "supertux/direction.hpp" #include "supertux/flip_level_transformer.hpp" #include "supertux/sector.hpp" #include "util/reader_mapping.hpp" @@ -34,8 +33,7 @@ PushButton::PushButton(const ReaderMapping& mapping) : MovingSprite(mapping, "images/objects/pushbutton/pushbutton.sprite", LAYER_BACKGROUNDTILES+1, COLGROUP_MOVING), m_script(), m_state(OFF), - m_dir(Direction::UP), - m_dir_in_allowed(0) + m_dir(Direction::UP) { SoundManager::current()->preload(BUTTON_SOUND); @@ -52,17 +50,6 @@ PushButton::PushButton(const ReaderMapping& mapping) : else if (mapping.get("upside-down", old_upside_down) && old_upside_down) m_dir = Direction::DOWN; - auto allowed_directions = get_allowed_directions(); - - for (int i = 0; i < static_cast(allowed_directions.size()); ++i) - { - if (allowed_directions[i] == m_dir) - { - m_dir_in_allowed = i; - break; - } - } - set_action("off", m_dir, -1); } @@ -71,7 +58,7 @@ PushButton::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::UP, "direction", 0, get_allowed_directions()); + result.add_direction(_("Direction"), &m_dir, get_allowed_directions(), "direction"); result.add_script(_("Script"), &m_script, "script"); result.reorder({"direction", "script", "x", "y"}); @@ -83,7 +70,6 @@ void PushButton::after_editor_set() { MovingSprite::after_editor_set(); - m_dir = get_allowed_directions()[m_dir_in_allowed]; set_action("off", m_dir); } @@ -147,7 +133,7 @@ PushButton::collision(GameObject& other, const CollisionHit& hit) std::vector PushButton::get_allowed_directions() const { - return {Direction::UP, Direction::DOWN}; + return { Direction::UP, Direction::DOWN }; } void diff --git a/src/object/pushbutton.hpp b/src/object/pushbutton.hpp index b0800dac96a..6ef027efacb 100644 --- a/src/object/pushbutton.hpp +++ b/src/object/pushbutton.hpp @@ -19,8 +19,6 @@ #include "object/moving_sprite.hpp" -enum class Direction; - /** PushButton - jump on it to run a script */ class PushButton final : public MovingSprite { @@ -40,7 +38,7 @@ class PushButton final : public MovingSprite virtual void on_flip(float height) override; private: - virtual std::vector get_allowed_directions() const; + std::vector get_allowed_directions() const; private: enum PushButtonState { @@ -52,7 +50,6 @@ class PushButton final : public MovingSprite PushButtonState m_state; Direction m_dir; - int m_dir_in_allowed; private: PushButton(const PushButton&) = delete; diff --git a/src/supertux/direction.cpp b/src/supertux/direction.cpp index 08441cedbbf..a8248b449cf 100644 --- a/src/supertux/direction.cpp +++ b/src/supertux/direction.cpp @@ -52,6 +52,31 @@ dir_to_string(const Direction& dir) } } +std::string +dir_to_translated_string(const Direction& dir) +{ + switch (dir) + { + case Direction::NONE: + return _("none"); + case Direction::LEFT: + return _("left"); + case Direction::RIGHT: + return _("right"); + case Direction::UP: + return _("up"); + case Direction::DOWN: + return _("down"); + default: + if (dir != Direction::AUTO) + { + // Display a warning when an invalid direction has been provided. + log_warning << "Unknown direction \"" << dir << "\". Switching to \"auto\"." << std::endl; + } + return _("auto"); + } +} + Direction string_to_dir(const std::string& dir_str) { diff --git a/src/supertux/direction.hpp b/src/supertux/direction.hpp index 6bac98d2e84..3477756156f 100644 --- a/src/supertux/direction.hpp +++ b/src/supertux/direction.hpp @@ -26,6 +26,7 @@ enum class Direction { AUTO, NONE, LEFT, RIGHT, UP, DOWN }; std::ostream& operator<<(std::ostream& o, const Direction& dir); std::string dir_to_string(const Direction& dir); +std::string dir_to_translated_string(const Direction& dir); Direction string_to_dir(const std::string& dir_str); #endif diff --git a/src/trigger/switch.cpp b/src/trigger/switch.cpp index dd164e673e1..60825acb810 100644 --- a/src/trigger/switch.cpp +++ b/src/trigger/switch.cpp @@ -34,8 +34,7 @@ Switch::Switch(const ReaderMapping& reader) : m_off_script(), m_state(OFF), m_bistable(), - m_dir(Direction::NONE), - m_dir_in_allowed(0) + m_dir(Direction::NONE) { std::string dir_str; if (reader.get("direction", dir_str)) @@ -43,17 +42,6 @@ Switch::Switch(const ReaderMapping& reader) : else m_dir = Direction::NONE; - auto allowed_directions = get_allowed_directions(); - - for (int i = 0; i < static_cast(allowed_directions.size()); ++i) - { - if (allowed_directions[i] == m_dir) - { - m_dir_in_allowed = i; - break; - } - } - set_action("off", m_dir); reader.get("script", m_script); @@ -71,7 +59,7 @@ Switch::get_settings() { ObjectSettings result = SpritedTrigger::get_settings(); - result.add_direction(_("Direction"), reinterpret_cast(&m_dir_in_allowed), Direction::NONE, "direction", 0, get_allowed_directions()); + result.add_direction(_("Direction"), &m_dir, get_allowed_directions(), "direction"); result.add_script(_("Turn on script"), &m_script, "script"); result.add_script(_("Turn off script"), &m_off_script, "off-script"); @@ -146,7 +134,7 @@ Switch::event(Player& , EventType type) std::vector Switch::get_allowed_directions() const { - return {Direction::NONE, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN}; + return { Direction::NONE, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN }; } void @@ -154,7 +142,6 @@ Switch::after_editor_set() { SpritedTrigger::after_editor_set(); - m_dir = get_allowed_directions()[m_dir_in_allowed]; set_action("off", m_dir); } diff --git a/src/trigger/switch.hpp b/src/trigger/switch.hpp index 267032d629d..3e92928e416 100644 --- a/src/trigger/switch.hpp +++ b/src/trigger/switch.hpp @@ -18,7 +18,6 @@ #define HEADER_SUPERTUX_TRIGGER_SWITCH_HPP #include "trigger/trigger_base.hpp" -#include "supertux/direction.hpp" class Switch final : public SpritedTrigger { @@ -40,7 +39,7 @@ class Switch final : public SpritedTrigger virtual void on_flip(float height) override; private: - virtual std::vector get_allowed_directions() const; + std::vector get_allowed_directions() const; private: enum SwitchState { @@ -56,7 +55,6 @@ class Switch final : public SpritedTrigger SwitchState m_state; bool m_bistable; Direction m_dir; - int m_dir_in_allowed; private: Switch(const Switch&) = delete; From 8a6a2f30e6a543cb4ad3214e98db7935effd8f3d Mon Sep 17 00:00:00 2001 From: Vankata453 <78196474+Vankata453@users.noreply.github.com> Date: Tue, 8 Aug 2023 02:58:42 +0300 Subject: [PATCH 08/14] Fix shadowing local variable --- src/editor/object_option.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/editor/object_option.cpp b/src/editor/object_option.cpp index c1799423385..ace1213382b 100644 --- a/src/editor/object_option.cpp +++ b/src/editor/object_option.cpp @@ -814,8 +814,8 @@ DirectionOption::add_to_menu(Menu& menu) const } menu.add_string_select(-1, get_text(), selected, labels) - .set_callback([value_ptr = m_value_ptr, possible_directions = m_possible_directions](int selected) { - *value_ptr = possible_directions.at(selected); + .set_callback([value_ptr = m_value_ptr, possible_directions = m_possible_directions](int index) { + *value_ptr = possible_directions.at(index); }); } From 3b8abcef1a4fd158996fa7f13fe04d11d401082f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Tue, 8 Aug 2023 03:03:08 +0200 Subject: [PATCH 09/14] set bumper's default direction to RIGHT --- src/object/bumper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/object/bumper.cpp b/src/object/bumper.cpp index f2c59aea1f6..557608fe20f 100644 --- a/src/object/bumper.cpp +++ b/src/object/bumper.cpp @@ -93,7 +93,7 @@ Bumper::get_physic() std::vector Bumper::get_allowed_directions() const { - return { Direction::LEFT, Direction::RIGHT }; + return { Direction::RIGHT, Direction::LEFT }; } void From 733d0ac73e193db7f0c66dd6dd6935b761a8194c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Tue, 8 Aug 2023 16:34:08 +0200 Subject: [PATCH 10/14] remove the need for "-none" for Direction::NONE sprite actions this ensures compatibility with existing custom switch sprites --- data/images/objects/switch/switch.sprite | 8 ++++---- src/object/moving_sprite.cpp | 11 +++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/data/images/objects/switch/switch.sprite b/data/images/objects/switch/switch.sprite index f215f0956a9..7ab8c9c547f 100644 --- a/data/images/objects/switch/switch.sprite +++ b/data/images/objects/switch/switch.sprite @@ -1,13 +1,13 @@ (supertux-sprite (action - (name "off-none") + (name "off") (hitbox 3 4 25 39) (images "switch-0.png" ) ) (action - (name "turnon-none") + (name "turnon") (fps 18) (hitbox 3 4 25 39) (images @@ -17,14 +17,14 @@ ) ) (action - (name "on-none") + (name "on") (hitbox 3 4 25 39) (images "switch-4.png" ) ) (action - (name "turnoff-none") + (name "turnoff") (fps 18) (hitbox 3 4 25 39) (images diff --git a/src/object/moving_sprite.cpp b/src/object/moving_sprite.cpp index 2f0f7b9c53e..cfff8c1f6fc 100644 --- a/src/object/moving_sprite.cpp +++ b/src/object/moving_sprite.cpp @@ -24,6 +24,7 @@ #include "math/util.hpp" #include "object/sprite_particle.hpp" #include "sprite/sprite_manager.hpp" +#include "supertux/direction.hpp" #include "supertux/sector.hpp" #include "util/reader_mapping.hpp" #include "util/writer.hpp" @@ -137,14 +138,20 @@ MovingSprite::set_action(const std::string& name, int loops) void MovingSprite::set_action(const std::string& name, const Direction& dir, int loops) { - m_sprite->set_action(name, dir, loops); + if (dir == Direction::NONE) + m_sprite->set_action(name, loops); + else + m_sprite->set_action(name, dir, loops); update_hitbox(); } void MovingSprite::set_action(const Direction& dir, const std::string& name, int loops) { - m_sprite->set_action(dir, name, loops); + if (dir == Direction::NONE) + m_sprite->set_action(name, loops); + else + m_sprite->set_action(dir, name, loops); update_hitbox(); } From 7da2bfb6a051cc2c3f577137fe3c18b854dbb4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Tue, 8 Aug 2023 17:41:43 +0200 Subject: [PATCH 11/14] make sure the default direction of each badguy can be set to a value different from Direction::AUTO if needed --- src/badguy/badguy.cpp | 27 +++++++++++++++++---------- src/badguy/badguy.hpp | 3 +++ src/badguy/darttrap.cpp | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/badguy/badguy.cpp b/src/badguy/badguy.cpp index 2b6ba4bc81c..2a6a6bccd45 100644 --- a/src/badguy/badguy.cpp +++ b/src/badguy/badguy.cpp @@ -41,15 +41,15 @@ static const float BURN_TIME = 1; static const float X_OFFSCREEN_DISTANCE = 1280; static const float Y_OFFSCREEN_DISTANCE = 800; -BadGuy::BadGuy(const Vector& pos, const std::string& sprite_name_, int layer_, +BadGuy::BadGuy(const Vector& pos, const std::string& sprite_name, int layer, const std::string& light_sprite_name, const std::string& ice_sprite_name) : - BadGuy(pos, Direction::LEFT, sprite_name_, layer_, light_sprite_name) + BadGuy(pos, Direction::LEFT, sprite_name, layer, light_sprite_name) { } -BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite_name_, int layer_, +BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite_name, int layer, const std::string& light_sprite_name, const std::string& ice_sprite_name) : - MovingSprite(pos, sprite_name_, layer_, COLGROUP_DISABLED), + MovingSprite(pos, sprite_name, layer, COLGROUP_DISABLED), ExposedObject(this), m_physic(), m_countMe(true), @@ -83,16 +83,23 @@ BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite m_lightsprite->set_blend(Blend::ADD); } -BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name_, int layer_, +BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name, int layer, const std::string& light_sprite_name, const std::string& ice_sprite_name) : - MovingSprite(reader, sprite_name_, layer_, COLGROUP_DISABLED), + BadGuy(reader, sprite_name, Direction::AUTO, layer, light_sprite_name, ice_sprite_name) +{ +} + +BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name, + Direction default_direction, int layer, + const std::string& light_sprite_name, const std::string& ice_sprite_name) : + MovingSprite(reader, sprite_name, layer, COLGROUP_DISABLED), ExposedObject(this), m_physic(), m_countMe(true), m_is_initialized(false), m_start_position(m_col.m_bbox.p1()), m_dir(Direction::LEFT), - m_start_dir(Direction::AUTO), + m_start_dir(default_direction), m_frozen(false), m_ignited(false), m_in_water(false), @@ -109,9 +116,9 @@ BadGuy::BadGuy(const ReaderMapping& reader, const std::string& sprite_name_, int m_floor_normal(0.0f, 0.0f), m_colgroup_active(COLGROUP_MOVING) { - std::string dir_str = "auto"; - reader.get("direction", dir_str); - m_start_dir = string_to_dir(dir_str); + std::string dir_str; + if (reader.get("direction", dir_str)) + m_start_dir = string_to_dir(dir_str); m_dir = m_start_dir; reader.get("dead-script", m_dead_script); diff --git a/src/badguy/badguy.hpp b/src/badguy/badguy.hpp index acbed144992..eb32cf3a954 100644 --- a/src/badguy/badguy.hpp +++ b/src/badguy/badguy.hpp @@ -44,6 +44,9 @@ class BadGuy : public MovingSprite, BadGuy(const ReaderMapping& reader, const std::string& sprite_name, int layer = LAYER_OBJECTS, const std::string& light_sprite_name = "images/objects/lightmap_light/lightmap_light-medium.sprite", const std::string& ice_sprite_name = "images/creatures/overlays/iceoverlay/iceoverlay.sprite"); + BadGuy(const ReaderMapping& reader, const std::string& sprite_name, Direction default_direction, int layer = LAYER_OBJECTS, + const std::string& light_sprite_name = "images/objects/lightmap_light/lightmap_light-medium.sprite", + const std::string& ice_sprite_name = "images/creatures/overlays/iceoverlay/iceoverlay.sprite"); /** Called when the badguy is drawn. The default implementation simply draws the badguy sprite on screen */ diff --git a/src/badguy/darttrap.cpp b/src/badguy/darttrap.cpp index 47f2819f470..4dfcbd557bb 100644 --- a/src/badguy/darttrap.cpp +++ b/src/badguy/darttrap.cpp @@ -27,7 +27,7 @@ #include "util/reader_mapping.hpp" DartTrap::DartTrap(const ReaderMapping& reader) : - BadGuy(reader, "images/creatures/darttrap/darttrap.sprite", LAYER_TILES-1), + BadGuy(reader, "images/creatures/darttrap/darttrap.sprite", get_allowed_directions()[0], LAYER_TILES-1), m_enabled(true), m_initial_delay(), m_fire_delay(), From 77a805e5a3cbebacbf58dc647564ff6e353948e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Wed, 9 Aug 2023 18:04:28 +0200 Subject: [PATCH 12/14] move direction::none sprite action check to sprite.cpp --- src/object/moving_sprite.cpp | 10 ++-------- src/sprite/sprite.cpp | 11 +++++++++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/object/moving_sprite.cpp b/src/object/moving_sprite.cpp index cfff8c1f6fc..b1e28a5a484 100644 --- a/src/object/moving_sprite.cpp +++ b/src/object/moving_sprite.cpp @@ -138,20 +138,14 @@ MovingSprite::set_action(const std::string& name, int loops) void MovingSprite::set_action(const std::string& name, const Direction& dir, int loops) { - if (dir == Direction::NONE) - m_sprite->set_action(name, loops); - else - m_sprite->set_action(name, dir, loops); + m_sprite->set_action(name, dir, loops); update_hitbox(); } void MovingSprite::set_action(const Direction& dir, const std::string& name, int loops) { - if (dir == Direction::NONE) - m_sprite->set_action(name, loops); - else - m_sprite->set_action(dir, name, loops); + m_sprite->set_action(dir, name, loops); update_hitbox(); } diff --git a/src/sprite/sprite.cpp b/src/sprite/sprite.cpp index 74b697bb39c..718796ad3eb 100644 --- a/src/sprite/sprite.cpp +++ b/src/sprite/sprite.cpp @@ -18,6 +18,7 @@ #include +#include "supertux/direction.hpp" #include "supertux/globals.hpp" #include "util/log.hpp" #include "video/surface.hpp" @@ -66,13 +67,19 @@ Sprite::clone() const void Sprite::set_action(const std::string& name, const Direction& dir, int loops) { - set_action(name + "-" + dir_to_string(dir), loops); + if (dir == Direction::NONE) + set_action(name, loops); + else + set_action(name + "-" + dir_to_string(dir), loops); } void Sprite::set_action(const Direction& dir, const std::string& name, int loops) { - set_action(dir_to_string(dir) + "-" + name, loops); + if (dir == Direction::NONE) + set_action(name, loops); + else + set_action(dir_to_string(dir) + "-" + name, loops); } void From 11cd777edba163863f69b59ccd88400517ce6276 Mon Sep 17 00:00:00 2001 From: Vankata453 <78196474+Vankata453@users.noreply.github.com> Date: Fri, 11 Aug 2023 21:58:52 +0300 Subject: [PATCH 13/14] Remove `get_allowed_directions()` from non-badguy objects --- src/object/bumper.cpp | 8 +------- src/object/bumper.hpp | 3 --- src/object/conveyor_belt.cpp | 8 +------- src/object/conveyor_belt.hpp | 2 -- src/object/ispy.cpp | 9 ++------- src/object/ispy.hpp | 3 --- src/object/pushbutton.cpp | 8 +------- src/object/pushbutton.hpp | 3 --- src/trigger/switch.cpp | 9 ++------- src/trigger/switch.hpp | 3 --- 10 files changed, 7 insertions(+), 49 deletions(-) diff --git a/src/object/bumper.cpp b/src/object/bumper.cpp index 557608fe20f..2d32e5976c7 100644 --- a/src/object/bumper.cpp +++ b/src/object/bumper.cpp @@ -50,7 +50,7 @@ Bumper::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_direction(_("Direction"), &m_dir, get_allowed_directions(), "direction"); + result.add_direction(_("Direction"), &m_dir, { Direction::RIGHT, Direction::LEFT }, "direction"); result.reorder({"direction", "sprite", "x", "y"}); return result; } @@ -90,12 +90,6 @@ Bumper::get_physic() return m_physic; } -std::vector -Bumper::get_allowed_directions() const -{ - return { Direction::RIGHT, Direction::LEFT }; -} - void Bumper::after_editor_set() { diff --git a/src/object/bumper.hpp b/src/object/bumper.hpp index 854d0d63cff..0e1b9e397e5 100644 --- a/src/object/bumper.hpp +++ b/src/object/bumper.hpp @@ -43,9 +43,6 @@ class Bumper final : public MovingSprite Physic& get_physic(); -private: - std::vector get_allowed_directions() const; - private: Physic m_physic; diff --git a/src/object/conveyor_belt.cpp b/src/object/conveyor_belt.cpp index d832fc500da..be75a6d68a9 100644 --- a/src/object/conveyor_belt.cpp +++ b/src/object/conveyor_belt.cpp @@ -57,7 +57,7 @@ ConveyorBelt::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_direction(_("Direction"), &m_dir, get_allowed_directions(), "direction"); + result.add_direction(_("Direction"), &m_dir, { Direction::LEFT, Direction::RIGHT }, "direction"); result.add_float(_("Speed"), &m_speed, "speed", 1.0f); result.add_bool(_("Running"), &m_running, "running", true); result.add_int(_("Length"), &m_length, "length", 3); @@ -168,10 +168,4 @@ ConveyorBelt::set_speed(float target_speed) m_speed = target_speed; } -std::vector -ConveyorBelt::get_allowed_directions() const -{ - return { Direction::LEFT, Direction::RIGHT }; -} - /* EOF */ diff --git a/src/object/conveyor_belt.hpp b/src/object/conveyor_belt.hpp index f0a42c4fc94..c3228811b1e 100644 --- a/src/object/conveyor_belt.hpp +++ b/src/object/conveyor_belt.hpp @@ -66,8 +66,6 @@ class ConveyorBelt final : public MovingSprite, private: void update_hitbox() override; - std::vector get_allowed_directions() const; - private: bool m_running; Direction m_dir; diff --git a/src/object/ispy.cpp b/src/object/ispy.cpp index 62d755cedb5..bdded6dd041 100644 --- a/src/object/ispy.cpp +++ b/src/object/ispy.cpp @@ -51,7 +51,8 @@ Ispy::get_settings() ObjectSettings result = MovingSprite::get_settings(); result.add_script(_("Script"), &m_script, "script"); - result.add_direction(_("Direction"), &m_dir, get_allowed_directions(), "direction"); + result.add_direction(_("Direction"), &m_dir, + { Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN }, "direction"); result.reorder({"script", "facing-down", "direction", "x", "y"}); @@ -123,12 +124,6 @@ Ispy::update(float dt_sec) } } -std::vector -Ispy::get_allowed_directions() const -{ - return { Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN }; -} - void Ispy::on_flip(float height) { diff --git a/src/object/ispy.hpp b/src/object/ispy.hpp index 8d635fb0516..efe771d9936 100644 --- a/src/object/ispy.hpp +++ b/src/object/ispy.hpp @@ -40,9 +40,6 @@ class Ispy final : public MovingSprite virtual void on_flip(float height) override; -private: - std::vector get_allowed_directions() const; - private: enum IspyState { ISPYSTATE_IDLE, diff --git a/src/object/pushbutton.cpp b/src/object/pushbutton.cpp index 52bd1659537..d575a43bf45 100644 --- a/src/object/pushbutton.cpp +++ b/src/object/pushbutton.cpp @@ -58,7 +58,7 @@ PushButton::get_settings() { ObjectSettings result = MovingSprite::get_settings(); - result.add_direction(_("Direction"), &m_dir, get_allowed_directions(), "direction"); + result.add_direction(_("Direction"), &m_dir, { Direction::UP, Direction::DOWN }, "direction"); result.add_script(_("Script"), &m_script, "script"); result.reorder({"direction", "script", "x", "y"}); @@ -130,12 +130,6 @@ PushButton::collision(GameObject& other, const CollisionHit& hit) return FORCE_MOVE; } -std::vector -PushButton::get_allowed_directions() const -{ - return { Direction::UP, Direction::DOWN }; -} - void PushButton::on_flip(float height) { diff --git a/src/object/pushbutton.hpp b/src/object/pushbutton.hpp index 6ef027efacb..2db785e5925 100644 --- a/src/object/pushbutton.hpp +++ b/src/object/pushbutton.hpp @@ -37,9 +37,6 @@ class PushButton final : public MovingSprite virtual void on_flip(float height) override; -private: - std::vector get_allowed_directions() const; - private: enum PushButtonState { OFF, diff --git a/src/trigger/switch.cpp b/src/trigger/switch.cpp index 60825acb810..53250d1ee19 100644 --- a/src/trigger/switch.cpp +++ b/src/trigger/switch.cpp @@ -59,7 +59,8 @@ Switch::get_settings() { ObjectSettings result = SpritedTrigger::get_settings(); - result.add_direction(_("Direction"), &m_dir, get_allowed_directions(), "direction"); + result.add_direction(_("Direction"), &m_dir, + { Direction::NONE, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN }, "direction"); result.add_script(_("Turn on script"), &m_script, "script"); result.add_script(_("Turn off script"), &m_off_script, "off-script"); @@ -131,12 +132,6 @@ Switch::event(Player& , EventType type) } } -std::vector -Switch::get_allowed_directions() const -{ - return { Direction::NONE, Direction::LEFT, Direction::RIGHT, Direction::UP, Direction::DOWN }; -} - void Switch::after_editor_set() { diff --git a/src/trigger/switch.hpp b/src/trigger/switch.hpp index 3e92928e416..7ad70c5bf1b 100644 --- a/src/trigger/switch.hpp +++ b/src/trigger/switch.hpp @@ -38,9 +38,6 @@ class Switch final : public SpritedTrigger virtual void after_editor_set() override; virtual void on_flip(float height) override; -private: - std::vector get_allowed_directions() const; - private: enum SwitchState { OFF, From 0107fe66b4d319e0271149e9192869ae6f49693a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pale=C4=8Dek?= Date: Sat, 12 Aug 2023 12:50:19 +0200 Subject: [PATCH 14/14] remove unneeded include --- src/object/moving_sprite.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/object/moving_sprite.cpp b/src/object/moving_sprite.cpp index b1e28a5a484..2f0f7b9c53e 100644 --- a/src/object/moving_sprite.cpp +++ b/src/object/moving_sprite.cpp @@ -24,7 +24,6 @@ #include "math/util.hpp" #include "object/sprite_particle.hpp" #include "sprite/sprite_manager.hpp" -#include "supertux/direction.hpp" #include "supertux/sector.hpp" #include "util/reader_mapping.hpp" #include "util/writer.hpp"