From 7579620459576c7598ebbd08dd371e1a6fd87056 Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Sun, 2 Feb 2025 22:56:16 +0100 Subject: [PATCH 01/11] HOTFIX update staging submodule to 242.0.1 (#34833) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index d7f6a9ba43..bd69d51d36 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit d7f6a9ba4394b34e55b3fdd4d98c5dfe2bb21642 +Subproject commit bd69d51d360ec77e79ca173e01caabb0a31a0a23 From f34233a0740f43cae771cfd4154805fdec3cdfa6 Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Sun, 2 Feb 2025 22:59:45 +0100 Subject: [PATCH 02/11] HOTFIX revert lecter visual update #34589 (#34826) --- .../Weapons/Guns/Rifles/lecter.rsi/base.png | Bin 583 -> 465 bytes .../Guns/Rifles/lecter.rsi/bolt-open.png | Bin 587 -> 459 bytes .../Rifles/lecter.rsi/equipped-BACKPACK.png | Bin 1246 -> 1045 bytes .../lecter.rsi/equipped-SUITSTORAGE.png | Bin 1280 -> 1045 bytes .../Weapons/Guns/Rifles/lecter.rsi/icon.png | Bin 645 -> 488 bytes .../Guns/Rifles/lecter.rsi/inhand-left.png | Bin 880 -> 391 bytes .../Guns/Rifles/lecter.rsi/inhand-right.png | Bin 883 -> 398 bytes .../Weapons/Guns/Rifles/lecter.rsi/mag-0.png | Bin 181 -> 196 bytes .../Weapons/Guns/Rifles/lecter.rsi/meta.json | 2 +- .../Rifles/lecter.rsi/wielded-inhand-left.png | Bin 922 -> 832 bytes .../lecter.rsi/wielded-inhand-right.png | Bin 905 -> 822 bytes 11 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/base.png b/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/base.png index c2c3b87a546468b8a637f33dd5b253de78cf952c..e30d896202d3f6b8464d3674ee77f473f05c4f5e 100644 GIT binary patch literal 465 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jPK-BC>eK@{oCO|{#S9GG z!XV7ZFl&wkP%uBhC&U#SOD1`PPVG5sxB@r`I!ky zN=iUwK%l9nA}uYQmXBLcmo4y` zXr$7!aaNQV*Vicimu}fSe8LPjgqE8uz5dx@A*ZG5ISD~?SGn_f`~eclbBjZSw8N&Z zdbP_=L{09z4%_R#GrsYRs(!iEa;7hN*JUnob8$;ft8BS*;UCk3HO!fcmm`u_>?x~N zl)rXi;){a<8+T4zxpz&%mwy%3ucqzd`RhCBcg@*L2hL6Z&2sbnE{nK7=jQ8wV#xTw VW91)NvjFHT22WQ%mvv4FO#s44t#$wa delta 569 zcmV-90>=H(1IGlA8Gi-<0047(dh`GQ00DDSM?wIu&K&6g00IF?L_t(oh3%BVOB7KU zz<*EX5H=zO9VBME2)o`Yty`pcT0BHTwVB7x)u|xpAfD5(6L<0E z77-iadRPbt)0HK99cJJ3=FP6Nc5QyAH{W~T`+eWMHv?5L7IhhUqDM9k+$i|M2S@)14rAkQmF_4E0f8{O7G>4 z$#eH?rr-Vy#neXn<|m=AS4b%lLilf>Z)B3fvzv^*`~VOd_ns-BI#>KYd*Q^*P4x9< z5f%`fsV>&fS$`h`6yD7;I{pHHnQOw$mRUYYyo(+uK)}` zd`0Q?Q}mcQ2Dmcx09vsp%K){|e5nR2_y1n6X5%0<{~V}Rs{{q;=tbb|a&&Bi_rrhg zzX*WyWigcVeekZiO+>u3wgtdmeIMwdSS*U*4k)pb4u9Wy?2pR}V!G@PrzWn;sauzr zUtVVe$Y!(T^LddeK@{oCO|{#S9GG z!XV7ZFl&wkP%tOJC&U#SOD1`PPVG5sxB@r`I!ky zN=iUwK%l9nA}uYQmXfo4B}mczC#>p`o_6wgd=>1JzhsS^`N~Sy>5j(T7Lx zMF8znDGBlmW?=g9`#%u;{`dd?9}wgJKV~omAy_zm|L1sjbYUe>{RvMO$B>FSZzrA> zYBJz)ZB}E|HeH|_B`Es$f4xa=x&&kCQ$PRBFYnBD4{o@xTCJ`#ZHC}Mf#86r9;`yk zB7AkZTdUT#XRkDMYwu*p=_sBh^hGNu+v(gyZnq!jv*^~O>v)8($TE-PWKj? zy{t$xj3`gZb7q}lQF^!Xii$+cZLi*zo?~Y|ah$yHkLkf0=1j%Q5y>ldep@)9Kl_E} zmkf^^r%qgn3p1#yKjpV;={|@0$){`grDY~OKV94OCOuY0zwY^Y@k)kHL++bDTH-*_ O#o+1c=d#Wzp$P!x4y_OX delta 573 zcmV-D0>b^v1Iq-E8Gi-<0047(dh`GQ00DDSM?wIu&K&6g00IR`L_t(oh3%9vXcJKw z$A7QfAoWU7a42$d>5y(}tD9Qs6dZ~LZLA$TtCNc0AWjMe*Bp-0R_Ri(OFlIQ;gb@A{=ouO#|L7XSFFpZ;>h2SH)Z~gkC(p!gZlbR@ zLs&rYPIY7bJ%8&pK>ovXhDV+P@a)n7)6=hldq1x}roZo$Vf8z7wFhLg{$aKNyy^x3 zgHtalzJ7?_Gi!hg19zd>d$0iX-#)xq-Jn>7_jeADSEIfg(mw$zl?p)s9X$lz7W)S_ zct8B>-m?HW-`|Ban|c5Z-FSX!4S?<14$wuRP!PcxP=ChCbnw=Fzb?&*$&z1A+_)?! zM$a*`u*?dO$z;goaw3&VMGv93MWcS2`K7g{A$B6JT{pqjMsP6_7IgTR%Y}<|)o9M=7khat6=IW6yuYL>Z zT1}=Kwl-R$`W^iV5CL|;zB*gI-hS1?<8@d#U00000 LNkvXXu0mjfYL^g^ diff --git a/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/equipped-BACKPACK.png b/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/equipped-BACKPACK.png index 31598a8d1d3a2642d3a347da7f26aa179183dd15..7e73d94ffa2ce457cba48ff86dd213181f594da7 100644 GIT binary patch delta 1011 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 diff --git a/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/equipped-SUITSTORAGE.png b/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/equipped-SUITSTORAGE.png index f77d6fed5bb78a22785aa6230a4c1999c3cf48df..7e73d94ffa2ce457cba48ff86dd213181f594da7 100644 GIT binary patch delta 1011 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 delta 631 zcmV--0*L+S1BC^U8Gi-<0047(dh`GQ00DDSM?wIu&K&6g00KWrL_t(oh3%6;YZFlv zg}+y3AxTPA6fE-6g{4Vdh}znnnBr=1AuXt}cImphQV?8-EByxvE{jy@rXU0g+DH+i zv_V{GEGnrXQHY44wkbh;E+#X+nQ4>Mg$T}pfjjrTbI-YV?tegl00I7Yh#fPIC)FRT zs z8G+hY>%He70P@{K7pK&J-dn~eJUhPxz^Cdrpo?5CCx4tBu!U{Wp>tQPI6osM^Hw-< zW<*V#?c@I30*ioYnxs-G5sgOuOIRz@sPAQVeyJ%4PoBTs@^3M`#7=%IfOI-7(&;o( zN;e;n)n|-NeH3F;R$2&w5CWzAUJaeNow(KgD2@7>(~*=?0YoAZho1-)8901|OvcUE zg}p}Fc5thU+aq0gvEt)NDb=AqYey^-8A_#+bI;qo5Y|ywI|D#laT}k%SkqZ~{R-1G zohjLZ`s3s~F)%tZ;1p_QKuW2?;V=jG?dJVz*)@T`1}UY|I}Z>bK!AT9zX0EL@aJsx REII%H002ovPDHLkV1l!AC=&nx diff --git a/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/inhand-left.png index a3344cc76ce37828c1a8fe04752aca91fdfde6c6..27972c97f500054d5e88d1af644ff92921307c44 100644 GIT binary patch delta 376 zcmeys*3LXZqMn7Bfq@~yJ?AZuVk{1FcVfJGQl}os;VkfoEM{Qf76xHPhFNnYfPxYM zJ|V6^+R@HdTT@*_L&MzMJjl<>+uM8cgu(AmF-O?F)mYqsZ;j1@#N_nkT+_%gJ(7&f&u!)5%v{WnOO6tz$`k_B>tb z38T!fYR;^no24_hPBQq?+vT_8Nyw2Ez7i1`-Ax8NIv>wA-o<*szg2JhfP}&*$DoA_PxftC)j*+@Dk_XvO)V7>ceSbn%>co*vTf`2O&tlMMSM_)ox|heU zu$}L3N6x3YOWV|T7O2mZxL5HZ=~|Akl3deIzdQFk*!9n)ef4;)^XTB|kmTuQ3^qW< zxxZ!I#Rr@-9(oz5JYp=5mYDQ!swlt227{Cv^O!z1+)DSD@%Mnh54*NK49^1PKi{$0 RxfB=z44$rjF6*2UngG)ppH~0? delta 869 zcmV-r1DgDY1Mmir7=H)`00020X>r~F0004VQb$4nuFf3k0009jNklShB9o8aFN7_#!J5XXTcTe|Veju^@+EomzK`Gc<$v%#06`E0K@bE%5ClQo ze6nRGNfP4~F2F00MYhbW-{CRm*stEa1uqf!3jhGwG7HIybv$bf=eb&mSf{*1;4VNo zS%6jiyf{Qqw!dL0!s^pHZVB^G0$*l8;G&Psm9xRg!H(d07G%o|2Rnju`1P|GlzLNG zjo%Y+up=DzK7Wnf<2l!P12{2z9hkiiHo6OFRP$HGxD~;6f1{eWhSf?0KYyI_P8V}4 zIEJmzaI%0G`CnLn*&Pf__*bVr->BxX(OrPq>$rPvMz{wSMUer3vGf?gK!o3SmvH_; zbuBlNS+>MR}$H9}yGMS0bW-=K>-#s5Get69uk2k(;E>0v#V&QNY0IV95}x7$h8Q!{YW4rD}!F3UpU2`&~tR#I_EwEHdoHB8lis7g3Fj8SwTov z5Kb1x@A1%0<}Sd@z5qAaj1!wHXCobCb!&~OTYqbw-^F(cWXsI#b+CZ~8r3}30sh5b z8Ye98fDL;;(JfqOKryHTfaMV3<`o*kxp;uH0Etu@#h~up7vPeRtc(<24CB$jx_qt=gfRL=rXK9;YoG1o$41EJbSIR7K+XI|Ee5KwL)U7q9Y+v~TG)?my zS6-4NrU7HdiOb6@kxByq48xcyZ{PdyfAa;np;Rid!^1;p!G~kUiQ+uM8c0Jnuw9CqI}e|J`+w!o`i{rG#$R&w9s0Y4XF;9_o5mg4FRU-hYvdoK z)pzz4^2m7He;hNpG@317!u7=H)`00020X>r~F0004VQb$4nuFf3k0009mNkln9i3IE zAR>aTD-1fyycC2|bg+%R9hziKli4;+oW1s<>~`ow z764Egc?tlK!U_C-G=ln(=4ilxWvfNJwm6KBXTMM?gnx_?q905c9}VHf)MGrK9)P-) zgA`667*$aRM5_trJAiX809eGU=?u$evut8&_O>;+W_&b+iK$s+vstF5GfZ0?My;4b z;HrvXR5f)Pm$kpm1b{}p2F)gf_ST>%ie)SVfQ5wxQ7)GO z0JT~T=YQwteIc|PIOjr=BmltJ*cf(pcB~p>vDlsbwdXbWMJGWJ1VIo4K@bE%5ClOG zJu)4h{J(!DHfI$)q@(uuba+ynJ)HCU+MUy!Rq&XB_CxeGbY#x%e(N3av3cr;c%Z`- z0Kie4(Q`A4^CL;3x3}v@8rEknJfFUE19Y^F6@Q^^tOx*R&&@2XO&FnT9I^^t)3pK3 zJwu>j!fJl7qcDOiT7nv0ud=QU06H+iLxNG&64XczmA#zaodE`N^}sC1Fu}8zl|xn` zIaG$%t$No6$g(VqUVL(>jMhGUFDrp_ZWFJjGi-N1&q{?;EH5v6w#r-^V2CdjPO-b6 zw_$}53?XjjRul!Hy*0#sMSf<2gt(}LxK#9wtOY@y}U}oXkrgC*RY>F(jh(?WK)e4GJQy4}-r* zS(%xbh>Ki#5o~U<#oXkJ(Q?mBZfBlHH3~88AK6)5fZ$(KO2jdVUq3yk8YRh9U-OkN zUFK7|bL%vQ0y*nB#fkHE85pLOYTt8FDXjmU`QpX%HFAu0O3#G5I%?iAu^zSVyj8fy Q8fZ6zr>mdKI;Vst09&~}xBvhE diff --git a/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/meta.json index 5c000b6638..c45db54aff 100644 --- a/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/meta.json +++ b/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken/modified from tgstation at https://github.com/tgstation/tgstation/pull/41393/commits/1e56473177d0994d163c9edca3d13d6e5b640cc4, https://github.com/tgstation/tgstation/tree/master/icons/obj/weapons/guns backpack sprite by Peptide (copy of carbine), backpack sling sprite edited by Boaz1111, wield sprites by RiceMar1244, all sprites heavily edited by Emisse for SS14 using a palette inspired by coolstation weapon sprites https://github.com/coolstation/coolstation", + "copyright": "Taken/modified from tgstation at https://github.com/tgstation/tgstation/pull/41393/commits/1e56473177d0994d163c9edca3d13d6e5b640cc4, https://github.com/tgstation/tgstation/tree/master/icons/obj/weapons/guns backpack sprite by Peptide (copy of carbine), backpack sling sprite edited by Boaz1111, wield sprites by RiceMar1244", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/wielded-inhand-left.png b/Resources/Textures/Objects/Weapons/Guns/Rifles/lecter.rsi/wielded-inhand-left.png index a3ba70d961987661ce0c4c428d5a92ef82fda567..42926b00f4705985591abade8f839204c0ee50f7 100644 GIT binary patch delta 810 zcmV+_1J(SR2fzl9BYy)2Nkl~?IK-kYl?x(T48aBs*CCZE$@+7$>@*?oZxQJ3z324a zAA{cqAP9mW2!bF8f_R3k6*V%(Bmn5Tj@#SY(x>Nnh2uRmjDIm%HmBT1#+cmM*}-@` z#?{qTZohuNFWc=l0Kjn^IF9pZl3<#qv@EM|VR9Qd=Q6J?2!dSE_4V~c5{~xwBmjK= z@>Mx8VN6Js*fgAT>AG%i{LAzU(=;UjxIe$+@L(4wXBTr%{z||wj3{O_8o_m43i>-D03Qh((`j`sItECwfM7fLG`BDx)EU*AOiv}Ob1?>C*Ep3cS3CBU*Q06=R) z!*DnPfVrpd`|y1~5yv^l`T6VMOyY68;lv}Oa_Tbs4+f~6z69eK3BCyOr2r=T-;2dE&r9l5==nb}sh<*H&|->gmU zrYlWyWkk0l-@kji^3{LTJnN4q2!bF8f*=TjAP9mW2!eR3RQms(F(!*V0!3x?I`sQ} zxhhTN!hc9u3Jbjep|Gy&QrC5~+ilEdGr4Sk0Anoj9{~5|H_!7TUqW3?-xKx;!o zST$NkEiX$q?N3P8B*PN>hmW61^-Fhv5JGa!0c3jy2CokQ{PPI-zK_F$-7s9q6Oe~# znlfASO+wT2yhL2DXGEp^GD;ILnM@)tK$d6V@_+I&>dQF?=e)8fAP>v3lr(MIPQ(p| zBW3N{M?ljw07;KPRZl=w-7L*`|0>$bmtfvtK@da}jJvWXcT?4&E9=HJv#6rIPFigZX>+01?0C0cf##i5U-L+M&ep+*xW{k>+ oZX@Tvm?X%XKoA5$5X3V01IEwN%>^*oWB>pF07*qoM6N<$f&%`6cmMzZ delta 900 zcmV-~1AF|y2AT(uBYyw^b5ch_0Itp)=>Px&R7pfZRCt{2noVdEK@`XTD^eOu8ZRl7 zbnQjT#2zYjTY6};MG%Uj5Tr?K&OutSc zgH**!Je6QA9x@)1T{pX%{m4f@==(unc4ue)Z(iQa9Nq&6f`1?gf*=TjAkH9yX>V_s zJDP1LVLsnc(;MbiHl453!5HH|r6BU@r1`>1PI5~@$A$YYBI|)xTPSNg&{JGG47Uv%DBvO3@h($^4v^R4vMj6#bS*YCctW1k~Z(>Llek9tj7bPm|s+7dYD z5Cj3iscczaGB&{hU`fhgEPbu4FCQPR``#aVzM(i_Nq@@VPTYEo+78Y+IyyRFb_P`J zkeyHwqZ`6#ua>@J2fL<*$S}ru_QF-=dA$8A#_tW|@1Hz^Q?E|U0Si$Sc{Cd3q9{7M zDjhO@yPqdx6MVS8t06kNL(mSQDDsh!5q$alQP(GLlX|hZx-Gn1_)yO&Yey7CJ~TMM zeLi19|9|}x&6!?2K@bE%5ClOG1VIo4K@h}g#Zjh3o3+u=sMNSwokES8wi6?Ac2`M< z?<54ZxW^xGUQl6E*W(Wu>efTkvOYjdp~VZ2KY)Ez&46s2_I*7gLaXcX2T*A77HblA z66GKFhPmBbq-6xix%Hl&9-T3Aoxn`rAyVtJWqiH9qPRw5Ufz9`i zb>p-(3T9YFfHt>2-n==ItzkPvY|4064Dgj_Bs0L3beB`Jdk8<()ZFj>jFTkyIFrZ)_=S2!HdlE!voQ|xm{>4GpGM@JrRT|2(bY42g6rm6zRP0`|y1~w(h!J z<=ASq7F~CUJXZTsOl>oZWm)j@86OCF`@WX>xK;DzA*YzpOiBDmwcG72sR^jdcS)p*)al8waNTaK#y%^g>JeB)Jx8UKgD6h1#+H02N8KbycrGp5B0Y+Wo;0wtsB{Kx)l-86ruQCi8j*qP+q^5X5Zr zJ_0K3MVeW3Q)K`EWxZV@@5Wp82}HH!zOyRLRC|Eud0X-`yH+R^@a=0kS4&G8?c&Zh zfMJ;EO}4N8&y^wo0000Px&LrFwIRCt{2nonpGK@`Tn1QE2XIkg~R z5mf5NOC_$wgRzmGiXxJOStRF0Dkyp>6pEDMp~tFt=-IYukbq(l#9~@cHBD(f*iaPO z2I(OeL+BrR$avW9cAIRH*=&+l`hKTmIy>LIH*Y7<_W=Y!5Pt+g5ClOG#}Jq8C;SSt zHGd&$U&E~BZrTs4=bwl$ahuuzLW5*LH9z z5&-}xZ`V+0X@4!dmvA`D0AO-*vT8v3+@+}5F)EJwPQ#jlG48h?UB>K7# z9G-!9YmueX>5}KIE1&{5@4f^8JWpnUf@%LXbzH#0dm$nvR^$OICD2hx~RVIpJ z!*6Ok-0Ey+@sVEk;C3h1zTCVE$47cg7F_xH13f)GIBH3FHTDJLQ{Nk3c3!WS1#etG za#he&ynkNr@eQ6J2!bF8f*=TjAP9mW2;zUk`Tx!}{-*2Ng}VZ*3=gv(9%JZo2GPhlj?+P3`xW`f-VKoUT5-yos4r+2sW=ug$J-)|1ESXa|r*EDVV5o{%( z*rkyhd*m|vcyyn@yU+93$o>W3Hv0XwqLutQfPIfZTU#63zW2JUW-gcGz6kjZ67%t@ z<7{_E>woKOC%=v}Q%{kH7Q9)v5t{b!D^2kU_}VWT>(|qp;P__{Xr52N+=;H2rlU(x z|5^Oq1b@Kt$}gkTGntGF6)bv(!eN*sL{U86<_v-$2!bFS@ed0*YJSnTF);uD002ov JPDHLkV1i(uuz&yn From 82bd0a291d95b99444498ecd356cf3c62145c355 Mon Sep 17 00:00:00 2001 From: pubbi <63283968+impubbi@users.noreply.github.com> Date: Sun, 2 Feb 2025 16:02:24 -0600 Subject: [PATCH 03/11] Allowed windows to be properly clicked on (#34751) --- Content.Client/Clickable/ClickMapManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Client/Clickable/ClickMapManager.cs b/Content.Client/Clickable/ClickMapManager.cs index 6a77c7e054..557ef57ef1 100644 --- a/Content.Client/Clickable/ClickMapManager.cs +++ b/Content.Client/Clickable/ClickMapManager.cs @@ -20,7 +20,7 @@ namespace Content.Client.Clickable "/Textures/Logo", }; - private const float Threshold = 0.25f; + private const float Threshold = 0.1f; private const int ClickRadius = 2; [Dependency] private readonly IResourceCache _resourceCache = default!; From f25720124ab5fe63a4389af74f67c9d430ae2294 Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Sun, 2 Feb 2025 23:03:31 +0100 Subject: [PATCH 04/11] Fix hitting through directional windows (and more!) (#34793) --- Content.Client/Weapons/Melee/MeleeWeaponSystem.cs | 2 +- Content.Server/Weapons/Melee/MeleeWeaponSystem.cs | 4 ++-- .../Interaction/SharedInteractionSystem.cs | 12 ++++++++---- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 8 +++++++- .../Entities/Structures/Windows/window.yml | 2 +- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index 26ec75f995..dc86580369 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -170,7 +170,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem var targetCoordinates = xform.Coordinates; var targetLocalAngle = xform.LocalRotation; - return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range); + return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range, overlapCheck: false); } protected override void DoDamageEffect(List targets, EntityUid? user, TransformComponent targetXform) diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs index ec462ae23e..3c8314041f 100644 --- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs @@ -78,7 +78,7 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem // Could also check the arc though future effort + if they're aimbotting it's not really going to make a difference. // (This runs lagcomp internally and is what clickattacks use) - if (!Interaction.InRangeUnobstructed(ignore, targetUid, range + 0.1f)) + if (!Interaction.InRangeUnobstructed(ignore, targetUid, range + 0.1f, overlapCheck: false)) return false; // TODO: Check arc though due to the aforementioned aimbot + damage split comments it's less important. @@ -193,7 +193,7 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem if (session is { } pSession) { (targetCoordinates, targetLocalAngle) = _lag.GetCoordinatesAngle(target, pSession); - return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range); + return Interaction.InRangeUnobstructed(user, target, targetCoordinates, targetLocalAngle, range, overlapCheck: false); } return Interaction.InRangeUnobstructed(user, target, range); diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 2926e1d1b3..a88f673cac 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -667,7 +667,8 @@ namespace Content.Shared.Interaction float range = InteractionRange, CollisionGroup collisionMask = InRangeUnobstructedMask, Ignored? predicate = null, - bool popup = false) + bool popup = false, + bool overlapCheck = true) { if (!Resolve(other, ref other.Comp)) return false; @@ -687,7 +688,8 @@ namespace Content.Shared.Interaction range, collisionMask, predicate, - popup); + popup, + overlapCheck); } /// @@ -717,6 +719,7 @@ namespace Content.Shared.Interaction /// /// True if the two points are within a given range without being obstructed. /// + /// If true, if the broadphase query returns an overlap (0f distance) this function will early out true with no raycast made. public bool InRangeUnobstructed( Entity origin, Entity other, @@ -725,7 +728,8 @@ namespace Content.Shared.Interaction float range = InteractionRange, CollisionGroup collisionMask = InRangeUnobstructedMask, Ignored? predicate = null, - bool popup = false) + bool popup = false, + bool overlapCheck = true) { Ignored combinedPredicate = e => e == origin.Owner || (predicate?.Invoke(e) ?? false); var inRange = true; @@ -768,7 +772,7 @@ namespace Content.Shared.Interaction inRange = false; } // Overlap, early out and no raycast. - else if (distance.Equals(0f)) + else if (overlapCheck && distance.Equals(0f)) { return true; } diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 767b5c4ef6..15b00e4610 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -731,7 +731,13 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem if (res.Count != 0) { - resSet.Add(res[0].HitEntity); + // If there's exact distance overlap, we simply have to deal with all overlapping objects to avoid selecting randomly. + var resChecked = res.Where(x => x.Distance.Equals(res[0].Distance)); + foreach (var r in resChecked) + { + if (Interaction.InRangeUnobstructed(ignore, r.HitEntity, range + 0.1f, overlapCheck: false)) + resSet.Add(r.HitEntity); + } } } diff --git a/Resources/Prototypes/Entities/Structures/Windows/window.yml b/Resources/Prototypes/Entities/Structures/Windows/window.yml index 861791c089..b7241b3d27 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/window.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/window.yml @@ -157,7 +157,7 @@ fix1: shape: !type:PhysShapeAabb - bounds: "-0.49,-0.49,0.49,-0.36" + bounds: "-0.5,-0.5,0.5,-0.28125" density: 1500 mask: - FullTileMask From 87fa6a3b74c66dc0f3f11009246841d228514db1 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sun, 2 Feb 2025 23:33:02 +0100 Subject: [PATCH 05/11] Revert "Add taped logo back for 10th anniversary" (#34831) --- Resources/Textures/Logo/logo-tape.png | Bin 55400 -> 0 bytes Resources/manifest.yml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 Resources/Textures/Logo/logo-tape.png diff --git a/Resources/Textures/Logo/logo-tape.png b/Resources/Textures/Logo/logo-tape.png deleted file mode 100644 index f8a7ae898c94d3f46ba946db691c7a434531dff9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55400 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 diff --git a/Resources/manifest.yml b/Resources/manifest.yml index 8b349c0a5e..6799118193 100644 --- a/Resources/manifest.yml +++ b/Resources/manifest.yml @@ -1,5 +1,5 @@ defaultWindowTitle: Space Station 14 windowIconSet: /Textures/Logo/icon -splashLogo: /Textures/Logo/logo-tape.png +splashLogo: /Textures/Logo/logo.png # PJB PLEASE From e58d0313002350cd7d9ed24ea390e0cb5724a662 Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Tue, 4 Feb 2025 22:55:09 +0100 Subject: [PATCH 06/11] Hristov & .60 changes - Hristov Rework, Part 2 (#31662) * Initial commit * Updated values to reflect new resistances * Review fixes * Review fixes * LINQ BEGONETH --- .../Projectiles/ProjectileSystem.cs | 57 +++++++++++++++++-- .../SpeedModifiedOnWieldComponent.cs | 23 ++++++++ .../Projectiles/ProjectileComponent.cs | 23 +++++++- .../Projectiles/SharedProjectileSystem.cs | 2 +- .../Wieldable/SharedWieldableSystem.cs | 27 ++++++++- .../Ammunition/Projectiles/antimateriel.yml | 9 ++- .../Objects/Weapons/Guns/Snipers/snipers.yml | 11 ++++ 7 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 Content.Shared/Movement/Components/SpeedModifiedOnWieldComponent.cs diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 5cbc56b8fb..57601c3e9f 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -1,9 +1,11 @@ using Content.Server.Administration.Logs; +using Content.Server.Destructible; using Content.Server.Effects; using Content.Server.Weapons.Ranged.Systems; using Content.Shared.Camera; using Content.Shared.Damage; using Content.Shared.Database; +using Content.Shared.FixedPoint; using Content.Shared.Projectiles; using Robust.Shared.Physics.Events; using Robust.Shared.Player; @@ -15,6 +17,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly ColorFlashEffectSystem _color = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly DestructibleSystem _destructibleSystem = default!; [Dependency] private readonly GunSystem _guns = default!; [Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!; @@ -28,7 +31,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem { // This is so entities that shouldn't get a collision are ignored. if (args.OurFixtureId != ProjectileFixture || !args.OtherFixture.Hard - || component.DamagedEntity || component is { Weapon: null, OnlyCollideWhenShot: true }) + || component.ProjectileSpent || component is { Weapon: null, OnlyCollideWhenShot: true }) return; var target = args.OtherEntity; @@ -45,7 +48,13 @@ public sealed class ProjectileSystem : SharedProjectileSystem RaiseLocalEvent(uid, ref ev); var otherName = ToPrettyString(target); - var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, origin: component.Shooter); + var damageRequired = _destructibleSystem.DestroyedAt(target); + if (TryComp(target, out var damageableComponent)) + { + damageRequired -= damageableComponent.TotalDamage; + damageRequired = FixedPoint2.Max(damageRequired, FixedPoint2.Zero); + } + var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, damageable: damageableComponent, origin: component.Shooter); var deleted = Deleted(target); if (modifiedDamage is not null && EntityManager.EntityExists(component.Shooter)) @@ -60,6 +69,46 @@ public sealed class ProjectileSystem : SharedProjectileSystem $"Projectile {ToPrettyString(uid):projectile} shot by {ToPrettyString(component.Shooter!.Value):user} hit {otherName:target} and dealt {modifiedDamage.GetTotal():damage} damage"); } + // If penetration is to be considered, we need to do some checks to see if the projectile should stop. + if (modifiedDamage is not null && component.PenetrationThreshold != 0) + { + // If a damage type is required, stop the bullet if the hit entity doesn't have that type. + if (component.PenetrationDamageTypeRequirement != null) + { + var stopPenetration = false; + foreach (var requiredDamageType in component.PenetrationDamageTypeRequirement) + { + if (!modifiedDamage.DamageDict.Keys.Contains(requiredDamageType)) + { + stopPenetration = true; + break; + } + } + if (stopPenetration) + component.ProjectileSpent = true; + } + + // If the object won't be destroyed, it "tanks" the penetration hit. + if (modifiedDamage.GetTotal() < damageRequired) + { + component.ProjectileSpent = true; + } + + if (!component.ProjectileSpent) + { + component.PenetrationAmount += damageRequired; + // The projectile has dealt enough damage to be spent. + if (component.PenetrationAmount >= component.PenetrationThreshold) + { + component.ProjectileSpent = true; + } + } + } + else + { + component.ProjectileSpent = true; + } + if (!deleted) { _guns.PlayImpactSound(target, modifiedDamage, component.SoundHit, component.ForceSound); @@ -68,9 +117,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem _sharedCameraRecoil.KickCamera(target, args.OurBody.LinearVelocity.Normalized()); } - component.DamagedEntity = true; - - if (component.DeleteOnCollide) + if (component.DeleteOnCollide && component.ProjectileSpent) QueueDel(uid); if (component.ImpactEffect != null && TryComp(uid, out TransformComponent? xform)) diff --git a/Content.Shared/Movement/Components/SpeedModifiedOnWieldComponent.cs b/Content.Shared/Movement/Components/SpeedModifiedOnWieldComponent.cs new file mode 100644 index 0000000000..01b02e60e6 --- /dev/null +++ b/Content.Shared/Movement/Components/SpeedModifiedOnWieldComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared.Wieldable; +using Robust.Shared.GameStates; + +namespace Content.Shared.Movement.Components; + +/// +/// Modifies the speed when an entity with this component is wielded. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedWieldableSystem)), AutoGenerateComponentState] +public sealed partial class SpeedModifiedOnWieldComponent : Component +{ + /// + /// How much the wielder's sprint speed is modified when the component owner is wielded. + /// + [DataField, AutoNetworkedField] + public float SprintModifier = 1f; + + /// + /// How much the wielder's walk speed is modified when the component owner is wielded. + /// + [DataField, AutoNetworkedField] + public float WalkModifier = 1f; +} diff --git a/Content.Shared/Projectiles/ProjectileComponent.cs b/Content.Shared/Projectiles/ProjectileComponent.cs index 8349252df2..9eee3767a6 100644 --- a/Content.Shared/Projectiles/ProjectileComponent.cs +++ b/Content.Shared/Projectiles/ProjectileComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Damage; +using Content.Shared.FixedPoint; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -75,8 +76,26 @@ public sealed partial class ProjectileComponent : Component public bool OnlyCollideWhenShot = false; /// - /// Whether this projectile has already damaged an entity. + /// If true, the projectile has hit enough targets and should no longer interact with further collisions pending deletion. /// [DataField] - public bool DamagedEntity; + public bool ProjectileSpent; + + /// + /// When a projectile has this threshold set, it will continue to penetrate entities until the damage dealt reaches this threshold. + /// + [DataField] + public FixedPoint2 PenetrationThreshold = FixedPoint2.Zero; + + /// + /// If set, the projectile will not penetrate objects that lack the ability to take these damage types. + /// + [DataField] + public List? PenetrationDamageTypeRequirement; + + /// + /// Tracks the amount of damage dealt for penetration purposes. + /// + [DataField] + public FixedPoint2 PenetrationAmount = FixedPoint2.Zero; } diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 85e75d6d29..bca9b36f89 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -79,7 +79,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem { projectile.Shooter = null; projectile.Weapon = null; - projectile.DamagedEntity = false; + projectile.ProjectileSpent = false; } // Land it just coz uhhh yeah diff --git a/Content.Shared/Wieldable/SharedWieldableSystem.cs b/Content.Shared/Wieldable/SharedWieldableSystem.cs index 24c63c5f2f..110c941dac 100644 --- a/Content.Shared/Wieldable/SharedWieldableSystem.cs +++ b/Content.Shared/Wieldable/SharedWieldableSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Interaction.Events; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Item; using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; using Content.Shared.Popups; using Content.Shared.Timing; using Content.Shared.Verbs; @@ -27,6 +28,7 @@ namespace Content.Shared.Wieldable; public abstract class SharedWieldableSystem : EntitySystem { + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; @@ -56,6 +58,9 @@ public abstract class SharedWieldableSystem : EntitySystem SubscribeLocalEvent(OnGunUnwielded); SubscribeLocalEvent(OnGunRefreshModifiers); SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnSpeedModifierWielded); + SubscribeLocalEvent(OnSpeedModifierUnwielded); + SubscribeLocalEvent>(OnRefreshSpeedWielded); SubscribeLocalEvent(OnGetMeleeDamage); } @@ -119,9 +124,29 @@ public abstract class SharedWieldableSystem : EntitySystem } } + private void OnSpeedModifierWielded(EntityUid uid, SpeedModifiedOnWieldComponent component, ItemWieldedEvent args) + { + if (args.User != null) + _movementSpeedModifier.RefreshMovementSpeedModifiers(args.User); + } + + private void OnSpeedModifierUnwielded(EntityUid uid, SpeedModifiedOnWieldComponent component, ItemUnwieldedEvent args) + { + if (args.User != null) + _movementSpeedModifier.RefreshMovementSpeedModifiers(args.User); + } + + private void OnRefreshSpeedWielded(EntityUid uid, SpeedModifiedOnWieldComponent component, ref HeldRelayedEvent args) + { + if (TryComp(uid, out var wield) && wield.Wielded) + { + args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier); + } + } + private void OnExamineRequires(Entity entity, ref ExaminedEvent args) { - if(entity.Comp.WieldRequiresExamineMessage != null) + if (entity.Comp.WieldRequiresExamineMessage != null) args.PushText(Loc.GetString(entity.Comp.WieldRequiresExamineMessage)); } diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimateriel.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimateriel.yml index 65b7dbc165..7556f2c371 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimateriel.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimateriel.yml @@ -7,7 +7,10 @@ - type: Projectile damage: types: - Piercing: 40 - Structural: 30 + Piercing: 75 + Structural: 226 + penetrationThreshold: 360 + penetrationDamageTypeRequirement: + - Structural - type: StaminaDamageOnCollide - damage: 35 + damage: 60 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml index cca3a6f302..23dc96c49f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml @@ -57,12 +57,23 @@ sprite: Objects/Weapons/Guns/Snipers/heavy_sniper.rsi - type: Clothing sprite: Objects/Weapons/Guns/Snipers/heavy_sniper.rsi + - type: GunRequiresWield + - type: Gun + fireRate: 0.4 + selectedMode: SemiAuto + availableModes: + - SemiAuto + soundGunshot: + path: /Audio/Weapons/Guns/Gunshots/sniper.ogg - type: BallisticAmmoProvider whitelist: tags: - CartridgeAntiMateriel capacity: 5 proto: CartridgeAntiMateriel + - type: SpeedModifiedOnWield + walkModifier: 0.25 + sprintModifier: 0.25 - type: CursorOffsetRequiresWield - type: EyeCursorOffset maxOffset: 3 From eb2aa083cced0c166e50bb0c01ab731c0a656d65 Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:00:49 +0100 Subject: [PATCH 07/11] revert engine --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index bd69d51d36..ee906af16e 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit bd69d51d360ec77e79ca173e01caabb0a31a0a23 +Subproject commit ee906af16e136c7e09930d88872ab9bba3137c8e From d6a9ddd9e0f2162ea661d9b80f26a09b36979d8c Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:12:52 +0100 Subject: [PATCH 08/11] Revert "Storage sidebar fix (#34680)" This reverts commit 3e091c4dfaf2c23dd469c3d2b8e0e66b292a596f. --- .../Systems/Storage/Controls/StorageWindow.cs | 26 +++++++++++++------ .../EntitySystems/SharedStorageSystem.cs | 1 - 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Content.Client/UserInterface/Systems/Storage/Controls/StorageWindow.cs b/Content.Client/UserInterface/Systems/Storage/Controls/StorageWindow.cs index 88b4c06d72..4527ffd59a 100644 --- a/Content.Client/UserInterface/Systems/Storage/Controls/StorageWindow.cs +++ b/Content.Client/UserInterface/Systems/Storage/Controls/StorageWindow.cs @@ -201,8 +201,7 @@ public sealed class StorageWindow : BaseWindow #region Sidebar _sidebar.Children.Clear(); - var rows = boundingGrid.Height + 1; - _sidebar.Rows = rows; + _sidebar.Rows = boundingGrid.Height + 1; var exitButton = new TextureButton { @@ -244,10 +243,12 @@ public sealed class StorageWindow : BaseWindow }; _sidebar.AddChild(exitContainer); - var offset = 2; + var offset = 1; - if (_entity.System().NestedStorage && rows > 0) + if (_entity.System().NestedStorage && boundingGrid.Height > 0) { + offset += 1; + _backButton = new TextureButton { TextureNormal = _backTexture, @@ -279,7 +280,7 @@ public sealed class StorageWindow : BaseWindow { new TextureRect { - Texture = rows > 2 ? _sidebarMiddleTexture : _sidebarBottomTexture, + Texture = boundingGrid.Height > 2 ? _sidebarMiddleTexture : _sidebarBottomTexture, TextureScale = new Vector2(2, 2), Children = { @@ -292,13 +293,22 @@ public sealed class StorageWindow : BaseWindow _sidebar.AddChild(backContainer); } - var fillerRows = rows - offset; + var rows = boundingGrid.Height - offset; - for (var i = 0; i < fillerRows; i++) + for (var i = 0; i < rows; i++) { _sidebar.AddChild(new TextureRect { - Texture = i != (fillerRows - 1) ? _sidebarMiddleTexture : _sidebarBottomTexture, + Texture = _sidebarMiddleTexture, + TextureScale = new Vector2(2, 2), + }); + } + + if (rows > 0) + { + _sidebar.AddChild(new TextureRect + { + Texture = _sidebarBottomTexture, TextureScale = new Vector2(2, 2), }); } diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 7bf6d74c60..f156db506a 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -1314,7 +1314,6 @@ public abstract class SharedStorageSystem : EntitySystem } Dirty(ent, ent.Comp); - UpdateUI((ent.Owner, ent.Comp)); } /// From fc80a18c35c0ffce989cd5e9dd38c0b0ba932c68 Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:13:06 +0100 Subject: [PATCH 09/11] Revert "Storage UI V2 (#33045)" This reverts commit fd25dac720b6e7e98336e63a1cc4fa7fb831aa53. --- .../Storage/StorageBoundUserInterface.cs | 65 +--- .../Storage/Systems/StorageSystem.cs | 150 ++++---- .../Systems/Hotbar/HotbarUIController.cs | 3 +- .../Systems/Hotbar/Widgets/HotbarGui.xaml | 5 +- .../Systems/Hotbar/Widgets/HotbarGui.xaml.cs | 2 +- .../Systems/Storage/Controls/ItemGridPiece.cs | 7 +- .../{StorageWindow.cs => StorageContainer.cs} | 328 ++++------------ .../Systems/Storage/StorageUIController.cs | 358 +++++++++--------- Content.Shared/CCVar/CCVars.Interactions.cs | 19 - .../EntitySystems/SharedStorageSystem.cs | 268 ++++--------- Content.Shared/Storage/StorageComponent.cs | 33 -- 11 files changed, 422 insertions(+), 816 deletions(-) rename Content.Client/UserInterface/Systems/Storage/Controls/{StorageWindow.cs => StorageContainer.cs} (64%) diff --git a/Content.Client/Storage/StorageBoundUserInterface.cs b/Content.Client/Storage/StorageBoundUserInterface.cs index bacc90eabf..b90977cbb4 100644 --- a/Content.Client/Storage/StorageBoundUserInterface.cs +++ b/Content.Client/Storage/StorageBoundUserInterface.cs @@ -1,80 +1,39 @@ -using Content.Client.UserInterface.Systems.Storage; -using Content.Client.UserInterface.Systems.Storage.Controls; +using Content.Client.Storage.Systems; using Content.Shared.Storage; using JetBrains.Annotations; -using Robust.Client.UserInterface; namespace Content.Client.Storage; [UsedImplicitly] public sealed class StorageBoundUserInterface : BoundUserInterface { - private StorageWindow? _window; + [Dependency] private readonly IEntityManager _entManager = default!; + + private readonly StorageSystem _storage; + + [Obsolete] public override bool DeferredClose => false; public StorageBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { + IoCManager.InjectDependencies(this); + _storage = _entManager.System(); } protected override void Open() { base.Open(); - _window = IoCManager.Resolve() - .GetUIController() - .CreateStorageWindow(Owner); - - if (EntMan.TryGetComponent(Owner, out StorageComponent? storage)) - { - _window.UpdateContainer((Owner, storage)); - } - - _window.OnClose += Close; - _window.FlagDirty(); - } - - public void Refresh() - { - _window?.FlagDirty(); - } - - public void Reclaim() - { - if (_window == null) - return; - - _window.OnClose -= Close; - _window.Orphan(); - _window = null; + if (_entManager.TryGetComponent(Owner, out var comp)) + _storage.OpenStorageWindow((Owner, comp)); } protected override void Dispose(bool disposing) { base.Dispose(disposing); - - Reclaim(); - } - - public void Hide() - { - if (_window == null) + if (!disposing) return; - _window.Visible = false; - } - - public void Show() - { - if (_window == null) - return; - - _window.Visible = true; - } - - public void ReOpen() - { - _window?.Orphan(); - _window = null; - Open(); + _storage.CloseStorageWindow(Owner); } } diff --git a/Content.Client/Storage/Systems/StorageSystem.cs b/Content.Client/Storage/Systems/StorageSystem.cs index ab4d9407b2..eea7b9ec79 100644 --- a/Content.Client/Storage/Systems/StorageSystem.cs +++ b/Content.Client/Storage/Systems/StorageSystem.cs @@ -4,8 +4,7 @@ using Content.Client.Animations; using Content.Shared.Hands; using Content.Shared.Storage; using Content.Shared.Storage.EntitySystems; -using Robust.Client.Player; -using Robust.Shared.GameStates; +using Robust.Shared.Collections; using Robust.Shared.Map; using Robust.Shared.Timing; @@ -14,95 +13,114 @@ namespace Content.Client.Storage.Systems; public sealed class StorageSystem : SharedStorageSystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly EntityPickupAnimationSystem _entityPickupAnimation = default!; - private Dictionary _oldStoredItems = new(); + private readonly List> _openStorages = new(); + public int OpenStorageAmount => _openStorages.Count; + + public event Action>? StorageUpdated; + public event Action?>? StorageOrderChanged; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnStorageHandleState); + SubscribeLocalEvent(OnShutdown); SubscribeNetworkEvent(HandlePickupAnimation); SubscribeAllEvent(HandleAnimatingInsertingEntities); } - private void OnStorageHandleState(EntityUid uid, StorageComponent component, ref ComponentHandleState args) + public override void UpdateUI(Entity entity) { - if (args.Current is not StorageComponentState state) - return; + if (Resolve(entity.Owner, ref entity.Comp)) + StorageUpdated?.Invoke((entity, entity.Comp)); + } - component.Grid.Clear(); - component.Grid.AddRange(state.Grid); - component.MaxItemSize = state.MaxItemSize; - component.Whitelist = state.Whitelist; - component.Blacklist = state.Blacklist; - - _oldStoredItems.Clear(); - - foreach (var item in component.StoredItems) + public void OpenStorageWindow(Entity entity) + { + if (_openStorages.Contains(entity)) { - _oldStoredItems.Add(item.Key, item.Value); - } - - component.StoredItems.Clear(); - - foreach (var (nent, location) in state.StoredItems) - { - var ent = EnsureEntity(nent, uid); - component.StoredItems[ent] = location; - } - - component.SavedLocations.Clear(); - - foreach (var loc in state.SavedLocations) - { - component.SavedLocations[loc.Key] = new(loc.Value); - } - - var uiDirty = !component.StoredItems.SequenceEqual(_oldStoredItems); - - if (uiDirty && UI.TryGetOpenUi(uid, StorageComponent.StorageUiKey.Key, out var storageBui)) - { - storageBui.Refresh(); - // Make sure nesting still updated. - var player = _player.LocalEntity; - - if (NestedStorage && player != null && ContainerSystem.TryGetContainingContainer((uid, null, null), out var container) && - UI.TryGetOpenUi(container.Owner, StorageComponent.StorageUiKey.Key, out var containerBui)) + if (_openStorages.LastOrDefault() == entity) { - containerBui.Hide(); + CloseStorageWindow((entity, entity.Comp)); } else { - storageBui.Show(); + var storages = new ValueList>(_openStorages); + var reverseStorages = storages.Reverse(); + + foreach (var storageEnt in reverseStorages) + { + if (storageEnt == entity) + break; + + CloseStorageBoundUserInterface(storageEnt.Owner); + _openStorages.Remove(entity); + } } + return; + } + + ClearNonParentStorages(entity); + _openStorages.Add(entity); + Entity? last = _openStorages.LastOrDefault(); + StorageOrderChanged?.Invoke(last); + } + + public void CloseStorageWindow(Entity entity) + { + if (!Resolve(entity, ref entity.Comp, false)) + return; + + if (!_openStorages.Contains((entity, entity.Comp))) + return; + + var storages = new ValueList>(_openStorages); + var reverseStorages = storages.Reverse(); + + foreach (var storage in reverseStorages) + { + CloseStorageBoundUserInterface(storage.Owner); + _openStorages.Remove(storage); + if (storage.Owner == entity.Owner) + break; + } + + Entity? last = null; + if (_openStorages.Any()) + last = _openStorages.LastOrDefault(); + StorageOrderChanged?.Invoke(last); + } + + private void ClearNonParentStorages(EntityUid uid) + { + var storages = new ValueList>(_openStorages); + var reverseStorages = storages.Reverse(); + + foreach (var storage in reverseStorages) + { + if (storage.Comp.Container.Contains(uid)) + break; + + CloseStorageBoundUserInterface(storage.Owner); + _openStorages.Remove(storage); } } - public override void UpdateUI(Entity entity) + private void CloseStorageBoundUserInterface(Entity entity) { - if (UI.TryGetOpenUi(entity.Owner, StorageComponent.StorageUiKey.Key, out var sBui)) - { - sBui.Refresh(); - } + if (!Resolve(entity, ref entity.Comp, false)) + return; + + if (entity.Comp.ClientOpenInterfaces.GetValueOrDefault(StorageComponent.StorageUiKey.Key) is not { } bui) + return; + + bui.Close(); } - protected override void HideStorageWindow(EntityUid uid, EntityUid actor) + private void OnShutdown(Entity ent, ref ComponentShutdown args) { - if (UI.TryGetOpenUi(uid, StorageComponent.StorageUiKey.Key, out var storageBui)) - { - storageBui.Hide(); - } - } - - protected override void ShowStorageWindow(EntityUid uid, EntityUid actor) - { - if (UI.TryGetOpenUi(uid, StorageComponent.StorageUiKey.Key, out var storageBui)) - { - storageBui.Show(); - } + CloseStorageWindow((ent, ent.Comp)); } /// @@ -124,7 +142,7 @@ public sealed class StorageSystem : SharedStorageSystem { if (!_timing.IsFirstTimePredicted) return; - + if (TransformSystem.InRange(finalCoords, initialCoords, 0.1f) || !Exists(initialCoords.EntityId) || !Exists(finalCoords.EntityId)) { diff --git a/Content.Client/UserInterface/Systems/Hotbar/HotbarUIController.cs b/Content.Client/UserInterface/Systems/Hotbar/HotbarUIController.cs index b89115da86..2f266cfdd6 100644 --- a/Content.Client/UserInterface/Systems/Hotbar/HotbarUIController.cs +++ b/Content.Client/UserInterface/Systems/Hotbar/HotbarUIController.cs @@ -31,12 +31,13 @@ public sealed class HotbarUIController : UIController ReloadHotbar(); } - public void Setup(HandsContainer handsContainer) + public void Setup(HandsContainer handsContainer, StorageContainer storageContainer) { _inventory = UIManager.GetUIController(); _hands = UIManager.GetUIController(); _storage = UIManager.GetUIController(); _hands.RegisterHandContainer(handsContainer); + _storage.RegisterStorageContainer(storageContainer); } public void ReloadHotbar() diff --git a/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml b/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml index 153e02bd55..00ba1878b4 100644 --- a/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml +++ b/Content.Client/UserInterface/Systems/Hotbar/Widgets/HotbarGui.xaml @@ -1,6 +1,7 @@  + (); - hotbarController.Setup(HandContainer); + hotbarController.Setup(HandContainer, StoragePanel); LayoutContainer.SetGrowVertical(this, LayoutContainer.GrowDirection.Begin); } diff --git a/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs b/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs index f4c4158b5c..dd9986e4c6 100644 --- a/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs +++ b/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs @@ -59,7 +59,7 @@ public sealed class ItemGridPiece : Control, IEntityControl Location = location; Visible = true; - MouseFilter = MouseFilterMode.Stop; + MouseFilter = MouseFilterMode.Pass; TooltipSupplier = SupplyTooltip; @@ -105,11 +105,8 @@ public sealed class ItemGridPiece : Control, IEntityControl return; } - if (_storageController.IsDragging && _storageController.DraggingGhost?.Entity == Entity && - _storageController.DraggingGhost != this) - { + if (_storageController.IsDragging && _storageController.DraggingGhost?.Entity == Entity && _storageController.DraggingGhost != this) return; - } var adjustedShape = _entityManager.System().GetAdjustedItemShape((Entity, itemComponent), Location.Rotation, Vector2i.Zero); var boundingGrid = adjustedShape.GetBoundingBox(); diff --git a/Content.Client/UserInterface/Systems/Storage/Controls/StorageWindow.cs b/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs similarity index 64% rename from Content.Client/UserInterface/Systems/Storage/Controls/StorageWindow.cs rename to Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs index 4527ffd59a..a9d7e09826 100644 --- a/Content.Client/UserInterface/Systems/Storage/Controls/StorageWindow.cs +++ b/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs @@ -3,9 +3,7 @@ using System.Linq; using System.Numerics; using Content.Client.Hands.Systems; using Content.Client.Items.Systems; -using Content.Client.Storage; using Content.Client.Storage.Systems; -using Content.Shared.IdentityManagement; using Content.Shared.Input; using Content.Shared.Item; using Content.Shared.Storage; @@ -13,14 +11,12 @@ using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; -using Robust.Shared.Collections; -using Robust.Shared.Containers; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Client.UserInterface.Systems.Storage.Controls; -public sealed class StorageWindow : BaseWindow +public sealed class StorageContainer : BaseWindow { [Dependency] private readonly IEntityManager _entity = default!; private readonly StorageUIController _storageController; @@ -31,20 +27,6 @@ public sealed class StorageWindow : BaseWindow private readonly GridContainer _backgroundGrid; private readonly GridContainer _sidebar; - private Control _titleContainer; - private Label _titleLabel; - - // Needs to be nullable in case a piece is in default spot. - private readonly Dictionary _pieces = new(); - private readonly List _controlGrid = new(); - - private ValueList _contained = new(); - private ValueList _toRemove = new(); - - private TextureButton? _backButton; - - private bool _isDirty; - public event Action? OnPiecePressed; public event Action? OnPieceUnpressed; @@ -69,10 +51,9 @@ public sealed class StorageWindow : BaseWindow private readonly string _sidebarFatTexturePath = "Storage/sidebar_fat"; private Texture? _sidebarFatTexture; - public StorageWindow() + public StorageContainer() { IoCManager.InjectDependencies(this); - Resizable = false; _storageController = UserInterfaceManager.GetUIController(); @@ -82,7 +63,6 @@ public sealed class StorageWindow : BaseWindow _sidebar = new GridContainer { - Name = "SideBar", HSeparationOverride = 0, VSeparationOverride = 0, Columns = 1 @@ -90,48 +70,21 @@ public sealed class StorageWindow : BaseWindow _pieceGrid = new GridContainer { - Name = "PieceGrid", HSeparationOverride = 0, VSeparationOverride = 0 }; _backgroundGrid = new GridContainer { - Name = "BackgroundGrid", HSeparationOverride = 0, VSeparationOverride = 0 }; - _titleLabel = new Label() - { - HorizontalExpand = true, - Name = "StorageLabel", - ClipText = true, - Text = "Dummy", - StyleClasses = - { - "FancyWindowTitle", - } - }; - - _titleContainer = new PanelContainer() - { - StyleClasses = - { - "WindowHeadingBackground" - }, - Children = - { - _titleLabel - } - }; - var container = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Vertical, Children = { - _titleContainer, new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal, @@ -177,22 +130,12 @@ public sealed class StorageWindow : BaseWindow if (entity == null) return; - if (UserInterfaceManager.GetUIController().WindowTitle) - { - _titleLabel.Text = Identity.Name(entity.Value, _entity); - _titleContainer.Visible = true; - } - else - { - _titleContainer.Visible = false; - } - BuildGridRepresentation(); } private void BuildGridRepresentation() { - if (!_entity.TryGetComponent(StorageEntity, out var comp) || comp.Grid.Count == 0) + if (!_entity.TryGetComponent(StorageEntity, out var comp) || !comp.Grid.Any()) return; var boundingGrid = comp.Grid.GetBoundingBox(); @@ -202,11 +145,11 @@ public sealed class StorageWindow : BaseWindow #region Sidebar _sidebar.Children.Clear(); _sidebar.Rows = boundingGrid.Height + 1; - var exitButton = new TextureButton { - Name = "ExitButton", - TextureNormal = _exitTexture, + TextureNormal = _entity.System().OpenStorageAmount == 1 + ?_exitTexture + : _backTexture, Scale = new Vector2(2, 2), }; exitButton.OnPressed += _ => @@ -222,10 +165,8 @@ public sealed class StorageWindow : BaseWindow args.Handle(); } }; - var exitContainer = new BoxContainer { - Name = "ExitContainer", Children = { new TextureRect @@ -241,61 +182,8 @@ public sealed class StorageWindow : BaseWindow } } }; - _sidebar.AddChild(exitContainer); - var offset = 1; - - if (_entity.System().NestedStorage && boundingGrid.Height > 0) - { - offset += 1; - - _backButton = new TextureButton - { - TextureNormal = _backTexture, - Scale = new Vector2(2, 2), - }; - _backButton.OnPressed += _ => - { - var containerSystem = _entity.System(); - - if (containerSystem.TryGetContainingContainer(StorageEntity.Value, out var container) && - _entity.TryGetComponent(container.Owner, out StorageComponent? storage)) - { - Close(); - - if (_entity.System() - .TryGetOpenUi(container.Owner, - StorageComponent.StorageUiKey.Key, - out var parentBui)) - { - parentBui.Show(); - } - } - }; - - var backContainer = new BoxContainer - { - Name = "ExitContainer", - Children = - { - new TextureRect - { - Texture = boundingGrid.Height > 2 ? _sidebarMiddleTexture : _sidebarBottomTexture, - TextureScale = new Vector2(2, 2), - Children = - { - _backButton, - } - } - } - }; - - _sidebar.AddChild(backContainer); - } - - var rows = boundingGrid.Height - offset; - - for (var i = 0; i < rows; i++) + for (var i = 0; i < boundingGrid.Height - 1; i++) { _sidebar.AddChild(new TextureRect { @@ -304,7 +192,7 @@ public sealed class StorageWindow : BaseWindow }); } - if (rows > 0) + if (boundingGrid.Height > 0) { _sidebar.AddChild(new TextureRect { @@ -315,7 +203,7 @@ public sealed class StorageWindow : BaseWindow #endregion - FlagDirty(); + BuildItemPieces(); } public void BuildBackground() @@ -352,127 +240,70 @@ public sealed class StorageWindow : BaseWindow } } - public void Reclaim(ItemStorageLocation location, ItemGridPiece draggingGhost) - { - draggingGhost.OnPiecePressed += OnPiecePressed; - draggingGhost.OnPieceUnpressed += OnPieceUnpressed; - _pieces[draggingGhost.Entity] = (location, draggingGhost); - draggingGhost.Location = location; - var controlIndex = GetGridIndex(draggingGhost); - _controlGrid[controlIndex].AddChild(draggingGhost); - } - - private int GetGridIndex(ItemGridPiece piece) - { - return piece.Location.Position.X + piece.Location.Position.Y * _pieceGrid.Columns; - } - - public void FlagDirty() - { - _isDirty = true; - } - - public void RemoveGrid(ItemGridPiece control) - { - control.Orphan(); - _pieces.Remove(control.Entity); - control.OnPiecePressed -= OnPiecePressed; - control.OnPieceUnpressed -= OnPieceUnpressed; - } - public void BuildItemPieces() { if (!_entity.TryGetComponent(StorageEntity, out var storageComp)) return; - if (storageComp.Grid.Count == 0) + if (!storageComp.Grid.Any()) return; var boundingGrid = storageComp.Grid.GetBoundingBox(); var size = _emptyTexture!.Size * 2; - _contained.Clear(); - _contained.AddRange(storageComp.Container.ContainedEntities.Reverse()); + var containedEntities = storageComp.Container.ContainedEntities.Reverse().ToArray(); - // Build the grid representation - if (_pieceGrid.Rows - 1 != boundingGrid.Height || _pieceGrid.Columns - 1 != boundingGrid.Width) + //todo. at some point, we may want to only rebuild the pieces that have actually received new data. + + _pieceGrid.RemoveAllChildren(); + _pieceGrid.Rows = boundingGrid.Height + 1; + _pieceGrid.Columns = boundingGrid.Width + 1; + for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++) { - _pieceGrid.Rows = boundingGrid.Height + 1; - _pieceGrid.Columns = boundingGrid.Width + 1; - _controlGrid.Clear(); - - for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++) + for (var x = boundingGrid.Left; x <= boundingGrid.Right; x++) { - for (var x = boundingGrid.Left; x <= boundingGrid.Right; x++) + var control = new Control { - var control = new Control - { - MinSize = size - }; - - _controlGrid.Add(control); - _pieceGrid.AddChild(control); - } - } - } - - _toRemove.Clear(); - - // Remove entities no longer relevant / Update existing ones - foreach (var (ent, data) in _pieces) - { - if (storageComp.StoredItems.TryGetValue(ent, out var updated)) - { - if (data.Loc.Equals(updated)) - { - DebugTools.Assert(data.Control.Location == updated); - continue; - } - - // Update - data.Control.Location = updated; - var index = GetGridIndex(data.Control); - data.Control.Orphan(); - _controlGrid[index].AddChild(data.Control); - _pieces[ent] = (updated, data.Control); - continue; - } - - _toRemove.Add(ent); - } - - foreach (var ent in _toRemove) - { - _pieces.Remove(ent, out var data); - data.Control.Orphan(); - } - - // Add new ones - foreach (var (ent, loc) in storageComp.StoredItems) - { - if (_pieces.TryGetValue(ent, out var existing)) - { - DebugTools.Assert(existing.Loc == loc); - continue; - } - - if (_entity.TryGetComponent(ent, out var itemEntComponent)) - { - var gridPiece = new ItemGridPiece((ent, itemEntComponent), loc, _entity) - { - MinSize = size, - Marked = _contained.IndexOf(ent) switch - { - 0 => ItemGridPieceMarks.First, - 1 => ItemGridPieceMarks.Second, - _ => null, - } + MinSize = size }; - gridPiece.OnPiecePressed += OnPiecePressed; - gridPiece.OnPieceUnpressed += OnPieceUnpressed; - var controlIndex = loc.Position.X + loc.Position.Y * (boundingGrid.Width + 1); - _controlGrid[controlIndex].AddChild(gridPiece); - _pieces[ent] = (loc, gridPiece); + var currentPosition = new Vector2i(x, y); + + foreach (var (itemEnt, itemPos) in storageComp.StoredItems) + { + if (itemPos.Position != currentPosition) + continue; + + if (_entity.TryGetComponent(itemEnt, out var itemEntComponent)) + { + ItemGridPiece gridPiece; + + if (_storageController.CurrentlyDragging?.Entity is { } dragging + && dragging == itemEnt) + { + _storageController.CurrentlyDragging.Orphan(); + gridPiece = _storageController.CurrentlyDragging; + } + else + { + gridPiece = new ItemGridPiece((itemEnt, itemEntComponent), itemPos, _entity) + { + MinSize = size, + Marked = Array.IndexOf(containedEntities, itemEnt) switch + { + 0 => ItemGridPieceMarks.First, + 1 => ItemGridPieceMarks.Second, + _ => null, + } + }; + gridPiece.OnPiecePressed += OnPiecePressed; + gridPiece.OnPieceUnpressed += OnPieceUnpressed; + } + + control.AddChild(gridPiece); + } + } + + _pieceGrid.AddChild(control); } } } @@ -484,35 +315,6 @@ public sealed class StorageWindow : BaseWindow if (!IsOpen) return; - if (_isDirty) - { - _isDirty = false; - BuildItemPieces(); - } - - var containerSystem = _entity.System(); - - if (_backButton != null) - { - if (StorageEntity != null && _entity.System().NestedStorage) - { - if (containerSystem.TryGetContainingContainer(StorageEntity.Value, out var container) && - _entity.HasComponent(container.Owner)) - { - _backButton.Visible = true; - } - else - { - _backButton.Visible = false; - } - } - // Hide the button. - else - { - _backButton.Visible = false; - } - } - var itemSystem = _entity.System(); var storageSystem = _entity.System(); var handsSystem = _entity.System(); @@ -522,7 +324,7 @@ public sealed class StorageWindow : BaseWindow child.ModulateSelfOverride = Color.FromHex("#222222"); } - if (UserInterfaceManager.CurrentlyHovered is StorageWindow con && con != this) + if (UserInterfaceManager.CurrentlyHovered is StorageContainer con && con != this) return; if (!_entity.TryGetComponent(StorageEntity, out var storageComponent)) @@ -571,7 +373,7 @@ public sealed class StorageWindow : BaseWindow continue; float spot = 0; - var marked = new ValueList(); + var marked = new List(); foreach (var location in locations.Value) { @@ -698,4 +500,14 @@ public sealed class StorageWindow : BaseWindow } } } + + public override void Close() + { + base.Close(); + + if (StorageEntity == null) + return; + + _entity.System().CloseStorageWindow(StorageEntity.Value); + } } diff --git a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs index cbcf4293e8..1e61ad9838 100644 --- a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs +++ b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs @@ -2,7 +2,6 @@ using System.Numerics; using Content.Client.Examine; using Content.Client.Hands.Systems; using Content.Client.Interaction; -using Content.Client.Storage; using Content.Client.Storage.Systems; using Content.Client.UserInterface.Systems.Hotbar.Widgets; using Content.Client.UserInterface.Systems.Storage.Controls; @@ -10,9 +9,9 @@ using Content.Client.Verbs.UI; using Content.Shared.CCVar; using Content.Shared.Input; using Content.Shared.Interaction; +using Content.Shared.Item; using Content.Shared.Storage; using Robust.Client.Input; -using Robust.Client.Player; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controllers; using Robust.Client.UserInterface.Controls; @@ -24,28 +23,19 @@ namespace Content.Client.UserInterface.Systems.Storage; public sealed class StorageUIController : UIController, IOnSystemChanged { - /* - * Things are a bit over the shop but essentially - * - Clicking into storagewindow is handled via storagewindow - * - Clicking out of it is via ItemGridPiece - * - Dragging around is handled here - * - Drawing is handled via ItemGridPiece - * - StorageSystem handles any sim stuff around open windows. - */ - [Dependency] private readonly IConfigurationManager _configuration = default!; + [Dependency] private readonly IEntityManager _entity = default!; [Dependency] private readonly IInputManager _input = default!; - [Dependency] private readonly IPlayerManager _player = default!; - [UISystemDependency] private readonly StorageSystem _storage = default!; - - /// - /// Cached positions for opening nested storage. - /// - private readonly Dictionary _reservedStorage = new(); + [Dependency] private readonly IUserInterfaceManager _ui = default!; private readonly DragDropHelper _menuDragHelper; + private StorageContainer? _container; - public ItemGridPiece? DraggingGhost => _menuDragHelper.Dragged; + private Vector2? _lastContainerPosition; + + private HotbarGui? Hotbar => UIManager.GetActiveUIWidgetOrNull(); + + public ItemGridPiece? DraggingGhost; public Angle DraggingRotation = Angle.Zero; public bool StaticStorageUIEnabled; public bool OpaqueStorageWindow; @@ -53,8 +43,6 @@ public sealed class StorageUIController : UIController, IOnSystemChanged _menuDragHelper.IsDragging; public ItemGridPiece? CurrentlyDragging => _menuDragHelper.Dragged; - public bool WindowTitle { get; private set; } = false; - public StorageUIController() { _menuDragHelper = new DragDropHelper(OnMenuBeginDrag, OnMenuContinueDrag, OnMenuEndDrag); @@ -64,93 +52,106 @@ public sealed class StorageUIController : UIController, IOnSystemChanged(); - - foreach (var bui in uiSystem.GetActorUis((_player.LocalEntity.Value, userComp))) - { - if (!uiSystem.TryGetOpenUi(bui.Entity, StorageComponent.StorageUiKey.Key, out var storageBui)) - continue; - - storageBui.ReOpen(); - } - } - - private void OnStorageWindowTitle(bool obj) - { - WindowTitle = obj; - } - - private void OnOpaqueWindowChanged(bool obj) - { - OpaqueStorageWindow = obj; - } - - private void OnStaticStorageChanged(bool obj) - { - StaticStorageUIEnabled = obj; - } - - public StorageWindow CreateStorageWindow(EntityUid uid) - { - var window = new StorageWindow(); - window.MouseFilter = Control.MouseFilterMode.Pass; - - window.OnPiecePressed += (args, piece) => - { - OnPiecePressed(args, window, piece); - }; - window.OnPieceUnpressed += (args, piece) => - { - OnPieceUnpressed(args, window, piece); - }; - - if (StaticStorageUIEnabled) - { - UIManager.GetActiveUIWidgetOrNull()?.StorageContainer.AddChild(window); - } - else - { - window.OpenCenteredLeft(); - - if (_reservedStorage.Remove(uid, out var pos)) - { - LayoutContainer.SetPosition(window, pos); - } - } - - return window; } public void OnSystemLoaded(StorageSystem system) { _input.FirstChanceOnKeyEvent += OnMiddleMouse; + system.StorageUpdated += OnStorageUpdated; + system.StorageOrderChanged += OnStorageOrderChanged; } public void OnSystemUnloaded(StorageSystem system) { _input.FirstChanceOnKeyEvent -= OnMiddleMouse; + system.StorageUpdated -= OnStorageUpdated; + system.StorageOrderChanged -= OnStorageOrderChanged; + } + + private void OnStorageOrderChanged(Entity? nullEnt) + { + if (_container == null) + return; + + if (IsDragging) + _menuDragHelper.EndDrag(); + + _container.UpdateContainer(nullEnt); + + if (nullEnt is not null) + { + // center it if we knock it off screen somehow. + if (!StaticStorageUIEnabled && + (_lastContainerPosition == null || + _lastContainerPosition.Value.X < 0 || + _lastContainerPosition.Value.Y < 0 || + _lastContainerPosition.Value.X > _ui.WindowRoot.Width || + _lastContainerPosition.Value.Y > _ui.WindowRoot.Height)) + { + _container.OpenCenteredAt(new Vector2(0.5f, 0.75f)); + } + else + { + _container.Open(); + + var pos = !StaticStorageUIEnabled && _lastContainerPosition != null + ? _lastContainerPosition.Value + : Vector2.Zero; + + LayoutContainer.SetPosition(_container, pos); + } + + if (StaticStorageUIEnabled) + { + // we have to orphan it here because Open() sets the parent. + _container.Orphan(); + Hotbar?.StorageContainer.AddChild(_container); + } + _lastContainerPosition = _container.GlobalPosition; + } + else + { + _lastContainerPosition = _container.GlobalPosition; + _container.Close(); + } + } + + private void OnStaticStorageChanged(bool obj) + { + if (StaticStorageUIEnabled == obj) + return; + + StaticStorageUIEnabled = obj; + _lastContainerPosition = null; + + if (_container == null) + return; + + if (!_container.IsOpen) + return; + + _container.Orphan(); + if (StaticStorageUIEnabled) + { + Hotbar?.StorageContainer.AddChild(_container); + } + else + { + _ui.WindowRoot.AddChild(_container); + } + + if (_entity.TryGetComponent(_container.StorageEntity, out var comp)) + OnStorageOrderChanged((_container.StorageEntity.Value, comp)); + } + + private void OnOpaqueWindowChanged(bool obj) + { + if (OpaqueStorageWindow == obj) + return; + OpaqueStorageWindow = obj; + _container?.BuildBackground(); } /// One might ask, Hey Emo, why are you parsing raw keyboard input just to rotate a rectangle? @@ -189,7 +190,7 @@ public sealed class StorageUIController : UIController, IOnSystemChanged().GetActiveHandEntity() == null) + if (!IsDragging && _entity.System().GetActiveHandEntity() == null) return; //clamp it to a cardinal. @@ -197,18 +198,43 @@ public sealed class StorageUIController : UIController, IOnSystemChanged uid) { - if (IsDragging || !window.IsOpen) + if (_container?.StorageEntity != uid) + return; + + _container.BuildItemPieces(); + } + + public void RegisterStorageContainer(StorageContainer container) + { + if (_container != null) + { + container.OnPiecePressed -= OnPiecePressed; + container.OnPieceUnpressed -= OnPieceUnpressed; + } + + _container = container; + container.OnPiecePressed += OnPiecePressed; + container.OnPieceUnpressed += OnPieceUnpressed; + + if (!StaticStorageUIEnabled) + _container.Orphan(); + } + + private void OnPiecePressed(GUIBoundKeyEventArgs args, ItemGridPiece control) + { + if (IsDragging || !_container?.IsOpen == true) return; if (args.Function == ContentKeyFunctions.MoveStoredItem) { DraggingRotation = control.Location.Rotation; + _menuDragHelper.MouseDown(control); _menuDragHelper.Update(0f); @@ -216,17 +242,17 @@ public sealed class StorageUIController : UIController, IOnSystemChanged().DoExamine(control.Entity); + _entity.System().DoExamine(control.Entity); args.Handle(); } else if (args.Function == EngineKeyFunctions.UseSecondary) @@ -236,102 +262,62 @@ public sealed class StorageUIController : UIController, IOnSystemChanged(storageEnt, out var storageComp)) return; - } - if (_menuDragHelper.IsDragging && DraggingGhost is { } draggingGhost) + if (DraggingGhost is { } draggingGhost) { var dragEnt = draggingGhost.Entity; var dragLoc = draggingGhost.Location; + var itemSys = _entity.System(); - // Dragging in the same storage - // The existing ItemGridPiece just stops rendering but still exists so check if it's hovered. - if (targetStorage == window) + var position = _container.GetMouseGridPieceLocation(dragEnt, dragLoc); + var itemBounding = itemSys.GetAdjustedItemShape(dragEnt, dragLoc).GetBoundingBox(); + var gridBounding = storageComp.Grid.GetBoundingBox(); + + // The extended bounding box for if this is out of the window is the grid bounding box dimensions combined + // with the item shape bounding box dimensions. Plus 1 on the left for the sidebar. This makes it so that. + // dropping an item on the floor requires dragging it all the way out of the window. + var left = gridBounding.Left - itemBounding.Width - 1; + var bottom = gridBounding.Bottom - itemBounding.Height; + var top = gridBounding.Top; + var right = gridBounding.Right; + var lenientBounding = new Box2i(left, bottom, right, top); + + if (lenientBounding.Contains(position)) { - var position = targetStorage.GetMouseGridPieceLocation(dragEnt, dragLoc); - var newLocation = new ItemStorageLocation(DraggingRotation, position); - - EntityManager.RaisePredictiveEvent(new StorageSetItemLocationEvent( - EntityManager.GetNetEntity(draggingGhost.Entity), - EntityManager.GetNetEntity(sourceStorage), - newLocation)); - - window.Reclaim(newLocation, control); - } - // Dragging to new storage - else if (targetStorage?.StorageEntity != null && targetStorage != window) - { - var position = targetStorage.GetMouseGridPieceLocation(dragEnt, dragLoc); - var newLocation = new ItemStorageLocation(DraggingRotation, position); - - // Check it fits and we can move to hand (no free transfers). - if (_storage.ItemFitsInGridLocation( - (dragEnt, null), - (targetStorage.StorageEntity.Value, null), - newLocation)) - { - // Can drop and move. - EntityManager.RaisePredictiveEvent(new StorageTransferItemEvent( - EntityManager.GetNetEntity(dragEnt), - EntityManager.GetNetEntity(targetStorage.StorageEntity.Value), - newLocation)); - - targetStorage.Reclaim(newLocation, control); - DraggingRotation = Angle.Zero; - } - else - { - // Cancel it (rather than dropping). - window.Reclaim(dragLoc, control); - } + _entity.RaisePredictiveEvent(new StorageSetItemLocationEvent( + _entity.GetNetEntity(draggingGhost.Entity), + _entity.GetNetEntity(storageEnt), + new ItemStorageLocation(DraggingRotation, position))); } - targetStorage?.FlagDirty(); + _menuDragHelper.EndDrag(); + _container?.BuildItemPieces(); } - // If we just clicked, then take it out of the bag. - else + else //if we just clicked, then take it out of the bag. { - EntityManager.RaisePredictiveEvent(new StorageInteractWithItemEvent( - EntityManager.GetNetEntity(control.Entity), - EntityManager.GetNetEntity(sourceStorage))); + _menuDragHelper.EndDrag(); + _entity.RaisePredictiveEvent(new StorageInteractWithItemEvent( + _entity.GetNetEntity(control.Entity), + _entity.GetNetEntity(storageEnt))); } - - _menuDragHelper.EndDrag(); args.Handle(); } @@ -340,8 +326,14 @@ public sealed class StorageUIController : UIController, IOnSystemChanged(dragged.Entity)), + dragged.Location, + _entity); + DraggingGhost.MouseFilter = Control.MouseFilterMode.Ignore; + DraggingGhost.Visible = true; + DraggingGhost.Orphan(); UIManager.PopupRoot.AddChild(DraggingGhost); SetDraggingRotation(); @@ -352,7 +344,6 @@ public sealed class StorageUIController : UIController, IOnSystemChanged public static readonly CVarDef OpaqueStorageWindow = CVarDef.Create("control.opaque_storage_background", false, CVar.CLIENTONLY | CVar.ARCHIVE); - - /// - /// Whether or not the storage window has a title of the entity name. - /// - public static readonly CVarDef StorageWindowTitle = - CVarDef.Create("control.storage_window_title", false, CVar.CLIENTONLY | CVar.ARCHIVE); - - /// - /// How many storage windows are allowed to be open at once. - /// Recommended that you utilise this in conjunction with - /// - public static readonly CVarDef StorageLimit = - CVarDef.Create("control.storage_limit", 1, CVar.REPLICATED | CVar.SERVER); - - /// - /// Whether or not storage can be opened recursively. - /// - public static readonly CVarDef NestedStorage = - CVarDef.Create("control.nested_storage", true, CVar.REPLICATED | CVar.SERVER); } diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index f156db506a..123282fbdb 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; -using Content.Shared.CCVar; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; using Content.Shared.Destructible; @@ -28,7 +27,6 @@ using Content.Shared.Verbs; using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; -using Robust.Shared.Configuration; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Input.Binding; @@ -37,46 +35,36 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization; -using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Shared.Storage.EntitySystems; public abstract class SharedStorageSystem : EntitySystem { - [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] protected readonly IGameTiming Timing = default!; - [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] protected readonly IRobustRandom Random = default!; - [Dependency] private readonly ISharedAdminLogManager _adminLog = default!; - [Dependency] protected readonly ActionBlockerSystem ActionBlocker = default!; - [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; - [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] protected readonly SharedEntityStorageSystem EntityStorage = default!; - [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] protected readonly SharedItemSystem ItemSystem = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - [Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!; - [Dependency] private readonly SharedStackSystem _stack = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!; + [Dependency] private readonly SharedStackSystem _stack = default!; [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; - [Dependency] protected readonly SharedUserInterfaceSystem UI = default!; + [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; [Dependency] protected readonly UseDelaySystem UseDelay = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLog = default!; private EntityQuery _itemQuery; private EntityQuery _stackQuery; private EntityQuery _xformQuery; - private EntityQuery _userQuery; - - /// - /// Whether we're allowed to go up-down storage via UI. - /// - public bool NestedStorage = true; [ValidatePrototypeId] public const string DefaultStorageMaxItemSize = "Normal"; @@ -88,15 +76,10 @@ public abstract class SharedStorageSystem : EntitySystem private ItemSizePrototype _defaultStorageMaxItemSize = default!; - /// - /// Flag for whether we're checking for nested storage interactions. - /// - private bool _nestedCheck; - public bool CheckingCanInsert; - private readonly List _entList = new(); - private readonly HashSet _entSet = new(); + private List _entList = new(); + private HashSet _entSet = new(); private readonly List _sortedSizes = new(); private FrozenDictionary _nextSmallest = FrozenDictionary.Empty; @@ -104,11 +87,6 @@ public abstract class SharedStorageSystem : EntitySystem private const string QuickInsertUseDelayID = "quickInsert"; private const string OpenUiUseDelayID = "storage"; - /// - /// How many storage windows are allowed to be open at once. - /// - private int _openStorageLimit = -1; - protected readonly List CantFillReasons = []; /// @@ -119,11 +97,8 @@ public abstract class SharedStorageSystem : EntitySystem _itemQuery = GetEntityQuery(); _stackQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); - _userQuery = GetEntityQuery(); _prototype.PrototypesReloaded += OnPrototypesReloaded; - Subs.CVar(_cfg, CCVars.StorageLimit, OnStorageLimitChanged, true); - Subs.BuiEvents(StorageComponent.StorageUiKey.Key, subs => { subs.Event(OnBoundUIClosed); @@ -133,6 +108,7 @@ public abstract class SharedStorageSystem : EntitySystem SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent>(AddUiVerb); SubscribeLocalEvent(OnStorageGetState); + SubscribeLocalEvent(OnStorageHandleState); SubscribeLocalEvent(OnComponentInit, before: new[] { typeof(SharedContainerSystem) }); SubscribeLocalEvent>(AddTransferVerbs); SubscribeLocalEvent(OnInteractUsing, after: new[] { typeof(ItemSlotsSystem) }); @@ -140,7 +116,6 @@ public abstract class SharedStorageSystem : EntitySystem SubscribeLocalEvent(OnImplantActivate); SubscribeLocalEvent(AfterInteract); SubscribeLocalEvent(OnDestroy); - SubscribeLocalEvent(OnBoundUIAttempt); SubscribeLocalEvent(OnBoundUIOpen); SubscribeLocalEvent(OnLockToggled); SubscribeLocalEvent(OnStackCountChanged); @@ -151,8 +126,6 @@ public abstract class SharedStorageSystem : EntitySystem SubscribeLocalEvent(OnDoAfter); - SubscribeAllEvent(OnStorageNested); - SubscribeAllEvent(OnStorageTransfer); SubscribeAllEvent(OnInteractWithItem); SubscribeAllEvent(OnSetItemLocation); SubscribeAllEvent(OnInsertItemIntoLocation); @@ -165,24 +138,12 @@ public abstract class SharedStorageSystem : EntitySystem .Bind(ContentKeyFunctions.OpenBelt, InputCmdHandler.FromDelegate(HandleOpenBelt, handle: false)) .Register(); - Subs.CVar(_cfg, CCVars.NestedStorage, OnNestedStorageCvar, true); - UpdatePrototypeCache(); } - private void OnNestedStorageCvar(bool obj) - { - NestedStorage = obj; - } - - private void OnStorageLimitChanged(int obj) - { - _openStorageLimit = obj; - } - private void OnRemove(Entity entity, ref ComponentRemove args) { - UI.CloseUi(entity.Owner, StorageComponent.StorageUiKey.Key); + _ui.CloseUi(entity.Owner, StorageComponent.StorageUiKey.Key); } private void OnMapInit(Entity entity, ref MapInitEvent args) @@ -211,6 +172,28 @@ public abstract class SharedStorageSystem : EntitySystem }; } + private void OnStorageHandleState(EntityUid uid, StorageComponent component, ref ComponentHandleState args) + { + if (args.Current is not StorageComponentState state) + return; + + component.Grid.Clear(); + component.Grid.AddRange(state.Grid); + component.MaxItemSize = state.MaxItemSize; + component.Whitelist = state.Whitelist; + component.Blacklist = state.Blacklist; + + component.StoredItems.Clear(); + + foreach (var (nent, location) in state.StoredItems) + { + var ent = EnsureEntity(nent, uid); + component.StoredItems[ent] = location; + } + + component.SavedLocations = state.SavedLocations; + } + public override void Shutdown() { _prototype.PrototypesReloaded -= OnPrototypesReloaded; @@ -245,7 +228,7 @@ public abstract class SharedStorageSystem : EntitySystem private void OnComponentInit(EntityUid uid, StorageComponent storageComp, ComponentInit args) { - storageComp.Container = ContainerSystem.EnsureContainer(uid, StorageComponent.ContainerId); + storageComp.Container = _containerSystem.EnsureContainer(uid, StorageComponent.ContainerId); UpdateAppearance((uid, storageComp, null)); } @@ -264,7 +247,7 @@ public abstract class SharedStorageSystem : EntitySystem // close ui foreach (var entity in storageComp.Container.ContainedEntities) { - UI.CloseUis(entity, actor); + _ui.CloseUis(entity, actor); } } @@ -273,7 +256,7 @@ public abstract class SharedStorageSystem : EntitySystem CloseNestedInterfaces(uid, args.Actor, storageComp); // If UI is closed for everyone - if (!UI.IsUiOpen(uid, args.UiKey)) + if (!_ui.IsUiOpen(uid, args.UiKey)) { UpdateAppearance((uid, storageComp, null)); Audio.PlayPredicted(storageComp.StorageCloseSound, uid, args.Actor); @@ -286,7 +269,7 @@ public abstract class SharedStorageSystem : EntitySystem return; // Does this player currently have the storage UI open? - var uiOpen = UI.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.User); + var uiOpen = _ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.User); ActivationVerb verb = new() { @@ -294,7 +277,7 @@ public abstract class SharedStorageSystem : EntitySystem { if (uiOpen) { - UI.CloseUi(uid, StorageComponent.StorageUiKey.Key, args.User); + _ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, args.User); } else { @@ -332,16 +315,16 @@ public abstract class SharedStorageSystem : EntitySystem if (!CanInteract(entity, (uid, storageComp), silent: silent)) return; - if (!UI.TryOpenUi(uid, StorageComponent.StorageUiKey.Key, entity)) - return; - if (!silent) { - Audio.PlayPredicted(storageComp.StorageOpenSound, uid, entity); + if (!_ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key)) + Audio.PlayPredicted(storageComp.StorageOpenSound, uid, entity); if (useDelay != null) UseDelay.TryResetDelay((uid, useDelay), id: OpenUiUseDelayID); } + + _ui.OpenUi(uid, StorageComponent.StorageUiKey.Key, entity); } public virtual void UpdateUI(Entity entity) {} @@ -401,43 +384,18 @@ public abstract class SharedStorageSystem : EntitySystem return; // Toggle - if (UI.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.User)) + if (_ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.User)) { - UI.CloseUi(uid, StorageComponent.StorageUiKey.Key, args.User); + _ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, args.User); } else { - // Handle recursively opening nested storages. - if (ContainerSystem.TryGetContainingContainer((args.Target, null, null), out var container) && - UI.IsUiOpen(container.Owner, StorageComponent.StorageUiKey.Key, args.User)) - { - _nestedCheck = true; - HideStorageWindow(container.Owner, args.User); - OpenStorageUI(uid, args.User, storageComp, silent: true); - _nestedCheck = false; - } - else - { - // If you need something more sophisticated for multi-UI you'll need to code some smarter - // interactions. - if (_openStorageLimit == 1) - UI.CloseUserUis(args.User); - - OpenStorageUI(uid, args.User, storageComp, silent: false); - } + OpenStorageUI(uid, args.User, storageComp, false); } args.Handled = true; } - protected virtual void HideStorageWindow(EntityUid uid, EntityUid actor) - { - } - - protected virtual void ShowStorageWindow(EntityUid uid, EntityUid actor) - { - } - /// /// Specifically for storage implants. /// @@ -446,10 +404,10 @@ public abstract class SharedStorageSystem : EntitySystem if (args.Handled) return; - var uiOpen = UI.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.Performer); + var uiOpen = _ui.IsUiOpen(uid, StorageComponent.StorageUiKey.Key, args.Performer); if (uiOpen) - UI.CloseUi(uid, StorageComponent.StorageUiKey.Key, args.Performer); + _ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, args.Performer); else OpenStorageUI(uid, args.Performer, storageComp, false); @@ -516,7 +474,7 @@ public abstract class SharedStorageSystem : EntitySystem if (args.Target is not { Valid: true } target) return; - if (ContainerSystem.IsEntityInContainer(target) + if (_containerSystem.IsEntityInContainer(target) || target == args.User || !_itemQuery.HasComponent(target)) { @@ -567,7 +525,7 @@ public abstract class SharedStorageSystem : EntitySystem var entity = GetEntity(args.Entities[i]); // Check again, situation may have changed for some entities, but we'll still pick up any that are valid - if (ContainerSystem.IsEntityInContainer(entity) + if (_containerSystem.IsEntityInContainer(entity) || entity == args.Args.User || !_itemQuery.HasComponent(entity)) { @@ -612,7 +570,7 @@ public abstract class SharedStorageSystem : EntitySystem private void OnReclaimed(EntityUid uid, StorageComponent storageComp, GotReclaimedEvent args) { - ContainerSystem.EmptyContainer(storageComp.Container, destination: args.ReclaimerCoordinates); + _containerSystem.EmptyContainer(storageComp.Container, destination: args.ReclaimerCoordinates); } private void OnDestroy(EntityUid uid, StorageComponent storageComp, DestructionEventArgs args) @@ -620,7 +578,7 @@ public abstract class SharedStorageSystem : EntitySystem var coordinates = TransformSystem.GetMoverCoordinates(uid); // Being destroyed so need to recalculate. - ContainerSystem.EmptyContainer(storageComp.Container, destination: coordinates); + _containerSystem.EmptyContainer(storageComp.Container, destination: coordinates); } /// @@ -680,54 +638,6 @@ public abstract class SharedStorageSystem : EntitySystem TrySetItemStorageLocation(item!, storage!, msg.Location); } - private void OnStorageNested(OpenNestedStorageEvent msg, EntitySessionEventArgs args) - { - if (!NestedStorage) - return; - - if (!TryGetEntity(msg.InteractedItemUid, out var itemEnt)) - return; - - _nestedCheck = true; - - var result = ValidateInput(args, - msg.StorageUid, - msg.InteractedItemUid, - out var player, - out var storage, - out var item); - - if (!result) - { - _nestedCheck = false; - return; - } - - HideStorageWindow(storage.Owner, player.Owner); - OpenStorageUI(item.Owner, player.Owner, silent: true); - _nestedCheck = false; - } - - private void OnStorageTransfer(StorageTransferItemEvent msg, EntitySessionEventArgs args) - { - if (!TryGetEntity(msg.ItemEnt, out var itemEnt)) - return; - - var localPlayer = args.SenderSession.AttachedEntity; - - if (!TryComp(localPlayer, out HandsComponent? handsComp) || !_sharedHandsSystem.TryPickup(localPlayer.Value, itemEnt.Value, handsComp: handsComp, animate: false)) - return; - - if (!ValidateInput(args, msg.StorageEnt, msg.ItemEnt, out var player, out var storage, out var item, held: true)) - return; - - _adminLog.Add( - LogType.Storage, - LogImpact.Low, - $"{ToPrettyString(player):player} is inserting {ToPrettyString(item):item} into {ToPrettyString(storage):storage}"); - InsertAt(storage!, item!, msg.Location, out _, player, stackAutomatically: false); - } - private void OnInsertItemIntoLocation(StorageInsertItemIntoLocationEvent msg, EntitySessionEventArgs args) { if (!ValidateInput(args, msg.StorageEnt, msg.ItemEnt, out var player, out var storage, out var item, held: true)) @@ -748,46 +658,9 @@ public abstract class SharedStorageSystem : EntitySystem SaveItemLocation(storage!, item.Owner); } - private void OnBoundUIOpen(Entity ent, ref BoundUIOpenedEvent args) + private void OnBoundUIOpen(EntityUid uid, StorageComponent storageComp, BoundUIOpenedEvent args) { - UpdateAppearance((ent.Owner, ent.Comp, null)); - } - - private void OnBoundUIAttempt(BoundUserInterfaceMessageAttempt args) - { - if (args.UiKey is not StorageComponent.StorageUiKey.Key || - _openStorageLimit == -1 || - _nestedCheck || - args.Message is not OpenBoundInterfaceMessage) - return; - - var uid = args.Target; - var actor = args.Actor; - var count = 0; - - if (_userQuery.TryComp(actor, out var userComp)) - { - foreach (var (ui, keys) in userComp.OpenInterfaces) - { - if (ui == uid) - continue; - - foreach (var key in keys) - { - if (key is not StorageComponent.StorageUiKey) - continue; - - count++; - - if (count >= _openStorageLimit) - { - args.Cancel(); - } - - break; - } - } - } + UpdateAppearance((uid, storageComp, null)); } private void OnEntInserted(Entity entity, ref EntInsertedIntoContainerMessage args) @@ -803,7 +676,7 @@ public abstract class SharedStorageSystem : EntitySystem { if (!TryGetAvailableGridSpace((entity.Owner, entity.Comp), (args.Entity, null), out var location)) { - ContainerSystem.Remove(args.Entity, args.Container, force: true); + _containerSystem.Remove(args.Entity, args.Container, force: true); return; } @@ -865,7 +738,7 @@ public abstract class SharedStorageSystem : EntitySystem var capacity = storage.Grid.GetArea(); var used = GetCumulativeItemAreas((uid, storage)); - var isOpen = UI.IsUiOpen(entity.Owner, StorageComponent.StorageUiKey.Key); + var isOpen = _ui.IsUiOpen(entity.Owner, StorageComponent.StorageUiKey.Key); _appearance.SetData(uid, StorageVisuals.StorageUsed, used, appearance); _appearance.SetData(uid, StorageVisuals.Capacity, capacity, appearance); @@ -975,7 +848,7 @@ public abstract class SharedStorageSystem : EntitySystem } CheckingCanInsert = true; - if (!ContainerSystem.CanInsert(insertEnt, storageComp.Container)) + if (!_containerSystem.CanInsert(insertEnt, storageComp.Container)) { CheckingCanInsert = false; reason = null; @@ -1076,7 +949,7 @@ public abstract class SharedStorageSystem : EntitySystem if (!stackAutomatically || !_stackQuery.TryGetComponent(insertEnt, out var insertStack)) { - if (!ContainerSystem.Insert(insertEnt, storageComp.Container)) + if (!_containerSystem.Insert(insertEnt, storageComp.Container)) return false; if (playSound) @@ -1102,7 +975,7 @@ public abstract class SharedStorageSystem : EntitySystem // Still stackable remaining if (insertStack.Count > 0 - && !ContainerSystem.Insert(insertEnt, storageComp.Container) + && !_containerSystem.Insert(insertEnt, storageComp.Container) && toInsertCount == insertStack.Count) { // Failed to insert anything. @@ -1181,7 +1054,6 @@ public abstract class SharedStorageSystem : EntitySystem return false; storageEnt.Comp.StoredItems[itemEnt] = location; - UpdateUI(storageEnt); Dirty(storageEnt, storageEnt.Comp); return true; } @@ -1485,16 +1357,16 @@ public abstract class SharedStorageSystem : EntitySystem return; // Gets everyone looking at the UI - foreach (var actor in UI.GetActors(uid, StorageComponent.StorageUiKey.Key).ToList()) + foreach (var actor in _ui.GetActors(uid, StorageComponent.StorageUiKey.Key).ToList()) { if (!CanInteract(actor, (uid, component))) - UI.CloseUi(uid, StorageComponent.StorageUiKey.Key, actor); + _ui.CloseUi(uid, StorageComponent.StorageUiKey.Key, actor); } } private void OnStackCountChanged(EntityUid uid, MetaDataComponent component, StackCountChangedEvent args) { - if (ContainerSystem.TryGetContainingContainer((uid, null, component), out var container) && + if (_containerSystem.TryGetContainingContainer((uid, null, component), out var container) && container.ID == StorageComponent.ContainerId) { UpdateAppearance(container.Owner); @@ -1526,13 +1398,13 @@ public abstract class SharedStorageSystem : EntitySystem if (!ActionBlocker.CanInteract(playerEnt, storageEnt)) return; - if (!UI.IsUiOpen(storageEnt.Value, StorageComponent.StorageUiKey.Key, playerEnt)) + if (!_ui.IsUiOpen(storageEnt.Value, StorageComponent.StorageUiKey.Key, playerEnt)) { OpenStorageUI(storageEnt.Value, playerEnt, silent: false); } else { - UI.CloseUi(storageEnt.Value, StorageComponent.StorageUiKey.Key, playerEnt); + _ui.CloseUi(storageEnt.Value, StorageComponent.StorageUiKey.Key, playerEnt); } } @@ -1587,7 +1459,7 @@ public abstract class SharedStorageSystem : EntitySystem // TODO STORAGE use BUI events // This would automatically validate that the UI is open & that the user can interact. // However, we still need to manually validate that items being used are in the users hands or in the storage. - if (!UI.IsUiOpen(storageUid.Value, StorageComponent.StorageUiKey.Key, playerUid)) + if (!_ui.IsUiOpen(storageUid.Value, StorageComponent.StorageUiKey.Key, playerUid)) return false; if (!ActionBlocker.CanInteract(playerUid, storageUid)) diff --git a/Content.Shared/Storage/StorageComponent.cs b/Content.Shared/Storage/StorageComponent.cs index c59f7ab00e..52547454a0 100644 --- a/Content.Shared/Storage/StorageComponent.cs +++ b/Content.Shared/Storage/StorageComponent.cs @@ -148,19 +148,6 @@ namespace Content.Shared.Storage } } - [Serializable, NetSerializable] - public sealed class OpenNestedStorageEvent : EntityEventArgs - { - public readonly NetEntity InteractedItemUid; - public readonly NetEntity StorageUid; - - public OpenNestedStorageEvent(NetEntity interactedItemUid, NetEntity storageUid) - { - InteractedItemUid = interactedItemUid; - StorageUid = storageUid; - } - } - [Serializable, NetSerializable] public sealed class StorageInteractWithItemEvent : EntityEventArgs { @@ -192,26 +179,6 @@ namespace Content.Shared.Storage } } - [Serializable, NetSerializable] - public sealed class StorageTransferItemEvent : EntityEventArgs - { - public readonly NetEntity ItemEnt; - - /// - /// Target storage to receive the transfer. - /// - public readonly NetEntity StorageEnt; - - public readonly ItemStorageLocation Location; - - public StorageTransferItemEvent(NetEntity itemEnt, NetEntity storageEnt, ItemStorageLocation location) - { - ItemEnt = itemEnt; - StorageEnt = storageEnt; - Location = location; - } - } - [Serializable, NetSerializable] public sealed class StorageInsertItemIntoLocationEvent : EntityEventArgs { From f2c74ef9d40d636ff5adc3f585eb97d2c499f52e Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:15:22 +0100 Subject: [PATCH 10/11] Revert "Update ScalingViewport for Engine PR (#28786)" This reverts commit 3ad83378bbc2118b3c2235d368050a7751bf2046. --- Content.Client/Viewport/ScalingViewport.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Client/Viewport/ScalingViewport.cs b/Content.Client/Viewport/ScalingViewport.cs index d9548d0f02..ccd9636d77 100644 --- a/Content.Client/Viewport/ScalingViewport.cs +++ b/Content.Client/Viewport/ScalingViewport.cs @@ -144,7 +144,7 @@ namespace Content.Client.Viewport _inputManager.ViewportKeyEvent(this, args); } - protected override void Draw(IRenderHandle handle) + protected override void Draw(DrawingHandleScreen handle) { EnsureViewportCreated(); @@ -170,7 +170,7 @@ namespace Content.Client.Viewport var drawBox = GetDrawBox(); var drawBoxGlobal = drawBox.Translated(GlobalPixelPosition); _viewport.RenderScreenOverlaysBelow(handle, this, drawBoxGlobal); - handle.DrawingHandleScreen.DrawTextureRect(_viewport.RenderTarget.Texture, drawBox); + handle.DrawTextureRect(_viewport.RenderTarget.Texture, drawBox); _viewport.RenderScreenOverlaysAbove(handle, this, drawBoxGlobal); } From 0c2baf91f3e44c09b6713dcd14a863fd1050ebb3 Mon Sep 17 00:00:00 2001 From: Vasilis The Pikachu Date: Thu, 6 Feb 2025 01:18:03 +0100 Subject: [PATCH 11/11] Update engine properly --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index f8410a4674..ee906af16e 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit f8410a4674a4978fcea3fd267c6fb26b2ba6e7de +Subproject commit ee906af16e136c7e09930d88872ab9bba3137c8e