From cddd6289e61793c70923f0f8864bfb06ce2ae9e3 Mon Sep 17 00:00:00 2001 From: fk Date: Wed, 11 Feb 2026 12:00:14 +0200 Subject: [PATCH] 'local' --- LICENSE | 5 + README.md | 2 + __pycache__/camera.cpython-313.pyc | Bin 0 -> 2048 bytes __pycache__/enemy.cpython-313.pyc | Bin 0 -> 1540 bytes __pycache__/enemy_manager.cpython-313.pyc | Bin 0 -> 1860 bytes __pycache__/game.cpython-313.pyc | Bin 0 -> 6566 bytes __pycache__/inventory.cpython-313.pyc | Bin 0 -> 4731 bytes __pycache__/items.cpython-313.pyc | Bin 0 -> 1509 bytes __pycache__/player.cpython-313.pyc | Bin 0 -> 4944 bytes __pycache__/settings.cpython-313.pyc | Bin 0 -> 2502 bytes __pycache__/tiles.cpython-313.pyc | Bin 0 -> 2193 bytes __pycache__/world.cpython-313.pyc | Bin 0 -> 9109 bytes camera.py | 21 +++ enemy.py | 17 ++ enemy_manager.py | 28 +++ game.py | 143 +++++++++++++++ inventory.py | 90 ++++++++++ items.py | 68 ++++++++ main.py | 12 ++ player.py | 111 ++++++++++++ settings.py | 173 ++++++++++++++++++ tiles.py | 38 ++++ world.py | 203 ++++++++++++++++++++++ 23 files changed, 911 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 __pycache__/camera.cpython-313.pyc create mode 100644 __pycache__/enemy.cpython-313.pyc create mode 100644 __pycache__/enemy_manager.cpython-313.pyc create mode 100644 __pycache__/game.cpython-313.pyc create mode 100644 __pycache__/inventory.cpython-313.pyc create mode 100644 __pycache__/items.cpython-313.pyc create mode 100644 __pycache__/player.cpython-313.pyc create mode 100644 __pycache__/settings.cpython-313.pyc create mode 100644 __pycache__/tiles.cpython-313.pyc create mode 100644 __pycache__/world.cpython-313.pyc create mode 100644 camera.py create mode 100644 enemy.py create mode 100644 enemy_manager.py create mode 100644 game.py create mode 100644 inventory.py create mode 100644 items.py create mode 100644 main.py create mode 100644 player.py create mode 100644 settings.py create mode 100644 tiles.py create mode 100644 world.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a6b3bea --- /dev/null +++ b/LICENSE @@ -0,0 +1,5 @@ +Copyright (C) 2026 by fkalnins fkalnins.e@rkg.lv + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ee690a3 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Projectdd + diff --git a/__pycache__/camera.cpython-313.pyc b/__pycache__/camera.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e426f0129d043645dba1b92dce12c5d1c39c1c74 GIT binary patch literal 2048 zcmb7F-ER{|5Z^u9mm`U5Q>OtEf?S9wj_XuH{h&ZeqL42|nAVrjNH$ts?Th8=a%c7~ zCGHbXC{)mtN)d@vDey!hRida5@Mq)%6490iq(0@XN>uf=GiRSe6Q`vk<>%Sinc2PH z%ucq$;bsDIe&BZLSBa3{ap6m_rfm0svPe{-(s9D^R~o0ENO5wQ94Bg^gQ&reAQpv7 zMVbh0qMA-nL3{G3`a)~U_WPhLk{sbwg%FkUfGTwm9>iokqy_O}2qG@>jk z`}-1Tfsc0kLMxJ=zY}C(?0zOk= zESk~e+x9*8ZQo<(cPifS+y^rOTHmM#s`4f>sg%j+6?e+R$vS zu2pPo{dhdifej+$*>XX1bP=w#Uv-IBwQmC4Ag^MF?wz}P?nUf`8$0nLmULsupN*%Q z+uP^H`sS652HP`FuerVbZmfS^d8M>2wA^Z02!9t|I`uI7Ap7v@gR5@G>E|C0yGMpy zW%y2b_Df}%? zlz@job%btW6`=R9HTQ5K0%gno2Nw}L&vo5oj>WE9+$f0l8Y{c(ZII6X008)j#(o&M zD=)RJbS`(UbS-ze(UYrz)qyqnm$oOJk2{}qJ??T(U09Fwtt)*VPf!W(IyjEfi32!t zO_;wha+?{Ou?n*W>aC1jo7IfkPK2@P61N?rWa_5H7{^J(yAa+*I0+!$Va#?kr<7-! zK8cjg0$O*0R9M2N`E8am!$o#;6d6?w+Nya_&2V>ZnOXZ literal 0 HcmV?d00001 diff --git a/__pycache__/enemy.cpython-313.pyc b/__pycache__/enemy.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf9fa9965b66cde09fb3303b69e615af28525584 GIT binary patch literal 1540 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 W8tJd?E2i^nlTXL)d%|E&`TPfb{T(|1 literal 0 HcmV?d00001 diff --git a/__pycache__/inventory.cpython-313.pyc b/__pycache__/inventory.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91680c98a4699e72b9cbd075cf327599e0d92431 GIT binary patch literal 4731 zcmbUlTTC0-^^RW{+ki2)F*X51fM5vOB?%Wf7+XEbs zQvF4OicfJCNEN9ENEJ1(I#{DZoyF<_GZrxeV3kFz0O%k7Rl>Z&Z0A9Oi{d*`m-27+e6I7>V8z z(b-=4{Dm{)0Wm_wasT@xgX0%t_l0OI6c)#?(b%2HgcJ_<+zs)<_>_u{^Xds7o0^u5 z9LEd1#Bs8%c=E?o8oE)OgzgiPb=1dok4;}2h_eBKtTLE4ZA}ULlKYuEW$R8dsoGxP z88&UriMFNgXWc1VPqHOddjg4D4Q?2!v+CecuzFm~l5IQ`*L`7Z*alT(052AK1j^BO&%0ZmU8Y)C6=jsP}EZg|VB7GUyR9|N&Q zYhb2NOFb~i6&Pn6AC?WFyRmzMB%)KltRiUMNM2i%*Otf^<8N1CE5>ax_IBw&Ek@lQDrC<64dzzwrxDVJ*8-ZL zE3v?ur76;2N=i@y1v3C^OpMXyRV=R=vaelg_i`he#h&YAjcXZhZi&n`T*mv ze|^@CYvp{zR`$Tu@}6Q%$U9j)N6@b+tKSUzEk*r?1N8so|JGmin*M{%8G>?uLsq@<6#VcBnBRRF~}JyT_3=P_5q2H;lyEK=qzTGzH&Mof4>2{e8wpNEq_yl|O-B zKET*oRrwvMeU`p@T(BRo1AD;C*5D1uXHmc$yVxEpO{@dU1;I4PT=_d8 zX5xi7zw%8Ug8I-oDjO$af*6a2#2j2OlEZB0)8?9Bpgy$&!haFQ;K_G*At=1xWGsi( z1;q#Mq4gS#h@yAcE5*DM(U=$kxzCY{VV>{67?Qh$D3%1kKZo!!wFANfgz^68J0n0j zOrKD|?5aoM941NMA=xlBJsG+ik#)CXf+Sb4)8ZurbbcrtDL$SQ-!+N>O>3c(bzvI1 zFY74qWdn@};Ruy=A4GYHHYn^UFG@3c6aKd)7UD8^(SR zv)a+{bx)5XRWlIRsZtjg`xp8X?=75N)_rDpZb;gmo7deZH;jECRaZaPG21bJa`D{4 zxs>flydrCJDwN~1-SGo={=Im@giV{J=W z+tSvKxc;j;*SvkPX`v}o*BQUCS>HH6ws>>lW~Tn=jCQlJb#dg=k*wX57+bpe>}E2Y z=^sqNuYEApG?cOreH+I$H#$3-wJXDRrT&m@rgEp6)Yi|LXU!R_H)ZuEoXd^L=?&`- z(5g~u^Qv#{`i6CItIqYC6Y~#${q7faUD=MeQyr&fug(n4zrX1@oES`dj?P^CSH0(} zwyvd--;QJ(+ZRW`%Pk96_kc~=#wJkT?w+sQv5kn>5l93gIV+^*jSNlUu9N0SN$tJ zYmT*#Ub;Si@8zNO-m4pp*S72p^VerD|7u`ofFZ7)S6X8CDIYjKlM?>-=X6` h!vrm2TKrneFw8$l)h2QNleBze7-N{`R|I=S_J8i%T2cT2 literal 0 HcmV?d00001 diff --git a/__pycache__/items.cpython-313.pyc b/__pycache__/items.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d61435d6eeb0a4337500f00c7215d36d671cb6a6 GIT binary patch literal 1509 zcmZ8h&u<$=6rNr0FWYhKIF6IL5;93zSWu%12~|cF0y$}9HN;USC6T();Ci>whK)DO zZUp3%3rLlWpod5tInq0S1V;o&tVX?YK)xmNp+|Uc*6T)$MIB|6*E9aC~9Ez`=Bvm|HHIa-}^dCBGJJgsHiU4s|s zLWA()VclLFVN13mly~}Rb16xJEUuAF8$?v`*|D zk2DC~?wi-jV3izv##;avCxIsl(qyq8c(y6D$P1nd(}$j! z?{d6SUPPEdC?U)OM0k>Bxz7`f^{=knJ6D1teTIS+$qBjA~Nm9KrG?UuWFrO?mhK1zj?LV!hMV=%+m@3USUbN|U{>_v>dAiRc~%^_gl67X*1kiI3aR71a& z#-J>;{V;T)qo2ST?IPR(;0pkIcJ6hN$dk$%Daw=y`hHp9T3NdIEOUnt=->`&h%ICw_+ zg4e*fAddmlwL5*K8AHe;_|<2>@m0btP;6D`oxP1usK_09v90h5kP+_Cl+zRz&^0HX@zV)p2!~T!%AH|j5v=uqtZF@#okr&7Q;4qT6Ao5tB?RUHDFm}X( zB0|nk z-va?p5&a5qnNt+yC8@tMOy$-U0l2IaefC+LSaXRrn^+4;Z853VlljG~l36h>t3)q9 z*AlCmSmnf8%+w`N=cIZqwk8+sVb!`3(BT7UuRvD(q<;o SzVcpPJ=e!aaDr8N(*FRX09GIX literal 0 HcmV?d00001 diff --git a/__pycache__/player.cpython-313.pyc b/__pycache__/player.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fbda90656fd7b743cac18b54592f9dc1875499e5 GIT binary patch literal 4944 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 literal 0 HcmV?d00001 diff --git a/__pycache__/tiles.cpython-313.pyc b/__pycache__/tiles.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..84e0e7c477c6083f73f162a4f04099328fd64e3b GIT binary patch literal 2193 zcmZ8hO>7%Q6rT02|JFbGani)5v?io2Zqq8Ijrv1XH*!pfn#8HcNz-V!U^7t?ZnEH{CVE@ zzIikA=FL_D0U3cUk1XiFIT8AUId$7#8&n<&pg&C6X zhH#tW;TgP~_2PC|Z6EHattN9;#+{%Iy#;sd}Q#8IV* zv(21s#;*a}gQA*zlMi1<(xSz=KrBGQ2mMG)yMxGlJkDt8OT4sWsPF@Xw4 zbB&2=w}dJOfL%rkA_7LlfdwK${lo~mEf5Jy#945}TvW>FhPEYwbTsE~NW-!)wlWN` zj3%3no#15DfF7yZWUo+Rn_x^wK4HA@G}T72m)B_1ktL| zbBXiHxTO(GNl#oJR0hmDMZ?VIEoF?Dx3!#|&mW)4>P2O`?kX(3bz=H1^{A>|)NNIz z0al(9b(bB2eKLNqEDM~JjRl(aBl7r=qt5HHiG=%vHu3|BQQOvQekyd4t>yU<-2 zjX8D;Or-&x*qNBP^%;n^k_`I_cG2yZ=Q8g}B^O8>-X<%Bsst&y7QC#L#{!38F52blFF%&bw1 zvj9;+j%wMu!{5dH2{NUbOW$z>76%JXAl~1h~HpM z$c?3_f0Mfr@qi1f%9)|zq?)-lmc&#nXf|ByP&y`%W(Zqs2T|S;)B`b@O_MLhno#Jg03saA!*|uIRSk;a?12do}hSC`-4`q@g zD$b-vlh6d|evIMHxhGvsr3R>%$NA7Toa~=~?RkdMsZlkRPS%3VrJljm@IV8}Tq-A0 zV`IrQr+qw8m7tzPs(-ja&0NAh)YPfl+LX2D1=1#7ow0NZvSo&nm>(WGoK=TP`~$$6 zp|`hIqzl!Dc#*IsX4SJU0B(zdAUs3+UP^8u{2T#nw;`uzzU^Lav2W$jYRBV~>!(ZJ zOJ&J3-+j+soLI@MzO^3OIA4-S%aV8g^rCa|&Z@Nf{(55LcFA|8k(*l2ZOSDd-aGeP zNggRn^8CQ!zLn0^zO`c;;%0ZrpDIiK`TXM9mFsJ5YoBabPp(`q1#dKxA8+8zyQSc@ zvJ_mB7O$`FU$Y-8n~5hA*;4b(M&ay6VpA^#C%4;uLC2iD-Gd_CPs6cNIQC2U*xvzn z$nnCDB0ckgd+u!?a{9|H?e`BZ9b|uzrAWEC?Y?)(``j%BT`!R2bUl|r{DMuJYpS#g U!s&l2A#f}gRPnpe$%NnaKhOFJeEeQZ-%cE8W|^LMbF#7^Rn#10|B`5=4_3*pO@9tb|&m7O8)PWVY>As;1}M z=l7m%JPh++FX6m<@4M&R<9pBf-E+@dFDfzvyCp3FB9_-Pc(7vtXbskbqY6+)v6OP@jk@$Ibi1F!PV58f|h)g38<4`0K zN<@bv1JW3YW*EqNKeX?Yeu9(m8jj($oW@Of9j67#qIh1kZt@Pp zn>i!&SU3|{+TgQ^?728UJov%@GYv3VN??Z;{;!*%_de-U zUIodpGOkZv8UnpInwCD3TnmzbUaqUp-5LH8a+VP^twhk2kmD_c#F!jTc@Gl~#%ekJw z$@b8>&h|6MMfO^BFmXw=N`1#WI*%PcBkC_jqC=MwBFl$kLlIHy?+l2#AwD`NZWxNh zB78Uz30=dL_i03(5E;HGG9dwfvBx9sJ-V+yC`5RnKXCTMvHte>wb*bxJSg;^;^Xf` z1`>mVEtkX5SpSIJ?5C2pj*N=NP$(LUCPE=`1KroHa@z=^g<&8ck`C4q#M!XUYMthPaW;%IUuj9DC#jn=&zhgOo6^?i-?*E`Pv7BE?xrW! z=6`ZFyu_g(!L({7rlKWdWy)Cj)L8ZVH4`an`Td#}-U?+f;onppA-Z2#?EW_MuPO~# zZ)1Vx7#nU1HMYl+u_*=yoAg>UIR4jk^A$7fQ?@sNzc{aeM>3p7_D8Td64Uf$&!rfV z%BY&$aRJ6_dw1+BCL~-=NS{?61zK*9>*_P8WuXyd8i00r2m#LC45wWl^#+^=kdxD* zw#v+)fmGgr|A(My>30}^RdScEUr0}w0e3O4sBQx9EAn1KQ++=~e7f!z|NHgVU%$W| zet~P^E8=mzc<@sq7IDGA_x(pZLMJ=UcAOOT;4p*n%c23F(O7~n!jb5yJg%C^9t-$6 zPSmw`2F{2qcc!PiBg>@{d?X@>MT(2X`H0}t@wi?h^S-!2xXND)4@5#4?_@>=G#w8V z9@mI>;J{MJbrd1gyKoVNzXkFkdG2tHCnm2?Twiq5j`_dRlj^2NXMgddzx>e`N4{jz zEk{ySN5;CJJKd63EuuF)D=AHvY+kANjkiyBPIOL%Cr&0E_j;Z=U1{f*mHMVvy63&A zil(%)X{Ej~_Z>{v(45<~UO`-2URp@;rqwNqD?Tw+EgP*%Mt92Sernvf4w{nz1)`yW z{y}~2`#=HU6+hsZpr(-51r#qsSyEB`tp)s4_N-C_yWa&XS;$`?4f_3=)js(|~XJC1{{{W<(k@1xNDU|>vFBv9klMw_>hNmO=Xbsm(mlEPTS4narFX;&napzhu;_V@rxIQNP^!4 zeWK}1=gE!`*Ll7JUPfaQ0$RDFfsT%DDV*R@YYKXRj8eZ8Qc%bgwFrr5j1nZ`&lTB$ z_;8#T^&_K0;mZ+G7l0T+WWysP!=s{ZkPlxIbv$$d>QWrk3j=_XF;PDNqxrCC5E5ZN z5qe)_Be6kvWZI)rdsI@0YHz?uH547>x8t;Cii;r>Em|a*v>_m3Bcv)t!d5&JM6-mg zsMStr3-tgy9})ny64!Wm$~99pT{TlPUGu0jRk1r={MMLZ#a;qv_x8lwN!MJ}Y}H)N zY|Ur;Q{F?r?o4^Q9-lg$@|;fF17qy-V(04woR~O~teJgaTTp`{Sx%vC#^Zr44^nu2*uTq9T6K46r{-h54eqCKP`X(1tKVr zSpd!RWYSngk(rCiOhCbNc_zRN(wKYYN_( z0q+##Riy^aeKm3`3y(c=pZW}%g2teM(`{AG8bG)YG6?slnOIO$M|^As<1k*}5hQ&q z{}wz%R)`P7{YC@$fM`K+!{^`|@ttlsaxuX-V>cdw67I1)y&`E7@ev8|^cUiZMEtT8 z9I7C<133^aL_L5SfE17GBANmAV8ZZ);fT*dp)I2aqLjezMXr5F+K}u=La~)9cL18A ziQ*Xon1G?Ef>)VnP_*OiJQfM_7e?tNT@YPzXqej{0B(V$P_}F@9%E%ZE1jvBu6P(t zRkSWZJhwAlyek{f=;i8BH09azg*WACOWXI$FjhKKK3)Fs2dRp!3yxI9j&$)(nGbQS zbgpu?@-tJ)yY~x!%6lMfKSl*I_WOBME zPfna%_V`x3zWIiVQ3OlNo)e7G>`N&c-zFOM1f&{k*(8fA?k9x|%;LG=1?J4iDXx)&<*ZG;ip z0A4xrN?|^Zku~<@-aQN9bVg%5GluBDPc+^95oq~LqAhb+C*o2Hg5L)H)Hy{{G#0uL zzZx49q#y$~jz$_7NW@1(V|Z{d6iq}f`^-`>VY(c@Dnvpfae@CHj70(OMPi9KKPnoe z_y7V5sWK|+slP!;M4zKPbwV9=S3Ffz|DyqeXt}^g!taJI495rF6&;f2Wp%v=EW%#^ z`H(DEdY3AywGO z(b>@j?~~G9E6uIrx=GW7X{tVD_a^IqWB08Zh`oB%M68Xk@rM5f<^SJ$1K{+xdxM&G zqqz-;FMGEYj2m9DM;?=eK0SXL9;yNIh@@(K>oZEY9>iX1UuJ3zp~UD&gg=M9y-0q5 z1+D1$cd{Z|$(_;Lqf@Sw&65PO<quDfaEk>>t7=a`0@+Ns-wA2IQ9WPaEMPs&u)5Nf1;IkXA|_(qg6oFVfCeWAJr3W zlG`e<@>A#(@FoDuZL^R2mu$6ZTkVu?#yo9)Vyj)UwI#32`F0{b4N`jEnVp5YP4a`Pv*lBKY@&nv2@5;HfZZ!A_cjr*6&Dkn!LMw5<-50d_g zTaPwBEo)sa-#BBNw#_q7%3H+ps&xZ#H?J8;u}|84EleX9W%o15c)7pe&gZ7o;R1<1 zLrYLeIl*qhn#XjiE5Z5~+&4&b!+r&INF^7<00z@a=xDv0uwNPzW7U~@gOamxx-2UM zT!-*b)rR3%Dx2Z-cmt7l8gz@nNCRgG8Ynu#jUfyc2^zv&`9-l+*ykQC*oX)8F$DE_ zoYs6kDRob+53VJY6Jj6rn_3|x&+-xDK0r6h4L01TT!Ft~6=|3nG|-hB$=L91#fDAY zFA(u~1Ck>^UO;O8YkHC6F_BMEo__~waQISluEJiNRMeg8>1mfvXKL-}L!$m%pP3WvFLaU_~A2{KgmpYB)2E8%3q`xhKfn-4FX`MmGbzNO~Fspi8gWmS_m@7}z3YrgbpS<6b(b~w*W)25_% z+L{cc%eR2Hl{I1#XU}V})(FQ{_5lXrzKVA1V53Z*a-bE)R1S}`0;nl@jBN4n$?IF> z^eMi{>CjgS`$C>nEsQE)_nD7i6p>ZSRj?R*FH0LmFPJn_R4<6>f;x^>K7rvWO3Ul# zrCut}~Dm481p^pF35Il%f zu`|%qE!B?oz_)F94~ZRIDIW7-Jo>fdxC}2KtkNi3iu7usDVE4#O6Kb}etP>Qyr4un z!i10A;AeK%C>h!nNM5O_9J8d zXL%9L=BKuX6-bwM-R-*9GtPjYjtlqptkiGK{W7-fbWN4s>s_he_G*90y>qXOf1WR3 zt&$XPq)zM8N=rx=6W6xe=fKbHF-W4r=XP@_bU8kF71HA&(HaU}xf&kMys?Es7o)t8 z7>>pwv3MxNJAs*gXyh>t=DU#eBY{Jb@PB{=&jTLf4?@EO3W_{kWM>E=G!Y#Lg%b%r zdf{p!B7{Pp5UQfI83+J85=406FpwXUHI13AUaKx+tJc2fX8p|C!5Y@NcAT-Y+g5AI z-UD9|BfDqK&9M8|tU9)4t=Is=i>>UA-_>qlTP5Kr67Fj|;6cv?(Fk8P64BU@z@NZA zdaBUL{|KAtc+v&TxL;bCgZz)74>3)^BV|>~Fw8%bBKTV&uHO>(f9cz`OzRp!O4 MAX_FALL_SPEED: + self.velocity.y = MAX_FALL_SPEED + + # Move horizontally + self.rect.x += self.velocity.x * dt + + # Move vertically + self.rect.y += self.velocity.y * dt + + # ========================================================== + # COLLISIONS + # ========================================================== + def handle_collisions(self, world): + self.on_ground = False + + # Get nearby tiles once + nearby_tiles = world.get_nearby_tiles(self.rect) + + # Horizontal collisions + for tile in nearby_tiles: + if tile["rect"].colliderect(self.rect) and tile["solid"]: + if self.velocity.x > 0: # Moving right + self.rect.right = tile["rect"].left + elif self.velocity.x < 0: # Moving left + self.rect.left = tile["rect"].right + self.velocity.x = 0 + + # Vertical collisions + for tile in nearby_tiles: + if tile["rect"].colliderect(self.rect) and tile["solid"]: + if self.velocity.y > 0: # Falling + self.rect.bottom = tile["rect"].top + self.on_ground = True + elif self.velocity.y < 0: # Jumping up + self.rect.top = tile["rect"].bottom + self.velocity.y = 0 + + # ========================================================== + # DRAW + # ========================================================== + def draw(self, screen, camera): + draw_rect = camera.apply(self.rect) + + pygame.draw.rect(screen, (255, 50, 50), draw_rect) + + if SHOW_COLLIDERS: + pygame.draw.rect(screen, (0, 255, 0), draw_rect, 2) diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..497c4ba --- /dev/null +++ b/settings.py @@ -0,0 +1,173 @@ +# ========================================== +# WINDOW / DISPLAY SETTINGS +# ========================================== + +SCREEN_WIDTH = 1280 +SCREEN_HEIGHT = 720 +FPS = 60 +VSYNC = True + +GAME_TITLE = "Terraria Clone" + +BACKGROUND_COLOR = (135, 206, 235) # Sky blue + + +# ========================================== +# TILE SETTINGS +# ========================================== + +TILE_SIZE = 32 +CHUNK_SIZE = 16 # 16x16 tiles per chunk +RENDER_DISTANCE = 3 # how many chunks visible around player + +# Tile IDs +# Tiles +AIR = 0 +DIRT = 1 +GRASS = 2 +STONE = 3 +WOOD = 4 +LEAVES = 5 +IRON_ORE = 6 +GOLD_ORE = 7 +COPPER_ORE = 8 +COAL_ORE = 9 + +# Items +ITEM_WOOD = 100 +ITEM_STONE = 101 +ITEM_IRON = 102 +ITEM_GOLD = 103 +ITEM_COPPER = 104 +ITEM_COAL = 105 + +# Tile properties +TILE_PROPERTIES = { + AIR: {"solid": False, "color": (0, 0, 0), "drop": None}, + + DIRT: {"solid": True, "color": (139, 69, 19), "drop": ITEM_STONE}, + GRASS: {"solid": True, "color": (34, 177, 76), "drop": ITEM_STONE}, + STONE: {"solid": True, "color": (100, 100, 100), "drop": ITEM_STONE}, + + WOOD: {"solid": True, "color": (160, 82, 45), "drop": ITEM_WOOD}, + LEAVES: {"solid": False, "color": (34, 139, 34), "drop": None}, + + # ORES + IRON_ORE: {"solid": True, "color": (180, 180, 180), "drop": ITEM_IRON}, + GOLD_ORE: {"solid": True, "color": (255, 215, 0), "drop": ITEM_GOLD}, + COPPER_ORE: {"solid": True, "color": (210, 120, 60), "drop": ITEM_COPPER}, + COAL_ORE: {"solid": True, "color": (40, 40, 40), "drop": ITEM_COAL}, +} + + +# ========================================== +# WORLD GENERATION +# ========================================== + +WORLD_WIDTH = 200 # in tiles +WORLD_HEIGHT = 100 # in tiles + +SEED = 42 + +SURFACE_LEVEL = 40 +CAVE_THRESHOLD = 0.4 +ORE_THRESHOLD = 0.75 + +NOISE_SCALE = 0.05 +OCTAVES = 4 + + +# ========================================== +# PLAYER SETTINGS +# ========================================== + +PLAYER_WIDTH = 28 +PLAYER_HEIGHT = 48 + +PLAYER_SPEED = 250 +PLAYER_ACCELERATION = 2000 +PLAYER_FRICTION = -0.15 + +GRAVITY = 1500 +MAX_FALL_SPEED = 1000 + +JUMP_FORCE = -500 +DOUBLE_JUMP = False + +MAX_HEALTH = 100 + + +# ========================================== +# CAMERA SETTINGS +# ========================================== + +CAMERA_SMOOTHING = 0.1 +CAMERA_OFFSET_Y = -100 + + +# ========================================== +# INVENTORY SETTINGS +# ========================================== + +INVENTORY_SIZE = 40 +HOTBAR_SIZE = 10 +STACK_LIMIT = 999 + +INVENTORY_SLOT_SIZE = 40 +INVENTORY_PADDING = 4 + + +# ========================================== +# BLOCK BREAKING / PLACING +# ========================================== + +BREAK_RANGE = 5 # tiles +BREAK_TIME = { + DIRT: 0.3, + GRASS: 0.3, + STONE: 0.8, + WOOD: 0.5, + IRON_ORE: 1.2, + GOLD_ORE: 1.5, + COPPER_ORE: 1.0, + COAL_ORE: 0.9, +} + +PLACE_RANGE = 5 + + +# ========================================== +# ENEMY SETTINGS +# ========================================== + +MAX_ENEMIES = 10 +ENEMY_SPAWN_RATE = 5 # seconds +ENEMY_SPEED = 100 +ENEMY_DAMAGE = 10 +ENEMY_HEALTH = 50 + + +# ========================================== +# PHYSICS SETTINGS +# ========================================== + +TERMINAL_VELOCITY = 1200 +COLLISION_STEPS = 4 + + +# ========================================== +# LIGHTING (for future use) +# ========================================== + +ENABLE_LIGHTING = False +LIGHT_RADIUS = 5 + + +# ========================================== +# DEBUG SETTINGS +# ========================================== + +DEBUG_MODE = True +SHOW_FPS = True +SHOW_COLLIDERS = False +SHOW_CHUNK_BORDERS = False diff --git a/tiles.py b/tiles.py new file mode 100644 index 0000000..2e3a145 --- /dev/null +++ b/tiles.py @@ -0,0 +1,38 @@ +from settings import * + +class Tile: + def __init__(self, tile_id, name, collidable, color, hardness=1.0, drop=None): + self.id = tile_id + self.name = name + self.collidable = collidable + self.color = color + self.hardness = hardness + self.drop = drop + +TILE_TYPES = { + AIR: Tile(AIR, "Air", False, (0,0,0), hardness=0, drop=None), + DIRT: Tile(DIRT, "Dirt", True, (139,69,19), hardness=0.4, drop=ITEM_STONE), + GRASS: Tile(GRASS, "Grass", True, (34,177,76), hardness=0.4, drop=ITEM_STONE), + STONE: Tile(STONE, "Stone", True, (100,100,100), hardness=0.8, drop=ITEM_STONE), + WOOD: Tile(WOOD, "Wood", True, (160,82,45), hardness=0.5, drop=ITEM_WOOD), + LEAVES: Tile(LEAVES, "Leaves", False, (34,139,34), hardness=0.2, drop=None), + IRON_ORE: Tile(IRON_ORE, "Iron Ore", True, (180,180,180), hardness=1.2, drop=ITEM_IRON), + GOLD_ORE: Tile(GOLD_ORE, "Gold Ore", True, (255,215,0), hardness=1.5, drop=ITEM_GOLD), + COPPER_ORE: Tile(COPPER_ORE, "Copper Ore", True, (210,120,60), hardness=1.0, drop=ITEM_COPPER), + COAL_ORE: Tile(COAL_ORE, "Coal Ore", True, (40,40,40), hardness=0.9, drop=ITEM_COAL), +} + +# ---------------------------- +# Compatibility for world.py +# ---------------------------- +TILE_PROPERTIES = { + tile_id: { + "solid": tile.collidable, + "color": tile.color, + "drop": tile.drop + } + for tile_id, tile in TILE_TYPES.items() +} + +def get_tile(tile_id): + return TILE_TYPES.get(tile_id, TILE_TYPES[AIR]) diff --git a/world.py b/world.py new file mode 100644 index 0000000..3b25162 --- /dev/null +++ b/world.py @@ -0,0 +1,203 @@ +import pygame +import random +from settings import * +from tiles import get_tile + + +class World: + def __init__(self): + self.width = WORLD_WIDTH + self.height = WORLD_HEIGHT + + # 2D grid: world[y][x] + self.grid = [ + [AIR for _ in range(self.width)] + for _ in range(self.height) + ] + + self.generate_world() + + # ========================================================== + # WORLD GENERATION + # ========================================================== + def generate_world(self): + # First generate terrain + for x in range(self.width): + + # Simple terrain height variation + surface_height = SURFACE_LEVEL + random.randint(-3, 3) + + for y in range(self.height): + + if y < surface_height: + self.grid[y][x] = AIR + + elif y == surface_height: + self.grid[y][x] = GRASS + + elif y < surface_height + 5: + self.grid[y][x] = DIRT + + else: + self.grid[y][x] = STONE + + # Then add trees + ores + self.generate_trees() + self.generate_ores() + + # ========================================================== + # DRAW + # ========================================================== + def draw(self, screen, camera): + + # Determine visible tile range + start_x = max(0, camera.offset.x // TILE_SIZE) + end_x = min(self.width, (camera.offset.x + SCREEN_WIDTH) // TILE_SIZE + 2) + + start_y = max(0, camera.offset.y // TILE_SIZE) + end_y = min(self.height, (camera.offset.y + SCREEN_HEIGHT) // TILE_SIZE + 2) + + for y in range(int(start_y), int(end_y)): + for x in range(int(start_x), int(end_x)): + + tile_id = self.grid[y][x] + + if tile_id != AIR: + color = get_tile(tile_id).color # FIXED: no more KeyError + + world_rect = pygame.Rect( + x * TILE_SIZE, + y * TILE_SIZE, + TILE_SIZE, + TILE_SIZE + ) + + screen_rect = camera.apply(world_rect) + + pygame.draw.rect(screen, color, screen_rect) + + # ========================================================== + # COLLISION SUPPORT + # ========================================================== + def get_nearby_tiles(self, rect): + tiles = [] + + # Determine tile range around player + start_x = max(0, rect.left // TILE_SIZE - 1) + end_x = min(self.width, rect.right // TILE_SIZE + 2) + + start_y = max(0, rect.top // TILE_SIZE - 1) + end_y = min(self.height, rect.bottom // TILE_SIZE + 2) + + for y in range(start_y, end_y): + for x in range(start_x, end_x): + + tile_id = self.grid[y][x] + + if tile_id != AIR: + tile_rect = pygame.Rect( + x * TILE_SIZE, + y * TILE_SIZE, + TILE_SIZE, + TILE_SIZE + ) + + tiles.append({ + "rect": tile_rect, + "solid": get_tile(tile_id).collidable, + "id": tile_id, + "x": x, + "y": y + }) + + return tiles + + # ========================================================== + # BLOCK BREAKING + # ========================================================== + def break_block(self, mouse_pos, camera, inventory): + world_x, world_y = camera.screen_to_world(mouse_pos) + tile_x = int(world_x // TILE_SIZE) + tile_y = int(world_y // TILE_SIZE) + + if self.in_bounds(tile_x, tile_y): + tile_id = self.grid[tile_y][tile_x] + tile = get_tile(tile_id) + + if tile_id != AIR: + if tile.drop: + inventory.add_item(tile.drop, 1) + self.grid[tile_y][tile_x] = AIR + + # ========================================================== + # BLOCK PLACING + # ========================================================== + def place_block(self, mouse_pos, camera, block_type=DIRT): + world_x, world_y = camera.screen_to_world(mouse_pos) + + tile_x = int(world_x // TILE_SIZE) + tile_y = int(world_y // TILE_SIZE) + + if self.in_bounds(tile_x, tile_y): + if self.grid[tile_y][tile_x] == AIR: + self.grid[tile_y][tile_x] = block_type + + # ========================================================== + # UTIL + # ========================================================== + def in_bounds(self, x, y): + return 0 <= x < self.width and 0 <= y < self.height + + # ========================================================== + # NEW: Get surface height for player spawn + # ========================================================== + def get_surface_height(self, x): + for y in range(self.height): + if self.grid[y][x] != AIR: + return y + return self.height - 1 + + # ========================================================== + # TREE GENERATION + # ========================================================== + def generate_trees(self): + for x in range(0, self.width, 6): + if random.random() < 0.25: # 25% chance + # find surface + for y in range(self.height): + if self.grid[y][x] == GRASS: + self.spawn_tree(x, y) + break + + def spawn_tree(self, x, surface_y): + # trunk height + height = random.randint(4, 7) + for i in range(height): + self.grid[surface_y - 1 - i][x] = WOOD + + # leaves + leaf_start = surface_y - height - 1 + for y in range(leaf_start, leaf_start - 4, -1): + for lx in range(x - 2, x + 3): + if 0 <= lx < self.width and 0 <= y < self.height: + if random.random() > 0.25: + self.grid[y][lx] = LEAVES + + # ========================================================== + # ORE GENERATION + # ========================================================== + def generate_ores(self): + for _ in range(250): + x = random.randint(0, self.width - 1) + y = random.randint(SURFACE_LEVEL + 5, self.height - 1) + + if self.grid[y][x] == STONE: + r = random.random() + if r < 0.5: + self.grid[y][x] = COAL_ORE + elif r < 0.75: + self.grid[y][x] = COPPER_ORE + elif r < 0.9: + self.grid[y][x] = IRON_ORE + else: + self.grid[y][x] = GOLD_ORE