From 830b29788e08f4d4680e6b4d9718eae15fcb70e7 Mon Sep 17 00:00:00 2001 From: Gui Date: Sat, 30 Aug 2025 09:00:26 +0100 Subject: [PATCH] Add new 3d camera shake example --- examples/README.md | 1 + examples/core/core_3d_camera_shake.c | 127 +++++++++++++++++++++++++ examples/core/core_3d_camera_shake.png | Bin 0 -> 13185 bytes 3 files changed, 128 insertions(+) create mode 100644 examples/core/core_3d_camera_shake.c create mode 100644 examples/core/core_3d_camera_shake.png diff --git a/examples/README.md b/examples/README.md index b391732e9..a920ba2e0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -38,6 +38,7 @@ Examples using raylib[core](../src/rcore.c) platform functionality like window c | [core_2d_camera_split_screen](core/core_2d_camera_split_screen.c) | core_2d_camera_split_screen | ⭐⭐⭐⭐️ | 4.5 | 4.5 | [Gabriel dos Santos Sanches](https://github.com/gabrielssanches) | | [core_3d_camera_mode](core/core_3d_camera_mode.c) | core_3d_camera_mode | ⭐☆☆☆ | 1.0 | 1.0 | [Ramon Santamaria](https://github.com/raysan5) | | [core_3d_camera_free](core/core_3d_camera_free.c) | core_3d_camera_free | ⭐☆☆☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) | +| [core_3d_camera_shake](core/core_3d_camera_shake.c) | core_3d_camera_shake | ⭐⭐⭐☆ | 5.5 | 5.5 | [Guilherme Silva](https://github.com/grssilva) | | [core_3d_camera_first_person](core/core_3d_camera_first_person.c) | core_3d_camera_first_person | ⭐⭐☆☆ | 1.3 | 1.3 | [Ramon Santamaria](https://github.com/raysan5) | | [core_3d_camera_split_screen](core/core_3d_camera_split_screen.c) | core_3d_camera_split_screen | ⭐⭐⭐☆ | 3.7 | 4.0 | [Jeffery Myers](https://github.com/JeffM2501) | | [core_3d_camera_fps](core/core_3d_camera_fps.c) | core_3d_camera_fps | ⭐⭐⭐☆ | 5.5 | 5.5 | [Agnis Aldins](https://github.com/nezvers) | diff --git a/examples/core/core_3d_camera_shake.c b/examples/core/core_3d_camera_shake.c new file mode 100644 index 000000000..89f454780 --- /dev/null +++ b/examples/core/core_3d_camera_shake.c @@ -0,0 +1,127 @@ +/******************************************************************************************* +* +* raylib [core] example - 3d camera shake +* +* Example complexity rating: [★★★☆] 3/4 +* +* Example originally created with raylib 5.5, last time updated with raylib 5.5 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2025-2025 Guilherme Silva (@grssilva) +* +********************************************************************************************/ + +#include "raylib.h" +#include "rcamera.h" + +#define MAX_YAW 10.0f // yaw is rotation around the y axis +#define MAX_PITCH 5.0f // pitch is rotation around the x axis +#define MAX_ROLL 20.0f // roll is rotation around the z axis +#define MAX_SCALE 1.5f // max scale is zoom in/out + +static Camera camera = { 0 }; +static float cameraTrauma = 0.0f; + +// Returns a random float between -1.0 and 1.0 +inline static float RandomFloat() { + return (float) GetRandomValue(-1000, 1000) / 1000.0f; +} + +void UpdateCameraTrauma(void) { + // Update camera shake + if (cameraTrauma > 1.0f) cameraTrauma = 1.0f; + else if (cameraTrauma < 0.01f) cameraTrauma = 0.0f; + else if (cameraTrauma == 0.0f) return; + + // Calculate the shake offsets + float yaw = MAX_YAW * RandomFloat() * cameraTrauma; + float pitch = MAX_PITCH * RandomFloat() * cameraTrauma; + float roll = MAX_ROLL * RandomFloat() * cameraTrauma; + float scale = 1.0f + (MAX_SCALE - 1.0f) * RandomFloat() * cameraTrauma; + + // Camera rotation, based on UpdateCameraPro implementation + const bool lockView = true; + const bool rotateAroundTarget = false; + const bool rotateUp = false; + + CameraPitch(&camera, -pitch * DEG2RAD, lockView, rotateAroundTarget, rotateUp); + CameraYaw(&camera, -yaw * DEG2RAD, rotateAroundTarget); + CameraRoll(&camera, roll * DEG2RAD); + + camera.fovy *= scale; + + // Degrade the camera trauma to create the smooth shake effect + cameraTrauma *= 0.95f; +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) { + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + // Seed the random number generator + SetRandomSeed((unsigned int) time(NULL)); + + InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera shake"); + + // Define the camera projection + camera.projection = CAMERA_PERSPECTIVE; // Camera mode type + + Vector3 cubePosition = { 0.0f, 0.0f, 0.0f }; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + + // Set the camera position and target + // Note: We need to set it every frame because the camera shake modifies it + camera.position = (Vector3) { 10.0f, 10.0f, 10.0f }; // Camera position + camera.target = (Vector3) { 0.0f, 0.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3) { 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 45.0f; // Camera field-of-view Y + + // Set the camera shake to 1.0f when pressing SPACE + if (IsKeyPressed(KEY_SPACE)) cameraTrauma = 1.0f; + + UpdateCameraTrauma(); + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + + DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, BLUE); + DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, MAROON); + + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawText("Press SPACE to shake the camera!", 10, 40, 20, DARKGRAY); + DrawText(TextFormat("Camera shake: %.0f", cameraTrauma * 100), 10, 60, 20, DARKGRAY); + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/core/core_3d_camera_shake.png b/examples/core/core_3d_camera_shake.png new file mode 100644 index 0000000000000000000000000000000000000000..0a090bbe8d579fea17fc8daf17c849a78a0e577a GIT binary patch literal 13185 zcmeAS@N?(olHy`uVBq!ia0y~yV3uZJU_8XZ#=yYPUT}q-fq{XsILO_JVcj{ImkbOH zEa{HEjtmSN`?>!lvNA9*a29w(7BevL9R^{>*<*42CGg)`sBZhaNI`c=u^{lUL$em#o+_cVTAGy?;J)$iF~co`TP zeh3<@n97#SE^1k4y18UzlpF)#=?-C$r~ zaN>|=U~u3_L{of3{C?fw3HXN#Ay@waHg^$ulr~ zaGnP?f7AS&C;!;~XZ>D%K6vTv^+n&kw|mDkGcc6efSiJC#l*kn(&0t_pFMo_IppQX z@SVTcUiwwM^f)U6!@pj2kV`t=s7t%=Kj?4oAHRH^o&VIl{j2WkhkyCO!0@32Vi}TW zPW+qQtNrqRPRz-_(e1bP_h#JH4}S?#st2(+#tvlviial&d6)RGX=wlT^L+ijKYyOv z``>44$OH#phyK35-)_IZWWL?2KKtA5`+xOnU#nhScY5{v@>E&#^545%u1nhfn|kN* z^StA>bL1J)o`YSa^!wzotNG`OwpWX8{e1SW{$0J=S``>^*Zazjy{_2xy*YX8*RNA+ zZpV8*pRHZKzhw89uM+d?5x$R#p+;1i@5G`}sO`hMs3IXSl7qb$>@}?)B+0v`!7{Kms?+U zXZ7E8+jjr5xmpd1-k0BAzFz!x-M#qO+Igq${>$3^<@1-)SD@JZ7JlhtRQ&Qw=PzGh zd+Fn)>kJ0c{@}!R)8EXizBp{x{at^jelf2K&)WW8HMadr>AE|AFTK5VeP`D9-IJ`} zT`dcrzufEm|EoV#t31?UuV&e{U^1 zzWbKmUz^&yt4j8)etY?RbbS2Fy??JhhdcVR?advN{$f(kF81Q+LPj zip`$8bKR?`*xu->KXJ!nmmas5vj1DN?c@5G-sy7RGwyu(yt**l{qa(-a z$?q3GpY`!GEUw%H%6>a)Kvclt$#`;P%)jiAo&VqcKDGb*J@+sB=gZsHt-SME=kI^< z9Y&}BFfiPk2+t|u`xVdK?X$V~TGF;IKXdQPYzBsBkHL|;==S8^keB7J=I{GE_0mVT z-1?CEUzP7adM|yvbUh=(jDCm_8`P!4FBL{ZW6kUMYfu=hEcyNA>&3G0_v_c$?cemS zUUlt%uUuOOhAogHMetk7;xFdsUlta{$9rx+KiB=`k8rQ!yH}b2-Bb4G%GHnSWZu@R z&h=k??=d4oz&Wr-UAC)Bzbt$CI^(|;zKTK$z%mf^Fo8NSiFt#f!ras z1G8^ddtC_5{v012|GJRzh5rqP3g)f6HQkq%lXR7}|XZ0}L`wQUEtg`l9mQt3H-R<&LV(V|l56Oqv4xC*#>-+B6_j7AcKd|z8Ubl6cs#ibn zfqqVD2J;n}UGFpM-t$OBhZpbvwa4|z4Tkl8Hy9+As3pAlDe>Xzs>N)-U;GW+cKrR; zc{|_DvpyiqDa~+DY1Wk!hSPfrmHXzuK9{U7EF=HGvxT>TLAAHK``WSXThCp4{QTDH z;wu~b@6^^bs0)}eSl!I-aQ~_CCoA;5+s-dPb{4%lb5rfo-Q(;tW^Q1-kRWXkR&F{s z+&8~OVwY9jx?5J&vi9Lsd%l=59Aa~ry7`#VtL=YwZrgg*?49npYtMY%)~$?t{N`lj z`4f+sZF)B_PS9L_G3M^wf_t@pR5!OxGS!`0{3pgV`L3)LW12z^!<>xVHg~f*)myKb zoZS2N>W1%eQ>G)Oaf6J`bZzG-8AQcn%B(j_SQ3?WqBQh% zht#<{vJLMn)DFnD@IH7OV|qw=ipgx%+tWHEwwIi_^tSqD9P?@3l*@FcpvC4 zVSN1g`T5hEbWUH7uitw!Yfpdo`Y$@nQWvkaZFgVm{`$i0i!uH8usp>x-S(kvm2HRnKewmu-H=i=ak1Q1m79<%U!a%bY1z1+~%_VwzkJ_ z6`TIM`kZ02uvx=#tE?N*E_P2fR+hJ^ZC-NEV`uin-Lbv>ht|)SyE5|niJgotLS_x_ zD>JWTeW^Kh@sh^3oTB2TUze?aziGSJ;&tsy4_j?{x4%S+IYA|ndB(jHZAGh}YPz4F z-xfy-!g*9OfCXu4Vh;z1x&Sh+Oekcz4`C8S+9CmC|~DE zOfKD87C!&G!6OL<-4@;itxV&cK9@Y^+CDu|sdd4-|Jf$@y|%ATl!m=OVb!<9bjf=6 z9gk-)Shny!C`&*4r08@0rQp}WeXD0JzG^bv^mk0BL~*FmH=gSro_hn+Rxl=b-(biK zytU^1C70To8lC$!pU-ZRv3V+?yZofw#xl#&C#%+OFWWCxef^_s=@O87L9>R5-=sDN z+0MQd<6b)bkFNWYy~jSSc;-=Es&jhNpP$R^qwBu)*GEiCjr{lH_xfr7Kh*#K(R_aT zxvz3pF9x48EZWxj?PIOQyoXk-_a<*(Yk@e*Dv2{kokx zr$1evuyHG|%-;3AYdbb(ACoEE`|jqyxq-fS8EiSE9XehdGhbD?O7i=g{^Ra9)_q;I z%KX@I5ubNyZA?Ms$?SB4=Cj@*t)X!*xE{;T)OeyMSsh4Qpe?9 z2Q5-bS>o2xw|T2o@b3E)?>)|)+u9S7BFhkci0#6X@WuBQZ7W=L_q~tj;qb*NdzGJU zzba%9q|9`D(oQwi>=^CkeKES%W6oPKeB+RIm?}AY%AEVp`ZH9fH!PRYeSLS+`+4&e zl}poK+^E)%I&B#3wh6iG1J7rEaKE)Wef>MZsM!bamA+r=yUzaqm-N#` zAK32;y0!N$y)1QIEAw0Q`qjp|*EtTzB{Ew~z1%a^#7j9RJ5FuI>Mxq^PpYQ%$^H~k zKED0`m+dd3@8(=jnx^*q%^#Pxp0%5$7`!;85A;7_>9I97$=S@>do^@%#O`NX*PXk1 zS220@_u#(8X}@0lwaz(z{_0lY)MZ~8=6T;>n6@-|bM@6DZWgO+x3y{3o+?bQeYw%S zK6F(`_6v{a)hgw?Gv?dZ$Jy2XDyf#;yoUXQ&mp!wJ3HKxLLO+WtPSzCwA=T;zI5Kg zi7{%`T@x3pl+0nhsI7WE-2dg|p53oJ&IYl6&^g4mXW=w9o$!$4OF_TyHY&$X{-Sw2 z!=GC+I=oRi_O;mJb+b)UmQDG7QylMIV zd=k}{E(n&^Ej|{jwXX3*R8h|QmrsvbHKYreJ!p+J-L?1%Ti=Oa+1aN;Zwa{BKdsn0 zVdAgH@ApUT`|)=D=Igt|URK%$OBn^(W}hr9*|YdNqgWzyoPSF5J*Dl6l9BcA4_u6@ z&i=gf`Bi5(txxk$-95E;-uEwd=XJL}IU#FbZv9{{hx7-fOVYK!s>IxR|HXd&W|7x# zxGida(Vpl79!ulbc7+tZt8VWJ@zmYfR~L8Q~AsGAm8-R#dRx-^Ekb z1(&=v?R~ksz30k{3xy@JSLU1+OW*qBm7qjb52MZe4UGK3m!Evgu&cUxZNJEw#FjbF zG}33!TQ{%h%)kG=hRuNsmG_#twfC&uGA(B7@41~4!WDdp%yJ9V5|+HHF)& zuA(Q`>@Gb&KY#lFFZTbYPq?UHRI+5@hVSR|yBA0G-RmzoZrJ#nL)u{G3pMj!Zt>Co zbsCeS|7W%B3fpIP)&2a1iETYsbgqBfR9t&MGW<(@cY`H|v_a!7w!N`(*GgvKYp7cvp6L>LGBQn$*SCQi)USlx?QQ$S}gf=)y);Q z;%nEv(ke6!^8s$}!G6*fd1ViW1uvGc0g z3f-dTh3>gKy`;WO^tz_=-6m&hTkq86GWI1RMq$P+y`fdUS7Xd=ugdK&+pit?n5mXS zx**Xdc($3aVAgZP+r2f>=Q~PQ7j8`ppFe*kue7&Y^IyKA>j}#?Y&7}v$L-YKzT)lK zrmySyAKc}X-tf3jy7~(5&g(nM@0~b*&VBt)*Bk3@-jwm$($ag?+V1HIUdh$=|NgCh z+I|0DRixDUaQ@5#asR*TocnjX;9F#it*i!mU(7n{qAOZxyNhI`2Mo*9~tRZJ*HvBl_I&=oH9Io*eOl#jndN*e>wWy9SA~4VORk@rIVsmYfPaFJ zSw-~b`z=M%v#p;^jE?S%`~1SxuXpu=E4%Up6O&(Fm~no|^_Xvmju}1pwQ8?h*7w({ zvD3eNy_>e0{a)vWgVDQxv(-Lbv2XRVvKBe>_a`c4*2cC>+NyEWOjOh?%)jo`MeUR| zeTR;%dTR6TrD0sZVb+|#Ywm_#o_OvQ5}CE6_Xno!@{~Ur zxqo-whrQR=KezG9mKHP%+id@|gF|xlqzxNS{gk|+W&7Q9QQt+Qywm$^mpp&@uIhm- zhxCWNU(`yL?`RrAwl|E$;-Gx76@wYe!bBATRsMJZ>k;*|D&W;$uJZ~o5f z*Qd_g`hAt=_0YHB^O?W4@ZON9WxjKZTVkd4>(d*rO*fuy4RXJl%JOX&6O&C32lojo zCWlI!g;ifabFW%?tDODwUpiN#g8MEdPs!tt5jIm0EDW{{t}2#}jqi+fJ{ETF*Im>8 z)!7f%ZP>_@_ig(-i+5M$+n?`xZDmzl+Pl&n_T7_zoALG?kM!z5f6I3qlB?wslYbX)^eOONx^CUq)#sJJSf#Bm zoOUkx;BFza2L}QVUtjdSS9{U17|Y_FoA=3N*IE`!=B{5RdFYss42#n7b617TzMZ>$ z^LM#-y<7T<%}d)#?DlTC!t&nlMuV_zUd(IXEY4klUso80pN(<57QQG=ul&Zk`#Ooq zS3heW=K*`ZXx-O2xo)$+rY~G_Q2*w(qbnXC&=xd%;N)9u@atJ@aB}B0>70^U)o;%> zeZS{(?dxpa935FFr`P}XUj0*8@$zf%uG%ec>(@`~d=)i!?e&}K6W>|s*-YHPC|NZ7 zsdc`?PSa^GEa%TlzIwv4?ugX4Pai*Stohy@?{|61aPCR)f5-nZo6Zy}5gD>ImN{=*!X(w4E1ZE==yhMi_b<}?=d~7)55ESH? zDDgXGm-o`OF1?n0=T7(3Z!S#X6<@vl%HHqCAMRbh+Bood zmhAS@|6%UO?W_LhZNA1@E?~CcP^@o8=Vqz&or2$1JYHe={%MYE_3o(ty&DWa2lwq& zm)U%~t#|cj!IjrbuUGT!J-&NV^`1L#-DYpqlkmLJ;I(&FqsMKj#j`(&nPyvlQi}_Y z@4U0~O>1xJqXQqWY?xSWw`l8?i<|QMUVNN>YW{A)(xzm7of-wNJgR*S_}a^f#XkyRvuh38{&|P2eXe;aF1Pyq-Gxj0rqvc-`FG~osKlv*h*Uf19@(|6lWE&$_D9e{V)`ZO;`gU32ZV z$|}7Xts59+x85u9+Hz-s+qIe5>nmr0J)`^lxku)Wh%%{H6VrI5-LogP^{T$G`L-dh z-2M7}o^{vWt-H1U)v2r5b3)&LOb|Q7Rw7o(d^h_n zzpUHra&peQHOAp5YqGYs^r|kt`7U?bhKbhiH*eUu>vhzbo%@Qm?K~ZI+bFSC^>x^$ z+|?70H+dXlW68|*a=SYx`nAqQqvaDzr+G*>ov?~Nd@Q59z4y+9ji)%JPoJ~w4=G+V zt9DUt{kQ2Sv!Y|yMn5_KZRYA-31x@aSXS+{yjz&Oa^+^1^_E|ymbY(?T6Ar5`VEcT zBi?r%LCDuk-)!|G(S6yw!WhA-z>G+49HZ4>E$+E6y8M z_l0UbPgh8vz5CA2vh=jI+8Q84T zn_5_VQwwfna7wPewPE9{!gbX>g(^jFLSAn4+Ag0Mefh-yYg1+v-c9J=z}URTd`;ll zitbsnU(1^Bn`PzwWmfQdP~b$KcyVz~jo?Tgd?$(AvF{4E;ZN2e-pO~Ls?mvH5 zOHb(f4I4$`>}}U|2dRF2`pUAu<5$rW8T0hnyU)Z(zxTb-aIos$l*O`Fcip=(%`-1& zZ{DW8$1E>d)n(pT!7FJyPt2vYck8xWJ7qQhKK>x%Zg211p1rVL&h(4KV~-~{R&Fld znx2xo)9X3IVh-toX7#%w+qRyoXui8D^15|Xu*S`t_t3Gx6E|KNzF)Cn6>S^E<2Ys_vN#gsMCQrUR=zXe=_q&@Y=-1&t~ZQTPhtsR%JVF-h1mgS1Z2V z)YzM4@jKt8JMQb#j#aRXguW#;oH|<)L3a|7<#l^0; zMw@0|`rLnbd*8a9i@&E&iktlM_3o?d65rM>U36W$s^*2~jRwv$1&>ecH z``Dg$(>AZmzOf>6MfFKu=B0_s<#%FK{Vl87d&7T5RvkL;e^P2%&TZ#+r2$GwJ{@W~ z2UOhdb4+hbd&FtJC%3nJO>XaMEBpF?dvwfOd)CfLm>_LdbZpgBX^X1X-dXdzXVtnz zu0K9AJI3hRhgY97_V(3(z0W>l>IOz($x`FWtF!M%R?J#^O}YAc8n0x0{ok+s`@Bk* zZ(dL!YIg41Tg~U10sR{;R@H8OcmMJ|(;fTf?=zpgcUStV%JutVjL(AdlEKUqv%OBA zedm#~cW2RaQ|Gli?dR+;T0_>3Cel z4Q}b`+r1@UrYu&=xe`;oQ|Y)}(68b`_uie?c1}5$xn;HKoV|4?RC5k2iOM$#H{WzG z>b34gtL@kRF3#Dnw{_R5(rE`a<{Wa7GI_@*x$=vqyXTD^R`RyJKW1jlJ>MZ!{Pt|> z`@J{cT(!8o%L(L|u;RlvcNg{s1?P9(EAC!rem(Al$K0z{(GPyS6W<>L8pxTUmy(k^ zzvxL@@8)8GrQc2WU(L3gX!>l|tkXQEago_EYU_o)IHe7CuAcqE=%%mM@_RxTi(kj) zcg~rxu{8Ac{lB00FU#lszI^?^PuKt3W?#7R>#BcU@y>t7Cac1q?{m3Qv*?&@aMhVh zZ{1&2_9dzvVzW7L(Kq9o^z*>Is&41jy6;u>az1wL>aIIqrzG=A-d-Pg_D@RkZ~Xw- zx&5oR%B|fx@7mwu-d~kjmgiP%J#p1Ye8$`jjQkt-N}erSd?&d2^PcRytzV~EWMyT~ zxoT}xkmGzT>$kad_5Ay9U(Db2_s`GIpT1s?zhB1tY}Ly5Z>l;2f|J+HOYV=G9&&!? zfi*3>6%&@E;sr+&AgpFT6{OvXZH4-)5xeS*g$&X(jU%-X=n|K*5uXw>Hu z($&3N?!{yuh-d-@O4K(lnVpFbHFDOhJLIKrs+gSZ67eRrdhfimFY`?8!%cs0>4@2X zVq;?1AvT*UFQg0jU-y<(o;bTLr8hmpXZ@?Sf1^V4PDI4E_NrPGY-#JcqHXhZhG*Vf z&|vDXO0BEaHWgfn%yMru&DZB%{xD@$c=*?I0!G``z0$2b6)0sCW|^3rDe>PY?}A12 zV%yDgpI<5SGyS~#aO8{`seI|C(`KaF6|g5V%jG?redA(p)w5aO-<(Z;_N;jQy|t?k zT$ExeI(%%^@!-BY6E@CS=5VE)1Kcy-KAThA zJa_Fg8TtGFy7b>sk`s;vge+DvUS>; z=U30m@8M2ljyu9*zHO59MdTKCtgx^15q(m1VA)y!x}%>$S}+ z{yoNt%yB+dpSv%8US_rGnNYazlOEIf$ZR{S@|V|FWo>Qiefl6fbf;qS&6&|#UM!cf zUB7)^^41-z997_Qu5lNx`tETJVHSui?p!@#qv%$#iR-TYeARz^{TB|Y z==xu;^iP-X|M7H-V)E)Iw@!+hu3u4DJVkDI$#3hJzcB}`h0Pwge%<%8cgMqii(?a~ zxo!L&<$hz`+t9tbjf>N7M9fc_6DyuR|L)S`!F_?BrAp^qy}Gr0S?Ya_#n<`gdE96a zzxMc9&GEBa-0$v;ayoYH#4K-_x%(G?-=4KE?5cLoo7z;Z*vDVK`b4c;ZT(EN_RO5u zy6)=>t2*M0D?|^m?b&fl`q5qUueWv;Z#8LlUzDvkvHIV-EWO%O7p2U;IUT!ZwO%zj z&sd}K)Wqs@i(F*?zmi%l7@EDV!}8#2A+ra(Pwq*iXFn-=ttTjGv`r~XFYt0p?9~_{ zi>$-PLQh0YdR?qJ=keU4bvsuV8w9P7%360cXm8e8(WkCRTY)juD!n1@YKEFzQsH5uHCwB;`3!&uf-@oXIbxZqrrUA zJstPEOQM&SPM4503eqio{;HO5eb&BT)_r$2OcXYn)>Bn=-u_mVZQZXgnUQh<dW7=~$TG-JNcx_Tk4Zxn$B4 zm6cL*zQ#P~czGvnPuTe-U*q@IFPAetTW5PPm_z!*vUyd?wyUbY9ecatosjJ@jndLH zU$R516P2w}a=uC}-@du@&hO~HnYqu4%+^a@iR0gVo_ik1x8JTvzuNZhrt`5d@5h@% z-dsO@wRBVMsg1nSmWs(ICHA`=nPFOV{_f;;kC!IIZ9cJAZl0Nb1ydsPy_k2t6=u+J z;x8v|6<@DecI^48XQl;f5|!06T+ZuP{W0EpyzWqN-`lMPrLkw8yqaT^srNzU5Zi|p zvvYn)KTyBAuQcp?XmH8&IlKP-xjg@_oKaAEw(P>M6)#QgChy(ZYd3%Gw^w&(i(hEp zz^HSYId5A!?~ONGQfiN@RKH80r$H zNf-D|HuZkHbDLu_XI7Z0ce|N$+Dfmy zxb^v|%#yUVe@{h~pOZ-s+I-LS_ZN|$QKQPMu%PyCQ z=Mh%dC7=8`bJlGAyz3t4H#XR{@GkK1TRiFVhdZjv{|PD2o_%lY^o+UB=Wcy|YxZN| zSNAr5pOTzkXPmkA{mSQDadS5?idgSjd0xh7Ti+}Fi?<}Mz6z}FocCEKwtC%a%eY#* z)o;%~UX#6l`Lb)XY8wn&co&@d_Uzx32^&Li&pC6UqHl%bD~Z6z*R*!>d2O3}6g25{ z*8INBtMp>c9ezdIncperFkGE?>%&kDe`zTzm zxFIG+J$*joX92SYHUIq&pI@23`D|>}HM3n4-$hn;${l}e=r(2Z=N;d-%WS?q`}~>L z{C_5FU^J=z)%$7?<$_B+<*6X#M;+#ip!U&+dqwnJu5h)eFJ03#LG|4 z+Af?d8S{Vcg;zEa$~92^$(f;mR`@<&p6&-*i~6&Y!Li7V)2rHlIKHR zds~Nk>uAQ(+<#WIE>-;mT+1H{j@9Z~w^kZ%Imh^4QWt6W8cBtnt zD5m9!6iIKL`Fuk1+~<=FuS-r{SO2b3cHx$9LKBnqC)+&jn5S&ew}G)`=Gw#+nQrN` zojYg!Jg1iaH+^pU`(0K2<(TugcXLyP=CZ02|{cDo`)&Aj~ zFDo`4moRm+d%7dL&p{`VnWH>hxbWJNX}hxdRM$QG-C{ZC=Cyj$>o>1li{2gYuRO2F z=I<>QP{J2jJiD{wv1`vwXHRFV*D^&@&y>2UzK-AYmP63ra$cQXWA-67gEJk^G z8nz3We!cR>xOwZl7sWDfv;L;fi$A|yCj2$S9=1eg39VHJC2lToJ-g85bxryV=XHyB z+GX;sTz-D#SIy(W&p5XTn>9SFGLv%mjouP&k#~OMqL^6i@cHNS*8b;wt`z!t%Hz(@ zEVnqM59mhCJ`y==+O^6jU31>Au$-bX^L@|P=^8U%_Y}=9viWPnSlYt-z|8dQ@t)qu zvo>BX{rh>W-1=^Pj^2HL?xk7jmh7hM<`vmg<~c|wGVch@6P{$2w)x~-_s)yOeKVXp z<&M8MJjUT(D!2E)$jjiqi`NYdLDhx+Lvsj-2YdImPQOr@XMeZ~bh7cKSNY z+h=lWPknrOdHL~kj4#}8Ff5y3*5RMK<5K068L8)#UuQ(FH&%W=VRgy6rMJ&NIk}a& zmQ(tG$u+j8FMGXw|C;`G>0Xl=BGEC&dEK;~=K|_Gr|o1~?|Fk^*{3bfUU|H;Ny|T1 z^nBv$zMtRbf!ww)`E1sg8QLGJT6hzz)?eB(=j>xm-X}$d{X%<>tt>oV^?hFc`;S$# zcA7niJH%$NG(0ozQsteCU%pK^xA=%pao$Y#btith^~EM{t7`bkA$?$rn@OAU8s3|q zJ}g_aY~!4dGH%yIEWce_@p#V6g=`6Rhu93ZhG%5NsphOHI_K$YAgP(or}}P2{(H+Q zJ3ikyu$e>pz?XTqZoDyF;6K;py%>%G}-@}$onwTjuEDdsTrhh6g6+Ld=? z(ed@6!PifG7D}{!zWjXZNt^cd2L1_lhuA(;T=Wg{TYN3}OHI+%bGu9b?|!rP|GjGe z5|jSL-x;Got0+1$roGFVru-~;(esbheWEX4)!3)Xg>U=3W0tXlb|Q0yr0wj3^OCRb zTHLnc+2_8UQ%~c7XVfr#(rpvDnQJ+-=lXZPNOtXyU;M4+yv6jGt;K9B z6muB#moF2Xvy*Z4O#jY_#r^ZVJFi7WecyjrIGb&r?+u2ypuAwyO&9YfTF)0MKl^vx z~udK8eVXu@cMpFebA@!dSwrBtXJ57~+nm1F z_V}vOvb;ri3VlFR`X9pLHcH-}efeIs|HY{4*Z*vL*Q7q#$WX*7ec*QG(PcSvFZ1lb z_y;VP-d4BcT&lz?*qrS6Cc-~;RcTafs zwbirM+tn{B^;>@_=lYz*vuYXMspK%&tmxFv+%n&2xzKvE<&%C+$>BEOPGo*iz1h6j z^y}3(#*1@LR86&9yN>C})#nVx!e$I+X*ns;v(H}p`+i%w-?w|!yY^bX-OCPYpE)E= zV_RSDF(Wl@=elS0Uw;bcp1*kcoH%HCLc;O8y>VAQEYbz?JX2dp2J`Z{H@+WKU7=9Qy-zTX-9sOnKi3 zOP!fjIaR8#=Zfrc8>3)d24xOu22rnB$0p^z7ZdGTbnL9@Vapi{*%~gk@HTjbpM89n zx2${h&Z#df!@75$J05$S6*SO&;lYZDc2)m9qn;aNPreJXsGrq9Igxq6#cd1cY(2Ox z^tR6Rcjl)$e!0mog9daR*w>30ZS$J-Ont5M$vs{+nY;{(IHeipx|psvJ~1=;oZ04= zvBmE{GJu+%FJ4TYeRlCq+rQ>N_r9`^l5Z$i%V8+_cFH&1YWtV3ldqmz8Xmz|;d+S8 zq2boCubkJORaG8j+x=zF)I9zVdH>XEnH$RD>wZ4vpY?p>X8U87zGgVEU)YR+t;)+eE;{{f-R7#TKN)(|au|5b zikWXe-u}b*{Q4uS&oDpmJH&Q?H!UZn`r4J^H5aQ`^cep2ZeT2!94Yl_#j~nuKc`kC zodqYoypr-_>DoUpK2}!+t!406%wf3mrccfMRpzmI6OXSltgdD7SIc4WNl1AhQMqRA z?!M}@W3w3IIyW#HbiO!N-gf_i`}vJWe6})xrb7~x0(lFr>@a<|F176f>j%9S-iDK3 za*n0vuBkq^@ks1(){5{$YzJDZOpe+f>oD21@8WS#**haFx7g51}!4L!98}RJo)9La5zM6jypLZ)_`k9SKg0C|_m|C%k@dHD8 z@8{JuzAtk6RO3=@8R8l@Fn$oIKKtWdwf{9Kk+@u2aP(G8*g7*W_f&eguvFGJMulk` z7(W=izZCNP+~=#!;s+*kN;B|Jx!qxZ_o~f$JN5^0|4g+S*l*QMeKmFN+29KdGv=#H zGw^@Y>8nQ-$LCFH^M~nIHVLxyexB=%06`Hka!`DTg$J{;ST$ z$DO9k1!u0WV2HbNd0&uf2E!gXe=~--J8yjVtoLQy12T>GjFd&%TJ8qBnqIC4yk~xZ z4f|TTiSdJ5TE+*EVYM964EfK_9VmRhkx}CO^%V^F&cw#A6f$AB2U;_opB3`?eReRz zd+!?z70DMHm8)tPo;}MBVaV6{q#fGC#xO63;ex5ek6R)P+KJ2!sV_w=s(Kg?y#8RS z#V~E@;qX-~ybM9}au_}^FI{->`Z5L!r5uJ2$~zmC%dHviOmeS_h_6|;gjt^8mO1t%}_Fl2#u9&pqv@DabOW8haJ|FciIIIHTN(4%+; P1_lOCS3j3^P6