From 84805b92cbddcac939e9eada71fad1161bb84f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Ryb=C3=A1rsky?= Date: Sun, 1 Jun 2025 22:13:02 +0200 Subject: [PATCH] Start atlas --- CMakeLists.txt | 15 +- assets/backgrounds/00water.png | Bin 0 -> 2150 bytes assets/backgrounds/01water.png | Bin 0 -> 2174 bytes assets/backgrounds/02grass.png | Bin 0 -> 849 bytes assets/backgrounds/03grass.png | Bin 0 -> 863 bytes assets/backgrounds/04grass.png | Bin 0 -> 854 bytes assets/backgrounds/05grass.png | Bin 0 -> 855 bytes assets/backgrounds/06grass.png | Bin 0 -> 860 bytes assets/backgrounds/07grass.png | Bin 0 -> 855 bytes assets/backgrounds/08grass.png | Bin 0 -> 858 bytes assets/backgrounds/09grass.png | Bin 0 -> 859 bytes assets/backgrounds/10grass.png | Bin 0 -> 878 bytes assets/backgrounds/11grass.png | Bin 0 -> 869 bytes assets/backgrounds/12grass.png | Bin 0 -> 868 bytes assets/backgrounds/13grass.png | Bin 0 -> 862 bytes assets/backgrounds/14sand.png | Bin 0 -> 2521 bytes assets/backgrounds/15sand.png | Bin 0 -> 2528 bytes assets/backgrounds/16sand.png | Bin 0 -> 2541 bytes assets/backgrounds/17sand.png | Bin 0 -> 2524 bytes assets/backgrounds/18sand.png | Bin 0 -> 2496 bytes assets/backgrounds/19sand.png | Bin 0 -> 2504 bytes assets/backgrounds/20sand.png | Bin 0 -> 2516 bytes assets/backgrounds/21sand.png | Bin 0 -> 2489 bytes assets/backgrounds/22tiles.png | Bin 0 -> 802 bytes assets/backgrounds/23tiles.png | Bin 0 -> 814 bytes assets/backgrounds/24tiles.png | Bin 0 -> 734 bytes assets/backgrounds/25tiles.png | Bin 0 -> 739 bytes assets/backgrounds/26cobble.png | Bin 0 -> 873 bytes assets/backgrounds/27cobble.png | Bin 0 -> 1034 bytes assets/backgrounds/28cobble.png | Bin 0 -> 990 bytes assets/backgrounds/29cobble.png | Bin 0 -> 837 bytes assets/backgrounds/30resource.png | Bin 0 -> 1450 bytes assets/backgrounds/31resource.png | Bin 0 -> 1503 bytes assets/backgrounds/32resource.png | Bin 0 -> 1471 bytes assets/backgrounds/33resource.png | Bin 0 -> 1235 bytes .../{02silver_ore.png => 01silver_ore.png} | Bin .../items/{04gold_ore.png => 02gold_ore.png} | Bin ...{06platinum_ore.png => 03platinum_ore.png} | Bin .../{01iron_ingot.png => 04iron_ingot.png} | Bin ...latinum_ingot.png => 06platinum_ingot.png} | Bin ...{03silver_ingot.png => 07silver_ingot.png} | Bin assets/tiles/3furnace.png | Bin 0 -> 557 bytes items/item.c | 119 ++++---- items/item.h | 35 ++- main.c | 258 ++++++++++++++---- player/player.c | 77 +++--- player/player.h | 11 +- tiles/belt.c | 11 +- tiles/belt.h | 5 +- tiles/furnace.c | 47 +++- tiles/furnace.h | 8 + tiles/tile.c | 182 +++++++++--- tiles/tile.h | 98 +++++-- tiles/tilecallbacks.c | 13 + tiles/tilecallbacks.h | 14 + util/atlas.c | 80 ++++++ util/atlas.h | 23 ++ util/audio.c | 113 ++++++-- util/audio.h | 19 +- util/font.h | 2 +- util/perlin.c | 52 ++++ util/perlin.h | 10 + util/util.c | 2 + util/util.h | 3 +- 64 files changed, 954 insertions(+), 243 deletions(-) create mode 100644 assets/backgrounds/00water.png create mode 100644 assets/backgrounds/01water.png create mode 100644 assets/backgrounds/02grass.png create mode 100644 assets/backgrounds/03grass.png create mode 100644 assets/backgrounds/04grass.png create mode 100644 assets/backgrounds/05grass.png create mode 100644 assets/backgrounds/06grass.png create mode 100644 assets/backgrounds/07grass.png create mode 100644 assets/backgrounds/08grass.png create mode 100644 assets/backgrounds/09grass.png create mode 100644 assets/backgrounds/10grass.png create mode 100644 assets/backgrounds/11grass.png create mode 100644 assets/backgrounds/12grass.png create mode 100644 assets/backgrounds/13grass.png create mode 100644 assets/backgrounds/14sand.png create mode 100644 assets/backgrounds/15sand.png create mode 100644 assets/backgrounds/16sand.png create mode 100644 assets/backgrounds/17sand.png create mode 100644 assets/backgrounds/18sand.png create mode 100644 assets/backgrounds/19sand.png create mode 100644 assets/backgrounds/20sand.png create mode 100644 assets/backgrounds/21sand.png create mode 100644 assets/backgrounds/22tiles.png create mode 100644 assets/backgrounds/23tiles.png create mode 100644 assets/backgrounds/24tiles.png create mode 100644 assets/backgrounds/25tiles.png create mode 100644 assets/backgrounds/26cobble.png create mode 100644 assets/backgrounds/27cobble.png create mode 100644 assets/backgrounds/28cobble.png create mode 100644 assets/backgrounds/29cobble.png create mode 100644 assets/backgrounds/30resource.png create mode 100644 assets/backgrounds/31resource.png create mode 100644 assets/backgrounds/32resource.png create mode 100644 assets/backgrounds/33resource.png rename assets/items/{02silver_ore.png => 01silver_ore.png} (100%) rename assets/items/{04gold_ore.png => 02gold_ore.png} (100%) rename assets/items/{06platinum_ore.png => 03platinum_ore.png} (100%) rename assets/items/{01iron_ingot.png => 04iron_ingot.png} (100%) rename assets/items/{07platinum_ingot.png => 06platinum_ingot.png} (100%) rename assets/items/{03silver_ingot.png => 07silver_ingot.png} (100%) create mode 100644 assets/tiles/3furnace.png create mode 100644 tiles/tilecallbacks.c create mode 100644 tiles/tilecallbacks.h create mode 100644 util/atlas.c create mode 100644 util/atlas.h create mode 100644 util/perlin.c create mode 100644 util/perlin.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2629265..da350bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,8 @@ pkg_check_modules(SDL2 REQUIRED sdl2) add_executable(factorygame + tiles/tile.c + tiles/tile.h util/font.c util/font.h util/audio.c @@ -17,13 +19,20 @@ add_executable(factorygame util/util.h items/item.c items/item.h - tiles/tile.c - tiles/tile.h tiles/belt.c tiles/belt.h + tiles/furnace.c + tiles/furnace.h player/player.c player/player.h # Ensure the target is defined before linking - main.c) + tiles/tilecallbacks.c + tiles/tilecallbacks.h + main.c + util/perlin.c + util/perlin.h + util/atlas.c + util/atlas.h +) # Define the path to the assets folder set(ASSETS_SOURCE_DIR "${CMAKE_SOURCE_DIR}/assets") diff --git a/assets/backgrounds/00water.png b/assets/backgrounds/00water.png new file mode 100644 index 0000000000000000000000000000000000000000..db6fec35bfa78fcb914f5b154fa4901ccff209c9 GIT binary patch literal 2150 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>U|>t~ zc6VX;4}uH!E}zW6z`$AH5n0T@z%2~Ij105pNH8!ku$OrHy0YJ5=M)zV-iOykA6J={*i!_!|MMIu8ccsf6ts^Jk77@T*1sO*9rn-Sk7`f zDde_p5#?a?E^hJV;DfsM8Un&{k^Uv|3cY`KA#=lJr9(kFteV+nzYq+6gY`Z|vSM zm6zRZ|Kp!eHcXD)Z?tt{it??JXN7t*;y+9ht_$H{lomEl?{O<&Z`t%-n<4O4r2`Ag zmiG%L1Rr~nvZHvOdgc9zQ#cOfOg+VZ(M*N?kq+-wpIc^GtFJs4wiO9gWj$JT+(n?} z_4a(5ef7_4Y~mR?q{99`nUc9zaQjJ~1~xB_Eru_jb2{j>MsQt!J@tG2|JjCCAHQ7= zuX_DMBI9+>H3bbe4^4{$b29vF-j~OS-ao$Tz086}No$6ME(sewy%M)Dnh6RvY1y$U z9eVsx;cniRYaMSbHeNF*|M%zf{mTFUv>6uj-}>#~#2hLi9HjE(%$icg4eu*n`>^-oNu-NI`36ockH@@rdZ+Yzasa>Uc)A#EwThhAX2#2QLMIT*qF^xc2bJR5ly?oBN*3j7@G{D@4CG_B1r1PvQ=P@}R@?c;{V!HzrsiD!a7m%9^rxu5`p2w}hAHQ+ zsQ-`(nd!SN6S(Os55iyXiAE^OQo@6cMXTjALek%^WQ*Dj1HG!F9ec5QET5MdEN z{%h51iP!QD*~h+%Zj|GFEz93zJAb~@=i7goO$_>)x8w^b7lq{A-}mv)-@pC&UW!YX zE?Cs`IKupj)9;?F8-L~$1xfvUeq#InzrA~MTh6}}KVi6Twb&aQPF-QA1UdG)IWr`` z)~QeF4KVw5`}V7UF@@zO%UgFj@Wd6g*oA2(d40Lpso7+G{x|zI>&K=Imu?(TdUi}i z`GvS!ZorTF|9g0!&9U(NlKWtJ!SB;<{~NOG3;aENwpf0Q5)8f5`zF7^nd?P^rMjkz zTSKI=;kyfor#!=3@4dA6Y%{ZJ!PmCu!kN;A`UYwnx4YeXU}1HSd0pF`Ih*IT%;7XN zI5*j1y+EAi7fvoiw*rIBp-qO@CrC~-wBy+M!14MYgT*J=OA4&&oHQk;Zh0l>bfAct zA2{pq0%jF&UgRhM+!T=XH%Y>&y42wRi*UfJXr zv$v>zwZ=-+X`m|3k|!KVfwE z%!;>SzQ}$eXnWl6 z^Z>JP=2tf+UUG;#Xe+|Yn9aMNWsRQovf|@4d!6Tdzfb;?Jhy-16yc>J%Ow9ZI_Ai6 V$nZ_JXJBAp@O1TaS?83{1OUxv?jryI literal 0 HcmV?d00001 diff --git a/assets/backgrounds/01water.png b/assets/backgrounds/01water.png new file mode 100644 index 0000000000000000000000000000000000000000..df8c4d11706560317ccc23faf229057c8dd3d0e6 GIT binary patch literal 2174 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>U|>t~ zc6VX;4}uH!E}zW6z`$AH5n0T@z%2~Ij105pNH8!ku$OrHy0YJ5=M)za%rE8~v8-|Vkk>!`XA)x7$0wR;w;gSTTyVR@J2QnOW| zYou;}ZQb7@Ok1w+UGt*Uzy1sQnOPlO7=B!r|9||a#fq&)r)x_W zOYyaEEH#a*+vBWxrIxvz!+mX&=o-@_J%69Jb!_;*jc?vpg|`dp*S&IO5j~o9SVzC- zwC79ViB8izr{r$cIAEN>b8FjWd43E3PZl3|l1h>q!j{i6e3o+Ctjgl{SJs9V{ic)F zxL8*lIp})7@NLM=jUSH4vuk?&TfQmmub?xG(l48EXTh9$WAQv3NOpFWk~AzyE*V>4V923%1L@zf!CI z+~s$J#~t;*kGp@SxK`I5e%<+|&z4bAF6EE?tLWIz>yLVf*6_>{4VhXf^Pn$RdRuNy zea(X3eqsk}te98=zH>hkE_V+VKN0n~#>RnV?XfA1$LFq5VA}RJCtu%YpB1Y|=qCOr zyNvDjpRf~~+buLRV{M0|mHMKt()Ta^swhdk%el_2)FiU>jrbp%3t1k4i}*JQmePnQu@~%ofr1+7ZCkpbCE}*^~8c1ib`8PHD5Y+)l|F3(Bji` z{eS7!6ILhhVM>_nU&D1u+j04Af3MpPqEU|K%Kv>1?bp1caq~p`gr|NssaLaV|2a&2 zy`*NJ;aBdt(_JU*D}FJ5%jwolZ)b2VpR*#&=esPYdrm|6FPSO1%EuS{aB&G-ow3hp zZO-jukDs$&y0;_GdanMlLWQ+XV$U8gh?DYhem$wkW6PTaR`rPuGlcJ!`l>A z!&0KQf#YvQf~ER|2Yv6XFIp=$ol!V)G3w;R9*O;Pe7f`so+WISKOzvaPmtec-WKEL zAddw-jjIi++btDkURvMcdvmC0{k?ZR=k>QpMM*!l@Cg0#!Xq}y@qU2dp3-f57Dj#e z{;<8B2$jsyT9?Y-%Yb6Qj1va{Yq}1oSw4#?v3^0N|P^5YwytVy&rx(snUooaPNo$(vkq4{h$Aq+>q2??~qkVU{Hm zk{>KRP-4S0lX)BO+sPtN|8NLgi*Q+4s=9vqck^R~2E}@+EE2UBs+XUBF4eBFH22DB zKke2qZPtqVYUk+W)8aZzcC4K4#wS_&t_H4Iu%2n{<9ADb>x(UAPp{Jvj6PVL^!U(& z$&bRn$)B1cy+T3GMa|Hv(!_4Ry!TI~6MF(y39K`ax#+0&gLBi{3X91O8mA8_3kV*o zoV1s*b8bz*$_sowT@U6wSLh1b-h0p5(CX;=6t=5cE>5~HSjv}+C5wGH-j%?2=4bD< zQ%}Fn{ry}$Q2xn|32z(r36^bmFTHB(#p?7g2j@&Sd@iJ`b|8YxzXFFQ#mt zOk3st$usACxAt)l{qgU${$u zCUI0POSJNwZF>YW-WgHr+7ng{kQ#-!m!FHzO&EttJn~$-*ck1d|SGVn6-=7w=MnG=L^QMW#>QxHUxz2~$99LN1 zyTiImVV{nzvgY}uGdw~@O_$8Nw5A=Kb8bJQPlXqM!jlEn3=9kmp00i_>zopr0EMs` ArT_o{ literal 0 HcmV?d00001 diff --git a/assets/backgrounds/02grass.png b/assets/backgrounds/02grass.png new file mode 100644 index 0000000000000000000000000000000000000000..b2fc2e6b180011a4941d69d18bf21fbc6a62d419 GIT binary patch literal 849 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tGN&|dCTm@|fxf8j0qImc{ z`T4#1`2zR^oCLTsxCHG51>6J#Tm%Fh1o(XT1g!;mVt9m1goG@Fg!P4aLwSXagoMn5 zcmsKPgL(Pg`FX>6g^h)I;&`}|xw+%HIdeHhbVNk-L`1YiL^MRWvbZ?&Il0ofIEpy_ z|NqYyRg=fSz%<>{#WAGf7H?PNrpF3A46R#PQkF1m)SdHm?abfth4;eC!%ZEc7&@O$ z>g-y!^;6fg@WiZa|EX%1Mr_ZGGKZ`v%{k2j>K6|2D zjl~){)^Cxo+WacSd&;&v^Kscz>=m#`;KvE?DUGN1Dt)yuT9SRx7>-1IlT1e&1rg&9I={3_Cc}x%Id?-Es-a$^6TxZtz=D#c(|MQiF))p@9)cV z0yaK)8U0%^`LXOdoz!0(tJ>}AvfuML-@5cRBJt1<#=H5Cf`yOr@148u?#mf%z3y$- zpVu$^6k?8KJxcQN-WAWrKAqgIDrD}J6)|i;2W3vAzpLxxJ4BfzI zX|q?^1*JTbUs~8+Y*f=G?Yz5SWxR9%kJ=5r-N%ED?X*19ensuf9PfVuYLQM~_89P| znXThs)3`f#o05E$h}zlZd&6YThi-IZXl=38{}dj-k-_Q3vP|m^hRS|srJ0NE3l8j7 zzE_|cE_+rnD(oTSmpyMUPmb+7{#-^O8p6&VuQ)vSPgTHL-8)NfMgPn? ztKRIgy2aFf+6%kRPY1J@D=OkoOR(u)H6L9qIme-`FR6*xiYu}Z3X$g`S}9)M6^WseE5VcgaoYxMf5}jTm%H11VnU1 z1nmU{90a)IxjAz=d4qX{^@X_;xrK~`xYD?|leu}rd4)`bc;a|OG(!J2x>!_O9KKu#n@5>^p|HiY)#t zU%DGFdKC*k**CK@WX0dlm3noF^WV2OmFoQ4R8g_6Bc|xu=hk1-*afTf9=K%Yd#F80 ze7@|~#*bg_7-i_iD883m6UoZB!YZ|6Ht!_$iB6jNtUGqOc!yQ!DW6$3b<36-#|pk? z-gPPu>&~lscSPz&#cIi{boP3|v?AGGu{*nA=^QsrnYi_}kEZ?L4^7xoIX^lj#r(%2 zkJ7R|v!`?HJluU)Bjee%{M*wLn|%^+a8%!`EEbSe5_2 z{A?EEf>~?6?2DE!Qu^DyHe*llf3IVle$1xY-wKW@Jp9|M=FWC5T664;0B3LIv)`}Hdspzoly~;j4^JCkwdy^5`FNe` zcD4OC8M|w{GdJj7UirL1V@pk{Nlx}M?FT!xj)mM5-1u76OLPDKXth_5@?Dxd8@?@< zlF8cr++b^!NzMPD_Tx9LZ`8OlDqF;zDQC9KwUPbk+4gsl^3nvZF4*VWsUo2|VCtvV-psT)RR|D&$iU0UNUovG6 UJ@6@wfq{X+)78&qol`;+0NnR$v;Y7A literal 0 HcmV?d00001 diff --git a/assets/backgrounds/04grass.png b/assets/backgrounds/04grass.png new file mode 100644 index 0000000000000000000000000000000000000000..cca5bc94a202852faef45e518c64eeb4395872ea GIT binary patch literal 854 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tGDgt~$T)7ju`Q7<>19=5p z1o(XT1RMnT0{8@+1bBmag)D>w+yr<-dAZWK1g!-HZ3X$g`T0Hhd1813?FEJPg++8k zgiM5Z!+E(fxOn1tgp7oEqIkIDxj9NWgv^AvleszbIYl%?ICD8gv_yoBg*l2ixU#rJ z^hEyu|F1FSZwmth(@akn$B>F!qL;WQ-L~Lib#PFS_r1ix=()_M$Yj37&EN8c+Tky< zl;TuU9ksTfdOA&N$^HCUl4^@f)D>F7RUg}2JFB?2Fd|uQ+harNvz?j8*cTUQbvSkR z{OJ+-lNilj_+#Z(7hZNZE*+Jt7yo`u_}3tu@#oOFxIHl<9?L7w&u9K8bz)Xa)EDD5 zS-ZFF+q3n2ZOfciQ>AAL$}c_iPuSg_#^12`(P@L0!a3UucdyX9Fl9@Ft}0`k*YmB5 zy5zn~M6Hy&w2u88+q(}RGFsf9XW#mmWU8i?e*0HQbo8cmbAKzdYTvvVAlUNT?1RER zz0A!^Y#O?D_f5!~Gv9H>q`1^KyFYT}MI}_%*CoWQ{JPQP)%vu*ioeapT{d03qtmFrhPU4OKn4lc0MIC`M{^H*yvk7B3j>q}c}X5PL3v!JR) zw%_63yJRc$C4MUOE}WZ8vE#h<-Y3Vkp`#QMOiyP zZfJV+DCSB^_KH1Mw(Bq0;IT>XPQ}Xtg{Mz$1l+lE+Cq3^V(GgGX`}eP+3$7pFDAya zZlAhKE9q=-n!&BJhKpY@7hMlg+A(RC+M(*qjWeugFqZ~zez21%?TOywjOvH#;WJm3 zibQHhU(pj%zPuv&%ZYvN=dx_`_8iFl^IK>7hKtMB-m^{oYIRN5_t}$4@pnJ>?+ceK ziFnJ(@zSTZy`qWf#et904yn6tp1%5g`i)PQR>)o7y?t`BrJnSQ|IEG(2F@EEh|gwV PU|{fc^>bP0l+XkKc|w8h literal 0 HcmV?d00001 diff --git a/assets/backgrounds/05grass.png b/assets/backgrounds/05grass.png new file mode 100644 index 0000000000000000000000000000000000000000..8d5a36d41e6b3969ad2c91f0cb2429de862bb945 GIT binary patch literal 855 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tGDgt~$Tm@|fxf8j0qImc{ z`T4#1`2zR^oCLTsxCHG51>6J#Tm%Fh1o(XT1g!;mVt9m1goG@Fg!P4aLwSXagoMn5 zcmsKPgL(Pg`FX>6g^h)I;&`}|xw+%H8Fcq?=5mVYh=}Nkh-it3XozrSadGBza;0%` z6mk6j|9^XCMFax_(=1OH$B>F!yj{_g9vg5lOk{LD)Z}r*tZ35NJ!aMamEYBrmWMJg zVVz(R&lNazi&MO|G-@p7fr&FL^#Ldy2gR77NXouDa^6(fZrVnzGzlm-}y4UKen% zaWa?i#xGra%l2%T5h)}dXM4D|``FxM+nE6&8Rt%V_^)GH^2NHRK(hMCHlJx)?4Oc4 z{;>bzn8a9-RGc~?&ARDAxB7C09|doF6BugV&A5H<ijy%%18E55A9w0h5%3g#n*8vP*$i=~BsAIopR!$bfR{@YMbw(q75sUclU1HrTyj7s`7@V2ao9=+!o-*a>hpD_N@g9 z-oXd2xT&8%Vm+_6|6D0Y1|P%LsKRUN&+adqH`8&!*Ro*I6`befN{?(>wnj+y+%2ue z6XG0R*_?d9rWE@#ZP#f>lSkrboly1%TDLC-&-*?l0PyZbfzgE2XkGz|wp`_23**_T= P7#KWV{an^LB{Ts54bF2J literal 0 HcmV?d00001 diff --git a/assets/backgrounds/06grass.png b/assets/backgrounds/06grass.png new file mode 100644 index 0000000000000000000000000000000000000000..c2dd0336669237a5c7a54a93e4cec098325b5fbf GIT binary patch literal 860 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tGssnsNTm@|fxf8j0qImc{ z`T4#1`2zR^oCLTsxCHG51>6J#Tm%Fh1o(XT1g!;mVt9m1goG@Fg!P4aLwSXagoMn5 zcmsKPgL(Pg`FX>6g^h)I;&`}|xw+%HIdeG~eD;axh=}NkFzD_R(Gn5S5aG(=;>_pd zO5@@v;`smnzk!VT1qKGDIi4<#Ar-fHyCSFEHsCpsQQYa;-E-u|vN=mPmsS0jF5Abj zhrJ<_O=a3?r;w5;?e!UTqRX~@TUjR1e_QRhVCA1%Du&GKFHS33vrJrk#_^(WDfgz< z>^s-=y-Yx_gTMXi<^y(KcQ0_hZ@SrcZBA!L`8+j^NDiB6mfjC+*4<9)dgjO7FY(Sj zZlME%|1zH=x45=l`f%vgyUA^zq?o?zYMYmE?`@qy;c^U)EjS!5s7y05H0%rBIt8g9Si$G4ZF9f_^$ z#ZLHdTYB#9{7sHWo0jLlo!!%0ZXvFDSb3$~uOHFX-}@(+m0O2N2;5)r+qdS0%THf> zzw7Txcn%-icp&?8z6NLX(zD)$^DnKtt$#K)0KH__xP~mUhR`xK+-h2U%+6}$k&to3lk$LEwC7kvz zc$#_QBum%x+{Ff)3tbHyUcTERT6uY~!ONms*+(nZb(solsJ^Y7c>mWKNr52qx!amK zCVaP1P%r+%&i1F|i1fNAt1ilXN)CL;P<8Ly!^yFI$A1fmFg#0L!W@zRhI8#K_df^b z{;3LBt9y6pt^Q9}XYsS04C8$LX~yQ?3Q>Gv+z&qNo5s<+?TQd@!nzF~y*Hjy>1a4K zcjG!yv6Qvn8iU{VI(3xW-2QiQM+xg>76W}&_sT_o%uEy6T5r8N>$A|OV4IHZ^HQP9 zIR(?AB&NP$iaO@WXC9Dy%zS%n*D>);OT*v(-nCg>XvT&$K?(2I1a7ph*Ij?Hn4kCB z9;T(udfcmbrG7MC_Wj@XTa4%0LjxsFvwF>lFLq$qHot!U-}zg__idOXeX?Gc$FS=c SYX&GEF?hQAxvX6J#Tm%Fh1o(XT1g!;mVt9m1goG@Fg!P4aLwSXagoMn5 zcmsKPgL(Pg`FX>6g^h)I;&`}|xw+%HIdeHhbVNk-L`1YiL^MPgviEUiadGBza;0%` z6mk6j|G(GQT#bQ&X_lvpV@Sm<-mb_=uPt~EXcTw4c=sH+v24!L>(h7s7hJxdA%syu zx8X?9Nf+14QI$v2!V_P;_9$(bao_AFfB)n2lYTJmsXG5OVs@NQ!pxsC2lr^-EQ)1$ z|5xEm2Y>U`MF;G>?_S_|-*mD6UT4AN^j3fGG2V<^75O4;TjJP ztke|V_^oMg$({|fB7?%?Vjt{teR^)=Jf9W5ndeS=`0H_9d66&i(CS-9?#wl={7;Sw zL@w-CkZn*8%jb#8bq}ce_(X(7{$7=;1jBr5ue;WADrdZwl@)A!ds<82FxUON6X*Y# z{MzV^rgVr-#jR`gD+?dXp3_O4#eUUU{{JfpHoaQOw-Jd=|C`>KZ(P~=X!xm_2 z+UD9G&fXla!5F>tte2tp#dWvk*BFX@tl0XPSKzpU z0=D{R&YkJic4D5A=oUSZ-G2Nrx(jItveX%zgZ{* z8E;{?+Qa^cH{2-nJ=?|2T-FypZ}azlTV!Lmd;x>Ob4j)fZt2`Fw$A)5{VwHtbJp8L zwXJ)nu3vjQWx|W88=f5(I;<9G7crZ8ckk96<}Y@Ny=U-l?Tzm$Rdi$#k?Sd4J7MBh zv!fyI>f(=N&+l`8ZX_~`#bI+$!8G+#@xkJ00W0?2iwf4@JSUfWWRqH^lUrGI>;MR{do4)*sfpZMcltHZd`92t>7p;EtKm`^HpuW$M65lQZ5Tm@6`^F$T+tq zOSfz8JiFujZ}Az}MTxe~)DPHm(^SnOq5l7O(|?oy9TJ~bto&0xcekOXhs*RS3=9km Mp00i_>zopr0R7Nrga7~l literal 0 HcmV?d00001 diff --git a/assets/backgrounds/08grass.png b/assets/backgrounds/08grass.png new file mode 100644 index 0000000000000000000000000000000000000000..ed0130c72910d9e7fb30f8c060bffdb8df0a122f GIT binary patch literal 858 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tGssnsNT)7ju`Q7<>19=5p z1o(XT1RMnT0{8@+1bBmag)D>w+yr<-dAZWK1g!-HZ3X$g`T0Hhd1813?FEJPg++8k zgiM5Z!+E(fxOn1tgp7oEqIkIDxj9NWgv^AvleszbIYl%?ICD8gv_yoBg*l2ixU#rJ z^hBmIGynhpfBA|%%NQ7#W_h|ehE&`Vy~I7~wgnHXgM)&+?prmop*wozx?(!KAbY;9ZSoHRVj0h@3LHC&X;}WaG6coVoSrbKN3Xz zSOYUAPMkln$v)?`0-ycXX)7Kc@MvsZVzq0(+!^@-rr!7)H@l~IGrcr<|Fif&Jagpa zfbP1s(A??Qrf>hYvwySPS(C&zELAz+ z{c=xO$i>HU&e~U$-`{c z?4=vOudMkjUiQh-O*8d(oXUx{CDC8Ea{Mo-U#FQaov~m8{$iUr%yU37`@}2``L}6`j4y3|9_kx^)F}t|2vsm zYGN{8N=-YzRYlV?pJR9CuUr3?`bo%b{V1_QTjFDa@)XDqj6=dyQ^y$RmFPqQE8Q$C6DQk7)V$kBH8az8AuU12tYYhsp>;evqq zE28pj)-2dzA#Qr}pYzkD`)V0mT_=@3Jm%fbtf_vy?a{NiV>3H=R~H|@HR0#0*y{Ys zZob!AJJwza70jMz)O<0oPcl?~!tP~42d7>!5xnQ!ee}*jnH95Vv7Q$-P%f8rzs+sG z^OVop#V4X}E-ITSG1El^bjjnt?Ac{Q*8 z%idE94rl2*c%;9wJ|ZmQz}~lA#7=Y8-k4gu!_%g_ecf?y?_8PFg*P4k%NMvCBygVz SnajYyz~JfX=d#Wzp$P!9uyoo0 literal 0 HcmV?d00001 diff --git a/assets/backgrounds/09grass.png b/assets/backgrounds/09grass.png new file mode 100644 index 0000000000000000000000000000000000000000..3da753edd5d3f0ae2825077cb7574d815314261c GIT binary patch literal 859 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tGssnsNT)7ju`Q7<>19=5p z1o(XT1RMnT0{8@+1bBmag)D>w+yr<-dAZWK1g!-HZ3X$g`T0Hhd1813?FEJPg++8k zgiM5Z!+E(fxOn1tgp7oEqIkIDxj9NWgv^AvlewodGjry1ifD*%=5mT?i3l4Da};rK zWpRn_+o;ypDfl@?do1}Jx7Oi!OHGi|X&Cnp^r9w)V zohy-H?KT| zEJN~_XHTs1-nr)5`L)*bXCArh&M&n?G44U`6;s&*`T9SCO4MYY-+695g;Qv8%7kMl z4yZodBQ7%S@xxdz4fgBR$0HqipFJ~8$S>iy)e~CkaM0trnagykt)7dwxvO(lg@{xf zNPc^Rd9C^NiOH`V4m$5D4)*frmylV1XS;7EW9Z2XYi;cf-bF;u&&mkDy6y8fUrD*G z;laJN+0Gf;xT7~*tUg!ycixTW%)DJEObz#H*h@Z_TU1^nakqYtf=Abi(?+4s^{*bc zmK7_zo?O1<{k8V#F?*MMf2sfT<*(Ii>fI+?tSz1VR^#OLoSpaIXX#f@{j>Ss&&rm# z%Y}=N?asEEE@SCF^P!dZpQ!)bx(jAc_u9KyN8yKG&#~M^rF*93S9Ao)d3o(fJ@VXQ z*TiJ6%cuU^Bre?6iYguRUYZiy{0~q3x33IJ+XM+4Chd>pB<`N+7q0m zH72FMnl-Vvcaz3?(bi?3ws9W3s;n;@I!lVDLsKd&C3y3l^Aa8#B}{chJKtSfvUOHi z{spz3N={#veM=4=<}5Yi(cjc8FSOZus)Cl`1KZH~S9o0dHg&DK9Kmok;n?35(aY3D z@_6s-G?6a?v98>?QAe z+}{f_G9?T0L~gNd<6ykd@c8V^a}PrGg{A$kzZBAc_I>x&$u(AaZ!`Wgv#T4-*yT1~ Rje&uI!PC{xWt~$(696AiYA^r* literal 0 HcmV?d00001 diff --git a/assets/backgrounds/10grass.png b/assets/backgrounds/10grass.png new file mode 100644 index 0000000000000000000000000000000000000000..21bda5b2460b566c217ab4846d7581798a601637 GIT binary patch literal 878 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tGS^|7RT=_lud182k%!GuE zg?U4H1>6L9qIme-`FR6*xiYu}Z3X$g`S}9)M6^WseE5VcgaoYxMf5}jTm%H11VnU1 z1nmU{90a)IxjAz=d4qX{^@X_;xrK~`xYD?|leu}rd4)`bc;a|AFffQ{h)iW>=FI2h zDB|EK;aJ4Lz?H=%sw(>b|Nkc;8S5Dsn3j6FIEGZ*;yuq@wb+WsA(1nMWm4AaqK1S? z7BlVAs=n_(aGUqfb87~Ro+)a_DkWWdBT_3)pPZ)j?8JM9n|IEf$dO$9Ddg&_RNvJz zuTFZ+I>Y$LHsPFb`Rf7R?lDm+MCmv18v)k+Y%UO{?K*4l3KoQjYC+nu1!)7&ew`X`=0o< z%;UBQyd^b>v+~BqRb@Fgw%2%OafhFXX3Gw9WLV`V`6#RTiRY4lQ`wB`^e#>kop+jJ zbEc}U?tFoHhZ9nj1%--#OS&)0ve zyVTJstDF<-JVA67UviUnjQ8KX{QKXToG$(B^z)pym3dn3oDZ+=Yt{eXI^XWhYMcKz zS@Q#>xSE!h&J{j3{Y32@F^|u$`od21bM)}2tbSkoVd`t`oGQ5wFAq0v4Lhx!-NvF6 zseffI|BbhsIzpf4?Y|Xc%n&k}dvD&g_BkH)$>JvWR@AFJ{_@-)U~^J=IFHc1xQtEd z4}7lfQN0zWuq@r}Tky1$qXvdd;Tx|;t-h?*@N$d##|x=9Z?^K9ZZWx3ll}G~-|qRB zH$-v9zgrRaLS){(yr`n=Pm^Ug+?YP2ZjDeJACEUy$@fVW#75KPxvg!+LKUkbf4$RhK~hD*d@PB zPFwL@=GX7}6Z0>fu({n38NbEBe4Y@Cs+;E-E%EAaA=}pqEODRH%2MU9X~FM@(z9E_ knDQ+bUS-vp(DS8!?iSN`zopr0L|@jmH+?% literal 0 HcmV?d00001 diff --git a/assets/backgrounds/11grass.png b/assets/backgrounds/11grass.png new file mode 100644 index 0000000000000000000000000000000000000000..e7c9faeb9101260947ec0cdb00f1e07a8fa836e9 GIT binary patch literal 869 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tG>H>U1T=_lud182k%!GuE zg?U4H1>6L9qIme-`FR6*xiYu}Z3X$g`S}9)M6^WseE5VcgaoYxMf5}jTm%H11VnU1 z1nmU{90a)IxsU8%;LPRZ4dxZr7v@gn7BUjzO5@^A=H?CO6*3XxiQ^H`5aG<{+I7*l&a9ah6P|ER z;J4$4%Fl(|TT6~bU0%?!w#j0SjdaH=%NRoz?h{AkB^T+n7pJmrl8R0|=p?wp`VQkQ zXO4LsFT|M_Pkp9zqSjdWXUnbsHs_D(zxn((U_;;0crpI-q7%K(RqYG7|4G5=JnI~d zx4b7?Y~I`nu{N7GFPiOatM(JyX4BOQ457<;3O74`QVG&{x|Sh)dd5kv^QkR2w@jKg zO`qd@;)a7$RqnjsnZjwf+I6*et8%FaXDmbM8_kqL-VIYOYqTDIz19DY(EmkTJ!bEV zyL;ZS#fz*w*H`{oNU^-4P=fXBonN`XjdCVvt~opDRoq%Dm$P+Jr(XWd|6P2sqf=Hn z=eE-X(N%oOP11L~{^rTw?`Lwl^t08^bJ|+wX|;1cJiD*;-|P3w!+TCK( zn(G>?^50jV&0<_IYt5H^(egz~e|y(v>nnDO&e#*J$};RlLCdsc2zPP*f?yFEzcmuWDy40|Nttr>mdKI;Vst05(H(W&i*H literal 0 HcmV?d00001 diff --git a/assets/backgrounds/12grass.png b/assets/backgrounds/12grass.png new file mode 100644 index 0000000000000000000000000000000000000000..1912282efe6a861537e7406a72b0cd60cfe3a7f1 GIT binary patch literal 868 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2tG>H>U1T=_lud182k%!GuE zg?U4H1>6L9qIme-`FR6*xiYu}Z3X$g`S}9)M6^WseE5VcgaoYxMf5}jTm%H11VnU1 z1nmU{90a)IxjAz=d4qX{^@X_;xrK~`xYD?|leu}rd4)`bc;a}D>|hYl5aG<{nR;yIA>Dq(|d=W)#drrcXU?^OPd zfB2e7MV9eF!9o)wzt0L;N3;&R`04$*>h6qdv~*r0 zeX#2DX`!sl{%qf7rEOe~HhfM|SoOQ$h=7iQa{Zo}R{7_qik`^QX=UYhU9hfzY0GW_ z8-WY_Y;M|TT_^nBtsS)R+xg>mzZ+|<%voPY{K+i*9wWH($(rB7UscPMzQi2pymV`V z&xym|T(>4a{WLMc#j=X&Kzw;-_o0 zNxCL@iIwHNXR1zq6-5%PzIT4*{xr*(pt;6((yOSoRxW4vNuPT6b9t>?$-yIig4=Wy zPjH2{dpAkn@%o!9zyCXv)1{xCexA>kGEe(z^yqAT=)bAouD54Se4Y_*v)Dw0#rw38 zYLEQH->GvIeY>rr6z?k=7){XG=X=n6-TDn5cQXID-nTXEGs8S*|YCL%IO0$S5mmmC!_qRDICblmtVuyjiN|Fg=aAH|gp zYB;=h<~7@TwRlFH>U1T)7ju`Q7<>19=5p z1o(XT1RMnT0{8@+1bBmag)D>w+yr<-dAZWK1g!-HZ3X$g`T0Hhd1813?FEJPg++8k zgiM5Z!+E(fxOn1tgp7oEqIkIDxj9NWgv^AvleszbIYl%?ICD8gv_wwqVGuSJ<|yLe z%Hle*gF!@3l?^Ba}69M|bwxbS%zOT`0M56iEqTmSFgl%4u+SEBsXV+R7B zHczxnt5~M_<;#~Be->slZ@jIkT3yIoRpKMkweaRv3P20 zR+El6T)q7=YRSDIz13ffU(204^XT2;$w3(%;)l#$&Ejj=`@s7m^J9zWcK1WnJbES_ zluBmMTlhIHph<@Reb5S-h4-w_apf79uW#A=(e}%Zh$)>XBd(jdOqbf~`FNXqx}vQX z*PIU#)@6^r@|8ZBVHNOU!n=i<%a;qgb%ejWy=+gz>XaAP=E+NxuUYFq^;YQCw~4=> z@$}zXz4F+;t%^3cTYGc9%qx!Dzx+YUtl4@+zK!v7<}*I`yJ&6K^Zx!nb`{MFTrat^;^Vtn=TwhRSbU~nu4>)d|3S(npM!S(ca1#oVJ>r2hVc5wTWbz$dR1y@ zKb|Hy|M81seVV@69p{)9-{Ru>ekIiIbH=;BwpkaPs|>D0WS(MjT^02t(Em(`X-CZw zAybVBD|tmu8ke6vA=a}wBSM?Y|7ou9iL1#GYU^xFB?N*^)|^a5FI-;HL zuPxa+D=hzl8sldtUzUAK1lx>XS2xYd>*$h3g*xOIx%$<=XcX7kovR*gIJzI?U%?~*&WF8*Ww Y>T57d_S}&$1_lNOPgg&ebxsLQ0DfqJTL1t6 literal 0 HcmV?d00001 diff --git a/assets/backgrounds/14sand.png b/assets/backgrounds/14sand.png new file mode 100644 index 0000000000000000000000000000000000000000..60afa1a61336b05281205ce33de57da9bce0c8ac GIT binary patch literal 2521 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 FCIDq6;LQL4 literal 0 HcmV?d00001 diff --git a/assets/backgrounds/15sand.png b/assets/backgrounds/15sand.png new file mode 100644 index 0000000000000000000000000000000000000000..29feadc0d119156850f3455fcd9cb476d7121733 GIT binary patch literal 2528 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 ZW%@U}EZDqPnt_3V!PC{xWt~$(6999p-pBv| literal 0 HcmV?d00001 diff --git a/assets/backgrounds/17sand.png b/assets/backgrounds/17sand.png new file mode 100644 index 0000000000000000000000000000000000000000..e8bfef5f99b1fcdcd0d9b0c3a8a5801deb72eaa9 GIT binary patch literal 2524 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4kiW$hCdQ-7c($0a29w(7Bet#3xhBt!>l~}c@n2Z_sPZca@U|?WN@^*J&_z!{$_AZ~yz`&X5>Eak7A<261=A=8< z5*aS+X46akFxRjY`w6&q-0}~+LhT}%q!$2rT14} z~o6JM7Gb(N@#f1LP2 zlSlaGxAQkXd}3+j6kqJ76|_H8>b5}iB(sU~ma5vU`~H|zZIx7<&O7<@=H2sG5h}d2J_DePpHzERAAJ(>up-c zii|t19_MN&31$7gVY92DX6A$S_Vuqg7+UuI_;+Kw(GH{gb-~}nGW7M|Ew}&ufrm{! zVWRlrv=^tg3zct3v}h{hzr9XJ@wJW5@y9<(Qs%t6TV{M#%{f71!ff6w>CXQzKmXpn zw`Yxa#+;OsndH?B2) zw&mQ(u4BevXn$_udIkID7N$SP?QJHyyzvXPk}jK_xM$h6kCR0Ltk_ch8!t0jiV7)C zbqh|lF?kppnk@1rf7gshkM*YPx+ZnLqJP8ZJ53B>>|5Wf<_Y`K*2hbG zOqi5<>UazHU6%UN^Ha4FW_NfvKm7UGdOXAP*okIw*K~E+`QJ7_6=|O`bJqC{%~@iu zYYTqJ*ZBr>{Si9cL5&OJk%KA=GnIDP z1ukt9U0E*2x1?Fo|54$TDN`-tGhM#wBtHIm^X)LBCy3@X0O-SZB{s)yl3ona3g9ZHE7|y(&C`%rR=R2Mzu` z$+l+!A7CvfGa!+f~#={m8 zuTD8PDWpw5&5@Du!D6D`fmg@h?wj!JY5ay2t2kVyxSZJ*TAp*ZK%$Sa=tyqmlvum- zulo)pZmrhd|93yruaMWz{`xVVzPC@w$ylVKg?&riizQ;Kz4=l#4N6R+e2R10z8;FhJCQe$mEh9$a%<^r*7fil=eQU@!ciR@L8DFJ6>pOSgt?%iFD;NxetCA=f&Kq)fBo0XXfP|zD09`WCWizw%?`!vc315BljO(u-rDD5_y4>F{BuR6e8ja~6q`E!lwA0D^VXNc zQ`FyoyPGuc&v|}!hK9nVdF)3Q+L?w4G}M3ZPJb*K?x3S)!spZPaC!Frn&qE3U$N&1 zI20LdP1(#N$l2Pm<#}d^*UmX?|39t2FTfL*>;L`y=i9M{GdHQvH(**RQfT!o^4jGc zE*fRdTUn#zxo=AKGEYDD8nXzWC{(@dzvu@WFGF?eh%L4rteYJY&^17P2v(KH;?ahzc zg@HwX8@?R+9M}=!c3#0n(euvV*9)v1&fT`JU;JPGHUHALR{dXf7#J8BJYD@<);T3K F0RRc_$4~$O literal 0 HcmV?d00001 diff --git a/assets/backgrounds/18sand.png b/assets/backgrounds/18sand.png new file mode 100644 index 0000000000000000000000000000000000000000..254e779aebc530265bd11490e439a82a83478123 GIT binary patch literal 2496 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 literal 0 HcmV?d00001 diff --git a/assets/backgrounds/20sand.png b/assets/backgrounds/20sand.png new file mode 100644 index 0000000000000000000000000000000000000000..7057fe2a0fee991253ba6a9cd03928730bd36862 GIT binary patch literal 2516 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 literal 0 HcmV?d00001 diff --git a/assets/backgrounds/21sand.png b/assets/backgrounds/21sand.png new file mode 100644 index 0000000000000000000000000000000000000000..97a03b7c01ae2338995e60f6e4cbc636e3df0794 GIT binary patch literal 2489 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 GWHtaww}L+a literal 0 HcmV?d00001 diff --git a/assets/backgrounds/23tiles.png b/assets/backgrounds/23tiles.png new file mode 100644 index 0000000000000000000000000000000000000000..3ae9c4d36ba2789413b599bbcef46296ba4a4df0 GIT binary patch literal 814 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANM!FFjoxLn2z&hVL%CV<2*8 z@%jdFBbOBuR5!3pTM!g8$t$5k(aWJi(J8GcL3itoYME-AV?EC}9z1;bw@kjIyThNM zgX7D+gA0rAU*6im&>((uzilyl!}sss#a;K7PYJpZ<($pCH7w_LTM$>(`@^m4c5G#1 zxba=~_O`jdrv&b_HjmD3p0I9eke|Mto!y7!3&lV2=r1c=YwE(HwpE6~OEc4CMa14~ z`BE#Zt-=>qRJtUb`m~Ix%CNoaxaH!dajQP=a!ZcR-p~7>rE9%N)fSuWYb2)z+Q#V% zHb!ecEshPp64m~wz<<-KfAWvtZTPTf{p(*z4)0~RhOK#RHRo&otKgd=Z9MbEr1w7G z-WK*fYpwH?D;50Pm(8eB*Sb>hy5NlUwAjL9>~VkZyxv>CxS?B8Ic>uiZquf!a~Bu! zO%3{xJn#AZ{^O5D7Ns69WMFt!d8|-o#&gN@&p)f4KVCRTJn-Ak8a_?NhV9$82irgG zdwf7w$!>eD^pq;Wt=IhC>=F;Jye=phG3{xqJwuJSV)w~~)Bk=tlp5LQoxL_$Z8vF6}~)yA78B%)Vuf6-&S!uh_Wjq^;-10QAuE{!z)X!f#y%83H?AJuLLw104QtvVsE zJ@fc4rLvhpr%o`-MQy+1>9u&`)ds7_y8l}e-^>2q7cazlJ$r2?N8`pco8wtK#8p19 z9xjc&e0KGQOD}l(=1p3Y5ns`eGP(8s)Ro)i(q09v|9Q6fpZ)P8o|>r@B^6pzd_p$P z`|kBUZK`X+qJp+qt!sC7EezjN!0Ook)0TU))I#xi(>ZIxY(l$)davnohdLbK-Z$|H S0|Nttr>mdKI;VstWHtbU@QU03 literal 0 HcmV?d00001 diff --git a/assets/backgrounds/24tiles.png b/assets/backgrounds/24tiles.png new file mode 100644 index 0000000000000000000000000000000000000000..67bd1e50bc18daeac46a1436932c39c5aaf29778 GIT binary patch literal 734 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANM!Gd*1#Ln2yx!{4rbY`}Aw zRnkGZqNVSGpp}#RX%1OMKA5{?T|(m@CL-ek?*b}gy!?cu(gH++p` zlYF+%=GJizt?i!BymFbBwzbPLuCVu4`y_XXEX)>Nu5RVh?WO*>P)6PQ$KkK1_+!K~ z%(9N(H0Zu#s>Rv7=L$pm8XW}&H%(^8*4ICtu}nF6Ic-VC`)`#~+umBtxTkGB^JlWd z(hGC_SBF2HnR&+H>+gHNw{vgH<&NFVbuCBMJJ0P!vB(<>!JW+Qv+w?}?Rz|h_d1*L z^4;2(?YAYHFBM_|JG@KV$_;_FuDi*WZ8rQK|5^ORPS+?_a-3FcY5R z!*xaUxogwzf2Druy3ezY@f!Twd!@l^p|D?bv*o#I=NV45@NKyoc!F<==)PxSrPUox zA$7eod*`I%BAZLyEX1^T>AKip}{kT;P*EeiWr{0ugE*I>}k&8sP_|$Q_uA2-F0X1 j>VN-g^{Pkm95bUQwqE0kVqjok@O1TaS?83{gvqD#@4frytS%1CGT!zz%X94;|FvJul@?u-x?b8U zR)0N8S?N--{bRfL=Tp2~j=cW5E7xnoRnVKfUTH`E?+*Dp?_5!A z_?zGVM02n8Nlx$Pb=@krcE+WXepPN<9$R~^P5j{Za1rCY^{=gbUpqOk$yk18&4o>i zdOD=b&j`qDZaB5lKv>Ok;_uDhiuNVE`>C_e=JZsaO6i}K%Z^`u=pimw#A3~4yfkmh zm$`z|w-=x6IQ+0W$-e&l9NRu+*GFzH#(Wc$wnm0My=VDKWNq&4eF2km5+`2xpi{m% zRmtSAra_v{4!}KSLmJO%Ayu06MC!A({sCh?k z^8CDcY*qKG@YjQ=xB>24a!6OSn!zt@y8-{Lr!cf@+&y;at`~YsNbiVBR5Xm_@i{S?WxqA*FV+Y?nwA} ts379+=3D#c-Mv_UR>V@}_nRI#?7#J8lUHx3vIVCh9vjOZIY+e8W literal 0 HcmV?d00001 diff --git a/assets/backgrounds/26cobble.png b/assets/backgrounds/26cobble.png new file mode 100644 index 0000000000000000000000000000000000000000..47e6e305a0e41b2c6f5a9bc1e8325d0b953cb090 GIT binary patch literal 873 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANOKDxNNmArY;$VY{b3HV|mu z%4Ig^!a}cv7ZX%>uuLyf-N`cbzy(F;cAlv}o2)M=oQd7HJiSxy9QzD@|LwQmiV89` zT$55_T5HO{@V)fz`|>AqmKXi{JDG3m#i+wlf;YbZb-w76xpwRSRfSOg& zcg@rfb54K%^MB?X<(26>>i5P4W*>d@;mMqd#cp0NnHd>8G{1h&y}cp#w!-mZ!HfTY zpY&vi5Op$L6}MXP($T+?Ocqvq}|{xxE8 zve8EYQLQI;9zTk6`Lg2c8mkjqGs2i0G?!{WbUr@gZ_Lsuc~=6TNVd*3$t*a{Q`Fxk zvwTWG_S-2868A*oo&{?z4PJDmVC`lZ1_O-+Nm`eePPiUrKdEOz_99`+2lb*F<~=dI zno@th&04;3&S!Vmip9ZI;p=^KmCo%vr77Dpi}6FElF;7B=2sjSZuF>2ElSD{no{`0 zM#E~=iQ1)8{%yHdAj2M&U12xhV*dFG8@am&3T2$1WVL*Hb>{nb^S;LtRYyPnba8o5 zw6mnW(Nj5TNlV_fmbCXd4{eUe1W0wtguDEacFQqnIi2}K_vaM5+^9RBpM>B;We4)=q-?h4AfNo`dB zSfF{8_dN65=P^>IlY5?5UOWGzw6`j(-|h23caw`3jF#Nl_x|s))!y%$y_zrx=CiF$f(bz-i^=7mfDXYN)D-CySPHKbQqcD+D( zPkP$pIro>}e*5i{(VV}l4CB1^tlgzJpYiv;axRG@FRS+MGwJ-&pf%@iS9)9e-HQ{0 eul#3fyAeL)<-e9Y3=9kmp00i_>zoprkl6s}->TpM literal 0 HcmV?d00001 diff --git a/assets/backgrounds/27cobble.png b/assets/backgrounds/27cobble.png new file mode 100644 index 0000000000000000000000000000000000000000..7ffdc81b7a040bb04d30866cc9bd5bdea518e689 GIT binary patch literal 1034 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANOKM?GB}Ln2yB!?mY9HV{}- z$i%|R%gW2j{)(fKxwysBsn5v$gJ21ZqqC!<VM4LnTG8{*g&974{>;qKVW@PhBZ$k1gMs0_ZPoi?{s$#@tKBtOw}!O{ObD8l z{$tPgzW(>`-`}5O%;58MYgob4If70*LRK`b5u9?|*znk8%X|AjFPg&i{;#KH`~%xX z1zWE;#H}{kcf7d&IP=!9J5kx|?^*BLx9`Cm=W~+xs(TCrvTZA4vt4@xlX-d!{Z8~4 zE}OX4bj|Co(pYt;YaVvHk4vP-h+4hoR19gH#qSc3xQtn<-j#{r-QMpj0~C1_eNq)$ zD&1HGM88h3pXaR^J8$tqZqchz%Y|CK)-E%=vpS&a%#p>{yI6XIkBFa*+U3n%5~5SZ<; z?To~t)u#fDc2(s)6`C6D_3~=eY&nbP59T;$=Q`OOn=vi>kH__MmS65YoO4(rnajTK zaSG4-;Oi2P@Ah2rc0E$mShfDSgj8F<=kkY-<0ih`yWjZ$^U^6ddraDVRN9WTwH+!x z7hLJZX?RTdb7o)R-EFzm6Q4S96r7tY@?q&?i?rj)SI*S6wI%-k^T(#OzYeTwzK?(essq-x$1<-4SESkP&PVXxu5W5!hrdt0AR5j71jIo~zI<^Z$V z&UGE0-jfP0bE-;P8%JE5``O`KrB-_J#h!5IE$SLkXR2I7rk~^bRdaFO^4u$Y3%oXN ziSzFA+mNC=InMX&-tXqZL5*wc)woX!{4h`YIP37!UF$N|o|P9Dp0jj!?root2l>W9 zH`du1mF(O6=bXTW>Qoo0)vB4F69ZXA7U`rtt~9lMS?d}k8o!tS!QWVih%HBS>R*>P zw4J!shUrXy7JD7{mC5l zhcO4sOh0EX)sT$5v~aQ+OZlOd#&e`nZ*S7eo)J{={{4G`p3Y)z~JfX=d#Wzp$VA{0EvI*tpET3 literal 0 HcmV?d00001 diff --git a/assets/backgrounds/28cobble.png b/assets/backgrounds/28cobble.png new file mode 100644 index 0000000000000000000000000000000000000000..23881108ad6817f22166fd249f96b0d1201988ae GIT binary patch literal 990 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANOKGd*1#Ln2yB!*#1(8wku< z#@NQqTgr0KN!HQ*MPStunS%xa8v+6XF0A2yGP`#1T4^EoY2VVz%C=Pg{`2RLp)tdO zIl^gemg)=#&RnQwXIL_&hX4N7YZs!LXEvHKC_I02K4$scd)8i>Gm4YWb?%<|(4sHZ zm^rZhmu&Aj%ZbI`6y*Ec*$)(3rq`!kDE7N!Dt$K2xFb7!{*+1VM~r8=Wam0=z1DTl zIxlO@1=fK?8`yv+2PFsKT>pk%k84kN0 zdoL+Ar(CO==6IQ{;lE6RKvv5!$r(xq_VTz~^wQLwqwq#$ms0KSCYg$fy)G%!H(c}5 zv75|Ya%l5f)As^(+LQVkHchsj8&q^LL4a%iq0ec|a+ z9n%87&yc*wSTpx|XYOr-=SL)%Z*NPyUv6JB@40^62jgQ0_Pw7cm^=6R%bQx80+&q} znozN~eEm)%^Jz`zRi>xSE_L+uO}wlx~{doFz-U#b1Tl}Z(o!%iZfVv zUzNPt`%6Gw>49lO46nf|O)a;Qc|nJK*C&SRo?|KTTfD08jGt=s+}C#(nkzGZ+O41^ z($2@`#Ae*^*y4|@?8ab?04wHK9Lt|FZaCLcczLQ((TbF?!WoH?UvBLaQku6j)89Bt z>*3We&Q3-Aa|@)voJ=378zi!cujJ9xLVFq`P zi-n56yg9F4xEgxKXI&dVug)i}Pmld9vexqXUU{Rj`&N*O1ap5zR_2VY@2XdC{kI}2 vbJ{M$f1V2i)0RrQFIqY8$hQCNIrc4YWUd#)FfcGMc)I$ztaD0eLS_R1;epbU literal 0 HcmV?d00001 diff --git a/assets/backgrounds/29cobble.png b/assets/backgrounds/29cobble.png new file mode 100644 index 0000000000000000000000000000000000000000..9087efaf17d8f3d7d0ec2a67fa534b1bf916c988 GIT binary patch literal 837 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANOK44y8IArY;uVY{b3G2l7; zL&hS+Vxe~cr`|!;9aFS(y#jVDbZY0Cdsda5m!-J;>%DznUpO}@aK0>*Ic~j_g&||D z;?kCDQVb2BtxC>*|GQ<`^PTsi4$lcX^!n>A$*!JXv!p)#y*Ts5n=|rDr!el6`nWUW zhuvSJL}iK?kbIKueF&T^eWrFan@9)(p4+23SK^1 zDHX=Xx#8Na=0$gdP6=Fiva|SkkIJHmY=`I9N@eG`|K=zOIwBRjwzR;j*~{{|g~jxs zSAnl1Sx*>dhB7&5E|q>L-*`1@;o>Qm=2~^zEZ$=5D|JUC@ZCj&Y%g?9O|~wY_^Ge(!k2?3MRrSD>@A<~ zUuzn3z4e;Zxz9&7=`eS^-sQB5@qvxM&3ylw+S;#wPj7m#YaQd2%6W5pU+=PNZWah^ z3pG#@?AjNqyOLST@WaHywXHMVLJ}771z1gWTGp?bsIf`C|Q*y zrTCn)Nqm-;d@irm0cV#}an~~fFyg@ot(+Co<~oVix7ob=a#IdqP6u zdrkRSf4}`++Jf3^qg?iwf8kwlPFeP&^v2f>mlpAT{qXwhF2)kxgz8MLyF2?%M(b3$ z{74Lvwv9R|!F~O`6l+N}{|=_24At!Fm0OICSGncBG(BI+ATRrXQB1bMZzp$`RNCsy zi`s+-F{_+@@mG}o3x%At#k^G$<%HoS^aR_EX~+tW97 z*?T#inBe38toqJX^q&27IQrzv>+ad-eu~*X&+PU$W?vO0u#YpeMrLDoZ;)JU)Y2)^ z-cldddr7Pm|B&fipW<@h&)yY}3uTVqwpRGHUT~E&>$AP`oKxSe-uh_c``@!y?0um0 n{G9@m^QX{2hJk^B!PC{xWt~$(6EYhB-Xno$ literal 0 HcmV?d00001 diff --git a/assets/backgrounds/30resource.png b/assets/backgrounds/30resource.png new file mode 100644 index 0000000000000000000000000000000000000000..76854a6f2439e552034db5e20eb16a93374b4b37 GIT binary patch literal 1450 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>U`_FK zaSVxQZ4LL$NDdYGcd2aRxulBOjTiXN=DKE->F?oTF)h%2Y1KOI+#+R`m&aVgje9;X z4l3EAC2PLDRmE?|+^=6-u>QWZE}bvaF@L#n;HMv!Rq~6(&wqBBb=m0qnfHOuef&RJZ?QKvW?m}7=JVY{ zQ*mia?(JGR28P?)zW#UO;81jW{$gHg_27WzOx43vT8pP4rlBGsj7@Re>X5RfwY6 zvBInhiF&(#;{31gGc!E;Xz}QyMb_4+_zDld?YECUd6LrK5~ydsc45fZTKT13qVJSh z4GsCO-g2&8-Pc^kzA{JeNF7)A^GX3H2T(xdD|9i7gx}t_a7t4Um$&w6jnGvL4e72z z3gr5Ad+@VO?eQlQJWC#AihZ~-;pv>O-rB1RjQExZbH&32C`Lv@z#`KBLwnp(T^%7N9R<@YFE%0W^M-{Qd@BhhL zOyBnL%#H)@@^w7wj~{$Kf9l7ssH3a%_jcCLlX=*lzyI*e&7N~CXCDcdj&FIz^iV~p zQ;yx@=Zb&}tuHk@Uut&7UcDB!`r5p0_fA^u*t>1n{;S(x6&UfkO-`=eqj26!Q?cbj zb*;*hewQ7wSC92(UsqH+R()UnSm4c)g;CW~Ht`xD_x64*y%QJhe9G^8E4PJS#GMz6 zOzO+3=COaD$aT~rb$Q|ATVE$E{kpd5ypuJr`r`+O9xZY__Rm#jvB}rk{mUC(vTmBf zaqYvlkGHJzowt75qU2)5w76=+#(i>3OM4U^pV_r|w&42DEH>8m>{o9!PcAHYe=e5y zS&Gb+?XPY<`W{#B+`i~x?&Ae_FFx4ASD>uP`_<;axN@@D6x!mHf5<@~}YN)p3#^tVY3awIGe@8?`vNGJ+Rpn`}dcCyt z4yU(fX$+??YeSryhvrsyGrPYmD|7T%{Nx@y2-k~0vitpg7p>Ws57aDqoE_k`bIlZ| z?YCP~BX>zNIhf6MwUKKVa4O#!U-wV2{kN>lyYdTFAA(=ZwBO$_TmH+M|F7R4-?8C` z(YN})y!UG&8wHe9W^)CeVBXU>;daA^fW%`{zIjhwr56>lMk6`srp%t@El&=2$a$Sz zc{N3sS4OSTBXbt-io7%D&cFQS_|P-8f40bU;{(T4^JM!)&&S$r_+*@7+Iarl`L9=5 z>e@fBJ^uJ()BE2XO$?nbPP;=m8WlJ$l4~>=Q`llA35a;@( zSK08Y>7@MaZGw}7(D^2+?&N-%QmR@e$ zCNpD~`i0ffDQh+R{ZG{xy<6QB>j_v<`of0~KItQf{`DR=2jT zbv`U=T<<>X!MfM41SSY>oH=u*vK{v)?ihuglb#ofeE9$Ofa&Rv_4BW%+)DA=kjcQn Oz~JfX=d#Wzp$P!3Nvo0o literal 0 HcmV?d00001 diff --git a/assets/backgrounds/31resource.png b/assets/backgrounds/31resource.png new file mode 100644 index 0000000000000000000000000000000000000000..7be3f4be16c7640e2d7608ceb7f191766728d027 GIT binary patch literal 1503 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>V4dmd z;usRqS~~rFc1)p+Qg$GP9NsL|saUBcQ+$LcnuxM%9g^PM7HcoX0u`|q#7X+sqGIjFF$&T4GXZGj3<@{~G zpWQ2d|9#H;pM~eMHO0Ey=RIdk=sEb=rpJ)s#Gblm&!2y+-M@0qzyF(zP1+3GTAo+x z=;CNXPn$%EORp|H z{%yDK{p8b6;}RGYrk}p~-ABz?ZF1+ylXmAnzqxgK3X4Yum+6XYQOupYQ|_e(Tx(WP zn0zu~@sm3W97$}dG4Z<}Bz1n_!^z&;B}=VtYK1fGC|+O0@a#I^XH5I zzT&wYoP5S_n{mlQA^$9C_nmUKpZQ;JY`DzTt-We1TlZ4gPe1qMrX0z5&2)FGMDm;7 zZK3N5D$}@LJb%2iT4utM_1`a<`|5{=LQeYj0@t>!c8! zo)1sm-!m0hZ9E&c^UxOqp4c5C+)G}r+&JUuBPM5afx8_)Ywk%sz1OkvcEG9JjRsBb zQ=U7E>jg$dKB(DsaO(H8oj-r-GO(T7%bRdHcoWm21KpPlwH$U$Ev`OtEiHG(lG}HW z9-k5>#_-~`uiV{&<4>>o`UkpvXPy+8SDJf^|3uQ0;Q5XJbfov+VS8}yFvo!s7t#FO zy*ZaJ`&w$+{jw{Rj+BhvYPQLfFCtq1HuJ8ax$X=R1%Y9w-X>+XvoAdxar>Q^>D_dR zZ6Rv)|HKKIYAeP_?*NuReod3pK&O@e=poXeB0 zfBWxn^Ch+0TkSvE^gAtdkZ8N|`uNIMzwKuFwDtAzef$31Q*E+g^KC0Dt96peZx*q5 z+s%JI`+c3l_4#S(+jXBkem*w(&pCGDY0;J+`|4NR&Q!QI~XkqubcZw$Fv%|`W4lVdmZIh>`uBayKav34!z=8 cU;i^&suXDnZ(v@@z`(%Z>FVdQ&MBb@0Mw1^CIA2c literal 0 HcmV?d00001 diff --git a/assets/backgrounds/32resource.png b/assets/backgrounds/32resource.png new file mode 100644 index 0000000000000000000000000000000000000000..59a10630865245a9a439e80cd8167b95c68637f4 GIT binary patch literal 1471 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>V6F6Y zaSVxQZJmD5TgFr5Sp6}}GpU}!Ju@d?=9Ie?J%35@?AfcgU3i{w=nl)>mOqSaOdt6L zQdQ*2J{o+~S#tcKFCXtDg;_ean+x4vd=yAiRWY5ObC~T;^Y2ry$_|O~*xrr(dT;gn zy}$Ndcm7)S_s}LChABZ2(^`YL7%KDn=FXMP+Is6i<+H#2IWz301WnO7t<<$ByNylK z_i{+Quluu_nZ_sPS^B^8E)qUos8=L;H2vuJ=4YZ4O|GB#tia)M`HK<5<%+dom;XPu z*vGc|^{yqET|r#8w|!mLeeQ`?F&isqfRSQJZ1{eY#oKbDr6-llJa*P(Te&mCp%12g z#s?p(`+u#uUfTIMVm;%Iz%!*6ccw<(*jB46IxY2OdZ5U~d%Qn?J(XcNKbu9tvoA6# z=7Rm^Yblq_&m1x+S3j!a*S$e|>5W^rl)99riHUEy?6=%`^UkxUPH~5Y#Xb04%uupB zea-r{&vKp!Ovsiwerx9+6@!&^oCoI5O{>ifwYIddU`eQ{iMikYFnh0>XW#nVbtW7| zm1%C$$2Xo^7}WJG&tv)J#JYctW!GMocuY#3^kjzLa^{Bj=U*!9y1O}MS22gk>dU2f z?|4pFti@<5#mg}3Y%cHKmo5_w1P$7MNS4Vwubh^e6500HV#?{%DM2cpPSxf|cKsCo z{ri1@SK!JJjp?VSoKEHIw<~y`CwhMO`P0+&fByMtEN}nMajR9^W1k5s$M;lzo>sTt zfB$`Ug`BONO$;72Tw&M1cQ3du&zh~*DOebzGW*!Ev^>Ya0vB0UOG%-Qm3_~m z92ccXO%rDd6>;6MZgsQpB%v&`qo?N2z2>U%GAk$N*%zn2jhzeBlp3{sFVD&7aGt!( z_hsJuww;^x&b3y_+gGlVmJnuj%)e23_j%;Bi?6>jOc0)!Dmg9n;_I&-lUlxR4Sjca z?WY$Ho#)y7YmKk}%UfSID|&mLp;3|a`Q7IiyYv72_BP+HS9GjKf?a$r$$WcsyPIX3MMh0o`icRu%hmU8t;mGC3C z#g1F8mIbSx$~Y%DQ0LWaQ*zv6@H=&TUd^F5R8()T&TS(XXK za@%fInL7$3M2ba6M_X2Zt+HLd=u>=FVua>bo6|-Qf)?`i+Zog(d%XVf^Jjcd&z)y~ zf0xghGv~>i+uPlR&qop{^Kd27_$LvMbkEV8$*)xCcA z-OO{_JAyVYHIA-M4Be6BAF0k2DKvMQ;)GeBv<{wF*KM1;@w$1l(S1*6r7X^v^h1t} zo(9V%oRHbv#>N_Ur6H*SRJJYp@zLT%mYMc4C$9-d-yc*`D)1~yv!A$3)7eoVX2$VD rF#&(hnC*TfZ~1RO{~aaS|J+Vbc5HfGb=ZM{fq}u()z4*}Q$iB}En>}w literal 0 HcmV?d00001 diff --git a/assets/backgrounds/33resource.png b/assets/backgrounds/33resource.png new file mode 100644 index 0000000000000000000000000000000000000000..8a4cfa8ee624389323dde586919f6bf6cca7cee2 GIT binary patch literal 1235 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>VCnUA zaSVxQZ4KX_t?VxHFK6|I*sY41F?~-`o)!h9hUiY)crYO%V4~Ir6_w1xrPpJhoBU)i zD)=F9pd%x5=V;hAA;s1iVN9OEt4`jX5}uJ;KmKl$tnjAe*E}x^{#ar$11pek=RoX-!++t;5fZFVuaLxjMg|rJ*#|S+lilckixs^126>8y;)^?7sQZ zZ_6ITdux8KaB!D>`0M)pJp!vHJAG$o)HRwlr?}rvz?!T6$xR97wWSBv7V}+;dhw6p z?w0n=mlfX~`_9ntw1#c>Ea%coZ-2gIiFz%oDpkvHqsn;e;v+223(h%yXAhVwVqf<^ zxWMq>+pK+i*V}*a@|YvGbK&N|)vH!b=;x|$oax7u{UPY8r=iV$7KS^$?+eSUj-1l_ zW62k_XqDFcE1r{bj1y%TudJEqtl4_Poz?Ar-SdOtWsNWI*FWsm-|zJN_3;lzmpN~ppZECk@%|gu zH`J~?t!aCA(Y)ZDa9FqHoZsxT&qvoQw5qGC#|1J(b1jK{aaQK^PoBe{z1O@sR9%+C zIq&r{+dK6Y;%^jpoY^AdIVq=Gu(JN+r(3fp+&cbX(rum7O;>yl$gXm^A=IzDWzPiOKAqD|mvoCc+yi<}-JX5m@Y^i8jF5ZxDtc4GPP@0p zhkLyhF^zOgwf!J+qU6sMGnO>H4cFKr1u8tu_$A-(Kk&5veT#_0CsqE_EKZD{0vq#g zeU6;)G5+4YioVR=Rjc+S9NBa&$v$uL5{FHDgS;x&p5EkQr)6NXpC_mwp|d?lGq^H_ zeGluCrmbbmcTYSl!F*T#$BP}(<^if)0<6!C^KM67V!D)^X~wW`rNWPwM@8S+@A=%o z6FV(2&5`Tkl@n~fnRm8&txJ`5ef!A7JAr|5hfHDknccRmo3u_b-#KB~rqnREOa1Km znu*VM?sqm*n#Z(KmQ^8rl338e!-Xqmy=2*C&vEawO?$`$rw^P8A`a6YZ_;62IBU(p z%KLjb7S8*n_dv?IME~@r-Z#G++}|%(U2>$TU&O-h_3f?gbP0 Hl+XkK`Y2;Q literal 0 HcmV?d00001 diff --git a/assets/items/02silver_ore.png b/assets/items/01silver_ore.png similarity index 100% rename from assets/items/02silver_ore.png rename to assets/items/01silver_ore.png diff --git a/assets/items/04gold_ore.png b/assets/items/02gold_ore.png similarity index 100% rename from assets/items/04gold_ore.png rename to assets/items/02gold_ore.png diff --git a/assets/items/06platinum_ore.png b/assets/items/03platinum_ore.png similarity index 100% rename from assets/items/06platinum_ore.png rename to assets/items/03platinum_ore.png diff --git a/assets/items/01iron_ingot.png b/assets/items/04iron_ingot.png similarity index 100% rename from assets/items/01iron_ingot.png rename to assets/items/04iron_ingot.png diff --git a/assets/items/07platinum_ingot.png b/assets/items/06platinum_ingot.png similarity index 100% rename from assets/items/07platinum_ingot.png rename to assets/items/06platinum_ingot.png diff --git a/assets/items/03silver_ingot.png b/assets/items/07silver_ingot.png similarity index 100% rename from assets/items/03silver_ingot.png rename to assets/items/07silver_ingot.png diff --git a/assets/tiles/3furnace.png b/assets/tiles/3furnace.png new file mode 100644 index 0000000000000000000000000000000000000000..72b9d1c6dc4da59b745bb54b3875c93eb3a1c143 GIT binary patch literal 557 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>V0`ZB z;usRqdUpD5KWRsiHhcHJy^7Ox3Mb0;@HkyiI9*~Q<~3DGbB_3i6@OSf-Cr$v%)3IV zYl~(>6PxCu!p=Jn*^a1&=@l*EHQFYqdF=SLp7M{IG-mysJ#%yQvv22j`sNgVeJU%r z<87Hq<)+~8L7$I0eUoGmx4%8_|8Co@Zzi*!{Ut9@9+b&YHMea4-isnnp7=lB7{L8x znXGT#PFse5+d{MFH#({>EXy)_(7yBz3&Us0^qyshGJ_586it$}ufns&L zI8BBl%hnhh+-R8Wr7S1NHg%Er(iePBcFt5OHa^)HqqRg+)b089a|agdfB8FGhatN9 z{?_-;?WdUMhFxd;vE}dC;ytBoN!m*{P2)Zyts*{SV!)mUM?;t-jxi=Ks=UXbu>aew z;)9=d$0~G93@CDpP)VQ8c;b8X!N}bI_X@vBa!4`$RVdGQ^3&V);CM?do64`&RHr}n zn^)c6J70XphVu<2a;IA!Jp05Vajg09YE1^Sd*38~Zd(1)y}hyh(P3@TlVyEVZKPfY zGlX>QXZZ1F>od##TDC)O2m0B&_WbOb^mo&u8wUdK-rn}c*kDatv!P-nkI=31&rg_E za{IWf*OL3LH$m1r_QH-G*P?` Item ItemRegistry[ITEMREGISTRY_SIZE]; @@ -22,22 +25,27 @@ void updateItems() { for (int y = 0; y < MAP_HEIGHT; y++) { for (int x = 0; x < MAP_WIDTH; x++) { Tile *t = &tileMap[y][x]; - if (t->type != TYPE_BELT) continue; + TileTypeReg tt = TileRegistry[t->type]; + if (t->type == TYPE_AIR) continue; int dir = t->direction; bool horz = (dir == ORIENT_LEFT || dir == ORIENT_RIGHT); bool vert = (dir == ORIENT_UP || dir == ORIENT_DOWN); - for (uint8_t lane = 0; lane < 2; lane++) { + for (uint8_t lane = 0; lane < ItemSlotCount; lane++) { + if (!tt.outputLane[lane]) continue; ItemOnBelt *itm = &t->items[lane]; if (itm->type == 0) continue; - - // 1) Advance - itm->offset += speed; + if (tt.itemMoves) { + itm->offset += speed; + // 1) Advance + } // 2) Time to hop? - if (itm->offset >= 0.5f) { - itm->offset -= 1.0f; + if (itm->offset >= 0.5f || !tt.itemMoves) { + if (tt.itemMoves) { + itm->offset -= 1.0f; + } // target coords int nx = x + dirDx[dir]; @@ -45,43 +53,49 @@ void updateItems() { // bounds & belt? if (nx < 0 || nx >= MAP_WIDTH || ny < 0 || ny >= MAP_HEIGHT) { - //itm->type = 0; - itm->offset += 1.0f - speed; + if (tt.itemMoves) { + itm->offset += 1.0f - speed; + } continue; } Tile *next = &tileMap[ny][nx]; - if (next->type != TYPE_BELT) { - //itm->type = 0; - itm->offset += 1.0f - speed; - continue; - } - - // Decide new lane + TileTypeReg ntt = TileRegistry[next->type]; int newLane = lane; - int newDir = next->direction; - bool nH = (newDir == ORIENT_LEFT || newDir == ORIENT_RIGHT); - bool nV = (newDir == ORIENT_UP || newDir == ORIENT_DOWN); + switch (next->type) { + case TYPE_BELT: + int newDir = next->direction; + bool nH = (newDir == ORIENT_LEFT || newDir == ORIENT_RIGHT); + bool nV = (newDir == ORIENT_UP || newDir == ORIENT_DOWN); - if ((horz && nH) || (vert && nV)) { - // same axis → keep lane - } else if (horz && nV) { - // came off a horizontal: lane0=top→vertical.left, lane1=bottom→vertical.right - newLane = (dir == ORIENT_RIGHT ^ newDir == ORIENT_UP ? 0 : 1); - itm->offset = 0.0f; - } else if (vert && nH) { - // came off vertical: lane0=left→horizontal.top, lane1=right→horizontal.bottom - newLane = (dir == ORIENT_UP ^ newDir == ORIENT_RIGHT ? 1 : 0); - itm->offset = 0.0f; + if ((horz && nH) || (vert && nV)) { + // same axis → keep lane + } else if (horz && nV) { + // came off a horizontal: lane0=top→vertical.left, lane1=bottom→vertical.right + newLane = (dir == ORIENT_RIGHT ^ newDir == ORIENT_UP ? 0 : 1); + itm->offset = 0.0f; + } else if (vert && nH) { + // came off vertical: lane0=left→horizontal.top, lane1=right→horizontal.bottom + newLane = (dir == ORIENT_UP ^ newDir == ORIENT_RIGHT ? 1 : 0); + itm->offset = 0.0f; + } + // (diagonals fall back to same-lane) + + // Find a free slot in + break; + + default: + itm->offset += 1.0f - speed; } - // (diagonals fall back to same-lane) - // Find a free slot in - if (next->items[newLane].type == 0) { + if (next->items[newLane].type == 0 && ntt.allowedInItems[newLane][itm->type]) { // MOVE it ItemOnBelt moved = *itm; moved.tileX = nx; moved.tileY = ny; + if (!ntt.itemMoves) { + moved.offset = 0.5f; + } next->items[newLane] = moved; // clear this one @@ -90,8 +104,15 @@ void updateItems() { // both slots full → wait at end itm->offset = epsilon; } + + } else { + } } + const UpdateTileCallback cb = ItemTileCallbacks[t->type]; + if (cb) { + cb(t); + } } } } @@ -108,7 +129,11 @@ void registerItem(char name[20], SDL_Renderer *renderer) { ItemRegistry[itemRegistryIndex].texture[ORIENT_LEFT], TILE_SIZE / 2, TILE_SIZE / 2); SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].textureOnBelt[ORIENT_LEFT], SDL_BLENDMODE_BLEND); + ItemRegistry[itemRegistryIndex].atlasRects[ORIENT_LEFT] = allocate_32x32(ItemRegistry[itemRegistryIndex].texture[ORIENT_LEFT], renderer); + ItemRegistry[itemRegistryIndex].atlasRectsOnBelt[ORIENT_LEFT] = allocate_16x16(ItemRegistry[itemRegistryIndex].textureOnBelt[ORIENT_LEFT], renderer); ItemRegistry[itemRegistryIndex].type = itemRegistryIndex; + ItemRegistry[itemRegistryIndex].isTile = false; + ItemRegistry[itemRegistryIndex].miscVal = 60; itemRegistryIndex++; } @@ -196,7 +221,7 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play yOffset += 0.0f * TILE_SIZE; break; case ORIENT_LEFT: - xOffset += 0.0f * TILE_SIZE + (TILE_SIZE / 2); + xOffset += 0.0f * TILE_SIZE + (TILE_SIZE); yOffset += 0.26f * TILE_SIZE; break; case ORIENT_LEFT_UP: @@ -205,14 +230,14 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play break; case ORIENT_UP: xOffset += 0.22f * TILE_SIZE; //GOTO HEHREHRHE - yOffset += 0.0f * TILE_SIZE + (TILE_SIZE / 2); + yOffset += 0.0f * TILE_SIZE + (TILE_SIZE); break; case ORIENT_RIGHT_UP: xOffset += 0.0f * TILE_SIZE; yOffset += 0.0f * TILE_SIZE; break; case ORIENT_RIGHT: - xOffset += 0.0f * TILE_SIZE; + xOffset += 0.0f * TILE_SIZE - (TILE_SIZE / 2); yOffset += 0.18f * TILE_SIZE; //FIX THIS break; case ORIENT_RIGHT_DOWN: @@ -220,8 +245,8 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play yOffset += 0.0f * TILE_SIZE; break; case ORIENT_DOWN: - xOffset += 0.18f * TILE_SIZE + (TILE_SIZE / 4); - yOffset += 0.0f * TILE_SIZE; + xOffset += 0.18f * TILE_SIZE + (TILE_SIZE / 8); + yOffset += 0.0f * TILE_SIZE - (TILE_SIZE / 2); break; default: break; @@ -275,7 +300,7 @@ void renderItem(ItemOnBelt item, SDL_Renderer *renderer, int lane, SDL_Rect play } } -void putItem(int x, int y, uint16_t itemType, uint8_t lane) { +void putItem(int x, int y, ItemType itemType, uint8_t lane) { tileMap[y][x].items[lane].type = itemType; tileMap[y][x].items[lane].offset = 0; tileMap[y][x].items[lane].tileX = x; @@ -283,33 +308,29 @@ void putItem(int x, int y, uint16_t itemType, uint8_t lane) { } void loadItems(SDL_Renderer *renderer) { + for (int i = 0; i < tileTypeIndex; i++) { - TileType tile = TileRegistry[i]; + TileTypeReg tile = TileRegistry[i]; strcpy(ItemRegistry[itemRegistryIndex].name, tile.name); memcpy(ItemRegistry[itemRegistryIndex].texture, tile.textures, sizeof(tile.textures)); + memcpy(ItemRegistry[itemRegistryIndex].atlasRects, tile.atlasRects, sizeof(tile.atlasRects)); for (int a = 0; a < ORIENT_DIRECTION_COUNT; a++) { SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].texture[a], SDL_BLENDMODE_BLEND); ItemRegistry[itemRegistryIndex].textureOnBelt[a] = ScaleTexture(renderer, tile.textures[a], TILE_SIZE / 2, TILE_SIZE / 2); + ItemRegistry[itemRegistryIndex].atlasRectsOnBelt[a] = allocate_16x16( + ItemRegistry[itemRegistryIndex].textureOnBelt[a], renderer); SDL_SetTextureBlendMode(ItemRegistry[itemRegistryIndex].textureOnBelt[a], SDL_BLENDMODE_BLEND); } ItemRegistry[itemRegistryIndex].type = itemRegistryIndex; + ItemRegistry[itemRegistryIndex].isTile = true; itemRegistryIndex++; } + itemRegistryIndex = ITEMREGISTRY_SIZE / 2; iterateSortedDir("./assets/items", (DirEntryCallback) registerItem, renderer); -// DIR *dir = opendir("./assets/items"); -// if (dir) { -// struct dirent *entry; -// while ((entry = readdir(dir)) != NULL) { -// if (entry->d_name[0] == '.') { -// continue; -// } -// registerItem(entry->d_name, mainRenderer); -// } -// } } \ No newline at end of file diff --git a/items/item.h b/items/item.h index 43df48b..b89b7b1 100644 --- a/items/item.h +++ b/items/item.h @@ -5,23 +5,46 @@ #include #include "../util/util.h" #include "../tiles/belt.h" +#include "../tiles/tile.h" #ifndef FACTORYGAME_ITEM_H #define FACTORYGAME_ITEM_H -#define ITEMREGISTRY_SIZE 20 +#define ITEMREGISTRY_SIZE 128 -typedef struct { + +typedef enum ItemType { + TYPE_AIR = 0, + TYPE_BLOCK, + TYPE_BELT, + TYPE_FURNACE, + IRON_ORE = ITEMREGISTRY_SIZE / 2, + SILVER_ORE, + GOLD_ORE, + PLATINUM_ORE, + IRON_INGOT, + SILVER_INGOT, + GOLD_INGOT, + PLATINUM_INGOT, + LOG +} ItemType; + + +typedef struct ItemOnBelt { float offset; int tileX, tileY; - uint16_t type; + ItemType type; } ItemOnBelt; -typedef struct { - uint16_t type; +typedef struct Item { + bool isTile; + ItemType type; char name[20]; + uint16_t miscVal; SDL_Texture * texture[ORIENT_DIRECTION_COUNT]; SDL_Texture * textureOnBelt[ORIENT_DIRECTION_COUNT]; + SDL_Rect atlasRects[ORIENT_DIRECTION_COUNT]; + SDL_Rect atlasRectsOnBelt[ORIENT_DIRECTION_COUNT]; } Item; @@ -39,5 +62,5 @@ extern uint8_t laneTarget; extern double speed; -void putItem(int x, int y, uint16_t itemType, uint8_t lane); +void putItem(int x, int y, ItemType itemType, uint8_t lane); #endif //FACTORYGAME_ITEM_H diff --git a/main.c b/main.c index 778425e..a2f0b24 100644 --- a/main.c +++ b/main.c @@ -7,28 +7,46 @@ #include "items/item.h" #include "stdlib.h" #include "player/player.h" +#include "util/perlin.h" +#include "util/atlas.h" -typedef struct { +typedef struct GameState { Player player; Tile tileMap[MAP_HEIGHT][MAP_WIDTH]; + BackgroundTile backgroundTileMap[MAP_HEIGHT][MAP_WIDTH]; + SynthVoice voices[NUM_SYNTH_VOICES]; } GameState; GameState gameState; -void loadGameState(char *filename, Player *plr) { +int loadGameState(char *filename, Player *plr) { printf("hello from load\n"); fflush(stdout); FILE *gameSave = fopen(filename, "rb"); if (gameSave) { + fseek(gameSave, 0L, SEEK_END); + long sz = ftell(gameSave); + if (sz != sizeof(gameState)) { + return 1; + } + rewind(gameSave); fread(&gameState, sizeof(gameState), 1, gameSave); fclose(gameSave); - memcpy(tileMap, gameState.tileMap, sizeof(tileMap)); memcpy(plr, &gameState.player, sizeof(gameState.player)); + memcpy(tileMap, gameState.tileMap, sizeof(tileMap)); + memcpy(backgroundMap, gameState.backgroundTileMap, sizeof(backgroundMap)); + memcpy(audioData.synthVoices, gameState.voices, sizeof(gameState.voices)); + plr->cursor.targetTile = NULL; + plr->cursor.prevTargetTile = NULL; + return 0; } + return 1; } void saveGameState(char *filename, Player *plr) { memcpy(&gameState.player, plr, sizeof(gameState.player)); memcpy(gameState.tileMap, tileMap, sizeof(gameState.tileMap)); + memcpy(gameState.backgroundTileMap, backgroundMap, sizeof(gameState.backgroundTileMap)); + memcpy(gameState.voices, audioData.synthVoices, sizeof(gameState.voices)); FILE *gameSave = fopen(filename, "wb"); if (!gameSave) { @@ -51,7 +69,7 @@ const int delayNeeded = 1000 / targetFPS; #define smallerFont fonts[2] #define smallestFont fonts[3] -const char *autosaveName = "autosave.dat"; +char *autosaveName = "autosave.dat"; unsigned long frames = 0; @@ -66,6 +84,8 @@ void msleep(unsigned int milliseconds) { SDL_GLContext glContext; +void genInitMap(); + int init() { //Initialize SDL @@ -75,7 +95,9 @@ int init() { screenRect.w = DISPLAY_WIDTH; screenRect.h = DISPLAY_HEIGHT; - srand(0); + srand(time(NULL)); + + memset(tileMap, 0, sizeof(tileMap)); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, NULL); SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "1"); @@ -107,8 +129,17 @@ int init() { return 1; } + initAtlas(mainRenderer); + loadTiles(mainRenderer); loadItems(mainRenderer); + setupTiles(); + + for (ItemType i = 0; i < ITEMREGISTRY_SIZE; i++) { + if (strlen(ItemRegistry[i].name)) { + printf("%d -> %s\n", i, ItemRegistry[i].name); + } + } // Create OpenGL context glContext = SDL_GL_CreateContext(window); if (!glContext) { @@ -119,21 +150,24 @@ int init() { // Use OpenGL context SDL_GL_MakeCurrent(window, glContext); // Make sure OpenGL context is current before any OpenGL rendering + audioData.playerRect = &player.rect; + audioData.maxPanDistance = DISPLAY_WIDTH / 2; + SDL_AudioSpec spec = {0}; spec.freq = SAMPLE_RATE; - spec.format = AUDIO_F32SYS; - spec.channels = 1; + spec.format = AUDIO_F32; + spec.channels = 2; spec.samples = 4096; spec.callback = audio_callback; spec.userdata = &audioData; - SDL_AudioDeviceID dev = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0); - if (dev == 0) { - printf("Failed to open audio: %s\n", SDL_GetError()); - SDL_Quit(); - } - - SDL_PauseAudioDevice(dev, 0); +// SDL_AudioDeviceID dev = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0); +// if (dev == 0) { +// printf("Failed to open audio: %s\n", SDL_GetError()); +// SDL_Quit(); +// } +// +// SDL_PauseAudioDevice(dev, 1); SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255); SDL_RenderClear(mainRenderer); @@ -150,6 +184,10 @@ int init() { initPlayer(&player); + for (ItemType i = 0; i < 13; i++) { + player.inventory.hotKeySlots[i] = i; + } + initTiles(); //generateTestMap(); @@ -163,20 +201,22 @@ int render() { SDL_RenderClear(mainRenderer); SDL_Rect rect2; - rect2.x = 20; - rect2.y = 20; + rect2.x = 0; + rect2.y = 0; rect2.w = 0; rect2.h = 0; renderAllTiles(mainRenderer, player.rect); - - SDL_RenderCopy(mainRenderer, tilesTexture, &screenRect, &screenRect); - SDL_RenderCopy(mainRenderer, itemsTexture, &screenRect, &screenRect); - SDL_RenderCopy(mainRenderer, entityTexture, &screenRect, &screenRect); - SDL_RenderCopy(mainRenderer, hudTexture, &screenRect, &screenRect); - renderPlayer(&player); +// SDL_RenderCopy(mainRenderer, backgroundTexture, &screenRect, &screenRect); +// SDL_RenderCopy(mainRenderer, tilesTexture, &screenRect, &screenRect); +// SDL_RenderCopy(mainRenderer, itemsTexture, &screenRect, &screenRect); +// SDL_RenderCopy(mainRenderer, entityTexture, &screenRect, &screenRect); +// SDL_RenderCopy(mainRenderer, hudTexture, &screenRect, &screenRect); + SDL_QueryTexture(atlasTexture, NULL, NULL, &rect2.w, &rect2.h); + SDL_RenderCopy(mainRenderer, atlasTexture, &rect2, &rect2); + SDL_RenderPresent(mainRenderer); frames++; @@ -206,11 +246,11 @@ int processEvent(SDL_Event e) { speed = speed == 0 ? 0.004f : 0; break; case SDLK_r: - if (player.cursor.canReach && player.cursor.targetTile->type == TYPE_BELT) { + if (player.cursor.canReach && player.cursor.targetTile->type != TYPE_AIR) { player.cursor.direction = player.cursor.targetTile->direction; } player.cursor.direction = (player.cursor.direction + 2) % ORIENT_DIRECTION_COUNT; - if (player.cursor.canReach && player.cursor.targetTile->type == TYPE_BELT) { + if (player.cursor.canReach) { player.cursor.targetTile->direction = player.cursor.direction; } break; @@ -220,6 +260,12 @@ int processEvent(SDL_Event e) { case SDLK_F3: debugMode = !debugMode; break; + case SDLK_F4: + Tile *tile = &tileMap[playerTileY][playerTileX]; + break; + case SDLK_LALT: + itemViewing = !itemViewing; + break; default: break; } @@ -274,15 +320,13 @@ void processMousePosition() { if (player.cursor.targetTile->type < tileTypeIndex) { player.inventory.slotCounts[player.cursor.targetTile->type]++; } - if (player.cursor.targetTile->type == TYPE_BELT) { - for (int lane = 0; lane < 2; lane++) { - if (player.cursor.targetTile->items[lane].type != 0) { - int itemType = player.cursor.targetTile->items[lane].type; - if (itemType < itemRegistryIndex) { - player.inventory.slotCounts[itemType]++; - } - player.cursor.targetTile->items[lane].type = 0; + for (int lane = 0; lane < 2; lane++) { + if (player.cursor.targetTile->items[lane].type != 0) { + int itemType = player.cursor.targetTile->items[lane].type; + if (itemType < itemRegistryIndex) { + player.inventory.slotCounts[itemType]++; } + player.cursor.targetTile->items[lane].type = 0; } } player.cursor.targetTile->type = TYPE_AIR; @@ -311,6 +355,18 @@ void processMousePosition() { player.cursor.windowY = (player.cursor.windowY - viewport.y) * DISPLAY_HEIGHT / viewport.h; player.cursor.tileX = (player.cursor.windowX + player.rect.x) / TILE_SIZE - (DISPLAY_WIDTH / TILE_SIZE / 2); player.cursor.tileY = (player.cursor.windowY + player.rect.y) / TILE_SIZE - (DISPLAY_HEIGHT / TILE_SIZE / 2); + if (player.cursor.tileX < 0) { + player.cursor.tileX = 0; + } + if (player.cursor.tileY < 0) { + player.cursor.tileY = 0; + } + if (player.cursor.tileX >= MAP_WIDTH) { + player.cursor.tileX = MAP_WIDTH - 1; + } + if (player.cursor.tileY >= MAP_HEIGHT) { + player.cursor.tileY = MAP_HEIGHT - 1; + } player.cursor.prevTargetTile = player.cursor.targetTile; player.cursor.targetTile = &tileMap[player.cursor.tileY][player.cursor.tileX]; @@ -329,7 +385,7 @@ void processKeyboardHeld() { int cameraSpeed = playerSpeed; if (keyboardState[SDL_SCANCODE_LSHIFT] || keyboardState[SDL_SCANCODE_RSHIFT]) { - cameraSpeed *= 2; + cameraSpeed *= 8; } if (keyboardState[SDL_SCANCODE_LCTRL] || keyboardState[SDL_SCANCODE_RCTRL]) { cameraSpeed /= 2; @@ -339,39 +395,57 @@ void processKeyboardHeld() { if (keyboardState[SDL_SCANCODE_W]) { // Example: move up player.rect.y -= cameraSpeed; - if (player.rect.y < (DISPLAY_HEIGHT / 2)) { - player.rect.y = (DISPLAY_HEIGHT / 2); +// if (player.rect.y < (DISPLAY_HEIGHT / 2)) { +// player.rect.y = (DISPLAY_HEIGHT / 2); +// } + if (player.rect.y < 0) { + player.rect.y = 0; } } if (keyboardState[SDL_SCANCODE_S]) { player.rect.y += cameraSpeed; - if (player.rect.y > (MAP_HEIGHT * TILE_SIZE) - (DISPLAY_HEIGHT / 2)) { - player.rect.y = (MAP_HEIGHT * TILE_SIZE) - (DISPLAY_HEIGHT / 2); +// if (player.rect.y > (MAP_HEIGHT * TILE_SIZE) - (DISPLAY_HEIGHT / 2)) { +// player.rect.y = (MAP_HEIGHT * TILE_SIZE) - (DISPLAY_HEIGHT / 2); +// } + if (player.rect.y > (MAP_HEIGHT * TILE_SIZE)) { + player.rect.y = (MAP_HEIGHT * TILE_SIZE); } } if (keyboardState[SDL_SCANCODE_A]) { player.rect.x -= cameraSpeed; - if (player.rect.x < (DISPLAY_WIDTH / 2)) { - player.rect.x = (DISPLAY_WIDTH / 2); +// if (player.rect.x < (DISPLAY_WIDTH / 2)) { +// player.rect.x = (DISPLAY_WIDTH / 2); +// } + if (player.rect.x < 0) { + player.rect.x = 0; } } if (keyboardState[SDL_SCANCODE_D]) { player.rect.x += cameraSpeed; - if (player.rect.x > (MAP_WIDTH * TILE_SIZE) - (DISPLAY_WIDTH / 2)) { - player.rect.x = (MAP_WIDTH * TILE_SIZE) - (DISPLAY_WIDTH / 2); +// if (player.rect.x > (MAP_WIDTH * TILE_SIZE) - (DISPLAY_WIDTH / 2)) { +// player.rect.x = (MAP_WIDTH * TILE_SIZE) - (DISPLAY_WIDTH / 2); +// } + if (player.rect.x > (MAP_WIDTH * TILE_SIZE)) { + player.rect.x = (MAP_WIDTH * TILE_SIZE); } } } if (keyboardState[SDL_SCANCODE_F]) { - for (int x = player.rect.x - 1; x < player.rect.x + 1; player.rect.x++) { + for (int x = playerTileX - 2; x < playerTileX + 2; x++) { if (x < 0) { continue; } - for (int y = player.rect.y - 1; y < player.rect.y + 1; player.rect.y++) { + if (x >= MAP_WIDTH) { + continue; + } + for (int y = playerTileY - 2; y < playerTileY + 2; y++) { if (y < 0) { continue; } + if (y >= MAP_HEIGHT) { + continue; + } Tile *t = &tileMap[y][x]; if (t->type == TYPE_BELT) { for (uint8_t lane = 0; lane < 2; lane++) { @@ -431,8 +505,8 @@ void processKeyboardHeld() { } else if (keyboardState[SDL_SCANCODE_EQUALS]) { slot = 13; } - if (slot > 0 && slot < itemRegistryIndex) { - setActivePlayerSlot(&player, slot); + if (slot > 0 && slot < sizeof(player.inventory.hotKeySlots) / sizeof(player.inventory.hotKeySlots[0])) { + setActivePlayerSlot(&player, player.inventory.hotKeySlots[slot]); } } @@ -441,7 +515,9 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) if (status) { return status; } - loadGameState(autosaveName, &player); + if (loadGameState(autosaveName, &player)) { + genInitMap(); + } //Hack to get window to stay up @@ -460,6 +536,7 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) updateItems(); updatePlayer(&player); + updateTiles(); status = render(); if (status) { return status; @@ -485,4 +562,91 @@ int main(__attribute__((unused)) int argc, __attribute__((unused)) char *args[]) SDL_Quit(); return 0; -} \ No newline at end of file +} + +// Main generator: +void genInitMap() { + const double terrainScale = 2; + const double humidityScale = 1; + const double oreScale = 2; + const int terrainOct = 4; + const int humidityOct = 4; + const int oreOct = 4; + + int seedX = rand(); + int seedY = rand(); + int seedN = rand(); + + // Min/max trackers + double terrainMin = 1e9, terrainMax = -1e9; + double humidityMin = 1e9, humidityMax = -1e9; + double oreNrmMin = 1e9, oreNrmMax = -1e9; + for (uint16_t y = 0; y < MAP_HEIGHT; y++) { + for (uint16_t x = 0; x < MAP_WIDTH; x++) { + double terrain = pnoise2d(x + 1000 + seedX, + y + 1000 + seedY, + terrainScale, terrainOct, seedN); + double humidity = pnoise2d(x + seedX, + y + seedY, + humidityScale, humidityOct, seedN); + double oreNrm = pnoise2d(x + 9999 + seedX, + y + 1111 + seedY, + oreScale, oreOct, seedN); + + // Track min/max + if (terrain < terrainMin) terrainMin = terrain; + if (terrain > terrainMax) terrainMax = terrain; + + if (humidity < humidityMin) humidityMin = humidity; + if (humidity > humidityMax) humidityMax = humidity; + + if (oreNrm < oreNrmMin) oreNrmMin = oreNrm; + if (oreNrm > oreNrmMax) oreNrmMax = oreNrm; + + // [Same as your original terrain generation logic...] + BackgroundType baseType; + if (terrain < 0.30) { + baseType = (humidity < 0.5) ? BGType_WATER0 : BGType_WATER1; + } else if (terrain < 0.35) { + if (humidity < 0.3) baseType = BGType_SAND4; + else if (humidity < 0.6) baseType = BGType_SAND2; + else baseType = BGType_SAND7; + } else if (terrain < 0.7) { + double grassVal = (terrain - 0.35) / (0.70 - 0.35); + int idx = (int) (grassVal * 8.0); + if (idx >= 8) idx = 7; + if (humidity > 0.6 && ((rand() & 0xFF) < 10)) { + int flowerIdx = rand() % 4; + baseType = (BackgroundType) (BGType_GRASS_FLOWER0 + flowerIdx); + } else { + baseType = (BackgroundType) (BGType_GRASS0 + idx); + } + } else if (terrain < 0.85) { + int idx = rand() % 4; + baseType = (BackgroundType) (BGType_COBBLE0 + idx); + } else { + int idx = rand() % 4; + baseType = (BackgroundType) (BGType_TILES0 + idx); + } + + BackgroundType finalType = baseType; + if (baseType != BGType_WATER0 && baseType != BGType_WATER1) { + if (oreNrm > 0.86) { + double sub = (oreNrm - 0.86) / (1.0 - 0.86); + if (sub < 0.25) finalType = BGType_PLATINUM_ORE; + else if (sub < 0.50) finalType = BGType_GOLD_ORE; + else if (sub < 0.80) finalType = BGType_SILVER_ORE; + else finalType = BGType_IRON_ORE; + } + } + + backgroundMap[y][x].type = finalType; + } + } + + // Debug printout + printf("Terrain Noise: min = %f, max = %f\n", terrainMin, terrainMax); + printf("Humidity Noise: min = %f, max = %f\n", humidityMin, humidityMax); + printf("Ore Normalized: min = %f, max = %f\n", oreNrmMin, oreNrmMax); +} + diff --git a/player/player.c b/player/player.c index f642f0a..79a61f4 100644 --- a/player/player.c +++ b/player/player.c @@ -26,7 +26,7 @@ SDL_Rect targetItemRect; SDL_Color healthBarColor = {0, 240, 0, 255}; SDL_Color breakingBarColor = {128, 128, 0, 255}; -void setActivePlayerSlot(Player *plr, uint16_t activeSlotIndex) { +void setActivePlayerSlot(Player *plr, ItemType activeSlotIndex) { activeSlotIndex = activeSlotIndex % itemRegistryIndex; if (activeSlotIndex <= 0) { activeSlotIndex = 1; @@ -35,35 +35,30 @@ void setActivePlayerSlot(Player *plr, uint16_t activeSlotIndex) { } void moveActivePlayerSlot(Player *plr, bool up, bool seek) { - if (seek) { - uint16_t prevSlot = plr->inventory.activeSlotIndex; - uint16_t newSlot = prevSlot; + ItemType prevSlot = plr->inventory.activeSlotIndex; + ItemType newSlot = prevSlot; - do { - newSlot = (newSlot + (up ? 1 : ITEMREGISTRY_SIZE - 1)) % ITEMREGISTRY_SIZE; + do { + newSlot = (newSlot + (up ? 1 : ITEMREGISTRY_SIZE - 1)) % ITEMREGISTRY_SIZE; - // Stop if we've looped all the way around - if (newSlot == prevSlot) break; + // Stop if we've looped all the way around + if (newSlot == prevSlot) break; - // Stop if we found a slot with count > 0 - if (plr->inventory.slotCounts[newSlot] > 0 && newSlot != 0) break; - } while (true); - plr->inventory.activeSlotIndex = newSlot; - } else { - if (plr->inventory.activeSlotIndex == 1 && !up) { - plr->inventory.activeSlotIndex = itemRegistryIndex - 1; - } else if (plr->inventory.activeSlotIndex == itemRegistryIndex - 1 && up) { - plr->inventory.activeSlotIndex = 1; + // Stop if we found a slot with count > 0 + if (!strlen(ItemRegistry[newSlot].name)) continue; + if (newSlot == 0) continue; + if (seek) { + if (plr->inventory.slotCounts[newSlot] > 0) break; } else { - plr->inventory.activeSlotIndex = - (plr->inventory.activeSlotIndex + (up ? 1 : ITEMREGISTRY_SIZE - 1)) % ITEMREGISTRY_SIZE; - + break; } - } + + } while (true); + plr->inventory.activeSlotIndex = newSlot; } -void adjustRect(SDL_Rect *rect, SDL_Rect playerRect) { +inline void adjustRect(SDL_Rect *rect, SDL_Rect playerRect) { rect->x -= playerRect.x; rect->y -= playerRect.y; rect->x += DISPLAY_WIDTH / 2; @@ -117,7 +112,11 @@ void initPlayer(Player *plr) { plr->rect.w = TILE_SIZE; plr->rect.h = TILE_SIZE; - for (uint16_t ui = 0; ui < itemRegistryIndex; ui++) { + for (ItemType ui = 0; ui < tileTypeIndex; ui++) { + plr->inventory.slotCounts[ui] = 64; + } + + for (ItemType ui = ITEMREGISTRY_SIZE / 2; ui < itemRegistryIndex; ui++) { plr->inventory.slotCounts[ui] = 64; } @@ -172,7 +171,7 @@ void renderPlayer(Player *plr) { SDL_SetRenderDrawColor(mainRenderer, plr->cursor.canReach ? 0 : 255, plr->cursor.canReach ? 255 : 0, 0, 128); DrawThickRect(mainRenderer, plr->cursor.targetTileRect, 4); - uint16_t itemIndex = plr->inventory.activeSlotIndex; + ItemType itemIndex = plr->inventory.activeSlotIndex; SDL_Texture *itemTex; char itemStringCount[6]; if (itemIndex < itemRegistryIndex) { @@ -205,8 +204,12 @@ void renderPlayer(Player *plr) { renderBar(mainRenderer, (DISPLAY_WIDTH / 2) - 128, DISPLAY_HEIGHT - 50, 200, 8, playerMaxHealth, plr->health, healthBarColor, 4); - renderBar(mainRenderer, (DISPLAY_WIDTH / 2) - 128, DISPLAY_HEIGHT - 70, 200, 8, - getBreakTime(plr->cursor.targetTile->type), plr->cursor.breakingProgress, breakingBarColor, 4); + if (plr->cursor.targetTile) { + uint16_t tempko = getBreakTime(plr->cursor.targetTile->type); + uint16_t tempko2 = plr->cursor.breakingProgress; + renderBar(mainRenderer, (DISPLAY_WIDTH / 2) - 128, DISPLAY_HEIGHT - 70, 200, 8, + tempko, tempko2, breakingBarColor, 4); + } SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255); @@ -215,7 +218,7 @@ void renderPlayer(Player *plr) { targetItemRect.y = DISPLAY_HEIGHT - 30 + TILE_SIZE / 4 + 3; targetItemRect.x = TILE_SIZE / 4; - for (uint16_t i = 1; i < itemRegistryIndex; i++) { + for (ItemType i = 1; i < ITEMREGISTRY_SIZE; i++) { itemTex = ItemRegistry[i].textureOnBelt[plr->cursor.direction]; if (itemTex == NULL) { itemTex = ItemRegistry[i].textureOnBelt[ORIENT_LEFT]; @@ -228,17 +231,17 @@ void renderPlayer(Player *plr) { SDL_RenderCopy(mainRenderer, itemTex, NULL, &targetItemRect); SDL_SetTextureColorMod(itemTex, 255, 255, 255); + if (plr->inventory.activeSlotIndex == i) { + SDL_SetRenderDrawColor(mainRenderer, 16, plr->inventory.slotCounts[i] > 0 ? 128 : 16, + plr->inventory.slotCounts[i] > 0 ? 32 : 128, 255); + DrawThickRect(mainRenderer, targetItemRect, 4); + } + sprintf(itemStringCount, "%d", plr->inventory.slotCounts[i]); + renderText(mainRenderer, fonts[2], itemStringCount, targetItemRect.x - 3, + targetItemRect.y - (fonts[2].size * 3 / 2)); + //targetItemRect.x += (TILE_SIZE / 2) + (TILE_SIZE / 4); + targetItemRect.x += TILE_SIZE; } - if (plr->inventory.activeSlotIndex == i) { - SDL_SetRenderDrawColor(mainRenderer, 16, plr->inventory.slotCounts[i] > 0 ? 128 : 16, - plr->inventory.slotCounts[i] > 0 ? 32 : 128, 255); - DrawThickRect(mainRenderer, targetItemRect, 4); - } - sprintf(itemStringCount, "%d", plr->inventory.slotCounts[i]); - renderText(mainRenderer, fonts[2], itemStringCount, targetItemRect.x - 3, - targetItemRect.y - (fonts[2].size * 3 / 2)); - //targetItemRect.x += (TILE_SIZE / 2) + (TILE_SIZE / 4); - targetItemRect.x += TILE_SIZE; } SDL_SetRenderTarget(mainRenderer, originalTarget); diff --git a/player/player.h b/player/player.h index 0405f69..bcfc228 100644 --- a/player/player.h +++ b/player/player.h @@ -28,12 +28,13 @@ void adjustRect(SDL_Rect *rect, SDL_Rect playerRect); #define playerMaxHealth 255 -typedef struct { +typedef struct PlayerInventory { uint16_t slotCounts[ITEMREGISTRY_SIZE]; - uint16_t activeSlotIndex; + ItemType activeSlotIndex; + ItemType hotKeySlots[13]; } PlayerInventory; -typedef struct { +typedef struct PlayerCursor { int windowX; int windowY; int tileX; @@ -50,7 +51,7 @@ typedef struct { int breakingProgress; } PlayerCursor; -typedef struct { +typedef struct Player{ PlayerCursor cursor; PlayerInventory inventory; uint8_t health; @@ -59,7 +60,7 @@ typedef struct { SDL_Rect rect; } Player; -void setActivePlayerSlot(Player *plr, uint16_t activeSlotIndex); +void setActivePlayerSlot(Player *plr, ItemType activeSlotIndex); void moveActivePlayerSlot(Player *plr, bool up, bool seek); diff --git a/tiles/belt.c b/tiles/belt.c index f7dd1bc..16b6729 100644 --- a/tiles/belt.c +++ b/tiles/belt.c @@ -16,7 +16,7 @@ void renderBelt(int x, int y, int w, int h, OrientDirection dir, SDL_Rect player Tile *t = &tileMap[y][x]; - uint16_t tileType = t->type; + ItemType tileType = t->type; SDL_Rect src1, src2, dst1, dst2; @@ -59,11 +59,8 @@ void renderBelt(int x, int y, int w, int h, OrientDirection dir, SDL_Rect player SDL_RenderCopy(renderer, TileRegistry[tileType].textures[dir], &src1, &dst1); SDL_RenderCopy(renderer, TileRegistry[tileType].textures[dir], &src2, &dst2); } +} + +void updateBelt(Tile * tile) { - SDL_SetRenderTarget(renderer, itemsTexture); - for (uint8_t lane = 0; lane < 2; lane++) { - if (t->items[lane].type != 0) { - renderItem(t->items[lane], renderer, lane, playerRect); - } - } } \ No newline at end of file diff --git a/tiles/belt.h b/tiles/belt.h index 009e993..6573d73 100644 --- a/tiles/belt.h +++ b/tiles/belt.h @@ -7,9 +7,12 @@ #include #include -#include "tile.h" #include "../util/util.h" +struct Tile; + void renderBelt(int x, int y, int w, int h, OrientDirection dir, SDL_Rect playerRect, SDL_Renderer *renderer); +void updateBelt(struct Tile * tile); + #endif //FACTORYGAME_BELT_H diff --git a/tiles/furnace.c b/tiles/furnace.c index 360d4d7..55474ec 100644 --- a/tiles/furnace.c +++ b/tiles/furnace.c @@ -4,11 +4,48 @@ #include "furnace.h" #include "tile.h" +#include "../util/audio.h" -uint16_t getFurnaceNewItem(uint16_t sourceItem) { - uint16_t realItemIndex = sourceItem - tileTypeIndex - 1; - if (realItemIndex < 8) { - return sourceItem + 1; +const ItemType FurnaceRecipes[ITEMREGISTRY_SIZE] = { + [IRON_ORE] = IRON_INGOT, + [SILVER_ORE] = SILVER_INGOT, + [GOLD_ORE] = GOLD_INGOT, + [PLATINUM_ORE] = PLATINUM_INGOT +}; + + +void updateFurnace(Tile *tile) { + ItemOnBelt *inItem = &tile->items[FURNACE_INPUT_SLOT]; + ItemOnBelt *outItem = &tile->items[FURNACE_OUTPUT_SLOT]; + Item inItemType = ItemRegistry[inItem->type]; + + ItemType targetOutItemType = FurnaceRecipes[inItem->type]; + + Item targetOutItem = ItemRegistry[targetOutItemType]; + + if (targetOutItemType != TYPE_AIR) { + if (tile->miscVal == 0) { + tile->audioCh = getAvailableChannel(); + if (tile->audioCh < NUM_SYNTH_VOICES) { + audioData.synthVoices[tile->audioCh].volume = 1; + audioData.synthVoices[tile->audioCh].phase = 0; + audioData.synthVoices[tile->audioCh].sourceRect.x = TILE_SIZE * tile->x; + audioData.synthVoices[tile->audioCh].sourceRect.y = TILE_SIZE * tile->y; + audioData.synthVoices[tile->audioCh].waveform = WAVE_TRIANGLE; + audioData.synthVoices[tile->audioCh].frequency = 99; + } + } + if (tile->audioCh < NUM_SYNTH_VOICES) { + audioData.synthVoices[tile->audioCh].frequency++; + } + if (outItem->type == 0 && ++tile->miscVal >= targetOutItem.miscVal) { + if (tile->audioCh < NUM_SYNTH_VOICES) { + audioData.synthVoices[tile->audioCh].volume = 0; + } + tile->miscVal = 0; + inItem->type = 0; + outItem->type = targetOutItemType; + outItem->offset = -0.5f; + } } - return 0; } \ No newline at end of file diff --git a/tiles/furnace.h b/tiles/furnace.h index 96702c1..7f9f046 100644 --- a/tiles/furnace.h +++ b/tiles/furnace.h @@ -5,6 +5,14 @@ #ifndef FACTORYGAME_FURNACE_H #define FACTORYGAME_FURNACE_H +#include "../items/item.h" #include "stdint.h" +extern const ItemType FurnaceRecipes[]; + +#define FURNACE_INPUT_SLOT 0 +#define FURNACE_OUTPUT_SLOT 1 + +void updateFurnace(Tile * tile); + #endif //FACTORYGAME_FURNACE_H diff --git a/tiles/tile.c b/tiles/tile.c index d737534..76fd88a 100644 --- a/tiles/tile.c +++ b/tiles/tile.c @@ -5,18 +5,26 @@ #include #include "tile.h" #include "../player/player.h" +#include "furnace.h" +#include "../util/atlas.h" int scrollFrame = 0; unsigned long beltFrames = 0; +SDL_Texture *backgroundTexture; SDL_Texture *tilesTexture; SDL_Texture *itemsTexture; Tile tileMap[MAP_HEIGHT][MAP_WIDTH]; -uint16_t tileTypeIndex = 0; +BackgroundTile backgroundMap[MAP_HEIGHT][MAP_WIDTH]; + +uint16_t tileTypeIndex = 0; +uint16_t backgroundTileTypeIndex = 0; + +TileTypeReg TileRegistry[TILEREGISTRY_SIZE]; +BackgroundTileType BackgroundTileRegistry[TILEREGISTRY_SIZE]; -TileType TileRegistry[TILEREGISTRY_SIZE]; void generateTestMap() { for (int y = 0; y < DISPLAY_MAP_HEIGHT; y++) { @@ -32,7 +40,7 @@ void generateTestMap() { for (int y = 0; y < MAP_HEIGHT; y += 1) { tileMap[y][x].type = TYPE_BELT; - tileMap[y][x].frameOffset = 0; + tileMap[y][x].miscVal = 0; //tileMap[y][x].direction = ((x + y) % 4 * 2) + 1; //tileMap[y][x].direction = 5; tileMap[y][x].direction = (rand() % 4 * 2) + 1; @@ -58,26 +66,68 @@ void registerTile(char name[20], SDL_Renderer *renderer) { TileRegistry[tileTypeIndex].textures[ORIENT_RIGHT] = createFlippedTexture(renderer, texture, SDL_FLIP_HORIZONTAL); TileRegistry[tileTypeIndex].textures[ORIENT_UP] = createRotatedTexture(renderer, texture, 90); TileRegistry[tileTypeIndex].textures[ORIENT_DOWN] = createRotatedTexture(renderer, texture, 270); + if (tileTypeIndex == 0) { + SDL_SetTextureAlphaMod(TileRegistry[0].textures[ORIENT_LEFT], 64); + SDL_SetTextureAlphaMod(TileRegistry[0].textures[ORIENT_RIGHT], 64); + SDL_SetTextureAlphaMod(TileRegistry[0].textures[ORIENT_UP], 64); + SDL_SetTextureAlphaMod(TileRegistry[0].textures[ORIENT_DOWN], 64); + } + SDL_SetTextureBlendMode(TileRegistry[tileTypeIndex].textures[ORIENT_LEFT], SDL_BLENDMODE_BLEND); + SDL_SetTextureBlendMode(TileRegistry[tileTypeIndex].textures[ORIENT_RIGHT], SDL_BLENDMODE_BLEND); + SDL_SetTextureBlendMode(TileRegistry[tileTypeIndex].textures[ORIENT_UP], SDL_BLENDMODE_BLEND); + SDL_SetTextureBlendMode(TileRegistry[tileTypeIndex].textures[ORIENT_DOWN], SDL_BLENDMODE_BLEND); + + TileRegistry[tileTypeIndex].atlasRects[ORIENT_LEFT] = allocate_32x32(TileRegistry[tileTypeIndex].textures[ORIENT_LEFT], renderer); + TileRegistry[tileTypeIndex].atlasRects[ORIENT_RIGHT] = allocate_32x32(TileRegistry[tileTypeIndex].textures[ORIENT_RIGHT], renderer); + TileRegistry[tileTypeIndex].atlasRects[ORIENT_UP] = allocate_32x32(TileRegistry[tileTypeIndex].textures[ORIENT_UP], renderer); + TileRegistry[tileTypeIndex].atlasRects[ORIENT_DOWN] = allocate_32x32(TileRegistry[tileTypeIndex].textures[ORIENT_DOWN], renderer); + TileRegistry[tileTypeIndex].type = tileTypeIndex; TileRegistry[tileTypeIndex].breakTime = 15; tileTypeIndex++; } +void registerBackgroundTile(char name[20], SDL_Renderer *renderer) { + const char *dot = strchr(name, '.'); + memcpy(BackgroundTileRegistry[backgroundTileTypeIndex].name, name, dot - name); + char texturePath[80]; + snprintf(texturePath, 80, "./assets/backgrounds/%s", name); + SDL_Texture *texture = IMG_LoadTexture(renderer, texturePath); + BackgroundTileRegistry[backgroundTileTypeIndex].texture = texture; + SDL_SetTextureBlendMode(BackgroundTileRegistry[backgroundTileTypeIndex].texture, SDL_BLENDMODE_NONE); + BackgroundTileRegistry[backgroundTileTypeIndex].atlasRect = allocate_32x32(BackgroundTileRegistry[backgroundTileTypeIndex].texture, renderer); + + BackgroundTileRegistry[backgroundTileTypeIndex].type = backgroundTileTypeIndex; + + backgroundTileTypeIndex++; +} + void loadTiles(SDL_Renderer *renderer) { iterateSortedDir("./assets/tiles", (DirEntryCallback) registerTile, renderer); -// DIR *dir = opendir("./assets/tiles"); -// if (dir) { -// struct dirent *entry; -// while ((entry = readdir(dir)) != NULL) { -// if (entry->d_name[0] == '.') { -// continue; -// } -// registerTile(entry->d_name, mainRenderer); -// } -// } - TileRegistry[0].breakTime = 0; + iterateSortedDir("./assets/backgrounds", (DirEntryCallback) registerBackgroundTile, renderer); + +} + +void setupTiles() { + TileRegistry[TYPE_AIR].breakTime = 0; + TileRegistry[TYPE_BELT].itemMoves = true; + for (uint16_t i = 0; i < ItemSlotCount; i++) { + TileRegistry[TYPE_BELT].outputLane[i] = true; + } + for (uint16_t l = 0; l < ItemSlotCount; l++) { + for (ItemType i = 0; i < itemRegistryIndex; i++) { + TileRegistry[TYPE_BELT].allowedInItems[l][i] = true; + } + } + + for (ItemType i = 0; i < itemRegistryIndex; i++) { + if (FurnaceRecipes[i] != 0) { + TileRegistry[TYPE_FURNACE].allowedInItems[FURNACE_INPUT_SLOT][i] = true; + } + } + TileRegistry[TYPE_FURNACE].outputLane[FURNACE_OUTPUT_SLOT] = 1; } uint16_t getBreakTime(int type) { @@ -92,6 +142,9 @@ void initTiles() { screenRect.h); tilesTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, screenRect.w, screenRect.h); + backgroundTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, + screenRect.w, + screenRect.h); SDL_SetTextureBlendMode(itemsTexture, SDL_BLENDMODE_BLEND); SDL_SetTextureBlendMode(tilesTexture, SDL_BLENDMODE_BLEND); } @@ -99,10 +152,14 @@ void initTiles() { void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_Texture *oldTarget = SDL_GetRenderTarget(renderer); - SDL_SetRenderTarget(renderer, itemsTexture); - SDL_RenderClear(renderer); - SDL_SetRenderTarget(renderer, tilesTexture); - SDL_RenderClear(renderer); + + int tileSize = TILE_SIZE; + + int minTileX = (playerRect.x / TILE_SIZE) - (DISPLAY_MAP_WIDTH / 2) - 1; + int maxTileX = (playerRect.x / TILE_SIZE) + (DISPLAY_MAP_WIDTH / 2) + 1; + int minTileY = (playerRect.y / TILE_SIZE) - (DISPLAY_MAP_HEIGHT / 2) - 1; + int maxTileY = (playerRect.y / TILE_SIZE) + (DISPLAY_MAP_HEIGHT / 2) + 1; + int scrollSpeed = 1; // pixels per step int scrollDelay = 1; // frames between steps @@ -110,42 +167,83 @@ void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect) { scrollFrame += scrollSpeed; } - int tileSize = TILE_SIZE; - for (int y = (playerRect.y / TILE_SIZE) - (DISPLAY_MAP_HEIGHT / 2) - 1; - y < (playerRect.y / TILE_SIZE) + (DISPLAY_MAP_HEIGHT / 2) + 1; y++) { - if (y < 0 || y >= MAP_HEIGHT) { - continue; - } - for (int x = (playerRect.x / TILE_SIZE) - (DISPLAY_MAP_WIDTH / 2) - 1; - x < (playerRect.x / TILE_SIZE) + (DISPLAY_MAP_WIDTH / 2) + 1; x++) { - if (x < 0 || x >= MAP_WIDTH) { - continue; + // --- Render background tiles --- + SDL_SetRenderTarget(renderer, backgroundTexture); + SDL_RenderClear(renderer); + + for (int y = minTileY; y < maxTileY; y++) { + if (y < 0 || y >= MAP_HEIGHT) continue; + for (int x = minTileX; x < maxTileX; x++) { + if (x < 0 || x >= MAP_WIDTH) continue; + + SDL_Rect dstRect = { + .x = x * TILE_SIZE, + .y = y * TILE_SIZE, + .w = TILE_SIZE, + .h = TILE_SIZE + }; + adjustRect(&dstRect, playerRect); + + BackgroundTile bt = backgroundMap[y][x]; + SDL_Texture *tex = BackgroundTileRegistry[bt.type].texture; + if (tex != NULL) { + SDL_RenderCopy(renderer, tex, NULL, &dstRect); } + } + } + + SDL_SetRenderTarget(renderer, tilesTexture); + SDL_RenderClear(renderer); + for (int y = minTileY; y < maxTileY; y++) { + if (y < 0 || y >= MAP_HEIGHT) continue; + for (int x = minTileX; x < maxTileX; x++) { + if (x < 0 || x >= MAP_WIDTH) continue; + + SDL_Rect dstRect = { + .x = x * TILE_SIZE, + .y = y * TILE_SIZE, + .w = TILE_SIZE, + .h = TILE_SIZE + }; + adjustRect(&dstRect, playerRect); + Tile t = tileMap[y][x]; - SDL_SetRenderTarget(renderer, tilesTexture); switch (t.type) { case TYPE_BELT: renderBelt(x, y, tileSize, tileSize, t.direction, playerRect, renderer); break; - default: - SDL_Rect dstRect; - dstRect.x = x * TILE_SIZE; - dstRect.y = y * TILE_SIZE; - dstRect.w = TILE_SIZE; - dstRect.h = TILE_SIZE; - adjustRect(&dstRect, playerRect); + default: { SDL_Texture *tex = TileRegistry[t.type].textures[t.direction]; - if (tex == NULL) { - tex = TileRegistry[t.type].textures[ORIENT_LEFT]; - } + if (tex == NULL) tex = TileRegistry[t.type].textures[ORIENT_LEFT]; if (tex != NULL) { SDL_RenderCopy(renderer, tex, NULL, &dstRect); } - - } - if (t.type == TYPE_BELT) { + } } } } + + SDL_SetRenderTarget(renderer, itemsTexture); + SDL_RenderClear(renderer); + for (int y = minTileY; y < maxTileY; y++) { + if (y < 0 || y >= MAP_HEIGHT) continue; + for (int x = minTileX; x < maxTileX; x++) { + if (x < 0 || x >= MAP_WIDTH) continue; + + Tile t = tileMap[y][x]; + if (t.type == TYPE_BELT || itemViewing) { + for (uint8_t lane = 0; lane < ItemSlotCount; lane++) { + if (t.items[lane].type != 0) { + renderItem(t.items[lane], renderer, lane, playerRect); + } + } + } + } + } + SDL_SetRenderTarget(renderer, oldTarget); +} + +void updateTiles() { + } \ No newline at end of file diff --git a/tiles/tile.h b/tiles/tile.h index a5478ce..fb5d115 100644 --- a/tiles/tile.h +++ b/tiles/tile.h @@ -5,15 +5,15 @@ #ifndef FACTORYGAME_TILE_H #define FACTORYGAME_TILE_H -#include "belt.h" #include "../util/util.h" #include "../items/item.h" +//#include "../items/item.h" -#define MAP_WIDTH 600 -#define MAP_HEIGHT 340 +#define MAP_WIDTH 100 +#define MAP_HEIGHT 100 -#define DISPLAY_MAP_WIDTH 44 -#define DISPLAY_MAP_HEIGHT 22 +#define DISPLAY_MAP_WIDTH 60 +#define DISPLAY_MAP_HEIGHT 31 #define TILE_SIZE 32 @@ -22,42 +22,108 @@ extern SDL_Texture *tilesTexture; extern SDL_Texture *itemsTexture; +extern SDL_Texture *backgroundTexture; extern int scrollFrame; extern unsigned long beltFrames; -typedef struct { - uint16_t type; + +#define ItemSlotCount 4 + +typedef enum BackgroundType { + BGType_WATER0, + BGType_WATER1, + BGType_GRASS0, + BGType_GRASS1, + BGType_GRASS2, + BGType_GRASS3, + BGType_GRASS4, + BGType_GRASS5, + BGType_GRASS6, + BGType_GRASS7, + BGType_GRASS_FLOWER0, + BGType_GRASS_FLOWER1, + BGType_GRASS_FLOWER2, + BGType_GRASS_FLOWER3, + BGType_SAND0, + BGType_SAND1, + BGType_SAND2, + BGType_SAND3, + BGType_SAND4, + BGType_SAND5, + BGType_SAND6, + BGType_SAND7, + BGType_TILES0, + BGType_TILES1, + BGType_TILES2, + BGType_TILES3, + BGType_COBBLE0, + BGType_COBBLE1, + BGType_COBBLE2, + BGType_COBBLE3, + BGType_PLATINUM_ORE, + BGType_GOLD_ORE, + BGType_SILVER_ORE, + BGType_IRON_ORE, + BGType_END +} BackgroundType; + +typedef struct TileTypeReg { + ItemType type; char name[20]; SDL_Texture *textures[ORIENT_DIRECTION_COUNT]; + SDL_Rect atlasRects[ORIENT_DIRECTION_COUNT]; uint16_t breakTime; -} TileType; + bool itemMoves; + bool allowedInItems[ItemSlotCount][ITEMREGISTRY_SIZE]; + bool outputLane[ItemSlotCount]; +} TileTypeReg; -#define TILEREGISTRY_SIZE 512 +typedef struct BackgroundTileType { + ItemType type; + char name[20]; + SDL_Texture *texture; + SDL_Rect atlasRect; +} BackgroundTileType; -extern TileType TileRegistry[TILEREGISTRY_SIZE]; +typedef struct BackgroundTile { + BackgroundType type; +} BackgroundTile; -#define TYPE_AIR 0 -#define TYPE_BELT 2 +#define TILEREGISTRY_SIZE 64 + +extern BackgroundTileType BackgroundTileRegistry[TILEREGISTRY_SIZE]; + +extern TileTypeReg TileRegistry[TILEREGISTRY_SIZE]; void renderAllTiles(SDL_Renderer *renderer, SDL_Rect playerRect); -typedef struct { +void updateTiles(); + +typedef struct Tile { OrientDirection direction; - uint16_t type; - int frameOffset; - ItemOnBelt items[2]; + ItemType type; + int miscVal; + ItemOnBelt items[ItemSlotCount]; + uint16_t audioCh; int x; int y; } Tile; + extern Tile tileMap[MAP_HEIGHT][MAP_WIDTH]; +extern BackgroundTile backgroundMap[MAP_HEIGHT][MAP_WIDTH]; + +void setupTiles(); + void generateTestMap(); void loadTiles(SDL_Renderer *renderer); extern uint16_t tileTypeIndex; +extern uint16_t backgroundTileTypeIndex; + uint16_t getBreakTime(int type); diff --git a/tiles/tilecallbacks.c b/tiles/tilecallbacks.c new file mode 100644 index 0000000..4924b72 --- /dev/null +++ b/tiles/tilecallbacks.c @@ -0,0 +1,13 @@ +// +// Created by bruno on 1.6.2025. +// + +#include "tilecallbacks.h" +#include "furnace.h" + +const UpdateTileCallback ItemTileCallbacks[TILEREGISTRY_SIZE] = { + [TYPE_AIR] = NULL, + [TYPE_BLOCK] = NULL, + [TYPE_BELT] = updateBelt, + [TYPE_FURNACE] = updateFurnace +}; \ No newline at end of file diff --git a/tiles/tilecallbacks.h b/tiles/tilecallbacks.h new file mode 100644 index 0000000..62ef2f7 --- /dev/null +++ b/tiles/tilecallbacks.h @@ -0,0 +1,14 @@ +// +// Created by bruno on 1.6.2025. +// + +#ifndef FACTORYGAME_TILECALLBACKS_H +#define FACTORYGAME_TILECALLBACKS_H + + +#include "tile.h" + +typedef void (*UpdateTileCallback)(struct Tile *tile); + +extern const UpdateTileCallback ItemTileCallbacks[]; +#endif //FACTORYGAME_TILECALLBACKS_H diff --git a/util/atlas.c b/util/atlas.c new file mode 100644 index 0000000..754248a --- /dev/null +++ b/util/atlas.c @@ -0,0 +1,80 @@ +// +// Created by bruno on 1.6.2025. +// + +#include "atlas.h" +#include "util.h" + +SDL_Texture *atlasTexture; + +int atlasX = 0, atlasY = 0; + +int tileIndex = 0; // Which 32x32 tile we're on +int quadrantIndex = 0; // Which 16x16 slot inside that tile + + + +SDL_Rect allocate_16x16(SDL_Texture *srcTexture, SDL_Renderer *renderer) { + SDL_Texture * oldTarget = SDL_GetRenderTarget(renderer); + SDL_SetRenderTarget(renderer, atlasTexture); + int tileX = tileIndex % ATLAS_TILES_PER_ROW; + int tileY = tileIndex / ATLAS_TILES_PER_ROW; + + int dx = (quadrantIndex % 2) * QUADRANT_SIZE; + int dy = (quadrantIndex / 2) * QUADRANT_SIZE; + + SDL_Rect destRect = { + tileX * TILE_SIZE + dx, + tileY * TILE_SIZE + dy, + QUADRANT_SIZE, + QUADRANT_SIZE + }; + SDL_RenderCopy(renderer, srcTexture, NULL, &destRect); + + quadrantIndex++; + if (quadrantIndex >= 4) { + tileIndex++; + quadrantIndex = 0; + } + SDL_SetRenderTarget(renderer, oldTarget); + return destRect; +} + +SDL_Rect allocate_32x32(SDL_Texture *srcTexture, SDL_Renderer *renderer) { + SDL_Texture * oldTarget = SDL_GetRenderTarget(renderer); + SDL_SetRenderTarget(renderer, atlasTexture); + // If we’re not at the start of a tile, skip to the next clean one + if (quadrantIndex != 0) { + tileIndex++; + quadrantIndex = 0; + } + + int tileX = tileIndex % ATLAS_TILES_PER_ROW; + int tileY = tileIndex / ATLAS_TILES_PER_ROW; + + SDL_Rect destRect = { + tileX * TILE_SIZE, + tileY * TILE_SIZE, + TILE_SIZE, + TILE_SIZE + }; + SDL_RenderCopy(renderer, srcTexture, NULL, &destRect); + + tileIndex++; // Move to next tile + // quadrantIndex stays 0 — new tile is fresh + SDL_SetRenderTarget(renderer, oldTarget); + return destRect; +} + +void initAtlas(SDL_Renderer *renderer) { + +// Clear atlas with transparent + SDL_SetRenderTarget(renderer, atlasTexture); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + + + atlasTexture = SDL_CreateTexture(renderer, + SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, + ATLAS_SIZE, ATLAS_SIZE); +} \ No newline at end of file diff --git a/util/atlas.h b/util/atlas.h new file mode 100644 index 0000000..85b4a3f --- /dev/null +++ b/util/atlas.h @@ -0,0 +1,23 @@ +// +// Created by bruno on 1.6.2025. +// + +#ifndef FACTORYGAME_ATLAS_H +#define FACTORYGAME_ATLAS_H + +#define ATLAS_SIZE 512 +#define TILE_SIZE 32 +#define QUADRANT_SIZE 16 +#define ATLAS_TILES_PER_ROW (ATLAS_SIZE / TILE_SIZE) + +#include "SDL2/SDL.h" + +extern SDL_Texture *atlasTexture; + +SDL_Rect allocate_32x32(SDL_Texture *srcTexture, SDL_Renderer *renderer); + +SDL_Rect allocate_16x16(SDL_Texture *srcTexture, SDL_Renderer *renderer); + +void initAtlas(SDL_Renderer *renderer); + +#endif //FACTORYGAME_ATLAS_H diff --git a/util/audio.c b/util/audio.c index fa87587..2260e0d 100644 --- a/util/audio.c +++ b/util/audio.c @@ -6,21 +6,93 @@ AudioData audioData; +uint16_t getAvailableChannel() { + for (uint16_t i = 0; i < NUM_SYNTH_VOICES; i++) { + if (audioData.synthVoices[i].volume == 0) { + return i; + } + } + return -1; +} + +// Helper: compute left/right gains from a pan value in [–1..+1] +// pan = –1.0 → full left (L=1, R=0) +// pan = +1.0 → full right (L=0, R=1) +// pan = 0.0 → center (L=R=1/sqrt(2) or just 0.707 to avoid clipping) +static void compute_stereo_gains(float pan, float *outL, float *outR) { + // Simple linear panning (no constant‐power law). + // If you prefer constant‐power, you could do: + // float angle = (pan + 1.0f) * (M_PI / 4.0f); + // *outL = cosf(angle); + // *outR = sinf(angle); + // + // Here we’ll just do linear: + pan = fmaxf(-1.0f, fminf(+1.0f, pan)); + if (pan <= 0.0f) { + *outL = 1.0f; + *outR = 1.0f + pan; // pan is negative, so R < 1 + } else { + *outL = 1.0f - pan; // pan is positive, so L < 1 + *outR = 1.0f; + } + // Optionally, scale down both so we never exceed 1.0f / sqrt(2) + // e.g. *outL *= 0.7071f; *outR *= 0.7071f; +} + +// This callback now writes stereo frames: interleaved L/R floats. void audio_callback(void *userdata, Uint8 *stream, int len) { AudioData *audio = (AudioData *) userdata; - int samples = len / sizeof(float); - for (int i = 0; i < samples; i++) { - float mix = 0.0f; - int activeVoices = 0; + // 'len' is total bytes; each sample‐frame is 2 floats (L+R), i.e. 2 * sizeof(float). + int frames = len / (2 * sizeof(float)); - for (int v = 0; v < NUM_SYNTH_VOICES; v++) { - SynthVoice *voice = &audio->synthVoices[v]; - if (voice->volume == 0 || voice->frequency == 0) continue; + // Zero out the entire output buffer (silence) + // We’ll accumulate into it. + // Each float is 4 bytes, so total floats = 2 * frames. + float *outBuf = (float *) stream; + for (int i = 0; i < 2 * frames; ++i) { + outBuf[i] = 0.0f; + } - float sample; + // Precompute the listener center + float listenerCx = audio->playerRect->x + audio->playerRect->w * 0.5f; + + // For each synth voice, mix into the stereo buffer + for (int v = 0; v < NUM_SYNTH_VOICES; v++) { + SynthVoice *voice = &audio->synthVoices[v]; + if (voice->volume == 0 || voice->frequency == 0) { + continue; // skip silent or inactive voices + } + + // Compute source center X + float sourceCx = voice->sourceRect.x + voice->sourceRect.w * 0.5f; + float dx = sourceCx - listenerCx; + + // Normalize for pan. If |dx| >= maxPanDistance → full left or full right. + float pan = dx / audio->maxPanDistance; + if (pan < -1.0f) pan = -1.0f; + if (pan > +1.0f) pan = +1.0f; + + float gainL, gainR; + compute_stereo_gains(pan, &gainL, &gainR); + + // Optional: You could also attenuate overall volume with distance + // float dist = fabsf(dx); + // float distanceAtten = 1.0f - fminf(dist / audio->maxPanDistance, 1.0f); + // float finalVolume = (voice->volume / 255.0f) * distanceAtten; + // But for now, we’ll just use voice->volume for amplitude. + + float amp = (voice->volume / 255.0f); + + // Phase increment per sample‐frame: + // (freq * 256) / SAMPLE_RATE tells how many phase steps per mono-sample. + // Because we’re writing stereo, we still advance phase once per frame. + uint8_t phaseInc = (uint8_t)((voice->frequency * 256) / SAMPLE_RATE); + + // Mix into each frame + for (int i = 0; i < frames; i++) { float t = (float) voice->phase / 255.0f * 2.0f - 1.0f; - + float sample; switch (voice->waveform) { default: case WAVE_SINE: @@ -33,18 +105,27 @@ void audio_callback(void *userdata, Uint8 *stream, int len) { sample = t; break; case WAVE_TRIANGLE: - sample = (t < 0) ? -t : t; + sample = (t < 0.0f) ? -t : t; break; case WAVE_NOISE: sample = ((float) rand() / RAND_MAX) * 2.0f - 1.0f; break; } - voice->phase += (uint8_t) ((voice->frequency * 256) / SAMPLE_RATE); - mix += sample * (voice->volume / 255.0f); - activeVoices++; - } + voice->phase += phaseInc; - ((float *) stream)[i] = (activeVoices > 0) ? mix / activeVoices : 0.0f; + // Interleaved index: left = 2*i, right = 2*i + 1 + int idxL = 2 * i; + int idxR = 2 * i + 1; + + // Accumulate into buffer + outBuf[idxL] += sample * amp * gainL; + outBuf[idxR] += sample * amp * gainR; + } } -} + + // Note: We did not normalize by active voices here, because each voice already + // uses its own volume. If you still want an automatic “divide by N active voices”, + // you would need to track active voices per‐frame, which is relatively expensive. + // In practice, you manage the volume per voice so clipping doesn’t occur. +} \ No newline at end of file diff --git a/util/audio.h b/util/audio.h index 1c770a6..e4f9bf7 100644 --- a/util/audio.h +++ b/util/audio.h @@ -11,9 +11,9 @@ #include #define SAMPLE_RATE 44100 -#define NUM_SYNTH_VOICES 3 +#define NUM_SYNTH_VOICES 256 -typedef enum { +typedef enum Waveform { WAVE_SINE, WAVE_SQUARE, WAVE_SAWTOOTH, @@ -21,19 +21,24 @@ typedef enum { WAVE_NOISE } Waveform; -typedef struct { - uint8_t volume; - uint16_t frequency; - uint8_t phase; +typedef struct SynthVoice { Waveform waveform; + uint8_t phase; + uint16_t frequency; + uint8_t volume; + SDL_Rect sourceRect; } SynthVoice; -typedef struct { +typedef struct AudioData { SynthVoice synthVoices[NUM_SYNTH_VOICES]; + SDL_Rect *playerRect; + float maxPanDistance; } AudioData; extern AudioData audioData; void audio_callback(void *userdata, Uint8 *stream, int len); +uint16_t getAvailableChannel(); + #endif //RISCB_AUDIO_H diff --git a/util/font.h b/util/font.h index e1f119b..3430e9b 100644 --- a/util/font.h +++ b/util/font.h @@ -11,7 +11,7 @@ #define fontCount 4 -typedef struct { +typedef struct BitmapFont { SDL_Texture *texture[256]; SDL_Surface *surface[256]; uint8_t size; diff --git a/util/perlin.c b/util/perlin.c new file mode 100644 index 0000000..1fb5c99 --- /dev/null +++ b/util/perlin.c @@ -0,0 +1,52 @@ + +#include + +double rawnoise(int n) { + n = (n << 13) ^ n; + return (1.0 - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0); +} + +double noise2d(int x, int y, int octave, int seed) { + return rawnoise(x * 1619 + y * 31337 + octave * 3463 + seed * 13397); +} + +double interpolate(double a, double b, double x) { + double f = (1 - cos(x * 3.141593)) * 0.5; + + return a * (1 - f) + b * f; +} + +double smooth2d(double x, double y, int octave, int seed) { + int intx = (int) x; + double fracx = x - intx; + int inty = (int) y; + double fracy = y - inty; + + double v1 = noise2d(intx, inty, octave, seed); + double v2 = noise2d(intx + 1, inty, octave, seed); + double v3 = noise2d(intx, inty + 1, octave, seed); + double v4 = noise2d(intx + 1, inty + 1, octave, seed); + + double i1 = interpolate(v1, v2, fracx); + double i2 = interpolate(v3, v4, fracx); + + return interpolate(i1, i2, fracy); +} + + +double pnoise2d(double x, double y, double persistence, int octaves, int seed) { + double total = 0.0; + double frequency = 1.0; + double amplitude = 1.0; + double max = 0.0; + + for (int i = 0; i < octaves; i++) { + total += smooth2d(x * frequency, y * frequency, i, seed) * amplitude; + max += amplitude; + frequency /= 2.0; + amplitude *= persistence; + } + + // Normalize to [0, 1] + return (total + max) / (2.0 * max); +} diff --git a/util/perlin.h b/util/perlin.h new file mode 100644 index 0000000..17b72ee --- /dev/null +++ b/util/perlin.h @@ -0,0 +1,10 @@ +// +// Created by bruno on 1.6.2025. +// + +#ifndef FACTORYGAME_PERLIN_H +#define FACTORYGAME_PERLIN_H + +double pnoise2d(double x, double y, double persistence, int octaves, int seed); + +#endif //FACTORYGAME_PERLIN_H diff --git a/util/util.c b/util/util.c index 34dd837..b916a60 100644 --- a/util/util.c +++ b/util/util.c @@ -6,11 +6,13 @@ #include "util.h" //#include "font.h" + //The window we'll be rendering to SDL_Window *window = NULL; volatile bool running = true; bool debugMode = false; +bool itemViewing = false; //The surface contained by the window SDL_Renderer *mainRenderer = NULL; diff --git a/util/util.h b/util/util.h index aef74a5..5695a8e 100644 --- a/util/util.h +++ b/util/util.h @@ -16,7 +16,7 @@ extern SDL_Renderer *mainRenderer; extern SDL_Rect screenRect; -typedef enum { +typedef enum OrientDirection{ ORIENT_LEFT_DOWN, ORIENT_LEFT, ORIENT_LEFT_UP, @@ -29,6 +29,7 @@ typedef enum { } OrientDirection; extern bool debugMode; +extern bool itemViewing; SDL_Texture *createRotatedTexture(SDL_Renderer *renderer, SDL_Texture *src, double angle);