From 56c9ccaf44e7e0f404652f6eb20b7b6acb9591e6 Mon Sep 17 00:00:00 2001 From: Zicklag Date: Fri, 2 Dec 2022 11:10:41 -0600 Subject: [PATCH 1/2] Rename "Grenades" Builtin to "Grenade" --- assets/map/elements/item/grenade/grenade.element.yaml | 2 +- src/assets.rs | 2 +- src/map/elements/grenade.rs | 6 +++--- src/metadata/map.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/map/elements/item/grenade/grenade.element.yaml b/assets/map/elements/item/grenade/grenade.element.yaml index fc36419068..16ce2e70b5 100644 --- a/assets/map/elements/item/grenade/grenade.element.yaml +++ b/assets/map/elements/item/grenade/grenade.element.yaml @@ -1,6 +1,6 @@ name: Grenades category: Weapons -builtin: !Grenades +builtin: !Grenade atlas: ./grenade.atlas.yaml explosion_atlas: ./explosion.atlas.yaml fuse_time: 4.0 diff --git a/src/assets.rs b/src/assets.rs index 3fb511d934..a31ba3a478 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -416,7 +416,7 @@ impl AssetLoader for MapElementMetaLoader { dependencies.push(path); *sound_handle = handle.typed(); } - BuiltinElementKind::Grenades { + BuiltinElementKind::Grenade { atlas, atlas_handle, explosion_atlas, diff --git a/src/map/elements/grenade.rs b/src/map/elements/grenade.rs index 4ec646f53a..aab9828d0b 100644 --- a/src/map/elements/grenade.rs +++ b/src/map/elements/grenade.rs @@ -65,7 +65,7 @@ fn pre_update_in_game( elements.sort_by_key(|x| x.1); for (entity, _sort, map_element_handle, transform) in elements { let map_element = element_assets.get(map_element_handle).unwrap(); - if let BuiltinElementKind::Grenades { + if let BuiltinElementKind::Grenade { body_size, body_offset, atlas_handle, @@ -148,7 +148,7 @@ fn update_idle_grenades( ) in items { let meta = element_assets.get(meta_handle).unwrap(); - let BuiltinElementKind::Grenades { + let BuiltinElementKind::Grenade { grab_offset, atlas_handle, throw_velocity, @@ -265,7 +265,7 @@ fn update_lit_grenades( items.sort_by_key(|x| x.0.id()); for (_, item_ent, mut grenade, transform, meta_handle) in items { let meta = element_assets.get(meta_handle).unwrap(); - let BuiltinElementKind::Grenades { + let BuiltinElementKind::Grenade { fuse_time, damage_region_size, damage_region_lifetime, diff --git a/src/metadata/map.rs b/src/metadata/map.rs index 7b0aaf7a16..d78e5beec6 100644 --- a/src/metadata/map.rs +++ b/src/metadata/map.rs @@ -208,7 +208,7 @@ pub enum BuiltinElementKind { /// Player spawner PlayerSpawner, /// Grenades item - Grenades { + Grenade { body_size: Vec2, body_offset: Vec2, grab_offset: Vec2, From 5ef739b06cd404b1c64c194022478891231f71aa Mon Sep 17 00:00:00 2001 From: Zicklag Date: Fri, 2 Dec 2022 12:26:59 -0600 Subject: [PATCH 2/2] Implement Box Item --- .../map/elements/item/crate/crate.atlas.yaml | 4 + .../elements/item/crate/crate.element.yaml | 16 + assets/map/elements/item/crate/crate.png | Bin 0 -> 497 bytes .../item/crate/crate_breaking.atlas.yaml | 4 + .../elements/item/crate/crate_breaking.png | Bin 0 -> 13320 bytes assets/map/elements/item/crate/fuse.ogg | Bin 0 -> 9182 bytes .../item/grenade/grenade.element.yaml | 9 +- assets/map/levels/level1.map.yaml | 4 + src/assets.rs | 24 ++ src/map.rs | 1 + src/map/elements.rs | 2 + src/map/elements/crate_item.rs | 336 ++++++++++++++++++ src/metadata/map.rs | 24 ++ src/physics/collisions.rs | 4 + 14 files changed, 426 insertions(+), 2 deletions(-) create mode 100644 assets/map/elements/item/crate/crate.atlas.yaml create mode 100644 assets/map/elements/item/crate/crate.png create mode 100644 assets/map/elements/item/crate/crate_breaking.atlas.yaml create mode 100644 assets/map/elements/item/crate/crate_breaking.png create mode 100644 assets/map/elements/item/crate/fuse.ogg create mode 100644 src/map/elements/crate_item.rs diff --git a/assets/map/elements/item/crate/crate.atlas.yaml b/assets/map/elements/item/crate/crate.atlas.yaml new file mode 100644 index 0000000000..fbb7e6cf45 --- /dev/null +++ b/assets/map/elements/item/crate/crate.atlas.yaml @@ -0,0 +1,4 @@ +image: ./crate.png +tile_size: [36, 31] +rows: 1 +columns: 1 diff --git a/assets/map/elements/item/crate/crate.element.yaml b/assets/map/elements/item/crate/crate.element.yaml index 1f875fb200..ad1891a365 100644 --- a/assets/map/elements/item/crate/crate.element.yaml +++ b/assets/map/elements/item/crate/crate.element.yaml @@ -1,2 +1,18 @@ name: Crate category: Weapons +builtin: !Crate + throw_velocity: [7, 5] + + atlas: ./crate.atlas.yaml + + breaking_atlas: ./crate_breaking.atlas.yaml + breaking_anim_length: 25 + breaking_anim_fps: 30 + + # TODO: Better break sound + break_sound: ./fuse.ogg + + body_size: [36, 30] + body_offset: [0, 0] + grab_offset: [14, -2] + break_timeout: 4 diff --git a/assets/map/elements/item/crate/crate.png b/assets/map/elements/item/crate/crate.png new file mode 100644 index 0000000000000000000000000000000000000000..00304e03e7f3ad02d4f7bfabbceacf3d85698e49 GIT binary patch literal 497 zcmVPx$t4TybR9J=0m@#U@FcgNLLdaMs6iU~Eq1awby>#dex+Y7P-o)f4UAn{v$>QK8 z76lz3NT06Zlxzc2%RH+vxvrr0D`=|moXSoKP& z003Zt1>0Q{6hvj~@pi(L*Qqz@UlXQmi@%E;-EXyy0lt*3!(12xM#W-EDR|-{KDX-_O8KNMl z_m5$jiqloA9gYkj+e#P;Vvg6cDaBgu)Rb)Z6Rbv_M@@sJc>+<&k$T zyn)tnWXv@}>6t>OxKM*aT}tJA(DpjvbQUDkRJQ5!feHqM+j}2W$e{N+gqq4JHW0ux zP+mY(HYnFYjH5w7w#_qyyn$Rnr(jtg{U1TD3PfewJOM5n=+~$Jao7o@pXOXpU77L; zIkvN)KR9whjtO+@fHrnz0Qe2cCF2OI!*#)O%XCq9ZB7e%jShMJ+3uRwC83nc>Z0?a n7+StpX)%`%4JZLHudg3}fTGuNIxWOD00000NkvXXu0mjf^aRuw literal 0 HcmV?d00001 diff --git a/assets/map/elements/item/crate/crate_breaking.atlas.yaml b/assets/map/elements/item/crate/crate_breaking.atlas.yaml new file mode 100644 index 0000000000..f2228373c0 --- /dev/null +++ b/assets/map/elements/item/crate/crate_breaking.atlas.yaml @@ -0,0 +1,4 @@ +image: ./crate_breaking.png +tile_size: [128, 128] +rows: 1 +columns: 25 diff --git a/assets/map/elements/item/crate/crate_breaking.png b/assets/map/elements/item/crate/crate_breaking.png new file mode 100644 index 0000000000000000000000000000000000000000..7e2b43f768c023c96c836b51896b2ab2a4c2ef12 GIT binary patch literal 13320 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 P53{4avt5xb^UVJO)kfS1 literal 0 HcmV?d00001 diff --git a/assets/map/elements/item/crate/fuse.ogg b/assets/map/elements/item/crate/fuse.ogg new file mode 100644 index 0000000000000000000000000000000000000000..1d3007e825fad40da8f3a5824c1b1f7865db0854 GIT binary patch literal 9182 zcmc(EcUV)&*YKqW1PoQc1Ow6%0qIg*2rUFc2}P;`p*N{2vbsnwkrE(;Dm9SMtCU4? zrS~Q%O)PZ9j;sFM{ch0R_xs*IzUTe#oo6!l%$zcF%AA>-Yvt=}3orvei-Nv3Hv|0J zqqvYsoaxHta8LgT#sG7_8{+{0{TG;yo-`&a2Is$p!O6fJHhf;?)&2YbDith0#K?nK zNB>YiMXPXMM3BFy!;x@=8A3%#SzSq4NgW~gyeQ${ie!jpV*9}>g*J3x0`vhu1Pw*8 zH4rkiM}#DZq7`N6G2}2DJtI*mWtgHpq&_S{FzxnALyz8?$+8C+3{|iT*5PIf+r<%; zA{QpYb`T)6iSV5WAxja6}_R3fP7H7(=Le z2?1FEP*yIN_FRq^D%Q#ci~s;sKTOaxThP1(Ha;uG1L1LE1C0SdKsroP6(+6PF~i5d zROWhxTww*OuGN36Rerp6zen+i9RmQ?HiCnpf*ePDUH|}bmrJYE%y{WYs&*>nNJy$@ z24Vq#6{O=fZ4t!1lbU|7JN@Cm*B{d&004-S^AchC#|01xF;T-3JijZKFdD{Z2uDvD z(yHS(kcrZSIoo;zh{UgN^ggf0y!qh*C;)f}YNp&8mV!tmcnZoIg;5b8v?=8{7QG_{ zS&N6FyNYARyFrd4Q-C`5BNCu!kOpj!Rk?T!l1lMRLk`0tCX|N}@!TpqWjU7Gqg5nJ z?J=p!416H6L(tZuWmA~Y(6Lw?(PV%m5S52S{;N5CWCl=SD7&av7P!C!!6TvJpghLiUgBgO?72FC7LyedPW0(0k>Pw;S1e zts~fN(amis>i?}rs>iYdu^BHxtAiDjQH9|;a!wW#00m+zHPc>TK!0RNJCS7k$-dF# zn&`sXRPiGc&@rIZz!F<=5oLQJ_#Y>LB#bf~p$pbg&=eq$ zqiZ&=hWj5@hGCfjF!(6#3!VqY`BPeQ3XAS4LzEnH`ltwc%xps1c0`0tp@t}7$X)pu zJ)EVC@x&s%AQFBpgAUrmh(+f>3tFxS=sZ5ahzSr8WDAwz1nZB5F`}1M6Z|IvKlpwD5L)yN5TY&Hc~5sLF{;z_ei5`S|^Va zgZNnw7NGH$CgkFW7NK`K{(rgAEO9RJzkw@LO zT>Kcw1_lr+6wHauHeL~N*=0vzN&{Um+#+vvq_!DJ4JT-9WAC;^lEMiX+kz@r`!j4$ zqc@s^)?--9EuVh9)Sph@A5&Qp`tMfJ2Y_pFEP(Ybp1RY;g16a(#aj6pNgV+(z#tQ| z@LUqrf@mxf^cBF(2w{pf`;qgiz(^QU+A5CV$EHF(xzR{kyPd>FTBv^w@1TVT) zkU#|O>BTt3;-TR7jhHfkh!Q5Wwh;*+9VG&a9x4XAU{;+{-hzQidz--qkpS6b0igCM zK25SGIZfhz1Sq&$hU5lC2vY`f)%YiS69v9KNN$a*m z10}#UV;cq+y|~MBK@x?btzw7-L)#YhZ6ioYF4;M~MN5`|M0N-hh?82P=4RE^B7wnE zS~Yl(%`p%R0egHJa>%(T2egVaQF&3UIv2l9_N-H0Vg@Y4VL%ywP_c*fZhfd)*O47U ze%RsXQC%Ey6kH6g7>AN{9+44nKh7vTw&Y0Nqi$8M_76mP0v>CIj5^}51}zURcJGJ- zoEHoFN01@v2mxw_tOiAanlb3W_Fn`N?Ej0vgXp}U9LQ?$Sj5tPa4;qUr80g$CIZ_d z2yCPogFg{R6XoJjKf6cNb_@=X?x_7o-Vf%V@)#3=;7>XL*g5iN3iepbJn>9sKVKrZ zfOWaV8Kzk93eykW{Yj2LTG9~{iu2$3e~SDe@y9Gb<^C^{0Pc}VEbbo@_##O*-h3K` zq0g9pBpqS!viT6aMQF)NV|&AS8v#1q96ZVhmm4CWSTk^s22O(81iaLAfZMZgQ7i!pkF*0rcdYvxa+fnU;b3?~=X$&Kl?EIvut}(~&fqM_Ceb*uI zK<~3SBk){YXvn)85y#In0#s$uIR<(Jp&_E4<1L?mUZ;3Fx7Oo-kf0$aBi=mVkl+SJ zZifQ;u1cgV?eXThl#^Ed&|89xD`*OM;4;S|ph^Is^nh{1a9!~xI|K`!iz1!40sS)} z<1vxDJ2$PM7;?QT$+@0Yr#Wl#EYAspeIay;vYdi1muM9% z_px#k)YIB|h=Rr_`ueG#@PXkXAC@TPgZ~{vD)=De@ zH!swLmsOh?0$T|AKIR5Rv+hccJim2JHbghH3@r3zg-$6ima7M+n|zs(U|}&tp(?aRv|oATqs~u@ zTj9n(lGw(xs={ z#rehC&u9?`-|pQt-d5v3cFsS)Aor()K4%nmocYv)oE%6SAsvv3xx9 zYn(Kweu2)3;lwzSlyl-hv_-muSX-4}l$W86g_;?U-|$%R3qM7T*Tr>hOCqDnZKs)Z z^=>qt-lMf;9*zp~8L$>``RP%ZBr2fnakzPVbA3O9=oCRmE@nQmkc~I0$ROXh?Ma3F zRLX=BB=iphDD}CLXf=Piq%qr9W!pHY30lI>b0}>#wH0BlUasAe`5Ju*;gW%^__o)Y z|0v6+{CJB<1R9@}x!yVYwHu}`<|vHEkg75VE0W(WP^b^wRfqW0w6GPPp-NgbLCl9a zd~z#V)ufFZFzQS7J7t?$*H=_QUXzlP^qZgu-?uq^ik^AB_7T0P$*_bPjbAIB!GrJ6Re3Fb zPMlh>BXyQVct3u8Tyo|*1z`TONfpp;#qe%(Ug^`y&WLRc1GlFVe}k}1+c%!yeA z^^Ou}6_8oIQlq)~Bw19h3>~8ghO+$q&KbD+eY2g6g9fI!0Vb$~_LPBUk}zxj>jjZ9+U88&@r*z=wx88Kg->6&k@t<%}?K5Mv#C z(&HDiejjFEJj}W?Tr}<>Y~N-`Pm(vQx5lR!I2{V;jMKljWJy`(zLZurKW39g%sH@p zwmN;LO#$*vNvCt|Z0&ErrXqPux~mqQ>Uj~xc0rz;KL&0`wO^#a1;vfMs_c_6ykjD$ zY$hS;>g!(iuF|OK>zhrhQ`HY6RWMii+iSFw4U9}jQZull@J*;t344yrh~gh|nnQjT zo@1u?;a=_GmccHRncgj915-Q=m3*E$_T1`MraM?s5}A&|srWpTRuRF1{fvlY^i z`FL**wSdGYPp)}|O<0#!Ju^0y32d5$`IGAt^m7GeTfNj!=^n+=%YO7X;89}q(5!+( zqtwIYkF`yN4!0mP^8?7eRi|j(oz}y@nolb#P{&9qkZh=sb&AtgnJ0fmUff}{-Zk33 z*t_#rcjvE=xn$C=>W#kqbKIN%i7qP}lQ_}+xe0%A{vrmQdEG+s*f6mhov)X946yj6 zWl8k=z`^`~GNF9g1T#L#;!uOo^Y?p*6B56erJk-ECZ8ry#*I(N&b#DsMr~KMhR`yK z7R#RuaCy$7UhL^=(9SIu1>vh(H|d7xB5Sv3)n3}8s}0c&rDIsQ)ysBsG(pd)#4SUJ zc<4Gc=;^VE6Rs1LGDs@(Ff)Ac>GiSFeU^^WWiy|ZJdNqFYvP@~o6(f*=v*^%gqf!U z=0lqFCN%+(SgTZ+mlw$n7~SV}Yua9t>6s!g$y5>rRZwy1?gz03U5;_nJh6aNa`Qhxzxwn z(Jt!Cipv2S_k&5>1h>mech*++ur-|yy(`)ad;O6-y1%qsbr8(`Yzec}XJ>L`6)?~| znROWDDIjlo$0k$F`^Kw{L$g)h*bn<>A1U9PLwetqKc7Syih`6Cc11T1J`8&9Vh#UrB>sIT7+k<+1<(d=>RN)1P?JAi2gCL+wvo zH%L}JpM_s8>ynd&R3!fcxCypJ6}!)Qe)hhi-=_ z%8S|xX0*QFtQa@Wsg~bbP$IWr73K?j{N`pYczjLP1WMM^d%lDO8M}(wHppAyAthUL zcjM&_GG5#W{V<)3+LX2qwc6BhbLY+~Pt2)qDQpd=$-k`o;{VR={i^}y?Wn}yjN=Z3 z2Er_>)`veU$ne|lTHGiZeF*K`i(mVU^X%f!066@L{wkkkH|E?I`e#<$ z19%~aTI?SUYH}lHx*=Gui0tNqB!ST6TnFdZ26jB^q#37iaw2yDQQ2wVk#$`(R{&8K zH#J>7V+nxI&wqUhWNNkWm&EWlx}qASluHO37uWxS(+*0s{dP`|wT7wLpRYAOTVO#~ zGm5%v7cU^nEz4A3**<3;aBx`)cfz(zq-yhbMcEb`E#Io!9@KO-;aRUhGsL90lgb+% z<;u8Y7evbYZz&d&zZxDJ=aLGMw|lGkLFqXici#^pyV%<{FSKN~U6rw4`#Csz_mlcu zR1v?MuYN!>BAkPZ8qk5umr^zJ0EAsp^%N*moDCe51rqYjOXNcK2_;luu8eozZ*$#4s11(rE8#l zuG}2o>HA&t>f!IbPR1K$LbJAU5xzB-?c6X2Z=?gyU%zD7HhcMln=Hlyr{%8fEv?i0 zs@g*quAf`Wz(_@6TjD6*C%9e1Sb#EcUqotGo7NNQGB^4jk-b5B;=W|LX+>(f8GL|i;?Xa`FhxNG{( zxn%TyH0F^?%1Z}vo?b$g_kFL5#)jhgzZWwyUdI6|2_tXr9V#YS*n9rWMY=w*>L^)9j{bBs!k-wLWPRv zW+ge;yghybCc*Q}M16NdfAUNf)8#ujOKC#0X8!o5k;)>boo;wuM9jCtGSDbrn~Uf{ z*ty<5d+}WHs~ZV#NA#P_Z@TOwBb;*=y^^4x*I%V-cbWXEXw`OnJ?^uAPMZnW<`oyc z2VNhcPlfMr2n?=KPM(BsZnSI2xip#jOnS=?Ii8C*#v@dr%%zB}D!w!>;GXD&5EN>_ zah0lcqF*0?7vI=wVrRMqchAG8<&KItW!{R$B{AhY20Rx#ZhvvHlw!wfYfzzAa4{sp zL0E_&;;TVtH_%8-N8RhAbk@E5eZizjnq4pKrK&M$#qi;kF55&TDvRxf`zjK*I6W0K zrF8Hz<(-Fn^d47Kq>pKs&u@W~K|}~$a3Wxx|1z?!0#T5z{&#scOuDe4pEtZwLt(%2 zHH9$ilR+bS(9&47F7RbZoktS&5QsS5PP|`CKQ40S!RkH}L{h>c%eqn8mdK}ku77J4 zd_!C$%Agf$-B3uboF_^w-kfX#IB|>|kJ@Z|5rQ7x23iXbfed(-E$(ygz6Jhe_6?w4Y{2d8fv7Uj9D?Rrb*gIk8{1a71KK#i9Xk}hvPcBVBIIPN&5TLNV zkTtitLo^1&{fT<+`td$8x9@Rq`^|j(x*`j>=Xs`Odl`+fb-fx>j4M>Zos%vJG2eSw zGk4&*>wZ;Bx#@*alf{{36CcB%Rm0=JXpV@oc<+r)*%#p`)x(^^os50IH??N(_Wk7MTQ#LXGvCR!zZ~Q4{>jEKU)fN(^zx7UG5Vxy zU5D`_UvGVLQc2_Pp_o;v5HM}$xuYakH;(>zU>#TauRk6bA0K4dR7N@Lo2hLy+TAL; zxTt(UmXD<0RAuv6|0idIf1T(_c?pDs0`4v(&RU-2b>&6#=L__3J2Y&>;6begB6NAl*4 z*F9Y8)C06RN3EOKzRuFQOV_&LpIi*q&S!n4*l8U$$vv0pbqy8N>Tv41lD|))C%qRf zymeRais_$@f0uwf7ZTr`^yQiAGp#5fU!pk)T85vNW7NG~rFk~Ow)pelEuoz;If z`}b=tyPIlNoz^=freCOM{qEQ*SH)`x5Wdaq;X<2R#HOehx%^5~uiZ)SbhGmQ+;K;A zHD;=>ZF=y+n2%z^0=5>X2Ti%Y06_m1>1Y~KIR(r>Dh1vC2=tTo+!wlZ+mrc)DUQQ&%sY+z((pH007!~$Yx zFB4c=va&g8LfKwKjFZ5!3!LN3FOppe;0LoguAOXX}!SUwe<=j|%bGQaKlu&x_@a^5685R9to}Y|pE@(|Hr2sL-t8-3i=uT3}f7 z#aKmCEEAj9(>t6NDH971Fmm42k|dIoLiuKY)l|athi4u`?zAcAk&!u*mVW z+}brO3d>65&d>{aL!0y4Z0(Ny^Vu^=b`AU0-8L;Nx4!UQ*A%0}?E0eLl?t5MTGy<8 z+wO{4GhqG9ajiRH58Fy!`x0&N=4!Y{&$aduu1s`J^7RO~B1?%zTCC-=6!|ut{-2>?8Wig$iH zTcT*h?$P03gf*9J;K`cW5AVvm?b=qVsvz#2w|UMX(+gg1J5!sOQ9PA#-Q#L0t5)SI z-dEO3nG>?{UaIO?k&yo*YB4oqt-8m}w#V21k{H3W&*#;lL!d0$v`A&c_n971qdE`Z z&6NAAIVC2D8lrd|H+JcvZU~RlxfwSFj+b(9RO%J4DU9B2r;dtQs$;EFK{op#N3H<) z;s0i$zAh6pGv(9jGBBu;A3*abk{S=TLq@u7>Q|#ds6zlW5-Fp7SECR`HcXV znz=Y8zIp%TKK!HGFj$gXM5mj4)8PkleaMr=;~$I1SyY{hVnR5M-|h4jZ+PwR(|ALt zYjMgjxJ|s`g`AJAsZX(oJe2GomFA|#H#g=$m}ubwR;kaY_)zEUJuKQX^fcTvwCtb* zdu*?1?>i)D&mxw$fBEfdz1p~d&cO2WFl;|P{>< literal 0 HcmV?d00001 diff --git a/assets/map/elements/item/grenade/grenade.element.yaml b/assets/map/elements/item/grenade/grenade.element.yaml index 16ce2e70b5..88e2db68e2 100644 --- a/assets/map/elements/item/grenade/grenade.element.yaml +++ b/assets/map/elements/item/grenade/grenade.element.yaml @@ -1,18 +1,23 @@ name: Grenades category: Weapons builtin: !Grenade - atlas: ./grenade.atlas.yaml - explosion_atlas: ./explosion.atlas.yaml fuse_time: 4.0 throw_velocity: [7, 5] damage_region_size: [60, 60] damage_region_lifetime: 0.6 + + atlas: ./grenade.atlas.yaml + + explosion_atlas: ./explosion.atlas.yaml explosion_lifetime: 1.0 explosion_frames: 12 explosion_fps: 8 explosion_sound: ./explosion.ogg + fuse_sound: ./fuse.ogg + body_size: [18, 18] grab_offset: [-7, -6] body_offset: [0, 2] + # TODO: Enable and fix rotation problems can_rotate: false diff --git a/assets/map/levels/level1.map.yaml b/assets/map/levels/level1.map.yaml index 069173fb71..1ce89af3d1 100644 --- a/assets/map/levels/level1.map.yaml +++ b/assets/map/levels/level1.map.yaml @@ -1222,6 +1222,10 @@ layers: - 536.0 - 309.5 element: ../elements/item/grenade/grenade.element.yaml + - pos: + - 719.0 + - 400.5 + element: ../elements/item/crate/crate.element.yaml - pos: - 292.2272 - 609.5 diff --git a/src/assets.rs b/src/assets.rs index a31ba3a478..e4690f53da 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -445,6 +445,30 @@ impl AssetLoader for MapElementMetaLoader { *handle = sound_handle.typed(); } } + BuiltinElementKind::Crate { + atlas, + atlas_handle, + breaking_atlas, + breaking_atlas_handle, + break_sound, + break_sound_handle, + .. + } => { + for (atlas, atlas_handle) in [ + (atlas, atlas_handle), + (breaking_atlas, breaking_atlas_handle), + ] { + let (path, handle) = get_relative_asset(load_context, self_path, atlas); + *atlas_handle = AssetHandle::new(path.clone(), handle.typed()); + dependencies.push(path); + } + + let (sound, handle) = (break_sound, break_sound_handle); + let (sound_path, sound_handle) = + get_relative_asset(load_context, self_path, sound); + dependencies.push(sound_path); + *handle = sound_handle.typed(); + } } // Load preloaded assets diff --git a/src/map.rs b/src/map.rs index 0f6dc2222d..06e0977848 100644 --- a/src/map.rs +++ b/src/map.rs @@ -39,6 +39,7 @@ impl Plugin for MapPlugin { /// script. #[derive(Reflect, Component, Default)] #[reflect(Component, Default)] +#[component(storage = "SparseSet")] pub struct MapElementHydrated; /// If this component and a [`Transform`] component is added to any entity, it will be moved back to diff --git a/src/map/elements.rs b/src/map/elements.rs index eac51f17b2..39d64048dc 100644 --- a/src/map/elements.rs +++ b/src/map/elements.rs @@ -18,6 +18,7 @@ pub mod player_spawner; pub mod sproinger; // Items +pub mod crate_item; pub mod grenade; pub mod sword; @@ -27,6 +28,7 @@ impl Plugin for MapElementsPlugin { fn build(&self, app: &mut App) { app.add_plugin(decoration::DecorationPlugin) .add_plugin(grenade::GrenadePlugin) + .add_plugin(crate_item::CrateItemPlugin) .add_plugin(player_spawner::PlayerSpawnerPlugin) .add_plugin(sproinger::SproingerPlugin) .add_plugin(sword::SwordPlugin); diff --git a/src/map/elements/crate_item.rs b/src/map/elements/crate_item.rs new file mode 100644 index 0000000000..2a30f5c4ed --- /dev/null +++ b/src/map/elements/crate_item.rs @@ -0,0 +1,336 @@ +//! The crate item. +//! +//! This module is inconsistently named with the rest of the modules ( i.e. has an `_item` suffix ) +//! because `crate` is a Rust keyword. + +use crate::{physics::collisions::TileCollision, player::PlayerKillCommand}; + +use super::*; + +pub struct CrateItemPlugin; + +#[derive(Reflect, Component, Clone, Debug)] +#[reflect(Component)] +pub struct IdleCrateItem { + /// The entity ID of the map element that spawned the crate + spawner: Entity, +} + +impl Default for IdleCrateItem { + fn default() -> Self { + Self { + spawner: crate::utils::invalid_entity(), + } + } +} + +#[derive(Reflect, Component, Clone, Debug)] +#[reflect(Component, Default)] +pub struct ThrownCrateItem { + /// The entity ID of the map element that spawned the crate + spawner: Entity, + /// The entity ID of the player that threw the box + owner: Entity, + age: f32, +} + +impl Default for ThrownCrateItem { + fn default() -> Self { + Self { + spawner: crate::utils::invalid_entity(), + owner: crate::utils::invalid_entity(), + age: 0.0, + } + } +} + +impl Plugin for CrateItemPlugin { + fn build(&self, app: &mut App) { + app.add_rollback_system(RollbackStage::PreUpdate, pre_update_in_game) + .add_rollback_system(RollbackStage::Update, update_thrown_crates) + .add_rollback_system(RollbackStage::Update, update_idle_crates) + .extend_rollback_plugin(|plugin| { + plugin + .register_rollback_type::() + .register_rollback_type::() + }); + } +} + +fn pre_update_in_game( + mut commands: Commands, + non_hydrated_map_elements: Query< + (Entity, &Sort, &Handle, &Transform), + Without, + >, + mut ridp: ResMut, + element_assets: ResMut>, +) { + // Hydrate any newly-spawned crates + let mut elements = non_hydrated_map_elements.iter().collect::>(); + elements.sort_by_key(|x| x.1); + for (entity, _sort, map_element_handle, transform) in elements { + let map_element = element_assets.get(map_element_handle).unwrap(); + if let BuiltinElementKind::Crate { + body_size, + body_offset, + atlas_handle, + .. + } = &map_element.builtin + { + commands.entity(entity).insert(MapElementHydrated); + + commands + .spawn() + .insert(Rollback::new(ridp.next_id())) + .insert(Item { + script: "core:crate".into(), + }) + .insert(IdleCrateItem { spawner: entity }) + .insert(EntityName("Item: Crate".into())) + .insert(AnimatedSprite { + start: 0, + end: 0, + atlas: atlas_handle.inner.clone(), + repeat: false, + ..default() + }) + .insert(map_element_handle.clone_weak()) + .insert_bundle(VisibilityBundle::default()) + .insert(MapRespawnPoint(transform.translation)) + .insert_bundle(TransformBundle { + local: *transform, + ..default() + }) + .insert(KinematicBody { + size: *body_size, + offset: *body_offset, + gravity: 1.0, + has_mass: true, + has_friction: true, + ..default() + }); + } + } +} + +fn update_idle_crates( + mut commands: Commands, + players: Query<(&AnimatedSprite, &Transform, &KinematicBody), With>, + mut grenades: Query< + ( + &Rollback, + Entity, + &IdleCrateItem, + &mut Transform, + &mut AnimatedSprite, + &mut KinematicBody, + &Handle, + Option<&Parent>, + Option<&ItemUsed>, + Option<&ItemDropped>, + ), + Without, + >, + mut ridp: ResMut, + element_assets: ResMut>, +) { + let mut items = grenades.iter_mut().collect::>(); + items.sort_by_key(|x| x.0.id()); + for ( + _, + item_ent, + crate_item, + mut transform, + mut sprite, + mut body, + meta_handle, + parent, + used, + dropped, + ) in items + { + let meta = element_assets.get(meta_handle).unwrap(); + let BuiltinElementKind::Crate { + grab_offset, + atlas_handle, + throw_velocity, + .. + } = &meta.builtin else { + unreachable!(); + }; + + // If the item is being held + if let Some(parent) = parent { + let (player_sprite, player_transform, player_body) = + players.get(parent.get()).expect("Parent is not player"); + + // Deactivate items while held + body.is_deactivated = true; + + // Flip the sprite to match the player orientation + let flip = player_sprite.flip_x; + sprite.flip_x = flip; + let flip_factor = if flip { -1.0 } else { 1.0 }; + let horizontal_flip_factor = Vec2::new(flip_factor, 1.0); + transform.translation.x = grab_offset.x * flip_factor; + transform.translation.y = grab_offset.y; + transform.translation.z = 0.0; + + // If the item is being used + if used.is_some() { + // Despawn the item from the player's hand + commands.entity(item_ent).despawn(); + + // Spawn a new, lit grenade + commands + .spawn() + .insert(Rollback::new(ridp.next_id())) + .insert(Name::new("Crate ( Thrown )")) + .insert(Transform::from_translation( + player_transform.translation + + (*grab_offset * horizontal_flip_factor).extend(0.0), + )) + .insert(GlobalTransform::default()) + .insert(Visibility::default()) + .insert(ComputedVisibility::default()) + .insert(AnimatedSprite { + atlas: atlas_handle.inner.clone(), + ..default() + }) + .insert(meta_handle.clone_weak()) + .insert(body.clone()) + .insert(ThrownCrateItem { + spawner: crate_item.spawner, + owner: parent.get(), + ..default() + }) + .insert(KinematicBody { + velocity: *throw_velocity * horizontal_flip_factor + player_body.velocity, + is_deactivated: false, + fall_through: true, + ..body.clone() + }); + } + } + + // If the item is dropped + if let Some(dropped) = dropped { + commands.entity(item_ent).remove::(); + let (player_sprite, player_transform, player_body) = + players.get(dropped.player).expect("Parent is not a player"); + + // Re-activate physics + body.is_deactivated = false; + + // Put sword in rest position + sprite.start = 0; + sprite.end = 0; + body.velocity = player_body.velocity; + body.is_spawning = true; + + let horizontal_flip_factor = if player_sprite.flip_x { + Vec2::new(-1.0, 1.0) + } else { + Vec2::ONE + }; + + // Drop item at player position + transform.translation = + player_transform.translation + (*grab_offset * horizontal_flip_factor).extend(0.0); + } + } +} + +fn update_thrown_crates( + mut commands: Commands, + players: Query>, + mut grenades: Query< + ( + &Rollback, + Entity, + &mut ThrownCrateItem, + &Transform, + &Handle, + ), + Without, + >, + mut ridp: ResMut, + element_assets: ResMut>, + player_inputs: Res, + effects: Res>, + collision_world: CollisionWorld, +) { + let mut items = grenades.iter_mut().collect::>(); + items.sort_by_key(|x| x.0.id()); + for (_, item_ent, mut crate_item, transform, meta_handle) in items { + let meta = element_assets.get(meta_handle).unwrap(); + let BuiltinElementKind::Crate { + breaking_atlas_handle, + breaking_anim_fps, + breaking_anim_length, + break_sound_handle, + break_timeout, + .. + } = &meta.builtin else { + unreachable!(); + }; + let frame_time = 1.0 / crate::FPS as f32; + + crate_item.age += frame_time; + + let colliding_with_wall = { + let collider = collision_world.get_collider(item_ent); + let width = collider.width + 2.0; + let height = collider.width + 2.0; + let pos = transform.translation.truncate(); + collision_world.collide_solids(pos, width, height) == TileCollision::Solid + }; + + let colliding_with_players = collision_world + .actor_collisions(item_ent) + .into_iter() + .filter(|&x| x != crate_item.owner && players.contains(x)) + .collect::>(); + + for &player in &colliding_with_players { + commands.add(PlayerKillCommand::new(player)); + } + + if !colliding_with_players.is_empty() + || colliding_with_wall + || crate_item.age > *break_timeout + { + if player_inputs.is_confirmed { + effects.play(break_sound_handle.clone_weak()); + } + + // Despawn the grenade + commands.entity(item_ent).despawn(); + // Cause the item to re-spawn by re-triggering spawner hydration + commands + .entity(crate_item.spawner) + .remove::(); + + // Spawn the explosion sprite entity + commands + .spawn() + .insert(Rollback::new(ridp.next_id())) + .insert(*transform) + .insert(GlobalTransform::default()) + .insert(Visibility::default()) + .insert(ComputedVisibility::default()) + .insert(AnimatedSprite { + start: 0, + end: *breaking_anim_length, + atlas: breaking_atlas_handle.inner.clone(), + repeat: false, + fps: *breaking_anim_fps, + ..default() + }) + .insert(Lifetime::new( + *breaking_anim_fps * *breaking_anim_length as f32, + )); + } + } +} diff --git a/src/metadata/map.rs b/src/metadata/map.rs index d78e5beec6..a256dff6ee 100644 --- a/src/metadata/map.rs +++ b/src/metadata/map.rs @@ -263,4 +263,28 @@ pub enum BuiltinElementKind { #[serde(skip)] sound_handle: Handle, }, + /// + Crate { + atlas: String, + #[serde(skip)] + atlas_handle: AssetHandle, + + breaking_atlas: String, + #[serde(skip)] + breaking_atlas_handle: AssetHandle, + breaking_anim_length: usize, + breaking_anim_fps: f32, + + break_sound: String, + #[serde(skip)] + break_sound_handle: Handle, + + throw_velocity: Vec2, + + body_size: Vec2, + body_offset: Vec2, + grab_offset: Vec2, + // How long to wait before despawning a thrown crate, if it hans't it anything yet. + break_timeout: f32, + }, } diff --git a/src/physics/collisions.rs b/src/physics/collisions.rs index 9de7bde63c..775e2c3be8 100644 --- a/src/physics/collisions.rs +++ b/src/physics/collisions.rs @@ -538,4 +538,8 @@ impl<'w, 's> CollisionWorld<'w, 's> { || tile == TileCollision::JumpThrough } } + + pub fn get_collider(&self, collider: Entity) -> &Collider { + self.actors.get(collider).unwrap().1 + } }