From 7b84f98f92dee74640d9bcf7cc664e18e1bf20b4 Mon Sep 17 00:00:00 2001 From: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com> Date: Thu, 19 Jun 2025 18:42:21 -0400 Subject: [PATCH 001/191] Fix the tail wagging action being blocked by cuffs. (#38454) Update types.yml --- Resources/Prototypes/Actions/types.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index f4f35649bd..d7971c706f 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -369,6 +369,7 @@ iconOn: { sprite: Mobs/Customization/reptilian_parts.rsi, state: tail_smooth_behind } itemIconStyle: NoItem useDelay: 1 # emote spam + checkCanInteract: false - type: entity parent: BaseAction From efc4fd05dda99c250751cfa38647691986a88883 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 19 Jun 2025 22:43:29 +0000 Subject: [PATCH 002/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 7626d29ed0..82986b08b6 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: SlamBamActionman - changes: - - message: Chest rig explosive resistance is increased from 50% to 90%. - type: Tweak - id: 8183 - time: '2025-04-14T20:45:38.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/35889 - author: ArtisticRoomba changes: - message: Certain randomly filled bags will now have the correct number of contents @@ -3910,3 +3903,10 @@ id: 8695 time: '2025-06-19T19:49:09.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36875 +- author: VerinSenpai + changes: + - message: Handcuffs no longer interfere with your daily tail wagging activities. + type: Fix + id: 8696 + time: '2025-06-19T22:42:21.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38454 From bd9d1a53f76feabbba574ccbc24fb3f8014bfb44 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Thu, 19 Jun 2025 20:02:46 -0400 Subject: [PATCH 003/191] Remove redundant IoC Resolve in `EmptyOrWindowValidInTile` (#38446) Remove redundant IoC Resolve in EmptyOrWindowValidInTile --- .../Construction/Conditions/EmptyOrWindowValidInTile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Construction/Conditions/EmptyOrWindowValidInTile.cs b/Content.Shared/Construction/Conditions/EmptyOrWindowValidInTile.cs index 5a70e145f6..73b23d11ae 100644 --- a/Content.Shared/Construction/Conditions/EmptyOrWindowValidInTile.cs +++ b/Content.Shared/Construction/Conditions/EmptyOrWindowValidInTile.cs @@ -21,7 +21,7 @@ namespace Content.Shared.Construction.Conditions foreach (var entity in lookupSys.GetEntitiesIntersecting(location, LookupFlags.Approximate | LookupFlags.Static)) { - if (IoCManager.Resolve().HasComponent(entity)) + if (entManager.HasComponent(entity)) result = true; } From 1cda984fdf539eff9fb7d75ad5dc53b4f0f4f302 Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Fri, 20 Jun 2025 03:18:19 -0400 Subject: [PATCH 004/191] fix: trim five minutes of silence from bottle_clunk_2 (#38463) --- Resources/Audio/Items/attributions.yml | 6 +++--- Resources/Audio/Items/bottle_clunk.ogg | Bin 17076 -> 14663 bytes Resources/Audio/Items/bottle_clunk_2.ogg | Bin 25857 -> 5152 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Audio/Items/attributions.yml b/Resources/Audio/Items/attributions.yml index 2dfdcb0555..9e44afac7d 100644 --- a/Resources/Audio/Items/attributions.yml +++ b/Resources/Audio/Items/attributions.yml @@ -55,12 +55,12 @@ - files: ["bottle_clunk.ogg"] license: "CC-BY-4.0" - copyright: "User volivieri on freesound.org. Modified by Velcroboy on github." + copyright: "User volivieri on freesound.org. Modified by Velcroboy on github. Made mono by perryprog." source: "https://freesound.org/people/volivieri/sounds/37190/" - files: ["bottle_clunk_2.ogg"] license: "CC-BY-4.0" - copyright: "User volivieri on freesound.org. Modified by Velcroboy on github." + copyright: "User volivieri on freesound.org. Modified by Velcroboy on github. Silence trimmed + made mono by perryprog." source: "https://freesound.org/people/volivieri/sounds/37190/" - files: ["bottle_close1.ogg"] @@ -158,4 +158,4 @@ - files: [ "toolbox_remove.ogg" ] license: "CC0-1.0" copyright: "Original sound by kooust on freesound.org. Converted to ogg and edited by themias." - source: "https://freesound.org/people/kooust/sounds/452712/" \ No newline at end of file + source: "https://freesound.org/people/kooust/sounds/452712/" diff --git a/Resources/Audio/Items/bottle_clunk.ogg b/Resources/Audio/Items/bottle_clunk.ogg index 1c6b435f3f13a4d7d9e4a5bb4195c5b1ecab9b9b..f9fd65ed201b57865050d339a7572a20da3ce9c2 100644 GIT binary patch literal 14663 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

6JhBH8MFm3}sJHt3o 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 BuXz9f diff --git a/Resources/Audio/Items/bottle_clunk_2.ogg b/Resources/Audio/Items/bottle_clunk_2.ogg index 81afc70a7da835095c87511271407b23d25ef079..16219bdf3b107e105364403c4045a4dc16492f01 100644 GIT binary patch literal 5152 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 literal 25857 zcmeI4dt6gjzUWs%h&v(BB!n~|c6LI*M1xHNw!~^D2?3KB6ar{K?FmUh0@}($JJvII zHX&dLfrbz%Q0hPgR4P^wY_+H71Ox<86kk>Q7^?WHecGv=$GPoY0ZQ9@_$@I%XJ>8Lwk;Ll!OZdG*=f&8CY z_5B`hgD>Um;Y*2w(!KDdk7&DnF@JSz_nXdX35=jK@627Xp>P|z-E7!6l|QON#X_N2 z_`K+OUqwRUC-s$=_n@!grpSC6`%ao12YA()2W$`Kn znzXCPY(!IwcbUwupd#T4Ad57vBd z%1>%K#H)fRIQR-#+Y&pHg$i%i=Xh;yt(rgWI7CCplpZLoi<}s0a>@{2&USu# zQz1qCU|KB{U{FnP*2z$Hg^!MhZ+OhTNRLhlUUMZT;Yw`Mm6bcvU)`0yY1g%l@XUtd z73ik#+VsY*O(%D4{qQ&ECntAIsgB}6bA+VvHs50iJ9c|jhrhM_C?1%6CTS)eU@!AT z<$3Oz58U7L%eiPO3gm&{s|{#2H@5$eGbf`i`~Q7PbBC7!DYWI;0`6KZH$lriTPRpG zW#M4}+f=F*3~FP~7Oh@etTUjo@o`0HndOgId8^mtaH8%JkGV z>1ivkrLMV_mi{jsB~!dWMR0HB)aPMHj4NZxEEvp|T0%*sOqT>BC~GFU6|P#XT0(IR za*w#7Zx5q(?aC3>FU{!lQCxR#2PY*Xojo5nz1%N3^k(YV-zS@jEK{1J0Q{O_t&LsIDjen? zYPOJ<9clI0mvtQNJ}({>oS1PQ9fdpx@U1R+K<719zN72fr%gR8*Ye+*uJoAXe$#Z1 zf{ltoiix4w445gT_#FRmtBd^WhNDJa5iJ>~g^bgn>pL%g8@_=aIsRCX60;^1@|>Q% zsczTJx*ZVU&2Rkiqjz_Gbn=g%+^G6XT=kt_oyOUo1LUwzPIX7O!7jSrTuPPmryF>t z=Oh#`&envhT-8>pr>@+3CEeF$ zuBB(E@80pz$zPw@zv?+I4&TL*R7Xf$gkSX>p8a-Q^`y=fX?F1%>hexCe(~_YiqAbk zzwJ4CTY0XnI=7;cXZQST=WHjO?Kx`;xo5Qk*mDMd({sF{@Oappx&`a5#9sVvHB>W& zk#Hp~Ddg7;0DDdfW3($=bs5@yNeVPQJu)SDC5-tmoY1iQr-pN@?1z`EB6{uh%z(sP zy98kqKjW7%imp};v*dQw3XhQ-Iv_mESe1ixbBxPF#RFMiui0JA-gXLkOZ}jg2!}la zNCAMT;;6VIk5_xnekZtir(*R}*yNtoRrgf~)_(VgXMpOje|RRTc6H7D@mM$y;ez0Y zDQ{sf$769gP?^2wnbF6qU6XralNfA!U35m068-?Rz*Erp58Jti}Vp z#;7Z4>Fq~4wrtwX9zC$sCvKe*>)4~P+rv>LsWHbMdm*;sfVn?5IthwVjZoac(x;VlI8n6|4C*&jVlN22Y1J z{ll_HuX(1js%W1U;RJa}F}|z9)A3l5o^3xyPQM1lJ)REwn)JxV4sv=(dU{<$`y+AE zhj%-U1!ORjeiqxH_#vE^rVs3JWB2*kt#9y8uSnmO{a#0I<1wFThT{IM?;V4K1a1+) zK=3&!G$9Uy({AM*r3+f|Yz_Cqs>XYJxFg-roLL#rj+bID1}Ee;-ml>JWqJ_w{rfWZOW7yZiW4UD8(t_tP_V|&aU%V1|q`ffE$4sGr-Wx#wrxs4s4 zpFS>t!6~sx>ASY(HnvxIcpV)br&q)#eFs5JPfg$YG@MkXV|G(uHIiV^raIpeVS19>ItfKXtR)*0bSxbXq_nQv7 zmkrhzjispf8_rAa^V-xfUXfcn#4?_jz%m`8d*N_khN1(G`$mT-3V_3296-H^H5W*U z7II()3AExtT=?1U*bCn&#$zMTb_u2pHCiAT^@VJG15Tp%8`_~|M!N+UV|>0v+nEH4@`To`u{o@l;b~uFt4lPs-Y6Ta7R;s!UstYSN)o^Y#G(pjH6Uup^ zzg&QI8kxNS>wZ~sADwAxxOAAw%GdH*^)D9?2NkS~Y39NEil}sr1=+ z$-WiLObuW`5NtLq96E5O{dIo6m5wj%R4(uWTsO~T@5^BNs(Pv*C?-#5KYPI^P8IZX zHCkYM!hVT?h-J|O>tl|s#;;|FHPW*3qXcjnjz`AUsVsd?=&Y~JP5HU3UxoRK{{Kjv zu{`dE|KcEQJUEfA-4?nrtl$ob#c@snW5&VW4S@-FYcumiC&}#l;ZtB=V{NuNi!MnZ z?AP-uyIu`~Ms>R~9ir>EURVTMMGF}WGk&qM95&pMUY4=y)Du?xq84|Z;b`ymEqlNA zwLpIKP`z>NgCCr-r4VQ1#op_m?sc@aGK!9NUDxG*z2+oco7V|_oxMMNy0?-KbGgv_ z^0m5NI1AyM*2c%fY7v}_>w|Fmq&VBYD=r^P#U7?|c z{E!$4?XSwfUL9$7uK)9Yz)cO97zKa=T~T${b+W`9eNwls%w6g>R4oxJWfi^cl+elM zYjvy25CeTPe@T3ztTK9kGJAk~vQGP;zV^D|Xs0z9B8ehENM*;ANO26-Fu?q*1U_g! z?!6@1LLf}i#bgTkthMN}*$+^y!;~7EjzSOaDDfCp0o@k;@FI*-Rn=1&@IdVK-}><` z)QS?=wm}@jiIoPw{D<3t8{X?9tx9MMg0mky@Bq$Ju0WL2*dVD6di^OUwT9-R*9N;8 zA=C3E0ecEyCV!hPC#U-0nLB=5g~{4FFiD6^h=h}XbSe`96~nM8pHIfH>DzzrfBH>@ z`ELKAQed;|-5-A1_R~*?bN9Wu|EHfG{`Av@9R>OOow#{)v`!MYT|WK7?;`iYGS&I|fXa`Uib|Uxe~$qJ*S$%Hp@PEG>@7X3c%Z|Rh&n~-r&J`$IW8jO&I+99e$1n*sm1Y%UJ3CJQI zVxip9a#2~y`f<^vlIB_sfj}g9n`m5Fo<**)kD)9^t+c)w@GLk+RS>455KA5) zh$5My8K`&H)9I2;9?BaWWMGnvlI;U=!Tv|kct*0xgFhE{hn~;|+q^lA#79+esl--` zN5hbk&NU1JR)l1v%_AO`@bA)H0pfOLp^T-B%s8|syixO+`9tBk@Kc|*5RLfwz0ab^4G}Mj= zOI7!1M zenUn2{)`lq+Z7eSq2efX2D7ZCrXT5F!eg}8q{)`;7n|M2-+f=Q;x*8E)e0PG@!P&T z^t0#LjYRy8s#^M zb1+TK2qRJ|wQSL&pXs&f?6NIHZ6wxI&%1+6cA~hRZ{#r(%6`AEnJ?uM!&QHn_`t5^ zxH>Ct^;KuQpnu19<5rl(+;y-eZ}3=FZ`SGKw?a~Sv%bs<&Ofv{|9RsTRC-`FpZ+%; zeZVGl6oD}e=QkF&Xu=^PB91G%`Z1Fbkp}@UmgpCjTx{X;qbh?K31Wm;JLHL>2FFnK zs{-rmL%v^QdiZhWy03ne+W(p9ZYz3r#i@V#@xRI+H!;-eb8hzy-SJx;gJbK|YPl;k zGO}!9?7P*SC!@*-gbMTD?KR5~bG#07eaWNCRlH!6bx0&9zzs)!(O+|HZX zp?_om_a}9K27hvYJi7e-=-=ahtG?_qib8rrRuU*5ZQ&iH_7@}) z%5NOrdgfe5Lg#b4lS0mTcBfym*bjGRQc{8|M`YZc-+T+6j(oi&i!kKXaVD(Da7D}* zHHs~CL{7xoRk*iGFlWr9Li`Y;Ii-#`V-syDM`}BH3@xmz z4)i2bVCWcJmdRTDIIjPheVoHQkD)97buuazkDAzPnit=+c|4qDKrEvI>dNh%i<+eY z%A3Ve7o|Hiv=E`@YVs=tca>zP*Vcn|HDvHMbGSBJf2IjO;CUg(j0gVVwOcJHJAfwr zB9jWR8Rgi2}QQr2?}V7 zXw<`4ltmcPr*LtUuuGy5d{`Anc!|GCTs;6J)I|y|Dg;P{RhCQ-3ML_`6pKDNmTV*r zQGf}-u%?)?nu0M2j*a3(i4i<6BIMBY6vT+X63W04+Hs|=LcKk4DUBMdIm0c%`aHRU zRh0%DCxe1cAkroTkpNGPS6@@lc_^@#D;+#$t&F1=KGZNt$Q6DVM@hta@y#@@oRO1d zPjDhNIznHUu-tYhj$l-BUEWxWEScf)lL`6f39I^Al?04hWun`7%(Km)j}6X%>~V83 zv5baS7%!V+iNOzwrNwk%VlnZpB0W$x#RLJK(m#chp4{QklGvgke6YXNDdD=#a&qChr&HCYwn0_9ly_E@VwSH*kUh68vi6~|`#+F90q^b(_yTT!1D zNX1nYL~?Lz<#@nnFg54(*PR1+4FxaqwpuF0r>rq@xdA8@m#!5*d=tSo2g4s5uD7Os z{6zA~qK+3oJiat0+tyfY|LDd0UrMjt4|`&2r;G;=%H4d$IUZk;)1qtdX=R0$G0OU* z>*}I6+vyb)n&$nJdA2H>t^SjfD`{KbUE3X$DoWF&P~JCm)dPj%)ZIE75rC`54PQsA z5NWFowR6h1rexzW4yyaIhC*5dno&a+8OZAxSNw5;$JPSsrjhxi$gad|m z0-NoY3d$08eO_-PiZ3@`WU2kol8c0^w(Z+*3tVg*&|wK|ERxN0)F!*F{fI`PiepCA zB-1T2Dg`^vBJ9)GD=h3u8^sy1bO>V+i9XVE#vgDiM?hH4WrWzS)LeR4Or#bjA`}UZ z-bcWb`@o=wx4yp_#BqcZUI{f`E>s}0EQTM-kc$-6O$8h@aX9qO0F4?-!yBb=9ZXCx zib;g}a#~ci1B<{fo%E7~*gE|$uUtEm=TP8}KH+R~&#RUp5?9iX%| zjrcQ^6JFqd#9c5{!H-ymrczHaNF**nsd8D#2agaE@wi1}1Xx0fm%^_PA=0D*_?#xk zP*zcqAr{pTV_4WgoJ~VxsWBX39aCFlsc-E^BdeQ6YW!&GhNu8TQm(5VkE8Qwq1!O8 ze>L~%iO;WJTcYmw9PiiSQD%q!@ljzIiI?_Ci<=Z7LlbL`y$AqfoVnrN7q_^1kK748 zgSC?PpR2gFDm$>Z>qAS{Cc^Q9%f1)fnusFW%GtMUlw>sP*1R04BYWYsWesVY~^&9~Eg%+$sID9Ek*|wZSxmGu()qbs7{^Gg# z-nJ9&t9vioUoTt{J;I^sc65F!+gH8pTbxHfNTt|#3a$ZhG*F@j4Iq)=)L3<~8X=bG zEFLi6g(PB>P!0@DMY3e{h+~8iT0=;o0jgVxw5kU5q&F(8npSt#Mv!- ze(c$oclOho*01H8{&XwDY$i2~sz1mo)~QSF7*3!jQ1QnZeZXa)Gs+NGt07X{nO)EI%E^wj+lBN1qheM-QZ%R_i2+94;^e-lER}JcRPaNI7 z^V=bV;DwzVpN$SYH^NJ*N#SWunOd6lw;DV0BC@(}WTvOz+F6d~-Kf83=A;F_uh-pP zT{b98>)ah3vp%TbAOunULUli20Ycwx9bIFU;Fbo3Q=K$FdmkDqAXbTp!w3?kTFO6< zCQ2!Be!M78ZLu6k4Yq`}n)9Y}67djs@+N1w{Q0Bb+z6VuH-vj<=6eHUot@Sexv{q^ zzc265NjRk*Y_IA}NUFS+{OI%=&!guA8{@ATRz9(f`J^YptF zM9JYBeh0`w^XHhkqdvU|-bVxAyQB8qTaVrND*LB-boV@$s`H0|IW?UD<7B`r7{2E^ zCvbluFMu(p!RAZg3>e){&4S_kfsfiJ`f&k_`NF`Qdd`4xEO-_SczXciR{w3o0vK}| zY`z4}fYI^vEEvAk#+(1me18Fq`NF`Qdd`5+zIYZ4- z#(ZI5PCaM9sE(Nh1HPmSBF!UD7QmR(VDlw#1`PZ2vtan1ftDXxieCU@zA!MSo-<&S zzc33175a`9z4UJjV9aT-`4Tt-hUNFOV9=m1@Wk=A7r>Y=49uzL3>g0_eijV)?E z1kQj_xM3CyxN`_dk2yB90LFY_U`{<}z<7Cj!yMlOE+z!yk7L_jT>xWFgUy$~88GrT z&5jtpH#0sBswiCmW4)#E`Two!RAZg3>ey1X2J07+^Wp^^Pd*Lm@f>>spkwB>x*W= zfIABT{hJ%d7QmR(VDlw#1`PG9vtan%7eBN4y;Tcf%ohgc)N=-m#J#g%pwRc&SoeXQh`asS@DFinurMD zap0$9u%5nqZdrdU^2K|@8Ai!<)xC~aw@e=G3V*~cy7qo-44J0r>goBou##*Jq3!)b z_pvETF z9YQ!#Qn22^qS1x&^$5WJQ;3J*hi$z52$D zX4b~dm4i}_fY?7FL~GCu_!8Sk@aRtnHFOD=cA*Ai2+JJE< z!>=!nh+zdmK!#(M$|?#J25Xb8Ac!nJ;&B(HjA2xe0A8E4=V)x+=H{NyhC?pYQ%htr z^O@Iq%S>HIW5xC|hCQ+^vMq)8ZqsD}|3z6>t6R<-b?2x=;%;60c2$>?6Y!2MS&({? z8>3~4*V#D-b?ve1)Df4T)iNT~qw07jW0b$RCWDvQh3@D7aHzG%S!R%$6lV6p9*s!7 z&K+I*c)zJjanr?%=2yvY%3HQKoF7)E2;9uerM&1=g**Dtc3z#&g?sF5XWPpJ|IwYT zj%Nf6x-QEZuq69A_hwfptmbDUa#45dcDB0I!S!u_F(^+oH{4!cmwKM(Cfe`1-{p4q ztiHP}^<`BKBS1XHW7;DJQM-Sr`E}&>X%&~3Xd}1&Z>WQo!!*ZLsdgtG;~Lb#k@L3#EHrMiKg;2UKOlwA|oJ! zS8h;=x}DF5*yVPpU1H|;9hxi2XYvl$8+1=CUe4T1OSpPG^Z4fWo@9ZEX~)^$Y3|`B zBqk;@)d6<$pcC$#b>6Zm6Yh!xH_sv`qzL#`ymE(w!4Gecs(_|P=aqQlNE z-Iq7QI?AsJ6Z(V+M~TZezOXy+OrG_Kms_De0q0U zLg3q&y(7!em3cKbEijc4>u%wKZZ1`9jpK<{kOZL|iqlFBFO>_K%{nOoEOqITQZ_vc zkE=vktbHhlCI{7QHUWvm;Q^OIk?AP3GMqgg^t%uqP2a_(kOun_Mo<=IkC(mA6;*j# zpCXW>{R%{m5b*fVCTh?2osCps@yf78ksCA)x`=0$Q}J1Bx+Pc-Hwmi{l4K|nkKQIQ zniY&nKY_JRj^Gbc@k&^_jRkB-jS@@Y1nDt6BN9-EeJX_wu(@ulM~75;2v$UhRF~-~ z%(ZNS6Vylt47Bh{z#;iXurN7+PU~|fe`I65(Ypa5U3-$}<{M?)#NKBtX^Nrf737gFxE zVYHoG?nHPNoypAJTSwSb*2!bGE zR&{s@+ppX~D;RSY5C{Yg@YGVg#bL?yDj~KWZn<1d31!3mm$`wu;wV3C2&}3c;OGR* zK@k;K>Ca+pBS;-0xuV>G6&WaS6LRAKgArPbk1%Ncvm*~5c4*yvj^7v}@f?Ob#NbaP z`=4;4E=xv={}D^8{^k Date: Fri, 20 Jun 2025 21:23:04 +0200 Subject: [PATCH 005/191] Update patrons (#38470) --- Resources/Credits/Patrons.yml | 1595 +++++++++++++++++++++++++++------ 1 file changed, 1345 insertions(+), 250 deletions(-) diff --git a/Resources/Credits/Patrons.yml b/Resources/Credits/Patrons.yml index 3423e797ab..fe313d4a8e 100644 --- a/Resources/Credits/Patrons.yml +++ b/Resources/Credits/Patrons.yml @@ -1,322 +1,1417 @@ -- Name: "Daniel Thompson" - Tier: Revolutionary -- Name: "Farewell Fire" +- Name: Luaan Tier: Syndicate Agent -- Name: "CPM311" +- Name: Nugmania Tier: Revolutionary -- Name: "vifs_vestige" +- Name: Xoxeyos + Tier: Free +- Name: CobaltCobra Tier: Syndicate Agent -- Name: "Anthony Fleck" +- Name: Train + Tier: Free +- Name: ExclusiveWizard + Tier: Syndicate Agent +- Name: Matthew Umney + Tier: Free +- Name: Hat Kid + Tier: Revolutionary +- Name: Ingvar Tornberg + Tier: Revolutionary +- Name: Joshua Hines Tier: Nuclear Operative -- Name: "Nico Thate" +- Name: Ashley Parfitt Tier: Revolutionary -- Name: "Zandario" +- Name: thehomelessmanbehindthedumpster + Tier: Free +- Name: VmasterVX + Tier: Revolutionary +- Name: JenericJen Tier: Nuclear Operative -- Name: "Darren Brady" +- Name: Cosu Tier: Revolutionary -- Name: "DramaBuns" - Tier: Revolutionary -- Name: "Ethan Keller" - Tier: Revolutionary -- Name: "Eric VW" - Tier: Revolutionary -- Name: "Joshington Awesomahee" - Tier: Revolutionary -- Name: "Altana" - Tier: Revolutionary -- Name: "clyf" +- Name: AileenOnline + Tier: Free +- Name: Grotty Bepis Tier: Nuclear Operative -- Name: "Will M." - Tier: Revolutionary -- Name: "The Hateful Flesh" - Tier: Revolutionary -- Name: "Viridian" - Tier: Revolutionary -- Name: "Nicholas Hillblom" - Tier: Revolutionary -- Name: "Austin Nelson" +- Name: Dizzy_Eevee Tier: Syndicate Agent -- Name: "Cormos Lemming" +- Name: Lizard + Tier: Free +- Name: Michael Van Gaasbeck + Tier: Revolutionary +- Name: Statix AraAra + Tier: Revolutionary +- Name: WWII Kitsune Tier: Nuclear Operative -- Name: "Hamcha" +- Name: Bruno Bigras Tier: Revolutionary -- Name: "Peter \"Azmond\" Newhouse" - Tier: Revolutionary -- Name: "Zakanater 19" - Tier: Revolutionary -- Name: "Kris Piper" - Tier: Revolutionary -- Name: "Mikhail" - Tier: Revolutionary -- Name: "Ramiro Agis" - Tier: Revolutionary -- Name: "osborn" - Tier: Syndicate Agent -- Name: "Uinseann" - Tier: Revolutionary -- Name: "Brandon Campbell" +- Name: TheLocalTryHard + Tier: Free +- Name: Sufary Tier: Nuclear Operative -- Name: "Jacob Schramm" - Tier: Revolutionary -- Name: "Matouš Hrdlička" +- Name: taydeo Tier: Nuclear Operative -- Name: "Pasemi" - Tier: Revolutionary -- Name: "Dan" +- Name: dubdubchan Tier: Syndicate Agent -- Name: "Scott MacCombie" +- Name: tremors08 + Tier: Syndicate Agent +- Name: Jan Gocník + Tier: Free +- Name: Tret Mekiao + Tier: Free +- Name: Ben Joja Hotop + Tier: Free +- Name: Sean Lenin + Tier: Free +- Name: KiddoTBH + Tier: Free +- Name: Kubakura + Tier: Syndicate Agent +- Name: Bullet Kin + Tier: Free +- Name: Abby Eveland + Tier: Revolutionary +- Name: Zoe + Tier: Free +- Name: ghost--page Tier: Nuclear Operative -- Name: "Tamora Droppa" +- Name: MechaKitty Tier: Revolutionary -- Name: "Gavin Simmons" - Tier: Syndicate Agent -- Name: "Saphire" - Tier: Revolutionary -- Name: "Never Solus" - Tier: Syndicate Agent -- Name: "Gnomo" +- Name: BlueNexa + Tier: Free +- Name: Noah Heaverin + Tier: Free +- Name: jamozen + Tier: Free +- Name: SireHambone Tier: Nuclear Operative -- Name: "TheGoldElite" +- Name: Blair Cyprus + Tier: Free +- Name: Ryan Hanson Tier: Revolutionary -- Name: "JhenMaster" +- Name: Heinrick Archsider + Tier: Free +- Name: Arsalan Sarwari + Tier: Free +- Name: Scardy + Tier: Free +- Name: Nagano Tier: Revolutionary -- Name: "Akira" +- Name: Aridia + Tier: Free +- Name: illpala Tier: Revolutionary -- Name: "Dave" +- Name: kayo josé + Tier: Free +- Name: Tymon Tymek + Tier: Free +- Name: AlexDragoon Tier: Revolutionary -- Name: "Александр Белошапка" - Tier: Revolutionary -- Name: "tomhendo" +- Name: Henrik Møller Tier: Syndicate Agent -- Name: "Odin7heWanderer" +- Name: Mythtic B Tier: Revolutionary -- Name: "tokie" +- Name: zedd Tier: Nuclear Operative -- Name: "Enricoc3l" +- Name: Penguin Commoner Tier: Revolutionary -- Name: "DadNotTheBelt" +- Name: Davyei + Tier: Free +- Name: Akaelik Tier: Nuclear Operative -- Name: "David" +- Name: Kyle Tyo Tier: Revolutionary -- Name: "Watson Whittington" +- Name: Rhys Williams + Tier: Free +- Name: Drakkenlupen + Tier: Revolutionary +- Name: Randomposter + Tier: Free +- Name: Justin White + Tier: Free +- Name: Ty Ashley + Tier: Revolutionary +- Name: Scorching Rae + Tier: Nuclear Operative +- Name: Cindy Sampaga + Tier: Free +- Name: Plague + Tier: Free +- Name: dude + Tier: Free +- Name: NIACO + Tier: Free +- Name: Tee The Kobold + Tier: Revolutionary +- Name: Redstones563 + Tier: Free +- Name: Celestrogen Tier: Syndicate Agent -- Name: "Tim Foley" +- Name: SotetSotetSotetSotetSotet Tier: Syndicate Agent -- Name: "Katarn" - Tier: Revolutionary -- Name: "eric156" - Tier: Revolutionary -- Name: "Glenn Olsen" +- Name: Guffin + Tier: Free +- Name: Southbridge + Tier: Nuclear Operative +- Name: ZazaRyn Tier: Syndicate Agent -- Name: "Constellations" +- Name: Magic Crystal Radio + Tier: Free +- Name: Sam Sullins + Tier: Free +- Name: Widoux 341 + Tier: Free +- Name: spookerton Tier: Syndicate Agent -- Name: "Shaina Gibson" +- Name: Tom Goodman (TauOmicronMu#2500) Tier: Revolutionary -- Name: "Wolfie" +- Name: Robbie Me + Tier: Free +- Name: momochitters + Tier: Free +- Name: Raspberry1111 Tier: Revolutionary -- Name: "Alexandre Courtin" +- Name: flipways + Tier: Nuclear Operative +- Name: Nictis Tier: Revolutionary -- Name: "Geekyhobo2" +- Name: crazybrain + Tier: Free +- Name: peanut + Tier: Nuclear Operative +- Name: Lewis McCalmont + Tier: Free +- Name: T.P_Gaming Tier: Revolutionary -- Name: "HCG" +- Name: A Patreon of the Ahts + Tier: Free +- Name: brbrbrbrbr brbrbr Tier: Revolutionary -- Name: "ShaunTexas" +- Name: Shadcake + Tier: Free +- Name: kristopher arnott Tier: Syndicate Agent -- Name: "Malachi Housewright" +- Name: Reebe + Tier: Free +- Name: blossom Tier: Revolutionary -- Name: "Petalmeat" +- Name: ml726 + Tier: Free +- Name: Logan Splett + Tier: Free +- Name: Patrick Mitchell + Tier: Free +- Name: Semiotics + Tier: Syndicate Agent +- Name: Snowlili Tier: Revolutionary -- Name: "Adam Smedstad" +- Name: Hyeronimus Bosch + Tier: Free +- Name: S1rFl0 Tier: Revolutionary -- Name: "DireBoar" +- Name: Gavin + Tier: Free +- Name: Faust Statler + Tier: Syndicate Agent +- Name: the good king wompicus + Tier: Syndicate Agent +- Name: DerBall Tier: Revolutionary -- Name: "Ignoramis" +- Name: Gallagin + Tier: Revolutionary +- Name: Edward Doyle + Tier: Revolutionary +- Name: Preston Pickering + Tier: Revolutionary +- Name: Andrew Danke + Tier: Syndicate Agent +- Name: Alexis + Tier: Nuclear Operative +- Name: Saphinara + Tier: Free +- Name: Random Person + Tier: Syndicate Agent +- Name: mage_period + Tier: Syndicate Agent +- Name: piras314 + Tier: Free +- Name: Mark Khudaev + Tier: Free +- Name: Saver + Tier: Free +- Name: Snilo Black + Tier: Free +- Name: CptFalcon + Tier: Revolutionary +- Name: Daxx (Justin Winningham) + Tier: Syndicate Agent +- Name: Posters Union Australia (Shlee) + Tier: Free +- Name: nathan + Tier: Free +- Name: funnylookingwolf + Tier: Revolutionary +- Name: Sporkyz + Tier: Revolutionary +- Name: SarahRaven + Tier: Revolutionary +- Name: Da Skolin + Tier: Free +- Name: Joshua Meyer + Tier: Free +- Name: BUMBLE + Tier: Free +- Name: Lily Roche + Tier: Free +- Name: Klive McToaster + Tier: Free +- Name: CasuallyAPerson + Tier: Free +- Name: Powder + Tier: Revolutionary +- Name: Noah Heaverin + Tier: Free +- Name: Violette + Tier: Revolutionary +- Name: Amble + Tier: Nuclear Operative +- Name: James Sanders + Tier: Free +- Name: Susaniel + Tier: Free +- Name: Eve Holly + Tier: Free +- Name: Superscope + Tier: Free +- Name: Nyxilath + Tier: Nuclear Operative +- Name: RedChair368 + Tier: Revolutionary +- Name: Blumbee + Tier: Revolutionary +- Name: Mason Fawcett + Tier: Revolutionary +- Name: Cerzix + Tier: Revolutionary +- Name: Aeris Ophelia + Tier: Free +- Name: Troll_King_907 + Tier: Nuclear Operative +- Name: Certified Hood Classic + Tier: Free +- Name: Hydraliks + Tier: Free +- Name: Meguneri + Tier: Free +- Name: hyphenation + Tier: Nuclear Operative +- Name: Psy + Tier: Revolutionary +- Name: Pinet + Tier: Nuclear Operative +- Name: Vivi Bunny + Tier: Free +- Name: James Stevens + Tier: Revolutionary +- Name: anharmlessguest + Tier: Syndicate Agent +- Name: Outlier + Tier: Free +- Name: Brandon Hackney + Tier: Free +- Name: eleanor nora + Tier: Free +- Name: starch + Tier: Syndicate Agent +- Name: Jacob Watson + Tier: Syndicate Agent +- Name: Seinfeld + Tier: Revolutionary +- Name: Prime! + Tier: Free +- Name: John + Tier: Free +- Name: FauxParadox + Tier: Revolutionary +- Name: Mr_Clank + Tier: Revolutionary +- Name: rafael rabelo + Tier: Free +- Name: TBF_Jack + Tier: Free +- Name: StaceyBalcom + Tier: Revolutionary +- Name: guh + Tier: Revolutionary +- Name: Fran Picek + Tier: Free +- Name: Austenn + Tier: Revolutionary +- Name: Flayn + Tier: Free +- Name: Nye + Tier: Free +- Name: Phycoweirdo + Tier: Nuclear Operative +- Name: Madeleine LeFleur + Tier: Syndicate Agent +- Name: Anton + Tier: Revolutionary +- Name: That Furry Fuck + Tier: Free +- Name: sat + Tier: Free +- Name: goober + Tier: Free +- Name: Boogerfoot + Tier: Nuclear Operative +- Name: Raiden Johnson + Tier: Revolutionary +- Name: Talbot + Tier: Revolutionary +- Name: Alen Kadric + Tier: Free +- Name: pogcat + Tier: Free +- Name: Dave + Tier: Free +- Name: Nathaniel Taylor + Tier: Revolutionary +- Name: Z4NEER + Tier: Nuclear Operative +- Name: Laying on my penis until it falls asleep, so it feels like I'm giving someone else a hando + Tier: Revolutionary +- Name: Sam Caesar + Tier: Revolutionary +- Name: Lykan + Tier: Free +- Name: tarna41 + Tier: Free +- Name: Ryan Johnson + Tier: Free +- Name: Andrew Montagne + Tier: Syndicate Agent +- Name: yakub mobutu + Tier: Free +- Name: Michael Krebs + Tier: Free +- Name: mr funny swag + Tier: Free +- Name: Crazyeyes24 + Tier: Syndicate Agent +- Name: Zylo + Tier: Free +- Name: ScarKy0 + Tier: Revolutionary +- Name: Randall Hudson + Tier: Free +- Name: FaberTheCatboy + Tier: Free +- Name: IamOzyman + Tier: Revolutionary +- Name: Jado0213 + Tier: Revolutionary +- Name: StaticeStorm + Tier: Syndicate Agent +- Name: Baron Mathot + Tier: Free +- Name: CrudeOil + Tier: Nuclear Operative +- Name: Sartec + Tier: Revolutionary +- Name: Mathew McGuire + Tier: Revolutionary +- Name: Jelloterd2 + Tier: Free +- Name: Commissar Fox + Tier: Syndicate Agent +- Name: tyler adams + Tier: Revolutionary +- Name: Logan Britt + Tier: Free +- Name: Joshua A. + Tier: Free +- Name: ya boi darth Vader + Tier: Free +- Name: Syndlkat + Tier: Free +- Name: Zyleak + Tier: Revolutionary +- Name: Hunter + Tier: Free +- Name: Killerqu00 + Tier: Free +- Name: lzk + Tier: Free +- Name: Cai Williamson + Tier: Syndicate Agent +- Name: Arykh + Tier: Nuclear Operative +- Name: Pallastovid + Tier: Free +- Name: Rome + Tier: Revolutionary +- Name: mawrk + Tier: Free +- Name: Jack + Tier: Revolutionary +- Name: stikfigz + Tier: Revolutionary +- Name: RyanTuek + Tier: Revolutionary +- Name: David N + Tier: Syndicate Agent +- Name: Panzer IV + Tier: Free +- Name: Novaq + Tier: Revolutionary +- Name: Trevor Doyle + Tier: Free +- Name: Borna Punda + Tier: Revolutionary +- Name: Patrick Long + Tier: Free +- Name: Alpha + Tier: Nuclear Operative +- Name: Gaëtan GOUSSEAUD + Tier: Revolutionary +- Name: Hayden Wessing + Tier: Revolutionary +- Name: Noodle is me + Tier: Syndicate Agent +- Name: snakebb + Tier: Revolutionary +- Name: Patrick + Tier: Revolutionary +- Name: Väinö Kontinen + Tier: Free +- Name: Devin Nelson + Tier: Revolutionary +- Name: Rat Man + Tier: Revolutionary +- Name: Christopher Day + Tier: Free +- Name: Wilhelm 17 + Tier: Free +- Name: Kieue Caprie + Tier: Revolutionary +- Name: RamenZorn + Tier: Syndicate Agent +- Name: Swix Swix + Tier: Revolutionary +- Name: Katharina + Tier: Free +- Name: Blackmare + Tier: Syndicate Agent +- Name: lukiw + Tier: Revolutionary +- Name: Magelfik + Tier: Revolutionary +- Name: Kevin + Tier: Nuclear Operative +- Name: Tomáš Sitarčík + Tier: Syndicate Agent +- Name: Coolsurf6 + Tier: Revolutionary +- Name: Neexdle + Tier: Free +- Name: Lucid + Tier: Free +- Name: MissKay1994 + Tier: Syndicate Agent +- Name: Raccoononi + Tier: Revolutionary +- Name: Avocado999 + Tier: Free +- Name: Invisorum + Tier: Nuclear Operative +- Name: Hannah + Tier: Nuclear Operative +- Name: Thomas + Tier: Revolutionary +- Name: Cozmicc + Tier: Free +- Name: Unalterableness Happiness + Tier: Free +- Name: Sparlight + Tier: Nuclear Operative +- Name: MAILS ADVISORY + Tier: Free +- Name: Rq52 + Tier: Free +- Name: Ben Wahba + Tier: Free +- Name: North + Tier: Free +- Name: Tyran + Tier: Free +- Name: TheSimpleTime + Tier: Free +- Name: Minemoder + Tier: Syndicate Agent +- Name: Richard "Rei" Weeks + Tier: Syndicate Agent +- Name: Sika-Voi + Tier: Free +- Name: Dumbdestroyer2 + Tier: Free +- Name: Ferox Heist + Tier: Free +- Name: Ryan Vieira + Tier: Free +- Name: kithri + Tier: Nuclear Operative +- Name: Luzurper + Tier: Revolutionary +- Name: Rainy + Tier: Revolutionary +- Name: Dusk + Tier: Revolutionary +- Name: Apocist + Tier: Revolutionary +- Name: Yukari_TK + Tier: Free +- Name: Iris + Tier: Free +- Name: Jamie Forkner + Tier: Free +- Name: rokudara + Tier: Free +- Name: Xionwalker + Tier: Free +- Name: Michael Schrader + Tier: Free +- Name: JT Billings + Tier: Revolutionary +- Name: Stedar + Tier: Syndicate Agent +- Name: Alex Burd + Tier: Nuclear Operative +- Name: Vasilis The Pikachu + Tier: Free +- Name: Erik Meinke + Tier: Revolutionary +- Name: Sleeepz + Tier: Syndicate Agent +- Name: Jonathan + Tier: Free +- Name: Trent Warfel + Tier: Nuclear Operative +- Name: Fritz Vohlkson + Tier: Free +- Name: Гоша Ушаров + Tier: Free +- Name: Cpt Blastahoe + Tier: Free +- Name: dedomena + Tier: Free +- Name: Nolan Downing + Tier: Free +- Name: Seth Morron + Tier: Revolutionary +- Name: Fumo Legionnaire + Tier: Free +- Name: obama + Tier: Free +- Name: Daniel Megiddo + Tier: Free +- Name: Денис + Tier: Free +- Name: Ubaser + Tier: Revolutionary +- Name: Catdere + Tier: Syndicate Agent +- Name: valery xolstov + Tier: Free +- Name: Marios Sardis + Tier: Revolutionary +- Name: Constrife + Tier: Free +- Name: TINYANGRYWOMAN + Tier: Free +- Name: Kerensky + Tier: Revolutionary +- Name: DEATHB4DEFEAT + Tier: Free +- Name: Kristy Denniss + Tier: Free +- Name: orange color + Tier: Revolutionary +- Name: Hectik + Tier: Free +- Name: SomeDogo + Tier: Free +- Name: Joshua Meyer + Tier: Nuclear Operative +- Name: Matthias Bechtold + Tier: Revolutionary +- Name: Serafim + Tier: Free +- Name: Cindy Xiao + Tier: Free +- Name: SmokingKilz + Tier: Free +- Name: Jacob Scott + Tier: Free +- Name: Ryan Kelly + Tier: Revolutionary +- Name: mikiju + Tier: Syndicate Agent +- Name: TheGungeonologist + Tier: Revolutionary +- Name: Tia + Tier: Revolutionary +- Name: yeoshua + Tier: Syndicate Agent +- Name: David Ilina + Tier: Free +- Name: Robyn Pothagan + Tier: Free +- Name: chomp + Tier: Free +- Name: Maksym Aleinikov + Tier: Free +- Name: Chase M. + Tier: Free +- Name: Ethan Maria + Tier: Free +- Name: Luna Rose + Tier: Free +- Name: Jordon + Tier: Revolutionary +- Name: Godfiend + Tier: Revolutionary +- Name: Jake Huxell + Tier: Free +- Name: Alexander Simakov + Tier: Free +- Name: Hannah Dawson + Tier: Syndicate Agent +- Name: Dieselmohawk D. + Tier: Revolutionary +- Name: Sean Lilly + Tier: Syndicate Agent +- Name: Brandon Roughley + Tier: Syndicate Agent +- Name: Jack + Tier: Free +- Name: Cooliano Rizzo + Tier: Free +- Name: North Star + Tier: Free +- Name: Flamebrain + Tier: Free +- Name: Andrew + Tier: Revolutionary +- Name: Ostoja + Tier: Free +- Name: Russian TS + Tier: Nuclear Operative +- Name: Kyu, The Meme Fairy + Tier: Free +- Name: LIMIT_bro . + Tier: Free +- Name: Kim + Tier: Revolutionary +- Name: Dourbii + Tier: Revolutionary +- Name: Autistic Dog + Tier: Free +- Name: Black Rose + Tier: Free +- Name: rose soul + Tier: Free +- Name: Toast + Tier: Free +- Name: Jiangshi Luvr + Tier: Free +- Name: OllyTheFoxcoon + Tier: Free +- Name: Christopher Peltola + Tier: Revolutionary +- Name: Bonefan223 + Tier: Nuclear Operative +- Name: Fie + Tier: Free +- Name: DuskyJay + Tier: Free +- Name: Zella Zane + Tier: Revolutionary +- Name: Pixel Realms + Tier: Free +- Name: ConcaveGnome + Tier: Syndicate Agent +- Name: Ростислав Мірошніченко + Tier: Free +- Name: Hunter Tew + Tier: Syndicate Agent +- Name: Daniel D + Tier: Syndicate Agent +- Name: Kick Buttowski + Tier: Free +- Name: Eggexe + Tier: Free +- Name: Lv + Tier: Free +- Name: Ali Alashar + Tier: Free +- Name: Nathaniel Joseph Hurd + Tier: Revolutionary +- Name: sk3iron + Tier: Revolutionary +- Name: Dax XZ + Tier: Free +- Name: debelizm + Tier: Free +- Name: Kaisering + Tier: Revolutionary +- Name: VividWisp + Tier: Syndicate Agent +- Name: Alex Mercer + Tier: Free +- Name: AmalgoMyte + Tier: Revolutionary +- Name: SeanKim + Tier: Free +- Name: Siegorvus + Tier: Free +- Name: вася васильев + Tier: Free +- Name: Mci + Tier: Revolutionary +- Name: KateSlime206 + Tier: Nuclear Operative +- Name: Robert + Tier: Free +- Name: Ben Z' + Tier: Revolutionary +- Name: Luke + Tier: Free +- Name: Tyler Putnam + Tier: Free +- Name: Ville Sarmiola + Tier: Free +- Name: kelaclone + Tier: Free +- Name: Archmonarch + Tier: Syndicate Agent +- Name: Brett + Tier: Free +- Name: Hanzdegloker + Tier: Syndicate Agent +- Name: Piotr + Tier: Revolutionary +- Name: CloudyClover + Tier: Revolutionary +- Name: Jim Masterson + Tier: Free +- Name: kenneth heller + Tier: Syndicate Agent +- Name: Gosera - + Tier: Syndicate Agent +- Name: Asaf Ataş + Tier: Free +- Name: DeckDevilYGO + Tier: Free +- Name: nokko + Tier: Free +- Name: Krokodiel89 + Tier: Free +- Name: Nojan Niaki + Tier: Revolutionary +- Name: YuNii + Tier: Nuclear Operative +- Name: Georgia Partyka + Tier: Syndicate Agent +- Name: Teeth + Tier: Free +- Name: DrMelon + Tier: Revolutionary +- Name: Molly the Bully + Tier: Nuclear Operative +- Name: An Evil Eel + Tier: Free +- Name: Hasan al-Binabi + Tier: Free +- Name: franz photos + Tier: Free +- Name: Milkshake45752 + Tier: Free +- Name: Bru Tal + Tier: Free +- Name: krberryy + Tier: Revolutionary +- Name: Thomas Mcintosh + Tier: Free +- Name: Alex Nordlund + Tier: Syndicate Agent +- Name: Swindel_ + Tier: Syndicate Agent +- Name: MrGloopy + Tier: Revolutionary +- Name: blitz gaming + Tier: Free +- Name: Nicholi + Tier: Syndicate Agent +- Name: Taber + Tier: Revolutionary +- Name: 以迪 黃 + Tier: Free +- Name: W Geertsma + Tier: Free +- Name: BloodyJMary + Tier: Free +- Name: quri1q + Tier: Free +- Name: SleepyBody91 + Tier: Free +- Name: David Gee + Tier: Free +- Name: ThoranTW + Tier: Free +- Name: pigeopeas + Tier: Free +- Name: Zakory L Holbrook + Tier: Revolutionary +- Name: Aiden + Tier: Revolutionary +- Name: Pangaron + Tier: Syndicate Agent +- Name: NeatVisor + Tier: Free +- Name: Solidus Snake + Tier: Nuclear Operative +- Name: Oliver Skyler + Tier: Free +- Name: mustafa kılıç + Tier: Free +- Name: IceStorm theDragon + Tier: Revolutionary +- Name: Indri + Tier: Revolutionary +- Name: Graded + Tier: Free +- Name: tunnp0 + Tier: Free +- Name: Vexxtraordinary + Tier: Revolutionary +- Name: Sam Hediger + Tier: Free +- Name: Aldi Abduali + Tier: Free +- Name: Adam Clarke + Tier: Free +- Name: Munt + Tier: Free +- Name: Obspogon + Tier: Free +- Name: Adrian Thompson + Tier: Free +- Name: albatrossMonarch + Tier: Free +- Name: JaQal + Tier: Free +- Name: Simofie + Tier: Free +- Name: redgunny + Tier: Free +- Name: Kevin Chapman + Tier: Free +- Name: Sjevi Aardvark + Tier: Free +- Name: Chris Walter + Tier: Free +- Name: David Sterling + Tier: Free +- Name: Zcore + Tier: Free +- Name: tapohuy + Tier: Free +- Name: MasterFurret + Tier: Revolutionary +- Name: Christian Hicks + Tier: Syndicate Agent +- Name: François Desautels + Tier: Revolutionary +- Name: awndrssk + Tier: Free +- Name: Vice Emargo + Tier: Syndicate Agent +- Name: Mihailo Trickovic + Tier: Syndicate Agent +- Name: Sandvich enjoyer + Tier: Revolutionary +- Name: Higgtastic Tier: Revolutionary - Name: "612" Tier: Revolutionary -- Name: "Higgtastic" +- Name: AlecRazec Tier: Revolutionary -- Name: "Sandvich enjoyer" - Tier: Revolutionary -- Name: "Mihailo Trickovic" - Tier: Syndicate Agent -- Name: "Vice Emargo" - Tier: Revolutionary -- Name: "François Desautels" - Tier: Revolutionary -- Name: "MasterFurret" - Tier: Revolutionary -- Name: "Sam Hediger" - Tier: Syndicate Agent -- Name: "Vexxtraordinary" - Tier: Revolutionary -- Name: "Graded" - Tier: Syndicate Agent -- Name: "Indri" - Tier: Revolutionary -- Name: "Solidus Snake" +- Name: Repo Tier: Nuclear Operative -- Name: "Pangaron" +- Name: Ignoramis + Tier: Free +- Name: Jeffery Pointer Tier: Syndicate Agent -- Name: "Sed" +- Name: DireBoar + Tier: Free +- Name: oBerry + Tier: Free +- Name: Adam Smedstad + Tier: Free +- Name: Superglue + Tier: Syndicate Agent +- Name: DrIvoPotato Tier: Nuclear Operative -- Name: "Aiden Baker" +- Name: Petalmeat + Tier: Free +- Name: Malachi Housewright Tier: Revolutionary -- Name: "Beans" - Tier: Revolutionary -- Name: "ThoranTW" - Tier: Revolutionary -- Name: "Crabby Arts" - Tier: Revolutionary -- Name: "以迪 黃" - Tier: Syndicate Agent -- Name: "Taber" - Tier: Revolutionary -- Name: "Nicholi" - Tier: Syndicate Agent -- Name: "blitz gaming" - Tier: Revolutionary -- Name: "MrGloopy" - Tier: Revolutionary -- Name: "Swindel_" - Tier: Syndicate Agent -- Name: "Alex Nordlund" - Tier: Revolutionary -- Name: "Thomas Mcintosh" - Tier: Revolutionary -- Name: "krberryy" - Tier: Revolutionary -- Name: "Hasan al-Binabi" +- Name: Fallcon + Tier: Free +- Name: ShaunTexas + Tier: Free +- Name: HCG + Tier: Free +- Name: GeneralMarty + Tier: Free +- Name: Nicholas Tier: Nuclear Operative -- Name: "Molly the Bully" +- Name: Geekyhobo2 + Tier: Revolutionary +- Name: Alexandre Courtin + Tier: Revolutionary +- Name: Wolfie + Tier: Revolutionary +- Name: Shaina Gibson + Tier: Free +- Name: Charles Baron + Tier: Free +- Name: Constellations + Tier: Free +- Name: tynobeard 123 + Tier: Revolutionary +- Name: Lyndomen + Tier: Revolutionary +- Name: Glenn Olsen + Tier: Syndicate Agent +- Name: Wiliam Tier: Nuclear Operative -- Name: "DrMelon" - Tier: Revolutionary -- Name: "Georgia Partyka" - Tier: Syndicate Agent -- Name: "Nojan Niaki" - Tier: Revolutionary -- Name: "nokko" - Tier: Revolutionary -- Name: "DeckDevilYGO" - Tier: Syndicate Agent -- Name: "Gosera -" - Tier: Syndicate Agent -- Name: "kenneth heller" - Tier: Syndicate Agent -- Name: "Piotr" +- Name: SHANE ALAN ZINDA + Tier: Free +- Name: Saiiryn + Tier: Free +- Name: AC Computing Tier: Nuclear Operative -- Name: "Hanzdegloker" +- Name: eric156 + Tier: Free +- Name: Katarn + Tier: Free +- Name: Blight Tier: Syndicate Agent -- Name: "Archmonarch" +- Name: Tim Foley + Tier: Free +- Name: Sven Tammerijn + Tier: Revolutionary +- Name: Raw Toast + Tier: Free +- Name: Watson Whittington Tier: Syndicate Agent -- Name: "Ville Sarmiola" +- Name: David + Tier: Revolutionary +- Name: DadNotTheBelt + Tier: Free +- Name: Enricoc3l + Tier: Revolutionary +- Name: Vandell + Tier: Free +- Name: BlueisDumb Tier: Nuclear Operative -- Name: "Tyler Putnam" - Tier: Revolutionary -- Name: "Luke" - Tier: Revolutionary -- Name: "Robert" +- Name: Buldinn Girshovich Tier: Nuclear Operative -- Name: "Zombine" +- Name: Whistling Jake + Tier: Revolutionary +- Name: Wallace Megas + Tier: Free +- Name: Samtaro Tier: Nuclear Operative -- Name: "Mci" +- Name: Aexxie Tier: Revolutionary -- Name: "The Silent Occupant" +- Name: Fiebertraum Illhead Tier: Syndicate Agent -- Name: "AmalgoMyte" - Tier: Syndicate Agent -- Name: "VividWisp" - Tier: Syndicate Agent -- Name: "Kaisering" - Tier: Revolutionary -- Name: "Dax XZ" - Tier: Syndicate Agent -- Name: "sk3iron" - Tier: Revolutionary -- Name: "Nathaniel Joseph Hurd" - Tier: Revolutionary -- Name: "Ali Alashar" - Tier: Syndicate Agent -- Name: "Lv" - Tier: Revolutionary -- Name: "Eggexe" - Tier: Revolutionary -- Name: "Kick Buttowski" - Tier: Syndicate Agent -- Name: "Daniel D" - Tier: Syndicate Agent -- Name: "Hunter Tew" - Tier: Syndicate Agent -- Name: "ConcaveGnome" - Tier: Syndicate Agent -- Name: "Pixel Realms" +- Name: tokie Tier: Nuclear Operative -- Name: "Zella Zane" - Tier: Revolutionary -- Name: "DuskyJay" - Tier: Revolutionary -- Name: "HogWithGreatArmpitHair" - Tier: Revolutionary -- Name: "Bonefan223" +- Name: Odin7heWanderer + Tier: Free +- Name: Jeff Tatem Tier: Nuclear Operative -- Name: "Christopher Peltola" - Tier: Nuclear Operative -- Name: "OllyTheFoxcoon" - Tier: Revolutionary -- Name: "Cherchar" - Tier: Revolutionary -- Name: "Black Rose" - Tier: Revolutionary -- Name: "Kim" - Tier: Nuclear Operative -- Name: "Kyu, The Meme Fairy" +- Name: M4shr00m + Tier: Free +- Name: Gordod Tier: Syndicate Agent -- Name: "Russian TS" - Tier: Nuclear Operative -- Name: "Andrew" - Tier: Revolutionary -- Name: "Cooliano Rizzo" - Tier: Nuclear Operative -- Name: "Brandon Roughley" +- Name: Skarlet + Tier: Free +- Name: March Tier: Syndicate Agent -- Name: "Sean Lilly" - Tier: Syndicate Agent -- Name: "Dieselmohawk D." +- Name: Александр Белошапка Tier: Revolutionary -- Name: "Hannah Dawson" - Tier: Syndicate Agent -- Name: "Jake Huxell" - Tier: Syndicate Agent -- Name: "Godfiend" +- Name: Dave Tier: Revolutionary -- Name: "Jordon" +- Name: Akira Tier: Revolutionary -- Name: "Luna Rose" - Tier: Revolutionary -- Name: "Ethan Maria" - Tier: Revolutionary -- Name: "Robyn Pothagan" - Tier: Revolutionary -- Name: "TheGungeonologist" - Tier: Revolutionary -- Name: "Ryan Kelly" - Tier: Revolutionary -- Name: "Jacob Scott" - Tier: Revolutionary -- Name: "Matthias Bechtold" - Tier: Revolutionary -- Name: "Joshua Meyer" +- Name: Alix Shepard Tier: Nuclear Operative -- Name: "Roxy Rirumi" +- Name: JhenMaster + Tier: Free +- Name: Lieutenant Colonel Orangejuice + Tier: Syndicate Agent +- Name: TheGoldElite Tier: Revolutionary -- Name: "Zero To template" +- Name: Gnomo Tier: Nuclear Operative -- Name: "Hectik" +- Name: Never Solus + Tier: Free +- Name: Ari. Tier: Revolutionary -- Name: "orange color" - Tier: Revolutionary -- Name: "Kristy Denniss" +- Name: Kevin Tier: Nuclear Operative -- Name: "Kerensky" +- Name: DubzyVEVO Tier: Revolutionary +- Name: ikamuse johnson + Tier: Revolutionary +- Name: Saphire + Tier: Revolutionary +- Name: Gavin Simmons + Tier: Syndicate Agent +- Name: Tamora Droppa + Tier: Revolutionary +- Name: Gaxeer + Tier: Syndicate Agent +- Name: rosysyn + Tier: Revolutionary +- Name: Scott MacCombie + Tier: Free +- Name: Jeremy Hernandez + Tier: Syndicate Agent +- Name: Kai + Tier: Nuclear Operative +- Name: Victoly + Tier: Free +- Name: chilicheesecat + Tier: Free +- Name: Arthur Norris + Tier: Syndicate Agent +- Name: Carbonhell + Tier: Syndicate Agent +- Name: BrittaBee93 + Tier: Free +- Name: Rasmus Cedergren + Tier: Revolutionary +- Name: Greggo + Tier: Syndicate Agent +- Name: Pasemi + Tier: Revolutionary +- Name: Unknown Kiwi + Tier: Revolutionary +- Name: Mitchell Marry + Tier: Revolutionary +- Name: Matouš Hrdlička + Tier: Revolutionary +- Name: Roman + Tier: Syndicate Agent +- Name: Jacob Schramm + Tier: Free +- Name: Nathan Zaldivar + Tier: Nuclear Operative +- Name: SpydAxe + Tier: Revolutionary +- Name: BokChoy + Tier: Nuclear Operative +- Name: Nicholas Williamson + Tier: Nuclear Operative +- Name: William Grondin + Tier: Revolutionary +- Name: KevKev + Tier: Free +- Name: Gothryd + Tier: Revolutionary +- Name: Brandon Campbell + Tier: Free +- Name: Matthew C Miklaucic + Tier: Revolutionary +- Name: Serianas + Tier: Nuclear Operative +- Name: lapatison + Tier: Revolutionary +- Name: Uinseann + Tier: Revolutionary +- Name: osborn + Tier: Syndicate Agent +- Name: LPGaming + Tier: Free +- Name: Alex Fry + Tier: Nuclear Operative +- Name: Ramiro Agis + Tier: Revolutionary +- Name: Mikhail + Tier: Revolutionary +- Name: SnapKick + Tier: Revolutionary +- Name: liltenhead + Tier: Revolutionary +- Name: James Andrew Peoples Jr + Tier: Revolutionary +- Name: Serathis + Tier: Revolutionary +- Name: Joel + Tier: Revolutionary +- Name: Rafał Kowalewski + Tier: Revolutionary +- Name: Kris Piper + Tier: Free +- Name: Anthony Chicko + Tier: Revolutionary +- Name: Taylor + Tier: Revolutionary +- Name: Phillip Inman + Tier: Revolutionary +- Name: Zakanater 19 + Tier: Revolutionary +- Name: Molon + Tier: Syndicate Agent +- Name: Peter "Azmond" Newhouse + Tier: Free +- Name: Snowni + Tier: Nuclear Operative +- Name: Tom Cruize + Tier: Syndicate Agent +- Name: Corpus Inc. + Tier: Revolutionary +- Name: BlastWind + Tier: Free +- Name: Echinodermata + Tier: Revolutionary +- Name: Hamcha + Tier: Revolutionary +- Name: Kaitlynn Brennan + Tier: Syndicate Agent +- Name: Oxyclean114 + Tier: Revolutionary +- Name: JustanAnus + Tier: Nuclear Operative +- Name: wtcwr68 + Tier: Nuclear Operative +- Name: Azmith Blackheart + Tier: Revolutionary +- Name: Collin R Terrell + Tier: Revolutionary +- Name: Cormos Lemming + Tier: Nuclear Operative +- Name: grhmhome + Tier: Revolutionary +- Name: SpiffyNnemonic + Tier: Revolutionary +- Name: Callum Tubrett + Tier: Nuclear Operative +- Name: Hydration + Tier: Revolutionary +- Name: Morty + Tier: Free +- Name: Dylan Roberts + Tier: Syndicate Agent +- Name: mrniceguy0o0 + Tier: Free +- Name: Wrexbe + Tier: Revolutionary +- Name: Frege Beach + Tier: Revolutionary +- Name: DBF + Tier: Syndicate Agent +- Name: Kesiath + Tier: Nuclear Operative +- Name: Voltinho + Tier: Revolutionary +- Name: AquaDraco + Tier: Revolutionary +- Name: A Yaj + Tier: Revolutionary +- Name: Miniwoffer + Tier: Revolutionary +- Name: hh + Tier: Syndicate Agent +- Name: Chase Trotter + Tier: Nuclear Operative +- Name: Skifan180 + Tier: Free +- Name: Florian + Tier: Revolutionary +- Name: Hoplophobia + Tier: Revolutionary +- Name: Viridian + Tier: Free +- Name: Daskata + Tier: Nuclear Operative +- Name: TacoCub + Tier: Revolutionary +- Name: Jack Rose + Tier: Revolutionary +- Name: Chem_Inventory + Tier: Nuclear Operative +- Name: Jax + Tier: Nuclear Operative +- Name: The Hateful Flesh + Tier: Revolutionary +- Name: Gavin Jones + Tier: Nuclear Operative +- Name: Prof. Omii + Tier: Nuclear Operative +- Name: mksky + Tier: Revolutionary +- Name: Greyhound Connect + Tier: Revolutionary +- Name: Alex Tempest + Tier: Revolutionary +- Name: Rat Fungus + Tier: Nuclear Operative +- Name: Will M. + Tier: Revolutionary +- Name: spinnermaster + Tier: Free +- Name: Koyki + Tier: Revolutionary +- Name: Hunter James + Tier: Nuclear Operative +- Name: Valinov + Tier: Nuclear Operative +- Name: Nemmay + Tier: Revolutionary +- Name: Dylan Hull + Tier: Revolutionary +- Name: FungiFog + Tier: Revolutionary +- Name: S.C. + Tier: Syndicate Agent +- Name: "'-Y-" + Tier: Syndicate Agent +- Name: pooba + Tier: Revolutionary +- Name: evilJenny + Tier: Revolutionary +- Name: clyf + Tier: Free +- Name: Kerb + Tier: Revolutionary +- Name: queednyeb + Tier: Revolutionary +- Name: Robin Rottstock + Tier: Revolutionary +- Name: Third + Tier: Revolutionary +- Name: Altana + Tier: Revolutionary +- Name: showgun117 + Tier: Syndicate Agent +- Name: Durp + Tier: Revolutionary +- Name: Joshington Awesomahee + Tier: Revolutionary +- Name: Diklyquill + Tier: Revolutionary +- Name: Eric VW + Tier: Revolutionary +- Name: Nat + Tier: Revolutionary +- Name: Evan Armstrong + Tier: Revolutionary +- Name: Citizen Battle + Tier: Nuclear Operative +- Name: Cyberate + Tier: Revolutionary +- Name: Enzoman12 + Tier: Nuclear Operative +- Name: Curtis Pearson + Tier: Revolutionary +- Name: Mono + Tier: Revolutionary +- Name: Xenon Dragon + Tier: Syndicate Agent +- Name: Jerg Jerginson + Tier: Nuclear Operative +- Name: kik + Tier: Revolutionary +- Name: Star Lord + Tier: Syndicate Agent +- Name: kannthus + Tier: Syndicate Agent +- Name: Ethan Keller + Tier: Revolutionary +- Name: John Doe + Tier: Nuclear Operative +- Name: Robert Reed + Tier: Nuclear Operative +- Name: DramaBuns + Tier: Revolutionary +- Name: Bush Boy + Tier: Nuclear Operative +- Name: Quonn + Tier: Syndicate Agent +- Name: KrystalDisc + Tier: Revolutionary +- Name: LeTobbs + Tier: Revolutionary +- Name: Darren Brady + Tier: Free +- Name: Zandario + Tier: Revolutionary +- Name: Lucas Welsh + Tier: Revolutionary +- Name: Nico Thate + Tier: Free +- Name: Anthony Fleck + Tier: Nuclear Operative +- Name: vifs_vestige + Tier: Syndicate Agent +- Name: Bobberunio + Tier: Revolutionary +- Name: Zadenae + Tier: Nuclear Operative +- Name: Into the Stars - Community Host + Tier: Nuclear Operative +- Name: arthropods + Tier: Revolutionary +- Name: Jack Gardiner + Tier: Revolutionary +- Name: BigMcLargeHuge + Tier: Free +- Name: merklaw + Tier: Revolutionary +- Name: CPM311 + Tier: Revolutionary +- Name: NetGlitch + Tier: Nuclear Operative +- Name: Bahke + Tier: Nuclear Operative +- Name: Prayer + Tier: Nuclear Operative +- Name: Trevor McConnell + Tier: Nuclear Operative +- Name: MetalClone + Tier: Free +- Name: Farewell Fire + Tier: Syndicate Agent +- Name: Ninja + Tier: Syndicate Agent +- Name: all fields + Tier: Syndicate Agent +- Name: Kyle Hipke + Tier: Nuclear Operative +- Name: Daniel Thompson + Tier: Revolutionary +- Name: Tyler Young + Tier: Nuclear Operative +- Name: LeoZ + Tier: Revolutionary +- Name: avcde + Tier: Nuclear Operative +- Name: Tomeno + Tier: Revolutionary + From c1de936c4b89a8b32476316aa117a235c777ef50 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 20 Jun 2025 18:11:12 -0400 Subject: [PATCH 006/191] Forbid string literals for `SharedActionsSystem` methods (#38472) Forbid string literals for SharedActionsSystem methods --- Content.Shared/Actions/SharedActionsSystem.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 6490dc3c0a..333d87157d 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -579,7 +579,7 @@ public abstract class SharedActionsSystem : EntitySystem #region AddRemoveActions public EntityUid? AddAction(EntityUid performer, - string? actionPrototypeId, + [ForbidLiteral] string? actionPrototypeId, EntityUid container = default, ActionsComponent? component = null) { @@ -599,7 +599,7 @@ public abstract class SharedActionsSystem : EntitySystem /// The entity that contains/enables this action (e.g., flashlight). public bool AddAction(EntityUid performer, [NotNullWhen(true)] ref EntityUid? actionId, - string? actionPrototypeId, + [ForbidLiteral] string? actionPrototypeId, EntityUid container = default, ActionsComponent? component = null) { @@ -610,7 +610,7 @@ public abstract class SharedActionsSystem : EntitySystem public bool AddAction(EntityUid performer, [NotNullWhen(true)] ref EntityUid? actionId, [NotNullWhen(true)] out ActionComponent? action, - string? actionPrototypeId, + [ForbidLiteral] string? actionPrototypeId, EntityUid container = default, ActionsComponent? component = null) { From a1969fe392fdc2553adb14e25d37ea7e054735e3 Mon Sep 17 00:00:00 2001 From: Qerd <73325910+BigfootBravo@users.noreply.github.com> Date: Fri, 20 Jun 2025 15:41:11 -0700 Subject: [PATCH 007/191] Chameleon Controller Implants can be deimplanted (#38439) --- Resources/Prototypes/Entities/Objects/Misc/implanters.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Objects/Misc/implanters.yml b/Resources/Prototypes/Entities/Objects/Misc/implanters.yml index e83649499b..0d98bd025f 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/implanters.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/implanters.yml @@ -46,6 +46,7 @@ - MindShieldImplant - FakeMindShieldImplant - RadioImplant + - ChameleonControllerImplant deimplantFailureDamage: types: Cellular: 50 From 49e810fd5b2593d068837b9b560a475c659991fb Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 20 Jun 2025 22:42:20 +0000 Subject: [PATCH 008/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 82986b08b6..9c66f1206c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: ArtisticRoomba - changes: - - message: Certain randomly filled bags will now have the correct number of contents - inside of them. - type: Fix - id: 8184 - time: '2025-04-14T21:02:49.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36155 - author: Will-Oliver-Br changes: - message: Now you can see if there's a beaker in the chemmaster. @@ -3910,3 +3902,10 @@ id: 8696 time: '2025-06-19T22:42:21.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38454 +- author: BigfootBravo + changes: + - message: Chameleon Controller Implants can be deimplanted. + type: Fix + id: 8697 + time: '2025-06-20T22:41:11.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38439 From 8e8f47497bd85378eefab422e18631734a7759e0 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 20 Jun 2025 18:43:53 -0400 Subject: [PATCH 009/191] Cleanup duplicate dependency in `AdminVerbSystem.Smites` (#38473) Cleanup duplicate dependency in AdminVerbSystem.Smites --- .../Administration/Systems/AdminVerbSystem.Smites.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index 3c6782d08e..1ae0543073 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -79,7 +79,6 @@ public sealed partial class AdminVerbSystem [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SuperBonkSystem _superBonkSystem = default!; [Dependency] private readonly SlipperySystem _slipperySystem = default!; - [Dependency] private readonly OutfitSystem _outfitSystem = default!; // All smite verbs have names so invokeverb works. private void AddSmiteVerbs(GetVerbsEvent args) @@ -587,7 +586,7 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Uniforms/Jumpskirt/janimaid.rsi"), "icon"), Act = () => { - _outfitSystem.SetOutfit(args.Target, "JanitorMaidGear", (_, clothing) => + _outfit.SetOutfit(args.Target, "JanitorMaidGear", (_, clothing) => { if (HasComp(clothing)) EnsureComp(clothing); From 416b276ac3f4d148282ae6b72c6dc21d6cb86d64 Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Fri, 20 Jun 2025 18:48:54 -0400 Subject: [PATCH 010/191] fix: set correct layer visibility for power cell sprites (#38458) * fix: set correct layer visibility for power cell sprites * fix: use a default charge level for power cell sprites' appearance data --- Content.Client/PowerCell/PowerCellSystem.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Content.Client/PowerCell/PowerCellSystem.cs b/Content.Client/PowerCell/PowerCellSystem.cs index 66fa1fa98f..fb40b911d1 100644 --- a/Content.Client/PowerCell/PowerCellSystem.cs +++ b/Content.Client/PowerCell/PowerCellSystem.cs @@ -48,17 +48,14 @@ public sealed class PowerCellSystem : SharedPowerCellSystem if (!_sprite.LayerExists((uid, args.Sprite), PowerCellVisualLayers.Unshaded)) return; - if (_appearance.TryGetData(uid, PowerCellVisuals.ChargeLevel, out var level, args.Component)) - { - if (level == 0) - { - _sprite.LayerSetVisible((uid, args.Sprite), PowerCellVisualLayers.Unshaded, false); - return; - } + if (!_appearance.TryGetData(uid, PowerCellVisuals.ChargeLevel, out var level, args.Component)) + level = 0; - _sprite.LayerSetVisible((uid, args.Sprite), PowerCellVisualLayers.Unshaded, false); + var positiveCharge = level > 0; + _sprite.LayerSetVisible((uid, args.Sprite), PowerCellVisualLayers.Unshaded, positiveCharge); + + if (positiveCharge) _sprite.LayerSetRsiState((uid, args.Sprite), PowerCellVisualLayers.Unshaded, $"o{level}"); - } } private enum PowerCellVisualLayers : byte From 61eddfebc4cba76036b446c291223dc5a2e90068 Mon Sep 17 00:00:00 2001 From: Ps3Moira <113228053+ps3moira@users.noreply.github.com> Date: Fri, 20 Jun 2025 15:50:35 -0700 Subject: [PATCH 011/191] Remove Icon Smoothing for Airlocks, Doors, and Plastic Flaps, and Shutters from Walls (#38456) * Nuke Icon Smoothing * Update highsec.yml * Update plastic_flaps.yml --- .../Entities/Structures/Doors/Airlocks/airlocks.yml | 6 ------ .../Structures/Doors/Airlocks/base_structureairlocks.yml | 3 --- .../Entities/Structures/Doors/Airlocks/highsec.yml | 3 --- .../Structures/Doors/MaterialDoors/material_doors.yml | 3 --- .../Entities/Structures/Doors/Shutter/shutters.yml | 3 --- Resources/Prototypes/Entities/Structures/plastic_flaps.yml | 3 --- 6 files changed, 21 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml index 1eb961dd78..f61b97076f 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml @@ -342,9 +342,6 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/xeno.rsi - - type: IconSmooth - key: chitin - mode: NoSprite - type: entity parent: AirlockGlass @@ -353,6 +350,3 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/xeno.rsi - - type: IconSmooth - key: chitin - mode: NoSprite diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index b1001797ae..3e70b5ccbe 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -158,9 +158,6 @@ - board - type: PlacementReplacement key: walls - - type: IconSmooth - key: walls - mode: NoSprite - type: PaintableAirlock group: Standard department: Civilian diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml index 6f7340c80f..28b63d9723 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/highsec.yml @@ -115,9 +115,6 @@ behaviors: - !type:DoActsBehavior acts: ["Destruction"] - - type: IconSmooth - key: walls - mode: NoSprite - type: Construction graph: Airlock node: highSecDoor diff --git a/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml b/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml index acb5faa209..5d6a87ea6b 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml @@ -55,9 +55,6 @@ behaviors: - !type:DoActsBehavior acts: ["Destruction"] - - type: IconSmooth - key: walls - mode: NoSprite - type: Occluder - type: BlockWeather diff --git a/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml b/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml index 7becad94bf..ca870d7c7d 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml @@ -78,9 +78,6 @@ behaviors: - !type:DoActsBehavior acts: ["Destruction"] - - type: IconSmooth - key: walls - mode: NoSprite - type: DoorSignalControl - type: DeviceNetwork deviceNetId: Wireless diff --git a/Resources/Prototypes/Entities/Structures/plastic_flaps.yml b/Resources/Prototypes/Entities/Structures/plastic_flaps.yml index ab705c035b..088ddfda61 100644 --- a/Resources/Prototypes/Entities/Structures/plastic_flaps.yml +++ b/Resources/Prototypes/Entities/Structures/plastic_flaps.yml @@ -35,9 +35,6 @@ behaviors: - !type:DoActsBehavior acts: ["Destruction"] - - type: IconSmooth - key: walls - mode: NoSprite - type: StaticPrice price: 100 From 48d897d31e3574938b70de8a7ac9131989394c4b Mon Sep 17 00:00:00 2001 From: Hannah Giovanna Dawson Date: Sat, 21 Jun 2025 00:39:08 +0100 Subject: [PATCH 012/191] Scurrets (#38218) * Scurrets. * Add missing equipment YAML * Fix count of NamesFirstScurret * Resolve PR comments, wa. Also add like a bunch more wa replacements * ed * Update Resources/Textures/Mobs/Animals/scurret/displacement.rsi/meta.json * :rivflabbergasted: * Fixed spacings in scurret_last.ftl * Fix mangled endings of some last names * wawa * the scug has a spear for self defence --- .../Audio/Ambience/Antag/attributions.yml | 4 + .../Audio/Ambience/Antag/traitor_wawa.ogg | Bin 0 -> 217702 bytes Resources/Audio/Animals/attributions.yml | 42 +++-- Resources/Audio/Animals/wawa_achoo.ogg | Bin 0 -> 33507 bytes Resources/Audio/Animals/wawa_chatter.ogg | Bin 0 -> 36144 bytes Resources/Audio/Animals/wawa_chillin.ogg | Bin 0 -> 33019 bytes Resources/Audio/Animals/wawa_depression.ogg | Bin 0 -> 36444 bytes Resources/Audio/Animals/wawa_exclaim.ogg | Bin 0 -> 23074 bytes Resources/Audio/Animals/wawa_mock.ogg | Bin 0 -> 46841 bytes Resources/Audio/Animals/wawa_protest.ogg | Bin 0 -> 65986 bytes Resources/Audio/Animals/wawa_question.ogg | Bin 0 -> 15319 bytes Resources/Audio/Animals/wawa_statement.ogg | Bin 0 -> 20599 bytes .../en-US/chat/managers/chat-manager.ftl | 6 + .../en-US/datasets/names/scurret_first.ftl | 36 ++++ .../en-US/datasets/names/scurret_last.ftl | 36 ++++ Resources/Locale/en-US/scurret copy/role.ftl | 25 +++ .../Prototypes/Accents/full_replacements.yml | 24 +++ .../Prototypes/Catalog/Cargo/cargo_fun.yml | 10 ++ .../Prototypes/Catalog/Fills/Crates/fun.yml | 19 ++ .../Prototypes/Datasets/Names/scurret.yml | 11 ++ .../Markers/Spawners/Random/posters.yml | 1 + .../Prototypes/Entities/Mobs/NPCs/scurret.yml | 170 ++++++++++++++++++ .../Entities/Objects/Devices/pda.yml | 11 ++ .../Structures/Wallmounts/Signs/posters.yml | 9 + .../scurret_inventory_template.yml | 62 +++++++ Resources/Prototypes/Palettes/critters.yml | 15 ++ .../Roles/Jobs/Fun/misc_startinggear.yml | 7 + .../Prototypes/SoundCollections/scurret.yml | 19 ++ .../Prototypes/Voice/speech_emote_sounds.yml | 18 ++ Resources/Prototypes/Voice/speech_sounds.yml | 9 + Resources/Prototypes/Voice/speech_verbs.yml | 9 + .../Animals/scurret/displacement.rsi/ears.png | Bin 0 -> 262 bytes .../Animals/scurret/displacement.rsi/eyes.png | Bin 0 -> 238 bytes .../scurret/displacement.rsi/gloves.png | Bin 0 -> 432 bytes .../Animals/scurret/displacement.rsi/hand.png | Bin 0 -> 426 bytes .../Animals/scurret/displacement.rsi/head.png | Bin 0 -> 291 bytes .../Animals/scurret/displacement.rsi/id.png | Bin 0 -> 271 bytes .../scurret/displacement.rsi/jumpsuit.png | Bin 0 -> 329 bytes .../Animals/scurret/displacement.rsi/mask.png | Bin 0 -> 264 bytes .../scurret/displacement.rsi/meta.json | 50 ++++++ .../Animals/scurret/displacement.rsi/neck.png | Bin 0 -> 530 bytes .../Animals/scurret/scurret.rsi/meta.json | 21 +++ .../Animals/scurret/scurret.rsi/scurret.png | Bin 0 -> 1995 bytes .../scurret/scurret.rsi/scurret_oof.png | Bin 0 -> 532 bytes .../scurret/scurret.rsi/scurret_rip.png | Bin 0 -> 562 bytes .../Wallmounts/posters.rsi/meta.json | 5 +- .../Wallmounts/posters.rsi/poster55_legit.png | Bin 0 -> 1634 bytes 47 files changed, 604 insertions(+), 15 deletions(-) create mode 100644 Resources/Audio/Ambience/Antag/traitor_wawa.ogg create mode 100644 Resources/Audio/Animals/wawa_achoo.ogg create mode 100644 Resources/Audio/Animals/wawa_chatter.ogg create mode 100644 Resources/Audio/Animals/wawa_chillin.ogg create mode 100644 Resources/Audio/Animals/wawa_depression.ogg create mode 100644 Resources/Audio/Animals/wawa_exclaim.ogg create mode 100644 Resources/Audio/Animals/wawa_mock.ogg create mode 100644 Resources/Audio/Animals/wawa_protest.ogg create mode 100644 Resources/Audio/Animals/wawa_question.ogg create mode 100644 Resources/Audio/Animals/wawa_statement.ogg create mode 100644 Resources/Locale/en-US/datasets/names/scurret_first.ftl create mode 100644 Resources/Locale/en-US/datasets/names/scurret_last.ftl create mode 100644 Resources/Locale/en-US/scurret copy/role.ftl create mode 100644 Resources/Prototypes/Datasets/Names/scurret.yml create mode 100644 Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml create mode 100644 Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml create mode 100644 Resources/Prototypes/SoundCollections/scurret.yml create mode 100644 Resources/Textures/Mobs/Animals/scurret/displacement.rsi/ears.png create mode 100644 Resources/Textures/Mobs/Animals/scurret/displacement.rsi/eyes.png create mode 100644 Resources/Textures/Mobs/Animals/scurret/displacement.rsi/gloves.png create mode 100644 Resources/Textures/Mobs/Animals/scurret/displacement.rsi/hand.png create mode 100644 Resources/Textures/Mobs/Animals/scurret/displacement.rsi/head.png create mode 100644 Resources/Textures/Mobs/Animals/scurret/displacement.rsi/id.png create mode 100644 Resources/Textures/Mobs/Animals/scurret/displacement.rsi/jumpsuit.png create mode 100644 Resources/Textures/Mobs/Animals/scurret/displacement.rsi/mask.png create mode 100644 Resources/Textures/Mobs/Animals/scurret/displacement.rsi/meta.json create mode 100644 Resources/Textures/Mobs/Animals/scurret/displacement.rsi/neck.png create mode 100644 Resources/Textures/Mobs/Animals/scurret/scurret.rsi/meta.json create mode 100644 Resources/Textures/Mobs/Animals/scurret/scurret.rsi/scurret.png create mode 100644 Resources/Textures/Mobs/Animals/scurret/scurret.rsi/scurret_oof.png create mode 100644 Resources/Textures/Mobs/Animals/scurret/scurret.rsi/scurret_rip.png create mode 100644 Resources/Textures/Structures/Wallmounts/posters.rsi/poster55_legit.png diff --git a/Resources/Audio/Ambience/Antag/attributions.yml b/Resources/Audio/Ambience/Antag/attributions.yml index 0d3d278a62..c0c34eb880 100644 --- a/Resources/Audio/Ambience/Antag/attributions.yml +++ b/Resources/Audio/Ambience/Antag/attributions.yml @@ -2,6 +2,10 @@ license: "CC-BY-SA-3.0" copyright: "Taken from TG station" source: "https://github.com/tgstation/tgstation/commit/827967c9650c23af64280ad1491405fed8f644c5#diff-6cc910b7cad9ac4333c8d0885fc844746066120b465d8d4ba8f7019169316574" +- files: ["traitor_wawa.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Taken from TG station, edited by FairlySadPanda (Github)" + source: "https://github.com/tgstation/tgstation/commit/827967c9650c23af64280ad1491405fed8f644c5#diff-6cc910b7cad9ac4333c8d0885fc844746066120b465d8d4ba8f7019169316574" - files: ["pirate_start.ogg"] license: "CC-BY-NC-SA-3.0" copyright: "Made by @MIXnikita#1474 (Discord) for SS14" diff --git a/Resources/Audio/Ambience/Antag/traitor_wawa.ogg b/Resources/Audio/Ambience/Antag/traitor_wawa.ogg new file mode 100644 index 0000000000000000000000000000000000000000..7134be84cc4f797a6c4ba331b85d07084823c412 GIT binary patch literal 217702 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 z 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Ŷ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%0AqgjoF=c=Z!M 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�#e6E;DaTcNBaeAf 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 literal 0 HcmV?d00001 diff --git a/Resources/Audio/Animals/attributions.yml b/Resources/Audio/Animals/attributions.yml index 7fd7e8b2e7..59d8bc5a6e 100644 --- a/Resources/Audio/Animals/attributions.yml +++ b/Resources/Audio/Animals/attributions.yml @@ -139,20 +139,34 @@ source: "https://github.com/ParadiseSS13/Paradise/commit/a34f1054cef5a44a67fdac3b67b811137c6071dd" - files: - - fox1.ogg - - fox2.ogg - - fox3.ogg - - fox4.ogg - - fox5.ogg - - fox6.ogg - - fox7.ogg - - fox8.ogg - - fox9.ogg - - fox10.ogg - - fox11.ogg - - fox12.ogg - - fox13.ogg - - fox14.ogg + - fox1.ogg + - fox2.ogg + - fox3.ogg + - fox4.ogg + - fox5.ogg + - fox6.ogg + - fox7.ogg + - fox8.ogg + - fox9.ogg + - fox10.ogg + - fox11.ogg + - fox12.ogg + - fox13.ogg + - fox14.ogg copyright: "Created by fujiwaranao" license: "CC-BY-NC-SA-4.0" source: "https://github.com/space-wizards/space-station-14/pull/27578" + +- files: + - wawa_achoo.ogg + - wawa_chatter.ogg + - wawa_chillin.ogg + - wawa_depression.ogg + - wawa_exclaim.ogg + - wawa_mock.ogg + - wawa_protest.ogg + - wawa_question.ogg + - wawa_statement.ogg + copyright: "created by FairlySadPanda" + license: "CC-BY-4.0" + source: "https://github.com/space-wizards/space-station-14/pull/37936" diff --git a/Resources/Audio/Animals/wawa_achoo.ogg b/Resources/Audio/Animals/wawa_achoo.ogg new file mode 100644 index 0000000000000000000000000000000000000000..56670c2d05966075a6826d6c9b30afc22b162d04 GIT binary patch literal 33507 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 literal 0 HcmV?d00001 diff --git a/Resources/Audio/Animals/wawa_chatter.ogg b/Resources/Audio/Animals/wawa_chatter.ogg new file mode 100644 index 0000000000000000000000000000000000000000..7e2a7ec3ca0589cde9b155447840c2349e0f5692 GIT binary patch literal 36144 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 literal 0 HcmV?d00001 diff --git a/Resources/Audio/Animals/wawa_chillin.ogg b/Resources/Audio/Animals/wawa_chillin.ogg new file mode 100644 index 0000000000000000000000000000000000000000..5955fdf96794462d9ae50ff82a1a219c7635f121 GIT binary patch literal 33019 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 B_nZI# literal 0 HcmV?d00001 diff --git a/Resources/Audio/Animals/wawa_mock.ogg b/Resources/Audio/Animals/wawa_mock.ogg new file mode 100644 index 0000000000000000000000000000000000000000..355c2aa841ba6a2f30577daf247b9365b38095cf GIT binary patch literal 46841 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 Br5*qP literal 0 HcmV?d00001 diff --git a/Resources/Audio/Animals/wawa_protest.ogg b/Resources/Audio/Animals/wawa_protest.ogg new file mode 100644 index 0000000000000000000000000000000000000000..cc1cf60a0403091adf73239654ceb9640a4404ab GIT binary patch literal 65986 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%0Ay1`y0@4}NH&bN9dmh=9@nJsQcJ1m%} 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 literal 0 HcmV?d00001 diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index c20852468b..e7e55c1ea4 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -157,3 +157,9 @@ chat-speech-verb-name-electricity = Electricity chat-speech-verb-electricity-1 = crackles chat-speech-verb-electricity-2 = buzzes chat-speech-verb-electricity-3 = screeches + +chat-speech-verb-name-wawa = Wawa +chat-speech-verb-wawa-1 = intones +chat-speech-verb-wawa-2 = states +chat-speech-verb-wawa-3 = declares +chat-speech-verb-wawa-4 = ponders diff --git a/Resources/Locale/en-US/datasets/names/scurret_first.ftl b/Resources/Locale/en-US/datasets/names/scurret_first.ftl new file mode 100644 index 0000000000..4bbbf943f0 --- /dev/null +++ b/Resources/Locale/en-US/datasets/names/scurret_first.ftl @@ -0,0 +1,36 @@ +names-scurret-first-dataset-1 = Wa +names-scurret-first-dataset-2 = Calm +names-scurret-first-dataset-3 = Contented +names-scurret-first-dataset-4 = Patient +names-scurret-first-dataset-5 = Relaxed +names-scurret-first-dataset-6 = Serene +names-scurret-first-dataset-7 = Blissful +names-scurret-first-dataset-8 = Eager +names-scurret-first-dataset-9 = Happy +names-scurret-first-dataset-10 = Inspired +names-scurret-first-dataset-11 = Lively +names-scurret-first-dataset-12 = Playful +names-scurret-first-dataset-13 = Radiant +names-scurret-first-dataset-14 = Vibrant +names-scurret-first-dataset-15 = Brave +names-scurret-first-dataset-16 = Capable +names-scurret-first-dataset-17 = Confident +names-scurret-first-dataset-18 = Daring +names-scurret-first-dataset-19 = Proud +names-scurret-first-dataset-20 = Valiant +names-scurret-first-dataset-21 = Caring +names-scurret-first-dataset-22 = Compassionate +names-scurret-first-dataset-23 = Empathic +names-scurret-first-dataset-24 = Strong +names-scurret-first-dataset-25 = Free +names-scurret-first-dataset-26 = Lucky +names-scurret-first-dataset-27 = Worthy +names-scurret-first-dataset-28 = Blessed +names-scurret-first-dataset-29 = Fortunate +names-scurret-first-dataset-30 = Trusting +names-scurret-first-dataset-31 = Fast +names-scurret-first-dataset-32 = Energetic +names-scurret-first-dataset-33 = Wise +names-scurret-first-dataset-34 = Alert +names-scurret-first-dataset-35 = Uplifting +names-scurret-first-dataset-36 = Considerate diff --git a/Resources/Locale/en-US/datasets/names/scurret_last.ftl b/Resources/Locale/en-US/datasets/names/scurret_last.ftl new file mode 100644 index 0000000000..effe2180f0 --- /dev/null +++ b/Resources/Locale/en-US/datasets/names/scurret_last.ftl @@ -0,0 +1,36 @@ +names-scurret-last-dataset-1 = Wa +names-scurret-last-dataset-2 = Trees +names-scurret-last-dataset-3 = Plants +names-scurret-last-dataset-4 = Rocks +names-scurret-last-dataset-5 = Rivers +names-scurret-last-dataset-6 = Groves +names-scurret-last-dataset-7 = Lakes +names-scurret-last-dataset-8 = Marshes +names-scurret-last-dataset-9 = Spring +names-scurret-last-dataset-10 = Reeds +names-scurret-last-dataset-11 = Sunshine +names-scurret-last-dataset-12 = Rain +names-scurret-last-dataset-13 = Clouds +names-scurret-last-dataset-14 = Snowfall +names-scurret-last-dataset-15 = Stones +names-scurret-last-dataset-16 = Pebbles +names-scurret-last-dataset-17 = Fishes +names-scurret-last-dataset-18 = Insects +names-scurret-last-dataset-19 = Fruits +names-scurret-last-dataset-20 = Flowers +names-scurret-last-dataset-21 = Blossoms +names-scurret-last-dataset-22 = Fogs +names-scurret-last-dataset-23 = Willows +names-scurret-last-dataset-24 = Alders +names-scurret-last-dataset-25 = Birches +names-scurret-last-dataset-26 = Poplars +names-scurret-last-dataset-27 = Marigolds +names-scurret-last-dataset-28 = Robins +names-scurret-last-dataset-29 = Orchids +names-scurret-last-dataset-30 = Rushes +names-scurret-last-dataset-31 = Lillies +names-scurret-last-dataset-32 = Violets +names-scurret-last-dataset-33 = Maples +names-scurret-last-dataset-34 = Oaks +names-scurret-last-dataset-35 = Hazels +names-scurret-last-dataset-36 = the All-Knowing diff --git a/Resources/Locale/en-US/scurret copy/role.ftl b/Resources/Locale/en-US/scurret copy/role.ftl new file mode 100644 index 0000000000..cbc0f89657 --- /dev/null +++ b/Resources/Locale/en-US/scurret copy/role.ftl @@ -0,0 +1,25 @@ +petting-success-scurret = You pet {THE($target)} on {POSS-ADJ($target)} legally distinct head. +petting-failure-scurret = You reach out to pet {THE($target)}, but {SUBJECT($target)} does a backflip! + +accent-words-scurret-1 = Wa! +accent-words-scurret-2 = Wa? +accent-words-scurret-3 = Wa. +accent-words-scurret-4 = Wa... +accent-words-scurret-5 = Wawa! +accent-words-scurret-6 = Wawa? +accent-words-scurret-7 = Wawa. +accent-words-scurret-8 = Wawa... +accent-words-scurret-9 = Wa wawa! +accent-words-scurret-10 = Wa wawa? +accent-words-scurret-11 = Wa wawa. +accent-words-scurret-12 = Wa wawa... +accent-words-scurret-13 = Wawa wa! +accent-words-scurret-14 = Wawa wa? +accent-words-scurret-15 = Wawa wa. +accent-words-scurret-16 = Wawa wa... +accent-words-scurret-17 = Waaaaaa. +accent-words-scurret-18 = Waaaaaa! +accent-words-scurret-19 = Waaaaaa? +accent-words-scurret-20 = Waaaaaa... + +station-event-random-sentience-flavor-scurret = scurret diff --git a/Resources/Prototypes/Accents/full_replacements.yml b/Resources/Prototypes/Accents/full_replacements.yml index f495de250f..737739ba9d 100644 --- a/Resources/Prototypes/Accents/full_replacements.yml +++ b/Resources/Prototypes/Accents/full_replacements.yml @@ -183,3 +183,27 @@ - accent-words-tomato-3 - accent-words-tomato-4 - accent-words-tomato-5 + +- type: accent + id: scurret + fullReplacements: + - accent-words-scurret-1 + - accent-words-scurret-2 + - accent-words-scurret-3 + - accent-words-scurret-4 + - accent-words-scurret-5 + - accent-words-scurret-6 + - accent-words-scurret-7 + - accent-words-scurret-8 + - accent-words-scurret-9 + - accent-words-scurret-10 + - accent-words-scurret-11 + - accent-words-scurret-12 + - accent-words-scurret-13 + - accent-words-scurret-14 + - accent-words-scurret-15 + - accent-words-scurret-16 + - accent-words-scurret-17 + - accent-words-scurret-18 + - accent-words-scurret-19 + - accent-words-scurret-20 diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml b/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml index 445e7d941b..7a8deb3151 100644 --- a/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml +++ b/Resources/Prototypes/Catalog/Cargo/cargo_fun.yml @@ -218,6 +218,16 @@ category: cargoproduct-category-name-fun group: market +- type: cargoProduct + id: HydratedScurret + icon: + sprite: Structures/Wallmounts/posters.rsi + state: poster55_legit + product: CrateFunScurret + cost: 25000 # You will surely not regret buying this + category: cargoproduct-category-name-fun + group: market + - type: cargoProduct id: FunCrateGambling icon: diff --git a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml index 18b0dc26d8..1c0d3668ae 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml @@ -458,3 +458,22 @@ amount: 1 prob: 0.05 +- type: entity + name: hydrated scurret + description: Wait, what? + id: CrateFunScurret + parent: CratePlastic + components: + - type: StorageFill + contents: + - id: MobEmotionalSupportScurret # How? Why? + - id: DrinkVodkaBottleFull + - id: DrinkWhiskeyBottleFull + - id: DrinkTequilaBottleFull # ...that explains it. + - id: DrinkShotGlass + amount: 2 + - id: Spear # self defence + - id: ClothingHeadHatHardhatYellow + - id: ClothingNeckMantleQM + - id: ClothingHeadsetCargo + diff --git a/Resources/Prototypes/Datasets/Names/scurret.yml b/Resources/Prototypes/Datasets/Names/scurret.yml new file mode 100644 index 0000000000..b3ac08e2ad --- /dev/null +++ b/Resources/Prototypes/Datasets/Names/scurret.yml @@ -0,0 +1,11 @@ +- type: localizedDataset + id: NamesFirstScurret + values: + prefix: names-scurret-first-dataset- + count: 36 + +- type: localizedDataset + id: NamesLastScurret + values: + prefix: names-scurret-last-dataset- + count: 36 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/posters.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/posters.yml index 70ddb4d7c0..0bc0d7655c 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/posters.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/posters.yml @@ -160,4 +160,5 @@ - PosterLegitSafetyMothSSD - PosterLegitOppenhopper - PosterLegitTyrone + - PosterLegitHelio chance: 1 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml b/Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml new file mode 100644 index 0000000000..458b977827 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml @@ -0,0 +1,170 @@ +# Defines the scurret, a small, agile sapient species intended for players who want to play as something similar to an ewok. +# Although based on MobBaseAncestor, this species does not have a "developed" playable species it is an ancestor to. + +- type: entity + name: scurret + id: MobBaseScurret + parent: MobBaseAncestor + abstract: true + components: + # Custom names + - type: ReplacementAccent + accent: scurret + - type: Sprite + layers: + - map: ["enum.DamageStateVisualLayers.Base"] + sprite: Mobs/Animals/scurret/scurret.rsi + state: scurret + - map: [ "enum.HumanoidVisualLayers.Handcuffs" ] + color: "#ffffff" + sprite: Objects/Misc/handcuffs.rsi + state: body-overlay-2 + visible: false + # Custom visuals for dead scurrets; be aware these do not work well with equipment. + - type: DamageStateVisuals + states: + Alive: + Base: scurret + Critical: + Base: scurret_oof + Dead: + Base: scurret_rip + # About as tough as a monkey + - type: MobThresholds + thresholds: + 0: Alive + 60: Critical + 125: Dead + - type: Temperature + heatDamageThreshold: 360 + coldDamageThreshold: 285 + currentTemperature: 310.15 + specificHeat: 42 + # Good eatin', you monster. + - type: Butcherable + butcheringType: Spike + spawned: + - id: FoodMeat + amount: 4 + # They wawa! + - type: Vocal + sounds: + Male: Scurret + Female: Scurret + Unsexed: Scurret + wilhelmProbability: 0.01 # the scug, it screams like a man + - type: Speech + speechVerb: Wawa + speechSounds: Wawa + allowedEmotes: ['Thump'] # They're 80% tail + - type: TypingIndicator + proto: moth + - type: InteractionPopup + successChance: 0.99 # Imagine not being allowed to headpat a scurret, chat + interactSuccessString: petting-success-scurret + interactFailureString: petting-failure-scurret + interactSuccessSpawn: EffectHearts + interactSuccessSound: + path: /Audio/Animals/wawa_chillin.ogg + interactFailureSound: + path: /Audio/Animals/wawa_chatter.ogg + # They nyoom + - type: MovementSpeedModifier + baseWalkSpeed: 5 # nyoom + baseSprintSpeed: 7 # NYOOOOOM + - type: MeleeWeapon + soundHit: + path: /Audio/Effects/bite.ogg + angle: 30 + animation: WeaponArcBite + damage: + types: + Piercing: 10 # NOM + - type: Inventory + speciesId: scurret + templateId: scurret + displacements: + jumpsuit: + sizeMaps: + 32: + sprite: Mobs/Animals/scurret/displacement.rsi + state: jumpsuit + id: + sizeMaps: + 32: + sprite: Mobs/Animals/scurret/displacement.rsi + state: id + gloves: + sizeMaps: + 32: + sprite: Mobs/Animals/scurret/displacement.rsi + state: gloves + neck: + sizeMaps: + 32: + sprite: Mobs/Animals/scurret/displacement.rsi + state: neck + - type: InventorySlots + - type: Food + - type: Hunger + baseDecayRate: 0.05 # They get a lil' hungy + - type: BodyEmotes # Grants them clapping and so on. + soundsId: GeneralBodyEmotes + - type: Tag + tags: + - DoorBumpOpener + - FootstepSound + - CanPilot + - VimPilot + - AnomalyHost + +# Spawnable scurrets have a random name and colour. + +- type: entity + name: scurret + description: Commonly known as Wawa, from the wetlands of Planet Wawa, these critters make up the bulk of Arnolds's Pizza's "loyal workforce". + id: MobScurret + parent: MobBaseScurret + components: + # The have a rich culture + - type: RandomMetadata + nameSegments: + - NamesFirstScurret + - NamesLastScurret + nameFormat: name-format-standard + # They come in a variety of colours + - type: RandomSprite + available: + - enum.DamageStateVisualLayers.Base: + scurret: ScurretColors + +# Emotional Support Scurrets have a ghost role and equipment. At the moment, these are intended to be used for admemes, but +# feel free to hook them into random content. + +- type: entity + name: Emotional Support Scurret + id: MobBaseEmotionalSupportScurret + parent: MobBaseScurret + abstract: true + components: + - type: Loadout + prototypes: [ EmotionalSupportScurretGear ] + - type: NpcFactionMember + factions: + - Passive + - type: GhostRole + makeSentient: true + name: ghost-role-information-emotional-support-scurret-name + description: ghost-role-information-emotional-support-scurret-description + rules: ghost-role-information-nonantagonist-rules + raffle: + settings: default + - type: GhostTakeoverAvailable + +# Spawnable ESS have a ghost role, equipment, a random name and a random colour. + +- type: entity + name: Emotional Support Scurret + id: MobEmotionalSupportScurret + parent: [MobScurret, MobBaseEmotionalSupportScurret] + description: Commonly known as Wawa, from the wetlands of Planet Wawa, these critters make up the bulk of Arnolds's Pizza's "loyal workforce". This one is here as a temp. diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index fbbdfe2b77..3de35c3519 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -1512,3 +1512,14 @@ borderColor: "#7F3300" - type: Icon state: pda-wizard + +- type: entity + parent: ClearPDA + id: ScurretPDA + description: A temporary PDA granted to scurret temps. Doesn't do much. Wawa! + components: + - type: Pda + id: VisitorIDCard + - type: Tag # Ignore Chameleon tags + tags: + - DoorBumpOpener diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml index b9a2191def..d4f8e29aa4 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml @@ -1123,6 +1123,15 @@ - type: Sprite state: poster54_legit +- type: entity + parent: PosterBase + id: PosterLegitHelio + name: Helio Logistics ad + description: "A poster advertising Helio Logistics and their adorable mascot. The slogan reads 'Come rain or shine, we deliver on time.'" + components: + - type: Sprite + state: poster55_legit + #maps - type: entity diff --git a/Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml b/Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml new file mode 100644 index 0000000000..10b9ac770d --- /dev/null +++ b/Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml @@ -0,0 +1,62 @@ +- type: inventoryTemplate + id: scurret + slots: + - name: ears + slotTexture: ears + slotFlags: EARS + stripTime: 3 + uiWindowPos: 0,2 + strippingWindowPos: 1,2 + displayName: Ears + - name: gloves + slotTexture: gloves + slotFlags: GLOVES + uiWindowPos: 0,1 + strippingWindowPos: 2,2 + displayName: Gloves + - name: head + slotTexture: head + slotFlags: HEAD + uiWindowPos: 1,3 + strippingWindowPos: 0,0 + displayName: Head + - name: mask + slotTexture: mask + slotFlags: MASK + uiWindowPos: 1,2 + strippingWindowPos: 1,1 + displayName: Mask + - name: neck + slotTexture: neck + slotFlags: NECK + uiWindowPos: 1,1 + strippingWindowPos: 0,1 + displayName: Neck + - name: jumpsuit + slotTexture: uniform + slotFlags: INNERCLOTHING + stripTime: 6 + uiWindowPos: 1,0 + strippingWindowPos: 0,2 + displayName: Jumpsuit + - name: id + slotTexture: id + fullTextureName: template_small + slotFlags: IDCARD + slotGroup: SecondHotbar + stripTime: 6 + uiWindowPos: 2,1 + strippingWindowPos: 2,4 + dependsOn: jumpsuit + displayName: ID + - name: suitstorage + slotTexture: suit_storage + slotFlags: SUITSTORAGE + slotGroup: MainHotbar + stripTime: 3 + uiWindowPos: 2,0 + strippingWindowPos: 2,5 + dependsOn: jumpsuit + dependsOnComponents: + - type: AllowSuitStorage + displayName: Suit Storage diff --git a/Resources/Prototypes/Palettes/critters.yml b/Resources/Prototypes/Palettes/critters.yml index 2f027b4b9c..af571840e0 100644 --- a/Resources/Prototypes/Palettes/critters.yml +++ b/Resources/Prototypes/Palettes/critters.yml @@ -41,3 +41,18 @@ HornA: "#d4c8bf" hornB: "#eec7ab" hornC: "#ad9584" + +- type: palette + id: ScurretColors + name: ScurretColors + colors: + Normal: "#ffffff" # Sluggy + Innocent: "#f6f439" # Monky + Angry: "#dc5864" # Hunty + Perceptive: "#222222" # Watchy + Eldritch: "#dc5864" + Fat: "#fefcab" + Violent: "#ab3430" + Speedy: "#679cfe" + Mutated: "#7824a0" + Damned: "#98e652" diff --git a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml index 0462f61fc1..1448a07cba 100644 --- a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml +++ b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml @@ -103,6 +103,13 @@ jumpsuit: ClothingUniformJumpsuitJacketMonkey id: PunPunIDCard +# Emotional Support Scurret + +- type: startingGear + id: EmotionalSupportScurretGear + equipment: + id: ScurretPDA + # DeathMatch Gear - type: startingGear diff --git a/Resources/Prototypes/SoundCollections/scurret.yml b/Resources/Prototypes/SoundCollections/scurret.yml new file mode 100644 index 0000000000..34c83ed41a --- /dev/null +++ b/Resources/Prototypes/SoundCollections/scurret.yml @@ -0,0 +1,19 @@ +- type: soundCollection + id: WawaLaugh + files: + - /Audio/Animals/wawa_mock.ogg + +- type: soundCollection + id: WawaSneeze + files: + - /Audio/Animals/wawa_achoo.ogg + +- type: soundCollection + id: WawaSigh + files: + - /Audio/Animals/wawa_depression.ogg + +- type: soundCollection + id: WawaScream + files: + - /Audio/Animals/wawa_protest.ogg diff --git a/Resources/Prototypes/Voice/speech_emote_sounds.yml b/Resources/Prototypes/Voice/speech_emote_sounds.yml index 7e06860bc6..8fb4230a5f 100644 --- a/Resources/Prototypes/Voice/speech_emote_sounds.yml +++ b/Resources/Prototypes/Voice/speech_emote_sounds.yml @@ -575,3 +575,21 @@ path: /Audio/Animals/goat_bah.ogg params: variation: 0.125 + +- type: emoteSounds + id: Scurret + params: + variation: 0.125 + sounds: + Laugh: + collection: WawaLaugh + Sneeze: + collection: WawaSneeze + Cough: + collection: MaleCoughs + Whistle: + collection: Whistles + Sigh: + collection: WawaSigh + Scream: + collection: WawaScream diff --git a/Resources/Prototypes/Voice/speech_sounds.yml b/Resources/Prototypes/Voice/speech_sounds.yml index 1c31dcc280..5f48013e87 100644 --- a/Resources/Prototypes/Voice/speech_sounds.yml +++ b/Resources/Prototypes/Voice/speech_sounds.yml @@ -177,3 +177,12 @@ path: /Audio/Animals/goat_bah.ogg exclaimSound: path: /Audio/Animals/goat_bah.ogg + +- type: speechSounds + id: Wawa + saySound: + path: /Audio/Animals/wawa_statement.ogg + askSound: + path: /Audio/Animals/wawa_question.ogg + exclaimSound: + path: /Audio/Animals/wawa_exclaim.ogg diff --git a/Resources/Prototypes/Voice/speech_verbs.yml b/Resources/Prototypes/Voice/speech_verbs.yml index f8202847fd..39f304018b 100644 --- a/Resources/Prototypes/Voice/speech_verbs.yml +++ b/Resources/Prototypes/Voice/speech_verbs.yml @@ -172,3 +172,12 @@ - chat-speech-verb-electricity-1 - chat-speech-verb-electricity-2 - chat-speech-verb-electricity-3 + +- type: speechVerb + id: Wawa + name: chat-speech-verb-name-wawa + speechVerbStrings: + - chat-speech-verb-wawa-1 + - chat-speech-verb-wawa-2 + - chat-speech-verb-wawa-3 + - chat-speech-verb-wawa-4 diff --git a/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/ears.png b/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/ears.png new file mode 100644 index 0000000000000000000000000000000000000000..40bba4fdd30e7493cd764ae9d2405cd7cec26db9 GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCil4dAc};RK&fVwUM{MfagFEpSx&3v(vxEgWn#db?cB2A>%;quHx} meYHLWbRl1AglC$sFM}44%>l$9a4C2)i1Kvxb6Mw<&;$To6jAyB literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/eyes.png b/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..0c6b045da700c6ee60413d89131bb74aa854e5e5 GIT binary patch literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCil;JY5_^D&pQ=*vQDBz|*kd)ziN*;`&u`8WOD#92wV!D45B<;{an^LB{Ts5&vZv^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/gloves.png b/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/gloves.png new file mode 100644 index 0000000000000000000000000000000000000000..70ec1726d0c1376f58eb960a1ae3f827239a1899 GIT binary patch literal 432 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WChAOc)B=-RK&fVnb_TIAmEa0e`o);DZdWeG6{PWze_crl4bQ-!Dp?=-Q|(I zu|Y?F`B>ZS`swHv@&4u09j(TF_0E@s#1mo{f7rU4&5&{FiERL2 z^7g)aGkP-LF`O4xNVZMXF=3ss#e-pzSQVqbZ18_>0VS|9O%~15$=&wVx^me$s%EFG z&qw+SyiCg9v;B7Z29xI(^1c^vYaETLWLp}cZOh}^xb20p$`)6*u62dn5ms;CP8O;< z`mQldW*2jn&^m`DKh9hb`Jtx5;MvWhkU5b7=F(un+Pi=IH#F>#+i*<%q)?xmh%X~d z4(udO2QEbk&zItHIbNl`I?oJ&Vat~q;hE;^%b*2ha{w_2Tne5HqC8#wT-G@yGywns Ca-FCE literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/hand.png b/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/hand.png new file mode 100644 index 0000000000000000000000000000000000000000..4611d66f51be4ab1d7ac813370355bf0bffaad7f GIT binary patch literal 426 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WChAudAc};RK&fVd7H1vK)|JY|65g;FS|`=`}78KY*}ia=9hHr!xOn%OZ5)d z7U_pK-oDEF zHY>$PI8@l>k`kjwlnTSlGj}d-V=ohY(0yJv@$F3iQh{6CoDM+A6DofKmR>7RZM_@j z@a|FY|F60ikBXfL-E(nI!!vQ%vI%7}A#7r65tw?gJb1{Gc3uMCPV zg>C5vrX~Gr{hED=Lg2GP{7|Z6&wZ3#sZCQnLG=rp% zw)VkyHE-^kZvm>2@p3qmqZrY^8}LV9=@Wxg&iIly3941BTLSi|r>5{-`&-1nx&CXu?LRl?@sIf%WaUNnowW|D zuAk`#GvV|~hSkg8T82L~Qcw8WCSN>l9ZUwN+1)k=B+A$FD>pX2*i!{`K3{5tXPU1s ZgBFm@0mLA1DR?r7@^tlcS?83{1OPswWat0@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/id.png b/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/id.png new file mode 100644 index 0000000000000000000000000000000000000000..6af79fb8a3b68afbf6bf7e60bb4029c15e526860 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCil)d%8G=RK&fVX~=chf#*O?Z8>w}v-@Y2yy7QFtLT186R7y~E9UducFTL4 zC7#>NzyJR{ceWPli z{gMU0ax`;XbP0l+XkK DCMj1_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/jumpsuit.png b/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/jumpsuit.png new file mode 100644 index 0000000000000000000000000000000000000000..2481e3854097c15b886c83fc2eaa5ce9eb30dc92 GIT binary patch literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%znLS+`Ln`LH zy>(ZpNkO3P;rV61#n#<9*Hz{e>vFyAyJhpnZ|t)a-8c(8H;bLG`Tk?2(0-%+sYR^% zT|PfQ-@H|mpE^-f^rQCYYMa@UcV--2{ZTsnPSN^nQ4iDC{j)yi_3+-+=}V4pEcqwT z+u^4m1tM;J;LWh(2%W$4g~FCkJ=*_1SU0e_s0-x&VEgfpXKvjkcAyg(7p=fS?83{1OWGLk0<~D literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/mask.png b/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/mask.png new file mode 100644 index 0000000000000000000000000000000000000000..c8e757e2e544916f89c0c7aaa91c60fca5787a94 GIT binary patch literal 264 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 Date: Fri, 20 Jun 2025 23:40:16 +0000 Subject: [PATCH 013/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 9c66f1206c..fbc8c87591 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Will-Oliver-Br - changes: - - message: Now you can see if there's a beaker in the chemmaster. - type: Tweak - id: 8185 - time: '2025-04-14T23:27:47.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36472 - author: EmoGarbage404, Fildrance changes: - message: new ui for analysis console, new ui for node scanner. Artifact node activation @@ -3909,3 +3902,14 @@ id: 8697 time: '2025-06-20T22:41:11.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38439 +- author: FairlySadPanda + changes: + - message: A new poster. + type: Add + - message: A new (very expensive) crate to order from cargo, worth 25,000 spesos + and containing a hydrated scurret. The hydrated scurret is a ghost role - a + non-antagonistic emotional support scurret. + type: Add + id: 8698 + time: '2025-06-20T23:39:08.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38218 From 6e59f748e0025229387747d0d5bdd3c2ccd7f239 Mon Sep 17 00:00:00 2001 From: Myra Date: Sat, 21 Jun 2025 02:32:31 +0200 Subject: [PATCH 014/191] Fail if we attempt to run publish on master (#38431) * Fail if we attempt to run publish on master BREAKING: FORKS REMOVE THIS * Update publish.yml --- .github/workflows/publish.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0294395632..bb50164b34 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,6 +14,10 @@ jobs: runs-on: ubuntu-latest steps: + - name: Fail if we are attempting to run on the master branch + if: ${{GITHUB.REF_NAME == 'master'}} && github.repository == 'space-wizards/space-station-14' + run: exit 1 + - name: Install dependencies run: sudo apt-get install -y python3-paramiko python3-lxml From 2e5ddb4bb55b4f906c9b61b5d3a3695b77aa9c56 Mon Sep 17 00:00:00 2001 From: crazybrain23 <44417085+crazybrain23@users.noreply.github.com> Date: Sat, 21 Jun 2025 02:13:25 +0100 Subject: [PATCH 015/191] Fix styling for Admin Menu lists (#38261) * remove styleclass from playertab 'button' lines * cvar, fix button-style header alignment * Fix requested changes And also the extra cvar things I was meant to remove in the upstream merge * Tiny tweaks --------- Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com> --- .../Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs | 2 +- .../Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs index 4b50771b0f..59e0e6040b 100644 --- a/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/ObjectsTab/ObjectsTab.xaml.cs @@ -132,8 +132,8 @@ public sealed partial class ObjectsTab : Control entry.OnTeleport += TeleportTo; entry.OnDelete += Delete; button.ToolTip = $"{info.Name}, {info.Entity}"; - button.AddChild(entry); + button.StyleClasses.Clear(); } private bool DataFilterCondition(string filter, ListData listData) diff --git a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs index 2a25d9e0aa..5a75e7223f 100644 --- a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTab.xaml.cs @@ -172,6 +172,7 @@ public sealed partial class PlayerTab : Control _playerTabSymbolSetting); button.AddChild(entry); button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}"; + button.StyleClasses.Clear(); } ///

From 7a63d1729f6e4c51f9af7407405bb50e04370c2e Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 21 Jun 2025 01:14:32 +0000 Subject: [PATCH 016/191] Automatic changelog update --- Resources/Changelog/Admin.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 0ea15cf9bf..966ee26e7f 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1179,5 +1179,13 @@ Entries: id: 142 time: '2025-06-16T10:25:44.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38317 +- author: Errant, crazybrain + changes: + - message: The admin player and object tabs now show entries in the correct visual + style. + type: Fix + id: 143 + time: '2025-06-21T01:13:25.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38261 Name: Admin Order: 2 From 357763c55e0ff22cf8c4728c40696d5b2c90b707 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 20 Jun 2025 21:31:32 -0400 Subject: [PATCH 017/191] Add interaction test for retractable arm blade (#38452) * Add interaction test for retractable arm blade * Update for HandsSystem refactor * Revert "Update for HandsSystem refactor" This reverts commit e01bb9a7e318dd916240d57b30f48af9594bff1f. * Combine WaitAssertion blocks --- .../Actions/RetractableItemActionTest.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Content.IntegrationTests/Tests/Actions/RetractableItemActionTest.cs diff --git a/Content.IntegrationTests/Tests/Actions/RetractableItemActionTest.cs b/Content.IntegrationTests/Tests/Actions/RetractableItemActionTest.cs new file mode 100644 index 0000000000..76c1a0367c --- /dev/null +++ b/Content.IntegrationTests/Tests/Actions/RetractableItemActionTest.cs @@ -0,0 +1,65 @@ +#nullable enable +using Content.IntegrationTests.Tests.Interaction; +using Content.Shared.Actions; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.RetractableItemAction; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests.Actions; + +public sealed class RetractableItemActionTest : InteractionTest +{ + private static readonly EntProtoId ArmBladeActionProtoId = "ActionRetractableItemArmBlade"; + + /// + /// Gives the player the arm blade action, then activates it and makes sure they are given the blade. + /// Afterwards, uses the action again to retract the blade and makes sure their hand is empty. + /// + [Test] + public async Task ArmBladeActivateDeactivateTest() + { + var actionsSystem = Server.System(); + var handsSystem = Server.System(); + var playerUid = SEntMan.GetEntity(Player); + + await Server.WaitAssertion(() => + { + // Make sure the player's hand starts empty + var heldItem = Hands.ActiveHandEntity; + Assert.That(heldItem, Is.Null, $"Player is holding an item ({SEntMan.ToPrettyString(heldItem)}) at start of test."); + + // Inspect the action prototype to find the item it spawns + var armBladeActionProto = ProtoMan.Index(ArmBladeActionProtoId); + + // Find the component + Assert.That(armBladeActionProto.TryGetComponent(out var actionComp, SEntMan.ComponentFactory)); + // Get the item protoId from the component + var spawnedProtoId = actionComp!.SpawnedPrototype; + + // Add the action to the player + var actionUid = actionsSystem.AddAction(playerUid, ArmBladeActionProtoId); + // Make sure the player has the action now + Assert.That(actionUid, Is.Not.Null, "Failed to add action to player."); + var actionEnt = actionsSystem.GetAction(actionUid); + + // Make sure the player's hand is still empty + heldItem = Hands.ActiveHandEntity; + Assert.That(heldItem, Is.Null, $"Player is holding an item ({SEntMan.ToPrettyString(heldItem)}) after adding action."); + + // Activate the arm blade + actionsSystem.PerformAction(ToServer(Player), actionEnt!.Value); + + // Make sure the player is now holding the expected item + heldItem = Hands.ActiveHandEntity; + Assert.That(heldItem, Is.Not.Null, $"Expected player to be holding {spawnedProtoId} but was holding nothing."); + AssertPrototype(spawnedProtoId, SEntMan.GetNetEntity(heldItem)); + + // Use the action again to retract the arm blade + actionsSystem.PerformAction(ToServer(Player), actionEnt.Value); + + // Make sure the player's hand is empty again + heldItem = Hands.ActiveHandEntity; + Assert.That(heldItem, Is.Null, $"Player is still holding an item ({SEntMan.ToPrettyString(heldItem)}) after second use."); + }); + } +} From 640c984ab21f46ee138f08695f4a656d6906ffdb Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Sat, 21 Jun 2025 02:32:30 -0400 Subject: [PATCH 018/191] Make... sloths... speak... slowly... (#38142) * Add SlowAccent * Apply SlowAccent to sloths * xmldocs --- .../Speech/Components/SlowAccentComponent.cs | 7 ++++ .../Speech/EntitySystems/SlowAccentSystem.cs | 41 +++++++++++++++++++ .../Prototypes/Entities/Mobs/NPCs/animals.yml | 1 + 3 files changed, 49 insertions(+) create mode 100644 Content.Server/Speech/Components/SlowAccentComponent.cs create mode 100644 Content.Server/Speech/EntitySystems/SlowAccentSystem.cs diff --git a/Content.Server/Speech/Components/SlowAccentComponent.cs b/Content.Server/Speech/Components/SlowAccentComponent.cs new file mode 100644 index 0000000000..1222475cb2 --- /dev/null +++ b/Content.Server/Speech/Components/SlowAccentComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server.Speech.Components; + +/// +/// Makes... the... entity... talk... like... this... +/// +[RegisterComponent] +public sealed partial class SlowAccentComponent : Component; diff --git a/Content.Server/Speech/EntitySystems/SlowAccentSystem.cs b/Content.Server/Speech/EntitySystems/SlowAccentSystem.cs new file mode 100644 index 0000000000..5a94b0a4d6 --- /dev/null +++ b/Content.Server/Speech/EntitySystems/SlowAccentSystem.cs @@ -0,0 +1,41 @@ +using System.Text.RegularExpressions; +using Content.Server.Speech.Components; + +namespace Content.Server.Speech.EntitySystems; + +public sealed partial class SlowAccentSystem : EntitySystem +{ + /// + /// Matches whitespace characters or commas (with or without a space after them). + /// + private static readonly Regex WordEndings = new("\\s|, |,"); + + /// + /// Matches the end of the string only if the last character is a "word" character. + /// + private static readonly Regex NoFinalPunctuation = new("\\w\\z"); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAccentGet); + } + + private void OnAccentGet(Entity ent, ref AccentGetEvent args) + { + args.Message = Accentuate(ent, args.Message); + } + + public string Accentuate(Entity ent, string message) + { + // Add... some... delay... between... each... word + message = WordEndings.Replace(message, "... "); + + // Add "..." to the end, if the last character is part of a word... + if (NoFinalPunctuation.IsMatch(message)) + message += "..."; + + return message; + } +} diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index f57d87ed2f..967dda00db 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -3266,6 +3266,7 @@ - type: Grammar attributes: gender: epicene + - type: SlowAccent - type: Tag tags: - VimPilot From 5d119543ecb6b63faf3101916828e2390e812795 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 21 Jun 2025 06:33:37 +0000 Subject: [PATCH 019/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index fbc8c87591..76ec6ad070 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,15 +1,4 @@ Entries: -- author: EmoGarbage404, Fildrance - changes: - - message: new ui for analysis console, new ui for node scanner. Artifact node activation - is now different. - type: Add - - message: artifacts cannot create massive distruction events, become guns or music - instruments. Sentient artifacts cannot activate nodes. - type: Remove - id: 8186 - time: '2025-04-15T00:34:53.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33370 - author: EmoGarbage404 changes: - message: Skeletons once again become living skulls on death. @@ -3913,3 +3902,10 @@ id: 8698 time: '2025-06-20T23:39:08.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38218 +- author: Tayrtahn + changes: + - message: Sloths... now... speak... slowly... + type: Add + id: 8699 + time: '2025-06-21T06:32:30.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38142 From a0544fdbf202fbbe8193edd63ef9c4e2043f086c Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Sat, 21 Jun 2025 05:06:49 -0400 Subject: [PATCH 020/191] Make gas tank UI a bit more network-happy (#38184) * fix: network gas tank output pressure * fix: don't overwrite gas tank output pressure during editing --- .../UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs | 4 +++- Content.Shared/Atmos/Components/GasTankComponent.cs | 2 +- Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs index 638df36504..2b06eb7f8d 100644 --- a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs +++ b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankWindow.cs @@ -207,7 +207,9 @@ public sealed class GasTankWindow _btnInternals.Disabled = !canConnectInternals; _lblInternals.SetMarkup(Loc.GetString("gas-tank-window-internal-text", ("status", Loc.GetString(internalsConnected ? "gas-tank-window-internal-connected" : "gas-tank-window-internal-disconnected")))); - _spbPressure.Value = outputPressure; + if (!_spbPressure.HasKeyboardFocus()) + // Don't update release pressure if we're currently editing it + _spbPressure.Value = outputPressure; } protected override void FrameUpdate(FrameEventArgs args) diff --git a/Content.Shared/Atmos/Components/GasTankComponent.cs b/Content.Shared/Atmos/Components/GasTankComponent.cs index 805874af33..7ca6098555 100644 --- a/Content.Shared/Atmos/Components/GasTankComponent.cs +++ b/Content.Shared/Atmos/Components/GasTankComponent.cs @@ -44,7 +44,7 @@ public sealed partial class GasTankComponent : Component, IGasMixtureHolder /// /// Distributed pressure. /// - [DataField] + [DataField, AutoNetworkedField] public float OutputPressure = DefaultOutputPressure; /// diff --git a/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs b/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs index 27c3d16f29..8c7ebe8332 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs @@ -52,6 +52,7 @@ public abstract class SharedGasTankSystem : EntitySystem ent.Comp.OutputPressure = pressure; Dirty(ent); + UpdateUserInterface(ent); } public virtual void UpdateUserInterface(Entity ent) From 3b6f4c5b3ea3cb0d5338a437a90242a9836682ee Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Sat, 21 Jun 2025 11:19:27 +0200 Subject: [PATCH 021/191] allow to publish news without ui (#35262) * allow to publish news without ui * the hell was that * apply * apply review * sure Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * okay --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../MassMedia/Systems/NewsSystem.cs | 71 +++++++++++++------ 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/Content.Server/MassMedia/Systems/NewsSystem.cs b/Content.Server/MassMedia/Systems/NewsSystem.cs index d3cd94f656..0abaa7fb16 100644 --- a/Content.Server/MassMedia/Systems/NewsSystem.cs +++ b/Content.Server/MassMedia/Systems/NewsSystem.cs @@ -133,7 +133,7 @@ public sealed class NewsSystem : SharedNewsSystem _adminLogger.Add( LogType.Chat, LogImpact.Medium, $"{ToPrettyString(msg.Actor):actor} deleted news article {article.Title} by {article.Author}: {article.Content}" - ); + ); articles.RemoveAt(msg.ArticleNum); _audio.PlayPvs(ent.Comp.ConfirmSound, ent); @@ -164,9 +164,6 @@ public sealed class NewsSystem : SharedNewsSystem if (!ent.Comp.PublishEnabled) return; - if (!TryGetArticles(ent, out var articles)) - return; - if (!CanUse(msg.Actor, ent.Owner)) return; @@ -180,32 +177,62 @@ public sealed class NewsSystem : SharedNewsSystem var title = msg.Title.Trim(); var content = msg.Content.Trim(); - var article = new NewsArticle + if (TryAddNews(ent, title, content, out var article, authorName, msg.Actor)) + { + _audio.PlayPvs(ent.Comp.ConfirmSound, ent); + + _chatManager.SendAdminAnnouncement(Loc.GetString("news-publish-admin-announcement", + ("actor", msg.Actor), + ("title", article.Value.Title), + ("author", article.Value.Author ?? Loc.GetString("news-read-ui-no-author")) + )); + } + } + + /// + /// Set the alert level based on the station's entity ID. + /// + /// Entity on the station to which news will be added. + /// Title of the news article. + /// Content of the news article. + /// Author of the news article. + /// Entity which caused the news article to publish. Used for admin logs. + public bool TryAddNews(EntityUid uid, string title, string content, [NotNullWhen(true)] out NewsArticle? article, string? author = null, EntityUid? actor = null) + { + if (!TryGetArticles(uid, out var articles)) + { + article = null; + return false; + } + + article = new NewsArticle { Title = title.Length <= MaxTitleLength ? title : $"{title[..MaxTitleLength]}...", Content = content.Length <= MaxContentLength ? content : $"{content[..MaxContentLength]}...", - Author = authorName, + Author = author, ShareTime = _ticker.RoundDuration() }; - _audio.PlayPvs(ent.Comp.ConfirmSound, ent); + articles.Add(article.Value); - _adminLogger.Add( - LogType.Chat, - LogImpact.Medium, - $"{ToPrettyString(msg.Actor):actor} created news article {article.Title} by {article.Author}: {article.Content}" - ); + if (actor != null) + { + _adminLogger.Add( + LogType.Chat, + LogImpact.Medium, + $"{ToPrettyString(actor):actor} created news article {article.Value.Title} by {article.Value.Author}: {article.Value.Content}"); + } + else + { + _adminLogger.Add( + LogType.Chat, + LogImpact.Medium, + $"Created news article {article.Value.Title} by {article.Value.Author}: {article.Value.Content}"); + } - _chatManager.SendAdminAnnouncement(Loc.GetString("news-publish-admin-announcement", - ("actor", msg.Actor), - ("title", article.Title), - ("author", article.Author ?? Loc.GetString("news-read-ui-no-author")) - )); - - articles.Add(article); - - var args = new NewsArticlePublishedEvent(article); + var args = new NewsArticlePublishedEvent(article.Value); var query = EntityQueryEnumerator(); + while (query.MoveNext(out var readerUid, out _)) { RaiseLocalEvent(readerUid, ref args); @@ -215,6 +242,8 @@ public sealed class NewsSystem : SharedNewsSystem Task.Run(async () => await SendArticleToDiscordWebhook(article)); UpdateWriterDevices(); + + return true; } #endregion From 27a4c1955cdf0a20345835829f2f548a1ce66a8e Mon Sep 17 00:00:00 2001 From: Just-a-Unity-Dev <67359748+Just-a-Unity-Dev@users.noreply.github.com> Date: Sat, 21 Jun 2025 17:19:57 +0800 Subject: [PATCH 022/191] add some documentation for visiting entity (#38475) * add some documentation for visiting entity to prevent someone from spending 5 minutes diving into the codebase * reviews Co-authored-by: LaCumbiaDelCoronavirus <90893484+LaCumbiaDelCoronavirus@users.noreply.github.com> --------- Co-authored-by: LaCumbiaDelCoronavirus <90893484+LaCumbiaDelCoronavirus@users.noreply.github.com> --- Content.Shared/Mind/MindComponent.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Content.Shared/Mind/MindComponent.cs b/Content.Shared/Mind/MindComponent.cs index 7864d61a8c..57cd628f90 100644 --- a/Content.Shared/Mind/MindComponent.cs +++ b/Content.Shared/Mind/MindComponent.cs @@ -55,6 +55,9 @@ public sealed partial class MindComponent : Component [ViewVariables] public bool IsVisitingEntity => VisitingEntity != null; + /// + /// The entity that this mind may be currently visiting. Used, for example, to allow admin ghosting to not make the owner's body catatonic, as opposed to when normally ghosting. + /// [DataField, AutoNetworkedField, Access(typeof(SharedMindSystem))] public EntityUid? VisitingEntity { get; set; } From 54cb731147f59294b82654b0526c540c797c46a3 Mon Sep 17 00:00:00 2001 From: Whatstone <166147148+whatston3@users.noreply.github.com> Date: Sat, 21 Jun 2025 05:41:04 -0400 Subject: [PATCH 023/191] SignalSwitchSystem: Check button is locked before toggling (#38474) Check button is locked before toggling --- Content.Server/DeviceLinking/Systems/SignalSwitchSystem.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Content.Server/DeviceLinking/Systems/SignalSwitchSystem.cs b/Content.Server/DeviceLinking/Systems/SignalSwitchSystem.cs index 67fad29d93..8a557049bc 100644 --- a/Content.Server/DeviceLinking/Systems/SignalSwitchSystem.cs +++ b/Content.Server/DeviceLinking/Systems/SignalSwitchSystem.cs @@ -1,6 +1,7 @@ using Content.Server.DeviceLinking.Components; using Content.Server.DeviceNetwork; using Content.Shared.Interaction; +using Content.Shared.Lock; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -10,6 +11,7 @@ public sealed class SignalSwitchSystem : EntitySystem { [Dependency] private readonly DeviceLinkSystem _deviceLink = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly LockSystem _lock = default!; public override void Initialize() { @@ -29,6 +31,9 @@ public sealed class SignalSwitchSystem : EntitySystem if (args.Handled || !args.Complex) return; + if (_lock.IsLocked(uid)) + return; + comp.State = !comp.State; _deviceLink.InvokePort(uid, comp.State ? comp.OnPort : comp.OffPort); From b4515e7b588a0c7878db66190ee878ddd6acfd9c Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 21 Jun 2025 09:42:11 +0000 Subject: [PATCH 024/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 76ec6ad070..22f393456f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: EmoGarbage404 - changes: - - message: Skeletons once again become living skulls on death. - type: Fix - id: 8187 - time: '2025-04-15T03:38:31.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36571 - author: Crazydave91920 changes: - message: Fixed Zookeeper not being on the double barrel shotgun contraband list @@ -3909,3 +3902,10 @@ id: 8699 time: '2025-06-21T06:32:30.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38142 +- author: whatston3 + changes: + - message: Locked buttons must now be unlocked to trigger. + type: Fix + id: 8700 + time: '2025-06-21T09:41:04.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38474 From 0d346840da52fdc7e091d93bf71187ad31f1e8c8 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 21 Jun 2025 12:37:08 +0200 Subject: [PATCH 025/191] Fix compile (#38477) --- Content.Server/MassMedia/Systems/NewsSystem.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Content.Server/MassMedia/Systems/NewsSystem.cs b/Content.Server/MassMedia/Systems/NewsSystem.cs index 0abaa7fb16..6c4a62ff09 100644 --- a/Content.Server/MassMedia/Systems/NewsSystem.cs +++ b/Content.Server/MassMedia/Systems/NewsSystem.cs @@ -239,12 +239,18 @@ public sealed class NewsSystem : SharedNewsSystem } if (_webhookSendDuringRound) - Task.Run(async () => await SendArticleToDiscordWebhook(article)); + AddNewsSendWebhook(article.Value); UpdateWriterDevices(); return true; } + + private async void AddNewsSendWebhook(NewsArticle article) + { + await Task.Run(async () => await SendArticleToDiscordWebhook(article)); + } + #endregion #region Reader Event Handlers From 686016098a88d67c393724691b0b8469c18f6e62 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sat, 21 Jun 2025 15:11:17 +0200 Subject: [PATCH 026/191] Fix wallmount interaction (#38111) --- .../Interaction/SharedInteractionSystem.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 91b3ffea0d..2c75cef132 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -747,7 +747,7 @@ namespace Content.Shared.Interaction var inRange = true; MapCoordinates originPos = default; var targetPos = _transform.ToMapCoordinates(otherCoordinates); - Angle targetRot = default; + Angle targetRot = _transform.GetWorldRotation(otherCoordinates.EntityId) + otherAngle; // So essentially: // 1. If fixtures available check nearest point. We take in coordinates / angles because we might want to use a lag compensated position @@ -766,8 +766,7 @@ namespace Content.Shared.Interaction { var (worldPosA, worldRotA) = _transform.GetWorldPositionRotation(origin.Comp); var xfA = new Transform(worldPosA, worldRotA); - var parentRotB = _transform.GetWorldRotation(otherCoordinates.EntityId); - var xfB = new Transform(targetPos.Position, parentRotB + otherAngle); + var xfB = new Transform(targetPos.Position, targetRot); // Different map or the likes. if (!_broadphase.TryGetNearest( @@ -809,8 +808,6 @@ namespace Content.Shared.Interaction else { originPos = _transform.GetMapCoordinates(origin, origin); - var otherParent = (other.Comp ?? Transform(other)).ParentUid; - targetRot = otherParent.IsValid() ? Transform(otherParent).LocalRotation + otherAngle : otherAngle; } // Do a raycast to check if relevant @@ -851,7 +848,7 @@ namespace Content.Shared.Interaction /// if the target entity is a wallmount we ignore all other entities on the tile. /// private Ignored GetPredicate( - MapCoordinates origin, + MapCoordinates originCoords, EntityUid target, MapCoordinates targetCoords, Angle targetRotation, @@ -871,7 +868,7 @@ namespace Content.Shared.Interaction if (target == otherEnt || !_physicsQuery.TryComp(otherEnt, out var otherBody) || !otherBody.CanCollide || - ((int) collisionMask & otherBody.CollisionLayer) == 0x0) + ((int)collisionMask & otherBody.CollisionLayer) == 0x0) { continue; } @@ -888,7 +885,7 @@ namespace Content.Shared.Interaction ignoreAnchored = true; else { - var angle = Angle.FromWorldVec(origin.Position - targetCoords.Position); + var angle = Angle.FromWorldVec(originCoords.Position - targetCoords.Position); var angleDelta = (wallMount.Direction + targetRotation - angle).Reduced().FlipPositive(); ignoreAnchored = angleDelta < wallMount.Arc / 2 || Math.Tau - angleDelta < wallMount.Arc / 2; } From a481f0bbbc268cc510a0b56aa6a81ef756715fc6 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 21 Jun 2025 13:12:24 +0000 Subject: [PATCH 027/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 22f393456f..679c9ac740 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Crazydave91920 - changes: - - message: Fixed Zookeeper not being on the double barrel shotgun contraband list - type: Fix - id: 8188 - time: '2025-04-15T18:27:29.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36583 - author: Minemoder5000 changes: - message: The Syndicate Elite Hardsuit and Cybersun Juggernaut Suit have been restricted @@ -3909,3 +3902,10 @@ id: 8700 time: '2025-06-21T09:41:04.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38474 +- author: slarticodefast + changes: + - message: Fixed wallmount interaction rotation. + type: Fix + id: 8701 + time: '2025-06-21T13:11:17.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38111 From 092f88c16fa9768121a3b54afb5e9ea124b51f34 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sat, 21 Jun 2025 15:40:56 +0200 Subject: [PATCH 028/191] Fix debug asserts when unequipping items (#38274) --- .../Clothing/Components/ClothingComponent.cs | 41 +++++-------------- .../Clothing/EntitySystems/ClothingSystem.cs | 17 +++----- .../Clothing/EntitySystems/MaskSystem.cs | 6 +-- 3 files changed, 19 insertions(+), 45 deletions(-) diff --git a/Content.Shared/Clothing/Components/ClothingComponent.cs b/Content.Shared/Clothing/Components/ClothingComponent.cs index 260af210e0..ff1d422189 100644 --- a/Content.Shared/Clothing/Components/ClothingComponent.cs +++ b/Content.Shared/Clothing/Components/ClothingComponent.cs @@ -11,12 +11,11 @@ namespace Content.Shared.Clothing.Components; /// /// This handles entities which can be equipped. /// -[NetworkedComponent] -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] [Access(typeof(ClothingSystem), typeof(InventorySystem))] public sealed partial class ClothingComponent : Component { - [DataField("clothingVisuals")] + [DataField] public Dictionary> ClothingVisuals = new(); /// @@ -25,8 +24,7 @@ public sealed partial class ClothingComponent : Component [DataField] public string? MappedLayer; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("quickEquip")] + [DataField] public bool QuickEquip = true; /// @@ -36,22 +34,18 @@ public sealed partial class ClothingComponent : Component /// /// Note that this may be a combination of different slot flags, not a singular bit. /// - [ViewVariables(VVAccess.ReadWrite)] [DataField(required: true)] [Access(typeof(ClothingSystem), typeof(InventorySystem), Other = AccessPermissions.ReadExecute)] public SlotFlags Slots = SlotFlags.NONE; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("equipSound")] + [DataField] public SoundSpecifier? EquipSound; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("unequipSound")] + [DataField] public SoundSpecifier? UnequipSound; [Access(typeof(ClothingSystem))] - [ViewVariables(VVAccess.ReadWrite)] - [DataField("equippedPrefix")] + [DataField, AutoNetworkedField] public string? EquippedPrefix; /// @@ -59,11 +53,9 @@ public sealed partial class ClothingComponent : Component /// useful when prototyping INNERCLOTHING items into OUTERCLOTHING items without duplicating/modifying RSIs etc. /// [Access(typeof(ClothingSystem))] - [ViewVariables(VVAccess.ReadWrite)] - [DataField("equippedState")] + [DataField, AutoNetworkedField] public string? EquippedState; - [ViewVariables(VVAccess.ReadWrite)] [DataField("sprite")] public string? RsiPath; @@ -72,7 +64,7 @@ public sealed partial class ClothingComponent : Component /// Note that this being non-null does not mean the clothing is considered "worn" or "equipped" unless the slot /// satisfies the flags. /// - [DataField] + [DataField, AutoNetworkedField] public string? InSlot; // TODO CLOTHING // Maybe keep this null unless its in a valid slot? @@ -82,16 +74,16 @@ public sealed partial class ClothingComponent : Component /// /// Slot flags of the slot the clothing is currently in. See also . /// - [DataField] + [DataField, AutoNetworkedField] public SlotFlags? InSlotFlag; // TODO CLOTHING // Maybe keep this null unless its in a valid slot? // And when doing this, combine InSlot and InSlotFlag, as it'd be a breaking change for downstreams anyway - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan EquipDelay = TimeSpan.Zero; - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan UnequipDelay = TimeSpan.Zero; /// @@ -102,17 +94,6 @@ public sealed partial class ClothingComponent : Component public TimeSpan StripDelay = TimeSpan.Zero; } -[Serializable, NetSerializable] -public sealed class ClothingComponentState : ComponentState -{ - public string? EquippedPrefix; - - public ClothingComponentState(string? equippedPrefix) - { - EquippedPrefix = equippedPrefix; - } -} - public enum ClothingMask : byte { NoMask = 0, diff --git a/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs index 10417045ff..6ebaa94e2e 100644 --- a/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs @@ -21,8 +21,7 @@ public abstract class ClothingSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnGetState); - SubscribeLocalEvent(OnHandleState); + SubscribeLocalEvent(AfterAutoHandleState); SubscribeLocalEvent(OnGotEquipped); SubscribeLocalEvent(OnGotUnequipped); @@ -84,6 +83,7 @@ public abstract class ClothingSystem : EntitySystem { component.InSlot = args.Slot; component.InSlotFlag = args.SlotFlags; + Dirty(uid, component); if ((component.Slots & args.SlotFlags) == SlotFlags.NONE) return; @@ -108,19 +108,12 @@ public abstract class ClothingSystem : EntitySystem component.InSlot = null; component.InSlotFlag = null; + Dirty(uid, component); } - private void OnGetState(EntityUid uid, ClothingComponent component, ref ComponentGetState args) + private void AfterAutoHandleState(Entity ent, ref AfterAutoHandleStateEvent args) { - args.State = new ClothingComponentState(component.EquippedPrefix); - } - - private void OnHandleState(EntityUid uid, ClothingComponent component, ref ComponentHandleState args) - { - if (args.Current is not ClothingComponentState state) - return; - - SetEquippedPrefix(uid, state.EquippedPrefix, component); + _itemSys.VisualsChanged(ent.Owner); } private void OnEquipDoAfter(Entity ent, ref ClothingEquipDoAfterEvent args) diff --git a/Content.Shared/Clothing/EntitySystems/MaskSystem.cs b/Content.Shared/Clothing/EntitySystems/MaskSystem.cs index 4f89b111bd..30e00faf0a 100644 --- a/Content.Shared/Clothing/EntitySystems/MaskSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/MaskSystem.cs @@ -75,13 +75,13 @@ public sealed class MaskSystem : EntitySystem private void ToggleMaskComponents(EntityUid uid, MaskComponent mask, EntityUid wearer, string? equippedPrefix = null, bool isEquip = false) { Dirty(uid, mask); - if (mask.ToggleActionEntity is {} action) + if (mask.ToggleActionEntity is { } action) _actionSystem.SetToggled(action, mask.IsToggled); - var maskEv = new ItemMaskToggledEvent((wearer, mask), wearer); + var maskEv = new ItemMaskToggledEvent((uid, mask), wearer); RaiseLocalEvent(uid, ref maskEv); - var wearerEv = new WearerMaskToggledEvent((wearer, mask)); + var wearerEv = new WearerMaskToggledEvent((uid, mask)); RaiseLocalEvent(wearer, ref wearerEv); } From f1d60b44e190c70b01ebf1946010e8504bd77e8f Mon Sep 17 00:00:00 2001 From: Prole <172158352+Prole0@users.noreply.github.com> Date: Sat, 21 Jun 2025 07:52:36 -0700 Subject: [PATCH 029/191] Flask Visual Overhaul & YML Organizing (#38032) * Adding Back Baseline V1 Changes Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> * Readability Change Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> * icon_open implementation, tidy up & attributions (fix lith/shiny flask ugly af open sprites) Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> * good enough Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> * No mo closed spills Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> * Inhands & Attributions Part 1 Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> * Inhands & Attributions Part 2 Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> * Flask Sounds & Attributions Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> * Cap flask inhands change Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> * Lith flask Inhand Changes Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> * Spacing * Fix? * Man... * Attributions Back in... * Im loosing it.. * REAL! * :/ * Again... * again :/ * Plz??? --------- Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> --- Resources/Audio/Items/attributions.yml | 14 +++- Resources/Audio/Items/flask_close1.ogg | Bin 0 -> 20371 bytes Resources/Audio/Items/flask_open1.ogg | Bin 0 -> 34411 bytes .../Consumable/Drinks/drinks_flasks.yml | 78 ++++++++++-------- .../Consumable/Drinks/drinks_special.yml | 30 +++++++ .../SoundCollections/drink_close_sounds.yml | 5 ++ .../SoundCollections/drink_open_sounds.yml | 5 ++ .../Drinks/barflask.rsi/icon_open.png | Bin 0 -> 517 bytes .../Consumable/Drinks/barflask.rsi/meta.json | 5 +- .../Drinks/detflask.rsi/icon_open.png | Bin 0 -> 540 bytes .../Drinks/detflask.rsi/inhand-left.png | Bin 0 -> 502 bytes .../Drinks/detflask.rsi/inhand-right.png | Bin 0 -> 471 bytes .../Consumable/Drinks/detflask.rsi/meta.json | 26 +++++- .../Consumable/Drinks/flask.rsi/icon_open.png | Bin 0 -> 873 bytes .../Drinks/flask.rsi/inhand-left.png | Bin 0 -> 488 bytes .../Drinks/flask.rsi/inhand-right.png | Bin 0 -> 484 bytes .../Consumable/Drinks/flask.rsi/meta.json | 26 +++++- .../Drinks/flask_old.rsi/inhand-left.png | Bin 0 -> 544 bytes .../Drinks/flask_old.rsi/inhand-right.png | Bin 0 -> 534 bytes .../Consumable/Drinks/flask_old.rsi/meta.json | 23 +++++- .../Consumable/Drinks/hosflask.rsi/icon.png | Bin 573 -> 612 bytes .../Drinks/hosflask.rsi/icon_open.png | Bin 0 -> 594 bytes .../Drinks/hosflask.rsi/inhand-left.png | Bin 0 -> 469 bytes .../Drinks/hosflask.rsi/inhand-right.png | Bin 0 -> 459 bytes .../Consumable/Drinks/hosflask.rsi/meta.json | 15 +++- .../Drinks/lithiumflask.rsi/icon_open.png | Bin 0 -> 677 bytes .../Drinks/lithiumflask.rsi/inhand-left.png | Bin 0 -> 571 bytes .../Drinks/lithiumflask.rsi/inhand-right.png | Bin 0 -> 557 bytes .../Drinks/lithiumflask.rsi/meta.json | 26 +++++- .../Drinks/mreflask.rsi/icon_open.png | Bin 0 -> 530 bytes .../Consumable/Drinks/mreflask.rsi/meta.json | 5 +- .../Drinks/shinyflask.rsi/icon_open.png | Bin 0 -> 803 bytes .../Drinks/shinyflask.rsi/inhand-left.png | Bin 0 -> 563 bytes .../Drinks/shinyflask.rsi/inhand-right.png | Bin 0 -> 558 bytes .../Drinks/shinyflask.rsi/meta.json | 26 +++++- .../Drinks/vacuumflask.rsi/icon_open.png | Bin 0 -> 387 bytes .../Drinks/vacuumflask.rsi/meta.json | 5 +- 37 files changed, 241 insertions(+), 48 deletions(-) create mode 100644 Resources/Audio/Items/flask_close1.ogg create mode 100644 Resources/Audio/Items/flask_open1.ogg create mode 100644 Resources/Textures/Objects/Consumable/Drinks/barflask.rsi/icon_open.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/detflask.rsi/icon_open.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/detflask.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/detflask.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/flask.rsi/icon_open.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/flask.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/flask.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/flask_old.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/flask_old.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/hosflask.rsi/icon_open.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/hosflask.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/hosflask.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/lithiumflask.rsi/icon_open.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/lithiumflask.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/lithiumflask.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/mreflask.rsi/icon_open.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/shinyflask.rsi/icon_open.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/shinyflask.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/shinyflask.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Consumable/Drinks/vacuumflask.rsi/icon_open.png diff --git a/Resources/Audio/Items/attributions.yml b/Resources/Audio/Items/attributions.yml index 9e44afac7d..52f0a6c9a2 100644 --- a/Resources/Audio/Items/attributions.yml +++ b/Resources/Audio/Items/attributions.yml @@ -149,13 +149,23 @@ license: "CC0-1.0" copyright: "Original sound by Nox_Sound on freesound.org. Converted to ogg and edited by themias." source: "https://freesound.org/people/Nox_Sound/sounds/556648/" - + - files: [ "toolbox_insert.ogg" ] license: "CC0-1.0" copyright: "Original sound by j1987 on freesound.org. Converted to ogg and edited by themias." source: "https://freesound.org/people/j1987/sounds/532137/" - + - files: [ "toolbox_remove.ogg" ] license: "CC0-1.0" copyright: "Original sound by kooust on freesound.org. Converted to ogg and edited by themias." source: "https://freesound.org/people/kooust/sounds/452712/" + +- files: [ "flask_open1.ogg" ] + license: "CC-BY-3.0" + copyright: "Original sound by kwahmah_02 on freesound.org. Converted to ogg and edited by Prole0 (GitHub)" + source: "https://freesound.org/s/277503/" + +- files: [ "flask_close1.ogg" ] + license: "CC-BY-NC-4.0" + copyright: "Original sound by QEDionium on freesound.org. Coverted to ogg and edited by Prole0 (GitHub)" + source: "https://freesound.org/s/489803/" diff --git a/Resources/Audio/Items/flask_close1.ogg b/Resources/Audio/Items/flask_close1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..54587628216068d27cabd83fc659d0bd13f7edae GIT binary patch literal 20371 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 literal 0 HcmV?d00001 diff --git a/Resources/Audio/Items/flask_open1.ogg b/Resources/Audio/Items/flask_open1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..7e766d465f1c11e00147054be9a8f412f62e7381 GIT binary patch literal 34411 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 VvfQta@wOx9)Wi=dXNjJ`WdH?4W$FL` literal 0 HcmV?d00001 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_flasks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_flasks.yml index 2ebc20e365..9e1b100dbf 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_flasks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_flasks.yml @@ -3,7 +3,13 @@ id: FlaskBase abstract: true components: - - type: Drink + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + - type: Sprite + state: icon + sprite: Objects/Consumable/Drinks/flask.rsi - type: PhysicalComposition materialComposition: Steel: 300 @@ -11,7 +17,31 @@ solution: drink - type: entity - parent: FlaskBase + id: DrinkFlaskVisualsOpenable + abstract: true + components: + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "icon_open"} + False: {state: "icon"} + - type: Appearance + - type: Sprite + layers: + - state: icon + map: ["enum.OpenableVisuals.Layer"] + - type: Openable + sound: + collection: flaskOpenSounds + closeable: true + closeSound: + collection: flaskCloseSounds + +# Flasks + +- type: entity + parent: [FlaskBase, DrinkFlaskVisualsOpenable] id: DrinkShinyFlask name: shiny flask description: A shiny metal flask. It appears to have a Greek symbol inscribed on it. @@ -20,8 +50,9 @@ sprite: Objects/Consumable/Drinks/shinyflask.rsi - type: entity - parent: FlaskBase + parent: [FlaskBase, DrinkFlaskVisualsOpenable] id: DrinkMREFlask + suffix: Full name: MRE flask description: An old military flask, filled with the finest contents for soldiers. components: @@ -37,7 +68,7 @@ solution: drink - type: entity - parent: FlaskBase + parent: [FlaskBase, DrinkFlaskVisualsOpenable] id: DrinkDetFlask name: inspector's flask description: A metal flask with a leather band and golden badge belonging to the inspector. @@ -46,7 +77,7 @@ sprite: Objects/Consumable/Drinks/detflask.rsi - type: entity - parent: FlaskBase + parent: [FlaskBase, DrinkFlaskVisualsOpenable] id: DrinkHosFlask name: hos's flask description: A metal flask, fit for a hard working HoS. @@ -55,7 +86,7 @@ sprite: Objects/Consumable/Drinks/hosflask.rsi - type: entity - parent: FlaskBase + parent: [FlaskBase, DrinkFlaskVisualsOpenable] id: DrinkFlask name: captain's flask description: A metal flask belonging to the captain. @@ -64,7 +95,7 @@ sprite: Objects/Consumable/Drinks/flask.rsi - type: entity - parent: FlaskBase + parent: [FlaskBase, DrinkFlaskVisualsOpenable] id: DrinkFlaskBar name: bar flask description: A metal flask often given out by the bartender on loan. Don't forget to return it! @@ -75,14 +106,14 @@ - type: entity parent: FlaskBase id: DrinkFlaskOld - name: flask - description: '' + name: old flask + description: A decrepit old flask, its lid seems to be missing. components: - type: Sprite sprite: Objects/Consumable/Drinks/flask_old.rsi - type: entity - parent: FlaskBase + parent: [FlaskBase, DrinkFlaskVisualsOpenable] id: DrinkLithiumFlask name: lithium flask description: A flask with a Lithium Atom symbol on it. @@ -91,35 +122,10 @@ sprite: Objects/Consumable/Drinks/lithiumflask.rsi - type: entity - parent: FlaskBase + parent: [FlaskBase, DrinkFlaskVisualsOpenable] id: DrinkVacuumFlask name: vacuum flask description: Keeping your drinks at the perfect temperature since 1892. components: - type: Sprite sprite: Objects/Consumable/Drinks/vacuumflask.rsi - -- type: entity - parent: FlaskBase - id: Pitcher - name: metal pitcher - description: A stainless steel insulated pitcher. Everyone's best friend in the morning. - components: - - type: SolutionContainerManager - solutions: - drink: - maxVol: 60 - - type: Sprite - sprite: Objects/Consumable/Drinks/pitcher.rsi - layers: - - state: icon - map: ["enum.SolutionContainerLayers.Base"] - - state: fill-6 - map: ["enum.SolutionContainerLayers.Fill"] - visible: false - - type: Appearance - - type: SolutionContainerVisuals - maxFillLevels: 6 - fillBaseName: fill- - inHandsMaxFillLevels: 2 - inHandsFillBaseName: -fill- diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml index a7e3bf18cf..02a4346ebc 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml @@ -177,3 +177,33 @@ - type: PhysicalComposition materialComposition: Steel: 75 + +- type: entity + parent: DrinkBase + id: Pitcher + name: metal pitcher + description: A stainless steel insulated pitcher. Everyone's best friend in the morning. + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 60 + - type: Sprite + sprite: Objects/Consumable/Drinks/pitcher.rsi + layers: + - state: icon + map: ["enum.SolutionContainerLayers.Base"] + - state: fill-6 + map: ["enum.SolutionContainerLayers.Fill"] + visible: false + - type: Appearance + - type: SolutionContainerVisuals + maxFillLevels: 6 + fillBaseName: fill- + inHandsMaxFillLevels: 2 + inHandsFillBaseName: -fill- + - type: PhysicalComposition + materialComposition: + Steel: 300 + - type: FitsInDispenser + solution: drink diff --git a/Resources/Prototypes/SoundCollections/drink_close_sounds.yml b/Resources/Prototypes/SoundCollections/drink_close_sounds.yml index 9da4d28168..2fc1c01dd6 100644 --- a/Resources/Prototypes/SoundCollections/drink_close_sounds.yml +++ b/Resources/Prototypes/SoundCollections/drink_close_sounds.yml @@ -2,3 +2,8 @@ id: bottleCloseSounds files: - /Audio/Items/bottle_close1.ogg + +- type: soundCollection + id: flaskCloseSounds + files: + - /Audio/Items/flask_close1.ogg diff --git a/Resources/Prototypes/SoundCollections/drink_open_sounds.yml b/Resources/Prototypes/SoundCollections/drink_open_sounds.yml index fea78d0464..21c2f61003 100644 --- a/Resources/Prototypes/SoundCollections/drink_open_sounds.yml +++ b/Resources/Prototypes/SoundCollections/drink_open_sounds.yml @@ -19,3 +19,8 @@ id: pop files: - /Audio/Items/pop.ogg + +- type: soundCollection + id: flaskOpenSounds + files: + - /Audio/Items/flask_open1.ogg diff --git a/Resources/Textures/Objects/Consumable/Drinks/barflask.rsi/icon_open.png b/Resources/Textures/Objects/Consumable/Drinks/barflask.rsi/icon_open.png new file mode 100644 index 0000000000000000000000000000000000000000..13bec09cdf785f57be5748831ccfd5a5c99b9ef5 GIT binary patch literal 517 zcmV+g0{Z=lP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUzgGod|R9M69mcL5FP!z_0ErJxU!(G%azJ+wm*1mzFMO>tBpn??L zeGQj_6|Dsa-=Gv6T)MVqsI`ACXfU*cak}BAh&E~CV!q|%kmP*d{m!|$fdmN>B>3-0 z&GYi`ZRp+KT(dle?NTXHt)+PcjNLJvPKVF;N#q)#1r#jH)7ROxEW10q)NX6!wsIc8 z=Egc#^}AFoEht!)cYJz8cQB&3w@>%A3zG@%heNyF2B13_QL9vE7BRB`DW#Xs=lxtM zg_JUoJC1`80>^P^wOS~peyyL5+B+#FN+|$r+XnE-UDuttPACDIP)8{xwr%^wQIHP1 zY7-{85Q2Wc4?rf9iATV+{X&R<^_nfF3?` z;7oE&9IFG<({7SSnSpc^`!#_XzZ0@>2h44ziOgu;TetwZvP!jb>E{piI{n@Vfa2jn z;4A<*J1P4!v#`VH;=JquG#{USeD3+BN2A%8>t7>5fz@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUznn^@KR9M69mOV?uU=)R)7Qv;oPJU4W{sR~3*s=HrELzk>x+|z4 zrS7hsT%80JjfDz=t5^gFC&9tAYGbu6Rg01ijYD`%TKs6*1_yJNn@h+&=bn3$H{imB z3m1P3Pd5*jU&?xBe~*3*rsDCAYV~y^KzrB7<#IG$>mApyEg%vO>*9K3aD?gTH1dH= zXdnm%^sHFEnO^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/detflask.rsi/inhand-left.png b/Resources/Textures/Objects/Consumable/Drinks/detflask.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..3ce0418112d41b49b2e7063a9fe27664062516c2 GIT binary patch literal 502 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXikZz zi(^Q|t+%%}dL0fBVSBKDc`h~tYTlapN3TlaNDzk2jw z1;&d9Imc0|Y zCo3~HI4$it{+pfQz{8i7eydMt#2XtYWwvawjup{LwdiYRe(zH|b@{b&R@~n{d!A^A`HuhprdgP;kB!)U`RKiWdNRik{yHY6w>Nn* zubG*}M30)V16xbqe|>#*)}4DX-tV7Ay0`>==VowN%p%#Be*Nis`2!EU-`lR^y%{<0 zEVs#Qv1eh+IydPG^BUM{FR<)cSjTo{wQ1*hE)hnasC^sRcAKf3B7zR&QKbLh*2~7atYRw-2 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/detflask.rsi/inhand-right.png b/Resources/Textures/Objects/Consumable/Drinks/detflask.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..569a2164cd5dcb7e7f25345cfe50fce78a39b9d9 GIT binary patch literal 471 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXili7 zi(^Q|t+%)Ay`2JOj(t4dy?Y-+XLjzTWfM3YO*jPa`etPHv@KY9cqpR69<;Aa@mokqRNDo+jH*jN6m_*yHHSeoy7oGcb^?8c4@uu&e z4}6NO{4LUUR{V(Uk4uy1#Vu`}%D+DUz=oem`3~QGtKRkA@3)n$W-gj&bpO7p=l8R1 z)45NHD6Rj@2(}wYBnV93+Y%DQ_+Cz4=souzcYVdV2@c*?39Ij3y!C6!XGYdLPnvZ8RNZ%>JvH<7x`ltfxqj#L1*R_6K>Lo^?{SQv4t4KBz@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBU!=Sf6CR9M69mR(2`Q5?m8O4LrS>@b+RL~)TYBKj~0ABq-&l8TCm zK1`w>N(g&5f*hs(Y9+;h(V&xJY6;Xfm1CY#5svE-c_9V|$|{_5(#I&0oc2qc^YgM))i#U}sy z4A%rI0)b?@_80o8JWxsSW{~pronvcB;oT2!a%&|$O@4JIdX zCTvty1wf1H1lz7po5Zvg*tc_6^4x{9l$5Rnz_Kg=oCzBMAq0}FAPhadB{>}uxo#9F zDP5U*H4q*CLY^ltvnI(3j-eyTiYrT95zt07{Dpo({k=%CLTybAx*0=~6&mX5Q;Pgj zVD>}kFZ5>=XsD~_;GW$mzI+<$>Z#bamFW05wrS!oPzVo4vZsKmszky=wA~1jNK6t5 z57BkE8{0I|qB>espM47~%OuZ}N2tG-xb4u~(u8AJXxi6I?e+OEj2MOy%T5MfK7B+% z@e({HaVrM;CmkDnlfK#S^x z-oBrG0>(s~d)>DYLQqh=gxZ=K01EtzX}WX?fTLas!-!F`b`_&nug$&%LI_k<Kbtji>{7a83nRJAU$q> zbw(FoRToS<1EZRj8MkrU!PG|pc+&HL=Dy1S4D_ogLS(NC(zD*< zm4HH;Tbk(Z>|`~|SyZGj8qr-V;L3uu{BhgC!@Bu*P@@k$Bv+?D#@wu*_(u+p>R$|Yg-cF#p`#3 zha(L1egfe3{fF$`zJ=e{LPOyheR$_Ehku425y4rA-Sy|Q00000NkvXXu0mjf86b?m literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/flask.rsi/inhand-left.png b/Resources/Textures/Objects/Consumable/Drinks/flask.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..7177a53c50a37d6e420ee3975b75532aa9cc60d6 GIT binary patch literal 488 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXil1^ zi(^Q|t+%)Ay$=V7uzt9G>7*iupI;LPpHq9%uS*A)I?4WGw<_`8@42MgqGa3rL%zF9 zUc@vh=qWW7AN(lFbk}dWN9^0Z6CVB4&9{#6i`(?1OF$3|-X3Y?`)yY2shnar+sP$m zqTZ5+Qc6liwU;$Bd1pS)PJ5;m<#;wMT5qxT$!jxf_Uv6{zcOug?44|r7vCQ2J#NtV zaPRR2x0_w}9etC!c7Xy%-MQn>)xWNu^;~>g!_};>udk#{U0ua2rzQ;4eR4sVxx-Us ziEFEbt}6em`aflJckx%n&oxR2u*m7w^jOBP^JiS!&2*b}x&F&@J4L(0bjw+{aqru| z+cbL4598OdjX!g^7(U!f{<+sB&SN3N0fRoq138gO`W4srY;t7;={zmbs&k+!++lOV zSEgf&Spsb*U4AV5viz6OwN=Ylv-c;pn#-vcEIfSipkmAm13A_Is~JE}(3Sotzs=Ek TvT6h{Di}On{an^LB{Ts5+rr6f literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/flask.rsi/inhand-right.png b/Resources/Textures/Objects/Consumable/Drinks/flask.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..2c508b9cf117a8d423f79deb4a9ae1557fe6cdc6 GIT binary patch literal 484 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXik!+ zi(^Q|t+%)Kdmna?Vf`@u!IJs0;au;~p7V0PLZ07Ly+;>r{u9l+ zwbc1}5KG}_dku>?#S3pOOJ5{-X88G4&R6xexK~>|{UxIk1Vqm~@nQd4%@dz4l^Cfp zoOxz^#fO)np!6_@!uh(~68N$S{X8yiYv-LlVM(9nK_K$yhR{Yt_H);8XMq54Kt^ux!8^YoamN&C(T z*Ppm9sHSeu?^2WHlMOvDMV&r+z3FwM$W#9>LNRPTw`C`#=zDrxI{EBytaw4}oPxXF z9JTx}c1;&}SLG7R)}!^bH||iuh3~KB4P?G#Djh!jmwDES3%eUYq3~&QzE;geK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXwE!O z7srr_TW@dO*LF^nVf*m>j8Iav&+;kfo~1v`FL3M#+daYir*O=Uu+Q~7F6zbVN~Pbr z;3@djX=41=8$PpJw{7x?3wJ-gbZ>rNm0kUT%inAB?#*UbPy&NR_iy>ndi3UqP}n7r zRi8qi>`G~gSlM!+R906`|5lBi%k^tZ$9lFrS0F98K*qaAGW9({Pg zQTx~SLR$I~`ON3mQ3niMGK!+kv>tW}wfi3haS+5+RvB7O-zP82_`XdczUJtU5Rc*_ zM!lPjTcaEK>X(06+YtNVW~b_;XUhs=7*}m)+QdCA?!XNnkJWd%mR!zU%6eX3C!oLGC7p)4(WX@O1TaS?83{1OR7P=7azM literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/flask_old.rsi/inhand-right.png b/Resources/Textures/Objects/Consumable/Drinks/flask_old.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..f3a2c49b689393b380e20735a445cec0c1d96f47 GIT binary patch literal 534 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXwDQ* z7srr_TW@de_hw3zVf*l2R!H-Ts~Wpw%opzs3I!{sN%2heDd_mu*LE!`>X-GBjhx5Uth$ut6yN!Vk<(6^YD}y?;>`ZhV!P^$v*{}u()Kzoo#f`OE`DXz>m@zC zMZej=!VW508{E3n(nVHXnH8cVwo~^&j$Ql3=J&U>_l9IY`RsS;@nz%ftToq5f`9Ue zZ`iumJNUA&N{-8mKPG2$8Tr4LFZ}`B!Cgd!0<=h{zb*XYSl+l P%^0Dy!5 z0Qvv`0D$NK0Cg|`0P0`>06Lfe02gqax=}m;00H7jL_t(o!|j$aYuiu|$A5DCB9$Nt z#({dX8yxf*I(GC>GADHDma+W;B}>M1>}*OqHzglN5E2ccP=By6b%cYIC;K|6dPpH{ zRaKp$|8%W7G^E?4ak_6kf@qNEmtZrQaua@7Lrzwk@8?J9P ze$6$)hV z&{~%S0K{>O)_)qM6!ZDKa!puO$$PyX0FLAQc5fWV0F^~l60qked2Vl%d|Os_bSc8s zO4#mX&$G1*6(;-XA$X|XK z0r2|E7=X8*cG$EU7SMm(HROk;P`BplY>j%e0<8${zbF1V9!zVY;(iBaS@RS z?$4r(BwKSWi}Q3{=`Z-JPI#9*C;OF<#%o}^W0~NQNXfoWNMp0WAMWtA(1w*#IF3UU zMaC|+Vn3}lQ51pi(^v}bBL~o0n--nhN~z{AB*jUXB#H4Cefc!}mkUaZ77q_U04YP| U%w%_W$N&HU07*qoM6N<$g1LShQD>AXdx^G;~adm8yxl- zdhF4M(sM!|AjkFvLM}O`&tOV>Zb}|zAtXM8Lczfhgke?LJ3VYH)*)%-jc?iiG?GU1 z|45@5L4yVjUOJ+>8`Cru0BbE;YqBh>6{}TOfVCEDE!}RnZhwg92>|GHIymRB*4B=( zwT^SY-xsYl07;Vg`TJ84tu@wK#^Z7Dbp$)6X(~yQpp?Qnw;})_&vTq}D5aQAr@=W9 z!6JeLe2A62)2~+McA|3YOoW?-u-huflR_{e{9Xt@a`)f( z!AZCz+=(J@1Ai;zfVclhSlnO%!%@%MGZBWP9y2M)KYr~4@ag9OfaC9bYzJmnuz=3% zEl+-QKH%)|nxpf9Uz-%d?cJP7A>MbR0-!wpn?*@aq+Cm)h|75q^a|Fh zgb&G=WKRhR!CE=6+lqW}BeG&YCL{zwCy@2__*E&xN-5$v#u(%6YAJTkVT^$++l&C< zQE~w198i|%rLB|-4IAE!s-3XfeyelKZup!JN`nS34}SrN3Ez@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUz&`Cr=R9M69m9c8uP!xv0>*Pj)APS}$yx9#78a;RH=%Hjz$OB|- zUm#@3m_CCk?c9_+h(|$4JcL3)#vp`2>a~43D7r`?Z51QvqJR2z?!D*#rE_%-v}nLy1Y5GHJ`_;%D$)1jTo56E5?()t?{S#w^r!l_>Rk)L#|Hl`S|4x7sq$a zw&EjK%iFyj`^IG?B6yFNGLd}EQ;OTwrq*BZqE7f!yb$}7k;dlZEBngCJbHnI4aR6hCZP5iUreveTs5}X?EVJ=)teA$5 geK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXil)F zi(^Q|t+%%fy^c6Yus#sKYvI`8bdS{Jry^e4 zE~&BjC|YZKuQ6h~{fBw;54(sHX8rT0d(Qg9AP51zh2?d(`JdHy z<&GsE3=MU4oXfxvv4*QW_U^u}wCS_W&)hfP|8t=&Q^S9dd8c1b-w@Z78u_hki*~|V z(OS-bvBy`IRj=88KhS{n$FtoDN=izL>L0V8epeHwedClEFx(kDUHx3vIVCg!0AGK( A6#xJL literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/hosflask.rsi/inhand-right.png b/Resources/Textures/Objects/Consumable/Drinks/hosflask.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..30d17583575be451f2c0b7b54b5beba026e34b25 GIT binary patch literal 459 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXpX0+ zi(^Q|t+%%fy^c7D9Q$ZK!=m{pm!j{fl8;O`?F1k8t!{pQ{|M{6XeLki>`#7so31}4q!-EO8Pjmmi|If2Ga>}xmqJo`| zBVO$i6P&ne)s%kwE9tVc^(8iYWa|csbQ-!HGMKU~bC2ys6;HLN(zRW$TP08Jzw!3z z!oQz3FVsEm4&G>;9r{&$)~zdZva*UVMtNT^y=Qh%=k%KM zKfyMGh=LPyiaAe(9{By;|9N=e@)_P7F)TG*$)2xo?c%HG+S#)B@UzOiUc*b<)|&V# zr$$=cZdl5hTUTdRbH8@Wxz#!rR!=^cRp^&hYs#|6z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBU!BS}O-R9M69md|U`U>L_gk-@x_A=svGDLL%qL_xulzrcgSh77hM z!VYt&py0t{85Fl2g@PN5q6|vQJc!Ug!JCI^WpsyGuw!;fUwaT`_t(o(lD2~+ChoHD zDS7hne!qEsyw3v>V{UpzRcjSBH$9^`3sh?rWl2j_YZYGt#-bMRy0ig6{oQ+C{uiPa z(6epd7LzO|(ROlA%By>qC=S_93!J$@V=0r2Pe zxNi?a{!c0Rvb)Q~YV~D70Zd)L3P4IEG15AdSwr2q zdppp1T2OM4O=EYuh-?}FyVC{Wr`_xOr9_gA@>?y^2$(rjxe4t~cO?0sf22jY(`a(* z=CvVvN+gH;dbUmE0*tiIPNRvD);;#at{n12uL#*>Qw(ghBg zB@;Z>0aqQwT7*st2BXnxw@w6E@m)mgYp-avTRdNS;t}JJJ1DJ|d>5hbact&HH8=xC z!I=v^+s4mk6lfn9C1P+81tAZ$l$tqHIW3kHVr07o*$K~ajx)z!_MhRk8xeq)00000 LNkvXXu0mjfJgFv0 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/lithiumflask.rsi/inhand-left.png b/Resources/Textures/Objects/Consumable/Drinks/lithiumflask.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..552b333b14e42c7733554d5fd21daa806ac3e746 GIT binary patch literal 571 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXwFVg z7srr_TW@b|^g0|M!uG)aQh0}_7sr+hfiXOlOzf@Of|vKT>KqMapT4Jsqv;2aozpgv zt)&IRS{-*(o+Omodb*wQ%C$h^#GHTH2+dZeO2ITwckv=Gygj-Yy6*`{(rCjP~+&ox(5M zCf?X}`8rR`-({(@{GRO1*&w}x!%6;WbZhtO?5p=Bzp{VYP|+gIGUM*qIe8Lm_x4}7 z#P^`?W6SB;-app$KcAjHXWpDu>@qEOWz6F5Q=6Mhlx{KR+e}(!Hi;=ID>*mv&^4pB zE|Ccj15{Youxya}uSGr{xUhVKBzE`z75pUXO@ GgeCx(R`@Fb literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/lithiumflask.rsi/inhand-right.png b/Resources/Textures/Objects/Consumable/Drinks/lithiumflask.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..6ac52171eec907cf2faa3a593e1e63f9a119ae6f GIT binary patch literal 557 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXwGU+ z7srr_TW@de_d4t#!~P-PD}LcJ-GvvN%w4&N}{VvF(sMs5y>gi&1I{5FT72D%hZ z9r|6>q`vB5zKP!*dz(jpo^TxgCy+Vs(VHJ<*4ajuKW_On^@Wc&L&v!-5jR(VF-wqT zomArkS_ytVVD?*XX0mdKI;Vst0G_|^_y7O^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/lithiumflask.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/lithiumflask.rsi/meta.json index db0ac608ed..41222914c7 100644 --- a/Resources/Textures/Objects/Consumable/Drinks/lithiumflask.rsi/meta.json +++ b/Resources/Textures/Objects/Consumable/Drinks/lithiumflask.rsi/meta.json @@ -1 +1,25 @@ -{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "https://github.com/discordia-space/CEV-Eris/raw/f7aa28fd4b4d0386c3393d829681ebca526f1d2d/icons/obj/drinks.dmi", "states": [{"name": "icon"}]} \ No newline at end of file +{ + "version": 1, + "copyright": "https://github.com/discordia-space/CEV-Eris/raw/f7aa28fd4b4d0386c3393d829681ebca526f1d2d/icons/obj/drinks.dmi, icon_open & inhands by Prole0 (GitHub)", + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "states": [ + { + "name": "icon" + }, + { + "name": "icon_open" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Consumable/Drinks/mreflask.rsi/icon_open.png b/Resources/Textures/Objects/Consumable/Drinks/mreflask.rsi/icon_open.png new file mode 100644 index 0000000000000000000000000000000000000000..92600e9a02c5595e25533c85b8b853fe13f7335d GIT binary patch literal 530 zcmV+t0`2{YP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUzkV!;AR9M69mcMJlKpe$C7I6w!i^V_|h0c=6u|eo02yXrtZVvqm z{0B0ZOkLb8f>RgQf~7cgDwrksgQyKRb+JP+U{SfW2`+il_r8~KFYmp(3#3Sq;>^e# zYFd`HJ=7nKMj4g)D99e0sMTs=T`U%NWw@~8IILDHeBTG4+wI1yK&4XI>rEyTTCElU zyEo8?e`P16_*K9bpnyTuOGW;KV&&$zP4#!q1V znWtI5S2_n=*FAbX|JwqULnzSIz<>v|*-$^<%{PWu0tB1Izj2A;#9 UNVmmp$p8QV07*qoM6N<$f&eqxg8%>k literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/mreflask.rsi/meta.json b/Resources/Textures/Objects/Consumable/Drinks/mreflask.rsi/meta.json index ec2769b3b9..53f0b3af2f 100644 --- a/Resources/Textures/Objects/Consumable/Drinks/mreflask.rsi/meta.json +++ b/Resources/Textures/Objects/Consumable/Drinks/mreflask.rsi/meta.json @@ -1,15 +1,18 @@ { "version": 1, + "copyright": "Created by EmoGarbage, inhands by TiniestShark (github), icon_open by Prole0 (GitHub)", "size": { "x": 32, "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "Created by EmoGarbage, inhands by TiniestShark (github)", "states": [ { "name": "icon" }, + { + "name": "icon_open" + }, { "name": "inhand-left", "directions": 4 diff --git a/Resources/Textures/Objects/Consumable/Drinks/shinyflask.rsi/icon_open.png b/Resources/Textures/Objects/Consumable/Drinks/shinyflask.rsi/icon_open.png new file mode 100644 index 0000000000000000000000000000000000000000..92e4559e38f2cf4f5dc581f3cc1e534ff39ab84a GIT binary patch literal 803 zcmV+;1Kj+HP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBU!p-DtRR9M69mQP5NVI0Rl3lB5ae7EV?n=7V`tV~d%o8h5DbeK0u z4?)+cTSSK;5vT=41W{B+nP?=0b?DMg2_lji9zrA&jrW~8_r80bx`Xg^Fkh!L*gM{r z4)uli^6-P-_xb$(ybsu{j##@$4D^eRSi9hFC^674lqE$Z2Kq}fP+_-$r%#>%Ffue; zQv4yi4XmhY$;llt0Py+CRLQZfa+6F>d=vnSiwXdSVQ}@@O&36Y__&z+x!`iN2WDnw zSzb;9plODAZY6oy1TM9;34mxr6geE?>%=q*sU@VtZf3v#z>0y2auQ#-*vhBmD5smw zplca|at#1MxdyMdigPW^;?4VaE?Wt7#M;G!M|}*w9z^o_&2wGL6vakPG%zwWOerQT zO+o$1M&jK)=vu~1TxbTM>q?iUa#|{Zx~w9$1o7@348veOzs{zF!0OWHTuIH~Hl}5Q9XLD_DAE@b!{R9jn zYu?c9@sW(4C)s+Lqi-wHpU0-vi2LRk2ANicdsk1G3JRTVRKqy>CKBt*6l61t` ze}UWM1E4DL7_=<4FZ%XSn46lm-+;c6H|q}&2m~ONCK>AlAQZ0K9oOb2R#ep)0y`ye z_uc|6OC%C0qTmRDLRE*3ftEFIpNw^amNhBVH#a$BU$v~y#C!f=bZoi*`V+6iUO%~mD zKLx8jU}+PMjgK9Od`I*1^V=m@Rh)U01`bOC;NHXDKgQg>9Vgn@!1%eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXwF7Y z7srr_TW@de_c|OP!uDbP(UY40n7Iv|V`5pnkMkwDI&QaX5RsV@B%tGZ#P@}sUX%ZW zWe=3>90V_luW%Mz#5;>e{A8J|N|WU6;sux9Z%?({`E$~2>p9E%F=l<6>H*8Bv zvv2=em+pOZ(p8RyySA-K*_|+zY}>EeE;_E`L9>c zqP_&5y(WKBcS6_WAMtHAyW?CiYyboLiutTK4I`njxgN@xNAFf{F% literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Consumable/Drinks/shinyflask.rsi/inhand-right.png b/Resources/Textures/Objects/Consumable/Drinks/shinyflask.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..11f51eb0eb08e51e0cc24e3484311c814a263313 GIT binary patch literal 558 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBXwDi> z7srr_TW@de_c|OP!uDbECC4L*p(5Oh&eq*sEjA4+G`2*`J8)e{VPAQr3TJKhmpGJxK zH4(PU{@%6SlvTRY+AM2d0IP)Ybf$NK8?UOIv+SLzoy)lB8Fy$y_v+J~Wt-glgUTJJ z)GUl=y#M|G6L-53#*omZjaL^mR6j9Nd)ow5`Byc5R=9ed{`vL$rqnlnpR?&1GsJ-) zvY}wf?BE%$3xLk!nx*G+SR~UXxO4N5g~{5>61k=u-nwtAA+iruDsiE;Y2 zUHn{D!J$=GUuJ%BDCV}bvbXl#bJ(abf5Ni|M`r9iV!1qNwI}2E74`S0uV#4b7w%bH zkW@AQ|FY}BOI|m;XHwT(`?F=unu8r3F~>9>$nmam%R1k2H3%3`+sm)ZAGf~EZc_5* q$gPqpwMLDfwe_yw8bFSmFvNSwSk3J%W507^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+2h97azU z$B>A_Z>KHnYcUXLt3MGYprtokLCf$3myH-t|7G_Bx$jsn%swpAeIw^kut1Z7W5R|d zWqxya+>xxR-o0%8oF&HbR+CRM@Ez~|m?K{ov%X~a{f)WOi!_{Kuiv}8X6rST76naJ zPfbq_$EfToufnAwhR5nS432qdmPQ=fJ~3m~v+FnZy>Bm`o5M0m<>2q9P3t8+E{h~e zMEzZnf4gh@XGuo^1_q1g*K}?FrpoPF*KEjk*kFN1*K_VCm3^s^0>?L&^8LTseS*`X zv7PJ8=QmLw{xe-Ru?>0fK`rO@HDRZK$Z7W Date: Sat, 21 Jun 2025 14:53:43 +0000 Subject: [PATCH 030/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 679c9ac740..fe1f7c4e0c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,16 +1,4 @@ Entries: -- author: Minemoder5000 - changes: - - message: The Syndicate Elite Hardsuit and Cybersun Juggernaut Suit have been restricted - to Nuclear Operative uplinks. - type: Tweak - - message: The Elite Web Vest has been added to the uplink for 5 tc. It has reduced - pierce resistance, but increased heat, radiation, blunt, slash, and caustic - resistance compared to the normal web vest. - type: Add - id: 8189 - time: '2025-04-15T21:39:18.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36225 - author: EmoGarbage404 changes: - message: Sentient artifacts can once again activate themselves. @@ -3909,3 +3897,12 @@ id: 8701 time: '2025-06-21T13:11:17.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38111 +- author: Prole0 + changes: + - message: Flask Visuals & Sounds! + type: Add + - message: Flasks now need to be opened to be drunk from. + type: Tweak + id: 8702 + time: '2025-06-21T14:52:36.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38032 From d4876be6f058ec54a5d7cab9d6d167b1bb5049af Mon Sep 17 00:00:00 2001 From: TemporalOroboros Date: Sat, 21 Jun 2025 08:23:19 -0700 Subject: [PATCH 031/191] Kills TurfHelpers (#37939) * Create TurfSystem equivalent for and obsolete TurfHelpers.GetTileRef * Fix EntitySystem uses of TurfHelpers.GetTileRef * Fix EntitySystem uses of TurfHelpers.TryGetTileRef * Fix construction condition uses of TurfHelpers.GetTileRef * Fix last use of TurfHelpers.IsBlockedTurf * Create TurfSystem equivalent to and obsolete TurfHelpers.GetContentTileDefinition * Fix uses of TurfHelpers.GetContentTileDefinition(TileRef) * Fix uses of TurfHelpers.GetContentTileDefinition(Tile) * Create TurfSystem equivalent to and obsolete TurfHelpers.IsSpace * Fix EntitySystem uses of TurfHelpers.IsSpace(Tile) * Fix EntitySystem uses of TurfHelpers.IsSpace(TileRef) * Fix remaining uses of TurfHelpers.IsSpace * Fix uses of TurfHelpers.GetEntitiesInTile * Delete TurfHelpers.cs * Add GetEntitiesInTile lookup methods * Convert some GetEntitiesInTile methods to LookupSystem extension methods * Use new GetEntitiesInTile methods * Recycle spiderweb hashset * Recycle floor tile hashset --- Content.Client/Mapping/MappingState.cs | 2 +- .../Abilities/Mime/MimePowersSystem.cs | 2 +- .../Commands/VariantizeCommand.cs | 4 +- .../TileReactions/CreateEntityTileReaction.cs | 44 +++--- .../Conditions/ComponentInTile.cs | 4 +- .../Decals/Commands/AddDecalCommand.cs | 3 +- Content.Server/Decals/DecalSystem.cs | 6 +- Content.Server/Dragon/DragonSystem.cs | 4 +- .../Electrocution/ElectrocutionSystem.cs | 3 +- .../EntityEffects/EntityEffectSystem.cs | 3 +- .../EntitySystems/SmokeOnTriggerSystem.cs | 3 +- .../Fluids/EntitySystems/PuddleSystem.cs | 4 +- Content.Server/Nuke/NukeSystem.cs | 4 +- .../Physics/Controllers/ConveyorController.cs | 3 +- Content.Server/Pinpointer/NavMapSystem.cs | 4 +- .../DungeonJob/DungeonJob.Exterior.cs | 2 +- .../Procedural/DungeonJob/DungeonJob.cs | 3 + Content.Server/Procedural/DungeonSystem.cs | 3 + .../Respawn/SpecialRespawnSystem.cs | 5 +- .../Revenant/EntitySystems/RevenantSystem.cs | 3 +- .../Systems/ShuttleSystem.FasterThanLight.cs | 2 +- .../Shuttles/Systems/ShuttleSystem.Impact.cs | 4 +- .../Shuttles/Systems/ShuttleSystem.cs | 3 +- .../Shuttles/Systems/ThrusterSystem.cs | 6 +- Content.Server/Spider/SpiderSystem.cs | 11 +- Content.Server/Spreader/SpreaderSystem.cs | 3 +- .../Debris/SimpleFloorPlanPopulatorSystem.cs | 3 +- .../Goliath/GoliathTentacleSystem.cs | 2 +- Content.Shared/Blocking/BlockingSystem.cs | 3 +- .../Construction/Conditions/TileNotBlocked.cs | 12 +- .../Construction/Conditions/TileType.cs | 9 +- Content.Shared/Magic/SharedMagicSystem.cs | 5 +- Content.Shared/Maps/TurfHelpers.cs | 144 ------------------ Content.Shared/Maps/TurfSystem.cs | 136 ++++++++++++++++- Content.Shared/RCD/Systems/RCDSystem.cs | 10 +- Content.Shared/Sound/SharedEmitSoundSystem.cs | 4 +- Content.Shared/Tiles/FloorTileSystem.cs | 14 +- 37 files changed, 251 insertions(+), 229 deletions(-) delete mode 100644 Content.Shared/Maps/TurfHelpers.cs diff --git a/Content.Client/Mapping/MappingState.cs b/Content.Client/Mapping/MappingState.cs index 6430f1db1e..97fbee70bc 100644 --- a/Content.Client/Mapping/MappingState.cs +++ b/Content.Client/Mapping/MappingState.cs @@ -793,7 +793,7 @@ public sealed class MappingState : GameplayStateBase if (_mapMan.TryFindGridAt(mapPos, out var gridUid, out var grid) && _entityManager.System().TryGetTileRef(gridUid, grid, coords, out var tileRef) && - _allPrototypesDict.TryGetValue(tileRef.GetContentTileDefinition(), out button)) + _allPrototypesDict.TryGetValue(_entityManager.System().GetContentTileDefinition(tileRef), out button)) { OnSelected(button); return true; diff --git a/Content.Server/Abilities/Mime/MimePowersSystem.cs b/Content.Server/Abilities/Mime/MimePowersSystem.cs index 3de356f608..29c7b1710c 100644 --- a/Content.Server/Abilities/Mime/MimePowersSystem.cs +++ b/Content.Server/Abilities/Mime/MimePowersSystem.cs @@ -82,7 +82,7 @@ namespace Content.Server.Abilities.Mime // Get the tile in front of the mime var offsetValue = xform.LocalRotation.ToWorldVec(); var coords = xform.Coordinates.Offset(offsetValue).SnapToGrid(EntityManager, _mapMan); - var tile = coords.GetTileRef(EntityManager, _mapMan); + var tile = _turf.GetTileRef(coords); if (tile == null) return; diff --git a/Content.Server/Administration/Commands/VariantizeCommand.cs b/Content.Server/Administration/Commands/VariantizeCommand.cs index 3f9b7efd07..a75f72b1c7 100644 --- a/Content.Server/Administration/Commands/VariantizeCommand.cs +++ b/Content.Server/Administration/Commands/VariantizeCommand.cs @@ -10,7 +10,6 @@ namespace Content.Server.Administration.Commands; public sealed class VariantizeCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; public string Command => "variantize"; @@ -40,10 +39,11 @@ public sealed class VariantizeCommand : IConsoleCommand var mapsSystem = _entManager.System(); var tileSystem = _entManager.System(); + var turfSystem = _entManager.System(); foreach (var tile in mapsSystem.GetAllTiles(euid.Value, gridComp)) { - var def = tile.GetContentTileDefinition(_tileDefManager); + var def = turfSystem.GetContentTileDefinition(tile); var newTile = new Tile(tile.Tile.TypeId, tile.Tile.Flags, tileSystem.PickVariant(def)); mapsSystem.SetTile(euid.Value, gridComp, tile.GridIndices, newTile); } diff --git a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs index 0249b6255a..1499967a42 100644 --- a/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CreateEntityTileReaction.cs @@ -41,33 +41,33 @@ public sealed partial class CreateEntityTileReaction : ITileReaction IEntityManager entityManager, List? data) { - if (reactVolume >= Usage) + if (reactVolume < Usage) + return FixedPoint2.Zero; + + if (Whitelist != null) { - if (Whitelist != null) + var lookup = entityManager.System(); + + int acc = 0; + foreach (var ent in lookup.GetEntitiesInTile(tile, LookupFlags.Static)) { - int acc = 0; - foreach (var ent in tile.GetEntitiesInTile()) - { - var whitelistSystem = entityManager.System(); - if (whitelistSystem.IsWhitelistPass(Whitelist, ent)) - acc += 1; + var whitelistSystem = entityManager.System(); + if (whitelistSystem.IsWhitelistPass(Whitelist, ent)) + acc += 1; - if (acc >= MaxOnTile) - return FixedPoint2.Zero; - } + if (acc >= MaxOnTile) + return FixedPoint2.Zero; } - - var random = IoCManager.Resolve(); - var xoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); - var yoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); - - var center = entityManager.System().GetTileCenter(tile); - var pos = center.Offset(new Vector2(xoffs, yoffs)); - entityManager.SpawnEntity(Entity, pos); - - return Usage; } - return FixedPoint2.Zero; + var random = IoCManager.Resolve(); + var xoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); + var yoffs = random.NextFloat(-RandomOffsetMax, RandomOffsetMax); + + var center = entityManager.System().GetTileCenter(tile); + var pos = center.Offset(new Vector2(xoffs, yoffs)); + entityManager.SpawnEntity(Entity, pos); + + return Usage; } } diff --git a/Content.Server/Construction/Conditions/ComponentInTile.cs b/Content.Server/Construction/Conditions/ComponentInTile.cs index 429c4fdabf..6af8c36e24 100644 --- a/Content.Server/Construction/Conditions/ComponentInTile.cs +++ b/Content.Server/Construction/Conditions/ComponentInTile.cs @@ -58,9 +58,7 @@ namespace Content.Server.Construction.Conditions if (!entityManager.System().TryGetTileRef(transform.GridUid.Value, grid, indices, out var tile)) return !HasEntity; - var entities = tile.GetEntitiesInTile(LookupFlags.Approximate | LookupFlags.Static, lookup); - - foreach (var ent in entities) + foreach (var ent in lookup.GetEntitiesInTile(tile, flags: LookupFlags.Approximate | LookupFlags.Static)) { if (entityManager.HasComponent(ent, type)) return HasEntity; diff --git a/Content.Server/Decals/Commands/AddDecalCommand.cs b/Content.Server/Decals/Commands/AddDecalCommand.cs index 2d9c5a1b25..e7463ff9cb 100644 --- a/Content.Server/Decals/Commands/AddDecalCommand.cs +++ b/Content.Server/Decals/Commands/AddDecalCommand.cs @@ -54,8 +54,9 @@ namespace Content.Server.Decals.Commands } var mapSystem = _entManager.System(); + var turfSystem = _entManager.System(); var coordinates = new EntityCoordinates(gridIdRaw.Value, new Vector2(x, y)); - if (mapSystem.GetTileRef(gridIdRaw.Value, grid, coordinates).IsSpace()) + if (turfSystem.IsSpace(mapSystem.GetTileRef(gridIdRaw.Value, grid, coordinates))) { shell.WriteError($"Cannot create decal on space tile at {coordinates}."); return; diff --git a/Content.Server/Decals/DecalSystem.cs b/Content.Server/Decals/DecalSystem.cs index 690d24c2e4..bc77900df7 100644 --- a/Content.Server/Decals/DecalSystem.cs +++ b/Content.Server/Decals/DecalSystem.cs @@ -29,7 +29,6 @@ namespace Content.Server.Decals { [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; [Dependency] private readonly IParallelManager _parMan = default!; [Dependency] private readonly ChunkingSystem _chunking = default!; [Dependency] private readonly IConfigurationManager _conf = default!; @@ -37,6 +36,7 @@ namespace Content.Server.Decals [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly TurfSystem _turf = default!; private readonly Dictionary> _dirtyChunks = new(); private readonly Dictionary>> _previousSentChunks = new(); @@ -167,7 +167,7 @@ namespace Content.Server.Decals foreach (var change in args.Changes) { - if (!change.NewTile.IsSpace(_tileDefMan)) + if (!_turf.IsSpace(change.NewTile)) continue; var indices = GetChunkIndices(change.GridIndices); @@ -308,7 +308,7 @@ namespace Content.Server.Decals if (!TryComp(gridId, out MapGridComponent? grid)) return false; - if (_mapSystem.GetTileRef(gridId.Value, grid, coordinates).IsSpace(_tileDefMan)) + if (_turf.IsSpace(_mapSystem.GetTileRef(gridId.Value, grid, coordinates))) return false; if (!TryComp(gridId, out DecalGridComponent? comp)) diff --git a/Content.Server/Dragon/DragonSystem.cs b/Content.Server/Dragon/DragonSystem.cs index efac049b35..960365a7ca 100644 --- a/Content.Server/Dragon/DragonSystem.cs +++ b/Content.Server/Dragon/DragonSystem.cs @@ -21,7 +21,6 @@ namespace Content.Server.Dragon; public sealed partial class DragonSystem : EntitySystem { [Dependency] private readonly CarpRiftsConditionSystem _carpRifts = default!; - [Dependency] private readonly ITileDefinitionManager _tileDef = default!; [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; [Dependency] private readonly NpcFactionSystem _faction = default!; [Dependency] private readonly PopupSystem _popup = default!; @@ -30,6 +29,7 @@ public sealed partial class DragonSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly TurfSystem _turf = default!; private EntityQuery _objQuery; @@ -159,7 +159,7 @@ public sealed partial class DragonSystem : EntitySystem // cant put a rift on solars foreach (var tile in _map.GetTilesIntersecting(xform.GridUid.Value, grid, new Circle(_transform.GetWorldPosition(xform), RiftTileRadius), false)) { - if (!tile.IsSpace(_tileDef)) + if (!_turf.IsSpace(tile)) continue; _popup.PopupEntity(Loc.GetString("carp-rift-space-proximity", ("proximity", RiftTileRadius)), uid, uid); diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index 957f881bed..8647f087fc 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -57,6 +57,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem [Dependency] private readonly SharedStutteringSystem _stuttering = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly TurfSystem _turf = default!; [ValidatePrototypeId] private const string StatusEffectKey = "Electrocution"; @@ -137,7 +138,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem return false; if (electrified.NoWindowInTile) { - var tileRef = transform.Coordinates.GetTileRef(EntityManager, _mapManager); + var tileRef = _turf.GetTileRef(transform.Coordinates); if (tileRef != null) { diff --git a/Content.Server/EntityEffects/EntityEffectSystem.cs b/Content.Server/EntityEffects/EntityEffectSystem.cs index 164199c200..03a0c8bb2b 100644 --- a/Content.Server/EntityEffects/EntityEffectSystem.cs +++ b/Content.Server/EntityEffects/EntityEffectSystem.cs @@ -74,6 +74,7 @@ public sealed class EntityEffectSystem : EntitySystem [Dependency] private readonly TemperatureSystem _temperature = default!; [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] private readonly VomitSystem _vomit = default!; + [Dependency] private readonly TurfSystem _turf = default!; public override void Initialize() { @@ -529,7 +530,7 @@ public sealed class EntityEffectSystem : EntitySystem return; } - if (_spreader.RequiresFloorToSpread(args.Effect.PrototypeId) && tileRef.Tile.IsSpace()) + if (_spreader.RequiresFloorToSpread(args.Effect.PrototypeId) && _turf.IsSpace(tileRef)) return; var coords = _map.MapToGrid(gridUid, mapCoords); diff --git a/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs b/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs index 31b4746ec2..19335d3446 100644 --- a/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/SmokeOnTriggerSystem.cs @@ -20,6 +20,7 @@ public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem [Dependency] private readonly SmokeSystem _smoke = default!; [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly SpreaderSystem _spreader = default!; + [Dependency] private readonly TurfSystem _turf = default!; public override void Initialize() { @@ -39,7 +40,7 @@ public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem return; } - if (_spreader.RequiresFloorToSpread(comp.SmokePrototype.ToString()) && tileRef.Tile.IsSpace()) + if (_spreader.RequiresFloorToSpread(comp.SmokePrototype.ToString()) && _turf.IsSpace(tileRef)) return; var coords = _map.MapToGrid(gridUid, mapCoords); diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index 94951b0113..bb7b06bd0b 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -47,7 +47,6 @@ public sealed partial class PuddleSystem : SharedPuddleSystem [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly ReactiveSystem _reactive = default!; @@ -59,6 +58,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem [Dependency] private readonly SpeedModifierContactsSystem _speedModContacts = default!; [Dependency] private readonly TileFrictionController _tile = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly TurfSystem _turf = default!; [ValidatePrototypeId] private const string Blood = "Blood"; @@ -686,7 +686,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem } // If space return early, let that spill go out into the void - if (tileRef.Tile.IsEmpty || tileRef.IsSpace(_tileDefMan)) + if (tileRef.Tile.IsEmpty || _turf.IsSpace(tileRef)) { puddleUid = EntityUid.Invalid; return false; diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index 77be71f7bc..81fbf074da 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -32,7 +32,6 @@ public sealed class NukeSystem : EntitySystem [Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly ExplosionSystem _explosions = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] private readonly NavMapSystem _navMap = default!; [Dependency] private readonly PointLightSystem _pointLight = default!; @@ -45,6 +44,7 @@ public sealed class NukeSystem : EntitySystem [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly TurfSystem _turf = default!; /// /// Used to calculate when the nuke song should start playing for maximum kino with the nuke sfx @@ -210,7 +210,7 @@ public sealed class NukeSystem : EntitySystem foreach (var tile in _map.GetTilesIntersecting(xform.GridUid.Value, grid, new Circle(worldPos, component.RequiredFloorRadius), false)) { - if (!tile.IsSpace(_tileDefManager)) + if (!_turf.IsSpace(tile)) continue; var msg = Loc.GetString("nuke-component-cant-anchor-floor"); diff --git a/Content.Server/Physics/Controllers/ConveyorController.cs b/Content.Server/Physics/Controllers/ConveyorController.cs index fa278278e2..9ec05be9b7 100644 --- a/Content.Server/Physics/Controllers/ConveyorController.cs +++ b/Content.Server/Physics/Controllers/ConveyorController.cs @@ -19,6 +19,7 @@ public sealed class ConveyorController : SharedConveyorController [Dependency] private readonly DeviceLinkSystem _signalSystem = default!; [Dependency] private readonly MaterialReclaimerSystem _materialReclaimer = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly TurfSystem _turf = default!; public override void Initialize() { @@ -123,7 +124,7 @@ public sealed class ConveyorController : SharedConveyorController var xform = ent.Comp; - var beltTileRef = xform.Coordinates.GetTileRef(EntityManager, MapManager); + var beltTileRef = _turf.GetTileRef(xform.Coordinates); if (beltTileRef != null) { diff --git a/Content.Server/Pinpointer/NavMapSystem.cs b/Content.Server/Pinpointer/NavMapSystem.cs index 7511e60cde..89e2837b35 100644 --- a/Content.Server/Pinpointer/NavMapSystem.cs +++ b/Content.Server/Pinpointer/NavMapSystem.cs @@ -28,7 +28,7 @@ public sealed partial class NavMapSystem : SharedNavMapSystem [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; + [Dependency] private readonly TurfSystem _turfSystem = default!; public const float CloseDistance = 15f; public const float FarDistance = 30f; @@ -118,7 +118,7 @@ public sealed partial class NavMapSystem : SharedNavMapSystem var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize); ref var tileData = ref chunk.TileData[GetTileIndex(relative)]; - if (change.NewTile.IsSpace(_tileDefManager)) + if (_turfSystem.IsSpace(change.NewTile)) { tileData = 0; if (PruneEmpty((ev.Entity, navMap), chunk)) diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Exterior.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Exterior.cs index 92237dca2a..4f2f564ded 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.Exterior.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.Exterior.cs @@ -30,7 +30,7 @@ public sealed partial class DungeonJob SharedPathfindingSystem.GridCast(startTile, position, tile => { if (!_maps.TryGetTileRef(_gridUid, _grid, tile, out var tileRef) || - tileRef.Tile.IsSpace(_tileDefManager)) + _turf.IsSpace(tileRef.Tile)) { return true; } diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.cs index f2bd5a4394..77404fc963 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.cs @@ -42,6 +42,7 @@ public sealed partial class DungeonJob : Job> private readonly EntityTableSystem _entTable; private readonly TagSystem _tags; private readonly TileSystem _tile; + private readonly TurfSystem _turf; private readonly SharedMapSystem _maps; private readonly SharedTransformSystem _transform; @@ -70,6 +71,7 @@ public sealed partial class DungeonJob : Job> DungeonSystem dungeon, EntityLookupSystem lookup, TileSystem tile, + TurfSystem turf, SharedTransformSystem transform, DungeonConfig gen, MapGridComponent grid, @@ -89,6 +91,7 @@ public sealed partial class DungeonJob : Job> _dungeon = dungeon; _lookup = lookup; _tile = tile; + _turf = turf; _tags = _entManager.System(); _maps = _entManager.System(); _entTable = _entManager.System(); diff --git a/Content.Server/Procedural/DungeonSystem.cs b/Content.Server/Procedural/DungeonSystem.cs index 9cc3fbb158..3a0a7ab2cd 100644 --- a/Content.Server/Procedural/DungeonSystem.cs +++ b/Content.Server/Procedural/DungeonSystem.cs @@ -36,6 +36,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem [Dependency] private readonly DecalSystem _decals = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly TileSystem _tile = default!; + [Dependency] private readonly TurfSystem _turf = default!; [Dependency] private readonly MapLoaderSystem _loader = default!; [Dependency] private readonly SharedMapSystem _maps = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; @@ -212,6 +213,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem this, _lookup, _tile, + _turf, _transform, gen, grid, @@ -244,6 +246,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem this, _lookup, _tile, + _turf, _transform, gen, grid, diff --git a/Content.Server/Respawn/SpecialRespawnSystem.cs b/Content.Server/Respawn/SpecialRespawnSystem.cs index cde0fd2aa0..45229b7418 100644 --- a/Content.Server/Respawn/SpecialRespawnSystem.cs +++ b/Content.Server/Respawn/SpecialRespawnSystem.cs @@ -17,7 +17,6 @@ namespace Content.Server.Respawn; public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem { [Dependency] private readonly IAdminLogManager _adminLog = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; @@ -108,7 +107,7 @@ public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem foreach (var tile in _map.GetTilesIntersecting(entityGridUid.Value, grid, circle)) { - if (tile.IsSpace(_tileDefinitionManager) + if (_turf.IsSpace(tile) || _turf.IsTileBlocked(tile, CollisionGroup.MobMask) || !_atmosphere.IsTileMixtureProbablySafe(entityGridUid, entityMapUid.Value, _map.TileIndicesFor((entityGridUid.Value, grid), mapPos))) @@ -179,7 +178,7 @@ public sealed class SpecialRespawnSystem : SharedSpecialRespawnSystem foreach (var newTileRef in _map.GetTilesIntersecting(targetGrid, grid, circle)) { - if (newTileRef.IsSpace(_tileDefinitionManager) || _turf.IsTileBlocked(newTileRef, CollisionGroup.MobMask) || !_atmosphere.IsTileMixtureProbablySafe(targetGrid, targetMap, mapTarget)) + if (_turf.IsSpace(newTileRef) || _turf.IsTileBlocked(newTileRef, CollisionGroup.MobMask) || !_atmosphere.IsTileMixtureProbablySafe(targetGrid, targetMap, mapTarget)) continue; found = true; diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs index b319db09e0..0501f5afc3 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs @@ -45,6 +45,7 @@ public sealed partial class RevenantSystem : EntitySystem [Dependency] private readonly StoreSystem _store = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly VisibilitySystem _visibility = default!; + [Dependency] private readonly TurfSystem _turf = default!; [ValidatePrototypeId] private const string RevenantShopId = "ActionRevenantShop"; @@ -164,7 +165,7 @@ public sealed partial class RevenantSystem : EntitySystem return false; } - var tileref = Transform(uid).Coordinates.GetTileRef(); + var tileref = _turf.GetTileRef(Transform(uid).Coordinates); if (tileref != null) { if(_physics.GetEntitiesIntersectingBody(uid, (int) CollisionGroup.Impassable).Count > 0) diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index 2fc11db4a4..13e13bf8f3 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -679,7 +679,7 @@ public sealed partial class ShuttleSystem // only toss if its on lattice/space var tile = _mapSystem.GetTileRef(shuttleEntity, shuttleGrid, childXform.Coordinates); - if (!tile.IsSpace(_tileDefManager)) + if (!_turf.IsSpace(tile)) return; var throwDirection = childXform.LocalPosition - shuttleBody.LocalCenter; diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs index f165990f1c..29657af295 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs @@ -275,7 +275,7 @@ public sealed partial class ShuttleSystem foreach (var tileRef in _mapSystem.GetLocalTilesIntersecting(uid, grid, new Circle(centerTile, radius))) { - var def = (ContentTileDefinition)_tileDefManager[tileRef.Tile.TypeId]; + var def = _turf.GetContentTileDefinition(tileRef); mass += def.Mass; tileCount++; @@ -402,7 +402,7 @@ public sealed partial class ShuttleSystem continue; // Mark tiles for breaking/effects - var def = (ContentTileDefinition)_tileDefManager[_mapSystem.GetTileRef(uid, grid, tileData.Tile).Tile.TypeId]; + var def = _turf.GetContentTileDefinition(_mapSystem.GetTileRef(uid, grid, tileData.Tile)); if (tileData.Energy > def.Mass * _tileBreakEnergyMultiplier) brokenTiles.Add((tileData.Tile, Tile.Empty)); diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.cs index 71d51e3187..f271be108f 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.cs @@ -28,6 +28,7 @@ using Robust.Shared.Physics.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; +using Content.Shared.Maps; namespace Content.Server.Shuttles.Systems; @@ -40,7 +41,6 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; [Dependency] private readonly BiomeSystem _biomes = default!; [Dependency] private readonly BodySystem _bobby = default!; [Dependency] private readonly BuckleSystem _buckle = default!; @@ -62,6 +62,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem [Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly ThrusterSystem _thruster = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly TurfSystem _turf = default!; private EntityQuery _buckleQuery; private EntityQuery _gridQuery; diff --git a/Content.Server/Shuttles/Systems/ThrusterSystem.cs b/Content.Server/Shuttles/Systems/ThrusterSystem.cs index 42c249c9ab..e7f93043cb 100644 --- a/Content.Server/Shuttles/Systems/ThrusterSystem.cs +++ b/Content.Server/Shuttles/Systems/ThrusterSystem.cs @@ -26,13 +26,13 @@ namespace Content.Server.Shuttles.Systems; public sealed class ThrusterSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly AmbientSoundSystem _ambient = default!; [Dependency] private readonly FixtureSystem _fixtureSystem = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly SharedPointLightSystem _light = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly TurfSystem _turf = default!; // Essentially whenever thruster enables we update the shuttle's available impulses which are used for movement. // This is done for each direction available. @@ -97,7 +97,7 @@ public sealed class ThrusterSystem : EntitySystem foreach (var change in args.Changes) { // If the old tile was space but the new one isn't then disable all adjacent thrusters - if (change.NewTile.IsSpace(_tileDefManager) || !change.OldTile.IsSpace(_tileDefManager)) + if (_turf.IsSpace(change.NewTile) || !_turf.IsSpace(change.OldTile)) continue; var tilePos = change.GridIndices; @@ -457,7 +457,7 @@ public sealed class ThrusterSystem : EntitySystem var mapGrid = Comp(xform.GridUid.Value); var tile = _mapSystem.GetTileRef(xform.GridUid.Value, mapGrid, new Vector2i((int)Math.Floor(x), (int)Math.Floor(y))); - return tile.Tile.IsSpace(); + return _turf.IsSpace(tile); } #region Burning diff --git a/Content.Server/Spider/SpiderSystem.cs b/Content.Server/Spider/SpiderSystem.cs index 449e43984c..6e2e2b59a2 100644 --- a/Content.Server/Spider/SpiderSystem.cs +++ b/Content.Server/Spider/SpiderSystem.cs @@ -10,6 +10,13 @@ namespace Content.Server.Spider; public sealed class SpiderSystem : SharedSpiderSystem { [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly TurfSystem _turf = default!; + + /// + /// A recycled hashset used to check turfs for spiderwebs. + /// + private readonly HashSet _webs = []; public override void Initialize() { @@ -66,7 +73,9 @@ public sealed class SpiderSystem : SharedSpiderSystem private bool IsTileBlockedByWeb(EntityCoordinates coords) { - foreach (var entity in coords.GetEntitiesInTile()) + _webs.Clear(); + _turf.GetEntitiesInTile(coords, _webs); + foreach (var entity in _webs) { if (HasComp(entity)) return true; diff --git a/Content.Server/Spreader/SpreaderSystem.cs b/Content.Server/Spreader/SpreaderSystem.cs index 50f5d81183..328f4f0504 100644 --- a/Content.Server/Spreader/SpreaderSystem.cs +++ b/Content.Server/Spreader/SpreaderSystem.cs @@ -23,6 +23,7 @@ public sealed class SpreaderSystem : EntitySystem [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly TurfSystem _turf = default!; /// /// Cached maximum number of updates per spreader prototype. This is applied per-grid. @@ -246,7 +247,7 @@ public sealed class SpreaderSystem : EntitySystem if (!_map.TryGetTileRef(neighborEnt, neighborGrid, neighborPos, out var tileRef) || tileRef.Tile.IsEmpty) continue; - if (spreaderPrototype.PreventSpreadOnSpaced && tileRef.Tile.IsSpace()) + if (spreaderPrototype.PreventSpreadOnSpaced && _turf.IsSpace(tileRef)) continue; var directionEnumerator = _map.GetAnchoredEntitiesEnumerator(neighborEnt, neighborGrid, neighborPos); diff --git a/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs b/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs index ed26053114..e936d550bb 100644 --- a/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs +++ b/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs @@ -14,6 +14,7 @@ public sealed class SimpleFloorPlanPopulatorSystem : BaseWorldSystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinition = default!; [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly TurfSystem _turf = default!; /// public override void Initialize() @@ -30,7 +31,7 @@ public sealed class SimpleFloorPlanPopulatorSystem : BaseWorldSystem while (enumerator.MoveNext(out var tile)) { var coords = _map.GridTileToLocal(uid, grid, tile.Value.GridIndices); - var selector = tile.Value.Tile.GetContentTileDefinition(_tileDefinition).ID; + var selector = _turf.GetContentTileDefinition(tile.Value).ID; if (!component.Caches.TryGetValue(selector, out var cache)) continue; diff --git a/Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs b/Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs index e0a0453285..a220781420 100644 --- a/Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs +++ b/Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs @@ -55,7 +55,7 @@ public sealed class GoliathTentacleSystem : EntitySystem foreach (var pos in spawnPos) { if (!_map.TryGetTileRef(grid, gridComp, pos, out var tileRef) || - tileRef.IsSpace() || + _turf.IsSpace(tileRef) || _turf.IsTileBlocked(tileRef, CollisionGroup.Impassable)) { continue; diff --git a/Content.Shared/Blocking/BlockingSystem.cs b/Content.Shared/Blocking/BlockingSystem.cs index c920fde13b..619adf7918 100644 --- a/Content.Shared/Blocking/BlockingSystem.cs +++ b/Content.Shared/Blocking/BlockingSystem.cs @@ -31,6 +31,7 @@ public sealed partial class BlockingSystem : EntitySystem [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; + [Dependency] private readonly TurfSystem _turf = default!; public override void Initialize() { @@ -165,7 +166,7 @@ public sealed partial class BlockingSystem : EntitySystem } //Don't allow someone to block if someone else is on the same tile - var playerTileRef = xform.Coordinates.GetTileRef(); + var playerTileRef = _turf.GetTileRef(xform.Coordinates); if (playerTileRef != null) { var intersecting = _lookup.GetLocalEntitiesIntersecting(playerTileRef.Value, 0f); diff --git a/Content.Shared/Construction/Conditions/TileNotBlocked.cs b/Content.Shared/Construction/Conditions/TileNotBlocked.cs index 02bc84f736..c56dcb1583 100644 --- a/Content.Shared/Construction/Conditions/TileNotBlocked.cs +++ b/Content.Shared/Construction/Conditions/TileNotBlocked.cs @@ -1,4 +1,5 @@ using Content.Shared.Maps; +using Content.Shared.Physics; using JetBrains.Annotations; using Robust.Shared.Map; @@ -14,24 +15,25 @@ public sealed partial class TileNotBlocked : IConstructionCondition public bool Condition(EntityUid user, EntityCoordinates location, Direction direction) { - var tileRef = location.GetTileRef(); + if (!IoCManager.Resolve().TrySystem(out var turfSystem)) + return false; - if (tileRef == null) + if (!turfSystem.TryGetTileRef(location, out var tileRef)) { return false; } - if (tileRef.Value.IsSpace() && _failIfSpace) + if (turfSystem.IsSpace(tileRef.Value) && _failIfSpace) { return false; } - if (!tileRef.Value.GetContentTileDefinition().Sturdy && _failIfNotSturdy) + if (!turfSystem.GetContentTileDefinition(tileRef.Value).Sturdy && _failIfNotSturdy) { return false; } - return !tileRef.Value.IsBlockedTurf(_filterMobs); + return !turfSystem.IsTileBlocked(tileRef.Value, _filterMobs ? CollisionGroup.MobMask : CollisionGroup.Impassable); } public ConstructionGuideEntry GenerateGuideEntry() diff --git a/Content.Shared/Construction/Conditions/TileType.cs b/Content.Shared/Construction/Conditions/TileType.cs index 3ffdf66050..e066e6af2e 100644 --- a/Content.Shared/Construction/Conditions/TileType.cs +++ b/Content.Shared/Construction/Conditions/TileType.cs @@ -20,12 +20,13 @@ namespace Content.Shared.Construction.Conditions public bool Condition(EntityUid user, EntityCoordinates location, Direction direction) { - var tileFound = location.GetTileRef(); - - if (tileFound == null) + if (!IoCManager.Resolve().TrySystem(out var turfSystem)) return false; - var tile = tileFound.Value.Tile.GetContentTileDefinition(); + if (!turfSystem.TryGetTileRef(location, out var tileFound)) + return false; + + var tile = turfSystem.GetContentTileDefinition(tileFound.Value); foreach (var targetTile in TargetTiles) { if (tile.ID == targetTile) diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index 4bfe2e1fe5..09b289c2be 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -62,6 +62,7 @@ public abstract class SharedMagicSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly TurfSystem _turf = default!; private static readonly ProtoId InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell"; @@ -158,7 +159,7 @@ public abstract class SharedMagicSystem : EntitySystem if (!TryComp(casterXform.GridUid, out var mapGrid)) return new List(); - if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager)) + if (!_turf.TryGetTileRef(directionPos, out var tileReference)) return new List(); var tileIndex = tileReference.Value.GridIndices; @@ -171,7 +172,7 @@ public abstract class SharedMagicSystem : EntitySystem if (!TryComp(casterXform.GridUid, out var mapGrid)) return new List(); - if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager)) + if (!_turf.TryGetTileRef(directionPos, out var tileReference)) return new List(); var tileIndex = tileReference.Value.GridIndices; diff --git a/Content.Shared/Maps/TurfHelpers.cs b/Content.Shared/Maps/TurfHelpers.cs deleted file mode 100644 index dfa12f3d8f..0000000000 --- a/Content.Shared/Maps/TurfHelpers.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.CompilerServices; -using Content.Shared.Physics; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; - -namespace Content.Shared.Maps -{ - // TODO move all these methods to LookupSystem or TurfSystem - // That, or make the interface arguments non-optional so people stop failing to pass them in. - public static class TurfHelpers - { - /// - /// Attempts to get the turf at a certain coordinates or null if no such turf is found. - /// - public static TileRef? GetTileRef(this EntityCoordinates coordinates, IEntityManager? entityManager = null, IMapManager? mapManager = null) - { - entityManager ??= IoCManager.Resolve(); - - if (!coordinates.IsValid(entityManager)) - return null; - - mapManager ??= IoCManager.Resolve(); - var pos = entityManager.System().ToMapCoordinates(coordinates); - if (!mapManager.TryFindGridAt(pos, out _, out var grid)) - return null; - - if (!grid.TryGetTileRef(coordinates, out var tile)) - return null; - - return tile; - } - - public static bool TryGetTileRef(this EntityCoordinates coordinates, [NotNullWhen(true)] out TileRef? turf, IEntityManager? entityManager = null, IMapManager? mapManager = null) - { - return (turf = coordinates.GetTileRef(entityManager, mapManager)) != null; - } - - /// - /// Returns the content tile definition for a tile. - /// - public static ContentTileDefinition GetContentTileDefinition(this Tile tile, ITileDefinitionManager? tileDefinitionManager = null) - { - tileDefinitionManager ??= IoCManager.Resolve(); - return (ContentTileDefinition)tileDefinitionManager[tile.TypeId]; - } - - /// - /// Returns whether a tile is considered space. - /// - public static bool IsSpace(this Tile tile, ITileDefinitionManager? tileDefinitionManager = null) - { - return tile.GetContentTileDefinition(tileDefinitionManager).MapAtmosphere; - } - - /// - /// Returns the content tile definition for a tile ref. - /// - public static ContentTileDefinition GetContentTileDefinition(this TileRef tile, ITileDefinitionManager? tileDefinitionManager = null) - { - return tile.Tile.GetContentTileDefinition(tileDefinitionManager); - } - - /// - /// Returns whether a tile ref is considered space. - /// - public static bool IsSpace(this TileRef tile, ITileDefinitionManager? tileDefinitionManager = null) - { - return tile.Tile.IsSpace(tileDefinitionManager); - } - - /// - /// Helper that returns all entities in a turf. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("Use the lookup system")] - public static IEnumerable GetEntitiesInTile(this TileRef turf, LookupFlags flags = LookupFlags.Static, EntityLookupSystem? lookupSystem = null) - { - lookupSystem ??= EntitySystem.Get(); - - if (!GetWorldTileBox(turf, out var worldBox)) - return Enumerable.Empty(); - - return lookupSystem.GetEntitiesIntersecting(turf.GridUid, worldBox, flags); - } - - /// - /// Helper that returns all entities in a turf. - /// - [Obsolete("Use the lookup system")] - public static IEnumerable GetEntitiesInTile(this EntityCoordinates coordinates, LookupFlags flags = LookupFlags.Static, EntityLookupSystem? lookupSystem = null) - { - var turf = coordinates.GetTileRef(); - - if (turf == null) - return Enumerable.Empty(); - - return GetEntitiesInTile(turf.Value, flags, lookupSystem); - } - - /// - /// Checks if a turf has something dense on it. - /// - [Obsolete("Use turf system")] - public static bool IsBlockedTurf(this TileRef turf, bool filterMobs, EntityLookupSystem? physics = null) - { - CollisionGroup mask = filterMobs - ? CollisionGroup.MobMask - : CollisionGroup.Impassable; - - return IoCManager.Resolve().GetEntitySystem().IsTileBlocked(turf, mask); - } - - /// - /// Creates a box the size of a tile, at the same position in the world as the tile. - /// - [Obsolete] - private static bool GetWorldTileBox(TileRef turf, out Box2Rotated res) - { - var entManager = IoCManager.Resolve(); - var xformSystem = entManager.System(); - - if (entManager.TryGetComponent(turf.GridUid, out var tileGrid)) - { - var gridRot = xformSystem.GetWorldRotation(turf.GridUid); - - // This is scaled to 90 % so it doesn't encompass walls on other tiles. - var tileBox = Box2.UnitCentered.Scale(0.9f); - tileBox = tileBox.Scale(tileGrid.TileSize); - var worldPos = tileGrid.GridTileToWorldPos(turf.GridIndices); - tileBox = tileBox.Translated(worldPos); - // Now tileBox needs to be rotated to match grid rotation - res = new Box2Rotated(tileBox, gridRot, worldPos); - return true; - } - - // Have to "return something" - res = Box2Rotated.UnitCentered; - return false; - } - } -} diff --git a/Content.Shared/Maps/TurfSystem.cs b/Content.Shared/Maps/TurfSystem.cs index 8a4bbf68be..f5477441d9 100644 --- a/Content.Shared/Maps/TurfSystem.cs +++ b/Content.Shared/Maps/TurfSystem.cs @@ -1,8 +1,11 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Numerics; using Content.Shared.Physics; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics; +using Robust.Shared.Toolshed.Commands.Values; namespace Content.Shared.Maps; @@ -11,8 +14,43 @@ namespace Content.Shared.Maps; /// public sealed class TurfSystem : EntitySystem { + [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefinitions = default!; + + + /// + /// Attempts to get the turf at or under some given coordinates or null if no such turf exists. + /// + /// The coordinates to search for a turf. + /// A for the turf found at the given coordinates or null if no such turf exists. + public TileRef? GetTileRef(EntityCoordinates coordinates) + { + if (!coordinates.IsValid(EntityManager)) + return null; + + var pos = _transform.ToMapCoordinates(coordinates); + if (!_mapManager.TryFindGridAt(pos, out var gridUid, out var gridComp)) + return null; + + if (!_mapSystem.TryGetTileRef(gridUid, gridComp, coordinates, out var tile)) + return null; + + return tile; + } + + /// + /// Attempts to get the turf at or under some given coordinates. + /// + /// The coordinates to search for a turf. + /// Returns the turf found at the given coordinates if any. + /// True if a turf was found at the given coordinates, false otherwise. + public bool TryGetTileRef(EntityCoordinates coordinates, [NotNullWhen(true)] out TileRef? tile) + { + return (tile = GetTileRef(coordinates)) is not null; + } /// /// Returns true if a given tile is blocked by physics-enabled entities. @@ -63,14 +101,14 @@ public sealed class TurfSystem : EntitySystem rot -= gridRot; pos = (-gridRot).RotateVec(pos - gridPos); - var xform = new Transform(pos, (float) rot.Theta); + var xform = new Transform(pos, (float)rot.Theta); foreach (var fixture in fixtures.Fixtures.Values) { if (!fixture.Hard) continue; - if ((fixture.CollisionLayer & (int) mask) == 0) + if ((fixture.CollisionLayer & (int)mask) == 0) continue; for (var i = 0; i < fixture.Shape.ChildCount; i++) @@ -86,6 +124,26 @@ public sealed class TurfSystem : EntitySystem return false; } + /// + /// Returns whether a tile is considered to be space or directly exposed to space. + /// + /// The tile in question. + /// True if the tile is considered to be space, false otherwise. + public bool IsSpace(Tile tile) + { + return GetContentTileDefinition(tile).MapAtmosphere; + } + + /// + /// Returns whether a tile is considered to be space or directly exposed to space. + /// + /// The tile in question. + /// True if the tile is considered to be space, false otherwise. + public bool IsSpace(TileRef tile) + { + return IsSpace(tile.Tile); + } + /// /// Returns the location of the centre of the tile in grid coordinates. /// @@ -95,4 +153,78 @@ public sealed class TurfSystem : EntitySystem var center = (turf.GridIndices + new Vector2(0.5f, 0.5f)) * grid.TileSize; return new EntityCoordinates(turf.GridUid, center); } + + /// + /// Returns the content tile definition for a tile. + /// + public ContentTileDefinition GetContentTileDefinition(Tile tile) + { + return (ContentTileDefinition)_tileDefinitions[tile.TypeId]; + } + + /// + /// Returns the content tile definition for a tile ref. + /// + public ContentTileDefinition GetContentTileDefinition(TileRef tile) + { + return GetContentTileDefinition(tile.Tile); + } + + /// + /// Collects all of the entities intersecting with the turf at a given position into a provided + /// + /// The position of the turf to search for entities. + /// The hashset used to collect the relevant entities. + /// A set of lookup categories to search for relevant entities. + public void GetEntitiesInTile(EntityCoordinates coords, HashSet intersecting, LookupFlags flags = LookupFlags.Static) + { + if (!TryGetTileRef(coords, out var tileRef)) + return; + + _entityLookup.GetEntitiesInTile(tileRef.Value, intersecting, flags); + } + + /// + /// Returns a collection containing all of the entities overlapping with the turf at a given position. + /// + /// + /// A hashset containing all of the entities overlapping with the turf in question. + public HashSet GetEntitiesInTile(EntityCoordinates coords, LookupFlags flags = LookupFlags.Static) + { + if (!TryGetTileRef(coords, out var tileRef)) + return []; + + return _entityLookup.GetEntitiesInTile(tileRef.Value, flags); + } +} + +/// +/// Extension methods for looking up entities with respect to given turfs. +/// +public static partial class TurfLookupExtensions +{ + /// + /// Collects all of the entities overlapping with a given turf into a provided . + /// + /// The turf in question. + /// The hashset used to collect the relevant entities. + /// A set of lookup categories to search for relevant entities. + public static void GetEntitiesInTile(this EntityLookupSystem lookupSystem, TileRef turf, HashSet intersecting, LookupFlags flags = LookupFlags.Static) + { + var bounds = lookupSystem.GetWorldBounds(turf); + bounds.Box = bounds.Box.Scale(0.9f); // Otherwise the box can clip into neighboring tiles. + lookupSystem.GetEntitiesIntersecting(turf.GridUid, bounds, intersecting, flags); + } + + /// + /// Returns a collection containing all of the entities overlapping with a given turf. + /// + /// + /// A hashset containing all of the entities overlapping with the turf in question. + public static HashSet GetEntitiesInTile(this EntityLookupSystem lookupSystem, TileRef turf, LookupFlags flags = LookupFlags.Static) + { + var intersecting = new HashSet(); + lookupSystem.GetEntitiesInTile(turf, intersecting, flags); + return intersecting; + } } diff --git a/Content.Shared/RCD/Systems/RCDSystem.cs b/Content.Shared/RCD/Systems/RCDSystem.cs index 238daf6bf7..6d85b171e0 100644 --- a/Content.Shared/RCD/Systems/RCDSystem.cs +++ b/Content.Shared/RCD/Systems/RCDSystem.cs @@ -175,7 +175,7 @@ public sealed class RCDSystem : EntitySystem else { var deconstructedTile = _mapSystem.GetTileRef(gridUid.Value, mapGrid, location); - var protoName = !deconstructedTile.IsSpace() ? _deconstructTileProto : _deconstructLatticeProto; + var protoName = !_turf.IsSpace(deconstructedTile) ? _deconstructTileProto : _deconstructLatticeProto; if (_protoManager.TryIndex(protoName, out var deconProto)) { @@ -383,7 +383,7 @@ public sealed class RCDSystem : EntitySystem } // Check rule: Must place on subfloor - if (prototype.ConstructionRules.Contains(RcdConstructionRule.MustBuildOnSubfloor) && !tile.Tile.GetContentTileDefinition().IsSubFloor) + if (prototype.ConstructionRules.Contains(RcdConstructionRule.MustBuildOnSubfloor) && !_turf.GetContentTileDefinition(tile).IsSubFloor) { if (popMsgs) _popup.PopupClient(Loc.GetString("rcd-component-must-build-on-subfloor-message"), uid, user); @@ -404,7 +404,7 @@ public sealed class RCDSystem : EntitySystem } // Check rule: Tiles can't be identical - if (tile.Tile.GetContentTileDefinition().ID == prototype.Prototype) + if (_turf.GetContentTileDefinition(tile).ID == prototype.Prototype) { if (popMsgs) _popup.PopupClient(Loc.GetString("rcd-component-cannot-build-identical-tile"), uid, user); @@ -487,7 +487,7 @@ public sealed class RCDSystem : EntitySystem } // The tile cannot be destroyed - var tileDef = (ContentTileDefinition) _tileDefMan[tile.Tile.TypeId]; + var tileDef = _turf.GetContentTileDefinition(tile); if (tileDef.Indestructible) { @@ -559,7 +559,7 @@ public sealed class RCDSystem : EntitySystem if (target == null) { // Deconstruct tile (either converts the tile to lattice, or removes lattice) - var tileDef = (tile.Tile.GetContentTileDefinition().ID != "Lattice") ? new Tile(_tileDefMan["Lattice"].TileId) : Tile.Empty; + var tileDef = (_turf.GetContentTileDefinition(tile).ID != "Lattice") ? new Tile(_tileDefMan["Lattice"].TileId) : Tile.Empty; _mapSystem.SetTile(gridUid, mapGrid, position, tileDef); _adminLogger.Add(LogType.RCD, LogImpact.High, $"{ToPrettyString(user):user} used RCD to set grid: {gridUid} tile: {position} open to space"); } diff --git a/Content.Shared/Sound/SharedEmitSoundSystem.cs b/Content.Shared/Sound/SharedEmitSoundSystem.cs index 58d541e363..38af0677fa 100644 --- a/Content.Shared/Sound/SharedEmitSoundSystem.cs +++ b/Content.Shared/Sound/SharedEmitSoundSystem.cs @@ -31,13 +31,13 @@ public abstract class SharedEmitSoundSystem : EntitySystem { [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] private readonly INetManager _netMan = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; [Dependency] protected readonly IRobustRandom Random = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambient = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly TurfSystem _turf = default!; public override void Initialize() { @@ -135,7 +135,7 @@ public abstract class SharedEmitSoundSystem : EntitySystem var tile = _map.GetTileRef(xform.GridUid.Value, grid, xform.Coordinates); // Handle maps being grids (we'll still emit the sound). - if (xform.GridUid != xform.MapUid && tile.IsSpace(_tileDefMan)) + if (xform.GridUid != xform.MapUid && _turf.IsSpace(tile)) return; // hand throwing not predicted sadly diff --git a/Content.Shared/Tiles/FloorTileSystem.cs b/Content.Shared/Tiles/FloorTileSystem.cs index d798fa2473..67283eeea0 100644 --- a/Content.Shared/Tiles/FloorTileSystem.cs +++ b/Content.Shared/Tiles/FloorTileSystem.cs @@ -37,9 +37,15 @@ public sealed class FloorTileSystem : EntitySystem [Dependency] private readonly TileSystem _tile = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly TurfSystem _turf = default!; private static readonly Vector2 CheckRange = new(1f, 1f); + /// + /// A recycled hashset used to check for walls when trying to place tiles on turfs. + /// + private readonly HashSet _turfCheck = []; + public override void Initialize() { base.Initialize(); @@ -104,14 +110,16 @@ public sealed class FloorTileSystem : EntitySystem // if user can access tile center then they can place floor // otherwise check it isn't blocked by a wall - if (!canAccessCenter) + if (!canAccessCenter && _turf.TryGetTileRef(location, out var tileRef)) { - foreach (var ent in location.GetEntitiesInTile(lookupSystem: _lookup)) + _turfCheck.Clear(); + _lookup.GetEntitiesInTile(tileRef.Value, _turfCheck); + foreach (var ent in _turfCheck) { if (physicQuery.TryGetComponent(ent, out var phys) && phys.BodyType == BodyType.Static && phys.Hard && - (phys.CollisionLayer & (int) CollisionGroup.Impassable) != 0) + (phys.CollisionLayer & (int)CollisionGroup.Impassable) != 0) { return; } From 9b5779bd331ddbd362ce0b9887f0aa0908c38cf2 Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Sat, 21 Jun 2025 18:34:11 +0200 Subject: [PATCH 032/191] Allow Maintainers to use customvote command (#38385) Changed from Moderator perm to Round perm --- Content.Server/Voting/VoteCommands.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/Voting/VoteCommands.cs b/Content.Server/Voting/VoteCommands.cs index 677fe1d0f6..e526829d27 100644 --- a/Content.Server/Voting/VoteCommands.cs +++ b/Content.Server/Voting/VoteCommands.cs @@ -63,7 +63,7 @@ namespace Content.Server.Voting } } - [AdminCommand(AdminFlags.Moderator)] + [AdminCommand(AdminFlags.Round)] public sealed class CreateCustomCommand : LocalizedEntityCommands { [Dependency] private readonly IVoteManager _voteManager = default!; @@ -218,7 +218,7 @@ namespace Content.Server.Voting } } - [AdminCommand(AdminFlags.Moderator)] + [AdminCommand(AdminFlags.Round)] public sealed class CancelVoteCommand : LocalizedEntityCommands { [Dependency] private readonly IAdminLogManager _adminLogger = default!; From 8d2f46ae344961c8b31bf76c9790b97581d26aa4 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 21 Jun 2025 16:35:18 +0000 Subject: [PATCH 033/191] Automatic changelog update --- Resources/Changelog/Admin.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 966ee26e7f..a45e7d52bf 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1187,5 +1187,13 @@ Entries: id: 143 time: '2025-06-21T01:13:25.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38261 +- author: SlamBamActionman + changes: + - message: customvote and cancelvote command permission has been moved from Moderator + to Round. + type: Tweak + id: 144 + time: '2025-06-21T16:34:11.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38385 Name: Admin Order: 2 From a15387bcfd9bef370beb9ea72fd3829f51fce474 Mon Sep 17 00:00:00 2001 From: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Date: Sat, 21 Jun 2025 10:54:11 -0700 Subject: [PATCH 034/191] Make role ban pannel pretty (#37952) * Make role ban pannel pretty * Removed unused depencency * refactor: wider panel (no jumping due to scroll in english lang) minor readability improvements --------- Co-authored-by: pa.pecherskij --- .../Administration/UI/BanPanel/BanPanel.xaml | 2 +- .../UI/BanPanel/BanPanel.xaml.cs | 191 +++++++++++++----- .../en-US/administration/ui/role-bans.ftl | 3 + 3 files changed, 144 insertions(+), 52 deletions(-) create mode 100644 Resources/Locale/en-US/administration/ui/role-bans.ftl diff --git a/Content.Client/Administration/UI/BanPanel/BanPanel.xaml b/Content.Client/Administration/UI/BanPanel/BanPanel.xaml index 333184f1c0..ede0ad3ee5 100644 --- a/Content.Client/Administration/UI/BanPanel/BanPanel.xaml +++ b/Content.Client/Administration/UI/BanPanel/BanPanel.xaml @@ -1,7 +1,7 @@ + Title="{Loc ban-panel-title}" MinSize="410 500"> diff --git a/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs b/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs index 3c7322d473..46090a6f3d 100644 --- a/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs +++ b/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs @@ -1,12 +1,14 @@ using System.Linq; using System.Net; using System.Net.Sockets; +using System.Numerics; using Content.Client.Administration.UI.CustomControls; using Content.Shared.Administration; using Content.Shared.CCVar; using Content.Shared.Database; using Content.Shared.Roles; using Robust.Client.AutoGenerated; +using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; @@ -31,14 +33,21 @@ public sealed partial class BanPanel : DefaultWindow private uint Multiplier { get; set; } private bool HasBanFlag { get; set; } private TimeSpan? ButtonResetOn { get; set; } + // This is less efficient than just holding a reference to the root control and enumerating children, but you // have to know how the controls are nested, which makes the code more complicated. - private readonly List _roleCheckboxes = new(); + // Role group name -> the role buttons themselves. + private readonly Dictionary> _roleCheckboxes = new(); private readonly ISawmill _banpanelSawmill; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly ILogManager _logManager = default!; + [Dependency] private readonly IEntityManager _entMan = default!; + [Dependency] private readonly IPrototypeManager _protoMan = default!; + + private const string ExpandedArrow = "▼"; + private const string ContractedArrow = "▶"; private enum TabNumbers { @@ -144,47 +153,90 @@ public sealed partial class BanPanel : DefaultWindow ReasonTextEdit.Placeholder = new Rope.Leaf(Loc.GetString("ban-panel-reason")); - var prototypeManager = IoCManager.Resolve(); - foreach (var proto in prototypeManager.EnumeratePrototypes()) + var departmentJobs = _protoMan.EnumeratePrototypes() + .OrderBy(x => x.Weight); + foreach (var proto in departmentJobs) { - CreateRoleGroup(proto.ID, proto.Roles.Select(p => p.Id), proto.Color); + var roles = proto.Roles.Select(x => _protoMan.Index(x)) + .OrderBy(x => x.ID); + CreateRoleGroup(proto.ID, proto.Color, roles); } - CreateRoleGroup("Antagonist", prototypeManager.EnumeratePrototypes().Select(p => p.ID), Color.Red); + var antagRoles = _protoMan.EnumeratePrototypes() + .OrderBy(x => x.ID); + CreateRoleGroup("Antagonist", Color.Red, antagRoles); } - private void CreateRoleGroup(string roleName, IEnumerable roleList, Color color) + /// + /// Creates a "Role group" which stores information and logic for one "group" of roll bans. + /// For example, all antags are one group, logi is a group, medical is a group, etc... + /// + private void CreateRoleGroup(string groupName, Color color, IEnumerable roles) where T : class, IPrototype { var outerContainer = new BoxContainer { - Name = $"{roleName}GroupOuterBox", + Name = $"{groupName}GroupOuterBox", HorizontalExpand = true, VerticalExpand = true, Orientation = BoxContainer.LayoutOrientation.Vertical, - Margin = new Thickness(4) + Margin = new Thickness(4), }; - var departmentCheckbox = new CheckBox + + // Stores stuff like ban all and expand buttons. + var roleGroupHeader = new BoxContainer { - Name = $"{roleName}GroupCheckbox", - Text = roleName, - Modulate = color, - HorizontalAlignment = HAlignment.Left + Orientation = BoxContainer.LayoutOrientation.Horizontal, }; - outerContainer.AddChild(departmentCheckbox); - var innerContainer = new BoxContainer + + // Stores the role checkboxes themselves. + var innerContainer = new GridContainer { - Name = $"{roleName}GroupInnerBox", + Name = $"{groupName}GroupInnerBox", HorizontalExpand = true, - Orientation = BoxContainer.LayoutOrientation.Horizontal + Columns = 2, + Visible = false, + Margin = new Thickness(15, 5, 0, 5), }; - departmentCheckbox.OnToggled += args => + + var roleGroupCheckbox = CreateRoleGroupHeader(groupName, roleGroupHeader, color, innerContainer); + + outerContainer.AddChild(roleGroupHeader); + + // Add the roles themselves + foreach (var role in roles) { - foreach (var child in innerContainer.Children) + AddRoleCheckbox(groupName, role.ID, innerContainer, roleGroupCheckbox); + } + + outerContainer.AddChild(innerContainer); + + RolesContainer.AddChild(new PanelContainer + { + PanelOverride = new StyleBoxFlat { - if (child is CheckBox c) - { - c.Pressed = args.Pressed; - } + BackgroundColor = color + } + }); + RolesContainer.AddChild(outerContainer); + RolesContainer.AddChild(new HSeparator()); + } + + private Button CreateRoleGroupHeader(string groupName, BoxContainer header, Color color, GridContainer innerContainer) + { + var roleGroupCheckbox = new Button + { + Name = $"{groupName}GroupCheckbox", + Text = "Ban all", + Margin = new Thickness(0, 0, 5, 0), + ToggleMode = true, + }; + + // When this is toggled, toggle all buttons in this group so they match. + roleGroupCheckbox.OnToggled += args => + { + foreach (var role in _roleCheckboxes[groupName]) + { + role.Pressed = args.Pressed; } if (args.Pressed) @@ -199,15 +251,12 @@ public sealed partial class BanPanel : DefaultWindow } else { - foreach (var childContainer in RolesContainer.Children) + foreach (var roleButtons in _roleCheckboxes.Values) { - if (childContainer is Container) + foreach (var button in roleButtons) { - foreach (var child in childContainer.Children) - { - if (child is CheckBox { Pressed: true }) - return; - } + if (button.Pressed) + return; } } @@ -220,38 +269,72 @@ public sealed partial class BanPanel : DefaultWindow SeverityOption.SelectId((int) newSeverity); } }; - outerContainer.AddChild(innerContainer); - foreach (var role in roleList) + + var hideButton = new Button { - AddRoleCheckbox(role, innerContainer, departmentCheckbox); - } - RolesContainer.AddChild(new PanelContainer + Text = Loc.GetString("role-bans-expand-roles") + " " + ContractedArrow, + ToggleMode = true, + }; + hideButton.OnPressed += args => { - PanelOverride = new StyleBoxFlat - { - BackgroundColor = color - } + innerContainer.Visible = args.Button.Pressed; + ((Button)args.Button).Text = args.Button.Pressed + ? Loc.GetString("role-bans-contract-roles") + " " + ExpandedArrow + : Loc.GetString("role-bans-expand-roles") + " " + ContractedArrow; + }; + header.AddChild(new Label + { + Text = groupName, + Modulate = color, + Margin = new Thickness(0, 0, 5, 0), }); - RolesContainer.AddChild(outerContainer); - RolesContainer.AddChild(new HSeparator()); + header.AddChild(roleGroupCheckbox); + header.AddChild(hideButton); + return roleGroupCheckbox; } - private void AddRoleCheckbox(string role, Control container, CheckBox header) + /// + /// Adds a checkbutton specifically for one "role" in a "group" + /// E.g. it would add the Chief Medical Officer "role" into the "Medical" group. + /// + private void AddRoleCheckbox(string group, string role, GridContainer roleGroupInnerContainer, Button roleGroupCheckbox) { - var roleCheckbox = new CheckBox + var roleCheckboxContainer = new BoxContainer(); + var roleCheckButton = new Button { Name = $"{role}RoleCheckbox", - Text = role + Text = role, + ToggleMode = true, }; - roleCheckbox.OnToggled += args => + roleCheckButton.OnToggled += args => { - if (args is { Pressed: true, Button.Parent: { } } && args.Button.Parent.Children.Where(e => e is CheckBox).All(e => ((CheckBox) e).Pressed)) - header.Pressed = args.Pressed; + // Checks the role group checkbox if all the children are pressed + if (args.Pressed && _roleCheckboxes[group].All(e => e.Pressed)) + roleGroupCheckbox.Pressed = args.Pressed; else - header.Pressed = false; + roleGroupCheckbox.Pressed = false; }; - container.AddChild(roleCheckbox); - _roleCheckboxes.Add(roleCheckbox); + + // This is adding the icon before the role name + // Yeah, this is sus, but having to split the functions up and stuff is worse imo. + if (_protoMan.TryIndex(role, out var jobPrototype) && _protoMan.TryIndex(jobPrototype.Icon, out var iconProto)) + { + var jobIconTexture = new TextureRect + { + Texture = _entMan.System().Frame0(iconProto.Icon), + TextureScale = new Vector2(2.5f, 2.5f), + Stretch = TextureRect.StretchMode.KeepCentered, + Margin = new Thickness(5, 0, 0, 0), + }; + roleCheckboxContainer.AddChild(jobIconTexture); + } + + roleCheckboxContainer.AddChild(roleCheckButton); + + roleGroupInnerContainer.AddChild(roleCheckboxContainer); + + _roleCheckboxes.TryAdd(group, []); + _roleCheckboxes[group].Add(roleCheckButton); } public void UpdateBanFlag(bool newFlag) @@ -469,7 +552,13 @@ public sealed partial class BanPanel : DefaultWindow if (_roleCheckboxes.Count == 0) throw new DebugAssertException("RoleCheckboxes was empty"); - rolesList.AddRange(_roleCheckboxes.Where(c => c is { Pressed: true, Text: { } }).Select(c => c.Text!)); + foreach (var button in _roleCheckboxes.Values.SelectMany(departmentButtons => departmentButtons)) + { + if (button is { Pressed: true, Text: not null }) + { + rolesList.Add(button.Text); + } + } if (rolesList.Count == 0) { diff --git a/Resources/Locale/en-US/administration/ui/role-bans.ftl b/Resources/Locale/en-US/administration/ui/role-bans.ftl new file mode 100644 index 0000000000..97109fdc7f --- /dev/null +++ b/Resources/Locale/en-US/administration/ui/role-bans.ftl @@ -0,0 +1,3 @@ +role-bans-ban-group = Ban All +role-bans-expand-roles = Show Roles +role-bans-contract-roles = Hide Roles From b8e8918af78999367faa81cd28f20d97c2498b94 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 21 Jun 2025 17:55:18 +0000 Subject: [PATCH 035/191] Automatic changelog update --- Resources/Changelog/Admin.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index a45e7d52bf..e40c01bbf2 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1195,5 +1195,12 @@ Entries: id: 144 time: '2025-06-21T16:34:11.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38385 +- author: beck-thompson + changes: + - message: Role ban panel is now prettier! + type: Tweak + id: 145 + time: '2025-06-21T17:54:11.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/37952 Name: Admin Order: 2 From 6b8842c44d17b6a0d870880fa9d82a33ec7a266d Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Sat, 21 Jun 2025 19:59:37 +0200 Subject: [PATCH 036/191] Generic Numeric Alerts (#38370) --- .../Alerts/GenericCounterAlertSystem.cs | 90 ++++++++++++++++++ .../Alerts/UpdateAlertSpriteEvent.cs | 5 +- Content.Client/Revenant/RevenantSystem.cs | 16 ++-- .../Systems/Alerts/AlertsUIController.cs | 3 +- .../Systems/Alerts/Controls/AlertControl.cs | 7 +- .../GenericCounterAlertComponent.cs | 63 ++++++++++++ Content.Shared/Revenant/SharedRevenant.cs | 8 -- Resources/Prototypes/Alerts/revenant.yml | 11 ++- .../Alerts/generic_counter.rsi/0.png | Bin 0 -> 131 bytes .../Alerts/generic_counter.rsi/1.png | Bin 0 -> 120 bytes .../Alerts/generic_counter.rsi/2.png | Bin 0 -> 142 bytes .../Alerts/generic_counter.rsi/3.png | Bin 0 -> 139 bytes .../Alerts/generic_counter.rsi/4.png | Bin 0 -> 135 bytes .../Alerts/generic_counter.rsi/5.png | Bin 0 -> 141 bytes .../Alerts/generic_counter.rsi/6.png | Bin 0 -> 142 bytes .../Alerts/generic_counter.rsi/7.png | Bin 0 -> 137 bytes .../Alerts/generic_counter.rsi/8.png | Bin 0 -> 134 bytes .../Alerts/generic_counter.rsi/9.png | Bin 0 -> 144 bytes .../Alerts/generic_counter.rsi/base.png | Bin 0 -> 265 bytes .../Alerts/generic_counter.rsi/meta.json | 44 +++++++++ 20 files changed, 225 insertions(+), 22 deletions(-) create mode 100644 Content.Client/Alerts/GenericCounterAlertSystem.cs create mode 100644 Content.Shared/Alert/Components/GenericCounterAlertComponent.cs create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/0.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/1.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/2.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/3.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/4.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/5.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/6.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/7.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/8.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/9.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/base.png create mode 100644 Resources/Textures/Interface/Alerts/generic_counter.rsi/meta.json diff --git a/Content.Client/Alerts/GenericCounterAlertSystem.cs b/Content.Client/Alerts/GenericCounterAlertSystem.cs new file mode 100644 index 0000000000..de9d97d063 --- /dev/null +++ b/Content.Client/Alerts/GenericCounterAlertSystem.cs @@ -0,0 +1,90 @@ +using System.Numerics; +using Content.Shared.Alert.Components; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; + +namespace Content.Client.Alerts; + +/// +/// This handles +/// +public sealed class GenericCounterAlertSystem : EntitySystem +{ + [Dependency] private readonly SpriteSystem _sprite = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnUpdateAlertSprite); + } + + private void OnUpdateAlertSprite(Entity ent, ref UpdateAlertSpriteEvent args) + { + var sprite = args.SpriteViewEnt.Comp; + + var ev = new GetGenericAlertCounterAmountEvent(args.Alert); + RaiseLocalEvent(args.ViewerEnt, ref ev); + + if (!ev.Handled) + return; + + // It cannot be null if its handled, but good to check to avoid ugly null ignores. + if (ev.Amount == null) + return; + + // How many digits can we display + var maxDigitCount = GetMaxDigitCount((ent, ent, sprite)); + + // Clamp it to a positive number that we can actually display in full (no rollover to 0) + var amount = (int) Math.Clamp(ev.Amount.Value, 0, Math.Pow(10, maxDigitCount) - 1); + + // This is super wack but ig it works? + var digitCount = ent.Comp.HideLeadingZeroes + ? amount.ToString().Length + : maxDigitCount; + + if (ent.Comp.HideLeadingZeroes) + { + for (var i = 0; i < ent.Comp.DigitKeys.Count; i++) + { + if (!_sprite.LayerMapTryGet(ent.Owner, ent.Comp.DigitKeys[i], out var layer, false)) + continue; + + _sprite.LayerSetVisible(ent.Owner, layer, i <= digitCount - 1); + } + } + + // ReSharper disable once PossibleLossOfFraction + var baseOffset = (ent.Comp.AlertSize.X - digitCount * ent.Comp.GlyphWidth) / 2 * (1f / EyeManager.PixelsPerMeter); + + for (var i = 0; i < ent.Comp.DigitKeys.Count; i++) + { + if (!_sprite.LayerMapTryGet(ent.Owner, ent.Comp.DigitKeys[i], out var layer, false)) + continue; + + var result = amount / (int) Math.Pow(10, i) % 10; + _sprite.LayerSetRsiState(ent.Owner, layer, result.ToString()); + + if (ent.Comp.CenterGlyph) + { + var offset = baseOffset + (digitCount - 1 - i) * ent.Comp.GlyphWidth * (1f / EyeManager.PixelsPerMeter); + _sprite.LayerSetOffset(ent.Owner, layer, new Vector2(offset, 0)); + } + } + } + + /// + /// Gets the number of digits that we can display. + /// + /// The number of digits. + private int GetMaxDigitCount(Entity ent) + { + for (var i = ent.Comp1.DigitKeys.Count - 1; i >= 0; i--) + { + if (_sprite.LayerExists((ent.Owner, ent.Comp2), ent.Comp1.DigitKeys[i])) + return i + 1; + } + + return 0; + } +} diff --git a/Content.Client/Alerts/UpdateAlertSpriteEvent.cs b/Content.Client/Alerts/UpdateAlertSpriteEvent.cs index 4f182c458c..d8222c2340 100644 --- a/Content.Client/Alerts/UpdateAlertSpriteEvent.cs +++ b/Content.Client/Alerts/UpdateAlertSpriteEvent.cs @@ -11,11 +11,14 @@ public record struct UpdateAlertSpriteEvent { public Entity SpriteViewEnt; + public EntityUid ViewerEnt; + public AlertPrototype Alert; - public UpdateAlertSpriteEvent(Entity spriteViewEnt, AlertPrototype alert) + public UpdateAlertSpriteEvent(Entity spriteViewEnt, EntityUid viewerEnt, AlertPrototype alert) { SpriteViewEnt = spriteViewEnt; + ViewerEnt = viewerEnt; Alert = alert; } } diff --git a/Content.Client/Revenant/RevenantSystem.cs b/Content.Client/Revenant/RevenantSystem.cs index 0534522b40..21d2d7888d 100644 --- a/Content.Client/Revenant/RevenantSystem.cs +++ b/Content.Client/Revenant/RevenantSystem.cs @@ -1,4 +1,6 @@ using Content.Client.Alerts; +using Content.Shared.Alert; +using Content.Shared.Alert.Components; using Content.Shared.Revenant; using Content.Shared.Revenant.Components; using Robust.Client.GameObjects; @@ -15,7 +17,7 @@ public sealed class RevenantSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnAppearanceChange); - SubscribeLocalEvent(OnUpdateAlert); + SubscribeLocalEvent(OnGetCounterAmount); } private void OnAppearanceChange(EntityUid uid, RevenantComponent component, ref AppearanceChangeEvent args) @@ -40,14 +42,14 @@ public sealed class RevenantSystem : EntitySystem } } - private void OnUpdateAlert(Entity ent, ref UpdateAlertSpriteEvent args) + private void OnGetCounterAmount(Entity ent, ref GetGenericAlertCounterAmountEvent args) { - if (args.Alert.ID != ent.Comp.EssenceAlert) + if (args.Handled) return; - var essence = Math.Clamp(ent.Comp.Essence.Int(), 0, 999); - _sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit1, $"{(essence / 100) % 10}"); - _sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit2, $"{(essence / 10) % 10}"); - _sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit3, $"{essence % 10}"); + if (ent.Comp.EssenceAlert != args.Alert) + return; + + args.Amount = ent.Comp.Essence.Int(); } } diff --git a/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs b/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs index 5c19512038..3fe553be3b 100644 --- a/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs +++ b/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs @@ -98,7 +98,8 @@ public sealed class AlertsUIController : UIController, IOnStateEntered(spriteViewEnt, out var sprite)) return; - var ev = new UpdateAlertSpriteEvent((spriteViewEnt, sprite), alert); + var ev = new UpdateAlertSpriteEvent((spriteViewEnt, sprite), player, alert); EntityManager.EventBus.RaiseLocalEvent(player, ref ev); + EntityManager.EventBus.RaiseLocalEvent(spriteViewEnt, ref ev); } } diff --git a/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs b/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs index 847b253586..fe22ebba40 100644 --- a/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs +++ b/Content.Client/UserInterface/Systems/Alerts/Controls/AlertControl.cs @@ -57,10 +57,15 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls _sprite = _entityManager.System(); TooltipSupplier = SupplyTooltip; Alert = alert; + + HorizontalAlignment = HAlignment.Left; _severity = severity; _icon = new SpriteView { - Scale = new Vector2(2, 2) + Scale = new Vector2(2, 2), + MaxSize = new Vector2(64, 64), + Stretch = SpriteView.StretchMode.None, + HorizontalAlignment = HAlignment.Left }; SetupIcon(); diff --git a/Content.Shared/Alert/Components/GenericCounterAlertComponent.cs b/Content.Shared/Alert/Components/GenericCounterAlertComponent.cs new file mode 100644 index 0000000000..d0c7fc1ad1 --- /dev/null +++ b/Content.Shared/Alert/Components/GenericCounterAlertComponent.cs @@ -0,0 +1,63 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Alert.Components; + +/// +/// This is used for an alert which simply displays a generic number over a texture. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class GenericCounterAlertComponent : Component +{ + /// + /// The width, in pixels, of an individual glyph, accounting for the space between glyphs. + /// A 3 pixel wide glyph with one pixel of space between it and the next would be a width of 4. + /// + [DataField] + public int GlyphWidth = 6; + + /// + /// Whether the numbers should be centered on the glyph or just follow a static position. + /// + [DataField] + public bool CenterGlyph = true; + + /// + /// Whether leading zeros should be hidden. + /// If true, "005" would display as "5". + /// + [DataField] + public bool HideLeadingZeroes = true; + + /// + /// The size of the alert sprite. + /// Used to calculate offsets. + /// + [DataField] + public Vector2i AlertSize = new(32, 32); + + /// + /// Digits that can be displayed by the alert, represented by their sprite layer. + /// Order defined corresponds to the digit it affects. 1st defined will affect 1st digit, 2nd affect 2nd digit and so on. + /// In this case ones would be on layer "1", tens on layer "10" etc. + /// + [DataField] + public List DigitKeys = new() + { + "1", + "10", + "100", + "1000", + "10000" + }; +} + +/// +/// Event raised to gather the amount the alert will display. +/// +/// The alert which is currently requesting an update. +/// The number to display on the alert. +[ByRefEvent] +public record struct GetGenericAlertCounterAmountEvent(AlertPrototype Alert, int? Amount = null) +{ + public bool Handled => Amount.HasValue; +} diff --git a/Content.Shared/Revenant/SharedRevenant.cs b/Content.Shared/Revenant/SharedRevenant.cs index 485ad26dd2..c44e4408aa 100644 --- a/Content.Shared/Revenant/SharedRevenant.cs +++ b/Content.Shared/Revenant/SharedRevenant.cs @@ -70,11 +70,3 @@ public enum RevenantVisuals : byte Stunned, Harvesting, } - -[NetSerializable, Serializable] -public enum RevenantVisualLayers : byte -{ - Digit1, - Digit2, - Digit3 -} diff --git a/Resources/Prototypes/Alerts/revenant.yml b/Resources/Prototypes/Alerts/revenant.yml index 38933df4fe..ab2b13905d 100644 --- a/Resources/Prototypes/Alerts/revenant.yml +++ b/Resources/Prototypes/Alerts/revenant.yml @@ -18,12 +18,15 @@ id: AlertEssenceSpriteView categories: [ HideSpawnMenu ] components: + - type: GenericCounterAlert + centerGlyph: false + hideLeadingZeroes: false - type: Sprite sprite: /Textures/Interface/Alerts/essence_counter.rsi layers: - map: [ "enum.AlertVisualLayers.Base" ] - - map: [ "enum.RevenantVisualLayers.Digit1" ] - - map: [ "enum.RevenantVisualLayers.Digit2" ] - offset: 0.125, 0 - - map: [ "enum.RevenantVisualLayers.Digit3" ] + - map: [ "1" ] offset: 0.25, 0 + - map: [ "10" ] + offset: 0.125, 0 + - map: [ "100" ] diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/0.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/0.png new file mode 100644 index 0000000000000000000000000000000000000000..8eb85f36dc347a26e25526f1d7ea1cfc8a18cc29 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}ww^AIArY-_ zuNd+HRk0kj{{R2^W@E8!O`X=VCONGv3_s?oYIhXa!t|JE}w bT*zfQ*(Ja}A?JM*&>#j+S3j3^P67$V-*sza4Q>Q+6z#N3l|BlP&M p`!^k4e=KeLEh6`ym7&2;IOLW5dxsYHEkH9FJYD@<);T3K0RT!wGxh)g literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/3.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/3.png new file mode 100644 index 0000000000000000000000000000000000000000..181f425acc5c5e9308e58c580b381f1ffd3488fe GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}uAVNAArY-_ zuPE{{7;rdWJoNYdotddKBh(x}%x89H<`iN`*fl}rwq0+BhHn9X{ne+dm{Xo^(9geX l`E0Jj?(6sdvokQ<6|)voe(NcCt`%q?gQu&X%Q~loCIHerE;#@I literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/4.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/4.png new file mode 100644 index 0000000000000000000000000000000000000000..fe5680eb412dca6dadb2a6f4c29781fed24c950f GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}j-D=#ArY-_ zFCF9ss&c+~?Qi+9$((BQULO1}ropPhz%Xmos+VV`^6Y2puzI^#z2oItVd)RrSGFx@ hpZMi9KLf)hGj}HWcNaGQxdAkc!PC{xWt~$(699TLFDC#1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/5.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/5.png new file mode 100644 index 0000000000000000000000000000000000000000..2361ca7f1d84086b6acd1d66b428d6f89eed079a GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}?w&4=ArY-_ zuNd+jFyLT0xcATh^GnL+Na<8AU-GcTsX^ss8RLpoQEi@4!8OcxHp`qS{OWIhN$$bR om~YYvyFR_;-y*+ICG!^d`Q6GN7A;ymA7~_lr>mdKI;Vst03L)ia{vGU literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/6.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/6.png new file mode 100644 index 0000000000000000000000000000000000000000..a636e7890b890384fdc70f9087af998a8c69472f GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}9-c0aArY-_ zuPE{{7;vy0Z2td$=B~C0S$-wQ0}pcraWZ_^Jz>eJRlfxUSF_gs%#P!~G0pafkg<5a p`;rukJ(3Sn_&@w-W?< literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/7.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/7.png new file mode 100644 index 0000000000000000000000000000000000000000..d29d6192ded80be417a454b2f24732264b8fdfc6 GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}&YmugArY-_ zFKiTKP~0WH#vh!I fSU=e_FnG*9nj`(k;6?gTe~DWM4fT8=J` literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/generic_counter.rsi/9.png b/Resources/Textures/Interface/Alerts/generic_counter.rsi/9.png new file mode 100644 index 0000000000000000000000000000000000000000..6325fbdabc40f4f58e287a92f4aa144c8520c401 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}UY;(FArY-_ zuNd+HRXJZg^!I(wWH#aOgGc_G#YD+WU|`s9?Ag^3!Jig(OQC)Kv)g-&-uz{{dVfFb q`48KKcKW2wE6=}E&(6Sbm(43i{q+aCpWlF{GI+ZBxvXxW4ZO5X@rY;Gi_uUMECfA}0_=^ADGJpN+-j%4;`yZZmYSWOKc4H62 z;qMJ$3pZ!1Jz#l8bLpo`zp^%Kul#DqVIwOjz=(tzn;lNe@0SdFxmQr)`1FU(zprvx zy*|j}C+Q>Ltk#t_>sa)0 Date: Sun, 22 Jun 2025 01:20:34 +0300 Subject: [PATCH 037/191] Fix styles for group button in loadout menu (#38488) * Fix styles color for group button in loadout menu * Show selected item count in loadout group UI Updated the loadout group container to display the number of selected items in a group. Added a new localization string to support this feature in the UI. * Resolve required changes --- .../UI/Loadouts/LoadoutGroupContainer.xaml.cs | 19 +++++++++++-------- .../Locale/en-US/preferences/loadouts.ftl | 5 +++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs b/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs index 0e18bf1ab5..b06e7e41d1 100644 --- a/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs +++ b/Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs @@ -144,8 +144,8 @@ public sealed partial class LoadoutGroupContainer : BoxContainer { subList.AddChild(proto); } - - UpdateToggleColor(toggle, subList); + var itemName = firstElement.Text ?? ""; + UpdateSubGroupSelectedInfo(firstElement, itemName, subList); } else { @@ -164,12 +164,14 @@ public sealed partial class LoadoutGroupContainer : BoxContainer }; toggle.Text = subContainer.Visible ? OpenedGroupMark : ClosedGroupMark; + toggle.Pressed = subContainer.Visible; toggle.OnPressed += _ => { var willOpen = !subContainer.Visible; subContainer.Visible = willOpen; toggle.Text = willOpen ? OpenedGroupMark : ClosedGroupMark; + toggle.Pressed = willOpen; _openedGroups[kvp.Key] = willOpen; }; @@ -178,15 +180,16 @@ public sealed partial class LoadoutGroupContainer : BoxContainer return toggle; } - private void UpdateToggleColor(Button toggle, BoxContainer subList) + private void UpdateSubGroupSelectedInfo(LoadoutContainer loadout, string itemName, BoxContainer subList) { - var anyActive = subList.Children + var countSubSelected = subList.Children .OfType() - .Any(c => c.Select.Pressed); + .Count(c => c.Select.Pressed); - toggle.Modulate = anyActive - ? Color.Green - : Color.White; + if (countSubSelected > 0) + { + loadout.Text = Loc.GetString("loadouts-count-items-in-group", ("item", itemName), ("count", countSubSelected)); + } } /// diff --git a/Resources/Locale/en-US/preferences/loadouts.ftl b/Resources/Locale/en-US/preferences/loadouts.ftl index f32c65eb24..2db7424fb1 100644 --- a/Resources/Locale/en-US/preferences/loadouts.ftl +++ b/Resources/Locale/en-US/preferences/loadouts.ftl @@ -9,4 +9,9 @@ loadouts-min-limit = Min count: {$count} loadouts-max-limit = Max count: {$count} loadouts-points-limit = Points: {$count} / {$max} +loadouts-count-items-in-group = {$item} and {$count} other {$count -> +[1] item +*[other] items +} + loadouts-points-restriction = Insufficient points From ef55c3db0685837561d12a202b14a7b9afaf92fc Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 21 Jun 2025 22:21:41 +0000 Subject: [PATCH 038/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index fe1f7c4e0c..b9aef721e6 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: EmoGarbage404 - changes: - - message: Sentient artifacts can once again activate themselves. - type: Tweak - - message: Increased sentient artifact self activation cooldown. - type: Tweak - id: 8190 - time: '2025-04-15T22:24:58.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36598 - author: EmoGarbage404 changes: - message: When an artifact's node is unlocked, the effect will now also occur. @@ -3906,3 +3897,10 @@ id: 8702 time: '2025-06-21T14:52:36.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38032 +- author: qrwas + changes: + - message: Loadout item group UI improvements + type: Tweak + id: 8703 + time: '2025-06-21T22:20:34.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38488 From 793f525c6bca590719d6f6ac8460fcc90dc22e7c Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Sat, 21 Jun 2025 18:23:39 -0400 Subject: [PATCH 039/191] fix: mops with liquid can hit people again (#38486) --- .../Fluids/EntitySystems/PuddleSystem.Spillable.cs | 3 ++- Content.Shared/Fluids/Components/SpillableComponent.cs | 8 ++++++++ .../Entities/Objects/Specific/Janitorial/janitor.yml | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs index 296b397146..880a4395b4 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs @@ -69,7 +69,8 @@ public sealed partial class PuddleSystem if (totalSplit == 0) return; - args.Handled = true; + // Optionally allow further melee handling occur + args.Handled = entity.Comp.PreventMelee; // First update the hit count so anything that is not reactive wont count towards the total! foreach (var hit in args.HitEntities) diff --git a/Content.Shared/Fluids/Components/SpillableComponent.cs b/Content.Shared/Fluids/Components/SpillableComponent.cs index cc57dbf2b6..d320be7dac 100644 --- a/Content.Shared/Fluids/Components/SpillableComponent.cs +++ b/Content.Shared/Fluids/Components/SpillableComponent.cs @@ -28,4 +28,12 @@ public sealed partial class SpillableComponent : Component /// [DataField] public bool SpillWhenThrown = true; + + /// + /// If true, melee processing will stop if any reagent is transferred. + /// Otherwise, melee processing keeps occuring allowing both reagent + /// transfer and melee damage to happen. + /// + [DataField] + public bool PreventMelee = true; } diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index a1c36e140d..49287d4a2a 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -26,6 +26,7 @@ - type: Spillable solution: absorbed spillWhenThrown: false + preventMelee: false - type: DrainableSolution solution: absorbed - type: Wieldable @@ -84,6 +85,7 @@ - type: Spillable solution: absorbed spillWhenThrown: false + preventMelee: false - type: DrainableSolution solution: absorbed - type: Wieldable From 1702e75a0b13a234c76b5202110e10a97f5d538d Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 21 Jun 2025 22:24:46 +0000 Subject: [PATCH 040/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index b9aef721e6..9f72513567 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,16 +1,4 @@ Entries: -- author: EmoGarbage404 - changes: - - message: When an artifact's node is unlocked, the effect will now also occur. - Effects can still be triggered manually through interaction. - type: Add - - message: Artifacts will now have more nodes on average. - type: Tweak - - message: Higher-class artifact nodes now grant significantly more points. - type: Tweak - id: 8191 - time: '2025-04-15T22:38:26.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36604 - author: EmoGarbage404 changes: - message: Restored Artifexium's interaction with artifacts. Now, a single spray @@ -3904,3 +3892,10 @@ id: 8703 time: '2025-06-21T22:20:34.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38488 +- author: perryprog + changes: + - message: Wet mops no longer have zero melee damage. + type: Fix + id: 8704 + time: '2025-06-21T22:23:39.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38486 From 6a582db4b8734efec4a14fe40bfb94a994930430 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Sat, 21 Jun 2025 18:25:20 -0400 Subject: [PATCH 041/191] Treat duplicate dependency warnings as errors (#38480) * Treat duplicate dependency warnings as errors * GitHub workflows * commas * Maybe we just don't use these? * I think we can get rid of these? --- .github/workflows/build-docfx.yml | 2 +- .github/workflows/build-map-renderer.yml | 2 +- .github/workflows/build-test-debug.yml | 2 +- Content.Client/Content.Client.csproj | 2 +- Content.Replay/Content.Replay.csproj | 2 +- Content.Server/Content.Server.csproj | 2 +- Content.Shared/Content.Shared.csproj | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-docfx.yml b/.github/workflows/build-docfx.yml index 3ef0497496..1f010b7291 100644 --- a/.github/workflows/build-docfx.yml +++ b/.github/workflows/build-docfx.yml @@ -27,7 +27,7 @@ jobs: run: dotnet restore - name: Build Project - run: dotnet build --no-restore /p:WarningsAsErrors=nullable + run: dotnet build --no-restore - name: Build DocFX uses: nikeee/docfx-action@v1.0.0 diff --git a/.github/workflows/build-map-renderer.yml b/.github/workflows/build-map-renderer.yml index 3d01618348..f93f4b25ae 100644 --- a/.github/workflows/build-map-renderer.yml +++ b/.github/workflows/build-map-renderer.yml @@ -42,7 +42,7 @@ jobs: run: dotnet restore - name: Build Project - run: dotnet build Content.MapRenderer --configuration Release --no-restore /p:WarningsAsErrors=nullable /m + run: dotnet build Content.MapRenderer --configuration Release --no-restore /m - name: Run Map Renderer run: dotnet run --project Content.MapRenderer Dev diff --git a/.github/workflows/build-test-debug.yml b/.github/workflows/build-test-debug.yml index 369239aecf..4e391b2aef 100644 --- a/.github/workflows/build-test-debug.yml +++ b/.github/workflows/build-test-debug.yml @@ -42,7 +42,7 @@ jobs: run: dotnet restore - name: Build Project - run: dotnet build --configuration DebugOpt --no-restore /p:WarningsAsErrors=nullable /m + run: dotnet build --configuration DebugOpt --no-restore /m - name: Run Content.Tests run: dotnet test --no-build --configuration DebugOpt Content.Tests/Content.Tests.csproj -- NUnit.ConsoleOut=0 diff --git a/Content.Client/Content.Client.csproj b/Content.Client/Content.Client.csproj index 43fd8df2e1..d8855ce508 100644 --- a/Content.Client/Content.Client.csproj +++ b/Content.Client/Content.Client.csproj @@ -7,7 +7,7 @@ false ..\bin\Content.Client\ Exe - nullable + RA0032;nullable enable Debug;Release;Tools;DebugOpt AnyCPU diff --git a/Content.Replay/Content.Replay.csproj b/Content.Replay/Content.Replay.csproj index 9785870143..28cfc7f666 100644 --- a/Content.Replay/Content.Replay.csproj +++ b/Content.Replay/Content.Replay.csproj @@ -6,7 +6,7 @@ false ..\bin\Content.Replay\ Exe - nullable + RA0032;nullable enable diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index ffc4df7ed1..e2b65b873c 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -9,7 +9,7 @@ true Exe 1998 - nullable + RA0032;nullable enable true diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index 4cca399b28..52ed717e96 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -5,7 +5,7 @@ 12 false false - nullable + RA0032;nullable enable From 2e90bc7b6dca98a336331446de9a116ea3d07bec Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:24:12 +0200 Subject: [PATCH 042/191] Retractable items get removed by handcuffs (#38441) * init * oops * happens * review * fix --- Content.Shared/Cuffs/SharedCuffableSystem.cs | 29 ++++++++--- .../EntitySystems/SharedHandsSystem.Relay.cs | 2 + .../RetractableItemActionSystem.cs | 49 ++++++++++++++++--- 3 files changed, 65 insertions(+), 15 deletions(-) diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index 8c4a871177..c55bbdb152 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Interaction.Components; using Content.Shared.Interaction.Events; +using Content.Shared.Inventory; using Content.Shared.Inventory.Events; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Item; @@ -472,6 +473,9 @@ namespace Content.Shared.Cuffs if (TryComp(target, out var hands) && hands.Count <= component.CuffedHandCount) return false; + var ev = new TargetHandcuffedEvent(); + RaiseLocalEvent(target, ref ev); + // Success! _hands.TryDrop(user, handcuff); @@ -807,15 +811,24 @@ namespace Content.Shared.Cuffs { return component.Container.ContainedEntities; } + } - [Serializable, NetSerializable] - private sealed partial class UnCuffDoAfterEvent : SimpleDoAfterEvent - { - } + [Serializable, NetSerializable] + public sealed partial class UnCuffDoAfterEvent : SimpleDoAfterEvent; - [Serializable, NetSerializable] - private sealed partial class AddCuffDoAfterEvent : SimpleDoAfterEvent - { - } + [Serializable, NetSerializable] + public sealed partial class AddCuffDoAfterEvent : SimpleDoAfterEvent; + + /// + /// Raised on the target when they get handcuffed. + /// Relayed to their held items. + /// + [ByRefEvent] + public record struct TargetHandcuffedEvent : IInventoryRelayEvent + { + /// + /// All slots to relay to + /// + public SlotFlags TargetSlots { get; set; } } } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs index 67db193894..e6f21abf1b 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs @@ -1,5 +1,6 @@ using Content.Shared.Atmos; using Content.Shared.Camera; +using Content.Shared.Cuffs; using Content.Shared.Hands.Components; using Content.Shared.Movement.Systems; using Content.Shared.Projectiles; @@ -22,6 +23,7 @@ public abstract partial class SharedHandsSystem SubscribeLocalEvent(RefRelayEvent); SubscribeLocalEvent(RefRelayEvent); SubscribeLocalEvent(RefRelayEvent); + SubscribeLocalEvent(RefRelayEvent); } private void RelayEvent(Entity entity, ref T args) where T : EntityEventArgs diff --git a/Content.Shared/RetractableItemAction/RetractableItemActionSystem.cs b/Content.Shared/RetractableItemAction/RetractableItemActionSystem.cs index b99b653cf0..c24da14c68 100644 --- a/Content.Shared/RetractableItemAction/RetractableItemActionSystem.cs +++ b/Content.Shared/RetractableItemAction/RetractableItemActionSystem.cs @@ -1,6 +1,10 @@ using Content.Shared.Actions; +using Content.Shared.Cuffs; +using Content.Shared.Hands; +using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction.Components; +using Content.Shared.Inventory; using Content.Shared.Popups; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; @@ -26,6 +30,7 @@ public sealed class RetractableItemActionSystem : EntitySystem SubscribeLocalEvent(OnRetractableItemAction); SubscribeLocalEvent(OnActionSummonedShutdown); + Subs.SubscribeWithRelay>(OnItemHandcuffed, inventory: false); } private void OnActionInit(Entity ent, ref MapInitEvent args) @@ -58,16 +63,11 @@ public sealed class RetractableItemActionSystem : EntitySystem if (_hands.IsHolding(args.Performer, ent.Comp.ActionItemUid)) { - RemComp(ent.Comp.ActionItemUid.Value); - var container = _containers.GetContainer(ent, RetractableItemActionComponent.ContainerId); - _containers.Insert(ent.Comp.ActionItemUid.Value, container); - _audio.PlayPredicted(ent.Comp.RetractSounds, action.Comp.AttachedEntity.Value, action.Comp.AttachedEntity.Value); + RetractRetractableItem(args.Performer, ent.Comp.ActionItemUid.Value, ent.Owner); } else { - _hands.TryForcePickup(args.Performer, ent.Comp.ActionItemUid.Value, userHand, checkActionBlocker: false); - _audio.PlayPredicted(ent.Comp.SummonSounds, action.Comp.AttachedEntity.Value, action.Comp.AttachedEntity.Value); - EnsureComp(ent.Comp.ActionItemUid.Value); + SummonRetractableItem(args.Performer, ent.Comp.ActionItemUid.Value, userHand, ent.Owner); } args.Handled = true; @@ -85,6 +85,20 @@ public sealed class RetractableItemActionSystem : EntitySystem PopulateActionItem(action.Owner); } + private void OnItemHandcuffed(Entity ent, ref HeldRelayedEvent args) + { + if (_actions.GetAction(ent.Comp.SummoningAction) is not { } action) + return; + + if (action.Comp.AttachedEntity == null) + return; + + if (_hands.GetActiveHand(action.Comp.AttachedEntity.Value) is not { } userHand) + return; + + RetractRetractableItem(action.Comp.AttachedEntity.Value, ent, action.Owner); + } + private void PopulateActionItem(Entity ent) { if (!Resolve(ent.Owner, ref ent.Comp, false) || TerminatingOrDeleted(ent)) @@ -102,4 +116,25 @@ public sealed class RetractableItemActionSystem : EntitySystem Dirty(ent); } + + private void RetractRetractableItem(EntityUid holder, EntityUid item, Entity action) + { + if (!Resolve(action, ref action.Comp, false)) + return; + + RemComp(item); + var container = _containers.GetContainer(action, RetractableItemActionComponent.ContainerId); + _containers.Insert(item, container); + _audio.PlayPredicted(action.Comp.RetractSounds, holder, holder); + } + + private void SummonRetractableItem(EntityUid holder, EntityUid item, Hand hand, Entity action) + { + if (!Resolve(action, ref action.Comp, false)) + return; + + _hands.TryForcePickup(holder, item, hand, checkActionBlocker: false); + _audio.PlayPredicted(action.Comp.SummonSounds, holder, holder); + EnsureComp(item); + } } From 019173c33847d8050d3eb444e7350fb8596a04f1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 21 Jun 2025 20:37:44 -0400 Subject: [PATCH 043/191] Update Credits (#38493) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index e8bf94fb30..08b0911101 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0leshe, 0tito, 0x6273, 12rabbits, 1337dakota, 13spacemen, 154942, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 3nderall, 4310v343k, 4dplanner, 612git, 778b, aaron, abadaba695, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aerocrux, Aeshus, Aexolott, Aexxie, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, Ahion, aiden, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alice4267, Alithsko, alliephante, ALMv1, Alpaccalypse, Alpha-Two, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, AndrewEyeke, AndreyCamper, Anzarot121, ApolloVector, Appiah, ar4ill, archee1, ArchPigeon, ArchRBX, areitpog, Arendian, arimah, Arkanic, ArkiveDev, armoks, Arteben, ArthurMousatov, ArtisticRoomba, artur, ArZarLordOfMango, as334, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, Awlod, AzzyIsNotHere, azzyisnothere, B-Kirill, B3CKDOOR, baa14453, BackeTako, Bakke, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, beck-thompson, bellwetherlogic, ben, benbryant0, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BigfootBravo, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlitzTheSquishy, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Bokser815, bolantej, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, BriBrooo, Bright0, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, bvelliquette, BWTCK, byondfuckery, c0rigin, c4llv07e, CaasGit, Caconym27, Calecute, Callmore, Camdot, capnsockless, CaptainMaru, captainsqrbeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, catlord, Catofquestionableethics, CatTheSystem, Centronias, Chaboricks, chairbender, Chaoticaa, Charlese2, charlie, chartman, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, ciaran, civilCornball, claustro305, Clement-O, clyf, Clyybber, CMDR-Piboy314, cnv41, coco, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, crazybrain23, Crazydave91920, creadth, CrigCrag, croilbird, Crotalus, CrudeWax, cryals, CrzyPotato, cubixthree, cutemoongod, Cyberboss, d34d10cc, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dan, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkenson, DawBla, Daxxi3, dch-GH, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, DebugOk, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, dexlerxd, dffdff2423, DieselMohawk, digitalic, Dimastra, DinnerCalzone, DinoWattz, Disp-Dev, DisposableCrewmember42, dissidentbullet, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, Emisse, emmafornash, EmoGarbage404, Endecc, Entvari, eoineoineoin, ephememory, eris, erohrs2, ERORR404V1, Errant-4, ertanic, esguard, estacaoespacialpirata, eugene, ewokswagger, exincore, exp111, f0x-n3rd, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, FillerVK, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FrostRibbon, Funce, FungiFellow, FunTust, Futuristic-OK, GalacticChimp, gamer3107, Gamewar360, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, genderGeometries, GeneralGaws, Genkail, Gentleman-Bird, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, gituhabu, GlassEclipse, GnarpGnarp, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, golubgik, GoodWheatley, Gorox221, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, Hardly3D, harikattar, he1acdvv, Hebi, helm4142, Henry, HerCoyote23, HighTechPuddle, Hitlinemoss, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hoshizora, Hreno, Hrosts, htmlsystem, hubismal, Hugal31, Huxellberger, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imatsoup, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, jacksonzck, Jacktastic09, Jackw2As, jacob, jamessimo, janekvap, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, JCGWE30, jerryimmouse, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jkwookee, jmcb, JohnGinnane, johnku1, Jophire, joshepvodka, JpegOfAFrog, jproads, JrInventor05, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, kaiserbirch, KaiShibaa, kalane15, kalanosh, KamTheSythe, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, kerisargit, keronshb, KIBORG04, KieueCaprie, Killerqu00, Kimpes, KingFroozy, kira-er, kiri-yoshikage, Kirillcas, Kirus59, Kistras, Kit0vras, KittenColony, Kittygyat, klaypexx, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kosticia, koteq, KrasnoshchekovPavel, Krunklehorn, Kupie, kxvvv, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, lanedon, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, lettern, LetterN, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, LightVillet, liltenhead, linkbro1, LinkUyx, Litraxx, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, ljm862, lmsnoise, localcc, lokachop, Lomcastar, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luizwritescode, Lukasz825700516, luminight, lunarcomets, Lusatia, lvvova1, Lyndomen, lyroth001, lzimann, lzk228, M1tht1c, M3739, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, MagnusCrowe, malchanceux, MaloTV, ManelNavola, manelnavola, Mangohydra, marboww, Markek1, matt, Matz05, max, MaxNox7, maylokana, MehimoNemo, MeltedPixel, memeproof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, misandrie, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, Mixelz, mjarduk, MjrLandWhale, mkanke-real, MLGTASTICa, moderatelyaware, modern-nm, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, Morb0, MossyGreySlope, mr-bo-jangles, Mr0maks, MrFippik, mrrobdemo, muburu, MureixloI, murolem, musicmanvr, MWKane, Myakot, Myctai, N3X15, nabegator, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, NazrinNya, neutrino-laser, NickPowers43, nikitosych, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, nkokic, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, not-gavnaed, notafet, notquitehadouken, NotSoDana, noudoit, noverd, Nox38, NuclearWinter, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, Nyxilath, och-och, OctoRocket, OldDanceJacket, OliverOtter, onesch, OneZerooo0, OnyxTheBrave, Orange-Winds, OrangeMoronage9622, Orsoniks, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, peccneck, Peptide90, peptron1, perryprog, PeterFuto, PetMudstone, pewter-wiz, Pgriha, Phantom-Lily, pheenty, philingham, Phill101, Phooooooooooooooooooooooooooooooosphate, phunnyguy, PicklOH, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, Pissachu, pissdemon, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, PotRoastPiggy, Princess-Cheeseballs, ProfanedBane, PROG-MohamedDwidar, Prole0, ProPandaBear, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, py01, qrtDaniil, qrwas, Quantum-cross, quatre, QueerNB, QuietlyWhisper, qwerltaz, Radezolid, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, ReeZer2, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, RobbyTheFish, robinthedragon, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, rosieposieeee, Roudenn, router, ruddygreat, RumiTiger, Ruzihm, S1rFl0, S1ss3l, Saakra, Sadie-silly, saga3152, saintmuntzer, Salex08, sam, samgithubaccount, Samuka-C, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SharkSnake98, shibechef, SignalWalker, siigiil, silicon14wastaken, Simyon264, sirdragooon, Sirionaut, Sk1tch, SkaldetSkaeg, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, snebl, snicket, sniperchance, Snowni, snowsignal, SolidusSnek, solstar2, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, soupkilove, southbridge-fur, sowelipililimute, Soydium, spacelizard, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, Spangs04, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, stellar-novas, stewie523, stomf, Stop-Signs, stopbreaking, stopka-html, StrawberryMoses, Stray-Pyramid, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, supergdpwyl, superjj18, Supernorn, SweptWasTaken, SyaoranFox, Sybil, SYNCHRONIC, Szunti, t, Tainakov, takemysoult, tap, TaralGit, Taran, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telyonok, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, TheBlueYowie, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, TheFlyingSentry, TheIntoxicatedCat, thekilk, themias, theomund, TheProNoob678, TherapyGoth, TheShuEd, thetolbean, thevinter, TheWaffleJesus, thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, TornadoTechnology, tosatur, TotallyLemon, ToxicSonicFan04, Tr1bute, trixxedbit, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, ubis1, UBlueberry, uhbg, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, Unkn0wnGh0st333, unusualcrow, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, Verslebas, vexerot, viceemargo, VigersRay, violet754, Visne, vitusveit, vlad, vlados1408, VMSolidus, vmzd, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wafehling, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Will-Oliver-Br, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, Wolfkey-SomeoneElseTookMyUsername, wrexbe, wtcwr68, xeri7, xkreksx, xprospero, xRiriq, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, YuriyKiss, yuriykiss, zach-hill, Zadeon, Zalycon, zamp, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex +0leshe, 0tito, 0x6273, 12rabbits, 1337dakota, 13spacemen, 154942, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 3nderall, 4310v343k, 4dplanner, 612git, 778b, aaron, abadaba695, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aerocrux, Aeshus, Aexolott, Aexxie, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, Ahion, aiden, Aidenkrz, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alice4267, Alithsko, alliephante, ALMv1, Alpaccalypse, Alpha-Two, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, AndrewEyeke, AndreyCamper, Anzarot121, ApolloVector, Appiah, ar4ill, archee1, ArchPigeon, ArchRBX, areitpog, Arendian, arimah, Arkanic, ArkiveDev, armoks, Arteben, ArthurMousatov, ArtisticRoomba, artur, ArZarLordOfMango, as334, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, Awlod, azzyisnothere, AzzyIsNotHere, B-Kirill, B3CKDOOR, baa14453, BackeTako, Bakke, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, beck-thompson, bellwetherlogic, ben, benbryant0, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BigfootBravo, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlitzTheSquishy, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Bokser815, bolantej, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, BriBrooo, Bright0, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, bvelliquette, BWTCK, byondfuckery, c0rigin, c4llv07e, CaasGit, Caconym27, Calecute, Callmore, Camdot, capnsockless, CaptainMaru, captainsqrbeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, catlord, Catofquestionableethics, CatTheSystem, Centronias, Chaboricks, chairbender, Chaoticaa, Charlese2, charlie, chartman, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, ciaran, citrea, civilCornball, claustro305, Clement-O, clyf, Clyybber, CMDR-Piboy314, cnv41, coco, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, crazybrain23, Crazydave91920, creadth, CrigCrag, croilbird, Crotalus, CrudeWax, cryals, CrzyPotato, cubixthree, cutemoongod, Cyberboss, d34d10cc, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dan, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkenson, DawBla, Daxxi3, dch-GH, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, DebugOk, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, dexlerxd, dffdff2423, DieselMohawk, digitalic, Dimastra, DinnerCalzone, DinoWattz, Disp-Dev, DisposableCrewmember42, dissidentbullet, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, Emisse, emmafornash, EmoGarbage404, Endecc, Entvari, eoineoineoin, ephememory, eris, erohrs2, ERORR404V1, Errant-4, ertanic, esguard, estacaoespacialpirata, eugene, ewokswagger, exincore, exp111, f0x-n3rd, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, FillerVK, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FrostRibbon, Funce, FungiFellow, FunTust, Futuristic-OK, GalacticChimp, gamer3107, Gamewar360, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, genderGeometries, GeneralGaws, Genkail, Gentleman-Bird, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, gituhabu, GlassEclipse, GnarpGnarp, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, golubgik, GoodWheatley, Gorox221, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, Hardly3D, harikattar, he1acdvv, Hebi, Helix-ctrl, helm4142, Henry, HerCoyote23, HighTechPuddle, Hitlinemoss, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hoshizora, Hreno, Hrosts, htmlsystem, hubismal, Hugal31, Huxellberger, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imatsoup, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, jacksonzck, Jacktastic09, Jackw2As, jacob, jamessimo, janekvap, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, JCGWE30, jerryimmouse, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jkwookee, jmcb, JohnGinnane, johnku1, Jophire, joshepvodka, JpegOfAFrog, jproads, JrInventor05, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, kaiserbirch, KaiShibaa, kalane15, kalanosh, KamTheSythe, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, kerisargit, keronshb, KIBORG04, KieueCaprie, Killerqu00, Kimpes, KingFroozy, kira-er, kiri-yoshikage, Kirillcas, Kirus59, Kistras, Kit0vras, KittenColony, Kittygyat, klaypexx, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kosticia, koteq, KrasnoshchekovPavel, Krunklehorn, Kupie, kxvvv, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, lanedon, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, lettern, LetterN, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, LightVillet, liltenhead, linkbro1, LinkUyx, Litraxx, little-meow-meow, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, ljm862, lmsnoise, localcc, lokachop, Lomcastar, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luizwritescode, Lukasz825700516, luminight, lunarcomets, Lusatia, lvvova1, Lyndomen, lyroth001, lzimann, lzk228, M1tht1c, M3739, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, MagnusCrowe, malchanceux, MaloTV, ManelNavola, manelnavola, Mangohydra, marboww, Markek1, matt, Matz05, max, MaxNox7, maylokana, MehimoNemo, MeltedPixel, memeproof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, misandrie, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, Mixelz, mjarduk, MjrLandWhale, mkanke-real, MLGTASTICa, moderatelyaware, modern-nm, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, Morb0, MossyGreySlope, mr-bo-jangles, Mr0maks, MrFippik, mrrobdemo, muburu, MureixloI, murolem, musicmanvr, MWKane, Myakot, Myctai, N3X15, nabegator, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, NazrinNya, neutrino-laser, NickPowers43, nikitosych, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, nkokic, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, not-gavnaed, notafet, notquitehadouken, NotSoDana, noudoit, noverd, Nox38, NuclearWinter, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, Nyxilath, och-och, OctoRocket, OldDanceJacket, OliverOtter, onesch, OneZerooo0, OnyxTheBrave, Orange-Winds, OrangeMoronage9622, Orsoniks, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, peccneck, Peptide90, peptron1, perryprog, PeterFuto, PetMudstone, pewter-wiz, Pgriha, Phantom-Lily, pheenty, philingham, Phill101, Phooooooooooooooooooooooooooooooosphate, phunnyguy, PicklOH, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, Pissachu, pissdemon, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, PotRoastPiggy, Princess-Cheeseballs, ProfanedBane, PROG-MohamedDwidar, Prole0, ProPandaBear, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, py01, qrtDaniil, qrwas, Quantum-cross, quatre, QueerNB, QuietlyWhisper, qwerltaz, Radezolid, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, ReeZer2, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, RobbyTheFish, robinthedragon, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, rosieposieeee, Roudenn, router, ruddygreat, RumiTiger, Ruzihm, S1rFl0, S1ss3l, Saakra, Sadie-silly, saga3152, saintmuntzer, Salex08, sam, samgithubaccount, Samuka-C, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SharkSnake98, shibechef, SignalWalker, siigiil, silicon14wastaken, Simyon264, sirdragooon, Sirionaut, Sk1tch, SkaldetSkaeg, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, snebl, snicket, sniperchance, Snowni, snowsignal, SolidusSnek, solstar2, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, soupkilove, southbridge-fur, sowelipililimute, Soydium, spacelizard, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, Spangs04, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, stellar-novas, stewie523, stomf, Stop-Signs, stopbreaking, stopka-html, StrawberryMoses, Stray-Pyramid, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, supergdpwyl, superjj18, Supernorn, SweptWasTaken, SyaoranFox, Sybil, SYNCHRONIC, Szunti, t, Tainakov, takemysoult, tap, TaralGit, Taran, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telyonok, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, TheBlueYowie, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, TheFlyingSentry, TheIntoxicatedCat, thekilk, themias, theomund, TheProNoob678, TherapyGoth, ThereDrD0, TheShuEd, thetolbean, thevinter, TheWaffleJesus, thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, TornadoTechnology, tosatur, TotallyLemon, ToxicSonicFan04, Tr1bute, trixxedbit, TrixxedHeart, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, ubis1, UBlueberry, uhbg, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, unusualcrow, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, Verslebas, vexerot, viceemargo, VigersRay, violet754, Visne, vitusveit, vlad, vlados1408, VMSolidus, vmzd, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wafehling, walksanatora, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Will-Oliver-Br, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, Wolfkey-SomeoneElseTookMyUsername, wrexbe, wtcwr68, xeri7, xkreksx, xprospero, xRiriq, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, yuriykiss, YuriyKiss, zach-hill, Zadeon, Zalycon, zamp, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex From 17f033e4f42dcdafef76e2b2f081b164f3dad43a Mon Sep 17 00:00:00 2001 From: Moomoobeef <62638182+Moomoobeef@users.noreply.github.com> Date: Sun, 22 Jun 2025 03:47:56 -0700 Subject: [PATCH 044/191] Fixed dirt tiles having identical names (#38500) * added prefix to planet dirt/grass * prefixes don't work, just doing renaming instead. --- Resources/Locale/en-US/tiles/tiles.ftl | 1 + Resources/Prototypes/Tiles/planet.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Resources/Locale/en-US/tiles/tiles.ftl b/Resources/Locale/en-US/tiles/tiles.ftl index a88c4e6dc1..02df11cbe0 100644 --- a/Resources/Locale/en-US/tiles/tiles.ftl +++ b/Resources/Locale/en-US/tiles/tiles.ftl @@ -119,6 +119,7 @@ tiles-wood2 = wood pattern floor tiles-desert-floor = desert floor tiles-low-desert-floor = low desert floor tiles-grass-planet-floor = grass planet floor +tiles-dirt-planet-floor = dirt planet floor tiles-basalt-floor = basalt floor tiles-snow-floor = snow floor tiles-wood3 = wood broken floor diff --git a/Resources/Prototypes/Tiles/planet.yml b/Resources/Prototypes/Tiles/planet.yml index f478f4e5a4..f0ef0f1346 100644 --- a/Resources/Prototypes/Tiles/planet.yml +++ b/Resources/Prototypes/Tiles/planet.yml @@ -1,6 +1,6 @@ - type: tile id: FloorPlanetDirt - name: tiles-dirt-floor + name: tiles-dirt-planet-floor sprite: /Textures/Tiles/Planet/dirt.rsi/dirt.png variants: 4 placementVariants: From c03afeb29cf3ed5e751e8d206e79f9f24981484e Mon Sep 17 00:00:00 2001 From: keronshb <54602815+keronshb@users.noreply.github.com> Date: Sun, 22 Jun 2025 12:50:59 -0400 Subject: [PATCH 045/191] Readds the Hypereutactic Blade for traitors, adds Hypereutatic blade for Nukies (#37182) * adds hypereutactic back into the uplinks * Disables hum * Adds the Hypereutatic Blade for Nukies * Actually makes the sound so quiet no one can hear it * Apply suggestions from code review * Update Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../Locale/en-US/store/uplink-catalog.ftl | 3 ++ .../Prototypes/Catalog/uplink_catalog.yml | 46 +++++++++++++++++++ .../Objects/Weapons/Melee/e_sword.yml | 22 +++++++-- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 85b1585c1f..70800e498c 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -23,6 +23,9 @@ uplink-esword-double-desc = A much more expensive counter part to the normal ene uplink-hypereutactic-blade-name = Hypereutactic Blade uplink-hypereutactic-blade-desc = A gigantic energy sword with power that matches its looks. Requires two hands. Slow and unwieldy, yet pretty adept at reflecting. Previously made infamous by an operative wearing a joy mask. You wouldn't want to see this coming at you down the hall! +uplink-hypereutatic-blade-name = Hypereutatic Blade +uplink-hypereutatic-blade-desc = A gigantic off-brand energy sword. Requires two hands. Slow and unwieldy, can reflect decently. Often mistaken for the Hypereutactic Blade. + uplink-edagger-name = Energy Dagger uplink-edagger-desc = A small energy blade conveniently disguised in the form of a pen. diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 2095edabff..653a17e5f8 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -66,6 +66,52 @@ categories: - UplinkWeaponry +- type: listing + id: UplinkHyperEutacticBlade + name: uplink-hypereutactic-blade-name + description: uplink-hypereutactic-blade-desc + icon: { sprite: /Textures/Objects/Weapons/Melee/hypereutactic_blade.rsi, state: icon } + discountCategory: veryRareDiscounts + discountDownTo: + Telecrystal: 15 + productEntity: HyperEutacticBlade + cost: + Telecrystal: 18 + categories: + - UplinkWeaponry + conditions: + - !type:BuyerWhitelistCondition + blacklist: + components: + - SurplusBundle + - !type:StoreWhitelistCondition + blacklist: + tags: + - NukeOpsUplink + +- type: listing + id: UplinkHyperEutaticBlade + name: uplink-hypereutatic-blade-name + description: uplink-hypereutatic-blade-desc + icon: { sprite: /Textures/Objects/Weapons/Melee/hypereutactic_blade.rsi, state: icon } + discountCategory: veryRareDiscounts + discountDownTo: + Telecrystal: 13 + productEntity: HyperEutaticBlade + cost: + Telecrystal: 16 + categories: + - UplinkWeaponry + conditions: + - !type:BuyerWhitelistCondition + blacklist: + components: + - SurplusBundle + - !type:StoreWhitelistCondition + whitelist: + tags: + - NukeOpsUplink + - type: listing id: UplinkEnergyDagger name: uplink-edagger-name diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index f879e2891e..21554eba07 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -296,11 +296,12 @@ Slash: 12 Heat: 12 Structural: 15 - - type: ItemToggleActiveSound - activeSound: - path: /Audio/Weapons/ebladehum.ogg - params: - volume: 3 + # Disabled until the wield active sfx no longer stacks + #- type: ItemToggleActiveSound + # activeSound: + # path: /Audio/Weapons/ebladehum.ogg + # params: + # volume: 3 - type: ComponentToggler components: - type: Sharp @@ -386,6 +387,17 @@ reflectProb: 1.0 spread: 75 +# Nukie variant, reduced reflection rate. +- type: entity + parent: HyperEutacticBlade + id: HyperEutaticBlade + name: hypereutatic-blade + description: Often mistaken for the Hypereutactic Blade. This mass produced, off-brand, knockoff gets the same job done but with less reflection. + components: + - type: Reflect + reflectProb: 0.75 + spread: 75 + # Borgs - type: entity suffix: One-Handed, For Borgs From e47f040f17987a92b2a1b57198769b77b5b3b7c6 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 22 Jun 2025 16:52:09 +0000 Subject: [PATCH 046/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 9f72513567..d91191f6df 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- author: EmoGarbage404 - changes: - - message: Restored Artifexium's interaction with artifacts. Now, a single spray - will randomly activate one of the triggers for a node, allowing you to subvert - puzzling artifacts. - type: Add - id: 8192 - time: '2025-04-15T22:49:58.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36595 - author: slarticodefast, Flareguy changes: - message: Added the Desynchronizer, an experimental device that allows the user @@ -3899,3 +3890,12 @@ id: 8704 time: '2025-06-21T22:23:39.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38486 +- author: keronshb + changes: + - message: Added the Hypereutactic Blade back to the Traitor Uplink + type: Add + - message: Added the Hypereutatic Blade to the Nukie uplink + type: Add + id: 8705 + time: '2025-06-22T16:50:59.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/37182 From 820f0dc1dc7919116476b9dbb16ca1aac20da71c Mon Sep 17 00:00:00 2001 From: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Date: Sun, 22 Jun 2025 12:28:21 -0700 Subject: [PATCH 047/191] Allow admins to export round logs to CSV files (#38206) --- .../UI/Logs/AdminLogsControl.xaml | 1 + .../Administration/UI/Logs/AdminLogsEui.cs | 73 ++++++++++++++++++- .../en-US/administration/ui/admin-logs.ftl | 1 + 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml index fc4d3ee3ac..cd93ffeb0a 100644 --- a/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml +++ b/Content.Client/Administration/UI/Logs/AdminLogsControl.xaml @@ -49,6 +49,7 @@ [DataField] - public float Duration = 4f; + public TimeSpan Duration = TimeSpan.FromSeconds(4); /// /// The prototype ID used for the visual effect. diff --git a/Content.Shared/Flash/Components/ActiveFlashComponent.cs b/Content.Shared/Flash/Components/ActiveFlashComponent.cs new file mode 100644 index 0000000000..18994f103e --- /dev/null +++ b/Content.Shared/Flash/Components/ActiveFlashComponent.cs @@ -0,0 +1,24 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Shared.Flash.Components; + +/// +/// Marks an entity with the as currently flashing. +/// Only used for an Update loop for resetting the visuals. +/// +/// +/// TODO: Replace this with something like sprite flick once that exists to get rid of the update loop. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] +[Access(typeof(SharedFlashSystem))] +public sealed partial class ActiveFlashComponent : Component +{ + /// + /// Time at which this flash will be considered no longer active. + /// At this time this component will be removed. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + [AutoNetworkedField, AutoPausedField] + public TimeSpan ActiveUntil = TimeSpan.Zero; +} diff --git a/Content.Shared/Flash/Components/DamagedByFlashingComponent.cs b/Content.Shared/Flash/Components/DamagedByFlashingComponent.cs new file mode 100644 index 0000000000..766ee303c5 --- /dev/null +++ b/Content.Shared/Flash/Components/DamagedByFlashingComponent.cs @@ -0,0 +1,18 @@ +using Content.Shared.Damage; +using Robust.Shared.GameStates; + +namespace Content.Shared.Flash.Components; + +/// +/// This entity will take damage from flashes. +/// +[RegisterComponent, NetworkedComponent] +[Access(typeof(DamagedByFlashingSystem))] +public sealed partial class DamagedByFlashingComponent : Component +{ + /// + /// How much damage it will take. + /// + [DataField(required: true)] + public DamageSpecifier FlashDamage = new(); +} diff --git a/Content.Shared/Flash/Components/FlashComponent.cs b/Content.Shared/Flash/Components/FlashComponent.cs index 29f92eb94f..d1a8b882d9 100644 --- a/Content.Shared/Flash/Components/FlashComponent.cs +++ b/Content.Shared/Flash/Components/FlashComponent.cs @@ -1,55 +1,79 @@ using Robust.Shared.Audio; using Robust.Shared.GameStates; -using Robust.Shared.Serialization; -namespace Content.Shared.Flash.Components +namespace Content.Shared.Flash.Components; + +/// +/// Allows this entity to flash someone by using it or melee attacking with it. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedFlashSystem))] +public sealed partial class FlashComponent : Component { - [RegisterComponent, NetworkedComponent, Access(typeof(SharedFlashSystem))] - public sealed partial class FlashComponent : Component + /// + /// Flash the area around the entity when used in hand? + /// + [DataField, AutoNetworkedField] + public bool FlashOnUse = true; + + /// + /// Flash the target when melee attacking them? + /// + [DataField, AutoNetworkedField] + public bool FlashOnMelee = true; + + /// + /// Time the Flash will be visually flashing after use. + /// For the actual interaction delay use UseDelayComponent. + /// These two times should be the same. + /// + [DataField, AutoNetworkedField] + public TimeSpan FlashingTime = TimeSpan.FromSeconds(4); + + /// + /// For how long the target will lose vision when melee attacked with the flash. + /// + [DataField, AutoNetworkedField] + public TimeSpan MeleeDuration = TimeSpan.FromSeconds(5); + + /// + /// For how long the target will lose vision when used in hand. + /// + [DataField, AutoNetworkedField] + public TimeSpan AoeFlashDuration = TimeSpan.FromSeconds(2); + + /// + /// How long a target is stunned when a melee flash is used. + /// If null, melee flashes will not stun at all. + /// + [DataField, AutoNetworkedField] + public TimeSpan? MeleeStunDuration = TimeSpan.FromSeconds(1.5); + + /// + /// Range of the flash when using it. + /// + [DataField, AutoNetworkedField] + public float Range = 7f; + + /// + /// Movement speed multiplier for slowing down the target while they are flashed. + /// + [DataField, AutoNetworkedField] + public float SlowTo = 0.5f; + + /// + /// The sound to play when flashing. + /// + + [DataField, AutoNetworkedField] + public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Weapons/flash.ogg") { + Params = AudioParams.Default.WithVolume(1f).WithMaxDistance(3f) + }; - [DataField("duration")] - [ViewVariables(VVAccess.ReadWrite)] - public int FlashDuration { get; set; } = 5000; - - /// - /// How long a target is stunned when a melee flash is used. - /// If null, melee flashes will not stun at all - /// - [DataField] - public TimeSpan? MeleeStunDuration = TimeSpan.FromSeconds(1.5); - - [DataField("range")] - [ViewVariables(VVAccess.ReadWrite)] - public float Range { get; set; } = 7f; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("aoeFlashDuration")] - public int AoeFlashDuration { get; set; } = 2000; - - [DataField("slowTo")] - [ViewVariables(VVAccess.ReadWrite)] - public float SlowTo { get; set; } = 0.5f; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("sound")] - public SoundSpecifier Sound { get; set; } = new SoundPathSpecifier("/Audio/Weapons/flash.ogg") - { - Params = AudioParams.Default.WithVolume(1f).WithMaxDistance(3f) - }; - - public bool Flashing; - - [DataField] - public float Probability = 1f; - } - - [Serializable, NetSerializable] - public enum FlashVisuals : byte - { - BaseLayer, - LightLayer, - Burnt, - Flashing, - } + /// + /// The probability of sucessfully flashing someone. + /// + [DataField, AutoNetworkedField] + public float Probability = 1f; } diff --git a/Content.Shared/Flash/Components/FlashImmunityComponent.cs b/Content.Shared/Flash/Components/FlashImmunityComponent.cs new file mode 100644 index 0000000000..149c27c517 --- /dev/null +++ b/Content.Shared/Flash/Components/FlashImmunityComponent.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Flash.Components; + +/// +/// Makes the entity immune to being flashed. +/// When given to clothes in the "head", "eyes" or "mask" slot it protects the wearer. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedFlashSystem))] +public sealed partial class FlashImmunityComponent : Component +{ + /// + /// Is this component currently enabled? + /// + [DataField, AutoNetworkedField] + public bool Enabled = true; +} diff --git a/Content.Shared/Flash/Components/FlashOnTriggerComponent.cs b/Content.Shared/Flash/Components/FlashOnTriggerComponent.cs index 7658ca0ae5..e735b3784a 100644 --- a/Content.Shared/Flash/Components/FlashOnTriggerComponent.cs +++ b/Content.Shared/Flash/Components/FlashOnTriggerComponent.cs @@ -7,7 +7,12 @@ namespace Content.Shared.Flash.Components; [RegisterComponent, NetworkedComponent] public sealed partial class FlashOnTriggerComponent : Component { - [DataField] public float Range = 1.0f; - [DataField] public float Duration = 8.0f; - [DataField] public float Probability = 1.0f; + [DataField] + public float Range = 1.0f; + + [DataField] + public TimeSpan Duration = TimeSpan.FromSeconds(8); + + [DataField] + public float Probability = 1.0f; } diff --git a/Content.Shared/Flash/Components/FlashedComponent.cs b/Content.Shared/Flash/Components/FlashedComponent.cs index 75bbb12304..e6c623b9ac 100644 --- a/Content.Shared/Flash/Components/FlashedComponent.cs +++ b/Content.Shared/Flash/Components/FlashedComponent.cs @@ -3,7 +3,7 @@ using Robust.Shared.GameStates; namespace Content.Shared.Flash.Components; /// -/// Exists for use as a status effect. Adds a shader to the client that obstructs vision. +/// Exists for use as a status effect. Adds a shader to the client that obstructs vision. /// [RegisterComponent, NetworkedComponent] -public sealed partial class FlashedComponent : Component { } +public sealed partial class FlashedComponent : Component; diff --git a/Content.Server/Flash/DamagedByFlashingSystem.cs b/Content.Shared/Flash/DamagedByFlashingSystem.cs similarity index 53% rename from Content.Server/Flash/DamagedByFlashingSystem.cs rename to Content.Shared/Flash/DamagedByFlashingSystem.cs index 5b4c19b8e5..103dfb663c 100644 --- a/Content.Server/Flash/DamagedByFlashingSystem.cs +++ b/Content.Shared/Flash/DamagedByFlashingSystem.cs @@ -1,7 +1,8 @@ -using Content.Server.Flash.Components; +using Content.Shared.Flash.Components; using Content.Shared.Damage; -namespace Content.Server.Flash; +namespace Content.Shared.Flash; + public sealed class DamagedByFlashingSystem : EntitySystem { [Dependency] private readonly DamageableSystem _damageable = default!; @@ -12,11 +13,14 @@ public sealed class DamagedByFlashingSystem : EntitySystem SubscribeLocalEvent(OnFlashAttempt); } + + // TODO: Attempt events should not be doing state changes. But using AfterFlashedEvent does not work because this entity cannot get the status effect. + // Best wait for Ed's status effect system rewrite. private void OnFlashAttempt(Entity ent, ref FlashAttemptEvent args) { _damageable.TryChangeDamage(ent, ent.Comp.FlashDamage); - //TODO: It would be more logical if different flashes had different power, - //and the damage would be inflicted depending on the strength of the flash. + // TODO: It would be more logical if different flashes had different power, + // and the damage would be inflicted depending on the strength of the flash. } } diff --git a/Content.Shared/Flash/FlashEvents.cs b/Content.Shared/Flash/FlashEvents.cs new file mode 100644 index 0000000000..1c18ca1676 --- /dev/null +++ b/Content.Shared/Flash/FlashEvents.cs @@ -0,0 +1,21 @@ +using Content.Shared.Inventory; + +namespace Content.Shared.Flash; + +/// +/// Called before a flash is used to check if the attempt is cancelled by blindness, items or FlashImmunityComponent. +/// Raised on the target hit by the flash and their inventory items. +/// +[ByRefEvent] +public record struct FlashAttemptEvent(EntityUid Target, EntityUid? User, EntityUid? Used, bool Cancelled = false) : IInventoryRelayEvent +{ + SlotFlags IInventoryRelayEvent.TargetSlots => SlotFlags.HEAD | SlotFlags.EYES | SlotFlags.MASK; +} + +/// +/// Called when a player is successfully flashed. +/// Raised on the target hit by the flash, the user of the flash and the flash used. +/// The Melee parameter is used to check for rev conversion. +/// +[ByRefEvent] +public record struct AfterFlashedEvent(EntityUid Target, EntityUid? User, EntityUid? Used, bool Melee); diff --git a/Content.Shared/Flash/FlashVisuals.cs b/Content.Shared/Flash/FlashVisuals.cs new file mode 100644 index 0000000000..f43780ff28 --- /dev/null +++ b/Content.Shared/Flash/FlashVisuals.cs @@ -0,0 +1,17 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Flash; + +[Serializable, NetSerializable] +public enum FlashVisuals : byte +{ + Burnt, + Flashing, +} + +[Serializable, NetSerializable] +public enum FlashVisualLayers : byte +{ + BaseLayer, + LightLayer, +} diff --git a/Content.Shared/Flash/SharedFlashSystem.cs b/Content.Shared/Flash/SharedFlashSystem.cs index b778809887..50652f9408 100644 --- a/Content.Shared/Flash/SharedFlashSystem.cs +++ b/Content.Shared/Flash/SharedFlashSystem.cs @@ -1,15 +1,265 @@ +using Content.Shared.Charges.Components; +using Content.Shared.Charges.Systems; +using Content.Shared.Examine; +using Content.Shared.Eye.Blinding.Components; using Content.Shared.Flash.Components; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction.Events; +using Content.Shared.Inventory; +using Content.Shared.Light; +using Content.Shared.Popups; using Content.Shared.StatusEffect; +using Content.Shared.Stunnable; +using Content.Shared.Tag; +using Content.Shared.Timing; +using Content.Shared.Traits.Assorted; +using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; +using System.Linq; namespace Content.Shared.Flash; public abstract class SharedFlashSystem : EntitySystem { + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedChargesSystem _sharedCharges = default!; + [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly UseDelaySystem _useDelay = default!; + + private EntityQuery _statusEffectsQuery; + private EntityQuery _damagedByFlashingQuery; + private HashSet _entSet = new(); + + // The tag to add when a flash has no charges left. + private static readonly ProtoId TrashTag = "Trash"; + // The key string for the status effect. public ProtoId FlashedKey = "Flashed"; - public virtual void FlashArea(Entity source, EntityUid? user, float range, float duration, float slowTo = 0.8f, bool displayPopup = false, float probability = 1f, SoundSpecifier? sound = null) + public override void Initialize() { + base.Initialize(); + + SubscribeLocalEvent(OnFlashMeleeHit); + SubscribeLocalEvent(OnFlashUseInHand); + SubscribeLocalEvent(OnLightToggle); + SubscribeLocalEvent(OnPermanentBlindnessFlashAttempt); + SubscribeLocalEvent(OnTemporaryBlindnessFlashAttempt); + Subs.SubscribeWithRelay(OnFlashImmunityFlashAttempt, held: false); + SubscribeLocalEvent(OnExamine); + + _statusEffectsQuery = GetEntityQuery(); + _damagedByFlashingQuery = GetEntityQuery(); + } + + private void OnFlashMeleeHit(Entity ent, ref MeleeHitEvent args) + { + if (!ent.Comp.FlashOnMelee || + !args.IsHit || + !args.HitEntities.Any() || + !UseFlash(ent, args.User)) + { + return; + } + + args.Handled = true; + foreach (var target in args.HitEntities) + { + Flash(target, args.User, ent.Owner, ent.Comp.MeleeDuration, ent.Comp.SlowTo, melee: true, stunDuration: ent.Comp.MeleeStunDuration); + } + } + + private void OnFlashUseInHand(Entity ent, ref UseInHandEvent args) + { + if (!ent.Comp.FlashOnUse || args.Handled || !UseFlash(ent, args.User)) + return; + + args.Handled = true; + FlashArea(ent.Owner, args.User, ent.Comp.Range, ent.Comp.AoeFlashDuration, ent.Comp.SlowTo, true, ent.Comp.Probability); + } + + // needed for the flash lantern and interrogator lamp + // TODO: This is awful and all the different components for toggleable lights need to be unified and changed to use Itemtoggle + private void OnLightToggle(Entity ent, ref LightToggleEvent args) + { + if (!args.IsOn || !UseFlash(ent, null)) + return; + + FlashArea(ent.Owner, null, ent.Comp.Range, ent.Comp.AoeFlashDuration, ent.Comp.SlowTo, true, ent.Comp.Probability); + } + + /// + /// Use charges and set the visuals. + /// + /// False if no charges are left or the flash is currently in use. + private bool UseFlash(Entity ent, EntityUid? user) + { + if (_useDelay.IsDelayed(ent.Owner)) + return false; + + if (TryComp(ent.Owner, out var charges) + && _sharedCharges.IsEmpty((ent.Owner, charges))) + return false; + + _sharedCharges.TryUseCharge((ent.Owner, charges)); + _audio.PlayPredicted(ent.Comp.Sound, ent.Owner, user); + + var active = EnsureComp(ent.Owner); + active.ActiveUntil = _timing.CurTime + ent.Comp.FlashingTime; + Dirty(ent.Owner, active); + _appearance.SetData(ent.Owner, FlashVisuals.Flashing, true); + + if (_sharedCharges.IsEmpty((ent.Owner, charges))) + { + _appearance.SetData(ent.Owner, FlashVisuals.Burnt, true); + _tag.AddTag(ent.Owner, TrashTag); + _popup.PopupClient(Loc.GetString("flash-component-becomes-empty"), user); + } + + return true; + } + + /// + /// Cause an entity to be flashed, obstructing their vision, slowing them down and stunning them. + /// In case of a melee attack this will do a check for revolutionary conversion. + /// + /// The mob to be flashed. + /// The mob causing the flash, if any. + /// The item causing the flash, if any. + /// The time target will be affected by the flash. + /// Movement speed modifier applied to the flashed target. Between 0 and 1. + /// Whether or not to show a popup to the target player. + /// Was this flash caused by a melee attack? Used for checking for revolutionary conversion. + /// The time the target will be stunned. If null the target will be slowed down instead. + public void Flash( + EntityUid target, + EntityUid? user, + EntityUid? used, + TimeSpan flashDuration, + float slowTo, + bool displayPopup = true, + bool melee = false, + TimeSpan? stunDuration = null) + { + var attempt = new FlashAttemptEvent(target, user, used); + RaiseLocalEvent(target, ref attempt, true); + + if (attempt.Cancelled) + return; + + // don't paralyze, slowdown or convert to rev if the target is immune to flashes + if (!_statusEffectsSystem.TryAddStatusEffect(target, FlashedKey, flashDuration, true)) + return; + + if (stunDuration != null) + _stun.TryParalyze(target, stunDuration.Value, true); + else + _stun.TrySlowdown(target, flashDuration, true, slowTo, slowTo); + + if (displayPopup && user != null && target != user && Exists(user.Value)) + { + _popup.PopupEntity(Loc.GetString("flash-component-user-blinds-you", + ("user", Identity.Entity(user.Value, EntityManager))), target, target); + } + + var ev = new AfterFlashedEvent(target, user, used, melee); + RaiseLocalEvent(target, ref ev); + + if (user != null) + RaiseLocalEvent(user.Value, ref ev); + if (used != null) + RaiseLocalEvent(used.Value, ref ev); + } + + /// + /// Cause all entities in range of a source entity to be flashed. + /// + /// The source of the flash, which will be at the epicenter. + /// The mob causing the flash, if any. + /// The time target will be affected by the flash. + /// Movement speed modifier applied to the flashed target. Between 0 and 1. + /// Whether or not to show a popup to the target player. + /// Chance to be flashed. Rolled separately for each target in range. + /// Additional sound to play at the source. + public void FlashArea(EntityUid source, EntityUid? user, float range, TimeSpan flashDuration, float slowTo = 0.8f, bool displayPopup = false, float probability = 1f, SoundSpecifier? sound = null) + { + var transform = Transform(source); + var mapPosition = _transform.GetMapCoordinates(transform); + + _entSet.Clear(); + _entityLookup.GetEntitiesInRange(transform.Coordinates, range, _entSet); + foreach (var entity in _entSet) + { + // TODO: Use RandomPredicted https://github.com/space-wizards/RobustToolbox/pull/5849 + var rand = new System.Random((int)_timing.CurTick.Value + GetNetEntity(entity).Id); + if (!rand.Prob(probability)) + continue; + + // Is the entity affected by the flash either through status effects or by taking damage? + if (!_statusEffectsQuery.HasComponent(entity) && !_damagedByFlashingQuery.HasComponent(entity)) + continue; + + // Check for entites in view. + // Put DamagedByFlashingComponent in the predicate because shadow anomalies block vision. + if (!_examine.InRangeUnOccluded(entity, mapPosition, range, predicate: (e) => _damagedByFlashingQuery.HasComponent(e))) + continue; + + Flash(entity, user, source, flashDuration, slowTo, displayPopup); + } + + _audio.PlayPredicted(sound, source, user, AudioParams.Default.WithVolume(1f).WithMaxDistance(3f)); + } + + // Handle the flash visuals + // TODO: Replace this with something like sprite flick once that exists to get rid of the update loop. + public override void Update(float frameTime) + { + base.Update(frameTime); + + var curTime = _timing.CurTime; + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var active)) + { + // reset the visuals and remove the component + if (active.ActiveUntil < curTime) + { + _appearance.SetData(uid, FlashVisuals.Flashing, false); + RemCompDeferred(uid); + } + } + } + + private void OnPermanentBlindnessFlashAttempt(Entity ent, ref FlashAttemptEvent args) + { + // check for total blindness + if (ent.Comp.Blindness == 0) + args.Cancelled = true; + } + + private void OnTemporaryBlindnessFlashAttempt(Entity ent, ref FlashAttemptEvent args) + { + args.Cancelled = true; + } + + private void OnFlashImmunityFlashAttempt(Entity ent, ref FlashAttemptEvent args) + { + if (ent.Comp.Enabled) + args.Cancelled = true; + } + + private void OnExamine(Entity ent, ref ExaminedEvent args) + { + args.PushMarkup(Loc.GetString("flash-protection")); } } diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index 7973af35ab..f6a719a59b 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -10,6 +10,7 @@ using Content.Shared.Damage.Events; using Content.Shared.Electrocution; using Content.Shared.Explosion; using Content.Shared.Eye.Blinding.Systems; +using Content.Shared.Flash; using Content.Shared.Gravity; using Content.Shared.IdentityManagement.Components; using Content.Shared.Implants; @@ -66,6 +67,7 @@ public partial class InventorySystem SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); + SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); diff --git a/Content.Shared/Light/SharedHandheldLightSystem.cs b/Content.Shared/Light/SharedHandheldLightSystem.cs index e93ca0a041..0f507e1365 100644 --- a/Content.Shared/Light/SharedHandheldLightSystem.cs +++ b/Content.Shared/Light/SharedHandheldLightSystem.cs @@ -1,10 +1,10 @@ using Content.Shared.Actions; using Content.Shared.Clothing.EntitySystems; using Content.Shared.Item; +using Content.Shared.Light; using Content.Shared.Light.Components; using Content.Shared.Toggleable; using Content.Shared.Verbs; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.GameStates; using Robust.Shared.Utility; @@ -63,6 +63,9 @@ public abstract class SharedHandheldLightSystem : EntitySystem Dirty(uid, component); UpdateVisuals(uid, component); + + var ev = new LightToggleEvent(activated); + RaiseLocalEvent(uid, ev); } public void UpdateVisuals(EntityUid uid, HandheldLightComponent? component = null, AppearanceComponent? appearance = null) diff --git a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml index e2a2000844..eaae4d410a 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml @@ -140,13 +140,13 @@ sprite: Objects/Misc/Lights/lampint.rsi layers: - state: lamp-int - map: [ "enum.FlashVisuals.BaseLayer" ] + map: [ "enum.FlashVisualLayers.BaseLayer" ] - state: lamp-int-on shader: unshaded visible: false map: [ "light" ] - state: flashing - map: [ "enum.FlashVisuals.LightLayer" ] + map: [ "enum.FlashVisualLayers.LightLayer" ] visible: false - type: Item sprite: Objects/Misc/Lights/lampint.rsi @@ -159,6 +159,10 @@ energy: 0.5 color: "#FFFFEE" - type: Flash + flashOnMelee: false + flashOnUse: false + - type: UseDelay + delay: 1 - type: LimitedCharges maxCharges: 3 - type: AutoRecharge @@ -176,10 +180,10 @@ - type: GenericVisualizer visuals: enum.FlashVisuals.Burnt: - enum.FlashVisuals.BaseLayer: + enum.FlashVisualLayers.BaseLayer: True: {state: burnt} enum.FlashVisuals.Flashing: - enum.FlashVisuals.LightLayer: + enum.FlashVisualLayers.LightLayer: True: {visible: true} False: {visible: false} diff --git a/Resources/Prototypes/Entities/Objects/Tools/lantern.yml b/Resources/Prototypes/Entities/Objects/Tools/lantern.yml index 3d25957851..24fdb88ed5 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/lantern.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/lantern.yml @@ -10,31 +10,33 @@ radiatingBehaviourId: radiating - type: LightBehaviour behaviours: - - !type:FadeBehaviour - id: radiating - maxDuration: 2.0 - startValue: 3.0 - endValue: 2.0 - isLooped: true - reverseWhenFinished: true - - !type:PulseBehaviour - id: blinking - interpolate: Nearest - maxDuration: 1.0 - minValue: 0.1 - maxValue: 2.0 - isLooped: true + - !type:FadeBehaviour + id: radiating + maxDuration: 2.0 + startValue: 3.0 + endValue: 2.0 + isLooped: true + reverseWhenFinished: true + - !type:PulseBehaviour + id: blinking + interpolate: Nearest + maxDuration: 1.0 + minValue: 0.1 + maxValue: 2.0 + isLooped: true - type: Sprite sprite: Objects/Tools/lantern.rsi layers: - - state: lantern - - state: lantern-on - shader: unshaded - visible: false - map: [ "light" ] + - state: lantern + - state: lantern-on + shader: unshaded + visible: false + map: [ "light" ] - type: Item sprite: Objects/Tools/lantern.rsi heldPrefix: off + - type: UseDelay + delay: 1 - type: PointLight enabled: false radius: 3 @@ -62,7 +64,7 @@ equippedPrefix: off quickEquip: false slots: - - Belt + - Belt - type: Tag tags: - Flashlight @@ -76,18 +78,20 @@ sprite: Objects/Tools/lantern.rsi layers: - state: lantern - map: [ "enum.FlashVisuals.BaseLayer" ] + map: [ "enum.FlashVisualLayers.BaseLayer" ] - state: lantern-on shader: unshaded visible: false map: [ "light" ] - state: flashing - map: [ "enum.FlashVisuals.LightLayer" ] + map: [ "enum.FlashVisualLayers.LightLayer" ] visible: false - type: PointLight radius: 5 energy: 10 - type: Flash + flashOnMelee: false + flashOnUse: false - type: LimitedCharges maxCharges: 15 - type: MeleeWeapon @@ -98,9 +102,9 @@ - type: GenericVisualizer visuals: enum.FlashVisuals.Burnt: - enum.FlashVisuals.BaseLayer: + enum.FlashVisualLayers.BaseLayer: True: {state: burnt} enum.FlashVisuals.Flashing: - enum.FlashVisuals.LightLayer: + enum.FlashVisualLayers.LightLayer: True: {visible: true} False: {visible: false} diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml index ade0107670..7f69d77f93 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -139,9 +139,9 @@ sprite: Objects/Weapons/Melee/flash.rsi layers: - state: flash - map: [ "enum.FlashVisuals.BaseLayer" ] + map: [ "enum.FlashVisualLayers.BaseLayer" ] - state: flashing - map: [ "enum.FlashVisuals.LightLayer" ] + map: [ "enum.FlashVisualLayers.LightLayer" ] visible: false shader: unshaded - type: Flash @@ -157,16 +157,18 @@ size: Small sprite: Objects/Weapons/Melee/flash.rsi - type: UseDelay + delay: 4 # has to be the same as the FlashingTime datafield in FlashComponent + - type: UseDelayOnMeleeHit - type: StaticPrice price: 40 - type: Appearance - type: GenericVisualizer visuals: enum.FlashVisuals.Burnt: - enum.FlashVisuals.BaseLayer: + enum.FlashVisualLayers.BaseLayer: True: {state: burnt} enum.FlashVisuals.Flashing: - enum.FlashVisuals.LightLayer: + enum.FlashVisualLayers.LightLayer: True: {visible: true} False: {visible: false} - type: GuideHelp From a77c24cc864a926e3dc103ca8bf0a9996d6648e4 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 23 Jun 2025 11:34:04 +0000 Subject: [PATCH 056/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 00c474864b..50260e1944 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: RedBookcase - changes: - - message: Minor blueprint tweaks and a new stored sprite for blueprints. - type: Tweak - id: 8194 - time: '2025-04-16T10:12:22.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36609 - author: SlamBamActionman changes: - message: Unfolded bandanas/lab coats now fit in crates again. @@ -3895,3 +3888,11 @@ id: 8706 time: '2025-06-22T19:38:22.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38509 +- author: slarticodefast + changes: + - message: The interrogator lamp and flash lantern now properly flash when toggled + on. + type: Fix + id: 8707 + time: '2025-06-23T11:32:57.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/37640 From b68c6b37ac8244552e675145f86a0b2654a26846 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 23 Jun 2025 16:41:41 -0400 Subject: [PATCH 057/191] Remove excess `SingularityLevelChangedEvent` subscriptions (#38536) * Remove excess SingularityLevelChangedEvent subscriptions * RadiationSourceComponent too * Cleanup commented out code --- .../EntitySystems/SharedSingularitySystem.cs | 78 ++++++------------- 1 file changed, 24 insertions(+), 54 deletions(-) diff --git a/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs b/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs index acac1dfd84..b7bf071457 100644 --- a/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs +++ b/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs @@ -45,10 +45,6 @@ public abstract class SharedSingularitySystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnSingularityStartup); - SubscribeLocalEvent(UpdateAppearance); - SubscribeLocalEvent(UpdateRadiation); - SubscribeLocalEvent(UpdateBody); - SubscribeLocalEvent(UpdateEventHorizon); SubscribeLocalEvent(UpdateDistortion); SubscribeLocalEvent(UpdateDistortion); SubscribeLocalEvent(UpdateDistortion); @@ -121,9 +117,32 @@ public abstract class SharedSingularitySystem : EntitySystem /// The state of the singularity which's level has changed. public void UpdateSingularityLevel(EntityUid uid, byte oldValue, SingularityComponent? singularity = null) { - if(!Resolve(uid, ref singularity)) + if (!Resolve(uid, ref singularity)) return; + if (TryComp(uid, out var eventHorizon)) + { + _horizons.SetRadius(uid, EventHorizonRadius(singularity), false, eventHorizon); + _horizons.SetCanBreachContainment(uid, CanBreachContainment(singularity), false, eventHorizon); + _horizons.UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon); + } + + if (TryComp(uid, out var body)) + { + if (singularity.Level <= 1 && oldValue > 1) // Apparently keeps singularities from getting stuck in the corners of containment fields. + _physics.SetLinearVelocity(uid, Vector2.Zero, body: body); // No idea how stopping the singularities movement keeps it from getting stuck though. + } + + if (TryComp(uid, out var appearance)) + { + _visualizer.SetData(uid, SingularityAppearanceKeys.Singularity, singularity.Level, appearance); + } + + if (TryComp(uid, out var radiationSource)) + { + UpdateRadiation(uid, singularity, radiationSource); + } + RaiseLocalEvent(uid, new SingularityLevelChangedEvent(singularity.Level, oldValue, singularity)); if (singularity.Level <= 0) QueueDel(uid); @@ -274,21 +293,6 @@ public abstract class SharedSingularitySystem : EntitySystem UpdateSingularityLevel(uid, comp); } - // TODO: Figure out which systems should have control of which coupling. - /// - /// Syncs the radius of an event horizon associated with a singularity that just changed levels. - /// - /// The entity that the event horizon and singularity are attached to. - /// The event horizon associated with the singularity. - /// The event arguments. - private void UpdateEventHorizon(EntityUid uid, EventHorizonComponent comp, SingularityLevelChangedEvent args) - { - var singulo = args.Singularity; - _horizons.SetRadius(uid, EventHorizonRadius(singulo), false, comp); - _horizons.SetCanBreachContainment(uid, CanBreachContainment(singulo), false, comp); - _horizons.UpdateEventHorizonFixture(uid, eventHorizon: comp); - } - /// /// Updates the distortion shader associated with a singularity when the singuarity changes levels. /// @@ -346,40 +350,6 @@ public abstract class SharedSingularitySystem : EntitySystem comp.Intensity = absIntensity > 1 ? comp.Intensity * MathF.Pow(absIntensity, factor) : comp.Intensity; } - /// - /// Updates the state of the physics body associated with a singularity when the singualrity changes levels. - /// - /// The entity that the physics body and singularity are attached to. - /// The physics body associated with the singularity. - /// The event arguments. - private void UpdateBody(EntityUid uid, PhysicsComponent comp, SingularityLevelChangedEvent args) - { - if (args.NewValue <= 1 && args.OldValue > 1) // Apparently keeps singularities from getting stuck in the corners of containment fields. - _physics.SetLinearVelocity(uid, Vector2.Zero, body: comp); // No idea how stopping the singularities movement keeps it from getting stuck though. - } - - /// - /// Updates the appearance of a singularity when the singularities level changes. - /// - /// The entity that the singularity is attached to. - /// The appearance associated with the singularity. - /// The event arguments. - private void UpdateAppearance(EntityUid uid, AppearanceComponent comp, SingularityLevelChangedEvent args) - { - _visualizer.SetData(uid, SingularityAppearanceKeys.Singularity, args.NewValue, comp); - } - - /// - /// Updates the amount of radiation a singularity emits when the singularities level changes. - /// - /// The entity that the singularity is attached to. - /// The radiation source associated with the singularity. - /// The event arguments. - private void UpdateRadiation(EntityUid uid, RadiationSourceComponent comp, SingularityLevelChangedEvent args) - { - UpdateRadiation(uid, args.Singularity, comp); - } - #endregion EventHandlers } From 6a2afa562554d4e9c96db10ef3a32d8321d4cfba Mon Sep 17 00:00:00 2001 From: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com> Date: Mon, 23 Jun 2025 17:25:35 -0400 Subject: [PATCH 058/191] Revisions and cleanup to dock and shuttle commands. (#38533) commit --- .../Commands/DelayShuttleRoundEndCommand.cs | 19 ++----- .../Shuttles/Commands/DockCommand.cs | 55 ++++++++----------- .../Commands/DockEmergencyShuttleCommand.cs | 14 ++--- .../Commands/LaunchEmergencyShuttleCommand.cs | 14 ++--- Resources/Locale/en-US/shuttles/docking.ftl | 3 - Resources/Locale/en-US/shuttles/emergency.ftl | 11 ++-- 6 files changed, 48 insertions(+), 68 deletions(-) diff --git a/Content.Server/Shuttles/Commands/DelayShuttleRoundEndCommand.cs b/Content.Server/Shuttles/Commands/DelayShuttleRoundEndCommand.cs index 3c881dcbb5..4750cd77c1 100644 --- a/Content.Server/Shuttles/Commands/DelayShuttleRoundEndCommand.cs +++ b/Content.Server/Shuttles/Commands/DelayShuttleRoundEndCommand.cs @@ -9,24 +9,17 @@ namespace Content.Server.Shuttles.Commands; /// Delays the round from ending via the shuttle call. Can still be ended via other means. /// [AdminCommand(AdminFlags.Fun)] -public sealed class DelayRoundEndCommand : IConsoleCommand +public sealed class DelayRoundEndCommand : LocalizedEntityCommands { - [Dependency] private readonly IEntitySystemManager _sysManager = default!; + [Dependency] private readonly EmergencyShuttleSystem _shuttleSystem = default!; - public string Command => "delayroundend"; - public string Description => Loc.GetString("emergency-shuttle-command-round-desc"); - public string Help => $"{Command}"; - public void Execute(IConsoleShell shell, string argStr, string[] args) + public override string Command => "delayroundend"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) { - var system = _sysManager.GetEntitySystem(); - - if (system.DelayEmergencyRoundEnd()) - { + if (_shuttleSystem.DelayEmergencyRoundEnd()) shell.WriteLine(Loc.GetString("emergency-shuttle-command-round-yes")); - } else - { shell.WriteLine(Loc.GetString("emergency-shuttle-command-round-no")); - } } } diff --git a/Content.Server/Shuttles/Commands/DockCommand.cs b/Content.Server/Shuttles/Commands/DockCommand.cs index 62634af2bc..14042cd953 100644 --- a/Content.Server/Shuttles/Commands/DockCommand.cs +++ b/Content.Server/Shuttles/Commands/DockCommand.cs @@ -7,70 +7,61 @@ using Robust.Shared.Console; namespace Content.Server.Shuttles.Commands; [AdminCommand(AdminFlags.Mapping)] -public sealed class DockCommand : IConsoleCommand +public sealed class DockCommand : LocalizedEntityCommands { - [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly DockingSystem _dockSystem = default!; - public string Command => "dock"; - public string Description => Loc.GetString("cmd-dock-desc"); - public string Help => Loc.GetString("cmd-dock-help"); - public void Execute(IConsoleShell shell, string argStr, string[] args) + public override string Command => "dock"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) { if (args.Length != 2) { - shell.WriteError(Loc.GetString("cmd-dock-args")); + shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", + ("properAmount", 2), + ("currentAmount", args.Length))); return; } - if (!NetEntity.TryParse(args[0], out var airlock1Net) || !_entManager.TryGetEntity(airlock1Net, out var airlock1)) + if (!NetEntity.TryParse(args[0], out var airlock1Net) || !EntityManager.TryGetEntity(airlock1Net, out var airlock1)) { - shell.WriteError(Loc.GetString("cmd-dock-invalid", ("entity", args[0]))); + shell.WriteError(Loc.GetString("shell-invalid-entity-uid", ("uid", args[0]))); return; } - if (!NetEntity.TryParse(args[1], out var airlock2Net) || !_entManager.TryGetEntity(airlock2Net, out var airlock2)) + if (!NetEntity.TryParse(args[1], out var airlock2Net) || !EntityManager.TryGetEntity(airlock2Net, out var airlock2)) { - shell.WriteError(Loc.GetString("cmd-dock-invalid", ("entity", args[1]))); + shell.WriteError(Loc.GetString("shell-invalid-entity-uid", ("uid", args[1]))); return; } - if (!_entManager.TryGetComponent(airlock1, out DockingComponent? dock1)) + if (!EntityManager.TryGetComponent(airlock1, out DockingComponent? dock1)) { - shell.WriteError(Loc.GetString("cmd-dock-found", ("airlock", airlock1))); + shell.WriteError(Loc.GetString("shell-entity-with-uid-lacks-component", ("uid", args[0]), ("componentName", nameof(DockingComponent)))); return; } - if (!_entManager.TryGetComponent(airlock2, out DockingComponent? dock2)) + if (!EntityManager.TryGetComponent(airlock2, out DockingComponent? dock2)) { - shell.WriteError(Loc.GetString("cmd-dock-found", ("airlock", airlock2))); + shell.WriteError(Loc.GetString("shell-entity-with-uid-lacks-component", ("uid", args[1]), ("componentName", nameof(DockingComponent)))); return; } - var dockSystem = _entManager.System(); - dockSystem.Dock((airlock1.Value, dock1), (airlock2.Value, dock2)); + _dockSystem.Dock((airlock1.Value, dock1), (airlock2.Value, dock2)); if (dock1.DockedWith == airlock2) - { shell.WriteLine(Loc.GetString("cmd-dock-success")); - } else - { shell.WriteError(Loc.GetString("cmd-dock-fail")); - } } - public CompletionResult GetCompletion(IConsoleShell shell, string[] args) + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) { - if (args.Length == 1) + return args.Length switch { - return CompletionResult.FromOptions(CompletionHelper.Components(args[0], _entManager)); - } - - if (args.Length == 2) - { - return CompletionResult.FromOptions(CompletionHelper.Components(args[1], _entManager)); - } - - return CompletionResult.Empty; + 1 => CompletionResult.FromOptions(CompletionHelper.Components(args[0], EntityManager)), + 2 => CompletionResult.FromOptions(CompletionHelper.Components(args[1], EntityManager)), + _ => CompletionResult.Empty, + }; } } diff --git a/Content.Server/Shuttles/Commands/DockEmergencyShuttleCommand.cs b/Content.Server/Shuttles/Commands/DockEmergencyShuttleCommand.cs index f219602bcb..b95cecc894 100644 --- a/Content.Server/Shuttles/Commands/DockEmergencyShuttleCommand.cs +++ b/Content.Server/Shuttles/Commands/DockEmergencyShuttleCommand.cs @@ -9,16 +9,14 @@ namespace Content.Server.Shuttles.Commands; /// Calls in the emergency shuttle. /// [AdminCommand(AdminFlags.Fun)] -public sealed class DockEmergencyShuttleCommand : IConsoleCommand +public sealed class DockEmergencyShuttleCommand : LocalizedEntityCommands { - [Dependency] private readonly IEntitySystemManager _sysManager = default!; + [Dependency] private readonly EmergencyShuttleSystem _shuttleSystem = default!; - public string Command => "dockemergencyshuttle"; - public string Description => Loc.GetString("emergency-shuttle-command-dock-desc"); - public string Help => $"{Command}"; - public void Execute(IConsoleShell shell, string argStr, string[] args) + public override string Command => "dockemergencyshuttle"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) { - var system = _sysManager.GetEntitySystem(); - system.DockEmergencyShuttle(); + _shuttleSystem.DockEmergencyShuttle(); } } diff --git a/Content.Server/Shuttles/Commands/LaunchEmergencyShuttleCommand.cs b/Content.Server/Shuttles/Commands/LaunchEmergencyShuttleCommand.cs index b9dd9c20e9..7f129e6d20 100644 --- a/Content.Server/Shuttles/Commands/LaunchEmergencyShuttleCommand.cs +++ b/Content.Server/Shuttles/Commands/LaunchEmergencyShuttleCommand.cs @@ -9,16 +9,14 @@ namespace Content.Server.Shuttles.Commands; /// Early launches in the emergency shuttle. /// [AdminCommand(AdminFlags.Fun)] -public sealed class LaunchEmergencyShuttleCommand : IConsoleCommand +public sealed class LaunchEmergencyShuttleCommand : LocalizedEntityCommands { - [Dependency] private readonly IEntitySystemManager _sysManager = default!; + [Dependency] private readonly EmergencyShuttleSystem _shuttleSystem = default!; - public string Command => "launchemergencyshuttle"; - public string Description => Loc.GetString("emergency-shuttle-command-launch-desc"); - public string Help => $"{Command}"; - public void Execute(IConsoleShell shell, string argStr, string[] args) + public override string Command => "launchemergencyshuttle"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) { - var system = _sysManager.GetEntitySystem(); - system.EarlyLaunch(); + _shuttleSystem.EarlyLaunch(); } } diff --git a/Resources/Locale/en-US/shuttles/docking.ftl b/Resources/Locale/en-US/shuttles/docking.ftl index 0379225829..026cbb9bd0 100644 --- a/Resources/Locale/en-US/shuttles/docking.ftl +++ b/Resources/Locale/en-US/shuttles/docking.ftl @@ -4,8 +4,5 @@ docking-component-undock = Undock cmd-dock-desc = Attempts to dock 2 airlocks together. Doesn't check whether it is valid. cmd-dock-help = dock -cmd-dock-args = Invalid number of args -cmd-dock-invalid = Invalid EntityUid {$entity} -cmd-dock-found = No docking component found on {$airlock} cmd-dock-success = Successfully docked cmd-dock-fail = Unable to dock diff --git a/Resources/Locale/en-US/shuttles/emergency.ftl b/Resources/Locale/en-US/shuttles/emergency.ftl index ef3582c623..c12c6534fe 100644 --- a/Resources/Locale/en-US/shuttles/emergency.ftl +++ b/Resources/Locale/en-US/shuttles/emergency.ftl @@ -1,14 +1,17 @@ # Commands ## Delay shuttle round end -emergency-shuttle-command-round-desc = Stops the timer that ends the round when the emergency shuttle exits hyperspace. +cmd-delayroundend-desc = Stops the timer that ends the round when the emergency shuttle exits hyperspace. +cmd-delayroundend-help = Usage: delayroundend emergency-shuttle-command-round-yes = Round delayed. emergency-shuttle-command-round-no = Unable to delay round end. ## Dock emergency shuttle -emergency-shuttle-command-dock-desc = Calls the emergency shuttle and docks it to the station... if it can. +cmd-dockemergencyshuttle-desc = Calls the emergency shuttle and docks it to the station... if it can. +cmd-dockemergencyshuttle-help = Usage: dockemergencyshuttle ## Launch emergency shuttle -emergency-shuttle-command-launch-desc = Early launches the emergency shuttle if possible. +cmd-launchemergencyshuttle-desc = Early launches the emergency shuttle if possible. +cmd-launchemergencyshuttle-help = Usage: launchemergencyshuttle # Emergency shuttle emergency-shuttle-left = The Emergency Shuttle has left the station. Estimate {$transitTime} seconds until the shuttle arrives at CentComm. @@ -37,4 +40,4 @@ emergency-shuttle-ui-remaining = Remaining: {$remaining} # Map Misc. map-name-centcomm = Central Command -map-name-terminal = Arrivals Terminal \ No newline at end of file +map-name-terminal = Arrivals Terminal From 60341cb245ad3dfb605cd5d30ffd184676ef590a Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Tue, 24 Jun 2025 17:56:14 +1000 Subject: [PATCH 059/191] Add wall-based ambient occlusion (#38276) * Add wall ambient occlusion * wawawewa * Work * cvars * Comment to make slart happy --- .../Light/AmbientOcclusionOverlay.cs | 137 ++++++++++++++++++ .../Light/EntitySystems/PlanetLightSystem.cs | 34 +++++ Content.Client/Light/RoofOverlay.cs | 8 +- .../Options/UI/Tabs/GraphicsTab.xaml | 1 + .../Options/UI/Tabs/GraphicsTab.xaml.cs | 1 + Content.Shared/CCVar/CCVars.Lighting.cs | 21 +++ .../en-US/escape-menu/ui/options-menu.ftl | 1 + Resources/Prototypes/Shaders/Stencils.yml | 11 ++ 8 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 Content.Client/Light/AmbientOcclusionOverlay.cs create mode 100644 Content.Shared/CCVar/CCVars.Lighting.cs diff --git a/Content.Client/Light/AmbientOcclusionOverlay.cs b/Content.Client/Light/AmbientOcclusionOverlay.cs new file mode 100644 index 0000000000..e24ee73bf4 --- /dev/null +++ b/Content.Client/Light/AmbientOcclusionOverlay.cs @@ -0,0 +1,137 @@ +using System.Numerics; +using Content.Shared.CCVar; +using Content.Shared.Maps; +using Robust.Client.Graphics; +using Robust.Shared.Configuration; +using Robust.Shared.Enums; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Client.Light; + +/// +/// Applies ambient-occlusion to the viewport. +/// +public sealed class AmbientOcclusionOverlay : Overlay +{ + [Dependency] private readonly IClyde _clyde = default!; + [Dependency] private readonly IConfigurationManager _cfgManager = default!; + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; + + public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities; + + private IRenderTexture? _aoTarget; + + // Couldn't figure out a way to avoid this so if you can then please do. + private IRenderTexture? _aoStencilTarget; + + public AmbientOcclusionOverlay() + { + IoCManager.InjectDependencies(this); + ZIndex = AfterLightTargetOverlay.ContentZIndex + 1; + } + + protected override void Draw(in OverlayDrawArgs args) + { + /* + * tl;dr + * - we draw a black square on each "ambient occlusion" entity. + * - we blur this. + * - We apply it to the viewport. + * + * We do this while ignoring lighting because it will wash out the actual effect. + * In 3D ambient occlusion is more complicated due top having to calculate normals but in 2D + * we don't have a concept of depth / corners necessarily. + */ + + var viewport = args.Viewport; + var mapId = args.MapId; + var worldBounds = args.WorldBounds; + var worldHandle = args.WorldHandle; + var color = Color.FromHex(_cfgManager.GetCVar(CCVars.AmbientOcclusionColor)); + var distance = _cfgManager.GetCVar(CCVars.AmbientOcclusionDistance); + //var color = Color.Red; + var target = viewport.RenderTarget; + var lightScale = target.Size / (Vector2) viewport.Size; + var scale = viewport.RenderScale / (Vector2.One / lightScale); + var maps = _entManager.System(); + var lookups = _entManager.System(); + var query = _entManager.System(); + var xformSystem = _entManager.System(); + var invMatrix = args.Viewport.GetWorldToLocalMatrix(); + + if (_aoTarget?.Texture.Size != target.Size) + { + _aoTarget?.Dispose(); + _aoTarget = _clyde.CreateRenderTarget(target.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "ambient-occlusion-target"); + } + + if (_aoStencilTarget?.Texture.Size != target.Size) + { + _aoStencilTarget?.Dispose(); + _aoStencilTarget = _clyde.CreateRenderTarget(target.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "ambient-occlusion-stencil-target"); + } + + // Draw the texture data to the texture. + args.WorldHandle.RenderInRenderTarget(_aoTarget, + () => + { + worldHandle.UseShader(_proto.Index("unshaded").Instance()); + var invMatrix = _aoTarget.GetWorldToLocalMatrix(viewport.Eye!, scale); + + foreach (var entry in query.QueryAabb(mapId, worldBounds)) + { + DebugTools.Assert(entry.Component.Enabled); + var matrix = xformSystem.GetWorldMatrix(entry.Transform); + var localMatrix = Matrix3x2.Multiply(matrix, invMatrix); + + worldHandle.SetTransform(localMatrix); + // 4 pixels + worldHandle.DrawRect(Box2.UnitCentered.Enlarged(distance / EyeManager.PixelsPerMeter), Color.White); + } + }, Color.Transparent); + + _clyde.BlurRenderTarget(viewport, _aoTarget, _aoTarget, viewport.Eye!, 14f); + + // Need to do stencilling after blur as it will nuke it. + // Draw stencil for the grid so we don't draw in space. + args.WorldHandle.RenderInRenderTarget(_aoStencilTarget, + () => + { + // Don't want lighting affecting it. + worldHandle.UseShader(_proto.Index("unshaded").Instance()); + + foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldBounds)) + { + var transform = xformSystem.GetWorldMatrix(grid.Owner); + var worldToTextureMatrix = Matrix3x2.Multiply(transform, invMatrix); + var tiles = maps.GetTilesEnumerator(grid.Owner, grid, worldBounds); + worldHandle.SetTransform(worldToTextureMatrix); + while (tiles.MoveNext(out var tileRef)) + { + if (tileRef.IsSpace(_tileDefManager)) + continue; + + var bounds = lookups.GetLocalBounds(tileRef, grid.TileSize); + worldHandle.DrawRect(bounds, Color.White); + } + } + + }, Color.Transparent); + + // Draw the stencil texture to depth buffer. + worldHandle.UseShader(_proto.Index("StencilMask").Instance()); + worldHandle.DrawTextureRect(_aoStencilTarget!.Texture, worldBounds); + + // Draw the Blurred AO texture finally. + worldHandle.UseShader(_proto.Index("StencilEqualDraw").Instance()); + worldHandle.DrawTextureRect(_aoTarget!.Texture, worldBounds, color); + + args.WorldHandle.SetTransform(Matrix3x2.Identity); + args.WorldHandle.UseShader(null); + } +} diff --git a/Content.Client/Light/EntitySystems/PlanetLightSystem.cs b/Content.Client/Light/EntitySystems/PlanetLightSystem.cs index cbe2f47f78..deabadaddf 100644 --- a/Content.Client/Light/EntitySystems/PlanetLightSystem.cs +++ b/Content.Client/Light/EntitySystems/PlanetLightSystem.cs @@ -1,17 +1,51 @@ +using Content.Shared.CCVar; using Robust.Client.Graphics; +using Robust.Shared.Configuration; namespace Content.Client.Light.EntitySystems; public sealed class PlanetLightSystem : EntitySystem { + [Dependency] private readonly IConfigurationManager _cfgManager = default!; [Dependency] private readonly IOverlayManager _overlayMan = default!; + /// + /// Enables / disables the ambient occlusion overlay. + /// + public bool AmbientOcclusion + { + get => _ambientOcclusion; + set + { + if (_ambientOcclusion == value) + return; + + _ambientOcclusion = value; + + if (value) + { + _overlayMan.AddOverlay(new AmbientOcclusionOverlay()); + } + else + { + _overlayMan.RemoveOverlay(); + } + } + } + + private bool _ambientOcclusion; + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnClearColor); + _cfgManager.OnValueChanged(CCVars.AmbientOcclusion, val => + { + AmbientOcclusion = val; + }, true); + _overlayMan.AddOverlay(new BeforeLightTargetOverlay()); _overlayMan.AddOverlay(new RoofOverlay(EntityManager)); _overlayMan.AddOverlay(new TileEmissionOverlay(EntityManager)); diff --git a/Content.Client/Light/RoofOverlay.cs b/Content.Client/Light/RoofOverlay.cs index 6e43466c24..9be4bfe4c4 100644 --- a/Content.Client/Light/RoofOverlay.cs +++ b/Content.Client/Light/RoofOverlay.cs @@ -62,6 +62,8 @@ public sealed class RoofOverlay : Overlay worldHandle.RenderInRenderTarget(target, () => { + var invMatrix = target.GetWorldToLocalMatrix(eye, scale); + for (var i = 0; i < _grids.Count; i++) { var grid = _grids[i]; @@ -69,8 +71,6 @@ public sealed class RoofOverlay : Overlay if (!_entManager.TryGetComponent(grid.Owner, out ImplicitRoofComponent? roof)) continue; - var invMatrix = target.GetWorldToLocalMatrix(eye, scale); - var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner); var matty = Matrix3x2.Multiply(gridMatrix, invMatrix); @@ -94,13 +94,13 @@ public sealed class RoofOverlay : Overlay worldHandle.RenderInRenderTarget(target, () => { + var invMatrix = target.GetWorldToLocalMatrix(eye, scale); + foreach (var grid in _grids) { if (!_entManager.TryGetComponent(grid.Owner, out RoofComponent? roof)) continue; - var invMatrix = target.GetWorldToLocalMatrix(eye, scale); - var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner); var matty = Matrix3x2.Multiply(gridMatrix, invMatrix); diff --git a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml index f1b9743cad..29279d1733 100644 --- a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml +++ b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml @@ -14,6 +14,7 @@ + public sealed class NarcolepsySystem : EntitySystem { - [ValidatePrototypeId] - private const string StatusEffectKey = "ForcedSleep"; // Same one used by N2O and other sleep chems. - [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; [Dependency] private readonly IRobustRandom _random = default!; @@ -56,8 +53,7 @@ public sealed class NarcolepsySystem : EntitySystem // Make sure the sleep time doesn't cut into the time to next incident. narcolepsy.NextIncidentTime += duration; - _statusEffects.TryAddStatusEffect(uid, StatusEffectKey, - TimeSpan.FromSeconds(duration), false); + _statusEffects.TryAddStatusEffect(uid, SleepingSystem.StatusEffectForcedSleeping, TimeSpan.FromSeconds(duration)); } } } diff --git a/Content.Shared/Bed/Sleep/ForcedSleepingComponent.cs b/Content.Shared/Bed/Sleep/ForcedSleepingComponent.cs deleted file mode 100644 index 197e8cc56a..0000000000 --- a/Content.Shared/Bed/Sleep/ForcedSleepingComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Bed.Sleep -{ - /// - /// Prevents waking up. Use as a status effect. - /// - [NetworkedComponent, RegisterComponent] - public sealed partial class ForcedSleepingComponent : Component - {} -} diff --git a/Content.Shared/Bed/Sleep/ForcedSleepingStatusEffectComponent.cs b/Content.Shared/Bed/Sleep/ForcedSleepingStatusEffectComponent.cs new file mode 100644 index 0000000000..b94c1d31c8 --- /dev/null +++ b/Content.Shared/Bed/Sleep/ForcedSleepingStatusEffectComponent.cs @@ -0,0 +1,10 @@ +using Content.Shared.StatusEffectNew.Components; +using Robust.Shared.GameStates; + +namespace Content.Shared.Bed.Sleep; + +/// +/// Prevents waking up. Use only in conjunction with , on the status effect entity. +/// +[NetworkedComponent, RegisterComponent] +public sealed partial class ForcedSleepingStatusEffectComponent : Component; diff --git a/Content.Shared/Bed/Sleep/SleepingSystem.cs b/Content.Shared/Bed/Sleep/SleepingSystem.cs index d1ca138431..9e1b27cfc9 100644 --- a/Content.Shared/Bed/Sleep/SleepingSystem.cs +++ b/Content.Shared/Bed/Sleep/SleepingSystem.cs @@ -19,6 +19,7 @@ using Content.Shared.Sound; using Content.Shared.Sound.Components; using Content.Shared.Speech; using Content.Shared.StatusEffect; +using Content.Shared.StatusEffectNew; using Content.Shared.Stunnable; using Content.Shared.Traits.Assorted; using Content.Shared.Verbs; @@ -37,10 +38,12 @@ public sealed partial class SleepingSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedEmitSoundSystem _emitSound = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffectOld = default!; + [Dependency] private readonly SharedStatusEffectsSystem _statusEffectNew = default!; public static readonly EntProtoId SleepActionId = "ActionSleep"; public static readonly EntProtoId WakeActionId = "ActionWake"; + public static readonly EntProtoId StatusEffectForcedSleeping = "StatusEffectForcedSleeping"; public override void Initialize() { @@ -65,7 +68,7 @@ public sealed partial class SleepingSystem : EntitySystem SubscribeLocalEvent>(AddWakeVerb); SubscribeLocalEvent(OnInteractHand); - SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnStatusEffectApplied); SubscribeLocalEvent(OnUnbuckleAttempt); SubscribeLocalEvent(OnEmoteAttempt); @@ -104,8 +107,8 @@ public sealed partial class SleepingSystem : EntitySystem if (args.FellAsleep) { // Expiring status effects would remove the components needed for sleeping - _statusEffectsSystem.TryRemoveStatusEffect(ent.Owner, "Stun"); - _statusEffectsSystem.TryRemoveStatusEffect(ent.Owner, "KnockedDown"); + _statusEffectOld.TryRemoveStatusEffect(ent.Owner, "Stun"); + _statusEffectOld.TryRemoveStatusEffect(ent.Owner, "KnockedDown"); EnsureComp(ent); EnsureComp(ent); @@ -248,9 +251,9 @@ public sealed partial class SleepingSystem : EntitySystem _emitSound.SetEnabled((ent, spam), args.NewMobState == MobState.Alive); } - private void OnInit(Entity ent, ref ComponentInit args) + private void OnStatusEffectApplied(Entity ent, ref StatusEffectAppliedEvent args) { - TrySleeping(ent.Owner); + TrySleeping(args.Target); } private void Wake(Entity ent) @@ -307,7 +310,7 @@ public sealed partial class SleepingSystem : EntitySystem if (!Resolve(ent, ref ent.Comp, false)) return false; - if (!force && HasComp(ent)) + if (!force && _statusEffectNew.HasEffectComp(ent)) { if (user != null) { diff --git a/Content.Shared/Damage/Systems/SharedGodmodeSystem.cs b/Content.Shared/Damage/Systems/SharedGodmodeSystem.cs index b6b487430b..d10600ab56 100644 --- a/Content.Shared/Damage/Systems/SharedGodmodeSystem.cs +++ b/Content.Shared/Damage/Systems/SharedGodmodeSystem.cs @@ -3,7 +3,7 @@ using Content.Shared.Damage.Events; using Content.Shared.Destructible; using Content.Shared.Rejuvenate; using Content.Shared.Slippery; -using Content.Shared.StatusEffect; +using Content.Shared.StatusEffectNew; namespace Content.Shared.Damage.Systems; diff --git a/Content.Shared/Drowsiness/DrowsinessComponent.cs b/Content.Shared/Drowsiness/DrowsinessStatusEffectComponent.cs similarity index 56% rename from Content.Shared/Drowsiness/DrowsinessComponent.cs rename to Content.Shared/Drowsiness/DrowsinessStatusEffectComponent.cs index 7e170ed232..e509ed18cc 100644 --- a/Content.Shared/Drowsiness/DrowsinessComponent.cs +++ b/Content.Shared/Drowsiness/DrowsinessStatusEffectComponent.cs @@ -1,26 +1,28 @@ using System.Numerics; +using Content.Shared.StatusEffectNew.Components; using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Drowsiness; /// -/// Exists for use as a status effect. Adds a shader to the client that scales with the effect duration. +/// Exists for use as a status effect. Adds a shader to the client that scales with the effect duration. +/// Use only in conjunction with , on the status effect entity. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentPause] -public sealed partial class DrowsinessComponent : Component +public sealed partial class DrowsinessStatusEffectComponent : Component { /// /// The random time between sleeping incidents, (min, max). /// - [DataField(required: true)] - public Vector2 TimeBetweenIncidents = new Vector2(5f, 60f); + [DataField] + public Vector2 TimeBetweenIncidents = new(5f, 60f); /// /// The duration of sleeping incidents, (min, max). /// - [DataField(required: true)] - public Vector2 DurationOfIncident = new Vector2(2, 5); + [DataField] + public Vector2 DurationOfIncident = new(2, 5); [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] [AutoPausedField] diff --git a/Content.Shared/Drowsiness/DrowsinessSystem.cs b/Content.Shared/Drowsiness/DrowsinessSystem.cs index 97d7c0952d..d1e84f5ff6 100644 --- a/Content.Shared/Drowsiness/DrowsinessSystem.cs +++ b/Content.Shared/Drowsiness/DrowsinessSystem.cs @@ -1,9 +1,5 @@ -using Content.Shared.StatusEffect; - namespace Content.Shared.Drowsiness; public abstract class SharedDrowsinessSystem : EntitySystem { - [ValidatePrototypeId] - public const string DrowsinessKey = "Drowsiness"; } diff --git a/Content.Shared/EntityEffects/Effects/StatusEffects/GenericStatusEffect.cs b/Content.Shared/EntityEffects/Effects/StatusEffects/GenericStatusEffect.cs index c6b162a82f..b770023604 100644 --- a/Content.Shared/EntityEffects/Effects/StatusEffects/GenericStatusEffect.cs +++ b/Content.Shared/EntityEffects/Effects/StatusEffects/GenericStatusEffect.cs @@ -13,6 +13,7 @@ namespace Content.Shared.EntityEffects.Effects.StatusEffects; /// /// Can be used for things like adding accents or something. I don't know. Go wild. /// +[Obsolete("Use ModifyStatusEffect with StatusEffectNewSystem instead")] public sealed partial class GenericStatusEffect : EntityEffect { [DataField(required: true)] diff --git a/Content.Shared/EntityEffects/Effects/StatusEffects/ModifyStatusEffect.cs b/Content.Shared/EntityEffects/Effects/StatusEffects/ModifyStatusEffect.cs new file mode 100644 index 0000000000..33021ecdab --- /dev/null +++ b/Content.Shared/EntityEffects/Effects/StatusEffects/ModifyStatusEffect.cs @@ -0,0 +1,66 @@ +using Content.Shared.StatusEffectNew; +using JetBrains.Annotations; +using Robust.Shared.Prototypes; + +namespace Content.Shared.EntityEffects.Effects.StatusEffects; + +/// +/// Changes status effects on entities: Adds, removes or sets time. +/// +[UsedImplicitly] +public sealed partial class ModifyStatusEffect : EntityEffect +{ + [DataField(required: true)] + public EntProtoId EffectProto; + + /// + /// Time for which status effect should be applied. Behaviour changes according to . + /// + [DataField] + public float Time = 2.0f; + + /// + /// true - refresh status effect time, false - accumulate status effect time. + /// + [DataField] + public bool Refresh = true; + + /// + /// Should this effect add the status effect, remove time from it, or set its cooldown? + /// + [DataField] + public StatusEffectMetabolismType Type = StatusEffectMetabolismType.Add; + + /// + public override void Effect(EntityEffectBaseArgs args) + { + var statusSys = args.EntityManager.EntitySysManager.GetEntitySystem(); + + var time = Time; + if (args is EntityEffectReagentArgs reagentArgs) + time *= reagentArgs.Scale.Float(); + + switch (Type) + { + case StatusEffectMetabolismType.Add: + statusSys.TryAddStatusEffect(args.TargetEntity, EffectProto, TimeSpan.FromSeconds(time), Refresh); + break; + case StatusEffectMetabolismType.Remove: + statusSys.TryAddTime(args.TargetEntity, EffectProto, -TimeSpan.FromSeconds(time)); + break; + case StatusEffectMetabolismType.Set: + statusSys.TrySetTime(args.TargetEntity, EffectProto, TimeSpan.FromSeconds(time)); + break; + } + } + + /// + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + => Loc.GetString( + "reagent-effect-guidebook-status-effect", + ("chance", Probability), + ("type", Type), + ("time", Time), + ("key", prototype.Index(EffectProto).Name) + ); +} diff --git a/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs b/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs index dfba833bcf..850ea8e8da 100644 --- a/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs +++ b/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Bed.Sleep; using Content.Shared.CCVar; +using Content.Shared.StatusEffectNew; using Robust.Shared.Configuration; using Robust.Shared.Player; using Robust.Shared.Timing; @@ -13,6 +14,7 @@ public sealed class SSDIndicatorSystem : EntitySystem { [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedStatusEffectsSystem _statusEffects = default!; private bool _icSsdSleep; private float _icSsdSleepTime; @@ -37,10 +39,11 @@ public sealed class SSDIndicatorSystem : EntitySystem component.FallAsleepTime = TimeSpan.Zero; if (component.ForcedSleepAdded) // Remove component only if it has been added by this system { - EntityManager.RemoveComponent(uid); + _statusEffects.TryRemoveStatusEffect(uid, SleepingSystem.StatusEffectForcedSleeping); component.ForcedSleepAdded = false; } } + Dirty(uid, component); } @@ -53,6 +56,7 @@ public sealed class SSDIndicatorSystem : EntitySystem { component.FallAsleepTime = _timing.CurTime + TimeSpan.FromSeconds(_icSsdSleepTime); } + Dirty(uid, component); } @@ -79,12 +83,11 @@ public sealed class SSDIndicatorSystem : EntitySystem while (query.MoveNext(out var uid, out var ssd)) { // Forces the entity to sleep when the time has come - if(ssd.IsSSD && + if (ssd.IsSSD && ssd.FallAsleepTime <= _timing.CurTime && - !TerminatingOrDeleted(uid) && - !HasComp(uid)) // Don't add the component if the entity has it from another sources + !TerminatingOrDeleted(uid)) { - EnsureComp(uid); + _statusEffects.TryAddStatusEffect(uid, SleepingSystem.StatusEffectForcedSleeping); ssd.ForcedSleepAdded = true; } } diff --git a/Content.Shared/StatusEffect/StatusEffectsSystem.cs b/Content.Shared/StatusEffect/StatusEffectsSystem.cs index 5fa634351d..9a73f83d34 100644 --- a/Content.Shared/StatusEffect/StatusEffectsSystem.cs +++ b/Content.Shared/StatusEffect/StatusEffectsSystem.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.Alert; using Content.Shared.Rejuvenate; +using Content.Shared.StatusEffectNew; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -8,6 +9,7 @@ using Robust.Shared.Utility; namespace Content.Shared.StatusEffect { + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public sealed class StatusEffectsSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -104,6 +106,7 @@ namespace Content.Shared.StatusEffect /// The status effects component to change, if you already have it. /// False if the effect could not be added or the component already exists, true otherwise. /// The component type to add and remove from the entity. + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, StatusEffectsComponent? status = null) where T : IComponent, new() @@ -123,6 +126,7 @@ namespace Content.Shared.StatusEffect } + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, string component, StatusEffectsComponent? status = null) { @@ -162,6 +166,7 @@ namespace Content.Shared.StatusEffect /// If the effect already exists, it will simply replace the cooldown with the new one given. /// If you want special 'effect merging' behavior, do it your own damn self! /// + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, @@ -255,6 +260,7 @@ namespace Content.Shared.StatusEffect /// Obviously this doesn't automatically clear any effects a status effect might have. /// That's up to the removed component to handle itself when it's removed. /// + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public bool TryRemoveStatusEffect(EntityUid uid, string key, StatusEffectsComponent? status = null, bool remComp = true) { @@ -298,6 +304,7 @@ namespace Content.Shared.StatusEffect /// The entity to remove effects from. /// The status effects component to change, if you already have it. /// False if any status effects failed to be removed, true if they all did. + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public bool TryRemoveAllStatusEffects(EntityUid uid, StatusEffectsComponent? status = null) { @@ -321,6 +328,7 @@ namespace Content.Shared.StatusEffect /// The entity to check on. /// The status effect ID to check for /// The status effect component, should you already have it. + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public bool HasStatusEffect(EntityUid uid, string key, StatusEffectsComponent? status = null) { @@ -338,6 +346,7 @@ namespace Content.Shared.StatusEffect /// The entity to check on. /// The status effect ID to check for /// The status effect component, should you already have it. + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public bool CanApplyEffect(EntityUid uid, string key, StatusEffectsComponent? status = null) { // don't log since stuff calling this prolly doesn't care if we don't actually have it @@ -364,6 +373,7 @@ namespace Content.Shared.StatusEffect /// The status effect to add time to. /// The amount of time to add. /// The status effect component, should you already have it. + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public bool TryAddTime(EntityUid uid, string key, TimeSpan time, StatusEffectsComponent? status = null) { @@ -395,6 +405,7 @@ namespace Content.Shared.StatusEffect /// The status effect to remove time from. /// The amount of time to add. /// The status effect component, should you already have it. + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public bool TryRemoveTime(EntityUid uid, string key, TimeSpan time, StatusEffectsComponent? status = null) { @@ -430,6 +441,7 @@ namespace Content.Shared.StatusEffect /// /// Not used internally; just sets it itself. /// + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public bool TrySetTime(EntityUid uid, string key, TimeSpan time, StatusEffectsComponent? status = null) { @@ -453,6 +465,7 @@ namespace Content.Shared.StatusEffect /// Out var for the time, if it exists. /// The status effects component to use, if any. /// False if the status effect was not active, true otherwise. + [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] public bool TryGetTime(EntityUid uid, string key, [NotNullWhen(true)] out (TimeSpan, TimeSpan)? time, StatusEffectsComponent? status = null) @@ -468,12 +481,6 @@ namespace Content.Shared.StatusEffect } } - /// - /// Raised on an entity before a status effect is added to determine if adding it should be cancelled. - /// - [ByRefEvent] - public record struct BeforeStatusEffectAddedEvent(string Key, bool Cancelled=false); - public readonly struct StatusEffectAddedEvent { public readonly EntityUid Uid; diff --git a/Content.Shared/StatusEffectNew/Components/StatusEffectComponent.cs b/Content.Shared/StatusEffectNew/Components/StatusEffectComponent.cs new file mode 100644 index 0000000000..6419874212 --- /dev/null +++ b/Content.Shared/StatusEffectNew/Components/StatusEffectComponent.cs @@ -0,0 +1,47 @@ +using Content.Shared.Alert; +using Content.Shared.Whitelist; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Shared.StatusEffectNew.Components; + +/// +/// Marker component for all status effects - every status effect entity should have it. +/// Provides a link between the effect and the affected entity, and some data common to all status effects. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] +[Access(typeof(SharedStatusEffectsSystem))] +[EntityCategory("StatusEffects")] +public sealed partial class StatusEffectComponent : Component +{ + /// + /// The entity that this status effect is applied to. + /// + [DataField, AutoNetworkedField] + public EntityUid? AppliedTo; + + /// + /// Status effect indication for the player. If Null, no Alert will be displayed. + /// + [DataField] + public ProtoId? Alert; + + /// + /// When this effect will end. If Null, the effect lasts indefinitely. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField, AutoNetworkedField] + public TimeSpan? EndEffectTime; + + /// + /// Whitelist, by which it is determined whether this status effect can be imposed on a particular entity. + /// + [DataField] + public EntityWhitelist? Whitelist; + + /// + /// Blacklist, by which it is determined whether this status effect can be imposed on a particular entity. + /// + [DataField] + public EntityWhitelist? Blacklist; +} diff --git a/Content.Shared/StatusEffectNew/Components/StatusEffectContainerComponent.cs b/Content.Shared/StatusEffectNew/Components/StatusEffectContainerComponent.cs new file mode 100644 index 0000000000..6d9efaf3ac --- /dev/null +++ b/Content.Shared/StatusEffectNew/Components/StatusEffectContainerComponent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.StatusEffectNew.Components; + +/// +/// Adds container for status effect entities that are applied to entity. +/// Is applied automatically upon adding any status effect. +/// Can be used for tracking currently applied status effects. +/// +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedStatusEffectsSystem))] +public sealed partial class StatusEffectContainerComponent : Component +{ + [DataField] + public HashSet ActiveStatusEffects = new(); +} + +[Serializable, NetSerializable] +public sealed class StatusEffectContainerComponentState(HashSet activeStatusEffects) : ComponentState +{ + public readonly HashSet ActiveStatusEffects = activeStatusEffects; +} diff --git a/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs b/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs new file mode 100644 index 0000000000..df27dca27c --- /dev/null +++ b/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs @@ -0,0 +1,208 @@ +using Content.Shared.Alert; +using Content.Shared.StatusEffectNew.Components; +using Content.Shared.Whitelist; +using Robust.Shared.GameStates; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Shared.StatusEffectNew; + +/// +/// This system controls status effects, their lifetime, and provides an API for adding them to entities, +/// removing them from entities, or getting information about current effects on entities. +/// +public abstract partial class SharedStatusEffectsSystem : EntitySystem +{ + [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IComponentFactory _compFactory = default!; + [Dependency] private readonly INetManager _net = default!; + + private EntityQuery _containerQuery; + private EntityQuery _effectQuery; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStatusEffectApplied); + SubscribeLocalEvent(OnStatusEffectRemoved); + + SubscribeLocalEvent(OnStatusEffectContainerAttached); + SubscribeLocalEvent(OnStatusEffectContainerDetached); + SubscribeLocalEvent(OnGetState); + + _containerQuery = GetEntityQuery(); + _effectQuery = GetEntityQuery(); + } + + private void OnGetState(Entity ent, ref ComponentGetState args) + { + args.State = new StatusEffectContainerComponentState(GetNetEntitySet(ent.Comp.ActiveStatusEffects)); + } + + private void OnStatusEffectContainerAttached(Entity ent, ref LocalPlayerAttachedEvent args) + { + foreach (var effect in ent.Comp.ActiveStatusEffects) + { + var ev = new StatusEffectPlayerAttachedEvent(ent); + RaiseLocalEvent(effect, ref ev); + } + } + + private void OnStatusEffectContainerDetached(Entity ent, ref LocalPlayerDetachedEvent args) + { + foreach (var effect in ent.Comp.ActiveStatusEffects) + { + var ev = new StatusEffectPlayerDetachedEvent(ent); + RaiseLocalEvent(effect, ref ev); + } + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var ent, out var effect)) + { + if (effect.EndEffectTime is null) + continue; + + if (!(_timing.CurTime >= effect.EndEffectTime)) + continue; + + if (effect.AppliedTo is null) + continue; + + var meta = MetaData(ent); + if (meta.EntityPrototype is null) + continue; + + TryRemoveStatusEffect(effect.AppliedTo.Value, meta.EntityPrototype); + } + } + + private void AddStatusEffectTime(EntityUid effect, TimeSpan delta) + { + if (!_effectQuery.TryComp(effect, out var effectComp)) + return; + + effectComp.EndEffectTime += delta; + Dirty(effect, effectComp); + + if (effectComp is { AppliedTo: not null, Alert: not null }) + { + (TimeSpan Start, TimeSpan End)? cooldown = effectComp.EndEffectTime is null + ? null + : (_timing.CurTime, effectComp.EndEffectTime.Value); + _alerts.ShowAlert( + effectComp.AppliedTo.Value, + effectComp.Alert.Value, + cooldown: cooldown + ); + } + } + + private void SetStatusEffectTime(EntityUid effect, TimeSpan duration) + { + if (!_effectQuery.TryComp(effect, out var effectComp)) + return; + + effectComp.EndEffectTime = _timing.CurTime + duration; + Dirty(effect, effectComp); + + if (effectComp is { AppliedTo: not null, Alert: not null }) + { + (TimeSpan, TimeSpan)? cooldown = effectComp.EndEffectTime is null + ? null + : (_timing.CurTime, effectComp.EndEffectTime.Value); + _alerts.ShowAlert( + effectComp.AppliedTo.Value, + effectComp.Alert.Value, + cooldown: cooldown + ); + } + } + + private void OnStatusEffectApplied(Entity ent, ref StatusEffectAppliedEvent args) + { + if (ent.Comp is { AppliedTo: not null, Alert: not null }) + { + (TimeSpan, TimeSpan)? cooldown = ent.Comp.EndEffectTime is null + ? null + : (_timing.CurTime, ent.Comp.EndEffectTime.Value); + _alerts.ShowAlert( + ent.Comp.AppliedTo.Value, + ent.Comp.Alert.Value, + cooldown: cooldown + ); + } + } + + private void OnStatusEffectRemoved(Entity ent, ref StatusEffectRemovedEvent args) + { + if (ent.Comp.AppliedTo is null) + return; + + if (ent.Comp is { AppliedTo: not null, Alert: not null }) + _alerts.ClearAlert(ent.Comp.AppliedTo.Value, ent.Comp.Alert.Value); + } + + private bool CanAddStatusEffect(EntityUid uid, EntProtoId effectProto) + { + if (!_proto.TryIndex(effectProto, out var effectProtoData)) + return false; + + if (!effectProtoData.TryGetComponent(out var effectProtoComp, _compFactory)) + return false; + + if (!_whitelist.CheckBoth(uid, effectProtoComp.Blacklist, effectProtoComp.Whitelist)) + return false; + + var ev = new BeforeStatusEffectAddedEvent(effectProto); + RaiseLocalEvent(uid, ref ev); + + if (ev.Cancelled) + return false; + + return true; + } +} + +/// +/// Calls on effect entity, when a status effect is applied. +/// +[ByRefEvent] +public readonly record struct StatusEffectAppliedEvent(EntityUid Target); + +/// +/// Calls on effect entity, when a status effect is removed. +/// +[ByRefEvent] +public readonly record struct StatusEffectRemovedEvent(EntityUid Target); + +/// +/// Called on a status effect entity inside +/// after a player has been to this container entity. +/// +[ByRefEvent] +public readonly record struct StatusEffectPlayerAttachedEvent(EntityUid Target); + +/// +/// Called on a status effect entity inside +/// after a player has been to this container entity. +/// +[ByRefEvent] +public readonly record struct StatusEffectPlayerDetachedEvent(EntityUid Target); + +/// +/// Raised on an entity before a status effect is added to determine if adding it should be cancelled. +/// +[ByRefEvent] +public record struct BeforeStatusEffectAddedEvent(EntProtoId Effect, bool Cancelled = false); diff --git a/Content.Shared/StatusEffectNew/StatusEffectNewSystem.API.cs b/Content.Shared/StatusEffectNew/StatusEffectNewSystem.API.cs new file mode 100644 index 0000000000..8f34f97930 --- /dev/null +++ b/Content.Shared/StatusEffectNew/StatusEffectNewSystem.API.cs @@ -0,0 +1,265 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.StatusEffectNew.Components; +using Robust.Shared.Prototypes; + +namespace Content.Shared.StatusEffectNew; + +public abstract partial class SharedStatusEffectsSystem +{ + /// + /// Attempts to add a status effect to the specified entity. Returns True if the effect is added or it already exists + /// and has been successfully extended in time, returns False if the status effect cannot be applied to this entity, + /// or for any other reason. + /// + /// The target entity to which the effect should be added. + /// ProtoId of the status effect entity. Make sure it has StatusEffectComponent on it. + /// Duration of status effect. Leave null and the effect will be permanent until it is removed using TryRemoveStatusEffect. + /// + /// If True, the effect duration time will be reset and reapplied. If False, the effect duration time will be overlaid with the existing one. + /// In the other case, the effect will either be added for the specified time or its time will be extended for the specified time. + /// + public bool TryAddStatusEffect( + EntityUid target, + EntProtoId effectProto, + TimeSpan? duration = null, + bool resetCooldown = false + ) + { + if (TryGetStatusEffect(target, effectProto, out var existedEffect)) + { + //We don't need to add the effect if it already exists + if (duration is null) + return true; + + if (resetCooldown) + SetStatusEffectTime(existedEffect.Value, duration.Value); + else + AddStatusEffectTime(existedEffect.Value, duration.Value); + + return true; + } + + if (!CanAddStatusEffect(target, effectProto)) + return false; + + var container = EnsureComp(target); + + //And only if all checks passed we spawn the effect + var effect = PredictedSpawnAttachedTo(effectProto, Transform(target).Coordinates); + _transform.SetParent(effect, target); + if (!_effectQuery.TryComp(effect, out var effectComp)) + return false; + + if (duration != null) + effectComp.EndEffectTime = _timing.CurTime + duration; + + container.ActiveStatusEffects.Add(effect); + effectComp.AppliedTo = target; + Dirty(target, container); + Dirty(effect, effectComp); + + var ev = new StatusEffectAppliedEvent(target); + RaiseLocalEvent(effect, ref ev); + + return true; + } + + /// + /// Attempting to remove a status effect from an entity. + /// Returns True if the status effect existed on the entity and was successfully removed, and False in otherwise. + /// + public bool TryRemoveStatusEffect(EntityUid target, EntProtoId effectProto) + { + if (_net.IsClient) //We cant remove the effect on the client (we need someone more robust at networking than me) + return false; + + if (!_containerQuery.TryComp(target, out var container)) + return false; + + foreach (var effect in container.ActiveStatusEffects) + { + var meta = MetaData(effect); + if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) + { + if (!_effectQuery.TryComp(effect, out var effectComp)) + return false; + + var ev = new StatusEffectRemovedEvent(target); + RaiseLocalEvent(effect, ref ev); + + QueueDel(effect); + container.ActiveStatusEffects.Remove(effect); + Dirty(target, container); + return true; + } + } + + return false; + } + + /// + /// Checks whether the specified entity is under a specific status effect. + /// + public bool HasStatusEffect(EntityUid target, EntProtoId effectProto) + { + if (!_containerQuery.TryComp(target, out var container)) + return false; + + foreach (var effect in container.ActiveStatusEffects) + { + var meta = MetaData(effect); + if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) + return true; + } + + return false; + } + + /// + /// Attempting to retrieve the EntityUid of a status effect from an entity. + /// + public bool TryGetStatusEffect(EntityUid target, EntProtoId effectProto, [NotNullWhen(true)] out EntityUid? effect) + { + effect = null; + if (!_containerQuery.TryComp(target, out var container)) + return false; + + foreach (var e in container.ActiveStatusEffects) + { + var meta = MetaData(e); + if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) + { + effect = e; + return true; + } + } + + return false; + } + + /// + /// Attempting to retrieve the time of a status effect from an entity. + /// + /// The target entity on which the effect is applied. + /// The prototype ID of the status effect to retrieve. + /// The output tuple containing the effect entity and its remaining time. + /// Optional. The status effect container component of the entity. + public bool TryGetTime( + EntityUid uid, + EntProtoId effectProto, + out (EntityUid EffectEnt, TimeSpan? EndEffectTime) time, + StatusEffectContainerComponent? container = null + ) + { + time = default; + if (!Resolve(uid, ref container)) + return false; + + foreach (var effect in container.ActiveStatusEffects) + { + var meta = MetaData(effect); + if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) + { + if (!_effectQuery.TryComp(effect, out var effectComp)) + return false; + + time = (effect, effectComp.EndEffectTime); + return true; + } + } + + return false; + } + + /// + /// Attempts to edit the remaining time for a status effect on an entity. + /// + /// The target entity on which the effect is applied. + /// The prototype ID of the status effect to modify. + /// + /// The time adjustment to apply to the status effect. Positive values extend the duration, + /// while negative values reduce it. + /// + /// True if duration was edited successfully, false otherwise. + public bool TryAddTime(EntityUid uid, EntProtoId effectProto, TimeSpan time) + { + if (!_containerQuery.TryComp(uid, out var container)) + return false; + + foreach (var effect in container.ActiveStatusEffects) + { + var meta = MetaData(effect); + if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) + { + AddStatusEffectTime(effect, time); + return true; + } + } + return false; + } + + /// + /// Attempts to set the remaining time for a status effect on an entity. + /// + /// The target entity on which the effect is applied. + /// The prototype ID of the status effect to modify. + /// The new duration for the status effect. + /// True if duration was set successfully, false otherwise. + public bool TrySetTime(EntityUid uid, EntProtoId effectProto, TimeSpan time) + { + if (!_containerQuery.TryComp(uid, out var container)) + return false; + + foreach (var effect in container.ActiveStatusEffects) + { + var meta = MetaData(effect); + if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) + { + SetStatusEffectTime(effect, time); + return true; + } + } + return false; + } + + /// + /// Checks if the specified component is present on any of the entity's status effects. + /// + public bool HasEffectComp(EntityUid? target) where T : IComponent + { + if (!_containerQuery.TryComp(target, out var container)) + return false; + + foreach (var effect in container.ActiveStatusEffects) + { + if (HasComp(effect)) + return true; + } + + return false; + } + + /// + /// Returns all status effects that have the specified component. + /// + public bool TryEffectsWithComp(EntityUid? target, [NotNullWhen(true)] out HashSet>? effects) where T : IComponent + { + effects = null; + if (!_containerQuery.TryComp(target, out var container)) + return false; + + foreach (var effect in container.ActiveStatusEffects) + { + if (!TryComp(effect, out var statusComp)) + continue; + + if (TryComp(effect, out var comp)) + { + effects ??= []; + effects.Add((effect, comp, statusComp)); + } + } + + return effects != null; + } +} diff --git a/Resources/Locale/en-US/entity-categories.ftl b/Resources/Locale/en-US/entity-categories.ftl index a5ed66dd01..8a4ea73d92 100644 --- a/Resources/Locale/en-US/entity-categories.ftl +++ b/Resources/Locale/en-US/entity-categories.ftl @@ -4,5 +4,6 @@ entity-category-name-objectives = Objectives entity-category-name-roles = Mind Roles entity-category-name-mapping = Mapping entity-category-name-donotmap = Do not map +entity-category-name-status-effects = Status Effects entity-category-suffix-donotmap = DO NOT MAP diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml b/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml index 3ba17c3319..de8f15f4c0 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml @@ -25,7 +25,6 @@ - Electrocution - TemporaryBlindness - RadiationProtection - - Drowsiness - Adrenaline - type: StandingState - type: Tag diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml index 1143f8be6c..bb095bfffd 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml @@ -22,12 +22,10 @@ - SlowedDown - Stutter - Electrocution - - ForcedSleep - TemporaryBlindness - Pacified - Flashed - RadiationProtection - - Drowsiness - Adrenaline - type: Buckle - type: StandingState @@ -100,13 +98,11 @@ - SlowedDown - Stutter - Electrocution - - ForcedSleep - TemporaryBlindness - Pacified - StaminaModifier - Flashed - RadiationProtection - - Drowsiness - Adrenaline - type: Bloodstream bloodMaxVolume: 150 diff --git a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml index 7ba6acf50d..fc2dddb23d 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml @@ -103,11 +103,9 @@ - SlowedDown - Stutter - Electrocution - - ForcedSleep - TemporaryBlindness - Pacified - RadiationProtection - - Drowsiness - Adrenaline - type: Temperature heatDamageThreshold: 800 diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index ed5d53aa97..b93ed915e2 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -130,13 +130,11 @@ - RatvarianLanguage - PressureImmunity - Muted - - ForcedSleep - TemporaryBlindness - Pacified - StaminaModifier - Flashed - RadiationProtection - - Drowsiness - Adrenaline - type: Body prototype: Human diff --git a/Resources/Prototypes/Entities/StatusEffects/misc.yml b/Resources/Prototypes/Entities/StatusEffects/misc.yml new file mode 100644 index 0000000000..254d1608bd --- /dev/null +++ b/Resources/Prototypes/Entities/StatusEffects/misc.yml @@ -0,0 +1,36 @@ +- type: entity + id: StatusEffectBase + abstract: true + components: + - type: StatusEffect + - type: Sprite + drawdepth: Effects + - type: Tag + tags: + - HideContextMenu + +- type: entity + parent: StatusEffectBase + id: MobStatusEffectBase + abstract: true + components: + - type: StatusEffect + whitelist: + components: + - MobState + +# The creature sleeps so heavily that nothing can wake him up. Not even its own death. +- type: entity + parent: MobStatusEffectBase + id: StatusEffectForcedSleeping + name: forced sleep + components: + - type: ForcedSleepingStatusEffect + +# Blurs your vision and makes you randomly fall asleep +- type: entity + parent: MobStatusEffectBase + id: StatusEffectDrowsiness + name: drowsiness + components: + - type: DrowsinessStatusEffect diff --git a/Resources/Prototypes/Entities/categories.yml b/Resources/Prototypes/Entities/categories.yml index 5b8e794309..aaf77989cd 100644 --- a/Resources/Prototypes/Entities/categories.yml +++ b/Resources/Prototypes/Entities/categories.yml @@ -27,3 +27,8 @@ id: DoNotMap name: entity-category-name-donotmap suffix: entity-category-suffix-donotmap + +- type: entityCategory + id: StatusEffects + name: entity-category-name-status-effects + hideSpawnMenu: true \ No newline at end of file diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml b/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml index 7294972e7e..adb9aa28e4 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml @@ -2018,9 +2018,9 @@ - !type:AdjustReagent reagent: Theobromine amount: 0.05 - - !type:GenericStatusEffect - key: Drowsiness - time: 1.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 1 type: Remove fizziness: 0.25 @@ -2049,9 +2049,9 @@ - !type:AdjustReagent reagent: Theobromine amount: 0.05 - - !type:GenericStatusEffect - key: Drowsiness - time: 1.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 1 type: Remove fizziness: 0.15 @@ -2080,9 +2080,9 @@ - !type:AdjustReagent reagent: Theobromine amount: 0.05 - - !type:GenericStatusEffect - key: Drowsiness - time: 1.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 1 type: Remove fizziness: 0.15 @@ -2111,9 +2111,9 @@ - !type:AdjustReagent reagent: Theobromine amount: 0.05 - - !type:GenericStatusEffect - key: Drowsiness - time: 1.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 1 type: Remove fizziness: 0.25 @@ -2142,9 +2142,9 @@ - !type:AdjustReagent reagent: Theobromine amount: 0.05 - - !type:GenericStatusEffect - key: Drowsiness - time: 1.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 1 type: Remove fizziness: 0.15 @@ -2173,9 +2173,9 @@ - !type:AdjustReagent reagent: Theobromine amount: 0.05 - - !type:GenericStatusEffect - key: Drowsiness - time: 1.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 1 type: Remove fizziness: 0.25 diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml b/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml index 1f092032d0..2d8351c97f 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml @@ -12,9 +12,9 @@ effects: - !type:SatiateThirst factor: 2 - - !type:GenericStatusEffect - key: Drowsiness - time: 2.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 2 type: Remove - !type:AdjustReagent reagent: Theobromine @@ -105,9 +105,9 @@ effects: - !type:SatiateThirst factor: 2 - - !type:GenericStatusEffect - key: Drowsiness - time: 2.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 2 type: Remove - type: reagent @@ -161,9 +161,9 @@ effects: - !type:SatiateThirst factor: 2 - - !type:GenericStatusEffect - key: Drowsiness - time: 2.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 2 type: Remove - type: reagent @@ -342,9 +342,9 @@ effects: - !type:SatiateThirst factor: 6 - - !type:GenericStatusEffect - key: Drowsiness - time: 3.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 3 type: Remove Poison: effects: @@ -383,9 +383,9 @@ effects: - !type:SatiateThirst factor: 2 - - !type:GenericStatusEffect - key: Drowsiness - time: 2.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 2 type: Remove - type: reagent diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml b/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml index d224d1573e..5ac667ec18 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml @@ -18,9 +18,9 @@ effects: - !type:SatiateThirst factor: 2 - - !type:GenericStatusEffect - key: Drowsiness - time: 1.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 1 type: Remove - type: reagent @@ -80,9 +80,9 @@ effects: - !type:SatiateThirst factor: 2 - - !type:GenericStatusEffect - key: Drowsiness - time: 2.0 + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness + time: 2 type: Remove - !type:AdjustReagent reagent: Theobromine @@ -261,7 +261,7 @@ state: icon_empty metamorphicMaxFillLevels: 5 metamorphicFillBaseName: fill- - metamorphicChangeColor: false + metamorphicChangeColor: false metabolisms: Drink: effects: diff --git a/Resources/Prototypes/Reagents/gases.yml b/Resources/Prototypes/Reagents/gases.yml index 2677fc0ed0..1087d7bad4 100644 --- a/Resources/Prototypes/Reagents/gases.yml +++ b/Resources/Prototypes/Reagents/gases.yml @@ -360,7 +360,7 @@ shouldHave: false walkSpeedModifier: 0.65 sprintSpeedModifier: 0.65 - - !type:GenericStatusEffect + - !type:ModifyStatusEffect conditions: - !type:ReagentThreshold reagent: NitrousOxide @@ -368,8 +368,7 @@ - !type:OrganType type: Slime shouldHave: false - key: ForcedSleep - component: ForcedSleeping + effectProto: StatusEffectForcedSleeping time: 3 type: Add - !type:HealthChange diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml index d553b8065d..2873372b52 100644 --- a/Resources/Prototypes/Reagents/medicine.yml +++ b/Resources/Prototypes/Reagents/medicine.yml @@ -83,9 +83,8 @@ key: Jitter time: 3.0 type: Remove - - !type:GenericStatusEffect - key: Drowsiness - component: Drowsiness + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness time: 1.5 type: Add refresh: false @@ -923,8 +922,8 @@ - !type:GenericStatusEffect key: Stutter component: StutteringAccent - - !type:GenericStatusEffect - key: Drowsiness + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness time: 10 type: Remove - !type:ResetNarcolepsy @@ -948,8 +947,8 @@ metabolisms: Medicine: effects: - - !type:GenericStatusEffect - key: Drowsiness + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness time: 10 type: Remove - !type:ResetNarcolepsy @@ -1390,9 +1389,8 @@ emote: Yawn showInChat: true probability: 0.1 - - !type:GenericStatusEffect - key: Drowsiness - component: Drowsiness + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness time: 4 type: Add refresh: false diff --git a/Resources/Prototypes/Reagents/narcotics.yml b/Resources/Prototypes/Reagents/narcotics.yml index 6e159278e9..aff87479b1 100644 --- a/Resources/Prototypes/Reagents/narcotics.yml +++ b/Resources/Prototypes/Reagents/narcotics.yml @@ -40,12 +40,12 @@ key: KnockedDown time: 3 type: Remove - - !type:GenericStatusEffect + - !type:ModifyStatusEffect conditions: - !type:ReagentThreshold reagent: Haloperidol max: 0.01 - key: Drowsiness + effectProto: StatusEffectDrowsiness time: 10 type: Remove Medicine: @@ -88,12 +88,12 @@ key: KnockedDown time: 1 type: Remove - - !type:GenericStatusEffect + - !type:ModifyStatusEffect conditions: - !type:ReagentThreshold reagent: Haloperidol max: 0.01 - key: Drowsiness + effectProto: StatusEffectDrowsiness time: 10 type: Remove - !type:PopupMessage @@ -152,16 +152,16 @@ component: StaminaModifier time: 3 type: Add - - !type:GenericStatusEffect - key: ForcedSleep + - !type:ModifyStatusEffect + effectProto: StatusEffectForcedSleeping time: 3 type: Remove - - !type:GenericStatusEffect + - !type:ModifyStatusEffect conditions: - !type:ReagentThreshold reagent: Haloperidol max: 0.01 - key: Drowsiness + effectProto: StatusEffectDrowsiness time: 10 type: Remove Medicine: @@ -296,14 +296,13 @@ metabolisms: Narcotic: effects: - - !type:GenericStatusEffect + - !type:ModifyStatusEffect conditions: - !type:ReagentThreshold reagent: Nocturine min: 8 - key: ForcedSleep - component: ForcedSleeping - refresh: false + effectProto: StatusEffectForcedSleeping + time: 3 type: Add - type: reagent diff --git a/Resources/Prototypes/Reagents/toxins.yml b/Resources/Prototypes/Reagents/toxins.yml index 72730c7990..6560f05995 100644 --- a/Resources/Prototypes/Reagents/toxins.yml +++ b/Resources/Prototypes/Reagents/toxins.yml @@ -63,9 +63,8 @@ - !type:MovespeedModifier walkSpeedModifier: 0.65 sprintSpeedModifier: 0.65 - - !type:GenericStatusEffect - key: Drowsiness - component: Drowsiness + - !type:ModifyStatusEffect + effectProto: StatusEffectDrowsiness time: 4 type: Add refresh: false diff --git a/Resources/Prototypes/status_effects.yml b/Resources/Prototypes/status_effects.yml index 49e5ccc579..e98dd4df02 100644 --- a/Resources/Prototypes/status_effects.yml +++ b/Resources/Prototypes/status_effects.yml @@ -1,6 +1,9 @@ # Status effect prototypes. # Holds no actual logic, just some basic data about the effect. +# Note: We have a new status effect system that needs all of these status effects to be fully ported to. +# Adding new status effects under the old system is NOT RECOMMENDED. + - type: statusEffect id: Stun alert: Stun @@ -45,9 +48,6 @@ id: Corporeal alert: Corporeal -- type: statusEffect - id: ForcedSleep #I.e., they will not wake on damage or similar - - type: statusEffect id: TemporaryBlindness @@ -66,9 +66,6 @@ - type: statusEffect id: RadiationProtection -- type: statusEffect - id: Drowsiness #blurs your vision and makes you randomly fall asleep - - type: statusEffect id: Adrenaline alert: Adrenaline From 6cffa8aabe48304a41bf4a1354ed2e1191652c5a Mon Sep 17 00:00:00 2001 From: Myra Date: Wed, 25 Jun 2025 13:56:47 +0200 Subject: [PATCH 085/191] Properly evaluate publish condition for master branch (#38556) --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bb50164b34..3ce5901841 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Fail if we are attempting to run on the master branch - if: ${{GITHUB.REF_NAME == 'master'}} && github.repository == 'space-wizards/space-station-14' + if: ${{GITHUB.REF_NAME == 'master' && github.repository == 'space-wizards/space-station-14'}} run: exit 1 - name: Install dependencies From 524725d3780ab3fc4cdec388f42c5bc56615b91c Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Wed, 25 Jun 2025 09:13:03 -0400 Subject: [PATCH 086/191] HandsSystem Refactor (#38438) * checkpoint * pt 2 * pt... i forgot * pt 4 * patch * More test fixes * optimization!!! * the REAL hand system * fix RetractableItemActionSystem.cs oversight * the review * test * remove test usage of body prototype * Update Content.IntegrationTests/Tests/Interaction/InteractionTest.cs Co-authored-by: Tayrtahn * hellcode * hellcode 2 * Minor cleanup * test * Chasing the last of the bugs * changes --------- Co-authored-by: Tayrtahn --- Content.Client/Hands/Systems/HandsSystem.cs | 230 +++++------- .../Inventory/StrippableBoundUserInterface.cs | 24 +- Content.Client/RCD/AlignRCDConstruction.cs | 7 +- .../RCD/RCDConstructionGhostSystem.cs | 9 +- Content.Client/SubFloor/TrayScannerSystem.cs | 6 +- .../Systems/Hands/HandsUIController.cs | 55 +-- .../Inventory/InventoryUIController.cs | 15 +- .../Actions/RetractableItemActionTest.cs | 8 +- .../Tests/Buckle/BuckleTest.cs | 8 +- .../Tests/Chemistry/DispenserTest.cs | 13 +- .../Tests/Commands/SuicideCommandTests.cs | 4 +- .../Interaction/WallConstruction.cs | 4 +- .../Tests/Hands/HandTests.cs | 16 +- .../Interaction/InteractionTest.Helpers.cs | 26 +- .../Tests/Interaction/InteractionTest.cs | 24 +- .../Tests/Tiles/TileConstructionTests.cs | 8 +- .../Commands/StripAllCommand.cs | 7 +- .../Administration/Systems/AdminSystem.cs | 4 +- .../Systems/AdminVerbSystem.Tools.cs | 2 +- .../Bed/Cryostorage/CryostorageSystem.cs | 7 +- .../Botany/Systems/PlantHolderSystem.cs | 8 +- Content.Server/Chat/SuicideSystem.cs | 8 +- .../ConstructionSystem.Initial.cs | 4 +- Content.Server/Hands/Systems/HandsSystem.cs | 37 +- Content.Server/HotPotato/HotPotatoSystem.cs | 2 +- .../ActiveHandComponentPrecondition.cs | 10 +- .../ActiveHandEntityPrecondition.cs | 7 +- .../Operators/Interactions/DropOperator.cs | 2 +- Content.Server/NPC/NPCBlackboard.cs | 26 +- .../NPC/Systems/NPCUtilitySystem.cs | 7 +- .../Shuttles/Commands/FTLDiskCommand.cs | 4 +- .../Silicons/Borgs/BorgSystem.Modules.cs | 8 +- Content.Server/Tabletop/TabletopSystem.cs | 11 +- .../Tools/Innate/InnateToolSystem.cs | 4 +- Content.Server/Verbs/VerbSystem.cs | 6 +- .../VoiceTrigger/StorageVoiceControlSystem.cs | 27 +- Content.Server/Wires/WiresSystem.cs | 14 +- .../Access/Systems/SharedIdCardSystem.cs | 6 +- Content.Shared/Blocking/BlockingSystem.cs | 4 +- .../SharedSolutionContainerSystem.cs | 7 +- .../Containers/ItemSlot/ItemSlotsSystem.cs | 18 +- Content.Shared/Cuffs/SharedCuffableSystem.cs | 12 +- .../Disposal/Unit/SharedDisposalUnitSystem.cs | 2 +- .../DoAfter/SharedDoAfterSystem.Update.cs | 2 +- Content.Shared/DoAfter/SharedDoAfterSystem.cs | 4 +- .../Foldable/DeployFoldableSystem.cs | 2 +- .../Hands/Components/HandHelpers.cs | 44 --- .../Hands/Components/HandsComponent.cs | 77 ++-- .../EntitySystems/SharedHandsSystem.AI.cs | 13 +- .../EntitySystems/SharedHandsSystem.Drop.cs | 106 +++--- .../SharedHandsSystem.Interactions.cs | 64 ++-- .../EntitySystems/SharedHandsSystem.Pickup.cs | 100 +++--- .../EntitySystems/SharedHandsSystem.Relay.cs | 2 +- .../Hands/EntitySystems/SharedHandsSystem.cs | 338 ++++++++++++------ .../Interaction/SharedInteractionSystem.cs | 4 +- .../Interaction/SmartEquipSystem.cs | 10 +- .../Inventory/InventorySystem.Equip.cs | 4 +- .../Inventory/InventorySystem.Helpers.cs | 7 +- .../VirtualItem/SharedVirtualItemSystem.cs | 16 +- Content.Shared/Item/MultiHandedItemSystem.cs | 3 +- Content.Shared/Item/SharedItemSystem.cs | 2 +- Content.Shared/Magic/SharedMagicSystem.cs | 2 +- .../Movement/Pulling/Systems/PullingSystem.cs | 10 +- .../Ninja/Systems/SharedNinjaGlovesSystem.cs | 5 +- .../Nutrition/EntitySystems/FoodSystem.cs | 2 +- Content.Shared/RCD/Systems/RCDSystem.cs | 8 +- .../RetractableItemActionSystem.cs | 13 +- Content.Shared/Stacks/SharedStackSystem.cs | 2 +- .../Station/SharedStationSpawningSystem.cs | 2 +- .../EntitySystems/SharedStorageSystem.cs | 14 +- .../Strip/SharedStrippableSystem.cs | 93 ++--- .../UserInterface/ActivatableUISystem.cs | 8 +- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 11 +- .../Weapons/Misc/SharedGrapplingGunSystem.cs | 13 +- .../Weapons/Misc/SharedTetherGunSystem.cs | 8 +- .../Weapons/Ranged/Systems/SharedGunSystem.cs | 6 +- .../Wieldable/SharedWieldableSystem.cs | 8 +- .../Prototypes/Body/Prototypes/a_ghost.yml | 22 -- .../Entities/Mobs/Player/admin_ghost.yml | 10 +- 79 files changed, 849 insertions(+), 897 deletions(-) delete mode 100644 Content.Shared/Hands/Components/HandHelpers.cs delete mode 100644 Resources/Prototypes/Body/Prototypes/a_ghost.yml diff --git a/Content.Client/Hands/Systems/HandsSystem.cs b/Content.Client/Hands/Systems/HandsSystem.cs index ab389a49d5..37bc9b1232 100644 --- a/Content.Client/Hands/Systems/HandsSystem.cs +++ b/Content.Client/Hands/Systems/HandsSystem.cs @@ -16,7 +16,6 @@ using Robust.Client.UserInterface; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Player; -using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Client.Hands.Systems @@ -27,16 +26,13 @@ namespace Content.Client.Hands.Systems [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IUserInterfaceManager _ui = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly StrippableSystem _stripSys = default!; [Dependency] private readonly SpriteSystem _sprite = default!; [Dependency] private readonly ExamineSystem _examine = default!; [Dependency] private readonly DisplacementMapSystem _displacement = default!; - public event Action? OnPlayerAddHand; - public event Action? OnPlayerRemoveHand; public event Action? OnPlayerSetActiveHand; - public event Action? OnPlayerHandsAdded; + public event Action>? OnPlayerHandsAdded; public event Action? OnPlayerHandsRemoved; public event Action? OnPlayerItemAdded; public event Action? OnPlayerItemRemoved; @@ -58,67 +54,28 @@ namespace Content.Client.Hands.Systems } #region StateHandling - private void HandleComponentState(EntityUid uid, HandsComponent component, ref ComponentHandleState args) + private void HandleComponentState(Entity ent, ref ComponentHandleState args) { if (args.Current is not HandsComponentState state) return; - var handsModified = component.Hands.Count != state.Hands.Count; - // we need to check that, even if we have the same amount, that the individual hands didn't change. - if (!handsModified) + var newHands = state.Hands.Keys.Except(ent.Comp.Hands.Keys); // hands that were added between states + var oldHands = ent.Comp.Hands.Keys.Except(state.Hands.Keys); // hands that were removed between states + + foreach (var handId in oldHands) { - foreach (var hand in component.Hands.Values) - { - if (state.Hands.Contains(hand)) - continue; - handsModified = true; - break; - } + RemoveHand(ent.AsNullable(), handId); } - var manager = EnsureComp(uid); - - if (handsModified) + foreach (var handId in state.SortedHands.Intersect(newHands)) { - List addedHands = new(); - foreach (var hand in state.Hands) - { - if (component.Hands.ContainsKey(hand.Name)) - continue; - - var container = _containerSystem.EnsureContainer(uid, hand.Name, manager); - var newHand = new Hand(hand.Name, hand.Location, container); - component.Hands.Add(hand.Name, newHand); - addedHands.Add(newHand); - } - - foreach (var name in component.Hands.Keys) - { - if (!state.HandNames.Contains(name)) - { - RemoveHand(uid, name, component); - } - } - - component.SortedHands.Clear(); - component.SortedHands.AddRange(state.HandNames); - var sorted = addedHands.OrderBy(hand => component.SortedHands.IndexOf(hand.Name)); - - foreach (var hand in sorted) - { - AddHand(uid, hand, component); - } + AddHand(ent.AsNullable(), handId, state.Hands[handId]); } + ent.Comp.SortedHands = new (state.SortedHands); - _stripSys.UpdateUi(uid); + SetActiveHand(ent.AsNullable(), state.ActiveHandId); - if (component.ActiveHand == null && state.ActiveHand == null) - return; //edge case - - if (component.ActiveHand != null && state.ActiveHand != component.ActiveHand.Name) - { - SetActiveHand(uid, component.Hands[state.ActiveHand!], component); - } + _stripSys.UpdateUi(ent); } #endregion @@ -129,47 +86,52 @@ namespace Content.Client.Hands.Systems return; } - OnPlayerHandsAdded?.Invoke(hands); + OnPlayerHandsAdded?.Invoke(hands.Value); } - public override void DoDrop(EntityUid uid, Hand hand, bool doDropInteraction = true, HandsComponent? hands = null, bool log = true) + public override void DoDrop(Entity ent, + string handId, + bool doDropInteraction = true, + bool log = true) { - base.DoDrop(uid, hand, doDropInteraction, hands, log); + base.DoDrop(ent, handId, doDropInteraction, log); - if (TryComp(hand.HeldEntity, out SpriteComponent? sprite)) + if (TryGetHeldItem(ent, handId, out var held) && TryComp(held, out SpriteComponent? sprite)) sprite.RenderOrder = EntityManager.CurrentTick.Value; } public EntityUid? GetActiveHandEntity() { - return TryGetPlayerHands(out var hands) ? hands.ActiveHandEntity : null; + return TryGetPlayerHands(out var hands) ? GetActiveItem(hands.Value.AsNullable()) : null; } /// /// Get the hands component of the local player /// - public bool TryGetPlayerHands([NotNullWhen(true)] out HandsComponent? hands) + public bool TryGetPlayerHands([NotNullWhen(true)] out Entity? hands) { var player = _playerManager.LocalEntity; hands = null; - return player != null && TryComp(player.Value, out hands); + if (player == null || !TryComp(player.Value, out var handsComp)) + return false; + + hands = (player.Value, handsComp); + return true; } /// /// Called when a user clicked on their hands GUI /// - public void UIHandClick(HandsComponent hands, string handName) + public void UIHandClick(Entity ent, string handName) { - if (!hands.Hands.TryGetValue(handName, out var pressedHand)) + var hands = ent.Comp; + if (hands.ActiveHandId == null) return; - if (hands.ActiveHand == null) - return; + var pressedEntity = GetHeldItem(ent.AsNullable(), handName); + var activeEntity = GetActiveItem(ent.AsNullable()); - var pressedEntity = pressedHand.HeldEntity; - var activeEntity = hands.ActiveHand.HeldEntity; - - if (pressedHand == hands.ActiveHand && activeEntity != null) + if (handName == hands.ActiveHandId && activeEntity != null) { // use item in hand // it will always be attack_self() in my heart. @@ -177,24 +139,24 @@ namespace Content.Client.Hands.Systems return; } - if (pressedHand != hands.ActiveHand && pressedEntity == null) + if (handName != hands.ActiveHandId && pressedEntity == null) { // change active hand EntityManager.RaisePredictiveEvent(new RequestSetHandEvent(handName)); return; } - if (pressedHand != hands.ActiveHand && pressedEntity != null && activeEntity != null) + if (handName != hands.ActiveHandId && pressedEntity != null && activeEntity != null) { // use active item on held item - EntityManager.RaisePredictiveEvent(new RequestHandInteractUsingEvent(pressedHand.Name)); + EntityManager.RaisePredictiveEvent(new RequestHandInteractUsingEvent(handName)); return; } - if (pressedHand != hands.ActiveHand && pressedEntity != null && activeEntity == null) + if (handName != hands.ActiveHandId && pressedEntity != null && activeEntity == null) { // move the item to the active hand - EntityManager.RaisePredictiveEvent(new RequestMoveHandItemEvent(pressedHand.Name)); + EntityManager.RaisePredictiveEvent(new RequestMoveHandItemEvent(handName)); } } @@ -210,13 +172,12 @@ namespace Content.Client.Hands.Systems public void UIInventoryExamine(string handName) { if (!TryGetPlayerHands(out var hands) || - !hands.Hands.TryGetValue(handName, out var hand) || - hand.HeldEntity is not { Valid: true } entity) + !TryGetHeldItem(hands.Value.AsNullable(), handName, out var heldEntity)) { return; } - _examine.DoExamine(entity); + _examine.DoExamine(heldEntity.Value); } /// @@ -226,13 +187,12 @@ namespace Content.Client.Hands.Systems public void UIHandOpenContextMenu(string handName) { if (!TryGetPlayerHands(out var hands) || - !hands.Hands.TryGetValue(handName, out var hand) || - hand.HeldEntity is not { Valid: true } entity) + !TryGetHeldItem(hands.Value.AsNullable(), handName, out var heldEntity)) { return; } - _ui.GetUIController().OpenVerbMenu(entity); + _ui.GetUIController().OpenVerbMenu(heldEntity.Value); } public void UIHandAltActivateItem(string handName) @@ -246,60 +206,67 @@ namespace Content.Client.Hands.Systems { base.HandleEntityInserted(uid, hands, args); - if (!hands.Hands.TryGetValue(args.Container.ID, out var hand)) + if (!hands.Hands.ContainsKey(args.Container.ID)) return; - UpdateHandVisuals(uid, args.Entity, hand); + + UpdateHandVisuals(uid, args.Entity, args.Container.ID); _stripSys.UpdateUi(uid); if (uid != _playerManager.LocalEntity) return; - OnPlayerItemAdded?.Invoke(hand.Name, args.Entity); + OnPlayerItemAdded?.Invoke(args.Container.ID, args.Entity); if (HasComp(args.Entity)) - OnPlayerHandBlocked?.Invoke(hand.Name); + OnPlayerHandBlocked?.Invoke(args.Container.ID); } protected override void HandleEntityRemoved(EntityUid uid, HandsComponent hands, EntRemovedFromContainerMessage args) { base.HandleEntityRemoved(uid, hands, args); - if (!hands.Hands.TryGetValue(args.Container.ID, out var hand)) + if (!hands.Hands.ContainsKey(args.Container.ID)) return; - UpdateHandVisuals(uid, args.Entity, hand); + + UpdateHandVisuals(uid, args.Entity, args.Container.ID); _stripSys.UpdateUi(uid); if (uid != _playerManager.LocalEntity) return; - OnPlayerItemRemoved?.Invoke(hand.Name, args.Entity); + OnPlayerItemRemoved?.Invoke(args.Container.ID, args.Entity); if (HasComp(args.Entity)) - OnPlayerHandUnblocked?.Invoke(hand.Name); + OnPlayerHandUnblocked?.Invoke(args.Container.ID); } /// /// Update the players sprite with new in-hand visuals. /// - private void UpdateHandVisuals(EntityUid uid, EntityUid held, Hand hand, HandsComponent? handComp = null, SpriteComponent? sprite = null) + private void UpdateHandVisuals(Entity ent, EntityUid held, string handId) { - if (!Resolve(uid, ref handComp, ref sprite, false)) + if (!Resolve(ent, ref ent.Comp1, ref ent.Comp2, false)) + return; + var handComp = ent.Comp1; + var sprite = ent.Comp2; + + if (!TryGetHand((ent, handComp), handId, out var hand)) return; // visual update might involve changes to the entity's effective sprite -> need to update hands GUI. - if (uid == _playerManager.LocalEntity) - OnPlayerItemAdded?.Invoke(hand.Name, held); + if (ent == _playerManager.LocalEntity) + OnPlayerItemAdded?.Invoke(handId, held); if (!handComp.ShowInHands) return; // Remove old layers. We could also just set them to invisible, but as items may add arbitrary layers, this // may eventually bloat the player with lots of layers. - if (handComp.RevealedLayers.TryGetValue(hand.Location, out var revealedLayers)) + if (handComp.RevealedLayers.TryGetValue(hand.Value.Location, out var revealedLayers)) { foreach (var key in revealedLayers) { - _sprite.RemoveLayer((uid, sprite), key); + _sprite.RemoveLayer((ent, sprite), key); } revealedLayers.Clear(); @@ -307,22 +274,22 @@ namespace Content.Client.Hands.Systems else { revealedLayers = new(); - handComp.RevealedLayers[hand.Location] = revealedLayers; + handComp.RevealedLayers[hand.Value.Location] = revealedLayers; } - if (hand.HeldEntity == null) + if (HandIsEmpty((ent, handComp), handId)) { // the held item was removed. - RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true); + RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(ent, revealedLayers), true); return; } - var ev = new GetInhandVisualsEvent(uid, hand.Location); + var ev = new GetInhandVisualsEvent(ent, hand.Value.Location); RaiseLocalEvent(held, ev); if (ev.Layers.Count == 0) { - RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true); + RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(ent, revealedLayers), true); return; } @@ -335,7 +302,7 @@ namespace Content.Client.Hands.Systems continue; } - var index = _sprite.LayerMapReserve((uid, sprite), key); + var index = _sprite.LayerMapReserve((ent, sprite), key); // In case no RSI is given, use the item's base RSI as a default. This cuts down on a lot of unnecessary yaml entries. if (layerData.RsiPath == null @@ -343,35 +310,34 @@ namespace Content.Client.Hands.Systems && sprite[index].Rsi == null) { if (TryComp(held, out var itemComponent) && itemComponent.RsiPath != null) - _sprite.LayerSetRsi((uid, sprite), index, new ResPath(itemComponent.RsiPath)); + _sprite.LayerSetRsi((ent, sprite), index, new ResPath(itemComponent.RsiPath)); else if (TryComp(held, out SpriteComponent? clothingSprite)) - _sprite.LayerSetRsi((uid, sprite), index, clothingSprite.BaseRSI); + _sprite.LayerSetRsi((ent, sprite), index, clothingSprite.BaseRSI); } - _sprite.LayerSetData((uid, sprite), index, layerData); + _sprite.LayerSetData((ent, sprite), index, layerData); // Add displacement maps - var displacement = hand.Location switch + var displacement = hand.Value.Location switch { HandLocation.Left => handComp.LeftHandDisplacement, HandLocation.Right => handComp.RightHandDisplacement, _ => handComp.HandDisplacement }; - if (displacement is not null && _displacement.TryAddDisplacement(displacement, (uid, sprite), index, key, out var displacementKey)) + if (displacement is not null && _displacement.TryAddDisplacement(displacement, (ent, sprite), index, key, out var displacementKey)) revealedLayers.Add(displacementKey); } - RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true); + RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(ent, revealedLayers), true); } private void OnVisualsChanged(EntityUid uid, HandsComponent component, VisualsChangedEvent args) { // update hands visuals if this item is in a hand (rather then inventory or other container). - if (component.Hands.TryGetValue(args.ContainerId, out var hand)) - { - UpdateHandVisuals(uid, GetEntity(args.Item), hand, component); - } + if (!component.Hands.ContainsKey(args.ContainerId)) + return; + UpdateHandVisuals((uid, component), GetEntity(args.Item), args.ContainerId); } #endregion @@ -379,7 +345,7 @@ namespace Content.Client.Hands.Systems private void HandlePlayerAttached(EntityUid uid, HandsComponent component, LocalPlayerAttachedEvent args) { - OnPlayerHandsAdded?.Invoke(component); + OnPlayerHandsAdded?.Invoke((uid, component)); } private void HandlePlayerDetached(EntityUid uid, HandsComponent component, LocalPlayerDetachedEvent args) @@ -390,7 +356,7 @@ namespace Content.Client.Hands.Systems private void OnHandsStartup(EntityUid uid, HandsComponent component, ComponentStartup args) { if (_playerManager.LocalEntity == uid) - OnPlayerHandsAdded?.Invoke(component); + OnPlayerHandsAdded?.Invoke((uid, component)); } private void OnHandsShutdown(EntityUid uid, HandsComponent component, ComponentShutdown args) @@ -400,36 +366,6 @@ namespace Content.Client.Hands.Systems } #endregion - private void AddHand(EntityUid uid, Hand newHand, HandsComponent? handsComp = null) - { - AddHand(uid, newHand.Name, newHand.Location, handsComp); - } - - public override void AddHand(EntityUid uid, string handName, HandLocation handLocation, HandsComponent? handsComp = null) - { - base.AddHand(uid, handName, handLocation, handsComp); - - if (uid == _playerManager.LocalEntity) - OnPlayerAddHand?.Invoke(handName, handLocation); - - if (handsComp == null) - return; - - if (handsComp.ActiveHand == null) - SetActiveHand(uid, handsComp.Hands[handName], handsComp); - } - public override void RemoveHand(EntityUid uid, string handName, HandsComponent? handsComp = null) - { - if (uid == _playerManager.LocalEntity && handsComp != null && - handsComp.Hands.ContainsKey(handName) && uid == - _playerManager.LocalEntity) - { - OnPlayerRemoveHand?.Invoke(handName); - } - - base.RemoveHand(uid, handName, handsComp); - } - private void OnHandActivated(Entity? ent) { if (ent is not { } hand) @@ -438,13 +374,7 @@ namespace Content.Client.Hands.Systems if (_playerManager.LocalEntity != hand.Owner) return; - if (hand.Comp.ActiveHand == null) - { - OnPlayerSetActiveHand?.Invoke(null); - return; - } - - OnPlayerSetActiveHand?.Invoke(hand.Comp.ActiveHand.Name); + OnPlayerSetActiveHand?.Invoke(hand.Comp.ActiveHandId); } } } diff --git a/Content.Client/Inventory/StrippableBoundUserInterface.cs b/Content.Client/Inventory/StrippableBoundUserInterface.cs index a9a937d5d8..381406d2ac 100644 --- a/Content.Client/Inventory/StrippableBoundUserInterface.cs +++ b/Content.Client/Inventory/StrippableBoundUserInterface.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Numerics; using Content.Client.Examine; +using Content.Client.Hands.Systems; using Content.Client.Strip; using Content.Client.Stylesheets; using Content.Client.UserInterface.Controls; @@ -34,6 +35,7 @@ namespace Content.Client.Inventory [Dependency] private readonly IUserInterfaceManager _ui = default!; private readonly ExamineSystem _examine; + private readonly HandsSystem _hands; private readonly InventorySystem _inv; private readonly SharedCuffableSystem _cuffable; private readonly StrippableSystem _strippable; @@ -65,6 +67,7 @@ namespace Content.Client.Inventory public StrippableBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { _examine = EntMan.System(); + _hands = EntMan.System(); _inv = EntMan.System(); _cuffable = EntMan.System(); _strippable = EntMan.System(); @@ -120,28 +123,28 @@ namespace Content.Client.Inventory { // good ol hands shit code. there is a GuiHands comparer that does the same thing... but these are hands // and not gui hands... which are different... - foreach (var hand in handsComp.Hands.Values) + foreach (var (id, hand) in handsComp.Hands) { if (hand.Location != HandLocation.Right) continue; - AddHandButton(hand); + AddHandButton((Owner, handsComp), id, hand); } - foreach (var hand in handsComp.Hands.Values) + foreach (var (id, hand) in handsComp.Hands) { if (hand.Location != HandLocation.Middle) continue; - AddHandButton(hand); + AddHandButton((Owner, handsComp), id, hand); } - foreach (var hand in handsComp.Hands.Values) + foreach (var (id, hand) in handsComp.Hands) { if (hand.Location != HandLocation.Left) continue; - AddHandButton(hand); + AddHandButton((Owner, handsComp), id, hand); } } @@ -177,20 +180,21 @@ namespace Content.Client.Inventory _strippingMenu.SetSize = new Vector2(horizontalMenuSize, verticalMenuSize); } - private void AddHandButton(Hand hand) + private void AddHandButton(Entity ent, string handId, Hand hand) { - var button = new HandButton(hand.Name, hand.Location); + var button = new HandButton(handId, hand.Location); button.Pressed += SlotPressed; - if (EntMan.TryGetComponent(hand.HeldEntity, out var virt)) + var heldEntity = _hands.GetHeldItem(ent.AsNullable(), handId); + if (EntMan.TryGetComponent(heldEntity, out var virt)) { button.Blocked = true; if (EntMan.TryGetComponent(Owner, out var cuff) && _cuffable.GetAllCuffs(cuff).Contains(virt.BlockingEntity)) button.BlockedRect.MouseFilter = MouseFilterMode.Ignore; } - UpdateEntityIcon(button, hand.HeldEntity); + UpdateEntityIcon(button, heldEntity); _strippingMenu!.HandsContainer.AddChild(button); LayoutContainer.SetPosition(button, new Vector2i(_handCount, 0) * (SlotControl.DefaultButtonSize + ButtonSeparation)); _handCount++; diff --git a/Content.Client/RCD/AlignRCDConstruction.cs b/Content.Client/RCD/AlignRCDConstruction.cs index fbaf62c83d..b155458836 100644 --- a/Content.Client/RCD/AlignRCDConstruction.cs +++ b/Content.Client/RCD/AlignRCDConstruction.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Client.Gameplay; +using Content.Client.Hands.Systems; using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.RCD.Components; @@ -17,6 +18,7 @@ public sealed class AlignRCDConstruction : PlacementMode [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; private readonly SharedMapSystem _mapSystem; + private readonly HandsSystem _handsSystem; private readonly RCDSystem _rcdSystem; private readonly SharedTransformSystem _transformSystem; [Dependency] private readonly IPlayerManager _playerManager = default!; @@ -34,6 +36,7 @@ public sealed class AlignRCDConstruction : PlacementMode { IoCManager.InjectDependencies(this); _mapSystem = _entityManager.System(); + _handsSystem = _entityManager.System(); _rcdSystem = _entityManager.System(); _transformSystem = _entityManager.System(); @@ -88,11 +91,9 @@ public sealed class AlignRCDConstruction : PlacementMode } // Determine if player is carrying an RCD in their active hand - if (!_entityManager.TryGetComponent(player, out var hands)) + if (!_handsSystem.TryGetActiveItem(player.Value, out var heldEntity)) return false; - var heldEntity = hands.ActiveHand?.HeldEntity; - if (!_entityManager.TryGetComponent(heldEntity, out var rcd)) return false; diff --git a/Content.Client/RCD/RCDConstructionGhostSystem.cs b/Content.Client/RCD/RCDConstructionGhostSystem.cs index 23dcf6485f..8ec980268b 100644 --- a/Content.Client/RCD/RCDConstructionGhostSystem.cs +++ b/Content.Client/RCD/RCDConstructionGhostSystem.cs @@ -1,3 +1,4 @@ +using Content.Client.Hands.Systems; using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.RCD; @@ -15,6 +16,7 @@ public sealed class RCDConstructionGhostSystem : EntitySystem [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlacementManager _placementManager = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly HandsSystem _hands = default!; private string _placementMode = typeof(AlignRCDConstruction).Name; private Direction _placementDirection = default; @@ -33,12 +35,11 @@ public sealed class RCDConstructionGhostSystem : EntitySystem return; // Determine if player is carrying an RCD in their active hand - var player = _playerManager.LocalSession?.AttachedEntity; - - if (!TryComp(player, out var hands)) + if (_playerManager.LocalSession?.AttachedEntity is not { } player) return; - var heldEntity = hands.ActiveHand?.HeldEntity; + if (!_hands.TryGetActiveItem(player, out var heldEntity)) + return; if (!TryComp(heldEntity, out var rcd)) { diff --git a/Content.Client/SubFloor/TrayScannerSystem.cs b/Content.Client/SubFloor/TrayScannerSystem.cs index 194453ab75..4c67890f6a 100644 --- a/Content.Client/SubFloor/TrayScannerSystem.cs +++ b/Content.Client/SubFloor/TrayScannerSystem.cs @@ -68,11 +68,15 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem foreach (var hand in _hands.EnumerateHands(player.Value)) { - if (!scannerQuery.TryGetComponent(hand.HeldEntity, out var heldScanner) || !heldScanner.Enabled) + if (!_hands.TryGetHeldItem(player.Value, hand, out var heldEntity)) + continue; + + if (!scannerQuery.TryGetComponent(heldEntity, out var heldScanner) || !heldScanner.Enabled) continue; range = MathF.Max(heldScanner.Range, range); canSee = true; + break; } inRange = new HashSet>(); diff --git a/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs b/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs index edc0260061..ecaddd8f98 100644 --- a/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs +++ b/Content.Client/UserInterface/Systems/Hands/HandsUIController.cs @@ -7,6 +7,7 @@ using Content.Shared.Hands.Components; using Content.Shared.Input; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Timing; +using JetBrains.Annotations; using Robust.Client.Player; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controllers; @@ -28,7 +29,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered _handContainerIndices = new(); private readonly Dictionary _handLookup = new(); private HandsComponent? _playerHandsComponent; - private HandButton? _activeHand = null; + private HandButton? _activeHand; // We only have two item status controls (left and right hand), // but we may have more than two hands. @@ -38,7 +39,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered UIManager.GetActiveUIWidgetOrNull(); @@ -48,7 +49,7 @@ public sealed class HandsUIController : UIController, IOnStateEntered entity, string name, HandLocation location) { + if (entity.Owner != _player.LocalEntity) + return; AddHand(name, location); } + private void OnRemoveHand(Entity entity, string name) + { + if (entity.Owner != _player.LocalEntity) + return; + RemoveHand(name); + } + private void HandPressed(GUIBoundKeyEventArgs args, SlotControl hand) { - if (_playerHandsComponent == null) - { + if (!_handsSystem.TryGetPlayerHands(out var hands)) return; - } if (args.Function == EngineKeyFunctions.UIClick) { - _handsSystem.UIHandClick(_playerHandsComponent, hand.SlotName); + _handsSystem.UIHandClick(hands.Value, hand.SlotName); args.Handle(); } else if (args.Function == EngineKeyFunctions.UseSecondary) @@ -122,33 +130,33 @@ public sealed class HandsUIController : UIController, IOnStateEntered handsComp) { DebugTools.Assert(_playerHandsComponent == null); if (HandsGui != null) HandsGui.Visible = true; _playerHandsComponent = handsComp; - foreach (var (name, hand) in handsComp.Hands) + foreach (var (name, hand) in handsComp.Comp.Hands) { var handButton = AddHand(name, hand.Location); - if (_entities.TryGetComponent(hand.HeldEntity, out VirtualItemComponent? virt)) + if (_handsSystem.TryGetHeldItem(handsComp.AsNullable(), name, out var held) && + _entities.TryGetComponent(held, out VirtualItemComponent? virt)) { handButton.SetEntity(virt.BlockingEntity); handButton.Blocked = true; } else { - handButton.SetEntity(hand.HeldEntity); + handButton.SetEntity(held); handButton.Blocked = false; } } - var activeHand = handsComp.ActiveHand; - if (activeHand == null) + if (handsComp.Comp.ActiveHandId == null) return; - SetActiveHand(activeHand.Name); + SetActiveHand(handsComp.Comp.ActiveHandId); } private void HandBlocked(string handName) @@ -260,19 +268,21 @@ public sealed class HandsUIController : UIController, IOnStateEntered(player, out var hands) || - hands.ActiveHandEntity is not { } held || + player == null || + !_handsSystem.TryGetActiveItem(player.Value, out var held) || !_entities.TryGetComponent(held, out SpriteComponent? sprite) || !_inventorySystem.TryGetSlotContainer(player.Value, control.SlotName, out var container, out var slotDef)) { @@ -342,12 +341,12 @@ public sealed class InventoryUIController : UIController, IOnStateEntered(hoverEntity); - var fits = _inventorySystem.CanEquip(player.Value, held, control.SlotName, out _, slotDef) && - _container.CanInsert(held, container); + var fits = _inventorySystem.CanEquip(player.Value, held.Value, control.SlotName, out _, slotDef) && + _container.CanInsert(held.Value, container); if (!fits && _entities.TryGetComponent(container.ContainedEntity, out var storage)) { - fits = _entities.System().CanInsert(container.ContainedEntity.Value, held, out _, storage); + fits = _entities.System().CanInsert(container.ContainedEntity.Value, held.Value, out _, storage); } else if (!fits && _entities.TryGetComponent(container.ContainedEntity, out var itemSlots)) { @@ -357,14 +356,14 @@ public sealed class InventoryUIController : UIController, IOnStateEntered { // Make sure the player's hand starts empty - var heldItem = Hands.ActiveHandEntity; + var heldItem = handsSystem.GetActiveItem((playerUid, Hands)); Assert.That(heldItem, Is.Null, $"Player is holding an item ({SEntMan.ToPrettyString(heldItem)}) at start of test."); // Inspect the action prototype to find the item it spawns @@ -43,14 +43,14 @@ public sealed class RetractableItemActionTest : InteractionTest var actionEnt = actionsSystem.GetAction(actionUid); // Make sure the player's hand is still empty - heldItem = Hands.ActiveHandEntity; + heldItem = handsSystem.GetActiveItem((playerUid, Hands)); Assert.That(heldItem, Is.Null, $"Player is holding an item ({SEntMan.ToPrettyString(heldItem)}) after adding action."); // Activate the arm blade actionsSystem.PerformAction(ToServer(Player), actionEnt!.Value); // Make sure the player is now holding the expected item - heldItem = Hands.ActiveHandEntity; + heldItem = handsSystem.GetActiveItem((playerUid, Hands)); Assert.That(heldItem, Is.Not.Null, $"Expected player to be holding {spawnedProtoId} but was holding nothing."); AssertPrototype(spawnedProtoId, SEntMan.GetNetEntity(heldItem)); @@ -58,7 +58,7 @@ public sealed class RetractableItemActionTest : InteractionTest actionsSystem.PerformAction(ToServer(Player), actionEnt.Value); // Make sure the player's hand is empty again - heldItem = Hands.ActiveHandEntity; + heldItem = handsSystem.GetActiveItem((playerUid, Hands)); Assert.That(heldItem, Is.Null, $"Player is still holding an item ({SEntMan.ToPrettyString(heldItem)}) after second use."); }); } diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs index 1b31fe38c2..d6dec6fe15 100644 --- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs @@ -293,9 +293,9 @@ namespace Content.IntegrationTests.Tests.Buckle Assert.That(buckle.Buckled); // With items in all hands - foreach (var hand in hands.Hands.Values) + foreach (var hand in hands.Hands.Keys) { - Assert.That(hand.HeldEntity, Is.Not.Null); + Assert.That(handsSys.GetHeldItem((human, hands), hand), Is.Not.Null); } var bodySystem = entityManager.System(); @@ -316,9 +316,9 @@ namespace Content.IntegrationTests.Tests.Buckle Assert.That(buckle.Buckled); // Now with no item in any hand - foreach (var hand in hands.Hands.Values) + foreach (var hand in hands.Hands.Keys) { - Assert.That(hand.HeldEntity, Is.Null); + Assert.That(handsSys.GetHeldItem((human, hands), hand), Is.Null); } buckleSystem.Unbuckle(human, human); diff --git a/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs b/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs index 52b7e555a9..4c5860f235 100644 --- a/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs @@ -1,7 +1,6 @@ using Content.Client.Chemistry.UI; using Content.IntegrationTests.Tests.Interaction; using Content.Shared.Chemistry; -using Content.Server.Chemistry.Components; using Content.Shared.Containers.ItemSlots; namespace Content.IntegrationTests.Tests.Chemistry; @@ -19,7 +18,7 @@ public sealed class DispenserTest : InteractionTest // Insert beaker await InteractUsing("Beaker"); - Assert.That(Hands.ActiveHandEntity, Is.Null); + Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null); // Open BUI await Interact(); @@ -29,18 +28,18 @@ public sealed class DispenserTest : InteractionTest await SendBui(ReagentDispenserUiKey.Key, ev); // Beaker is back in the player's hands - Assert.That(Hands.ActiveHandEntity, Is.Not.Null); - AssertPrototype("Beaker", SEntMan.GetNetEntity(Hands.ActiveHandEntity)); + Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Not.Null); + AssertPrototype("Beaker", SEntMan.GetNetEntity(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)))); // Re-insert the beaker await Interact(); - Assert.That(Hands.ActiveHandEntity, Is.Null); + Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null); // Re-eject using the button directly instead of sending a BUI event. This test is really just a test of the // bui/window helper methods. await ClickControl(nameof(ReagentDispenserWindow.EjectButton)); await RunTicks(5); - Assert.That(Hands.ActiveHandEntity, Is.Not.Null); - AssertPrototype("Beaker", SEntMan.GetNetEntity(Hands.ActiveHandEntity)); + Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Not.Null); + AssertPrototype("Beaker", SEntMan.GetNetEntity(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)))); } } diff --git a/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs b/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs index 94dd98425b..b53b87dd5c 100644 --- a/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs +++ b/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs @@ -267,7 +267,7 @@ public sealed class SuicideCommandTests await server.WaitPost(() => { var item = entManager.SpawnEntity("SharpTestObject", transformSystem.GetMapCoordinates(player)); - Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHand!)); + Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHandId!)); entManager.TryGetComponent(item, out var executionComponent); Assert.That(executionComponent, Is.Not.EqualTo(null)); }); @@ -342,7 +342,7 @@ public sealed class SuicideCommandTests await server.WaitPost(() => { var item = entManager.SpawnEntity("MixedDamageTestObject", transformSystem.GetMapCoordinates(player)); - Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHand!)); + Assert.That(handsSystem.TryPickup(player, item, handsComponent.ActiveHandId!)); entManager.TryGetComponent(item, out var executionComponent); Assert.That(executionComponent, Is.Not.EqualTo(null)); }); diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs index 292bf0c55a..b94bda3d95 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs @@ -13,10 +13,10 @@ public sealed class WallConstruction : InteractionTest { await StartConstruction(Wall); await InteractUsing(Steel, 2); - Assert.That(Hands.ActiveHandEntity, Is.Null); + Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null); ClientAssertPrototype(Girder, Target); await InteractUsing(Steel, 2); - Assert.That(Hands.ActiveHandEntity, Is.Null); + Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null); AssertPrototype(WallSolid); } diff --git a/Content.IntegrationTests/Tests/Hands/HandTests.cs b/Content.IntegrationTests/Tests/Hands/HandTests.cs index b88f2dc9eb..d5cf75c463 100644 --- a/Content.IntegrationTests/Tests/Hands/HandTests.cs +++ b/Content.IntegrationTests/Tests/Hands/HandTests.cs @@ -53,20 +53,20 @@ public sealed class HandTests var xform = entMan.GetComponent(player); item = entMan.SpawnEntity("Crowbar", tSys.GetMapCoordinates(player, xform: xform)); hands = entMan.GetComponent(player); - sys.TryPickup(player, item, hands.ActiveHand!); + sys.TryPickup(player, item, hands.ActiveHandId!); }); // run ticks here is important, as errors may happen within the container system's frame update methods. await pair.RunTicksSync(5); - Assert.That(hands.ActiveHandEntity, Is.EqualTo(item)); + Assert.That(sys.GetActiveItem((player, hands)), Is.EqualTo(item)); await server.WaitPost(() => { - sys.TryDrop(player, item, null!); + sys.TryDrop(player, item); }); await pair.RunTicksSync(5); - Assert.That(hands.ActiveHandEntity, Is.Null); + Assert.That(sys.GetActiveItem((player, hands)), Is.Null); await server.WaitPost(() => mapSystem.DeleteMap(data.MapId)); await pair.CleanReturnAsync(); @@ -105,10 +105,10 @@ public sealed class HandTests player = playerMan.Sessions.First().AttachedEntity!.Value; tSys.PlaceNextTo(player, item); hands = entMan.GetComponent(player); - sys.TryPickup(player, item, hands.ActiveHand!); + sys.TryPickup(player, item, hands.ActiveHandId!); }); await pair.RunTicksSync(5); - Assert.That(hands.ActiveHandEntity, Is.EqualTo(item)); + Assert.That(sys.GetActiveItem((player, hands)), Is.EqualTo(item)); // Open then close the box to place the player, who is holding the crowbar, inside of it var storage = server.System(); @@ -125,12 +125,12 @@ public sealed class HandTests // with the item not being in the player's hands await server.WaitPost(() => { - sys.TryDrop(player, item, null!); + sys.TryDrop(player, item); }); await pair.RunTicksSync(5); var xform = entMan.GetComponent(player); var itemXform = entMan.GetComponent(item); - Assert.That(hands.ActiveHandEntity, Is.Not.EqualTo(item)); + Assert.That(sys.GetActiveItem((player, hands)), Is.Not.EqualTo(item)); Assert.That(containerSystem.IsInSameOrNoContainer((player, xform), (item, itemXform))); await server.WaitPost(() => mapSystem.DeleteMap(map.MapId)); diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs index 0f9e9b5ebe..3302b1bafc 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs @@ -120,18 +120,18 @@ public abstract partial class InteractionTest /// protected async Task DeleteHeldEntity() { - if (Hands.ActiveHandEntity is { } held) + if (HandSys.GetActiveItem((ToServer(Player), Hands)) is { } held) { await Server.WaitPost(() => { - Assert.That(HandSys.TryDrop(SEntMan.GetEntity(Player), null, false, true, Hands)); + Assert.That(HandSys.TryDrop((SEntMan.GetEntity(Player), Hands), null, false, true)); SEntMan.DeleteEntity(held); SLogger.Debug($"Deleting held entity"); }); } await RunTicks(1); - Assert.That(Hands.ActiveHandEntity, Is.Null); + Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.Null); } /// @@ -152,7 +152,7 @@ public abstract partial class InteractionTest /// Whether or not to automatically enable any toggleable items protected async Task PlaceInHands(EntitySpecifier entity, bool enableToggleable = true) { - if (Hands.ActiveHand == null) + if (Hands.ActiveHandId == null) { Assert.Fail("No active hand"); return default; @@ -169,7 +169,7 @@ public abstract partial class InteractionTest { var playerEnt = SEntMan.GetEntity(Player); - Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHand, false, false, Hands)); + Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHandId, false, false, false, Hands)); // turn on welders if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated) @@ -179,7 +179,7 @@ public abstract partial class InteractionTest }); await RunTicks(1); - Assert.That(Hands.ActiveHandEntity, Is.EqualTo(item)); + Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.EqualTo(item)); if (enableToggleable && itemToggle != null) Assert.That(itemToggle.Activated); @@ -193,7 +193,7 @@ public abstract partial class InteractionTest { entity ??= Target; - if (Hands.ActiveHand == null) + if (Hands.ActiveHandId == null) { Assert.Fail("No active hand"); return; @@ -212,11 +212,11 @@ public abstract partial class InteractionTest await Server.WaitPost(() => { - Assert.That(HandSys.TryPickup(SEntMan.GetEntity(Player), uid.Value, Hands.ActiveHand, false, false, Hands, item)); + Assert.That(HandSys.TryPickup(ToServer(Player), uid.Value, Hands.ActiveHandId, false, false, false, Hands, item)); }); await RunTicks(1); - Assert.That(Hands.ActiveHandEntity, Is.EqualTo(uid)); + Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.EqualTo(uid)); } /// @@ -224,7 +224,7 @@ public abstract partial class InteractionTest /// protected async Task Drop() { - if (Hands.ActiveHandEntity == null) + if (HandSys.GetActiveItem((ToServer(Player), Hands)) == null) { Assert.Fail("Not holding any entity to drop"); return; @@ -232,11 +232,11 @@ public abstract partial class InteractionTest await Server.WaitPost(() => { - Assert.That(HandSys.TryDrop(SEntMan.GetEntity(Player), handsComp: Hands)); + Assert.That(HandSys.TryDrop((ToServer(Player), Hands))); }); await RunTicks(1); - Assert.That(Hands.ActiveHandEntity, Is.Null); + Assert.That(HandSys.GetActiveItem((ToServer(Player), Hands)), Is.Null); } #region Interact @@ -246,7 +246,7 @@ public abstract partial class InteractionTest /// protected async Task UseInHand() { - if (Hands.ActiveHandEntity is not { } target) + if (HandSys.GetActiveItem((ToServer(Player), Hands)) is not { } target) { Assert.Fail("Not holding any entity"); return; diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index 47a1f748eb..79756ea5b4 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -1,15 +1,12 @@ #nullable enable -using System.Linq; using System.Numerics; using Content.Client.Construction; using Content.Client.Examine; using Content.Client.Gameplay; using Content.IntegrationTests.Pair; -using Content.Server.Body.Systems; using Content.Server.Hands.Systems; using Content.Server.Stack; using Content.Server.Tools; -using Content.Shared.Body.Part; using Content.Shared.DoAfter; using Content.Shared.Hands.Components; using Content.Shared.Interaction; @@ -135,10 +132,13 @@ public abstract partial class InteractionTest - type: entity id: InteractionTestMob components: - - type: Body - prototype: Aghost - type: DoAfter - type: Hands + hands: + hand_right: # only one hand, so that they do not accidentally pick up deconstruction products + location: Right + sortedHands: + - hand_right - type: ComplexInteraction - type: MindContainer - type: Stripping @@ -230,20 +230,6 @@ public abstract partial class InteractionTest SEntMan.DeleteEntity(old.Value); }); - // Ensure that the player only has one hand, so that they do not accidentally pick up deconstruction products - await Server.WaitPost(() => - { - // I lost an hour of my life trying to track down how the hell interaction tests were breaking - // so greatz to this. Just make your own body prototype! - var bodySystem = SEntMan.System(); - var hands = bodySystem.GetBodyChildrenOfType(SEntMan.GetEntity(Player), BodyPartType.Hand).ToArray(); - - for (var i = 1; i < hands.Length; i++) - { - SEntMan.DeleteEntity(hands[i].Id); - } - }); - // Change UI state to in-game. var state = Client.ResolveDependency(); await Client.WaitPost(() => state.RequestStateChange()); diff --git a/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs b/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs index eef420df20..0827e11b70 100644 --- a/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs +++ b/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs @@ -17,7 +17,7 @@ public sealed class TileConstructionTests : InteractionTest await SetTile(null); await InteractUsing(Rod); await AssertTile(Lattice); - Assert.That(Hands.ActiveHandEntity, Is.Null); + Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null); await InteractUsing(Cut); await AssertTile(null); await AssertEntityLookup((Rod, 1)); @@ -49,7 +49,7 @@ public sealed class TileConstructionTests : InteractionTest AssertGridCount(1); // Cut lattice - Assert.That(Hands.ActiveHandEntity, Is.Null); + Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null); await InteractUsing(Cut); await AssertTile(null); AssertGridCount(0); @@ -83,13 +83,13 @@ public sealed class TileConstructionTests : InteractionTest // Lattice -> Plating await InteractUsing(FloorItem); - Assert.That(Hands.ActiveHandEntity, Is.Null); + Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null); await AssertTile(Plating); AssertGridCount(1); // Plating -> Tile await InteractUsing(FloorItem); - Assert.That(Hands.ActiveHandEntity, Is.Null); + Assert.That(HandSys.GetActiveItem((SEntMan.GetEntity(Player), Hands)), Is.Null); await AssertTile(Floor); AssertGridCount(1); diff --git a/Content.Server/Administration/Commands/StripAllCommand.cs b/Content.Server/Administration/Commands/StripAllCommand.cs index ee3e595595..90fcde136d 100644 --- a/Content.Server/Administration/Commands/StripAllCommand.cs +++ b/Content.Server/Administration/Commands/StripAllCommand.cs @@ -42,13 +42,12 @@ public sealed class StripAllCommand : LocalizedEntityCommands if (EntityManager.TryGetComponent(targetEntity, out var hands)) { - foreach (var hand in _handsSystem.EnumerateHands(targetEntity.Value, hands)) + foreach (var hand in _handsSystem.EnumerateHands((targetEntity.Value, hands))) { - _handsSystem.TryDrop(targetEntity.Value, + _handsSystem.TryDrop((targetEntity.Value, hands), hand, checkActionBlocker: false, - doDropInteraction: false, - handsComp: hands); + doDropInteraction: false); } } } diff --git a/Content.Server/Administration/Systems/AdminSystem.cs b/Content.Server/Administration/Systems/AdminSystem.cs index b9916bc5b2..0e5138ba96 100644 --- a/Content.Server/Administration/Systems/AdminSystem.cs +++ b/Content.Server/Administration/Systems/AdminSystem.cs @@ -433,9 +433,9 @@ public sealed class AdminSystem : EntitySystem if (TryComp(entity, out HandsComponent? hands)) { - foreach (var hand in _hands.EnumerateHands(entity, hands)) + foreach (var hand in _hands.EnumerateHands((entity, hands))) { - _hands.TryDrop(entity, hand, checkActionBlocker: false, doDropInteraction: false, handsComp: hands); + _hands.TryDrop((entity, hands), hand, checkActionBlocker: false, doDropInteraction: false); } } diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs index ccc7a98a1e..22a1ccadc9 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs @@ -820,7 +820,7 @@ public sealed partial class AdminVerbSystem } else if (TryComp(target, out var hands)) { - foreach (var held in _handsSystem.EnumerateHeld(target, hands)) + foreach (var held in _handsSystem.EnumerateHeld((target, hands))) { if (HasComp(held)) { diff --git a/Content.Server/Bed/Cryostorage/CryostorageSystem.cs b/Content.Server/Bed/Cryostorage/CryostorageSystem.cs index 21f43c3195..7cadc3bb8f 100644 --- a/Content.Server/Bed/Cryostorage/CryostorageSystem.cs +++ b/Content.Server/Bed/Cryostorage/CryostorageSystem.cs @@ -97,8 +97,7 @@ public sealed class CryostorageSystem : SharedCryostorageSystem EntityUid? entity = null; if (args.Type == CryostorageRemoveItemBuiMessage.RemovalType.Hand) { - if (_hands.TryGetHand(cryoContained, args.Key, out var hand)) - entity = hand.HeldEntity; + entity = _hands.GetHeldItem(cryoContained, args.Key); } else { @@ -320,10 +319,10 @@ public sealed class CryostorageSystem : SharedCryostorageSystem foreach (var hand in _hands.EnumerateHands(uid)) { - if (hand.HeldEntity == null) + if (!_hands.TryGetHeldItem(uid, hand, out var heldEntity)) continue; - data.HeldItems.Add(hand.Name, Name(hand.HeldEntity.Value)); + data.HeldItems.Add(hand, Name(heldEntity.Value)); } return data; diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 0686f32097..d8381b2a79 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Botany.Components; +using Content.Server.Hands.Systems; using Content.Server.Kitchen.Components; using Content.Server.Popups; using Content.Shared.Chemistry.EntitySystems; @@ -37,6 +38,7 @@ public sealed class PlantHolderSystem : EntitySystem [Dependency] private readonly MutationSystem _mutation = default!; [Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; @@ -45,7 +47,7 @@ public sealed class PlantHolderSystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - + public const float HydroponicsSpeedMultiplier = 1f; public const float HydroponicsConsumptionMultiplier = 2f; @@ -706,9 +708,9 @@ public sealed class PlantHolderSystem : EntitySystem if (component.Harvest && !component.Dead) { - if (TryComp(user, out var hands)) + if (_hands.TryGetActiveItem(user, out var activeItem)) { - if (!_botany.CanHarvest(component.Seed, hands.ActiveHandEntity)) + if (!_botany.CanHarvest(component.Seed, activeItem)) { _popup.PopupCursor(Loc.GetString("plant-holder-component-ligneous-cant-harvest-message"), user); return false; diff --git a/Content.Server/Chat/SuicideSystem.cs b/Content.Server/Chat/SuicideSystem.cs index b9a3ede2bd..8727429fc9 100644 --- a/Content.Server/Chat/SuicideSystem.cs +++ b/Content.Server/Chat/SuicideSystem.cs @@ -1,9 +1,9 @@ using Content.Server.Ghost; +using Content.Server.Hands.Systems; using Content.Shared.Administration.Logs; using Content.Shared.Chat; using Content.Shared.Damage; using Content.Shared.Database; -using Content.Shared.Hands.Components; using Content.Shared.IdentityManagement; using Content.Shared.Interaction.Events; using Content.Shared.Item; @@ -22,6 +22,7 @@ public sealed class SuicideSystem : EntitySystem { [Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; @@ -116,10 +117,9 @@ public sealed class SuicideSystem : EntitySystem var suicideByEnvironmentEvent = new SuicideByEnvironmentEvent(victim); // Try to suicide by raising an event on the held item - if (EntityManager.TryGetComponent(victim, out HandsComponent? handsComponent) - && handsComponent.ActiveHandEntity is { } item) + if (_hands.TryGetActiveItem(victim.Owner, out var item)) { - RaiseLocalEvent(item, suicideByEnvironmentEvent); + RaiseLocalEvent(item.Value, suicideByEnvironmentEvent); if (suicideByEnvironmentEvent.Handled) { args.Handled = suicideByEnvironmentEvent.Handled; diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs index e489cdf100..767399335a 100644 --- a/Content.Server/Construction/ConstructionSystem.Initial.cs +++ b/Content.Server/Construction/ConstructionSystem.Initial.cs @@ -471,7 +471,7 @@ namespace Content.Server.Construction } if (!_actionBlocker.CanInteract(user, null) - || !EntityManager.TryGetComponent(user, out HandsComponent? hands) || hands.ActiveHandEntity == null) + || !EntityManager.TryGetComponent(user, out HandsComponent? hands) || _handsSystem.GetActiveItem((user, hands)) == null) { Cleanup(); return; @@ -496,7 +496,7 @@ namespace Content.Server.Construction var valid = false; - if (hands.ActiveHandEntity is not {Valid: true} holding) + if (_handsSystem.GetActiveItem((user, hands)) is not {Valid: true} holding) { Cleanup(); return; diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index 357046be56..1ccb80b32e 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -1,5 +1,4 @@ using System.Numerics; -using Content.Server.Inventory; using Content.Server.Stack; using Content.Server.Stunnable; using Content.Shared.ActionBlocker; @@ -10,9 +9,7 @@ using Content.Shared.Explosion; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Input; -using Content.Shared.Inventory.VirtualItem; using Content.Shared.Movement.Pulling.Components; -using Content.Shared.Movement.Pulling.Events; using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Stacks; using Content.Shared.Standing; @@ -24,7 +21,6 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Timing; -using Robust.Shared.Utility; namespace Content.Server.Hands.Systems { @@ -87,10 +83,9 @@ namespace Content.Server.Hands.Systems if (ent.Comp.DisableExplosionRecursion) return; - foreach (var hand in ent.Comp.Hands.Values) + foreach (var held in EnumerateHeld(ent.AsNullable())) { - if (hand.HeldEntity is { } uid) - args.Contents.Add(uid); + args.Contents.Add(held); } } @@ -112,7 +107,7 @@ namespace Content.Server.Hands.Systems args.Handled = true; // no shove/stun. } - private void HandleBodyPartAdded(EntityUid uid, HandsComponent component, ref BodyPartAddedEvent args) + private void HandleBodyPartAdded(Entity ent, ref BodyPartAddedEvent args) { if (args.Part.Comp.PartType != BodyPartType.Hand) return; @@ -127,7 +122,7 @@ namespace Content.Server.Hands.Systems _ => throw new ArgumentOutOfRangeException(nameof(args.Part.Comp.Symmetry)) }; - AddHand(uid, args.Slot, location); + AddHand(ent.AsNullable(), args.Slot, location); } private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref BodyPartRemovedEvent args) @@ -155,8 +150,8 @@ namespace Content.Server.Hands.Systems { if (ContainerSystem.IsEntityInContainer(player) || !TryComp(player, out HandsComponent? hands) || - hands.ActiveHandEntity is not { } throwEnt || - !_actionBlockerSystem.CanThrow(player, throwEnt)) + !TryGetActiveItem((player, hands), out var throwEnt) || + !_actionBlockerSystem.CanThrow(player, throwEnt.Value)) return false; if (_timing.CurTime < hands.NextThrowTime) @@ -165,7 +160,7 @@ namespace Content.Server.Hands.Systems if (EntityManager.TryGetComponent(throwEnt, out StackComponent? stack) && stack.Count > 1 && stack.ThrowIndividually) { - var splitStack = _stackSystem.Split(throwEnt, 1, EntityManager.GetComponent(player).Coordinates, stack); + var splitStack = _stackSystem.Split(throwEnt.Value, 1, EntityManager.GetComponent(player).Coordinates, stack); if (splitStack is not {Valid: true}) return false; @@ -185,14 +180,14 @@ namespace Content.Server.Hands.Systems // Let other systems change the thrown entity (useful for virtual items) // or the throw strength. - var ev = new BeforeThrowEvent(throwEnt, direction, throwSpeed, player); + var ev = new BeforeThrowEvent(throwEnt.Value, direction, throwSpeed, player); RaiseLocalEvent(player, ref ev); if (ev.Cancelled) return true; // This can grief the above event so we raise it afterwards - if (IsHolding(player, throwEnt, out _, hands) && !TryDrop(player, throwEnt, handsComp: hands)) + if (IsHolding((player, hands), throwEnt, out _) && !TryDrop(player, throwEnt.Value)) return false; _throwingSystem.TryThrow(ev.ItemUid, ev.Direction, ev.ThrowSpeed, ev.PlayerUid, compensateFriction: !HasComp(ev.ItemUid)); @@ -207,20 +202,20 @@ namespace Content.Server.Hands.Systems var spreadMaxAngle = Angle.FromDegrees(DropHeldItemsSpread); var fellEvent = new FellDownEvent(entity); - RaiseLocalEvent(entity, fellEvent, false); + RaiseLocalEvent(entity, fellEvent); - foreach (var hand in entity.Comp.Hands.Values) + foreach (var hand in entity.Comp.Hands.Keys) { - if (hand.HeldEntity is not EntityUid held) + if (!TryGetHeldItem(entity.AsNullable(), hand, out var heldEntity)) continue; var throwAttempt = new FellDownThrowAttemptEvent(entity); - RaiseLocalEvent(hand.HeldEntity.Value, ref throwAttempt); + RaiseLocalEvent(heldEntity.Value, ref throwAttempt); if (throwAttempt.Cancelled) continue; - if (!TryDrop(entity, hand, null, checkActionBlocker: false, handsComp: entity.Comp)) + if (!TryDrop(entity.AsNullable(), hand, checkActionBlocker: false)) continue; // Rotate the item's throw vector a bit for each item @@ -231,12 +226,12 @@ namespace Content.Server.Hands.Systems itemVelocity *= _random.NextFloat(1f); // Heavier objects don't get thrown as far // If the item doesn't have a physics component, it isn't going to get thrown anyway, but we'll assume infinite mass - itemVelocity *= _physicsQuery.TryComp(held, out var heldPhysics) ? heldPhysics.InvMass : 0; + itemVelocity *= _physicsQuery.TryComp(heldEntity, out var heldPhysics) ? heldPhysics.InvMass : 0; // Throw at half the holder's intentional throw speed and // vary the speed a little to make it look more interesting var throwSpeed = entity.Comp.BaseThrowspeed * _random.NextFloat(0.45f, 0.55f); - _throwingSystem.TryThrow(held, + _throwingSystem.TryThrow(heldEntity.Value, itemVelocity, throwSpeed, entity, diff --git a/Content.Server/HotPotato/HotPotatoSystem.cs b/Content.Server/HotPotato/HotPotatoSystem.cs index 115a7b6cb7..8ca33fb8cd 100644 --- a/Content.Server/HotPotato/HotPotatoSystem.cs +++ b/Content.Server/HotPotato/HotPotatoSystem.cs @@ -43,7 +43,7 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem if (!TryComp(hitEntity, out var hands)) continue; - if (!_hands.IsHolding(hitEntity, uid, out _, hands) && _hands.TryForcePickupAnyHand(hitEntity, uid, handsComp: hands)) + if (!_hands.IsHolding((hitEntity, hands), uid, out _) && _hands.TryForcePickupAnyHand(hitEntity, uid, handsComp: hands)) { _popup.PopupEntity(Loc.GetString("hot-potato-passed", ("from", args.User), ("to", hitEntity)), uid, PopupType.Medium); diff --git a/Content.Server/NPC/HTN/Preconditions/ActiveHandComponentPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/ActiveHandComponentPrecondition.cs index e2e65684ff..93f7679766 100644 --- a/Content.Server/NPC/HTN/Preconditions/ActiveHandComponentPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/ActiveHandComponentPrecondition.cs @@ -1,4 +1,4 @@ -using Content.Shared.Hands.Components; +using Content.Server.Hands.Systems; using Robust.Shared.Prototypes; namespace Content.Server.NPC.HTN.Preconditions; @@ -18,14 +18,18 @@ public sealed partial class ActiveHandComponentPrecondition : HTNPrecondition public override bool IsMet(NPCBlackboard blackboard) { - if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var hand, _entManager) || hand.HeldEntity == null) + if (!blackboard.TryGetValue(NPCBlackboard.Owner, out var owner, _entManager) || + !blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var hand, _entManager)) { return Invert; } + if (!_entManager.System().TryGetHeldItem(owner, hand, out var entity)) + return Invert; + foreach (var comp in Components) { - var hasComp = _entManager.HasComponent(hand.HeldEntity, comp.Value.Component.GetType()); + var hasComp = _entManager.HasComponent(entity, comp.Value.Component.GetType()); if (!hasComp || Invert && hasComp) diff --git a/Content.Server/NPC/HTN/Preconditions/ActiveHandEntityPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/ActiveHandEntityPrecondition.cs index 0e6c63a433..049d81130a 100644 --- a/Content.Server/NPC/HTN/Preconditions/ActiveHandEntityPrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/ActiveHandEntityPrecondition.cs @@ -1,4 +1,4 @@ -using Content.Shared.Hands.Components; +using Content.Server.Hands.Systems; namespace Content.Server.NPC.HTN.Preconditions; @@ -11,11 +11,12 @@ public sealed partial class ActiveHandEntityPrecondition : HTNPrecondition public override bool IsMet(NPCBlackboard blackboard) { - if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out Hand? activeHand, _entManager)) + if (!blackboard.TryGetValue(NPCBlackboard.Owner, out EntityUid owner, _entManager) || + !blackboard.TryGetValue(NPCBlackboard.ActiveHand, out string? activeHand, _entManager)) { return false; } - return activeHand.HeldEntity != null; + return !_entManager.System().HandIsEmpty(owner, activeHand); } } diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Interactions/DropOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Interactions/DropOperator.cs index 0ea062a910..89d68283b6 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Interactions/DropOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Interactions/DropOperator.cs @@ -12,7 +12,7 @@ public sealed partial class DropOperator : HTNOperator public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) { - if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out Hand? activeHand, _entManager)) + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out string? activeHand, _entManager)) { return HTNOperatorStatus.Finished; } diff --git a/Content.Server/NPC/NPCBlackboard.cs b/Content.Server/NPC/NPCBlackboard.cs index ffdb55045d..8b70569177 100644 --- a/Content.Server/NPC/NPCBlackboard.cs +++ b/Content.Server/NPC/NPCBlackboard.cs @@ -1,11 +1,10 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; -using Content.Server.Interaction; +using Content.Server.Hands.Systems; using Content.Shared.Access.Systems; using Content.Shared.ActionBlocker; using Content.Shared.Hands.Components; using Content.Shared.Interaction; -using Content.Shared.Inventory; using JetBrains.Annotations; using Robust.Shared.Utility; @@ -152,6 +151,8 @@ public sealed partial class NPCBlackboard : IEnumerable(); + switch (key) { case Access: @@ -168,25 +169,24 @@ public sealed partial class NPCBlackboard : IEnumerable(owner, out var hands) || - hands.ActiveHand == null) + handSys.GetActiveHand(owner) is not { } activeHand) { return false; } - value = hands.ActiveHand; + value = activeHand; return true; } case ActiveHandFree: { if (!TryGetValue(Owner, out owner, entManager) || !entManager.TryGetComponent(owner, out var hands) || - hands.ActiveHand == null) + handSys.GetActiveHand(owner) is not { } activeHand) { return false; } - value = hands.ActiveHand.IsEmpty; + value = handSys.HandIsEmpty((owner, hands), activeHand); return true; } case CanMove: @@ -204,16 +204,16 @@ public sealed partial class NPCBlackboard : IEnumerable(owner, out var hands) || - hands.ActiveHand == null) + handSys.GetActiveHand(owner) is null) { return false; } var handos = new List(); - foreach (var (id, hand) in hands.Hands) + foreach (var id in hands.Hands.Keys) { - if (!hand.IsEmpty) + if (!handSys.HandIsEmpty((owner, hands), id)) continue; handos.Add(id); @@ -226,16 +226,16 @@ public sealed partial class NPCBlackboard : IEnumerable(owner, out var hands) || - hands.ActiveHand == null) + handSys.GetActiveHand(owner) is null) { return false; } var handos = new List(); - foreach (var (id, hand) in hands.Hands) + foreach (var id in hands.Hands.Keys) { - if (!hand.IsEmpty) + if (!handSys.HandIsEmpty((owner, hands), id)) continue; handos.Add(id); diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index c5b463d0d3..489ac6de55 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -1,5 +1,6 @@ using Content.Server.Atmos.Components; using Content.Server.Fluids.EntitySystems; +using Content.Server.Hands.Systems; using Content.Server.NPC.Queries; using Content.Server.NPC.Queries.Considerations; using Content.Server.NPC.Queries.Curves; @@ -44,6 +45,7 @@ public sealed class NPCUtilitySystem : EntitySystem [Dependency] private readonly DrinkSystem _drink = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly FoodSystem _food = default!; + [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; @@ -256,8 +258,9 @@ public sealed class NPCUtilitySystem : EntitySystem } case TargetAmmoMatchesCon: { - if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out Hand? activeHand, EntityManager) || - !TryComp(activeHand.HeldEntity, out var heldGun)) + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out string? activeHand, EntityManager) || + !_hands.TryGetHeldItem(owner, activeHand, out var heldEntity) || + !TryComp(heldEntity, out var heldGun)) { return 0f; } diff --git a/Content.Server/Shuttles/Commands/FTLDiskCommand.cs b/Content.Server/Shuttles/Commands/FTLDiskCommand.cs index 014dbe6d99..63d45e2364 100644 --- a/Content.Server/Shuttles/Commands/FTLDiskCommand.cs +++ b/Content.Server/Shuttles/Commands/FTLDiskCommand.cs @@ -152,7 +152,7 @@ public sealed class FTLDiskCommand : LocalizedCommands if (_entManager.TryGetComponent(cdCaseUid, out var storage) && storageSystem.Insert(cdCaseUid, cdUid, out _, storageComp: storage, playSound: false)) { - if (_entManager.TryGetComponent(entity, out var handsComponent) && handsSystem.TryGetEmptyHand(entity, out var emptyHand, handsComponent)) + if (_entManager.TryGetComponent(entity, out var handsComponent) && handsSystem.TryGetEmptyHand((entity, handsComponent), out var emptyHand)) { handsSystem.TryPickup(entity, cdCaseUid, emptyHand, checkActionBlocker: false, handsComp: handsComponent); } @@ -161,7 +161,7 @@ public sealed class FTLDiskCommand : LocalizedCommands { _entManager.DeleteEntity(cdCaseUid); // something went wrong so just yeet the chaf - if (_entManager.TryGetComponent(entity, out var handsComponent) && handsSystem.TryGetEmptyHand(entity, out var emptyHand, handsComponent)) + if (_entManager.TryGetComponent(entity, out var handsComponent) && handsSystem.TryGetEmptyHand((entity, handsComponent), out var emptyHand)) { handsSystem.TryPickup(entity, cdUid, emptyHand, checkActionBlocker: false, handsComp: handsComponent); } diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs index 784c2f8fbb..1b6040773c 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs @@ -217,8 +217,8 @@ public sealed partial class BorgSystem var handId = $"{uid}-item{component.HandCounter}"; component.HandCounter++; - _hands.AddHand(chassis, handId, HandLocation.Middle, hands); - _hands.DoPickup(chassis, hands.Hands[handId], item, hands); + _hands.AddHand((chassis, hands), handId, HandLocation.Middle); + _hands.DoPickup(chassis, handId, item, hands); EnsureComp(item); component.ProvidedItems.Add(handId, item); } @@ -239,7 +239,7 @@ public sealed partial class BorgSystem foreach (var (hand, item) in component.ProvidedItems) { QueueDel(item); - _hands.RemoveHand(chassis, hand, hands); + _hands.RemoveHand(chassis, hand); } component.ProvidedItems.Clear(); return; @@ -252,7 +252,7 @@ public sealed partial class BorgSystem RemComp(item); _container.Insert(item, component.ProvidedContainer); } - _hands.RemoveHand(chassis, handId, hands); + _hands.RemoveHand(chassis, handId); } component.ProvidedItems.Clear(); } diff --git a/Content.Server/Tabletop/TabletopSystem.cs b/Content.Server/Tabletop/TabletopSystem.cs index 6938372233..f3f5801b2d 100644 --- a/Content.Server/Tabletop/TabletopSystem.cs +++ b/Content.Server/Tabletop/TabletopSystem.cs @@ -1,3 +1,4 @@ +using Content.Server.Hands.Systems; using Content.Server.Popups; using Content.Server.Tabletop.Components; using Content.Shared.CCVar; @@ -22,6 +23,7 @@ namespace Content.Server.Tabletop { [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly EyeSystem _eye = default!; + [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly ViewSubscriberSystem _viewSubscriberSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; @@ -84,18 +86,13 @@ namespace Content.Server.Tabletop if (component.Session is not { } session) return; - if (hands.ActiveHand == null) + if (!_hands.TryGetActiveItem(uid, out var handEnt)) return; - if (hands.ActiveHand.HeldEntity == null) - return; - - var handEnt = hands.ActiveHand.HeldEntity.Value; - if (!TryComp(handEnt, out var item)) return; - var meta = MetaData(handEnt); + var meta = MetaData(handEnt.Value); var protoId = meta.EntityPrototype?.ID; var hologram = Spawn(protoId, session.Position.Offset(-1, 0)); diff --git a/Content.Server/Tools/Innate/InnateToolSystem.cs b/Content.Server/Tools/Innate/InnateToolSystem.cs index b8d1dd935c..6afe886303 100644 --- a/Content.Server/Tools/Innate/InnateToolSystem.cs +++ b/Content.Server/Tools/Innate/InnateToolSystem.cs @@ -90,9 +90,9 @@ public sealed class InnateToolSystem : EntitySystem if (TryComp(uid, out var hands)) { - foreach (var hand in hands.Hands) + foreach (var hand in hands.Hands.Keys) { - _sharedHandsSystem.TryDrop(uid, hand.Value, checkActionBlocker: false, handsComp: hands); + _sharedHandsSystem.TryDrop((uid, hands), hand, checkActionBlocker: false); } } } diff --git a/Content.Server/Verbs/VerbSystem.cs b/Content.Server/Verbs/VerbSystem.cs index a0ca0a80df..7a1e5facd0 100644 --- a/Content.Server/Verbs/VerbSystem.cs +++ b/Content.Server/Verbs/VerbSystem.cs @@ -1,10 +1,10 @@ using System.Linq; using Content.Server.Administration.Managers; +using Content.Server.Hands.Systems; using Content.Server.Popups; using Content.Shared.Administration; using Content.Shared.Administration.Logs; using Content.Shared.Database; -using Content.Shared.Hands.Components; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Verbs; using Robust.Shared.Utility; @@ -14,6 +14,7 @@ namespace Content.Server.Verbs public sealed class VerbSystem : SharedVerbSystem { [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly IAdminManager _adminMgr = default!; @@ -91,8 +92,7 @@ namespace Content.Server.Verbs { // first get the held item. again. EntityUid? holding = null; - if (TryComp(user, out HandsComponent? hands) && - hands.ActiveHandEntity is EntityUid heldEntity) + if (_hands.GetActiveItem(user) is { } heldEntity) { holding = heldEntity; } diff --git a/Content.Server/VoiceTrigger/StorageVoiceControlSystem.cs b/Content.Server/VoiceTrigger/StorageVoiceControlSystem.cs index c3fde14517..1755c6df08 100644 --- a/Content.Server/VoiceTrigger/StorageVoiceControlSystem.cs +++ b/Content.Server/VoiceTrigger/StorageVoiceControlSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Hands.Systems; using Content.Server.Storage.EntitySystems; using Content.Shared.Administration.Logs; using Content.Shared.Database; -using Content.Shared.Hands.Components; using Content.Shared.Inventory; using Content.Shared.Popups; using Content.Shared.Storage; @@ -40,24 +39,20 @@ public sealed class StorageVoiceControlSystem : EntitySystem if (!TryComp(ent, out var storage)) return; - // Get the hands component - if (!TryComp(args.Source, out var hands)) - return; - // If the player has something in their hands, try to insert it into the storage - if (hands.ActiveHand != null && hands.ActiveHand.HeldEntity.HasValue) + if (_hands.TryGetActiveItem(ent.Owner, out var activeItem)) { // Disallow insertion and provide a reason why if the person decides to insert the item into itself - if (ent.Owner.Equals(hands.ActiveHand.HeldEntity.Value)) + if (ent.Owner.Equals(activeItem.Value)) { - _popup.PopupEntity(Loc.GetString("comp-storagevoicecontrol-self-insert", ("entity", hands.ActiveHand.HeldEntity.Value)), ent, args.Source); + _popup.PopupEntity(Loc.GetString("comp-storagevoicecontrol-self-insert", ("entity", activeItem.Value)), ent, args.Source); return; } - if (_storage.CanInsert(ent, hands.ActiveHand.HeldEntity.Value, out var failedReason)) + if (_storage.CanInsert(ent, activeItem.Value, out var failedReason)) { // We adminlog before insertion, otherwise the logger will attempt to pull info on an entity that no longer is present and throw an exception - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Source)} inserted {ToPrettyString(hands.ActiveHand.HeldEntity.Value)} into {ToPrettyString(ent)} via voice control"); - _storage.Insert(ent, hands.ActiveHand.HeldEntity.Value, out _); + _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Source)} inserted {ToPrettyString(activeItem.Value)} into {ToPrettyString(ent)} via voice control"); + _storage.Insert(ent, activeItem.Value, out _); return; } { @@ -67,7 +62,7 @@ public sealed class StorageVoiceControlSystem : EntitySystem _popup.PopupEntity(Loc.GetString(failedReason), ent, args.Source); _adminLogger.Add(LogType.Action, LogImpact.Low, - $"{ToPrettyString(args.Source)} failed to insert {ToPrettyString(hands.ActiveHand.HeldEntity.Value)} into {ToPrettyString(ent)} via voice control"); + $"{ToPrettyString(args.Source)} failed to insert {ToPrettyString(activeItem.Value)} into {ToPrettyString(ent)} via voice control"); } return; } @@ -80,7 +75,7 @@ public sealed class StorageVoiceControlSystem : EntitySystem // E.g "go go s" would give you the screwdriver because "screwdriver" contains "s" if (Name(item).Contains(args.MessageWithoutPhrase)) { - ExtractItemFromStorage(ent, item, args.Source, hands); + ExtractItemFromStorage(ent, item, args.Source); break; } } @@ -92,16 +87,14 @@ public sealed class StorageVoiceControlSystem : EntitySystem /// The entity with the /// The entity to be extracted from the attached storage /// The entity wearing the item - /// The of the person wearing the item private void ExtractItemFromStorage(Entity ent, EntityUid item, - EntityUid source, - HandsComponent hands) + EntityUid source) { _container.RemoveEntity(ent, item); _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(source)} retrieved {ToPrettyString(item)} from {ToPrettyString(ent)} via voice control"); - _hands.TryPickup(source, item, handsComp: hands); + _hands.TryPickup(source, item); } } diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index 7f382e15bb..e6a50425b4 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading; using Content.Server.Construction; using Content.Server.Construction.Components; +using Content.Server.Hands.Systems; using Content.Server.Power.Components; using Content.Shared.DoAfter; using Content.Shared.GameTicking; @@ -24,6 +25,7 @@ public sealed class WiresSystem : SharedWiresSystem { [Dependency] private readonly IPrototypeManager _protoMan = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; @@ -405,19 +407,13 @@ public sealed class WiresSystem : SharedWiresSystem return; } - var activeHand = handsComponent.ActiveHand; - - if (activeHand == null) + if (!_hands.TryGetActiveItem((player, handsComponent), out var heldEntity)) return; - if (activeHand.HeldEntity == null) + if (!EntityManager.TryGetComponent(heldEntity, out ToolComponent? tool)) return; - var activeHandEntity = activeHand.HeldEntity.Value; - if (!EntityManager.TryGetComponent(activeHandEntity, out ToolComponent? tool)) - return; - - TryDoWireAction(uid, player, activeHandEntity, args.Id, args.Action, component, tool); + TryDoWireAction(uid, player, heldEntity.Value, args.Id, args.Action, component, tool); } private void OnDoAfter(EntityUid uid, WiresComponent component, WireDoAfterEvent args) diff --git a/Content.Shared/Access/Systems/SharedIdCardSystem.cs b/Content.Shared/Access/Systems/SharedIdCardSystem.cs index dd603cbb02..513abb20ee 100644 --- a/Content.Shared/Access/Systems/SharedIdCardSystem.cs +++ b/Content.Shared/Access/Systems/SharedIdCardSystem.cs @@ -3,7 +3,7 @@ using Content.Shared.Access.Components; using Content.Shared.Administration.Logs; using Content.Shared.CCVar; using Content.Shared.Database; -using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; using Content.Shared.Inventory; using Content.Shared.PDA; @@ -21,6 +21,7 @@ public abstract class SharedIdCardSystem : EntitySystem [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedAccessSystem _access = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly MetaDataSystem _metaSystem = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -83,8 +84,7 @@ public abstract class SharedIdCardSystem : EntitySystem public bool TryFindIdCard(EntityUid uid, out Entity idCard) { // check held item? - if (TryComp(uid, out HandsComponent? hands) && - hands.ActiveHandEntity is EntityUid heldItem && + if (_hands.GetActiveItem(uid) is { } heldItem && TryGetIdCard(heldItem, out idCard)) { return true; diff --git a/Content.Shared/Blocking/BlockingSystem.cs b/Content.Shared/Blocking/BlockingSystem.cs index 619adf7918..6223f8e840 100644 --- a/Content.Shared/Blocking/BlockingSystem.cs +++ b/Content.Shared/Blocking/BlockingSystem.cs @@ -97,7 +97,7 @@ public sealed partial class BlockingSystem : EntitySystem if (!handQuery.TryGetComponent(args.Performer, out var hands)) return; - var shields = _handsSystem.EnumerateHeld(args.Performer, hands).ToArray(); + var shields = _handsSystem.EnumerateHeld((args.Performer, hands)).ToArray(); foreach (var shield in shields) { @@ -277,7 +277,7 @@ public sealed partial class BlockingSystem : EntitySystem if (!handQuery.TryGetComponent(user, out var hands)) return; - var shields = _handsSystem.EnumerateHeld(user, hands).ToArray(); + var shields = _handsSystem.EnumerateHeld((user, hands)).ToArray(); foreach (var shield in shields) { diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs index f536beef2b..e314db0ddb 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs @@ -947,12 +947,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem if (!entity.Comp.HeldOnly) return true; - if (TryComp(examiner, out HandsComponent? handsComp)) - { - return Hands.IsHolding(examiner, entity, out _, handsComp); - } - - return true; + return Hands.IsHolding(examiner, entity, out _); } private void OnMapInit(Entity entity, ref MapInitEvent args) diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index 3b5a880d46..4f5f567e43 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -255,7 +255,7 @@ namespace Content.Shared.Containers.ItemSlots } // Drop the held item onto the floor. Return if the user cannot drop. - if (!_handsSystem.TryDrop(args.User, args.Used, handsComp: hands)) + if (!_handsSystem.TryDrop(args.User, args.Used)) return; slots.Sort(SortEmpty); @@ -395,17 +395,17 @@ namespace Content.Shared.Containers.ItemSlots if (!Resolve(user, ref hands, false)) return false; - if (hands.ActiveHand?.HeldEntity is not { } held) + if (!_handsSystem.TryGetActiveItem((uid, hands), out var held)) return false; - if (!CanInsert(uid, held, user, slot)) + if (!CanInsert(uid, held.Value, user, slot)) return false; // hands.Drop(item) checks CanDrop action blocker - if (!_handsSystem.TryDrop(user, hands.ActiveHand)) + if (!_handsSystem.TryDrop(user, hands.ActiveHandId!)) return false; - Insert(uid, slot, held, user, excludeUserAudio: excludeUserAudio); + Insert(uid, slot, held.Value, user, excludeUserAudio: excludeUserAudio); return true; } @@ -428,16 +428,14 @@ namespace Content.Shared.Containers.ItemSlots if (!Resolve(ent, ref ent.Comp, false)) return false; - TryComp(user, out HandsComponent? handsComp); - if (!TryGetAvailableSlot(ent, item, - user == null ? null : (user.Value, handsComp), + user, out var itemSlot, emptyOnly: true)) return false; - if (user != null && !_handsSystem.TryDrop(user.Value, item, handsComp: handsComp)) + if (user != null && !_handsSystem.TryDrop(user.Value, item)) return false; Insert(ent, itemSlot, item, user, excludeUserAudio: excludeUserAudio); @@ -466,7 +464,7 @@ namespace Content.Shared.Containers.ItemSlots && Resolve(user, ref user.Comp) && _handsSystem.IsHolding(user, item)) { - if (!_handsSystem.CanDrop(user, item, user.Comp)) + if (!_handsSystem.CanDrop(user, item)) return false; } diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index c55bbdb152..c16bbd1e85 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -397,6 +397,10 @@ namespace Content.Shared.Cuffs /// private void OnHandCountChanged(Entity ent, ref HandCountChangedEvent message) { + // TODO: either don't store a container ref, or make it actually nullable. + if (ent.Comp.Container == default!) + return; + var dirty = false; var handCount = CompOrNull(ent.Owner)?.Count ?? 0; @@ -431,19 +435,19 @@ namespace Content.Shared.Cuffs return; var freeHands = 0; - foreach (var hand in _hands.EnumerateHands(uid, handsComponent)) + foreach (var hand in _hands.EnumerateHands((uid, handsComponent))) { - if (hand.HeldEntity == null) + if (!_hands.TryGetHeldItem((uid, handsComponent), hand, out var held)) { freeHands++; continue; } // Is this entity removable? (it might be an existing handcuff blocker) - if (HasComp(hand.HeldEntity)) + if (HasComp(held)) continue; - _hands.DoDrop(uid, hand, true, handsComponent); + _hands.DoDrop(uid, hand, true); freeHands++; if (freeHands == 2) break; diff --git a/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs index 1db24c700d..e92552ef6d 100644 --- a/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs +++ b/Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs @@ -141,7 +141,7 @@ public abstract class SharedDisposalUnitSystem : EntitySystem Category = VerbCategory.Insert, Act = () => { - _handsSystem.TryDropIntoContainer(args.User, args.Using.Value, component.Container, checkActionBlocker: false, args.Hands); + _handsSystem.TryDropIntoContainer((args.User, args.Hands), args.Using.Value, component.Container, checkActionBlocker: false); _adminLog.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} inserted {ToPrettyString(args.Using.Value)} into {ToPrettyString(uid)}"); AfterInsert(uid, component, args.Using.Value, args.User); } diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs index f9718be52d..f6eb9ef996 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs @@ -233,7 +233,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem return true; // If the user changes which hand is active at all, interrupt the do-after - if (args.BreakOnHandChange && hands.ActiveHand?.Name != doAfter.InitialHand) + if (args.BreakOnHandChange && hands.ActiveHandId != doAfter.InitialHand) return true; } diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.cs index 9765bac912..1dc1e58be6 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.cs @@ -221,8 +221,8 @@ public abstract partial class SharedDoAfterSystem : EntitySystem if (!TryComp(args.User, out HandsComponent? handsComponent)) return false; - doAfter.InitialHand = handsComponent.ActiveHand?.Name; - doAfter.InitialItem = handsComponent.ActiveHandEntity; + doAfter.InitialHand = handsComponent.ActiveHandId; + doAfter.InitialItem = _hands.GetActiveItem((args.User, handsComponent)); } doAfter.NetInitialItem = GetNetEntity(doAfter.InitialItem); diff --git a/Content.Shared/Foldable/DeployFoldableSystem.cs b/Content.Shared/Foldable/DeployFoldableSystem.cs index a83518dfa3..cac73f6428 100644 --- a/Content.Shared/Foldable/DeployFoldableSystem.cs +++ b/Content.Shared/Foldable/DeployFoldableSystem.cs @@ -70,7 +70,7 @@ public sealed class DeployFoldableSystem : EntitySystem } if (!TryComp(args.User, out HandsComponent? hands) - || !_hands.TryDrop(args.User, args.Used, targetDropLocation: args.ClickLocation, handsComp: hands)) + || !_hands.TryDrop((args.User, hands), args.Used, targetDropLocation: args.ClickLocation)) return; if (!_foldable.TrySetFolded(ent, foldable, false)) diff --git a/Content.Shared/Hands/Components/HandHelpers.cs b/Content.Shared/Hands/Components/HandHelpers.cs deleted file mode 100644 index aecf3a6936..0000000000 --- a/Content.Shared/Hands/Components/HandHelpers.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Linq; -using Content.Shared.Hands.EntitySystems; - -namespace Content.Shared.Hands.Components; - -/// -/// These helpers exist to make getting basic information out of the hands component more convenient, without -/// needing to resolve hands system or something like that. -/// -public static class HandHelpers -{ - /// - /// Returns true if any hand is free. This is a LinQ method, not a property, so - /// cache it instead of accessing this multiple times. - /// - public static bool IsAnyHandFree(this HandsComponent component) => component.Hands.Values.Any(hand => hand.IsEmpty); - - /// - /// Get the number of hands that are not currently holding anything. This is a LinQ method, not a property, so - /// cache it instead of accessing this multiple times. - /// - public static int CountFreeHands(this HandsComponent component) => component.Hands.Values.Count(hand => hand.IsEmpty); - - /// - /// Get the number of hands that are not currently holding anything. This is a LinQ method, not a property, so - /// cache it instead of accessing this multiple times. - /// - public static int CountFreeableHands(this Entity component, SharedHandsSystem system) - { - return system.CountFreeableHands(component); - } - - /// - /// Get a list of hands that are currently holding nothing. This is a LinQ method, not a property, so cache - /// it instead of accessing this multiple times. - /// - public static IEnumerable GetFreeHands(this HandsComponent component) => component.Hands.Values.Where(hand => !hand.IsEmpty); - - /// - /// Get a list of hands that are currently holding nothing. This is a LinQ method, not a property, so cache - /// it instead of accessing this multiple times. - /// - public static IEnumerable GetFreeHandNames(this HandsComponent component) => GetFreeHands(component).Select(hand => hand.Name); -} diff --git a/Content.Shared/Hands/Components/HandsComponent.cs b/Content.Shared/Hands/Components/HandsComponent.cs index 0af318ba06..2aa97e7841 100644 --- a/Content.Shared/Hands/Components/HandsComponent.cs +++ b/Content.Shared/Hands/Components/HandsComponent.cs @@ -1,6 +1,5 @@ using Content.Shared.DisplacementMap; using Content.Shared.Hands.EntitySystems; -using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Serialization; @@ -13,50 +12,50 @@ public sealed partial class HandsComponent : Component /// /// The currently active hand. /// - [ViewVariables] - public Hand? ActiveHand; + [DataField] + public string? ActiveHandId; /// - /// The item currently held in the active hand. + /// Dictionary relating a unique hand ID corresponding to a container slot on the attached entity to a class containing information about the Hand itself. /// - [ViewVariables] - public EntityUid? ActiveHandEntity => ActiveHand?.HeldEntity; - - [ViewVariables] + [DataField] public Dictionary Hands = new(); + /// + /// The number of hands + /// + [ViewVariables] public int Count => Hands.Count; /// /// List of hand-names. These are keys for . The order of this list determines the order in which hands are iterated over. /// + [DataField] public List SortedHands = new(); /// /// If true, the items in the hands won't be affected by explosions. /// [DataField] - public bool DisableExplosionRecursion = false; + public bool DisableExplosionRecursion; /// /// Modifies the speed at which items are thrown. /// [DataField] - [ViewVariables(VVAccess.ReadWrite)] - public float BaseThrowspeed { get; set; } = 11f; + public float BaseThrowspeed = 11f; /// /// Distance after which longer throw targets stop increasing throw impulse. /// - [DataField("throwRange")] - [ViewVariables(VVAccess.ReadWrite)] - public float ThrowRange { get; set; } = 8f; + [DataField] + public float ThrowRange = 8f; /// /// Whether or not to add in-hand sprites for held items. Some entities (e.g., drones) don't want these. /// Used by the client. /// - [DataField("showInHands")] + [DataField] public bool ShowInHands = true; /// @@ -68,14 +67,13 @@ public sealed partial class HandsComponent : Component /// /// The time at which throws will be allowed again. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - [AutoPausedField] + [DataField, AutoPausedField] public TimeSpan NextThrowTime; /// /// The minimum time inbetween throws. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan ThrowCooldown = TimeSpan.FromSeconds(0.5f); /// @@ -103,48 +101,37 @@ public sealed partial class HandsComponent : Component public bool CanBeStripped = true; } +[DataDefinition] [Serializable, NetSerializable] -public sealed class Hand //TODO: This should definitely be a struct - Jezi +public partial record struct Hand { - [ViewVariables] - public string Name { get; } + [DataField] + public HandLocation Location = HandLocation.Right; - [ViewVariables] - public HandLocation Location { get; } - - /// - /// The container used to hold the contents of this hand. Nullable because the client must get the containers via , - /// which may not be synced with the server when the client hands are created. - /// - [ViewVariables, NonSerialized] - public ContainerSlot? Container; - - [ViewVariables] - public EntityUid? HeldEntity => Container?.ContainedEntity; - - public bool IsEmpty => HeldEntity == null; - - public Hand(string name, HandLocation location, ContainerSlot? container = null) + public Hand() + { + + } + + public Hand(HandLocation location) { - Name = name; Location = location; - Container = container; } } [Serializable, NetSerializable] public sealed class HandsComponentState : ComponentState { - public readonly List Hands; - public readonly List HandNames; - public readonly string? ActiveHand; + public readonly Dictionary Hands; + public readonly List SortedHands; + public readonly string? ActiveHandId; public HandsComponentState(HandsComponent handComp) { // cloning lists because of test networking. - Hands = new(handComp.Hands.Values); - HandNames = new(handComp.SortedHands); - ActiveHand = handComp.ActiveHand?.Name; + Hands = new(handComp.Hands); + SortedHands = new(handComp.SortedHands); + ActiveHandId = handComp.ActiveHandId; } } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.AI.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.AI.cs index 1eac3a2263..e84b23400b 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.AI.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.AI.cs @@ -6,17 +6,17 @@ namespace Content.Shared.Hands.EntitySystems; // These functions are mostly unused except for some AI operator stuff // Nothing stops them from being used in general. If they ever get used elsewhere, then this file probably needs to be renamed. -public abstract partial class SharedHandsSystem : EntitySystem +public abstract partial class SharedHandsSystem { public bool TrySelect(EntityUid uid, EntityUid? entity, HandsComponent? handsComp = null) { if (!Resolve(uid, ref handsComp, false)) return false; - if (!IsHolding(uid, entity, out var hand, handsComp)) + if (!IsHolding((uid, handsComp), entity, out var hand)) return false; - SetActiveHand(uid, hand, handsComp); + SetActiveHand((uid, handsComp), hand); return true; } @@ -26,9 +26,12 @@ public abstract partial class SharedHandsSystem : EntitySystem if (!Resolve(uid, ref handsComp, false)) return false; - foreach (var hand in handsComp.Hands.Values) + foreach (var hand in handsComp.Hands.Keys) { - if (TryComp(hand.HeldEntity, out component)) + if (!TryGetHeldItem((uid, handsComp), hand, out var held)) + continue; + + if (TryComp(held, out component)) return true; } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs index 95773697db..c1b44efba4 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs @@ -7,6 +7,7 @@ using Content.Shared.Tag; using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Shared.Hands.EntitySystems; @@ -28,10 +29,10 @@ public abstract partial class SharedHandsSystem return; } - var gotUnequipped = new GotUnequippedHandEvent(uid, args.Entity, hand); + var gotUnequipped = new GotUnequippedHandEvent(uid, args.Entity, hand.Value); RaiseLocalEvent(args.Entity, gotUnequipped); - var didUnequip = new DidUnequipHandEvent(uid, args.Entity, hand); + var didUnequip = new DidUnequipHandEvent(uid, args.Entity, hand.Value); RaiseLocalEvent(uid, didUnequip); if (TryComp(args.Entity, out VirtualItemComponent? @virtual)) @@ -47,26 +48,29 @@ public abstract partial class SharedHandsSystem /// /// Checks whether an entity can drop a given entity. Will return false if they are not holding the entity. /// - public bool CanDrop(EntityUid uid, EntityUid entity, HandsComponent? handsComp = null, bool checkActionBlocker = true) + public bool CanDrop(Entity ent, EntityUid entity, bool checkActionBlocker = true) { - if (!Resolve(uid, ref handsComp)) + if (!Resolve(ent, ref ent.Comp, false)) return false; - if (!IsHolding(uid, entity, out var hand, handsComp)) + if (!IsHolding(ent, entity, out var hand)) return false; - return CanDropHeld(uid, hand, checkActionBlocker); + return CanDropHeld(ent, hand, checkActionBlocker); } /// /// Checks if the contents of a hand is able to be removed from its container. /// - public bool CanDropHeld(EntityUid uid, Hand hand, bool checkActionBlocker = true) + public bool CanDropHeld(EntityUid uid, string handId, bool checkActionBlocker = true) { - if (hand.Container?.ContainedEntity is not {} held) + if (!ContainerSystem.TryGetContainer(uid, handId, out var container)) return false; - if (!ContainerSystem.CanRemove(held, hand.Container)) + if (container.ContainedEntities.FirstOrNull() is not {} held) + return false; + + if (!ContainerSystem.CanRemove(held, container)) return false; if (checkActionBlocker && !_actionBlocker.CanDrop(uid)) @@ -78,98 +82,100 @@ public abstract partial class SharedHandsSystem /// /// Attempts to drop the item in the currently active hand. /// - public bool TryDrop(EntityUid uid, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true, HandsComponent? handsComp = null) + public bool TryDrop(Entity ent, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true) { - if (!Resolve(uid, ref handsComp)) + if (!Resolve(ent, ref ent.Comp, false)) return false; - if (handsComp.ActiveHand == null) + if (ent.Comp.ActiveHandId == null) return false; - return TryDrop(uid, handsComp.ActiveHand, targetDropLocation, checkActionBlocker, doDropInteraction, handsComp); + return TryDrop(ent, ent.Comp.ActiveHandId, targetDropLocation, checkActionBlocker, doDropInteraction); } /// /// Drops an item at the target location. /// - public bool TryDrop(EntityUid uid, EntityUid entity, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true, HandsComponent? handsComp = null) + public bool TryDrop(Entity ent, EntityUid entity, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true) { - if (!Resolve(uid, ref handsComp)) + if (!Resolve(ent, ref ent.Comp, false)) return false; - if (!IsHolding(uid, entity, out var hand, handsComp)) + if (!IsHolding(ent, entity, out var hand)) return false; - return TryDrop(uid, hand, targetDropLocation, checkActionBlocker, doDropInteraction, handsComp); + return TryDrop(ent, hand, targetDropLocation, checkActionBlocker, doDropInteraction); } /// /// Drops a hands contents at the target location. /// - public bool TryDrop(EntityUid uid, Hand hand, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true, HandsComponent? handsComp = null) + public bool TryDrop(Entity ent, string handId, EntityCoordinates? targetDropLocation = null, bool checkActionBlocker = true, bool doDropInteraction = true) { - if (!Resolve(uid, ref handsComp)) + if (!Resolve(ent, ref ent.Comp, false)) return false; - if (!CanDropHeld(uid, hand, checkActionBlocker)) + if (!CanDropHeld(ent, handId, checkActionBlocker)) return false; - var entity = hand.HeldEntity!.Value; + if (!TryGetHeldItem(ent, handId, out var entity)) + return false; // if item is a fake item (like with pulling), just delete it rather than bothering with trying to drop it into the world if (TryComp(entity, out VirtualItemComponent? @virtual)) - _virtualSystem.DeleteVirtualItem((entity, @virtual), uid); + _virtualSystem.DeleteVirtualItem((entity.Value, @virtual), ent); if (TerminatingOrDeleted(entity)) return true; - var itemXform = Transform(entity); + var itemXform = Transform(entity.Value); if (itemXform.MapUid == null) return true; - var userXform = Transform(uid); - var isInContainer = ContainerSystem.IsEntityOrParentInContainer(uid, xform: userXform); + var userXform = Transform(ent); + var isInContainer = ContainerSystem.IsEntityOrParentInContainer(ent, xform: userXform); // if the user is in a container, drop the item inside the container - if (isInContainer) { - TransformSystem.DropNextTo((entity, itemXform), (uid, userXform)); + if (isInContainer) + { + TransformSystem.DropNextTo((entity.Value, itemXform), (ent, userXform)); return true; } // drop the item with heavy calculations from their hands and place it at the calculated interaction range position // The DoDrop is handle if there's no drop target - DoDrop(uid, hand, doDropInteraction: doDropInteraction, handsComp); + DoDrop(ent, handId, doDropInteraction: doDropInteraction); // if there's no drop location stop here if (targetDropLocation == null) return true; // otherwise, also move dropped item and rotate it properly according to grid/map - var (itemPos, itemRot) = TransformSystem.GetWorldPositionRotation(entity); + var (itemPos, itemRot) = TransformSystem.GetWorldPositionRotation(entity.Value); var origin = new MapCoordinates(itemPos, itemXform.MapID); var target = TransformSystem.ToMapCoordinates(targetDropLocation.Value); - TransformSystem.SetWorldPositionRotation(entity, GetFinalDropCoordinates(uid, origin, target, entity), itemRot); + TransformSystem.SetWorldPositionRotation(entity.Value, GetFinalDropCoordinates(ent, origin, target, entity.Value), itemRot); return true; } /// /// Attempts to move a held item from a hand into a container that is not another hand, without dropping it on the floor in-between. /// - public bool TryDropIntoContainer(EntityUid uid, EntityUid entity, BaseContainer targetContainer, bool checkActionBlocker = true, HandsComponent? handsComp = null) + public bool TryDropIntoContainer(Entity ent, EntityUid entity, BaseContainer targetContainer, bool checkActionBlocker = true) { - if (!Resolve(uid, ref handsComp)) + if (!Resolve(ent, ref ent.Comp, false)) return false; - if (!IsHolding(uid, entity, out var hand, handsComp)) + if (!IsHolding(ent, entity, out var hand)) return false; - if (!CanDropHeld(uid, hand, checkActionBlocker)) + if (!CanDropHeld(ent, hand, checkActionBlocker)) return false; if (!ContainerSystem.CanInsert(entity, targetContainer)) return false; - DoDrop(uid, hand, false, handsComp); + DoDrop(ent, hand, false); ContainerSystem.Insert(entity, targetContainer); return true; } @@ -202,34 +208,38 @@ public abstract partial class SharedHandsSystem /// /// Removes the contents of a hand from its container. Assumes that the removal is allowed. In general, you should not be calling this directly. /// - public virtual void DoDrop(EntityUid uid, Hand hand, bool doDropInteraction = true, HandsComponent? handsComp = null, bool log = true) + public virtual void DoDrop(Entity ent, + string handId, + bool doDropInteraction = true, + bool log = true) { - if (!Resolve(uid, ref handsComp)) + if (!Resolve(ent, ref ent.Comp, false)) return; - if (hand.Container?.ContainedEntity == null) + if (!ContainerSystem.TryGetContainer(ent, handId, out var container)) return; - var entity = hand.Container.ContainedEntity.Value; - - if (TerminatingOrDeleted(uid) || TerminatingOrDeleted(entity)) + if (!TryGetHeldItem(ent, handId, out var entity)) return; - if (!ContainerSystem.Remove(entity, hand.Container)) + if (TerminatingOrDeleted(ent) || TerminatingOrDeleted(entity)) + return; + + if (!ContainerSystem.Remove(entity.Value, container)) { - Log.Error($"Failed to remove {ToPrettyString(entity)} from users hand container when dropping. User: {ToPrettyString(uid)}. Hand: {hand.Name}."); + Log.Error($"Failed to remove {ToPrettyString(entity)} from users hand container when dropping. User: {ToPrettyString(ent)}. Hand: {handId}."); return; } - Dirty(uid, handsComp); + Dirty(ent); if (doDropInteraction) - _interactionSystem.DroppedInteraction(uid, entity); + _interactionSystem.DroppedInteraction(ent, entity.Value); if (log) - _adminLogger.Add(LogType.Drop, LogImpact.Low, $"{ToPrettyString(uid):user} dropped {ToPrettyString(entity):entity}"); + _adminLogger.Add(LogType.Drop, LogImpact.Low, $"{ToPrettyString(ent):user} dropped {ToPrettyString(entity):entity}"); - if (hand == handsComp.ActiveHand) - RaiseLocalEvent(entity, new HandDeselectedEvent(uid)); + if (handId == ent.Comp.ActiveHandId) + RaiseLocalEvent(entity.Value, new HandDeselectedEvent(ent)); } } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs index 6e383cd69c..0e8d7a7556 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs @@ -97,20 +97,20 @@ public abstract partial class SharedHandsSystem : EntitySystem if (!_actionBlocker.CanInteract(session.AttachedEntity.Value, null)) return; - if (component.ActiveHand == null || component.Hands.Count < 2) + if (component.ActiveHandId == null || component.Hands.Count < 2) return; - var currentIndex = component.SortedHands.IndexOf(component.ActiveHand.Name); + var currentIndex = component.SortedHands.IndexOf(component.ActiveHandId); var newActiveIndex = (currentIndex + (reverse ? -1 : 1) + component.Hands.Count) % component.Hands.Count; var nextHand = component.SortedHands[newActiveIndex]; - TrySetActiveHand(session.AttachedEntity.Value, nextHand, component); + TrySetActiveHand((session.AttachedEntity.Value, component), nextHand); } private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid netEntity) { - if (TryComp(session?.AttachedEntity, out HandsComponent? hands) && hands.ActiveHand != null) - TryDrop(session.AttachedEntity.Value, hands.ActiveHand, coords, handsComp: hands); + if (TryComp(session?.AttachedEntity, out HandsComponent? hands) && hands.ActiveHandId != null) + TryDrop((session.AttachedEntity.Value, hands), hands.ActiveHandId, coords); // always send to server. return false; @@ -122,14 +122,14 @@ public abstract partial class SharedHandsSystem : EntitySystem if (!Resolve(uid, ref handsComp, false)) return false; - Hand? hand; - if (handName == null || !handsComp.Hands.TryGetValue(handName, out hand)) - hand = handsComp.ActiveHand; + var hand = handName; + if (!TryGetHand(uid, hand, out _)) + hand = handsComp.ActiveHandId; - if (hand?.HeldEntity is not { } held) + if (!TryGetHeldItem((uid, handsComp), hand, out var held)) return false; - return _interactionSystem.InteractionActivate(uid, held); + return _interactionSystem.InteractionActivate(uid, held.Value); } public bool TryInteractHandWithActiveHand(EntityUid uid, string handName, HandsComponent? handsComp = null) @@ -137,16 +137,13 @@ public abstract partial class SharedHandsSystem : EntitySystem if (!Resolve(uid, ref handsComp, false)) return false; - if (handsComp.ActiveHandEntity == null) + if (!TryGetActiveItem((uid, handsComp), out var activeHeldItem)) return false; - if (!handsComp.Hands.TryGetValue(handName, out var hand)) + if (!TryGetHeldItem((uid, handsComp), handName, out var held)) return false; - if (hand.HeldEntity == null) - return false; - - _interactionSystem.InteractUsing(uid, handsComp.ActiveHandEntity.Value, hand.HeldEntity.Value, Transform(hand.HeldEntity.Value).Coordinates); + _interactionSystem.InteractUsing(uid, activeHeldItem.Value, held.Value, Transform(held.Value).Coordinates); return true; } @@ -155,17 +152,16 @@ public abstract partial class SharedHandsSystem : EntitySystem if (!Resolve(uid, ref handsComp, false)) return false; - Hand? hand; - if (handName == null || !handsComp.Hands.TryGetValue(handName, out hand)) - hand = handsComp.ActiveHand; + var hand = handName; + if (!TryGetHand(uid, hand, out _)) + hand = handsComp.ActiveHandId; - if (hand?.HeldEntity is not { } held) + if (!TryGetHeldItem((uid, handsComp), hand, out var held)) return false; if (altInteract) - return _interactionSystem.AltInteract(uid, held); - else - return _interactionSystem.UseInHandInteraction(uid, held); + return _interactionSystem.AltInteract(uid, held.Value); + return _interactionSystem.UseInHandInteraction(uid, held.Value); } /// @@ -176,22 +172,20 @@ public abstract partial class SharedHandsSystem : EntitySystem if (!Resolve(uid, ref handsComp)) return false; - if (handsComp.ActiveHand == null || !handsComp.ActiveHand.IsEmpty) + if (handsComp.ActiveHandId == null || !HandIsEmpty((uid, handsComp), handsComp.ActiveHandId)) return false; - if (!handsComp.Hands.TryGetValue(handName, out var hand)) + if (!TryGetHeldItem((uid, handsComp), handName, out var entity)) return false; - if (!CanDropHeld(uid, hand, checkActionBlocker)) + if (!CanDropHeld(uid, handName, checkActionBlocker)) return false; - var entity = hand.HeldEntity!.Value; - - if (!CanPickupToHand(uid, entity, handsComp.ActiveHand, checkActionBlocker, handsComp)) + if (!CanPickupToHand(uid, entity.Value, handsComp.ActiveHandId, checkActionBlocker, handsComp)) return false; - DoDrop(uid, hand, false, handsComp, log:false); - DoPickup(uid, handsComp.ActiveHand, entity, handsComp, log: false); + DoDrop(uid, handName, false, log: false); + DoPickup(uid, handsComp.ActiveHandId, entity.Value, handsComp, log: false); return true; } @@ -200,19 +194,19 @@ public abstract partial class SharedHandsSystem : EntitySystem if (args.Handled) return; - if (component.ActiveHandEntity.HasValue) + if (TryGetActiveItem((uid, component), out var activeHeldItem)) { // allow for the item to return a different entity, e.g. virtual items - RaiseLocalEvent(component.ActiveHandEntity.Value, ref args); + RaiseLocalEvent(activeHeldItem.Value, ref args); } - args.Used ??= component.ActiveHandEntity; + args.Used ??= activeHeldItem; } //TODO: Actually shows all items/clothing/etc. private void HandleExamined(EntityUid examinedUid, HandsComponent handsComp, ExaminedEvent args) { - var heldItemNames = EnumerateHeld(examinedUid, handsComp) + var heldItemNames = EnumerateHeld((examinedUid, handsComp)) .Where(entity => !HasComp(entity)) .Select(item => FormattedMessage.EscapeText(Identity.Name(item, EntityManager))) .Select(itemName => Loc.GetString("comp-hands-examine-wrapper", ("item", itemName))) diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs index 5addd7c029..4558f6c528 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs @@ -1,15 +1,15 @@ -using Content.Shared.Clothing.Components; +using System.Diagnostics; using Content.Shared.Database; using Content.Shared.Hands.Components; using Content.Shared.Item; using Robust.Shared.Containers; -using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; +using Robust.Shared.Utility; namespace Content.Shared.Hands.EntitySystems; -public abstract partial class SharedHandsSystem : EntitySystem +public abstract partial class SharedHandsSystem { private void InitializePickup() { @@ -23,11 +23,11 @@ public abstract partial class SharedHandsSystem : EntitySystem return; } - var didEquip = new DidEquipHandEvent(uid, args.Entity, hand); - RaiseLocalEvent(uid, didEquip, false); + var didEquip = new DidEquipHandEvent(uid, args.Entity, hand.Value); + RaiseLocalEvent(uid, didEquip); - var gotEquipped = new GotEquippedHandEvent(uid, args.Entity, hand); - RaiseLocalEvent(args.Entity, gotEquipped, false); + var gotEquipped = new GotEquippedHandEvent(uid, args.Entity, hand.Value); + RaiseLocalEvent(args.Entity, gotEquipped); } /// @@ -35,32 +35,6 @@ public abstract partial class SharedHandsSystem : EntitySystem /// public const float MaxAnimationRange = 10; - /// - /// Tries to pick up an entity to a specific hand. If no explicit hand is specified, defaults to using the currently active hand. - /// - public bool TryPickup( - EntityUid uid, - EntityUid entity, - string? handName = null, - bool checkActionBlocker = true, - bool animateUser = false, - bool animate = true, - HandsComponent? handsComp = null, - ItemComponent? item = null) - { - if (!Resolve(uid, ref handsComp, false)) - return false; - - var hand = handsComp.ActiveHand; - if (handName != null && !handsComp.Hands.TryGetValue(handName, out hand)) - return false; - - if (hand == null) - return false; - - return TryPickup(uid, entity, hand, checkActionBlocker, animate, handsComp, item); - } - /// /// Attempts to pick up an item into any empty hand. Prioritizes the currently active hand. /// @@ -80,17 +54,21 @@ public abstract partial class SharedHandsSystem : EntitySystem if (!Resolve(uid, ref handsComp, false)) return false; - if (!TryGetEmptyHand(uid, out var hand, handsComp)) + if (!TryGetEmptyHand((uid, handsComp), out var hand)) return false; - return TryPickup(uid, entity, hand, checkActionBlocker, animate, handsComp, item); + return TryPickup(uid, entity, hand, checkActionBlocker, animateUser, animate, handsComp, item); } + /// + /// Tries to pick up an entity to a specific hand. If no explicit hand is specified, defaults to using the currently active hand. + /// public bool TryPickup( EntityUid uid, EntityUid entity, - Hand hand, + string? handId = null, bool checkActionBlocker = true, + bool animateUser = false, bool animate = true, HandsComponent? handsComp = null, ItemComponent? item = null) @@ -98,10 +76,15 @@ public abstract partial class SharedHandsSystem : EntitySystem if (!Resolve(uid, ref handsComp, false)) return false; + handId ??= handsComp.ActiveHandId; + + if (handId == null) + return false; + if (!Resolve(entity, ref item, false)) return false; - if (!CanPickupToHand(uid, entity, hand, checkActionBlocker, handsComp, item)) + if (!CanPickupToHand(uid, entity, handId, checkActionBlocker, handsComp, item)) return false; if (animate) @@ -119,7 +102,7 @@ public abstract partial class SharedHandsSystem : EntitySystem _storage.PlayPickupAnimation(entity, initialPosition, xform.Coordinates, itemXform.LocalRotation, uid); } } - DoPickup(uid, hand, entity, handsComp); + DoPickup(uid, handId, entity, handsComp); return true; } @@ -129,20 +112,20 @@ public abstract partial class SharedHandsSystem : EntitySystem /// By default it does check if it's possible to drop items. /// public bool TryForcePickup( - EntityUid uid, + Entity ent, EntityUid entity, - Hand hand, + string hand, bool checkActionBlocker = true, bool animate = true, HandsComponent? handsComp = null, ItemComponent? item = null) { - if (!Resolve(uid, ref handsComp, false)) + if (!Resolve(ent, ref ent.Comp, false)) return false; - TryDrop(uid, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp); + TryDrop(ent, hand, checkActionBlocker: checkActionBlocker); - return TryPickup(uid, entity, hand, checkActionBlocker, animate, handsComp, item); + return TryPickup(ent, entity, hand, checkActionBlocker, animate: animate, handsComp: handsComp, item: item); } /// @@ -157,9 +140,9 @@ public abstract partial class SharedHandsSystem : EntitySystem if (TryPickupAnyHand(uid, entity, checkActionBlocker: checkActionBlocker, handsComp: handsComp)) return true; - foreach (var hand in handsComp.Hands.Values) + foreach (var hand in handsComp.Hands.Keys) { - if (TryDrop(uid, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp) && + if (TryDrop((uid, handsComp), hand, checkActionBlocker: checkActionBlocker) && TryPickup(uid, entity, hand, checkActionBlocker: checkActionBlocker, handsComp: handsComp)) { return true; @@ -173,7 +156,7 @@ public abstract partial class SharedHandsSystem : EntitySystem if (!Resolve(uid, ref handsComp, false)) return false; - if (!TryGetEmptyHand(uid, out var hand, handsComp)) + if (!TryGetEmptyHand((uid, handsComp), out var hand)) return false; return CanPickupToHand(uid, entity, hand, checkActionBlocker, handsComp, item); @@ -182,13 +165,15 @@ public abstract partial class SharedHandsSystem : EntitySystem /// /// Checks whether a given item will fit into a specific user's hand. Unless otherwise specified, this will also check the general CanPickup action blocker. /// - public bool CanPickupToHand(EntityUid uid, EntityUid entity, Hand hand, bool checkActionBlocker = true, HandsComponent? handsComp = null, ItemComponent? item = null) + public bool CanPickupToHand(EntityUid uid, EntityUid entity, string handId, bool checkActionBlocker = true, HandsComponent? handsComp = null, ItemComponent? item = null) { if (!Resolve(uid, ref handsComp, false)) return false; - var handContainer = hand.Container; - if (handContainer == null || handContainer.ContainedEntity != null) + if (!ContainerSystem.TryGetContainer(uid, handId, out var handContainer)) + return false; + + if (handContainer.ContainedEntities.FirstOrNull() != null) return false; if (!Resolve(entity, ref item, false)) @@ -231,8 +216,7 @@ public abstract partial class SharedHandsSystem : EntitySystem { if (uid == null || !Resolve(uid.Value, ref handsComp, false) - || !TryGetEmptyHand(uid.Value, out var hand, handsComp) - || !TryPickup(uid.Value, entity, hand, checkActionBlocker, animate, handsComp, item)) + || !TryPickupAnyHand(uid.Value, entity, checkActionBlocker, animateUser, animate, handsComp, item)) { // TODO make this check upwards for any container, and parent to that. // Currently this just checks the direct parent, so items can still teleport through containers. @@ -248,18 +232,20 @@ public abstract partial class SharedHandsSystem : EntitySystem /// /// Puts an entity into the player's hand, assumes that the insertion is allowed. In general, you should not be calling this function directly. /// - public virtual void DoPickup(EntityUid uid, Hand hand, EntityUid entity, HandsComponent? hands = null, bool log = true) + public virtual void DoPickup(EntityUid uid, string hand, EntityUid entity, HandsComponent? hands = null, bool log = true) { if (!Resolve(uid, ref hands)) return; - var handContainer = hand.Container; - if (handContainer == null || handContainer.ContainedEntity != null) + if (!ContainerSystem.TryGetContainer(uid, hand, out var handContainer)) + return; + + if (handContainer.ContainedEntities.FirstOrNull() != null) return; if (!ContainerSystem.Insert(entity, handContainer)) { - Log.Error($"Failed to insert {ToPrettyString(entity)} into users hand container when picking up. User: {ToPrettyString(uid)}. Hand: {hand.Name}."); + Log.Error($"Failed to insert {ToPrettyString(entity)} into users hand container when picking up. User: {ToPrettyString(uid)}. Hand: {hand}."); return; } @@ -270,7 +256,7 @@ public abstract partial class SharedHandsSystem : EntitySystem Dirty(uid, hands); - if (hand == hands.ActiveHand) - RaiseLocalEvent(entity, new HandSelectedEvent(uid), false); + if (hand == hands.ActiveHandId) + RaiseLocalEvent(entity, new HandSelectedEvent(uid)); } } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs index e6f21abf1b..bfb9a41b0b 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs @@ -41,7 +41,7 @@ public abstract partial class SharedHandsSystem { var ev = new HeldRelayedEvent(args); - foreach (var held in EnumerateHeld(entity, entity.Comp)) + foreach (var held in EnumerateHeld(entity.AsNullable())) { RaiseLocalEvent(held, ref ev); } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs index 84beabf9ac..4a24b9de02 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Inventory.VirtualItem; using Content.Shared.Storage.EntitySystems; using Robust.Shared.Containers; using Robust.Shared.Input.Binding; +using Robust.Shared.Utility; namespace Content.Shared.Hands.EntitySystems; @@ -23,6 +24,8 @@ public abstract partial class SharedHandsSystem [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; [Dependency] private readonly SharedVirtualItemSystem _virtualSystem = default!; + public event Action, string, HandLocation>? OnPlayerAddHand; + public event Action, string>? OnPlayerRemoveHand; protected event Action?>? OnHandSetActive; public override void Initialize() @@ -33,6 +36,9 @@ public abstract partial class SharedHandsSystem InitializeDrop(); InitializePickup(); InitializeRelay(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnMapInit); } public override void Shutdown() @@ -41,71 +47,94 @@ public abstract partial class SharedHandsSystem CommandBinds.Unregister(); } - public virtual void AddHand(EntityUid uid, string handName, HandLocation handLocation, HandsComponent? handsComp = null) + private void OnInit(Entity ent, ref ComponentInit args) { - if (!Resolve(uid, ref handsComp, false)) - return; - - if (handsComp.Hands.ContainsKey(handName)) - return; - - var container = ContainerSystem.EnsureContainer(uid, handName); - container.OccludesLight = false; - - var newHand = new Hand(handName, handLocation, container); - handsComp.Hands.Add(handName, newHand); - handsComp.SortedHands.Add(handName); - - if (handsComp.ActiveHand == null) - SetActiveHand(uid, newHand, handsComp); - - RaiseLocalEvent(uid, new HandCountChangedEvent(uid)); - Dirty(uid, handsComp); + var container = EnsureComp(ent); + foreach (var id in ent.Comp.Hands.Keys) + { + ContainerSystem.EnsureContainer(ent, id, container); + } } - public virtual void RemoveHand(EntityUid uid, string handName, HandsComponent? handsComp = null) + private void OnMapInit(Entity ent, ref MapInitEvent args) { - if (!Resolve(uid, ref handsComp, false)) + if (ent.Comp.ActiveHandId == null) + SetActiveHand(ent.AsNullable(), ent.Comp.SortedHands.FirstOrDefault()); + } + + /// + /// Adds a hand with the given container id and supplied location to the specified entity. + /// + public void AddHand(Entity ent, string handName, HandLocation handLocation) + { + AddHand(ent, handName, new Hand(handLocation)); + } + + /// + /// Adds a hand with the given container id and supplied hand definition to the given entity. + /// + public void AddHand(Entity ent, string handName, Hand hand) + { + if (!Resolve(ent, ref ent.Comp, false)) return; - if (!handsComp.Hands.Remove(handName, out var hand)) + if (ent.Comp.Hands.ContainsKey(handName)) return; - handsComp.SortedHands.Remove(hand.Name); - TryDrop(uid, hand, null, false, true, handsComp); - if (hand.Container != null) - ContainerSystem.ShutdownContainer(hand.Container); + var container = ContainerSystem.EnsureContainer(ent, handName); + container.OccludesLight = false; - if (handsComp.ActiveHand == hand) - TrySetActiveHand(uid, handsComp.SortedHands.FirstOrDefault(), handsComp); + ent.Comp.Hands.Add(handName, hand); + ent.Comp.SortedHands.Add(handName); + Dirty(ent); - RaiseLocalEvent(uid, new HandCountChangedEvent(uid)); - Dirty(uid, handsComp); + OnPlayerAddHand?.Invoke((ent, ent.Comp), handName, hand.Location); + + if (ent.Comp.ActiveHandId == null) + SetActiveHand(ent, handName); + + RaiseLocalEvent(ent, new HandCountChangedEvent(ent)); + } + + /// + /// Removes the specified hand from the specified entity + /// + public virtual void RemoveHand(Entity ent, string handName) + { + if (!Resolve(ent, ref ent.Comp, false)) + return; + + OnPlayerRemoveHand?.Invoke((ent, ent.Comp), handName); + + TryDrop(ent, handName, null, false); + + if (!ent.Comp.Hands.Remove(handName)) + return; + + if (ContainerSystem.TryGetContainer(ent, handName, out var container)) + ContainerSystem.ShutdownContainer(container); + + ent.Comp.SortedHands.Remove(handName); + if (ent.Comp.ActiveHandId == handName) + TrySetActiveHand(ent, ent.Comp.SortedHands.FirstOrDefault()); + + RaiseLocalEvent(ent, new HandCountChangedEvent(ent)); + Dirty(ent); } /// /// Gets rid of all the entity's hands. /// - /// - /// - public void RemoveHands(EntityUid uid, HandsComponent? handsComp = null) + public void RemoveHands(Entity ent) { - if (!Resolve(uid, ref handsComp)) + if (!Resolve(ent, ref ent.Comp, false)) return; - RemoveHands(uid, EnumerateHands(uid), handsComp); - } - - private void RemoveHands(EntityUid uid, IEnumerable hands, HandsComponent handsComp) - { - if (!hands.Any()) - return; - - var hand = hands.First(); - RemoveHand(uid, hand.Name, handsComp); - - // Repeats it for any additional hands. - RemoveHands(uid, hands, handsComp); + var handIds = new List(ent.Comp.Hands.Keys); + foreach (var handId in handIds) + { + RemoveHand(ent, handId); + } } private void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs) @@ -119,15 +148,15 @@ public abstract partial class SharedHandsSystem /// /// Get any empty hand. Prioritizes the currently active hand. /// - public bool TryGetEmptyHand(EntityUid uid, [NotNullWhen(true)] out Hand? emptyHand, HandsComponent? handComp = null) + public bool TryGetEmptyHand(Entity ent, [NotNullWhen(true)] out string? emptyHand) { emptyHand = null; - if (!Resolve(uid, ref handComp, false)) + if (!Resolve(ent, ref ent.Comp, false)) return false; - foreach (var hand in EnumerateHands(uid, handComp)) + foreach (var hand in EnumerateHands(ent)) { - if (hand.IsEmpty) + if (HandIsEmpty(ent, hand)) { emptyHand = hand; return true; @@ -137,28 +166,20 @@ public abstract partial class SharedHandsSystem return false; } - public bool TryGetActiveHand(Entity entity, [NotNullWhen(true)] out Hand? hand) - { - if (!Resolve(entity, ref entity.Comp, false)) - { - hand = null; - return false; - } - - hand = entity.Comp.ActiveHand; - return hand != null; - } - + /// + /// Attempts to retrieve the item held in the entity's active hand. + /// public bool TryGetActiveItem(Entity entity, [NotNullWhen(true)] out EntityUid? item) { - if (!TryGetActiveHand(entity, out var hand)) - { - item = null; + item = null; + if (!Resolve(entity, ref entity.Comp, false)) return false; - } - item = hand.HeldEntity; - return item != null; + if (!TryGetHeldItem(entity, entity.Comp.ActiveHandId, out var held)) + return false; + + item = held; + return true; } /// @@ -174,55 +195,73 @@ public abstract partial class SharedHandsSystem return item.Value; } - public Hand? GetActiveHand(Entity entity) + /// + /// Gets the current active hand's Id for the specified entity + /// + /// + /// + public string? GetActiveHand(Entity entity) { - if (!Resolve(entity, ref entity.Comp)) + if (!Resolve(entity, ref entity.Comp, false)) return null; - return entity.Comp.ActiveHand; + return entity.Comp.ActiveHandId; } + /// + /// Gets the current active hand's held entity for the specified entity + /// + /// + /// public EntityUid? GetActiveItem(Entity entity) { - return GetActiveHand(entity)?.HeldEntity; + if (!Resolve(entity, ref entity.Comp, false)) + return null; + + return GetHeldItem(entity, entity.Comp.ActiveHandId); + } + + public bool ActiveHandIsEmpty(Entity entity) + { + return GetActiveItem(entity) == null; } /// /// Enumerate over hands, starting with the currently active hand. /// - public IEnumerable EnumerateHands(EntityUid uid, HandsComponent? handsComp = null) + public IEnumerable EnumerateHands(Entity ent) { - if (!Resolve(uid, ref handsComp, false)) + if (!Resolve(ent, ref ent.Comp, false)) yield break; - if (handsComp.ActiveHand != null) - yield return handsComp.ActiveHand; + if (ent.Comp.ActiveHandId != null) + yield return ent.Comp.ActiveHandId; - foreach (var name in handsComp.SortedHands) + foreach (var name in ent.Comp.SortedHands) { - if (name != handsComp.ActiveHand?.Name) - yield return handsComp.Hands[name]; + if (name != ent.Comp.ActiveHandId) + yield return name; } } /// /// Enumerate over held items, starting with the item in the currently active hand (if there is one). /// - public IEnumerable EnumerateHeld(EntityUid uid, HandsComponent? handsComp = null) + public IEnumerable EnumerateHeld(Entity ent) { - if (!Resolve(uid, ref handsComp, false)) + if (!Resolve(ent, ref ent.Comp, false)) yield break; - if (handsComp.ActiveHandEntity != null) - yield return handsComp.ActiveHandEntity.Value; + if (TryGetActiveItem(ent, out var activeHeld)) + yield return activeHeld.Value; - foreach (var name in handsComp.SortedHands) + foreach (var name in ent.Comp.SortedHands) { - if (name == handsComp.ActiveHand?.Name) + if (name == ent.Comp.ActiveHandId) continue; - if (handsComp.Hands[name].HeldEntity is { } held) - yield return held; + if (TryGetHeldItem(ent, name, out var held)) + yield return held.Value; } } @@ -231,18 +270,17 @@ public abstract partial class SharedHandsSystem /// /// True if the active hand was set to a NEW value. Setting it to the same value returns false and does /// not trigger interactions. - public virtual bool TrySetActiveHand(EntityUid uid, string? name, HandsComponent? handComp = null) + public bool TrySetActiveHand(Entity ent, string? name) { - if (!Resolve(uid, ref handComp)) + if (!Resolve(ent, ref ent.Comp, false)) return false; - if (name == handComp.ActiveHand?.Name) + if (name == ent.Comp.ActiveHandId) return false; - Hand? hand = null; - if (name != null && !handComp.Hands.TryGetValue(name, out hand)) + if (name != null && !ent.Comp.Hands.ContainsKey(name)) return false; - return SetActiveHand(uid, hand, handComp); + return SetActiveHand(ent, name); } /// @@ -250,50 +288,50 @@ public abstract partial class SharedHandsSystem /// /// True if the active hand was set to a NEW value. Setting it to the same value returns false and does /// not trigger interactions. - public bool SetActiveHand(EntityUid uid, Hand? hand, HandsComponent? handComp = null) + public bool SetActiveHand(Entity ent, string? handId) { - if (!Resolve(uid, ref handComp)) + if (!Resolve(ent, ref ent.Comp)) return false; - if (hand == handComp.ActiveHand) + if (handId == ent.Comp.ActiveHandId) return false; - if (handComp.ActiveHand?.HeldEntity is { } held) - RaiseLocalEvent(held, new HandDeselectedEvent(uid)); + if (TryGetHeldItem(ent, handId, out var oldHeld)) + RaiseLocalEvent(oldHeld.Value, new HandDeselectedEvent(ent)); - if (hand == null) + if (handId == null) { - handComp.ActiveHand = null; + ent.Comp.ActiveHandId = null; return true; } - handComp.ActiveHand = hand; - OnHandSetActive?.Invoke((uid, handComp)); + ent.Comp.ActiveHandId = handId; + OnHandSetActive?.Invoke((ent, ent.Comp)); - if (hand.HeldEntity != null) - RaiseLocalEvent(hand.HeldEntity.Value, new HandSelectedEvent(uid)); + if (TryGetHeldItem(ent, handId, out var newHeld)) + RaiseLocalEvent(newHeld.Value, new HandSelectedEvent(ent)); - Dirty(uid, handComp); + Dirty(ent); return true; } public bool IsHolding(Entity entity, [NotNullWhen(true)] EntityUid? item) { - return IsHolding(entity, item, out _, entity); + return IsHolding(entity, item, out _); } - public bool IsHolding(EntityUid uid, [NotNullWhen(true)] EntityUid? entity, [NotNullWhen(true)] out Hand? inHand, HandsComponent? handsComp = null) + public bool IsHolding(Entity ent, [NotNullWhen(true)] EntityUid? entity, [NotNullWhen(true)] out string? inHand) { inHand = null; if (entity == null) return false; - if (!Resolve(uid, ref handsComp, false)) + if (!Resolve(ent, ref ent.Comp, false)) return false; - foreach (var hand in handsComp.Hands.Values) + foreach (var hand in ent.Comp.Hands.Keys) { - if (hand.HeldEntity == entity) + if (GetHeldItem(ent, hand) == entity) { inHand = hand; return true; @@ -303,23 +341,89 @@ public abstract partial class SharedHandsSystem return false; } - public bool TryGetHand(EntityUid handsUid, string handId, [NotNullWhen(true)] out Hand? hand, - HandsComponent? hands = null) + /// + /// Attempts to retrieve the associated hand struct corresponding to a hand ID on a given entity. + /// + public bool TryGetHand(Entity ent, [NotNullWhen(true)] string? handId, [NotNullWhen(true)] out Hand? hand) { hand = null; - if (!Resolve(handsUid, ref hands)) + if (handId == null) return false; - return hands.Hands.TryGetValue(handId, out hand); + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + if (!ent.Comp.Hands.TryGetValue(handId, out var handsHand)) + return false; + + hand = handsHand; + return true; + } + + /// + /// Gets the item currently held in the entity's specified hand. Returns null if no hands are present or there is no item. + /// + public EntityUid? GetHeldItem(Entity ent, string? handId) + { + TryGetHeldItem(ent, handId, out var held); + return held; + } + + /// + /// Gets the item currently held in the entity's specified hand. Returns false if no hands are present or there is no item. + /// + public bool TryGetHeldItem(Entity ent, string? handId, [NotNullWhen(true)] out EntityUid? held) + { + held = null; + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + // Sanity check to make sure this is actually a hand. + if (handId == null || !ent.Comp.Hands.ContainsKey(handId)) + return false; + + if (!ContainerSystem.TryGetContainer(ent, handId, out var container)) + return false; + + held = container.ContainedEntities.FirstOrNull(); + return held != null; + } + + public bool HandIsEmpty(Entity ent, string handId) + { + return GetHeldItem(ent, handId) == null; + } + + public int GetHandCount(Entity ent) + { + if (!Resolve(ent, ref ent.Comp, false)) + return 0; + + return ent.Comp.Hands.Count; + } + + public int CountFreeHands(Entity ent) + { + if (!Resolve(ent, ref ent.Comp, false)) + return 0; + + var free = 0; + foreach (var name in ent.Comp.Hands.Keys) + { + if (HandIsEmpty(ent, name)) + free++; + } + + return free; } public int CountFreeableHands(Entity hands) { var freeable = 0; - foreach (var hand in hands.Comp.Hands.Values) + foreach (var name in hands.Comp.Hands.Keys) { - if (hand.IsEmpty || CanDropHeld(hands, hand)) + if (HandIsEmpty(hands.AsNullable(), name) || CanDropHeld(hands, name)) freeable++; } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 2c75cef132..b91f56a836 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Database; using Content.Shared.Ghost; using Content.Shared.Hands; using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; using Content.Shared.Input; using Content.Shared.Interaction.Components; using Content.Shared.Interaction.Events; @@ -58,6 +59,7 @@ namespace Content.Shared.Interaction [Dependency] private readonly ISharedChatManager _chat = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly PullingSystem _pullSystem = default!; [Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!; @@ -343,7 +345,7 @@ namespace Content.Shared.Interaction public bool CombatModeCanHandInteract(EntityUid user, EntityUid? target) { // Always allow attack in these cases - if (target == null || !_handsQuery.TryComp(user, out var hands) || hands.ActiveHand?.HeldEntity is not null) + if (target == null || !_handsQuery.TryComp(user, out var hands) || _hands.GetActiveItem((user, hands)) is not null) return false; // Only eat input if: diff --git a/Content.Shared/Interaction/SmartEquipSystem.cs b/Content.Shared/Interaction/SmartEquipSystem.cs index 746bc994ee..797dc367b4 100644 --- a/Content.Shared/Interaction/SmartEquipSystem.cs +++ b/Content.Shared/Interaction/SmartEquipSystem.cs @@ -64,10 +64,10 @@ public sealed class SmartEquipSystem : EntitySystem return; // early out if we don't have any hands or a valid inventory slot - if (!TryComp(uid, out var hands) || hands.ActiveHand == null) + if (!TryComp(uid, out var hands) || hands.ActiveHandId == null) return; - var handItem = hands.ActiveHand.HeldEntity; + var handItem = _hands.GetActiveItem((uid, hands)); // can the user interact, and is the item interactable? e.g. virtual items if (!_actionBlocker.CanInteract(uid, handItem)) @@ -80,7 +80,7 @@ public sealed class SmartEquipSystem : EntitySystem } // early out if we have an item and cant drop it at all - if (handItem != null && !_hands.CanDropHeld(uid, hands.ActiveHand)) + if (hands.ActiveHandId != null && !_hands.CanDropHeld(uid, hands.ActiveHandId)) { _popup.PopupClient(Loc.GetString("smart-equip-cant-drop"), uid, uid); return; @@ -121,7 +121,7 @@ public sealed class SmartEquipSystem : EntitySystem return; } - _hands.TryDrop(uid, hands.ActiveHand, handsComp: hands); + _hands.TryDrop((uid, hands), hands.ActiveHandId!); _inventory.TryEquip(uid, handItem.Value, equipmentSlot, predicted: true, checkDoafter:true); return; } @@ -149,7 +149,7 @@ public sealed class SmartEquipSystem : EntitySystem return; } - _hands.TryDrop(uid, hands.ActiveHand, handsComp: hands); + _hands.TryDrop((uid, hands), hands.ActiveHandId!); _storage.Insert(slotItem, handItem.Value, out var stacked, out _); // if the hand item stacked with the things in inventory, but there's no more space left for the rest diff --git a/Content.Shared/Inventory/InventorySystem.Equip.cs b/Content.Shared/Inventory/InventorySystem.Equip.cs index f2fb3987b7..de24a64a1c 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -83,7 +83,7 @@ public abstract partial class InventorySystem if (!TryComp(actor, out InventoryComponent? inventory) || !TryComp(actor, out var hands)) return; - var held = hands.ActiveHandEntity; + var held = _handsSystem.GetActiveItem((actor, hands)); TryGetSlotEntity(actor, ev.Slot, out var itemUid, inventory); // attempt to perform some interaction @@ -115,7 +115,7 @@ public abstract partial class InventorySystem return; } - if (!_handsSystem.CanDropHeld(actor, hands.ActiveHand!, checkActionBlocker: false)) + if (!_handsSystem.CanDropHeld(actor, hands.ActiveHandId!, checkActionBlocker: false)) return; RaiseLocalEvent(held.Value, new HandDeselectedEvent(actor)); diff --git a/Content.Shared/Inventory/InventorySystem.Helpers.cs b/Content.Shared/Inventory/InventorySystem.Helpers.cs index 746342e2f1..44dede02c7 100644 --- a/Content.Shared/Inventory/InventorySystem.Helpers.cs +++ b/Content.Shared/Inventory/InventorySystem.Helpers.cs @@ -16,12 +16,9 @@ public partial class InventorySystem { if (Resolve(user.Owner, ref user.Comp1, false)) { - foreach (var hand in user.Comp1.Hands.Values) + foreach (var held in _handsSystem.EnumerateHeld(user)) { - if (hand.HeldEntity == null) - continue; - - yield return hand.HeldEntity.Value; + yield return held; } } diff --git a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs index 779ad18789..d76a4a3415 100644 --- a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs +++ b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.Hands; -using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; @@ -8,7 +7,6 @@ using Content.Shared.Inventory.Events; using Content.Shared.Item; using Content.Shared.Popups; using Robust.Shared.Containers; -using Robust.Shared.Network; using Robust.Shared.Prototypes; namespace Content.Shared.Inventory.VirtualItem; @@ -88,9 +86,9 @@ public abstract class SharedVirtualItemSystem : EntitySystem // if the user is holding the real item the virtual item points to, // we allow them to use it in the interaction - foreach (var hand in _handsSystem.EnumerateHands(args.User)) + foreach (var held in _handsSystem.EnumerateHeld(args.User)) { - if (hand.HeldEntity == ent.Comp.BlockingEntity) + if (held == ent.Comp.BlockingEntity) { args.Used = ent.Comp.BlockingEntity; return; @@ -112,7 +110,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem } /// - public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem, bool dropOthers = false, Hand? empty = null) + public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem, bool dropOthers = false, string? empty = null) { virtualItem = null; if (empty == null && !_handsSystem.TryGetEmptyHand(user, out empty)) @@ -122,7 +120,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem foreach (var hand in _handsSystem.EnumerateHands(user)) { - if (hand.HeldEntity is not { } held) + if (!_handsSystem.TryGetHeldItem(user, hand, out var held)) continue; if (held == blockingEnt) @@ -155,11 +153,11 @@ public abstract class SharedVirtualItemSystem : EntitySystem /// public void DeleteInHandsMatching(EntityUid user, EntityUid matching) { - foreach (var hand in _handsSystem.EnumerateHands(user)) + foreach (var held in _handsSystem.EnumerateHeld(user)) { - if (TryComp(hand.HeldEntity, out VirtualItemComponent? virt) && virt.BlockingEntity == matching) + if (TryComp(held, out VirtualItemComponent? virt) && virt.BlockingEntity == matching) { - DeleteVirtualItem((hand.HeldEntity.Value, virt), user); + DeleteVirtualItem((held, virt), user); } } } diff --git a/Content.Shared/Item/MultiHandedItemSystem.cs b/Content.Shared/Item/MultiHandedItemSystem.cs index da9d895dd2..f17ccdc922 100644 --- a/Content.Shared/Item/MultiHandedItemSystem.cs +++ b/Content.Shared/Item/MultiHandedItemSystem.cs @@ -1,5 +1,4 @@ using Content.Shared.Hands; -using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Popups; @@ -38,7 +37,7 @@ public sealed class MultiHandedItemSystem : EntitySystem private void OnAttemptPickup(Entity ent, ref GettingPickedUpAttemptEvent args) { - if (TryComp(args.User, out var hands) && hands.CountFreeHands() >= ent.Comp.HandsNeeded) + if (_hands.CountFreeHands(ent.Owner) >= ent.Comp.HandsNeeded) return; args.Cancel(); diff --git a/Content.Shared/Item/SharedItemSystem.cs b/Content.Shared/Item/SharedItemSystem.cs index c277bb7e87..9319755501 100644 --- a/Content.Shared/Item/SharedItemSystem.cs +++ b/Content.Shared/Item/SharedItemSystem.cs @@ -110,7 +110,7 @@ public abstract class SharedItemSystem : EntitySystem if (args.Handled) return; - args.Handled = _handsSystem.TryPickup(args.User, uid, animateUser: false); + args.Handled = _handsSystem.TryPickup(args.User, uid, null, animateUser: false); } private void AddPickupVerb(EntityUid uid, ItemComponent component, GetVerbsEvent args) diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index 09b289c2be..a860f1dcfb 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -434,7 +434,7 @@ public abstract class SharedMagicSystem : EntitySystem return; EntityUid? wand = null; - foreach (var item in _hands.EnumerateHeld(ev.Performer, handsComp)) + foreach (var item in _hands.EnumerateHeld((ev.Performer, handsComp))) { if (!_tag.HasTag(item, ev.WandTag)) continue; diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index bc505ee989..e1c07df3c3 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -109,16 +109,12 @@ public sealed class PullingSystem : EntitySystem // Try find hand that is doing this pull. // and clear it. - foreach (var hand in component.Hands.Values) + foreach (var held in _handsSystem.EnumerateHeld((uid, component))) { - if (hand.HeldEntity == null - || !TryComp(hand.HeldEntity, out VirtualItemComponent? virtualItem) - || virtualItem.BlockingEntity != args.PulledUid) - { + if (!TryComp(held, out VirtualItemComponent? virtualItem) || virtualItem.BlockingEntity != args.PulledUid) continue; - } - _handsSystem.TryDrop(args.PullerUid, hand, handsComp: component); + _handsSystem.TryDrop((args.PullerUid, component), held); break; } } diff --git a/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs b/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs index 8b892190b7..1f3cc3881d 100644 --- a/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Clothing.Components; using Content.Shared.CombatMode; using Content.Shared.Examine; using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Inventory.Events; using Content.Shared.Item.ItemToggle; @@ -19,6 +20,7 @@ public abstract class SharedNinjaGlovesSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedCombatModeSystem _combatMode = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; @@ -128,8 +130,7 @@ public abstract class SharedNinjaGlovesSystem : EntitySystem target = args.Target; return _timing.IsFirstTimePredicted && !_combatMode.IsInCombatMode(uid) - && TryComp(uid, out var hands) - && hands.ActiveHandEntity == null + && _hands.GetActiveItem(uid) == null && _interaction.InRangeUnobstructed(uid, target); } } diff --git a/Content.Shared/Nutrition/EntitySystems/FoodSystem.cs b/Content.Shared/Nutrition/EntitySystems/FoodSystem.cs index a4122168e4..6099939725 100644 --- a/Content.Shared/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/FoodSystem.cs @@ -459,7 +459,7 @@ public sealed class FoodSystem : EntitySystem var usedTypes = UtensilType.None; - foreach (var item in _hands.EnumerateHeld(user, hands)) + foreach (var item in _hands.EnumerateHeld((user, hands))) { // Is utensil? if (!TryComp(item, out var utensil)) diff --git a/Content.Shared/RCD/Systems/RCDSystem.cs b/Content.Shared/RCD/Systems/RCDSystem.cs index 6d85b171e0..68056ef267 100644 --- a/Content.Shared/RCD/Systems/RCDSystem.cs +++ b/Content.Shared/RCD/Systems/RCDSystem.cs @@ -5,7 +5,7 @@ using Content.Shared.Construction; using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.Examine; -using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Maps; using Content.Shared.Physics; @@ -35,6 +35,7 @@ public sealed class RCDSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedChargesSystem _sharedCharges = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly TurfSystem _turf = default!; @@ -296,11 +297,10 @@ public sealed class RCDSystem : EntitySystem var uid = GetEntity(ev.NetEntity); // Determine if player that send the message is carrying the specified RCD in their active hand - if (session.SenderSession.AttachedEntity == null) + if (session.SenderSession.AttachedEntity is not { } player) return; - if (!TryComp(session.SenderSession.AttachedEntity, out var hands) || - uid != hands.ActiveHand?.HeldEntity) + if (_hands.GetActiveItem(player) != uid) return; if (!TryComp(uid, out var rcd)) diff --git a/Content.Shared/RetractableItemAction/RetractableItemActionSystem.cs b/Content.Shared/RetractableItemAction/RetractableItemActionSystem.cs index c24da14c68..1820541746 100644 --- a/Content.Shared/RetractableItemAction/RetractableItemActionSystem.cs +++ b/Content.Shared/RetractableItemAction/RetractableItemActionSystem.cs @@ -1,7 +1,6 @@ using Content.Shared.Actions; using Content.Shared.Cuffs; using Content.Shared.Hands; -using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction.Components; using Content.Shared.Inventory; @@ -42,7 +41,7 @@ public sealed class RetractableItemActionSystem : EntitySystem private void OnRetractableItemAction(Entity ent, ref OnRetractableItemActionEvent args) { - if (_hands.GetActiveHand(args.Performer) is not { } userHand) + if (_hands.GetActiveHand(args.Performer) is not { } activeHand) return; if (_actions.GetAction(ent.Owner) is not { } action) @@ -55,7 +54,9 @@ public sealed class RetractableItemActionSystem : EntitySystem return; // Don't allow to summon an item if holding an unremoveable item unless that item is summoned by the action. - if (userHand.HeldEntity != null && !_hands.IsHolding(args.Performer, ent.Comp.ActionItemUid) && !_hands.CanDropHeld(args.Performer, userHand, false)) + if (_hands.GetActiveItem(ent.Owner) != null + && !_hands.IsHolding(args.Performer, ent.Comp.ActionItemUid) + && !_hands.CanDropHeld(args.Performer, activeHand, false)) { _popups.PopupClient(Loc.GetString("retractable-item-hand-cannot-drop"), args.Performer, args.Performer); return; @@ -67,7 +68,7 @@ public sealed class RetractableItemActionSystem : EntitySystem } else { - SummonRetractableItem(args.Performer, ent.Comp.ActionItemUid.Value, userHand, ent.Owner); + SummonRetractableItem(args.Performer, ent.Comp.ActionItemUid.Value, activeHand, ent.Owner); } args.Handled = true; @@ -93,7 +94,7 @@ public sealed class RetractableItemActionSystem : EntitySystem if (action.Comp.AttachedEntity == null) return; - if (_hands.GetActiveHand(action.Comp.AttachedEntity.Value) is not { } userHand) + if (_hands.GetActiveHand(action.Comp.AttachedEntity.Value) is not { }) return; RetractRetractableItem(action.Comp.AttachedEntity.Value, ent, action.Owner); @@ -128,7 +129,7 @@ public sealed class RetractableItemActionSystem : EntitySystem _audio.PlayPredicted(action.Comp.RetractSounds, holder, holder); } - private void SummonRetractableItem(EntityUid holder, EntityUid item, Hand hand, Entity action) + private void SummonRetractableItem(EntityUid holder, EntityUid item, string hand, Entity action) { if (!Resolve(action, ref action.Comp, false)) return; diff --git a/Content.Shared/Stacks/SharedStackSystem.cs b/Content.Shared/Stacks/SharedStackSystem.cs index c444b8936d..cd2f38d47f 100644 --- a/Content.Shared/Stacks/SharedStackSystem.cs +++ b/Content.Shared/Stacks/SharedStackSystem.cs @@ -146,7 +146,7 @@ namespace Content.Shared.Stacks } // This is shit code until hands get fixed and give an easy way to enumerate over items, starting with the currently active item. - foreach (var held in Hands.EnumerateHeld(user, hands)) + foreach (var held in Hands.EnumerateHeld((user, hands))) { TryMergeStacks(item, held, out _, donorStack: itemStack); diff --git a/Content.Shared/Station/SharedStationSpawningSystem.cs b/Content.Shared/Station/SharedStationSpawningSystem.cs index 389f696db2..a0a2257402 100644 --- a/Content.Shared/Station/SharedStationSpawningSystem.cs +++ b/Content.Shared/Station/SharedStationSpawningSystem.cs @@ -141,7 +141,7 @@ public abstract class SharedStationSpawningSystem : EntitySystem { var inhandEntity = EntityManager.SpawnEntity(prototype, coords); - if (_handsSystem.TryGetEmptyHand(entity, out var emptyHand, handsComponent)) + if (_handsSystem.TryGetEmptyHand((entity, handsComponent), out var emptyHand)) { _handsSystem.TryPickup(entity, inhandEntity, emptyHand, checkActionBlocker: false, handsComp: handsComponent); } diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 8143cb9cdd..adba19e047 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -689,7 +689,7 @@ public abstract class SharedStorageSystem : EntitySystem return; // If the user's active hand is empty, try pick up the item. - if (player.Comp.ActiveHandEntity == null) + if (!_sharedHandsSystem.TryGetActiveItem(player.AsNullable(), out var activeItem)) { _adminLog.Add( LogType.Storage, @@ -709,11 +709,11 @@ public abstract class SharedStorageSystem : EntitySystem _adminLog.Add( LogType.Storage, LogImpact.Low, - $"{ToPrettyString(player):player} is interacting with {ToPrettyString(item):item} while it is stored in {ToPrettyString(storage):storage} using {ToPrettyString(player.Comp.ActiveHandEntity):used}"); + $"{ToPrettyString(player):player} is interacting with {ToPrettyString(item):item} while it is stored in {ToPrettyString(storage):storage} using {ToPrettyString(activeItem):used}"); // Else, interact using the held item if (_interactionSystem.InteractUsing(player, - player.Comp.ActiveHandEntity.Value, + activeItem.Value, item, Transform(item).Coordinates, checkCanInteract: false)) @@ -1208,10 +1208,10 @@ public abstract class SharedStorageSystem : EntitySystem { if (!Resolve(ent.Owner, ref ent.Comp) || !Resolve(player.Owner, ref player.Comp) - || player.Comp.ActiveHandEntity == null) + || !_sharedHandsSystem.TryGetActiveItem(player, out var activeItem)) return false; - var toInsert = player.Comp.ActiveHandEntity; + var toInsert = activeItem; if (!CanInsert(ent, toInsert.Value, out var reason, ent.Comp)) { @@ -1219,7 +1219,7 @@ public abstract class SharedStorageSystem : EntitySystem return false; } - if (!_sharedHandsSystem.CanDrop(player, toInsert.Value, player.Comp)) + if (!_sharedHandsSystem.CanDrop(player, toInsert.Value)) { _popupSystem.PopupClient(Loc.GetString("comp-storage-cant-drop", ("entity", toInsert.Value)), ent, player); return false; @@ -1933,7 +1933,7 @@ public abstract class SharedStorageSystem : EntitySystem if (held) { - if (!_sharedHandsSystem.IsHolding(player, itemUid, out _)) + if (!_sharedHandsSystem.IsHolding(player.AsNullable(), itemUid, out _)) return false; } else diff --git a/Content.Shared/Strip/SharedStrippableSystem.cs b/Content.Shared/Strip/SharedStrippableSystem.cs index 2b971ae7bb..49be180503 100644 --- a/Content.Shared/Strip/SharedStrippableSystem.cs +++ b/Content.Shared/Strip/SharedStrippableSystem.cs @@ -104,8 +104,8 @@ public abstract class SharedStrippableSystem : EntitySystem var hasEnt = _inventorySystem.TryGetSlotEntity(strippable, args.Slot, out var held, inventory); - if (userHands.ActiveHandEntity != null && !hasEnt) - StartStripInsertInventory((user, userHands), strippable.Owner, userHands.ActiveHandEntity.Value, args.Slot); + if (_handsSystem.GetActiveItem((user, userHands)) is { } activeItem && !hasEnt) + StartStripInsertInventory((user, userHands), strippable.Owner, activeItem, args.Slot); else if (hasEnt) StartStripRemoveInventory(user, strippable.Owner, held!.Value, args.Slot); } @@ -124,11 +124,10 @@ public abstract class SharedStrippableSystem : EntitySystem if (!target.Comp.CanBeStripped) return; - if (!_handsSystem.TryGetHand(target.Owner, handId, out var handSlot)) - return; + var heldEntity = _handsSystem.GetHeldItem(target.Owner, handId); // Is the target a handcuff? - if (TryComp(handSlot.HeldEntity, out var virtualItem) && + if (TryComp(heldEntity, out var virtualItem) && TryComp(target.Owner, out var cuffable) && _cuffableSystem.GetAllCuffs(cuffable).Contains(virtualItem.BlockingEntity)) { @@ -136,10 +135,10 @@ public abstract class SharedStrippableSystem : EntitySystem return; } - if (user.Comp.ActiveHandEntity != null && handSlot.HeldEntity == null) - StartStripInsertHand(user, target, user.Comp.ActiveHandEntity.Value, handId, targetStrippable); - else if (handSlot.HeldEntity != null) - StartStripRemoveHand(user, target, handSlot.HeldEntity.Value, handId, targetStrippable); + if (_handsSystem.GetActiveItem(user.AsNullable()) is { } activeItem && heldEntity == null) + StartStripInsertHand(user, target, activeItem, handId, targetStrippable); + else if (heldEntity != null) + StartStripRemoveHand(user, target, heldEntity.Value, handId, targetStrippable); } /// @@ -154,16 +153,10 @@ public abstract class SharedStrippableSystem : EntitySystem if (!Resolve(user, ref user.Comp)) return false; - if (user.Comp.ActiveHand == null) + if (!_handsSystem.TryGetActiveItem(user, out var activeItem) || activeItem != held) return false; - if (user.Comp.ActiveHandEntity == null) - return false; - - if (user.Comp.ActiveHandEntity != held) - return false; - - if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHand)) + if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHandId!)) { _popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop")); return false; @@ -210,10 +203,14 @@ public abstract class SharedStrippableSystem : EntitySystem var (time, stealth) = GetStripTimeModifiers(user, target, held, slotDef.StripTime); if (!stealth) + { _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert", ("user", Identity.Entity(user, EntityManager)), - ("item", user.Comp.ActiveHandEntity!.Value)), - target, target, PopupType.Large); + ("item", _handsSystem.GetActiveItem((user, user.Comp))!.Value)), + target, + target, + PopupType.Large); + } var prefix = stealth ? "stealthily " : ""; _adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}place the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s {slot} slot"); @@ -246,7 +243,7 @@ public abstract class SharedStrippableSystem : EntitySystem if (!CanStripInsertInventory(user, target, held, slot)) return; - if (!_handsSystem.TryDrop(user, handsComp: user.Comp)) + if (!_handsSystem.TryDrop(user)) return; _inventorySystem.TryEquip(user, target, held, slot, triggerHandContact: true); @@ -305,10 +302,15 @@ public abstract class SharedStrippableSystem : EntitySystem if (IsStripHidden(slotDef, user)) _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-hidden", ("slot", slot)), target, target, PopupType.Large); else + { _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", Identity.Entity(user, EntityManager)), ("item", item)), - target, target, PopupType.Large); + target, + target, + PopupType.Large); + + } } var prefix = stealth ? "stealthily " : ""; @@ -368,23 +370,16 @@ public abstract class SharedStrippableSystem : EntitySystem if (!target.Comp.CanBeStripped) return false; - if (user.Comp.ActiveHand == null) + if (!_handsSystem.TryGetActiveItem(user, out var activeItem) || activeItem != held) return false; - if (user.Comp.ActiveHandEntity == null) - return false; - - if (user.Comp.ActiveHandEntity != held) - return false; - - if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHand)) + if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHandId!)) { _popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop")); return false; } - if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp) || - !_handsSystem.CanPickupToHand(target, user.Comp.ActiveHandEntity.Value, handSlot, checkActionBlocker: false, target.Comp)) + if (!_handsSystem.CanPickupToHand(target, activeItem.Value, handName, checkActionBlocker: false, target.Comp)) { _popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-put-message", ("owner", Identity.Entity(target, EntityManager)))); return false; @@ -414,10 +409,15 @@ public abstract class SharedStrippableSystem : EntitySystem var (time, stealth) = GetStripTimeModifiers(user, target, null, targetStrippable.HandStripDelay); if (!stealth) + { _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert-hand", ("user", Identity.Entity(user, EntityManager)), - ("item", user.Comp.ActiveHandEntity!.Value)), - target, target, PopupType.Large); + ("item", _handsSystem.GetActiveItem(user)!.Value)), + target, + target, + PopupType.Large); + + } var prefix = stealth ? "stealthily " : ""; _adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}place the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands"); @@ -452,7 +452,7 @@ public abstract class SharedStrippableSystem : EntitySystem if (!CanStripInsertHand(user, target, held, handName)) return; - _handsSystem.TryDrop(user, checkActionBlocker: false, handsComp: user.Comp); + _handsSystem.TryDrop(user, checkActionBlocker: false); _handsSystem.TryPickup(target, held, handName, checkActionBlocker: false, animateUser: stealth, animate: !stealth, handsComp: target.Comp); _adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has placed the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands"); @@ -474,22 +474,22 @@ public abstract class SharedStrippableSystem : EntitySystem if (!target.Comp.CanBeStripped) return false; - if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp)) + if (!_handsSystem.TryGetHand(target, handName, out _)) { _popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Identity.Entity(target, EntityManager)))); return false; } - if (HasComp(handSlot.HeldEntity)) + if (!_handsSystem.TryGetHeldItem(target, handName, out var heldEntity)) return false; - if (handSlot.HeldEntity == null) + if (HasComp(heldEntity)) return false; - if (handSlot.HeldEntity != item) + if (heldEntity != item) return false; - if (!_handsSystem.CanDropHeld(target, handSlot, false)) + if (!_handsSystem.CanDropHeld(target, handName, false)) { _popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Identity.Entity(target, EntityManager)))); return false; @@ -519,10 +519,13 @@ public abstract class SharedStrippableSystem : EntitySystem var (time, stealth) = GetStripTimeModifiers(user, target, null, targetStrippable.HandStripDelay); if (!stealth) + { _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", Identity.Entity(user, EntityManager)), ("item", item)), - target, target); + target, + target); + } var prefix = stealth ? "stealthily " : ""; _adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}strip the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands"); @@ -560,7 +563,7 @@ public abstract class SharedStrippableSystem : EntitySystem if (!CanStripRemoveHand(user, target, item, handName)) return; - _handsSystem.TryDrop(target, item, checkActionBlocker: false, handsComp: target.Comp); + _handsSystem.TryDrop(target, item, checkActionBlocker: false); _handsSystem.PickupOrDrop(user, item, animateUser: stealth, animate: !stealth, handsComp: user.Comp); _adminLogger.Add(LogType.Stripping, LogImpact.High, $"{ToPrettyString(user):actor} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands"); @@ -580,13 +583,17 @@ public abstract class SharedStrippableSystem : EntitySystem { if ( ev.Event.InsertOrRemove && !CanStripInsertInventory((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) || !ev.Event.InsertOrRemove && !CanStripRemoveInventory(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName)) - ev.Cancel(); + { + ev.Cancel(); + } } else { if ( ev.Event.InsertOrRemove && !CanStripInsertHand((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) || !ev.Event.InsertOrRemove && !CanStripRemoveHand(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName)) - ev.Cancel(); + { + ev.Cancel(); + } } } diff --git a/Content.Shared/UserInterface/ActivatableUISystem.cs b/Content.Shared/UserInterface/ActivatableUISystem.cs index 7eb195c0b1..a3f9a033df 100644 --- a/Content.Shared/UserInterface/ActivatableUISystem.cs +++ b/Content.Shared/UserInterface/ActivatableUISystem.cs @@ -108,10 +108,10 @@ public sealed partial class ActivatableUISystem : EntitySystem if (component.InHandsOnly) { - if (!_hands.IsHolding(args.User, uid, out var hand, args.Hands)) + if (!_hands.IsHolding((args.User, args.Hands), uid, out var hand )) return false; - if (component.RequireActiveHand && args.Hands.ActiveHand != hand) + if (component.RequireActiveHand && args.Hands.ActiveHandId != hand) return false; } } @@ -202,10 +202,10 @@ public sealed partial class ActivatableUISystem : EntitySystem if (!TryComp(user, out HandsComponent? hands)) return false; - if (!_hands.IsHolding(user, uiEntity, out var hand, hands)) + if (!_hands.IsHolding((user, hands), uiEntity, out var hand)) return false; - if (aui.RequireActiveHand && hands.ActiveHand != hand) + if (aui.RequireActiveHand && hands.ActiveHandId != hand) return false; } diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index cf9120f5f3..953abc8107 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Hands; using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Inventory; @@ -51,6 +52,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; [Dependency] protected readonly ActionBlockerSystem Blocker = default!; [Dependency] protected readonly DamageableSystem Damageable = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly MeleeSoundSystem _meleeSound = default!; [Dependency] protected readonly MobStateSystem MobState = default!; @@ -288,15 +290,14 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem } // Use inhands entity if we got one. - if (EntityManager.TryGetComponent(entity, out HandsComponent? hands) && - hands.ActiveHandEntity is { } held) + if (_hands.TryGetActiveItem(entity, out var held)) { // Make sure the entity is a weapon AND it doesn't need // to be equipped to be used (E.g boxing gloves). if (EntityManager.TryGetComponent(held, out melee) && !melee.MustBeEquippedToUse) { - weaponUid = held; + weaponUid = held.Value; return true; } @@ -858,9 +859,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem EntityUid? inTargetHand = null; - if (targetHandsComponent?.ActiveHand is { IsEmpty: false }) + if (_hands.TryGetActiveItem(target.Value, out var activeHeldEntity)) { - inTargetHand = targetHandsComponent.ActiveHand.HeldEntity!.Value; + inTargetHand = activeHeldEntity.Value; } var attemptEvent = new DisarmAttemptEvent(target.Value, user, inTargetHand); diff --git a/Content.Shared/Weapons/Misc/SharedGrapplingGunSystem.cs b/Content.Shared/Weapons/Misc/SharedGrapplingGunSystem.cs index e790973538..d27efa4d76 100644 --- a/Content.Shared/Weapons/Misc/SharedGrapplingGunSystem.cs +++ b/Content.Shared/Weapons/Misc/SharedGrapplingGunSystem.cs @@ -1,7 +1,7 @@ using System.Numerics; using Content.Shared.CombatMode; using Content.Shared.Hands; -using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Movement.Events; using Content.Shared.Physics; @@ -25,6 +25,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedJointSystem _joints = default!; [Dependency] private readonly SharedGunSystem _gun = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; @@ -80,9 +81,11 @@ public abstract class SharedGrapplingGunSystem : EntitySystem private void OnGrapplingReel(RequestGrapplingReelMessage msg, EntitySessionEventArgs args) { - var player = args.SenderSession.AttachedEntity; - if (!TryComp(player, out var hands) || - !TryComp(hands.ActiveHandEntity, out var grappling)) + if (args.SenderSession.AttachedEntity is not { } player) + return; + + if (!_hands.TryGetActiveItem(player, out var activeItem) || + !TryComp(activeItem, out var grappling)) { return; } @@ -94,7 +97,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem return; } - SetReeling(hands.ActiveHandEntity.Value, grappling, msg.Reeling, player.Value); + SetReeling(activeItem.Value, grappling, msg.Reeling, player); } private void OnWeightlessMove(ref CanWeightlessMoveEvent ev) diff --git a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs index f6702569bf..0f6baf06f2 100644 --- a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs +++ b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.ActionBlocker; using Content.Shared.Buckle.Components; using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Events; @@ -23,6 +24,7 @@ public abstract partial class SharedTetherGunSystem : EntitySystem { [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly ActionBlockerSystem _blocker = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly MobStateSystem _mob = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; @@ -139,14 +141,14 @@ public abstract partial class SharedTetherGunSystem : EntitySystem gunUid = null; gun = null; - if (!TryComp(user, out var hands) || - !TryComp(hands.ActiveHandEntity, out gun) || + if (!_hands.TryGetActiveItem(user, out var activeItem) || + !TryComp(activeItem, out gun) || _container.IsEntityInContainer(user)) { return false; } - gunUid = hands.ActiveHandEntity.Value; + gunUid = activeItem.Value; return true; } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index bebfe6f922..b6dee7fc1b 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -10,7 +10,7 @@ using Content.Shared.Damage; using Content.Shared.Examine; using Content.Shared.Gravity; using Content.Shared.Hands; -using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; using Content.Shared.Popups; using Content.Shared.Projectiles; using Content.Shared.Tag; @@ -49,6 +49,7 @@ public abstract partial class SharedGunSystem : EntitySystem [Dependency] protected readonly ISharedAdminLogManager Logs = default!; [Dependency] protected readonly DamageableSystem Damageable = default!; [Dependency] protected readonly ExamineSystemShared Examine = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly ItemSlotsSystem _slots = default!; [Dependency] private readonly RechargeBasicEntityAmmoSystem _recharge = default!; [Dependency] protected readonly SharedActionsSystem Actions = default!; @@ -173,8 +174,7 @@ public abstract partial class SharedGunSystem : EntitySystem gunEntity = default; gunComp = null; - if (EntityManager.TryGetComponent(entity, out HandsComponent? hands) && - hands.ActiveHandEntity is { } held && + if (_hands.GetActiveItem(entity) is { } held && TryComp(held, out GunComponent? gun)) { gunEntity = held; diff --git a/Content.Shared/Wieldable/SharedWieldableSystem.cs b/Content.Shared/Wieldable/SharedWieldableSystem.cs index 83ed58388a..c3375c3fe1 100644 --- a/Content.Shared/Wieldable/SharedWieldableSystem.cs +++ b/Content.Shared/Wieldable/SharedWieldableSystem.cs @@ -112,7 +112,7 @@ public abstract class SharedWieldableSystem : EntitySystem private void OnDeselectWieldable(EntityUid uid, WieldableComponent component, HandDeselectedEvent args) { - if (_hands.EnumerateHands(args.User).Count() > 2) + if (_hands.GetHandCount(uid) > 2) return; TryUnwield(uid, component, args.User); @@ -168,7 +168,7 @@ public abstract class SharedWieldableSystem : EntitySystem if (args.Hands == null || !args.CanAccess || !args.CanInteract) return; - if (!_hands.IsHolding(args.User, uid, out _, args.Hands)) + if (!_hands.IsHolding((args.User, args.Hands), uid, out _)) return; // TODO VERB TOOLTIPS Make CanWield or some other function return string, set as verb tooltip and disable @@ -252,7 +252,7 @@ public abstract class SharedWieldableSystem : EntitySystem } // Is it.. actually in one of their hands? - if (!_hands.IsHolding(user, uid, out _, hands)) + if (!_hands.IsHolding((user, hands), uid, out _)) { if (!quiet) _popup.PopupClient(Loc.GetString("wieldable-component-not-in-hands", ("item", uid)), user, user); @@ -373,7 +373,7 @@ public abstract class SharedWieldableSystem : EntitySystem /// If this is true we will bypass UnwieldAttemptEvent. public void UnwieldAll(Entity wielder, bool force = false) { - foreach (var held in _hands.EnumerateHeld(wielder.Owner, wielder.Comp)) + foreach (var held in _hands.EnumerateHeld(wielder)) { if (TryComp(held, out var wieldable)) TryUnwield(held, wieldable, wielder, force); diff --git a/Resources/Prototypes/Body/Prototypes/a_ghost.yml b/Resources/Prototypes/Body/Prototypes/a_ghost.yml deleted file mode 100644 index 7d358a5762..0000000000 --- a/Resources/Prototypes/Body/Prototypes/a_ghost.yml +++ /dev/null @@ -1,22 +0,0 @@ -- type: body - id: Aghost - name: "aghost" - root: torso - slots: - torso: - part: TorsoHuman - connections: - - right_arm - - left_arm - right_arm: - part: RightArmHuman - connections: - - right_hand - left_arm: - part: LeftArmHuman - connections: - - left_hand - right_hand: - part: RightHandHuman - left_hand: - part: LeftHandHuman diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index 2304c0d386..0a5577c6d3 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -22,6 +22,14 @@ canInteract: true - type: GhostHearing - type: Hands + hands: + hand_right: + location: Right + hand_left: + location: Left + sortedHands: + - hand_right + - hand_left - type: ComplexInteraction - type: Puller needsHands: false @@ -29,8 +37,6 @@ - type: Physics ignorePaused: true bodyType: Kinematic - - type: Body - prototype: Aghost - type: Access groups: - AllAccess From d998d71ce291bd72269bc3e18933011bb7e07b4d Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:38:57 +0200 Subject: [PATCH 087/191] Fix PowerCellDrawComponent draw rate (#38562) * fix power cell draw rate * comment --- Content.Server/PowerCell/PowerCellSystem.Draw.cs | 2 +- .../PowerCell/PowerCellDrawComponent.cs | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Content.Server/PowerCell/PowerCellSystem.Draw.cs b/Content.Server/PowerCell/PowerCellSystem.Draw.cs index ae43b6dae9..9156d30b1f 100644 --- a/Content.Server/PowerCell/PowerCellSystem.Draw.cs +++ b/Content.Server/PowerCell/PowerCellSystem.Draw.cs @@ -28,7 +28,7 @@ public sealed partial class PowerCellSystem if (!TryGetBatteryFromSlot(uid, out var batteryEnt, out var battery, slot)) continue; - if (_battery.TryUseCharge(batteryEnt.Value, comp.DrawRate, battery)) + if (_battery.TryUseCharge(batteryEnt.Value, comp.DrawRate * (float)comp.Delay.TotalSeconds, battery)) continue; var ev = new PowerCellSlotEmptyEvent(); diff --git a/Content.Shared/PowerCell/PowerCellDrawComponent.cs b/Content.Shared/PowerCell/PowerCellDrawComponent.cs index 7af44420a7..cecf23d041 100644 --- a/Content.Shared/PowerCell/PowerCellDrawComponent.cs +++ b/Content.Shared/PowerCell/PowerCellDrawComponent.cs @@ -18,13 +18,13 @@ public sealed partial class PowerCellDrawComponent : Component /// /// Whether there is any charge available to draw. /// - [ViewVariables(VVAccess.ReadWrite), DataField("canDraw"), AutoNetworkedField] + [DataField, AutoNetworkedField] public bool CanDraw; /// /// Whether there is sufficient charge to use. /// - [ViewVariables(VVAccess.ReadWrite), DataField("canUse"), AutoNetworkedField] + [DataField, AutoNetworkedField] public bool CanUse; #endregion @@ -37,17 +37,20 @@ public sealed partial class PowerCellDrawComponent : Component public bool Enabled = true; /// - /// How much the entity draws while the UI is open. + /// How much the entity draws while the UI is open (in Watts). /// Set to 0 if you just wish to check for power upon opening the UI. /// - [ViewVariables(VVAccess.ReadWrite), DataField("drawRate")] + [DataField] public float DrawRate = 1f; /// - /// How much power is used whenever the entity is "used". + /// How much power is used whenever the entity is "used" (in Joules). /// This is used to ensure the UI won't open again without a minimum use power. /// - [ViewVariables(VVAccess.ReadWrite), DataField("useRate")] + /// + /// This is not a rate how the datafield name implies, but a one-time cost. + /// + [DataField] public float UseRate; /// From 296283c0b191017826290922bb89570addcb6c3e Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 25 Jun 2025 17:41:19 +0200 Subject: [PATCH 088/191] Hand refactor bugfix (#38576) --- Content.Shared/Wieldable/SharedWieldableSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Wieldable/SharedWieldableSystem.cs b/Content.Shared/Wieldable/SharedWieldableSystem.cs index c3375c3fe1..0a49622f8b 100644 --- a/Content.Shared/Wieldable/SharedWieldableSystem.cs +++ b/Content.Shared/Wieldable/SharedWieldableSystem.cs @@ -112,7 +112,7 @@ public abstract class SharedWieldableSystem : EntitySystem private void OnDeselectWieldable(EntityUid uid, WieldableComponent component, HandDeselectedEvent args) { - if (_hands.GetHandCount(uid) > 2) + if (_hands.GetHandCount(args.User) > 2) return; TryUnwield(uid, component, args.User); From 747dfe901e810a11afdb1b15cb1721875e891f5b Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 25 Jun 2025 18:26:20 +0200 Subject: [PATCH 089/191] Fix hand refactor 2 (#38578) * fix unwielding * hand refactor bugfix 2 --- Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs index 4a24b9de02..4f259a3bde 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs @@ -296,7 +296,7 @@ public abstract partial class SharedHandsSystem if (handId == ent.Comp.ActiveHandId) return false; - if (TryGetHeldItem(ent, handId, out var oldHeld)) + if (TryGetActiveItem(ent, out var oldHeld)) RaiseLocalEvent(oldHeld.Value, new HandDeselectedEvent(ent)); if (handId == null) From e3580f076aff71515cd5f4d774f0a933e469416d Mon Sep 17 00:00:00 2001 From: rumaks Date: Wed, 25 Jun 2025 17:52:32 +0000 Subject: [PATCH 090/191] Added a ConfirmableAction component to ActionRevertPolymorph (#38570) * Added a ConfirmableAction component to ActionRevertPolymorph * Update Resources/Locale/en-US/actions/actions/polymorph.ftl --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Resources/Locale/en-US/actions/actions/polymorph.ftl | 2 +- Resources/Prototypes/Actions/polymorph.yml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Resources/Locale/en-US/actions/actions/polymorph.ftl b/Resources/Locale/en-US/actions/actions/polymorph.ftl index 4a65a60ecf..129d8995b5 100644 --- a/Resources/Locale/en-US/actions/actions/polymorph.ftl +++ b/Resources/Locale/en-US/actions/actions/polymorph.ftl @@ -1 +1 @@ -gera-transformation-popup = This action will transform you. Use it again to confirm. +revert-polymorph-action-popup = This action is irreversible. Use it again to confirm. diff --git a/Resources/Prototypes/Actions/polymorph.yml b/Resources/Prototypes/Actions/polymorph.yml index 2f931e29e2..343f6bdcf4 100644 --- a/Resources/Prototypes/Actions/polymorph.yml +++ b/Resources/Prototypes/Actions/polymorph.yml @@ -4,6 +4,8 @@ name: Revert description: Revert back into your original form. components: + - type: ConfirmableAction + popup: revert-polymorph-action-popup - type: InstantAction event: !type:RevertPolymorphActionEvent From 76d11cc0e1fdccc38c3981cd612a32f4a976c68e Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 25 Jun 2025 17:53:42 +0000 Subject: [PATCH 091/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 93ea1d6c96..2c53d5863e 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: sowelipililimute - changes: - - message: The medical and security techfabs now announce new recipes that they - can fabricate as they are researched - type: Add - id: 8201 - time: '2025-04-16T19:29:25.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/34959 - author: Linkbro1 changes: - message: changed MV and HV cable item sprites, changed MV cable structure sprites @@ -3883,3 +3875,10 @@ id: 8713 time: '2025-06-25T01:46:17.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38557 +- author: rumaks-xyz + changes: + - message: The action of reverting from a polymorph now has to be confirmed. + type: Tweak + id: 8714 + time: '2025-06-25T17:52:32.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38570 From 1c58b6efc7df8b09f356a885886beef0bb0df7eb Mon Sep 17 00:00:00 2001 From: poklj Date: Wed, 25 Jun 2025 16:50:47 -0300 Subject: [PATCH 092/191] Fixup Sericulture to be clonable (#38516) * Add CloningEvent and an action entity prototype * Remove redundant action prototype from Yaml * Add a field that might be changed * CR * CR - guard statement and Dirty --- .../Sericulture/SericultureComponent.cs | 4 ++-- Content.Shared/Sericulture/SericultureSystem.cs | 16 ++++++++++++++++ .../Entities/Mobs/Species/arachnid.yml | 1 - 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Sericulture/SericultureComponent.cs b/Content.Shared/Sericulture/SericultureComponent.cs index e0cc2b7d91..a07e96a34c 100644 --- a/Content.Shared/Sericulture/SericultureComponent.cs +++ b/Content.Shared/Sericulture/SericultureComponent.cs @@ -31,10 +31,10 @@ public sealed partial class SericultureComponent : Component /// /// The entity needed to actually preform sericulture. This will be granted (and removed) upon the entity's creation. /// - [DataField(required: true)] + [DataField] [ViewVariables(VVAccess.ReadWrite)] [AutoNetworkedField] - public EntProtoId Action; + public EntProtoId Action = "ActionSericulture"; [AutoNetworkedField] [DataField("actionEntity")] diff --git a/Content.Shared/Sericulture/SericultureSystem.cs b/Content.Shared/Sericulture/SericultureSystem.cs index 8c10d0f3d0..e5942a433e 100644 --- a/Content.Shared/Sericulture/SericultureSystem.cs +++ b/Content.Shared/Sericulture/SericultureSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Actions; +using Content.Shared.Cloning.Events; using Content.Shared.DoAfter; using Content.Shared.Nutrition.EntitySystems; using Robust.Shared.Serialization; @@ -32,6 +33,21 @@ public abstract partial class SharedSericultureSystem : EntitySystem SubscribeLocalEvent(OnCompRemove); SubscribeLocalEvent(OnSericultureStart); SubscribeLocalEvent(OnSericultureDoAfter); + SubscribeLocalEvent(OnClone); + } + + private void OnClone(Entity ent, ref CloningEvent args) + { + if(!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) + return; + + var comp = EnsureComp(args.CloneUid); + comp.PopupText = ent.Comp.PopupText; + comp.ProductionLength = ent.Comp.ProductionLength; + comp.HungerCost = ent.Comp.HungerCost; + comp.EntityProduced = ent.Comp.EntityProduced; + comp.MinHungerThreshold = ent.Comp.MinHungerThreshold; + Dirty(args.CloneUid, comp); } /// diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml index a93b8e802e..110e6513da 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml @@ -13,7 +13,6 @@ - type: Hunger - type: Thirst - type: Sericulture - action: ActionSericulture productionLength: 2 entityProduced: MaterialWebSilk1 hungerCost: 4 # Should total to 25 total silk on full hunger From 433893679e0b1aa2c750a4e3c0f52883b0876b5f Mon Sep 17 00:00:00 2001 From: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:02:01 -0400 Subject: [PATCH 093/191] Convert DumpReagentGuideText command to LEC. (#38569) * commit * revert linq conversion --- .../Commands/DumpReagentGuideText.cs | 20 +++++++++---------- .../dump-reagent-guide-text-command.ftl | 4 ++++ 2 files changed, 14 insertions(+), 10 deletions(-) create mode 100644 Resources/Locale/en-US/commands/dump-reagent-guide-text-command.ftl diff --git a/Content.Server/Chemistry/Commands/DumpReagentGuideText.cs b/Content.Server/Chemistry/Commands/DumpReagentGuideText.cs index 563708b8f3..a70c2196ab 100644 --- a/Content.Server/Chemistry/Commands/DumpReagentGuideText.cs +++ b/Content.Server/Chemistry/Commands/DumpReagentGuideText.cs @@ -7,32 +7,31 @@ using Robust.Shared.Prototypes; namespace Content.Server.Chemistry.Commands; [AdminCommand(AdminFlags.Debug)] -public sealed class DumpReagentGuideText : IConsoleCommand +public sealed class DumpReagentGuideText : LocalizedEntityCommands { [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IEntitySystemManager _entSys = default!; - public string Command => "dumpreagentguidetext"; - public string Description => "Dumps the guidebook text for a reagent to the console"; - public string Help => "dumpreagentguidetext "; + public override string Command => "dumpreagentguidetext"; - public void Execute(IConsoleShell shell, string argStr, string[] args) + public override void Execute(IConsoleShell shell, string argStr, string[] args) { if (args.Length != 1) { - shell.WriteError("Must have only 1 argument"); + shell.WriteError(Loc.GetString($"shell-need-exactly-one-argument")); return; } if (!_prototype.TryIndex(args[0], out var reagent)) { - shell.WriteError($"Invalid prototype: {args[0]}"); + shell.WriteError(Loc.GetString($"shell-argument-must-be-prototype", + ("index", args[0]), + ("prototype", nameof(ReagentPrototype)))); return; } if (reagent.Metabolisms is null) { - shell.WriteLine("Nothing to dump."); + shell.WriteLine(Loc.GetString($"cmd-dumpreagentguidetext-nothing-to-dump")); return; } @@ -40,7 +39,8 @@ public sealed class DumpReagentGuideText : IConsoleCommand { foreach (var effect in entry.Effects) { - shell.WriteLine(effect.GuidebookEffectDescription(_prototype, _entSys) ?? $"[skipped effect of type {effect.GetType()}]"); + shell.WriteLine(effect.GuidebookEffectDescription(_prototype, EntityManager.EntitySysManager) ?? + Loc.GetString($"cmd-dumpreagentguidetext-skipped", ("effect", effect.GetType()))); } } } diff --git a/Resources/Locale/en-US/commands/dump-reagent-guide-text-command.ftl b/Resources/Locale/en-US/commands/dump-reagent-guide-text-command.ftl new file mode 100644 index 0000000000..694854c1cd --- /dev/null +++ b/Resources/Locale/en-US/commands/dump-reagent-guide-text-command.ftl @@ -0,0 +1,4 @@ +cmd-dumpreagentguidetext-desc = Dumps the guidebook text for a reagent to the console. +cmd-dumpreagentguidetext-help = Usage: dumpreagentguidetext +cmd-dumpreagentguidetext-nothing-to-dump = Nothing to dump. +cmd-dumpreagentguidetext-skipped = [skipped effect of type {$effect}] From 1ba01918086c5eb3b0bb201c2c26e633701750ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20M=C4=99drek?= Date: Wed, 25 Jun 2025 20:22:05 +0000 Subject: [PATCH 094/191] fix: EyeOffset when eyes are closed (#38534) * fix: EyeOffset when eyes are closed * fix: Relay only blocked on eyes closed action * cleanup: whitespace * fix: missing cancel on PVS, dependencies * remove: namespace import * change: apply from review * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Content.Shared/Camera/GetEyeOffsetEvent.cs | 7 +++++++ Content.Shared/Camera/GetEyePvsScaleEvent.cs | 7 +++++++ .../Eye/Blinding/Systems/BlindableSystem.cs | 15 +++++++++++++++ .../Movement/Systems/SharedContentEyeSystem.cs | 18 ++++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/Content.Shared/Camera/GetEyeOffsetEvent.cs b/Content.Shared/Camera/GetEyeOffsetEvent.cs index 0e3c00110a..fc118315ec 100644 --- a/Content.Shared/Camera/GetEyeOffsetEvent.cs +++ b/Content.Shared/Camera/GetEyeOffsetEvent.cs @@ -19,6 +19,13 @@ namespace Content.Shared.Camera; [ByRefEvent] public record struct GetEyeOffsetEvent(Vector2 Offset); +/// +/// Raised before the and , to check if any of the subscribed +/// systems want to cancel offset changes. +/// +[ByRefEvent] +public record struct GetEyeOffsetAttemptEvent(bool Cancelled); + /// /// Raised on any equipped and in-hand items that may modify the eye offset. /// Pockets and suitstorage are excluded. diff --git a/Content.Shared/Camera/GetEyePvsScaleEvent.cs b/Content.Shared/Camera/GetEyePvsScaleEvent.cs index 482b755db8..4e8c48ade1 100644 --- a/Content.Shared/Camera/GetEyePvsScaleEvent.cs +++ b/Content.Shared/Camera/GetEyePvsScaleEvent.cs @@ -20,6 +20,13 @@ namespace Content.Shared.Camera; [ByRefEvent] public record struct GetEyePvsScaleEvent(float Scale); +/// +/// Raised before the and , to check if any on the subscribed +/// systems want to cancel PVS changes. +/// +[ByRefEvent] +public record struct GetEyePvsScaleAttemptEvent(bool Cancelled); + /// /// Raised on any equipped and in-hand items that may modify the eye offset. /// Pockets and suitstorage are excluded. diff --git a/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs b/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs index 24eed3adcf..ae81226a4b 100644 --- a/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Camera; using Content.Shared.Eye.Blinding.Components; using Content.Shared.Inventory; using Content.Shared.Rejuvenate; @@ -15,6 +16,8 @@ public sealed class BlindableSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnRejuvenate); SubscribeLocalEvent(OnDamageChanged); + SubscribeLocalEvent(OnGetEyePvsScaleAttemptEvent); + SubscribeLocalEvent(OnGetEyeOffsetAttemptEvent); } private void OnRejuvenate(Entity ent, ref RejuvenateEvent args) @@ -28,6 +31,18 @@ public sealed class BlindableSystem : EntitySystem _eyelids.UpdateEyesClosable((ent.Owner, ent.Comp)); } + private void OnGetEyePvsScaleAttemptEvent(Entity ent, ref GetEyePvsScaleAttemptEvent args) + { + if (ent.Comp.IsBlind) + args.Cancelled = true; + } + + private void OnGetEyeOffsetAttemptEvent(Entity ent, ref GetEyeOffsetAttemptEvent args) + { + if (ent.Comp.IsBlind) + args.Cancelled = true; + } + [PublicAPI] public void UpdateIsBlind(Entity blindable) { diff --git a/Content.Shared/Movement/Systems/SharedContentEyeSystem.cs b/Content.Shared/Movement/Systems/SharedContentEyeSystem.cs index 71bc65a79e..8063948eea 100644 --- a/Content.Shared/Movement/Systems/SharedContentEyeSystem.cs +++ b/Content.Shared/Movement/Systems/SharedContentEyeSystem.cs @@ -142,6 +142,15 @@ public abstract class SharedContentEyeSystem : EntitySystem public void UpdateEyeOffset(Entity eye) { + var evAttempt = new GetEyeOffsetAttemptEvent(); + RaiseLocalEvent(eye, ref evAttempt); + + if (evAttempt.Cancelled) + { + _eye.SetOffset(eye, Vector2.Zero, eye); + return; + } + var ev = new GetEyeOffsetEvent(); RaiseLocalEvent(eye, ref ev); @@ -156,6 +165,15 @@ public abstract class SharedContentEyeSystem : EntitySystem if (!Resolve(uid, ref contentEye) || !Resolve(uid, ref eye)) return; + var evAttempt = new GetEyePvsScaleAttemptEvent(); + RaiseLocalEvent(uid, ref evAttempt); + + if (evAttempt.Cancelled) + { + _eye.SetPvsScale((uid, eye), 1); + return; + } + var ev = new GetEyePvsScaleEvent(); RaiseLocalEvent(uid, ref ev); From a708e09b179936d49d93df3d3e2c334a54dd1289 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 25 Jun 2025 20:23:13 +0000 Subject: [PATCH 095/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 2c53d5863e..7226b0adbc 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Linkbro1 - changes: - - message: changed MV and HV cable item sprites, changed MV cable structure sprites - type: Tweak - id: 8202 - time: '2025-04-16T20:26:25.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/34955 - author: ciaran changes: - message: Fixed duplicate crew monitor console entries for crew with tracking implants @@ -3882,3 +3875,10 @@ id: 8714 time: '2025-06-25T17:52:32.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38570 +- author: Lukasz825700516 + changes: + - message: Closed eyes and binoculars no longer allow you to see through walls. + type: Fix + id: 8715 + time: '2025-06-25T20:22:05.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38534 From 69a426c63036d315d5352344cb1edf5486620061 Mon Sep 17 00:00:00 2001 From: Hitlinemoss <209321380+Hitlinemoss@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:27:27 -0400 Subject: [PATCH 096/191] Updated hat descriptions (#38156) * Updated hat descriptions * final touches * typo fix * final touchups * Quote fixes --- .../Entities/Clothing/Head/hats.yml | 86 +++++++++---------- .../Entities/Clothing/Head/soft.yml | 30 +++---- .../Entities/Objects/Weapons/Melee/knife.yml | 2 +- 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index 9bc40688e4..ec7801802b 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -2,7 +2,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBeaverHat name: beaver hat - description: 'Gentlemen?' + description: "\"Gentlemen?\"" components: - type: Sprite sprite: Clothing/Head/Hats/beaver_hat.rsi @@ -13,7 +13,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBeret name: beret - description: A beret, an artists favorite headwear. + description: An artist's favorite headwear. components: - type: Sprite sprite: Clothing/Head/Hats/beret.rsi @@ -30,7 +30,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBeretFrench name: french beret - description: A French beret, "vive la France". + description: A French beret. "Vive la France!" components: - type: Sprite sprite: Clothing/Head/Hats/beret_french.rsi @@ -48,7 +48,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBeretSecurity name: security beret - description: A stylish clothing option for security officers. + description: A beret with the security department's insignia. For officers that are more inclined towards style than safety. components: - type: Sprite sprite: Clothing/Head/Hats/beret_security.rsi @@ -65,7 +65,7 @@ parent: ClothingHeadBase id: ClothingHeadHatSecurityTrooper name: trooper hat - description: A campaign hat for the Nanotrasen Troopers, comes with a case too, but you lost it. + description: A campaign hat for the Nanotrasen Troopers. It's supposed to come with a case, too... components: - type: Sprite sprite: Clothing/Head/Hats/security_trooper_hat.rsi @@ -108,7 +108,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBeretEngineering name: engineering beret - description: A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety. + description: A beret with the engineering department's insignia. For engineers that are more inclined towards style than safety. components: - type: Sprite sprite: Clothing/Head/Hats/beret_engineering.rsi @@ -125,7 +125,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBeretQM name: quartermaster's beret - description: A beret with the cargo's insignia emblazoned on it. For quartermasters that are more inclined towards style. + description: A beret with the cargo department's insignia. components: - type: Sprite sprite: Clothing/Head/Hats/beret_qm.rsi @@ -159,7 +159,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBeretWarden name: warden's beret - description: A corporate blue beret with a warden's rank emblem. For officers that are more inclined towards style than safety. + description: A corporate-blue beret with a warden's rank emblem. For officers that are more inclined towards style than safety. components: - type: Sprite sprite: Clothing/Head/Hats/beret_warden.rsi @@ -181,7 +181,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBeretBrigmedic name: brigmedical beret - description: White beret, looks like a cream pie on the head. + description: A white beret for brigmedics. components: - type: Sprite sprite: Clothing/Head/Hats/beret_brigmedic.rsi @@ -192,7 +192,7 @@ parent: [ ClothingHeadBase ] id: ClothingHeadHatBeretMerc name: mercenary beret - description: Olive beret, the badge depicts a jackal on a rock. + description: An olive beret with a badge depicting a jackal on a rock. components: - type: Sprite sprite: Clothing/Head/Hats/beret_merc.rsi @@ -203,7 +203,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBeretCommand name: command beret - description: A beret with a Command insignia emblazoned on it. It has an aura of authority. + description: A beret with the Nanotrasen logo. It has an aura of authority. components: - type: Sprite sprite: Clothing/Head/Hats/beret_command.rsi @@ -219,7 +219,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBowlerHat name: bowler hat - description: Stylish bowler hat. + description: A stylish bowler hat. components: - type: Sprite sprite: Clothing/Head/Hats/bowler_hat.rsi @@ -230,7 +230,7 @@ parent: [ClothingHeadBase, BaseCommandContraband] id: ClothingHeadHatCaptain name: captain's hardhat - description: It's good being the king. + description: Feels good being the king. components: - type: Sprite sprite: Clothing/Head/Hats/captain.rsi @@ -262,7 +262,7 @@ parent: [ ClothingHeadBase, BaseCentcommContraband ] id: ClothingHeadHatCentcom name: CentComm brand hat - description: "It's good to be the emperor." + description: Feels good being the king's boss. components: - type: Sprite sprite: Clothing/Head/Hats/centcom.rsi @@ -273,7 +273,7 @@ parent: ClothingHeadBase id: ClothingHeadHatChef name: chef's hat - description: "It's a hat used by chefs to keep hair out of your food. Judging by the food in the mess, they don't work." + description: A hat used by chefs to keep their hair out of your food. Judging by the food in the mess, they don't work. components: - type: Sprite sprite: Clothing/Head/Hats/chefhat.rsi @@ -304,7 +304,7 @@ parent: ClothingHeadBase id: ClothingHeadHatFedoraBrown name: brown fedora - description: It's a brown fedora. + description: A brown fedora. components: - type: Sprite sprite: Clothing/Head/Hats/brownfedora.rsi @@ -321,7 +321,7 @@ parent: ClothingHeadBase id: ClothingHeadHatFedoraGrey name: grey fedora - description: It's a grey fedora. + description: A grey fedora. components: - type: Sprite sprite: Clothing/Head/Hats/greyfedora.rsi @@ -343,7 +343,7 @@ parent: [ClothingHeadBase, BaseCommandContraband] id: ClothingHeadHatHopcap name: head of personnel's cap - description: A grand, stylish head of personnel's cap. + description: A stylish cap worn by the Head of Personnel. Shows paperwork who's in charge. components: - type: Sprite sprite: Clothing/Head/Hats/hopcap.rsi @@ -360,7 +360,7 @@ parent: [ClothingHeadBase, BaseCommandContraband] id: ClothingHeadHatHoshat name: head of security cap - description: The robust standard-issue cap of the Head of Security. For showing the officers who's in charge. + description: A stylish cap worn by the Head of Security. Shows the officers who's in charge. components: - type: Sprite sprite: Clothing/Head/Hats/hoshat.rsi @@ -412,7 +412,7 @@ parent: ClothingHeadBase id: ClothingHeadHatPirate name: pirate hat - description: 'Yo ho ho and a bottle of rum!' + description: "\"Yo ho ho and a bottle of rum!\"" components: - type: Sprite sprite: Clothing/Head/Hats/pirate.rsi @@ -444,7 +444,7 @@ parent: ClothingHeadHatWizardBase id: ClothingHeadHatRedwizard name: red wizard hat - description: Strange-looking red hat-wear that most certainly belongs to a real magic user. + description: A strange-looking red hat that most certainly belongs to a powerful magic-user. components: - type: Sprite sprite: Clothing/Head/Hats/redwizard.rsi @@ -491,7 +491,7 @@ parent: ClothingHeadBase id: ClothingHeadHatSombrero name: sombrero - description: "Perfectly for Space Mexico, si?" + description: "\"Perfect for Space Mexico, si?\"" components: - type: Sprite sprite: Clothing/Head/Hats/sombrero.rsi @@ -576,7 +576,7 @@ parent: [ClothingHeadBase, BaseFoldable] id: ClothingHeadHatUshanka name: ushanka - description: "Perfect for winter in Siberia, da?" + description: "\"Perfect for winter in Siberia, da?\"" components: - type: Clothing sprite: Clothing/Head/Hats/ushanka.rsi @@ -639,7 +639,7 @@ parent: ClothingHeadHatWizardBase id: ClothingHeadHatVioletwizard name: violet wizard hat - description: "Strange-looking violet hat-wear that most certainly belongs to a real magic user." + description: A strange-looking violet hat that most certainly belongs to a powerful magic-user. components: - type: Sprite sprite: Clothing/Head/Hats/violetwizard.rsi @@ -655,7 +655,7 @@ parent: ClothingHeadBase id: ClothingHeadHatWarden name: warden's cap - description: A police officer's Hat. This hat emphasizes that you are THE LAW + description: This hat emphasizes that you are THE LAW. components: - type: Sprite sprite: Clothing/Head/Hats/warden.rsi @@ -688,7 +688,7 @@ parent: ClothingHeadBase id: ClothingHeadHatWizardFake name: fake wizard hat - description: It has WIZZARD written across it in sequins. Comes with a cool beard. + description: It has "WIZZARD" written across it in garish sequins. Comes with a cool beard. components: - type: Sprite sprite: Clothing/Head/Hats/wizard_fake.rsi @@ -711,7 +711,7 @@ parent: ClothingHeadHatWizardBase id: ClothingHeadHatWizard name: wizard hat - description: Strange-looking blue hat-wear that most certainly belongs to a powerful magic user. + description: A strange-looking blue hat that most certainly belongs to a powerful magic-user. components: - type: Sprite sprite: Clothing/Head/Hats/wizardhat.rsi @@ -729,7 +729,7 @@ parent: ClothingHeadBase id: ClothingHeadHatXmasCrown name: xmas crown - description: 'Happy Christmas!' + description: "\"Happy Christmas!\"" components: - type: Sprite sprite: Clothing/Head/Hats/xmascrown.rsi @@ -740,7 +740,7 @@ parent: ClothingHeadBase id: ClothingHeadHatTrucker name: trucker hat - description: Formerly Chucks, this hat is yours now. + description: Formerly Chuck's, this hat is yours now. components: - type: Sprite sprite: Clothing/Head/Hats/truckershat.rsi @@ -799,7 +799,7 @@ parent: ClothingHeadBase id: ClothingHeadPaperSack name: papersack hat - description: A paper sack with crude holes cut out for eyes. Useful for hiding one's identity or ugliness. + description: A paper sack with crude holes cut out for eyes. Useful for hiding one's identity and/or ugliness. components: - type: Sprite sprite: Clothing/Head/Hats/papersack.rsi @@ -833,7 +833,7 @@ parent: ClothingHeadBase id: ClothingHeadFishCap name: fishing cap - description: Women fear me. Fish fear me. Men turn their eyes away from me. As I walk no beast dares make a sound in my presence. I am alone on this barren Earth. + description: "\"Women fear me. Fish fear me. Men turn their eyes away from me. As I walk no beast dares make a sound in my presence. I am alone on this barren Earth.\"" components: - type: Sprite sprite: Clothing/Head/Hats/fishcap.rsi @@ -844,7 +844,7 @@ parent: ClothingHeadBase id: ClothingHeadWehcellentCap name: wehcellent cap - description: It was a net hat! Tiders wear them all the time! It's got airholes in the back to keep a cross-breeze going and everything! + description: "\"It was a net hat! Tiders wear them all the time! It's got airholes in the back to keep a cross-breeze going and everything!\"" components: - type: Sprite sprite: Clothing/Head/Hats/wehcellentcap.rsi @@ -899,7 +899,7 @@ parent: ClothingHeadBase id: ClothingHeadHatJester name: jester hat - description: A hat with bells, to add some merriness to the suit. + description: A hat with bells, to add some merriness to your head. components: - type: Sprite sprite: Clothing/Head/Hats/jester.rsi @@ -924,7 +924,7 @@ parent: [ClothingHeadBase, BaseCommandContraband] id: ClothingHeadHatBeretCmo name: chief medical officer's beret - description: Turquoise beret with a cross on the front. The sight of it calms you down and makes it clear that you will be cured. + description: A turquoise beret with a cross on the front. The sight of it calms you down and makes it clear that you will be cured. components: - type: Sprite sprite: Clothing/Head/Hats/beret_cmo.rsi @@ -941,7 +941,7 @@ parent: ClothingHeadBase id: ClothingHeadHatPirateTricord name: pirate hat - description: 'Yo ho ho and a bottle of rum!' + description: "\"Yo ho ho and a bottle of rum!\"" components: - type: Sprite sprite: Clothing/Head/Hats/piratetricord.rsi @@ -1004,7 +1004,7 @@ parent: ClothingHeadBase id: ClothingHeadHatTacticalMaidHeadband name: tactical maid headband - description: "A red headband - don't imagine yourself a Rambo and don't pick up a few machine guns." + description: A red headband - don't imagine yourself a Rambo and don't pick up a few machine guns. components: - type: Sprite sprite: Clothing/Head/Hats/tacticalmaidheadband.rsi @@ -1026,7 +1026,7 @@ parent: ClothingHeadBase id: ClothingHeadHatMagician name: magician's tophat - description: "A magician's tophat." + description: A magician's top hat. components: - type: Icon sprite: Clothing/Head/Hats/magician.rsi @@ -1060,7 +1060,7 @@ parent: ClothingHeadBase id: ClothingHeadHatCapcap name: cap cap - description: A grand, stylish captain cap. + description: A grand, stylish cap worn by the Captain. Shows the station who's in charge. components: - type: Sprite sprite: Clothing/Head/Hats/capcap.rsi @@ -1083,7 +1083,7 @@ parent: [ ClothingHeadBase, BaseCentcommContraband ] id: ClothingHeadHatCentcomcap name: CentComm cap - description: An extravagant, fancy Central Commander cap. + description: An extravagant, stylish cap worn by CentComm officials. Shows the Captain who's in charge. components: - type: Sprite sprite: Clothing/Head/Hats/comcap.rsi @@ -1188,7 +1188,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBrownFlatcap name: brown flatcap - description: Stupid clown! You made me look bad! + description: "\"Stupid clown! You made me look bad!\"" components: - type: Tag tags: @@ -1202,7 +1202,7 @@ parent: ClothingHeadBase id: ClothingHeadHatCowboyBrown name: brown cowboy hat - description: This hat ain't big enough for the both of us. + description: "\"This hat ain't big enough for the two of us.\"" components: - type: Sprite sprite: Clothing/Head/Hats/cowboyhatbrown.rsi @@ -1310,7 +1310,7 @@ parent: ClothingHeadBase id: ClothingHeadHatBeretMedic name: medical beret - description: White beret that encourages you to be clean. + description: A sterile white beret. components: - type: Sprite sprite: Clothing/Head/Hats/beret_medic.rsi @@ -1321,7 +1321,7 @@ parent: ClothingHeadBase id: ClothingHeadHatSolidHeadband name: solid headband - description: "You'll feel like you're Invisible while wearing this! (DISCLAIMER: DOES NOT ACTUALLY MAKE THE WEARER INVISIBLE)" + description: "\"You'll feel like you're Invisible while wearing this! (DISCLAIMER: DOES NOT ACTUALLY MAKE THE WEARER INVISIBLE)\"" components: - type: Sprite sprite: Clothing/Head/Hats/solidheadband.rsi diff --git a/Resources/Prototypes/Entities/Clothing/Head/soft.yml b/Resources/Prototypes/Entities/Clothing/Head/soft.yml index 8b6955fd52..815f86032f 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/soft.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/soft.yml @@ -44,7 +44,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatBluesoft name: blue cap - description: "It's a baseball hat in a tasteless blue colour." + description: A blue baseball cap. components: - type: Sprite sprite: Clothing/Head/Soft/bluesoft.rsi @@ -60,7 +60,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatCargosoft name: cargo cap - description: "It's a baseball hat painted in Cargo colours." + description: A baseball cap colored to match cargo's uniforms. components: - type: Sprite sprite: Clothing/Head/Soft/cargosoft.rsi @@ -89,7 +89,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatQMsoft name: quartermaster's cap - description: "It's a baseball hat painted in the Quartermaster's colors." + description: A baseball cap colored to match the Quartermaster's uniform. components: - type: Sprite sprite: Clothing/Head/Soft/qmsoft.rsi @@ -105,7 +105,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatCommandSoft name: command cap - description: It's a baseball hat painted in Command colours. + description: A baseball cap in command's distinctive shade of blue. components: - type: Sprite sprite: Clothing/Head/Soft/commandsoft.rsi @@ -121,7 +121,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatCorpsoft name: corporate cap - description: A baseball bat in corporation colors. + description: A baseball cap in corporation colors. components: - type: Sprite sprite: Clothing/Head/Soft/corpsoft.rsi @@ -137,7 +137,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatGreensoft name: green cap - description: "It's a baseball hat in a tasteless green colour." + description: A green baseball cap. components: - type: Sprite sprite: Clothing/Head/Soft/greensoft.rsi @@ -153,7 +153,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatBlacksoft name: black cap - description: "It's a baseball hat in a tasteless black colour." + description: A black baseball cap. components: - type: Sprite sprite: Clothing/Head/Soft/blacksoft.rsi @@ -169,7 +169,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatGreysoft name: grey cap - description: "It's a baseball hat in a tasteless grey colour." + description: A grey baseball cap. components: - type: Sprite sprite: Clothing/Head/Soft/greysoft.rsi @@ -185,7 +185,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatMimesoft name: white cap - description: "It's a baseball hat in a tasteless white colour." + description: A white baseball cap. components: - type: Sprite sprite: Clothing/Head/Soft/mimesoft.rsi @@ -201,7 +201,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatOrangesoft name: orange cap - description: It's a baseball hat in a good-looking orange colour. + description: An orange baseball cap. components: - type: Sprite sprite: Clothing/Head/Soft/orangesoft.rsi @@ -217,7 +217,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatPurplesoft name: purple cap - description: It's a baseball hat in a tasteless purple colour. + description: A purple baseball cap. components: - type: Sprite sprite: Clothing/Head/Soft/purplesoft.rsi @@ -246,7 +246,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatRedsoft name: red cap - description: It's a baseball hat in a tasteless red colour. + description: A red baseball cap. components: - type: Sprite sprite: Clothing/Head/Soft/redsoft.rsi @@ -262,7 +262,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatSecsoft name: security cap - description: It's a robust baseball hat in tasteful red colour. + description: A baseball cap colored to match security's uniforms. components: - type: Sprite sprite: Clothing/Head/Soft/secsoft.rsi @@ -278,7 +278,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatYellowsoft name: yellow cap - description: A yellow baseball hat. + description: A yellow baseball cap. components: - type: Sprite sprite: Clothing/Head/Soft/yellowsoft.rsi @@ -310,7 +310,7 @@ parent: ClothingHeadHeadHatBaseFlippable id: ClothingHeadHatParamedicsoft name: paramedic cap - description: "It's a paramedic's baseball hat with a medical logo." + description: A blue baseball cap with a cross on the front. components: - type: Sprite sprite: Clothing/Head/Soft/paramedicsoft.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml index 993c7eb6ce..72955e0f0d 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml @@ -175,7 +175,7 @@ parent: BladedFlatcapGrey id: BladedFlatcapBrown name: brown flatcap - description: Stupid clown! You made me look bad! It has glass shards hidden in the brim. + description: "\"Stupid clown! You made me look bad!\" It has glass shards hidden in the brim." components: - type: Construction graph: BladedFlatcapBrown From 3851e942cc7a2c83f792a50f2a4014dc4e314ca6 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 25 Jun 2025 20:28:34 +0000 Subject: [PATCH 097/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 7226b0adbc..abe23a0560 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: ciaran - changes: - - message: Fixed duplicate crew monitor console entries for crew with tracking implants - type: Fix - id: 8203 - time: '2025-04-16T21:14:05.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/35918 - author: Seam_Less changes: - message: Standardized the In-Hand Sprites for all gloves @@ -3882,3 +3875,10 @@ id: 8715 time: '2025-06-25T20:22:05.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38534 +- author: Hitlinemoss + changes: + - message: Various hats have had their descriptions updated. + type: Tweak + id: 8716 + time: '2025-06-25T20:27:27.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38156 From a594c7b667c3c1e7379fb711934a5676e922c0a9 Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Wed, 25 Jun 2025 17:23:05 -0400 Subject: [PATCH 098/191] Add /showaccessreaders to +MAPPING and +DEBUG (#37759) feat: add /showaccessreaders to +MAPPING and +DEBUG --- Resources/clientCommandPerms.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/clientCommandPerms.yml b/Resources/clientCommandPerms.yml index 9d183a3f8c..1cef3812cb 100644 --- a/Resources/clientCommandPerms.yml +++ b/Resources/clientCommandPerms.yml @@ -73,12 +73,14 @@ - localdelete - fullstatereset - dumpentities + - showaccessreaders - Flags: MAPPING Commands: - mapping - toggleautosave - toggledecals + - showaccessreaders - Flags: QUERY Commands: From 2073eb33d0a8b54fad0d4692129b581fa416f480 Mon Sep 17 00:00:00 2001 From: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com> Date: Thu, 26 Jun 2025 05:54:07 -0400 Subject: [PATCH 099/191] ShowHealthBarsCommand to LEC. (#38588) * mfw * Update Content.Client/Commands/ShowHealthBarsCommand.cs --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../Commands/ShowHealthBarsCommand.cs | 33 +++++++------------ .../commands/show-health-bars-command.ftl | 6 ++-- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/Content.Client/Commands/ShowHealthBarsCommand.cs b/Content.Client/Commands/ShowHealthBarsCommand.cs index 6ea9d06c8c..a0408cc97d 100644 --- a/Content.Client/Commands/ShowHealthBarsCommand.cs +++ b/Content.Client/Commands/ShowHealthBarsCommand.cs @@ -1,57 +1,46 @@ using Content.Shared.Damage.Prototypes; using Content.Shared.Overlays; -using Robust.Client.Player; using Robust.Shared.Console; using Robust.Shared.Prototypes; using System.Linq; namespace Content.Client.Commands; -public sealed class ShowHealthBarsCommand : LocalizedCommands +public sealed class ShowHealthBarsCommand : LocalizedEntityCommands { - [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - public override string Command => "showhealthbars"; - public override string Help => LocalizationManager.GetString($"cmd-{Command}-help", ("command", Command)); - public override void Execute(IConsoleShell shell, string argStr, string[] args) { - var player = _playerManager.LocalSession; + var player = shell.Player; if (player == null) { - shell.WriteError(LocalizationManager.GetString($"cmd-{Command}-error-not-player")); + shell.WriteError(Loc.GetString("shell-only-players-can-run-this-command")); return; } - var playerEntity = player?.AttachedEntity; - if (!playerEntity.HasValue) + if (player.AttachedEntity is not { } playerEntity) { - shell.WriteError(LocalizationManager.GetString($"cmd-{Command}-error-no-entity")); + shell.WriteError(Loc.GetString("shell-must-be-attached-to-entity")); return; } - if (!_entityManager.HasComponent(playerEntity)) + if (!EntityManager.HasComponent(playerEntity)) { var showHealthBarsComponent = new ShowHealthBarsComponent { DamageContainers = args.Select(arg => new ProtoId(arg)).ToList(), HealthStatusIcon = null, - NetSyncEnabled = false + NetSyncEnabled = false, }; - _entityManager.AddComponent(playerEntity.Value, showHealthBarsComponent, true); + EntityManager.AddComponent(playerEntity, showHealthBarsComponent, true); - shell.WriteLine(LocalizationManager.GetString($"cmd-{Command}-notify-enabled", ("args", string.Join(", ", args)))); + shell.WriteLine(Loc.GetString("cmd-showhealthbars-notify-enabled", ("args", string.Join(", ", args)))); return; } - else - { - _entityManager.RemoveComponentDeferred(playerEntity.Value); - shell.WriteLine(LocalizationManager.GetString($"cmd-{Command}-notify-disabled")); - } - return; + EntityManager.RemoveComponentDeferred(playerEntity); + shell.WriteLine(Loc.GetString("cmd-showhealthbars-notify-disabled")); } } diff --git a/Resources/Locale/en-US/commands/show-health-bars-command.ftl b/Resources/Locale/en-US/commands/show-health-bars-command.ftl index d660e93ce1..6020835d59 100644 --- a/Resources/Locale/en-US/commands/show-health-bars-command.ftl +++ b/Resources/Locale/en-US/commands/show-health-bars-command.ftl @@ -1,6 +1,4 @@ cmd-showhealthbars-desc = Toggles health bars above mobs. -cmd-showhealthbars-help = Usage: {$command} [] -cmd-showhealthbars-error-not-player = You aren't a player. -cmd-showhealthbars-error-no-entity = You do not have an attached entity. +cmd-showhealthbars-help = Usage: showhealthbars [] cmd-showhealthbars-notify-enabled = Enabled health overlay for DamageContainers: {$args}. -cmd-showhealthbars-notify-disabled = Disabled health overlay. \ No newline at end of file +cmd-showhealthbars-notify-disabled = Disabled health overlay. From b75aaf5e56d2e23b349aa95785a6db11b2c28952 Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Thu, 26 Jun 2025 06:43:40 -0400 Subject: [PATCH 100/191] Forgor changelog for permission change (#38593) From 966122f7a532250975ab6a982fb23bc15780388b Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 26 Jun 2025 10:44:47 +0000 Subject: [PATCH 101/191] Automatic changelog update --- Resources/Changelog/Admin.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 3465724e1b..29d119caa8 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1209,5 +1209,13 @@ Entries: id: 146 time: '2025-06-22T19:28:21.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38206 +- author: perryprog + changes: + - message: The /showaccessreaders command is now usable by those with +MAPPING or + +DEBUG, instead of being +HOST only. + type: Tweak + id: 147 + time: '2025-06-26T10:43:40.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38593 Name: Admin Order: 2 From bebc077fcc91e9807c2d9d42621a1f58dc181600 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 26 Jun 2025 14:47:39 +0200 Subject: [PATCH 102/191] MapRenderer code fixes (#38357) * Fix MapRenderer integration test usage to properly show output. Added an ITestContextLike interface that can be used to properly run the integration test infrastructure OUTSIDE A TEST. * Use System.Test.Json instead of Newtonsoft.Json for MapRenderer * Fix map renderer JSON output being broken I love not testing or even reading the surrounding code. * Fix un-reusable integration instances getting leaked. The pair state was always getting set to Ready even if the instance was killed, meaning it was getting put back into the pool even if killed. * Mark map renderer integration instances as destructive to avoid memory leak. * Fix file specification handling. Map file specification is now backwards compatible again (loose filename match to search prototypes). It also supports proper direct OS filename arguments. The former is the fallback scenario is extremely important for the map server still. Cleaned up the way that target map files are passed through the application, so mixed file/prototype specifications are now handled properly (which can be caused by the fallback behavior). Fixes JSON data export to use the proper user-facing map name. This only works if a prototype ID is specified *or* the legacy file behavior is used. Restructured MapPainter into an instance that has multiple functions called on it, so not all data has to be passed through a single Paint() call. Clean up the godawful map/grid detection code. Now we just load both in a single call, because yes you can do that. This relies on LogOrphanedGrids = false in the map loader options, which I think is fine for our purposes. Improved error handling in much of the program. * Fix duplicate map names in map renderer output I'm not sure *what* this output is used for, but I'm sure having it duplicated per grid isn't intentional. * Make maprenderer command line parsing bail on unknown - options * Fix incorrect docs for --viewer maprenderer argument It doesn't change directory layout * Fix parallax layer specification to not use imgur as a fucking CDN Files are now copied to a separate folder _parallax, and these files are referenced by the parallax configuration. Parallax data is only output when instructed to via --parallax. This will break parallax on current map server builds, but it should be graceful. Also, that's fucking good considering we shouldn't be using imgur links. Purge it. * Fix incorrect assert in test pair clean return * Restore other map viewer parallax layers, fix attribution. * This isn't a valid copyright statement but the validator forces me to enter something here. --- .../ExternalTestContext.cs | 12 + Content.IntegrationTests/ITestContextLike.cs | 13 + .../NUnitTestContextWrap.cs | 12 + .../Pair/TestPair.Recycle.cs | 4 +- Content.IntegrationTests/PoolManager.cs | 19 +- Content.MapRenderer/CommandLineArguments.cs | 14 +- Content.MapRenderer/MapViewerData.cs | 22 +- Content.MapRenderer/Painters/MapPainter.cs | 239 +++++++++--------- Content.MapRenderer/ParallaxOutput.cs | 41 +++ Content.MapRenderer/Program.cs | 132 ++++++++-- Content.MapRenderer/RenderMap.cs | 55 ++++ .../Textures/Parallaxes/attributions.yml | 5 + Resources/Textures/Parallaxes/layer2.png | Bin 0 -> 49049 bytes Resources/Textures/Parallaxes/layer2.png.yml | 1 + Resources/Textures/Parallaxes/layer3.png | Bin 0 -> 1580 bytes Resources/Textures/Parallaxes/layer3.png.yml | 1 + Resources/Textures/Parallaxes/meta.json | 14 - 17 files changed, 414 insertions(+), 170 deletions(-) create mode 100644 Content.IntegrationTests/ExternalTestContext.cs create mode 100644 Content.IntegrationTests/ITestContextLike.cs create mode 100644 Content.IntegrationTests/NUnitTestContextWrap.cs create mode 100644 Content.MapRenderer/ParallaxOutput.cs create mode 100644 Content.MapRenderer/RenderMap.cs create mode 100644 Resources/Textures/Parallaxes/layer2.png create mode 100644 Resources/Textures/Parallaxes/layer2.png.yml create mode 100644 Resources/Textures/Parallaxes/layer3.png create mode 100644 Resources/Textures/Parallaxes/layer3.png.yml delete mode 100644 Resources/Textures/Parallaxes/meta.json diff --git a/Content.IntegrationTests/ExternalTestContext.cs b/Content.IntegrationTests/ExternalTestContext.cs new file mode 100644 index 0000000000..e23b2ee636 --- /dev/null +++ b/Content.IntegrationTests/ExternalTestContext.cs @@ -0,0 +1,12 @@ +using System.IO; + +namespace Content.IntegrationTests; + +/// +/// Generic implementation of for usage outside of actual tests. +/// +public sealed class ExternalTestContext(string name, TextWriter writer) : ITestContextLike +{ + public string FullName => name; + public TextWriter Out => writer; +} diff --git a/Content.IntegrationTests/ITestContextLike.cs b/Content.IntegrationTests/ITestContextLike.cs new file mode 100644 index 0000000000..47b6e08529 --- /dev/null +++ b/Content.IntegrationTests/ITestContextLike.cs @@ -0,0 +1,13 @@ +using System.IO; + +namespace Content.IntegrationTests; + +/// +/// Something that looks like a , for passing to integration tests. +/// +public interface ITestContextLike +{ + string FullName { get; } + TextWriter Out { get; } +} + diff --git a/Content.IntegrationTests/NUnitTestContextWrap.cs b/Content.IntegrationTests/NUnitTestContextWrap.cs new file mode 100644 index 0000000000..849c1b0910 --- /dev/null +++ b/Content.IntegrationTests/NUnitTestContextWrap.cs @@ -0,0 +1,12 @@ +using System.IO; + +namespace Content.IntegrationTests; + +/// +/// Canonical implementation of for usage in actual NUnit tests. +/// +public sealed class NUnitTestContextWrap(TestContext context, TextWriter writer) : ITestContextLike +{ + public string FullName => context.Test.FullName; + public TextWriter Out => writer; +} diff --git a/Content.IntegrationTests/Pair/TestPair.Recycle.cs b/Content.IntegrationTests/Pair/TestPair.Recycle.cs index 89a9eb6463..694d6cfa64 100644 --- a/Content.IntegrationTests/Pair/TestPair.Recycle.cs +++ b/Content.IntegrationTests/Pair/TestPair.Recycle.cs @@ -13,6 +13,7 @@ using Robust.Server.Player; using Robust.Shared.Exceptions; using Robust.Shared.GameObjects; using Robust.Shared.Network; +using Robust.Shared.Utility; namespace Content.IntegrationTests.Pair; @@ -84,6 +85,7 @@ public sealed partial class TestPair : IAsyncDisposable var returnTime = Watch.Elapsed; await _testOut.WriteLineAsync($"{nameof(CleanReturnAsync)}: PoolManager took {returnTime.TotalMilliseconds} ms to put pair {Id} back into the pool"); + State = PairState.Ready; } private async Task ResetModifiedPreferences() @@ -104,7 +106,7 @@ public sealed partial class TestPair : IAsyncDisposable await _testOut.WriteLineAsync($"{nameof(CleanReturnAsync)}: Return of pair {Id} started"); State = PairState.CleanDisposed; await OnCleanDispose(); - State = PairState.Ready; + DebugTools.Assert(State is PairState.Dead or PairState.Ready); PoolManager.NoCheckReturn(this); ClearContext(); } diff --git a/Content.IntegrationTests/PoolManager.cs b/Content.IntegrationTests/PoolManager.cs index c7b8dcaee9..64aac16751 100644 --- a/Content.IntegrationTests/PoolManager.cs +++ b/Content.IntegrationTests/PoolManager.cs @@ -182,24 +182,29 @@ public static partial class PoolManager /// /// See /// - public static async Task GetServerClient(PoolSettings? poolSettings = null) + public static async Task GetServerClient( + PoolSettings? poolSettings = null, + ITestContextLike? testContext = null) { - return await GetServerClientPair(poolSettings ?? new PoolSettings()); + return await GetServerClientPair( + poolSettings ?? new PoolSettings(), + testContext ?? new NUnitTestContextWrap(TestContext.CurrentContext, TestContext.Out)); } - private static string GetDefaultTestName(TestContext testContext) + private static string GetDefaultTestName(ITestContextLike testContext) { - return testContext.Test.FullName.Replace("Content.IntegrationTests.Tests.", ""); + return testContext.FullName.Replace("Content.IntegrationTests.Tests.", ""); } - private static async Task GetServerClientPair(PoolSettings poolSettings) + private static async Task GetServerClientPair( + PoolSettings poolSettings, + ITestContextLike testContext) { if (!_initialized) throw new InvalidOperationException($"Pool manager has not been initialized"); // Trust issues with the AsyncLocal that backs this. - var testContext = TestContext.CurrentContext; - var testOut = TestContext.Out; + var testOut = testContext.Out; DieIfPoolFailure(); var currentTestName = poolSettings.TestName ?? GetDefaultTestName(testContext); diff --git a/Content.MapRenderer/CommandLineArguments.cs b/Content.MapRenderer/CommandLineArguments.cs index f75b671dcb..a4f3c83bf2 100644 --- a/Content.MapRenderer/CommandLineArguments.cs +++ b/Content.MapRenderer/CommandLineArguments.cs @@ -13,6 +13,7 @@ public sealed class CommandLineArguments public string OutputPath { get; set; } = DirectoryExtensions.MapImages().FullName; public bool ArgumentsAreFileNames { get; set; } = false; public bool ShowMarkers { get; set; } = false; + public bool OutputParallax { get; set; } = false; public static bool TryParse(IReadOnlyList args, [NotNullWhen(true)] out CommandLineArguments? parsed) { @@ -70,7 +71,17 @@ public sealed class CommandLineArguments PrintHelp(); return false; + case "--parallax": + parsed.OutputParallax = true; + break; + default: + if (argument.StartsWith('-')) + { + Console.WriteLine($"Unknown argument: {argument}"); + return false; + } + parsed.Maps.Add(argument); break; } @@ -95,7 +106,6 @@ Options: Defaults to: png --viewer Causes the map renderer to create the map.json files required for use with the map viewer. - Also puts the maps in the required directory structure. -o / --output Changes the path the rendered maps will get saved to. Defaults to Resources/MapImages @@ -104,6 +114,8 @@ Options: Example: Content.MapRenderer -f /Maps/box.yml /Maps/bagel.yml -m / --markers Show hidden markers on map render. Defaults to false. + --parallax + Output images and data used for map viewer parallax. -h / --help Displays this help text"); } diff --git a/Content.MapRenderer/MapViewerData.cs b/Content.MapRenderer/MapViewerData.cs index b7b720e004..b58d0c664b 100644 --- a/Content.MapRenderer/MapViewerData.cs +++ b/Content.MapRenderer/MapViewerData.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Numerics; -using Robust.Shared.Maths; +using System.Text.Json.Serialization; +using Robust.Shared.ContentPack; +using Robust.Shared.Utility; using SixLabors.ImageSharp.PixelFormats; namespace Content.MapRenderer; @@ -43,31 +45,31 @@ public sealed class LayerGroup public GroupSource Source { get; set; } = new(); public List Layers { get; set; } = new(); - public static LayerGroup DefaultParallax() + public static LayerGroup DefaultParallax(IResourceManager resourceManager, ParallaxOutput output) { return new LayerGroup { Scale = new Position(0.1f, 0.1f), Source = new GroupSource { - Url = "https://i.imgur.com/3YO8KRd.png", - Extent = new Extent(6000, 4000) + Url = output.ReferenceResourceFile(resourceManager, new ResPath("/Textures/Parallaxes/layer1.png")), + Extent = new Extent(6000, 4000), }, Layers = new List { new() { - Url = "https://i.imgur.com/IannmmK.png" + Url = output.ReferenceResourceFile(resourceManager, new ResPath("/Textures/Parallaxes/layer1.png")), }, new() { - Url = "https://i.imgur.com/T3W6JsE.png", + Url = output.ReferenceResourceFile(resourceManager, new ResPath("/Textures/Parallaxes/layer2.png")), Composition = "lighter", ParallaxScale = new Position(0.2f, 0.2f) }, new() { - Url = "https://i.imgur.com/T3W6JsE.png", + Url = output.ReferenceResourceFile(resourceManager, new ResPath("/Textures/Parallaxes/layer3.png")), Composition = "lighter", ParallaxScale = new Position(0.3f, 0.3f) } @@ -91,9 +93,13 @@ public sealed class Layer public readonly struct Extent { + [JsonInclude] public readonly float X1; + [JsonInclude] public readonly float Y1; + [JsonInclude] public readonly float X2; + [JsonInclude] public readonly float Y2; public Extent() @@ -123,7 +129,9 @@ public readonly struct Extent public readonly struct Position { + [JsonInclude] public readonly float X; + [JsonInclude] public readonly float Y; public Position(float x, float y) diff --git a/Content.MapRenderer/Painters/MapPainter.cs b/Content.MapRenderer/Painters/MapPainter.cs index e861227bcc..991fa74fe1 100644 --- a/Content.MapRenderer/Painters/MapPainter.cs +++ b/Content.MapRenderer/Painters/MapPainter.cs @@ -1,149 +1,158 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Numerics; using System.IO; using System.Threading.Tasks; +using Content.Client.Markers; using Content.IntegrationTests; +using Content.IntegrationTests.Pair; using Content.Server.GameTicking; using Robust.Client.GameObjects; using Robust.Server.GameObjects; using Robust.Server.Player; +using Robust.Shared.ContentPack; using Robust.Shared.EntitySerialization; using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Map.Components; -using Robust.Shared.Map.Events; using Robust.Shared.Maths; using Robust.Shared.Timing; -using Robust.Shared.Utility; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; namespace Content.MapRenderer.Painters { - public sealed class MapPainter + public sealed class MapPainter : IAsyncDisposable { - public static async IAsyncEnumerable> Paint(string map, - bool mapIsFilename = false, - bool showMarkers = false) - { - var stopwatch = new Stopwatch(); - stopwatch.Start(); + private readonly RenderMap _map; + private readonly ITestContextLike _testContextLike; - await using var pair = await PoolManager.GetServerClient(new PoolSettings + private TestPair? _pair; + private Entity[] _grids = []; + + public MapPainter(RenderMap map, ITestContextLike testContextLike) + { + _map = map; + _testContextLike = testContextLike; + } + + public async Task Initialize() + { + var stopwatch = RStopwatch.StartNew(); + + var poolSettings = new PoolSettings { DummyTicker = false, Connected = true, + Destructive = true, Fresh = true, // Seriously whoever made MapPainter use GameMapPrototype I wish you step on a lego one time. - Map = mapIsFilename ? "Empty" : map, - }); - - var server = pair.Server; - var client = pair.Client; + Map = _map is RenderMapPrototype prototype ? prototype.Prototype : PoolManager.TestMap, + }; + _pair = await PoolManager.GetServerClient(poolSettings, _testContextLike); Console.WriteLine($"Loaded client and server in {(int)stopwatch.Elapsed.TotalMilliseconds} ms"); - stopwatch.Restart(); - - var cEntityManager = client.ResolveDependency(); - var cPlayerManager = client.ResolveDependency(); - - await client.WaitPost(() => + if (_map is RenderMapFile mapFile) { - if (cEntityManager.TryGetComponent(cPlayerManager.LocalEntity, out SpriteComponent? sprite)) + using var stream = File.OpenRead(mapFile.FileName); + + await _pair.Server.WaitPost(() => { - cEntityManager.System().SetVisible((cPlayerManager.LocalEntity.Value, sprite), false); + var loadOptions = new MapLoadOptions + { + // Accept loading both maps and grids without caring about what the input file truly is. + DeserializationOptions = + { + LogOrphanedGrids = false, + }, + }; + + if (!_pair.Server.System().TryLoadGeneric(stream, mapFile.FileName, out var loadResult, loadOptions)) + throw new IOException($"File {mapFile.FileName} could not be read"); + + _grids = loadResult.Grids.ToArray(); + }); + } + } + + public async Task SetupView(bool showMarkers) + { + if (_pair == null) + throw new InvalidOperationException("Instance not initialized!"); + + await _pair.Client.WaitPost(() => + { + if (_pair.Client.EntMan.TryGetComponent(_pair.Client.PlayerMan.LocalEntity, out SpriteComponent? sprite)) + { + _pair.Client.System() + .SetVisible((_pair.Client.PlayerMan.LocalEntity.Value, sprite), false); } }); if (showMarkers) - await pair.WaitClientCommand("showmarkers"); + { + await _pair.Client.WaitPost(() => + { + _pair.Client.System().MarkersVisible = true; + }); + } + } + + public async Task GenerateMapViewerData(ParallaxOutput? parallaxOutput) + { + if (_pair == null) + throw new InvalidOperationException("Instance not initialized!"); + + var mapShort = _map.ShortName; + + string fullName; + if (_map is RenderMapPrototype prototype) + { + fullName = _pair.Server.ProtoMan.Index(prototype.Prototype).MapName; + } + else + { + fullName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(mapShort); + } + + var mapViewerData = new MapViewerData + { + Id = mapShort, + Name = fullName, + }; + + if (parallaxOutput != null) + { + await _pair.Client.WaitPost(() => + { + var res = _pair.Client.InstanceDependencyCollection.Resolve(); + mapViewerData.ParallaxLayers.Add(LayerGroup.DefaultParallax(res, parallaxOutput)); + }); + } + + return mapViewerData; + } + + public async IAsyncEnumerable> Paint() + { + if (_pair == null) + throw new InvalidOperationException("Instance not initialized!"); + + var client = _pair.Client; + var server = _pair.Server; var sEntityManager = server.ResolveDependency(); var sPlayerManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); - var mapLoader = entityManager.System(); var mapSys = entityManager.System(); - var deps = server.ResolveDependency().DependencyCollection; - Entity[] grids = []; - - if (mapIsFilename) - { - var resPath = new ResPath(map); - - if (!mapLoader.TryReadFile(resPath, out var data)) - throw new IOException($"File {map} could not be read"); - - var ev = new BeforeEntityReadEvent(); - server.EntMan.EventBus.RaiseEvent(EventSource.Local, ev); - - var deserializer = new EntityDeserializer(deps, - data, - DeserializationOptions.Default, - ev.RenamedPrototypes, - ev.DeletedPrototypes); - - if (!deserializer.TryProcessData()) - { - throw new IOException($"Failed to process entity data in {map}"); - } - - if (deserializer.Result.Category == FileCategory.Unknown && deserializer.Result.Version < 7) - { - var mapCount = 0; - var gridCount = 0; - foreach (var (entId, ent) in deserializer.YamlEntities) - { - if (ent.Components != null && ent.Components.ContainsKey("MapGrid")) - { - gridCount++; - } - if (ent.Components != null && ent.Components.ContainsKey("Map")) - { - mapCount++; - } - } - if (mapCount == 1) - deserializer.Result.Category = FileCategory.Map; - else if (mapCount == 0 && gridCount == 1) - deserializer.Result.Category = FileCategory.Grid; - } - - switch (deserializer.Result.Category) - { - case FileCategory.Map: - await server.WaitPost(() => - { - if (mapLoader.TryLoadMap(resPath, out _, out var loadedGrids)) - { - grids = loadedGrids.ToArray(); - } - }); - break; - - case FileCategory.Grid: - await server.WaitPost(() => - { - if (mapLoader.TryLoadGrid(resPath, out _, out var loadedGrids)) - { - grids = [(Entity)loadedGrids]; - } - }); - break; - - default: - throw new IOException($"Unknown category {deserializer.Result.Category}"); - - } - } - - await pair.RunTicksSync(10); + await _pair.RunTicksSync(10); await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync()); var sMapManager = server.ResolveDependency(); @@ -162,23 +171,23 @@ namespace Content.MapRenderer.Painters sEntityManager.DeleteEntity(playerEntity.Value); } - if (!mapIsFilename) + if (_map is RenderMapPrototype) { var mapId = sEntityManager.System().DefaultMap; - grids = sMapManager.GetAllGrids(mapId).ToArray(); + _grids = sMapManager.GetAllGrids(mapId).ToArray(); } - foreach (var (uid, _) in grids) + foreach (var (uid, _) in _grids) { var gridXform = xformQuery.GetComponent(uid); xformSystem.SetWorldRotation(gridXform, Angle.Zero); } }); - await pair.RunTicksSync(10); + await _pair.RunTicksSync(10); await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync()); - foreach (var (uid, grid) in grids) + foreach (var (uid, grid) in _grids) { var tiles = mapSys.GetAllTiles(uid, grid).ToList(); if (tiles.Count == 0) @@ -219,16 +228,20 @@ namespace Content.MapRenderer.Painters yield return renderedImage; } + } - // We don't care if it fails as we have already saved the images. - try - { - await pair.CleanReturnAsync(); - } - catch - { - // ignored - } + public async Task CleanReturnAsync() + { + if (_pair == null) + throw new InvalidOperationException("Instance not initialized!"); + + await _pair.CleanReturnAsync(); + } + + public async ValueTask DisposeAsync() + { + if (_pair != null) + await _pair.DisposeAsync(); } } } diff --git a/Content.MapRenderer/ParallaxOutput.cs b/Content.MapRenderer/ParallaxOutput.cs new file mode 100644 index 0000000000..bedbb1dc53 --- /dev/null +++ b/Content.MapRenderer/ParallaxOutput.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.IO; +using Robust.Shared.ContentPack; +using Robust.Shared.Utility; + +namespace Content.MapRenderer; + +/// +/// Helper class for collecting the files used for parallax output +/// +public sealed class ParallaxOutput +{ + public const string OutputDirectory = "_parallax"; + + public readonly HashSet FilesToCopy = []; + + private readonly string _outputPath; + + /// + /// Helper class for collecting the files used for parallax output + /// + public ParallaxOutput(string outputPath) + { + _outputPath = outputPath; + Directory.CreateDirectory(Path.Combine(_outputPath, OutputDirectory)); + } + + public string ReferenceResourceFile(IResourceManager resourceManager, ResPath path) + { + var fileName = Path.Combine(OutputDirectory, path.Filename); + if (FilesToCopy.Add(path)) + { + using var file = resourceManager.ContentFileRead(path); + using var target = File.Create(Path.Combine(_outputPath, fileName)); + + file.CopyTo(target); + } + + return fileName; + } +} diff --git a/Content.MapRenderer/Program.cs b/Content.MapRenderer/Program.cs index 115d83e65e..9d7843bcd0 100644 --- a/Content.MapRenderer/Program.cs +++ b/Content.MapRenderer/Program.cs @@ -3,12 +3,11 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading; +using System.Text.Json; using System.Threading.Tasks; using Content.IntegrationTests; using Content.MapRenderer.Painters; using Content.Server.Maps; -using Newtonsoft.Json; using Robust.Shared.Prototypes; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Webp; @@ -21,20 +20,19 @@ namespace Content.MapRenderer private static readonly Func ChosenMapIdNotIntMessage = id => $"The chosen id is not a valid integer: {id}"; private static readonly Func NoMapFoundWithIdMessage = id => $"No map found with chosen id: {id}"; - private static readonly MapPainter MapPainter = new(); - internal static async Task Main(string[] args) { - if (!CommandLineArguments.TryParse(args, out var arguments)) return; + var testContext = new ExternalTestContext("Content.MapRenderer", Console.Out); + PoolManager.Startup(); if (arguments.Maps.Count == 0) { Console.WriteLine("Didn't specify any maps to paint! Loading the map list..."); - await using var pair = await PoolManager.GetServerClient(); + await using var pair = await PoolManager.GetServerClient(testContext: testContext); var mapIds = pair.Server .ResolveDependency() .EnumeratePrototypes() @@ -104,45 +102,118 @@ namespace Content.MapRenderer Console.WriteLine($"Selected maps: {string.Join(", ", selectedMapPrototypes)}"); } + var maps = new List(); + if (arguments.ArgumentsAreFileNames) { Console.WriteLine("Retrieving maps by file names..."); + + // + // Handle legacy command line processing: + // Ideally, people pass file names that are relative to the process working directory. + // i.e. regular command-line behavior. + // + // However, the map renderer was originally written to only handle gameMap prototypes, + // so it would actually go through the list of prototypes and match file name arguments + // via a *very* coarse check. + // + // So if we have any input filenames that don't exist... we run the old behavior. + // Yes by the way this means a typo means spinning up an entire integration pool pair + // before the map renderer can report a proper failure. + // + // Note that this legacy processing is very important! The map server currently relies on it, + // because it wants to work with file names, but we *need* to resolve the input to a prototype + // to properly export viewer JSON data. + // + + var lookupPrototypeFiles = new List(); + + foreach (var map in arguments.Maps) + { + if (File.Exists(map)) + { + maps.Add(new RenderMapFile { FileName = map }); + } + else + { + lookupPrototypeFiles.Add(map); + } + } + + if (lookupPrototypeFiles.Count > 0) + { + Console.Write($"Following map files did not exist on disk directly, searching through prototypes: {string.Join(", ", lookupPrototypeFiles)}"); + + await using var pair = await PoolManager.GetServerClient(); + var mapPrototypes = pair.Server + .ResolveDependency() + .EnumeratePrototypes() + .ToArray(); + + foreach (var toFind in lookupPrototypeFiles) + { + foreach (var mapPrototype in mapPrototypes) + { + if (mapPrototype.MapPath.Filename == toFind) + { + maps.Add(new RenderMapPrototype { Prototype = mapPrototype, }); + Console.WriteLine($"Found matching map prototype: {mapPrototype.MapName}"); + goto found; + } + } + + await Console.Error.WriteLineAsync($"Found no map prototype for file '{toFind}'!"); + + found: ; + } + } + } + else + { + foreach (var map in arguments.Maps) + { + maps.Add(new RenderMapPrototype { Prototype = map }); + } } - await Run(arguments); + await Run(arguments, maps, testContext); PoolManager.Shutdown(); } - private static async Task Run(CommandLineArguments arguments) + private static async Task Run( + CommandLineArguments arguments, + List toRender, + ExternalTestContext testContext) { - Console.WriteLine($"Creating images for {arguments.Maps.Count} maps"); + Console.WriteLine($"Creating images for {toRender.Count} maps"); + + var parallaxOutput = arguments.OutputParallax ? new ParallaxOutput(arguments.OutputPath) : null; var mapNames = new List(); - foreach (var map in arguments.Maps) + foreach (var map in toRender) { Console.WriteLine($"Painting map {map}"); - var mapViewerData = new MapViewerData - { - Id = map, - Name = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(map) - }; + await using var painter = new MapPainter(map, testContext); + await painter.Initialize(); + await painter.SetupView(showMarkers: arguments.ShowMarkers); - mapViewerData.ParallaxLayers.Add(LayerGroup.DefaultParallax()); - var directory = Path.Combine(arguments.OutputPath, Path.GetFileNameWithoutExtension(map)); + var mapViewerData = await painter.GenerateMapViewerData(parallaxOutput); + + var mapShort = map.ShortName; + var directory = Path.Combine(arguments.OutputPath, mapShort); + + mapNames.Add(mapShort); var i = 0; try { - await foreach (var renderedGrid in MapPainter.Paint(map, - arguments.ArgumentsAreFileNames, - arguments.ShowMarkers)) + await foreach (var renderedGrid in painter.Paint()) { var grid = renderedGrid.Image; Directory.CreateDirectory(directory); - var fileName = Path.GetFileNameWithoutExtension(map); - var savePath = $"{directory}{Path.DirectorySeparatorChar}{fileName}-{i}.{arguments.Format}"; + var savePath = $"{directory}{Path.DirectorySeparatorChar}{mapShort}-{i}.{arguments.Format}"; Console.WriteLine($"Writing grid of size {grid.Width}x{grid.Height} to {savePath}"); @@ -167,9 +238,7 @@ namespace Content.MapRenderer grid.Dispose(); - mapViewerData.Grids.Add(new GridLayer(renderedGrid, Path.Combine(map, Path.GetFileName(savePath)))); - - mapNames.Add(fileName); + mapViewerData.Grids.Add(new GridLayer(renderedGrid, Path.Combine(mapShort, Path.GetFileName(savePath)))); i++; } } @@ -182,8 +251,17 @@ namespace Content.MapRenderer if (arguments.ExportViewerJson) { - var json = JsonConvert.SerializeObject(mapViewerData); - await File.WriteAllTextAsync(Path.Combine(arguments.OutputPath, map, "map.json"), json); + var json = JsonSerializer.Serialize(mapViewerData); + await File.WriteAllTextAsync(Path.Combine(directory, "map.json"), json); + } + + try + { + await painter.CleanReturnAsync(); + } + catch (Exception e) + { + Console.WriteLine($"Exception while shutting down painter: {e}"); } } diff --git a/Content.MapRenderer/RenderMap.cs b/Content.MapRenderer/RenderMap.cs new file mode 100644 index 0000000000..4ebf4ee5d4 --- /dev/null +++ b/Content.MapRenderer/RenderMap.cs @@ -0,0 +1,55 @@ +using System.IO; +using Content.Server.Maps; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.MapRenderer; + +/// +/// A single target map that the map renderer should render. +/// +/// +/// +public abstract class RenderMap +{ + /// + /// Short identifier of the map that should be unique-ish. Used in file names and other important stuff. + /// + public abstract string ShortName { get; } +} + +/// +/// Specifies a map prototype that the map renderer should render. +/// +public sealed class RenderMapPrototype : RenderMap +{ + /// + /// The ID of the prototype to render. + /// + public required ProtoId Prototype; + + public override string ShortName => Prototype; + + public override string ToString() + { + return $"{nameof(RenderMapPrototype)}({Prototype})"; + } +} + +/// +/// Specifies a map file on disk that the map renderer should render. +/// +public sealed class RenderMapFile : RenderMap +{ + /// + /// The path to the file that should be rendered. This is an OS disk path, *not* a . + /// + public required string FileName; + + public override string ShortName => Path.GetFileNameWithoutExtension(FileName); + + public override string ToString() + { + return $"{nameof(RenderMapFile)}({FileName})"; + } +} diff --git a/Resources/Textures/Parallaxes/attributions.yml b/Resources/Textures/Parallaxes/attributions.yml index 131e096862..f980eafaec 100644 --- a/Resources/Textures/Parallaxes/attributions.yml +++ b/Resources/Textures/Parallaxes/attributions.yml @@ -32,3 +32,8 @@ license: "CC-BY-NC-SA-3.0" copyright: "Made by SlamBamActionman" source: "https://github.com/space-wizards/space-station-14/blob/master/Resources/Textures/Parallaxes/space_map3.png" + +- files: [ "layer1.png", "layer2.png", "layer3.png" ] + license: "CC-BY-SA-3.0" + copyright: "Taken from TGstation13" + source: "https://github.com/tgstation/tgstation/blob/bce9afc2cf39c229f2799c6ad2124f4d69a2dbbf/icons/effects/parallax.dmi" diff --git a/Resources/Textures/Parallaxes/layer2.png b/Resources/Textures/Parallaxes/layer2.png new file mode 100644 index 0000000000000000000000000000000000000000..43443fa0459770c5111bd92962a32be2ac9fa7eb GIT binary patch literal 49049 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 CTzicG literal 0 HcmV?d00001 diff --git a/Resources/Textures/Parallaxes/layer3.png.yml b/Resources/Textures/Parallaxes/layer3.png.yml new file mode 100644 index 0000000000..a2cfb0a1fe --- /dev/null +++ b/Resources/Textures/Parallaxes/layer3.png.yml @@ -0,0 +1 @@ +preload: false diff --git a/Resources/Textures/Parallaxes/meta.json b/Resources/Textures/Parallaxes/meta.json deleted file mode 100644 index cacbbf2659..0000000000 --- a/Resources/Textures/Parallaxes/meta.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "https://github.com/tgstation/tgstation/blob/master/icons/effects/parallax.dmi.", - "size": { - "x": 480, - "y": 480 - }, - "states": [ - { - "name": "layer1" - } - ] -} \ No newline at end of file From 4af6c8461fa55e368821bfbfb2198d4bc92e388f Mon Sep 17 00:00:00 2001 From: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com> Date: Thu, 26 Jun 2025 09:00:44 -0400 Subject: [PATCH 103/191] Hide mechanism and show mechanism commands to LEC (#38587) commit --- .../Commands/HideMechanismsCommand.cs | 19 +++++++------------ .../Commands/ShowMechanismsCommand.cs | 15 +++++---------- .../commands/hide-mechanisms-command.ftl | 4 ++-- .../commands/show-mechanisms-command.ftl | 2 +- 4 files changed, 15 insertions(+), 25 deletions(-) diff --git a/Content.Client/Commands/HideMechanismsCommand.cs b/Content.Client/Commands/HideMechanismsCommand.cs index db68a56257..00e024a72c 100644 --- a/Content.Client/Commands/HideMechanismsCommand.cs +++ b/Content.Client/Commands/HideMechanismsCommand.cs @@ -5,32 +5,27 @@ using Robust.Shared.Containers; namespace Content.Client.Commands; -public sealed class HideMechanismsCommand : LocalizedCommands +public sealed class HideMechanismsCommand : LocalizedEntityCommands { - [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SpriteSystem _spriteSystem = default!; public override string Command => "hidemechanisms"; - public override string Description => LocalizationManager.GetString($"cmd-{Command}-desc", ("showMechanismsCommand", ShowMechanismsCommand.CommandName)); - - public override string Help => LocalizationManager.GetString($"cmd-{Command}-help", ("command", Command)); - public override void Execute(IConsoleShell shell, string argStr, string[] args) { - var containerSys = _entityManager.System(); - var spriteSys = _entityManager.System(); - var query = _entityManager.AllEntityQueryEnumerator(); + var query = EntityManager.AllEntityQueryEnumerator(); while (query.MoveNext(out var uid, out _, out var sprite)) { - spriteSys.SetContainerOccluded((uid, sprite), false); + _spriteSystem.SetContainerOccluded((uid, sprite), false); var tempParent = uid; - while (containerSys.TryGetContainingContainer((tempParent, null, null), out var container)) + while (_containerSystem.TryGetContainingContainer((tempParent, null, null), out var container)) { if (!container.ShowContents) { - spriteSys.SetContainerOccluded((uid, sprite), true); + _spriteSystem.SetContainerOccluded((uid, sprite), true); break; } diff --git a/Content.Client/Commands/ShowMechanismsCommand.cs b/Content.Client/Commands/ShowMechanismsCommand.cs index 19305b207c..091fe05f89 100644 --- a/Content.Client/Commands/ShowMechanismsCommand.cs +++ b/Content.Client/Commands/ShowMechanismsCommand.cs @@ -4,24 +4,19 @@ using Robust.Shared.Console; namespace Content.Client.Commands; -public sealed class ShowMechanismsCommand : LocalizedCommands +public sealed class ShowMechanismsCommand : LocalizedEntityCommands { - [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly SpriteSystem _spriteSystem = default!; - public const string CommandName = "showmechanisms"; - - public override string Command => CommandName; - - public override string Help => LocalizationManager.GetString($"cmd-{Command}-help", ("command", Command)); + public override string Command => "showmechanisms"; public override void Execute(IConsoleShell shell, string argStr, string[] args) { - var spriteSys = _entManager.System(); - var query = _entManager.AllEntityQueryEnumerator(); + var query = EntityManager.AllEntityQueryEnumerator(); while (query.MoveNext(out var uid, out _, out var sprite)) { - spriteSys.SetContainerOccluded((uid, sprite), false); + _spriteSystem.SetContainerOccluded((uid, sprite), false); } } } diff --git a/Resources/Locale/en-US/commands/hide-mechanisms-command.ftl b/Resources/Locale/en-US/commands/hide-mechanisms-command.ftl index 75c9cbc815..db15e90aa4 100644 --- a/Resources/Locale/en-US/commands/hide-mechanisms-command.ftl +++ b/Resources/Locale/en-US/commands/hide-mechanisms-command.ftl @@ -1,2 +1,2 @@ -cmd-hidemechanisms-desc = Reverts the effects of {$showMechanismsCommand} -cmd-hidemechanisms-help = Usage: {$command} \ No newline at end of file +cmd-hidemechanisms-desc = Reverts the effects of showmechanisms command. +cmd-hidemechanisms-help = Usage: hidemechanisms diff --git a/Resources/Locale/en-US/commands/show-mechanisms-command.ftl b/Resources/Locale/en-US/commands/show-mechanisms-command.ftl index 0512381b63..0415482e8e 100644 --- a/Resources/Locale/en-US/commands/show-mechanisms-command.ftl +++ b/Resources/Locale/en-US/commands/show-mechanisms-command.ftl @@ -1,2 +1,2 @@ cmd-showmechanisms-desc = Makes mechanisms visible, even when they shouldn't be. -cmd-showmechanisms-help = Usage: {$command} \ No newline at end of file +cmd-showmechanisms-help = Usage: showmechanisms From faec9bd83baade6bde40ad581991052ee161fa9c Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Thu, 26 Jun 2025 17:29:22 +0200 Subject: [PATCH 104/191] Fix multi handed items (#38603) --- Content.Shared/Item/MultiHandedItemSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Item/MultiHandedItemSystem.cs b/Content.Shared/Item/MultiHandedItemSystem.cs index f17ccdc922..db64610eae 100644 --- a/Content.Shared/Item/MultiHandedItemSystem.cs +++ b/Content.Shared/Item/MultiHandedItemSystem.cs @@ -37,7 +37,7 @@ public sealed class MultiHandedItemSystem : EntitySystem private void OnAttemptPickup(Entity ent, ref GettingPickedUpAttemptEvent args) { - if (_hands.CountFreeHands(ent.Owner) >= ent.Comp.HandsNeeded) + if (_hands.CountFreeHands(args.User) >= ent.Comp.HandsNeeded) return; args.Cancel(); From 268b8d1947ce996f299d2d7c60a57b6353cbc15e Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 26 Jun 2025 15:30:31 +0000 Subject: [PATCH 105/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index abe23a0560..7d6ce77705 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Seam_Less - changes: - - message: Standardized the In-Hand Sprites for all gloves - type: Tweak - id: 8204 - time: '2025-04-16T22:15:38.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/35997 - author: DissidentBullet changes: - message: Monkeys can now wear juggernaut suits @@ -3882,3 +3875,10 @@ id: 8716 time: '2025-06-25T20:27:27.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38156 +- author: slarticodefast + changes: + - message: Multi-handed items can be picked up again. + type: Fix + id: 8717 + time: '2025-06-26T15:29:22.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38603 From f0f7d68727c4f1a950156071c4e4f1058cc8b437 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Thu, 26 Jun 2025 18:17:25 +0200 Subject: [PATCH 106/191] fix smart equip (#38605) --- Content.Shared/Interaction/SmartEquipSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Interaction/SmartEquipSystem.cs b/Content.Shared/Interaction/SmartEquipSystem.cs index 797dc367b4..da79fc1e69 100644 --- a/Content.Shared/Interaction/SmartEquipSystem.cs +++ b/Content.Shared/Interaction/SmartEquipSystem.cs @@ -80,7 +80,7 @@ public sealed class SmartEquipSystem : EntitySystem } // early out if we have an item and cant drop it at all - if (hands.ActiveHandId != null && !_hands.CanDropHeld(uid, hands.ActiveHandId)) + if (handItem != null && !_hands.CanDropHeld(uid, hands.ActiveHandId)) { _popup.PopupClient(Loc.GetString("smart-equip-cant-drop"), uid, uid); return; From 27914467fdbbe1a2580d97389e50abfb1a941c95 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 26 Jun 2025 16:18:33 +0000 Subject: [PATCH 107/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 7d6ce77705..156f9abcc7 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: DissidentBullet - changes: - - message: Monkeys can now wear juggernaut suits - type: Add - id: 8205 - time: '2025-04-16T23:14:28.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/34121 - author: SpaceRox1244 changes: - message: Cargo can now order handheld artifact containers for transporting small @@ -3882,3 +3875,10 @@ id: 8717 time: '2025-06-26T15:29:22.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38603 +- author: slarticodefast + changes: + - message: Fixed smart equip not retrieving items. + type: Fix + id: 8718 + time: '2025-06-26T16:17:26.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38605 From 283d0863a0f7d233ab4188076c46eafe6161a06d Mon Sep 17 00:00:00 2001 From: Red <96445749+TheShuEd@users.noreply.github.com> Date: Thu, 26 Jun 2025 23:05:58 +0300 Subject: [PATCH 108/191] New Status Effects system: Relay events (#38579) --- Content.Client/Drowsiness/DrowsinessSystem.cs | 14 +++-- .../SSDIndicator/SSDIndicatorComponent.cs | 7 --- .../SSDIndicator/SSDIndicatorSystem.cs | 13 ++--- .../SharedStatusEffectsSystem.cs | 37 +------------- ...ystem.API.cs => StatusEffectSystem.API.cs} | 0 .../StatusEffectSystem.Relay.cs | 51 +++++++++++++++++++ .../Entities/StatusEffects/misc.yml | 8 +++ 7 files changed, 72 insertions(+), 58 deletions(-) rename Content.Shared/StatusEffectNew/{StatusEffectNewSystem.API.cs => StatusEffectSystem.API.cs} (100%) create mode 100644 Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs diff --git a/Content.Client/Drowsiness/DrowsinessSystem.cs b/Content.Client/Drowsiness/DrowsinessSystem.cs index 152d6ebdf8..3b35101489 100644 --- a/Content.Client/Drowsiness/DrowsinessSystem.cs +++ b/Content.Client/Drowsiness/DrowsinessSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Drowsiness; using Content.Shared.StatusEffectNew; using Robust.Client.Graphics; using Robust.Client.Player; +using Robust.Shared.Player; namespace Content.Client.Drowsiness; @@ -20,8 +21,8 @@ public sealed class DrowsinessSystem : SharedDrowsinessSystem SubscribeLocalEvent(OnDrowsinessApply); SubscribeLocalEvent(OnDrowsinessShutdown); - SubscribeLocalEvent(OnStatusEffectPlayerAttached); - SubscribeLocalEvent(OnStatusEffectPlayerDetached); + SubscribeLocalEvent>(OnStatusEffectPlayerAttached); + SubscribeLocalEvent>(OnStatusEffectPlayerDetached); _overlay = new(); } @@ -44,17 +45,14 @@ public sealed class DrowsinessSystem : SharedDrowsinessSystem } } - private void OnStatusEffectPlayerAttached(Entity ent, ref StatusEffectPlayerAttachedEvent args) + private void OnStatusEffectPlayerAttached(Entity ent, ref StatusEffectRelayedEvent args) { - if (_player.LocalEntity != args.Target) - return; - _overlayMan.AddOverlay(_overlay); } - private void OnStatusEffectPlayerDetached(Entity ent, ref StatusEffectPlayerDetachedEvent args) + private void OnStatusEffectPlayerDetached(Entity ent, ref StatusEffectRelayedEvent args) { - if (_player.LocalEntity != args.Target) + if (_player.LocalEntity is null) return; if (!_statusEffects.HasEffectComp(_player.LocalEntity.Value)) diff --git a/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs b/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs index 47ac8afe72..cef8bd15e3 100644 --- a/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs +++ b/Content.Shared/SSDIndicator/SSDIndicatorComponent.cs @@ -25,11 +25,4 @@ public sealed partial class SSDIndicatorComponent : Component /// [DataField, AutoPausedField, Access(typeof(SSDIndicatorSystem))] public TimeSpan FallAsleepTime = TimeSpan.Zero; - - /// - /// Required to don't remove forced sleep from other sources - /// - [ViewVariables(VVAccess.ReadWrite)] - [AutoNetworkedField] - public bool ForcedSleepAdded = false; } diff --git a/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs b/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs index 850ea8e8da..a13b6b915c 100644 --- a/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs +++ b/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs @@ -1,8 +1,8 @@ -using Content.Shared.Bed.Sleep; using Content.Shared.CCVar; using Content.Shared.StatusEffectNew; using Robust.Shared.Configuration; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Timing; namespace Content.Shared.SSDIndicator; @@ -12,6 +12,8 @@ namespace Content.Shared.SSDIndicator; /// public sealed class SSDIndicatorSystem : EntitySystem { + public static readonly EntProtoId StatusEffectSSDSleeping = "StatusEffectSSDSleeping"; + [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedStatusEffectsSystem _statusEffects = default!; @@ -37,11 +39,7 @@ public sealed class SSDIndicatorSystem : EntitySystem if (_icSsdSleep) { component.FallAsleepTime = TimeSpan.Zero; - if (component.ForcedSleepAdded) // Remove component only if it has been added by this system - { - _statusEffects.TryRemoveStatusEffect(uid, SleepingSystem.StatusEffectForcedSleeping); - component.ForcedSleepAdded = false; - } + _statusEffects.TryRemoveStatusEffect(uid, StatusEffectSSDSleeping); } Dirty(uid, component); @@ -87,8 +85,7 @@ public sealed class SSDIndicatorSystem : EntitySystem ssd.FallAsleepTime <= _timing.CurTime && !TerminatingOrDeleted(uid)) { - _statusEffects.TryAddStatusEffect(uid, SleepingSystem.StatusEffectForcedSleeping); - ssd.ForcedSleepAdded = true; + _statusEffects.TryAddStatusEffect(uid, StatusEffectSSDSleeping); } } } diff --git a/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs b/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs index df27dca27c..c836b8205c 100644 --- a/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs +++ b/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs @@ -3,7 +3,6 @@ using Content.Shared.StatusEffectNew.Components; using Content.Shared.Whitelist; using Robust.Shared.GameStates; using Robust.Shared.Network; -using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -30,11 +29,11 @@ public abstract partial class SharedStatusEffectsSystem : EntitySystem { base.Initialize(); + InitializeRelay(); + SubscribeLocalEvent(OnStatusEffectApplied); SubscribeLocalEvent(OnStatusEffectRemoved); - SubscribeLocalEvent(OnStatusEffectContainerAttached); - SubscribeLocalEvent(OnStatusEffectContainerDetached); SubscribeLocalEvent(OnGetState); _containerQuery = GetEntityQuery(); @@ -46,24 +45,6 @@ public abstract partial class SharedStatusEffectsSystem : EntitySystem args.State = new StatusEffectContainerComponentState(GetNetEntitySet(ent.Comp.ActiveStatusEffects)); } - private void OnStatusEffectContainerAttached(Entity ent, ref LocalPlayerAttachedEvent args) - { - foreach (var effect in ent.Comp.ActiveStatusEffects) - { - var ev = new StatusEffectPlayerAttachedEvent(ent); - RaiseLocalEvent(effect, ref ev); - } - } - - private void OnStatusEffectContainerDetached(Entity ent, ref LocalPlayerDetachedEvent args) - { - foreach (var effect in ent.Comp.ActiveStatusEffects) - { - var ev = new StatusEffectPlayerDetachedEvent(ent); - RaiseLocalEvent(effect, ref ev); - } - } - public override void Update(float frameTime) { base.Update(frameTime); @@ -187,20 +168,6 @@ public readonly record struct StatusEffectAppliedEvent(EntityUid Target); [ByRefEvent] public readonly record struct StatusEffectRemovedEvent(EntityUid Target); -/// -/// Called on a status effect entity inside -/// after a player has been to this container entity. -/// -[ByRefEvent] -public readonly record struct StatusEffectPlayerAttachedEvent(EntityUid Target); - -/// -/// Called on a status effect entity inside -/// after a player has been to this container entity. -/// -[ByRefEvent] -public readonly record struct StatusEffectPlayerDetachedEvent(EntityUid Target); - /// /// Raised on an entity before a status effect is added to determine if adding it should be cancelled. /// diff --git a/Content.Shared/StatusEffectNew/StatusEffectNewSystem.API.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs similarity index 100% rename from Content.Shared/StatusEffectNew/StatusEffectNewSystem.API.cs rename to Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs new file mode 100644 index 0000000000..6d97a75edd --- /dev/null +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs @@ -0,0 +1,51 @@ +using Content.Shared.StatusEffectNew.Components; +using Robust.Shared.Player; + +namespace Content.Shared.StatusEffectNew; + +public abstract partial class SharedStatusEffectsSystem +{ + protected void InitializeRelay() + { + SubscribeLocalEvent(RelayStatusEffectEvent); + SubscribeLocalEvent(RelayStatusEffectEvent); + } + + protected void RefRelayStatusEffectEvent(EntityUid uid, StatusEffectContainerComponent component, ref T args) where T : struct + { + RelayEvent((uid, component), ref args); + } + + protected void RelayStatusEffectEvent(EntityUid uid, StatusEffectContainerComponent component, T args) where T : class + { + RelayEvent((uid, component), args); + } + + public void RelayEvent(Entity statusEffect, ref T args) where T : struct + { + // this copies the by-ref event if it is a struct + var ev = new StatusEffectRelayedEvent(args); + foreach (var activeEffect in statusEffect.Comp.ActiveStatusEffects) + { + RaiseLocalEvent(activeEffect, ref ev); + } + // and now we copy it back + args = ev.Args; + } + + public void RelayEvent(Entity statusEffect, T args) where T : class + { + // this copies the by-ref event if it is a struct + var ev = new StatusEffectRelayedEvent(args); + foreach (var activeEffect in statusEffect.Comp.ActiveStatusEffects) + { + RaiseLocalEvent(activeEffect, ref ev); + } + } +} + +/// +/// Event wrapper for relayed events. +/// +[ByRefEvent] +public record struct StatusEffectRelayedEvent(TEvent Args); diff --git a/Resources/Prototypes/Entities/StatusEffects/misc.yml b/Resources/Prototypes/Entities/StatusEffects/misc.yml index 254d1608bd..bccdea773f 100644 --- a/Resources/Prototypes/Entities/StatusEffects/misc.yml +++ b/Resources/Prototypes/Entities/StatusEffects/misc.yml @@ -27,6 +27,14 @@ components: - type: ForcedSleepingStatusEffect +# This creature is asleep because it's disconnected from the game. +- type: entity + parent: MobStatusEffectBase + id: StatusEffectSSDSleeping + name: forced sleep + components: + - type: ForcedSleepingStatusEffect + # Blurs your vision and makes you randomly fall asleep - type: entity parent: MobStatusEffectBase From cc2a89ed86dfee8955ba0c62b0647d071b01655d Mon Sep 17 00:00:00 2001 From: Southbridge <7013162+southbridge-fur@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:32:36 -0400 Subject: [PATCH 109/191] Fixed Snacks Mispredicting on Clients (#38522) * Well this works * Removed merge conflict bait * PredictedDel * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Content.Shared/Nutrition/EntitySystems/FoodSystem.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Nutrition/EntitySystems/FoodSystem.cs b/Content.Shared/Nutrition/EntitySystems/FoodSystem.cs index 6099939725..ee2c9f3a4a 100644 --- a/Content.Shared/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/FoodSystem.cs @@ -342,7 +342,7 @@ public sealed class FoodSystem : EntitySystem if (component.Trash.Count == 0) { - QueueDel(food); + PredictedQueueDel(food); return; } @@ -353,10 +353,10 @@ public sealed class FoodSystem : EntitySystem var trashes = component.Trash; var tryPickup = _hands.IsHolding(user, food, out _); - Del(food); + PredictedDel(food); foreach (var trash in trashes) { - var spawnedTrash = Spawn(trash, position); + var spawnedTrash = EntityManager.PredictedSpawn(trash, position); // If the user is holding the item if (tryPickup) From 3a8974f574f3e658bfe2700074d25c720e9afdd4 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Thu, 26 Jun 2025 18:08:01 -0400 Subject: [PATCH 110/191] Improve lathe queue performance (#38583) * Use an actual Queue * Store ProtoIds instead of prototypes * Network as NetListAsArray * Remove Serializable & NetSerializable from LatheRecipePrototype * Convert CurrentlyProducing too * No point using NetListAsArray if you're going to .ToArray() it anyways. --------- Co-authored-by: PJB3005 --- Content.Client/Lathe/UI/LatheMenu.xaml.cs | 13 ++++++++----- Content.Server/Lathe/LatheSystem.cs | 17 ++++++++++------- Content.Shared/Lathe/LatheComponent.cs | 4 ++-- Content.Shared/Lathe/LatheMessages.cs | 7 ++++--- .../Research/Prototypes/LatheRecipePrototype.cs | 3 +-- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml.cs b/Content.Client/Lathe/UI/LatheMenu.xaml.cs index 1e71d1ae89..66d875b0f2 100644 --- a/Content.Client/Lathe/UI/LatheMenu.xaml.cs +++ b/Content.Client/Lathe/UI/LatheMenu.xaml.cs @@ -223,13 +223,14 @@ public sealed partial class LatheMenu : DefaultWindow /// Populates the build queue list with all queued items /// /// - public void PopulateQueueList(List queue) + public void PopulateQueueList(IReadOnlyCollection> queue) { QueueList.DisposeAllChildren(); var idx = 1; - foreach (var recipe in queue) + foreach (var recipeProto in queue) { + var recipe = _prototypeManager.Index(recipeProto); var queuedRecipeBox = new BoxContainer(); queuedRecipeBox.Orientation = BoxContainer.LayoutOrientation.Horizontal; @@ -243,12 +244,14 @@ public sealed partial class LatheMenu : DefaultWindow } } - public void SetQueueInfo(LatheRecipePrototype? recipe) + public void SetQueueInfo(ProtoId? recipeProto) { - FabricatingContainer.Visible = recipe != null; - if (recipe == null) + FabricatingContainer.Visible = recipeProto != null; + if (recipeProto == null) return; + var recipe = _prototypeManager.Index(recipeProto.Value); + FabricatingDisplayContainer.Children.Clear(); FabricatingDisplayContainer.AddChild(GetRecipeDisplayControl(recipe)); diff --git a/Content.Server/Lathe/LatheSystem.cs b/Content.Server/Lathe/LatheSystem.cs index 3c5ecde1c6..6ecd5bdf62 100644 --- a/Content.Server/Lathe/LatheSystem.cs +++ b/Content.Server/Lathe/LatheSystem.cs @@ -183,7 +183,7 @@ namespace Content.Server.Lathe _materialStorage.TryChangeMaterialAmount(uid, mat, adjustedAmount); } - component.Queue.Add(recipe); + component.Queue.Enqueue(recipe); return true; } @@ -195,8 +195,8 @@ namespace Content.Server.Lathe if (component.CurrentRecipe != null || component.Queue.Count <= 0 || !this.IsPowered(uid, EntityManager)) return false; - var recipe = component.Queue.First(); - component.Queue.RemoveAt(0); + var recipeProto = component.Queue.Dequeue(); + var recipe = _proto.Index(recipeProto); var time = _reagentSpeed.ApplySpeed(uid, recipe.CompleteTime) * component.TimeMultiplier; @@ -226,13 +226,14 @@ namespace Content.Server.Lathe if (comp.CurrentRecipe != null) { - if (comp.CurrentRecipe.Result is { } resultProto) + var currentRecipe = _proto.Index(comp.CurrentRecipe.Value); + if (currentRecipe.Result is { } resultProto) { var result = Spawn(resultProto, Transform(uid).Coordinates); _stack.TryMergeToContacts(result); } - if (comp.CurrentRecipe.ResultReagents is { } resultReagents && + if (currentRecipe.ResultReagents is { } resultReagents && comp.ReagentOutputSlotId is { } slotId) { var toAdd = new Solution( @@ -269,9 +270,11 @@ namespace Content.Server.Lathe if (!Resolve(uid, ref component)) return; - var producing = component.CurrentRecipe ?? component.Queue.FirstOrDefault(); + var producing = component.CurrentRecipe; + if (producing == null && component.Queue.TryPeek(out var next)) + producing = next; - var state = new LatheUpdateState(GetAvailableRecipes(uid, component), component.Queue, producing); + var state = new LatheUpdateState(GetAvailableRecipes(uid, component), component.Queue.ToArray(), producing); _uiSys.SetUiState(uid, LatheUiKey.Key, state); } diff --git a/Content.Shared/Lathe/LatheComponent.cs b/Content.Shared/Lathe/LatheComponent.cs index 80f4f62a31..8b701ff64e 100644 --- a/Content.Shared/Lathe/LatheComponent.cs +++ b/Content.Shared/Lathe/LatheComponent.cs @@ -29,7 +29,7 @@ namespace Content.Shared.Lathe /// The lathe's construction queue /// [DataField] - public List Queue = new(); + public Queue> Queue = new(); /// /// The sound that plays when the lathe is producing an item, if any @@ -64,7 +64,7 @@ namespace Content.Shared.Lathe /// The recipe the lathe is currently producing /// [ViewVariables] - public LatheRecipePrototype? CurrentRecipe; + public ProtoId? CurrentRecipe; #region MachineUpgrading /// diff --git a/Content.Shared/Lathe/LatheMessages.cs b/Content.Shared/Lathe/LatheMessages.cs index 820c496d30..1c1c6440f1 100644 --- a/Content.Shared/Lathe/LatheMessages.cs +++ b/Content.Shared/Lathe/LatheMessages.cs @@ -1,4 +1,5 @@ using Content.Shared.Research.Prototypes; +using NetSerializer; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -9,11 +10,11 @@ public sealed class LatheUpdateState : BoundUserInterfaceState { public List> Recipes; - public List Queue; + public ProtoId[] Queue; - public LatheRecipePrototype? CurrentlyProducing; + public ProtoId? CurrentlyProducing; - public LatheUpdateState(List> recipes, List queue, LatheRecipePrototype? currentlyProducing = null) + public LatheUpdateState(List> recipes, ProtoId[] queue, ProtoId? currentlyProducing = null) { Recipes = recipes; Queue = queue; diff --git a/Content.Shared/Research/Prototypes/LatheRecipePrototype.cs b/Content.Shared/Research/Prototypes/LatheRecipePrototype.cs index 7d26971b11..957526d802 100644 --- a/Content.Shared/Research/Prototypes/LatheRecipePrototype.cs +++ b/Content.Shared/Research/Prototypes/LatheRecipePrototype.cs @@ -3,13 +3,12 @@ using Content.Shared.FixedPoint; using Content.Shared.Lathe.Prototypes; using Content.Shared.Materials; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; using Robust.Shared.Utility; namespace Content.Shared.Research.Prototypes { - [NetSerializable, Serializable, Prototype] + [Prototype] public sealed partial class LatheRecipePrototype : IPrototype, IInheritingPrototype { [ViewVariables] From 787b3b3ffddf80db085aef6bb68bc0175c31b5e4 Mon Sep 17 00:00:00 2001 From: Hitlinemoss <209321380+Hitlinemoss@users.noreply.github.com> Date: Thu, 26 Jun 2025 18:09:36 -0400 Subject: [PATCH 111/191] Quartermaster's PDA has AstroNav preinstalled (#38445) Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Resources/Prototypes/Entities/Objects/Devices/pda.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 3de35c3519..c5218dea9f 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -473,6 +473,13 @@ accentVColor: "#a23e3e" - type: Icon state: pda-qm + - type: CartridgeLoader + preinstalled: + - CrewManifestCartridge + - NotekeeperCartridge + - NanoTaskCartridge + - NewsReaderCartridge + - AstroNavCartridge - type: entity parent: BasePDA From c8f72798952f05abc613c84bcf08389ff503414b Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 26 Jun 2025 22:10:45 +0000 Subject: [PATCH 112/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 156f9abcc7..47600981e8 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: SpaceRox1244 - changes: - - message: Cargo can now order handheld artifact containers for transporting small - artifacts. - type: Add - id: 8206 - time: '2025-04-16T23:35:57.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33327 - author: ScarKy0 changes: - message: Hydroponics tray flatpacks can now be bought for 750 spesos. @@ -3882,3 +3874,10 @@ id: 8718 time: '2025-06-26T16:17:26.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38605 +- author: Hitlinemoss + changes: + - message: The Quartermaster's PDA now comes with AstroNav preinstalled. + type: Tweak + id: 8719 + time: '2025-06-26T22:09:36.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38445 From 18d9a03ef6a6e488955b1b5ddce581379ae6af0c Mon Sep 17 00:00:00 2001 From: Qerd <73325910+BigfootBravo@users.noreply.github.com> Date: Thu, 26 Jun 2025 15:57:46 -0700 Subject: [PATCH 113/191] Bladed flatcaps are minor contraband (#38597) --- Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml index 72955e0f0d..edab0e8a94 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml @@ -146,7 +146,7 @@ storedSprite: null - type: entity - parent: ClothingHeadHatGreyFlatcap + parent: [ClothingHeadHatGreyFlatcap, BaseMinorContraband] id: BladedFlatcapGrey name: grey flatcap description: Fashionable for both the working class and old man Jenkins. It has glass shards hidden in the brim. From bb7e7c3e5f56b92215ec0b762be90623f810e362 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 26 Jun 2025 22:58:54 +0000 Subject: [PATCH 114/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 47600981e8..f985491da7 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: ScarKy0 - changes: - - message: Hydroponics tray flatpacks can now be bought for 750 spesos. - type: Add - id: 8207 - time: '2025-04-16T23:51:00.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36630 - author: EmoGarbage404 changes: - message: Fixed artifacts sometimes having negative research values. @@ -3881,3 +3874,10 @@ id: 8719 time: '2025-06-26T22:09:36.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38445 +- author: BigfootBravo + changes: + - message: Bladed flatcaps are now minor contraband. + type: Tweak + id: 8720 + time: '2025-06-26T22:57:46.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38597 From 73df3b15939e4c40ea58f1526ede5ce3bc3ed5ad Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 27 Jun 2025 01:27:23 +0200 Subject: [PATCH 115/191] Stop network serializing prototypes (#38602) * Stop network serializing prototypes Send the damn proto ID instead. * Fix sandbox violation --- .../Administration/AdminNameOverlay.cs | 45 ++++++++++++++----- .../Systems/AdminSystem.Overlay.cs | 5 ++- .../UI/Tabs/PlayerTab/PlayerTabEntry.xaml.cs | 15 ++++--- .../NetworkConfiguratorLinkMenu.xaml.cs | 10 +++-- .../Administration/Logs/AdminLogManager.cs | 6 ++- .../Administration/Systems/AdminSystem.cs | 6 +-- .../Systems/NetworkConfiguratorSystem.cs | 6 ++- .../EntitySystems/MumbleAccentSystem.cs | 10 ++++- .../Speech/EntitySystems/VocalSystem.cs | 16 +++++-- Content.Shared/Administration/PlayerInfo.cs | 3 +- .../Cargo/Prototypes/CargoBountyPrototype.cs | 2 +- .../Chat/Prototypes/EmoteSoundsPrototype.cs | 2 +- .../Prototypes/DamageContainerPrototype.cs | 1 - .../Damage/Prototypes/DamageGroupPrototype.cs | 1 - .../DeviceLinking/DevicePortPrototype.cs | 3 -- .../DeviceLinking/SharedDeviceLinkSystem.cs | 11 +++++ .../DeviceNetwork/DeviceFrequencyPrototype.cs | 1 - .../NetworkConfiguratorUserInterfaceState.cs | 8 ++-- Content.Shared/Mind/RoleTypePrototype.cs | 12 +++-- Content.Shared/Roles/AntagPrototype.cs | 1 - .../Silicons/Laws/SiliconLawPrototype.cs | 1 - .../Silicons/Laws/SiliconLawsetPrototype.cs | 2 +- .../Speech/Components/VocalComponent.cs | 2 +- Content.Shared/Store/CurrencyPrototype.cs | 2 +- Content.Shared/Store/ListingPrototype.cs | 3 +- .../Store/StoreCategoryPrototype.cs | 1 - .../Prototypes/StoryTemplatePrototype.cs | 2 +- .../VendingMachineInventoryPrototype.cs | 2 +- 28 files changed, 120 insertions(+), 59 deletions(-) diff --git a/Content.Client/Administration/AdminNameOverlay.cs b/Content.Client/Administration/AdminNameOverlay.cs index 0d424cbff0..abeed65732 100644 --- a/Content.Client/Administration/AdminNameOverlay.cs +++ b/Content.Client/Administration/AdminNameOverlay.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using System.Linq; using System.Numerics; using Content.Client.Administration.Systems; @@ -24,6 +25,7 @@ internal sealed class AdminNameOverlay : Overlay private readonly EntityLookupSystem _entityLookup; private readonly IUserInterfaceManager _userInterfaceManager; private readonly SharedRoleSystem _roles; + private readonly IPrototypeManager _prototypeManager; private readonly Font _font; private readonly Font _fontBold; private AdminOverlayAntagFormat _overlayFormat; @@ -36,8 +38,9 @@ internal sealed class AdminNameOverlay : Overlay private float _overlayMergeDistance; //TODO make this adjustable via GUI? - private readonly ProtoId[] _filter = - ["SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"]; + private static readonly FrozenSet> Filter = + new ProtoId[] {"SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"} + .ToFrozenSet(); private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic"); @@ -49,7 +52,8 @@ internal sealed class AdminNameOverlay : Overlay EntityLookupSystem entityLookup, IUserInterfaceManager userInterfaceManager, IConfigurationManager config, - SharedRoleSystem roles) + SharedRoleSystem roles, + IPrototypeManager prototypeManager) { _system = system; _entityManager = entityManager; @@ -57,6 +61,7 @@ internal sealed class AdminNameOverlay : Overlay _entityLookup = entityLookup; _userInterfaceManager = userInterfaceManager; _roles = roles; + _prototypeManager = prototypeManager; ZIndex = 200; // Setting these to a specific ttf would break the antag symbols _font = resourceCache.NotoStack(); @@ -125,6 +130,14 @@ internal sealed class AdminNameOverlay : Overlay foreach (var info in sortable.OrderBy(s => s.Item4.Y).ToList()) { var playerInfo = info.Item1; + var rolePrototype = playerInfo.RoleProto == null + ? null + : _prototypeManager.Index(playerInfo.RoleProto.Value); + + var roleName = Loc.GetString(rolePrototype?.Name ?? RoleTypePrototype.FallbackName); + var roleColor = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor; + var roleSymbol = rolePrototype?.Symbol ?? RoleTypePrototype.FallbackSymbol; + var aabb = info.Item2; var entity = info.Item3; var screenCoordinatesCenter = info.Item4; @@ -209,7 +222,7 @@ internal sealed class AdminNameOverlay : Overlay switch (_overlaySymbolStyle) { case AdminOverlayAntagSymbolStyle.Specific: - symbol = playerInfo.RoleProto.Symbol; + symbol = roleSymbol; break; case AdminOverlayAntagSymbolStyle.Basic: symbol = Loc.GetString("player-tab-antag-prefix"); @@ -225,17 +238,17 @@ internal sealed class AdminNameOverlay : Overlay switch (_overlayFormat) { case AdminOverlayAntagFormat.Roletype: - color = playerInfo.RoleProto.Color; - symbol = _filter.Contains(playerInfo.RoleProto) ? symbol : string.Empty; - text = _filter.Contains(playerInfo.RoleProto) - ? Loc.GetString(playerInfo.RoleProto.Name).ToUpper() + color = roleColor; + symbol = IsFiltered(playerInfo.RoleProto) ? symbol : string.Empty; + text = IsFiltered(playerInfo.RoleProto) + ? roleName.ToUpper() : string.Empty; break; case AdminOverlayAntagFormat.Subtype: - color = playerInfo.RoleProto.Color; - symbol = _filter.Contains(playerInfo.RoleProto) ? symbol : string.Empty; - text = _filter.Contains(playerInfo.RoleProto) - ? _roles.GetRoleSubtypeLabel(playerInfo.RoleProto.Name, playerInfo.Subtype).ToUpper() + color = roleColor; + symbol = IsFiltered(playerInfo.RoleProto) ? symbol : string.Empty; + text = IsFiltered(playerInfo.RoleProto) + ? _roles.GetRoleSubtypeLabel(roleName, playerInfo.Subtype).ToUpper() : string.Empty; break; default: @@ -258,4 +271,12 @@ internal sealed class AdminNameOverlay : Overlay drawnOverlays.Add((screenCoordinatesCenter, currentOffset)); } } + + private static bool IsFiltered(ProtoId? roleProtoId) + { + if (roleProtoId == null) + return false; + + return Filter.Contains(roleProtoId.Value); + } } diff --git a/Content.Client/Administration/Systems/AdminSystem.Overlay.cs b/Content.Client/Administration/Systems/AdminSystem.Overlay.cs index a630df4521..e000bdc0ba 100644 --- a/Content.Client/Administration/Systems/AdminSystem.Overlay.cs +++ b/Content.Client/Administration/Systems/AdminSystem.Overlay.cs @@ -4,6 +4,7 @@ using Robust.Client.Graphics; using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; using Robust.Shared.Configuration; +using Robust.Shared.Prototypes; namespace Content.Client.Administration.Systems { @@ -17,6 +18,7 @@ namespace Content.Client.Administration.Systems [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly SharedRoleSystem _roles = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; private AdminNameOverlay _adminNameOverlay = default!; @@ -33,7 +35,8 @@ namespace Content.Client.Administration.Systems _entityLookup, _userInterfaceManager, _configurationManager, - _roles); + _roles, + _proto); _adminManager.AdminStatusUpdated += OnAdminStatusUpdated; } diff --git a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabEntry.xaml.cs b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabEntry.xaml.cs index 41e3fca761..6a2d939b4b 100644 --- a/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabEntry.xaml.cs +++ b/Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabEntry.xaml.cs @@ -1,9 +1,11 @@ using Content.Shared.Administration; +using Content.Shared.Mind; using Content.Shared.Roles; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; namespace Content.Client.Administration.UI.Tabs.PlayerTab; @@ -11,6 +13,7 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab; public sealed partial class PlayerTabEntry : PanelContainer { [Dependency] private readonly IEntityManager _entMan = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; public PlayerTabEntry( PlayerInfo player, @@ -23,6 +26,8 @@ public sealed partial class PlayerTabEntry : PanelContainer RobustXamlLoader.Load(this); var roles = _entMan.System(); + var rolePrototype = player.RoleProto == null ? null : _prototype.Index(player.RoleProto.Value); + UsernameLabel.Text = player.Username; if (!player.Connected) UsernameLabel.StyleClasses.Add("Disabled"); @@ -57,19 +62,19 @@ public sealed partial class PlayerTabEntry : PanelContainer break; default: case AdminPlayerTabSymbolOption.Specific: - symbol = player.Antag ? player.RoleProto.Symbol : string.Empty; + symbol = player.Antag ? rolePrototype?.Symbol ?? RoleTypePrototype.FallbackSymbol : string.Empty; break; } CharacterLabel.Text = Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", player.CharacterName)); if (player.Antag && colorAntags) - CharacterLabel.FontColorOverride = player.RoleProto.Color; + CharacterLabel.FontColorOverride = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor; if (player.IdentityName != player.CharacterName) CharacterLabel.Text += $" [{player.IdentityName}]"; - var roletype = RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name); - var subtype = roles.GetRoleSubtypeLabel(player.RoleProto.Name, player.Subtype); + var roletype = RoleTypeLabel.Text = Loc.GetString(rolePrototype?.Name ?? RoleTypePrototype.FallbackName); + var subtype = roles.GetRoleSubtypeLabel(rolePrototype?.Name ?? RoleTypePrototype.FallbackName, player.Subtype); switch (roleSetting) { case AdminPlayerTabRoleTypeOption.RoleTypeSubtype: @@ -92,7 +97,7 @@ public sealed partial class PlayerTabEntry : PanelContainer } if (colorRoles) - RoleTypeLabel.FontColorOverride = player.RoleProto.Color; + RoleTypeLabel.FontColorOverride = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor; BackgroundColorPanel.PanelOverride = styleBoxFlat; OverallPlaytimeLabel.Text = player.PlaytimeString; } diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs index 8cdffd16af..c627dc2ce8 100644 --- a/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs +++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs @@ -1,4 +1,5 @@ -using System.Numerics; +using System.Linq; +using System.Numerics; using Content.Client.UserInterface.Controls; using Content.Shared.DeviceLinking; using Content.Shared.DeviceNetwork; @@ -14,6 +15,8 @@ namespace Content.Client.NetworkConfigurator; [GenerateTypedNameReferences] public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow { + private readonly IPrototypeManager _prototypeManager = null!; + private const string PanelBgColor = "#202023"; private readonly LinksRender _links; @@ -33,6 +36,7 @@ public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow public NetworkConfiguratorLinkMenu() { RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); var footerStyleBox = new StyleBoxFlat() { @@ -61,7 +65,7 @@ public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow ButtonContainerRight.RemoveAllChildren(); _sources.Clear(); - _sources.AddRange(linkState.Sources); + _sources.AddRange(linkState.Sources.Select(s => _prototypeManager.Index(s))); _links.SourceButtons.Clear(); var i = 0; foreach (var source in _sources) @@ -73,7 +77,7 @@ public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow } _sinks.Clear(); - _sinks.AddRange(linkState.Sinks); + _sinks.AddRange(linkState.Sinks.Select(s => _prototypeManager.Index(s))); _links.SinkButtons.Clear(); i = 0; foreach (var sink in _sinks) diff --git a/Content.Server/Administration/Logs/AdminLogManager.cs b/Content.Server/Administration/Logs/AdminLogManager.cs index 1d6ff13a5a..add51a7775 100644 --- a/Content.Server/Administration/Logs/AdminLogManager.cs +++ b/Content.Server/Administration/Logs/AdminLogManager.cs @@ -9,12 +9,14 @@ using Content.Shared.Administration.Logs; using Content.Shared.CCVar; using Content.Shared.Chat; using Content.Shared.Database; +using Content.Shared.Mind; using Content.Shared.Players.PlayTimeTracking; using Prometheus; using Robust.Shared; using Robust.Shared.Configuration; using Robust.Shared.Network; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Reflection; using Robust.Shared.Timing; @@ -33,6 +35,7 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa [Dependency] private readonly ISharedPlayerManager _player = default!; [Dependency] private readonly ISharedPlaytimeManager _playtime = default!; [Dependency] private readonly ISharedChatManager _chat = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; public const string SawmillId = "admin.logs"; @@ -330,7 +333,8 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa var cachedInfo = adminSys.GetCachedPlayerInfo(new NetUserId(id)); if (cachedInfo != null && cachedInfo.Antag) { - var subtype = Loc.GetString(cachedInfo.Subtype ?? cachedInfo.RoleProto.Name); + var proto = cachedInfo.RoleProto == null ? null : _proto.Index(cachedInfo.RoleProto.Value); + var subtype = Loc.GetString(cachedInfo.Subtype ?? proto?.Name ?? RoleTypePrototype.FallbackName); logMessage = Loc.GetString( "admin-alert-antag-label", ("message", logMessage), diff --git a/Content.Server/Administration/Systems/AdminSystem.cs b/Content.Server/Administration/Systems/AdminSystem.cs index 0e5138ba96..95c6578a94 100644 --- a/Content.Server/Administration/Systems/AdminSystem.cs +++ b/Content.Server/Administration/Systems/AdminSystem.cs @@ -231,7 +231,7 @@ public sealed class AdminSystem : EntitySystem var antag = false; // Starting role, antagonist status and role type - RoleTypePrototype roleType = new(); + RoleTypePrototype? roleType = null; var startingRole = string.Empty; LocId? subtype = null; if (_minds.TryGetMind(session, out var mindId, out var mindComp) && mindComp is not null) @@ -244,7 +244,7 @@ public sealed class AdminSystem : EntitySystem subtype = mindComp.Subtype; } else - Log.Error($"{ToPrettyString(mindId)} has invalid Role Type '{mindComp.RoleType}'. Displaying '{Loc.GetString(roleType.Name)}' instead"); + Log.Error($"{ToPrettyString(mindId)} has invalid Role Type '{mindComp.RoleType}'. Displaying '{Loc.GetString(RoleTypePrototype.FallbackName)}' instead"); antag = _role.MindIsAntagonist(mindId); startingRole = _jobs.MindTryGetJobName(mindId); @@ -270,7 +270,7 @@ public sealed class AdminSystem : EntitySystem identityName, startingRole, antag, - roleType, + roleType?.ID, subtype, sortWeight, GetNetEntity(session?.AttachedEntity), diff --git a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs index 6bcbe30456..e34929bd2e 100644 --- a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs @@ -18,6 +18,7 @@ using Robust.Server.Audio; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Map.Events; +using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -496,14 +497,15 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem return; var sources = _deviceLinkSystem.GetSourcePorts(sourceUid, sourceComponent); - var sinks = _deviceLinkSystem.GetSinkPorts(sinkUid, sinkComponent); + var sinks = _deviceLinkSystem.GetSinkPortIds((sinkUid, sinkComponent)); var links = _deviceLinkSystem.GetLinks(sourceUid, sinkUid, sourceComponent); var defaults = _deviceLinkSystem.GetDefaults(sources); + var sourceIds = sources.Select(s => (ProtoId)s.ID).ToArray(); var sourceAddress = Resolve(sourceUid, ref sourceNetworkComponent, false) ? sourceNetworkComponent.Address : ""; var sinkAddress = Resolve(sinkUid, ref sinkNetworkComponent, false) ? sinkNetworkComponent.Address : ""; - var state = new DeviceLinkUserInterfaceState(sources, sinks, links, sourceAddress, sinkAddress, defaults); + var state = new DeviceLinkUserInterfaceState(sourceIds, sinks, links, sourceAddress, sinkAddress, defaults); _uiSystem.SetUiState(configuratorUid, NetworkConfiguratorUiKey.Link, state); } diff --git a/Content.Server/Speech/EntitySystems/MumbleAccentSystem.cs b/Content.Server/Speech/EntitySystems/MumbleAccentSystem.cs index 6b1af5c227..57469a25aa 100644 --- a/Content.Server/Speech/EntitySystems/MumbleAccentSystem.cs +++ b/Content.Server/Speech/EntitySystems/MumbleAccentSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Chat.Systems; using Content.Server.Speech.Components; using Content.Shared.Chat.Prototypes; using Content.Shared.Speech.Components; +using Robust.Shared.Prototypes; namespace Content.Server.Speech.EntitySystems; @@ -9,6 +10,7 @@ public sealed class MumbleAccentSystem : EntitySystem { [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly ReplacementAccentSystem _replacement = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; public override void Initialize() { @@ -23,10 +25,14 @@ public sealed class MumbleAccentSystem : EntitySystem if (args.Handled || !args.Emote.Category.HasFlag(EmoteCategory.Vocal)) return; - if (TryComp(ent.Owner, out var vocalComp)) + if (TryComp(ent.Owner, out var vocalComp) && vocalComp.EmoteSounds is { } sounds) { // play a muffled version of the vocal emote - args.Handled = _chat.TryPlayEmoteSound(ent.Owner, vocalComp.EmoteSounds, args.Emote, ent.Comp.EmoteAudioParams); + args.Handled = _chat.TryPlayEmoteSound( + ent.Owner, + _prototype.Index(sounds), + args.Emote, + ent.Comp.EmoteAudioParams); } } diff --git a/Content.Server/Speech/EntitySystems/VocalSystem.cs b/Content.Server/Speech/EntitySystems/VocalSystem.cs index 14fac0bab8..fb88238288 100644 --- a/Content.Server/Speech/EntitySystems/VocalSystem.cs +++ b/Content.Server/Speech/EntitySystems/VocalSystem.cs @@ -64,8 +64,11 @@ public sealed class VocalSystem : EntitySystem return; } + if (component.EmoteSounds is not { } sounds) + return; + // just play regular sound based on emote proto - args.Handled = _chat.TryPlayEmoteSound(uid, component.EmoteSounds, args.Emote); + args.Handled = _chat.TryPlayEmoteSound(uid, _proto.Index(sounds), args.Emote); } private void OnScreamAction(EntityUid uid, VocalComponent component, ScreamActionEvent args) @@ -85,7 +88,10 @@ public sealed class VocalSystem : EntitySystem return true; } - return _chat.TryPlayEmoteSound(uid, component.EmoteSounds, component.ScreamId); + if (component.EmoteSounds is not { } sounds) + return false; + + return _chat.TryPlayEmoteSound(uid, _proto.Index(sounds), component.ScreamId); } private void LoadSounds(EntityUid uid, VocalComponent component, Sex? sex = null) @@ -97,6 +103,10 @@ public sealed class VocalSystem : EntitySystem if (!component.Sounds.TryGetValue(sex.Value, out var protoId)) return; - _proto.TryIndex(protoId, out component.EmoteSounds); + + if (!_proto.HasIndex(protoId)) + return; + + component.EmoteSounds = protoId; } } diff --git a/Content.Shared/Administration/PlayerInfo.cs b/Content.Shared/Administration/PlayerInfo.cs index 5164425347..0f8dd814a5 100644 --- a/Content.Shared/Administration/PlayerInfo.cs +++ b/Content.Shared/Administration/PlayerInfo.cs @@ -1,5 +1,6 @@ using Content.Shared.Mind; using Robust.Shared.Network; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Administration; @@ -11,7 +12,7 @@ public sealed record PlayerInfo( string IdentityName, string StartingJob, bool Antag, - RoleTypePrototype RoleProto, + ProtoId? RoleProto, LocId? Subtype, int SortWeight, NetEntity? NetEntity, diff --git a/Content.Shared/Cargo/Prototypes/CargoBountyPrototype.cs b/Content.Shared/Cargo/Prototypes/CargoBountyPrototype.cs index 38ca2286ee..495ad14a9d 100644 --- a/Content.Shared/Cargo/Prototypes/CargoBountyPrototype.cs +++ b/Content.Shared/Cargo/Prototypes/CargoBountyPrototype.cs @@ -10,7 +10,7 @@ namespace Content.Shared.Cargo.Prototypes; /// that must be sold together in a labeled container in order /// to receive a monetary reward. /// -[Prototype, Serializable, NetSerializable] +[Prototype] public sealed partial class CargoBountyPrototype : IPrototype { /// diff --git a/Content.Shared/Chat/Prototypes/EmoteSoundsPrototype.cs b/Content.Shared/Chat/Prototypes/EmoteSoundsPrototype.cs index 73a3ba0bbf..4364e13d84 100644 --- a/Content.Shared/Chat/Prototypes/EmoteSoundsPrototype.cs +++ b/Content.Shared/Chat/Prototypes/EmoteSoundsPrototype.cs @@ -9,7 +9,7 @@ namespace Content.Shared.Chat.Prototypes; /// Sounds collection for each . /// Different entities may use different sounds collections. /// -[Prototype, Serializable, NetSerializable] +[Prototype] public sealed partial class EmoteSoundsPrototype : IPrototype { [IdDataField] diff --git a/Content.Shared/Damage/Prototypes/DamageContainerPrototype.cs b/Content.Shared/Damage/Prototypes/DamageContainerPrototype.cs index 836978dd03..95de0fb29d 100644 --- a/Content.Shared/Damage/Prototypes/DamageContainerPrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageContainerPrototype.cs @@ -13,7 +13,6 @@ namespace Content.Shared.Damage.Prototypes /// cref="DamageableComponent"/> should support. /// [Prototype] - [Serializable, NetSerializable] public sealed partial class DamageContainerPrototype : IPrototype { [ViewVariables] diff --git a/Content.Shared/Damage/Prototypes/DamageGroupPrototype.cs b/Content.Shared/Damage/Prototypes/DamageGroupPrototype.cs index 6df2138cd4..7615724467 100644 --- a/Content.Shared/Damage/Prototypes/DamageGroupPrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageGroupPrototype.cs @@ -12,7 +12,6 @@ namespace Content.Shared.Damage.Prototypes /// to change/get/set damage in a . /// [Prototype(2)] - [Serializable, NetSerializable] public sealed partial class DamageGroupPrototype : IPrototype { [IdDataField] public string ID { get; private set; } = default!; diff --git a/Content.Shared/DeviceLinking/DevicePortPrototype.cs b/Content.Shared/DeviceLinking/DevicePortPrototype.cs index d01f4d6d2a..bf062ddce4 100644 --- a/Content.Shared/DeviceLinking/DevicePortPrototype.cs +++ b/Content.Shared/DeviceLinking/DevicePortPrototype.cs @@ -7,7 +7,6 @@ namespace Content.Shared.DeviceLinking; /// /// A prototype for a device port, for use with device linking. /// -[Serializable, NetSerializable] public abstract class DevicePortPrototype { [IdDataField] @@ -28,13 +27,11 @@ public abstract class DevicePortPrototype } [Prototype] -[Serializable, NetSerializable] public sealed partial class SinkPortPrototype : DevicePortPrototype, IPrototype { } [Prototype] -[Serializable, NetSerializable] public sealed partial class SourcePortPrototype : DevicePortPrototype, IPrototype { /// diff --git a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs index e51087fdf6..5d51bbc3e8 100644 --- a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs +++ b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.DeviceLinking.Events; @@ -142,6 +143,11 @@ public abstract class SharedDeviceLinkSystem : EntitySystem } } + public ProtoId[] GetSourcePortIds(Entity source) + { + return source.Comp.Ports.ToArray(); + } + /// /// Retrieves the available ports from a source /// @@ -160,6 +166,11 @@ public abstract class SharedDeviceLinkSystem : EntitySystem return sourcePorts; } + public ProtoId[] GetSinkPortIds(Entity source) + { + return source.Comp.Ports.ToArray(); + } + /// /// Retrieves the available ports from a sink /// diff --git a/Content.Shared/DeviceNetwork/DeviceFrequencyPrototype.cs b/Content.Shared/DeviceNetwork/DeviceFrequencyPrototype.cs index 19ccb5e2fe..ac5b6eec18 100644 --- a/Content.Shared/DeviceNetwork/DeviceFrequencyPrototype.cs +++ b/Content.Shared/DeviceNetwork/DeviceFrequencyPrototype.cs @@ -7,7 +7,6 @@ namespace Content.Shared.DeviceNetwork; /// A named device network frequency. Useful for ensuring entity prototypes can communicate with each other. /// [Prototype] -[Serializable, NetSerializable] public sealed partial class DeviceFrequencyPrototype : IPrototype { [IdDataField] diff --git a/Content.Shared/DeviceNetwork/NetworkConfiguratorUserInterfaceState.cs b/Content.Shared/DeviceNetwork/NetworkConfiguratorUserInterfaceState.cs index 13000d00c9..4c02124487 100644 --- a/Content.Shared/DeviceNetwork/NetworkConfiguratorUserInterfaceState.cs +++ b/Content.Shared/DeviceNetwork/NetworkConfiguratorUserInterfaceState.cs @@ -29,16 +29,16 @@ public sealed class DeviceListUserInterfaceState : BoundUserInterfaceState [Serializable, NetSerializable] public sealed class DeviceLinkUserInterfaceState : BoundUserInterfaceState { - public readonly List Sources; - public readonly List Sinks; + public readonly ProtoId[] Sources; + public readonly ProtoId[] Sinks; public readonly HashSet<(ProtoId source, ProtoId sink)> Links; public readonly List<(string source, string sink)>? Defaults; public readonly string SourceAddress; public readonly string SinkAddress; public DeviceLinkUserInterfaceState( - List sources, - List sinks, + ProtoId[] sources, + ProtoId[] sinks, HashSet<(ProtoId source, ProtoId sink)> links, string sourceAddress, string sinkAddress, diff --git a/Content.Shared/Mind/RoleTypePrototype.cs b/Content.Shared/Mind/RoleTypePrototype.cs index 03f88ba751..690bd894a0 100644 --- a/Content.Shared/Mind/RoleTypePrototype.cs +++ b/Content.Shared/Mind/RoleTypePrototype.cs @@ -5,27 +5,31 @@ namespace Content.Shared.Mind; /// /// The core properties of Role Types /// -[Prototype, Serializable] +[Prototype] public sealed partial class RoleTypePrototype : IPrototype { [IdDataField] public string ID { get; private set; } = default!; + public static readonly LocId FallbackName = "role-type-crew-aligned-name"; + public const string FallbackSymbol = ""; + public static readonly Color FallbackColor = Color.FromHex("#eeeeee"); + /// /// The role's name as displayed on the UI. /// [DataField] - public LocId Name = "role-type-crew-aligned-name"; + public LocId Name = FallbackName; /// /// The role's displayed color. /// [DataField] - public Color Color = Color.FromHex("#eeeeee"); + public Color Color = FallbackColor; /// /// A symbol used to represent the role type. /// [DataField] - public string Symbol = string.Empty; + public string Symbol = FallbackSymbol; } diff --git a/Content.Shared/Roles/AntagPrototype.cs b/Content.Shared/Roles/AntagPrototype.cs index 3f5b2a1bd6..ff2712600a 100644 --- a/Content.Shared/Roles/AntagPrototype.cs +++ b/Content.Shared/Roles/AntagPrototype.cs @@ -8,7 +8,6 @@ namespace Content.Shared.Roles; /// Describes information for a single antag. /// [Prototype] -[Serializable, NetSerializable] public sealed partial class AntagPrototype : IPrototype { [ViewVariables] diff --git a/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs b/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs index 434f80e16e..d2bdf7d02b 100644 --- a/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs +++ b/Content.Shared/Silicons/Laws/SiliconLawPrototype.cs @@ -78,7 +78,6 @@ public partial class SiliconLaw : IComparable, IEquatable [Prototype] -[Serializable, NetSerializable] public sealed partial class SiliconLawPrototype : SiliconLaw, IPrototype { /// diff --git a/Content.Shared/Silicons/Laws/SiliconLawsetPrototype.cs b/Content.Shared/Silicons/Laws/SiliconLawsetPrototype.cs index 49c618ef8c..a3861504e9 100644 --- a/Content.Shared/Silicons/Laws/SiliconLawsetPrototype.cs +++ b/Content.Shared/Silicons/Laws/SiliconLawsetPrototype.cs @@ -61,7 +61,7 @@ public sealed partial class SiliconLawset /// This is a prototype for a list. /// Cannot be used directly since it is a list of prototype ids rather than List. /// -[Prototype, Serializable, NetSerializable] +[Prototype] public sealed partial class SiliconLawsetPrototype : IPrototype { /// diff --git a/Content.Shared/Speech/Components/VocalComponent.cs b/Content.Shared/Speech/Components/VocalComponent.cs index a520da4354..0f62a39d45 100644 --- a/Content.Shared/Speech/Components/VocalComponent.cs +++ b/Content.Shared/Speech/Components/VocalComponent.cs @@ -49,5 +49,5 @@ public sealed partial class VocalComponent : Component /// [ViewVariables] [AutoNetworkedField] - public EmoteSoundsPrototype? EmoteSounds = null; + public ProtoId? EmoteSounds = null; } diff --git a/Content.Shared/Store/CurrencyPrototype.cs b/Content.Shared/Store/CurrencyPrototype.cs index 03cc59d2df..fdd113d3a2 100644 --- a/Content.Shared/Store/CurrencyPrototype.cs +++ b/Content.Shared/Store/CurrencyPrototype.cs @@ -11,7 +11,7 @@ namespace Content.Shared.Store; /// This is separate to the cargo ordering system. /// [Prototype] -[DataDefinition, Serializable, NetSerializable] +[DataDefinition] public sealed partial class CurrencyPrototype : IPrototype { [ViewVariables] diff --git a/Content.Shared/Store/ListingPrototype.cs b/Content.Shared/Store/ListingPrototype.cs index b68c5ffa0d..4d01f91c44 100644 --- a/Content.Shared/Store/ListingPrototype.cs +++ b/Content.Shared/Store/ListingPrototype.cs @@ -242,7 +242,6 @@ public partial class ListingData : IEquatable /// Defines a set item listing that is available in a store /// [Prototype] -[Serializable, NetSerializable] [DataDefinition] public sealed partial class ListingPrototype : ListingData, IPrototype { @@ -423,7 +422,7 @@ public sealed partial class ListingDataWithCostModifiers : ListingData /// how will be filled by respective system. /// [Prototype] -[DataDefinition, Serializable, NetSerializable] +[DataDefinition] public sealed partial class DiscountCategoryPrototype : IPrototype { [ViewVariables] diff --git a/Content.Shared/Store/StoreCategoryPrototype.cs b/Content.Shared/Store/StoreCategoryPrototype.cs index 5713ecd98a..fcf7ec3496 100644 --- a/Content.Shared/Store/StoreCategoryPrototype.cs +++ b/Content.Shared/Store/StoreCategoryPrototype.cs @@ -7,7 +7,6 @@ namespace Content.Shared.Store; /// Used to define different categories for a store. /// [Prototype] -[Serializable, NetSerializable, DataDefinition] public sealed partial class StoreCategoryPrototype : IPrototype { private string _name = string.Empty; diff --git a/Content.Shared/StoryGen/Prototypes/StoryTemplatePrototype.cs b/Content.Shared/StoryGen/Prototypes/StoryTemplatePrototype.cs index 38dab902e2..564a3f16d4 100644 --- a/Content.Shared/StoryGen/Prototypes/StoryTemplatePrototype.cs +++ b/Content.Shared/StoryGen/Prototypes/StoryTemplatePrototype.cs @@ -6,7 +6,7 @@ namespace Content.Shared.StoryGen; /// /// Prototype for a story template that can be filled in with words chosen from s. /// -[Serializable, Prototype] +[Prototype] public sealed partial class StoryTemplatePrototype : IPrototype { /// diff --git a/Content.Shared/VendingMachines/VendingMachineInventoryPrototype.cs b/Content.Shared/VendingMachines/VendingMachineInventoryPrototype.cs index b39e29aead..be4816b319 100644 --- a/Content.Shared/VendingMachines/VendingMachineInventoryPrototype.cs +++ b/Content.Shared/VendingMachines/VendingMachineInventoryPrototype.cs @@ -4,7 +4,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy namespace Content.Shared.VendingMachines { - [Serializable, NetSerializable, Prototype] + [Prototype] public sealed partial class VendingMachineInventoryPrototype : IPrototype { [ViewVariables] From 75db49f9c0885c570b7f7c7c2ae61193914ae88a Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Thu, 26 Jun 2025 19:50:49 -0400 Subject: [PATCH 116/191] Clean up all missing `EntitySystem` proxy method uses (#38353) --- Content.Client/Actions/ActionsSystem.cs | 2 +- Content.Client/Audio/AmbientSoundSystem.cs | 2 +- .../Construction/ConstructionSystem.cs | 20 ++++++------- Content.Client/Examine/ExamineSystem.cs | 4 +-- .../Explosion/TriggerSystem.Proximity.cs | 2 +- Content.Client/Hands/Systems/HandsSystem.cs | 10 +++---- .../Humanoid/HumanoidAppearanceSystem.cs | 2 +- .../Inventory/ClientInventorySystem.cs | 8 ++--- .../EntitySystems/LightBehaviorSystem.cs | 12 ++++---- Content.Client/Markers/MarkerSystem.cs | 2 +- Content.Client/Orbit/OrbitVisualsSystem.cs | 2 +- Content.Client/Sandbox/SandboxSystem.cs | 2 +- Content.Client/Tabletop/TabletopSystem.cs | 4 +-- Content.Client/Verbs/VerbSystem.cs | 2 +- .../Weapons/Ranged/Systems/GunSystem.cs | 6 ++-- .../Tests/Disposal/DisposalUnitTest.cs | 2 +- .../Networking/SimplePredictReconcileTest.cs | 2 +- .../Access/Systems/AccessOverriderSystem.cs | 4 +-- .../Access/Systems/IdCardConsoleSystem.cs | 6 ++-- Content.Server/Access/Systems/IdCardSystem.cs | 4 +-- .../Administration/Systems/AdminSystem.cs | 2 +- .../Systems/AdminVerbSystem.Smites.cs | 4 +-- .../Systems/AdminVerbSystem.Tools.cs | 2 +- .../Administration/Systems/AdminVerbSystem.cs | 14 ++++----- .../Ame/EntitySystems/AmeControllerSystem.cs | 4 +-- .../Arcade/BlockGame/BlockGameArcadeSystem.cs | 2 +- .../SpaceVillainArcadeSystem.cs | 2 +- .../Consoles/AtmosMonitoringConsoleSystem.cs | 4 +-- .../Atmos/EntitySystems/AirtightSystem.cs | 2 +- .../AtmosphereSystem.Processing.cs | 8 ++--- .../Atmos/EntitySystems/FlammableSystem.cs | 2 +- .../Monitor/Systems/AtmosAlarmableSystem.cs | 2 +- .../Binary/EntitySystems/GasRecyclerSystem.cs | 2 +- .../EntitySystems/AtmosPipeColorSystem.cs | 6 ++-- .../AtmosUnsafeUnanchorSystem.cs | 4 +-- .../Trinary/EntitySystems/GasFilterSystem.cs | 4 +-- .../Trinary/EntitySystems/GasMixerSystem.cs | 6 ++-- .../Unary/EntitySystems/GasPortableSystem.cs | 4 +-- .../Unary/EntitySystems/GasVentPumpSystem.cs | 2 +- .../EntitySystems/GasVentScrubberSystem.cs | 2 +- Content.Server/Bible/BibleSystem.cs | 4 +-- .../Cargo/Systems/CargoSystem.Orders.cs | 2 +- .../Cartridges/NanoTaskCartridgeSystem.cs | 4 +-- Content.Server/Chat/SuicideSystem.cs | 4 +-- Content.Server/Chat/Systems/ChatSystem.cs | 2 +- .../DeleteOnSolutionEmptySystem.cs | 2 +- .../Chemistry/EntitySystems/InjectorSystem.cs | 8 ++--- .../Chemistry/EntitySystems/VaporSystem.cs | 8 ++--- Content.Server/Cloning/CloningPodSystem.cs | 12 ++++---- Content.Server/Cloning/CloningSystem.cs | 2 +- Content.Server/Codewords/CodewordSystem.cs | 2 +- .../ConstructionSystem.Computer.cs | 2 +- .../Construction/ConstructionSystem.Graph.cs | 4 +-- .../ConstructionSystem.Initial.cs | 6 ++-- .../Construction/ConstructionSystem.cs | 8 ++--- Content.Server/Crayon/CrayonSystem.cs | 4 +-- .../Systems/DamageOnToolInteractSystem.cs | 2 +- .../DeviceNetwork/Systems/ApcNetworkSystem.cs | 4 +-- .../Systems/Devices/ApcNetSwitchSystem.cs | 4 +-- .../Disposal/Tube/DisposalTubeSystem.cs | 2 +- .../Disposal/Unit/DisposableSystem.cs | 6 ++-- .../EntitySystems/SpawnAfterInteractSystem.cs | 4 +-- .../EntityEffects/EntityEffectSystem.cs | 20 ++++++------- Content.Server/Examine/ExamineSystem.cs | 2 +- .../EntitySystems/ExplosionSystem.Airtight.cs | 8 ++--- .../EntitySystems/ExplosionSystem.GridMap.cs | 2 +- .../EntitySystems/ExplosionSystem.cs | 2 +- .../EntitySystems/TriggerSystem.Proximity.cs | 4 +-- .../Explosion/EntitySystems/TriggerSystem.cs | 2 +- .../EntitySystems/TwoStageTriggerSystem.cs | 2 +- Content.Server/Fax/FaxSystem.cs | 2 +- .../Fluids/EntitySystems/DrainSystem.cs | 2 +- .../PuddleDebugDebugOverlaySystem.cs | 2 +- .../Fluids/EntitySystems/PuddleSystem.cs | 2 +- .../Systems/ForensicScannerSystem.cs | 4 +-- .../GameTicking/GameTicker.Spawning.cs | 4 +-- Content.Server/Ghost/GhostSystem.cs | 6 ++-- Content.Server/Ghost/Roles/GhostRoleSystem.cs | 4 +-- Content.Server/Hands/Systems/HandsSystem.cs | 4 +-- Content.Server/Holosign/HolosignSystem.cs | 2 +- .../Humanoid/Systems/RandomHumanoidSystem.cs | 4 +-- .../ImmovableRod/ImmovableRodSystem.cs | 4 +-- .../Instruments/InstrumentSystem.cs | 10 +++---- .../Kitchen/EntitySystems/MicrowaveSystem.cs | 4 +-- .../Kitchen/EntitySystems/SharpSystem.cs | 6 ++-- .../EntitySystems/ExpendableLightSystem.cs | 2 +- .../Light/EntitySystems/PoweredLightSystem.cs | 12 ++++---- .../MassMedia/Systems/NewsSystem.cs | 2 +- .../Mech/Systems/MechAssemblySystem.cs | 2 +- Content.Server/Medical/HealingSystem.cs | 4 +-- .../Medical/SuitSensors/SuitSensorSystem.cs | 4 +-- Content.Server/Morgue/CrematoriumSystem.cs | 4 +-- .../NPC/Systems/NPCSteeringSystem.cs | 2 +- .../Nutrition/EntitySystems/CreamPieSystem.cs | 6 ++-- .../EntitySystems/SmokingSystem.Cigar.cs | 6 ++-- .../SmokingSystem.SmokingPipe.cs | 6 ++-- .../EntitySystems/SmokingSystem.Vape.cs | 4 +-- .../TrashOnSolutionEmptySystem.cs | 2 +- .../Payload/EntitySystems/PayloadSystem.cs | 4 +-- .../Controllers/RandomWalkController.cs | 6 ++-- Content.Server/Pinpointer/PinpointerSystem.cs | 2 +- .../Pointing/EntitySystems/PointingSystem.cs | 4 +-- .../EntitySystems/RoguePointingSystem.cs | 4 +-- .../Power/EntitySystems/CableSystem.Placer.cs | 2 +- .../EntitySystems/ExtensionCableSystem.cs | 2 +- .../PowerMonitoringConsoleSystem.cs | 30 +++++++++---------- Content.Server/PowerSink/PowerSinkSystem.cs | 2 +- Content.Server/Prayer/PrayerSystem.cs | 4 +-- .../Procedural/DungeonSystem.Commands.cs | 2 +- .../Projectiles/ProjectileSystem.cs | 2 +- .../Systems/RadiationSystem.GridCast.cs | 2 +- Content.Server/Repairable/RepairableSystem.cs | 2 +- .../Research/Disk/ResearchDiskSystem.cs | 2 +- Content.Server/Rotatable/RotatableSystem.cs | 24 +++++++-------- .../Shuttles/Systems/DockingSystem.cs | 22 +++++++------- .../Shuttles/Systems/ShuttleConsoleSystem.cs | 8 ++--- .../Shuttles/Systems/ShuttleSystem.cs | 8 ++--- .../Shuttles/Systems/ThrusterSystem.cs | 18 +++++------ .../Silicons/StationAi/StationAiSystem.cs | 2 +- .../EntitySystems/EventHorizonSystem.cs | 6 ++-- .../EntitySystems/RadiationCollectorSystem.cs | 2 +- .../SingularityGeneratorSystem.cs | 8 ++--- .../EntitySystems/SingularitySystem.cs | 4 +-- .../Solar/EntitySystems/PowerSolarSystem.cs | 2 +- .../EntitySystems/ConditionalSpawnerSystem.cs | 6 ++-- Content.Server/Species/Systems/NymphSystem.cs | 2 +- .../EntitySystems/AddAccentClothingSystem.cs | 2 +- Content.Server/Spreader/KudzuSystem.cs | 4 +-- .../Station/Systems/StationSpawningSystem.cs | 2 +- .../StationEvents/Events/VentClogRule.cs | 2 +- .../EntitySystems/BluespaceLockerSystem.cs | 4 +-- .../EntitySystems/ItemCounterSystem.cs | 2 +- .../EntitySystems/SpawnItemsOnUseSystem.cs | 4 +-- .../Store/Systems/StoreSystem.Ui.cs | 2 +- .../Stunnable/Systems/StunOnCollideSystem.cs | 2 +- .../SurveillanceCameraMonitorSystem.cs | 4 +-- Content.Server/Tabletop/TabletopSystem.Map.cs | 2 +- .../Tabletop/TabletopSystem.Session.cs | 16 +++++----- Content.Server/Tabletop/TabletopSystem.cs | 10 +++---- Content.Server/Telephone/TelephoneSystem.cs | 2 +- Content.Server/Traits/TraitSystem.cs | 2 +- Content.Server/Verbs/VerbSystem.cs | 2 +- Content.Server/Wires/WiresSystem.cs | 4 +-- .../Access/Systems/IdExaminableSystem.cs | 4 +-- Content.Shared/Actions/ActionUpgradeSystem.cs | 4 +-- Content.Shared/Alert/AlertsSystem.cs | 14 ++++----- Content.Shared/Animals/UdderSystem.cs | 4 +-- Content.Shared/Animals/WoolySystem.cs | 2 +- .../Cryostorage/SharedCryostorageSystem.cs | 2 +- .../EntitySystems/HypospraySystem.cs | 2 +- .../SharedSolutionContainerSystem.cs | 6 ++-- .../MetabolismMovespeedModifierSystem.cs | 2 +- .../EntitySystems/AnchorableSystem.cs | 4 +-- .../Containers/ContainerFillSystem.cs | 4 +-- .../Containers/ItemSlot/ItemSlotsSystem.cs | 8 ++--- Content.Shared/Cuffs/SharedCuffableSystem.cs | 2 +- .../Systems/DamageOnHighSpeedImpactSystem.cs | 2 +- .../Damage/Systems/SlowOnDamageSystem.cs | 2 +- .../Mailing/SharedMailingUnitSystem.cs | 2 +- Content.Shared/Examine/ExamineSystemShared.cs | 2 +- Content.Shared/Gravity/SharedGravitySystem.cs | 2 +- .../Implants/SharedImplanterSystem.cs | 2 +- .../Inventory/InventorySystem.Helpers.cs | 4 +-- .../Jittering/SharedJitteringSystem.cs | 4 +-- Content.Shared/Magic/SharedMagicSystem.cs | 2 +- .../Movement/Pulling/Systems/PullingSystem.cs | 4 +-- .../Systems/FrictionContactsSystem.cs | 2 +- .../Systems/SpeedModifierContactsSystem.cs | 2 +- .../EntitySystems/SharedCreamPieSystem.cs | 4 +-- .../Nutrition/EntitySystems/UtensilSystem.cs | 4 +-- Content.Shared/Paper/PaperSystem.cs | 2 +- Content.Shared/RCD/Systems/RCDSystem.cs | 4 +-- .../EntitySystems/SharedEventHorizonSystem.cs | 2 +- .../Station/SharedStationSpawningSystem.cs | 4 +-- .../StatusEffect/StatusEffectsSystem.cs | 8 ++--- .../EntitySystems/SharedItemCounterSystem.cs | 4 +-- .../EntitySystems/SharedItemMapperSystem.cs | 4 +-- .../SubFloor/SharedSubFloorHideSystem.cs | 4 +-- .../Tabletop/SharedTabletopSystem.cs | 2 +- Content.Shared/Throwing/ThrownItemSystem.cs | 6 ++-- .../Systems/SharedToolSystem.MultipleTool.cs | 2 +- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 2 +- .../Systems/SharedGunSystem.Revolver.cs | 2 +- .../Weapons/Ranged/Systems/SharedGunSystem.cs | 10 +++---- .../Artifact/XAE/XAEApplyComponentsSystem.cs | 6 ++-- 185 files changed, 418 insertions(+), 418 deletions(-) diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs index 23ff23997f..3bfacb5bc6 100644 --- a/Content.Client/Actions/ActionsSystem.cs +++ b/Content.Client/Actions/ActionsSystem.cs @@ -211,7 +211,7 @@ namespace Content.Client.Actions else { var request = new RequestPerformActionEvent(GetNetEntity(action)); - EntityManager.RaisePredictiveEvent(request); + RaisePredictiveEvent(request); } } diff --git a/Content.Client/Audio/AmbientSoundSystem.cs b/Content.Client/Audio/AmbientSoundSystem.cs index b525747aa9..e6ea94c3a6 100644 --- a/Content.Client/Audio/AmbientSoundSystem.cs +++ b/Content.Client/Audio/AmbientSoundSystem.cs @@ -168,7 +168,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem _targetTime = _gameTiming.CurTime + TimeSpan.FromSeconds(_cooldown); var player = _playerManager.LocalEntity; - if (!EntityManager.TryGetComponent(player, out TransformComponent? xform)) + if (!TryComp(player, out TransformComponent? xform)) { ClearSounds(); return; diff --git a/Content.Client/Construction/ConstructionSystem.cs b/Content.Client/Construction/ConstructionSystem.cs index 24d339dfce..0e7557724f 100644 --- a/Content.Client/Construction/ConstructionSystem.cs +++ b/Content.Client/Construction/ConstructionSystem.cs @@ -286,14 +286,14 @@ namespace Content.Client.Construction if (!CheckConstructionConditions(prototype, loc, dir, user, showPopup: true)) return false; - ghost = EntityManager.SpawnEntity("constructionghost", loc); - var comp = EntityManager.GetComponent(ghost.Value); + ghost = Spawn("constructionghost", loc); + var comp = Comp(ghost.Value); comp.Prototype = prototype; comp.GhostId = ghost.GetHashCode(); - EntityManager.GetComponent(ghost.Value).LocalRotation = dir.ToAngle(); + Comp(ghost.Value).LocalRotation = dir.ToAngle(); _ghosts.Add(comp.GhostId, ghost.Value); - var sprite = EntityManager.GetComponent(ghost.Value); + var sprite = Comp(ghost.Value); _sprite.SetColor((ghost.Value, sprite), new Color(48, 255, 48, 128)); if (targetProto.TryGetComponent(out IconComponent? icon, EntityManager.ComponentFactory)) @@ -306,7 +306,7 @@ namespace Content.Client.Construction else if (targetProto.Components.TryGetValue("Sprite", out _)) { var dummy = EntityManager.SpawnEntity(targetProtoId, MapCoordinates.Nullspace); - var targetSprite = EntityManager.EnsureComponent(dummy); + var targetSprite = EnsureComp(dummy); EntityManager.System().OnChangeData(dummy, targetSprite); for (var i = 0; i < targetSprite.AllLayers.Count(); i++) @@ -325,7 +325,7 @@ namespace Content.Client.Construction _sprite.LayerSetVisible((ghost.Value, sprite), i, true); } - EntityManager.DeleteEntity(dummy); + Del(dummy); } else return false; @@ -367,7 +367,7 @@ namespace Content.Client.Construction { foreach (var ghost in _ghosts) { - if (EntityManager.GetComponent(ghost.Value).Coordinates.Equals(loc)) + if (Comp(ghost.Value).Coordinates.Equals(loc)) return true; } @@ -384,7 +384,7 @@ namespace Content.Client.Construction throw new ArgumentException($"Can't start construction for a ghost with no prototype. Ghost id: {ghostId}"); } - var transform = EntityManager.GetComponent(ghostId); + var transform = Comp(ghostId); var msg = new TryStartStructureConstructionMessage(GetNetCoordinates(transform.Coordinates), ghostComp.Prototype.ID, transform.LocalRotation, ghostId.GetHashCode()); RaiseNetworkEvent(msg); } @@ -405,7 +405,7 @@ namespace Content.Client.Construction if (!_ghosts.TryGetValue(ghostId, out var ghost)) return; - EntityManager.QueueDeleteEntity(ghost); + QueueDel(ghost); _ghosts.Remove(ghostId); } @@ -416,7 +416,7 @@ namespace Content.Client.Construction { foreach (var ghost in _ghosts.Values) { - EntityManager.QueueDeleteEntity(ghost); + QueueDel(ghost); } _ghosts.Clear(); diff --git a/Content.Client/Examine/ExamineSystem.cs b/Content.Client/Examine/ExamineSystem.cs index 9f0496e578..0385672977 100644 --- a/Content.Client/Examine/ExamineSystem.cs +++ b/Content.Client/Examine/ExamineSystem.cs @@ -110,7 +110,7 @@ namespace Content.Client.Examine { var entity = args.EntityUid; - if (!args.EntityUid.IsValid() || !EntityManager.EntityExists(entity)) + if (!args.EntityUid.IsValid() || !Exists(entity)) { return false; } @@ -225,7 +225,7 @@ namespace Content.Client.Examine vBox.AddChild(hBox); - if (EntityManager.HasComponent(target)) + if (HasComp(target)) { var spriteView = new SpriteView { diff --git a/Content.Client/Explosion/TriggerSystem.Proximity.cs b/Content.Client/Explosion/TriggerSystem.Proximity.cs index d49f483664..03e7436971 100644 --- a/Content.Client/Explosion/TriggerSystem.Proximity.cs +++ b/Content.Client/Explosion/TriggerSystem.Proximity.cs @@ -61,7 +61,7 @@ public sealed partial class TriggerSystem private void OnProximityInit(EntityUid uid, TriggerOnProximityComponent component, ComponentInit args) { - EntityManager.EnsureComponent(uid); + EnsureComp(uid); } private void OnProxAppChange(EntityUid uid, TriggerOnProximityComponent component, ref AppearanceChangeEvent args) diff --git a/Content.Client/Hands/Systems/HandsSystem.cs b/Content.Client/Hands/Systems/HandsSystem.cs index 37bc9b1232..2613313585 100644 --- a/Content.Client/Hands/Systems/HandsSystem.cs +++ b/Content.Client/Hands/Systems/HandsSystem.cs @@ -135,28 +135,28 @@ namespace Content.Client.Hands.Systems { // use item in hand // it will always be attack_self() in my heart. - EntityManager.RaisePredictiveEvent(new RequestUseInHandEvent()); + RaisePredictiveEvent(new RequestUseInHandEvent()); return; } if (handName != hands.ActiveHandId && pressedEntity == null) { // change active hand - EntityManager.RaisePredictiveEvent(new RequestSetHandEvent(handName)); + RaisePredictiveEvent(new RequestSetHandEvent(handName)); return; } if (handName != hands.ActiveHandId && pressedEntity != null && activeEntity != null) { // use active item on held item - EntityManager.RaisePredictiveEvent(new RequestHandInteractUsingEvent(handName)); + RaisePredictiveEvent(new RequestHandInteractUsingEvent(handName)); return; } if (handName != hands.ActiveHandId && pressedEntity != null && activeEntity == null) { // move the item to the active hand - EntityManager.RaisePredictiveEvent(new RequestMoveHandItemEvent(handName)); + RaisePredictiveEvent(new RequestMoveHandItemEvent(handName)); } } @@ -166,7 +166,7 @@ namespace Content.Client.Hands.Systems /// public void UIHandActivate(string handName) { - EntityManager.RaisePredictiveEvent(new RequestActivateInHandEvent(handName)); + RaisePredictiveEvent(new RequestActivateInHandEvent(handName)); } public void UIInventoryExamine(string handName) diff --git a/Content.Client/Humanoid/HumanoidAppearanceSystem.cs b/Content.Client/Humanoid/HumanoidAppearanceSystem.cs index d9897c56e2..6700cf2a18 100644 --- a/Content.Client/Humanoid/HumanoidAppearanceSystem.cs +++ b/Content.Client/Humanoid/HumanoidAppearanceSystem.cs @@ -36,7 +36,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem private void OnCvarChanged(bool value) { - var humanoidQuery = EntityManager.AllEntityQueryEnumerator(); + var humanoidQuery = AllEntityQuery(); while (humanoidQuery.MoveNext(out var uid, out var humanoidComp, out var spriteComp)) { UpdateSprite((uid, humanoidComp, spriteComp)); diff --git a/Content.Client/Inventory/ClientInventorySystem.cs b/Content.Client/Inventory/ClientInventorySystem.cs index dce401eefd..de58077e44 100644 --- a/Content.Client/Inventory/ClientInventorySystem.cs +++ b/Content.Client/Inventory/ClientInventorySystem.cs @@ -194,12 +194,12 @@ namespace Content.Client.Inventory public void UIInventoryActivate(string slot) { - EntityManager.RaisePredictiveEvent(new UseSlotNetworkMessage(slot)); + RaisePredictiveEvent(new UseSlotNetworkMessage(slot)); } public void UIInventoryStorageActivate(string slot) { - EntityManager.RaisePredictiveEvent(new OpenSlotStorageNetworkMessage(slot)); + RaisePredictiveEvent(new OpenSlotStorageNetworkMessage(slot)); } public void UIInventoryExamine(string slot, EntityUid uid) @@ -223,7 +223,7 @@ namespace Content.Client.Inventory if (!TryGetSlotEntity(uid, slot, out var item)) return; - EntityManager.RaisePredictiveEvent( + RaisePredictiveEvent( new InteractInventorySlotEvent(GetNetEntity(item.Value), altInteract: false)); } @@ -232,7 +232,7 @@ namespace Content.Client.Inventory if (!TryGetSlotEntity(uid, slot, out var item)) return; - EntityManager.RaisePredictiveEvent(new InteractInventorySlotEvent(GetNetEntity(item.Value), altInteract: true)); + RaisePredictiveEvent(new InteractInventorySlotEvent(GetNetEntity(item.Value), altInteract: true)); } protected override void UpdateInventoryTemplate(Entity ent) diff --git a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs index 5b823946f7..d4eaad3882 100644 --- a/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs +++ b/Content.Client/Light/EntitySystems/LightBehaviorSystem.cs @@ -63,7 +63,7 @@ public sealed class LightBehaviorSystem : EntitySystem /// private void CopyLightSettings(Entity entity, string property) { - if (EntityManager.TryGetComponent(entity, out PointLightComponent? light)) + if (TryComp(entity, out PointLightComponent? light)) { var propertyValue = AnimationHelper.GetAnimatableProperty(light, property); if (propertyValue != null) @@ -73,7 +73,7 @@ public sealed class LightBehaviorSystem : EntitySystem } else { - Log.Warning($"{EntityManager.GetComponent(entity).EntityName} has a {nameof(LightBehaviourComponent)} but it has no {nameof(PointLightComponent)}! Check the prototype!"); + Log.Warning($"{Comp(entity).EntityName} has a {nameof(LightBehaviourComponent)} but it has no {nameof(PointLightComponent)}! Check the prototype!"); } } @@ -84,7 +84,7 @@ public sealed class LightBehaviorSystem : EntitySystem /// public void StartLightBehaviour(Entity entity, string id = "") { - if (!EntityManager.TryGetComponent(entity, out AnimationPlayerComponent? animation)) + if (!TryComp(entity, out AnimationPlayerComponent? animation)) { return; } @@ -113,7 +113,7 @@ public sealed class LightBehaviorSystem : EntitySystem /// Should the light have its original settings applied? public void StopLightBehaviour(Entity entity, string id = "", bool removeBehaviour = false, bool resetToOriginalSettings = false) { - if (!EntityManager.TryGetComponent(entity, out AnimationPlayerComponent? animation)) + if (!TryComp(entity, out AnimationPlayerComponent? animation)) { return; } @@ -143,7 +143,7 @@ public sealed class LightBehaviorSystem : EntitySystem comp.Animations.Remove(container); } - if (resetToOriginalSettings && EntityManager.TryGetComponent(entity, out PointLightComponent? light)) + if (resetToOriginalSettings && TryComp(entity, out PointLightComponent? light)) { foreach (var (property, value) in comp.OriginalPropertyValues) { @@ -161,7 +161,7 @@ public sealed class LightBehaviorSystem : EntitySystem public bool HasRunningBehaviours(Entity entity) { //var uid = Owner; - if (!EntityManager.TryGetComponent(entity, out AnimationPlayerComponent? animation)) + if (!TryComp(entity, out AnimationPlayerComponent? animation)) { return false; } diff --git a/Content.Client/Markers/MarkerSystem.cs b/Content.Client/Markers/MarkerSystem.cs index 2ff567e69e..37888d16c2 100644 --- a/Content.Client/Markers/MarkerSystem.cs +++ b/Content.Client/Markers/MarkerSystem.cs @@ -33,7 +33,7 @@ public sealed class MarkerSystem : EntitySystem private void UpdateVisibility(EntityUid uid) { - if (EntityManager.TryGetComponent(uid, out SpriteComponent? sprite)) + if (TryComp(uid, out SpriteComponent? sprite)) { _sprite.SetVisible((uid, sprite), MarkersVisible); } diff --git a/Content.Client/Orbit/OrbitVisualsSystem.cs b/Content.Client/Orbit/OrbitVisualsSystem.cs index 9824ccac29..144c489a54 100644 --- a/Content.Client/Orbit/OrbitVisualsSystem.cs +++ b/Content.Client/Orbit/OrbitVisualsSystem.cs @@ -64,7 +64,7 @@ public sealed class OrbitVisualsSystem : EntitySystem { base.FrameUpdate(frameTime); - var query = EntityManager.EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var orbit, out var sprite)) { var progress = (float)(_timing.CurTime.TotalSeconds / orbit.OrbitLength) % 1; diff --git a/Content.Client/Sandbox/SandboxSystem.cs b/Content.Client/Sandbox/SandboxSystem.cs index abc717642c..99a84fa23b 100644 --- a/Content.Client/Sandbox/SandboxSystem.cs +++ b/Content.Client/Sandbox/SandboxSystem.cs @@ -90,7 +90,7 @@ namespace Content.Client.Sandbox // Try copy entity. if (uid.IsValid() - && EntityManager.TryGetComponent(uid, out MetaDataComponent? comp) + && TryComp(uid, out MetaDataComponent? comp) && !comp.EntityDeleted) { if (comp.EntityPrototype == null || comp.EntityPrototype.HideSpawnMenu || comp.EntityPrototype.Abstract) diff --git a/Content.Client/Tabletop/TabletopSystem.cs b/Content.Client/Tabletop/TabletopSystem.cs index f83b0e0bd0..25d5907b05 100644 --- a/Content.Client/Tabletop/TabletopSystem.cs +++ b/Content.Client/Tabletop/TabletopSystem.cs @@ -131,7 +131,7 @@ namespace Content.Client.Tabletop // Get the camera entity that the server has created for us var camera = GetEntity(msg.CameraUid); - if (!EntityManager.TryGetComponent(camera, out var eyeComponent)) + if (!TryComp(camera, out var eyeComponent)) { // If there is no eye, print error and do not open any window Log.Error("Camera entity does not have eye component!"); @@ -258,7 +258,7 @@ namespace Content.Client.Tabletop private void StopDragging(bool broadcast = true) { // Set the dragging player on the component to noone - if (broadcast && _draggedEntity != null && EntityManager.HasComponent(_draggedEntity.Value)) + if (broadcast && _draggedEntity != null && HasComp(_draggedEntity.Value)) { RaisePredictiveEvent(new TabletopMoveEvent(GetNetEntity(_draggedEntity.Value), Transforms.GetMapCoordinates(_draggedEntity.Value), GetNetEntity(_table!.Value))); RaisePredictiveEvent(new TabletopDraggingPlayerChangedEvent(GetNetEntity(_draggedEntity.Value), false)); diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 92596f9dc0..77b8531888 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -225,7 +225,7 @@ namespace Content.Client.Verbs // is this a client exclusive (gui) verb? ExecuteVerb(verb, user, GetEntity(target)); else - EntityManager.RaisePredictiveEvent(new ExecuteVerbEvent(target, verb)); + RaisePredictiveEvent(new ExecuteVerbEvent(target, verb)); } private void HandleVerbResponse(VerbsResponseEvent msg) diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index de2f4e42b1..dd26effb33 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -178,7 +178,7 @@ public sealed partial class GunSystem : SharedGunSystem if (_inputSystem.CmdStates.GetState(useKey) != BoundKeyState.Down && !gun.BurstActivated) { if (gun.ShotCounter != 0) - EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = GetNetEntity(gunUid) }); + RaisePredictiveEvent(new RequestStopShootEvent { Gun = GetNetEntity(gunUid) }); return; } @@ -190,7 +190,7 @@ public sealed partial class GunSystem : SharedGunSystem if (mousePos.MapId == MapId.Nullspace) { if (gun.ShotCounter != 0) - EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = GetNetEntity(gunUid) }); + RaisePredictiveEvent(new RequestStopShootEvent { Gun = GetNetEntity(gunUid) }); return; } @@ -204,7 +204,7 @@ public sealed partial class GunSystem : SharedGunSystem Log.Debug($"Sending shoot request tick {Timing.CurTick} / {Timing.CurTime}"); - EntityManager.RaisePredictiveEvent(new RequestShootEvent + RaisePredictiveEvent(new RequestShootEvent { Target = target, Coordinates = GetNetCoordinates(coordinates), diff --git a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs index 8cfe8fbfcd..52b669b09d 100644 --- a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs +++ b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs @@ -28,7 +28,7 @@ namespace Content.IntegrationTests.Tests.Disposal SubscribeLocalEvent(ev => { var (_, toInsert, unit) = ev; - var insertTransform = EntityManager.GetComponent(toInsert); + var insertTransform = Comp(toInsert); // Not in a tube yet Assert.That(insertTransform.ParentUid, Is.EqualTo(unit)); }, after: new[] { typeof(SharedDisposalUnitSystem) }); diff --git a/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs b/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs index 29f2573c2d..3e49499845 100644 --- a/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs +++ b/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs @@ -410,7 +410,7 @@ namespace Content.IntegrationTests.Tests.Networking { var uid = GetEntity(message.Uid); - var component = EntityManager.GetComponent(uid); + var component = Comp(uid); var old = component.Foo; if (Allow) { diff --git a/Content.Server/Access/Systems/AccessOverriderSystem.cs b/Content.Server/Access/Systems/AccessOverriderSystem.cs index 51d35c50a4..68bdd6b9a9 100644 --- a/Content.Server/Access/Systems/AccessOverriderSystem.cs +++ b/Content.Server/Access/Systems/AccessOverriderSystem.cs @@ -113,7 +113,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem if (component.TargetAccessReaderId is { Valid: true } accessReader) { - targetLabel = Loc.GetString("access-overrider-window-target-label") + " " + EntityManager.GetComponent(component.TargetAccessReaderId).EntityName; + targetLabel = Loc.GetString("access-overrider-window-target-label") + " " + Comp(component.TargetAccessReaderId).EntityName; targetLabelColor = Color.White; if (!_accessReader.GetMainAccessReader(accessReader, out var accessReaderEnt)) @@ -125,7 +125,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem if (component.PrivilegedIdSlot.Item is { Valid: true } idCard) { - privilegedIdName = EntityManager.GetComponent(idCard).EntityName; + privilegedIdName = Comp(idCard).EntityName; if (component.TargetAccessReaderId is { Valid: true }) { diff --git a/Content.Server/Access/Systems/IdCardConsoleSystem.cs b/Content.Server/Access/Systems/IdCardConsoleSystem.cs index 9fd30ffcb1..62dfddbb58 100644 --- a/Content.Server/Access/Systems/IdCardConsoleSystem.cs +++ b/Content.Server/Access/Systems/IdCardConsoleSystem.cs @@ -73,7 +73,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem List>? possibleAccess = null; if (component.PrivilegedIdSlot.Item is { Valid: true } item) { - privilegedIdName = EntityManager.GetComponent(item).EntityName; + privilegedIdName = Comp(item).EntityName; possibleAccess = _accessReader.FindAccessTags(item).ToList(); } @@ -95,8 +95,8 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem } else { - var targetIdComponent = EntityManager.GetComponent(targetId); - var targetAccessComponent = EntityManager.GetComponent(targetId); + var targetIdComponent = Comp(targetId); + var targetAccessComponent = Comp(targetId); var jobProto = targetIdComponent.JobPrototype ?? new ProtoId(string.Empty); if (TryComp(targetId, out var keyStorage) diff --git a/Content.Server/Access/Systems/IdCardSystem.cs b/Content.Server/Access/Systems/IdCardSystem.cs index 05ee45b463..0fef62d970 100644 --- a/Content.Server/Access/Systems/IdCardSystem.cs +++ b/Content.Server/Access/Systems/IdCardSystem.cs @@ -47,12 +47,12 @@ public sealed class IdCardSystem : SharedIdCardSystem { _popupSystem.PopupCoordinates(Loc.GetString("id-card-component-microwave-burnt", ("id", uid)), transformComponent.Coordinates, PopupType.Medium); - EntityManager.SpawnEntity("FoodBadRecipe", + Spawn("FoodBadRecipe", transformComponent.Coordinates); } _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.Microwave)} burnt {ToPrettyString(uid):entity}"); - EntityManager.QueueDeleteEntity(uid); + QueueDel(uid); return; } diff --git a/Content.Server/Administration/Systems/AdminSystem.cs b/Content.Server/Administration/Systems/AdminSystem.cs index 95c6578a94..56bbc225cc 100644 --- a/Content.Server/Administration/Systems/AdminSystem.cs +++ b/Content.Server/Administration/Systems/AdminSystem.cs @@ -224,7 +224,7 @@ public sealed class AdminSystem : EntitySystem // Visible (identity) name can be different from real name if (session?.AttachedEntity != null) { - entityName = EntityManager.GetComponent(session.AttachedEntity.Value).EntityName; + entityName = Comp(session.AttachedEntity.Value).EntityName; identityName = Identity.Name(session.AttachedEntity.Value, EntityManager); } diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index 1ae0543073..442c768709 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -83,7 +83,7 @@ public sealed partial class AdminVerbSystem // All smite verbs have names so invokeverb works. private void AddSmiteVerbs(GetVerbsEvent args) { - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + if (!TryComp(args.User, out ActorComponent? actor)) return; var player = actor.PlayerSession; @@ -622,7 +622,7 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Materials/materials.rsi"), "ash"), Act = () => { - EntityManager.QueueDeleteEntity(args.Target); + QueueDel(args.Target); Spawn("Ash", Transform(args.Target).Coordinates); _popupSystem.PopupEntity(Loc.GetString("admin-smite-turned-ash-other", ("name", args.Target)), args.Target, PopupType.LargeCaution); }, diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs index 22a1ccadc9..6e88d59be6 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs @@ -58,7 +58,7 @@ public sealed partial class AdminVerbSystem private void AddTricksVerbs(GetVerbsEvent args) { - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + if (!TryComp(args.User, out ActorComponent? actor)) return; var player = actor.PlayerSession; diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.cs b/Content.Server/Administration/Systems/AdminVerbSystem.cs index 15ec7da467..b5ddc4bb19 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.cs @@ -88,7 +88,7 @@ namespace Content.Server.Administration.Systems private void AddAdminVerbs(GetVerbsEvent args) { - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + if (!TryComp(args.User, out ActorComponent? actor)) return; var player = actor.PlayerSession; @@ -395,7 +395,7 @@ namespace Content.Server.Administration.Systems private void AddDebugVerbs(GetVerbsEvent args) { - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + if (!TryComp(args.User, out ActorComponent? actor)) return; var player = actor.PlayerSession; @@ -408,7 +408,7 @@ namespace Content.Server.Administration.Systems Text = Loc.GetString("delete-verb-get-data-text"), Category = VerbCategory.Debug, Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png")), - Act = () => EntityManager.DeleteEntity(args.Target), + Act = () => Del(args.Target), Impact = LogImpact.Medium, ConfirmationPopup = true }; @@ -451,7 +451,7 @@ namespace Content.Server.Administration.Systems // Make Sentient verb if (_groupController.CanCommand(player, "makesentient") && args.User != args.Target && - !EntityManager.HasComponent(args.Target)) + !HasComp(args.Target)) { Verb verb = new() { @@ -517,7 +517,7 @@ namespace Content.Server.Administration.Systems // Get Disposal tube direction verb if (_groupController.CanCommand(player, "tubeconnections") && - EntityManager.TryGetComponent(args.Target, out DisposalTubeComponent? tube)) + TryComp(args.Target, out DisposalTubeComponent? tube)) { Verb verb = new() { @@ -544,7 +544,7 @@ namespace Content.Server.Administration.Systems } if (_groupController.CanAdminMenu(player) && - EntityManager.TryGetComponent(args.Target, out ConfigurationComponent? config)) + TryComp(args.Target, out ConfigurationComponent? config)) { Verb verb = new() { @@ -558,7 +558,7 @@ namespace Content.Server.Administration.Systems // Add verb to open Solution Editor if (_groupController.CanCommand(player, "addreagent") && - EntityManager.HasComponent(args.Target)) + HasComp(args.Target)) { Verb verb = new() { diff --git a/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs b/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs index a2e25f269a..4276eafd60 100644 --- a/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs +++ b/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs @@ -246,7 +246,7 @@ public sealed class AmeControllerSystem : EntitySystem return; var humanReadableState = value ? "Inject" : "Not inject"; - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{EntityManager.ToPrettyString(user.Value):player} has set the AME to {humanReadableState}"); + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user.Value):player} has set the AME to {humanReadableState}"); } public void ToggleInjecting(EntityUid uid, EntityUid? user = null, AmeControllerComponent? controller = null) @@ -281,7 +281,7 @@ public sealed class AmeControllerSystem : EntitySystem var logImpact = (oldValue <= safeLimit && value > safeLimit) ? LogImpact.Extreme : LogImpact.Medium; - _adminLogger.Add(LogType.Action, logImpact, $"{EntityManager.ToPrettyString(user.Value):player} has set the AME to inject {controller.InjectionAmount} while set to {humanReadableState}"); + _adminLogger.Add(LogType.Action, logImpact, $"{ToPrettyString(user.Value):player} has set the AME to inject {controller.InjectionAmount} while set to {humanReadableState}"); } public void AdjustInjectionAmount(EntityUid uid, int delta, EntityUid? user = null, AmeControllerComponent? controller = null) diff --git a/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs b/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs index a0e52e9b48..1e8ed8cdf3 100644 --- a/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs +++ b/Content.Server/Arcade/BlockGame/BlockGameArcadeSystem.cs @@ -29,7 +29,7 @@ public sealed class BlockGameArcadeSystem : EntitySystem public override void Update(float frameTime) { - var query = EntityManager.EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); while (query.MoveNext(out var _, out var blockGame)) { blockGame.Game?.GameTick(frameTime); diff --git a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs index 2070ab8bfe..bb717c7012 100644 --- a/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs +++ b/Content.Server/Arcade/SpaceVillainGame/SpaceVillainArcadeSystem.cs @@ -42,7 +42,7 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem if (arcade.RewardAmount <= 0) return; - EntityManager.SpawnEntity(_random.Pick(arcade.PossibleRewards), xform.Coordinates); + Spawn(_random.Pick(arcade.PossibleRewards), xform.Coordinates); arcade.RewardAmount--; } diff --git a/Content.Server/Atmos/Consoles/AtmosMonitoringConsoleSystem.cs b/Content.Server/Atmos/Consoles/AtmosMonitoringConsoleSystem.cs index a7debbdc80..3cce1c956e 100644 --- a/Content.Server/Atmos/Consoles/AtmosMonitoringConsoleSystem.cs +++ b/Content.Server/Atmos/Consoles/AtmosMonitoringConsoleSystem.cs @@ -497,7 +497,7 @@ public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleS if (component.NavMapBlip == null) return; - var netEntity = EntityManager.GetNetEntity(uid); + var netEntity = GetNetEntity(uid); var query = AllEntityQuery(); while (query.MoveNext(out var ent, out var entConsole, out var entXform)) @@ -531,7 +531,7 @@ public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleS if (component.NavMapBlip == null) return; - var netEntity = EntityManager.GetNetEntity(uid); + var netEntity = GetNetEntity(uid); var query = AllEntityQuery(); while (query.MoveNext(out var ent, out var entConsole)) diff --git a/Content.Server/Atmos/EntitySystems/AirtightSystem.cs b/Content.Server/Atmos/EntitySystems/AirtightSystem.cs index cd07da7112..4a108b38a4 100644 --- a/Content.Server/Atmos/EntitySystems/AirtightSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AirtightSystem.cs @@ -122,7 +122,7 @@ namespace Content.Server.Atmos.EntitySystems public void InvalidatePosition(Entity grid, Vector2i pos) { - var query = EntityManager.GetEntityQuery(); + var query = GetEntityQuery(); _explosionSystem.UpdateAirtightMap(grid, pos, grid, query); _atmosphereSystem.InvalidateTile(grid.Owner, pos); } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs index 59320ba67b..02d389b215 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs @@ -390,10 +390,10 @@ namespace Content.Server.Atmos.EntitySystems // Note: This is still processed even if space wind is turned off since this handles playing the sounds. var number = 0; - var bodies = EntityManager.GetEntityQuery(); - var xforms = EntityManager.GetEntityQuery(); - var metas = EntityManager.GetEntityQuery(); - var pressureQuery = EntityManager.GetEntityQuery(); + var bodies = GetEntityQuery(); + var xforms = GetEntityQuery(); + var metas = GetEntityQuery(); + var pressureQuery = GetEntityQuery(); while (atmosphere.CurrentRunTiles.TryDequeue(out var tile)) { diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index 0cb40627ab..13b3e480c9 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -119,7 +119,7 @@ namespace Content.Server.Atmos.EntitySystems var otherEnt = args.OtherEntity; - if (!EntityManager.TryGetComponent(otherEnt, out FlammableComponent? flammable)) + if (!TryComp(otherEnt, out FlammableComponent? flammable)) return; //Only ignite when the colliding fixture is projectile or ignition. diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs index 918b0f33ff..b6bc4bd303 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosAlarmableSystem.cs @@ -82,7 +82,7 @@ public sealed class AtmosAlarmableSystem : EntitySystem { if (component.IgnoreAlarms) return; - if (!EntityManager.TryGetComponent(uid, out DeviceNetworkComponent? netConn)) + if (!TryComp(uid, out DeviceNetworkComponent? netConn)) return; if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? cmd) diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs index f778979409..9645b0a133 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs @@ -39,7 +39,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems private void OnExamined(Entity ent, ref ExaminedEvent args) { var comp = ent.Comp; - if (!EntityManager.GetComponent(ent).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status. + if (!Comp(ent).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status. return; if (!_nodeContainer.TryGetNode(ent.Owner, comp.InletName, out PipeNode? inlet)) diff --git a/Content.Server/Atmos/Piping/EntitySystems/AtmosPipeColorSystem.cs b/Content.Server/Atmos/Piping/EntitySystems/AtmosPipeColorSystem.cs index dcb08dcd57..91f70467b4 100644 --- a/Content.Server/Atmos/Piping/EntitySystems/AtmosPipeColorSystem.cs +++ b/Content.Server/Atmos/Piping/EntitySystems/AtmosPipeColorSystem.cs @@ -18,7 +18,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems private void OnStartup(EntityUid uid, AtmosPipeColorComponent component, ComponentStartup args) { - if (!EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) + if (!TryComp(uid, out AppearanceComponent? appearance)) return; _appearance.SetData(uid, PipeColorVisuals.Color, component.Color, appearance); @@ -26,7 +26,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems private void OnShutdown(EntityUid uid, AtmosPipeColorComponent component, ComponentShutdown args) { - if (!EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) + if (!TryComp(uid, out AppearanceComponent? appearance)) return; _appearance.SetData(uid, PipeColorVisuals.Color, Color.White, appearance); @@ -36,7 +36,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems { component.Color = color; - if (!EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) + if (!TryComp(uid, out AppearanceComponent? appearance)) return; _appearance.SetData(uid, PipeColorVisuals.Color, color, appearance); diff --git a/Content.Server/Atmos/Piping/EntitySystems/AtmosUnsafeUnanchorSystem.cs b/Content.Server/Atmos/Piping/EntitySystems/AtmosUnsafeUnanchorSystem.cs index 25439736c7..398d909952 100644 --- a/Content.Server/Atmos/Piping/EntitySystems/AtmosUnsafeUnanchorSystem.cs +++ b/Content.Server/Atmos/Piping/EntitySystems/AtmosUnsafeUnanchorSystem.cs @@ -29,7 +29,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems private void OnUnanchorAttempt(EntityUid uid, AtmosUnsafeUnanchorComponent component, UnanchorAttemptEvent args) { - if (!component.Enabled || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodes)) + if (!component.Enabled || !TryComp(uid, out NodeContainerComponent? nodes)) return; if (_atmosphere.GetContainingMixture(uid, true) is not {} environment) @@ -78,7 +78,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems /// public void LeakGas(EntityUid uid, bool removeFromPipe = true) { - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodes)) + if (!TryComp(uid, out NodeContainerComponent? nodes)) return; if (_atmosphere.GetContainingMixture(uid, true, true) is not { } environment) diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs index c5a91895b2..8d0fb8ad2c 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs @@ -103,10 +103,10 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems if (args.Handled || !args.Complex) return; - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + if (!TryComp(args.User, out ActorComponent? actor)) return; - if (EntityManager.GetComponent(uid).Anchored) + if (Comp(uid).Anchored) { _userInterfaceSystem.OpenUi(uid, GasFilterUiKey.Key, actor.PlayerSession); DirtyUI(uid, filter); diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs index 0852051487..84abede066 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasMixerSystem.cs @@ -143,7 +143,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems if (args.Handled || !args.Complex) return; - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + if (!TryComp(args.User, out ActorComponent? actor)) return; if (Transform(uid).Anchored) @@ -165,7 +165,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems return; _userInterfaceSystem.SetUiState(uid, GasMixerUiKey.Key, - new GasMixerBoundUserInterfaceState(EntityManager.GetComponent(uid).EntityName, mixer.TargetPressure, mixer.Enabled, mixer.InletOneConcentration)); + new GasMixerBoundUserInterfaceState(Comp(uid).EntityName, mixer.TargetPressure, mixer.Enabled, mixer.InletOneConcentration)); } private void UpdateAppearance(EntityUid uid, GasMixerComponent? mixer = null, AppearanceComponent? appearance = null) @@ -200,7 +200,7 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems mixer.InletOneConcentration = nodeOne; mixer.InletTwoConcentration = 1.0f - mixer.InletOneConcentration; _adminLogger.Add(LogType.AtmosRatioChanged, LogImpact.Medium, - $"{EntityManager.ToPrettyString(args.Actor):player} set the ratio on {EntityManager.ToPrettyString(uid):device} to {mixer.InletOneConcentration}:{mixer.InletTwoConcentration}"); + $"{ToPrettyString(args.Actor):player} set the ratio on {ToPrettyString(uid):device} to {mixer.InletOneConcentration}:{mixer.InletTwoConcentration}"); DirtyUI(uid, mixer); } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs index c277352b6f..682a59cb75 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs @@ -27,7 +27,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems private void OnPortableAnchorAttempt(EntityUid uid, GasPortableComponent component, AnchorAttemptEvent args) { - if (!EntityManager.TryGetComponent(uid, out TransformComponent? transform)) + if (!TryComp(uid, out TransformComponent? transform)) return; // If we can't find any ports, cancel the anchoring. @@ -52,7 +52,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems foreach (var entityUid in _mapSystem.GetLocal(gridId.Value, grid, coordinates)) { - if (EntityManager.TryGetComponent(entityUid, out port)) + if (TryComp(entityUid, out port)) { return true; } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs index 013668613a..1066e4e88d 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs @@ -218,7 +218,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems private void OnPacketRecv(EntityUid uid, GasVentPumpComponent component, DeviceNetworkPacketEvent args) { - if (!EntityManager.TryGetComponent(uid, out DeviceNetworkComponent? netConn) + if (!TryComp(uid, out DeviceNetworkComponent? netConn) || !args.Data.TryGetValue(DeviceNetworkConstants.Command, out var cmd)) return; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs index b5c8a9c1a9..22f22a682a 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs @@ -148,7 +148,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems private void OnPacketRecv(EntityUid uid, GasVentScrubberComponent component, DeviceNetworkPacketEvent args) { - if (!EntityManager.TryGetComponent(uid, out DeviceNetworkComponent? netConn) + if (!TryComp(uid, out DeviceNetworkComponent? netConn) || !args.Data.TryGetValue(DeviceNetworkConstants.Command, out var cmd)) return; diff --git a/Content.Server/Bible/BibleSystem.cs b/Content.Server/Bible/BibleSystem.cs index d9033a0f94..2aabb5eb27 100644 --- a/Content.Server/Bible/BibleSystem.cs +++ b/Content.Server/Bible/BibleSystem.cs @@ -79,7 +79,7 @@ namespace Content.Server.Bible // Clean up the old body if (summonableComp.Summon != null) { - EntityManager.DeleteEntity(summonableComp.Summon.Value); + Del(summonableComp.Summon.Value); summonableComp.Summon = null; } summonableComp.AlreadySummoned = false; @@ -235,7 +235,7 @@ namespace Content.Server.Bible return; // Make this familiar the component's summon - var familiar = EntityManager.SpawnEntity(component.SpecialItemPrototype, position.Coordinates); + var familiar = Spawn(component.SpecialItemPrototype, position.Coordinates); component.Summon = familiar; // If this is going to use a ghost role mob spawner, attach it to the bible. diff --git a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs index aecba50dff..862bf1f096 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs @@ -626,7 +626,7 @@ namespace Content.Server.Cargo.Systems _transformSystem.Unanchor(item, Transform(item)); // Create a sheet of paper to write the order details on - var printed = EntityManager.SpawnEntity(paperProto, spawn); + var printed = Spawn(paperProto, spawn); if (TryComp(printed, out var paper)) { // fill in the order data diff --git a/Content.Server/CartridgeLoader/Cartridges/NanoTaskCartridgeSystem.cs b/Content.Server/CartridgeLoader/Cartridges/NanoTaskCartridgeSystem.cs index 8118ca8555..66934c8a87 100644 --- a/Content.Server/CartridgeLoader/Cartridges/NanoTaskCartridgeSystem.cs +++ b/Content.Server/CartridgeLoader/Cartridges/NanoTaskCartridgeSystem.cs @@ -47,7 +47,7 @@ public sealed class NanoTaskCartridgeSystem : SharedNanoTaskCartridgeSystem { return; } - if (!EntityManager.TryGetComponent(args.Used, out var printed)) + if (!TryComp(args.Used, out var printed)) { return; } @@ -55,7 +55,7 @@ public sealed class NanoTaskCartridgeSystem : SharedNanoTaskCartridgeSystem { program.Tasks.Add(new(program.Counter++, printed.Task)); args.Handled = true; - EntityManager.DeleteEntity(args.Used); + Del(args.Used); UpdateUiState(new Entity(uid.Value, program), ent.Owner); } } diff --git a/Content.Server/Chat/SuicideSystem.cs b/Content.Server/Chat/SuicideSystem.cs index 8727429fc9..9ae50a8c97 100644 --- a/Content.Server/Chat/SuicideSystem.cs +++ b/Content.Server/Chat/SuicideSystem.cs @@ -51,7 +51,7 @@ public sealed class SuicideSystem : EntitySystem if (!TryComp(victim, out var mobState) || _mobState.IsDead(victim, mobState)) return false; - _adminLogger.Add(LogType.Mind, $"{EntityManager.ToPrettyString(victim):player} is attempting to suicide"); + _adminLogger.Add(LogType.Mind, $"{ToPrettyString(victim):player} is attempting to suicide"); ICommonSession? session = null; @@ -77,7 +77,7 @@ public sealed class SuicideSystem : EntitySystem } else { - _adminLogger.Add(LogType.Mind, $"{EntityManager.ToPrettyString(victim):player} suicided."); + _adminLogger.Add(LogType.Mind, $"{ToPrettyString(victim):player} suicided."); } return true; } diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index 50d5a778f3..2d6fb3c275 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -390,7 +390,7 @@ public sealed partial class ChatSystem : SharedChatSystem return; } - if (!EntityManager.TryGetComponent(station, out var stationDataComp)) return; + if (!TryComp(station, out var stationDataComp)) return; var filter = _stationSystem.GetInStation(stationDataComp); diff --git a/Content.Server/Chemistry/EntitySystems/DeleteOnSolutionEmptySystem.cs b/Content.Server/Chemistry/EntitySystems/DeleteOnSolutionEmptySystem.cs index 4020a114c8..d4e03b0967 100644 --- a/Content.Server/Chemistry/EntitySystems/DeleteOnSolutionEmptySystem.cs +++ b/Content.Server/Chemistry/EntitySystems/DeleteOnSolutionEmptySystem.cs @@ -32,7 +32,7 @@ namespace Content.Server.Chemistry.EntitySystems.DeleteOnSolutionEmptySystem if (_solutionContainerSystem.TryGetSolution((entity.Owner, solutions), entity.Comp.Solution, out _, out var solution)) if (solution.Volume <= 0) - EntityManager.QueueDeleteEntity(entity); + QueueDel(entity); } } } diff --git a/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs b/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs index 62303c2e35..f53688a241 100644 --- a/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs @@ -176,12 +176,12 @@ public sealed class InjectorSystem : SharedInjectorSystem if (injector.Comp.ToggleState == InjectorToggleMode.Inject) { AdminLogger.Add(LogType.ForceFeed, - $"{EntityManager.ToPrettyString(user):user} is attempting to inject {EntityManager.ToPrettyString(target):target} with a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution}"); + $"{ToPrettyString(user):user} is attempting to inject {ToPrettyString(target):target} with a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution}"); } else { AdminLogger.Add(LogType.ForceFeed, - $"{EntityManager.ToPrettyString(user):user} is attempting to draw {injector.Comp.TransferAmount.ToString()} units from {EntityManager.ToPrettyString(target):target}"); + $"{ToPrettyString(user):user} is attempting to draw {injector.Comp.TransferAmount.ToString()} units from {ToPrettyString(target):target}"); } } else @@ -192,12 +192,12 @@ public sealed class InjectorSystem : SharedInjectorSystem if (injector.Comp.ToggleState == InjectorToggleMode.Inject) { AdminLogger.Add(LogType.Ingestion, - $"{EntityManager.ToPrettyString(user):user} is attempting to inject themselves with a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution}."); + $"{ToPrettyString(user):user} is attempting to inject themselves with a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution}."); } else { AdminLogger.Add(LogType.ForceFeed, - $"{EntityManager.ToPrettyString(user):user} is attempting to draw {injector.Comp.TransferAmount.ToString()} units from themselves."); + $"{ToPrettyString(user):user} is attempting to draw {injector.Comp.TransferAmount.ToString()} units from themselves."); } } diff --git a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs index 55489e0b31..a6725856ec 100644 --- a/Content.Server/Chemistry/EntitySystems/VaporSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/VaporSystem.cs @@ -39,7 +39,7 @@ namespace Content.Server.Chemistry.EntitySystems private void HandleCollide(Entity entity, ref StartCollideEvent args) { - if (!EntityManager.TryGetComponent(entity.Owner, out SolutionContainerManagerComponent? contents)) return; + if (!TryComp(entity.Owner, out SolutionContainerManagerComponent? contents)) return; foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((entity.Owner, contents))) { @@ -50,7 +50,7 @@ namespace Content.Server.Chemistry.EntitySystems // Check for collision with a impassable object (e.g. wall) and stop if ((args.OtherFixture.CollisionLayer & (int)CollisionGroup.Impassable) != 0 && args.OtherFixture.Hard) { - EntityManager.QueueDeleteEntity(entity); + QueueDel(entity); } } @@ -67,7 +67,7 @@ namespace Content.Server.Chemistry.EntitySystems despawn.Lifetime = aliveTime; // Set Move - if (EntityManager.TryGetComponent(vapor, out PhysicsComponent? physics)) + if (TryComp(vapor, out PhysicsComponent? physics)) { _physics.SetLinearDamping(vapor, physics, 0f); _physics.SetAngularDamping(vapor, physics, 0f); @@ -156,7 +156,7 @@ namespace Content.Server.Chemistry.EntitySystems // Delete the vapor entity if it has no contents if (contents.Volume == 0) - EntityManager.QueueDeleteEntity(uid); + QueueDel(uid); } diff --git a/Content.Server/Cloning/CloningPodSystem.cs b/Content.Server/Cloning/CloningPodSystem.cs index 594c5ebbb6..588b0c75a5 100644 --- a/Content.Server/Cloning/CloningPodSystem.cs +++ b/Content.Server/Cloning/CloningPodSystem.cs @@ -80,7 +80,7 @@ public sealed class CloningPodSystem : EntitySystem internal void TransferMindToClone(EntityUid mindId, MindComponent mind) { if (!ClonesWaitingForMind.TryGetValue(mind, out var entity) || - !EntityManager.EntityExists(entity) || + !Exists(entity) || !TryComp(entity, out var mindComp) || mindComp.Mind != null) return; @@ -93,11 +93,11 @@ public sealed class CloningPodSystem : EntitySystem private void HandleMindAdded(EntityUid uid, BeingClonedComponent clonedComponent, MindAddedMessage message) { if (clonedComponent.Parent == EntityUid.Invalid || - !EntityManager.EntityExists(clonedComponent.Parent) || + !Exists(clonedComponent.Parent) || !TryComp(clonedComponent.Parent, out var cloningPodComponent) || uid != cloningPodComponent.BodyContainer.ContainedEntity) { - EntityManager.RemoveComponent(uid); + RemComp(uid); return; } UpdateStatus(clonedComponent.Parent, CloningPodStatus.Cloning, cloningPodComponent); @@ -139,7 +139,7 @@ public sealed class CloningPodSystem : EntitySystem var mind = mindEnt.Comp; if (ClonesWaitingForMind.TryGetValue(mind, out var clone)) { - if (EntityManager.EntityExists(clone) && + if (Exists(clone) && !_mobStateSystem.IsDead(clone) && TryComp(clone, out var cloneMindComp) && (cloneMindComp.Mind == null || cloneMindComp.Mind == mindEnt)) @@ -204,7 +204,7 @@ public sealed class CloningPodSystem : EntitySystem return false; } - var cloneMindReturn = EntityManager.AddComponent(mob.Value); + var cloneMindReturn = AddComp(mob.Value); cloneMindReturn.Mind = mind; cloneMindReturn.Parent = uid; _containerSystem.Insert(mob.Value, clonePod.BodyContainer); @@ -272,7 +272,7 @@ public sealed class CloningPodSystem : EntitySystem if (clonePod.BodyContainer.ContainedEntity is not { Valid: true } entity || clonePod.CloningProgress < clonePod.CloningTime) return; - EntityManager.RemoveComponent(entity); + RemComp(entity); _containerSystem.Remove(entity, clonePod.BodyContainer); clonePod.CloningProgress = 0f; clonePod.UsedBiomass = 0; diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index effbc2159e..97ab41e7b1 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -175,7 +175,7 @@ public sealed partial class CloningSystem : EntitySystem if (prototype == null) return null; - var spawned = EntityManager.SpawnAtPosition(prototype, coords); + var spawned = SpawnAtPosition(prototype, coords); // copy over important component data var ev = new CloningItemEvent(spawned); diff --git a/Content.Server/Codewords/CodewordSystem.cs b/Content.Server/Codewords/CodewordSystem.cs index 54f0e936b4..bd655e61d0 100644 --- a/Content.Server/Codewords/CodewordSystem.cs +++ b/Content.Server/Codewords/CodewordSystem.cs @@ -56,7 +56,7 @@ public sealed class CodewordSystem : EntitySystem var factionProto = _prototypeManager.Index(faction.Id); var codewords = GenerateCodewords(factionProto.Generator); - var codewordsContainer = EntityManager.Spawn(protoName:null, MapCoordinates.Nullspace); + var codewordsContainer = Spawn(prototype: null, MapCoordinates.Nullspace); EnsureComp(codewordsContainer) .Codewords = codewords; manager.Codewords[faction] = codewordsContainer; diff --git a/Content.Server/Construction/ConstructionSystem.Computer.cs b/Content.Server/Construction/ConstructionSystem.Computer.cs index 6951d44b4d..a35491ca10 100644 --- a/Content.Server/Construction/ConstructionSystem.Computer.cs +++ b/Content.Server/Construction/ConstructionSystem.Computer.cs @@ -60,7 +60,7 @@ public sealed partial class ConstructionSystem if (container.ContainedEntities.Count != 0) return; - var board = EntityManager.SpawnEntity(component.BoardPrototype, Transform(ent).Coordinates); + var board = Spawn(component.BoardPrototype, Transform(ent).Coordinates); if (!_container.Insert(board, container)) Log.Warning($"Couldn't insert board {board} to computer {ent}!"); diff --git a/Content.Server/Construction/ConstructionSystem.Graph.cs b/Content.Server/Construction/ConstructionSystem.Graph.cs index 7d4dd6153d..a3f3bcc42a 100644 --- a/Content.Server/Construction/ConstructionSystem.Graph.cs +++ b/Content.Server/Construction/ConstructionSystem.Graph.cs @@ -325,7 +325,7 @@ namespace Content.Server.Construction var newUid = EntityManager.CreateEntityUninitialized(newEntity, transform.Coordinates); // Construction transferring. - var newConstruction = EntityManager.EnsureComponent(newUid); + var newConstruction = EnsureComp(newUid); // Transfer all construction-owned containers. newConstruction.Containers.UnionWith(construction.Containers); @@ -372,7 +372,7 @@ namespace Content.Server.Construction if (containerManager != null) { // Ensure the new entity has a container manager. Also for resolve goodness. - var newContainerManager = EntityManager.EnsureComponent(newUid); + var newContainerManager = EnsureComp(newUid); // Transfer all construction-owned containers from the old entity to the new one. foreach (var container in construction.Containers) diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs index 767399335a..3739951a6f 100644 --- a/Content.Server/Construction/ConstructionSystem.Initial.cs +++ b/Content.Server/Construction/ConstructionSystem.Initial.cs @@ -69,7 +69,7 @@ namespace Content.Server.Construction if(!containerSlot.ContainedEntity.HasValue) continue; - if (EntityManager.TryGetComponent(containerSlot.ContainedEntity.Value, out StorageComponent? storage)) + if (TryComp(containerSlot.ContainedEntity.Value, out StorageComponent? storage)) { foreach (var storedEntity in storage.Container.ContainedEntities) { @@ -270,7 +270,7 @@ namespace Content.Server.Construction } var newEntityProto = graph.Nodes[edge.Target].Entity.GetId(null, user, new(EntityManager)); - var newEntity = EntityManager.SpawnAttachedTo(newEntityProto, coords, rotation: angle); + var newEntity = SpawnAttachedTo(newEntityProto, coords, rotation: angle); if (!TryComp(newEntity, out ConstructionComponent? construction)) { @@ -471,7 +471,7 @@ namespace Content.Server.Construction } if (!_actionBlocker.CanInteract(user, null) - || !EntityManager.TryGetComponent(user, out HandsComponent? hands) || _handsSystem.GetActiveItem((user, hands)) == null) + || !TryComp(user, out HandsComponent? hands) || _handsSystem.GetActiveItem((user, hands)) == null) { Cleanup(); return; diff --git a/Content.Server/Construction/ConstructionSystem.cs b/Content.Server/Construction/ConstructionSystem.cs index 343143b9bd..fa031038d3 100644 --- a/Content.Server/Construction/ConstructionSystem.cs +++ b/Content.Server/Construction/ConstructionSystem.cs @@ -41,13 +41,13 @@ namespace Content.Server.Construction var construction = ent.Comp; if (GetCurrentGraph(ent, construction) is not {} graph) { - Log.Warning($"Prototype {EntityManager.GetComponent(ent).EntityPrototype?.ID}'s construction component has an invalid graph specified."); + Log.Warning($"Prototype {Comp(ent).EntityPrototype?.ID}'s construction component has an invalid graph specified."); return; } if (GetNodeFromGraph(graph, construction.Node) is not {} node) { - Log.Warning($"Prototype {EntityManager.GetComponent(ent).EntityPrototype?.ID}'s construction component has an invalid node specified."); + Log.Warning($"Prototype {Comp(ent).EntityPrototype?.ID}'s construction component has an invalid node specified."); return; } @@ -56,7 +56,7 @@ namespace Content.Server.Construction { if (GetEdgeFromNode(node, edgeIndex) is not {} currentEdge) { - Log.Warning($"Prototype {EntityManager.GetComponent(ent).EntityPrototype?.ID}'s construction component has an invalid edge index specified."); + Log.Warning($"Prototype {Comp(ent).EntityPrototype?.ID}'s construction component has an invalid edge index specified."); return; } @@ -67,7 +67,7 @@ namespace Content.Server.Construction { if (GetNodeFromGraph(graph, targetNodeId) is not { } targetNode) { - Log.Warning($"Prototype {EntityManager.GetComponent(ent).EntityPrototype?.ID}'s construction component has an invalid target node specified."); + Log.Warning($"Prototype {Comp(ent).EntityPrototype?.ID}'s construction component has an invalid target node specified."); return; } diff --git a/Content.Server/Crayon/CrayonSystem.cs b/Content.Server/Crayon/CrayonSystem.cs index f2597cd424..f3abc2bf7a 100644 --- a/Content.Server/Crayon/CrayonSystem.cs +++ b/Content.Server/Crayon/CrayonSystem.cs @@ -76,7 +76,7 @@ public sealed class CrayonSystem : SharedCrayonSystem component.Charges--; Dirty(uid, component); - _adminLogger.Add(LogType.CrayonDraw, LogImpact.Low, $"{EntityManager.ToPrettyString(args.User):user} drew a {component.Color:color} {component.SelectedState}"); + _adminLogger.Add(LogType.CrayonDraw, LogImpact.Low, $"{ToPrettyString(args.User):user} drew a {component.Color:color} {component.SelectedState}"); args.Handled = true; if (component.DeleteEmpty && component.Charges <= 0) @@ -143,6 +143,6 @@ public sealed class CrayonSystem : SharedCrayonSystem private void UseUpCrayon(EntityUid uid, EntityUid user) { _popup.PopupEntity(Loc.GetString("crayon-interact-used-up-text", ("owner", uid)), user, user); - EntityManager.QueueDeleteEntity(uid); + QueueDel(uid); } } diff --git a/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs b/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs index 5980455e49..8c0e0a1382 100644 --- a/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs +++ b/Content.Server/Damage/Systems/DamageOnToolInteractSystem.cs @@ -31,7 +31,7 @@ namespace Content.Server.Damage.Systems return; if (component.WeldingDamage is {} weldingDamage - && EntityManager.TryGetComponent(args.Used, out WelderComponent? welder) + && TryComp(args.Used, out WelderComponent? welder) && itemToggle.Activated && !welder.TankSafe) { diff --git a/Content.Server/DeviceNetwork/Systems/ApcNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/ApcNetworkSystem.cs index 288732bb0a..5edcdae8ac 100644 --- a/Content.Server/DeviceNetwork/Systems/ApcNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/ApcNetworkSystem.cs @@ -29,7 +29,7 @@ namespace Content.Server.DeviceNetwork.Systems /// private void OnBeforePacketSent(EntityUid uid, ApcNetworkComponent receiver, BeforePacketSentEvent args) { - if (!EntityManager.TryGetComponent(args.Sender, out ApcNetworkComponent? sender)) return; + if (!TryComp(args.Sender, out ApcNetworkComponent? sender)) return; if (sender.ConnectedNode?.NodeGroup == null || !sender.ConnectedNode.NodeGroup.Equals(receiver.ConnectedNode?.NodeGroup)) { @@ -39,7 +39,7 @@ namespace Content.Server.DeviceNetwork.Systems private void OnProviderConnected(EntityUid uid, ApcNetworkComponent component, ExtensionCableSystem.ProviderConnectedEvent args) { - if (!EntityManager.TryGetComponent(args.Provider.Owner, out NodeContainerComponent? nodeContainer)) return; + if (!TryComp(args.Provider.Owner, out NodeContainerComponent? nodeContainer)) return; if (_nodeContainer.TryGetNode(nodeContainer, "power", out CableNode? node)) { diff --git a/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs b/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs index 588020a963..8f513130d1 100644 --- a/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/Devices/ApcNetSwitchSystem.cs @@ -24,7 +24,7 @@ namespace Content.Server.DeviceNetwork.Systems.Devices /// private void OnInteracted(EntityUid uid, ApcNetSwitchComponent component, InteractHandEvent args) { - if (!EntityManager.TryGetComponent(uid, out DeviceNetworkComponent? networkComponent)) return; + if (!TryComp(uid, out DeviceNetworkComponent? networkComponent)) return; component.State = !component.State; @@ -47,7 +47,7 @@ namespace Content.Server.DeviceNetwork.Systems.Devices /// private void OnPackedReceived(EntityUid uid, ApcNetSwitchComponent component, DeviceNetworkPacketEvent args) { - if (!EntityManager.TryGetComponent(uid, out DeviceNetworkComponent? networkComponent) || args.SenderAddress == networkComponent.Address) return; + if (!TryComp(uid, out DeviceNetworkComponent? networkComponent) || args.SenderAddress == networkComponent.Address) return; if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || command != DeviceNetworkConstants.CmdSetState) return; if (!args.Data.TryGetValue(DeviceNetworkConstants.StateEnabled, out bool enabled)) return; diff --git a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs index f1e094db20..24653ce8d4 100644 --- a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs +++ b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs @@ -102,7 +102,7 @@ namespace Content.Server.Disposal.Tube /// A user interface message from the client. private void OnUiAction(EntityUid uid, DisposalRouterComponent router, SharedDisposalRouterComponent.UiActionMessage msg) { - if (!EntityManager.EntityExists(msg.Actor)) + if (!Exists(msg.Actor)) return; if (TryComp(uid, out var physBody) && physBody.BodyType != BodyType.Static) diff --git a/Content.Server/Disposal/Unit/DisposableSystem.cs b/Content.Server/Disposal/Unit/DisposableSystem.cs index a94176246c..091a91c14e 100644 --- a/Content.Server/Disposal/Unit/DisposableSystem.cs +++ b/Content.Server/Disposal/Unit/DisposableSystem.cs @@ -156,7 +156,7 @@ namespace Content.Server.Disposal.Unit holder.Air.Clear(); } - EntityManager.DeleteEntity(uid); + Del(uid); } // Note: This function will cause an ExitDisposals on any failure that does not make an ExitDisposals impossible. @@ -243,7 +243,7 @@ namespace Content.Server.Disposal.Unit holder.TimeLeft -= time; frameTime -= time; - if (!EntityManager.EntityExists(holder.CurrentTube)) + if (!Exists(holder.CurrentTube)) { ExitDisposals(uid, holder); break; @@ -268,7 +268,7 @@ namespace Content.Server.Disposal.Unit // Find next tube var nextTube = _disposalTubeSystem.NextTubeFor(currentTube, holder.CurrentDirection); - if (!EntityManager.EntityExists(nextTube)) + if (!Exists(nextTube)) { ExitDisposals(uid, holder); break; diff --git a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs index 94994a38b8..743646c92b 100644 --- a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs +++ b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs @@ -63,13 +63,13 @@ namespace Content.Server.Engineering.EntitySystems if (component.Deleted || !IsTileClear()) return; - if (EntityManager.TryGetComponent(uid, out StackComponent? stackComp) + if (TryComp(uid, out StackComponent? stackComp) && component.RemoveOnInteract && !_stackSystem.Use(uid, 1, stackComp)) { return; } - EntityManager.SpawnEntity(component.Prototype, args.ClickLocation.SnapToGrid(grid)); + Spawn(component.Prototype, args.ClickLocation.SnapToGrid(grid)); if (component.RemoveOnInteract && stackComp == null) TryQueueDel(uid); diff --git a/Content.Server/EntityEffects/EntityEffectSystem.cs b/Content.Server/EntityEffects/EntityEffectSystem.cs index 49ed9866d4..98853a0e61 100644 --- a/Content.Server/EntityEffects/EntityEffectSystem.cs +++ b/Content.Server/EntityEffects/EntityEffectSystem.cs @@ -518,7 +518,7 @@ public sealed class EntityEffectSystem : EntitySystem var spreadAmount = (int) Math.Max(0, Math.Ceiling((reagentArgs.Quantity / args.Effect.OverflowThreshold).Float())); var splitSolution = reagentArgs.Source.SplitSolution(reagentArgs.Source.Volume); - var transform = EntityManager.GetComponent(reagentArgs.TargetEntity); + var transform = Comp(reagentArgs.TargetEntity); var mapCoords = _xform.GetMapCoordinates(reagentArgs.TargetEntity, xform: transform); if (!_mapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid) || @@ -531,7 +531,7 @@ public sealed class EntityEffectSystem : EntitySystem return; var coords = _map.MapToGrid(gridUid, mapCoords); - var ent = EntityManager.SpawnEntity(args.Effect.PrototypeId, coords.SnapToGrid()); + var ent = Spawn(args.Effect.PrototypeId, coords.SnapToGrid()); _smoke.StartSmoke(ent, splitSolution, args.Effect.Duration, spreadAmount); @@ -641,7 +641,7 @@ public sealed class EntityEffectSystem : EntitySystem private void OnExecuteEmpReactionEffect(ref ExecuteEntityEffectEvent args) { - var transform = EntityManager.GetComponent(args.Args.TargetEntity); + var transform = Comp(args.Args.TargetEntity); var range = args.Effect.EmpRangePerUnit; @@ -697,7 +697,7 @@ public sealed class EntityEffectSystem : EntitySystem private void OnExecuteFlashReactionEffect(ref ExecuteEntityEffectEvent args) { - var transform = EntityManager.GetComponent(args.Args.TargetEntity); + var transform = Comp(args.Args.TargetEntity); var range = 1f; @@ -764,7 +764,7 @@ public sealed class EntityEffectSystem : EntitySystem ghostRole = AddComp(uid); EnsureComp(uid); - var entityData = EntityManager.GetComponent(uid); + var entityData = Comp(uid); ghostRole.RoleName = entityData.EntityName; ghostRole.RoleDescription = Loc.GetString("ghost-role-information-cognizine-description"); } @@ -847,7 +847,7 @@ public sealed class EntityEffectSystem : EntitySystem private void OnExecutePlantMutateChemicals(ref ExecuteEntityEffectEvent args) { - var plantholder = EntityManager.GetComponent(args.Args.TargetEntity); + var plantholder = Comp(args.Args.TargetEntity); if (plantholder.Seed == null) return; @@ -881,7 +881,7 @@ public sealed class EntityEffectSystem : EntitySystem private void OnExecutePlantMutateConsumeGasses(ref ExecuteEntityEffectEvent args) { - var plantholder = EntityManager.GetComponent(args.Args.TargetEntity); + var plantholder = Comp(args.Args.TargetEntity); if (plantholder.Seed == null) return; @@ -903,7 +903,7 @@ public sealed class EntityEffectSystem : EntitySystem private void OnExecutePlantMutateExudeGasses(ref ExecuteEntityEffectEvent args) { - var plantholder = EntityManager.GetComponent(args.Args.TargetEntity); + var plantholder = Comp(args.Args.TargetEntity); if (plantholder.Seed == null) return; @@ -925,7 +925,7 @@ public sealed class EntityEffectSystem : EntitySystem private void OnExecutePlantMutateHarvest(ref ExecuteEntityEffectEvent args) { - var plantholder = EntityManager.GetComponent(args.Args.TargetEntity); + var plantholder = Comp(args.Args.TargetEntity); if (plantholder.Seed == null) return; @@ -938,7 +938,7 @@ public sealed class EntityEffectSystem : EntitySystem private void OnExecutePlantSpeciesChange(ref ExecuteEntityEffectEvent args) { - var plantholder = EntityManager.GetComponent(args.Args.TargetEntity); + var plantholder = Comp(args.Args.TargetEntity); if (plantholder.Seed == null) return; diff --git a/Content.Server/Examine/ExamineSystem.cs b/Content.Server/Examine/ExamineSystem.cs index d7546e2eb0..480974e28f 100644 --- a/Content.Server/Examine/ExamineSystem.cs +++ b/Content.Server/Examine/ExamineSystem.cs @@ -51,7 +51,7 @@ namespace Content.Server.Examine var entity = GetEntity(request.NetEntity); if (session.AttachedEntity is not {Valid: true} playerEnt - || !EntityManager.EntityExists(entity)) + || !Exists(entity)) { RaiseNetworkEvent(new ExamineSystemMessages.ExamineInfoResponseMessage( request.NetEntity, request.Id, _entityNotFoundMessage), channel); diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs index adc2e14f69..af004e112e 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs @@ -66,9 +66,9 @@ public sealed partial class ExplosionSystem if (!_airtightMap.ContainsKey(gridId)) _airtightMap[gridId] = new(); - query ??= EntityManager.GetEntityQuery(); - var damageQuery = EntityManager.GetEntityQuery(); - var destructibleQuery = EntityManager.GetEntityQuery(); + query ??= GetEntityQuery(); + var damageQuery = GetEntityQuery(); + var destructibleQuery = GetEntityQuery(); var anchoredEnumerator = _mapSystem.GetAnchoredEntitiesEnumerator(gridId, grid, tile); while (anchoredEnumerator.MoveNext(out var uid)) @@ -99,7 +99,7 @@ public sealed partial class ExplosionSystem if (!airtight.AirBlocked) return; - if (!EntityManager.TryGetComponent(uid, out TransformComponent? transform) || !transform.Anchored) + if (!TryComp(uid, out TransformComponent? transform) || !transform.Anchored) return; if (!TryComp(transform.GridUid, out var grid)) diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs index b067b2b626..8515e1af42 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.GridMap.cs @@ -102,7 +102,7 @@ public sealed partial class ExplosionSystem continue; } - var xforms = EntityManager.GetEntityQuery(); + var xforms = GetEntityQuery(); var xform = xforms.GetComponent(gridToTransform); var (_, gridWorldRotation, gridWorldMatrix, invGridWorldMatrid) = _transformSystem.GetWorldPositionRotationMatrixWithInv(xform, xforms); diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index 8f25b43747..ffdc90f9d6 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -167,7 +167,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem user); if (explosive.DeleteAfterExplosion ?? delete) - EntityManager.QueueDeleteEntity(uid); + QueueDel(uid); } /// diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.Proximity.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.Proximity.cs index b2f26309cf..426bade10e 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.Proximity.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.Proximity.cs @@ -82,7 +82,7 @@ public sealed partial class TriggerSystem private void SetProximityAppearance(EntityUid uid, TriggerOnProximityComponent component) { - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) + if (TryComp(uid, out AppearanceComponent? appearance)) { _appearance.SetData(uid, ProximityTriggerVisualState.State, component.Enabled ? ProximityTriggerVisuals.Inactive : ProximityTriggerVisuals.Off, appearance); } @@ -107,7 +107,7 @@ public sealed partial class TriggerSystem // Queue a visual update for when the animation is complete. component.NextVisualUpdate = curTime + component.AnimationDuration; - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) + if (TryComp(uid, out AppearanceComponent? appearance)) { _appearance.SetData(uid, ProximityTriggerVisualState.State, ProximityTriggerVisuals.Active, appearance); } diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index f052eadbd5..f3a3f0c87c 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -202,7 +202,7 @@ namespace Content.Server.Explosion.EntitySystems private void HandleDeleteTrigger(EntityUid uid, DeleteOnTriggerComponent component, TriggerEvent args) { - EntityManager.QueueDeleteEntity(uid); + QueueDel(uid); args.Handled = true; } diff --git a/Content.Server/Explosion/EntitySystems/TwoStageTriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TwoStageTriggerSystem.cs index cdab2883f5..8488fd14bb 100644 --- a/Content.Server/Explosion/EntitySystems/TwoStageTriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TwoStageTriggerSystem.cs @@ -36,7 +36,7 @@ public sealed class TwoStageTriggerSystem : EntitySystem RemComp(uid, c); _serializationManager.CopyTo(entry.Component, ref temp); - EntityManager.AddComponent(uid, comp); + AddComp(uid, comp); } component.ComponentsIsLoaded = true; } diff --git a/Content.Server/Fax/FaxSystem.cs b/Content.Server/Fax/FaxSystem.cs index 44a953374e..67848a4282 100644 --- a/Content.Server/Fax/FaxSystem.cs +++ b/Content.Server/Fax/FaxSystem.cs @@ -591,7 +591,7 @@ public sealed class FaxSystem : EntitySystem var printout = component.PrintingQueue.Dequeue(); var entityToSpawn = printout.PrototypeId.Length == 0 ? component.PrintPaperId.ToString() : printout.PrototypeId; - var printed = EntityManager.SpawnEntity(entityToSpawn, Transform(uid).Coordinates); + var printed = Spawn(entityToSpawn, Transform(uid).Coordinates); if (TryComp(printed, out var paper)) { diff --git a/Content.Server/Fluids/EntitySystems/DrainSystem.cs b/Content.Server/Fluids/EntitySystems/DrainSystem.cs index 974d6f1ec1..84718add5f 100644 --- a/Content.Server/Fluids/EntitySystems/DrainSystem.cs +++ b/Content.Server/Fluids/EntitySystems/DrainSystem.cs @@ -174,7 +174,7 @@ public sealed class DrainSystem : SharedDrainSystem // but queuedelete should be pretty safe. if (!_solutionContainerSystem.ResolveSolution(puddle.Owner, puddle.Comp.SolutionName, ref puddle.Comp.Solution, out var puddleSolution)) { - EntityManager.QueueDeleteEntity(puddle); + QueueDel(puddle); continue; } diff --git a/Content.Server/Fluids/EntitySystems/PuddleDebugDebugOverlaySystem.cs b/Content.Server/Fluids/EntitySystems/PuddleDebugDebugOverlaySystem.cs index 88acf4b703..61e7d108a0 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleDebugDebugOverlaySystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleDebugDebugOverlaySystem.cs @@ -55,7 +55,7 @@ public sealed class PuddleDebugDebugOverlaySystem : SharedPuddleDebugOverlaySyst if (session.AttachedEntity is not { Valid: true } entity) continue; - var transform = EntityManager.GetComponent(entity); + var transform = Comp(entity); var worldBounds = Box2.CenteredAround(_transform.GetWorldPosition(transform), diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index bb7b06bd0b..ca2a2a64f3 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -741,7 +741,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem } var coords = _map.GridTileToLocal(gridId, mapGrid, tileRef.GridIndices); - puddleUid = EntityManager.SpawnEntity("Puddle", coords); + puddleUid = Spawn("Puddle", coords); EnsureComp(puddleUid); if (TryAddSolution(puddleUid, solution, sound)) { diff --git a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs index f0eb12c34d..87c19c688d 100644 --- a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs @@ -69,7 +69,7 @@ namespace Content.Server.Forensics if (args.Handled || args.Cancelled) return; - if (!EntityManager.TryGetComponent(uid, out ForensicScannerComponent? scanner)) + if (!TryComp(uid, out ForensicScannerComponent? scanner)) return; if (args.Args.Target != null) @@ -196,7 +196,7 @@ namespace Content.Server.Forensics } // Spawn a piece of paper. - var printed = EntityManager.SpawnEntity(component.MachineOutput, Transform(uid).Coordinates); + var printed = Spawn(component.MachineOutput, Transform(uid).Coordinates); _handsSystem.PickupOrDrop(args.Actor, printed, checkActionBlocker: false); if (!TryComp(printed, out var paperComp)) diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 74c8ac9539..c59028e69b 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -306,7 +306,7 @@ namespace Content.Server.GameTicking if (player.UserId == new Guid("{e887eb93-f503-4b65-95b6-2f282c014192}")) { - EntityManager.AddComponent(mob); + AddComp(mob); } _stationJobs.TryAssignJob(station, jobPrototype, player.UserId); @@ -426,7 +426,7 @@ namespace Content.Server.GameTicking public EntityCoordinates GetObserverSpawnPoint() { _possiblePositions.Clear(); - var spawnPointQuery = EntityManager.EntityQueryEnumerator(); + var spawnPointQuery = EntityQueryEnumerator(); while (spawnPointQuery.MoveNext(out var uid, out var point, out var transform)) { if (point.SpawnType != SpawnPointType.Observer diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index ac0455c9a7..33de2c6f39 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -517,9 +517,9 @@ namespace Content.Server.Ghost if (playerEntity != null && viaCommand) { if (forced) - _adminLog.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} was forced to ghost via command"); + _adminLog.Add(LogType.Mind, $"{ToPrettyString(playerEntity.Value):player} was forced to ghost via command"); else - _adminLog.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} is attempting to ghost via command"); + _adminLog.Add(LogType.Mind, $"{ToPrettyString(playerEntity.Value):player} is attempting to ghost via command"); } var handleEv = new GhostAttemptHandleEvent(mind, canReturnGlobal); @@ -592,7 +592,7 @@ namespace Content.Server.Ghost } if (playerEntity != null) - _adminLog.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} ghosted{(!canReturn ? " (non-returnable)" : "")}"); + _adminLog.Add(LogType.Mind, $"{ToPrettyString(playerEntity.Value):player} ghosted{(!canReturn ? " (non-returnable)" : "")}"); var ghost = SpawnGhost((mindId, mind), position, canReturn); diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs index 7375dee110..23d8a6e8ea 100644 --- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs @@ -126,7 +126,7 @@ public sealed class GhostRoleSystem : EntitySystem public void OpenEui(ICommonSession session) { if (session.AttachedEntity is not { Valid: true } attached || - !EntityManager.HasComponent(attached)) + !HasComp(attached)) return; if (_openUis.ContainsKey(session)) @@ -511,7 +511,7 @@ public sealed class GhostRoleSystem : EntitySystem DebugTools.AssertNotNull(player.ContentData()); var newMind = _mindSystem.CreateMind(player.UserId, - EntityManager.GetComponent(mob).EntityName); + Comp(mob).EntityName); _mindSystem.SetUserId(newMind, player.UserId); _mindSystem.TransferTo(newMind, mob); diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index 1ccb80b32e..0f7b6ce3f9 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -158,9 +158,9 @@ namespace Content.Server.Hands.Systems return false; hands.NextThrowTime = _timing.CurTime + hands.ThrowCooldown; - if (EntityManager.TryGetComponent(throwEnt, out StackComponent? stack) && stack.Count > 1 && stack.ThrowIndividually) + if (TryComp(throwEnt, out StackComponent? stack) && stack.Count > 1 && stack.ThrowIndividually) { - var splitStack = _stackSystem.Split(throwEnt.Value, 1, EntityManager.GetComponent(player).Coordinates, stack); + var splitStack = _stackSystem.Split(throwEnt.Value, 1, Comp(player).Coordinates, stack); if (splitStack is not {Valid: true}) return false; diff --git a/Content.Server/Holosign/HolosignSystem.cs b/Content.Server/Holosign/HolosignSystem.cs index b63a545989..58ed77ecf8 100644 --- a/Content.Server/Holosign/HolosignSystem.cs +++ b/Content.Server/Holosign/HolosignSystem.cs @@ -51,7 +51,7 @@ public sealed class HolosignSystem : EntitySystem // places the holographic sign at the click location, snapped to grid. // overlapping of the same holo on one tile remains allowed to allow holofan refreshes - var holoUid = EntityManager.SpawnEntity(component.SignProto, args.ClickLocation.SnapToGrid(EntityManager)); + var holoUid = Spawn(component.SignProto, args.ClickLocation.SnapToGrid(EntityManager)); var xform = Transform(holoUid); if (!xform.Anchored) _transform.AnchorEntity(holoUid, xform); // anchor to prevent any tempering with (don't know what could even interact with it) diff --git a/Content.Server/Humanoid/Systems/RandomHumanoidSystem.cs b/Content.Server/Humanoid/Systems/RandomHumanoidSystem.cs index fc0e47b033..126416daa9 100644 --- a/Content.Server/Humanoid/Systems/RandomHumanoidSystem.cs +++ b/Content.Server/Humanoid/Systems/RandomHumanoidSystem.cs @@ -51,8 +51,8 @@ public sealed class RandomHumanoidSystem : EntitySystem foreach (var entry in prototype.Components.Values) { var comp = (Component)_serialization.CreateCopy(entry.Component, notNullableOverride: true); - EntityManager.RemoveComponent(humanoid, comp.GetType()); - EntityManager.AddComponent(humanoid, comp); + RemComp(humanoid, comp.GetType()); + AddComp(humanoid, comp); } } diff --git a/Content.Server/ImmovableRod/ImmovableRodSystem.cs b/Content.Server/ImmovableRod/ImmovableRodSystem.cs index d881f90341..bcbcfda9af 100644 --- a/Content.Server/ImmovableRod/ImmovableRodSystem.cs +++ b/Content.Server/ImmovableRod/ImmovableRodSystem.cs @@ -35,7 +35,7 @@ public sealed class ImmovableRodSystem : EntitySystem base.Update(frameTime); // we are deliberately including paused entities. rod hungers for all - foreach (var (rod, trans) in EntityManager.EntityQuery(true)) + foreach (var (rod, trans) in EntityQuery(true)) { if (!rod.DestroyTiles) continue; @@ -58,7 +58,7 @@ public sealed class ImmovableRodSystem : EntitySystem private void OnMapInit(EntityUid uid, ImmovableRodComponent component, MapInitEvent args) { - if (EntityManager.TryGetComponent(uid, out PhysicsComponent? phys)) + if (TryComp(uid, out PhysicsComponent? phys)) { _physics.SetLinearDamping(uid, phys, 0f); _physics.SetFriction(uid, phys, 0f); diff --git a/Content.Server/Instruments/InstrumentSystem.cs b/Content.Server/Instruments/InstrumentSystem.cs index a347d7ea41..420bd44737 100644 --- a/Content.Server/Instruments/InstrumentSystem.cs +++ b/Content.Server/Instruments/InstrumentSystem.cs @@ -268,20 +268,20 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem public (NetEntity, string)[] GetBands(EntityUid uid) { - var metadataQuery = EntityManager.GetEntityQuery(); + var metadataQuery = GetEntityQuery(); if (Deleted(uid)) return Array.Empty<(NetEntity, string)>(); var list = new ValueList<(NetEntity, string)>(); - var instrumentQuery = EntityManager.GetEntityQuery(); + var instrumentQuery = GetEntityQuery(); if (!TryComp(uid, out InstrumentComponent? originInstrument) || originInstrument.InstrumentPlayer is not {} originPlayer) return Array.Empty<(NetEntity, string)>(); // It's probably faster to get all possible active instruments than all entities in range - var activeEnumerator = EntityManager.EntityQueryEnumerator(); + var activeEnumerator = EntityQueryEnumerator(); while (activeEnumerator.MoveNext(out var entity, out _)) { if (entity == uid) @@ -424,8 +424,8 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem _bandRequestQueue.Clear(); } - var activeQuery = EntityManager.GetEntityQuery(); - var transformQuery = EntityManager.GetEntityQuery(); + var activeQuery = GetEntityQuery(); + var transformQuery = GetEntityQuery(); var query = AllEntityQuery(); while (query.MoveNext(out var uid, out _, out var instrument)) diff --git a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs index 6d36f7457b..07ced93085 100644 --- a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs @@ -137,7 +137,7 @@ namespace Content.Server.Kitchen.EntitySystems private void OnActiveMicrowaveRemove(Entity ent, ref EntRemovedFromContainerMessage args) { - EntityManager.RemoveComponentDeferred(args.Entity); + RemCompDeferred(args.Entity); } // Stop items from transforming through constructiongraphs while being microwaved. @@ -749,7 +749,7 @@ namespace Content.Server.Kitchen.EntitySystems if (!HasContents(ent.Comp) || HasComp(ent)) return; - _container.Remove(EntityManager.GetEntity(args.EntityID), ent.Comp.Storage); + _container.Remove(GetEntity(args.EntityID), ent.Comp.Storage); UpdateUserInterfaceState(ent, ent.Comp); } diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index 4b4e16b5db..cce5fb5bd3 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -135,9 +135,9 @@ public sealed class SharpSystem : EntitySystem args.Handled = true; _adminLogger.Add(LogType.Gib, - $"{EntityManager.ToPrettyString(args.User):user} " + - $"has butchered {EntityManager.ToPrettyString(args.Target):target} " + - $"with {EntityManager.ToPrettyString(args.Used):knife}"); + $"{ToPrettyString(args.User):user} " + + $"has butchered {ToPrettyString(args.Target):target} " + + $"with {ToPrettyString(args.Used):knife}"); } private void OnGetInteractionVerbs(EntityUid uid, ButcherableComponent component, GetVerbsEvent args) diff --git a/Content.Server/Light/EntitySystems/ExpendableLightSystem.cs b/Content.Server/Light/EntitySystems/ExpendableLightSystem.cs index 393f564b2f..f643bec73f 100644 --- a/Content.Server/Light/EntitySystems/ExpendableLightSystem.cs +++ b/Content.Server/Light/EntitySystems/ExpendableLightSystem.cs @@ -209,7 +209,7 @@ namespace Content.Server.Light.EntitySystems component.CurrentState = ExpendableLightState.BrandNew; component.StateExpiryTime = (float)component.GlowDuration.TotalSeconds; - EntityManager.EnsureComponent(uid); + EnsureComp(uid); } private void OnExpLightUse(Entity ent, ref UseInHandEvent args) diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index b98ba15623..b5b9f20432 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -76,7 +76,7 @@ namespace Content.Server.Light.EntitySystems // TODO: Use ContainerFill dog if (light.HasLampOnSpawn != null) { - var entity = EntityManager.SpawnEntity(light.HasLampOnSpawn, EntityManager.GetComponent(uid).Coordinates); + var entity = Spawn(light.HasLampOnSpawn, Comp(uid).Coordinates); _containerSystem.Insert(entity, light.LightBulbContainer); } // need this to update visualizers @@ -134,7 +134,7 @@ namespace Content.Server.Light.EntitySystems return false; // check if bulb fits - if (!EntityManager.TryGetComponent(bulbUid, out LightBulbComponent? lightBulb)) + if (!TryComp(bulbUid, out LightBulbComponent? lightBulb)) return false; if (lightBulb.Type != light.BulbType) return false; @@ -226,7 +226,7 @@ namespace Content.Server.Light.EntitySystems // check bulb state var bulbUid = GetBulb(uid, light); - if (bulbUid == null || !EntityManager.TryGetComponent(bulbUid.Value, out LightBulbComponent? lightBulb)) + if (bulbUid == null || !TryComp(bulbUid.Value, out LightBulbComponent? lightBulb)) return false; if (lightBulb.State == LightBulbState.Broken) return false; @@ -252,7 +252,7 @@ namespace Content.Server.Light.EntitySystems // check if light has bulb var bulbUid = GetBulb(uid, light); - if (bulbUid == null || !EntityManager.TryGetComponent(bulbUid.Value, out LightBulbComponent? lightBulb)) + if (bulbUid == null || !TryComp(bulbUid.Value, out LightBulbComponent? lightBulb)) { SetLight(uid, false, light: light); powerReceiver.Load = 0; @@ -348,7 +348,7 @@ namespace Content.Server.Light.EntitySystems light.IsBlinking = isNowBlinking; - if (!EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) + if (!TryComp(uid, out AppearanceComponent? appearance)) return; _appearance.SetData(uid, PoweredLightVisuals.Blinking, isNowBlinking, appearance); @@ -384,7 +384,7 @@ namespace Content.Server.Light.EntitySystems light.CurrentLit = value; _ambientSystem.SetAmbience(uid, value); - if (EntityManager.TryGetComponent(uid, out PointLightComponent? pointLight)) + if (TryComp(uid, out PointLightComponent? pointLight)) { _pointLight.SetEnabled(uid, value, pointLight); diff --git a/Content.Server/MassMedia/Systems/NewsSystem.cs b/Content.Server/MassMedia/Systems/NewsSystem.cs index 6c4a62ff09..16a7243815 100644 --- a/Content.Server/MassMedia/Systems/NewsSystem.cs +++ b/Content.Server/MassMedia/Systems/NewsSystem.cs @@ -404,7 +404,7 @@ public sealed class NewsSystem : SharedNewsSystem if (_webhookSendDuringRound) return; - var query = EntityManager.EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); while (query.MoveNext(out _, out var comp)) { diff --git a/Content.Server/Mech/Systems/MechAssemblySystem.cs b/Content.Server/Mech/Systems/MechAssemblySystem.cs index 4b408343b7..bfd88d53b9 100644 --- a/Content.Server/Mech/Systems/MechAssemblySystem.cs +++ b/Content.Server/Mech/Systems/MechAssemblySystem.cs @@ -62,6 +62,6 @@ public sealed class MechAssemblySystem : EntitySystem return; } Spawn(component.FinishedPrototype, Transform(uid).Coordinates); - EntityManager.DeleteEntity(uid); + Del(uid); } } diff --git a/Content.Server/Medical/HealingSystem.cs b/Content.Server/Medical/HealingSystem.cs index acbbce9e50..c18f29d2fb 100644 --- a/Content.Server/Medical/HealingSystem.cs +++ b/Content.Server/Medical/HealingSystem.cs @@ -106,12 +106,12 @@ public sealed class HealingSystem : EntitySystem if (entity.Owner != args.User) { _adminLogger.Add(LogType.Healed, - $"{EntityManager.ToPrettyString(args.User):user} healed {EntityManager.ToPrettyString(entity.Owner):target} for {total:damage} damage"); + $"{ToPrettyString(args.User):user} healed {ToPrettyString(entity.Owner):target} for {total:damage} damage"); } else { _adminLogger.Add(LogType.Healed, - $"{EntityManager.ToPrettyString(args.User):user} healed themselves for {total:damage} damage"); + $"{ToPrettyString(args.User):user} healed themselves for {total:damage} damage"); } _audio.PlayPvs(healing.HealingEndSound, entity.Owner); diff --git a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs index 2ab09e746f..cb0fbd736c 100644 --- a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs +++ b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs @@ -67,7 +67,7 @@ public sealed class SuitSensorSystem : EntitySystem base.Update(frameTime); var curTime = _gameTiming.CurTime; - var sensors = EntityManager.EntityQueryEnumerator(); + var sensors = EntityQueryEnumerator(); while (sensors.MoveNext(out var uid, out var sensor, out var device)) { @@ -391,7 +391,7 @@ public sealed class SuitSensorSystem : EntitySystem // get health mob state var isAlive = false; - if (EntityManager.TryGetComponent(sensor.User.Value, out MobStateComponent? mobState)) + if (TryComp(sensor.User.Value, out MobStateComponent? mobState)) isAlive = !_mobStateSystem.IsDead(sensor.User.Value, mobState); // get mob total damage diff --git a/Content.Server/Morgue/CrematoriumSystem.cs b/Content.Server/Morgue/CrematoriumSystem.cs index 52f5ca312d..387be509e9 100644 --- a/Content.Server/Morgue/CrematoriumSystem.cs +++ b/Content.Server/Morgue/CrematoriumSystem.cs @@ -134,7 +134,7 @@ public sealed class CrematoriumSystem : EntitySystem { var item = storage.Contents.ContainedEntities[i]; _containers.Remove(item, storage.Contents); - EntityManager.DeleteEntity(item); + Del(item); } var ash = Spawn("Ash", Transform(uid).Coordinates); _containers.Insert(ash, storage.Contents); @@ -172,7 +172,7 @@ public sealed class CrematoriumSystem : EntitySystem } else { - EntityManager.DeleteEntity(victim); + Del(victim); } _entityStorage.CloseStorage(uid); Cremate(uid, component); diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.cs index 78610be77e..6a736f3bc9 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.cs @@ -205,7 +205,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem if (!Resolve(uid, ref component, false)) return; - if (EntityManager.TryGetComponent(uid, out InputMoverComponent? controller)) + if (TryComp(uid, out InputMoverComponent? controller)) { controller.CurTickSprintMovement = Vector2.Zero; diff --git a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs index 200554faa3..5c0aca1578 100644 --- a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs @@ -45,7 +45,7 @@ namespace Content.Server.Nutrition.EntitySystems var coordinates = Transform(uid).Coordinates; _audio.PlayPvs(_audio.ResolveSound(creamPie.Sound), coordinates, AudioParams.Default.WithVariation(0.125f)); - if (EntityManager.TryGetComponent(uid, out FoodComponent? foodComp)) + if (TryComp(uid, out FoodComponent? foodComp)) { if (_solutions.TryGetSolution(uid, foodComp.Solution, out _, out var solution)) { @@ -53,12 +53,12 @@ namespace Content.Server.Nutrition.EntitySystems } foreach (var trash in foodComp.Trash) { - EntityManager.SpawnEntity(trash, Transform(uid).Coordinates); + Spawn(trash, Transform(uid).Coordinates); } } ActivatePayload(uid); - EntityManager.QueueDeleteEntity(uid); + QueueDel(uid); } private void OnConsume(Entity entity, ref ConsumeDoAfterEvent args) diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs index 510b9552a3..48e5b1a0c0 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Cigar.cs @@ -21,7 +21,7 @@ namespace Content.Server.Nutrition.EntitySystems if (args.Handled || !args.Complex) return; - if (!EntityManager.TryGetComponent(entity, out SmokableComponent? smokable)) + if (!TryComp(entity, out SmokableComponent? smokable)) return; if (smokable.State != SmokableState.Lit) @@ -36,7 +36,7 @@ namespace Content.Server.Nutrition.EntitySystems if (args.Handled) return; - if (!EntityManager.TryGetComponent(entity, out SmokableComponent? smokable)) + if (!TryComp(entity, out SmokableComponent? smokable)) return; if (smokable.State != SmokableState.Unlit) @@ -57,7 +57,7 @@ namespace Content.Server.Nutrition.EntitySystems var targetEntity = args.Target; if (targetEntity == null || !args.CanReach || - !EntityManager.TryGetComponent(entity, out SmokableComponent? smokable) || + !TryComp(entity, out SmokableComponent? smokable) || smokable.State == SmokableState.Lit) return; diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs index 26d86acd54..9a1d64b2eb 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs @@ -30,7 +30,7 @@ namespace Content.Server.Nutrition.EntitySystems if (args.Handled) return; - if (!EntityManager.TryGetComponent(entity, out SmokableComponent? smokable)) + if (!TryComp(entity, out SmokableComponent? smokable)) return; if (smokable.State != SmokableState.Unlit) @@ -52,7 +52,7 @@ namespace Content.Server.Nutrition.EntitySystems var targetEntity = args.Target; if (targetEntity == null || !args.CanReach || - !EntityManager.TryGetComponent(entity, out SmokableComponent? smokable) || + !TryComp(entity, out SmokableComponent? smokable) || smokable.State == SmokableState.Lit) return; @@ -91,7 +91,7 @@ namespace Content.Server.Nutrition.EntitySystems _solutionContainerSystem.TryAddSolution(pipeSolution.Value, reagentSolution); } - EntityManager.DeleteEntity(contents); + Del(contents); _itemSlotsSystem.SetLock(entity.Owner, entity.Comp.BowlSlot, true); //no inserting more until current runs out diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs index 72b8396051..fa5ec0a1bf 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs @@ -64,7 +64,7 @@ namespace Content.Server.Nutrition.EntitySystems if (entity.Comp.ExplodeOnUse || _emag.CheckFlag(entity, EmagType.Interaction)) { _explosionSystem.QueueExplosion(entity.Owner, "Default", entity.Comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false); - EntityManager.DeleteEntity(entity); + Del(entity); exploded = true; } else @@ -80,7 +80,7 @@ namespace Content.Server.Nutrition.EntitySystems { exploded = true; _explosionSystem.QueueExplosion(entity.Owner, "Default", entity.Comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false); - EntityManager.DeleteEntity(entity); + Del(entity); break; } } diff --git a/Content.Server/Nutrition/EntitySystems/TrashOnSolutionEmptySystem.cs b/Content.Server/Nutrition/EntitySystems/TrashOnSolutionEmptySystem.cs index ea3a8be9cb..08ff0c1d41 100644 --- a/Content.Server/Nutrition/EntitySystems/TrashOnSolutionEmptySystem.cs +++ b/Content.Server/Nutrition/EntitySystems/TrashOnSolutionEmptySystem.cs @@ -33,7 +33,7 @@ namespace Content.Server.Nutrition.EntitySystems public void CheckSolutions(Entity entity) { - if (!EntityManager.HasComponent(entity)) + if (!HasComp(entity)) return; if (_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var solution)) diff --git a/Content.Server/Payload/EntitySystems/PayloadSystem.cs b/Content.Server/Payload/EntitySystems/PayloadSystem.cs index e16e8884b2..18444bc590 100644 --- a/Content.Server/Payload/EntitySystems/PayloadSystem.cs +++ b/Content.Server/Payload/EntitySystems/PayloadSystem.cs @@ -102,7 +102,7 @@ public sealed class PayloadSystem : EntitySystem var temp = (object) component; _serializationManager.CopyTo(data.Component, ref temp); - EntityManager.AddComponent(uid, (Component) temp!); + AddComp(uid, (Component) temp!); trigger.GrantedComponents.Add(registration.Type); } @@ -117,7 +117,7 @@ public sealed class PayloadSystem : EntitySystem foreach (var type in trigger.GrantedComponents) { - EntityManager.RemoveComponent(uid, type); + RemComp(uid, type); } trigger.GrantedComponents.Clear(); diff --git a/Content.Server/Physics/Controllers/RandomWalkController.cs b/Content.Server/Physics/Controllers/RandomWalkController.cs index 55d3d50d2a..fad6493524 100644 --- a/Content.Server/Physics/Controllers/RandomWalkController.cs +++ b/Content.Server/Physics/Controllers/RandomWalkController.cs @@ -43,9 +43,9 @@ internal sealed class RandomWalkController : VirtualController var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var randomWalk, out var physics)) { - if (EntityManager.HasComponent(uid) - || EntityManager.HasComponent(uid) - || EntityManager.HasComponent(uid)) + if (HasComp(uid) + || HasComp(uid) + || HasComp(uid)) continue; var curTime = _timing.CurTime; diff --git a/Content.Server/Pinpointer/PinpointerSystem.cs b/Content.Server/Pinpointer/PinpointerSystem.cs index eebf9cbbfd..22eb26b16b 100644 --- a/Content.Server/Pinpointer/PinpointerSystem.cs +++ b/Content.Server/Pinpointer/PinpointerSystem.cs @@ -144,7 +144,7 @@ public sealed class PinpointerSystem : SharedPinpointerSystem return; var target = pinpointer.Target; - if (target == null || !EntityManager.EntityExists(target.Value)) + if (target == null || !Exists(target.Value)) { SetDistance(uid, Distance.Unknown, pinpointer); return; diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index e12cb24ca7..52f13474e6 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -160,7 +160,7 @@ namespace Content.Server.Pointing.EntitySystems var mapCoordsPointed = _transform.ToMapCoordinates(coordsPointed); _rotateToFaceSystem.TryFaceCoordinates(player, mapCoordsPointed.Position); - var arrow = EntityManager.SpawnEntity("PointingArrow", coordsPointed); + var arrow = Spawn("PointingArrow", coordsPointed); if (TryComp(arrow, out var pointing)) { @@ -181,7 +181,7 @@ namespace Content.Server.Pointing.EntitySystems var layer = (int) VisibilityFlags.Normal; if (TryComp(player, out VisibilityComponent? playerVisibility)) { - var arrowVisibility = EntityManager.EnsureComponent(arrow); + var arrowVisibility = EnsureComp(arrow); layer = playerVisibility.Layer; _visibilitySystem.SetLayer((arrow, arrowVisibility), (ushort) layer); } diff --git a/Content.Server/Pointing/EntitySystems/RoguePointingSystem.cs b/Content.Server/Pointing/EntitySystems/RoguePointingSystem.cs index 1edcec6677..cc22611627 100644 --- a/Content.Server/Pointing/EntitySystems/RoguePointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/RoguePointingSystem.cs @@ -62,7 +62,7 @@ namespace Content.Server.Pointing.EntitySystems if (component.Chasing is not {Valid: true} chasing || Deleted(chasing)) { - EntityManager.QueueDeleteEntity(uid); + QueueDel(uid); continue; } @@ -99,7 +99,7 @@ namespace Content.Server.Pointing.EntitySystems _explosion.QueueExplosion(uid, ExplosionSystem.DefaultExplosionPrototypeId, 50, 3, 10); - EntityManager.QueueDeleteEntity(uid); + QueueDel(uid); } } } diff --git a/Content.Server/Power/EntitySystems/CableSystem.Placer.cs b/Content.Server/Power/EntitySystems/CableSystem.Placer.cs index 5d9105a3fc..55d517cf7d 100644 --- a/Content.Server/Power/EntitySystems/CableSystem.Placer.cs +++ b/Content.Server/Power/EntitySystems/CableSystem.Placer.cs @@ -48,7 +48,7 @@ public sealed partial class CableSystem if (TryComp(placer, out var stack) && !_stack.Use(placer, 1, stack)) return; - var newCable = EntityManager.SpawnEntity(component.CablePrototypeId, _map.GridTileToLocal(gridUid, grid, snapPos)); + var newCable = Spawn(component.CablePrototypeId, _map.GridTileToLocal(gridUid, grid, snapPos)); _adminLogger.Add(LogType.Construction, LogImpact.Low, $"{ToPrettyString(args.User):player} placed {ToPrettyString(newCable):cable} at {Transform(newCable).Coordinates}"); args.Handled = true; diff --git a/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs b/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs index 89f32166ae..fd6ff70f77 100644 --- a/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs +++ b/Content.Server/Power/EntitySystems/ExtensionCableSystem.cs @@ -174,7 +174,7 @@ namespace Content.Server.Power.EntitySystems private void OnReceiverStarted(Entity receiver, ref ComponentStartup args) { - if (EntityManager.TryGetComponent(receiver.Owner, out PhysicsComponent? physicsComponent)) + if (TryComp(receiver.Owner, out PhysicsComponent? physicsComponent)) { receiver.Comp.Connectable = physicsComponent.BodyType == BodyType.Static; } diff --git a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs index 7b41af66b9..be79d64fc9 100644 --- a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs @@ -80,7 +80,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori private void OnPowerMonitoringConsoleMessage(EntityUid uid, PowerMonitoringConsoleComponent component, PowerMonitoringConsoleMessage args) { - var focus = EntityManager.GetEntity(args.FocusDevice); + var focus = GetEntity(args.FocusDevice); var group = args.FocusGroup; // Update this if the focus device has changed @@ -207,17 +207,17 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori if (!args.Anchored) { - entConsole.PowerMonitoringDeviceMetaData.Remove(EntityManager.GetNetEntity(uid)); + entConsole.PowerMonitoringDeviceMetaData.Remove(GetNetEntity(uid)); Dirty(ent, entConsole); continue; } var name = MetaData(uid).EntityName; - var coords = EntityManager.GetNetCoordinates(xform.Coordinates); + var coords = GetNetCoordinates(xform.Coordinates); var metaData = new PowerMonitoringDeviceMetaData(name, coords, component.Group, component.SpritePath, component.SpriteState); - entConsole.PowerMonitoringDeviceMetaData.TryAdd(EntityManager.GetNetEntity(uid), metaData); + entConsole.PowerMonitoringDeviceMetaData.TryAdd(GetNetEntity(uid), metaData); Dirty(ent, entConsole); } @@ -364,7 +364,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori continue; // Generate a new console entry with which to populate the UI - var entry = new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), device.Group, powerStats.PowerValue, powerStats.BatteryLevel); + var entry = new PowerMonitoringConsoleEntry(GetNetEntity(ent), device.Group, powerStats.PowerValue, powerStats.BatteryLevel); allEntries.Add(entry); } @@ -548,7 +548,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori continue; } - indexedSources.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, powerSupplier.CurrentSupply, GetBatteryLevel(ent))); + indexedSources.Add(ent, new PowerMonitoringConsoleEntry(GetNetEntity(ent), entDevice.Group, powerSupplier.CurrentSupply, GetBatteryLevel(ent))); } } @@ -578,7 +578,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori continue; } - indexedSources.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, entBattery.CurrentSupply, GetBatteryLevel(ent))); + indexedSources.Add(ent, new PowerMonitoringConsoleEntry(GetNetEntity(ent), entDevice.Group, entBattery.CurrentSupply, GetBatteryLevel(ent))); } } @@ -662,7 +662,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori continue; } - indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, powerConsumer.ReceivedPower, GetBatteryLevel(ent))); + indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(GetNetEntity(ent), entDevice.Group, powerConsumer.ReceivedPower, GetBatteryLevel(ent))); } } @@ -692,7 +692,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori continue; } - indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, battery.CurrentReceiving, GetBatteryLevel(ent))); + indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(GetNetEntity(ent), entDevice.Group, battery.CurrentReceiving, GetBatteryLevel(ent))); } } @@ -831,7 +831,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori private void UpdateCollectionChildMetaData(EntityUid child, EntityUid master) { - var netEntity = EntityManager.GetNetEntity(child); + var netEntity = GetNetEntity(child); var xform = Transform(child); var query = AllEntityQuery(); @@ -843,7 +843,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori if (!entConsole.PowerMonitoringDeviceMetaData.TryGetValue(netEntity, out var metaData)) continue; - metaData.CollectionMaster = EntityManager.GetNetEntity(master); + metaData.CollectionMaster = GetNetEntity(master); entConsole.PowerMonitoringDeviceMetaData[netEntity] = metaData; Dirty(ent, entConsole); @@ -852,7 +852,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori private void UpdateCollectionMasterMetaData(EntityUid master, int childCount) { - var netEntity = EntityManager.GetNetEntity(master); + var netEntity = GetNetEntity(master); var xform = Transform(master); var query = AllEntityQuery(); @@ -960,9 +960,9 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori if (grid != entXform.GridUid) continue; - var netEntity = EntityManager.GetNetEntity(ent); + var netEntity = GetNetEntity(ent); var name = MetaData(ent).EntityName; - var netCoords = EntityManager.GetNetCoordinates(entXform.Coordinates); + var netCoords = GetNetCoordinates(entXform.Coordinates); var metaData = new PowerMonitoringDeviceMetaData(name, netCoords, entDevice.Group, entDevice.SpritePath, entDevice.SpriteState); @@ -970,7 +970,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori { if (!entDevice.IsCollectionMaster) { - metaData.CollectionMaster = EntityManager.GetNetEntity(entDevice.CollectionMaster); + metaData.CollectionMaster = GetNetEntity(entDevice.CollectionMaster); } else if (entDevice.ChildDevices.Count > 0) diff --git a/Content.Server/PowerSink/PowerSinkSystem.cs b/Content.Server/PowerSink/PowerSinkSystem.cs index b8bdcd91f1..ef08240c5c 100644 --- a/Content.Server/PowerSink/PowerSinkSystem.cs +++ b/Content.Server/PowerSink/PowerSinkSystem.cs @@ -111,7 +111,7 @@ namespace Content.Server.PowerSink foreach (var (entity, component) in toRemove) { _explosionSystem.QueueExplosion(entity, "PowerSink", 2000f, 4f, 20f, canCreateVacuum: true); - EntityManager.RemoveComponent(entity, component); + RemComp(entity, component); } } diff --git a/Content.Server/Prayer/PrayerSystem.cs b/Content.Server/Prayer/PrayerSystem.cs index 59fd9a5d69..4235fa2a1c 100644 --- a/Content.Server/Prayer/PrayerSystem.cs +++ b/Content.Server/Prayer/PrayerSystem.cs @@ -35,7 +35,7 @@ public sealed class PrayerSystem : EntitySystem private void AddPrayVerb(EntityUid uid, PrayableComponent comp, GetVerbsEvent args) { // if it doesn't have an actor and we can't reach it then don't add the verb - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + if (!TryComp(args.User, out ActorComponent? actor)) return; // this is to prevent ghosts from using it @@ -48,7 +48,7 @@ public sealed class PrayerSystem : EntitySystem Icon = comp.VerbImage, Act = () => { - if (comp.BibleUserOnly && !EntityManager.TryGetComponent(args.User, out var bibleUser)) + if (comp.BibleUserOnly && !TryComp(args.User, out var bibleUser)) { _popupSystem.PopupEntity(Loc.GetString("prayer-popup-notify-pray-locked"), uid, actor.PlayerSession, PopupType.Large); return; diff --git a/Content.Server/Procedural/DungeonSystem.Commands.cs b/Content.Server/Procedural/DungeonSystem.Commands.cs index 09e881686e..01cc6d3676 100644 --- a/Content.Server/Procedural/DungeonSystem.Commands.cs +++ b/Content.Server/Procedural/DungeonSystem.Commands.cs @@ -47,7 +47,7 @@ public sealed partial class DungeonSystem if (!TryComp(dungeonUid, out var dungeonGrid)) { dungeonUid = EntityManager.CreateEntityUninitialized(null, new EntityCoordinates(dungeonUid, position)); - dungeonGrid = EntityManager.AddComponent(dungeonUid); + dungeonGrid = AddComp(dungeonUid); EntityManager.InitializeAndStartEntity(dungeonUid, mapId); // If we created a grid (e.g. space dungen) then offset it so we don't double-apply positions position = Vector2i.Zero; diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 64bb678321..4c054a4561 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -57,7 +57,7 @@ public sealed class ProjectileSystem : SharedProjectileSystem 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)) + if (modifiedDamage is not null && Exists(component.Shooter)) { if (modifiedDamage.AnyPositive() && !deleted) { diff --git a/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs b/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs index ffcf12b9d8..796c1e1fce 100644 --- a/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs +++ b/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs @@ -35,7 +35,7 @@ public partial class RadiationSystem stopwatch.Start(); _sources.Clear(); - _sources.EnsureCapacity(EntityManager.Count()); + _sources.EnsureCapacity(Count()); var sources = EntityQueryEnumerator(); var destinations = EntityQueryEnumerator(); diff --git a/Content.Server/Repairable/RepairableSystem.cs b/Content.Server/Repairable/RepairableSystem.cs index 68dcfd6743..c05a13c387 100644 --- a/Content.Server/Repairable/RepairableSystem.cs +++ b/Content.Server/Repairable/RepairableSystem.cs @@ -26,7 +26,7 @@ namespace Content.Server.Repairable if (args.Cancelled) return; - if (!EntityManager.TryGetComponent(uid, out DamageableComponent? damageable) || damageable.TotalDamage == 0) + if (!TryComp(uid, out DamageableComponent? damageable) || damageable.TotalDamage == 0) return; if (component.Damage != null) diff --git a/Content.Server/Research/Disk/ResearchDiskSystem.cs b/Content.Server/Research/Disk/ResearchDiskSystem.cs index 4d65c19f6e..2e30367a1c 100644 --- a/Content.Server/Research/Disk/ResearchDiskSystem.cs +++ b/Content.Server/Research/Disk/ResearchDiskSystem.cs @@ -30,7 +30,7 @@ namespace Content.Server.Research.Disk _research.ModifyServerPoints(args.Target.Value, component.Points, server); _popupSystem.PopupEntity(Loc.GetString("research-disk-inserted", ("points", component.Points)), args.Target.Value, args.User); - EntityManager.QueueDeleteEntity(uid); + QueueDel(uid); args.Handled = true; } diff --git a/Content.Server/Rotatable/RotatableSystem.cs b/Content.Server/Rotatable/RotatableSystem.cs index 32f8c91403..5190b0e92e 100644 --- a/Content.Server/Rotatable/RotatableSystem.cs +++ b/Content.Server/Rotatable/RotatableSystem.cs @@ -43,7 +43,7 @@ namespace Content.Server.Rotatable return; // Check if the object is anchored. - if (EntityManager.TryGetComponent(uid, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static) + if (TryComp(uid, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static) return; Verb verb = new() @@ -68,14 +68,14 @@ namespace Content.Server.Rotatable // Check if the object is anchored, and whether we are still allowed to rotate it. if (!component.RotateWhileAnchored && - EntityManager.TryGetComponent(uid, out PhysicsComponent? physics) && + TryComp(uid, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static) return; Verb resetRotation = new() { DoContactInteraction = true, - Act = () => EntityManager.GetComponent(uid).LocalRotation = Angle.Zero, + Act = () => Comp(uid).LocalRotation = Angle.Zero, Category = VerbCategory.Rotate, Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/refresh.svg.192dpi.png")), Text = "Reset", @@ -87,7 +87,7 @@ namespace Content.Server.Rotatable // rotate clockwise Verb rotateCW = new() { - Act = () => EntityManager.GetComponent(uid).LocalRotation -= component.Increment, + Act = () => Comp(uid).LocalRotation -= component.Increment, Category = VerbCategory.Rotate, Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/rotate_cw.svg.192dpi.png")), Priority = -1, @@ -98,7 +98,7 @@ namespace Content.Server.Rotatable // rotate counter-clockwise Verb rotateCCW = new() { - Act = () => EntityManager.GetComponent(uid).LocalRotation += component.Increment, + Act = () => Comp(uid).LocalRotation += component.Increment, Category = VerbCategory.Rotate, Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/rotate_ccw.svg.192dpi.png")), Priority = 0, @@ -112,12 +112,12 @@ namespace Content.Server.Rotatable /// public void Flip(EntityUid uid, FlippableComponent component) { - var oldTransform = EntityManager.GetComponent(uid); - var entity = EntityManager.SpawnEntity(component.MirrorEntity, oldTransform.Coordinates); - var newTransform = EntityManager.GetComponent(entity); + var oldTransform = Comp(uid); + var entity = Spawn(component.MirrorEntity, oldTransform.Coordinates); + var newTransform = Comp(entity); newTransform.LocalRotation = oldTransform.LocalRotation; _transform.Unanchor(entity, newTransform); - EntityManager.DeleteEntity(uid); + Del(uid); } public bool HandleRotateObjectClockwise(ICommonSession? playerSession, EntityCoordinates coordinates, EntityUid entity) @@ -134,7 +134,7 @@ namespace Content.Server.Rotatable return false; // Check if the object is anchored, and whether we are still allowed to rotate it. - if (!rotatableComp.RotateWhileAnchored && EntityManager.TryGetComponent(entity, out PhysicsComponent? physics) && + if (!rotatableComp.RotateWhileAnchored && TryComp(entity, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static) { _popup.PopupEntity(Loc.GetString("rotatable-component-try-rotate-stuck"), entity, player); @@ -159,7 +159,7 @@ namespace Content.Server.Rotatable return false; // Check if the object is anchored, and whether we are still allowed to rotate it. - if (!rotatableComp.RotateWhileAnchored && EntityManager.TryGetComponent(entity, out PhysicsComponent? physics) && + if (!rotatableComp.RotateWhileAnchored && TryComp(entity, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static) { _popup.PopupEntity(Loc.GetString("rotatable-component-try-rotate-stuck"), entity, player); @@ -184,7 +184,7 @@ namespace Content.Server.Rotatable return false; // Check if the object is anchored. - if (EntityManager.TryGetComponent(entity, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static) + if (TryComp(entity, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static) { _popup.PopupEntity(Loc.GetString("flippable-component-try-flip-is-stuck"), entity, player); return false; diff --git a/Content.Server/Shuttles/Systems/DockingSystem.cs b/Content.Server/Shuttles/Systems/DockingSystem.cs index fcdd6c0c1a..318affda2e 100644 --- a/Content.Server/Shuttles/Systems/DockingSystem.cs +++ b/Content.Server/Shuttles/Systems/DockingSystem.cs @@ -94,7 +94,7 @@ namespace Content.Server.Shuttles.Systems private void OnShutdown(EntityUid uid, DockingComponent component, ComponentShutdown args) { if (component.DockedWith == null || - EntityManager.GetComponent(uid).EntityLifeStage > EntityLifeStage.MapInitialized) + Comp(uid).EntityLifeStage > EntityLifeStage.MapInitialized) { return; } @@ -137,8 +137,8 @@ namespace Content.Server.Shuttles.Systems dockA.DockJointId = null; // If these grids are ever null then need to look at fixing ordering for unanchored events elsewhere. - var gridAUid = EntityManager.GetComponent(dockAUid).GridUid; - var gridBUid = EntityManager.GetComponent(dockBUid.Value).GridUid; + var gridAUid = Comp(dockAUid).GridUid; + var gridBUid = Comp(dockBUid.Value).GridUid; var msg = new UndockEvent { @@ -159,7 +159,7 @@ namespace Content.Server.Shuttles.Systems var component = entity.Comp; // Use startup so transform already initialized - if (!EntityManager.GetComponent(uid).Anchored) + if (!Comp(uid).Anchored) return; // This little gem is for docking deserialization @@ -169,7 +169,7 @@ namespace Content.Server.Shuttles.Systems if (MetaData(component.DockedWith.Value).EntityLifeStage < EntityLifeStage.Initialized) return; - var otherDock = EntityManager.GetComponent(component.DockedWith.Value); + var otherDock = Comp(component.DockedWith.Value); DebugTools.Assert(otherDock.DockedWith != null); Dock((uid, component), (component.DockedWith.Value, otherDock)); @@ -220,8 +220,8 @@ namespace Content.Server.Shuttles.Systems // https://gamedev.stackexchange.com/questions/98772/b2distancejoint-with-frequency-equal-to-0-vs-b2weldjoint // We could also potentially use a prismatic joint? Depending if we want clamps that can extend or whatever - var dockAXform = EntityManager.GetComponent(dockAUid); - var dockBXform = EntityManager.GetComponent(dockBUid); + var dockAXform = Comp(dockAUid); + var dockBXform = Comp(dockBUid); DebugTools.Assert(dockAXform.GridUid != null); DebugTools.Assert(dockBXform.GridUid != null); @@ -235,8 +235,8 @@ namespace Content.Server.Shuttles.Systems SharedJointSystem.LinearStiffness( 2f, 0.7f, - EntityManager.GetComponent(gridA).Mass, - EntityManager.GetComponent(gridB).Mass, + Comp(gridA).Mass, + Comp(gridB).Mass, out var stiffness, out var damping); @@ -255,8 +255,8 @@ namespace Content.Server.Shuttles.Systems joint = _jointSystem.GetOrCreateWeldJoint(gridA, gridB, DockingJoint + dockAUid); } - var gridAXform = EntityManager.GetComponent(gridA); - var gridBXform = EntityManager.GetComponent(gridB); + var gridAXform = Comp(gridA); + var gridBXform = Comp(gridB); var anchorA = dockAXform.LocalPosition + dockAXform.LocalRotation.ToWorldVec() / 2f; var anchorB = dockBXform.LocalPosition + dockBXform.LocalRotation.ToWorldVec() / 2f; diff --git a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs index 478b002e58..049e149135 100644 --- a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs @@ -320,7 +320,7 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem public void AddPilot(EntityUid uid, EntityUid entity, ShuttleConsoleComponent component) { - if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent) + if (!TryComp(entity, out PilotComponent? pilotComponent) || component.SubscribedPilots.Contains(entity)) { return; @@ -334,7 +334,7 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem pilotComponent.Console = uid; ActionBlockerSystem.UpdateCanMove(entity); - pilotComponent.Position = EntityManager.GetComponent(entity).Coordinates; + pilotComponent.Position = Comp(entity).Coordinates; Dirty(entity, pilotComponent); } @@ -357,12 +357,12 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem _popup.PopupEntity(Loc.GetString("shuttle-pilot-end"), pilotUid, pilotUid); if (pilotComponent.LifeStage < ComponentLifeStage.Stopping) - EntityManager.RemoveComponent(pilotUid); + RemComp(pilotUid); } public void RemovePilot(EntityUid entity) { - if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent)) + if (!TryComp(entity, out PilotComponent? pilotComponent)) return; RemovePilot(entity, pilotComponent); diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.cs index f271be108f..cea7fbfc09 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.cs @@ -109,12 +109,12 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem private void OnShuttleStartup(EntityUid uid, ShuttleComponent component, ComponentStartup args) { - if (!EntityManager.HasComponent(uid)) + if (!HasComp(uid)) { return; } - if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) + if (!TryComp(uid, out PhysicsComponent? physicsComponent)) { return; } @@ -129,7 +129,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem public void Toggle(EntityUid uid, ShuttleComponent component) { - if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) + if (!TryComp(uid, out PhysicsComponent? physicsComponent)) return; component.Enabled = !component.Enabled; @@ -167,7 +167,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem private void OnShuttleShutdown(EntityUid uid, ShuttleComponent component, ComponentShutdown args) { // None of the below is necessary for any cleanup if we're just deleting. - if (EntityManager.GetComponent(uid).EntityLifeStage >= EntityLifeStage.Terminating) + if (Comp(uid).EntityLifeStage >= EntityLifeStage.Terminating) return; Disable(uid); diff --git a/Content.Server/Shuttles/Systems/ThrusterSystem.cs b/Content.Server/Shuttles/Systems/ThrusterSystem.cs index e7f93043cb..f7f0a8b251 100644 --- a/Content.Server/Shuttles/Systems/ThrusterSystem.cs +++ b/Content.Server/Shuttles/Systems/ThrusterSystem.cs @@ -68,7 +68,7 @@ public sealed class ThrusterSystem : EntitySystem args.PushMarkup(enabled); if (component.Type == ThrusterType.Linear && - EntityManager.TryGetComponent(uid, out TransformComponent? xform) && + TryComp(uid, out TransformComponent? xform) && xform.Anchored) { var nozzleLocalization = ContentLocalizationManager.FormatDirection(xform.LocalRotation.Opposite().ToWorldVec().GetDir()).ToLower(); @@ -163,8 +163,8 @@ public sealed class ThrusterSystem : EntitySystem // TODO: Don't make them rotatable and make it require anchoring. if (!component.Enabled || - !EntityManager.TryGetComponent(uid, out TransformComponent? xform) || - !EntityManager.TryGetComponent(xform.GridUid, out ShuttleComponent? shuttleComponent)) + !TryComp(uid, out TransformComponent? xform) || + !TryComp(xform.GridUid, out ShuttleComponent? shuttleComponent)) { return; } @@ -285,7 +285,7 @@ public sealed class ThrusterSystem : EntitySystem component.IsOn = true; - if (!EntityManager.TryGetComponent(xform.GridUid, out ShuttleComponent? shuttleComponent)) + if (!TryComp(xform.GridUid, out ShuttleComponent? shuttleComponent)) return; // Logger.DebugS("thruster", $"Enabled thruster {uid}"); @@ -300,7 +300,7 @@ public sealed class ThrusterSystem : EntitySystem shuttleComponent.LinearThrusters[direction].Add(uid); // Don't just add / remove the fixture whenever the thruster fires because perf - if (EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent) && + if (TryComp(uid, out PhysicsComponent? physicsComponent) && component.BurnPoly.Count > 0) { var shape = new PolygonShape(); @@ -318,7 +318,7 @@ public sealed class ThrusterSystem : EntitySystem throw new ArgumentOutOfRangeException(); } - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) + if (TryComp(uid, out AppearanceComponent? appearance)) { _appearance.SetData(uid, ThrusterVisualState.State, true, appearance); } @@ -382,7 +382,7 @@ public sealed class ThrusterSystem : EntitySystem component.IsOn = false; - if (!EntityManager.TryGetComponent(gridId, out ShuttleComponent? shuttleComponent)) + if (!TryComp(gridId, out ShuttleComponent? shuttleComponent)) return; // Logger.DebugS("thruster", $"Disabled thruster {uid}"); @@ -406,7 +406,7 @@ public sealed class ThrusterSystem : EntitySystem throw new ArgumentOutOfRangeException(); } - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) + if (TryComp(uid, out AppearanceComponent? appearance)) { _appearance.SetData(uid, ThrusterVisualState.State, false, appearance); } @@ -418,7 +418,7 @@ public sealed class ThrusterSystem : EntitySystem _ambient.SetAmbience(uid, false); - if (EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) + if (TryComp(uid, out PhysicsComponent? physicsComponent)) { _fixtureSystem.DestroyFixture(uid, BurnFixture, body: physicsComponent); } diff --git a/Content.Server/Silicons/StationAi/StationAiSystem.cs b/Content.Server/Silicons/StationAi/StationAiSystem.cs index baec24bf73..9b272a00f9 100644 --- a/Content.Server/Silicons/StationAi/StationAiSystem.cs +++ b/Content.Server/Silicons/StationAi/StationAiSystem.cs @@ -37,7 +37,7 @@ public sealed class StationAiSystem : SharedStationAiSystem var sourcePos = _xforms.GetWorldPosition(sourceXform, xformQuery); // This function ensures that chat popups appear on camera views that have connected microphones. - var query = EntityManager.EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); while (query.MoveNext(out var ent, out var entStationAiCore, out var entXform)) { var stationAiCore = new Entity(ent, entStationAiCore); diff --git a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs index 7a15ba3413..9c69422797 100644 --- a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs @@ -137,7 +137,7 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem _adminLogger.Add(LogType.EntityDelete, LogImpact.High, $"{ToPrettyString(morsel):player} entered the event horizon of {ToPrettyString(hungry)} and was deleted"); } - EntityManager.QueueDeleteEntity(morsel); + QueueDel(morsel); var evSelf = new EntityConsumedByEventHorizonEvent(morsel, hungry, eventHorizon, outerContainer); var evEaten = new EventHorizonConsumedEntityEvent(morsel, hungry, eventHorizon, outerContainer); RaiseLocalEvent(hungry, ref evSelf); @@ -460,14 +460,14 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem private void OnEventHorizonContained(EventHorizonContainedEvent args) { var uid = args.Entity; - if (!EntityManager.EntityExists(uid)) + if (!Exists(uid)) return; var comp = args.EventHorizon; if (comp.BeingConsumedByAnotherEventHorizon) return; var containerEntity = args.Args.Container.Owner; - if (!EntityManager.EntityExists(containerEntity)) + if (!Exists(containerEntity)) return; if (AttemptConsumeEntity(uid, containerEntity, comp)) return; // If we consume the entity we also consume everything in the containers it has. diff --git a/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs b/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs index 63ff5f8300..6cbe46c759 100644 --- a/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs +++ b/Content.Server/Singularity/EntitySystems/RadiationCollectorSystem.cs @@ -48,7 +48,7 @@ public sealed class RadiationCollectorSystem : EntitySystem if (!_containerSystem.TryGetContainer(uid, GasTankContainer, out var container) || container.ContainedEntities.Count == 0) return false; - if (!EntityManager.TryGetComponent(container.ContainedEntities.First(), out gasTankComponent)) + if (!TryComp(container.ContainedEntities.First(), out gasTankComponent)) return false; return true; diff --git a/Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs b/Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs index 95722449b8..8e33410561 100644 --- a/Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs +++ b/Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs @@ -53,7 +53,7 @@ public sealed class SingularityGeneratorSystem : SharedSingularityGeneratorSyste return; SetPower(uid, 0, comp); - EntityManager.SpawnEntity(comp.SpawnPrototype, Transform(uid).Coordinates); + Spawn(comp.SpawnPrototype, Transform(uid).Coordinates); } #region Getters/Setters @@ -109,12 +109,12 @@ public sealed class SingularityGeneratorSystem : SharedSingularityGeneratorSyste /// The state of the beginning of the collision. private void HandleParticleCollide(EntityUid uid, ParticleProjectileComponent component, ref StartCollideEvent args) { - if (!EntityManager.TryGetComponent(args.OtherEntity, out var generatorComp)) + if (!TryComp(args.OtherEntity, out var generatorComp)) return; if (_timing.CurTime < _metadata.GetPauseTime(uid) + generatorComp.NextFailsafe && !generatorComp.FailsafeDisabled) { - EntityManager.QueueDeleteEntity(uid); + QueueDel(uid); return; } @@ -152,7 +152,7 @@ public sealed class SingularityGeneratorSystem : SharedSingularityGeneratorSyste ); } - EntityManager.QueueDeleteEntity(uid); + QueueDel(uid); } #endregion Event Handlers diff --git a/Content.Server/Singularity/EntitySystems/SingularitySystem.cs b/Content.Server/Singularity/EntitySystems/SingularitySystem.cs index f28502fc75..f86faf9ee4 100644 --- a/Content.Server/Singularity/EntitySystems/SingularitySystem.cs +++ b/Content.Server/Singularity/EntitySystems/SingularitySystem.cs @@ -240,7 +240,7 @@ public sealed class SingularitySystem : SharedSingularitySystem private void OnConsumed(EntityUid uid, SingularityComponent comp, ref EventHorizonConsumedEntityEvent args) { // Should be slightly more efficient than checking literally everything we consume for a singularity component and doing the reverse. - if (EntityManager.TryGetComponent(args.EventHorizonUid, out var singulo)) + if (TryComp(args.EventHorizonUid, out var singulo)) { AdjustEnergy(args.EventHorizonUid, comp.Energy, singularity: singulo); SetEnergy(uid, 0.0f, comp); @@ -255,7 +255,7 @@ public sealed class SingularitySystem : SharedSingularitySystem /// The event arguments. public void OnConsumed(EntityUid uid, SinguloFoodComponent comp, ref EventHorizonConsumedEntityEvent args) { - if (EntityManager.TryGetComponent(args.EventHorizonUid, out var singulo)) + if (TryComp(args.EventHorizonUid, out var singulo)) { // Calculate the percentage change (positive or negative) var percentageChange = singulo.Energy * (comp.EnergyFactor - 1f); diff --git a/Content.Server/Solar/EntitySystems/PowerSolarSystem.cs b/Content.Server/Solar/EntitySystems/PowerSolarSystem.cs index 4f1cdb0eb0..d6c048d13f 100644 --- a/Content.Server/Solar/EntitySystems/PowerSolarSystem.cs +++ b/Content.Server/Solar/EntitySystems/PowerSolarSystem.cs @@ -122,7 +122,7 @@ namespace Content.Server.Solar.EntitySystems private void UpdatePanelCoverage(Entity panel) { var entity = panel.Owner; - var xform = EntityManager.GetComponent(entity); + var xform = Comp(entity); // So apparently, and yes, I *did* only find this out later, // this is just a really fancy way of saying "Lambert's law of cosines". diff --git a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs index 7331894bcf..96deb70b47 100644 --- a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs +++ b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs @@ -89,14 +89,14 @@ namespace Content.Server.Spawners.EntitySystems } if (!Deleted(uid)) - EntityManager.SpawnEntity(_robustRandom.Pick(component.Prototypes), Transform(uid).Coordinates); + Spawn(_robustRandom.Pick(component.Prototypes), Transform(uid).Coordinates); } private void Spawn(EntityUid uid, RandomSpawnerComponent component) { if (component.RarePrototypes.Count > 0 && (component.RareChance == 1.0f || _robustRandom.Prob(component.RareChance))) { - EntityManager.SpawnEntity(_robustRandom.Pick(component.RarePrototypes), Transform(uid).Coordinates); + Spawn(_robustRandom.Pick(component.RarePrototypes), Transform(uid).Coordinates); return; } @@ -118,7 +118,7 @@ namespace Content.Server.Spawners.EntitySystems var coordinates = Transform(uid).Coordinates.Offset(new Vector2(xOffset, yOffset)); - EntityManager.SpawnEntity(_robustRandom.Pick(component.Prototypes), coordinates); + Spawn(_robustRandom.Pick(component.Prototypes), coordinates); } private void Spawn(Entity ent) diff --git a/Content.Server/Species/Systems/NymphSystem.cs b/Content.Server/Species/Systems/NymphSystem.cs index d491b957bf..11493b65b3 100644 --- a/Content.Server/Species/Systems/NymphSystem.cs +++ b/Content.Server/Species/Systems/NymphSystem.cs @@ -35,7 +35,7 @@ public sealed partial class NymphSystem : EntitySystem // Get the organs' position & spawn a nymph there var coords = Transform(uid).Coordinates; - var nymph = EntityManager.SpawnAtPosition(entityProto.ID, coords); + var nymph = SpawnAtPosition(entityProto.ID, coords); if (HasComp(args.OldBody)) // Zombify the new nymph if old one is a zombie _zombie.ZombifyEntity(nymph); diff --git a/Content.Server/Speech/EntitySystems/AddAccentClothingSystem.cs b/Content.Server/Speech/EntitySystems/AddAccentClothingSystem.cs index 98d35efae4..261377a14a 100644 --- a/Content.Server/Speech/EntitySystems/AddAccentClothingSystem.cs +++ b/Content.Server/Speech/EntitySystems/AddAccentClothingSystem.cs @@ -39,7 +39,7 @@ public sealed class AddAccentClothingSystem : EntitySystem // try to remove accent var componentType = Factory.GetRegistration(component.Accent).Type; - EntityManager.RemoveComponent(args.Wearer, componentType); + RemComp(args.Wearer, componentType); component.IsActive = false; } diff --git a/Content.Server/Spreader/KudzuSystem.cs b/Content.Server/Spreader/KudzuSystem.cs index dc176ebc2a..92e6609fd7 100644 --- a/Content.Server/Spreader/KudzuSystem.cs +++ b/Content.Server/Spreader/KudzuSystem.cs @@ -36,7 +36,7 @@ public sealed class KudzuSystem : EntitySystem component.GrowthLevel = 3; component.GrowthLevel = Math.Max(1, component.GrowthLevel - growthDamage); - if (EntityManager.TryGetComponent(uid, out var appearance)) + if (TryComp(uid, out var appearance)) { _appearance.SetData(uid, KudzuVisuals.GrowthLevel, component.GrowthLevel, appearance); } @@ -79,7 +79,7 @@ public sealed class KudzuSystem : EntitySystem private void SetupKudzu(EntityUid uid, KudzuComponent component, ComponentStartup args) { - if (!EntityManager.TryGetComponent(uid, out var appearance)) + if (!TryComp(uid, out var appearance)) { return; } diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 924146bc1f..7b9abee5d2 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -109,7 +109,7 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem if (prototype?.JobEntity != null) { DebugTools.Assert(entity is null); - var jobEntity = EntityManager.SpawnEntity(prototype.JobEntity, coordinates); + var jobEntity = Spawn(prototype.JobEntity, coordinates); MakeSentientCommand.MakeSentient(jobEntity, EntityManager); // Make sure custom names get handled, what is gameticker control flow whoopy. diff --git a/Content.Server/StationEvents/Events/VentClogRule.cs b/Content.Server/StationEvents/Events/VentClogRule.cs index 3b6eeebe85..ad8e4b5a42 100644 --- a/Content.Server/StationEvents/Events/VentClogRule.cs +++ b/Content.Server/StationEvents/Events/VentClogRule.cs @@ -29,7 +29,7 @@ public sealed class VentClogRule : StationEventSystem .Where(x => !x.Abstract) .Select(x => x.ID).ToList(); - foreach (var (_, transform) in EntityManager.EntityQuery()) + foreach (var (_, transform) in EntityQuery()) { if (CompOrNull(transform.GridUid)?.Station != chosenStation) { diff --git a/Content.Server/Storage/EntitySystems/BluespaceLockerSystem.cs b/Content.Server/Storage/EntitySystems/BluespaceLockerSystem.cs index de5ab7a6ca..11f3fb1bdb 100644 --- a/Content.Server/Storage/EntitySystems/BluespaceLockerSystem.cs +++ b/Content.Server/Storage/EntitySystems/BluespaceLockerSystem.cs @@ -95,7 +95,7 @@ public sealed class BluespaceLockerSystem : EntitySystem if (component.BehaviorProperties.TransportEntities || component.BehaviorProperties.TransportSentient) foreach (var entity in target.Value.storageComponent.Contents.ContainedEntities.ToArray()) { - if (EntityManager.HasComponent(entity)) + if (HasComp(entity)) { if (!component.BehaviorProperties.TransportSentient) continue; @@ -312,7 +312,7 @@ public sealed class BluespaceLockerSystem : EntitySystem if (component.BehaviorProperties.TransportEntities || component.BehaviorProperties.TransportSentient) foreach (var entity in entityStorageComponent.Contents.ContainedEntities.ToArray()) { - if (EntityManager.HasComponent(entity)) + if (HasComp(entity)) { if (!component.BehaviorProperties.TransportSentient) continue; diff --git a/Content.Server/Storage/EntitySystems/ItemCounterSystem.cs b/Content.Server/Storage/EntitySystems/ItemCounterSystem.cs index 43fcb32d1f..a1ac2bfdbb 100644 --- a/Content.Server/Storage/EntitySystems/ItemCounterSystem.cs +++ b/Content.Server/Storage/EntitySystems/ItemCounterSystem.cs @@ -13,7 +13,7 @@ namespace Content.Server.Storage.EntitySystems [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; protected override int? GetCount(ContainerModifiedMessage msg, ItemCounterComponent itemCounter) { - if (!EntityManager.TryGetComponent(msg.Container.Owner, out StorageComponent? component)) + if (!TryComp(msg.Container.Owner, out StorageComponent? component)) { return null; } diff --git a/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs b/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs index 548fa7bfda..ec02006b27 100644 --- a/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs +++ b/Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs @@ -40,7 +40,7 @@ namespace Content.Server.Storage.EntitySystems // Calculate the average price of the possible spawned items args.Price += _pricing.GetPrice(protUid) * entry.SpawnProbability * entry.GetAmount(getAverage: true); - EntityManager.DeleteEntity(protUid); + Del(protUid); } foreach (var group in orGroups) @@ -54,7 +54,7 @@ namespace Content.Server.Storage.EntitySystems (entry.SpawnProbability / group.CumulativeProbability) * entry.GetAmount(getAverage: true); - EntityManager.DeleteEntity(protUid); + Del(protUid); } } diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index f9772e48da..742434ff23 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -352,7 +352,7 @@ public sealed partial class StoreSystem _actionContainer.RemoveAction(purchase, logMissing: false); - EntityManager.DeleteEntity(purchase); + Del(purchase); } component.BoughtEntities.Clear(); diff --git a/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs b/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs index 52e3cab79c..ae10957bd8 100644 --- a/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs +++ b/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs @@ -23,7 +23,7 @@ namespace Content.Server.Stunnable private void TryDoCollideStun(EntityUid uid, StunOnCollideComponent component, EntityUid target) { - if (EntityManager.TryGetComponent(target, out var status)) + if (TryComp(target, out var status)) { _stunSystem.TryStun(target, TimeSpan.FromSeconds(component.StunAmount), true, status); diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs index 30bbb2f0e9..4f59654bb9 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs @@ -57,7 +57,7 @@ public sealed class SurveillanceCameraMonitorSystem : EntitySystem if (monitor.LastHeartbeat > _maxHeartbeatTime) { DisconnectCamera(uid, true, monitor); - EntityManager.RemoveComponent(uid); + RemComp(uid); } } } @@ -256,7 +256,7 @@ public sealed class SurveillanceCameraMonitorSystem : EntitySystem monitor.ActiveCamera = null; monitor.ActiveCameraAddress = string.Empty; - EntityManager.RemoveComponent(uid); + RemComp(uid); UpdateUserInterface(uid, monitor); } diff --git a/Content.Server/Tabletop/TabletopSystem.Map.cs b/Content.Server/Tabletop/TabletopSystem.Map.cs index 837ab2df24..5e116d81ef 100644 --- a/Content.Server/Tabletop/TabletopSystem.Map.cs +++ b/Content.Server/Tabletop/TabletopSystem.Map.cs @@ -52,7 +52,7 @@ namespace Content.Server.Tabletop TabletopMap = mapId; _tabletops = 0; - var mapComp = EntityManager.GetComponent(mapUid); + var mapComp = Comp(mapUid); // Lighting is always disabled in tabletop world. mapComp.LightingEnabled = false; diff --git a/Content.Server/Tabletop/TabletopSystem.Session.cs b/Content.Server/Tabletop/TabletopSystem.Session.cs index c2bb8426fc..2f271b4b66 100644 --- a/Content.Server/Tabletop/TabletopSystem.Session.cs +++ b/Content.Server/Tabletop/TabletopSystem.Session.cs @@ -42,7 +42,7 @@ namespace Content.Server.Tabletop /// The UID of the tabletop game entity. public void CleanupSession(EntityUid uid) { - if (!EntityManager.TryGetComponent(uid, out TabletopGameComponent? tabletop)) + if (!TryComp(uid, out TabletopGameComponent? tabletop)) return; if (tabletop.Session is not { } session) @@ -55,7 +55,7 @@ namespace Content.Server.Tabletop foreach (var euid in session.Entities) { - EntityManager.QueueDeleteEntity(euid); + QueueDel(euid); } tabletop.Session = null; @@ -68,7 +68,7 @@ namespace Content.Server.Tabletop /// The UID of the tabletop game entity. public void OpenSessionFor(ICommonSession player, EntityUid uid) { - if (!EntityManager.TryGetComponent(uid, out TabletopGameComponent? tabletop) || player.AttachedEntity is not {Valid: true} attachedEntity) + if (!TryComp(uid, out TabletopGameComponent? tabletop) || player.AttachedEntity is not {Valid: true} attachedEntity) return; // Make sure we have a session, and add the player to it if not added already. @@ -77,7 +77,7 @@ namespace Content.Server.Tabletop if (session.Players.ContainsKey(player)) return; - if(EntityManager.TryGetComponent(attachedEntity, out TabletopGamerComponent? gamer)) + if(TryComp(attachedEntity, out TabletopGamerComponent? gamer)) CloseSessionFor(player, gamer.Tabletop, false); // Set the entity as an absolute GAMER. @@ -100,26 +100,26 @@ namespace Content.Server.Tabletop /// Whether to remove the from the player's attached entity. public void CloseSessionFor(ICommonSession player, EntityUid uid, bool removeGamerComponent = true) { - if (!EntityManager.TryGetComponent(uid, out TabletopGameComponent? tabletop) || tabletop.Session is not { } session) + if (!TryComp(uid, out TabletopGameComponent? tabletop) || tabletop.Session is not { } session) return; if (!session.Players.TryGetValue(player, out var data)) return; - if(removeGamerComponent && player.AttachedEntity is {} attachedEntity && EntityManager.TryGetComponent(attachedEntity, out TabletopGamerComponent? gamer)) + if(removeGamerComponent && player.AttachedEntity is {} attachedEntity && TryComp(attachedEntity, out TabletopGamerComponent? gamer)) { // We invalidate this to prevent an infinite feedback from removing the component. gamer.Tabletop = EntityUid.Invalid; // You stop being a gamer....... - EntityManager.RemoveComponent(attachedEntity); + RemComp(attachedEntity); } session.Players.Remove(player); session.Entities.Remove(data.Camera); // Deleting the view subscriber automatically cleans up subscriptions, no need to do anything else. - EntityManager.QueueDeleteEntity(data.Camera); + QueueDel(data.Camera); } /// diff --git a/Content.Server/Tabletop/TabletopSystem.cs b/Content.Server/Tabletop/TabletopSystem.cs index f3f5801b2d..e771add0e4 100644 --- a/Content.Server/Tabletop/TabletopSystem.cs +++ b/Content.Server/Tabletop/TabletopSystem.cs @@ -80,7 +80,7 @@ namespace Content.Server.Tabletop if (!_cfg.GetCVar(CCVars.GameTabletopPlace)) return; - if (!EntityManager.TryGetComponent(args.User, out HandsComponent? hands)) + if (!TryComp(args.User, out HandsComponent? hands)) return; if (component.Session is not { } session) @@ -128,7 +128,7 @@ namespace Content.Server.Tabletop if (!args.CanAccess || !args.CanInteract) return; - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + if (!TryComp(args.User, out ActorComponent? actor)) return; var playVerb = new ActivationVerb() @@ -147,7 +147,7 @@ namespace Content.Server.Tabletop return; // Check that a player is attached to the entity. - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + if (!TryComp(args.User, out ActorComponent? actor)) return; OpenSessionFor(actor.PlayerSession, uid); @@ -171,7 +171,7 @@ namespace Content.Server.Tabletop private void OnGamerShutdown(EntityUid uid, TabletopGamerComponent component, ComponentShutdown args) { - if (!EntityManager.TryGetComponent(uid, out ActorComponent? actor)) + if (!TryComp(uid, out ActorComponent? actor)) return; if(component.Tabletop.IsValid()) @@ -190,7 +190,7 @@ namespace Content.Server.Tabletop if (!TryComp(uid, out ActorComponent? actor)) { - EntityManager.RemoveComponent(uid); + RemComp(uid); return; } diff --git a/Content.Server/Telephone/TelephoneSystem.cs b/Content.Server/Telephone/TelephoneSystem.cs index dd068612f7..ab3026fc97 100644 --- a/Content.Server/Telephone/TelephoneSystem.cs +++ b/Content.Server/Telephone/TelephoneSystem.cs @@ -124,7 +124,7 @@ public sealed class TelephoneSystem : SharedTelephoneSystem { base.Update(frameTime); - var query = EntityManager.EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var telephone)) { var entity = new Entity(uid, telephone); diff --git a/Content.Server/Traits/TraitSystem.cs b/Content.Server/Traits/TraitSystem.cs index 38cbc1859c..59cb9537f2 100644 --- a/Content.Server/Traits/TraitSystem.cs +++ b/Content.Server/Traits/TraitSystem.cs @@ -55,7 +55,7 @@ public sealed class TraitSystem : EntitySystem continue; var coords = Transform(args.Mob).Coordinates; - var inhandEntity = EntityManager.SpawnEntity(traitPrototype.TraitGear, coords); + var inhandEntity = Spawn(traitPrototype.TraitGear, coords); _sharedHandsSystem.TryPickup(args.Mob, inhandEntity, checkActionBlocker: false, diff --git a/Content.Server/Verbs/VerbSystem.cs b/Content.Server/Verbs/VerbSystem.cs index 7a1e5facd0..871b75f7f0 100644 --- a/Content.Server/Verbs/VerbSystem.cs +++ b/Content.Server/Verbs/VerbSystem.cs @@ -29,7 +29,7 @@ namespace Content.Server.Verbs { var player = eventArgs.SenderSession; - if (!EntityManager.EntityExists(GetEntity(args.EntityUid))) + if (!Exists(GetEntity(args.EntityUid))) { Log.Warning($"{nameof(HandleVerbRequest)} called on a non-existent entity with id {args.EntityUid} by player {player}."); return; diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index e6a50425b4..fef52a5b5d 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -395,7 +395,7 @@ public sealed class WiresSystem : SharedWiresSystem { var player = args.Actor; - if (!EntityManager.TryGetComponent(player, out HandsComponent? handsComponent)) + if (!TryComp(player, out HandsComponent? handsComponent)) { _popupSystem.PopupEntity(Loc.GetString("wires-component-ui-on-receive-message-no-hands"), uid, player); return; @@ -410,7 +410,7 @@ public sealed class WiresSystem : SharedWiresSystem if (!_hands.TryGetActiveItem((player, handsComponent), out var heldEntity)) return; - if (!EntityManager.TryGetComponent(heldEntity, out ToolComponent? tool)) + if (!TryComp(heldEntity, out ToolComponent? tool)) return; TryDoWireAction(uid, player, heldEntity.Value, args.Id, args.Action, component, tool); diff --git a/Content.Shared/Access/Systems/IdExaminableSystem.cs b/Content.Shared/Access/Systems/IdExaminableSystem.cs index 807ccc6616..c05262f2f6 100644 --- a/Content.Shared/Access/Systems/IdExaminableSystem.cs +++ b/Content.Shared/Access/Systems/IdExaminableSystem.cs @@ -51,13 +51,13 @@ public sealed class IdExaminableSystem : EntitySystem if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid)) { // PDA - if (EntityManager.TryGetComponent(idUid, out PdaComponent? pda) && + if (TryComp(idUid, out PdaComponent? pda) && TryComp(pda.ContainedId, out var id)) { return GetNameAndJob(id); } // ID Card - if (EntityManager.TryGetComponent(idUid, out id)) + if (TryComp(idUid, out id)) { return GetNameAndJob(id); } diff --git a/Content.Shared/Actions/ActionUpgradeSystem.cs b/Content.Shared/Actions/ActionUpgradeSystem.cs index 72e412b50f..8cddf2daeb 100644 --- a/Content.Shared/Actions/ActionUpgradeSystem.cs +++ b/Content.Shared/Actions/ActionUpgradeSystem.cs @@ -54,7 +54,7 @@ public sealed class ActionUpgradeSystem : EntitySystem // TODO: Preserve ordering of actions - _entityManager.DeleteEntity(uid); + Del(uid); } public bool TryUpgradeAction(EntityUid? actionId, out EntityUid? upgradeActionId, ActionUpgradeComponent? actionUpgradeComponent = null, int newLevel = 0) @@ -185,7 +185,7 @@ public sealed class ActionUpgradeSystem : EntitySystem // TODO: Preserve ordering of actions - _entityManager.DeleteEntity(actionId); + Del(actionId); return upgradedActionId.Value; } diff --git a/Content.Shared/Alert/AlertsSystem.cs b/Content.Shared/Alert/AlertsSystem.cs index 429c0670ad..94085c3a27 100644 --- a/Content.Shared/Alert/AlertsSystem.cs +++ b/Content.Shared/Alert/AlertsSystem.cs @@ -15,7 +15,7 @@ public abstract class AlertsSystem : EntitySystem public IReadOnlyDictionary? GetActiveAlerts(EntityUid euid) { - return EntityManager.TryGetComponent(euid, out AlertsComponent? comp) + return TryComp(euid, out AlertsComponent? comp) ? comp.Alerts : null; } @@ -38,7 +38,7 @@ public abstract class AlertsSystem : EntitySystem public bool IsShowingAlert(EntityUid euid, ProtoId alertType) { - if (!EntityManager.TryGetComponent(euid, out AlertsComponent? alertsComponent)) + if (!TryComp(euid, out AlertsComponent? alertsComponent)) return false; if (TryGet(alertType, out var alert)) @@ -53,13 +53,13 @@ public abstract class AlertsSystem : EntitySystem /// true iff an alert of the indicated alert category is currently showing public bool IsShowingAlertCategory(EntityUid euid, ProtoId alertCategory) { - return EntityManager.TryGetComponent(euid, out AlertsComponent? alertsComponent) + return TryComp(euid, out AlertsComponent? alertsComponent) && alertsComponent.Alerts.ContainsKey(AlertKey.ForCategory(alertCategory)); } public bool TryGetAlertState(EntityUid euid, AlertKey key, out AlertState alertState) { - if (EntityManager.TryGetComponent(euid, out AlertsComponent? alertsComponent)) + if (TryComp(euid, out AlertsComponent? alertsComponent)) return alertsComponent.Alerts.TryGetValue(key, out alertState); alertState = default; @@ -155,7 +155,7 @@ public abstract class AlertsSystem : EntitySystem if (_timing.ApplyingState) return; - if (!EntityManager.TryGetComponent(euid, out AlertsComponent? alertsComponent)) + if (!TryComp(euid, out AlertsComponent? alertsComponent)) return; if (TryGet(alertType, out var alert)) @@ -311,14 +311,14 @@ public abstract class AlertsSystem : EntitySystem private void HandleClickAlert(ClickAlertEvent msg, EntitySessionEventArgs args) { var player = args.SenderSession.AttachedEntity; - if (player is null || !EntityManager.HasComponent(player)) + if (player is null || !HasComp(player)) return; if (!IsShowingAlert(player.Value, msg.Type)) { Log.Debug("User {0} attempted to" + " click alert {1} which is not currently showing for them", - EntityManager.GetComponent(player.Value).EntityName, msg.Type); + Comp(player.Value).EntityName, msg.Type); return; } diff --git a/Content.Shared/Animals/UdderSystem.cs b/Content.Shared/Animals/UdderSystem.cs index 90cc717321..ad813f259a 100644 --- a/Content.Shared/Animals/UdderSystem.cs +++ b/Content.Shared/Animals/UdderSystem.cs @@ -71,7 +71,7 @@ public sealed class UdderSystem : EntitySystem continue; // Actually there is food digestion so no problem with instant reagent generation "OnFeed" - if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger)) + if (TryComp(uid, out HungerComponent? hunger)) { // Is there enough nutrition to produce reagent? if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay) @@ -133,7 +133,7 @@ public sealed class UdderSystem : EntitySystem { if (args.Using == null || !args.CanInteract || - !EntityManager.HasComponent(args.Using.Value)) + !HasComp(args.Using.Value)) return; var uid = entity.Owner; diff --git a/Content.Shared/Animals/WoolySystem.cs b/Content.Shared/Animals/WoolySystem.cs index 172e3b56e1..734de2f34c 100644 --- a/Content.Shared/Animals/WoolySystem.cs +++ b/Content.Shared/Animals/WoolySystem.cs @@ -65,7 +65,7 @@ public sealed class WoolySystem : EntitySystem continue; // Actually there is food digestion so no problem with instant reagent generation "OnFeed" - if (EntityManager.TryGetComponent(uid, out HungerComponent? hunger)) + if (TryComp(uid, out HungerComponent? hunger)) { // Is there enough nutrition to produce reagent? if (_hunger.GetHungerThreshold(hunger) < HungerThreshold.Okay) diff --git a/Content.Shared/Bed/Cryostorage/SharedCryostorageSystem.cs b/Content.Shared/Bed/Cryostorage/SharedCryostorageSystem.cs index d1066b43e2..a93f2e0618 100644 --- a/Content.Shared/Bed/Cryostorage/SharedCryostorageSystem.cs +++ b/Content.Shared/Bed/Cryostorage/SharedCryostorageSystem.cs @@ -158,7 +158,7 @@ public abstract class SharedCryostorageSystem : EntitySystem if (PausedMap == null || !Exists(PausedMap)) return; - EntityManager.DeleteEntity(PausedMap.Value); + Del(PausedMap.Value); PausedMap = null; } diff --git a/Content.Shared/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Shared/Chemistry/EntitySystems/HypospraySystem.cs index 97cf9396d2..86f6edc903 100644 --- a/Content.Shared/Chemistry/EntitySystems/HypospraySystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/HypospraySystem.cs @@ -179,7 +179,7 @@ public sealed class HypospraySystem : EntitySystem RaiseLocalEvent(target, ref ev); // same LogType as syringes... - _adminLogger.Add(LogType.ForceFeed, $"{EntityManager.ToPrettyString(user):user} injected {EntityManager.ToPrettyString(target):target} with a solution {SharedSolutionContainerSystem.ToPrettyString(removedSolution):removedSolution} using a {EntityManager.ToPrettyString(uid):using}"); + _adminLogger.Add(LogType.ForceFeed, $"{ToPrettyString(user):user} injected {ToPrettyString(target):target} with a solution {SharedSolutionContainerSystem.ToPrettyString(removedSolution):removedSolution} using a {ToPrettyString(uid):using}"); return true; } diff --git a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs index e314db0ddb..fe4d102536 100644 --- a/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SharedSolutionContainerSystem.cs @@ -282,8 +282,8 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem public FixedPoint2 GetTotalPrototypeQuantity(EntityUid owner, string reagentId) { var reagentQuantity = FixedPoint2.New(0); - if (EntityManager.EntityExists(owner) - && EntityManager.TryGetComponent(owner, out SolutionContainerManagerComponent? managerComponent)) + if (Exists(owner) + && TryComp(owner, out SolutionContainerManagerComponent? managerComponent)) { foreach (var (_, soln) in EnumerateSolutions((owner, managerComponent))) { @@ -335,7 +335,7 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem var (uid, comp, appearanceComponent) = soln; var solution = comp.Solution; - if (!EntityManager.EntityExists(uid) || !Resolve(uid, ref appearanceComponent, false)) + if (!Exists(uid) || !Resolve(uid, ref appearanceComponent, false)) return; AppearanceSystem.SetData(uid, SolutionContainerVisuals.FillFraction, solution.FillFraction, appearanceComponent); diff --git a/Content.Shared/Chemistry/MetabolismMovespeedModifierSystem.cs b/Content.Shared/Chemistry/MetabolismMovespeedModifierSystem.cs index e9d3b7de53..68a55dd468 100644 --- a/Content.Shared/Chemistry/MetabolismMovespeedModifierSystem.cs +++ b/Content.Shared/Chemistry/MetabolismMovespeedModifierSystem.cs @@ -52,7 +52,7 @@ namespace Content.Shared.Chemistry continue; _components.RemoveAt(i); - EntityManager.RemoveComponent(metabolism); + RemComp(metabolism); _movespeed.RefreshMovementSpeedModifiers(metabolism); } diff --git a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs index cd0996a017..e690b93826 100644 --- a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs +++ b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs @@ -122,7 +122,7 @@ public sealed partial class AnchorableSystem : EntitySystem _adminLogger.Add( LogType.Unanchor, LogImpact.Low, - $"{EntityManager.ToPrettyString(args.User):user} unanchored {EntityManager.ToPrettyString(uid):anchored} using {EntityManager.ToPrettyString(used):using}" + $"{ToPrettyString(args.User):user} unanchored {ToPrettyString(uid):anchored} using {ToPrettyString(used):using}" ); } @@ -174,7 +174,7 @@ public sealed partial class AnchorableSystem : EntitySystem _adminLogger.Add( LogType.Anchor, LogImpact.Low, - $"{EntityManager.ToPrettyString(args.User):user} anchored {EntityManager.ToPrettyString(uid):anchored} using {EntityManager.ToPrettyString(used):using}" + $"{ToPrettyString(args.User):user} anchored {ToPrettyString(uid):anchored} using {ToPrettyString(used):using}" ); } diff --git a/Content.Shared/Containers/ContainerFillSystem.cs b/Content.Shared/Containers/ContainerFillSystem.cs index 3d49079ea7..2eff64aaf0 100644 --- a/Content.Shared/Containers/ContainerFillSystem.cs +++ b/Content.Shared/Containers/ContainerFillSystem.cs @@ -40,7 +40,7 @@ public sealed class ContainerFillSystem : EntitySystem var ent = Spawn(proto, coords); if (!_containerSystem.Insert(ent, container, containerXform: xform)) { - var alreadyContained = container.ContainedEntities.Count > 0 ? string.Join("\n", container.ContainedEntities.Select(e => $"\t - {EntityManager.ToPrettyString(e)}")) : "< empty >"; + var alreadyContained = container.ContainedEntities.Count > 0 ? string.Join("\n", container.ContainedEntities.Select(e => $"\t - {ToPrettyString(e)}")) : "< empty >"; Log.Error($"Entity {ToPrettyString(uid)} with a {nameof(ContainerFillComponent)} failed to insert an entity: {ToPrettyString(ent)}.\nCurrent contents:\n{alreadyContained}"); _transform.AttachToGridOrMap(ent); break; @@ -74,7 +74,7 @@ public sealed class ContainerFillSystem : EntitySystem var spawn = Spawn(proto, coords); if (!_containerSystem.Insert(spawn, container, containerXform: xform)) { - var alreadyContained = container.ContainedEntities.Count > 0 ? string.Join("\n", container.ContainedEntities.Select(e => $"\t - {EntityManager.ToPrettyString(e)}")) : "< empty >"; + var alreadyContained = container.ContainedEntities.Count > 0 ? string.Join("\n", container.ContainedEntities.Select(e => $"\t - {ToPrettyString(e)}")) : "< empty >"; Log.Error($"Entity {ToPrettyString(ent)} with a {nameof(EntityTableContainerFillComponent)} failed to insert an entity: {ToPrettyString(spawn)}.\nCurrent contents:\n{alreadyContained}"); _transform.AttachToGridOrMap(spawn); break; diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index 4f5f567e43..b3eb4b23c2 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -102,7 +102,7 @@ namespace Content.Shared.Containers.ItemSlots { if (existing.Local) Log.Error( - $"Duplicate item slot key. Entity: {EntityManager.GetComponent(uid).EntityName} ({uid}), key: {id}"); + $"Duplicate item slot key. Entity: {Comp(uid).EntityName} ({uid}), key: {id}"); else // server state takes priority slot.CopyFrom(existing); @@ -132,7 +132,7 @@ namespace Content.Shared.Containers.ItemSlots itemSlots.Slots.Remove(slot.ContainerSlot.ID); if (itemSlots.Slots.Count == 0) - EntityManager.RemoveComponent(uid, itemSlots); + RemComp(uid, itemSlots); else Dirty(uid, itemSlots); } @@ -206,7 +206,7 @@ namespace Content.Shared.Containers.ItemSlots if (args.Handled) return; - if (!EntityManager.TryGetComponent(args.User, out HandsComponent? hands)) + if (!TryComp(args.User, out HandsComponent? hands)) return; if (itemSlots.Slots.Count == 0) @@ -700,7 +700,7 @@ namespace Content.Shared.Containers.ItemSlots var verbSubject = slot.Name != string.Empty ? Loc.GetString(slot.Name) - : EntityManager.GetComponent(slot.Item.Value).EntityName ?? string.Empty; + : Comp(slot.Item.Value).EntityName ?? string.Empty; AlternativeVerb verb = new() { diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index c16bbd1e85..3b0f6c8a30 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -236,7 +236,7 @@ namespace Content.Shared.Cuffs private void HandleMoveAttempt(EntityUid uid, CuffableComponent component, UpdateCanMoveEvent args) { - if (component.CanStillInteract || !EntityManager.TryGetComponent(uid, out PullableComponent? pullable) || !pullable.BeingPulled) + if (component.CanStillInteract || !TryComp(uid, out PullableComponent? pullable) || !pullable.BeingPulled) return; args.Cancel(); diff --git a/Content.Shared/Damage/Systems/DamageOnHighSpeedImpactSystem.cs b/Content.Shared/Damage/Systems/DamageOnHighSpeedImpactSystem.cs index c35e95ecb8..6ef41e8721 100644 --- a/Content.Shared/Damage/Systems/DamageOnHighSpeedImpactSystem.cs +++ b/Content.Shared/Damage/Systems/DamageOnHighSpeedImpactSystem.cs @@ -30,7 +30,7 @@ public sealed class DamageOnHighSpeedImpactSystem : EntitySystem if (!args.OurFixture.Hard || !args.OtherFixture.Hard) return; - if (!EntityManager.HasComponent(uid)) + if (!HasComp(uid)) return; //TODO: This should solve after physics solves diff --git a/Content.Shared/Damage/Systems/SlowOnDamageSystem.cs b/Content.Shared/Damage/Systems/SlowOnDamageSystem.cs index 6b5f57c595..e339727200 100644 --- a/Content.Shared/Damage/Systems/SlowOnDamageSystem.cs +++ b/Content.Shared/Damage/Systems/SlowOnDamageSystem.cs @@ -30,7 +30,7 @@ namespace Content.Shared.Damage private void OnRefreshMovespeed(EntityUid uid, SlowOnDamageComponent component, RefreshMovementSpeedModifiersEvent args) { - if (!EntityManager.TryGetComponent(uid, out var damage)) + if (!TryComp(uid, out var damage)) return; if (damage.TotalDamage == FixedPoint2.Zero) diff --git a/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs b/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs index cb7a8c46c8..077649f7a8 100644 --- a/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs +++ b/Content.Shared/Disposal/Mailing/SharedMailingUnitSystem.cs @@ -156,7 +156,7 @@ public abstract class SharedMailingUnitSystem : EntitySystem if (args.Handled || !args.Complex) return; - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) + if (!TryComp(args.User, out ActorComponent? actor)) { return; } diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 228926fac4..7c063f6ce9 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -111,7 +111,7 @@ namespace Content.Shared.Examine if (!examinerComp.CheckInRangeUnOccluded) return true; - if (EntityManager.GetComponent(examiner).MapID != target.MapId) + if (Comp(examiner).MapID != target.MapId) return false; // Do target InRangeUnoccluded which has different checks. diff --git a/Content.Shared/Gravity/SharedGravitySystem.cs b/Content.Shared/Gravity/SharedGravitySystem.cs index 2f532d0f1d..3ff9792e86 100644 --- a/Content.Shared/Gravity/SharedGravitySystem.cs +++ b/Content.Shared/Gravity/SharedGravitySystem.cs @@ -148,7 +148,7 @@ namespace Content.Shared.Gravity private void OnGridInit(GridInitializeEvent ev) { - EntityManager.EnsureComponent(ev.EntityUid); + EnsureComp(ev.EntityUid); } [Serializable, NetSerializable] diff --git a/Content.Shared/Implants/SharedImplanterSystem.cs b/Content.Shared/Implants/SharedImplanterSystem.cs index 41c2daed3e..6e806384a0 100644 --- a/Content.Shared/Implants/SharedImplanterSystem.cs +++ b/Content.Shared/Implants/SharedImplanterSystem.cs @@ -56,7 +56,7 @@ public abstract class SharedImplanterSystem : EntitySystem private void OnEntInserted(EntityUid uid, ImplanterComponent component, EntInsertedIntoContainerMessage args) { - var implantData = EntityManager.GetComponent(args.Entity); + var implantData = Comp(args.Entity); component.ImplantData = (implantData.EntityName, implantData.EntityDescription); } diff --git a/Content.Shared/Inventory/InventorySystem.Helpers.cs b/Content.Shared/Inventory/InventorySystem.Helpers.cs index 44dede02c7..259c3b6c8d 100644 --- a/Content.Shared/Inventory/InventorySystem.Helpers.cs +++ b/Content.Shared/Inventory/InventorySystem.Helpers.cs @@ -73,12 +73,12 @@ public partial class InventorySystem return false; // Let's spawn this first... - var item = EntityManager.SpawnEntity(prototype, Transform(uid).Coordinates); + var item = Spawn(prototype, Transform(uid).Coordinates); // Helper method that deletes the item and returns false. bool DeleteItem() { - EntityManager.DeleteEntity(item); + Del(item); return false; } diff --git a/Content.Shared/Jittering/SharedJitteringSystem.cs b/Content.Shared/Jittering/SharedJitteringSystem.cs index 1ac8413375..fd0758c3c4 100644 --- a/Content.Shared/Jittering/SharedJitteringSystem.cs +++ b/Content.Shared/Jittering/SharedJitteringSystem.cs @@ -25,7 +25,7 @@ namespace Content.Shared.Jittering private void OnRejuvenate(EntityUid uid, JitteringComponent component, RejuvenateEvent args) { - EntityManager.RemoveComponentDeferred(uid); + RemCompDeferred(uid); } /// @@ -54,7 +54,7 @@ namespace Content.Shared.Jittering if (StatusEffects.TryAddStatusEffect(uid, "Jitter", time, refresh, status)) { - var jittering = EntityManager.GetComponent(uid); + var jittering = Comp(uid); if(forceValueChange || jittering.Amplitude < amplitude) jittering.Amplitude = amplitude; diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index a860f1dcfb..e5258f2518 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -359,7 +359,7 @@ public abstract class SharedMagicSystem : EntitySystem var component = (Component)Factory.GetComponent(name); var temp = (object)component; _seriMan.CopyTo(data.Component, ref temp); - EntityManager.AddComponent(target, (Component)temp!); + AddComp(target, (Component)temp!); } } diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index e1c07df3c3..bc169aa201 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -226,7 +226,7 @@ public sealed class PullingSystem : EntitySystem if (component.Pulling != args.BlockingEntity) return; - if (EntityManager.TryGetComponent(args.BlockingEntity, out PullableComponent? comp)) + if (TryComp(args.BlockingEntity, out PullableComponent? comp)) { TryStopPull(args.BlockingEntity, comp); } @@ -424,7 +424,7 @@ public sealed class PullingSystem : EntitySystem return false; } - if (!EntityManager.TryGetComponent(pullableUid, out var physics)) + if (!TryComp(pullableUid, out var physics)) { return false; } diff --git a/Content.Shared/Movement/Systems/FrictionContactsSystem.cs b/Content.Shared/Movement/Systems/FrictionContactsSystem.cs index 09d74bfb40..44fc3933e3 100644 --- a/Content.Shared/Movement/Systems/FrictionContactsSystem.cs +++ b/Content.Shared/Movement/Systems/FrictionContactsSystem.cs @@ -77,7 +77,7 @@ public sealed class FrictionContactsSystem : EntitySystem private void OnRefreshFrictionModifiers(Entity entity, ref RefreshFrictionModifiersEvent args) { - if (!EntityManager.TryGetComponent(entity, out var physicsComponent)) + if (!TryComp(entity, out var physicsComponent)) return; var friction = 0.0f; diff --git a/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs index e2dee87019..8a0b085a83 100644 --- a/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs +++ b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs @@ -78,7 +78,7 @@ public sealed class SpeedModifierContactsSystem : EntitySystem private void OnRefreshMovementSpeedModifiers(EntityUid uid, SpeedModifiedByContactComponent component, RefreshMovementSpeedModifiersEvent args) { - if (!EntityManager.TryGetComponent(uid, out var physicsComponent)) + if (!TryComp(uid, out var physicsComponent)) return; var walkSpeed = 0.0f; diff --git a/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs b/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs index bd7251b943..fa07aa20c5 100644 --- a/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs @@ -40,7 +40,7 @@ namespace Content.Shared.Nutrition.EntitySystems creamPied.CreamPied = value; - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) + if (TryComp(uid, out AppearanceComponent? appearance)) { _appearance.SetData(uid, CreamPiedVisuals.Creamed, value, appearance); } @@ -58,7 +58,7 @@ namespace Content.Shared.Nutrition.EntitySystems private void OnCreamPiedHitBy(EntityUid uid, CreamPiedComponent creamPied, ThrowHitByEvent args) { - if (!EntityManager.EntityExists(args.Thrown) || !EntityManager.TryGetComponent(args.Thrown, out CreamPieComponent? creamPie)) return; + if (!Exists(args.Thrown) || !TryComp(args.Thrown, out CreamPieComponent? creamPie)) return; SetCreamPied(uid, creamPied, true); diff --git a/Content.Shared/Nutrition/EntitySystems/UtensilSystem.cs b/Content.Shared/Nutrition/EntitySystems/UtensilSystem.cs index 431302593d..63fe822186 100644 --- a/Content.Shared/Nutrition/EntitySystems/UtensilSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/UtensilSystem.cs @@ -38,7 +38,7 @@ public sealed class UtensilSystem : EntitySystem public (bool Success, bool Handled) TryUseUtensil(EntityUid user, EntityUid target, Entity utensil) { - if (!EntityManager.TryGetComponent(target, out FoodComponent? food)) + if (!TryComp(target, out FoodComponent? food)) return (false, false); //Prevents food usage with a wrong utensil @@ -67,7 +67,7 @@ public sealed class UtensilSystem : EntitySystem if (_robustRandom.Prob(component.BreakChance)) { _audio.PlayPredicted(component.BreakSound, userUid, userUid, AudioParams.Default.WithVolume(-2f)); - EntityManager.DeleteEntity(uid); + Del(uid); } } } diff --git a/Content.Shared/Paper/PaperSystem.cs b/Content.Shared/Paper/PaperSystem.cs index 04d6d4026c..75496d93b4 100644 --- a/Content.Shared/Paper/PaperSystem.cs +++ b/Content.Shared/Paper/PaperSystem.cs @@ -217,7 +217,7 @@ public sealed class PaperSystem : EntitySystem { if (!_paperQuery.TryComp(ent, out var paperComp)) { - Log.Warning($"{EntityManager.ToPrettyString(ent)} has a {nameof(RandomPaperContentComponent)} but no {nameof(PaperComponent)}!"); + Log.Warning($"{ToPrettyString(ent)} has a {nameof(RandomPaperContentComponent)} but no {nameof(PaperComponent)}!"); RemCompDeferred(ent, ent.Comp); return; } diff --git a/Content.Shared/RCD/Systems/RCDSystem.cs b/Content.Shared/RCD/Systems/RCDSystem.cs index 68056ef267..cb8450a500 100644 --- a/Content.Shared/RCD/Systems/RCDSystem.cs +++ b/Content.Shared/RCD/Systems/RCDSystem.cs @@ -206,7 +206,7 @@ public sealed class RCDSystem : EntitySystem // Try to start the do after var effect = Spawn(effectPrototype, location); - var ev = new RCDDoAfterEvent(GetNetCoordinates(location), component.ConstructionDirection, component.ProtoId, cost, EntityManager.GetNetEntity(effect)); + var ev = new RCDDoAfterEvent(GetNetCoordinates(location), component.ConstructionDirection, component.ProtoId, cost, GetNetEntity(effect)); var doAfterArgs = new DoAfterArgs(EntityManager, user, delay, ev, uid, target: args.Target, used: uid) { @@ -261,7 +261,7 @@ public sealed class RCDSystem : EntitySystem { // Delete the effect entity if the do-after was cancelled (server-side only) if (_net.IsServer) - QueueDel(EntityManager.GetEntity(args.Effect)); + QueueDel(GetEntity(args.Effect)); return; } diff --git a/Content.Shared/Singularity/EntitySystems/SharedEventHorizonSystem.cs b/Content.Shared/Singularity/EntitySystems/SharedEventHorizonSystem.cs index c2b52c5af3..a791a70668 100644 --- a/Content.Shared/Singularity/EntitySystems/SharedEventHorizonSystem.cs +++ b/Content.Shared/Singularity/EntitySystems/SharedEventHorizonSystem.cs @@ -172,7 +172,7 @@ public abstract class SharedEventHorizonSystem : EntitySystem _physics.SetHard(uid, collider, true, fixtures); } - EntityManager.Dirty(uid, fixtures); + Dirty(uid, fixtures); } #endregion Getters/Setters diff --git a/Content.Shared/Station/SharedStationSpawningSystem.cs b/Content.Shared/Station/SharedStationSpawningSystem.cs index a0a2257402..ac65bd5584 100644 --- a/Content.Shared/Station/SharedStationSpawningSystem.cs +++ b/Content.Shared/Station/SharedStationSpawningSystem.cs @@ -127,7 +127,7 @@ public abstract class SharedStationSpawningSystem : EntitySystem var equipmentStr = startingGear.GetGear(slot.Name); if (!string.IsNullOrEmpty(equipmentStr)) { - var equipmentEntity = EntityManager.SpawnEntity(equipmentStr, xform.Coordinates); + var equipmentEntity = Spawn(equipmentStr, xform.Coordinates); InventorySystem.TryEquip(entity, equipmentEntity, slot.Name, silent: true, force: true); } } @@ -139,7 +139,7 @@ public abstract class SharedStationSpawningSystem : EntitySystem var coords = xform.Coordinates; foreach (var prototype in inhand) { - var inhandEntity = EntityManager.SpawnEntity(prototype, coords); + var inhandEntity = Spawn(prototype, coords); if (_handsSystem.TryGetEmptyHand((entity, handsComponent), out var emptyHand)) { diff --git a/Content.Shared/StatusEffect/StatusEffectsSystem.cs b/Content.Shared/StatusEffect/StatusEffectsSystem.cs index 9a73f83d34..f3409d1c2c 100644 --- a/Content.Shared/StatusEffect/StatusEffectsSystem.cs +++ b/Content.Shared/StatusEffect/StatusEffectsSystem.cs @@ -120,7 +120,7 @@ namespace Content.Shared.StatusEffect if (HasComp(uid)) return true; - EntityManager.AddComponent(uid); + AddComp(uid); status.ActiveEffects[key].RelevantComponent = Factory.GetComponentName(); return true; @@ -136,10 +136,10 @@ namespace Content.Shared.StatusEffect if (TryAddStatusEffect(uid, key, time, refresh, status)) { // If they already have the comp, we just won't bother updating anything. - if (!EntityManager.HasComponent(uid, Factory.GetRegistration(component).Type)) + if (!HasComp(uid, Factory.GetRegistration(component).Type)) { var newComponent = (Component) Factory.GetComponent(component); - EntityManager.AddComponent(uid, newComponent); + AddComp(uid, newComponent); status.ActiveEffects[key].RelevantComponent = component; } return true; @@ -279,7 +279,7 @@ namespace Content.Shared.StatusEffect && Factory.TryGetRegistration(state.RelevantComponent, out var registration)) { var type = registration.Type; - EntityManager.RemoveComponent(uid, type); + RemComp(uid, type); } if (proto.Alert != null) diff --git a/Content.Shared/Storage/EntitySystems/SharedItemCounterSystem.cs b/Content.Shared/Storage/EntitySystems/SharedItemCounterSystem.cs index 8ef62c45c4..62f91d8663 100644 --- a/Content.Shared/Storage/EntitySystems/SharedItemCounterSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedItemCounterSystem.cs @@ -21,7 +21,7 @@ namespace Content.Shared.Storage.EntitySystems private void CounterEntityInserted(EntityUid uid, ItemCounterComponent itemCounter, EntInsertedIntoContainerMessage args) { - if (!EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent)) + if (!TryComp(uid, out AppearanceComponent? appearanceComponent)) return; var count = GetCount(args, itemCounter); @@ -37,7 +37,7 @@ namespace Content.Shared.Storage.EntitySystems private void CounterEntityRemoved(EntityUid uid, ItemCounterComponent itemCounter, EntRemovedFromContainerMessage args) { - if (!EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent)) + if (!TryComp(uid, out AppearanceComponent? appearanceComponent)) return; var count = GetCount(args, itemCounter); diff --git a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs index 7ae821d8d9..3b4b08bbfd 100644 --- a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs @@ -34,7 +34,7 @@ public abstract class SharedItemMapperSystem : EntitySystem val.Layer = layerName; } - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent)) + if (TryComp(uid, out AppearanceComponent? appearanceComponent)) { var list = new List(component.MapLayers.Keys); _appearance.SetData(uid, StorageMapVisuals.InitLayers, new ShowLayerData(list), appearanceComponent); @@ -67,7 +67,7 @@ public abstract class SharedItemMapperSystem : EntitySystem if (!Resolve(uid, ref itemMapper)) return; - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent) + if (TryComp(uid, out AppearanceComponent? appearanceComponent) && TryGetLayers(uid, itemMapper, out var containedLayers)) { _appearance.SetData(uid, diff --git a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs index c7edf01499..822b8acaf0 100644 --- a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs +++ b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs @@ -92,13 +92,13 @@ namespace Content.Shared.SubFloor { UpdateFloorCover(uid, component); UpdateAppearance(uid, component); - EntityManager.EnsureComponent(uid); + EnsureComp(uid); } private void OnSubFloorTerminating(EntityUid uid, SubFloorHideComponent component, ComponentShutdown _) { // If component is being deleted don't need to worry about updating any component stuff because it won't matter very shortly. - if (EntityManager.GetComponent(uid).EntityLifeStage >= EntityLifeStage.Terminating) + if (Comp(uid).EntityLifeStage >= EntityLifeStage.Terminating) return; // Regardless of whether we're on a subfloor or not, unhide. diff --git a/Content.Shared/Tabletop/SharedTabletopSystem.cs b/Content.Shared/Tabletop/SharedTabletopSystem.cs index 03dd094805..ab6fe94fdb 100644 --- a/Content.Shared/Tabletop/SharedTabletopSystem.cs +++ b/Content.Shared/Tabletop/SharedTabletopSystem.cs @@ -39,7 +39,7 @@ namespace Content.Shared.Tabletop return; // Move the entity and dirty it (we use the map ID from the entity so noone can try to be funny and move the item to another map) - var transform = EntityManager.GetComponent(moved); + var transform = Comp(moved); Transforms.SetParent(moved, transform, _mapSystem.GetMapOrInvalid(transform.MapID)); Transforms.SetLocalPositionNoLerp(moved, msg.Coordinates.Position, transform); } diff --git a/Content.Shared/Throwing/ThrownItemSystem.cs b/Content.Shared/Throwing/ThrownItemSystem.cs index 3e1a4f18de..65c5a0f13e 100644 --- a/Content.Shared/Throwing/ThrownItemSystem.cs +++ b/Content.Shared/Throwing/ThrownItemSystem.cs @@ -47,7 +47,7 @@ namespace Content.Shared.Throwing private void ThrowItem(EntityUid uid, ThrownItemComponent component, ref ThrownEvent @event) { - if (!EntityManager.TryGetComponent(uid, out FixturesComponent? fixturesComponent) || + if (!TryComp(uid, out FixturesComponent? fixturesComponent) || fixturesComponent.Fixtures.Count != 1 || !TryComp(uid, out var body)) { @@ -86,7 +86,7 @@ namespace Content.Shared.Throwing private void HandlePullStarted(PullStartedMessage message) { // TODO: this isn't directed so things have to be done the bad way - if (EntityManager.TryGetComponent(message.PulledUid, out ThrownItemComponent? thrownItemComponent)) + if (TryComp(message.PulledUid, out ThrownItemComponent? thrownItemComponent)) StopThrow(message.PulledUid, thrownItemComponent); } @@ -100,7 +100,7 @@ namespace Content.Shared.Throwing _broadphase.RegenerateContacts((uid, physics)); } - if (EntityManager.TryGetComponent(uid, out FixturesComponent? manager)) + if (TryComp(uid, out FixturesComponent? manager)) { var fixture = _fixtures.GetFixtureOrNull(uid, ThrowingFixture, manager: manager); diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs b/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs index 595ef8a925..5a5d719dde 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.MultipleTool.cs @@ -22,7 +22,7 @@ public abstract partial class SharedToolSystem private void OnMultipleToolStartup(EntityUid uid, MultipleToolComponent multiple, ComponentStartup args) { // Only set the multiple tool if we have a tool component. - if (EntityManager.TryGetComponent(uid, out ToolComponent? tool)) + if (TryComp(uid, out ToolComponent? tool)) SetMultipleTool(uid, multiple, tool); } diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 953abc8107..bd44dcfd66 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -294,7 +294,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem { // Make sure the entity is a weapon AND it doesn't need // to be equipped to be used (E.g boxing gloves). - if (EntityManager.TryGetComponent(held, out melee) && + if (TryComp(held, out melee) && !melee.MustBeEquippedToUse) { weaponUid = held.Value; diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs index 38f427429f..29664c6eea 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs @@ -100,7 +100,7 @@ public partial class SharedGunSystem return false; // If it's a speedloader try to get ammo from it. - if (EntityManager.HasComponent(uid)) + if (HasComp(uid)) { var freeSlots = 0; diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index b6dee7fc1b..982b8c1345 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -121,7 +121,7 @@ public abstract partial class SharedGunSystem : EntitySystem if (melee.NextAttack > component.NextFire) { component.NextFire = melee.NextAttack; - EntityManager.DirtyField(uid, component, nameof(GunComponent.NextFire)); + DirtyField(uid, component, nameof(GunComponent.NextFire)); } } @@ -201,7 +201,7 @@ public abstract partial class SharedGunSystem : EntitySystem gun.ShotCounter = 0; gun.ShootCoordinates = null; gun.Target = null; - EntityManager.DirtyField(uid, gun, nameof(GunComponent.ShotCounter)); + DirtyField(uid, gun, nameof(GunComponent.ShotCounter)); } /// @@ -212,7 +212,7 @@ public abstract partial class SharedGunSystem : EntitySystem gun.ShootCoordinates = toCoordinates; AttemptShoot(user, gunUid, gun); gun.ShotCounter = 0; - EntityManager.DirtyField(gunUid, gun, nameof(GunComponent.ShotCounter)); + DirtyField(gunUid, gun, nameof(GunComponent.ShotCounter)); } /// @@ -281,7 +281,7 @@ public abstract partial class SharedGunSystem : EntitySystem } // NextFire has been touched regardless so need to dirty the gun. - EntityManager.DirtyField(gunUid, gun, nameof(GunComponent.NextFire)); + DirtyField(gunUid, gun, nameof(GunComponent.NextFire)); // Get how many shots we're actually allowed to make, due to clip size or otherwise. // Don't do this in the loop so we still reset NextFire. @@ -335,7 +335,7 @@ public abstract partial class SharedGunSystem : EntitySystem // Even if we don't actually shoot update the ShotCounter. This is to avoid spamming empty sounds // where the gun may be SemiAuto or Burst. gun.ShotCounter += shots; - EntityManager.DirtyField(gunUid, gun, nameof(GunComponent.ShotCounter)); + DirtyField(gunUid, gun, nameof(GunComponent.ShotCounter)); if (ev.Ammo.Count <= 0) { diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEApplyComponentsSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEApplyComponentsSystem.cs index e5bd33f581..e36729851f 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEApplyComponentsSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEApplyComponentsSystem.cs @@ -21,18 +21,18 @@ public sealed class XAEApplyComponentsSystem : BaseXAESystem Date: Fri, 27 Jun 2025 02:27:25 +0200 Subject: [PATCH 117/191] Cleanup SharedRoleCodewordSystem (#38310) * cleanup * Update Content.Shared/Roles/RoleCodeword/SharedRoleCodewordSystem.cs Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> * Apply suggestions from code review --------- Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> --- .../GameTicking/Rules/TraitorRuleSystem.cs | 6 +-- .../RoleCodeword/RoleCodewordComponent.cs | 3 -- .../RoleCodeword/SharedRoleCodewordSystem.cs | 44 ++----------------- 3 files changed, 6 insertions(+), 47 deletions(-) diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index bfe98de862..2d96cae861 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -133,11 +133,11 @@ public sealed class TraitorRuleSystem : GameRuleSystem Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - did not get traitor briefing"); } - // Send codewords to only the traitor client var color = TraitorCodewordColor; // Fall back to a dark red Syndicate color if a prototype is not found - RoleCodewordComponent codewordComp = EnsureComp(mindId); - _roleCodewordSystem.SetRoleCodewords(codewordComp, "traitor", factionCodewords.ToList(), color); + // The mind entity is stored in nullspace with a PVS override for the owner, so only they can see the codewords. + var codewordComp = EnsureComp(mindId); + _roleCodewordSystem.SetRoleCodewords((mindId, codewordComp), "traitor", factionCodewords.ToList(), color); // Change the faction Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Change faction"); diff --git a/Content.Shared/Roles/RoleCodeword/RoleCodewordComponent.cs b/Content.Shared/Roles/RoleCodeword/RoleCodewordComponent.cs index a1723dbc7e..222aec9bf7 100644 --- a/Content.Shared/Roles/RoleCodeword/RoleCodewordComponent.cs +++ b/Content.Shared/Roles/RoleCodeword/RoleCodewordComponent.cs @@ -1,5 +1,4 @@ using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Roles.RoleCodeword; @@ -16,8 +15,6 @@ public sealed partial class RoleCodewordComponent : Component /// [DataField, AutoNetworkedField] public Dictionary RoleCodewords = new(); - - public override bool SessionSpecific => true; } [DataDefinition, Serializable, NetSerializable] diff --git a/Content.Shared/Roles/RoleCodeword/SharedRoleCodewordSystem.cs b/Content.Shared/Roles/RoleCodeword/SharedRoleCodewordSystem.cs index 9f860715fb..345cfd4e21 100644 --- a/Content.Shared/Roles/RoleCodeword/SharedRoleCodewordSystem.cs +++ b/Content.Shared/Roles/RoleCodeword/SharedRoleCodewordSystem.cs @@ -1,49 +1,11 @@ -using Content.Shared.Mind; -using Robust.Shared.GameStates; -using Robust.Shared.Player; - namespace Content.Shared.Roles.RoleCodeword; public abstract class SharedRoleCodewordSystem : EntitySystem { - [Dependency] private readonly SharedMindSystem _mindSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnCodewordCompGetStateAttempt); - } - - /// - /// Determines if a codeword component should be sent to the client. - /// - private void OnCodewordCompGetStateAttempt(EntityUid uid, RoleCodewordComponent comp, ref ComponentGetStateAttemptEvent args) - { - args.Cancelled = !CanGetState(args.Player, comp); - } - - /// - /// The criteria that determine whether a codeword component should be sent to a client. - /// Sends the component if its owner is the player mind. - /// - /// The Player the component will be sent to. - /// The component being checked against - /// - private bool CanGetState(ICommonSession? player, RoleCodewordComponent comp) - { - if (!_mindSystem.TryGetMind(player, out EntityUid mindId, out var _)) - return false; - - if (!TryComp(mindId, out RoleCodewordComponent? playerComp) && comp != playerComp) - return false; - - return true; - } - - public void SetRoleCodewords(RoleCodewordComponent comp, string key, List codewords, Color color) + public void SetRoleCodewords(Entity ent, string key, List codewords, Color color) { var data = new CodewordsData(color, codewords); - comp.RoleCodewords[key] = data; + ent.Comp.RoleCodewords[key] = data; + Dirty(ent); } } From a1cf4903606c08928b2469357e876f14d491d5b1 Mon Sep 17 00:00:00 2001 From: Tiniest Shark Date: Thu, 26 Jun 2025 21:07:29 -0400 Subject: [PATCH 118/191] Toy/Plushie Inhands and Wearables (#38514) --- .../Entities/Objects/Fun/mech_figurines.yml | 218 +++++++++++++++++- .../Entities/Objects/Fun/plushies.yml | 105 ++++++++- .../Prototypes/Entities/Objects/Fun/toys.yml | 61 ++++- .../Ghosts/ghost_human.rsi/inhand-left.png | Bin 0 -> 518 bytes .../Ghosts/ghost_human.rsi/inhand-right.png | Bin 0 -> 523 bytes .../Mobs/Ghosts/ghost_human.rsi/meta.json | 24 +- .../Mobs/Ghosts/revenant.rsi/inhand-left.png | Bin 0 -> 923 bytes .../Mobs/Ghosts/revenant.rsi/inhand-right.png | Bin 0 -> 923 bytes .../Mobs/Ghosts/revenant.rsi/meta.json | 10 +- .../Objects/Fun/AI.rsi/equipped-HELMET.png | Bin 0 -> 417 bytes .../Objects/Fun/AI.rsi/inhand-left.png | Bin 0 -> 489 bytes .../Objects/Fun/AI.rsi/inhand-right.png | Bin 0 -> 490 bytes .../Textures/Objects/Fun/AI.rsi/meta.json | 14 +- .../Plushies/arachnid.rsi/equipped-HELMET.png | Bin 0 -> 874 bytes .../Fun/Plushies/arachnid.rsi/inhand-left.png | Bin 0 -> 680 bytes .../Plushies/arachnid.rsi/inhand-right.png | Bin 0 -> 673 bytes .../Fun/Plushies/arachnid.rsi/meta.json | 14 +- .../Plushies/atmosian.rsi/equipped-HELMET.png | Bin 0 -> 857 bytes .../Fun/Plushies/atmosian.rsi/inhand-left.png | Bin 0 -> 764 bytes .../Plushies/atmosian.rsi/inhand-right.png | Bin 0 -> 775 bytes .../Fun/Plushies/atmosian.rsi/meta.json | 14 +- .../Plushies/diona.rsi/equipped-HELMET.png | Bin 0 -> 1272 bytes .../Fun/Plushies/diona.rsi/inhand-left.png | Bin 0 -> 1190 bytes .../Fun/Plushies/diona.rsi/inhand-right.png | Bin 0 -> 1226 bytes .../Objects/Fun/Plushies/diona.rsi/meta.json | 14 +- .../Plushies/hampter.rsi/equipped-HELMET.png | Bin 0 -> 777 bytes .../Fun/Plushies/hampter.rsi/inhand-left.png | Bin 0 -> 699 bytes .../Fun/Plushies/hampter.rsi/inhand-right.png | Bin 0 -> 691 bytes .../Fun/Plushies/hampter.rsi/meta.json | 14 +- .../Plushies/human.rsi/equipped-HELMET.png | Bin 0 -> 752 bytes .../Fun/Plushies/human.rsi/inhand-left.png | Bin 0 -> 619 bytes .../Fun/Plushies/human.rsi/inhand-right.png | Bin 0 -> 634 bytes .../Objects/Fun/Plushies/human.rsi/meta.json | 14 +- .../Fun/Plushies/lamp.rsi/inhand-left.png | Bin 0 -> 586 bytes .../Fun/Plushies/lamp.rsi/inhand-right.png | Bin 0 -> 589 bytes .../Objects/Fun/Plushies/lamp.rsi/meta.json | 10 +- .../Fun/Plushies/moth.rsi/equipped-HELMET.png | Bin 0 -> 1004 bytes .../Fun/Plushies/moth.rsi/inhand-left.png | Bin 0 -> 971 bytes .../Fun/Plushies/moth.rsi/inhand-right.png | Bin 0 -> 948 bytes .../Objects/Fun/Plushies/moth.rsi/meta.json | 14 +- .../Fun/Plushies/narsie.rsi/equipped-NECK.png | Bin 0 -> 1604 bytes .../Fun/Plushies/narsie.rsi/inhand-left.png | Bin 0 -> 2001 bytes .../Fun/Plushies/narsie.rsi/inhand-right.png | Bin 0 -> 2001 bytes .../Objects/Fun/Plushies/narsie.rsi/meta.json | 14 +- .../Plushies/nukie.rsi/equipped-HELMET.png | Bin 0 -> 868 bytes .../Fun/Plushies/nukie.rsi/inhand-left.png | Bin 0 -> 864 bytes .../Fun/Plushies/nukie.rsi/inhand-right.png | Bin 0 -> 870 bytes .../Objects/Fun/Plushies/nukie.rsi/meta.json | 14 +- .../Plushies/penguin.rsi/equipped-HELMET.png | Bin 0 -> 494 bytes .../Fun/Plushies/penguin.rsi/inhand-left.png | Bin 0 -> 578 bytes .../Fun/Plushies/penguin.rsi/inhand-right.png | Bin 0 -> 579 bytes .../Fun/Plushies/penguin.rsi/meta.json | 14 +- .../Fun/Plushies/ratvar.rsi/equipped-NECK.png | Bin 0 -> 2224 bytes .../Fun/Plushies/ratvar.rsi/inhand-left.png | Bin 0 -> 2081 bytes .../Fun/Plushies/ratvar.rsi/inhand-right.png | Bin 0 -> 2081 bytes .../Objects/Fun/Plushies/ratvar.rsi/meta.json | 14 +- .../Plushies/rouny.rsi/equipped-HELMET.png | Bin 0 -> 983 bytes .../Fun/Plushies/rouny.rsi/inhand-left.png | Bin 0 -> 1136 bytes .../Fun/Plushies/rouny.rsi/inhand-right.png | Bin 0 -> 1136 bytes .../Objects/Fun/Plushies/rouny.rsi/meta.json | 14 +- .../Plushies/slime.rsi/equipped-HELMET.png | Bin 0 -> 654 bytes .../Fun/Plushies/slime.rsi/inhand-left.png | Bin 0 -> 539 bytes .../Fun/Plushies/slime.rsi/inhand-right.png | Bin 0 -> 516 bytes .../Objects/Fun/Plushies/slime.rsi/meta.json | 14 +- .../Plushies/snake.rsi/equipped-NECK-vox.png | Bin 0 -> 609 bytes .../Objects/Fun/Plushies/snake.rsi/meta.json | 4 + .../Plushies/vox.rsi/equipped-HELMET-vox.png | Bin 0 -> 976 bytes .../Fun/Plushies/vox.rsi/equipped-HELMET.png | Bin 0 -> 963 bytes .../Fun/Plushies/vox.rsi/inhand-left.png | Bin 0 -> 886 bytes .../Fun/Plushies/vox.rsi/inhand-right.png | Bin 0 -> 896 bytes .../Objects/Fun/Plushies/vox.rsi/meta.json | 18 +- .../Fun/Plushies/xeno.rsi/equipped-HELMET.png | Bin 0 -> 862 bytes .../Fun/Plushies/xeno.rsi/inhand-left.png | Bin 0 -> 873 bytes .../Fun/Plushies/xeno.rsi/inhand-right.png | Bin 0 -> 882 bytes .../Objects/Fun/Plushies/xeno.rsi/meta.json | 14 +- .../Fun/clownrecorder.rsi/inhand-left.png | Bin 0 -> 351 bytes .../Fun/clownrecorder.rsi/inhand-right.png | Bin 0 -> 399 bytes .../Objects/Fun/clownrecorder.rsi/meta.json | 16 +- .../mech_figurines.rsi/inhand-left-base.png | Bin 0 -> 189 bytes .../mech_figurines.rsi/inhand-left-body.png | Bin 0 -> 272 bytes .../mech_figurines.rsi/inhand-left-trim.png | Bin 0 -> 241 bytes .../mech_figurines.rsi/inhand-left-visor.png | Bin 0 -> 125 bytes .../mech_figurines.rsi/inhand-right-base.png | Bin 0 -> 179 bytes .../mech_figurines.rsi/inhand-right-body.png | Bin 0 -> 263 bytes .../mech_figurines.rsi/inhand-right-trim.png | Bin 0 -> 228 bytes .../mech_figurines.rsi/inhand-right-visor.png | Bin 0 -> 124 bytes .../Objects/Fun/mech_figurines.rsi/meta.json | 106 ++++++--- .../Fun/newton_cradle.rsi/inhand-left.png | Bin 0 -> 509 bytes .../Fun/newton_cradle.rsi/inhand-right.png | Bin 0 -> 510 bytes .../Objects/Fun/newton_cradle.rsi/meta.json | 88 +++++-- .../Fun/pequeno.rsi/equipped-HELMET-body.png | Bin 0 -> 446 bytes .../Fun/pequeno.rsi/equipped-HELMET-vis.png | Bin 0 -> 239 bytes .../Fun/pequeno.rsi/inhand-left-body.png | Bin 0 -> 405 bytes .../Fun/pequeno.rsi/inhand-left-vis.png | Bin 0 -> 228 bytes .../Fun/pequeno.rsi/inhand-right-body.png | Bin 0 -> 413 bytes .../Fun/pequeno.rsi/inhand-right-vis.png | Bin 0 -> 226 bytes .../Objects/Fun/pequeno.rsi/meta.json | 26 ++- .../Fun/rubber_chicken.rsi/inhand-left.png | Bin 0 -> 732 bytes .../Fun/rubber_chicken.rsi/inhand-right.png | Bin 0 -> 744 bytes .../Objects/Fun/rubber_chicken.rsi/meta.json | 10 +- .../Fun/toy_ian.rsi/equipped-HELMET.png | Bin 0 -> 413 bytes .../Objects/Fun/toy_ian.rsi/inhand-left.png | Bin 0 -> 370 bytes .../Objects/Fun/toy_ian.rsi/inhand-right.png | Bin 0 -> 378 bytes .../Objects/Fun/toy_ian.rsi/meta.json | 12 + .../Objects/Fun/toy_mouse.rsi/inhand-left.png | Bin 0 -> 424 bytes .../Fun/toy_mouse.rsi/inhand-right.png | Bin 0 -> 411 bytes .../Objects/Fun/toy_mouse.rsi/meta.json | 10 +- .../Fun/toy_nuke.rsi/equipped-HELMET.png | Bin 0 -> 766 bytes .../Objects/Fun/toy_nuke.rsi/inhand-left.png | Bin 0 -> 770 bytes .../Objects/Fun/toy_nuke.rsi/inhand-right.png | Bin 0 -> 727 bytes .../Objects/Fun/toy_nuke.rsi/meta.json | 14 +- .../Objects/Fun/toy_singularity.rsi/icon.png | Bin 11271 -> 11050 bytes .../Objects/Fun/toy_singularity.rsi/meta.json | 71 +++++- .../toy_singularity.rsi/singu-inhand-left.png | Bin 0 -> 2886 bytes .../singu-inhand-right.png | Bin 0 -> 2959 bytes .../Misc/Lights/lampgreen.rsi/meta.json | 2 +- .../Lights/lampgreen.rsi/off-inhand-left.png | Bin 544 -> 636 bytes .../Lights/lampgreen.rsi/off-inhand-right.png | Bin 525 -> 639 bytes .../Lights/lampgreen.rsi/on-inhand-left.png | Bin 403 -> 586 bytes .../Lights/lampgreen.rsi/on-inhand-right.png | Bin 403 -> 589 bytes .../Tesla/energy_miniball.rsi/meta.json | 66 +++++- .../energy_miniball.rsi/tesla-inhand-left.png | Bin 0 -> 1472 bytes .../tesla-inhand-right.png | Bin 0 -> 1425 bytes 123 files changed, 972 insertions(+), 95 deletions(-) create mode 100644 Resources/Textures/Mobs/Ghosts/ghost_human.rsi/inhand-left.png create mode 100644 Resources/Textures/Mobs/Ghosts/ghost_human.rsi/inhand-right.png create mode 100644 Resources/Textures/Mobs/Ghosts/revenant.rsi/inhand-left.png create mode 100644 Resources/Textures/Mobs/Ghosts/revenant.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/AI.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/AI.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/AI.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/arachnid.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/arachnid.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/arachnid.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/atmosian.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/atmosian.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/atmosian.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/diona.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/diona.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/diona.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/hampter.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/hampter.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/hampter.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/human.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/human.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/human.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/lamp.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/lamp.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/moth.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/moth.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/moth.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/narsie.rsi/equipped-NECK.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/narsie.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/narsie.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/nukie.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/nukie.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/nukie.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/penguin.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/penguin.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/penguin.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/ratvar.rsi/equipped-NECK.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/ratvar.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/ratvar.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/rouny.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/rouny.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/rouny.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/slime.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/slime.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/slime.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/snake.rsi/equipped-NECK-vox.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/vox.rsi/equipped-HELMET-vox.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/vox.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/vox.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/vox.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/xeno.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/xeno.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/Plushies/xeno.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/clownrecorder.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/clownrecorder.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-left-base.png create mode 100644 Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-left-body.png create mode 100644 Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-left-trim.png create mode 100644 Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-left-visor.png create mode 100644 Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-right-base.png create mode 100644 Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-right-body.png create mode 100644 Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-right-trim.png create mode 100644 Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-right-visor.png create mode 100644 Resources/Textures/Objects/Fun/newton_cradle.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/newton_cradle.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/pequeno.rsi/equipped-HELMET-body.png create mode 100644 Resources/Textures/Objects/Fun/pequeno.rsi/equipped-HELMET-vis.png create mode 100644 Resources/Textures/Objects/Fun/pequeno.rsi/inhand-left-body.png create mode 100644 Resources/Textures/Objects/Fun/pequeno.rsi/inhand-left-vis.png create mode 100644 Resources/Textures/Objects/Fun/pequeno.rsi/inhand-right-body.png create mode 100644 Resources/Textures/Objects/Fun/pequeno.rsi/inhand-right-vis.png create mode 100644 Resources/Textures/Objects/Fun/rubber_chicken.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/rubber_chicken.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/toy_ian.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/toy_ian.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/toy_ian.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/toy_mouse.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/toy_mouse.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/toy_nuke.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Objects/Fun/toy_nuke.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/toy_nuke.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Fun/toy_singularity.rsi/singu-inhand-left.png create mode 100644 Resources/Textures/Objects/Fun/toy_singularity.rsi/singu-inhand-right.png create mode 100644 Resources/Textures/Structures/Power/Generation/Tesla/energy_miniball.rsi/tesla-inhand-left.png create mode 100644 Resources/Textures/Structures/Power/Generation/Tesla/energy_miniball.rsi/tesla-inhand-right.png diff --git a/Resources/Prototypes/Entities/Objects/Fun/mech_figurines.yml b/Resources/Prototypes/Entities/Objects/Fun/mech_figurines.yml index 7cc6aaaeaf..f04fa19a04 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/mech_figurines.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/mech_figurines.yml @@ -17,7 +17,7 @@ - type: Tag tags: - Figurine - + #Ripley APLU - type: entity parent: BaseFigurineMech @@ -27,6 +27,24 @@ components: - type: Sprite state: ripley + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#d5c644" + - state: inhand-left-trim + color: "#524c14" + - state: inhand-left-visor + color: "#7aef83" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#d5c644" + - state: inhand-right-trim + color: "#524c14" + - state: inhand-right-visor + color: "#7aef83" #Fire Ripley - type: entity @@ -37,6 +55,24 @@ components: - type: Sprite state: fireripley + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#819a73" + - state: inhand-left-trim + color: "#486b2f" + - state: inhand-left-visor + color: "#5a150e" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#819a73" + - state: inhand-right-trim + color: "#486b2f" + - state: inhand-right-visor + color: "#5a150e" #Death Ripley - type: entity @@ -47,6 +83,24 @@ components: - type: Sprite state: deathripley + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#575757" + - state: inhand-left-trim + color: "#b3001b" + - state: inhand-left-visor + color: "#e1131e" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#575757" + - state: inhand-right-trim + color: "#b3001b" + - state: inhand-right-visor + color: "#e1131e" #Ggygax - type: entity @@ -57,6 +111,24 @@ components: - type: Sprite state: gygax + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#e76241" + - state: inhand-left-trim + color: "#4f5359" + - state: inhand-left-visor + color: "#00ff33" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#e76241" + - state: inhand-right-trim + color: "#4f5359" + - state: inhand-right-visor + color: "#00ff33" #Durand - type: entity @@ -67,6 +139,24 @@ components: - type: Sprite state: durand + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#a8abb3" + - state: inhand-left-trim + color: "#686d78" + - state: inhand-left-visor + color: "#8cc865" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#a8abb3" + - state: inhand-right-trim + color: "#686d78" + - state: inhand-right-visor + color: "#8cc865" #H.O.N.K - type: entity @@ -77,6 +167,24 @@ components: - type: Sprite state: honk + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#ed1c24" + - state: inhand-left-trim + color: "#ffffff" + - state: inhand-left-visor + color: "#ff040b" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#ed1c24" + - state: inhand-right-trim + color: "#ffffff" + - state: inhand-right-visor + color: "#ff040b" #Marauder - type: entity @@ -87,6 +195,24 @@ components: - type: Sprite state: marauder + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#9da6be" + - state: inhand-left-trim + color: "#557a48" + - state: inhand-left-visor + color: "#00ff33" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#9da6be" + - state: inhand-right-trim + color: "#557a48" + - state: inhand-right-visor + color: "#00ff33" #Seraph - type: entity @@ -97,6 +223,24 @@ components: - type: Sprite state: seraph + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#878c97" + - state: inhand-left-trim + color: "#943226" + - state: inhand-left-visor + color: "#f31a33" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#878c97" + - state: inhand-right-trim + color: "#943226" + - state: inhand-right-visor + color: "#f31a33" #Mauler - type: entity @@ -107,6 +251,24 @@ components: - type: Sprite state: mauler + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#2b2b2b" + - state: inhand-left-trim + color: "#660000" + - state: inhand-left-visor + color: "#ff0000" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#2b2b2b" + - state: inhand-right-trim + color: "#660000" + - state: inhand-right-visor + color: "#ff0000" #Odysseus - type: entity @@ -117,6 +279,24 @@ components: - type: Sprite state: odysseus + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#ffffff" + - state: inhand-left-trim + color: "#7d7f97" + - state: inhand-left-visor + color: "#857149" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#ffffff" + - state: inhand-right-trim + color: "#7d7f97" + - state: inhand-right-visor + color: "#857149" #Phazon - type: entity @@ -127,6 +307,24 @@ components: - type: Sprite state: phazon + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#4d79a0" + - state: inhand-left-trim + color: "#f38233" + - state: inhand-left-visor + color: "#f38233" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#4d79a0" + - state: inhand-right-trim + color: "#f38233" + - state: inhand-right-visor + color: "#f38233" #Reticence - type: entity @@ -137,3 +335,21 @@ components: - type: Sprite state: reticence + - type: Item + inhandVisuals: + left: + - state: inhand-left-base + - state: inhand-left-body + color: "#d9d9d9" + - state: inhand-left-trim + color: "#8b8b8b" + - state: inhand-left-visor + color: "#1f1f1f" + right: + - state: inhand-right-base + - state: inhand-right-body + color: "#d9d9d9" + - state: inhand-right-trim + color: "#8b8b8b" + - state: inhand-right-visor + color: "#1f1f1f" diff --git a/Resources/Prototypes/Entities/Objects/Fun/plushies.yml b/Resources/Prototypes/Entities/Objects/Fun/plushies.yml index f29841ea08..f0ab2931c0 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/plushies.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/plushies.yml @@ -127,6 +127,7 @@ noRot: true - type: Item size: Normal + - type: MultiHandedItem - type: Construction graph: PlushieGhostRevenant node: plushie @@ -202,6 +203,15 @@ wideAnimationRotation: 180 soundHit: path: /Audio/Items/Toys/mousesqueek.ogg + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/hampter.rsi + slots: + - HEAD + clothingVisuals: + head: + - state: equipped-HELMET + offset: "0, 0.04" - type: FoodSequenceElement entries: CottonBurger: HamptrPlushie @@ -240,6 +250,15 @@ - type: Sprite sprite: Objects/Fun/Plushies/nukie.rsi state: icon + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/nukie.rsi + slots: + - HEAD + clothingVisuals: + head: + - state: equipped-HELMET + offset: "0, 0.03" - type: FoodSequenceElement entries: CottonBurger: NukiePlushie @@ -254,6 +273,15 @@ sprite: Objects/Fun/Plushies/rouny.rsi state: icon - type: Rotatable + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/rouny.rsi + slots: + - HEAD + clothingVisuals: + head: + - state: equipped-HELMET + offset: "0, 0.03" - type: FoodSequenceElement entries: CottonBurger: RounyPlushie @@ -307,6 +335,11 @@ wideAnimationRotation: 180 soundHit: path: /Audio/Voice/Arachnid/arachnid_laugh.ogg + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/arachnid.rsi + slots: + - HEAD - type: FoodSequenceElement entries: CottonBurger: ArachnidPlushie @@ -650,6 +683,15 @@ - LowImpassable - type: CollisionWake enabled: false + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/diona.rsi + slots: + - HEAD + clothingVisuals: + head: + - state: equipped-HELMET + offset: "0, 0.09" - type: FoodSequenceElement entries: CottonBurger: DionaPlushie @@ -739,7 +781,7 @@ parent: BasePlushie id: PlushieRatvar name: ratvar plushie - description: A small stuffed doll of the elder god Ratvar. + description: A large stuffed doll of the elder god Ratvar. components: - type: Sprite sprite: Objects/Fun/Plushies/ratvar.rsi @@ -747,6 +789,12 @@ - type: Item size: Normal sprite: Objects/Fun/Plushies/ratvar.rsi + - type: MultiHandedItem + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/ratvar.rsi + slots: + - neck - type: FoodSequenceElement entries: CottonBurger: RatvarPlushie @@ -755,7 +803,7 @@ parent: BasePlushie id: PlushieNar name: nar'sie plushie - description: A small stuffed doll of the elder goddess Nar'Sie. + description: A large stuffed doll of the elder goddess Nar'Sie. components: - type: Sprite sprite: Objects/Fun/Plushies/narsie.rsi @@ -763,6 +811,12 @@ - type: Item size: Normal sprite: Objects/Fun/Plushies/narsie.rsi + - type: MultiHandedItem + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/narsie.rsi + slots: + - neck - type: FoodSequenceElement entries: CottonBurger: NarPlushie @@ -885,6 +939,11 @@ wideAnimationRotation: 180 soundHit: path: /Audio/Voice/Slime/slime_squish.ogg + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/slime.rsi + slots: + - HEAD - type: FoodSequenceElement entries: CottonBurger: SlimePlushie @@ -1007,6 +1066,15 @@ wideAnimationRotation: 180 soundHit: path: /Audio/Voice/Vox/shriek1.ogg + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/vox.rsi + slots: + - HEAD + clothingVisuals: + head: + - state: equipped-HELMET + offset: "0, 0.09" - type: FoodSequenceElement entries: CottonBurger: VoxPlushie @@ -1020,6 +1088,15 @@ - type: Sprite sprite: Objects/Fun/Plushies/atmosian.rsi state: icon + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/atmosian.rsi + slots: + - HEAD + clothingVisuals: + head: + - state: equipped-HELMET + offset: "0, 0.04" - type: FoodSequenceElement entries: CottonBurger: AtmosianPlushie @@ -1058,6 +1135,15 @@ wideAnimationRotation: 180 soundHit: path: /Audio/Weapons/Xeno/alien_spitacid.ogg + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/xeno.rsi + slots: + - HEAD + clothingVisuals: + head: + - state: equipped-HELMET + offset: "0, 0.03" - type: FoodSequenceElement entries: CottonBurger: XenoPlushie @@ -1071,6 +1157,11 @@ - type: Sprite sprite: Objects/Fun/Plushies/penguin.rsi state: icon + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/penguin.rsi + slots: + - HEAD - type: FoodSequenceElement entries: CottonBurger: PenguinPlushie @@ -1106,6 +1197,11 @@ - type: EmitSoundOnTrigger sound: path: /Audio/Voice/Human/malescream_5.ogg + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/human.rsi + slots: + - HEAD - type: FoodSequenceElement entries: CottonBurger: HumanPlushie @@ -1141,6 +1237,11 @@ requiresSpecialDigestion: true useSound: path: /Audio/Voice/Moth/moth_chitter.ogg + - type: Clothing + quickEquip: false + sprite: Objects/Fun/Plushies/moth.rsi + slots: + - HEAD - type: FoodSequenceElement entries: CottonBurger: MothPlushie diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index 495d044f01..4daffe1c56 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -155,6 +155,15 @@ - type: Sprite sprite: Objects/Fun/AI.rsi state: icon + - type: Clothing + quickEquip: false + sprite: Objects/Fun/AI.rsi + slots: + - HEAD + clothingVisuals: + head: + - state: equipped-HELMET + offset: "0, 0.06" - type: entity parent: BaseFigurineCheapo @@ -165,6 +174,15 @@ - type: Sprite sprite: Objects/Fun/toy_nuke.rsi state: icon + - type: Clothing + quickEquip: false + sprite: Objects/Fun/toy_nuke.rsi + slots: + - HEAD + clothingVisuals: + head: + - state: equipped-HELMET + offset: "0, 0.12" - type: Tag tags: - Payload @@ -198,6 +216,15 @@ - type: Sprite sprite: Objects/Fun/toy_ian.rsi state: icon + - type: Clothing + quickEquip: false + sprite: Objects/Fun/toy_ian.rsi + slots: + - HEAD + clothingVisuals: + head: + - state: equipped-HELMET + offset: "0, 0.06" - type: Item size: Tiny - type: EmitSoundOnUse @@ -478,11 +505,17 @@ sprite: Objects/Fun/toy_singularity.rsi state: icon - type: SingularityDistortion - intensity: 2000 + intensity: 25 falloffPower: 2.6 - type: Item size: Normal - sprite: Objects/Fun/icon.rsi + inhandVisuals: + left: + - state: singu-inhand-left + shader: unshaded + right: + - state: singu-inhand-right + shader: unshaded - type: entity parent: BaseItem @@ -495,6 +528,14 @@ shader: unshaded layers: - state: tesla_projectile + - type: Item + inhandVisuals: + left: + - state: tesla-inhand-left + shader: unshaded + right: + - state: tesla-inhand-right + shader: unshaded - type: PointLight enabled: true radius: 5 @@ -632,6 +673,22 @@ - state: base map: [ "enum.DamageStateVisualLayers.Base" ] - state: visor + - type: Clothing + sprite: Objects/Fun/pequeno.rsi + slots: + - HEAD + clothingVisuals: + head: + - state: equipped-HELMET-body + - state: equipped-HELMET-vis + - type: Item + inhandVisuals: + left: + - state: inhand-left-body + - state: inhand-left-vis + right: + - state: inhand-right-body + - state: inhand-right-vis - type: RandomSprite available: - enum.DamageStateVisualLayers.Base: diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/inhand-left.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..c29cb7ca649acb17dec00e4cffc33d9017ced4e1 GIT binary patch literal 518 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/AI.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/AI.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..3b91539d8eabc83a94caf8ce9599a14e6445117f GIT binary patch literal 490 zcmVPx$q)9|URCt{2+ObOlVHgJR=QOY;!Qmju(WSNNDsO14B z=r6FLe?SyzGl!54YHJWZxFu=&8jeJ(J5If??(F$3dVJjRe)m1Pye~jRL_|bHME{TC zwot3pgxi{~>#p}j&4SxPp&iHe&N^&MAemBNTLPWl0DZIdoApiP>!JBMDAyMN0JbG? zoPEcInQ%U_goZh5ney!A^TY3!ZTq^%^96Kz1839CCIAh|)Z8S0DCV{Jcy|K;*w8cp zP9o-wX9D(*4p1qU#;)g$Yo@E!s_StZh7ro&LRwXaHNSSdzG@8RMy54-Xkk4hA|fIp zA|fIpA|fLCJE3nLOw;n7glBIs4Cpp55Uvuq}bICa=ty5+FOC zpRW6hG9C+H#suiPt`ypFoDbGe&*XxtX-qPujJ4>47D%?rT1?vTM5S1gI+qa0pP&;_ zIWORBYZ-XB#p()R`0V*hwU|f@cbZmJv3c1L*~2Sw`XVAZKQmPVMkg_V@1rzPGI6A$ gI66Z_L`3wH4@A&=#$qnXivR!s07*qoM6N<$f{Ci%pa1{> literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/AI.rsi/meta.json b/Resources/Textures/Objects/Fun/AI.rsi/meta.json index e5ddb0f4d7..5312d061af 100644 --- a/Resources/Textures/Objects/Fun/AI.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/AI.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-NC-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432, inhand and helmet sprites by TiniestShark (github)", "size": { "x": 32, "y": 32 @@ -9,6 +9,18 @@ "states": [ { "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/Plushies/arachnid.rsi/equipped-HELMET.png b/Resources/Textures/Objects/Fun/Plushies/arachnid.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..885e8484fec3dce45c75b961ef74e9bc520a6c76 GIT binary patch literal 874 zcmV-w1C{)VP)Px&BuPX;RCt{2nmte3P!xt=R}E>4lnh3#5KM`Rz=K5xM*aZu&VmF3>c+&*z|O=* z8QB@gz|@5wfaJkQMfO@Hss~$v_+xN~^f=cvPHZ*Y$sbqO=bU@4@0HIhp&*K? zMRacHEvaZf^M3!EqA!=1mO%#@BN{bL(%xT$Tmev2EmBl1y1umhnb8#5rzROm4(E0Q3_1~(fMK5)1bQ8OawU$ zgZLQLZXCI%Kw6qpcI4?VE7iqj)o!dkN=@?get~1ImKDv<&;#Px#(*eEr`*yU}s? z7=~dOhG7_nVHk#C7=|g%Yy97q)#hBKFC41^P-vUY4`sM~cAh5`0>4{&mJK0%YG!^zn> z_ID2uh5@==XKedSr7_>{0RT>qkMOcKUnX3~#_919fPu~97#RRVZ%I^s^8FrM#~xam zE(@d^Am_6bOj83uDhmn%1;TY~nC8Retez02`4DF(*-OrkQj82;21Th!v$>HNb=R@s zKbcZlmc^tUef5a=%rqZBz6?{249x&hl0qGl7=c!SGED{1CQ&p|tIL06HAt1sjGY1i z;tolX<#`S|ZENnM5A;XA)3)Gw{VzV*C@M!rO@W~fi0Sfjv^#AJA3rtXbL+?5T!YY9 z+m1wg=o}cR$AF|yA|s~&(2ua|*l-;iowkLQ)%AXxjLXa{AX`>e*U@QPAbZ6OO`8I_ zN3XHA9jDX0N1rI8O$@^@48t%C!!QiPFictb4K880?=zh1%>V!Z07*qoM6N<$g6?vZ Ae*gdg literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/arachnid.rsi/inhand-left.png b/Resources/Textures/Objects/Fun/Plushies/arachnid.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..1f13d528eb0546e0badec682c8a3acf661161287 GIT binary patch literal 680 zcmV;Z0$2TsP)Px%Vo5|nRCt{2+OccfKokb>Po0-S0`2d1W{ymZasb>PL3O1yLnKL z^!GH~@@BkHG*Ey4Qd*qS-T(kNIc^|#chPHWx+Shkf~H%-{~Lb-Ww<4?^ABG>4Yk#o zxuMw|rRkPXXXa#vbcxgitW{R9wY}rD-+X-QU8{HQi*eUUlez?2ts}g8{tN)HwY`I< zt4{zp1GWb(#T~f+8UXtJQ_Rn2WrpUFx(Nc#V9lj6`u$Th5BFg%mC@^VE^UbJ+V6Ec zaF>C7aUOZ&j7#bg0G!$FbvtMt?&DFV=6xTu1PVn1_QiQ3g9j2wfXhF4;NNTSE(ia; zLeWSntYi|xwI}a*_?Cl?{i7aK9Im3;FrJm zPyj$a{{XiAd+463&Z6D^0RU+K{FZ23L_|bHL_|bHL_{}E+&TiOM`KBfTSxF;{rfpi zJ;KxuM#7H0lj7DRxSuF7O)UYVwE)*)(s~47!UTk!=Q}DS`XtU!7!`By$*|cYD*w3qSPb!MNYuDbNn<5 zsz(@oE^-3==lE$CR*x|HY~&d=%&N1IsI&O~_4SQ-6UNmeH1^&N#r_8<*aedZCAqTz O0000Px%TS-JgRCt{2+A(X}Koke?zf3`C9PEIz7`!xY78L}dU<^e|AmBlTmV#TncnJ9f zU7Dd&hi1u|&(Ic13pJ%vwhl2AL!j=EDqRd(Y-mI5TGMrC@3>g9t;CM3EAIye>wJ>l zJL%5DJ%A8G2qAhl2^YdTgyPUI_4}9)7j}Bqnxr>#RESg7$@cXWk3m_*? zYkxREZY_^q*Aok3U1K-GGIai-P>j8e5; zV4O$;FuJ3vYP~?1)U*zK&yatTP%kk0KxQ#<0FqiSFwYo52O#P70+V8pW4{u90XUVH)#?Q<`;Y^i4^l6Tnq%|JiT7T00000NkvXX Hu0mjf1c5bK literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/arachnid.rsi/meta.json b/Resources/Textures/Objects/Fun/Plushies/arachnid.rsi/meta.json index c26c4da7ce..cbd97acd20 100644 --- a/Resources/Textures/Objects/Fun/Plushies/arachnid.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/Plushies/arachnid.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-NC-SA-3.0", - "copyright": "Sprite by PixelTheKermit (github)", + "copyright": "Sprite by PixelTheKermit (github), inhand and helmet sprites by TiniestShark (github)", "size": { "x": 32, "y": 32 @@ -9,6 +9,18 @@ "states": [ { "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/Plushies/atmosian.rsi/equipped-HELMET.png b/Resources/Textures/Objects/Fun/Plushies/atmosian.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..9dfdff7d096df223b3fb81faf9d1acad683e5790 GIT binary patch literal 857 zcmV-f1E&0mP)Px&6G=otRCt{2+CNAeQ5*;G?-ikNgR#bdM+K$AEf(ymMGorVP|P595X2#Ep$RWgq9 zl}jhV-+$}^0Q@@mgS#EK0RS&w?817YK3))ZO6LcMrSfq{SP#4kl{<|Ock!sDX#S`r)cr6!N zJkehJAD^8XP(l zB9RDO*9Bt?j^hM6mzHV=S1*h)Fvcn#=SEIIu~-BEFvc{)Fe=)3l^=u6z%zY`b$PxV-{iy|I>K`OrEh_S#ZLI6(+Ks90054*m(`=K5fMwMX9WJ}ba^6VNUeZ) zq8%H@-RS=M9eDuxyaAx{yj->X_<4jcYX*Qkkk4ZS-H0au;9@07VCf{dZ=W>NG zBCaB821HE04W*(j={?~28xuoAL#Ia>#Ikb;7d|ToTy1AH10V3}-BSQSCX)f+NcC6B z4tt)5P$&cdSb8?o@C^_+Ey`G#foYl?I4hWtr8EJq>naUiZ=UBV$0$7krxh5SF$Tjh z;5c$itX>SmKrWX90HtaXrTPHJaWqk!(2R=3qH?KBpaKcUabQ_i!=C_xWm&x0RwN;W j5JCtcgb+dqAx-Kpt~+b;jvo0p00000NkvXXu0mjf3x9ml literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/atmosian.rsi/inhand-left.png b/Resources/Textures/Objects/Fun/Plushies/atmosian.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..903b2e68b857ad23d563eaa4c840ae647667769d GIT binary patch literal 764 zcmVPx%wn;=mRCt{2+A&C5K^O<{za>+|#$tt@v=k&9looUoQ|v(<3mqhL0=`j%ZYi`b zrGp)%AQY;g2n8vjC@uwsI2fF2ThrkYG8G=EAvIuzB77afOJlUOdHGHw{eFv&C;8{@ zyL*qocYqK=2qA>{KXq`1rIL#L>sJdNTehFenaM7dRFvBY)9^qqUyW-|_4W_DbczxtEI8nv5~Dyu3`t7}8p^KC|(7yuE)6DWym$ zMaCHNuQ~I%3(C%)I8ZHyF@|Np^|?X_A%qY@2qAY;2d?Xm8U|fnu>COh4bR-Miis4paozM*sjGn=l@Yjicw@Q)|z^ ze+Z*=SU>y ulm+mwySln+_g&Y$baN3x2qA>HP`?0d#1{>-;2pF80000Px%!AV3xRCt{2nlVTlQ5eVnDVY>np$I)GSR@=31)VBJ=!0gnEwnacNg!9~AkOJx zhti=PUAie)ghG%E5yuD-lHit>W;s&nARO3)XhJguz7Fx}2~Bc|@APcH-;$S?cklk+ z_uji4{6BzU7=~dOhWS77IT;j>Y9!kq*DXiaToqEQGM(|LM#Y^0nw=V^;?4lsli^jA z>9hw=zxV&iB;4pWpj>GnzEnjpID`0773E4pjY5~1c+_xo1lkS?hwgUG-e&s1)6WAL zLnoab0}w)x5wb8hJp=$48e712at*6#4Vg^F)4@BX>k+ta7>3c)^U2@o$JQzf0<2a? z!TYOUDwTQ~-*yKqM}kxmr5C{a$eeb5TZ2}If zHN^5O0DxQhZ|6Q^`4t>iYZp^`NnBW`uInU(phO};LI~1z-IFUX35UaK^=X<$nx>Hu zf@E9i4HTn%fn?x7D;YuvPbUDlJ~|nOVHk#C7=~dOhG7_{3vQUQo;5h^%uWr{!)ZSv z(PIG4jZY_ZbcQ-{nMD)1+#Z6#8O%RX?@afVfOu3Rxf(4r+ATDZ|N6_7L`ZACdhExu zS52k&=s5v0czd0|Zeb9)+#Y;`V=A=)IJ3AnzT7$KIZ!%(Upt^&X`p(1cBw3&a0BV> zSH6Fs?c^FZ(`x7MX**qI(Zr9`1IiYT5P54}-4T$%-4k=9Kc5ExysWL`@j?u@C*QdG zAj}qyu$tBYm`dmE>j?bJWPPE){nl<61&;QUN=5yR(Qy-7FHGE=aQ#-jA%vjM@7!~Y zrN^i1I`vxp;e-p9$ZUPeV02vVwzOlKCOmC)8isLNGvJ0$Y00}~%lG{z2mr62PEzxv zErcM`RKF7}%iXukz9+XABGl{k(|)%FGYrEp48t&r_ygn1BS9a9tf2q^002ovPDHLk FV1fvfUqt`_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/atmosian.rsi/meta.json b/Resources/Textures/Objects/Fun/Plushies/atmosian.rsi/meta.json index e5ddb0f4d7..5312d061af 100644 --- a/Resources/Textures/Objects/Fun/Plushies/atmosian.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/Plushies/atmosian.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-NC-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432, inhand and helmet sprites by TiniestShark (github)", "size": { "x": 32, "y": 32 @@ -9,6 +9,18 @@ "states": [ { "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/Plushies/diona.rsi/equipped-HELMET.png b/Resources/Textures/Objects/Fun/Plushies/diona.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..9caf99231418b6aada89fad8747c7d032de69504 GIT binary patch literal 1272 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 Ef~CqZVgLXD literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/diona.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/Plushies/diona.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..015a920f336f4c58eee1bc2cf1a63b97807972dd GIT binary patch literal 1226 zcmV;*1U37KP)Px(gh@m}RCt{2+HXi3XB-Fc?=^ZRxkMAef2jetk7U@MIgM-Se8X&jfQKZG=pK!z7|KM&l! zm12_l0#-azu-Z(j4OW(b@cb%cwVANunL;vOzzZFA06=qd6C_Cn0Cby;xOwBgZhL^j zYBNE|3AN1u{li_P^JF`Q*fzAFEQayo+tV0H2xNJN8y**ME~> zE{AQtfL5EaK_S9_YVb} zM46~3M!iImWPJJKJ=J(sCqYS)@fz2Pg-6M~NziDqLz3jG|6Clk(p5^5Wb8co+eJ;; z5Kt%=Mi%2tTh|qs#+O(TItKsj>uJHcmYK82ckbjaOw1m@G|VdCu>x50wRlXW^QSNS!N8_!I3@zp~8OL8?QK;dOryxHfdM=WI|Nyfyeml!Q}#Pl==lybWq`tP+kFnzr9OUdkN`j`m4MMw+)j5;Jzj5WUIu~Ug9Ct}#fogc ofR^T_1MBe=MNt$*QIt;n3%>^74;iB{UjP6A07*qoM6N<$g4Px%!%0LzRCt{2+A(MoVH5}O|49gDN~MEJsZgnkMQM=I2A9w}938|;Ndg@L7V0S2 z7Lz5MA&V7Sic`r@u%M7iD49BCaEUYq5tK@_7_gij+F(M5E8*aCNbfT$)ccO)F2egk zAjxs}-uLdyWq4151?g0jZGSBD&!4`0#rkGZvT`k_;|rgZPDPoj?(xrcO$PwH`|#P8 zBD6aGDbe8Non1V9{2a51H2^?#;RRw(U*g8~%Lt7RG0_A>;~R^wYdT-NcBJFNx$~Cd zn=U{hpJl#45MvWzNRk9umH~jjADajxxxCW0A{6pj_V&#-fM0jr?QsDByk|}W0Fuiq zDCDz3@r8DPVM&QV_Mb&v)A8id6#Q4b{MdXvZsn4u%)}IY|Mmkw{Al1@cgW+R8Imo5T;tv$h$C=WsmXbehB-o1tJ{e{-&l}<(3 zk`iG{N`x`S7yxDjcG7VUj4{UZOQ)iy^D@;P{&O&)c|M>M$g&Jcl6ae3Rrg@F2&Q_2 znT;I$!vm;mI<%w4OaRbmPj`aR!ETHWPVs!)9v3njIaBAFZjKt&9k|v9Um(c4v0KS$ zHWFWTzhT%vB3lZwTjx_dBsj1Lg?h&2qAPx%bxA})RCt{2+COL$aTEve?@1Azq8x~&CF1*r?TU>cnvXps-1>(gyvzb}*Dnd2$^=L_`3nXlH|M*AZl; z4eB+Pl{V<^wTooutd?tvcDCmjVeIKQfa?f4dHym0K+`msrrEJwSXhMXh%Fc3I)Vxd zivSSZNxqF*z2B*J)%PPnRRLJH002ch3!pLC+36|N*6Xe3g`jG|?fSk0;)yW;KujOO zIdd|Ycx}Co3stySjf^YG|q(&C2FF90C%GuWJjX1y-HTdaXf0MPgcKP`WNHjx2U1>%V@9GJWl zX$=3R=_k*11cgoHKwGQK)BpAvV~jDz7-Nhv#u#Iau^qt9Ssj(#UBxept4@NSXe(_{ zxs;DQR^P?ZcFkpRRqUKa4<0Y!{-ZL^j3?lEtB9He=?R#foPx%ZAnByRCt{2+COL$aTEve?}2uzXX#Re2HUA*a1;{gaFbKk1CNec35tRcqNq@DxDXDW!}*JMiFcUxuH51KK;ZuV z?)~2V8S);$7-Nhv#u#J&At`8Qty-qT*B^W9Z{K}D{pCU6IdQVOvOu3cw*zB7>$js; zEmN!2M(V-{0HABxxPEmC04OO*7#knO*!U|p=;Tmb@2rI#XRr| zP(ugQhX8rXsb*L;uv>xaQKbP^N&@x29V=#b_MaBs`qEF|JX*lZpU;KV%Devk8u zk34bvPQsl8wM7>i)T4A4FK zC>$#R05Ey^5|s2se1X$U;jYRi$*}_#jR!*1eFL#fuxaQdeoQz^5*~m002ovPDHLkV1naWKfwS1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/hampter.rsi/meta.json b/Resources/Textures/Objects/Fun/Plushies/hampter.rsi/meta.json index ddeb50c744..0f99a998c3 100644 --- a/Resources/Textures/Objects/Fun/Plushies/hampter.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/Plushies/hampter.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-NC-SA-3.0", - "copyright": "Sprite by RenLou#4333", + "copyright": "Sprite by RenLou#4333, inhand and helmet sprites by TiniestShark (github)", "size": { "x": 32, "y": 32 @@ -9,6 +9,18 @@ "states": [ { "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/Plushies/human.rsi/equipped-HELMET.png b/Resources/Textures/Objects/Fun/Plushies/human.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..944db83589c469594d36e1718cae684fa3edefc2 GIT binary patch literal 752 zcmVPx%s!2paRCt{2nlWqJKorOSvQ(rHFgTN)jsXvGO^1fkp-Z7&izg2uUm#@6Xfl<4 zhJFD}o{ZPz1B8asq1EEOMw0{uSrV3V9h8%$lv>i+vSoO`$&!-0ckli0J-Bxg1ab8~ z*6Ibsu z<>i}re)#rU^9w29;be%~hnuCxzu8+^JVy>agt7(z2pmgn5l<9W007iJeo4tqdV96? z9Iz>J8~%7VWZ~Sxi%(zw`#+1xB~zNON_U8>_nF%4ELHIPyKo}Rt)6gh!Lw&8|HiXt z&_t}rK{5bJy#?Pi;%`d51wbqSdn4V&yM zTQd(uUA@n=?jSyO-S%C8B8c1@So!h!;sYl-d0nR&Sr+`>001b5N6|~Q0q^H%rLEbW zp{K>4{kQ@U_2#RJ9C~;+y@>;rr&o}xH5ND)?%SUshaOI^M{5~bCa2dUJe&*xEY+Gt zri{6?c;rQ!klTPaP5~g4wOm_Os0#4ssyHHPPdQbf-HRpV>WDLK6)Hk9^Se4yiEtxT zfDgz~4v*r-&N8Px%B}qg&xhC)W#{*oG4EE@B zA2SUB2u_-kp`8&;K<~~=?!S10nT8xc=;V%<{P%ynYuUejH!INu0DO4$6kxUz#6(|Y zW>Fu#_*A3?l2+^e>@_w2QF z*rm)g%+-q^o&cDa$X$yQ-K(}Tm*d5^fEo6It2qQ05}mPsKzx#MCx;pK*WwU#vIUZj zQm)LJvdVR$@c^$}PhExw2MM_Tc)wsS&Hyq^D2;_gXWRl#R)7i5iN*sLp@Df~&BYns zMiIYM;p?aOnfegc;%V(qaRU5O1z4Y_PNkGmN-4EUegdgf-=|8vD&7D9002ovPDHLk FV1nLCB@F-o literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/human.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/Plushies/human.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..e08a9977b1793784cf48fdf79438005d71997361 GIT binary patch literal 634 zcmV-=0)_pFP)Px%G)Y83RCt{2+P`ZPVI0Tt_sNMKl~U71nz}d?BsfS8adps6LjHhEf>Vczd#4Vr zX2{w>@(09K2f?Y776)~yv{M=!niFE%#K zgP`XD;C@(3DYX1ZOu+Wd=iGVrilFDQd$&G~m?rZ3-Ddxi9i~zf69Dk>#bbbeCFGj_ zZKUCL+?P(ZY0cVAFz^mX8voD5cs18Ji_2WOQNo&E0>E_J{QCMCztf_yzMJbrguI$- z;$j_m33xTvERhJ?Y+BZWkYY_M4r$Ns2Z0CfNJ*8h~q0-dF%9TR(DN_Tl1BwjP-< zei0E75fKp)5fPD4Zr|>n7wu4`*_|3|qG`9w{6@uO^-4bWwRlo6XU7<7Gs2{@XwDAR z9Udg;c~jjlaQo@YOy(Uc0pUNfy|S5EVCRJ``m~K#0g)bjR)Ix1)ut7AhXhA^sn0z# zH$l5t2H^68jZ}YYnRbZj8w;voIr2g;2 z7zrq>?^@r#yvJEw#-1w>93KE=x?#fav_{_gGOUR*8kWA_X#vd4rU?-d5fKsi0~k%( UR*O-q?f?J)07*qoM6N<$f|>R!ApigX literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/human.rsi/meta.json b/Resources/Textures/Objects/Fun/Plushies/human.rsi/meta.json index 23d0f71a20..ad2be4bd40 100644 --- a/Resources/Textures/Objects/Fun/Plushies/human.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/Plushies/human.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-NC-SA-3.0", - "copyright": "Sprite by TheShuEd", + "copyright": "Sprite by TheShuEd, inhand and helmet sprites by TiniestShark (github)", "size": { "x": 32, "y": 32 @@ -9,6 +9,18 @@ "states": [ { "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/Plushies/lamp.rsi/inhand-left.png b/Resources/Textures/Objects/Fun/Plushies/lamp.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..02153a45dc235bb7f8f5d3090a70e5e9a4998f62 GIT binary patch literal 586 zcmV-Q0=4~#P)Px%1W80eRCt{2+ObZ;P!tB>zeYC`lNuA|#<2Jf$jlQk`yQS2CF&F43otlAaUu>b zE(|c>&^UB6CTemV=!HsJ>22?YHu=A;p*`GtXv=VZfQX2Qh=|A(9|9jzn>u^tL+}K_ z0)n8z;)A4yWk*)3z5;jzyqn41UMMU(PklK7>8vv}D5Dr{X3N>MS^Hts-8mznnW+NBvZj8ypIq(xgZpN4o z=fD+^`BwKKxY``fhjWByu8pPbPdIQT{Z4?7)p1S(qA2XEX+SOYr! z*xBB^qBivW@|kVm20Nvn~-45fKp)5fKqlb&Z_qn`9yz zprdvT4^VC_UiP~d$w;j(O{kO&(2>p&)TseL$!vg*oKbo$2CAj90XhtC-grJhI~$-w zWrq?fN02p8Ey{QU)R45@jp3YJJNTLaZ$NZev2ad^B5NXjQ0W&uH|1GMCXD8hj&WkM828(@2mf2a^eZ4B>E>;1eF5fL@Z YFCD9oGNMm%XaE2J07*qoM6N<$f_V=BB>(^b literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/lamp.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/Plushies/lamp.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..b70a213869049a6b73db27cd739545369439b020 GIT binary patch literal 589 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/narsie.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/Plushies/narsie.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..8374c6d4aa089c18873b3e0e6782932b3f4eafc5 GIT binary patch literal 2001 zcmV;?2QK)DP)Px+i%CR5RCt{2TF+}6M-=`>8P=c>gN23VMy-M@7HT^I2`6!AE~TM?T$4irIrL!2 zUl8ae_lEohKIPa$$+gfV#g{@8YMet7LxpYRSdC>AY)Be|#U|5({l;%c`)hWSv`~E@ zq}_S*zBg~)yj{&3V1NMz7+`<_28e9_?(7=NnVZ)yTfGI%m*({fiqg*N=qAx)Xg)paCxkq1WZc+?-x-6b&TD z$?v)o>iU5`TmCE#u;tgu=ayjv4TwH8&;=oU1Lo~vdTjW4TheGSK^eA@WXGvf#7Ydb+IT_%%gK(BMcuXcm(4#|=JAE{=L2P&+y4Cp(H^Y~E&yiA14;Ovcm(HD6640MZ=g#~3Ob-|PbVRLl8~!A);g|e^ zhk6w5|Na}?gU2FW=cJ76W6rE2rRsT}NW)J^`e=;e_a&g`?4^MW%NhY(AL-|b$d;c& zhCCT;_$2_e*4F{-|1E{*VWr!N47paT<(u(Vs}&`#^TQMX2T;VP`&;r8a#8}G7b_RV zp96psl}cYHp3t7X?&?sbMCdxh=y{%Sonc=Bbfi?zUUx;19)ihd!yhI?NW+hy*;(!P3H^B{2eAA$79PM2U~t?5_8a|1JW9oR7Vz=Jo-H#>6Q}!Jlow)ENdc+$A&)( zJ{8XZ0}L?000aDA5myENujtH#D1#P+_~6Dy>HOE6Lq_op2b=vn8-#IWb#^XZBphEW zB^HEG^~Mb9jhX!#!RQ$1fWn8EnT@$Q<;eX*4_tq}F@psm)YnRV`d3?idc!;`;7EVL zG|LQ8#fcYV`G+5vNbBs>-dhkteSi9_J{v%&eDOJ+EG?oiItBpPezm3BlKv6lONi^K zH)aC)e)!`t(ndmR2RKryU*5gLwXx6iJt~#)-()xdJXuDpaZ5&a2K`7uU~qXGyxLyv7OVs}-y@S8;E=tao$y44F#; z*q=2sWEX@`W;B^0Ps^!REaCEI8&!7#H`WgK_tHdx^-~~d@KtvLmp9wmI5Kqm)fS#C zE$Y5MZoLEmTs(55PcPjg8DM`2a9y;uO@m)+u42A=xWAVs60AJtNU1K&oYlLyRBpPw z+13%j_x5SlDGGR5-JC5)O2td_;8v%vwsz2hDFww^94w|)ve&l9}Qe6@ncW*c|j>;VAQnyWs}tjO#Dj(Z2? zvc_f`OXFp2tobf*;^buiUj4~Q!6Soae(m6NO5-5M++!uV4}rvMC20K<-24dMA06tu zPoaBbZQVLUJ5s9oYDFKj;W_~z_jV7>-JQPsws{~`1gvyBaPuR+5X`;xp9gaDBUtHn z^s-2UwdShk0uGm3?(LqIsTE6_D-En2KnBr_6Pmj_zC#nu-5t335!{wC)$_UMdE&N| zaPuR9x;fq-9m?c{#GuNmc@!q9Vrs?GnvV3J8RAu8TC4Am z4h6O~cXtvwz?3CUTKhq5raeiK$pM^nI$u!D-r;e1*BSQJ5cP?SgdAKpOm~9)l1NdJY|aZL#dN-)xD$f7b7{o0?$00000NkvXXu0mjfFRkYR literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/narsie.rsi/meta.json b/Resources/Textures/Objects/Fun/Plushies/narsie.rsi/meta.json index e5ddb0f4d7..2962536bce 100644 --- a/Resources/Textures/Objects/Fun/Plushies/narsie.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/Plushies/narsie.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-NC-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432, inhand and neck sprites by TiniestShark (github)", "size": { "x": 32, "y": 32 @@ -9,6 +9,18 @@ "states": [ { "name": "icon" + }, + { + "name": "equipped-NECK", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/Plushies/nukie.rsi/equipped-HELMET.png b/Resources/Textures/Objects/Fun/Plushies/nukie.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..87a57d085ecdd16fe7c15edb8a87f5cc79044202 GIT binary patch literal 868 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/nukie.rsi/meta.json b/Resources/Textures/Objects/Fun/Plushies/nukie.rsi/meta.json index e5ddb0f4d7..5312d061af 100644 --- a/Resources/Textures/Objects/Fun/Plushies/nukie.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/Plushies/nukie.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-NC-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432, inhand and helmet sprites by TiniestShark (github)", "size": { "x": 32, "y": 32 @@ -9,6 +9,18 @@ "states": [ { "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/Plushies/penguin.rsi/equipped-HELMET.png b/Resources/Textures/Objects/Fun/Plushies/penguin.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..e8c77676802718a007a859c3576c29a43851dc7a GIT binary patch literal 494 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 FV1hu}y72%2 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/rouny.rsi/inhand-left.png b/Resources/Textures/Objects/Fun/Plushies/rouny.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..c37131b84aa8df3d461164ed19756b986c2d6e2b GIT binary patch literal 1136 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/slime.rsi/inhand-left.png b/Resources/Textures/Objects/Fun/Plushies/slime.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..1e30789bde68ffee3c83b61805de875fe73cb35f GIT binary patch literal 539 zcmV+$0_6RPP)Px$)k#D_RCt{2+OcaIVHn5p?}bu2go2XED+T)p1l;mJbgDxf4+`y)km}gE6h}3X zwd6)SxMivTKsLc4V*@1!0ZCGE@J`IzA-rNk3yD4lY4iKQaooxM4j!I+9iA6NL_|bH zMC9=aEW@Ix;{aa1c*T#i?*PPc+4LRO)~tdGg@QXgd_z80WMh4ee6C2hyNh$qS>(6s zeUqHqObQT19XA*Z2*Z%sG+{Wt2EgC*opImH#cQ8w?TJ;;`*+`X_VhXX2V1o2eLj8O zq*m<#aB+FMa{k7;S+oAeBy}>mx;dvK_I8f>@Ntv9onwpWxvV>+E+5O^F z_o43%0K@69m(LXeXn%c;bIt`pkXaFYu~#Xy`NVN?AiC_{fV*(cxo&rNUhrjQGIE_w z6Dr$BRJM<@h$o6VE)2uJ{&t@%^8)N2Yz1&rghsPu9Y5y_+-R0o`ouEnfJr(6pjPb> z$K@vwE7eRm|9h0PEe`&r5#&MZiwP(5k=~?002ovPDHLkV1fc{_^ln=s9yC;R z;c$8V=z;tflOuwH>&rgN1y|J-2-?QIfBf*P)gFBZ7cltWpMP;tW|RX zTey~O{;RTdiW7&##zoW4mtXgsv#>hjkcqBl%M6?A7w432W%N7DX4S6N6CvofX2n|L zy+N!?j0NX&t?;%Io>}uV#_rWm|Fc)wRvhfi1p4x&&ymdzm$_O$pA7NJPsrYR%*)(S zb>@l}w^-tL2j5>)sdBht4vUem_o~PG#)2TQ^E>O$g!L05m)5KSMk|A-tDnm{r-UW| DCdlce literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/slime.rsi/meta.json b/Resources/Textures/Objects/Fun/Plushies/slime.rsi/meta.json index e5ddb0f4d7..5312d061af 100644 --- a/Resources/Textures/Objects/Fun/Plushies/slime.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/Plushies/slime.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-NC-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432, inhand and helmet sprites by TiniestShark (github)", "size": { "x": 32, "y": 32 @@ -9,6 +9,18 @@ "states": [ { "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/Plushies/snake.rsi/equipped-NECK-vox.png b/Resources/Textures/Objects/Fun/Plushies/snake.rsi/equipped-NECK-vox.png new file mode 100644 index 0000000000000000000000000000000000000000..b80dd1be4b90fe66da09dbb4d2a5488b341575c9 GIT binary patch literal 609 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/vox.rsi/inhand-left.png b/Resources/Textures/Objects/Fun/Plushies/vox.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..be3707ce65f3ad731ca0ab9dc6f21416652cd018 GIT binary patch literal 886 zcmV-+1Bv{JP)Px&FiAu~RCt{2nomm`K^VrLElHIa2uf%OUTX1D522JI4Q2ZQatSP+JghXZLZsy4 zMfwRsi#W*D5(tG}+FOy9ob1g*pjc?*pXfFvu4@vQ9;R_uYio9$u{pdySaxQ} zJ~R8|oi}0M1qgy52!bF8ajedQxzD|#bspD53+6L?p=Zw`v}B-&6w7D8A|c)m^#Rr~ zZ`UngZrR&hZY@98cZ)F5)mbp>m@l^6cE-m8KqSNsnVRozKDSmR1Y}>xxp_cB1eDvr zP}CVasBshgMu5zl2L$GT)mgsa^_3QyHlPOi`1(osqOUrYPos$^RlZvAEEyPCQ~ZYbTHF4F;Flt6a_RLY%-;K z-oL4d(|f*Hkdq9B8u0OR6##I3dJU#+BKXQctZ1B!dm(y5L8J;!&G&w#}T zFIR>a8UaI5=Wv@Li*g$%?eVHnsE?P=yhfi;171Ie(zJm$&ss>wMJoS^rh~Z+FYP%d zc-aQRa{vH5eGd#Ek}UVnTed z2O?mgV4SpNnlQ5frS!Yx*l6%Fyq^dVPBNjgY}jB|kpwjzEE_ghHf%6}SCr3Il`leg z1Z?kDVcI5QMdRWRUjj`BixmxQ+}cEIdI_2i_WdibDtj?OxB+Uqf^d#khGh~f8Zd1W z+xt~mn<@ZcqwGEOg&R;=w#IfuOP|8DO@wo6P}3CzZhim&=ri88T|oHC0QA>X zZ0~!2DZ&d|V>^h~oW5=G1K{r%NS(YF7#SY*_38~l5ClOG1VM0q0r=NPp|+XqssI20 M07*qoM6N<$f?#)yU;qFB literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/vox.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/Plushies/vox.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..3143c99893cb3c807ee37a0b1c88fa875a7d9a84 GIT binary patch literal 896 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/xeno.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/Plushies/xeno.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..ded904dbe3becc81c34640d2f2dcc304504a20a7 GIT binary patch literal 882 zcmV-&1C9KNP)Px&EJ;K`RCt{2nq6<3FcgMgQnnIXD$G+S(X`r4H|2i+|0ndmYI9Sy45^h!Qq_d6 zFza1KBa1VIr0E!L(1jJryIj^%vg_`XrA5aX`;V{-0pv~tuu;~5dBV7>ObePU zC+s}eR~ObcvKH21+*O==001dx008wH5aMqXL{SK9(2ry|8jTeIOlM)Oyo2%=5BE2g zHpFnq5k+Bf42S*50{{>Nmn%wFMYp9da_-^b{-*fR@t3bk${CzPjx_xR+y02;S@y#F zn&Hq@*3!jhbelR1fIOs4<@G=zd?e2jY4(ETSwaXORXL)IEEc(=V^ z;W)gAge1xHfHqJcVBA$g_{9!ZcG}t;aC$aYS(bux4@n{sMIn+zf^!d9mg4km+}Rwk z4(+gY5xdp^S(YMAUjXDgggyWuO<#(o&_%@OlfpUL`#^QB>P;v&#mw-Wd$8>fI5@B? zmu;hegMGq$$l=^mI+6*3AP9mW2!bF8f*^=KvWu$JRxL8?*jKHPaaZZuf7J_4#@(%Z z7i!-Lnm4Ut9}N?XyUID_2!aXf7tq9VZUw;v z(^=T&J>QId@S*)VHw`d6Tl5S~XCbDu-3U}8gIY#gt86rY&I8NPQvhHJvahBIZG^Vx z-IxT0@DT(PQwW%X>~j|o!bdFUt<{907*qo IM6N<$f~=#9@c;k- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Plushies/xeno.rsi/meta.json b/Resources/Textures/Objects/Fun/Plushies/xeno.rsi/meta.json index e36a30b200..bd583cd1ab 100644 --- a/Resources/Textures/Objects/Fun/Plushies/xeno.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/Plushies/xeno.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-NC-SA-3.0", - "copyright": "Sprite by LinkUyx#6557", + "copyright": "Sprite by LinkUyx#6557, inhand and helmet sprites by TiniestShark (github)", "size": { "x": 32, "y": 32 @@ -9,6 +9,18 @@ "states": [ { "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/clownrecorder.rsi/inhand-left.png b/Resources/Textures/Objects/Fun/clownrecorder.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..190072658baad8b63c247c8e0862f200e69aeda3 GIT binary patch literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%zB|TjnLn`LH zy=9np$U&g>p=MG0RI}SVHr6m^Sg>Vk2(V5+;Tg2#oT$bGjhd#ImrFaYZCaxta<6sc zpL$!d&kZ8Tf<&>|<9HuKBJpI?6? zux$S1nePog>DTYQv*qTyE%tMk#64wt@zlIQVDi0qpWlZ)XTSBketPYR_=GdH3_nji ze;l9nW7*rb^yw9GrmEBLhg>fE$87MMcj2{XaaVf5wt|QaT&wjwW?Zdx-@&nzOX#~v z?sMY@L9CtspKL#>XU)8oOXy}r#dO9gdLBN!*2l6l7k&G5di$C9gTe~DWM4fv_P2^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/clownrecorder.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/clownrecorder.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..836570c0f5b44c3bf24bf99c2a58dfe570514dc1 GIT binary patch literal 399 zcmV;A0dW3_P)Px$Nl8RORCt{2+C5IgKoExEK}yE~jEpHPB+C~_nlwlpA!&kyg2W{_2@=#?!9^q& zJ^?3yhKgH!6ljqX5I^ybv8DH^T+PnT{cJ~fJ)TV9k~#~|DXQ3r`A1?h|RS&cP-bH9NX~d4-S=geUXSCg<;SvAO$%OCr>U-bPVJCRS|FtB|J|^9F t3*BIvG}=WNL+hN2B+Z7g8UO%bUvC-o>@g_wtNDeGGd% zgNtv@3ATLYZ$Ibnx7m67KgUlmnme~x>+X(_zxp5*K(NQaml&- g{>^DV28IK=){ghYneX=gT?SI+>FVdQ&MBb@0BWW}hyVZp literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-left-body.png b/Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-left-body.png new file mode 100644 index 0000000000000000000000000000000000000000..52215ce9a59bc45979fd9a89fad5091291906516 GIT binary patch literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|PIv}b#Z zjUx?3)r!7OY1P-iS^M$oW_-6BjtY!KMD#okW6+@#v zt_cT-D}Ro|8nB_E@mJN0y8##WvvhZv3>+sr3k-`b2T;s O@jPAqT-G@yGywp?@glYW literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-right-base.png b/Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-right-base.png new file mode 100644 index 0000000000000000000000000000000000000000..c34bca921da527a400bb8c97e9c97b01d22c425c GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|ay?xfLn`LH zy>*b+A%KVBg49tawhagEEv1`MeIBJSXG=V_78DnEQn_bsnmg;;JvX2h28Q~eIikn( zp6afBVyMnO3YimK1t*>&v_EU(e}Z=TLh3 z^Ov)i7RdJWFZ7syJpWAOlJoPqL1uzLKycds19j`CZd{)7jepMDuf;R}GXLHic1&#N z3;rFGZrU5X3w2BT@38ZcGmu>AeBzD#j@SQ~ff^6ouVVVURG!gi;dhV+89ZJ6T-G@y GGywp}4Rt~Q literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-right-trim.png b/Resources/Textures/Objects/Fun/mech_figurines.rsi/inhand-right-trim.png new file mode 100644 index 0000000000000000000000000000000000000000..8288a861bdd8e33efa55db276d6bc0dd66ace156 GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|=6Sj}hE&XX zd&`#ZumTU;g%#iT_wpZDdgEn?sNh%k%%+}S_oggRu(L_oxSf}Qp<#KD*Y7ml$;vbH z*PC65Dwnsvwn^;KmotBtt(m^h#bwLCo%Qh>1=@}KTP<`iwuYUY8Sk>?qx=S^((p4p zK+6~yCfq*rvr$`m)9#-Q{%Pm<_gHJU&iu^0^42Hg=j=h-U!=Erm;c;vUR;-8!_C0( aAiVra&zj1wQ%*60^m@AbxvXX0vjdfZz=!9bZ|AyRy4LF1n)v1apRdke zQ+l$o?(VK;Zt||Ic0&o-NGy`}SAngQ_?6pCBPgg&ebxsLQ0NZTx3jhEB literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/newton_cradle.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/newton_cradle.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..23dfd5d2eaf0a1025aba20ac19281372a9dadef2 GIT binary patch literal 510 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSjKx9jP7LeL$-D%zcX+xuhE&XX zd-q`OVh53ihtET^K6GknFa>s+sxDlqvWTN^U4yXi8M!bQ&nY=crzMj<*Sb&mn|wy* zz5pZ8ED*4nbvRvpTB@d?;AIn67nj@H41I&Y)c${&p66MAyUxbme)@Zv%lqH|yZqm8 z*_2bio@~vne*JBF-1qiG3m2Csb56f3S+$94+poWxOQ+jgJ&!Tl`M)}nvHE@ej!C=L zX$uN|w%Koa&&1corRVXF#o7V&x9uR-LdoBo&TUS>Ud3PA z|C9NH^wT-3*Bz?f)Xl!&TANVKg4eq?o!kBT{d#^B6Yg9K4c`1(jRoZTBfq-7?t8!5 z=i#gT+xO3|zWV(_{jP6HL0+k~pKfj2`z?Fjw_Wx;hZP^)cN5t9jp@d#@5`se{d+N| z_~w&;_LsPBRR4c;byJ?1!THa#ANyW*aXDhqXTOW->-M+I$TpTIM^4+MOV literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/newton_cradle.rsi/meta.json b/Resources/Textures/Objects/Fun/newton_cradle.rsi/meta.json index 0ff9e80425..b92514703a 100644 --- a/Resources/Textures/Objects/Fun/newton_cradle.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/newton_cradle.rsi/meta.json @@ -1,25 +1,85 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from https://github.com/ParadiseSS13/Paradise/commit/aa7a7b3cee09b313705343a6859658b928a3da03", + "copyright": "Taken from https://github.com/ParadiseSS13/Paradise/commit/aa7a7b3cee09b313705343a6859658b928a3da03, inhands by TiniestShark (github)", "size": { "x": 32, "y": 32 }, "states": [ - { - "name": "icon-off" - }, - { - "name": "icon", - "delays": [ - [ - 0.3, - 0.1, - 0.3, - 0.1 + { + "name": "icon-off" + }, + { + "name": "icon", + "delays": [ + [ + 0.3, + 0.1, + 0.3, + 0.1 + ] ] - ] - } + }, + { + "name": "inhand-left", + "directions": 4, + "delays":[ + [ + 0.1, + 0.3, + 0.1, + 0.3 + ], + [ + 0.1, + 0.3, + 0.1, + 0.3 + ], + [ + 0.1, + 0.3, + 0.1, + 0.3 + ], + [ + 0.1, + 0.3, + 0.1, + 0.3 + ] + ] + }, + { + "name": "inhand-right", + "directions": 4, + "delays": [ + [ + 0.1, + 0.3, + 0.1, + 0.3 + ], + [ + 0.1, + 0.3, + 0.1, + 0.3 + ], + [ + 0.1, + 0.3, + 0.1, + 0.3 + ], + [ + 0.1, + 0.3, + 0.1, + 0.3 + ] + ] + } ] } diff --git a/Resources/Textures/Objects/Fun/pequeno.rsi/equipped-HELMET-body.png b/Resources/Textures/Objects/Fun/pequeno.rsi/equipped-HELMET-body.png new file mode 100644 index 0000000000000000000000000000000000000000..0dff7f0535b18b646d02051a7a0b3b10e92c5af1 GIT binary patch literal 446 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%zD?D8sLn`LH zz4dTyvw{HohtE%WpO~v9FqbDVEh}IuOJG)eAT9mVZcDo6%V3A@8Gq_KGjp}(T%T+* zk2&;Gl>VQ(dCwP?%~}0?y;VT=UJk`;weM>$tIV;i|LAD*)5Q1n#%s5hh6-JpoWAw* z|0^tl^ZzXWE7oK9y6nh%&yOalJ&H@O-2P|L7n&>@exN?J?5I;kTXY!1#mBSkov%t2 zm@XA*6}YCmPsZ!+Hm0ogKSK&xkJLL9+5KO(d!2Rr(v$m?egeG%1>2|8MqBRveCxLE z%UeG-?fbG)_3p2k4Z7}+ZvSOUpQgTJk!akZsNDj4tJaiU+Wq1|WtdHTeX*6t`Qyd%8GNs7V`bgoMXfy2tmgXNA(!V~Aeh}fz zvD^OAJ5>Sxf_9Z7hR;GWu1N`mGX=&!_5aeLUl*bEe0us#eh(Y=H&udP?upN>5!FoH j?)k#f4CYjZ2E9LwGY+0C_~L)|JxI{g)z4*}Q$iB}tU$y+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/pequeno.rsi/equipped-HELMET-vis.png b/Resources/Textures/Objects/Fun/pequeno.rsi/equipped-HELMET-vis.png new file mode 100644 index 0000000000000000000000000000000000000000..5cc26f4de912f97a01b6b8bca688ae3fcda6f4bb GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|R(iTPhE&XX zduuoEAp-%13lbZ-lnq!+jSZZZC9-9;d6;>)d6tCqIW@Z6QEB{c<^6At|Fqe=S3TOl z^ir#!;AJ&U(f6CTC7)c>(Xmbb{;_Ibr$DA4ub2NS{PZ8x?0WO*6VM6}sJT?)oRWQf z($k7rc@Z*~6Hh*g5z7C)G3)onTTVhn>z&?(&zSyp|Hd8b?Cl?v-F1sUAeP(|AA0@k fD|vaa{(vW}@4orusP2)F1@S#y{an^LB{Ts5#)Mu{ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/pequeno.rsi/inhand-left-body.png b/Resources/Textures/Objects/Fun/pequeno.rsi/inhand-left-body.png new file mode 100644 index 0000000000000000000000000000000000000000..db68481440c6b8c3b0f3b3aa38f406f0922bd52f GIT binary patch literal 405 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%z{XJbALn`LH zy>&MCh=Ks?gTg6IHH(?73baEUwM!gWix(>wCAE5Pvj34N`LlNV)hMxU_A_$NIt2uQ z;G(SViGy>Bcm8{~x4qAvDW~b&&YYf`vB6hQi$9b$^w|CB_O{qx3$x?h*)ziHmbHsB ziYyRyI2VZ?V!Obmtz{>cOym<+IJRR-d-&D6%TK)jyzu&p zaPCiBMsWPa*Se&hms(7#3f4QU{&j!adhRcMMH|kQEpUp=E>Azf^PCgp zCJ^6ub2%^J;u13k(PJ|SpKawd!5}$Cb5*Iga7|-y##~4?ebW(kDDLYwvWmFF6vGDq5xX v#i#byb;q7X&$lo8E$+~2&;bN5-qkUjjNMVdSSWM@7|slyu6{1-oD!M<#}TfT literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/pequeno.rsi/inhand-left-vis.png b/Resources/Textures/Objects/Fun/pequeno.rsi/inhand-left-vis.png new file mode 100644 index 0000000000000000000000000000000000000000..db7fa0e8beda83641e8595f8ec1257d242cd04aa GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|=6Sj}hE&XX zd&`jPkb?m01^JIf*XA7zu4K#R?oZ&_XzFg*6Uw6PXujfyT#mKzpR#%;peA1#{p+)8 zeoafCww67w{I}PnI*G|Dp`o$YOV@tiVstt&n8R~ZRb5NV43`}Gopt?K71^yt*-mb;P?FS-?Jb0$A_A-&ao*IjQcp5wMC4T2dL@aP2PRB Vp>naZAAf<&@O1TaS?83{1ORcIRd)aY literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/pequeno.rsi/inhand-right-body.png b/Resources/Textures/Objects/Fun/pequeno.rsi/inhand-right-body.png new file mode 100644 index 0000000000000000000000000000000000000000..520171f62101072ce5c22e1989a368f11299973e GIT binary patch literal 413 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%z!#!ObLn`LH zy>)tHvw{fQ1N-tk$2Th;lqB$nIf!m^Vh=gc*LOn6nXN}(=H!Z>`+GYB4_n&A6fh`( zz%SSSt=v6^hX3t&4!^Pu-z3pvsFt<1yZGMkXw}4Gg=@Ds_6RZDNmu!kyLZFuUF8R& zUiYso44o}J!R^4!tJ^-;zjPPuti3P(+==7p$5(0Y4t7T_@9{q_z%-|Lc?|QXmAj6n zFkO1JeoE7Kxv1CW9~k_k|4iAsHOovuaOR$mUI&@in?B52|IW4Yc&Epl*B)ROfr&-J z#>b9`NpI?|ZuZ_6X<-<;ykJUf@4n!NE?+q$zSq1>dvY^p9Y2Fz^}kmKnWu6}{0_U= z(&3VwoAa?gl#@g8Ky|!``;xY;w_4_`3bqIe1v;l+rK}MXKXz2u4+Mt z%Tf^rk)y&dKTqU6>mJs9tK}~L9&bigK_K|MN!X;EhL*=ElCp4U-r?&5OfX;t(S^(iYKzFvHO`Qs-yo{|-oOIHtF6N>J7`Px%mPtfGRCt{2n!#$@KoEw1$@XCph7c&mE(sK@>gyTx!i2h0b@^uhGA=tKFRe1VIo4 zK@f=O`M!Nhx-O;I;x4x3j)%ovOd70-0()I7!bAbN38V(NIfa6%!Oba3*}qh(eTPMu z3KIpkt+k~dDUOk6p{=zY4_mga_0tHL8m6IN1TiFiu9^|?D z?D>amZ&vO>YQW2&1g;F=(j_yXQtdm_$pD@jk?-706j-jm3z-2X3T#2uFr5rgP&G0W zlDZt^$&@Y?=D0xRZQn7c$^t|OqJ%Du;3{MWbl?BPi<=t&!1s4Qa9u2t>t>$iA&B}g z^xqz?1VIo4K@bE%5Cq|I(q5O-S1_=-JN#uoLYnuH8_@H8mL`eEaC0i&+TKCAUp}_V1nJVI<79yIiy$E+doL%kHC_rSC{A5+dtrt zUV)-yPQFUtPLAv>F2PeHOeX`@_SWPSTlYQWQ{ZhY%YuoevZ1pB@I;c#2{E|!tB?y8bJmeQlpCW!qZ@@MH O00004c literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/rubber_chicken.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/rubber_chicken.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c2de8ad12cc87f6ec35d0d042d29c0840c7d809c GIT binary patch literal 744 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/toy_mouse.rsi/meta.json b/Resources/Textures/Objects/Fun/toy_mouse.rsi/meta.json index f917441a32..1871ac4942 100644 --- a/Resources/Textures/Objects/Fun/toy_mouse.rsi/meta.json +++ b/Resources/Textures/Objects/Fun/toy_mouse.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-NC-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432, toy-mouse-equipped-HELMET is a resprited 1-equipped-HELMET in mouse.rsi by PuroSlavKing (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432, toy-mouse-equipped-HELMET is a resprited 1-equipped-HELMET in mouse.rsi by PuroSlavKing (Github), inhands resprited from Mouse inhands by TiniestShark", "size": { "x": 32, "y": 32 @@ -13,6 +13,14 @@ { "name": "equipped-HELMET", "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Fun/toy_nuke.rsi/equipped-HELMET.png b/Resources/Textures/Objects/Fun/toy_nuke.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..2de0b5c5ce98c95f727cc6fcd4ec1cbcfbca7485 GIT binary patch literal 766 zcmVPx%xJg7oRCt{2+CNAeQ5*;GZ?vR;AlJ095D+{Ln&E^ZLaiVu4ua6JP%Sv$pdbh> z-OLaWBq9__dxJwes$H^Nhhm^~kR~l0rGyM=#eia^CY3u-d6>Koa^gkP-o223@cV4< zg}m?Yevr%XegOzj@7>~3q;k5pO6|yh{}T{7OGN*$#B295A1$H$Hjyt$kqV?x7#)$@ zkV0XcZAPm|p)eFBiZ?%RHgt~f1{j9H0U#J`L-Ofe1cPm^{bqA=A|2Q8?dxwOMy@xM z@4ErTVi5qqSQXC~e}J*7Yrol?oYLMAdhXxB^7}^(gzpBtdixoP@(rNu-v3~lLIwC`nkJqVLKoI0Lg>x;L^}T#Ube*E z!j~~0s;csTtuIr)(BB{-gb+dqA%qY@2qAFt zN0?h5;ffN4Z86MhT`m^he4v|6 zU?E!pV^usD41=*Mwl-}Hj^>*F6Br!L1-3SASN1}-f^Iee?9K-c*aoI2=a5RJJe}vc zU`^9_Z*MPTSw?607Ybu>#8wjkfWlZD0PuKwyCv^{cb%OafGasFCv+12>kH6er$LI@#*5JCtcgb;GxUkxTJeY5lCS^xk507*qoM6N<$g3xqaQ2+n{ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/toy_nuke.rsi/inhand-left.png b/Resources/Textures/Objects/Fun/toy_nuke.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..7266a1a86dc931781e5057aa84354424cb4696ec GIT binary patch literal 770 zcmV+d1O5DoP)Px%yh%hsRCt{2+CNAeaTEveZ))iv7~tHXr7-Mto3lw~x?HzaT z_siq*TkZ=8A%qY@2qA>Hegd4$`i6q!tykWc0^aLx-AIp%_x241sgdXXy1~_`ZzxEb zra;paJV{JL)0CQl{0mJ}xLQ?V89-rqm^4j+U2-sCL}2AJ80;A<58=d)`s#OLM{9;R zVMI8#T7&Hk*#G_$0MP$%6#!u6GnicZftAhY0D#hd3oOgJ@{O=8i%R<~08oC;<@V*q zGJyO2OM#tj8;)~^-hmJt=L|Eer?}@l2KJ@{$Icc~qiL9?d1W8dG?5xjUJ5=6=!vp})#VG*5*Tf{Ei&NM=Qcx@wv9`8`xw*MNu4fnq#bPn2s)~3# zUcP_*<16ZZ@QnQsWNVlV)<5vtMn)R?@h-zK$X-~$e5rEkCxb!q*8e44*D06FQN7xa zTrNktu9LUs*ke+_i?tuRu9K>&Rm0@iqYy#}A%qY@2qAwzP8rO z3N>pDZ+|~|BmlU{y)ocC&okIFh7X@+-Ak+iCpV7-;CB>fmgZl7`K$hH5gD(+o-tVY z3?_^S?2-dbQ&3nQX8F2XFOQUbdY6Fne32jQ1K8;7BrBf*P|5YU`5`X#@<`|yC!n!B zl8gTg=oqJsTW|29yV81R+s5YSL$$JST&Hp#N$+Z>)M&clNDkW>kj-XE zRaJyT-%v=LVP?FN6#`oE2!~utVd|h2I5;imkqU`PtiOw5_sAkYc_e`G4RCWk^>F5U zIP*xXF3a^aE84=DM`CkXuE$$*;>;rnA;h)y3!#@mZM^E@>i_@%07*qoM6N<$f;AUy AR{#J2 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/toy_nuke.rsi/inhand-right.png b/Resources/Textures/Objects/Fun/toy_nuke.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..2ae111a3aa005edf8cde52eb2e79d67c0c92389e GIT binary patch literal 727 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 Q|6>fWLRy>Gm^#P(2g^m1TL1t6 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/toy_singularity.rsi/singu-inhand-right.png b/Resources/Textures/Objects/Fun/toy_singularity.rsi/singu-inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..4314aeb33624a693ed85fe135bb51228790d7966 GIT binary patch literal 2959 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 o#Qd^Jrs3|!{5$z|=IBjE6bJCXO1rg-AjCuI zR(*lYO-uqEx@c)3q@`ms=2LX*C-9QZcEu~~C*TH_SEl%N1 zdgnOs|A9$Fth3(VJ~{9nKtx1DL`0+|heZy%HS65sq56tBEPq}te&2<~07U8ng!@Q7CQv3#}24D;<#z=-Qhq!!u*)D|D;v>KNfN}u69ui|&@2ieYn)y@Zw5Le_ z=Y6sHI=%f1WP}->&S20u&f;LH#&F7zF`$pLp~#0}N-|SDP_AL-hMNmc8L8YUy~*nF zCt%gje{+6?TNfCg<=NpJ`o5dNu*B#0UpI~Vil$fx^Pt}|FSP#W(6}Ufd9niQ?uI{A z)7QvfS|ztpuOq#7mJ+|LtA1}*pf87F5vPoYLd}Fd5)w%-m+t-bH&WZzz(#Q!+Y|rX z3i%~_bROp2-N3xPzRvpF?CBHMryLe8>Q2z_(7y9Qq2FW4@>+RIM&%|S(Rl)YrRNxV z=xp98JmnCk^D%t(!~MvXXOXObGFpyoIokfgJJzavTI+Yk2-cE^tJix>_4j%r-nZ8_ zjD7FN#rrev9dA?rA%5`2x&npL(|^yJ=6(@0?ukF(@;2AqIQynv+wC7#N&X!PTw#w> n&;PSaSo7v4w?sE2tUvSiO3vc>Gx@JP0}yz+`njxgN@xNANzdLW diff --git a/Resources/Textures/Objects/Misc/Lights/lampgreen.rsi/off-inhand-right.png b/Resources/Textures/Objects/Misc/Lights/lampgreen.rsi/off-inhand-right.png index 2aa9a84475fced4e33a0cc261e9a2dbc7314b6af..bd41d68df9f52b83468b65363ceacf634caea575 100644 GIT binary patch delta 614 zcmV-s0-61d1pfq(B!2;OQb$4nuFf3k0006xNklljA7{>9x5p1wns>p!Y z@E7RZ5>bhP#S2JPL5yMKQ?T&`$ifyeuw-GYh&rJPB<2jPI7RY?I-z4|&b6Jh<2a6M zOV3Zr&?dd!`^I;Uau0}zh=_=YxP%x&%uP>YV-@FpyY$%+hJXF!C&LtP`KxKonulKKcDx_c_VFHNx=zx<==VFV6RpC-WPg zZAOH$Ur!xt8o;u)KpgKF4Gl@crjy@&~TL z=QE$f&P!`d5IpCV;RNS5Bc}8#f?1Vw$IZxr-(g z z1uXt7IA3~v$MjNTLjh8?$j91=?vH(II zWjTeWuY!#lP$z)Uw9f4Vd0)nNJDVXA5fPD$|2MK9NkMa`(EtDd07*qoM6N<$f-dtc Apa1{> delta 499 zcmey*(#tYIrJl3EBeIx*f$sf}!IulY@*H!=nuh0`05~7CH<^5*ZcvxEbn!a%^sa3%r7RcFnsVd0?CH zfARPJoiW)rE7vY-hz|KxU-jeYtrP1`e=S{IC9`z~lh~*7Pu3e=?lIz?($TeSCLd>8 z!_Ua3H=AQ-C;Xcb(sq;Cef`g^F=m$=6&=K@l)2ZluR3=;Y>(60Nbi8NH51v@HZ8OM zthd3A<#Qq5N3|~p>UFl=u4BEs!*egM-J9nTtR3DLtqi16uE$3Icz-?N^nn=#S0DW0 zlW_W&%k$2=AR4zv;C^iI_$Ye)z0Ls+U#Fw5rd3|udijcf+Q!Wd%YM9iy-4b=y*|I* zgZaHX>b~Bxd$E-@Cq%uj%UyPDHD9p#<6n{{xTfgq}<&tbKp|!9AfT4u!QL_Zbya+*K7=AR&9sT&!kZ U(ll8WV+J7bboFyt=akR{0BP{p5dZ)H diff --git a/Resources/Textures/Objects/Misc/Lights/lampgreen.rsi/on-inhand-left.png b/Resources/Textures/Objects/Misc/Lights/lampgreen.rsi/on-inhand-left.png index 22c2c62be5cd849ea6ccb93d4a8a46bc5599347d..02153a45dc235bb7f8f5d3090a70e5e9a4998f62 100644 GIT binary patch delta 562 zcmV-20?qxC1Ih%DBYyw^b5ch_0Itp)=>Px%1W80eRCt{2+ObZ;P!tB>zeYC`lNuA| z#<2Jf$jlQk`yQS2CF&F43otlAaUu>bE(|c>&^UB6CTemV=!HsJ>22?YHu=A;p*`Gt zXv=VZfQX2Qh=|A(9|9jzn>u^tL+}K_0)n8z;)A4yWk*)3zJCIE1iYSyQcVaTUe5y_ z%`eTWC|Ye+KJG-m$ASRjEj_(EA-Tp|dJVhv6YC~?k3~fEnvmLjV;cahnh*|LQ3^F- zd-GM^9~!K@17;%^?QV?8!#VI1LT<*G59h!Ykoi{kA-LKc&WCe^XReKBYJ^Sv6FB)9W_+2(-H~W~0Bi zZmR_+iXga!v$MeM>fBcA@44!cvcXsbI{nz$-n^nV^!)Ofd%gN>d-KX{+ z!htJgLMs$T8(@2mf2a^eZ4B>E>;1eF5fL@ZFCD9oGNMm%XaE2J07*qoM6N<$f=|`{ AhX4Qo delta 377 zcmV-<0fzp{1d{`hBYy!}NklVz?2h`YT7xV3<$Q@S4$^ukk`y)o|^@*}~!0@DeA%;X|5r09dU-v4?UV!qVHO*tO zlt6Qo(84njA4`eR3fyhHd~basau>MPl`Gn@NOMs11y##lvjF2n>}B1rW%f_k-*+xn z4&C{^;{KfMuYk93C$If)cp^_?nzLdn$*UXQU;9z_2LJ#70000006;EU-w*^_8f2!7 z#Y3!!n>s{Q8FD|_&l77Nrr&>J2BV>F#LC27d%aog?6bk{N%-Vb$KvbO7{{!{cCEiV z9gsQ|jy39nO~&Z?r^BgJ;Yjwj_Un|-ZE~Fa1=!wr+qfeC6#YLLM(*05&;S7bED`wv Xo^XGMQg~F<00000NkvXXu0mjfl~J^A diff --git a/Resources/Textures/Objects/Misc/Lights/lampgreen.rsi/on-inhand-right.png b/Resources/Textures/Objects/Misc/Lights/lampgreen.rsi/on-inhand-right.png index 71d3696007f1bb5e891a616d4d823b55ac02d53a..b70a213869049a6b73db27cd739545369439b020 100644 GIT binary patch delta 565 zcmV-50?Pf91I+}GBYyw^b5ch_0Itp)=>Px%2T4RhRCt{2+Ock%P!z`TKUKR`szepZ zGpfAg9TLxZg3Nsnrt~G~6QnQTA(QQ7iaK=Z;=vix0cBu|A~lsx2j|+D00BZUNZ)S+ z8=rIF;^BTEA|fIpB9bS>2(f*aQCll@M;JF4R|6fw4MtR6_kX*>GZ7Y?1aJZTZo9IM zTtILV@X`>Ti?HA}Fe1zz=Vtfc3?Pn&_Y?%T!KRmL-$*xaX7)HAZ1b=kK(!%cn>Vw? zxo<;l9@z9$G=%LtAjCL?)pUh@ox{Hcmp?u;o+K=1e&=pHC=5F976sJJ!E~S zLw45u;@ltZ-G3*wbffjZnd>q4!*|yQo0Hkc-VLMA19A1`)eb<2-CZ9PECJ^XFOIR% z2rqqjIrAA$lJ=`^S>BQzjn+MJw%ZeA2JrLO!{!Tl7hleNj!u)@bA51hns`O;E7z0j z(Rp9z;;$*LD?_PML_|bHL_|bHM5J8ratd7*l+G#iLVx&FOoaf#tEDz<-~6{Kdm2Ff zz4w|5ateEu-fXP5_g+&$PN5gxd;n;J{_io>B=dr93K&YrFr?B^ILQz1XEP&92yG9q;q}oehJV~D2iLwAf z6J-{cQGW;R{0000006^T?$`gOY#Cq#UTE764Ct=0qCt?P@e|95sk-MpTQ=yh6- zXO8D#X4W=hT|a0CG|h3wVh%&g?Z0I`*&ySla4&{O1S$PhI~T)b#*>5s1hS X+ZBR`4th!Y00000NkvXXu0mjfdR@JA diff --git a/Resources/Textures/Structures/Power/Generation/Tesla/energy_miniball.rsi/meta.json b/Resources/Textures/Structures/Power/Generation/Tesla/energy_miniball.rsi/meta.json index 029bc54b5a..d818915f87 100644 --- a/Resources/Textures/Structures/Power/Generation/Tesla/energy_miniball.rsi/meta.json +++ b/Resources/Textures/Structures/Power/Generation/Tesla/energy_miniball.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "taken from tgstation https://github.com/tgstation/tgstation/blob/HEAD/icons/obj/weapons/guns/projectiles.dmi", + "copyright": "taken from tgstation https://github.com/tgstation/tgstation/blob/HEAD/icons/obj/weapons/guns/projectiles.dmi, inhands by TiniestShark (github)", "size": { "x": 32, "y": 32 @@ -25,6 +25,66 @@ 0.08 ] ] - } + }, + { + "name": "tesla-inhand-left", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "tesla-inhand-right", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } ] -} \ No newline at end of file +} diff --git a/Resources/Textures/Structures/Power/Generation/Tesla/energy_miniball.rsi/tesla-inhand-left.png b/Resources/Textures/Structures/Power/Generation/Tesla/energy_miniball.rsi/tesla-inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..3e8260ea5901a678f390036c22f76c843845d24a GIT binary patch literal 1472 zcmYjQc{J1u6dvo4Cqs&O3=*PasT?6QLt}k4vPFc6N}jTfJ?4jcWi4AN(Rc`1MhImY zq-PsTll{reI~v*FWTr93Y;Sa4=XB4x_q+F>d++(~{n9U>>}B_<>;r*7vW^Z&S0N_v zxs;?Z_Q9?Yg&=})wYLG`$9`P}fg}K5Tlzj+NQE^R2OM`&Q*xFFGqGW3O7?u73ouB=4-Q z`H^^4c7o^e^N(H>%Ku(}pO5vlz~?hutWmF1|+p)HM9X9@y(g9nu64O(-Zmt_>Og^P?O^x=zdr^AF*-C`s!NAjmsEkk6{+4jPXc?gBd4Gb zJ8|#^`_i>IMGKjuf;NUvv-;)9aJ@(YcK5!|=W8?PJXuYF$q=@XGbJmwf-$gMrt4VE zO}_~(*lLt(eaJscov(_TGeKDMx2VkNGn!X%A3fp)W4d>3N+d>zu@M$trw(}K02(LM z^`aZNWlwvWSh)A?6N7UIPX@@tvvT}!Pae0!Y_<7#&3;kOsw5AvHHj*BCZdI|G#IpW ze{m52DUF1YVZfZR)QmR(@ZTu~|ewu&v?1P~pMa-6Rnz zXQ<(mR)5G#6E>M{zyf<+il8i=z1mKOBMH7Vs2<=-gK7a2FW`-D$MCD#-E4uPN3bv?M|E=iQSl_4N&z?98&V#g9$?ic<+&H!e4a{E|iD zF-_U?7=A^6kMd?+!_Y|_J7>Kf*3IOEp3bH0s!oN!n`#lObqe5JG2$4WaSTW*A>w|w zcHpEAhlgYiXByfpHVdX047OHyABTi4fW|D1T)@G>q;)P(r%yefXT zrv6dHYD9mKuooLvgcuoSk;DT~1da~GleaY}GbNtFODIoXT&f|6qbGzshcr2_)^q|zh{@_3Iv<2H z%AO*M5Y#tC;Oh1hNn@bLR7#g4?wU(9UY5I)#I6+(4L~aO6pLbfWBh->EFTOox*<`hM-7>Qj`N)x=br|9M3K4<$3fA2xFxf@VHw0w)RgQ*a6g)- za`}j9%`9|=qLe-)_4Lk|Y$N|Ho5*50xxN5Tp+x=jk3wMQ(S>cjm}*tXJ&0@^6zQLw zb?O0hT-3Jv>zke5PFYDVcsles{yB?CJu9LwbJK%cOgM0fF~`T9_x~|JpOj6x0YF@c zGT5PGi$t)6T3DOZ$YFyV96$uSMV1x?%JOBfLAH2p%iVh$)2trKVG_l90^4z6uWcqG z96`C-7txt&f6>4YzyOdBvis}lh0f5xMiK?f9h1xpbyQGl*wF%2tdb3DY1>jmczRWd z1t4kbV5L}7D{DsnZ7+Bmh)D$71AG~bOq<+K*}c0OsT!09!$J-3I)$Ewy7gKOKz086 z#V?FCVRQ?&wizQ|2X)72Z!Ss{<_P;Q{&rUX-Fv;Ieuo!3t@mL>QGKggPUqp&$YZ`n zp}1=kcZ{=EYKk#j+*9?R0UW2HI%DK_w_}4?DFs4@Ov}^Dvm*_r`zsSk2Wd=2O{IHO zts(*!J}+i92%WwSAH-(cfhXM+iVl^*3#>;oG#>Wy91YZ#{2D|Su=~iiQ4kV z=bp2Sig2Db(y=TRg_uyB(%hV!T=J~4lF8Ru9ZuJ?-GR2lCp!|OzoH>9aOgTvb$im` z)Y)ji@^qgW%~GkyUUwp60wrR+bBxn2q6uC(sbWz21fy#{s+&CL#>U_X=iG3K0<)ho zw2){t*(}6eDLcaY{)qL50z7kT-%TP0;t`M>bxNye`S~76_Mz9}Yq!B%;Oc}8$Wb(51=3quEjN&#=)F(dq{PBH z{!kSe@SsYD0WZckT3oi%v*=ynd-`J?bqTzJ!`ssSX3ko{^h+M NKpjIP9~_Oy_!pP Date: Fri, 27 Jun 2025 01:08:36 +0000 Subject: [PATCH 119/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f985491da7..f7f0cc2170 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: EmoGarbage404 - changes: - - message: Fixed artifacts sometimes having negative research values. - type: Fix - id: 8208 - time: '2025-04-17T03:14:17.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36642 - author: VerinSenpai changes: - message: You can now use actions within the action window. @@ -3881,3 +3874,13 @@ id: 8720 time: '2025-06-26T22:57:46.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38597 +- author: TiniestShark + changes: + - message: Added inhand sprites to all plushies/toys and the ability to wear (most) + of them in either your head or neck slot. + type: Add + - message: Nar'sie, Ratvar and Revenant plushies now require two hands to hold. + type: Tweak + id: 8721 + time: '2025-06-27T01:07:29.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38514 From d70589b36ff4189f1644db4a19f7b26b5077bbd9 Mon Sep 17 00:00:00 2001 From: Andrew Malcolm O'Neill <105134723+maland1@users.noreply.github.com> Date: Fri, 27 Jun 2025 03:03:23 +0100 Subject: [PATCH 120/191] Thief Guidebook Refresh (#38586) --- Resources/Locale/en-US/thief/backpack.ftl | 16 +++---- .../Prototypes/Catalog/thief_toolbox_sets.yml | 14 +++--- .../Guidebook/Antagonist/Thieves.xml | 25 ++++------ Resources/ServerInfo/Guidebook/Command.xml | 2 +- Resources/ServerInfo/Guidebook/Glossary.xml | 2 +- .../ServerInfo/Guidebook/SpaceStation14.xml | 4 +- Resources/ServerInfo/Guidebook/Survival.xml | 46 +++++++++---------- 7 files changed, 52 insertions(+), 57 deletions(-) diff --git a/Resources/Locale/en-US/thief/backpack.ftl b/Resources/Locale/en-US/thief/backpack.ftl index 962480e2e2..953542179c 100644 --- a/Resources/Locale/en-US/thief/backpack.ftl +++ b/Resources/Locale/en-US/thief/backpack.ftl @@ -1,4 +1,4 @@ -thief-backpack-window-title = thieving kit +thief-backpack-window-title = Thieving Kit thief-backpack-window-description = Inside are your tools of the trade, which will dissolve when you're ready. @@ -15,48 +15,48 @@ thief-backpack-button-deselect = Select [X] # Sets -thief-backpack-category-chameleon-name = chameleon kit +thief-backpack-category-chameleon-name = Chameleon Kit thief-backpack-category-chameleon-description = You are everyone and no one; you are a master of disguise. Includes: A full set of chameleon clothing, a chameleon projector, and an Agent ID. Disguise as anyone and anything. -thief-backpack-category-tools-name = breacher kit +thief-backpack-category-tools-name = Breacher Kit thief-backpack-category-tools-description = What's that sound? A lil' C4 knockin' at their door. No obstacle can stop you! Includes: Two C4, a multitool, remote signaller, jaws of life, advanced welder, engineering goggles, and insulated gloves. -thief-backpack-category-chemistry-name = anatomy kit +thief-backpack-category-chemistry-name = Anatomy Kit thief-backpack-category-chemistry-description = You've reached peak physical performance... with a little help. Includes: Storage implanter, DNA scrambler implanter, ephedrine bottle, syringe, empty shaker, and omega soap -thief-backpack-category-syndie-name = syndie kit +thief-backpack-category-syndie-name = Syndie Kit thief-backpack-category-syndie-description = Trinkets from a disavowed past, or stolen from a careless agent? You've made some connections. Whiskey, echo... Includes: An Emag, Access Breaker, Interdyne cigs, a Syndicate codeword, a Radio Jammer, a lighter and some strange red crystals. -thief-backpack-category-sleeper-name = sleeper kit +thief-backpack-category-sleeper-name = Sleeper Kit thief-backpack-category-sleeper-description = Until we close our eyes for good, use your illegal prescriptions to keep others asleep. Includes: Sleepy nitrous oxide tank, two nocturine bottles, and a hypopen. -thief-backpack-category-communicator-name = communicator kit +thief-backpack-category-communicator-name = Communicator Kit thief-backpack-category-communicator-description = Money is power, and secrets are money. Use your silver tongue and wealth to subvert the station. Includes: Master key for all station channels, a CyberSun pen, voice chameleon mask, and 20k spesos inside a briefcase. -thief-backpack-category-smuggler-name = smuggler kit +thief-backpack-category-smuggler-name = Smuggler Kit thief-backpack-category-smuggler-description = All thieves need somewhere to stash their goods in the dark. Don't forget to link your fulton. diff --git a/Resources/Prototypes/Catalog/thief_toolbox_sets.yml b/Resources/Prototypes/Catalog/thief_toolbox_sets.yml index a17cb128a7..79831bedc5 100644 --- a/Resources/Prototypes/Catalog/thief_toolbox_sets.yml +++ b/Resources/Prototypes/Catalog/thief_toolbox_sets.yml @@ -3,7 +3,7 @@ name: thief-backpack-category-chameleon-name description: thief-backpack-category-chameleon-description sprite: - sprite: /Textures/Clothing/OuterClothing/Misc/black_hoodie.rsi + sprite: Objects/Devices/chameleon_projector.rsi state: icon content: - ClothingBackpackChameleonFill @@ -16,8 +16,8 @@ name: thief-backpack-category-tools-name description: thief-backpack-category-tools-description sprite: - sprite: Objects/Tools/jaws_of_life.rsi - state: jaws_pry + sprite: Objects/Weapons/Bombs/c4.rsi + state: icon content: - WelderIndustrialAdvanced - ClothingEyesGlassesMeson @@ -77,8 +77,8 @@ name: thief-backpack-category-communicator-name description: thief-backpack-category-communicator-description sprite: - sprite: Objects/Tools/spy_device.rsi - state: icon + sprite: Objects/Devices/communication.rsi + state: old-radio content: - EncryptionKeyStationMaster - CyberPen @@ -91,8 +91,8 @@ name: thief-backpack-category-smuggler-name description: thief-backpack-category-smuggler-description sprite: - sprite: Clothing/Neck/Cloaks/void.rsi - state: icon + sprite: Objects/Tools/fulton.rsi + state: extraction_pack content: - InvisibleCrate - FultonBeacon diff --git a/Resources/ServerInfo/Guidebook/Antagonist/Thieves.xml b/Resources/ServerInfo/Guidebook/Antagonist/Thieves.xml index ec871851c6..511b6a752f 100644 --- a/Resources/ServerInfo/Guidebook/Antagonist/Thieves.xml +++ b/Resources/ServerInfo/Guidebook/Antagonist/Thieves.xml @@ -14,15 +14,10 @@ Unlike other antagonists, [bold]staying out of trouble is almost a requirement for you[/bold] because of your implant. You can only run if [color=#cb0000]Security[/color] picks a fight with you, and because you work alone you don't have any friends to pull you out of danger. - But against all odds, you'll sucessfully swipe what you want using determination, sleight of hand, a few tools and your [color=cyan]thieving gloves.[/color] + But against all odds, you'll successfully swipe what you want using determination, sleight of hand, a few tools and your [color=cyan]light fingers.[/color] - The pair of gloves you have give you a major advantage: [bold]the ability to take things off of people without them even noticing.[/bold] With a little practice, you can steal their wallet and keep up a conversation at the same time. - - They're also made of the highest quality [bold]holographic chameleon fibers[/bold] you could steal, but while they can take the appearance of insulated gloves you'll still need to find a real pair to keep your hands from burning. - - - - + Your years of experience have given you the ability to [bold]swiftly take things off another person's body without them even noticing.[/bold] + With a little practice, you can even steal their wallet and keep up a conversation at the same time. ## Tools of the Trade You've got two more aces up your stolen sleeves: your [color=cyan]beacon[/color] and your [color=cyan]satchel.[/color] @@ -42,14 +37,14 @@ - + - + - + @@ -65,10 +60,10 @@ Things that you may desire include but are not limited to: - - - - + + + + diff --git a/Resources/ServerInfo/Guidebook/Command.xml b/Resources/ServerInfo/Guidebook/Command.xml index 11696e4275..96fe99639e 100644 --- a/Resources/ServerInfo/Guidebook/Command.xml +++ b/Resources/ServerInfo/Guidebook/Command.xml @@ -42,7 +42,7 @@ The remote influences where people move around. Heads can lock personnel out of You also have access to your very own [color=#1b67a5]Command[/color] channel, like every other department. However, yours is particularly special as the people who can hear you have [color=#a4885c]entire departments[/color] at their fingertips. Use this to communicate with other heads and be a representative of what your workers want or need. -The [color=#1b67a5]Captain[/color] and the [color=#9fed58]Head of Personnel[/color] get access to the lucrative [color=#a4885c]master encryption key[/color], letting them tune into every radio channel on the station. This is extremly valuable, as you can coordinate between departments yourself. +The [color=#1b67a5]Captain[/color] and the [color=#9fed58]Head of Personnel[/color] get access to the lucrative [color=#a4885c]master encryption key[/color], letting them tune into every radio channel on the station. This is extremely valuable, as you can coordinate between departments yourself. [color=#a4885c]Don't waste this vital advantage![/color] diff --git a/Resources/ServerInfo/Guidebook/Glossary.xml b/Resources/ServerInfo/Guidebook/Glossary.xml index 835f964740..200fe67f64 100644 --- a/Resources/ServerInfo/Guidebook/Glossary.xml +++ b/Resources/ServerInfo/Guidebook/Glossary.xml @@ -125,7 +125,7 @@ In-Character way to refer to rounds. A shortening of the Singularity Engine. A Singulo can create infinite power for the station but is very dangerous. ## Singuloose -A Singularity that has grownth too much and breached it's containment. It will rip through the station causing massive damage. +A Singularity that has grown too much and breached its containment. It will rip through the station causing massive damage. ## Spacing An event which causes an area to lose air pressure, i.e. a hull breach. diff --git a/Resources/ServerInfo/Guidebook/SpaceStation14.xml b/Resources/ServerInfo/Guidebook/SpaceStation14.xml index 3955c8d0da..a59ee1b7c7 100644 --- a/Resources/ServerInfo/Guidebook/SpaceStation14.xml +++ b/Resources/ServerInfo/Guidebook/SpaceStation14.xml @@ -7,7 +7,7 @@ Rounds typically last from [color=cyan]45 to 90 minutes[/color], but players are ## Shuttle Terminal If you join in the middle of a shift, you will likely spawn on the [color=lime]shuttle terminal[/color]. -Here you can grab drinks, conversate with other players, and wait for the shuttle to take you to the station. +Here you can grab drinks, converse with other players, and wait for the shuttle to take you to the station. Screens on the walls will give you an ETA for the shuttle. When it arrives, simply walk on to be transported to the station. @@ -38,7 +38,7 @@ The round doesn't officially end until you reach [bold]Central Command[/bold] -If you need to leave a round early, you can do so through a [color=lime]cyrogenic sleep unit[/color]. +If you need to leave a round early, you can do so through a [color=lime]cryogenic sleep unit[/color]. Simply going inside and leaving the game will store your belongings and ensure that they are not stolen or misused. ## More Info diff --git a/Resources/ServerInfo/Guidebook/Survival.xml b/Resources/ServerInfo/Guidebook/Survival.xml index eeb0d7b4d6..aa3151fba2 100644 --- a/Resources/ServerInfo/Guidebook/Survival.xml +++ b/Resources/ServerInfo/Guidebook/Survival.xml @@ -1,26 +1,26 @@ - -# Survival -It is generally wise to avoid situations that will cause you harm, because medical can only heal you so much with limited chemicals and medical items and especially when there's nuclear operatives on your doorstep. + +# Survival +It is generally wise to avoid situations that will cause you harm, because medical can only heal you so much with limited chemicals and medical items and especially when there's nuclear operatives on your doorstep. -## Identifying your situation -Your PDA contains both a vessel name and list of crew currently active in your vessel, a reminder for your name if needed, and your assigned job for the shift, allowing you to quickly assess the situation. +## Identifying your situation +Your PDA contains both a vessel name and list of crew currently active in your vessel, a reminder for your name if needed, and your assigned job for the shift, allowing you to quickly assess the situation. -## Emergency Treatment -In the event of a serious emergency, there're a few things you can do to help ensure your long-term survival, including: -- If entering critical condition, use the emergency medipen from your emergency box, it'll make sure you don't end up unable to do anything. Emergency medipens can also be used to revive people currently in crit on the floor, and to prolong the amount of time you have to deal with poisons/etc. +## Emergency Treatment +In the event of a serious emergency, there are a few things you can do to help ensure your long-term survival, including: +- If entering critical condition, use the emergency medipen from your emergency box, it'll make sure you don't end up unable to do anything. Emergency medipens can also be used to revive people currently in crit on the floor, and to prolong the amount of time you have to deal with poisons/etc. - - + + -- Your emergency box contains a breath mask and oxygen tank, which can help you survive longer in a spacing situation. If you're one of the slimepeople, be aware that nitrogen replaces oxygen for you and that your emergency box will start with a red tank of nitrogen replacing the standard blue/yellow oxygen tank. +- Your emergency box contains a breath mask and oxygen tank, which can help you survive longer in a spacing situation. If you're one of the slimepeople, be aware that nitrogen replaces oxygen for you and that your emergency box will start with a red tank of nitrogen replacing the standard blue/yellow oxygen tank. - - - + + + -- If actively bleeding out, or simply wishing to prepare, it's possible to slice up cloth items with a knife or other sharp object and use the resulting cloth to create gauze in the crafting menu to stem bleeding with, however cloth alone can be used to less of a degree in emergencies. +- If actively bleeding out, or simply wishing to prepare, it's possible to slice up cloth items with a knife or other sharp object and use the resulting cloth to create gauze in the crafting menu to stem bleeding with, however cloth alone can be used to less of a degree in emergencies. @@ -28,11 +28,11 @@ In the event of a serious emergency, there're a few things you can do to help en ## Other Various Tips -- In lieu of an actual health analyzer, simply examining yourself and using the detailed examine is a good way to figure out what wounds you have. -- If going blind, carrots are another way to treat the issue (as they contain Oculine, the chemistry drug used to treat blindness) should they be available. -- Well-made meals (cooked food not from a vending machine) is generally much better for your overall health and can help heal smaller wounds more quickly. -- Simple bed rest can allow some wounds to close up on their own. Medical beds are best for this, providing a sterile surface and support for all damaged body parts, but any bed works. -- Actually sleeping on a bed boosts your healing rate even farther. -- Meals that contain proteins, such as meat, will tremendously help your body's condition after severe bloodloss. It's still advised to visit a doctor and get treated though. - - +- In lieu of an actual health analyzer, simply examining yourself and using the detailed examine is a good way to figure out what wounds you have. +- If going blind, carrots are another way to treat the issue (as they contain Oculine, the chemistry drug used to treat blindness) should they be available. +- Well-made meals (cooked food not from a vending machine) is generally much better for your overall health and can help heal smaller wounds more quickly. +- Simple bed rest can allow some wounds to close up on their own. Medical beds are best for this, providing a sterile surface and support for all damaged body parts, but any bed works. +- Actually sleeping on a bed boosts your healing rate even farther. +- Meals that contain proteins, such as meat, will tremendously help your body's condition after severe bloodloss. It's still advised to visit a doctor and get treated though. + + From e916135a9c6b9ac8fbea993adc70c976096be57e Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 27 Jun 2025 02:04:30 +0000 Subject: [PATCH 121/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f7f0cc2170..79fea7696f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: VerinSenpai - changes: - - message: You can now use actions within the action window. - type: Add - id: 8209 - time: '2025-04-17T10:08:09.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/35642 - author: ElectroJr changes: - message: Fixed some intense explosion types using incorrect overlay sprites. @@ -3884,3 +3877,11 @@ id: 8721 time: '2025-06-27T01:07:29.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38514 +- author: maland1 + changes: + - message: Updating Thief guidebook page including making guidebook and thieving + satchel images more consistent + type: Tweak + id: 8722 + time: '2025-06-27T02:03:24.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38586 From d7d83bd87c590a559035c23a41ebca27cba23117 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:33:43 -0700 Subject: [PATCH 122/191] RespiratorSystem Cleanup (#38572) * Respirator Debodied * Forgot about alerts (also respirator testa and events) * Fix Urist eating air and not giving it back * Stop nuke ops from taking in a breath then taking in a second breath causing them to get a headache from carbon dioxide poisoning and failing TryStopNukeOpsFromConstantlyFailing(); * Consts are smelly, * Actually we don't need to raise the entity, just the component * Don't forget to remove the unused code today, said me yesterday * Remove all fallbacks * Debody that too --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Body/Components/RespiratorComponent.cs | 24 +++ .../Body/Systems/InternalsSystem.cs | 3 +- Content.Server/Body/Systems/LungSystem.cs | 3 + .../Body/Systems/RespiratorSystem.cs | 182 +++++++++++++----- 4 files changed, 158 insertions(+), 54 deletions(-) diff --git a/Content.Server/Body/Components/RespiratorComponent.cs b/Content.Server/Body/Components/RespiratorComponent.cs index a81062362a..19585e9f00 100644 --- a/Content.Server/Body/Components/RespiratorComponent.cs +++ b/Content.Server/Body/Components/RespiratorComponent.cs @@ -1,4 +1,6 @@ using Content.Server.Body.Systems; +using Content.Shared.Alert; +using Content.Shared.Atmos; using Content.Shared.Chat.Prototypes; using Content.Shared.Damage; using Robust.Shared.Prototypes; @@ -9,6 +11,28 @@ namespace Content.Server.Body.Components [RegisterComponent, Access(typeof(RespiratorSystem))] public sealed partial class RespiratorComponent : Component { + /// + /// Gas container for this entity + /// + [DataField] + public GasMixture Air = new() + { + Volume = 6, // 6 liters, the average lung capacity for a human according to Google + Temperature = Atmospherics.NormalBodyTemperature + }; + + /// + /// Volume of our breath in liters + /// + [DataField] + public float BreathVolume = Atmospherics.BreathVolume; + + /// + /// How much of the gas we inhale is metabolized? Value range is (0, 1] + /// + [DataField] + public float Ratio = 1.0f; + /// /// The next time that this body will inhale or exhale. /// diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index 93dedf5fff..72c91fe2be 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Atmos.EntitySystems; +using Content.Server.Body.Components; using Content.Server.Popups; using Content.Shared.Alert; using Content.Shared.Atmos; @@ -58,7 +59,7 @@ public sealed class InternalsSystem : SharedInternalsSystem if (AreInternalsWorking(ent)) { var gasTank = Comp(ent.Comp.GasTankEntity!.Value); - args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume); + args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), args.Respirator.BreathVolume); // TODO: Should listen to gas tank updates instead I guess? _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } diff --git a/Content.Server/Body/Systems/LungSystem.cs b/Content.Server/Body/Systems/LungSystem.cs index 273a8466ca..fdc071c5f1 100644 --- a/Content.Server/Body/Systems/LungSystem.cs +++ b/Content.Server/Body/Systems/LungSystem.cs @@ -63,6 +63,9 @@ public sealed class LungSystem : EntitySystem _solutionContainerSystem.UpdateChemicals(lung.Solution.Value); } + /* This should really be moved to somewhere in the atmos system and modernized, + so that other systems, like CondenserSystem, can use it. + */ private void GasToReagent(GasMixture gas, Solution solution) { foreach (var gasId in Enum.GetValues()) diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index 1b4cf4d698..bc57108720 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -50,6 +50,8 @@ public sealed class RespiratorSystem : EntitySystem SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnUnpaused); SubscribeLocalEvent(OnApplyMetabolicMultiplier); + SubscribeLocalEvent(OnGasInhaled); + SubscribeLocalEvent(OnGasExhaled); } private void OnMapInit(Entity ent, ref MapInitEvent args) @@ -77,18 +79,18 @@ public sealed class RespiratorSystem : EntitySystem if (_mobState.IsDead(uid)) continue; - UpdateSaturation(uid, -(float) respirator.UpdateInterval.TotalSeconds, respirator); + UpdateSaturation(uid, -(float)respirator.UpdateInterval.TotalSeconds, respirator); if (!_mobState.IsIncapacitated(uid)) // cannot breathe in crit. { switch (respirator.Status) { case RespiratorStatus.Inhaling: - Inhale(uid, body); + Inhale(uid); respirator.Status = RespiratorStatus.Exhaling; break; case RespiratorStatus.Exhaling: - Exhale(uid, body); + Exhale(uid); respirator.Status = RespiratorStatus.Inhaling; break; } @@ -99,7 +101,10 @@ public sealed class RespiratorSystem : EntitySystem if (_gameTiming.CurTime >= respirator.LastGaspEmoteTime + respirator.GaspEmoteCooldown) { respirator.LastGaspEmoteTime = _gameTiming.CurTime; - _chat.TryEmoteWithChat(uid, respirator.GaspEmote, ChatTransmitRange.HideChat, ignoreActionBlocker: true); + _chat.TryEmoteWithChat(uid, + respirator.GaspEmote, + ChatTransmitRange.HideChat, + ignoreActionBlocker: true); } TakeSuffocationDamage((uid, respirator)); @@ -112,68 +117,54 @@ public sealed class RespiratorSystem : EntitySystem } } - public void Inhale(EntityUid uid, BodyComponent? body = null) + public bool Inhale(Entity entity) { - if (!Resolve(uid, ref body, logMissing: false)) - return; - - var organs = _bodySystem.GetBodyOrganEntityComps((uid, body)); + if (!Resolve(entity, ref entity.Comp, logMissing: false)) + return false; // Inhale gas - var ev = new InhaleLocationEvent(); - RaiseLocalEvent(uid, ref ev); + var ev = new InhaleLocationEvent + { + Respirator = entity.Comp, + }; + RaiseLocalEvent(entity, ref ev); - ev.Gas ??= _atmosSys.GetContainingMixture(uid, excite: true); + ev.Gas ??= _atmosSys.GetContainingMixture(entity.Owner, excite: true); if (ev.Gas is null) { - return; + return false; } - var actualGas = ev.Gas.RemoveVolume(Atmospherics.BreathVolume); + var gas = ev.Gas.RemoveVolume(entity.Comp.BreathVolume); - var lungRatio = 1.0f / organs.Count; - var gas = organs.Count == 1 ? actualGas : actualGas.RemoveRatio(lungRatio); - foreach (var (organUid, lung, _) in organs) - { - // Merge doesn't remove gas from the giver. - _atmosSys.Merge(lung.Air, gas); - _lungSystem.GasToReagent(organUid, lung); - } + var inhaleEv = new InhaledGasEvent(gas); + RaiseLocalEvent(entity, ref inhaleEv); + + return inhaleEv.Handled && inhaleEv.Succeeded; } - public void Exhale(EntityUid uid, BodyComponent? body = null) + public void Exhale(Entity entity) { - if (!Resolve(uid, ref body, logMissing: false)) + if (!Resolve(entity, ref entity.Comp, logMissing: false)) return; - var organs = _bodySystem.GetBodyOrganEntityComps((uid, body)); - // exhale gas var ev = new ExhaleLocationEvent(); - RaiseLocalEvent(uid, ref ev, broadcast: false); + RaiseLocalEvent(entity, ref ev, broadcast: false); if (ev.Gas is null) { - ev.Gas = _atmosSys.GetContainingMixture(uid, excite: true); + ev.Gas = _atmosSys.GetContainingMixture(entity.Owner, excite: true); // Walls and grids without atmos comp return null. I guess it makes sense to not be able to exhale in walls, // but this also means you cannot exhale on some grids. ev.Gas ??= GasMixture.SpaceGas; } - var outGas = new GasMixture(ev.Gas.Volume); - foreach (var (organUid, lung, _) in organs) - { - _atmosSys.Merge(outGas, lung.Air); - lung.Air.Clear(); - - if (_solutionContainerSystem.ResolveSolution(organUid, lung.SolutionName, ref lung.Solution)) - _solutionContainerSystem.RemoveAllSolution(lung.Solution.Value); - } - - _atmosSys.Merge(ev.Gas, outGas); + var exhaleEv = new ExhaledGasEvent(ev.Gas); + RaiseLocalEvent(entity, ref exhaleEv); } /// -public abstract partial class SharedStatusEffectsSystem : EntitySystem +public sealed partial class StatusEffectsSystem : EntitySystem { - [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly IComponentFactory _compFactory = default!; - [Dependency] private readonly INetManager _net = default!; private EntityQuery _containerQuery; private EntityQuery _effectQuery; @@ -32,20 +27,15 @@ public abstract partial class SharedStatusEffectsSystem : EntitySystem InitializeRelay(); - SubscribeLocalEvent(OnStatusEffectApplied); - SubscribeLocalEvent(OnStatusEffectRemoved); - - SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnStatusContainerInit); + SubscribeLocalEvent(OnStatusContainerShutdown); + SubscribeLocalEvent(OnEntityInserted); + SubscribeLocalEvent(OnEntityRemoved); _containerQuery = GetEntityQuery(); _effectQuery = GetEntityQuery(); } - private void OnGetState(Entity ent, ref ComponentGetState args) - { - args.State = new StatusEffectContainerComponentState(GetNetEntitySet(ent.Comp.ActiveStatusEffects)); - } - public override void Update(float frameTime) { base.Update(frameTime); @@ -70,15 +60,59 @@ public abstract partial class SharedStatusEffectsSystem : EntitySystem } } - private void AddStatusEffectTime(EntityUid effect, TimeSpan delta) + private void OnStatusContainerInit(Entity ent, ref ComponentInit args) { - if (!_effectQuery.TryComp(effect, out var effectComp)) + ent.Comp.ActiveStatusEffects = + _container.EnsureContainer(ent, StatusEffectContainerComponent.ContainerId); + // We show the contents of the container to allow status effects to have visible sprites. + ent.Comp.ActiveStatusEffects.ShowContents = true; + } + + private void OnStatusContainerShutdown(Entity ent, ref ComponentShutdown args) + { + if (ent.Comp.ActiveStatusEffects is { } container) + _container.ShutdownContainer(container); + } + + private void OnEntityInserted(Entity ent, ref EntInsertedIntoContainerMessage args) + { + if (args.Container.ID != StatusEffectContainerComponent.ContainerId) return; - effectComp.EndEffectTime += delta; - Dirty(effect, effectComp); + if (!TryComp(args.Entity, out var statusComp)) + return; - ShowAlertIfNeeded(effectComp); + // Make sure AppliedTo is set correctly so events can rely on it + if (statusComp.AppliedTo != ent) + { + statusComp.AppliedTo = ent; + Dirty(args.Entity, statusComp); + } + + var ev = new StatusEffectAppliedEvent(ent); + RaiseLocalEvent(args.Entity, ref ev); + } + + private void OnEntityRemoved(Entity ent, ref EntRemovedFromContainerMessage args) + { + if (args.Container.ID != StatusEffectContainerComponent.ContainerId) + return; + + if (!TryComp(args.Entity, out var statusComp)) + return; + + var ev = new StatusEffectRemovedEvent(ent); + RaiseLocalEvent(args.Entity, ref ev); + + // Clear AppliedTo after events are handled so event handlers can use it. + if (statusComp.AppliedTo == null) + return; + + // Why not just delete it? Well, that might end up being best, but this + // could theoretically allow for moving status effects from one entity + // to another. That might be good to have for polymorphs or something. + statusComp.AppliedTo = null; + Dirty(args.Entity, statusComp); } private void SetStatusEffectTime(EntityUid effect, TimeSpan? duration) @@ -97,8 +131,6 @@ public abstract partial class SharedStatusEffectsSystem : EntitySystem effectComp.EndEffectTime = _timing.CurTime + duration; Dirty(effect, effectComp); - - ShowAlertIfNeeded(effectComp); } private void UpdateStatusEffectTime(EntityUid effect, TimeSpan? duration) @@ -122,24 +154,6 @@ public abstract partial class SharedStatusEffectsSystem : EntitySystem } Dirty(effect, effectComp); - - ShowAlertIfNeeded(effectComp); - } - - - private void OnStatusEffectApplied(Entity ent, ref StatusEffectAppliedEvent args) - { - StatusEffectComponent statusEffect = ent; - ShowAlertIfNeeded(statusEffect); - } - - private void OnStatusEffectRemoved(Entity ent, ref StatusEffectRemovedEvent args) - { - if (ent.Comp.AppliedTo is null) - return; - - if (ent.Comp is { AppliedTo: not null, Alert: not null }) - _alerts.ClearAlert(ent.Comp.AppliedTo.Value, ent.Comp.Alert.Value); } private bool CanAddStatusEffect(EntityUid uid, EntProtoId effectProto) @@ -147,7 +161,7 @@ public abstract partial class SharedStatusEffectsSystem : EntitySystem if (!_proto.TryIndex(effectProto, out var effectProtoData)) return false; - if (!effectProtoData.TryGetComponent(out var effectProtoComp, _compFactory)) + if (!effectProtoData.TryGetComponent(out var effectProtoComp, Factory)) return false; if (!_whitelist.CheckBoth(uid, effectProtoComp.Blacklist, effectProtoComp.Whitelist)) @@ -181,43 +195,50 @@ public abstract partial class SharedStatusEffectsSystem : EntitySystem if (!CanAddStatusEffect(target, effectProto)) return false; - var container = EnsureComp(target); + EnsureComp(target); + + // And only if all checks passed we spawn the effect + if (!PredictedTrySpawnInContainer(effectProto, + target, + StatusEffectContainerComponent.ContainerId, + out var effect)) + return false; - //And only if all checks passed we spawn the effect - var effect = PredictedSpawnAttachedTo(effectProto, Transform(target).Coordinates); - _transform.SetParent(effect, target); if (!_effectQuery.TryComp(effect, out var effectComp)) return false; statusEffect = effect; - - if (duration != null) - effectComp.EndEffectTime = _timing.CurTime + duration; - - container.ActiveStatusEffects.Add(effect); - effectComp.AppliedTo = target; - Dirty(target, container); - Dirty(effect, effectComp); - - var ev = new StatusEffectAppliedEvent(target); - RaiseLocalEvent(effect, ref ev); + SetStatusEffectEndTime((effect.Value, effectComp), _timing.CurTime + duration); return true; } - private void ShowAlertIfNeeded(StatusEffectComponent effectComp) + private void AddStatusEffectTime(EntityUid effect, TimeSpan delta) { - if (effectComp is { AppliedTo: not null, Alert: not null }) - { - (TimeSpan, TimeSpan)? cooldown = effectComp.EndEffectTime is null - ? null - : (_timing.CurTime, effectComp.EndEffectTime.Value); - _alerts.ShowAlert( - effectComp.AppliedTo.Value, - effectComp.Alert.Value, - cooldown: cooldown - ); - } + if (!_effectQuery.TryComp(effect, out var effectComp)) + return; + + // If we don't have an end time set, we want to just make the status effect end in delta time from now. + SetStatusEffectEndTime((effect, effectComp), (effectComp.EndEffectTime ?? _timing.CurTime) + delta); + } + + private void SetStatusEffectEndTime(Entity ent, TimeSpan? endTime) + { + if (!_effectQuery.Resolve(ent, ref ent.Comp)) + return; + + if (ent.Comp.EndEffectTime == endTime) + return; + + ent.Comp.EndEffectTime = endTime; + + if (ent.Comp.AppliedTo is not { } appliedTo) + return; // Not much we can do! + + var ev = new StatusEffectEndTimeUpdatedEvent(appliedTo, endTime); + RaiseLocalEvent(ent, ref ev); + + Dirty(ent); } } @@ -238,3 +259,11 @@ public readonly record struct StatusEffectRemovedEvent(EntityUid Target); /// @@ -199,14 +190,15 @@ public sealed class RespiratorSystem : EntitySystem if (!Resolve(ent, ref ent.Comp)) return false; - var ev = new InhaleLocationEvent(); - RaiseLocalEvent(ent, ref ev); - - var gas = ev.Gas ?? _atmosSys.GetContainingMixture(ent.Owner); - if (gas == null) + if (!Inhale(ent)) return false; - return CanMetabolizeGas(ent, gas); + // If we don't have a body we can't be poisoned by gas, yet... + var success = TryMetabolizeGas((ent, ent.Comp)); + + // Don't keep that gas in our lungs lest it poisons a poor nuclear operative. + Exhale(ent); + return success; } /// @@ -224,7 +216,7 @@ public sealed class RespiratorSystem : EntitySystem gas = new GasMixture(gas); var lungRatio = 1.0f / organs.Count; - gas.Multiply(MathF.Min(lungRatio * gas.Volume/Atmospherics.BreathVolume, lungRatio)); + gas.Multiply(MathF.Min(lungRatio * gas.Volume / ent.Comp.BreathVolume, lungRatio)); var solution = _lungSystem.GasToReagent(gas); float saturation = 0; @@ -238,6 +230,71 @@ public sealed class RespiratorSystem : EntitySystem return saturation > ent.Comp.UpdateInterval.TotalSeconds; } + public bool TryInhaleGasToBody(Entity entity, GasMixture gas) + { + if (!Resolve(entity, ref entity.Comp)) + return false; + + var organs = _bodySystem.GetBodyOrganEntityComps((entity, entity.Comp)); + if (organs.Count == 0) + return false; + + var lungRatio = 1.0f / organs.Count; + var splitGas = organs.Count == 1 ? gas : gas.RemoveRatio(lungRatio); + foreach (var (organUid, lung, _) in organs) + { + // Merge doesn't remove gas from the giver. + _atmosSys.Merge(lung.Air, splitGas); + _lungSystem.GasToReagent(organUid, lung); + } + + return true; + } + + public void RemoveGasFromBody(Entity ent, GasMixture gas) + { + var outGas = new GasMixture(gas.Volume); + + var organs = _bodySystem.GetBodyOrganEntityComps((ent, ent.Comp)); + if (organs.Count == 0) + return; + + foreach (var (organUid, lung, _) in organs) + { + _atmosSys.Merge(outGas, lung.Air); + lung.Air.Clear(); + + if (_solutionContainerSystem.ResolveSolution(organUid, lung.SolutionName, ref lung.Solution)) + _solutionContainerSystem.RemoveAllSolution(lung.Solution.Value); + } + + _atmosSys.Merge(gas, outGas); + } + + /// + /// Tries to safely metabolize the current solutions in a body's lungs. + /// + private bool TryMetabolizeGas(Entity ent) + { + if (!Resolve(ent, ref ent.Comp2)) + return false; + + var organs = _bodySystem.GetBodyOrganEntityComps((ent, null)); + if (organs.Count == 0) + return false; + + float saturation = 0; + foreach (var organ in organs) + { + var solution = _lungSystem.GasToReagent(organ.Comp1.Air); + saturation += GetSaturation(solution, organ.Owner, out var toxic); + if (toxic) + return false; + } + + return saturation > ent.Comp1.UpdateInterval.TotalSeconds; + } + /// /// Get the amount of saturation that would be generated if the lung were to metabolize the given solution. /// @@ -301,6 +358,8 @@ public sealed class RespiratorSystem : EntitySystem if (ent.Comp.SuffocationCycles == 2) _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} started suffocating"); + _damageableSys.TryChangeDamage(ent, ent.Comp.Damage, interruptsDoAfters: false); + if (ent.Comp.SuffocationCycles >= ent.Comp.SuffocationCycleThreshold) { // TODO: This is not going work with multiple different lungs, if that ever becomes a possibility @@ -310,8 +369,6 @@ public sealed class RespiratorSystem : EntitySystem _alertsSystem.ShowAlert(ent, entity.Comp1.Alert); } } - - _damageableSys.TryChangeDamage(ent, ent.Comp.Damage, interruptsDoAfters: false); } private void StopSuffocation(Entity ent) @@ -319,18 +376,17 @@ public sealed class RespiratorSystem : EntitySystem if (ent.Comp.SuffocationCycles >= 2) _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} stopped suffocating"); + _damageableSys.TryChangeDamage(ent, ent.Comp.DamageRecovery); + // TODO: This is not going work with multiple different lungs, if that ever becomes a possibility var organs = _bodySystem.GetBodyOrganEntityComps((ent, null)); foreach (var entity in organs) { _alertsSystem.ClearAlert(ent, entity.Comp1.Alert); } - - _damageableSys.TryChangeDamage(ent, ent.Comp.DamageRecovery); } - public void UpdateSaturation(EntityUid uid, float amount, - RespiratorComponent? respirator = null) + public void UpdateSaturation(EntityUid uid, float amount, RespiratorComponent? respirator = null) { if (!Resolve(uid, ref respirator, false)) return; @@ -362,10 +418,30 @@ public sealed class RespiratorSystem : EntitySystem ent.Comp.MaxSaturation /= args.Multiplier; ent.Comp.MinSaturation /= args.Multiplier; } + + private void OnGasInhaled(Entity entity, ref InhaledGasEvent args) + { + args.Handled = true; + + args.Succeeded = TryInhaleGasToBody((entity, entity.Comp), args.Gas); + } + + private void OnGasExhaled(Entity entity, ref ExhaledGasEvent args) + { + args.Handled = true; + + RemoveGasFromBody(entity, args.Gas); + } } [ByRefEvent] -public record struct InhaleLocationEvent(GasMixture? Gas); +public record struct InhaleLocationEvent(GasMixture? Gas, RespiratorComponent Respirator); [ByRefEvent] public record struct ExhaleLocationEvent(GasMixture? Gas); + +[ByRefEvent] +public record struct InhaledGasEvent(GasMixture Gas, bool Handled = false, bool Succeeded = false); + +[ByRefEvent] +public record struct ExhaledGasEvent(GasMixture Gas, bool Handled = false); From 1d2907fb4eace37776d93e5e36905c8d1ee0bb82 Mon Sep 17 00:00:00 2001 From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Date: Fri, 27 Jun 2025 04:37:00 -0700 Subject: [PATCH 123/191] Fix the sensor monitoring console forcing a GC every 3 seconds (#38146) * Optimize sensor monitoring window graph drawing * Add shared static Vector2 pool for all GraphView instances * Address requested changes * remove lock --- .../SensorMonitoringWindow.xaml.cs | 48 +++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/Content.Client/SensorMonitoring/SensorMonitoringWindow.xaml.cs b/Content.Client/SensorMonitoring/SensorMonitoringWindow.xaml.cs index 717ab40765..e833e7d7a9 100644 --- a/Content.Client/SensorMonitoring/SensorMonitoringWindow.xaml.cs +++ b/Content.Client/SensorMonitoring/SensorMonitoringWindow.xaml.cs @@ -25,6 +25,29 @@ public sealed partial class SensorMonitoringWindow : FancyWindow, IComputerWindo private TimeSpan _retentionTime; private readonly Dictionary _sensorData = new(); + /// + /// A shared array used to store vertices for drawing graphs in . + /// Prevents excessive allocations by reusing the same array across multiple graph views. + /// This effectively makes it so that each has its own pooled array. + /// + private Vector2[] _sharedVertices = []; + + /// + /// Retrieves a shared array of vertices, ensuring that it has at least the requested size. + /// Assigns a new array of the requested size if the current shared array is smaller than the requested size. + /// + /// The minimum number of vertices required in the shared array. + /// An array of representing the shared vertices. + /// This does not prevent other threads from accessing the same shared pool. + public Vector2[] GetSharedVertices(int requestedSize) + { + if (_sharedVertices.Length < requestedSize) + { + _sharedVertices = new Vector2[requestedSize]; + } + return _sharedVertices; + } + public SensorMonitoringWindow() { RobustXamlLoader.Load(this); @@ -145,7 +168,7 @@ public sealed partial class SensorMonitoringWindow : FancyWindow, IComputerWindo } }); - Asdf.AddChild(new GraphView(stream.Samples, startTime, curTime, maxValue * 1.1f) { MinHeight = 150 }); + Asdf.AddChild(new GraphView(stream.Samples, startTime, curTime, maxValue * 1.1f, this) { MinHeight = 150 }); Asdf.AddChild(new PanelContainer { StyleClasses = { StyleBase.ClassLowDivider } }); } } @@ -197,31 +220,37 @@ public sealed partial class SensorMonitoringWindow : FancyWindow, IComputerWindo private readonly TimeSpan _startTime; private readonly TimeSpan _curTime; private readonly float _maxY; + private readonly SensorMonitoringWindow _parentWindow; - public GraphView(Queue samples, TimeSpan startTime, TimeSpan curTime, float maxY) + public GraphView(Queue samples, + TimeSpan startTime, + TimeSpan curTime, + float maxY, + SensorMonitoringWindow parentWindow) { _samples = samples; _startTime = startTime; _curTime = curTime; _maxY = maxY; RectClipContent = true; + _parentWindow = parentWindow; } protected override void Draw(DrawingHandleScreen handle) { base.Draw(handle); - var window = (float) (_curTime - _startTime).TotalSeconds; - - // TODO: omg this is terrible don't fucking hardcode this size to something uncached huge omfg. - var vertices = new Vector2[25000]; + var window = (float)(_curTime - _startTime).TotalSeconds; var countVtx = 0; var lastPoint = new Vector2(float.NaN, float.NaN); + var requiredVertices = 6 * (_samples.Count - 1); + + var vertices = _parentWindow.GetSharedVertices(requiredVertices); foreach (var (time, sample) in _samples) { - var relTime = (float) (time - _startTime).TotalSeconds; + var relTime = (float)(time - _startTime).TotalSeconds; var posY = PixelHeight - (sample / _maxY) * PixelHeight; var posX = (relTime / window) * PixelWidth; @@ -242,8 +271,9 @@ public sealed partial class SensorMonitoringWindow : FancyWindow, IComputerWindo lastPoint = newPoint; } - - handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, vertices.AsSpan(0, countVtx), Color.White.WithAlpha(0.1f)); + handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, + vertices.AsSpan(0, countVtx), + Color.White.WithAlpha(0.1f)); } } } From cf4a883f111d57b48e37be1339808065b7ff4535 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Fri, 27 Jun 2025 05:00:20 -0700 Subject: [PATCH 124/191] New Status Effects API Overload (#38617) * I love API changes * Update Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs * fix --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../StatusEffectNew/StatusEffectSystem.API.cs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs index 8f34f97930..14b02dd9db 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs @@ -18,23 +18,27 @@ public abstract partial class SharedStatusEffectsSystem /// If True, the effect duration time will be reset and reapplied. If False, the effect duration time will be overlaid with the existing one. /// In the other case, the effect will either be added for the specified time or its time will be extended for the specified time. /// + /// The EntityUid of the status effect we have just created or null if it doesn't exist. public bool TryAddStatusEffect( EntityUid target, EntProtoId effectProto, + out EntityUid? statusEffect, TimeSpan? duration = null, bool resetCooldown = false ) { - if (TryGetStatusEffect(target, effectProto, out var existedEffect)) + statusEffect = null; + if (TryGetStatusEffect(target, effectProto, out var existingEffect)) { + statusEffect = existingEffect; //We don't need to add the effect if it already exists if (duration is null) return true; if (resetCooldown) - SetStatusEffectTime(existedEffect.Value, duration.Value); + SetStatusEffectTime(existingEffect.Value, duration.Value); else - AddStatusEffectTime(existedEffect.Value, duration.Value); + AddStatusEffectTime(existingEffect.Value, duration.Value); return true; } @@ -46,6 +50,7 @@ public abstract partial class SharedStatusEffectsSystem //And only if all checks passed we spawn the effect var effect = PredictedSpawnAttachedTo(effectProto, Transform(target).Coordinates); + statusEffect = effect; _transform.SetParent(effect, target); if (!_effectQuery.TryComp(effect, out var effectComp)) return false; @@ -64,6 +69,20 @@ public abstract partial class SharedStatusEffectsSystem return true; } + /// + /// An overload of + /// that doesn't return a status effect EntityUid. + /// + public bool TryAddStatusEffect( + EntityUid target, + EntProtoId effectProto, + TimeSpan? duration = null, + bool resetCooldown = false + ) + { + return TryAddStatusEffect(target, effectProto, out _, duration, resetCooldown); + } + /// /// Attempting to remove a status effect from an entity. /// Returns True if the status effect existed on the entity and was successfully removed, and False in otherwise. From 5bc778d42e2bd397d219837d5192d2510d4583ac Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 27 Jun 2025 15:03:16 +0200 Subject: [PATCH 125/191] Fix silicons looking at their laws crashing the server (#38623) --- Content.Server/Silicons/Laws/SiliconLawSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Silicons/Laws/SiliconLawSystem.cs b/Content.Server/Silicons/Laws/SiliconLawSystem.cs index 8dfc8d51f8..444732b2b3 100644 --- a/Content.Server/Silicons/Laws/SiliconLawSystem.cs +++ b/Content.Server/Silicons/Laws/SiliconLawSystem.cs @@ -270,7 +270,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem }; foreach (var law in proto.Laws) { - laws.Laws.Add(_prototype.Index(law)); + laws.Laws.Add(_prototype.Index(law).ShallowClone()); } laws.ObeysTo = proto.ObeysTo; From 6be925ab914ce92de4efc4737cd3384e75038b1a Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Fri, 27 Jun 2025 18:39:49 +0200 Subject: [PATCH 126/191] fix RCD ghosts not disappearing when changing hand to empty (#38622) --- Content.Client/RCD/RCDConstructionGhostSystem.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Content.Client/RCD/RCDConstructionGhostSystem.cs b/Content.Client/RCD/RCDConstructionGhostSystem.cs index 8ec980268b..fcbe3aece7 100644 --- a/Content.Client/RCD/RCDConstructionGhostSystem.cs +++ b/Content.Client/RCD/RCDConstructionGhostSystem.cs @@ -1,9 +1,7 @@ using Content.Client.Hands.Systems; -using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.RCD; using Content.Shared.RCD.Components; -using Content.Shared.RCD.Systems; using Robust.Client.Placement; using Robust.Client.Player; using Robust.Shared.Enums; @@ -11,14 +9,18 @@ using Robust.Shared.Prototypes; namespace Content.Client.RCD; +/// +/// System for handling structure ghost placement in places where RCD can create objects. +/// public sealed class RCDConstructionGhostSystem : EntitySystem { + private const string PlacementMode = nameof(AlignRCDConstruction); + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlacementManager _placementManager = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly HandsSystem _hands = default!; - - private string _placementMode = typeof(AlignRCDConstruction).Name; + private Direction _placementDirection = default; public override void Update(float frameTime) @@ -38,8 +40,7 @@ public sealed class RCDConstructionGhostSystem : EntitySystem if (_playerManager.LocalSession?.AttachedEntity is not { } player) return; - if (!_hands.TryGetActiveItem(player, out var heldEntity)) - return; + var heldEntity = _hands.GetActiveItem(player); if (!TryComp(heldEntity, out var rcd)) { @@ -66,7 +67,7 @@ public sealed class RCDConstructionGhostSystem : EntitySystem var newObjInfo = new PlacementInformation { MobUid = heldEntity.Value, - PlacementOption = _placementMode, + PlacementOption = PlacementMode, EntityType = prototype.Prototype, Range = (int) Math.Ceiling(SharedInteractionSystem.InteractionRange), IsTile = (prototype.Mode == RcdMode.ConstructTile), From ad2616a53f30397da68459ca17864ae39ddb517a Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 27 Jun 2025 16:40:56 +0000 Subject: [PATCH 127/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 79fea7696f..e9bc2eb7e0 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: ElectroJr - changes: - - message: Fixed some intense explosion types using incorrect overlay sprites. - type: Fix - id: 8210 - time: '2025-04-17T10:12:14.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36644 - author: Beck Thompson, SlamBamActionman changes: - message: Fixed trash bag visuals. The appearance will now update correctly depending @@ -3885,3 +3878,10 @@ id: 8722 time: '2025-06-27T02:03:24.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38586 +- author: slarticodefast + changes: + - message: Fixed RCD ghosts not disappearing when dropping the RCD. + type: Fix + id: 8723 + time: '2025-06-27T16:39:49.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38622 From bb116e32191f32cd71fb80cfd8e1e4374c1ea818 Mon Sep 17 00:00:00 2001 From: Red <96445749+TheShuEd@users.noreply.github.com> Date: Fri, 27 Jun 2025 21:19:04 +0300 Subject: [PATCH 128/191] Refactor SeeingRainbows to new status effect system (#38620) --- .../Drowsiness/DrowsinessOverlay.cs | 23 ++------- Content.Client/Drugs/DrugOverlaySystem.cs | 47 +++++++++---------- Content.Client/Drugs/RainbowOverlay.cs | 17 ++++--- .../Drugs/SeeingRainbowsComponent.cs | 5 +- .../StatusEffectNew/StatusEffectSystem.API.cs | 37 ++++++++++++++- .../Prototypes/Entities/Mobs/Species/base.yml | 1 - .../Entities/StatusEffects/misc.yml | 8 ++++ .../Reagents/Consumable/Drink/alcohol.yml | 5 +- Resources/Prototypes/Reagents/cleaning.yml | 7 ++- Resources/Prototypes/Reagents/gases.yml | 10 ++-- Resources/Prototypes/Reagents/medicine.yml | 13 +++-- Resources/Prototypes/Reagents/narcotics.yml | 29 +++++------- Resources/Prototypes/Reagents/toxins.yml | 5 +- Resources/Prototypes/status_effects.yml | 3 -- 14 files changed, 110 insertions(+), 100 deletions(-) diff --git a/Content.Client/Drowsiness/DrowsinessOverlay.cs b/Content.Client/Drowsiness/DrowsinessOverlay.cs index 9705aa7313..1687216b3e 100644 --- a/Content.Client/Drowsiness/DrowsinessOverlay.cs +++ b/Content.Client/Drowsiness/DrowsinessOverlay.cs @@ -1,7 +1,5 @@ -using Content.Shared.Bed.Sleep; using Content.Shared.Drowsiness; using Content.Shared.StatusEffectNew; -using Content.Shared.StatusEffectNew.Components; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Shared.Enums; @@ -23,8 +21,6 @@ public sealed class DrowsinessOverlay : Overlay public override bool RequestScreenTexture => true; private readonly ShaderInstance _drowsinessShader; - private EntityQuery _statusQuery; - public float CurrentPower = 0.0f; private const float PowerDivisor = 250.0f; @@ -34,9 +30,9 @@ public sealed class DrowsinessOverlay : Overlay public DrowsinessOverlay() { IoCManager.InjectDependencies(this); + _statusEffects = _sysMan.GetEntitySystem(); - _statusQuery = _entityManager.GetEntityQuery(); _drowsinessShader = _prototypeManager.Index("Drowsiness").InstanceUnique(); } @@ -47,22 +43,11 @@ public sealed class DrowsinessOverlay : Overlay if (playerEntity == null) return; - if (!_statusEffects.TryEffectsWithComp(playerEntity, out var drowsinessEffects)) + if (!_statusEffects.TryGetEffectsEndTimeWithComp(playerEntity, out var endTime)) return; - TimeSpan? remainingTime = TimeSpan.Zero; - foreach (var (_, _, statusEffectComp) in drowsinessEffects) - { - if (statusEffectComp.EndEffectTime > remainingTime) - remainingTime = statusEffectComp.EndEffectTime; - } - - if (remainingTime is null) - return; - - var curTime = _timing.CurTime; - var timeLeft = (float)(remainingTime - curTime).Value.TotalSeconds; - + endTime ??= TimeSpan.MaxValue; + var timeLeft = (float)(endTime - _timing.CurTime).Value.TotalSeconds; CurrentPower += 8f * (0.5f * timeLeft - CurrentPower) * args.DeltaSeconds / (timeLeft + 1); } diff --git a/Content.Client/Drugs/DrugOverlaySystem.cs b/Content.Client/Drugs/DrugOverlaySystem.cs index 608ae02842..e156e616b8 100644 --- a/Content.Client/Drugs/DrugOverlaySystem.cs +++ b/Content.Client/Drugs/DrugOverlaySystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Drugs; +using Content.Shared.StatusEffectNew; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Shared.Player; @@ -17,49 +18,47 @@ public sealed class DrugOverlaySystem : EntitySystem private RainbowOverlay _overlay = default!; - public static string RainbowKey = "SeeingRainbows"; - public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnApplied); + SubscribeLocalEvent(OnRemoved); - SubscribeLocalEvent(OnPlayerAttached); - SubscribeLocalEvent(OnPlayerDetached); + SubscribeLocalEvent>(OnPlayerAttached); + SubscribeLocalEvent>(OnPlayerDetached); _overlay = new(); } - private void OnPlayerAttached(EntityUid uid, SeeingRainbowsComponent component, LocalPlayerAttachedEvent args) + private void OnRemoved(Entity ent, ref StatusEffectRemovedEvent args) { - _overlayMan.AddOverlay(_overlay); - } + if (_player.LocalEntity != args.Target) + return; - private void OnPlayerDetached(EntityUid uid, SeeingRainbowsComponent component, LocalPlayerDetachedEvent args) - { _overlay.Intoxication = 0; _overlay.TimeTicker = 0; _overlayMan.RemoveOverlay(_overlay); } - private void OnInit(EntityUid uid, SeeingRainbowsComponent component, ComponentInit args) + private void OnApplied(Entity ent, ref StatusEffectAppliedEvent args) { - if (_player.LocalEntity == uid) - { - _overlay.Phase = _random.NextFloat(MathF.Tau); // random starting phase for movement effect - _overlayMan.AddOverlay(_overlay); - } + if (_player.LocalEntity != args.Target) + return; + + _overlay.Phase = _random.NextFloat(MathF.Tau); // random starting phase for movement effect + _overlayMan.AddOverlay(_overlay); } - private void OnShutdown(EntityUid uid, SeeingRainbowsComponent component, ComponentShutdown args) + private void OnPlayerAttached(Entity ent, ref StatusEffectRelayedEvent args) { - if (_player.LocalEntity == uid) - { - _overlay.Intoxication = 0; - _overlay.TimeTicker = 0; - _overlayMan.RemoveOverlay(_overlay); - } + _overlayMan.AddOverlay(_overlay); + } + + private void OnPlayerDetached(Entity ent, ref StatusEffectRelayedEvent args) + { + _overlay.Intoxication = 0; + _overlay.TimeTicker = 0; + _overlayMan.RemoveOverlay(_overlay); } } diff --git a/Content.Client/Drugs/RainbowOverlay.cs b/Content.Client/Drugs/RainbowOverlay.cs index 4e0bed1264..bfb3815649 100644 --- a/Content.Client/Drugs/RainbowOverlay.cs +++ b/Content.Client/Drugs/RainbowOverlay.cs @@ -1,6 +1,6 @@ using Content.Shared.CCVar; using Content.Shared.Drugs; -using Content.Shared.StatusEffect; +using Content.Shared.StatusEffectNew; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Shared.Configuration; @@ -17,6 +17,8 @@ public sealed class RainbowOverlay : Overlay [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IEntitySystemManager _sysMan = default!; + [Dependency] private readonly IGameTiming _timing = default!; + private readonly SharedStatusEffectsSystem _statusEffects = default!; public override OverlaySpace Space => OverlaySpace.WorldSpace; public override bool RequestScreenTexture => true; @@ -37,6 +39,8 @@ public sealed class RainbowOverlay : Overlay { IoCManager.InjectDependencies(this); + _statusEffects = _sysMan.GetEntitySystem(); + _rainbowShader = _prototypeManager.Index("Rainbow").InstanceUnique(); _config.OnValueChanged(CCVars.ReducedMotion, OnReducedMotionChanged, invokeImmediately: true); } @@ -54,18 +58,13 @@ public sealed class RainbowOverlay : Overlay if (playerEntity == null) return; - if (!_entityManager.HasComponent(playerEntity) - || !_entityManager.TryGetComponent(playerEntity, out var status)) + if (!_statusEffects.TryGetEffectsEndTimeWithComp(playerEntity, out var endTime)) return; - var statusSys = _sysMan.GetEntitySystem(); - if (!statusSys.TryGetTime(playerEntity.Value, DrugOverlaySystem.RainbowKey, out var time, status)) - return; - - var timeLeft = (float)(time.Value.Item2 - time.Value.Item1).TotalSeconds; + endTime ??= TimeSpan.MaxValue; + var timeLeft = (float)(endTime - _timing.CurTime).Value.TotalSeconds; TimeTicker += args.DeltaSeconds; - if (timeLeft - TimeTicker > timeLeft / 16f) { Intoxication += (timeLeft - Intoxication) * args.DeltaSeconds / 16f; diff --git a/Content.Shared/Drugs/SeeingRainbowsComponent.cs b/Content.Shared/Drugs/SeeingRainbowsComponent.cs index 221599c26e..2f22b1dc6a 100644 --- a/Content.Shared/Drugs/SeeingRainbowsComponent.cs +++ b/Content.Shared/Drugs/SeeingRainbowsComponent.cs @@ -3,7 +3,8 @@ using Robust.Shared.GameStates; namespace Content.Shared.Drugs; /// -/// Exists for use as a status effect. Adds a shader to the client that scales with the effect duration. +/// Adds a shader to the client that scales with the effect duration. +/// Use only in conjunction with , on the status effect entity. /// [RegisterComponent, NetworkedComponent] -public sealed partial class SeeingRainbowsComponent : Component { } +public sealed partial class SeeingRainbowsStatusEffectComponent : Component; diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs index 14b02dd9db..2d1ec89c9d 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs @@ -269,7 +269,7 @@ public abstract partial class SharedStatusEffectsSystem foreach (var effect in container.ActiveStatusEffects) { - if (!TryComp(effect, out var statusComp)) + if (!_effectQuery.TryComp(effect, out var statusComp)) continue; if (TryComp(effect, out var comp)) @@ -279,6 +279,39 @@ public abstract partial class SharedStatusEffectsSystem } } - return effects != null; + return effects is not null; + } + + /// + /// Helper function for calculating how long it takes for all effects with a particular component to disappear. Useful for overlays. + /// + /// An entity from which status effects are checked. + /// The farthest end time of effects with this component is returned. Can be null if one of the effects is infinite. + /// True if effects with the specified component were found, or False if there are no such effects. + public bool TryGetEffectsEndTimeWithComp(EntityUid? target, out TimeSpan? endTime) where T : IComponent + { + endTime = _timing.CurTime; + if (!_containerQuery.TryComp(target, out var container)) + return false; + + foreach (var effect in container.ActiveStatusEffects) + { + if (!HasComp(effect)) + continue; + + if (!_effectQuery.TryComp(effect, out var statusComp)) + continue; + + if (statusComp.EndEffectTime is null) + { + endTime = null; + return true; //This effect never ends, so we return null at endTime, but return true that there is time. + } + + if (statusComp.EndEffectTime > endTime) + endTime = statusComp.EndEffectTime; + } + + return endTime is not null; } } diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index b93ed915e2..5bf9a9ede0 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -123,7 +123,6 @@ - KnockedDown - SlowedDown - Stutter - - SeeingRainbows - Electrocution - Drunk - SlurredSpeech diff --git a/Resources/Prototypes/Entities/StatusEffects/misc.yml b/Resources/Prototypes/Entities/StatusEffects/misc.yml index bccdea773f..bb2aa9de93 100644 --- a/Resources/Prototypes/Entities/StatusEffects/misc.yml +++ b/Resources/Prototypes/Entities/StatusEffects/misc.yml @@ -42,3 +42,11 @@ name: drowsiness components: - type: DrowsinessStatusEffect + +# Adds drugs overlay +- type: entity + parent: MobStatusEffectBase + id: StatusEffectSeeingRainbow + name: hallucinations + components: + - type: SeeingRainbowsStatusEffect \ No newline at end of file diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml b/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml index adb9aa28e4..741108cca1 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml @@ -27,12 +27,11 @@ - !type:AdjustReagent reagent: Ethanol amount: 0.05 - - !type:GenericStatusEffect + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow conditions: - !type:ReagentThreshold min: 10 - key: SeeingRainbows - component: SeeingRainbows type: Add time: 5 refresh: false diff --git a/Resources/Prototypes/Reagents/cleaning.yml b/Resources/Prototypes/Reagents/cleaning.yml index b9a0af01de..33c0a0e86f 100644 --- a/Resources/Prototypes/Reagents/cleaning.yml +++ b/Resources/Prototypes/Reagents/cleaning.yml @@ -107,11 +107,10 @@ metabolisms: Narcotic: effects: - - !type:GenericStatusEffect - key: SeeingRainbows - component: SeeingRainbows - type: Add + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow time: 5 + type: Add refresh: false Drink: effects: diff --git a/Resources/Prototypes/Reagents/gases.yml b/Resources/Prototypes/Reagents/gases.yml index 1087d7bad4..4a97cb8b16 100644 --- a/Resources/Prototypes/Reagents/gases.yml +++ b/Resources/Prototypes/Reagents/gases.yml @@ -402,9 +402,8 @@ damage: types: Cellular: 1 - - !type:GenericStatusEffect - key: SeeingRainbows - component: SeeingRainbows + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow type: Add time: 15 refresh: false @@ -439,13 +438,12 @@ damage: types: Cellular: 0.5 - - !type:GenericStatusEffect + - !type:ModifyStatusEffect conditions: - !type:ReagentThreshold reagent: Frezon min: 1 - key: SeeingRainbows - component: SeeingRainbows + effectProto: StatusEffectSeeingRainbow type: Add time: 500 refresh: false diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml index 2873372b52..dfecb112d5 100644 --- a/Resources/Prototypes/Reagents/medicine.yml +++ b/Resources/Prototypes/Reagents/medicine.yml @@ -749,8 +749,8 @@ key: KnockedDown time: 3.0 type: Remove - - !type:GenericStatusEffect - key: SeeingRainbows + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow time: 15.0 type: Remove @@ -1323,12 +1323,11 @@ damage: types: Poison: 2 - - !type:GenericStatusEffect + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow conditions: - !type:ReagentThreshold min: 30 - key: SeeingRainbows - component: SeeingRainbows type: Add time: 8 refresh: false @@ -1398,8 +1397,8 @@ key: Jitter time: 4.0 type: Remove - - !type:GenericStatusEffect - key: SeeingRainbows + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow time: 10.0 type: Remove - !type:AdjustReagent diff --git a/Resources/Prototypes/Reagents/narcotics.yml b/Resources/Prototypes/Reagents/narcotics.yml index aff87479b1..ed8b8acc65 100644 --- a/Resources/Prototypes/Reagents/narcotics.yml +++ b/Resources/Prototypes/Reagents/narcotics.yml @@ -198,11 +198,10 @@ metabolisms: Narcotic: effects: - - !type:GenericStatusEffect - key: SeeingRainbows - component: SeeingRainbows - type: Add + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow time: 16 + type: Add refresh: false - type: reagent @@ -236,11 +235,10 @@ damage: types: Poison: 2 - - !type:GenericStatusEffect - key: SeeingRainbows - component: SeeingRainbows - type: Add + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow time: 10 + type: Add refresh: false - !type:ChemVomit # Vomiting is a symptom of brain damage probability: 0.05 @@ -258,9 +256,8 @@ metabolisms: Narcotic: effects: - - !type:GenericStatusEffect - key: SeeingRainbows - component: SeeingRainbows + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow type: Add time: 5 refresh: false @@ -276,9 +273,8 @@ metabolisms: Narcotic: effects: - - !type:GenericStatusEffect - key: SeeingRainbows - component: SeeingRainbows + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow type: Add time: 5 refresh: false @@ -467,9 +463,8 @@ conditions: - !type:ReagentThreshold max: 20 - - !type:GenericStatusEffect - key: SeeingRainbows - component: SeeingRainbows + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow type: Add time: 5 refresh: false diff --git a/Resources/Prototypes/Reagents/toxins.yml b/Resources/Prototypes/Reagents/toxins.yml index 6560f05995..96761ae778 100644 --- a/Resources/Prototypes/Reagents/toxins.yml +++ b/Resources/Prototypes/Reagents/toxins.yml @@ -339,9 +339,8 @@ metabolisms: Poison: effects: - - !type:GenericStatusEffect - key: SeeingRainbows - component: SeeingRainbows + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow type: Add time: 10 refresh: false diff --git a/Resources/Prototypes/status_effects.yml b/Resources/Prototypes/status_effects.yml index e98dd4df02..7c96b88c22 100644 --- a/Resources/Prototypes/status_effects.yml +++ b/Resources/Prototypes/status_effects.yml @@ -25,9 +25,6 @@ - type: statusEffect id: AllCaps -- type: statusEffect - id: SeeingRainbows - - type: statusEffect id: Electrocution From 69674a17106a4aad92488be221c7e24c8775ffe4 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 27 Jun 2025 20:23:12 +0200 Subject: [PATCH 129/191] Make Review Requested label get applied to all opened PRs (#38625) It previously only applied automatically to PRs that have reviews requested, which happens if the PR touches files owned by code owners. Now it applies to *all* opened PRs. --- .github/workflows/labeler-needsreview.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler-needsreview.yml b/.github/workflows/labeler-needsreview.yml index 819b34b7bb..d3373ce91d 100644 --- a/.github/workflows/labeler-needsreview.yml +++ b/.github/workflows/labeler-needsreview.yml @@ -2,7 +2,7 @@ on: pull_request_target: - types: [review_requested] + types: [review_requested, opened] jobs: add_label: From a09e47e857dc961d7fccc12836e7333eed655824 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 27 Jun 2025 15:18:27 -0400 Subject: [PATCH 130/191] Fix vocalization emotes (#38627) --- Content.Shared/Speech/Components/VocalComponent.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Speech/Components/VocalComponent.cs b/Content.Shared/Speech/Components/VocalComponent.cs index 0f62a39d45..3a32a54bb3 100644 --- a/Content.Shared/Speech/Components/VocalComponent.cs +++ b/Content.Shared/Speech/Components/VocalComponent.cs @@ -19,9 +19,9 @@ public sealed partial class VocalComponent : Component /// Emote sounds prototype id for each sex (not gender). /// Entities without considered to be . /// - [DataField("sounds", customTypeSerializer: typeof(PrototypeIdValueDictionarySerializer))] + [DataField] [AutoNetworkedField] - public Dictionary? Sounds; + public Dictionary>? Sounds; [DataField("screamId", customTypeSerializer: typeof(PrototypeIdSerializer))] [AutoNetworkedField] From a120730291471f587c94a65721b7b6486e6c7ce4 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 27 Jun 2025 22:32:29 +0200 Subject: [PATCH 131/191] Update submodule to 264.0.0 (#38629) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index f9d0dd551a..56eda3ea92 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit f9d0dd551a9df5e9b1cab7771e809538b262c347 +Subproject commit 56eda3ea92913e60c27aa029ba7877c8559dfe9d From e9c90fe66dec0ed9579847807d9babe934a66bae Mon Sep 17 00:00:00 2001 From: 96flo <163569033+96flo@users.noreply.github.com> Date: Sat, 28 Jun 2025 01:28:38 +0300 Subject: [PATCH 132/191] HoP's beret (#38601) * added HoP beret * added beret to loadout * added craft to machine * added beret to hop ddresser * Updated the authorship * Change on request * added 4-spaced * Hot fix of dresser --- .../Catalog/Fills/Lockers/dressers.yml | 1 + .../Entities/Clothing/Head/hats.yml | 17 ++++++++++ .../Jobs/Command/head_of_personnel.yml | 5 +++ .../Prototypes/Loadouts/loadout_groups.yml | 1 + .../Recipes/Lathes/Packs/clothing.yml | 1 + .../Prototypes/Recipes/Lathes/clothing.yml | 5 +++ .../beret_hop.rsi/equipped-HELMET-hamster.png | Bin 0 -> 861 bytes .../Hats/beret_hop.rsi/equipped-HELMET.png | Bin 0 -> 902 bytes .../Clothing/Head/Hats/beret_hop.rsi/icon.png | Bin 0 -> 415 bytes .../Head/Hats/beret_hop.rsi/inhand-left.png | Bin 0 -> 695 bytes .../Head/Hats/beret_hop.rsi/inhand-right.png | Bin 0 -> 789 bytes .../Head/Hats/beret_hop.rsi/meta.json | 30 ++++++++++++++++++ 12 files changed, 60 insertions(+) create mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET-hamster.png create mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/meta.json diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml b/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml index e111b51cff..237b8a4748 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml @@ -50,6 +50,7 @@ - type: StorageFill contents: - id: ClothingHeadHatHopcap + - id: ClothingHeadHatBeretHop - id: ClothingOuterWinterHoP - id: ClothingEyesGlasses - id: ClothingNeckCloakHop diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index ec7801802b..26e0c6aba2 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -1360,3 +1360,20 @@ path: /Audio/Items/flashlight_on.ogg soundDeactivate: path: /Audio/Items/flashlight_off.ogg + +- type: entity + parent: ClothingHeadBase + id: ClothingHeadHatBeretHop + name: head of personnel's beret + description: A dark blue beret with a ruby inserted in the center, for true connoisseurs of bureaucracy! + components: + - type: Sprite + sprite: Clothing/Head/Hats/beret_hop.rsi + - type: Clothing + sprite: Clothing/Head/Hats/beret_hop.rsi + - type: Tag + tags: + - HamsterWearable + - ClothMade + - Recyclable + - WhitelistChameleon \ No newline at end of file diff --git a/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml b/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml index 45223bea14..0855100e61 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml @@ -35,6 +35,11 @@ equipment: head: ClothingHeadHatHopcap +- type: loadout + id: HoPBeret + equipment: + head: ClothingHeadHatBeretHop + # Neck - type: loadout id: HoPCloak diff --git a/Resources/Prototypes/Loadouts/loadout_groups.yml b/Resources/Prototypes/Loadouts/loadout_groups.yml index 39b6be2b04..b3c8aa8129 100644 --- a/Resources/Prototypes/Loadouts/loadout_groups.yml +++ b/Resources/Prototypes/Loadouts/loadout_groups.yml @@ -153,6 +153,7 @@ minLimit: 0 loadouts: - HoPHead + - HoPBeret - type: loadoutGroup id: HoPJumpsuit diff --git a/Resources/Prototypes/Recipes/Lathes/Packs/clothing.yml b/Resources/Prototypes/Recipes/Lathes/Packs/clothing.yml index aa95781729..808f1c680a 100644 --- a/Resources/Prototypes/Recipes/Lathes/Packs/clothing.yml +++ b/Resources/Prototypes/Recipes/Lathes/Packs/clothing.yml @@ -33,6 +33,7 @@ - ClothingUniformJumpskirtCapFormalDress # HoP - ClothingHeadHatHopcap + - ClothingHeadHatBeretHop - ClothingUniformJumpsuitHoP - ClothingUniformJumpskirtHoP # Generic diff --git a/Resources/Prototypes/Recipes/Lathes/clothing.yml b/Resources/Prototypes/Recipes/Lathes/clothing.yml index c5f81153e2..cf6e082b64 100644 --- a/Resources/Prototypes/Recipes/Lathes/clothing.yml +++ b/Resources/Prototypes/Recipes/Lathes/clothing.yml @@ -810,6 +810,11 @@ id: ClothingHeadHatHopcap result: ClothingHeadHatHopcap +- type: latheRecipe + parent: BaseCommandHatRecipe + id: ClothingHeadHatBeretHop + result: ClothingHeadHatBeretHop + - type: latheRecipe parent: BaseCommandHatRecipe id: ClothingHeadHatQMsoft diff --git a/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET-hamster.png b/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET-hamster.png new file mode 100644 index 0000000000000000000000000000000000000000..b55441dd0200112d64aeb49b73363a558b8b50b4 GIT binary patch literal 861 zcmV-j1ETziP)Px&7fD1xRCt{2n$K<$Q543%!wd|}3>0ZF!G&V$q8mY|?yQOFLl}J$;}f`ZZ{jnk z%P#6d7fnTI#EnL2bRovZwzTw*1H)VwQ%aEN^4SXLOGEVUE{ktu(RzhnJg+s!jlfU9K1d+Fgx!CX>I`Z+OfM8jijfX zw(Wf{y8{5KmNj^QQ#l~4^|qTEz`|20Evj_nA79$xx3|jP2 zvafUo6wM?-075vvL>?Pljrp1e|KdhGMWKj}@Wd4zAKvWA^zn2RI73JsJaB#ukcJHi zT0qdS0YM808a5zk0YM{wD=dLx>Yn@9jnlgk^269?z}OpbqA8$Xz6Kn;)#>bRLD;tE zF|_1l-?r6Q|9_{}J{$_c?zEt)Q3x&WIc^tU21L?nUk7^}_IkZImAbnEinrKH4$ zULs$)I4uxQVfNQI%K!XKh#GF2PdsNp)mD1-`R;!O%me`7ac#-34r%T+*YNQ6eHg{N z*eWgHPST{;4T!SU7PDxQh#TbC~{c3Va<1qkIuvDRuri@86< zoT1U47WOrm=P6K#X@u2kui;Y(IZdH_SQ%N_e5TbrwOGDRf_W`M?+Mb55U? n8HQmPhG7_nVHk#C&Le*S@*URI3?Lay00000NkvXXu0mjfITU{1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..1b491f4c1e01f3517a915758c74236b902386ee6 GIT binary patch literal 902 zcmV;119|+3P)Px&KuJVFRCt{2noV!iMi|F`W3Sg{Hx5Z?BsK@SNgyGrSaE0&cTR{4UxjNwK(BlV zdf?Xb0Z?x}utB^@gcaSC3TdkbX+g>6#f?Xfz?7fT*sInHC)psxKNJ0UcFU%(Qg;cmf(tXf#=h;!^RA1b84KT|9tH zV0(k#?%n36KYoF!*<%-BB^=Ym2jTw3!J0lAP1<4IIDVz*#N|dy5l`sB+c&WC6o8wT zuIX#NE&#d83kUb|;&b=di95vnqCQ7;+MqCav=nx7E&|Lppyjnp4e82u$3@t;8v_vMP z++exWR(hNY6A6m10JI1Sfx!y<&-fwV6p9@D$jHw4SKwR231B+`+c*JiCtw>Vfb9fq z;{>psfNh)rwiB?8(O%$VPG49g2!f$&{Xd@-wpWSyzd^hBeva>lNGYk+#*uQPw69VE zOrE>U=XUbr)AOa0=)ZVecy6tJaLZi{M@f=Ryb|lVumImpFYXVr%skQ|#G|K=#E;BD z`DQjJ{vF^1u$_QyoB*~Hu#FSIb^^9>0@zN#HckNB2^;~ZzQ(xJW@zcum?8tR;b!cW zz`SK7dw&_ne>6F_A9$xJOQptzbYL9cSb(K!th>Y`qX+Ii_~(_n63Aoin?0JG+i%ei z)?j`D*82dw>~+-wsZo3*0S+sk!J3JyJ#G%(8O^```d5CwaZ3+IMs?hHM}(5Wwp z$CD_74^+w&e;gRU1Q*gZA%xG?=4wTFQR;i1aWVgw;vEi$!{Kl^91e%W;cz${4u`|x caGWeY0FgZS$LjglR{#J207*qoM6N<$f*Mz(od5s; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/icon.png b/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..09a41f29640f6515cbfa07f62c7fcb6f7970545e GIT binary patch literal 415 zcmV;Q0bu@#P)Px$SxH1eR9J=Wlg~;5Q5431cQj|h3KGUr2t-A+g}W9$LEE09hv)%%h+d+$JwfE6 zMGFlbrIkWLfu)S7b6Z%b{h51>7D4y7y@&IC=R4mX62ypjRW*^yrgFS40{@d*5ZR!>V;ycu)V;xGM~IDiw^ z>Z_H2=OiWBGHkow3&Zl{zh$5u-G=6wG4_9a#5 zAE7^(#ky!>&Hi+7RoCTow+ujO`Yh^-w#t86C>_v z$omh%bCU9-XkpS9!Nf-f6GU{Kh$RSuDKZFU@G)vg?}GQ&xDDbuI%n(cVJu&!EpWMY znQw#H#jK$2Z5PZ^Wcs?5uRL_Gv-xK5@_UZov*$I>ZO;Ec_Z(q46nKD(v24xe{U2D9L^vNOtEBX-WUB~LC?9Vt84MDl$r#XTv&{FLbN9Y-BMzZsjXVBVz$7;;oF}Et^Lhu=l4xAU&Fj)LY>gA z$X|hKmrbmdro9eb`nqA0t3mL6bq9vDh)_@Y)4jGz0wMZLr^F5}IK))Keu0+qN9(PwRi&r&X{m#%ph!d(#bz2^43>Qyy8D@6?It?M(BGerw#3d_1Eh&xUNn|v6} zI-jSDKPY&0F1;u2ysf$B@$Dn)PctbmNU&zw?D_NT0r~rK3=V<5!%8Pv9?a; z=NC-S-S{NJY{*}L-pB}+k5!OoyRO#dIv W|6@FrYYH$WGI+ZBxvXPx%&q+ioGvoZ{_ZeHs48Sl9 z!!QiPFbu;m40FaH0GOPdMlv@F0-=x|l)*tac79Xc+K1D3&YeUW5@{1wbrXiB0px&0 zKOAchB9RFt$f~S{nlFTgrL5VO!_3|v0s!gMQCwVT#&!RoLV8eQXav~Z2CP~$UIgT| zjWrUrO4Ee6vXnI?k}1IG1Jc=Myzm{;Gr^DWJ`~gEB{F%p(MbO3!UNo2d;%m>c$2@_ z=4!7Ad-s=fLvHebn?vQ7douxGXat2+dv)so@LmkM$)~gKq;d%WaQEs>cT}TYv$tSG zSOJwE@4{VRf4@C8I`yxjGkFvNiN?|g{7b-dtX#qb_DBc=cD)Kn2@)ElW5dXgg%j8T zbK(wuetg#T{Ne2~2_nO`ZOf4of&|veaPRu%*_LJPp##9Uy+xnOB?$nNR3#_!VviKd z-?*^3Lb{&7>Sx!8D`e3JUw^X##_cUKlGy~%2LZsbTds5J8QJ+pLNkDr*%B9laR>iT zQalJ6aOPkGR0~iI8=zW%YS;kP0#w5Ws1~3aHbAui)vy7_9T@e00jfBb0eWsUY8}ep zC7@b>YS;kP0#u`S9&&PCKY!TLp1pl9im&MjJ+FXZ@<+wuLQf4SzIuFQUtCQ%^UnY_ zK(zqXbo~}6IJ*)FZ@LEH0GOKxUOZ~sf(GLICg^CuVLyUO0|V~vp|_*#XKr2wO^9pq zKo=G8HkCQ_078@Xub8*L08)pgpOy!5EUfT^NW@F`LsQC=TFbu;m%nA4lk`l^Y T=SO$+00000NkvXXu0mjfLLX%( literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/meta.json b/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/meta.json new file mode 100644 index 0000000000..b5ca3514c5 --- /dev/null +++ b/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/meta.json @@ -0,0 +1,30 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Sprited by upnostnote (Discord), resprite by 96flo (Discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "equipped-HELMET-hamster", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} \ No newline at end of file From 7f530bb7ad5f14f99a6f41e7ad0c2d1fc01e0111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Lindert?= Date: Sat, 28 Jun 2025 02:10:52 +0200 Subject: [PATCH 133/191] patchfor broken NetworkConfiguratorLinkMenu (#38632) fix --- .../NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs b/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs index c627dc2ce8..a316b9e34a 100644 --- a/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs +++ b/Content.Client/NetworkConfigurator/NetworkConfiguratorLinkMenu.xaml.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Numerics; using Content.Client.UserInterface.Controls; using Content.Shared.DeviceLinking; @@ -15,7 +15,7 @@ namespace Content.Client.NetworkConfigurator; [GenerateTypedNameReferences] public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow { - private readonly IPrototypeManager _prototypeManager = null!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private const string PanelBgColor = "#202023"; From 948ecc82795dad244b494e58afa8bc3e7a3c05b6 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 27 Jun 2025 20:13:24 -0400 Subject: [PATCH 134/191] Fix solutions flickering when transferring contents (#34838) * Use Solution clones when applying SolutionComponent states * Revert "Use Solution clones when applying SolutionComponent states" This reverts commit 013fd111cf92b22562e00f98a7aaa49bc4b4ed62. * Make Solution implement ICloneable and rename Clone method. * Copy CanReact value when cloning a Solution * Convert to IRobustCloneable --- Content.Shared/Chemistry/Components/Solution.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Chemistry/Components/Solution.cs b/Content.Shared/Chemistry/Components/Solution.cs index c65ba0e80e..9622ccae30 100644 --- a/Content.Shared/Chemistry/Components/Solution.cs +++ b/Content.Shared/Chemistry/Components/Solution.cs @@ -15,7 +15,7 @@ namespace Content.Shared.Chemistry.Components /// [Serializable, NetSerializable] [DataDefinition] - public sealed partial class Solution : IEnumerable, ISerializationHooks + public sealed partial class Solution : IEnumerable, ISerializationHooks, IRobustCloneable { // This is a list because it is actually faster to add and remove reagents from // a list than a dictionary, though contains-reagent checks are slightly slower, @@ -174,6 +174,7 @@ namespace Content.Shared.Chemistry.Components Volume = solution.Volume; MaxVolume = solution.MaxVolume; Temperature = solution.Temperature; + CanReact = solution.CanReact; _heatCapacity = solution._heatCapacity; _heatCapacityDirty = solution._heatCapacityDirty; _heatCapacityUpdateCounter = solution._heatCapacityUpdateCounter; From 80ce9b94c3594689b2bc4eeb36c7da43c3a4c1be Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 28 Jun 2025 00:14:34 +0000 Subject: [PATCH 135/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index e9bc2eb7e0..5f6695717f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: Beck Thompson, SlamBamActionman - changes: - - message: Fixed trash bag visuals. The appearance will now update correctly depending - on how many items it contains! - type: Fix - id: 8211 - time: '2025-04-17T10:36:23.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32386 - author: Callmore changes: - message: Cyborgs now require being pryed to open/close the panel instead of screwed. @@ -3885,3 +3877,11 @@ id: 8723 time: '2025-06-27T16:39:49.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38622 +- author: Tayrtahn + changes: + - message: Solution containers (beakers, glasses, etc) no longer rapidly flicker + through different fill level visuals when transferring their contents. + type: Fix + id: 8724 + time: '2025-06-28T00:13:24.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/34838 From 3ffeeef8361b5c3a45419183b2ca7460d2025745 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 28 Jun 2025 12:49:32 +0200 Subject: [PATCH 136/191] =?UTF-8?q?Fix=20typos=20in=20guidebook:=20Buisnes?= =?UTF-8?q?s=20=E2=86=92=20Business=20(#38636)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix typo in `MinorAntagonists.xml` Buisness → Business * Fix typo in `YourFirstCharacter.xml` Buisness → Business --- Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml | 2 +- Resources/ServerInfo/Guidebook/NewPlayer/YourFirstCharacter.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml b/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml index 59c7cb9bc3..e051f1e9d1 100644 --- a/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml +++ b/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml @@ -40,7 +40,7 @@ - You can conjure a cloud of [color=#77b58e]ammonia[/color] using [color=purple][italic]Rat King's Domain[/italic][/color]. This purple gas is mildly poisonous to humans but heals rats. - [color=#9daa98][italic]Rummage[/italic][/color] through a disposal unit by using the context menu. Use this to find [bold]scraps of food[/bold] to grow your army. - Besides your abilities, [bold]you are stronger than most other animals.[/bold] You and your servants are capable of speech and you're both small enough to fit under airlocks and tables. - - [bold]Your underlings are still very fragile![/bold] Beware of spacings, explosions and the buisness end of a melee weapon. + - [bold]Your underlings are still very fragile![/bold] Beware of spacings, explosions and the business end of a melee weapon. # Space Dragon diff --git a/Resources/ServerInfo/Guidebook/NewPlayer/YourFirstCharacter.xml b/Resources/ServerInfo/Guidebook/NewPlayer/YourFirstCharacter.xml index d8d2f58842..780747761d 100644 --- a/Resources/ServerInfo/Guidebook/NewPlayer/YourFirstCharacter.xml +++ b/Resources/ServerInfo/Guidebook/NewPlayer/YourFirstCharacter.xml @@ -33,7 +33,7 @@ Again, you may be intimidated by the vast range of options. That's okay. [color=#a4885c]Take your time with it.[/color] You can skim or click through each of the options and find which one you like. Sometimes it's easier to set your desired hair color before looking at the options. -If you really can't decide, some popular haircut options include Floorlength Bedhead, Modern, Buisness, CIA or simply bald. +If you really can't decide, some popular haircut options include Floorlength Bedhead, Modern, Business, CIA or simply bald. Alternatively, [color=#a4885c]randomize[/color] a separate character and see what kind of haircuts they're rocking with. Suprise yourself. Don't feel obligated to use any of these, though. From 2c4cae2af994b49b9b00a08df959a67ef47ef594 Mon Sep 17 00:00:00 2001 From: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com> Date: Sat, 28 Jun 2025 07:11:08 -0400 Subject: [PATCH 137/191] AddBodyPartCommand localization. (#38612) commit --- .../Commands/AddBodyPartCommand.cs | 75 ++++++++----------- .../en-US/commands/add-body-part-command.ftl | 2 + 2 files changed, 34 insertions(+), 43 deletions(-) create mode 100644 Resources/Locale/en-US/commands/add-body-part-command.ftl diff --git a/Content.Server/Administration/Commands/AddBodyPartCommand.cs b/Content.Server/Administration/Commands/AddBodyPartCommand.cs index 892a88d41a..a28fa93ad0 100644 --- a/Content.Server/Administration/Commands/AddBodyPartCommand.cs +++ b/Content.Server/Administration/Commands/AddBodyPartCommand.cs @@ -3,52 +3,41 @@ using Content.Shared.Administration; using Content.Shared.Body.Part; using Robust.Shared.Console; -namespace Content.Server.Administration.Commands +namespace Content.Server.Administration.Commands; + +[AdminCommand(AdminFlags.Admin)] +public sealed class AddBodyPartCommand : LocalizedEntityCommands { - [AdminCommand(AdminFlags.Admin)] - public sealed class AddBodyPartCommand : IConsoleCommand + [Dependency] private readonly BodySystem _bodySystem = default!; + + public override string Command => "addbodypart"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) { - [Dependency] private readonly IEntityManager _entManager = default!; - - public string Command => "addbodypart"; - public string Description => "Adds a given entity to a containing body."; - public string Help => "Usage: addbodypart "; - - public void Execute(IConsoleShell shell, string argStr, string[] args) + if (args.Length != 4) { - if (args.Length != 3) - { - shell.WriteError(Loc.GetString("shell-wrong-arguments-number")); - return; - } - - if (!NetEntity.TryParse(args[0], out var childNetId)) - { - shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number")); - return; - } - - if (!NetEntity.TryParse(args[1], out var parentNetId)) - { - shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number")); - return; - } - - var childId = _entManager.GetEntity(childNetId); - var parentId = _entManager.GetEntity(parentNetId); - var bodySystem = _entManager.System(); - - - - if (Enum.TryParse(args[3], out var partType) && - bodySystem.TryCreatePartSlotAndAttach(parentId, args[2], childId, partType)) - { - shell.WriteLine($@"Added {childId} to {parentId}."); - } - else - { - shell.WriteError($@"Could not add {childId} to {parentId}."); - } + shell.WriteError(Loc.GetString("shell-wrong-arguments-number")); + return; } + + if (!NetEntity.TryParse(args[0], out var childNetId) || !EntityManager.TryGetEntity(childNetId, out var childId)) + { + shell.WriteError(Loc.GetString("shell-invalid-entity-uid", ("uid", args[0]))); + return; + } + + if (!NetEntity.TryParse(args[1], out var parentNetId) || !EntityManager.TryGetEntity(parentNetId, out var parentId)) + { + shell.WriteError(Loc.GetString("shell-invalid-entity-uid", ("uid", args[1]))); + return; + } + + if (Enum.TryParse(args[3], out var partType) && + _bodySystem.TryCreatePartSlotAndAttach(parentId.Value, args[2], childId.Value, partType)) + { + shell.WriteLine($@"Added {childId} to {parentId}."); + } + else + shell.WriteError($@"Could not add {childId} to {parentId}."); } } diff --git a/Resources/Locale/en-US/commands/add-body-part-command.ftl b/Resources/Locale/en-US/commands/add-body-part-command.ftl new file mode 100644 index 0000000000..6a7a54f730 --- /dev/null +++ b/Resources/Locale/en-US/commands/add-body-part-command.ftl @@ -0,0 +1,2 @@ +cmd-addbodypart-desc = Adds a given entity to a containing body. +cmd-addbodypart-help = Usage: addbodypart From 6db9f65046dfb43e74bd7620f2fdf4cc5b2eb0bb Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 28 Jun 2025 11:12:16 +0000 Subject: [PATCH 138/191] Automatic changelog update --- Resources/Changelog/Admin.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 29d119caa8..67fc878264 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1217,5 +1217,13 @@ Entries: id: 147 time: '2025-06-26T10:43:40.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38593 +- author: VerinSenpai + changes: + - message: The addbodypart command no longer incorrectly errors out when you try + to pass the required number of arguments. + type: Fix + id: 148 + time: '2025-06-28T11:11:08.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38612 Name: Admin Order: 2 From 0f74bac4154bb553933e6ca9c4e8f05136105485 Mon Sep 17 00:00:00 2001 From: Boaz1111 <149967078+Boaz1111@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:01:28 +0200 Subject: [PATCH 139/191] Various Headphones Fixes and Tweaks (#38479) * sprites, wearables * neck * icon-on sprite --- .../VendingMachines/Inventories/pride.yml | 2 +- .../Entities/Clothing/Multiple/misc.yml | 40 ++++ .../Entities/Clothing/Neck/misc.yml | 33 ---- .../Markers/Spawners/Random/maintenance.yml | 2 +- .../Entities/Structures/Furniture/dresser.yml | 2 +- .../Loadouts/Miscellaneous/trinkets.yml | 2 +- .../Roles/Jobs/Civilian/musician.yml | 2 +- .../Multiple/headphones.rsi/icon-on.png | Bin 0 -> 1088 bytes .../Clothing/Multiple/headphones.rsi/icon.png | Bin 0 -> 487 bytes .../Multiple/headphones.rsi/inhand-left.png | Bin 0 -> 499 bytes .../Multiple/headphones.rsi/inhand-right.png | Bin 0 -> 508 bytes .../Multiple/headphones.rsi/meta.json | 175 ++++++++++++++++++ .../headphones.rsi/off-equipped-EARS.png} | Bin .../headphones.rsi/off-equipped-HELMET.png | Bin 0 -> 273 bytes .../headphones.rsi/off-equipped-NECK.png | Bin 0 -> 374 bytes .../headphones.rsi/on-equipped-EARS.png} | Bin .../headphones.rsi/on-equipped-HELMET.png | Bin 0 -> 1425 bytes .../headphones.rsi/on-equipped-NECK.png | Bin 0 -> 1449 bytes .../Neck/Misc/headphones.rsi/icon-on.png | Bin 176 -> 0 bytes .../Neck/Misc/headphones.rsi/icon.png | Bin 176 -> 0 bytes .../Neck/Misc/headphones.rsi/inhand-left.png | Bin 329 -> 0 bytes .../Neck/Misc/headphones.rsi/inhand-right.png | Bin 329 -> 0 bytes .../Neck/Misc/headphones.rsi/meta.json | 71 ------- Resources/migration.yml | 3 + 24 files changed, 223 insertions(+), 109 deletions(-) create mode 100644 Resources/Prototypes/Entities/Clothing/Multiple/misc.yml create mode 100644 Resources/Textures/Clothing/Multiple/headphones.rsi/icon-on.png create mode 100644 Resources/Textures/Clothing/Multiple/headphones.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Multiple/headphones.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Multiple/headphones.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Multiple/headphones.rsi/meta.json rename Resources/Textures/Clothing/{Neck/Misc/headphones.rsi/off-equipped-NECK.png => Multiple/headphones.rsi/off-equipped-EARS.png} (100%) create mode 100644 Resources/Textures/Clothing/Multiple/headphones.rsi/off-equipped-HELMET.png create mode 100644 Resources/Textures/Clothing/Multiple/headphones.rsi/off-equipped-NECK.png rename Resources/Textures/Clothing/{Neck/Misc/headphones.rsi/on-equipped-NECK.png => Multiple/headphones.rsi/on-equipped-EARS.png} (100%) create mode 100644 Resources/Textures/Clothing/Multiple/headphones.rsi/on-equipped-HELMET.png create mode 100644 Resources/Textures/Clothing/Multiple/headphones.rsi/on-equipped-NECK.png delete mode 100644 Resources/Textures/Clothing/Neck/Misc/headphones.rsi/icon-on.png delete mode 100644 Resources/Textures/Clothing/Neck/Misc/headphones.rsi/icon.png delete mode 100644 Resources/Textures/Clothing/Neck/Misc/headphones.rsi/inhand-left.png delete mode 100644 Resources/Textures/Clothing/Neck/Misc/headphones.rsi/inhand-right.png delete mode 100644 Resources/Textures/Clothing/Neck/Misc/headphones.rsi/meta.json diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/pride.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/pride.yml index 23d836b7c1..79ce6426dc 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/pride.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/pride.yml @@ -35,7 +35,7 @@ ClothingNeckScarfStripedTrans: 2 ClothingHeadHatXmasCrown: 2 BedsheetRainbow: 2 - ClothingNeckHeadphones: 2 + ClothingMultipleHeadphones: 2 ClothingHeadHatFlowerWreath: 2 ClothingUniformColorRainbow: 2 ClothingUnderSocksCoder: 2 diff --git a/Resources/Prototypes/Entities/Clothing/Multiple/misc.yml b/Resources/Prototypes/Entities/Clothing/Multiple/misc.yml new file mode 100644 index 0000000000..95b45bd8dc --- /dev/null +++ b/Resources/Prototypes/Entities/Clothing/Multiple/misc.yml @@ -0,0 +1,40 @@ +- type: entity + parent: Clothing + id: ClothingMultipleHeadphones + name: headphones + description: Quality headphones from Drunk Masters, with good sound insulation. + components: + - type: Sprite + sprite: Clothing/Multiple/headphones.rsi + layers: + - state: icon + map: [ "enum.ToggleableVisuals.Layer" ] + - type: Clothing + equippedPrefix: off + sprite: Clothing/Multiple/headphones.rsi + slots: + - HEAD + - EARS + - NECK + - type: ToggleableVisuals + spriteLayer: enum.ToggleableVisuals.Layer + clothingVisuals: + head: + - state: on-equipped-HELMET + ears: + - state: on-equipped-EARS + neck: + - state: on-equipped-NECK + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ToggleableVisuals.Enabled: + enum.ToggleableVisuals.Layer: + True: {state: icon-on} + False: {state: icon} + - type: ItemToggle + predictable: false + soundActivate: + path: /Audio/Items/flashlight_on.ogg + soundDeactivate: + path: /Audio/Items/flashlight_off.ogg diff --git a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml index c8892bc0b5..60ea7c49ec 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml @@ -1,36 +1,3 @@ -- type: entity - parent: ClothingNeckBase - id: ClothingNeckHeadphones - name: headphones - description: Quality headphones from Drunk Masters, with good sound insulation. - components: - - type: Sprite - sprite: Clothing/Neck/Misc/headphones.rsi - layers: - - state: icon - map: [ "enum.ToggleableVisuals.Layer" ] - - type: Clothing - equippedPrefix: off - sprite: Clothing/Neck/Misc/headphones.rsi - - type: ToggleableVisuals - spriteLayer: enum.ToggleableVisuals.Layer - clothingVisuals: - neck: - - state: on-equipped-NECK - - type: Appearance - - type: GenericVisualizer - visuals: - enum.ToggleableVisuals.Enabled: - enum.ToggleableVisuals.Layer: - True: {state: icon-on} - False: {state: icon} - - type: ItemToggle - predictable: false - soundActivate: - path: /Audio/Items/flashlight_on.ogg - soundDeactivate: - path: /Audio/Items/flashlight_off.ogg - - type: entity parent: Clothing id: ClothingNeckStethoscope diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml index 5f54e9a5c3..6def2009cc 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml @@ -56,7 +56,7 @@ - id: ClothingHeadHatPaper - id: ClothingHeadHatPirate - id: ClothingMaskSterile - - id: ClothingNeckHeadphones + - id: ClothingMultipleHeadphones - id: ClothingNeckTieRed - id: ClothingOuterCoatGentle - !type:AllSelector diff --git a/Resources/Prototypes/Entities/Structures/Furniture/dresser.yml b/Resources/Prototypes/Entities/Structures/Furniture/dresser.yml index 2690906fc1..ccfb25a802 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/dresser.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/dresser.yml @@ -241,7 +241,7 @@ - id: ClothingHeadHatAnimalHeadslime prob: 0.03 orGroup: dresserthirdloot - - id: ClothingNeckHeadphones + - id: ClothingMultipleHeadphones prob: 0.03 orGroup: dresserthirdloot - id: ClothingUnderSocksCoder diff --git a/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml b/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml index 156bade83b..2649dfa958 100644 --- a/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml +++ b/Resources/Prototypes/Loadouts/Miscellaneous/trinkets.yml @@ -26,7 +26,7 @@ id: Headphones storage: back: - - ClothingNeckHeadphones + - ClothingMultipleHeadphones # Toys - type: loadout diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/musician.yml b/Resources/Prototypes/Roles/Jobs/Civilian/musician.yml index 04891ab2c6..03032151dc 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/musician.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/musician.yml @@ -31,6 +31,6 @@ equipment: head: ClothingHeadHatTophat mask: ClothingMaskBreath - neck: ClothingNeckHeadphones + neck: ClothingMultipleHeadphones outerClothing: ClothingOuterWinterMusician gloves: ClothingHandsGlovesColorBlack diff --git a/Resources/Textures/Clothing/Multiple/headphones.rsi/icon-on.png b/Resources/Textures/Clothing/Multiple/headphones.rsi/icon-on.png new file mode 100644 index 0000000000000000000000000000000000000000..2de89a0630f34b048e26b4e41e9e0f39b75db295 GIT binary patch literal 1088 zcmV-G1i$-Px&;7LS5RCt{2oj-2tFbu{&KfC}L19U0S#dpZ!W8@e)QjgK)9V0-F;H5y80vQ8j z@Ea1Nj{nG%Et0auAK<~MqffLbMUqW6L*i@#mUSQaJkRs0d0v)f7Fv4>cL0Bt>##_M z8rntO=)l7vn&+CRJ3zY^yx!dXv*(;<-K}n91EUV0*8q=Ra>{<|Fb`@El1z>Qv8T)%1-y5(L{eKgO<52oOKL_NWfTAeKLyXV0 zYt-KtMRmucFrJvE6RTLs@-2;6EIjzO24; zBj|R!`P6;(_e@`2-#H09pU<;*q?%>;Zn1{em;CNh^hdXX&*!t}<6Gc*y#ffcrD^)Q z`ax+MLYYu;lDa-HaVvPKtnDeEFMak4IsoGhY9fF+-w1 zr|Gkw<(05Gjr(1e8IN=5_v%Ljl+J4;?Z2bvX<&@eG6oNKB(@J=bTJ1=9CLug8JQUI z21nX_Ll@^9AbfD75hH|UU>zJO;|!J`pow~ZfEvCExW2QN`~WfMM-U4RO+7zAtoAQt z6|n650AcFDL$3qWp2{UUMttGv>&=g#_(Di(`2pgzU&yoDlc~q~X5ayZ6ho0gzL7xV z#A!cZ`2mvF2P{88^7@t^AYpyL@&hET4_JPHMLJ! zwnDk|Kk~f#eOSW#6j0lKU@`9lgn{0Z^nz14QO*w#XMDUQG1@PhAAmv};n{jf+uxZ4 z=J}6u6fK}R{Kk=3NzQ)D50DHB`2o5W(8cltbnWl-biV&;*?q@8#=wj*rZ3atvtN42 zX&8oK7)Ct&2k+9&*e<8m`2YX_4rN$LW=%~1DgXcg2mk;800000(o>TF0000M1MG8<*qQo_#Bsf2V7NJjy2W;+X5JPXJM zfdn81>4(v1mNEeSKY^Wr1*pQn$k>2!0mM|04%P(_lcoUKAixAPhY74Q$kGDHg6c8^ zhXaGE`@G)8CR>jJ6{dN*IEGmGzwLA6JD|Y9!us%kyxyrrQ>k8bws?StX?=e+N5rgt2Tye`UH1&%nF o1V#IYFupvjKHsA0^X>&zjGH)w!~GX6`v`KIr>mdKI;Vst0Lc?{fB*mh literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Multiple/headphones.rsi/inhand-left.png b/Resources/Textures/Clothing/Multiple/headphones.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..0febda5d254a3f9e70adc6f6d936f4d16898a0b8 GIT binary patch literal 499 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucL5ULAh?3y^w370~qEv=}#LT=BJwMkF1yemk zJ@f8s-z|Y^wxvdRrg?g5F>nAmtPE0&tPG4mmKP99L)jqLXfQH^#hHL?Lq;YB0U#X( z#F_0ZVDT&<8w3)77^ELYqgl$p%)l^#oq+|Y!obMbfN=rDRFDqV1rU>_0NEhG1T=>U ztTM>b0?2~uGBhv%$*Q`~>s@TJ^(c_J$kW9!q$2L^Ek~}y3IYrVE{OkVu~EMN;lKRH zX-X9tnUmU5v_;eO)@htps<<^5q?zH}3Cs1mHQnnw&RjhjHZh#LA4uDr-+feeZf@t9 zuV)vp-L7+b)3PbI-UZvPF2D6HAZ4AgliY$SmloBgzYCeTbC=XtZm>BF2NdF>pVi;B z```L7jYG-RG{E`Rk44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`212l#}z1_lNyC@5H1SQr=>0L4!2JPR_AwIs+d_&*RZ{NBbF3>4rj@Q5sC zVBk9p!i>lBSEK+1B}!Z)N`mv#O3D+9QW*jgGxJLH{9Hp6O!W-)%)76Bw*;!$mKx!i z=IN=$zyUIrL5h);ff2~^0%B<>8{~2gMrN=$6Oe7l$iyH3q@#d1vz-Mjo&{usKmrhh z^uuU0OBsNEpTN$*0#spOWNg5=0Aeag2kQceNmGDq5MTnD!vt0tWN86pL3J5|gMmTS zeO~WkldVUA3hO;x977`9-%dLybU=W|CHmz5`eN0L0*6y8H^0Bod#=jZFJ=!XivWj! zgTfyVf#SQN(?2iRn3Zu#Gym_j85gF73V5V1n^5Et9>zGe-m<;qsj>SFMkWpx4g~>) z1_ul64({z6?k(7SJ>YAs`I~Oen0}VSEbH8x3O{hjHzq_M_`|T(-M(Mwlw1JF(Vnh; JF6*2UngD=0eU<eF ztQryxx_Rv15@g;#Tsh+a^8~G+t|N+N2l)QPrx$;-RtzjulDM7vx?Qp5&78?oCY|}b z=cj%BO!?$9+2>=Qw+qCz?XA8ay6=0P>66R9BtNfT+PZ4i+O4ZiuSEr3)tVik{XZ;f zht6sLtZi0S@h-n?n1GIe0EwgP4mL?7`)PI-`z_1NvRYD*vaMswY<7){d&J+@+Ff>i zGOw=dIkQ}dsdVxYiQrtZ1*!J&`3FAB)_nc_gQ@+?<;#LFqY@O#e;4E_uF!b?1tjF@ L>gTe~DWM4f{o`;5 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Multiple/headphones.rsi/off-equipped-NECK.png b/Resources/Textures/Clothing/Multiple/headphones.rsi/off-equipped-NECK.png new file mode 100644 index 0000000000000000000000000000000000000000..a90ae1dbc8199f56116ef5469e8fc51acc8764e3 GIT binary patch literal 374 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 diff --git a/Resources/Textures/Clothing/Neck/Misc/headphones.rsi/icon.png b/Resources/Textures/Clothing/Neck/Misc/headphones.rsi/icon.png deleted file mode 100644 index f1ea592a6036588a97959a049034af15f2ace0f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{*8>L*1OxIE)xP8;&yi>^i{e!|b4vy+L!z`J5bvg$Yb+77Bzopr04(Y-KL7v# diff --git a/Resources/Textures/Clothing/Neck/Misc/headphones.rsi/inhand-left.png b/Resources/Textures/Clothing/Neck/Misc/headphones.rsi/inhand-left.png deleted file mode 100644 index ab7a61a1237c8eb111b28605904939e9a7e4d32a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0J3?w7mbKU|ep#Yx{S0Ei280f~q(aXh^C?F6g zBvjo0dNxp$u_VYZn8D%MjWi&qs466)#3i*jxhS)sBr`vcfuUkfa9BZ6>GvSI(O!L@P<%>i^dNg>74h`Jjqbh)4RjMILP?2v6sS}N0W|B3Q-8wR5mqSs-SGI zztzId-oLZkSatIXi6t|;jn`@znu{JWdlD2@@U@sxnxDa)LpD=+y?y}D_H<7d#}JRs zz zB)WmEA!4D!8?QFe#R(!kOjQhwYg`$a6uh`)92lofEo5-XY-9%Viq=XbF)-|^XGwec SuxA0#N(N6?KbLh*2~7Y8XlRuH diff --git a/Resources/Textures/Clothing/Neck/Misc/headphones.rsi/inhand-right.png b/Resources/Textures/Clothing/Neck/Misc/headphones.rsi/inhand-right.png deleted file mode 100644 index 8a09470e9454a88d8a04cdc8930c9ed582a8443c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0J3?w7mbKU|ep#Yx{S0Ei280f~q(aXh^C?F6g zBvjo0dNxp$u_VYZn8D%MjWi&qs466)#3i*jxhS)sBr`vcfuUkfa9BZ6>GvSI(O!L@P<%>i^dNg>74h`Jjqbh)4RjMILP?2v6sS}N0W|B3Q-8wR5mqSs-SGI zztzId-oLZkSatIXi6t|;jn`@znu{JWdlD2@@U@sxnxDa)LpD=+y?y}D_H<7d#}JRs zS_#?Fm5 zM4cJ60~oUz3d4Gu0u8t_SXnlRGI%fqialW9VlmPRifoW=IK?=P!E24gCkBSc|5!>N TIBNlY$H3s}>gTe~DWM4fw}@w) diff --git a/Resources/Textures/Clothing/Neck/Misc/headphones.rsi/meta.json b/Resources/Textures/Clothing/Neck/Misc/headphones.rsi/meta.json deleted file mode 100644 index c4b27b734f..0000000000 --- a/Resources/Textures/Clothing/Neck/Misc/headphones.rsi/meta.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/5a73e8f825ff279e82949b9329783a9e3070e2da", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "on-equipped-NECK", - "directions": 4, - "delays": [ - [ - 0.2, - 0.2, - 0.2, - 0.2, - 0.2, - 0.2, - 0.2 - ], - [ - 0.2, - 0.2, - 0.2, - 0.2, - 0.2, - 0.2, - 0.2 - ], - [ - 0.2, - 0.2, - 0.2, - 0.2, - 0.2, - 0.2, - 0.2 - ], - [ - 0.2, - 0.2, - 0.2, - 0.2, - 0.2, - 0.2, - 0.2 - ] - ] - }, - { - "name": "off-equipped-NECK", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "icon" - }, - { - "name": "icon-on" - } - ] -} diff --git a/Resources/migration.yml b/Resources/migration.yml index d420668b8d..87a996e23b 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -681,3 +681,6 @@ BarSignEmprahAlignTile: BarSignEmprah BarSignSpacebucksAlignTile: BarSignSpacebucks BarSignMaltroachAlignTile: BarSignMaltroach BarSignWhiskeyEchoesAlignTile: BarSignWhiskeyEchoes + +# 2025-06-21 +ClothingNeckHeadphones: ClothingMultipleHeadphones From 6c8749bc61decd18da5ddc8f1236e55655f2b20c Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 28 Jun 2025 14:02:36 +0000 Subject: [PATCH 140/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 5f6695717f..c80ca684a3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Callmore - changes: - - message: Cyborgs now require being pryed to open/close the panel instead of screwed. - type: Tweak - id: 8212 - time: '2025-04-17T10:38:00.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32796 - author: Errant changes: - message: Handheld geiger counters can now by heard by everyone nearby. @@ -3885,3 +3878,11 @@ id: 8724 time: '2025-06-28T00:13:24.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/34838 +- author: Boaz1111 + changes: + - message: Headphone sprites have been changed a bit, and they are now wearable + on the head, ears and neck. + type: Tweak + id: 8725 + time: '2025-06-28T14:01:29.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38479 From 97f0bab5b8ad12b5c62e00de32b1eee30d5a4e6a Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Sat, 28 Jun 2025 14:12:36 -0400 Subject: [PATCH 141/191] Cleanup prototype instantiation in `DamageTest` (#38639) Cleanup prototype instantiation in DamageTest --- Content.Tests/Shared/DamageTest.cs | 35 ++++++++++-------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/Content.Tests/Shared/DamageTest.cs b/Content.Tests/Shared/DamageTest.cs index 88beca8841..58fa8fbde3 100644 --- a/Content.Tests/Shared/DamageTest.cs +++ b/Content.Tests/Shared/DamageTest.cs @@ -4,7 +4,6 @@ using NUnit.Framework; using Robust.Shared.IoC; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager; -using System.Collections.Generic; using Content.Shared.FixedPoint; namespace Content.Tests.Shared @@ -16,23 +15,6 @@ namespace Content.Tests.Shared [TestOf(typeof(DamageGroupPrototype))] public sealed class DamageTest : ContentUnitTest { - - private static Dictionary _resistanceCoefficientDict = new() - { - // "missing" blunt entry - { "Piercing", -2 },// Turn Piercing into Healing - { "Slash", 3 }, - { "Radiation", 1.5f }, - }; - - private static Dictionary _resistanceReductionDict = new() - { - { "Blunt", - 5 }, - // "missing" piercing entry - { "Slash", 8 }, - { "Radiation", 0.5f }, // Fractional adjustment - }; - private IPrototypeManager _prototypeManager; private DamageSpecifier _damageSpec; @@ -139,11 +121,7 @@ namespace Content.Tests.Shared DamageSpecifier damageSpec = 10 * new DamageSpecifier(_damageSpec); // Create a modifier set - DamageModifierSetPrototype modifierSet = new() - { - Coefficients = _resistanceCoefficientDict, - FlatReduction = _resistanceReductionDict - }; + var modifierSet = _prototypeManager.Index("ModifierTestSet"); //damage is initially 20 / 20 / 10 / 30 //Each time we subtract -5 / 0 / 8 / 0.5 @@ -289,6 +267,17 @@ namespace Content.Tests.Shared flatReductions: Blunt: 5 +- type: damageModifierSet + id: ModifierTestSet + coefficients: + Piercing: -2 + Slash: 3 + Radiation: 1.5 + flatReductions: + Blunt: -5 + Slash: 8 + Radiation: 0.5 + - type: damageContainer id: Biological supportedGroups: From 2f37923db622ed66c63282aa562d8913100c895f Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Sat, 28 Jun 2025 15:34:15 -0400 Subject: [PATCH 142/191] Cleanup prototype instantiation in `ExplosionSystem` (#38642) Cleanup prototype instantiation in ExplosionSystem --- .../Explosion/EntitySystems/ExplosionSystem.Processing.cs | 4 ++-- Content.Server/Explosion/EntitySystems/ExplosionSystem.cs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index 3f08532e37..b61f78e909 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -913,10 +913,10 @@ sealed class Explosion /// /// Data needed to spawn an explosion with . /// -public sealed class QueuedExplosion +public sealed class QueuedExplosion(ExplosionPrototype proto) { public MapCoordinates Epicenter; - public ExplosionPrototype Proto = new(); + public ExplosionPrototype Proto = proto; public float TotalIntensity, Slope, MaxTileIntensity, TileBreakScale; public int MaxTileBreak; public bool CanCreateVacuum; diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index ffdc90f9d6..0cdc6d6b23 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -306,10 +306,9 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem return; } - var boom = new QueuedExplosion() + var boom = new QueuedExplosion(type) { Epicenter = epicenter, - Proto = type, TotalIntensity = totalIntensity, Slope = slope, MaxTileIntensity = maxTileIntensity, From 90915075fb2f5c26f9a77afbfc1ec72b5aa795ab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 29 Jun 2025 03:00:28 +0200 Subject: [PATCH 143/191] Update Credits (#38646) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index 08b0911101..8f68e79e82 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0leshe, 0tito, 0x6273, 12rabbits, 1337dakota, 13spacemen, 154942, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 3nderall, 4310v343k, 4dplanner, 612git, 778b, aaron, abadaba695, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aerocrux, Aeshus, Aexolott, Aexxie, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, Ahion, aiden, Aidenkrz, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alice4267, Alithsko, alliephante, ALMv1, Alpaccalypse, Alpha-Two, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, AndrewEyeke, AndreyCamper, Anzarot121, ApolloVector, Appiah, ar4ill, archee1, ArchPigeon, ArchRBX, areitpog, Arendian, arimah, Arkanic, ArkiveDev, armoks, Arteben, ArthurMousatov, ArtisticRoomba, artur, ArZarLordOfMango, as334, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, Awlod, azzyisnothere, AzzyIsNotHere, B-Kirill, B3CKDOOR, baa14453, BackeTako, Bakke, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, beck-thompson, bellwetherlogic, ben, benbryant0, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BigfootBravo, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlitzTheSquishy, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Bokser815, bolantej, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, BriBrooo, Bright0, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, bvelliquette, BWTCK, byondfuckery, c0rigin, c4llv07e, CaasGit, Caconym27, Calecute, Callmore, Camdot, capnsockless, CaptainMaru, captainsqrbeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, catlord, Catofquestionableethics, CatTheSystem, Centronias, Chaboricks, chairbender, Chaoticaa, Charlese2, charlie, chartman, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, ciaran, citrea, civilCornball, claustro305, Clement-O, clyf, Clyybber, CMDR-Piboy314, cnv41, coco, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, crazybrain23, Crazydave91920, creadth, CrigCrag, croilbird, Crotalus, CrudeWax, cryals, CrzyPotato, cubixthree, cutemoongod, Cyberboss, d34d10cc, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dan, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkenson, DawBla, Daxxi3, dch-GH, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, DebugOk, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, dexlerxd, dffdff2423, DieselMohawk, digitalic, Dimastra, DinnerCalzone, DinoWattz, Disp-Dev, DisposableCrewmember42, dissidentbullet, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, Emisse, emmafornash, EmoGarbage404, Endecc, Entvari, eoineoineoin, ephememory, eris, erohrs2, ERORR404V1, Errant-4, ertanic, esguard, estacaoespacialpirata, eugene, ewokswagger, exincore, exp111, f0x-n3rd, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, FillerVK, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FrostRibbon, Funce, FungiFellow, FunTust, Futuristic-OK, GalacticChimp, gamer3107, Gamewar360, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, genderGeometries, GeneralGaws, Genkail, Gentleman-Bird, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, gituhabu, GlassEclipse, GnarpGnarp, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, golubgik, GoodWheatley, Gorox221, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, Hardly3D, harikattar, he1acdvv, Hebi, Helix-ctrl, helm4142, Henry, HerCoyote23, HighTechPuddle, Hitlinemoss, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hoshizora, Hreno, Hrosts, htmlsystem, hubismal, Hugal31, Huxellberger, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imatsoup, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, jacksonzck, Jacktastic09, Jackw2As, jacob, jamessimo, janekvap, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, JCGWE30, jerryimmouse, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jkwookee, jmcb, JohnGinnane, johnku1, Jophire, joshepvodka, JpegOfAFrog, jproads, JrInventor05, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, kaiserbirch, KaiShibaa, kalane15, kalanosh, KamTheSythe, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, kerisargit, keronshb, KIBORG04, KieueCaprie, Killerqu00, Kimpes, KingFroozy, kira-er, kiri-yoshikage, Kirillcas, Kirus59, Kistras, Kit0vras, KittenColony, Kittygyat, klaypexx, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kosticia, koteq, KrasnoshchekovPavel, Krunklehorn, Kupie, kxvvv, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, lanedon, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, lettern, LetterN, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, LightVillet, liltenhead, linkbro1, LinkUyx, Litraxx, little-meow-meow, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, ljm862, lmsnoise, localcc, lokachop, Lomcastar, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luizwritescode, Lukasz825700516, luminight, lunarcomets, Lusatia, lvvova1, Lyndomen, lyroth001, lzimann, lzk228, M1tht1c, M3739, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, MagnusCrowe, malchanceux, MaloTV, ManelNavola, manelnavola, Mangohydra, marboww, Markek1, matt, Matz05, max, MaxNox7, maylokana, MehimoNemo, MeltedPixel, memeproof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, misandrie, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, Mixelz, mjarduk, MjrLandWhale, mkanke-real, MLGTASTICa, moderatelyaware, modern-nm, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, Morb0, MossyGreySlope, mr-bo-jangles, Mr0maks, MrFippik, mrrobdemo, muburu, MureixloI, murolem, musicmanvr, MWKane, Myakot, Myctai, N3X15, nabegator, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, NazrinNya, neutrino-laser, NickPowers43, nikitosych, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, nkokic, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, not-gavnaed, notafet, notquitehadouken, NotSoDana, noudoit, noverd, Nox38, NuclearWinter, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, Nyxilath, och-och, OctoRocket, OldDanceJacket, OliverOtter, onesch, OneZerooo0, OnyxTheBrave, Orange-Winds, OrangeMoronage9622, Orsoniks, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, peccneck, Peptide90, peptron1, perryprog, PeterFuto, PetMudstone, pewter-wiz, Pgriha, Phantom-Lily, pheenty, philingham, Phill101, Phooooooooooooooooooooooooooooooosphate, phunnyguy, PicklOH, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, Pissachu, pissdemon, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, PotRoastPiggy, Princess-Cheeseballs, ProfanedBane, PROG-MohamedDwidar, Prole0, ProPandaBear, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, py01, qrtDaniil, qrwas, Quantum-cross, quatre, QueerNB, QuietlyWhisper, qwerltaz, Radezolid, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, ReeZer2, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, RobbyTheFish, robinthedragon, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, rosieposieeee, Roudenn, router, ruddygreat, RumiTiger, Ruzihm, S1rFl0, S1ss3l, Saakra, Sadie-silly, saga3152, saintmuntzer, Salex08, sam, samgithubaccount, Samuka-C, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SharkSnake98, shibechef, SignalWalker, siigiil, silicon14wastaken, Simyon264, sirdragooon, Sirionaut, Sk1tch, SkaldetSkaeg, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, snebl, snicket, sniperchance, Snowni, snowsignal, SolidusSnek, solstar2, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, soupkilove, southbridge-fur, sowelipililimute, Soydium, spacelizard, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, Spangs04, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, stellar-novas, stewie523, stomf, Stop-Signs, stopbreaking, stopka-html, StrawberryMoses, Stray-Pyramid, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, supergdpwyl, superjj18, Supernorn, SweptWasTaken, SyaoranFox, Sybil, SYNCHRONIC, Szunti, t, Tainakov, takemysoult, tap, TaralGit, Taran, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telyonok, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, TheBlueYowie, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, TheFlyingSentry, TheIntoxicatedCat, thekilk, themias, theomund, TheProNoob678, TherapyGoth, ThereDrD0, TheShuEd, thetolbean, thevinter, TheWaffleJesus, thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, TornadoTechnology, tosatur, TotallyLemon, ToxicSonicFan04, Tr1bute, trixxedbit, TrixxedHeart, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, ubis1, UBlueberry, uhbg, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, unusualcrow, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, Verslebas, vexerot, viceemargo, VigersRay, violet754, Visne, vitusveit, vlad, vlados1408, VMSolidus, vmzd, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wafehling, walksanatora, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Will-Oliver-Br, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, Wolfkey-SomeoneElseTookMyUsername, wrexbe, wtcwr68, xeri7, xkreksx, xprospero, xRiriq, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, yuriykiss, YuriyKiss, zach-hill, Zadeon, Zalycon, zamp, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex +0leshe, 0tito, 0x6273, 12rabbits, 1337dakota, 13spacemen, 154942, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 3nderall, 4310v343k, 4dplanner, 612git, 778b, 96flo, aaron, abadaba695, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aerocrux, Aeshus, Aexolott, Aexxie, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, Ahion, aiden, Aidenkrz, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alice4267, Alithsko, alliephante, ALMv1, Alpaccalypse, Alpha-Two, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, AndrewEyeke, AndreyCamper, Anzarot121, ApolloVector, Appiah, ar4ill, archee1, ArchPigeon, ArchRBX, areitpog, Arendian, areyouconfused, arimah, Arkanic, ArkiveDev, armoks, Arteben, ArthurMousatov, ArtisticRoomba, artur, ArZarLordOfMango, as334, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, Awlod, azzyisnothere, AzzyIsNotHere, B-Kirill, B3CKDOOR, baa14453, BackeTako, Bakke, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, beck-thompson, beesterman, bellwetherlogic, ben, benbryant0, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BigfootBravo, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blitzthesquishy, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Bokser815, bolantej, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, BriBrooo, Bright0, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, bvelliquette, BWTCK, byondfuckery, c0rigin, c4llv07e, CaasGit, Caconym27, Calecute, Callmore, Camdot, capnsockless, CaptainMaru, captainsqrbeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, catlord, Catofquestionableethics, CatTheSystem, Centronias, Chaboricks, chairbender, Chaoticaa, Charlese2, charlie, chartman, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, ciaran, citrea, civilCornball, claustro305, Clement-O, clyf, Clyybber, CMDR-Piboy314, cnv41, coco, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, crazybrain23, Crazydave91920, creadth, CrigCrag, croilbird, Crotalus, CrudeWax, cryals, CrzyPotato, cubixthree, cutemoongod, Cyberboss, d34d10cc, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dan, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkenson, DawBla, Daxxi3, dch-GH, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, DebugOk, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, dexlerxd, dffdff2423, DieselMohawk, digitalic, Dimastra, DinnerCalzone, DinoWattz, Disp-Dev, DisposableCrewmember42, dissidentbullet, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, Dragonjspider, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, Emisse, emmafornash, EmoGarbage404, Endecc, Entvari, eoineoineoin, ephememory, eris, erohrs2, ERORR404V1, Errant-4, ertanic, esguard, estacaoespacialpirata, eugene, ewokswagger, exincore, exp111, f0x-n3rd, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, FillerVK, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FrostRibbon, Funce, FungiFellow, FunTust, Futuristic-OK, GalacticChimp, gamer3107, Gamewar360, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, genderGeometries, GeneralGaws, Genkail, Gentleman-Bird, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, gituhabu, GlassEclipse, GnarpGnarp, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, golubgik, GoodWheatley, Gorox221, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, Hardly3D, harikattar, he1acdvv, Hebi, Helix-ctrl, helm4142, Henry, HerCoyote23, HighTechPuddle, Hitlinemoss, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, Hoshizora, Hreno, Hrosts, htmlsystem, hubismal, Hugal31, Huxellberger, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imatsoup, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, insoPL, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, jacksonzck, Jacktastic09, Jackw2As, jacob, jamessimo, janekvap, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, JCGWE30, jerryimmouse, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jkwookee, jmcb, JohnGinnane, johnku1, Jophire, joshepvodka, JpegOfAFrog, jproads, JrInventor05, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, kaiserbirch, KaiShibaa, kalane15, kalanosh, KamTheSythe, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, kerisargit, keronshb, KIBORG04, KieueCaprie, Killerqu00, Kimpes, KingFroozy, kira-er, kiri-yoshikage, Kirillcas, Kirus59, Kistras, Kit0vras, KittenColony, Kittygyat, klaypexx, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kosticia, koteq, KrasnoshchekovPavel, Krunklehorn, Kupie, kxvvv, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, lanedon, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, lettern, LetterN, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, LightVillet, liltenhead, linkbro1, linkuyx, Litraxx, little-meow-meow, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, ljm862, lmsnoise, localcc, lokachop, Lomcastar, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luizwritescode, Lukasz825700516, luminight, lunarcomets, Lusatia, lvvova1, Lyndomen, lyroth001, lzimann, lzk228, M1tht1c, M3739, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, MagnusCrowe, maland1, malchanceux, MaloTV, ManelNavola, manelnavola, Mangohydra, marboww, Markek1, matt, Matz05, max, MaxNox7, maylokana, MehimoNemo, MeltedPixel, memeproof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, misandrie, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, Mixelz, mjarduk, MjrLandWhale, mkanke-real, MLGTASTICa, moderatelyaware, modern-nm, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, Morb0, MossyGreySlope, mr-bo-jangles, Mr0maks, MrFippik, mrrobdemo, muburu, MureixloI, murolem, musicmanvr, MWKane, Myakot, Myctai, N3X15, nabegator, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, NazrinNya, neutrino-laser, NickPowers43, nikitosych, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, nkokic, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, not-gavnaed, notafet, notquitehadouken, NotSoDana, noudoit, noverd, Nox38, NuclearWinter, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, Nyxilath, och-och, OctoRocket, OldDanceJacket, OliverOtter, onesch, OneZerooo0, OnyxTheBrave, Orange-Winds, OrangeMoronage9622, Orsoniks, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, peccneck, Peptide90, peptron1, perryprog, PeterFuto, PetMudstone, pewter-wiz, Pgriha, Phantom-Lily, pheenty, philingham, Phill101, Phooooooooooooooooooooooooooooooosphate, phunnyguy, PicklOH, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, Pissachu, pissdemon, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, PotRoastPiggy, Princess-Cheeseballs, ProfanedBane, PROG-MohamedDwidar, Prole0, ProPandaBear, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, py01, qrtDaniil, qrwas, Quantum-cross, quatre, QueerNB, QuietlyWhisper, qwerltaz, Radezolid, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, ReeZer2, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, RobbyTheFish, robinthedragon, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, rosieposieeee, Roudenn, router, ruddygreat, rumaks, RumiTiger, Ruzihm, S1rFl0, S1ss3l, Saakra, Sadie-silly, saga3152, saintmuntzer, Salex08, sam, samgithubaccount, Samuka-C, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SharkSnake98, shibechef, SignalWalker, siigiil, silicon14wastaken, Simyon264, sirdragooon, Sirionaut, Sk1tch, SkaldetSkaeg, Skarletto, Skrauz, Skybailey-dev, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, snebl, snicket, sniperchance, Snowni, snowsignal, SolidusSnek, solstar2, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, soupkilove, southbridge-fur, sowelipililimute, Soydium, spacelizard, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, Spangs04, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, stellar-novas, stewie523, stomf, Stop-Signs, stopbreaking, stopka-html, StrawberryMoses, Stray-Pyramid, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, supergdpwyl, superjj18, Supernorn, SweptWasTaken, SyaoranFox, Sybil, SYNCHRONIC, Szunti, t, Tainakov, takemysoult, tap, TaralGit, Taran, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telyonok, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, TheBlueYowie, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, TheFlyingSentry, TheIntoxicatedCat, thekilk, themias, theomund, TheProNoob678, TherapyGoth, ThereDrD0, TheShuEd, thetolbean, thevinter, TheWaffleJesus, thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, tornado-technology, TornadoTechnology, tosatur, TotallyLemon, ToxicSonicFan04, Tr1bute, trixxedbit, TrixxedHeart, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, ubis1, UBlueberry, uhbg, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, unusualcrow, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, Verslebas, vexerot, viceemargo, VigersRay, violet754, Visne, vitusveit, vlad, vlados1408, VMSolidus, vmzd, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wafehling, walksanatora, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Will-Oliver-Br, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, Wolfkey-SomeoneElseTookMyUsername, wrexbe, wtcwr68, xeri7, xkreksx, xprospero, xRiriq, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, yuriykiss, YuriyKiss, zach-hill, Zadeon, Zalycon, zamp, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex From 4bd437272f9c69e8ce9fe830ae55a95b1021eebf Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sun, 29 Jun 2025 16:33:58 +0200 Subject: [PATCH 144/191] fix ItemSlotsSystem debug assert (#38655) --- Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index b3eb4b23c2..88e6ca44dc 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -395,7 +395,7 @@ namespace Content.Shared.Containers.ItemSlots if (!Resolve(user, ref hands, false)) return false; - if (!_handsSystem.TryGetActiveItem((uid, hands), out var held)) + if (!_handsSystem.TryGetActiveItem((user, hands), out var held)) return false; if (!CanInsert(uid, held.Value, user, slot)) From 5d191d7b872f00b3e3c185e134372e5a88efd9fa Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 29 Jun 2025 14:35:08 +0000 Subject: [PATCH 145/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c80ca684a3..195ca58079 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Errant - changes: - - message: Handheld geiger counters can now by heard by everyone nearby. - type: Tweak - id: 8213 - time: '2025-04-17T11:24:47.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/30463 - author: Centronias changes: - message: Tank harnesses can be made at lathes, similar to utility belts. @@ -3886,3 +3879,11 @@ id: 8725 time: '2025-06-28T14:01:29.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38479 +- author: slarticodefast + changes: + - message: Fixed smart equip not being able to insert items into item slots (for + example for the sabre sheath). + type: Fix + id: 8726 + time: '2025-06-29T14:33:58.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38655 From 7951db15ca0977302fe34d21533b728a222dadf8 Mon Sep 17 00:00:00 2001 From: BramvanZijp <56019239+BramvanZijp@users.noreply.github.com> Date: Sun, 29 Jun 2025 18:21:14 +0200 Subject: [PATCH 146/191] Allow the Command & Super door remotes to use the access of their user. (Re-creation of PR due to changes to game balance) (#35536) --- Content.Server/Remotes/DoorRemoteSystem.cs | 16 +++++++++++---- .../Remotes/Components/DoorRemoteComponent.cs | 7 +++++++ .../Catalog/Fills/Lockers/heads.yml | 2 +- .../Entities/Objects/Devices/door_remote.yml | 20 +++++++++++++++++++ 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/Content.Server/Remotes/DoorRemoteSystem.cs b/Content.Server/Remotes/DoorRemoteSystem.cs index 27df5ef34d..c3425f347a 100644 --- a/Content.Server/Remotes/DoorRemoteSystem.cs +++ b/Content.Server/Remotes/DoorRemoteSystem.cs @@ -51,11 +51,19 @@ namespace Content.Shared.Remotes return; } + var accessTarget = args.Used; + // This covers the accesses the REMOTE has, and is not effected by the user's ID card. + if (entity.Comp.IncludeUserAccess) // Allows some door remotes to inherit the user's access. + { + accessTarget = args.User; + // This covers the accesses the USER has, which always includes the remote's access since holding a remote acts like holding an ID card. + } + if (TryComp(args.Target, out var accessComponent) - && !_doorSystem.HasAccess(args.Target.Value, args.Used, doorComp, accessComponent)) + && !_doorSystem.HasAccess(args.Target.Value, accessTarget, doorComp, accessComponent)) { if (isAirlock) - _doorSystem.Deny(args.Target.Value, doorComp, args.User); + _doorSystem.Deny(args.Target.Value, doorComp, accessTarget); Popup.PopupEntity(Loc.GetString("door-remote-denied"), args.User, args.User); return; } @@ -63,7 +71,7 @@ namespace Content.Shared.Remotes switch (entity.Comp.Mode) { case OperatingMode.OpenClose: - if (_doorSystem.TryToggleDoor(args.Target.Value, doorComp, args.Used)) + if (_doorSystem.TryToggleDoor(args.Target.Value, doorComp, accessTarget)) _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)}: {doorComp.State}"); @@ -73,7 +81,7 @@ namespace Content.Shared.Remotes { if (!boltsComp.BoltWireCut) { - _doorSystem.SetBoltsDown((args.Target.Value, boltsComp), !boltsComp.BoltsDown, args.Used); + _doorSystem.SetBoltsDown((args.Target.Value, boltsComp), !boltsComp.BoltsDown, accessTarget); _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to {(boltsComp.BoltsDown ? "" : "un")}bolt it"); diff --git a/Content.Shared/Remotes/Components/DoorRemoteComponent.cs b/Content.Shared/Remotes/Components/DoorRemoteComponent.cs index b157596e3b..64977596c2 100644 --- a/Content.Shared/Remotes/Components/DoorRemoteComponent.cs +++ b/Content.Shared/Remotes/Components/DoorRemoteComponent.cs @@ -8,6 +8,13 @@ public sealed partial class DoorRemoteComponent : Component [AutoNetworkedField] [DataField] public OperatingMode Mode = OperatingMode.OpenClose; + + /// + /// Does the remote allow the user to manipulate doors that they have access to, even if the remote itself does not? + /// + [AutoNetworkedField] + [DataField] + public bool IncludeUserAccess = false; } public enum OperatingMode : byte diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml index 2a15014ce2..6b1efddad1 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml @@ -43,7 +43,7 @@ - id: ClothingHeadsetAltCommand - id: ClothingOuterArmorCaptainCarapace - id: CommsComputerCircuitboard - - id: DoorRemoteCommand + - id: DoorRemoteCustom - id: MedalCase - id: NukeDisk - id: PinpointerNuclear diff --git a/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml b/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml index 45689958cf..8f7f6957b2 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/door_remote.yml @@ -29,6 +29,24 @@ - type: Access groups: - Command + - type: DoorRemote + +- type: entity + parent: [DoorRemoteDefault, BaseCommandContraband] + id: DoorRemoteCustom + name: custom door remote + description: A gadget which can open and bolt doors remotely. This advanced variant does not have built-in access, instead inheriting the ID access of the user. + components: + - type: Sprite + layers: + - state: door_remotebase + - state: door_remotelightscolour + color: "#0077FF" + - state: door_remotescreencolour + color: "#0033EE" + - type: Access + - type: DoorRemote + includeUserAccess: true - type: entity parent: [DoorRemoteDefault, BaseCommandContraband] @@ -155,6 +173,8 @@ color: "#2eba22" - state: door_remotescreencolour color: "#22871a" + - type: DoorRemote + includeUserAccess: true - type: Access groups: - AllAccess From ec2d4ddd5ea7a4f04453ceaff2faa2ebbbb277a5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 29 Jun 2025 16:22:22 +0000 Subject: [PATCH 147/191] Automatic changelog update --- Resources/Changelog/Admin.yml | 8 ++++++++ Resources/Changelog/Changelog.yml | 21 +++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 67fc878264..b10997e927 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1225,5 +1225,13 @@ Entries: id: 148 time: '2025-06-28T11:11:08.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38612 +- author: BramvanZijp + changes: + - message: The Super Door Remote can now effect doors that the user's ID card has + access to, even if the remote itself somehow cannot access them. + type: Tweak + id: 149 + time: '2025-06-29T16:21:15.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35536 Name: Admin Order: 2 diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 195ca58079..8c9af293ff 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: Centronias - changes: - - message: Tank harnesses can be made at lathes, similar to utility belts. - type: Add - - message: Tank harnesses take up a 1x2 space in inventories, half of their previous - 2x2. - type: Tweak - id: 8214 - time: '2025-04-17T12:33:31.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/35590 - author: Jacktastic09 changes: - message: Added the Solid Headband, available via hacked ClothesMate vending machines @@ -3887,3 +3877,14 @@ id: 8726 time: '2025-06-29T14:33:58.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38655 +- author: BramvanZijp + changes: + - message: Added the custom door remote, it does not have any built-in access, instead + inheriting the ID access that the user has. + type: Add + - message: The captain's locker now comes with a custom door remote instead of a + command door remote. + type: Tweak + id: 8727 + time: '2025-06-29T16:21:15.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35536 From cd2ce56aefae03a80ddc92d5280afe4834473a57 Mon Sep 17 00:00:00 2001 From: Nox Date: Sun, 29 Jun 2025 15:14:12 -0700 Subject: [PATCH 148/191] Added directional beacons (#38284) * Added directional beacons Signed-off-by: Nox38 * Fixed names Signed-off-by: Nox38 --------- Signed-off-by: Nox38 --- .../en-US/navmap-beacons/station-beacons.ftl | 16 +++ .../Objects/Devices/station_beacon.yml | 128 ++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/Resources/Locale/en-US/navmap-beacons/station-beacons.ftl b/Resources/Locale/en-US/navmap-beacons/station-beacons.ftl index 89b4266ddc..2d5deb8bb6 100644 --- a/Resources/Locale/en-US/navmap-beacons/station-beacons.ftl +++ b/Resources/Locale/en-US/navmap-beacons/station-beacons.ftl @@ -47,6 +47,14 @@ station-beacon-engineering = Engineering station-beacon-ce = CE station-beacon-ame = AME station-beacon-solars = Solars +station-beacon-solars-N = Solars N +station-beacon-solars-NE = Solars NE +station-beacon-solars-E = Solars E +station-beacon-solars-SE = Solars SE +station-beacon-solars-S = Solars S +station-beacon-solars-SW = Solars SW +station-beacon-solars-W = Solars W +station-beacon-solars-NW = Solars NW station-beacon-gravgen = Grav station-beacon-pa = PA Control station-beacon-smes = SMES @@ -81,4 +89,12 @@ station-beacon-tools = Tools station-beacon-disposals = Disposals station-beacon-cryosleep = Cryosleep station-beacon-escape-pod = Escape Pod +station-beacon-escape-pod-N = Escape Pod N +station-beacon-escape-pod-NE = Escape Pod NE +station-beacon-escape-pod-E = Escape Pod E +station-beacon-escape-pod-SE = Escape Pod SE +station-beacon-escape-pod-S = Escape Pod S +station-beacon-escape-pod-SW = Escape Pod SW +station-beacon-escape-pod-W = Escape Pod W +station-beacon-escape-pod-NW = Escape Pod NW station-beacon-vox = Vox Break Room diff --git a/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml b/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml index 569fd47d00..870e66654f 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/station_beacon.yml @@ -456,6 +456,70 @@ - type: NavMapBeacon defaultText: station-beacon-solars +- type: entity + parent: DefaultStationBeaconEngineering + id: DefaultStationBeaconSolarsN + suffix: Solars, North + components: + - type: NavMapBeacon + defaultText: station-beacon-solars-N + +- type: entity + parent: DefaultStationBeaconEngineering + id: DefaultStationBeaconSolarsNE + suffix: Solars, Northeast + components: + - type: NavMapBeacon + defaultText: station-beacon-solars-NE + +- type: entity + parent: DefaultStationBeaconEngineering + id: DefaultStationBeaconSolarsE + suffix: Solars, East + components: + - type: NavMapBeacon + defaultText: station-beacon-solars-E + +- type: entity + parent: DefaultStationBeaconEngineering + id: DefaultStationBeaconSolarsSE + suffix: Solars, Southeast + components: + - type: NavMapBeacon + defaultText: station-beacon-solars-SE + +- type: entity + parent: DefaultStationBeaconEngineering + id: DefaultStationBeaconSolarsS + suffix: Solars, South + components: + - type: NavMapBeacon + defaultText: station-beacon-solars-S + +- type: entity + parent: DefaultStationBeaconEngineering + id: DefaultStationBeaconSolarsSW + suffix: Solars, Southwest + components: + - type: NavMapBeacon + defaultText: station-beacon-solars-SW + +- type: entity + parent: DefaultStationBeaconEngineering + id: DefaultStationBeaconSolarsW + suffix: Solars, West + components: + - type: NavMapBeacon + defaultText: station-beacon-solars-W + +- type: entity + parent: DefaultStationBeaconEngineering + id: DefaultStationBeaconSolarsNW + suffix: Solars, Northwest + components: + - type: NavMapBeacon + defaultText: station-beacon-solars-NW + - type: entity parent: DefaultStationBeaconEngineering id: DefaultStationBeaconGravGen @@ -706,6 +770,70 @@ - type: NavMapBeacon defaultText: station-beacon-escape-pod +- type: entity + parent: DefaultStationBeacon + id: DefaultStationBeaconEscapePodN + suffix: Escape Pod, North + components: + - type: NavMapBeacon + defaultText: station-beacon-escape-pod-N + +- type: entity + parent: DefaultStationBeacon + id: DefaultStationBeaconEscapePodNE + suffix: Escape Pod, Northeast + components: + - type: NavMapBeacon + defaultText: station-beacon-escape-pod-NE + +- type: entity + parent: DefaultStationBeacon + id: DefaultStationBeaconEscapePodE + suffix: Escape Pod, East + components: + - type: NavMapBeacon + defaultText: station-beacon-escape-pod-E + +- type: entity + parent: DefaultStationBeacon + id: DefaultStationBeaconEscapePodSE + suffix: Escape Pod, Southeast + components: + - type: NavMapBeacon + defaultText: station-beacon-escape-pod-SE + +- type: entity + parent: DefaultStationBeacon + id: DefaultStationBeaconEscapePodS + suffix: Escape Pod, South + components: + - type: NavMapBeacon + defaultText: station-beacon-escape-pod-S + +- type: entity + parent: DefaultStationBeacon + id: DefaultStationBeaconEscapePodSW + suffix: Escape Pod, Southwest + components: + - type: NavMapBeacon + defaultText: station-beacon-escape-pod-SW + +- type: entity + parent: DefaultStationBeacon + id: DefaultStationBeaconEscapePodW + suffix: Escape Pod, West + components: + - type: NavMapBeacon + defaultText: station-beacon-escape-pod-W + +- type: entity + parent: DefaultStationBeacon + id: DefaultStationBeaconEscapePodNW + suffix: Escape Pod, Northwest + components: + - type: NavMapBeacon + defaultText: station-beacon-escape-pod-NW + - type: entity parent: DefaultStationBeacon id: DefaultStationBeaconVox From 34825464dcbeb71e0de6f9029a0a11f13aebc26f Mon Sep 17 00:00:00 2001 From: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com> Date: Sun, 29 Jun 2025 21:41:55 -0400 Subject: [PATCH 149/191] Power stat and nuke codes commands get some LEC love. (#38585) * commit * requested changes. --- .../Nuke/Commands/SendNukeCodesCommand.cs | 75 +++++++++---------- .../Power/Commands/PowerStatCommand.cs | 33 ++++---- .../en-US/commands/nukecodes-command.ftl | 2 + .../en-US/commands/powerstat-command.ftl | 6 ++ 4 files changed, 57 insertions(+), 59 deletions(-) create mode 100644 Resources/Locale/en-US/commands/nukecodes-command.ftl create mode 100644 Resources/Locale/en-US/commands/powerstat-command.ftl diff --git a/Content.Server/Nuke/Commands/SendNukeCodesCommand.cs b/Content.Server/Nuke/Commands/SendNukeCodesCommand.cs index d0e83e4f75..8ac4f95a97 100644 --- a/Content.Server/Nuke/Commands/SendNukeCodesCommand.cs +++ b/Content.Server/Nuke/Commands/SendNukeCodesCommand.cs @@ -1,55 +1,48 @@ using Content.Server.Administration; using Content.Server.Station.Components; using Content.Shared.Administration; -using JetBrains.Annotations; using Robust.Shared.Console; -namespace Content.Server.Nuke.Commands +namespace Content.Server.Nuke.Commands; + +[AdminCommand(AdminFlags.Fun)] +public sealed class SendNukeCodesCommand : LocalizedEntityCommands { - [UsedImplicitly] - [AdminCommand(AdminFlags.Fun)] - public sealed class SendNukeCodesCommand : IConsoleCommand + [Dependency] private readonly NukeCodePaperSystem _nukeCodeSystem = default!; + + public override string Command => "nukecodes"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) { - public string Command => "nukecodes"; - public string Description => "Send nuke codes to a station's communication consoles"; - public string Help => "nukecodes [station EntityUid]"; - - [Dependency] private readonly IEntityManager _entityManager = default!; - - public void Execute(IConsoleShell shell, string argStr, string[] args) + if (args.Length != 1) { - if (args.Length != 1) - { - shell.WriteError(Loc.GetString("shell-need-exactly-one-argument")); - return; - } - - if (!NetEntity.TryParse(args[0], out var uidNet) || !_entityManager.TryGetEntity(uidNet, out var uid)) - { - shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number")); - return; - } - - _entityManager.System().SendNukeCodes(uid.Value); + shell.WriteError(Loc.GetString("shell-need-exactly-one-argument")); + return; } - public CompletionResult GetCompletion(IConsoleShell shell, string[] args) + if (!NetEntity.TryParse(args[0], out var uidNet) || !EntityManager.TryGetEntity(uidNet, out var uid)) { - if (args.Length != 1) - { - return CompletionResult.Empty; - } - - var stations = new List(); - var query = _entityManager.EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var stationData)) - { - var meta = _entityManager.GetComponent(uid); - - stations.Add(new CompletionOption(uid.ToString(), meta.EntityName)); - } - - return CompletionResult.FromHintOptions(stations, null); + shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number")); + return; } + + _nukeCodeSystem.SendNukeCodes(uid.Value); + } + + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + if (args.Length != 1) + return CompletionResult.Empty; + + var stations = new List(); + var query = EntityManager.EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _)) + { + var meta = EntityManager.GetComponent(uid); + + stations.Add(new CompletionOption(uid.ToString(), meta.EntityName)); + } + + return CompletionResult.FromHintOptions(stations, null); } } diff --git a/Content.Server/Power/Commands/PowerStatCommand.cs b/Content.Server/Power/Commands/PowerStatCommand.cs index 127050393a..b9d71ee9bc 100644 --- a/Content.Server/Power/Commands/PowerStatCommand.cs +++ b/Content.Server/Power/Commands/PowerStatCommand.cs @@ -3,25 +3,22 @@ using Content.Server.Power.EntitySystems; using Content.Shared.Administration; using Robust.Shared.Console; -namespace Content.Server.Power.Commands +namespace Content.Server.Power.Commands; + +[AdminCommand(AdminFlags.Debug)] +public sealed class PowerStatCommand : LocalizedEntityCommands { - [AdminCommand(AdminFlags.Debug)] - public sealed class PowerStatCommand : IConsoleCommand + [Dependency] private readonly PowerNetSystem _powerNet = default!; + + public override string Command => "powerstat"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) { - [Dependency] private readonly IEntityManager _e = default!; - - public string Command => "powerstat"; - public string Description => "Shows statistics for pow3r"; - public string Help => "Usage: powerstat"; - - public void Execute(IConsoleShell shell, string argStr, string[] args) - { - var stats = _e.System().GetStatistics(); - - shell.WriteLine($"networks: {stats.CountNetworks}"); - shell.WriteLine($"loads: {stats.CountLoads}"); - shell.WriteLine($"supplies: {stats.CountSupplies}"); - shell.WriteLine($"batteries: {stats.CountBatteries}"); - } + var stats = _powerNet.GetStatistics(); + shell.WriteLine(Loc.GetString("cmd-powerstat-output", + ("networks", stats.CountNetworks), + ("loads", stats.CountLoads), + ("supplies", stats.CountSupplies), + ("batteries", stats.CountBatteries))); } } diff --git a/Resources/Locale/en-US/commands/nukecodes-command.ftl b/Resources/Locale/en-US/commands/nukecodes-command.ftl new file mode 100644 index 0000000000..50e324c524 --- /dev/null +++ b/Resources/Locale/en-US/commands/nukecodes-command.ftl @@ -0,0 +1,2 @@ +cmd-nukecodes-desc = Send nuke codes to a station's communication consoles. +cmd-nukecodes-help = Usage: nukecodes diff --git a/Resources/Locale/en-US/commands/powerstat-command.ftl b/Resources/Locale/en-US/commands/powerstat-command.ftl new file mode 100644 index 0000000000..c22cd5e22a --- /dev/null +++ b/Resources/Locale/en-US/commands/powerstat-command.ftl @@ -0,0 +1,6 @@ +cmd-powerstat-desc = Shows statistics for pow3r. +cmd-powerstat-help = Usage: powerstat +cmd-powerstat-output = Networks: {$networks} + Loads: {$loads} + Supplies: {$supplies} + Batteries: {$batteries} From 32f5551e2a43193cecaec747943a28c5a8033430 Mon Sep 17 00:00:00 2001 From: Kowlin <10947836+Kowlin@users.noreply.github.com> Date: Mon, 30 Jun 2025 23:09:16 +0200 Subject: [PATCH 150/191] Tweaks to admin CSV exporting (#38531) --- .../Administration/UI/Logs/AdminLogsEui.cs | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/Content.Client/Administration/UI/Logs/AdminLogsEui.cs b/Content.Client/Administration/UI/Logs/AdminLogsEui.cs index 2c64b82d20..1544b827ae 100644 --- a/Content.Client/Administration/UI/Logs/AdminLogsEui.cs +++ b/Content.Client/Administration/UI/Logs/AdminLogsEui.cs @@ -20,6 +20,10 @@ public sealed class AdminLogsEui : BaseEui [Dependency] private readonly IFileDialogManager _dialogManager = default!; [Dependency] private readonly ILogManager _log = default!; + private const char CsvSeparator = ','; + private const string CsvQuote = "\""; + private const string CsvHeader = "Date,ID,PlayerID,Severity,Type,Message"; + private ISawmill _sawmill; private bool _currentlyExportingLogs = false; @@ -100,7 +104,9 @@ public sealed class AdminLogsEui : BaseEui try { - await using var writer = new StreamWriter(file.Value.fileStream); + // Buffer is set to 4KB for performance reasons. As the average export of 1000 logs is ~200KB + await using var writer = new StreamWriter(file.Value.fileStream, bufferSize: 4096); + await writer.WriteLineAsync(CsvHeader); foreach (var child in LogsControl.LogsContainer.Children) { if (child is not AdminLogLabel logLabel || !child.Visible) @@ -108,28 +114,31 @@ public sealed class AdminLogsEui : BaseEui var log = logLabel.Log; + // Date // I swear to god if someone adds ,s or "s to the other fields... await writer.WriteAsync(log.Date.ToString("s", System.Globalization.CultureInfo.InvariantCulture)); - await writer.WriteAsync(','); + await writer.WriteAsync(CsvSeparator); + // ID await writer.WriteAsync(log.Id.ToString()); - await writer.WriteAsync(','); - await writer.WriteAsync(log.Impact.ToString()); - await writer.WriteAsync(','); - // Message - await writer.WriteAsync('"'); - await writer.WriteAsync(log.Message.Replace("\"", "\"\"")); - await writer.WriteAsync('"'); - // End of message - await writer.WriteAsync(','); - + await writer.WriteAsync(CsvSeparator); + // PlayerID var players = log.Players; for (var i = 0; i < players.Length; i++) { await writer.WriteAsync(players[i] + (i == players.Length - 1 ? "" : " ")); } - - await writer.WriteAsync(','); + await writer.WriteAsync(CsvSeparator); + // Severity + await writer.WriteAsync(log.Impact.ToString()); + await writer.WriteAsync(CsvSeparator); + // Type await writer.WriteAsync(log.Type.ToString()); + await writer.WriteAsync(CsvSeparator); + // Message + await writer.WriteAsync(CsvQuote); + await writer.WriteAsync(log.Message.Replace(CsvQuote, CsvQuote + CsvQuote)); + await writer.WriteAsync(CsvQuote); + await writer.WriteLineAsync(); } } From bfefb915749ccd3ba93dc0bf9dd2fb382be77540 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 30 Jun 2025 21:10:26 +0000 Subject: [PATCH 151/191] Automatic changelog update --- Resources/Changelog/Admin.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index b10997e927..62c8d88d0d 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1233,5 +1233,12 @@ Entries: id: 149 time: '2025-06-29T16:21:15.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/35536 +- author: Kowlin + changes: + - message: Changed the way CSVs are exported + type: Tweak + id: 150 + time: '2025-06-30T21:09:16.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38531 Name: Admin Order: 2 From 0009ad32ab90395c1eab01e282f7c1f450c638e4 Mon Sep 17 00:00:00 2001 From: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com> Date: Mon, 30 Jun 2025 19:33:59 -0400 Subject: [PATCH 152/191] Dsay Dirty and Follow commands converted to LEC and localized. (#38666) * commit * whoopwhoopwhoop --- .../Administration/Commands/DSay.cs | 57 +++++++++---------- .../Administration/Commands/DirtyCommand.cs | 22 +++---- .../Administration/Commands/FollowCommand.cs | 17 ++---- .../administration/commands/dsay-command.ftl | 2 - .../commands/follow-command.ftl | 2 - .../Locale/en-US/commands/dirty-command.ftl | 2 + .../Locale/en-US/commands/dsay-command.ftl | 2 + .../Locale/en-US/commands/follow-command.ftl | 2 + 8 files changed, 48 insertions(+), 58 deletions(-) delete mode 100644 Resources/Locale/en-US/administration/commands/dsay-command.ftl delete mode 100644 Resources/Locale/en-US/administration/commands/follow-command.ftl create mode 100644 Resources/Locale/en-US/commands/dirty-command.ftl create mode 100644 Resources/Locale/en-US/commands/dsay-command.ftl create mode 100644 Resources/Locale/en-US/commands/follow-command.ftl diff --git a/Content.Server/Administration/Commands/DSay.cs b/Content.Server/Administration/Commands/DSay.cs index 8e7f0f4bf0..f5e0b32793 100644 --- a/Content.Server/Administration/Commands/DSay.cs +++ b/Content.Server/Administration/Commands/DSay.cs @@ -2,39 +2,36 @@ using Content.Server.Chat.Systems; using Content.Shared.Administration; using Robust.Shared.Console; -namespace Content.Server.Administration.Commands +namespace Content.Server.Administration.Commands; + +[AdminCommand(AdminFlags.Moderator)] +public sealed class DsayCommand : LocalizedEntityCommands { - [AdminCommand(AdminFlags.Moderator)] - sealed class DSay : IConsoleCommand + [Dependency] private readonly ChatSystem _chatSystem = default!; + + public override string Command => "dsay"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) { - [Dependency] private readonly IEntityManager _e = default!; - - public string Command => "dsay"; - - public string Description => Loc.GetString("dsay-command-description"); - - public string Help => Loc.GetString("dsay-command-help-text", ("command", Command)); - - public void Execute(IConsoleShell shell, string argStr, string[] args) + if (shell.Player is not { } player) { - if (shell.Player is not { } player) - { - shell.WriteError(Loc.GetString("shell-cannot-run-command-from-server")); - return; - } - - if (player.AttachedEntity is not { Valid: true } entity) - return; - - if (args.Length < 1) - return; - - var message = string.Join(" ", args).Trim(); - if (string.IsNullOrEmpty(message)) - return; - - var chat = _e.System(); - chat.TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Dead, false, shell, player); + shell.WriteError(Loc.GetString("shell-cannot-run-command-from-server")); + return; } + + if (player.AttachedEntity is not { Valid: true } entity) + { + shell.WriteError(Loc.GetString("shell-must-be-attached-to-entity")); + return; + } + + if (args.Length < 1) + return; + + var message = string.Join(" ", args).Trim(); + if (string.IsNullOrEmpty(message)) + return; + + _chatSystem.TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Dead, false, shell, player); } } diff --git a/Content.Server/Administration/Commands/DirtyCommand.cs b/Content.Server/Administration/Commands/DirtyCommand.cs index cc0b0fcec6..dd0b79291c 100644 --- a/Content.Server/Administration/Commands/DirtyCommand.cs +++ b/Content.Server/Administration/Commands/DirtyCommand.cs @@ -4,22 +4,18 @@ using Robust.Shared.Console; namespace Content.Server.Administration.Commands; [AdminCommand(AdminFlags.Debug)] -public sealed class DirtyCommand : IConsoleCommand +public sealed class DirtyCommand : LocalizedEntityCommands { - [Dependency] private readonly IEntityManager _entManager = default!; + public override string Command => "dirty"; - public string Command => "dirty"; - public string Description => "Marks all components on an entity as dirty, if not specified, dirties everything"; - public string Help => $"Usage: {Command} [entityUid]"; - - public async void Execute(IConsoleShell shell, string argStr, string[] args) + public override async void Execute(IConsoleShell shell, string argStr, string[] args) { switch (args.Length) { case 0: - foreach (var entity in _entManager.GetEntities()) + foreach (var entity in EntityManager.GetEntities()) { - DirtyAll(_entManager, entity); + DirtyAll(entity); } break; case 1: @@ -28,7 +24,7 @@ public sealed class DirtyCommand : IConsoleCommand shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number")); return; } - DirtyAll(_entManager, _entManager.GetEntity(parsedTarget)); + DirtyAll(EntityManager.GetEntity(parsedTarget)); break; default: shell.WriteLine(Loc.GetString("shell-wrong-arguments-number")); @@ -36,11 +32,11 @@ public sealed class DirtyCommand : IConsoleCommand } } - private static void DirtyAll(IEntityManager manager, EntityUid entityUid) + private void DirtyAll(EntityUid entityUid) { - foreach (var component in manager.GetNetComponents(entityUid)) + foreach (var component in EntityManager.GetNetComponents(entityUid)) { - manager.Dirty(entityUid, component.component); + EntityManager.Dirty(entityUid, component.component); } } } diff --git a/Content.Server/Administration/Commands/FollowCommand.cs b/Content.Server/Administration/Commands/FollowCommand.cs index b59a99b8b4..27ecbb99cf 100644 --- a/Content.Server/Administration/Commands/FollowCommand.cs +++ b/Content.Server/Administration/Commands/FollowCommand.cs @@ -6,15 +6,13 @@ using Robust.Shared.Enums; namespace Content.Server.Administration.Commands; [AdminCommand(AdminFlags.Admin)] -public sealed class FollowCommand : IConsoleCommand +public sealed class FollowCommand : LocalizedEntityCommands { - [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly FollowerSystem _followerSystem = default!; - public string Command => "follow"; - public string Description => Loc.GetString("follow-command-description"); - public string Help => Loc.GetString("follow-command-help"); + public override string Command => "follow"; - public void Execute(IConsoleShell shell, string argStr, string[] args) + public override void Execute(IConsoleShell shell, string argStr, string[] args) { if (shell.Player is not { } player) { @@ -34,10 +32,7 @@ public sealed class FollowCommand : IConsoleCommand return; } - var entity = args[0]; - if (NetEntity.TryParse(entity, out var uidNet) && _entManager.TryGetEntity(uidNet, out var uid)) - { - _entManager.System().StartFollowingEntity(playerEntity, uid.Value); - } + if (NetEntity.TryParse(args[0], out var uidNet) && EntityManager.TryGetEntity(uidNet, out var uid)) + _followerSystem.StartFollowingEntity(playerEntity, uid.Value); } } diff --git a/Resources/Locale/en-US/administration/commands/dsay-command.ftl b/Resources/Locale/en-US/administration/commands/dsay-command.ftl deleted file mode 100644 index 4f9a7cc221..0000000000 --- a/Resources/Locale/en-US/administration/commands/dsay-command.ftl +++ /dev/null @@ -1,2 +0,0 @@ -dsay-command-description = Sends a message to deadchat as an admin -dsay-command-help-text = Usage: {$command} \ No newline at end of file diff --git a/Resources/Locale/en-US/administration/commands/follow-command.ftl b/Resources/Locale/en-US/administration/commands/follow-command.ftl deleted file mode 100644 index 3ef5fde075..0000000000 --- a/Resources/Locale/en-US/administration/commands/follow-command.ftl +++ /dev/null @@ -1,2 +0,0 @@ -follow-command-description = Makes you begin following an entity -follow-command-help = Usage: follow [netEntity] \ No newline at end of file diff --git a/Resources/Locale/en-US/commands/dirty-command.ftl b/Resources/Locale/en-US/commands/dirty-command.ftl new file mode 100644 index 0000000000..c3e7bc1c39 --- /dev/null +++ b/Resources/Locale/en-US/commands/dirty-command.ftl @@ -0,0 +1,2 @@ +cmd-dirty-desc = Marks all components on an entity as dirty. If not specified, dirties everything. +cmd-dirty-help = Usage: dirty [entityUid] diff --git a/Resources/Locale/en-US/commands/dsay-command.ftl b/Resources/Locale/en-US/commands/dsay-command.ftl new file mode 100644 index 0000000000..eef9747f86 --- /dev/null +++ b/Resources/Locale/en-US/commands/dsay-command.ftl @@ -0,0 +1,2 @@ +cmd-dsay-desc = Sends a message to deadchat as an admin. +cmd-dsay-help = Usage: dsay diff --git a/Resources/Locale/en-US/commands/follow-command.ftl b/Resources/Locale/en-US/commands/follow-command.ftl new file mode 100644 index 0000000000..04c805bc50 --- /dev/null +++ b/Resources/Locale/en-US/commands/follow-command.ftl @@ -0,0 +1,2 @@ +cmd-follow-desc = Makes you begin following an entity. +cmd-follow-help = Usage: follow [netEntity] From 89f982fe1c24dd66d6d29d7766db668f2eba73ad Mon Sep 17 00:00:00 2001 From: Nox Date: Mon, 30 Jun 2025 16:49:44 -0700 Subject: [PATCH 153/191] Retro laser sprite fix (#38676) * Fixed everything except the icon Signed-off-by: Nox38 * fixed icon Signed-off-by: Nox38 --------- Signed-off-by: Nox38 --- .../Guns/Battery/laser_retro.rsi/base.png | Bin 2204 -> 518 bytes .../Guns/Battery/laser_retro.rsi/icon.png | Bin 2316 -> 912 bytes .../Battery/laser_retro.rsi/mag-unshaded-0.png | Bin 2201 -> 142 bytes .../Battery/laser_retro.rsi/mag-unshaded-1.png | Bin 2246 -> 168 bytes .../Battery/laser_retro.rsi/mag-unshaded-2.png | Bin 2277 -> 171 bytes .../Battery/laser_retro.rsi/mag-unshaded-3.png | Bin 2280 -> 171 bytes .../Battery/laser_retro.rsi/mag-unshaded-4.png | Bin 2323 -> 182 bytes 7 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/Textures/Objects/Weapons/Guns/Battery/laser_retro.rsi/base.png b/Resources/Textures/Objects/Weapons/Guns/Battery/laser_retro.rsi/base.png index 5833e7876af99ec98d19db31dbbc9e9e6cbb5701..8a94ea7a87dd716fe6106b92a82971d5b59f0071 100644 GIT binary patch delta 493 zcmVa zz)2d%i1p9VCA`F~pdgfDm#%`JK!k$r=V=|?I+O}_tW%}n;B4~{I^~goqErlYeI06o znwK}!E#qZl~VP9gip{QwrL&t^z+p^GT zG(uywEej@N_*yQv>a|*p13<6W+cM%MpePDv^SN)}?d1T^A9WNp4P(QA$yk^^L@&an zU(IYL<2&ma(0_GZIBYQ20ZfI1|AU*v-`^l4U$~yLdPO-mdH3Ioq~j+qSR&&y99DFPHgQE+-UM(rEyI z<2b$qO~z2j=keO_2Y&@o_+G6-#Ydx&FhNSGL@5oN8)hj1LI}UARxz7SJ#mvU@7hYG z65lZqnMgmCF!6l}9dc;#Py@#h4>WKr@lXT1wL^%<#Kgn^zz^yr(*`t37C-<101jnX jNoGw=04e|g00;m8000000Mb*F00000NkvXXu0mjf*c#Q! delta 2193 zcmV;C2yXX=1e_6&BYyaB^>EX>4U6ba`-PAZ2)IW&i+q+U-|KlH@iF zyz>=)0tpaYo&#Jmu^!jcI>YB&G+d%3@G5c%Yyg3~Dv@*?NU4LwB^=<-aD5f$;-KOPVG ztu@M8^TVK%lbS8C6WzV4404MLq63&dDV4n9`aTc$>gSCNFoAV8e8VHE55W@%> zR+xM2p(DgdlQKr44#Z7XI8&t#G6F|C!=miC^56m{I+$aK>53(6zt#M~S%K#fph*G7 z!143xUIm|?PlndAG7rI*3oFD$R$mCin6oRiKuB08rhl^W4el8H>tVz)7?cfjL4xYr zVWLU6*A{P{*^7f}^v8Y)d7K6S5qt}R2@D2&3M?T7pNVn^z_EZIMdZYh8W;%Tga)Fd zi0TJ9h0Hge$zkh~&>=>90TF^Nl0i*n1z9mlOxSM)C8SPRqKG8PQlyF|PBBSJDJSbw znmNZTIe+DxORiZ9msq5vl1nMIXr;<%W8!M6xt3b1Dx7Lm*iz^#1dSTE*rcVFTWPgv zNBQ*FrKg^I>9y;i!2>P8jWqHoqYj;6qLgNwGSken%sO?U))ue0WTll?S#{~f+R5rO zYp=}xH*4W!jW$uHm+!1Wob$~@uy!KG85pA&z<+o)22jvsocR)BBxBAv^Oe{!3n5A| zaz-%5z+jw#WQB{nJ9A(1=78?c@<#8Nb4J~FFz1ZA2j+g{?G0-~ce~|HknKWXiVn%x zeo&Z2si?h|J{sMp5B@Rq+Zbvn(LI}Unrwx32*+@?4dA6K(7Uo#n)ak2$Qtg62x7AyGCEfxC=1Y5Ok{0^+pq1G^&ZYJyWNm<8Z%B}Ogxjg1hH%GEX@6{G004NL zeUUv#!$2IxUt3E>TO7wjIGXL+Cdb3~M)$pD{7Jj-;$B3>t+-n4Yi`@{mPNDA>e z@t8puB!1+&?D8AuvcrC!DHyrT9I-$wmb+N)VpcI!;wj>oqH2`y&%3N}-r}s*>a2ZF z{=#rkTh4Nw<}i|2L<(t$kWoh!HCTw#s*z$ML;DF2f7J1($t9Dk3V%k9d2B$1!6%3MYWzi*0|50HIx=)v)dFW7}?>0D))VO6&MHn!wB_>5YySI|2r_fs5;o zChq~4JHXJBE*X*|1!($9CE)#xzNr8V-U5BAZg1^T2l*I5-4G%apz5@$TO4 z-u^w)?(YXhrE-;Vl7CGf00006VoOIv0RI600RN!9r;`8x010qNS#tmYE+YT{E+YYW zr9XB6000McNliru+ZbMbi42J9`CR3 z=Y8Jic{vCaC{Q2|sj4beRTZmRFL^EA&gB5OyT4~Mne=7aF$@3{MFBwG*Z|<-^3vuP za@*wW^iqcRO_;fL5y|xdIf6MN7Ee-rvpdLOw6ziNs8R zmtGHmOe!^Nntusm14zVT^!j~-WIb#qmEx({G{ZYf6Z4~Vne*En+qf*t42Q$t2z+iy z;v{q6dt<-4ebq>(oyRNy;kJ-yUjd+Ln)q+!jK*V2>y)6AJg{9sqtO5$9*>)SyOlE9 z2OJ+BG8&J~__n5t ToECim015yANkvXXu0mjfXa*$l diff --git a/Resources/Textures/Objects/Weapons/Guns/Battery/laser_retro.rsi/icon.png b/Resources/Textures/Objects/Weapons/Guns/Battery/laser_retro.rsi/icon.png index f3cba12470477e5b65672232cb5e400424c88f93..bbd544a39468ecfa19bcc51a0514ce392899bdf2 100644 GIT binary patch delta 890 zcmV-=1BLvI5|9UwBYy#YX+uL$Nkc;*aB^>EX>4Tx04R}tkv&L4Q5c4wd(DbS9CC48E7?hfBXaQRqj<<9D~-Np`RtIp{2RjhSnem`hjR_Y$|GrxN%fa zBKwx}@SgAeINy6fuI6>q=@n|9zaf~;q4TB zN$9(Qp4ED8jeAb+Azp#SYuG=4;SBiK+U(ukn%loUjeq=p07y)7mRH+{(*OVgvq?lj zR9J=WlRs<1Kp4iKfPz?bYsC78(j`3=7eS#=M2F5oKY~!O{XE5QP^eh2V;u@oaFCAH zL+DUE3@A#)K!+~ZK@*J0X{hLw-;(6L_ul8ZH}?WAE-o$t2qByh!uP%3V;8(BmpK58 zr&B13!hieyJ^&ydi#ZY>Fow&E3;mN*N+m}IbjNS)wr%q1YZGbT;yvv(ubmt%o?)Ma zjQ8BMc2}wJ`)bwJglAPal}HFr)$R50RlLG)Bmw|<7v!nKA;1{EXERg0R4Q=*Xfzro zBSr^gS;lxWu@t<`w^0AMMkc*0Gg&*Iyhhq@mLHm&8Bz?obWTB zZ*vsybUL;jq?AgOQej<39Uz48VxfTXXk>^F7&CrbHk;*ZCLFVM!#43<*;r)X;u{Sd zLR@IzP~sa6tjG2t?&9L&0swz(-_W)Rrhha5000hUSV?A0O#mtY000O800000007cc QlK=n!07*qoM6N<$g0G;NGynhq delta 2305 zcmV+c3I6tw2aFPsBYy=tdQ@0+Qek%>aB^>EX>4U6ba`-PAZ2)IW&i+q+U-|clH@21 z{O1&V1SAlG4u9cdt~F%QT3RU) zS8lnn;ibmwx>}0)JkQ1VC-mRQ!{Y-(l+dQDTh^^_kjry`=6_q>-^zoI+fKa=-5*{I zCZE}7o41pj?vy)3J|!|9fje2(8i>9t41kD2+kM^mH5 z*Vk+O_N;raYkzf?O8{KuUewD{&Knc~X-;QY!L9fS-u2uKca0VYSesaC53!@uh=KT~ zOp}Z@tMg78d6I$Rj@L^T!NS%zR;Ps`FxnHV&+NE zWP&kp{Pqs-1>e2j7~0#zoG8`@R`82GzTk#4=TB&X5P#Y?rgHG5a5eG$<-{g1C z1zPNvi7xbvt#o)!Q36zRyz+%~-Ua{><`y_Z2@J%7@TNjhnUx4QR`6r+oKxlw21tVN zgqNICDj=t_&y6ukN0)^!G0F>wkW`Q)6$BZ`%2{$jzEy;f1xb=cq=-tDCRS3(MM^1J zyiS=W&3`IdRJCeTtEuKHwN$OOwrY)ALK&0LO4C+bYi5|#vcZ-?-yrDRQ_o#`>Dp^= z-3G#EdCM+acinB9W}xt=~*gIM*&1bI0T=M0Q-CVw!l&Hw_MowHaeXLjbCv)DKVR4Im+ z;^a(n#=u}+#IotZ-IKX5c?&@IS9#;lmG-yQhJ(Er9zM~I%cwU)~^^-DNs@NR18VkfKL==5kcrdGmb5i#uD zGk>Yt%#Yb-<30*LMmw&3*@o!a`8Yq#c0y8QI&F++0b`rj`eDf1DI?FarlJP!*tS%p zVCl5_Ld|YncTr?iZ(|PV^)hX*j6U+;FpZ!9Y*d4-edAD2Sq&-Gv+kL^V79&&Dr7|! z!7L3^f@XhL2R-1f|FvXA4`a=pHXx)wSphg}Qs z?iNa$C>(tmY8C(yMcF|)>(OnA+oDl%`oQ``GtPYP-eq)AGYKw-58cP*iE@kjEd*8D z7p8=X-M03&MggR75w#JJ?$JjttLa?8sC0my&&cY=P(ByfDLk(y&VN%B1gYT!hkq7+ zxH}ku%4SOtFo=^H5Ix!M4h}wcugJap`u#Q)2L>X{WpxSG2f)MTh0f?}i%mV=&Q;3J zUa7e+U)aN#tV(0O!zw&G?mo}ANYS&UH3b2PzoT86ES<2Kyv;GtaoB;MF{NQ}br*y- zdEHNJ0}cY#z2L&;*=>5{G+qN#V1ENu5ATIsJ3M0r$g?eDVeb(@R_#hZ-nT#FQ3R~Q z4+j~Lc6e9l_#0lmKVRLrN5sbkeq1o@R*@0EX>4Tx0C=2zkv&MmKpe$i zTWdvHI@m$PAw%nAK~%(1s()An3#F~ls)Na;U(lo>NpW!$Tni3^-MA$~P%l+J+BP16M26zPGIi~3*@dokCrm1n>Cyua+D1Q^56OZe3LE=ZQ zOAfzrF5B$onGrpgnJ11A3*{b`dYDyog?O4cDl01Gd-D#sElHTlU(IX(d4P0DzHE9pH+yRE4 zG*K6A$w$*$Dgp0j^i2g|=oT1Ub9(FSe@vRQBW;2 z5`*3#d!H6Tj}W!3AeA6UY!jgjNDC*EEGU#BXmMMlljF?jShkzr<_!1W`OdlLTn0RO z^5kiVR80;EzYQ@UPWUybjLnH9p3 z$iV0y>UGd{60z8zWd?`^AQB2uZ!{2+Ia`TXjN5YANPq7jb)Bc zGVnetiS5L?>x12@@SG(cx1Tcs_^U#qdJI4|n-zcUoL0MSvUUa9tpm#ylu9K4!r`zn zmd<1-WVU!pZ_;YFjp9-ig~>p`(TSaU3vgf?uC1<$nyy<~ucqq+I!>F9kG2qZ_xD_1 zT}f`T<$BnLC#R=UZ^iaUkW40p91Nmqng!y$)pC7ZzF`acQ`U73P%IWDfC;}}Oh=;z z#PZS-fUw&;(n~n%liLd+2ZNmFatwnQ8^CWN#tQHoh_M5V3gPJvg diff --git a/Resources/Textures/Objects/Weapons/Guns/Battery/laser_retro.rsi/mag-unshaded-0.png b/Resources/Textures/Objects/Weapons/Guns/Battery/laser_retro.rsi/mag-unshaded-0.png index 36ef0cea023526f2ce5a199cf707f582dd0f9c94..9c47712c1bb7ae6d5c1f7723359138157c9bdcf7 100644 GIT binary patch delta 114 zcmbO!*vB|QIh%pAz$3Dlfk96hgc&QA+LtjfFsOODIEF;DzCFjt%b>u+yn%B^?H4{t z{w1LoSQvf;PM+tvU7q29wBET;=J{RsBTfKy@TEq0ruq6ZXaU(AKnwzxf+vG0Pgg&e IbxsLQ03-aB^>EX>4U6ba`-PAZ2)IW&i+q+U-|alH@21 z{pS>O1V9MEal{reH<;tk6Lvb6&YFqNuAh!*yV8IxYk5z|E;s-Ad%3@G#OSk2EG5^R zJ&sgUjfzR}$8n6or+wW=Ja1wBBzM=12oYhVW0~ivuaNWo0e?xSygkdE^wUl`4F&(? zn#{A!#~I{lh~gm+=i1ft0nHEToWIylv$wQf)=2=yVA!MR;-o-+U*qA4j-TSkxCSCq z#Hd7+kL!>4xeK~C0A8~f{QNC4y2Ei8x}}w0a609IU*vSTiBA!QczKSG5f%30ACCw8 z)*5B4d1R*vb$?V-K9jnovS(0ifRpxP8K>aKn9DvZ&f?W~ur`otbG~6wLj>Y0q%eYp z73Lm$=m;^z83kjE9f+H(ki;50$Ox3|3@eJGN`nem>0pi^j#n&U`>o~=&I&w_0L>U+ z3>-h7?p5&V`DAD_uQqIUb~JsXrKkSk%x>j>d+Y`N@=FaGf$ai)~O4%wrHirD=%4P)uk6}C#%n_ zy)yUTtc8=cxQQ}7xw8hb$jwBscEZLP7>g-@aepxeP|(ph^ChGdjXC4Y*GP_tBcfy@ zX9Qym45k?(uW)g9XYNbh9MJt)-r_svoKg23%sHd(fw>=fd&An$-EL_UWV;ZUqC*n4 z9~5TMSk&H2AC2zQ2mcuQZ45P(=$_3vO}0Wigkv~cQpFN)vhu;Ac`cr5c85jOur0S! z(SMOHZ==T6Gj4j_p?x_zZ`x@GKU5cCQ>3s*?{@-ysrCA2gGGM@$ zN&qC`oR`UFOgG_Q&hdggGE+2XKnY`ubuiiyHFOw8whKJwwk+7Zhy)E6Z1S?iE@KWP z`6$mN)RsU4NWsd7t}pM3kb*0G~)a%XGscUMHU3v~7jtCxGCKZGVgap-aaCz|1GL_glBfiB2^J}K3dVG24?)z)jtNGGZXR9#PlupCA?Rv{ zD1sodTQ~;M!MQ{c^7qvay49L>d$jzluK5a7cw0FcvZPgqq|odC)0ZH#7pJmS&~~de7vjCq;N|(**J-)h(9Lq@4zk%S!qyg=ra2(S{l4#Xt$7XTbUL=n z3=UAKTU6-wdPE`-VzC&}XmnbvvdnNew8sAlV4e)az?k{ng!~CH8jXBItg#|33$ZeQ vWg%7yuna`_KR)5Vy-uwb{MQ5u6v$Oy5uwWi8)`c900000NkvXXu0mjf-xM5V diff --git a/Resources/Textures/Objects/Weapons/Guns/Battery/laser_retro.rsi/mag-unshaded-1.png b/Resources/Textures/Objects/Weapons/Guns/Battery/laser_retro.rsi/mag-unshaded-1.png index c456d45dcb4aa35c9dd9a7b9b6fe70c0c03912df..2b59b5aa0ab6630734e89527344f7b92145dafff 100644 GIT binary patch delta 141 zcmX>mxPozlayaB^>EX>4U6ba`-PAZ2)IW&i+q+U-|cmgFW3 z{bvM z!wPe^J#@I3;*5ee#ty_yR!Cxv9b^Omk29<&jw%f*V5NgOf;irN3EOTpdoWgDc?8Ui z0m{Jf^XXm%Up=1`tz~H*f-iTh;8(Q#f*Z!1-7yP$1>b`?HXVg6~_akrbur_orx3meeDg>rj zAqnM&3A1P{YWvccTKCl#e=GXmC~8R2-J5foY=w3S$8a{JiXnEg@`XY3T0GV4E{(`x z8-H%2q9fg{jT&3ec<6Pz_F?P1%}(3-p}Gh~k-~1h-vRWcw$^uLEDwx|%5!+^r!yB_ zIhYdaqV8$*Ho%er=LT-2O_en!4SX|p*p+3k_M^tDkGsn1Dm#$ zQPJ7FRSPgt^^*-`-5|g88x|Y4IH}1Euz!OW>j@n-uai2@2*aj(360Vf-w<%lPr-tK z1t?3F=nlX->vew*K0T{N4s7tTlZ0MFDQg+8E~jdj7>Hd^mvmI26Ec3^j9Z9rRpz{))VXKn+cXQUpIg?|uY z3mLx+^FWR*h0ff%Tb7vHVITxJvQ&nvvk*@i+__NCG9YeaG%hPiSj8^||Y_^v*-o#h~cttmF%U=9yx5Pvs_UU&mF;cm$QOze>&0Wylptg-!N`PtU}EX>4Tx0C=2zkv&MmKpe$iTT4Y-9LylaAwzYtAS&W0 zRV;#q(pG5I!Q|2}XktiGTpR`0f`cE6RRs_2@d7t}p zM3kb*0G~)a%XGscUMHU3v~7jtCxGCKZGVgap-aaCz|1GJ-<_?0m)zJ>6gLlw+AKrluf|nq;>L7|BSn49u5GW#z$6_g1`y+03&_-ht z8*1J1LP&DXch2{m?}r2e1qu{sj8s)cRn>o6FO6Hgwz>+yf5qh`ON)!XoHk4o07X#% zkmu(C*grUM*oM6}*)EkhJv+0F9iNdfv$8NTyP*0M*ZEJ_-S*iIz@}jg+qIPJmP*foL`vO7`q%=`^>snibyPG;&_Z z9I|)5?wFTlf01$M$+*wi0A}h#s2>4PE|>q@Io)2*c6Te# zQ5`r^P_0%0NG6lkSfN-P7Ardx@0X=+uV)pPqA1M8411Y_^$~RD3GV#0ZWU{W_&x-#Q*>R07*qoM6N<$f`f=9qW}N^ diff --git a/Resources/Textures/Objects/Weapons/Guns/Battery/laser_retro.rsi/mag-unshaded-2.png b/Resources/Textures/Objects/Weapons/Guns/Battery/laser_retro.rsi/mag-unshaded-2.png index 216d3e5cad85e3910ad41450ae44bc1c2cc98743..e6caa1505fdf0e7b35c425ba790e889adc6cb8f1 100644 GIT binary patch delta 144 zcmaDVxSDZ-ay8LHo p&q&(^uYkt!rABzB`T8aB^>EX>4U6ba`-PAZ2)IW&i+q+U-|clI$i7 z{bvi*oS*lhzc7ScJAaEJZZpBDbWLBu6w(OOz5 z5l3#hQAel7>$ut!^SPgc?nf9u$;0CTLBweJ8kT*}?~u#;3xAq#`FNHGowuHPD@yTc zY4(-9ziuOMMNF4G-Q&>i7kc^N&VA9m)xNp)GR_JxmOvjdlqY!^8nNFEk2H#U<$LIUCLzdz@fx!PODsz@Q}p;+Aq+ zsV7VOv@=bZm}!%uC7M9oWtEj0O^}f|`>`gZnBmO%9kW14vVR*>Irye=9(NP3SgK0I@jfT=lN`I2uD%q5{qjPe2^1Sz7Rrf~;ZIZH0cw~7$zL0Gbg6j7@Y1(RQt#>TibBC^u`5l5mgGU}R%Ft0q z8+{^tW}Y(3)LCbnebM5D5)f8dy6S4H@3aw0J8#)#>#n=)ZiqEw%O+M#ty;7GVC`o0 znYCBu{++dSvqpy~pQrDvL9F_wB6&Fx=M0QdPJdurodEJP0zX>=m(}?ozvz zW-+YHM>FHuOxi z&DS)fYadC*Kz)&gD%A{SN_dWm`ZX3#)PIHQ;P(LI7ZhVBaD2wG?WMn^k0mskLx+LC zR|YE8P36)(YOthrU}0CjrWGc@p*En}V4G|ing^UT7JL!W1JjMFq+DQf8Mud3uq-;$ zmBr#M1jif)xdP%Q02$vw34o`&U$-2OPB?i#d>nk}U!HZ8eh>v^CVdHt9S_tmfq%D# zC@I6-*ka5YP|p!8V(!?xGRtx6m$1 z(h}pkv<7XoRZxa?+TQSi*60i14#SgKnvjz|1SogB!AbVU9<};PGDS4Q`g8*Ff+7ok z{w17CH%AzNp74w(w?6MW5_0vze}Ck>LGPru=|1{QU*9VzzD!|>mL)9-5W%1zkO2sF z&tPo!>oONfG!xwJP73~60O!Qjr?IQX4vd1BReKaSk;j!vFvQglR)VP)S2WAaHVTW@&6? z004NLeUUv#!$2IxUt3E>TO7t+-n4Yi`@{mP zNDA>e@t8puB!1+&?D8AuvcrC!DHyrT9I-$wmb+N)VpcI!;wj>oqJL_X@6WreaNgpq z*6OT%PyWJiQCrS(o#rr-SVRhGh>%f76*X9h)2fkTB18KL4}aA0r^zLgs|rSrd2B$1 z!6%3MYWzi*0|50HIx=)v)dFW7}?>0D))VO6&MHn!wB_>5YySI|2r_ zfs5;oChq~4JHXJBE=(DcBL!&sOC{j_jJ~M=4Bi5Lt8Q=YeVjf3IqGWZ1~@nbM$44F z=JD>{?%w`A)9&vFMWu3;agt3R00006VoOIv0RI600RN!9r<0K(7=H(JNliruy48tEFm&-ZEFO7!%yq?XHNG2Wg2S1|tCt2?v4N`7K?xGoMx-#xO)|7zYbg}D3{9sBoYbxU0&CFy1s>; z-;|oImR($mqA(qg`#SNflI-Yfg~q`r>mdKI;Vst05v}_ZvX%Q delta 2248 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 Date: Tue, 1 Jul 2025 12:25:52 +0200 Subject: [PATCH 154/191] fix water coolers (#38681) --- Content.Shared/Storage/EntitySystems/BinSystem.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Storage/EntitySystems/BinSystem.cs b/Content.Shared/Storage/EntitySystems/BinSystem.cs index 3b57dd8fea..9009df3753 100644 --- a/Content.Shared/Storage/EntitySystems/BinSystem.cs +++ b/Content.Shared/Storage/EntitySystems/BinSystem.cs @@ -67,12 +67,18 @@ public sealed class BinSystem : EntitySystem private void OnEntInserted(Entity ent, ref EntInsertedIntoContainerMessage args) { + if (args.Container.ID != ent.Comp.ContainerId) + return; + ent.Comp.Items.Add(args.Entity); } - private void OnEntRemoved(EntityUid uid, BinComponent component, EntRemovedFromContainerMessage args) + private void OnEntRemoved(Entity ent, ref EntRemovedFromContainerMessage args) { - component.Items.Remove(args.Entity); + if (args.Container.ID != ent.Comp.ContainerId) + return; + + ent.Comp.Items.Remove(args.Entity); } private void OnInteractHand(EntityUid uid, BinComponent component, InteractHandEvent args) From bbf6f6b69c1cd85d3363a6089268899c10f05b47 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 1 Jul 2025 10:27:00 +0000 Subject: [PATCH 155/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 8c9af293ff..3d67cec87c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Jacktastic09 - changes: - - message: Added the Solid Headband, available via hacked ClothesMate vending machines - type: Add - id: 8215 - time: '2025-04-17T13:43:44.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36650 - author: Hoodie42 changes: - message: The banjo can now be worn on the back or suit storage slots. @@ -3888,3 +3881,10 @@ id: 8727 time: '2025-06-29T16:21:15.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/35536 +- author: slarticodefast + changes: + - message: Fixed water cups not being able to be picked up from water coolers. + type: Fix + id: 8728 + time: '2025-07-01T10:25:52.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38681 From 9a87d3fce25a860bddacd2f2bfec2674c999d716 Mon Sep 17 00:00:00 2001 From: imatsoup <93290208+imatsoup@users.noreply.github.com> Date: Tue, 1 Jul 2025 18:49:15 +0000 Subject: [PATCH 156/191] Monochromacy typo fix (#38686) * fixes the typo * Fixed cloning looking for the trait, not the component, RE https://github.com/space-wizards/space-station-14/pull/38686#issuecomment-3025093504 * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Resources/Locale/en-US/traits/traits.ftl | 4 ++-- Resources/Prototypes/Entities/Mobs/Player/clone.yml | 2 +- Resources/Prototypes/Traits/disabilities.yml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 44bfd9d923..d7ab6ca76a 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -15,8 +15,8 @@ permanent-blindness-trait-examined = [color=lightblue]{CAPITALIZE(POSS-ADJ($targ trait-lightweight-name = Lightweight drunk trait-lightweight-desc = Alcohol has a stronger effect on you. -trait-monochromancy-name = Monochromancy -trait-monochromancy-desc = You are fully colorblind, everything you perceive ranges from blacks to whites. +trait-monochromacy-name = Monochromacy +trait-monochromacy-desc = You are fully colorblind, everything you perceive ranges from blacks to whites. trait-muted-name = Muted trait-muted-desc = You can't speak. diff --git a/Resources/Prototypes/Entities/Mobs/Player/clone.yml b/Resources/Prototypes/Entities/Mobs/Player/clone.yml index 80db45bf00..9ca2bfaf6d 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml @@ -16,8 +16,8 @@ - NpcFactionMember # traits # - LegsParalyzed (you get healed) + - BlackAndWhiteOverlay - LightweightDrunk - - Monochromacy - Muted - Narcolepsy - Pacified diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml index b435379dbf..a5c1f54d6b 100644 --- a/Resources/Prototypes/Traits/disabilities.yml +++ b/Resources/Prototypes/Traits/disabilities.yml @@ -45,9 +45,9 @@ cloneable: true - type: trait - id: Monochromancy - name: trait-monochromancy-name - description: trait-monochromancy-desc + id: Monochromacy + name: trait-monochromacy-name + description: trait-monochromacy-desc category: Disabilities components: - type: BlackAndWhiteOverlay From 49370410ad839442957cecb41cb003cbc27b37e1 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Tue, 1 Jul 2025 20:31:39 -0400 Subject: [PATCH 157/191] Validate `CloningSettingsPrototype`s (#38688) * Validate CloningSettingsPrototypes * Update Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Check EventComponents too --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../Cloning/CloningSettingsPrototypeTest.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs diff --git a/Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs b/Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs new file mode 100644 index 0000000000..9edc115eee --- /dev/null +++ b/Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs @@ -0,0 +1,46 @@ +using Content.Shared.Cloning; + +namespace Content.IntegrationTests.Tests.Cloning; + +public sealed class CloningSettingsPrototypeTest +{ + /// + /// Checks that the components named in every are valid components known to the server. + /// This is used instead of because we only care if the components are registered with the server, + /// and instead of a because we only need component names. + /// + [Test] + public async Task ValidatePrototypes() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + var protoMan = server.ProtoMan; + var compFactory = server.EntMan.ComponentFactory; + + await server.WaitAssertion(() => + { + Assert.Multiple(() => + { + var protos = protoMan.EnumeratePrototypes(); + foreach (var proto in protos) + { + foreach (var compName in proto.Components) + { + Assert.That(compFactory.TryGetRegistration(compName, out _), + $"Failed to find a component named {compName} for {nameof(CloningSettingsPrototype)} \"{proto.ID}\"" + ); + } + + foreach (var eventCompName in proto.EventComponents) + { + Assert.That(compFactory.TryGetRegistration(eventCompName, out _), + $"Failed to find a component named {eventCompName} for {nameof(CloningSettingsPrototype)} \"{proto.ID}\"" + ); + } + } + }); + }); + + await pair.CleanReturnAsync(); + } +} From e63905ce5fda2041d724c6a39277c30870b24f77 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Tue, 1 Jul 2025 20:57:57 -0400 Subject: [PATCH 158/191] Add test of objective-related console commands (#36400) * Add test of objective add/list/remove commands * Not sure why we're validating test prototypes, but sure * We don't need a map --- .../Tests/Commands/ObjectiveCommandsTest.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs diff --git a/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs b/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs new file mode 100644 index 0000000000..a77761a7d1 --- /dev/null +++ b/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs @@ -0,0 +1,72 @@ +#nullable enable +using System.Linq; +using Content.Server.Objectives; +using Content.Shared.Mind; +using Robust.Shared.GameObjects; +using Robust.Shared.Player; + +namespace Content.IntegrationTests.Tests.Commands; + +public sealed class ObjectiveCommandsTest +{ + + private const string ObjectiveProtoId = "MindCommandsTestObjective"; + private const string DummyUsername = "MindCommandsTestUser"; + + [TestPrototypes] + private const string Prototypes = $""" +- type: entity + id: {ObjectiveProtoId} + components: + - type: Objective + difficulty: 1 + issuer: objective-issuer-syndicate + icon: + sprite: error.rsi + state: error + - type: DieCondition +"""; + + /// + /// Creates a dummy session, and assigns it a mind, then + /// tests using addobjective, lsobjectives, + /// and rmobjective on it. + /// + [Test] + public async Task AddListRemoveObjectiveTest() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + var entMan = server.EntMan; + var playerMan = server.ResolveDependency(); + var mindSys = server.System(); + var objectivesSystem = server.System(); + + await server.AddDummySession(DummyUsername); + await server.WaitRunTicks(5); + + var playerSession = playerMan.Sessions.Single(); + + Entity? mindEnt = null; + await server.WaitPost(() => + { + mindEnt = mindSys.CreateMind(playerSession.UserId); + }); + + Assert.That(mindEnt, Is.Not.Null); + var mindComp = mindEnt.Value.Comp; + Assert.That(mindComp.Objectives, Is.Empty, "Dummy player started with objectives."); + + await pair.WaitCommand($"addobjective {playerSession.Name} {ObjectiveProtoId}"); + + Assert.That(mindComp.Objectives, Has.Count.EqualTo(1), "addobjective failed to increase Objectives count."); + + await pair.WaitCommand($"lsobjectives {playerSession.Name}"); + + await pair.WaitCommand($"rmobjective {playerSession.Name} 0"); + + Assert.That(mindComp.Objectives, Is.Empty, "rmobjective failed to remove objective"); + + await pair.CleanReturnAsync(); + } +} From 32a019c6b6d98291a5c33af18570507c40156a45 Mon Sep 17 00:00:00 2001 From: UpAndLeaves <92269094+Alpha-Two@users.noreply.github.com> Date: Wed, 2 Jul 2025 04:14:37 +0100 Subject: [PATCH 159/191] CC Genpop and other misc fixes (#37494) * First commit: genpop and AI satellite * Revert AI satellite, add representative locker. --- Resources/Maps/centcomm.yml | 3796 ++++++++++++++++++++++------------- 1 file changed, 2440 insertions(+), 1356 deletions(-) diff --git a/Resources/Maps/centcomm.yml b/Resources/Maps/centcomm.yml index 6405c65923..c5dd3ac46b 100644 --- a/Resources/Maps/centcomm.yml +++ b/Resources/Maps/centcomm.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Grid - engineVersion: 250.0.0 + engineVersion: 259.0.0 forkId: "" forkVersion: "" - time: 04/24/2025 00:06:42 - entityCount: 9680 + time: 06/02/2025 19:48:10 + entityCount: 9753 maps: [] grids: - 1668 @@ -59,156 +59,156 @@ entities: chunks: -1,-1: ind: -1,-1 - tiles: eQAAAAAATQAAAAAATQAAAAAAeQAAAAAATQAAAAAATQAAAAAAeQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAAWQAAAAADWQAAAAADHQAAAAABHQAAAAAAHQAAAAABHQAAAAABHQAAAAACHQAAAAACHQAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAABHQAAAAAAHQAAAAACHQAAAAACWQAAAAACWQAAAAAAHQAAAAADHQAAAAABHQAAAAACHQAAAAABHQAAAAABHQAAAAADHQAAAAACHQAAAAAAHQAAAAABHQAAAAADHQAAAAABHQAAAAADHQAAAAACHQAAAAABWQAAAAAAWQAAAAABHQAAAAAAHQAAAAACHQAAAAADHQAAAAADHQAAAAABHQAAAAABHQAAAAADHQAAAAADHQAAAAADHQAAAAACHQAAAAAAHQAAAAAAHQAAAAABHQAAAAABWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABWQAAAAADWQAAAAAAHQAAAAADHQAAAAAAHQAAAAAAHQAAAAADHQAAAAADHQAAAAACHQAAAAAAeQAAAAAAWQAAAAABWQAAAAACWQAAAAABWQAAAAADeQAAAAAAHQAAAAACWQAAAAADWQAAAAACCwAAAAAACwAAAAAAHQAAAAADEQAAAAAAEQAAAAAAHQAAAAABHQAAAAABeQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAWQAAAAADHQAAAAABHQAAAAADWQAAAAABWQAAAAADHQAAAAACHQAAAAABHQAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAADeQAAAAAAWQAAAAAAWQAAAAABWQAAAAADWQAAAAADeQAAAAAAHQAAAAABWQAAAAAAWQAAAAADNgAAAAAANgAAAAAAHQAAAAADEQAAAAAAEQAAAAAAHQAAAAABHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAAHQAAAAACHQAAAAABHQAAAAACHQAAAAAAHQAAAAABHQAAAAACHQAAAAABHQAAAAAAeQAAAAAAWQAAAAADWQAAAAADWQAAAAAAWQAAAAABWQAAAAADWQAAAAACWQAAAAADWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAACWQAAAAADWQAAAAABWQAAAAABWQAAAAACWQAAAAABWQAAAAABAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAeQAAAAAAWQAAAAABWQAAAAADWQAAAAACWQAAAAABWQAAAAAAWQAAAAABWQAAAAADWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABWQAAAAAAWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABHQAAAAAAHQAAAAADHQAAAAACHQAAAAABHQAAAAABHQAAAAAAeQAAAAAAWQAAAAADWQAAAAADWQAAAAABeQAAAAAAHQAAAAABdgAAAAAAHQAAAAAAHQAAAAAAWQAAAAAAWQAAAAACWQAAAAABWQAAAAABWQAAAAADWQAAAAACWQAAAAABHQAAAAADWQAAAAACWQAAAAABWQAAAAABeQAAAAAAHQAAAAADdgAAAAABPgAAAAAAPgAAAAAAWQAAAAACWQAAAAAAWQAAAAABWQAAAAADWQAAAAABWQAAAAAAWQAAAAADHQAAAAADWQAAAAAAWQAAAAABWQAAAAADeQAAAAAAHQAAAAADdgAAAAAAPgAAAAAAPgAAAAAA - version: 6 + tiles: eQAAAAAAAE0AAAAAAABNAAAAAAAAeQAAAAAAAE0AAAAAAABNAAAAAAAAeQAAAAAAAE0AAAAAAABNAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAABZAAAAAAMAWQAAAAADAB0AAAAAAQAdAAAAAAAAHQAAAAABAB0AAAAAAQAdAAAAAAIAHQAAAAACAB0AAAAAAQAdAAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAEAHQAAAAAAAB0AAAAAAgAdAAAAAAIAWQAAAAACAFkAAAAAAAAdAAAAAAMAHQAAAAABAB0AAAAAAgAdAAAAAAEAHQAAAAABAB0AAAAAAwAdAAAAAAIAHQAAAAAAAB0AAAAAAQAdAAAAAAMAHQAAAAABAB0AAAAAAwAdAAAAAAIAHQAAAAABAFkAAAAAAABZAAAAAAEAHQAAAAAAAB0AAAAAAgAdAAAAAAMAHQAAAAADAB0AAAAAAQAdAAAAAAEAHQAAAAADAB0AAAAAAwAdAAAAAAMAHQAAAAACAB0AAAAAAAAdAAAAAAAAHQAAAAABAB0AAAAAAQBZAAAAAAAAWQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAADAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAEAWQAAAAADAFkAAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAAAdAAAAAAMAHQAAAAADAB0AAAAAAgAdAAAAAAAAeQAAAAAAAFkAAAAAAQBZAAAAAAIAWQAAAAABAFkAAAAAAwB5AAAAAAAAHQAAAAACAFkAAAAAAwBZAAAAAAIACwAAAAAAAAsAAAAAAAAdAAAAAAMAEQAAAAAAABEAAAAAAAAdAAAAAAEAHQAAAAABAHkAAAAAAABZAAAAAAAAWQAAAAAAAHkAAAAAAABZAAAAAAMAHQAAAAABAB0AAAAAAwBZAAAAAAEAWQAAAAADAB0AAAAAAgAdAAAAAAEAHQAAAAADAB0AAAAAAAAdAAAAAAAAHQAAAAAAAB0AAAAAAwB5AAAAAAAAWQAAAAAAAFkAAAAAAQBZAAAAAAMAWQAAAAADAHkAAAAAAAAdAAAAAAEAWQAAAAAAAFkAAAAAAwA2AAAAAAAANgAAAAAAAB0AAAAAAwARAAAAAAAAEQAAAAAAAB0AAAAAAQAdAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAAAdAAAAAAIAHQAAAAABAB0AAAAAAgAdAAAAAAAAHQAAAAABAB0AAAAAAgAdAAAAAAEAHQAAAAAAAHkAAAAAAABZAAAAAAMAWQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAADAFkAAAAAAgBZAAAAAAMAWQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAAAAFkAAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAQBZAAAAAAIAWQAAAAABAFkAAAAAAQABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAeQAAAAAAAFkAAAAAAQBZAAAAAAMAWQAAAAACAFkAAAAAAQBZAAAAAAAAWQAAAAABAFkAAAAAAwBZAAAAAAEAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABZAAAAAAEAWQAAAAAAAFkAAAAAAgB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAQAdAAAAAAAAHQAAAAADAB0AAAAAAgAdAAAAAAEAHQAAAAABAB0AAAAAAAB5AAAAAAAAWQAAAAADAFkAAAAAAwBZAAAAAAEAeQAAAAAAAB0AAAAAAQB2AAAAAAAAHQAAAAAAAB0AAAAAAABZAAAAAAAAWQAAAAACAFkAAAAAAQBZAAAAAAEAWQAAAAADAFkAAAAAAgBZAAAAAAEAHQAAAAADAFkAAAAAAgBZAAAAAAEAWQAAAAABAHkAAAAAAAAdAAAAAAMAdgAAAAABAD4AAAAAAAA+AAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAEAWQAAAAADAFkAAAAAAQBZAAAAAAAAWQAAAAADAB0AAAAAAwBZAAAAAAAAWQAAAAABAFkAAAAAAwB5AAAAAAAAHQAAAAADAHYAAAAAAAA+AAAAAAAAPgAAAAAAAA== + version: 7 0,-1: ind: 0,-1 - tiles: WQAAAAAAHQAAAAADAQAAAAAAHQAAAAACAgAAAAAAAgAAAAAAHQAAAAABHQAAAAABAgAAAAAAAgAAAAAAHQAAAAACeQAAAAAAHQAAAAABHQAAAAACHQAAAAABHQAAAAABWQAAAAADHQAAAAADAQAAAAAAHQAAAAADHQAAAAAAHQAAAAABHQAAAAABHQAAAAADHQAAAAADAgAAAAAAHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAADHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAHQAAAAADHQAAAAACHQAAAAABHQAAAAADWQAAAAAAHQAAAAAAeQAAAAAAHQAAAAABHQAAAAADWQAAAAAAWQAAAAABWQAAAAADWQAAAAADWQAAAAABWQAAAAACeQAAAAAAbAAAAAADbAAAAAAAWQAAAAABWQAAAAABWQAAAAAAHQAAAAADHQAAAAACHQAAAAABHQAAAAADbAAAAAABbAAAAAACbAAAAAADbAAAAAADbAAAAAACWQAAAAAAHQAAAAADbAAAAAADbAAAAAAAWQAAAAABWQAAAAABWQAAAAABHQAAAAABHQAAAAAAHQAAAAACHQAAAAACbAAAAAAAbAAAAAAAbAAAAAADbAAAAAAAbAAAAAACWQAAAAACHQAAAAABbAAAAAADbAAAAAADWQAAAAADWQAAAAACWQAAAAABHQAAAAACHQAAAAAAHQAAAAAAHQAAAAACbAAAAAACbAAAAAADbAAAAAAAbAAAAAAAbAAAAAACWQAAAAAAHQAAAAABbAAAAAAAbAAAAAAAWQAAAAACWQAAAAABWQAAAAACHQAAAAABeQAAAAAAHQAAAAADHQAAAAACWQAAAAADWQAAAAADWQAAAAAAWQAAAAAAWQAAAAACWQAAAAACeQAAAAAAbAAAAAAAbAAAAAAAWQAAAAAAWQAAAAABHQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAADeQAAAAAAeQAAAAAAbAAAAAADbAAAAAADWQAAAAACWQAAAAACWQAAAAABWQAAAAAAWQAAAAADWQAAAAAAWQAAAAACWQAAAAACWQAAAAABeQAAAAAAWQAAAAABbAAAAAABbAAAAAABbAAAAAABbAAAAAABbAAAAAAAWQAAAAADWQAAAAABWQAAAAAAWQAAAAABWQAAAAABWQAAAAADWQAAAAAAWQAAAAABWQAAAAAAeQAAAAAAWQAAAAAAbAAAAAACbAAAAAADbAAAAAAAbAAAAAABbAAAAAADbAAAAAACbAAAAAAAWQAAAAADWQAAAAADWQAAAAADWQAAAAABWQAAAAABWQAAAAACWQAAAAADeQAAAAAAWQAAAAADbAAAAAACbAAAAAADbAAAAAADbAAAAAAAbAAAAAACbAAAAAACbAAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAbAAAAAADbAAAAAABeQAAAAAAeQAAAAAAHQAAAAACeQAAAAAAHQAAAAACHQAAAAAAdgAAAAACHQAAAAAAeQAAAAAAWQAAAAADWQAAAAADWQAAAAABeQAAAAAAHQAAAAADWQAAAAACWQAAAAACWQAAAAABWQAAAAABWQAAAAADWQAAAAABWQAAAAABPgAAAAAAdgAAAAACHQAAAAAATQAAAAAAWQAAAAADWQAAAAACWQAAAAADHQAAAAACHQAAAAACWQAAAAABWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAABWQAAAAABWQAAAAACPgAAAAAAdgAAAAACHQAAAAABeQAAAAAAWQAAAAACWQAAAAABWQAAAAABHQAAAAAAHQAAAAACWQAAAAABWQAAAAACWQAAAAADWQAAAAAAWQAAAAAAWQAAAAABWQAAAAAD - version: 6 + tiles: WQAAAAAAAB0AAAAAAwABAAAAAAAAHQAAAAACAAIAAAAAAAACAAAAAAAAHQAAAAABAB0AAAAAAQACAAAAAAAAAgAAAAAAAB0AAAAAAgB5AAAAAAAAHQAAAAABAB0AAAAAAgAdAAAAAAEAHQAAAAABAFkAAAAAAwAdAAAAAAMAAQAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAABAB0AAAAAAQAdAAAAAAMAHQAAAAADAAIAAAAAAAAdAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABZAAAAAAMAHQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAEAeQAAAAAAAHkAAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAQAdAAAAAAMAWQAAAAAAAB0AAAAAAAB5AAAAAAAAHQAAAAABAB0AAAAAAwBZAAAAAAAAWQAAAAABAFkAAAAAAwBZAAAAAAMAWQAAAAABAFkAAAAAAgB5AAAAAAAAbAAAAAADAGwAAAAAAABZAAAAAAEAWQAAAAABAFkAAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAQAdAAAAAAMAbAAAAAABAGwAAAAAAgBsAAAAAAMAbAAAAAADAGwAAAAAAgBZAAAAAAAAHQAAAAADAGwAAAAAAwBsAAAAAAAAWQAAAAABAFkAAAAAAQBZAAAAAAEAHQAAAAABAB0AAAAAAAAdAAAAAAIAHQAAAAACAGwAAAAAAABsAAAAAAAAbAAAAAADAGwAAAAAAABsAAAAAAIAWQAAAAACAB0AAAAAAQBsAAAAAAMAbAAAAAADAFkAAAAAAwBZAAAAAAIAWQAAAAABAB0AAAAAAgAdAAAAAAAAHQAAAAAAAB0AAAAAAgBsAAAAAAIAbAAAAAADAGwAAAAAAABsAAAAAAAAbAAAAAACAFkAAAAAAAAdAAAAAAEAbAAAAAAAAGwAAAAAAABZAAAAAAIAWQAAAAABAFkAAAAAAgAdAAAAAAEAeQAAAAAAAB0AAAAAAwAdAAAAAAIAWQAAAAADAFkAAAAAAwBZAAAAAAAAWQAAAAAAAFkAAAAAAgBZAAAAAAIAeQAAAAAAAGwAAAAAAABsAAAAAAAAWQAAAAAAAFkAAAAAAQAdAAAAAAEAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAABZAAAAAAMAeQAAAAAAAHkAAAAAAABsAAAAAAMAbAAAAAADAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAABZAAAAAAMAWQAAAAAAAFkAAAAAAgBZAAAAAAIAWQAAAAABAHkAAAAAAABZAAAAAAEAbAAAAAABAGwAAAAAAQBsAAAAAAEAbAAAAAABAGwAAAAAAABZAAAAAAMAWQAAAAABAFkAAAAAAABZAAAAAAEAWQAAAAABAFkAAAAAAwBZAAAAAAAAWQAAAAABAFkAAAAAAAB5AAAAAAAAWQAAAAAAAGwAAAAAAgBsAAAAAAMAbAAAAAAAAGwAAAAAAQBsAAAAAAMAbAAAAAACAGwAAAAAAABZAAAAAAMAWQAAAAADAFkAAAAAAwBZAAAAAAEAWQAAAAABAFkAAAAAAgBZAAAAAAMAeQAAAAAAAFkAAAAAAwBsAAAAAAIAbAAAAAADAGwAAAAAAwBsAAAAAAAAbAAAAAACAGwAAAAAAgBsAAAAAAEAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAQBZAAAAAAAAWQAAAAAAAHkAAAAAAAB5AAAAAAAAbAAAAAADAGwAAAAAAQB5AAAAAAAAeQAAAAAAAB0AAAAAAgB5AAAAAAAAHQAAAAACAB0AAAAAAAB2AAAAAAIAHQAAAAAAAHkAAAAAAABZAAAAAAMAWQAAAAADAFkAAAAAAQB5AAAAAAAAHQAAAAADAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAQBZAAAAAAMAWQAAAAABAFkAAAAAAQA+AAAAAAAAdgAAAAACAB0AAAAAAABNAAAAAAAAWQAAAAADAFkAAAAAAgBZAAAAAAMAHQAAAAACAB0AAAAAAgBZAAAAAAEAWQAAAAAAAFkAAAAAAABZAAAAAAAAWQAAAAABAFkAAAAAAQBZAAAAAAIAPgAAAAAAAHYAAAAAAgAdAAAAAAEAeQAAAAAAAFkAAAAAAgBZAAAAAAEAWQAAAAABAB0AAAAAAAAdAAAAAAIAWQAAAAABAFkAAAAAAgBZAAAAAAMAWQAAAAAAAFkAAAAAAABZAAAAAAEAWQAAAAADAA== + version: 7 -1,0: ind: -1,0 - tiles: WQAAAAACWQAAAAABWQAAAAABWQAAAAAAWQAAAAACWQAAAAADWQAAAAADHQAAAAACWQAAAAACWQAAAAABWQAAAAAAHQAAAAACHQAAAAAAdgAAAAABdgAAAAAAdgAAAAACHQAAAAACHQAAAAAAHQAAAAADHQAAAAADHQAAAAAAHQAAAAAAHQAAAAACeQAAAAAAWQAAAAAAWQAAAAACWQAAAAADeQAAAAAAHQAAAAACHQAAAAAAHQAAAAADHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABWQAAAAAAWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAABWQAAAAADWQAAAAAAWQAAAAABWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAADWQAAAAABWQAAAAAAWQAAAAACWQAAAAABWQAAAAACWQAAAAACWQAAAAADPgAAAAAAeQAAAAAAHQAAAAACHQAAAAAAHQAAAAACHQAAAAABHQAAAAAAeQAAAAAAWQAAAAADWQAAAAACWQAAAAADWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAADWQAAAAADPgAAAAAAHQAAAAAAHQAAAAACHQAAAAAAHQAAAAACHQAAAAACHQAAAAADeQAAAAAAeQAAAAAAAQAAAAAAAQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABHQAAAAABPgAAAAAAeQAAAAAAHQAAAAACHQAAAAAAHQAAAAACHQAAAAAAHQAAAAAAeQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAACeQAAAAAAHQAAAAACWQAAAAABWQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAACHQAAAAABHQAAAAABHQAAAAADAQAAAAAAHQAAAAABWQAAAAADWQAAAAACHQAAAAABeQAAAAAAHQAAAAABHQAAAAADHQAAAAACeQAAAAAAHQAAAAABHQAAAAACHQAAAAACHQAAAAACHQAAAAACHQAAAAADAQAAAAAAHQAAAAABWQAAAAAAWQAAAAABPgAAAAAAeQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAHQAAAAAAHQAAAAAAHQAAAAADHQAAAAADHQAAAAADHQAAAAADeQAAAAAAHQAAAAADWQAAAAABWQAAAAAAPgAAAAAAeQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAHQAAAAABHQAAAAAAHQAAAAADHQAAAAADHQAAAAADHQAAAAACAQAAAAAAHQAAAAAAWQAAAAADWQAAAAABPgAAAAAAHQAAAAACPgAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAHQAAAAABHQAAAAADHQAAAAABHQAAAAAAHQAAAAABHQAAAAABAQAAAAAAHQAAAAAAWQAAAAADWQAAAAABHQAAAAAAeQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAeQAAAAAAHQAAAAADHQAAAAABeQAAAAAAeQAAAAAAWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAACHQAAAAADeQAAAAAAWQAAAAAAWQAAAAADWQAAAAABWQAAAAAAWQAAAAAAWQAAAAABWQAAAAABCgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAHQAAAAABHQAAAAAAeQAAAAAAWQAAAAADWQAAAAACWQAAAAADWQAAAAAAWQAAAAADWQAAAAACWQAAAAAA - version: 6 + tiles: WQAAAAACAFkAAAAAAQBZAAAAAAEAWQAAAAAAAFkAAAAAAgBZAAAAAAMAWQAAAAADAB0AAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAAAdAAAAAAIAHQAAAAAAAHYAAAAAAQB2AAAAAAAAdgAAAAACAB0AAAAAAgAdAAAAAAAAHQAAAAADAB0AAAAAAwAdAAAAAAAAHQAAAAAAAB0AAAAAAgB5AAAAAAAAWQAAAAAAAFkAAAAAAgBZAAAAAAMAeQAAAAAAAB0AAAAAAgAdAAAAAAAAHQAAAAADAB0AAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAQBZAAAAAAAAWQAAAAABAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAHkAAAAAAABZAAAAAAAAWQAAAAAAAFkAAAAAAABZAAAAAAEAWQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAACAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAADAFkAAAAAAQBZAAAAAAAAWQAAAAACAFkAAAAAAQBZAAAAAAIAWQAAAAACAFkAAAAAAwA+AAAAAAAAeQAAAAAAAB0AAAAAAgAdAAAAAAAAHQAAAAACAB0AAAAAAQAdAAAAAAAAeQAAAAAAAFkAAAAAAwBZAAAAAAIAWQAAAAADAFkAAAAAAABZAAAAAAAAWQAAAAAAAFkAAAAAAwBZAAAAAAMAPgAAAAAAAB0AAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAgAdAAAAAAIAHQAAAAADAHkAAAAAAAB5AAAAAAAAAQAAAAAAAAEAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAEAHQAAAAABAD4AAAAAAAB5AAAAAAAAHQAAAAACAB0AAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAAB5AAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAACAHkAAAAAAAAdAAAAAAIAWQAAAAABAFkAAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAgAdAAAAAAEAHQAAAAABAB0AAAAAAwABAAAAAAAAHQAAAAABAFkAAAAAAwBZAAAAAAIAHQAAAAABAHkAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAgB5AAAAAAAAHQAAAAABAB0AAAAAAgAdAAAAAAIAHQAAAAACAB0AAAAAAgAdAAAAAAMAAQAAAAAAAB0AAAAAAQBZAAAAAAAAWQAAAAABAD4AAAAAAAB5AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAeQAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAADAB0AAAAAAwAdAAAAAAMAHQAAAAADAHkAAAAAAAAdAAAAAAMAWQAAAAABAFkAAAAAAAA+AAAAAAAAeQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAHkAAAAAAAAdAAAAAAEAHQAAAAAAAB0AAAAAAwAdAAAAAAMAHQAAAAADAB0AAAAAAgABAAAAAAAAHQAAAAAAAFkAAAAAAwBZAAAAAAEAPgAAAAAAAB0AAAAAAgA+AAAAAAAAPgAAAAAAAD4AAAAAAAB5AAAAAAAAHQAAAAABAB0AAAAAAwAdAAAAAAEAHQAAAAAAAB0AAAAAAQAdAAAAAAEAAQAAAAAAAB0AAAAAAABZAAAAAAMAWQAAAAABAB0AAAAAAAB5AAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAEAeQAAAAAAAHkAAAAAAABZAAAAAAIAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAAAAFkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAIAHQAAAAADAHkAAAAAAABZAAAAAAAAWQAAAAADAFkAAAAAAQBZAAAAAAAAWQAAAAAAAFkAAAAAAQBZAAAAAAEACgAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAKAAAAAAAAHQAAAAABAB0AAAAAAAB5AAAAAAAAWQAAAAADAFkAAAAAAgBZAAAAAAMAWQAAAAAAAFkAAAAAAwBZAAAAAAIAWQAAAAAAAA== + version: 7 0,0: ind: 0,0 - tiles: dgAAAAABdgAAAAACHQAAAAADHQAAAAAAWQAAAAACWQAAAAADWQAAAAAAHQAAAAAAHQAAAAADWQAAAAAAWQAAAAAAWQAAAAADWQAAAAAAWQAAAAAAWQAAAAABWQAAAAADHQAAAAAAHQAAAAADHQAAAAADeQAAAAAAWQAAAAACWQAAAAABWQAAAAACeQAAAAAAHQAAAAAAWQAAAAADWQAAAAABWQAAAAABWQAAAAADWQAAAAAAWQAAAAACWQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAACWQAAAAACWQAAAAADeQAAAAAAeQAAAAAAHQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAHQAAAAADeQAAAAAAHQAAAAAAWQAAAAAAWQAAAAABWQAAAAAAWQAAAAACWQAAAAADWQAAAAABWQAAAAACeQAAAAAAHQAAAAADHQAAAAAAHQAAAAAAHQAAAAABHQAAAAAAHQAAAAABHQAAAAADHQAAAAABWQAAAAACWQAAAAACWQAAAAADWQAAAAAAWQAAAAACWQAAAAAAWQAAAAADeQAAAAAAHQAAAAABHQAAAAAAHQAAAAADHQAAAAABHQAAAAAAHQAAAAACHQAAAAACHQAAAAACWQAAAAADWQAAAAABWQAAAAADWQAAAAAAWQAAAAADWQAAAAADWQAAAAABeQAAAAAAHQAAAAAAHQAAAAAAHQAAAAABHQAAAAADHQAAAAACHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAAHQAAAAABeQAAAAAAHQAAAAACHQAAAAADHQAAAAAAHQAAAAABWQAAAAACHQAAAAADeQAAAAAAAwAAAAAAAwAAAAAJAwAAAAAAAwAAAAALAwAAAAAAHQAAAAADHQAAAAAAHQAAAAAAHQAAAAADHQAAAAACHQAAAAADHQAAAAADHQAAAAACWQAAAAABHQAAAAABeQAAAAAAAwAAAAAAAwAAAAAAAwAAAAADAwAAAAAAAwAAAAADHQAAAAAAHQAAAAABHQAAAAAAHQAAAAADHQAAAAAAHQAAAAACHQAAAAABHQAAAAABWQAAAAABHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAACHQAAAAADHQAAAAABHQAAAAABHQAAAAACHQAAAAAAWQAAAAACHQAAAAAAeQAAAAAAWQAAAAACWQAAAAABWQAAAAABWQAAAAACWQAAAAABWQAAAAACHQAAAAACHQAAAAAAHQAAAAADHQAAAAACHQAAAAACHQAAAAABHQAAAAACWQAAAAAAHQAAAAACeQAAAAAAWQAAAAABWQAAAAACWQAAAAACWQAAAAABWQAAAAADeQAAAAAAHQAAAAACHQAAAAABHQAAAAAAHQAAAAADHQAAAAACHQAAAAAAHQAAAAADWQAAAAADHQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAACWQAAAAADWQAAAAACHQAAAAACHQAAAAACHQAAAAAAHQAAAAACHQAAAAACHQAAAAADHQAAAAADHQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABeQAAAAAAHQAAAAABHQAAAAACHQAAAAACeQAAAAAAWQAAAAACWQAAAAADWQAAAAADWQAAAAABWQAAAAAAWQAAAAABWQAAAAABWQAAAAABWQAAAAABWQAAAAADWQAAAAACWQAAAAACWQAAAAADWQAAAAADWQAAAAAAWQAAAAACWQAAAAACWQAAAAADWQAAAAAAWQAAAAABWQAAAAACWQAAAAADWQAAAAAAWQAAAAABWQAAAAABWQAAAAADWQAAAAAAWQAAAAADWQAAAAAAWQAAAAADWQAAAAAAWQAAAAAC - version: 6 + tiles: dgAAAAABAHYAAAAAAgAdAAAAAAMAHQAAAAAAAFkAAAAAAgBZAAAAAAMAWQAAAAAAAB0AAAAAAAAdAAAAAAMAWQAAAAAAAFkAAAAAAABZAAAAAAMAWQAAAAAAAFkAAAAAAABZAAAAAAEAWQAAAAADAB0AAAAAAAAdAAAAAAMAHQAAAAADAHkAAAAAAABZAAAAAAIAWQAAAAABAFkAAAAAAgB5AAAAAAAAHQAAAAAAAFkAAAAAAwBZAAAAAAEAWQAAAAABAFkAAAAAAwBZAAAAAAAAWQAAAAACAFkAAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAACAFkAAAAAAgBZAAAAAAMAeQAAAAAAAHkAAAAAAAAdAAAAAAAAHQAAAAABAHkAAAAAAAB5AAAAAAAAHQAAAAADAHkAAAAAAAAdAAAAAAAAWQAAAAAAAFkAAAAAAQBZAAAAAAAAWQAAAAACAFkAAAAAAwBZAAAAAAEAWQAAAAACAHkAAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAAAdAAAAAAEAHQAAAAAAAB0AAAAAAQAdAAAAAAMAHQAAAAABAFkAAAAAAgBZAAAAAAIAWQAAAAADAFkAAAAAAABZAAAAAAIAWQAAAAAAAFkAAAAAAwB5AAAAAAAAHQAAAAABAB0AAAAAAAAdAAAAAAMAHQAAAAABAB0AAAAAAAAdAAAAAAIAHQAAAAACAB0AAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAwBZAAAAAAAAWQAAAAADAFkAAAAAAwBZAAAAAAEAeQAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAABAB0AAAAAAwAdAAAAAAIAHQAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAABAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAAAAB0AAAAAAQB5AAAAAAAAHQAAAAACAB0AAAAAAwAdAAAAAAAAHQAAAAABAFkAAAAAAgAdAAAAAAMAeQAAAAAAAFkAAAAAAABZAAAAAAAAWQAAAAAAAFkAAAAAAAB5AAAAAAAAHQAAAAADAB0AAAAAAAAdAAAAAAAAHQAAAAADAB0AAAAAAgAdAAAAAAMAHQAAAAADAB0AAAAAAgBZAAAAAAEAHQAAAAABAHkAAAAAAABZAAAAAAAAWQAAAAAAAFkAAAAAAABZAAAAAAAAWQAAAAAAAB0AAAAAAAAdAAAAAAEAHQAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAACAB0AAAAAAQAdAAAAAAEAWQAAAAABAB0AAAAAAwB5AAAAAAAAWQAAAAAAAFkAAAAAAABZAAAAAAAAWQAAAAAAAHkAAAAAAAAdAAAAAAAAHQAAAAADAB0AAAAAAgAdAAAAAAMAHQAAAAABAB0AAAAAAQAdAAAAAAIAHQAAAAAAAFkAAAAAAgAdAAAAAAAAeQAAAAAAAFkAAAAAAgBZAAAAAAEAWQAAAAABAFkAAAAAAgB5AAAAAAAAHQAAAAAAAB0AAAAAAgAdAAAAAAAAHQAAAAADAB0AAAAAAgAdAAAAAAIAHQAAAAABAB0AAAAAAgBZAAAAAAAAHQAAAAACAHkAAAAAAABZAAAAAAEAWQAAAAACAFkAAAAAAgBZAAAAAAEAHQAAAAAAAB0AAAAAAAAdAAAAAAIAHQAAAAABAB0AAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAAAdAAAAAAMAWQAAAAADAB0AAAAAAAB5AAAAAAAAWQAAAAAAAFkAAAAAAABZAAAAAAIAWQAAAAADAHkAAAAAAAAdAAAAAAAAHQAAAAACAB0AAAAAAAAdAAAAAAIAHQAAAAACAB0AAAAAAwAdAAAAAAMAHQAAAAAAAFkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAAAdAAAAAAEAeQAAAAAAAB0AAAAAAQAdAAAAAAIAHQAAAAACAHkAAAAAAABZAAAAAAIAWQAAAAADAFkAAAAAAwBZAAAAAAEAWQAAAAAAAFkAAAAAAQBZAAAAAAEAWQAAAAABAFkAAAAAAQBZAAAAAAMAWQAAAAACAFkAAAAAAgBZAAAAAAMAWQAAAAADAFkAAAAAAABZAAAAAAIAWQAAAAACAFkAAAAAAwBZAAAAAAAAWQAAAAABAFkAAAAAAgBZAAAAAAMAWQAAAAAAAFkAAAAAAQBZAAAAAAEAWQAAAAADAFkAAAAAAABZAAAAAAMAWQAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAACAA== + version: 7 1,-1: ind: 1,-1 - tiles: HQAAAAACHQAAAAAAeQAAAAAAHQAAAAAAHQAAAAADHQAAAAADHQAAAAABHQAAAAADHQAAAAACHQAAAAACHQAAAAADHQAAAAACHQAAAAACHQAAAAABHQAAAAABHQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABeQAAAAAAHQAAAAADHQAAAAABHQAAAAADHQAAAAADHQAAAAABHQAAAAAAHQAAAAACHQAAAAACeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAHQAAAAACHQAAAAACHQAAAAABWQAAAAAAWQAAAAACWQAAAAADbAAAAAACbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAACHQAAAAACHQAAAAABWQAAAAADWQAAAAABWQAAAAABbAAAAAACbAAAAAACbAAAAAADbAAAAAABbAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAABHQAAAAADHQAAAAADWQAAAAADWQAAAAADWQAAAAADbAAAAAADbAAAAAACbAAAAAABbAAAAAADbAAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAAAHQAAAAABHQAAAAABWQAAAAACWQAAAAADWQAAAAAAbAAAAAAAbAAAAAADbAAAAAAAbAAAAAABbAAAAAACeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAADHQAAAAAAHQAAAAACWQAAAAAAWQAAAAAAWQAAAAABHQAAAAACHQAAAAADHQAAAAADHQAAAAADHQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAHQAAAAADHQAAAAAAHQAAAAADWQAAAAAAWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABeQAAAAAAWQAAAAABWQAAAAADeQAAAAAAHQAAAAAAHQAAAAACHQAAAAABHQAAAAACHQAAAAABHQAAAAAAHQAAAAABHQAAAAAAHQAAAAACHQAAAAAAHQAAAAADHQAAAAADHQAAAAADbAAAAAACbAAAAAADHQAAAAACHQAAAAAAWQAAAAACWQAAAAAAWQAAAAACWQAAAAADWQAAAAADWQAAAAACWQAAAAABWQAAAAAAWQAAAAADWQAAAAABWQAAAAACWQAAAAABbAAAAAAAWQAAAAACeQAAAAAAHQAAAAADWQAAAAACWQAAAAADWQAAAAAAWQAAAAAAWQAAAAACWQAAAAADWQAAAAAAWQAAAAACWQAAAAAAWQAAAAADWQAAAAACWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADWQAAAAADWQAAAAADWQAAAAACWQAAAAADWQAAAAAAWQAAAAACWQAAAAACWQAAAAACWQAAAAABWQAAAAAAWQAAAAACWQAAAAABWQAAAAACHQAAAAACeQAAAAAAHQAAAAACWQAAAAADWQAAAAAAWQAAAAABWQAAAAACWQAAAAABWQAAAAACWQAAAAADWQAAAAABWQAAAAABWQAAAAACWQAAAAAAWQAAAAACWQAAAAADHQAAAAADHQAAAAACHQAAAAAAWQAAAAABWQAAAAADWQAAAAAAWQAAAAAAWQAAAAABWQAAAAABWQAAAAADWQAAAAADWQAAAAABWQAAAAABWQAAAAACWQAAAAABWQAAAAADHQAAAAAAHQAAAAAAHQAAAAADWQAAAAACWQAAAAACWQAAAAACWQAAAAABWQAAAAADWQAAAAADWQAAAAAAWQAAAAABWQAAAAABWQAAAAACWQAAAAADWQAAAAAC - version: 6 + tiles: HQAAAAACAB0AAAAAAAB5AAAAAAAAHQAAAAAAAB0AAAAAAwAdAAAAAAMAHQAAAAABAB0AAAAAAwAdAAAAAAIAHQAAAAACAB0AAAAAAwAdAAAAAAIAHQAAAAACAB0AAAAAAQAdAAAAAAEAHQAAAAACAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAABAHkAAAAAAAAdAAAAAAMAHQAAAAABAB0AAAAAAwAdAAAAAAMAHQAAAAABAB0AAAAAAAAdAAAAAAIAHQAAAAACAHkAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB5AAAAAAAAHQAAAAACAB0AAAAAAgAdAAAAAAEAWQAAAAAAAFkAAAAAAgBZAAAAAAMAbAAAAAACAGwAAAAAAABsAAAAAAAAbAAAAAAAAGwAAAAAAwB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAB0AAAAAAgAdAAAAAAIAHQAAAAABAFkAAAAAAwBZAAAAAAEAWQAAAAABAGwAAAAAAgBsAAAAAAIAbAAAAAADAGwAAAAAAQBsAAAAAAAAeQAAAAAAAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHkAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAwBZAAAAAAMAWQAAAAADAFkAAAAAAwBsAAAAAAMAbAAAAAACAGwAAAAAAQBsAAAAAAMAbAAAAAADAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAAAAB0AAAAAAQAdAAAAAAEAWQAAAAACAFkAAAAAAwBZAAAAAAAAbAAAAAAAAGwAAAAAAwBsAAAAAAAAbAAAAAABAGwAAAAAAgB5AAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAACAFkAAAAAAABZAAAAAAAAWQAAAAABAB0AAAAAAgAdAAAAAAMAHQAAAAADAB0AAAAAAwAdAAAAAAAAeQAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAAAAHkAAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAwBZAAAAAAAAWQAAAAABAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAQB5AAAAAAAAWQAAAAABAFkAAAAAAwB5AAAAAAAAHQAAAAAAAB0AAAAAAgAdAAAAAAEAHQAAAAACAB0AAAAAAQAdAAAAAAAAHQAAAAABAB0AAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAwAdAAAAAAMAHQAAAAADAGwAAAAAAgBsAAAAAAMAHQAAAAACAB0AAAAAAABZAAAAAAIAWQAAAAAAAFkAAAAAAgBZAAAAAAMAWQAAAAADAFkAAAAAAgBZAAAAAAEAWQAAAAAAAFkAAAAAAwBZAAAAAAEAWQAAAAACAFkAAAAAAQBsAAAAAAAAWQAAAAACAHkAAAAAAAAdAAAAAAMAWQAAAAACAFkAAAAAAwBZAAAAAAAAWQAAAAAAAFkAAAAAAgBZAAAAAAMAWQAAAAAAAFkAAAAAAgBZAAAAAAAAWQAAAAADAFkAAAAAAgBZAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAADAFkAAAAAAwBZAAAAAAMAWQAAAAACAFkAAAAAAwBZAAAAAAAAWQAAAAACAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAABZAAAAAAIAWQAAAAABAFkAAAAAAgAdAAAAAAIAeQAAAAAAAB0AAAAAAgBZAAAAAAMAWQAAAAAAAFkAAAAAAQBZAAAAAAIAWQAAAAABAFkAAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAQBZAAAAAAIAWQAAAAAAAFkAAAAAAgBZAAAAAAMAHQAAAAADAB0AAAAAAgAdAAAAAAAAWQAAAAABAFkAAAAAAwBZAAAAAAAAWQAAAAAAAFkAAAAAAQBZAAAAAAEAWQAAAAADAFkAAAAAAwBZAAAAAAEAWQAAAAABAFkAAAAAAgBZAAAAAAEAWQAAAAADAB0AAAAAAAAdAAAAAAAAHQAAAAADAFkAAAAAAgBZAAAAAAIAWQAAAAACAFkAAAAAAQBZAAAAAAMAWQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAABAFkAAAAAAgBZAAAAAAMAWQAAAAACAA== + version: 7 1,0: ind: 1,0 - tiles: WQAAAAACHQAAAAADHQAAAAABHQAAAAABWQAAAAAAWQAAAAACWQAAAAABWQAAAAADWQAAAAADWQAAAAACWQAAAAACWQAAAAACWQAAAAAAWQAAAAADWQAAAAAAWQAAAAABWQAAAAADHQAAAAACeQAAAAAAHQAAAAADWQAAAAACWQAAAAADWQAAAAAAWQAAAAAAWQAAAAADWQAAAAAAWQAAAAABWQAAAAACWQAAAAADWQAAAAACWQAAAAABWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAAWQAAAAACWQAAAAACWQAAAAADWQAAAAACWQAAAAABWQAAAAAAWQAAAAABWQAAAAACWQAAAAACWQAAAAAAWQAAAAADWQAAAAAAHQAAAAAAHQAAAAAAeQAAAAAAHQAAAAADWQAAAAADWQAAAAABWQAAAAABWQAAAAAAWQAAAAAAWQAAAAABWQAAAAABWQAAAAABWQAAAAADWQAAAAAAWQAAAAACWQAAAAAAHQAAAAADHQAAAAAAHQAAAAAAHQAAAAACWQAAAAAAWQAAAAACWQAAAAACWQAAAAACWQAAAAABWQAAAAACWQAAAAACWQAAAAAAWQAAAAADWQAAAAAAWQAAAAAAWQAAAAABHQAAAAAAHQAAAAACeQAAAAAAHQAAAAAAHQAAAAADHQAAAAAAHQAAAAABHQAAAAABHQAAAAABHQAAAAABHQAAAAAAHQAAAAAAHQAAAAADHQAAAAAAHQAAAAABHQAAAAACHQAAAAACHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAADeQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAeQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAeQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAHQAAAAAAHQAAAAAAeQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAeQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAeQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAHQAAAAADHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAACHQAAAAAAWQAAAAABWQAAAAADWQAAAAACWQAAAAAAWQAAAAAAWQAAAAADeQAAAAAAHQAAAAABPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAAAHQAAAAAAeQAAAAAAWQAAAAADWQAAAAACWQAAAAAAWQAAAAADWQAAAAABeQAAAAAAHQAAAAACdgAAAAACdgAAAAADdgAAAAACdgAAAAABdgAAAAABdgAAAAACHQAAAAADHQAAAAADHQAAAAABWQAAAAADWQAAAAAAWQAAAAAAWQAAAAADWQAAAAABeQAAAAAAHQAAAAAAdgAAAAAAdgAAAAAAdgAAAAADdgAAAAADdgAAAAACdgAAAAABHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAWQAAAAACWQAAAAACHQAAAAADHQAAAAAAHQAAAAACHQAAAAAAHQAAAAABHQAAAAABHQAAAAACHQAAAAABHQAAAAACHQAAAAACHQAAAAADHQAAAAACHQAAAAAAHQAAAAABWQAAAAACWQAAAAACHQAAAAADHQAAAAACHQAAAAACHQAAAAADPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAAD - version: 6 + tiles: WQAAAAACAB0AAAAAAwAdAAAAAAEAHQAAAAABAFkAAAAAAABZAAAAAAIAWQAAAAABAFkAAAAAAwBZAAAAAAMAWQAAAAACAFkAAAAAAgBZAAAAAAIAWQAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAABAFkAAAAAAwAdAAAAAAIAeQAAAAAAAB0AAAAAAwBZAAAAAAIAWQAAAAADAFkAAAAAAABZAAAAAAAAWQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAACAFkAAAAAAwBZAAAAAAIAWQAAAAABAFkAAAAAAQB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAAAWQAAAAACAFkAAAAAAgBZAAAAAAMAWQAAAAACAFkAAAAAAQBZAAAAAAAAWQAAAAABAFkAAAAAAgBZAAAAAAIAWQAAAAAAAFkAAAAAAwBZAAAAAAAAHQAAAAAAAB0AAAAAAAB5AAAAAAAAHQAAAAADAFkAAAAAAwBZAAAAAAEAWQAAAAABAFkAAAAAAABZAAAAAAAAWQAAAAABAFkAAAAAAQBZAAAAAAEAWQAAAAADAFkAAAAAAABZAAAAAAIAWQAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAAAAB0AAAAAAgBZAAAAAAAAWQAAAAACAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAgBZAAAAAAIAWQAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAAAAFkAAAAAAQAdAAAAAAAAHQAAAAACAHkAAAAAAAAdAAAAAAAAHQAAAAADAB0AAAAAAAAdAAAAAAEAHQAAAAABAB0AAAAAAQAdAAAAAAEAHQAAAAAAAB0AAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAQAdAAAAAAIAHQAAAAACAB0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAMAeQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAeQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAHkAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAdAAAAAAAAHQAAAAAAAHkAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAHkAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAB5AAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAHQAAAAADAB0AAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAgAdAAAAAAAAWQAAAAABAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAHkAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAAdAAAAAAAAHQAAAAAAAFkAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAB5AAAAAAAAdgAAAAACAHYAAAAAAwB2AAAAAAIAdgAAAAABAHYAAAAAAQB2AAAAAAIAHQAAAAADAB0AAAAAAwBZAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAeQAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAADAHYAAAAAAwB2AAAAAAIAdgAAAAABAB0AAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAEAeQAAAAAAAHkAAAAAAABZAAAAAAIAWQAAAAACAB0AAAAAAwAdAAAAAAAAHQAAAAACAB0AAAAAAAAdAAAAAAEAHQAAAAABAB0AAAAAAgAdAAAAAAEAHQAAAAACAB0AAAAAAgAdAAAAAAMAHQAAAAACAB0AAAAAAAAdAAAAAAEAWQAAAAACAFkAAAAAAgAdAAAAAAMAHQAAAAACAB0AAAAAAgAdAAAAAAMAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAHQAAAAADAA== + version: 7 2,0: ind: 2,0 - tiles: WQAAAAADeQAAAAAATQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAHQAAAAADTQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAABHQAAAAACTQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAHQAAAAABTQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAADHQAAAAAATQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAAAHQAAAAACTQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgAAAAAAPgAAAAAAHQAAAAACeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAADdgAAAAAAHQAAAAACeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAADdgAAAAAAHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAACHQAAAAADHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAACHQAAAAABHQAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: WQAAAAADAHkAAAAAAABNAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFkAAAAAAAAdAAAAAAMATQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZAAAAAAEAHQAAAAACAE0AAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAAB0AAAAAAQBNAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFkAAAAAAwAdAAAAAAAATQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdAAAAAAAAHQAAAAACAE0AAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAA+AAAAAAAAHQAAAAACAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2AAAAAAMAdgAAAAAAAB0AAAAAAgB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAADAHYAAAAAAAAdAAAAAAEAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdAAAAAAIAHQAAAAADAB0AAAAAAQB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAACAB0AAAAAAQAdAAAAAAMAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 2,-1: ind: 2,-1 - tiles: HQAAAAABHQAAAAACHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAACHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABHQAAAAADHQAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAAAHQAAAAACHQAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADHQAAAAADHQAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAACHQAAAAACHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAAAHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAACHQAAAAADTQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAHQAAAAACTQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAABHQAAAAAATQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAHQAAAAACTQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAHQAAAAACTQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAACeQAAAAAATQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAACeQAAAAAATQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: HQAAAAABAB0AAAAAAgAdAAAAAAEAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAdAAAAAAEAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAHQAAAAACAB0AAAAAAQB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABAB0AAAAAAwAdAAAAAAMAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0AAAAAAAAdAAAAAAIAHQAAAAADAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdAAAAAAMAHQAAAAADAB0AAAAAAwB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAACAB0AAAAAAgAdAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAdAAAAAAAAHQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAACAB0AAAAAAwBNAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFkAAAAAAAAdAAAAAAIATQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZAAAAAAEAHQAAAAAAAE0AAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAAB0AAAAAAgBNAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFkAAAAAAAAdAAAAAAIATQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZAAAAAAIAeQAAAAAAAE0AAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAACAHkAAAAAAABNAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 1,-2: ind: 1,-2 - tiles: bAAAAAABbAAAAAABeQAAAAAAHQAAAAAAHQAAAAABHQAAAAABHQAAAAACeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbAAAAAABbAAAAAADeQAAAAAAHQAAAAABHQAAAAABHQAAAAABHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbAAAAAACbAAAAAABeQAAAAAAHQAAAAABHQAAAAADHQAAAAABHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAHQAAAAABHQAAAAABHQAAAAAAHQAAAAADHQAAAAAAHQAAAAADHQAAAAAAHQAAAAACHQAAAAACHQAAAAACHQAAAAAAHQAAAAADHQAAAAACHQAAAAACHQAAAAACWQAAAAACHQAAAAADHQAAAAADHQAAAAABHQAAAAADHQAAAAABHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAADBgAAAAAABgAAAAABBgAAAAAAHQAAAAADHQAAAAADWQAAAAADHQAAAAADHQAAAAADHQAAAAADHQAAAAAAHQAAAAAAHQAAAAADHQAAAAABHQAAAAAABgAAAAAABgAAAAAABgAAAAABBgAAAAABBgAAAAAABgAAAAADHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAABHQAAAAADHQAAAAADHQAAAAABBgAAAAABBgAAAAACBgAAAAAAHQAAAAAAHQAAAAACHQAAAAADHQAAAAAAeQAAAAAABAAAAAAABAAAAAAAeQAAAAAAHQAAAAABHQAAAAADHQAAAAACHQAAAAAABgAAAAABBgAAAAAAHQAAAAABHQAAAAADHQAAAAABHQAAAAADHQAAAAADHQAAAAACeQAAAAAABAAAAAAABAAAAAAAeQAAAAAAdgAAAAADdgAAAAACHQAAAAABBgAAAAAABgAAAAACHQAAAAAAHQAAAAABHQAAAAAAHQAAAAACHQAAAAAAHQAAAAABHQAAAAAAeQAAAAAABAAAAAAABAAAAAAAeQAAAAAAdgAAAAAAdgAAAAADHQAAAAABBgAAAAACBgAAAAABHQAAAAADHQAAAAABHQAAAAABHQAAAAACHQAAAAADHQAAAAAAHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAdgAAAAADdgAAAAADHQAAAAAABgAAAAAABgAAAAAAHQAAAAABHQAAAAACHQAAAAADHQAAAAACHQAAAAADHQAAAAABHQAAAAAAeQAAAAAAHQAAAAAAHQAAAAAAHQAAAAADHQAAAAAAHQAAAAADHQAAAAABHQAAAAADBgAAAAADBgAAAAACHQAAAAACHQAAAAAAHQAAAAADHQAAAAACHQAAAAABHQAAAAADHQAAAAABBQAAAAACBQAAAAAAHQAAAAABHQAAAAABHQAAAAABHQAAAAABHQAAAAADHQAAAAAABgAAAAAABgAAAAADBgAAAAAAHQAAAAACHQAAAAADHQAAAAADHQAAAAABeQAAAAAABQAAAAACBQAAAAACHQAAAAACHQAAAAABHQAAAAADHQAAAAACHQAAAAAAHQAAAAADHQAAAAABBgAAAAADBgAAAAABBgAAAAADBgAAAAABBgAAAAADBgAAAAABHQAAAAADBQAAAAACBQAAAAABeQAAAAAAHQAAAAADHQAAAAAAHQAAAAADHQAAAAACHQAAAAACHQAAAAAAHQAAAAACHQAAAAAABgAAAAACBgAAAAABBgAAAAACHQAAAAAAHQAAAAAC - version: 6 + tiles: bAAAAAABAGwAAAAAAQB5AAAAAAAAHQAAAAAAAB0AAAAAAQAdAAAAAAEAHQAAAAACAB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGwAAAAAAQBsAAAAAAMAeQAAAAAAAB0AAAAAAQAdAAAAAAEAHQAAAAABAB0AAAAAAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsAAAAAAIAbAAAAAABAHkAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAEAHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAAAdAAAAAAEAHQAAAAABAB0AAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAACAB0AAAAAAgAdAAAAAAIAHQAAAAAAAB0AAAAAAwAdAAAAAAIAHQAAAAACAB0AAAAAAgBZAAAAAAIAHQAAAAADAB0AAAAAAwAdAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAADAAYAAAAAAAAGAAAAAAEABgAAAAAAAB0AAAAAAwAdAAAAAAMAWQAAAAADAB0AAAAAAwAdAAAAAAMAHQAAAAADAB0AAAAAAAAdAAAAAAAAHQAAAAADAB0AAAAAAQAdAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAEABgAAAAABAAYAAAAAAAAGAAAAAAMAHQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAEAHQAAAAADAB0AAAAAAwAdAAAAAAEABgAAAAABAAYAAAAAAgAGAAAAAAAAHQAAAAAAAB0AAAAAAgAdAAAAAAMAHQAAAAAAAHkAAAAAAAAEAAAAAAAABAAAAAAAAHkAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAgAdAAAAAAAABgAAAAABAAYAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAMAHQAAAAADAB0AAAAAAgB5AAAAAAAABAAAAAAAAAQAAAAAAAB5AAAAAAAAdgAAAAADAHYAAAAAAgAdAAAAAAEABgAAAAAAAAYAAAAAAgAdAAAAAAAAHQAAAAABAB0AAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAQAdAAAAAAAAeQAAAAAAAAQAAAAAAAAEAAAAAAAAeQAAAAAAAHYAAAAAAAB2AAAAAAMAHQAAAAABAAYAAAAAAgAGAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAEAHQAAAAACAB0AAAAAAwAdAAAAAAAAHQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB2AAAAAAMAdgAAAAADAB0AAAAAAAAGAAAAAAAABgAAAAAAAB0AAAAAAQAdAAAAAAIAHQAAAAADAB0AAAAAAgAdAAAAAAMAHQAAAAABAB0AAAAAAAB5AAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAwAdAAAAAAEAHQAAAAADAAYAAAAAAwAGAAAAAAIAHQAAAAACAB0AAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAQAdAAAAAAMAHQAAAAABAAUAAAAAAgAFAAAAAAAAHQAAAAABAB0AAAAAAQAdAAAAAAEAHQAAAAABAB0AAAAAAwAdAAAAAAAABgAAAAAAAAYAAAAAAwAGAAAAAAAAHQAAAAACAB0AAAAAAwAdAAAAAAMAHQAAAAABAHkAAAAAAAAFAAAAAAIABQAAAAACAB0AAAAAAgAdAAAAAAEAHQAAAAADAB0AAAAAAgAdAAAAAAAAHQAAAAADAB0AAAAAAQAGAAAAAAMABgAAAAABAAYAAAAAAwAGAAAAAAEABgAAAAADAAYAAAAAAQAdAAAAAAMABQAAAAACAAUAAAAAAQB5AAAAAAAAHQAAAAADAB0AAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAgAdAAAAAAAAHQAAAAACAB0AAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAgAdAAAAAAAAHQAAAAACAA== + version: 7 0,-2: ind: 0,-2 - tiles: HQAAAAABeQAAAAAAHQAAAAAAeQAAAAAAAQAAAAAAeQAAAAAAdgAAAAACdgAAAAABdgAAAAACdgAAAAABHQAAAAAAeQAAAAAAWQAAAAACWQAAAAAAeQAAAAAAWQAAAAACHQAAAAAAeQAAAAAAHQAAAAACeQAAAAAAAQAAAAAAeQAAAAAAdgAAAAADdgAAAAACdgAAAAAAdgAAAAACHQAAAAAAHQAAAAADWQAAAAADWQAAAAABeQAAAAAAWQAAAAADHQAAAAACeQAAAAAAHQAAAAABeQAAAAAAAQAAAAAAeQAAAAAAHQAAAAACHQAAAAAAHQAAAAABHQAAAAABHQAAAAABeQAAAAAAWQAAAAACWQAAAAACeQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAACWQAAAAABeQAAAAAAeQAAAAAAWQAAAAABWQAAAAAAWQAAAAADWQAAAAACWQAAAAABWQAAAAADWQAAAAABWQAAAAACWQAAAAABWQAAAAADWQAAAAABWQAAAAAAWQAAAAACWQAAAAAAWQAAAAAAWQAAAAADWQAAAAABWQAAAAACWQAAAAAAWQAAAAACWQAAAAABWQAAAAABWQAAAAABWQAAAAACWQAAAAADWQAAAAAAWQAAAAACWQAAAAACWQAAAAACWQAAAAABWQAAAAAAWQAAAAACWQAAAAACWQAAAAACWQAAAAABWQAAAAADWQAAAAABWQAAAAACWQAAAAACWQAAAAADWQAAAAAAWQAAAAABWQAAAAAAWQAAAAADWQAAAAAAWQAAAAADWQAAAAAAWQAAAAACWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABWQAAAAABHQAAAAADHQAAAAADAgAAAAAAAgAAAAAAHQAAAAAAHQAAAAAAHQAAAAABHQAAAAAAHQAAAAADHQAAAAACeQAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAAWQAAAAADHQAAAAABAQAAAAAAHQAAAAACAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAHQAAAAADeQAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAAWQAAAAABHQAAAAADAQAAAAAAHQAAAAAAAgAAAAAAHQAAAAAAHQAAAAAAHQAAAAABHQAAAAAAAgAAAAAAHQAAAAABeQAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAAWQAAAAACHQAAAAABAQAAAAAAHQAAAAADAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAHQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAADeQAAAAAAWQAAAAAAHQAAAAADHQAAAAADAgAAAAAAAgAAAAAAAgAAAAAAHQAAAAACHQAAAAADAgAAAAAAAgAAAAAAAgAAAAAAHQAAAAADBQAAAAACBQAAAAACBQAAAAADHQAAAAABWQAAAAAAHQAAAAADHQAAAAACAgAAAAAAAgAAAAAAAgAAAAAAHQAAAAAAHQAAAAADAgAAAAAAAgAAAAAAAgAAAAAAHQAAAAABBQAAAAAABQAAAAAABQAAAAADBQAAAAACWQAAAAABHQAAAAABHQAAAAADAgAAAAAAAgAAAAAAAgAAAAAAHQAAAAACHQAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAHQAAAAACBQAAAAABBQAAAAADBQAAAAADBQAAAAADWQAAAAADHQAAAAADAQAAAAAAHQAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAHQAAAAABeQAAAAAABQAAAAABBQAAAAACBQAAAAACBQAAAAAA - version: 6 + tiles: HQAAAAABAHkAAAAAAAAdAAAAAAAAeQAAAAAAAAEAAAAAAAB5AAAAAAAAdgAAAAACAHYAAAAAAQB2AAAAAAIAdgAAAAABAB0AAAAAAAB5AAAAAAAAWQAAAAACAFkAAAAAAAB5AAAAAAAAWQAAAAACAB0AAAAAAAB5AAAAAAAAHQAAAAACAHkAAAAAAAABAAAAAAAAeQAAAAAAAHYAAAAAAwB2AAAAAAIAdgAAAAAAAHYAAAAAAgAdAAAAAAAAHQAAAAADAFkAAAAAAwBZAAAAAAEAeQAAAAAAAFkAAAAAAwAdAAAAAAIAeQAAAAAAAB0AAAAAAQB5AAAAAAAAAQAAAAAAAHkAAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAQAdAAAAAAEAHQAAAAABAHkAAAAAAABZAAAAAAIAWQAAAAACAHkAAAAAAABZAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAACAFkAAAAAAQB5AAAAAAAAeQAAAAAAAFkAAAAAAQBZAAAAAAAAWQAAAAADAFkAAAAAAgBZAAAAAAEAWQAAAAADAFkAAAAAAQBZAAAAAAIAWQAAAAABAFkAAAAAAwBZAAAAAAEAWQAAAAAAAFkAAAAAAgBZAAAAAAAAWQAAAAAAAFkAAAAAAwBZAAAAAAEAWQAAAAACAFkAAAAAAABZAAAAAAIAWQAAAAABAFkAAAAAAQBZAAAAAAEAWQAAAAACAFkAAAAAAwBZAAAAAAAAWQAAAAACAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAABZAAAAAAIAWQAAAAACAFkAAAAAAgBZAAAAAAEAWQAAAAADAFkAAAAAAQBZAAAAAAIAWQAAAAACAFkAAAAAAwBZAAAAAAAAWQAAAAABAFkAAAAAAABZAAAAAAMAWQAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAACAFkAAAAAAgB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAQBZAAAAAAEAHQAAAAADAB0AAAAAAwACAAAAAAAAAgAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAABAB0AAAAAAAAdAAAAAAMAHQAAAAACAHkAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAWQAAAAADAB0AAAAAAQABAAAAAAAAHQAAAAACAAIAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAB0AAAAAAwB5AAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAFkAAAAAAQAdAAAAAAMAAQAAAAAAAB0AAAAAAAACAAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAEAHQAAAAAAAAIAAAAAAAAdAAAAAAEAeQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAABZAAAAAAIAHQAAAAABAAEAAAAAAAAdAAAAAAMAAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAHQAAAAABAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAwB5AAAAAAAAWQAAAAAAAB0AAAAAAwAdAAAAAAMAAgAAAAAAAAIAAAAAAAACAAAAAAAAHQAAAAACAB0AAAAAAwACAAAAAAAAAgAAAAAAAAIAAAAAAAAdAAAAAAMABQAAAAACAAUAAAAAAgAFAAAAAAMAHQAAAAABAFkAAAAAAAAdAAAAAAMAHQAAAAACAAIAAAAAAAACAAAAAAAAAgAAAAAAAB0AAAAAAAAdAAAAAAMAAgAAAAAAAAIAAAAAAAACAAAAAAAAHQAAAAABAAUAAAAAAAAFAAAAAAAABQAAAAADAAUAAAAAAgBZAAAAAAEAHQAAAAABAB0AAAAAAwACAAAAAAAAAgAAAAAAAAIAAAAAAAAdAAAAAAIAHQAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAB0AAAAAAgAFAAAAAAEABQAAAAADAAUAAAAAAwAFAAAAAAMAWQAAAAADAB0AAAAAAwABAAAAAAAAHQAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAB0AAAAAAQB5AAAAAAAABQAAAAABAAUAAAAAAgAFAAAAAAIABQAAAAAAAA== + version: 7 -1,-2: ind: -1,-2 - tiles: eQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAHQAAAAACDAAAAAABDAAAAAADDAAAAAACDAAAAAABeQAAAAAAAQAAAAAAeQAAAAAAHQAAAAABeQAAAAAAHQAAAAABHQAAAAADeQAAAAAAWQAAAAADWQAAAAABHQAAAAADHQAAAAABDAAAAAAADAAAAAAADAAAAAADDAAAAAABeQAAAAAAAQAAAAAAeQAAAAAAHQAAAAACeQAAAAAAHQAAAAABHQAAAAADeQAAAAAAWQAAAAACWQAAAAABeQAAAAAAHQAAAAACHQAAAAABHQAAAAADHQAAAAACHQAAAAAAeQAAAAAAAQAAAAAAeQAAAAAAHQAAAAAAeQAAAAAAHQAAAAACHQAAAAABeQAAAAAAWQAAAAACWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABWQAAAAABWQAAAAAAWQAAAAACWQAAAAAAWQAAAAACWQAAAAADWQAAAAABWQAAAAAAWQAAAAAAWQAAAAABWQAAAAABWQAAAAABWQAAAAABWQAAAAACWQAAAAAAWQAAAAAAWQAAAAACWQAAAAABWQAAAAABWQAAAAADWQAAAAACWQAAAAAAWQAAAAADWQAAAAACWQAAAAABWQAAAAAAWQAAAAAAWQAAAAABWQAAAAAAWQAAAAACWQAAAAAAWQAAAAADWQAAAAABWQAAAAAAWQAAAAAAWQAAAAABWQAAAAADWQAAAAACWQAAAAACWQAAAAADWQAAAAACWQAAAAABWQAAAAABWQAAAAABWQAAAAACWQAAAAACWQAAAAADWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAABWQAAAAADWQAAAAABAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAHQAAAAACWQAAAAACWQAAAAADAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAHQAAAAACWQAAAAAAWQAAAAABAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAHQAAAAADWQAAAAACWQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAADWQAAAAAAWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADWQAAAAABWQAAAAAAeQAAAAAAPAAAAAAAPAAAAAAAeQAAAAAANgAAAAAANgAAAAAAeQAAAAAAEQAAAAAAEQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAHQAAAAACWQAAAAADWQAAAAABeQAAAAAAPAAAAAAAPAAAAAAAeQAAAAAANgAAAAAANgAAAAAAeQAAAAAAEQAAAAAAEQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAHQAAAAAAWQAAAAADWQAAAAAC - version: 6 + tiles: eQAAAAAAAFkAAAAAAABZAAAAAAAAeQAAAAAAAB0AAAAAAgAMAAAAAAEADAAAAAADAAwAAAAAAgAMAAAAAAEAeQAAAAAAAAEAAAAAAAB5AAAAAAAAHQAAAAABAHkAAAAAAAAdAAAAAAEAHQAAAAADAHkAAAAAAABZAAAAAAMAWQAAAAABAB0AAAAAAwAdAAAAAAEADAAAAAAAAAwAAAAAAAAMAAAAAAMADAAAAAABAHkAAAAAAAABAAAAAAAAeQAAAAAAAB0AAAAAAgB5AAAAAAAAHQAAAAABAB0AAAAAAwB5AAAAAAAAWQAAAAACAFkAAAAAAQB5AAAAAAAAHQAAAAACAB0AAAAAAQAdAAAAAAMAHQAAAAACAB0AAAAAAAB5AAAAAAAAAQAAAAAAAHkAAAAAAAAdAAAAAAAAeQAAAAAAAB0AAAAAAgAdAAAAAAEAeQAAAAAAAFkAAAAAAgBZAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAABAFkAAAAAAQBZAAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAIAWQAAAAADAFkAAAAAAQBZAAAAAAAAWQAAAAAAAFkAAAAAAQBZAAAAAAEAWQAAAAABAFkAAAAAAQBZAAAAAAIAWQAAAAAAAFkAAAAAAABZAAAAAAIAWQAAAAABAFkAAAAAAQBZAAAAAAMAWQAAAAACAFkAAAAAAABZAAAAAAMAWQAAAAACAFkAAAAAAQBZAAAAAAAAWQAAAAAAAFkAAAAAAQBZAAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAMAWQAAAAABAFkAAAAAAABZAAAAAAAAWQAAAAABAFkAAAAAAwBZAAAAAAIAWQAAAAACAFkAAAAAAwBZAAAAAAIAWQAAAAABAFkAAAAAAQBZAAAAAAEAWQAAAAACAFkAAAAAAgBZAAAAAAMAWQAAAAACAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAAAAFkAAAAAAwB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAHQAAAAABAFkAAAAAAwBZAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAB0AAAAAAgBZAAAAAAIAWQAAAAADAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAdAAAAAAIAWQAAAAAAAFkAAAAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAHQAAAAADAFkAAAAAAgBZAAAAAAEAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAB0AAAAAAwBZAAAAAAAAWQAAAAABAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAMAWQAAAAABAFkAAAAAAAB5AAAAAAAAPAAAAAAAADwAAAAAAAB5AAAAAAAANgAAAAAAADYAAAAAAAB5AAAAAAAAEQAAAAAAABEAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAKAAAAAAAAHQAAAAACAFkAAAAAAwBZAAAAAAEAeQAAAAAAADwAAAAAAAA8AAAAAAAAeQAAAAAAADYAAAAAAAA2AAAAAAAAeQAAAAAAABEAAAAAAAARAAAAAAAAeQAAAAAAAHkAAAAAAABoAAAAAAAAeQAAAAAAAB0AAAAAAABZAAAAAAMAWQAAAAACAA== + version: 7 2,-2: ind: 2,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABHQAAAAADHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABHQAAAAABHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABHQAAAAADHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAABCAAAAAAACAAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAAAdgAAAAADdgAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAACdgAAAAADdgAAAAACeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAADdgAAAAACdgAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAADCAAAAAABCAAAAAACeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADHQAAAAACHQAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABHQAAAAACHQAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0AAAAAAQAdAAAAAAMAHQAAAAABAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdAAAAAAEAHQAAAAABAB0AAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABAB0AAAAAAwAdAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAdAAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAEACAAAAAAAAAgAAAAAAwB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAAAAHYAAAAAAwB2AAAAAAMAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHYAAAAAAgB2AAAAAAMAdgAAAAACAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2AAAAAAMAdgAAAAACAHYAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAADAAgAAAAAAQAIAAAAAAIAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAwB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABAB0AAAAAAgAdAAAAAAMAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 1,1: ind: 1,1 - tiles: WQAAAAABWQAAAAAAHQAAAAADHQAAAAACHQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAACeQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAADeQAAAAAAHQAAAAACHQAAAAABeQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAACeQAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAdgAAAAACdgAAAAAAdgAAAAABdgAAAAABdgAAAAAAdgAAAAAAdgAAAAACdgAAAAADdgAAAAABdgAAAAABdgAAAAADHQAAAAAAeQAAAAAAPgAAAAAAPgAAAAAAHQAAAAAAdgAAAAABdgAAAAABPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAdgAAAAAAdgAAAAACeQAAAAAAeQAAAAAAPgAAAAAAPgAAAAAAHQAAAAABdgAAAAABdgAAAAACPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAdgAAAAAAdgAAAAAATQAAAAAAeQAAAAAAPgAAAAAAPgAAAAAAHQAAAAADdgAAAAAAdgAAAAADdgAAAAADdgAAAAADdgAAAAACdgAAAAACdgAAAAABdgAAAAACdgAAAAADdgAAAAABdgAAAAADTQAAAAAAeQAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAdgAAAAABeQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAdgAAAAABTQAAAAAAeQAAAAAAHQAAAAAAHQAAAAAAeQAAAAAAHQAAAAADHQAAAAADPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAABHQAAAAAATQAAAAAAeQAAAAAAHQAAAAABHQAAAAABHQAAAAACHQAAAAABHQAAAAAAHQAAAAAAHQAAAAACHQAAAAAAHQAAAAADHQAAAAABHQAAAAADHQAAAAAAHQAAAAADHQAAAAADTQAAAAAAeQAAAAAAHQAAAAADHQAAAAACeQAAAAAAHQAAAAADHQAAAAACHQAAAAAAHQAAAAABHQAAAAACHQAAAAAAHQAAAAADHQAAAAABHQAAAAAAHQAAAAABHQAAAAACTQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAeQAAAAAAHQAAAAACHQAAAAABHQAAAAACHQAAAAADHQAAAAABHQAAAAAAHQAAAAADHQAAAAABHQAAAAADHQAAAAAAHQAAAAABTQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAAeQAAAAAAeQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: WQAAAAABAFkAAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAgB5AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAAdAAAAAAMAeQAAAAAAAB0AAAAAAgAdAAAAAAEAeQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAHQAAAAACAHkAAAAAAAA+AAAAAAAAPgAAAAAAAHkAAAAAAAB2AAAAAAIAdgAAAAAAAHYAAAAAAQB2AAAAAAEAdgAAAAAAAHYAAAAAAAB2AAAAAAIAdgAAAAADAHYAAAAAAQB2AAAAAAEAdgAAAAADAB0AAAAAAAB5AAAAAAAAPgAAAAAAAD4AAAAAAAAdAAAAAAAAdgAAAAABAHYAAAAAAQA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAdgAAAAAAAHYAAAAAAgB5AAAAAAAAeQAAAAAAAD4AAAAAAAA+AAAAAAAAHQAAAAABAHYAAAAAAQB2AAAAAAIAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAHYAAAAAAAB2AAAAAAAATQAAAAAAAHkAAAAAAAA+AAAAAAAAPgAAAAAAAB0AAAAAAwB2AAAAAAAAdgAAAAADAHYAAAAAAwB2AAAAAAMAdgAAAAACAHYAAAAAAgB2AAAAAAEAdgAAAAACAHYAAAAAAwB2AAAAAAEAdgAAAAADAE0AAAAAAAB5AAAAAAAAPgAAAAAAAD4AAAAAAAB5AAAAAAAAdgAAAAABAHkAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAeQAAAAAAAHYAAAAAAQBNAAAAAAAAeQAAAAAAAB0AAAAAAAAdAAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAMAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAB0AAAAAAQAdAAAAAAAATQAAAAAAAHkAAAAAAAAdAAAAAAEAHQAAAAABAB0AAAAAAgAdAAAAAAEAHQAAAAAAAB0AAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAwAdAAAAAAEAHQAAAAADAB0AAAAAAAAdAAAAAAMAHQAAAAADAE0AAAAAAAB5AAAAAAAAHQAAAAADAB0AAAAAAgB5AAAAAAAAHQAAAAADAB0AAAAAAgAdAAAAAAAAHQAAAAABAB0AAAAAAgAdAAAAAAAAHQAAAAADAB0AAAAAAQAdAAAAAAAAHQAAAAABAB0AAAAAAgBNAAAAAAAAeQAAAAAAAHkAAAAAAAAKAAAAAAAAeQAAAAAAAB0AAAAAAgAdAAAAAAEAHQAAAAACAB0AAAAAAwAdAAAAAAEAHQAAAAAAAB0AAAAAAwAdAAAAAAEAHQAAAAADAB0AAAAAAAAdAAAAAAEATQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAE0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAAAHkAAAAAAAB5AAAAAAAATQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 2,1: ind: 2,1 - tiles: HQAAAAABHQAAAAACHQAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAHQAAAAACeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAABHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABPgAAAAAAPgAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADPgAAAAAAPgAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAAAHQAAAAABHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAACHQAAAAADHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAACHQAAAAADHQAAAAACeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADHQAAAAACHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: HQAAAAABAB0AAAAAAgAdAAAAAAMAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAACAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAHQAAAAABAB0AAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAD4AAAAAAAA+AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0AAAAAAQA+AAAAAAAAPgAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdAAAAAAAAPgAAAAAAAD4AAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADAD4AAAAAAAA+AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAA+AAAAAAAAPgAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdAAAAAAAAHQAAAAABAB0AAAAAAQB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAACAB0AAAAAAwAdAAAAAAEAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0AAAAAAgAdAAAAAAMAHQAAAAACAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 0,1: ind: 0,1 - tiles: WQAAAAADWQAAAAADWQAAAAADWQAAAAADWQAAAAADWQAAAAAAWQAAAAADWQAAAAACWQAAAAADWQAAAAADWQAAAAABWQAAAAAAWQAAAAABWQAAAAACWQAAAAADWQAAAAABWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAACeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAADWQAAAAABWQAAAAADWQAAAAADeQAAAAAAWQAAAAADWQAAAAAAWQAAAAADeQAAAAAAHQAAAAAAHQAAAAACHQAAAAACHQAAAAAAHQAAAAADHQAAAAADWQAAAAACWQAAAAAAWQAAAAABWQAAAAADWQAAAAACWQAAAAADWQAAAAABWQAAAAAAWQAAAAADHQAAAAABHQAAAAADHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAADHQAAAAAAWQAAAAABWQAAAAAAWQAAAAADWQAAAAADWQAAAAAAWQAAAAAAWQAAAAABWQAAAAABWQAAAAACeQAAAAAAHQAAAAACHQAAAAAAHQAAAAACHQAAAAAAHQAAAAAAHQAAAAADHQAAAAABHQAAAAACHQAAAAABHQAAAAADHQAAAAADeQAAAAAAWQAAAAACWQAAAAAAWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAHQAAAAAAeQAAAAAAHQAAAAACHQAAAAADeQAAAAAAeQAAAAAAWQAAAAADWQAAAAADWQAAAAACeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAHQAAAAACHQAAAAADHQAAAAADHQAAAAABHQAAAAADeQAAAAAAeQAAAAAAWQAAAAADWQAAAAACeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAWQAAAAAAWQAAAAABWQAAAAACWQAAAAABWQAAAAACWQAAAAAAWQAAAAADWQAAAAACWQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAWQAAAAADWQAAAAAAWQAAAAAAWQAAAAABWQAAAAACWQAAAAABWQAAAAACWQAAAAADWQAAAAADeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAWQAAAAABWQAAAAAAWQAAAAADWQAAAAADWQAAAAAAWQAAAAADWQAAAAACWQAAAAAAWQAAAAABeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAWQAAAAADWQAAAAAAWQAAAAABWQAAAAAAWQAAAAADWQAAAAADWQAAAAABWQAAAAADWQAAAAADeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAWQAAAAAAWQAAAAABWQAAAAACWQAAAAABWQAAAAAAWQAAAAABWQAAAAAAWQAAAAADeQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAWQAAAAADWQAAAAACWQAAAAADWQAAAAABWQAAAAAAWQAAAAACWQAAAAAAWQAAAAABeQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAAHQAAAAACTQAAAAAATQAAAAAAWQAAAAABWQAAAAAAWQAAAAADWQAAAAADWQAAAAAAWQAAAAACWQAAAAAAWQAAAAACeQAAAAAAeQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAAWQAAAAACWQAAAAAAWQAAAAADWQAAAAAAWQAAAAADWQAAAAAAWQAAAAABWQAAAAAAeQAAAAAAeQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAA - version: 6 + tiles: WQAAAAADAFkAAAAAAwBZAAAAAAMAWQAAAAADAFkAAAAAAwBZAAAAAAAAWQAAAAADAFkAAAAAAgBZAAAAAAMAWQAAAAADAFkAAAAAAQBZAAAAAAAAWQAAAAABAFkAAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAIAeQAAAAAAAHkAAAAAAABZAAAAAAAAWQAAAAADAFkAAAAAAQBZAAAAAAMAWQAAAAADAHkAAAAAAABZAAAAAAMAWQAAAAAAAFkAAAAAAwB5AAAAAAAAHQAAAAAAAB0AAAAAAgAdAAAAAAIAHQAAAAAAAB0AAAAAAwAdAAAAAAMAWQAAAAACAFkAAAAAAABZAAAAAAEAWQAAAAADAFkAAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAABZAAAAAAMAHQAAAAABAB0AAAAAAwAdAAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAMAHQAAAAAAAFkAAAAAAQBZAAAAAAAAWQAAAAADAFkAAAAAAwBZAAAAAAAAWQAAAAAAAFkAAAAAAQBZAAAAAAEAWQAAAAACAHkAAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAgAdAAAAAAAAHQAAAAAAAB0AAAAAAwAdAAAAAAEAHQAAAAACAB0AAAAAAQAdAAAAAAMAHQAAAAADAHkAAAAAAABZAAAAAAIAWQAAAAAAAFkAAAAAAQB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAABAHkAAAAAAAB5AAAAAAAAHQAAAAAAAHkAAAAAAAAdAAAAAAIAHQAAAAADAHkAAAAAAAB5AAAAAAAAWQAAAAADAFkAAAAAAwBZAAAAAAIAeQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAB0AAAAAAgAdAAAAAAMAHQAAAAADAB0AAAAAAQAdAAAAAAMAeQAAAAAAAHkAAAAAAABZAAAAAAMAWQAAAAACAHkAAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABZAAAAAAAAWQAAAAABAFkAAAAAAgBZAAAAAAEAWQAAAAACAFkAAAAAAABZAAAAAAMAWQAAAAACAFkAAAAAAAB5AAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAAWQAAAAADAFkAAAAAAABZAAAAAAAAWQAAAAABAFkAAAAAAgBZAAAAAAEAWQAAAAACAFkAAAAAAwBZAAAAAAMAeQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAFkAAAAAAQBZAAAAAAAAWQAAAAADAFkAAAAAAwBZAAAAAAAAWQAAAAADAFkAAAAAAgBZAAAAAAAAWQAAAAABAHkAAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABZAAAAAAMAWQAAAAAAAFkAAAAAAQBZAAAAAAAAWQAAAAADAFkAAAAAAwBZAAAAAAEAWQAAAAADAFkAAAAAAwB5AAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAAWQAAAAAAAFkAAAAAAQBZAAAAAAIAWQAAAAABAFkAAAAAAABZAAAAAAEAWQAAAAAAAFkAAAAAAwB5AAAAAAAAeQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAFkAAAAAAwBZAAAAAAIAWQAAAAADAFkAAAAAAQBZAAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAEAeQAAAAAAAHkAAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAAAdAAAAAAIATQAAAAAAAE0AAAAAAABZAAAAAAEAWQAAAAAAAFkAAAAAAwBZAAAAAAMAWQAAAAAAAFkAAAAAAgBZAAAAAAAAWQAAAAACAHkAAAAAAAB5AAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAMAWQAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAABAFkAAAAAAAB5AAAAAAAAeQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAA== + version: 7 -1,1: ind: -1,1 - tiles: CgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAHQAAAAAAHQAAAAADeQAAAAAAWQAAAAACWQAAAAAAWQAAAAADWQAAAAABWQAAAAABWQAAAAAAWQAAAAADCgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAHQAAAAAAHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAADWQAAAAADCgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAHQAAAAADHQAAAAAAHQAAAAADHQAAAAADHQAAAAABHQAAAAABDQAAAAABeQAAAAAAWQAAAAABWQAAAAACCgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAHQAAAAABHQAAAAABHQAAAAACDQAAAAACDQAAAAAADQAAAAACDQAAAAACAQAAAAAAWQAAAAADWQAAAAABCgAAAAAACgAAAAAACgAAAAAACgAAAAAACgAAAAAACgAAAAAAHQAAAAABHQAAAAADHQAAAAAADQAAAAABDQAAAAABDQAAAAAADQAAAAABeQAAAAAAWQAAAAADWQAAAAADHQAAAAABHQAAAAACHQAAAAACHQAAAAABHQAAAAADHQAAAAABHQAAAAABHQAAAAADHQAAAAACDQAAAAABDQAAAAADDQAAAAACDQAAAAAAAQAAAAAAWQAAAAACHQAAAAAAHQAAAAABHQAAAAABHQAAAAAAHQAAAAADHQAAAAAAHQAAAAADHQAAAAABHQAAAAAAHQAAAAADHQAAAAADHQAAAAAAHQAAAAACDQAAAAADeQAAAAAAWQAAAAADeQAAAAAADQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAADHQAAAAADDQAAAAACeQAAAAAAHQAAAAAAHQAAAAABHQAAAAADHQAAAAADHQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAWQAAAAABWQAAAAACDQAAAAACeQAAAAAAHQAAAAACHQAAAAABHQAAAAADHQAAAAADHQAAAAACeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAWQAAAAABWQAAAAAADQAAAAACeQAAAAAAHQAAAAAAHQAAAAACHQAAAAAAHQAAAAACHQAAAAADeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAWQAAAAADWQAAAAACDQAAAAAAeQAAAAAAHQAAAAACHQAAAAACHQAAAAABHQAAAAADHQAAAAACeQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAWQAAAAABWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAACWQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAWQAAAAACWQAAAAADWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAWQAAAAADWQAAAAACWQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADWQAAAAAAWQAAAAABWQAAAAAC - version: 6 + tiles: CgAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAKAAAAAAAAHQAAAAAAAB0AAAAAAwB5AAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAMAWQAAAAABAFkAAAAAAQBZAAAAAAAAWQAAAAADAAoAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAACgAAAAAAAB0AAAAAAAAdAAAAAAMAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAADAFkAAAAAAwAKAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAoAAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAwAdAAAAAAMAHQAAAAABAB0AAAAAAQANAAAAAAEAeQAAAAAAAFkAAAAAAQBZAAAAAAIACgAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAKAAAAAAAAHQAAAAABAB0AAAAAAQAdAAAAAAIADQAAAAACAA0AAAAAAAANAAAAAAIADQAAAAACAAEAAAAAAABZAAAAAAMAWQAAAAABAAoAAAAAAAAKAAAAAAAACgAAAAAAAAoAAAAAAAAKAAAAAAAACgAAAAAAAB0AAAAAAQAdAAAAAAMAHQAAAAAAAA0AAAAAAQANAAAAAAEADQAAAAAAAA0AAAAAAQB5AAAAAAAAWQAAAAADAFkAAAAAAwAdAAAAAAEAHQAAAAACAB0AAAAAAgAdAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAEAHQAAAAADAB0AAAAAAgANAAAAAAEADQAAAAADAA0AAAAAAgANAAAAAAAAAQAAAAAAAFkAAAAAAgAdAAAAAAAAHQAAAAABAB0AAAAAAQAdAAAAAAAAHQAAAAADAB0AAAAAAAAdAAAAAAMAHQAAAAABAB0AAAAAAAAdAAAAAAMAHQAAAAADAB0AAAAAAAAdAAAAAAIADQAAAAADAHkAAAAAAABZAAAAAAMAeQAAAAAAAA0AAAAAAgB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAIAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAADAB0AAAAAAwANAAAAAAIAeQAAAAAAAB0AAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAwAdAAAAAAAAeQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAAeQAAAAAAAFkAAAAAAQBZAAAAAAIADQAAAAACAHkAAAAAAAAdAAAAAAIAHQAAAAABAB0AAAAAAwAdAAAAAAMAHQAAAAACAHkAAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAHkAAAAAAABZAAAAAAEAWQAAAAAAAA0AAAAAAgB5AAAAAAAAHQAAAAAAAB0AAAAAAgAdAAAAAAAAHQAAAAACAB0AAAAAAwB5AAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAAB5AAAAAAAAWQAAAAADAFkAAAAAAgANAAAAAAAAeQAAAAAAAB0AAAAAAgAdAAAAAAIAHQAAAAABAB0AAAAAAwAdAAAAAAIAeQAAAAAAAE0AAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAAeQAAAAAAAFkAAAAAAQBZAAAAAAEAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABZAAAAAAIAWQAAAAAAAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHkAAAAAAABZAAAAAAIAWQAAAAADAFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAWQAAAAADAFkAAAAAAgBZAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAACAA== + version: 7 1,2: ind: 1,2 - tiles: CQAAAAAAeQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAAeQAAAAAAeQAAAAAATQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: CQAAAAAAAHkAAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAAAAAAAB5AAAAAAAAeQAAAAAAAE0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 0,2: ind: 0,2 - tiles: HQAAAAAAHQAAAAADHQAAAAACHQAAAAACHQAAAAACHQAAAAADHQAAAAABHQAAAAACeQAAAAAAeQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAAeQAAAAAACgAAAAAACgAAAAAAeQAAAAAACgAAAAAACgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAACQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAeQAAAAAACgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: HQAAAAAAAB0AAAAAAwAdAAAAAAIAHQAAAAACAB0AAAAAAgAdAAAAAAMAHQAAAAABAB0AAAAAAgB5AAAAAAAAeQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAHkAAAAAAAAKAAAAAAAACgAAAAAAAHkAAAAAAAAKAAAAAAAACgAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAACgAAAAAAAHkAAAAAAAAKAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 -2,0: ind: -2,0 - tiles: WQAAAAAAWQAAAAABWQAAAAABWQAAAAACWQAAAAABWQAAAAADWQAAAAACWQAAAAAAWQAAAAABWQAAAAADWQAAAAABWQAAAAACWQAAAAACWQAAAAAAWQAAAAABWQAAAAABHQAAAAAAHQAAAAAAHQAAAAACHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAACHQAAAAADHQAAAAABHQAAAAABHQAAAAABHQAAAAABHQAAAAABHQAAAAABHQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAdgAAAAAAHQAAAAAAeQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAeQAAAAAAEAAAAAAAdgAAAAAAdgAAAAADdgAAAAABdgAAAAADHQAAAAAAHQAAAAAAHQAAAAAAdgAAAAAAHQAAAAAAeQAAAAAADwAAAAACDwAAAAABDwAAAAADDwAAAAACeQAAAAAAHQAAAAACdgAAAAAAdgAAAAABdgAAAAACdgAAAAADPgAAAAAAPgAAAAAAeQAAAAAAdgAAAAAAHQAAAAAAHQAAAAADAwAAAAABAwAAAAAAAwAAAAAAAwAAAAAAHQAAAAADHQAAAAABdgAAAAAAdgAAAAADdgAAAAADdgAAAAADPgAAAAAAPgAAAAAAPgAAAAAAdgAAAAAAHQAAAAAAHQAAAAACAwAAAAAAAwAAAAAAAwAAAAACAwAAAAAAHQAAAAACHQAAAAADdgAAAAAAdgAAAAADdgAAAAACdgAAAAACPgAAAAAAPgAAAAAAPgAAAAAAdgAAAAAAHQAAAAAAeQAAAAAADwAAAAACDwAAAAADDwAAAAABDwAAAAADeQAAAAAAHQAAAAADdgAAAAAAdgAAAAABdgAAAAADdgAAAAABPgAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABdgAAAAAAHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAABHQAAAAADHQAAAAAAHQAAAAABHQAAAAABHQAAAAADHQAAAAADHQAAAAAAdgAAAAAAHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAAAdgAAAAAAdgAAAAADdgAAAAAAdgAAAAADdgAAAAACPgAAAAAAPgAAAAAAdgAAAAAAHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAACdgAAAAABPgAAAAAAPgAAAAAAPgAAAAAAdgAAAAADPgAAAAAAPgAAAAAAdgAAAAAAHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAADdgAAAAADPgAAAAAAPgAAAAAAPgAAAAAAdgAAAAABPgAAAAAAPgAAAAAAdgAAAAAAHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAADHQAAAAACHQAAAAABHQAAAAAAHQAAAAADHQAAAAABHQAAAAADHQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAbAAAAAABbAAAAAADeQAAAAAA - version: 6 + tiles: WQAAAAAAAFkAAAAAAQBZAAAAAAEAWQAAAAACAFkAAAAAAQBZAAAAAAMAWQAAAAACAFkAAAAAAABZAAAAAAEAWQAAAAADAFkAAAAAAQBZAAAAAAIAWQAAAAACAFkAAAAAAABZAAAAAAEAWQAAAAABAB0AAAAAAAAdAAAAAAAAHQAAAAACAB0AAAAAAAAdAAAAAAAAHQAAAAAAAB0AAAAAAgAdAAAAAAMAHQAAAAABAB0AAAAAAQAdAAAAAAEAHQAAAAABAB0AAAAAAQAdAAAAAAEAHQAAAAAAAB0AAAAAAQB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAgB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAdgAAAAAAAB0AAAAAAAB5AAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAHkAAAAAAAAQAAAAAAAAdgAAAAAAAHYAAAAAAwB2AAAAAAEAdgAAAAADAB0AAAAAAAAdAAAAAAAAHQAAAAAAAHYAAAAAAAAdAAAAAAAAeQAAAAAAAA8AAAAAAgAPAAAAAAEADwAAAAADAA8AAAAAAgB5AAAAAAAAHQAAAAACAHYAAAAAAAB2AAAAAAEAdgAAAAACAHYAAAAAAwA+AAAAAAAAPgAAAAAAAHkAAAAAAAB2AAAAAAAAHQAAAAAAAB0AAAAAAwADAAAAAAEAAwAAAAAAAAMAAAAAAAADAAAAAAAAHQAAAAADAB0AAAAAAQB2AAAAAAAAdgAAAAADAHYAAAAAAwB2AAAAAAMAPgAAAAAAAD4AAAAAAAA+AAAAAAAAdgAAAAAAAB0AAAAAAAAdAAAAAAIAAwAAAAAAAAMAAAAAAAADAAAAAAIAAwAAAAAAAB0AAAAAAgAdAAAAAAMAdgAAAAAAAHYAAAAAAwB2AAAAAAIAdgAAAAACAD4AAAAAAAA+AAAAAAAAPgAAAAAAAHYAAAAAAAAdAAAAAAAAeQAAAAAAAA8AAAAAAgAPAAAAAAMADwAAAAABAA8AAAAAAwB5AAAAAAAAHQAAAAADAHYAAAAAAAB2AAAAAAEAdgAAAAADAHYAAAAAAQA+AAAAAAAAPgAAAAAAAD4AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAABAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAEAdgAAAAAAAB0AAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAAAdAAAAAAEAHQAAAAABAB0AAAAAAwAdAAAAAAMAHQAAAAAAAHYAAAAAAAAdAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAHQAAAAAAAHYAAAAAAAB2AAAAAAMAdgAAAAAAAHYAAAAAAwB2AAAAAAIAPgAAAAAAAD4AAAAAAAB2AAAAAAAAHQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAB0AAAAAAgB2AAAAAAEAPgAAAAAAAD4AAAAAAAA+AAAAAAAAdgAAAAADAD4AAAAAAAA+AAAAAAAAdgAAAAAAAB0AAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAdAAAAAAMAdgAAAAADAD4AAAAAAAA+AAAAAAAAPgAAAAAAAHYAAAAAAQA+AAAAAAAAPgAAAAAAAHYAAAAAAAAdAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAHQAAAAADAB0AAAAAAgAdAAAAAAEAHQAAAAAAAB0AAAAAAwAdAAAAAAEAHQAAAAADAB0AAAAAAgB5AAAAAAAAeQAAAAAAAHkAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB4AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAADAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAGwAAAAAAQBsAAAAAAMAeQAAAAAAAA== + version: 7 -2,-1: ind: -2,-1 - tiles: TQAAAAAAeQAAAAAATQAAAAAATQAAAAAAeQAAAAAATQAAAAAATQAAAAAAeQAAAAAATQAAAAAATQAAAAAAeQAAAAAATQAAAAAATQAAAAAAeQAAAAAATQAAAAAATQAAAAAAHQAAAAACHQAAAAAAHQAAAAACHQAAAAACHQAAAAADHQAAAAABHQAAAAABHQAAAAAAHQAAAAADHQAAAAACHQAAAAADHQAAAAABHQAAAAADHQAAAAADHQAAAAAAHQAAAAABHQAAAAACHQAAAAABHQAAAAADHQAAAAACHQAAAAACHQAAAAACHQAAAAAAHQAAAAACHQAAAAACHQAAAAAAHQAAAAABHQAAAAADHQAAAAAAHQAAAAADHQAAAAADHQAAAAACHQAAAAAAHQAAAAACHQAAAAABHQAAAAACHQAAAAACHQAAAAACHQAAAAACHQAAAAAAHQAAAAAAHQAAAAACHQAAAAACHQAAAAAAHQAAAAADHQAAAAAAHQAAAAADHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAADwAAAAACDwAAAAADDwAAAAADDwAAAAAADwAAAAAAdgAAAAACDwAAAAABDwAAAAAADwAAAAACDwAAAAABeQAAAAAAHQAAAAABHQAAAAAAHQAAAAAAHQAAAAAAeQAAAAAADwAAAAACdgAAAAADdgAAAAABdgAAAAADdgAAAAAAdgAAAAABdgAAAAADdgAAAAABdgAAAAAADwAAAAACeQAAAAAAHQAAAAAAHQAAAAABHQAAAAABHQAAAAACeQAAAAAADwAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAdgAAAAABeQAAAAAAeQAAAAAAeQAAAAAADwAAAAABeQAAAAAAHQAAAAAAHQAAAAADHQAAAAABHQAAAAAAeQAAAAAADwAAAAABeQAAAAAAdgAAAAABdgAAAAADdgAAAAADdgAAAAAAdgAAAAAAdgAAAAACeQAAAAAADwAAAAABeQAAAAAAHQAAAAABHQAAAAACHQAAAAABHQAAAAADeQAAAAAADwAAAAAAeQAAAAAAdgAAAAACdgAAAAACdgAAAAABdgAAAAADdgAAAAAAdgAAAAAAeQAAAAAADwAAAAADeQAAAAAAHQAAAAACHQAAAAADHQAAAAABHQAAAAADeQAAAAAADwAAAAAAeQAAAAAAdgAAAAACdgAAAAADHQAAAAACHQAAAAADHQAAAAABdgAAAAACeQAAAAAADwAAAAAAeQAAAAAAHQAAAAAAHQAAAAACHQAAAAABHQAAAAADeQAAAAAADwAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAADwAAAAAAeQAAAAAAHQAAAAACHQAAAAAAHQAAAAADHQAAAAAAeQAAAAAADwAAAAABDwAAAAABAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAGAwAAAAAIDwAAAAAADwAAAAABDwAAAAABHQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAAAHQAAAAACHQAAAAACHQAAAAABHQAAAAADHQAAAAACHQAAAAADHQAAAAADHQAAAAACHQAAAAABHQAAAAAAHQAAAAAAHQAAAAACHQAAAAADHQAAAAADWQAAAAAAWQAAAAADWQAAAAACWQAAAAACWQAAAAADWQAAAAAAWQAAAAACWQAAAAABWQAAAAACWQAAAAACWQAAAAABWQAAAAACWQAAAAABWQAAAAABWQAAAAACWQAAAAABWQAAAAAAWQAAAAACWQAAAAADWQAAAAABWQAAAAACWQAAAAADWQAAAAABWQAAAAADWQAAAAAAWQAAAAACWQAAAAABWQAAAAACWQAAAAAAWQAAAAAAWQAAAAADWQAAAAAA - version: 6 + tiles: TQAAAAAAAHkAAAAAAABNAAAAAAAATQAAAAAAAHkAAAAAAABNAAAAAAAATQAAAAAAAHkAAAAAAABNAAAAAAAATQAAAAAAAHkAAAAAAABNAAAAAAAATQAAAAAAAHkAAAAAAABNAAAAAAAATQAAAAAAAB0AAAAAAgAdAAAAAAAAHQAAAAACAB0AAAAAAgAdAAAAAAMAHQAAAAABAB0AAAAAAQAdAAAAAAAAHQAAAAADAB0AAAAAAgAdAAAAAAMAHQAAAAABAB0AAAAAAwAdAAAAAAMAHQAAAAAAAB0AAAAAAQAdAAAAAAIAHQAAAAABAB0AAAAAAwAdAAAAAAIAHQAAAAACAB0AAAAAAgAdAAAAAAAAHQAAAAACAB0AAAAAAgAdAAAAAAAAHQAAAAABAB0AAAAAAwAdAAAAAAAAHQAAAAADAB0AAAAAAwAdAAAAAAIAHQAAAAAAAB0AAAAAAgAdAAAAAAEAHQAAAAACAB0AAAAAAgAdAAAAAAIAHQAAAAACAB0AAAAAAAAdAAAAAAAAHQAAAAACAB0AAAAAAgAdAAAAAAAAHQAAAAADAB0AAAAAAAAdAAAAAAMAHQAAAAADAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAADwAAAAACAA8AAAAAAwAPAAAAAAMADwAAAAAAAA8AAAAAAAB2AAAAAAIADwAAAAABAA8AAAAAAAAPAAAAAAIADwAAAAABAHkAAAAAAAAdAAAAAAEAHQAAAAAAAB0AAAAAAAAdAAAAAAAAeQAAAAAAAA8AAAAAAgB2AAAAAAMAdgAAAAABAHYAAAAAAwB2AAAAAAAAdgAAAAABAHYAAAAAAwB2AAAAAAEAdgAAAAAAAA8AAAAAAgB5AAAAAAAAHQAAAAAAAB0AAAAAAQAdAAAAAAEAHQAAAAACAHkAAAAAAAAPAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHYAAAAAAQB5AAAAAAAAeQAAAAAAAHkAAAAAAAAPAAAAAAEAeQAAAAAAAB0AAAAAAAAdAAAAAAMAHQAAAAABAB0AAAAAAAB5AAAAAAAADwAAAAABAHkAAAAAAAB2AAAAAAEAdgAAAAADAHYAAAAAAwB2AAAAAAAAdgAAAAAAAHYAAAAAAgB5AAAAAAAADwAAAAABAHkAAAAAAAAdAAAAAAEAHQAAAAACAB0AAAAAAQAdAAAAAAMAeQAAAAAAAA8AAAAAAAB5AAAAAAAAdgAAAAACAHYAAAAAAgB2AAAAAAEAdgAAAAADAHYAAAAAAAB2AAAAAAAAeQAAAAAAAA8AAAAAAwB5AAAAAAAAHQAAAAACAB0AAAAAAwAdAAAAAAEAHQAAAAADAHkAAAAAAAAPAAAAAAAAeQAAAAAAAHYAAAAAAgB2AAAAAAMAHQAAAAACAB0AAAAAAwAdAAAAAAEAdgAAAAACAHkAAAAAAAAPAAAAAAAAeQAAAAAAAB0AAAAAAAAdAAAAAAIAHQAAAAABAB0AAAAAAwB5AAAAAAAADwAAAAADAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAADwAAAAAAAHkAAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAwAdAAAAAAAAeQAAAAAAAA8AAAAAAQAPAAAAAAEAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAGAAMAAAAACAAPAAAAAAAADwAAAAABAA8AAAAAAQAdAAAAAAIAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAACAB0AAAAAAgAdAAAAAAEAHQAAAAADAB0AAAAAAgAdAAAAAAMAHQAAAAADAB0AAAAAAgAdAAAAAAEAHQAAAAAAAB0AAAAAAAAdAAAAAAIAHQAAAAADAB0AAAAAAwBZAAAAAAAAWQAAAAADAFkAAAAAAgBZAAAAAAIAWQAAAAADAFkAAAAAAABZAAAAAAIAWQAAAAABAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAgBZAAAAAAEAWQAAAAABAFkAAAAAAgBZAAAAAAEAWQAAAAAAAFkAAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAwBZAAAAAAAAWQAAAAACAFkAAAAAAQBZAAAAAAIAWQAAAAAAAFkAAAAAAABZAAAAAAMAWQAAAAAAAA== + version: 7 -2,1: ind: -2,1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAbAAAAAAAbAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAADQAAAAACDQAAAAABDQAAAAADTQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAHQAAAAABHQAAAAADHQAAAAACTQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAHQAAAAAAHQAAAAADHQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAHQAAAAACHQAAAAABHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAHQAAAAABHQAAAAAAHQAAAAACeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAHQAAAAABHQAAAAABHQAAAAAATQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAHQAAAAADHQAAAAAAHQAAAAABTQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAHQAAAAABHQAAAAABHQAAAAACTQAAAAAATQAAAAAATQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAHQAAAAAAHQAAAAACHQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAGwAAAAAAABsAAAAAAAAeQAAAAAAAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAADQAAAAACAA0AAAAAAQANAAAAAAMATQAAAAAAAE0AAAAAAABNAAAAAAAAeQAAAAAAAHgAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAaAAAAAAAAB0AAAAAAQAdAAAAAAMAHQAAAAACAE0AAAAAAABNAAAAAAAATQAAAAAAAHkAAAAAAAB4AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAGgAAAAAAAAdAAAAAAAAHQAAAAADAB0AAAAAAABNAAAAAAAATQAAAAAAAE0AAAAAAAB5AAAAAAAAeAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABoAAAAAAAAHQAAAAACAB0AAAAAAQAdAAAAAAMAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHgAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAaAAAAAAAAB0AAAAAAQAdAAAAAAAAHQAAAAACAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB4AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAGgAAAAAAAAdAAAAAAAAHQAAAAAAAB0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABoAAAAAAAAHQAAAAABAB0AAAAAAQAdAAAAAAAATQAAAAAAAE0AAAAAAABNAAAAAAAAeQAAAAAAAHgAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAaAAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAABAE0AAAAAAABNAAAAAAAATQAAAAAAAHkAAAAAAAB4AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAGgAAAAAAAAdAAAAAAEAHQAAAAABAB0AAAAAAgBNAAAAAAAATQAAAAAAAE0AAAAAAAB5AAAAAAAAeAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABoAAAAAAAAHQAAAAAAAB0AAAAAAgAdAAAAAAEAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHgAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB4AAAAAAAAeAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 -1,2: ind: -1,2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAADHQAAAAADHQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAMAHQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 -3,1: ind: -3,1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 0,-3: ind: 0,-3 - tiles: HQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAAAeQAAAAAAHQAAAAACHQAAAAABHQAAAAACHQAAAAACHQAAAAABWQAAAAADHQAAAAACHQAAAAADHQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAADWQAAAAAAWQAAAAACWQAAAAAAWQAAAAABeQAAAAAAHQAAAAACHQAAAAACWQAAAAACWQAAAAADWQAAAAADWQAAAAADWQAAAAADWQAAAAACWQAAAAACeQAAAAAAWQAAAAADWQAAAAACWQAAAAABWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABWQAAAAACWQAAAAADWQAAAAABWQAAAAACWQAAAAADWQAAAAABWQAAAAADWQAAAAACWQAAAAAAWQAAAAABWQAAAAABWQAAAAACeQAAAAAAWQAAAAABeQAAAAAAWQAAAAABWQAAAAADWQAAAAABWQAAAAAAWQAAAAADWQAAAAABWQAAAAACWQAAAAABWQAAAAABWQAAAAACWQAAAAAAWQAAAAADWQAAAAAAeQAAAAAAWQAAAAADWQAAAAAANgAAAAAAWQAAAAADWQAAAAAAWQAAAAABWQAAAAAAWQAAAAADWQAAAAABWQAAAAACWQAAAAACWQAAAAAAWQAAAAAAWQAAAAACWQAAAAADeQAAAAAAWQAAAAABWQAAAAAANgAAAAAAWQAAAAAAWQAAAAAAWQAAAAADWQAAAAADWQAAAAADWQAAAAAAWQAAAAACWQAAAAADWQAAAAADWQAAAAAAWQAAAAAAWQAAAAABeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAACWQAAAAAAWQAAAAACWQAAAAABWQAAAAADWQAAAAABWQAAAAAAWQAAAAABWQAAAAAAWQAAAAADWQAAAAABWQAAAAAAWQAAAAABeQAAAAAAWQAAAAACWQAAAAAAWQAAAAABWQAAAAADWQAAAAABWQAAAAADWQAAAAAAWQAAAAADWQAAAAAAWQAAAAAAWQAAAAACWQAAAAAAWQAAAAAAWQAAAAADWQAAAAADeQAAAAAAWQAAAAAAeQAAAAAAWQAAAAACWQAAAAADWQAAAAADWQAAAAABWQAAAAABWQAAAAACWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABHQAAAAACHQAAAAACeQAAAAAAWQAAAAADWQAAAAACWQAAAAAAWQAAAAABHQAAAAACHQAAAAAAHQAAAAADHQAAAAAAHQAAAAABHQAAAAADDgAAAAADHQAAAAADdgAAAAADdgAAAAACHQAAAAAAeQAAAAAAWQAAAAADWQAAAAAAWQAAAAAAWQAAAAAADgAAAAABDgAAAAAADgAAAAAADgAAAAACDgAAAAAADgAAAAABDgAAAAACHQAAAAACdgAAAAADdgAAAAACHQAAAAADeQAAAAAAWQAAAAACWQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAAAHQAAAAADHQAAAAACHQAAAAACHQAAAAADHQAAAAADHQAAAAABdgAAAAACdgAAAAAAHQAAAAACeQAAAAAAWQAAAAADWQAAAAABeQAAAAAAWQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADeQAAAAAAdgAAAAABdgAAAAACHQAAAAACeQAAAAAAWQAAAAACWQAAAAACeQAAAAAAWQAAAAAD - version: 6 + tiles: HQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAHQAAAAAAAHkAAAAAAAAdAAAAAAIAHQAAAAABAB0AAAAAAgAdAAAAAAIAHQAAAAABAFkAAAAAAwAdAAAAAAIAHQAAAAADAB0AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAB0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAADAB0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAEAeQAAAAAAAB0AAAAAAgAdAAAAAAIAWQAAAAACAFkAAAAAAwBZAAAAAAMAWQAAAAADAFkAAAAAAwBZAAAAAAIAWQAAAAACAHkAAAAAAABZAAAAAAMAWQAAAAACAFkAAAAAAQBZAAAAAAAAWQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAQBZAAAAAAIAWQAAAAADAFkAAAAAAQBZAAAAAAIAWQAAAAADAFkAAAAAAQBZAAAAAAMAWQAAAAACAFkAAAAAAABZAAAAAAEAWQAAAAABAFkAAAAAAgB5AAAAAAAAWQAAAAABAHkAAAAAAABZAAAAAAEAWQAAAAADAFkAAAAAAQBZAAAAAAAAWQAAAAADAFkAAAAAAQBZAAAAAAIAWQAAAAABAFkAAAAAAQBZAAAAAAIAWQAAAAAAAFkAAAAAAwBZAAAAAAAAeQAAAAAAAFkAAAAAAwBZAAAAAAAANgAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAABAFkAAAAAAABZAAAAAAMAWQAAAAABAFkAAAAAAgBZAAAAAAIAWQAAAAAAAFkAAAAAAABZAAAAAAIAWQAAAAADAHkAAAAAAABZAAAAAAEAWQAAAAAAADYAAAAAAABZAAAAAAAAWQAAAAAAAFkAAAAAAwBZAAAAAAMAWQAAAAADAFkAAAAAAABZAAAAAAIAWQAAAAADAFkAAAAAAwBZAAAAAAAAWQAAAAAAAFkAAAAAAQB5AAAAAAAAWQAAAAAAAFkAAAAAAABZAAAAAAIAWQAAAAAAAFkAAAAAAgBZAAAAAAEAWQAAAAADAFkAAAAAAQBZAAAAAAAAWQAAAAABAFkAAAAAAABZAAAAAAMAWQAAAAABAFkAAAAAAABZAAAAAAEAeQAAAAAAAFkAAAAAAgBZAAAAAAAAWQAAAAABAFkAAAAAAwBZAAAAAAEAWQAAAAADAFkAAAAAAABZAAAAAAMAWQAAAAAAAFkAAAAAAABZAAAAAAIAWQAAAAAAAFkAAAAAAABZAAAAAAMAWQAAAAADAHkAAAAAAABZAAAAAAAAeQAAAAAAAFkAAAAAAgBZAAAAAAMAWQAAAAADAFkAAAAAAQBZAAAAAAEAWQAAAAACAFkAAAAAAgB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAQAdAAAAAAIAHQAAAAACAHkAAAAAAABZAAAAAAMAWQAAAAACAFkAAAAAAABZAAAAAAEAHQAAAAACAB0AAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAQAdAAAAAAMADgAAAAADAB0AAAAAAwB2AAAAAAMAdgAAAAACAB0AAAAAAAB5AAAAAAAAWQAAAAADAFkAAAAAAABZAAAAAAAAWQAAAAAAAA4AAAAAAQAOAAAAAAAADgAAAAAAAA4AAAAAAgAOAAAAAAAADgAAAAABAA4AAAAAAgAdAAAAAAIAdgAAAAADAHYAAAAAAgAdAAAAAAMAeQAAAAAAAFkAAAAAAgBZAAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAwAdAAAAAAIAHQAAAAACAB0AAAAAAwAdAAAAAAMAHQAAAAABAHYAAAAAAgB2AAAAAAAAHQAAAAACAHkAAAAAAABZAAAAAAMAWQAAAAABAHkAAAAAAABZAAAAAAMAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAADAHkAAAAAAAB2AAAAAAEAdgAAAAACAB0AAAAAAgB5AAAAAAAAWQAAAAACAFkAAAAAAgB5AAAAAAAAWQAAAAADAA== + version: 7 -1,-3: ind: -1,-3 - tiles: HQAAAAADWQAAAAABHQAAAAACHQAAAAADHQAAAAADHQAAAAACHQAAAAABeQAAAAAAHQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAADHQAAAAAAHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAACHQAAAAADHQAAAAAAeQAAAAAAWQAAAAABWQAAAAADWQAAAAABWQAAAAABWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAACWQAAAAACWQAAAAACWQAAAAABWQAAAAAAeQAAAAAAWQAAAAACWQAAAAAAWQAAAAABWQAAAAACWQAAAAACWQAAAAAAWQAAAAADWQAAAAACWQAAAAAAeQAAAAAAWQAAAAAAWQAAAAACWQAAAAAAWQAAAAACWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAABWQAAAAADWQAAAAAAWQAAAAABWQAAAAABWQAAAAABWQAAAAACeQAAAAAAWQAAAAABWQAAAAADWQAAAAACWQAAAAAAWQAAAAABWQAAAAABWQAAAAACWQAAAAAAWQAAAAAAWQAAAAABWQAAAAAAWQAAAAAAWQAAAAADWQAAAAABWQAAAAACeQAAAAAAWQAAAAACWQAAAAAAWQAAAAADWQAAAAAAWQAAAAAAWQAAAAACWQAAAAACWQAAAAABWQAAAAADWQAAAAADWQAAAAABWQAAAAABNgAAAAAANgAAAAAAWQAAAAABeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAABWQAAAAADWQAAAAACWQAAAAADWQAAAAADWQAAAAABWQAAAAACWQAAAAABWQAAAAADWQAAAAACNgAAAAAANgAAAAAAWQAAAAABeQAAAAAAWQAAAAACWQAAAAADWQAAAAACWQAAAAADWQAAAAAAWQAAAAAAWQAAAAACWQAAAAADWQAAAAABWQAAAAAAWQAAAAACWQAAAAACWQAAAAACWQAAAAADWQAAAAADeQAAAAAAWQAAAAACWQAAAAADWQAAAAACWQAAAAADWQAAAAABWQAAAAABWQAAAAAAWQAAAAACWQAAAAAAWQAAAAADWQAAAAAAWQAAAAADWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAACWQAAAAADWQAAAAABWQAAAAAAWQAAAAADWQAAAAACWQAAAAAAWQAAAAABWQAAAAABWQAAAAACWQAAAAAAeQAAAAAAHQAAAAABHQAAAAADHQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAABeQAAAAAAHQAAAAADDAAAAAABDAAAAAABHQAAAAACDgAAAAABHQAAAAACHQAAAAADHQAAAAABHQAAAAAAHQAAAAACHQAAAAAAHQAAAAADeQAAAAAAWQAAAAAAWQAAAAADeQAAAAAAHQAAAAABDAAAAAADDAAAAAABHQAAAAADDgAAAAADDgAAAAABDgAAAAAADgAAAAADDgAAAAACDgAAAAAADgAAAAACDgAAAAADeQAAAAAAWQAAAAACWQAAAAACeQAAAAAAHQAAAAACDAAAAAABDAAAAAAAHQAAAAAAHQAAAAACHQAAAAAAHQAAAAACHQAAAAADHQAAAAADHQAAAAABHQAAAAABHQAAAAABeQAAAAAAWQAAAAACWQAAAAAAeQAAAAAAHQAAAAADDAAAAAABDAAAAAACeQAAAAAAHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAB - version: 6 + tiles: HQAAAAADAFkAAAAAAQAdAAAAAAIAHQAAAAADAB0AAAAAAwAdAAAAAAIAHQAAAAABAHkAAAAAAAAdAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAADAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAHQAAAAACAB0AAAAAAwAdAAAAAAAAeQAAAAAAAFkAAAAAAQBZAAAAAAMAWQAAAAABAFkAAAAAAQBZAAAAAAEAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABZAAAAAAIAWQAAAAACAFkAAAAAAgBZAAAAAAEAWQAAAAAAAHkAAAAAAABZAAAAAAIAWQAAAAAAAFkAAAAAAQBZAAAAAAIAWQAAAAACAFkAAAAAAABZAAAAAAMAWQAAAAACAFkAAAAAAAB5AAAAAAAAWQAAAAAAAFkAAAAAAgBZAAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAAAWQAAAAAAAFkAAAAAAABZAAAAAAEAWQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAABAFkAAAAAAQBZAAAAAAIAeQAAAAAAAFkAAAAAAQBZAAAAAAMAWQAAAAACAFkAAAAAAABZAAAAAAEAWQAAAAABAFkAAAAAAgBZAAAAAAAAWQAAAAAAAFkAAAAAAQBZAAAAAAAAWQAAAAAAAFkAAAAAAwBZAAAAAAEAWQAAAAACAHkAAAAAAABZAAAAAAIAWQAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAAAAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAwBZAAAAAAMAWQAAAAABAFkAAAAAAQA2AAAAAAAANgAAAAAAAFkAAAAAAQB5AAAAAAAAWQAAAAAAAFkAAAAAAABZAAAAAAEAWQAAAAADAFkAAAAAAgBZAAAAAAMAWQAAAAADAFkAAAAAAQBZAAAAAAIAWQAAAAABAFkAAAAAAwBZAAAAAAIANgAAAAAAADYAAAAAAABZAAAAAAEAeQAAAAAAAFkAAAAAAgBZAAAAAAMAWQAAAAACAFkAAAAAAwBZAAAAAAAAWQAAAAAAAFkAAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAABZAAAAAAIAWQAAAAACAFkAAAAAAgBZAAAAAAMAWQAAAAADAHkAAAAAAABZAAAAAAIAWQAAAAADAFkAAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAQBZAAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAMAWQAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAACAFkAAAAAAwBZAAAAAAEAWQAAAAAAAFkAAAAAAwBZAAAAAAIAWQAAAAAAAFkAAAAAAQBZAAAAAAEAWQAAAAACAFkAAAAAAAB5AAAAAAAAHQAAAAABAB0AAAAAAwAdAAAAAAIAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAAAAFkAAAAAAABZAAAAAAEAeQAAAAAAAB0AAAAAAwAMAAAAAAEADAAAAAABAB0AAAAAAgAOAAAAAAEAHQAAAAACAB0AAAAAAwAdAAAAAAEAHQAAAAAAAB0AAAAAAgAdAAAAAAAAHQAAAAADAHkAAAAAAABZAAAAAAAAWQAAAAADAHkAAAAAAAAdAAAAAAEADAAAAAADAAwAAAAAAQAdAAAAAAMADgAAAAADAA4AAAAAAQAOAAAAAAAADgAAAAADAA4AAAAAAgAOAAAAAAAADgAAAAACAA4AAAAAAwB5AAAAAAAAWQAAAAACAFkAAAAAAgB5AAAAAAAAHQAAAAACAAwAAAAAAQAMAAAAAAAAHQAAAAAAAB0AAAAAAgAdAAAAAAAAHQAAAAACAB0AAAAAAwAdAAAAAAMAHQAAAAABAB0AAAAAAQAdAAAAAAEAeQAAAAAAAFkAAAAAAgBZAAAAAAAAeQAAAAAAAB0AAAAAAwAMAAAAAAEADAAAAAACAHkAAAAAAAAdAAAAAAMAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAABAA== + version: 7 -2,-2: ind: -2,-2 - tiles: AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAbAAAAAADbAAAAAABWQAAAAADAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAbAAAAAADbAAAAAABWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAbAAAAAABbAAAAAADWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAACWQAAAAAAWQAAAAABWQAAAAAAWQAAAAADWQAAAAABWQAAAAAAWQAAAAAAWQAAAAADWQAAAAACWQAAAAADWQAAAAABWQAAAAAAWQAAAAABWQAAAAABWQAAAAACWQAAAAACWQAAAAABWQAAAAAAWQAAAAABWQAAAAACWQAAAAAAWQAAAAAAWQAAAAACWQAAAAAAWQAAAAADWQAAAAADWQAAAAAAWQAAAAABWQAAAAACWQAAAAABWQAAAAACWQAAAAADWQAAAAABWQAAAAADWQAAAAACWQAAAAACWQAAAAACWQAAAAACWQAAAAACWQAAAAABWQAAAAAAWQAAAAADWQAAAAACWQAAAAADWQAAAAAAWQAAAAABWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAEAAAAAAAeQAAAAAAEAAAAAAAEAAAAAAAeQAAAAAAbAAAAAAAbAAAAAAAeQAAAAAACwAAAAAACwAAAAAAeQAAAAAAbAAAAAACbAAAAAADeQAAAAAAHQAAAAADHQAAAAACEAAAAAAAeQAAAAAAEAAAAAAAEAAAAAAAeQAAAAAAbAAAAAAAbAAAAAAAeQAAAAAACwAAAAAACwAAAAAAeQAAAAAAbAAAAAADbAAAAAAAeQAAAAAAHQAAAAABHQAAAAAD - version: 6 + tiles: AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAGwAAAAAAwBsAAAAAAEAWQAAAAADAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAABsAAAAAAMAbAAAAAABAFkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAbAAAAAABAGwAAAAAAwBZAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAgBZAAAAAAAAWQAAAAABAFkAAAAAAABZAAAAAAMAWQAAAAABAFkAAAAAAABZAAAAAAAAWQAAAAADAFkAAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAABZAAAAAAEAWQAAAAABAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAABZAAAAAAEAWQAAAAACAFkAAAAAAABZAAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAMAWQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAACAFkAAAAAAQBZAAAAAAIAWQAAAAADAFkAAAAAAQBZAAAAAAMAWQAAAAACAFkAAAAAAgBZAAAAAAIAWQAAAAACAFkAAAAAAgBZAAAAAAEAWQAAAAAAAFkAAAAAAwBZAAAAAAIAWQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAACAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAQAAAAAAAAeQAAAAAAABAAAAAAAAAQAAAAAAAAeQAAAAAAAGwAAAAAAABsAAAAAAAAeQAAAAAAAAsAAAAAAAALAAAAAAAAeQAAAAAAAGwAAAAAAgBsAAAAAAMAeQAAAAAAAB0AAAAAAwAdAAAAAAIAEAAAAAAAAHkAAAAAAAAQAAAAAAAAEAAAAAAAAHkAAAAAAABsAAAAAAAAbAAAAAAAAHkAAAAAAAALAAAAAAAACwAAAAAAAHkAAAAAAABsAAAAAAMAbAAAAAAAAHkAAAAAAAAdAAAAAAEAHQAAAAADAA== + version: 7 -2,-3: ind: -2,-3 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAwAAAAACeQAAAAAAHQAAAAABHQAAAAABHQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAwAAAAAAeQAAAAAAHQAAAAACHQAAAAABHQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAABWQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAwAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAACWQAAAAABWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAwAAAAAAeQAAAAAAWQAAAAADWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAwAAAAAAeQAAAAAAWQAAAAABWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAABWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAwAAAAAAeQAAAAAAWQAAAAADWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAwAAAAAAeQAAAAAAWQAAAAACWQAAAAAAWQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAwAAAAAAeQAAAAAAWQAAAAACWQAAAAAAWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAbAAAAAACbAAAAAADWQAAAAACAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAbAAAAAACbAAAAAACWQAAAAAD - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAwAAAAACAHkAAAAAAAAdAAAAAAEAHQAAAAABAB0AAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAMAAAAAAAB5AAAAAAAAHQAAAAACAB0AAAAAAQAdAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAQBZAAAAAAEAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAwAAAAAAAHkAAAAAAABZAAAAAAAAWQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAACAFkAAAAAAQBZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAADAAAAAAAAeQAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAwAAAAAAAHkAAAAAAABZAAAAAAEAWQAAAAAAAFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAAAAFkAAAAAAQBZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAADAAAAAAAAeQAAAAAAAFkAAAAAAwBZAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABZAAAAAAAAWQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAMAAAAAAAB5AAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAADAAAAAAAAeQAAAAAAAFkAAAAAAgBZAAAAAAAAWQAAAAACAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAbAAAAAACAGwAAAAAAwBZAAAAAAIAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAGwAAAAAAgBsAAAAAAIAWQAAAAADAA== + version: 7 1,-3: ind: 1,-3 - tiles: eQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAAAHQAAAAABeQAAAAAAAwAAAAAFeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABHQAAAAABeQAAAAAAAwAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAACWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAADWQAAAAAAeQAAAAAAAwAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAACWQAAAAACeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAABWQAAAAABeQAAAAAAAwAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAACeQAAAAAAAwAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAAwAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAACWQAAAAAAeQAAAAAAAwAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAACWQAAAAAAeQAAAAAAAwAAAAAKeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbAAAAAABbAAAAAACeQAAAAAAHQAAAAADHQAAAAADHQAAAAADHQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbAAAAAAAbAAAAAADeQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: eQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0AAAAAAAAdAAAAAAEAeQAAAAAAAAMAAAAABQB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdAAAAAAEAHQAAAAABAHkAAAAAAAADAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAACAFkAAAAAAQB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFkAAAAAAwBZAAAAAAAAeQAAAAAAAAMAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZAAAAAAIAWQAAAAACAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAABAFkAAAAAAQB5AAAAAAAAAwAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFkAAAAAAABZAAAAAAIAeQAAAAAAAAMAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZAAAAAAAAWQAAAAABAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAAFkAAAAAAAB5AAAAAAAAAwAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFkAAAAAAABZAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZAAAAAAIAWQAAAAAAAHkAAAAAAAADAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAACAFkAAAAAAAB5AAAAAAAAAwAAAAAKAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0AAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsAAAAAAEAbAAAAAACAHkAAAAAAAAdAAAAAAMAHQAAAAADAB0AAAAAAwAdAAAAAAAAHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbAAAAAAAAGwAAAAAAwB5AAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAABAB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 1,-4: ind: 1,-4 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 0,-4: ind: 0,-4 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADHQAAAAACHQAAAAACHQAAAAADHQAAAAAAHQAAAAADHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAACHQAAAAADHQAAAAADHQAAAAAAHQAAAAACHQAAAAAAHQAAAAABWQAAAAACHQAAAAADHQAAAAAC - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAgAdAAAAAAMAHQAAAAAAAB0AAAAAAwAdAAAAAAMAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAABAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAHQAAAAACAB0AAAAAAwAdAAAAAAMAHQAAAAAAAB0AAAAAAgAdAAAAAAAAHQAAAAABAFkAAAAAAgAdAAAAAAMAHQAAAAACAA== + version: 7 -1,-4: ind: -1,-4 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAAAHQAAAAACHQAAAAAAHQAAAAABHQAAAAACHQAAAAAAHQAAAAABHQAAAAAAWQAAAAABHQAAAAAAHQAAAAADHQAAAAABHQAAAAACHQAAAAACHQAAAAAAHQAAAAABPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAACHQAAAAAB - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAACAB0AAAAAAAAdAAAAAAEAHQAAAAACAB0AAAAAAAAdAAAAAAEAHQAAAAAAAFkAAAAAAQAdAAAAAAAAHQAAAAADAB0AAAAAAQAdAAAAAAIAHQAAAAACAB0AAAAAAAAdAAAAAAEAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAAdAAAAAAIAHQAAAAABAA== + version: 7 -2,-4: ind: -2,-4 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAHQAAAAAC - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAKAAAAAAAAHQAAAAACAA== + version: 7 -3,0: ind: -3,0 - tiles: WQAAAAAAWQAAAAAAWQAAAAABWQAAAAABWQAAAAAAWQAAAAABWQAAAAACWQAAAAABWQAAAAABWQAAAAACWQAAAAABWQAAAAABWQAAAAADWQAAAAABWQAAAAAAWQAAAAADHQAAAAABHQAAAAAAHQAAAAAAHQAAAAABHQAAAAABHQAAAAABHQAAAAACHQAAAAACHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAADHQAAAAAAHQAAAAACHQAAAAACHQAAAAADeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADeQAAAAAAdgAAAAABeQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAHQAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAACeQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAHQAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAABeQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAHQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAADeQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAeQAAAAAAHQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAANgAAAAAACAAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: WQAAAAAAAFkAAAAAAABZAAAAAAEAWQAAAAABAFkAAAAAAABZAAAAAAEAWQAAAAACAFkAAAAAAQBZAAAAAAEAWQAAAAACAFkAAAAAAQBZAAAAAAEAWQAAAAADAFkAAAAAAQBZAAAAAAAAWQAAAAADAB0AAAAAAQAdAAAAAAAAHQAAAAAAAB0AAAAAAQAdAAAAAAEAHQAAAAABAB0AAAAAAgAdAAAAAAIAHQAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAADAB0AAAAAAAAdAAAAAAIAHQAAAAACAB0AAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwB5AAAAAAAAdgAAAAABAHkAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAHkAAAAAAAAdAAAAAAAAdgAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAgB5AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAB5AAAAAAAAHQAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAAB2AAAAAAEAeQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAeQAAAAAAAB0AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAdgAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAADAHkAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAHkAAAAAAAAdAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAHQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAHQAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAB0AAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAA2AAAAAAAACAAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAAB2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAHQAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAB0AAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAAB2AAAAAAAAdgAAAAAAAHYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 -3,-1: ind: -3,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAATQAAAAAATQAAAAAAeQAAAAAATQAAAAAATQAAAAAAeQAAAAAATQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAADHQAAAAABHQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAACHQAAAAAAHQAAAAABHQAAAAACHQAAAAABHQAAAAACHQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAHQAAAAAAHQAAAAADHQAAAAADHQAAAAAAHQAAAAAAHQAAAAACHQAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAeQAAAAAAHQAAAAADHQAAAAABHQAAAAAAHQAAAAAAHQAAAAACHQAAAAACHQAAAAAAHQAAAAABHQAAAAADHQAAAAACHQAAAAAAeQAAAAAAHQAAAAADHQAAAAACAAAAAAAAeQAAAAAAHQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAADHQAAAAACHQAAAAABHQAAAAADHQAAAAABHQAAAAAAHQAAAAACeQAAAAAAHQAAAAACHQAAAAADeQAAAAAAeQAAAAAAHQAAAAADPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAACHQAAAAAAHQAAAAABHQAAAAAAHQAAAAACHQAAAAAAHQAAAAACHQAAAAADHQAAAAABHQAAAAACEgAAAAAAeQAAAAAAHQAAAAACPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAACHQAAAAAANgAAAAAANgAAAAAAHQAAAAACHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAAAHQAAAAADEgAAAAAAeQAAAAAAHQAAAAADPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAAAHQAAAAABHQAAAAAAHQAAAAAAHQAAAAADHQAAAAAAHQAAAAABHQAAAAAAHQAAAAACHQAAAAACEgAAAAAAeQAAAAAAHQAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAHQAAAAABHQAAAAADHQAAAAACHQAAAAABHQAAAAABHQAAAAADHQAAAAAAeQAAAAAAHQAAAAAAHQAAAAAAEgAAAAAAeQAAAAAAHQAAAAABHQAAAAAAHQAAAAAAHQAAAAADHQAAAAAAHQAAAAACHQAAAAADHQAAAAAAHQAAAAAAHQAAAAADHQAAAAABeQAAAAAAHQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAACeQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAAAHQAAAAADHQAAAAABHQAAAAAAHQAAAAADHQAAAAADHQAAAAADHQAAAAACHQAAAAABHQAAAAABHQAAAAABHQAAAAADHQAAAAACHQAAAAAAHQAAAAADHQAAAAABWQAAAAAAWQAAAAACWQAAAAADWQAAAAABWQAAAAAAWQAAAAAAWQAAAAADWQAAAAACWQAAAAACWQAAAAABWQAAAAADWQAAAAADWQAAAAACWQAAAAADWQAAAAACWQAAAAADWQAAAAACWQAAAAAAWQAAAAABWQAAAAACWQAAAAACWQAAAAABWQAAAAABWQAAAAACWQAAAAABWQAAAAAAWQAAAAABWQAAAAADWQAAAAAAWQAAAAABWQAAAAAAWQAAAAAB - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAATQAAAAAAAE0AAAAAAAB5AAAAAAAATQAAAAAAAE0AAAAAAAB5AAAAAAAATQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAMAHQAAAAABAB0AAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAQAdAAAAAAIAHQAAAAABAB0AAAAAAgAdAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAHQAAAAAAAB0AAAAAAwAdAAAAAAMAHQAAAAAAAB0AAAAAAAAdAAAAAAIAHQAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAEAHQAAAAAAAB0AAAAAAAAdAAAAAAIAHQAAAAACAB0AAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAgAdAAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAIAAAAAAAAAAHkAAAAAAAAdAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAHQAAAAADAB0AAAAAAgAdAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAAAHQAAAAACAHkAAAAAAAAdAAAAAAIAHQAAAAADAHkAAAAAAAB5AAAAAAAAHQAAAAADAD4AAAAAAAA+AAAAAAAAPgAAAAAAAB0AAAAAAgAdAAAAAAAAHQAAAAABAB0AAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAgAdAAAAAAMAHQAAAAABAB0AAAAAAgASAAAAAAAAeQAAAAAAAB0AAAAAAgA+AAAAAAAAPgAAAAAAAD4AAAAAAAAdAAAAAAIAHQAAAAAAADYAAAAAAAA2AAAAAAAAHQAAAAACAB0AAAAAAAAdAAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAMAEgAAAAAAAHkAAAAAAAAdAAAAAAMAPgAAAAAAAD4AAAAAAAA+AAAAAAAAHQAAAAAAAB0AAAAAAQAdAAAAAAAAHQAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAABAB0AAAAAAAAdAAAAAAIAHQAAAAACABIAAAAAAAB5AAAAAAAAHQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAB0AAAAAAQAdAAAAAAMAHQAAAAACAB0AAAAAAQAdAAAAAAEAHQAAAAADAB0AAAAAAAB5AAAAAAAAHQAAAAAAAB0AAAAAAAASAAAAAAAAeQAAAAAAAB0AAAAAAQAdAAAAAAAAHQAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAACAB0AAAAAAwAdAAAAAAAAHQAAAAAAAB0AAAAAAwAdAAAAAAEAeQAAAAAAAB0AAAAAAAAdAAAAAAEAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAgB5AAAAAAAAHQAAAAABAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAAAdAAAAAAMAHQAAAAABAB0AAAAAAAAdAAAAAAMAHQAAAAADAB0AAAAAAwAdAAAAAAIAHQAAAAABAB0AAAAAAQAdAAAAAAEAHQAAAAADAB0AAAAAAgAdAAAAAAAAHQAAAAADAB0AAAAAAQBZAAAAAAAAWQAAAAACAFkAAAAAAwBZAAAAAAEAWQAAAAAAAFkAAAAAAABZAAAAAAMAWQAAAAACAFkAAAAAAgBZAAAAAAEAWQAAAAADAFkAAAAAAwBZAAAAAAIAWQAAAAADAFkAAAAAAgBZAAAAAAMAWQAAAAACAFkAAAAAAABZAAAAAAEAWQAAAAACAFkAAAAAAgBZAAAAAAEAWQAAAAABAFkAAAAAAgBZAAAAAAEAWQAAAAAAAFkAAAAAAQBZAAAAAAMAWQAAAAAAAFkAAAAAAQBZAAAAAAAAWQAAAAABAA== + version: 7 -3,-2: ind: -3,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAABWQAAAAADWQAAAAAAWQAAAAAAWQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAACgAAAAAAWQAAAAADWQAAAAAAWQAAAAACWQAAAAABWQAAAAABWQAAAAAAWQAAAAACWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAACWQAAAAACWQAAAAACWQAAAAABWQAAAAABWQAAAAADWQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAEAAAAAAAEAAAAAAAeQAAAAAAEAAAAAAAEAAAAAAAeQAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAEAAAAAAAEAAAAAAAeQAAAAAAEAAAAAAAEAAAAAAAeQAAAAAAEAAAAAAA - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAAAAFkAAAAAAABZAAAAAAAAWQAAAAABAFkAAAAAAwBZAAAAAAAAWQAAAAAAAFkAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAACgAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAACAFkAAAAAAQBZAAAAAAEAWQAAAAAAAFkAAAAAAgBZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABZAAAAAAAAWQAAAAACAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAQBZAAAAAAMAWQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAQAAAAAAAAEAAAAAAAAHkAAAAAAAAQAAAAAAAAEAAAAAAAAHkAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAEAAAAAAAABAAAAAAAAB5AAAAAAAAEAAAAAAAABAAAAAAAAB5AAAAAAAAEAAAAAAAAA== + version: 7 -4,-1: ind: -4,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAEgAAAAAAEgAAAAAAEgAAAAAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAEgAAAAAAEgAAAAAAEgAAAAAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAEgAAAAAAEgAAAAAAEgAAAAAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAEgAAAAAAEgAAAAAAEgAAAAAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAACeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAADHQAAAAABHQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAWQAAAAADWQAAAAAAWQAAAAADWQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAWQAAAAACWQAAAAABWQAAAAACWQAAAAAA - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAASAAAAAAAAEgAAAAAAABIAAAAAAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAEgAAAAAAABIAAAAAAAASAAAAAAAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAABIAAAAAAAASAAAAAAAAEgAAAAAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAASAAAAAAAAEgAAAAAAABIAAAAAAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAdAAAAAAIAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAMAHQAAAAABAB0AAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAoAAAAAAABZAAAAAAMAWQAAAAAAAFkAAAAAAwBZAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAKAAAAAAAAWQAAAAACAFkAAAAAAQBZAAAAAAIAWQAAAAAAAA== + version: 7 -4,0: ind: -4,0 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAACgAAAAAAWQAAAAADWQAAAAADWQAAAAACWQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAABHQAAAAABHQAAAAABHQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAHQAAAAADeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAdgAAAAADdgAAAAAAdgAAAAACdgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAdgAAAAADdgAAAAADdgAAAAABdgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAdgAAAAABdgAAAAADdgAAAAABdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAdgAAAAABdgAAAAACdgAAAAACdgAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAKAAAAAAAAWQAAAAADAFkAAAAAAwBZAAAAAAIAWQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAQAdAAAAAAEAHQAAAAABAB0AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAdgAAAAADAHYAAAAAAAB2AAAAAAIAdgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHYAAAAAAwB2AAAAAAMAdgAAAAABAHYAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB2AAAAAAEAdgAAAAADAHYAAAAAAQB2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAdgAAAAABAHYAAAAAAgB2AAAAAAIAdgAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 -3,-3: ind: -3,-3 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 - type: Broadphase - type: Physics bodyStatus: InAir @@ -365,7 +365,6 @@ entities: 1108: -13,26 1109: -12,27 1110: -11,26 - 1130: 13,9 1167: 3,31 2839: -6,-10 - node: @@ -394,11 +393,57 @@ entities: 1448: -16,24 1449: -16,25 2840: -6,-11 + - node: + color: '#CC8000FF' + id: BrickCornerOverlayNE + decals: + 3355: 6,12 + - node: + color: '#CC8000FF' + id: BrickCornerOverlayNW + decals: + 3352: 3,12 + - node: + color: '#CC8000FF' + id: BrickCornerOverlaySE + decals: + 3344: 6,7 + - node: + color: '#CC8000FF' + id: BrickCornerOverlaySW + decals: + 3347: 3,7 + - node: + color: '#CC8000FF' + id: BrickLineOverlayE + decals: + 3342: 6,10 + 3343: 6,9 + - node: + color: '#CC8000FF' + id: BrickLineOverlayN + decals: + 3353: 4,12 + 3354: 5,12 - node: color: '#FFFFFFFF' id: BrickLineOverlayN decals: 2958: -27.001663,-17.373186 + - node: + color: '#CC8000FF' + id: BrickLineOverlayS + decals: + 3345: 5,7 + 3346: 4,7 + - node: + color: '#CC8000FF' + id: BrickLineOverlayW + decals: + 3348: 3,8 + 3349: 3,9 + 3350: 3,10 + 3351: 3,11 - node: color: '#FFFFFFFF' id: BrickLineOverlayW @@ -569,11 +614,6 @@ entities: 619: 34,-16 695: 34,-26 2689: -43,-6 - - node: - color: '#52B4E996' - id: BrickTileWhiteCornerNe - decals: - 832: 23,12 - node: color: '#79150096' id: BrickTileWhiteCornerNe @@ -589,12 +629,10 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteCornerNe decals: - 821: 7,12 - 870: 17,12 986: 16,20 - 1494: 11,12 1763: -10,-39 1874: -18,-40 + 3312: 10,12 - node: color: '#EFD2415D' id: BrickTileWhiteCornerNe @@ -618,7 +656,6 @@ entities: id: BrickTileWhiteCornerNw decals: 735: 9,-5 - 831: 19,12 - node: color: '#79150096' id: BrickTileWhiteCornerNw @@ -634,12 +671,12 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteCornerNw decals: - 820: 3,12 - 859: 9,12 985: 10,20 - 1495: 15,12 1764: -14,-39 1875: -19,-40 + 3232: 15,12 + 3300: 12,11 + 3313: 8,12 - node: color: '#334E6DC8' id: BrickTileWhiteCornerSe @@ -647,11 +684,6 @@ entities: 621: 34,-18 693: 34,-28 2691: -43,-10 - - node: - color: '#52B4E996' - id: BrickTileWhiteCornerSe - decals: - 834: 23,10 - node: color: '#79150096' id: BrickTileWhiteCornerSe @@ -667,7 +699,6 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteCornerSe decals: - 823: 7,10 843: 17,3 982: 16,18 1766: -10,-44 @@ -699,7 +730,6 @@ entities: id: BrickTileWhiteCornerSw decals: 733: 9,-7 - 833: 19,10 - node: color: '#79150096' id: BrickTileWhiteCornerSw @@ -715,11 +745,11 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteCornerSw decals: - 822: 3,10 848: 12,3 981: 10,18 1765: -14,-44 1872: -19,-43 + 3316: 8,7 - node: color: '#EFCC4163' id: BrickTileWhiteCornerSw @@ -746,8 +776,8 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteInnerNe decals: - 864: 11,11 865: 13,11 + 3304: 10,8 - node: color: '#EFD2415D' id: BrickTileWhiteInnerNe @@ -773,8 +803,10 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteInnerNw decals: - 863: 15,11 866: 13,11 + 3233: 15,11 + 3239: 17,12 + 3303: 12,8 - node: color: '#FFFFFFFF' id: BrickTileWhiteInnerNw @@ -789,7 +821,7 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteInnerSe decals: - 881: 10,7 + 3305: 10,7 - node: color: '#EFD2415D' id: BrickTileWhiteInnerSe @@ -815,7 +847,8 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteInnerSw decals: - 852: 12,7 + 3306: 12,7 + 3318: 9,7 - node: color: '#EFD2415D' id: BrickTileWhiteInnerSw @@ -870,11 +903,6 @@ entities: 554: 31,-12 555: 31,-11 556: 31,-10 - - node: - color: '#52B4E996' - id: BrickTileWhiteLineE - decals: - 830: 23,11 - node: color: '#79150096' id: BrickTileWhiteLineE @@ -896,8 +924,6 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteLineE decals: - 827: 7,11 - 871: 17,11 872: 17,9 873: 17,8 874: 17,7 @@ -914,6 +940,9 @@ entities: 1773: -10,-40 1870: -18,-42 1871: -18,-41 + 3309: 10,9 + 3310: 10,10 + 3311: 10,11 - node: color: '#EFCC4163' id: BrickTileWhiteLineE @@ -1058,9 +1087,6 @@ entities: 740: 14,-5 741: 15,-5 742: 16,-5 - 835: 20,12 - 836: 21,12 - 837: 22,12 - node: color: '#9FED5896' id: BrickTileWhiteLineN @@ -1072,10 +1098,6 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteLineN decals: - 817: 4,12 - 818: 5,12 - 819: 6,12 - 867: 12,11 868: 14,11 987: 15,20 988: 14,20 @@ -1084,6 +1106,7 @@ entities: 1778: -13,-39 1779: -12,-39 1780: -11,-39 + 3308: 11,8 - node: color: '#EFD2415D' id: BrickTileWhiteLineN @@ -1242,9 +1265,6 @@ entities: decals: 731: 11,-7 732: 10,-7 - 838: 20,10 - 839: 21,10 - 840: 22,10 - node: color: '#9FED5896' id: BrickTileWhiteLineS @@ -1256,14 +1276,10 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteLineS decals: - 824: 6,10 - 825: 5,10 - 826: 4,10 844: 16,3 845: 15,3 846: 14,3 847: 13,3 - 853: 11,7 976: 12,18 977: 14,18 978: 15,18 @@ -1271,6 +1287,7 @@ entities: 1767: -11,-44 1768: -12,-44 1769: -13,-44 + 3307: 11,7 - node: color: '#EFCC4163' id: BrickTileWhiteLineS @@ -1376,7 +1393,6 @@ entities: 728: 12,-9 729: 12,-8 734: 9,-6 - 829: 19,11 - node: color: '#79150096' id: BrickTileWhiteLineW @@ -1398,12 +1414,9 @@ entities: color: '#DE3A3A96' id: BrickTileWhiteLineW decals: - 828: 3,11 849: 12,4 850: 12,5 851: 12,6 - 857: 9,9 - 858: 9,11 882: 9,6 883: 9,5 884: 9,4 @@ -1415,6 +1428,10 @@ entities: 1777: -14,-40 1868: -19,-41 1869: -19,-42 + 3301: 12,10 + 3302: 12,9 + 3314: 8,10 + 3315: 8,9 - node: color: '#EFCC4163' id: BrickTileWhiteLineW @@ -1471,17 +1488,11 @@ entities: 1899: 19,-46 1904: -21,-47 2439: -15,3 - - node: - color: '#FFFFFFFF' - id: BushDThree - decals: - 1457: 6.9084163,7.696661 - 1458: 6.4396663,7.352911 + 3290: 21.088001,10.494442 - node: color: '#FFFFFFFF' id: BushDTwo decals: - 1459: 3.2052913,7.727911 2185: -24,-21 - node: color: '#FFFFFFFF' @@ -1505,6 +1516,7 @@ entities: 1894: 19,-42 2402: -28.567415,3.2062178 2437: -11,-5 + 3277: 22.989494,10.9857645 - node: color: '#FFFFFFFF' id: Bushc2 @@ -1515,6 +1527,12 @@ entities: id: Bushc3 decals: 1900: 19,-44 + - node: + color: '#FFFFFFFF' + id: Bushd1 + decals: + 3271: 20.260328,11.579927 + 3272: 19.291578,10.798135 - node: color: '#FFFFFFFF' id: Bushe1 @@ -1537,14 +1555,13 @@ entities: decals: 153: 18.801558,6.901756 154: 33.138065,6.979881 - 1456: 3.7990413,7.759161 - node: color: '#FFFFFFFF' id: Bushf1 decals: - 1455: 4.7990413,7.446661 2186: -12,-22 2187: -4,-22 + 3289: 19.926857,10.994789 - node: color: '#FFFFFFFF' id: Bushf2 @@ -1562,6 +1579,11 @@ entities: id: Bushg1 decals: 2232: 2,-22 + - node: + color: '#FFFFFFFF' + id: Bushg3 + decals: + 3288: 19.739357,11.3387785 - node: color: '#FFFFFFFF' id: Bushg4 @@ -1595,8 +1617,6 @@ entities: color: '#FFFFFFFF' id: Bushi2 decals: - 1453: 3.0802913,6.946661 - 1454: 6.2677913,7.806036 1915: 19,-41 1978: 4.0082064,-31.102646 2179: -4,-21 @@ -1636,16 +1656,20 @@ entities: 2449: -10,-5 2799: -40,-23 2800: -36,-32 + 3281: 19.458244,11.64247 + 3287: 21.043343,12.0816 - node: color: '#FFFFFFFF' id: Bushj1 decals: 2409: -29.004915,3.0343428 + 3276: 23.281162,10.78771 - node: color: '#FFFFFFFF' id: Bushj2 decals: 2411: -26.27054,4.378093 + 3283: 19.223536,9.903004 - node: color: '#FFFFFFFF' id: Bushj3 @@ -1822,30 +1846,6 @@ entities: 2540: -37,-14 2541: -36,-14 2542: -38,-14 - - node: - color: '#D381C95D' - id: CheckerNWSE - decals: - 2862: 20,-30 - 2863: 19,-30 - 2864: 19,-31 - 2865: 20,-31 - 2866: 21,-31 - 2867: 21,-30 - 2868: 22,-30 - 2869: 22,-31 - 2870: 22,-32 - 2871: 21,-32 - 2872: 20,-32 - 2873: 19,-32 - 2874: 19,-33 - 2875: 20,-33 - 2876: 21,-33 - 2877: 22,-33 - 2878: 22,-34 - 2879: 21,-34 - 2880: 20,-34 - 2881: 19,-34 - node: color: '#D4D4D428' id: CheckerNWSE @@ -2044,20 +2044,16 @@ entities: 2933: 14,-44 2934: -16,-44 2935: -16,-39 - - node: - color: '#52B4E996' - id: DeliveryGreyscale - decals: - 841: 18,10 - 1497: 15,12 - node: color: '#DE3A3A96' id: DeliveryGreyscale decals: - 842: 8,10 891: 10,13 892: 16,13 - 1496: 11,12 + 3360: 10,12 + 3361: 10,11 + 3362: 10,10 + 3363: 10,9 - node: color: '#FFFFFFFF' id: DeliveryGreyscale @@ -2243,8 +2239,9 @@ entities: color: '#52B4E996' id: FullTileOverlayGreyscale decals: - 2823: -13,-9 - 2824: -12,-9 + 3263: -11,-7 + 3264: -12,-7 + 3265: -13,-7 - node: color: '#79150096' id: FullTileOverlayGreyscale @@ -2269,8 +2266,8 @@ entities: color: '#A4610696' id: FullTileOverlayGreyscale decals: - 2821: -13,-7 - 2822: -12,-7 + 3266: -13,-9 + 3267: -12,-9 - node: color: '#D381C996' id: FullTileOverlayGreyscale @@ -2660,7 +2657,6 @@ entities: decals: 1856: 13,-35 1857: 13,-33 - 1858: 13,-31 - node: color: '#D4D4D428' id: HalfTileOverlayGreyscale270 @@ -2886,6 +2882,35 @@ entities: 1374: -2,26 1375: -2,27 1376: -2,28 + - node: + color: '#D381C996' + id: QuarterTileOverlayGreyscale + decals: + 3126: 19,-30 + 3127: 20,-30 + 3128: 21,-30 + 3129: 22,-30 + 3130: 23,-30 + 3131: 23,-31 + 3132: 22,-31 + 3133: 21,-31 + 3134: 20,-31 + 3135: 19,-31 + 3136: 23,-34 + 3137: 23,-33 + 3138: 23,-32 + 3139: 22,-32 + 3140: 22,-33 + 3141: 22,-34 + 3142: 21,-34 + 3143: 20,-34 + 3144: 20,-33 + 3145: 21,-33 + 3146: 21,-32 + 3147: 20,-32 + 3148: 19,-32 + 3149: 19,-33 + 3150: 19,-34 - node: color: '#D4D4D428' id: QuarterTileOverlayGreyscale @@ -3077,6 +3102,35 @@ entities: 1356: 2,18 1357: 3,18 1358: 4,18 + - node: + color: '#D381C996' + id: QuarterTileOverlayGreyscale180 + decals: + 3151: 19,-32 + 3152: 19,-33 + 3153: 19,-34 + 3154: 20,-34 + 3155: 20,-33 + 3156: 20,-32 + 3157: 21,-32 + 3158: 21,-33 + 3159: 21,-34 + 3160: 22,-34 + 3161: 22,-33 + 3162: 22,-32 + 3163: 23,-32 + 3164: 23,-33 + 3165: 23,-34 + 3166: 23,-31 + 3167: 23,-30 + 3168: 22,-30 + 3169: 22,-31 + 3170: 21,-31 + 3171: 21,-30 + 3172: 19,-30 + 3173: 20,-30 + 3174: 20,-31 + 3175: 19,-31 - node: color: '#D4D4D428' id: QuarterTileOverlayGreyscale180 @@ -3639,13 +3693,20 @@ entities: 1404: 4,19 1405: 4,20 1498: 7,7 - 1499: 7,8 + - node: + color: '#CC8000FF' + id: WarnLineGreyscaleE + decals: + 3358: 6,8 + 3359: 6,11 - node: color: '#DE3A3A96' id: WarnLineGreyscaleE decals: 887: 17,10 888: 17,4 + 3238: 17,12 + 3269: 17,11 - node: color: '#FFFFFFFF' id: WarnLineGreyscaleE @@ -3685,9 +3746,9 @@ entities: 480: 9,1 481: 10,1 702: 33,-16 - 889: 10,12 890: 16,12 992: 13,20 + 3339: 9,12 - node: color: '#FA750096' id: WarnLineGreyscaleN @@ -3730,9 +3791,8 @@ entities: color: '#DE3A3A96' id: WarnLineGreyscaleW decals: - 886: 9,10 - 1500: 9,7 - 1501: 9,8 + 3337: 8,8 + 3338: 8,11 - node: color: '#FFFFFFFF' id: WarnLineGreyscaleW @@ -4192,25 +4252,25 @@ entities: 0,1: 0: 45567 0,2: - 0: 47931 + 0: 48059 0,3: 0: 65307 0,4: 0: 65311 1,1: - 0: 61559 + 0: 28791 1,2: - 0: 65295 + 0: 63359 1,3: - 0: 65295 + 0: 65287 1,4: 0: 56591 2,1: 0: 63231 2,2: - 0: 61423 + 0: 30591 2,3: - 0: 65359 + 0: 65319 2,4: 0: 64783 3,1: @@ -4226,7 +4286,7 @@ entities: 4,1: 0: 46015 4,2: - 0: 48955 + 0: 65339 4,3: 0: 65311 4,-5: @@ -4247,13 +4307,14 @@ entities: 0: 15 1: 3584 6,-2: - 0: 65520 + 0: 65524 6,-1: 0: 65535 6,-5: 0: 65535 6,-3: - 1: 57344 + 1: 14 + 0: 60928 6,0: 0: 65535 7,-4: @@ -4288,10 +4349,10 @@ entities: 0: 61167 6,1: 0: 61183 - 6,3: - 0: 65294 6,2: - 0: 60942 + 0: 56590 + 6,3: + 0: 65293 6,4: 0: 65535 7,1: @@ -4342,6 +4403,8 @@ entities: 0: 65535 6,-6: 0: 65535 + 6,-8: + 0: 16384 7,-7: 0: 32767 7,-6: @@ -4731,22 +4794,20 @@ entities: 0: 4368 -5,-6: 0: 4368 - -6,-13: - 0: 57344 -6,-12: 0: 2176 -6,-11: 0: 34824 -6,-10: 0: 32896 + -6,-13: + 0: 34944 -5,-13: 0: 61440 -5,-11: 0: 26214 4,-13: - 0: 61440 - 5,-13: - 0: 12288 + 0: 63616 -12,0: 0: 53503 -12,-1: @@ -4766,11 +4827,11 @@ entities: -10,0: 0: 61695 -10,1: - 0: 61439 + 0: 65535 -10,-1: 0: 65522 -10,2: - 0: 61152 + 0: 61156 -10,3: 0: 238 -12,-2: @@ -4892,6 +4953,7 @@ entities: chunkSize: 4 - type: OccluderTree - type: Shuttle + dampingModifier: 0.25 - type: RadiationGridResistance - type: GravityShake shakeTimes: 10 @@ -4992,15 +5054,22 @@ entities: parent: 1668 - type: DeviceLinkSink invokeCounter: 1 + - uid: 1589 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 11.5,8.5 + parent: 1668 - uid: 1604 components: - type: Transform pos: 16.5,13.5 parent: 1668 - - uid: 1605 + - uid: 1615 components: - type: Transform - pos: 10.5,13.5 + rot: 3.141592653589793 rad + pos: 11.5,7.5 parent: 1668 - proto: AirlockBrigLocked entities: @@ -5289,6 +5358,11 @@ entities: parent: 1668 - proto: AirlockCommandGlassLocked entities: + - uid: 1587 + components: + - type: Transform + pos: 26.5,-28.5 + parent: 1668 - uid: 6753 components: - type: Transform @@ -5566,6 +5640,13 @@ entities: - type: Transform pos: 14.5,-20.5 parent: 1668 +- proto: AirlockGlass + entities: + - uid: 9994 + components: + - type: Transform + pos: 26.5,-7.5 + parent: 1668 - proto: AirlockGlassShuttle entities: - uid: 3642 @@ -5717,20 +5798,6 @@ entities: - type: Transform pos: -18.5,-43.5 parent: 1668 -- proto: AirlockSecurityGlassLocked - entities: - - uid: 1581 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 8.5,10.5 - parent: 1668 - - uid: 1582 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 18.5,10.5 - parent: 1668 - proto: AirlockSecurityLocked entities: - uid: 556 @@ -5755,17 +5822,15 @@ entities: parent: 1668 - proto: AirlockShuttle entities: - - uid: 4617 + - uid: 5218 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 21.5,-48.5 + pos: -20.5,-50.5 parent: 1668 - - uid: 4618 + - uid: 9118 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -22.5,-48.5 + pos: 19.5,-50.5 parent: 1668 - proto: AlwaysPoweredLightSodium entities: @@ -6269,16 +6334,6 @@ entities: - type: Transform pos: -21.5,18.5 parent: 1668 - - uid: 4619 - components: - - type: Transform - pos: 21.5,-48.5 - parent: 1668 - - uid: 4620 - components: - - type: Transform - pos: -22.5,-48.5 - parent: 1668 - uid: 5646 components: - type: Transform @@ -6588,16 +6643,6 @@ entities: - type: Transform pos: 33.5,-19.5 parent: 1668 - - uid: 1623 - components: - - type: Transform - pos: 23.5,10.5 - parent: 1668 - - uid: 1624 - components: - - type: Transform - pos: 3.5,10.5 - parent: 1668 - uid: 2826 components: - type: Transform @@ -7230,6 +7275,11 @@ entities: - type: Transform pos: 29.5,-13.5 parent: 1668 + - uid: 2104 + components: + - type: Transform + pos: 9.5,13.5 + parent: 1668 - uid: 2252 components: - type: Transform @@ -7304,6 +7354,11 @@ entities: - type: Transform pos: -23.5,11.5 parent: 1668 + - uid: 2923 + components: + - type: Transform + pos: 5.5,7.5 + parent: 1668 - uid: 4446 components: - type: Transform @@ -7409,6 +7464,11 @@ entities: - type: Transform pos: -0.24795187,1.5721796 parent: 1668 + - uid: 1611 + components: + - type: Transform + pos: 33.64069,11.514202 + parent: 1668 - uid: 1986 components: - type: Transform @@ -7419,11 +7479,6 @@ entities: - type: Transform pos: 27.5,21.5 parent: 1668 - - uid: 2083 - components: - - type: Transform - pos: 32.58944,11.550044 - parent: 1668 - uid: 6767 components: - type: Transform @@ -7456,6 +7511,40 @@ entities: - type: Transform pos: -17.46816,12.582452 parent: 1668 + - uid: 4560 + components: + - type: Transform + parent: 4559 + - type: Storage + storedItems: + 4617: + position: 0,0 + _rotation: South + 4618: + position: 1,0 + _rotation: South + 4619: + position: 3,0 + _rotation: South + 4620: + position: 2,0 + _rotation: South + 4656: + position: 4,0 + _rotation: South + - type: ContainerContainer + containers: + storagebase: !type:Container + showEnts: False + occludes: True + ents: + - 4617 + - 4618 + - 4619 + - 4620 + - 4656 + - type: Physics + canCollide: False - uid: 9577 components: - type: Transform @@ -7473,6 +7562,12 @@ entities: - type: Transform pos: -3.6052928,-1.4909649 parent: 1668 + - uid: 4838 + components: + - type: Transform + parent: 4559 + - type: Physics + canCollide: False - uid: 5999 components: - type: Transform @@ -7529,7 +7624,7 @@ entities: - uid: 533 components: - type: Transform - pos: -0.71670187,1.5721796 + pos: -0.73396444,1.5546008 parent: 1668 - uid: 1987 components: @@ -7541,10 +7636,10 @@ entities: - type: Transform pos: 25.5,21.5 parent: 1668 - - uid: 2089 + - uid: 2088 components: - type: Transform - pos: 32.386314,11.675044 + pos: 33.411522,11.701833 parent: 1668 - uid: 6768 components: @@ -7580,7 +7675,7 @@ entities: - uid: 8815 components: - type: Transform - pos: -13.5,-6.5 + pos: -13.649994,-6.1762576 parent: 1668 - proto: BoxingBell entities: @@ -7594,7 +7689,7 @@ entities: - uid: 1093 components: - type: Transform - pos: 34.536674,-20.85105 + pos: 34.34723,-21.065468 parent: 1668 - proto: BoxMagazineLightRifle entities: @@ -7610,10 +7705,10 @@ entities: parent: 1668 - proto: BoxMagazinePistolPractice entities: - - uid: 2984 + - uid: 9962 components: - type: Transform - pos: 11.430329,8.720324 + pos: 18.81643,12.32891 parent: 1668 - proto: BoxMagazinePistolSubMachineGun entities: @@ -7639,10 +7734,10 @@ entities: parent: 1668 - proto: BoxMagazinePistolSubMachineGunPractice entities: - - uid: 1742 + - uid: 9979 components: - type: Transform - pos: 11.445954,9.423449 + pos: 18.618511,12.672898 parent: 1668 - proto: BoxMagazinePistolSubMachineGunTopMounted entities: @@ -7699,10 +7794,10 @@ entities: - type: InsideEntityStorage - proto: BoxMagazineRiflePractice entities: - - uid: 1735 + - uid: 9978 components: - type: Transform - pos: 11.430329,9.079699 + pos: 18.524761,12.381029 parent: 1668 - proto: BoxMagazineShotgun entities: @@ -7776,42 +7871,42 @@ entities: - uid: 1323 components: - type: Transform - pos: 34.536674,-21.41355 + pos: 34.325996,-21.70354 parent: 1668 - proto: BoxShotgunFlare entities: - uid: 1329 components: - type: Transform - pos: 34.567924,-22.2573 + pos: 34.711796,-22.506569 parent: 1668 - proto: BoxShotgunIncendiary entities: - uid: 1311 components: - type: Transform - pos: 34.52105,-21.1323 + pos: 34.723774,-21.334171 parent: 1668 - proto: BoxShotgunSlug entities: - uid: 1335 components: - type: Transform - pos: 34.5523,-21.72605 + pos: 34.71797,-21.851793 parent: 1668 - proto: BoxShotgunUranium entities: - uid: 1334 components: - type: Transform - pos: 34.567924,-21.97605 + pos: 34.31652,-22.311989 parent: 1668 - proto: BoxZiptie entities: - - uid: 1744 + - uid: 1570 components: - type: Transform - pos: 13.5,7.5 + pos: 14.462548,8.4831295 parent: 1668 - uid: 6811 components: @@ -7846,28 +7941,63 @@ entities: - type: Transform pos: -38.7273,13.7666025 parent: 1668 -- proto: BrigTimer +- proto: BriefcaseBrown entities: - - uid: 1601 + - uid: 4559 components: - type: Transform - pos: 18.5,9.5 - parent: 1668 - - type: DeviceLinkSource - linkedPorts: - 1582: - - Start: Close - - Timer: Open - - uid: 1602 - components: - - type: Transform - pos: 8.5,9.5 - parent: 1668 - - type: DeviceLinkSource - linkedPorts: - 1581: - - Start: Close - - Timer: Open + parent: 1610 + - type: Storage + storedItems: + 4560: + position: 0,0 + _rotation: South + 4838: + position: 1,0 + _rotation: South + 7566: + position: 2,0 + _rotation: South + 9115: + position: 2,1 + _rotation: South + 9449: + position: 3,0 + _rotation: South + 9457: + position: 3,1 + _rotation: South + 9463: + position: 4,1 + _rotation: South + 9681: + position: 4,0 + _rotation: South + 9682: + position: 0,2 + _rotation: South + 9683: + position: 5,0 + _rotation: South + - type: ContainerContainer + containers: + storagebase: !type:Container + showEnts: False + occludes: True + ents: + - 4560 + - 4838 + - 7566 + - 9115 + - 9449 + - 9457 + - 9463 + - 9681 + - 9682 + - 9683 + - type: Physics + canCollide: False + - type: InsideEntityStorage - proto: Bucket entities: - uid: 1034 @@ -9231,6 +9361,41 @@ entities: - type: Transform pos: 21.5,25.5 parent: 1668 + - uid: 912 + components: + - type: Transform + pos: 7.5,7.5 + parent: 1668 + - uid: 916 + components: + - type: Transform + pos: 5.5,6.5 + parent: 1668 + - uid: 919 + components: + - type: Transform + pos: 7.5,8.5 + parent: 1668 + - uid: 921 + components: + - type: Transform + pos: 2.5,7.5 + parent: 1668 + - uid: 922 + components: + - type: Transform + pos: 4.5,6.5 + parent: 1668 + - uid: 923 + components: + - type: Transform + pos: 6.5,6.5 + parent: 1668 + - uid: 924 + components: + - type: Transform + pos: 2.5,8.5 + parent: 1668 - uid: 939 components: - type: Transform @@ -9986,16 +10151,6 @@ entities: - type: Transform pos: 4.5,10.5 parent: 1668 - - uid: 1665 - components: - - type: Transform - pos: 3.5,10.5 - parent: 1668 - - uid: 1666 - components: - - type: Transform - pos: 2.5,10.5 - parent: 1668 - uid: 1667 components: - type: Transform @@ -10036,35 +10191,15 @@ entities: - type: Transform pos: 6.5,13.5 parent: 1668 - - uid: 1676 - components: - - type: Transform - pos: 8.5,11.5 - parent: 1668 - uid: 1677 components: - type: Transform pos: 7.5,9.5 parent: 1668 - - uid: 1678 - components: - - type: Transform - pos: 6.5,9.5 - parent: 1668 - - uid: 1679 - components: - - type: Transform - pos: 5.5,9.5 - parent: 1668 - uid: 1680 components: - type: Transform - pos: 4.5,9.5 - parent: 1668 - - uid: 1681 - components: - - type: Transform - pos: 3.5,9.5 + pos: 5.5,7.5 parent: 1668 - uid: 1682 components: @@ -10276,6 +10411,11 @@ entities: - type: Transform pos: 10.5,3.5 parent: 1668 + - uid: 1738 + components: + - type: Transform + pos: -20.5,-49.5 + parent: 1668 - uid: 2679 components: - type: Transform @@ -12226,6 +12366,11 @@ entities: - type: Transform pos: -0.5,7.5 parent: 1668 + - uid: 4505 + components: + - type: Transform + pos: 19.5,-49.5 + parent: 1668 - uid: 5165 components: - type: Transform @@ -12371,11 +12516,6 @@ entities: - type: Transform pos: 19.5,-48.5 parent: 1668 - - uid: 5194 - components: - - type: Transform - pos: 20.5,-48.5 - parent: 1668 - uid: 5195 components: - type: Transform @@ -12491,11 +12631,6 @@ entities: - type: Transform pos: -20.5,-48.5 parent: 1668 - - uid: 5218 - components: - - type: Transform - pos: -21.5,-48.5 - parent: 1668 - uid: 5219 components: - type: Transform @@ -15201,6 +15336,66 @@ entities: - type: Transform pos: -0.5,-8.5 parent: 1668 + - uid: 9769 + components: + - type: Transform + pos: 5.5,8.5 + parent: 1668 + - uid: 9784 + components: + - type: Transform + pos: 5.5,9.5 + parent: 1668 + - uid: 9886 + components: + - type: Transform + pos: 23.5,-30.5 + parent: 1668 + - uid: 9984 + components: + - type: Transform + pos: 5.5,10.5 + parent: 1668 + - uid: 9985 + components: + - type: Transform + pos: 4.5,8.5 + parent: 1668 + - uid: 9986 + components: + - type: Transform + pos: 3.5,8.5 + parent: 1668 + - uid: 9987 + components: + - type: Transform + pos: 3.5,11.5 + parent: 1668 + - uid: 9995 + components: + - type: Transform + pos: 26.5,-5.5 + parent: 1668 + - uid: 9996 + components: + - type: Transform + pos: 26.5,-6.5 + parent: 1668 + - uid: 9997 + components: + - type: Transform + pos: 26.5,-7.5 + parent: 1668 + - uid: 9998 + components: + - type: Transform + pos: 26.5,-8.5 + parent: 1668 + - uid: 9999 + components: + - type: Transform + pos: 26.5,-9.5 + parent: 1668 - proto: CableApcStack entities: - uid: 6776 @@ -20029,7 +20224,7 @@ entities: - uid: 499 components: - type: Transform - pos: 1.5249417,-2.3881862 + pos: -3.5328715,-1.3873212 parent: 1668 - uid: 2812 components: @@ -20050,6 +20245,18 @@ entities: parent: 1668 - proto: Carpet entities: + - uid: 926 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 32.5,-21.5 + parent: 1668 + - uid: 929 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 33.5,-21.5 + parent: 1668 - uid: 1194 components: - type: Transform @@ -20060,16 +20267,6 @@ entities: - type: Transform pos: 32.5,-22.5 parent: 1668 - - uid: 1294 - components: - - type: Transform - pos: 32.5,-21.5 - parent: 1668 - - uid: 1295 - components: - - type: Transform - pos: 32.5,-20.5 - parent: 1668 - uid: 1976 components: - type: Transform @@ -20120,6 +20317,42 @@ entities: - type: Transform pos: -49.5,5.5 parent: 1668 + - uid: 10000 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 33.5,-22.5 + parent: 1668 + - uid: 10001 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 34.5,-22.5 + parent: 1668 + - uid: 10002 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 34.5,-21.5 + parent: 1668 + - uid: 10003 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 34.5,-20.5 + parent: 1668 + - uid: 10004 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 33.5,-20.5 + parent: 1668 + - uid: 10005 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 32.5,-20.5 + parent: 1668 - proto: CarpetBlack entities: - uid: 9650 @@ -20520,26 +20753,26 @@ entities: parent: 1668 - proto: CarpetOrange entities: + - uid: 1526 + components: + - type: Transform + pos: 31.5,12.5 + parent: 1668 + - uid: 1576 + components: + - type: Transform + pos: 33.5,12.5 + parent: 1668 + - uid: 1579 + components: + - type: Transform + pos: 33.5,11.5 + parent: 1668 - uid: 2074 components: - type: Transform pos: 31.5,11.5 parent: 1668 - - uid: 2076 - components: - - type: Transform - pos: 30.5,12.5 - parent: 1668 - - uid: 2085 - components: - - type: Transform - pos: 30.5,11.5 - parent: 1668 - - uid: 2090 - components: - - type: Transform - pos: 31.5,12.5 - parent: 1668 - uid: 2091 components: - type: Transform @@ -20696,6 +20929,11 @@ entities: parent: 1668 - proto: Catwalk entities: + - uid: 1739 + components: + - type: Transform + pos: 19.5,-49.5 + parent: 1668 - uid: 3236 components: - type: Transform @@ -20801,6 +21039,11 @@ entities: - type: Transform pos: -27.5,20.5 parent: 1668 + - uid: 4562 + components: + - type: Transform + pos: -20.5,-49.5 + parent: 1668 - uid: 4840 components: - type: Transform @@ -20816,11 +21059,6 @@ entities: - type: Transform pos: -20.5,-48.5 parent: 1668 - - uid: 4845 - components: - - type: Transform - pos: -21.5,-48.5 - parent: 1668 - uid: 4846 components: - type: Transform @@ -20836,11 +21074,6 @@ entities: - type: Transform pos: 19.5,-48.5 parent: 1668 - - uid: 4849 - components: - - type: Transform - pos: 20.5,-48.5 - parent: 1668 - uid: 6217 components: - type: Transform @@ -20966,8 +21199,10 @@ entities: - uid: 500 components: - type: Transform - pos: 1.6499417,-2.5131862 - parent: 1668 + parent: 1610 + - type: Physics + canCollide: False + - type: InsideEntityStorage - uid: 2814 components: - type: Transform @@ -20980,18 +21215,18 @@ entities: - type: Transform pos: -15.5,13.5 parent: 1668 + - uid: 2437 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-2.5 + parent: 1668 - uid: 2820 components: - type: Transform rot: 1.5707963267948966 rad pos: -38.5,12.5 parent: 1668 - - uid: 2936 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-2.5 - parent: 1668 - uid: 6006 components: - type: Transform @@ -21003,13 +21238,10 @@ entities: - uid: 22 components: - type: Transform - pos: 1.5543553,-2.2420077 - parent: 1668 - - uid: 935 - components: - - type: Transform - pos: 1.5543553,-2.2420077 - parent: 1668 + parent: 1610 + - type: Physics + canCollide: False + - type: InsideEntityStorage - uid: 2817 components: - type: Transform @@ -21256,6 +21488,12 @@ entities: rot: -1.5707963267948966 rad pos: 28.5,12.5 parent: 1668 + - uid: 2121 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 4.5,7.5 + parent: 1668 - uid: 2122 components: - type: Transform @@ -21446,23 +21684,23 @@ entities: rot: -1.5707963267948966 rad pos: 33.5,-10.5 parent: 1668 + - uid: 1528 + components: + - type: Transform + pos: 33.5,12.5 + parent: 1668 + - uid: 1549 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 31.5,11.5 + parent: 1668 - uid: 1621 components: - type: Transform rot: 3.141592653589793 rad pos: 13.5,12.5 parent: 1668 - - uid: 2075 - components: - - type: Transform - pos: 32.5,12.5 - parent: 1668 - - uid: 2121 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 30.570415,11.596919 - parent: 1668 - uid: 2895 components: - type: Transform @@ -22528,6 +22766,13 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage + - uid: 9686 + components: + - type: Transform + parent: 1610 + - type: Physics + canCollide: False + - type: InsideEntityStorage - proto: ClothingMaskGasDeathSquad entities: - uid: 2469 @@ -22674,6 +22919,15 @@ entities: - type: Transform pos: -3.5,-31.5 parent: 1668 +- proto: ClothingNeckScarfStripedCentcom + entities: + - uid: 4523 + components: + - type: Transform + parent: 1610 + - type: Physics + canCollide: False + - type: InsideEntityStorage - proto: ClothingOuterArmorCaptainCarapace entities: - uid: 1101 @@ -22681,6 +22935,15 @@ entities: - type: Transform pos: -11.664687,7.5583878 parent: 1668 +- proto: ClothingOuterArmorCentcommCarapace + entities: + - uid: 9685 + components: + - type: Transform + parent: 1610 + - type: Physics + canCollide: False + - type: InsideEntityStorage - proto: ClothingOuterArmorHeavyRed entities: - uid: 4640 @@ -22977,6 +23240,15 @@ entities: - type: Transform pos: -22.480219,-6.422328 parent: 1668 +- proto: ClothingUniformJumpskirtCentcomFormalDress + entities: + - uid: 1750 + components: + - type: Transform + parent: 1610 + - type: Physics + canCollide: False + - type: InsideEntityStorage - proto: ClothingUniformJumpskirtHoSParadeMale entities: - uid: 3242 @@ -22992,6 +23264,15 @@ entities: - type: Transform pos: -8.014507,-17.209332 parent: 1668 +- proto: ClothingUniformJumpsuitCentcomFormal + entities: + - uid: 1737 + components: + - type: Transform + parent: 1610 + - type: Physics + canCollide: False + - type: InsideEntityStorage - proto: ClothingUniformJumpsuitDeathSquad entities: - uid: 2467 @@ -23357,14 +23638,6 @@ entities: - type: Transform pos: -2.5,32.5 parent: 1668 -- proto: ComputerCargoShuttle - entities: - - uid: 3907 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 4.5,23.5 - parent: 1668 - proto: ComputerCloningConsole entities: - uid: 732 @@ -23375,9 +23648,11 @@ entities: - type: DeviceLinkSource linkedPorts: 734: - - MedicalScannerSender: MedicalScannerReceiver + - - MedicalScannerSender + - MedicalScannerReceiver 733: - - CloningPodSender: CloningPodReceiver + - - CloningPodSender + - CloningPodReceiver - proto: ComputerComms entities: - uid: 2821 @@ -23392,6 +23667,14 @@ entities: rot: 3.141592653589793 rad pos: -43.5,-8.5 parent: 1668 +- proto: ComputerCrewMonitoring + entities: + - uid: 2438 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-2.5 + parent: 1668 - proto: ComputerCriminalRecords entities: - uid: 363 @@ -23399,9 +23682,10 @@ entities: - type: Transform pos: 14.5,4.5 parent: 1668 - - uid: 1620 + - uid: 1538 components: - type: Transform + rot: -1.5707963267948966 rad pos: 14.5,12.5 parent: 1668 - proto: ComputerId @@ -23439,6 +23723,12 @@ entities: - type: Transform pos: 3.5,32.5 parent: 1668 + - uid: 3907 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 4.5,23.5 + parent: 1668 - proto: ComputerSurveillanceCameraMonitor entities: - uid: 8816 @@ -23461,11 +23751,29 @@ entities: parent: 1668 - proto: ConveyorBelt entities: + - uid: 1600 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 23.5,10.5 + parent: 1668 + - uid: 1624 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 24.5,12.5 + parent: 1668 + - uid: 1625 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 24.5,11.5 + parent: 1668 - uid: 1626 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 23.5,12.5 + rot: 1.5707963267948966 rad + pos: 24.5,10.5 parent: 1668 - uid: 1627 components: @@ -23475,12 +23783,34 @@ entities: parent: 1668 - type: DeviceLinkSink invokeCounter: 1 + - uid: 1744 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 22.5,12.5 + parent: 1668 + - uid: 1751 + components: + - type: Transform + pos: 22.5,11.5 + parent: 1668 + - uid: 2085 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 23.5,12.5 + parent: 1668 - uid: 3264 components: - type: Transform rot: 3.141592653589793 rad pos: -0.5,30.5 parent: 1668 + - uid: 3520 + components: + - type: Transform + pos: 22.5,10.5 + parent: 1668 - uid: 3638 components: - type: Transform @@ -23644,6 +23974,20 @@ entities: - type: Transform pos: -23.5,-4.5 parent: 1668 +- proto: CrayonBox + entities: + - uid: 8183 + components: + - type: Transform + pos: 6.515189,7.596311 + parent: 1668 +- proto: CrewMonitoringServer + entities: + - uid: 8793 + components: + - type: Transform + pos: -10.5,-6.5 + parent: 1668 - proto: CrowbarGreen entities: - uid: 3173 @@ -23712,6 +24056,36 @@ entities: - type: Transform pos: -30.541765,-7.579217 parent: 1668 +- proto: CryogenicSleepUnit + entities: + - uid: 9981 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 3.5,8.5 + parent: 1668 + - uid: 9990 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 25.5,-8.5 + parent: 1668 + - uid: 9991 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 25.5,-9.5 + parent: 1668 + - uid: 9992 + components: + - type: Transform + pos: 27.5,-8.5 + parent: 1668 + - uid: 9993 + components: + - type: Transform + pos: 27.5,-9.5 + parent: 1668 - proto: CryoPod entities: - uid: 677 @@ -23904,6 +24278,15 @@ entities: - type: Transform pos: 9.760128,-23.47621 parent: 1668 +- proto: DeathRattleImplanterCentcomm + entities: + - uid: 4544 + components: + - type: Transform + parent: 1610 + - type: Physics + canCollide: False + - type: InsideEntityStorage - proto: DeathsquadPDA entities: - uid: 2470 @@ -23956,19 +24339,84 @@ entities: - type: Transform pos: -12.5,5.5 parent: 1668 -- proto: DefaultStationBeaconCentComm +- proto: DefaultStationBeaconAnchor entities: - - uid: 4656 + - uid: 9928 components: - type: Transform - pos: -0.5,3.5 + pos: -11.5,26.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconArmory + entities: + - uid: 9910 + components: + - type: Transform + pos: 13.5,22.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconArrivals + entities: + - uid: 9977 + components: + - type: Transform + pos: 26.5,-0.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconAtmospherics + entities: + - uid: 9908 + components: + - type: Transform + pos: -19.5,21.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconBar + entities: + - uid: 9914 + components: + - type: Transform + pos: 8.5,-33.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconBotany + entities: + - uid: 9918 + components: + - type: Transform + pos: 6.5,-21.5 + parent: 1668 + - type: NavMapBeacon + text: Botany + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconCargoBay + entities: + - uid: 9909 + components: + - type: Transform + pos: 3.5,27.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconCentComm + entities: + - uid: 9119 + components: + - type: Transform + pos: -0.5,-0.5 parent: 1668 - proto: DefaultStationBeaconCentCommAfterhours entities: - uid: 9452 components: - type: Transform - pos: 22.5,-21.5 + pos: 27.5,-21.5 parent: 1668 - proto: DefaultStationBeaconCentCommERT entities: @@ -23984,6 +24432,125 @@ entities: - type: Transform pos: -0.5,-41.5 parent: 1668 +- proto: DefaultStationBeaconChemistry + entities: + - uid: 9919 + components: + - type: Transform + pos: 7.5,-10.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconCourtroom + entities: + - uid: 9911 + components: + - type: Transform + pos: 26.5,17.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconEngineering + entities: + - uid: 9907 + components: + - type: Transform + pos: -6.5,10.5 + parent: 1668 + - type: NavMapBeacon + text: Engineering + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconGravGen + entities: + - uid: 9927 + components: + - type: Transform + pos: -5.5,26.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconJanitorsCloset + entities: + - uid: 9923 + components: + - type: Transform + pos: -5.5,-10.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconKitchen + entities: + - uid: 9915 + components: + - type: Transform + pos: -9.5,-33.5 + parent: 1668 + missingComponents: + - WarpPoint + - uid: 9917 + components: + - type: Transform + pos: 15.5,-17.5 + parent: 1668 + - type: NavMapBeacon + text: Kitchen + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconLawOffice + entities: + - uid: 9922 + components: + - type: Transform + pos: 29.5,11.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconMedbay + entities: + - uid: 9920 + components: + - type: Transform + pos: 18.5,-10.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconScience + entities: + - uid: 9924 + components: + - type: Transform + pos: 21.5,-30.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconSecurity + entities: + - uid: 9912 + components: + - type: Transform + pos: 13.5,9.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconSecurityCheckpoint + entities: + - uid: 9925 + components: + - type: Transform + pos: 33.5,-11.5 + parent: 1668 + missingComponents: + - WarpPoint +- proto: DefaultStationBeaconTelecoms + entities: + - uid: 9921 + components: + - type: Transform + pos: -12.5,-7.5 + parent: 1668 + missingComponents: + - WarpPoint - proto: DefibrillatorCabinetFilled entities: - uid: 511 @@ -26314,10 +26881,10 @@ entities: parent: 1668 - proto: DrinkMugMetal entities: - - uid: 2088 + - uid: 905 components: - type: Transform - pos: 31.480066,11.690669 + pos: 32.55041,11.7296295 parent: 1668 - proto: DrinkShaker entities: @@ -26489,6 +27056,36 @@ entities: - type: Transform pos: 13.91264,32.80469 parent: 1668 + - uid: 2936 + components: + - type: Transform + pos: 17.020287,35.027782 + parent: 1668 + - uid: 6390 + components: + - type: Transform + pos: 16.71473,34.59847 + parent: 1668 + - uid: 9973 + components: + - type: Transform + pos: 17.020287,34.79305 + parent: 1668 + - uid: 9974 + components: + - type: Transform + pos: 16.702385,35.043224 + parent: 1668 + - uid: 9975 + components: + - type: Transform + pos: 16.699299,34.827023 + parent: 1668 + - uid: 9976 + components: + - type: Transform + pos: 17.020287,34.57685 + parent: 1668 - proto: ERTEngineerPDA entities: - uid: 6784 @@ -26676,24 +27273,12 @@ entities: rot: -1.5707963267948966 rad pos: -22.5,-11.5 parent: 1668 -- proto: filingCabinetDrawerRandom - entities: - - uid: 498 - components: - - type: Transform - pos: 0.5,-2.5 - parent: 1668 - - uid: 2084 - components: - - type: Transform - pos: 25.5,10.5 - parent: 1668 - proto: filingCabinetRandom entities: - - uid: 2082 + - uid: 1537 components: - type: Transform - pos: 30.5,12.5 + pos: 31.5,12.5 parent: 1668 - uid: 6725 components: @@ -26897,11 +27482,6 @@ entities: parent: 1668 - proto: FlashlightSeclite entities: - - uid: 3613 - components: - - type: Transform - pos: 15.5,7.5 - parent: 1668 - uid: 6756 components: - type: Transform @@ -27222,6 +27802,18 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: FoodPlateSmall + entities: + - uid: 1198 + components: + - type: Transform + pos: 6.4804764,10.508464 + parent: 1668 + - uid: 4186 + components: + - type: Transform + pos: 6.4908934,9.705824 + parent: 1668 - proto: FoodPoppy entities: - uid: 1709 @@ -27243,6 +27835,20 @@ entities: - type: Transform pos: 21.580595,-15.908045 parent: 1668 +- proto: ForkPlastic + entities: + - uid: 1356 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 6.51475,9.705824 + parent: 1668 + - uid: 2093 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 6.5043335,10.518888 + parent: 1668 - proto: FreedomImplanter entities: - uid: 2473 @@ -28192,6 +28798,14 @@ entities: color: '#0335FCFF' - proto: GasPipeStraight entities: + - uid: 498 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -0.5,3.5 + parent: 1668 + - type: AtmosPipeColor + color: '#FF1212FF' - uid: 695 components: - type: Transform @@ -28208,6 +28822,22 @@ entities: - type: Transform pos: 15.5,-9.5 parent: 1668 + - uid: 1357 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 21.5,10.5 + parent: 1668 + - type: AtmosPipeColor + color: '#FF1212FF' + - uid: 1748 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 21.5,12.5 + parent: 1668 + - type: AtmosPipeColor + color: '#0335FCFF' - uid: 3481 components: - type: Transform @@ -28890,14 +29520,6 @@ entities: parent: 1668 - type: AtmosPipeColor color: '#FF1212FF' - - uid: 7566 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,3.5 - parent: 1668 - - type: AtmosPipeColor - color: '#FF1212FF' - uid: 7568 components: - type: Transform @@ -32074,13 +32696,6 @@ entities: parent: 1668 - type: AtmosPipeColor color: '#0335FCFF' - - uid: 8046 - components: - - type: Transform - pos: 9.5,7.5 - parent: 1668 - - type: AtmosPipeColor - color: '#0335FCFF' - uid: 8047 components: - type: Transform @@ -32286,14 +32901,6 @@ entities: parent: 1668 - type: AtmosPipeColor color: '#FF1212FF' - - uid: 8077 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 8.5,10.5 - parent: 1668 - - type: AtmosPipeColor - color: '#FF1212FF' - uid: 8078 components: - type: Transform @@ -36955,6 +37562,21 @@ entities: rot: 1.5707963267948966 rad pos: 17.5,-9.5 parent: 1668 + - uid: 1571 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 9.5,7.5 + parent: 1668 + - type: AtmosPipeColor + color: '#0335FCFF' + - uid: 1575 + components: + - type: Transform + pos: 8.5,10.5 + parent: 1668 + - type: AtmosPipeColor + color: '#FF1212FF' - uid: 1734 components: - type: Transform @@ -38093,6 +38715,8 @@ entities: - type: Transform pos: 12.5,-21.5 parent: 1668 + - type: GasThermoMachine + targetTemperature: 73.15 - proto: GasThermoMachineFreezerEnabled entities: - uid: 683 @@ -38100,8 +38724,26 @@ entities: - type: Transform pos: 18.5,-8.5 parent: 1668 + - type: GasThermoMachine + targetTemperature: 73.15 - proto: GasVentPump entities: + - uid: 3666 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 22.5,12.5 + parent: 1668 + - type: AtmosPipeColor + color: '#0335FCFF' + - uid: 4185 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 8.5,7.5 + parent: 1668 + - type: AtmosPipeColor + color: '#0335FCFF' - uid: 7547 components: - type: Transform @@ -38244,14 +38886,6 @@ entities: parent: 1668 - type: AtmosPipeColor color: '#0335FCFF' - - uid: 8186 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 21.5,12.5 - parent: 1668 - - type: AtmosPipeColor - color: '#0335FCFF' - uid: 8330 components: - type: Transform @@ -38683,6 +39317,22 @@ entities: color: '#0335FCFF' - proto: GasVentScrubber entities: + - uid: 1742 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 8.5,9.5 + parent: 1668 + - type: AtmosPipeColor + color: '#FF1212FF' + - uid: 3670 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 22.5,10.5 + parent: 1668 + - type: AtmosPipeColor + color: '#FF1212FF' - uid: 7507 components: - type: Transform @@ -38805,14 +39455,6 @@ entities: parent: 1668 - type: AtmosPipeColor color: '#FF1212FF' - - uid: 8183 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 21.5,10.5 - parent: 1668 - - type: AtmosPipeColor - color: '#FF1212FF' - uid: 8184 components: - type: Transform @@ -39475,6 +40117,11 @@ entities: - type: Transform pos: 29.5,-14.5 parent: 1668 + - uid: 94 + components: + - type: Transform + pos: 23.5,-29.5 + parent: 1668 - uid: 132 components: - type: Transform @@ -39490,6 +40137,11 @@ entities: - type: Transform pos: 28.5,7.5 parent: 1668 + - uid: 149 + components: + - type: Transform + pos: 23.5,-30.5 + parent: 1668 - uid: 154 components: - type: Transform @@ -39660,11 +40312,6 @@ entities: - type: Transform pos: 28.5,-11.5 parent: 1668 - - uid: 912 - components: - - type: Transform - pos: 28.5,-10.5 - parent: 1668 - uid: 913 components: - type: Transform @@ -39680,11 +40327,6 @@ entities: - type: Transform pos: 24.5,-11.5 parent: 1668 - - uid: 916 - components: - - type: Transform - pos: 24.5,-10.5 - parent: 1668 - uid: 917 components: - type: Transform @@ -39695,16 +40337,16 @@ entities: - type: Transform pos: 25.5,-7.5 parent: 1668 - - uid: 919 - components: - - type: Transform - pos: 26.5,-7.5 - parent: 1668 - uid: 920 components: - type: Transform pos: 27.5,-7.5 parent: 1668 + - uid: 935 + components: + - type: Transform + pos: 23.5,-33.5 + parent: 1668 - uid: 1102 components: - type: Transform @@ -39760,41 +40402,16 @@ entities: - type: Transform pos: 27.5,-28.5 parent: 1668 - - uid: 1350 - components: - - type: Transform - pos: 26.5,-28.5 - parent: 1668 - uid: 1351 components: - type: Transform pos: 25.5,-28.5 parent: 1668 - - uid: 1525 - components: - - type: Transform - pos: 3.5,9.5 - parent: 1668 - - uid: 1527 - components: - - type: Transform - pos: 6.5,9.5 - parent: 1668 - - uid: 1528 - components: - - type: Transform - pos: 5.5,9.5 - parent: 1668 - uid: 1529 components: - type: Transform pos: 2.5,7.5 parent: 1668 - - uid: 1530 - components: - - type: Transform - pos: 4.5,9.5 - parent: 1668 - uid: 1531 components: - type: Transform @@ -39830,16 +40447,6 @@ entities: - type: Transform pos: 6.5,13.5 parent: 1668 - - uid: 1549 - components: - - type: Transform - pos: 7.5,9.5 - parent: 1668 - - uid: 1551 - components: - - type: Transform - pos: 2.5,10.5 - parent: 1668 - uid: 1552 components: - type: Transform @@ -39850,6 +40457,11 @@ entities: - type: Transform pos: 2.5,12.5 parent: 1668 + - uid: 1554 + components: + - type: Transform + pos: 23.5,-32.5 + parent: 1668 - uid: 1564 components: - type: Transform @@ -39880,15 +40492,22 @@ entities: - type: Transform pos: 4.5,13.5 parent: 1668 - - uid: 1570 + - uid: 1580 components: - type: Transform - pos: 18.5,11.5 + pos: 23.5,-31.5 parent: 1668 - - uid: 1571 + - uid: 1588 components: - type: Transform - pos: 8.5,11.5 + rot: 1.5707963267948966 rad + pos: 7.5,7.5 + parent: 1668 + - uid: 1601 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 11.5,12.5 parent: 1668 - uid: 1612 components: @@ -39900,15 +40519,23 @@ entities: - type: Transform pos: 15.5,13.5 parent: 1668 - - uid: 1614 + - uid: 1684 components: - type: Transform - pos: 11.5,13.5 + rot: 1.5707963267948966 rad + pos: 7.5,12.5 parent: 1668 - - uid: 1615 + - uid: 1746 components: - type: Transform - pos: 9.5,13.5 + rot: 1.5707963267948966 rad + pos: 7.5,10.5 + parent: 1668 + - uid: 1747 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 11.5,10.5 parent: 1668 - uid: 1879 components: @@ -39970,6 +40597,18 @@ entities: - type: Transform pos: 35.5,12.5 parent: 1668 + - uid: 2071 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 10.5,13.5 + parent: 1668 + - uid: 2077 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 11.5,11.5 + parent: 1668 - uid: 2144 components: - type: Transform @@ -40250,6 +40889,12 @@ entities: - type: Transform pos: -11.5,-3.5 parent: 1668 + - uid: 2700 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 7.5,9.5 + parent: 1668 - uid: 2730 components: - type: Transform @@ -41489,31 +42134,6 @@ entities: - type: Transform pos: 23.5,-34.5 parent: 1668 - - uid: 9115 - components: - - type: Transform - pos: 23.5,-33.5 - parent: 1668 - - uid: 9116 - components: - - type: Transform - pos: 23.5,-32.5 - parent: 1668 - - uid: 9117 - components: - - type: Transform - pos: 23.5,-31.5 - parent: 1668 - - uid: 9118 - components: - - type: Transform - pos: 23.5,-30.5 - parent: 1668 - - uid: 9119 - components: - - type: Transform - pos: 23.5,-29.5 - parent: 1668 - uid: 9121 components: - type: Transform @@ -41615,6 +42235,12 @@ entities: - type: Transform pos: -22.524752,13.470394 parent: 1668 + - uid: 9683 + components: + - type: Transform + parent: 4559 + - type: Physics + canCollide: False - proto: HandheldCrewMonitor entities: - uid: 904 @@ -41664,16 +42290,16 @@ entities: parent: 1668 - proto: HighSecCommandLocked entities: - - uid: 94 - components: - - type: Transform - pos: 3.5,0.5 - parent: 1668 - - uid: 328 + - uid: 1583 components: - type: Transform pos: -4.5,0.5 parent: 1668 + - uid: 1584 + components: + - type: Transform + pos: 3.5,0.5 + parent: 1668 - proto: HolofanProjector entities: - uid: 4088 @@ -41977,6 +42603,20 @@ entities: - type: Transform pos: 16.5,-21.5 parent: 1668 +- proto: KnifePlastic + entities: + - uid: 1350 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 6.57725,9.549465 + parent: 1668 + - uid: 9830 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 6.5355835,10.36253 + parent: 1668 - proto: Lamp entities: - uid: 527 @@ -41984,10 +42624,10 @@ entities: - type: Transform pos: -1.4329706,1.8655163 parent: 1668 - - uid: 2081 + - uid: 2089 components: - type: Transform - pos: 31.448816,12.768794 + pos: 32.48791,12.751173 parent: 1668 - uid: 2811 components: @@ -42109,31 +42749,49 @@ entities: - type: Transform pos: -33.5,-4.5 parent: 1668 + - type: EntityStorage + air: + volume: 200 + immutable: False + temperature: 293.14673 + moles: + - 1.7459903 + - 6.568249 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 - type: ContainerContainer containers: entity_storage: !type:Container showEnts: False occludes: True ents: - - 6780 - 6779 - - 6778 - - 6777 - - 6776 - - 6775 - - 6774 - - 6773 - - 6772 - - 6771 - - 6781 - - 6782 - - 6783 - - 6784 - - 6785 - - 6786 - - 6787 - - 6788 - 6789 + - 6788 + - 6787 + - 6786 + - 6785 + - 6784 + - 6783 + - 6782 + - 6781 + - 6771 + - 6772 + - 6773 + - 6774 + - 6775 + - 6776 + - 6777 + - 6778 + - 6780 paper_label: !type:ContainerSlot showEnts: False occludes: True @@ -42145,22 +42803,37 @@ entities: - type: Transform pos: -32.5,-4.5 parent: 1668 + - type: EntityStorage + air: + volume: 200 + immutable: False + temperature: 293.14673 + moles: + - 1.7459903 + - 6.568249 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 - type: ContainerContainer containers: entity_storage: !type:Container showEnts: False occludes: True ents: + - 6793 - 6800 - - 6799 - 6798 - 6797 - 6796 - - 6795 - 6794 - - 6793 - 6792 - - 6791 - 6801 - 6802 - 6803 @@ -42170,6 +42843,9 @@ entities: - 6807 - 6808 - 6809 + - 6799 + - 6791 + - 6795 paper_label: !type:ContainerSlot showEnts: False occludes: True @@ -42183,30 +42859,48 @@ entities: - type: Transform pos: -33.5,-10.5 parent: 1668 + - type: EntityStorage + air: + volume: 200 + immutable: False + temperature: 293.14673 + moles: + - 1.7459903 + - 6.568249 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 - type: ContainerContainer containers: entity_storage: !type:Container showEnts: False occludes: True ents: - - 6855 - 6854 - - 6853 - - 6852 - - 6851 - - 6850 - - 6849 - - 6848 - - 6847 - - 6856 - - 6857 - - 6858 - - 6859 - - 6860 - - 6861 - - 6862 - - 6863 - 6864 + - 6863 + - 6862 + - 6861 + - 6860 + - 6859 + - 6858 + - 6857 + - 6856 + - 6847 + - 6848 + - 6849 + - 6850 + - 6851 + - 6852 + - 6853 + - 6855 paper_label: !type:ContainerSlot showEnts: False occludes: True @@ -42260,18 +42954,6 @@ entities: - type: Transform pos: -15.5,23.5 parent: 1668 -- proto: LockerEvidence - entities: - - uid: 1356 - components: - - type: Transform - pos: 11.5,12.5 - parent: 1668 - - uid: 1357 - components: - - type: Transform - pos: 15.5,12.5 - parent: 1668 - proto: LockerFreezer entities: - uid: 1183 @@ -42470,8 +43152,79 @@ entities: showEnts: False occludes: True ent: null +- proto: LockerPrisoner + entities: + - uid: 1620 + components: + - type: Transform + pos: 10.5,12.5 + parent: 1668 +- proto: LockerPrisoner2 + entities: + - uid: 1623 + components: + - type: Transform + pos: 10.5,11.5 + parent: 1668 +- proto: LockerPrisoner3 + entities: + - uid: 1628 + components: + - type: Transform + pos: 10.5,10.5 + parent: 1668 +- proto: LockerPrisoner4 + entities: + - uid: 1730 + components: + - type: Transform + pos: 10.5,9.5 + parent: 1668 - proto: LockerRepresentative entities: + - uid: 1610 + components: + - type: Transform + pos: 1.5,-2.5 + parent: 1668 + - type: EntityStorage + air: + volume: 200 + immutable: False + temperature: 293.14673 + moles: + - 1.7459903 + - 6.568249 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - type: ContainerContainer + containers: + entity_storage: !type:Container + showEnts: False + occludes: True + ents: + - 9685 + - 4559 + - 4544 + - 4541 + - 4523 + - 22 + - 1750 + - 1737 + - 500 + - 9686 + paper_label: !type:ContainerSlot + showEnts: False + occludes: True + ent: null - uid: 6884 components: - type: MetaData @@ -42479,30 +43232,48 @@ entities: - type: Transform pos: -28.5,-4.5 parent: 1668 + - type: EntityStorage + air: + volume: 200 + immutable: False + temperature: 293.14673 + moles: + - 1.7459903 + - 6.568249 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 - type: ContainerContainer containers: entity_storage: !type:Container showEnts: False occludes: True ents: - - 6902 - 6901 - - 6900 - - 6899 - - 6898 - - 6897 - - 6896 - - 6895 - - 6894 - - 6893 - - 6892 - - 6891 - - 6890 - - 6889 - - 6888 - - 6887 - - 6886 - 6885 + - 6886 + - 6887 + - 6888 + - 6889 + - 6890 + - 6891 + - 6892 + - 6893 + - 6894 + - 6895 + - 6896 + - 6897 + - 6898 + - 6899 + - 6900 + - 6902 paper_label: !type:ContainerSlot showEnts: False occludes: True @@ -42985,6 +43756,11 @@ entities: - type: Transform pos: -39.5,-6.5 parent: 1668 + - uid: 9957 + components: + - type: Transform + pos: 32.31388,-22.19726 + parent: 1668 - proto: MedkitOxygenFilled entities: - uid: 774 @@ -43036,6 +43812,11 @@ entities: - type: Transform pos: 16.557764,-13.5264435 parent: 1668 + - uid: 9959 + components: + - type: Transform + pos: 32.68194,-22.523876 + parent: 1668 - proto: MetalFoamGrenade entities: - uid: 2418 @@ -43317,25 +44098,73 @@ entities: parent: 1668 - proto: Paper entities: - - uid: 1748 + - uid: 4617 components: - type: Transform - pos: 7.501362,11.6733 - parent: 1668 - - uid: 1749 + parent: 4560 + - type: Physics + canCollide: False + - uid: 4618 components: - type: Transform - pos: 7.501362,11.6733 - parent: 1668 - - uid: 1750 + parent: 4560 + - type: Physics + canCollide: False + - uid: 4619 components: - type: Transform - pos: 19.535091,11.6733 - parent: 1668 - - uid: 1751 + parent: 4560 + - type: Physics + canCollide: False + - uid: 4620 components: - type: Transform - pos: 19.535091,11.6733 + parent: 4560 + - type: Physics + canCollide: False + - uid: 4656 + components: + - type: Transform + parent: 4560 + - type: Physics + canCollide: False + - uid: 9115 + components: + - type: Transform + parent: 4559 + - type: Physics + canCollide: False + - uid: 9449 + components: + - type: Transform + parent: 4559 + - type: Physics + canCollide: False + - uid: 9457 + components: + - type: Transform + parent: 4559 + - type: Physics + canCollide: False + - uid: 9463 + components: + - type: Transform + parent: 4559 + - type: Physics + canCollide: False + - uid: 9681 + components: + - type: Transform + parent: 4559 + - type: Physics + canCollide: False +- proto: PaperBin20 + entities: + - uid: 8186 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 3.5,7.5 parent: 1668 - proto: PaperBin5 entities: @@ -43392,15 +44221,15 @@ entities: parent: 1668 - proto: Pen entities: - - uid: 1746 + - uid: 9982 components: - type: Transform - pos: 19.5,11.5 + pos: 3.2651894,7.4295287 parent: 1668 - - uid: 1747 + - uid: 9983 components: - type: Transform - pos: 7.5,11.5 + pos: 3.806856,7.6866517 parent: 1668 - proto: PenCentcom entities: @@ -43414,6 +44243,12 @@ entities: - type: Transform pos: -17.37441,12.973077 parent: 1668 + - uid: 7566 + components: + - type: Transform + parent: 4559 + - type: Physics + canCollide: False - uid: 9080 components: - type: Transform @@ -43708,6 +44543,8 @@ entities: - type: Transform pos: -0.5,-2.5 parent: 1668 + missingComponents: + - WarpPoint - proto: PlushieGhost entities: - uid: 6072 @@ -44851,6 +45688,12 @@ entities: rot: 1.5707963267948966 rad pos: 19.5,-21.5 parent: 1668 + - uid: 1665 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 34.5,-11.5 + parent: 1668 - uid: 3990 components: - type: Transform @@ -45029,6 +45872,17 @@ entities: - type: Transform pos: 17.5,-15.5 parent: 1668 + - uid: 1524 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 17.5,7.5 + parent: 1668 + - uid: 1631 + components: + - type: Transform + pos: 8.5,12.5 + parent: 1668 - uid: 1633 components: - type: Transform @@ -45045,11 +45899,11 @@ entities: rot: 1.5707963267948966 rad pos: 19.5,7.5 parent: 1668 - - uid: 1733 + - uid: 2090 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 17.5,9.5 + rot: 3.141592653589793 rad + pos: 8.5,7.5 parent: 1668 - uid: 2098 components: @@ -45115,11 +45969,6 @@ entities: rot: 3.141592653589793 rad pos: 10.5,-23.5 parent: 1668 - - uid: 2700 - components: - - type: Transform - pos: 8.5,8.5 - parent: 1668 - uid: 2737 components: - type: Transform @@ -45791,12 +46640,6 @@ entities: rot: 1.5707963267948966 rad pos: 12.5,-22.5 parent: 1668 - - uid: 1156 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 34.5,-11.5 - parent: 1668 - uid: 2441 components: - type: Transform @@ -45856,6 +46699,11 @@ entities: - type: Transform pos: -23.5,13.5 parent: 1668 + - uid: 2982 + components: + - type: Transform + pos: 19.5,-48.5 + parent: 1668 - uid: 3359 components: - type: Transform @@ -45938,15 +46786,10 @@ entities: rot: -1.5707963267948966 rad pos: -9.5,-36.5 parent: 1668 - - uid: 4838 + - uid: 5194 components: - type: Transform - pos: 18.5,-48.5 - parent: 1668 - - uid: 4839 - components: - - type: Transform - pos: -19.5,-48.5 + pos: -20.5,-48.5 parent: 1668 - uid: 7184 components: @@ -46028,23 +46871,29 @@ entities: rot: 1.5707963267948966 rad pos: 32.5,-21.5 parent: 1668 + - uid: 1294 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 29.5,-10.5 + parent: 1668 - uid: 2100 components: - type: Transform pos: 34.5,12.5 parent: 1668 - - uid: 2923 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 25.5,11.5 - parent: 1668 - uid: 2926 components: - type: Transform rot: 1.5707963267948966 rad pos: -13.5,11.5 parent: 1668 + - uid: 3613 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 26.5,11.5 + parent: 1668 - uid: 3908 components: - type: Transform @@ -46269,17 +47118,23 @@ entities: - type: InsideEntityStorage - proto: Railing entities: - - uid: 3037 + - uid: 2075 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 7.5,7.5 + rot: -1.5707963267948966 rad + pos: 19.5,10.5 parent: 1668 - - uid: 3670 + - uid: 3721 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 7.5,8.5 + rot: -1.5707963267948966 rad + pos: 19.5,12.5 + parent: 1668 + - uid: 8077 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 19.5,11.5 parent: 1668 - proto: RandomDrinkBottle entities: @@ -46507,6 +47362,11 @@ entities: - type: Transform pos: -2.5,-3.5 parent: 1668 + - uid: 82 + components: + - type: Transform + pos: 11.5,10.5 + parent: 1668 - uid: 86 components: - type: Transform @@ -46587,6 +47447,11 @@ entities: - type: Transform pos: 16.5,2.5 parent: 1668 + - uid: 328 + components: + - type: Transform + pos: 23.5,-29.5 + parent: 1668 - uid: 348 components: - type: Transform @@ -46662,36 +47527,11 @@ entities: - type: Transform pos: 20.5,-7.5 parent: 1668 - - uid: 921 - components: - - type: Transform - pos: 28.5,-12.5 - parent: 1668 - - uid: 922 - components: - - type: Transform - pos: 28.5,-11.5 - parent: 1668 - - uid: 923 - components: - - type: Transform - pos: 28.5,-10.5 - parent: 1668 - - uid: 924 - components: - - type: Transform - pos: 28.5,-9.5 - parent: 1668 - uid: 925 components: - type: Transform pos: 27.5,-7.5 parent: 1668 - - uid: 926 - components: - - type: Transform - pos: 26.5,-7.5 - parent: 1668 - uid: 927 components: - type: Transform @@ -46702,11 +47542,6 @@ entities: - type: Transform pos: 24.5,-9.5 parent: 1668 - - uid: 929 - components: - - type: Transform - pos: 24.5,-10.5 - parent: 1668 - uid: 930 components: - type: Transform @@ -46742,11 +47577,6 @@ entities: - type: Transform pos: 35.5,-16.5 parent: 1668 - - uid: 1198 - components: - - type: Transform - pos: 26.5,-28.5 - parent: 1668 - uid: 1199 components: - type: Transform @@ -46792,26 +47622,6 @@ entities: - type: Transform pos: 2.5,7.5 parent: 1668 - - uid: 1524 - components: - - type: Transform - pos: 6.5,9.5 - parent: 1668 - - uid: 1526 - components: - - type: Transform - pos: 5.5,9.5 - parent: 1668 - - uid: 1537 - components: - - type: Transform - pos: 4.5,9.5 - parent: 1668 - - uid: 1538 - components: - - type: Transform - pos: 3.5,9.5 - parent: 1668 - uid: 1539 components: - type: Transform @@ -46822,11 +47632,6 @@ entities: - type: Transform pos: 22.5,9.5 parent: 1668 - - uid: 1554 - components: - - type: Transform - pos: 2.5,10.5 - parent: 1668 - uid: 1555 components: - type: Transform @@ -46847,11 +47652,6 @@ entities: - type: Transform pos: 19.5,9.5 parent: 1668 - - uid: 1559 - components: - - type: Transform - pos: 18.5,11.5 - parent: 1668 - uid: 1560 components: - type: Transform @@ -46872,16 +47672,6 @@ entities: - type: Transform pos: 22.5,13.5 parent: 1668 - - uid: 1575 - components: - - type: Transform - pos: 7.5,9.5 - parent: 1668 - - uid: 1576 - components: - - type: Transform - pos: 8.5,11.5 - parent: 1668 - uid: 1577 components: - type: Transform @@ -46892,6 +47682,43 @@ entities: - type: Transform pos: 4.5,13.5 parent: 1668 + - uid: 1581 + components: + - type: Transform + pos: 23.5,-32.5 + parent: 1668 + - uid: 1582 + components: + - type: Transform + pos: 23.5,-30.5 + parent: 1668 + - uid: 1585 + components: + - type: Transform + pos: 23.5,-31.5 + parent: 1668 + - uid: 1586 + components: + - type: Transform + pos: 23.5,-33.5 + parent: 1668 + - uid: 1590 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 7.5,10.5 + parent: 1668 + - uid: 1593 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 10.5,13.5 + parent: 1668 + - uid: 1605 + components: + - type: Transform + pos: 11.5,12.5 + parent: 1668 - uid: 1608 components: - type: Transform @@ -46902,30 +47729,22 @@ entities: - type: Transform pos: 15.5,13.5 parent: 1668 - - uid: 1610 + - uid: 1685 components: - type: Transform - pos: 11.5,13.5 + rot: 1.5707963267948966 rad + pos: 7.5,9.5 parent: 1668 - - uid: 1611 + - uid: 1733 components: - type: Transform - pos: 9.5,13.5 + rot: 1.5707963267948966 rad + pos: 7.5,12.5 parent: 1668 - - uid: 1737 + - uid: 1749 components: - type: Transform - pos: 23.5,-30.5 - parent: 1668 - - uid: 1738 - components: - - type: Transform - pos: 23.5,-33.5 - parent: 1668 - - uid: 1739 - components: - - type: Transform - pos: 23.5,-29.5 + pos: 11.5,11.5 parent: 1668 - uid: 1769 components: @@ -47197,21 +48016,11 @@ entities: - type: Transform pos: -22.5,8.5 parent: 1668 - - uid: 2933 - components: - - type: Transform - pos: 23.5,-32.5 - parent: 1668 - uid: 2981 components: - type: Transform pos: 23.5,-34.5 parent: 1668 - - uid: 2982 - components: - - type: Transform - pos: 23.5,-31.5 - parent: 1668 - uid: 2988 components: - type: Transform @@ -47254,6 +48063,12 @@ entities: - type: Transform pos: -8.5,3.5 parent: 1668 + - uid: 3266 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 7.5,7.5 + parent: 1668 - uid: 3286 components: - type: Transform @@ -48545,7 +49360,7 @@ entities: - uid: 8822 components: - type: Transform - pos: -10.5,-6.5 + pos: -13.362957,-6.653441 parent: 1668 - proto: SecurityTechFab entities: @@ -48846,20 +49661,30 @@ entities: - type: DeviceLinkSource linkedPorts: 542: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 543: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 544: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 545: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 546: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 603 components: - type: MetaData @@ -48870,20 +49695,30 @@ entities: - type: DeviceLinkSource linkedPorts: 539: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 538: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 537: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 540: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 541: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 9388 components: - type: MetaData @@ -48895,17 +49730,25 @@ entities: - type: DeviceLinkSource linkedPorts: 9380: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 9381: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 9382: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 9383: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 9389 components: - type: MetaData @@ -48917,17 +49760,25 @@ entities: - type: DeviceLinkSource linkedPorts: 9387: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 9386: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 9385: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 9384: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - proto: SignalSwitchDirectional entities: - uid: 336 @@ -48940,65 +49791,105 @@ entities: - type: DeviceLinkSource linkedPorts: 332: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 331: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 330: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 329: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 81: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 327: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 314: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 315: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 318: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 319: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 320: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 321: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 322: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 323: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 324: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 325: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 326: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 335: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 334: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 333: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 569 components: - type: MetaData @@ -49009,20 +49900,30 @@ entities: - type: DeviceLinkSource linkedPorts: 568: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 567: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 566: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 565: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 564: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 570 components: - type: MetaData @@ -49034,20 +49935,30 @@ entities: - type: DeviceLinkSource linkedPorts: 559: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 560: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 561: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 562: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 563: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 607 components: - type: MetaData @@ -49059,9 +49970,11 @@ entities: - type: DeviceLinkSource linkedPorts: 554: - - Status: Toggle + - - Status + - Toggle 555: - - Status: Toggle + - - Status + - Toggle - uid: 608 components: - type: MetaData @@ -49073,9 +49986,11 @@ entities: - type: DeviceLinkSource linkedPorts: 578: - - Status: Toggle + - - Status + - Toggle 577: - - Status: Toggle + - - Status + - Toggle - uid: 610 components: - type: MetaData @@ -49087,8 +50002,10 @@ entities: - type: DeviceLinkSource linkedPorts: 612: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 611 components: - type: MetaData @@ -49100,8 +50017,10 @@ entities: - type: DeviceLinkSource linkedPorts: 609: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 728 components: - type: MetaData @@ -49112,14 +50031,20 @@ entities: - type: DeviceLinkSource linkedPorts: 731: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 730: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 729: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 1130 components: - type: Transform @@ -49129,14 +50054,20 @@ entities: - type: DeviceLinkSource linkedPorts: 1142: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 1141: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 1140: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 1131 components: - type: Transform @@ -49146,48 +50077,72 @@ entities: - type: DeviceLinkSource linkedPorts: 1143: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 1144: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 1145: - - On: Open - - Off: Close - - uid: 1587 + - - On + - Open + - - Off + - Close + - uid: 1630 components: - type: MetaData - name: Inner Windoor Switch + name: Conveyor Toggle - type: Transform rot: -1.5707963267948966 rad - pos: 18.5,11.5 - parent: 1668 - - type: DeviceLinkSource - linkedPorts: - 1585: - - Status: Toggle - - uid: 1588 - components: - - type: MetaData - name: Inner Windoor Toggle - - type: Transform - rot: 1.5707963267948966 rad - pos: 8.5,11.5 - parent: 1668 - - type: DeviceLinkSource - linkedPorts: - 1586: - - Status: Toggle - - uid: 1663 - components: - - type: Transform - pos: 23.5,13.5 + pos: 18.5,9.5 parent: 1668 - type: DeviceLinkSource linkedPorts: + 3520: + - - Off + - Off + - - On + - Reverse + 1751: + - - Off + - Off + - - On + - Reverse + 1744: + - - Off + - Off + - - On + - Reverse + 2085: + - - Off + - Off + - - On + - Reverse + 1624: + - - Off + - Off + - - On + - Reverse + 1625: + - - On + - Forward + - - Off + - Off + - - On + - Reverse 1626: - - On: Forward - - Off: Off + - - Off + - Off + - - On + - Reverse + 1600: + - - Off + - Off + - - On + - Reverse - uid: 1728 components: - type: Transform @@ -49196,8 +50151,10 @@ entities: - type: DeviceLinkSource linkedPorts: 1627: - - On: Forward - - Off: Off + - - On + - Forward + - - Off + - Off - uid: 2250 components: - type: Transform @@ -49207,17 +50164,25 @@ entities: - type: DeviceLinkSource linkedPorts: 2247: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 2246: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 2248: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 2249: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 2254 components: - type: Transform @@ -49227,17 +50192,25 @@ entities: - type: DeviceLinkSource linkedPorts: 2257: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 2256: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 2253: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 2252: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 2803 components: - type: Transform @@ -49246,11 +50219,15 @@ entities: - type: DeviceLinkSource linkedPorts: 2805: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 2804: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 3914 components: - type: Transform @@ -49260,11 +50237,15 @@ entities: - type: DeviceLinkSource linkedPorts: 3913: - - Off: Close - - On: Open + - - Off + - Close + - - On + - Open 3912: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 4715 components: - type: MetaData @@ -49276,20 +50257,30 @@ entities: - type: DeviceLinkSource linkedPorts: 4726: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4727: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4728: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4729: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4730: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 4716 components: - type: MetaData @@ -49301,41 +50292,65 @@ entities: - type: DeviceLinkSource linkedPorts: 4737: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4738: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4739: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4740: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4741: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4742: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4759: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4758: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4757: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4756: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4755: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4754: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 4717 components: - type: MetaData @@ -49347,20 +50362,30 @@ entities: - type: DeviceLinkSource linkedPorts: 4743: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4744: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4745: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4746: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 4747: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - uid: 9217 components: - type: MetaData @@ -49372,14 +50397,20 @@ entities: - type: DeviceLinkSource linkedPorts: 6903: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 6904: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close 6905: - - On: Open - - Off: Close + - - On + - Open + - - Off + - Close - proto: SignArmory entities: - uid: 2163 @@ -49479,6 +50510,13 @@ entities: - type: Transform pos: -15.5,-28.5 parent: 1668 +- proto: SignGenpop + entities: + - uid: 2076 + components: + - type: Transform + pos: 8.5,13.5 + parent: 1668 - proto: SignGravity entities: - uid: 4028 @@ -49647,30 +50685,11 @@ entities: parent: 1668 - proto: SignPrison entities: - - uid: 1631 - components: - - type: Transform - pos: 8.5,13.5 - parent: 1668 - uid: 1632 components: - type: Transform pos: 18.5,6.5 parent: 1668 -- proto: SignRedOne - entities: - - uid: 1589 - components: - - type: Transform - pos: 8.5,11.5 - parent: 1668 -- proto: SignRedTwo - entities: - - uid: 1590 - components: - - type: Transform - pos: 18.5,11.5 - parent: 1668 - proto: SignScience entities: - uid: 9123 @@ -50003,15 +51022,15 @@ entities: - type: Transform pos: 16.07121,34.553917 parent: 1668 - - uid: 2437 + - uid: 9722 components: - type: Transform - pos: 16.07121,34.413292 + pos: 16.067247,34.42217 parent: 1668 - - uid: 2438 + - uid: 9929 components: - type: Transform - pos: 16.07121,34.413292 + pos: 16.070206,34.493206 parent: 1668 - proto: SprayBottleSpaceCleaner entities: @@ -50127,6 +51146,12 @@ entities: parent: 1668 - proto: Stool entities: + - uid: 501 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 5.5,9.5 + parent: 1668 - uid: 1319 components: - type: Transform @@ -50145,17 +51170,11 @@ entities: rot: -1.5707963267948966 rad pos: 16.5,6.5 parent: 1668 - - uid: 1684 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 20.5,11.5 - parent: 1668 - - uid: 1685 + - uid: 1614 components: - type: Transform rot: 1.5707963267948966 rad - pos: 6.5,11.5 + pos: 5.5,10.5 parent: 1668 - uid: 3027 components: @@ -50917,16 +51936,6 @@ entities: - SurveillanceCameraGeneral nameSet: True id: Afterhours Lounge North - - uid: 9449 - components: - - type: Transform - pos: 29.5,-27.5 - parent: 1668 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraGeneral - nameSet: True - id: Afterhours Lounge South - uid: 9454 components: - type: Transform @@ -51015,17 +52024,6 @@ entities: - SurveillanceCameraGeneral nameSet: True id: Green Team Ready Room - - uid: 9463 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 17.5,-39.5 - parent: 1668 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraGeneral - nameSet: True - id: Green Team Hall - uid: 9464 components: - type: Transform @@ -51192,10 +52190,10 @@ entities: parent: 1668 - proto: SurveillanceCameraRouterMedical entities: - - uid: 8793 + - uid: 8818 components: - type: Transform - pos: -11.5,-8.5 + pos: -11.5,-6.5 parent: 1668 - proto: SurveillanceCameraRouterScience entities: @@ -51220,24 +52218,11 @@ entities: parent: 1668 - proto: SurveillanceCameraRouterSupply entities: - - uid: 8790 + - uid: 9684 components: - type: Transform - pos: -11.5,-6.5 + pos: -11.5,-8.5 parent: 1668 -- proto: SurveillanceCameraScience - entities: - - uid: 9457 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 19.5,-30.5 - parent: 1668 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraScience - nameSet: True - id: Afterhours Science Lounge - proto: SurveillanceCameraSecurity entities: - uid: 9413 @@ -51557,16 +52542,6 @@ entities: - type: Transform pos: 8.5,3.5 parent: 1668 - - uid: 1625 - components: - - type: Transform - pos: 7.5,11.5 - parent: 1668 - - uid: 1628 - components: - - type: Transform - pos: 19.5,11.5 - parent: 1668 - uid: 2111 components: - type: Transform @@ -51731,11 +52706,6 @@ entities: - type: Transform pos: -13.5,-6.5 parent: 1668 - - uid: 8818 - components: - - type: Transform - pos: -10.5,-6.5 - parent: 1668 - uid: 9091 components: - type: Transform @@ -51773,6 +52743,16 @@ entities: parent: 1668 - proto: TableCarpet entities: + - uid: 1527 + components: + - type: Transform + pos: 32.5,12.5 + parent: 1668 + - uid: 1530 + components: + - type: Transform + pos: 33.5,11.5 + parent: 1668 - uid: 2037 components: - type: Transform @@ -51788,16 +52768,6 @@ entities: - type: Transform pos: 33.5,27.5 parent: 1668 - - uid: 2077 - components: - - type: Transform - pos: 31.5,11.5 - parent: 1668 - - uid: 2093 - components: - - type: Transform - pos: 31.5,12.5 - parent: 1668 - uid: 2109 components: - type: Transform @@ -52148,11 +53118,6 @@ entities: - type: Transform pos: -3.5,-2.5 parent: 1668 - - uid: 149 - components: - - type: Transform - pos: 1.5,-2.5 - parent: 1668 - uid: 151 components: - type: Transform @@ -52203,12 +53168,6 @@ entities: - type: Transform pos: 3.5,-1.5 parent: 1668 - - uid: 515 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 8.5,8.5 - parent: 1668 - uid: 575 components: - type: Transform @@ -52386,12 +53345,6 @@ entities: - type: Transform pos: 16.5,-4.5 parent: 1668 - - uid: 905 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 11.5,9.5 - parent: 1668 - uid: 936 components: - type: Transform @@ -52681,16 +53634,12 @@ entities: rot: 1.5707963267948966 rad pos: 34.5,-12.5 parent: 1668 - - uid: 1579 + - uid: 1540 components: - type: Transform + rot: -1.5707963267948966 rad pos: 18.5,12.5 parent: 1668 - - uid: 1580 - components: - - type: Transform - pos: 8.5,12.5 - parent: 1668 - uid: 1591 components: - type: Transform @@ -52701,21 +53650,11 @@ entities: - type: Transform pos: 14.5,6.5 parent: 1668 - - uid: 1598 - components: - - type: Transform - pos: 15.5,7.5 - parent: 1668 - uid: 1599 components: - type: Transform pos: 14.5,7.5 parent: 1668 - - uid: 1600 - components: - - type: Transform - pos: 13.5,7.5 - parent: 1668 - uid: 1603 components: - type: Transform @@ -52736,11 +53675,10 @@ entities: - type: Transform pos: 12.5,12.5 parent: 1668 - - uid: 2103 + - uid: 2083 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 11.5,7.5 + pos: 6.5,10.5 parent: 1668 - uid: 2148 components: @@ -52995,6 +53933,11 @@ entities: - type: Transform pos: -17.5,4.5 parent: 1668 + - uid: 2984 + components: + - type: Transform + pos: 6.5,9.5 + parent: 1668 - uid: 3021 components: - type: Transform @@ -53040,17 +53983,11 @@ entities: - type: Transform pos: -3.5,22.5 parent: 1668 - - uid: 3520 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 8.5,7.5 - parent: 1668 - uid: 3671 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 11.5,8.5 + rot: -1.5707963267948966 rad + pos: 18.5,10.5 parent: 1668 - uid: 3884 components: @@ -53084,6 +54021,12 @@ entities: - type: Transform pos: -18.5,26.5 parent: 1668 + - uid: 4180 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 18.5,11.5 + parent: 1668 - uid: 4225 components: - type: Transform @@ -53448,6 +54391,12 @@ entities: - type: Transform pos: -20.5,12.5 parent: 1668 + - uid: 2985 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 3.5,7.5 + parent: 1668 - uid: 4205 components: - type: Transform @@ -53636,14 +54585,27 @@ entities: rot: 1.5707963267948966 rad pos: -42.5,5.5 parent: 1668 -- proto: TargetClown - entities: - - uid: 3100 + - uid: 9829 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 5.5,8.5 + rot: -1.5707963267948966 rad + pos: 6.5,7.5 parent: 1668 + - uid: 9960 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 32.5,-22.5 + parent: 1668 +- proto: TargetClown + entities: + - uid: 2081 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 22.5,12.5 + parent: 1668 + - type: Conveyed - proto: TargetDarts entities: - uid: 9132 @@ -53651,14 +54613,24 @@ entities: - type: Transform pos: 21.5,-29.5 parent: 1668 -- proto: TargetSyndicate +- proto: TargetHuman entities: - - uid: 3666 + - uid: 3100 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 4.5,7.5 + rot: -1.5707963267948966 rad + pos: 22.5,10.5 parent: 1668 + - type: Conveyed +- proto: TargetSyndicate + entities: + - uid: 9834 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 24.5,11.5 + parent: 1668 + - type: Conveyed - proto: TearGasGrenade entities: - uid: 3169 @@ -53686,7 +54658,7 @@ entities: - uid: 8786 components: - type: Transform - pos: -12.5,-6.5 + pos: -12.5,-8.5 parent: 1668 - proto: TelecomServerFilledCommand entities: @@ -53711,10 +54683,10 @@ entities: parent: 1668 - proto: TelecomServerFilledMedical entities: - - uid: 6390 + - uid: 8790 components: - type: Transform - pos: -12.5,-8.5 + pos: -12.5,-6.5 parent: 1668 - proto: TelecomServerFilledScience entities: @@ -53773,6 +54745,24 @@ entities: - type: Transform pos: 29.5,-14.5 parent: 1668 + - uid: 1666 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 28.5,-9.5 + parent: 1668 + - uid: 1676 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 28.5,-11.5 + parent: 1668 + - uid: 1678 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 28.5,-12.5 + parent: 1668 - uid: 2114 components: - type: Transform @@ -53896,6 +54886,14 @@ entities: - type: Transform pos: -20.5,30.5 parent: 1668 +- proto: Truncheon + entities: + - uid: 9682 + components: + - type: Transform + parent: 4559 + - type: Physics + canCollide: False - proto: TurboItemRecharger entities: - uid: 209 @@ -53903,6 +54901,28 @@ entities: - type: Transform pos: 14.5,5.5 parent: 1668 +- proto: TurnstileGenpopEnter + entities: + - uid: 4187 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 7.5,8.5 + parent: 1668 +- proto: TurnstileGenpopLeave + entities: + - uid: 1598 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 9.5,13.5 + parent: 1668 + - uid: 1735 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 7.5,11.5 + parent: 1668 - proto: TwoWayLever entities: - uid: 3648 @@ -53913,41 +54933,68 @@ entities: - type: DeviceLinkSource linkedPorts: 3644: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3645: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3663: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3651: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3646: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3649: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3638: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3654: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3662: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off - uid: 3652 components: - type: Transform @@ -53956,41 +55003,68 @@ entities: - type: DeviceLinkSource linkedPorts: 3655: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3653: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3656: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3647: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3650: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3657: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3658: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3659: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off 3264: - - Left: Forward - - Right: Reverse - - Middle: Off + - - Left + - Forward + - - Right + - Reverse + - - Middle + - Off - uid: 9083 components: - type: Transform @@ -53999,9 +55073,12 @@ entities: - type: DeviceLinkSource linkedPorts: 8745: - - Left: Reverse - - Right: Forward - - Middle: Off + - - Left + - Reverse + - - Right + - Forward + - - Middle + - Off - proto: UraniumReinforcedWindowDirectional entities: - uid: 9601 @@ -54183,10 +55260,10 @@ entities: - type: Transform pos: 34.5,14.5 parent: 1668 - - uid: 3607 + - uid: 3037 components: - type: Transform - pos: 13.5,8.5 + pos: 12.5,10.5 parent: 1668 - uid: 9096 components: @@ -54221,6 +55298,11 @@ entities: parent: 1668 - proto: VendingMachineGames entities: + - uid: 1525 + components: + - type: Transform + pos: 3.5,9.5 + parent: 1668 - uid: 9107 components: - type: Transform @@ -54242,10 +55324,10 @@ entities: parent: 1668 - proto: VendingMachineLawDrobe entities: - - uid: 2071 + - uid: 1563 components: - type: Transform - pos: 25.5,12.5 + pos: 30.5,12.5 parent: 1668 - proto: VendingMachineMedical entities: @@ -54282,22 +55364,22 @@ entities: parent: 1668 - proto: VendingMachineSec entities: - - uid: 82 - components: - - type: Transform - pos: 13.5,5.5 - parent: 1668 - uid: 1138 components: - type: Transform pos: 34.5,-8.5 parent: 1668 -- proto: VendingMachineSecDrobe - entities: - - uid: 1593 + - uid: 2103 components: - type: Transform - pos: 13.5,6.5 + pos: 15.5,7.5 + parent: 1668 +- proto: VendingMachineSecDrobe + entities: + - uid: 3607 + components: + - type: Transform + pos: 15.5,12.5 parent: 1668 - proto: VendingMachineSeeds entities: @@ -54336,6 +55418,11 @@ entities: parent: 1668 - proto: VendingMachineSustenance entities: + - uid: 4179 + components: + - type: Transform + pos: 3.5,10.5 + parent: 1668 - uid: 9199 components: - type: Transform @@ -55029,12 +56116,6 @@ entities: - type: Transform pos: 19.5,-7.5 parent: 1668 - - uid: 409 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 24.5,11.5 - parent: 1668 - uid: 410 components: - type: Transform @@ -55050,12 +56131,6 @@ entities: - type: Transform pos: 7.5,2.5 parent: 1668 - - uid: 501 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 24.5,10.5 - parent: 1668 - uid: 502 components: - type: Transform @@ -55068,6 +56143,12 @@ entities: rot: 1.5707963267948966 rad pos: 19.5,13.5 parent: 1668 + - uid: 515 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 25.5,12.5 + parent: 1668 - uid: 516 components: - type: Transform @@ -55509,6 +56590,12 @@ entities: - type: Transform pos: 34.5,-14.5 parent: 1668 + - uid: 1156 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 27.5,-10.5 + parent: 1668 - uid: 1159 components: - type: Transform @@ -55637,6 +56724,12 @@ entities: - type: Transform pos: 31.5,-23.5 parent: 1668 + - uid: 1295 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 28.5,-10.5 + parent: 1668 - uid: 1522 components: - type: Transform @@ -55657,11 +56750,6 @@ entities: - type: Transform pos: 8.5,13.5 parent: 1668 - - uid: 1540 - components: - - type: Transform - pos: 8.5,9.5 - parent: 1668 - uid: 1541 components: - type: Transform @@ -55684,11 +56772,39 @@ entities: - type: Transform pos: 2.5,13.5 parent: 1668 - - uid: 1563 + - uid: 1551 + components: + - type: Transform + pos: 2.5,10.5 + parent: 1668 + - uid: 1559 components: - type: Transform rot: 1.5707963267948966 rad - pos: 24.5,12.5 + pos: 25.5,10.5 + parent: 1668 + - uid: 1602 + components: + - type: Transform + pos: 11.5,13.5 + parent: 1668 + - uid: 1663 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 25.5,11.5 + parent: 1668 + - uid: 1679 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 24.5,-10.5 + parent: 1668 + - uid: 1681 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 26.5,-10.5 parent: 1668 - uid: 1752 components: @@ -56042,6 +57158,11 @@ entities: - type: Transform pos: 25.5,13.5 parent: 1668 + - uid: 2084 + components: + - type: Transform + pos: 11.5,9.5 + parent: 1668 - uid: 2123 components: - type: Transform @@ -56791,6 +57912,11 @@ entities: - type: Transform pos: -14.5,4.5 parent: 1668 + - uid: 2933 + components: + - type: Transform + pos: -21.5,-50.5 + parent: 1668 - uid: 2987 components: - type: Transform @@ -57872,7 +58998,7 @@ entities: - uid: 4464 components: - type: Transform - pos: 18.5,-49.5 + pos: -21.5,-49.5 parent: 1668 - uid: 4465 components: @@ -57932,7 +59058,7 @@ entities: - uid: 4482 components: - type: Transform - pos: -20.5,-49.5 + pos: -21.5,-48.5 parent: 1668 - uid: 4483 components: @@ -58029,11 +59155,6 @@ entities: - type: Transform pos: -18.5,-49.5 parent: 1668 - - uid: 4505 - components: - - type: Transform - pos: -19.5,-49.5 - parent: 1668 - uid: 4506 components: - type: Transform @@ -58089,11 +59210,6 @@ entities: - type: Transform pos: -18.5,-34.5 parent: 1668 - - uid: 4523 - components: - - type: Transform - pos: -21.5,-49.5 - parent: 1668 - uid: 4524 components: - type: Transform @@ -58179,11 +59295,6 @@ entities: - type: Transform pos: -19.5,-33.5 parent: 1668 - - uid: 4541 - components: - - type: Transform - pos: -22.5,-49.5 - parent: 1668 - uid: 4542 components: - type: Transform @@ -58194,11 +59305,6 @@ entities: - type: Transform pos: -21.5,-47.5 parent: 1668 - - uid: 4544 - components: - - type: Transform - pos: -22.5,-47.5 - parent: 1668 - uid: 4545 components: - type: Transform @@ -58269,25 +59375,10 @@ entities: - type: Transform pos: -20.5,-34.5 parent: 1668 - - uid: 4559 - components: - - type: Transform - pos: 19.5,-49.5 - parent: 1668 - - uid: 4560 - components: - - type: Transform - pos: 20.5,-49.5 - parent: 1668 - uid: 4561 components: - type: Transform - pos: 21.5,-49.5 - parent: 1668 - - uid: 4562 - components: - - type: Transform - pos: 21.5,-47.5 + pos: 20.5,-48.5 parent: 1668 - uid: 4563 components: @@ -58379,6 +59470,21 @@ entities: - type: Transform pos: -23.5,-28.5 parent: 1668 + - uid: 4839 + components: + - type: Transform + pos: 20.5,-49.5 + parent: 1668 + - uid: 4845 + components: + - type: Transform + pos: -19.5,-50.5 + parent: 1668 + - uid: 4849 + components: + - type: Transform + pos: -19.5,-49.5 + parent: 1668 - uid: 5512 components: - type: Transform @@ -59873,11 +60979,32 @@ entities: - type: Transform pos: -8.5,-8.5 parent: 1668 + - uid: 9116 + components: + - type: Transform + pos: 20.5,-50.5 + parent: 1668 + - uid: 9117 + components: + - type: Transform + pos: 18.5,-49.5 + parent: 1668 - uid: 9280 components: - type: Transform pos: -40.5,8.5 parent: 1668 + - uid: 9699 + components: + - type: Transform + pos: 18.5,-50.5 + parent: 1668 + - uid: 9988 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 25.5,-10.5 + parent: 1668 - proto: WallWood entities: - uid: 6135 @@ -59982,11 +61109,6 @@ entities: - type: Transform pos: 3.5,11.5 parent: 1668 - - uid: 1630 - components: - - type: Transform - pos: 23.5,11.5 - parent: 1668 - proto: WarningN2 entities: - uid: 3375 @@ -60018,11 +61140,6 @@ entities: - type: Transform pos: 20.5,16.5 parent: 1668 - - uid: 3266 - components: - - type: Transform - pos: 13.5,9.5 - parent: 1668 - uid: 3994 components: - type: Transform @@ -60033,6 +61150,11 @@ entities: - type: Transform pos: 1.5,18.5 parent: 1668 + - uid: 8046 + components: + - type: Transform + pos: 12.5,11.5 + parent: 1668 - uid: 8763 components: - type: Transform @@ -60058,6 +61180,11 @@ entities: - type: Transform pos: -30.5,4.5 parent: 1668 + - uid: 9980 + components: + - type: Transform + pos: 6.5,12.5 + parent: 1668 - proto: WaterTankHighCapacity entities: - uid: 1031 @@ -60116,11 +61243,6 @@ entities: - type: Transform pos: -23.5,10.5 parent: 1668 - - uid: 2985 - components: - - type: Transform - pos: 11.5,7.5 - parent: 1668 - uid: 4631 components: - type: Transform @@ -60177,6 +61299,12 @@ entities: - type: Transform pos: -35.5,13.5 parent: 1668 + - uid: 9835 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 18.5,10.5 + parent: 1668 - proto: WeaponDisabler entities: - uid: 6817 @@ -60202,10 +61330,10 @@ entities: - type: InsideEntityStorage - proto: WeaponDisablerPractice entities: - - uid: 4180 + - uid: 9958 components: - type: Transform - pos: 11.414704,7.5328236 + pos: 18.50393,10.681932 parent: 1668 - proto: WeaponDisablerSMG entities: @@ -60272,10 +61400,11 @@ entities: parent: 1668 - proto: WeaponLaserCarbinePractice entities: - - uid: 4179 + - uid: 9961 components: - type: Transform - pos: 11.461579,8.095324 + rot: -1.5707963267948966 rad + pos: 18.587261,11.463725 parent: 1668 - proto: WeaponLaserGun entities: @@ -60342,6 +61471,13 @@ entities: - type: Transform pos: -11.42821,5.5092115 parent: 1668 + - uid: 4541 + components: + - type: Transform + parent: 1610 + - type: Physics + canCollide: False + - type: InsideEntityStorage - uid: 6897 components: - type: Transform @@ -60413,7 +61549,7 @@ entities: - uid: 2430 components: - type: Transform - pos: 15.508711,34.397667 + pos: 15.50552,34.388195 parent: 1668 - proto: WeaponRifleAk entities: @@ -60900,18 +62036,6 @@ entities: parent: 1668 - proto: WindoorSecureBrigLocked entities: - - uid: 1583 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 18.5,12.5 - parent: 1668 - - uid: 1584 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 8.5,12.5 - parent: 1668 - uid: 1864 components: - type: Transform @@ -61125,6 +62249,12 @@ entities: parent: 1668 - proto: WindoorSecureSecurityLocked entities: + - uid: 409 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 18.5,11.5 + parent: 1668 - uid: 557 components: - type: Transform @@ -61155,22 +62285,6 @@ entities: rot: -1.5707963267948966 rad pos: 32.5,-10.5 parent: 1668 - - uid: 1585 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 18.5,12.5 - parent: 1668 - - type: DeviceLinkSink - invokeCounter: 1 - - uid: 1586 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 8.5,12.5 - parent: 1668 - - type: DeviceLinkSink - invokeCounter: 1 - uid: 1616 components: - type: Transform @@ -61186,17 +62300,17 @@ entities: - type: Transform pos: 26.5,19.5 parent: 1668 - - uid: 2104 + - uid: 2082 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 8.5,8.5 + rot: -1.5707963267948966 rad + pos: 18.5,12.5 parent: 1668 - - uid: 3721 + - uid: 4184 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 8.5,7.5 + rot: -1.5707963267948966 rad + pos: 18.5,10.5 parent: 1668 - proto: Window entities: @@ -61395,12 +62509,6 @@ entities: - type: Transform pos: 14.5,13.5 parent: 1668 - - uid: 1730 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 21.5,12.5 - parent: 1668 - uid: 1731 components: - type: Transform @@ -61703,30 +62811,6 @@ entities: rot: -1.5707963267948966 rad pos: -3.5,-20.5 parent: 1668 - - uid: 4184 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 11.5,7.5 - parent: 1668 - - uid: 4185 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 11.5,8.5 - parent: 1668 - - uid: 4186 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 11.5,9.5 - parent: 1668 - - uid: 4187 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 11.5,9.5 - parent: 1668 - uid: 4795 components: - type: Transform From 159c1879712e3d543a13f57e04d885720b03cb8f Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 2 Jul 2025 03:15:49 +0000 Subject: [PATCH 160/191] Automatic changelog update --- Resources/Changelog/Maps.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Maps.yml b/Resources/Changelog/Maps.yml index 50080c357a..ea755b5405 100644 --- a/Resources/Changelog/Maps.yml +++ b/Resources/Changelog/Maps.yml @@ -387,4 +387,11 @@ id: 47 time: '2025-06-19T04:06:17.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38435 +- author: UpAndLeaves + changes: + - message: CentComm security now uses a genpop system. + type: Add + id: 48 + time: '2025-07-02T03:14:37.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/37494 Order: 1 From 579b38b92b6dfc800b2d8dbfe1e2fc0c79c003b2 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Wed, 2 Jul 2025 10:46:30 -0700 Subject: [PATCH 161/191] Improvements and fixups for New Status Effect API (#38660) --- Content.Server/Drowsiness/DrowsinessSystem.cs | 4 +- .../Traits/Assorted/NarcolepsySystem.cs | 2 +- .../StatusEffects/ModifyStatusEffect.cs | 12 +- .../SSDIndicator/SSDIndicatorSystem.cs | 2 +- .../SharedStatusEffectsSystem.cs | 135 ++++++++++++---- .../StatusEffectNew/StatusEffectSystem.API.cs | 152 +++++++++++------- 6 files changed, 210 insertions(+), 97 deletions(-) diff --git a/Content.Server/Drowsiness/DrowsinessSystem.cs b/Content.Server/Drowsiness/DrowsinessSystem.cs index 0489b16f51..6de270abcc 100644 --- a/Content.Server/Drowsiness/DrowsinessSystem.cs +++ b/Content.Server/Drowsiness/DrowsinessSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.StatusEffectNew; +using Content.Server.StatusEffectNew; using Content.Shared.Bed.Sleep; using Content.Shared.Drowsiness; using Content.Shared.StatusEffectNew; @@ -47,7 +47,7 @@ public sealed class DrowsinessSystem : SharedDrowsinessSystem // Make sure the sleep time doesn't cut into the time to next incident. drowsiness.NextIncidentTime += duration; - _statusEffects.TryAddStatusEffect(statusEffect.AppliedTo.Value, SleepingSystem.StatusEffectForcedSleeping, duration); + _statusEffects.TryAddStatusEffectDuration(statusEffect.AppliedTo.Value, SleepingSystem.StatusEffectForcedSleeping, duration); } } } diff --git a/Content.Server/Traits/Assorted/NarcolepsySystem.cs b/Content.Server/Traits/Assorted/NarcolepsySystem.cs index 9d0ff9470a..b0746fa377 100644 --- a/Content.Server/Traits/Assorted/NarcolepsySystem.cs +++ b/Content.Server/Traits/Assorted/NarcolepsySystem.cs @@ -53,7 +53,7 @@ public sealed class NarcolepsySystem : EntitySystem // Make sure the sleep time doesn't cut into the time to next incident. narcolepsy.NextIncidentTime += duration; - _statusEffects.TryAddStatusEffect(uid, SleepingSystem.StatusEffectForcedSleeping, TimeSpan.FromSeconds(duration)); + _statusEffects.TryAddStatusEffectDuration(uid, SleepingSystem.StatusEffectForcedSleeping, TimeSpan.FromSeconds(duration)); } } } diff --git a/Content.Shared/EntityEffects/Effects/StatusEffects/ModifyStatusEffect.cs b/Content.Shared/EntityEffects/Effects/StatusEffects/ModifyStatusEffect.cs index 33021ecdab..5ebb8aad1b 100644 --- a/Content.Shared/EntityEffects/Effects/StatusEffects/ModifyStatusEffect.cs +++ b/Content.Shared/EntityEffects/Effects/StatusEffects/ModifyStatusEffect.cs @@ -20,7 +20,7 @@ public sealed partial class ModifyStatusEffect : EntityEffect public float Time = 2.0f; /// - /// true - refresh status effect time, false - accumulate status effect time. + /// true - refresh status effect time (update to greater value), false - accumulate status effect time. /// [DataField] public bool Refresh = true; @@ -40,16 +40,20 @@ public sealed partial class ModifyStatusEffect : EntityEffect if (args is EntityEffectReagentArgs reagentArgs) time *= reagentArgs.Scale.Float(); + var duration = TimeSpan.FromSeconds(time); switch (Type) { case StatusEffectMetabolismType.Add: - statusSys.TryAddStatusEffect(args.TargetEntity, EffectProto, TimeSpan.FromSeconds(time), Refresh); + if (Refresh) + statusSys.TryUpdateStatusEffectDuration(args.TargetEntity, EffectProto, duration); + else + statusSys.TryAddStatusEffectDuration(args.TargetEntity, EffectProto, duration); break; case StatusEffectMetabolismType.Remove: - statusSys.TryAddTime(args.TargetEntity, EffectProto, -TimeSpan.FromSeconds(time)); + statusSys.TryAddTime(args.TargetEntity, EffectProto, -duration); break; case StatusEffectMetabolismType.Set: - statusSys.TrySetTime(args.TargetEntity, EffectProto, TimeSpan.FromSeconds(time)); + statusSys.TrySetStatusEffectDuration(args.TargetEntity, EffectProto, duration); break; } } diff --git a/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs b/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs index a13b6b915c..ca7d73ac83 100644 --- a/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs +++ b/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs @@ -85,7 +85,7 @@ public sealed class SSDIndicatorSystem : EntitySystem ssd.FallAsleepTime <= _timing.CurTime && !TerminatingOrDeleted(uid)) { - _statusEffects.TryAddStatusEffect(uid, StatusEffectSSDSleeping); + _statusEffects.TrySetStatusEffectDuration(uid, StatusEffectSSDSleeping, null); } } } diff --git a/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs b/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs index c836b8205c..9e6ed4d7ff 100644 --- a/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs +++ b/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Shared.Alert; using Content.Shared.StatusEffectNew.Components; using Content.Shared.Whitelist; @@ -77,53 +78,59 @@ public abstract partial class SharedStatusEffectsSystem : EntitySystem effectComp.EndEffectTime += delta; Dirty(effect, effectComp); - if (effectComp is { AppliedTo: not null, Alert: not null }) - { - (TimeSpan Start, TimeSpan End)? cooldown = effectComp.EndEffectTime is null - ? null - : (_timing.CurTime, effectComp.EndEffectTime.Value); - _alerts.ShowAlert( - effectComp.AppliedTo.Value, - effectComp.Alert.Value, - cooldown: cooldown - ); - } + ShowAlertIfNeeded(effectComp); } - private void SetStatusEffectTime(EntityUid effect, TimeSpan duration) + private void SetStatusEffectTime(EntityUid effect, TimeSpan? duration) { if (!_effectQuery.TryComp(effect, out var effectComp)) return; - effectComp.EndEffectTime = _timing.CurTime + duration; + if (duration is null) + { + if(effectComp.EndEffectTime is null) + return; + + effectComp.EndEffectTime = null; + } + else + effectComp.EndEffectTime = _timing.CurTime + duration; + Dirty(effect, effectComp); - if (effectComp is { AppliedTo: not null, Alert: not null }) - { - (TimeSpan, TimeSpan)? cooldown = effectComp.EndEffectTime is null - ? null - : (_timing.CurTime, effectComp.EndEffectTime.Value); - _alerts.ShowAlert( - effectComp.AppliedTo.Value, - effectComp.Alert.Value, - cooldown: cooldown - ); - } + ShowAlertIfNeeded(effectComp); } + private void UpdateStatusEffectTime(EntityUid effect, TimeSpan? duration) + { + if (!_effectQuery.TryComp(effect, out var effectComp)) + return; + + // It's already infinitely long + if (effectComp.EndEffectTime is null) + return; + + if (duration is null) + effectComp.EndEffectTime = null; + else + { + var newEndTime = _timing.CurTime + duration; + if (effectComp.EndEffectTime >= newEndTime) + return; + + effectComp.EndEffectTime = newEndTime; + } + + Dirty(effect, effectComp); + + ShowAlertIfNeeded(effectComp); + } + + private void OnStatusEffectApplied(Entity ent, ref StatusEffectAppliedEvent args) { - if (ent.Comp is { AppliedTo: not null, Alert: not null }) - { - (TimeSpan, TimeSpan)? cooldown = ent.Comp.EndEffectTime is null - ? null - : (_timing.CurTime, ent.Comp.EndEffectTime.Value); - _alerts.ShowAlert( - ent.Comp.AppliedTo.Value, - ent.Comp.Alert.Value, - cooldown: cooldown - ); - } + StatusEffectComponent statusEffect = ent; + ShowAlertIfNeeded(statusEffect); } private void OnStatusEffectRemoved(Entity ent, ref StatusEffectRemovedEvent args) @@ -154,6 +161,64 @@ public abstract partial class SharedStatusEffectsSystem : EntitySystem return true; } + + /// + /// Attempts to add a status effect to the specified entity. Returns True if the effect is added, does not check if one + /// already exists as it's intended to be called after a check for an existing effect has already failed. + /// + /// The target entity to which the effect should be added. + /// ProtoId of the status effect entity. Make sure it has StatusEffectComponent on it. + /// Duration of status effect. Leave null and the effect will be permanent until it is removed using TryRemoveStatusEffect. + /// The EntityUid of the status effect we have just created or null if we couldn't create one. + private bool TryAddStatusEffect( + EntityUid target, + EntProtoId effectProto, + [NotNullWhen(true)] out EntityUid? statusEffect, + TimeSpan? duration = null + ) + { + statusEffect = null; + if (!CanAddStatusEffect(target, effectProto)) + return false; + + var container = EnsureComp(target); + + //And only if all checks passed we spawn the effect + var effect = PredictedSpawnAttachedTo(effectProto, Transform(target).Coordinates); + _transform.SetParent(effect, target); + if (!_effectQuery.TryComp(effect, out var effectComp)) + return false; + + statusEffect = effect; + + if (duration != null) + effectComp.EndEffectTime = _timing.CurTime + duration; + + container.ActiveStatusEffects.Add(effect); + effectComp.AppliedTo = target; + Dirty(target, container); + Dirty(effect, effectComp); + + var ev = new StatusEffectAppliedEvent(target); + RaiseLocalEvent(effect, ref ev); + + return true; + } + + private void ShowAlertIfNeeded(StatusEffectComponent effectComp) + { + if (effectComp is { AppliedTo: not null, Alert: not null }) + { + (TimeSpan, TimeSpan)? cooldown = effectComp.EndEffectTime is null + ? null + : (_timing.CurTime, effectComp.EndEffectTime.Value); + _alerts.ShowAlert( + effectComp.AppliedTo.Value, + effectComp.Alert.Value, + cooldown: cooldown + ); + } + } } /// diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs index 2d1ec89c9d..5e20cea1bb 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs @@ -7,80 +7,94 @@ namespace Content.Shared.StatusEffectNew; public abstract partial class SharedStatusEffectsSystem { /// - /// Attempts to add a status effect to the specified entity. Returns True if the effect is added or it already exists - /// and has been successfully extended in time, returns False if the status effect cannot be applied to this entity, - /// or for any other reason. + /// Increments duration of status effect by . + /// Tries to add status effect if it is not yet present on entity. /// /// The target entity to which the effect should be added. /// ProtoId of the status effect entity. Make sure it has StatusEffectComponent on it. /// Duration of status effect. Leave null and the effect will be permanent until it is removed using TryRemoveStatusEffect. - /// - /// If True, the effect duration time will be reset and reapplied. If False, the effect duration time will be overlaid with the existing one. - /// In the other case, the effect will either be added for the specified time or its time will be extended for the specified time. - /// /// The EntityUid of the status effect we have just created or null if it doesn't exist. - public bool TryAddStatusEffect( + /// True if effect exists and its duration is set properly, false in case effect cannot be applied. + public bool TryAddStatusEffectDuration( EntityUid target, EntProtoId effectProto, - out EntityUid? statusEffect, - TimeSpan? duration = null, - bool resetCooldown = false + [NotNullWhen(true)] out EntityUid? statusEffect, + TimeSpan duration ) { - statusEffect = null; - if (TryGetStatusEffect(target, effectProto, out var existingEffect)) - { - statusEffect = existingEffect; - //We don't need to add the effect if it already exists - if (duration is null) - return true; + if (!TryGetStatusEffect(target, effectProto, out statusEffect)) + return TryAddStatusEffect(target, effectProto, out statusEffect, duration); - if (resetCooldown) - SetStatusEffectTime(existingEffect.Value, duration.Value); - else - AddStatusEffectTime(existingEffect.Value, duration.Value); - - return true; - } - - if (!CanAddStatusEffect(target, effectProto)) - return false; - - var container = EnsureComp(target); - - //And only if all checks passed we spawn the effect - var effect = PredictedSpawnAttachedTo(effectProto, Transform(target).Coordinates); - statusEffect = effect; - _transform.SetParent(effect, target); - if (!_effectQuery.TryComp(effect, out var effectComp)) - return false; - - if (duration != null) - effectComp.EndEffectTime = _timing.CurTime + duration; - - container.ActiveStatusEffects.Add(effect); - effectComp.AppliedTo = target; - Dirty(target, container); - Dirty(effect, effectComp); - - var ev = new StatusEffectAppliedEvent(target); - RaiseLocalEvent(effect, ref ev); + AddStatusEffectTime(statusEffect.Value, duration); return true; } + + /// + public bool TryAddStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan duration) + { + return TryAddStatusEffectDuration(target, effectProto, out _, duration); + } + /// - /// An overload of - /// that doesn't return a status effect EntityUid. + /// Sets duration of status effect by . + /// Tries to add status effect if it is not yet present on entity. /// - public bool TryAddStatusEffect( + /// The target entity to which the effect should be added. + /// ProtoId of the status effect entity. Make sure it has StatusEffectComponent on it. + /// Duration of status effect. Leave null and the effect will be permanent until it is removed using TryRemoveStatusEffect. + /// The EntityUid of the status effect we have just created or null if it doesn't exist. + /// True if effect exists and its duration is set properly, false in case effect cannot be applied. + public bool TrySetStatusEffectDuration( EntityUid target, EntProtoId effectProto, - TimeSpan? duration = null, - bool resetCooldown = false + [NotNullWhen(true)] out EntityUid? statusEffect, + TimeSpan? duration = null ) { - return TryAddStatusEffect(target, effectProto, out _, duration, resetCooldown); + if (!TryGetStatusEffect(target, effectProto, out statusEffect)) + return TryAddStatusEffect(target, effectProto, out statusEffect, duration); + + SetStatusEffectTime(statusEffect.Value, duration); + + return true; + } + + /// + public bool TrySetStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan? duration = null) + { + return TrySetStatusEffectDuration(target, effectProto, out _, duration); + } + + /// + /// Updates duration of effect to larger value between provided and current effect duration. + /// Tries to add status effect if it is not yet present on entity. + /// + /// The target entity to which the effect should be added. + /// ProtoId of the status effect entity. Make sure it has StatusEffectComponent on it. + /// Duration of status effect. Leave null and the effect will be permanent until it is removed using TryRemoveStatusEffect. + /// The EntityUid of the status effect we have just created or null if it doesn't exist. + /// True if effect exists and its duration is set properly, false in case effect cannot be applied. + public bool TryUpdateStatusEffectDuration( + EntityUid target, + EntProtoId effectProto, + [NotNullWhen(true)] out EntityUid? statusEffect, + TimeSpan? duration = null + ) + { + if (!TryGetStatusEffect(target, effectProto, out statusEffect)) + return TryAddStatusEffect(target, effectProto, out statusEffect, duration); + + UpdateStatusEffectTime(statusEffect.Value, duration); + + return true; + } + + /// + public bool TryUpdateStatusEffectDuration(EntityUid target, EntProtoId effectProto, TimeSpan? duration = null) + { + return TryUpdateStatusEffectDuration(target, effectProto, out _, duration); } /// @@ -190,6 +204,36 @@ public abstract partial class SharedStatusEffectsSystem return false; } + /// + /// Attempts to get the maximum time left for a given Status Effect Component, returns false if no such + /// component exists. + /// + /// The target entity on which the effect is applied. + /// Returns the EntityUid of the status effect with the most time left, and the end effect time + /// of that status effect. + /// True if a status effect entity with the given component exists + public bool TryGetMaxTime(EntityUid uid, out (EntityUid EffectEnt, TimeSpan? EndEffectTime) time) where T : IComponent + { + time = default; + if (!TryEffectsWithComp(uid, out var status)) + return false; + + time.Item2 = TimeSpan.Zero; + + foreach (var effect in status) + { + if (effect.Comp2.EndEffectTime == null) + { + time = (effect.Owner, null); + return true; + } + + if (effect.Comp2.EndEffectTime > time.Item2) + time = (effect.Owner, effect.Comp2.EndEffectTime); + } + return true; + } + /// /// Attempts to edit the remaining time for a status effect on an entity. /// From a6c58aba12945e8abb77feed556615d44d75259a Mon Sep 17 00:00:00 2001 From: little-meow-meow <204685920+little-meow-meow@users.noreply.github.com> Date: Wed, 2 Jul 2025 13:26:03 -0700 Subject: [PATCH 162/191] ci: include pull request id in changelog link (#38504) Signed-off-by: little-meow-meow <204685920+little-meow-meow@users.noreply.github.com> --- Tools/actions_changelogs_since_last_run.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tools/actions_changelogs_since_last_run.py b/Tools/actions_changelogs_since_last_run.py index 8df972d258..4337980c14 100755 --- a/Tools/actions_changelogs_since_last_run.py +++ b/Tools/actions_changelogs_since_last_run.py @@ -189,7 +189,8 @@ def changelog_entries_to_message_lines(entries: Iterable[ChangelogEntry]) -> lis message = message[: DISCORD_SPLIT_LIMIT - 100].rstrip() + " [...]" if url is not None: - line = f"{emoji} - {message} [PR]({url}) \n" + pr_number = url.split("/")[-1] + line = f"{emoji} - {message} ([#{pr_number}]({url}))\n" else: line = f"{emoji} - {message}\n" From 80d733a15211ca96a2668dba009965638ef490b4 Mon Sep 17 00:00:00 2001 From: Andrew Malcolm O'Neill <105134723+maland1@users.noreply.github.com> Date: Wed, 2 Jul 2025 21:50:22 +0100 Subject: [PATCH 163/191] Resolving Wizard casting recall on nuke disk making it impossible to disarm (#38661) * Resolving Wizard Recall on Nuke disk making it impossible to disarm - Adding a DisarmBomb case to nuke status update loop - Changing a few methods and parameters to properly follow formatting standards - Updating some names to follow camelCase * Updating missed tag * Reverting DataField change Should prevent this preventative bugfix being a breaking change. --- Content.Server/Nuke/NukeComponent.cs | 2 +- Content.Server/Nuke/NukeSystem.cs | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Content.Server/Nuke/NukeComponent.cs b/Content.Server/Nuke/NukeComponent.cs index b14ad9c165..cd1c0227eb 100644 --- a/Content.Server/Nuke/NukeComponent.cs +++ b/Content.Server/Nuke/NukeComponent.cs @@ -56,7 +56,7 @@ namespace Content.Server.Nuke /// How long a user must wait to disarm the bomb. /// [DataField("disarmDoafterLength")] - public float DisarmDoafterLength = 30.0f; + public float DisarmDoAfterLength = 30.0f; [DataField("alertLevelOnActivate")] public string AlertLevelOnActivate = default!; [DataField("alertLevelOnDeactivate")] public string AlertLevelOnDeactivate = default!; diff --git a/Content.Server/Nuke/NukeSystem.cs b/Content.Server/Nuke/NukeSystem.cs index 81fbf074da..f110734a60 100644 --- a/Content.Server/Nuke/NukeSystem.cs +++ b/Content.Server/Nuke/NukeSystem.cs @@ -165,7 +165,7 @@ public sealed class NukeSystem : EntitySystem { UpdateUserInterface(uid, component); - if (args.Anchored == false && component.Status == NukeStatus.ARMED && component.RemainingTime > component.DisarmDoafterLength) + if (args.Anchored == false && component.Status == NukeStatus.ARMED && component.RemainingTime > component.DisarmDoAfterLength) { // yes, this means technically if you can find a way to unanchor the nuke, you can disarm it // without the doafter. but that takes some effort, and it won't allow you to disarm a nuke that can't be disarmed by the doafter. @@ -271,7 +271,7 @@ public sealed class NukeSystem : EntitySystem else { - DisarmBombDoafter(uid, args.Actor, component); + DisarmBombDoAfter(uid, args.Actor, component); } } @@ -381,7 +381,12 @@ public sealed class NukeSystem : EntitySystem // do nothing, wait for arm button to be pressed break; case NukeStatus.ARMED: - // do nothing, wait for arm button to be unpressed + // handling case of wizard recalling disk out of armed Nuke + if (!component.DiskSlot.HasItem) + { + DisarmBomb(uid, component); + } + break; } } @@ -409,7 +414,7 @@ public sealed class NukeSystem : EntitySystem AllowArm = allowArm, EnteredCodeLength = component.EnteredCode.Length, MaxCodeLength = component.CodeLength, - CooldownTime = (int) component.CooldownTime + CooldownTime = (int) component.CooldownTime, }; _ui.SetUiState(uid, NukeUiKey.Key, state); @@ -436,7 +441,7 @@ public sealed class NukeSystem : EntitySystem 8 => 9, 9 => 10, 0 => component.LastPlayedKeypadSemitones + 12, - _ => 0 + _ => 0, }; // Don't double-dip on the octave shifting @@ -617,9 +622,9 @@ public sealed class NukeSystem : EntitySystem #endregion - private void DisarmBombDoafter(EntityUid uid, EntityUid user, NukeComponent nuke) + private void DisarmBombDoAfter(EntityUid uid, EntityUid user, NukeComponent nuke) { - var doAfter = new DoAfterArgs(EntityManager, user, nuke.DisarmDoafterLength, new NukeDisarmDoAfterEvent(), uid, target: uid) + var doAfter = new DoAfterArgs(EntityManager, user, nuke.DisarmDoAfterLength, new NukeDisarmDoAfterEvent(), uid, target: uid) { BreakOnDamage = true, BreakOnMove = true, @@ -629,8 +634,10 @@ public sealed class NukeSystem : EntitySystem if (!_doAfter.TryStartDoAfter(doAfter)) return; - _popups.PopupEntity(Loc.GetString("nuke-component-doafter-warning"), user, - user, PopupType.LargeCaution); + _popups.PopupEntity(Loc.GetString("nuke-component-doafter-warning"), + user, + user, + PopupType.LargeCaution); } private void UpdateAppearance(EntityUid uid, NukeComponent nuke) From 8597acd030bc6b3b4cb5c7b1ac0a411db25655c9 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 2 Jul 2025 20:51:29 +0000 Subject: [PATCH 164/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3d67cec87c..c51b0b9058 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Hoodie42 - changes: - - message: The banjo can now be worn on the back or suit storage slots. - type: Tweak - id: 8216 - time: '2025-04-17T14:05:27.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/34057 - author: muburu changes: - message: Space Dragons now have randomly generated names. @@ -3888,3 +3881,11 @@ id: 8728 time: '2025-07-01T10:25:52.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38681 +- author: maland1 + changes: + - message: Fixed Wizard recalling nuke disk from inside an armed nuke making it + impossible to defuse + type: Fix + id: 8729 + time: '2025-07-02T20:50:22.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38661 From 38232d2255206cf31bd43719769d279aaabeb4df Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Thu, 3 Jul 2025 01:20:31 +0200 Subject: [PATCH 165/191] Predict healing and bloodstream (#38690) * initial commit * reapply 38126 * fix rootable * someone missed an important minus sign here * try this * fix * fix * reenable crit hits * cleanup * fix status time dirtying * fix * camelCase --- .../Body/Systems/BloodStreamSystem.cs | 5 + .../Systems/AdminVerbSystem.Smites.cs | 2 +- Content.Server/Atmos/Rotting/RottingSystem.cs | 2 +- .../Body/Components/BloodstreamComponent.cs | 186 ------- .../Body/Components/MetabolizerComponent.cs | 1 + .../Body/Systems/BloodstreamSystem.cs | 485 +--------------- Content.Server/Body/Systems/BodySystem.cs | 1 - .../Chemistry/EntitySystems/InjectorSystem.cs | 5 +- .../SolutionInjectOnEventSystem.cs | 4 +- Content.Server/Devour/DevourSystem.cs | 2 +- .../EntityEffects/EntityEffectSystem.cs | 9 +- .../Fluids/EntitySystems/SmokeSystem.cs | 4 +- .../Forensics/Systems/ForensicsSystem.cs | 2 +- Content.Server/Implants/ImplantedSystem.cs | 2 +- .../BiomassReclaimerSystem.cs | 3 +- .../Medical/Components/HealingComponent.cs | 66 --- Content.Server/Medical/CryoPodSystem.cs | 4 +- .../Medical/HealthAnalyzerSystem.cs | 4 +- Content.Server/Medical/VomitSystem.cs | 1 - .../Mind/TransferMindOnGibSystem.cs | 2 +- .../EntitySystems/SmokingSystem.Vape.cs | 2 +- .../Nutrition/EntitySystems/SmokingSystem.cs | 6 +- Content.Server/Rootable/RootableSystem.cs | 7 +- Content.Server/Silicons/Borgs/BorgSystem.cs | 3 +- .../Zombies/ZombieSystem.Transform.cs | 1 + .../Body/Components/BloodstreamComponent.cs | 200 +++++++ .../Events/ApplyMetabolicMultiplierEvent.cs | 2 +- .../Body/Events}/BeingGibbedEvent.cs | 2 +- .../Body/Systems/SharedBloodstreamSystem.cs | 519 ++++++++++++++++++ Content.Shared/Body/Systems/StomachSystem.cs | 1 + .../EntitySystems/HypospraySystem.cs | 4 +- Content.Shared/Damage/DamageSpecifier.cs | 6 + .../Damage/Systems/DamageableSystem.cs | 2 +- .../Medical/Healing/HealingComponent.cs | 65 +++ .../Medical/Healing}/HealingSystem.cs | 136 +++-- .../medical/components/healing-component.ftl | 8 +- .../Objects/Specific/Medical/healing.yml | 2 +- 37 files changed, 915 insertions(+), 841 deletions(-) create mode 100644 Content.Client/Body/Systems/BloodStreamSystem.cs delete mode 100644 Content.Server/Body/Components/BloodstreamComponent.cs delete mode 100644 Content.Server/Medical/Components/HealingComponent.cs create mode 100644 Content.Shared/Body/Components/BloodstreamComponent.cs rename {Content.Server/Body/Components => Content.Shared/Body/Events}/BeingGibbedEvent.cs (81%) create mode 100644 Content.Shared/Body/Systems/SharedBloodstreamSystem.cs create mode 100644 Content.Shared/Medical/Healing/HealingComponent.cs rename {Content.Server/Medical => Content.Shared/Medical/Healing}/HealingSystem.cs (54%) diff --git a/Content.Client/Body/Systems/BloodStreamSystem.cs b/Content.Client/Body/Systems/BloodStreamSystem.cs new file mode 100644 index 0000000000..85f4f6198e --- /dev/null +++ b/Content.Client/Body/Systems/BloodStreamSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Body.Systems; + +namespace Content.Client.Body.Systems; + +public sealed class BloodstreamSystem : SharedBloodstreamSystem; diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index 442c768709..b764c7f68d 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -273,7 +273,7 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new ("/Textures/Fluids/tomato_splat.rsi"), "puddle-1"), Act = () => { - _bloodstreamSystem.SpillAllSolutions(args.Target, bloodstream); + _bloodstreamSystem.SpillAllSolutions((args.Target, bloodstream)); var xform = Transform(args.Target); _popupSystem.PopupEntity(Loc.GetString("admin-smite-remove-blood-self"), args.Target, args.Target, PopupType.LargeCaution); diff --git a/Content.Server/Atmos/Rotting/RottingSystem.cs b/Content.Server/Atmos/Rotting/RottingSystem.cs index 43dddce4a4..6f14debc3d 100644 --- a/Content.Server/Atmos/Rotting/RottingSystem.cs +++ b/Content.Server/Atmos/Rotting/RottingSystem.cs @@ -1,8 +1,8 @@ using Content.Server.Atmos.EntitySystems; -using Content.Server.Body.Components; using Content.Server.Temperature.Components; using Content.Shared.Atmos; using Content.Shared.Atmos.Rotting; +using Content.Shared.Body.Events; using Content.Shared.Damage; using Robust.Server.Containers; using Robust.Shared.Physics.Components; diff --git a/Content.Server/Body/Components/BloodstreamComponent.cs b/Content.Server/Body/Components/BloodstreamComponent.cs deleted file mode 100644 index 35e76403bb..0000000000 --- a/Content.Server/Body/Components/BloodstreamComponent.cs +++ /dev/null @@ -1,186 +0,0 @@ -using Content.Server.Body.Systems; -using Content.Server.Chemistry.EntitySystems; -using Content.Shared.Alert; -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Reagent; -using Content.Shared.Damage; -using Content.Shared.Damage.Prototypes; -using Content.Shared.FixedPoint; -using Robust.Shared.Audio; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; - -namespace Content.Server.Body.Components -{ - [RegisterComponent, Access(typeof(BloodstreamSystem), typeof(ReactionMixerSystem))] - public sealed partial class BloodstreamComponent : Component - { - public static string DefaultChemicalsSolutionName = "chemicals"; - public static string DefaultBloodSolutionName = "bloodstream"; - public static string DefaultBloodTemporarySolutionName = "bloodstreamTemporary"; - - /// - /// The next time that blood level will be updated and bloodloss damage dealt. - /// - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] - public TimeSpan NextUpdate; - - /// - /// The interval at which this component updates. - /// - [DataField] - public TimeSpan UpdateInterval = TimeSpan.FromSeconds(3); - - /// - /// How much is this entity currently bleeding? - /// Higher numbers mean more blood lost every tick. - /// - /// Goes down slowly over time, and items like bandages - /// or clotting reagents can lower bleeding. - /// - /// - /// This generally corresponds to an amount of damage and can't go above 100. - /// - [ViewVariables(VVAccess.ReadWrite)] - public float BleedAmount; - - /// - /// How much should bleeding be reduced every update interval? - /// - [DataField] - public float BleedReductionAmount = 0.33f; - - /// - /// How high can go? - /// - [DataField] - public float MaxBleedAmount = 10.0f; - - /// - /// What percentage of current blood is necessary to avoid dealing blood loss damage? - /// - [DataField] - public float BloodlossThreshold = 0.9f; - - /// - /// The base bloodloss damage to be incurred if below - /// The default values are defined per mob/species in YML. - /// - [DataField(required: true)] - public DamageSpecifier BloodlossDamage = new(); - - /// - /// The base bloodloss damage to be healed if above - /// The default values are defined per mob/species in YML. - /// - [DataField(required: true)] - public DamageSpecifier BloodlossHealDamage = new(); - - // TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth. - /// - /// How much reagent of blood should be restored each update interval? - /// - [DataField] - public FixedPoint2 BloodRefreshAmount = 1.0f; - - /// - /// How much blood needs to be in the temporary solution in order to create a puddle? - /// - [DataField] - public FixedPoint2 BleedPuddleThreshold = 1.0f; - - /// - /// A modifier set prototype ID corresponding to how damage should be modified - /// before taking it into account for bloodloss. - /// - /// - /// For example, piercing damage is increased while poison damage is nullified entirely. - /// - [DataField] - public ProtoId DamageBleedModifiers = "BloodlossHuman"; - - /// - /// The sound to be played when a weapon instantly deals blood loss damage. - /// - [DataField] - public SoundSpecifier InstantBloodSound = new SoundCollectionSpecifier("blood"); - - /// - /// The sound to be played when some damage actually heals bleeding rather than starting it. - /// - [DataField] - public SoundSpecifier BloodHealedSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"); - - /// - /// The minimum amount damage reduction needed to play the healing sound/popup. - /// This prevents tiny amounts of heat damage from spamming the sound, e.g. spacing. - /// - [DataField] - public float BloodHealedSoundThreshold = -0.1f; - - // TODO probably damage bleed thresholds. - - /// - /// Max volume of internal chemical solution storage - /// - [DataField] - public FixedPoint2 ChemicalMaxVolume = FixedPoint2.New(250); - - /// - /// Max volume of internal blood storage, - /// and starting level of blood. - /// - [DataField] - public FixedPoint2 BloodMaxVolume = FixedPoint2.New(300); - - /// - /// Which reagent is considered this entities 'blood'? - /// - /// - /// Slime-people might use slime as their blood or something like that. - /// - [DataField] - public ProtoId BloodReagent = "Blood"; - - /// Name/Key that is indexed by. - [DataField] - public string BloodSolutionName = DefaultBloodSolutionName; - - /// Name/Key that is indexed by. - [DataField] - public string ChemicalSolutionName = DefaultChemicalsSolutionName; - - /// Name/Key that is indexed by. - [DataField] - public string BloodTemporarySolutionName = DefaultBloodTemporarySolutionName; - - /// - /// Internal solution for blood storage - /// - [ViewVariables] - public Entity? BloodSolution; - - /// - /// Internal solution for reagent storage - /// - [ViewVariables] - public Entity? ChemicalSolution; - - /// - /// Temporary blood solution. - /// When blood is lost, it goes to this solution, and when this - /// solution hits a certain cap, the blood is actually spilled as a puddle. - /// - [ViewVariables] - public Entity? TemporarySolution; - - /// - /// Variable that stores the amount of status time added by having a low blood level. - /// - [ViewVariables(VVAccess.ReadWrite)] - public TimeSpan StatusTime; - - [DataField] - public ProtoId BleedingAlert = "Bleed"; - } -} diff --git a/Content.Server/Body/Components/MetabolizerComponent.cs b/Content.Server/Body/Components/MetabolizerComponent.cs index 90c99df7db..3699267ebf 100644 --- a/Content.Server/Body/Components/MetabolizerComponent.cs +++ b/Content.Server/Body/Components/MetabolizerComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Body.Components; using Content.Server.Body.Systems; using Content.Shared.Body.Prototypes; using Content.Shared.FixedPoint; diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs index 6d85affad3..c2185750af 100644 --- a/Content.Server/Body/Systems/BloodstreamSystem.cs +++ b/Content.Server/Body/Systems/BloodstreamSystem.cs @@ -1,189 +1,32 @@ -using Content.Server.Body.Components; -using Content.Server.Fluids.EntitySystems; -using Content.Server.Popups; -using Content.Shared.Alert; -using Content.Shared.Body.Events; -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Chemistry.Reaction; +using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Damage; -using Content.Shared.Damage.Prototypes; -using Content.Shared.Drunk; -using Content.Shared.EntityEffects.Effects; -using Content.Shared.FixedPoint; using Content.Shared.Forensics; -using Content.Shared.Forensics.Components; -using Content.Shared.HealthExaminable; -using Content.Shared.Mobs.Systems; -using Content.Shared.Popups; -using Content.Shared.Rejuvenate; -using Content.Shared.Speech.EntitySystems; -using Robust.Server.Audio; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; namespace Content.Server.Body.Systems; -public sealed class BloodstreamSystem : EntitySystem +public sealed class BloodstreamSystem : SharedBloodstreamSystem { - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; - [Dependency] private readonly AudioSystem _audio = default!; - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly PuddleSystem _puddleSystem = default!; - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly SharedDrunkSystem _drunkSystem = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; - [Dependency] private readonly SharedStutteringSystem _stutteringSystem = default!; - [Dependency] private readonly AlertsSystem _alertsSystem = default!; - public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnUnpaused); - SubscribeLocalEvent(OnDamageChanged); - SubscribeLocalEvent(OnHealthBeingExamined); - SubscribeLocalEvent(OnBeingGibbed); - SubscribeLocalEvent(OnApplyMetabolicMultiplier); - SubscribeLocalEvent(OnReactionAttempt); - SubscribeLocalEvent>(OnReactionAttempt); - SubscribeLocalEvent(OnRejuvenate); SubscribeLocalEvent(OnDnaGenerated); } - private void OnMapInit(Entity ent, ref MapInitEvent args) - { - ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval; - } - - private void OnUnpaused(Entity ent, ref EntityUnpausedEvent args) - { - ent.Comp.NextUpdate += args.PausedTime; - } - - private void OnReactionAttempt(Entity entity, ref ReactionAttemptEvent args) - { - if (args.Cancelled) - return; - - foreach (var effect in args.Reaction.Effects) - { - switch (effect) - { - case CreateEntityReactionEffect: // Prevent entities from spawning in the bloodstream - case AreaReactionEffect: // No spontaneous smoke or foam leaking out of blood vessels. - args.Cancelled = true; - return; - } - } - - // The area-reaction effect canceling is part of avoiding smoke-fork-bombs (create two smoke bombs, that when - // ingested by mobs create more smoke). This also used to act as a rapid chemical-purge, because all the - // reagents would get carried away by the smoke/foam. This does still work for the stomach (I guess people vomit - // up the smoke or spawned entities?). - - // TODO apply organ damage instead of just blocking the reaction? - // Having cheese-clots form in your veins can't be good for you. - } - - private void OnReactionAttempt(Entity entity, ref SolutionRelayEvent args) - { - if (args.Name != entity.Comp.BloodSolutionName - && args.Name != entity.Comp.ChemicalSolutionName - && args.Name != entity.Comp.BloodTemporarySolutionName) - { - return; - } - - OnReactionAttempt(entity, ref args.Event); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var bloodstream)) - { - if (_gameTiming.CurTime < bloodstream.NextUpdate) - continue; - - bloodstream.NextUpdate += bloodstream.UpdateInterval; - - if (!_solutionContainerSystem.ResolveSolution(uid, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution)) - continue; - - // Adds blood to their blood level if it is below the maximum; Blood regeneration. Must be alive. - if (bloodSolution.Volume < bloodSolution.MaxVolume && !_mobStateSystem.IsDead(uid)) - { - TryModifyBloodLevel(uid, bloodstream.BloodRefreshAmount, bloodstream); - } - - // Removes blood from the bloodstream based on bleed amount (bleed rate) - // as well as stop their bleeding to a certain extent. - if (bloodstream.BleedAmount > 0) - { - // Blood is removed from the bloodstream at a 1-1 rate with the bleed amount - TryModifyBloodLevel(uid, (-bloodstream.BleedAmount), bloodstream); - // Bleed rate is reduced by the bleed reduction amount in the bloodstream component. - TryModifyBleedAmount(uid, -bloodstream.BleedReductionAmount, bloodstream); - } - - // deal bloodloss damage if their blood level is below a threshold. - var bloodPercentage = GetBloodLevelPercentage(uid, bloodstream); - if (bloodPercentage < bloodstream.BloodlossThreshold && !_mobStateSystem.IsDead(uid)) - { - // bloodloss damage is based on the base value, and modified by how low your blood level is. - var amt = bloodstream.BloodlossDamage / (0.1f + bloodPercentage); - - _damageableSystem.TryChangeDamage(uid, amt, - ignoreResistances: false, interruptsDoAfters: false); - - // Apply dizziness as a symptom of bloodloss. - // The effect is applied in a way that it will never be cleared without being healthy. - // Multiplying by 2 is arbitrary but works for this case, it just prevents the time from running out - _drunkSystem.TryApplyDrunkenness( - uid, - (float) bloodstream.UpdateInterval.TotalSeconds * 2, - applySlur: false); - _stutteringSystem.DoStutter(uid, bloodstream.UpdateInterval * 2, refresh: false); - - // storing the drunk and stutter time so we can remove it independently from other effects additions - bloodstream.StatusTime += bloodstream.UpdateInterval * 2; - } - else if (!_mobStateSystem.IsDead(uid)) - { - // If they're healthy, we'll try and heal some bloodloss instead. - _damageableSystem.TryChangeDamage( - uid, - bloodstream.BloodlossHealDamage * bloodPercentage, - ignoreResistances: true, interruptsDoAfters: false); - - // Remove the drunk effect when healthy. Should only remove the amount of drunk and stutter added by low blood level - _drunkSystem.TryRemoveDrunkenessTime(uid, bloodstream.StatusTime.TotalSeconds); - _stutteringSystem.DoRemoveStutterTime(uid, bloodstream.StatusTime.TotalSeconds); - // Reset the drunk and stutter time to zero - bloodstream.StatusTime = TimeSpan.Zero; - } - } - } - + // not sure if we can move this to shared or not + // it would certainly help if SolutionContainer was documented + // but since we usually don't add the component dynamically to entities we can keep this unpredicted for now private void OnComponentInit(Entity entity, ref ComponentInit args) { - if (!_solutionContainerSystem.EnsureSolution(entity.Owner, + if (!SolutionContainer.EnsureSolution(entity.Owner, entity.Comp.ChemicalSolutionName, out var chemicalSolution) || - !_solutionContainerSystem.EnsureSolution(entity.Owner, + !SolutionContainer.EnsureSolution(entity.Owner, entity.Comp.BloodSolutionName, out var bloodSolution) || - !_solutionContainerSystem.EnsureSolution(entity.Owner, + !SolutionContainer.EnsureSolution(entity.Owner, entity.Comp.BloodTemporarySolutionName, out var tempSolution)) return; @@ -197,298 +40,10 @@ public sealed class BloodstreamSystem : EntitySystem bloodSolution.AddReagent(new ReagentId(entity.Comp.BloodReagent, GetEntityBloodData(entity.Owner)), entity.Comp.BloodMaxVolume - bloodSolution.Volume); } - private void OnDamageChanged(Entity ent, ref DamageChangedEvent args) - { - if (args.DamageDelta is null || !args.DamageIncreased) - { - return; - } - - // TODO probably cache this or something. humans get hurt a lot - if (!_prototypeManager.TryIndex(ent.Comp.DamageBleedModifiers, out var modifiers)) - return; - - // some reagents may deal and heal different damage types in the same tick, which means DamageIncreased will be true - // but we only want to consider the dealt damage when causing bleeding - var damage = DamageSpecifier.GetPositive(args.DamageDelta); - var bloodloss = DamageSpecifier.ApplyModifierSet(damage, modifiers); - - if (bloodloss.Empty) - return; - - // Does the calculation of how much bleed rate should be added/removed, then applies it - var oldBleedAmount = ent.Comp.BleedAmount; - var total = bloodloss.GetTotal(); - var totalFloat = total.Float(); - TryModifyBleedAmount(ent, totalFloat, ent); - - /// - /// Critical hit. Causes target to lose blood, using the bleed rate modifier of the weapon, currently divided by 5 - /// The crit chance is currently the bleed rate modifier divided by 25. - /// Higher damage weapons have a higher chance to crit! - /// - var prob = Math.Clamp(totalFloat / 25, 0, 1); - if (totalFloat > 0 && _robustRandom.Prob(prob)) - { - TryModifyBloodLevel(ent, -total / 5, ent); - _audio.PlayPvs(ent.Comp.InstantBloodSound, ent); - } - - // Heat damage will cauterize, causing the bleed rate to be reduced. - else if (totalFloat <= ent.Comp.BloodHealedSoundThreshold && oldBleedAmount > 0) - { - // Magically, this damage has healed some bleeding, likely - // because it's burn damage that cauterized their wounds. - - // We'll play a special sound and popup for feedback. - _audio.PlayPvs(ent.Comp.BloodHealedSound, ent); - _popupSystem.PopupEntity(Loc.GetString("bloodstream-component-wounds-cauterized"), ent, - ent, PopupType.Medium); - } - } - /// - /// Shows text on health examine, based on bleed rate and blood level. - /// - private void OnHealthBeingExamined(Entity ent, ref HealthBeingExaminedEvent args) - { - // Shows massively bleeding at 0.75x the max bleed rate. - if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount * 0.75f) - { - args.Message.PushNewline(); - args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-massive-bleeding", ("target", ent.Owner))); - } - // Shows bleeding message when bleeding above half the max rate, but less than massively. - else if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount * 0.5f) - { - args.Message.PushNewline(); - args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-strong-bleeding", ("target", ent.Owner))); - } - // Shows bleeding message when bleeding above 0.25x the max rate, but less than half the max. - else if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount * 0.25f) - { - args.Message.PushNewline(); - args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-bleeding", ("target", ent.Owner))); - } - // Shows bleeding message when bleeding below 0.25x the max cap - else if (ent.Comp.BleedAmount > 0) - { - args.Message.PushNewline(); - args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-slight-bleeding", ("target", ent.Owner))); - } - - // If the mob's blood level is below the damage threshhold, the pale message is added. - if (GetBloodLevelPercentage(ent, ent) < ent.Comp.BloodlossThreshold) - { - args.Message.PushNewline(); - args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-looks-pale", ("target", ent.Owner))); - } - } - - private void OnBeingGibbed(Entity ent, ref BeingGibbedEvent args) - { - SpillAllSolutions(ent, ent); - } - - private void OnApplyMetabolicMultiplier( - Entity ent, - ref ApplyMetabolicMultiplierEvent args) - { - // TODO REFACTOR THIS - // This will slowly drift over time due to floating point errors. - // Instead, raise an event with the base rates and allow modifiers to get applied to it. - if (args.Apply) - { - ent.Comp.UpdateInterval *= args.Multiplier; - return; - } - ent.Comp.UpdateInterval /= args.Multiplier; - } - - private void OnRejuvenate(Entity entity, ref RejuvenateEvent args) - { - TryModifyBleedAmount(entity.Owner, -entity.Comp.BleedAmount, entity.Comp); - - if (_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.BloodSolutionName, ref entity.Comp.BloodSolution, out var bloodSolution)) - TryModifyBloodLevel(entity.Owner, bloodSolution.AvailableVolume, entity.Comp); - - if (_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.ChemicalSolutionName, ref entity.Comp.ChemicalSolution)) - _solutionContainerSystem.RemoveAllSolution(entity.Comp.ChemicalSolution.Value); - } - - /// - /// Attempt to transfer provided solution to internal solution. - /// - public bool TryAddToChemicals(EntityUid uid, Solution solution, BloodstreamComponent? component = null) - { - return Resolve(uid, ref component, logMissing: false) - && _solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution) - && _solutionContainerSystem.TryAddSolution(component.ChemicalSolution.Value, solution); - } - - public bool FlushChemicals(EntityUid uid, string excludedReagentID, FixedPoint2 quantity, BloodstreamComponent? component = null) - { - if (!Resolve(uid, ref component, logMissing: false) - || !_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution)) - return false; - - for (var i = chemSolution.Contents.Count - 1; i >= 0; i--) - { - var (reagentId, _) = chemSolution.Contents[i]; - if (reagentId.Prototype != excludedReagentID) - { - _solutionContainerSystem.RemoveReagent(component.ChemicalSolution.Value, reagentId, quantity); - } - } - - return true; - } - - public float GetBloodLevelPercentage(EntityUid uid, BloodstreamComponent? component = null) - { - if (!Resolve(uid, ref component) - || !_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution)) - { - return 0.0f; - } - - return bloodSolution.FillFraction; - } - - public void SetBloodLossThreshold(EntityUid uid, float threshold, BloodstreamComponent? comp = null) - { - if (!Resolve(uid, ref comp)) - return; - - comp.BloodlossThreshold = threshold; - } - - /// - /// Attempts to modify the blood level of this entity directly. - /// - public bool TryModifyBloodLevel(EntityUid uid, FixedPoint2 amount, BloodstreamComponent? component = null) - { - if (!Resolve(uid, ref component, logMissing: false) - || !_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution)) - { - return false; - } - - if (amount >= 0) - return _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, amount, null, GetEntityBloodData(uid)); - - // Removal is more involved, - // since we also wanna handle moving it to the temporary solution - // and then spilling it if necessary. - var newSol = _solutionContainerSystem.SplitSolution(component.BloodSolution.Value, -amount); - - if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodTemporarySolutionName, ref component.TemporarySolution, out var tempSolution)) - return true; - - tempSolution.AddSolution(newSol, _prototypeManager); - - if (tempSolution.Volume > component.BleedPuddleThreshold) - { - // Pass some of the chemstream into the spilled blood. - if (_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution)) - { - var temp = _solutionContainerSystem.SplitSolution(component.ChemicalSolution.Value, tempSolution.Volume / 10); - tempSolution.AddSolution(temp, _prototypeManager); - } - - _puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, sound: false); - - tempSolution.RemoveAllSolution(); - } - - _solutionContainerSystem.UpdateChemicals(component.TemporarySolution.Value); - - return true; - } - - /// - /// Tries to make an entity bleed more or less - /// - public bool TryModifyBleedAmount(EntityUid uid, float amount, BloodstreamComponent? component = null) - { - if (!Resolve(uid, ref component, logMissing: false)) - return false; - - component.BleedAmount += amount; - component.BleedAmount = Math.Clamp(component.BleedAmount, 0, component.MaxBleedAmount); - - if (component.BleedAmount == 0) - _alertsSystem.ClearAlert(uid, component.BleedingAlert); - else - { - var severity = (short) Math.Clamp(Math.Round(component.BleedAmount, MidpointRounding.ToZero), 0, 10); - _alertsSystem.ShowAlert(uid, component.BleedingAlert, severity); - } - - return true; - } - - /// - /// BLOOD FOR THE BLOOD GOD - /// - public void SpillAllSolutions(EntityUid uid, BloodstreamComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - var tempSol = new Solution(); - - if (_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution)) - { - tempSol.MaxVolume += bloodSolution.MaxVolume; - tempSol.AddSolution(bloodSolution, _prototypeManager); - _solutionContainerSystem.RemoveAllSolution(component.BloodSolution.Value); - } - - if (_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution)) - { - tempSol.MaxVolume += chemSolution.MaxVolume; - tempSol.AddSolution(chemSolution, _prototypeManager); - _solutionContainerSystem.RemoveAllSolution(component.ChemicalSolution.Value); - } - - if (_solutionContainerSystem.ResolveSolution(uid, component.BloodTemporarySolutionName, ref component.TemporarySolution, out var tempSolution)) - { - tempSol.MaxVolume += tempSolution.MaxVolume; - tempSol.AddSolution(tempSolution, _prototypeManager); - _solutionContainerSystem.RemoveAllSolution(component.TemporarySolution.Value); - } - - _puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid); - } - - /// - /// Change what someone's blood is made of, on the fly. - /// - public void ChangeBloodReagent(EntityUid uid, string reagent, BloodstreamComponent? component = null) - { - if (!Resolve(uid, ref component, logMissing: false) - || reagent == component.BloodReagent) - { - return; - } - - if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution)) - { - component.BloodReagent = reagent; - return; - } - - var currentVolume = bloodSolution.RemoveReagent(component.BloodReagent, bloodSolution.Volume, ignoreReagentData: true); - - component.BloodReagent = reagent; - - if (currentVolume > 0) - _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, currentVolume, null, GetEntityBloodData(uid)); - } - + // forensics is not predicted yet private void OnDnaGenerated(Entity entity, ref GenerateDnaEvent args) { - if (_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.BloodSolutionName, ref entity.Comp.BloodSolution, out var bloodSolution)) + if (SolutionContainer.ResolveSolution(entity.Owner, entity.Comp.BloodSolutionName, ref entity.Comp.BloodSolution, out var bloodSolution)) { foreach (var reagent in bloodSolution.Contents) { @@ -500,22 +55,4 @@ public sealed class BloodstreamSystem : EntitySystem else Log.Error("Unable to set bloodstream DNA, solution entity could not be resolved"); } - - /// - /// Get the reagent data for blood that a specific entity should have. - /// - public List GetEntityBloodData(EntityUid uid) - { - var bloodData = new List(); - var dnaData = new DnaData(); - - if (TryComp(uid, out var donorComp) && donorComp.DNA != null) - dnaData.DNA = donorComp.DNA; - else - dnaData.DNA = Loc.GetString("forensics-dna-unknown"); - - bloodData.Add(dnaData); - - return bloodData; - } } diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs index d2fc3d6558..957dea9d41 100644 --- a/Content.Server/Body/Systems/BodySystem.cs +++ b/Content.Server/Body/Systems/BodySystem.cs @@ -1,5 +1,4 @@ using System.Numerics; -using Content.Server.Body.Components; using Content.Server.Ghost; using Content.Server.Humanoid; using Content.Shared.Body.Components; diff --git a/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs b/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs index f53688a241..7b43e7f092 100644 --- a/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs @@ -1,10 +1,9 @@ -using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Chemistry.Reagent; +using Content.Shared.Body.Components; using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.FixedPoint; @@ -237,7 +236,7 @@ public sealed class InjectorSystem : SharedInjectorSystem // Move units from attackSolution to targetSolution var removedSolution = SolutionContainers.SplitSolution(target.Comp.ChemicalSolution.Value, realTransferAmount); - _blood.TryAddToChemicals(target, removedSolution, target.Comp); + _blood.TryAddToChemicals(target.AsNullable(), removedSolution); _reactiveSystem.DoEntityReaction(target, removedSolution, ReactionMethod.Injection); diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs index 503a0ebde6..7b4deea9f4 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs @@ -1,7 +1,7 @@ -using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Server.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Body.Components; using Content.Shared.Chemistry.Events; using Content.Shared.Inventory; using Content.Shared.Popups; @@ -148,7 +148,7 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem // Take our portion of the adjusted solution for this target var individualInjection = solutionToInject.SplitSolution(volumePerBloodstream); // Inject our portion into the target's bloodstream - if (_bloodstream.TryAddToChemicals(targetBloodstream.Owner, individualInjection, targetBloodstream.Comp)) + if (_bloodstream.TryAddToChemicals(targetBloodstream.AsNullable(), individualInjection)) anySuccess = true; } diff --git a/Content.Server/Devour/DevourSystem.cs b/Content.Server/Devour/DevourSystem.cs index 8ee4cf852b..88edc3ec4c 100644 --- a/Content.Server/Devour/DevourSystem.cs +++ b/Content.Server/Devour/DevourSystem.cs @@ -1,5 +1,5 @@ -using Content.Server.Body.Components; using Content.Server.Body.Systems; +using Content.Shared.Body.Events; using Content.Shared.Chemistry.Components; using Content.Shared.Devour; using Content.Shared.Devour.Components; diff --git a/Content.Server/EntityEffects/EntityEffectSystem.cs b/Content.Server/EntityEffects/EntityEffectSystem.cs index 98853a0e61..e18b3b1470 100644 --- a/Content.Server/EntityEffects/EntityEffectSystem.cs +++ b/Content.Server/EntityEffects/EntityEffectSystem.cs @@ -22,6 +22,7 @@ using Content.Server.Temperature.Systems; using Content.Server.Traits.Assorted; using Content.Server.Zombies; using Content.Shared.Atmos; +using Content.Shared.Body.Components; using Content.Shared.Coordinates.Helpers; using Content.Shared.EntityEffects.EffectConditions; using Content.Shared.EntityEffects.Effects.PlantMetabolism; @@ -558,11 +559,11 @@ public sealed class EntityEffectSystem : EntitySystem return; cleanseRate *= reagentArgs.Scale.Float(); - _bloodstream.FlushChemicals(args.Args.TargetEntity, reagentArgs.Reagent.ID, cleanseRate); + _bloodstream.FlushChemicals(args.Args.TargetEntity, reagentArgs.Reagent, cleanseRate); } else { - _bloodstream.FlushChemicals(args.Args.TargetEntity, "", cleanseRate); + _bloodstream.FlushChemicals(args.Args.TargetEntity, null, cleanseRate); } } @@ -780,7 +781,7 @@ public sealed class EntityEffectSystem : EntitySystem amt *= reagentArgs.Scale.Float(); } - _bloodstream.TryModifyBleedAmount(args.Args.TargetEntity, amt, blood); + _bloodstream.TryModifyBleedAmount((args.Args.TargetEntity, blood), amt); } } @@ -796,7 +797,7 @@ public sealed class EntityEffectSystem : EntitySystem amt *= reagentArgs.Scale; } - _bloodstream.TryModifyBloodLevel(args.Args.TargetEntity, amt, blood); + _bloodstream.TryModifyBloodLevel((args.Args.TargetEntity, blood), amt); } } diff --git a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs index dfbad1bbe9..13695caff1 100644 --- a/Content.Server/Fluids/EntitySystems/SmokeSystem.cs +++ b/Content.Server/Fluids/EntitySystems/SmokeSystem.cs @@ -1,8 +1,8 @@ using Content.Server.Administration.Logs; -using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Shared.EntityEffects.Effects; using Content.Server.Spreader; +using Content.Shared.Body.Components; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; @@ -288,7 +288,7 @@ public sealed class SmokeSystem : EntitySystem if (blockIngestion) return; - if (_blood.TryAddToChemicals(entity, transferSolution, bloodstream)) + if (_blood.TryAddToChemicals((entity, bloodstream), transferSolution)) { // Log solution addition by smoke _logger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(entity):target} ingested smoke {SharedSolutionContainerSystem.ToPrettyString(transferSolution)}"); diff --git a/Content.Server/Forensics/Systems/ForensicsSystem.cs b/Content.Server/Forensics/Systems/ForensicsSystem.cs index ec4683460b..9f94e39fb7 100644 --- a/Content.Server/Forensics/Systems/ForensicsSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicsSystem.cs @@ -1,9 +1,9 @@ -using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Server.DoAfter; using Content.Server.Fluids.EntitySystems; using Content.Server.Forensics.Components; using Content.Server.Popups; +using Content.Shared.Body.Events; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Popups; using Content.Shared.Chemistry.Components; diff --git a/Content.Server/Implants/ImplantedSystem.cs b/Content.Server/Implants/ImplantedSystem.cs index c5048cbd8d..7851758730 100644 --- a/Content.Server/Implants/ImplantedSystem.cs +++ b/Content.Server/Implants/ImplantedSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.Body.Components; +using Content.Shared.Body.Events; using Content.Shared.Implants.Components; using Content.Shared.Storage; using Robust.Shared.Containers; diff --git a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs index f6b751c398..42e455a47f 100644 --- a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs +++ b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs @@ -1,11 +1,11 @@ using System.Numerics; -using Content.Server.Body.Components; using Content.Server.Botany.Components; using Content.Server.Fluids.EntitySystems; using Content.Server.Materials; using Content.Server.Power.Components; using Content.Shared.Administration.Logs; using Content.Shared.Audio; +using Content.Shared.Body.Components; using Content.Shared.CCVar; using Content.Shared.Chemistry.Components; using Content.Shared.Climbing.Events; @@ -30,7 +30,6 @@ using Robust.Server.Player; using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; using Robust.Shared.Physics.Components; -using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server.Medical.BiomassReclaimer diff --git a/Content.Server/Medical/Components/HealingComponent.cs b/Content.Server/Medical/Components/HealingComponent.cs deleted file mode 100644 index a56bc17143..0000000000 --- a/Content.Server/Medical/Components/HealingComponent.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Content.Shared.Damage; -using Content.Shared.Damage.Prototypes; -using Robust.Shared.Audio; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; - -namespace Content.Server.Medical.Components -{ - /// - /// Applies a damage change to the target when used in an interaction. - /// - [RegisterComponent] - public sealed partial class HealingComponent : Component - { - [DataField("damage", required: true)] - [ViewVariables(VVAccess.ReadWrite)] - public DamageSpecifier Damage = default!; - - /// - /// This should generally be negative, - /// since you're, like, trying to heal damage. - /// - [DataField("bloodlossModifier")] - [ViewVariables(VVAccess.ReadWrite)] - public float BloodlossModifier = 0.0f; - - /// - /// Restore missing blood. - /// - [DataField("ModifyBloodLevel")] - [ViewVariables(VVAccess.ReadWrite)] - public float ModifyBloodLevel = 0.0f; - - /// - /// The supported damage types are specified using a s. For a - /// HealingComponent this filters what damage container type this component should work on. If null, - /// all damage container types are supported. - /// - [DataField("damageContainers", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List? DamageContainers; - - /// - /// How long it takes to apply the damage. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("delay")] - public float Delay = 3f; - - /// - /// Delay multiplier when healing yourself. - /// - [DataField("selfHealPenaltyMultiplier")] - public float SelfHealPenaltyMultiplier = 3f; - - /// - /// Sound played on healing begin - /// - [DataField("healingBeginSound")] - public SoundSpecifier? HealingBeginSound = null; - - /// - /// Sound played on healing end - /// - [DataField("healingEndSound")] - public SoundSpecifier? HealingEndSound = null; - } -} diff --git a/Content.Server/Medical/CryoPodSystem.cs b/Content.Server/Medical/CryoPodSystem.cs index 8dd0330a27..1160f6aa17 100644 --- a/Content.Server/Medical/CryoPodSystem.cs +++ b/Content.Server/Medical/CryoPodSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Administration.Logs; using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Piping.Components; using Content.Server.Atmos.Piping.Unary.EntitySystems; -using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Server.Medical.Components; using Content.Server.NodeContainer.EntitySystems; @@ -10,6 +9,7 @@ using Content.Server.NodeContainer.NodeGroups; using Content.Server.NodeContainer.Nodes; using Content.Server.Temperature.Components; using Content.Shared.Atmos; +using Content.Shared.Body.Components; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; @@ -116,7 +116,7 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem } var solutionToInject = _solutionContainerSystem.SplitSolution(containerSolution.Value, cryoPod.BeakerTransferAmount); - _bloodstreamSystem.TryAddToChemicals(patient.Value, solutionToInject, bloodstream); + _bloodstreamSystem.TryAddToChemicals((patient.Value, bloodstream), solutionToInject); _reactiveSystem.DoEntityReaction(patient.Value, solutionToInject, ReactionMethod.Injection); } } diff --git a/Content.Server/Medical/HealthAnalyzerSystem.cs b/Content.Server/Medical/HealthAnalyzerSystem.cs index f2235363ad..11e4ed4fcf 100644 --- a/Content.Server/Medical/HealthAnalyzerSystem.cs +++ b/Content.Server/Medical/HealthAnalyzerSystem.cs @@ -1,8 +1,7 @@ -using Content.Server.Body.Components; using Content.Server.Medical.Components; using Content.Server.PowerCell; using Content.Server.Temperature.Components; -using Content.Shared.Traits.Assorted; +using Content.Shared.Body.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Damage; using Content.Shared.DoAfter; @@ -14,6 +13,7 @@ using Content.Shared.Item.ItemToggle.Components; using Content.Shared.MedicalScanner; using Content.Shared.Mobs.Components; using Content.Shared.Popups; +using Content.Shared.Traits.Assorted; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; diff --git a/Content.Server/Medical/VomitSystem.cs b/Content.Server/Medical/VomitSystem.cs index 9d27247a38..66cc9bf5f6 100644 --- a/Content.Server/Medical/VomitSystem.cs +++ b/Content.Server/Medical/VomitSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Server.Fluids.EntitySystems; using Content.Server.Forensics; diff --git a/Content.Server/Mind/TransferMindOnGibSystem.cs b/Content.Server/Mind/TransferMindOnGibSystem.cs index 758a23c868..ff71fa0560 100644 --- a/Content.Server/Mind/TransferMindOnGibSystem.cs +++ b/Content.Server/Mind/TransferMindOnGibSystem.cs @@ -1,5 +1,5 @@ using System.Linq; -using Content.Server.Body.Components; +using Content.Shared.Body.Events; using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Tag; diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs index fa5ec0a1bf..5a269eace5 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs @@ -1,8 +1,8 @@ -using Content.Server.Body.Components; using Content.Server.DoAfter; using Content.Server.Explosion.EntitySystems; using Content.Server.Nutrition.Components; using Content.Server.Popups; +using Content.Shared.Body.Components; using Content.Shared.Atmos; using Content.Shared.Damage; using Content.Shared.DoAfter; diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs index b3ef8bff69..4960ff1ba8 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs @@ -1,10 +1,9 @@ using Content.Server.Atmos.EntitySystems; -using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Shared.Chemistry.EntitySystems; using Content.Server.Forensics; +using Content.Shared.Body.Components; using Content.Shared.Chemistry; -using Content.Shared.Chemistry.Reagent; using Content.Shared.Clothing.Components; using Content.Shared.Clothing.EntitySystems; using Content.Shared.FixedPoint; @@ -17,7 +16,6 @@ using Content.Shared.Temperature; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; -using System.Linq; using Content.Shared.Atmos; namespace Content.Server.Nutrition.EntitySystems @@ -159,7 +157,7 @@ namespace Content.Server.Nutrition.EntitySystems } _reactiveSystem.DoEntityReaction(containerManager.Owner, inhaledSolution, ReactionMethod.Ingestion); - _bloodstreamSystem.TryAddToChemicals(containerManager.Owner, inhaledSolution, bloodstream); + _bloodstreamSystem.TryAddToChemicals((containerManager.Owner, bloodstream), inhaledSolution); } _timer -= UpdateTimer; diff --git a/Content.Server/Rootable/RootableSystem.cs b/Content.Server/Rootable/RootableSystem.cs index ce88f18dc3..cd18315bd0 100644 --- a/Content.Server/Rootable/RootableSystem.cs +++ b/Content.Server/Rootable/RootableSystem.cs @@ -1,6 +1,6 @@ -using Content.Server.Body.Components; -using Content.Server.Body.Systems; +using Content.Server.Body.Systems; using Content.Shared.Administration.Logs; +using Content.Shared.Body.Components; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; @@ -12,6 +12,7 @@ using Robust.Shared.Timing; namespace Content.Server.Rootable; +// TODO: Move all of this to shared /// /// Adds an action to toggle rooting to the ground, primarily for the Diona species. /// @@ -68,7 +69,7 @@ public sealed class RootableSystem : SharedRootableSystem _reactive.DoEntityReaction(entity, transferSolution, ReactionMethod.Ingestion); - if (_blood.TryAddToChemicals(entity, transferSolution, entity.Comp2)) + if (_blood.TryAddToChemicals((entity, entity.Comp2), transferSolution)) { // Log solution addition by puddle _logger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(entity):target} absorbed puddle {SharedSolutionContainerSystem.ToPrettyString(transferSolution)}"); diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs index 3957e02d2d..fd40aa8816 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -1,13 +1,12 @@ using Content.Server.Actions; using Content.Server.Administration.Logs; using Content.Server.Administration.Managers; -using Content.Server.Body.Components; +using Content.Shared.Body.Events; using Content.Server.DeviceNetwork.Systems; using Content.Server.Explosion.EntitySystems; using Content.Server.Hands.Systems; using Content.Server.PowerCell; using Content.Shared.Alert; -using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index 03be40aad2..cf0dac30b2 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -13,6 +13,7 @@ using Content.Server.NPC.HTN; using Content.Server.NPC.Systems; using Content.Server.Speech.Components; using Content.Server.Temperature.Components; +using Content.Shared.Body.Components; using Content.Shared.CombatMode; using Content.Shared.CombatMode.Pacification; using Content.Shared.Damage; diff --git a/Content.Shared/Body/Components/BloodstreamComponent.cs b/Content.Shared/Body/Components/BloodstreamComponent.cs new file mode 100644 index 0000000000..7997d92066 --- /dev/null +++ b/Content.Shared/Body/Components/BloodstreamComponent.cs @@ -0,0 +1,200 @@ +using Content.Shared.Alert; +using Content.Shared.Body.Systems; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; +using Content.Shared.FixedPoint; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Shared.Body.Components; + +/// +/// Gives an entity a bloodstream. +/// +[RegisterComponent, NetworkedComponent,] +[AutoGenerateComponentState(fieldDeltas: true), AutoGenerateComponentPause] +[Access(typeof(SharedBloodstreamSystem))] +public sealed partial class BloodstreamComponent : Component +{ + public const string DefaultChemicalsSolutionName = "chemicals"; + public const string DefaultBloodSolutionName = "bloodstream"; + public const string DefaultBloodTemporarySolutionName = "bloodstreamTemporary"; + + /// + /// The next time that blood level will be updated and bloodloss damage dealt. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + [AutoNetworkedField, AutoPausedField] + public TimeSpan NextUpdate; + + /// + /// The interval at which this component updates. + /// + [DataField, AutoNetworkedField] + public TimeSpan UpdateInterval = TimeSpan.FromSeconds(3); + + /// + /// How much is this entity currently bleeding? + /// Higher numbers mean more blood lost every tick. + /// + /// Goes down slowly over time, and items like bandages + /// or clotting reagents can lower bleeding. + /// + /// + /// This generally corresponds to an amount of damage and can't go above 100. + /// + [DataField, AutoNetworkedField] + public float BleedAmount; + + /// + /// How much should bleeding be reduced every update interval? + /// + [DataField, AutoNetworkedField] + public float BleedReductionAmount = 0.33f; + + /// + /// How high can go? + /// + [DataField, AutoNetworkedField] + public float MaxBleedAmount = 10.0f; + + /// + /// What percentage of current blood is necessary to avoid dealing blood loss damage? + /// + [DataField, AutoNetworkedField] + public float BloodlossThreshold = 0.9f; + + /// + /// The base bloodloss damage to be incurred if below + /// The default values are defined per mob/species in YML. + /// + [DataField(required: true), AutoNetworkedField] + public DamageSpecifier BloodlossDamage = new(); + + /// + /// The base bloodloss damage to be healed if above + /// The default values are defined per mob/species in YML. + /// + [DataField(required: true), AutoNetworkedField] + public DamageSpecifier BloodlossHealDamage = new(); + + // TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth. + /// + /// How much reagent of blood should be restored each update interval? + /// + [DataField, AutoNetworkedField] + public FixedPoint2 BloodRefreshAmount = 1.0f; + + /// + /// How much blood needs to be in the temporary solution in order to create a puddle? + /// + [DataField, AutoNetworkedField] + public FixedPoint2 BleedPuddleThreshold = 1.0f; + + /// + /// A modifier set prototype ID corresponding to how damage should be modified + /// before taking it into account for bloodloss. + /// + /// + /// For example, piercing damage is increased while poison damage is nullified entirely. + /// + [DataField, AutoNetworkedField] + public ProtoId DamageBleedModifiers = "BloodlossHuman"; + + /// + /// The sound to be played when a weapon instantly deals blood loss damage. + /// + [DataField, AutoNetworkedField] + public SoundSpecifier InstantBloodSound = new SoundCollectionSpecifier("blood"); + + /// + /// The sound to be played when some damage actually heals bleeding rather than starting it. + /// + [DataField] + public SoundSpecifier BloodHealedSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"); + + /// + /// The minimum amount damage reduction needed to play the healing sound/popup. + /// This prevents tiny amounts of heat damage from spamming the sound, e.g. spacing. + /// + [DataField] + public float BloodHealedSoundThreshold = -0.1f; + + // TODO probably damage bleed thresholds. + + /// + /// Max volume of internal chemical solution storage + /// + [DataField] + public FixedPoint2 ChemicalMaxVolume = FixedPoint2.New(250); + + /// + /// Max volume of internal blood storage, + /// and starting level of blood. + /// + [DataField] + public FixedPoint2 BloodMaxVolume = FixedPoint2.New(300); + + /// + /// Which reagent is considered this entities 'blood'? + /// + /// + /// Slime-people might use slime as their blood or something like that. + /// + [DataField, AutoNetworkedField] + public ProtoId BloodReagent = "Blood"; + + /// + /// Name/Key that is indexed by. + /// + [DataField] + public string BloodSolutionName = DefaultBloodSolutionName; + + /// + /// Name/Key that is indexed by. + /// + [DataField] + public string ChemicalSolutionName = DefaultChemicalsSolutionName; + + /// + /// Name/Key that is indexed by. + /// + [DataField] + public string BloodTemporarySolutionName = DefaultBloodTemporarySolutionName; + + /// + /// Internal solution for blood storage + /// + [ViewVariables] + public Entity? BloodSolution; + + /// + /// Internal solution for reagent storage + /// + [ViewVariables] + public Entity? ChemicalSolution; + + /// + /// Temporary blood solution. + /// When blood is lost, it goes to this solution, and when this + /// solution hits a certain cap, the blood is actually spilled as a puddle. + /// + [ViewVariables] + public Entity? TemporarySolution; + + /// + /// Variable that stores the amount of status time added by having a low blood level. + /// + [DataField, AutoNetworkedField] + public TimeSpan StatusTime; + + /// + /// Alert to show when bleeding. + /// + [DataField] + public ProtoId BleedingAlert = "Bleed"; +} diff --git a/Content.Shared/Body/Events/ApplyMetabolicMultiplierEvent.cs b/Content.Shared/Body/Events/ApplyMetabolicMultiplierEvent.cs index dafc1e49de..1a7b589392 100644 --- a/Content.Shared/Body/Events/ApplyMetabolicMultiplierEvent.cs +++ b/Content.Shared/Body/Events/ApplyMetabolicMultiplierEvent.cs @@ -1,4 +1,4 @@ -namespace Content.Shared.Body.Events; +namespace Content.Shared.Body.Events; // TODO REFACTOR THIS // This will cause rates to slowly drift over time due to floating point errors. diff --git a/Content.Server/Body/Components/BeingGibbedEvent.cs b/Content.Shared/Body/Events/BeingGibbedEvent.cs similarity index 81% rename from Content.Server/Body/Components/BeingGibbedEvent.cs rename to Content.Shared/Body/Events/BeingGibbedEvent.cs index a010855f78..7ab34f2e18 100644 --- a/Content.Server/Body/Components/BeingGibbedEvent.cs +++ b/Content.Shared/Body/Events/BeingGibbedEvent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Body.Components; +namespace Content.Shared.Body.Events; /// /// Raised when a body gets gibbed, before it is deleted. diff --git a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs new file mode 100644 index 0000000000..3b1674cf3c --- /dev/null +++ b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs @@ -0,0 +1,519 @@ +using Content.Shared.Alert; +using Content.Shared.Body.Components; +using Content.Shared.Body.Events; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Reaction; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Damage; +using Content.Shared.Drunk; +using Content.Shared.EntityEffects.Effects; +using Content.Shared.FixedPoint; +using Content.Shared.Fluids; +using Content.Shared.Forensics.Components; +using Content.Shared.HealthExaminable; +using Content.Shared.Mobs.Systems; +using Content.Shared.Popups; +using Content.Shared.Rejuvenate; +using Content.Shared.Speech.EntitySystems; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Shared.Body.Systems; + +public abstract class SharedBloodstreamSystem : EntitySystem +{ + [Dependency] protected readonly SharedSolutionContainerSystem SolutionContainer = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPuddleSystem _puddle = default!; + [Dependency] private readonly AlertsSystem _alertsSystem = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly SharedDrunkSystem _drunkSystem = default!; + [Dependency] private readonly SharedStutteringSystem _stutteringSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnEntRemoved); + SubscribeLocalEvent(OnReactionAttempt); + SubscribeLocalEvent>(OnReactionAttempt); + SubscribeLocalEvent(OnDamageChanged); + SubscribeLocalEvent(OnHealthBeingExamined); + SubscribeLocalEvent(OnBeingGibbed); + SubscribeLocalEvent(OnApplyMetabolicMultiplier); + SubscribeLocalEvent(OnRejuvenate); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var curTime = _timing.CurTime; + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var bloodstream)) + { + if (curTime < bloodstream.NextUpdate) + continue; + + bloodstream.NextUpdate += bloodstream.UpdateInterval; + DirtyField(uid, bloodstream, nameof(BloodstreamComponent.NextUpdate)); // needs to be dirtied on the client so it can be rerolled during prediction + + if (!SolutionContainer.ResolveSolution(uid, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution)) + continue; + + // Adds blood to their blood level if it is below the maximum; Blood regeneration. Must be alive. + if (bloodSolution.Volume < bloodSolution.MaxVolume && !_mobStateSystem.IsDead(uid)) + { + TryModifyBloodLevel((uid, bloodstream), bloodstream.BloodRefreshAmount); + } + + // Removes blood from the bloodstream based on bleed amount (bleed rate) + // as well as stop their bleeding to a certain extent. + if (bloodstream.BleedAmount > 0) + { + // Blood is removed from the bloodstream at a 1-1 rate with the bleed amount + TryModifyBloodLevel((uid, bloodstream), -bloodstream.BleedAmount); + // Bleed rate is reduced by the bleed reduction amount in the bloodstream component. + TryModifyBleedAmount((uid, bloodstream), -bloodstream.BleedReductionAmount); + } + + // deal bloodloss damage if their blood level is below a threshold. + var bloodPercentage = GetBloodLevelPercentage((uid, bloodstream)); + if (bloodPercentage < bloodstream.BloodlossThreshold && !_mobStateSystem.IsDead(uid)) + { + // bloodloss damage is based on the base value, and modified by how low your blood level is. + var amt = bloodstream.BloodlossDamage / (0.1f + bloodPercentage); + + _damageableSystem.TryChangeDamage(uid, amt, + ignoreResistances: false, interruptsDoAfters: false); + + // Apply dizziness as a symptom of bloodloss. + // The effect is applied in a way that it will never be cleared without being healthy. + // Multiplying by 2 is arbitrary but works for this case, it just prevents the time from running out + _drunkSystem.TryApplyDrunkenness( + uid, + (float)bloodstream.UpdateInterval.TotalSeconds * 2, + applySlur: false); + _stutteringSystem.DoStutter(uid, bloodstream.UpdateInterval * 2, refresh: false); + + // storing the drunk and stutter time so we can remove it independently from other effects additions + bloodstream.StatusTime += bloodstream.UpdateInterval * 2; + DirtyField(uid, bloodstream, nameof(BloodstreamComponent.StatusTime)); + } + else if (!_mobStateSystem.IsDead(uid)) + { + // If they're healthy, we'll try and heal some bloodloss instead. + _damageableSystem.TryChangeDamage( + uid, + bloodstream.BloodlossHealDamage * bloodPercentage, + ignoreResistances: true, interruptsDoAfters: false); + + // Remove the drunk effect when healthy. Should only remove the amount of drunk and stutter added by low blood level + _drunkSystem.TryRemoveDrunkenessTime(uid, bloodstream.StatusTime.TotalSeconds); + _stutteringSystem.DoRemoveStutterTime(uid, bloodstream.StatusTime.TotalSeconds); + // Reset the drunk and stutter time to zero + bloodstream.StatusTime = TimeSpan.Zero; + DirtyField(uid, bloodstream, nameof(BloodstreamComponent.StatusTime)); + } + } + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextUpdate = _timing.CurTime + ent.Comp.UpdateInterval; + DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.NextUpdate)); + } + + // prevent the infamous UdderSystem debug assert, see https://github.com/space-wizards/space-station-14/pull/35314 + // TODO: find a better solution than copy pasting this into every shared system that caches solution entities + private void OnEntRemoved(Entity entity, ref EntRemovedFromContainerMessage args) + { + // Make sure the removed entity was our contained solution and set it to null + if (args.Entity == entity.Comp.BloodSolution?.Owner) + entity.Comp.BloodSolution = null; + + if (args.Entity == entity.Comp.ChemicalSolution?.Owner) + entity.Comp.ChemicalSolution = null; + + if (args.Entity == entity.Comp.TemporarySolution?.Owner) + entity.Comp.TemporarySolution = null; + } + + private void OnReactionAttempt(Entity ent, ref ReactionAttemptEvent args) + { + if (args.Cancelled) + return; + + foreach (var effect in args.Reaction.Effects) + { + switch (effect) + { + case CreateEntityReactionEffect: // Prevent entities from spawning in the bloodstream + case AreaReactionEffect: // No spontaneous smoke or foam leaking out of blood vessels. + args.Cancelled = true; + return; + } + } + + // The area-reaction effect canceling is part of avoiding smoke-fork-bombs (create two smoke bombs, that when + // ingested by mobs create more smoke). This also used to act as a rapid chemical-purge, because all the + // reagents would get carried away by the smoke/foam. This does still work for the stomach (I guess people vomit + // up the smoke or spawned entities?). + + // TODO apply organ damage instead of just blocking the reaction? + // Having cheese-clots form in your veins can't be good for you. + } + + private void OnReactionAttempt(Entity ent, ref SolutionRelayEvent args) + { + if (args.Name != ent.Comp.BloodSolutionName + && args.Name != ent.Comp.ChemicalSolutionName + && args.Name != ent.Comp.BloodTemporarySolutionName) + { + return; + } + + OnReactionAttempt(ent, ref args.Event); + } + + private void OnDamageChanged(Entity ent, ref DamageChangedEvent args) + { + // The incoming state from the server raises a DamageChangedEvent as well. + // But the changes to the bloodstream have also been dirtied, + // so we prevent applying them twice. + if (_timing.ApplyingState) + return; + + if (args.DamageDelta is null || !args.DamageIncreased) + { + return; + } + + // TODO probably cache this or something. humans get hurt a lot + if (!_prototypeManager.TryIndex(ent.Comp.DamageBleedModifiers, out var modifiers)) + return; + + // some reagents may deal and heal different damage types in the same tick, which means DamageIncreased will be true + // but we only want to consider the dealt damage when causing bleeding + var damage = DamageSpecifier.GetPositive(args.DamageDelta); + var bloodloss = DamageSpecifier.ApplyModifierSet(damage, modifiers); + + if (bloodloss.Empty) + return; + + // Does the calculation of how much bleed rate should be added/removed, then applies it + var oldBleedAmount = ent.Comp.BleedAmount; + var total = bloodloss.GetTotal(); + var totalFloat = total.Float(); + TryModifyBleedAmount(ent.AsNullable(), totalFloat); + + /// Critical hit. Causes target to lose blood, using the bleed rate modifier of the weapon, currently divided by 5 + /// The crit chance is currently the bleed rate modifier divided by 25. + /// Higher damage weapons have a higher chance to crit! + + // TODO: Replace with RandomPredicted once the engine PR is merged + // Use both the receiver and the damage causing entity for the seed so that we have different results for multiple attacks in the same tick + var seed = HashCode.Combine((int)_timing.CurTick.Value, GetNetEntity(ent).Id, GetNetEntity(args.Origin)?.Id ?? 0); + var rand = new System.Random(seed); + var prob = Math.Clamp(totalFloat / 25, 0, 1); + if (totalFloat > 0 && rand.Prob(prob)) + { + TryModifyBloodLevel(ent.AsNullable(), -total / 5); + _audio.PlayPredicted(ent.Comp.InstantBloodSound, ent, args.Origin); + } + + // Heat damage will cauterize, causing the bleed rate to be reduced. + else if (totalFloat <= ent.Comp.BloodHealedSoundThreshold && oldBleedAmount > 0) + { + // Magically, this damage has healed some bleeding, likely + // because it's burn damage that cauterized their wounds. + + // We'll play a special sound and popup for feedback. + _popup.PopupEntity(Loc.GetString("bloodstream-component-wounds-cauterized"), ent, + ent, PopupType.Medium); // only the burned entity can see this + _audio.PlayPredicted(ent.Comp.BloodHealedSound, ent, args.Origin); + } + } + + /// + /// Shows text on health examine, based on bleed rate and blood level. + /// + private void OnHealthBeingExamined(Entity ent, ref HealthBeingExaminedEvent args) + { + // Shows massively bleeding at 0.75x the max bleed rate. + if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount * 0.75f) + { + args.Message.PushNewline(); + args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-massive-bleeding", ("target", ent.Owner))); + } + // Shows bleeding message when bleeding above half the max rate, but less than massively. + else if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount * 0.5f) + { + args.Message.PushNewline(); + args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-strong-bleeding", ("target", ent.Owner))); + } + // Shows bleeding message when bleeding above 0.25x the max rate, but less than half the max. + else if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount * 0.25f) + { + args.Message.PushNewline(); + args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-bleeding", ("target", ent.Owner))); + } + // Shows bleeding message when bleeding below 0.25x the max cap + else if (ent.Comp.BleedAmount > 0) + { + args.Message.PushNewline(); + args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-slight-bleeding", ("target", ent.Owner))); + } + + // If the mob's blood level is below the damage threshhold, the pale message is added. + if (GetBloodLevelPercentage(ent.AsNullable()) < ent.Comp.BloodlossThreshold) + { + args.Message.PushNewline(); + args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-looks-pale", ("target", ent.Owner))); + } + } + + private void OnBeingGibbed(Entity ent, ref BeingGibbedEvent args) + { + SpillAllSolutions(ent.AsNullable()); + } + + private void OnApplyMetabolicMultiplier(Entity ent, ref ApplyMetabolicMultiplierEvent args) + { + // TODO REFACTOR THIS + // This will slowly drift over time due to floating point errors. + // Instead, raise an event with the base rates and allow modifiers to get applied to it. + if (args.Apply) + ent.Comp.UpdateInterval *= args.Multiplier; + else + ent.Comp.UpdateInterval /= args.Multiplier; + + DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.UpdateInterval)); + } + + private void OnRejuvenate(Entity ent, ref RejuvenateEvent args) + { + TryModifyBleedAmount(ent.AsNullable(), -ent.Comp.BleedAmount); + + if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution, out var bloodSolution)) + TryModifyBloodLevel(ent.AsNullable(), bloodSolution.AvailableVolume); + + if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.ChemicalSolutionName, ref ent.Comp.ChemicalSolution)) + SolutionContainer.RemoveAllSolution(ent.Comp.ChemicalSolution.Value); + } + + /// + /// Returns the current blood level as a percentage (between 0 and 1). + /// + public float GetBloodLevelPercentage(Entity ent) + { + if (!Resolve(ent, ref ent.Comp) + || !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution, out var bloodSolution)) + { + return 0.0f; + } + + return bloodSolution.FillFraction; + } + + /// + /// Setter for the BloodlossThreshold datafield. + /// + public void SetBloodLossThreshold(Entity ent, float threshold) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + ent.Comp.BloodlossThreshold = threshold; + DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.BloodlossThreshold)); + } + + /// + /// Attempt to transfer a provided solution to internal solution. + /// + public bool TryAddToChemicals(Entity ent, Solution solution) + { + if (!Resolve(ent, ref ent.Comp, logMissing: false) + || !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.ChemicalSolutionName, ref ent.Comp.ChemicalSolution)) + return false; + + if (SolutionContainer.TryAddSolution(ent.Comp.ChemicalSolution.Value, solution)) + return true; + + return false; + } + + /// + /// Removes a certain amount of all reagents except of a single excluded one from the bloodstream. + /// + public bool FlushChemicals(Entity ent, ProtoId? excludedReagentID, FixedPoint2 quantity) + { + if (!Resolve(ent, ref ent.Comp, logMissing: false) + || !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.ChemicalSolutionName, ref ent.Comp.ChemicalSolution, out var chemSolution)) + return false; + + for (var i = chemSolution.Contents.Count - 1; i >= 0; i--) + { + var (reagentId, _) = chemSolution.Contents[i]; + if (reagentId.Prototype != excludedReagentID) + { + SolutionContainer.RemoveReagent(ent.Comp.ChemicalSolution.Value, reagentId, quantity); + } + } + + return true; + } + + /// + /// Attempts to modify the blood level of this entity directly. + /// + public bool TryModifyBloodLevel(Entity ent, FixedPoint2 amount) + { + if (!Resolve(ent, ref ent.Comp, logMissing: false) + || !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution)) + return false; + + if (amount >= 0) + return SolutionContainer.TryAddReagent(ent.Comp.BloodSolution.Value, ent.Comp.BloodReagent, amount, null, GetEntityBloodData(ent)); + + // Removal is more involved, + // since we also wanna handle moving it to the temporary solution + // and then spilling it if necessary. + var newSol = SolutionContainer.SplitSolution(ent.Comp.BloodSolution.Value, -amount); + + if (!SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodTemporarySolutionName, ref ent.Comp.TemporarySolution, out var tempSolution)) + return true; + + tempSolution.AddSolution(newSol, _prototypeManager); + + if (tempSolution.Volume > ent.Comp.BleedPuddleThreshold) + { + // Pass some of the chemstream into the spilled blood. + if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.ChemicalSolutionName, ref ent.Comp.ChemicalSolution)) + { + var temp = SolutionContainer.SplitSolution(ent.Comp.ChemicalSolution.Value, tempSolution.Volume / 10); + tempSolution.AddSolution(temp, _prototypeManager); + } + + _puddle.TrySpillAt(ent.Owner, tempSolution, out _, sound: false); + + tempSolution.RemoveAllSolution(); + } + + SolutionContainer.UpdateChemicals(ent.Comp.TemporarySolution.Value); + + return true; + } + + /// + /// Tries to make an entity bleed more or less. + /// + public bool TryModifyBleedAmount(Entity ent, float amount) + { + if (!Resolve(ent, ref ent.Comp, logMissing: false)) + return false; + + ent.Comp.BleedAmount += amount; + ent.Comp.BleedAmount = Math.Clamp(ent.Comp.BleedAmount, 0, ent.Comp.MaxBleedAmount); + + DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.BleedAmount)); + + if (ent.Comp.BleedAmount == 0) + _alertsSystem.ClearAlert(ent, ent.Comp.BleedingAlert); + else + { + var severity = (short)Math.Clamp(Math.Round(ent.Comp.BleedAmount, MidpointRounding.ToZero), 0, 10); + _alertsSystem.ShowAlert(ent, ent.Comp.BleedingAlert, severity); + } + + return true; + } + + /// + /// Spill all bloodstream solutions into a puddle. + /// BLOOD FOR THE BLOOD GOD + /// + public void SpillAllSolutions(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + var tempSol = new Solution(); + + if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution, out var bloodSolution)) + { + tempSol.MaxVolume += bloodSolution.MaxVolume; + tempSol.AddSolution(bloodSolution, _prototypeManager); + SolutionContainer.RemoveAllSolution(ent.Comp.BloodSolution.Value); + } + + if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.ChemicalSolutionName, ref ent.Comp.ChemicalSolution, out var chemSolution)) + { + tempSol.MaxVolume += chemSolution.MaxVolume; + tempSol.AddSolution(chemSolution, _prototypeManager); + SolutionContainer.RemoveAllSolution(ent.Comp.ChemicalSolution.Value); + } + + if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodTemporarySolutionName, ref ent.Comp.TemporarySolution, out var tempSolution)) + { + tempSol.MaxVolume += tempSolution.MaxVolume; + tempSol.AddSolution(tempSolution, _prototypeManager); + SolutionContainer.RemoveAllSolution(ent.Comp.TemporarySolution.Value); + } + + _puddle.TrySpillAt(ent, tempSol, out _); + } + + /// + /// Change what someone's blood is made of, on the fly. + /// + public void ChangeBloodReagent(Entity ent, ProtoId reagent) + { + if (!Resolve(ent, ref ent.Comp, logMissing: false) + || reagent == ent.Comp.BloodReagent) + { + return; + } + + if (!SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution, out var bloodSolution)) + { + ent.Comp.BloodReagent = reagent; + return; + } + + var currentVolume = bloodSolution.RemoveReagent(ent.Comp.BloodReagent, bloodSolution.Volume, ignoreReagentData: true); + + ent.Comp.BloodReagent = reagent; + DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.BloodReagent)); + + if (currentVolume > 0) + SolutionContainer.TryAddReagent(ent.Comp.BloodSolution.Value, ent.Comp.BloodReagent, currentVolume, null, GetEntityBloodData(ent)); + } + + /// + /// Get the reagent data for blood that a specific entity should have. + /// + public List GetEntityBloodData(EntityUid uid) + { + var bloodData = new List(); + var dnaData = new DnaData(); + + if (TryComp(uid, out var donorComp) && donorComp.DNA != null) + dnaData.DNA = donorComp.DNA; + else + dnaData.DNA = Loc.GetString("forensics-dna-unknown"); + + bloodData.Add(dnaData); + + return bloodData; + } +} diff --git a/Content.Shared/Body/Systems/StomachSystem.cs b/Content.Shared/Body/Systems/StomachSystem.cs index 8b2df453a0..d0ecb6b93b 100644 --- a/Content.Shared/Body/Systems/StomachSystem.cs +++ b/Content.Shared/Body/Systems/StomachSystem.cs @@ -1,6 +1,7 @@ using Content.Shared.Body.Components; using Content.Shared.Body.Events; using Content.Shared.Body.Organ; +using Content.Shared.Body.Events; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; diff --git a/Content.Shared/Chemistry/EntitySystems/HypospraySystem.cs b/Content.Shared/Chemistry/EntitySystems/HypospraySystem.cs index 86f6edc903..34bc44f1cb 100644 --- a/Content.Shared/Chemistry/EntitySystems/HypospraySystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/HypospraySystem.cs @@ -1,6 +1,8 @@ using Content.Shared.Administration.Logs; -using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Body.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Hypospray.Events; using Content.Shared.Database; using Content.Shared.FixedPoint; diff --git a/Content.Shared/Damage/DamageSpecifier.cs b/Content.Shared/Damage/DamageSpecifier.cs index 51472a56bd..00bd416e1f 100644 --- a/Content.Shared/Damage/DamageSpecifier.cs +++ b/Content.Shared/Damage/DamageSpecifier.cs @@ -1,3 +1,4 @@ +using System.Linq; using System.Text.Json.Serialization; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; @@ -77,6 +78,11 @@ namespace Content.Shared.Damage [JsonIgnore] public bool Empty => DamageDict.Count == 0; + public override string ToString() + { + return "DamageSpecifier(" + string.Join("; ", DamageDict.Select(x => x.Key + ":" + x.Value)) + ")"; + } + #region constructors /// /// Constructor that just results in an empty dictionary. diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index 31426f6bd0..70fbc46806 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -353,7 +353,7 @@ namespace Content.Shared.Damage // Has the damage actually changed? DamageSpecifier newDamage = new() { DamageDict = new(state.DamageDict) }; - var delta = component.Damage - newDamage; + var delta = newDamage - component.Damage; delta.TrimZeros(); if (!delta.Empty) diff --git a/Content.Shared/Medical/Healing/HealingComponent.cs b/Content.Shared/Medical/Healing/HealingComponent.cs new file mode 100644 index 0000000000..53358fd28d --- /dev/null +++ b/Content.Shared/Medical/Healing/HealingComponent.cs @@ -0,0 +1,65 @@ +using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Medical.Healing; + +/// +/// Applies a damage change to the target when used in an interaction. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class HealingComponent : Component +{ + /// + /// The amount of damage to heal per use. + /// + [DataField(required: true), AutoNetworkedField] + public DamageSpecifier Damage = default!; + + /// + /// This should generally be negative, + /// since you're, like, trying to heal damage. + /// + [DataField, AutoNetworkedField] + public float BloodlossModifier = 0.0f; + + /// + /// Restore missing blood. + /// + [DataField, AutoNetworkedField] + public float ModifyBloodLevel = 0.0f; + + /// + /// The supported damage types are specified using a s. For a + /// HealingComponent this filters what damage container type this component should work on. If null, + /// all damage container types are supported. + /// + [DataField, AutoNetworkedField] + public List>? DamageContainers; + + /// + /// How long it takes to apply the damage. + /// + [DataField, AutoNetworkedField] + public float Delay = 3f; + + /// + /// Delay multiplier when healing yourself. + /// + [DataField, AutoNetworkedField] + public float SelfHealPenaltyMultiplier = 3f; + + /// + /// Sound played on healing begin. + /// + [DataField] + public SoundSpecifier? HealingBeginSound = null; + + /// + /// Sound played on healing end. + /// + [DataField] + public SoundSpecifier? HealingEndSound = null; +} diff --git a/Content.Server/Medical/HealingSystem.cs b/Content.Shared/Medical/Healing/HealingSystem.cs similarity index 54% rename from Content.Server/Medical/HealingSystem.cs rename to Content.Shared/Medical/Healing/HealingSystem.cs index c18f29d2fb..74cb8881f4 100644 --- a/Content.Server/Medical/HealingSystem.cs +++ b/Content.Shared/Medical/Healing/HealingSystem.cs @@ -1,9 +1,6 @@ -using Content.Server.Administration.Logs; -using Content.Server.Body.Components; -using Content.Server.Body.Systems; -using Content.Server.Medical.Components; -using Content.Server.Popups; -using Content.Server.Stack; +using Content.Shared.Administration.Logs; +using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Damage; using Content.Shared.Database; @@ -12,77 +9,74 @@ using Content.Shared.FixedPoint; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; -using Content.Shared.Medical; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; using Content.Shared.Stacks; using Robust.Shared.Audio.Systems; -using Robust.Shared.Random; -using Robust.Shared.Audio; -namespace Content.Server.Medical; +namespace Content.Shared.Medical.Healing; public sealed class HealingSystem : EntitySystem { [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly DamageableSystem _damageable = default!; - [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; + [Dependency] private readonly SharedBloodstreamSystem _bloodstreamSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - [Dependency] private readonly StackSystem _stacks = default!; + [Dependency] private readonly SharedStackSystem _stacks = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnHealingUse); SubscribeLocalEvent(OnHealingAfterInteract); SubscribeLocalEvent(OnDoAfter); } - private void OnDoAfter(Entity entity, ref HealingDoAfterEvent args) + private void OnDoAfter(Entity target, ref HealingDoAfterEvent args) { - var dontRepeat = false; - - if (!TryComp(args.Used, out HealingComponent? healing)) - return; if (args.Handled || args.Cancelled) return; + if (!TryComp(args.Used, out HealingComponent? healing)) + return; + if (healing.DamageContainers is not null && - entity.Comp.DamageContainerID is not null && - !healing.DamageContainers.Contains(entity.Comp.DamageContainerID)) + target.Comp.DamageContainerID is not null && + !healing.DamageContainers.Contains(target.Comp.DamageContainerID.Value)) { return; } + TryComp(target, out var bloodstream); + // Heal some bloodloss damage. - if (healing.BloodlossModifier != 0) + if (healing.BloodlossModifier != 0 && bloodstream != null) { - if (!TryComp(entity, out var bloodstream)) - return; var isBleeding = bloodstream.BleedAmount > 0; - _bloodstreamSystem.TryModifyBleedAmount(entity.Owner, healing.BloodlossModifier); + _bloodstreamSystem.TryModifyBleedAmount((target.Owner, bloodstream), healing.BloodlossModifier); if (isBleeding != bloodstream.BleedAmount > 0) { - var popup = (args.User == entity.Owner) + var popup = (args.User == target.Owner) ? Loc.GetString("medical-item-stop-bleeding-self") - : Loc.GetString("medical-item-stop-bleeding", ("target", Identity.Entity(entity.Owner, EntityManager))); - _popupSystem.PopupEntity(popup, entity, args.User); + : Loc.GetString("medical-item-stop-bleeding", ("target", Identity.Entity(target.Owner, EntityManager))); + _popupSystem.PopupClient(popup, target, args.User); } } // Restores missing blood - if (healing.ModifyBloodLevel != 0) - _bloodstreamSystem.TryModifyBloodLevel(entity.Owner, healing.ModifyBloodLevel); + if (healing.ModifyBloodLevel != 0 && bloodstream != null) + _bloodstreamSystem.TryModifyBloodLevel((target.Owner, bloodstream), healing.ModifyBloodLevel); - var healed = _damageable.TryChangeDamage(entity.Owner, healing.Damage * _damageable.UniversalTopicalsHealModifier, true, origin: args.Args.User); + var healed = _damageable.TryChangeDamage(target.Owner, healing.Damage * _damageable.UniversalTopicalsHealModifier, true, origin: args.Args.User); if (healed == null && healing.BloodlossModifier != 0) return; @@ -90,7 +84,7 @@ public sealed class HealingSystem : EntitySystem var total = healed?.GetTotal() ?? FixedPoint2.Zero; // Re-verify that we can heal the damage. - + var dontRepeat = false; if (TryComp(args.Used.Value, out var stackComp)) { _stacks.Use(args.Used.Value, 1, stackComp); @@ -100,13 +94,13 @@ public sealed class HealingSystem : EntitySystem } else { - QueueDel(args.Used.Value); + PredictedQueueDel(args.Used.Value); } - if (entity.Owner != args.User) + if (target.Owner != args.User) { _adminLogger.Add(LogType.Healed, - $"{ToPrettyString(args.User):user} healed {ToPrettyString(entity.Owner):target} for {total:damage} damage"); + $"{ToPrettyString(args.User):user} healed {ToPrettyString(target.Owner):target} for {total:damage} damage"); } else { @@ -114,19 +108,19 @@ public sealed class HealingSystem : EntitySystem $"{ToPrettyString(args.User):user} healed themselves for {total:damage} damage"); } - _audio.PlayPvs(healing.HealingEndSound, entity.Owner); + _audio.PlayPredicted(healing.HealingEndSound, target.Owner, args.User); // Logic to determine the whether or not to repeat the healing action - args.Repeat = (HasDamage(entity, healing) && !dontRepeat); + args.Repeat = HasDamage((args.Used.Value, healing), target) && !dontRepeat; if (!args.Repeat && !dontRepeat) - _popupSystem.PopupEntity(Loc.GetString("medical-item-finished-using", ("item", args.Used)), entity.Owner, args.User); + _popupSystem.PopupClient(Loc.GetString("medical-item-finished-using", ("item", args.Used)), target.Owner, args.User); args.Handled = true; } - private bool HasDamage(Entity ent, HealingComponent healing) + private bool HasDamage(Entity healing, Entity target) { - var damageableDict = ent.Comp.Damage.DamageDict; - var healingDict = healing.Damage.DamageDict; + var damageableDict = target.Comp.Damage.DamageDict; + var healingDict = healing.Comp.Damage.DamageDict; foreach (var type in healingDict) { if (damageableDict[type.Key].Value > 0) @@ -135,18 +129,18 @@ public sealed class HealingSystem : EntitySystem } } - if (TryComp(ent, out var bloodstream)) + if (TryComp(target, out var bloodstream)) { // Is ent missing blood that we can restore? - if (healing.ModifyBloodLevel > 0 - && _solutionContainerSystem.ResolveSolution(ent.Owner, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution) + if (healing.Comp.ModifyBloodLevel > 0 + && _solutionContainerSystem.ResolveSolution(target.Owner, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution) && bloodSolution.Volume < bloodSolution.MaxVolume) { return true; } // Is ent bleeding and can we stop it? - if (healing.BloodlossModifier < 0 && bloodstream.BleedAmount > 0) + if (healing.Comp.BloodlossModifier < 0 && bloodstream.BleedAmount > 0) { return true; } @@ -155,64 +149,64 @@ public sealed class HealingSystem : EntitySystem return false; } - private void OnHealingUse(Entity entity, ref UseInHandEvent args) + private void OnHealingUse(Entity healing, ref UseInHandEvent args) { if (args.Handled) return; - if (TryHeal(entity, args.User, args.User, entity.Comp)) + if (TryHeal(healing, args.User, args.User)) args.Handled = true; } - private void OnHealingAfterInteract(Entity entity, ref AfterInteractEvent args) + private void OnHealingAfterInteract(Entity healing, ref AfterInteractEvent args) { if (args.Handled || !args.CanReach || args.Target == null) return; - if (TryHeal(entity, args.User, args.Target.Value, entity.Comp)) + if (TryHeal(healing, args.Target.Value, args.User)) args.Handled = true; } - private bool TryHeal(EntityUid uid, EntityUid user, EntityUid target, HealingComponent component) + private bool TryHeal(Entity healing, Entity target, EntityUid user) { - if (!TryComp(target, out var targetDamage)) + if (!Resolve(target, ref target.Comp, false)) return false; - if (component.DamageContainers is not null && - targetDamage.DamageContainerID is not null && - !component.DamageContainers.Contains(targetDamage.DamageContainerID)) + if (healing.Comp.DamageContainers is not null && + target.Comp.DamageContainerID is not null && + !healing.Comp.DamageContainers.Contains(target.Comp.DamageContainerID.Value)) { return false; } - if (user != target && !_interactionSystem.InRangeUnobstructed(user, target, popup: true)) + if (user != target.Owner && !_interactionSystem.InRangeUnobstructed(user, target.Owner, popup: true)) return false; - if (TryComp(uid, out var stack) && stack.Count < 1) + if (TryComp(healing, out var stack) && stack.Count < 1) return false; - if (!HasDamage((target, targetDamage), component)) + if (!HasDamage(healing, target!)) { - _popupSystem.PopupEntity(Loc.GetString("medical-item-cant-use", ("item", uid)), uid, user); + _popupSystem.PopupClient(Loc.GetString("medical-item-cant-use", ("item", healing.Owner)), healing, user); return false; } - _audio.PlayPvs(component.HealingBeginSound, uid); + _audio.PlayPredicted(healing.Comp.HealingBeginSound, healing, user); - var isNotSelf = user != target; + var isNotSelf = user != target.Owner; if (isNotSelf) { - var msg = Loc.GetString("medical-item-popup-target", ("user", Identity.Entity(user, EntityManager)), ("item", uid)); + var msg = Loc.GetString("medical-item-popup-target", ("user", Identity.Entity(user, EntityManager)), ("item", healing.Owner)); _popupSystem.PopupEntity(msg, target, target, PopupType.Medium); } var delay = isNotSelf - ? component.Delay - : component.Delay * GetScaledHealingPenalty(user, component); + ? healing.Comp.Delay + : healing.Comp.Delay * GetScaledHealingPenalty(healing); var doAfterEventArgs = - new DoAfterArgs(EntityManager, user, delay, new HealingDoAfterEvent(), target, target: target, used: uid) + new DoAfterArgs(EntityManager, user, delay, new HealingDoAfterEvent(), target, target: target, used: healing) { // Didn't break on damage as they may be trying to prevent it and // not being able to heal your own ticking damage would be frustrating. @@ -231,18 +225,18 @@ public sealed class HealingSystem : EntitySystem /// /// /// - public float GetScaledHealingPenalty(EntityUid uid, HealingComponent component) + public float GetScaledHealingPenalty(Entity healing) { - var output = component.Delay; - if (!TryComp(uid, out var mobThreshold) || - !TryComp(uid, out var damageable)) + var output = healing.Comp.Delay; + if (!TryComp(healing, out var mobThreshold) || + !TryComp(healing, out var damageable)) return output; - if (!_mobThresholdSystem.TryGetThresholdForState(uid, MobState.Critical, out var amount, mobThreshold)) + if (!_mobThresholdSystem.TryGetThresholdForState(healing, MobState.Critical, out var amount, mobThreshold)) return 1; - var percentDamage = (float) (damageable.TotalDamage / amount); + var percentDamage = (float)(damageable.TotalDamage / amount); //basically make it scale from 1 to the multiplier. - var modifier = percentDamage * (component.SelfHealPenaltyMultiplier - 1) + 1; + var modifier = percentDamage * (healing.Comp.SelfHealPenaltyMultiplier - 1) + 1; return Math.Max(modifier, 1); } } diff --git a/Resources/Locale/en-US/medical/components/healing-component.ftl b/Resources/Locale/en-US/medical/components/healing-component.ftl index 20ad23dcb2..d206150a0e 100644 --- a/Resources/Locale/en-US/medical/components/healing-component.ftl +++ b/Resources/Locale/en-US/medical/components/healing-component.ftl @@ -1,5 +1,5 @@ -medical-item-finished-using = You have finished healing with the {$item} -medical-item-cant-use = There is no damage you can heal with the {$item} -medical-item-stop-bleeding = {CAPITALIZE($target)} has stopped bleeding -medical-item-stop-bleeding-self = You have stopped bleeding +medical-item-finished-using = You have finished healing with the {$item}. +medical-item-cant-use = There is no damage you can heal with the {$item}. +medical-item-stop-bleeding = {CAPITALIZE($target)} has stopped bleeding. +medical-item-stop-bleeding-self = You have stopped bleeding. medical-item-popup-target = {CAPITALIZE(THE($user))} is trying to heal you with the {$item}! diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml index 98565931fd..4a05817646 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml @@ -257,7 +257,7 @@ damage: types: Bloodloss: -0.5 #lowers bloodloss damage - ModifyBloodLevel: 15 #restores about 5% blood per use on standard humanoids. + modifyBloodLevel: 15 #restores about 5% blood per use on standard humanoids. healingBeginSound: path: "/Audio/Items/Medical/brutepack_begin.ogg" params: From 70ed8835c64369f8ea7925ddbd53dd9e37c48d50 Mon Sep 17 00:00:00 2001 From: LaCumbiaDelCoronavirus <90893484+LaCumbiaDelCoronavirus@users.noreply.github.com> Date: Thu, 3 Jul 2025 08:27:56 +0800 Subject: [PATCH 166/191] make telesci wreck easier (#37569) rel --- .../Maps/Ruins/displaced_telescience.yml | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/Resources/Maps/Ruins/displaced_telescience.yml b/Resources/Maps/Ruins/displaced_telescience.yml index 47b13089d7..b5c2690275 100644 --- a/Resources/Maps/Ruins/displaced_telescience.yml +++ b/Resources/Maps/Ruins/displaced_telescience.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Grid - engineVersion: 255.0.0 + engineVersion: 260.0.0 forkId: "" forkVersion: "" - time: 04/30/2025 11:12:40 - entityCount: 485 + time: 05/18/2025 11:12:07 + entityCount: 487 maps: [] grids: - 1 @@ -33,20 +33,20 @@ entities: chunks: 0,0: ind: 0,0 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAgAAAAAAAwAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAQAAAAAAAQAAAAAAAgAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAQAAAAAAAwAAAAAAAgAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAA - version: 6 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAACAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAgAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAABAAAAAAAAAQAAAAAAAAIAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAIAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAQAAAAAAAAMAAAAAAAACAAAAAAAAAwAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAEAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAACAAAAAAAAAgAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAA== + version: 7 0,-1: ind: 0,-1 - tiles: AwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAABAAAAAAABAAAAAAAAQAAAAAAAQAAAAAABAAAAAAABAAAAAAABAAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAQAAAAAABAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAAAgAAAAAAAgAAAAAABAAAAAAAAQAAAAAABAAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAABAAAAAAABAAAAAAABAAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAgAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAA - version: 6 + tiles: AwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAIAAAAAAAADAAAAAAAAAwAAAAAAAAIAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAIAAAAAAAACAAAAAAAAAwAAAAAAAAMAAAAAAAACAAAAAAAAAgAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAADAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAgAAAAAAAAIAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAQAAAAAAAAEAAAAAAAAAQAAAAAAAAEAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAABAAAAAAAAAgAAAAAAAAIAAAAAAAABAAAAAAAABAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAgAAAAAAAAIAAAAAAAAEAAAAAAAAAQAAAAAAAAQAAAAAAAACAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAEAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAgAAAAAAAAIAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAgAAAAAAAAEAAAAAAAACAAAAAAAAAgAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAACAAAAAAAAAgAAAAAAAAEAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAEAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAIAAAAAAAACAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAgAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAA== + version: 7 -1,-1: ind: -1,-1 - tiles: AwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAQAAAAAAAgAAAAAABQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAQAAAAAAAQAAAAAABAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAQAAAAAABAAAAAAABAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAQAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAQAAAAAABAAAAAAABAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAQAAAAAAAgAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAQAAAAAAAgAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAQAAAAAAAgAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAAAAAAAA - version: 6 + tiles: AwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAACAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAgAAAAAAAAEAAAAAAAACAAAAAAAABQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAQAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAgAAAAAAAAIAAAAAAAABAAAAAAAAAgAAAAAAAAIAAAAAAAABAAAAAAAAAQAAAAAAAAQAAAAAAAAEAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAACAAAAAAAAAQAAAAAAAAIAAAAAAAACAAAAAAAAAQAAAAAAAAEAAAAAAAAEAAAAAAAABAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAACAAAAAAAAAgAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAEAAAAAAAACAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAABAAAAAAAAAgAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAAAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAgAAAAAAAAEAAAAAAAACAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAAAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAA== + version: 7 -1,0: ind: -1,0 - tiles: AwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAQAAAAAAAgAAAAAAAQAAAAAAAQAAAAAABQAAAAAABQAAAAAAAQAAAAAAAQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAgAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAgAAAAAAAQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAABAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAgAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAA - version: 6 + tiles: AwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAACAAAAAAAAAQAAAAAAAAIAAAAAAAABAAAAAAAAAQAAAAAAAAUAAAAAAAAFAAAAAAAAAQAAAAAAAAEAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAACAAAAAAAAAgAAAAAAAAEAAAAAAAACAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAgAAAAAAAAMAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAAQAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAIAAAAAAAACAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAACAAAAAAAABAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAgAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAA== + version: 7 - type: Broadphase - type: Physics bodyStatus: InAir @@ -59,6 +59,7 @@ entities: - type: OccluderTree - type: SpreaderGrid - type: Shuttle + dampingModifier: 0.25 - type: GridPathfinding - type: Gravity gravityShakeSound: !type:SoundPathSpecifier @@ -1756,6 +1757,7 @@ entities: chunkSize: 4 - type: GasTileOverlay - type: RadiationGridResistance + - type: ImplicitRoof - uid: 476 mapInit: true paused: true @@ -1843,7 +1845,7 @@ entities: pos: -1.5,-7.5 parent: 1 - type: Door - secondsUntilStateChange: -5362.1104 + secondsUntilStateChange: -5774.7476 state: Opening - type: DeviceLinkSource lastSignals: @@ -4425,6 +4427,14 @@ entities: rot: 3.141592653589793 rad pos: 1.5,-8.5 parent: 1 +- proto: PoweredSmallLightEmpty + entities: + - uid: 486 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-1.5 + parent: 1 - proto: PoweredStrobeLightEmpty entities: - uid: 178 @@ -4472,6 +4482,12 @@ entities: rot: -1.5707963267948966 rad pos: -5.5,0.5 parent: 1 + - uid: 487 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-5.5 + parent: 1 - proto: Railing entities: - uid: 362 From aded799ae500c9e1e6fec5b4eebb3a5d401c10e2 Mon Sep 17 00:00:00 2001 From: Alzore <140123969+Blackern5000@users.noreply.github.com> Date: Wed, 2 Jul 2025 19:54:02 -0500 Subject: [PATCH 167/191] Reduce most salvage mob health, reduce most salvage weapon damage. (#38131) --- .../Entities/Mobs/NPCs/asteroid.yml | 4 +-- .../Prototypes/Entities/Mobs/NPCs/carp.yml | 6 +++- .../Prototypes/Entities/Mobs/NPCs/space.yml | 10 ++++-- .../Entities/Mobs/NPCs/spacetick.yml | 2 +- .../Weapons/Guns/Projectiles/projectiles.yml | 21 +++++++++-- .../Entities/Objects/Weapons/Melee/mining.yml | 36 ++++++++++++++----- 6 files changed, 60 insertions(+), 19 deletions(-) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml b/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml index de8f15f4c0..941091335a 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml @@ -57,7 +57,7 @@ - type: MobThresholds thresholds: 0: Alive - 250: Dead + 150: Dead - type: MeleeWeapon soundHit: path: "/Audio/Weapons/smash.ogg" @@ -378,7 +378,7 @@ - type: MobThresholds thresholds: 0: Alive - 75: Dead + 45: Dead - type: MeleeWeapon angle: 0 animation: WeaponArcBite diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml index 8db27e19c5..971f5e1dc6 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml @@ -156,6 +156,10 @@ suffix: "Salvage Ruleset" components: - type: SalvageMobRestrictions + - type: MobThresholds + thresholds: + 0: Alive + 30: Dead - type: entity name: space carp @@ -222,7 +226,7 @@ - type: MobThresholds thresholds: 0: Alive - 150: Dead + 82: Dead # Might seem random, but this brings up the hits to kill with a crusher mark to 3 - type: Stamina critThreshold: 150 - type: DamageStateVisuals diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/space.yml b/Resources/Prototypes/Entities/Mobs/NPCs/space.yml index 2bd0de128b..039cac3358 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/space.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/space.yml @@ -34,7 +34,7 @@ - type: MobThresholds thresholds: 0: Alive - 100: Dead + 80: Dead - type: Stamina critThreshold: 150 - type: MovementAlwaysTouching @@ -168,6 +168,10 @@ spawned: - id: FoodMeat amount: 1 + - type: MobThresholds + thresholds: + 0: Alive + 60: Dead - type: entity id: MobKangarooSpaceSalvage @@ -194,7 +198,7 @@ - type: MobThresholds thresholds: 0: Alive - 90: Dead + 45: Dead - type: Stamina critThreshold: 150 - type: DamageStateVisuals @@ -295,7 +299,7 @@ - type: MobThresholds thresholds: 0: Alive - 100: Dead + 45: Dead - type: Stamina critThreshold: 150 - type: DamageStateVisuals diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/spacetick.yml b/Resources/Prototypes/Entities/Mobs/NPCs/spacetick.yml index 92b27c7a4c..2eaf7c819f 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/spacetick.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/spacetick.yml @@ -41,7 +41,7 @@ - type: MobThresholds thresholds: 0: Alive - 15: Dead + 5: Dead - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index 712be28de3..7c94dd65cc 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -496,7 +496,7 @@ impactEffect: BulletImpactEffectKinetic damage: types: - Blunt: 25 + Blunt: 15 Structural: 30 # Short lifespan - type: TimedDespawn @@ -553,8 +553,8 @@ - MobState damage: types: - Blunt: 20 - Slash: 5 + Blunt: 18 + Slash: 4 - type: Projectile impactEffect: BulletImpactEffectKinetic damage: @@ -564,6 +564,21 @@ - type: TimedDespawn lifetime: 0.4 + # Deals less damage, glaive has better HP recovery +- type: entity + id: BulletChargeGlaive + name: leech bolt + parent: BulletCharge + components: + - type: DamageMarkerOnCollide + whitelist: + components: + - MobState + damage: + types: + Blunt: 1 + Slash: 5 + - type: entity parent: BaseBullet id: AnomalousParticleDelta diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml index 8cbf2530ec..3ed57e0495 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml @@ -106,6 +106,7 @@ enabled: false radius: 4 +# Very high burst damage if you land a mark, while also providing a small amount of healing. - type: entity parent: [BaseWeaponCrusher, BaseSecurityCargoContraband] id: WeaponCrusher @@ -132,7 +133,7 @@ - type: LeechOnMarker leech: groups: - Brute: -10 + Brute: -6 - type: Gun soundGunshot: /Audio/Weapons/plasma_cutter.ogg fireRate: 1 @@ -146,25 +147,26 @@ capacity: 1 count: 1 - type: MeleeWeapon - attackRate: 1.5 + attackRate: 1 wideAnimationRotation: -135 damage: types: - Blunt: 10 - Slash: 5 + Blunt: 6 + Slash: 4 soundHit: collection: MetalThud - type: Wieldable - type: IncreaseDamageOnWield damage: types: - Blunt: 2.5 - Slash: 2.5 + Blunt: 6 + Slash: 2 Structural: 30 - type: GunRequiresWield - type: DisarmMalus - type: Prying +# No mark ability in exchange for wideswing, autoattack, and being one-handed - type: entity parent: [ BaseKnife, BaseWeaponCrusher, BaseSecurityCargoContraband] id: WeaponCrusherDagger @@ -194,12 +196,12 @@ - type: ThrowingAngle angle: 225 -# Like a crusher... but better +# Less mark damage in exchange for more healing. Also has a better ratio of blunt to slash damage, but less structural. - type: entity parent: [ WeaponCrusher, BaseSecurityCargoContraband] id: WeaponCrusherGlaive name: crusher glaive - description: An early design of the proto-kinetic accelerator, in glaive form. + description: An early design of the proto-kinetic accelerator, in glaive form. Provides better healing in exchange for less charged damage. components: - type: Sprite sprite: Objects/Weapons/Melee/crusher_glaive.rsi @@ -215,11 +217,27 @@ - suitStorage - type: UseDelay delay: 1.9 + - type: BasicEntityAmmoProvider + proto: BulletChargeGlaive + capacity: 1 + count: 1 - type: LeechOnMarker leech: groups: Brute: -21 - - type: MeleeWeapon - type: Tag tags: - Pickaxe + - type: MeleeWeapon + attackRate: 1 + wideAnimationRotation: -135 + damage: + types: + Blunt: 3 + Slash: 7 + - type: IncreaseDamageOnWield + damage: + types: + Blunt: 2 + Slash: 6 + Structural: 20 From 41160063905566ac04399d3c04f4ce25e7f753ff Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 3 Jul 2025 00:55:10 +0000 Subject: [PATCH 168/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c51b0b9058..0d6c85ffe5 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: muburu - changes: - - message: Space Dragons now have randomly generated names. - type: Add - id: 8217 - time: '2025-04-17T21:05:14.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36656 - author: archee1 changes: - message: Cats can walk slowly to avoid slipping on puddles. @@ -3889,3 +3882,12 @@ id: 8729 time: '2025-07-02T20:50:22.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38661 +- author: Blackern5000 + changes: + - message: Most salvage enemies have had their HP reduced + type: Tweak + - message: Most salvage weapons have had their damage reduced + type: Tweak + id: 8730 + time: '2025-07-03T00:54:02.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38131 From 752957a81946fb2ab1d70b0d8730e636a61f2870 Mon Sep 17 00:00:00 2001 From: Mora <46364955+TrixxedHeart@users.noreply.github.com> Date: Wed, 2 Jul 2025 20:08:06 -0500 Subject: [PATCH 169/191] Switch HSV to the default colorspace for character customization (#38434) * Made HSV default for character editor * Adds/fixes comments to HSV defaulting * Added dropbox fix, potentially cursed * Revert "Added dropbox fix, potentially cursed" This reverts commit a709883366fbee813e839742125e70844672af29. --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> --- Content.Client/Humanoid/EyeColorPicker.cs | 1 + Content.Client/Humanoid/MarkingPicker.xaml.cs | 1 + Content.Client/Humanoid/SingleMarkingPicker.xaml.cs | 3 ++- Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Content.Client/Humanoid/EyeColorPicker.cs b/Content.Client/Humanoid/EyeColorPicker.cs index 1c864b1082..8a21b5e8b3 100644 --- a/Content.Client/Humanoid/EyeColorPicker.cs +++ b/Content.Client/Humanoid/EyeColorPicker.cs @@ -27,6 +27,7 @@ public sealed class EyeColorPicker : Control AddChild(vBox); vBox.AddChild(_colorSelectors = new ColorSelectorSliders()); + _colorSelectors.SelectorType = ColorSelectorSliders.ColorSelectorType.Hsv; // defaults color selector to HSV _colorSelectors.OnColorChanged += ColorValueChanged; } diff --git a/Content.Client/Humanoid/MarkingPicker.xaml.cs b/Content.Client/Humanoid/MarkingPicker.xaml.cs index 629f379f71..5a571942b8 100644 --- a/Content.Client/Humanoid/MarkingPicker.xaml.cs +++ b/Content.Client/Humanoid/MarkingPicker.xaml.cs @@ -416,6 +416,7 @@ public sealed partial class MarkingPicker : Control CMarkingColors.AddChild(colorContainer); ColorSelectorSliders colorSelector = new ColorSelectorSliders(); + colorSelector.SelectorType = ColorSelectorSliders.ColorSelectorType.Hsv; // defaults color selector to HSV colorSliders.Add(colorSelector); colorContainer.AddChild(new Label { Text = $"{stateNames[i]} color:" }); diff --git a/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs b/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs index 50a6036c8b..822768893e 100644 --- a/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs +++ b/Content.Client/Humanoid/SingleMarkingPicker.xaml.cs @@ -15,7 +15,7 @@ public sealed partial class SingleMarkingPicker : BoxContainer [Dependency] private readonly IEntityManager _entityManager = default!; private readonly SpriteSystem _sprite; - + /// /// What happens if a marking is selected. /// It will send the 'slot' (marking index) @@ -231,6 +231,7 @@ public sealed partial class SingleMarkingPicker : BoxContainer HorizontalExpand = true }; selector.Color = marking.MarkingColors[i]; + selector.SelectorType = ColorSelectorSliders.ColorSelectorType.Hsv; // defaults color selector to HSV var colorIndex = i; selector.OnColorChanged += color => diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index aa18751db0..f22d1416ca 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -240,6 +240,7 @@ namespace Content.Client.Lobby.UI }; RgbSkinColorContainer.AddChild(_rgbSkinColorSelector = new ColorSelectorSliders()); + _rgbSkinColorSelector.SelectorType = ColorSelectorSliders.ColorSelectorType.Hsv; // defaults color selector to HSV _rgbSkinColorSelector.OnColorChanged += _ => { OnSkinColorOnValueChanged(); From f432943625d19db615c892dcd7bd32e7a191e932 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 3 Jul 2025 01:09:14 +0000 Subject: [PATCH 170/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 0d6c85ffe5..107d7ef828 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: archee1 - changes: - - message: Cats can walk slowly to avoid slipping on puddles. - type: Tweak - - message: Cak's (cake cats) previously incorrect sprinting and walking speeds have - been corrected - type: Fix - id: 8218 - time: '2025-04-18T01:15:25.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36542 - author: Killerqu00 changes: - message: Contraband examine icon now shows whether or not you are allowed to possess @@ -3891,3 +3881,10 @@ id: 8730 time: '2025-07-03T00:54:02.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38131 +- author: TrixxedHeart + changes: + - message: Made HSV the default color selector for character customization. + type: Tweak + id: 8731 + time: '2025-07-03T01:08:07.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38434 From fdf3d6715e99037a8ff3a9ee6d48ac846aaa129a Mon Sep 17 00:00:00 2001 From: Emisse <99158783+Emisse@users.noreply.github.com> Date: Wed, 2 Jul 2025 20:31:19 -0600 Subject: [PATCH 171/191] Centcomm update (#38697) --- Resources/Maps/centcomm.yml | 192 ++++++++++++++++++++++++------------ 1 file changed, 127 insertions(+), 65 deletions(-) diff --git a/Resources/Maps/centcomm.yml b/Resources/Maps/centcomm.yml index c5dd3ac46b..7830a3742e 100644 --- a/Resources/Maps/centcomm.yml +++ b/Resources/Maps/centcomm.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Grid - engineVersion: 259.0.0 + engineVersion: 264.0.0 forkId: "" forkVersion: "" - time: 06/02/2025 19:48:10 - entityCount: 9753 + time: 07/02/2025 19:31:54 + entityCount: 9761 maps: [] grids: - 1668 @@ -35,6 +35,7 @@ tilemap: 11: FloorRedCircuit 77: FloorReinforced 9: FloorReinforcedHardened + 19: FloorShuttleRed 89: FloorSteel 5: FloorSteelCheckerDark 12: FloorSteelCheckerLight @@ -91,7 +92,7 @@ entities: version: 7 1,-2: ind: 1,-2 - tiles: bAAAAAABAGwAAAAAAQB5AAAAAAAAHQAAAAAAAB0AAAAAAQAdAAAAAAEAHQAAAAACAB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGwAAAAAAQBsAAAAAAMAeQAAAAAAAB0AAAAAAQAdAAAAAAEAHQAAAAABAB0AAAAAAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsAAAAAAIAbAAAAAABAHkAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAEAHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAAAdAAAAAAEAHQAAAAABAB0AAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAACAB0AAAAAAgAdAAAAAAIAHQAAAAAAAB0AAAAAAwAdAAAAAAIAHQAAAAACAB0AAAAAAgBZAAAAAAIAHQAAAAADAB0AAAAAAwAdAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAADAAYAAAAAAAAGAAAAAAEABgAAAAAAAB0AAAAAAwAdAAAAAAMAWQAAAAADAB0AAAAAAwAdAAAAAAMAHQAAAAADAB0AAAAAAAAdAAAAAAAAHQAAAAADAB0AAAAAAQAdAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAEABgAAAAABAAYAAAAAAAAGAAAAAAMAHQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAEAHQAAAAADAB0AAAAAAwAdAAAAAAEABgAAAAABAAYAAAAAAgAGAAAAAAAAHQAAAAAAAB0AAAAAAgAdAAAAAAMAHQAAAAAAAHkAAAAAAAAEAAAAAAAABAAAAAAAAHkAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAgAdAAAAAAAABgAAAAABAAYAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAMAHQAAAAADAB0AAAAAAgB5AAAAAAAABAAAAAAAAAQAAAAAAAB5AAAAAAAAdgAAAAADAHYAAAAAAgAdAAAAAAEABgAAAAAAAAYAAAAAAgAdAAAAAAAAHQAAAAABAB0AAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAQAdAAAAAAAAeQAAAAAAAAQAAAAAAAAEAAAAAAAAeQAAAAAAAHYAAAAAAAB2AAAAAAMAHQAAAAABAAYAAAAAAgAGAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAEAHQAAAAACAB0AAAAAAwAdAAAAAAAAHQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB2AAAAAAMAdgAAAAADAB0AAAAAAAAGAAAAAAAABgAAAAAAAB0AAAAAAQAdAAAAAAIAHQAAAAADAB0AAAAAAgAdAAAAAAMAHQAAAAABAB0AAAAAAAB5AAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAwAdAAAAAAEAHQAAAAADAAYAAAAAAwAGAAAAAAIAHQAAAAACAB0AAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAQAdAAAAAAMAHQAAAAABAAUAAAAAAgAFAAAAAAAAHQAAAAABAB0AAAAAAQAdAAAAAAEAHQAAAAABAB0AAAAAAwAdAAAAAAAABgAAAAAAAAYAAAAAAwAGAAAAAAAAHQAAAAACAB0AAAAAAwAdAAAAAAMAHQAAAAABAHkAAAAAAAAFAAAAAAIABQAAAAACAB0AAAAAAgAdAAAAAAEAHQAAAAADAB0AAAAAAgAdAAAAAAAAHQAAAAADAB0AAAAAAQAGAAAAAAMABgAAAAABAAYAAAAAAwAGAAAAAAEABgAAAAADAAYAAAAAAQAdAAAAAAMABQAAAAACAAUAAAAAAQB5AAAAAAAAHQAAAAADAB0AAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAgAdAAAAAAAAHQAAAAACAB0AAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAgAdAAAAAAAAHQAAAAACAA== + tiles: bAAAAAABAGwAAAAAAQB5AAAAAAAAHQAAAAAAAB0AAAAAAQAdAAAAAAEAHQAAAAACAB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGwAAAAAAQBsAAAAAAMAeQAAAAAAAB0AAAAAAQAdAAAAAAEAHQAAAAABAB0AAAAAAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsAAAAAAIAbAAAAAABAHkAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAEAHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAAAdAAAAAAEAHQAAAAABAB0AAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAwAdAAAAAAAAHQAAAAACAB0AAAAAAgAdAAAAAAIAHQAAAAAAAB0AAAAAAwAdAAAAAAIAHQAAAAACAB0AAAAAAgBZAAAAAAIAHQAAAAADAB0AAAAAAwAdAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAAAHQAAAAADAAYAAAAAAAAGAAAAAAEABgAAAAAAAB0AAAAAAwAdAAAAAAMAWQAAAAADAB0AAAAAAwAdAAAAAAMAHQAAAAADAB0AAAAAAAAdAAAAAAAAHQAAAAADAB0AAAAAAQAdAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAEABgAAAAABAAYAAAAAAAAGAAAAAAMAHQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAB0AAAAAAwAdAAAAAAEAHQAAAAADAB0AAAAAAwAdAAAAAAEABgAAAAABAAYAAAAAAgAGAAAAAAAAHQAAAAAAAB0AAAAAAgAdAAAAAAMAHQAAAAAAAHkAAAAAAAAEAAAAAAAABAAAAAAAAHkAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAgAdAAAAAAAABgAAAAABAAYAAAAAAAAdAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAMAHQAAAAADAB0AAAAAAgB5AAAAAAAABAAAAAAAAAQAAAAAAAB5AAAAAAAAdgAAAAADAHYAAAAAAgAdAAAAAAEABgAAAAAAAAYAAAAAAgAdAAAAAAAAHQAAAAABAB0AAAAAAAAdAAAAAAIAHQAAAAAAAB0AAAAAAQAdAAAAAAAAeQAAAAAAAAQAAAAAAAAEAAAAAAAAeQAAAAAAAHYAAAAAAAB2AAAAAAMAHQAAAAABAAYAAAAAAgAGAAAAAAEAHQAAAAADAB0AAAAAAQAdAAAAAAEAHQAAAAACAB0AAAAAAwAdAAAAAAAAHQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB2AAAAAAMAdgAAAAADAB0AAAAAAAAGAAAAAAAABgAAAAAAAB0AAAAAAQAdAAAAAAIAHQAAAAADAB0AAAAAAgAdAAAAAAMAHQAAAAABAB0AAAAAAAB5AAAAAAAAHQAAAAAAAB0AAAAAAAAdAAAAAAMAHQAAAAAAAB0AAAAAAwAdAAAAAAEAHQAAAAADAAYAAAAAAwAGAAAAAAIAHQAAAAACAB0AAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAQAdAAAAAAMAHQAAAAABAAUAAAAAAgAFAAAAAAAAHQAAAAABAB0AAAAAAQAdAAAAAAEAHQAAAAABAB0AAAAAAwAdAAAAAAAABgAAAAAAAAYAAAAAAwAGAAAAAAAAHQAAAAACAB0AAAAAAwAdAAAAAAMAHQAAAAABAHkAAAAAAAAFAAAAAAIABQAAAAACAB0AAAAAAgAdAAAAAAEAHQAAAAADAB0AAAAAAgAdAAAAAAAAHQAAAAADAB0AAAAAAQAGAAAAAAMABgAAAAABAAYAAAAAAwAGAAAAAAEABgAAAAADAAYAAAAAAQAdAAAAAAMABQAAAAACAAUAAAAAAQB5AAAAAAAAHQAAAAADAB0AAAAAAAAdAAAAAAMAHQAAAAACAB0AAAAAAgAdAAAAAAAAHQAAAAACAB0AAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAgAdAAAAAAAAHQAAAAACAA== version: 7 0,-2: ind: 0,-2 @@ -159,7 +160,7 @@ entities: version: 7 -2,-2: ind: -2,-2 - tiles: AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAGwAAAAAAwBsAAAAAAEAWQAAAAADAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAABsAAAAAAMAbAAAAAABAFkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAbAAAAAABAGwAAAAAAwBZAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAgBZAAAAAAAAWQAAAAABAFkAAAAAAABZAAAAAAMAWQAAAAABAFkAAAAAAABZAAAAAAAAWQAAAAADAFkAAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAABZAAAAAAEAWQAAAAABAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAABZAAAAAAEAWQAAAAACAFkAAAAAAABZAAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAMAWQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAACAFkAAAAAAQBZAAAAAAIAWQAAAAADAFkAAAAAAQBZAAAAAAMAWQAAAAACAFkAAAAAAgBZAAAAAAIAWQAAAAACAFkAAAAAAgBZAAAAAAEAWQAAAAAAAFkAAAAAAwBZAAAAAAIAWQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAACAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAQAAAAAAAAeQAAAAAAABAAAAAAAAAQAAAAAAAAeQAAAAAAAGwAAAAAAABsAAAAAAAAeQAAAAAAAAsAAAAAAAALAAAAAAAAeQAAAAAAAGwAAAAAAgBsAAAAAAMAeQAAAAAAAB0AAAAAAwAdAAAAAAIAEAAAAAAAAHkAAAAAAAAQAAAAAAAAEAAAAAAAAHkAAAAAAABsAAAAAAAAbAAAAAAAAHkAAAAAAAALAAAAAAAACwAAAAAAAHkAAAAAAABsAAAAAAMAbAAAAAAAAHkAAAAAAAAdAAAAAAEAHQAAAAADAA== + tiles: AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAGwAAAAAAwBsAAAAAAEAWQAAAAADAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAABsAAAAAAMAbAAAAAABAFkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAbAAAAAABAGwAAAAAAwBZAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAFkAAAAAAgBZAAAAAAAAWQAAAAABAFkAAAAAAABZAAAAAAMAWQAAAAABAFkAAAAAAABZAAAAAAAAWQAAAAADAFkAAAAAAgBZAAAAAAMAWQAAAAABAFkAAAAAAABZAAAAAAEAWQAAAAABAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAABZAAAAAAEAWQAAAAACAFkAAAAAAABZAAAAAAAAWQAAAAACAFkAAAAAAABZAAAAAAMAWQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAACAFkAAAAAAQBZAAAAAAIAWQAAAAADAFkAAAAAAQBZAAAAAAMAWQAAAAACAFkAAAAAAgBZAAAAAAIAWQAAAAACAFkAAAAAAgBZAAAAAAEAWQAAAAAAAFkAAAAAAwBZAAAAAAIAWQAAAAADAFkAAAAAAABZAAAAAAEAWQAAAAACAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABoAAAAAAAAeQAAAAAAABMAAAAAAAATAAAAAAAAeQAAAAAAAGwAAAAAAABsAAAAAAAAeQAAAAAAAAsAAAAAAAALAAAAAAAAeQAAAAAAAGwAAAAAAgBsAAAAAAMAeQAAAAAAAB0AAAAAAwAdAAAAAAIAeQAAAAAAAHkAAAAAAAATAAAAAAAAEwAAAAAAAHkAAAAAAABsAAAAAAAAbAAAAAAAAHkAAAAAAAALAAAAAAAACwAAAAAAAHkAAAAAAABsAAAAAAMAbAAAAAAAAHkAAAAAAAAdAAAAAAEAHQAAAAADAA== version: 7 -2,-3: ind: -2,-3 @@ -195,7 +196,7 @@ entities: version: 7 -3,-2: ind: -3,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAAAAFkAAAAAAABZAAAAAAAAWQAAAAABAFkAAAAAAwBZAAAAAAAAWQAAAAAAAFkAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAACgAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAACAFkAAAAAAQBZAAAAAAEAWQAAAAAAAFkAAAAAAgBZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABZAAAAAAAAWQAAAAACAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAQBZAAAAAAMAWQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAQAAAAAAAAEAAAAAAAAHkAAAAAAAAQAAAAAAAAEAAAAAAAAHkAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAEAAAAAAAABAAAAAAAAB5AAAAAAAAEAAAAAAAABAAAAAAAAB5AAAAAAAAEAAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAWQAAAAAAAFkAAAAAAABZAAAAAAAAWQAAAAABAFkAAAAAAwBZAAAAAAAAWQAAAAAAAFkAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAB5AAAAAAAACgAAAAAAAFkAAAAAAwBZAAAAAAAAWQAAAAACAFkAAAAAAQBZAAAAAAEAWQAAAAAAAFkAAAAAAgBZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAABZAAAAAAAAWQAAAAACAFkAAAAAAgBZAAAAAAIAWQAAAAABAFkAAAAAAQBZAAAAAAMAWQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAQAAAAAAAAEAAAAAAAAHkAAAAAAAAQAAAAAAAAEAAAAAAAAHkAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAEAAAAAAAABAAAAAAAAB5AAAAAAAAEAAAAAAAABAAAAAAAAB5AAAAAAAAeQAAAAAAAA== version: 7 -4,-1: ind: -4,-1 @@ -4961,6 +4962,7 @@ entities: - type: SpreaderGrid - type: GridPathfinding - type: NavMap + - type: ImplicitRoof - proto: AccessConfiguratorUniversal entities: - uid: 2056 @@ -5358,11 +5360,6 @@ entities: parent: 1668 - proto: AirlockCommandGlassLocked entities: - - uid: 1587 - components: - - type: Transform - pos: 26.5,-28.5 - parent: 1668 - uid: 6753 components: - type: Transform @@ -14976,6 +14973,11 @@ entities: - type: Transform pos: -31.5,-25.5 parent: 1668 + - uid: 8770 + components: + - type: Transform + pos: -31.5,-17.5 + parent: 1668 - uid: 8800 components: - type: Transform @@ -21074,6 +21076,12 @@ entities: - type: Transform pos: 19.5,-48.5 parent: 1668 + - uid: 6124 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -32.5,-16.5 + parent: 1668 - uid: 6217 components: - type: Transform @@ -21084,6 +21092,11 @@ entities: - type: Transform pos: -5.5,-17.5 parent: 1668 + - uid: 6672 + components: + - type: Transform + pos: -32.5,-17.5 + parent: 1668 - uid: 7339 components: - type: Transform @@ -22604,6 +22617,13 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: ClothingHeadHatCatEars + entities: + - uid: 9693 + components: + - type: Transform + pos: -26.009851,-17.232796 + parent: 1668 - proto: ClothingHeadHatCentcomcap entities: - uid: 9398 @@ -24127,26 +24147,6 @@ entities: parent: 1668 - proto: CurtainsBlack entities: - - uid: 8768 - components: - - type: Transform - pos: -28.5,-15.5 - parent: 1668 - - uid: 8769 - components: - - type: Transform - pos: -29.5,-15.5 - parent: 1668 - - uid: 8770 - components: - - type: Transform - pos: -31.5,-15.5 - parent: 1668 - - uid: 8771 - components: - - type: Transform - pos: -32.5,-15.5 - parent: 1668 - uid: 8772 components: - type: Transform @@ -27614,6 +27614,13 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: FoodCheeseSlice + entities: + - uid: 9687 + components: + - type: Transform + pos: -31.446606,-17.398685 + parent: 1668 - proto: FoodCorn entities: - uid: 5453 @@ -40497,6 +40504,11 @@ entities: - type: Transform pos: 23.5,-31.5 parent: 1668 + - uid: 1587 + components: + - type: Transform + pos: 26.5,-28.5 + parent: 1668 - uid: 1588 components: - type: Transform @@ -42547,6 +42559,13 @@ entities: - type: Transform pos: -11.5,-32.5 parent: 1668 +- proto: KitchenKnife + entities: + - uid: 9692 + components: + - type: Transform + pos: -29.015015,-17.370127 + parent: 1668 - proto: KitchenMicrowave entities: - uid: 1169 @@ -44566,6 +44585,18 @@ entities: - type: Transform pos: -36.464035,13.631715 parent: 1668 + - uid: 9690 + components: + - type: Transform + pos: -28.43419,-17.370127 + parent: 1668 +- proto: PlushieLizardInversed + entities: + - uid: 9691 + components: + - type: Transform + pos: -29.558764,-17.370127 + parent: 1668 - proto: PlushieSharkBlue entities: - uid: 9288 @@ -45705,6 +45736,12 @@ entities: - type: Transform pos: -4.5,-16.5 parent: 1668 + - uid: 6678 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -32.5,-17.5 + parent: 1668 - proto: Poweredlight entities: - uid: 126 @@ -46381,12 +46418,6 @@ entities: rot: 3.141592653589793 rad pos: -29.5,-17.5 parent: 1668 - - uid: 6678 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -32.5,-17.5 - parent: 1668 - uid: 6679 components: - type: Transform @@ -49139,6 +49170,11 @@ entities: - type: Transform pos: -36.5,-3.5 parent: 1668 + - uid: 9694 + components: + - type: Transform + pos: 26.5,-28.5 + parent: 1668 - proto: RitualDagger entities: - uid: 6114 @@ -50617,7 +50653,7 @@ entities: - uid: 4662 components: - type: MetaData - desc: Current Headmin. + desc: I'm not a cat. name: nikthechampiongr - type: Transform pos: -24.5,-15.5 @@ -50670,6 +50706,22 @@ entities: - type: Transform pos: -22.5,-9.5 parent: 1668 + - uid: 8768 + components: + - type: MetaData + desc: Current Headmin. + name: Reisama + - type: Transform + pos: -30.5,-15.5 + parent: 1668 + - uid: 8769 + components: + - type: MetaData + desc: Current Headmin. + name: AdmiralObvious + - type: Transform + pos: -27.5,-15.5 + parent: 1668 - uid: 9395 components: - type: MetaData @@ -51497,13 +51549,6 @@ entities: - type: Transform pos: -15.5,25.5 parent: 1668 -- proto: SuitStorageBasic - entities: - - uid: 2911 - components: - - type: Transform - pos: -10.5,5.5 - parent: 1668 - proto: SuitStorageEngi entities: - uid: 4035 @@ -51511,6 +51556,13 @@ entities: - type: Transform pos: -15.5,24.5 parent: 1668 +- proto: SuitStorageEVA + entities: + - uid: 2911 + components: + - type: Transform + pos: -10.5,5.5 + parent: 1668 - proto: SurveillanceCameraCommand entities: - uid: 9397 @@ -52665,6 +52717,11 @@ entities: rot: 3.141592653589793 rad pos: -31.5,-7.5 parent: 1668 + - uid: 6671 + components: + - type: Transform + pos: -31.5,-17.5 + parent: 1668 - uid: 6681 components: - type: Transform @@ -52701,6 +52758,12 @@ entities: rot: 3.141592653589793 rad pos: -31.5,-10.5 parent: 1668 + - uid: 8771 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -32.5,-17.5 + parent: 1668 - uid: 8817 components: - type: Transform @@ -52871,6 +52934,18 @@ entities: - type: Transform pos: -37.5,3.5 parent: 1668 +- proto: TableFancyCyan + entities: + - uid: 9688 + components: + - type: Transform + pos: -29.5,-17.5 + parent: 1668 + - uid: 9689 + components: + - type: Transform + pos: -28.5,-17.5 + parent: 1668 - proto: TableFancyGreen entities: - uid: 6066 @@ -52883,32 +52958,12 @@ entities: - type: Transform pos: -11.5,-17.5 parent: 1668 - - uid: 6124 - components: - - type: Transform - pos: -29.5,-17.5 - parent: 1668 - - uid: 6125 - components: - - type: Transform - pos: -28.5,-17.5 - parent: 1668 - uid: 6173 components: - type: Transform rot: 1.5707963267948966 rad pos: -24.5,-7.5 parent: 1668 - - uid: 6671 - components: - - type: Transform - pos: -31.5,-17.5 - parent: 1668 - - uid: 6672 - components: - - type: Transform - pos: -32.5,-17.5 - parent: 1668 - uid: 6673 components: - type: Transform @@ -54844,6 +54899,13 @@ entities: - type: Transform pos: -19.5,-8.5 parent: 1668 +- proto: ToyMouse + entities: + - uid: 6125 + components: + - type: Transform + pos: -32.09308,-17.190353 + parent: 1668 - proto: ToyRubberDuck entities: - uid: 2793 From fe7b96147c05afc160c33f033deca384e2886755 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 3 Jul 2025 14:36:06 +1000 Subject: [PATCH 172/191] Biome rework (#37735) * DungeonData rework Back to fields, serializes better, just make new layers dumby. * wawawewa * Fix this * Fixes * Port the work over * wawawewa * zoom * Kinda workin * Adjust wawa * Unloading work * Ore + entitytable fixes Iterate every dungeon not just last. * Big shot * wawawewa * Fixes * true * Fixes # Conflicts: # Content.Server/Procedural/DungeonJob/DungeonJob.cs * wawawewa * Fixes * Fix * Lot of work * wawawewa * Fixing * eh? * a * Fix a heap of stuff * Better ignored check * Reserve tile changes * biome * changes * wawawewa * Fixes & snow * Shadow fixes * wawawewa * smol * Add layer API * More work * wawawewa * Preloads and running again * wawawewa * Modified * Replacements and command * Runtime support * werk * Fix expeds + dungeon alltiles * reh --------- Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> --- Content.Client/Parallax/BiomeDebugOverlay.cs | 87 -- Content.Client/Parallax/BiomeSystem.cs | 8 - .../Parallax/Commands/ShowBiomeCommand.cs | 22 - Content.Client/Parallax/ParallaxOverlay.cs | 6 +- .../Salvage/UI/OfferingWindowOption.xaml.cs | 14 - .../Components/GatewayGeneratorComponent.cs | 6 +- .../Gateway/Systems/GatewayGeneratorSystem.cs | 9 +- Content.Server/Maps/PlanetCommand.cs | 20 +- .../Pathfinding/PathfindingSystem.Simple.cs | 6 +- .../Parallax/BiomeSystem.Commands.cs | 188 --- Content.Server/Parallax/BiomeSystem.cs | 1086 ----------------- .../Procedural/BiomeSystem.Commands.cs | 105 ++ .../Procedural/BiomeSystem.Planet.cs | 65 + Content.Server/Procedural/BiomeSystem.cs | 661 ++++++++++ .../DungeonJob/DungeonJob.AutoCabling.cs | 3 +- .../Procedural/DungeonJob/DungeonJob.Biome.cs | 74 -- .../DungeonJob/DungeonJob.BiomeMarkerLayer.cs | 105 -- .../DungeonJob/DungeonJob.BoundaryWall.cs | 29 +- .../Procedural/DungeonJob/DungeonJob.Chunk.cs | 42 + .../DungeonJob/DungeonJob.Corridor.cs | 8 +- .../DungeonJob/DungeonJob.CorridorClutter.cs | 8 +- .../DungeonJob.CorridorDecalSkirting.cs | 9 +- .../DungeonJob.DunGenNoiseDistance.cs | 8 +- .../DungeonJob/DungeonJob.DunGenPrefab.cs | 13 +- .../DungeonJob.DunGenReplaceTile.cs | 1 + .../DungeonJob/DungeonJob.DungeonEntrance.cs | 7 +- .../DungeonJob.EntityTableDunGen.cs | 1 + .../DungeonJob/DungeonJob.EntranceFlank.cs | 11 +- .../DungeonJob/DungeonJob.Exterior.cs | 28 +- .../DungeonJob/DungeonJob.ExternalWindow.cs | 12 +- .../Procedural/DungeonJob/DungeonJob.Fill.cs | 32 +- .../DungeonJob/DungeonJob.Helpers.cs | 2 + .../DungeonJob/DungeonJob.InternalWindow.cs | 11 +- .../DungeonJob/DungeonJob.Junction.cs | 11 +- .../DungeonJob/DungeonJob.MiddleConnection.cs | 19 +- .../Procedural/DungeonJob/DungeonJob.Mobs.cs | 1 + .../Procedural/DungeonJob/DungeonJob.Noise.cs | 10 +- .../Procedural/DungeonJob/DungeonJob.Ore.cs | 193 +-- .../Procedural/DungeonJob/DungeonJob.Roof.cs | 38 + .../DungeonJob/DungeonJob.RoomEntrance.cs | 11 +- .../DungeonJob/DungeonJob.SampleDecal.cs | 64 + .../DungeonJob/DungeonJob.SampleEntity.cs | 62 + .../DungeonJob/DungeonJob.SampleTile.cs | 66 + .../DungeonJob.SplineDungeonConnector.cs | 8 +- .../DungeonJob/DungeonJob.WallMount.cs | 11 +- .../Procedural/DungeonJob/DungeonJob.cs | 123 +- .../Procedural/DungeonSystem.Rooms.cs | 11 +- Content.Server/Procedural/DungeonSystem.cs | 16 +- .../Salvage/SpawnSalvageMissionJob.cs | 76 +- .../Shuttles/Systems/ArrivalsSystem.cs | 10 +- .../Systems/ShuttleSystem.FasterThanLight.cs | 13 +- .../Shuttles/Systems/ShuttleSystem.cs | 3 + .../Components/StationBiomeComponent.cs | 3 +- .../Station/Systems/StationBiomeSystem.cs | 1 + Content.Server/Tabletop/TabletopSystem.cs | 2 +- Content.Shared/CCVar/CCVars.Biome.cs | 18 + .../Parallax/Biomes/BiomeComponent.cs | 87 -- .../Parallax/Biomes/BiomeTemplatePrototype.cs | 16 - .../Parallax/Biomes/Layers/BiomeDecalLayer.cs | 34 - .../Parallax/Biomes/Layers/BiomeDummyLayer.cs | 18 - .../Biomes/Layers/BiomeEntityLayer.cs | 27 - .../Parallax/Biomes/Layers/BiomeMetaLayer.cs | 27 - .../Parallax/Biomes/Layers/IBiomeLayer.cs | 22 - .../Biomes/Layers/IBiomeWorldLayer.cs | 12 - .../Markers/BiomeMarkerLayerPrototype.cs | 53 - .../Biomes/Markers/IBiomeMarkerLayer.cs | 22 - .../Parallax/Biomes/SharedBiomeSystem.cs | 386 ------ .../Procedural/Components/BiomeComponent.cs | 86 ++ .../Components/BiomeForceUnloadComponent.cs | 9 + .../Distance/DunGenDistanceSquared.cs | 7 + Content.Shared/Procedural/DungeonConfig.cs | 9 +- Content.Shared/Procedural/DungeonData.cs | 41 + .../DungeonGenerators/ChunkDunGen.cs | 24 + .../DungeonGenerators/ExteriorDunGen.cs | 6 + .../DungeonGenerators/PrototypeDunGen.cs | 19 + .../AutoCablingDunGen.cs | 0 .../BoundaryWallDunGen.cs | 0 .../CornerClutterDunGen.cs | 0 .../CorridorClutterDunGen.cs | 0 .../CorridorDecalSkirtingDunGen.cs | 0 .../CorridorDunGen.cs | 0 .../DungeonEntranceDunGen.cs | 0 .../EntranceFlankDunGen.cs | 1 + .../ExternalWindowDunGen.cs | 1 - .../DungeonLayers/FillGridDunGen.cs | 32 +- .../InternalWindowDunGen.cs | 0 .../JunctionDunGen.cs | 0 .../MiddleConnectionDunGen.cs | 0 .../Procedural/DungeonLayers/MobsDunGen.cs | 2 - .../Procedural/DungeonLayers/RoofDunGen.cs | 15 + .../RoomEntranceDunGen.cs | 1 - .../DungeonLayers/SampleDecalDunGen.cs | 36 + .../DungeonLayers/SampleEntityDunGen.cs | 31 + .../DungeonLayers/SampleTileDunGen.cs} | 16 +- .../SplineDungeonConnectorDunGen.cs | 6 +- .../WallMountDunGen.cs | 0 .../WormCorridorDunGen.cs | 0 Content.Shared/Procedural/Loot/BiomeLoot.cs | 12 + .../Procedural/Loot/BiomeMarkerLoot.cs | 15 - .../Procedural/Loot/BiomeTemplateLoot.cs | 14 - .../Procedural/PostGeneration/BiomeDunGen.cs | 21 - .../PostGeneration/BiomeMarkerLayerDunGen.cs | 19 - .../Modifiers/SalvageBiomeModPrototype.cs | 6 +- Resources/Locale/en-US/procedural/biome.ftl | 4 - Resources/Prototypes/Entities/Tiles/water.yml | 6 +- .../Prototypes/Procedural/Magnet/asteroid.yml | 20 +- .../Procedural/Magnet/space_debris.yml | 10 +- .../Magnet/space_debris_templates.yml | 16 +- .../Prototypes/Procedural/biome_markers.yml | 147 ++- .../Procedural/biome_ore_templates.yml | 219 ++-- .../Procedural/biome_ore_templates_low.yml | 219 ++-- .../Prototypes/Procedural/biome_templates.yml | 1040 +++++++++------- .../Prototypes/Procedural/dungeon_configs.yml | 3 + .../Prototypes/Procedural/salvage_loot.yml | 244 ++-- .../Prototypes/Procedural/salvage_mods.yml | 8 +- Resources/Prototypes/Procedural/vgroid.yml | 16 +- 116 files changed, 3005 insertions(+), 3537 deletions(-) delete mode 100644 Content.Client/Parallax/BiomeDebugOverlay.cs delete mode 100644 Content.Client/Parallax/BiomeSystem.cs delete mode 100644 Content.Client/Parallax/Commands/ShowBiomeCommand.cs delete mode 100644 Content.Server/Parallax/BiomeSystem.Commands.cs delete mode 100644 Content.Server/Parallax/BiomeSystem.cs create mode 100644 Content.Server/Procedural/BiomeSystem.Commands.cs create mode 100644 Content.Server/Procedural/BiomeSystem.Planet.cs create mode 100644 Content.Server/Procedural/BiomeSystem.cs delete mode 100644 Content.Server/Procedural/DungeonJob/DungeonJob.Biome.cs delete mode 100644 Content.Server/Procedural/DungeonJob/DungeonJob.BiomeMarkerLayer.cs create mode 100644 Content.Server/Procedural/DungeonJob/DungeonJob.Chunk.cs create mode 100644 Content.Server/Procedural/DungeonJob/DungeonJob.Roof.cs create mode 100644 Content.Server/Procedural/DungeonJob/DungeonJob.SampleDecal.cs create mode 100644 Content.Server/Procedural/DungeonJob/DungeonJob.SampleEntity.cs create mode 100644 Content.Server/Procedural/DungeonJob/DungeonJob.SampleTile.cs create mode 100644 Content.Shared/CCVar/CCVars.Biome.cs delete mode 100644 Content.Shared/Parallax/Biomes/BiomeComponent.cs delete mode 100644 Content.Shared/Parallax/Biomes/BiomeTemplatePrototype.cs delete mode 100644 Content.Shared/Parallax/Biomes/Layers/BiomeDecalLayer.cs delete mode 100644 Content.Shared/Parallax/Biomes/Layers/BiomeDummyLayer.cs delete mode 100644 Content.Shared/Parallax/Biomes/Layers/BiomeEntityLayer.cs delete mode 100644 Content.Shared/Parallax/Biomes/Layers/BiomeMetaLayer.cs delete mode 100644 Content.Shared/Parallax/Biomes/Layers/IBiomeLayer.cs delete mode 100644 Content.Shared/Parallax/Biomes/Layers/IBiomeWorldLayer.cs delete mode 100644 Content.Shared/Parallax/Biomes/Markers/BiomeMarkerLayerPrototype.cs delete mode 100644 Content.Shared/Parallax/Biomes/Markers/IBiomeMarkerLayer.cs delete mode 100644 Content.Shared/Parallax/Biomes/SharedBiomeSystem.cs create mode 100644 Content.Shared/Procedural/Components/BiomeComponent.cs create mode 100644 Content.Shared/Procedural/Components/BiomeForceUnloadComponent.cs create mode 100644 Content.Shared/Procedural/Distance/DunGenDistanceSquared.cs create mode 100644 Content.Shared/Procedural/DungeonData.cs create mode 100644 Content.Shared/Procedural/DungeonGenerators/ChunkDunGen.cs rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/AutoCablingDunGen.cs (100%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/BoundaryWallDunGen.cs (100%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/CornerClutterDunGen.cs (100%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/CorridorClutterDunGen.cs (100%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/CorridorDecalSkirtingDunGen.cs (100%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/CorridorDunGen.cs (100%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/DungeonEntranceDunGen.cs (100%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/EntranceFlankDunGen.cs (93%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/ExternalWindowDunGen.cs (94%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/InternalWindowDunGen.cs (100%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/JunctionDunGen.cs (100%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/MiddleConnectionDunGen.cs (100%) create mode 100644 Content.Shared/Procedural/DungeonLayers/RoofDunGen.cs rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/RoomEntranceDunGen.cs (93%) create mode 100644 Content.Shared/Procedural/DungeonLayers/SampleDecalDunGen.cs create mode 100644 Content.Shared/Procedural/DungeonLayers/SampleEntityDunGen.cs rename Content.Shared/{Parallax/Biomes/Layers/BiomeTileLayer.cs => Procedural/DungeonLayers/SampleTileDunGen.cs} (66%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/SplineDungeonConnectorDunGen.cs (83%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/WallMountDunGen.cs (100%) rename Content.Shared/Procedural/{PostGeneration => DungeonLayers}/WormCorridorDunGen.cs (100%) create mode 100644 Content.Shared/Procedural/Loot/BiomeLoot.cs delete mode 100644 Content.Shared/Procedural/Loot/BiomeMarkerLoot.cs delete mode 100644 Content.Shared/Procedural/Loot/BiomeTemplateLoot.cs delete mode 100644 Content.Shared/Procedural/PostGeneration/BiomeDunGen.cs delete mode 100644 Content.Shared/Procedural/PostGeneration/BiomeMarkerLayerDunGen.cs diff --git a/Content.Client/Parallax/BiomeDebugOverlay.cs b/Content.Client/Parallax/BiomeDebugOverlay.cs deleted file mode 100644 index c914cb5de7..0000000000 --- a/Content.Client/Parallax/BiomeDebugOverlay.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Numerics; -using System.Text; -using Content.Shared.Parallax.Biomes; -using Robust.Client.Graphics; -using Robust.Client.Input; -using Robust.Client.ResourceManagement; -using Robust.Shared.Enums; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; - -namespace Content.Client.Parallax; - -public sealed class BiomeDebugOverlay : Overlay -{ - public override OverlaySpace Space => OverlaySpace.ScreenSpace; - - [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IEyeManager _eyeManager = default!; - [Dependency] private readonly IInputManager _inputManager = default!; - [Dependency] private readonly IResourceCache _cache = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; - - private BiomeSystem _biomes; - private SharedMapSystem _maps; - - private Font _font; - - public BiomeDebugOverlay() - { - IoCManager.InjectDependencies(this); - - _biomes = _entManager.System(); - _maps = _entManager.System(); - - _font = new VectorFont(_cache.GetResource("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 12); - } - - protected override bool BeforeDraw(in OverlayDrawArgs args) - { - var mapUid = _maps.GetMapOrInvalid(args.MapId); - - return _entManager.HasComponent(mapUid); - } - - protected override void Draw(in OverlayDrawArgs args) - { - var mouseScreenPos = _inputManager.MouseScreenPosition; - var mousePos = _eyeManager.ScreenToMap(mouseScreenPos); - - if (mousePos.MapId == MapId.Nullspace || mousePos.MapId != args.MapId) - return; - - var mapUid = _maps.GetMapOrInvalid(args.MapId); - - if (!_entManager.TryGetComponent(mapUid, out BiomeComponent? biomeComp) || !_entManager.TryGetComponent(mapUid, out MapGridComponent? grid)) - return; - - var sb = new StringBuilder(); - var nodePos = _maps.WorldToTile(mapUid, grid, mousePos.Position); - - if (_biomes.TryGetEntity(nodePos, biomeComp, (mapUid, grid), out var ent)) - { - var text = $"Entity: {ent}"; - sb.AppendLine(text); - } - - if (_biomes.TryGetDecals(nodePos, biomeComp.Layers, biomeComp.Seed, (mapUid, grid), out var decals)) - { - var text = $"Decals: {decals.Count}"; - sb.AppendLine(text); - - foreach (var decal in decals) - { - var decalText = $"- {decal.ID}"; - sb.AppendLine(decalText); - } - } - - if (_biomes.TryGetBiomeTile(nodePos, biomeComp.Layers, biomeComp.Seed, (mapUid, grid), out var tile)) - { - var tileText = $"Tile: {_tileDefManager[tile.Value.TypeId].ID}"; - sb.AppendLine(tileText); - } - - args.ScreenHandle.DrawString(_font, mouseScreenPos.Position + new Vector2(0f, 32f), sb.ToString()); - } -} diff --git a/Content.Client/Parallax/BiomeSystem.cs b/Content.Client/Parallax/BiomeSystem.cs deleted file mode 100644 index dc326e1fa7..0000000000 --- a/Content.Client/Parallax/BiomeSystem.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Shared.Parallax.Biomes; - -namespace Content.Client.Parallax; - -public sealed class BiomeSystem : SharedBiomeSystem -{ - -} diff --git a/Content.Client/Parallax/Commands/ShowBiomeCommand.cs b/Content.Client/Parallax/Commands/ShowBiomeCommand.cs deleted file mode 100644 index 2a628dd931..0000000000 --- a/Content.Client/Parallax/Commands/ShowBiomeCommand.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Robust.Client.Graphics; -using Robust.Shared.Console; - -namespace Content.Client.Parallax.Commands; - -public sealed class ShowBiomeCommand : LocalizedCommands -{ - [Dependency] private readonly IOverlayManager _overlayMgr = default!; - - public override string Command => "showbiome"; - public override void Execute(IConsoleShell shell, string argStr, string[] args) - { - if (_overlayMgr.HasOverlay()) - { - _overlayMgr.RemoveOverlay(); - } - else - { - _overlayMgr.AddOverlay(new BiomeDebugOverlay()); - } - } -} diff --git a/Content.Client/Parallax/ParallaxOverlay.cs b/Content.Client/Parallax/ParallaxOverlay.cs index 06f830675d..8e3a1dddfe 100644 --- a/Content.Client/Parallax/ParallaxOverlay.cs +++ b/Content.Client/Parallax/ParallaxOverlay.cs @@ -1,8 +1,6 @@ using System.Numerics; using Content.Client.Parallax.Managers; using Content.Shared.CCVar; -using Content.Shared.Parallax.Biomes; -using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Configuration; using Robust.Shared.Enums; @@ -19,7 +17,6 @@ public sealed class ParallaxOverlay : Overlay [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IParallaxManager _manager = default!; - private readonly SharedMapSystem _mapSystem; private readonly ParallaxSystem _parallax; public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowWorld; @@ -28,13 +25,12 @@ public sealed class ParallaxOverlay : Overlay { ZIndex = ParallaxSystem.ParallaxZIndex; IoCManager.InjectDependencies(this); - _mapSystem = _entManager.System(); _parallax = _entManager.System(); } protected override bool BeforeDraw(in OverlayDrawArgs args) { - if (args.MapId == MapId.Nullspace || _entManager.HasComponent(_mapSystem.GetMapOrInvalid(args.MapId))) + if (args.MapId == MapId.Nullspace) return false; return true; diff --git a/Content.Client/Salvage/UI/OfferingWindowOption.xaml.cs b/Content.Client/Salvage/UI/OfferingWindowOption.xaml.cs index 7855577e69..8d718f1be5 100644 --- a/Content.Client/Salvage/UI/OfferingWindowOption.xaml.cs +++ b/Content.Client/Salvage/UI/OfferingWindowOption.xaml.cs @@ -1,23 +1,9 @@ -using System.Linq; -using Content.Client.Computer; using Content.Client.Stylesheets; -using Content.Client.UserInterface.Controls; -using Content.Shared.CCVar; -using Content.Shared.Parallax.Biomes; -using Content.Shared.Procedural; -using Content.Shared.Salvage; -using Content.Shared.Salvage.Expeditions; -using Content.Shared.Salvage.Expeditions.Modifiers; -using Content.Shared.Shuttles.BUIStates; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Configuration; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; -using Robust.Shared.Utility; namespace Content.Client.Salvage.UI; diff --git a/Content.Server/Gateway/Components/GatewayGeneratorComponent.cs b/Content.Server/Gateway/Components/GatewayGeneratorComponent.cs index d65760e270..03a7ec5c4d 100644 --- a/Content.Server/Gateway/Components/GatewayGeneratorComponent.cs +++ b/Content.Server/Gateway/Components/GatewayGeneratorComponent.cs @@ -1,4 +1,4 @@ -using Content.Shared.Parallax.Biomes.Markers; +using Content.Shared.Procedural; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; @@ -42,7 +42,7 @@ public sealed partial class GatewayGeneratorComponent : Component /// Mob layers to pick from. /// [DataField] - public List> MobLayers = new() + public List> MobLayers = new() { "Carps", "Xenos", @@ -54,7 +54,7 @@ public sealed partial class GatewayGeneratorComponent : Component /// /// Loot layers to pick from. /// - public List> LootLayers = new() + public List> LootLayers = new() { "OreIron", "OreQuartz", diff --git a/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs b/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs index 83471cdbc1..7f4bdccf2e 100644 --- a/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs +++ b/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs @@ -1,12 +1,11 @@ using System.Linq; using Content.Server.Gateway.Components; -using Content.Server.Parallax; using Content.Server.Procedural; using Content.Shared.CCVar; using Content.Shared.Dataset; using Content.Shared.Maps; -using Content.Shared.Parallax.Biomes; using Content.Shared.Procedural; +using Content.Shared.Procedural.Components; using Content.Shared.Salvage; using Robust.Shared.Configuration; using Robust.Shared.Map; @@ -111,7 +110,7 @@ public sealed class GatewayGeneratorSystem : EntitySystem }; AddComp(mapUid, restricted); - _biome.EnsurePlanet(mapUid, _protoManager.Index("Continental"), seed); + _biome.EnsurePlanet(mapUid, _protoManager.Index("BiomeContinental"), seed); var grid = Comp(mapUid); @@ -199,7 +198,7 @@ public sealed class GatewayGeneratorSystem : EntitySystem var layer = lootLayers[layerIdx]; lootLayers.RemoveSwap(layerIdx); - _biome.AddMarkerLayer(ent.Owner, biomeComp, layer.Id); + _biome.AddLayer((ent.Owner, biomeComp), $"{layer.Id}-{i}", layer.Id); } // - Mobs @@ -211,7 +210,7 @@ public sealed class GatewayGeneratorSystem : EntitySystem var layer = mobLayers[layerIdx]; mobLayers.RemoveSwap(layerIdx); - _biome.AddMarkerLayer(ent.Owner, biomeComp, layer.Id); + _biome.AddLayer((ent.Owner, biomeComp), $"{layer.Id}-{i}", layer.Id); } } } diff --git a/Content.Server/Maps/PlanetCommand.cs b/Content.Server/Maps/PlanetCommand.cs index 8e8b5b10ed..d3b7abc171 100644 --- a/Content.Server/Maps/PlanetCommand.cs +++ b/Content.Server/Maps/PlanetCommand.cs @@ -1,20 +1,11 @@ using System.Linq; using Content.Server.Administration; -using Content.Server.Atmos; -using Content.Server.Atmos.Components; -using Content.Server.Atmos.EntitySystems; -using Content.Server.Parallax; +using Content.Server.Procedural; using Content.Shared.Administration; -using Content.Shared.Atmos; -using Content.Shared.Gravity; -using Content.Shared.Movement.Components; -using Content.Shared.Parallax.Biomes; -using Robust.Shared.Audio; +using Content.Shared.Procedural.Components; using Robust.Shared.Console; using Robust.Shared.Map; -using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; -using Robust.Shared.Random; namespace Content.Server.Maps; @@ -52,7 +43,7 @@ public sealed class PlanetCommand : LocalizedEntityCommands return; } - if (!_protoManager.TryIndex(args[1], out var biomeTemplate)) + if (!_protoManager.TryIndex(args[1], out var biomeTemplate)) { shell.WriteError(Loc.GetString("cmd-planet-map-prototype", ("prototype", args[1]))); return; @@ -70,9 +61,12 @@ public sealed class PlanetCommand : LocalizedEntityCommands if (args.Length == 1) return CompletionResult.FromHintOptions(CompletionHelper.MapIds(_entManager), "Map Id"); + var biomeName = _entManager.ComponentFactory.GetComponentName(); + if (args.Length == 2) { - var options = _protoManager.EnumeratePrototypes() + var options = _protoManager.EnumeratePrototypes() + .Where(o => o.Components.ContainsKey(biomeName)) .Select(o => new CompletionOption(o.ID, "Biome")); return CompletionResult.FromOptions(options); } diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.Simple.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.Simple.cs index 7afd3d78df..07d6a624c8 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.Simple.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.Simple.cs @@ -65,6 +65,9 @@ public sealed partial class PathfindingSystem { for (var y = -1; y <= 1; y++) { + if (x == 0 && y == 0) + continue; + var neighbor = node + new Vector2i(x, y); var neighborCost = OctileDistance(node, neighbor) * args.TileCost?.Invoke(neighbor) ?? 1f; @@ -121,8 +124,7 @@ public sealed partial class PathfindingSystem cameFrom[neighbor] = node; costSoFar[neighbor] = gScore; - // Still use octile even for manhattan distance. - var hScore = OctileDistance(args.End, neighbor) * 1.001f; + var hScore = ManhattanDistance(args.End, neighbor); var fScore = gScore + hScore; frontier.Enqueue(neighbor, fScore); } diff --git a/Content.Server/Parallax/BiomeSystem.Commands.cs b/Content.Server/Parallax/BiomeSystem.Commands.cs deleted file mode 100644 index a915e17fcb..0000000000 --- a/Content.Server/Parallax/BiomeSystem.Commands.cs +++ /dev/null @@ -1,188 +0,0 @@ -using Content.Server.Administration; -using Content.Shared.Administration; -using Content.Shared.Parallax.Biomes; -using Content.Shared.Parallax.Biomes.Layers; -using Content.Shared.Parallax.Biomes.Markers; -using Robust.Shared.Console; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; - -namespace Content.Server.Parallax; - -public sealed partial class BiomeSystem -{ - private void InitializeCommands() - { - _console.RegisterCommand("biome_clear", Loc.GetString("cmd-biome_clear-desc"), Loc.GetString("cmd-biome_clear-help"), BiomeClearCallback, BiomeClearCallbackHelper); - _console.RegisterCommand("biome_addlayer", Loc.GetString("cmd-biome_addlayer-desc"), Loc.GetString("cmd-biome_addlayer-help"), AddLayerCallback, AddLayerCallbackHelp); - _console.RegisterCommand("biome_addmarkerlayer", Loc.GetString("cmd-biome_addmarkerlayer-desc"), Loc.GetString("cmd-biome_addmarkerlayer-desc"), AddMarkerLayerCallback, AddMarkerLayerCallbackHelper); - } - - [AdminCommand(AdminFlags.Fun)] - private void BiomeClearCallback(IConsoleShell shell, string argstr, string[] args) - { - if (args.Length != 1) - { - return; - } - - int.TryParse(args[0], out var mapInt); - var mapId = new MapId(mapInt); - var mapUid = _mapSystem.GetMapOrInvalid(mapId); - - if (_mapSystem.MapExists(mapId) || - !TryComp(mapUid, out var biome)) - { - return; - } - - ClearTemplate(mapUid, biome); - } - - private CompletionResult BiomeClearCallbackHelper(IConsoleShell shell, string[] args) - { - if (args.Length == 1) - { - return CompletionResult.FromHintOptions(CompletionHelper.Components(args[0], EntityManager), "Biome"); - } - - return CompletionResult.Empty; - } - - [AdminCommand(AdminFlags.Fun)] - private void AddLayerCallback(IConsoleShell shell, string argstr, string[] args) - { - if (args.Length < 3 || args.Length > 4) - { - return; - } - - if (!int.TryParse(args[0], out var mapInt)) - { - return; - } - - var mapId = new MapId(mapInt); - var mapUid = _mapSystem.GetMapOrInvalid(mapId); - - if (!_mapSystem.MapExists(mapId) || !TryComp(mapUid, out var biome)) - { - return; - } - - if (!ProtoManager.TryIndex(args[1], out var template)) - { - return; - } - - var offset = 0; - - if (args.Length == 4) - { - int.TryParse(args[3], out offset); - } - - AddTemplate(mapUid, biome, args[2], template, offset); - } - - private CompletionResult AddLayerCallbackHelp(IConsoleShell shell, string[] args) - { - if (args.Length == 1) - { - return CompletionResult.FromHintOptions(CompletionHelper.MapIds(EntityManager), "Map ID"); - } - - if (args.Length == 2) - { - return CompletionResult.FromHintOptions( - CompletionHelper.PrototypeIDs(proto: ProtoManager), "Biome template"); - } - - if (args.Length == 3) - { - if (int.TryParse(args[0], out var mapInt)) - { - var mapId = new MapId(mapInt); - - if (TryComp(_mapSystem.GetMapOrInvalid(mapId), out var biome)) - { - var results = new List(); - - foreach (var layer in biome.Layers) - { - if (layer is not BiomeDummyLayer dummy) - continue; - - results.Add(dummy.ID); - } - - return CompletionResult.FromHintOptions(results, "Dummy layer ID"); - } - } - } - - if (args.Length == 4) - { - return CompletionResult.FromHint("Seed offset"); - } - - return CompletionResult.Empty; - } - - [AdminCommand(AdminFlags.Fun)] - private void AddMarkerLayerCallback(IConsoleShell shell, string argstr, string[] args) - { - if (args.Length != 2) - { - return; - } - - if (!int.TryParse(args[0], out var mapInt)) - { - return; - } - - var mapId = new MapId(mapInt); - - if (!_mapSystem.MapExists(mapId) || !TryComp(_mapSystem.GetMapOrInvalid(mapId), out var biome)) - { - return; - } - - if (!ProtoManager.HasIndex(args[1])) - { - return; - } - - if (!biome.MarkerLayers.Add(args[1])) - { - return; - } - - biome.ForcedMarkerLayers.Add(args[1]); - } - - private CompletionResult AddMarkerLayerCallbackHelper(IConsoleShell shell, string[] args) - { - if (args.Length == 1) - { - var allQuery = AllEntityQuery(); - var options = new List(); - - while (allQuery.MoveNext(out var mapComp, out _)) - { - options.Add(new CompletionOption(mapComp.MapId.ToString())); - } - - return CompletionResult.FromHintOptions(options, "Biome"); - } - - if (args.Length == 2) - { - return CompletionResult.FromHintOptions( - CompletionHelper.PrototypeIDs(proto: ProtoManager), "Marker"); - } - - return CompletionResult.Empty; - } -} diff --git a/Content.Server/Parallax/BiomeSystem.cs b/Content.Server/Parallax/BiomeSystem.cs deleted file mode 100644 index 496cb387e8..0000000000 --- a/Content.Server/Parallax/BiomeSystem.cs +++ /dev/null @@ -1,1086 +0,0 @@ -using System.Linq; -using System.Numerics; -using System.Threading.Tasks; -using Content.Server.Atmos; -using Content.Server.Atmos.Components; -using Content.Server.Atmos.EntitySystems; -using Content.Server.Decals; -using Content.Server.Ghost.Roles.Components; -using Content.Server.Shuttles.Events; -using Content.Server.Shuttles.Systems; -using Content.Shared.Atmos; -using Content.Shared.Decals; -using Content.Shared.Ghost; -using Content.Shared.Gravity; -using Content.Shared.Light.Components; -using Content.Shared.Parallax.Biomes; -using Content.Shared.Parallax.Biomes.Layers; -using Content.Shared.Parallax.Biomes.Markers; -using Content.Shared.Tag; -using Microsoft.Extensions.ObjectPool; -using Robust.Server.Player; -using Robust.Shared; -using Robust.Shared.Collections; -using Robust.Shared.Configuration; -using Robust.Shared.Console; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Threading; -using Robust.Shared.Utility; -using ChunkIndicesEnumerator = Robust.Shared.Map.Enumerators.ChunkIndicesEnumerator; - -namespace Content.Server.Parallax; - -public sealed partial class BiomeSystem : SharedBiomeSystem -{ - [Dependency] private readonly IConfigurationManager _configManager = default!; - [Dependency] private readonly IConsoleHost _console = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IParallelManager _parallel = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly AtmosphereSystem _atmos = default!; - [Dependency] private readonly DecalSystem _decals = default!; - [Dependency] private readonly SharedMapSystem _mapSystem = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly ShuttleSystem _shuttles = default!; - [Dependency] private readonly TagSystem _tags = default!; - - private EntityQuery _biomeQuery; - private EntityQuery _fixturesQuery; - private EntityQuery _ghostQuery; - private EntityQuery _xformQuery; - - private readonly HashSet _handledEntities = new(); - private const float DefaultLoadRange = 16f; - private float _loadRange = DefaultLoadRange; - private static readonly ProtoId AllowBiomeLoadingTag = "AllowBiomeLoading"; - - private List<(Vector2i, Tile)> _tiles = new(); - - private ObjectPool> _tilePool = - new DefaultObjectPool>(new SetPolicy(), 256); - - /// - /// Load area for chunks containing tiles, decals etc. - /// - private Box2 _loadArea = new(-DefaultLoadRange, -DefaultLoadRange, DefaultLoadRange, DefaultLoadRange); - - /// - /// Stores the chunks active for this tick temporarily. - /// - private readonly Dictionary> _activeChunks = new(); - - private readonly Dictionary>> _markerChunks = new(); - - public override void Initialize() - { - base.Initialize(); - Log.Level = LogLevel.Debug; - _biomeQuery = GetEntityQuery(); - _fixturesQuery = GetEntityQuery(); - _ghostQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); - SubscribeLocalEvent(OnBiomeMapInit); - SubscribeLocalEvent(OnFTLStarted); - SubscribeLocalEvent(OnShuttleFlatten); - Subs.CVar(_configManager, CVars.NetMaxUpdateRange, SetLoadRange, true); - InitializeCommands(); - SubscribeLocalEvent(ProtoReload); - } - - private void ProtoReload(PrototypesReloadedEventArgs obj) - { - if (!obj.ByType.TryGetValue(typeof(BiomeTemplatePrototype), out var reloads)) - return; - - var query = AllEntityQuery(); - - while (query.MoveNext(out var uid, out var biome)) - { - if (biome.Template == null || !reloads.Modified.TryGetValue(biome.Template, out var proto)) - continue; - - SetTemplate(uid, biome, (BiomeTemplatePrototype)proto); - } - } - - private void SetLoadRange(float obj) - { - // Round it up - _loadRange = MathF.Ceiling(obj / ChunkSize) * ChunkSize; - _loadArea = new Box2(-_loadRange, -_loadRange, _loadRange, _loadRange); - } - - private void OnBiomeMapInit(EntityUid uid, BiomeComponent component, MapInitEvent args) - { - if (component.Seed == -1) - { - SetSeed(uid, component, _random.Next()); - } - - if (_proto.TryIndex(component.Template, out var biome)) - SetTemplate(uid, component, biome); - - var xform = Transform(uid); - var mapId = xform.MapID; - - if (mapId != MapId.Nullspace && HasComp(uid)) - { - var setTiles = new List<(Vector2i Index, Tile tile)>(); - - foreach (var grid in _mapManager.GetAllGrids(mapId)) - { - if (!_fixturesQuery.TryGetComponent(grid.Owner, out var fixtures)) - continue; - - // Don't want shuttles flying around now do we. - _shuttles.Disable(grid.Owner); - var pTransform = _physics.GetPhysicsTransform(grid.Owner); - - foreach (var fixture in fixtures.Fixtures.Values) - { - for (var i = 0; i < fixture.Shape.ChildCount; i++) - { - var aabb = fixture.Shape.ComputeAABB(pTransform, i); - - setTiles.Clear(); - ReserveTiles(uid, aabb, setTiles); - } - } - } - } - } - - public void SetEnabled(Entity ent, bool enabled = true) - { - if (!Resolve(ent, ref ent.Comp) || ent.Comp.Enabled == enabled) - return; - - ent.Comp.Enabled = enabled; - Dirty(ent, ent.Comp); - } - - public void SetSeed(EntityUid uid, BiomeComponent component, int seed, bool dirty = true) - { - component.Seed = seed; - - if (dirty) - Dirty(uid, component); - } - - public void ClearTemplate(EntityUid uid, BiomeComponent component, bool dirty = true) - { - component.Layers.Clear(); - component.Template = null; - - if (dirty) - Dirty(uid, component); - } - - /// - /// Sets the and refreshes layers. - /// - public void SetTemplate(EntityUid uid, BiomeComponent component, BiomeTemplatePrototype template, bool dirty = true) - { - component.Layers.Clear(); - component.Template = template.ID; - - foreach (var layer in template.Layers) - { - component.Layers.Add(layer); - } - - if (dirty) - Dirty(uid, component); - } - - /// - /// Adds the specified layer at the specified marker if it exists. - /// - public void AddLayer(EntityUid uid, BiomeComponent component, string id, IBiomeLayer addedLayer, int seedOffset = 0) - { - for (var i = 0; i < component.Layers.Count; i++) - { - var layer = component.Layers[i]; - - if (layer is not BiomeDummyLayer dummy || dummy.ID != id) - continue; - - addedLayer.Noise.SetSeed(addedLayer.Noise.GetSeed() + seedOffset); - component.Layers.Insert(i, addedLayer); - break; - } - - Dirty(uid, component); - } - - public void AddMarkerLayer(EntityUid uid, BiomeComponent component, string marker) - { - component.MarkerLayers.Add(marker); - Dirty(uid, component); - } - - /// - /// Adds the specified template at the specified marker if it exists, withour overriding every layer. - /// - public void AddTemplate(EntityUid uid, BiomeComponent component, string id, BiomeTemplatePrototype template, int seedOffset = 0) - { - for (var i = 0; i < component.Layers.Count; i++) - { - var layer = component.Layers[i]; - - if (layer is not BiomeDummyLayer dummy || dummy.ID != id) - continue; - - for (var j = template.Layers.Count - 1; j >= 0; j--) - { - var addedLayer = template.Layers[j]; - addedLayer.Noise.SetSeed(addedLayer.Noise.GetSeed() + seedOffset); - component.Layers.Insert(i, addedLayer); - } - - break; - } - - Dirty(uid, component); - } - - private void OnFTLStarted(ref FTLStartedEvent ev) - { - var targetMap = _transform.ToMapCoordinates(ev.TargetCoordinates); - var targetMapUid = _mapSystem.GetMapOrInvalid(targetMap.MapId); - - if (!TryComp(targetMapUid, out var biome)) - return; - - var preloadArea = new Vector2(32f, 32f); - var targetArea = new Box2(targetMap.Position - preloadArea, targetMap.Position + preloadArea); - Preload(targetMapUid, biome, targetArea); - } - - private void OnShuttleFlatten(ref ShuttleFlattenEvent ev) - { - if (!TryComp(ev.MapUid, out var biome) || - !TryComp(ev.MapUid, out var grid)) - { - return; - } - - var tiles = new List<(Vector2i Index, Tile Tile)>(); - - foreach (var aabb in ev.AABBs) - { - for (var x = Math.Floor(aabb.Left); x <= Math.Ceiling(aabb.Right); x++) - { - for (var y = Math.Floor(aabb.Bottom); y <= Math.Ceiling(aabb.Top); y++) - { - var index = new Vector2i((int)x, (int)y); - var chunk = SharedMapSystem.GetChunkIndices(index, ChunkSize); - - var mod = biome.ModifiedTiles.GetOrNew(chunk * ChunkSize); - - if (!mod.Add(index) || !TryGetBiomeTile(index, biome.Layers, biome.Seed, (ev.MapUid, grid), out var tile)) - continue; - - // If we flag it as modified then the tile is never set so need to do it ourselves. - tiles.Add((index, tile.Value)); - } - } - } - - _mapSystem.SetTiles(ev.MapUid, grid, tiles); - } - - /// - /// Preloads biome for the specified area. - /// - public void Preload(EntityUid uid, BiomeComponent component, Box2 area) - { - var markers = component.MarkerLayers; - var goobers = _markerChunks.GetOrNew(component); - - foreach (var layer in markers) - { - var proto = ProtoManager.Index(layer); - var enumerator = new ChunkIndicesEnumerator(area, proto.Size); - - while (enumerator.MoveNext(out var chunk)) - { - var chunkOrigin = chunk * proto.Size; - var layerChunks = goobers.GetOrNew(proto.ID); - layerChunks.Add(chunkOrigin.Value); - } - } - } - - private bool CanLoad(EntityUid uid) - { - return !_ghostQuery.HasComp(uid) || _tags.HasTag(uid, AllowBiomeLoadingTag); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - var biomes = AllEntityQuery(); - - while (biomes.MoveNext(out var biome)) - { - if (biome.LifeStage < ComponentLifeStage.Running) - continue; - - _activeChunks.Add(biome, _tilePool.Get()); - _markerChunks.GetOrNew(biome); - } - - // Get chunks in range - foreach (var pSession in Filter.GetAllPlayers(_playerManager)) - { - if (_xformQuery.TryGetComponent(pSession.AttachedEntity, out var xform) && - _handledEntities.Add(pSession.AttachedEntity.Value) && - _biomeQuery.TryGetComponent(xform.MapUid, out var biome) && - biome.Enabled && - CanLoad(pSession.AttachedEntity.Value)) - { - var worldPos = _transform.GetWorldPosition(xform); - AddChunksInRange(biome, worldPos); - - foreach (var layer in biome.MarkerLayers) - { - var layerProto = ProtoManager.Index(layer); - AddMarkerChunksInRange(biome, worldPos, layerProto); - } - } - - foreach (var viewer in pSession.ViewSubscriptions) - { - if (!_handledEntities.Add(viewer) || - !_xformQuery.TryGetComponent(viewer, out xform) || - !_biomeQuery.TryGetComponent(xform.MapUid, out biome) || - !biome.Enabled || - !CanLoad(viewer)) - { - continue; - } - - var worldPos = _transform.GetWorldPosition(xform); - AddChunksInRange(biome, worldPos); - - foreach (var layer in biome.MarkerLayers) - { - var layerProto = ProtoManager.Index(layer); - AddMarkerChunksInRange(biome, worldPos, layerProto); - } - } - } - - var loadBiomes = AllEntityQuery(); - - while (loadBiomes.MoveNext(out var gridUid, out var biome, out var grid)) - { - // If not MapInit don't run it. - if (biome.LifeStage < ComponentLifeStage.Running) - continue; - - if (!biome.Enabled) - continue; - - // Load new chunks - LoadChunks(biome, gridUid, grid, biome.Seed); - // Unload old chunks - UnloadChunks(biome, gridUid, grid, biome.Seed); - } - - _handledEntities.Clear(); - - foreach (var tiles in _activeChunks.Values) - { - _tilePool.Return(tiles); - } - - _activeChunks.Clear(); - _markerChunks.Clear(); - } - - private void AddChunksInRange(BiomeComponent biome, Vector2 worldPos) - { - var enumerator = new ChunkIndicesEnumerator(_loadArea.Translated(worldPos), ChunkSize); - - while (enumerator.MoveNext(out var chunkOrigin)) - { - _activeChunks[biome].Add(chunkOrigin.Value * ChunkSize); - } - } - - private void AddMarkerChunksInRange(BiomeComponent biome, Vector2 worldPos, IBiomeMarkerLayer layer) - { - // Offset the load area so it's centralised. - var loadArea = new Box2(0, 0, layer.Size, layer.Size); - var halfLayer = new Vector2(layer.Size / 2f); - - var enumerator = new ChunkIndicesEnumerator(loadArea.Translated(worldPos - halfLayer), layer.Size); - - while (enumerator.MoveNext(out var chunkOrigin)) - { - var lay = _markerChunks[biome].GetOrNew(layer.ID); - lay.Add(chunkOrigin.Value * layer.Size); - } - } - - #region Load - - /// - /// Loads all of the chunks for a particular biome, as well as handle any marker chunks. - /// - private void LoadChunks( - BiomeComponent component, - EntityUid gridUid, - MapGridComponent grid, - int seed) - { - BuildMarkerChunks(component, gridUid, grid, seed); - - var active = _activeChunks[component]; - - foreach (var chunk in active) - { - LoadChunkMarkers(component, gridUid, grid, chunk, seed); - - if (!component.LoadedChunks.Add(chunk)) - continue; - - // Load NOW! - LoadChunk(component, gridUid, grid, chunk, seed); - } - } - - /// - /// Goes through all marker chunks that haven't been calculated, then calculates what spawns there are and - /// allocates them to the relevant actual chunks in the biome (marker chunks may be many times larger than biome chunks). - /// - private void BuildMarkerChunks(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, int seed) - { - var markers = _markerChunks[component]; - var loadedMarkers = component.LoadedMarkers; - var idx = 0; - - foreach (var (layer, chunks) in markers) - { - // I know dictionary ordering isn't guaranteed but I just need something to differentiate seeds. - idx++; - var localIdx = idx; - - Parallel.ForEach(chunks, new ParallelOptions() { MaxDegreeOfParallelism = _parallel.ParallelProcessCount }, chunk => - { - if (loadedMarkers.TryGetValue(layer, out var mobChunks) && mobChunks.Contains(chunk)) - return; - - var forced = component.ForcedMarkerLayers.Contains(layer); - - // Make a temporary version and copy back in later. - var pending = new Dictionary>>(); - - // Essentially get the seed + work out a buffer to adjacent chunks so we don't - // inadvertantly spawn too many near the edges. - var layerProto = ProtoManager.Index(layer); - var markerSeed = seed + chunk.X * ChunkSize + chunk.Y + localIdx; - var rand = new Random(markerSeed); - var buffer = (int)(layerProto.Radius / 2f); - var bounds = new Box2i(chunk + buffer, chunk + layerProto.Size - buffer); - var count = (int)(bounds.Area / (layerProto.Radius * layerProto.Radius)); - count = Math.Min(count, layerProto.MaxCount); - - GetMarkerNodes(gridUid, component, grid, layerProto, forced, bounds, count, rand, - out var spawnSet, out var existing); - - // Forcing markers to spawn so delete any that were found to be in the way. - if (forced && existing.Count > 0) - { - // Lock something so we can delete these safely. - lock (component.PendingMarkers) - { - foreach (var ent in existing) - { - Del(ent); - } - } - } - - foreach (var node in spawnSet.Keys) - { - var chunkOrigin = SharedMapSystem.GetChunkIndices(node, ChunkSize) * ChunkSize; - - if (!pending.TryGetValue(chunkOrigin, out var pendingMarkers)) - { - pendingMarkers = new Dictionary>(); - pending[chunkOrigin] = pendingMarkers; - } - - if (!pendingMarkers.TryGetValue(layer, out var layerMarkers)) - { - layerMarkers = new List(); - pendingMarkers[layer] = layerMarkers; - } - - layerMarkers.Add(node); - } - - lock (loadedMarkers) - { - if (!loadedMarkers.TryGetValue(layer, out var lockMobChunks)) - { - lockMobChunks = new HashSet(); - loadedMarkers[layer] = lockMobChunks; - } - - lockMobChunks.Add(chunk); - - foreach (var (chunkOrigin, layers) in pending) - { - if (!component.PendingMarkers.TryGetValue(chunkOrigin, out var lockMarkers)) - { - lockMarkers = new Dictionary>(); - component.PendingMarkers[chunkOrigin] = lockMarkers; - } - - foreach (var (lockLayer, nodes) in layers) - { - lockMarkers[lockLayer] = nodes; - } - } - } - }); - } - - component.ForcedMarkerLayers.Clear(); - } - - /// - /// Gets the marker nodes for the specified area. - /// - /// Should we include empty tiles when determine markers (e.g. if they are yet to be loaded) - public void GetMarkerNodes( - EntityUid gridUid, - BiomeComponent biome, - MapGridComponent grid, - BiomeMarkerLayerPrototype layerProto, - bool forced, - Box2i bounds, - int count, - Random rand, - out Dictionary spawnSet, - out HashSet existingEnts, - bool emptyTiles = true) - { - DebugTools.Assert(count > 0); - var remainingTiles = _tilePool.Get(); - var nodeEntities = new Dictionary(); - var nodeMask = new Dictionary(); - - // Okay so originally we picked a random tile and BFS outwards - // the problem is if you somehow get a cooked frontier then it might drop entire veins - // hence we'll grab all valid tiles up front and use that as possible seeds. - // It's hella more expensive but stops issues. - for (var x = bounds.Left; x < bounds.Right; x++) - { - for (var y = bounds.Bottom; y < bounds.Top; y++) - { - var node = new Vector2i(x, y); - - // Empty tile, skip if relevant. - if (!emptyTiles && (!_mapSystem.TryGetTile(grid, node, out var tile) || tile.IsEmpty)) - continue; - - // Check if it's a valid spawn, if so then use it. - var enumerator = _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, node); - enumerator.MoveNext(out var existing); - - if (!forced && existing != null) - continue; - - // Check if mask matches // anything blocking. - TryGetEntity(node, biome, (gridUid, grid), out var proto); - - // If there's an existing entity and it doesn't match the mask then skip. - if (layerProto.EntityMask.Count > 0 && - (proto == null || - !layerProto.EntityMask.ContainsKey(proto))) - { - continue; - } - - // If it's just a flat spawn then just check for anything blocking. - if (proto != null && layerProto.Prototype != null) - { - continue; - } - - DebugTools.Assert(layerProto.EntityMask.Count == 0 || !string.IsNullOrEmpty(proto)); - remainingTiles.Add(node); - nodeEntities.Add(node, existing); - nodeMask.Add(node, proto); - } - } - - var frontier = new ValueList(32); - // TODO: Need poisson but crashes whenever I use moony's due to inputs or smth idk - // Get the total amount of groups to spawn across the entire chunk. - // We treat a null entity mask as requiring nothing else on the tile - - spawnSet = new Dictionary(); - existingEnts = new HashSet(); - - // Iterate the group counts and pathfind out each group. - for (var i = 0; i < count; i++) - { - var groupSize = rand.Next(layerProto.MinGroupSize, layerProto.MaxGroupSize + 1); - - // While we have remaining tiles keep iterating - while (groupSize > 0 && remainingTiles.Count > 0) - { - var startNode = rand.PickAndTake(remainingTiles); - frontier.Clear(); - frontier.Add(startNode); - - // This essentially may lead to a vein being split in multiple areas but the count matters more than position. - while (frontier.Count > 0 && groupSize > 0) - { - // Need to pick a random index so we don't just get straight lines of ores. - var frontierIndex = rand.Next(frontier.Count); - var node = frontier[frontierIndex]; - frontier.RemoveSwap(frontierIndex); - remainingTiles.Remove(node); - - // Add neighbors if they're valid, worst case we add no more and pick another random seed tile. - for (var x = -1; x <= 1; x++) - { - for (var y = -1; y <= 1; y++) - { - var neighbor = new Vector2i(node.X + x, node.Y + y); - - if (frontier.Contains(neighbor) || !remainingTiles.Contains(neighbor)) - continue; - - frontier.Add(neighbor); - } - } - - // Tile valid salad so add it. - var mask = nodeMask[node]; - spawnSet.Add(node, mask); - groupSize--; - - if (nodeEntities.TryGetValue(node, out var existing)) - { - Del(existing); - } - } - } - - if (groupSize > 0) - { - Log.Warning($"Found remaining group size for ore veins!"); - } - } - - _tilePool.Return(remainingTiles); - } - - /// - /// Loads the pre-deteremined marker nodes for a particular chunk. - /// This is calculated in - /// - /// - /// Note that the marker chunks do not correspond to this chunk. - /// - private void LoadChunkMarkers( - BiomeComponent component, - EntityUid gridUid, - MapGridComponent grid, - Vector2i chunk, - int seed) - { - // Load any pending marker tiles first. - if (!component.PendingMarkers.TryGetValue(chunk, out var layers)) - return; - - // This needs to be done separately in case we try to add a marker layer and want to force it on existing - // loaded chunks. - component.ModifiedTiles.TryGetValue(chunk, out var modified); - modified ??= _tilePool.Get(); - - foreach (var (layer, nodes) in layers) - { - var layerProto = ProtoManager.Index(layer); - - foreach (var node in nodes) - { - if (modified.Contains(node)) - continue; - - // Need to ensure the tile under it has loaded for anchoring. - if (TryGetBiomeTile(node, component.Layers, seed, (gridUid, grid), out var tile)) - { - _mapSystem.SetTile(gridUid, grid, node, tile.Value); - } - - string? prototype; - - if (TryGetEntity(node, component, (gridUid, grid), out var proto) && - layerProto.EntityMask.TryGetValue(proto, out var maskedProto)) - { - prototype = maskedProto; - } - else - { - prototype = layerProto.Prototype; - } - - // If it is a ghost role then purge it - // TODO: This is *kind* of a bandaid but natural mobs spawns needs a lot more work. - // Ideally we'd just have ghost role and non-ghost role variants for some stuff. - var uid = EntityManager.CreateEntityUninitialized(prototype, _mapSystem.GridTileToLocal(gridUid, grid, node)); - RemComp(uid); - RemComp(uid); - EntityManager.InitializeAndStartEntity(uid); - modified.Add(node); - } - } - - if (modified.Count == 0) - { - component.ModifiedTiles.Remove(chunk); - _tilePool.Return(modified); - } - - component.PendingMarkers.Remove(chunk); - } - - /// - /// Loads a particular queued chunk for a biome. - /// - private void LoadChunk( - BiomeComponent component, - EntityUid gridUid, - MapGridComponent grid, - Vector2i chunk, - int seed) - { - component.ModifiedTiles.TryGetValue(chunk, out var modified); - modified ??= _tilePool.Get(); - _tiles.Clear(); - - // Set tiles first - for (var x = 0; x < ChunkSize; x++) - { - for (var y = 0; y < ChunkSize; y++) - { - var indices = new Vector2i(x + chunk.X, y + chunk.Y); - - // Pass in null so we don't try to get the tileref. - if (modified.Contains(indices)) - continue; - - // If there's existing data then don't overwrite it. - if (_mapSystem.TryGetTileRef(gridUid, grid, indices, out var tileRef) && !tileRef.Tile.IsEmpty) - continue; - - if (!TryGetBiomeTile(indices, component.Layers, seed, (gridUid, grid), out var biomeTile)) - continue; - - _tiles.Add((indices, biomeTile.Value)); - } - } - - _mapSystem.SetTiles(gridUid, grid, _tiles); - _tiles.Clear(); - - // Now do entities - var loadedEntities = new Dictionary(); - component.LoadedEntities.Add(chunk, loadedEntities); - - for (var x = 0; x < ChunkSize; x++) - { - for (var y = 0; y < ChunkSize; y++) - { - var indices = new Vector2i(x + chunk.X, y + chunk.Y); - - if (modified.Contains(indices)) - continue; - - // Don't mess with anything that's potentially anchored. - var anchored = _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, indices); - - if (anchored.MoveNext(out _) || !TryGetEntity(indices, component, (gridUid, grid), out var entPrototype)) - continue; - - // TODO: Fix non-anchored ents spawning. - // Just track loaded chunks for now. - var ent = Spawn(entPrototype, _mapSystem.GridTileToLocal(gridUid, grid, indices)); - - // At least for now unless we do lookups or smth, only work with anchoring. - if (_xformQuery.TryGetComponent(ent, out var xform) && !xform.Anchored) - { - _transform.AnchorEntity((ent, xform), (gridUid, grid), indices); - } - - loadedEntities.Add(ent, indices); - } - } - - // Decals - var loadedDecals = new Dictionary(); - component.LoadedDecals.Add(chunk, loadedDecals); - - for (var x = 0; x < ChunkSize; x++) - { - for (var y = 0; y < ChunkSize; y++) - { - var indices = new Vector2i(x + chunk.X, y + chunk.Y); - - if (modified.Contains(indices)) - continue; - - // Don't mess with anything that's potentially anchored. - var anchored = _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, indices); - - if (anchored.MoveNext(out _) || !TryGetDecals(indices, component.Layers, seed, (gridUid, grid), out var decals)) - continue; - - foreach (var decal in decals) - { - if (!_decals.TryAddDecal(decal.ID, new EntityCoordinates(gridUid, decal.Position), out var dec)) - continue; - - loadedDecals.Add(dec, indices); - } - } - } - - if (modified.Count == 0) - { - _tilePool.Return(modified); - component.ModifiedTiles.Remove(chunk); - } - else - { - component.ModifiedTiles[chunk] = modified; - } - } - - #endregion - - #region Unload - - /// - /// Handles all of the queued chunk unloads for a particular biome. - /// - private void UnloadChunks(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, int seed) - { - var active = _activeChunks[component]; - List<(Vector2i, Tile)>? tiles = null; - - foreach (var chunk in component.LoadedChunks) - { - if (active.Contains(chunk) || !component.LoadedChunks.Remove(chunk)) - continue; - - // Unload NOW! - tiles ??= new List<(Vector2i, Tile)>(ChunkSize * ChunkSize); - UnloadChunk(component, gridUid, grid, chunk, seed, tiles); - } - } - - /// - /// Unloads a specific biome chunk. - /// - private void UnloadChunk(BiomeComponent component, EntityUid gridUid, MapGridComponent grid, Vector2i chunk, int seed, List<(Vector2i, Tile)> tiles) - { - // Reverse order to loading - component.ModifiedTiles.TryGetValue(chunk, out var modified); - modified ??= new HashSet(); - - // Delete decals - foreach (var (dec, indices) in component.LoadedDecals[chunk]) - { - // If we couldn't remove it then flag the tile to never be touched. - if (!_decals.RemoveDecal(gridUid, dec)) - { - modified.Add(indices); - } - } - - component.LoadedDecals.Remove(chunk); - - // Delete entities - // Ideally any entities that aren't modified just get deleted and re-generated later - // This is because if we want to save the map (e.g. persistent server) it makes the file much smaller - // and also if the map is enormous will make stuff like physics broadphase much faster - var xformQuery = GetEntityQuery(); - - foreach (var (ent, tile) in component.LoadedEntities[chunk]) - { - if (Deleted(ent) || !xformQuery.TryGetComponent(ent, out var xform)) - { - modified.Add(tile); - continue; - } - - // It's moved - var entTile = _mapSystem.LocalToTile(gridUid, grid, xform.Coordinates); - - if (!xform.Anchored || entTile != tile) - { - modified.Add(tile); - continue; - } - - if (!EntityManager.IsDefault(ent)) - { - modified.Add(tile); - continue; - } - - Del(ent); - } - - component.LoadedEntities.Remove(chunk); - - // Unset tiles (if the data is custom) - - for (var x = 0; x < ChunkSize; x++) - { - for (var y = 0; y < ChunkSize; y++) - { - var indices = new Vector2i(x + chunk.X, y + chunk.Y); - - if (modified.Contains(indices)) - continue; - - // Don't mess with anything that's potentially anchored. - var anchored = _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, indices); - - if (anchored.MoveNext(out _)) - { - modified.Add(indices); - continue; - } - - // If it's default data unload the tile. - if (!TryGetBiomeTile(indices, component.Layers, seed, null, out var biomeTile) || - _mapSystem.TryGetTileRef(gridUid, grid, indices, out var tileRef) && tileRef.Tile != biomeTile.Value) - { - modified.Add(indices); - continue; - } - - tiles.Add((indices, Tile.Empty)); - } - } - - _mapSystem.SetTiles(gridUid, grid, tiles); - tiles.Clear(); - component.LoadedChunks.Remove(chunk); - - if (modified.Count == 0) - { - component.ModifiedTiles.Remove(chunk); - } - else - { - component.ModifiedTiles[chunk] = modified; - } - } - - #endregion - - /// - /// Creates a simple planet setup for a map. - /// - public void EnsurePlanet(EntityUid mapUid, BiomeTemplatePrototype biomeTemplate, int? seed = null, MetaDataComponent? metadata = null, Color? mapLight = null) - { - if (!Resolve(mapUid, ref metadata)) - return; - - EnsureComp(mapUid); - var biome = EntityManager.ComponentFactory.GetComponent(); - seed ??= _random.Next(); - SetSeed(mapUid, biome, seed.Value, false); - SetTemplate(mapUid, biome, biomeTemplate, false); - AddComp(mapUid, biome, true); - Dirty(mapUid, biome, metadata); - - var gravity = EnsureComp(mapUid); - gravity.Enabled = true; - gravity.Inherent = true; - Dirty(mapUid, gravity, metadata); - - // Day lighting - // Daylight: #D8B059 - // Midday: #E6CB8B - // Moonlight: #2b3143 - // Lava: #A34931 - var light = EnsureComp(mapUid); - light.AmbientLightColor = mapLight ?? Color.FromHex("#D8B059"); - Dirty(mapUid, light, metadata); - - EnsureComp(mapUid); - - EnsureComp(mapUid); - - EnsureComp(mapUid); - EnsureComp(mapUid); - - var moles = new float[Atmospherics.AdjustedNumberOfGases]; - moles[(int)Gas.Oxygen] = 21.824779f; - moles[(int)Gas.Nitrogen] = 82.10312f; - - var mixture = new GasMixture(moles, Atmospherics.T20C); - - _atmos.SetMapAtmosphere(mapUid, false, mixture); - } - - /// - /// Sets the specified tiles as relevant and marks them as modified. - /// - public void ReserveTiles(EntityUid mapUid, Box2 bounds, List<(Vector2i Index, Tile Tile)> tiles, BiomeComponent? biome = null, MapGridComponent? mapGrid = null) - { - if (!Resolve(mapUid, ref biome, ref mapGrid, false)) - return; - - foreach (var tileSet in _mapSystem.GetLocalTilesIntersecting(mapUid, mapGrid, bounds, false)) - { - Vector2i chunkOrigin; - HashSet modified; - - // Existing, ignore - if (_mapSystem.TryGetTileRef(mapUid, mapGrid, tileSet.GridIndices, out var existingRef) && !existingRef.Tile.IsEmpty) - { - chunkOrigin = SharedMapSystem.GetChunkIndices(tileSet.GridIndices, ChunkSize) * ChunkSize; - modified = biome.ModifiedTiles.GetOrNew(chunkOrigin); - modified.Add(tileSet.GridIndices); - continue; - } - - if (!TryGetBiomeTile(tileSet.GridIndices, biome.Layers, biome.Seed, (mapUid, mapGrid), out var tile)) - { - continue; - } - - chunkOrigin = SharedMapSystem.GetChunkIndices(tileSet.GridIndices, ChunkSize) * ChunkSize; - modified = biome.ModifiedTiles.GetOrNew(chunkOrigin); - modified.Add(tileSet.GridIndices); - tiles.Add((tileSet.GridIndices, tile.Value)); - } - - _mapSystem.SetTiles(mapUid, mapGrid, tiles); - } -} diff --git a/Content.Server/Procedural/BiomeSystem.Commands.cs b/Content.Server/Procedural/BiomeSystem.Commands.cs new file mode 100644 index 0000000000..ef90310483 --- /dev/null +++ b/Content.Server/Procedural/BiomeSystem.Commands.cs @@ -0,0 +1,105 @@ +using Content.Server.Administration; +using Content.Shared.Administration; +using Content.Shared.Procedural; +using Content.Shared.Procedural.Components; +using Robust.Shared.Console; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; + +namespace Content.Server.Procedural; + +public sealed partial class BiomeSystem +{ + [AdminCommand(AdminFlags.Mapping)] + public sealed class BiomeAddLayerCommand : LocalizedEntityCommands + { + [Dependency] private readonly IPrototypeManager _protoManager = default!; + + public override string Command => "biome_addlayer"; + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 3) + { + shell.WriteError(Help); + return; + } + + if (!int.TryParse(args[0], out var entInt)) + { + return; + } + + var entId = new EntityUid(entInt); + + if (!EntityManager.TryGetComponent(entId, out BiomeComponent? biome)) + { + return; + } + + if (!_protoManager.TryIndex(args[2], out DungeonConfigPrototype? config)) + { + return; + } + + var system = EntityManager.System(); + system.AddLayer((entId, biome), args[1], config); + } + + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + switch (args.Length) + { + case 1: + return CompletionResult.FromOptions(CompletionHelper.Components(args[0])); + case 2: + return CompletionResult.FromHint("layerId"); + case 3: + return CompletionResult.FromOptions(CompletionHelper.PrototypeIDs()); + } + + return CompletionResult.Empty; + } + } + + [AdminCommand(AdminFlags.Mapping)] + public sealed class BiomeRemoveLayerCommand : LocalizedEntityCommands + { + public override string Command => "biome_removelayer"; + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 2) + { + shell.WriteError(Help); + return; + } + + if (!int.TryParse(args[0], out var entInt)) + { + return; + } + + var entId = new EntityUid(entInt); + + if (!EntityManager.TryGetComponent(entId, out BiomeComponent? biome)) + { + return; + } + + var system = EntityManager.System(); + system.RemoveLayer((entId, biome), args[1]); + } + + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + switch (args.Length) + { + case 0: + return CompletionResult.FromOptions(CompletionHelper.Components(args[0])); + case 1: + return CompletionResult.FromHint("layerId"); + } + + return CompletionResult.Empty; + } + } +} diff --git a/Content.Server/Procedural/BiomeSystem.Planet.cs b/Content.Server/Procedural/BiomeSystem.Planet.cs new file mode 100644 index 0000000000..c5138ca04e --- /dev/null +++ b/Content.Server/Procedural/BiomeSystem.Planet.cs @@ -0,0 +1,65 @@ +using Content.Shared.Atmos; +using Content.Shared.Gravity; +using Content.Shared.Light.Components; +using Content.Shared.Procedural.Components; +using Robust.Shared.Map.Components; +using Robust.Shared.Prototypes; + +namespace Content.Server.Procedural; + +public sealed partial class BiomeSystem +{ + /// + /// Copies the biomecomponent to the specified map. + /// + public BiomeComponent? AddBiome(Entity mapUid, EntProtoId biomeTemplate, int? seed = null) + { + if (!_protomanager.Index(biomeTemplate).Components.TryGetComponent(Factory.GetComponentName(), out var template)) + { + return null; + } + + var biome = Factory.GetComponent(); + var biomeObj = (object)biome; + _serManager.CopyTo(template, ref biomeObj, notNullableOverride: true); + seed ??= _random.Next(); + biome.Seed = seed.Value; + AddComp(mapUid, biome, true); + return biome; + } + + /// + /// Creates a simple planet setup for a map. + /// + public void EnsurePlanet(EntityUid mapUid, EntProtoId biomeTemplate, int? seed = null, MetaDataComponent? metadata = null, Color? mapLight = null) + { + if (!Resolve(mapUid, ref metadata)) + return; + + EnsureComp(mapUid); + AddBiome(mapUid, biomeTemplate, seed); + var gravity = EnsureComp(mapUid); + gravity.Enabled = true; + gravity.Inherent = true; + Dirty(mapUid, gravity, metadata); + + var light = EnsureComp(mapUid); + light.AmbientLightColor = mapLight ?? Color.FromHex("#D8B059"); + Dirty(mapUid, light, metadata); + + EnsureComp(mapUid); + + EnsureComp(mapUid); + + EnsureComp(mapUid); + EnsureComp(mapUid); + + var moles = new float[Atmospherics.AdjustedNumberOfGases]; + moles[(int)Gas.Oxygen] = 21.824779f; + moles[(int)Gas.Nitrogen] = 82.10312f; + + var mixture = new GasMixture(moles, Atmospherics.T20C); + + _atmos.SetMapAtmosphere(mapUid, false, mixture); + } +} diff --git a/Content.Server/Procedural/BiomeSystem.cs b/Content.Server/Procedural/BiomeSystem.cs new file mode 100644 index 0000000000..6d584ad3b2 --- /dev/null +++ b/Content.Server/Procedural/BiomeSystem.cs @@ -0,0 +1,661 @@ +using System.Numerics; +using System.Threading; +using System.Threading.Tasks; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Decals; +using Content.Server.Shuttles.Events; +using Content.Shared.CCVar; +using Content.Shared.Decals; +using Content.Shared.Ghost; +using Content.Shared.Procedural; +using Content.Shared.Procedural.Components; +using Content.Shared.Procedural.DungeonGenerators; +using Content.Shared.Sprite; +using Content.Shared.Tag; +using Robust.Server.Player; +using Robust.Shared.Configuration; +using Robust.Shared.CPUJob.JobQueues; +using Robust.Shared.CPUJob.JobQueues.Queues; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Map.Enumerators; +using Robust.Shared.Physics.Components; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Timing; +using Robust.Shared.Utility; + +namespace Content.Server.Procedural; + +public sealed partial class BiomeSystem : EntitySystem +{ + /* + * Handles loading in biomes around players. + * These are essentially chunked-areas that load in dungeons and can also be unloaded. + */ + + [Dependency] private readonly IConfigurationManager _cfgManager = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _protomanager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly ISerializationManager _serManager = default!; + [Dependency] private readonly AtmosphereSystem _atmos = default!; + [Dependency] private readonly SharedMapSystem _maps = default!; + [Dependency] private readonly TagSystem _tags = default!; + [Dependency] private readonly SharedTransformSystem _xforms = default!; + + /// + /// Ignored components for default checks + /// + public static readonly List IgnoredComponents = new(); + + /// + /// Jobs for biomes to load. + /// + private JobQueue _biomeQueue = default!; + + private float _loadRange = 1f; + private float _loadTime; + + private EntityQuery _ghostQuery; + private EntityQuery _biomeQuery; + + private static readonly ProtoId AllowBiomeLoadingTag = "AllowBiomeLoading"; + + public override void Initialize() + { + base.Initialize(); + _ghostQuery = GetEntityQuery(); + _biomeQuery = GetEntityQuery(); + + IgnoredComponents.Add(Factory.GetComponentName()); + + Subs.CVar(_cfgManager, CCVars.BiomeLoadRange, OnLoadRange, true); + Subs.CVar(_cfgManager, CCVars.BiomeLoadTime, OnLoadTime, true); + + SubscribeLocalEvent(OnFTLStarted); + } + + private void OnLoadTime(float obj) + { + _biomeQueue = new JobQueue(obj); + _loadTime = obj; + } + + private void OnLoadRange(float obj) + { + _loadRange = obj; + } + + private void OnFTLStarted(ref FTLStartedEvent ev) + { + var targetMap = _xforms.ToMapCoordinates(ev.TargetCoordinates); + var targetMapUid = _maps.GetMapOrInvalid(targetMap.MapId); + + if (!TryComp(targetMapUid, out var biome)) + return; + + var preloadArea = new Vector2(32f, 32f); + var targetArea = new Box2(targetMap.Position - preloadArea, targetMap.Position + preloadArea); + Preload(targetMapUid, biome, (Box2i) targetArea); + } + + /// + /// Preloads biome for the specified area. + /// + public void Preload(EntityUid uid, BiomeComponent component, Box2i area) + { + component.PreloadAreas.Add(area); + } + + private bool CanLoad(EntityUid uid) + { + return !_ghostQuery.HasComp(uid) || _tags.HasTag(uid, AllowBiomeLoadingTag); + } + + public bool RemoveLayer(Entity biome, string label) + { + if (!Resolve(biome.Owner, ref biome.Comp)) + return false; + + if (!biome.Comp.Layers.ContainsKey(label)) + { + return false; + } + + // Technically this can race-condition with adds but uhh tell people to not do that. + biome.Comp.PendingRemovals.Add(label); + return true; + } + + public void AddLayer(Entity biome, string label, BiomeMetaLayer layer) + { + if (!Resolve(biome.Owner, ref biome.Comp)) + return; + + if (!biome.Comp.Layers.TryAdd(label, layer)) + { + Log.Warning($"Tried to add layer {label} to biome {ToPrettyString(biome)} that already has it?"); + return; + } + } + + public void AddLayer(Entity biome, string label, ProtoId layer) + { + if (!Resolve(biome.Owner, ref biome.Comp)) + return; + + var metaLayer = new BiomeMetaLayer() + { + Dungeon = layer, + }; + + AddLayer(biome, label, metaLayer); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = AllEntityQuery(); + + while (query.MoveNext(out var biome)) + { + // If it's still loading then don't touch the observer bounds. + if (biome.Loading) + continue; + + biome.LoadedBounds.Clear(); + + // Make sure preloads go in. + foreach (var preload in biome.PreloadAreas) + { + biome.LoadedBounds.Add(preload); + } + } + + // Get all relevant players. + foreach (var player in _player.Sessions) + { + if (player.AttachedEntity != null) + { + TryAddBiomeBounds(player.AttachedEntity.Value); + } + + foreach (var viewer in player.ViewSubscriptions) + { + TryAddBiomeBounds(viewer); + } + } + + // Unload first in case we can't catch up. + UnloadChunks(); + + var loadQuery = AllEntityQuery(); + + // Check if any biomes are intersected and queue up loads. + while (loadQuery.MoveNext(out var uid, out var biome, out var grid)) + { + if (biome.Loading || biome.LoadedBounds.Count == 0) + continue; + + biome.Loading = true; + var job = new BiomeLoadJob(_loadTime) + { + Grid = (uid, biome, grid), + }; + _biomeQueue.EnqueueJob(job); + } + + // Process jobs. + _biomeQueue.Process(); + } + + private void UnloadChunks() + { + var query = AllEntityQuery(); + + while (query.MoveNext(out var uid, out var biome, out var grid)) + { + // Only start unloading if it's currently not loading anything. + if (biome.Loading) + continue; + + var toUnload = new Dictionary>(); + + foreach (var (layerId, loadedLayer) in biome.LoadedData) + { + var layer = biome.Layers[layerId]; + + // If it can't unload then ignore it. + if (!layer.CanUnload) + continue; + + var size = GetSize(_protomanager.Index(layer.Dungeon), layer); + + if (size == null) + continue; + + // Go through each loaded chunk and check if they can be unloaded by checking if any players are in range. + foreach (var chunk in loadedLayer.Keys) + { + var chunkBounds = new Box2i(chunk, chunk + size.Value); + var canUnload = true; + + foreach (var playerView in biome.LoadedBounds) + { + // Give a buffer range so we don't immediately unload if we wiggle, we'll just double the load area. + var enlarged = playerView.Enlarged((int) _loadRange); + + // Still relevant + if (chunkBounds.Intersects(enlarged)) + { + canUnload = false; + break; + } + } + + if (!canUnload) + continue; + + toUnload.GetOrNew(layerId).Add(chunk); + } + } + + if (biome.PendingRemovals.Count > 0) + { + foreach (var label in biome.PendingRemovals) + { + var bounds = toUnload.GetOrNew(label); + + foreach (var chunkOrigin in biome.LoadedData[label].Keys) + { + bounds.Add(chunkOrigin); + } + } + } + + if (toUnload.Count == 0) + continue; + + // Queue up unloads. + biome.Loading = true; + var job = new BiomeUnloadJob(_loadTime) + { + Biome = (uid, grid, biome), + ToUnload = toUnload, + }; + _biomeQueue.EnqueueJob(job); + } + } + + /// + /// Gets the full bounds to be loaded. Considers layer dependencies where they may have different chunk sizes. + /// + private Box2i GetFullBounds(BiomeComponent component, Box2i bounds) + { + var result = bounds; + + foreach (var layer in component.Layers.Values) + { + var layerBounds = GetLayerBounds(layer, result); + + if (layer.DependsOn != null) + { + foreach (var sub in layer.DependsOn) + { + var depLayer = component.Layers[sub]; + + layerBounds = layerBounds.Union(GetLayerBounds(depLayer, layerBounds)); + } + } + + result = result.Union(layerBounds); + } + + return result; + } + + /// + /// Tries to add the viewer bounds of this entity for loading. + /// + private void TryAddBiomeBounds(EntityUid uid) + { + if (!CanLoad(uid)) + return; + + var xform = Transform(uid); + + // No biome to load + if (!_biomeQuery.TryComp(xform.MapUid, out var biome)) + return; + + // Currently already loading. + if (biome.Loading) + return; + + var center = _xforms.GetWorldPosition(uid); + + var bounds = new Box2i((center - new Vector2(_loadRange, _loadRange)).Floored(), (center + new Vector2(_loadRange, _loadRange)).Floored()); + + // If it's moving then preload in that direction + if (TryComp(uid, out PhysicsComponent? physics)) + { + bounds = bounds.Union(bounds.Translated((physics.LinearVelocity * 2f).Floored())); + } + + var adjustedBounds = GetFullBounds(biome, bounds); + biome.LoadedBounds.Add(adjustedBounds); + } + + public int? GetSize(DungeonConfigPrototype config, BiomeMetaLayer layer) + { + var size = layer.Size; + + if (size == null && config.Layers[0] is ChunkDunGen chunkGen) + { + size = chunkGen.Size; + } + // No size + else + { + Log.Warning($"Unable to infer chunk size for biome {layer} / config {config.ID}"); + return null; + } + + return size.Value; + } + + public Box2i GetLayerBounds(BiomeMetaLayer layer, Box2i layerBounds) + { + var size = GetSize(_protomanager.Index(layer.Dungeon), layer); + + if (size == null) + return Box2i.Empty; + + var chunkSize = new Vector2(size.Value, size.Value); + + // Need to round the bounds to our chunk size to ensure we load whole chunks. + // We also need to know the minimum bounds for our dependencies to load. + var layerBL = (layerBounds.BottomLeft / chunkSize).Floored() * chunkSize; + var layerTR = (layerBounds.TopRight / chunkSize).Ceiled() * chunkSize; + + var loadBounds = new Box2i(layerBL.Floored(), layerTR.Ceiled()); + return loadBounds; + } +} + + public sealed class BiomeLoadJob : Job + { + [Dependency] private IEntityManager _entManager = default!; + [Dependency] private IPrototypeManager _protoManager = default!; + + private BiomeSystem System = default!; + private DungeonSystem DungeonSystem = default!; + + public Entity Grid; + + internal ISawmill _sawmill = default!; + + public BiomeLoadJob(double maxTime, CancellationToken cancellation = default) : base(maxTime, cancellation) + { + IoCManager.InjectDependencies(this); + System = _entManager.System(); + DungeonSystem = _entManager.System(); + } + + protected override async Task Process() + { + try + { + foreach (var bound in Grid.Comp1.LoadedBounds) + { + foreach (var (layerId, layer) in Grid.Comp1.Layers) + { + await LoadLayer(layerId, layer, bound); + } + } + } + finally + { + // Finished + DebugTools.Assert(Grid.Comp1.Loading); + Grid.Comp1.Loading = false; + } + + // If we have any preloads then mark those as modified so they persist. + foreach (var preload in Grid.Comp1.PreloadAreas) + { + for (var x = preload.Left; x <= preload.Right; x++) + { + for (var y = preload.Bottom; y <= preload.Top; y++) + { + var index = new Vector2i(x, y); + Grid.Comp1.ModifiedTiles.Add(index); + } + } + + await SuspendIfOutOfTime(); + } + + Grid.Comp1.PreloadAreas.Clear(); + + return true; + } + + private async Task LoadLayer(string layerId, BiomeMetaLayer layer, Box2i parentBounds) + { + // Nothing to do + var dungeon = _protoManager.Index(layer.Dungeon); + + if (dungeon.Layers.Count == 0) + return; + + var loadBounds = System.GetLayerBounds(layer, parentBounds); + + // Make sure our dependencies are loaded first. + if (layer.DependsOn != null) + { + foreach (var sub in layer.DependsOn) + { + var actualLayer = Grid.Comp1.Layers[sub]; + + await LoadLayer(sub, actualLayer, loadBounds); + } + } + + var size = System.GetSize(dungeon, layer); + + if (size == null) + return; + + // The reason we do this is so if we dynamically add similar layers (e.g. we add 3 mob layers at runtime) + // they don't all have the same seeds. + var layerSeed = Grid.Comp1.Seed + layerId.GetHashCode(); + + // Okay all of our dependencies loaded so we can send it. + var chunkEnumerator = new NearestChunkEnumerator(loadBounds, size.Value); + + while (chunkEnumerator.MoveNext(out var chunk)) + { + var chunkOrigin = chunk.Value; + var layerLoaded = Grid.Comp1.LoadedData.GetOrNew(layerId); + + // Layer already loaded for this chunk. + // This can potentially happen if we're moving and the player's bounds changed but some existing chunks remain. + if (layerLoaded.ContainsKey(chunkOrigin)) + { + continue; + } + + // Load dungeon here async await and all that jaz. + var (_, data) = await WaitAsyncTask(DungeonSystem + .GenerateDungeonAsync(dungeon, Grid.Owner, Grid.Comp2, chunkOrigin, layerSeed, reservedTiles: Grid.Comp1.ModifiedTiles)); + + // If we can unload it then store the data to check for later. + if (layer.CanUnload) + { + layerLoaded.Add(chunkOrigin, data); + } + } + } +} + +public sealed class BiomeUnloadJob : Job +{ + [Dependency] private EntityManager _entManager = default!; + + public Entity Biome; + public Dictionary> ToUnload = default!; + + public BiomeUnloadJob(double maxTime, CancellationToken cancellation = default) : base(maxTime, cancellation) + { + IoCManager.InjectDependencies(this); + } + + public BiomeUnloadJob(double maxTime, IStopwatch stopwatch, CancellationToken cancellation = default) : base(maxTime, stopwatch, cancellation) + { + } + + protected override async Task Process() + { + try + { + var grid = Biome.Comp1; + var biome = Biome.Comp2; + DebugTools.Assert(biome.Loading); + var maps = _entManager.System(); + var decals = _entManager.System(); + var lookup = _entManager.System(); + _entManager.TryGetComponent(Biome.Owner, out DecalGridComponent? decalGrid); + var forceUnload = _entManager.GetEntityQuery(); + var entities = new HashSet(); + var tiles = new List<(Vector2i, Tile)>(); + + foreach (var (layer, chunkOrigins) in ToUnload) + { + if (!biome.Layers.TryGetValue(layer, out var meta)) + continue; + + if (!biome.LoadedData.TryGetValue(layer, out var data)) + continue; + + DebugTools.Assert(meta.CanUnload); + + foreach (var chunk in chunkOrigins) + { + // Not loaded anymore? + if (!data.Remove(chunk, out var loaded)) + continue; + + tiles.Clear(); + + foreach (var (ent, pos) in loaded.Entities) + { + // Already flagged as modified so go next. + if (biome.ModifiedTiles.Contains(pos)) + { + continue; + } + + // IsDefault is actually super expensive so really need to run this check in the loop. + await SuspendIfOutOfTime(); + + if (forceUnload.HasComp(ent)) + { + _entManager.DeleteEntity(ent); + continue; + } + + // Deleted so counts as modified. + if (!_entManager.TransformQuery.TryComp(ent, out var xform)) + { + biome.ModifiedTiles.Add(pos); + continue; + } + + // If it stayed still and had no data change then keep it. + if (pos == xform.LocalPosition.Floored() && xform.GridUid == Biome.Owner && _entManager.IsDefault(ent, BiomeSystem.IgnoredComponents)) + { + _entManager.DeleteEntity(ent); + continue; + } + + // Need the entity's current tile to be flagged for unloading. + if (Biome.Owner == xform.GridUid) + { + var entTile = maps.LocalToTile(Biome.Owner, grid, xform.Coordinates); + biome.ModifiedTiles.Add(entTile); + } + } + + foreach (var (decal, pos) in loaded.Decals) + { + // Modified go NEXT + if (biome.ModifiedTiles.Contains(pos.Floored())) + continue; + + // Should just be able to remove them as you can't actually edit a decal. + if (!decals.RemoveDecal(Biome.Owner, decal, decalGrid)) + { + biome.ModifiedTiles.Add(pos.Floored()); + } + } + + await SuspendIfOutOfTime(); + + foreach (var (index, tile) in loaded.Tiles) + { + await SuspendIfOutOfTime(); + + if (Biome.Comp2.ModifiedTiles.Contains(index)) + { + continue; + } + + if (!maps.TryGetTileRef(Biome.Owner, Biome.Comp1, index, out var tileRef) || + tileRef.Tile != tile) + { + Biome.Comp2.ModifiedTiles.Add(index); + continue; + } + + entities.Clear(); + var tileBounds = lookup.GetLocalBounds(index, Biome.Comp1.TileSize).Enlarged(-0.05f); + + lookup.GetEntitiesIntersecting(Biome.Owner, + tileBounds, + entities); + + // Still entities remaining so just leave the tile. + if (entities.Count > 0) + { + Biome.Comp2.ModifiedTiles.Add(index); + continue; + } + + if (decals.GetDecalsIntersecting(Biome.Owner, tileBounds, component: decalGrid).Count > 0) + { + Biome.Comp2.ModifiedTiles.Add(index); + continue; + } + + // Clear it + tiles.Add((index, Tile.Empty)); + } + + maps.SetTiles(Biome.Owner, Biome.Comp1, tiles); + } + } + } + finally + { + Biome.Comp2.Loading = false; + } + + Biome.Comp2.PendingRemovals.Clear(); + + return true; + } +} diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.AutoCabling.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.AutoCabling.cs index 8c49a7d606..83ff71f8b7 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.AutoCabling.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.AutoCabling.cs @@ -151,7 +151,8 @@ public sealed partial class DungeonJob if (found) continue; - _entManager.SpawnEntity(gen.Entity, _maps.GridTileToLocal(_gridUid, _grid, tile)); + var ent = _entManager.SpawnEntity(gen.Entity, _maps.GridTileToLocal(_gridUid, _grid, tile)); + AddLoadedEntity(tile, ent); } } } diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Biome.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Biome.cs deleted file mode 100644 index 48adb8af18..0000000000 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.Biome.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Threading.Tasks; -using Content.Server.Parallax; -using Content.Shared.Maps; -using Content.Shared.Parallax.Biomes; -using Content.Shared.Procedural; -using Content.Shared.Procedural.PostGeneration; -using Robust.Shared.Map; -using Robust.Shared.Utility; - -namespace Content.Server.Procedural.DungeonJob; - -public sealed partial class DungeonJob -{ - /// - /// - /// - private async Task PostGen(BiomeDunGen dunGen, Dungeon dungeon, HashSet reservedTiles, Random random) - { - if (!_prototype.TryIndex(dunGen.BiomeTemplate, out var indexedBiome)) - return; - - var biomeSystem = _entManager.System(); - - var seed = random.Next(); - var xformQuery = _entManager.GetEntityQuery(); - - var tiles = _maps.GetAllTilesEnumerator(_gridUid, _grid); - while (tiles.MoveNext(out var tileRef)) - { - var node = tileRef.Value.GridIndices; - - if (reservedTiles.Contains(node)) - continue; - - if (dunGen.TileMask is not null) - { - if (!dunGen.TileMask.Contains(((ContentTileDefinition)_tileDefManager[tileRef.Value.Tile.TypeId]).ID)) - continue; - } - - // Need to set per-tile to override data. - if (biomeSystem.TryGetTile(node, indexedBiome.Layers, seed, (_gridUid, _grid), out var tile)) - { - _maps.SetTile(_gridUid, _grid, node, tile.Value); - } - - if (biomeSystem.TryGetDecals(node, indexedBiome.Layers, seed, (_gridUid, _grid), out var decals)) - { - foreach (var decal in decals) - { - _decals.TryAddDecal(decal.ID, new EntityCoordinates(_gridUid, decal.Position), out _); - } - } - - if (biomeSystem.TryGetEntity(node, indexedBiome.Layers, tile ?? tileRef.Value.Tile, seed, (_gridUid, _grid), out var entityProto)) - { - var ent = _entManager.SpawnEntity(entityProto, new EntityCoordinates(_gridUid, node + _grid.TileSizeHalfVector)); - var xform = xformQuery.Get(ent); - - if (!xform.Comp.Anchored) - { - _transform.AnchorEntity(ent, xform); - } - - // TODO: Engine bug with SpawnAtPosition - DebugTools.Assert(xform.Comp.Anchored); - } - - await SuspendDungeon(); - if (!ValidateResume()) - return; - } - } -} diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.BiomeMarkerLayer.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.BiomeMarkerLayer.cs deleted file mode 100644 index abc74ddc4f..0000000000 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.BiomeMarkerLayer.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System.Threading.Tasks; -using Content.Server.Parallax; -using Content.Shared.Parallax.Biomes; -using Content.Shared.Parallax.Biomes.Markers; -using Content.Shared.Procedural; -using Content.Shared.Procedural.PostGeneration; -using Content.Shared.Random.Helpers; -using Robust.Shared.Map; -using Robust.Shared.Utility; - -namespace Content.Server.Procedural.DungeonJob; - -public sealed partial class DungeonJob -{ - /// - /// - /// - private async Task PostGen(BiomeMarkerLayerDunGen dunGen, Dungeon dungeon, HashSet reservedTiles, Random random) - { - // If we're adding biome then disable it and just use for markers. - if (_entManager.EnsureComponent(_gridUid, out BiomeComponent biomeComp)) - { - biomeComp.Enabled = false; - } - - var biomeSystem = _entManager.System(); - var weightedRandom = _prototype.Index(dunGen.MarkerTemplate); - var xformQuery = _entManager.GetEntityQuery(); - var templates = new Dictionary(); - - for (var i = 0; i < dunGen.Count; i++) - { - var template = weightedRandom.Pick(random); - var count = templates.GetOrNew(template); - count++; - templates[template] = count; - } - - foreach (var (template, count) in templates) - { - var markerTemplate = _prototype.Index(template); - - var bounds = new Box2i(); - - foreach (var tile in dungeon.RoomTiles) - { - bounds = bounds.UnionTile(tile); - } - - await SuspendDungeon(); - if (!ValidateResume()) - return; - - biomeSystem.GetMarkerNodes(_gridUid, biomeComp, _grid, markerTemplate, true, bounds, count, - random, out var spawnSet, out var existing, false); - - await SuspendDungeon(); - if (!ValidateResume()) - return; - - var checkTile = reservedTiles.Count > 0; - - foreach (var ent in existing) - { - if (checkTile && reservedTiles.Contains(_maps.LocalToTile(_gridUid, _grid, _xformQuery.GetComponent(ent).Coordinates))) - { - continue; - } - - _entManager.DeleteEntity(ent); - - await SuspendDungeon(); - if (!ValidateResume()) - return; - } - - foreach (var (node, mask) in spawnSet) - { - if (reservedTiles.Contains(node)) - continue; - - string? proto; - - if (mask != null && markerTemplate.EntityMask.TryGetValue(mask, out var maskedProto)) - { - proto = maskedProto; - } - else - { - proto = markerTemplate.Prototype; - } - - var ent = _entManager.SpawnAtPosition(proto, new EntityCoordinates(_gridUid, node + _grid.TileSizeHalfVector)); - var xform = xformQuery.Get(ent); - - if (!xform.Comp.Anchored) - _transform.AnchorEntity(ent, xform); - - await SuspendDungeon(); - if (!ValidateResume()) - return; - } - } - } -} diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.BoundaryWall.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.BoundaryWall.cs index 1c48a84cce..7bc7527ff5 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.BoundaryWall.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.BoundaryWall.cs @@ -32,7 +32,10 @@ public sealed partial class DungeonJob if (!_anchorable.TileFree((_gridUid, _grid), neighbor, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask)) continue; - tiles.Add((neighbor, _tile.GetVariantTile((ContentTileDefinition) tileDef, random))); + var tile = _tile.GetVariantTile((ContentTileDefinition)tileDef, random); + tiles.Add((neighbor, tile)); + AddLoadedTile(neighbor, tile); + DebugTools.Assert(dungeon.AllTiles.Contains(neighbor)); } foreach (var index in dungeon.CorridorExteriorTiles) @@ -43,7 +46,10 @@ public sealed partial class DungeonJob if (!_anchorable.TileFree((_gridUid, _grid), index, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask)) continue; - tiles.Add((index, _tile.GetVariantTile((ContentTileDefinition)tileDef, random))); + var tile = _tile.GetVariantTile((ContentTileDefinition)tileDef, random); + tiles.Add((index, tile)); + AddLoadedTile(index, tile); + DebugTools.Assert(dungeon.AllTiles.Contains(index)); } _maps.SetTiles(_gridUid, _grid, tiles); @@ -82,18 +88,21 @@ public sealed partial class DungeonJob } if (isCorner) - _entManager.SpawnEntity(cornerWall, _maps.GridTileToLocal(_gridUid, _grid, index.Index)); + { + var uid = _entManager.SpawnEntity(cornerWall, _maps.GridTileToLocal(_gridUid, _grid, index.Index)); + AddLoadedEntity(index.Index, uid); + } if (!isCorner) - _entManager.SpawnEntity(wall, _maps.GridTileToLocal(_gridUid, _grid, index.Index)); - - if (i % 20 == 0) { - await SuspendDungeon(); - - if (!ValidateResume()) - return; + var uid = _entManager.SpawnEntity(wall, _maps.GridTileToLocal(_gridUid, _grid, index.Index)); + AddLoadedEntity(index.Index, uid); } + + await SuspendDungeon(); + + if (!ValidateResume()) + return; } } } diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Chunk.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Chunk.cs new file mode 100644 index 0000000000..391fb9161b --- /dev/null +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.Chunk.cs @@ -0,0 +1,42 @@ +using System.Threading.Tasks; +using Content.Shared.Procedural; +using Content.Shared.Procedural.DungeonGenerators; +using Content.Shared.Procedural.PostGeneration; + +namespace Content.Server.Procedural.DungeonJob; + +public sealed partial class DungeonJob +{ + /// + /// + /// + private async Task PostGen(ChunkDunGen dunGen, HashSet reservedTiles, Random random) + { + var dungeon = new Dungeon(); + var tiles = new HashSet(); + var tr = _position + new Vector2i(dunGen.Size, dunGen.Size); + var oldSeed = dunGen.Noise?.GetSeed() ?? 0; + dunGen.Noise?.SetSeed(_seed + oldSeed); + + for (var x = 0; x < dunGen.Size; x++) + { + for (var y = 0; y < dunGen.Size; y++) + { + var index = new Vector2i(_position.X + x, _position.Y + y); + + if (reservedTiles.Contains(index)) + continue; + + if (dunGen.Noise?.GetNoise(x, y) < dunGen.Threshold) + continue; + + tiles.Add(index); + } + } + + dunGen.Noise?.SetSeed(oldSeed); + var room = new DungeonRoom(tiles, (tr - _position) / 2 + _position, new Box2i(_position, tr), new HashSet()); + dungeon.AddRoom(room); + return dungeon; + } +} diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Corridor.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Corridor.cs index bf9f910b94..8353d81f16 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.Corridor.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.Corridor.cs @@ -94,12 +94,14 @@ public sealed partial class DungeonJob var setTiles = new List<(Vector2i, Tile)>(); var tileDef = (ContentTileDefinition) _tileDefManager[gen.Tile]; - foreach (var tile in corridorTiles) + foreach (var node in corridorTiles) { - if (reservedTiles.Contains(tile)) + if (reservedTiles.Contains(node)) continue; - setTiles.Add((tile, _tile.GetVariantTile(tileDef, random))); + var tile = _tile.GetVariantTile(tileDef, random); + setTiles.Add((node, tile)); + AddLoadedTile(node, tile); } _maps.SetTiles(_gridUid, _grid, setTiles); diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.CorridorClutter.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.CorridorClutter.cs index e0be852733..e28c6798f2 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.CorridorClutter.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.CorridorClutter.cs @@ -48,7 +48,13 @@ public sealed partial class DungeonJob var protos = _entTable.GetSpawns(contents, random); var coords = _maps.ToCenterCoordinates(_gridUid, tile, _grid); - _entManager.SpawnEntitiesAttachedTo(coords, protos); + var uids = _entManager.SpawnEntitiesAttachedTo(coords, protos); + + foreach (var uid in uids) + { + AddLoadedEntity(tile, uid); + } + await SuspendIfOutOfTime(); if (!ValidateResume()) diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.CorridorDecalSkirting.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.CorridorDecalSkirting.cs index cd8737e6ec..48088a4718 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.CorridorDecalSkirting.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.CorridorDecalSkirting.cs @@ -83,7 +83,8 @@ public sealed partial class DungeonJob { // Decals not being centered biting my ass again var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile).Offset(offset); - _decals.TryAddDecal(cDir, gridPos, out _, color: decks.Color); + _decals.TryAddDecal(cDir, gridPos, out var did, color: decks.Color); + AddLoadedDecal(tile, did); } } @@ -96,7 +97,8 @@ public sealed partial class DungeonJob { // Decals not being centered biting my ass again var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile).Offset(offset); - _decals.TryAddDecal(cDir, gridPos, out _, color: decks.Color); + _decals.TryAddDecal(cDir, gridPos, out var did, color: decks.Color); + AddLoadedDecal(tile, did); } continue; @@ -111,7 +113,8 @@ public sealed partial class DungeonJob if (decks.CornerDecals.TryGetValue(dirFlag, out var cDir)) { var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile).Offset(offset); - _decals.TryAddDecal(cDir, gridPos, out _, color: decks.Color); + _decals.TryAddDecal(cDir, gridPos, out var did, color: decks.Color); + AddLoadedDecal(tile, did); } } } diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenNoiseDistance.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenNoiseDistance.cs index f1808ec90c..10481c5b5f 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenNoiseDistance.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenNoiseDistance.cs @@ -28,7 +28,7 @@ public sealed partial class DungeonJob Random random) { var tiles = new List<(Vector2i, Tile)>(); - var matrix = Matrix3Helpers.CreateTranslation(position); + var matrix = Matrix3Helpers.CreateTranslation(_position + position); foreach (var layer in dungen.Layers) { @@ -76,7 +76,9 @@ public sealed partial class DungeonJob break; } - tiles.Add((adjusted, new Tile(tileDef.TileId, variant: variant))); + var tile = new Tile(tileDef.TileId, variant: variant); + tiles.Add((adjusted, tile)); + AddLoadedTile(adjusted, tile); roomTiles.Add(adjusted); break; } @@ -101,6 +103,8 @@ public sealed partial class DungeonJob { switch (distance) { + case DunGenDistanceSquared: + return dx * dx + dy * dy; case DunGenEuclideanSquaredDistance: return MathF.Min(1f, (dx * dx + dy * dy) / MathF.Sqrt(2)); case DunGenSquareBump: diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenPrefab.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenPrefab.cs index 8eb85e2cb8..f348ce7301 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenPrefab.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenPrefab.cs @@ -196,7 +196,9 @@ public sealed partial class DungeonJob if (reservedTiles.Contains(index)) continue; - tiles.Add((index, new Tile(_tileDefManager[fallbackTile.Value].TileId))); + var tile = new Tile(_tileDefManager[fallbackTile.Value].TileId); + tiles.Add((index, tile)); + AddLoadedTile(index, tile); } } @@ -230,7 +232,14 @@ public sealed partial class DungeonJob var dungeonMatty = Matrix3x2.Multiply(matty, dungeonTransform); // The expensive bit yippy. - _dungeon.SpawnRoom(_gridUid, _grid, dungeonMatty, room, reservedTiles); + var data = _dungeon.SpawnRoom(_gridUid, _grid, dungeonMatty, room, reservedTiles); + + _data.Merge(data); + + await SuspendDungeon(); + + if (!ValidateResume()) + return dungeon; var roomCenter = (room.Offset + room.Size / 2f) * _grid.TileSize; var roomTiles = new HashSet(room.Size.X * room.Size.Y); diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenReplaceTile.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenReplaceTile.cs index dfc0932915..4c53141bc9 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenReplaceTile.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.DunGenReplaceTile.cs @@ -42,6 +42,7 @@ public sealed partial class DungeonJob } replacements.Add((node, tile)); + AddLoadedTile(node, tile); break; } diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.DungeonEntrance.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.DungeonEntrance.cs index dceeac3f12..35a35e9656 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.DungeonEntrance.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.DungeonEntrance.cs @@ -71,14 +71,17 @@ public sealed partial class DungeonJob isValid = true; // Entrance wew - _maps.SetTile(_gridUid, _grid, tile, _tile.GetVariantTile(tileDef, random)); + var tileVariant = _tile.GetVariantTile(tileDef, random); + _maps.SetTile(_gridUid, _grid, tile, tileVariant); + AddLoadedTile(tile, tileVariant); ClearDoor(dungeon, _grid, tile); var gridCoords = _maps.GridTileToLocal(_gridUid, _grid, tile); // Need to offset the spawn to avoid spawning in the room. foreach (var ent in _entTable.GetSpawns(contents, random)) { - _entManager.SpawnAtPosition(ent, gridCoords); + var uid = _entManager.SpawnAtPosition(ent, gridCoords); + AddLoadedEntity(tile, uid); } // Clear out any biome tiles nearby to avoid blocking it diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.EntityTableDunGen.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.EntityTableDunGen.cs index 6483448240..02686f7a26 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.EntityTableDunGen.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.EntityTableDunGen.cs @@ -51,6 +51,7 @@ public sealed partial class DungeonJob foreach (var ent in entities) { var uid = _entManager.SpawnAtPosition(ent, _maps.GridTileToLocal(_gridUid, _grid, tile)); + AddLoadedEntity(tile, uid); _entManager.RemoveComponent(uid); _entManager.RemoveComponent(uid); npcs.SleepNPC(uid); diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.EntranceFlank.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.EntranceFlank.cs index 1788c23cae..831081cdf1 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.EntranceFlank.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.EntranceFlank.cs @@ -35,7 +35,9 @@ public sealed partial class DungeonJob if (reservedTiles.Contains(neighbor)) continue; - tiles.Add((neighbor, _tile.GetVariantTile((ContentTileDefinition) tileDef, random))); + var tile = _tile.GetVariantTile((ContentTileDefinition)tileDef, random); + tiles.Add((neighbor, tile)); + AddLoadedTile(neighbor, tile); spawnPositions.Add(neighbor); } } @@ -45,7 +47,12 @@ public sealed partial class DungeonJob foreach (var entrance in spawnPositions) { - _entManager.SpawnEntitiesAttachedTo(_maps.GridTileToLocal(_gridUid, _grid, entrance), _entTable.GetSpawns(contents, random)); + var uids = _entManager.SpawnEntitiesAttachedTo(_maps.GridTileToLocal(_gridUid, _grid, entrance), _entTable.GetSpawns(contents, random)); + + foreach (var uid in uids) + { + AddLoadedEntity(entrance, uid); + } } } } diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Exterior.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Exterior.cs index 4f2f564ded..1248ab7505 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.Exterior.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.Exterior.cs @@ -1,3 +1,4 @@ +using System.Numerics; using System.Threading.Tasks; using Content.Shared.Maps; using Content.Shared.NPC; @@ -13,15 +14,22 @@ public sealed partial class DungeonJob /// /// /// - private async Task> GenerateExteriorDungen(Vector2i position, ExteriorDunGen dungen, HashSet reservedTiles, Random random) + private async Task> GenerateExteriorDungen(int runCount, int maxRuns, Vector2i position, ExteriorDunGen dungen, HashSet reservedTiles, Random random) { DebugTools.Assert(_grid.ChunkCount > 0); var aabb = new Box2i(_grid.LocalAABB.BottomLeft.Floored(), _grid.LocalAABB.TopRight.Floored()); - var angle = random.NextAngle(); + // TODO: Cross-layer seeding. Need this because we need to be able to spread the dungeons out. + var angle = new Random(_seed).NextAngle(); + var divisors = new Angle(Angle.FromDegrees(360) / maxRuns); + + // Offset each dungeon so they don't generate on top of each other. + for (var i = 0; i < runCount; i++) + { + angle += (random.NextFloat(0.6f, 1.4f)) * divisors; + } var distance = Math.Max(aabb.Width / 2f + 1f, aabb.Height / 2f + 1f); - var startTile = new Vector2i(0, (int) distance).Rotate(angle); Vector2i? dungeonSpawn = null; @@ -47,9 +55,19 @@ public sealed partial class DungeonJob }; } - var config = _prototype.Index(dungen.Proto); + // Move it further in based on the spawn angle. + if (dungen.Penetration.Y > 0) + { + var penetration = random.Next(dungen.Penetration.X, dungen.Penetration.Y); + var diff = dungeonSpawn.Value - startTile; + var diffVec = new Vector2(diff.X, diff.Y); + dungeonSpawn = (diffVec.Normalized() * (penetration + diffVec.Length())).Floored() + startTile; + } + + var subConfig = _prototype.Index(dungen.Proto); var nextSeed = random.Next(); - var dungeons = await GetDungeons(dungeonSpawn.Value, config, config.Layers, reservedTiles, nextSeed, new Random(nextSeed)); + var (dungeons, newReserved) = await GetDungeons(dungeonSpawn.Value, subConfig, subConfig.Layers, nextSeed, new Random(nextSeed), reserved: reservedTiles); + reservedTiles.UnionWith(newReserved); return dungeons; } diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.ExternalWindow.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.ExternalWindow.cs index 482cb34a56..1608e50266 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.ExternalWindow.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.ExternalWindow.cs @@ -105,7 +105,9 @@ public sealed partial class DungeonJob if (reservedTiles.Contains(neighbor)) continue; - tiles.Add((neighbor, _tile.GetVariantTile((ContentTileDefinition) tileDef, random))); + var tileVariant = _tile.GetVariantTile((ContentTileDefinition)tileDef, random); + tiles.Add((neighbor, tileVariant)); + AddLoadedTile(neighbor, tileVariant); index++; takenTiles.Add(neighbor); } @@ -119,7 +121,13 @@ public sealed partial class DungeonJob { var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile.Item1); - _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(contents, random)); + var uids = _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(contents, random)); + + foreach (var uid in uids) + { + AddLoadedEntity(tile.Item1, uid); + } + await SuspendDungeon(); if (!ValidateResume()) diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Fill.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Fill.cs index b579d4e5a1..95ec9d56e5 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.Fill.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.Fill.cs @@ -18,21 +18,45 @@ public sealed partial class DungeonJob if (reservedTiles.Contains(tile)) continue; + await SuspendDungeon(); + if (!ValidateResume()) + return; + if (!_maps.TryGetTileDef(_grid, tile, out var tileDef)) continue; if (fill.AllowedTiles != null && !fill.AllowedTiles.Contains(tileDef.ID)) continue; + // If noise then check it matches. + if (fill.ReservedNoise != null) + { + var value = fill.ReservedNoise.GetNoise(tile.X, tile.Y); + + if (fill.DistanceConfig != null) + { + // Need to get dx - dx in a range from -1 -> 1 + var dx = 2 * tile.X / fill.Size.X; + var dy = 2 * tile.Y / fill.Size.Y; + + var distance = GetDistance(dx, dy, fill.DistanceConfig); + + value = MathHelper.Lerp(value, 1f - distance, fill.DistanceConfig.BlendWeight); + } + + value *= (fill.Invert ? -1 : 1); + + if (value < fill.Threshold) + continue; + } + if (!_anchorable.TileFree((_gridUid, _grid), tile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask)) continue; var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile); - _entManager.SpawnEntity(fill.Entity, gridPos); + var uid = _entManager.SpawnEntity(fill.Entity, gridPos); - await SuspendDungeon(); - if (!ValidateResume()) - break; + AddLoadedEntity(tile, uid); } } } diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Helpers.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Helpers.cs index c57757b421..4ced89ba0e 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.Helpers.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.Helpers.cs @@ -52,6 +52,8 @@ public sealed partial class DungeonJob } } } + + dungeon.RefreshAllTiles(); } private void WidenCorridor(Dungeon dungeon, float width, ICollection corridorTiles) diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.InternalWindow.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.InternalWindow.cs index f80b3face7..c736eb6070 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.InternalWindow.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.InternalWindow.cs @@ -81,9 +81,16 @@ public sealed partial class DungeonJob { var tile = validTiles[j]; var gridPos = _maps.GridTileToLocal(_gridUid, _grid, tile); - _maps.SetTile(_gridUid, _grid, tile, _tile.GetVariantTile((ContentTileDefinition) tileDef, random)); + var tileVariant = _tile.GetVariantTile((ContentTileDefinition)tileDef, random); + _maps.SetTile(_gridUid, _grid, tile, tileVariant); + AddLoadedTile(tile, tileVariant); - _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(contents, random)); + var uids = _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(contents, random)); + + foreach (var uid in uids) + { + AddLoadedEntity(tile, uid); + } } if (validTiles.Count > 0) diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Junction.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Junction.cs index 28cbc9b208..88db2fad42 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.Junction.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.Junction.cs @@ -113,10 +113,17 @@ public sealed partial class DungeonJob if (reservedTiles.Contains(weh)) continue; - _maps.SetTile(_gridUid, _grid, weh, _tile.GetVariantTile((ContentTileDefinition) tileDef, random)); + var tileVariant = _tile.GetVariantTile((ContentTileDefinition)tileDef, random); + _maps.SetTile(_gridUid, _grid, weh, tileVariant); + AddLoadedTile(weh, tileVariant); var coords = _maps.GridTileToLocal(_gridUid, _grid, weh); - _entManager.SpawnEntitiesAttachedTo(coords, _entTable.GetSpawns(contents, random)); + var uids = _entManager.SpawnEntitiesAttachedTo(coords, _entTable.GetSpawns(contents, random)); + + foreach (var uid in uids) + { + AddLoadedEntity(weh, uid); + } } break; diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.MiddleConnection.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.MiddleConnection.cs index d6e3c09d62..52edaf3433 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.MiddleConnection.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.MiddleConnection.cs @@ -18,6 +18,7 @@ public sealed partial class DungeonJob // Grab all of the room bounds // Then, work out connections between them var roomBorders = new Dictionary>(dungeon.Rooms.Count); + var flank = gen.Flank; foreach (var room in dungeon.Rooms) { @@ -107,18 +108,30 @@ public sealed partial class DungeonJob continue; width--; - _maps.SetTile(_gridUid, _grid, node, _tile.GetVariantTile((ContentTileDefinition) tileDef, random)); + var tileVariant = _tile.GetVariantTile((ContentTileDefinition)tileDef, random); + _maps.SetTile(_gridUid, _grid, node, tileVariant); + AddLoadedTile(node, tileVariant); if (flankContents != null && nodeDistances.Count - i <= 2) { - _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(flankContents, random)); + var uids = _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(flankContents, random)); + + foreach (var uid in uids) + { + AddLoadedEntity(node, uid); + } } else { // Iterate neighbors and check for blockers, if so bulldoze ClearDoor(dungeon, _grid, node); - _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(contents, random)); + var uids = _entManager.SpawnEntitiesAttachedTo(gridPos, _entTable.GetSpawns(contents, random)); + + foreach (var uid in uids) + { + AddLoadedEntity(node, uid); + } } if (width == 0) diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Mobs.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Mobs.cs index caf6828e43..341dbf168a 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.Mobs.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.Mobs.cs @@ -49,6 +49,7 @@ public sealed partial class DungeonJob _entManager.RemoveComponent(uid); _entManager.RemoveComponent(uid); npcs.SleepNPC(uid); + AddLoadedEntity(tile, uid); } break; diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Noise.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Noise.cs index b2526ec17d..201948fc02 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.Noise.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.Noise.cs @@ -98,7 +98,9 @@ public sealed partial class DungeonJob var variant = _tile.PickVariant((ContentTileDefinition) tileDef, random); var adjusted = Vector2.Transform(node + _grid.TileSizeHalfVector, matrix).Floored(); - tiles.Add((adjusted, new Tile(tileDef.TileId, variant: variant))); + var tileVariant = new Tile(tileDef.TileId, variant: variant); + tiles.Add((adjusted, tileVariant)); + AddLoadedTile(adjusted, tileVariant); roomTiles.Add(adjusted); tileCount++; break; @@ -127,8 +129,7 @@ public sealed partial class DungeonJob } } - await SuspendIfOutOfTime(); - ValidateResume(); + await SuspendDungeon(); } var center = Vector2.Zero; @@ -140,8 +141,7 @@ public sealed partial class DungeonJob center /= roomTiles.Count; rooms.Add(new DungeonRoom(roomTiles, center, roomArea, new HashSet())); - await SuspendIfOutOfTime(); - ValidateResume(); + await SuspendDungeon(); } _maps.SetTiles(_gridUid, _grid, tiles); diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Ore.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Ore.cs index 78ab2b7a0d..035820fd90 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.Ore.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.Ore.cs @@ -19,12 +19,26 @@ public sealed partial class DungeonJob HashSet reservedTiles, Random random) { + var emptyTiles = false; + var replaceEntities = new Dictionary(); + var availableTiles = new List(); + var remapName = _entManager.ComponentFactory.GetComponentName(); + var replacementRemapping = new Dictionary(); + + if (_prototype.TryIndex(gen.Replacement, out var replacementProto) && + replacementProto.Components.TryGetComponent(remapName, out var replacementComps)) + { + var remappingComp = (EntityRemapComponent) replacementComps; + replacementRemapping = remappingComp.Mask; + } + + if (gen.Replacement != null) + { + replacementRemapping[gen.Replacement.Value] = gen.Entity; + } + foreach (var dungeon in dungeons) { - var emptyTiles = false; - var replaceEntities = new Dictionary(); - var availableTiles = new List(); - foreach (var node in dungeon.AllTiles) { if (reservedTiles.Contains(node)) @@ -41,19 +55,23 @@ public sealed partial class DungeonJob // We use existing entities as a mark to spawn in place // OR // We check for any existing entities to see if we can spawn there. - while (enumerator.MoveNext(out var uid)) + // We can't replace so just stop here. + if (gen.Replacement != null) { - // We can't replace so just stop here. - if (gen.Replacement == null) - break; - - var prototype = _entManager.GetComponent(uid.Value).EntityPrototype; - - if (prototype?.ID == gen.Replacement) + while (enumerator.MoveNext(out var uid)) { - replaceEntities[node] = uid.Value; - found = true; - break; + var prototype = _entManager.GetComponent(uid.Value).EntityPrototype; + + if (string.IsNullOrEmpty(prototype?.ID)) + continue; + + // It has a valid remapping so take it over. + if (replacementRemapping.ContainsKey(prototype.ID)) + { + replaceEntities[node] = uid.Value; + found = true; + break; + } } } @@ -68,83 +86,86 @@ public sealed partial class DungeonJob if (!ValidateResume()) return; } + } - var remapping = new Dictionary(); + var remapping = new Dictionary(); - // TODO: Move this to engine - if (_prototype.TryIndex(gen.Entity, out var proto) && - proto.Components.TryGetComponent("EntityRemap", out var comps)) + // TODO: Move this to engine + if (_prototype.TryIndex(gen.Entity, out var proto) && + proto.Components.TryGetComponent(remapName, out var comps)) + { + var remappingComp = (EntityRemapComponent) comps; + remapping = remappingComp.Mask; + } + + var frontier = new ValueList(32); + + // Iterate the group counts and pathfind out each group. + for (var i = 0; i < gen.Count; i++) + { + var groupSize = random.Next(gen.MinGroupSize, gen.MaxGroupSize + 1); + + // While we have remaining tiles keep iterating + while (groupSize > 0 && availableTiles.Count > 0) { - var remappingComp = (EntityRemapComponent) comps; - remapping = remappingComp.Mask; + var startNode = random.PickAndTake(availableTiles); + frontier.Clear(); + frontier.Add(startNode); + + // This essentially may lead to a vein being split in multiple areas but the count matters more than position. + while (frontier.Count > 0 && groupSize > 0) + { + // Need to pick a random index so we don't just get straight lines of ores. + var frontierIndex = random.Next(frontier.Count); + var node = frontier[frontierIndex]; + frontier.RemoveSwap(frontierIndex); + availableTiles.Remove(node); + + // Add neighbors if they're valid, worst case we add no more and pick another random seed tile. + for (var x = -1; x <= 1; x++) + { + for (var y = -1; y <= 1; y++) + { + var neighbor = new Vector2i(node.X + x, node.Y + y); + + if (frontier.Contains(neighbor) || !availableTiles.Contains(neighbor)) + continue; + + frontier.Add(neighbor); + } + } + + var prototype = gen.Entity; + + // May have been deleted while iteration was suspended. + if (replaceEntities.TryGetValue(node, out var existingEnt) && _entManager.TryGetComponent(existingEnt, out MetaDataComponent? metadata)) + { + var existingProto = metadata.EntityPrototype; + _entManager.DeleteEntity(existingEnt); + + if (existingProto != null && remapping.TryGetValue(existingProto.ID, out var remapped)) + { + prototype = remapped; + } + } + + // Tile valid salad so add it. + var uid = _entManager.SpawnAtPosition(prototype, _maps.GridTileToLocal(_gridUid, _grid, node)); + AddLoadedEntity(node, uid); + + groupSize--; + + await SuspendDungeon(); + + if (!ValidateResume()) + return; + } } - var frontier = new ValueList(32); - - // Iterate the group counts and pathfind out each group. - for (var i = 0; i < gen.Count; i++) + if (groupSize > 0) { - await SuspendDungeon(); - - if (!ValidateResume()) - return; - - var groupSize = random.Next(gen.MinGroupSize, gen.MaxGroupSize + 1); - - // While we have remaining tiles keep iterating - while (groupSize > 0 && availableTiles.Count > 0) - { - var startNode = random.PickAndTake(availableTiles); - frontier.Clear(); - frontier.Add(startNode); - - // This essentially may lead to a vein being split in multiple areas but the count matters more than position. - while (frontier.Count > 0 && groupSize > 0) - { - // Need to pick a random index so we don't just get straight lines of ores. - var frontierIndex = random.Next(frontier.Count); - var node = frontier[frontierIndex]; - frontier.RemoveSwap(frontierIndex); - availableTiles.Remove(node); - - // Add neighbors if they're valid, worst case we add no more and pick another random seed tile. - for (var x = -1; x <= 1; x++) - { - for (var y = -1; y <= 1; y++) - { - var neighbor = new Vector2i(node.X + x, node.Y + y); - - if (frontier.Contains(neighbor) || !availableTiles.Contains(neighbor)) - continue; - - frontier.Add(neighbor); - } - } - - var prototype = gen.Entity; - - if (replaceEntities.TryGetValue(node, out var existingEnt)) - { - var existingProto = _entManager.GetComponent(existingEnt).EntityPrototype; - _entManager.DeleteEntity(existingEnt); - - if (existingProto != null && remapping.TryGetValue(existingProto.ID, out var remapped)) - { - prototype = remapped; - } - } - - // Tile valid salad so add it. - _entManager.SpawnAtPosition(prototype, _maps.GridTileToLocal(_gridUid, _grid, node)); - - groupSize--; - } - } - - if (groupSize > 0) - { - _sawmill.Warning($"Found remaining group size for ore veins of {gen.Replacement ?? "null"}!"); - } + // Not super worried depending on the gen it's fine. + _sawmill.Debug($"Found remaining group size for ore veins of {gen.Replacement ?? "null"} / {gen.Entity}!"); } } } diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.Roof.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.Roof.cs new file mode 100644 index 0000000000..2d00dbe2ab --- /dev/null +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.Roof.cs @@ -0,0 +1,38 @@ +using System.Threading.Tasks; +using Content.Server.Light.EntitySystems; +using Content.Shared.Light.Components; +using Content.Shared.Procedural; +using Content.Shared.Procedural.DungeonLayers; + +namespace Content.Server.Procedural.DungeonJob; + +public sealed partial class DungeonJob +{ + public async Task RoofGen(RoofDunGen roof, List dungeons, HashSet reservedTiles, Random random) + { + var roofComp = _entManager.EnsureComponent(_gridUid); + + var noise = roof.Noise; + var oldSeed = noise?.GetSeed() ?? 0; + noise?.SetSeed(_seed + oldSeed); + var rooves = _entManager.System(); + + foreach (var dungeon in dungeons) + { + foreach (var tile in dungeon.AllTiles) + { + if (reservedTiles.Contains(tile)) + continue; + + var value = noise?.GetNoise(tile.X, tile.Y) ?? 1f; + + if (value < roof.Threshold) + continue; + + rooves.SetRoof((_gridUid, _grid, roofComp), tile, true); + } + } + + noise?.SetSeed(oldSeed); + } +} diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.RoomEntrance.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.RoomEntrance.cs index a4a01b5f0b..17ad9b5e35 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.RoomEntrance.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.RoomEntrance.cs @@ -25,7 +25,9 @@ public sealed partial class DungeonJob if (reservedTiles.Contains(entrance)) continue; - setTiles.Add((entrance, _tile.GetVariantTile((ContentTileDefinition) tileDef, random))); + var tileVariant = _tile.GetVariantTile((ContentTileDefinition)tileDef, random); + setTiles.Add((entrance, tileVariant)); + AddLoadedTile(entrance, tileVariant); } } @@ -38,10 +40,15 @@ public sealed partial class DungeonJob if (reservedTiles.Contains(entrance)) continue; - _entManager.SpawnEntitiesAttachedTo( + var uids = _entManager.SpawnEntitiesAttachedTo( _maps.GridTileToLocal(_gridUid, _grid, entrance), _entTable.GetSpawns(contents, random)); + foreach (var uid in uids) + { + AddLoadedEntity(entrance, uid); + } + await SuspendDungeon(); if (!ValidateResume()) diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.SampleDecal.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.SampleDecal.cs new file mode 100644 index 0000000000..b240c0b84c --- /dev/null +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.SampleDecal.cs @@ -0,0 +1,64 @@ +using System.Threading.Tasks; +using Content.Shared.Procedural; +using Content.Shared.Procedural.DungeonLayers; +using Robust.Shared.Map; +using Robust.Shared.Random; + +namespace Content.Server.Procedural.DungeonJob; + +public sealed partial class DungeonJob +{ + /// + /// + /// + private async Task PostGen(SampleDecalDunGen gen, + List dungeons, + HashSet reservedTiles, + Random random) + { + var oldSeed = gen.Noise.GetSeed(); + gen.Noise.SetSeed(_seed + oldSeed); + + foreach (var dungeon in dungeons) + { + foreach (var tile in dungeon.AllTiles) + { + if (reservedTiles.Contains(tile)) + continue; + + var invert = gen.Invert; + var value = gen.Noise.GetNoise(tile.X, tile.Y); + value = invert ? value * -1 : value; + + if (value < gen.Threshold) + continue; + + // Not allowed + if (!_maps.TryGetTileRef(_gridUid, _grid, tile, out var tileRef) || + !gen.AllowedTiles.Contains(_tileDefManager[tileRef.Tile.TypeId].ID)) + { + continue; + } + + // Occupied? + if (!_anchorable.TileFree(_grid, tile, DungeonSystem.CollisionLayer, DungeonSystem.CollisionMask)) + continue; + + _decals.TryAddDecal(random.Pick(gen.Decals), new EntityCoordinates(_gridUid, tile), out var did); + AddLoadedDecal(tile, did); + + if (gen.ReserveTiles) + { + reservedTiles.Add(tile); + } + + await SuspendDungeon(); + + if (!ValidateResume()) + return; + } + } + + gen.Noise.SetSeed(oldSeed); + } +} diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.SampleEntity.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.SampleEntity.cs new file mode 100644 index 0000000000..1a9fc8b2e2 --- /dev/null +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.SampleEntity.cs @@ -0,0 +1,62 @@ +using System.Threading.Tasks; +using Content.Shared.Procedural; +using Content.Shared.Procedural.DungeonLayers; +using Robust.Shared.Map; +using Robust.Shared.Random; + +namespace Content.Server.Procedural.DungeonJob; + +public sealed partial class DungeonJob +{ + /// + /// + /// + private async Task PostGen( + SampleEntityDunGen gen, + List dungeons, + HashSet reservedTiles, + Random random) + { + var oldSeed = gen.Noise.GetSeed(); + gen.Noise.SetSeed(_seed + oldSeed); + + foreach (var dungeon in dungeons) + { + foreach (var tile in dungeon.AllTiles) + { + if (reservedTiles.Contains(tile)) + continue; + + var invert = gen.Invert; + var value = gen.Noise.GetNoise(tile.X, tile.Y); + value = invert ? value * -1 : value; + + if (value < gen.Threshold) + continue; + + // Not allowed + if (!_maps.TryGetTileRef(_gridUid, _grid, tile, out var tileRef) || + !gen.AllowedTiles.Contains(_tileDefManager[tileRef.Tile.TypeId].ID)) + { + continue; + } + + var gridTile = _maps.GridTileToLocal(_gridUid, _grid, tile); + var uid = _entManager.SpawnAttachedTo(random.Pick(gen.Entities), gridTile); + AddLoadedEntity(tile, uid); + + if (gen.ReserveTiles) + { + reservedTiles.Add(tile); + } + + await SuspendDungeon(); + + if (!ValidateResume()) + return; + } + } + + gen.Noise.SetSeed(oldSeed); + } +} diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.SampleTile.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.SampleTile.cs new file mode 100644 index 0000000000..97875b22fc --- /dev/null +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.SampleTile.cs @@ -0,0 +1,66 @@ +using System.Threading.Tasks; +using Content.Shared.Procedural; +using Content.Shared.Procedural.DungeonLayers; +using Robust.Shared.Map; +using Robust.Shared.Noise; +using Robust.Shared.Random; + +namespace Content.Server.Procedural.DungeonJob; + +public sealed partial class DungeonJob +{ + /// + /// + /// + private async Task PostGen(SampleTileDunGen gen, + List dungeons, + HashSet reservedTiles, + Random random) + { + var noise = gen.Noise; + var oldSeed = noise.GetSeed(); + noise.SetSeed(_seed + oldSeed); + var tiles = new List<(Vector2i Index, Tile Tile)>(); + var tileDef = _prototype.Index(gen.Tile); + var variants = tileDef.PlacementVariants.Length; + + foreach (var dungeon in dungeons) + { + foreach (var tile in dungeon.AllTiles) + { + if (reservedTiles.Contains(tile)) + continue; + + var invert = gen.Invert; + var value = noise.GetNoise(tile.X, tile.Y); + value = invert ? value * -1 : value; + + if (value < gen.Threshold) + continue; + + var variantValue = (noise.GetNoise(tile.X * 8, tile.Y * 8, variants) + 1f) * 100; + var variant = _tile.PickVariant(tileDef, (int)variantValue); + var tileVariant = new Tile(tileDef.TileId, variant: variant); + + tiles.Add((tile, tileVariant)); + AddLoadedTile(tile, tileVariant); + + await SuspendDungeon(); + + if (!ValidateResume()) + return; + } + } + + gen.Noise.SetSeed(oldSeed); + _maps.SetTiles(_gridUid, _grid, tiles); + + if (gen.ReserveTiles) + { + foreach (var tile in tiles) + { + reservedTiles.Add(tile.Index); + } + } + } +} diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.SplineDungeonConnector.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.SplineDungeonConnector.cs index a131efd353..8635bc0f48 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.SplineDungeonConnector.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.SplineDungeonConnector.cs @@ -2,6 +2,7 @@ using System.Numerics; using System.Threading.Tasks; using Content.Server.NPC.Pathfinding; using Content.Shared.Procedural; +using Content.Shared.Procedural.DungeonLayers; using Content.Shared.Procedural.PostGeneration; using Robust.Shared.Map; using Robust.Shared.Random; @@ -11,10 +12,10 @@ namespace Content.Server.Procedural.DungeonJob; public sealed partial class DungeonJob { /// - /// + /// /// private async Task PostGen( - SplineDungeonConnectorDunGen gen, + Shared.Procedural.DungeonLayers.SplineDungeonConnectorDunGen gen, List dungeons, HashSet reservedTiles, Random random) @@ -59,6 +60,7 @@ public sealed partial class DungeonJob { Start = pair.Start, End = pair.End, + Diagonals = false, TileCost = node => { // We want these to get prioritised internally and into space if it's a space dungeon. @@ -110,6 +112,7 @@ public sealed partial class DungeonJob } tiles.Add((node, tile)); + AddLoadedTile(node, tile); } _maps.SetTiles(_gridUid, _grid, tiles); @@ -123,6 +126,7 @@ public sealed partial class DungeonJob allTiles.Add(node); tiles.Add((node, pathTile)); + AddLoadedTile(node, pathTile); } _maps.SetTiles(_gridUid, _grid, tiles); diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.WallMount.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.WallMount.cs index e5bb32bd0c..fa2921b80a 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.WallMount.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.WallMount.cs @@ -32,11 +32,18 @@ public sealed partial class DungeonJob if (reservedTiles.Contains(neighbor)) continue; - _maps.SetTile(_gridUid, _grid, neighbor, _tile.GetVariantTile(tileDef, random)); + var tileVariant = _tile.GetVariantTile(tileDef, random); + _maps.SetTile(_gridUid, _grid, neighbor, tileVariant); + AddLoadedTile(neighbor, tileVariant); var gridPos = _maps.GridTileToLocal(_gridUid, _grid, neighbor); var protoNames = _entTable.GetSpawns(contents, random); - _entManager.SpawnEntitiesAttachedTo(gridPos, protoNames); + var uids = _entManager.SpawnEntitiesAttachedTo(gridPos, protoNames); + + foreach (var uid in uids) + { + AddLoadedEntity(neighbor, uid); + } await SuspendDungeon(); if (!ValidateResume()) diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.cs index 77404fc963..8145492205 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.cs @@ -1,8 +1,7 @@ -using System.Linq; +using System.Numerics; using System.Threading; using System.Threading.Tasks; using Content.Server.Decals; -using Content.Server.NPC.Components; using Content.Server.NPC.HTN; using Content.Server.NPC.Systems; using Content.Server.Shuttles.Systems; @@ -23,11 +22,10 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; -using IDunGenLayer = Content.Shared.Procedural.IDunGenLayer; namespace Content.Server.Procedural.DungeonJob; -public sealed partial class DungeonJob : Job> +public sealed partial class DungeonJob : Job<(List, DungeonData)> { public bool TimeSlice = true; @@ -60,6 +58,10 @@ public sealed partial class DungeonJob : Job> private readonly ISawmill _sawmill; + private DungeonData _data = new(); + + private HashSet? _reservedTiles; + public DungeonJob( ISawmill sawmill, double maxTime, @@ -79,12 +81,14 @@ public sealed partial class DungeonJob : Job> int seed, Vector2i position, EntityCoordinates? targetCoordinates = null, - CancellationToken cancellation = default) : base(maxTime, cancellation) + CancellationToken cancellation = default, + HashSet? reservedTiles = null) : base(maxTime, cancellation) { _sawmill = sawmill; _entManager = entManager; _prototype = prototype; _tileDefManager = tileDefManager; + _reservedTiles = reservedTiles; _anchorable = anchorable; _decals = decals; @@ -111,17 +115,18 @@ public sealed partial class DungeonJob : Job> /// /// Gets the relevant dungeon, running recursively as relevant. /// - /// Should we reserve tiles even if the config doesn't specify. - private async Task> GetDungeons( + /// Should we reserve tiles even if the config doesn't specify. + private async Task<(List, HashSet)> GetDungeons( Vector2i position, DungeonConfig config, List layers, - HashSet reservedTiles, int seed, Random random, + HashSet? reserved = null, List? existing = null) { var dungeons = new List(); + var reservedTiles = reserved == null ? new HashSet() : new HashSet(reserved); // Don't pass dungeons back up the "stack". They are ref types though it's a caller problem if they start trying to mutate it. if (existing != null) @@ -137,8 +142,8 @@ public sealed partial class DungeonJob : Job> foreach (var layer in layers) { - var dungCount = dungeons.Count; - await RunLayer(dungeons, position, layer, reservedTiles, seed, random); + var dungCount = dungeons.Count; + await RunLayer(i, count, config, dungeons, position, layer, reservedTiles, seed, random); if (config.ReserveTiles) { @@ -152,24 +157,23 @@ public sealed partial class DungeonJob : Job> await SuspendDungeon(); if (!ValidateResume()) - return new List(); + return (new List(), new HashSet()); } } - return dungeons; + // Only return the new dungeons and applicable reserved tiles. + return (dungeons[(existing?.Count ?? 0)..], config.ReturnReserved ? reservedTiles : new HashSet()); } - protected override async Task?> Process() + protected override async Task<(List, DungeonData)> Process() { _sawmill.Info($"Generating dungeon {_gen} with seed {_seed} on {_entManager.ToPrettyString(_gridUid)}"); _grid.CanSplit = false; var random = new Random(_seed); + var oldTileCount = _reservedTiles?.Count ?? 0; var position = (_position + random.NextPolarVector2(_gen.MinOffset, _gen.MaxOffset)).Floored(); - // Tiles we can no longer generate on due to being reserved elsewhere. - var reservedTiles = new HashSet(); - - var dungeons = await GetDungeons(position, _gen, _gen.Layers, reservedTiles, _seed, random); + var (dungeons, _) = await GetDungeons(position, _gen, _gen.Layers, _seed, random, reserved: _reservedTiles); // To make it slightly more deterministic treat this RNG as separate ig. // Post-processing after finishing loading. @@ -181,6 +185,7 @@ public sealed partial class DungeonJob : Job> } // Defer splitting so they don't get spammed and so we don't have to worry about tracking the grid along the way. + DebugTools.Assert(oldTileCount == (_reservedTiles?.Count ?? 0)); _grid.CanSplit = true; _entManager.System().CheckSplits(_gridUid); var npcSystem = _entManager.System(); @@ -194,10 +199,13 @@ public sealed partial class DungeonJob : Job> } _sawmill.Info($"Finished generating dungeon {_gen} with seed {_seed}"); - return dungeons; + return (dungeons, _data); } private async Task RunLayer( + int runCount, + int maxRuns, + DungeonConfig config, List dungeons, Vector2i position, IDunGenLayer layer, @@ -205,7 +213,7 @@ public sealed partial class DungeonJob : Job> int seed, Random random) { - _sawmill.Debug($"Doing postgen {layer.GetType()} for {_gen} with seed {_seed}"); + // _sawmill.Debug($"Doing postgen {layer.GetType()} for {_gen} with seed {_seed}"); // If there's a way to just call the methods directly for the love of god tell me. // Some of these don't care about reservedtiles because they only operate on dungeon tiles (which should @@ -219,15 +227,12 @@ public sealed partial class DungeonJob : Job> case AutoCablingDunGen cabling: await PostGen(cabling, dungeons[^1], reservedTiles, random); break; - case BiomeMarkerLayerDunGen markerPost: - await PostGen(markerPost, dungeons[^1], reservedTiles, random); - break; - case BiomeDunGen biome: - await PostGen(biome, dungeons[^1], reservedTiles, random); - break; case BoundaryWallDunGen boundary: await PostGen(boundary, dungeons[^1], reservedTiles, random); break; + case ChunkDunGen chunk: + dungeons.Add(await PostGen(chunk, reservedTiles, random)); + break; case CornerClutterDunGen clutter: await PostGen(clutter, dungeons[^1], reservedTiles, random); break; @@ -244,7 +249,7 @@ public sealed partial class DungeonJob : Job> await PostGen(flank, dungeons[^1], reservedTiles, random); break; case ExteriorDunGen exterior: - dungeons.AddRange(await GenerateExteriorDungen(position, exterior, reservedTiles, random)); + dungeons.AddRange(await GenerateExteriorDungen(runCount, maxRuns, position, exterior, reservedTiles, random)); break; case FillGridDunGen fill: await GenerateFillDunGen(fill, dungeons, reservedTiles); @@ -285,27 +290,67 @@ public sealed partial class DungeonJob : Job> case PrototypeDunGen prototypo: var groupConfig = _prototype.Index(prototypo.Proto); position = (position + random.NextPolarVector2(groupConfig.MinOffset, groupConfig.MaxOffset)).Floored(); + List? inheritedDungeons = null; + HashSet? inheritedReserved = null; + + switch (prototypo.InheritReserved) + { + case ReservedInheritance.All: + inheritedReserved = new HashSet(reservedTiles); + break; + case ReservedInheritance.None: + break; + default: + throw new NotImplementedException(); + } switch (prototypo.InheritDungeons) { case DungeonInheritance.All: - dungeons.AddRange(await GetDungeons(position, groupConfig, groupConfig.Layers, reservedTiles, seed, random, existing: dungeons)); + inheritedDungeons = dungeons; break; case DungeonInheritance.Last: - dungeons.AddRange(await GetDungeons(position, groupConfig, groupConfig.Layers, reservedTiles, seed, random, existing: dungeons.GetRange(dungeons.Count - 1, 1))); + inheritedDungeons = dungeons.GetRange(dungeons.Count - 1, 1); break; case DungeonInheritance.None: - dungeons.AddRange(await GetDungeons(position, groupConfig, groupConfig.Layers, reservedTiles, seed, random)); break; + default: + throw new NotImplementedException(); + } + + var (newDungeons, newReserved) = await GetDungeons(position, + groupConfig, + groupConfig.Layers, + seed, + random, + reserved: inheritedReserved, + existing: inheritedDungeons); + dungeons.AddRange(newDungeons); + + if (groupConfig.ReturnReserved) + { + reservedTiles.UnionWith(newReserved); } break; case ReplaceTileDunGen replace: await GenerateTileReplacementDunGen(replace, dungeons, reservedTiles, random); break; + case RoofDunGen roof: + await RoofGen(roof, dungeons, reservedTiles, random); + break; case RoomEntranceDunGen rEntrance: await PostGen(rEntrance, dungeons[^1], reservedTiles, random); break; + case SampleDecalDunGen sdec: + await PostGen(sdec, dungeons, reservedTiles, random); + break; + case SampleEntityDunGen sent: + await PostGen(sent, dungeons, reservedTiles, random); + break; + case SampleTileDunGen stile: + await PostGen(stile, dungeons, reservedTiles, random); + break; case SplineDungeonConnectorDunGen spline: dungeons.Add(await PostGen(spline, dungeons, reservedTiles, random)); break; @@ -320,11 +365,6 @@ public sealed partial class DungeonJob : Job> } } - private void LogDataError(Type type) - { - _sawmill.Error($"Unable to find dungeon data keys for {type}"); - } - [Pure] private bool ValidateResume() { @@ -346,4 +386,19 @@ public sealed partial class DungeonJob : Job> await SuspendIfOutOfTime(); } + + private void AddLoadedEntity(Vector2i tile, EntityUid ent) + { + _data.Entities[ent] = tile; + } + + private void AddLoadedDecal(Vector2 tile, uint decal) + { + _data.Decals[decal] = tile; + } + + private void AddLoadedTile(Vector2i index, Tile tile) + { + _data.Tiles[index] = tile; + } } diff --git a/Content.Server/Procedural/DungeonSystem.Rooms.cs b/Content.Server/Procedural/DungeonSystem.Rooms.cs index e5b0981b3d..e5143454d3 100644 --- a/Content.Server/Procedural/DungeonSystem.Rooms.cs +++ b/Content.Server/Procedural/DungeonSystem.Rooms.cs @@ -113,7 +113,7 @@ public sealed partial class DungeonSystem return roomRotation; } - public void SpawnRoom( + public DungeonData SpawnRoom( EntityUid gridUid, MapGridComponent grid, Matrix3x2 roomTransform, @@ -126,6 +126,7 @@ public sealed partial class DungeonSystem var templateMapUid = _maps.GetMapOrInvalid(roomMap); var templateGrid = Comp(templateMapUid); var roomDimensions = room.Size; + var data = new DungeonData(); var finalRoomRotation = roomTransform.Rotation(); @@ -154,6 +155,7 @@ public sealed partial class DungeonSystem } _tiles.Add((rounded, tileRef.Tile)); + data.Tiles[rounded] = tileRef.Tile; if (clearExisting) { @@ -186,6 +188,7 @@ public sealed partial class DungeonSystem // TODO: Copy the templated entity as is with serv var ent = Spawn(protoId, new EntityCoordinates(gridUid, childPos)); + data.Entities.Add(ent, childPos.Floored()); var childXform = _xformQuery.GetComponent(ent); var anchored = templateXform.Anchored; @@ -256,14 +259,18 @@ public sealed partial class DungeonSystem var result = _decals.TryAddDecal( decal.Id, new EntityCoordinates(gridUid, position), - out _, + out var did, decal.Color, angle, decal.ZIndex, decal.Cleanable); + data.Decals.Add(did, position); + DebugTools.Assert(result); } } + + return data; } } diff --git a/Content.Server/Procedural/DungeonSystem.cs b/Content.Server/Procedural/DungeonSystem.cs index 3a0a7ab2cd..b758fa2d16 100644 --- a/Content.Server/Procedural/DungeonSystem.cs +++ b/Content.Server/Procedural/DungeonSystem.cs @@ -199,7 +199,8 @@ public sealed partial class DungeonSystem : SharedDungeonSystem MapGridComponent grid, Vector2i position, int seed, - EntityCoordinates? coordinates = null) + EntityCoordinates? coordinates = null, + HashSet? reservedTiles = null) { var cancelToken = new CancellationTokenSource(); var job = new DungeonJob.DungeonJob( @@ -221,18 +222,20 @@ public sealed partial class DungeonSystem : SharedDungeonSystem seed, position, coordinates, - cancelToken.Token); + cancelToken.Token, + reservedTiles); _dungeonJobs.Add(job, cancelToken); _dungeonJobQueue.EnqueueJob(job); } - public async Task> GenerateDungeonAsync( + public async Task<(List, DungeonData)> GenerateDungeonAsync( DungeonConfig gen, EntityUid gridUid, MapGridComponent grid, Vector2i position, - int seed) + int seed, + HashSet? reservedTiles = null) { var cancelToken = new CancellationTokenSource(); var job = new DungeonJob.DungeonJob( @@ -254,7 +257,8 @@ public sealed partial class DungeonSystem : SharedDungeonSystem seed, position, null, - cancelToken.Token); + cancelToken.Token, + reservedTiles); _dungeonJobs.Add(job, cancelToken); _dungeonJobQueue.EnqueueJob(job); @@ -265,7 +269,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem throw job.Exception; } - return job.Result!; + return job.Result; } public Angle GetDungeonRotation(int seed) diff --git a/Content.Server/Salvage/SpawnSalvageMissionJob.cs b/Content.Server/Salvage/SpawnSalvageMissionJob.cs index cbce4dc692..3a74a67dbf 100644 --- a/Content.Server/Salvage/SpawnSalvageMissionJob.cs +++ b/Content.Server/Salvage/SpawnSalvageMissionJob.cs @@ -1,22 +1,17 @@ -using System.Collections; using System.Linq; using System.Numerics; using System.Threading; using System.Threading.Tasks; -using Content.Server.Atmos; using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Robust.Shared.CPUJob.JobQueues; using Content.Server.Ghost.Roles.Components; -using Content.Server.Parallax; using Content.Server.Procedural; using Content.Server.Salvage.Expeditions; -using Content.Server.Salvage.Expeditions.Structure; using Content.Shared.Atmos; using Content.Shared.Construction.EntitySystems; using Content.Shared.Dataset; using Content.Shared.Gravity; -using Content.Shared.Parallax.Biomes; using Content.Shared.Physics; using Content.Shared.Procedural; using Content.Shared.Procedural.Loot; @@ -25,15 +20,14 @@ using Content.Shared.Salvage; using Content.Shared.Salvage.Expeditions; using Content.Shared.Salvage.Expeditions.Modifiers; using Content.Shared.Shuttles.Components; -using Content.Shared.Storage; using Robust.Shared.Collections; -using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; using Robust.Shared.Utility; using Content.Server.Shuttles.Components; +using Content.Shared.Procedural.Components; namespace Content.Server.Salvage; @@ -120,14 +114,13 @@ public sealed class SpawnSalvageMissionJob : Job .GetMission(difficultyProto, _missionParams.Seed); var missionBiome = _prototypeManager.Index(mission.Biome); + BiomeComponent? biome = null; if (missionBiome.BiomePrototype != null) { - var biome = _entManager.AddComponent(mapUid); var biomeSystem = _entManager.System(); - biomeSystem.SetTemplate(mapUid, biome, _prototypeManager.Index(missionBiome.BiomePrototype)); - biomeSystem.SetSeed(mapUid, biome, mission.Seed); - _entManager.Dirty(mapUid, biome); + + biome = biomeSystem.AddBiome(mapUid, missionBiome.BiomePrototype.Value, mission.Seed); // Gravity var gravity = _entManager.EnsureComponent(mapUid); @@ -172,7 +165,7 @@ public sealed class SpawnSalvageMissionJob : Job dungeonOffset = dungeonRotation.RotateVec(dungeonOffset); var dungeonMod = _prototypeManager.Index(mission.Dungeon); var dungeonConfig = _prototypeManager.Index(dungeonMod.Proto); - var dungeons = await WaitAsyncTask(_dungeon.GenerateDungeonAsync(dungeonConfig, mapUid, grid, (Vector2i)dungeonOffset, + var (dungeons, data) = await WaitAsyncTask(_dungeon.GenerateDungeonAsync(dungeonConfig, mapUid, grid, (Vector2i)dungeonOffset, _missionParams.Seed)); var dungeon = dungeons.First(); @@ -183,18 +176,20 @@ public sealed class SpawnSalvageMissionJob : Job return false; } - expedition.DungeonLocation = dungeonOffset; - - List reservedTiles = new(); - - foreach (var tile in _map.GetTilesIntersecting(mapUid, grid, new Circle(Vector2.Zero, landingPadRadius), false)) + // Don't modify any dungeon tiles with chunk gen. + // Have to defer biome loading until the primo dungen is generated. + if (biome != null) { - if (!_biome.TryGetBiomeTile(mapUid, grid, tile.GridIndices, out _)) - continue; + foreach (var tile in dungeon.AllTiles) + { + biome.ModifiedTiles.Add(tile); + } - reservedTiles.Add(tile.GridIndices); + biome.Enabled = true; } + expedition.DungeonLocation = dungeonOffset; + var budgetEntries = new List(); /* @@ -208,13 +203,14 @@ public sealed class SpawnSalvageMissionJob : Job if (!lootProto.Guaranteed) continue; - try + foreach (var rule in lootProto.LootRules) { - await SpawnDungeonLoot(lootProto, mapUid); - } - catch (Exception e) - { - _sawmill.Error($"Failed to spawn guaranteed loot {lootProto.ID}: {e}"); + switch (rule) + { + case BiomeLoot biomeLoot: + _biome.AddLayer(mapUid, $"{rule}", biomeLoot.Proto); + break; + } } } @@ -323,32 +319,4 @@ public sealed class SpawnSalvageMissionJob : Job // oh noooooooooooo } - - private async Task SpawnDungeonLoot(SalvageLootPrototype loot, EntityUid gridUid) - { - for (var i = 0; i < loot.LootRules.Count; i++) - { - var rule = loot.LootRules[i]; - - switch (rule) - { - case BiomeMarkerLoot biomeLoot: - { - if (_entManager.TryGetComponent(gridUid, out var biome)) - { - _biome.AddMarkerLayer(gridUid, biome, biomeLoot.Prototype); - } - } - break; - case BiomeTemplateLoot biomeLoot: - { - if (_entManager.TryGetComponent(gridUid, out var biome)) - { - _biome.AddTemplate(gridUid, biome, "Loot", _prototypeManager.Index(biomeLoot.Prototype), i); - } - } - break; - } - } - } } diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index aa1c2e6dff..d2a3157232 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -6,6 +6,7 @@ using Content.Server.DeviceNetwork.Systems; using Content.Server.GameTicking; using Content.Server.GameTicking.Events; using Content.Server.Parallax; +using Content.Server.Procedural; using Content.Server.Screens.Components; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; @@ -22,7 +23,6 @@ using Content.Shared.DeviceNetwork.Components; using Content.Shared.GameTicking; using Content.Shared.Mobs.Components; using Content.Shared.Movement.Components; -using Content.Shared.Parallax.Biomes; using Content.Shared.Salvage; using Content.Shared.Shuttles.Components; using Content.Shared.Tiles; @@ -82,11 +82,11 @@ public sealed class ArrivalsSystem : EntitySystem /// private const float RoundStartFTLDuration = 10f; - private readonly List> _arrivalsBiomeOptions = new() + private readonly List _arrivalsBiomeOptions = new() { - "Grasslands", - "LowDesert", - "Snow", + "BiomeGrasslands", + "BiomeLowDesert", + "BiomeSnow", }; public override void Initialize() diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index 13e13bf8f3..9e9bcb6c42 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -1,12 +1,14 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; +using Content.Server.Decals; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; using Content.Server.Station.Events; using Content.Shared.Body.Components; using Content.Shared.CCVar; using Content.Shared.Database; +using Content.Shared.Decals; using Content.Shared.Ghost; using Content.Shared.Maps; using Content.Shared.Parallax; @@ -956,6 +958,7 @@ public sealed partial class ShuttleSystem var transform = _physics.GetRelativePhysicsTransform((uid, xform), xform.MapUid.Value); var aabbs = new List(manager.Fixtures.Count); var tileSet = new List<(Vector2i, Tile)>(); + TryComp(xform.MapUid.Value, out DecalGridComponent? decalGrid); foreach (var fixture in manager.Fixtures.Values) { @@ -969,9 +972,15 @@ public sealed partial class ShuttleSystem aabb = aabb.Enlarged(0.2f); aabbs.Add(aabb); - // Handle clearing biome stuff as relevant. + if (decalGrid != null) + { + foreach (var decal in _decals.GetDecalsIntersecting(xform.MapUid.Value, aabb)) + { + _decals.RemoveDecal(xform.MapUid.Value, decal.Index, decalGrid); + } + } + tileSet.Clear(); - _biomes.ReserveTiles(xform.MapUid.Value, aabb, tileSet); _lookupEnts.Clear(); _immuneEnts.Clear(); // TODO: Ideally we'd query first BEFORE moving grid but needs adjustments above. diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.cs index cea7fbfc09..525e16ae1a 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Administration.Logs; using Content.Server.Body.Systems; using Content.Server.Buckle.Systems; +using Content.Server.Decals; using Content.Server.Parallax; using Content.Server.Procedural; using Content.Server.Shuttles.Components; @@ -41,10 +42,12 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; [Dependency] private readonly BiomeSystem _biomes = default!; [Dependency] private readonly BodySystem _bobby = default!; [Dependency] private readonly BuckleSystem _buckle = default!; [Dependency] private readonly DamageableSystem _damageSys = default!; + [Dependency] private readonly DecalSystem _decals = default!; [Dependency] private readonly DockingSystem _dockSystem = default!; [Dependency] private readonly DungeonSystem _dungeon = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; diff --git a/Content.Server/Station/Components/StationBiomeComponent.cs b/Content.Server/Station/Components/StationBiomeComponent.cs index 0eb64aaff8..a2105d6cef 100644 --- a/Content.Server/Station/Components/StationBiomeComponent.cs +++ b/Content.Server/Station/Components/StationBiomeComponent.cs @@ -1,5 +1,4 @@ using Content.Server.Station.Systems; -using Content.Shared.Parallax.Biomes; using Robust.Shared.Prototypes; namespace Content.Server.Station.Components; @@ -11,7 +10,7 @@ namespace Content.Server.Station.Components; public sealed partial class StationBiomeComponent : Component { [DataField(required: true)] - public ProtoId Biome = "Grasslands"; + public EntProtoId Biome = "BiomeGrasslands"; // If null, its random [DataField] diff --git a/Content.Server/Station/Systems/StationBiomeSystem.cs b/Content.Server/Station/Systems/StationBiomeSystem.cs index c12e2f47e4..c0777f6052 100644 --- a/Content.Server/Station/Systems/StationBiomeSystem.cs +++ b/Content.Server/Station/Systems/StationBiomeSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Parallax; +using Content.Server.Procedural; using Content.Server.Station.Components; using Content.Server.Station.Events; using Robust.Shared.Prototypes; diff --git a/Content.Server/Tabletop/TabletopSystem.cs b/Content.Server/Tabletop/TabletopSystem.cs index e771add0e4..3c3065c95a 100644 --- a/Content.Server/Tabletop/TabletopSystem.cs +++ b/Content.Server/Tabletop/TabletopSystem.cs @@ -191,7 +191,7 @@ namespace Content.Server.Tabletop if (!TryComp(uid, out ActorComponent? actor)) { RemComp(uid); - return; + continue; } if (actor.PlayerSession.Status != SessionStatus.InGame || !CanSeeTable(uid, gamer.Tabletop)) diff --git a/Content.Shared/CCVar/CCVars.Biome.cs b/Content.Shared/CCVar/CCVars.Biome.cs new file mode 100644 index 0000000000..13c6cd548e --- /dev/null +++ b/Content.Shared/CCVar/CCVars.Biome.cs @@ -0,0 +1,18 @@ +using Robust.Shared.Configuration; + +namespace Content.Shared.CCVar; + +public sealed partial class CCVars +{ + /// + /// Load range for biomes. Set this higher than PVS so server can have some time to load in before the client arrives. + /// + public static readonly CVarDef BiomeLoadRange = + CVarDef.Create("biome.load_range", 20f, CVar.SERVERONLY); + + /// + /// Time allocation (ms) for how long biomes are allowed to load. + /// + public static readonly CVarDef BiomeLoadTime = + CVarDef.Create("biome.load_time", 0.03f, CVar.SERVERONLY); +} diff --git a/Content.Shared/Parallax/Biomes/BiomeComponent.cs b/Content.Shared/Parallax/Biomes/BiomeComponent.cs deleted file mode 100644 index af8eb88683..0000000000 --- a/Content.Shared/Parallax/Biomes/BiomeComponent.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Content.Shared.Parallax.Biomes.Layers; -using Content.Shared.Parallax.Biomes.Markers; -using Robust.Shared.GameStates; -using Robust.Shared.Noise; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; - -namespace Content.Shared.Parallax.Biomes; - -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(SharedBiomeSystem))] -public sealed partial class BiomeComponent : Component -{ - /// - /// Do we load / deload. - /// - [DataField, ViewVariables(VVAccess.ReadWrite), Access(Other = AccessPermissions.ReadWriteExecute)] - public bool Enabled = true; - - [ViewVariables(VVAccess.ReadWrite), DataField("seed")] - [AutoNetworkedField] - public int Seed = -1; - - /// - /// The underlying entity, decal, and tile layers for the biome. - /// - [DataField("layers")] - [AutoNetworkedField] - public List Layers = new(); - - /// - /// Templates to use for . - /// If this is set on mapinit, it will fill out layers automatically. - /// If not set, use BiomeSystem to do it. - /// Prototype reloading will also use this. - /// - [DataField] - public ProtoId? Template; - - /// - /// If we've already generated a tile and couldn't deload it then we won't ever reload it in future. - /// Stored by [Chunkorigin, Tiles] - /// - [DataField("modifiedTiles")] - public Dictionary> ModifiedTiles = new(); - - /// - /// Decals that have been loaded as a part of this biome. - /// - [DataField("decals")] - public Dictionary> LoadedDecals = new(); - - [DataField("entities")] - public Dictionary> LoadedEntities = new(); - - /// - /// Currently active chunks - /// - [DataField("loadedChunks")] - public HashSet LoadedChunks = new(); - - #region Markers - - /// - /// Work out entire marker tiles in advance but only load the entities when in range. - /// - [DataField("pendingMarkers")] - public Dictionary>> PendingMarkers = new(); - - /// - /// Track what markers we've loaded already to avoid double-loading. - /// - [DataField("loadedMarkers", customTypeSerializer:typeof(PrototypeIdDictionarySerializer, BiomeMarkerLayerPrototype>))] - public Dictionary> LoadedMarkers = new(); - - [DataField] - public HashSet> MarkerLayers = new(); - - /// - /// One-tick forcing of marker layers to bulldoze any entities in the way. - /// - [DataField] - public HashSet> ForcedMarkerLayers = new(); - - #endregion -} diff --git a/Content.Shared/Parallax/Biomes/BiomeTemplatePrototype.cs b/Content.Shared/Parallax/Biomes/BiomeTemplatePrototype.cs deleted file mode 100644 index 437ead63a7..0000000000 --- a/Content.Shared/Parallax/Biomes/BiomeTemplatePrototype.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Content.Shared.Parallax.Biomes.Layers; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Parallax.Biomes; - -/// -/// A preset group of biome layers to be used for a -/// -[Prototype] -public sealed partial class BiomeTemplatePrototype : IPrototype -{ - [IdDataField] public string ID { get; private set; } = default!; - - [DataField("layers")] - public List Layers = new(); -} diff --git a/Content.Shared/Parallax/Biomes/Layers/BiomeDecalLayer.cs b/Content.Shared/Parallax/Biomes/Layers/BiomeDecalLayer.cs deleted file mode 100644 index 5e31e513a9..0000000000 --- a/Content.Shared/Parallax/Biomes/Layers/BiomeDecalLayer.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Content.Shared.Decals; -using Content.Shared.Maps; -using Robust.Shared.Noise; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; - -namespace Content.Shared.Parallax.Biomes.Layers; - -[Serializable, NetSerializable] -public sealed partial class BiomeDecalLayer : IBiomeWorldLayer -{ - /// - [DataField("allowedTiles", customTypeSerializer:typeof(PrototypeIdListSerializer))] - public List AllowedTiles { get; private set; } = new(); - - /// - /// Divide each tile up by this amount. - /// - [DataField("divisions")] - public float Divisions = 1f; - - [DataField("noise")] - public FastNoiseLite Noise { get; private set; } = new(0); - - /// - [DataField("threshold")] - public float Threshold { get; private set; } = 0.8f; - - /// - [DataField("invert")] public bool Invert { get; private set; } = false; - - [DataField("decals", required: true, customTypeSerializer:typeof(PrototypeIdListSerializer))] - public List Decals = new(); -} diff --git a/Content.Shared/Parallax/Biomes/Layers/BiomeDummyLayer.cs b/Content.Shared/Parallax/Biomes/Layers/BiomeDummyLayer.cs deleted file mode 100644 index 2beeba6e03..0000000000 --- a/Content.Shared/Parallax/Biomes/Layers/BiomeDummyLayer.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Robust.Shared.Noise; -using Robust.Shared.Serialization; - -namespace Content.Shared.Parallax.Biomes.Layers; - -/// -/// Dummy layer that specifies a marker to be replaced by external code. -/// For example if they wish to add their own layers at specific points across different templates. -/// -[Serializable, NetSerializable] -public sealed partial class BiomeDummyLayer : IBiomeLayer -{ - [DataField("id", required: true)] public string ID = string.Empty; - - public FastNoiseLite Noise { get; } = new(); - public float Threshold { get; } - public bool Invert { get; } -} diff --git a/Content.Shared/Parallax/Biomes/Layers/BiomeEntityLayer.cs b/Content.Shared/Parallax/Biomes/Layers/BiomeEntityLayer.cs deleted file mode 100644 index 21ffdd96e5..0000000000 --- a/Content.Shared/Parallax/Biomes/Layers/BiomeEntityLayer.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Content.Shared.Maps; -using Robust.Shared.Noise; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; - -namespace Content.Shared.Parallax.Biomes.Layers; - -[Serializable, NetSerializable] -public sealed partial class BiomeEntityLayer : IBiomeWorldLayer -{ - /// - [DataField("allowedTiles", customTypeSerializer:typeof(PrototypeIdListSerializer))] - public List AllowedTiles { get; private set; } = new(); - - [DataField("noise")] public FastNoiseLite Noise { get; private set; } = new(0); - - /// - [DataField("threshold")] - public float Threshold { get; private set; } = 0.5f; - - /// - [DataField("invert")] public bool Invert { get; private set; } = false; - - [DataField("entities", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List Entities = new(); -} diff --git a/Content.Shared/Parallax/Biomes/Layers/BiomeMetaLayer.cs b/Content.Shared/Parallax/Biomes/Layers/BiomeMetaLayer.cs deleted file mode 100644 index 51231405a8..0000000000 --- a/Content.Shared/Parallax/Biomes/Layers/BiomeMetaLayer.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Robust.Shared.Noise; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Parallax.Biomes.Layers; - -/// -/// Contains more biome layers recursively via a biome template. -/// Can be used for sub-biomes. -/// -[Serializable, NetSerializable] -public sealed partial class BiomeMetaLayer : IBiomeLayer -{ - [DataField("noise")] - public FastNoiseLite Noise { get; private set; } = new(0); - - /// - [DataField("threshold")] - public float Threshold { get; private set; } = -1f; - - /// - [DataField("invert")] - public bool Invert { get; private set; } - - [DataField("template", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string Template = string.Empty; -} diff --git a/Content.Shared/Parallax/Biomes/Layers/IBiomeLayer.cs b/Content.Shared/Parallax/Biomes/Layers/IBiomeLayer.cs deleted file mode 100644 index 3b1ad5c76c..0000000000 --- a/Content.Shared/Parallax/Biomes/Layers/IBiomeLayer.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Robust.Shared.Noise; - -namespace Content.Shared.Parallax.Biomes.Layers; - -[ImplicitDataDefinitionForInheritors] -public partial interface IBiomeLayer -{ - /// - /// Seed is used an offset from the relevant BiomeComponent's seed. - /// - FastNoiseLite Noise { get; } - - /// - /// Threshold for this layer to be present. If set to 0 forces it for every tile. - /// - float Threshold { get; } - - /// - /// Is the thresold inverted so we need to be lower than it. - /// - public bool Invert { get; } -} diff --git a/Content.Shared/Parallax/Biomes/Layers/IBiomeWorldLayer.cs b/Content.Shared/Parallax/Biomes/Layers/IBiomeWorldLayer.cs deleted file mode 100644 index e04db913b7..0000000000 --- a/Content.Shared/Parallax/Biomes/Layers/IBiomeWorldLayer.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Content.Shared.Parallax.Biomes.Layers; - -/// -/// Handles actual objects such as decals and entities. -/// -public partial interface IBiomeWorldLayer : IBiomeLayer -{ - /// - /// What tiles we're allowed to spawn on, real or biome. - /// - List AllowedTiles { get; } -} diff --git a/Content.Shared/Parallax/Biomes/Markers/BiomeMarkerLayerPrototype.cs b/Content.Shared/Parallax/Biomes/Markers/BiomeMarkerLayerPrototype.cs deleted file mode 100644 index fbc3a04eb4..0000000000 --- a/Content.Shared/Parallax/Biomes/Markers/BiomeMarkerLayerPrototype.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Parallax.Biomes.Markers; - -/// -/// Spawns entities inside of the specified area with the minimum specified radius. -/// -[Prototype] -public sealed partial class BiomeMarkerLayerPrototype : IBiomeMarkerLayer -{ - [IdDataField] public string ID { get; private set; } = default!; - - /// - /// Checks for the relevant entity for the tile before spawning. Useful for substituting walls with ore veins for example. - /// - [DataField] - public Dictionary EntityMask { get; private set; } = new(); - - /// - /// Default prototype to spawn. If null will fall back to entity mask. - /// - [DataField] - public string? Prototype { get; private set; } - - /// - /// Minimum radius between 2 points - /// - [DataField("radius")] - public float Radius = 32f; - - /// - /// Maximum amount of group spawns - /// - [DataField("maxCount")] - public int MaxCount = int.MaxValue; - - /// - /// Minimum entities to spawn in one group. - /// - [DataField] - public int MinGroupSize = 1; - - /// - /// Maximum entities to spawn in one group. - /// - [DataField] - public int MaxGroupSize = 1; - - /// - [DataField("size")] - public int Size { get; private set; } = 128; -} diff --git a/Content.Shared/Parallax/Biomes/Markers/IBiomeMarkerLayer.cs b/Content.Shared/Parallax/Biomes/Markers/IBiomeMarkerLayer.cs deleted file mode 100644 index de2913bb09..0000000000 --- a/Content.Shared/Parallax/Biomes/Markers/IBiomeMarkerLayer.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Robust.Shared.Prototypes; - -namespace Content.Shared.Parallax.Biomes.Markers; - -/// -/// Specifies one-off marker points to be used. This could be for dungeon markers, mob markers, etc. -/// These are run outside of the tile / decal / entity layers. -/// -public interface IBiomeMarkerLayer : IPrototype -{ - /// - /// Biome template to use as a mask for this layer. - /// - public Dictionary EntityMask { get; } - - public string? Prototype { get; } - - /// - /// How large the pre-generated points area is. - /// - public int Size { get; } -} diff --git a/Content.Shared/Parallax/Biomes/SharedBiomeSystem.cs b/Content.Shared/Parallax/Biomes/SharedBiomeSystem.cs deleted file mode 100644 index a5238e8c6e..0000000000 --- a/Content.Shared/Parallax/Biomes/SharedBiomeSystem.cs +++ /dev/null @@ -1,386 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Numerics; -using Content.Shared.Maps; -using Content.Shared.Parallax.Biomes.Layers; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Noise; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; -using Robust.Shared.Utility; - -namespace Content.Shared.Parallax.Biomes; - -public abstract class SharedBiomeSystem : EntitySystem -{ - [Dependency] protected readonly IPrototypeManager ProtoManager = default!; - [Dependency] private readonly ISerializationManager _serManager = default!; - [Dependency] protected readonly ITileDefinitionManager TileDefManager = default!; - [Dependency] private readonly TileSystem _tile = default!; - [Dependency] private readonly SharedMapSystem _map = default!; - - protected const byte ChunkSize = 8; - - private T Pick(List collection, float value) - { - // Listen I don't need this exact and I'm too lazy to finetune just for random ent picking. - value %= 1f; - value = Math.Clamp(value, 0f, 1f); - - if (collection.Count == 1) - return collection[0]; - - var randValue = value * collection.Count; - - foreach (var item in collection) - { - randValue -= 1f; - - if (randValue <= 0f) - { - return item; - } - } - - throw new ArgumentOutOfRangeException(); - } - - private int Pick(int count, float value) - { - value %= 1f; - value = Math.Clamp(value, 0f, 1f); - - if (count == 1) - return 0; - - value *= count; - - for (var i = 0; i < count; i++) - { - value -= 1f; - - if (value <= 0f) - { - return i; - } - } - - throw new ArgumentOutOfRangeException(); - } - - public bool TryGetBiomeTile(EntityUid uid, MapGridComponent grid, Vector2i indices, [NotNullWhen(true)] out Tile? tile) - { - if (_map.TryGetTileRef(uid, grid, indices, out var tileRef) && !tileRef.Tile.IsEmpty) - { - tile = tileRef.Tile; - return true; - } - - if (!TryComp(uid, out var biome)) - { - tile = null; - return false; - } - - return TryGetBiomeTile(indices, biome.Layers, biome.Seed, (uid, grid), out tile); - } - - /// - /// Tries to get the tile, real or otherwise, for the specified indices. - /// - public bool TryGetBiomeTile(Vector2i indices, List layers, int seed, Entity? grid, [NotNullWhen(true)] out Tile? tile) - { - if (grid is { } gridEnt && _map.TryGetTileRef(gridEnt, gridEnt.Comp, indices, out var tileRef) && !tileRef.Tile.IsEmpty) - { - tile = tileRef.Tile; - return true; - } - - return TryGetTile(indices, layers, seed, grid, out tile); - } - - /// - /// Tries to get the tile, real or otherwise, for the specified indices. - /// - [Obsolete("Use the Entity? overload")] - public bool TryGetBiomeTile(Vector2i indices, List layers, int seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile) - { - return TryGetBiomeTile(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out tile); - } - - /// - /// Gets the underlying biome tile, ignoring any existing tile that may be there. - /// - public bool TryGetTile(Vector2i indices, List layers, int seed, Entity? grid, [NotNullWhen(true)] out Tile? tile) - { - for (var i = layers.Count - 1; i >= 0; i--) - { - var layer = layers[i]; - var noiseCopy = GetNoise(layer.Noise, seed); - - var invert = layer.Invert; - var value = noiseCopy.GetNoise(indices.X, indices.Y); - value = invert ? value * -1 : value; - - if (value < layer.Threshold) - continue; - - // Check if the tile is from meta layer, otherwise fall back to default layers. - if (layer is BiomeMetaLayer meta) - { - if (TryGetBiomeTile(indices, ProtoManager.Index(meta.Template).Layers, seed, grid, out tile)) - { - return true; - } - - continue; - } - - if (layer is not BiomeTileLayer tileLayer) - continue; - - if (TryGetTile(indices, noiseCopy, tileLayer.Invert, tileLayer.Threshold, ProtoManager.Index(tileLayer.Tile), tileLayer.Variants, out tile)) - { - return true; - } - } - - tile = null; - return false; - } - - /// - /// Gets the underlying biome tile, ignoring any existing tile that may be there. - /// - [Obsolete("Use the Entity? overload")] - public bool TryGetTile(Vector2i indices, List layers, int seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile) - { - return TryGetTile(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out tile); - } - - /// - /// Gets the underlying biome tile, ignoring any existing tile that may be there. - /// - private bool TryGetTile(Vector2i indices, FastNoiseLite noise, bool invert, float threshold, ContentTileDefinition tileDef, List? variants, [NotNullWhen(true)] out Tile? tile) - { - var found = noise.GetNoise(indices.X, indices.Y); - found = invert ? found * -1 : found; - - if (found < threshold) - { - tile = null; - return false; - } - - byte variant = 0; - var variantCount = variants?.Count ?? tileDef.Variants; - - // Pick a variant tile if they're available as well - if (variantCount > 1) - { - var variantValue = (noise.GetNoise(indices.X * 8, indices.Y * 8, variantCount) + 1f) * 100; - variant = _tile.PickVariant(tileDef, (int)variantValue); - } - - tile = new Tile(tileDef.TileId, variant); - return true; - } - - /// - /// Tries to get the relevant entity for this tile. - /// - public bool TryGetEntity(Vector2i indices, BiomeComponent component, Entity? grid, - [NotNullWhen(true)] out string? entity) - { - if (!TryGetBiomeTile(indices, component.Layers, component.Seed, grid, out var tile)) - { - entity = null; - return false; - } - - return TryGetEntity(indices, component.Layers, tile.Value, component.Seed, grid, out entity); - } - - /// - /// Tries to get the relevant entity for this tile. - /// - [Obsolete("Use the Entity? overload")] - public bool TryGetEntity(Vector2i indices, BiomeComponent component, MapGridComponent grid, - [NotNullWhen(true)] out string? entity) - { - return TryGetEntity(indices, component, grid == null ? null : (grid.Owner, grid), out entity); - } - - public bool TryGetEntity(Vector2i indices, List layers, Tile tileRef, int seed, Entity? grid, - [NotNullWhen(true)] out string? entity) - { - var tileId = TileDefManager[tileRef.TypeId].ID; - - for (var i = layers.Count - 1; i >= 0; i--) - { - var layer = layers[i]; - - switch (layer) - { - case BiomeDummyLayer: - continue; - case IBiomeWorldLayer worldLayer: - if (!worldLayer.AllowedTiles.Contains(tileId)) - continue; - - break; - case BiomeMetaLayer: - break; - default: - continue; - } - - var noiseCopy = GetNoise(layer.Noise, seed); - - var invert = layer.Invert; - var value = noiseCopy.GetNoise(indices.X, indices.Y); - value = invert ? value * -1 : value; - - if (value < layer.Threshold) - continue; - - if (layer is BiomeMetaLayer meta) - { - if (TryGetEntity(indices, ProtoManager.Index(meta.Template).Layers, tileRef, seed, grid, out entity)) - { - return true; - } - - continue; - } - - // Decals might block entity so need to check if there's one in front of us. - if (layer is not BiomeEntityLayer biomeLayer) - { - entity = null; - return false; - } - - var noiseValue = noiseCopy.GetNoise(indices.X, indices.Y, i); - entity = Pick(biomeLayer.Entities, (noiseValue + 1f) / 2f); - return true; - } - - entity = null; - return false; - } - - [Obsolete("Use the Entity? overload")] - public bool TryGetEntity(Vector2i indices, List layers, Tile tileRef, int seed, MapGridComponent grid, - [NotNullWhen(true)] out string? entity) - { - return TryGetEntity(indices, layers, tileRef, seed, grid == null ? null : (grid.Owner, grid), out entity); - } - - /// - /// Tries to get the relevant decals for this tile. - /// - public bool TryGetDecals(Vector2i indices, List layers, int seed, Entity? grid, - [NotNullWhen(true)] out List<(string ID, Vector2 Position)>? decals) - { - if (!TryGetBiomeTile(indices, layers, seed, grid, out var tileRef)) - { - decals = null; - return false; - } - - var tileId = TileDefManager[tileRef.Value.TypeId].ID; - - for (var i = layers.Count - 1; i >= 0; i--) - { - var layer = layers[i]; - - // Entities might block decal so need to check if there's one in front of us. - switch (layer) - { - case BiomeDummyLayer: - continue; - case IBiomeWorldLayer worldLayer: - if (!worldLayer.AllowedTiles.Contains(tileId)) - continue; - - break; - case BiomeMetaLayer: - break; - default: - continue; - } - - var invert = layer.Invert; - var noiseCopy = GetNoise(layer.Noise, seed); - var value = noiseCopy.GetNoise(indices.X, indices.Y); - value = invert ? value * -1 : value; - - if (value < layer.Threshold) - continue; - - if (layer is BiomeMetaLayer meta) - { - if (TryGetDecals(indices, ProtoManager.Index(meta.Template).Layers, seed, grid, out decals)) - { - return true; - } - - continue; - } - - // Check if the other layer should even render, if not then keep going. - if (layer is not BiomeDecalLayer decalLayer) - { - decals = null; - return false; - } - - decals = new List<(string ID, Vector2 Position)>(); - - for (var x = 0; x < decalLayer.Divisions; x++) - { - for (var y = 0; y < decalLayer.Divisions; y++) - { - var index = new Vector2(indices.X + x * 1f / decalLayer.Divisions, indices.Y + y * 1f / decalLayer.Divisions); - var decalValue = noiseCopy.GetNoise(index.X, index.Y); - decalValue = invert ? decalValue * -1 : decalValue; - - if (decalValue < decalLayer.Threshold) - continue; - - decals.Add((Pick(decalLayer.Decals, (noiseCopy.GetNoise(indices.X, indices.Y, x + y * decalLayer.Divisions) + 1f) / 2f), index)); - } - } - - // Check other layers - if (decals.Count == 0) - continue; - - return true; - } - - decals = null; - return false; - } - - /// - /// Tries to get the relevant decals for this tile. - /// - [Obsolete("Use the Entity? overload")] - public bool TryGetDecals(Vector2i indices, List layers, int seed, MapGridComponent grid, - [NotNullWhen(true)] out List<(string ID, Vector2 Position)>? decals) - { - return TryGetDecals(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out decals); - } - - private FastNoiseLite GetNoise(FastNoiseLite seedNoise, int seed) - { - var noiseCopy = new FastNoiseLite(); - _serManager.CopyTo(seedNoise, ref noiseCopy, notNullableOverride: true); - noiseCopy.SetSeed(noiseCopy.GetSeed() + seed); - // Ensure re-calculate is run. - noiseCopy.SetFractalOctaves(noiseCopy.GetFractalOctaves()); - return noiseCopy; - } -} diff --git a/Content.Shared/Procedural/Components/BiomeComponent.cs b/Content.Shared/Procedural/Components/BiomeComponent.cs new file mode 100644 index 0000000000..f79a31c414 --- /dev/null +++ b/Content.Shared/Procedural/Components/BiomeComponent.cs @@ -0,0 +1,86 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Procedural.Components; + +/// +/// A layer inside of +/// +[DataRecord] +public sealed record BiomeMetaLayer +{ + /// + /// Chunk dimensions for this meta layer. Will try to infer it from the first layer of the dungeon if null. + /// + [DataField] + public int? Size; + + /// + /// Meta layers that this one requires to be loaded first. + /// Will ensure all of the chunks for our corresponding area are loaded. + /// + public List? DependsOn; + + /// + /// Can this layer be unloaded if no one is in range. + /// + public bool CanUnload = true; + + /// + /// Dungeon config to load inside the specified area. + /// + [DataField(required: true)] + public ProtoId Dungeon = new(); +} + +[RegisterComponent] +public sealed partial class BiomeComponent : Component +{ + /// + /// Can we load / unload chunks. + /// + [DataField] + public bool Enabled = true; + + /// + /// Areas queued for preloading. Will add these during and then flag as modified so they retain. + /// + [DataField] + public List PreloadAreas = new(); + + /// + /// Is there currently a job that's loading. + /// + public bool Loading = false; + + [DataField] + public int Seed; + + /// + /// Layer key and associated data. + /// + [DataField(required: true)] + public Dictionary Layers = new(); + + /// + /// Layer removals that are pending. + /// + [DataField] + public List PendingRemovals = new(); + + /// + /// Data that is currently loaded. + /// + [DataField] + public Dictionary> LoadedData = new(); + + /// + /// Flag modified tiles so we don't try and unload / reload them. + /// + [DataField] + public HashSet ModifiedTiles = new(); + + /// + /// Bounds loaded by players for this tick. + /// + public List LoadedBounds = new(); +} diff --git a/Content.Shared/Procedural/Components/BiomeForceUnloadComponent.cs b/Content.Shared/Procedural/Components/BiomeForceUnloadComponent.cs new file mode 100644 index 0000000000..215a8f345d --- /dev/null +++ b/Content.Shared/Procedural/Components/BiomeForceUnloadComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Procedural.Components; + +/// +/// Will forcibly unload an entity no matter what. Useful if you have consistent entities that will never be default or the likes. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class BiomeForceUnloadComponent : Component; diff --git a/Content.Shared/Procedural/Distance/DunGenDistanceSquared.cs b/Content.Shared/Procedural/Distance/DunGenDistanceSquared.cs new file mode 100644 index 0000000000..813f5fc1d4 --- /dev/null +++ b/Content.Shared/Procedural/Distance/DunGenDistanceSquared.cs @@ -0,0 +1,7 @@ +namespace Content.Shared.Procedural.Distance; + +public sealed partial class DunGenDistanceSquared : IDunGenDistance +{ + [DataField] + public float BlendWeight { get; set; } = 0.50f; +} diff --git a/Content.Shared/Procedural/DungeonConfig.cs b/Content.Shared/Procedural/DungeonConfig.cs index 7c84b1a6a3..56131e5b28 100644 --- a/Content.Shared/Procedural/DungeonConfig.cs +++ b/Content.Shared/Procedural/DungeonConfig.cs @@ -12,11 +12,18 @@ public partial class DungeonConfig public List Layers = new(); /// - /// Should we reserve the tiles generated by this config so no other dungeons can spawn on it within the same job? + /// Should we reserve the tiles generated by this config so no other layers at the same level can spawn on this tile? /// [DataField] public bool ReserveTiles; + /// + /// Should we return the reserved tiles to the upper level. + /// Set to false if you don't care if this dungeon has its tiles overwritten at higher levels. + /// + [DataField] + public bool ReturnReserved = true; + /// /// Minimum times to run the config. /// diff --git a/Content.Shared/Procedural/DungeonData.cs b/Content.Shared/Procedural/DungeonData.cs new file mode 100644 index 0000000000..a0209f91a6 --- /dev/null +++ b/Content.Shared/Procedural/DungeonData.cs @@ -0,0 +1,41 @@ +using System.Linq; +using System.Numerics; +using Robust.Shared.Map; + +namespace Content.Shared.Procedural; + +/// +/// Contains the loaded data for a dungeon. +/// +[DataDefinition] +public sealed partial class DungeonData +{ + [DataField] + public Dictionary Decals = new(); + + [DataField] + public Dictionary Entities = new(); + + [DataField] + public Dictionary Tiles = new(); + + public static DungeonData Empty = new(); + + public void Merge(DungeonData data) + { + foreach (var did in data.Decals) + { + Decals[did.Key] = did.Value; + } + + foreach (var ent in data.Entities) + { + Entities[ent.Key] = ent.Value; + } + + foreach (var tile in data.Tiles) + { + Tiles[tile.Key] = tile.Value; + } + } +} diff --git a/Content.Shared/Procedural/DungeonGenerators/ChunkDunGen.cs b/Content.Shared/Procedural/DungeonGenerators/ChunkDunGen.cs new file mode 100644 index 0000000000..b48a1b3fd6 --- /dev/null +++ b/Content.Shared/Procedural/DungeonGenerators/ChunkDunGen.cs @@ -0,0 +1,24 @@ +using Robust.Shared.Noise; + +namespace Content.Shared.Procedural.DungeonGenerators; + +/// +/// Turns a chunked area into a dungeon for layer purposes. Assumes the position is the BL origin. +/// +public sealed partial class ChunkDunGen : IDunGenLayer +{ + [DataField] + public int Size = 16; + + /// + /// Noise to apply for each tile conditionally. + /// + [DataField] + public FastNoiseLite? Noise; + + /// + /// Threshold for noise. Does nothing if is null. + /// + [DataField] + public float Threshold = -1f; +} diff --git a/Content.Shared/Procedural/DungeonGenerators/ExteriorDunGen.cs b/Content.Shared/Procedural/DungeonGenerators/ExteriorDunGen.cs index e9a5181f8d..2ece880851 100644 --- a/Content.Shared/Procedural/DungeonGenerators/ExteriorDunGen.cs +++ b/Content.Shared/Procedural/DungeonGenerators/ExteriorDunGen.cs @@ -10,4 +10,10 @@ public sealed partial class ExteriorDunGen : IDunGenLayer { [DataField(required: true)] public ProtoId Proto; + + /// + /// Minimum and maximum penetration. + /// + [DataField] + public Vector2i Penetration = new Vector2i(5, 15); } diff --git a/Content.Shared/Procedural/DungeonGenerators/PrototypeDunGen.cs b/Content.Shared/Procedural/DungeonGenerators/PrototypeDunGen.cs index 89a4ab216a..c3f7ae3f33 100644 --- a/Content.Shared/Procedural/DungeonGenerators/PrototypeDunGen.cs +++ b/Content.Shared/Procedural/DungeonGenerators/PrototypeDunGen.cs @@ -14,6 +14,12 @@ public sealed partial class PrototypeDunGen : IDunGenLayer [DataField] public DungeonInheritance InheritDungeons = DungeonInheritance.None; + /// + /// Should we pass in the current level's reserved tiles to the prototype. + /// + [DataField] + public ReservedInheritance InheritReserved = ReservedInheritance.All; + [DataField(required: true)] public ProtoId Proto; } @@ -35,3 +41,16 @@ public enum DungeonInheritance : byte /// All, } + +public enum ReservedInheritance : byte +{ + /// + /// Don't inherit any reserved tiles. + /// + None, + + /// + /// Inherit reserved tiles, + /// + All, +} diff --git a/Content.Shared/Procedural/PostGeneration/AutoCablingDunGen.cs b/Content.Shared/Procedural/DungeonLayers/AutoCablingDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/AutoCablingDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/AutoCablingDunGen.cs diff --git a/Content.Shared/Procedural/PostGeneration/BoundaryWallDunGen.cs b/Content.Shared/Procedural/DungeonLayers/BoundaryWallDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/BoundaryWallDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/BoundaryWallDunGen.cs diff --git a/Content.Shared/Procedural/PostGeneration/CornerClutterDunGen.cs b/Content.Shared/Procedural/DungeonLayers/CornerClutterDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/CornerClutterDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/CornerClutterDunGen.cs diff --git a/Content.Shared/Procedural/PostGeneration/CorridorClutterDunGen.cs b/Content.Shared/Procedural/DungeonLayers/CorridorClutterDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/CorridorClutterDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/CorridorClutterDunGen.cs diff --git a/Content.Shared/Procedural/PostGeneration/CorridorDecalSkirtingDunGen.cs b/Content.Shared/Procedural/DungeonLayers/CorridorDecalSkirtingDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/CorridorDecalSkirtingDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/CorridorDecalSkirtingDunGen.cs diff --git a/Content.Shared/Procedural/PostGeneration/CorridorDunGen.cs b/Content.Shared/Procedural/DungeonLayers/CorridorDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/CorridorDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/CorridorDunGen.cs diff --git a/Content.Shared/Procedural/PostGeneration/DungeonEntranceDunGen.cs b/Content.Shared/Procedural/DungeonLayers/DungeonEntranceDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/DungeonEntranceDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/DungeonEntranceDunGen.cs diff --git a/Content.Shared/Procedural/PostGeneration/EntranceFlankDunGen.cs b/Content.Shared/Procedural/DungeonLayers/EntranceFlankDunGen.cs similarity index 93% rename from Content.Shared/Procedural/PostGeneration/EntranceFlankDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/EntranceFlankDunGen.cs index f9be6caf6a..cd6cf169fc 100644 --- a/Content.Shared/Procedural/PostGeneration/EntranceFlankDunGen.cs +++ b/Content.Shared/Procedural/DungeonLayers/EntranceFlankDunGen.cs @@ -1,5 +1,6 @@ using Content.Shared.EntityTable; using Content.Shared.Maps; +using Content.Shared.Storage; using Robust.Shared.Prototypes; namespace Content.Shared.Procedural.PostGeneration; diff --git a/Content.Shared/Procedural/PostGeneration/ExternalWindowDunGen.cs b/Content.Shared/Procedural/DungeonLayers/ExternalWindowDunGen.cs similarity index 94% rename from Content.Shared/Procedural/PostGeneration/ExternalWindowDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/ExternalWindowDunGen.cs index fc992ea7b8..30b8302263 100644 --- a/Content.Shared/Procedural/PostGeneration/ExternalWindowDunGen.cs +++ b/Content.Shared/Procedural/DungeonLayers/ExternalWindowDunGen.cs @@ -1,6 +1,5 @@ using Content.Shared.EntityTable; using Content.Shared.Maps; -using Content.Shared.Storage; using Robust.Shared.Prototypes; namespace Content.Shared.Procedural.PostGeneration; diff --git a/Content.Shared/Procedural/DungeonLayers/FillGridDunGen.cs b/Content.Shared/Procedural/DungeonLayers/FillGridDunGen.cs index 363de0a511..e2506298fd 100644 --- a/Content.Shared/Procedural/DungeonLayers/FillGridDunGen.cs +++ b/Content.Shared/Procedural/DungeonLayers/FillGridDunGen.cs @@ -1,4 +1,7 @@ +using System.Numerics; using Content.Shared.Maps; +using Content.Shared.Procedural.Distance; +using Robust.Shared.Noise; using Robust.Shared.Prototypes; namespace Content.Shared.Procedural.DungeonLayers; @@ -6,10 +9,6 @@ namespace Content.Shared.Procedural.DungeonLayers; /// /// Fills unreserved tiles with the specified entity prototype. /// -/// -/// DungeonData keys are: -/// - Fill -/// public sealed partial class FillGridDunGen : IDunGenLayer { /// @@ -20,4 +19,29 @@ public sealed partial class FillGridDunGen : IDunGenLayer [DataField(required: true)] public EntProtoId Entity; + + #region Noise + + [DataField] + public bool Invert; + + /// + /// Optionally don't spawn entities if the noise value matches. + /// + [DataField] + public FastNoiseLite? ReservedNoise; + + /// + /// Noise threshold for . Does nothing without it. + /// + [DataField] + public float Threshold = -1f; + + [DataField] + public IDunGenDistance? DistanceConfig; + + [DataField] + public Vector2 Size; + + #endregion } diff --git a/Content.Shared/Procedural/PostGeneration/InternalWindowDunGen.cs b/Content.Shared/Procedural/DungeonLayers/InternalWindowDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/InternalWindowDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/InternalWindowDunGen.cs diff --git a/Content.Shared/Procedural/PostGeneration/JunctionDunGen.cs b/Content.Shared/Procedural/DungeonLayers/JunctionDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/JunctionDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/JunctionDunGen.cs diff --git a/Content.Shared/Procedural/PostGeneration/MiddleConnectionDunGen.cs b/Content.Shared/Procedural/DungeonLayers/MiddleConnectionDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/MiddleConnectionDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/MiddleConnectionDunGen.cs diff --git a/Content.Shared/Procedural/DungeonLayers/MobsDunGen.cs b/Content.Shared/Procedural/DungeonLayers/MobsDunGen.cs index 5525341eb9..1b754d3778 100644 --- a/Content.Shared/Procedural/DungeonLayers/MobsDunGen.cs +++ b/Content.Shared/Procedural/DungeonLayers/MobsDunGen.cs @@ -1,10 +1,8 @@ using Content.Shared.EntityTable; -using Content.Shared.Storage; using Robust.Shared.Prototypes; namespace Content.Shared.Procedural.DungeonLayers; - /// /// Spawns mobs inside of the dungeon randomly. /// diff --git a/Content.Shared/Procedural/DungeonLayers/RoofDunGen.cs b/Content.Shared/Procedural/DungeonLayers/RoofDunGen.cs new file mode 100644 index 0000000000..fbb174dc14 --- /dev/null +++ b/Content.Shared/Procedural/DungeonLayers/RoofDunGen.cs @@ -0,0 +1,15 @@ +using Robust.Shared.Noise; + +namespace Content.Shared.Procedural.DungeonLayers; + +/// +/// Sets tiles as rooved. +/// +public sealed partial class RoofDunGen : IDunGenLayer +{ + [DataField] + public float Threshold = -1f; + + [DataField] + public FastNoiseLite? Noise; +} diff --git a/Content.Shared/Procedural/PostGeneration/RoomEntranceDunGen.cs b/Content.Shared/Procedural/DungeonLayers/RoomEntranceDunGen.cs similarity index 93% rename from Content.Shared/Procedural/PostGeneration/RoomEntranceDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/RoomEntranceDunGen.cs index 1436f7473d..f0fea57588 100644 --- a/Content.Shared/Procedural/PostGeneration/RoomEntranceDunGen.cs +++ b/Content.Shared/Procedural/DungeonLayers/RoomEntranceDunGen.cs @@ -1,6 +1,5 @@ using Content.Shared.EntityTable; using Content.Shared.Maps; -using Content.Shared.Storage; using Robust.Shared.Prototypes; namespace Content.Shared.Procedural.PostGeneration; diff --git a/Content.Shared/Procedural/DungeonLayers/SampleDecalDunGen.cs b/Content.Shared/Procedural/DungeonLayers/SampleDecalDunGen.cs new file mode 100644 index 0000000000..616e643b52 --- /dev/null +++ b/Content.Shared/Procedural/DungeonLayers/SampleDecalDunGen.cs @@ -0,0 +1,36 @@ +using Content.Shared.Decals; +using Content.Shared.Maps; +using Robust.Shared.Noise; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Procedural.DungeonLayers; + +public sealed partial class SampleDecalDunGen : IDunGenLayer +{ + /// + /// Reserve any tiles we update. + /// + [DataField] + public bool ReserveTiles = true; + + [DataField(customTypeSerializer:typeof(PrototypeIdListSerializer))] + public List AllowedTiles { get; private set; } = new(); + + /// + /// Divide each tile up by this amount. + /// + [DataField] + public float Divisions = 1f; + + [DataField] + public FastNoiseLite Noise { get; private set; } = new(0); + + [DataField] + public float Threshold { get; private set; } = 0.8f; + + [DataField] public bool Invert { get; private set; } = false; + + [DataField(required: true)] + public List> Decals = new(); +} diff --git a/Content.Shared/Procedural/DungeonLayers/SampleEntityDunGen.cs b/Content.Shared/Procedural/DungeonLayers/SampleEntityDunGen.cs new file mode 100644 index 0000000000..2daf7e7aa7 --- /dev/null +++ b/Content.Shared/Procedural/DungeonLayers/SampleEntityDunGen.cs @@ -0,0 +1,31 @@ +using Content.Shared.Maps; +using Robust.Shared.Noise; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Procedural.DungeonLayers; + +/// +/// Samples noise to spawn the specified entity +/// +public sealed partial class SampleEntityDunGen : IDunGenLayer +{ + /// + /// Reserve any tiles we update. + /// + [DataField] + public bool ReserveTiles = true; + + [DataField(customTypeSerializer:typeof(PrototypeIdListSerializer))] + public List AllowedTiles { get; private set; } = new(); + + [DataField] public FastNoiseLite Noise { get; private set; } = new(0); + + [DataField] + public float Threshold { get; private set; } = 0.5f; + + [DataField] public bool Invert { get; private set; } = false; + + [DataField] + public List Entities = new(); +} diff --git a/Content.Shared/Parallax/Biomes/Layers/BiomeTileLayer.cs b/Content.Shared/Procedural/DungeonLayers/SampleTileDunGen.cs similarity index 66% rename from Content.Shared/Parallax/Biomes/Layers/BiomeTileLayer.cs rename to Content.Shared/Procedural/DungeonLayers/SampleTileDunGen.cs index 9dee35da4e..6d23d201f5 100644 --- a/Content.Shared/Parallax/Biomes/Layers/BiomeTileLayer.cs +++ b/Content.Shared/Procedural/DungeonLayers/SampleTileDunGen.cs @@ -2,20 +2,26 @@ using Content.Shared.Maps; using Robust.Shared.Noise; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Shared.Parallax.Biomes.Layers; +namespace Content.Shared.Procedural.DungeonLayers; +/// +/// Samples noise and spawns the specified tile in the dungeon area. +/// [Serializable, NetSerializable] -public sealed partial class BiomeTileLayer : IBiomeLayer +public sealed partial class SampleTileDunGen : IDunGenLayer { + /// + /// Reserve any tiles we update. + /// + [DataField] + public bool ReserveTiles = true; + [DataField] public FastNoiseLite Noise { get; private set; } = new(0); - /// [DataField] public float Threshold { get; private set; } = 0.5f; - /// [DataField] public bool Invert { get; private set; } = false; /// diff --git a/Content.Shared/Procedural/PostGeneration/SplineDungeonConnectorDunGen.cs b/Content.Shared/Procedural/DungeonLayers/SplineDungeonConnectorDunGen.cs similarity index 83% rename from Content.Shared/Procedural/PostGeneration/SplineDungeonConnectorDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/SplineDungeonConnectorDunGen.cs index d2f5a2126a..4a4bc0b36a 100644 --- a/Content.Shared/Procedural/PostGeneration/SplineDungeonConnectorDunGen.cs +++ b/Content.Shared/Procedural/DungeonLayers/SplineDungeonConnectorDunGen.cs @@ -1,7 +1,7 @@ using Content.Shared.Maps; using Robust.Shared.Prototypes; -namespace Content.Shared.Procedural.PostGeneration; +namespace Content.Shared.Procedural.DungeonLayers; /// /// Connects dungeons via points that get subdivided. @@ -18,11 +18,11 @@ public sealed partial class SplineDungeonConnectorDunGen : IDunGenLayer /// Will divide the distance between the start and end points so that no subdivision is more than these metres away. /// [DataField] - public int DivisionDistance = 10; + public int DivisionDistance = 20; /// /// How much each subdivision can vary from the middle. /// [DataField] - public float VarianceMax = 0.35f; + public float VarianceMax = 0.15f; } diff --git a/Content.Shared/Procedural/PostGeneration/WallMountDunGen.cs b/Content.Shared/Procedural/DungeonLayers/WallMountDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/WallMountDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/WallMountDunGen.cs diff --git a/Content.Shared/Procedural/PostGeneration/WormCorridorDunGen.cs b/Content.Shared/Procedural/DungeonLayers/WormCorridorDunGen.cs similarity index 100% rename from Content.Shared/Procedural/PostGeneration/WormCorridorDunGen.cs rename to Content.Shared/Procedural/DungeonLayers/WormCorridorDunGen.cs diff --git a/Content.Shared/Procedural/Loot/BiomeLoot.cs b/Content.Shared/Procedural/Loot/BiomeLoot.cs new file mode 100644 index 0000000000..1330043493 --- /dev/null +++ b/Content.Shared/Procedural/Loot/BiomeLoot.cs @@ -0,0 +1,12 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Procedural.Loot; + +/// +/// Adds the prototype as a biome layer. +/// +public sealed partial class BiomeLoot : IDungeonLoot +{ + [DataField(required: true)] + public ProtoId Proto; +} diff --git a/Content.Shared/Procedural/Loot/BiomeMarkerLoot.cs b/Content.Shared/Procedural/Loot/BiomeMarkerLoot.cs deleted file mode 100644 index 2eda4b059c..0000000000 --- a/Content.Shared/Procedural/Loot/BiomeMarkerLoot.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Content.Shared.Parallax.Biomes.Markers; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; - -namespace Content.Shared.Procedural.Loot; - -/// -/// Adds a biome marker layer for dungeon loot. -/// -public sealed partial class BiomeMarkerLoot : IDungeonLoot -{ - [DataField("proto", required: true)] - public ProtoId Prototype = new(); -} diff --git a/Content.Shared/Procedural/Loot/BiomeTemplateLoot.cs b/Content.Shared/Procedural/Loot/BiomeTemplateLoot.cs deleted file mode 100644 index e4968b6e42..0000000000 --- a/Content.Shared/Procedural/Loot/BiomeTemplateLoot.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Content.Shared.Parallax.Biomes; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Procedural.Loot; - -/// -/// Adds a biome template layer for dungeon loot. -/// -public sealed partial class BiomeTemplateLoot : IDungeonLoot -{ - [DataField("proto", required: true)] - public ProtoId Prototype = string.Empty; -} diff --git a/Content.Shared/Procedural/PostGeneration/BiomeDunGen.cs b/Content.Shared/Procedural/PostGeneration/BiomeDunGen.cs deleted file mode 100644 index e21e582211..0000000000 --- a/Content.Shared/Procedural/PostGeneration/BiomeDunGen.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Content.Shared.Maps; -using Content.Shared.Parallax.Biomes; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Procedural.PostGeneration; - -/// -/// Generates a biome on top of valid tiles, then removes the biome when done. -/// Only works if no existing biome is present. -/// -public sealed partial class BiomeDunGen : IDunGenLayer -{ - [DataField(required: true)] - public ProtoId BiomeTemplate; - - /// - /// creates a biome only on the specified tiles - /// - [DataField] - public HashSet>? TileMask; -} diff --git a/Content.Shared/Procedural/PostGeneration/BiomeMarkerLayerDunGen.cs b/Content.Shared/Procedural/PostGeneration/BiomeMarkerLayerDunGen.cs deleted file mode 100644 index af5d7c5d8f..0000000000 --- a/Content.Shared/Procedural/PostGeneration/BiomeMarkerLayerDunGen.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Content.Shared.Random; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Procedural.PostGeneration; - -/// -/// Spawns the specified marker layer on top of the dungeon rooms. -/// -public sealed partial class BiomeMarkerLayerDunGen : IDunGenLayer -{ - /// - /// How many times to spawn marker layers; can duplicate. - /// - [DataField] - public int Count = 6; - - [DataField(required: true)] - public ProtoId MarkerTemplate; -} diff --git a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeModPrototype.cs b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeModPrototype.cs index e84223ed1f..10106cd666 100644 --- a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeModPrototype.cs +++ b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeModPrototype.cs @@ -1,6 +1,4 @@ -using Content.Shared.Parallax.Biomes; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Salvage.Expeditions.Modifiers; @@ -26,6 +24,6 @@ public sealed partial class SalvageBiomeModPrototype : IPrototype, ISalvageMod [DataField("weather")] public bool Weather = true; - [DataField("biome", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] - public string? BiomePrototype; + [DataField("biome", required: true)] + public EntProtoId? BiomePrototype; } diff --git a/Resources/Locale/en-US/procedural/biome.ftl b/Resources/Locale/en-US/procedural/biome.ftl index d24ec7d72e..7c2a88a696 100644 --- a/Resources/Locale/en-US/procedural/biome.ftl +++ b/Resources/Locale/en-US/procedural/biome.ftl @@ -1,6 +1,2 @@ -cmd-biome_clear-desc = Clears a biome entirely -cmd-biome_clear-help = biome_clear cmd-biome_addlayer-desc = Adds another biome layer cmd-biome_addlayer-help = biome_addlayer [seed offset] -cmd-biome_addmarkerlayer-desc = Adds another biome marker layer -cmd-biome_addmarkerlayer-help = biome_addmarkerlayer diff --git a/Resources/Prototypes/Entities/Tiles/water.yml b/Resources/Prototypes/Entities/Tiles/water.yml index c488df231b..357bc9f98c 100644 --- a/Resources/Prototypes/Entities/Tiles/water.yml +++ b/Resources/Prototypes/Entities/Tiles/water.yml @@ -17,10 +17,14 @@ drawdepth: BelowFloor layers: - state: shoreline_water + - type: BiomeForceUnload + #- type: ContainerContainer + # containers: + # solution@pool: !type:ContainerSlot - type: SolutionContainerManager solutions: pool: - maxVol: 9999999 #.inf seems to break the whole yaml file, but would definitely be preferable. + maxVol: 9999999 #.inf seems to break the whole yaml file, but would definitely be preferable. reagents: - ReagentId: Water Quantity: 9999999 diff --git a/Resources/Prototypes/Procedural/Magnet/asteroid.yml b/Resources/Prototypes/Procedural/Magnet/asteroid.yml index a77a6b287b..f838104f3c 100644 --- a/Resources/Prototypes/Procedural/Magnet/asteroid.yml +++ b/Resources/Prototypes/Procedural/Magnet/asteroid.yml @@ -18,8 +18,9 @@ lacunarity: 2 # Generate biome - - !type:BiomeDunGen - biomeTemplate: Asteroid + - !type:PrototypeDunGen + proto: Asteroid + inheritDungeons: All - !type:OreDunGen replacement: AsteroidRock entity: AsteroidRockGibtonite @@ -47,8 +48,9 @@ lacunarity: 2 # Generate biome - - !type:BiomeDunGen - biomeTemplate: Asteroid + - !type:PrototypeDunGen + proto: Asteroid + inheritDungeons: All - !type:OreDunGen replacement: AsteroidRock entity: AsteroidRockGibtonite @@ -76,8 +78,9 @@ cellularDistanceFunction: Euclidean # Generate biome - - !type:BiomeDunGen - biomeTemplate: Asteroid + - !type:PrototypeDunGen + proto: Asteroid + inheritDungeons: All - !type:OreDunGen replacement: AsteroidRock entity: AsteroidRockGibtonite @@ -104,8 +107,9 @@ lacunarity: 2 # Generate biome - - !type:BiomeDunGen - biomeTemplate: Asteroid + - !type:PrototypeDunGen + proto: Asteroid + inheritDungeons: All - !type:OreDunGen replacement: AsteroidRock entity: AsteroidRockGibtonite diff --git a/Resources/Prototypes/Procedural/Magnet/space_debris.yml b/Resources/Prototypes/Procedural/Magnet/space_debris.yml index 11c1afdabe..a9bd823e6b 100644 --- a/Resources/Prototypes/Procedural/Magnet/space_debris.yml +++ b/Resources/Prototypes/Procedural/Magnet/space_debris.yml @@ -36,8 +36,9 @@ gain: 0.5 # Generate biome - - !type:BiomeDunGen - biomeTemplate: SpaceDebris + - !type:PrototypeDunGen + proto: SpaceDebris + inheritDungeons: All - type: dungeonConfig id: ChunkDebrisSmall @@ -77,5 +78,6 @@ gain: 0.5 # Generate biome - - !type:BiomeDunGen - biomeTemplate: SpaceDebris + - !type:PrototypeDunGen + proto: SpaceDebris + inheritDungeons: All diff --git a/Resources/Prototypes/Procedural/Magnet/space_debris_templates.yml b/Resources/Prototypes/Procedural/Magnet/space_debris_templates.yml index 47aa47fce4..e181f89268 100644 --- a/Resources/Prototypes/Procedural/Magnet/space_debris_templates.yml +++ b/Resources/Prototypes/Procedural/Magnet/space_debris_templates.yml @@ -1,8 +1,8 @@ # Asteroid -- type: biomeTemplate +- type: dungeonConfig id: SpaceDebris layers: - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen threshold: 0.20 noise: seed: 0 @@ -23,7 +23,7 @@ - WallReinforced - WallSolid - WallSolid - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen threshold: 0.5 noise: seed: 0 @@ -41,7 +41,7 @@ - Grille - Grille - GrilleBroken - - !type:BiomeDecalLayer + - !type:SampleDecalDunGen allowedTiles: - FloorSteel threshold: -0.5 @@ -56,7 +56,7 @@ - DirtMedium - DirtMedium - DirtLight - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen threshold: 0.45 noise: seed: 1 @@ -71,7 +71,7 @@ - FloorSteel entities: - SalvageSpawnerStructuresVarious - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen allowedTiles: - FloorSteel - Plating @@ -85,7 +85,7 @@ - SalvageSpawnerScrapCommon - SalvageSpawnerScrapCommon - SalvageSpawnerScrapCommon75 - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen allowedTiles: - FloorSteel - Plating @@ -97,7 +97,7 @@ - SalvageSpawnerTreasure - SalvageSpawnerTreasure - SalvageSpawnerTreasureValuable - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen allowedTiles: - FloorSteel - Plating diff --git a/Resources/Prototypes/Procedural/biome_markers.yml b/Resources/Prototypes/Procedural/biome_markers.yml index 5c4fdeb178..ef28b314ca 100644 --- a/Resources/Prototypes/Procedural/biome_markers.yml +++ b/Resources/Prototypes/Procedural/biome_markers.yml @@ -1,67 +1,122 @@ -- type: biomeMarkerLayer +- type: dungeonConfig id: Lizards - prototype: MobLizard - minGroupSize: 3 - maxGroupSize: 5 + layers: + - !type:ChunkDunGen + size: 128 + - !type:EntityTableDunGen + table: !type:EntSelector + id: MobLizard + amount: !type:RangeNumberSelector + range: 3, 5 -- type: biomeMarkerLayer + +- type: dungeonConfig id: WatchersLavaland - prototype: MobWatcherLavaland - minGroupSize: 3 - maxGroupSize: 3 + layers: + - !type:ChunkDunGen + size: 128 + - !type:EntityTableDunGen + table: !type:EntSelector + id: MobWatcherLavaland + amount: !type:RangeNumberSelector + range: 3, 3 -- type: biomeMarkerLayer +- type: dungeonConfig id: WatchersIcewing - prototype: MobWatcherIcewing - minGroupSize: 3 - maxGroupSize: 3 + layers: + - !type:ChunkDunGen + size: 128 + - !type:EntityTableDunGen + table: !type:EntSelector + id: MobWatcherIcewing + amount: !type:RangeNumberSelector + range: 3, 3 -- type: biomeMarkerLayer +- type: dungeonConfig id: WatchersMagmawing - prototype: MobWatcherMagmawing - minGroupSize: 3 - maxGroupSize: 3 + layers: + - !type:ChunkDunGen + size: 128 + - !type:EntityTableDunGen + table: !type:EntSelector + id: MobWatcherMagmawing + amount: !type:RangeNumberSelector + range: 3, 3 -- type: biomeMarkerLayer +- type: dungeonConfig id: Cows - prototype: MobCow - minGroupSize: 1 - maxGroupSize: 2 + layers: + - !type:ChunkDunGen + size: 128 + - !type:EntityTableDunGen + table: !type:EntSelector + id: MobCow + amount: !type:RangeNumberSelector + range: 1, 2 -- type: biomeMarkerLayer +- type: dungeonConfig id: Chickens - prototype: MobChicken - minGroupSize: 1 - maxGroupSize: 2 + layers: + - !type:ChunkDunGen + size: 128 + - !type:EntityTableDunGen + table: !type:EntSelector + id: MobChicken + amount: !type:RangeNumberSelector + range: 1, 2 -- type: biomeMarkerLayer +- type: dungeonConfig id: Pigs - prototype: MobPig - minGroupSize: 1 - maxGroupSize: 2 + layers: + - !type:ChunkDunGen + size: 128 + - !type:EntityTableDunGen + table: !type:EntSelector + id: MobPig + amount: !type:RangeNumberSelector + range: 1, 2 -- type: biomeMarkerLayer +- type: dungeonConfig id: Foxes - prototype: MobFox - minGroupSize: 1 - maxGroupSize: 1 + layers: + - !type:ChunkDunGen + size: 128 + - !type:EntityTableDunGen + table: !type:EntSelector + id: MobFox + amount: !type:RangeNumberSelector + range: 1, 1 -- type: biomeMarkerLayer +- type: dungeonConfig id: Goats - prototype: MobGoat - minGroupSize: 1 - maxGroupSize: 1 + layers: + - !type:ChunkDunGen + size: 128 + - !type:EntityTableDunGen + table: !type:EntSelector + id: MobGoat + amount: !type:RangeNumberSelector + range: 1, 1 -# TODO: Needs to be more robust -- type: biomeMarkerLayer - id: Xenos - prototype: MobXeno - -- type: biomeMarkerLayer +- type: dungeonConfig id: Carps - prototype: MobCarpDungeon + layers: + - !type:ChunkDunGen + size: 128 + - !type:EntityTableDunGen + table: !type:EntSelector + id: MobCarp + amount: !type:RangeNumberSelector + range: 1, 2 +- type: dungeonConfig + id: Xenos + layers: + - !type:ChunkDunGen + size: 128 + - !type:EntityTableDunGen + table: !type:EntSelector + id: MobXeno + amount: !type:RangeNumberSelector + range: 1, 2 -#- type: biomeMarkerLayer -# id: Experiment -# proto: DungeonMarkerExperiment diff --git a/Resources/Prototypes/Procedural/biome_ore_templates.yml b/Resources/Prototypes/Procedural/biome_ore_templates.yml index ae033230b6..16b2c7e64f 100644 --- a/Resources/Prototypes/Procedural/biome_ore_templates.yml +++ b/Resources/Prototypes/Procedural/biome_ore_templates.yml @@ -1,146 +1,127 @@ # Low value -- type: biomeMarkerLayer +- type: dungeonConfig id: OreIron - entityMask: - AsteroidRock: AsteroidRockTin - WallRock: WallRockTin - WallRockBasalt: WallRockBasaltTin - WallRockChromite: WallRockChromiteTin - WallRockSand: WallRockSandTin - WallRockSnow: WallRockSnowTin - maxCount: 30 - minGroupSize: 10 - maxGroupSize: 15 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockTin + count: 30 + minGroupSize: 10 + maxGroupSize: 15 -- type: biomeMarkerLayer +- type: dungeonConfig id: OreQuartz - entityMask: - AsteroidRock: AsteroidRockQuartz - WallRock: WallRockQuartz - WallRockBasalt: WallRockBasaltQuartz - WallRockChromite: WallRockChromiteQuartz - WallRockSnow: WallRockSnowQuartz - maxCount: 30 - minGroupSize: 10 - maxGroupSize: 15 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockQuartz + count: 30 + minGroupSize: 10 + maxGroupSize: 15 -- type: biomeMarkerLayer +- type: dungeonConfig id: OreCoal - entityMask: - AsteroidRock: AsteroidRockCoal - WallRock: WallRockCoal - WallRockBasalt: WallRockBasaltCoal - WallRockChromite: WallRockChromiteCoal - WallRockSand: WallRockSandCoal - WallRockSnow: WallRockSnowCoal - maxCount: 30 - minGroupSize: 8 - maxGroupSize: 12 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockCoal + count: 30 + minGroupSize: 8 + maxGroupSize: 12 -- type: biomeMarkerLayer +- type: dungeonConfig id: OreSalt - entityMask: - AsteroidRock: AsteroidRockSalt - WallRock: WallRockSalt - WallRockBasalt: WallRockBasaltSalt - WallRockChromite: WallRockChromiteSalt - WallRockSand: WallRockSandSalt - WallRockSnow: WallRockSnowSalt - maxCount: 30 - minGroupSize: 8 - maxGroupSize: 12 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockSalt + count: 30 + minGroupSize: 8 + maxGroupSize: 12 # Medium value # Gold -- type: biomeMarkerLayer +- type: dungeonConfig id: OreGold - entityMask: - AsteroidRock: AsteroidRockGold - WallRock: WallRockGold - WallRockBasalt: WallRockBasaltGold - WallRockChromite: WallRockChromiteGold - WallRockSand: WallRockSandGold - WallRockSnow: WallRockSnowGold - maxCount: 20 - minGroupSize: 5 - maxGroupSize: 10 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockGold + count: 20 + minGroupSize: 5 + maxGroupSize: 10 # Silver -- type: biomeMarkerLayer +- type: dungeonConfig id: OreSilver - entityMask: - AsteroidRock: AsteroidRockSilver - WallRock: WallRockSilver - WallRockBasalt: WallRockBasaltSilver - WallRockChromite: WallRockChromiteSilver - WallRockSand: WallRockSandSilver - WallRockSnow: WallRockSnowSilver - maxCount: 20 - minGroupSize: 5 - maxGroupSize: 10 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockSilver + count: 20 + minGroupSize: 5 + maxGroupSize: 10 # High value # Plasma -- type: biomeMarkerLayer +- type: dungeonConfig id: OrePlasma - entityMask: - AsteroidRock: AsteroidRockPlasma - WallRock: WallRockPlasma - WallRockBasalt: WallRockBasaltPlasma - WallRockChromite: WallRockChromitePlasma - WallRockSand: WallRockSandPlasma - WallRockSnow: WallRockSnowPlasma - maxCount: 12 - minGroupSize: 4 - maxGroupSize: 8 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockPlasma + count: 12 + minGroupSize: 4 + maxGroupSize: 8 # Uranium -- type: biomeMarkerLayer +- type: dungeonConfig id: OreUranium - entityMask: - AsteroidRock: AsteroidRockUranium - WallRock: WallRockUranium - WallRockBasalt: WallRockBasaltUranium - WallRockChromite: WallRockChromiteUranium - WallRockSand: WallRockSandUranium - WallRockSnow: WallRockSnowUranium - maxCount: 15 - minGroupSize: 4 - maxGroupSize: 8 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockUranium + count: 15 + minGroupSize: 4 + maxGroupSize: 8 -- type: biomeMarkerLayer +- type: dungeonConfig id: OreDiamond - entityMask: - AsteroidRock: AsteroidRockDiamond - WallRock: WallRockDiamond - WallRockBasalt: WallRockBasaltDiamond - WallRockChromite: WallRockChromiteDiamond - WallRockSand: WallRockSandDiamond - WallRockSnow: WallRockSnowDiamond - maxCount: 6 - minGroupSize: 1 - maxGroupSize: 2 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockDiamond + count: 6 + minGroupSize: 1 + maxGroupSize: 2 # Artifact Fragment -- type: biomeMarkerLayer +- type: dungeonConfig id: OreArtifactFragment - entityMask: - AsteroidRock: AsteroidRockArtifactFragment - WallRock: WallRockArtifactFragment - WallRockBasalt: WallRockBasaltArtifactFragment - WallRockChromite: WallRockChromiteArtifactFragment - WallRockSand: WallRockSandArtifactFragment - WallRockSnow: WallRockSnowArtifactFragment - maxCount: 6 - minGroupSize: 1 - maxGroupSize: 2 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockArtifactFragment + count: 6 + minGroupSize: 1 + maxGroupSize: 2 diff --git a/Resources/Prototypes/Procedural/biome_ore_templates_low.yml b/Resources/Prototypes/Procedural/biome_ore_templates_low.yml index 3b0e242446..9af10e3c2b 100644 --- a/Resources/Prototypes/Procedural/biome_ore_templates_low.yml +++ b/Resources/Prototypes/Procedural/biome_ore_templates_low.yml @@ -1,146 +1,127 @@ # Low value -- type: biomeMarkerLayer +- type: dungeonConfig id: OreIronLow - entityMask: - AsteroidRock: AsteroidRockTin - WallRock: WallRockTin - WallRockBasalt: WallRockBasaltTin - WallRockChromite: WallRockChromiteTin - WallRockSand: WallRockSandTin - WallRockSnow: WallRockSnowTin - maxCount: 20 - minGroupSize: 4 - maxGroupSize: 8 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockTin + count: 20 + minGroupSize: 4 + maxGroupSize: 8 -- type: biomeMarkerLayer +- type: dungeonConfig id: OreQuartzLow - entityMask: - AsteroidRock: AsteroidRockQuartz - WallRock: WallRockQuartz - WallRockBasalt: WallRockBasaltQuartz - WallRockChromite: WallRockChromiteQuartz - WallRockSnow: WallRockSnowQuartz - maxCount: 20 - minGroupSize: 4 - maxGroupSize: 8 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockQuartz + count: 20 + minGroupSize: 4 + maxGroupSize: 8 -- type: biomeMarkerLayer +- type: dungeonConfig id: OreCoalLow - entityMask: - AsteroidRock: AsteroidRockCoal - WallRock: WallRockCoal - WallRockBasalt: WallRockBasaltCoal - WallRockChromite: WallRockChromiteCoal - WallRockSand: WallRockSandCoal - WallRockSnow: WallRockSnowCoal - maxCount: 20 - minGroupSize: 4 - maxGroupSize: 6 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockCoal + count: 20 + minGroupSize: 4 + maxGroupSize: 6 -- type: biomeMarkerLayer +- type: dungeonConfig id: OreSaltLow - entityMask: - AsteroidRock: AsteroidRockSalt - WallRock: WallRockSalt - WallRockBasalt: WallRockBasaltSalt - WallRockChromite: WallRockChromiteSalt - WallRockSand: WallRockSandSalt - WallRockSnow: WallRockSnowSalt - maxCount: 15 - minGroupSize: 4 - maxGroupSize: 6 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockSalt + count: 15 + minGroupSize: 4 + maxGroupSize: 6 # Medium value # Gold -- type: biomeMarkerLayer +- type: dungeonConfig id: OreGoldLow - entityMask: - AsteroidRock: AsteroidRockGold - WallRock: WallRockGold - WallRockBasalt: WallRockBasaltGold - WallRockChromite: WallRockChromiteGold - WallRockSand: WallRockSandGold - WallRockSnow: WallRockSnowGold - maxCount: 10 - minGroupSize: 2 - maxGroupSize: 5 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockGold + count: 12 + minGroupSize: 2 + maxGroupSize: 5 # Silver -- type: biomeMarkerLayer +- type: dungeonConfig id: OreSilverLow - entityMask: - AsteroidRock: AsteroidRockSilver - WallRock: WallRockSilver - WallRockBasalt: WallRockBasaltSilver - WallRockChromite: WallRockChromiteSilver - WallRockSand: WallRockSandSilver - WallRockSnow: WallRockSnowSilver - maxCount: 10 - minGroupSize: 2 - maxGroupSize: 5 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockSilver + count: 12 + minGroupSize: 2 + maxGroupSize: 5 # High value # Plasma -- type: biomeMarkerLayer +- type: dungeonConfig id: OrePlasmaLow - entityMask: - AsteroidRock: AsteroidRockPlasma - WallRock: WallRockPlasma - WallRockBasalt: WallRockBasaltPlasma - WallRockChromite: WallRockChromitePlasma - WallRockSand: WallRockSandPlasma - WallRockSnow: WallRockSnowPlasma - maxCount: 6 - minGroupSize: 2 - maxGroupSize: 4 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockPlasma + count: 6 + minGroupSize: 2 + maxGroupSize: 4 # Uranium -- type: biomeMarkerLayer +- type: dungeonConfig id: OreUraniumLow - entityMask: - AsteroidRock: AsteroidRockUranium - WallRock: WallRockUranium - WallRockBasalt: WallRockBasaltUranium - WallRockChromite: WallRockChromiteUranium - WallRockSand: WallRockSandUranium - WallRockSnow: WallRockSnowUranium - maxCount: 7 - minGroupSize: 2 - maxGroupSize: 4 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockUranium + count: 7 + minGroupSize: 2 + maxGroupSize: 4 -- type: biomeMarkerLayer +- type: dungeonConfig id: OreDiamondLow - entityMask: - AsteroidRock: AsteroidRockDiamond - WallRock: WallRockDiamond - WallRockBasalt: WallRockBasaltDiamond - WallRockChromite: WallRockChromiteDiamond - WallRockSand: WallRockSandDiamond - WallRockSnow: WallRockSnowDiamond - maxCount: 3 - minGroupSize: 1 - maxGroupSize: 2 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockDiamond + count: 3 + minGroupSize: 1 + maxGroupSize: 2 # Artifact Fragment -- type: biomeMarkerLayer +- type: dungeonConfig id: OreArtifactFragmentLow - entityMask: - AsteroidRock: AsteroidRockArtifactFragment - WallRock: WallRockArtifactFragment - WallRockBasalt: WallRockBasaltArtifactFragment - WallRockChromite: WallRockChromiteArtifactFragment - WallRockSand: WallRockSandArtifactFragment - WallRockSnow: WallRockSnowArtifactFragment - maxCount: 3 - minGroupSize: 1 - maxGroupSize: 2 - radius: 4 + layers: + - !type:ChunkDunGen + size: 128 + - !type:OreDunGen + replacement: WallRock + entity: WallRockArtifactFragment + count: 3 + minGroupSize: 1 + maxGroupSize: 2 diff --git a/Resources/Prototypes/Procedural/biome_templates.yml b/Resources/Prototypes/Procedural/biome_templates.yml index d630ebad48..747660dc8f 100644 --- a/Resources/Prototypes/Procedural/biome_templates.yml +++ b/Resources/Prototypes/Procedural/biome_templates.yml @@ -1,435 +1,551 @@ -# Contains several biomes -- type: biomeTemplate - id: Continental - layers: - - !type:BiomeMetaLayer - template: Lava - - !type:BiomeMetaLayer - template: Caves - threshold: -0.5 - noise: - frequency: 0.001 - noiseType: OpenSimplex2 - fractalType: FBm - octaves: 2 - lacunarity: 2 - - !type:BiomeMetaLayer - template: Grasslands - threshold: 0 - noise: - frequency: 0.001 - noiseType: OpenSimplex2 - fractalType: FBm - octaves: 2 - lacunarity: 2 - - !type:BiomeMetaLayer - template: Snow - threshold: 0.5 - noise: - frequency: 0.001 - noiseType: OpenSimplex2 - fractalType: FBm - octaves: 2 - lacunarity: 2 - # Desert -# TODO: Water in desert -- type: biomeTemplate - id: LowDesert - layers: - - !type:BiomeEntityLayer - threshold: 0.95 - noise: - seed: 0 - frequency: 2 - noiseType: OpenSimplex2 - allowedTiles: - - FloorAsteroidSand - entities: - - FloraRockSolid - # Large rock areas - - !type:BiomeEntityLayer - threshold: -0.20 - noise: - seed: 0 - frequency: 0.04 - noiseType: Cellular - fractalType: FBm - octaves: 5 - lacunarity: 2 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - allowedTiles: - - FloorAsteroidSand - entities: - - WallRockSand - - !type:BiomeDummyLayer - id: Loot - # Fill layer - - !type:BiomeTileLayer - threshold: -1 - tile: FloorAsteroidSand +- type: entity + id: BiomeLowDesert + categories: [ HideSpawnMenu ] + components: + - type: Biome + layers: + terrain: + dungeon: LowDesertTerrain -# Grass -- type: biomeTemplate - id: Grasslands +- type: dungeonConfig + id: LowDesertTerrain layers: - # Sparse vegetation - - !type:BiomeDecalLayer - allowedTiles: - - FloorPlanetGrass - divisions: 2 - threshold: -0.50 - noise: - seed: 0 - noiseType: Cellular - frequency: 1 - decals: - - BushDOne - - BushDTwo - - BushDThree - - !type:BiomeDecalLayer - allowedTiles: - - FloorPlanetGrass - noise: - seed: 0 - noiseType: OpenSimplex2 - frequency: 1 - divisions: 1 - threshold: 0.8 - decals: - - FlowersBROne - - FlowersBRTwo - - FlowersBRThree - # Dense vegetation - - !type:BiomeDecalLayer - allowedTiles: - - FloorPlanetGrass - divisions: 1 - threshold: -0.35 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.2 - fractalType: FBm - octaves: 5 - lacunarity: 2 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - decals: - - BushAOne - - BushATwo - - BushAThree - - BushCOne - - BushCTwo - - BushCThree - - !type:BiomeEntityLayer - threshold: 0.5 - noise: - seed: 0 - noiseType: OpenSimplex2 - fractalType: FBm - frequency: 2 - allowedTiles: - - FloorPlanetGrass - entities: - - FloraTree - - FloraTreeLarge - # Rock formations - - !type:BiomeEntityLayer - allowedTiles: - - FloorPlanetGrass - - FloorPlanetDirt - threshold: -0.30 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.05 - lacunarity: 2 - fractalType: FBm - octaves: 5 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - entities: - - WallRock - - !type:BiomeDummyLayer - id: Loot - # Water - - !type:BiomeEntityLayer - allowedTiles: - - FloorPlanetGrass - - FloorPlanetDirt - threshold: 0.95 - noise: - seed: 3 - noiseType: OpenSimplex2 - frequency: 0.003 - lacunarity: 1.50 - fractalType: Ridged - octaves: 1 - entities: - - FloorWaterEntity - # Fill remainder with dirt. - - !type:BiomeTileLayer - threshold: -1.0 - tile: FloorPlanetDirt - - !type:BiomeTileLayer - threshold: -0.90 - tile: FloorPlanetGrass - noise: - seed: 0 - frequency: 0.02 - fractalType: None - # Water sand - - !type:BiomeTileLayer - tile: FloorPlanetDirt - threshold: 0.95 - noise: - seed: 3 - noiseType: OpenSimplex2 - frequency: 0.003 - lacunarity: 1.50 - fractalType: Ridged - octaves: 1 - # Rock formation sand - - !type:BiomeTileLayer - tile: FloorPlanetDirt - threshold: -0.30 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.05 - lacunarity: 2 - fractalType: FBm - octaves: 5 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 + - !type:ChunkDunGen + - !type:PrototypeDunGen + proto: LowDesertTiles + inheritDungeons: All + - !type:PrototypeDunGen + proto: LowDesertEntities + inheritDungeons: All + +- type: dungeonConfig + id: LowDesertTiles + returnReserved: false + layers: + - !type:SampleTileDunGen + threshold: -1 + tile: FloorAsteroidSand + +- type: dungeonConfig + id: LowDesertEntities + reserveTiles: true + layers: + - !type:SampleEntityDunGen + threshold: 0.95 + noise: + seed: 0 + frequency: 2 + noiseType: OpenSimplex2 + allowedTiles: + - FloorAsteroidSand + entities: + - FloraRockSolid + # Large rock areas + - !type:SampleEntityDunGen + threshold: -0.20 + noise: + seed: 0 + frequency: 0.04 + noiseType: Cellular + fractalType: FBm + octaves: 5 + lacunarity: 2 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + allowedTiles: + - FloorAsteroidSand + entities: + - WallRockSand + + +- type: entity + id: BiomeGrasslands + categories: [ HideSpawnMenu ] + components: + - type: Biome + layers: + terrain: + dungeon: GrasslandsTerrain + +- type: dungeonConfig + id: GrasslandsTerrain + layers: + - !type:ChunkDunGen + - !type:PrototypeDunGen + proto: GrasslandsTiles + inheritDungeons: All + - !type:PrototypeDunGen + proto: GrasslandsEntities + inheritDungeons: All + - !type:PrototypeDunGen + proto: GrasslandsDecals + inheritDungeons: All + +- type: dungeonConfig + id: GrasslandsTiles + returnReserved: false + layers: + # Water sand + - !type:SampleTileDunGen + tile: FloorPlanetDirt + threshold: 0.95 + noise: + seed: 3 + noiseType: OpenSimplex2 + frequency: 0.003 + lacunarity: 1.50 + fractalType: Ridged + octaves: 1 + # Rock formation sand + - !type:SampleTileDunGen + tile: FloorPlanetDirt + threshold: -0.30 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.05 + lacunarity: 2 + fractalType: FBm + octaves: 5 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + - !type:SampleTileDunGen + threshold: -0.80 + tile: FloorPlanetGrass + noise: + seed: 0 + noiseType: OpenSimplex2 + lacunarity: 1.50 + frequency: 0.02 + fractalType: None + octaves: 1 + # Fill remainder with dirt. + - !type:SampleTileDunGen + threshold: -1.0 + tile: FloorPlanetDirt + +- type: dungeonConfig + id: GrasslandsEntities + reserveTiles: true + layers: + # Water + - !type:SampleEntityDunGen + allowedTiles: + - FloorPlanetGrass + - FloorPlanetDirt + threshold: 0.95 + noise: + seed: 3 + noiseType: OpenSimplex2 + frequency: 0.003 + lacunarity: 1.50 + fractalType: Ridged + octaves: 1 + entities: + - FloorWaterEntity + # Rock formations + - !type:SampleEntityDunGen + allowedTiles: + - FloorPlanetGrass + - FloorPlanetDirt + threshold: -0.30 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.05 + lacunarity: 2 + fractalType: FBm + octaves: 5 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + entities: + - WallRock + - !type:SampleEntityDunGen + threshold: 0.5 + noise: + seed: 0 + noiseType: OpenSimplex2 + fractalType: FBm + frequency: 2 + allowedTiles: + - FloorPlanetGrass + entities: + - FloraTree + - FloraTreeLarge + +- type: dungeonConfig + id: GrasslandsDecals + returnReserved: false + layers: + # Dense vegetation + - !type:SampleDecalDunGen + allowedTiles: + - FloorPlanetGrass + divisions: 1 + threshold: -0.35 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.2 + fractalType: FBm + octaves: 5 + lacunarity: 2 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + decals: + - BushAOne + - BushATwo + - BushAThree + - BushCOne + - BushCTwo + - BushCThree + - !type:SampleDecalDunGen + allowedTiles: + - FloorPlanetGrass + noise: + seed: 0 + noiseType: OpenSimplex2 + frequency: 1 + divisions: 1 + threshold: 0.8 + decals: + - FlowersBROne + - FlowersBRTwo + - FlowersBRThree + # Sparse vegetation + - !type:SampleDecalDunGen + allowedTiles: + - FloorPlanetGrass + divisions: 2 + threshold: -0.50 + noise: + seed: 0 + noiseType: Cellular + frequency: 1 + decals: + - BushDOne + - BushDTwo + - BushDThree # Lava -- type: biomeTemplate - id: Lava +- type: entity + id: BiomeLava + categories: [ HideSpawnMenu ] + components: + - type: Biome + layers: + terrain: + dungeon: LavaTerrain + +- type: dungeonConfig + id: LavaTerrain layers: - - !type:BiomeEntityLayer - threshold: 0.9 - noise: - frequency: 1 - seed: 2 - allowedTiles: - - FloorBasalt - entities: - - BasaltOne - - BasaltTwo - - BasaltThree - - BasaltFour - - BasaltFive - - !type:BiomeDecalLayer - allowedTiles: - - FloorBasalt - threshold: 0.9 - divisions: 1 - noise: - seed: 1 - frequency: 1 - decals: - - Basalt1 - - Basalt2 - - Basalt3 - - Basalt4 - - Basalt5 - - Basalt6 - - Basalt7 - - Basalt8 - - Basalt9 - - !type:BiomeEntityLayer - threshold: 0.95 - noise: - seed: 0 - noiseType: OpenSimplex2 - frequency: 1 - allowedTiles: - - FloorBasalt - entities: - - FloraRockSolid - - !type:BiomeEntityLayer - threshold: 0.2 - noise: - seed: 0 - frequency: 0.02 - fractalType: FBm - octaves: 5 - lacunarity: 2 - gain: 0.4 - allowedTiles: - - FloorBasalt - entities: - - FloorLavaEntity - # Rock formations - - !type:BiomeEntityLayer - allowedTiles: - - FloorBasalt - threshold: -0.30 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.05 - lacunarity: 2 - fractalType: FBm - octaves: 5 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - entities: - - WallRockBasalt - - !type:BiomeDummyLayer - id: Loot - # Fill basalt - - !type:BiomeTileLayer - threshold: -1 - tile: FloorBasalt + - !type:ChunkDunGen + - !type:PrototypeDunGen + proto: LavaTiles + inheritDungeons: All + - !type:PrototypeDunGen + proto: LavaEntities + inheritDungeons: All + - !type:PrototypeDunGen + proto: LavaDecals + inheritDungeons: All + - !type:PrototypeDunGen + proto: LavaBasalt + inheritDungeons: All + +- type: dungeonConfig + id: LavaTiles + returnReserved: false + layers: + # Fill basalt + - !type:SampleTileDunGen + threshold: -1 + tile: FloorBasalt + +- type: dungeonConfig + id: LavaEntities + reserveTiles: true + layers: + - !type:SampleEntityDunGen + threshold: 0.2 + noise: + seed: 0 + frequency: 0.02 + fractalType: FBm + octaves: 5 + lacunarity: 2 + gain: 0.4 + allowedTiles: + - FloorBasalt + entities: + - FloorLavaEntity + # Rock formations + - !type:SampleEntityDunGen + allowedTiles: + - FloorBasalt + threshold: -0.30 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.05 + lacunarity: 2 + fractalType: FBm + octaves: 5 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + entities: + - WallRockBasalt + - !type:SampleEntityDunGen + threshold: 0.95 + noise: + seed: 0 + noiseType: OpenSimplex2 + frequency: 1 + allowedTiles: + - FloorBasalt + entities: + - FloraRockSolid + +- type: dungeonConfig + id: LavaDecals + returnReserved: false + layers: + - !type:SampleDecalDunGen + allowedTiles: + - FloorBasalt + threshold: 0.9 + divisions: 1 + noise: + seed: 1 + frequency: 1 + decals: + - Basalt1 + - Basalt2 + - Basalt3 + - Basalt4 + - Basalt5 + - Basalt6 + - Basalt7 + - Basalt8 + - Basalt9 + +- type: dungeonConfig + id: LavaBasalt + returnReserved: false + layers: + - !type:SampleEntityDunGen + threshold: 0.9 + noise: + frequency: 1 + seed: 2 + allowedTiles: + - FloorBasalt + entities: + - BasaltOne + - BasaltTwo + - BasaltThree + - BasaltFour + - BasaltFive # Snow -- type: biomeTemplate - id: Snow # Similar to Grasslands... but snow +- type: entity + id: BiomeSnow + categories: [ HideSpawnMenu ] + components: + - type: Biome + layers: + terrain: + dungeon: SnowTerrain + +- type: dungeonConfig + id: SnowTerrain layers: - # Sparse vegetation - - !type:BiomeDecalLayer - allowedTiles: - - FloorSnow - divisions: 2 - threshold: -0.50 - noise: - seed: 0 - noiseType: Cellular - frequency: 1 - decals: - - grasssnowa1 - - grasssnowa2 - - grasssnowa3 - - grasssnowb1 - - grasssnowb2 - - grasssnowb3 - - grasssnowc1 - - grasssnowc2 - - grasssnowc3 - # Dense, bland grass - - !type:BiomeDecalLayer - allowedTiles: - - FloorSnow - divisions: 1 - threshold: -0.35 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.2 - fractalType: FBm - octaves: 5 - lacunarity: 2 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - decals: - - grasssnow - - grasssnow01 - - grasssnow02 - - grasssnow03 - - grasssnow04 - - grasssnow05 - - grasssnow06 - - grasssnow07 - - grasssnow08 - - grasssnow09 - - grasssnow10 - - grasssnow11 - - grasssnow12 - - grasssnow13 - # Little bit of coloured grass - - !type:BiomeDecalLayer - allowedTiles: - - FloorSnow - divisions: 1 - threshold: -0.0 - noise: - seed: 0 - noiseType: Cellular - frequency: 1 - fractalType: None - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - decals: - - bushsnowa1 - - bushsnowa2 - - bushsnowa3 - - bushsnowb3 - - bushsnowb2 - - bushsnowb3 - - !type:BiomeEntityLayer - threshold: 0.5 - noise: - seed: 0 - noiseType: OpenSimplex2 - fractalType: FBm - frequency: 2 - allowedTiles: - - FloorSnow - entities: - - FloraTreeSnow - # Rock formations - - !type:BiomeEntityLayer - allowedTiles: - - FloorSnow - threshold: -0.30 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.05 - lacunarity: 2 - fractalType: FBm - octaves: 5 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - entities: - - WallRockSnow - # Ice tiles - - !type:BiomeTileLayer - tile: FloorIce - threshold: -0.9 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.03 - lacunarity: 2 - fractalType: FBm - octaves: 5 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - # Liquid plasma rivers. Ice moon baby - - !type:BiomeEntityLayer - allowedTiles: - - FloorSnow - - FloorIce - threshold: 0.95 - noise: - seed: 3 - noiseType: OpenSimplex2 - frequency: 0.003 - lacunarity: 1.50 - fractalType: Ridged - octaves: 1 - entities: - - FloorLiquidPlasmaEntity - - !type:BiomeDummyLayer - id: Loot - - !type:BiomeTileLayer - threshold: -0.7 - tile: FloorSnow - noise: - seed: 0 - frequency: 0.02 - fractalType: None + - !type:ChunkDunGen + - !type:PrototypeDunGen + proto: SnowTiles + inheritDungeons: All + - !type:PrototypeDunGen + proto: SnowEntities + inheritDungeons: All + - !type:PrototypeDunGen + proto: SnowDecals + inheritDungeons: All + +- type: dungeonConfig + id: SnowTiles + returnReserved: false + layers: + - !type:SampleTileDunGen + threshold: -0.7 + tile: FloorSnow + noise: + seed: 0 + frequency: 0.02 + fractalType: None + - !type:SampleTileDunGen + tile: FloorIce + threshold: -0.9 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.03 + lacunarity: 2 + fractalType: FBm + octaves: 5 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + + +- type: dungeonConfig + id: SnowEntities + reserveTiles: true + layers: + # Liquid plasma rivers. Ice moon baby + - !type:SampleEntityDunGen + allowedTiles: + - FloorSnow + - FloorIce + threshold: 0.95 + noise: + seed: 3 + noiseType: OpenSimplex2 + frequency: 0.003 + lacunarity: 1.50 + fractalType: Ridged + octaves: 1 + entities: + - FloorLiquidPlasmaEntity + # Rock formations + - !type:SampleEntityDunGen + allowedTiles: + - FloorSnow + threshold: -0.30 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.05 + lacunarity: 2 + fractalType: FBm + octaves: 5 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + entities: + - WallRockSnow + - !type:SampleEntityDunGen + threshold: 0.5 + noise: + seed: 0 + noiseType: OpenSimplex2 + fractalType: FBm + frequency: 2 + allowedTiles: + - FloorSnow + entities: + - FloraTreeSnow + +- type: dungeonConfig + id: SnowDecals + returnReserved: false + layers: + # Sparse vegetation + - !type:SampleDecalDunGen + allowedTiles: + - FloorSnow + divisions: 2 + threshold: -0.50 + noise: + seed: 0 + noiseType: Cellular + frequency: 1 + decals: + - grasssnowa1 + - grasssnowa2 + - grasssnowa3 + - grasssnowb1 + - grasssnowb2 + - grasssnowb3 + - grasssnowc1 + - grasssnowc2 + - grasssnowc3 + # Dense, bland grass + - !type:SampleDecalDunGen + allowedTiles: + - FloorSnow + divisions: 1 + threshold: -0.35 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.2 + fractalType: FBm + octaves: 5 + lacunarity: 2 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + decals: + - grasssnow + - grasssnow01 + - grasssnow02 + - grasssnow03 + - grasssnow04 + - grasssnow05 + - grasssnow06 + - grasssnow07 + - grasssnow08 + - grasssnow09 + - grasssnow10 + - grasssnow11 + - grasssnow12 + - grasssnow13 + # Little bit of coloured grass + - !type:SampleDecalDunGen + allowedTiles: + - FloorSnow + divisions: 1 + threshold: -0.0 + noise: + seed: 0 + noiseType: Cellular + frequency: 1 + fractalType: None + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + decals: + - bushsnowa1 + - bushsnowa2 + - bushsnowa3 + - bushsnowb3 + - bushsnowb2 + - bushsnowb3 # Shadow -> Derived from lava -- type: biomeTemplate - id: Shadow +- type: entity + id: BiomeShadow + categories: [ HideSpawnMenu ] + components: + - type: Biome + layers: + terrain: + dungeon: ShadowTerrain + +- type: dungeonConfig + id: ShadowTerrain layers: - - !type:BiomeEntityLayer + - !type:ChunkDunGen + - !type:PrototypeDunGen + proto: ShadowTiles + inheritDungeons: All + - !type:PrototypeDunGen + proto: ShadowEntities + inheritDungeons: All + +- type: dungeonConfig + id: ShadowEntities + reserveTiles: true + layers: + - !type:SampleEntityDunGen threshold: 0.70 noise: frequency: 1 @@ -442,7 +558,7 @@ - ShadowBasaltThree - ShadowBasaltFour - ShadowBasaltFive - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen threshold: 0.97 noise: frequency: 1 @@ -451,7 +567,7 @@ - FloorChromite entities: - CrystalPink - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen threshold: 0.97 noise: seed: 1 @@ -462,7 +578,7 @@ entities: - ShadowTree # Rock formations - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen threshold: -0.2 invert: true noise: @@ -477,7 +593,7 @@ entities: - WallRockChromite # chasm time - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen allowedTiles: - FloorChromite threshold: 0.2 @@ -490,18 +606,43 @@ gain: 0.4 entities: - FloorChromiteChasm - - !type:BiomeDummyLayer - id: Loot - # Fill chromite - - !type:BiomeTileLayer - threshold: -1 - tile: FloorChromite + +- type: dungeonConfig + id: ShadowTiles + returnReserved: false + layers: + # Fill chromite + - !type:SampleTileDunGen + threshold: -1 + tile: FloorChromite # Caves -- type: biomeTemplate - id: Caves +- type: entity + id: BiomeCaves + categories: [ HideSpawnMenu ] + components: + - type: Biome + layers: + terrain: + dungeon: CavesTerrain + +- type: dungeonConfig + id: CavesTerrain layers: - - !type:BiomeEntityLayer + - !type:ChunkDunGen + - !type:PrototypeDunGen + proto: CavesTiles + inheritDungeons: All + - !type:RoofDunGen + - !type:PrototypeDunGen + proto: CavesEntities + inheritDungeons: All + +- type: dungeonConfig + id: CavesEntities + reserveTiles: true + layers: + - !type:SampleEntityDunGen threshold: 0.85 noise: seed: 2 @@ -517,7 +658,7 @@ - CrystalBlue - CrystalYellow - CrystalCyan - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen threshold: 0.95 noise: seed: 1 @@ -527,7 +668,7 @@ - FloorAsteroidSand entities: - FloraStalagmite - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen threshold: -0.5 invert: true noise: @@ -541,17 +682,24 @@ - FloorAsteroidSand entities: - WallRock - - !type:BiomeDummyLayer - id: Loot - - !type:BiomeTileLayer - threshold: -1.0 - tile: FloorAsteroidSand + +- type: dungeonConfig + id: CavesTiles + returnReserved: false + layers: + - !type:SampleTileDunGen + threshold: -1.0 + tile: FloorAsteroidSand # Asteroid -- type: biomeTemplate +- type: dungeonConfig id: Asteroid layers: - - !type:BiomeEntityLayer + - !type:SampleTileDunGen + threshold: -1.0 + tile: FloorAsteroidSand + reserveTiles: false + - !type:SampleEntityDunGen threshold: 0.85 noise: seed: 2 @@ -567,7 +715,7 @@ - CrystalBlue - CrystalYellow - CrystalCyan - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen threshold: 0.95 noise: seed: 1 @@ -577,7 +725,8 @@ - FloorAsteroidSand entities: - FloraStalagmite - - !type:BiomeEntityLayer + - !type:SampleEntityDunGen + reserveTiles: false threshold: -0.6 invert: true noise: @@ -591,6 +740,3 @@ - FloorAsteroidSand entities: - AsteroidRock - - !type:BiomeTileLayer - threshold: -1.0 - tile: FloorAsteroidSand diff --git a/Resources/Prototypes/Procedural/dungeon_configs.yml b/Resources/Prototypes/Procedural/dungeon_configs.yml index 5da9592995..18d50ec6cd 100644 --- a/Resources/Prototypes/Procedural/dungeon_configs.yml +++ b/Resources/Prototypes/Procedural/dungeon_configs.yml @@ -79,6 +79,9 @@ id: Haunted layers: - !type:PrefabDunGen + roomWhitelist: + tags: + - Haunted presets: - Bucket - Wow diff --git a/Resources/Prototypes/Procedural/salvage_loot.yml b/Resources/Prototypes/Procedural/salvage_loot.yml index cf23601978..289fa5fd57 100644 --- a/Resources/Prototypes/Procedural/salvage_loot.yml +++ b/Resources/Prototypes/Procedural/salvage_loot.yml @@ -3,108 +3,108 @@ - type: salvageLoot id: SalvageLoot loots: - - !type:RandomSpawnsLoot - entries: - - proto: AdvMopItem - prob: 0.5 - - proto: AmmoTechFabCircuitboard - cost: 2 - - proto: AutolatheMachineCircuitboard - cost: 2 - - proto: BiomassReclaimerMachineCircuitboard - cost: 2 - - proto: BluespaceBeaker - cost: 2 - - proto: CyborgEndoskeleton - cost: 3 - prob: 0.5 - - proto: ChemDispenserMachineCircuitboard - cost: 2 - - proto: CircuitImprinter - cost: 2 - - proto: CloningConsoleComputerCircuitboard - cost: 2 - - proto: CloningPodMachineCircuitboard - cost: 2 - - proto: ChemistryBottleCognizine - - proto: FoodBoxDonkpocketCarp - prob: 0.5 - - proto: CrateSalvageEquipment - cost: 3 - prob: 0.5 - - proto: GasRecycler - cost: 2 - - proto: GeneratorRTG - cost: 5 - - proto: GravityGeneratorMini - cost: 2 - - proto: GyroscopeUnanchored - cost: 2 - prob: 0.1 - - proto: MedicalScannerMachineCircuitboard - cost: 2 - - proto: NuclearBombKeg - cost: 5 - - proto: ChemistryBottleOmnizine - prob: 0.5 - - proto: PortableGeneratorPacman - cost: 2 - - proto: PortableGeneratorSuperPacman - cost: 3 - - proto: PowerCellAntiqueProto - cost: 5 - prob: 0.5 - - proto: ProtolatheMachineCircuitboard - - proto: RandomArtifactSpawner - cost: 2 - - proto: RandomCargoCorpseSpawner - cost: 2 - prob: 0.5 - - proto: RandomCommandCorpseSpawner - cost: 5 - prob: 0.5 - - proto: RandomEngineerCorpseSpawner - cost: 2 - prob: 0.5 - - proto: RandomMedicCorpseSpawner - cost: 2 - prob: 0.5 - - proto: RandomScienceCorpseSpawner - cost: 2 - prob: 0.5 - - proto: RandomSecurityCorpseSpawner - cost: 2 - prob: 0.5 - - proto: RandomServiceCorpseSpawner - cost: 2 - prob: 0.5 - - proto: ResearchAndDevelopmentServerMachineCircuitboard - cost: 5 - prob: 0.5 - - proto: ResearchDisk10000 - prob: 0.5 - - proto: ResearchDisk5000 - prob: 0.5 - - proto: RipleyHarness - cost: 3 - prob: 0.5 - - proto: SpaceCash1000 - - proto: SpaceCash10000 - cost: 10 - - proto: SpaceCash2500 - cost: 3 - - proto: SpaceCash5000 - cost: 5 - - proto: TechnologyDiskRare - cost: 5 - prob: 0.5 - - proto: ThrusterUnanchored - - proto: WaterTankHighCapacity - - proto: WeldingFuelTankHighCapacity - cost: 3 - - proto: WeaponTeslaGun - prob: 0.1 - cost: 2 + - !type:RandomSpawnsLoot + entries: + - proto: AdvMopItem + prob: 0.5 + - proto: AmmoTechFabCircuitboard + cost: 2 + - proto: AutolatheMachineCircuitboard + cost: 2 + - proto: BiomassReclaimerMachineCircuitboard + cost: 2 + - proto: BluespaceBeaker + cost: 2 + - proto: CyborgEndoskeleton + cost: 3 + prob: 0.5 + - proto: ChemDispenserMachineCircuitboard + cost: 2 + - proto: CircuitImprinter + cost: 2 + - proto: CloningConsoleComputerCircuitboard + cost: 2 + - proto: CloningPodMachineCircuitboard + cost: 2 + - proto: ChemistryBottleCognizine + - proto: FoodBoxDonkpocketCarp + prob: 0.5 + - proto: CrateSalvageEquipment + cost: 3 + prob: 0.5 + - proto: GasRecycler + cost: 2 + - proto: GeneratorRTG + cost: 5 + - proto: GravityGeneratorMini + cost: 2 + - proto: GyroscopeUnanchored + cost: 2 + prob: 0.1 + - proto: MedicalScannerMachineCircuitboard + cost: 2 + - proto: NuclearBombKeg + cost: 5 + - proto: ChemistryBottleOmnizine + prob: 0.5 + - proto: PortableGeneratorPacman + cost: 2 + - proto: PortableGeneratorSuperPacman + cost: 3 + - proto: PowerCellAntiqueProto + cost: 5 + prob: 0.5 + - proto: ProtolatheMachineCircuitboard + - proto: RandomArtifactSpawner + cost: 2 + - proto: RandomCargoCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomCommandCorpseSpawner + cost: 5 + prob: 0.5 + - proto: RandomEngineerCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomMedicCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomScienceCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomSecurityCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomServiceCorpseSpawner + cost: 2 + prob: 0.5 + - proto: ResearchAndDevelopmentServerMachineCircuitboard + cost: 5 + prob: 0.5 + - proto: ResearchDisk10000 + prob: 0.5 + - proto: ResearchDisk5000 + prob: 0.5 + - proto: RipleyHarness + cost: 3 + prob: 0.5 + - proto: SpaceCash1000 + - proto: SpaceCash10000 + cost: 10 + - proto: SpaceCash2500 + cost: 3 + - proto: SpaceCash5000 + cost: 5 + - proto: TechnologyDiskRare + cost: 5 + prob: 0.5 + - proto: ThrusterUnanchored + - proto: WaterTankHighCapacity + - proto: WeldingFuelTankHighCapacity + cost: 3 + - proto: WeaponTeslaGun + prob: 0.1 + cost: 2 # Mob loot table @@ -117,70 +117,70 @@ id: OreIron guaranteed: true loots: - - !type:BiomeMarkerLoot - proto: OreIron + - !type:BiomeLoot + proto: OreIron - type: salvageLoot id: OreCoal guaranteed: true loots: - - !type:BiomeMarkerLoot - proto: OreCoal + - !type:BiomeLoot + proto: OreCoal - type: salvageLoot id: OreQuartz guaranteed: true loots: - - !type:BiomeMarkerLoot - proto: OreQuartz + - !type:BiomeLoot + proto: OreQuartz - type: salvageLoot id: OreSalt guaranteed: true loots: - - !type:BiomeMarkerLoot - proto: OreSalt + - !type:BiomeLoot + proto: OreSalt # - Medium value - type: salvageLoot id: OreGold guaranteed: true loots: - - !type:BiomeMarkerLoot - proto: OreGold + - !type:BiomeLoot + proto: OreGold - type: salvageLoot id: OreSilver guaranteed: true loots: - - !type:BiomeMarkerLoot - proto: OreSilver + - !type:BiomeLoot + proto: OreSilver # - High value - type: salvageLoot id: OrePlasma guaranteed: true loots: - - !type:BiomeMarkerLoot - proto: OrePlasma + - !type:BiomeLoot + proto: OrePlasma - type: salvageLoot id: OreUranium guaranteed: true loots: - - !type:BiomeMarkerLoot - proto: OreUranium + - !type:BiomeLoot + proto: OreUranium - type: salvageLoot id: OreDiamond guaranteed: true loots: - - !type:BiomeMarkerLoot - proto: OreDiamond + - !type:BiomeLoot + proto: OreDiamond - type: salvageLoot id: OreArtifactFragment guaranteed: true loots: - - !type:BiomeMarkerLoot - proto: OreArtifactFragment + - !type:BiomeLoot + proto: OreArtifactFragment diff --git a/Resources/Prototypes/Procedural/salvage_mods.yml b/Resources/Prototypes/Procedural/salvage_mods.yml index ca64d29a52..e8b18dd14a 100644 --- a/Resources/Prototypes/Procedural/salvage_mods.yml +++ b/Resources/Prototypes/Procedural/salvage_mods.yml @@ -8,24 +8,24 @@ - type: salvageBiomeMod id: Caves desc: salvage-biome-mod-caves - biome: Caves + biome: BiomeCaves - type: salvageBiomeMod id: Grasslands desc: salvage-biome-mod-grasslands - biome: Grasslands + biome: BiomeGrasslands - type: salvageBiomeMod id: Snow desc: salvage-biome-mod-snow cost: 1 - biome: Snow + biome: BiomeSnow - type: salvageBiomeMod id: Lava desc: salvage-biome-mod-lava cost: 2 - biome: Lava + biome: BiomeLava #- type: salvageBiomeMod # id: Space diff --git a/Resources/Prototypes/Procedural/vgroid.yml b/Resources/Prototypes/Procedural/vgroid.yml index 0caa9f0e1f..ad759721cb 100644 --- a/Resources/Prototypes/Procedural/vgroid.yml +++ b/Resources/Prototypes/Procedural/vgroid.yml @@ -117,8 +117,8 @@ - type: dungeonConfig id: VGRoidSmaller - minOffset: 40 - maxOffset: 60 + minOffset: 60 + maxOffset: 80 layers: - !type:NoiseDistanceDunGen size: 150, 150 @@ -151,6 +151,7 @@ maxCount: 3 layers: - !type:ExteriorDunGen + penetration: 5,15 proto: Experiment - !type:EntityTableDunGen minCount: 25 @@ -228,5 +229,16 @@ layers: - !type:FillGridDunGen entity: IronRock + threshold: -0.95 + size: 350, 350 + distanceConfig: !type:DunGenEuclideanSquaredDistance + blendWeight: -0.50 + reservedNoise: + frequency: 0.080 + noiseType: OpenSimplex2 + fractalType: FBm + octaves: 5 + lacunarity: 1.5 + gain: 0.5 allowedTiles: - FloorAsteroidSand From 433ef5dd27181e4093ea9316145f9eff4251a403 Mon Sep 17 00:00:00 2001 From: Winkarst-cpu <74284083+Winkarst-cpu@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:10:20 +0300 Subject: [PATCH 173/191] Fix: Don't deploy foldables when clicking on items inside containers (#38709) * Fix * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Content.Shared/Foldable/DeployFoldableSystem.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Content.Shared/Foldable/DeployFoldableSystem.cs b/Content.Shared/Foldable/DeployFoldableSystem.cs index cac73f6428..c690f3d51f 100644 --- a/Content.Shared/Foldable/DeployFoldableSystem.cs +++ b/Content.Shared/Foldable/DeployFoldableSystem.cs @@ -59,6 +59,10 @@ public sealed class DeployFoldableSystem : EntitySystem if (args.Handled || !args.CanReach) return; + // Don't do anything unless you clicked on the floor. + if (args.Target.HasValue) + return; + if (!TryComp(ent, out var foldable)) return; From 10fa6ff4af32f0ae59ed01f243164fcf6b31f965 Mon Sep 17 00:00:00 2001 From: Kowlin <10947836+Kowlin@users.noreply.github.com> Date: Thu, 3 Jul 2025 16:44:37 +0200 Subject: [PATCH 174/191] Fix world generation (#38713) * Fix world generation * Remove unused impoty --- Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs b/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs index 7f4bdccf2e..5d08247b56 100644 --- a/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs +++ b/Content.Server/Gateway/Systems/GatewayGeneratorSystem.cs @@ -110,7 +110,7 @@ public sealed class GatewayGeneratorSystem : EntitySystem }; AddComp(mapUid, restricted); - _biome.EnsurePlanet(mapUid, _protoManager.Index("BiomeContinental"), seed); + _biome.EnsurePlanet(mapUid, _protoManager.Index("BiomeGrasslands"), seed); var grid = Comp(mapUid); From 1bc3d37d40be0cab7378a00e40ba57bfd478b358 Mon Sep 17 00:00:00 2001 From: Hannah Giovanna Dawson Date: Thu, 3 Jul 2025 16:28:44 +0100 Subject: [PATCH 175/191] Scurrets - Audio Improvements (#38482) * Scurret audio tuning * Add new sfx * Update sneezing sfx * YAML support * Rename a folder --- Resources/Audio/Animals/attributions.yml | 3 ++ Resources/Audio/Animals/wawa_achoo.ogg | Bin 33507 -> 20250 bytes Resources/Audio/Animals/wawa_chatter.ogg | Bin 36144 -> 32829 bytes Resources/Audio/Animals/wawa_chillin.ogg | Bin 33019 -> 29014 bytes Resources/Audio/Animals/wawa_depression.ogg | Bin 36444 -> 21462 bytes Resources/Audio/Animals/wawa_despair.ogg | Bin 0 -> 36237 bytes Resources/Audio/Animals/wawa_exclaim.ogg | Bin 23074 -> 20644 bytes Resources/Audio/Animals/wawa_mock.ogg | Bin 46841 -> 30534 bytes Resources/Audio/Animals/wawa_protest.ogg | Bin 65986 -> 37543 bytes Resources/Audio/Animals/wawa_question.ogg | Bin 15319 -> 14119 bytes Resources/Audio/Animals/wawa_statement.ogg | Bin 20599 -> 18971 bytes Resources/Audio/Animals/wawa_the_end.ogg | Bin 0 -> 20165 bytes Resources/Audio/Animals/wawa_yawn.ogg | Bin 0 -> 26050 bytes Resources/Audio/Effects/attributions.yml | 5 +++ Resources/Audio/Effects/soft_thump.ogg | Bin 0 -> 16851 bytes Resources/Locale/en-US/chat/emotes.ftl | 1 + .../en-US/{scurret copy => scurret}/role.ftl | 0 .../Prototypes/Entities/Mobs/NPCs/scurret.yml | 6 ++-- .../Prototypes/SoundCollections/scurret.yml | 15 +++++++++ .../Prototypes/Voice/speech_emote_sounds.yml | 30 ++++++++++++++++++ Resources/Prototypes/Voice/speech_emotes.yml | 6 ++++ 21 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 Resources/Audio/Animals/wawa_despair.ogg create mode 100644 Resources/Audio/Animals/wawa_the_end.ogg create mode 100644 Resources/Audio/Animals/wawa_yawn.ogg create mode 100644 Resources/Audio/Effects/soft_thump.ogg rename Resources/Locale/en-US/{scurret copy => scurret}/role.ftl (100%) diff --git a/Resources/Audio/Animals/attributions.yml b/Resources/Audio/Animals/attributions.yml index 59d8bc5a6e..f91d98b974 100644 --- a/Resources/Audio/Animals/attributions.yml +++ b/Resources/Audio/Animals/attributions.yml @@ -162,11 +162,14 @@ - wawa_chatter.ogg - wawa_chillin.ogg - wawa_depression.ogg + - wawa_despair.ogg - wawa_exclaim.ogg - wawa_mock.ogg - wawa_protest.ogg - wawa_question.ogg - wawa_statement.ogg + - wawa_the_end.ogg + - wawa_yawn.ogg copyright: "created by FairlySadPanda" license: "CC-BY-4.0" source: "https://github.com/space-wizards/space-station-14/pull/37936" diff --git a/Resources/Audio/Animals/wawa_achoo.ogg b/Resources/Audio/Animals/wawa_achoo.ogg index 56670c2d05966075a6826d6c9b30afc22b162d04..411c4b5b0a94e8b593a58ff050fd287d44f3ceb8 100644 GIT binary patch delta 16146 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 oum6(x*dX-S diff --git a/Resources/Audio/Animals/wawa_chatter.ogg b/Resources/Audio/Animals/wawa_chatter.ogg index 7e2a7ec3ca0589cde9b155447840c2349e0f5692..5537611307313fdc5066ef4c4a3db69deb7ef3ea 100644 GIT binary patch delta 28863 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 e_*4U=3;KWVX$Niq delta 32204 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 RYs7hp)v&x}fdBuW{|Ao?oe%&3 diff --git a/Resources/Audio/Animals/wawa_chillin.ogg b/Resources/Audio/Animals/wawa_chillin.ogg index 5955fdf96794462d9ae50ff82a1a219c7635f121..d548f6a26f1e0a6d83a8a5ee7e7069b995dfa206 100644 GIT binary patch delta 25019 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 diff --git a/Resources/Audio/Animals/wawa_depression.ogg b/Resources/Audio/Animals/wawa_depression.ogg index 3f8167acb78072362ec3784afec2b3e650d0b953..d592654a07b343cfb2cae873486675a6687c703f 100644 GIT binary patch delta 17524 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⪻|tm{uc-wKXL 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 X8&}^kYw~X#aIbEoKQ>s126X=i%P&q@ diff --git a/Resources/Audio/Animals/wawa_despair.ogg b/Resources/Audio/Animals/wawa_despair.ogg new file mode 100644 index 0000000000000000000000000000000000000000..ce4ecc4db2b1e0f46938a058d0cd69a7ca033762 GIT binary patch literal 36237 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

-j>wWY_^@AdonW!xk+&jeet%h-sqHG`@kfrj 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 delta 19031 zcmYJZ1yCH#*Y-V&C%6Q6m*5^e1h?S9Ew~2vSs*|N9tb43OM<(*ySqzpcioTs|GZV- z)YME*SM6*~_31uW|IU?@3CXI05UW^PssnJq|2r%c|F40gZg~_-%!c;gGvuW!*sSw+ ztcDE>%1c{PMOi~aQbPpkrL7^Sp(rKtf4xddQp*3=DCRM(-fC;kw-|PN+&42%~ zqCA010{QQN!T4Y>H5iNy2E&EHcwjI9whMa#USnWjtOPBNOrB7sz%a34DfMD+K0&UI z@AO;wYX4YIZ6ovVAY7Qo-e$D;&!^%VXG#>?xl1R9f@6h!G{Wn)BtEcv7qvR)fK!l3F)>WSXvaNDV&Gt2ajnmZ>b;9o11N!Sm&p2e?N6ob(e&%7O!Gcq1s@VIa@BA z=3TxC(V_WidGU$g7-u_Z$2VW51g@wyx+bI#k&h_vy6(?-H2%F^wbGC(>KDXE@xew9 zOwhz);!AySw!mj zCdKm3q469#_kb2RW}mWO_H17LyE@b?1%rIfV(iZ~KdPlK{&oLTa{4BUu1`O=S`r_S zcJ=s`rw)O0Z+|usnlVwFtz3zmk8Pe-uWgi0miJ^)=wOv|UjBs;Uw| z>htWGG3LjW_&cMPFA1}$G@kSRx?7vU)$i1nXi-_0b|-Q5mgut`cngC$kJ{8DNmp{~ z+7Um5ZpMZRrOm`I*3#&-!qVSW{MOqjN`)~OJO|E?tLWqWQ+Jl?2SJW8DPEaD7j464Barsf7?)XG z0fS#d?!W7%*cnp>U1^Nu48aA#&Y}d1x+zS_(uiZl-c|W?cKcAg-#| zV*ND6$bX2YGcrtLwNEA|Hz=S|N8DQ~`>C}0d?v*&j8jG6HkL?+oTT(3czfHc*1R>| zfsW#UC-fq)(w-Tsm?nV3Xr9kHsg}tCkUE3eSM=gEBBCJT$%=yP=O@nFd58<-N5}K$ z(xowY!NPQ*A3s|aF(;WCZ!c^qP{c6Rsawr{3+IS$MQZ_aQE}0=F@v;@u86oC9 zU8@Z>cRbMe8Eni5J8~~zZ*{4iF=EAx;_O{*DH^I0&0D@B*D}Su>?yQeEk05paLcLHl@Xk~5pM$AX?2*8$X zIMU&j`4IrMia@B571I27!LFn1^8@Qc8kA9d0iwgK%*W}wpZs?7G#-1rxvNKmuh z``k}h;PTt_LC2Z0y#!I1fe!khd@+SN=i^nwTc5$&{AuK?p=%cBMDMZH(e{7*oO6b{ z$wkxYjA~)-PLpRC&fCv-?;l@}pqu(1*?7;q+n}6gNUxUYg;~W19v_V!Udw#oA(`Sn z9LBy2d$NxdX`3wPwkvgMQtCdO7bGV?)$#L|0V!ZUchIotg7=FiYBYz|2FN^J!e zIu-qKqhl3lKuns6x~Ly7ikX2Vx|B}*j&m~+vl&(xf?1of;lJ^t`zN7J7u6Wg3)VC> z?

private const float RoundStartFTLDuration = 10f; - private readonly List _arrivalsBiomeOptions = new() + private readonly List> _arrivalsBiomeOptions = new() { - "BiomeGrasslands", - "BiomeLowDesert", - "BiomeSnow", + "Grasslands", + "LowDesert", + "Snow", }; public override void Initialize() diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index 9e9bcb6c42..13e13bf8f3 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -1,14 +1,12 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; -using Content.Server.Decals; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; using Content.Server.Station.Events; using Content.Shared.Body.Components; using Content.Shared.CCVar; using Content.Shared.Database; -using Content.Shared.Decals; using Content.Shared.Ghost; using Content.Shared.Maps; using Content.Shared.Parallax; @@ -958,7 +956,6 @@ public sealed partial class ShuttleSystem var transform = _physics.GetRelativePhysicsTransform((uid, xform), xform.MapUid.Value); var aabbs = new List(manager.Fixtures.Count); var tileSet = new List<(Vector2i, Tile)>(); - TryComp(xform.MapUid.Value, out DecalGridComponent? decalGrid); foreach (var fixture in manager.Fixtures.Values) { @@ -972,15 +969,9 @@ public sealed partial class ShuttleSystem aabb = aabb.Enlarged(0.2f); aabbs.Add(aabb); - if (decalGrid != null) - { - foreach (var decal in _decals.GetDecalsIntersecting(xform.MapUid.Value, aabb)) - { - _decals.RemoveDecal(xform.MapUid.Value, decal.Index, decalGrid); - } - } - + // Handle clearing biome stuff as relevant. tileSet.Clear(); + _biomes.ReserveTiles(xform.MapUid.Value, aabb, tileSet); _lookupEnts.Clear(); _immuneEnts.Clear(); // TODO: Ideally we'd query first BEFORE moving grid but needs adjustments above. diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.cs index 525e16ae1a..cea7fbfc09 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Administration.Logs; using Content.Server.Body.Systems; using Content.Server.Buckle.Systems; -using Content.Server.Decals; using Content.Server.Parallax; using Content.Server.Procedural; using Content.Server.Shuttles.Components; @@ -42,12 +41,10 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; [Dependency] private readonly BiomeSystem _biomes = default!; [Dependency] private readonly BodySystem _bobby = default!; [Dependency] private readonly BuckleSystem _buckle = default!; [Dependency] private readonly DamageableSystem _damageSys = default!; - [Dependency] private readonly DecalSystem _decals = default!; [Dependency] private readonly DockingSystem _dockSystem = default!; [Dependency] private readonly DungeonSystem _dungeon = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; diff --git a/Content.Server/Station/Components/StationBiomeComponent.cs b/Content.Server/Station/Components/StationBiomeComponent.cs index a2105d6cef..0eb64aaff8 100644 --- a/Content.Server/Station/Components/StationBiomeComponent.cs +++ b/Content.Server/Station/Components/StationBiomeComponent.cs @@ -1,4 +1,5 @@ using Content.Server.Station.Systems; +using Content.Shared.Parallax.Biomes; using Robust.Shared.Prototypes; namespace Content.Server.Station.Components; @@ -10,7 +11,7 @@ namespace Content.Server.Station.Components; public sealed partial class StationBiomeComponent : Component { [DataField(required: true)] - public EntProtoId Biome = "BiomeGrasslands"; + public ProtoId Biome = "Grasslands"; // If null, its random [DataField] diff --git a/Content.Server/Station/Systems/StationBiomeSystem.cs b/Content.Server/Station/Systems/StationBiomeSystem.cs index c0777f6052..c12e2f47e4 100644 --- a/Content.Server/Station/Systems/StationBiomeSystem.cs +++ b/Content.Server/Station/Systems/StationBiomeSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Parallax; -using Content.Server.Procedural; using Content.Server.Station.Components; using Content.Server.Station.Events; using Robust.Shared.Prototypes; diff --git a/Content.Server/Tabletop/TabletopSystem.cs b/Content.Server/Tabletop/TabletopSystem.cs index 3c3065c95a..e771add0e4 100644 --- a/Content.Server/Tabletop/TabletopSystem.cs +++ b/Content.Server/Tabletop/TabletopSystem.cs @@ -191,7 +191,7 @@ namespace Content.Server.Tabletop if (!TryComp(uid, out ActorComponent? actor)) { RemComp(uid); - continue; + return; } if (actor.PlayerSession.Status != SessionStatus.InGame || !CanSeeTable(uid, gamer.Tabletop)) diff --git a/Content.Shared/CCVar/CCVars.Biome.cs b/Content.Shared/CCVar/CCVars.Biome.cs deleted file mode 100644 index 13c6cd548e..0000000000 --- a/Content.Shared/CCVar/CCVars.Biome.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Robust.Shared.Configuration; - -namespace Content.Shared.CCVar; - -public sealed partial class CCVars -{ - /// - /// Load range for biomes. Set this higher than PVS so server can have some time to load in before the client arrives. - /// - public static readonly CVarDef BiomeLoadRange = - CVarDef.Create("biome.load_range", 20f, CVar.SERVERONLY); - - /// - /// Time allocation (ms) for how long biomes are allowed to load. - /// - public static readonly CVarDef BiomeLoadTime = - CVarDef.Create("biome.load_time", 0.03f, CVar.SERVERONLY); -} diff --git a/Content.Shared/Parallax/Biomes/BiomeComponent.cs b/Content.Shared/Parallax/Biomes/BiomeComponent.cs new file mode 100644 index 0000000000..af8eb88683 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/BiomeComponent.cs @@ -0,0 +1,87 @@ +using Content.Shared.Parallax.Biomes.Layers; +using Content.Shared.Parallax.Biomes.Markers; +using Robust.Shared.GameStates; +using Robust.Shared.Noise; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Parallax.Biomes; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(SharedBiomeSystem))] +public sealed partial class BiomeComponent : Component +{ + /// + /// Do we load / deload. + /// + [DataField, ViewVariables(VVAccess.ReadWrite), Access(Other = AccessPermissions.ReadWriteExecute)] + public bool Enabled = true; + + [ViewVariables(VVAccess.ReadWrite), DataField("seed")] + [AutoNetworkedField] + public int Seed = -1; + + /// + /// The underlying entity, decal, and tile layers for the biome. + /// + [DataField("layers")] + [AutoNetworkedField] + public List Layers = new(); + + /// + /// Templates to use for . + /// If this is set on mapinit, it will fill out layers automatically. + /// If not set, use BiomeSystem to do it. + /// Prototype reloading will also use this. + /// + [DataField] + public ProtoId? Template; + + /// + /// If we've already generated a tile and couldn't deload it then we won't ever reload it in future. + /// Stored by [Chunkorigin, Tiles] + /// + [DataField("modifiedTiles")] + public Dictionary> ModifiedTiles = new(); + + /// + /// Decals that have been loaded as a part of this biome. + /// + [DataField("decals")] + public Dictionary> LoadedDecals = new(); + + [DataField("entities")] + public Dictionary> LoadedEntities = new(); + + /// + /// Currently active chunks + /// + [DataField("loadedChunks")] + public HashSet LoadedChunks = new(); + + #region Markers + + /// + /// Work out entire marker tiles in advance but only load the entities when in range. + /// + [DataField("pendingMarkers")] + public Dictionary>> PendingMarkers = new(); + + /// + /// Track what markers we've loaded already to avoid double-loading. + /// + [DataField("loadedMarkers", customTypeSerializer:typeof(PrototypeIdDictionarySerializer, BiomeMarkerLayerPrototype>))] + public Dictionary> LoadedMarkers = new(); + + [DataField] + public HashSet> MarkerLayers = new(); + + /// + /// One-tick forcing of marker layers to bulldoze any entities in the way. + /// + [DataField] + public HashSet> ForcedMarkerLayers = new(); + + #endregion +} diff --git a/Content.Shared/Parallax/Biomes/BiomeTemplatePrototype.cs b/Content.Shared/Parallax/Biomes/BiomeTemplatePrototype.cs new file mode 100644 index 0000000000..437ead63a7 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/BiomeTemplatePrototype.cs @@ -0,0 +1,16 @@ +using Content.Shared.Parallax.Biomes.Layers; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Parallax.Biomes; + +/// +/// A preset group of biome layers to be used for a +/// +[Prototype] +public sealed partial class BiomeTemplatePrototype : IPrototype +{ + [IdDataField] public string ID { get; private set; } = default!; + + [DataField("layers")] + public List Layers = new(); +} diff --git a/Content.Shared/Parallax/Biomes/Layers/BiomeDecalLayer.cs b/Content.Shared/Parallax/Biomes/Layers/BiomeDecalLayer.cs new file mode 100644 index 0000000000..5e31e513a9 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/Layers/BiomeDecalLayer.cs @@ -0,0 +1,34 @@ +using Content.Shared.Decals; +using Content.Shared.Maps; +using Robust.Shared.Noise; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Parallax.Biomes.Layers; + +[Serializable, NetSerializable] +public sealed partial class BiomeDecalLayer : IBiomeWorldLayer +{ + /// + [DataField("allowedTiles", customTypeSerializer:typeof(PrototypeIdListSerializer))] + public List AllowedTiles { get; private set; } = new(); + + /// + /// Divide each tile up by this amount. + /// + [DataField("divisions")] + public float Divisions = 1f; + + [DataField("noise")] + public FastNoiseLite Noise { get; private set; } = new(0); + + /// + [DataField("threshold")] + public float Threshold { get; private set; } = 0.8f; + + /// + [DataField("invert")] public bool Invert { get; private set; } = false; + + [DataField("decals", required: true, customTypeSerializer:typeof(PrototypeIdListSerializer))] + public List Decals = new(); +} diff --git a/Content.Shared/Parallax/Biomes/Layers/BiomeDummyLayer.cs b/Content.Shared/Parallax/Biomes/Layers/BiomeDummyLayer.cs new file mode 100644 index 0000000000..2beeba6e03 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/Layers/BiomeDummyLayer.cs @@ -0,0 +1,18 @@ +using Robust.Shared.Noise; +using Robust.Shared.Serialization; + +namespace Content.Shared.Parallax.Biomes.Layers; + +/// +/// Dummy layer that specifies a marker to be replaced by external code. +/// For example if they wish to add their own layers at specific points across different templates. +/// +[Serializable, NetSerializable] +public sealed partial class BiomeDummyLayer : IBiomeLayer +{ + [DataField("id", required: true)] public string ID = string.Empty; + + public FastNoiseLite Noise { get; } = new(); + public float Threshold { get; } + public bool Invert { get; } +} diff --git a/Content.Shared/Parallax/Biomes/Layers/BiomeEntityLayer.cs b/Content.Shared/Parallax/Biomes/Layers/BiomeEntityLayer.cs new file mode 100644 index 0000000000..21ffdd96e5 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/Layers/BiomeEntityLayer.cs @@ -0,0 +1,27 @@ +using Content.Shared.Maps; +using Robust.Shared.Noise; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Parallax.Biomes.Layers; + +[Serializable, NetSerializable] +public sealed partial class BiomeEntityLayer : IBiomeWorldLayer +{ + /// + [DataField("allowedTiles", customTypeSerializer:typeof(PrototypeIdListSerializer))] + public List AllowedTiles { get; private set; } = new(); + + [DataField("noise")] public FastNoiseLite Noise { get; private set; } = new(0); + + /// + [DataField("threshold")] + public float Threshold { get; private set; } = 0.5f; + + /// + [DataField("invert")] public bool Invert { get; private set; } = false; + + [DataField("entities", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List Entities = new(); +} diff --git a/Content.Shared/Parallax/Biomes/Layers/BiomeMetaLayer.cs b/Content.Shared/Parallax/Biomes/Layers/BiomeMetaLayer.cs new file mode 100644 index 0000000000..51231405a8 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/Layers/BiomeMetaLayer.cs @@ -0,0 +1,27 @@ +using Robust.Shared.Noise; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Parallax.Biomes.Layers; + +/// +/// Contains more biome layers recursively via a biome template. +/// Can be used for sub-biomes. +/// +[Serializable, NetSerializable] +public sealed partial class BiomeMetaLayer : IBiomeLayer +{ + [DataField("noise")] + public FastNoiseLite Noise { get; private set; } = new(0); + + /// + [DataField("threshold")] + public float Threshold { get; private set; } = -1f; + + /// + [DataField("invert")] + public bool Invert { get; private set; } + + [DataField("template", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] + public string Template = string.Empty; +} diff --git a/Content.Shared/Procedural/DungeonLayers/SampleTileDunGen.cs b/Content.Shared/Parallax/Biomes/Layers/BiomeTileLayer.cs similarity index 66% rename from Content.Shared/Procedural/DungeonLayers/SampleTileDunGen.cs rename to Content.Shared/Parallax/Biomes/Layers/BiomeTileLayer.cs index 6d23d201f5..9dee35da4e 100644 --- a/Content.Shared/Procedural/DungeonLayers/SampleTileDunGen.cs +++ b/Content.Shared/Parallax/Biomes/Layers/BiomeTileLayer.cs @@ -2,26 +2,20 @@ using Content.Shared.Maps; using Robust.Shared.Noise; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Shared.Procedural.DungeonLayers; +namespace Content.Shared.Parallax.Biomes.Layers; -/// -/// Samples noise and spawns the specified tile in the dungeon area. -/// [Serializable, NetSerializable] -public sealed partial class SampleTileDunGen : IDunGenLayer +public sealed partial class BiomeTileLayer : IBiomeLayer { - /// - /// Reserve any tiles we update. - /// - [DataField] - public bool ReserveTiles = true; - [DataField] public FastNoiseLite Noise { get; private set; } = new(0); + /// [DataField] public float Threshold { get; private set; } = 0.5f; + /// [DataField] public bool Invert { get; private set; } = false; /// diff --git a/Content.Shared/Parallax/Biomes/Layers/IBiomeLayer.cs b/Content.Shared/Parallax/Biomes/Layers/IBiomeLayer.cs new file mode 100644 index 0000000000..3b1ad5c76c --- /dev/null +++ b/Content.Shared/Parallax/Biomes/Layers/IBiomeLayer.cs @@ -0,0 +1,22 @@ +using Robust.Shared.Noise; + +namespace Content.Shared.Parallax.Biomes.Layers; + +[ImplicitDataDefinitionForInheritors] +public partial interface IBiomeLayer +{ + /// + /// Seed is used an offset from the relevant BiomeComponent's seed. + /// + FastNoiseLite Noise { get; } + + /// + /// Threshold for this layer to be present. If set to 0 forces it for every tile. + /// + float Threshold { get; } + + /// + /// Is the thresold inverted so we need to be lower than it. + /// + public bool Invert { get; } +} diff --git a/Content.Shared/Parallax/Biomes/Layers/IBiomeWorldLayer.cs b/Content.Shared/Parallax/Biomes/Layers/IBiomeWorldLayer.cs new file mode 100644 index 0000000000..e04db913b7 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/Layers/IBiomeWorldLayer.cs @@ -0,0 +1,12 @@ +namespace Content.Shared.Parallax.Biomes.Layers; + +/// +/// Handles actual objects such as decals and entities. +/// +public partial interface IBiomeWorldLayer : IBiomeLayer +{ + /// + /// What tiles we're allowed to spawn on, real or biome. + /// + List AllowedTiles { get; } +} diff --git a/Content.Shared/Parallax/Biomes/Markers/BiomeMarkerLayerPrototype.cs b/Content.Shared/Parallax/Biomes/Markers/BiomeMarkerLayerPrototype.cs new file mode 100644 index 0000000000..fbc3a04eb4 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/Markers/BiomeMarkerLayerPrototype.cs @@ -0,0 +1,53 @@ +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Parallax.Biomes.Markers; + +/// +/// Spawns entities inside of the specified area with the minimum specified radius. +/// +[Prototype] +public sealed partial class BiomeMarkerLayerPrototype : IBiomeMarkerLayer +{ + [IdDataField] public string ID { get; private set; } = default!; + + /// + /// Checks for the relevant entity for the tile before spawning. Useful for substituting walls with ore veins for example. + /// + [DataField] + public Dictionary EntityMask { get; private set; } = new(); + + /// + /// Default prototype to spawn. If null will fall back to entity mask. + /// + [DataField] + public string? Prototype { get; private set; } + + /// + /// Minimum radius between 2 points + /// + [DataField("radius")] + public float Radius = 32f; + + /// + /// Maximum amount of group spawns + /// + [DataField("maxCount")] + public int MaxCount = int.MaxValue; + + /// + /// Minimum entities to spawn in one group. + /// + [DataField] + public int MinGroupSize = 1; + + /// + /// Maximum entities to spawn in one group. + /// + [DataField] + public int MaxGroupSize = 1; + + /// + [DataField("size")] + public int Size { get; private set; } = 128; +} diff --git a/Content.Shared/Parallax/Biomes/Markers/IBiomeMarkerLayer.cs b/Content.Shared/Parallax/Biomes/Markers/IBiomeMarkerLayer.cs new file mode 100644 index 0000000000..de2913bb09 --- /dev/null +++ b/Content.Shared/Parallax/Biomes/Markers/IBiomeMarkerLayer.cs @@ -0,0 +1,22 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Parallax.Biomes.Markers; + +/// +/// Specifies one-off marker points to be used. This could be for dungeon markers, mob markers, etc. +/// These are run outside of the tile / decal / entity layers. +/// +public interface IBiomeMarkerLayer : IPrototype +{ + /// + /// Biome template to use as a mask for this layer. + /// + public Dictionary EntityMask { get; } + + public string? Prototype { get; } + + /// + /// How large the pre-generated points area is. + /// + public int Size { get; } +} diff --git a/Content.Shared/Parallax/Biomes/SharedBiomeSystem.cs b/Content.Shared/Parallax/Biomes/SharedBiomeSystem.cs new file mode 100644 index 0000000000..a5238e8c6e --- /dev/null +++ b/Content.Shared/Parallax/Biomes/SharedBiomeSystem.cs @@ -0,0 +1,386 @@ +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using Content.Shared.Maps; +using Content.Shared.Parallax.Biomes.Layers; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Noise; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Utility; + +namespace Content.Shared.Parallax.Biomes; + +public abstract class SharedBiomeSystem : EntitySystem +{ + [Dependency] protected readonly IPrototypeManager ProtoManager = default!; + [Dependency] private readonly ISerializationManager _serManager = default!; + [Dependency] protected readonly ITileDefinitionManager TileDefManager = default!; + [Dependency] private readonly TileSystem _tile = default!; + [Dependency] private readonly SharedMapSystem _map = default!; + + protected const byte ChunkSize = 8; + + private T Pick(List collection, float value) + { + // Listen I don't need this exact and I'm too lazy to finetune just for random ent picking. + value %= 1f; + value = Math.Clamp(value, 0f, 1f); + + if (collection.Count == 1) + return collection[0]; + + var randValue = value * collection.Count; + + foreach (var item in collection) + { + randValue -= 1f; + + if (randValue <= 0f) + { + return item; + } + } + + throw new ArgumentOutOfRangeException(); + } + + private int Pick(int count, float value) + { + value %= 1f; + value = Math.Clamp(value, 0f, 1f); + + if (count == 1) + return 0; + + value *= count; + + for (var i = 0; i < count; i++) + { + value -= 1f; + + if (value <= 0f) + { + return i; + } + } + + throw new ArgumentOutOfRangeException(); + } + + public bool TryGetBiomeTile(EntityUid uid, MapGridComponent grid, Vector2i indices, [NotNullWhen(true)] out Tile? tile) + { + if (_map.TryGetTileRef(uid, grid, indices, out var tileRef) && !tileRef.Tile.IsEmpty) + { + tile = tileRef.Tile; + return true; + } + + if (!TryComp(uid, out var biome)) + { + tile = null; + return false; + } + + return TryGetBiomeTile(indices, biome.Layers, biome.Seed, (uid, grid), out tile); + } + + /// + /// Tries to get the tile, real or otherwise, for the specified indices. + /// + public bool TryGetBiomeTile(Vector2i indices, List layers, int seed, Entity? grid, [NotNullWhen(true)] out Tile? tile) + { + if (grid is { } gridEnt && _map.TryGetTileRef(gridEnt, gridEnt.Comp, indices, out var tileRef) && !tileRef.Tile.IsEmpty) + { + tile = tileRef.Tile; + return true; + } + + return TryGetTile(indices, layers, seed, grid, out tile); + } + + /// + /// Tries to get the tile, real or otherwise, for the specified indices. + /// + [Obsolete("Use the Entity? overload")] + public bool TryGetBiomeTile(Vector2i indices, List layers, int seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile) + { + return TryGetBiomeTile(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out tile); + } + + /// + /// Gets the underlying biome tile, ignoring any existing tile that may be there. + /// + public bool TryGetTile(Vector2i indices, List layers, int seed, Entity? grid, [NotNullWhen(true)] out Tile? tile) + { + for (var i = layers.Count - 1; i >= 0; i--) + { + var layer = layers[i]; + var noiseCopy = GetNoise(layer.Noise, seed); + + var invert = layer.Invert; + var value = noiseCopy.GetNoise(indices.X, indices.Y); + value = invert ? value * -1 : value; + + if (value < layer.Threshold) + continue; + + // Check if the tile is from meta layer, otherwise fall back to default layers. + if (layer is BiomeMetaLayer meta) + { + if (TryGetBiomeTile(indices, ProtoManager.Index(meta.Template).Layers, seed, grid, out tile)) + { + return true; + } + + continue; + } + + if (layer is not BiomeTileLayer tileLayer) + continue; + + if (TryGetTile(indices, noiseCopy, tileLayer.Invert, tileLayer.Threshold, ProtoManager.Index(tileLayer.Tile), tileLayer.Variants, out tile)) + { + return true; + } + } + + tile = null; + return false; + } + + /// + /// Gets the underlying biome tile, ignoring any existing tile that may be there. + /// + [Obsolete("Use the Entity? overload")] + public bool TryGetTile(Vector2i indices, List layers, int seed, MapGridComponent? grid, [NotNullWhen(true)] out Tile? tile) + { + return TryGetTile(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out tile); + } + + /// + /// Gets the underlying biome tile, ignoring any existing tile that may be there. + /// + private bool TryGetTile(Vector2i indices, FastNoiseLite noise, bool invert, float threshold, ContentTileDefinition tileDef, List? variants, [NotNullWhen(true)] out Tile? tile) + { + var found = noise.GetNoise(indices.X, indices.Y); + found = invert ? found * -1 : found; + + if (found < threshold) + { + tile = null; + return false; + } + + byte variant = 0; + var variantCount = variants?.Count ?? tileDef.Variants; + + // Pick a variant tile if they're available as well + if (variantCount > 1) + { + var variantValue = (noise.GetNoise(indices.X * 8, indices.Y * 8, variantCount) + 1f) * 100; + variant = _tile.PickVariant(tileDef, (int)variantValue); + } + + tile = new Tile(tileDef.TileId, variant); + return true; + } + + /// + /// Tries to get the relevant entity for this tile. + /// + public bool TryGetEntity(Vector2i indices, BiomeComponent component, Entity? grid, + [NotNullWhen(true)] out string? entity) + { + if (!TryGetBiomeTile(indices, component.Layers, component.Seed, grid, out var tile)) + { + entity = null; + return false; + } + + return TryGetEntity(indices, component.Layers, tile.Value, component.Seed, grid, out entity); + } + + /// + /// Tries to get the relevant entity for this tile. + /// + [Obsolete("Use the Entity? overload")] + public bool TryGetEntity(Vector2i indices, BiomeComponent component, MapGridComponent grid, + [NotNullWhen(true)] out string? entity) + { + return TryGetEntity(indices, component, grid == null ? null : (grid.Owner, grid), out entity); + } + + public bool TryGetEntity(Vector2i indices, List layers, Tile tileRef, int seed, Entity? grid, + [NotNullWhen(true)] out string? entity) + { + var tileId = TileDefManager[tileRef.TypeId].ID; + + for (var i = layers.Count - 1; i >= 0; i--) + { + var layer = layers[i]; + + switch (layer) + { + case BiomeDummyLayer: + continue; + case IBiomeWorldLayer worldLayer: + if (!worldLayer.AllowedTiles.Contains(tileId)) + continue; + + break; + case BiomeMetaLayer: + break; + default: + continue; + } + + var noiseCopy = GetNoise(layer.Noise, seed); + + var invert = layer.Invert; + var value = noiseCopy.GetNoise(indices.X, indices.Y); + value = invert ? value * -1 : value; + + if (value < layer.Threshold) + continue; + + if (layer is BiomeMetaLayer meta) + { + if (TryGetEntity(indices, ProtoManager.Index(meta.Template).Layers, tileRef, seed, grid, out entity)) + { + return true; + } + + continue; + } + + // Decals might block entity so need to check if there's one in front of us. + if (layer is not BiomeEntityLayer biomeLayer) + { + entity = null; + return false; + } + + var noiseValue = noiseCopy.GetNoise(indices.X, indices.Y, i); + entity = Pick(biomeLayer.Entities, (noiseValue + 1f) / 2f); + return true; + } + + entity = null; + return false; + } + + [Obsolete("Use the Entity? overload")] + public bool TryGetEntity(Vector2i indices, List layers, Tile tileRef, int seed, MapGridComponent grid, + [NotNullWhen(true)] out string? entity) + { + return TryGetEntity(indices, layers, tileRef, seed, grid == null ? null : (grid.Owner, grid), out entity); + } + + /// + /// Tries to get the relevant decals for this tile. + /// + public bool TryGetDecals(Vector2i indices, List layers, int seed, Entity? grid, + [NotNullWhen(true)] out List<(string ID, Vector2 Position)>? decals) + { + if (!TryGetBiomeTile(indices, layers, seed, grid, out var tileRef)) + { + decals = null; + return false; + } + + var tileId = TileDefManager[tileRef.Value.TypeId].ID; + + for (var i = layers.Count - 1; i >= 0; i--) + { + var layer = layers[i]; + + // Entities might block decal so need to check if there's one in front of us. + switch (layer) + { + case BiomeDummyLayer: + continue; + case IBiomeWorldLayer worldLayer: + if (!worldLayer.AllowedTiles.Contains(tileId)) + continue; + + break; + case BiomeMetaLayer: + break; + default: + continue; + } + + var invert = layer.Invert; + var noiseCopy = GetNoise(layer.Noise, seed); + var value = noiseCopy.GetNoise(indices.X, indices.Y); + value = invert ? value * -1 : value; + + if (value < layer.Threshold) + continue; + + if (layer is BiomeMetaLayer meta) + { + if (TryGetDecals(indices, ProtoManager.Index(meta.Template).Layers, seed, grid, out decals)) + { + return true; + } + + continue; + } + + // Check if the other layer should even render, if not then keep going. + if (layer is not BiomeDecalLayer decalLayer) + { + decals = null; + return false; + } + + decals = new List<(string ID, Vector2 Position)>(); + + for (var x = 0; x < decalLayer.Divisions; x++) + { + for (var y = 0; y < decalLayer.Divisions; y++) + { + var index = new Vector2(indices.X + x * 1f / decalLayer.Divisions, indices.Y + y * 1f / decalLayer.Divisions); + var decalValue = noiseCopy.GetNoise(index.X, index.Y); + decalValue = invert ? decalValue * -1 : decalValue; + + if (decalValue < decalLayer.Threshold) + continue; + + decals.Add((Pick(decalLayer.Decals, (noiseCopy.GetNoise(indices.X, indices.Y, x + y * decalLayer.Divisions) + 1f) / 2f), index)); + } + } + + // Check other layers + if (decals.Count == 0) + continue; + + return true; + } + + decals = null; + return false; + } + + /// + /// Tries to get the relevant decals for this tile. + /// + [Obsolete("Use the Entity? overload")] + public bool TryGetDecals(Vector2i indices, List layers, int seed, MapGridComponent grid, + [NotNullWhen(true)] out List<(string ID, Vector2 Position)>? decals) + { + return TryGetDecals(indices, layers, seed, grid == null ? null : (grid.Owner, grid), out decals); + } + + private FastNoiseLite GetNoise(FastNoiseLite seedNoise, int seed) + { + var noiseCopy = new FastNoiseLite(); + _serManager.CopyTo(seedNoise, ref noiseCopy, notNullableOverride: true); + noiseCopy.SetSeed(noiseCopy.GetSeed() + seed); + // Ensure re-calculate is run. + noiseCopy.SetFractalOctaves(noiseCopy.GetFractalOctaves()); + return noiseCopy; + } +} diff --git a/Content.Shared/Procedural/Components/BiomeComponent.cs b/Content.Shared/Procedural/Components/BiomeComponent.cs deleted file mode 100644 index f79a31c414..0000000000 --- a/Content.Shared/Procedural/Components/BiomeComponent.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Robust.Shared.Prototypes; - -namespace Content.Shared.Procedural.Components; - -/// -/// A layer inside of -/// -[DataRecord] -public sealed record BiomeMetaLayer -{ - /// - /// Chunk dimensions for this meta layer. Will try to infer it from the first layer of the dungeon if null. - /// - [DataField] - public int? Size; - - /// - /// Meta layers that this one requires to be loaded first. - /// Will ensure all of the chunks for our corresponding area are loaded. - /// - public List? DependsOn; - - /// - /// Can this layer be unloaded if no one is in range. - /// - public bool CanUnload = true; - - /// - /// Dungeon config to load inside the specified area. - /// - [DataField(required: true)] - public ProtoId Dungeon = new(); -} - -[RegisterComponent] -public sealed partial class BiomeComponent : Component -{ - /// - /// Can we load / unload chunks. - /// - [DataField] - public bool Enabled = true; - - /// - /// Areas queued for preloading. Will add these during and then flag as modified so they retain. - /// - [DataField] - public List PreloadAreas = new(); - - /// - /// Is there currently a job that's loading. - /// - public bool Loading = false; - - [DataField] - public int Seed; - - /// - /// Layer key and associated data. - /// - [DataField(required: true)] - public Dictionary Layers = new(); - - /// - /// Layer removals that are pending. - /// - [DataField] - public List PendingRemovals = new(); - - /// - /// Data that is currently loaded. - /// - [DataField] - public Dictionary> LoadedData = new(); - - /// - /// Flag modified tiles so we don't try and unload / reload them. - /// - [DataField] - public HashSet ModifiedTiles = new(); - - /// - /// Bounds loaded by players for this tick. - /// - public List LoadedBounds = new(); -} diff --git a/Content.Shared/Procedural/Components/BiomeForceUnloadComponent.cs b/Content.Shared/Procedural/Components/BiomeForceUnloadComponent.cs deleted file mode 100644 index 215a8f345d..0000000000 --- a/Content.Shared/Procedural/Components/BiomeForceUnloadComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Procedural.Components; - -/// -/// Will forcibly unload an entity no matter what. Useful if you have consistent entities that will never be default or the likes. -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class BiomeForceUnloadComponent : Component; diff --git a/Content.Shared/Procedural/Distance/DunGenDistanceSquared.cs b/Content.Shared/Procedural/Distance/DunGenDistanceSquared.cs deleted file mode 100644 index 813f5fc1d4..0000000000 --- a/Content.Shared/Procedural/Distance/DunGenDistanceSquared.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Content.Shared.Procedural.Distance; - -public sealed partial class DunGenDistanceSquared : IDunGenDistance -{ - [DataField] - public float BlendWeight { get; set; } = 0.50f; -} diff --git a/Content.Shared/Procedural/DungeonConfig.cs b/Content.Shared/Procedural/DungeonConfig.cs index 56131e5b28..7c84b1a6a3 100644 --- a/Content.Shared/Procedural/DungeonConfig.cs +++ b/Content.Shared/Procedural/DungeonConfig.cs @@ -12,18 +12,11 @@ public partial class DungeonConfig public List Layers = new(); /// - /// Should we reserve the tiles generated by this config so no other layers at the same level can spawn on this tile? + /// Should we reserve the tiles generated by this config so no other dungeons can spawn on it within the same job? /// [DataField] public bool ReserveTiles; - /// - /// Should we return the reserved tiles to the upper level. - /// Set to false if you don't care if this dungeon has its tiles overwritten at higher levels. - /// - [DataField] - public bool ReturnReserved = true; - /// /// Minimum times to run the config. /// diff --git a/Content.Shared/Procedural/DungeonData.cs b/Content.Shared/Procedural/DungeonData.cs deleted file mode 100644 index a0209f91a6..0000000000 --- a/Content.Shared/Procedural/DungeonData.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Linq; -using System.Numerics; -using Robust.Shared.Map; - -namespace Content.Shared.Procedural; - -/// -/// Contains the loaded data for a dungeon. -/// -[DataDefinition] -public sealed partial class DungeonData -{ - [DataField] - public Dictionary Decals = new(); - - [DataField] - public Dictionary Entities = new(); - - [DataField] - public Dictionary Tiles = new(); - - public static DungeonData Empty = new(); - - public void Merge(DungeonData data) - { - foreach (var did in data.Decals) - { - Decals[did.Key] = did.Value; - } - - foreach (var ent in data.Entities) - { - Entities[ent.Key] = ent.Value; - } - - foreach (var tile in data.Tiles) - { - Tiles[tile.Key] = tile.Value; - } - } -} diff --git a/Content.Shared/Procedural/DungeonGenerators/ChunkDunGen.cs b/Content.Shared/Procedural/DungeonGenerators/ChunkDunGen.cs deleted file mode 100644 index b48a1b3fd6..0000000000 --- a/Content.Shared/Procedural/DungeonGenerators/ChunkDunGen.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Robust.Shared.Noise; - -namespace Content.Shared.Procedural.DungeonGenerators; - -/// -/// Turns a chunked area into a dungeon for layer purposes. Assumes the position is the BL origin. -/// -public sealed partial class ChunkDunGen : IDunGenLayer -{ - [DataField] - public int Size = 16; - - /// - /// Noise to apply for each tile conditionally. - /// - [DataField] - public FastNoiseLite? Noise; - - /// - /// Threshold for noise. Does nothing if is null. - /// - [DataField] - public float Threshold = -1f; -} diff --git a/Content.Shared/Procedural/DungeonGenerators/ExteriorDunGen.cs b/Content.Shared/Procedural/DungeonGenerators/ExteriorDunGen.cs index 2ece880851..e9a5181f8d 100644 --- a/Content.Shared/Procedural/DungeonGenerators/ExteriorDunGen.cs +++ b/Content.Shared/Procedural/DungeonGenerators/ExteriorDunGen.cs @@ -10,10 +10,4 @@ public sealed partial class ExteriorDunGen : IDunGenLayer { [DataField(required: true)] public ProtoId Proto; - - /// - /// Minimum and maximum penetration. - /// - [DataField] - public Vector2i Penetration = new Vector2i(5, 15); } diff --git a/Content.Shared/Procedural/DungeonGenerators/PrototypeDunGen.cs b/Content.Shared/Procedural/DungeonGenerators/PrototypeDunGen.cs index c3f7ae3f33..89a4ab216a 100644 --- a/Content.Shared/Procedural/DungeonGenerators/PrototypeDunGen.cs +++ b/Content.Shared/Procedural/DungeonGenerators/PrototypeDunGen.cs @@ -14,12 +14,6 @@ public sealed partial class PrototypeDunGen : IDunGenLayer [DataField] public DungeonInheritance InheritDungeons = DungeonInheritance.None; - /// - /// Should we pass in the current level's reserved tiles to the prototype. - /// - [DataField] - public ReservedInheritance InheritReserved = ReservedInheritance.All; - [DataField(required: true)] public ProtoId Proto; } @@ -41,16 +35,3 @@ public enum DungeonInheritance : byte /// All, } - -public enum ReservedInheritance : byte -{ - /// - /// Don't inherit any reserved tiles. - /// - None, - - /// - /// Inherit reserved tiles, - /// - All, -} diff --git a/Content.Shared/Procedural/DungeonLayers/FillGridDunGen.cs b/Content.Shared/Procedural/DungeonLayers/FillGridDunGen.cs index e2506298fd..363de0a511 100644 --- a/Content.Shared/Procedural/DungeonLayers/FillGridDunGen.cs +++ b/Content.Shared/Procedural/DungeonLayers/FillGridDunGen.cs @@ -1,7 +1,4 @@ -using System.Numerics; using Content.Shared.Maps; -using Content.Shared.Procedural.Distance; -using Robust.Shared.Noise; using Robust.Shared.Prototypes; namespace Content.Shared.Procedural.DungeonLayers; @@ -9,6 +6,10 @@ namespace Content.Shared.Procedural.DungeonLayers; /// /// Fills unreserved tiles with the specified entity prototype. /// +/// +/// DungeonData keys are: +/// - Fill +/// public sealed partial class FillGridDunGen : IDunGenLayer { /// @@ -19,29 +20,4 @@ public sealed partial class FillGridDunGen : IDunGenLayer [DataField(required: true)] public EntProtoId Entity; - - #region Noise - - [DataField] - public bool Invert; - - /// - /// Optionally don't spawn entities if the noise value matches. - /// - [DataField] - public FastNoiseLite? ReservedNoise; - - /// - /// Noise threshold for . Does nothing without it. - /// - [DataField] - public float Threshold = -1f; - - [DataField] - public IDunGenDistance? DistanceConfig; - - [DataField] - public Vector2 Size; - - #endregion } diff --git a/Content.Shared/Procedural/DungeonLayers/MobsDunGen.cs b/Content.Shared/Procedural/DungeonLayers/MobsDunGen.cs index 1b754d3778..5525341eb9 100644 --- a/Content.Shared/Procedural/DungeonLayers/MobsDunGen.cs +++ b/Content.Shared/Procedural/DungeonLayers/MobsDunGen.cs @@ -1,8 +1,10 @@ using Content.Shared.EntityTable; +using Content.Shared.Storage; using Robust.Shared.Prototypes; namespace Content.Shared.Procedural.DungeonLayers; + /// /// Spawns mobs inside of the dungeon randomly. /// diff --git a/Content.Shared/Procedural/DungeonLayers/RoofDunGen.cs b/Content.Shared/Procedural/DungeonLayers/RoofDunGen.cs deleted file mode 100644 index fbb174dc14..0000000000 --- a/Content.Shared/Procedural/DungeonLayers/RoofDunGen.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Robust.Shared.Noise; - -namespace Content.Shared.Procedural.DungeonLayers; - -/// -/// Sets tiles as rooved. -/// -public sealed partial class RoofDunGen : IDunGenLayer -{ - [DataField] - public float Threshold = -1f; - - [DataField] - public FastNoiseLite? Noise; -} diff --git a/Content.Shared/Procedural/DungeonLayers/SampleDecalDunGen.cs b/Content.Shared/Procedural/DungeonLayers/SampleDecalDunGen.cs deleted file mode 100644 index 616e643b52..0000000000 --- a/Content.Shared/Procedural/DungeonLayers/SampleDecalDunGen.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Content.Shared.Decals; -using Content.Shared.Maps; -using Robust.Shared.Noise; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; - -namespace Content.Shared.Procedural.DungeonLayers; - -public sealed partial class SampleDecalDunGen : IDunGenLayer -{ - /// - /// Reserve any tiles we update. - /// - [DataField] - public bool ReserveTiles = true; - - [DataField(customTypeSerializer:typeof(PrototypeIdListSerializer))] - public List AllowedTiles { get; private set; } = new(); - - /// - /// Divide each tile up by this amount. - /// - [DataField] - public float Divisions = 1f; - - [DataField] - public FastNoiseLite Noise { get; private set; } = new(0); - - [DataField] - public float Threshold { get; private set; } = 0.8f; - - [DataField] public bool Invert { get; private set; } = false; - - [DataField(required: true)] - public List> Decals = new(); -} diff --git a/Content.Shared/Procedural/DungeonLayers/SampleEntityDunGen.cs b/Content.Shared/Procedural/DungeonLayers/SampleEntityDunGen.cs deleted file mode 100644 index 2daf7e7aa7..0000000000 --- a/Content.Shared/Procedural/DungeonLayers/SampleEntityDunGen.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Content.Shared.Maps; -using Robust.Shared.Noise; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; - -namespace Content.Shared.Procedural.DungeonLayers; - -/// -/// Samples noise to spawn the specified entity -/// -public sealed partial class SampleEntityDunGen : IDunGenLayer -{ - /// - /// Reserve any tiles we update. - /// - [DataField] - public bool ReserveTiles = true; - - [DataField(customTypeSerializer:typeof(PrototypeIdListSerializer))] - public List AllowedTiles { get; private set; } = new(); - - [DataField] public FastNoiseLite Noise { get; private set; } = new(0); - - [DataField] - public float Threshold { get; private set; } = 0.5f; - - [DataField] public bool Invert { get; private set; } = false; - - [DataField] - public List Entities = new(); -} diff --git a/Content.Shared/Procedural/Loot/BiomeLoot.cs b/Content.Shared/Procedural/Loot/BiomeLoot.cs deleted file mode 100644 index 1330043493..0000000000 --- a/Content.Shared/Procedural/Loot/BiomeLoot.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Robust.Shared.Prototypes; - -namespace Content.Shared.Procedural.Loot; - -/// -/// Adds the prototype as a biome layer. -/// -public sealed partial class BiomeLoot : IDungeonLoot -{ - [DataField(required: true)] - public ProtoId Proto; -} diff --git a/Content.Shared/Procedural/Loot/BiomeMarkerLoot.cs b/Content.Shared/Procedural/Loot/BiomeMarkerLoot.cs new file mode 100644 index 0000000000..2eda4b059c --- /dev/null +++ b/Content.Shared/Procedural/Loot/BiomeMarkerLoot.cs @@ -0,0 +1,15 @@ +using Content.Shared.Parallax.Biomes.Markers; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; + +namespace Content.Shared.Procedural.Loot; + +/// +/// Adds a biome marker layer for dungeon loot. +/// +public sealed partial class BiomeMarkerLoot : IDungeonLoot +{ + [DataField("proto", required: true)] + public ProtoId Prototype = new(); +} diff --git a/Content.Shared/Procedural/Loot/BiomeTemplateLoot.cs b/Content.Shared/Procedural/Loot/BiomeTemplateLoot.cs new file mode 100644 index 0000000000..e4968b6e42 --- /dev/null +++ b/Content.Shared/Procedural/Loot/BiomeTemplateLoot.cs @@ -0,0 +1,14 @@ +using Content.Shared.Parallax.Biomes; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Procedural.Loot; + +/// +/// Adds a biome template layer for dungeon loot. +/// +public sealed partial class BiomeTemplateLoot : IDungeonLoot +{ + [DataField("proto", required: true)] + public ProtoId Prototype = string.Empty; +} diff --git a/Content.Shared/Procedural/DungeonLayers/AutoCablingDunGen.cs b/Content.Shared/Procedural/PostGeneration/AutoCablingDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/AutoCablingDunGen.cs rename to Content.Shared/Procedural/PostGeneration/AutoCablingDunGen.cs diff --git a/Content.Shared/Procedural/PostGeneration/BiomeDunGen.cs b/Content.Shared/Procedural/PostGeneration/BiomeDunGen.cs new file mode 100644 index 0000000000..e21e582211 --- /dev/null +++ b/Content.Shared/Procedural/PostGeneration/BiomeDunGen.cs @@ -0,0 +1,21 @@ +using Content.Shared.Maps; +using Content.Shared.Parallax.Biomes; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Procedural.PostGeneration; + +/// +/// Generates a biome on top of valid tiles, then removes the biome when done. +/// Only works if no existing biome is present. +/// +public sealed partial class BiomeDunGen : IDunGenLayer +{ + [DataField(required: true)] + public ProtoId BiomeTemplate; + + /// + /// creates a biome only on the specified tiles + /// + [DataField] + public HashSet>? TileMask; +} diff --git a/Content.Shared/Procedural/PostGeneration/BiomeMarkerLayerDunGen.cs b/Content.Shared/Procedural/PostGeneration/BiomeMarkerLayerDunGen.cs new file mode 100644 index 0000000000..af5d7c5d8f --- /dev/null +++ b/Content.Shared/Procedural/PostGeneration/BiomeMarkerLayerDunGen.cs @@ -0,0 +1,19 @@ +using Content.Shared.Random; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Procedural.PostGeneration; + +/// +/// Spawns the specified marker layer on top of the dungeon rooms. +/// +public sealed partial class BiomeMarkerLayerDunGen : IDunGenLayer +{ + /// + /// How many times to spawn marker layers; can duplicate. + /// + [DataField] + public int Count = 6; + + [DataField(required: true)] + public ProtoId MarkerTemplate; +} diff --git a/Content.Shared/Procedural/DungeonLayers/BoundaryWallDunGen.cs b/Content.Shared/Procedural/PostGeneration/BoundaryWallDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/BoundaryWallDunGen.cs rename to Content.Shared/Procedural/PostGeneration/BoundaryWallDunGen.cs diff --git a/Content.Shared/Procedural/DungeonLayers/CornerClutterDunGen.cs b/Content.Shared/Procedural/PostGeneration/CornerClutterDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/CornerClutterDunGen.cs rename to Content.Shared/Procedural/PostGeneration/CornerClutterDunGen.cs diff --git a/Content.Shared/Procedural/DungeonLayers/CorridorClutterDunGen.cs b/Content.Shared/Procedural/PostGeneration/CorridorClutterDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/CorridorClutterDunGen.cs rename to Content.Shared/Procedural/PostGeneration/CorridorClutterDunGen.cs diff --git a/Content.Shared/Procedural/DungeonLayers/CorridorDecalSkirtingDunGen.cs b/Content.Shared/Procedural/PostGeneration/CorridorDecalSkirtingDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/CorridorDecalSkirtingDunGen.cs rename to Content.Shared/Procedural/PostGeneration/CorridorDecalSkirtingDunGen.cs diff --git a/Content.Shared/Procedural/DungeonLayers/CorridorDunGen.cs b/Content.Shared/Procedural/PostGeneration/CorridorDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/CorridorDunGen.cs rename to Content.Shared/Procedural/PostGeneration/CorridorDunGen.cs diff --git a/Content.Shared/Procedural/DungeonLayers/DungeonEntranceDunGen.cs b/Content.Shared/Procedural/PostGeneration/DungeonEntranceDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/DungeonEntranceDunGen.cs rename to Content.Shared/Procedural/PostGeneration/DungeonEntranceDunGen.cs diff --git a/Content.Shared/Procedural/DungeonLayers/EntranceFlankDunGen.cs b/Content.Shared/Procedural/PostGeneration/EntranceFlankDunGen.cs similarity index 93% rename from Content.Shared/Procedural/DungeonLayers/EntranceFlankDunGen.cs rename to Content.Shared/Procedural/PostGeneration/EntranceFlankDunGen.cs index cd6cf169fc..f9be6caf6a 100644 --- a/Content.Shared/Procedural/DungeonLayers/EntranceFlankDunGen.cs +++ b/Content.Shared/Procedural/PostGeneration/EntranceFlankDunGen.cs @@ -1,6 +1,5 @@ using Content.Shared.EntityTable; using Content.Shared.Maps; -using Content.Shared.Storage; using Robust.Shared.Prototypes; namespace Content.Shared.Procedural.PostGeneration; diff --git a/Content.Shared/Procedural/DungeonLayers/ExternalWindowDunGen.cs b/Content.Shared/Procedural/PostGeneration/ExternalWindowDunGen.cs similarity index 94% rename from Content.Shared/Procedural/DungeonLayers/ExternalWindowDunGen.cs rename to Content.Shared/Procedural/PostGeneration/ExternalWindowDunGen.cs index 30b8302263..fc992ea7b8 100644 --- a/Content.Shared/Procedural/DungeonLayers/ExternalWindowDunGen.cs +++ b/Content.Shared/Procedural/PostGeneration/ExternalWindowDunGen.cs @@ -1,5 +1,6 @@ using Content.Shared.EntityTable; using Content.Shared.Maps; +using Content.Shared.Storage; using Robust.Shared.Prototypes; namespace Content.Shared.Procedural.PostGeneration; diff --git a/Content.Shared/Procedural/DungeonLayers/InternalWindowDunGen.cs b/Content.Shared/Procedural/PostGeneration/InternalWindowDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/InternalWindowDunGen.cs rename to Content.Shared/Procedural/PostGeneration/InternalWindowDunGen.cs diff --git a/Content.Shared/Procedural/DungeonLayers/JunctionDunGen.cs b/Content.Shared/Procedural/PostGeneration/JunctionDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/JunctionDunGen.cs rename to Content.Shared/Procedural/PostGeneration/JunctionDunGen.cs diff --git a/Content.Shared/Procedural/DungeonLayers/MiddleConnectionDunGen.cs b/Content.Shared/Procedural/PostGeneration/MiddleConnectionDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/MiddleConnectionDunGen.cs rename to Content.Shared/Procedural/PostGeneration/MiddleConnectionDunGen.cs diff --git a/Content.Shared/Procedural/DungeonLayers/RoomEntranceDunGen.cs b/Content.Shared/Procedural/PostGeneration/RoomEntranceDunGen.cs similarity index 93% rename from Content.Shared/Procedural/DungeonLayers/RoomEntranceDunGen.cs rename to Content.Shared/Procedural/PostGeneration/RoomEntranceDunGen.cs index f0fea57588..1436f7473d 100644 --- a/Content.Shared/Procedural/DungeonLayers/RoomEntranceDunGen.cs +++ b/Content.Shared/Procedural/PostGeneration/RoomEntranceDunGen.cs @@ -1,5 +1,6 @@ using Content.Shared.EntityTable; using Content.Shared.Maps; +using Content.Shared.Storage; using Robust.Shared.Prototypes; namespace Content.Shared.Procedural.PostGeneration; diff --git a/Content.Shared/Procedural/DungeonLayers/SplineDungeonConnectorDunGen.cs b/Content.Shared/Procedural/PostGeneration/SplineDungeonConnectorDunGen.cs similarity index 83% rename from Content.Shared/Procedural/DungeonLayers/SplineDungeonConnectorDunGen.cs rename to Content.Shared/Procedural/PostGeneration/SplineDungeonConnectorDunGen.cs index 4a4bc0b36a..d2f5a2126a 100644 --- a/Content.Shared/Procedural/DungeonLayers/SplineDungeonConnectorDunGen.cs +++ b/Content.Shared/Procedural/PostGeneration/SplineDungeonConnectorDunGen.cs @@ -1,7 +1,7 @@ using Content.Shared.Maps; using Robust.Shared.Prototypes; -namespace Content.Shared.Procedural.DungeonLayers; +namespace Content.Shared.Procedural.PostGeneration; /// /// Connects dungeons via points that get subdivided. @@ -18,11 +18,11 @@ public sealed partial class SplineDungeonConnectorDunGen : IDunGenLayer /// Will divide the distance between the start and end points so that no subdivision is more than these metres away. /// [DataField] - public int DivisionDistance = 20; + public int DivisionDistance = 10; /// /// How much each subdivision can vary from the middle. /// [DataField] - public float VarianceMax = 0.15f; + public float VarianceMax = 0.35f; } diff --git a/Content.Shared/Procedural/DungeonLayers/WallMountDunGen.cs b/Content.Shared/Procedural/PostGeneration/WallMountDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/WallMountDunGen.cs rename to Content.Shared/Procedural/PostGeneration/WallMountDunGen.cs diff --git a/Content.Shared/Procedural/DungeonLayers/WormCorridorDunGen.cs b/Content.Shared/Procedural/PostGeneration/WormCorridorDunGen.cs similarity index 100% rename from Content.Shared/Procedural/DungeonLayers/WormCorridorDunGen.cs rename to Content.Shared/Procedural/PostGeneration/WormCorridorDunGen.cs diff --git a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeModPrototype.cs b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeModPrototype.cs index 10106cd666..e84223ed1f 100644 --- a/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeModPrototype.cs +++ b/Content.Shared/Salvage/Expeditions/Modifiers/SalvageBiomeModPrototype.cs @@ -1,4 +1,6 @@ +using Content.Shared.Parallax.Biomes; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Salvage.Expeditions.Modifiers; @@ -24,6 +26,6 @@ public sealed partial class SalvageBiomeModPrototype : IPrototype, ISalvageMod [DataField("weather")] public bool Weather = true; - [DataField("biome", required: true)] - public EntProtoId? BiomePrototype; + [DataField("biome", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] + public string? BiomePrototype; } diff --git a/Resources/Locale/en-US/procedural/biome.ftl b/Resources/Locale/en-US/procedural/biome.ftl index 7c2a88a696..d24ec7d72e 100644 --- a/Resources/Locale/en-US/procedural/biome.ftl +++ b/Resources/Locale/en-US/procedural/biome.ftl @@ -1,2 +1,6 @@ +cmd-biome_clear-desc = Clears a biome entirely +cmd-biome_clear-help = biome_clear cmd-biome_addlayer-desc = Adds another biome layer cmd-biome_addlayer-help = biome_addlayer [seed offset] +cmd-biome_addmarkerlayer-desc = Adds another biome marker layer +cmd-biome_addmarkerlayer-help = biome_addmarkerlayer diff --git a/Resources/Prototypes/Entities/Tiles/water.yml b/Resources/Prototypes/Entities/Tiles/water.yml index 357bc9f98c..c488df231b 100644 --- a/Resources/Prototypes/Entities/Tiles/water.yml +++ b/Resources/Prototypes/Entities/Tiles/water.yml @@ -17,14 +17,10 @@ drawdepth: BelowFloor layers: - state: shoreline_water - - type: BiomeForceUnload - #- type: ContainerContainer - # containers: - # solution@pool: !type:ContainerSlot - type: SolutionContainerManager solutions: pool: - maxVol: 9999999 #.inf seems to break the whole yaml file, but would definitely be preferable. + maxVol: 9999999 #.inf seems to break the whole yaml file, but would definitely be preferable. reagents: - ReagentId: Water Quantity: 9999999 diff --git a/Resources/Prototypes/Procedural/Magnet/asteroid.yml b/Resources/Prototypes/Procedural/Magnet/asteroid.yml index f838104f3c..a77a6b287b 100644 --- a/Resources/Prototypes/Procedural/Magnet/asteroid.yml +++ b/Resources/Prototypes/Procedural/Magnet/asteroid.yml @@ -18,9 +18,8 @@ lacunarity: 2 # Generate biome - - !type:PrototypeDunGen - proto: Asteroid - inheritDungeons: All + - !type:BiomeDunGen + biomeTemplate: Asteroid - !type:OreDunGen replacement: AsteroidRock entity: AsteroidRockGibtonite @@ -48,9 +47,8 @@ lacunarity: 2 # Generate biome - - !type:PrototypeDunGen - proto: Asteroid - inheritDungeons: All + - !type:BiomeDunGen + biomeTemplate: Asteroid - !type:OreDunGen replacement: AsteroidRock entity: AsteroidRockGibtonite @@ -78,9 +76,8 @@ cellularDistanceFunction: Euclidean # Generate biome - - !type:PrototypeDunGen - proto: Asteroid - inheritDungeons: All + - !type:BiomeDunGen + biomeTemplate: Asteroid - !type:OreDunGen replacement: AsteroidRock entity: AsteroidRockGibtonite @@ -107,9 +104,8 @@ lacunarity: 2 # Generate biome - - !type:PrototypeDunGen - proto: Asteroid - inheritDungeons: All + - !type:BiomeDunGen + biomeTemplate: Asteroid - !type:OreDunGen replacement: AsteroidRock entity: AsteroidRockGibtonite diff --git a/Resources/Prototypes/Procedural/Magnet/space_debris.yml b/Resources/Prototypes/Procedural/Magnet/space_debris.yml index a9bd823e6b..11c1afdabe 100644 --- a/Resources/Prototypes/Procedural/Magnet/space_debris.yml +++ b/Resources/Prototypes/Procedural/Magnet/space_debris.yml @@ -36,9 +36,8 @@ gain: 0.5 # Generate biome - - !type:PrototypeDunGen - proto: SpaceDebris - inheritDungeons: All + - !type:BiomeDunGen + biomeTemplate: SpaceDebris - type: dungeonConfig id: ChunkDebrisSmall @@ -78,6 +77,5 @@ gain: 0.5 # Generate biome - - !type:PrototypeDunGen - proto: SpaceDebris - inheritDungeons: All + - !type:BiomeDunGen + biomeTemplate: SpaceDebris diff --git a/Resources/Prototypes/Procedural/Magnet/space_debris_templates.yml b/Resources/Prototypes/Procedural/Magnet/space_debris_templates.yml index e181f89268..47aa47fce4 100644 --- a/Resources/Prototypes/Procedural/Magnet/space_debris_templates.yml +++ b/Resources/Prototypes/Procedural/Magnet/space_debris_templates.yml @@ -1,8 +1,8 @@ # Asteroid -- type: dungeonConfig +- type: biomeTemplate id: SpaceDebris layers: - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer threshold: 0.20 noise: seed: 0 @@ -23,7 +23,7 @@ - WallReinforced - WallSolid - WallSolid - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer threshold: 0.5 noise: seed: 0 @@ -41,7 +41,7 @@ - Grille - Grille - GrilleBroken - - !type:SampleDecalDunGen + - !type:BiomeDecalLayer allowedTiles: - FloorSteel threshold: -0.5 @@ -56,7 +56,7 @@ - DirtMedium - DirtMedium - DirtLight - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer threshold: 0.45 noise: seed: 1 @@ -71,7 +71,7 @@ - FloorSteel entities: - SalvageSpawnerStructuresVarious - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer allowedTiles: - FloorSteel - Plating @@ -85,7 +85,7 @@ - SalvageSpawnerScrapCommon - SalvageSpawnerScrapCommon - SalvageSpawnerScrapCommon75 - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer allowedTiles: - FloorSteel - Plating @@ -97,7 +97,7 @@ - SalvageSpawnerTreasure - SalvageSpawnerTreasure - SalvageSpawnerTreasureValuable - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer allowedTiles: - FloorSteel - Plating diff --git a/Resources/Prototypes/Procedural/biome_markers.yml b/Resources/Prototypes/Procedural/biome_markers.yml index ef28b314ca..5c4fdeb178 100644 --- a/Resources/Prototypes/Procedural/biome_markers.yml +++ b/Resources/Prototypes/Procedural/biome_markers.yml @@ -1,122 +1,67 @@ -- type: dungeonConfig +- type: biomeMarkerLayer id: Lizards - layers: - - !type:ChunkDunGen - size: 128 - - !type:EntityTableDunGen - table: !type:EntSelector - id: MobLizard - amount: !type:RangeNumberSelector - range: 3, 5 + prototype: MobLizard + minGroupSize: 3 + maxGroupSize: 5 - -- type: dungeonConfig +- type: biomeMarkerLayer id: WatchersLavaland - layers: - - !type:ChunkDunGen - size: 128 - - !type:EntityTableDunGen - table: !type:EntSelector - id: MobWatcherLavaland - amount: !type:RangeNumberSelector - range: 3, 3 + prototype: MobWatcherLavaland + minGroupSize: 3 + maxGroupSize: 3 -- type: dungeonConfig +- type: biomeMarkerLayer id: WatchersIcewing - layers: - - !type:ChunkDunGen - size: 128 - - !type:EntityTableDunGen - table: !type:EntSelector - id: MobWatcherIcewing - amount: !type:RangeNumberSelector - range: 3, 3 + prototype: MobWatcherIcewing + minGroupSize: 3 + maxGroupSize: 3 -- type: dungeonConfig +- type: biomeMarkerLayer id: WatchersMagmawing - layers: - - !type:ChunkDunGen - size: 128 - - !type:EntityTableDunGen - table: !type:EntSelector - id: MobWatcherMagmawing - amount: !type:RangeNumberSelector - range: 3, 3 + prototype: MobWatcherMagmawing + minGroupSize: 3 + maxGroupSize: 3 -- type: dungeonConfig +- type: biomeMarkerLayer id: Cows - layers: - - !type:ChunkDunGen - size: 128 - - !type:EntityTableDunGen - table: !type:EntSelector - id: MobCow - amount: !type:RangeNumberSelector - range: 1, 2 + prototype: MobCow + minGroupSize: 1 + maxGroupSize: 2 -- type: dungeonConfig +- type: biomeMarkerLayer id: Chickens - layers: - - !type:ChunkDunGen - size: 128 - - !type:EntityTableDunGen - table: !type:EntSelector - id: MobChicken - amount: !type:RangeNumberSelector - range: 1, 2 + prototype: MobChicken + minGroupSize: 1 + maxGroupSize: 2 -- type: dungeonConfig +- type: biomeMarkerLayer id: Pigs - layers: - - !type:ChunkDunGen - size: 128 - - !type:EntityTableDunGen - table: !type:EntSelector - id: MobPig - amount: !type:RangeNumberSelector - range: 1, 2 + prototype: MobPig + minGroupSize: 1 + maxGroupSize: 2 -- type: dungeonConfig +- type: biomeMarkerLayer id: Foxes - layers: - - !type:ChunkDunGen - size: 128 - - !type:EntityTableDunGen - table: !type:EntSelector - id: MobFox - amount: !type:RangeNumberSelector - range: 1, 1 + prototype: MobFox + minGroupSize: 1 + maxGroupSize: 1 -- type: dungeonConfig +- type: biomeMarkerLayer id: Goats - layers: - - !type:ChunkDunGen - size: 128 - - !type:EntityTableDunGen - table: !type:EntSelector - id: MobGoat - amount: !type:RangeNumberSelector - range: 1, 1 + prototype: MobGoat + minGroupSize: 1 + maxGroupSize: 1 -- type: dungeonConfig - id: Carps - layers: - - !type:ChunkDunGen - size: 128 - - !type:EntityTableDunGen - table: !type:EntSelector - id: MobCarp - amount: !type:RangeNumberSelector - range: 1, 2 - -- type: dungeonConfig +# TODO: Needs to be more robust +- type: biomeMarkerLayer id: Xenos - layers: - - !type:ChunkDunGen - size: 128 - - !type:EntityTableDunGen - table: !type:EntSelector - id: MobXeno - amount: !type:RangeNumberSelector - range: 1, 2 + prototype: MobXeno +- type: biomeMarkerLayer + id: Carps + prototype: MobCarpDungeon + + +#- type: biomeMarkerLayer +# id: Experiment +# proto: DungeonMarkerExperiment diff --git a/Resources/Prototypes/Procedural/biome_ore_templates.yml b/Resources/Prototypes/Procedural/biome_ore_templates.yml index 16b2c7e64f..ae033230b6 100644 --- a/Resources/Prototypes/Procedural/biome_ore_templates.yml +++ b/Resources/Prototypes/Procedural/biome_ore_templates.yml @@ -1,127 +1,146 @@ # Low value -- type: dungeonConfig +- type: biomeMarkerLayer id: OreIron - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockTin - count: 30 - minGroupSize: 10 - maxGroupSize: 15 + entityMask: + AsteroidRock: AsteroidRockTin + WallRock: WallRockTin + WallRockBasalt: WallRockBasaltTin + WallRockChromite: WallRockChromiteTin + WallRockSand: WallRockSandTin + WallRockSnow: WallRockSnowTin + maxCount: 30 + minGroupSize: 10 + maxGroupSize: 15 + radius: 4 -- type: dungeonConfig +- type: biomeMarkerLayer id: OreQuartz - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockQuartz - count: 30 - minGroupSize: 10 - maxGroupSize: 15 + entityMask: + AsteroidRock: AsteroidRockQuartz + WallRock: WallRockQuartz + WallRockBasalt: WallRockBasaltQuartz + WallRockChromite: WallRockChromiteQuartz + WallRockSnow: WallRockSnowQuartz + maxCount: 30 + minGroupSize: 10 + maxGroupSize: 15 + radius: 4 -- type: dungeonConfig +- type: biomeMarkerLayer id: OreCoal - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockCoal - count: 30 - minGroupSize: 8 - maxGroupSize: 12 + entityMask: + AsteroidRock: AsteroidRockCoal + WallRock: WallRockCoal + WallRockBasalt: WallRockBasaltCoal + WallRockChromite: WallRockChromiteCoal + WallRockSand: WallRockSandCoal + WallRockSnow: WallRockSnowCoal + maxCount: 30 + minGroupSize: 8 + maxGroupSize: 12 + radius: 4 -- type: dungeonConfig +- type: biomeMarkerLayer id: OreSalt - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockSalt - count: 30 - minGroupSize: 8 - maxGroupSize: 12 + entityMask: + AsteroidRock: AsteroidRockSalt + WallRock: WallRockSalt + WallRockBasalt: WallRockBasaltSalt + WallRockChromite: WallRockChromiteSalt + WallRockSand: WallRockSandSalt + WallRockSnow: WallRockSnowSalt + maxCount: 30 + minGroupSize: 8 + maxGroupSize: 12 + radius: 4 # Medium value # Gold -- type: dungeonConfig +- type: biomeMarkerLayer id: OreGold - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockGold - count: 20 - minGroupSize: 5 - maxGroupSize: 10 + entityMask: + AsteroidRock: AsteroidRockGold + WallRock: WallRockGold + WallRockBasalt: WallRockBasaltGold + WallRockChromite: WallRockChromiteGold + WallRockSand: WallRockSandGold + WallRockSnow: WallRockSnowGold + maxCount: 20 + minGroupSize: 5 + maxGroupSize: 10 + radius: 4 # Silver -- type: dungeonConfig +- type: biomeMarkerLayer id: OreSilver - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockSilver - count: 20 - minGroupSize: 5 - maxGroupSize: 10 + entityMask: + AsteroidRock: AsteroidRockSilver + WallRock: WallRockSilver + WallRockBasalt: WallRockBasaltSilver + WallRockChromite: WallRockChromiteSilver + WallRockSand: WallRockSandSilver + WallRockSnow: WallRockSnowSilver + maxCount: 20 + minGroupSize: 5 + maxGroupSize: 10 + radius: 4 # High value # Plasma -- type: dungeonConfig +- type: biomeMarkerLayer id: OrePlasma - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockPlasma - count: 12 - minGroupSize: 4 - maxGroupSize: 8 + entityMask: + AsteroidRock: AsteroidRockPlasma + WallRock: WallRockPlasma + WallRockBasalt: WallRockBasaltPlasma + WallRockChromite: WallRockChromitePlasma + WallRockSand: WallRockSandPlasma + WallRockSnow: WallRockSnowPlasma + maxCount: 12 + minGroupSize: 4 + maxGroupSize: 8 + radius: 4 # Uranium -- type: dungeonConfig +- type: biomeMarkerLayer id: OreUranium - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockUranium - count: 15 - minGroupSize: 4 - maxGroupSize: 8 + entityMask: + AsteroidRock: AsteroidRockUranium + WallRock: WallRockUranium + WallRockBasalt: WallRockBasaltUranium + WallRockChromite: WallRockChromiteUranium + WallRockSand: WallRockSandUranium + WallRockSnow: WallRockSnowUranium + maxCount: 15 + minGroupSize: 4 + maxGroupSize: 8 + radius: 4 -- type: dungeonConfig +- type: biomeMarkerLayer id: OreDiamond - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockDiamond - count: 6 - minGroupSize: 1 - maxGroupSize: 2 + entityMask: + AsteroidRock: AsteroidRockDiamond + WallRock: WallRockDiamond + WallRockBasalt: WallRockBasaltDiamond + WallRockChromite: WallRockChromiteDiamond + WallRockSand: WallRockSandDiamond + WallRockSnow: WallRockSnowDiamond + maxCount: 6 + minGroupSize: 1 + maxGroupSize: 2 + radius: 4 # Artifact Fragment -- type: dungeonConfig +- type: biomeMarkerLayer id: OreArtifactFragment - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockArtifactFragment - count: 6 - minGroupSize: 1 - maxGroupSize: 2 + entityMask: + AsteroidRock: AsteroidRockArtifactFragment + WallRock: WallRockArtifactFragment + WallRockBasalt: WallRockBasaltArtifactFragment + WallRockChromite: WallRockChromiteArtifactFragment + WallRockSand: WallRockSandArtifactFragment + WallRockSnow: WallRockSnowArtifactFragment + maxCount: 6 + minGroupSize: 1 + maxGroupSize: 2 + radius: 4 diff --git a/Resources/Prototypes/Procedural/biome_ore_templates_low.yml b/Resources/Prototypes/Procedural/biome_ore_templates_low.yml index 9af10e3c2b..3b0e242446 100644 --- a/Resources/Prototypes/Procedural/biome_ore_templates_low.yml +++ b/Resources/Prototypes/Procedural/biome_ore_templates_low.yml @@ -1,127 +1,146 @@ # Low value -- type: dungeonConfig +- type: biomeMarkerLayer id: OreIronLow - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockTin - count: 20 - minGroupSize: 4 - maxGroupSize: 8 + entityMask: + AsteroidRock: AsteroidRockTin + WallRock: WallRockTin + WallRockBasalt: WallRockBasaltTin + WallRockChromite: WallRockChromiteTin + WallRockSand: WallRockSandTin + WallRockSnow: WallRockSnowTin + maxCount: 20 + minGroupSize: 4 + maxGroupSize: 8 + radius: 4 -- type: dungeonConfig +- type: biomeMarkerLayer id: OreQuartzLow - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockQuartz - count: 20 - minGroupSize: 4 - maxGroupSize: 8 + entityMask: + AsteroidRock: AsteroidRockQuartz + WallRock: WallRockQuartz + WallRockBasalt: WallRockBasaltQuartz + WallRockChromite: WallRockChromiteQuartz + WallRockSnow: WallRockSnowQuartz + maxCount: 20 + minGroupSize: 4 + maxGroupSize: 8 + radius: 4 -- type: dungeonConfig +- type: biomeMarkerLayer id: OreCoalLow - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockCoal - count: 20 - minGroupSize: 4 - maxGroupSize: 6 + entityMask: + AsteroidRock: AsteroidRockCoal + WallRock: WallRockCoal + WallRockBasalt: WallRockBasaltCoal + WallRockChromite: WallRockChromiteCoal + WallRockSand: WallRockSandCoal + WallRockSnow: WallRockSnowCoal + maxCount: 20 + minGroupSize: 4 + maxGroupSize: 6 + radius: 4 -- type: dungeonConfig +- type: biomeMarkerLayer id: OreSaltLow - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockSalt - count: 15 - minGroupSize: 4 - maxGroupSize: 6 + entityMask: + AsteroidRock: AsteroidRockSalt + WallRock: WallRockSalt + WallRockBasalt: WallRockBasaltSalt + WallRockChromite: WallRockChromiteSalt + WallRockSand: WallRockSandSalt + WallRockSnow: WallRockSnowSalt + maxCount: 15 + minGroupSize: 4 + maxGroupSize: 6 + radius: 4 # Medium value # Gold -- type: dungeonConfig +- type: biomeMarkerLayer id: OreGoldLow - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockGold - count: 12 - minGroupSize: 2 - maxGroupSize: 5 + entityMask: + AsteroidRock: AsteroidRockGold + WallRock: WallRockGold + WallRockBasalt: WallRockBasaltGold + WallRockChromite: WallRockChromiteGold + WallRockSand: WallRockSandGold + WallRockSnow: WallRockSnowGold + maxCount: 10 + minGroupSize: 2 + maxGroupSize: 5 + radius: 4 # Silver -- type: dungeonConfig +- type: biomeMarkerLayer id: OreSilverLow - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockSilver - count: 12 - minGroupSize: 2 - maxGroupSize: 5 + entityMask: + AsteroidRock: AsteroidRockSilver + WallRock: WallRockSilver + WallRockBasalt: WallRockBasaltSilver + WallRockChromite: WallRockChromiteSilver + WallRockSand: WallRockSandSilver + WallRockSnow: WallRockSnowSilver + maxCount: 10 + minGroupSize: 2 + maxGroupSize: 5 + radius: 4 # High value # Plasma -- type: dungeonConfig +- type: biomeMarkerLayer id: OrePlasmaLow - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockPlasma - count: 6 - minGroupSize: 2 - maxGroupSize: 4 + entityMask: + AsteroidRock: AsteroidRockPlasma + WallRock: WallRockPlasma + WallRockBasalt: WallRockBasaltPlasma + WallRockChromite: WallRockChromitePlasma + WallRockSand: WallRockSandPlasma + WallRockSnow: WallRockSnowPlasma + maxCount: 6 + minGroupSize: 2 + maxGroupSize: 4 + radius: 4 # Uranium -- type: dungeonConfig +- type: biomeMarkerLayer id: OreUraniumLow - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockUranium - count: 7 - minGroupSize: 2 - maxGroupSize: 4 + entityMask: + AsteroidRock: AsteroidRockUranium + WallRock: WallRockUranium + WallRockBasalt: WallRockBasaltUranium + WallRockChromite: WallRockChromiteUranium + WallRockSand: WallRockSandUranium + WallRockSnow: WallRockSnowUranium + maxCount: 7 + minGroupSize: 2 + maxGroupSize: 4 + radius: 4 -- type: dungeonConfig +- type: biomeMarkerLayer id: OreDiamondLow - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockDiamond - count: 3 - minGroupSize: 1 - maxGroupSize: 2 + entityMask: + AsteroidRock: AsteroidRockDiamond + WallRock: WallRockDiamond + WallRockBasalt: WallRockBasaltDiamond + WallRockChromite: WallRockChromiteDiamond + WallRockSand: WallRockSandDiamond + WallRockSnow: WallRockSnowDiamond + maxCount: 3 + minGroupSize: 1 + maxGroupSize: 2 + radius: 4 # Artifact Fragment -- type: dungeonConfig +- type: biomeMarkerLayer id: OreArtifactFragmentLow - layers: - - !type:ChunkDunGen - size: 128 - - !type:OreDunGen - replacement: WallRock - entity: WallRockArtifactFragment - count: 3 - minGroupSize: 1 - maxGroupSize: 2 + entityMask: + AsteroidRock: AsteroidRockArtifactFragment + WallRock: WallRockArtifactFragment + WallRockBasalt: WallRockBasaltArtifactFragment + WallRockChromite: WallRockChromiteArtifactFragment + WallRockSand: WallRockSandArtifactFragment + WallRockSnow: WallRockSnowArtifactFragment + maxCount: 3 + minGroupSize: 1 + maxGroupSize: 2 + radius: 4 diff --git a/Resources/Prototypes/Procedural/biome_templates.yml b/Resources/Prototypes/Procedural/biome_templates.yml index 747660dc8f..d630ebad48 100644 --- a/Resources/Prototypes/Procedural/biome_templates.yml +++ b/Resources/Prototypes/Procedural/biome_templates.yml @@ -1,551 +1,435 @@ +# Contains several biomes +- type: biomeTemplate + id: Continental + layers: + - !type:BiomeMetaLayer + template: Lava + - !type:BiomeMetaLayer + template: Caves + threshold: -0.5 + noise: + frequency: 0.001 + noiseType: OpenSimplex2 + fractalType: FBm + octaves: 2 + lacunarity: 2 + - !type:BiomeMetaLayer + template: Grasslands + threshold: 0 + noise: + frequency: 0.001 + noiseType: OpenSimplex2 + fractalType: FBm + octaves: 2 + lacunarity: 2 + - !type:BiomeMetaLayer + template: Snow + threshold: 0.5 + noise: + frequency: 0.001 + noiseType: OpenSimplex2 + fractalType: FBm + octaves: 2 + lacunarity: 2 + # Desert -- type: entity - id: BiomeLowDesert - categories: [ HideSpawnMenu ] - components: - - type: Biome - layers: - terrain: - dungeon: LowDesertTerrain - -- type: dungeonConfig - id: LowDesertTerrain +# TODO: Water in desert +- type: biomeTemplate + id: LowDesert layers: - - !type:ChunkDunGen - - !type:PrototypeDunGen - proto: LowDesertTiles - inheritDungeons: All - - !type:PrototypeDunGen - proto: LowDesertEntities - inheritDungeons: All + - !type:BiomeEntityLayer + threshold: 0.95 + noise: + seed: 0 + frequency: 2 + noiseType: OpenSimplex2 + allowedTiles: + - FloorAsteroidSand + entities: + - FloraRockSolid + # Large rock areas + - !type:BiomeEntityLayer + threshold: -0.20 + noise: + seed: 0 + frequency: 0.04 + noiseType: Cellular + fractalType: FBm + octaves: 5 + lacunarity: 2 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + allowedTiles: + - FloorAsteroidSand + entities: + - WallRockSand + - !type:BiomeDummyLayer + id: Loot + # Fill layer + - !type:BiomeTileLayer + threshold: -1 + tile: FloorAsteroidSand -- type: dungeonConfig - id: LowDesertTiles - returnReserved: false +# Grass +- type: biomeTemplate + id: Grasslands layers: - - !type:SampleTileDunGen - threshold: -1 - tile: FloorAsteroidSand - -- type: dungeonConfig - id: LowDesertEntities - reserveTiles: true - layers: - - !type:SampleEntityDunGen - threshold: 0.95 - noise: - seed: 0 - frequency: 2 - noiseType: OpenSimplex2 - allowedTiles: - - FloorAsteroidSand - entities: - - FloraRockSolid - # Large rock areas - - !type:SampleEntityDunGen - threshold: -0.20 - noise: - seed: 0 - frequency: 0.04 - noiseType: Cellular - fractalType: FBm - octaves: 5 - lacunarity: 2 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - allowedTiles: - - FloorAsteroidSand - entities: - - WallRockSand - - -- type: entity - id: BiomeGrasslands - categories: [ HideSpawnMenu ] - components: - - type: Biome - layers: - terrain: - dungeon: GrasslandsTerrain - -- type: dungeonConfig - id: GrasslandsTerrain - layers: - - !type:ChunkDunGen - - !type:PrototypeDunGen - proto: GrasslandsTiles - inheritDungeons: All - - !type:PrototypeDunGen - proto: GrasslandsEntities - inheritDungeons: All - - !type:PrototypeDunGen - proto: GrasslandsDecals - inheritDungeons: All - -- type: dungeonConfig - id: GrasslandsTiles - returnReserved: false - layers: - # Water sand - - !type:SampleTileDunGen - tile: FloorPlanetDirt - threshold: 0.95 - noise: - seed: 3 - noiseType: OpenSimplex2 - frequency: 0.003 - lacunarity: 1.50 - fractalType: Ridged - octaves: 1 - # Rock formation sand - - !type:SampleTileDunGen - tile: FloorPlanetDirt - threshold: -0.30 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.05 - lacunarity: 2 - fractalType: FBm - octaves: 5 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - - !type:SampleTileDunGen - threshold: -0.80 - tile: FloorPlanetGrass - noise: - seed: 0 - noiseType: OpenSimplex2 - lacunarity: 1.50 - frequency: 0.02 - fractalType: None - octaves: 1 - # Fill remainder with dirt. - - !type:SampleTileDunGen - threshold: -1.0 - tile: FloorPlanetDirt - -- type: dungeonConfig - id: GrasslandsEntities - reserveTiles: true - layers: - # Water - - !type:SampleEntityDunGen - allowedTiles: - - FloorPlanetGrass - - FloorPlanetDirt - threshold: 0.95 - noise: - seed: 3 - noiseType: OpenSimplex2 - frequency: 0.003 - lacunarity: 1.50 - fractalType: Ridged - octaves: 1 - entities: - - FloorWaterEntity - # Rock formations - - !type:SampleEntityDunGen - allowedTiles: - - FloorPlanetGrass - - FloorPlanetDirt - threshold: -0.30 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.05 - lacunarity: 2 - fractalType: FBm - octaves: 5 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - entities: - - WallRock - - !type:SampleEntityDunGen - threshold: 0.5 - noise: - seed: 0 - noiseType: OpenSimplex2 - fractalType: FBm - frequency: 2 - allowedTiles: - - FloorPlanetGrass - entities: - - FloraTree - - FloraTreeLarge - -- type: dungeonConfig - id: GrasslandsDecals - returnReserved: false - layers: - # Dense vegetation - - !type:SampleDecalDunGen - allowedTiles: - - FloorPlanetGrass - divisions: 1 - threshold: -0.35 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.2 - fractalType: FBm - octaves: 5 - lacunarity: 2 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - decals: - - BushAOne - - BushATwo - - BushAThree - - BushCOne - - BushCTwo - - BushCThree - - !type:SampleDecalDunGen - allowedTiles: - - FloorPlanetGrass - noise: - seed: 0 - noiseType: OpenSimplex2 - frequency: 1 - divisions: 1 - threshold: 0.8 - decals: - - FlowersBROne - - FlowersBRTwo - - FlowersBRThree - # Sparse vegetation - - !type:SampleDecalDunGen - allowedTiles: - - FloorPlanetGrass - divisions: 2 - threshold: -0.50 - noise: - seed: 0 - noiseType: Cellular - frequency: 1 - decals: - - BushDOne - - BushDTwo - - BushDThree + # Sparse vegetation + - !type:BiomeDecalLayer + allowedTiles: + - FloorPlanetGrass + divisions: 2 + threshold: -0.50 + noise: + seed: 0 + noiseType: Cellular + frequency: 1 + decals: + - BushDOne + - BushDTwo + - BushDThree + - !type:BiomeDecalLayer + allowedTiles: + - FloorPlanetGrass + noise: + seed: 0 + noiseType: OpenSimplex2 + frequency: 1 + divisions: 1 + threshold: 0.8 + decals: + - FlowersBROne + - FlowersBRTwo + - FlowersBRThree + # Dense vegetation + - !type:BiomeDecalLayer + allowedTiles: + - FloorPlanetGrass + divisions: 1 + threshold: -0.35 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.2 + fractalType: FBm + octaves: 5 + lacunarity: 2 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + decals: + - BushAOne + - BushATwo + - BushAThree + - BushCOne + - BushCTwo + - BushCThree + - !type:BiomeEntityLayer + threshold: 0.5 + noise: + seed: 0 + noiseType: OpenSimplex2 + fractalType: FBm + frequency: 2 + allowedTiles: + - FloorPlanetGrass + entities: + - FloraTree + - FloraTreeLarge + # Rock formations + - !type:BiomeEntityLayer + allowedTiles: + - FloorPlanetGrass + - FloorPlanetDirt + threshold: -0.30 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.05 + lacunarity: 2 + fractalType: FBm + octaves: 5 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + entities: + - WallRock + - !type:BiomeDummyLayer + id: Loot + # Water + - !type:BiomeEntityLayer + allowedTiles: + - FloorPlanetGrass + - FloorPlanetDirt + threshold: 0.95 + noise: + seed: 3 + noiseType: OpenSimplex2 + frequency: 0.003 + lacunarity: 1.50 + fractalType: Ridged + octaves: 1 + entities: + - FloorWaterEntity + # Fill remainder with dirt. + - !type:BiomeTileLayer + threshold: -1.0 + tile: FloorPlanetDirt + - !type:BiomeTileLayer + threshold: -0.90 + tile: FloorPlanetGrass + noise: + seed: 0 + frequency: 0.02 + fractalType: None + # Water sand + - !type:BiomeTileLayer + tile: FloorPlanetDirt + threshold: 0.95 + noise: + seed: 3 + noiseType: OpenSimplex2 + frequency: 0.003 + lacunarity: 1.50 + fractalType: Ridged + octaves: 1 + # Rock formation sand + - !type:BiomeTileLayer + tile: FloorPlanetDirt + threshold: -0.30 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.05 + lacunarity: 2 + fractalType: FBm + octaves: 5 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 # Lava -- type: entity - id: BiomeLava - categories: [ HideSpawnMenu ] - components: - - type: Biome - layers: - terrain: - dungeon: LavaTerrain - -- type: dungeonConfig - id: LavaTerrain +- type: biomeTemplate + id: Lava layers: - - !type:ChunkDunGen - - !type:PrototypeDunGen - proto: LavaTiles - inheritDungeons: All - - !type:PrototypeDunGen - proto: LavaEntities - inheritDungeons: All - - !type:PrototypeDunGen - proto: LavaDecals - inheritDungeons: All - - !type:PrototypeDunGen - proto: LavaBasalt - inheritDungeons: All - -- type: dungeonConfig - id: LavaTiles - returnReserved: false - layers: - # Fill basalt - - !type:SampleTileDunGen - threshold: -1 - tile: FloorBasalt - -- type: dungeonConfig - id: LavaEntities - reserveTiles: true - layers: - - !type:SampleEntityDunGen - threshold: 0.2 - noise: - seed: 0 - frequency: 0.02 - fractalType: FBm - octaves: 5 - lacunarity: 2 - gain: 0.4 - allowedTiles: - - FloorBasalt - entities: - - FloorLavaEntity - # Rock formations - - !type:SampleEntityDunGen - allowedTiles: - - FloorBasalt - threshold: -0.30 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.05 - lacunarity: 2 - fractalType: FBm - octaves: 5 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - entities: - - WallRockBasalt - - !type:SampleEntityDunGen - threshold: 0.95 - noise: - seed: 0 - noiseType: OpenSimplex2 - frequency: 1 - allowedTiles: - - FloorBasalt - entities: - - FloraRockSolid - -- type: dungeonConfig - id: LavaDecals - returnReserved: false - layers: - - !type:SampleDecalDunGen - allowedTiles: - - FloorBasalt - threshold: 0.9 - divisions: 1 - noise: - seed: 1 - frequency: 1 - decals: - - Basalt1 - - Basalt2 - - Basalt3 - - Basalt4 - - Basalt5 - - Basalt6 - - Basalt7 - - Basalt8 - - Basalt9 - -- type: dungeonConfig - id: LavaBasalt - returnReserved: false - layers: - - !type:SampleEntityDunGen - threshold: 0.9 - noise: - frequency: 1 - seed: 2 - allowedTiles: - - FloorBasalt - entities: - - BasaltOne - - BasaltTwo - - BasaltThree - - BasaltFour - - BasaltFive + - !type:BiomeEntityLayer + threshold: 0.9 + noise: + frequency: 1 + seed: 2 + allowedTiles: + - FloorBasalt + entities: + - BasaltOne + - BasaltTwo + - BasaltThree + - BasaltFour + - BasaltFive + - !type:BiomeDecalLayer + allowedTiles: + - FloorBasalt + threshold: 0.9 + divisions: 1 + noise: + seed: 1 + frequency: 1 + decals: + - Basalt1 + - Basalt2 + - Basalt3 + - Basalt4 + - Basalt5 + - Basalt6 + - Basalt7 + - Basalt8 + - Basalt9 + - !type:BiomeEntityLayer + threshold: 0.95 + noise: + seed: 0 + noiseType: OpenSimplex2 + frequency: 1 + allowedTiles: + - FloorBasalt + entities: + - FloraRockSolid + - !type:BiomeEntityLayer + threshold: 0.2 + noise: + seed: 0 + frequency: 0.02 + fractalType: FBm + octaves: 5 + lacunarity: 2 + gain: 0.4 + allowedTiles: + - FloorBasalt + entities: + - FloorLavaEntity + # Rock formations + - !type:BiomeEntityLayer + allowedTiles: + - FloorBasalt + threshold: -0.30 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.05 + lacunarity: 2 + fractalType: FBm + octaves: 5 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + entities: + - WallRockBasalt + - !type:BiomeDummyLayer + id: Loot + # Fill basalt + - !type:BiomeTileLayer + threshold: -1 + tile: FloorBasalt # Snow -- type: entity - id: BiomeSnow - categories: [ HideSpawnMenu ] - components: - - type: Biome - layers: - terrain: - dungeon: SnowTerrain - -- type: dungeonConfig - id: SnowTerrain +- type: biomeTemplate + id: Snow # Similar to Grasslands... but snow layers: - - !type:ChunkDunGen - - !type:PrototypeDunGen - proto: SnowTiles - inheritDungeons: All - - !type:PrototypeDunGen - proto: SnowEntities - inheritDungeons: All - - !type:PrototypeDunGen - proto: SnowDecals - inheritDungeons: All - -- type: dungeonConfig - id: SnowTiles - returnReserved: false - layers: - - !type:SampleTileDunGen - threshold: -0.7 - tile: FloorSnow - noise: - seed: 0 - frequency: 0.02 - fractalType: None - - !type:SampleTileDunGen - tile: FloorIce - threshold: -0.9 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.03 - lacunarity: 2 - fractalType: FBm - octaves: 5 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - - -- type: dungeonConfig - id: SnowEntities - reserveTiles: true - layers: - # Liquid plasma rivers. Ice moon baby - - !type:SampleEntityDunGen - allowedTiles: - - FloorSnow - - FloorIce - threshold: 0.95 - noise: - seed: 3 - noiseType: OpenSimplex2 - frequency: 0.003 - lacunarity: 1.50 - fractalType: Ridged - octaves: 1 - entities: - - FloorLiquidPlasmaEntity - # Rock formations - - !type:SampleEntityDunGen - allowedTiles: - - FloorSnow - threshold: -0.30 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.05 - lacunarity: 2 - fractalType: FBm - octaves: 5 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - entities: - - WallRockSnow - - !type:SampleEntityDunGen - threshold: 0.5 - noise: - seed: 0 - noiseType: OpenSimplex2 - fractalType: FBm - frequency: 2 - allowedTiles: - - FloorSnow - entities: - - FloraTreeSnow - -- type: dungeonConfig - id: SnowDecals - returnReserved: false - layers: - # Sparse vegetation - - !type:SampleDecalDunGen - allowedTiles: - - FloorSnow - divisions: 2 - threshold: -0.50 - noise: - seed: 0 - noiseType: Cellular - frequency: 1 - decals: - - grasssnowa1 - - grasssnowa2 - - grasssnowa3 - - grasssnowb1 - - grasssnowb2 - - grasssnowb3 - - grasssnowc1 - - grasssnowc2 - - grasssnowc3 - # Dense, bland grass - - !type:SampleDecalDunGen - allowedTiles: - - FloorSnow - divisions: 1 - threshold: -0.35 - noise: - seed: 0 - noiseType: Cellular - frequency: 0.2 - fractalType: FBm - octaves: 5 - lacunarity: 2 - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - decals: - - grasssnow - - grasssnow01 - - grasssnow02 - - grasssnow03 - - grasssnow04 - - grasssnow05 - - grasssnow06 - - grasssnow07 - - grasssnow08 - - grasssnow09 - - grasssnow10 - - grasssnow11 - - grasssnow12 - - grasssnow13 - # Little bit of coloured grass - - !type:SampleDecalDunGen - allowedTiles: - - FloorSnow - divisions: 1 - threshold: -0.0 - noise: - seed: 0 - noiseType: Cellular - frequency: 1 - fractalType: None - cellularDistanceFunction: Euclidean - cellularReturnType: Distance2 - decals: - - bushsnowa1 - - bushsnowa2 - - bushsnowa3 - - bushsnowb3 - - bushsnowb2 - - bushsnowb3 + # Sparse vegetation + - !type:BiomeDecalLayer + allowedTiles: + - FloorSnow + divisions: 2 + threshold: -0.50 + noise: + seed: 0 + noiseType: Cellular + frequency: 1 + decals: + - grasssnowa1 + - grasssnowa2 + - grasssnowa3 + - grasssnowb1 + - grasssnowb2 + - grasssnowb3 + - grasssnowc1 + - grasssnowc2 + - grasssnowc3 + # Dense, bland grass + - !type:BiomeDecalLayer + allowedTiles: + - FloorSnow + divisions: 1 + threshold: -0.35 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.2 + fractalType: FBm + octaves: 5 + lacunarity: 2 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + decals: + - grasssnow + - grasssnow01 + - grasssnow02 + - grasssnow03 + - grasssnow04 + - grasssnow05 + - grasssnow06 + - grasssnow07 + - grasssnow08 + - grasssnow09 + - grasssnow10 + - grasssnow11 + - grasssnow12 + - grasssnow13 + # Little bit of coloured grass + - !type:BiomeDecalLayer + allowedTiles: + - FloorSnow + divisions: 1 + threshold: -0.0 + noise: + seed: 0 + noiseType: Cellular + frequency: 1 + fractalType: None + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + decals: + - bushsnowa1 + - bushsnowa2 + - bushsnowa3 + - bushsnowb3 + - bushsnowb2 + - bushsnowb3 + - !type:BiomeEntityLayer + threshold: 0.5 + noise: + seed: 0 + noiseType: OpenSimplex2 + fractalType: FBm + frequency: 2 + allowedTiles: + - FloorSnow + entities: + - FloraTreeSnow + # Rock formations + - !type:BiomeEntityLayer + allowedTiles: + - FloorSnow + threshold: -0.30 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.05 + lacunarity: 2 + fractalType: FBm + octaves: 5 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + entities: + - WallRockSnow + # Ice tiles + - !type:BiomeTileLayer + tile: FloorIce + threshold: -0.9 + noise: + seed: 0 + noiseType: Cellular + frequency: 0.03 + lacunarity: 2 + fractalType: FBm + octaves: 5 + cellularDistanceFunction: Euclidean + cellularReturnType: Distance2 + # Liquid plasma rivers. Ice moon baby + - !type:BiomeEntityLayer + allowedTiles: + - FloorSnow + - FloorIce + threshold: 0.95 + noise: + seed: 3 + noiseType: OpenSimplex2 + frequency: 0.003 + lacunarity: 1.50 + fractalType: Ridged + octaves: 1 + entities: + - FloorLiquidPlasmaEntity + - !type:BiomeDummyLayer + id: Loot + - !type:BiomeTileLayer + threshold: -0.7 + tile: FloorSnow + noise: + seed: 0 + frequency: 0.02 + fractalType: None # Shadow -> Derived from lava -- type: entity - id: BiomeShadow - categories: [ HideSpawnMenu ] - components: - - type: Biome - layers: - terrain: - dungeon: ShadowTerrain - -- type: dungeonConfig - id: ShadowTerrain +- type: biomeTemplate + id: Shadow layers: - - !type:ChunkDunGen - - !type:PrototypeDunGen - proto: ShadowTiles - inheritDungeons: All - - !type:PrototypeDunGen - proto: ShadowEntities - inheritDungeons: All - -- type: dungeonConfig - id: ShadowEntities - reserveTiles: true - layers: - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer threshold: 0.70 noise: frequency: 1 @@ -558,7 +442,7 @@ - ShadowBasaltThree - ShadowBasaltFour - ShadowBasaltFive - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer threshold: 0.97 noise: frequency: 1 @@ -567,7 +451,7 @@ - FloorChromite entities: - CrystalPink - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer threshold: 0.97 noise: seed: 1 @@ -578,7 +462,7 @@ entities: - ShadowTree # Rock formations - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer threshold: -0.2 invert: true noise: @@ -593,7 +477,7 @@ entities: - WallRockChromite # chasm time - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer allowedTiles: - FloorChromite threshold: 0.2 @@ -606,43 +490,18 @@ gain: 0.4 entities: - FloorChromiteChasm - -- type: dungeonConfig - id: ShadowTiles - returnReserved: false - layers: - # Fill chromite - - !type:SampleTileDunGen - threshold: -1 - tile: FloorChromite + - !type:BiomeDummyLayer + id: Loot + # Fill chromite + - !type:BiomeTileLayer + threshold: -1 + tile: FloorChromite # Caves -- type: entity - id: BiomeCaves - categories: [ HideSpawnMenu ] - components: - - type: Biome - layers: - terrain: - dungeon: CavesTerrain - -- type: dungeonConfig - id: CavesTerrain +- type: biomeTemplate + id: Caves layers: - - !type:ChunkDunGen - - !type:PrototypeDunGen - proto: CavesTiles - inheritDungeons: All - - !type:RoofDunGen - - !type:PrototypeDunGen - proto: CavesEntities - inheritDungeons: All - -- type: dungeonConfig - id: CavesEntities - reserveTiles: true - layers: - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer threshold: 0.85 noise: seed: 2 @@ -658,7 +517,7 @@ - CrystalBlue - CrystalYellow - CrystalCyan - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer threshold: 0.95 noise: seed: 1 @@ -668,7 +527,7 @@ - FloorAsteroidSand entities: - FloraStalagmite - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer threshold: -0.5 invert: true noise: @@ -682,24 +541,17 @@ - FloorAsteroidSand entities: - WallRock - -- type: dungeonConfig - id: CavesTiles - returnReserved: false - layers: - - !type:SampleTileDunGen - threshold: -1.0 - tile: FloorAsteroidSand - -# Asteroid -- type: dungeonConfig - id: Asteroid - layers: - - !type:SampleTileDunGen + - !type:BiomeDummyLayer + id: Loot + - !type:BiomeTileLayer threshold: -1.0 tile: FloorAsteroidSand - reserveTiles: false - - !type:SampleEntityDunGen + +# Asteroid +- type: biomeTemplate + id: Asteroid + layers: + - !type:BiomeEntityLayer threshold: 0.85 noise: seed: 2 @@ -715,7 +567,7 @@ - CrystalBlue - CrystalYellow - CrystalCyan - - !type:SampleEntityDunGen + - !type:BiomeEntityLayer threshold: 0.95 noise: seed: 1 @@ -725,8 +577,7 @@ - FloorAsteroidSand entities: - FloraStalagmite - - !type:SampleEntityDunGen - reserveTiles: false + - !type:BiomeEntityLayer threshold: -0.6 invert: true noise: @@ -740,3 +591,6 @@ - FloorAsteroidSand entities: - AsteroidRock + - !type:BiomeTileLayer + threshold: -1.0 + tile: FloorAsteroidSand diff --git a/Resources/Prototypes/Procedural/dungeon_configs.yml b/Resources/Prototypes/Procedural/dungeon_configs.yml index 18d50ec6cd..5da9592995 100644 --- a/Resources/Prototypes/Procedural/dungeon_configs.yml +++ b/Resources/Prototypes/Procedural/dungeon_configs.yml @@ -79,9 +79,6 @@ id: Haunted layers: - !type:PrefabDunGen - roomWhitelist: - tags: - - Haunted presets: - Bucket - Wow diff --git a/Resources/Prototypes/Procedural/salvage_loot.yml b/Resources/Prototypes/Procedural/salvage_loot.yml index 289fa5fd57..cf23601978 100644 --- a/Resources/Prototypes/Procedural/salvage_loot.yml +++ b/Resources/Prototypes/Procedural/salvage_loot.yml @@ -3,108 +3,108 @@ - type: salvageLoot id: SalvageLoot loots: - - !type:RandomSpawnsLoot - entries: - - proto: AdvMopItem - prob: 0.5 - - proto: AmmoTechFabCircuitboard - cost: 2 - - proto: AutolatheMachineCircuitboard - cost: 2 - - proto: BiomassReclaimerMachineCircuitboard - cost: 2 - - proto: BluespaceBeaker - cost: 2 - - proto: CyborgEndoskeleton - cost: 3 - prob: 0.5 - - proto: ChemDispenserMachineCircuitboard - cost: 2 - - proto: CircuitImprinter - cost: 2 - - proto: CloningConsoleComputerCircuitboard - cost: 2 - - proto: CloningPodMachineCircuitboard - cost: 2 - - proto: ChemistryBottleCognizine - - proto: FoodBoxDonkpocketCarp - prob: 0.5 - - proto: CrateSalvageEquipment - cost: 3 - prob: 0.5 - - proto: GasRecycler - cost: 2 - - proto: GeneratorRTG - cost: 5 - - proto: GravityGeneratorMini - cost: 2 - - proto: GyroscopeUnanchored - cost: 2 - prob: 0.1 - - proto: MedicalScannerMachineCircuitboard - cost: 2 - - proto: NuclearBombKeg - cost: 5 - - proto: ChemistryBottleOmnizine - prob: 0.5 - - proto: PortableGeneratorPacman - cost: 2 - - proto: PortableGeneratorSuperPacman - cost: 3 - - proto: PowerCellAntiqueProto - cost: 5 - prob: 0.5 - - proto: ProtolatheMachineCircuitboard - - proto: RandomArtifactSpawner - cost: 2 - - proto: RandomCargoCorpseSpawner - cost: 2 - prob: 0.5 - - proto: RandomCommandCorpseSpawner - cost: 5 - prob: 0.5 - - proto: RandomEngineerCorpseSpawner - cost: 2 - prob: 0.5 - - proto: RandomMedicCorpseSpawner - cost: 2 - prob: 0.5 - - proto: RandomScienceCorpseSpawner - cost: 2 - prob: 0.5 - - proto: RandomSecurityCorpseSpawner - cost: 2 - prob: 0.5 - - proto: RandomServiceCorpseSpawner - cost: 2 - prob: 0.5 - - proto: ResearchAndDevelopmentServerMachineCircuitboard - cost: 5 - prob: 0.5 - - proto: ResearchDisk10000 - prob: 0.5 - - proto: ResearchDisk5000 - prob: 0.5 - - proto: RipleyHarness - cost: 3 - prob: 0.5 - - proto: SpaceCash1000 - - proto: SpaceCash10000 - cost: 10 - - proto: SpaceCash2500 - cost: 3 - - proto: SpaceCash5000 - cost: 5 - - proto: TechnologyDiskRare - cost: 5 - prob: 0.5 - - proto: ThrusterUnanchored - - proto: WaterTankHighCapacity - - proto: WeldingFuelTankHighCapacity - cost: 3 - - proto: WeaponTeslaGun - prob: 0.1 - cost: 2 + - !type:RandomSpawnsLoot + entries: + - proto: AdvMopItem + prob: 0.5 + - proto: AmmoTechFabCircuitboard + cost: 2 + - proto: AutolatheMachineCircuitboard + cost: 2 + - proto: BiomassReclaimerMachineCircuitboard + cost: 2 + - proto: BluespaceBeaker + cost: 2 + - proto: CyborgEndoskeleton + cost: 3 + prob: 0.5 + - proto: ChemDispenserMachineCircuitboard + cost: 2 + - proto: CircuitImprinter + cost: 2 + - proto: CloningConsoleComputerCircuitboard + cost: 2 + - proto: CloningPodMachineCircuitboard + cost: 2 + - proto: ChemistryBottleCognizine + - proto: FoodBoxDonkpocketCarp + prob: 0.5 + - proto: CrateSalvageEquipment + cost: 3 + prob: 0.5 + - proto: GasRecycler + cost: 2 + - proto: GeneratorRTG + cost: 5 + - proto: GravityGeneratorMini + cost: 2 + - proto: GyroscopeUnanchored + cost: 2 + prob: 0.1 + - proto: MedicalScannerMachineCircuitboard + cost: 2 + - proto: NuclearBombKeg + cost: 5 + - proto: ChemistryBottleOmnizine + prob: 0.5 + - proto: PortableGeneratorPacman + cost: 2 + - proto: PortableGeneratorSuperPacman + cost: 3 + - proto: PowerCellAntiqueProto + cost: 5 + prob: 0.5 + - proto: ProtolatheMachineCircuitboard + - proto: RandomArtifactSpawner + cost: 2 + - proto: RandomCargoCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomCommandCorpseSpawner + cost: 5 + prob: 0.5 + - proto: RandomEngineerCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomMedicCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomScienceCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomSecurityCorpseSpawner + cost: 2 + prob: 0.5 + - proto: RandomServiceCorpseSpawner + cost: 2 + prob: 0.5 + - proto: ResearchAndDevelopmentServerMachineCircuitboard + cost: 5 + prob: 0.5 + - proto: ResearchDisk10000 + prob: 0.5 + - proto: ResearchDisk5000 + prob: 0.5 + - proto: RipleyHarness + cost: 3 + prob: 0.5 + - proto: SpaceCash1000 + - proto: SpaceCash10000 + cost: 10 + - proto: SpaceCash2500 + cost: 3 + - proto: SpaceCash5000 + cost: 5 + - proto: TechnologyDiskRare + cost: 5 + prob: 0.5 + - proto: ThrusterUnanchored + - proto: WaterTankHighCapacity + - proto: WeldingFuelTankHighCapacity + cost: 3 + - proto: WeaponTeslaGun + prob: 0.1 + cost: 2 # Mob loot table @@ -117,70 +117,70 @@ id: OreIron guaranteed: true loots: - - !type:BiomeLoot - proto: OreIron + - !type:BiomeMarkerLoot + proto: OreIron - type: salvageLoot id: OreCoal guaranteed: true loots: - - !type:BiomeLoot - proto: OreCoal + - !type:BiomeMarkerLoot + proto: OreCoal - type: salvageLoot id: OreQuartz guaranteed: true loots: - - !type:BiomeLoot - proto: OreQuartz + - !type:BiomeMarkerLoot + proto: OreQuartz - type: salvageLoot id: OreSalt guaranteed: true loots: - - !type:BiomeLoot - proto: OreSalt + - !type:BiomeMarkerLoot + proto: OreSalt # - Medium value - type: salvageLoot id: OreGold guaranteed: true loots: - - !type:BiomeLoot - proto: OreGold + - !type:BiomeMarkerLoot + proto: OreGold - type: salvageLoot id: OreSilver guaranteed: true loots: - - !type:BiomeLoot - proto: OreSilver + - !type:BiomeMarkerLoot + proto: OreSilver # - High value - type: salvageLoot id: OrePlasma guaranteed: true loots: - - !type:BiomeLoot - proto: OrePlasma + - !type:BiomeMarkerLoot + proto: OrePlasma - type: salvageLoot id: OreUranium guaranteed: true loots: - - !type:BiomeLoot - proto: OreUranium + - !type:BiomeMarkerLoot + proto: OreUranium - type: salvageLoot id: OreDiamond guaranteed: true loots: - - !type:BiomeLoot - proto: OreDiamond + - !type:BiomeMarkerLoot + proto: OreDiamond - type: salvageLoot id: OreArtifactFragment guaranteed: true loots: - - !type:BiomeLoot - proto: OreArtifactFragment + - !type:BiomeMarkerLoot + proto: OreArtifactFragment diff --git a/Resources/Prototypes/Procedural/salvage_mods.yml b/Resources/Prototypes/Procedural/salvage_mods.yml index e8b18dd14a..ca64d29a52 100644 --- a/Resources/Prototypes/Procedural/salvage_mods.yml +++ b/Resources/Prototypes/Procedural/salvage_mods.yml @@ -8,24 +8,24 @@ - type: salvageBiomeMod id: Caves desc: salvage-biome-mod-caves - biome: BiomeCaves + biome: Caves - type: salvageBiomeMod id: Grasslands desc: salvage-biome-mod-grasslands - biome: BiomeGrasslands + biome: Grasslands - type: salvageBiomeMod id: Snow desc: salvage-biome-mod-snow cost: 1 - biome: BiomeSnow + biome: Snow - type: salvageBiomeMod id: Lava desc: salvage-biome-mod-lava cost: 2 - biome: BiomeLava + biome: Lava #- type: salvageBiomeMod # id: Space diff --git a/Resources/Prototypes/Procedural/vgroid.yml b/Resources/Prototypes/Procedural/vgroid.yml index ad759721cb..0caa9f0e1f 100644 --- a/Resources/Prototypes/Procedural/vgroid.yml +++ b/Resources/Prototypes/Procedural/vgroid.yml @@ -117,8 +117,8 @@ - type: dungeonConfig id: VGRoidSmaller - minOffset: 60 - maxOffset: 80 + minOffset: 40 + maxOffset: 60 layers: - !type:NoiseDistanceDunGen size: 150, 150 @@ -151,7 +151,6 @@ maxCount: 3 layers: - !type:ExteriorDunGen - penetration: 5,15 proto: Experiment - !type:EntityTableDunGen minCount: 25 @@ -229,16 +228,5 @@ layers: - !type:FillGridDunGen entity: IronRock - threshold: -0.95 - size: 350, 350 - distanceConfig: !type:DunGenEuclideanSquaredDistance - blendWeight: -0.50 - reservedNoise: - frequency: 0.080 - noiseType: OpenSimplex2 - fractalType: FBm - octaves: 5 - lacunarity: 1.5 - gain: 0.5 allowedTiles: - FloorAsteroidSand From 44617ae841a98ff50700ce3f3c04b264f020a655 Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Thu, 3 Jul 2025 20:51:40 +0200 Subject: [PATCH 179/191] Exo - Minor balance changes and fixes (#38689) --- Resources/Maps/Shuttles/emergency_exo.yml | 38 +- Resources/Maps/exo.yml | 2784 +++++++++++------ .../Prototypes/Entities/Mobs/NPCs/xeno.yml | 4 +- .../Structures/Furniture/Tables/tables.yml | 3 + .../Structures/Machines/Medical/cryo_pod.yml | 9 +- Resources/Prototypes/Maps/exo.yml | 8 +- .../Construction/Graphs/furniture/tables.yml | 12 + 7 files changed, 1898 insertions(+), 960 deletions(-) diff --git a/Resources/Maps/Shuttles/emergency_exo.yml b/Resources/Maps/Shuttles/emergency_exo.yml index ed5b16861d..d3c34c6dc5 100644 --- a/Resources/Maps/Shuttles/emergency_exo.yml +++ b/Resources/Maps/Shuttles/emergency_exo.yml @@ -4,8 +4,8 @@ meta: engineVersion: 262.0.0 forkId: "" forkVersion: "" - time: 06/16/2025 11:37:57 - entityCount: 1017 + time: 07/01/2025 12:52:53 + entityCount: 1020 maps: [] grids: - 2 @@ -398,7 +398,7 @@ entities: 2,-2: 0: 7644 2,-3: - 0: 3584 + 2: 3584 3,-2: 0: 40951 3,-3: @@ -448,6 +448,21 @@ entities: - 0 - 0 - 0 + - volume: 2500 + temperature: 293.15 + moles: + - 1400.0662 + - 5266.916 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 chunkSize: 4 - type: GasTileOverlay - type: RadiationGridResistance @@ -923,6 +938,23 @@ entities: rot: 1.5707963267948966 rad pos: 17.5,1.5 parent: 2 +- proto: AtmosFixAirMarker + entities: + - uid: 1018 + components: + - type: Transform + pos: 9.5,-9.5 + parent: 2 + - uid: 1019 + components: + - type: Transform + pos: 10.5,-9.5 + parent: 2 + - uid: 1020 + components: + - type: Transform + pos: 11.5,-9.5 + parent: 2 - proto: BaseComputer entities: - uid: 639 diff --git a/Resources/Maps/exo.yml b/Resources/Maps/exo.yml index 39f16e1213..5ce2d601a4 100644 --- a/Resources/Maps/exo.yml +++ b/Resources/Maps/exo.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Map - engineVersion: 262.0.0 + engineVersion: 264.0.0 forkId: "" forkVersion: "" - time: 06/19/2025 00:17:06 - entityCount: 19539 + time: 07/03/2025 16:09:47 + entityCount: 19679 maps: - 1 grids: @@ -132,11 +132,11 @@ entities: version: 7 0,-3: ind: 0,-3 - tiles: IAAAAAACAAMAAAAAAAADAAAAAAAAgQAAAAAAAC0AAAAAAAAtAAAAAAAAIAAAAAAAACUAAAAAAwAlAAAAAAAAJQAAAAAAACUAAAAAAAAlAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAcAAAAAAAADAAAAAAAAAwAAAAAAAIEAAAAAAAAtAAAAAAAALQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAABcAAAAAAACBAAAAAAAAgQAAAAAAABcAAAAAAAAXAAAAAAAAgQAAAAAAAC0AAAAAAAAtAAAAAAAALQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAAy0AAAAAAAMtAAAAAAADgQAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAAAAAAAAAAFwAAAAAAAIEAAAAAAAAtAAAAAAAALQAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAC0AAAAAAAMtAAAAAAADLQAAAAAAA4EAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAAC0AAAAAAAAYAAAAAAAAJAAAAAACABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAALQAAAAAAAC0AAAAAAAAtAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAABcAAAAAAAAAAAAAAAAAgQAAAAAAAC0AAAAAAAAlAAAAAAIALQAAAAAAAC0AAAAAAACBAAAAAAAAgQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAIEAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAABcAAAAAAACBAAAAAAAAgQAAAAAAABcAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAA4EAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAAAKwAAAAAABy4AAAAAAAEYAAAAAAAALQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADKwAAAAACACsAAAAAAgArAAAAAAIAKwAAAAADACUAAAAAAAArAAAAAAIAGAAAAAAAACsAAAAAAAArAAAAAAEAKwAAAAADACsAAAAAAAArAAAAAAEAKwAAAAAAACsAAAAAAAArAAAAAAEAKwAAAAAAAC4AAAAAAAItAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAxgAAAAAAAAtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALgAAAAAAAS0AAAAAAAMtAAAAAAADLQAAAAAAA4EAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAG8AAAAAAACBAAAAAAAAYAAAAAADAGAAAAAAAABgAAAAAAMAYAAAAAAAAA== + tiles: IAAAAAACAAMAAAAAAAADAAAAAAAAgQAAAAAAAC0AAAAAAAAtAAAAAAAAIAAAAAAAACUAAAAAAwAlAAAAAAAAJQAAAAAAACUAAAAAAAAlAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAcAAAAAAAADAAAAAAAAAwAAAAAAAIEAAAAAAAAtAAAAAAAALQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAXAAAAAAAALgAAAAAAAi0AAAAAAAOBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALgAAAAAAAi4AAAAAAACBAAAAAAAAgQAAAAAAABcAAAAAAAAXAAAAAAAAgQAAAAAAAC0AAAAAAAAtAAAAAAAALQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAAy0AAAAAAAMtAAAAAAADgQAAAAAAAC0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAAAAAAAAAAFwAAAAAAAIEAAAAAAAAtAAAAAAAALQAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAC0AAAAAAAMtAAAAAAADLQAAAAAAA4EAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAAC0AAAAAAAAYAAAAAAAAJAAAAAACABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAALQAAAAAAAC0AAAAAAAAtAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAABcAAAAAAAAAAAAAAAAAgQAAAAAAAC0AAAAAAAAlAAAAAAIALQAAAAAAAC0AAAAAAACBAAAAAAAAgQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAIEAAAAAAAAtAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALgAAAAAAAS4AAAAAAAOBAAAAAAAAgQAAAAAAABcAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAABcAAAAAAAAuAAAAAAABLQAAAAAAA4EAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAA4EAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAAAKwAAAAAABy4AAAAAAAEYAAAAAAAALQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADKwAAAAACACsAAAAAAgArAAAAAAIAKwAAAAADACUAAAAAAAArAAAAAAIAGAAAAAAAACsAAAAAAAArAAAAAAEAKwAAAAADACsAAAAAAAArAAAAAAEAKwAAAAAAACsAAAAAAAArAAAAAAEAKwAAAAAAAC4AAAAAAAItAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAxgAAAAAAAAtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALgAAAAAAAS0AAAAAAAMtAAAAAAADLQAAAAAAA4EAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAG8AAAAAAACBAAAAAAAAYAAAAAADAGAAAAAAAABgAAAAAAMAYAAAAAAAAA== version: 7 1,-3: ind: 1,-3 - tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAAYAAAAAAAALQAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAJQAAAAAAACUAAAAAAwAlAAAAAAEAJQAAAAADACUAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAFwAAAAAAAAAAAAAAAACBAAAAAAAAIAAAAAABACsAAAAAAQAgAAAAAAIAgQAAAAAAACUAAAAAAwAlAAAAAAMAJQAAAAABACUAAAAAAQAlAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAAAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAABgAAAAAAAAlAAAAAAEAJQAAAAABACUAAAAAAQAlAAAAAAIAJQAAAAAAAC4AAAAAAAItAAAAAAADLgAAAAAAA4EAAAAAAACBAAAAAAAAAAAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAAAYAAAAAAAAJQAAAAABACUAAAAAAgAlAAAAAAEAJQAAAAACACUAAAAAAAAtAAAAAAAABAAAAAAAAC0AAAAAAACBAAAAAAAAgQAAAAAAAAAAAAAAAACBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAACUAAAAAAwAlAAAAAAAAJQAAAAACACUAAAAAAQAlAAAAAAAALgAAAAAAAS0AAAAAAAMuAAAAAAAAgQAAAAAAAIEAAAAAAAAAAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAAAlAAAAAAMAJQAAAAABACUAAAAAAAAlAAAAAAIAJQAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAJQAAAAADACUAAAAAAwAlAAAAAAMAJQAAAAABACUAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAFwAAAAAAAAAAAAAAAACBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAACUAAAAAAwAlAAAAAAEAJQAAAAAAACUAAAAAAAAlAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAAAQAAAAAAMAEAAAAAABABAAAAAAAwAQAAAAAAEAJQAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAEAAAAAACABAAAAAAAAAQAAAAAAAAEAAAAAADACUAAAAAAAAtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMYAAAAAAAALgAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAKwAAAAACACsAAAAAAgArAAAAAAEAKwAAAAAAACsAAAAAAAArAAAAAAAAGAAAAAAAACsAAAAAAQAlAAAAAAEALQAAAAAAAC0AAAAAAACBAAAAAAAALwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAxgAAAAAAAAuAAAAAAADKwAAAAAABy0AAAAAAAAtAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAADLQAAAAAAAy0AAAAAAAOBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAALQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAeAAAAAAAAYAAAAAADAGAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAC0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== + tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAAYAAAAAAAALQAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAADFwAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAJQAAAAAAACUAAAAAAwAlAAAAAAEAJQAAAAADACUAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALgAAAAAAAS4AAAAAAAOBAAAAAAAAIAAAAAABACsAAAAAAQAgAAAAAAIAgQAAAAAAACUAAAAAAwAlAAAAAAMAJQAAAAABACUAAAAAAQAlAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAABgAAAAAAAAlAAAAAAEAJQAAAAABACUAAAAAAQAlAAAAAAIAJQAAAAAAAC4AAAAAAAItAAAAAAADLgAAAAAAA4EAAAAAAACBAAAAAAAALQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAAAYAAAAAAAAJQAAAAABACUAAAAAAgAlAAAAAAEAJQAAAAACACUAAAAAAAAtAAAAAAAABAAAAAAAAC0AAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAACBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAACUAAAAAAwAlAAAAAAAAJQAAAAACACUAAAAAAQAlAAAAAAAALgAAAAAAAS0AAAAAAAMuAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAAAlAAAAAAMAJQAAAAABACUAAAAAAAAlAAAAAAIAJQAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAJQAAAAADACUAAAAAAwAlAAAAAAMAJQAAAAABACUAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALgAAAAAAAi4AAAAAAACBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAACUAAAAAAwAlAAAAAAEAJQAAAAAAACUAAAAAAAAlAAAAAAIALQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy4AAAAAAAAXAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAAAQAAAAAAMAEAAAAAABABAAAAAAAwAQAAAAAAEAJQAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAEAAAAAACABAAAAAAAAAQAAAAAAAAEAAAAAADACUAAAAAAAAtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMYAAAAAAAALgAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAKwAAAAACACsAAAAAAgArAAAAAAEAKwAAAAAAACsAAAAAAAArAAAAAAAAGAAAAAAAACsAAAAAAQAlAAAAAAEALQAAAAAAAC0AAAAAAACBAAAAAAAALwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAxgAAAAAAAAuAAAAAAADKwAAAAAABy0AAAAAAAAtAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAADLQAAAAAAAy0AAAAAAAOBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAALQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAeAAAAAAAAYAAAAAADAGAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAC0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== version: 7 -1,-3: ind: -1,-3 @@ -144,7 +144,7 @@ entities: version: 7 -2,-2: ind: -2,-2 - tiles: gQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAIAAAAAABAAEAAAAAAwAYAAAAAAAAAAAAAAAAAIEAAAAAAAABAAAAAAAAAQAAAAADAAEAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAACAAAAAAAwABAAAAAAIAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAYAAAAAAQAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAAAgAAAAAAMAAQAAAAACABgAAAAAAAAmAAAAAAIAJgAAAAABABgAAAAAAAAGAAAAAAIAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAABgAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAAAgAAAAAAEALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAACAAAAAAAwAgAAAAAAIAIAAAAAADAAEAAAAAAwABAAAAAAEAAQAAAAADAAYAAAAAAAABAAAAAAEAAQAAAAADAAEAAAAAAwCBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAACAAAAAAAAAgAAAAAAAALgAAAAAAAi0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy4AAAAAAAMgAAAAAAAALgAAAAAAA4EAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAIAAAAAACAC4AAAAAAAEtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAAAIAAAAAAAAC4AAAAAAAAYAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAACAAAAAAAwAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAEAIAAAAAADACAAAAAAAwAgAAAAAAMAIAAAAAACACAAAAAAAQAuAAAAAAADGAAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAIAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC8AAAAAAACBAAAAAAAALQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAAC0AAAAAAACBAAAAAAAARAAAAAAAAEQAAAAAAAAYAAAAAAAARAAAAAAAAC0AAAAAAAAYAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAAtAAAAAAAAgQAAAAAAAC4AAAAAAAItAAAAAAADLQAAAAAAAy4AAAAAAAMuAAAAAAAAGAAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAALQAAAAAAABgAAAAAAAAtAAAAAAAAGAAAAAAAABgAAAAAAAAtAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAAC0AAAAAAACBAAAAAAAALgAAAAAAAS0AAAAAAAMtAAAAAAADLQAAAAAAA4EAAAAAAACBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAALQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAGAAAAAAAAEQAAAAAAACBAAAAAAAAIAAAAAADAC0AAAAAAAArAAAAAAAHLQAAAAAAAC0AAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAgQAAAAAAABgAAAAAAABEAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAAAtAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAAAEAAAAAAAAGAAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAAA== + tiles: gQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAIAAAAAABAAEAAAAAAwAYAAAAAAAAAAAAAAAAAIEAAAAAAAABAAAAAAAAAQAAAAADAAEAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAACAAAAAAAwABAAAAAAIAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAYAAAAAAQAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAAAgAAAAAAMAAQAAAAACAAEAAAAAAAAmAAAAAAIAJgAAAAABABgAAAAAAAAGAAAAAAIAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAABgAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAAAgAAAAAAEALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAACAAAAAAAwAgAAAAAAIAIAAAAAADAAEAAAAAAwABAAAAAAEAAQAAAAADAAYAAAAAAAABAAAAAAEAAQAAAAADAAEAAAAAAwCBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAACAAAAAAAAAgAAAAAAAALgAAAAAAAi0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy4AAAAAAAMgAAAAAAAALgAAAAAAA4EAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAIAAAAAACAC4AAAAAAAEtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAAAIAAAAAAAAC4AAAAAAAAYAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAACAAAAAAAwAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAEAIAAAAAADACAAAAAAAwAgAAAAAAMAIAAAAAACACAAAAAAAQAuAAAAAAADGAAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAIAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC8AAAAAAACBAAAAAAAALQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAAC0AAAAAAACBAAAAAAAARAAAAAAAAEQAAAAAAAAYAAAAAAAARAAAAAAAAC0AAAAAAAAYAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAAtAAAAAAAAgQAAAAAAAC4AAAAAAAItAAAAAAADLQAAAAAAAy4AAAAAAAMuAAAAAAAAGAAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAALQAAAAAAABgAAAAAAAAtAAAAAAAAGAAAAAAAABgAAAAAAAAtAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAAC0AAAAAAACBAAAAAAAALgAAAAAAAS0AAAAAAAMtAAAAAAADLQAAAAAAA4EAAAAAAACBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAALQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAGAAAAAAAAEQAAAAAAACBAAAAAAAAIAAAAAADAC0AAAAAAAArAAAAAAAHLQAAAAAAAC0AAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAgQAAAAAAABgAAAAAAABEAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAAAtAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAAAEAAAAAAAAGAAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAAA== version: 7 -2,-1: ind: -2,-1 @@ -164,11 +164,11 @@ entities: version: 7 -1,-4: ind: -1,-4 - tiles: gQAAAAAAABsAAAAAAAAbAAAAAAAAGwAAAAAAABsAAAAAAACBAAAAAAAAIAAAAAABACAAAAAAAAAgAAAAAAEAIAAAAAADACAAAAAAAwAgAAAAAAMAIAAAAAADACAAAAAAAQAgAAAAAAIAIAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAADACAAAAAAAgAgAAAAAAEAIAAAAAACACAAAAAAAAAgAAAAAAEAIAAAAAADACAAAAAAAwBgAAAAAAMAYAAAAAACAGAAAAAAAgBgAAAAAAMAYAAAAAABAIEAAAAAAAAgAAAAAAAAIAAAAAACACAAAAAAAwAgAAAAAAIAIAAAAAADACAAAAAAAAAgAAAAAAIAIAAAAAACACAAAAAAAQAgAAAAAAMAYAAAAAACAGAAAAAAAABgAAAAAAEAYAAAAAACAGAAAAAAAQCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAQBgAAAAAAEAYAAAAAADAGAAAAAAAwBgAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAIAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACAAAAAAAwCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAIAAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAcAAAAAAAAgAAAAAAEABwAAAAAAAAcAAAAAAAAHAAAAAAAAIAAAAAACAC0AAAAAAAMuAAAAAAADLgAAAAAAASAAAAAAAQCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAHAAAAAAAAIAAAAAACACAAAAAAAQAgAAAAAAEAIAAAAAAAACAAAAAAAwAlAAAAAAIALgAAAAAAAS4AAAAAAAOBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAABwAAAAAAACAAAAAAAgAgAAAAAAMABwAAAAAAAAcAAAAAAAAHAAAAAAAAJQAAAAACACUAAAAAAwAuAAAAAAABLgAAAAAAA4EAAAAAAABvAAAAAAAAbwAAAAAAAG8AAAAAAABvAAAAAAAAgQAAAAAAAAcAAAAAAAAgAAAAAAEAIAAAAAAAACAAAAAAAwAgAAAAAAAAIAAAAAABAC4AAAAAAAMlAAAAAAIAJQAAAAAAACAAAAAAAQBvAAAAAAAAbwAAAAAAAG8AAAAAAABvAAAAAAAAbwAAAAAAAIEAAAAAAAAgAAAAAAAAIAAAAAADACAAAAAAAAAgAAAAAAMAIAAAAAACACAAAAAAAQAuAAAAAAABLgAAAAAAAyUAAAAAAwAgAAAAAAEAbwAAAAAAAG8AAAAAAABvAAAAAAAAbwAAAAAAAG8AAAAAAACBAAAAAAAAgQAAAAAAACAAAAAAAwCBAAAAAAAAIAAAAAAAAIEAAAAAAAAgAAAAAAIAgQAAAAAAAC0AAAAAAAArAAAAAAAHIAAAAAABAG8AAAAAAABvAAAAAAAAbwAAAAAAAG8AAAAAAABvAAAAAAAAgQAAAAAAACAAAAAAAQAgAAAAAAAAgQAAAAAAACAAAAAAAwCBAAAAAAAAIAAAAAACAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAbwAAAAAAAG8AAAAAAABvAAAAAAAAbwAAAAAAAIEAAAAAAAAHAAAAAAAABwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACAAAAAAAgCBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAAG8AAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAIAAAAAADAIEAAAAAAAAgAAAAAAEAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAACAAAAAAAABvAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAAgQAAAAAAACAAAAAAAwAgAAAAAAIAIAAAAAACACAAAAAAAAAgAAAAAAIAIAAAAAABAA== + tiles: gQAAAAAAABsAAAAAAAAbAAAAAAAAGwAAAAAAABsAAAAAAACBAAAAAAAAIAAAAAABACAAAAAAAAAgAAAAAAEAIAAAAAADACAAAAAAAwAgAAAAAAMAIAAAAAADACAAAAAAAQAgAAAAAAIAIAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAADACAAAAAAAgAgAAAAAAEAIAAAAAACACAAAAAAAAAgAAAAAAEAIAAAAAADACAAAAAAAwBgAAAAAAMAYAAAAAACAGAAAAAAAgBgAAAAAAMAYAAAAAABAIEAAAAAAAAgAAAAAAAAIAAAAAACACAAAAAAAwAgAAAAAAIAIAAAAAADACAAAAAAAAAgAAAAAAIAIAAAAAACACAAAAAAAQAgAAAAAAMAYAAAAAACAGAAAAAAAABgAAAAAAEAYAAAAAACAGAAAAAAAQCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAQBgAAAAAAEAYAAAAAADAGAAAAAAAwBgAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAIAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACAAAAAAAwCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAIAAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAcAAAAAAAAgAAAAAAEABwAAAAAAAAcAAAAAAAAHAAAAAAAAIAAAAAACAC0AAAAAAAMuAAAAAAADLwAAAAAAAC8AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAHAAAAAAAAIAAAAAACACAAAAAAAQAgAAAAAAEAIAAAAAAAACAAAAAAAwAlAAAAAAIALgAAAAAAAS4AAAAAAAMvAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAABwAAAAAAACAAAAAAAgAgAAAAAAMABwAAAAAAAAcAAAAAAAAHAAAAAAAAJQAAAAACACUAAAAAAwAuAAAAAAABLgAAAAAAA4EAAAAAAABvAAAAAAAAbwAAAAAAAG8AAAAAAABvAAAAAAAAgQAAAAAAAAcAAAAAAAAgAAAAAAEAIAAAAAAAACAAAAAAAwAgAAAAAAAAIAAAAAABAC4AAAAAAAMlAAAAAAIAJQAAAAAAACAAAAAAAQBvAAAAAAAAbwAAAAAAAG8AAAAAAABvAAAAAAAAbwAAAAAAAIEAAAAAAAAgAAAAAAAAIAAAAAADACAAAAAAAAAgAAAAAAMAIAAAAAACACAAAAAAAQAuAAAAAAABLgAAAAAAAyUAAAAAAwAgAAAAAAEAbwAAAAAAAG8AAAAAAABvAAAAAAAAbwAAAAAAAG8AAAAAAACBAAAAAAAAgQAAAAAAACAAAAAAAwCBAAAAAAAAIAAAAAAAAIEAAAAAAAAgAAAAAAIAgQAAAAAAAC0AAAAAAAArAAAAAAAHIAAAAAABAG8AAAAAAABvAAAAAAAAbwAAAAAAAG8AAAAAAABvAAAAAAAAgQAAAAAAACAAAAAAAQAgAAAAAAAAgQAAAAAAACAAAAAAAwCBAAAAAAAAIAAAAAACAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAbwAAAAAAAG8AAAAAAABvAAAAAAAAbwAAAAAAAIEAAAAAAAAHAAAAAAAABwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACAAAAAAAgCBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAAG8AAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAIAAAAAADAIEAAAAAAAAgAAAAAAEAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAACAAAAAAAABvAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAAgQAAAAAAACAAAAAAAwAgAAAAAAIAIAAAAAACACAAAAAAAAAgAAAAAAIAIAAAAAABAA== version: 7 0,-5: ind: 0,-5 - tiles: LwAAAAAAAIEAAAAAAAAbAAAAAAAAGwAAAAAAABsAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAuAAAAAAACLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy8AAAAAAACBAAAAAAAAGwAAAAAAACkAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAACUAAAAAAAArAAAAAAMAKwAAAAAAACsAAAAAAAArAAAAAAAAJQAAAAACACsAAAAAAgAvAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACkAAAAAAQCBAAAAAAAAgQAAAAAAAC4AAAAAAAEtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLgAAAAAAAysAAAAAAAcuAAAAAAACgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAABsAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAMAKQAAAAABAIEAAAAAAACBAAAAAAAAJQAAAAADACUAAAAAAwAlAAAAAAIAJQAAAAACAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAEAgQAAAAAAACkAAAAAAgCBAAAAAAAAgQAAAAAAACUAAAAAAwAlAAAAAAIAJQAAAAAAAIEAAAAAAAAuAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAKQAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAuAAAAAAACLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy4AAAAAAAArAAAAAAAHLQAAAAAAABsAAAAAAACBAAAAAAAAKQAAAAABACkAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAACUAAAAAAAArAAAAAAMAKwAAAAABACsAAAAAAAArAAAAAAIAJQAAAAADAC0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLgAAAAAAAi0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAAALQAAAAAAAy0AAAAAAAMuAAAAAAADgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAYAAAAAABAGAAAAAAAgBgAAAAAAIAYAAAAAAAAC0AAAAAAAMuAAAAAAADLQAAAAAAAC0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAAGAAAAAAAgBgAAAAAAMAYAAAAAAAAGAAAAAAAwAgAAAAAAEALQAAAAAAAC0AAAAAAAAtAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACAAAAAAAgCBAAAAAAAAIAAAAAADAC0AAAAAAAAuAAAAAAABLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAJAAAAAADACQAAAAAAAAgAAAAAAIAJAAAAAADACAAAAAAAwAtAAAAAAAAJQAAAAADACsAAAAAAgArAAAAAAAAKwAAAAACACsAAAAAAwArAAAAAAEAKwAAAAADACUAAAAAAgAtAAAAAAAAIAAAAAABACAAAAAAAgAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAMALgAAAAAAAS0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLgAAAAAAAIEAAAAAAAAKAAAAAAAACgAAAAAAACAAAAAAAwAgAAAAAAEAgQAAAAAAAC0AAAAAAAAtAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== + tiles: LwAAAAAAAIEAAAAAAAAbAAAAAAAAGwAAAAAAABsAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAuAAAAAAACLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy8AAAAAAACBAAAAAAAAGwAAAAAAACkAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAACUAAAAAAAArAAAAAAMAKwAAAAAAACsAAAAAAAArAAAAAAAAJQAAAAACACsAAAAAAgAvAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACkAAAAAAQCBAAAAAAAAgQAAAAAAAC4AAAAAAAEtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLgAAAAAAAysAAAAAAAcuAAAAAAACgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAABsAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAJQAAAAADACUAAAAAAwAlAAAAAAIAJQAAAAACAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAEAgQAAAAAAACkAAAAAAACBAAAAAAAAgQAAAAAAACUAAAAAAwAlAAAAAAIAJQAAAAAAAIEAAAAAAAAuAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAKQAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAuAAAAAAACLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy4AAAAAAAArAAAAAAAHLQAAAAAAABsAAAAAAACBAAAAAAAAKQAAAAABACkAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAACUAAAAAAAArAAAAAAMAKwAAAAABACsAAAAAAAArAAAAAAIAJQAAAAADAC0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLgAAAAAAAi0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAAALQAAAAAAAy0AAAAAAAMuAAAAAAADgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAYAAAAAABAGAAAAAAAgBgAAAAAAIAYAAAAAAAAC0AAAAAAAMuAAAAAAADLQAAAAAAAC0AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAALQAAAAAAACsAAAAAAActAAAAAAAAgQAAAAAAAGAAAAAAAgBgAAAAAAMAYAAAAAAAAGAAAAAAAwAgAAAAAAEALQAAAAAAAC0AAAAAAAAtAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAC0AAAAAAAArAAAAAAAHLQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACAAAAAAAgCBAAAAAAAAIAAAAAADAC0AAAAAAAAuAAAAAAABLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAAAKwAAAAAABy0AAAAAAACBAAAAAAAAJAAAAAADACQAAAAAAAAgAAAAAAIAJAAAAAADACAAAAAAAwAtAAAAAAAAJQAAAAADACsAAAAAAgArAAAAAAAAKwAAAAACACsAAAAAAwArAAAAAAEAKwAAAAADACUAAAAAAgAtAAAAAAAAIAAAAAABACAAAAAAAgAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAMALgAAAAAAAS0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLgAAAAAAAIEAAAAAAAAKAAAAAAAACgAAAAAAACAAAAAAAwAgAAAAAAEAgQAAAAAAAC0AAAAAAAAtAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== version: 7 -2,-4: ind: -2,-4 @@ -312,7 +312,7 @@ entities: version: 7 3,-3: ind: 3,-3 - tiles: gQAAAAAAAB4AAAAAAACBAAAAAAAAgQAAAAAAAB4AAAAAAAAeAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAQBgAAAAAAMAYAAAAAABAIEAAAAAAABgAAAAAAAAYAAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAgBgAAAAAAIAYAAAAAACAGAAAAAAAgCBAAAAAAAAYAAAAAAAAGAAAAAAAwAlAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAAAYAAAAAACAGAAAAAAAgBgAAAAAAIAgQAAAAAAAGAAAAAAAwBgAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAYAAAAAABAGAAAAAAAgBgAAAAAAAAYAAAAAAAAIEAAAAAAABgAAAAAAAAYAAAAAAAACUAAAAAAwCBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAgQAAAAAAAGAAAAAAAwBgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAADAGAAAAAAAgAlAAAAAAEAgQAAAAAAAC8AAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAABgAAAAAAIAYAAAAAABAGAAAAAAAQBgAAAAAAIAYAAAAAAAAGAAAAAAAQBgAAAAAAMAJQAAAAABAIEAAAAAAAAvAAAAAAAAGAAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAARAAAAAAAAGAAAAAAAQBgAAAAAAIAYAAAAAABAGAAAAAAAQBgAAAAAAAAYAAAAAAAACUAAAAAAwCBAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAADAGAAAAAAAgCBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAAIEAAAAAAABgAAAAAAMAYAAAAAABAGAAAAAAAABgAAAAAAIAgQAAAAAAAGAAAAAAAABgAAAAAAMAgQAAAAAAAIEAAAAAAAAYAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAYAAAAAACAGAAAAAAAABgAAAAAAAAYAAAAAABAGAAAAAAAwBgAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAAIEAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAgBgAAAAAAMAYAAAAAACAGAAAAAAAQBgAAAAAAIAYAAAAAACAGAAAAAAAwCBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAABgAAAAAAMAYAAAAAAAAGAAAAAAAABgAAAAAAMAgQAAAAAAAGAAAAAAAwBgAAAAAAEAgQAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAbwAAAAAAAGAAAAAAAgBgAAAAAAIAbwAAAAAAAIEAAAAAAABgAAAAAAIAYAAAAAABAIEAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAGAAAAAAAwBgAAAAAAAAYAAAAAADAG8AAAAAAACBAAAAAAAAYAAAAAADAGAAAAAAAQAYAAAAAAAAGAAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAAAAGAAAAAAAwCBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAGQAAAAACABkAAAAAAAAZAAAAAAIAGQAAAAAAAIEAAAAAAABgAAAAAAEAYAAAAAABAA== + tiles: gQAAAAAAAB4AAAAAAACBAAAAAAAAgQAAAAAAAB4AAAAAAAAeAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAQBgAAAAAAMAYAAAAAABAIEAAAAAAABgAAAAAAAAYAAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAgBgAAAAAAIAYAAAAAACAGAAAAAAAgCBAAAAAAAAYAAAAAAAAGAAAAAAAwAlAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAAAYAAAAAACAGAAAAAAAgBgAAAAAAIAgQAAAAAAAGAAAAAAAwBgAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAYAAAAAABAGAAAAAAAgBgAAAAAAAAYAAAAAAAAIEAAAAAAABgAAAAAAAAYAAAAAAAACUAAAAAAwCBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAgQAAAAAAAGAAAAAAAwBgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAADAGAAAAAAAgAlAAAAAAEAgQAAAAAAAC8AAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAABgAAAAAAIAYAAAAAABAGAAAAAAAQBgAAAAAAIAYAAAAAAAAGAAAAAAAQBgAAAAAAMAJQAAAAABAIEAAAAAAAAvAAAAAAAAGAAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAARAAAAAAAAGAAAAAAAQBgAAAAAAIAYAAAAAABAGAAAAAAAQBgAAAAAAAAYAAAAAAAACUAAAAAAwCBAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAEAYAAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAADAGAAAAAAAgCBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAAIEAAAAAAABgAAAAAAMAYAAAAAABAGAAAAAAAABgAAAAAAIAgQAAAAAAAGAAAAAAAABgAAAAAAMAgQAAAAAAAIEAAAAAAAAYAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAYAAAAAACAGAAAAAAAABgAAAAAAAAYAAAAAABAGAAAAAAAwBgAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAAIEAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAgBgAAAAAAMAYAAAAAACAGAAAAAAAQBgAAAAAAIAYAAAAAACAGAAAAAAAwCBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAABgAAAAAAMAYAAAAAAAAGAAAAAAAABgAAAAAAMAgQAAAAAAAGAAAAAAAwBgAAAAAAEAgQAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAbwAAAAAAAGAAAAAAAgBgAAAAAAIAbwAAAAAAAIEAAAAAAABgAAAAAAIAYAAAAAABAIEAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAGAAAAAAAwBgAAAAAAAAYAAAAAADAG8AAAAAAACBAAAAAAAAYAAAAAADAGAAAAAAAQAYAAAAAAAAGAAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAAAAGAAAAAAAwCBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAACBAAAAAAAAGQAAAAACABkAAAAAAAAZAAAAAAIAGQAAAAAAAIEAAAAAAABgAAAAAAEAYAAAAAABAA== version: 7 4,-2: ind: 4,-2 @@ -320,7 +320,7 @@ entities: version: 7 3,-2: ind: 3,-2 - tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAGQAAAAABABkAAAAAAAAZAAAAAAMAGQAAAAACAIEAAAAAAABgAAAAAAIAYAAAAAADAC8AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAvAAAAAAAALwAAAAAAAIEAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAEAYAAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAwAvAAAAAAAAgQAAAAAAAC8AAAAAAACBAAAAAAAAgQAAAAAAAC8AAAAAAACBAAAAAAAAGAAAAAAAAIEAAAAAAABgAAAAAAMAYAAAAAADAGAAAAAAAgBgAAAAAAMAYAAAAAACAGAAAAAAAQBgAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAADAIEAAAAAAABgAAAAAAEAYAAAAAABABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAGAAAAAAAQBgAAAAAAEAYAAAAAACAGAAAAAAAABgAAAAAAEAYAAAAAACAGAAAAAAAwCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAABgAAAAAAEAYAAAAAAAAGAAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAABHAAAAAAAALQAAAAAAAy0AAAAAAAMtAAAAAAADgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAABLwAAAAAAAC0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAABLQAAAAAAAS8AAAAAAAAuAAAAAAABLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAAALgAAAAAAAS0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAS0AAAAAAAEvAAAAAAAAIAAAAAACACAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAgAgAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAARwAAAAAAACUAAAAAAAAlAAAAAAMAJQAAAAAAACUAAAAAAwAlAAAAAAAAJQAAAAAAACUAAAAAAgAlAAAAAAAAJQAAAAABACUAAAAAAwAlAAAAAAMARwAAAAAAAIEAAAAAAABHAAAAAAAAIAAAAAADACUAAAAAAABgAAAAAAAAYAAAAAACAGAAAAAAAgBgAAAAAAAAYAAAAAAAAGAAAAAAAgBgAAAAAAMAYAAAAAAAAGAAAAAAAQCBAAAAAAAAIAAAAAABACAAAAAAAwAgAAAAAAMAIAAAAAADACAAAAAAAwAlAAAAAAMAIgAAAAADACIAAAAAAQAiAAAAAAMAIgAAAAADACQAAAAAAgAiAAAAAAMAIgAAAAACACIAAAAAAgBgAAAAAAEAIAAAAAAAACAAAAAAAwAgAAAAAAIAIAAAAAADACAAAAAAAgCBAAAAAAAARwAAAAAAAGAAAAAAAgBgAAAAAAIAYAAAAAABAGAAAAAAAwBgAAAAAAMAYAAAAAADAGAAAAAAAgAiAAAAAAEAYAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACUAAAAAAwAfAAAAAAIAgQAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAIEAAAAAAABgAAAAAAAAIgAAAAAAAGAAAAAAAABHAAAAAAAAJQAAAAAAACUAAAAAAwAlAAAAAAIAJQAAAAABACUAAAAAAwAlAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAQAiAAAAAAMAIgAAAAAAACIAAAAAAQBgAAAAAAEAgQAAAAAAACQAAAAAAgAlAAAAAAAAJQAAAAAAACUAAAAAAQAlAAAAAAMAJQAAAAABAA== + tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAGQAAAAABABkAAAAAAAAZAAAAAAMAGQAAAAACAIEAAAAAAABgAAAAAAIAYAAAAAADAC8AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAvAAAAAAAALwAAAAAAAIEAAAAAAAAYAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAEAYAAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAwAvAAAAAAAAgQAAAAAAAC8AAAAAAACBAAAAAAAAgQAAAAAAAC8AAAAAAACBAAAAAAAAGAAAAAAAAIEAAAAAAABgAAAAAAMAYAAAAAADAGAAAAAAAgBgAAAAAAMAYAAAAAACAGAAAAAAAQBgAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAABgAAAAAAACBAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAADAIEAAAAAAABgAAAAAAEAYAAAAAABABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAgQAAAAAAAGAAAAAAAQBgAAAAAAEAYAAAAAACAGAAAAAAAABgAAAAAAEAYAAAAAACAGAAAAAAAwCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAIEAAAAAAABgAAAAAAEAYAAAAAAAAGAAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAABHAAAAAAAALQAAAAAAAy0AAAAAAAMtAAAAAAADgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAtAAAAAAABLwAAAAAAAC0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAABLQAAAAAAAS8AAAAAAAAuAAAAAAABLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMuAAAAAAAALgAAAAAAAS0AAAAAAAMtAAAAAAADLQAAAAAAAy0AAAAAAAMtAAAAAAADLQAAAAAAAS0AAAAAAAEvAAAAAAAAIAAAAAACACAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAgAgAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAARwAAAAAAACUAAAAAAAAlAAAAAAMAJQAAAAAAACUAAAAAAwAlAAAAAAAAJQAAAAAAACUAAAAAAgAlAAAAAAAAJQAAAAABACUAAAAAAwAlAAAAAAMARwAAAAAAAIEAAAAAAABHAAAAAAAAgQAAAAAAACUAAAAAAABgAAAAAAAAYAAAAAACAGAAAAAAAgBgAAAAAAAAYAAAAAAAAGAAAAAAAgBgAAAAAAMAYAAAAAAAAGAAAAAAAQCBAAAAAAAAIAAAAAABACAAAAAAAwAgAAAAAAMAIAAAAAADACAAAAAAAwAlAAAAAAMAIgAAAAADACIAAAAAAQAiAAAAAAMAIgAAAAADACQAAAAAAgAiAAAAAAMAIgAAAAACACIAAAAAAgBgAAAAAAEAIAAAAAAAACAAAAAAAwAgAAAAAAIAIAAAAAADACAAAAAAAgCBAAAAAAAARwAAAAAAAGAAAAAAAgBgAAAAAAIAYAAAAAABAGAAAAAAAwBgAAAAAAMAYAAAAAADAGAAAAAAAgAiAAAAAAEAYAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACUAAAAAAwAfAAAAAAIAgQAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAIEAAAAAAABgAAAAAAAAIgAAAAAAAGAAAAAAAABHAAAAAAAAJQAAAAAAACUAAAAAAwAlAAAAAAIAJQAAAAABACUAAAAAAwAlAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAQAiAAAAAAMAIgAAAAAAACIAAAAAAQBgAAAAAAEAgQAAAAAAACQAAAAAAgAlAAAAAAAAJQAAAAAAACUAAAAAAQAlAAAAAAMAJQAAAAABAA== version: 7 2,-2: ind: 2,-2 @@ -1913,6 +1913,11 @@ entities: 3720: -19,-28 3721: -18,-28 3722: -17,-28 + - node: + color: '#00000066' + id: DiagonalCheckerAOverlay + decals: + 4107: -24,-30 - node: color: '#00000067' id: DiagonalCheckerAOverlay @@ -2061,7 +2066,6 @@ entities: 1936: 41,-32 2122: -8,-7 2403: 10,-84 - 3409: 62,-22 - node: cleanable: True color: '#000000FF' @@ -3600,11 +3604,6 @@ entities: 3043: 18,-101 3044: 18,-100 3045: 18,-99 - - node: - color: '#43990996' - id: MiniTileLineOverlayW - decals: - 495: 17,-93 - node: color: '#9FED5896' id: MiniTileLineOverlayW @@ -3613,6 +3612,7 @@ entities: 1454: -15,-35 1912: 8,-71 1914: 23,-33 + 4109: 17,-93 - node: color: '#D381C926' id: MiniTileLineOverlayW @@ -3726,7 +3726,6 @@ entities: color: '#FFFFFFFF' id: MiniTileSteelLineW decals: - 494: 17,-93 1439: -35,-57 1453: -15,-35 1722: 53,-14 @@ -3742,6 +3741,7 @@ entities: 3501: 62,-42 3503: 62,-43 3504: 62,-44 + 4108: 17,-93 - node: color: '#1488C2FF' id: MiniTileWhiteInnerNe @@ -4343,6 +4343,9 @@ entities: 3242: 61,-54 3243: 59,-53 3244: 60,-53 + 4119: 58,-22 + 4120: 59,-22 + 4121: 61,-22 - node: color: '#70FFB3FF' id: WarnLineGreyscaleS @@ -4386,6 +4389,9 @@ entities: 3248: 59,-52 3249: 60,-52 3250: 61,-52 + 4122: 58,-22 + 4123: 59,-22 + 4124: 61,-22 - node: color: '#D381C9FF' id: WarnLineGreyscaleW @@ -4456,6 +4462,9 @@ entities: 3912: -19,-71 3913: -18,-71 4096: 46,-68 + 4112: 59,-22 + 4113: 58,-22 + 4114: 61,-22 - node: color: '#FFFFFFFF' id: WarnLineS @@ -4502,6 +4511,9 @@ entities: 3466: -17,-17 3467: -17,-20 4097: 46,-72 + 4110: 61,-22 + 4111: 59,-22 + 4115: 58,-22 - node: angle: 4.71238898038469 rad color: '#FFFFFFFF' @@ -4988,33 +5000,33 @@ entities: 2,-11: 0: 61166 3,-10: - 0: 61440 - 1: 4 + 0: 61638 + 1: 32 + 3,-12: + 1: 32 + 0: 9920 3,-11: - 0: 32992 + 0: 8416 3,-13: 0: 61164 - 3,-12: - 1: 1024 - 0: 32768 + 4,-12: + 0: 240 4,-11: - 0: 34679 + 0: 1911 4,-10: - 0: 61440 + 0: 61680 4,-13: 0: 15288 - 4,-12: - 0: 32768 5,-12: - 1: 256 - 0: 34952 + 0: 43928 + 1: 32 5,-10: - 1: 1 - 0: 63624 + 0: 63643 + 1: 32 5,-13: 0: 65535 5,-11: - 0: 34952 + 0: 43690 6,-12: 0: 64435 6,-11: @@ -5105,12 +5117,12 @@ entities: 0: 56799 -7,-4: 0: 65523 + -6,-8: + 0: 1800 -6,-7: 0: 65535 -6,-5: 0: 45942 - -6,-8: - 0: 1544 -6,-6: 0: 58980 -6,-9: @@ -5234,10 +5246,10 @@ entities: 0: 55551 8,-16: 0: 271 - 4: 17408 + 2: 17408 8,-15: 1: 4096 - 4: 4 + 2: 4 8,-14: 0: 32752 8,-13: @@ -5251,7 +5263,7 @@ entities: -5,-15: 0: 63949 -4,-14: - 0: 65527 + 0: 65535 -5,-14: 0: 40191 -4,-17: @@ -5289,7 +5301,7 @@ entities: 1,-20: 0: 30711 1,-19: - 0: 18023 + 0: 9831 1,-17: 0: 4095 1,-18: @@ -5932,8 +5944,8 @@ entities: 0: 65535 9,-16: 0: 15 - 5: 4352 - 2: 17408 + 3: 4352 + 4: 17408 10,-20: 0: 65280 10,-19: @@ -5944,8 +5956,8 @@ entities: 0: 65535 10,-16: 0: 15 - 2: 4352 - 6: 17408 + 4: 4352 + 5: 17408 11,-20: 0: 65024 11,-19: @@ -5957,7 +5969,7 @@ entities: 11,-16: 0: 15 1: 35840 - 2: 4352 + 4: 4352 12,-20: 0: 65280 12,-19: @@ -5990,7 +6002,7 @@ entities: 0: 61183 12,-16: 0: 3 - 2: 2184 + 4: 2184 1: 8960 13,-20: 0: 65024 @@ -6003,7 +6015,7 @@ entities: 13,-21: 1: 16177 13,-16: - 2: 819 + 4: 819 1: 8 14,-20: 0: 61704 @@ -6206,7 +6218,7 @@ entities: 0: 59392 11,-15: 1: 32904 - 2: 1 + 4: 1 12,-14: 0: 62926 11,-14: @@ -6218,7 +6230,7 @@ entities: 0: 61262 12,-12: 0: 43215 - 3: 4352 + 6: 4352 13,-15: 0: 65280 1: 4 @@ -6316,12 +6328,12 @@ entities: 1: 63624 11,-12: 0: 4508 - 3: 52224 + 6: 52224 12,-11: - 3: 4369 + 6: 4369 0: 52424 11,-11: - 3: 52428 + 6: 52428 0: 4371 12,-10: 0: 17748 @@ -6342,7 +6354,7 @@ entities: 13,-8: 0: 35768 14,-11: - 0: 20206 + 0: 52974 14,-10: 0: 61166 14,-9: @@ -6358,7 +6370,7 @@ entities: 16,-6: 0: 60967 15,-6: - 0: 65167 + 0: 64143 16,-5: 0: 1918 15,-5: @@ -6540,14 +6552,14 @@ entities: 11,-3: 1: 12288 9,-15: - 5: 1 - 2: 4 + 3: 1 + 4: 4 1: 32768 9,-14: 0: 36848 10,-15: - 2: 1 - 6: 4 + 4: 1 + 5: 4 10,-14: 0: 65520 -8,-24: @@ -6643,6 +6655,7 @@ entities: - volume: 2500 temperature: 293.15 moles: + - 6666.982 - 0 - 0 - 0 @@ -6654,6 +6667,50 @@ entities: - 0 - 0 - 0 + - volume: 2500 + temperature: 293.15 + moles: + - 0 + - 6666.982 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - volume: 2500 + temperature: 293.15 + moles: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - volume: 2500 + temperature: 293.15 + moles: + - 0 + - 0 + - 0 + - 6666.982 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 - 0 - volume: 2500 temperature: 235 @@ -6670,51 +6727,6 @@ entities: - 0 - 0 - 0 - - volume: 2500 - temperature: 293.15 - moles: - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - volume: 2500 - temperature: 293.15 - moles: - - 0 - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - volume: 2500 - temperature: 293.15 - moles: - - 0 - - 0 - - 0 - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 chunkSize: 4 - type: GasTileOverlay - type: RadiationGridResistance @@ -9307,18 +9319,6 @@ entities: rot: 3.141592653589793 rad pos: -31.5,-7.5 parent: 2 - - uid: 7770 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 25.5,-9.5 - parent: 2 - - uid: 18401 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 23.5,-9.5 - parent: 2 - proto: AirlockExternalGlassShuttleEscape entities: - uid: 1106 @@ -9345,6 +9345,18 @@ entities: parent: 2 - proto: AirlockExternalGlassShuttleLocked entities: + - uid: 616 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 23.5,-9.5 + parent: 2 + - uid: 738 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 25.5,-9.5 + parent: 2 - uid: 6945 components: - type: Transform @@ -10046,7 +10058,7 @@ entities: pos: 11.5,-30.5 parent: 2 - type: Door - secondsUntilStateChange: -114584.41 + secondsUntilStateChange: -127812.8 state: Opening - type: DeviceLinkSource lastSignals: @@ -10464,11 +10476,6 @@ entities: - type: Transform pos: -8.5,-60.5 parent: 2 - - uid: 616 - components: - - type: Transform - pos: -12.5,-56.5 - parent: 2 - uid: 617 components: - type: Transform @@ -10529,11 +10536,6 @@ entities: - type: Transform pos: 6.5,-68.5 parent: 2 - - uid: 738 - components: - - type: Transform - pos: 6.5,-72.5 - parent: 2 - uid: 742 components: - type: Transform @@ -10705,6 +10707,12 @@ entities: - type: Transform pos: 65.5,-53.5 parent: 2 + - uid: 12409 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-56.5 + parent: 2 - uid: 13804 components: - type: Transform @@ -10740,6 +10748,11 @@ entities: - type: Transform pos: 49.5,-31.5 parent: 2 + - uid: 15470 + components: + - type: Transform + pos: 5.5,-72.5 + parent: 2 - uid: 18040 components: - type: Transform @@ -11525,14 +11538,6 @@ entities: rot: 1.5707963267948966 rad pos: 57.5,-68.5 parent: 2 - - uid: 4999 - components: - - type: MetaData - name: Security Outpost APC - - type: Transform - rot: -1.5707963267948966 rad - pos: 6.5,-51.5 - parent: 2 - uid: 5615 components: - type: MetaData @@ -11859,6 +11864,13 @@ entities: rot: 1.5707963267948966 rad pos: -9.5,-22.5 parent: 2 + - uid: 11956 + components: + - type: MetaData + name: Security Outpost APC + - type: Transform + pos: 9.5,-46.5 + parent: 2 - uid: 12036 components: - type: MetaData @@ -12003,7 +12015,7 @@ entities: - uid: 5641 components: - type: Transform - pos: -22.45828,-51.977993 + pos: -22.370735,-51.96643 parent: 2 - proto: ArrivalsShuttleTimer entities: @@ -12054,6 +12066,11 @@ entities: - type: Transform pos: 29.930677,-44.420216 parent: 2 + - uid: 19661 + components: + - type: Transform + pos: -48.02379,-45.496872 + parent: 2 - proto: AsimovCircuitBoard entities: - uid: 18617 @@ -12576,11 +12593,6 @@ entities: parent: 2 - proto: Beaker entities: - - uid: 3873 - components: - - type: Transform - pos: -3.5478697,-47.418797 - parent: 2 - uid: 4085 components: - type: Transform @@ -12596,16 +12608,6 @@ entities: - type: Transform pos: 44.564533,-15.393791 parent: 2 - - uid: 19022 - components: - - type: Transform - pos: -3.5270362,-47.1165 - parent: 2 - - uid: 19023 - components: - - type: Transform - pos: -3.7145362,-47.27286 - parent: 2 - proto: Bed entities: - uid: 354 @@ -12882,7 +12884,7 @@ entities: - uid: 18036 components: - type: Transform - pos: 80.17493,-26.997755 + pos: 72.49216,-25.507568 parent: 2 - proto: Biogenerator entities: @@ -12928,24 +12930,6 @@ entities: - type: Transform pos: 61.5,-53.5 parent: 2 - - uid: 5029 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,-31.5 - parent: 2 - - uid: 5030 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,-32.5 - parent: 2 - - uid: 5031 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,-33.5 - parent: 2 - uid: 13934 components: - type: Transform @@ -12958,6 +12942,12 @@ entities: parent: 2 - proto: BlastDoorXeno entities: + - uid: 313 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-33.5 + parent: 2 - uid: 487 components: - type: Transform @@ -13028,11 +13018,134 @@ entities: - type: Transform pos: -66.5,-62.5 parent: 2 + - uid: 1341 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-32.5 + parent: 2 + - uid: 2501 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-31.5 + parent: 2 - uid: 7185 components: - type: Transform pos: 59.5,-78.5 parent: 2 +- proto: BlastDoorXenoOpen + entities: + - uid: 14056 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 14.5,-43.5 + parent: 2 + - uid: 16098 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-40.5 + parent: 2 + - uid: 19556 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 19.5,-39.5 + parent: 2 + - uid: 19557 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 18.5,-39.5 + parent: 2 + - uid: 19558 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 17.5,-39.5 + parent: 2 + - uid: 19559 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 16.5,-39.5 + parent: 2 + - uid: 19560 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 15.5,-39.5 + parent: 2 + - uid: 19561 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-42.5 + parent: 2 + - uid: 19562 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-44.5 + parent: 2 + - uid: 19563 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-41.5 + parent: 2 + - uid: 19564 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 14.5,-40.5 + parent: 2 + - uid: 19565 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 14.5,-44.5 + parent: 2 + - uid: 19566 + components: + - type: Transform + pos: 18.5,-45.5 + parent: 2 + - uid: 19567 + components: + - type: Transform + pos: 17.5,-45.5 + parent: 2 + - uid: 19568 + components: + - type: Transform + pos: 16.5,-45.5 + parent: 2 + - uid: 19570 + components: + - type: Transform + pos: 15.5,-45.5 + parent: 2 + - uid: 19571 + components: + - type: Transform + pos: 19.5,-45.5 + parent: 2 + - uid: 19572 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-43.5 + parent: 2 + - uid: 19575 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 14.5,-41.5 + parent: 2 - proto: BodyBagFolded entities: - uid: 13193 @@ -13310,6 +13423,11 @@ entities: parent: 2 - proto: BoxBeaker entities: + - uid: 7456 + components: + - type: Transform + pos: -3.621653,-47.22137 + parent: 2 - uid: 7760 components: - type: Transform @@ -13402,7 +13520,7 @@ entities: - uid: 6212 components: - type: Transform - pos: 13.349184,-58.437775 + pos: 13.349543,-58.569397 parent: 2 - uid: 18168 components: @@ -13417,7 +13535,7 @@ entities: - uid: 18182 components: - type: Transform - pos: 13.424348,-58.281063 + pos: 13.42246,-58.37134 parent: 2 - uid: 18184 components: @@ -13425,6 +13543,12 @@ entities: rot: -1.5707963267948966 rad pos: 11.485994,-16.436533 parent: 2 + - uid: 19656 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -48.45786,-44.761845 + parent: 2 - proto: BoxFolderGreen entities: - uid: 18186 @@ -13620,6 +13744,11 @@ entities: - type: Transform pos: 46.49753,-47.4596 parent: 2 + - uid: 19673 + components: + - type: Transform + pos: -49.55333,-43.35533 + parent: 2 - proto: Brutepack entities: - uid: 5938 @@ -13750,6 +13879,11 @@ entities: - type: Transform pos: 36.5,-82.5 parent: 2 + - uid: 19573 + components: + - type: Transform + pos: 13.5,-41.5 + parent: 2 - proto: ButtonFrameCautionSecurity entities: - uid: 2697 @@ -14206,6 +14340,11 @@ entities: - type: Transform pos: -9.5,-22.5 parent: 2 + - uid: 3127 + components: + - type: Transform + pos: 5.5,-73.5 + parent: 2 - uid: 3183 components: - type: Transform @@ -14496,6 +14635,11 @@ entities: - type: Transform pos: 57.5,-24.5 parent: 2 + - uid: 4068 + components: + - type: Transform + pos: 9.5,-48.5 + parent: 2 - uid: 4070 components: - type: Transform @@ -14726,11 +14870,6 @@ entities: - type: Transform pos: 57.5,-64.5 parent: 2 - - uid: 5117 - components: - - type: Transform - pos: 6.5,-51.5 - parent: 2 - uid: 5181 components: - type: Transform @@ -14746,11 +14885,6 @@ entities: - type: Transform pos: -12.5,-34.5 parent: 2 - - uid: 5207 - components: - - type: Transform - pos: 5.5,-51.5 - parent: 2 - uid: 5210 components: - type: Transform @@ -21321,30 +21455,10 @@ entities: - type: Transform pos: 6.5,-77.5 parent: 2 - - uid: 12407 - components: - - type: Transform - pos: 6.5,-76.5 - parent: 2 - - uid: 12408 - components: - - type: Transform - pos: 6.5,-75.5 - parent: 2 - - uid: 12409 - components: - - type: Transform - pos: 6.5,-74.5 - parent: 2 - - uid: 12410 - components: - - type: Transform - pos: 6.5,-73.5 - parent: 2 - uid: 12411 components: - type: Transform - pos: 6.5,-72.5 + pos: 5.5,-75.5 parent: 2 - uid: 12412 components: @@ -26241,11 +26355,6 @@ entities: - type: Transform pos: 17.5,-50.5 parent: 2 - - uid: 18416 - components: - - type: Transform - pos: 10.5,-48.5 - parent: 2 - uid: 18417 components: - type: Transform @@ -26561,6 +26670,11 @@ entities: - type: Transform pos: 48.5,-56.5 parent: 2 + - uid: 19089 + components: + - type: Transform + pos: -15.5,-19.5 + parent: 2 - uid: 19091 components: - type: Transform @@ -26871,6 +26985,76 @@ entities: - type: Transform pos: 21.5,-23.5 parent: 2 + - uid: 19537 + components: + - type: Transform + pos: 5.5,-72.5 + parent: 2 + - uid: 19584 + components: + - type: Transform + pos: 5.5,-71.5 + parent: 2 + - uid: 19585 + components: + - type: Transform + pos: 5.5,-76.5 + parent: 2 + - uid: 19586 + components: + - type: Transform + pos: 5.5,-74.5 + parent: 2 + - uid: 19597 + components: + - type: Transform + pos: 46.5,-71.5 + parent: 2 + - uid: 19601 + components: + - type: Transform + pos: 46.5,-70.5 + parent: 2 + - uid: 19602 + components: + - type: Transform + pos: 46.5,-69.5 + parent: 2 + - uid: 19604 + components: + - type: Transform + pos: 14.5,-56.5 + parent: 2 + - uid: 19627 + components: + - type: Transform + pos: 9.5,-47.5 + parent: 2 + - uid: 19628 + components: + - type: Transform + pos: 9.5,-46.5 + parent: 2 + - uid: 19676 + components: + - type: Transform + pos: 94.5,-43.5 + parent: 2 + - uid: 19677 + components: + - type: Transform + pos: 95.5,-43.5 + parent: 2 + - uid: 19678 + components: + - type: Transform + pos: 94.5,-47.5 + parent: 2 + - uid: 19679 + components: + - type: Transform + pos: 95.5,-47.5 + parent: 2 - proto: CableApcStack entities: - uid: 9765 @@ -32767,8 +32951,23 @@ entities: - type: Transform pos: 37.5,-71.5 parent: 2 + - uid: 19591 + components: + - type: Transform + pos: 2.5,-75.5 + parent: 2 + - uid: 19592 + components: + - type: Transform + pos: 2.5,-76.5 + parent: 2 - proto: CableMV entities: + - uid: 185 + components: + - type: Transform + pos: 7.5,-49.5 + parent: 2 - uid: 256 components: - type: Transform @@ -32887,7 +33086,7 @@ entities: - uid: 2769 components: - type: Transform - pos: 6.5,-51.5 + pos: 5.5,-49.5 parent: 2 - uid: 2770 components: @@ -32897,7 +33096,7 @@ entities: - uid: 2771 components: - type: Transform - pos: 5.5,-51.5 + pos: 4.5,-50.5 parent: 2 - uid: 2778 components: @@ -33114,6 +33313,11 @@ entities: - type: Transform pos: 4.5,-56.5 parent: 2 + - uid: 4557 + components: + - type: Transform + pos: 6.5,-49.5 + parent: 2 - uid: 4558 components: - type: Transform @@ -33149,6 +33353,11 @@ entities: - type: Transform pos: 82.5,-45.5 parent: 2 + - uid: 4999 + components: + - type: Transform + pos: 4.5,-49.5 + parent: 2 - uid: 5009 components: - type: Transform @@ -36774,6 +36983,11 @@ entities: - type: Transform pos: 5.5,-25.5 parent: 2 + - uid: 15123 + components: + - type: Transform + pos: 8.5,-49.5 + parent: 2 - uid: 15148 components: - type: Transform @@ -37344,6 +37558,31 @@ entities: - type: Transform pos: -45.5,-35.5 parent: 2 + - uid: 19623 + components: + - type: Transform + pos: 9.5,-49.5 + parent: 2 + - uid: 19624 + components: + - type: Transform + pos: 9.5,-48.5 + parent: 2 + - uid: 19625 + components: + - type: Transform + pos: 9.5,-47.5 + parent: 2 + - uid: 19626 + components: + - type: Transform + pos: 9.5,-46.5 + parent: 2 + - uid: 19646 + components: + - type: Transform + pos: 62.5,-21.5 + parent: 2 - proto: CableTerminal entities: - uid: 3002 @@ -37462,6 +37701,24 @@ entities: - type: Transform pos: -20.522596,-33.289913 parent: 2 + - uid: 19615 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -19.744207,-32.486958 + parent: 2 + - uid: 19616 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -19.320597,-32.493908 + parent: 2 + - uid: 19617 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -19.52893,-32.65374 + parent: 2 - proto: CandlePurpleSmall entities: - uid: 4411 @@ -37989,6 +38246,36 @@ entities: - type: Transform pos: 52.5,-55.5 parent: 2 + - uid: 19610 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -19.5,-33.5 + parent: 2 + - uid: 19611 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -19.5,-32.5 + parent: 2 + - uid: 19612 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -20.5,-32.5 + parent: 2 + - uid: 19613 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -19.5,-31.5 + parent: 2 + - uid: 19614 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-32.5 + parent: 2 - proto: CarpetBlue entities: - uid: 5129 @@ -38106,6 +38393,31 @@ entities: - type: Transform pos: -51.5,-60.5 parent: 2 +- proto: CarpetChapel + entities: + - uid: 5030 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -20.5,-31.5 + parent: 2 + - uid: 7206 + components: + - type: Transform + pos: -20.5,-33.5 + parent: 2 + - uid: 12850 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-31.5 + parent: 2 + - uid: 19390 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-33.5 + parent: 2 - proto: CarpetGreen entities: - uid: 5945 @@ -39844,6 +40156,12 @@ entities: - type: Transform pos: 56.5,-73.5 parent: 2 + - uid: 12410 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-56.5 + parent: 2 - uid: 12854 components: - type: Transform @@ -40585,16 +40903,6 @@ entities: rot: -1.5707963267948966 rad pos: 36.5,-91.5 parent: 2 - - uid: 19537 - components: - - type: Transform - pos: -11.5,-56.5 - parent: 2 - - uid: 19538 - components: - - type: Transform - pos: -10.5,-56.5 - parent: 2 - uid: 19539 components: - type: Transform @@ -41064,6 +41372,12 @@ entities: rot: 1.5707963267948966 rad pos: 48.5,-25.5 parent: 2 + - uid: 19669 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -46.5,-44.5 + parent: 2 - proto: ChairFoldingSpawnFolded entities: - uid: 9949 @@ -41489,6 +41803,13 @@ entities: rot: 1.5707963267948966 rad pos: -21.5,-35.5 parent: 2 +- proto: Cigar + entities: + - uid: 19658 + components: + - type: Transform + pos: -48.56308,-44.345364 + parent: 2 - proto: CigaretteBanana entities: - uid: 7237 @@ -41508,6 +41829,11 @@ entities: - type: Transform pos: 68.09442,-38.40559 parent: 2 + - uid: 19660 + components: + - type: Transform + pos: -47.900097,-45.23187 + parent: 2 - proto: CigarGold entities: - uid: 4505 @@ -41524,6 +41850,13 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: CigarSpent + entities: + - uid: 19659 + components: + - type: Transform + pos: -47.31308,-45.27867 + parent: 2 - proto: CircuitImprinter entities: - uid: 5897 @@ -41615,11 +41948,6 @@ entities: - type: Transform pos: -20.5,-30.5 parent: 2 - - uid: 313 - components: - - type: Transform - pos: -21.5,-29.5 - parent: 2 - uid: 314 components: - type: Transform @@ -41685,11 +42013,6 @@ entities: - type: Transform pos: -22.5,-28.5 parent: 2 - - uid: 1341 - components: - - type: Transform - pos: -23.5,-29.5 - parent: 2 - uid: 1429 components: - type: Transform @@ -41765,11 +42088,6 @@ entities: - type: Transform pos: -17.5,-35.5 parent: 2 - - uid: 14808 - components: - - type: Transform - pos: -22.5,-29.5 - parent: 2 - uid: 16186 components: - type: Transform @@ -42015,11 +42333,6 @@ entities: - type: Transform pos: 35.5,-78.5 parent: 2 - - uid: 18908 - components: - - type: Transform - pos: 5.5,-73.5 - parent: 2 - uid: 19187 components: - type: Transform @@ -42573,6 +42886,11 @@ entities: - type: Transform pos: 8.443563,-87.12137 parent: 2 + - uid: 19651 + components: + - type: Transform + pos: -47.73321,-44.589058 + parent: 2 - proto: ClothingEyesGlassesChemical entities: - uid: 7693 @@ -42625,12 +42943,12 @@ entities: - uid: 7938 components: - type: Transform - pos: -22.386917,-49.45568 + pos: -22.36449,-49.554092 parent: 2 - uid: 19371 components: - type: Transform - pos: -22.50776,-49.283688 + pos: -22.554304,-49.354877 parent: 2 - proto: ClothingHandsGlovesLatex entities: @@ -43020,6 +43338,13 @@ entities: - type: Transform pos: 83.44597,-72.587036 parent: 2 +- proto: ClothingMultipleHeadphones + entities: + - uid: 17553 + components: + - type: Transform + pos: -37.47268,-60.168076 + parent: 2 - proto: ClothingNeckBling entities: - uid: 18022 @@ -43107,13 +43432,6 @@ entities: - type: Transform pos: 77.515434,-20.379131 parent: 2 -- proto: ClothingNeckHeadphones - entities: - - uid: 17553 - components: - - type: Transform - pos: -37.47268,-60.168076 - parent: 2 - proto: ClothingNeckScarfStripedBlack entities: - uid: 19011 @@ -43361,6 +43679,13 @@ entities: - type: Transform pos: 35.344784,-91.27624 parent: 2 +- proto: ClothingOuterWinterHydro + entities: + - uid: 3873 + components: + - type: Transform + pos: -5.464688,-51.46298 + parent: 2 - proto: ClothingShoesBootsCowboyFancy entities: - uid: 19229 @@ -43698,12 +44023,6 @@ entities: rot: 3.141592653589793 rad pos: -40.5,-80.5 parent: 2 - - uid: 17715 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 5.5,-73.5 - parent: 2 - uid: 17722 components: - type: Transform @@ -44017,6 +44336,12 @@ entities: parent: 2 - proto: ComputerCriminalRecords entities: + - uid: 373 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -2.5,-14.5 + parent: 2 - uid: 3547 components: - type: Transform @@ -44057,6 +44382,14 @@ entities: rot: -1.5707963267948966 rad pos: 72.5,-50.5 parent: 2 +- proto: ComputerFundingAllocation + entities: + - uid: 19368 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 15.5,-58.5 + parent: 2 - proto: ComputerId entities: - uid: 5510 @@ -44221,11 +44554,16 @@ entities: parent: 2 - proto: ComputerSurveillanceCameraMonitor entities: - - uid: 538 + - uid: 374 components: - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-14.5 + rot: -1.5707963267948966 rad + pos: 60.5,-45.5 + parent: 2 + - uid: 7040 + components: + - type: Transform + pos: 69.5,-45.5 parent: 2 - uid: 8248 components: @@ -44242,19 +44580,6 @@ entities: - type: Transform pos: 59.5,-26.5 parent: 2 -- proto: ComputerSurveillanceWirelessCameraMonitor - entities: - - uid: 7040 - components: - - type: Transform - pos: 69.5,-45.5 - parent: 2 - - uid: 10774 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 60.5,-45.5 - parent: 2 - proto: ComputerTelevision entities: - uid: 5478 @@ -45088,21 +45413,21 @@ entities: parent: 2 - proto: CrateCoffin entities: + - uid: 5029 + components: + - type: Transform + pos: -22.5,-29.5 + parent: 2 + - uid: 5031 + components: + - type: Transform + pos: -21.5,-29.5 + parent: 2 - uid: 10301 components: - type: Transform pos: 35.5,-25.5 parent: 2 - - uid: 17707 - components: - - type: Transform - pos: -24.5,-29.5 - parent: 2 - - uid: 19390 - components: - - type: Transform - pos: -24.5,-30.5 - parent: 2 - proto: CrateContrabandStorageSecure entities: - uid: 10331 @@ -45419,6 +45744,13 @@ entities: - 0 - 0 - 0 +- proto: CrateHydroponicsSeeds + entities: + - uid: 19590 + components: + - type: Transform + pos: 6.5,-73.5 + parent: 2 - proto: CrateLockBoxEngineering entities: - uid: 15392 @@ -45435,16 +45767,16 @@ entities: parent: 2 - proto: CrateLockBoxScience entities: + - uid: 536 + components: + - type: Transform + pos: -10.5,-15.5 + parent: 2 - uid: 4348 components: - type: Transform pos: -20.5,-27.5 parent: 2 - - uid: 7099 - components: - - type: Transform - pos: -14.5,-21.5 - parent: 2 - proto: CrateLockBoxSecurity entities: - uid: 12856 @@ -45647,7 +45979,7 @@ entities: - uid: 5592 components: - type: Transform - pos: -53.47501,-42.818943 + pos: -53.56222,-43.09171 parent: 2 - uid: 5593 components: @@ -46525,6 +46857,12 @@ entities: parent: 2 - proto: DisposalBend entities: + - uid: 377 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,-20.5 + parent: 2 - uid: 1189 components: - type: Transform @@ -46619,12 +46957,6 @@ entities: rot: 1.5707963267948966 rad pos: 56.5,-70.5 parent: 2 - - uid: 5426 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-20.5 - parent: 2 - uid: 5730 components: - type: Transform @@ -47239,6 +47571,17 @@ entities: rot: 3.141592653589793 rad pos: 25.5,-77.5 parent: 2 + - uid: 19542 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 28.5,-28.5 + parent: 2 + - uid: 19543 + components: + - type: Transform + pos: 35.5,-28.5 + parent: 2 - proto: DisposalJunction entities: - uid: 4039 @@ -47316,6 +47659,12 @@ entities: parent: 2 - proto: DisposalJunctionFlipped entities: + - uid: 529 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 28.5,-23.5 + parent: 2 - uid: 8061 components: - type: Transform @@ -47403,6 +47752,30 @@ entities: rot: 1.5707963267948966 rad pos: 22.5,-58.5 parent: 2 + - uid: 378 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-20.5 + parent: 2 + - uid: 379 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-20.5 + parent: 2 + - uid: 528 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-20.5 + parent: 2 + - uid: 533 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-20.5 + parent: 2 - uid: 1462 components: - type: Transform @@ -47536,11 +47909,6 @@ entities: rot: 1.5707963267948966 rad pos: -9.5,-20.5 parent: 2 - - uid: 5101 - components: - - type: Transform - pos: -10.5,-19.5 - parent: 2 - uid: 5373 components: - type: Transform @@ -47553,11 +47921,6 @@ entities: rot: 3.141592653589793 rad pos: 56.5,-72.5 parent: 2 - - uid: 5460 - components: - - type: Transform - pos: -10.5,-18.5 - parent: 2 - uid: 5732 components: - type: Transform @@ -48107,11 +48470,6 @@ entities: rot: 3.141592653589793 rad pos: 4.5,-44.5 parent: 2 - - uid: 13452 - components: - - type: Transform - pos: -10.5,-17.5 - parent: 2 - uid: 13821 components: - type: Transform @@ -49430,12 +49788,6 @@ entities: rot: 1.5707963267948966 rad pos: 29.5,-23.5 parent: 2 - - uid: 15698 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 28.5,-23.5 - parent: 2 - uid: 15699 components: - type: Transform @@ -50892,11 +51244,6 @@ entities: - type: Transform pos: -47.5,-36.5 parent: 2 - - uid: 16521 - components: - - type: Transform - pos: -10.5,-16.5 - parent: 2 - uid: 16863 components: - type: Transform @@ -50961,6 +51308,12 @@ entities: - type: Transform pos: -2.5,-31.5 parent: 2 + - uid: 17213 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -20.5,-49.5 + parent: 2 - uid: 17295 components: - type: Transform @@ -51077,6 +51430,60 @@ entities: - type: Transform pos: 7.5,-58.5 parent: 2 + - uid: 19540 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 28.5,-25.5 + parent: 2 + - uid: 19541 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 28.5,-26.5 + parent: 2 + - uid: 19545 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 28.5,-27.5 + parent: 2 + - uid: 19546 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 29.5,-28.5 + parent: 2 + - uid: 19547 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 30.5,-28.5 + parent: 2 + - uid: 19548 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 31.5,-28.5 + parent: 2 + - uid: 19549 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 32.5,-28.5 + parent: 2 + - uid: 19550 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 33.5,-28.5 + parent: 2 + - uid: 19551 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 34.5,-28.5 + parent: 2 - proto: DisposalPipeBroken entities: - uid: 7201 @@ -51092,6 +51499,12 @@ entities: parent: 2 - proto: DisposalTrunk entities: + - uid: 535 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-21.5 + parent: 2 - uid: 1459 components: - type: Transform @@ -51127,11 +51540,6 @@ entities: - type: Transform pos: 31.5,-37.5 parent: 2 - - uid: 5100 - components: - - type: Transform - pos: -10.5,-15.5 - parent: 2 - uid: 5801 components: - type: Transform @@ -51337,18 +51745,24 @@ entities: rot: 1.5707963267948966 rad pos: 19.5,-25.5 parent: 2 - - uid: 19368 + - uid: 19367 components: - type: Transform rot: -1.5707963267948966 rad - pos: -20.5,-49.5 + pos: -19.5,-49.5 + parent: 2 + - uid: 19544 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 35.5,-29.5 parent: 2 - proto: DisposalUnit entities: - - uid: 1856 + - uid: 375 components: - type: Transform - pos: -10.5,-15.5 + pos: -14.5,-21.5 parent: 2 - uid: 1883 components: @@ -51450,11 +51864,6 @@ entities: - type: Transform pos: 19.5,-25.5 parent: 2 - - uid: 7461 - components: - - type: Transform - pos: -20.5,-49.5 - parent: 2 - uid: 7755 components: - type: Transform @@ -51545,6 +51954,11 @@ entities: - type: Transform pos: 31.5,-37.5 parent: 2 + - uid: 19593 + components: + - type: Transform + pos: -19.5,-49.5 + parent: 2 - proto: DisposalXJunction entities: - uid: 7892 @@ -51653,7 +52067,7 @@ entities: - type: Transform pos: -9.316594,-6.1209764 parent: 2 -- proto: DoubleEmergencyNitrogenTank +- proto: DoubleEmergencyOxygenTankFilled entities: - uid: 4161 components: @@ -51665,8 +52079,6 @@ entities: - type: Transform pos: 80.4613,-23.302551 parent: 2 -- proto: DoubleEmergencyOxygenTankFilled - entities: - uid: 7692 components: - type: Transform @@ -51786,6 +52198,24 @@ entities: - type: Transform pos: 21.681454,-19.427164 parent: 2 +- proto: DrinkEnergyDrinkCan + entities: + - uid: 19665 + components: + - type: Transform + pos: -46.49681,-46.443466 + parent: 2 + - uid: 19667 + components: + - type: Transform + pos: -46.298893,-46.683216 + parent: 2 + - uid: 19668 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -46.486393,-46.93339 + parent: 2 - proto: DrinkEnergyDrinkJug entities: - uid: 17562 @@ -51798,7 +52228,7 @@ entities: - uid: 5481 components: - type: Transform - pos: -38.302177,-55.24425 + pos: -38.411427,-55.289524 parent: 2 - proto: DrinkFlaskBar entities: @@ -51916,6 +52346,20 @@ entities: - type: Transform pos: 21.108538,-19.239534 parent: 2 +- proto: DrinkMugBlack + entities: + - uid: 19662 + components: + - type: Transform + pos: -48.28667,-46.57887 + parent: 2 +- proto: DrinkMugBlue + entities: + - uid: 19663 + components: + - type: Transform + pos: -48.168613,-44.37595 + parent: 2 - proto: DrinkMugMetal entities: - uid: 18321 @@ -51947,6 +52391,11 @@ entities: - type: Transform pos: 32.460705,-83.503525 parent: 2 + - uid: 19664 + components: + - type: Transform + pos: -47.64084,-45.744957 + parent: 2 - proto: DrinkMugRed entities: - uid: 17563 @@ -51987,7 +52436,7 @@ entities: - uid: 19296 components: - type: Transform - pos: -5.7709203,-19.359259 + pos: -3.766451,-19.48293 parent: 2 - proto: DrinkWaterBottleFull entities: @@ -52653,11 +53102,6 @@ entities: rot: -1.5707963267948966 rad pos: 8.5,-4.5 parent: 2 - - uid: 18339 - components: - - type: Transform - pos: 11.5,-70.5 - parent: 2 - uid: 18340 components: - type: Transform @@ -52674,6 +53118,12 @@ entities: - type: Transform pos: -27.5,-62.5 parent: 2 + - uid: 19650 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 11.5,-69.5 + parent: 2 - proto: FaxMachineBase entities: - uid: 1889 @@ -58307,6 +58757,12 @@ entities: - type: Transform pos: 31.513302,-94.31904 parent: 2 + - uid: 19666 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -47.392643,-44.84861 + parent: 2 - proto: FoodDonkpocketPizza entities: - uid: 6972 @@ -58544,7 +59000,7 @@ entities: - uid: 5482 components: - type: Transform - pos: -38.651333,-55.430145 + pos: -42.49177,-52.28213 parent: 2 - proto: FoodPieXenoSlice entities: @@ -76479,6 +76935,11 @@ entities: - type: Transform pos: -42.492443,-82.46442 parent: 2 + - uid: 19670 + components: + - type: Transform + pos: -52.550312,-52.240044 + parent: 2 - proto: Girder entities: - uid: 4080 @@ -76898,36 +77359,6 @@ entities: - type: Transform pos: 16.5,-47.5 parent: 2 - - uid: 373 - components: - - type: Transform - pos: 14.5,-43.5 - parent: 2 - - uid: 374 - components: - - type: Transform - pos: 15.5,-45.5 - parent: 2 - - uid: 375 - components: - - type: Transform - pos: 18.5,-45.5 - parent: 2 - - uid: 377 - components: - - type: Transform - pos: 16.5,-45.5 - parent: 2 - - uid: 378 - components: - - type: Transform - pos: 17.5,-45.5 - parent: 2 - - uid: 379 - components: - - type: Transform - pos: 15.5,-40.5 - parent: 2 - uid: 422 components: - type: Transform @@ -76968,32 +77399,12 @@ entities: - type: Transform pos: -3.5,-23.5 parent: 2 - - uid: 528 - components: - - type: Transform - pos: 14.5,-40.5 - parent: 2 - - uid: 529 - components: - - type: Transform - pos: 14.5,-41.5 - parent: 2 - - uid: 533 - components: - - type: Transform - pos: 15.5,-41.5 - parent: 2 - - uid: 535 - components: - - type: Transform - pos: 19.5,-44.5 - parent: 2 - - uid: 536 - components: - - type: Transform - pos: 18.5,-40.5 - parent: 2 - uid: 537 + components: + - type: Transform + pos: 20.5,-43.5 + parent: 2 + - uid: 538 components: - type: Transform pos: 20.5,-41.5 @@ -77001,57 +77412,77 @@ entities: - uid: 539 components: - type: Transform - pos: 16.5,-39.5 + pos: 19.5,-43.5 parent: 2 - uid: 540 components: - type: Transform - pos: 17.5,-40.5 + pos: 20.5,-40.5 parent: 2 - uid: 541 components: - type: Transform - pos: 19.5,-39.5 + pos: 19.5,-44.5 parent: 2 - uid: 542 components: - type: Transform - pos: 20.5,-40.5 + pos: 14.5,-41.5 parent: 2 - uid: 555 components: - type: Transform - pos: 18.5,-39.5 + pos: 15.5,-41.5 parent: 2 - uid: 557 components: - type: Transform - pos: 17.5,-39.5 + pos: 15.5,-40.5 parent: 2 - uid: 558 components: - type: Transform - pos: 19.5,-40.5 + pos: 14.5,-40.5 parent: 2 - uid: 559 components: - type: Transform - pos: 20.5,-43.5 + pos: 14.5,-44.5 parent: 2 - uid: 560 components: - type: Transform - pos: 20.5,-44.5 + pos: 18.5,-39.5 + parent: 2 + - uid: 562 + components: + - type: Transform + pos: 15.5,-39.5 + parent: 2 + - uid: 563 + components: + - type: Transform + pos: 14.5,-43.5 parent: 2 - uid: 565 components: - type: Transform - pos: 16.5,-40.5 + pos: 18.5,-40.5 parent: 2 - uid: 566 components: - type: Transform - pos: 15.5,-39.5 + pos: 17.5,-39.5 + parent: 2 + - uid: 568 + components: + - type: Transform + pos: 17.5,-40.5 + parent: 2 + - uid: 581 + components: + - type: Transform + pos: 16.5,-40.5 parent: 2 - uid: 614 components: @@ -77626,17 +78057,17 @@ entities: - uid: 1558 components: - type: Transform - pos: 18.5,-44.5 + pos: 15.5,-45.5 parent: 2 - uid: 1559 components: - type: Transform - pos: 17.5,-44.5 + pos: 19.5,-40.5 parent: 2 - uid: 1560 components: - type: Transform - pos: 19.5,-45.5 + pos: 19.5,-41.5 parent: 2 - uid: 1564 components: @@ -77656,12 +78087,12 @@ entities: - uid: 1596 components: - type: Transform - pos: 14.5,-44.5 + pos: 15.5,-43.5 parent: 2 - uid: 1597 components: - type: Transform - pos: 15.5,-44.5 + pos: 16.5,-39.5 parent: 2 - uid: 1634 components: @@ -77701,12 +78132,32 @@ entities: - uid: 1642 components: - type: Transform - pos: 15.5,-43.5 + pos: 17.5,-44.5 parent: 2 - uid: 1644 components: - type: Transform - pos: 16.5,-44.5 + pos: 18.5,-45.5 + parent: 2 + - uid: 1645 + components: + - type: Transform + pos: 19.5,-42.5 + parent: 2 + - uid: 1646 + components: + - type: Transform + pos: 20.5,-42.5 + parent: 2 + - uid: 1647 + components: + - type: Transform + pos: 19.5,-45.5 + parent: 2 + - uid: 1648 + components: + - type: Transform + pos: 20.5,-44.5 parent: 2 - uid: 1659 components: @@ -77738,6 +78189,16 @@ entities: - type: Transform pos: -30.5,-46.5 parent: 2 + - uid: 1667 + components: + - type: Transform + pos: 18.5,-44.5 + parent: 2 + - uid: 1668 + components: + - type: Transform + pos: 16.5,-44.5 + parent: 2 - uid: 1671 components: - type: Transform @@ -77813,11 +78274,26 @@ entities: - type: Transform pos: -40.5,-43.5 parent: 2 + - uid: 1688 + components: + - type: Transform + pos: 19.5,-39.5 + parent: 2 + - uid: 1692 + components: + - type: Transform + pos: 16.5,-45.5 + parent: 2 - uid: 1693 components: - type: Transform pos: 26.5,-40.5 parent: 2 + - uid: 1696 + components: + - type: Transform + pos: 15.5,-44.5 + parent: 2 - uid: 1703 components: - type: Transform @@ -79093,18 +79569,6 @@ entities: - type: Transform pos: 39.5,-9.5 parent: 2 - - uid: 6244 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 19.5,-43.5 - parent: 2 - - uid: 6248 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 20.5,-42.5 - parent: 2 - uid: 6302 components: - type: Transform @@ -80360,6 +80824,11 @@ entities: - type: Transform pos: 61.5,-46.5 parent: 2 + - uid: 13843 + components: + - type: Transform + pos: 62.5,-21.5 + parent: 2 - uid: 13979 components: - type: Transform @@ -80496,12 +80965,6 @@ entities: - type: Transform pos: -5.5,-18.5 parent: 2 - - uid: 16097 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 19.5,-42.5 - parent: 2 - uid: 16131 components: - type: Transform @@ -80512,12 +80975,6 @@ entities: - type: Transform pos: 65.5,-44.5 parent: 2 - - uid: 16170 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 19.5,-41.5 - parent: 2 - uid: 16183 components: - type: Transform @@ -80653,6 +81110,11 @@ entities: - type: Transform pos: -18.5,-88.5 parent: 2 + - uid: 16818 + components: + - type: Transform + pos: 17.5,-45.5 + parent: 2 - uid: 16865 components: - type: Transform @@ -80673,11 +81135,6 @@ entities: - type: Transform pos: 57.5,-47.5 parent: 2 - - uid: 17213 - components: - - type: Transform - pos: 59.5,-40.5 - parent: 2 - uid: 17241 components: - type: Transform @@ -81309,6 +81766,23 @@ entities: parent: 2 - proto: GrilleDiagonal entities: + - uid: 1797 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 20.5,-45.5 + parent: 2 + - uid: 1798 + components: + - type: Transform + pos: 14.5,-39.5 + parent: 2 + - uid: 1799 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-45.5 + parent: 2 - uid: 4656 components: - type: Transform @@ -81378,29 +81852,12 @@ entities: rot: -1.5707963267948966 rad pos: 25.5,-51.5 parent: 2 - - uid: 19070 + - uid: 14429 components: - type: Transform rot: -1.5707963267948966 rad pos: 20.5,-39.5 parent: 2 - - uid: 19088 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 14.5,-45.5 - parent: 2 - - uid: 19089 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 20.5,-45.5 - parent: 2 - - uid: 19090 - components: - - type: Transform - pos: 14.5,-39.5 - parent: 2 - proto: GrilleSpawner entities: - uid: 6412 @@ -81970,7 +82427,7 @@ entities: - uid: 13879 components: - type: Transform - pos: 62.5,-40.5 + pos: 61.49383,-42.487232 parent: 2 - uid: 19346 components: @@ -82027,7 +82484,7 @@ entities: - uid: 7218 components: - type: Transform - pos: 72.516365,-25.447449 + pos: 80.20865,-27.102425 parent: 2 - proto: HappyHonkMime entities: @@ -82697,7 +83154,7 @@ entities: - uid: 5182 components: - type: Transform - pos: -3.353551,-47.34583 + pos: -3.371653,-47.471542 parent: 2 - proto: IceCrust entities: @@ -84045,21 +84502,66 @@ entities: parent: 2 - proto: MaintenancePlantSpawner entities: + - uid: 2407 + components: + - type: Transform + pos: -16.5,-46.5 + parent: 2 + - uid: 7770 + components: + - type: Transform + pos: 6.5,-74.5 + parent: 2 + - uid: 12407 + components: + - type: Transform + pos: 6.5,-75.5 + parent: 2 - uid: 17240 components: - type: Transform pos: 75.5,-59.5 parent: 2 - - uid: 17242 - components: - - type: Transform - pos: 6.5,-79.5 - parent: 2 - uid: 17243 components: - type: Transform pos: -36.5,-72.5 parent: 2 + - uid: 17715 + components: + - type: Transform + pos: 6.5,-79.5 + parent: 2 + - uid: 19577 + components: + - type: Transform + pos: -9.5,-57.5 + parent: 2 + - uid: 19578 + components: + - type: Transform + pos: -47.5,-22.5 + parent: 2 + - uid: 19579 + components: + - type: Transform + pos: 54.5,-77.5 + parent: 2 + - uid: 19580 + components: + - type: Transform + pos: 50.5,-33.5 + parent: 2 + - uid: 19581 + components: + - type: Transform + pos: 76.5,-30.5 + parent: 2 + - uid: 19582 + components: + - type: Transform + pos: 75.5,-38.5 + parent: 2 - proto: MaintenanceToolSpawner entities: - uid: 7684 @@ -84173,7 +84675,7 @@ entities: - uid: 16376 components: - type: Transform - pos: 15.404405,-58.247597 + pos: 15.45371,-59.515717 parent: 2 - proto: MaterialCloth1 entities: @@ -84201,7 +84703,7 @@ entities: - uid: 16377 components: - type: Transform - pos: 15.690466,-58.495316 + pos: 13.287043,-58.162865 parent: 2 - proto: MaterialWebSilk1 entities: @@ -84230,6 +84732,11 @@ entities: - type: Transform pos: 69.52955,-67.498146 parent: 2 + - uid: 19587 + components: + - type: Transform + pos: 5.5,-79.5 + parent: 2 - proto: MaterialWoodPlank1 entities: - uid: 7336 @@ -84237,6 +84744,13 @@ entities: - type: Transform pos: 80.49108,-26.808891 parent: 2 +- proto: MaterialWoodPlank10 + entities: + - uid: 7461 + components: + - type: Transform + pos: 6.5,-76.5 + parent: 2 - proto: MedicalBed entities: - uid: 466 @@ -84622,7 +85136,7 @@ entities: - uid: 18174 components: - type: Transform - pos: -53.444557,-43.036716 + pos: -53.37472,-43.263702 parent: 2 - proto: MysteryFigureBox entities: @@ -85039,17 +85553,39 @@ entities: - uid: 19179 components: - type: Transform - pos: -48.143295,-45.120937 + pos: -48.52036,-46.15865 parent: 2 - uid: 19180 components: - type: Transform - pos: -47.81517,-45.324207 + pos: -48.3398,-46.283733 parent: 2 - uid: 19181 components: - type: Transform - pos: -48.09642,-45.73074 + pos: -48.47175,-46.374077 + parent: 2 + - uid: 19652 + components: + - type: Transform + pos: -47.56897,-44.41438 + parent: 2 + - uid: 19653 + components: + - type: Transform + pos: -47.437027,-44.581165 + parent: 2 + - uid: 19654 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -47.39536,-45.58186 + parent: 2 + - uid: 19655 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -48.55508,-44.581165 parent: 2 - proto: PaperBin10 entities: @@ -85092,17 +85628,17 @@ entities: - uid: 5487 components: - type: Transform - pos: -44.441452,-54.403904 + pos: -44.415276,-54.439785 parent: 2 - uid: 5488 components: - type: Transform - pos: -45.003952,-54.435177 + pos: -44.545387,-54.377243 parent: 2 - uid: 5489 components: - type: Transform - pos: -45.128952,-54.403904 + pos: -45.073162,-54.39809 parent: 2 - proto: PaperCNCSheet entities: @@ -85285,7 +85821,13 @@ entities: - uid: 19182 components: - type: Transform - pos: -48.237045,-46.090363 + pos: -48.624527,-46.49916 + parent: 2 + - uid: 19657 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -47.357563,-45.783386 parent: 2 - proto: PersonalAI entities: @@ -85319,6 +85861,13 @@ entities: - type: Transform pos: 9.692059,-84.39211 parent: 2 +- proto: PillCanisterPotassiumIodide + entities: + - uid: 19671 + components: + - type: Transform + pos: -52.315937,-52.474583 + parent: 2 - proto: PillSpaceDrugs entities: - uid: 18015 @@ -85397,6 +85946,12 @@ entities: rot: -1.5707963267948966 rad pos: -61.5,-63.5 parent: 2 + - uid: 9585 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 6.5,-77.5 + parent: 2 - uid: 10826 components: - type: Transform @@ -85439,6 +85994,11 @@ entities: rot: -1.5707963267948966 rad pos: -59.5,-61.5 parent: 2 + - uid: 18339 + components: + - type: Transform + pos: 58.5,-20.5 + parent: 2 - uid: 19317 components: - type: Transform @@ -85624,6 +86184,12 @@ entities: parent: 2 - proto: PlasmaWindoorSecureScienceLocked entities: + - uid: 5117 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -23.5,-29.5 + parent: 2 - uid: 11364 components: - type: Transform @@ -85660,6 +86226,12 @@ entities: rot: 3.141592653589793 rad pos: 59.5,-55.5 parent: 2 + - uid: 18316 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 62.5,-20.5 + parent: 2 - proto: PlasmaWindow entities: - uid: 4020 @@ -86718,7 +87290,7 @@ entities: - uid: 19374 components: - type: Transform - pos: -22.293167,-52.05227 + pos: -22.231848,-52.133213 parent: 2 - proto: PowerCellPotato entities: @@ -86760,6 +87332,12 @@ entities: - type: Transform pos: 77.5,-21.5 parent: 2 + - uid: 14432 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-18.5 + parent: 2 - uid: 18523 components: - type: Transform @@ -86777,6 +87355,17 @@ entities: rot: 3.141592653589793 rad pos: 29.5,-90.5 parent: 2 + - uid: 19595 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 62.5,-40.5 + parent: 2 + - uid: 19672 + components: + - type: Transform + pos: -53.5,-42.5 + parent: 2 - proto: PowerComputerCircuitboard entities: - uid: 18307 @@ -87184,12 +87773,6 @@ entities: - type: Transform pos: 56.5,-48.5 parent: 2 - - uid: 9585 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-55.5 - parent: 2 - uid: 9678 components: - type: Transform @@ -87315,12 +87898,6 @@ entities: rot: 3.141592653589793 rad pos: 45.5,-73.5 parent: 2 - - uid: 11956 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 62.5,-21.5 - parent: 2 - uid: 12786 components: - type: Transform @@ -88960,11 +89537,6 @@ entities: rot: -1.5707963267948966 rad pos: -27.5,-53.5 parent: 2 - - uid: 18145 - components: - - type: Transform - pos: -16.5,-52.5 - parent: 2 - uid: 18146 components: - type: Transform @@ -89186,6 +89758,22 @@ entities: rot: 3.141592653589793 rad pos: 47.5,-72.5 parent: 2 + - uid: 19538 + components: + - type: Transform + pos: -17.5,-53.5 + parent: 2 + - uid: 19576 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,-51.5 + parent: 2 + - uid: 19644 + components: + - type: Transform + pos: 62.5,-20.5 + parent: 2 - proto: PoweredlightExterior entities: - uid: 3206 @@ -89386,6 +89974,30 @@ entities: rot: -1.5707963267948966 rad pos: 7.5,-41.5 parent: 2 + - uid: 19596 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 13.5,-38.5 + parent: 2 + - uid: 19598 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 13.5,-46.5 + parent: 2 + - uid: 19599 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 21.5,-38.5 + parent: 2 + - uid: 19600 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 21.5,-46.5 + parent: 2 - proto: PoweredLightPostSmall entities: - uid: 235 @@ -90237,6 +90849,12 @@ entities: rot: 1.5707963267948966 rad pos: 8.5,-90.5 parent: 2 + - uid: 18401 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -12.5,-56.5 + parent: 2 - uid: 19030 components: - type: Transform @@ -90307,11 +90925,17 @@ entities: parent: 2 - proto: Protolathe entities: - - uid: 8016 + - uid: 19088 components: - type: Transform - pos: -3.5,-19.5 + pos: -4.5,-19.5 parent: 2 + - type: TechnologyDatabase + supportedDisciplines: + - Industrial + - Arsenal + - Experimental + - CivilianServices - proto: ProximitySensor entities: - uid: 4726 @@ -90319,13 +90943,6 @@ entities: - type: Transform pos: 69.654045,-72.594284 parent: 2 -- proto: Puddle - entities: - - uid: 16818 - components: - - type: Transform - pos: 79.5,-29.5 - parent: 2 - proto: PuddleBlood entities: - uid: 1197 @@ -90333,6 +90950,41 @@ entities: - type: Transform pos: 42.5,-28.5 parent: 2 + - uid: 1856 + components: + - type: Transform + pos: 79.5,-29.5 + parent: 2 + - uid: 5083 + components: + - type: Transform + pos: 74.5,-43.5 + parent: 2 + - uid: 5100 + components: + - type: Transform + pos: 75.5,-25.5 + parent: 2 + - uid: 5101 + components: + - type: Transform + pos: 74.5,-42.5 + parent: 2 + - uid: 5426 + components: + - type: Transform + pos: 79.5,-30.5 + parent: 2 + - uid: 5460 + components: + - type: Transform + pos: 75.5,-23.5 + parent: 2 + - uid: 6244 + components: + - type: Transform + pos: 75.5,-24.5 + parent: 2 - uid: 6711 components: - type: Transform @@ -90493,43 +91145,23 @@ entities: - type: Transform pos: 56.5,-48.5 parent: 2 -- proto: PuddleBloodSmall - entities: - - uid: 1797 + - uid: 19552 components: - type: Transform - pos: 75.5,-23.5 + pos: 74.5,-41.5 parent: 2 +- proto: PuddleBloodSmall + entities: - uid: 7101 components: - type: Transform pos: 74.5,-46.5 parent: 2 - - uid: 7264 - components: - - type: Transform - pos: 75.5,-25.5 - parent: 2 - uid: 8141 components: - type: Transform pos: 77.5,-55.5 parent: 2 - - uid: 13942 - components: - - type: Transform - pos: 74.5,-43.5 - parent: 2 - - uid: 16052 - components: - - type: Transform - pos: 74.5,-42.5 - parent: 2 - - uid: 16098 - components: - - type: Transform - pos: 74.5,-41.5 - parent: 2 - uid: 16810 components: - type: Transform @@ -90660,11 +91292,6 @@ entities: - type: Transform pos: 24.5,-27.5 parent: 2 - - uid: 17968 - components: - - type: Transform - pos: 75.5,-24.5 - parent: 2 - uid: 18023 components: - type: Transform @@ -90690,6 +91317,21 @@ entities: - type: Transform pos: 54.5,-50.5 parent: 2 + - uid: 19553 + components: + - type: Transform + pos: 76.5,-41.5 + parent: 2 + - uid: 19554 + components: + - type: Transform + pos: 76.5,-27.5 + parent: 2 + - uid: 19555 + components: + - type: Transform + pos: 74.5,-23.5 + parent: 2 - proto: PuddleFluorosulfuricAcid entities: - uid: 7934 @@ -91498,12 +92140,6 @@ entities: rot: 1.5707963267948966 rad pos: -14.5,-17.5 parent: 2 - - uid: 2501 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 17.5,-83.5 - parent: 2 - uid: 13891 components: - type: Transform @@ -91528,11 +92164,6 @@ entities: - type: Transform pos: 10.5,-40.5 parent: 2 - - uid: 14139 - components: - - type: Transform - pos: -19.5,-32.5 - parent: 2 - uid: 18376 components: - type: Transform @@ -91715,6 +92346,16 @@ entities: - type: Transform pos: -2.5,-45.5 parent: 2 + - uid: 19588 + components: + - type: Transform + pos: 4.5,-78.5 + parent: 2 + - uid: 19589 + components: + - type: Transform + pos: -0.5,-75.5 + parent: 2 - proto: RandomInstruments entities: - uid: 2249 @@ -91813,11 +92454,6 @@ entities: - type: Transform pos: 48.5,-16.5 parent: 2 - - uid: 19044 - components: - - type: Transform - pos: 9.5,-46.5 - parent: 2 - uid: 19212 components: - type: Transform @@ -91833,6 +92469,11 @@ entities: - type: Transform pos: 17.5,-14.5 parent: 2 + - uid: 19622 + components: + - type: Transform + pos: 10.5,-46.5 + parent: 2 - proto: RandomProduce entities: - uid: 3426 @@ -92162,11 +92803,6 @@ entities: - type: Transform pos: -23.5,-32.5 parent: 2 - - uid: 185 - components: - - type: Transform - pos: -23.5,-29.5 - parent: 2 - uid: 191 components: - type: Transform @@ -92212,26 +92848,6 @@ entities: - type: Transform pos: -20.5,-29.5 parent: 2 - - uid: 562 - components: - - type: Transform - pos: 14.5,-40.5 - parent: 2 - - uid: 563 - components: - - type: Transform - pos: 16.5,-39.5 - parent: 2 - - uid: 568 - components: - - type: Transform - pos: 15.5,-39.5 - parent: 2 - - uid: 581 - components: - - type: Transform - pos: 17.5,-39.5 - parent: 2 - uid: 804 components: - type: Transform @@ -92272,71 +92888,6 @@ entities: - type: Transform pos: -17.5,-31.5 parent: 2 - - uid: 1645 - components: - - type: Transform - pos: 18.5,-45.5 - parent: 2 - - uid: 1646 - components: - - type: Transform - pos: 20.5,-41.5 - parent: 2 - - uid: 1647 - components: - - type: Transform - pos: 20.5,-44.5 - parent: 2 - - uid: 1648 - components: - - type: Transform - pos: 17.5,-45.5 - parent: 2 - - uid: 1667 - components: - - type: Transform - pos: 19.5,-45.5 - parent: 2 - - uid: 1668 - components: - - type: Transform - pos: 20.5,-40.5 - parent: 2 - - uid: 1688 - components: - - type: Transform - pos: 14.5,-41.5 - parent: 2 - - uid: 1692 - components: - - type: Transform - pos: 14.5,-44.5 - parent: 2 - - uid: 1696 - components: - - type: Transform - pos: 14.5,-43.5 - parent: 2 - - uid: 1731 - components: - - type: Transform - pos: 19.5,-39.5 - parent: 2 - - uid: 1798 - components: - - type: Transform - pos: 20.5,-43.5 - parent: 2 - - uid: 1799 - components: - - type: Transform - pos: 15.5,-45.5 - parent: 2 - - uid: 1802 - components: - - type: Transform - pos: 18.5,-39.5 - parent: 2 - uid: 2617 components: - type: Transform @@ -92452,22 +93003,31 @@ entities: - type: Transform pos: -17.5,-30.5 parent: 2 + - uid: 6248 + components: + - type: Transform + pos: 17.5,-39.5 + parent: 2 - uid: 6644 components: - type: Transform pos: 8.5,-51.5 parent: 2 - - uid: 6916 + - uid: 6902 components: - type: Transform - rot: 3.141592653589793 rad - pos: 20.5,-42.5 + pos: 19.5,-39.5 parent: 2 - uid: 7066 components: - type: Transform pos: 6.5,-48.5 parent: 2 + - uid: 7099 + components: + - type: Transform + pos: 20.5,-40.5 + parent: 2 - uid: 7444 components: - type: Transform @@ -92528,26 +93088,91 @@ entities: - type: Transform pos: -25.5,-72.5 parent: 2 - - uid: 11402 + - uid: 11403 components: - type: Transform - pos: 16.5,-45.5 + pos: 16.5,-39.5 + parent: 2 + - uid: 13942 + components: + - type: Transform + pos: 15.5,-39.5 + parent: 2 + - uid: 14024 + components: + - type: Transform + pos: 20.5,-41.5 + parent: 2 + - uid: 14053 + components: + - type: Transform + pos: 14.5,-44.5 + parent: 2 + - uid: 14241 + components: + - type: Transform + pos: 18.5,-39.5 + parent: 2 + - uid: 14435 + components: + - type: Transform + pos: 14.5,-41.5 + parent: 2 + - uid: 14436 + components: + - type: Transform + pos: 20.5,-44.5 + parent: 2 + - uid: 14437 + components: + - type: Transform + pos: 18.5,-45.5 + parent: 2 + - uid: 16052 + components: + - type: Transform + pos: 20.5,-43.5 + parent: 2 + - uid: 16097 + components: + - type: Transform + pos: 17.5,-45.5 + parent: 2 + - uid: 16170 + components: + - type: Transform + pos: 14.5,-40.5 parent: 2 - uid: 16466 components: - type: Transform pos: -17.5,-33.5 parent: 2 + - uid: 16521 + components: + - type: Transform + pos: 15.5,-45.5 + parent: 2 - uid: 16620 components: - type: Transform pos: 27.5,-65.5 parent: 2 + - uid: 16875 + components: + - type: Transform + pos: 20.5,-42.5 + parent: 2 - uid: 17373 components: - type: Transform pos: -17.5,-32.5 parent: 2 + - uid: 17968 + components: + - type: Transform + pos: 14.5,-43.5 + parent: 2 - uid: 18152 components: - type: Transform @@ -92558,6 +93183,11 @@ entities: - type: Transform pos: 44.5,-62.5 parent: 2 + - uid: 18331 + components: + - type: Transform + pos: 62.5,-21.5 + parent: 2 - uid: 18367 components: - type: Transform @@ -92583,8 +93213,24 @@ entities: - type: Transform pos: 36.5,-62.5 parent: 2 + - uid: 19070 + components: + - type: Transform + pos: 19.5,-45.5 + parent: 2 + - uid: 19569 + components: + - type: Transform + pos: 16.5,-45.5 + parent: 2 - proto: ReinforcedPlasmaWindowDiagonal entities: + - uid: 1731 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 20.5,-45.5 + parent: 2 - uid: 4654 components: - type: Transform @@ -92654,52 +93300,100 @@ entities: - type: Transform pos: 23.5,-51.5 parent: 2 - - uid: 10446 + - uid: 6916 components: - type: Transform rot: -1.5707963267948966 rad pos: 20.5,-39.5 parent: 2 - - uid: 10447 + - uid: 14034 components: - type: Transform pos: 14.5,-39.5 parent: 2 - - uid: 13978 - components: - - type: Transform - pos: 19.5,-44.5 - parent: 2 - - uid: 14025 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 15.5,-40.5 - parent: 2 - - uid: 14047 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 19.5,-40.5 - parent: 2 - - uid: 14056 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 15.5,-44.5 - parent: 2 - - uid: 14432 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 20.5,-45.5 - parent: 2 - - uid: 14433 + - uid: 14035 components: - type: Transform rot: 1.5707963267948966 rad pos: 14.5,-45.5 parent: 2 +- proto: ReinforcedUraniumWindow + entities: + - uid: 7264 + components: + - type: Transform + pos: 19.5,-41.5 + parent: 2 + - uid: 8016 + components: + - type: Transform + pos: 16.5,-40.5 + parent: 2 + - uid: 10446 + components: + - type: Transform + pos: 19.5,-40.5 + parent: 2 + - uid: 10447 + components: + - type: Transform + pos: 19.5,-43.5 + parent: 2 + - uid: 10774 + components: + - type: Transform + pos: 19.5,-42.5 + parent: 2 + - uid: 11402 + components: + - type: Transform + pos: 15.5,-40.5 + parent: 2 + - uid: 13274 + components: + - type: Transform + pos: 17.5,-44.5 + parent: 2 + - uid: 13452 + components: + - type: Transform + pos: 16.5,-44.5 + parent: 2 + - uid: 13978 + components: + - type: Transform + pos: 18.5,-40.5 + parent: 2 + - uid: 14005 + components: + - type: Transform + pos: 18.5,-44.5 + parent: 2 + - uid: 14025 + components: + - type: Transform + pos: 17.5,-40.5 + parent: 2 + - uid: 14029 + components: + - type: Transform + pos: 19.5,-44.5 + parent: 2 + - uid: 14047 + components: + - type: Transform + pos: 15.5,-41.5 + parent: 2 + - uid: 15698 + components: + - type: Transform + pos: 15.5,-44.5 + parent: 2 + - uid: 17173 + components: + - type: Transform + pos: 15.5,-43.5 + parent: 2 - proto: ReinforcedWindow entities: - uid: 79 @@ -93215,7 +93909,7 @@ entities: - uid: 6095 components: - type: Transform - pos: 60.5,-21.5 + pos: 64.5,-21.5 parent: 2 - uid: 6857 components: @@ -93267,11 +93961,6 @@ entities: - type: Transform pos: 61.5,-45.5 parent: 2 - - uid: 7206 - components: - - type: Transform - pos: 64.5,-21.5 - parent: 2 - uid: 7278 components: - type: Transform @@ -93362,11 +94051,6 @@ entities: - type: Transform pos: 61.5,-18.5 parent: 2 - - uid: 15470 - components: - - type: Transform - pos: 59.5,-40.5 - parent: 2 - uid: 16349 components: - type: Transform @@ -93855,6 +94539,13 @@ entities: - type: Transform pos: 51.5,-17.5 parent: 2 +- proto: SeedExtractorMachineCircuitboard + entities: + - uid: 19583 + components: + - type: Transform + pos: 4.635383,-79.48305 + parent: 2 - proto: ShardCrystalRandom entities: - uid: 15273 @@ -93877,7 +94568,7 @@ entities: - uid: 17021 components: - type: Transform - pos: 4.3700624,-79.34264 + pos: 4.3205686,-79.307 parent: 2 - uid: 19233 components: @@ -93957,11 +94648,11 @@ entities: - type: Transform pos: -12.6246805,-71.2753 parent: 2 - - uid: 14241 + - uid: 14433 components: - type: Transform rot: 3.141592653589793 rad - pos: -11.485548,-18.454052 + pos: -15.5,-18.5 parent: 2 - proto: SheetPlasma1 entities: @@ -94037,6 +94728,11 @@ entities: - type: Transform pos: 32.586887,-61.429493 parent: 2 + - uid: 19603 + components: + - type: Transform + pos: 45.393383,-73.1507 + parent: 2 - proto: SheetRGlass1 entities: - uid: 18778 @@ -94056,12 +94752,12 @@ entities: - uid: 1882 components: - type: Transform - pos: 46.418232,-73.38268 + pos: 46.415913,-73.32666 parent: 2 - uid: 3341 components: - type: Transform - pos: 46.615986,-73.28868 + pos: 46.594925,-73.20312 parent: 2 - uid: 6671 components: @@ -94091,12 +94787,12 @@ entities: - uid: 13337 components: - type: Transform - pos: -5.400783,-19.33013 + pos: -3.438326,-19.436022 parent: 2 - uid: 13646 components: - type: Transform - pos: 45.610485,-73.26664 + pos: 45.696777,-73.20621 parent: 2 - uid: 17911 components: @@ -95546,13 +96242,13 @@ entities: parent: 2 - type: DeviceLinkSource linkedPorts: - 5029: + 2501: - - Pressed - Toggle - 5030: + 1341: - - Pressed - Toggle - 5031: + 313: - - Pressed - Toggle - uid: 17829 @@ -95813,6 +96509,70 @@ entities: 18213: - - Pressed - Toggle + - uid: 19574 + components: + - type: Transform + pos: 13.5,-41.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 19575: + - - Pressed + - Toggle + 19564: + - - Pressed + - Toggle + 19560: + - - Pressed + - Toggle + 19559: + - - Pressed + - Toggle + 19558: + - - Pressed + - Toggle + 19557: + - - Pressed + - Toggle + 19556: + - - Pressed + - Toggle + 16098: + - - Pressed + - Toggle + 19563: + - - Pressed + - Toggle + 19561: + - - Pressed + - Toggle + 19572: + - - Pressed + - Toggle + 19562: + - - Pressed + - Toggle + 19571: + - - Pressed + - Toggle + 19566: + - - Pressed + - Toggle + 19567: + - - Pressed + - Toggle + 19568: + - - Pressed + - Toggle + 19570: + - - Pressed + - Toggle + 19565: + - - Pressed + - Toggle + 14056: + - - Pressed + - Toggle - proto: SignAnomaly entities: - uid: 16210 @@ -95963,6 +96723,11 @@ entities: rot: -1.5707963267948966 rad pos: 16.5,-70.5 parent: 2 + - uid: 14139 + components: + - type: Transform + pos: -0.5,-58.5 + parent: 2 - uid: 16179 components: - type: Transform @@ -95974,11 +96739,6 @@ entities: - type: Transform pos: 20.5,-25.5 parent: 2 - - uid: 18315 - components: - - type: Transform - pos: -0.5,-58.5 - parent: 2 - uid: 19281 components: - type: Transform @@ -95999,6 +96759,13 @@ entities: - type: Transform pos: 4.5,-2.5 parent: 2 +- proto: SignCryogenicsMed + entities: + - uid: 3832 + components: + - type: Transform + pos: -3.5,-50.5 + parent: 2 - proto: SignDirectionalAtmos entities: - uid: 9434 @@ -96007,18 +96774,18 @@ entities: rot: 3.141592653589793 rad pos: -15.498708,-51.71671 parent: 2 + - uid: 16151 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 5.5008106,-33.718235 + parent: 2 - uid: 17980 components: - type: Transform rot: 1.5707963267948966 rad pos: -11.500819,-41.285877 parent: 2 - - uid: 17981 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 5.5,-33.5 - parent: 2 - uid: 17982 components: - type: Transform @@ -96035,11 +96802,11 @@ entities: rot: 1.5707963267948966 rad pos: -23.5,-54.5 parent: 2 - - uid: 17987 + - uid: 19621 components: - type: Transform rot: 1.5707963267948966 rad - pos: 6.4997983,-55.716797 + pos: 12.5,-51.5 parent: 2 - proto: SignDirectionalBar entities: @@ -96075,12 +96842,6 @@ entities: rot: -1.5707963267948966 rad pos: 18.49973,-51.71622 parent: 2 - - uid: 16180 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 6.4990706,-55.28383 - parent: 2 - uid: 16185 components: - type: Transform @@ -96092,6 +96853,32 @@ entities: - type: Transform pos: -30.5,-18.5 parent: 2 + - uid: 19629 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 6.5,-51.5 + parent: 2 + - uid: 19631 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 5.5013075,-33.288094 + parent: 2 +- proto: SignDirectionalBrig + entities: + - uid: 19645 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 61.5,-40.5 + parent: 2 + - uid: 19648 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 61.5,-31.5 + parent: 2 - proto: SignDirectionalChapel entities: - uid: 12824 @@ -96124,16 +96911,16 @@ entities: - type: Transform pos: 22.5,-59.5 parent: 2 + - uid: 16127 + components: + - type: Transform + pos: 6.5,-55.5 + parent: 2 - uid: 16134 components: - type: Transform pos: 2.498827,-33.284237 parent: 2 - - uid: 16151 - components: - - type: Transform - pos: 7.5,-51.5 - parent: 2 - proto: SignDirectionalCryo entities: - uid: 16159 @@ -96142,6 +96929,12 @@ entities: rot: 3.141592653589793 rad pos: 22.5,-47.5 parent: 2 + - uid: 16180 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 7.50015,-51.284824 + parent: 2 - uid: 17820 components: - type: Transform @@ -96186,6 +96979,12 @@ entities: rot: 1.5707963267948966 rad pos: -26.501791,-54.716873 parent: 2 + - uid: 14808 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 5.5,-33.5 + parent: 2 - uid: 15613 components: - type: Transform @@ -96218,11 +97017,16 @@ entities: rot: -1.5707963267948966 rad pos: 17.5,-80.5 parent: 2 - - uid: 17984 + - uid: 19632 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 5.5003753,-33.716583 + pos: -15.5013685,-35.71809 + parent: 2 + - uid: 19633 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 6.50085,-51.716766 parent: 2 - proto: SignDirectionalEvac entities: @@ -96238,36 +97042,18 @@ entities: rot: -1.5707963267948966 rad pos: 62.5,-22.5 parent: 2 - - uid: 4068 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 51.5,-25.5 - parent: 2 - uid: 4358 components: - type: Transform rot: 3.141592653589793 rad pos: 64.5,-54.5 parent: 2 - - uid: 4557 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-29.5 - parent: 2 - uid: 5213 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5015326,-8.71623 parent: 2 - - uid: 6847 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 6.5,-55.5 - parent: 2 - uid: 7888 components: - type: Transform @@ -96361,17 +97147,23 @@ entities: rot: 3.141592653589793 rad pos: -30.5,-15.5 parent: 2 + - uid: 16155 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 37.5,-32.5 + parent: 2 - uid: 16162 components: - type: Transform rot: -1.5707963267948966 rad pos: 18.49889,-51.283894 parent: 2 - - uid: 16163 + - uid: 16168 components: - type: Transform - rot: 3.141592653589793 rad - pos: 22.500296,-33.285934 + rot: -1.5707963267948966 rad + pos: 22.50092,-33.28325 parent: 2 - uid: 16174 components: @@ -96379,11 +97171,11 @@ entities: rot: 3.141592653589793 rad pos: 10.5,-64.5 parent: 2 - - uid: 16178 + - uid: 16177 components: - type: Transform rot: 3.141592653589793 rad - pos: 26.5,-75.5 + pos: 26.501543,-75.28514 parent: 2 - uid: 17221 components: @@ -96397,6 +97189,12 @@ entities: rot: -1.5707963267948966 rad pos: -15.499661,-41.28664 parent: 2 + - uid: 17707 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5012491,-29.285704 + parent: 2 - uid: 17809 components: - type: Transform @@ -96420,6 +97218,11 @@ entities: - type: Transform pos: -5.5,-4.5 parent: 2 + - uid: 18315 + components: + - type: Transform + pos: 43.5,-27.5 + parent: 2 - uid: 19406 components: - type: Transform @@ -96437,6 +97240,23 @@ entities: rot: -1.5707963267948966 rad pos: 7.5003424,-8.714865 parent: 2 + - uid: 19630 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 6.498548,-51.284073 + parent: 2 + - uid: 19637 + components: + - type: Transform + pos: -5.5,-0.5 + parent: 2 + - uid: 19642 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 57.5,-25.5 + parent: 2 - proto: SignDirectionalFood entities: - uid: 5177 @@ -96456,17 +97276,23 @@ entities: rot: 1.5707963267948966 rad pos: 1.4994224,-33.283478 parent: 2 - - uid: 16177 + - uid: 16176 components: - type: Transform rot: 3.141592653589793 rad - pos: 26.499176,-75.28583 + pos: 26.5,-75.5 parent: 2 - uid: 18204 components: - type: Transform pos: 22.498983,-37.717163 parent: 2 + - uid: 19636 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 12.499051,-51.286167 + parent: 2 - proto: SignDirectionalHop entities: - uid: 16132 @@ -96517,10 +97343,10 @@ entities: rot: -1.5707963267948966 rad pos: 3.500022,-33.28552 parent: 2 - - uid: 16168 + - uid: 17078 components: - type: Transform - pos: -1.5000485,-12.718935 + pos: -1.4999341,-15.283161 parent: 2 - proto: SignDirectionalLibrary entities: @@ -96529,10 +97355,10 @@ entities: - type: Transform pos: 3.4989862,-33.716446 parent: 2 - - uid: 16155 + - uid: 16163 components: - type: Transform - pos: 7.500051,-51.71732 + pos: 6.499085,-55.716106 parent: 2 - proto: SignDirectionalMed entities: @@ -96553,11 +97379,6 @@ entities: rot: 1.5707963267948966 rad pos: -26.499496,-33.717285 parent: 2 - - uid: 16127 - components: - - type: Transform - pos: -1.500161,-15.284051 - parent: 2 - uid: 16160 components: - type: Transform @@ -96570,17 +97391,34 @@ entities: rot: -1.5707963267948966 rad pos: 22.5,-33.5 parent: 2 + - uid: 16178 + components: + - type: Transform + pos: -1.4999341,-12.716049 + parent: 2 - uid: 17821 components: - type: Transform pos: 22.5,-27.5 parent: 2 + - uid: 17984 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 7.5,-51.5 + parent: 2 - uid: 18201 components: - type: Transform rot: 3.141592653589793 rad pos: 7.5010014,-69.072586 parent: 2 + - uid: 19639 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -22.499754,-10.717974 + parent: 2 - proto: SignDirectionalSalvage entities: - uid: 16209 @@ -96601,7 +97439,13 @@ entities: components: - type: Transform rot: 3.141592653589793 rad - pos: 1.5014317,-29.285109 + pos: 7.50015,-51.71722 + parent: 2 + - uid: 16156 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 1.5,-29.5 parent: 2 - uid: 16157 components: @@ -96657,20 +97501,32 @@ entities: - type: Transform pos: -1.5,-15.5 parent: 2 - - uid: 16176 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 26.499176,-75.71822 - parent: 2 - uid: 17813 components: - type: Transform rot: 1.5707963267948966 rad pos: -11.499488,-41.71687 parent: 2 + - uid: 17922 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 26.500002,-75.714455 + parent: 2 + - uid: 19635 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 12.499051,-51.718567 + parent: 2 - proto: SignDirectionalSolar entities: + - uid: 6847 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 29.5,-32.5 + parent: 2 - uid: 6964 components: - type: Transform @@ -96748,12 +97604,6 @@ entities: rot: 3.141592653589793 rad pos: 45.5,-30.5 parent: 2 - - uid: 17078 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 46.5,-24.5 - parent: 2 - uid: 17814 components: - type: Transform @@ -96793,6 +97643,12 @@ entities: rot: -1.5707963267948966 rad pos: -30.500843,-58.713875 parent: 2 + - uid: 19643 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 45.5,-24.5 + parent: 2 - proto: SignDirectionalSupply entities: - uid: 16112 @@ -96816,11 +97672,6 @@ entities: rot: 1.5707963267948966 rad pos: -26.501286,-54.50067 parent: 2 - - uid: 16156 - components: - - type: Transform - pos: 7.49974,-51.2822 - parent: 2 - uid: 17364 components: - type: Transform @@ -96844,6 +97695,11 @@ entities: - type: Transform pos: 12.5,-75.5 parent: 2 + - uid: 19634 + components: + - type: Transform + pos: 6.499085,-55.283707 + parent: 2 - proto: SignDisposalSpace entities: - uid: 15409 @@ -96956,6 +97812,19 @@ entities: - type: Transform pos: 3.5,-17.5 parent: 2 +- proto: SignExamroom + entities: + - uid: 5207 + components: + - type: Transform + pos: 11.5,-70.5 + parent: 2 + - uid: 19649 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 57.5,-30.5 + parent: 2 - proto: SignGravity entities: - uid: 16203 @@ -96980,16 +97849,16 @@ entities: parent: 2 - proto: SignHydro1 entities: + - uid: 17987 + components: + - type: Transform + pos: 38.5,-47.5 + parent: 2 - uid: 18313 components: - type: Transform pos: 14.5,-64.5 parent: 2 - - uid: 18331 - components: - - type: Transform - pos: 38.5,-47.5 - parent: 2 - proto: SignInterrogation entities: - uid: 6125 @@ -97008,7 +97877,7 @@ entities: parent: 2 - proto: SignKitchen entities: - - uid: 18316 + - uid: 18300 components: - type: Transform pos: 1.5,-51.5 @@ -97058,11 +97927,6 @@ entities: parent: 2 - proto: SignMedical entities: - - uid: 17922 - components: - - type: Transform - pos: 6.5,-33.5 - parent: 2 - uid: 17923 components: - type: Transform @@ -97073,6 +97937,11 @@ entities: - type: Transform pos: 18.5,-64.5 parent: 2 + - uid: 19638 + components: + - type: Transform + pos: 7.5,-29.5 + parent: 2 - proto: SignMorgue entities: - uid: 924 @@ -97316,6 +98185,18 @@ entities: rot: 3.141592653589793 rad pos: 41.5,-22.5 parent: 2 + - uid: 17981 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 60.5,-22.5 + parent: 2 + - uid: 18145 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-55.5 + parent: 2 - uid: 18361 components: - type: Transform @@ -97361,6 +98242,20 @@ entities: - type: Transform pos: 64.5,-26.5 parent: 2 +- proto: SignShipDock + entities: + - uid: 19640 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -30.5,-9.5 + parent: 2 + - uid: 19641 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -38.5,-9.5 + parent: 2 - proto: SignShock entities: - uid: 11726 @@ -97534,6 +98429,12 @@ entities: rot: 3.141592653589793 rad pos: -6.5,-26.5 parent: 2 + - uid: 19090 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 12.5,-25.5 + parent: 2 - proto: SmartFridge entities: - uid: 13948 @@ -98346,6 +99247,11 @@ entities: - type: Transform pos: 18.5,-66.5 parent: 2 + - uid: 19620 + components: + - type: Transform + pos: 19.5,-66.5 + parent: 2 - proto: SpawnPointChiefEngineer entities: - uid: 15316 @@ -98590,6 +99496,11 @@ entities: - type: Transform pos: -21.5,-25.5 parent: 2 + - uid: 19618 + components: + - type: Transform + pos: -12.5,-26.5 + parent: 2 - proto: SpawnPointSecurityCadet entities: - uid: 19059 @@ -98673,6 +99584,11 @@ entities: - type: Transform pos: -14.5,-59.5 parent: 2 + - uid: 19619 + components: + - type: Transform + pos: -15.5,-60.5 + parent: 2 - proto: SpawnPointTechnicalAssistant entities: - uid: 15320 @@ -100143,6 +101059,28 @@ entities: - SurveillanceCameraEngineering nameSet: True id: Singularity Chamber, South + - uid: 19607 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 53.5,-65.5 + parent: 2 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraEngineering + nameSet: True + id: Burn Chamber Entrance + - uid: 19609 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,-70.5 + parent: 2 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraEngineering + nameSet: True + id: Singularity Chamber, Entrance - proto: SurveillanceCameraGeneral entities: - uid: 3912 @@ -100521,6 +101459,17 @@ entities: - SurveillanceCameraGeneral nameSet: True id: Engineering Hallway + - uid: 19608 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 7.5,-55.5 + parent: 2 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraGeneral + nameSet: True + id: HoP Hallway - proto: SurveillanceCameraMedical entities: - uid: 17592 @@ -100618,6 +101567,27 @@ entities: - SurveillanceCameraMedical nameSet: True id: Medical Outpost + - uid: 19022 + components: + - type: Transform + pos: 31.5,-29.5 + parent: 2 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraMedical + nameSet: True + id: Morgue + - uid: 19606 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 21.5,-30.5 + parent: 2 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraMedical + nameSet: True + id: Medical Bay - proto: SurveillanceCameraRouterCommand entities: - uid: 15917 @@ -100951,16 +101921,6 @@ entities: - SurveillanceCameraService nameSet: True id: Freezer - - uid: 17476 - components: - - type: Transform - pos: -55.5,-63.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraService - nameSet: True - id: Clown & Mime Rooms - uid: 17596 components: - type: Transform @@ -101057,19 +102017,19 @@ entities: - SurveillanceCameraService nameSet: True id: Chaplain's Room -- proto: SurveillanceCameraSupply - entities: - - uid: 3832 + - uid: 19605 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 17.5,-98.5 + rot: 3.141592653589793 rad + pos: -58.5,-61.5 parent: 2 - type: SurveillanceCamera setupAvailableNetworks: - - SurveillanceCameraSupply + - SurveillanceCameraService nameSet: True - id: Cargo Dock, West + id: Clown & Mime Rooms +- proto: SurveillanceCameraSupply + entities: - uid: 4688 components: - type: Transform @@ -101156,6 +102116,17 @@ entities: - SurveillanceCameraSupply nameSet: True id: Cargo Employee Entrance + - uid: 19023 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 17.5,-92.5 + parent: 2 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraSupply + nameSet: True + id: Cargo Delivery Conveyors - uid: 19092 components: - type: Transform @@ -101217,6 +102188,11 @@ entities: - type: Transform pos: 83.72667,-72.569664 parent: 2 + - uid: 19675 + components: + - type: Transform + pos: -42.356354,-52.532303 + parent: 2 - proto: Syringe entities: - uid: 5220 @@ -101276,6 +102252,12 @@ entities: - type: Transform pos: 57.5,-28.5 parent: 2 + - uid: 1802 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-19.5 + parent: 2 - uid: 2533 components: - type: Transform @@ -101406,11 +102388,6 @@ entities: - type: Transform pos: 32.5,-41.5 parent: 2 - - uid: 5083 - components: - - type: Transform - pos: -5.5,-19.5 - parent: 2 - uid: 5116 components: - type: Transform @@ -101902,11 +102879,6 @@ entities: - type: Transform pos: 11.5,-77.5 parent: 2 - - uid: 19367 - components: - - type: Transform - pos: -19.5,-49.5 - parent: 2 - proto: TableCarpet entities: - uid: 4876 @@ -102601,12 +103573,6 @@ entities: - type: Transform pos: -56.5,-28.5 parent: 2 - - uid: 7456 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 15.5,-58.5 - parent: 2 - uid: 7658 components: - type: Transform @@ -102717,6 +103683,12 @@ entities: - type: Transform pos: -56.5,-29.5 parent: 2 + - uid: 17476 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 59.5,-40.5 + parent: 2 - uid: 17662 components: - type: Transform @@ -102907,6 +103879,11 @@ entities: - type: Transform pos: -21.5,-46.5 parent: 2 + - uid: 19674 + components: + - type: Transform + pos: -42.5,-52.5 + parent: 2 - proto: TableXeno entities: - uid: 486 @@ -103607,7 +104584,7 @@ entities: - uid: 19370 components: - type: Transform - pos: -19.419056,-49.5568 + pos: -22.592426,-51.68456 parent: 2 - proto: ToolboxElectricalFilled entities: @@ -103663,7 +104640,7 @@ entities: - uid: 5738 components: - type: Transform - pos: -19.65343,-49.29099 + pos: -22.350601,-49.183464 parent: 2 - uid: 17023 components: @@ -103697,12 +104674,12 @@ entities: - uid: 5642 components: - type: Transform - pos: -22.663105,-51.29238 + pos: -22.536871,-51.17958 parent: 2 - uid: 5643 components: - type: Transform - pos: -22.350605,-51.495647 + pos: -22.379463,-51.43902 parent: 2 - uid: 5912 components: @@ -103781,7 +104758,7 @@ entities: - uid: 19391 components: - type: Transform - pos: -21.504704,-26.468311 + pos: -18.517744,-31.41124 parent: 2 - proto: ToyFigurineQueen entities: @@ -104213,92 +105190,6 @@ entities: - type: Transform pos: 9.660529,-25.157125 parent: 2 -- proto: UraniumWindow - entities: - - uid: 6902 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 19.5,-42.5 - parent: 2 - - uid: 11403 - components: - - type: Transform - pos: 16.5,-40.5 - parent: 2 - - uid: 13274 - components: - - type: Transform - pos: 15.5,-41.5 - parent: 2 - - uid: 14005 - components: - - type: Transform - pos: 15.5,-43.5 - parent: 2 - - uid: 14024 - components: - - type: Transform - pos: 17.5,-40.5 - parent: 2 - - uid: 14029 - components: - - type: Transform - pos: 16.5,-44.5 - parent: 2 - - uid: 14034 - components: - - type: Transform - pos: 17.5,-44.5 - parent: 2 - - uid: 14035 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 19.5,-41.5 - parent: 2 - - uid: 14053 - components: - - type: Transform - pos: 18.5,-44.5 - parent: 2 - - uid: 16875 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 19.5,-43.5 - parent: 2 - - uid: 17173 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 18.5,-40.5 - parent: 2 -- proto: UraniumWindowDiagonal - entities: - - uid: 14429 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 15.5,-44.5 - parent: 2 - - uid: 14435 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 19.5,-44.5 - parent: 2 - - uid: 14436 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 19.5,-40.5 - parent: 2 - - uid: 14437 - components: - - type: Transform - pos: 15.5,-40.5 - parent: 2 - proto: Vaccinator entities: - uid: 7778 @@ -104710,6 +105601,11 @@ entities: - type: Transform pos: -8.5,-28.5 parent: 2 + - uid: 17242 + components: + - type: Transform + pos: -20.5,-49.5 + parent: 2 - proto: VendingMachineViroDrobe entities: - uid: 7781 @@ -117606,11 +118502,6 @@ entities: - type: Transform pos: 4.5,-74.5 parent: 2 - - uid: 2407 - components: - - type: Transform - pos: 5.5,-72.5 - parent: 2 - uid: 2408 components: - type: Transform @@ -117886,11 +118777,6 @@ entities: - type: Transform pos: -43.5,-64.5 parent: 2 - - uid: 3127 - components: - - type: Transform - pos: -12.5,-55.5 - parent: 2 - uid: 3141 components: - type: Transform @@ -119752,6 +120638,11 @@ entities: - type: Transform pos: 65.5,-67.5 parent: 2 + - uid: 12408 + components: + - type: Transform + pos: 6.5,-72.5 + parent: 2 - uid: 12473 components: - type: Transform @@ -120925,6 +121816,11 @@ entities: rot: -1.5707963267948966 rad pos: 62.5,-57.5 parent: 2 + - uid: 18908 + components: + - type: Transform + pos: 59.5,-40.5 + parent: 2 - proto: WindoorSecureAtmosphericsLocked entities: - uid: 8009 @@ -121148,11 +122044,17 @@ entities: rot: 1.5707963267948966 rad pos: 61.5,-42.5 parent: 2 - - uid: 13843 + - uid: 19594 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 62.5,-20.5 + rot: 3.141592653589793 rad + pos: 59.5,-40.5 + parent: 2 + - uid: 19647 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 61.5,-27.5 parent: 2 - proto: WindoorSecureServiceLocked entities: @@ -121372,12 +122274,6 @@ entities: rot: 3.141592653589793 rad pos: 61.5,-67.5 parent: 2 - - uid: 15123 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 62.5,-21.5 - parent: 2 - uid: 15384 components: - type: Transform @@ -121421,11 +122317,6 @@ entities: - type: Transform pos: 58.5,-56.5 parent: 2 - - uid: 18300 - components: - - type: Transform - pos: 58.5,-20.5 - parent: 2 - uid: 19483 components: - type: Transform @@ -121503,7 +122394,7 @@ entities: - uid: 18303 components: - type: Transform - pos: -15.501174,-18.454052 + pos: -13.546322,-21.423643 parent: 2 - uid: 18373 components: @@ -123337,15 +124228,10 @@ entities: - type: Transform pos: 49.5,-62.5 parent: 2 - - uid: 12850 - components: - - type: Transform - pos: 59.5,-22.5 - parent: 2 - uid: 12851 components: - type: Transform - pos: 58.5,-22.5 + pos: 59.5,-22.5 parent: 2 - uid: 13647 components: @@ -123434,6 +124320,11 @@ entities: - type: Transform pos: 63.5,-63.5 parent: 2 + - uid: 18416 + components: + - type: Transform + pos: 58.5,-22.5 + parent: 2 - uid: 18467 components: - type: Transform @@ -123470,6 +124361,11 @@ entities: - type: Transform pos: -16.5,-70.5 parent: 2 + - uid: 19044 + components: + - type: Transform + pos: 60.5,-21.5 + parent: 2 - uid: 19193 components: - type: Transform diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index fd298463d3..8c3b0521be 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -458,8 +458,8 @@ # Mob mapped to Xeno Station that can not be ghostrole / pry - type: entity - name: lone praetorian - description: The last of its kind. + name: '"Dale"' + description: A praetorian left over from the station's initial security sweep. Has a pair of bloodied dog tags engraved with the name "Pvt. Dale" stuck in its maw. parent: SimpleSpaceMobBase id: MobXenoLonePraetorianNoGhost components: diff --git a/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml b/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml index 7df0ba8c63..132d87a969 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml @@ -643,6 +643,9 @@ - type: FootstepModifier footstepSoundCollection: collection: FootstepFloor + - type: Construction + graph: Table + node: TableXeno # Fancy tables diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml index ae1490c951..7bcb9fc3c6 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml @@ -117,7 +117,7 @@ - Cryogenics - type: entity - parent: BaseStructure + parent: BaseMachine id: CryoPodDestroyed name: destroyed cryo pod description: A cryo pod that has seen better days. It's entirely inoperable; not good for anything but scrap. @@ -128,22 +128,17 @@ noRot: true offset: 0, 0.5 state: pod-cracked - - type: Transform - noRot: true - type: Fixtures fixtures: fix1: shape: !type:PhysShapeAabb - bounds: "-0.5,-0.5,0.5,0.90" + bounds: "-0.5,-0.5,0.5,0.5" density: 200 mask: - MachineMask layer: - MachineLayer - - type: Damageable - damageContainer: StructuralInorganic - damageModifierSet: StructuralMetallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Maps/exo.yml b/Resources/Prototypes/Maps/exo.yml index df36be5370..5026d68c44 100644 --- a/Resources/Prototypes/Maps/exo.yml +++ b/Resources/Prototypes/Maps/exo.yml @@ -2,8 +2,8 @@ id: Exo mapName: 'Exo' mapPath: /Maps/exo.yml - minPlayers: 30 - maxPlayers: 70 + minPlayers: 40 + maxPlayers: 80 stations: Exo: stationProto: StandardNanotrasenStation @@ -25,7 +25,7 @@ Bartender: [ 2, 2 ] Botanist: [ 2, 2 ] Chef: [ 2, 2 ] - Janitor: [ 1, 2 ] + Janitor: [ 2, 2 ] Chaplain: [ 1, 1 ] Librarian: [ 1, 1 ] ServiceWorker: [ 2, 2 ] @@ -54,7 +54,7 @@ #supply Quartermaster: [ 1, 1 ] SalvageSpecialist: [ 3, 3 ] - CargoTechnician: [ 3, 3 ] + CargoTechnician: [ 4, 4 ] #civilian Passenger: [ -1, -1 ] Clown: [ 1, 1 ] diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/tables.yml b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/tables.yml index 4cee536bf9..37c94f0b39 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/tables.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/tables.yml @@ -404,3 +404,15 @@ steps: - tool: Prying doAfter: 1 + + - node: TableXeno + entity: TableXeno + edges: + - to: TableFrame + completed: + - !type:SpawnPrototype + prototype: SheetSteel1 + amount: 1 + steps: + - tool: Anchoring + doAfter: 1 From f6946f1db7c833baf71dda4b3e8402dff697f0d0 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 3 Jul 2025 18:52:49 +0000 Subject: [PATCH 180/191] Automatic changelog update --- Resources/Changelog/Changelog.yml | 19 +++++++++---------- Resources/Changelog/Maps.yml | 11 +++++++++++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c39c60116c..cf05e28dbe 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- author: mjarduk - changes: - - message: Torches can now be refueled. - type: Tweak - - message: Burnt torches/glowsticks names' can now display if they're glued, cluwnified, - etc. - type: Fix - id: 8220 - time: '2025-04-18T01:59:41.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36209 - author: lzk228 changes: - message: Borgs, clowns and mimes now can choose a custom name in loadouts. @@ -3887,3 +3877,12 @@ id: 8732 time: '2025-07-03T16:00:35.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/36708 +- author: SlamBamActionman + changes: + - message: Xeno tables can now be dismantled. + type: Fix + - message: Broken cryo pods can now be unanchored, allowing them to be scrapped. + type: Fix + id: 8733 + time: '2025-07-03T18:51:40.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38689 diff --git a/Resources/Changelog/Maps.yml b/Resources/Changelog/Maps.yml index ea755b5405..b938873843 100644 --- a/Resources/Changelog/Maps.yml +++ b/Resources/Changelog/Maps.yml @@ -394,4 +394,15 @@ id: 48 time: '2025-07-02T03:14:37.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/37494 +- author: SlamBamActionman + changes: + - message: Exo station has had several minor changes and fixes, including a change + to the Nuke vault to allow it to be anchored in its center and its windows blocked + with blastdoors. + type: Fix + - message: Exo station's playercap has been increased from 30-70 to 40-80. + type: Tweak + id: 49 + time: '2025-07-03T18:51:40.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38689 Order: 1 From 32f0ba340a809b8fed2c2408d840d70a969b9083 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Fri, 4 Jul 2025 07:11:21 +1200 Subject: [PATCH 181/191] Fix TextLinkTag (#32203) --- Content.Client/Guidebook/Richtext/KeyBindTag.cs | 2 +- Content.Client/Guidebook/Richtext/ProtodataTag.cs | 2 +- Content.Client/Guidebook/Richtext/TextLinkTag.cs | 15 ++++++--------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Content.Client/Guidebook/Richtext/KeyBindTag.cs b/Content.Client/Guidebook/Richtext/KeyBindTag.cs index 478f1b9bf6..ab74ddf1a0 100644 --- a/Content.Client/Guidebook/Richtext/KeyBindTag.cs +++ b/Content.Client/Guidebook/Richtext/KeyBindTag.cs @@ -6,7 +6,7 @@ using Robust.Shared.Utility; namespace Content.Client.Guidebook.Richtext; [UsedImplicitly] -public sealed class KeyBindTag : IMarkupTag +public sealed class KeyBindTag : IMarkupTagHandler { [Dependency] private readonly IInputManager _inputManager = default!; diff --git a/Content.Client/Guidebook/Richtext/ProtodataTag.cs b/Content.Client/Guidebook/Richtext/ProtodataTag.cs index a725fd4e4b..2a6eca4e48 100644 --- a/Content.Client/Guidebook/Richtext/ProtodataTag.cs +++ b/Content.Client/Guidebook/Richtext/ProtodataTag.cs @@ -9,7 +9,7 @@ namespace Content.Client.Guidebook.RichText; /// In order to be accessed by this tag, the desired field/property must /// be tagged with . /// -public sealed class ProtodataTag : IMarkupTag +public sealed class ProtodataTag : IMarkupTagHandler { [Dependency] private readonly ILogManager _logMan = default!; [Dependency] private readonly IEntityManager _entMan = default!; diff --git a/Content.Client/Guidebook/Richtext/TextLinkTag.cs b/Content.Client/Guidebook/Richtext/TextLinkTag.cs index 27aaa71939..a551b18473 100644 --- a/Content.Client/Guidebook/Richtext/TextLinkTag.cs +++ b/Content.Client/Guidebook/Richtext/TextLinkTag.cs @@ -10,16 +10,14 @@ using Content.Client.UserInterface.ControlExtensions; namespace Content.Client.Guidebook.RichText; [UsedImplicitly] -public sealed class TextLinkTag : IMarkupTag +public sealed class TextLinkTag : IMarkupTagHandler { public static Color LinkColor => Color.CornflowerBlue; public string Name => "textlink"; - public Control? Control; - /// - public bool TryGetControl(MarkupNode node, [NotNullWhen(true)] out Control? control) + public bool TryCreateControl(MarkupNode node, [NotNullWhen(true)] out Control? control) { if (!node.Value.TryGetString(out var text) || !node.Attributes.TryGetValue("link", out var linkParameter) @@ -38,22 +36,21 @@ public sealed class TextLinkTag : IMarkupTag label.OnMouseEntered += _ => label.FontColorOverride = Color.LightSkyBlue; label.OnMouseExited += _ => label.FontColorOverride = Color.CornflowerBlue; - label.OnKeyBindDown += args => OnKeybindDown(args, link); + label.OnKeyBindDown += args => OnKeybindDown(args, link, label); control = label; - Control = label; return true; } - private void OnKeybindDown(GUIBoundKeyEventArgs args, string link) + private void OnKeybindDown(GUIBoundKeyEventArgs args, string link, Control? control) { if (args.Function != EngineKeyFunctions.UIClick) return; - if (Control == null) + if (control == null) return; - if (Control.TryGetParentHandler(out var handler)) + if (control.TryGetParentHandler(out var handler)) handler.HandleClick(link); else Logger.Warning("Warning! No valid ILinkClickHandler found."); From 3540e2e44a697ba42db28dc8308a4c7efcbedd20 Mon Sep 17 00:00:00 2001 From: Hannah Giovanna Dawson Date: Sat, 5 Jul 2025 21:15:54 +0100 Subject: [PATCH 182/191] Scurret PDA slot fix (#38777) --- .../Prototypes/InventoryTemplates/scurret_inventory_template.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml b/Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml index 10b9ac770d..8433564ea6 100644 --- a/Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml +++ b/Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml @@ -47,7 +47,6 @@ stripTime: 6 uiWindowPos: 2,1 strippingWindowPos: 2,4 - dependsOn: jumpsuit displayName: ID - name: suitstorage slotTexture: suit_storage From 654a459351402f9193771ec164b206ceea718c89 Mon Sep 17 00:00:00 2001 From: Hannah Giovanna Dawson Date: Sun, 6 Jul 2025 14:08:56 +0100 Subject: [PATCH 183/191] Scurrets get emergency EVA suit access (#38778) * Scurrets - spacing protection via adorable outfits * ALPHABETIZATION!! --- .../Clothing/OuterClothing/hardsuits.yml | 1 + .../Clothing/OuterClothing/softsuits.yml | 1 + .../scurret_inventory_template.yml | 10 ++++++++++ Resources/Prototypes/tags.yml | 5 +++-- .../equipped-OUTERCLOTHING-scurret.png | Bin 0 -> 1619 bytes .../Hardsuits/syndicate.rsi/meta.json | 6 +++++- .../equipped-OUTERCLOTHING-scurret.png | Bin 0 -> 1402 bytes .../Suits/eva_emergency.rsi/meta.json | 6 +++++- 8 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 Resources/Textures/Clothing/OuterClothing/Hardsuits/syndicate.rsi/equipped-OUTERCLOTHING-scurret.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Suits/eva_emergency.rsi/equipped-OUTERCLOTHING-scurret.png diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml index 59ee0786ed..6904727d2b 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml @@ -579,6 +579,7 @@ - Hardsuit - WhitelistChameleon - CorgiWearable + - ScurretWearable - type: StaticPrice price: 5000 diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/softsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/softsuits.yml index 574bdc2640..d5612210c6 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/softsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/softsuits.yml @@ -62,6 +62,7 @@ - type: Tag tags: - CorgiWearable + - ScurretWearable - MonkeyWearable - WhitelistChameleon diff --git a/Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml b/Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml index 8433564ea6..28b95eb3d3 100644 --- a/Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml +++ b/Resources/Prototypes/InventoryTemplates/scurret_inventory_template.yml @@ -59,3 +59,13 @@ dependsOnComponents: - type: AllowSuitStorage displayName: Suit Storage + - name: outerClothing + slotTexture: suit + slotFlags: OUTERCLOTHING + stripTime: 6 + uiWindowPos: 0,3 + strippingWindowPos: 1,3 + displayName: Suit + whitelist: + tags: + - ScurretWearable diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index c10eb51486..cdcd6c8afc 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -225,7 +225,6 @@ - type: Tag id: CanPilot - - type: Tag id: CaptainSabre @@ -1203,6 +1202,9 @@ - type: Tag id: Screwdriver +- type: Tag # Flags items that scurrets can wear in their back and outerwear slots. + id: ScurretWearable + - type: Tag id: SecBeltEquip @@ -1514,5 +1516,4 @@ - type: Tag id: XenoborgModuleStealth - # ALPHABETICAL diff --git a/Resources/Textures/Clothing/OuterClothing/Hardsuits/syndicate.rsi/equipped-OUTERCLOTHING-scurret.png b/Resources/Textures/Clothing/OuterClothing/Hardsuits/syndicate.rsi/equipped-OUTERCLOTHING-scurret.png new file mode 100644 index 0000000000000000000000000000000000000000..8125ec9e1ecb62e24ccc1428feb6c1a33b530195 GIT binary patch literal 1619 zcmV-Z2CVssP)Px*4M{{nRCt{2noUd-SscKBIE9MkQ`>4`*{1D?&5EcAB^oyCM`TUZjV9=E6T>E6 zIgyxfP!68<(fdPB1$DFb!~7Q0!rMT0SFg8_bCSkOFP zuJV_XtNe9ZZ>W;pv?n4&gE4WmCn8kIZmmP*#DLvFrQHI6$K?`buJScG;LdkVihAyG zxx}&D_0{0-J=-$mt7Yuv^jp`n#h|70rS5AqRhl?wu;NGw1vqE0(moU7Qny#Dd`%38 zN5EhO5JtasT_c*OfJ$I&6myY@z}RT6LujVAunK@@!6+Sv4RqYSO~+vafw57#=I6w9 zEoh1WfX-*rU|$S9kIO}$-=Az>Wgb?PP?>VTsT&En@imYj094v7JOp^8ZA{G(5Z+OX zxulprzaMjvDOr{j6W&p)btuhjy4A}W{}4b%1OS}z53%W1ujVl|EgQ{}q=a|W5_>U) zxulrlLK9On)1qGS*8sq(Er-Vs>{Uobz*70@%JA#WKQ+nL909QxQ=&8%nKJ4Ctsa?X zNm}vMpjncX^a!|qqLhxix4C|z6xJdFnk7jY|E3;NzkvD-P^W?V3!L!};jFOnW1nxu zBH{Rfy^3nMI(9nhyTPVgy_#iyu(SGWwt8f)#0`8~?dJE#UOFU&FAI$P`+AO33wffw zekO#o!p4N9hD+VoKH3S*lB6_MnneAa!OAaINz_$2OUYL5dsl&GNm70|a)OP~L9WCN zgnV9F=H~#|;|nlgcd#cIpv&%{Yp9>vU;tz{ghMzrnfY${s_|tz8)+Hpr#%s2IK0@W zv(Jax$5~;6lvC)wrjUgr-sym5Nm5!pGFKjZfu!*Rb_Yh=W-Jy9_tqD%=g}kXZQDk5 zbv4mwlmP&r7i2fR@fGku+S<=!Z)RQK6>UOh+)I`0Cgl6@ilHPya?%jC3-kjy@=;-L^ z=;-L^=;-L^=;-L^{2$3FKOnuHzE}TX*thEW0RVMQS?Ew33}jr>`Y*JmjgdO1tPI#4 zRLO3dDox4qWTTcLR+OfbjTyQO*c}~k?8|>uN>wg6P;Ik0C+h0oU_AYViQEwF;yM)pGdW%~A1^;{td}o5f1O$RHT3v{{nbTEC5q5lihD zuN7)mNGPGuX4?$Fg=mC=kwMyQHC(v-6fVo)OgiCxNtD6?OW*KXJ&SYc`^W+px9rrtBs3R8!bcqi+KcA8Txz~ z_Nfk8N7`1nYILhdX2Md#gr$bz@CfaR2rWbXO!oI=C{nhwk<+j8s0{{qIXk;1#sj>Z zouxJy;Pk6J%64Whhm-w1X?X-A!j5#?($jeIRO2TsHNwEoXVau-W%L$S(KSCOysXxb z<{RmnpA*J=!%x@bTfklMg#u8te!bA)Xugs9nGmG1NYd-e*`%i07yz!s4G{N&NIvi` zn{>Fe?S5eaZDpl2Og!Pp!UpP`va;GAMC!YKxAclT8?F2_Hv_3>kkD^w9p17@O{U0e zXlPL4@i^t>OVM@Q!~=0Di(>fZI3 Rm^uIe002ovPDHLkV1nP=DXahh literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Hardsuits/syndicate.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Hardsuits/syndicate.rsi/meta.json index 9e60a70b9e..78875bc273 100644 --- a/Resources/Textures/Clothing/OuterClothing/Hardsuits/syndicate.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Hardsuits/syndicate.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/fb2d71495bfe81446159ef528534193d09dd8d34, equipped-OUTERCLOTHING-monkey made by Dutch-VanDerLinde, vox state made by Flareguy for SS14, equipped-OUTERCLOTHING-dog modified from equipped-OUTERCLOTHING by casiliuscestus (GitHub)", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/fb2d71495bfe81446159ef528534193d09dd8d34, equipped-OUTERCLOTHING-monkey made by Dutch-VanDerLinde, vox state made by Flareguy for SS14, equipped-OUTERCLOTHING-dog modified from equipped-OUTERCLOTHING by casiliuscestus (GitHub), equipped-OUTERCLOTHING-scurret modified from equipped-OUTERCLOTHING by FairlySadPanda (Github)", "size": { "x": 32, "y": 32 @@ -26,6 +26,10 @@ "name": "equipped-OUTERCLOTHING-dog", "directions": 4 }, + { + "name": "equipped-OUTERCLOTHING-scurret", + "directions": 4 + }, { "name": "inhand-left", "directions": 4 diff --git a/Resources/Textures/Clothing/OuterClothing/Suits/eva_emergency.rsi/equipped-OUTERCLOTHING-scurret.png b/Resources/Textures/Clothing/OuterClothing/Suits/eva_emergency.rsi/equipped-OUTERCLOTHING-scurret.png new file mode 100644 index 0000000000000000000000000000000000000000..ecf9ff02c1633e19d53c35e49ec579b315cc319b GIT binary patch literal 1402 zcmV-=1%>*FP)Px)G)Y83RCt{2nq6pAM-<0@yESbgf*(yyTcS2elNfvv_aX%oO0_6SB-jSY2Usk^ zBBjNLJP6Y0()h9#--?6?tu&BSQY*@~fl5=vtdww*NPzvkHb^!aOJ^8LvY z4u{Q`#(H!7r$S+RdwZq*OMl=>3ubW<#HJnFo+`MXf!EG2uK*(>BPPJJAJ^sh|LeUG z0P5=Mq+*b2thZsVSq0^#V9YvptDe3K+ULtB!ra`P2|y&6~ z;11xz><|E-Bt?1t)n)*OcQ@PZk2O6x5h5YXV@*zd%_>pu*WmS+odsEJ8ov8keg-Zn zKz0D*Zwvxpv%n3$k4trLw#0gcME?(2EH7*}sVagm6jxX7vB z&p2hzE5dL6X8U7iKJO-vY_O1ulYe^awX1V&mK~nl%aPyPsSqz`3WY+UP$(1%g+ifF zC=`l+8`4`w;c(dAo^t2u`}D$XK}ABCZojEe*uExvMnF1Xy$y3~wUZIXwO4rV$HEBE zS}FDjt-VJpTRonXPtQxI$1VDFu`gd%0yDphJ^4aWyV~o!PZ!(cevnR$U99RN*)hes zgQsm+O?fHyp@l-Swfgb0!WPoEKFdDtce!#3+H2+ek(vQrU0vqDB$Vy#w9f-nWJGrM z>@I)VSwEcJC45$7;`W(^vb~)QOo|v~`jQeL+275bjXOLxceb`rSiZ6|!gn_AAUQuf z^CfQqtVhq8s$o*8lzmXbO-)VBvYZWA%7CKOLzT`t%zQ5#2%e&7BX_{v0y1wL^ z;E@98ZH|hEZaVgvd){^z7@`%8x4s%;;9iZ?VEpjwty{eFTr)8=hIco62Av(7dkl|S zAgx%aF?b(sr!qR7XngR3?r~JM()%b%BofQM5= Date: Mon, 7 Jul 2025 00:01:55 +0200 Subject: [PATCH 184/191] Revert "HoP's beret (#38601)" This reverts commit e9c90fe66dec0ed9579847807d9babe934a66bae. Please check the maintainer meeting for details --- .../Catalog/Fills/Lockers/dressers.yml | 1 - .../Entities/Clothing/Head/hats.yml | 17 ---------- .../Jobs/Command/head_of_personnel.yml | 5 --- .../Prototypes/Loadouts/loadout_groups.yml | 1 - .../Recipes/Lathes/Packs/clothing.yml | 1 - .../Prototypes/Recipes/Lathes/clothing.yml | 5 --- .../beret_hop.rsi/equipped-HELMET-hamster.png | Bin 861 -> 0 bytes .../Hats/beret_hop.rsi/equipped-HELMET.png | Bin 902 -> 0 bytes .../Clothing/Head/Hats/beret_hop.rsi/icon.png | Bin 415 -> 0 bytes .../Head/Hats/beret_hop.rsi/inhand-left.png | Bin 695 -> 0 bytes .../Head/Hats/beret_hop.rsi/inhand-right.png | Bin 789 -> 0 bytes .../Head/Hats/beret_hop.rsi/meta.json | 30 ------------------ 12 files changed, 60 deletions(-) delete mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET-hamster.png delete mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET.png delete mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/icon.png delete mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/inhand-left.png delete mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/inhand-right.png delete mode 100644 Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/meta.json diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml b/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml index 237b8a4748..e111b51cff 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml @@ -50,7 +50,6 @@ - type: StorageFill contents: - id: ClothingHeadHatHopcap - - id: ClothingHeadHatBeretHop - id: ClothingOuterWinterHoP - id: ClothingEyesGlasses - id: ClothingNeckCloakHop diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index 26e0c6aba2..ec7801802b 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -1360,20 +1360,3 @@ path: /Audio/Items/flashlight_on.ogg soundDeactivate: path: /Audio/Items/flashlight_off.ogg - -- type: entity - parent: ClothingHeadBase - id: ClothingHeadHatBeretHop - name: head of personnel's beret - description: A dark blue beret with a ruby inserted in the center, for true connoisseurs of bureaucracy! - components: - - type: Sprite - sprite: Clothing/Head/Hats/beret_hop.rsi - - type: Clothing - sprite: Clothing/Head/Hats/beret_hop.rsi - - type: Tag - tags: - - HamsterWearable - - ClothMade - - Recyclable - - WhitelistChameleon \ No newline at end of file diff --git a/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml b/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml index 0855100e61..45223bea14 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Command/head_of_personnel.yml @@ -35,11 +35,6 @@ equipment: head: ClothingHeadHatHopcap -- type: loadout - id: HoPBeret - equipment: - head: ClothingHeadHatBeretHop - # Neck - type: loadout id: HoPCloak diff --git a/Resources/Prototypes/Loadouts/loadout_groups.yml b/Resources/Prototypes/Loadouts/loadout_groups.yml index b3c8aa8129..39b6be2b04 100644 --- a/Resources/Prototypes/Loadouts/loadout_groups.yml +++ b/Resources/Prototypes/Loadouts/loadout_groups.yml @@ -153,7 +153,6 @@ minLimit: 0 loadouts: - HoPHead - - HoPBeret - type: loadoutGroup id: HoPJumpsuit diff --git a/Resources/Prototypes/Recipes/Lathes/Packs/clothing.yml b/Resources/Prototypes/Recipes/Lathes/Packs/clothing.yml index 808f1c680a..aa95781729 100644 --- a/Resources/Prototypes/Recipes/Lathes/Packs/clothing.yml +++ b/Resources/Prototypes/Recipes/Lathes/Packs/clothing.yml @@ -33,7 +33,6 @@ - ClothingUniformJumpskirtCapFormalDress # HoP - ClothingHeadHatHopcap - - ClothingHeadHatBeretHop - ClothingUniformJumpsuitHoP - ClothingUniformJumpskirtHoP # Generic diff --git a/Resources/Prototypes/Recipes/Lathes/clothing.yml b/Resources/Prototypes/Recipes/Lathes/clothing.yml index cf6e082b64..c5f81153e2 100644 --- a/Resources/Prototypes/Recipes/Lathes/clothing.yml +++ b/Resources/Prototypes/Recipes/Lathes/clothing.yml @@ -810,11 +810,6 @@ id: ClothingHeadHatHopcap result: ClothingHeadHatHopcap -- type: latheRecipe - parent: BaseCommandHatRecipe - id: ClothingHeadHatBeretHop - result: ClothingHeadHatBeretHop - - type: latheRecipe parent: BaseCommandHatRecipe id: ClothingHeadHatQMsoft diff --git a/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET-hamster.png b/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET-hamster.png deleted file mode 100644 index b55441dd0200112d64aeb49b73363a558b8b50b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 861 zcmV-j1ETziP)Px&7fD1xRCt{2n$K<$Q543%!wd|}3>0ZF!G&V$q8mY|?yQOFLl}J$;}f`ZZ{jnk z%P#6d7fnTI#EnL2bRovZwzTw*1H)VwQ%aEN^4SXLOGEVUE{ktu(RzhnJg+s!jlfU9K1d+Fgx!CX>I`Z+OfM8jijfX zw(Wf{y8{5KmNj^QQ#l~4^|qTEz`|20Evj_nA79$xx3|jP2 zvafUo6wM?-075vvL>?Pljrp1e|KdhGMWKj}@Wd4zAKvWA^zn2RI73JsJaB#ukcJHi zT0qdS0YM808a5zk0YM{wD=dLx>Yn@9jnlgk^269?z}OpbqA8$Xz6Kn;)#>bRLD;tE zF|_1l-?r6Q|9_{}J{$_c?zEt)Q3x&WIc^tU21L?nUk7^}_IkZImAbnEinrKH4$ zULs$)I4uxQVfNQI%K!XKh#GF2PdsNp)mD1-`R;!O%me`7ac#-34r%T+*YNQ6eHg{N z*eWgHPST{;4T!SU7PDxQh#TbC~{c3Va<1qkIuvDRuri@86< zoT1U47WOrm=P6K#X@u2kui;Y(IZdH_SQ%N_e5TbrwOGDRf_W`M?+Mb55U? n8HQmPhG7_nVHk#C&Le*S@*URI3?Lay00000NkvXXu0mjfITU{1 diff --git a/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/equipped-HELMET.png deleted file mode 100644 index 1b491f4c1e01f3517a915758c74236b902386ee6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 902 zcmV;119|+3P)Px&KuJVFRCt{2noV!iMi|F`W3Sg{Hx5Z?BsK@SNgyGrSaE0&cTR{4UxjNwK(BlV zdf?Xb0Z?x}utB^@gcaSC3TdkbX+g>6#f?Xfz?7fT*sInHC)psxKNJ0UcFU%(Qg;cmf(tXf#=h;!^RA1b84KT|9tH zV0(k#?%n36KYoF!*<%-BB^=Ym2jTw3!J0lAP1<4IIDVz*#N|dy5l`sB+c&WC6o8wT zuIX#NE&#d83kUb|;&b=di95vnqCQ7;+MqCav=nx7E&|Lppyjnp4e82u$3@t;8v_vMP z++exWR(hNY6A6m10JI1Sfx!y<&-fwV6p9@D$jHw4SKwR231B+`+c*JiCtw>Vfb9fq z;{>psfNh)rwiB?8(O%$VPG49g2!f$&{Xd@-wpWSyzd^hBeva>lNGYk+#*uQPw69VE zOrE>U=XUbr)AOa0=)ZVecy6tJaLZi{M@f=Ryb|lVumImpFYXVr%skQ|#G|K=#E;BD z`DQjJ{vF^1u$_QyoB*~Hu#FSIb^^9>0@zN#HckNB2^;~ZzQ(xJW@zcum?8tR;b!cW zz`SK7dw&_ne>6F_A9$xJOQptzbYL9cSb(K!th>Y`qX+Ii_~(_n63Aoin?0JG+i%ei z)?j`D*82dw>~+-wsZo3*0S+sk!J3JyJ#G%(8O^```d5CwaZ3+IMs?hHM}(5Wwp z$CD_74^+w&e;gRU1Q*gZA%xG?=4wTFQR;i1aWVgw;vEi$!{Kl^91e%W;cz${4u`|x caGWeY0FgZS$LjglR{#J207*qoM6N<$f*Mz(od5s; diff --git a/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/icon.png b/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/icon.png deleted file mode 100644 index 09a41f29640f6515cbfa07f62c7fcb6f7970545e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 415 zcmV;Q0bu@#P)Px$SxH1eR9J=Wlg~;5Q5431cQj|h3KGUr2t-A+g}W9$LEE09hv)%%h+d+$JwfE6 zMGFlbrIkWLfu)S7b6Z%b{h51>7D4y7y@&IC=R4mX62ypjRW*^yrgFS40{@d*5ZR!>V;ycu)V;xGM~IDiw^ z>Z_H2=OiWBGHkow3&Zl{zh$5u-G=6wG4_9a#5 zAE7^(#ky!>&Hi+7RoCTow+ujO`Yh^-w#t86C>_v z$omh%bCU9-XkpS9!Nf-f6GU{Kh$RSuDKZFU@G)vg?}GQ&xDDbuI%n(cVJu&!EpWMY znQw#H#jK$2Z5PZ^Wcs?5uRL_Gv-xK5@_UZov*$I>ZO;Ec_Z(q46nKD(v24xe{U2D9L^vNOtEBX-WUB~LC?9Vt84MDl$r#XTv&{FLbN9Y-BMzZsjXVBVz$7;;oF}Et^Lhu=l4xAU&Fj)LY>gA z$X|hKmrbmdro9eb`nqA0t3mL6bq9vDh)_@Y)4jGz0wMZLr^F5}IK))Keu0+qN9(PwRi&r&X{m#%ph!d(#bz2^43>Qyy8D@6?It?M(BGerw#3d_1Eh&xUNn|v6} zI-jSDKPY&0F1;u2ysf$B@$Dn)PctbmNU&zw?D_NT0r~rK3=V<5!%8Pv9?a; z=NC-S-S{NJY{*}L-pB}+k5!OoyRO#dIv W|6@FrYYH$WGI+ZBxvXPx%&q+ioGvoZ{_ZeHs48Sl9 z!!QiPFbu;m40FaH0GOPdMlv@F0-=x|l)*tac79Xc+K1D3&YeUW5@{1wbrXiB0px&0 zKOAchB9RFt$f~S{nlFTgrL5VO!_3|v0s!gMQCwVT#&!RoLV8eQXav~Z2CP~$UIgT| zjWrUrO4Ee6vXnI?k}1IG1Jc=Myzm{;Gr^DWJ`~gEB{F%p(MbO3!UNo2d;%m>c$2@_ z=4!7Ad-s=fLvHebn?vQ7douxGXat2+dv)so@LmkM$)~gKq;d%WaQEs>cT}TYv$tSG zSOJwE@4{VRf4@C8I`yxjGkFvNiN?|g{7b-dtX#qb_DBc=cD)Kn2@)ElW5dXgg%j8T zbK(wuetg#T{Ne2~2_nO`ZOf4of&|veaPRu%*_LJPp##9Uy+xnOB?$nNR3#_!VviKd z-?*^3Lb{&7>Sx!8D`e3JUw^X##_cUKlGy~%2LZsbTds5J8QJ+pLNkDr*%B9laR>iT zQalJ6aOPkGR0~iI8=zW%YS;kP0#w5Ws1~3aHbAui)vy7_9T@e00jfBb0eWsUY8}ep zC7@b>YS;kP0#u`S9&&PCKY!TLp1pl9im&MjJ+FXZ@<+wuLQf4SzIuFQUtCQ%^UnY_ zK(zqXbo~}6IJ*)FZ@LEH0GOKxUOZ~sf(GLICg^CuVLyUO0|V~vp|_*#XKr2wO^9pq zKo=G8HkCQ_078@Xub8*L08)pgpOy!5EUfT^NW@F`LsQC=TFbu;m%nA4lk`l^Y T=SO$+00000NkvXXu0mjfLLX%( diff --git a/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/meta.json b/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/meta.json deleted file mode 100644 index b5ca3514c5..0000000000 --- a/Resources/Textures/Clothing/Head/Hats/beret_hop.rsi/meta.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Sprited by upnostnote (Discord), resprite by 96flo (Discord)", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - }, - { - "name": "equipped-HELMET", - "directions": 4 - }, - { - "name": "equipped-HELMET-hamster", - "directions": 4 - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - } - ] -} \ No newline at end of file From 87b8f1c7df0cf73943b7e89b0defa8096308c86e Mon Sep 17 00:00:00 2001 From: Vasilis The Pikachu Date: Mon, 7 Jul 2025 00:02:16 +0200 Subject: [PATCH 185/191] Revert "Readds the Hypereutactic Blade for traitors, adds Hypereutatic blade for Nukies (#37182)" This reverts commit c03afeb29cf3ed5e751e8d206e79f9f24981484e. Please check the maintainer meeting for details --- .../Locale/en-US/store/uplink-catalog.ftl | 3 -- .../Prototypes/Catalog/uplink_catalog.yml | 46 ------------------- .../Objects/Weapons/Melee/e_sword.yml | 22 ++------- 3 files changed, 5 insertions(+), 66 deletions(-) diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 6aa07ce9da..3c7218b69c 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -23,9 +23,6 @@ uplink-esword-double-desc = A much more expensive counter part to the normal ene uplink-hypereutactic-blade-name = Hypereutactic Blade uplink-hypereutactic-blade-desc = A gigantic energy sword with power that matches its looks. Requires two hands. Slow and unwieldy, yet pretty adept at reflecting. Previously made infamous by an operative wearing a joy mask. You wouldn't want to see this coming at you down the hall! -uplink-hypereutatic-blade-name = Hypereutatic Blade -uplink-hypereutatic-blade-desc = A gigantic off-brand energy sword. Requires two hands. Slow and unwieldy, can reflect decently. Often mistaken for the Hypereutactic Blade. - uplink-edagger-name = Energy Dagger uplink-edagger-desc = A small energy blade conveniently disguised in the form of a pen. diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 653a17e5f8..2095edabff 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -66,52 +66,6 @@ categories: - UplinkWeaponry -- type: listing - id: UplinkHyperEutacticBlade - name: uplink-hypereutactic-blade-name - description: uplink-hypereutactic-blade-desc - icon: { sprite: /Textures/Objects/Weapons/Melee/hypereutactic_blade.rsi, state: icon } - discountCategory: veryRareDiscounts - discountDownTo: - Telecrystal: 15 - productEntity: HyperEutacticBlade - cost: - Telecrystal: 18 - categories: - - UplinkWeaponry - conditions: - - !type:BuyerWhitelistCondition - blacklist: - components: - - SurplusBundle - - !type:StoreWhitelistCondition - blacklist: - tags: - - NukeOpsUplink - -- type: listing - id: UplinkHyperEutaticBlade - name: uplink-hypereutatic-blade-name - description: uplink-hypereutatic-blade-desc - icon: { sprite: /Textures/Objects/Weapons/Melee/hypereutactic_blade.rsi, state: icon } - discountCategory: veryRareDiscounts - discountDownTo: - Telecrystal: 13 - productEntity: HyperEutaticBlade - cost: - Telecrystal: 16 - categories: - - UplinkWeaponry - conditions: - - !type:BuyerWhitelistCondition - blacklist: - components: - - SurplusBundle - - !type:StoreWhitelistCondition - whitelist: - tags: - - NukeOpsUplink - - type: listing id: UplinkEnergyDagger name: uplink-edagger-name diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index 21554eba07..f879e2891e 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -296,12 +296,11 @@ Slash: 12 Heat: 12 Structural: 15 - # Disabled until the wield active sfx no longer stacks - #- type: ItemToggleActiveSound - # activeSound: - # path: /Audio/Weapons/ebladehum.ogg - # params: - # volume: 3 + - type: ItemToggleActiveSound + activeSound: + path: /Audio/Weapons/ebladehum.ogg + params: + volume: 3 - type: ComponentToggler components: - type: Sharp @@ -387,17 +386,6 @@ reflectProb: 1.0 spread: 75 -# Nukie variant, reduced reflection rate. -- type: entity - parent: HyperEutacticBlade - id: HyperEutaticBlade - name: hypereutatic-blade - description: Often mistaken for the Hypereutactic Blade. This mass produced, off-brand, knockoff gets the same job done but with less reflection. - components: - - type: Reflect - reflectProb: 0.75 - spread: 75 - # Borgs - type: entity suffix: One-Handed, For Borgs From 575694e5b5898d1ad1cfd94c1930ee5d7ccbfb96 Mon Sep 17 00:00:00 2001 From: Vasilis The Pikachu Date: Mon, 7 Jul 2025 00:10:04 +0200 Subject: [PATCH 186/191] Changelog removal --- Resources/Changelog/Changelog.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index cf05e28dbe..f0137f6dbb 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -3660,15 +3660,6 @@ id: 8704 time: '2025-06-21T22:23:39.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/38486 -- author: keronshb - changes: - - message: Added the Hypereutactic Blade back to the Traitor Uplink - type: Add - - message: Added the Hypereutatic Blade to the Nukie uplink - type: Add - id: 8705 - time: '2025-06-22T16:50:59.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/37182 - author: Cojoke-dot changes: - message: Pacifists can now use the Staff of Healing From f5fd2dcb761b2f48ae14aa59ae63c7276cc16a96 Mon Sep 17 00:00:00 2001 From: Hannah Giovanna Dawson Date: Sun, 6 Jul 2025 23:30:44 +0100 Subject: [PATCH 187/191] Scurret naming fixes (#38776) Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../NameIdentifier/NameIdentifierSystem.cs | 49 ++++++++++++------- .../NameIdentifier/NameIdentifierComponent.cs | 10 ++-- .../en-US/datasets/names/scurret_first.ftl | 31 ++++++++++++ .../en-US/datasets/names/scurret_last.ftl | 38 ++++++++++++-- .../Prototypes/Datasets/Names/scurret.yml | 4 +- .../Prototypes/Entities/Mobs/NPCs/scurret.yml | 10 +++- 6 files changed, 112 insertions(+), 30 deletions(-) diff --git a/Content.Server/NameIdentifier/NameIdentifierSystem.cs b/Content.Server/NameIdentifier/NameIdentifierSystem.cs index 0a9f87557a..1e5ed67a63 100644 --- a/Content.Server/NameIdentifier/NameIdentifierSystem.cs +++ b/Content.Server/NameIdentifier/NameIdentifierSystem.cs @@ -20,7 +20,7 @@ public sealed class NameIdentifierSystem : EntitySystem /// Free IDs available per . ///
[ViewVariables] - public readonly Dictionary> CurrentIds = new(); + public readonly Dictionary> CurrentIds = []; public override void Initialize() { @@ -35,18 +35,22 @@ public sealed class NameIdentifierSystem : EntitySystem InitialSetupPrototypes(); } - private void OnComponentShutdown(EntityUid uid, NameIdentifierComponent component, ComponentShutdown args) + private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) { - if (CurrentIds.TryGetValue(component.Group, out var ids)) + if (ent.Comp.Group is null) + return; + + if (CurrentIds.TryGetValue(ent.Comp.Group, out var ids) && ids.Count > 0) { // Avoid inserting the value right back at the end or shuffling in place: // just pick a random spot to put it and then move that one to the end. var randomIndex = _robustRandom.Next(ids.Count); var random = ids[randomIndex]; - ids[randomIndex] = component.Identifier; + ids[randomIndex] = ent.Comp.Identifier; ids.Add(random); } - _nameModifier.RefreshNameModifiers(uid); + + _nameModifier.RefreshNameModifiers(ent.Owner); } /// @@ -83,46 +87,53 @@ public sealed class NameIdentifierSystem : EntitySystem : $"{randomVal}"; } - private void OnMapInit(EntityUid uid, NameIdentifierComponent component, MapInitEvent args) + private void OnMapInit(Entity ent, ref MapInitEvent args) { - if (!_prototypeManager.TryIndex(component.Group, out var group)) + if (ent.Comp.Group is null) + return; + + if (!_prototypeManager.TryIndex(ent.Comp.Group, out var group)) return; int id; string uniqueName; // If it has an existing valid identifier then use that, otherwise generate a new one. - if (component.Identifier != -1 && - CurrentIds.TryGetValue(component.Group, out var ids) && - ids.Remove(component.Identifier)) + if (ent.Comp.Identifier != -1 && + CurrentIds.TryGetValue(ent.Comp.Group, out var ids) && + ids.Remove(ent.Comp.Identifier)) { - id = component.Identifier; + id = ent.Comp.Identifier; uniqueName = group.Prefix is not null ? $"{group.Prefix}-{id}" : $"{id}"; } else { - uniqueName = GenerateUniqueName(uid, group, out id); - component.Identifier = id; + uniqueName = GenerateUniqueName(ent, group, out id); + ent.Comp.Identifier = id; } - component.FullIdentifier = group.FullName + ent.Comp.FullIdentifier = group.FullName ? uniqueName : $"({uniqueName})"; - Dirty(uid, component); - _nameModifier.RefreshNameModifiers(uid); + Dirty(ent); + _nameModifier.RefreshNameModifiers(ent.Owner); } private void OnRefreshNameModifiers(Entity ent, ref RefreshNameModifiersEvent args) { + if (ent.Comp.Group is null) + return; + // Don't apply the modifier if the component is being removed if (ent.Comp.LifeStage > ComponentLifeStage.Running) return; - if (!_prototypeManager.TryIndex(ent.Comp.Group, out var group)) + if (!_prototypeManager.TryIndex(ent.Comp.Group, out var group)) return; + var format = group.FullName ? "name-identifier-format-full" : "name-identifier-format-append"; // We apply the modifier with a low priority to keep it near the base name // "Beep (Si-4562) the zombie" instead of "Beep the zombie (Si-4562)" @@ -188,13 +199,13 @@ public sealed class NameIdentifierSystem : EntitySystem foreach (var proto in set.Modified.Values) { - var name_proto = (NameIdentifierGroupPrototype) proto; + var name_proto = (NameIdentifierGroupPrototype)proto; // Only bother adding new ones. if (CurrentIds.ContainsKey(proto.ID)) continue; - var ids = GetOrCreateIdList(name_proto); + var ids = GetOrCreateIdList(name_proto); FillGroup(name_proto, ids); } } diff --git a/Content.Shared/NameIdentifier/NameIdentifierComponent.cs b/Content.Shared/NameIdentifier/NameIdentifierComponent.cs index 49be08a6a3..f9f9eef49b 100644 --- a/Content.Shared/NameIdentifier/NameIdentifierComponent.cs +++ b/Content.Shared/NameIdentifier/NameIdentifierComponent.cs @@ -1,5 +1,5 @@ using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Prototypes; namespace Content.Shared.NameIdentifier; @@ -9,18 +9,18 @@ namespace Content.Shared.NameIdentifier; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class NameIdentifierComponent : Component { - [DataField("group", required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] - public string Group = string.Empty; + [DataField] + public ProtoId? Group; /// /// The randomly generated ID for this entity. /// - [DataField("identifier"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField, AutoNetworkedField] public int Identifier = -1; /// /// The full name identifier for this entity. /// - [DataField("fullIdentifier"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField, AutoNetworkedField] public string FullIdentifier = string.Empty; } diff --git a/Resources/Locale/en-US/datasets/names/scurret_first.ftl b/Resources/Locale/en-US/datasets/names/scurret_first.ftl index 4bbbf943f0..a01a6db8bb 100644 --- a/Resources/Locale/en-US/datasets/names/scurret_first.ftl +++ b/Resources/Locale/en-US/datasets/names/scurret_first.ftl @@ -1,3 +1,19 @@ +# Scurrets from Planet Wawa have two parts to their name - a 'chosen' and 'qualitative' name. + +# The chosen name is picked by the scurret themselves, +# encompassing a trait or value they hold themselves +# to or have a high value for. Scurrets sometimes change this +# name to denote an important moment in their life. + +# It appears to be common for scurret sets to share the same +# chosen name, with the gaining of a pup's own chosen name +# signalling their transition to adulthood in the community. + +# Given the scurret language is untranslated, these names are +# usually guessed via charades or Pictionary. + +# When all else fails, to NT and her crews, Wa is as good a name as any. + names-scurret-first-dataset-1 = Wa names-scurret-first-dataset-2 = Calm names-scurret-first-dataset-3 = Contented @@ -34,3 +50,18 @@ names-scurret-first-dataset-33 = Wise names-scurret-first-dataset-34 = Alert names-scurret-first-dataset-35 = Uplifting names-scurret-first-dataset-36 = Considerate +names-scurret-first-dataset-37 = Surviving +names-scurret-first-dataset-38 = Meditating +names-scurret-first-dataset-39 = Hunting +names-scurret-first-dataset-40 = Watching +names-scurret-first-dataset-41 = Resting +names-scurret-first-dataset-42 = Delivering +names-scurret-first-dataset-43 = Swimming +names-scurret-first-dataset-44 = Swinging +names-scurret-first-dataset-45 = Exploding +names-scurret-first-dataset-46 = Romancing +names-scurret-first-dataset-47 = Far-Seeing +names-scurret-first-dataset-48 = Loyal +names-scurret-first-dataset-49 = Inquisitive +# After consulting with lawyers, NT added this one to the dictionary. +names-scurret-first-dataset-50 = Legally Distinct diff --git a/Resources/Locale/en-US/datasets/names/scurret_last.ftl b/Resources/Locale/en-US/datasets/names/scurret_last.ftl index effe2180f0..bac9b818d1 100644 --- a/Resources/Locale/en-US/datasets/names/scurret_last.ftl +++ b/Resources/Locale/en-US/datasets/names/scurret_last.ftl @@ -1,3 +1,19 @@ +# Scurrets from Planet Wawa have two parts to their name - a 'chosen' and 'qualitative' name. + +# The qualitative name is usually related to an important feature +# of Wawa's wetland habitats that the scurret is associated with +# by their community. + +# Scurret pups, due to both their quantity and complete lack +# of any survival instinct, lack a qualitative name entirely. +# Researchers believe their parents simply give them a number. + +# Given that the scurret language is untranslated, these names are +# usually deduced via the showing of photographs, annoyed and +# repeated pointing at nearby objects, or games of Pictionary. + +# When all else fails, to NT and her crews, Wa is as good a name as any. + names-scurret-last-dataset-1 = Wa names-scurret-last-dataset-2 = Trees names-scurret-last-dataset-3 = Plants @@ -6,12 +22,12 @@ names-scurret-last-dataset-5 = Rivers names-scurret-last-dataset-6 = Groves names-scurret-last-dataset-7 = Lakes names-scurret-last-dataset-8 = Marshes -names-scurret-last-dataset-9 = Spring +names-scurret-last-dataset-9 = Springs names-scurret-last-dataset-10 = Reeds names-scurret-last-dataset-11 = Sunshine names-scurret-last-dataset-12 = Rain names-scurret-last-dataset-13 = Clouds -names-scurret-last-dataset-14 = Snowfall +names-scurret-last-dataset-14 = Snowfalls names-scurret-last-dataset-15 = Stones names-scurret-last-dataset-16 = Pebbles names-scurret-last-dataset-17 = Fishes @@ -25,7 +41,7 @@ names-scurret-last-dataset-24 = Alders names-scurret-last-dataset-25 = Birches names-scurret-last-dataset-26 = Poplars names-scurret-last-dataset-27 = Marigolds -names-scurret-last-dataset-28 = Robins +names-scurret-last-dataset-28 = Rowans names-scurret-last-dataset-29 = Orchids names-scurret-last-dataset-30 = Rushes names-scurret-last-dataset-31 = Lillies @@ -33,4 +49,20 @@ names-scurret-last-dataset-32 = Violets names-scurret-last-dataset-33 = Maples names-scurret-last-dataset-34 = Oaks names-scurret-last-dataset-35 = Hazels +# AND SIR GIDEON OFNIR names-scurret-last-dataset-36 = the All-Knowing +names-scurret-last-dataset-37 = Tarns +names-scurret-last-dataset-38 = Waters +names-scurret-last-dataset-39 = Reservoirs +names-scurret-last-dataset-40 = Dams +names-scurret-last-dataset-41 = Moors +names-scurret-last-dataset-42 = Fens +names-scurret-last-dataset-43 = Temples +names-scurret-last-dataset-44 = Hills +names-scurret-last-dataset-45 = Copses +names-scurret-last-dataset-46 = Fields +names-scurret-last-dataset-47 = Ancestors +names-scurret-last-dataset-48 = Forests +names-scurret-last-dataset-49 = Secrets +# Nobody's quite sure how this one is in the dictionary. +names-scurret-last-dataset-50 = Space Ferret diff --git a/Resources/Prototypes/Datasets/Names/scurret.yml b/Resources/Prototypes/Datasets/Names/scurret.yml index b3ac08e2ad..9e23557b38 100644 --- a/Resources/Prototypes/Datasets/Names/scurret.yml +++ b/Resources/Prototypes/Datasets/Names/scurret.yml @@ -2,10 +2,10 @@ id: NamesFirstScurret values: prefix: names-scurret-first-dataset- - count: 36 + count: 50 - type: localizedDataset id: NamesLastScurret values: prefix: names-scurret-last-dataset- - count: 36 + count: 50 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml b/Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml index 19ba323a0a..1f0eb0f9e2 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml @@ -139,6 +139,14 @@ available: - enum.DamageStateVisualLayers.Base: scurret: ScurretColors + # They are of a mysterious gender. + - type: Grammar + attributes: + gender: epicene + proper: true + # Strips the name identifier from them, so they're just "Confident Waters" rather than "Confident Waters (123)" + - type: NameIdentifier + group: null # Emotional Support Scurrets have a ghost role and equipment. At the moment, these are intended to be used for admemes, but # feel free to hook them into random content. @@ -169,4 +177,4 @@ name: Emotional Support Scurret id: MobEmotionalSupportScurret parent: [MobScurret, MobBaseEmotionalSupportScurret] - description: Commonly known as Wawa, from the wetlands of Planet Wawa, these critters make up the bulk of Arnolds's Pizza's "loyal workforce". This one is here as a temp. + description: Commonly known as Wawa, from the wetlands of Planet Wawa, these critters make up the bulk of Arnold's Pizza's "loyal workforce". This one is here as a temp. From 5f7db3b1514e58211041ce1c9c3ed23ef889df7b Mon Sep 17 00:00:00 2001 From: Hannah Giovanna Dawson Date: Sun, 6 Jul 2025 23:42:13 +0100 Subject: [PATCH 188/191] Scurret displacement map fixes (#38775) --- .../Prototypes/Entities/Mobs/NPCs/scurret.yml | 32 ++++++++++++++++++ .../Animals/scurret/displacement.rsi/back.png | Bin 0 -> 279 bytes .../Animals/scurret/displacement.rsi/hand.png | Bin 426 -> 382 bytes .../scurret/displacement.rsi/jumpsuit.png | Bin 329 -> 510 bytes .../scurret/displacement.rsi/meta.json | 4 +++ 5 files changed, 36 insertions(+) create mode 100644 Resources/Textures/Mobs/Animals/scurret/displacement.rsi/back.png diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml b/Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml index 1f0eb0f9e2..6d328014db 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/scurret.yml @@ -104,6 +104,38 @@ 32: sprite: Mobs/Animals/scurret/displacement.rsi state: neck + ears: + sizeMaps: + 32: + sprite: Mobs/Animals/scurret/displacement.rsi + state: ears + eyes: + sizeMaps: + 32: + sprite: Mobs/Animals/scurret/displacement.rsi + state: eyes + head: + sizeMaps: + 32: + sprite: Mobs/Animals/scurret/displacement.rsi + state: head + mask: + sizeMaps: + 32: + sprite: Mobs/Animals/scurret/displacement.rsi + state: mask + back: + sizeMaps: + 32: + sprite: Mobs/Animals/scurret/displacement.rsi + state: back + - type: Hands + leftHandDisplacement: + sizeMaps: + 32: + sprite: Mobs/Animals/scurret/displacement.rsi + state: hand + - type: InventorySlots - type: Food - type: Hunger diff --git a/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/back.png b/Resources/Textures/Mobs/Animals/scurret/displacement.rsi/back.png new file mode 100644 index 0000000000000000000000000000000000000000..110555738328a9c2754c179f5ede423c3028c6ba GIT binary patch literal 279 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|E_%8+hE&XX zJIj%)*+7J~zU#V~2Y+6yXw1|{+I*R5VR|O@MjA>Hs>PSL#jRg0Zr^n9Z{*rlS~KTc z?0K$U{ezpwf%}3)gV+K?23?0_hNy;P3|km_88aBAm@hDyuq|NCz@=fIMY7Y zTS?b%1$*pVyDH_q==L8{jMDrkru?hlAr!#Gxq$hC2ZL;b8`BGhddnmC{#HL@TgNiR z0mwBGa$sLjsGMN+;`RAQP7W&{HM2ABJbK%7zr~cq|i4t&wuXDwCm|P|LSGYyG9Ylofi(x`WRWZ zXydWJ-#-7GF@wjE-;r}d00XjXz}8P>uwv+CxxkpA(!l4S%vi!8g`s5U9tK6dbL=7K VG^>)FyPq)tfv2mV%Q~loCICj*mka;^ delta 387 zcmeyzw2FCxK|KRYx}&cn1H;CC?mvmF3=E7`o-U3d6>)E8-sWpE5OC?<|5nxI%Wl)z zKD~h)Tb7!q`6V6u@I>y`QoX~qMf%~5x3BX4`1SCY#{XzPkEmV32C@Ib`!<%;_O4_3 zp;X@;yK7%--u4F3*Y-O^T*H~J%}VhR4i$E}q{QeErNS`t%$@p++t|wlA9SDBO?*4k zzf|BBH>U$o_JqoxfTh<8R9o+cIlOxm{Qs-&#iL>;Lib$U(=g|#bbEG_nvTn@hb$G_ zyfWe(-UaTw$E}b#kwHb*_bY>7%WE@LkQDyXISfie$VT&g3XYH1G!e5m@@fAeA$| zGfQ~*7XRDwLy&=ifiE?}GtJkR ZK?}&{0Adih6g(M3dAjDH|r(ggfD8?HcbH2L$F=0P({uM?U4}u)0=XX=*YZC@Jj)DEe`5cB~XLcI#r2WDX zkD*6vE%273|8HX@aCYcAz(>7od0K}s{D0wTg2l&-(OT5-sYrk&_7(9ibKEo&F26B N002ovPDHLkV1i8^*(3k} delta 289 zcmeyze3EH`VLh{_i(^Q|oVT~`3N Date: Mon, 7 Jul 2025 14:23:45 +0200 Subject: [PATCH 189/191] sanitize MIDI parser (#38806) Co-authored-by: Pieter-Jan Briers --- .../Instruments/MidiParser/MidiParser.cs | 2 ++ .../Instruments/InstrumentSystem.cs | 15 ++++++----- .../Instruments/SharedInstrumentComponent.cs | 26 +++++++++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/Content.Client/Instruments/MidiParser/MidiParser.cs b/Content.Client/Instruments/MidiParser/MidiParser.cs index 937384e439..0b65e472fc 100644 --- a/Content.Client/Instruments/MidiParser/MidiParser.cs +++ b/Content.Client/Instruments/MidiParser/MidiParser.cs @@ -102,6 +102,8 @@ public static class MidiParser // 0x03 is TrackName, // 0x04 is InstrumentName + // This string can potentially contain control characters, including 0x00 which can cause problems if it ends up in database entries via admin logs + // we sanitize TrackName and InstrumentName after they have been send to the server var text = Encoding.ASCII.GetString(metaData, 0, (int)metaLength); switch (metaType) { diff --git a/Content.Server/Instruments/InstrumentSystem.cs b/Content.Server/Instruments/InstrumentSystem.cs index 420bd44737..f6a2162271 100644 --- a/Content.Server/Instruments/InstrumentSystem.cs +++ b/Content.Server/Instruments/InstrumentSystem.cs @@ -156,6 +156,15 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem return; } + + foreach (var t in msg.Tracks) + { + // Remove any control characters that may be part of the midi file so they don't end up in the admin logs. + t?.SanitizeFields(); + // Truncate any track names too long. + t?.TruncateFields(_cfg.GetCVar(CCVars.MidiMaxChannelNameLength)); + } + var tracksString = string.Join("\n", msg.Tracks .Where(t => t != null) @@ -166,12 +175,6 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem LogImpact.Low, $"{ToPrettyString(args.SenderSession.AttachedEntity)} set the midi channels for {ToPrettyString(uid)} to {tracksString}"); - // Truncate any track names too long. - foreach (var t in msg.Tracks) - { - t?.TruncateFields(_cfg.GetCVar(CCVars.MidiMaxChannelNameLength)); - } - activeInstrument.Tracks = msg.Tracks; Dirty(uid, activeInstrument); diff --git a/Content.Shared/Instruments/SharedInstrumentComponent.cs b/Content.Shared/Instruments/SharedInstrumentComponent.cs index 97eef752eb..41bef64902 100644 --- a/Content.Shared/Instruments/SharedInstrumentComponent.cs +++ b/Content.Shared/Instruments/SharedInstrumentComponent.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Text; using Robust.Shared.Audio.Midi; using Robust.Shared.GameStates; using Robust.Shared.Serialization; @@ -207,6 +208,18 @@ public sealed class MidiTrack ProgramName = Truncate(ProgramName, limit); } + public void SanitizeFields() + { + if (InstrumentName != null) + InstrumentName = Sanitize(InstrumentName); + + if (TrackName != null) + TrackName = Sanitize(TrackName); + + if (ProgramName != null) + ProgramName = Sanitize(ProgramName); + } + private const string Postfix = "…"; // TODO: Make a general method to use in RT? idk if we have that. private string Truncate(string input, int limit) @@ -218,4 +231,17 @@ public sealed class MidiTrack return input.Substring(0, truncatedLength) + Postfix; } + + private static string Sanitize(string input) + { + var sanitized = new StringBuilder(input.Length); + + foreach (char c in input) + { + if (!char.IsControl(c) && c <= 127) // no control characters, only ASCII + sanitized.Append(c); + } + + return sanitized.ToString(); + } } From 218158503050856f586e294a797859b42160a40c Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:01:52 +0200 Subject: [PATCH 190/191] Add Breach of Permanent Confinement to Space Law (#38663) * Initial commit * Fix category in text --- .../ServerRules/SpaceLaw/SLCrimeList.xml | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLCrimeList.xml b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLCrimeList.xml index c6d29bfba9..2ea20b5a0c 100644 --- a/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLCrimeList.xml +++ b/Resources/ServerInfo/Guidebook/ServerRules/SpaceLaw/SLCrimeList.xml @@ -56,7 +56,7 @@ - Refusal of Mental Shielding (E) + Breach of Permanent Confinement (E) @@ -251,8 +251,9 @@ - + + Refusal of Mental Shielding (R) @@ -752,7 +753,7 @@ - While rare, this charge can be bumped to an execution if the suspect has repeatedly attempted to break out of the permanent brig. Includes people breaking others out. + Includes breaking out other people. @@ -847,17 +848,17 @@ - Refusal of Mental Shielding + Breach of Permanent Confinement - To refuse to comply with a reasonable Mind Shielding procedure. + To break someone sentenced to Permanent Confinement out of a cell or custody with the intention of escaping. - Applies if the suspect is excessively uncooperative or the implant fails to function due to the mental state of the prisoner already being too far gone. If the implant fails execution is heavily recommended. + Includes breaking out other people sentenced to Permanent Confinement. @@ -925,5 +926,25 @@ 5-06 + + + Refusal of Mental Shielding + + + + + To refuse to comply with a reasonable Mind Shielding procedure. + + + + + Applies if the suspect is excessively uncooperative or the implant fails to function due to the mental state of the prisoner already being too far gone. If the implant fails execution is heavily recommended. + + + + + 5-07 + + From f4d223b2e1b8d714f50abc217a68105156573238 Mon Sep 17 00:00:00 2001 From: Fildrance Date: Sat, 12 Jul 2025 20:53:08 +0300 Subject: [PATCH 191/191] refactor: rework the new status effect system to use containers (#38915) (#38943) Co-authored-by: Perry Fraser --- .../Drowsiness/DrowsinessOverlay.cs | 4 +- Content.Client/Drowsiness/DrowsinessSystem.cs | 2 +- Content.Client/Drugs/RainbowOverlay.cs | 4 +- .../ClientStatusEffectsSystem.cs | 50 ------ Content.Server/Drowsiness/DrowsinessSystem.cs | 1 - .../StatusEffectNew/StatusEffectsSystem.cs | 23 --- .../Traits/Assorted/NarcolepsySystem.cs | 2 +- Content.Shared/Bed/Sleep/SleepingSystem.cs | 4 +- .../StatusEffects/ModifyStatusEffect.cs | 2 +- .../SSDIndicator/SSDIndicatorSystem.cs | 2 +- .../StatusEffect/StatusEffectsSystem.cs | 24 +-- .../Components/StatusEffectAlertComponent.cs | 26 +++ .../Components/StatusEffectComponent.cs | 8 +- .../StatusEffectContainerComponent.cs | 17 +- .../StatusEffectAlertSystem.cs | 62 +++++++ .../StatusEffectNew/StatusEffectSystem.API.cs | 46 +++-- .../StatusEffectSystem.Relay.cs | 12 +- ...ffectsSystem.cs => StatusEffectsSystem.cs} | 169 ++++++++++-------- 18 files changed, 244 insertions(+), 214 deletions(-) delete mode 100644 Content.Client/StatusEffectNew/ClientStatusEffectsSystem.cs delete mode 100644 Content.Server/StatusEffectNew/StatusEffectsSystem.cs create mode 100644 Content.Shared/StatusEffectNew/Components/StatusEffectAlertComponent.cs create mode 100644 Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs rename Content.Shared/StatusEffectNew/{SharedStatusEffectsSystem.cs => StatusEffectsSystem.cs} (56%) diff --git a/Content.Client/Drowsiness/DrowsinessOverlay.cs b/Content.Client/Drowsiness/DrowsinessOverlay.cs index 1687216b3e..bc176a63b1 100644 --- a/Content.Client/Drowsiness/DrowsinessOverlay.cs +++ b/Content.Client/Drowsiness/DrowsinessOverlay.cs @@ -15,7 +15,7 @@ public sealed class DrowsinessOverlay : Overlay [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IEntitySystemManager _sysMan = default!; [Dependency] private readonly IGameTiming _timing = default!; - private readonly SharedStatusEffectsSystem _statusEffects = default!; + private readonly StatusEffectsSystem _statusEffects = default!; public override OverlaySpace Space => OverlaySpace.WorldSpace; public override bool RequestScreenTexture => true; @@ -31,7 +31,7 @@ public sealed class DrowsinessOverlay : Overlay { IoCManager.InjectDependencies(this); - _statusEffects = _sysMan.GetEntitySystem(); + _statusEffects = _sysMan.GetEntitySystem(); _drowsinessShader = _prototypeManager.Index("Drowsiness").InstanceUnique(); } diff --git a/Content.Client/Drowsiness/DrowsinessSystem.cs b/Content.Client/Drowsiness/DrowsinessSystem.cs index 3b35101489..632d2134ca 100644 --- a/Content.Client/Drowsiness/DrowsinessSystem.cs +++ b/Content.Client/Drowsiness/DrowsinessSystem.cs @@ -10,7 +10,7 @@ public sealed class DrowsinessSystem : SharedDrowsinessSystem { [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IOverlayManager _overlayMan = default!; - [Dependency] private readonly SharedStatusEffectsSystem _statusEffects = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; private DrowsinessOverlay _overlay = default!; diff --git a/Content.Client/Drugs/RainbowOverlay.cs b/Content.Client/Drugs/RainbowOverlay.cs index bfb3815649..16c34e63ab 100644 --- a/Content.Client/Drugs/RainbowOverlay.cs +++ b/Content.Client/Drugs/RainbowOverlay.cs @@ -18,7 +18,7 @@ public sealed class RainbowOverlay : Overlay [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IEntitySystemManager _sysMan = default!; [Dependency] private readonly IGameTiming _timing = default!; - private readonly SharedStatusEffectsSystem _statusEffects = default!; + private readonly StatusEffectsSystem _statusEffects = default!; public override OverlaySpace Space => OverlaySpace.WorldSpace; public override bool RequestScreenTexture => true; @@ -39,7 +39,7 @@ public sealed class RainbowOverlay : Overlay { IoCManager.InjectDependencies(this); - _statusEffects = _sysMan.GetEntitySystem(); + _statusEffects = _sysMan.GetEntitySystem(); _rainbowShader = _prototypeManager.Index("Rainbow").InstanceUnique(); _config.OnValueChanged(CCVars.ReducedMotion, OnReducedMotionChanged, invokeImmediately: true); diff --git a/Content.Client/StatusEffectNew/ClientStatusEffectsSystem.cs b/Content.Client/StatusEffectNew/ClientStatusEffectsSystem.cs deleted file mode 100644 index e35c09190e..0000000000 --- a/Content.Client/StatusEffectNew/ClientStatusEffectsSystem.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Content.Shared.StatusEffectNew; -using Content.Shared.StatusEffectNew.Components; -using Robust.Shared.Collections; -using Robust.Shared.GameStates; - -namespace Content.Client.StatusEffectNew; - -/// -public sealed partial class ClientStatusEffectsSystem : SharedStatusEffectsSystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnHandleState); - } - - private void OnHandleState(Entity ent, ref ComponentHandleState args) - { - if (args.Current is not StatusEffectContainerComponentState state) - return; - - var toRemove = new ValueList(); - foreach (var effect in ent.Comp.ActiveStatusEffects) - { - if (state.ActiveStatusEffects.Contains(GetNetEntity(effect))) - continue; - - toRemove.Add(effect); - } - - foreach (var effect in toRemove) - { - ent.Comp.ActiveStatusEffects.Remove(effect); - var ev = new StatusEffectRemovedEvent(ent); - RaiseLocalEvent(effect, ref ev); - } - - foreach (var effect in state.ActiveStatusEffects) - { - var effectUid = GetEntity(effect); - if (ent.Comp.ActiveStatusEffects.Contains(effectUid)) - continue; - - ent.Comp.ActiveStatusEffects.Add(effectUid); - var ev = new StatusEffectAppliedEvent(ent); - RaiseLocalEvent(effectUid, ref ev); - } - } -} diff --git a/Content.Server/Drowsiness/DrowsinessSystem.cs b/Content.Server/Drowsiness/DrowsinessSystem.cs index 6de270abcc..13fdc42e10 100644 --- a/Content.Server/Drowsiness/DrowsinessSystem.cs +++ b/Content.Server/Drowsiness/DrowsinessSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.StatusEffectNew; using Content.Shared.Bed.Sleep; using Content.Shared.Drowsiness; using Content.Shared.StatusEffectNew; diff --git a/Content.Server/StatusEffectNew/StatusEffectsSystem.cs b/Content.Server/StatusEffectNew/StatusEffectsSystem.cs deleted file mode 100644 index e5d7433396..0000000000 --- a/Content.Server/StatusEffectNew/StatusEffectsSystem.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Content.Shared.StatusEffectNew; -using Content.Shared.StatusEffectNew.Components; - -namespace Content.Server.StatusEffectNew; - -/// -public sealed partial class StatusEffectsSystem : SharedStatusEffectsSystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnContainerShutdown); - } - - private void OnContainerShutdown(Entity ent, ref ComponentShutdown args) - { - foreach (var effect in ent.Comp.ActiveStatusEffects) - { - QueueDel(effect); - } - } -} diff --git a/Content.Server/Traits/Assorted/NarcolepsySystem.cs b/Content.Server/Traits/Assorted/NarcolepsySystem.cs index b0746fa377..159e953369 100644 --- a/Content.Server/Traits/Assorted/NarcolepsySystem.cs +++ b/Content.Server/Traits/Assorted/NarcolepsySystem.cs @@ -1,5 +1,5 @@ -using Content.Server.StatusEffectNew; using Content.Shared.Bed.Sleep; +using Content.Shared.StatusEffectNew; using Robust.Shared.Random; namespace Content.Server.Traits.Assorted; diff --git a/Content.Shared/Bed/Sleep/SleepingSystem.cs b/Content.Shared/Bed/Sleep/SleepingSystem.cs index 9e1b27cfc9..cdff4b7fd7 100644 --- a/Content.Shared/Bed/Sleep/SleepingSystem.cs +++ b/Content.Shared/Bed/Sleep/SleepingSystem.cs @@ -38,8 +38,8 @@ public sealed partial class SleepingSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedEmitSoundSystem _emitSound = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffectOld = default!; - [Dependency] private readonly SharedStatusEffectsSystem _statusEffectNew = default!; + [Dependency] private readonly StatusEffect.StatusEffectsSystem _statusEffectOld = default!; + [Dependency] private readonly StatusEffectNew.StatusEffectsSystem _statusEffectNew = default!; public static readonly EntProtoId SleepActionId = "ActionSleep"; public static readonly EntProtoId WakeActionId = "ActionWake"; diff --git a/Content.Shared/EntityEffects/Effects/StatusEffects/ModifyStatusEffect.cs b/Content.Shared/EntityEffects/Effects/StatusEffects/ModifyStatusEffect.cs index 5ebb8aad1b..a232d34925 100644 --- a/Content.Shared/EntityEffects/Effects/StatusEffects/ModifyStatusEffect.cs +++ b/Content.Shared/EntityEffects/Effects/StatusEffects/ModifyStatusEffect.cs @@ -34,7 +34,7 @@ public sealed partial class ModifyStatusEffect : EntityEffect /// public override void Effect(EntityEffectBaseArgs args) { - var statusSys = args.EntityManager.EntitySysManager.GetEntitySystem(); + var statusSys = args.EntityManager.EntitySysManager.GetEntitySystem(); var time = Time; if (args is EntityEffectReagentArgs reagentArgs) diff --git a/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs b/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs index ca7d73ac83..b9c6659c9c 100644 --- a/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs +++ b/Content.Shared/SSDIndicator/SSDIndicatorSystem.cs @@ -16,7 +16,7 @@ public sealed class SSDIndicatorSystem : EntitySystem [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly SharedStatusEffectsSystem _statusEffects = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; private bool _icSsdSleep; private float _icSsdSleepTime; diff --git a/Content.Shared/StatusEffect/StatusEffectsSystem.cs b/Content.Shared/StatusEffect/StatusEffectsSystem.cs index f3409d1c2c..b56acd3cc5 100644 --- a/Content.Shared/StatusEffect/StatusEffectsSystem.cs +++ b/Content.Shared/StatusEffect/StatusEffectsSystem.cs @@ -9,7 +9,7 @@ using Robust.Shared.Utility; namespace Content.Shared.StatusEffect { - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public sealed class StatusEffectsSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -106,7 +106,7 @@ namespace Content.Shared.StatusEffect /// The status effects component to change, if you already have it. /// False if the effect could not be added or the component already exists, true otherwise. /// The component type to add and remove from the entity. - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, StatusEffectsComponent? status = null) where T : IComponent, new() @@ -126,7 +126,7 @@ namespace Content.Shared.StatusEffect } - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, string component, StatusEffectsComponent? status = null) { @@ -166,7 +166,7 @@ namespace Content.Shared.StatusEffect /// If the effect already exists, it will simply replace the cooldown with the new one given. /// If you want special 'effect merging' behavior, do it your own damn self! /// - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, @@ -260,7 +260,7 @@ namespace Content.Shared.StatusEffect /// Obviously this doesn't automatically clear any effects a status effect might have. /// That's up to the removed component to handle itself when it's removed. /// - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public bool TryRemoveStatusEffect(EntityUid uid, string key, StatusEffectsComponent? status = null, bool remComp = true) { @@ -304,7 +304,7 @@ namespace Content.Shared.StatusEffect /// The entity to remove effects from. /// The status effects component to change, if you already have it. /// False if any status effects failed to be removed, true if they all did. - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public bool TryRemoveAllStatusEffects(EntityUid uid, StatusEffectsComponent? status = null) { @@ -328,7 +328,7 @@ namespace Content.Shared.StatusEffect /// The entity to check on. /// The status effect ID to check for /// The status effect component, should you already have it. - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public bool HasStatusEffect(EntityUid uid, string key, StatusEffectsComponent? status = null) { @@ -346,7 +346,7 @@ namespace Content.Shared.StatusEffect /// The entity to check on. /// The status effect ID to check for /// The status effect component, should you already have it. - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public bool CanApplyEffect(EntityUid uid, string key, StatusEffectsComponent? status = null) { // don't log since stuff calling this prolly doesn't care if we don't actually have it @@ -373,7 +373,7 @@ namespace Content.Shared.StatusEffect /// The status effect to add time to. /// The amount of time to add. /// The status effect component, should you already have it. - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public bool TryAddTime(EntityUid uid, string key, TimeSpan time, StatusEffectsComponent? status = null) { @@ -405,7 +405,7 @@ namespace Content.Shared.StatusEffect /// The status effect to remove time from. /// The amount of time to add. /// The status effect component, should you already have it. - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public bool TryRemoveTime(EntityUid uid, string key, TimeSpan time, StatusEffectsComponent? status = null) { @@ -441,7 +441,7 @@ namespace Content.Shared.StatusEffect /// /// Not used internally; just sets it itself. /// - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public bool TrySetTime(EntityUid uid, string key, TimeSpan time, StatusEffectsComponent? status = null) { @@ -465,7 +465,7 @@ namespace Content.Shared.StatusEffect /// Out var for the time, if it exists. /// The status effects component to use, if any. /// False if the status effect was not active, true otherwise. - [Obsolete("Migration to Content.Shared.StatusEffectNew.SharedStatusEffectsSystem is required")] + [Obsolete("Migration to Content.Shared.StatusEffectNew.StatusEffectsSystem is required")] public bool TryGetTime(EntityUid uid, string key, [NotNullWhen(true)] out (TimeSpan, TimeSpan)? time, StatusEffectsComponent? status = null) diff --git a/Content.Shared/StatusEffectNew/Components/StatusEffectAlertComponent.cs b/Content.Shared/StatusEffectNew/Components/StatusEffectAlertComponent.cs new file mode 100644 index 0000000000..a389be6947 --- /dev/null +++ b/Content.Shared/StatusEffectNew/Components/StatusEffectAlertComponent.cs @@ -0,0 +1,26 @@ +using Content.Shared.Alert; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.StatusEffectNew.Components; + +/// +/// Used in conjunction with to display an alert when the status effect is present. +/// +[RegisterComponent, NetworkedComponent] +[EntityCategory("StatusEffects")] +public sealed partial class StatusEffectAlertComponent : Component +{ + /// + /// Status effect indication for the player. + /// + [DataField] + public ProtoId Alert; + + /// + /// If the status effect has a set end time and this is true, a duration + /// indicator will be displayed with the alert. + /// + [DataField] + public bool ShowDuration = true; +} diff --git a/Content.Shared/StatusEffectNew/Components/StatusEffectComponent.cs b/Content.Shared/StatusEffectNew/Components/StatusEffectComponent.cs index 6419874212..25f40718e9 100644 --- a/Content.Shared/StatusEffectNew/Components/StatusEffectComponent.cs +++ b/Content.Shared/StatusEffectNew/Components/StatusEffectComponent.cs @@ -11,7 +11,7 @@ namespace Content.Shared.StatusEffectNew.Components; /// Provides a link between the effect and the affected entity, and some data common to all status effects. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] -[Access(typeof(SharedStatusEffectsSystem))] +[Access(typeof(StatusEffectsSystem))] [EntityCategory("StatusEffects")] public sealed partial class StatusEffectComponent : Component { @@ -21,12 +21,6 @@ public sealed partial class StatusEffectComponent : Component [DataField, AutoNetworkedField] public EntityUid? AppliedTo; - /// - /// Status effect indication for the player. If Null, no Alert will be displayed. - /// - [DataField] - public ProtoId? Alert; - /// /// When this effect will end. If Null, the effect lasts indefinitely. /// diff --git a/Content.Shared/StatusEffectNew/Components/StatusEffectContainerComponent.cs b/Content.Shared/StatusEffectNew/Components/StatusEffectContainerComponent.cs index 6d9efaf3ac..9c2820653a 100644 --- a/Content.Shared/StatusEffectNew/Components/StatusEffectContainerComponent.cs +++ b/Content.Shared/StatusEffectNew/Components/StatusEffectContainerComponent.cs @@ -1,5 +1,5 @@ +using Robust.Shared.Containers; using Robust.Shared.GameStates; -using Robust.Shared.Serialization; namespace Content.Shared.StatusEffectNew.Components; @@ -9,15 +9,14 @@ namespace Content.Shared.StatusEffectNew.Components; /// Can be used for tracking currently applied status effects. ///
[RegisterComponent, NetworkedComponent] -[Access(typeof(SharedStatusEffectsSystem))] +[Access(typeof(StatusEffectsSystem))] public sealed partial class StatusEffectContainerComponent : Component { - [DataField] - public HashSet ActiveStatusEffects = new(); -} + public const string ContainerId = "status-effects"; -[Serializable, NetSerializable] -public sealed class StatusEffectContainerComponentState(HashSet activeStatusEffects) : ComponentState -{ - public readonly HashSet ActiveStatusEffects = activeStatusEffects; + /// + /// The actual container holding references to the active status effects + /// + [ViewVariables] + public Container? ActiveStatusEffects; } diff --git a/Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs b/Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs new file mode 100644 index 0000000000..d540f865c0 --- /dev/null +++ b/Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs @@ -0,0 +1,62 @@ +using Content.Shared.Alert; +using Content.Shared.StatusEffectNew.Components; +using Robust.Shared.Timing; + +namespace Content.Shared.StatusEffectNew; + +/// +/// Handles displaying status effects that should show an alert, optionally with a duration. +/// +public sealed class StatusEffectAlertSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly AlertsSystem _alerts = default!; + + private EntityQuery _effectQuery; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStatusEffectApplied); + SubscribeLocalEvent(OnStatusEffectRemoved); + SubscribeLocalEvent(OnEndTimeUpdated); + + _effectQuery = GetEntityQuery(); + } + + private void OnStatusEffectApplied(Entity ent, ref StatusEffectAppliedEvent args) + { + if (!_effectQuery.TryComp(ent, out var effectComp)) + return; + + RefreshAlert(ent, args.Target, effectComp.EndEffectTime); + } + + private void OnStatusEffectRemoved(Entity ent, ref StatusEffectRemovedEvent args) + { + _alerts.ClearAlert(args.Target, ent.Comp.Alert); + } + + private void OnEndTimeUpdated(Entity ent, ref StatusEffectEndTimeUpdatedEvent args) + { + RefreshAlert(ent, args.Target, args.EndTime); + } + + private void RefreshAlert(Entity ent, EntityUid target, TimeSpan? endTime) + { + (TimeSpan Start, TimeSpan End)? cooldown = null; + + // Make sure the start time of the alert cooldown is still accurate + // This ensures the progress wheel doesn't "reset" every duration change. + if (ent.Comp.ShowDuration + && endTime is not null + && _alerts.TryGet(ent.Comp.Alert, out var alert)) + { + _alerts.TryGetAlertState(target, alert.AlertKey, out var alertState); + cooldown = (alertState.Cooldown?.Item1 ?? _timing.CurTime, endTime.Value); + } + + _alerts.ShowAlert(target, ent.Comp.Alert, cooldown: cooldown); + } +} diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs index 5e20cea1bb..d508ea8b73 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs @@ -4,7 +4,7 @@ using Robust.Shared.Prototypes; namespace Content.Shared.StatusEffectNew; -public abstract partial class SharedStatusEffectsSystem +public sealed partial class StatusEffectsSystem { /// /// Increments duration of status effect by . @@ -103,28 +103,22 @@ public abstract partial class SharedStatusEffectsSystem /// public bool TryRemoveStatusEffect(EntityUid target, EntProtoId effectProto) { - if (_net.IsClient) //We cant remove the effect on the client (we need someone more robust at networking than me) - return false; - if (!_containerQuery.TryComp(target, out var container)) return false; - foreach (var effect in container.ActiveStatusEffects) + foreach (var effect in container.ActiveStatusEffects?.ContainedEntities ?? []) { var meta = MetaData(effect); - if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) - { - if (!_effectQuery.TryComp(effect, out var effectComp)) - return false; - var ev = new StatusEffectRemovedEvent(target); - RaiseLocalEvent(effect, ref ev); + if (meta.EntityPrototype is null + || meta.EntityPrototype != effectProto) + continue; - QueueDel(effect); - container.ActiveStatusEffects.Remove(effect); - Dirty(target, container); - return true; - } + if (!_effectQuery.HasComp(effect)) + return false; + + PredictedQueueDel(effect); + return true; } return false; @@ -138,7 +132,7 @@ public abstract partial class SharedStatusEffectsSystem if (!_containerQuery.TryComp(target, out var container)) return false; - foreach (var effect in container.ActiveStatusEffects) + foreach (var effect in container.ActiveStatusEffects?.ContainedEntities ?? []) { var meta = MetaData(effect); if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) @@ -157,7 +151,7 @@ public abstract partial class SharedStatusEffectsSystem if (!_containerQuery.TryComp(target, out var container)) return false; - foreach (var e in container.ActiveStatusEffects) + foreach (var e in container.ActiveStatusEffects?.ContainedEntities ?? []) { var meta = MetaData(e); if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) @@ -188,7 +182,7 @@ public abstract partial class SharedStatusEffectsSystem if (!Resolve(uid, ref container)) return false; - foreach (var effect in container.ActiveStatusEffects) + foreach (var effect in container.ActiveStatusEffects?.ContainedEntities ?? []) { var meta = MetaData(effect); if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) @@ -218,7 +212,7 @@ public abstract partial class SharedStatusEffectsSystem if (!TryEffectsWithComp(uid, out var status)) return false; - time.Item2 = TimeSpan.Zero; + time.EndEffectTime = TimeSpan.Zero; foreach (var effect in status) { @@ -228,7 +222,7 @@ public abstract partial class SharedStatusEffectsSystem return true; } - if (effect.Comp2.EndEffectTime > time.Item2) + if (effect.Comp2.EndEffectTime > time.EndEffectTime) time = (effect.Owner, effect.Comp2.EndEffectTime); } return true; @@ -249,7 +243,7 @@ public abstract partial class SharedStatusEffectsSystem if (!_containerQuery.TryComp(uid, out var container)) return false; - foreach (var effect in container.ActiveStatusEffects) + foreach (var effect in container.ActiveStatusEffects?.ContainedEntities ?? []) { var meta = MetaData(effect); if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) @@ -273,7 +267,7 @@ public abstract partial class SharedStatusEffectsSystem if (!_containerQuery.TryComp(uid, out var container)) return false; - foreach (var effect in container.ActiveStatusEffects) + foreach (var effect in container.ActiveStatusEffects?.ContainedEntities ?? []) { var meta = MetaData(effect); if (meta.EntityPrototype is not null && meta.EntityPrototype == effectProto) @@ -293,7 +287,7 @@ public abstract partial class SharedStatusEffectsSystem if (!_containerQuery.TryComp(target, out var container)) return false; - foreach (var effect in container.ActiveStatusEffects) + foreach (var effect in container.ActiveStatusEffects?.ContainedEntities ?? []) { if (HasComp(effect)) return true; @@ -311,7 +305,7 @@ public abstract partial class SharedStatusEffectsSystem if (!_containerQuery.TryComp(target, out var container)) return false; - foreach (var effect in container.ActiveStatusEffects) + foreach (var effect in container.ActiveStatusEffects?.ContainedEntities ?? []) { if (!_effectQuery.TryComp(effect, out var statusComp)) continue; @@ -338,7 +332,7 @@ public abstract partial class SharedStatusEffectsSystem if (!_containerQuery.TryComp(target, out var container)) return false; - foreach (var effect in container.ActiveStatusEffects) + foreach (var effect in container.ActiveStatusEffects?.ContainedEntities ?? []) { if (!HasComp(effect)) continue; diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs index 6d97a75edd..224a3dc4a4 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs @@ -3,20 +3,20 @@ using Robust.Shared.Player; namespace Content.Shared.StatusEffectNew; -public abstract partial class SharedStatusEffectsSystem +public sealed partial class StatusEffectsSystem { - protected void InitializeRelay() + private void InitializeRelay() { SubscribeLocalEvent(RelayStatusEffectEvent); SubscribeLocalEvent(RelayStatusEffectEvent); } - protected void RefRelayStatusEffectEvent(EntityUid uid, StatusEffectContainerComponent component, ref T args) where T : struct + private void RefRelayStatusEffectEvent(EntityUid uid, StatusEffectContainerComponent component, ref T args) where T : struct { RelayEvent((uid, component), ref args); } - protected void RelayStatusEffectEvent(EntityUid uid, StatusEffectContainerComponent component, T args) where T : class + private void RelayStatusEffectEvent(EntityUid uid, StatusEffectContainerComponent component, T args) where T : class { RelayEvent((uid, component), args); } @@ -25,7 +25,7 @@ public abstract partial class SharedStatusEffectsSystem { // this copies the by-ref event if it is a struct var ev = new StatusEffectRelayedEvent(args); - foreach (var activeEffect in statusEffect.Comp.ActiveStatusEffects) + foreach (var activeEffect in statusEffect.Comp.ActiveStatusEffects?.ContainedEntities ?? []) { RaiseLocalEvent(activeEffect, ref ev); } @@ -37,7 +37,7 @@ public abstract partial class SharedStatusEffectsSystem { // this copies the by-ref event if it is a struct var ev = new StatusEffectRelayedEvent(args); - foreach (var activeEffect in statusEffect.Comp.ActiveStatusEffects) + foreach (var activeEffect in statusEffect.Comp.ActiveStatusEffects?.ContainedEntities ?? []) { RaiseLocalEvent(activeEffect, ref ev); } diff --git a/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs b/Content.Shared/StatusEffectNew/StatusEffectsSystem.cs similarity index 56% rename from Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs rename to Content.Shared/StatusEffectNew/StatusEffectsSystem.cs index 9e6ed4d7ff..7f39a1f7c5 100644 --- a/Content.Shared/StatusEffectNew/SharedStatusEffectsSystem.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectsSystem.cs @@ -1,9 +1,7 @@ using System.Diagnostics.CodeAnalysis; -using Content.Shared.Alert; using Content.Shared.StatusEffectNew.Components; using Content.Shared.Whitelist; -using Robust.Shared.GameStates; -using Robust.Shared.Network; +using Robust.Shared.Containers; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -13,15 +11,12 @@ namespace Content.Shared.StatusEffectNew; /// This system controls status effects, their lifetime, and provides an API for adding them to entities, /// removing them from entities, or getting information about current effects on entities. ///
[ByRefEvent] public record struct BeforeStatusEffectAddedEvent(EntProtoId Effect, bool Cancelled = false); + +/// +/// Raised on an effect entity when its is updated in any way. +/// +/// The entity the effect is attached to. +/// The new end time of the status effect, included for convenience. +[ByRefEvent] +public record struct StatusEffectEndTimeUpdatedEvent(EntityUid Target, TimeSpan? EndTime);