From 833cc9082c3948aab4c24951cbe77b3c96d0a8dd Mon Sep 17 00:00:00 2001 From: Arceveti <73617174+Arceveti@users.noreply.github.com> Date: Sun, 26 Sep 2021 14:49:21 -0700 Subject: [PATCH] Add breath meter --- .gitignore | 1 + Makefile.split | 2 +- .../breath_meter_five_segments.rgba16.png | Bin 0 -> 8820 bytes .../breath_meter_four_segments.rgba16.png | Bin 0 -> 8907 bytes .../breath_meter/breath_meter_full.rgba16.png | Bin 0 -> 9006 bytes .../breath_meter_left_side.rgba16.png | Bin 0 -> 10411 bytes .../breath_meter_one_segment.rgba16.png | Bin 0 -> 8395 bytes .../breath_meter_right_side.rgba16.png | Bin 0 -> 9532 bytes .../breath_meter_seven_segments.rgba16.png | Bin 0 -> 9097 bytes .../breath_meter_six_segments.rgba16.png | Bin 0 -> 8890 bytes .../breath_meter_three_segments.rgba16.png | Bin 0 -> 8640 bytes .../breath_meter_two_segments.rgba16.png | Bin 0 -> 8455 bytes actors/breath_meter/model.inc.c | 130 +++++++ actors/common1.c | 3 + actors/common1.h | 8 + include/config.h | 2 + include/types.h | 4 + src/engine/math_util.c | 43 ++- src/engine/math_util.h | 11 + src/game/behaviors/recovery_heart.inc.c | 5 +- src/game/camera.c | 49 +-- src/game/camera.h | 2 +- src/game/hud.c | 364 ++++++++++-------- src/game/hud.h | 9 + src/game/interaction.c | 28 +- src/game/level_update.c | 15 +- src/game/level_update.h | 6 + src/game/mario.c | 50 ++- src/game/mario_actions_cutscene.c | 27 +- src/game/obj_behaviors_2.c | 2 +- src/game/object_helpers.c | 128 +----- src/game/object_helpers.h | 3 - 32 files changed, 551 insertions(+), 341 deletions(-) create mode 100644 actors/breath_meter/breath_meter_five_segments.rgba16.png create mode 100644 actors/breath_meter/breath_meter_four_segments.rgba16.png create mode 100644 actors/breath_meter/breath_meter_full.rgba16.png create mode 100644 actors/breath_meter/breath_meter_left_side.rgba16.png create mode 100644 actors/breath_meter/breath_meter_one_segment.rgba16.png create mode 100644 actors/breath_meter/breath_meter_right_side.rgba16.png create mode 100644 actors/breath_meter/breath_meter_seven_segments.rgba16.png create mode 100644 actors/breath_meter/breath_meter_six_segments.rgba16.png create mode 100644 actors/breath_meter/breath_meter_three_segments.rgba16.png create mode 100644 actors/breath_meter/breath_meter_two_segments.rgba16.png create mode 100644 actors/breath_meter/model.inc.c diff --git a/.gitignore b/.gitignore index 1649f4d3..fd02a3e9 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ build/* !/assets/**/*custom*.bin !/assets/**/*custom*/**/*.bin !/textures/crash_custom/*.png +!/actors/breath_meter/breath_meter_*.png # libultra !/lib/*.a diff --git a/Makefile.split b/Makefile.split index 02925fee..d6b6cb9d 100644 --- a/Makefile.split +++ b/Makefile.split @@ -66,7 +66,7 @@ KING_BOBOMB_DIRS := king_bobomb water_bubble BOO_DIRS := bookend book chair small_key mad_piano boo haunted_cage MR_I_DIRS := mr_i_eyeball mr_i_iris swoop snufit dorrie scuttlebug HOOT_DIRS := yellow_sphere_small hoot yoshi_egg thwomp bullet_bill heave_ho -COINS_DIRS := mist explosion butterfly coin warp_pipe door bowser_key flame blue_fish pebble leaves warp_collision mario_cap power_meter mushroom_1up star sand dirt transparent_star white_particle wooden_signpost tree +COINS_DIRS := mist explosion butterfly coin warp_pipe door bowser_key flame blue_fish pebble leaves warp_collision mario_cap breath_meter power_meter mushroom_1up star sand dirt transparent_star white_particle wooden_signpost tree BUBBA_DIRS := bubba wiggler wiggler_body_part lakitu_enemy spiny_egg spiny SKEETER_DIRS := skeeter seaweed water_mine cyan_fish bub water_ring treasure_chest KLEPTO_DIRS := klepto eyerok pokey tornado diff --git a/actors/breath_meter/breath_meter_five_segments.rgba16.png b/actors/breath_meter/breath_meter_five_segments.rgba16.png new file mode 100644 index 0000000000000000000000000000000000000000..7c99cf9e2fc5fc1737f32910a9ec7c14ae4e96d2 GIT binary patch literal 8820 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 Q7^w8|boFyt=akR{05r_k=>Px# literal 0 HcmV?d00001 diff --git a/actors/breath_meter/breath_meter_four_segments.rgba16.png b/actors/breath_meter/breath_meter_four_segments.rgba16.png new file mode 100644 index 0000000000000000000000000000000000000000..03ce6917e537c279961b3672fa2070e2bf73fb25 GIT binary patch literal 8907 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 B8zBGy literal 0 HcmV?d00001 diff --git a/actors/breath_meter/breath_meter_full.rgba16.png b/actors/breath_meter/breath_meter_full.rgba16.png new file mode 100644 index 0000000000000000000000000000000000000000..5a0c4292326cd40d5b7ad28b2ca93f6061320289 GIT binary patch literal 9006 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 BI^qBT literal 0 HcmV?d00001 diff --git a/actors/breath_meter/breath_meter_left_side.rgba16.png b/actors/breath_meter/breath_meter_left_side.rgba16.png new file mode 100644 index 0000000000000000000000000000000000000000..446d6805cd8736f9ede5d3435df3ee2b4074084f GIT binary patch literal 10411 zcmeAS@N?(olHy`uVBq!ia0y~yU{GLSU~u4IV_;yY?kio#z#w8!6%tVrlvu7%P?VpR znUkteQdy9ykXn(M#=uZjfFgYq~;patm@BR1p|MUF*@n6e% z_dmb4e~O68HH3JZJiQ_uAxW-TirqZ@yR7zFo4f-j9FfzkP4)|Lhjnn|epRZNuVz z{(i+}<&1&#_l}~LM{Y#Gg+4sKU z@%Myh@1NV(-oM@d-Cn%J{K)=0mgnAE|NmCkZTIKSx$E~WSMMtRwtM$HBgM1xeQR@{ z?t65=@x)_W-OtfKjN_*NzWrk@-xt52ee-_u)Gg1qn=tM1tiRej2hM@r3#ewU;x&1=>lz5jb#_3oG2=5h0so1Z7mGj6(k zxc>2Xef^R?h4+7h?^}dEJWPNXqx0f@Z`gjk{{FoGlhnxtcMZbLZp{A} zmo(#lg11GN`NfIMz0bE_tBIV?Eui!+SLc|!(T5cIv+4h*2y}fvvT@SHf77oj((`&cfih4bFcGn#>wS#s&2i!ANl#+w7LHezrTNe*8fvcJ7!*Ld}^INC+gI7)jcncOffbRo235O zRg&R?qh<9fe@%;}ewNjd6>F>7h**WE3d?k1M%wn&* z^~%M5D}Q&N`D>V8KWVG#)|vh#=FVEqtY15)-0$4V{!hBN;N|mu%ToRy-uzpdy)TsC z{D#Q1dEv8s&!@PTh~8My`#5X+=36&bGW3`|voREl>z}%K{pL9jzZ;%svzGQ-K1J}` z$?SC}RCjvaFcS<6xBM6(WplQD#=I+GUDg5rnorLSp8a;GRCmVGbH4k|pG+-%=h->^ z>}<=PXU8sQy*(Udv(xtWDLK`?huqJV7b&mZax*sa^&6S*H>Ng4EKdtL9Ibrgu};PW zA;pF1OJ;1H&r!UYXVd2IS;g97mF65P7HyLe{PpZXQB_jx8l&rbBeM1K)TjCM?R?tN zdwZP~Z|V!<`!5Q{ZwglvQ zW@op@6#m{L`pfpQ(zT-tTe{-w&T-uOW%qi*>fB!LYoR-kVIl zo+;;bHAVKUhUu4D_MGUt{)|~$-^HgPxd)l|}v~i2x zaI8C4=%USTn=97!v+oElyk=oIZ>dG`(nHfu{0hFY>H1mcO>cYt+T3{^uzS}HnaJ&0 zRvRm`uI_s86~!i8INR*q^@~{~?9*z2jAjvT z*1I>PmU5YgTTeCvO+ASMkjxBJOe-Li~J6i(b= zInH~l{qZ@YDP{{UGx23N)H|h^A6H%&0l53uKpKkPy7CY2EVc7%WiF(JZWF3O! zzSmq<`?1r>@tsX*GoQf4*)*~xyRyhHTfkApcrA2K%9N!i_E)`s7CAvFC3AU_hs)VQjVTWEcojE4IGZ07 zzTzP7XNMv=*~EjZKYf=mlWLniy|tr3z&$peYhAOLfCdl$I|DXtq3lAAi+O=}_N`c< z$m+ZC9oGz*Tkd~_%~O_`pPYWhOGJYI3ZKxU05#1$9wK|DxxRct^$DM#Ma zj^~XQU#EWbz2a@1v!KgOK2}HSVC%mH4ZAdLD~!}n2_DVW5%q6f%{uo~Vou6p*;c-= zY}MwY%amqS?@uwjAUah(#kX?@=yFBns5=&fUWLzTqoGT9PYc5qR zwc9q`{7Bbat`5humpzOV)^3|~w3T)7r`rAhx{3w&R-P41<9RsAN$cqJDSwZO?vTHv zHLZiwN%MSxLE)Kn{@J@HZ``!`z=PvF1`+=iI365`zWPMk?NqRYl(_Se%BEXJcTT@K z-L=Y8?g7jH36WX7cU31kIs4rxb^p(`_kESrnKGtDeXpf<2P@y6bJf%LqDywDAREIO zN4wk)zjXUrWvALkI3L}!OpCSpvPUiB?J0fyw;K~@@I;B|PkJWkH0^(O6LXmTY|}|i zFU1%aH2n}|=upphTq?wF)9zWsp1tpw^ab0urGXQz?mu<=tjB!#jFFdrhqC3K*D6Wr z>J}{*g3a?(Pw#W$VUTr8)P3_;G(WD{w8gV4|I5A2saKYrXIi?U_D6!J{L!69J;U@L zPhh{A)n@_@=xB~Ub?N?yX#yc ztH-LX=}rPAi7`K<%zqg?x~6+YWaT}N>#bLJ6wZxSb9j6xHlXEqRIB-`90(L)D?jO__4|bspOd5Fy&zA4dm@VW?D_sT_>^zW-_gAAhH@fD{3oV$zWx6q znwI<4PF=F8V@5`S!~R8wr+r-Uj`zli0%`p~;WGxlYTLZ-9(?WdV1uHgqGFSnnfUs7 zF6Sh^Xh_E@xp4=jN`KmAearLo=D1f`P5GQQ;*-i%dM?Pb%HH3{a%jrVfJnECjlr{~ z2mSk&aI`~!3zH)AFrhYxG`1p|QZM#jq7u*Cl*|teY zwnRh-{W#WeQgC~4iErSR)}0zrn~F}A+Nduvl9j*U^M-x%$yuIqOv)?=ZzjBw;`|X~ zb?|F#lvPp90gf+6-#lL;Iafqxt%OTiqkCT8of!WhvaX0*CVy|~$L0F%=7ko94;EY2#I-moo?HC& z9{;C#Q-2C^EsJku?cT(GXp3ABoBSc!WhQPrdA_ApEN1Y1Ag!1lu9*EIpDTBLPs3JU z6+0uL1Y@fM1%}OEE@@omlz1lD=d#G!Pmw8oy7j7_wI8_uxxCxV@}b@S&r!Kani^9s z2>)NWukYyfKKGJ0mrG(byo+2T_3Yfw7jk4RU{qetxgvCr)tXB)HXK={#+9~IbAy4N zg7C4|nQK;-Ni_UgS&_So+iHdCYM1X*uG$pneq*;yTG4N1|D*H7B{7Y-3my|9=WqFF zVw+QlicMv^V??6BgLi zlb6M}`05GP2UUlbRGoV2uVM7fY4Hb_sG#e9scHuj=#p=e)_b9L4ZPG5$ML%Y%v#lII!cQiFG z4q5q2+Vnxl#+aEcFZJT~v2J?Urz4nj>tk(v^2#Hei^>-UyXNuPv+y`r|9QcqCexp+7my_emr0Mn(c9%`?_J2t4G!1>Ho5oVr+ zj6w^)*>wEuG(L6nl>X(76SNk}NFF%KEj2?*T41)D=f^#jUZ;#?ldHcz>Z%P~ z&2;#t^>0q6{C&a-nT`39lesFoUW>3Sxu$osoU?LLpo@C}1G9F-(|{AtLxgM2##L4u zMt+yz^jNxn`SQP6Ki+Kf-|%?<#j1m={@wb1{UWdLG8g{se>L87sOb9g+^OO}zL0;u z>n3;66yECR{CeI|eN2^!b<%xGj#f*!a?)47QFD1HVkxxbk9vMdx1ucXyU(hJrI>H$ zFYVfLb+;}v>z07fqnx)oIo~v@&&#{G_?_2%k&TX$(r$B|9>tW#@7&gsj?(}7I%mCaV6>d9&kwJ(2rug5GOo2@$r*~hyJY) zT(D{7zh8xyMdszaDRy2y`N5JEk^O&ezD_oMESoP8a3^lwqz&9pG=9G@5OZE4Xe_IB zXhV(n_A?)&PVn}gy(sYF7{}COE1FlBnXK&2dKi3umW^0B`;rgrryTz8T4j^HKYrVW zX#%?G0w*|U?yHTt|N8Ar-(MV;i{4%;e6)I3U%u#qBS%l0*y&5|caagYxn*Svh`E9!O`U_LXT%VthwV>7W6q}%X~Ti;+yB>GAra`{(swio$2Gw z6&KAHWv}!rb-eK~rjSea;494w=a#XV8Krd3u4#`le(*c;+P7Kj?|#fL+m}KX=q^vay6Zh^n554*0 zw&H62<>EV)Jv9B>OuLUse)J6g7Mb1kiBWe(<*M4VYv0EfXH1=P$5FuchT2Q%Xkm`} zg`a1M1M*@V4~->ZFO*8*I z%#C?+ue`@m&6&M-meg`ouavkq}IE*1IxJbV4GHBag`YP>ggx~*Pv=32oL z@4X5(9n4z`{?5+&v@LiRx9?e#m6I>r@$vl3$>6SheC5-7Hp$AZGNwNa3Pq}g9SS-h zG`2nxoSL!z?`MPSW#vXec9R8XSe#R_F3?_Zzl}Mu?xXVllI7gmDwiaqWj?6=+j4!& zi{I_{=I$)oC-$|;28mDVmDo_0^?Rqaoi?7L7P=QrE2uEq5cTUd>GH@WaPOmDZfnRzQ&?RNjK z@7rzXe<}YJ_WG~eh5e6Jb}F4cJcltc>HYgV#txYlqD4;SmQjc1WPWM;>Hp=}_y3b_ zEM%Q!6p{UOdc{%gtGmnlKKz(}p6zi3i`V9$O5H^j?>?;AcmGn%&W}4o-_E;h75H%# zf5o+3n(P6F%LJTi9m{SgM7&**xs8vvIJEZuNogsGm%BcC?O8nYZBS0sgt99uG=BZk zD_VJWX~1`(EhX>-3 z8X=2SuFei?xN;%vj^!*5hAS3}>T)f4_Z>fawCL~k9ew|j_xl|xWYdw}#qzRcNuh4; z+k@vHt&LZH{bH}_wn@$V>)4WmH>7Uquvk;Ee~I+_l!Dkl$%p$?s|vjxFALAv%U^nF z{mCOgW7a)vYCCwf!Pm<-zv${!fzQADTN;0_P8a{U@Q*{u1O=OmFK<0C_}X+&_||O; z=fdW1e$H1vFaA=_t#SHgYA#COkrSPY|C_Z4)An#hK(IE zFjUN`ooMTEI6$P;Ke*dfi%(3X@I(OX!KM`*i$gYYhim+#Ht%bxe{D#4;Jl}Izo&oIs+`PdH?gAcWt;0BN1M%= zFXw#kJiJCl^T_ifkJ(};{FwN9)9L&<-F)0O#yKfLpN}LaZM<=?h<)>c-mj#PnPRIHZt82`Ti~3Uk?B!Y zlp0*+7m{3+ootz+WN*i1Q(;w+TacStlBiITo0C^;Rbi_HHrFbz*a{@9ucQE0Qj%?} z6yY17;GAESs$imLqGzD%T9H|1q-4jXU{jQmW)}AAQ`ZCkR4KyTL3o~MK#RtV8!4tvU15!E(JNy)5TT^ zWWQBPesX4t6_}ZtW@wa_m~5t-Xq=X&Ym#DashebwXr!B#m||(3WSnS`WRQkrlxJRX zNn&1dD#)mc+ycGK%oHoL#3akqRC8n9B;!O=U6Z7gR9#DpWCPt4V-v$v6EkCrWD`Rq zBm9dp(=+oDbC6vHGAboA#mdmo($w56F-6xR&C*EM#5~nN*V4emOxMCV)!4+$AjL8{ z8DdmQvXxtYQEp<1tx{%gVtT56L0&r84p4wwIR<#zDjDe+AVdOk5=+wZi*jw1!ZY(y z^2>`6QX!eSslg?Qpzt&_GqNx;HZU+ZGB&m_v@}B~3QH|2&dkpPnQCaDXJi7F0Y!|J ze^F*?UP&S-rP?Y%99NNBVC7ttnpl!w6q28xW2*#ml!B3-p#eC7E7(Ay#Ur!0B)_QA z4xFaJIVU)^5W<6Gf}BjSl!5{{;aVjoLo6vyEK5xRJ3s*@m7I~7m!6tps{~C_F!4+* z(Pf-!W@Kq(V61ClW@)Z#Vw#w!YiVknq-$wpU}TwOm}rokYKdxkaei7!d16tjV|r>{ ziLH`*W^Mu4Hwqfiz|=%lU!IW)3Mm64LtO(4T?5k)BV#KAb1PFLC4GqZZ1h2S8s=LY zeT?vcC_pL%?6?#lVnHr$c3d|4;F1VbDnSecl?t@P(0HJw6$%QYmXH*_qro*ATqK16 zNs32P*JyB&6apkE9!*_T3ob51cPBM3#a5|Y$=>e4Ox#DF+)HyUABOEguF3RyR)<$B>MBZ)f;t z$cBm>tG}o@uTdo3i_17cRXNN`v?bi&Lc_FUt_xjRcY7CJ-ZkNoUwLWZ#9bS93F_$O zh%a(AZ(7u}Vj`ECcKm^Q|1~9An&+Cu=PF8CwHv)HHT+WioC$ zZ8>dg=g-px*Wa@3pCD2EE!{(O3qzNT;O@HowWWvTlakZ)o+aEoB01-VwaJ!CH}wB$ z)IWE9*|6%7<+oo&51#ZGzU4X6lRDJR< zHiet>U`MR~mI~Hq`qSUoY+-#S=5=9D!yB#?ZqB~Pn`*fuLZ69VzZTf`uyHZlX|+4$ zGw$}z%?M;t6}`&4KI-+n6PLHJ@Vl?tSlCjgbjc!WK3~P-$;ZCe9_sY`(_S`F{DNPx z(|NZwbp?(O*Q>2P?>{j<`BWfzpbc}?L)aYx(sBW|lEoI8Fbe!p+c zJCjDOg@-IuE4WN1Mu$7)*IY{AbQBPHXWg{>s(N5Y-5j-FA5`D$>_|6xDse$~pXLJ2 zGkGn{Td&mB9BjGGyiCt&x2f;vwa!0gep)ZSD-3ksB_*;?b!~zxAYJk7n0?N|VjpUA3hvA|NR8 zUDG9+;=0n`PV=6t?DrSG(~!mBK2_x9$)?#aSYua(7P~B&c5hGC z+Pjauo=1K8HSPP!il+u$-+E#SIUlWAK6RDS;k^2K4K4TEg4?DY_*r_O>aKav-|$pX zwnu(VIc44xY>s|iSA1YoHhVZ{b*G1(+F)8LR{uQj2>*bUDdv`|KHsccW0@_);dpe+q|CV_wD}lkCi*h zrN5<#zg|~l92gVrbwua1z~Yi~=M6$cYOiE%1rh5w z@75W9{3Feh{M+%D@TXttcilHXk~;qNz2&-lj}HZK-bp=hf78ql8<*cS5xVfRc-o^b zr@)*)4@*iOZP47ivf;aoqRecCf@!OiBhSpNe9rnve)*}wC|l`Tt8Z$3vp%NBtI2$Q zJ-0Cbv6r>s#hv^f(f22nD@FKR=q))QIossmH-Z0+6E3=cUmSi=cH3(e-_0_p&5`;P{Y|D0M~h`m}THhE{ zBiC}B;qlFB`g^agkDl>-=ezHIw?3c08J=ga_xtwyuCLn+{JTCbnP79@@M${Z$$zZV z+05V8b(i|QT&%j}dxFH1@Ul>ANe&&DcYXA24)f3&kq1&n5d$g!him->mO)^>&soNax`Zt5CN3{ma^JzpTeq1^K3Y`E5zD7n_<= zXFPv1JI+Gog|vGS@1K8v{-oFWRs^h@DJ?BiXXot0sh=a+cd4{w(c*&IB;6~2r5J<_ zw{Ms+;X{MIqNm2e>k`=6 zO9xLhZ)cZ1HluLa%;jf{UgvB+zp>%`DethzQq%3f zq^#d%ab7UYJx=kjkS~=jQCn3#(*_K6CioFmXJRy(sd{;rLiH^(iEA>~<8drFfXRk86aGh_egRr<8?Enh1s`JniO z>G77$&%d$Ge?NKFt)7>T7Ri14f-D5)RbCai%-ipBCvwNMl{Y3&yT}(~bMleol+~|2zkARA?WFZ+H;;eMLsb+@V%BB_2E@t-)&pn8c((rNf-7OOqaUw ziy>upV9ENKADj}(7b!?ugo*lx9A^5mxzR!Oafa{mvpcTOO8@j*#>q5ka_;L_9~JnY zvZ)qco>#qE$Zq|+TIJrY>zSt9;M>Gt@^V&o(vL1?2a%0!=d;<@ONzWJEoAHAIw&Y> z(OuBj%69G;3r{ES(gTq?#*Mrd%`!*zysyM^ZJT+R|C>u)hPwZv*_BnuVZ3?XI1t($g2ZU1E&QWf$v~-I%_`lu@I5 zOM8&l;#HE1(}eD2Su?#)DbV56JFv{pzop1+s`IQ=HP5y0cbs}Ru|es7=mM?415)#E zo}5;^-jRiEa|w^LUT(yf_I*8X*wp6ks6W*2`C^@=?4**|RsuuXk=@sc3hcm6~ z(<^;0bY-K=nXV)6wMrL0=9H`wPCGoKTF%8wt&9KF{`gC^5>L1q`pP{%6gZUcc$~Pi zz43`rKuhDik|h&k{@c3FoNw;Gan`w1zJdwdXOG!Uy=(vGaKP0v(X82GT$@Xpr>Je~ zHj0SYJ2T>DgTlm&PfGW?%jwp`m1!QZ6V zYw61Nd+uW4ROz?dwWc;snCclPUd$l>>frkVvrBvzx2=0ESf=(WAp7^Rjk=NUSw)BM zRPRnwO-S+NKBBqda=W_33nA7W?lEOO?9H)BtNAZZIc+>Q_CX6|tT#(3LSzPcGY zk3T-k;_hxRnVx&YG|6C<%Cy<(Y@4}ly=x|Dr%ju3aqS+rf*kK>OPSp`iZ%qb8>j{F ze+@Vh@pa>_oz?L_%DI_8MVWAK-f(7%n72y7gTFaZV!^wf$Ui%raKvZx9e0lMDKA#7 zXiw=B*s<^XjSmbbPffeCm|;=G0X+vpsYWr+;A6{)e_pBK}6I_l%R(9v@DP{G#f<=ycgp z)qqQRq3hm0ev#SM!2bRi^HDZIK9)k=sa>%X>$?lqTZdgmxZb`dwoANq)7BW?L59&^Q~LlopUWOj4ml|NN^D1ow!k9yWD#g z1Lnk3FFlLYKQ*6!Imfc$_!WMFV zUHi{Bl3Tu42ySaL|6{+=;o@YUC&4Dl2Uay(dupz{;ux~XwIgpv#*9D9KK;MJ9;Fb| z_?NFm`ba>9o7IAd_SG*UUu9`YTfdlLuIaDSm0(yCxZ{4xSGP4rzYZ1U_zE*gPrs0> z)USER)yIIPTew(k5^whz^QY^>LzJ&9-+xY2xg}Nd|2Iayg*o5-KB@jL-(*l_Y&=up z^=~EC)1{eduf)T@e3~}3tib715pUrO&vJ(a+eHIoS8Vs3%KZM|lh3O@3%}ldZp$O4 z$&ufqqxY@69r>%P<3Rr#(P=05b~P?1lbE2*d0>h)lYyjl=iERAgF}r+ODwEE zJ#e*-J$-U>`6f;4mn>ehJ*@L*XdXMS`qcd5kq@SsxAtw%{wthcDCjeP&Xk`vDKEd9 z&F1D!I2P~CD9W#Lrp&;%_~F8thqi|7S+v6DviX~J6ORj(MTCEsE|~XO?7#1Z?!er= zvCmWwaej@qtJgYGsqM0rOK`@wIu(%sU(;;R4qtbI%8?hk+auJ&@D{r~spICY)W!d)=Pem?p zde2&t#^~{2`^mdoe}*Q^n03)@ld{Ren(bOs4jpBYy~O0(zQ8!{PMY{9*(+PMKl48B zewvN%}3>R!K8^?v-5$ zxsm#&hJCm6kt3B;gf|C9Do5GozjQ9p;JNT)<2b53TB5?vjd74 z{xlltKjRbe+Vq(DqMPRCEQh+xlds;?U&=U}etRXyrnASk=Y5~Tcuad?jhyxzrDa+h zo+@0QrFu_YO=0%BC-1KB6n`POx-Wb4reH~>n~q0h#Y!XIB_1yRS9E?)l~L!G>}4N} zf3JJ;d}DCWv9uNCLXyX`&ev|_Nzo{Y{92q*E~;jIpjg9W>!zUG?DfX|$JwQ9xjy^n z{(t)F-(~TCyBjwgd3SGjJ#&uno#23+-nR@4jBT0D&HYHQ(t^TkR}Y2? zu+%%;xid#3cFB?7lbpIG^lk1Pulz1^Z#Tbyl<2ZqvBC|jBHgs)gg5UD-S}J7k;Q;P z{+LSItIjV;Z1cXB`qze(2hMwX_j~$Rt;)%ab`vZ5UbeaJakSZ-`Et(p&cka|G><$# z@|Z1l!jFlsH=WL()6K_iW1N!`^!Z3)(#9JHi`X|$S}5c%>@E_zxJL2yrbCt8o1<24 zyk`58+qp7V^=xU>;f`cZP8CHbH{R5c1VyjiZe5)sbC28naqhGC)SDTTYEY!JfJ=1J zWA^{e_cxnz2T#rm(qLnW%lVzm^5P!rtkT>6?6$pq#`&P0ZQYh_`)@I%*?X?vzV(FQ zh3&jCG22Zm`FGr5WSd`da)QiBe{T(uhVRC|mdRYG=l-&0XL0u2$6pvvTv#(LzoFSV zVT;Gj*K+Uq%FeyLKkwR~ItKsFqM2c-~aDsl@L zK)}Ynq98FjJGDe1DK$Ma&sORE?)^#%nJKnP;ikR@z6H*y8JQkcMXAA6ej&+K*~ykE zO7?bKHWgMCxdpkYC5Z|ZxjA{oRu#5NU~{eVimgDx`br95B_-LmN)f&R3eNdOsR|}~ zCVB?Ct`(VOMoM;E3N}S4X;wilZcrnNQqpXdGD=Dctn~HE%ggo3jrH=2()A53EiLs8 zjP#9+bc<5bbc-wVN)jt{^NN*0MnKGPNi9w;$}A|!%+FH*nVFcBUs__Tq{OA5pa3jTz^4nQ4ZKUDarb&IjOm+c_qdAhI)o5R=Ruo2EcWIf*?IJw*aiGBDVlV zWl3flBCt?=1CjxI2iYMNxdm`@QB=eH4ps~fBP*Bu>l z7O5uYDMqQfX=&!hx+dl(X1W%}$(FiCX@(Yt=9VU@rWR&MMtSBHmn7yTr-F>C$Su&z z%uKN|OEgY2HL*z3wKOm_&^1XmHq=ctNJ`T+Fif*ZO*Sz%G&eU!GQz(oGd(jeF$dXI zAfr+;Q>@HWEtAqL4U=^f4U9~5P14egbS=#ilXZ;@EX_>~Qq0m!4O75Ir6gOqKEjtgC#%#Zsi!@X{%(UXMhk1$Vn_o%P-2cRSM6{OUW-UMo5KZ=B5Uh zB!a@z(9Fof$k@QZ(!kin*wDZTp(rf1s5mn}4`iyLfu4~GSOyd^R{lkqsd*)dpp zWC#|o$StsPE=o--$uA1Y&(E<{0y#>-NYBs!oWK=qAkpHHSzMA|RA~oJ)8L#FoLUIs zK{7#3CRj>A0i1BH5|bg86epIYLP9|SCY79#n3tZKVygsAQZVsMEYX!>ZfTZkVU(TzEgG(Y%sRS_)R4ULCL*s##RwyWpT0&Czjt19gaFG-OBq<(EU8BK8 zQV5Wwcr7#P@+yxmQL70(Y)*J~21_t&LPhVH|ryOi-99)}^YilqtF#q;+aSVw#{C2v3hD@o*aeHmS zdk);jN1e8rJou)-V#?Q0W_uv2CcYpr& z4e8b2?4w`0+R5#ot@2PiLDWZP;c7v%z5?Cm@&!DP4mTWMecVm+ee>!3XE`=az7GxpC(c0I@!x?;{6!-_p!Tc`az-#4YqHQ`U*q#Ijj z{Mat}h<$TN2xq7yBg=*ZMh4Z7jx1}=`fZs!MbfmOq~}%m<`--+)#~d#{F&tcPg1#f zSap&0`o$A||6b6erAq9(#e{4Bb3WYl zbWhYQr8~@ejGHsBo!!E6Yii+kq2gl~HuyG~dY7aNT`mgAxAmI7K~SzR9h2 z?}=w39UO}fDnDOgYCe7Gw~u!ZRfRY#kbIaaw?`vkS!0*MW0}n_lO9_>xVg^ozsV<- z4AVlUuvGVh40AXxuc}bmyDDhSvt4I}KeQj(c4V)QRmTdBxay53&Gw(;i0f5e0lD7q$}ZSuUX&ITP_!uO`LvSz_PWQ7ixfUT=bN%jZ1R$K zJQwG4zg=8x?_9a|Ft{NQSj!CdCT(i z`y;%k-Mo@CX~wk2{rUS<>LS{C&&KYx3}9hty6z?+BYD2mad*~f^>^x>vAvygXA-5m zcUBl`*w*L8!<_~s%|GoX$QgPkqH+qkseDIJ+HR;WED3uA3rE$aGAHH#@1cTa_^ZNCwzVd zY>xB}6Pw1tU(SEw=Kg<`ckF*m|MYpslx*$2Q`+lSE|+7|zxDZ_^!JPQFYWHtKm08C zzJAxcOV`;Oy(d2Zu`l;&@&D_k|7{QaHmR6eFR^0ndG^oG`G2LiU8s|Mv3bb~(U)KU z@m#TIKKh~Vfjx6id9?q_wf}asX1@RY@0MqE{am@A-<3D_?_KYbpZ=^=uv+)ux!dncPMfRUn`hmeZgAeb>GI+KkDlwZ zTe(kAKlHXYIJmARAaDaus$c&dSEoNe4+ZXH{P*kU*ZH5MPA2S2%*!dT`7E21_W$6t z0+IYh%I0bF?niwtt!GnkIWsr1r$6O^rd?Y6pJ3+|_k@qT)v+;~cB%OUJt*dz9oi<6 ztZ1p5k+(1S?_TpKAL80Jd${zqI%TP3Ojymbv2p$^DG4ddXH$yxc0QX{EvGsyq)2nA z*u?uQHZGf$y>jceTdy-VE}a&gxz+S~m9*^_4$Eh8Rd3gRw_}^k=O$NEU2s^!_>9Hn zGn>y?-F{Q7^I-F(Q>)kPniXB!D_eHw!x6pR@3#Gxd&a8~xhd!9D%00yd;!)UmrVAz z{JQ1x`5)(Im2ZFZC*S^m<+VTI*AAVXlDFo5v$i!sz+KXCo5JO#Z6^oke#x5 z`Ohg$!LO!lUfzAVC}C>k$(wPK%FQQhP8_d){;vPb-znzPFV#=o{$Z-+@+*J!jh;{0 zwg1GO_m#gVugP6`wrbIC$skFs75+>9_~j^ns9$&T!ueX?lmE_c{{6Q(_%olep|t7z z%iE&fo-r=AHQqSm^ObkH@=gU=kzv=ZQg;e%?@cV`i|3E7JJ%zXq|#w=>s0YPvFrDG zo}Nu(KC%3kO6uhNg6DF#FBR5oyYXku*B?ne?{i(lws1thU;QIwcj<-WQO{1g6;(yA zp0{Iu&g%DkW{(yBZWXs%t)14Ezx-X?dA{S@)Q`>jSa7)K=(kmy*H1pKHtC;<$-x?( zJ118^t5Wxzyzu7N$jJfj`_B9Qs1dzu{&_?0LfPnG=I^JNL?l=joZ$>g3@$&W$D+P& z=Q*CQ49c4q-#1XVy8rFun~VJ$8&66!y}qNKYrwfKBEFM9yX<`)Ln>|t zi?xE%q<`w^pL|=UGu8AIy;c_5RvEbH$$~%3636!4s;ru9(*Mf%r<%o-1j*tZd)(J% z&eSz(s9m-5O^$H=62^HGXU-0I80o#`x!Szf+EaXI{w%L;jF#EaIe&{-{!$s;sP7vR z-l;nOd{P&+IAp%?qm2_@@5$U7aADtx|BYGRcg=MDy;i(iG^=6iiY1qSpR}90?WP2$ zG>c@;a?UF&>{rcNI{oq?v$BVIG6K`giw(ppVzp))a58N4*yEccx@t?}+iBk-URTC5 z{JEuZ{uQ60?9Qx{aygt+xPIq2=})i{VY~74;^gag7sazu?N88TJjMrz& zUt-{`cv_iM4eO^xTPbuA=GePN1%>MG5=UcL>`i4Puk1!K_)|f4a5# z>WuHnw%2^hDx*U`=JZPV2;Wj@UcsKR|&w(0p!!9#QI z#A$x`re_wcjDZ>XubT?FZ;LXutUOd!8+MX+VSDNirlyd@h{}0f%@gkJ z+_~`llZl2Us~uI7{>Sw^=-brb`R!R8Q~t^h)kvY8>1RUhRA%d+%sa$%MdQNOhg*xo z%PypaP7k}bd~@rCjV4+?shr_~O5fCe1cu(ySd;iQdO^*EhmBKfGOT$!Cab*Jd#dq< zK?4QkIxGxD)sjrS{BWi$rbfwI)7T?k(q+;7j&%il@P5szWt)akLP(4oA|5t zEfCW!y1l960pmVT(WMz2s%lA%Yc4N0>Y1}JJ5j0LF22~IV?yzz69rdHV^wM-r?7A^ zB~`6Z2srsJaE@Si7?Z=M88^`0=*&HHrDfAY71~sIQSB6^Po18J_R1V@Gg!ltsG=gD zw|>I0V786R{&|=l(ONt0$x(0bl*w9k+B#d-GZk+?6!mxKp=aT(Yoim37oKEw=$e0U zkDY2-OG=CBw7l8ju0J@0#g~g@$!zkyGBLxa{GesyLDPdP7vA^rT6%HiuNxQiJmw`G z414qFz=ueQo2g9~raXy1mv~l3=#SP!EoH64@m9Lid#A9KFy^-(+NAsa;&11dPt8nC z7cA}4*vi{7T|!F!v135=lZk>G0~W0k7Ag&Wx!b($#9P?_?$b9}eix=cj;r`}JmH?_ z{fM}IfBRGw4UZaGS%@!KtGG{IMUExTNntJj=8Xbtrdb%TJND?Q&z89o?|xku<(*@k zr}Ee*z=ET6h4zb*PyIcg7&k5U?mMKtpd*O&q{!C({7UEO)(L8%6YiY&nQ>j!vvYcc zP=2K1r6tE0zaI7CV%5m;pHlDSI`MK}u-wi5bma*dc`6Z{j~5p2mYP>{KcOY#v6k!n z*T&X7ZMuyqO3U~H8}y%NJjgtGz4e%b=drzqcblFH*x6$Y@eHz3nSl!$W>3-6e1da@zYmcH3r>NyVgiw?Nv3j{M%4naYSfC*^5ss zvl|6C8x(HsS$WOegI}^?TGjG>7oW5#>j;}REYsZ57Jo2`=R@1yybAZOumk)*-W|13 zE=fN1qV+J#O5@pE)h{kR6&`oS|Ko%;szO`iJDHZAJed~O@Y+#S$Gh?9)+)z?j-oq{ zsHHRDn;XSROd)*PA>)bRX~P-x6H@qWb&_qu0L9x=x$RUz)gG zY3dMrCJ?#TC42q<#hEXHW*mH)-F^4Nylp2kG%{kUPpwu{=v!QKr9Uq+BP#ipS*~QS zdf7ycho6M&no}1Zx;5E3Xd;V_S*_{y%lYx24aK-@H*I*pePQA9W6i01(`U?Kc(~!+ z3)uphr^=gL_H5Ii`1fliYn7aa;AghC=E57NUb(@(?wszH6%TD3i@Xh#Zu<9jFo*Bp z{v>EVAu`aED@60;w$w9Ej>lhg<7>>L;V4#3sHwF0aloZFzP?-@)UOOpn+$hIO?L z3;qb?aqw%N?=_vyb%660*QNOa&byZMt~ceGe{A`a6{{Wu%0553YYQh&$I@e!F^i^l z<^0yV|K4a>@q#N`mbz@W{2m;jC6skz)}{ITN}8@2?ryv$X0#wf`O$X87hig|B#6Wt zi+SnJ_>yvB{R9>LiPP(q{!TSJk$pes(Sbu-bPjx6csjz$X5we}=yST6Yx3Ny`MH2Ze`+SS7i2B`3hsK!gv$G%kFJo4F9#|SQ`KmSFttyEI1ILe6=1JMClN4JSj4Zom zUeK@i7WtD~>X_x2pB)}Qd#~g?Pp=8bYP&aHln8U?y4cB(5 z_rou(vpId}$^`rmn<3#P>vOnB(`Mc41&y0tX&sL4UfUTYepzU_ubN!P_cQ@* zhFuC~d$LkmU!|;Ld3S4)y8o-NzRr)CYgd$f$yhC+JWz#t@!QW8gy*J2#Ia7QDS_u`xwkQ&V@P!;15!w^uY)T&gpvIr2r~(A*z<=9+5WzWq{b zmzkYVsBdxzUD0E+vB!XCX|R&Yz0^ssC+xX+Y}M*1qQZ%~rPs{UrT;Hzo?6BqDRs3a zX42Hw)0?iSDCcL-JJ+1?yz^_^z*BWDtLaS*Gsl79`k+#s)tR=5!jmqycU4c>xa{_8!Ive^H0>pWVx}y~^-TLD%5uqhT0nTg zip%!1l*%SCA91t@xa`T_>;8>%|APZlRyMp}sCcPw?YtxZZ_AvJ=DEY(t@+6>#8~4( zrp6_&_bU&s*zvgNq9xl7|BVOlUAX%)U%E6z;M1+>EV-P!mhznq@jJk~_sl8ZYxBc9eVa8)&Rrf^J4QF6#azy~cHI!~~B8p%yc~J2(jEd+NB|&pXa)YRwTFx@z+GY^OwB zMz`m&KHca1C9DK%{{*HOFFzQlvv`&0)kvAMrAhbs{Ib8Syuo{UWBh8Zd$s?!HC{`b zn|JuJPw@JhU+?!Xx*!r)EcU+Asj%?y7UKkwFXpDY{QdryFBQ(TntSQ@S1y^ke{J0w zXEM)Y3;%7>mF&A;I%IWp)sM4{x2AY5cpq4<_2hy=!V!@h+-y9|KQ1y#WE~EYmB@cT z->XEet4v|@v(=83PFndZ%ZSF-P7o0U}6o3+7_5nDF$ z>_5b1`&~O|{gQ+iYyKa-fAr4h`TrQ-=Fj`z&(`(p^SRGE>X|dk?pFO>doYTDfw3*q z**U<|*%>w zliO5|9t|wewfM0)qO_p!+SP-h0xb0ockav)iCuE!_avuo34NP;$1A_f+}q7BASJqN zR;+Ntsz^62IpNJaLpS~wb!0JMkUyr9_Nwzs65G75rT(=c<$?2_-u<5bRjYC`qus=c zzL#yTdmL>xXTF^Cz4P!I70n~hk343Jo$zDg>rJQg=XCRN+ZgAh1bse|n6&Z6!6Np} zlNJj33%iSiF0N60z3EV8_vWaT8?V{^bbwH*;$-D_wg6T6BpJ@%Wr6QPT1mc^R?W2zOr*~@6WsTr;fqDvuI{mYWYkC240EG zkcg59UmvUF{9L`nl>DSry^7od1`x2ZuP8`N&Q2{+NJ>r5%(GQ`zk9!uLS~AsQn;zF zfp39xYDT68?tx|+m%sQCX6ih6pSa-+*Mm-a&RqMQ#DyTolzXzk?Nn!^p}d zKe-g-JWm%}C6N7ADf!8nDOO;nk)frjWm=Mnu7yd8xvq&pqNT1yvW04Z|L^BIr6Z6C*T}wlwWZfjw zG&2h$(-bpJ71;Q>*AR0!B{DL}-6T-@xqZ1lk;5vWvx7zipAXo;cmKuaqW6h3IUQ7kEX8C;36pmNK!nSx~LXhT!`*YYF>)1Qn`}7-DmrfSOx|Lwj^(N z7f_Fgf#Ij~?s^6W2F?PH$YKTtZeb8+WSBKaf`Ng7y~NYkmHjCP8ymZ2cYoMN1_qWU zPZ!6KjC*fq_-DwZN*t?~cCTQMnlX{3Y=PkRfW&HzCF~0=x6F`h*|Pjd@NH**rEkkv z1*a_PSh05A684RZ8(H)t1+r{+HabOfm{`tKIOvnnZ5;6NcLMW$?pR6w%{TAnn5A7- z*}H?E=gj>4Z?(_R)=JkNh;J(H2)<0t0D36~`n8(3PNwBG;Z=FM`Z*8==5Eh)O{%myD1 z-@mzY<J+{AY!rOYbf6PK|J~97u{XHwb z*Y9dvE%|4<(gX$@c}bbqehw^>Hm80Z-@fT~__=o{TSWX=9GB&)_i+3xn0aaCXOZ~X z2co=xdT=%@J>C{%s1g@7U(1;$CcED@lUEH|nxcCqbj2QPFCqQB9UCj2SO~}e+4Z_k!lPk{V1?|u zjhq1?M~@m_^*@(Tw|}~R#i}22EQeFw^y1EbQ(<10o9(XUv}9VrS)u&*VFUUo_60)Id8@)l(Ke%?u~5^ z-Y)SG2(bDrmn^QQEa!XGgv*_MVbt27%Ks&a?y491mcJLwTVHc8L}a=kK^y1Epu@YRN%j-Tkw2i?G-8S z`={fRu0?I(3w)rg|0RlvacY>v-9?(_Z&oMC>6NA&XkWf|>cNMCRhd5LJGcDZQWE=Q z*TksJvDc+qUobrQ`QiDWZ$WR?|28}+z2LSMyq2F6b`?A+wdA9clZx@7z?xhm z{tp&veaRm_>13XL;N$h?;Gr|7F=xNsDTw9tsoCAME^>=P##tqH=2PuXTjnjl^~ccP zCf@N7M`(L7S5~L=q&-#XRpHm}e6E#``{F1Y^}6DzTH)oIC0Eyfd%Awt^;u@FR|R=Q zJ$sTLeiJE6m2>rYzU=#3>&A2wbMa>nh0JQ>dp mic6DQzJUvXi|+E(t@Vs2f|XU=#FtD5m0zB&elF{r5}E)>M?o^X_+~x z3MG{VsS2qTnQ06R6}RrrjGQEWQqbl9XAvu>cc%-BOfU16)YzV9GI*n=x!C9P(qjVN z3nzH*Xm5A#yZQgubHo1+^na!J`Ye4mZSzz8Z8vJ3`))lSzyJKXW4~+u|9P!#SHCmQ zF1|vKHSX}|jnA(CegFEM@qy2mDyP>;tT=aF{PAmX-F?T_-)E~jE3-vuZ~XekTiFX9 z%-6q@-S98x^s}(j{m~M?D`Vf!z4Cni#o#sZ)Bj$6?(`)rY2Jgse0mY{YCFy|HT|;` zw#t81>$l6W<@{8EQ`rVgpH^Gkln+bYxA;@#^Sa`{(i)2P*LlVFu^HFL)mPqKntwf> zd6x7e`82-I#-FFZUU}P(BtezULlPSqaoZJyBnd-IvaAM`)& zyW{>ikoV18hkb9hG#@WHTbW>~x9jOH>%9BdU#fZc)croR`n&LglC^D*X3sw;b)cte z$7An|#K!6T!WGQnf75l}oxA<6(2};@gibE@y(j*>BbjynIftoBJ~DH}c-3w6l6OAQ5WM%&&-)DL>BPws{)oW4jeQrta{QUYH?_PfULmkS!Hm6oB?zLNZzQ_2i z&FM91+ip&ePh2Rhy?)cHtkwD37hdYKe!J)Ny4`Q<7Tn@gpI7opbNRfoU+kvoauM}a ze=M`!_0HPw@$%Rw$KLtVKFJ;O^qXYBY$z|=A+E@d^p4zehoaX=C&T;J8&uppuqOSSQ`5k|mwl2*w&NUEMT)MC+ zi}~gD@9XYfuhU{SeExR<(|7s!-oJ;X|HrdSmGy_FyU#x#S0>)XyQ5OK{+Lzq)EuS> zx?g^N>B{1_E2!+>^Xt|}hQj7_)~sI9%U^m92&izF9e!Q8%bCOiUU4>WfT(|swT}Suj&R-@A@BO{{@%MyNyRv*W`PN^}@>+Pl?9JZ9*2=U6kG977 z-P^V8kj3VLlWzmRbk9qOmD{?(AamQY$JeK>nf2!X9}Dl7OJZ)Z&JwG0?$oyazh~=( z6A7D--Mq8?u<_fo-Ot!d<{v*F{^XqS&NAicop;_@t_?e{wX}Du+pDy-HO%+Dw*6Hy zu}=L|=^y+5a`<9%<1dq&)gPadG~BW|;;G+}q?|IjOP6*zvD~}cSeYN9u;lXeOWRw0 zt!9PPc;+RtUh;H&&6WP+aMRb)YY8j$-L$jkT~l|IIAazS`TAU$(B`MfkLE3n7U7t^ zCtRx5^wqytsisFvCf?9VE1luUuw2=#_UpaZeh$KB6NQZmXP)Eu)5dGLB>ntd=|F4m z^UOt4jaT+A{I;+<>;8!#0qa`(vQU?_H*rzMxjE<0ce!8S_%-qSihbvA`pVCj>`s(R{OQ%<8MfK$Mn)5!qxWOa(we|1D7`3L} zh+~d#E|fAG99HbMVw^zUnR?`I< znX^~DEJ|U0_>k3)x0R{0a)Qyb3ONx!`G`&5U+%mVw)bljyW+bw8AqEpD@$tyPw?+3 zSk+vyG3c3GN!-!hhbC-_oYL0!SL*cd!1d{TnN~?tP9DDWkRwcIk!LYac;D&Xrs?b4 ze%el`vEY4w(=3Hmf+tQ*OQ*(EP42Y7%6|>E?5yYO8>Qu@*L65)85I5x(mXb&dEJ=_ zuVZBIFHLf~xGdy_G`E*;L7!3Bj9TvG?F>_b<}H<-QaLNxX!m2gIVE+yz6XPLar{{K zM%m5KTIq%QGd8`Yy(~MHTnO4SL7b^d>BDp@fsev@f{rHwO7|UWc8WDp^{7aY+Hzwm z*H^Co)8-75H#KrEW7b}#dr6kdXU@+66-~d735Hcx+hVwm=uXgQKFUy$DnIwc#9L?N zbdH{LF!;1l_(CK9N26z*?@y#$QP1Rz^xklNnZvt>99ns&x=v^X^|j4jW4=PhTT7t+ zVaSI9=`|dS!*AqE%xry|UgOGF&>OI*L14=jgUhAP0#5|q)QWi;K6zGFbw@IFl$xUEm4gnZD+Qju35==idUs-v*YCq}0%A_yy1}Y>Ja@j%ljGBjZS-3Gb#t!$ zjm8JZ6xF-9ZYbPLo1lWQ4e=;M=5$<{hs-E$%P5`BPP7?ka)T(PjsDD_5Pd zlYZuUa+66vpGf+|a*ZjzYiH(Z>E`>%mItnok)NJ7u~}Pq>sO(Fo-Xn!S53b$Y?ZsV zuVt@eSK7rKo0Fo`Id7I--utrn{lae(|G1k4$V_K&4Q+MQ6V4Wr61o?Z$&kPlJTd8_ zM~qzQM$si#`~tcrTskTw$RS$xlskFhv$tkiHw(3Qwz+NiH)s1L_WFm@ydn=-w>h-D z-j&pC@A_Wmowgx8>`Pd8HkiX~CP%YoZO_K-Npt7U;=MF8>(oC1rw~&W z-3+0**EoB^w9OB3DJ)U>%X2z5Ff~~(vCXpl`}7!TufVQnC%aiAW0F!9FTUuy_RgfB zEswI&ziqm@Y;T0Cim_zcCJipS3~yyubqlHIgzkInLdClT${f<3bV3@b0(1LA8l3=3o|BYK$ zv9Im2i%xtZ&dp?9|Iw ziaCtms^Ljqb?M*61&QVcENdHFdL3q{D0kmodOo1N>-w%wCx7!a3qDgjV&8fCgS3~h z-w_o|xtJaph>Tu(wuU7Y5o1Gtruhg5xz?!k{ zC&vQM$i6RG9bP+@UAnbfPhTMLb<-EMIpJUQmoRX&%IJrtWN1gKY@J(Ex~%A-{j60f z%S+F%vDiClMd!3xi5{n|eBQ6hc3!f4^4V2hR%>qsIYs5)VitPSF7qiRe8YF=EiJq{ zY?%?k8`;)a|9Fu1;pXqVJ(se5eir^wyn6bmUCQPQN89wiJy4HW5oj%+ox5(kn(e0G z|NMm?)^RW{zTg#qHUElJ0Q3Hayd#B@zH<*=ys_a8H}kGHGB$IMold%z=xMQX`H`^5 zO|DF-%KN`h&3nST^I6cNO_S%%2|SuKU+#A^kIKQ`n<5S@fzJe%B^}dPp(C+sL$cM^ zMam6}g#*k^GHjlyd#cVS-Z9ZonK8S1X7?u3t1&X%oNl29jMhE=Tj)D=cFe9=g9*|) zs&ZHUt$M0aYyVHnqv7SGDHREIzj>vk^(tjcE?Bo^w$&V4{k^jE$^15%EL+Xcl6#KK z(bINud}+ISzbx48(YElbD<%e7PWu_DsNSXUbYfjwT6j8-O_JZMhnqjVxxQeeeJ4J^StU zga_BGQe`a)S$)$h@zm?%&?^W6gGz`R3OUy00-VzAvY|3)mMdiD$H6E~|5 zd1Nc@_Vr)5nN8}3RF6aWyEhYh)Ydk# zJ>u_kKieS61_Duk)N29_J*M+!8&#^mg&F-n8$QGg;4nv8y_EB~W(S?sUJd_FoUG z*LA)>cBA2vtrLIrrH;3@8PD>b^_{VJYHaoApzNF4d5d4(*1Y)m)I^OFq3V*pYAw&0 z{zpB@PBzLs5m7LiFR*+z|LiXT#vneqf~6&{T>4YV_T-PbAYF_Gpt9>z)&%#cA~Av;Q*0V|KM&{Ej}@k z!V>|k2b)%OEDqVoA?6jmFqD1r^nZ?%+fL_^~;nw4m_X)q|k|EcFg|?#vO1 zU2^32B&Ti(eVcp7E5FO!+s!W^CAw@@tZ>7sNH;Ax;mtckH~toNWHDfnKc{3jAFJg2T)o7U{G?R9irfMQ5U{bYC`e4s zPAySLN=?tqvsHS(d%u!GW{Ry+xT&v!Z-H}aMy5wqQEG6NUr2IQcCuxPlD!?5O@&oO zZb5EpNuokUZcbjYRfVk**j%f;Vk?lazLEl1NlCV?QiN}Sf^&XRs)C80iJpP3Yei<6 zk&+#kf=y9MnpKdC8`OxRlr&qVjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(y-J+B< z-Qvo;lEez#ykcdL5fC$6Qj3#|G7CyF^YauyW+o=(mzLNnDRC(%C_oLb$Sv^og&Ut& z3=M_k{9OHt!~%UoJp=vRTzzC6#U-v~CHQp|hg24%>IbD3=a&{Gr@EG<=9MTT8 z*I!UtlmqroO0s@xPHJvyUP-aOp`Ia%mF}Lt0dO6lAV|;5EdcAP$SpuoS(2HC2rLxe zfMmelL3T(*ZUNj}6xA@lgB63r$jT)@xfJ9)PZwJyko{IE`N^3nR$yk5iDj~3N~)o* zk$G~8u8Db?scw>~ahh(DsildzVTx&rnQ?id^U_mOY?Yu%3MQV3CAut9O;b}W&69M~5)D&yO^gka zbuCR&%ylh{Q!LF>P0cKl4U>dP}y zK_O*eWTv_e5))Dn`ycQm+0gNvjPAW89P>KY9$l0twa#iOZY z=qFmBl7LNSF z0ke*Jt80{qMg94k!2Ox8@uR`yH+f~-O%*C$Hp}l%oH6tL_r2e1-+yPDcPse!{M-z4 ze*c>3DnFke;CP|oBG)oQLnSYBO|71W^tT8Zt!V}A4;N2wZ~G_A^i;ZDovZOTWA9V_ zi{GyWo?;bYSQf&jld*2`A=5dDt>ufn@=|1ydA#4t%5`jh?kP9BUGQT6^p1CD7Eft^ z+ViMq9&2jj#Rb<59vgRXb96LhI)toWxX-q)_=&`3ZBM1y1usMLUE1W><<|agF;8E> zIxXBabol{h&c*~;vuhp7^2V{=oJVe+p7ho?;Ir|LN5MZHXl=Tl`10-F*cs=)FxNl- zEwZO@Dc5Yiu$SCdS~ZWbMe-!Rlv<-J&~EoaEs245Mvmt;?Fp}BCQ2>+cT(aP|NNU% zRVtMjKN~3h`Ni|;q0+tcr!~x-o-wbu+7X_8B+(&9sl#}S1@qg@Ap#0ZEmKk^et5>S zOVqgTah=hG12>~Rg<{qv`S~XLi)@sc&}mqb+_*!s;hJWF&fDp~8jmk{)!h;9qdZYR z>5gQWon7aPi~^Qfuj_vGp43!nI?!&wVr<#|WRK3a66Mkuq2(_FR-g7*cS;d`8Z%Tr>$%jAXl)zPYZbJEOJho;Ytd4ZBZc$7tn>+Hh;iq=y;pO? zUM4U1+>5+XR?O$8J6JBzc@n_6H87vM`~uSylLJM+zA&D4e4FcZB0)%S1+P8p{fYS} zrv{u|u$^hwsVn}r66d}OW!PP*SnspoTH3AD71k^gjA|}?z1!Zu{owaT$;GiMXx2L+ zmVl2o@4Qvli0}EgJn`qJyDC3&cs?(hKW)mbqA`Y$+6ya-T3$>Lv*-C z{k!5?C0o&Ie&y2#ZR3veY0o^qz{y^C(p7cc!fjTUmNAC1$V_H|nyp{=xc}R#S)?qQBD`+@zaJNC!vC`}Jk|a_=?)|RdA6G@ce^IKvq=Vd ooL(?*>q+^7e^>1f1~dO)cfQuz&XYOI2UHe%y85}Sb4q9e08oQJZvX%Q literal 0 HcmV?d00001 diff --git a/actors/breath_meter/breath_meter_six_segments.rgba16.png b/actors/breath_meter/breath_meter_six_segments.rgba16.png new file mode 100644 index 0000000000000000000000000000000000000000..a4ead4310e81e348b4914ecf757dd7a86dd728a1 GIT binary patch literal 8890 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 literal 0 HcmV?d00001 diff --git a/actors/breath_meter/breath_meter_three_segments.rgba16.png b/actors/breath_meter/breath_meter_three_segments.rgba16.png new file mode 100644 index 0000000000000000000000000000000000000000..0980cefef25a1c251c0ac2338061fa2402946689 GIT binary patch literal 8640 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANNPN>w2dB|(Yh3I#>^X_+~x z3MG{VsS2qTnQ06R6}RrrjGXlNgrLj+&mvY%?@kvMnO^2Csj>B+FlSZVl8I+)R%W_3 zDNOLLXxYtv>rMUN&ok;j=x=%8>$CLPw9QZBb2k1w=eYI!{<`~fd%yqv_j&#NAOGI& z`T6I8rRWcy^Ves^U*CUz_j!i%?$0%9C0Crg&j0u~zwW+c>+N~I%(BdIwTfHccq@Cs zgZc7zvK#*8oPHK|`nqn<-#nRD7@CQMJM6=bxuPkNx%V zPqss`^Pkjb2KL7F*5}`S>%U$3f9~d0mg)ApUmFTv`g7|_wf6T8Zgxff@7vC-{xID_ z@B7t?Q0H%c3h`yP4rcFqX0t&?{BF@*+3oM2mrPc5+h6$v^0FJzYPDc)9>badCA zuBKED%e}>-4Ld%cTU^5Yr|$0${~c@sKIUhp@|Ic8Q*RhhjUEiEhb;E&=JE!AYv^Ra~0OrW9$dTsEzW&+^%nV!g;G*S$41E}7)5xpm8> z>=hfA&B|VR^-5UvXI+QBl~2Q>a@T%6w`A@_>+*8F7qveG-DEPK1iH!Qe)0Tu#^Ums z&1bUGX4n1{6|>p+No)1GZNJK9hukV$KDYd4>Gr$)E6Wabs?V)CwQ~8~e~xEovR|7Y zS1)J!-R*4rl9I%q3y!T_x~IKh(PWh~4rkoiAGvZ&xDYqv=BuPHZWlO>Zy(7#x%rez z!8G9{1&QsSU+&;9t5^8WYcBja{?OU|6OEhRy>~drvFpCVcjLVM4!NGW%XptDEinoS z$X)Q~+r6#7Z~3PxJlhkmQtqN*o8gKAsxp@s?dG1D z>ofQDQk%;yQ$2fym0q4aYi#f*_L#-9o$3YOrP7y|mhE_a)FtEDx#X68RX@Hiv#HLC zd_H-neaoVkN}I3xh90m{JmY93bZ|Fom+{=X?8Uu%z2( z@%tUWF8uue9F}M28-L&5Xqq{V)z5CKYona2{&L|{5xXqjPji3O&f9bB<}UTdc7+*9 zK5;!^*V0Uk+iqVyXq(wSt*v>YL#pAqnND}q7Ht(@EyEt&RPpxV_ZQ3azisN4x703a z>wI;TA?Klb`7@h?-;d=bzta_69$(CQ?x&MTU*TedgU=RCVBb@@OELBNoN5a_ogISL zHt|mpN%NiXy3gGH;$5Ze_(u=_c`RahHh0mclutRc& zH?_~5%Q~JYdapn6_m%~xokTR+>?&vO{NZLJzPdrD|Nh(=;yX(A3e^ZQJ+qv)K-@h; zZJ(AyZ>m=DpO4)l#?j_m_dSg3-fgvJX|L0`_S6Ti)W~3YfZi&K55O*SvntmQ&blww4c@fxb6I(gFI4}SAAubqc+~( zwY9e0uBiMf|7O0#h6^0VEs7^DINpD)HCOXZqRf|T+f8OR6v^bwYuLJFz;Zf(5NO?`cH&KbK_8JqT7PNl8;Iw;70k`dLx3(qX#Z%iuKc$Ll zoc*?1cd}2{c|+YJuN%(yYN~Qv7M$LEa_y@9>l$4wp6qMqygBm%%iY=g1i5Z{i4@-w zat>1av8a)UM~7E6{UF!B_M;Eq$8x@k5zRJonx#^aJA-rM&w@>|azE1Bx*D`Y{W->_tQyOnfnONP^RUq5tdBxXlqnS1X%R4<)+n3uP{>`2B zciWSWW0PmNuPpqKo^tGIV_Bs__rsfFSG&Umj)>o%W5V;O?ZBFy!Uwuf*)po#O+OjO z8mDA);qEc1^o;)4mL*es*ENfo#-3zbXDE58X?kMZd9kl4i<4`L*9pDqyZS0HAh{t&xLUIjatljpnJ}K&+jG0#P<8RKkvRll%q-IRI>J>gITI{vH z!K91tXZMBPVL0d%XtlQ=v(3Cb4^B*AIHkU|gL_u%=Y$h(@1%c}FEwP*&^@h} zwM1{%j}`V3&qRv_D~hf=6ga%_44rPZj5qeR$nF$LF@^FB?ejyxD4Qm6IN@&+tuFm z&{%F^4ntf8BM+mmdYp2u@RDcGZJ#QuFY4^RIc8Go2Yl*j-m-}CR)G?SFQW5g0 zWs3UdiKh;Wa7fEqylAr)@K!R@ysD~yVO#mkXICBf?weSuY7zEzQZl>Uv%`IR#5@9B zcPLIO^73|{In6~^?B@m3X-QeTC#AA~e=S(>$Dobt$_A?(j`@xDH@)9=i|_TE8_oA; zPfTOEzU$Ad4IZ3Znr6r3s;IR38*Td0$Whq9VZ;`5iEB=;#EGNH zrS4%i4U(OUW-L70b@<1m&s=&scV*?o?jHE0aJZ`HHNGP*B)F$bFMP3+dG?ee;%$Aa1D5}|7vy5nJ58pzL%^AH zAvg0GVi1PkmhU~ceXrg2z4uOtUEBGlP6z%8kh0LEfQ#2xHPxd@2GnO^SzYVlX{Z^_Q-mM*^15BXd551ZF~9b1JRs! zb{eP$9dDSy_mg+`=Q@Uk2f{mc1Wc_}(s{Q+_i?CTRb=>1Ca$oS1q+u~Mg6IIW+qcr znCKs)dHn>Zr~2&*+r}OJRbO}>y$)v9dD_|1u|q;?M!ql8RG0lR_wKs=U3`0keSMO2 zijbmJzRBZ#d!Or7sTuFumG$7+?ix2XmXDL4CB0kUW8j+UHSbxqdCNSpC#SOF%(s>; z$@Q;O+sNFxx?SnVvD2S9G)-j;R!k9ja{1bnLMC%&#Wxd}Clx*ZvzqtEwDp~v`jlTy zesyq?9uLi zDZlQs^Md^)DhDUbP!QMt{(PgX)yJwy%Vw$`Ucp$Dw9n2zWhR5ef!$R(N00TD-MlbO zDnUSkTiJ1b^Pj9U9jxA-{yeiS>wgH$Jngt~;lJ~Toig0G4|RPNyB^E9w$?)0s`*HU zwf*^d+a|gmT=~W|+0*sB>ziN2TUC~B?md6t3wMXWhKn<06AD(ywEEWvty;J+Z-$}! z+DE%J`BrbLe1uQwg4?A{!;a^p4IpWM!sxvFPNqYif@ zb8@OEI=S(th9oF@?RM+x6q$S6=8toqy{F#Hm{fxzodsN?lOD7GZ@$0TlskBGUXTVG zOI*(HWR@5ASZ9^q{%5!C?K92?^=#|5Y}ls47YguJQ{>uF6ifOi{A8m(KO)W`O zsL0L9E4HezRRWu9l~-&964qBz04piUwpEJo4N!2-FG^J~(KFFA&~>fIEHhHF<5I9G zN=dT{a&dziQIwKqtCUevQedU8UtV6WS8lAAUzDzIXlZGwZ(yWvWTab^lBQc+nOBln zp_^B%3^D>@hD&O3a#3bMNoIbY0?5q7r2NtnTO}nf1qB7D;T5?BzP@nd^NOLNker{Z zUy)d#Z>VRWpPQ?XtfRQZwX6icj^dEYf>iyW)Z+ZoqU2Q9vedj1Wn?2#lHvLbN{e#9 z-bqQ;Pt8fqP0cGQ);H8MM6uG{(>DOF0~7@5nYjgET@|?nC@M=b(-47$;v0|**gMD$ zsmLvWn~S0v=6A4Sa2Q#+oMvdI zn`Q|z%F@h2H_602RoBQMG1<_>D9td%%nWQ)O0tz(eo<~>iLFv*Zen_>enDP3SOOH_ zR*nIlwn|2N5Ql?B0&)^d((;RPZI!|^^HTE5ixG-LGILXdOA-LXn4X$fVyonynOgw%je-U=Ff~zKQ=X9u3Mm64 zLtO(4T?5k)BV#KA3oAoQC4GqZZ1h2S8s=LYeT?vcC_pL%?6?#lVnHr$c3d|4;F1Vb zDnSecl?t@P(0HJw6$%QYmXH*_qro*ATqK16Ns32P*JyB&6apkE9!*_T3ob51cPBM3 z#a5|Y$=+^HpY}@z1_rhyZ+90^kBEWcr}OT51_lPs0*}aI1_o|n5N2eUHAjMhfq}im z)7O>#DF+)Hhw$|^%`X@jSTa3b977@wzn$)%ArmTd++N#qPoqeh*F>Lt8O_OY& zOi)^?8+7AE>SN87i(*A{u6P#-M%&z&=OcJ9AYi+kiX;C`u1Q}4ZZ10U?THS@yF0rU zT-yF%e&vKizh#*oCzhGdo_ltg;6d;D4+-D)R(}4w@BRDpzjbZ4w12aYey5;+{;tQQ z2OJWO5!(%RwZ7oU);*UfKc)Mq;tZ=^d%o%O&OZGo%yjhpJJl?n9pXZ@#iGC6Tb^@x zEq%SzvOp%oY~jU#8E+f@oUBw^<|UE5V&BJ#ckzNVc287!=HL4-kM(bD>mGfU%7p?N zF-tEfxd?__V@$bz?`i7MQ(cEFNjc*={ z#^h*RkuvN(A>g`U*F@C|TU%L=zO`nYoL|@XAmxgL*MmRXJum&*=~t2~_kY5}X4a!; zoaCaM)`|u5^WNe(*3g-_*zQ)Vw|Cdo^|4LGr+wzneR^u8tZJK(Ms1Zjr~RCVCzWSC z%4F40t66waq$FB^apTLGYa63>m{&+REo4ZPO*{8gv-Xeh+xR!*|*>66C%FfJ1LN1 z{#wBONv)#T3R9l7+aBy_G7~6o(0zI1w3(U6=FPib>c)!1L><|GC1TH16-lPDt%j4UV3&@)v93Tlr85E3#6re<2bcx!Rze9q8;y}AMIA$r@gZO_mX$>ZiK#! zInT;$u~y9XX8W=5zyn!HcbO8_)CazE)wudRTynzB%@4|^yf}VMN@T+M{;=bP2frWu zbM1^)LS28=#MIvhQ`z_YzOl9`Np$sUuKVog@9$p~#du%*QT*aHOl7ATxN@H~zGGut zqj62kK$7F!fu!#Yd$)*Qa_AF%G((5+eT2hm<&O=oA`2MT9Qw?&)@;pMO+Eeh&Ngi~ z4n&0}+&Q}|;Zf(wDR1+x8Xp&HyrDHkd2hLydF!V=Qk;3#_w&p|E$&UQdQhRU)ZnAv zlyb)E#gEI+zjXL6+T!LsW3K1HwkGdci75l|!FaZ+Jwkp5!_`%X>VKJ>Tl`a}@s)Rb@i}AFlryS*<#xAZBLdFl9axwZT~R1^ zZMF4-yG&angMIdGI`hohpLaiZfz2P`&m4LBqPkh?i`X09K6oLf*SLM!gDO?qo!1Ip zc6tAMVzcjk|Gqx&_NQ-A)UEQ!?S6@K&lSg*JM;9q|UGmec*t==9`i;$sk$a}9 zZ0$ewqCnVSZ{=3|e*(oH`&fPU?LBqtwEOpbk6qvHy;bR~i%|cQ#3}gsK*5_ut2O5A z_9@s_vtinr`7V1>cYZ#Z{US|vn|oNNU9ZT!kC}H?S}r?(j*rzoAiC~h+N3SFrT#w7 zdNu8LZc4Vo!{Z|J=BroKHFmCyOp#u<-(Z*NdD(#AuIumA-nqjCDN`ey06$*;-(=u~X z6-p`#QWa7wGSe6sDsJ7K5jpA2jy{+FJG&`AvGe*-b;sB5=lODOPL6BY{(FtLtOL5v zcYol!FhBR_rB!wJ58XIlT3Z%0zdk*F=f6E~?$>Ewi#}wdd2FuJr<&ta=Ci8&=GQCD zd-Yc=d&Z^flPB0kA62N|H2MEFt?#SD_q{t5Z}b1C=1QCMvix;sX+L-U`&c<4e%^lO zUD=QFjhFv4{p^4K`On1r@7=cjFAghx{wBTLR@^7*o^S28qW2jV29>++a>+l9-*NqS zX3Z+*y~5M=?f*V7cIFn`K4-#VF55X9-j}wW6e_+Gxbv7r;S}4|p2^+DmF@YPY7ceJ zii-N7y{xlZ*#GhMs6%tN-+h_3!es_+RI$+EuRlgjA5>~+y8H7ys+Gq>}RLgcFUg^a6Z(ndpK}cpPz}yI{!7SvF{mX zbnI+B+S0;a!}wt#%X-5sjvE^v%|GWbbwMYyILpKx5vgZ1Mdt)BKI<#byFGWx51Y2l z1uaV^iCo`ulW(D3NmcHSvv_coor7F&M%FT>W!es)EtE(ZHs|2>_3&^GVy+t26sirK|= zUk|<(uz!zR_|@|Q2Vc0HNw3g()^Pl)4a0)PKhIpBP+aoN>hsL}W0^vcOJ@{^8T3Cq zxW{>l@V=Vwhl}lho_+c!{-1&Lqi%l=M z*)-pO{f2K3*!#b0tX@BtUoY>0PvfPs$udecVwOO`adX33>pOih8~uDsMPvV8ZSeOs?|By2x+ zbIa|QGrt{k?PlI_Uif+T&d(9+Vo!_eY_Fbp^;P7m+%=|4c5gjpT66i)@2?l$EIIjn z-kyK{>(Aw-y;{D+cet#1paX&--TyR}+^W9A-KKcShM zqia^*l=*t`MC|0d>;G02NklSEn8>~DO~D1Jn@jIrSzk82>-@ZBr74=yEM8YbFRl5# z#VN{$`PYT!Yd|Kwr{N5 zQyS!*&s}7fyrvsp@blKnqzA^682=cFuFE%W<$XLe`s{k0X$`l9&l`zGTP?lLE1GO- zKJP*Ucd$f4j?1y&Yo-qlNefwt*vQ>fnZGw!uhoR1Y_h40*zWclrKh@dt4^|ZBptB* zc+GImR=#6mi|3fSABtvdp^eozJXhmB|*Kwfy$r`#qPeBWkC^ zu03*-J=N=ZXqnyjFqN9!yS}?=WzT0-xy!qdyW#T8WImtuI~Y6!b1Y(|8_pgmILmEP z5YoXX&KR|K+Gd8=%vX%G(ykmj7n$i*cTg&2<rPa2~zD_|7Z%^twkB~lL9YRwlo&?C#*B^*;k~! z^>+J?tOH76JFm5#zkkAis?Hk)-3ZoLj@t_758nt?zge32wPI(6Y>=7SJ%t%LuYYdW z78J@#_AGgDo741I%f?N6dv&}IuqLk+Vc)W>evzpK`(-hQU2F^ zXO$kFe7OCErklqP*DK|W*2nHhZ+^UP+XVj_!OtvKVji&y*-iB-i;t@xS!@w(cg${U zYo}Y(GP?zGRm&|WFFl&~Vc+#>KF4P-3G|kH;wm6@`tDxov-9N)UEQx0_TP9_X1!wb z%I0#9Z zb8NEhbGpveaX9NGZcuY})n=RIUHD6Y?+D9%lSMOH=PxkNt4-w96!3q$W7$kw1<%7i z+#eN}P8_`U4tBTA~Y#U(i(HAvr8t-hP{W3}Q}gWd(UUA?zXbh4g_DLp@EPXA@e@~V?uuQPz zc<0WN@`z(1T~oUHjEXi}d|eip*J(Sib@un9gAq@j0aaRppG38>#nn%j1)St znj<@cqsivMb^X^uHM%~iQ&*Y(zP59@PB1+gawN@H0{r{x#uo%~$YoWvP>Vf$P) z$vEZIOP`M&oZ0GCzB;vX>P(l?YD15kEE{s0C2gv;!h7t}WE`4A4#0!9NAQB{OZ|IQ`|A%aPQc>HH_kO{Q*Nxw&P-#DI=jX`YAuoCGA!t};$> zPkza9qrvNRc;W4Op2rNvJKi$YdV8Er6xp_8M)~Bk8^2$kwLNgQVaR4iraUL^$ZeVm z5<8@R&Rp;Q@wlPmX*s2U295@n;c6iKk)6+KPU57FF$vEIDTaxOk>t)?|`9k=Pq`bZ#V=+YXZzLSvEbs)FF$12CS3bi7GT(>s=sLW+Y7G`{r&sy`l8K> zXVs%5TvWBz7G^_eff(q&7(ewtbtbx^Qyvg9-N zwTiR447bKah+K-u|xlU48HqVVE zM0Wn)MS}ZYJu5ody>rd7)&>9g_xU4o-V0bpy|r!W(D*g^y-$N2yRux3vJtOa)4lo(i-U9Rw*GpU z{=f1~UG+ak|9_jl9M$mfvYf4($lg_^!+iePljFf3Bg7ol1WLEdeR^_a>9k)QJ7O2` ztvX)py()ic<(B4#c{eLe3`@CX(pT+^39a|Jy1y&&Im6wW@M``YS9k2|j-*H)Ez zMJe4`aaT8^e$JXH-xHqp{(7N!cE9!c;`%Da^-h{AYTRCg$V{s9omMgLkXYE+`Jc_d z&ohg#o$x>)B5Q`b%DIG}YbX2kyNmHzGcTFz{=;Zb+}cTR_9Wg3dpGag1k1)1QDQru z7pl(`a1Y|((@f*>{v9q@p`sYFN$_U*+}YPXQL8%}cV3$OGs z=@PYj;=vW-GkweTYdSNL0AQ;hHm^V z>d0ciAb(6H?N#TOB(`~9OZ{s@$^+*;z56}=t5)S?M!Sg>eJ|Tw_c+>Y&U`uNd*|Ub zDw;>0A9>6cJK@K~*PBk~&*|plwlU603Hp2_F=^wCgGKC{CoL567j_p3U0kF1defoG z?#)pvH(s;-$?aU3t9rIH>TpLgC#Q;{lN)bpNP?o*Znv&Zk-5ih{y6v9d+N=MNi`_a zS->Sa=`s8N=KGsXxq~O?1!=Ic#O3@>W_fXsbyn%^e|Fp6KI42)&$e#Mw*9vl((FCg zZ{K=C@WOW9n3(ORmHa#IFtW`rIXOY*q`$X@NW*vIU&~}J)N_AXv$HsR?&B|vCoZg+ zmfz6qoUp~?=4-k4d}Zg}-k*2vPaT7QXVJ{C)bg1O47?JVArU1JzCKpT`MG+DDfvmM zdKI|^3?N`*Ur~^loSj;tkd&I9nP;o?e)oPQh0GLNrEpVU1K$GY)Qn7zs-o23D!-8A zs_bOT6eW8*E}IIgirj+S)RIJnirk#MVyg;UC9t_xdBs*BVSOb9u#%E&Tcrr!00rm# zqErPFJrg|xUDt}tG9x8BE(M#Slr*a#7dNO8MJZ{vN*N_31y=g{<>lpi<;HsXMd|v6 zmX?*7iAWdWaj57fXqxx$}cUkRZ`+o zP*8vxUXfei>kBtNuNWE%$@#hZ6^RA?&D!{jtmi)7<8U31H116`A3!xY`b6iZ9plq56bloWG|G&6G}B%?g@ic1pnl2buO zRpb`vWoD*WC8rvhTc)HL=$cs=C+eD*rJ3p`8kr{PCRtjfB_$acTO=l%A{pUdl$oBH zmzaa>Dv(hrAfu8KlZ}iG&2^2^3=DNm3``7lEey;}bq!1`Op{HFP0cM$Q@}>0BwM-V z7v(0F*eYe_CZ?zA7v!abB|rggBaeJCFO}lsgCKXc_p?=?wPp- zVBaWcKm$_~RegCzDk!83j0|-REOZS_LyU~A3@oe+O_lT^-m}pMp=z~ilP^knl5L7DA5<}yGmR2Yzj9Nlc_>KnGXmF7f0wgIOOn2-{PSLGev%aM12{pIRh_3c|y>MgGvWm?tJm=J8yrJ>EEYoAf&A0EKd1I#8G~Gr*Y~RfMy8qwn-_L#@ zZPB&($Na-qXAUp^KKV#JV^XUI&xN%O7CZ*M9MuPy3m2S!cJxx^p1K)zXE*&5W>hZ! z=gHD~Jp0PDyd=4`4_7fMINbcap^-N^BD|xWOXOUyVZ|~Znd3TjMW?FN7#Dw^ta6W! zvwp5}%-rP*y4Ig6;Oc8MP5Afb=3dT~8X+^gr&#tr_`>m2|JlXZ9Us%K39c5mxwAs( zk45KR$$)D!G!OW_4-fibwIM*qZsrYSk@kUk4d-A z8s1quAu&`&As{uZC5kKTz>;veEtR^G&YH4%H_ayV9zGQ=zCiF&s6*}IuA6Hft`=Ha zcAX)7sz9ZcMXze|j+EQ7{XQHPi4H%~TUSn1Imz+k&vccQe@mGCMSVBto{tDfYAHMX z`Aq!j#^i4){)~xV^>)pE++f)DV!c+LmcscgcGqCR$`2PLykZ|5h%gS(+3BSN&W5UL`r)T{+gS^ImgNHdfr)0XO*Tcy4H@JD*G+Z)&(-%$U9elplo7t9=kz!k4Lt7 zj{NpeLy!Cl<2z1a4z<>cKK#;f`_z3QidEC*z2t0L!DZ4hEbqTm-g(>{$=Dsvrt^EM z(XICh-*zs@otfG1W}NGB_O+T|*ZcVN!au3n^~RfG_kCES literal 0 HcmV?d00001 diff --git a/actors/breath_meter/model.inc.c b/actors/breath_meter/model.inc.c new file mode 100644 index 00000000..1e709e9c --- /dev/null +++ b/actors/breath_meter/model.inc.c @@ -0,0 +1,130 @@ +#ifdef BREATH_METER +// Breath Meter HUD + +// 0x030233E0 +ALIGNED8 static const Texture texture_breath_meter_left_side[] = { +#include "actors/breath_meter/breath_meter_left_side.rgba16.inc.c" +}; + +// 0x030243E0 +ALIGNED8 static const Texture texture_breath_meter_right_side[] = { +#include "actors/breath_meter/breath_meter_right_side.rgba16.inc.c" +}; + +// 0x030253E0 +ALIGNED8 static const Texture texture_breath_meter_full[] = { +#include "actors/breath_meter/breath_meter_full.rgba16.inc.c" +}; + +// 0x03025BE0 +ALIGNED8 static const Texture texture_breath_meter_seven_segments[] = { +#include "actors/breath_meter/breath_meter_seven_segments.rgba16.inc.c" +}; + +// 0x030263E0 +ALIGNED8 static const Texture texture_breath_meter_six_segments[] = { +#include "actors/breath_meter/breath_meter_six_segments.rgba16.inc.c" +}; + +// 0x03026BE0 +ALIGNED8 static const Texture texture_breath_meter_five_segments[] = { +#include "actors/breath_meter/breath_meter_five_segments.rgba16.inc.c" +}; + +// 0x030273E0 +ALIGNED8 static const Texture texture_breath_meter_four_segments[] = { +#include "actors/breath_meter/breath_meter_four_segments.rgba16.inc.c" +}; + +// 0x03027BE0 +ALIGNED8 static const Texture texture_breath_meter_three_segments[] = { +#include "actors/breath_meter/breath_meter_three_segments.rgba16.inc.c" +}; + +// 0x030283E0 +ALIGNED8 static const Texture texture_breath_meter_two_segments[] = { +#include "actors/breath_meter/breath_meter_two_segments.rgba16.inc.c" +}; + +// 0x03028BE0 +ALIGNED8 static const Texture texture_breath_meter_one_segments[] = { +#include "actors/breath_meter/breath_meter_one_segment.rgba16.inc.c" +}; + +// 0x030293E0 +const Texture *const breath_meter_segments_lut[] = { + texture_breath_meter_one_segments, + texture_breath_meter_two_segments, + texture_breath_meter_three_segments, + texture_breath_meter_four_segments, + texture_breath_meter_five_segments, + texture_breath_meter_six_segments, + texture_breath_meter_seven_segments, + texture_breath_meter_full, +}; + +// 0x03029400 +static const Vtx vertex_breath_meter_base[] = { + {{{ -32, -32, 0}, 0, { 0, 2016}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, -32, 0}, 0, { 992, 2016}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, 32, 0}, 0, { 992, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ -32, 32, 0}, 0, { 0, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, -32, 0}, 0, { 1, 2016}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 32, -32, 0}, 0, { 1024, 2016}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 32, 32, 0}, 0, { 1024, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 0, 32, 0}, 0, { 1, 0}, {0xff, 0xff, 0xff, 0xff}}}, +}; + +// 0x03029480 - 0x03029530 +const Gfx dl_breath_meter_base[] = { + gsDPPipeSync(), + gsSPClearGeometryMode(G_LIGHTING), + gsDPSetCombineMode(G_CC_DECALRGBA, G_CC_DECALRGBA), + gsDPSetRenderMode(G_RM_TEX_EDGE, G_RM_TEX_EDGE2), + gsDPSetTextureFilter(G_TF_POINT), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsSPVertex(vertex_breath_meter_base, 8, 0), + gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, (G_TX_WRAP | G_TX_NOMIRROR), G_TX_NOMASK, G_TX_NOLOD, (G_TX_WRAP | G_TX_NOMIRROR), G_TX_NOMASK, G_TX_NOLOD), + gsDPTileSync(), + gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 0, G_TX_RENDERTILE, 0, G_TX_CLAMP, 6, G_TX_NOLOD, G_TX_CLAMP, 5, G_TX_NOLOD), + gsDPSetTileSize(0, 0, 0, ((32 - 1) << G_TEXTURE_IMAGE_FRAC), ((64 - 1) << G_TEXTURE_IMAGE_FRAC)), + gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture_breath_meter_left_side), + gsDPLoadSync(), + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, 32 * 64 - 1, CALC_DXT(32, G_IM_SIZ_16b_BYTES)), + gsSP2Triangles( 0, 1, 2, 0x0, 0, 2, 3, 0x0), + gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture_breath_meter_right_side), + gsDPLoadSync(), + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, 32 * 64 - 1, CALC_DXT(32, G_IM_SIZ_16b_BYTES)), + gsSP2Triangles( 4, 5, 6, 0x0, 4, 6, 7, 0x0), + gsSPEndDisplayList(), +}; + +// 0x03029530 +static const Vtx vertex_breath_meter_health_segments[] = { + {{{ -16, -16, 0}, 0, { 0, 992}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 16, -16, 0}, 0, { 992, 992}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ 16, 16, 0}, 0, { 992, 0}, {0xff, 0xff, 0xff, 0xff}}}, + {{{ -16, 16, 0}, 0, { 0, 0}, {0xff, 0xff, 0xff, 0xff}}}, +}; + +// 0x03029570 - 0x030295A0 +const Gfx dl_breath_meter_health_segments_begin[] = { + gsDPPipeSync(), + gsSPVertex(vertex_breath_meter_health_segments, 4, 0), + gsDPTileSync(), + gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 0, G_TX_RENDERTILE, 0, G_TX_CLAMP, 5, G_TX_NOLOD, G_TX_CLAMP, 5, G_TX_NOLOD), + gsDPSetTileSize(0, 0, 0, ((32 - 1) << G_TEXTURE_IMAGE_FRAC), ((32 - 1) << G_TEXTURE_IMAGE_FRAC)), + gsSPEndDisplayList(), +}; + +// 0x030295A0 - 0x030295D8 +const Gfx dl_breath_meter_health_segments_end[] = { + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsSPSetGeometryMode(G_LIGHTING), + gsDPSetRenderMode(G_RM_OPA_SURF, G_RM_OPA_SURF2), + gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), + gsDPSetTextureFilter(G_TF_BILERP), + gsSPEndDisplayList(), +}; +#endif diff --git a/actors/common1.c b/actors/common1.c index 702fd215..b34bb61f 100644 --- a/actors/common1.c +++ b/actors/common1.c @@ -56,6 +56,9 @@ UNUSED static const u64 binid_11 = 11; UNUSED static const u64 binid_12 = 12; #include "power_meter/model.inc.c" +#ifdef BREATH_METER +#include "breath_meter/model.inc.c" +#endif UNUSED static const u64 binid_13 = 13; UNUSED static const u64 binid_14 = 14; diff --git a/actors/common1.h b/actors/common1.h index a6663a19..b9070c0a 100644 --- a/actors/common1.h +++ b/actors/common1.h @@ -195,6 +195,14 @@ extern const Gfx dl_power_meter_base[]; extern const Gfx dl_power_meter_health_segments_begin[]; extern const Gfx dl_power_meter_health_segments_end[]; +#ifdef BREATH_METER +// breath_meter +extern const Texture *const breath_meter_segments_lut[]; +extern const Gfx dl_breath_meter_base[]; +extern const Gfx dl_breath_meter_health_segments_begin[]; +extern const Gfx dl_breath_meter_health_segments_end[]; +#endif + // sand extern const Gfx sand_seg3_dl_0302BCD0[]; diff --git a/include/config.h b/include/config.h index 18422ac1..c6e5a01a 100644 --- a/include/config.h +++ b/include/config.h @@ -28,6 +28,8 @@ // -- GAME SETTINGS -- // Disable lives and hide the lives counter #define DISABLE_LIVES +// Air/Breath meter is separate from health meter when underwater +//#define BREATH_METER // Number of coins to spawn the "100 coin" star. If you remove the define altogether, then there won't be a 100 coin star at all. #define X_COIN_STAR 100 // Stars don't kick you out of the level diff --git a/include/types.h b/include/types.h index dcce4d0b..4037586a 100644 --- a/include/types.h +++ b/include/types.h @@ -377,6 +377,10 @@ struct MarioState /*0xBC*/ f32 peakHeight; /*0xC0*/ f32 quicksandDepth; /*0xC4*/ f32 windGravity; +#ifdef BREATH_METER + s16 breath; + u8 breathCounter; +#endif }; #endif // TYPES_H diff --git a/src/engine/math_util.c b/src/engine/math_util.c index d2a3b0c9..ec1e88cd 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -554,7 +554,6 @@ void vec3f_get_dist_and_angle(Vec3f from, Vec3f to, f32 *dist, s16 *pitch, s16 * register f32 x = to[0] - from[0]; register f32 y = to[1] - from[1]; register f32 z = to[2] - from[2]; - *dist = sqrtf(sqr(x) + sqr(y) + sqr(z)); *pitch = atan2s(sqrtf(sqr(x) + sqr(z)), y); *yaw = atan2s(z, x); @@ -570,6 +569,21 @@ void vec3f_set_dist_and_angle(Vec3f from, Vec3f to, f32 dist, s32 pitch, s32 yaw to[2] = from[2] + dist * coss(pitch) * coss(yaw); } +s32 approach_s16(s32 current, s32 target, s32 inc, s32 dec) { + s16 dist = (target - current); + if (dist >= 0) { // target >= current + current = ((dist > inc) ? (current + inc) : target); + } else { // target < current + current = ((dist < -dec) ? (current - dec) : target); + } + return current; +} + +Bool32 approach_s16_bool(s16 *current, s32 target, s32 inc, s32 dec) { + *current = approach_s16(*current, target, inc, dec); + return !(*current == target); +} + /** * Return the value 'current' after it tries to approach target, going up at * most 'inc' and going down at most 'dec'. @@ -584,6 +598,11 @@ s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec) { return current; } +Bool32 approach_s32_bool(s32 *current, s32 target, s32 inc, s32 dec) { + *current = approach_s32(*current, target, inc, dec); + return !(*current == target); +} + /** * Return the value 'current' after it tries to approach target, going up at * most 'inc' and going down at most 'dec'. @@ -598,6 +617,28 @@ f32 approach_f32(f32 current, f32 target, f32 inc, f32 dec) { return current; } +Bool32 approach_f32_bool(f32 *current, f32 target, f32 inc, f32 dec) { + *current = approach_f32(*current, target, inc, dec); + return !(*current == target); +} + +s32 approach_f32_signed(f32 *current, f32 target, f32 inc) { + s32 reachedTarget = FALSE; + *current += inc; + if (inc >= 0.0f) { + if (*current > target) { + *current = target; + reachedTarget = TRUE; + } + } else { + if (*current < target) { + *current = target; + reachedTarget = TRUE; + } + } + return reachedTarget; +} + /** * Similar to approach_s32, but converts to s16 and allows for overflow between 32767 and -32768 */ diff --git a/src/engine/math_util.h b/src/engine/math_util.h index b4224fa9..787e3ffd 100644 --- a/src/engine/math_util.h +++ b/src/engine/math_util.h @@ -374,8 +374,19 @@ void mtxf_rotate_xy(Mtx *mtx, s32 angle); void get_pos_from_transform_mtx(Vec3f dest, Mat4 objMtx, Mat4 camMtx); void vec3f_get_dist_and_angle(Vec3f from, Vec3f to, f32 *dist, s16 *pitch, s16 *yaw); void vec3f_set_dist_and_angle(Vec3f from, Vec3f to, f32 dist, s32 pitch, s32 yaw); +s32 approach_s16(s32 current, s32 target, s32 inc, s32 dec); s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec); f32 approach_f32(f32 current, f32 target, f32 inc, f32 dec); +Bool32 approach_s16_bool(s16 *current, s32 target, s32 inc, s32 dec); +Bool32 approach_s32_bool(s32 *current, s32 target, s32 inc, s32 dec); +Bool32 approach_f32_bool(f32 *current, f32 target, f32 inc, f32 dec); +#define approach_s16_symmetric(current, target, inc) approach_s16((current), (target), (inc), (inc)) +#define approach_s32_symmetric(current, target, inc) approach_s32((current), (target), (inc), (inc)) +#define approach_f32_symmetric(current, target, inc) approach_f32((current), (target), (inc), (inc)) +#define approach_s16_symmetric_bool(current, target, inc) approach_s16_bool((current), (target), (inc), (inc)) +#define approach_s32_symmetric_bool(current, target, inc) approach_s32_bool((current), (target), (inc), (inc)) +#define approach_f32_symmetric_bool(current, target, inc) approach_f32_bool((current), (target), (inc), (inc)) +s32 approach_f32_signed(f32 *current, f32 target, f32 inc); s32 approach_angle(s32 current, s32 target, s32 inc); s16 atan2s(f32 y, f32 x); f32 atan2f(f32 a, f32 b); diff --git a/src/game/behaviors/recovery_heart.inc.c b/src/game/behaviors/recovery_heart.inc.c index 6bebfaa3..32f1de05 100644 --- a/src/game/behaviors/recovery_heart.inc.c +++ b/src/game/behaviors/recovery_heart.inc.c @@ -30,7 +30,10 @@ void bhv_recovery_heart_loop(void) { } if ((o->oSpinningHeartTotalSpin += o->oAngleVelYaw) >= 0x10000) { - gMarioStates[0].healCounter += 4; + gMarioState->healCounter += 4; +#ifdef BREATH_METER + gMarioState->breathCounter += 4; +#endif o->oSpinningHeartTotalSpin -= 0x10000; } diff --git a/src/game/camera.c b/src/game/camera.c index 7d71d17e..ed046c7d 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -3793,42 +3793,21 @@ s32 collide_with_walls(Vec3f pos, f32 offsetY, f32 radius) { * Compare a vector to a position, return TRUE if they match. */ s32 vec3f_compare(Vec3f pos, f32 posX, f32 posY, f32 posZ) { - s32 equal = FALSE; - - if (pos[0] == posX && pos[1] == posY && pos[2] == posZ) { - equal = TRUE; - } - return equal; + return (pos[0] == posX && pos[1] == posY && pos[2] == posZ); } -s32 clamp_pitch(Vec3f from, Vec3f to, s16 maxPitch, s16 minPitch) { - s32 outOfRange = 0; - s16 pitch; - s16 yaw; +void clamp_pitch(Vec3f from, Vec3f to, s16 maxPitch, s16 minPitch) { + s16 pitch, yaw; f32 dist; - vec3f_get_dist_and_angle(from, to, &dist, &pitch, &yaw); - if (pitch > maxPitch) { - pitch = maxPitch; - outOfRange++; - } - if (pitch < minPitch) { - pitch = minPitch; - outOfRange++; - } + pitch = CLAMP(pitch, minPitch, maxPitch); vec3f_set_dist_and_angle(from, to, dist, pitch, yaw); - return outOfRange; } s32 is_within_100_units_of_mario(f32 posX, f32 posY, f32 posZ) { - s32 isCloseToMario = 0; Vec3f pos; - vec3f_set(pos, posX, posY, posZ); - if (calc_abs_dist(sMarioCamState->pos, pos) < 100.f) { - isCloseToMario = 1; - } - return isCloseToMario; + return (calc_abs_dist(sMarioCamState->pos, pos) < 100.f); } s32 set_or_approach_f32_asymptotic(f32 *dst, f32 goal, f32 scale) { @@ -3837,11 +3816,7 @@ s32 set_or_approach_f32_asymptotic(f32 *dst, f32 goal, f32 scale) { } else { *dst = goal; } - if (*dst == goal) { - return FALSE; - } else { - return TRUE; - } + return (*dst != goal); } /** @@ -3854,11 +3829,7 @@ s32 approach_f32_asymptotic_bool(f32 *current, f32 target, f32 multiplier) { multiplier = 1.f; } *current = *current + (target - *current) * multiplier; - if (*current == target) { - return FALSE; - } else { - return TRUE; - } + return (*current != target); } /** @@ -3885,11 +3856,7 @@ s32 approach_s16_asymptotic_bool(s16 *current, s16 target, s16 divisor) { temp += target; *current = temp; } - if (*current == target) { - return FALSE; - } else { - return TRUE; - } + return (*current != target); } /** diff --git a/src/game/camera.h b/src/game/camera.h index da826ee4..6b3518f5 100644 --- a/src/game/camera.h +++ b/src/game/camera.h @@ -696,7 +696,7 @@ void shake_camera_handheld(Vec3f pos, Vec3f focus); s32 find_c_buttons_pressed(u16 currentState, u16 buttonsPressed, u16 buttonsDown); s32 update_camera_hud_status(struct Camera *c); s32 collide_with_walls(Vec3f pos, f32 offsetY, f32 radius); -s32 clamp_pitch(Vec3f from, Vec3f to, s16 maxPitch, s16 minPitch); +void clamp_pitch(Vec3f from, Vec3f to, s16 maxPitch, s16 minPitch); s32 is_within_100_units_of_mario(f32 posX, f32 posY, f32 posZ); s32 set_or_approach_f32_asymptotic(f32 *dst, f32 goal, f32 scale); s32 approach_f32_asymptotic_bool(f32 *current, f32 target, f32 multiplier); diff --git a/src/game/hud.c b/src/game/hud.c index 4f99deb2..71ebb376 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -14,6 +14,7 @@ #include "save_file.h" #include "print.h" #include "engine/surface_load.h" +#include "engine/math_util.h" #include "puppycam2.h" #include "puppyprint.h" @@ -25,6 +26,23 @@ * cannon reticle, and the unused keys. **/ +#define HUD_POWER_METER_X 140 +#define HUD_POWER_METER_EMPHASIZED_Y 166 +#define HUD_POWER_METER_Y 200 +#define HUD_POWER_METER_HIDDEN_Y 300 + +#ifdef BREATH_METER +// #ifdef DISABLE_LIVES +// #define HUD_BREATH_METER_X 64 +// #define HUD_BREATH_METER_Y 200 +// #define HUD_BREATH_METER_HIDDEN_Y 300 +// #else +#define HUD_BREATH_METER_X 40 +#define HUD_BREATH_METER_Y 32 +#define HUD_BREATH_METER_HIDDEN_Y -20 +// #endif +#endif + // ------------- FPS COUNTER --------------- // To use it, call print_fps(x,y); every frame. #define FRAMETIME_COUNT 30 @@ -41,9 +59,9 @@ f32 calculate_and_update_fps() { frameTimes[curFrameTimeIndex] = newTime; curFrameTimeIndex++; - if (curFrameTimeIndex >= FRAMETIME_COUNT) + if (curFrameTimeIndex >= FRAMETIME_COUNT) { curFrameTimeIndex = 0; - + } return ((f32)FRAMETIME_COUNT * 1000000.0f) / (s32)OS_CYCLES_TO_USEC(newTime - oldTime); } @@ -66,7 +84,6 @@ struct PowerMeterHUD { s8 animation; s16 x; s16 y; - f32 unused; }; struct CameraHUD { @@ -79,9 +96,8 @@ static s16 sPowerMeterStoredHealth; static struct PowerMeterHUD sPowerMeterHUD = { POWER_METER_HIDDEN, - 140, - 166, - 1.0, + HUD_POWER_METER_X, + HUD_POWER_METER_HIDDEN_Y, }; // Power Meter timer that keeps counting when it's visible. @@ -89,12 +105,22 @@ static struct PowerMeterHUD sPowerMeterHUD = { // when the power meter is hidden. s32 sPowerMeterVisibleTimer = 0; +#ifdef BREATH_METER +static s16 sBreathMeterStoredValue; +static struct PowerMeterHUD sBreathMeterHUD = { + BREATH_METER_HIDDEN, + HUD_BREATH_METER_X, + HUD_BREATH_METER_HIDDEN_Y, +}; +s32 sBreathMeterVisibleTimer = 0; +#endif + static struct CameraHUD sCameraHUD = { CAM_STATUS_NONE }; /** * Renders a rgba16 16x16 glyph texture from a table list. */ -void render_hud_tex_lut(s32 x, s32 y, u8 *texture) { +void render_hud_tex_lut(s32 x, s32 y, Texture *texture) { gDPPipeSync(gDisplayListHead++); gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture); gSPDisplayList(gDisplayListHead++, &dl_hud_img_load_tex_block); @@ -105,17 +131,17 @@ void render_hud_tex_lut(s32 x, s32 y, u8 *texture) { /** * Renders a rgba16 8x8 glyph texture from a table list. */ -void render_hud_small_tex_lut(s32 x, s32 y, u8 *texture) { - gDPSetTile(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, - G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD); - gDPTileSync(gDisplayListHead++); - gDPSetTile(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 2, 0, G_TX_RENDERTILE, 0, - G_TX_CLAMP, 3, G_TX_NOLOD, G_TX_CLAMP, 3, G_TX_NOLOD); - gDPSetTileSize(gDisplayListHead++, G_TX_RENDERTILE, 0, 0, (8 - 1) << G_TEXTURE_IMAGE_FRAC, (8 - 1) << G_TEXTURE_IMAGE_FRAC); - gDPPipeSync(gDisplayListHead++); - gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture); - gDPLoadSync(gDisplayListHead++); - gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_16b_BYTES)); +void render_hud_small_tex_lut(s32 x, s32 y, Texture *texture) { + gDPSetTile( gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD); + gDPTileSync( gDisplayListHead++); + gDPSetTile( gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 2, 0, G_TX_RENDERTILE, 0, + G_TX_CLAMP, 3, G_TX_NOLOD, G_TX_CLAMP, 3, G_TX_NOLOD); + gDPSetTileSize( gDisplayListHead++, G_TX_RENDERTILE, 0, 0, (8 - 1) << G_TEXTURE_IMAGE_FRAC, (8 - 1) << G_TEXTURE_IMAGE_FRAC); + gDPPipeSync( gDisplayListHead++); + gDPSetTextureImage( gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, texture); + gDPLoadSync( gDisplayListHead++); + gDPLoadBlock( gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_16b_BYTES)); gSPTextureRectangle(gDisplayListHead++, x << 2, y << 2, (x + 7) << 2, (y + 7) << 2, G_TX_RENDERTILE, 0, 0, 4 << 10, 1 << 10); } @@ -124,17 +150,14 @@ void render_hud_small_tex_lut(s32 x, s32 y, u8 *texture) { * Renders power meter health segment texture using a table list. */ void render_power_meter_health_segment(s16 numHealthWedges) { - u8 *(*healthLUT)[]; - + Texture *(*healthLUT)[]; healthLUT = segmented_to_virtual(&power_meter_health_segments_lut); - - gDPPipeSync(gDisplayListHead++); - gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, - (*healthLUT)[numHealthWedges - 1]); - gDPLoadSync(gDisplayListHead++); - gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 32 * 32 - 1, CALC_DXT(32, G_IM_SIZ_16b_BYTES)); - gSP1Triangle(gDisplayListHead++, 0, 1, 2, 0); - gSP1Triangle(gDisplayListHead++, 0, 2, 3, 0); + gDPPipeSync( gDisplayListHead++); + gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, (*healthLUT)[numHealthWedges - 1]); + gDPLoadSync( gDisplayListHead++); + gDPLoadBlock( gDisplayListHead++, G_TX_LOADTILE, 0, 0, 32 * 32 - 1, CALC_DXT(32, G_IM_SIZ_16b_BYTES)); + gSP1Triangle( gDisplayListHead++, 0, 1, 2, 0); + gSP1Triangle( gDisplayListHead++, 0, 2, 3, 0); } /** @@ -142,14 +165,8 @@ void render_power_meter_health_segment(s16 numHealthWedges) { * That includes the "POWER" base and the colored health segment textures. */ void render_dl_power_meter(s16 numHealthWedges) { - Mtx *mtx; - - mtx = alloc_display_list(sizeof(Mtx)); - - if (mtx == NULL) { - return; - } - + Mtx *mtx = alloc_display_list(sizeof(Mtx)); + if (mtx == NULL) return; guTranslate(mtx, (f32) sPowerMeterHUD.x, (f32) sPowerMeterHUD.y, 0); gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx++), @@ -170,8 +187,7 @@ void render_dl_power_meter(s16 numHealthWedges) { * Checks its timer to later change into deemphasizing mode. */ void animate_power_meter_emphasized(void) { - s16 hudDisplayFlags; - hudDisplayFlags = gHudDisplay.flags; + s16 hudDisplayFlags = gHudDisplay.flags; if (!(hudDisplayFlags & HUD_DISPLAY_FLAG_EMPHASIZE_POWER)) { if (sPowerMeterVisibleTimer == 45.0) { @@ -188,23 +204,12 @@ void animate_power_meter_emphasized(void) { */ static void animate_power_meter_deemphasizing(void) { s16 speed = 5; - - if (sPowerMeterHUD.y >= 181) { - speed = 3; - } - - if (sPowerMeterHUD.y >= 191) { - speed = 2; - } - - if (sPowerMeterHUD.y >= 196) { - speed = 1; - } - + if (sPowerMeterHUD.y > (HUD_POWER_METER_Y - 20)) speed = 3; + if (sPowerMeterHUD.y > (HUD_POWER_METER_Y - 10)) speed = 2; + if (sPowerMeterHUD.y > (HUD_POWER_METER_Y - 5)) speed = 1; sPowerMeterHUD.y += speed; - - if (sPowerMeterHUD.y >= 201) { - sPowerMeterHUD.y = 200; + if (sPowerMeterHUD.y > HUD_POWER_METER_Y) { + sPowerMeterHUD.y = HUD_POWER_METER_Y; sPowerMeterHUD.animation = POWER_METER_VISIBLE; } } @@ -215,7 +220,7 @@ static void animate_power_meter_deemphasizing(void) { */ static void animate_power_meter_hiding(void) { sPowerMeterHUD.y += 20; - if (sPowerMeterHUD.y >= 301) { + if (sPowerMeterHUD.y > HUD_POWER_METER_HIDDEN_Y) { sPowerMeterHUD.animation = POWER_METER_HIDDEN; sPowerMeterVisibleTimer = 0; } @@ -228,31 +233,29 @@ void handle_power_meter_actions(s16 numHealthWedges) { // Show power meter if health is not full, less than 8 if (numHealthWedges < 8 && sPowerMeterStoredHealth == 8 && sPowerMeterHUD.animation == POWER_METER_HIDDEN) { sPowerMeterHUD.animation = POWER_METER_EMPHASIZED; - sPowerMeterHUD.y = 166; + sPowerMeterHUD.y = HUD_POWER_METER_EMPHASIZED_Y; } - // Show power meter if health is full, has 8 if (numHealthWedges == 8 && sPowerMeterStoredHealth == 7) { sPowerMeterVisibleTimer = 0; } - // After health is full, hide power meter if (numHealthWedges == 8 && sPowerMeterVisibleTimer > 45.0) { sPowerMeterHUD.animation = POWER_METER_HIDING; } - // Update to match health value sPowerMeterStoredHealth = numHealthWedges; - +#ifndef BREATH_METER // If Mario is swimming, keep power meter visible if (gPlayerCameraState->action & ACT_FLAG_SWIMMING) { if (sPowerMeterHUD.animation == POWER_METER_HIDDEN || sPowerMeterHUD.animation == POWER_METER_EMPHASIZED) { sPowerMeterHUD.animation = POWER_METER_DEEMPHASIZING; - sPowerMeterHUD.y = 166; + sPowerMeterHUD.y = HUD_POWER_METER_EMPHASIZED_Y; } sPowerMeterVisibleTimer = 0; } +#endif } /** @@ -262,40 +265,114 @@ void handle_power_meter_actions(s16 numHealthWedges) { */ void render_hud_power_meter(void) { s16 shownHealthWedges = gHudDisplay.wedges; - - if (sPowerMeterHUD.animation != POWER_METER_HIDING) { - handle_power_meter_actions(shownHealthWedges); - } - - if (sPowerMeterHUD.animation == POWER_METER_HIDDEN) { - return; - } - + if (sPowerMeterHUD.animation != POWER_METER_HIDING) handle_power_meter_actions(shownHealthWedges); + if (sPowerMeterHUD.animation == POWER_METER_HIDDEN) return; switch (sPowerMeterHUD.animation) { - case POWER_METER_EMPHASIZED: - animate_power_meter_emphasized(); - break; - case POWER_METER_DEEMPHASIZING: - animate_power_meter_deemphasizing(); - break; - case POWER_METER_HIDING: - animate_power_meter_hiding(); - break; - default: - break; + case POWER_METER_EMPHASIZED: animate_power_meter_emphasized(); break; + case POWER_METER_DEEMPHASIZING: animate_power_meter_deemphasizing(); break; + case POWER_METER_HIDING: animate_power_meter_hiding(); break; + default: break; } - render_dl_power_meter(shownHealthWedges); - - sPowerMeterVisibleTimer += 1; + sPowerMeterVisibleTimer++; } -#ifdef VERSION_JP -#define HUD_TOP_Y 210 -#else -#define HUD_TOP_Y 209 +#ifdef BREATH_METER +/** + * Renders breath meter health segment texture using a table list. + */ +void render_breath_meter_segment(s16 numBreathWedges) { + Texture *(*breathLUT)[]; + breathLUT = segmented_to_virtual(&breath_meter_segments_lut); + gDPPipeSync( gDisplayListHead++); + gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, (*breathLUT)[numBreathWedges - 1]); + gDPLoadSync( gDisplayListHead++); + gDPLoadBlock( gDisplayListHead++, G_TX_LOADTILE, 0, 0, ((32 * 32) - 1), CALC_DXT(32, G_IM_SIZ_16b_BYTES)); + gSP1Triangle( gDisplayListHead++, 0, 1, 2, 0); + gSP1Triangle( gDisplayListHead++, 0, 2, 3, 0); +} + +/** + * Renders breath meter display lists. + * That includes the base and the colored segment textures. + */ +void render_dl_breath_meter(s16 numBreathWedges) { + Mtx *mtx = alloc_display_list(sizeof(Mtx)); + if (mtx == NULL) return; + guTranslate(mtx, ((f32) sBreathMeterHUD.x), ((f32) sBreathMeterHUD.y), 0); + gSPMatrix( gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx++), + (G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH)); + gSPDisplayList( gDisplayListHead++, &dl_breath_meter_base); + if (numBreathWedges != 0) { + gSPDisplayList(gDisplayListHead++, &dl_breath_meter_health_segments_begin); + render_breath_meter_segment(numBreathWedges); + gSPDisplayList(gDisplayListHead++, &dl_breath_meter_health_segments_end); + } + gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); +} + +/** + * Breath meter animation called after emphasized mode. + * Moves breath meter y pos speed until it's visible. + */ +static void animate_breath_meter_sliding_in(void) { + approach_s16_symmetric_bool(&sBreathMeterHUD.y, HUD_BREATH_METER_Y, 5); + if (sBreathMeterHUD.y == HUD_BREATH_METER_Y) { + sBreathMeterHUD.animation = BREATH_METER_VISIBLE; + } +} + +/** + * Breath meter animation called when there's 8 health segments. + * Moves breath meter y pos quickly until it's hidden. + */ +static void animate_breath_meter_sliding_out(void) { + approach_s16_symmetric_bool(&sBreathMeterHUD.y, HUD_BREATH_METER_HIDDEN_Y, 20); + if (sBreathMeterHUD.y == HUD_BREATH_METER_HIDDEN_Y) { + sBreathMeterHUD.animation = BREATH_METER_HIDDEN; + } +} + +/** + * Handles breath meter actions depending of the health segments values. + */ +void handle_breath_meter_actions(s16 numBreathWedges) { + // Show breath meter if health is not full, less than 8 + if ((numBreathWedges < 8) && (sBreathMeterStoredValue == 8) && sBreathMeterHUD.animation == BREATH_METER_HIDDEN) { + sBreathMeterHUD.animation = BREATH_METER_SHOWING; + // sBreathMeterHUD.y = HUD_BREATH_METER_Y; + } + // Show breath meter if breath is full, has 8 + if ((numBreathWedges == 8) && (sBreathMeterStoredValue == 7)) sBreathMeterVisibleTimer = 0; + // After breath is full, hide breath meter + if ((numBreathWedges == 8) && (sBreathMeterVisibleTimer > 45)) sBreathMeterHUD.animation = BREATH_METER_HIDING; + // Update to match breath value + sBreathMeterStoredValue = numBreathWedges; + // If Mario is swimming, keep breath meter visible + if (gPlayerCameraState->action & ACT_FLAG_SWIMMING) { + if (sBreathMeterHUD.animation == BREATH_METER_HIDDEN) { + sBreathMeterHUD.animation = BREATH_METER_SHOWING; + } + sBreathMeterVisibleTimer = 0; + } +} + +void render_hud_breath_meter(void) { + s16 shownBreathAmount = gHudDisplay.breath; + if (sBreathMeterHUD.animation != BREATH_METER_HIDING) handle_breath_meter_actions(shownBreathAmount); + if (sBreathMeterHUD.animation == BREATH_METER_HIDDEN) return; + switch (sBreathMeterHUD.animation) { + case BREATH_METER_SHOWING: animate_breath_meter_sliding_in(); break; + case BREATH_METER_HIDING: animate_breath_meter_sliding_out(); break; + default: break; + } + render_dl_breath_meter(shownBreathAmount); + sBreathMeterVisibleTimer++; +} #endif +#define HUD_TOP_Y 209 + /** * Renders the amount of lives Mario has. */ @@ -326,31 +403,17 @@ void render_hud_coins(void) { print_text_fmt_int(198, HUD_TOP_Y, "%d", gHudDisplay.coins); } -#ifdef VERSION_JP -#define HUD_STARS_X 73 -#else #define HUD_STARS_X 78 -#endif /** * Renders the amount of stars collected. * Disables "X" glyph when Mario has 100 stars or more. */ void render_hud_stars(void) { - s8 showX = 0; - - if (gHudFlash == 1 && gGlobalTimer & 0x08) { - return; - } - - if (gHudDisplay.stars < 100) { - showX = 1; - } - + if ((gHudFlash == 1) && (gGlobalTimer & 0x08)) return; + s8 showX = (gHudDisplay.stars < 100); print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X), HUD_TOP_Y, "-"); // 'Star' glyph - if (showX == 1) { - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X) + 16, HUD_TOP_Y, "*"); // 'X' glyph - } + if (showX) print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X) + 16, HUD_TOP_Y, "*"); // 'X' glyph print_text_fmt_int((showX * 14) + GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X - 16), HUD_TOP_Y, "%d", gHudDisplay.stars); } @@ -361,7 +424,6 @@ void render_hud_stars(void) { */ void render_hud_keys(void) { s16 i; - for (i = 0; i < gHudDisplay.keys; i++) { print_text((i * 16) + 220, 142, "/"); // unused glyph - beta key } @@ -371,31 +433,20 @@ void render_hud_keys(void) { * Renders the timer when Mario start sliding in PSS. */ void render_hud_timer(void) { - u8 *(*hudLUT)[58]; - u16 timerValFrames; - u16 timerMins; - u16 timerSecs; - u16 timerFracSecs; - + Texture *(*hudLUT)[58]; hudLUT = segmented_to_virtual(&main_hud_lut); - timerValFrames = gHudDisplay.timer; + u16 timerValFrames = gHudDisplay.timer; #ifdef VERSION_EU switch (eu_get_language()) { - case LANGUAGE_ENGLISH: - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "TIME"); - break; - case LANGUAGE_FRENCH: - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(155), 185, "TEMPS"); - break; - case LANGUAGE_GERMAN: - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "ZEIT"); - break; + case LANGUAGE_ENGLISH: print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "TIME"); break; + case LANGUAGE_FRENCH: print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(155), 185, "TEMPS"); break; + case LANGUAGE_GERMAN: print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "ZEIT"); break; } #endif - timerMins = timerValFrames / (30 * 60); - timerSecs = (timerValFrames - (timerMins * 1800)) / 30; + u16 timerMins = timerValFrames / (30 * 60); + u16 timerSecs = (timerValFrames - (timerMins * 1800)) / 30; - timerFracSecs = ((timerValFrames - (timerMins * 1800) - (timerSecs * 30)) & 0xFFFF) / 3; + u16 timerFracSecs = ((timerValFrames - (timerMins * 1800) - (timerSecs * 30)) & 0xFFFF) / 3; #ifndef VERSION_EU print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "TIME"); #endif @@ -421,13 +472,10 @@ void set_hud_camera_status(s16 status) { * the camera status called, a defined glyph is rendered. */ void render_hud_camera_status(void) { - u8 *(*cameraLUT)[6]; - s32 x; - s32 y; - + Texture *(*cameraLUT)[6]; cameraLUT = segmented_to_virtual(&main_hud_camera_lut); - x = GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(54); - y = 205; + s32 x = GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(54); + s32 y = 205; if (sCameraHUD.status == CAM_STATUS_NONE) { return; @@ -453,7 +501,7 @@ void render_hud_camera_status(void) { render_hud_small_tex_lut(x + 4, y + 16, (*cameraLUT)[GLYPH_CAM_ARROW_DOWN]); break; case CAM_STATUS_C_UP: - render_hud_small_tex_lut(x + 4, y - 8, (*cameraLUT)[GLYPH_CAM_ARROW_UP]); + render_hud_small_tex_lut(x + 4, y - 8, (*cameraLUT)[GLYPH_CAM_ARROW_UP]); break; } @@ -476,14 +524,16 @@ void render_hud(void) { sPowerMeterHUD.animation = POWER_METER_HIDDEN; sPowerMeterStoredHealth = 8; sPowerMeterVisibleTimer = 0; +#ifdef BREATH_METER + sBreathMeterHUD.animation = BREATH_METER_HIDDEN; + sBreathMeterStoredValue = 8; + sBreathMeterVisibleTimer = 0; +#endif } else { #ifdef VERSION_EU // basically create_dl_ortho_matrix but guOrtho screen width is different - mtx = alloc_display_list(sizeof(*mtx)); - if (mtx == NULL) { - return; - } + if (mtx == NULL) return; create_dl_identity_matrix(); guOrtho(mtx, -16.0f, SCREEN_WIDTH + 16, 0, SCREEN_HEIGHT, -10.0f, 10.0f, 1.0f); gSPPerspNormalize(gDisplayListHead++, 0xFFFF); @@ -492,7 +542,6 @@ void render_hud(void) { #else create_dl_ortho_matrix(); #endif - if (gCurrentArea != NULL && gCurrentArea->camera->mode == CAMERA_MODE_INSIDE_CANNON) { render_hud_cannon_reticle(); } @@ -501,38 +550,27 @@ void render_hud(void) { render_hud_mario_lives(); } #endif - if (hudDisplayFlags & HUD_DISPLAY_FLAG_COIN_COUNT) { - render_hud_coins(); - } - - if (hudDisplayFlags & HUD_DISPLAY_FLAG_STAR_COUNT) { - render_hud_stars(); - } - - if (hudDisplayFlags & HUD_DISPLAY_FLAG_KEYS) { - render_hud_keys(); - } + if (hudDisplayFlags & HUD_DISPLAY_FLAG_COIN_COUNT ) render_hud_coins(); + if (hudDisplayFlags & HUD_DISPLAY_FLAG_STAR_COUNT ) render_hud_stars(); + if (hudDisplayFlags & HUD_DISPLAY_FLAG_KEYS ) render_hud_keys(); +#ifdef BREATH_METER + if (hudDisplayFlags & HUD_DISPLAY_FLAG_BREATH_METER) render_hud_breath_meter(); +#endif if (hudDisplayFlags & HUD_DISPLAY_FLAG_CAMERA_AND_POWER) { render_hud_power_meter(); - #ifdef PUPPYCAM - if (!gPuppyCam.enabled) - #endif +#ifdef PUPPYCAM + if (!gPuppyCam.enabled) { +#endif render_hud_camera_status(); +#ifdef PUPPYCAM + } +#endif } + if (hudDisplayFlags & HUD_DISPLAY_FLAG_TIMER) render_hud_timer(); - if (hudDisplayFlags & HUD_DISPLAY_FLAG_TIMER) { - render_hud_timer(); - } - - if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_SURFACES) - { - print_text(10, 40, "SURFACE POOL FULL"); - } - if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_NODES) - { - print_text(10, 60, "SURFACE NODE POOL FULL"); - } + if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_SURFACES) print_text(10, 40, "SURFACE POOL FULL"); + if (gSurfacePoolError & NOT_ENOUGH_ROOM_FOR_NODES ) print_text(10, 60, "SURFACE NODE POOL FULL"); #ifdef CUSTOM_DEBUG if (gCustomDebugMode) { render_debug_mode(); diff --git a/src/game/hud.h b/src/game/hud.h index bfa44fa5..11586693 100644 --- a/src/game/hud.h +++ b/src/game/hud.h @@ -12,6 +12,15 @@ enum PowerMeterAnimation { POWER_METER_VISIBLE }; +#ifdef BREATH_METER +enum AirMeterAnimation { + BREATH_METER_HIDDEN, + BREATH_METER_SHOWING, + BREATH_METER_HIDING, + BREATH_METER_VISIBLE +}; +#endif + enum CameraHUDLut { GLYPH_CAM_CAMERA, GLYPH_CAM_MARIO_HEAD, diff --git a/src/game/interaction.c b/src/game/interaction.c index cf82c5c5..8dfef56a 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -736,13 +736,15 @@ void reset_mario_pitch(struct MarioState *m) { } } -u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { - m->numCoins += o->oDamageOrCoinValue; - m->healCounter += 4 * o->oDamageOrCoinValue; - - o->oInteractStatus = INT_STATUS_INTERACTED; +u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object *obj) { + m->numCoins += obj->oDamageOrCoinValue; + m->healCounter += 4 * obj->oDamageOrCoinValue; +#ifdef BREATH_METER + m->breathCounter += (4 * obj->oDamageOrCoinValue); +#endif + obj->oInteractStatus = INT_STATUS_INTERACTED; #ifdef X_COIN_STAR - if (COURSE_IS_MAIN_COURSE(gCurrCourseNum) && m->numCoins - o->oDamageOrCoinValue < X_COIN_STAR + if (COURSE_IS_MAIN_COURSE(gCurrCourseNum) && m->numCoins - obj->oDamageOrCoinValue < X_COIN_STAR && m->numCoins >= X_COIN_STAR) { bhv_spawn_star_no_level_exit(6); } @@ -752,13 +754,16 @@ u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object * queue_rumble_data(5, 80); } #endif - return FALSE; } -u32 interact_water_ring(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { - m->healCounter += 4 * o->oDamageOrCoinValue; - o->oInteractStatus = INT_STATUS_INTERACTED; +u32 interact_water_ring(struct MarioState *m, UNUSED u32 interactType, struct Object *obj) { +#ifdef BREATH_METER + m->breathCounter += (4 * obj->oDamageOrCoinValue); +#else + m->healCounter += (4 * obj->oDamageOrCoinValue); +#endif + obj->oInteractStatus = INT_STATUS_INTERACTED; return FALSE; } @@ -781,6 +786,9 @@ u32 interact_star_or_key(struct MarioState *m, UNUSED u32 interactType, struct O if (!noExit) { m->hurtCounter = 0; m->healCounter = 0; +#ifdef BREATH_METER + m->breathCounter = 0; +#endif if (m->capTimer > 1) { m->capTimer = 1; } diff --git a/src/game/level_update.c b/src/game/level_update.c index 840d65de..895868c6 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -904,8 +904,10 @@ void initiate_delayed_warp(void) { void update_hud_values(void) { if (gCurrCreditsEntry == NULL) { - s16 numHealthWedges = gMarioState->health > 0 ? gMarioState->health >> 8 : 0; - + s16 numHealthWedges = ((gMarioState->health > 0) ? (gMarioState->health >> 8) : 0); +#ifdef BREATH_METER + s16 numBreathWedges = ((gMarioState->breath > 0) ? (gMarioState->breath >> 8) : 0); +#endif if (gCurrCourseNum >= COURSE_MIN) { gHudDisplay.flags |= HUD_DISPLAY_FLAG_COIN_COUNT; } else { @@ -946,12 +948,19 @@ void update_hud_values(void) { play_sound(SOUND_MENU_POWER_METER, gGlobalSoundSource); } gHudDisplay.wedges = numHealthWedges; - if (gMarioState->hurtCounter > 0) { gHudDisplay.flags |= HUD_DISPLAY_FLAG_EMPHASIZE_POWER; } else { gHudDisplay.flags &= ~HUD_DISPLAY_FLAG_EMPHASIZE_POWER; } +#ifdef BREATH_METER + gHudDisplay.breath = numBreathWedges; + if (gMarioState->breath > 0) { + gHudDisplay.flags |= HUD_DISPLAY_FLAG_BREATH_METER; + } else { + gHudDisplay.flags &= ~HUD_DISPLAY_FLAG_BREATH_METER; + } +#endif } } diff --git a/src/game/level_update.h b/src/game/level_update.h index a8bd0e3c..440f2776 100644 --- a/src/game/level_update.h +++ b/src/game/level_update.h @@ -107,6 +107,9 @@ struct HudDisplay { /*0x08*/ s16 keys; /*0x0A*/ s16 flags; /*0x0C*/ u16 timer; +#ifdef BREATH_METER + u16 breath; +#endif }; extern struct HudDisplay gHudDisplay; @@ -120,6 +123,9 @@ enum HUDDisplayFlag { HUD_DISPLAY_FLAG_KEYS = (1 << 4), // 0x0010 HUD_DISPLAY_FLAG_UNKNOWN_0020 = (1 << 5), // 0x0020 HUD_DISPLAY_FLAG_TIMER = (1 << 6), // 0x0040 +#ifdef BREATH_METER + HUD_DISPLAY_FLAG_BREATH_METER = (1 << 14), // 0x4000 +#endif HUD_DISPLAY_FLAG_EMPHASIZE_POWER = (1 << 15), // 0x8000 HUD_DISPLAY_NONE = (0 << 0), // 0x0000 diff --git a/src/game/mario.c b/src/game/mario.c index 7cef3c9d..61a4fe01 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -1483,7 +1483,10 @@ void update_mario_health(struct MarioState *m) { } else { if ((m->action & ACT_FLAG_SWIMMING) && !(m->action & ACT_FLAG_INTANGIBLE)) { terrainIsSnow = (m->area->terrainType & TERRAIN_MASK) == TERRAIN_SNOW; - +#ifdef BREATH_METER + // when in snow terrains lose 3 health. + if ((m->pos[1] < (m->waterLevel - 140)) && terrainIsSnow) m->health -= 3; +#else // When Mario is near the water surface, recover health (unless in snow), // when in snow terrains lose 3 health. // If using the debug level select, do not lose any HP to water. @@ -1492,6 +1495,7 @@ void update_mario_health(struct MarioState *m) { } else if (!gDebugLevelSelect) { m->health -= (terrainIsSnow ? 3 : 1); } +#endif } } } @@ -1511,7 +1515,7 @@ void update_mario_health(struct MarioState *m) { if (m->health < 0x100) { m->health = 0xFF; } - +#ifndef BREATH_METER // Play a noise to alert the player when Mario is close to drowning. if (((m->action & ACT_GROUP_MASK) == ACT_GROUP_SUBMERGED) && (m->health < 0x300)) { play_sound(SOUND_MOVING_ALMOST_DROWNING, gGlobalSoundSource); @@ -1526,9 +1530,43 @@ void update_mario_health(struct MarioState *m) { gRumblePakTimer = 0; #endif } +#endif } } +#ifdef BREATH_METER +void update_mario_breath(struct MarioState *m) { + if (m->breath >= 0x100 && m->health >= 0x100) { + if (m->pos[1] < (m->waterLevel - 140) && !(m->flags & MARIO_METAL_CAP) && !(m->action & ACT_FLAG_INTANGIBLE)) { + m->breath--; + if (m->breath < 0x300) { + // Play a noise to alert the player when Mario is close to drowning. + play_sound(SOUND_MOVING_ALMOST_DROWNING, gGlobalSoundSource); +#if ENABLE_RUMBLE + if (gRumblePakTimer == 0) { + gRumblePakTimer = 36; + if (is_rumble_finished_and_queue_empty()) queue_rumble_data(3, 30); + } + } else { + gRumblePakTimer = 0; +#endif + } + } else if (!(m->input & INPUT_IN_POISON_GAS)) { + m->breath += 0x1A; + } + if (m->breathCounter > 0) { + m->breath += 0x40; + m->breathCounter--; + } + if (m->breath > 0x880) m->breath = 0x880; + if (m->breath < 0x100) { + m->breath = 0xFF; + m->health = 0xFF; + } + } +} +#endif + /** * Updates some basic info for camera usage. */ @@ -1772,6 +1810,9 @@ s32 execute_mario_action(UNUSED struct Object *o) { squish_mario_model(gMarioState); set_submerged_cam_preset_and_spawn_bubbles(gMarioState); update_mario_health(gMarioState); +#ifdef BREATH_METER + update_mario_breath(gMarioState); +#endif update_mario_info_for_cam(gMarioState); mario_update_hitbox_and_cap_model(gMarioState); @@ -1900,7 +1941,10 @@ void init_mario_from_save_file(void) { gMarioState->numLives = 4; gMarioState->health = 0x880; - +#ifdef BREATH_METER + gMarioState->breath = 0x880; + gHudDisplay.breath = 8; +#endif gMarioState->prevNumStarsForDialog = gMarioState->numStars; gMarioState->animYTrans = 0xBD; diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index a10ed013..cef67c53 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -742,6 +742,9 @@ s32 act_eaten_by_bubba(struct MarioState *m) { play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED); set_mario_animation(m, MARIO_ANIM_A_POSE); m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; +#ifdef BREATH_METER + m->breath = 0xFF; +#endif m->health = 0xFF; if (m->actionTimer++ == 60) { level_trigger_warp(m, WARP_OP_DEATH); @@ -1038,6 +1041,9 @@ s32 act_spawn_spin_landing(struct MarioState *m) { * particle flag that generates sparkles. */ s32 act_exit_airborne(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif if (15 < m->actionTimer++ && launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_GENERAL_FALL, -32.0f)) { // heal Mario @@ -1050,6 +1056,9 @@ s32 act_exit_airborne(struct MarioState *m) { } s32 act_falling_exit_airborne(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif if (launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_GENERAL_FALL, 0.0f)) { // heal Mario m->healCounter = 31; @@ -1144,8 +1153,10 @@ s32 act_exit_land_save_dialog(struct MarioState *m) { } s32 act_death_exit(struct MarioState *m) { - if (15 < m->actionTimer++ - && launch_mario_until_land(m, ACT_DEATH_EXIT_LAND, MARIO_ANIM_GENERAL_FALL, -32.0f)) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif + if (15 < m->actionTimer++ && launch_mario_until_land(m, ACT_DEATH_EXIT_LAND, MARIO_ANIM_GENERAL_FALL, -32.0f)) { #ifdef VERSION_JP play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); #else @@ -1164,6 +1175,9 @@ s32 act_death_exit(struct MarioState *m) { } s32 act_unused_death_exit(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif if (launch_mario_until_land(m, ACT_FREEFALL_LAND_STOP, MARIO_ANIM_GENERAL_FALL, 0.0f)) { #ifdef VERSION_JP play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); @@ -1180,6 +1194,9 @@ s32 act_unused_death_exit(struct MarioState *m) { } s32 act_falling_death_exit(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif if (launch_mario_until_land(m, ACT_DEATH_EXIT_LAND, MARIO_ANIM_GENERAL_FALL, 0.0f)) { #ifdef VERSION_JP play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); @@ -1200,6 +1217,9 @@ s32 act_falling_death_exit(struct MarioState *m) { // waits 11 frames before actually executing, also has reduced fvel s32 act_special_exit_airborne(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif struct Object *marioObj = m->marioObj; play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED); @@ -1225,6 +1245,9 @@ s32 act_special_exit_airborne(struct MarioState *m) { } s32 act_special_death_exit(struct MarioState *m) { +#ifdef BREATH_METER + m->breath = 0x880; +#endif struct Object *marioObj = m->marioObj; if (m->actionTimer++ < 11) { diff --git a/src/game/obj_behaviors_2.c b/src/game/obj_behaviors_2.c index 74c3b7a2..ddd945b0 100644 --- a/src/game/obj_behaviors_2.c +++ b/src/game/obj_behaviors_2.c @@ -452,7 +452,7 @@ static s32 obj_smooth_turn(s16 *angleVel, s32 *angle, s16 targetAngle, f32 targe currentSpeed = absi(*angleVel); clamp_s16(¤tSpeed, minSpeed, maxSpeed); - *angle = approach_s16_symmetric(*angle, targetAngle, currentSpeed); + *angle = approach_angle(*angle, targetAngle, currentSpeed); return (s16)(*angle) == targetAngle; } diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index fcc81a9a..c47b212e 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -282,88 +282,15 @@ void cur_obj_forward_vel_approach_upward(f32 target, f32 increment) { } } -s32 approach_f32_signed(f32 *value, f32 target, f32 increment) { - s32 reachedTarget = FALSE; - - *value += increment; - - if (increment >= 0.0f) { - if (*value > target) { - *value = target; - reachedTarget = TRUE; - } - } else { - if (*value < target) { - *value = target; - reachedTarget = TRUE; - } - } - - return reachedTarget; -} - -f32 approach_f32_symmetric(f32 value, f32 target, f32 increment) { - f32 dist; - - if ((dist = target - value) >= 0.0f) { - if (dist > increment) { - value += increment; - } else { - value = target; - } - } else { - if (dist < -increment) { - value -= increment; - } else { - value = target; - } - } - - return value; -} - -s16 approach_s16_symmetric(s16 value, s16 target, s16 increment) { - s16 dist = target - value; - - if (dist >= 0) { - if (dist > increment) { - value += increment; - } else { - value = target; - } - } else { - if (dist < -increment) { - value -= increment; - } else { - value = target; - } - } - - return value; -} - s32 cur_obj_rotate_yaw_toward(s16 target, s16 increment) { - s16 startYaw; - - startYaw = (s16) o->oMoveAngleYaw; + s16 startYaw = (s16) o->oMoveAngleYaw; o->oMoveAngleYaw = approach_s16_symmetric(o->oMoveAngleYaw, target, increment); - if ((o->oAngleVelYaw = (s16)((s16) o->oMoveAngleYaw - startYaw)) == 0) { - return TRUE; - } else { - return FALSE; - } + return ((o->oAngleVelYaw = (s16)((s16) o->oMoveAngleYaw - startYaw)) == 0); } s16 obj_angle_to_object(struct Object *obj1, struct Object *obj2) { - f32 z1, x1, z2, x2; - s16 angle; - - z1 = obj1->oPosZ; z2 = obj2->oPosZ; // ordering of instructions.. - x1 = obj1->oPosX; x2 = obj2->oPosX; - - angle = atan2s(z2 - z1, x2 - x1); - return angle; + return atan2s((obj2->oPosZ - obj1->oPosZ), (obj2->oPosX - obj1->oPosX)); } s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleIndex, s16 turnAmount) { @@ -376,12 +303,12 @@ s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleI case O_FACE_ANGLE_PITCH_INDEX: a = target->oPosX - obj->oPosX; c = target->oPosZ - obj->oPosZ; - a = sqrtf(a * a + c * c); + a = sqrtf(sqr(a) + sqr(c)); b = -obj->oPosY; d = -target->oPosY; - targetAngle = atan2s(a, d - b); + targetAngle = atan2s(a, (d - b)); break; case O_MOVE_ANGLE_YAW_INDEX: @@ -391,7 +318,7 @@ s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleI b = obj->oPosX; d = target->oPosX; - targetAngle = atan2s(c - a, d - b); + targetAngle = atan2s((c - a), (d - b)); break; } @@ -401,25 +328,16 @@ s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleI } void obj_set_parent_relative_pos(struct Object *obj, s16 relX, s16 relY, s16 relZ) { - obj->oParentRelativePosX = relX; - obj->oParentRelativePosY = relY; - obj->oParentRelativePosZ = relZ; + vec3_set(&obj->oParentRelativePosVec, relX, relY, relZ); } void obj_set_pos(struct Object *obj, s16 x, s16 y, s16 z) { - obj->oPosX = x; - obj->oPosY = y; - obj->oPosZ = z; + vec3_set(&obj->oPosVec, x, y, z); } void obj_set_angle(struct Object *obj, s16 pitch, s16 yaw, s16 roll) { - obj->oFaceAnglePitch = pitch; - obj->oFaceAngleYaw = yaw; - obj->oFaceAngleRoll = roll; - - obj->oMoveAnglePitch = pitch; - obj->oMoveAngleYaw = yaw; - obj->oMoveAngleRoll = roll; + vec3_set(&obj->oFaceAngleVec, pitch, yaw, roll); + vec3_set(&obj->oMoveAngleVec, pitch, yaw, roll); } /* @@ -453,7 +371,7 @@ struct Object *spawn_object_rel_with_rot(struct Object *parent, u32 model, const struct Object *spawn_obj_with_transform_flags(struct Object *parent, s32 model, const BehaviorScript *behavior) { struct Object *newObj = spawn_object(parent, model, behavior); - newObj->oFlags |= OBJ_FLAG_UPDATE_TRANSFORM_FOR_THROW_MATRIX | OBJ_FLAG_SET_THROW_MATRIX_FROM_TRANSFORM; + newObj->oFlags |= (OBJ_FLAG_UPDATE_TRANSFORM_FOR_THROW_MATRIX | OBJ_FLAG_SET_THROW_MATRIX_FROM_TRANSFORM); return newObj; } @@ -508,8 +426,7 @@ struct Object *spawn_object_at_origin(struct Object *parent, UNUSED s32 unusedAr obj->header.gfx.areaIndex = parent->header.gfx.areaIndex; obj->header.gfx.activeAreaIndex = parent->header.gfx.areaIndex; - geo_obj_init((struct GraphNodeObject *) &obj->header.gfx, gLoadedGraphNodes[model], gVec3fZero, - gVec3sZero); + geo_obj_init((struct GraphNodeObject *) &obj->header.gfx, gLoadedGraphNodes[model], gVec3fZero, gVec3sZero); return obj; } @@ -575,9 +492,7 @@ struct Object *spawn_object_relative_with_scale(s16 behaviorParam, s16 relativeP } void cur_obj_move_using_vel(void) { - o->oPosX += o->oVelX; - o->oPosY += o->oVelY; - o->oPosZ += o->oVelZ; + vec3f_add(&o->oPosVec, &o->oVelVec); } void obj_copy_graph_y_offset(struct Object *dst, struct Object *src) { @@ -590,25 +505,16 @@ void obj_copy_pos_and_angle(struct Object *dst, struct Object *src) { } void obj_copy_pos(struct Object *dst, struct Object *src) { - dst->oPosX = src->oPosX; - dst->oPosY = src->oPosY; - dst->oPosZ = src->oPosZ; + vec3f_copy(&dst->oPosVec, &src->oPosVec); } void obj_copy_angle(struct Object *dst, struct Object *src) { - dst->oMoveAnglePitch = src->oMoveAnglePitch; - dst->oMoveAngleYaw = src->oMoveAngleYaw; - dst->oMoveAngleRoll = src->oMoveAngleRoll; - - dst->oFaceAnglePitch = src->oFaceAnglePitch; - dst->oFaceAngleYaw = src->oFaceAngleYaw; - dst->oFaceAngleRoll = src->oFaceAngleRoll; + vec3_copy(&dst->oMoveAngleVec, &src->oMoveAngleVec); + vec3_copy(&dst->oFaceAngleVec, &src->oFaceAngleVec); } void obj_set_gfx_pos_from_pos(struct Object *obj) { - obj->header.gfx.pos[0] = obj->oPosX; - obj->header.gfx.pos[1] = obj->oPosY; - obj->header.gfx.pos[2] = obj->oPosZ; + vec3f_copy(obj->header.gfx.pos, &obj->oPosVec); } void obj_init_animation(struct Object *obj, s32 animIndex) { diff --git a/src/game/object_helpers.h b/src/game/object_helpers.h index 7040dcfa..72fcbb9b 100644 --- a/src/game/object_helpers.h +++ b/src/game/object_helpers.h @@ -69,9 +69,6 @@ void obj_set_held_state(struct Object *obj, const BehaviorScript *heldBehavior); f32 lateral_dist_between_objects(struct Object *obj1, struct Object *obj2); f32 dist_between_objects(struct Object *obj1, struct Object *obj2); void cur_obj_forward_vel_approach_upward(f32 target, f32 increment); -s32 approach_f32_signed(f32 *value, f32 target, f32 increment); -f32 approach_f32_symmetric(f32 value, f32 target, f32 increment); -s16 approach_s16_symmetric(s16 value, s16 target, s16 increment); s32 cur_obj_rotate_yaw_toward(s16 target, s16 increment); s16 obj_angle_to_object(struct Object *obj1, struct Object *obj2); s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleIndex, s16 turnAmount);