From c4896266d2c7c554a88a06d38610d3648f916657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E4=B8=83=E4=B8=8D=E7=94=9C?= <1633557643@qq.com> Date: Sun, 30 Aug 2020 15:34:52 +0800 Subject: [PATCH 01/23] Add Schinese icon --- TLM/TLM/Resources/RoadUI/sign_stop_zh.png | Bin 0 -> 7239 bytes TLM/TLM/Resources/RoadUI/sign_yield_zh.png | Bin 0 -> 10486 bytes .../TrafficLights/light_counter_zh.png | Bin 0 -> 4285 bytes .../Resources/TrafficLights/light_mode_zh.png | Bin 0 -> 5325 bytes .../TrafficLights/pedestrian_mode_1_zh.png | Bin 0 -> 4879 bytes .../TrafficLights/pedestrian_mode_2_zh.png | Bin 0 -> 4947 bytes TLM/TLM/TLM.csproj | 8 +++++++- TLM/TLM/UI/Textures/RoadUI.cs | 4 ++-- TLM/TLM/UI/Textures/TrafficLightTextures.cs | 7 +++++-- 9 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 TLM/TLM/Resources/RoadUI/sign_stop_zh.png create mode 100644 TLM/TLM/Resources/RoadUI/sign_yield_zh.png create mode 100644 TLM/TLM/Resources/TrafficLights/light_counter_zh.png create mode 100644 TLM/TLM/Resources/TrafficLights/light_mode_zh.png create mode 100644 TLM/TLM/Resources/TrafficLights/pedestrian_mode_1_zh.png create mode 100644 TLM/TLM/Resources/TrafficLights/pedestrian_mode_2_zh.png diff --git a/TLM/TLM/Resources/RoadUI/sign_stop_zh.png b/TLM/TLM/Resources/RoadUI/sign_stop_zh.png new file mode 100644 index 0000000000000000000000000000000000000000..d223dbd39892ce729ba83ea576444d0573ed7360 GIT binary patch literal 7239 zcmbVRc{o(>+qWBJn>AuywLSUei6ASDNPkwGB9ii%PQc?Cs9gd|v21|cggLp>GY2zgZnMO9fP z@ZSfNstxbzrfR8Y@V72%O9SdbAmCJ`rGtWkq=Mw6uy}WAgo=vF9~rW;a4H4dKLkT? z4u)g=h5wPDhxT{Dd*TS5SPb}&L}wH>fS>`TBK?;MzBp6Ue+$O=|LrKM%cO&yancAW z8EIeNKX&~^?N6{o|2K^PNbPSGf_OS6^*Jn-jg~g z&OUlrmjGWhhG3+p0i`~Xa`kjo)t8Y|R8o{hsL1Fk=xZYo%K9>T$_mQ*+KTeB@^Z@Z z|H$}Hys|R-^0Ej$Jv}8^Jp@8uTU%F2S4Ur4MGv7ZtAfy1{)g8H<4h>q*u7 zH?Qvh$*Zc1M>`X+cq=T{=bs3$@W2wV{vKEySof+T_@a#`#uXdnFZO46{$;Hm8t)m1 zb~V6beZhbESJm@B=vOgNl$Do9D9TdN|7EwVioA-pGD1#IAA!)3Q-uD_>-zuknKTuQ z^q=7PAHniZ7c~O^RR10N)W*Lj4;n+AF?j0K;JWs@j)sOW$VgAyDtLN5k0sb@fTQwzjswM5?h%`0l2BE(*Y0LZ5=rH=GJA)l**k z`qfWS!ox_a^(U%cW+rIiuCB7h`4>e+7se}`v|?hO5f4XAr1k5?qf5%m-@g=DsBu$a zXbWDA6N>PV5*JVW)s+8%zE;91l0;52=MhWm@`@b2B}(g#Acx(X$GjmybX#uiZ{$gZ z{kCCfWIjEJM(a2E~S3jZA! zFJDx~aR5k7lT68zGOS}E0p2S+rbZ*7wgE|7G{U6I`e!9NGjzS`N-Tq?RRZ&u zE#A%w05lvAO=i}4zecKg6K~cf_GQ5$0!v$41(9s0XPt!v+iQj#YruaNL1qES)gQs`^oXqfNOoQ!F-mug2RLWNf^Y>Ia6=KHe(c6 z3dgxao4YG%;AE)vpzl8Rx>K{ zzz)G;N`WWyE90uuFyMq2vzFmw=_OQO7!Q*ShcLzV869IB%$UH@oAZyZ2bxT@=h?6% z!>X^@YCcgh>{@?+YFm(OIbI}j4ZL7Qq*qcafX=`@Dezi*Ry2@nz`DSddBY1`-*CAz zMHx&5!?fWC>HShvdY2}OB5u_L?j@K&G_T?ey$0nNXG?1C-}Z#4MK*V3=qxWU^RvyU z#4eCpDGBYB=c})+k}5B`_dMS6zsRQu79>#W{<8(%Lj<`LJ38M18Gjtp0o3nC|*XzzyRkZQq%LM=`^it6; z1<_@b6OjUSnCMSwZU}+27auI+ud;GX)9s{csng_bG$?%Df2~kE(y}YzF}=gBJSXvd zR0Lu{u-}uJ`h5dN^X|Oz(;7KjdN>?6pLh;DKJ%ov{YI~;4U-5RUO-9hjCm{L%Z|cC z6)p>}p+X@g(i-u4O*$mmUT7;_NnZ13E&=d~MN@N|z7XUw=O=Gz} z@w~nIfX1H~eE$(K&0-x zRdI8{DbgL`l3zRvq(^0zezEG;*pTx@+5r`Z!q4_sZUmiqRsO!a%g*oYX`^EGgy@9+ z$KtRm+JpgXqqX1{DBtvNsS!KTT~`%wi;;6z5h~Xmf}(cYgLYi?Jn>hMCg3QTJ=&N2y8K3EvGwvDhQNRSW7#(cbpxGhJ zb)Xdg7g<8$um7a-YUZla&;fTM7c--QyIKn{5BdKcO*-y=qSzi)fc zkx)^?VU&s%Yw2~(foXuBw<(x^EOT+4bW{l6LktyOmf-6F!Hh6Xz*FS&EyFA4+NfIw zFC{gR(#6WB%dZhUQJ?o0n{CKvr8P=p7}9fc+NRl`+q9PJ%s-k+eQ8_S0X)S-P6)9` z{wdb;^hSv?>v3P2pVQc@#Q=bEu7wQV!~0vhj3Vvx(>f^jjI9t8 zHijI~;(pI0lq^#=rzxH?jcNf=`5=pcTfTG&Y)RukLI%Vujo1x`&gf`(yv{d4|q|j zb}ZiYiy1lN(yni?Re=nm6#PmJacHeG(Snn=wYX? z7%>K!$6!v>gxwxrFDr2y41E!?Wi)u>?C>Gu6kGn!qOZk29q^(vfEz-&mnnYV_mx^-0~9-yT^Zu6d!FkG*=%y(s(Kzu`hISUvq)yvR3P zIwtq{Z1l%&9`MR=tGT~Ydw3zRWM#yr)AhEiDqTlvQo7c%%_3q@UczE7|7$OghmQ_& zoGa5e*6b?Q9cV(ogA*sT%*x#!Ec=Yy82VJ<9udf{-ezfnn+v1{trrt3pAhtcA(DFQzb%($a&rW+~ z@4nbTbos8^^Kr$MfjaYc|Mdn{;?k-b6JqHz7;LVcN2>Dh_22NU}zDT89TF1Z}9%T&w2w7#6qO&1BHU;g{J)ezi6e z&CHG~t za9bmDXiFW)=j`6bmO@vby!yTpdOk~@x9@n1BsKiIr)FTbslC|lenFB5TPn-dH{XvI zlFP_zHn9MWBj$Gw5uq%Ehx=+V!n)%8YWC{cY_usZQRi~zd&>4BSAJ5&W-_AV@>grW zCmx#V`a!lX%xFHmV)NE)g~Q;&T3UjLiu!8Am!0vjt;e2pP66!lzEdx+pR)1;x%=GF z{|c3mV?3np^YZaCH3Ns`!lL#E>rax>8xI^ViGtNX=J@M}4*U-ISbXCi;u6xf$yrl~ zL+65vX&gPRGaHu{2OTSI!Y{qu5I-&pfZ=7fC+)vk#fS6?3Dx{`K;6(yzx3_xq8j15 z+*s4lC9F?OV)iZ$%#f`b6jeoD5&bSm|4gHsi)5=LDbdKQ*d^khe)|r+=tZ2}<0UxY z*$w`8-H9P&T#C%o)9<{rky%w>iXtfHvRPLyh#`jmQ;PSPtFT)MdFCb(X`51c@Jktx z{V4uH9lRU-nm)U!h$j9WOu&eP_qVC2%9HfUagMhgbTW@^Sc^u1GMQ6v{C9Y) z%(9Ei;zSllA{#TS&%$_sYT1O753)T6t2Emt8B%%O_g&BI1w(kOum!S|Q;xQ-EAE^1 zPQ*{etnFyP+AOsb#iFnxv$7YELkq?7WL&ze(-4eRQ)L^Z@2ArK)^{D z=emo5Q79+nlug^30z-^QUT!5ati-M<(CFv%*d`{X$YwHOB`3e*)BZ&UklCy6-_qLc zL-V~pK6$U93;^gt@2v~k5;%%nS3yUgRG zSLXL~8U?R28$C753>i&7&b{-YfQw-U1E^~N8y*MT0`tAIjt|qg?xk(L-0NM~6ZkIH zNGNozSe3C{cUx3Otaf40xAd1fXV`nr^g$+v4BOQKcXXuoPgVCrXI4fKhqA1TR723Z z4$|VhiBAI@iCP=0uH{P!8&?wFX>NL52lGUNoNm)!+b_a;%VeTybv?Pa^nY;?YKSK% z$zE#_P_S_WzdLCDm(P98mqq>b)YmZ061@u-4|NIU`CoSZ%SuVw4!P+Y76vwYRgXc> zbp6C=P;H-hU+syzY~--lEj2^&r<9D5y=l+fgI>+4T+svKkkFUE?PmALtnqP zU*ApVB3a}<(pzE%!!BxcKGJw z^{&4pZFP$(`A)-hbR43l(Kqm;I)>XAraCfmHt)=va1EW)aVCO18#mU@y4*xE)zarQ=1O&Nht|b?m)y@A*Wq{v_LSaWp_v|m*SJ`Y_yIF{JI4XVk23V zXd>}Llk)=?CXPR@BRcavmSl1%tBEQ-s&RY7!);q!7rs(8%2m^Z0#^~$+iqOTt?J9Z zHxlyWi2opbX-|BRdC92#U5Z2cDR4^U!C_3Kng|8%$OJCQtLoe4Kbmb_6Y%YX$T_65 zf>THX!Wl6?s(3!89_kc8obqBjNLzYZjZ2Mpk2E(C-vT1!pFv<~T2CVJC%$`b|z#IGY;NZwa&qrlWu=&uawmJ|!Q& zSZ&6hZnSwPCO;b>cUOHOw0eRS#kiMt2|(ZEO+U0imzbO*q)hO6<4V z(4l)=5i0qkZ2zSPOoiuxy_B+3O%5%uEd;^VrE5yBop-IfBLdr(qsB@d$wYNsV(yuS zF{_LY%91HTVCVw&`bjHFtNBM+0N0C>iPNA0SNa_SJ&?b_M8c$(1{PM=VjCq_${8QD zA2hFWEiYolTyf)A-uN9|F=Pix9fsUt5KC7gvkh%gt4zzDtiXbIO2|Dx>Uisl*yxXoG`!*9UW`j-@D4mKLHNOOFTo z!^->aNeUU+4EPOo8uzlXGtjAti;EvRg>84wZ+{~D-{Jdi)-uTSONk@)Gc}WyU%>4S z=qT{`WoWtmvSYHeF+=AfdRDv`R|BULXN_Crg>|I*a5@0rNaEk}IiPx>PRH!J3?^0Q z;Rze_Up@->7y4D7mLtYMieJ9mVIu9gIhwdN;?4Q;tICDw zQo(EIIK*kF3B*f$sSOev0G0zp+@8;bPVEBM3P|J=*~|oDwSzMpu-3-}KB49Ck~Fls zoT}U$i(t8xp^N$zZ%-+)UAzdoI)GV~`)rHU9nh|2OsIm5Rypvc$*?ioUF1cXc`fAg zl4M|6v!gA`fOn~7sNU|x!1_YprjERXy)F=Ttde3h_Q!oS*rpsAXFBq37@bMf!*Z+W zXxGw0#w7^v{7pJie3sq-Cgo2O)cc}a{Wz|aZ`itW4W-}g;7<*%RvG7do&n4|0e_40 zBsp*GCv-sr7zhYpNM|V1te@a8k+e8yw#qd;AO6OFMvSHB)|FO@QRW||uy>XKr5n_g zfq|mh>O_>DJum?tyFO)kk-9J2T>9ZpwSo#x_QD%e8-9)+e#&|bG536fi%G%d_JVgl z*ChP}n&Q5(S5HJY-zuYsmU?FYA1>$^b+(E4UAV!bYEIC!Ljci--l$CN!Bn zeW%>HxzE|Tn%DHw`m1$j|M$D^7r?)7(=yQn=2z0ea#_0n{Ms|pH`6QExf%6eL;eAk literal 0 HcmV?d00001 diff --git a/TLM/TLM/Resources/RoadUI/sign_yield_zh.png b/TLM/TLM/Resources/RoadUI/sign_yield_zh.png new file mode 100644 index 0000000000000000000000000000000000000000..7f6ef7e35c9a9a080e040055498f7959f90eb7c2 GIT binary patch literal 10486 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 E05(;--v9sr literal 0 HcmV?d00001 diff --git a/TLM/TLM/Resources/TrafficLights/light_counter_zh.png b/TLM/TLM/Resources/TrafficLights/light_counter_zh.png new file mode 100644 index 0000000000000000000000000000000000000000..f80b490819b0d52f39b0be5dad25ea47d15e6ee9 GIT binary patch literal 4285 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 acmcfypM>CL_AS4EFq#@!8de#2#r+Qgse7CN literal 0 HcmV?d00001 diff --git a/TLM/TLM/Resources/TrafficLights/pedestrian_mode_1_zh.png b/TLM/TLM/Resources/TrafficLights/pedestrian_mode_1_zh.png new file mode 100644 index 0000000000000000000000000000000000000000..91c921dace225c2d4322e19fdce286eb81c83f72 GIT binary patch literal 4879 zcmbVQc|4R|`yczhMUt%{5@9wBlYO5U`w}SzgTYv4#=eIVjcrsES&L*JTh>sPB818o zvSlw@(L?X_JWtR2`{Vh%fBf#xec$Ig=f1A%du``)&be=ynHX@e3bFzK01l*~t_8J4 zQXhS0M(R6S@)--YIZf2NOtkcMBL<=I7=V_muQLXS#G&0W78tZ^h<_hO6#$@f!&+S? zUPc)sTzqlz=wCeY!8kvvGytHg5$uO{@xl;+&KP&BkDB;abGtYY>#8PhtB3-l{B$rL zSi?{}#xm5z$|cmxMa5NILmj9ZjG!vOVTfp8FwWbDfCyF-|5Go5O8*)Li39&+A$qBa z|7Fx=lo?RR7mood%ERPbzz_&fNl6}}ps1t-kp)7*5GV*ty_Mu33J66d1QZVZ`x2+3 z;a%Mj7P|U>V^Mo*;vPhz9|8ml3JQ`Bg30^h-9Zo)6_sB)pins~ha4fqhlmcA^C6u5 ztw9$>&n;ePq` zCpUp;f%&f)|H@6U3h~2$EHDJ$0K5w|A8u!V3sYnFzkz-+QmsLl;<41EpuKf{T>@|z zA0kp$O`Q5g-WBVLP}JAe1*<@zP%T}!o)!e6tpwK7)lq_I>*;Ch!gUmX>-aZWn1UW$ zS5bk=2i1W<^t4o9ih8;Vif}ks8?K=)VCLaV^d)%s`T=z;m4N4Mu|BT8K?Lbv>G{iBT?`%@h;h}&`{ICq#utJ8 z7kqsk6*v?E)rRW)<+qBWj+TlJTvEo?7_!fQV1$w@2TF;0) zM!hnAvyY(H%qL^s5QSf^Tn;FOj@Uo`1XjqAdAw>Ra>qWg7s#2YSzK86QY*xo$1_A9 zNY9+Qd~UpFy0!Rc(Eh`L>49_OlD7&d27~KMADfpS?&NI!Jl@I<8;;}7kj^?zKR~TwyXEf^6>O5AS}Nz$KmAa>g&@VV;8;#_ihAQ25sG}CtfXf0vp~y zr@me+XlYS>H8@yYRwgTR;X+|N+bPd`!iGD`GZ#38l{F&{ck{(`ZuAbu&@owzgoMjDjfPO^b|xnhiA0?1WbpLx$UAG0+{;okxk3D~ z(O?;JaB1TT!%DZ%>lb}wDeRkU6$Q6`fz`=|%JuB7xm3EE>-ovC7y*T2o4oT*emOH+ z_m-`!0Z$_fNPfz)lP~?jEIp%&%1pCu$A?}M^*A9B_3+H@lryH4E!*xJLJTWyKloXQ zPJQW;HFWB^rf)@ZOVz?lO97f%y=cu+8K6H$>H9U(v_Xz#Uf%UvHfCd6qd{@k2m}iK zDP#5EFTmEX-9mh<@gBnkdb^(s+ebr1inL-GlX%YR57BX{0=roG40<}a0rAcpOc!{| z`YmED=Q5bHOW&F4FpOlI6qh-rI+r+Gp-`k2c{tpbHa2l9Y=1pJF_E=t@h+q`@+h?Y zy{4d&cV;FOBRG()V#�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 literal 0 HcmV?d00001 diff --git a/TLM/TLM/TLM.csproj b/TLM/TLM/TLM.csproj index 03050fab7..f43d504f3 100644 --- a/TLM/TLM/TLM.csproj +++ b/TLM/TLM/TLM.csproj @@ -1,4 +1,4 @@ - + @@ -429,7 +429,9 @@ + + @@ -458,9 +460,11 @@ + + @@ -592,8 +596,10 @@ + + diff --git a/TLM/TLM/UI/Textures/RoadUI.cs b/TLM/TLM/UI/Textures/RoadUI.cs index 6ef014601..07556a542 100644 --- a/TLM/TLM/UI/Textures/RoadUI.cs +++ b/TLM/TLM/UI/Textures/RoadUI.cs @@ -18,8 +18,8 @@ static RoadUI() { PrioritySignTextures = new TinyDictionary { [PriorityType.None] = LoadDllResource("RoadUI.sign_none.png", 200, 200), [PriorityType.Main] = LoadDllResource("RoadUI.sign_priority.png", 200, 200), - [PriorityType.Stop] = LoadDllResource("RoadUI.sign_stop.png", 200, 200), - [PriorityType.Yield] = LoadDllResource("RoadUI.sign_yield.png", 200, 200), + [PriorityType.Stop] = LoadDllResource(Translation.GetTranslatedFileName("RoadUI.sign_stop.png"), 200, 200), + [PriorityType.Yield] = LoadDllResource(Translation.GetTranslatedFileName("RoadUI.sign_yield.png"), 200, 200), }; // delete priority sign diff --git a/TLM/TLM/UI/Textures/TrafficLightTextures.cs b/TLM/TLM/UI/Textures/TrafficLightTextures.cs index 382ead147..3ab7754f2 100644 --- a/TLM/TLM/UI/Textures/TrafficLightTextures.cs +++ b/TLM/TLM/UI/Textures/TrafficLightTextures.cs @@ -101,10 +101,13 @@ static TrafficLightTextures() { // pedestrian mode PedestrianModeAutomatic = LoadDllResource( - "TrafficLights.pedestrian_mode_1.png", + Translation.GetTranslatedFileName("TrafficLights.pedestrian_mode_1.png"), + 73, + 70); + PedestrianModeManual = LoadDllResource( + Translation.GetTranslatedFileName("TrafficLights.pedestrian_mode_2.png"), 73, 70); - PedestrianModeManual = LoadDllResource("TrafficLights.pedestrian_mode_2.png", 73, 73); // timer ClockPlay = LoadDllResource("TrafficLights.clock_play.png", 512, 512); From 57b3a44e1f4ace29ec343a543f0bf6c0c3b27956 Mon Sep 17 00:00:00 2001 From: egi Date: Mon, 25 May 2020 02:35:08 +0200 Subject: [PATCH 02/23] - no need to allocate a new List each time if we can work with locks instead. - use of lock statement instead Monitor directly. - removed commented out debug.log calls. - formatting (cherry picked from commit c878dd26c2d04de71e8dbf109883e83bef19a5f0) --- TLM/TLM/State/GlobalConfig.cs | 4 ++-- TLM/TLM/Util/GenericObservable.cs | 40 +++++++++++++------------------ 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/TLM/TLM/State/GlobalConfig.cs b/TLM/TLM/State/GlobalConfig.cs index 4a104d9ed..7377f5586 100644 --- a/TLM/TLM/State/GlobalConfig.cs +++ b/TLM/TLM/State/GlobalConfig.cs @@ -17,8 +17,8 @@ public static GlobalConfig Instance { get => instance; private set { if (value != null && instance != null) { - value.Observers = instance.Observers; - value.ObserverLock = instance.ObserverLock; + value._observers = instance._observers; + value._lock = instance._lock; } instance = value; diff --git a/TLM/TLM/Util/GenericObservable.cs b/TLM/TLM/Util/GenericObservable.cs index 0ea46eb77..58b283229 100644 --- a/TLM/TLM/Util/GenericObservable.cs +++ b/TLM/TLM/Util/GenericObservable.cs @@ -1,8 +1,7 @@ -namespace TrafficManager.Util { +namespace TrafficManager.Util { using CSUtil.Commons; - using System.Collections.Generic; - using System.Threading; using System; + using System.Collections.Generic; using TrafficManager.API.Util; public abstract class GenericObservable : IObservable { @@ -10,12 +9,12 @@ public abstract class GenericObservable : IObservable { /// Holds a list of observers which are being notified as soon as the managed node's /// geometry is updated (but not neccessarily modified) /// - protected List> Observers = new List>(); + protected List> _observers = new List>(); /// /// Lock object. Acquire this before accessing the HashSets. /// - protected object ObserverLock = new object(); + protected object _lock = new object(); /// /// Registers an observer. @@ -23,33 +22,26 @@ public abstract class GenericObservable : IObservable { /// /// An unsubscriber public IDisposable Subscribe(IObserver observer) { - // Log._Debug($"GenericObserable.Subscribe: Subscribing observer {observer} to observable {this}"); - try { - Monitor.Enter(ObserverLock); - Observers.Add(observer); - } - finally { - Monitor.Exit(ObserverLock); + lock (_lock) { + _observers.Add(observer); } - return new GenericUnsubscriber(Observers, observer, ObserverLock); + return new GenericUnsubscriber(_observers, observer, _lock); } /// /// Notifies all observers that the observable object' state has changed /// public virtual void NotifyObservers(T subject) { - // Log._Debug($"GenericObserable.NotifyObservers: Notifying observers of observable {this}"); - // in case somebody unsubscribes while iterating over subscribers - var myObservers = new List>(Observers); - - foreach (IObserver observer in myObservers) { - try { - observer.OnUpdate(subject); - } - catch (Exception e) { - Log.Error("GenericObserable.NotifyObservers: An exception occured while " + - $"notifying an observer of observable {this}: {e}"); + lock (_lock) { + foreach (IObserver observer in _observers) { + try { + observer.OnUpdate(subject); + } + catch (Exception e) { + Log.Error("GenericObserable.NotifyObservers: An exception occured while " + + $"notifying an observer of observable {this}: {e}"); + } } } } From d0633b44b30e389763e51ac5fce09435c672ab86 Mon Sep 17 00:00:00 2001 From: "kian.zarrin" Date: Tue, 2 Jun 2020 09:45:00 +0200 Subject: [PATCH 03/23] fixed stay-in-lane for MOM tracks. (cherry picked from commit 4d281c850c2422d6ee716bcc480d734d3e502f3b) --- TLM/TLM/UI/SubTools/LaneConnectorTool.cs | 28 ++++++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs index bb8a1a0cc..483c08188 100644 --- a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs +++ b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs @@ -553,7 +553,7 @@ public static bool GetSortedSegments(ushort nodeId, out List segments) { /// /// /// determines for which side to connect lanes. - /// false if there is only one incomming/outgoing lane, true otherwise + /// true if any lanes were connectde, false otherwise public static bool StayInLane(ushort nodeId, StayInLaneMode mode = StayInLaneMode.None) { Log._Debug($"Stay In Lane called node:{nodeId} mode:{mode}"); LaneConnectionManager.Instance.RemoveLaneConnectionsFromNode(nodeId); @@ -629,12 +629,12 @@ private static bool StayInLane( // count relavent source(going toward the junction) lanes and // target (going aginst the junction) lanes on each segment. - int laneCountMinorSource = minorSegmentId == 0 ? 0 : PriorityRoad.CountLanesTowardJunction(minorSegmentId, nodeId); - int laneCountMinorTarget = minorSegmentId == 0 ? 0 : PriorityRoad.CountLanesAgainstJunction(minorSegmentId, nodeId); - int laneCountMinor2Source = minorSegment2Id == 0 ? 0 : PriorityRoad.CountLanesTowardJunction(minorSegment2Id, nodeId); - int laneCountMinor2Target = minorSegment2Id == 0 ? 0 : PriorityRoad.CountLanesAgainstJunction(minorSegment2Id, nodeId); - int laneCountMainSource = PriorityRoad.CountLanesTowardJunction(mainSegmentSourceId, nodeId); - int laneCountMainTarget = PriorityRoad.CountLanesAgainstJunction(mainSegmentTargetId, nodeId); + int laneCountMinorSource = minorSegmentId == 0 ? 0 : CountLanesTowardJunction(minorSegmentId, nodeId); + int laneCountMinorTarget = minorSegmentId == 0 ? 0 : CountLanesAgainstJunction(minorSegmentId, nodeId); + int laneCountMinor2Source = minorSegment2Id == 0 ? 0 : CountLanesTowardJunction(minorSegment2Id, nodeId); + int laneCountMinor2Target = minorSegment2Id == 0 ? 0 : CountLanesAgainstJunction(minorSegment2Id, nodeId); + int laneCountMainSource = CountLanesTowardJunction(mainSegmentSourceId, nodeId); + int laneCountMainTarget = CountLanesAgainstJunction(mainSegmentTargetId, nodeId); int totalSource = laneCountMinorSource + laneCountMainSource + laneCountMinor2Source; int totalTarget = laneCountMinorTarget + laneCountMainTarget + laneCountMinor2Target; @@ -779,6 +779,20 @@ bool ConnectToMinor2(int sourceIdx, int targetIdx) { return true; } + private static int CountLanes(ushort segmentId, ushort nodeId, bool toward) { + return netService.GetSortedLanes( + segmentId, + ref segmentId.ToSegment(), + netService.IsStartNode(segmentId, nodeId) ^ (!toward), + LaneConnectionManager.LANE_TYPES, + LaneConnectionManager.VEHICLE_TYPES, + true + ).Count; + } + internal static int CountLanesTowardJunction(ushort segmentId, ushort nodeId) => CountLanes(segmentId, nodeId, true); + internal static int CountLanesAgainstJunction(ushort segmentId, ushort nodeId) => CountLanes(segmentId, nodeId, false); + + public override void OnPrimaryClickOverlay() { #if DEBUG bool logLaneConn = DebugSwitch.LaneConnections.Get(); From c052823b386e97b73a9a183e73d57474d52a3be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cham=C3=ABleon?= Date: Thu, 30 Jul 2020 14:09:45 +0200 Subject: [PATCH 04/23] Updated icons for RoadSelectionPanel to optimized 8bit as the previous version causes sometimes at rebuild strange white pixel artifacts. (cherry picked from commit 4544d30fbb8a244f08ad7f1ef0a229a5a3e456eb) --- .../RoadSelectionPanel/Clear-fg-disabled.png | Bin 1727 -> 1848 bytes .../RoadSelectionPanel/Clear-fg-normal.png | Bin 1906 -> 1845 bytes .../HighPriority_LHT-fg-disabled.png | Bin 898 -> 1403 bytes .../HighPriority_LHT-fg-normal.png | Bin 830 -> 1427 bytes .../HighPriority_RHT-fg-disabled.png | Bin 896 -> 1405 bytes .../HighPriority_RHT-fg-normal.png | Bin 845 -> 1424 bytes .../RoundButton-bg-active.png | Bin 2581 -> 2592 bytes .../RoundButton-bg-disabled.png | Bin 508 -> 1564 bytes .../RoundButton-bg-hovered.png | Bin 2548 -> 2481 bytes .../RoundButton-bg-normal.png | Bin 2107 -> 2043 bytes .../Roundabout_LHT-fg-disabled.png | Bin 1241 -> 1496 bytes .../Roundabout_LHT-fg-normal.png | Bin 1088 -> 1530 bytes .../Roundabout_RHT-fg-disabled.png | Bin 1233 -> 1493 bytes .../Roundabout_RHT-fg-normal.png | Bin 1089 -> 1531 bytes .../Stop_LHT-fg-disabled.png | Bin 918 -> 1453 bytes .../RoadSelectionPanel/Stop_LHT-fg-normal.png | Bin 938 -> 1468 bytes .../Stop_RHT-fg-disabled.png | Bin 930 -> 1441 bytes .../RoadSelectionPanel/Stop_RHT-fg-normal.png | Bin 951 -> 1450 bytes .../Yield_LHT-fg-disabled.png | Bin 1025 -> 1432 bytes .../Yield_LHT-fg-normal.png | Bin 893 -> 1452 bytes .../Yield_RHT-fg-disabled.png | Bin 967 -> 1428 bytes .../Yield_RHT-fg-normal.png | Bin 904 -> 1446 bytes 22 files changed, 0 insertions(+), 0 deletions(-) diff --git a/TLM/TLM/Resources/RoadSelectionPanel/Clear-fg-disabled.png b/TLM/TLM/Resources/RoadSelectionPanel/Clear-fg-disabled.png index e1cc1a312cee07fc7c00d02597f18e0b105f337c..33a10fb323f77c5062af3ab591ae41be39d088a3 100644 GIT binary patch literal 1848 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 delta 1504 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 diff --git a/TLM/TLM/Resources/RoadSelectionPanel/HighPriority_LHT-fg-normal.png b/TLM/TLM/Resources/RoadSelectionPanel/HighPriority_LHT-fg-normal.png index 5a3bdc314e9108457bbd25d22c193368f9861f3e..e1533e941b86085af8f6c6f5fabd53cdcc5d2ca0 100644 GIT binary patch literal 1427 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxK#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvNA9*GX(gAxB|s_czCR=tgNlA9UUD#Jw3g=yb===lai8>lao_YQZh3$v$C>E zOH0ej$|@@>tE#H1tE+2jYHDk1|8LR$zs;brv9Y$Yv%{@SHvfOJ<;amEM~@yoapJ`7+qduEzyI>(%U7>ny?*`r z|Myq_f4u(x^WFcSAO8RP^#Avl|G&Tf`t|Gg@8AFb{`~*{KhR&JU^D~>hJZki_%vWX zVJHdm3kD{ekqj7uzq6|X1B|o4BeIx*f$ty)Gwzs}{T3KN0-i38Ar}5`C*2G?WFX)m zC=zpHiiTKZm%;z(UbL$-8il>eaY0V9LWlMXPD-D^=~}CvbOW~K3V3- zKDp%gdrG>pKZFFByXw!IS=+2`9rxgyeEF6&p3YyTp9*hzB)D6agFoxl^+ZG7sYe9n zHvZl>`P-+;m|X{*k~&_sO_^u^_sXIv-X?*+uPEKHJIwOo^^0$Rp1&9Lt9QO);4-7i iI&c&JB7Jrhu8-V|wSIRwNRs6bc25*ng1deT(RD z`$ET$h<<*ea=FaY{S^5(tsblr8xo8@#|#Do3Iqa*Zf^XY=)<9C&eX|cePnqggTWwA z_cc5&_+g0+gK2Fi;&gplnQ7)s9Y5658jS`&c!cWY`-FxBqt7usf*28{SS(@$zro}S zxjAtJpW{zm$K!$@me`nrHGjv6zzytP%NkTgb3AfbgOy5!*C1~cXKPRo*qGPPyO?*g z(X{66B$LUDU3_Bh;gWcBFA4XsN3Owvz!oE>T5!lvNA9*GX(gAxB|s_czDds%`Ge}92^`R9UY^hqGDrXR> z^78WY^9u?J3JVL1i;GK2N=i#hD=RCjs;a81t7~d%>g(%gHw4aZ3hwIa>hA7dHnU;* z%%Pbe*O9lYgcYq2SlqktXs2j!`h7- z)@|IdVe{tAM~@yob?VgV)2D%M8wH~wK+_Nq=Ve?6%qI*bL4LviM>b#x{?4un3~bH< zkH}&M2EGFz%$RkplM^WT%hSa%#KQmWq}!9440s%_Yrojk{b=i}b&viw&3g5}K6-oZ zr67rvnvyxT`koJbJTmN$O#aht*`V~PN$I|=>OBjGnGp*$9DpZ;{+4JMBot{MU>LaK7(iAW9 zPl~AO$<>S7ado%CkEo_3Wu~kCvoCl(FI&()@8u4=-*YGRz3bWHEBAY5T)~gIN77!) zUO%Wf_qq0@|ArhJ86K3M;W_u<-w&$=lQk`*+(o`dicVg1xXiV>`pMae0gfpy%|btW rxgIq}Y}@Mm@0muNug;@h7sY=JSxN0hw@&K&fYPC-tDnm{r-UW|hh^&W delta 857 zcmV-f1E&1_3V;WY83+ad004~sxNVUkAAbM|a7bBm000ia000ia0czHX2><{C6-h)v zR9HvtmQP62VHn4sFPik3yW2+cS%5@TB}zgHETj8}iECjL^t!R)MM8m7ut|_#XEg8X7cQ zB-~011Q3m23G@THAoY@fG28g~xUH$F>1Ev9Nf9wC9L+FD;c%FO!64zs$!fJyG#af$UyC&%NHWR5sDBQ?Xn$;Zd3guj zH}aG)M}^%8JwSSHA*B?FL}+z&b$sL4iDbJW7t8bPL|Kwzk$n6bglCZ7qFZ z*koa0A-P;GnxCKNp^S!^XzOn{ktgkxa1% zpF)2&X_h#uinpL|C=A{1?rw#}Vv(vB=Ny%mmP%T1$xG-NOs2w8^RR#tB| zoAp-#A3gGv5K1>R-z1N_y1HuX>gs4}YKlfjM`tm1ANs-zl1Y9+^9{pAgd1f}9#VwT ztIh}YxZUp0OG`_?VD2OOccDY(klMXe&wJd_{135&5P2+cikkcT`*+YO=%@pkWCi`rP6&zD5ZBlzIvX1Om_TlDX5?);5du3P&*O#M8@F40TI`XNgq%?<9z( j_|#Dis;H>^-w^!<3~wH|jP41S00000NkvXXu0mjf2Y{N0 diff --git a/TLM/TLM/Resources/RoadSelectionPanel/HighPriority_RHT-fg-normal.png b/TLM/TLM/Resources/RoadSelectionPanel/HighPriority_RHT-fg-normal.png index c7fce5a098dacddf806c9df1552450825b1a766f..e96f59d0831ac43c0ca17c3799e705a07e357d83 100644 GIT binary patch literal 1424 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxK#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvNA9*GX(gAxB|s_czCR=tgNlA9UUD#Jw3g=yb===lai8>lao_YQZh3$v$C>E zOH0ej$|@@>tE#H1tE+2jYHDk1|8LR$zs;brv9Y$Yv%{@SHvfOJ<;amEM~@yoapJ`7+qduEzyI>(%U7>ny?*`r z|Myq_f4u(x^WFcSAO8RP^#Avl|G&Tf`t|Gg@8AFb{`~*{KhR&JU^D~>hJZki_%vWX zVJHdm3kD{ekqj7uzq6|X1B|o4BeIx*f$ty)Gwzs}{T3KNyq+$OAr}5`Cq)JyG7xBB zJjiV0z!c=LvPq+(Nyh2vw}k)y^|?IDE-#aP@!^C)jsEW0b(?R_RXbGvM1zf?>x5j_ z;)i~VjM+GZ7mHYOI32lsu(PZ5CF7OQg@#A2oQ{9X6Z|sq*`v;z%PWgRPp-H#Ge`XM zw=Gj&>zsDgIeM*b%Zz>VYYmR=;$H2b&gkKD>-4q{60iS9h2PH-XR+rF=m~!qTY0Eu zllgW=*|-;dd*?qY^>9CzFPFCI`SU}YUZ^xwpS}C|i*H_R(A9z^O^xQ~b4%|<+|1Nr zP~%IJO8#*A`v2#}r)nQ-@=R3Umoy{i*Ue=w_Q`!LVhItj3sgS(@tdTrLP>V>gVPh= g%-+$-EMyta9Pww;Cy~4ZyFkgy)78&qol`;+0O?mTO#lD@ delta 806 zcmV+>1KIqL3(W?Q83+ad004~sxNVUkAAbM|a7bBm000ic000ic0Tn1pfB*mj;z>k7 zR9Hvtn9XYwK^Vq&y$OMw6+#Y;UKEv3NJ{7_3AD%j5BA=FB01%t_0Y6AD3}Na@=SI}>*&voYIkOQ89|!_K@jJAL}j*S2P}*)(PulYfXr zaW=Uoo-@nbU{K};gL0V$&vabOGZ>VsZLrm9twRk_JsW)0owsnmuXVfK4U{Dumrq~P z2E7DsLEoT|t{ui!`~ChZ$|~G0pT0Ep6q}|QkHup3{r&yxp-^ZK^{8laVuizDOO%}u zrdkwzqI0bZq!Nilr`>MTU@)M1y?;&%3kz)IjpseB=&yTc0r@(S1lWA2d z6{3?bMDLyu{W+nntu0zyT&$w~zWNS~KExV)jIzYl?nm076iB4g>Du=8Hqp;dMDI&P z)&rssFgy54#bS|`mX^xscTe{|jIKisUE8$M23g=PruF6K<|fgv&qRmMh<|o5qf*CB zWVBEDe4gU*cnSS^15J(N=eTfsu-nyu9W;y^(P*?~XAYU7qvvjOQXhAThCghRkw~Q6 z>-F%uFKkmP9G7xcMnS{qRI61K!QoTAIVm;?c)Cx=9v{z360f=)b5=${!?=kI?)*Q4 zuaLp-R45b({Vy+rEWm59R(~pCW7um@$>cHCzF33U&AbM0>&BTRFI;_`6;&Xa&1P%m za#`$RY#83fNB?$lDwT5WVhQhF-o>Ii!a2UPzhtak4X6UkIBIt6HEExNxMFb*^7JEf zFVGIhuKqSC+wghF=b{LY`b{fsP!&ielgW-{Su`9DsnKWt?_TiAmmFI@xK`!&=gpLPP{XB+5!JvzFPfY&o3~ z7DBj?krXRdh?q4WGOe_Za;i&DKc(95MPQ;D22zBA=XvQU3Uc^HDVLWaIA6+s{)gx0+Y&dP;!M2TZKth`*74CyMn-}0NE-6M}<#O6S-;< zS4B!ylX+?^M}tezVp4QIT#XM`hv#aDJPpXz5_noXPlw^^eb!gw)*CQ9Be-6V;~VjO zJyB2%@(l#R3?qrBq3~yD$$Xu!KM=O47Fcfr_+}u@f=;&}(oP}-rw{@wLU4P-MY~te8w2M9wskGR-ux z5iPa@Vh12@M2gR$vYL=tEl6oAD!T=cwIXG0D0wSVevv4)_=v4ou?;WzoseY(vup&J z%~xz9i_K)Q#aC*fN=$wdGcC(Rm74ve7Jr$UCbN*GR*K9@%C=GDHmcm}mt&(TY*>jM zBXQuMEC(j55hrT|v+SVML6F%AG6$I5NRT&TWlcWvCY-zlBX7meG>TTDq6x2PA!XYs zaywb>@KrRB6^&Gd-B015DIFw56J4mIi}ZnFeSp|Nmly-H41rljhSb228W}QUfXoz- zZJsAT7AQBH~3quZ>7&+!3#S9BmX`w5u^ORQl23vsA#!y-r8*R)@R=Uy-$lH{cf(-wEd5Ar?5vg?Gq> z{q&vvjG}(VuKu9nyUg9Lpxt*DKr`GSd+vtpaYG>`?$EvNuoCyo35*^_ABDj@UW;JSaCfIv2dnv%8pky}wNe3n&9i zx<77zTf^R1{O4!&^}m?EzHEB9V|VVgFZY-BJ$T@J_|U1$K6ma>=?gDSRNcODFDPYS z_2l8vM8#rU^Mm1qTFxn})#k!>EqT3Bapr1c>ZShi@hztD0*OFNsDq}_o9x}D)R6tA zw%EP+g)YG=pAQYd;^B?lOZ}~_t#9?>>V;g_D+;;2eRXt0M9}l$#;K{PH{VCQC3z#t z_#PI}5l3m;!MpIy z6szt$lbC=jKe%r6_UMyU0_c&(k63;f=kAj1M6~OgJ3qKj57ySYJ^P|XLWSBV%A=whUfvii{Qd0sr@E5^Cr+F{*IN^@bb$hTI^nUaKv}V( z!?o?_>&l%#2AR=;*<~&|l@MMhX$xPW(%07)p4-v&7`kC^yHFJB zJ|7rIaj}*cO-`yvi@uADWH4kDPydb%^BUE=4{uy|*7ml_-#uj(ymj_o-&m=>m7MuF zJh`oHRq-ybAAZ>O#<|?%H!qJlPo^iW(w8^UK%Z9>GlJz{uHmM zkbnZkmYkQPqp!c6h_G}RjiaN5g(Fnw(4*E8na{!c`+ltXVea0Ml&Yh?M+V9-OB((= zwIxOox56#`1U}!I+fmwk?&|oh4IL{TSYl+uKykvyxjzy{?%kX4zQ6x#vF)ZC0_nz#Iif2atfBRHU_mFa8zyK_HyA$TVhPV@J?kf>+Nvx4k^L@FDbYceyjc6Tg3I&UIQR zjIpPvEbe#tpz>6ivvasvse$A5iUK)+{mOA?8{%2hXd_HsEnIPab94ATdhY11BzGm) zJ$Y@mXt=XJ3DsX>?krY?3?;ul3wkpDes)(Tspe`Y=Igd>V^l0@k}w z(}8U><;%#c1(~U(@e?c-y?xJ_l((b+v_g^?7*dYm^Tb+~z;XS}R^d zP=&yR_xLeI)N{(K@*sdQuR_e>&H?u3I-*~NH@`c$@Zfe;x)Pa2v4DTvlC|b3%7+VX zuvQVmED~Uqa>%Qdp=k-H!RH10s15<@<~F!3Zj;;gG=fYH@$ny|&E`GlZ8<*bw%1YV zkP;>sgS`k$=#dT{je#l^{E)CF4n!T?XVd0>ES!U*2OfEmuKujruOJg1oasLFIOf$c zH@Zazi-LP;LT^2M+SGs2j09;$tYaIojnZ09>*lt&P0ZtC%)!$U2?Y5KkvMl+Hs9eJ zo&XjBF92ID2!<>O)z=UfM@_G`T2_EMcl7Wg+CvN8+552PW!Ru#j2^-DC|M1Q z59^TO(L97Ye05!1Mxk|T8*IyMGY96voS2&sOei^w74~kojk#B%C-OQQV{@)TO)E6b z8*YeedWtSCqlkagVkhx}gaf!FE;$?sVoUGg(W8>)q{~#6Bnf`>?N)7 zI^L|@)HIXc5h3VGw+$l(dH{ohd*Z?IDD8ZjUr(AJ;t@pW%V}K7`h3Z&r#ZxFoUaU6 z*m6cx5B8DMrxd#kdZ5F>6>D1KLGOW+ambrMoacc@$cycQ9-st=S~7fZk-JMp5VtQ_ zt@8arCoq5R4-&=`UE)5?c4DI_YVdlt(K>@|TpvPhi|vF1xKx}xL0k=2Ft|5NYYv7y z4=P~jB`u-Cr%MiF8;1UBd14!t4e)71s`dck04{}fD}f;2Tls2en%xnZ6dl3v!1WBC zH#`iQ4tX4Eo{!ftkr$T*WSFD0*c%)bzyU;GY2<&&%|MVZ)lgnRY?V51k)CLe1aa?U zJ)oZCutdI~mN$VZb>ShQ=^R8H*VBT2(f{@bu;=Y85*m*-uAsu}cJ=@{OjPY9PjrFy z)b}=y7jd44=xTOxzSgUw!)s~)rG_JG1;T3#Jmr|lgf)8605PI%6hX4%sS za@)my{PMHo{K#jnWvy0WXSxrXtlf6oeGtb>oO`SiM>$?OFhJ*79irzMo3cQBb^Ld`v5$*NM`oXIyBEDWBbkF^G65Lc1`MwZ&HrG-0I9{CvXHmMfqT{?Ei_} z#$Sm>)n}A&*xaQS=EIbP7fUuvX8wO3vfX?6Yr=I5^103a=s9}fk=s0{a%X~&_LIv^ zV_Qpx%xOyn?#!E6+5a<@Of(-<73@}vC&(X)tPpz#nXEIyjpP!%c@~G3<-vuef8+C8 zEa3xY;|yO7q3OB7T2>DJj_V>uhrbu{$4nM};WuVL2E^J2u?Y56A29JEC|q>@%zyL) zW?lG#pSJhm4s9OA^^iXS<8otq@Dz^dPENr{5243#Nj?FuU@rb1ufQwg!PE<8pvzliB0000< KMNUMnLSTX%nlNtw diff --git a/TLM/TLM/Resources/RoadSelectionPanel/RoundButton-bg-disabled.png b/TLM/TLM/Resources/RoadSelectionPanel/RoundButton-bg-disabled.png index 6aacb026a69d89a312a5816edc7cff53430a0918..a194720e1d9c255485a0c0ce2f979d3705e77712 100644 GIT binary patch literal 1564 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*HX>Ft%knI|q0=J1Ybf<)>xlqynYS zPn~G%aX3Jv)jxQvk|?{iN8yP8)`LweIu^TZuOb*>n-~y@%ns)=}zl} zBrL@O8rg1sZT^4o{mrPKD?Rl!H5l*e_kA{GVkv8MJ6pJ~Uv_yR(~tR$+Iyd_ztVWq z|5CQDZV?Ml4zu`mUEiC`dgToZ)t${!!#`aX`c(PgZ_hqWZk~Hgwb}RX%zAE7%W%nL z^;78w4+JE11HM(Ymw(@O{_T0|cYhTAGR*l>w8M9Umlp#AGedw+h$}FHM!{$ZjMxy^ zZ1AN6SUNG31o;L3AK8E*_&d8QFd=XjctjR6Fz6|QFe7{PpZ`EX6;Bt(5DWk0AO0#5 xp&jBqT1U9uL?5v(=K9za*jjP$LHLjV_6$1HtfYh2>Yf6nCQnyCmvv4FO#of6+c*FK delta 91 zcmbQk^M`qYBnKM<14GZh9l0A7r!cxRa29w(7BevDDS6#lQz^KbyNfM9x-qy__Hv|?B&i0A3*=d#Wzp$Pzx!Wa|) diff --git a/TLM/TLM/Resources/RoadSelectionPanel/RoundButton-bg-hovered.png b/TLM/TLM/Resources/RoadSelectionPanel/RoundButton-bg-hovered.png index 7b562e7d7e683d6cb10cd6f077f787c7a45220fb..15a33b342822dfad8690660a87625d889d207d21 100644 GIT binary patch delta 2065 zcmchXTToMn7KUT3T2Pd+P^%yyP%GskDprpLMHDrA?`%SnO+pAoxk$YhL?{Hv4!Pe5 zfdDNCT5S-RC@}^TAw;4?1|x}p7_JGIcC5!4Qaf5HN07sLJ1>3ppKsQzS?l5dAJ(il zpQ=BVZTw_827}T29ZOMfku5`)WAG3uc4=}fSAoq_9Q&aW%vS*U4M2V)RL}qwG~(zD zFuf5dXg*e;1nJEHU5TaNz|wDG3tO;-Dr`{;P^5wio1sD_T-1y!Qo;-+RIGv-DxgRW zFm7WR8Z4s~i{8PaS{y?Sp&EQqBfhvfp0R`ykD@AosRfxj98-fU(c(&UaV%{dTL+Z1 zgRC|zs~u$PvFv*w#{h6TVNM&&)#JDZm}ej`l?0}W#8e_IC5fd<;Hcu6Y644*XKC)kaJ2-kj>J|Y95uq#ka-#sS4-mR5S|v{>j=EIc)mWKj~d7V9Z{es2n-}a8%d~7 z5Va+U^oYnn;i?mPY6?$76=)~|Ek&rKinLUbjwaU9#JWUrTcTJ`5gVwIHmXFQB-JNM z3{z7OI8hHyY=*>-VM#9}vA|LbSlS1c_5;#> zP&xpXJq61K!Lnz7>=`Jtf#pMB`3P9C#3)!X23C%Pm3FYw0m;W8`8ZTL230OqJA_s_ z;A%Ur+JP4u2_j>>xGP?4!i&3!5)(nvO%fXsiIF7fN|1ITr6y8oHzGA5((ZUk55Ck) zl=cv$=6Go@sjP=0HB!pD$g*yNtQV2>P-RA{tSeDwqLg>j%8j)0E_}5e|Dyx{lLM)- zlWH7Dtv#XEkx+|Hkn1KWmnJB64oclbV%_A@C4NYu|8KVQdX*;zvkY^Q023EKjL;8b z^N%h*|G2>rXIFh8S1sFsE;nEgA0+LKD;=CW;Bms+n7yyMr8F>jH0DkfyqAb)x3plU-@AUe3?odR<>Ld&Y3`%Ab_% z#gXl@41Q?!iE2;y;q@3>x*|vv7$o|-_WJ0ftc$X%r~jhc7vAkN+(WBVS03ehHM#T* zjB>__l-#>>g$a_Uf!B!$CF1)KXJ$W=MU}=`w{@biD=*bKgaFe zV)iYcSm77F8i^eb4f^*?aj0!9iEv?7d%5)$i@%j{1)v4V&`-u9(pay5be}xl9Kf8mpk8a z{`1z&*Aex{%hTs9R%=M=Wbm@ureNy191uL3nTakMjYje(=-P#a`ufeGp%&_lr>l?m z&uZ#TgDukOeyH^E^E1@k-H}qy8#|^nxnom3ckk}4`YLMSLF_p9&BZF$uvj05_8FhK zTQ_~ADUw-^d4loquK4QN#-;XkcQLo(I-|6BUK@m8t^@$MPowtGtlo|IMK=DadC5@YyOqS9Z!pK_26ay zu2y`sIC+b6zQySTGVp!-UkqoD?}%8nBB;vgY7cN|^ z+mm^4N`Z!1=OfVe!>QkRQNuVVlZ5H?_e6$fa!W}5z50*0X^XVRz`VJ^N;gb;z|__n zmyzLK*4eXtA=v?&R(u&_g^=c%#;Yfz^KX&K_o-(B0%OigT5b+U4vlWQbjoJS*+U+D ze}0U8$J^8U`q;tuIMm+7mv UK=;Lxk^enBY%F}ZCFVHmzii`6RR910 delta 2151 zcmV-t2$=V=6Z8|183+ad004~sxNWf^kO6-H32;bRa{vGf6951U69E94oEQKA2pCC3 zK~z{r-B{gkT}Kh$*?X?{NHj$#6(KJ?@efcT0VOT1nkqtw z@=#S&Y6%HN2>KWB#6v$&DnuJ3P+kiNlc>ZG6>6HA+Kv<7k8^hU{pRePyZ72o9Rz<8 zVy@=y?9A-$Z)d-c`(GzX=`GVk&-||F&fR%?t1X|3&M%0_ZV917TlBskW4tCVEceUu za=G%}l{cUHVyOjhk!~5_=YM)>c8_yMQ1lDn!y*m%v<5vZLn zR2Dzwq}8I+vJ2s2kn;Ht2XCMI;}c)oQA9ry;IY$-9aj_&Ks@Q4I|>j}u|#`SPwm9g zRANGnQxnCkfICi#DS{sa=iY$!c~_PfUVr>hpT67CO#y!C**|qUv(t}w?@xcYSRIT* z)D632Q`*?pGZaD7(oB*Uv zM{lH~9DrE5bIujr6%6+ed|CbCl_w6>lRHt0#fU7zK~UA;4i#-o>NDkl-ja0C|5x6hTg6#ZGZiNLsBL1=vkSt(vyL8Ub>xZpiXxr*B?X>q6FK zw#7DMDBC{xr;nZfGbUzD4M07zG1Et5oCZu) z&$CR=vvFHvr@T$wO`E_Fqs)dNv&5zVv2wb&5qIn1nJAFw6Xy*Y8<0srxjnCA%s!FF zs_tNa)XT2|+!5K%kdx&{yT&+^EH%n)Taf}Dkpc`M_V9m(z;><~gc+QM%JLj8%9>`q zZFCdd=rZ52I<{y-Q=Ey|75g}2^p2PoiLBy()wK#3Qx~y;MqD|@@_H2kQ%{B*l77=K zx9Qboq7L&*ddoYH3$4U8@1wq5YJfg0L%Hnme&9TeiB-lH5V47>jxw3HBsczMGp0|{ zt8eZ@nRS2CMmaHM#lZIq9$XX@AfARx=)`52!UeMK3<_k-k)WQYOs{^hHU71@wJkA8 z*SH|E11Cq=AvRb0A0_3|D)UbXFvg^#%JxYIMJyc!lbrY3ZHRhAE+LGPMSRKYj zl%+FIaT)XS_phG1Uw3Q;ND5{DJXZ51nFc^iRO^}b++>YNijvCU%4FJ;Poo2?FUpvY zMl1PRJ#3S06GJF_ZzWjcr_7gMy7U#?34a~due9U(dqB?%CY%!M{SAJ0e32;UsgXse zNt1uf)E2HWudTA{4(E668wRPq`r4!Sy{3ggO$153tM8r*;@^%~MWeK+p zTwWP5GVHm`Kh;qtnSh2ZQ4xPCGo6(AX=H!TC!O!ky@ODG{go5H@lZ zR;FiHW$w;;eoU~!wU&ULrv#%;=>*9 z;>W$nli~W+g}CxA#sWJKY|8l4f=o@`f-F*F5lnh+$TEMI^D}#wapJrHz^8Zga5sO_ zPao~pxA0~-g~$7RT*tQ5$BD>=gMTYXfO%@9!16S)4jQ&H*|m?4_LV5|oD8dCS}CBO?ApEPDsy_4i%8@lWUQxFI#*n!k*ICShwpy7q0l7-Z&lyfZqN zFwSSL`@zd^e*4p7UlVe=DL~S((|>>KxE5dh;UwR!uIx>P^{W7iqq6L84bI3w799Xv zz&5g%pE(9WL_fbp%(>q@cKF}_{Fak_z;v}2?(|L6zIN#X+^O&-DS^&rOhFk-YH*R=)IG>hw!-hyH4BXm=1cuFA5 zvV<}ID{SY{mH*x+;7L0E+}p))W_n*s;(qU104TFj>T5b4VE`D{aNsRhQI^`*H?G*( d`hP~w{R@5lbAB3QW>WwF002ovPDHLkV1j&YCKdny diff --git a/TLM/TLM/Resources/RoadSelectionPanel/RoundButton-bg-normal.png b/TLM/TLM/Resources/RoadSelectionPanel/RoundButton-bg-normal.png index 81f981b7ab7f25db1b0ab6a22c2215f0be257a34..9da7dc4e49e600089c51c013bffe3d26b13abea5 100644 GIT binary patch literal 2043 zcmeH{`#Y2g6vyA#NVZy2NvbDOR$^R6Gj28Jel@puX5M$qm@yef<2K2vamhoPG#Ztb zmV}V*%2T8@ld@72S|R0@LbcVSs8uO;`XlzI-Sd3D=lpPf`+lEu4sckMMGNg00sydx zMkVr0)8)Gsn45Zo162k9^D?&+2pk%L;J`_UkK7&;VOn?QA7QuWMX|?ty@ZMyFMbi1TiNXsPw8=sMdv`o=?f`KOlG9I2kWUJ=Qw zDE2=g8}=K1d@=SA6^xTjDM}vlkKB6#K2OtzaQfC=%@Nq%!`;% z*PJ>1@6F{(Tp=ZL?fr-u%l7ww7~Z3!nd zIm)*Pnj>w*jB59Rp9cyquW5Z$HC4b^@ju`uY4VqZkp z=*zqwo7+y#Ge;1!1vjydNKkSQerD&qS8wx)oBpmr?D4q&DEpi~Xzb`jV=bY_HZ}Ik z?2D5V>1%MGog}{V)5igeA=0p0RO}3=ak28){PqFu)S{?8ckQcfsAJx>3WStBdOWi1 zmAz_w()jJo&Q!}CuT)rCHS)v0$A!mcA5_-DUhexiYL+tWIB;TCz2UN3X@2qk!TDu zEto=Mk?Fw{I-9~|Qt50OgF|PU;L?M63>L^_^GRGHnMWe?$Ycmd;ZZ4)AexXyhv;k> zgDqnQa|A4|ki&zyd=Zx)0t&^TP{M~ygz{t{UlhWJB?4F~6v>1kQb;0&Lc(CNoFT_C z)B52%L5zTe1TjDZG#{M3~lE_FQREj z9{d7V!Ldz(LKi`iiH*V{S7Fg6=(rnnViRnl#0@TU7ndW&>#A`)?NjPFrp;Ic=q;igp5k_0sQ}2NV_C z0f3n$jfms>vi04c#R#0;%^jTxcg8*DL&Mt}2G1+r6BU76RCj&vt=ogX%Y+8`s;uJ8A`2Y2r*fjD7U!RT6qu^bGCKK`vc2tzpj36JYqW*p_gU3_b+#q%8&7Mo zwss{6lPlc9MnqxC-mR74rGtLH0_)9&(Q7%1g)``ME32%t*p5P+IjZAntu4KzHDif| zh4U63y7|V~?6mgFv9z`{S9$m5Zyn87UsD&=)PL&jE~hr;X?Ju_xA52M2rk#9V)dWT ze?6R7IiA0-NUhbvzlB8;mYP+aG+<`Cn(Ad$^1PJUrX~#1Y6Mk>{;(XcnV15tWlvAn zc=oE~KQ2CM1zm&BXkR;a#Z4^*+Z)s)A3WZ~diUh8`YO&O$DE^owRZFinb0W8l_$2K zGL6^l*V?NLZ`-#@*2V5o?G7uGC!(r+ev(?I|1yYq@Da7(xfwpM?^@3lz1hdQZ$GS8 zQCa47xldC3YuD5T4FL|L#lx-_8^mSRi8)v$4!s^asc-u`K=Wi1<^w@jLY?$)>xT)o z`2hwmJH%7x%4v)&}YJ6DcKWABIoB#j- delta 1707 zcmV;c22}a`54#YM83+ad004~sxNWf^kO6-H32;bRa{vGf6951U69E94oEQKA246`; zK~z{r-B{gkR7Dg&^Rc`4ZnxWZw=D7{El@s06JkU}5qJ<3Q4=5V!Dr)xfKMVXYDf%; z;2Q}12R=v)#K=b=F@V1CfDtrUD9{2?q3msUyX}75y?4gn+%8*|E?S@;#^2`LyLW%? z+&RBFbI#0}q5nEq<^IrEyQ59ECS-~tG><8s)WjMY!xNx2l^E%q=F0I3)SbzAw@>Zp z@q!N@5)Vyq)#o$gsn+NkPW)xY*b=lbk2EFGR3rWe1o2$LC5LARWUpzhJAJLsq@AJD zKYjY=Ku~9l7&F05n@G~y`Rg?o+Zcc6i%^tggAHNU-%OOZiQEv8FNP}%pD0`=iWW$> zWJt$`TutPVuP-z0?WR*#PaHf*W3uSK5`25#lM%)vD~Pd=g_7%7!49)@g4i7xtpH+U zfn6j$Els+$kF;HIeeRuvAMDnuboS8Z8?g3+QR#AhytOkGk$zFfS{+ce> z^*O{Gkn>=%vBHzYvT>pe#OH!p<%K-tzoeP)I#$Z_mwp(6$?M~R1UK$~GGip&J(={Y{7Ox-G+nVVmMU^ zHaE-ZSojy~Tr{kQ!YHZJ!$}aM+i);k3?~#06BJ?52Gh1HCsuztm_#ugkhZl;o7$=b znb0PLl6nxnSHE+UCWZreCKYl_lnfKpikeW^q>GXi9;Q+V958>xaV!?dEGh&!=N1Yr z?PAse)2S5g1(YHPmLT~Jo<@KQ!F6ioUIYY{Qbjmu5lam^QR}pmEhK_mp@e@U6E)+M zMdW13D%bsJGKC@mhXDnt5`+yMTpZTmj{0D-#c+VJ9F85KVHOa?*02Zfx<%_CoB)x8 zhp7=oh6Da~`$d21sS-5%R^vGk4@58AQXk$vm~bvmbor(J-qeiZ`BaC;HlSJ|KMS@ioz=4rEO%o=tI3R87JXU zk0OivNVXKOGN!g4{iv@SZ>p%Vpn=QzStLDsWotfbEls0_>1W-xA6rb@Mh8YK3*XyQ z)bgw;z88Nr7BskYB<;;zuP;b$`Le0JOf;6Geg=tT8U7fwIu5|F@`f@+m!&B*E9W4K z`La+>??1BnX0cMEictg&%npCv#@H`X30K7ZUUV002ovPDHLkV1gbE BElU6Z diff --git a/TLM/TLM/Resources/RoadSelectionPanel/Roundabout_LHT-fg-disabled.png b/TLM/TLM/Resources/RoadSelectionPanel/Roundabout_LHT-fg-disabled.png index 4a2982e5e3be48403177d91cd2b92c87f1e3f399..5f557502da196bda542f22caa2e1df37ed40a2a8 100644 GIT binary patch literal 1496 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxK#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvNA9*GX(gAxB|sFIXQK7baZufjf{-U&CR{Ny?uOqe0_Zb0s^9o=@jxnUg;t=_P1&BhID zH*Q$Bal?kqn>TOSvSsVmt=qP3+rEAKjvYI8?%cU+*RI{WckkJ=XYby<`}XbIzkmOM z0|yQsJP36EC>RX^vP0m7&bbC)K4B;c@(cbyvH?TzcXm}^Kyns%L>4nJ@ErhQ#;j|d zoWKAI^>lFzvG9L8`DWN*1p$YMxzspsvfRfWk56lvg2H^hTl`YGHm_-lnL&%v(LFrk$F8t-IV{;K zFt?YtG@)u@xS2rG)oqJDaaUZNZn7h8d*FqaN3Q8^F8*4yF*EtrtpnZfZXf>qefG=9 zB;)IET~-*JjeGCL?^=};6mHS6$1u0(SK0jMKa(fQoa{Nl{KJS@VcW79Z`aK5&_6%l zesb(g@4)ZhkM=4Cea;kKTGXSkroDM-`mUQzlJo8OzJ;(ion;VQb+6oX*8WD$y&Na@ z@00cI`f}vifjJtJ7T!6|^X;Oc{;A0`;|`syaGGfL%!uz;_O9LzmzJ}+h1=B?MAA8? zUTj@+c3!5_L`9Lx&<{DZb?Kz zR9Hvtmu*O7RUF2TZM(ft(IO&dP*7r-y%1roZ66GT4a9Z84U`=O9jUan+Gs@!A_xa* zAtaUxnQff1>{1a+n51R1idyOleJFZer{8n$on_wVjIXmQQ=mYB0{=lKCMGWR;!dhz_(--uKZx=qw7MS7`GaF)uFiV!r%|WNsPl zM>2dKj6{(Sl~H^I-HRgL&E-X2BY#K-K|(V+jGjZ!q7X*@B4&kacN5+~@6x`{skFd) z1nJ-ff;!nn2d@&<$tfFif_5heyU=bgmlakcNWuW&F(h+8JC*6FKwly~+O+M+bp_!h z8Y0(gPGuHyjvxuICg{=nbC|mmz1P>*cNy_hbe$@b@pAZ4(94|_n@9X4I)6&y({3>> zngoZty1H(ysj1o4-rg>=FQZ#@G8r$|QKZ~i$!d^0yqKWJd&H?o&wc2rmX?;f`ucja zxVUJ1KA&l9YgK7+`|;A(Lp-e*Vfi%R##We{QSJx+}t#?v$JMx zZO!!b^qAV(T0Ma-qzUA}sD)9KL*@94<4Na48gd!D7A?4k322r+&%iwx2m~B+b8~a< z8IPHn8MCpmVS0OeW%41a4k2A9w1&peN)lgAz-E%9 zYTD=b`_0PAiit!bj@h!ZGBY$Z6dSph&{w)326@%@4)Xr))?7HW4vz6oM#JH76jRah z@$o1VNH;Ws5|)voq9SMAlR@w1{~1WO9qmz4l)SiVC;7O$yMH@6IyxHd=;)BS4^YBR zf;q9>D_UXuv7mPP%pX8I(Q%2#6aOWn?d?rw?9BrM1A3f3l(Iz0VHCV#POJG1T;kt-hlO5}Ikc1lqeMM{3Za`WlYEV4iBK@OeGxs6VqHTq> zL6ToiSVVgN^nV|62-EsPwu0+5Sw2V8=z!{MuztjXPPC_LbpAjuqL0uKjAq)eb&b~1 zZnOtILECp!Z%bYVXh$)b(h;z=w(J?{g zO_jH7FdmZLM9?GDzp`e#elPVue438FA!a4Xpq8O?F(QKn3jCXR6QscZ31j{Sz5I=P Tp=Ske00000NkvXXu0mjf0EkZ_ diff --git a/TLM/TLM/Resources/RoadSelectionPanel/Roundabout_LHT-fg-normal.png b/TLM/TLM/Resources/RoadSelectionPanel/Roundabout_LHT-fg-normal.png index 7c0dd48e369d78f52e7d484eb84ecfaeb2e632e9..2d5d895bd3cf1b5f6c067c0f447061f8f431ce52 100644 GIT binary patch literal 1530 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxK#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvNA9*GX(gAxB|sFIXMjt4ULS9OiWB19UYyVoI*lELPJAiV`Jmu;*ye*Qc_Yf zGc&WYvT}2C^Yily3k!>iii(SiOG``tZ_)n0&7iflwXLnKr>AH7^yxEZ%$PZI=B!z> zX3w6zWXY1HOP8))yLR2Wbz8S?-L`Gp{~MG3-(_7JzJ34x{r}Go|9^e@ z|NG1T-(P?H`0?}S&tJcO{r>&?|KFc~|Ni~=@8AFb|A8Se3Px-QbaZV8<^e{Ak|4ie zU}6}_fFbxhyDBgta29w(7BevL9Ry*<9TT(P0t2Yr)5S5w!vF2$n_-6xL|o?!@De)9fzRNl~KchK5vIT~&T z;YSSRrbTcpE^93pStjxLp0LX+CxI7>`sbWzJmX-;(x%3yyUY7TQy4?zq>eIaCdJ7H zlgd6FcX@Nzife-TiA*uO0Z5=rM6mXJYE|gV*-Fd~&Vw?&JL;s$QF_ zzZ#camk#hg=CUa&XC4pNCrhnkuA3_E*iXqzopr0NNRc1ONa4 delta 1051 zcmV+$1myer3&04F83+ad004~sxNVUkAAbM|a7bBm000ic000ic0Tn1pfB*mk)k#D_ zR9Hvtm(6RGR}_b5ing_+MX4KwFqNc5P_%?V5=KEx0$qwAxDsrIiYpiX0R>62Fr{D@ znIyCdCW0wRm6}<`g~`rpX%>}Mqllm=6!AMV(c|-b=S(u6ab_|Jg7d(S`<`>}J%8_+ zJNN4i)u>Tpg)uXKstzA``OZ>Nf{fC~UamtQNRv!kQq{Ls*l+3f#j*MBy9HEaF-{np&vJWPGwJQa%Rv9lIgS>M)6LUn7k{@b=nnhz>?7;y>awP$rWozDfC7lAO+3wObjhpAgN{E) zZf3F}5{aA-MvjrPnKRkRrFHkBS@KzsWPN?TMskBX6H|*MlU?$c4IPUmI59jt97ML8 zrwS*RmXZYh-SnH^Bypuj@s_*Fht7wQet9k%8Nmy0AHhEu!Kc>U-G6PgpC3U%-1I2^ z=&tgivlhuUH*?V(9ErtP7{MIO8Ob|i^K+2ZtU35qs@np@)>&TpA`t}8&R^_W*4o+G zIWjmn=&P6oqg6b!u!^J6Xu(yinCK%nmj;1~3U;#9TnOf5un(RL_CfvqjNDo3vrZs- z1p6L+mEhZs&e9>+YJc>Nf}-5bHQL(RIuVb@EtyQ(*w~nH?B$&#Bo{`j-K-uYq+h!R z>3CwpD|iRgSv);8HKnuoDt1KvJSoW>UsWK`=ST^P8%TG-+_SdS+sr06s|$gdV3tHe z7uJJbRT8vUa=H)pxvP2*^b>Rp34Mng@T!uaz4pC-5KDat7k`AsAn*}%0PRP*iLm)C zzK^Ni+o}D2cb81rpPgxVnJ$}$(RWBAas}N$vq<}!u4qbr4*h~+e1blr%!61|X@RGn zDP+HvU2%e$5Ogzz_B(wm>+|v@()aLRyy!aRqU0o~ZRk}x!5TGI5?>%S-X}t#H|)!> Vcq{$atpET300>D%PDHLkV1fxx2?GEC diff --git a/TLM/TLM/Resources/RoadSelectionPanel/Roundabout_RHT-fg-disabled.png b/TLM/TLM/Resources/RoadSelectionPanel/Roundabout_RHT-fg-disabled.png index 3fc4e20e007f32d6ab03824c1ba75c104ee50c70..c2858aa558b1be346aa96b69f1333531df6f40d2 100644 GIT binary patch literal 1493 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxK#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvNA9*GX(gAxB|s_czDds%`Ge}92^`R9UY^hqGDrXR> z^78WY^9u?J3JVL1i;GK2N=i#hD=RCjs;a81t7~d%>g(%gHw4aZ3hwIa>hA7dHnU;* z%%_Q`VI2^y-mq@X#tmyXZdkW*!-mb9 zH*eXpW$V_h+qP}nzJ2?S9XodJ+_`JluHCzL@7c3w@7}%p_U+rhfB%642Y_xK1*0KA zl@KU9U>F6=Ck!P)e!>4oHed+;&aMg!RL%mA$YKTtz5^i4n02j_6Bs~2o-U3d7XEK1 z-3)6s5OBDw6>>2vc4hY6RT<7JCJ21`e_wR7kJy4QKh}L$om}9VW^_|%|C9JTCmL>q zv}|Bgo7H!sYfp%>yU^aG%uIfQdjUDUnpZl(;_O3vESH1dj`SR0;g=g$cRlmiv{N$s=Z&P~L zpShyS_h|c`_}cd4?C<+CXW6A+T>3BIX3?9z3mq0GpI!34e0_b!BC!>|m%1YIEoLcg zoo{wAqV{#?5fhzF*86IX?V8v1^6LVLjQQs;hFsP?Rya+%3F(i$Lkv)78&q Iol`;+03vuJpa1{> delta 1197 zcmV;e1XBCe3(*OX83+ad004~sxNVUkAAbM|a7bBm000ia000ia0czHX2><{DW=TXr zR9HvtmupC5Wf;fb$!)v2&|)HH&{Zt63lY}Z_Q4NAfw)fSlCmP{NTsdO)+XAA2o?t^ zD2eTYSuAXZba70nKCK%hMMW$jl9rg_Sn5hqLEFvK?|;r28)s+coDudzFn{>*oPYD4 z_kG^yJn!uslT}t(W%WPR`1trvm)-9Dh4zCC6>#mgMaNfRuBooBKH-!&qPzuj7yN1$ z81p^+LcQ0WTYuVCfNnM(f>yW;K7;${+``(TPCe!rTm=6>4g{$a!TQ#DuJtWCuEorQ zvhy$<7J@q749?XE9Dpb&xW6D{Wq*`Gf(v8n-~j^q#4b!D>hHiP%g?~m@C3X=+it8c z`OYAnJ&V~3d;A0^QSWbzhQFKZSFw9wuj|W~d}R=aAM zPoW=Qui1rZAr=_Ju!M+SP>S3Z>U&led(<*I%yrCw!hWDslkX(1e;f98W%Brd= zv$(iug2AAvsj0b_@*cQFm6Aw4Lv9}_<(0u(;WTgjopXDZ?tp{)P+eV}Sz1~$3kwUj zC5xuAva*rqkHF2U952`BX@5{3DU~un-fRN%Ih1j2f%dg{mBAr8s&8&?HY+PDW^QiI zY;0_pnVA{0y1HsQJ3CE9MTJJ79khTF^(AaCK%~7ZewFJ!CauyVByN8w6taVxot<@_ zxSF1xHtXx_rmL$-BmC0r2iGO*`!S`GnpFQco(9qEP0|Nu5sHn)4l$5x|TUAz8 zX7cm%O*kAj6B85K^DcLuY^n%;bS{(2E5NrPnvgw7nRse+boA}P!9i0{P+*FSw@$Ir z(o&O`muLF=`po3yq<>BkogP2f5C7Bcpm~^5o+k;5;{wc|1ey3TAl(UvYHMrjo0^); z^768opP#pbEi5cF{r&y1%)JO-t3gcoRo|E3H{Is%Ega>#&QeMH;3#v_8W|a}2+A58 z8?#tIdZ8iMvPuJiK-zupTuCR%iH?pAD-wxVt*xyJ?gQ97Nq>0B9n9NZ!^6YXT%Ye2 zd&<|xf=}sqJI0E=p|`hJ!x;o^BX)3@8ev)1Ph?Ri%$XF^W{Z3WNGDfg);#9SSzj}> zQx^4^b{MXO7vKnf*0G-QTE+3wM*ATh>T@ZTzJ*;7{}Sb(ZA!uE`!L6Ea8f1#9)v0Q zf*{i#Pw^s{H-9jCx!*xUy-53uvA&e?9K`wknCIamI7$F1_c7{fpX-2o;4#<r0yf;!N4JXlzeh?gO#wMDa`?nz6q+Wko^i>*Np6zIh!eI1T9_yn?Tvu^B5dK+O;P z?<-w^@uJ@K7!A4pkM%ox26Tp>qWw46tg^~TntSeLRvKBIdl~aDr>u!7Lc9DL00000 LNkvXXu0mjf9~D`e diff --git a/TLM/TLM/Resources/RoadSelectionPanel/Roundabout_RHT-fg-normal.png b/TLM/TLM/Resources/RoadSelectionPanel/Roundabout_RHT-fg-normal.png index ab7b3203b52e46611356237146ed0a06d6ac597c..7f55b2098f3873e25123bd356a203459fb4d74e3 100644 GIT binary patch literal 1531 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxK#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvNA9*GX(gAxB|s_czCR=tgNlA9UUD#Jw3g=yb===lai8>lao_YQZh3$v$C>E zOH0ej$|@@>tE#H1tE+2jYHDk1|8LR$zs;brv9Y#r0wj4Qf>C^@bTlvPoF-0{`~pNmoMMGef$3X`~ROG{{Q;)|M!>wzrX(Y z@#E*upTB|AL#v2Fp@)nU#N5nFrP4#1o;I6)5Ayx z48h;oRe@=Nv%n*=n1O-sAP6(=n3(+*7(f-CE{-7<{%Dt&?>^ z8@ZK}jr}K2=vgMV@!y$8oR+g%-@HuCGHjWcD|79f^-t3kCDGkkt;NNS4OCoR0NtJC&$)t@cXE16fjYEA6mT;BD? zt74;P@t;_)FHh#r4L7!{NeQn|t`MwEHr}xJ>R+w-pVwvHUi44syo9dSHhwMfn(Mm* zw1bqQ^{<_?YUK%;wdKS#zr&9$PjEWBnEkam*na8uY7N!HE|t{+3yQdw=e^ z_sp65?tS;ZOp_)}?jmO9wfb?7m;YNDmLRivzv~gV9|(?=WWB5ze)w^UC$rh?JoOA^ z-2OU64&4k5nar=~2L6vDvw>(f&^u@e{f5q>Eb<tjYHWi1zzOsVN`E94L%Y`^u$APS=yxwQ_Jq6Z3xQ9fuh9W-+5tYV zZ9&^%bj<4qkYbz+&U!(><4EhR)tjpE6w;=s+D|b&)^Tsz8wA>qR?rb|szz65XXlR# z3k&P5t*zf;w@1x8Q7n(S-AFrZf?$s#iGs3%veW9M}L9#Ly>zY zimBLc)&>Ez$hBiTy{QsM2L=YtO-@dlt*x3}{LpM;&Bn&Yth>8=3jg7#TN1_e*e%|a z9tL-#B{wV4)8F4eJw85e_Rk+?Kj+N~JiV(VTfAW-BO}(=*Ed1G=c4XO6kEUHW;z)7 z75W#&oOM^svpm+%M@L7^Zhx_yE}t>GaA-@{cz3T|x1pgS>+0&t(_b4X0Wm*8wQUv# zzM!nOv3-*qY;SKr7iNx`vMZk^lZ&-{!L0OGNV2W1O*45g8XiH+_aryd!{DpR49aJ3 zerjqe@?2&TDkT?7NrE0X;}S1Qn)jUGDFuh}SIlF~;Q8HW@JnX!Pk$R89ya<{n!yS5 zmYeBeur|rEo8@Q?PUrJ%jIajFX7bwX<{D%-YYjdf4cmg)-|IKrtPKL}L)ETjy@P{; z(-RX+MA*e_813RKTe~=y%T?UPiivhRnRZ7(Zj+!m%B$vlSd-yBcsjfX_4t{&&+zNi zP@G7ajNYxGemBYW_J8)y7YYR{l}a`{J8K+!iIaqcT5M(IVOw2&k@|h!yf@;1dM5&% z#Y>Bei#m(H!`?iL@yB<=Gq$mzBkn=8=i7V&0zQFK-)0!YjtO_cOGqfeN8MdN2+9r! zcezvxoBqd;F07)2PP?nV5Lk~%N&bX(yLu&{|7$4zK6w{Cs5U8`=0Q3@aX+YE&-=pDvm@0p38mE`5V8p*6G*>3XK**U?8P zPps>dsVPtJUILZeuA1O>==ThT9=X1c^>Ka+=^OY5#$Tjt(j*mHdtPnQr14}j{{c}0 Wt1kIHsowwq00{s|MNUMnLSTXvVgg+N diff --git a/TLM/TLM/Resources/RoadSelectionPanel/Stop_LHT-fg-disabled.png b/TLM/TLM/Resources/RoadSelectionPanel/Stop_LHT-fg-disabled.png index 3151b24a64caa0e04a11e05e234165acdab6e90e..e3ae3027df0b00083f6b1d9d3c497b7efa6a40bb 100644 GIT binary patch literal 1453 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxK#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvNA9*GX(gAxB|s_czBHD=YI4m6XiQ%`Ge}9F&wC)YPIC6k-$R>^78WY z^9u?J3JVL1i;GK2N=i#hD=RCjs;a81t7~d%>g(&<+}$Uqr_XK(oZS@M)z#JA-Myft zWl>MhvY8FbXErUL)x3Om+lo0IE9Q2toZq`*)~uBa`d2NOuzvme4Qp3!SO-L_H>_K; zal_h;8`f>yuwnD&&0Dr?0eWW?jD`U5A>eo~q#u}17)pZtg8z?fz!3bMT@@H|oCO|{ z#S9F52SAuH>slu#Fo3i?T^vI!{NGM~9dyWn$4xL`&w``UtZT(yvu>>rcXeI8=KX)K zY%@;hP1|Juy`K5fxcZC7di zKB1K(P~JbkzUX_QNb=hWi(<=^WGnX1w&j&O7T6@a;D}^;;d>AZg|!a00t_K9XwU#Iu;_9w0X{If}Jjl`uD3H^UR*>=r17|qZA{qYM4 zYlp5^GLJdV8Co8=rRbuc_V%*p!Rd;;4`%4bz7J^hxY!?=SoLRyW|5q2?@YIjxHC2_ n7T@DnBz@yno%T`OHM^c+-e)75Qd#egTe~DWM4fIjI7U delta 879 zcmV-#1Cac!3zi3v83+ad004~sxNVUkAAbM|a7bBm000ia000ia0czHX2><{CD@jB_ zR9HvtmrqEOaTv#+Z8Qaq4uLpgjF4#{7K~7E2VHm+4_!S38s(HuLrn>SI(Uol5FSFo z!wyZFPU%>5*+mMfDXd6jvtdk61>y+azR$DwUF&tz_pR4P@P6RS@6Yq&<-6aX=YRJb z+1S|F*ciqMU4|5g!||KAPmS@Injz*`RSaq-EybahMqmR3L(c_y-`wj9-%W8Obj(<~KU7eKba6LZg&D7M?50vkr0C!rAET`U>CN0hb zb+kh-hK7cecs#C*j*jw}mr#&{C*e{{BgCOFS73;X5@jd&3iP#D6}WwOU|`@n_AG60 zZ&M%;-~)Ftl}epQ>c$Fb_S4!g_-$Y$gS1ZaF93357y8!r?IGa(_A6+S($Q z%SByXU37SOcqyGu^S)$*Y_uYl=BzUKAVj*myKj$=k5fLMr_IgHVg$6dwo-F*GtJM> z6Hfm#bUlRl#~s1U3>w7)-GjMXlarIQx3@c{wzaj9$K#=eg#}t!SxKOa4`5D$Wt_$m^D?Eg z*{nO0$z1L0>!XH-1~IC^GpFsrL?RJG*L~=-1a+9XNz6RAK0;5D$$#Y2+1XiYYHE@h zMEOi1XbM!W@fMhddHP2X@-}G(~y=2xy&N>ox?%s=tiU%gRMYjN$V5iuYw{8Fc002ovPDHLk FV1i8=rdt32 diff --git a/TLM/TLM/Resources/RoadSelectionPanel/Stop_LHT-fg-normal.png b/TLM/TLM/Resources/RoadSelectionPanel/Stop_LHT-fg-normal.png index 3d48550ef829c5a3d63c9d56d3797740966b6cd3..81078d53733d44a00ce878276f8753cb322fb0ad 100644 GIT binary patch literal 1468 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*FxK#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvNA9*GX(gAxB|s_czCR=tgNlA1J%`|m6gkZNL#x?S-C<*#nI8x)6>(-%PTQ4 zF)1l2IXO8cB_%U6Gb<~rw6wIWtgN!KvZ|`8y1Kfirlz*GcCV=D1%Cbuf`U&N7~V55 z{0AaN#{V1~|M~g_Wr*``~NnB#>U3x=H`}`me$tRwzjtR_V%8hp6S!4&zLb| z=FFM1X3d&Cd-jqgOO`HOx_0f_b?eq`-MV$#wr&3#8vbwE^nd^U|2HQ6zcKm$t?B=7 z&G>(J{{OoR{@+{t|K5`SkJkNvwEq8-&HtZlIdbI4(W6KIU%U4I`>X#yUjP63?*GpZ z|9^e@|NG1T-(P?I`t|$w@Be>){{Ihj<0u%pA&|(oQxcd@7)pZtf`N%)Bm;)v@9e6; zl)zcw5n0T@z;_UY8Fx&~ehUmBOHUWa5DWjelQV-4Iq4jdK_t)dH?$PYYWT*F3SC|nDWzU^BmpKzqKz+eBMm>EVVx9 z)bd+_T-ABdH+9dvOfURwVpR;ROpXUDBaQ$^oxeAMfH}CAec9kiFLGbR3-a5bA zObSWiXMQa^-YePM=UpM_K>? delta 899 zcmV-}1AP3v3#tc@83+ad004~sxNVUkAAbM|a7bBm000ib000ib0l1NC?EnA+KS@ME zR9HvtSifsiQ51d&Eo4Xu9WqoX8Im?bq?S-fN^ojBWl~4~khWWvcIu=<(q>RF5fqwe zZL^sy?Wk0RAVMohT@}l1Wja($2dn%cMiM{cEX{dp{bj} zAnp5nh#;BB8jo>~G}jJAQQ~4GLi?km=phuyCYMl2>pZ*c{3LO^d4?XefzPLD$Hy)` zas-`!GiRLYYmiW0Af!&LVB(L*Tk zDaNiL$2t#)1cSlS=H@2VYBk!~*`Zi0R)(Ii=1LSriN-ebqKj<(?CflIZEcOH_J!ze zj;J(E^d>{};{$DMY|z}?+(YEWj5!ip1p)2!3osFlMl&lbE2!8p(YqqicI2D~cZp6v zQaYXH*Zlzbm#sbwwhB7TeSd~EuN{H0@$vDjGFSgjbhO=U&fwuaRavfNAQ0f!JtjV{ zMF7w?ZQ$tFKm#0`(G+vKdaaCh<_sR(QEB;np68X(RUXW26)doh|B1jDDw17YT}1`I z!Q>O!Tyq5!lvNA9*GX(gAxB|s_czBHD=YI4m6XiQ%`Ge}9F&wC)YPIC6k-$R>^78WY z^9u?J3JVL1i;GK2N=i#hD=RCjs;a81t7~d%>g(&<+}$Uqr_XK(oZS@M)z#JA-Myft zWl>MhvY8FbXErUL)x3Om+lo0IE9Q2toZq`*)~uBa`d2NOuzvme4Qp3!SO-L_H>_K; zal_h;8`f>yuwnD&&0Dr?0eWW?jD`U5A>eo~q#u}17)pZtg8z?fz!3bMT@@H|oCO|{ z#S9F52SAuH>slu#Fo5JdT^vI!{NGM~8Ft7(!0qN8fnMn?8KP^Y1w_oZXho&g{15MX zdFG0jXVAaHw#%1KerfADxlVVbWn`D1PLgj%mW{)iq@@!(db#+8dmeFBIlaj#a84HS zy!@unIazG6+?T}Hd@SZB!AUdUD+wtqdi!vnYtFqjfd{#}V>iDjzpoz^5&BvsFRZX5 zCF%K_&viv5U0YvQ97_0OQPFq%y2T;p_~Vm2k4`?^udeyj`%P@2I(v7WK>rr?r?OK@Qoqw^^cwgPmE%R+QzxUWZQ&IG4r9quU&$6=I&YOiR z&KoY6yCeBox%%`ksa;8B1-rdiGo_->Z2BT{L}#f+TK$JZ+h(NhNd7jz^1oB~8Lmi| bz?JdaBaCcT%>I85l<+)V{an^LB{Ts556}OV delta 891 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 delta 929 zcmV;S177@;3&#hL83+ad004~sxNVUkAAbM|a7bBm000ia000ia0czHX2><{CTuDSh zR9HvtmS0GdQ5eSG*{0?nN>VTb*NsMV-B=KACgp{6AxZ^-cBS5_7hQDG41ytfAs8XV z2tw#8@J11ffuR?oH`T=~!l{wEWdE8>n!&X8^}OE>ICS5p>zfAd2M_0*_nhr}IDhB7 z=N!tAAwz~^jz*NKq0wk^MKuu;2#b0`R5b{5{C{&=t=3pjP!J97A{Y#E(FcQ%{3nZc{j{pRN8+b~GH>6e$6NvG3MU0oe{z1~Y`YcXa8 zQZo4lO356P$e&>&Ct@)5?=un2n6IauD-sWTrO8N#HWE1TIV0ef|b>w zh(uclN6Fw>=qYCIPDe)vd4D_}T3A?!_6YobKNS`hQbR)nFZ&gkJc;lJC?pTqA!T!n z(#D-^ZEd{)(aOpSEiNvK@LrIiR9ad}0|NszH8n-!ohqTU#C#ZkhJUFNWNTGzZ7pSI zXVd!n&K5Hm3{+fPESsC1oqd7O0L00nDv6TB2f48KPKCqaD!x}adp9;VsIszBUe>|E zL7JYP=9zmAec>OeO|oO=JLH+4pFe{O|3Xbo4QD0k^?Di_8X~*h{u=$yAO{a-(4Pxw zlZe0jC;AI@kZ+Q%u756RbaYfgW-Hz#`1mKxv{Y176j$hHJrTo`a**qX@8VZIJv|ru z`ugTjzXk2tbNKj?y$3lv-(k;npyWf`&%rnx>3fh5#6oEIVZ56odk^j#EcKzk2M-Jk z9ol;^C1C2G--BrfzHE?5H2*(fj-Oi@GGsWW5d8%|QD;*aaSn@}00000NkvXXu0mjf D%?+^s diff --git a/TLM/TLM/Resources/RoadSelectionPanel/Yield_RHT-fg-normal.png b/TLM/TLM/Resources/RoadSelectionPanel/Yield_RHT-fg-normal.png index dd8ee54ebd063ba0bd332735e3f91a9ccaacd79e..31e5396c9575d7c86da29cf396dcb8df0df6422c 100644 GIT binary patch literal 1446 zcmeHG`%jVq6#dM^RBV*#Txqyc@;PmD&0&sV5RXzEx;eFFAWosCEyD#qnj$(KL~0wT z`KYjIDNaLXmX#CQ)JBgrbC%4=nU7HBPz0*m=igyx_uR94f4leW)TgD=e7%o)BM9Ql zpi|hWhVSx#JKA4_&~s67D`C@;5sfiu1s(PmkWm%1{K|7u~;IJ zn3$MICX*Qq29wF;a5!8pH!m-b$K&z&e1Sk96begAOGP4)SS&6pD=RNAuc)X11OX2Z z@bUsI7H~N5^Mil@z~g~HfQSf)j)tTppwofNh5URdECjI_Dl0)71-iJZsw#;@Qd3hS zl}c-CYwPOjWHOmjsZ^;{EiEmrt*vcsZECf;ySux;zh9%#3=a?MbUKhoK&1k;8is~o zwh3mNVNMBiDwywt`7SU$0+Sld8ZZyQ;xky(>h*eq!2q+fu(SmBRoGbr$2wT8V6(yY zHaM){+ys{mTw6}3)8%pjT7Mh>=w<)0Cq8iF-nVo#C&*29YC7usUIvnG_Y6Z#_qtBc zDnXEg9ufGBReE*+mx@B>iV1j@`=IR zQ@#Adp*tAG5y!f1nMW&@y_$R-IQ3rN`{4BC!dXnU^B4`^=|N*?4{jNrXBkEOp!U#( zJCmM9d%*{~K##dkscXiwCVKoU2e&j{{vY+@wtY+~>(^Yx*P?SN@#fq0Z~Xjn49%7A02^d_WWvbg6I6an~B;a-Fm!e6^jyMs4 z@b}CLOCk>z|FB#b&6Hzp!LAt}qj7C0pTq5~Gzicia>Y$N0qQ0;M1*S?QGsiYiIwKRBEe?)$WH&erYAQvO He5Lvy-!NiP delta 865 zcmV-n1D^b*3y24h83+ad004~sxNVUkAAbM|a7bBm000ib000ib0l1NC?EnA+9Z5t% zR9Hvtn9)m{cgR;y-!)vDnGto=4)S?)}!oFG6lQ!cC8254CpKUc-&>XFvgW6!Cb z9rHQH03X@)lVk#{DI(X=(gJL60|y6%%+Irj?2?P>SOwLr8j%P_Mp(?#;UVDlvQ_S} zrjJul&8iW(Om?xp&T2-Zqim74EPv@^N}!shpl*;K#G|zrAo37hT|hbw?CsH!L&6j) zeaoPl8dPs*XXonj^73zw$McT$lP44Tgm4%L1Xz>DYM&EOO(&GUzrTNJad8o;R0=aQ zGwA5(NYKBf_0J*F4YxZ5%Q6eY-Z4&#YUV=)hK7b_CnqPlbA1Qap8|i9n17gcRd>jJ<15x(7t&Oh* zs%fBlcv)YELLuPj8?f;LSl|`SvK41W_Ye#Q(c9Y_W`DDQvSV{%&6Oz@$l1>*fBr1G zDRR~8_0HOvW2UhAvM{(TUw;?4N`Klxwzs#NOkUM~PotEI`Z*2T6j_>@nqraKc&-PR z#RN&2%YFR&1#%h%bu*Du#?Gh6uuYL`%;4O~8GOSG9$;*2jE98x{4!{u%sn_AHv5=; z4>~fr70ca&T+QYlys7<8DN3oRpVP>P@(m9UPe-HC%qr%BF{^m<-+wCh`~78Cu~U_{ z<@#u5K2(5P&76Ht+WX)Wdml8HpP75bc1D~xM#NnKmkP#|K{cIFexJ{`lt?6aw5Ab@ z#lT}vKS@N^;Bon}w@$d-A0(OF=WJK(8BeMD{&5FB%F)p?em^)%a7OiiKWJGl2A9s% rPObU}Sf!dTkE&HOz-rat0gC(ue3#(i2@|IB00000NkvXXu0mjfMaZQJ From eb34bdda37849478e1e3fcdf18df44bf215dee83 Mon Sep 17 00:00:00 2001 From: krzychu124 Date: Fri, 14 Aug 2020 03:36:12 +0200 Subject: [PATCH 05/23] Bugfix floating vehicles if ParkingAI disabled (cherry picked from commit e32778ac804477d1a76a895a18bc36d1a4283f84) --- .../Manager/Impl/AdvancedParkingManager.cs | 44 +++++++++++++++++++ .../Manager/Impl/VehicleBehaviorManager.cs | 14 +++--- .../Manager/IAdvancedParkingManager.cs | 25 +++++++++++ 3 files changed, 76 insertions(+), 7 deletions(-) diff --git a/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs b/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs index d738359f4..a8aca6101 100644 --- a/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs +++ b/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs @@ -2883,6 +2883,50 @@ public bool FindParkingSpaceBuilding(VehicleInfo vehicleInfo, out parkOffset) != 0; } + public bool VanillaFindParkingSpaceWithoutRestrictions(bool isElectric, + ushort homeId, + Vector3 refPos, + Vector3 searchDir, + ushort segment, + float width, + float length, + out Vector3 parkPos, + out Quaternion parkRot, + out float parkOffset) { + if (!CustomPassengerCarAI.FindParkingSpace(isElectric, + homeId, + refPos, + searchDir, + segment, + width, + length, + out parkPos, + out parkRot, + out parkOffset)) { + return false; + } + + // in vanilla parkOffset is always >= 0 for RoadSideParkingSpace + if (Options.parkingRestrictionsEnabled && parkOffset >= 0) { + if (Singleton.instance.m_segments.m_buffer[segment].GetClosestLanePosition( + refPos, + NetInfo.LaneType.Parking, + VehicleInfo.VehicleType.Car, + out _, + out _, + out int laneIndex, + out _)) { + NetInfo.Direction direction + = Singleton.instance.m_segments.m_buffer[segment].Info.m_lanes[laneIndex].m_finalDirection; + if (!ParkingRestrictionsManager.Instance.IsParkingAllowed(segment, direction)) { + return false; + } + } + } + + return true; + } + public bool GetBuildingInfoViewColor(ushort buildingId, ref Building buildingData, ref ExtBuilding extBuilding, diff --git a/TLM/TLM/Manager/Impl/VehicleBehaviorManager.cs b/TLM/TLM/Manager/Impl/VehicleBehaviorManager.cs index e7e5ef3f2..d0bca5b33 100644 --- a/TLM/TLM/Manager/Impl/VehicleBehaviorManager.cs +++ b/TLM/TLM/Manager/Impl/VehicleBehaviorManager.cs @@ -241,16 +241,16 @@ bool citizenDebug } if (!searchedParkingSpace) { + bool isElectric = vehicleInfo.m_class.m_subService != ItemClass.SubService.ResidentialLow; foundParkingSpace = - Constants.ManagerFactory.AdvancedParkingManager.FindParkingSpaceInVicinity( + Constants.ManagerFactory.AdvancedParkingManager.VanillaFindParkingSpaceWithoutRestrictions( + isElectric, + homeID, refPos, searchDir, - vehicleInfo, - homeID, - vehicleID, - GlobalConfig.Instance.ParkingAI.MaxBuildingToPedestrianLaneDistance, - out ExtParkingSpaceLocation parkLoc, - out ushort parkId, + pathPos.m_segment, + vehicleInfo.m_generatedInfo.m_size.x, + vehicleInfo.m_generatedInfo.m_size.z, out parkPos, out parkRot, out parkOffset); diff --git a/TLM/TMPE.API/Manager/IAdvancedParkingManager.cs b/TLM/TMPE.API/Manager/IAdvancedParkingManager.cs index d2778ce15..ea3be4c3a 100644 --- a/TLM/TMPE.API/Manager/IAdvancedParkingManager.cs +++ b/TLM/TMPE.API/Manager/IAdvancedParkingManager.cs @@ -367,5 +367,30 @@ bool FindParkingSpacePropAtBuilding(VehicleInfo vehicleInfo, out Vector3 parkPos, out Quaternion parkRot, out float parkOffset); + + /// + /// Tries to find parking space using vanilla code, preserving parking restrictions + /// + /// Is electric vehicle + /// Home building id of the citizen (citizens are not allowed to park their car on foreign residential premises) + /// Target position that is used as a center point for the search procedure + /// Search direction + /// If != 0, the building is forced to be "accessible" from this segment (where accessible means "close enough") + /// Vehicle width + /// Vehicle length + /// Identified parking space position (only valid if method returns true) + /// Identified parking space rotation (only valid if method returns true) + /// Identified parking space offset (only valid if method returns true and a segment id was given) + /// + bool VanillaFindParkingSpaceWithoutRestrictions(bool isElectric, + ushort homeId, + Vector3 refPos, + Vector3 searchDir, + ushort segment, + float width, + float length, + out Vector3 parkPos, + out Quaternion parkRot, + out float parkOffset); } } \ No newline at end of file From 37c5d0200111690d3f2fa89e7e9f6a7c5ed4db70 Mon Sep 17 00:00:00 2001 From: krzychu124 Date: Sat, 14 Nov 2020 18:53:49 +0100 Subject: [PATCH 06/23] Apply Shift+Click speed limit to entire roundabout (#920) #869: Apply Shift+Click speed limit to entire roundabout (cherry picked from commit ec3227dcdce6a7df9dc8f8cee280e399280625e3) --- TLM/TLM/Manager/Impl/ExtSegmentEndManager.cs | 58 +++++---- .../SubTools/SpeedLimits/SpeedLimitsTool.cs | 122 ++++++++++++++---- 2 files changed, 130 insertions(+), 50 deletions(-) diff --git a/TLM/TLM/Manager/Impl/ExtSegmentEndManager.cs b/TLM/TLM/Manager/Impl/ExtSegmentEndManager.cs index 6e5a9b207..1254a841d 100644 --- a/TLM/TLM/Manager/Impl/ExtSegmentEndManager.cs +++ b/TLM/TLM/Manager/Impl/ExtSegmentEndManager.cs @@ -12,17 +12,10 @@ public class ExtSegmentEndManager : AbstractCustomManager, IExtSegmentEndManager { - public static ExtSegmentEndManager Instance { get; } - static ExtSegmentEndManager() { Instance = new ExtSegmentEndManager(); } - /// - /// All additional data for segment ends - /// - public ExtSegmentEnd[] ExtSegmentEnds { get; } - private ExtSegmentEndManager() { ExtSegmentEnds = new ExtSegmentEnd[NetManager.MAX_SEGMENT_COUNT * 2]; for (uint i = 0; i < NetManager.MAX_SEGMENT_COUNT; ++i) { @@ -31,6 +24,13 @@ private ExtSegmentEndManager() { } } + public static ExtSegmentEndManager Instance { get; } + + /// + /// All additional data for segment ends + /// + public ExtSegmentEnd[] ExtSegmentEnds { get; } + #if DEBUG public string GenerateVehicleChainDebugInfo(ushort segmentId, bool startNode) { int index = GetIndex(segmentId, startNode); @@ -298,23 +298,33 @@ private void Recalculate(ref ExtSegmentEnd segEnd) { public void CalculateCorners(ushort segmentId, bool startNode) { if (!Shortcuts.netService.IsSegmentValid(segmentId)) return; - ref ExtSegmentEnd segEnd = ref ExtSegmentEnds[GetIndex(segmentId, startNode)]; - segmentId.ToSegment().CalculateCorner( - segmentID: segmentId, - heightOffset: true, - start: startNode, - leftSide: false, - cornerPos: out segEnd.RightCorner, - cornerDirection: out segEnd.RightCornerDir, - smooth: out _); - segmentId.ToSegment().CalculateCorner( - segmentID: segmentId, - heightOffset: true, - start: startNode, - leftSide: true, - cornerPos: out segEnd.LeftCorner, - cornerDirection: out segEnd.LeftCornerDir, - smooth: out _); + if (!segmentId.ToSegment().Info) { + Log.Warning($"segment {segmentId} has null info"); + return; + } + + try { + ref ExtSegmentEnd segEnd = ref ExtSegmentEnds[GetIndex(segmentId, startNode)]; + segmentId.ToSegment().CalculateCorner( + segmentID: segmentId, + heightOffset: true, + start: startNode, + leftSide: false, + cornerPos: out segEnd.RightCorner, + cornerDirection: out segEnd.RightCornerDir, + smooth: out _); + segmentId.ToSegment().CalculateCorner( + segmentID: segmentId, + heightOffset: true, + start: startNode, + leftSide: true, + cornerPos: out segEnd.LeftCorner, + cornerDirection: out segEnd.LeftCornerDir, + smooth: out _); + } catch (Exception e) { + Log.Error($"failed calculating corner for segment:{segmentId}, info={segmentId.ToSegment().Info}\n" + + e.Message); + } } private void CalculateIncomingOutgoing(ushort segmentId, diff --git a/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs b/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs index 9ff9e56f8..c152c5173 100644 --- a/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs +++ b/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs @@ -43,7 +43,7 @@ public const int /// 0 = no limit /// null = default /// - private SpeedValue ?currentPaletteSpeedLimit = new SpeedValue(-1f); + private SpeedValue? currentPaletteSpeedLimit = new SpeedValue(-1f); private readonly Dictionary> segmentCenterByDir = new Dictionary>(); @@ -96,12 +96,21 @@ private struct RenderData { private RenderData renderData_; public SpeedLimitsTool(TrafficManagerTool mainTool) - : base(mainTool) - { + : base(mainTool) { CachedVisibleSegmentIds = new GenericArrayCache(NetManager.MAX_SEGMENT_COUNT); LastCachedCamera = new CameraTransformValue(); } + internal static void SetSpeedLimit(LanePos lane, SpeedValue? speed) { + ushort segmentId = lane.laneId.ToLane().m_segment; + SpeedLimitManager.Instance.SetSpeedLimit( + segmentId: segmentId, + laneIndex: lane.laneIndex, + laneInfo: segmentId.ToSegment().Info.m_lanes[lane.laneIndex], + laneId: lane.laneId, + speedLimit: speed?.GameUnits); + } + public override bool IsCursorInPanel() { return base.IsCursorInPanel() || cursorInSecondaryPanel; } @@ -156,17 +165,21 @@ public override void OnToolGUI(Event e) { private void RenderLaneOverlay(RenderManager.CameraInfo cameraInfo, uint laneId) { var marker = new SegmentLaneMarker(laneBuffer[laneId].m_bezier); bool pressed = Input.GetMouseButton(0); - Color color = MainTool.GetToolColor(pressed,false); + Color color = MainTool.GetToolColor(pressed, false); if (!ShowLimitsPerLane) { marker.Size = 3f; // lump the lanes together. } marker.RenderOverlay(cameraInfo, color, pressed); } + /// + /// Renders all lanes with the given + /// if NetInfo.Direction.None, all lanes are rendered. + /// private int RenderSegmentSideOverlay( RenderManager.CameraInfo cameraInfo, ushort segmentId, - NetInfo.Direction finalDirection) { + NetInfo.Direction finalDirection = NetInfo.Direction.None) { int count = 0; bool pressed = Input.GetMouseButton(0); Color color = MainTool.GetToolColor(pressed, false); @@ -180,7 +193,7 @@ private int RenderSegmentSideOverlay( byte laneIndex) => { bool render = (laneInfo.m_laneType & SpeedLimitManager.LANE_TYPES) != 0; render &= (laneInfo.m_vehicleType & SpeedLimitManager.VEHICLE_TYPES) != 0; - render &= laneInfo.m_finalDirection == finalDirection; + render &= laneInfo.m_finalDirection == finalDirection || finalDirection == NetInfo.Direction.None; if (render) { RenderLaneOverlay(cameraInfo, laneId); count++; @@ -193,6 +206,10 @@ private int RenderSegmentSideOverlay( private void RenderLanes(RenderManager.CameraInfo cameraInfo) { if (!MultiSegmentMode) { RenderLaneOverlay(cameraInfo, renderData_.LaneId); + } else if (RoundaboutMassEdit.Instance.TraverseLoop(renderData_.SegmentId, out var segmentList)) { + var lanes = FollowRoundaboutLane(segmentList, renderData_.SegmentId, renderData_.SortedLaneIndex); + foreach (var lane in lanes) + RenderLaneOverlay(cameraInfo, lane.laneId); } else { bool LaneVisitorFun(SegmentLaneTraverser.SegmentLaneVisitData data) { if (data.SortedLaneIndex == renderData_.SortedLaneIndex) { @@ -200,6 +217,7 @@ bool LaneVisitorFun(SegmentLaneTraverser.SegmentLaneVisitData data) { } return true; } + { SegmentLaneTraverser.Traverse( renderData_.SegmentId, SegmentTraverser.TraverseDirection.AnyDirection, @@ -211,10 +229,53 @@ bool LaneVisitorFun(SegmentLaneTraverser.SegmentLaneVisitData data) { LaneVisitorFun); } } + } + + /// + /// iterates through the given roundabout returning an enumeration + /// of all lanes with a matching based on + /// + /// input list of roundabout segments (must be oneway, and in the same direction). + /// The segment to match lane agaisnt + /// + private IEnumerable FollowRoundaboutLane(List segmentList, ushort segmentId0, int sortedLaneIndex) { + bool invert0 = segmentId0.ToSegment().m_flags.IsFlagSet(NetSegment.Flags.Invert); + int count0 = netService.GetSortedLanes( + segmentId: segmentId0, + segment: ref segmentId0.ToSegment(), + startNode: null, + laneTypeFilter: SpeedLimitManager.LANE_TYPES, + vehicleTypeFilter: SpeedLimitManager.VEHICLE_TYPES, + sort: false).Count; + foreach (ushort segmentId in segmentList) { + bool invert = segmentId.ToSegment().m_flags.IsFlagSet(NetSegment.Flags.Invert); + var lanes = netService.GetSortedLanes( + segmentId: segmentId, + segment: ref segmentId.ToSegment(), + startNode: null, + laneTypeFilter: SpeedLimitManager.LANE_TYPES, + vehicleTypeFilter: SpeedLimitManager.VEHICLE_TYPES, + reverse: invert != invert0, + sort: true); + int index = sortedLaneIndex; + + // if lane count does not match, assume segments are connected from outer side of the roundabout. + if (invert0) { + int diff = lanes.Count - count0; + index += diff; + } + if (0 <= index && index < lanes.Count) { + yield return lanes[index]; + } + } // foreach + } private void RenderSegmentsSide(RenderManager.CameraInfo cameraInfo) { if (!MultiSegmentMode) { RenderSegmentSideOverlay(cameraInfo, renderData_.SegmentId, renderData_.FinalDirection); + } else if (RoundaboutMassEdit.Instance.TraverseLoop(renderData_.SegmentId, out var segmentList)) { + foreach (ushort segmentId in segmentList) + RenderSegmentSideOverlay(cameraInfo, segmentId); } else { SegmentTraverser.Traverse( renderData_.SegmentId, @@ -305,8 +366,7 @@ private void ShowSigns(bool viewOnly) { bool hover = false; for (int segmentIdIndex = CachedVisibleSegmentIds.Size - 1; segmentIdIndex >= 0; - segmentIdIndex--) - { + segmentIdIndex--) { ushort segmentId = CachedVisibleSegmentIds.Values[segmentIdIndex]; // draw speed limits if ((MainTool.GetToolMode() == ToolMode.VehicleRestrictions) && @@ -315,7 +375,7 @@ private void ShowSigns(bool viewOnly) { } // no speed limit overlay on selected segment when in vehicle restrictions mode - hover|= DrawSpeedLimitHandles( + hover |= DrawSpeedLimitHandles( segmentId, ref netManager.m_segments.m_buffer[segmentId], viewOnly, @@ -485,8 +545,7 @@ private void GuiDefaultsWindow(int num) { GUILayout.FlexibleSpace(); if (GUILayout.Button(Translation.SpeedLimits.Get("Button:Save"), - GUILayout.Width(70))) - { + GUILayout.Width(70))) { SpeedLimitManager.Instance.FixCurrentSpeedLimits(info); SpeedLimitManager.Instance.SetCustomNetInfoSpeedLimit(info, currentSpeedLimit.GameUnits); } @@ -512,8 +571,7 @@ private void UpdateRoadTex(NetInfo info) { if (info != null) { if ((info.m_Atlas != null) && (info.m_Atlas.material != null) && (info.m_Atlas.material.mainTexture != null) && - info.m_Atlas.material.mainTexture is Texture2D mainTex) - { + info.m_Atlas.material.mainTexture is Texture2D mainTex) { UITextureAtlas.SpriteInfo spriteInfo = info.m_Atlas[info.m_Thumbnail]; if ((spriteInfo != null) && (spriteInfo.texture != null) && @@ -569,7 +627,7 @@ private void GuiSpeedLimitsWindow(int num) { foreach (SpeedValue speedLimit in allSpeedLimits) { // Highlight palette item if it is very close to its float speed - if (currentPaletteSpeedLimit !=null && + if (currentPaletteSpeedLimit != null && FloatUtil.NearlyEqual( (float)currentPaletteSpeedLimit?.GameUnits, speedLimit.GameUnits)) { @@ -589,7 +647,7 @@ private void GuiSpeedLimitsWindow(int num) { } } { - if (currentPaletteSpeedLimit == null ) { + if (currentPaletteSpeedLimit == null) { GUI.color = Color.gray; } GuiSpeedLimitsWindow_AddClearButton(); @@ -606,7 +664,7 @@ private void GuiSpeedLimitsWindow(int num) { if (GUILayout.Button(Translation.SpeedLimits.Get("Window.Title:Default speed limits"), GUILayout.Width(200))) { - TrafficManagerTool.ShowAdvisor(this.GetType().Name + "_Defaults"); + TrafficManagerTool.ShowAdvisor(GetType().Name + "_Defaults"); defaultsWindowVisible = true; } @@ -728,7 +786,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, Vector3 center = segment.m_bounds.center; NetManager netManager = Singleton.instance; - SpeedValue ?speedLimitToSet = viewOnly + SpeedValue? speedLimitToSet = viewOnly ? new SpeedValue(-1f) : currentPaletteSpeedLimit; @@ -812,8 +870,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, if (!viewOnly && !onlyMonorailLanes && ((laneInfo.m_vehicleType & VehicleInfo.VehicleType.Monorail) != - VehicleInfo.VehicleType.None)) - { + VehicleInfo.VehicleType.None)) { Texture2D tex1 = RoadUI.VehicleInfoSignTextures[ LegacyExtVehicleType.ToNew(ExtVehicleType.PassengerTrain)]; MainTool.DrawStaticSquareOverlayGridTexture( @@ -845,8 +902,15 @@ private bool DrawSpeedLimitHandles(ushort segmentId, speedLimitToSet?.GameUnits); if (MultiSegmentMode) { + if (new RoundaboutMassEdit().TraverseLoop(segmentId, out var segmentList)) { + var lanes = FollowRoundaboutLane(segmentList, segmentId, sortedLaneIndex); + foreach (var lane in lanes) { + if (lane.laneId == laneId) // the speed limit for this lane has already been set. + continue; + SetSpeedLimit(lane, speedLimitToSet); + } + } else { int slIndexCopy = sortedLaneIndex; - SegmentLaneTraverser.Traverse( segmentId, SegmentTraverser.TraverseDirection.AnyDirection, @@ -883,6 +947,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, }); } } + } ++x; } @@ -897,7 +962,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, GeometryUtil.CalculateSegmentCenterByDir( segmentId, segCenter, - speedLimitSignSize*TrafficManagerTool.MAX_ZOOM); + speedLimitSignSize * TrafficManagerTool.MAX_ZOOM); } foreach (KeyValuePair e in segCenter) { @@ -941,6 +1006,13 @@ private bool DrawSpeedLimitHandles(ushort segmentId, currentPaletteSpeedLimit?.GameUnits); if (MultiSegmentMode) { + if (new RoundaboutMassEdit().TraverseLoop(segmentId, out var segmentList)) { + foreach (ushort segId in segmentList) { + SpeedLimitManager.Instance.SetSpeedLimit( + segId, + currentPaletteSpeedLimit?.GameUnits); + } + } else { NetInfo.Direction normDir = e.Key; if ((netManager.m_segments.m_buffer[segmentId].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None) { normDir = NetInfo.InvertDirection(normDir); @@ -954,8 +1026,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, SegmentTraverser.SegmentStopCriterion.Junction, SpeedLimitManager.LANE_TYPES, SpeedLimitManager.VEHICLE_TYPES, - data => - { + data => { if (data.SegVisitData.Initial) { return true; } @@ -974,8 +1045,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, if (((netManager.m_segments.m_buffer[otherSegmentId].m_flags & NetSegment.Flags.Invert) - != NetSegment.Flags.None) ^ reverse) - { + != NetSegment.Flags.None) ^ reverse) { otherNormDir = NetInfo.InvertDirection(otherNormDir); } @@ -988,6 +1058,7 @@ private bool DrawSpeedLimitHandles(ushort segmentId, return true; }); + } } } @@ -1124,6 +1195,5 @@ public static float GetVerticalTextureScale() { ? 1.25f : 1.0f; } - } // end class } From 2278e4cd1ea35d26990c9fad8ac102721f8c772c Mon Sep 17 00:00:00 2001 From: krzychu124 Date: Sat, 14 Nov 2020 19:09:46 +0100 Subject: [PATCH 07/23] Right-click close main menu if no tool selected, right-click exit tool - set current tool to None, update OSD (cherry picked from commit 3c0b6e42833e82c15470f400281b37184a2acd93) Road selection Panel - close on single right click, but leave menu open (cherry picked from commit 39087f6ce21f075d0284e6aa07550d3b3ca5e002) --- .../UI/SubTools/JunctionRestrictionsTool.cs | 8 +++-- TLM/TLM/UI/SubTools/LaneConnectorTool.cs | 4 +++ .../UI/SubTools/ManualTrafficLightsTool.cs | 10 ++++-- .../UI/SubTools/ParkingRestrictionsTool.cs | 4 +++ .../PrioritySigns/PrioritySignsTool.cs | 8 +++-- .../SubTools/SpeedLimits/SpeedLimitsTool.cs | 4 +++ .../TimedTrafficLightsTool.cs | 8 +++-- .../UI/SubTools/ToggleTrafficLightsTool.cs | 4 +++ .../UI/SubTools/VehicleRestrictionsTool.cs | 9 ++++-- TLM/TLM/UI/TrafficManagerTool.cs | 31 +++++++++++++++---- 10 files changed, 72 insertions(+), 18 deletions(-) diff --git a/TLM/TLM/UI/SubTools/JunctionRestrictionsTool.cs b/TLM/TLM/UI/SubTools/JunctionRestrictionsTool.cs index 2dc7e7f49..3cc977216 100644 --- a/TLM/TLM/UI/SubTools/JunctionRestrictionsTool.cs +++ b/TLM/TLM/UI/SubTools/JunctionRestrictionsTool.cs @@ -164,8 +164,12 @@ public override void OnPrimaryClickOverlay() { } public override void OnSecondaryClickOverlay() { - SelectedNodeId = 0; - MainTool.RequestOnscreenDisplayUpdate(); + if (SelectedNodeId != 0) { + SelectedNodeId = 0; + MainTool.RequestOnscreenDisplayUpdate(); + } else { + MainTool.SetToolMode(ToolMode.None); + } } public override void OnActivate() { diff --git a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs index 483c08188..f82c24e87 100644 --- a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs +++ b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs @@ -966,6 +966,10 @@ public override void OnSecondaryClickOverlay() { break; } } + + if (GetSelectionMode() == SelectionMode.None) { + MainTool.SetToolMode(ToolMode.None); + } } public override void OnActivate() { diff --git a/TLM/TLM/UI/SubTools/ManualTrafficLightsTool.cs b/TLM/TLM/UI/SubTools/ManualTrafficLightsTool.cs index b6f05ae07..098fa434f 100644 --- a/TLM/TLM/UI/SubTools/ManualTrafficLightsTool.cs +++ b/TLM/TLM/UI/SubTools/ManualTrafficLightsTool.cs @@ -27,9 +27,13 @@ public override void OnSecondaryClickOverlay() { return; } - Cleanup(); - SelectedNodeId = 0; - MainTool.RequestOnscreenDisplayUpdate(); + if (SelectedNodeId != 0) { + Cleanup(); + SelectedNodeId = 0; + MainTool.RequestOnscreenDisplayUpdate(); + } else { + MainTool.SetToolMode(ToolMode.None); + } } public override void OnPrimaryClickOverlay() { diff --git a/TLM/TLM/UI/SubTools/ParkingRestrictionsTool.cs b/TLM/TLM/UI/SubTools/ParkingRestrictionsTool.cs index 5b022569f..8540b8dc7 100644 --- a/TLM/TLM/UI/SubTools/ParkingRestrictionsTool.cs +++ b/TLM/TLM/UI/SubTools/ParkingRestrictionsTool.cs @@ -66,6 +66,10 @@ public override void OnActivate() { public override void OnPrimaryClickOverlay() { } + public override void OnSecondaryClickOverlay() { + MainTool.SetToolMode(ToolMode.None); + } + private void RenderSegmentParkings(RenderManager.CameraInfo cameraInfo) { bool allowed = parkingManager.IsParkingAllowed(renderInfo_.SegmentId, renderInfo_.FinalDirection); bool pressed = Input.GetMouseButton(0); diff --git a/TLM/TLM/UI/SubTools/PrioritySigns/PrioritySignsTool.cs b/TLM/TLM/UI/SubTools/PrioritySigns/PrioritySignsTool.cs index c08da5371..ac34d1552 100644 --- a/TLM/TLM/UI/SubTools/PrioritySigns/PrioritySignsTool.cs +++ b/TLM/TLM/UI/SubTools/PrioritySigns/PrioritySignsTool.cs @@ -76,7 +76,7 @@ public override void OnPrimaryClickOverlay() { if (!isRoundabout) { record_ = PriorityRoad.FixRoad(HoveredSegmentId); } - // TODO: benchmark why bulk setup takes a long time. + // TODO: benchmark why bulk setup takes a long time. Log.Info("After FixRoundabout/FixRoad. Before RefreshMassEditOverlay"); // log time for benchmarking. RefreshMassEditOverlay(); Log.Info("After RefreshMassEditOverlay."); // log time for benchmarking. @@ -125,7 +125,7 @@ public override void OnPrimaryClickOverlay() { massEditMode = PrioritySignsMassEditMode.Min; } } - + // refresh cache if(ControlIsPressed) RefreshMassEditOverlay(); @@ -133,6 +133,10 @@ public override void OnPrimaryClickOverlay() { RefreshCurrentPriorityNodeIds(); } + public override void OnSecondaryClickOverlay() { + MainTool.SetToolMode(ToolMode.None); + } + public override void OnToolGUI(Event e) { } /// diff --git a/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs b/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs index c152c5173..3ddcc598c 100644 --- a/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs +++ b/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs @@ -122,6 +122,10 @@ public override void OnActivate() { public override void OnPrimaryClickOverlay() { } + public override void OnSecondaryClickOverlay() { + MainTool.SetToolMode(ToolMode.None); + } + public override void OnToolGUI(Event e) { base.OnToolGUI(e); diff --git a/TLM/TLM/UI/SubTools/TimedTrafficLights/TimedTrafficLightsTool.cs b/TLM/TLM/UI/SubTools/TimedTrafficLights/TimedTrafficLightsTool.cs index 036f1ad6c..1c8a3cbff 100644 --- a/TLM/TLM/UI/SubTools/TimedTrafficLights/TimedTrafficLightsTool.cs +++ b/TLM/TLM/UI/SubTools/TimedTrafficLights/TimedTrafficLightsTool.cs @@ -106,8 +106,12 @@ public override void OnActivate() { public override void OnSecondaryClickOverlay() { if (!IsCursorInPanel()) { - Cleanup(); - this.SetToolMode(TTLToolMode.SelectNode); + if (ttlToolMode_ != TTLToolMode.SelectNode) { + Cleanup(); + this.SetToolMode(TTLToolMode.SelectNode); + } else { + MainTool.SetToolMode(ToolMode.None); + } } } diff --git a/TLM/TLM/UI/SubTools/ToggleTrafficLightsTool.cs b/TLM/TLM/UI/SubTools/ToggleTrafficLightsTool.cs index a5e92d3ab..ae19e4eb4 100644 --- a/TLM/TLM/UI/SubTools/ToggleTrafficLightsTool.cs +++ b/TLM/TLM/UI/SubTools/ToggleTrafficLightsTool.cs @@ -51,6 +51,10 @@ public override void OnPrimaryClickOverlay() { }); } + public override void OnSecondaryClickOverlay() { + MainTool.SetToolMode(ToolMode.None); + } + public void ToggleTrafficLight(ushort nodeId, ref NetNode node, bool showMessageOnError = true) { diff --git a/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs b/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs index b531e4b3c..2b07ea1ce 100644 --- a/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs +++ b/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs @@ -122,8 +122,12 @@ public override void OnPrimaryClickOverlay() { public override void OnSecondaryClickOverlay() { if (!IsCursorInPanel()) { - SelectedSegmentId = 0; - MainTool.RequestOnscreenDisplayUpdate(); + if (SelectedSegmentId != 0) { + SelectedSegmentId = 0; + MainTool.RequestOnscreenDisplayUpdate(); + } else { + MainTool.SetToolMode(ToolMode.None); + } } } @@ -195,7 +199,6 @@ public override void RenderOverlay(RenderManager.CameraInfo cameraInfo) { RenderRoadLane(cameraInfo); } else { RenderLaneOverlay(cameraInfo, renderData_.laneId); - } } } diff --git a/TLM/TLM/UI/TrafficManagerTool.cs b/TLM/TLM/UI/TrafficManagerTool.cs index 69cf6b13e..d8cb114b7 100644 --- a/TLM/TLM/UI/TrafficManagerTool.cs +++ b/TLM/TLM/UI/TrafficManagerTool.cs @@ -286,6 +286,8 @@ public void SetToolMode(ToolMode newToolMode) { toolMode_ = ToolMode.None; Log._Debug($"SetToolMode: reset because toolmode not found {newToolMode}"); + OnscreenDisplay.DisplayIdle(); + ModUI.Instance.MainMenu.UpdateButtons(); return; } @@ -329,6 +331,10 @@ private void SetToolMode_Activate(ToolMode newToolMode) { // Overridden to disable base class behavior protected override void OnEnable() { + // If TMPE was enabled by switching back from another tool (eg: buldozer, free camera), show main menue panel. + if (ModUI.Instance != null && !ModUI.Instance.IsVisible()) + ModUI.Instance.ShowMainMenu(); + if (legacySubTools_ != null) { Log._Debug("TrafficManagerTool.OnEnable(): Performing cleanup"); foreach (KeyValuePair e in legacySubTools_) { @@ -339,7 +345,10 @@ protected override void OnEnable() { } protected override void OnDisable() { - // Overridden to disable base class behavior + // If TMPE was disabled by switching to another tool, hide main menue panel. + if (ModUI.Instance != null && ModUI.Instance.IsVisible()) + ModUI.Instance.CloseMainMenu(); + // no call to base method to disable base class behavior } public override void RenderGeometry(RenderManager.CameraInfo cameraInfo) { @@ -459,7 +468,7 @@ protected override void OnToolUpdate() { } bool primaryMouseClicked = Input.GetMouseButtonDown(0); - bool secondaryMouseClicked = Input.GetMouseButtonDown(1); + bool secondaryMouseClicked = Input.GetMouseButtonUp(1); // check if clicked if (!primaryMouseClicked && !secondaryMouseClicked) { @@ -485,8 +494,17 @@ protected override void OnToolUpdate() { } if (secondaryMouseClicked) { - activeLegacySubTool_?.OnSecondaryClickOverlay(); - activeSubTool_?.OnToolRightClick(); + if (GetToolMode() == ToolMode.None) { + RoadSelectionPanels roadSelectionPanels = UIView.GetAView().GetComponent(); + if (roadSelectionPanels && roadSelectionPanels.RoadWorldInfoPanelExt && roadSelectionPanels.RoadWorldInfoPanelExt.isVisible) { + RoadSelectionPanels.RoadWorldInfoPanel.Hide(); + } else { + ModUI.Instance.CloseMainMenu(); + } + } else { + activeLegacySubTool_?.OnSecondaryClickOverlay(); + activeSubTool_?.OnToolRightClick(); + } } } } @@ -510,6 +528,9 @@ public void OnToolGUIImpl(Event e) { if (!Input.GetMouseButtonDown(0)) { _mouseClickProcessed = false; } + if (e.type == EventType.keyDown && e.keyCode == KeyCode.Escape) { + ModUI.Instance.CloseMainMenu(); + } if (Options.nodesOverlay) { DebugGuiDisplaySegments(); @@ -575,8 +596,6 @@ void DefaultOnToolGUI(Event e) { instanceID, HitPos); }); - } else if (e.type == EventType.MouseDown && e.button == 1) { - SimulationManager.instance.m_ThreadingWrapper.QueueMainThread(RoadSelectionPanels.RoadWorldInfoPanel.Hide); } } From 2914bdcbd8a7870d774a0eb83322643e1417eb24 Mon Sep 17 00:00:00 2001 From: krzychu124 Date: Sat, 14 Nov 2020 19:26:04 +0100 Subject: [PATCH 08/23] Version bump --- TLM/SharedAssemblyInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TLM/SharedAssemblyInfo.cs b/TLM/SharedAssemblyInfo.cs index 050b42ab7..3a0de65f5 100644 --- a/TLM/SharedAssemblyInfo.cs +++ b/TLM/SharedAssemblyInfo.cs @@ -20,4 +20,4 @@ // Minor Version // Build Number // Revision -[assembly: AssemblyVersion("11.5.0.*")] +[assembly: AssemblyVersion("11.5.1.*")] From 5875d42205915ee97ea9fba6983f199f4f806380 Mon Sep 17 00:00:00 2001 From: krzychu124 Date: Sat, 21 Nov 2020 00:52:53 +0100 Subject: [PATCH 09/23] Fix - RMB lane connector - to select node --- TLM/TLM/UI/SubTools/LaneConnectorTool.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs index f82c24e87..99f15125d 100644 --- a/TLM/TLM/UI/SubTools/LaneConnectorTool.cs +++ b/TLM/TLM/UI/SubTools/LaneConnectorTool.cs @@ -935,6 +935,8 @@ public override void OnSecondaryClickOverlay() { return; } + ushort previouslySelectedNodeId = SelectedNodeId; + switch (GetSelectionMode()) { // also: case MarkerSelectionMode.None: default: { @@ -967,7 +969,7 @@ public override void OnSecondaryClickOverlay() { } } - if (GetSelectionMode() == SelectionMode.None) { + if (GetSelectionMode() == SelectionMode.None && previouslySelectedNodeId == 0) { MainTool.SetToolMode(ToolMode.None); } } From 24f741ca51ebffcf2b0db7753f8aee5b45c57515 Mon Sep 17 00:00:00 2001 From: egi Date: Thu, 8 Apr 2021 01:12:42 +0200 Subject: [PATCH 10/23] Fixed SA1410 warnings by replacing the delegates with lambdas. --- TLM/TLM/Patch/_InfoManager/SetModePatch.cs | 2 +- TLM/TLM/TrafficManagerMod.cs | 2 +- TLM/TLM/UI/Helpers/GuideHandler.cs | 6 +++--- TLM/TLM/UI/RoadSelectionPanels.cs | 4 ++-- TLM/TLM/UI/TrafficManagerTool.cs | 2 +- TLM/TLM/Util/RoadSelectionUtil.cs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/TLM/TLM/Patch/_InfoManager/SetModePatch.cs b/TLM/TLM/Patch/_InfoManager/SetModePatch.cs index 3518d9ba6..be02c09e0 100644 --- a/TLM/TLM/Patch/_InfoManager/SetModePatch.cs +++ b/TLM/TLM/Patch/_InfoManager/SetModePatch.cs @@ -28,7 +28,7 @@ public static void Prefix(InfoMode mode, SubInfoMode subMode) // UI to be handled by Default tool ModUI.Instance.CloseMainMenu(); - SimulationManager.instance.m_ThreadingWrapper.QueueMainThread(delegate () { + SimulationManager.instance.m_ThreadingWrapper.QueueMainThread(() => { DefaultTool.OpenWorldInfoPanel( Singleton.instance.GetSelectedInstance(), Input.mousePosition); diff --git a/TLM/TLM/TrafficManagerMod.cs b/TLM/TLM/TrafficManagerMod.cs index 9c71d199b..0cffbc00a 100644 --- a/TLM/TLM/TrafficManagerMod.cs +++ b/TLM/TLM/TrafficManagerMod.cs @@ -105,7 +105,7 @@ public void OnEnabled() { #if DEBUG const bool installHarmonyASAP = false; // set true for fast testing if (installHarmonyASAP) - HarmonyHelper.DoOnHarmonyReady(delegate () { Patcher.Create().Install(); }); + HarmonyHelper.DoOnHarmonyReady(() => { Patcher.Create().Install(); }); #endif } diff --git a/TLM/TLM/UI/Helpers/GuideHandler.cs b/TLM/TLM/UI/Helpers/GuideHandler.cs index 185c7d035..64e42897d 100644 --- a/TLM/TLM/UI/Helpers/GuideHandler.cs +++ b/TLM/TLM/UI/Helpers/GuideHandler.cs @@ -25,7 +25,7 @@ public void Activate(string localeKey) { if (guide == null) { Log.Error("guide is null"); } else { - Singleton.instance.AddAction(delegate () { + Singleton.instance.AddAction(() => { guide.Activate(); }); } @@ -36,7 +36,7 @@ public void Deactivate(string localeKey) { if (guide == null) { Log.Error("Unreachable code."); } else { - Singleton.instance.AddAction(delegate () { + Singleton.instance.AddAction(() => { guide.Deactivate(); }); } @@ -44,7 +44,7 @@ public void Deactivate(string localeKey) { } public void DeactivateAll() { - Singleton.instance.AddAction(delegate () { + Singleton.instance.AddAction(() => { foreach (var item in GuideTable) { item.Value?.Deactivate(); } // end foreach diff --git a/TLM/TLM/UI/RoadSelectionPanels.cs b/TLM/TLM/UI/RoadSelectionPanels.cs index 93cdd46bb..a6270c8ff 100644 --- a/TLM/TLM/UI/RoadSelectionPanels.cs +++ b/TLM/TLM/UI/RoadSelectionPanels.cs @@ -175,7 +175,7 @@ public void Refresh(bool reset = false) { } private void RefreshOnEvent() => - EnqueueAction(delegate () { Root?.Refresh(reset: true); }); + EnqueueAction(() => { Root?.Refresh(reset: true); }); internal void RenderOverlay() { //Log._Debug("Render over lay called st:\n" + Environment.StackTrace); @@ -260,7 +260,7 @@ private void HideMassEditOverlay() { private void ShowAdvisorOnEvent(UIComponent component, bool value) { if (value) { - EnqueueAction(delegate () { + EnqueueAction(() => { TrafficManagerTool.ShowAdvisor("RoadSelection"); }); } diff --git a/TLM/TLM/UI/TrafficManagerTool.cs b/TLM/TLM/UI/TrafficManagerTool.cs index 8e2c86e90..f1719490e 100644 --- a/TLM/TLM/UI/TrafficManagerTool.cs +++ b/TLM/TLM/UI/TrafficManagerTool.cs @@ -578,7 +578,7 @@ void DefaultOnToolGUI(Event e) { NetSegment = HoveredSegmentId, }; - SimulationManager.instance.m_ThreadingWrapper.QueueMainThread(delegate () { + SimulationManager.instance.m_ThreadingWrapper.QueueMainThread(() => { OpenWorldInfoPanel( instanceID, HitPos); diff --git a/TLM/TLM/Util/RoadSelectionUtil.cs b/TLM/TLM/Util/RoadSelectionUtil.cs index d758cdffc..0f16f5be3 100644 --- a/TLM/TLM/Util/RoadSelectionUtil.cs +++ b/TLM/TLM/Util/RoadSelectionUtil.cs @@ -29,7 +29,7 @@ public static void Release() { /// This will join target segment to the same road as source segment. /// public static void CopySegmentName(ushort sourceSegmentID, ushort targetSegmentID) { - SimulationManager.instance.AddAction(delegate () { + SimulationManager.instance.AddAction(() => { string sourceName = NetManager.instance.GetSegmentName(sourceSegmentID); string targetName = NetManager.instance.GetSegmentName(targetSegmentID); if (sourceName != targetName) { From f064a2cb23ce59355772b7569ad066a275d33648 Mon Sep 17 00:00:00 2001 From: egi Date: Thu, 8 Apr 2021 01:20:36 +0200 Subject: [PATCH 11/23] Fixed "SA1137 Elements should have the same indentation" warnings. --- TLM/TLM/Custom/AI/CustomTrainAI.cs | 2 +- TLM/TLM/Manager/Impl/AdvancedParkingManager.cs | 10 +++++----- TLM/TLM/Manager/Impl/TrafficPriorityManager.cs | 10 +++++----- TLM/TLM/TrafficLight/Impl/CustomSegmentLights.cs | 10 +++++----- TLM/TLM/TrafficLight/Impl/TimedTrafficLightsStep.cs | 10 +++++----- TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs | 2 +- TLM/TMPE.UnitTest/Translation/CsvParser.cs | 2 +- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/TLM/TLM/Custom/AI/CustomTrainAI.cs b/TLM/TLM/Custom/AI/CustomTrainAI.cs index 3973377f1..6deabee87 100644 --- a/TLM/TLM/Custom/AI/CustomTrainAI.cs +++ b/TLM/TLM/Custom/AI/CustomTrainAI.cs @@ -152,7 +152,7 @@ public void CustomSimulationStep(ushort vehicleId, ref Vehicle vehicleData, Vect if (++num3 > 16384) { CODebugBase.Error( LogChannel.Core, - $"Invalid list detected!\n{Environment.StackTrace}"); + $"Invalid list detected!\n{Environment.StackTrace}"); break; } } diff --git a/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs b/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs index 429bdc317..34eab5388 100644 --- a/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs +++ b/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs @@ -1307,11 +1307,11 @@ private ExtSoftPathState default: { // ... and a path to a parking spot was calculated: dismiss path and // restart path-finding for walking - Log._DebugIf( - logParkingAi, - () => $"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): " + - "A parking space car path was queried but it turned out that no car is " + - "needed. Retrying path-finding for walking."); + Log._DebugIf( + logParkingAi, + () => $"AdvancedParkingManager.OnCitizenPathFindSuccess({instanceId}): " + + "A parking space car path was queried but it turned out that no car is " + + "needed. Retrying path-finding for walking."); extCitInstMan.Reset(ref extInstance); extInstance.pathMode = ExtPathMode.RequiresWalkingPathToTarget; diff --git a/TLM/TLM/Manager/Impl/TrafficPriorityManager.cs b/TLM/TLM/Manager/Impl/TrafficPriorityManager.cs index 27e5fb241..b110ea66c 100644 --- a/TLM/TLM/Manager/Impl/TrafficPriorityManager.cs +++ b/TLM/TLM/Manager/Impl/TrafficPriorityManager.cs @@ -1108,11 +1108,11 @@ public bool DetectCollision(bool logPriority, wouldCollide = true; } else { // both are going to a different lane: check lane order - Log._DebugIf( - logPriority, - () => $" TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): " + - "Target and incoming are going to the same segment BUT NOT to the same lane. " + - "Determining if lane order is correct."); + Log._DebugIf( + logPriority, + () => $" TrafficPriorityManager.DetectCollision({vehicleId}, {incomingVehicleId}): " + + "Target and incoming are going to the same segment BUT NOT to the same lane. " + + "Determining if lane order is correct."); switch (targetToDir) { case ArrowDirection.Left: diff --git a/TLM/TLM/TrafficLight/Impl/CustomSegmentLights.cs b/TLM/TLM/TrafficLight/Impl/CustomSegmentLights.cs index 4acdb0e1d..a8c9f67e3 100644 --- a/TLM/TLM/TrafficLight/Impl/CustomSegmentLights.cs +++ b/TLM/TLM/TrafficLight/Impl/CustomSegmentLights.cs @@ -766,11 +766,11 @@ public void Housekeeping(bool mayDelete, bool calculateAutoPedLight) { VehicleTypeByLaneIndex[laneIndex] = ExtVehicleType.None; } - Log._DebugIf( - logHouseKeeping, - () => $"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): " + - $"housekeeping @ seg. {SegmentId}, node {nodeId}: Added default main vehicle " + - $"light: {defaultSegmentLight}"); + Log._DebugIf( + logHouseKeeping, + () => $"CustomSegmentLights.Housekeeping({mayDelete}, {calculateAutoPedLight}): " + + $"housekeeping @ seg. {SegmentId}, node {nodeId}: Added default main vehicle " + + $"light: {defaultSegmentLight}"); // addPedestrianLight = true; } else { diff --git a/TLM/TLM/TrafficLight/Impl/TimedTrafficLightsStep.cs b/TLM/TLM/TrafficLight/Impl/TimedTrafficLightsStep.cs index 052abb748..38b6b7ea5 100644 --- a/TLM/TLM/TrafficLight/Impl/TimedTrafficLightsStep.cs +++ b/TLM/TLM/TrafficLight/Impl/TimedTrafficLightsStep.cs @@ -972,11 +972,11 @@ IDictionary movingVehiclesMetric } else { curTotalLaneWait += numVehicles; ++numLaneWaits; - Log._DebugIf( - logTrafficLights, - () => "TimedTrafficLightsStep.calcWaitFlow: ## Vehicles @ " + - $"lane {laneIndex}, seg. {sourceSegmentId} going to seg. " + - $"{targetSegmentId}: COUTING as WAITING -- numVehicles={numVehicles}"); + Log._DebugIf( + logTrafficLights, + () => "TimedTrafficLightsStep.calcWaitFlow: ## Vehicles @ " + + $"lane {laneIndex}, seg. {sourceSegmentId} going to seg. " + + $"{targetSegmentId}: COUTING as WAITING -- numVehicles={numVehicles}"); } Log._DebugIf( diff --git a/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs b/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs index a7ee0a520..1def0c4ce 100644 --- a/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs +++ b/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs @@ -146,7 +146,7 @@ public override void OnToolGUI(Event e) { 255, windowRect, GuiVehicleRestrictionsWindow, - T("Dialog.Title:Vehicle restrictions"), + T("Dialog.Title:Vehicle restrictions"), WindowStyle, EmptyOptionsArray); cursorInSecondaryPanel = windowRect.Contains(Event.current.mousePosition); diff --git a/TLM/TMPE.UnitTest/Translation/CsvParser.cs b/TLM/TMPE.UnitTest/Translation/CsvParser.cs index 038372753..c364b5066 100644 --- a/TLM/TMPE.UnitTest/Translation/CsvParser.cs +++ b/TLM/TMPE.UnitTest/Translation/CsvParser.cs @@ -33,7 +33,7 @@ public static void InitializeClass(TestContext testContext) { } private static void PrepareData(byte[] testFileData, out List columns, out string dataBlock) { - PrivateType lookupTableType = new PrivateType(typeof(LookupTable)); + PrivateType lookupTableType = new PrivateType(typeof(LookupTable)); string columnsRow; using (var m = new MemoryStream(testFileData)) { using (var sr = new StreamReader(m)) { From 4e85745855276f0f3c532ab6094106e18e233afd Mon Sep 17 00:00:00 2001 From: egi Date: Thu, 8 Apr 2021 01:22:09 +0200 Subject: [PATCH 12/23] Fixed "SA1137 Elements should have the same indentation" warning. --- TLM/OptionsFramework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TLM/OptionsFramework b/TLM/OptionsFramework index 9da22408e..6a66e3bc8 160000 --- a/TLM/OptionsFramework +++ b/TLM/OptionsFramework @@ -1 +1 @@ -Subproject commit 9da22408e84909f01bd6cba91d06dc94ee7afdce +Subproject commit 6a66e3bc8ff68d9ca2f52313c3128ede81e64a54 From 514a644c4400c41642a3c13b95bdd5c67815a6fb Mon Sep 17 00:00:00 2001 From: egi Date: Thu, 8 Apr 2021 02:00:36 +0200 Subject: [PATCH 13/23] Fixed all instances of delegate instance allocation (HAA0603) where its used to feed GUILayout.Window. This will reduce allocations when some UI tools are open. --- TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs | 10 ++++++++-- .../TimedTrafficLights/TimedTrafficLightsTool.cs | 14 +++++++++++--- TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs | 6 +++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs b/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs index 779249784..9196fe9c5 100644 --- a/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs +++ b/TLM/TLM/UI/SubTools/SpeedLimits/SpeedLimitsTool.cs @@ -36,6 +36,9 @@ public const int private const int GUI_SPEED_SIGN_SIZE = 80; private readonly float speedLimitSignSize = 70f; + private readonly GUI.WindowFunction _guiDefaultsWindowDelegate; + private readonly GUI.WindowFunction _guiSpeedLimitsWindowDelegate; + private bool cursorInSecondaryPanel; /// Currently selected speed limit on the limits palette @@ -97,6 +100,9 @@ private struct RenderData { public SpeedLimitsTool(TrafficManagerTool mainTool) : base(mainTool) { + _guiDefaultsWindowDelegate = GuiDefaultsWindow; + _guiSpeedLimitsWindowDelegate = GuiSpeedLimitsWindow; + CachedVisibleSegmentIds = new GenericArrayCache(NetManager.MAX_SEGMENT_COUNT); LastCachedCamera = new CameraTransformValue(); } @@ -142,7 +148,7 @@ public override void OnToolGUI(Event e) { paletteWindowRect = GUILayout.Window( id: 254, screenRect: paletteWindowRect, - func: GuiSpeedLimitsWindow, + func: _guiSpeedLimitsWindowDelegate, text: Translation.Menu.Get("Tooltip:Speed limits") + unitTitle, style: WindowStyle, options: EmptyOptionsArray); @@ -151,7 +157,7 @@ public override void OnToolGUI(Event e) { defaultsWindowRect = GUILayout.Window( id: 258, screenRect: defaultsWindowRect, - func: GuiDefaultsWindow, + func: _guiDefaultsWindowDelegate, text: Translation.SpeedLimits.Get("Window.Title:Default speed limits"), style: WindowStyle, options: EmptyOptionsArray); diff --git a/TLM/TLM/UI/SubTools/TimedTrafficLights/TimedTrafficLightsTool.cs b/TLM/TLM/UI/SubTools/TimedTrafficLights/TimedTrafficLightsTool.cs index 4862f2456..868a8e499 100644 --- a/TLM/TLM/UI/SubTools/TimedTrafficLights/TimedTrafficLightsTool.cs +++ b/TLM/TLM/UI/SubTools/TimedTrafficLights/TimedTrafficLightsTool.cs @@ -20,6 +20,10 @@ public class TimedTrafficLightsTool : LegacySubTool, UI.MainMenu.IOnscreenDisplayProvider { + private readonly GUI.WindowFunction _guiTimedControlPanelDelegate; + private readonly GUI.WindowFunction _guiTimedTrafficLightsNodeWindowDelegate; + private readonly GUI.WindowFunction _guiTimedTrafficLightsPasteWindowDelegate; + private TTLToolMode ttlToolMode_ = TTLToolMode.SelectNode; private readonly GUIStyle _counterStyle = new GUIStyle(); @@ -54,6 +58,10 @@ public class TimedTrafficLightsTool public TimedTrafficLightsTool(TrafficManagerTool mainTool) : base(mainTool) { + _guiTimedControlPanelDelegate = GuiTimedControlPanel; + _guiTimedTrafficLightsNodeWindowDelegate = GuiTimedTrafficLightsNodeWindow; + _guiTimedTrafficLightsPasteWindowDelegate = GuiTimedTrafficLightsPasteWindow; + currentTimedNodeIds = new HashSet(); } @@ -1093,7 +1101,7 @@ private void GuiTimedTrafficLightsNode() { _windowRect2 = GUILayout.Window( id: 252, screenRect: _windowRect2, - func: GuiTimedTrafficLightsNodeWindow, + func: _guiTimedTrafficLightsNodeWindowDelegate, text: T("TTL.Window.Title:Select nodes"), style: WindowStyle, options: EmptyOptionsArray); @@ -1110,7 +1118,7 @@ private void GuiTimedTrafficLights() { _windowRect = GUILayout.Window( id: 253, screenRect: _windowRect, - func: GuiTimedControlPanel, + func: _guiTimedControlPanelDelegate, text: T("Dialog.Title:Timed traffic lights manager"), style: WindowStyle, options: EmptyOptionsArray); @@ -1133,7 +1141,7 @@ private void GuiTimedTrafficLightsCopy() { _windowRect2 = GUILayout.Window( id: 255, screenRect: _windowRect2, - func: GuiTimedTrafficLightsPasteWindow, + func: _guiTimedTrafficLightsPasteWindowDelegate, text: T("TTL.Window.Title:Paste"), style: WindowStyle, options: EmptyOptionsArray); diff --git a/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs b/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs index a7ee0a520..0ee0e0a39 100644 --- a/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs +++ b/TLM/TLM/UI/SubTools/VehicleRestrictionsTool.cs @@ -34,6 +34,8 @@ public class VehicleRestrictionsTool private readonly float vehicleRestrictionsSignSize = 80f; + private readonly GUI.WindowFunction _guiVehicleRestrictionsWindowDelegate; + private bool cursorInSecondaryPanel; private bool overlayHandleHovered; @@ -59,6 +61,8 @@ private struct RenderData { public VehicleRestrictionsTool(TrafficManagerTool mainTool) : base(mainTool) { + _guiVehicleRestrictionsWindowDelegate = GuiVehicleRestrictionsWindow; + currentRestrictedSegmentIds = new HashSet(); } @@ -145,7 +149,7 @@ public override void OnToolGUI(Event e) { windowRect = GUILayout.Window( 255, windowRect, - GuiVehicleRestrictionsWindow, + _guiVehicleRestrictionsWindowDelegate, T("Dialog.Title:Vehicle restrictions"), WindowStyle, EmptyOptionsArray); From ebe29eaddc5553fd1f07486800167bc576991b71 Mon Sep 17 00:00:00 2001 From: kianzarrin Date: Thu, 8 Apr 2021 15:31:51 +0300 Subject: [PATCH 14/23] cleaning up life cycle code (#1068) * made lifecycle functions/variables static and removed OnCreated OnRelease for better hot reload. * cleanup lifecycle/Patcher. --- TLM/TLM/LoadingExtension.cs | 137 +++++++-------------- TLM/TLM/Patcher.cs | 60 +++------ TLM/TLM/State/Options.cs | 2 +- TLM/TLM/State/SerializableDataExtension.cs | 32 ++--- TLM/TLM/TrafficManagerMod.cs | 24 ++-- TLM/TLM/UI/Helpers/ExtUITabStrip.cs | 5 +- TLM/TLM/UI/MainMenu/MainMenuWindow.cs | 3 +- 7 files changed, 90 insertions(+), 173 deletions(-) diff --git a/TLM/TLM/LoadingExtension.cs b/TLM/TLM/LoadingExtension.cs index 837768df2..f4abbfa78 100644 --- a/TLM/TLM/LoadingExtension.cs +++ b/TLM/TLM/LoadingExtension.cs @@ -16,23 +16,27 @@ namespace TrafficManager { using UnityEngine.SceneManagement; using Util; using Object = UnityEngine.Object; + using System.Linq; [UsedImplicitly] public class LoadingExtension : LoadingExtensionBase { static LoadingExtension() { TranslationDatabase.LoadAllTranslations(); + RegisterCustomManagers(); } - public LoadingExtension() { - } - - internal static LoadingExtension Instance = null; + static FastList GetSimManagers() => + typeof(SimulationManager) + .GetField("m_managers", BindingFlags.Static | BindingFlags.NonPublic) + ?.GetValue(null) + as FastList + ?? throw new Exception("could not get SimulationManager.m_managers"); - FastList simManager => - typeof(SimulationManager).GetField("m_managers", BindingFlags.Static | BindingFlags.NonPublic) - ?.GetValue(null) as FastList; + internal static AppMode? AppMode => SimulationManager.instance.m_ManagersWrapper.loading.currentMode; - internal static AppMode? AppMode => Instance?.loadingManager?.currentMode; + public static SimulationManager.UpdateMode UpdateMode => SimulationManager.instance.m_metaData.m_updateMode; + public static LoadMode Mode => (LoadMode)UpdateMode; + public static string Scene => SceneManager.GetActiveScene().name; /// /// determines whether Game mode as oppose to edit mode (eg asset editor). @@ -46,37 +50,15 @@ public LoadingExtension() { /// public static Translation TranslationDatabase = new Translation(); - public static UITransportDemand TransportDemandUI { get; private set; } - public static List RegisteredManagers { get; private set; } public static bool IsGameLoaded { get; private set; } - public static bool IsPathManagerReplaced { - get; private set; - } - - public override void OnCreated(ILoading loading) { - Log._Debug("LoadingExtension.OnCreated() called"); - - // SelfDestruct.DestructOldInstances(this); - base.OnCreated(loading); - if (IsGameLoaded) { - // When another mod is detected, OnCreated is called again for god - or CS team - knows what reason! - Log._Debug("Hot reload of another mod detected. Skipping LoadingExtension.OnCreated() ..."); - return; - } - InGameUtil.Instantiate(); + public static bool IsPathManagerReplaced { get; private set; } + private static void RegisterCustomManagers() { RegisteredManagers = new List(); - CustomPathManager = new CustomPathManager(); - - RegisterCustomManagers(); - - Instance = this; - } - private void RegisterCustomManagers() { // TODO represent data dependencies differently RegisteredManagers.Add(ExtNodeManager.Instance); RegisteredManagers.Add(ExtSegmentManager.Instance); @@ -107,24 +89,19 @@ private void RegisterCustomManagers() { RegisteredManagers.Add(JunctionRestrictionsManager.Instance); } - public override void OnReleased() { - TrafficManagerMod.Instance.InGameHotReload = false; - Instance = null; - base.OnReleased(); - } + public override void OnLevelUnloading() => Unload(); + + public override void OnLevelLoaded(LoadMode mode) => Load(); + + public static void Unload() { + Log.Info("LoadingExtension.Unload()"); - public override void OnLevelUnloading() { - Log.Info("OnLevelUnloading"); - base.OnLevelUnloading(); if (IsPathManagerReplaced) { CustomPathManager._instance.WaitForAllPaths(); } try { - var reverseManagers = new List(RegisteredManagers); - reverseManagers.Reverse(); - - foreach (ICustomManager manager in reverseManagers) { + foreach (ICustomManager manager in RegisteredManagers.AsEnumerable().Reverse()) { Log.Info($"OnLevelUnloading: {manager.GetType().Name}"); manager.OnLevelUnloading(); } @@ -132,24 +109,12 @@ public override void OnLevelUnloading() { Flags.OnLevelUnloading(); GlobalConfig.OnLevelUnloading(); - var gameObject = UIView.GetAView().gameObject; - - void Destroy() where T : MonoBehaviour { - Object obj = (Object)gameObject.GetComponent(); - if (obj != null) { - Object.Destroy(obj); - } - } - - Destroy(); - Destroy(); - Destroy(); - - //It's MonoBehaviour - comparing to null is wrong - if (TransportDemandUI) { - Object.Destroy(TransportDemandUI); - TransportDemandUI = null; - } + var uiviewGO = UIView.GetAView().gameObject; + Object.Destroy(uiviewGO.GetComponent()); + Object.Destroy(uiviewGO.GetComponent()); + Object.Destroy(uiviewGO.GetComponent()); + Object.Destroy(uiviewGO.GetComponent()); + Object.Destroy(uiviewGO.GetComponent()); Log.Info("Removing Controls from UI."); if (ModUI.Instance != null) { @@ -164,18 +129,21 @@ void Destroy() where T : MonoBehaviour { // ignored - prevents collision with other mods } - Patcher.Instance?.Uninstall(); + Patcher.Uninstall(); IsGameLoaded = false; + TrafficManagerMod.InGameHotReload = false; } - public override void OnLevelLoaded(LoadMode mode) { - SimulationManager.UpdateMode updateMode = SimulationManager.instance.m_metaData.m_updateMode; - string scene = SceneManager.GetActiveScene().name; - Log.Info($"OnLevelLoaded({mode}) called. updateMode={updateMode}, scene={scene}"); + public static void Load() { + Log.Info($"LoadingExtension.Load() called. {Mode} called. updateMode={UpdateMode}, scene={Scene}"); - if (scene == "ThemeEditor") + if(Scene == "ThemeEditor") return; + InGameUtil.Instantiate(); + + RegisterCustomManagers(); + IsGameLoaded = false; if (BuildConfig.applicationVersion != BuildConfig.VersionToString( @@ -247,7 +215,7 @@ public override void OnLevelLoaded(LoadMode mode) { IsGameLoaded = true; //it will replace stock PathManager or already Replaced before HotReload - if (!IsPathManagerReplaced || TrafficManagerMod.Instance.InGameHotReload) { + if (!IsPathManagerReplaced || TrafficManagerMod.InGameHotReload) { try { Log.Info("Pathfinder Compatible. Setting up CustomPathManager and SimManager."); FieldInfo pathManagerInstance = typeof(Singleton).GetField( @@ -278,17 +246,14 @@ public override void OnLevelLoaded(LoadMode mode) { pathManagerInstance.SetValue(null, CustomPathManager); - Log._Debug("Getting Current SimulationManager"); - var simManager = this.simManager; - if (simManager == null) { - throw new Exception("simManager is null"); - } + Log._Debug("Getting Current SimulationManagers"); + var simManagers = GetSimManagers(); Log._Debug("Removing Stock PathManager"); - simManager.Remove(stockPathManager); + simManagers.Remove(stockPathManager); Log._Debug("Adding Custom PathManager"); - simManager.Add(CustomPathManager); + simManagers.Add(CustomPathManager); Object.Destroy(stockPathManager, 10f); @@ -318,22 +283,14 @@ public override void OnLevelLoaded(LoadMode mode) { ModUI.OnLevelLoaded(); if (PlayMode) { - // Init transport demand UI - if (TransportDemandUI == null) { - UIView uiView = UIView.GetAView(); - TransportDemandUI = (UITransportDemand)uiView.AddUIComponent(typeof(UITransportDemand)); - } - - // add "remove vehicle" button - UIView.GetAView().gameObject.AddComponent(); - - // add "remove citizen instance" button - UIView.GetAView().gameObject.AddComponent(); - - UIView.GetAView().gameObject.AddComponent(); + UIView uiView = UIView.GetAView(); + uiView.AddUIComponent(typeof(UITransportDemand)); + uiView.gameObject.AddComponent(); + uiView.gameObject.AddComponent(); + uiView.gameObject.AddComponent(); } - Patcher.Create().Install(); + Patcher.Install(); // Log.Info("Fixing non-created nodes with problems..."); // FixNonCreatedNodeProblems(); diff --git a/TLM/TLM/Patcher.cs b/TLM/TLM/Patcher.cs index 1445ac2d4..349768ae7 100644 --- a/TLM/TLM/Patcher.cs +++ b/TLM/TLM/Patcher.cs @@ -9,20 +9,10 @@ namespace TrafficManager { using TrafficManager.RedirectionFramework; using TrafficManager.Util; - public class Patcher { - public static Patcher Instance { get; private set; } - - public static Patcher Create() => Instance = new Patcher(); - + public static class Patcher { private const string HARMONY_ID = "de.viathinksoft.tmpe"; - private bool initialized_ = false; - - public void Install() { - if (initialized_) { - return; - } - + public static void Install() { Log.Info("Init detours"); bool fail = false; @@ -31,10 +21,8 @@ public void Install() { Harmony.DEBUG = true; #endif // Harmony attribute-driven patching - Log.Info($"Performing Harmony attribute-driven patching"); - var harmony = new Harmony(HARMONY_ID); - Shortcuts.Assert(harmony != null, "HarmonyInst!=null"); - harmony.PatchAll(); + Log.Info($"Performing Harmony attribute-driven patches"); + new Harmony(HARMONY_ID).PatchAll(); Log.Info($"Harmony attribute-driven patching successfull!"); } catch (Exception e) { @@ -56,36 +44,24 @@ public void Install() { } if (fail) { - Log.Info("Detours failed"); - Singleton.instance.m_ThreadingWrapper.QueueMainThread( - () => { - UIView.library - .ShowModal("ExceptionPanel") - .SetMessage( - "TM:PE failed to load", - "Traffic Manager: President Edition failed to load. You can " + - "continue playing but it's NOT recommended. Traffic Manager will " + - "not work as expected.", - true); - }); + Log.Info("patcher failed"); + UIView.library + .ShowModal("ExceptionPanel") + .SetMessage( + "TM:PE failed to load", + "Traffic Manager: President Edition failed to load. You can " + + "continue playing but it's NOT recommended. Traffic Manager will " + + "not work as expected.", + true); } else { - Log.Info("Detours successful"); + Log.Info("TMPE patches installed successfully"); } - - initialized_ = true; } - public void Uninstall() { - if (!initialized_) { - return; - } - - var harmony = new Harmony(HARMONY_ID); - Shortcuts.Assert(harmony != null, "HarmonyInst!=null"); - harmony.UnpatchAll(HARMONY_ID); - - initialized_ = false; - Log.Info("Reverting detours finished."); + public static void Uninstall() { + new Harmony(HARMONY_ID).UnpatchAll(HARMONY_ID); + AssemblyRedirector.Revert(); + Log.Info("TMPE patches uninstalled."); } } } diff --git a/TLM/TLM/State/Options.cs b/TLM/TLM/State/Options.cs index a981ea7ea..8495fb5c5 100644 --- a/TLM/TLM/State/Options.cs +++ b/TLM/TLM/State/Options.cs @@ -132,7 +132,7 @@ internal static void RebuildMenu() { } } - public static void MakeSettings(UIHelperBase helper) { + public static void MakeSettings(UIHelper helper) { ExtUITabstrip tabStrip = ExtUITabstrip.Create(helper); OptionsGeneralTab.MakeSettings_General(tabStrip); OptionsGameplayTab.MakeSettings_Gameplay(tabStrip); diff --git a/TLM/TLM/State/SerializableDataExtension.cs b/TLM/TLM/State/SerializableDataExtension.cs index d8747dce7..4ebc9f9f4 100644 --- a/TLM/TLM/State/SerializableDataExtension.cs +++ b/TLM/TLM/State/SerializableDataExtension.cs @@ -15,28 +15,14 @@ public class SerializableDataExtension { private const string DATA_ID = "TrafficManager_v1.0"; - private static ISerializableData _serializableData; + private static ISerializableData SerializableData => SimulationManager.instance.m_SerializableDataWrapper; private static Configuration _configuration; public static bool StateLoading; - public override void OnCreated(ISerializableData serializableData) { - Log._Debug("SerializableDataExtension.OnCreated() called"); - if(LoadingExtension.IsGameLoaded) { - Log._Debug("Hot reload of another mod detected. Skipping SerializableDataExtension.OnCreated() ..."); - return; - } - _serializableData = serializableData; - - if (TrafficManagerMod.Instance.InGameHotReload) { - Log._Debug("HOT RELOAD ..."); - OnLoadData(); - LoadingExtension.Instance.OnLevelLoaded(LoadMode.LoadGame); - } - } - - public override void OnReleased() { } + public override void OnLoadData() => Load(); + public override void OnSaveData() => Save(); - public override void OnLoadData() { + public static void Load() { Log.Info("Loading Traffic Manager: PE Data"); StateLoading = true; bool loadingSucceeded = true; @@ -64,7 +50,7 @@ public override void OnLoadData() { Log.Info("Initialization done. Loading mod data now."); try { - byte[] data = _serializableData.LoadData(DATA_ID); + byte[] data = SerializableData.LoadData(DATA_ID); DeserializeData(data); } catch (Exception e) { @@ -74,7 +60,7 @@ public override void OnLoadData() { // load options try { - byte[] options = _serializableData.LoadData("TMPE_Options"); + byte[] options = SerializableData.LoadData("TMPE_Options"); if (options != null) { if (!OptionsManager.Instance.LoadData(options)) { loadingSucceeded = false; @@ -288,7 +274,7 @@ private static void LoadDataState(out bool error) { } } - public override void OnSaveData() { + public static void Save() { bool success = true; // try { @@ -369,7 +355,7 @@ public override void OnSaveData() { try { // save options - _serializableData.SaveData("TMPE_Options", OptionsManager.Instance.SaveData(ref success)); + SerializableData.SaveData("TMPE_Options", OptionsManager.Instance.SaveData(ref success)); } catch (Exception ex) { Log.Error("Unexpected error while saving options: " + ex.Message); success = false; @@ -382,7 +368,7 @@ public override void OnSaveData() { binaryFormatter.Serialize(memoryStream, configuration); memoryStream.Position = 0; Log.Info($"Save data byte length {memoryStream.Length}"); - _serializableData.SaveData(DATA_ID, memoryStream.ToArray()); + SerializableData.SaveData(DATA_ID, memoryStream.ToArray()); } catch (Exception ex) { Log.Error("Unexpected error while saving data: " + ex); success = false; diff --git a/TLM/TLM/TrafficManagerMod.cs b/TLM/TLM/TrafficManagerMod.cs index 9c71d199b..69b5ccadf 100644 --- a/TLM/TLM/TrafficManagerMod.cs +++ b/TLM/TLM/TrafficManagerMod.cs @@ -49,14 +49,12 @@ public TrafficManagerMod() { public string Description => "Manage your city's traffic"; - internal static TrafficManagerMod Instance = null; - - internal bool InGameHotReload { get; set; } = false; + internal static bool InGameHotReload { get; set; } = false; /// /// determines if simulation is inside game/editor. useful to detect hot-reload. /// - internal static bool InGame() => + internal static bool InGameOrEditor() => SceneManager.GetActiveScene().name != "IntroScreen" && SceneManager.GetActiveScene().name != "Startup"; @@ -97,11 +95,15 @@ public void OnEnabled() { Log._Debug("Scene is " + SceneManager.GetActiveScene().name); - Instance = this; - InGameHotReload = InGame(); + InGameHotReload = InGameOrEditor(); HarmonyHelper.EnsureHarmonyInstalled(); + if(InGameHotReload) { + SerializableDataExtension.Load(); + LoadingExtension.Load(); + } + #if DEBUG const bool installHarmonyASAP = false; // set true for fast testing if (installHarmonyASAP) @@ -116,16 +118,14 @@ public void OnDisabled() { LocaleManager.eventLocaleChanged -= Translation.HandleGameLocaleChange; Translation.IsListeningToGameLocaleChanged = false; // is this necessary? - if (InGame() && LoadingExtension.Instance != null) { - //Hot reload Unloading - LoadingExtension.Instance.OnLevelUnloading(); - LoadingExtension.Instance.OnReleased(); + if (InGameOrEditor() && LoadingExtension.IsGameLoaded) { + //Hot Unload + LoadingExtension.Unload(); } - Instance = null; } [UsedImplicitly] - public void OnSettingsUI(UIHelperBase helper) { + public void OnSettingsUI(UIHelper helper) { // Note: This bugs out if done in OnEnabled(), hence doing it here instead. if (!Translation.IsListeningToGameLocaleChanged) { Translation.IsListeningToGameLocaleChanged = true; diff --git a/TLM/TLM/UI/Helpers/ExtUITabStrip.cs b/TLM/TLM/UI/Helpers/ExtUITabStrip.cs index a5a46b7ff..3665965d5 100644 --- a/TLM/TLM/UI/Helpers/ExtUITabStrip.cs +++ b/TLM/TLM/UI/Helpers/ExtUITabStrip.cs @@ -104,9 +104,8 @@ public UIHelper AddTabPage(string name, bool scrollBars = true) { return panelHelper; } - public static ExtUITabstrip Create(UIHelperBase helperBase) { - UIHelper actualHelper = helperBase as UIHelper; - UIComponent optionsContainer = actualHelper.self as UIComponent; + public static ExtUITabstrip Create(UIHelper helper) { + UIComponent optionsContainer = helper.self as UIComponent; float orgOptsContainerWidth = optionsContainer.height; float orgOptsContainerHeight = optionsContainer.width; diff --git a/TLM/TLM/UI/MainMenu/MainMenuWindow.cs b/TLM/TLM/UI/MainMenu/MainMenuWindow.cs index 6c9528594..f44421d65 100644 --- a/TLM/TLM/UI/MainMenu/MainMenuWindow.cs +++ b/TLM/TLM/UI/MainMenu/MainMenuWindow.cs @@ -372,8 +372,7 @@ private void SetupControls_DebugLabels(UiBuilder builder, } } - // Hot Reload version label (debug only) - if (TrafficManagerMod.Instance.InGameHotReload) { + if (TrafficManagerMod.InGameHotReload) { // Hot Reload version label (debug only) string text = $"HOT RELOAD {Assembly.GetExecutingAssembly().GetName().Version}"; using (var hotReloadB = builder.Label(text)) { From 443acadd8829e9e727ee4d595871b1ad46045f35 Mon Sep 17 00:00:00 2001 From: krzychu124 Date: Sun, 11 Apr 2021 03:07:29 +0200 Subject: [PATCH 15/23] Main Camera cache reinitialization to prevent use of inactive camera --- TLM/TLM/UI/TrafficManagerTool.cs | 8 ++++++++ TLM/TLM/Util/InGameUtil.cs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/TLM/TLM/UI/TrafficManagerTool.cs b/TLM/TLM/UI/TrafficManagerTool.cs index f1719490e..562703584 100644 --- a/TLM/TLM/UI/TrafficManagerTool.cs +++ b/TLM/TLM/UI/TrafficManagerTool.cs @@ -323,6 +323,14 @@ protected override void OnEnable() { e.Value.Cleanup(); } } + + // Disabled camera may indicate the main camera change. + // Reinitialize camera cache to make sure we will use the correct one + if (!InGameUtil.Instance.CachedMainCamera.enabled) { + Log.Info("CachedMainCamera disabled - camera cache reinitialization"); + InGameUtil.Instantiate(); + } + // no call to base method to disable base class behavior } diff --git a/TLM/TLM/Util/InGameUtil.cs b/TLM/TLM/Util/InGameUtil.cs index d117bc4b1..3f193ab7f 100644 --- a/TLM/TLM/Util/InGameUtil.cs +++ b/TLM/TLM/Util/InGameUtil.cs @@ -18,7 +18,7 @@ private InGameUtil() { } /// - /// Call after loading savegame to cache + /// Call after loading savegame to cache main camera reference /// public static void Instantiate() { Instance = new InGameUtil(); From 2c541a3ae5d6d486a1b8bfc2d720f450aea4c60c Mon Sep 17 00:00:00 2001 From: krzychu124 Date: Sun, 11 Apr 2021 04:20:39 +0200 Subject: [PATCH 16/23] Hide overlays if no longer necessary --- TLM/TLM/UI/TrafficManagerTool.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TLM/TLM/UI/TrafficManagerTool.cs b/TLM/TLM/UI/TrafficManagerTool.cs index f1719490e..d9af97f7e 100644 --- a/TLM/TLM/UI/TrafficManagerTool.cs +++ b/TLM/TLM/UI/TrafficManagerTool.cs @@ -330,6 +330,9 @@ protected override void OnDisable() { // If TMPE was disabled by switching to another tool, hide main menue panel. if (ModUI.Instance != null && ModUI.Instance.IsVisible()) ModUI.Instance.CloseMainMenu(); + + //hide speed limit overlay if necessary + SubTools.PrioritySigns.MassEditOverlay.Show = RoadSelectionPanels.Root.ShouldShowMassEditOverlay(); // no call to base method to disable base class behavior } From 57c0e929f8e0c3d71166e5c96ba910fb1b3435d3 Mon Sep 17 00:00:00 2001 From: kianzarrin Date: Mon, 12 Apr 2021 13:05:26 +0300 Subject: [PATCH 17/23] 730 cleanup loading custom path manager (#1072) * encapsulate code for the lifecycle of CustomPathManager. * restore old path manager and its path finds when unloading. * hot unload now works --- TLM/TLM/Custom/PathFinding/CustomPathFind.cs | 6 +- .../Custom/PathFinding/CustomPathManager.cs | 144 ++++++++++++------ TLM/TLM/LoadingExtension.cs | 82 +--------- TLM/TLM/Patcher.cs | 4 - TLM/TLM/TLM.csproj | 12 +- TLM/TLM/TrafficManagerMod.cs | 2 +- TLM/TLM/UI/Helpers/ExtUITabStrip.cs | 2 +- 7 files changed, 111 insertions(+), 141 deletions(-) diff --git a/TLM/TLM/Custom/PathFinding/CustomPathFind.cs b/TLM/TLM/Custom/PathFinding/CustomPathFind.cs index e821a8446..91efe6816 100644 --- a/TLM/TLM/Custom/PathFinding/CustomPathFind.cs +++ b/TLM/TLM/Custom/PathFinding/CustomPathFind.cs @@ -23,12 +23,10 @@ namespace TrafficManager.Custom.PathFinding { #endif /// - /// This replaces game PathFind class if PF_DIJKSTRA is defined + /// This replaces game PathFind class /// This is ALL targets except Benchmark /// -#if PF_DIJKSTRA [TargetType(typeof(PathFind))] -#endif public class CustomPathFind : PathFind { private const float BYTE_TO_FLOAT_OFFSET_CONVERSION_FACTOR = Constants.BYTE_TO_FLOAT_SCALE; @@ -238,9 +236,7 @@ private void OnDestroy() { } } -#if PF_DIJKSTRA [RedirectMethod] -#endif public new bool CalculatePath(uint unit, bool skipQueue) { return ExtCalculatePath(unit, skipQueue); } diff --git a/TLM/TLM/Custom/PathFinding/CustomPathManager.cs b/TLM/TLM/Custom/PathFinding/CustomPathManager.cs index ea3a68416..643bb3f58 100644 --- a/TLM/TLM/Custom/PathFinding/CustomPathManager.cs +++ b/TLM/TLM/Custom/PathFinding/CustomPathManager.cs @@ -7,17 +7,13 @@ namespace TrafficManager.Custom.PathFinding { using CSUtil.Commons; using JetBrains.Annotations; using System.Reflection; - using System.Threading; using System; using TrafficManager.API.Traffic.Data; using TrafficManager.Manager.Impl; using TrafficManager.RedirectionFramework.Attributes; using TrafficManager.State; using UnityEngine; - -#if !PF_DIJKSTRA - using CustomPathFind = CustomPathFind_Old; -#endif + using ColossalFramework.UI; [TargetType(typeof(PathManager))] public class CustomPathManager : PathManager { @@ -30,20 +26,58 @@ public class CustomPathManager : PathManager { public static CustomPathManager _instance; + private PathManager stockPathManager_; + + private static FastList GetSimulationManagers() => + typeof(SimulationManager) + .GetField("m_managers", BindingFlags.Static | BindingFlags.NonPublic) + ?.GetValue(null) + as FastList + ?? throw new Exception("could not get SimulationManager.m_managers"); + + private static FieldInfo PathManagerInstance => + typeof(Singleton) + .GetField( + "sInstance", + BindingFlags.Static | BindingFlags.NonPublic) ?? + throw new Exception("pathManagerInstance is null"); + #if QUEUEDSTATS - public static uint TotalQueuedPathFinds { - get; private set; - } + public static uint TotalQueuedPathFinds { get; private set; } #endif - public static bool InitDone { - get; private set; + public static void OnLevelLoaded() { + try { + Log.Info("CustomPathManager.OnLevelLoaded() called."); + PathManager.instance.gameObject.AddComponent(); + } catch(Exception ex) { + string error = + "Traffic Manager: President Edition failed to load. You can continue " + + "playing but it's NOT recommended. Traffic Manager will not work as expected."; + Log.Error(error); + Log.Error($"Path manager replacement error: {ex}"); + UIView.library.ShowModal("ExceptionPanel") + .SetMessage("TM:PE failed to load", error, true); + } } - // On waking up, replace the stock pathfinders with the custom one [UsedImplicitly] - public new virtual void Awake() { + protected override void Awake() { + // On waking up, replace the stock pathfinders with the custom one + // but retain the original version for future replace + // also suppress call to base class. _instance = this; + stockPathManager_ = PathManager.instance + ?? throw new Exception("stockPathManager is null"); + Log._Debug($"Got stock PathManager instance {stockPathManager_?.GetName()}"); + PathManagerInstance.SetValue(null, this); + Log._Debug("Should be custom: " + PathManager.instance.GetType()); + + UpdateWithPathManagerValues(stockPathManager_); + + var simManagers = GetSimulationManagers(); + simManagers.Remove(stockPathManager_); + simManagers.Add(this); } public void UpdateWithPathManagerValues(PathManager stockPathManager) { @@ -57,8 +91,6 @@ public void UpdateWithPathManagerValues(PathManager stockPathManager) { m_pathUnits = stockPathManager.m_pathUnits; m_bufferLock = stockPathManager.m_bufferLock; - Log._Debug("Waking up CustomPathManager."); - QueueItems = new PathUnitQueueItem[MAX_PATHUNIT_COUNT]; PathFind[] stockPathFinds = GetComponents(); @@ -67,26 +99,18 @@ public void UpdateWithPathManagerValues(PathManager stockPathManager) { Log._Debug("Creating " + numCustomPathFinds + " custom PathFind objects."); _replacementPathFinds = new CustomPathFind[numCustomPathFinds]; + FieldInfo f_pathfinds = typeof(PathManager).GetField( + "m_pathfinds", + BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new Exception("f_pathFinds is null"); lock(m_bufferLock) { for (int i = 0; i < numCustomPathFinds; i++) { _replacementPathFinds[i] = gameObject.AddComponent(); -#if !PF_DIJKSTRA - _replacementPathFinds[i].pfId = i; - if (i == 0) { - _replacementPathFinds[i].IsMasterPathFind = true; - } -#endif } - Log._Debug("Setting _replacementPathFinds"); - FieldInfo fieldInfo = typeof(PathManager).GetField( - "m_pathfinds", - BindingFlags.NonPublic | BindingFlags.Instance); - - Log._Debug("Setting m_pathfinds to custom collection"); - fieldInfo?.SetValue(this, _replacementPathFinds); + f_pathfinds?.SetValue(this, _replacementPathFinds); for (int i = 0; i < numOfStockPathFinds; i++) { Log._Debug($"PF {i}: {stockPathFinds[i].m_queuedPathFindCount} queued path-finds"); @@ -96,8 +120,39 @@ public void UpdateWithPathManagerValues(PathManager stockPathManager) { Destroy(stockPathFinds[i]); } } + } + + public void UpdateOldPathManagerValues(PathManager stockPathManager) { + stockPathManager.m_drawCallData = m_drawCallData; + stockPathManager.m_pathUnitCount = m_pathUnitCount; + stockPathManager.m_renderPathGizmo = m_renderPathGizmo; + + int n = _replacementPathFinds.Length; + + Log._Debug("Creating " + n + " stock PathFind objects."); + PathFind[] stockPathFinds = new PathFind[n]; - InitDone = true; + FieldInfo f_pathfinds = typeof(PathManager).GetField( + "m_pathfinds", + BindingFlags.NonPublic | BindingFlags.Instance) + ?? throw new Exception("f_pathFinds is null"); + + // both stcok and custom PathMangers use the same lock object + lock (m_bufferLock) { + for (int i = 0; i < n; i++) { + Log._Debug($"PF {i}: {_replacementPathFinds[i].m_queuedPathFindCount} queued path-finds"); + + // would cause deadlock since we have a lock on m_bufferLock + // customPathFinds[i].WaitForAllPaths(); + Destroy(_replacementPathFinds[i]); + } + + for (int i = 0; i < n; i++) { + stockPathFinds[i] = gameObject.AddComponent(); + } + + f_pathfinds?.SetValue(stockPathManager, stockPathFinds); + } } [RedirectMethod] @@ -248,15 +303,9 @@ public bool CustomCreatePath(out uint unit, pathFind = pathFindCandidate; } -#if PF_DIJKSTRA if (pathFind != null && pathFind.CalculatePath(unit, args.skipQueue)) { return true; } -#else - if (pathFind != null && pathFind.ExtCalculatePath(unit, args.skipQueue)) { - return true; - } -#endif // NON-STOCK CODE START lock(m_bufferLock) { @@ -360,19 +409,26 @@ public static bool FindCitizenPathPosition(Vector3 pos, return position.m_segment != 0; } - /*internal void ResetQueueItem(uint unit) { - queueItems[unit].Reset(); - }*/ - - private void StopPathFinds() { - foreach (CustomPathFind pathFind in _replacementPathFinds) { - Destroy(pathFind); - } + public void OnLevelUnloading() { + Log.Info("CustomPathManager.OnLevelUnloading()"); + DestroyImmediate(this); } protected virtual void OnDestroy() { Log._Debug("CustomPathManager: OnDestroy"); - StopPathFinds(); + WaitForAllPaths(); + + PathManagerInstance.SetValue(null, stockPathManager_); + Log._Debug("Should be stock: " + PathManager.instance.GetType()); + + UpdateOldPathManagerValues(stockPathManager_); + var simManagers = GetSimulationManagers(); + + simManagers.Remove(this); + + simManagers.Add(stockPathManager_); + + _instance = null; } } -} +} \ No newline at end of file diff --git a/TLM/TLM/LoadingExtension.cs b/TLM/TLM/LoadingExtension.cs index f4abbfa78..b3df12a9c 100644 --- a/TLM/TLM/LoadingExtension.cs +++ b/TLM/TLM/LoadingExtension.cs @@ -25,13 +25,6 @@ static LoadingExtension() { RegisterCustomManagers(); } - static FastList GetSimManagers() => - typeof(SimulationManager) - .GetField("m_managers", BindingFlags.Static | BindingFlags.NonPublic) - ?.GetValue(null) - as FastList - ?? throw new Exception("could not get SimulationManager.m_managers"); - internal static AppMode? AppMode => SimulationManager.instance.m_ManagersWrapper.loading.currentMode; public static SimulationManager.UpdateMode UpdateMode => SimulationManager.instance.m_metaData.m_updateMode; @@ -43,8 +36,6 @@ as FastList /// internal static bool PlayMode => AppMode != null && AppMode == ICities.AppMode.Game; - public static CustomPathManager CustomPathManager { get; set; } - /// /// Contains loaded languages and lookup functions for text translations /// @@ -54,8 +45,6 @@ as FastList public static bool IsGameLoaded { get; private set; } - public static bool IsPathManagerReplaced { get; private set; } - private static void RegisterCustomManagers() { RegisteredManagers = new List(); @@ -96,9 +85,7 @@ private static void RegisterCustomManagers() { public static void Unload() { Log.Info("LoadingExtension.Unload()"); - if (IsPathManagerReplaced) { - CustomPathManager._instance.WaitForAllPaths(); - } + CustomPathManager._instance?.OnLevelUnloading(); try { foreach (ICustomManager manager in RegisteredManagers.AsEnumerable().Reverse()) { @@ -214,72 +201,7 @@ public static void Load() { IsGameLoaded = true; - //it will replace stock PathManager or already Replaced before HotReload - if (!IsPathManagerReplaced || TrafficManagerMod.InGameHotReload) { - try { - Log.Info("Pathfinder Compatible. Setting up CustomPathManager and SimManager."); - FieldInfo pathManagerInstance = typeof(Singleton).GetField( - "sInstance", - BindingFlags.Static | BindingFlags.NonPublic); - if (pathManagerInstance == null) { - throw new Exception("pathManagerInstance is null"); - } - - PathManager stockPathManager = PathManager.instance; - if (stockPathManager == null) { - throw new Exception("stockPathManager is null"); - } - - Log._Debug($"Got stock PathManager instance {stockPathManager?.GetName()}"); - - CustomPathManager = - stockPathManager.gameObject.AddComponent(); - Log._Debug("Added CustomPathManager to gameObject List"); - - if (CustomPathManager == null) { - Log.Error("CustomPathManager null. Error creating it."); - return; - } - - CustomPathManager.UpdateWithPathManagerValues(stockPathManager); - Log._Debug("UpdateWithPathManagerValues success"); - - pathManagerInstance.SetValue(null, CustomPathManager); - - Log._Debug("Getting Current SimulationManagers"); - var simManagers = GetSimManagers(); - - Log._Debug("Removing Stock PathManager"); - simManagers.Remove(stockPathManager); - - Log._Debug("Adding Custom PathManager"); - simManagers.Add(CustomPathManager); - - Object.Destroy(stockPathManager, 10f); - - Log._Debug("Should be custom: " + Singleton.instance.GetType()); - - IsPathManagerReplaced = true; - } - catch (Exception ex) { - string error = - "Traffic Manager: President Edition failed to load. You can continue " + - "playing but it's NOT recommended. Traffic Manager will not work as expected."; - Log.Error(error); - Log.Error($"Path manager replacement error: {ex}"); - - Singleton.instance.m_ThreadingWrapper.QueueMainThread( - () => { - UIView.library - .ShowModal( - "ExceptionPanel") - .SetMessage( - "TM:PE failed to load", - error, - true); - }); - } - } + CustomPathManager.OnLevelLoaded(); ModUI.OnLevelLoaded(); if (PlayMode) { diff --git a/TLM/TLM/Patcher.cs b/TLM/TLM/Patcher.cs index 349768ae7..cacd70c99 100644 --- a/TLM/TLM/Patcher.cs +++ b/TLM/TLM/Patcher.cs @@ -1,13 +1,9 @@ namespace TrafficManager { - using ColossalFramework; using ColossalFramework.UI; using CSUtil.Commons; using HarmonyLib; using System; - using System.Collections.Generic; - using System.Reflection; using TrafficManager.RedirectionFramework; - using TrafficManager.Util; public static class Patcher { private const string HARMONY_ID = "de.viathinksoft.tmpe"; diff --git a/TLM/TLM/TLM.csproj b/TLM/TLM/TLM.csproj index fca4e9103..8a3cf5122 100644 --- a/TLM/TLM/TLM.csproj +++ b/TLM/TLM/TLM.csproj @@ -19,7 +19,7 @@ full false bin\Debug\ - DEBUG;PF_DIJKSTRA;QUEUEDSTATS;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS + DEBUG;QUEUEDSTATS;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS prompt 4 true @@ -29,7 +29,7 @@ pdbonly true bin\Release\ - TRACE;PF_DIJKSTRA;QUEUEDSTATS;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS + TRACE;QUEUEDSTATS;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS prompt 4 true @@ -38,7 +38,7 @@ true bin\Debug\ - DEBUG;PF_DIJKSTRA;QUEUEDSTATS;DEBUGVSTATE;DEBUGNEWPF;DEBUGCOSTS;DEBUGCONN2;DEBUGHWJUNCTIONROUTING;DEBUGHK;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS + DEBUG;QUEUEDSTATS;DEBUGVSTATE;DEBUGNEWPF;DEBUGCOSTS;DEBUGCONN2;DEBUGHWJUNCTIONROUTING;DEBUGHK;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS true 4 full @@ -49,7 +49,7 @@ true bin\PF2_Debug\ - DEBUG;QUEUEDSTATS;DEBUGVSTATE;DEBUGNEWPF;DEBUGCOSTS;DEBUGCONN2;DEBUGHWJUNCTIONROUTING;DEBUGROUTING;DEBUGHK;PF_DIJKSTRA;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS + DEBUG;QUEUEDSTATS;DEBUGVSTATE;DEBUGNEWPF;DEBUGCOSTS;DEBUGCONN2;DEBUGHWJUNCTIONROUTING;DEBUGROUTING;DEBUGHK;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS true 4 full @@ -59,7 +59,7 @@ bin\Release LABS\ - PF_DIJKSTRA;QUEUEDSTATS;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS;LABS + QUEUEDSTATS;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS;LABS true true pdbonly @@ -79,7 +79,7 @@ bin\Benchmark\ - PF_DIJKSTRA;QUEUEDSTATS;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS;BENCHMARK + QUEUEDSTATS;SPEEDLIMITS;ROUTING;JUNCTIONRESTRICTIONS;VEHICLERESTRICTIONS;ADVANCEDAI;CUSTOMTRAFFICLIGHTS;BENCHMARK true true pdbonly diff --git a/TLM/TLM/TrafficManagerMod.cs b/TLM/TLM/TrafficManagerMod.cs index 0f108a2af..8a19429c1 100644 --- a/TLM/TLM/TrafficManagerMod.cs +++ b/TLM/TLM/TrafficManagerMod.cs @@ -107,7 +107,7 @@ public void OnEnabled() { #if DEBUG const bool installHarmonyASAP = false; // set true for fast testing if (installHarmonyASAP) - HarmonyHelper.DoOnHarmonyReady(() => { Patcher.Create().Install(); }); + HarmonyHelper.DoOnHarmonyReady(Patcher.Install); #endif } diff --git a/TLM/TLM/UI/Helpers/ExtUITabStrip.cs b/TLM/TLM/UI/Helpers/ExtUITabStrip.cs index 3665965d5..618f1fc93 100644 --- a/TLM/TLM/UI/Helpers/ExtUITabStrip.cs +++ b/TLM/TLM/UI/Helpers/ExtUITabStrip.cs @@ -128,7 +128,7 @@ public static ExtUITabstrip Create(UIHelper helper) { #if DEBUG public static class Test { private static int n = 0; - public static void OnSettingsUI(UIHelperBase helperBase) { + public static void OnSettingsUI(UIHelper helperBase) { n = 0; ExtUITabstrip tabStrip = ExtUITabstrip.Create(helperBase); AddTab(tabStrip, "A"); From f4d55aefbbe14158105551737654b1aa81ed5b2e Mon Sep 17 00:00:00 2001 From: kianzarrin Date: Mon, 12 Apr 2021 13:26:19 +0300 Subject: [PATCH 18/23] harmon patcher continues on error (#1076) Harmony patcher continues on error (#1074). Also, it logs which functions have been patched failed patching (#1075). --- .../Custom/PathFinding/CustomPathManager.cs | 10 +-- TLM/TLM/Patcher.cs | 87 ++++++++++++++----- TLM/TLM/Util/Shortcuts.cs | 19 ++++ 3 files changed, 88 insertions(+), 28 deletions(-) diff --git a/TLM/TLM/Custom/PathFinding/CustomPathManager.cs b/TLM/TLM/Custom/PathFinding/CustomPathManager.cs index 643bb3f58..7e03b5fe5 100644 --- a/TLM/TLM/Custom/PathFinding/CustomPathManager.cs +++ b/TLM/TLM/Custom/PathFinding/CustomPathManager.cs @@ -50,7 +50,7 @@ public static void OnLevelLoaded() { try { Log.Info("CustomPathManager.OnLevelLoaded() called."); PathManager.instance.gameObject.AddComponent(); - } catch(Exception ex) { + } catch (Exception ex) { string error = "Traffic Manager: President Edition failed to load. You can continue " + "playing but it's NOT recommended. Traffic Manager will not work as expected."; @@ -104,7 +104,7 @@ public void UpdateWithPathManagerValues(PathManager stockPathManager) { BindingFlags.NonPublic | BindingFlags.Instance) ?? throw new Exception("f_pathFinds is null"); - lock(m_bufferLock) { + lock (m_bufferLock) { for (int i = 0; i < numCustomPathFinds; i++) { _replacementPathFinds[i] = gameObject.AddComponent(); @@ -164,7 +164,7 @@ public void UpdateOldPathManagerValues(PathManager stockPathManager) { if (m_pathUnits.m_buffer[unit].m_simulationFlags == 0) { return; } - lock(m_bufferLock) { + lock (m_bufferLock) { int numIters = 0; while (unit != 0u) { @@ -201,7 +201,7 @@ public bool CustomCreatePath(out uint unit, ref Randomizer randomizer, PathCreationArgs args) { uint pathUnitId; - lock(m_bufferLock) { + lock (m_bufferLock) { int numIters = 0; while (true) { @@ -308,7 +308,7 @@ public bool CustomCreatePath(out uint unit, } // NON-STOCK CODE START - lock(m_bufferLock) { + lock (m_bufferLock) { QueueItems[pathUnitId].queued = false; // NON-STOCK CODE END diff --git a/TLM/TLM/Patcher.cs b/TLM/TLM/Patcher.cs index cacd70c99..4533f9828 100644 --- a/TLM/TLM/Patcher.cs +++ b/TLM/TLM/Patcher.cs @@ -4,29 +4,44 @@ namespace TrafficManager { using HarmonyLib; using System; using TrafficManager.RedirectionFramework; + using CitiesHarmony.API; + using System.Runtime.CompilerServices; + using System.Reflection; + using TrafficManager.Util; + using System.Linq; public static class Patcher { private const string HARMONY_ID = "de.viathinksoft.tmpe"; + private const string ERROR_MESSAGE = + "****** ERRRROOORRRRRR!!!!!!!!!! **************\n" + + "**********************************************\n" + + " HARMONY MOD DEPENDENCY IS NOT INSTALLED!\n\n" + + SOLUTION + "\n" + + "**********************************************\n" + + "**********************************************\n"; + private const string SOLUTION = + "solution:\n" + + " - exit to desktop.\n" + + " - unsub harmony mod.\n" + + " - make sure harmony mod is deleted from the content folder\n" + + " - resub to harmony mod.\n" + + " - run the game again."; + + internal static void AssertCitiesHarmonyInstalled() { + if(!HarmonyHelper.IsHarmonyInstalled) { + Shortcuts.ShowErrorDialog("Error: Missing Harmony", SOLUTION); + throw new Exception(ERROR_MESSAGE); + } + } + public static void Install() { - Log.Info("Init detours"); bool fail = false; - - try { #if DEBUG - Harmony.DEBUG = true; + Harmony.DEBUG = false; // set to true to get harmony debug info. #endif - // Harmony attribute-driven patching - Log.Info($"Performing Harmony attribute-driven patches"); - new Harmony(HARMONY_ID).PatchAll(); - Log.Info($"Harmony attribute-driven patching successfull!"); - } - catch (Exception e) { - Log.Error("Could not apply Harmony patches because the following exception occured:\n " + - e + - "\n -- End of inner exception stack trace -- "); - fail = true; - } + AssertCitiesHarmonyInstalled(); + fail = !PatchAll(); try { Log.Info("Deploying attribute-driven detours"); @@ -41,19 +56,45 @@ public static void Install() { if (fail) { Log.Info("patcher failed"); - UIView.library - .ShowModal("ExceptionPanel") - .SetMessage( - "TM:PE failed to load", - "Traffic Manager: President Edition failed to load. You can " + - "continue playing but it's NOT recommended. Traffic Manager will " + - "not work as expected.", - true); + Shortcuts.ShowErrorDialog( + "TM:PE failed to load", + "Traffic Manager: President Edition failed to load. You can " + + "continue playing but it's NOT recommended. Traffic Manager will " + + "not work as expected."); } else { Log.Info("TMPE patches installed successfully"); } } + /// + /// applies all attribute diven harmony patches. + /// continues on error. + /// + /// false if exception happens, true otherwise + [MethodImpl(MethodImplOptions.NoInlining)] + static bool PatchAll() { + try { + bool success = true; + var harmony = new Harmony(HARMONY_ID); + var assembly = Assembly.GetExecutingAssembly(); + foreach (var type in AccessTools.GetTypesFromAssembly(assembly)) { + try { + var methods = harmony.CreateClassProcessor(type).Patch(); + if (methods != null && methods.Any()) { + var strMethods = methods.Select(_method => _method.Name).ToArray(); + } + } catch (Exception ex) { + ex.LogException(); + success = false; + } + } + return success; + } catch (Exception ex) { + ex.LogException(); + return false; + } + } + public static void Uninstall() { new Harmony(HARMONY_ID).UnpatchAll(HARMONY_ID); AssemblyRedirector.Revert(); diff --git a/TLM/TLM/Util/Shortcuts.cs b/TLM/TLM/Util/Shortcuts.cs index b173e7f51..f299a943f 100644 --- a/TLM/TLM/Util/Shortcuts.cs +++ b/TLM/TLM/Util/Shortcuts.cs @@ -12,6 +12,7 @@ namespace TrafficManager.Util { using TrafficManager.API.Traffic.Enums; using TrafficManager.Manager.Impl; using UnityEngine; + using ColossalFramework.UI; internal static class Shortcuts { internal static bool InSimulationThread() => @@ -160,6 +161,24 @@ internal static void Assert(bool con, string m = "") { } } + internal static void LogException(this Exception ex, bool showInPanel = false) { + if (ex is null) + Log.Error("null argument ex was passed to Log.Exception()"); + try { + Log.Error(ex.ToString() + "\n\t===================="); // stack trace is prited after this. + UnityEngine.Debug.LogException(ex); + if (showInPanel) + UIView.ForwardException(ex); + } catch (Exception ex2) { + Log.Error(ex2.ToString()); + } + } + + internal static void ShowErrorDialog(string title, string message) { + UIView.library.ShowModal("ExceptionPanel") + .SetMessage(title, message, true); + } + internal static bool ShiftIsPressed => Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift); internal static bool ControlIsPressed => Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl); From 6b0b6ccfe3a44a6c10c93c2fec823fdb97daf9b6 Mon Sep 17 00:00:00 2001 From: kianzarrin Date: Mon, 12 Apr 2021 13:52:25 +0300 Subject: [PATCH 19/23] lifecycle class (#1077) Moved all lifecycle code to a TMPELifeCycle. --- TLM/SharedAssemblyInfo.cs | 2 +- TLM/TLM/Lifecycle/LoadingExtension.cs | 14 + TLM/TLM/{ => Lifecycle}/Patcher.cs | 7 +- .../SerializableDataExtension.cs | 16 +- .../TMPELifecycle.cs} | 280 +++++++++++------- TLM/TLM/Lifecycle/ThreadingExtension.cs | 24 ++ TLM/TLM/Lifecycle/TrafficManagerMod.cs | 46 +++ TLM/TLM/Manager/Impl/OptionsManager.cs | 4 +- TLM/TLM/Manager/Impl/UtilityManager.cs | 4 +- TLM/TLM/Patch/_DefaultTool/OnToolGUIPatch.cs | 4 +- .../Patch/_DefaultTool/RenderOverlayPatch.cs | 3 +- TLM/TLM/State/Asset/AssetData.cs | 1 + TLM/TLM/State/GlobalConfig.cs | 3 +- TLM/TLM/State/Options.cs | 29 +- .../State/OptionsTabs/OptionsGameplayTab.cs | 3 +- .../State/OptionsTabs/OptionsGeneralTab.cs | 7 +- .../OptionsTabs/OptionsMaintenanceTab.cs | 6 +- TLM/TLM/State/TMPEMoveITIntegration.cs | 1 + TLM/TLM/TLM.csproj | 11 +- TLM/TLM/ThreadingExtension.cs | 48 --- TLM/TLM/TrafficManagerMod.cs | 142 --------- TLM/TLM/UI/Helpers/GuideHandler.cs | 5 +- TLM/TLM/UI/IncompatibleModsPanel.cs | 1 + TLM/TLM/UI/Localization/Translation.cs | 35 +-- TLM/TLM/UI/MainMenu/DebugMenu.cs | 5 +- TLM/TLM/UI/MainMenu/MainMenuWindow.cs | 3 +- TLM/TLM/UI/ModUI.cs | 9 +- TLM/TLM/UI/TrafficManagerTool.cs | 9 +- 28 files changed, 349 insertions(+), 373 deletions(-) create mode 100644 TLM/TLM/Lifecycle/LoadingExtension.cs rename TLM/TLM/{ => Lifecycle}/Patcher.cs (96%) rename TLM/TLM/{State => Lifecycle}/SerializableDataExtension.cs (96%) rename TLM/TLM/{LoadingExtension.cs => Lifecycle/TMPELifecycle.cs} (54%) create mode 100644 TLM/TLM/Lifecycle/ThreadingExtension.cs create mode 100644 TLM/TLM/Lifecycle/TrafficManagerMod.cs delete mode 100644 TLM/TLM/ThreadingExtension.cs delete mode 100644 TLM/TLM/TrafficManagerMod.cs diff --git a/TLM/SharedAssemblyInfo.cs b/TLM/SharedAssemblyInfo.cs index 6721d8567..1140fc608 100644 --- a/TLM/SharedAssemblyInfo.cs +++ b/TLM/SharedAssemblyInfo.cs @@ -20,4 +20,4 @@ // Minor Version // Build Number // Revision -[assembly: AssemblyVersion("11.5.3.*")] +[assembly: AssemblyVersion("11.5.4.*")] diff --git a/TLM/TLM/Lifecycle/LoadingExtension.cs b/TLM/TLM/Lifecycle/LoadingExtension.cs new file mode 100644 index 000000000..81f6e1885 --- /dev/null +++ b/TLM/TLM/Lifecycle/LoadingExtension.cs @@ -0,0 +1,14 @@ +namespace TrafficManager.Lifecycle { + using ColossalFramework; + using CSUtil.Commons; + using ICities; + using JetBrains.Annotations; + using System.Collections.Generic; + using System.Reflection; + + [UsedImplicitly] + public class LoadingExtension : LoadingExtensionBase { + public override void OnLevelLoaded(LoadMode mode) => TMPELifecycle.Instance.Load(); + public override void OnLevelUnloading() => TMPELifecycle.Instance.Unload(); + } +} \ No newline at end of file diff --git a/TLM/TLM/Patcher.cs b/TLM/TLM/Lifecycle/Patcher.cs similarity index 96% rename from TLM/TLM/Patcher.cs rename to TLM/TLM/Lifecycle/Patcher.cs index 4533f9828..16a04a863 100644 --- a/TLM/TLM/Patcher.cs +++ b/TLM/TLM/Lifecycle/Patcher.cs @@ -1,4 +1,4 @@ -namespace TrafficManager { +namespace TrafficManager.Lifecycle { using ColossalFramework.UI; using CSUtil.Commons; using HarmonyLib; @@ -29,7 +29,7 @@ public static class Patcher { " - run the game again."; internal static void AssertCitiesHarmonyInstalled() { - if(!HarmonyHelper.IsHarmonyInstalled) { + if (!HarmonyHelper.IsHarmonyInstalled) { Shortcuts.ShowErrorDialog("Error: Missing Harmony", SOLUTION); throw new Exception(ERROR_MESSAGE); } @@ -46,8 +46,7 @@ public static void Install() { try { Log.Info("Deploying attribute-driven detours"); AssemblyRedirector.Deploy(); - } - catch (Exception e) { + } catch (Exception e) { Log.Error("Could not deploy attribute-driven detours because the following exception occured:\n " + e + "\n -- End of inner exception stack trace -- "); diff --git a/TLM/TLM/State/SerializableDataExtension.cs b/TLM/TLM/Lifecycle/SerializableDataExtension.cs similarity index 96% rename from TLM/TLM/State/SerializableDataExtension.cs rename to TLM/TLM/Lifecycle/SerializableDataExtension.cs index 4ebc9f9f4..833f4d878 100644 --- a/TLM/TLM/State/SerializableDataExtension.cs +++ b/TLM/TLM/Lifecycle/SerializableDataExtension.cs @@ -1,4 +1,4 @@ -namespace TrafficManager.State { +namespace TrafficManager.Lifecycle { using CSUtil.Commons; using ICities; using JetBrains.Annotations; @@ -8,6 +8,7 @@ namespace TrafficManager.State { using System; using TrafficManager.API.Manager; using TrafficManager.Manager.Impl; + using TrafficManager.State; [UsedImplicitly] public class SerializableDataExtension @@ -17,14 +18,13 @@ public class SerializableDataExtension private static ISerializableData SerializableData => SimulationManager.instance.m_SerializableDataWrapper; private static Configuration _configuration; - public static bool StateLoading; public override void OnLoadData() => Load(); public override void OnSaveData() => Save(); public static void Load() { Log.Info("Loading Traffic Manager: PE Data"); - StateLoading = true; + TMPELifecycle.Instance.Deserializing = true; bool loadingSucceeded = true; try { @@ -36,7 +36,7 @@ public static void Load() { loadingSucceeded = false; } - foreach (ICustomManager manager in LoadingExtension.RegisteredManagers) { + foreach (ICustomManager manager in TMPELifecycle.Instance.RegisteredManagers) { try { Log.Info($"OnBeforeLoadData: {manager.GetType().Name}"); manager.OnBeforeLoadData(); @@ -86,9 +86,9 @@ public static void Load() { // steps under 'In case problems arise'.", true); } - StateLoading = false; + TMPELifecycle.Instance.Deserializing = false; - foreach (ICustomManager manager in LoadingExtension.RegisteredManagers) { + foreach (ICustomManager manager in TMPELifecycle.Instance.RegisteredManagers) { try { Log.Info($"OnAfterLoadData: {manager.GetType().Name}"); manager.OnAfterLoadData(); @@ -287,7 +287,7 @@ public static void Save() { // error = true; // } - foreach (ICustomManager manager in LoadingExtension.RegisteredManagers) { + foreach (ICustomManager manager in TMPELifecycle.Instance.RegisteredManagers) { try { Log.Info($"OnBeforeSaveData: {manager.GetType().Name}"); manager.OnBeforeSaveData(); @@ -376,7 +376,7 @@ public static void Save() { memoryStream.Close(); } - var reverseManagers = new List(LoadingExtension.RegisteredManagers); + var reverseManagers = new List(TMPELifecycle.Instance.RegisteredManagers); reverseManagers.Reverse(); foreach (ICustomManager manager in reverseManagers) { try { diff --git a/TLM/TLM/LoadingExtension.cs b/TLM/TLM/Lifecycle/TMPELifecycle.cs similarity index 54% rename from TLM/TLM/LoadingExtension.cs rename to TLM/TLM/Lifecycle/TMPELifecycle.cs index b3df12a9c..d1bc0bcc4 100644 --- a/TLM/TLM/LoadingExtension.cs +++ b/TLM/TLM/Lifecycle/TMPELifecycle.cs @@ -1,31 +1,62 @@ -namespace TrafficManager { +namespace TrafficManager.Lifecycle { + using CitiesHarmony.API; using ColossalFramework; + using ColossalFramework.Globalization; using ColossalFramework.UI; using CSUtil.Commons; using ICities; - using JetBrains.Annotations; using System; using System.Collections.Generic; + using System.Linq; using System.Reflection; using TrafficManager.API.Manager; using TrafficManager.Custom.PathFinding; using TrafficManager.Manager.Impl; using TrafficManager.State; using TrafficManager.UI; - using UnityEngine; + using TrafficManager.Util; using UnityEngine.SceneManagement; - using Util; - using Object = UnityEngine.Object; - using System.Linq; - - [UsedImplicitly] - public class LoadingExtension : LoadingExtensionBase { - static LoadingExtension() { - TranslationDatabase.LoadAllTranslations(); - RegisterCustomManagers(); + using UnityEngine; + using JetBrains.Annotations; + + /// + /// Do not use Singleton.instance to prevent memory leak. + /// Instead use the TMPELifecycle.Instance property. + /// + public class TMPELifecycle : MonoBehaviour { + public static TMPELifecycle Instance { + /// returns instance of TMPELifecycle if it exists. returns null otherwise. + get; + private set; } - internal static AppMode? AppMode => SimulationManager.instance.m_ManagersWrapper.loading.currentMode; + /// TMPE is in the middle of deserializing data. + public bool Deserializing; + + // These values from `BuildConfig` class (`APPLICATION_VERSION` constants) in game file `Managed/Assembly-CSharp.dll` (use ILSpy to inspect them) + public const uint GAME_VERSION = 188868624U; + public const uint GAME_VERSION_A = 1u; + public const uint GAME_VERSION_B = 13u; + public const uint GAME_VERSION_C = 0u; + public const uint GAME_VERSION_BUILD = 8u; + + /// + /// Contains loaded languages and lookup functions for text translations + /// + public Translation TranslationDatabase { get; } = new Translation(); + public List RegisteredManagers { get; } = new List(); + + public bool InGameHotReload { get; private set; } + public bool IsGameLoaded { get; private set; } + + /// + /// determines if simulation is inside game/editor. useful to detect hot-reload. + /// + public static bool InGameOrEditor() => + SceneManager.GetActiveScene().name != "IntroScreen" && + SceneManager.GetActiveScene().name != "Startup"; + + public static AppMode? AppMode => SimulationManager.instance.m_ManagersWrapper.loading?.currentMode; public static SimulationManager.UpdateMode UpdateMode => SimulationManager.instance.m_metaData.m_updateMode; public static LoadMode Mode => (LoadMode)UpdateMode; @@ -36,18 +67,13 @@ static LoadingExtension() { /// internal static bool PlayMode => AppMode != null && AppMode == ICities.AppMode.Game; - /// - /// Contains loaded languages and lookup functions for text translations - /// - public static Translation TranslationDatabase = new Translation(); - - public static List RegisteredManagers { get; private set; } - - public static bool IsGameLoaded { get; private set; } - private static void RegisterCustomManagers() { - RegisteredManagers = new List(); + private static void CheckForIncompatibleMods() { + ModsCompatibilityChecker mcc = new ModsCompatibilityChecker(); + mcc.PerformModCheck(); + } + private void RegisterCustomManagers() { // TODO represent data dependencies differently RegisteredManagers.Add(ExtNodeManager.Instance); RegisteredManagers.Add(ExtSegmentManager.Instance); @@ -78,63 +104,114 @@ private static void RegisterCustomManagers() { RegisteredManagers.Add(JunctionRestrictionsManager.Instance); } - public override void OnLevelUnloading() => Unload(); + [UsedImplicitly] + void Awake() { + try { + Log.Info("TMPELifecycle.Awake()"); + Instance = this; +#if BENCHMARK + Benchmark.BenchmarkManager.Setup(); +#endif + TranslationDatabase.LoadAllTranslations(); + RegisterCustomManagers(); + + Log.InfoFormat( + "TM:PE enabled. Version {0}, Build {1} {2} for game version {3}.{4}.{5}-f{6}", + TrafficManagerMod.VersionString, + Assembly.GetExecutingAssembly().GetName().Version, + TrafficManagerMod.BRANCH, + GAME_VERSION_A, + GAME_VERSION_B, + GAME_VERSION_C, + GAME_VERSION_BUILD); + Log.InfoFormat( + "Enabled TM:PE has GUID {0}", + Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId); + + // check for incompatible mods + if (UIView.GetAView() != null) { + // when TM:PE is enabled in content manager + CheckForIncompatibleMods(); + } else { + // or when game first loads if TM:PE was already enabled + LoadingManager.instance.m_introLoaded += CheckForIncompatibleMods; + } - public override void OnLevelLoaded(LoadMode mode) => Load(); + // Log Mono version + Type monoRt = Type.GetType("Mono.Runtime"); + if (monoRt != null) { + MethodInfo displayName = monoRt.GetMethod( + "GetDisplayName", + BindingFlags.NonPublic | BindingFlags.Static); + if (displayName != null) { + Log.InfoFormat("Mono version: {0}", displayName.Invoke(null, null)); + } + } - public static void Unload() { - Log.Info("LoadingExtension.Unload()"); + Log._Debug("Scene is " + SceneManager.GetActiveScene().name); - CustomPathManager._instance?.OnLevelUnloading(); + HarmonyHelper.EnsureHarmonyInstalled(); - try { - foreach (ICustomManager manager in RegisteredManagers.AsEnumerable().Reverse()) { - Log.Info($"OnLevelUnloading: {manager.GetType().Name}"); - manager.OnLevelUnloading(); + InGameHotReload = InGameOrEditor(); + if (InGameHotReload) { + SerializableDataExtension.Load(); + Load(); } - Flags.OnLevelUnloading(); - GlobalConfig.OnLevelUnloading(); +#if DEBUG + const bool installHarmonyASAP = false; // set true for fast testing + if (installHarmonyASAP) + HarmonyHelper.DoOnHarmonyReady(Patcher.Install); +#endif + } catch (Exception ex) { + ex.LogException(true); + } + } - var uiviewGO = UIView.GetAView().gameObject; - Object.Destroy(uiviewGO.GetComponent()); - Object.Destroy(uiviewGO.GetComponent()); - Object.Destroy(uiviewGO.GetComponent()); - Object.Destroy(uiviewGO.GetComponent()); - Object.Destroy(uiviewGO.GetComponent()); + [UsedImplicitly] + void OnDestroy() { + try { + Log.Info("TMPELifecycle.OnDestroy()"); + LoadingManager.instance.m_introLoaded -= CheckForIncompatibleMods; + LocaleManager.eventLocaleChanged -= Translation.HandleGameLocaleChange; - Log.Info("Removing Controls from UI."); - if (ModUI.Instance != null) { - ModUI.Instance.CloseMainMenu(); // Hide the UI ASAP - ModUI.Instance.Destroy(); - Log._Debug("removed UIBase instance."); + if (InGameOrEditor() && IsGameLoaded) { + //Hot Unload + Unload(); } + Instance = null; + } catch(Exception ex) { + ex.LogException(true); } - catch (Exception e) { - Log.Error("Exception unloading mod. " + e.Message); + } - // ignored - prevents collision with other mods - } +#if DEBUG + ~TMPELifecycle() { + // ensure there is no memory leak: + Log._Debug("TMPELifecycle.~TMPELifecycle()"); + } +#endif + internal static void StartMod() { + var go = new GameObject(nameof(TMPELifecycle), typeof(TMPELifecycle)); + DontDestroyOnLoad(go); // don't destroy when scene changes. + } - Patcher.Uninstall(); - IsGameLoaded = false; - TrafficManagerMod.InGameHotReload = false; + internal static void EndMod() { + Destroy(Instance?.gameObject); } - public static void Load() { - Log.Info($"LoadingExtension.Load() called. {Mode} called. updateMode={UpdateMode}, scene={Scene}"); + internal void Load() { + Log.Info($"TMPELifecycle.Load() called. Mode={Mode}, UpdateMode={UpdateMode}, Scene={Scene}"); - if(Scene == "ThemeEditor") + if (Scene == "ThemeEditor") return; InGameUtil.Instantiate(); - RegisterCustomManagers(); - IsGameLoaded = false; if (BuildConfig.applicationVersion != BuildConfig.VersionToString( - TrafficManagerMod.GAME_VERSION, + GAME_VERSION, false)) { string[] majorVersionElms = BuildConfig.applicationVersion.Split('-'); string[] versionElms = majorVersionElms[0].Split('.'); @@ -144,19 +221,15 @@ public static void Load() { Log.Info($"Detected game version v{BuildConfig.applicationVersion}"); - bool isModTooOld = TrafficManagerMod.GAME_VERSION_A < versionA || - (TrafficManagerMod.GAME_VERSION_A == versionA && - TrafficManagerMod.GAME_VERSION_B < versionB); - // || (TrafficManagerMod.GameVersionA == versionA - // && TrafficManagerMod.GameVersionB == versionB - // && TrafficManagerMod.GameVersionC < versionC); + bool isModTooOld = GAME_VERSION_A < versionA || + (GAME_VERSION_A == versionA && + GAME_VERSION_B < versionB); + + + bool isModNewer = GAME_VERSION_A < versionA || + (GAME_VERSION_A == versionA && + GAME_VERSION_B > versionB); - bool isModNewer = TrafficManagerMod.GAME_VERSION_A < versionA || - (TrafficManagerMod.GAME_VERSION_A == versionA && - TrafficManagerMod.GAME_VERSION_B > versionB); - // || (TrafficManagerMod.GameVersionA == versionA - // && TrafficManagerMod.GameVersionB == versionB - // && TrafficManagerMod.GameVersionC > versionC); if (isModTooOld) { string msg = string.Format( @@ -165,7 +238,7 @@ public static void Load() { "Please be aware that TM:PE has not been updated for the newest game " + "version yet and thus it is very likely it will not work as expected.", BuildConfig.applicationVersion, - BuildConfig.VersionToString(TrafficManagerMod.GAME_VERSION, false)); + BuildConfig.VersionToString(GAME_VERSION, false)); Log.Error(msg); Singleton.instance.m_ThreadingWrapper.QueueMainThread( @@ -182,7 +255,7 @@ public static void Load() { "Traffic Manager: President Edition has been built for game version {0}. " + "You are running game version {1}. Some features of TM:PE will not " + "work with older game versions. Please let Steam update your game.", - BuildConfig.VersionToString(TrafficManagerMod.GAME_VERSION, false), + BuildConfig.VersionToString(GAME_VERSION, false), BuildConfig.applicationVersion); Log.Error(msg); @@ -227,47 +300,40 @@ public static void Load() { Log.Info("OnLevelLoaded complete."); } - [UsedImplicitly] - private bool Check3rdPartyModLoaded(string namespaceStr, bool printAll = false) { - bool thirdPartyModLoaded = false; - - FieldInfo loadingWrapperLoadingExtensionsField = typeof(LoadingWrapper).GetField( - "m_LoadingExtensions", - BindingFlags.NonPublic | BindingFlags.Instance); - List loadingExtensions = null; - - if (loadingWrapperLoadingExtensionsField != null) { - loadingExtensions = - (List)loadingWrapperLoadingExtensionsField.GetValue( - Singleton.instance.m_LoadingWrapper); - } else { - Log.Warning("Could not get loading extensions field"); - } + internal void Unload() { + CustomPathManager._instance?.OnLevelUnloading(); - if (loadingExtensions != null) { - foreach (ILoadingExtension extension in loadingExtensions) { - if (printAll) { - Log.Info($"Detected extension: {extension.GetType().Name} in " + - $"namespace {extension.GetType().Namespace}"); - } + try { + foreach (ICustomManager manager in RegisteredManagers.AsEnumerable().Reverse()) { + Log.Info($"OnLevelUnloading: {manager.GetType().Name}"); + manager.OnLevelUnloading(); + } - if (extension.GetType().Namespace == null) { - continue; - } + Flags.OnLevelUnloading(); + GlobalConfig.OnLevelUnloading(); - string nsStr = extension.GetType().Namespace; + var uiviewGO = UIView.GetAView().gameObject; + Destroy(uiviewGO.GetComponent()); + Destroy(uiviewGO.GetComponent()); + Destroy(uiviewGO.GetComponent()); + Destroy(uiviewGO.GetComponent()); + Destroy(uiviewGO.GetComponent()); - if (namespaceStr.Equals(nsStr)) { - Log.Info($"The mod '{namespaceStr}' has been detected."); - thirdPartyModLoaded = true; - break; - } + Log.Info("Removing Controls from UI."); + if (ModUI.Instance != null) { + ModUI.Instance.CloseMainMenu(); // Hide the UI ASAP + ModUI.Instance.Destroy(); + Log._Debug("removed UIBase instance."); } - } else { - Log._Debug("Could not get loading extensions"); + } catch (Exception e) { + Log.Error("Exception unloading mod. " + e.Message); + + // ignored - prevents collision with other mods } - return thirdPartyModLoaded; + Patcher.Uninstall(); + IsGameLoaded = false; + InGameHotReload = false; } } -} \ No newline at end of file +} diff --git a/TLM/TLM/Lifecycle/ThreadingExtension.cs b/TLM/TLM/Lifecycle/ThreadingExtension.cs new file mode 100644 index 000000000..67afbc015 --- /dev/null +++ b/TLM/TLM/Lifecycle/ThreadingExtension.cs @@ -0,0 +1,24 @@ +namespace TrafficManager.Lifecycle { + using ICities; + using JetBrains.Annotations; + using TrafficManager.State; + using TrafficManager.Manager.Impl; + + [UsedImplicitly] + public sealed class ThreadingExtension : ThreadingExtensionBase { + public override void OnBeforeSimulationTick() { + base.OnBeforeSimulationTick(); + + GeometryManager.Instance.SimulationStep(); + RoutingManager.Instance.SimulationStep(); + } + + public override void OnBeforeSimulationFrame() { + base.OnBeforeSimulationFrame(); + + if (Options.timedLightsEnabled) { + TrafficLightSimulationManager.Instance.SimulationStep(); + } + } + } // end class +} \ No newline at end of file diff --git a/TLM/TLM/Lifecycle/TrafficManagerMod.cs b/TLM/TLM/Lifecycle/TrafficManagerMod.cs new file mode 100644 index 000000000..d0fe2d811 --- /dev/null +++ b/TLM/TLM/Lifecycle/TrafficManagerMod.cs @@ -0,0 +1,46 @@ +namespace TrafficManager.Lifecycle { + using ColossalFramework.Globalization; + using CSUtil.Commons; + using ICities; + using JetBrains.Annotations; + using System; + using TrafficManager.State; + using TrafficManager.UI; + + public class TrafficManagerMod : IUserMod { +#if LABS + public const string BRANCH = "LABS"; +#elif DEBUG + public const string BRANCH = "DEBUG"; +#else + public const string BRANCH = "STABLE"; +#endif + + // Use SharedAssemblyInfo.cs to modify TM:PE version + // External mods (eg. CSUR Toolbox) reference the versioning for compatibility purposes + public static Version ModVersion => typeof(TrafficManagerMod).Assembly.GetName().Version; + + // used for in-game display + public static string VersionString => ModVersion.ToString(3); + + public static readonly string ModName = "TM:PE " + VersionString + " " + BRANCH; + + public string Name => ModName; + + public string Description => "Manage your city's traffic"; + + [UsedImplicitly] + public void OnEnabled() => TMPELifecycle.StartMod(); + + [UsedImplicitly] + public void OnDisabled() => TMPELifecycle.EndMod(); + + [UsedImplicitly] + public void OnSettingsUI(UIHelper helper) { + // Note: This bugs out if done in OnEnabled(), hence doing it here instead. + LocaleManager.eventLocaleChanged -= Translation.HandleGameLocaleChange; + LocaleManager.eventLocaleChanged += Translation.HandleGameLocaleChange; + Options.MakeSettings(helper); + } + } +} diff --git a/TLM/TLM/Manager/Impl/OptionsManager.cs b/TLM/TLM/Manager/Impl/OptionsManager.cs index bafa2c760..2fbf52b75 100644 --- a/TLM/TLM/Manager/Impl/OptionsManager.cs +++ b/TLM/TLM/Manager/Impl/OptionsManager.cs @@ -5,6 +5,7 @@ namespace TrafficManager.Manager.Impl { using TrafficManager.API.Traffic.Enums; using TrafficManager.State; using TrafficManager.UI.Helpers; + using TrafficManager.Lifecycle; public class OptionsManager : AbstractCustomManager, @@ -36,7 +37,8 @@ private static byte ConvertFromSimulationAccuracy(SimulationAccuracy value) { } public bool MayPublishSegmentChanges() { - return Options.instantEffects && !SerializableDataExtension.StateLoading; + return Options.instantEffects && TMPELifecycle.InGameOrEditor() && + !TMPELifecycle.Instance.Deserializing; } public bool LoadData(byte[] data) { diff --git a/TLM/TLM/Manager/Impl/UtilityManager.cs b/TLM/TLM/Manager/Impl/UtilityManager.cs index c226e41b1..e17c46fa1 100644 --- a/TLM/TLM/Manager/Impl/UtilityManager.cs +++ b/TLM/TLM/Manager/Impl/UtilityManager.cs @@ -7,7 +7,7 @@ namespace TrafficManager.Manager.Impl { using TrafficManager.API.Manager; using TrafficManager.State; using UnityEngine; - + using TrafficManager.Lifecycle; public class UtilityManager : AbstractCustomManager, IUtilityManager { static UtilityManager() { Instance = new UtilityManager(); @@ -63,7 +63,7 @@ public void PrintAllDebugInfo() { Log.Error($"Error occurred while printing debug info for flags: {e}"); } - foreach (ICustomManager manager in LoadingExtension.RegisteredManagers) { + foreach (ICustomManager manager in TMPELifecycle.Instance.RegisteredManagers) { try { manager.PrintDebugInfo(); } diff --git a/TLM/TLM/Patch/_DefaultTool/OnToolGUIPatch.cs b/TLM/TLM/Patch/_DefaultTool/OnToolGUIPatch.cs index c3f85df81..20b604b49 100644 --- a/TLM/TLM/Patch/_DefaultTool/OnToolGUIPatch.cs +++ b/TLM/TLM/Patch/_DefaultTool/OnToolGUIPatch.cs @@ -3,7 +3,7 @@ namespace TrafficManager.Patch._DefaultTool { using JetBrains.Annotations; using TrafficManager.UI; using UnityEngine; - + using TrafficManager.Lifecycle; [HarmonyPatch(typeof(DefaultTool), "OnToolGUI")] [UsedImplicitly] public static class OnToolGUIPatch { @@ -13,7 +13,7 @@ public static class OnToolGUIPatch { [HarmonyPostfix] [UsedImplicitly] public static void Postfix(Event e) { - if (LoadingExtension.PlayMode && !TrafficManagerTool.IsCurrentTool) { + if (TMPELifecycle.PlayMode && !TrafficManagerTool.IsCurrentTool) { if (UI.SubTools.PrioritySigns.MassEditOverlay.IsActive) { ModUI.GetTrafficManagerTool(true).OnToolGUIImpl(e); } diff --git a/TLM/TLM/Patch/_DefaultTool/RenderOverlayPatch.cs b/TLM/TLM/Patch/_DefaultTool/RenderOverlayPatch.cs index 7a4237715..f125fbae2 100644 --- a/TLM/TLM/Patch/_DefaultTool/RenderOverlayPatch.cs +++ b/TLM/TLM/Patch/_DefaultTool/RenderOverlayPatch.cs @@ -2,6 +2,7 @@ namespace TrafficManager.Patch._DefaultTool { using HarmonyLib; using JetBrains.Annotations; using TrafficManager.UI; + using TrafficManager.Lifecycle; [HarmonyPatch(typeof(DefaultTool), "RenderOverlay")] [UsedImplicitly] @@ -12,7 +13,7 @@ public static class RenderOverlayPatch { [HarmonyPostfix] [UsedImplicitly] public static void Postfix(RenderManager.CameraInfo cameraInfo) { - if (LoadingExtension.PlayMode && !TrafficManagerTool.IsCurrentTool) { + if (TMPELifecycle.PlayMode && !TrafficManagerTool.IsCurrentTool) { if (UI.SubTools.PrioritySigns.MassEditOverlay.IsActive) { ModUI.GetTrafficManagerTool(true).RenderOverlayImpl(cameraInfo); } diff --git a/TLM/TLM/State/Asset/AssetData.cs b/TLM/TLM/State/Asset/AssetData.cs index ce81c4eef..38b26f7a0 100644 --- a/TLM/TLM/State/Asset/AssetData.cs +++ b/TLM/TLM/State/Asset/AssetData.cs @@ -5,6 +5,7 @@ namespace TrafficManager.State.Asset { using System.Collections.Generic; using TrafficManager.Util.Record; using static Util.Shortcuts; + using TrafficManager.Lifecycle; [Serializable] public class AssetData { diff --git a/TLM/TLM/State/GlobalConfig.cs b/TLM/TLM/State/GlobalConfig.cs index 34b2b2dd6..92cfb1335 100644 --- a/TLM/TLM/State/GlobalConfig.cs +++ b/TLM/TLM/State/GlobalConfig.cs @@ -6,6 +6,7 @@ namespace TrafficManager.State { using System; using TrafficManager.State.ConfigData; using TrafficManager.Util; + using TrafficManager.Lifecycle; [XmlRootAttribute("GlobalConfig", Namespace = "http://www.viathinksoft.de/tmpe", IsNullable = false)] public class GlobalConfig : GenericObservable { @@ -133,7 +134,7 @@ public static GlobalConfig Load(out DateTime modifiedTime) { XmlSerializer serializer = new XmlSerializer(typeof(GlobalConfig)); Log.Info($"Global config loaded."); GlobalConfig conf = (GlobalConfig)serializer.Deserialize(fs); - if (LoadingExtension.IsGameLoaded + if (TMPELifecycle.Instance.IsGameLoaded #if DEBUG && !DebugSwitch.NoRoutingRecalculationOnConfigReload.Get() #endif diff --git a/TLM/TLM/State/Options.cs b/TLM/TLM/State/Options.cs index 8495fb5c5..47fd40644 100644 --- a/TLM/TLM/State/Options.cs +++ b/TLM/TLM/State/Options.cs @@ -7,7 +7,10 @@ namespace TrafficManager.State { using TrafficManager.API.Traffic.Enums; using TrafficManager.UI.Helpers; using TrafficManager.UI; + using TrafficManager.Util; using UnityEngine; + using TrafficManager.Lifecycle; + using System; public class Options : MonoBehaviour { #if DEBUG @@ -125,22 +128,26 @@ internal static void RebuildMenu() { ModUI.Instance.MainMenuButton.UpdateButtonImageAndTooltip(); } - LoadingExtension.TranslationDatabase.ReloadTutorialTranslations(); - LoadingExtension.TranslationDatabase.ReloadGuideTranslations(); + TMPELifecycle.Instance.TranslationDatabase.ReloadTutorialTranslations(); + TMPELifecycle.Instance.TranslationDatabase.ReloadGuideTranslations(); } else { Log._Debug("Rebuilding the TM:PE menu: ignored, ModUI is null"); } } public static void MakeSettings(UIHelper helper) { - ExtUITabstrip tabStrip = ExtUITabstrip.Create(helper); - OptionsGeneralTab.MakeSettings_General(tabStrip); - OptionsGameplayTab.MakeSettings_Gameplay(tabStrip); - OptionsVehicleRestrictionsTab.MakeSettings_VehicleRestrictions(tabStrip); - OptionsOverlaysTab.MakeSettings_Overlays(tabStrip); - OptionsMaintenanceTab.MakeSettings_Maintenance(tabStrip); - OptionsKeybindsTab.MakeSettings_Keybinds(tabStrip); - tabStrip.Invalidate(); + try { + ExtUITabstrip tabStrip = ExtUITabstrip.Create(helper); + OptionsGeneralTab.MakeSettings_General(tabStrip); + OptionsGameplayTab.MakeSettings_Gameplay(tabStrip); + OptionsVehicleRestrictionsTab.MakeSettings_VehicleRestrictions(tabStrip); + OptionsOverlaysTab.MakeSettings_Overlays(tabStrip); + OptionsMaintenanceTab.MakeSettings_Maintenance(tabStrip); + OptionsKeybindsTab.MakeSettings_Keybinds(tabStrip); + tabStrip.Invalidate(); + } catch (Exception ex) { + ex.LogException(); + } } internal static void Indent(T component) where T : UIComponent { @@ -164,7 +171,7 @@ internal static void Indent(T component) where T : UIComponent { /// Whether to display a warning popup /// The game is loaded internal static bool IsGameLoaded(bool warn = true) { - if (SerializableDataExtension.StateLoading || LoadingExtension.IsGameLoaded) { + if (TMPELifecycle.InGameOrEditor()) { return true; } diff --git a/TLM/TLM/State/OptionsTabs/OptionsGameplayTab.cs b/TLM/TLM/State/OptionsTabs/OptionsGameplayTab.cs index 7f1ac34dc..f80ee5e0b 100644 --- a/TLM/TLM/State/OptionsTabs/OptionsGameplayTab.cs +++ b/TLM/TLM/State/OptionsTabs/OptionsGameplayTab.cs @@ -6,6 +6,7 @@ namespace TrafficManager.State { using TrafficManager.UI.Helpers; using TrafficManager.UI; using UnityEngine; + using TrafficManager.Lifecycle; public static class OptionsGameplayTab { private static UICheckBox _individualDrivingStyleToggle; @@ -169,7 +170,7 @@ private static void OnAltLaneSelectionRatioChanged(float newVal) { Options.altLaneSelectionRatio + " %"; // Only call this if the game is running, not during the loading time - if (LoadingExtension.IsGameLoaded) { + if (TMPELifecycle.Instance.IsGameLoaded) { _altLaneSelectionRatioSlider.RefreshTooltip(); } diff --git a/TLM/TLM/State/OptionsTabs/OptionsGeneralTab.cs b/TLM/TLM/State/OptionsTabs/OptionsGeneralTab.cs index e9c5329b8..23ab6a6f6 100644 --- a/TLM/TLM/State/OptionsTabs/OptionsGeneralTab.cs +++ b/TLM/TLM/State/OptionsTabs/OptionsGeneralTab.cs @@ -9,6 +9,7 @@ namespace TrafficManager.State { using TrafficManager.UI.SubTools.SpeedLimits; using TrafficManager.UI; using UnityEngine; + using TrafficManager.Lifecycle; public static class OptionsGeneralTab { private static UICheckBox _instantEffectsToggle; @@ -243,7 +244,7 @@ private static void OnGuiOpacityChanged(float newVal) { = string.Format( T("General.Tooltip.Format:Window transparency: {0}%"), GlobalConfig.Instance.Main.GuiOpacity); - if (LoadingExtension.IsGameLoaded) { + if (TMPELifecycle.Instance.IsGameLoaded) { _guiOpacitySlider.RefreshTooltip(); } @@ -257,7 +258,7 @@ private static void OnGuiScaleChanged(float newVal) { = string.Format( T("General.Tooltip.Format:GUI scale: {0}%"), GlobalConfig.Instance.Main.GuiScale); - if (LoadingExtension.IsGameLoaded) { + if (TMPELifecycle.Instance.IsGameLoaded) { _guiScaleSlider.RefreshTooltip(); } @@ -275,7 +276,7 @@ private static void OnOverlayTransparencyChanged(float newVal) { T("General.Tooltip.Format:Overlay transparency: {0}%"), GlobalConfig.Instance.Main.OverlayTransparency); GlobalConfig.WriteConfig(); - if (LoadingExtension.IsGameLoaded) { + if (TMPELifecycle.Instance.IsGameLoaded) { _overlayTransparencySlider.RefreshTooltip(); } diff --git a/TLM/TLM/State/OptionsTabs/OptionsMaintenanceTab.cs b/TLM/TLM/State/OptionsTabs/OptionsMaintenanceTab.cs index 88006b117..47effe4ce 100644 --- a/TLM/TLM/State/OptionsTabs/OptionsMaintenanceTab.cs +++ b/TLM/TLM/State/OptionsTabs/OptionsMaintenanceTab.cs @@ -6,7 +6,7 @@ namespace TrafficManager.State { using TrafficManager.Manager.Impl; using TrafficManager.UI.Helpers; using TrafficManager.UI; - + using TrafficManager.Lifecycle; public static class OptionsMaintenanceTab { [UsedImplicitly] private static UIButton _resetStuckEntitiesBtn; @@ -119,8 +119,8 @@ internal static void MakeSettings_Maintenance(ExtUITabstrip tabStrip) { Options.Indent(_turnOnRedEnabledToggle); // TODO [issue ##959] remove when TTL is implemented in asset editor. - bool inEditor = (SerializableDataExtension.StateLoading || LoadingExtension.IsGameLoaded) - && LoadingExtension.AppMode != AppMode.Game; + bool inEditor = TMPELifecycle.InGameOrEditor() + && TMPELifecycle.AppMode != AppMode.Game; if (inEditor) { EnableTimedLightsToggle.isChecked = false; EnableTimedLightsToggle.isEnabled = false; diff --git a/TLM/TLM/State/TMPEMoveITIntegration.cs b/TLM/TLM/State/TMPEMoveITIntegration.cs index 0a4400e54..96f1f828a 100644 --- a/TLM/TLM/State/TMPEMoveITIntegration.cs +++ b/TLM/TLM/State/TMPEMoveITIntegration.cs @@ -4,6 +4,7 @@ namespace TrafficManager.State { using System.Collections.Generic; using TrafficManager.Util.Record; using CSUtil.Commons; + using TrafficManager.Lifecycle; public class TMPEMoveItIntegrationFactory : IMoveItIntegrationFactory { public MoveItIntegrationBase GetInstance() => new TMPEMoveItIntegration(); diff --git a/TLM/TLM/TLM.csproj b/TLM/TLM/TLM.csproj index 8a3cf5122..d3691a531 100644 --- a/TLM/TLM/TLM.csproj +++ b/TLM/TLM/TLM.csproj @@ -154,7 +154,8 @@ - + + @@ -261,7 +262,7 @@ - + @@ -362,8 +363,8 @@ - - + + @@ -372,7 +373,7 @@ - + diff --git a/TLM/TLM/ThreadingExtension.cs b/TLM/TLM/ThreadingExtension.cs deleted file mode 100644 index aca52766b..000000000 --- a/TLM/TLM/ThreadingExtension.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace TrafficManager { - using ColossalFramework.UI; - using ColossalFramework; - using CSUtil.Commons; - using ICities; - using JetBrains.Annotations; - using static LoadingExtension; - using System.Collections.Generic; - using System.Reflection; - using TrafficManager.API.Manager; - using TrafficManager.RedirectionFramework; - using TrafficManager.State; - using TrafficManager.UI; - using UnityEngine; - using TrafficManager.UI.Helpers; - - [UsedImplicitly] - public sealed class ThreadingExtension : ThreadingExtensionBase { - // int ticksSinceLastMinuteUpdate = 0; - ITrafficLightSimulationManager tlsMan = - Constants.ManagerFactory.TrafficLightSimulationManager; - - IGeometryManager geoMan = Constants.ManagerFactory.GeometryManager; - IRoutingManager routeMan = Constants.ManagerFactory.RoutingManager; - IUtilityManager utilMan = Constants.ManagerFactory.UtilityManager; - - public override void OnCreated(IThreading threading) { - base.OnCreated(threading); - - //ticksSinceLastMinuteUpdate = 0; - } - - public override void OnBeforeSimulationTick() { - base.OnBeforeSimulationTick(); - - geoMan.SimulationStep(); - routeMan.SimulationStep(); - } - - public override void OnBeforeSimulationFrame() { - base.OnBeforeSimulationFrame(); - - if (Options.timedLightsEnabled) { - tlsMan.SimulationStep(); - } - } - } // end class -} \ No newline at end of file diff --git a/TLM/TLM/TrafficManagerMod.cs b/TLM/TLM/TrafficManagerMod.cs deleted file mode 100644 index 8a19429c1..000000000 --- a/TLM/TLM/TrafficManagerMod.cs +++ /dev/null @@ -1,142 +0,0 @@ -namespace TrafficManager { - using ColossalFramework.Globalization; - using ColossalFramework.UI; - using CSUtil.Commons; - using ICities; - using JetBrains.Annotations; - using System.Reflection; - using System; - using TrafficManager.State; - using TrafficManager.UI; - using TrafficManager.Util; - using static TrafficManager.Util.Shortcuts; - using ColossalFramework; - using UnityEngine.SceneManagement; - using CitiesHarmony.API; - - public class TrafficManagerMod : IUserMod { -#if BENCHMARK - public TrafficManagerMod() { - Benchmark.BenchmarkManager.Setup(); - } -#endif - -#if LABS - public const string BRANCH = "LABS"; -#elif DEBUG - public const string BRANCH = "DEBUG"; -#else - public const string BRANCH = "STABLE"; -#endif - - // These values from `BuildConfig` class (`APPLICATION_VERSION` constants) in game file `Managed/Assembly-CSharp.dll` (use ILSpy to inspect them) - public const uint GAME_VERSION = 188868624U; - public const uint GAME_VERSION_A = 1u; - public const uint GAME_VERSION_B = 13u; - public const uint GAME_VERSION_C = 0u; - public const uint GAME_VERSION_BUILD = 8u; - - // Use SharedAssemblyInfo.cs to modify TM:PE version - // External mods (eg. CSUR Toolbox) reference the versioning for compatibility purposes - public static Version ModVersion => typeof(TrafficManagerMod).Assembly.GetName().Version; - - // used for in-game display - public static string VersionString => ModVersion.ToString(3); - - public static readonly string ModName = "TM:PE " + VersionString + " " + BRANCH; - - public string Name => ModName; - - public string Description => "Manage your city's traffic"; - - internal static bool InGameHotReload { get; set; } = false; - - /// - /// determines if simulation is inside game/editor. useful to detect hot-reload. - /// - internal static bool InGameOrEditor() => - SceneManager.GetActiveScene().name != "IntroScreen" && - SceneManager.GetActiveScene().name != "Startup"; - - [UsedImplicitly] - public void OnEnabled() { - Log.InfoFormat( - "TM:PE enabled. Version {0}, Build {1} {2} for game version {3}.{4}.{5}-f{6}", - VersionString, - Assembly.GetExecutingAssembly().GetName().Version, - BRANCH, - GAME_VERSION_A, - GAME_VERSION_B, - GAME_VERSION_C, - GAME_VERSION_BUILD); - Log.InfoFormat( - "Enabled TM:PE has GUID {0}", - Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId); - - // check for incompatible mods - if (UIView.GetAView() != null) { - // when TM:PE is enabled in content manager - CheckForIncompatibleMods(); - } else { - // or when game first loads if TM:PE was already enabled - LoadingManager.instance.m_introLoaded += CheckForIncompatibleMods; - } - - // Log Mono version - Type monoRt = Type.GetType("Mono.Runtime"); - if (monoRt != null) { - MethodInfo displayName = monoRt.GetMethod( - "GetDisplayName", - BindingFlags.NonPublic | BindingFlags.Static); - if (displayName != null) { - Log.InfoFormat("Mono version: {0}", displayName.Invoke(null, null)); - } - } - - Log._Debug("Scene is " + SceneManager.GetActiveScene().name); - - InGameHotReload = InGameOrEditor(); - - HarmonyHelper.EnsureHarmonyInstalled(); - - if(InGameHotReload) { - SerializableDataExtension.Load(); - LoadingExtension.Load(); - } - -#if DEBUG - const bool installHarmonyASAP = false; // set true for fast testing - if (installHarmonyASAP) - HarmonyHelper.DoOnHarmonyReady(Patcher.Install); -#endif - } - - [UsedImplicitly] - public void OnDisabled() { - Log.Info("TM:PE disabled."); - LoadingManager.instance.m_introLoaded -= CheckForIncompatibleMods; - LocaleManager.eventLocaleChanged -= Translation.HandleGameLocaleChange; - Translation.IsListeningToGameLocaleChanged = false; // is this necessary? - - if (InGameOrEditor() && LoadingExtension.IsGameLoaded) { - //Hot Unload - LoadingExtension.Unload(); - } - } - - [UsedImplicitly] - public void OnSettingsUI(UIHelper helper) { - // Note: This bugs out if done in OnEnabled(), hence doing it here instead. - if (!Translation.IsListeningToGameLocaleChanged) { - Translation.IsListeningToGameLocaleChanged = true; - LocaleManager.eventLocaleChanged += new LocaleManager.LocaleChangedHandler(Translation.HandleGameLocaleChange); - } - Options.MakeSettings(helper); - } - - private static void CheckForIncompatibleMods() { - ModsCompatibilityChecker mcc = new ModsCompatibilityChecker(); - mcc.PerformModCheck(); - } - } -} diff --git a/TLM/TLM/UI/Helpers/GuideHandler.cs b/TLM/TLM/UI/Helpers/GuideHandler.cs index 64e42897d..f7a9cac9a 100644 --- a/TLM/TLM/UI/Helpers/GuideHandler.cs +++ b/TLM/TLM/UI/Helpers/GuideHandler.cs @@ -2,12 +2,13 @@ namespace TrafficManager.UI.Helpers { using ColossalFramework; using CSUtil.Commons; using System.Collections.Generic; + using TrafficManager.Lifecycle; public class GuideHandler { private Dictionary GuideTable = new Dictionary(); public GuideHandler() { - foreach (string localeKey in LoadingExtension.TranslationDatabase.GetGuides()) { + foreach (string localeKey in TMPELifecycle.Instance.TranslationDatabase.GetGuides()) { Log._Debug($"calling AddGuide(localeKey={localeKey}) ..."); AddGuide(localeKey); } @@ -19,7 +20,7 @@ private GuideWrapper AddGuide(string localeKey) => public void Activate(string localeKey) { if (!GuideTable.TryGetValue(localeKey, out GuideWrapper guide)) { Log.Error($"Guide {localeKey} does not exists"); - LoadingExtension.TranslationDatabase.AddMissingGuideString(localeKey); + TMPELifecycle.Instance.TranslationDatabase.AddMissingGuideString(localeKey); guide = AddGuide(localeKey); } if (guide == null) { diff --git a/TLM/TLM/UI/IncompatibleModsPanel.cs b/TLM/TLM/UI/IncompatibleModsPanel.cs index 96e093728..79514d470 100644 --- a/TLM/TLM/UI/IncompatibleModsPanel.cs +++ b/TLM/TLM/UI/IncompatibleModsPanel.cs @@ -9,6 +9,7 @@ namespace TrafficManager.UI { using System; using TrafficManager.State; using UnityEngine; + using TrafficManager.Lifecycle; public class IncompatibleModsPanel : UIPanel { private const ulong LOCAL_MOD = ulong.MaxValue; diff --git a/TLM/TLM/UI/Localization/Translation.cs b/TLM/TLM/UI/Localization/Translation.cs index 6e30e864a..9d15d30b6 100644 --- a/TLM/TLM/UI/Localization/Translation.cs +++ b/TLM/TLM/UI/Localization/Translation.cs @@ -7,6 +7,7 @@ namespace TrafficManager.UI { using ColossalFramework.Globalization; using CSUtil.Commons; using TrafficManager.State; + using TrafficManager.Lifecycle; /// /// Adding a new language step by step: @@ -44,65 +45,59 @@ public class Translation { private Localization.LookupTable menuLookup_; public static Localization.LookupTable Menu => - LoadingExtension.TranslationDatabase.menuLookup_; + TMPELifecycle.Instance.TranslationDatabase.menuLookup_; private Localization.LookupTable optionsLookup_; public static Localization.LookupTable Options => - LoadingExtension.TranslationDatabase.optionsLookup_; + TMPELifecycle.Instance.TranslationDatabase.optionsLookup_; private Localization.LookupTable junctionRestrictionsLookup_; public static Localization.LookupTable JunctionRestrictions => - LoadingExtension.TranslationDatabase.junctionRestrictionsLookup_; + TMPELifecycle.Instance.TranslationDatabase.junctionRestrictionsLookup_; private Localization.LookupTable laneRoutingLookup_; public static Localization.LookupTable LaneRouting => - LoadingExtension.TranslationDatabase.laneRoutingLookup_; + TMPELifecycle.Instance.TranslationDatabase.laneRoutingLookup_; private Localization.LookupTable modConflictsLookup_; public static Localization.LookupTable ModConflicts => - LoadingExtension.TranslationDatabase.modConflictsLookup_; + TMPELifecycle.Instance.TranslationDatabase.modConflictsLookup_; private Localization.LookupTable parkingRestrictionsLookup_; public static Localization.LookupTable ParkingRestrictions => - LoadingExtension.TranslationDatabase.parkingRestrictionsLookup_; + TMPELifecycle.Instance.TranslationDatabase.parkingRestrictionsLookup_; private Localization.LookupTable prioritySignsLookup_; public static Localization.LookupTable PrioritySigns => - LoadingExtension.TranslationDatabase.prioritySignsLookup_; + TMPELifecycle.Instance.TranslationDatabase.prioritySignsLookup_; private Localization.LookupTable speedLimitsLookup_; public static Localization.LookupTable SpeedLimits => - LoadingExtension.TranslationDatabase.speedLimitsLookup_; + TMPELifecycle.Instance.TranslationDatabase.speedLimitsLookup_; private Localization.LookupTable trafficLightsLookup_; public static Localization.LookupTable TrafficLights => - LoadingExtension.TranslationDatabase.trafficLightsLookup_; + TMPELifecycle.Instance.TranslationDatabase.trafficLightsLookup_; private Localization.LookupTable vehicleRestrictionsLookup_; public static Localization.LookupTable VehicleRestrictions => - LoadingExtension.TranslationDatabase.vehicleRestrictionsLookup_; + TMPELifecycle.Instance.TranslationDatabase.vehicleRestrictionsLookup_; private Localization.LookupTable tutorialsLookup_; public static Localization.LookupTable Tutorials => - LoadingExtension.TranslationDatabase.tutorialsLookup_; + TMPELifecycle.Instance.TranslationDatabase.tutorialsLookup_; private Localization.LookupTable guideLookup_; public static Localization.LookupTable Guide => - LoadingExtension.TranslationDatabase.guideLookup_; + TMPELifecycle.Instance.TranslationDatabase.guideLookup_; private Localization.LookupTable aiCitizenLookup_; public static Localization.LookupTable AICitizen => - LoadingExtension.TranslationDatabase.aiCitizenLookup_; + TMPELifecycle.Instance.TranslationDatabase.aiCitizenLookup_; private Localization.LookupTable aiCarLookup_; public static Localization.LookupTable AICar => - LoadingExtension.TranslationDatabase.aiCarLookup_; - - /// - /// Gets or sets a value indicating whether we're currently listening to the event fired when user changes game langauge. - /// The event is hooked in and unhooked in . - /// - public static bool IsListeningToGameLocaleChanged { get; set; } = false; + TMPELifecycle.Instance.TranslationDatabase.aiCarLookup_; /// /// Gets or sets a value indicating the current lanugage to use for translations. diff --git a/TLM/TLM/UI/MainMenu/DebugMenu.cs b/TLM/TLM/UI/MainMenu/DebugMenu.cs index 7cbe7916e..e20b2633f 100644 --- a/TLM/TLM/UI/MainMenu/DebugMenu.cs +++ b/TLM/TLM/UI/MainMenu/DebugMenu.cs @@ -11,7 +11,8 @@ namespace TrafficManager.UI.MainMenu { using global::TrafficManager.State; using JetBrains.Annotations; using UnityEngine; - using Util; + using TrafficManager.Util; + using TrafficManager.Lifecycle; #if DEBUG // whole class coverage public class DebugMenuPanel : UIPanel @@ -198,7 +199,7 @@ private void ClickGoToNode(UIComponent component, UIMouseEventParameter eventPar private void ClickPrintDebugInfo(UIComponent component, UIMouseEventParameter eventParam) { Constants.ServiceFactory.SimulationService.AddAction( () => { - foreach (ICustomManager customManager in LoadingExtension + foreach (ICustomManager customManager in TMPELifecycle.Instance .RegisteredManagers) { customManager.PrintDebugInfo(); } diff --git a/TLM/TLM/UI/MainMenu/MainMenuWindow.cs b/TLM/TLM/UI/MainMenu/MainMenuWindow.cs index f44421d65..2d900428b 100644 --- a/TLM/TLM/UI/MainMenu/MainMenuWindow.cs +++ b/TLM/TLM/UI/MainMenu/MainMenuWindow.cs @@ -14,6 +14,7 @@ namespace TrafficManager.UI.MainMenu { using TrafficManager.U.Autosize; using TrafficManager.Util; using UnityEngine; + using TrafficManager.Lifecycle; public class MainMenuWindow : U.Panel.BaseUWindowPanel, @@ -372,7 +373,7 @@ private void SetupControls_DebugLabels(UiBuilder builder, } } - if (TrafficManagerMod.InGameHotReload) { + if (TMPELifecycle.Instance.InGameHotReload) { // Hot Reload version label (debug only) string text = $"HOT RELOAD {Assembly.GetExecutingAssembly().GetName().Version}"; using (var hotReloadB = builder.Label(text)) { diff --git a/TLM/TLM/UI/ModUI.cs b/TLM/TLM/UI/ModUI.cs index 09f7be368..09087860b 100644 --- a/TLM/TLM/UI/ModUI.cs +++ b/TLM/TLM/UI/ModUI.cs @@ -5,6 +5,7 @@ namespace TrafficManager.UI { using TrafficManager.UI.MainMenu; using TrafficManager.Util; using UnityEngine; + using TrafficManager.Lifecycle; /// /// Globally available UI manager class which contains the main menu button and the panel. @@ -84,8 +85,8 @@ public void Awake() { ToolMode = TrafficManagerMode.None; // One time load - LoadingExtension.TranslationDatabase.ReloadTutorialTranslations(); - LoadingExtension.TranslationDatabase.ReloadGuideTranslations(); + TMPELifecycle.Instance.TranslationDatabase.ReloadTutorialTranslations(); + TMPELifecycle.Instance.TranslationDatabase.ReloadGuideTranslations(); } private void CreateMainMenuButtonAndWindow() { @@ -232,8 +233,8 @@ public static void OnLevelLoaded() { .gameObject .AddComponent(); } - LoadingExtension.TranslationDatabase.ReloadTutorialTranslations(); - LoadingExtension.TranslationDatabase.ReloadGuideTranslations(); + TMPELifecycle.Instance.TranslationDatabase.ReloadTutorialTranslations(); + TMPELifecycle.Instance.TranslationDatabase.ReloadGuideTranslations(); } public static void DisableTool() { diff --git a/TLM/TLM/UI/TrafficManagerTool.cs b/TLM/TLM/UI/TrafficManagerTool.cs index e77edc1e0..a77308f6f 100644 --- a/TLM/TLM/UI/TrafficManagerTool.cs +++ b/TLM/TLM/UI/TrafficManagerTool.cs @@ -27,6 +27,7 @@ namespace TrafficManager.UI { using TrafficManager.UI.SubTools.LaneArrows; using TrafficManager.UI.SubTools.PrioritySigns; using TrafficManager.UI.SubTools.TimedTrafficLights; + using TrafficManager.Lifecycle; using static TrafficManager.Util.Shortcuts; using static TrafficManager.Util.SegmentTraverser; @@ -244,7 +245,7 @@ public ToolMode GetToolMode() { public void SetToolMode(ToolMode newToolMode) { ToolMode oldToolMode = toolMode_; - if(toolMode_ != ToolMode.None && LoadingExtension.PlayMode) { + if(toolMode_ != ToolMode.None && TMPELifecycle.PlayMode) { // Make it impossible for user to undo changes performed by Road selection panels // after changing traffic rule vis other tools. // TODO: This code will not be necessary when we implement intent. @@ -388,7 +389,7 @@ public void RenderOverlayImpl(RenderManager.CameraInfo cameraInfo) { /// void DefaultRenderOverlay(RenderManager.CameraInfo cameraInfo) { - if (!LoadingExtension.PlayMode) { + if (!TMPELifecycle.PlayMode) { return; // world info view panels are not availble in edit mode } SubTools.PrioritySigns.MassEditOverlay.Show @@ -563,7 +564,7 @@ public void OnToolGUIImpl(Event e) { } void DefaultOnToolGUI(Event e) { - if (!LoadingExtension.PlayMode) { + if (!TMPELifecycle.PlayMode) { return; // world info view panels are not availble in edit mode } if (e.type == EventType.MouseDown && e.button == 0) { @@ -1001,7 +1002,7 @@ public bool DrawGenericOverlayTexture(Texture2D texture, /// Shows a tutorial message. Must be called by a Unity thread. /// Tutorial key. public static void ShowAdvisor(string localeKey) { - if (!GlobalConfig.Instance.Main.EnableTutorial || !LoadingExtension.PlayMode) { + if (!GlobalConfig.Instance.Main.EnableTutorial || !TMPELifecycle.PlayMode) { return; } From 01ac7bf67ff4e2079d66b755b8d5845938f43882 Mon Sep 17 00:00:00 2001 From: egi Date: Mon, 12 Apr 2021 20:15:07 +0200 Subject: [PATCH 20/23] fixed merge bug. --- TLM/TLM/UI/Textures/RoadUI.cs | 4 ++-- TLM/TLM/UI/Textures/TrafficLightTextures.cs | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/TLM/TLM/UI/Textures/RoadUI.cs b/TLM/TLM/UI/Textures/RoadUI.cs index 770ccc32f..26d76e5bc 100644 --- a/TLM/TLM/UI/Textures/RoadUI.cs +++ b/TLM/TLM/UI/Textures/RoadUI.cs @@ -20,8 +20,8 @@ static RoadUI() { PrioritySignTextures = new TinyDictionary { [PriorityType.None] = LoadDllResource("RoadUI.sign_none.png", size), [PriorityType.Main] = LoadDllResource("RoadUI.sign_priority.png", size), - [PriorityType.Stop] = LoadDllResource("RoadUI.sign_stop.png", size), - [PriorityType.Yield] = LoadDllResource("RoadUI.sign_yield.png", size), + [PriorityType.Stop] = LoadDllResource(Translation.GetTranslatedFileName("RoadUI.sign_stop.png"), size), + [PriorityType.Yield] = LoadDllResource(Translation.GetTranslatedFileName("RoadUI.sign_yield.png"), size), }; // delete priority sign diff --git a/TLM/TLM/UI/Textures/TrafficLightTextures.cs b/TLM/TLM/UI/Textures/TrafficLightTextures.cs index a4ceb0b5c..8ce395265 100644 --- a/TLM/TLM/UI/Textures/TrafficLightTextures.cs +++ b/TLM/TLM/UI/Textures/TrafficLightTextures.cs @@ -103,11 +103,14 @@ static TrafficLightTextures() { tlModeSize); // pedestrian mode + IntVector2 pedModeSize = new IntVector2(73, 70); + PedestrianModeAutomatic = LoadDllResource( - "TrafficLights.pedestrian_mode_1.png", - new IntVector2(73, 70)); - PedestrianModeManual = LoadDllResource("TrafficLights.pedestrian_mode_2.png", - new IntVector2(73, 73)); + Translation.GetTranslatedFileName("TrafficLights.pedestrian_mode_1.png"), + pedModeSize); + PedestrianModeManual = LoadDllResource( + Translation.GetTranslatedFileName("TrafficLights.pedestrian_mode_2.png"), + pedModeSize); // timer IntVector2 timerSize = new IntVector2(512); From 290e8ae3d60c7600c79c28be564e13d9866fb06f Mon Sep 17 00:00:00 2001 From: egi Date: Mon, 12 Apr 2021 21:27:26 +0200 Subject: [PATCH 21/23] Converted .ruleset to .editorconfig --- TLM/.editorconfig | 36 ++++++++++++++++ TLM/Benchmarks/Benchmarks.csproj | 2 +- TLM/CSUtil.CameraControl | 2 +- TLM/CSUtil.Commons/CSUtil.Commons.csproj | 2 +- TLM/OptionsFramework | 2 +- TLM/TLM/TLM.csproj | 17 +++++--- TLM/TMPE.API/TMPE.API.csproj | 2 +- .../TMPE.CitiesGameBridge.csproj | 2 +- .../TMPE.GenericGameBridge.csproj | 2 +- TLM/TMPE.UnitTest/TMPE.UnitTest.csproj | 2 +- TLM/TMPE.ruleset | 43 ------------------- 11 files changed, 55 insertions(+), 57 deletions(-) delete mode 100644 TLM/TMPE.ruleset diff --git a/TLM/.editorconfig b/TLM/.editorconfig index a480af62f..9e581602a 100644 --- a/TLM/.editorconfig +++ b/TLM/.editorconfig @@ -5,6 +5,42 @@ max_line_length=100 insert_final_newline=true trim_trailing_whitespace=true indent_style=space +dotnet_diagnostic.RAS0001.severity = error +dotnet_diagnostic.SA1000.severity = none +dotnet_diagnostic.SA1005.severity = none +dotnet_diagnostic.SA1027.severity = none +dotnet_diagnostic.SA1028.severity = none +dotnet_diagnostic.SA1100.severity = none +dotnet_diagnostic.SA1101.severity = none +dotnet_diagnostic.SA1107.severity = none +dotnet_diagnostic.SA1115.severity = none +dotnet_diagnostic.SA1116.severity = none +dotnet_diagnostic.SA1118.severity = none +dotnet_diagnostic.SA1200.severity = none +dotnet_diagnostic.SA1208.severity = none +dotnet_diagnostic.SA1210.severity = none +dotnet_diagnostic.SA1211.severity = none +dotnet_diagnostic.SA1216.severity = none +dotnet_diagnostic.SA1217.severity = none +dotnet_diagnostic.SA1308.severity = suggestion +dotnet_diagnostic.SA1309.severity = silent +dotnet_diagnostic.SA1310.severity = none +dotnet_diagnostic.SA1500.severity = none +dotnet_diagnostic.SA1503.severity = suggestion +dotnet_diagnostic.SA1505.severity = silent +dotnet_diagnostic.SA1512.severity = none +dotnet_diagnostic.SA1513.severity = none +dotnet_diagnostic.SA1515.severity = none +dotnet_diagnostic.SA1516.severity = silent +dotnet_diagnostic.SA1629.severity = none +dotnet_diagnostic.SA1633.severity = none +dotnet_diagnostic.SA1634.severity = none +dotnet_diagnostic.SA1635.severity = none +dotnet_diagnostic.SA1636.severity = none +dotnet_diagnostic.SA1637.severity = none +dotnet_diagnostic.SA1638.severity = none +dotnet_diagnostic.SA1640.severity = none +dotnet_diagnostic.SA1652.severity = none [*] charset=utf-8 diff --git a/TLM/Benchmarks/Benchmarks.csproj b/TLM/Benchmarks/Benchmarks.csproj index 73b4cfb08..8f9f18df6 100644 --- a/TLM/Benchmarks/Benchmarks.csproj +++ b/TLM/Benchmarks/Benchmarks.csproj @@ -1,4 +1,4 @@ - + diff --git a/TLM/CSUtil.CameraControl b/TLM/CSUtil.CameraControl index a908f9cbe..ec4e50b87 160000 --- a/TLM/CSUtil.CameraControl +++ b/TLM/CSUtil.CameraControl @@ -1 +1 @@ -Subproject commit a908f9cbe1d3bcfa632ca6557ad6fc028ddbcf76 +Subproject commit ec4e50b87903af8a67eefe0a4c4cb3f9c2b8e306 diff --git a/TLM/CSUtil.Commons/CSUtil.Commons.csproj b/TLM/CSUtil.Commons/CSUtil.Commons.csproj index 228b2cea2..415d9b494 100644 --- a/TLM/CSUtil.Commons/CSUtil.Commons.csproj +++ b/TLM/CSUtil.Commons/CSUtil.Commons.csproj @@ -22,7 +22,7 @@ DEBUG;TRACE prompt 4 - ..\TMPE.ruleset + MinimumRecommendedRules.ruleset pdbonly diff --git a/TLM/OptionsFramework b/TLM/OptionsFramework index 6a66e3bc8..e7758278d 160000 --- a/TLM/OptionsFramework +++ b/TLM/OptionsFramework @@ -1 +1 @@ -Subproject commit 6a66e3bc8ff68d9ca2f52313c3128ede81e64a54 +Subproject commit e7758278d38acd60e1a2b8862a10093a9c1faa08 diff --git a/TLM/TLM/TLM.csproj b/TLM/TLM/TLM.csproj index d3691a531..74b53527a 100644 --- a/TLM/TLM/TLM.csproj +++ b/TLM/TLM/TLM.csproj @@ -23,7 +23,7 @@ prompt 4 true - ..\TMPE.ruleset + MinimumRecommendedRules.ruleset pdbonly @@ -33,7 +33,8 @@ prompt 4 true - ..\TMPE.ruleset + + true @@ -44,7 +45,8 @@ full AnyCPU prompt - ..\TMPE.ruleset + + true @@ -55,7 +57,8 @@ full AnyCPU prompt - ..\TMPE.ruleset + + bin\Release LABS\ @@ -65,7 +68,8 @@ pdbonly AnyCPU prompt - ..\TMPE.ruleset + + ~/Library/Application Support/Steam/ @@ -86,7 +90,8 @@ AnyCPU latest prompt - ..\TMPE.ruleset + + diff --git a/TLM/TMPE.API/TMPE.API.csproj b/TLM/TMPE.API/TMPE.API.csproj index 3b6ccadbf..c3768ec52 100644 --- a/TLM/TMPE.API/TMPE.API.csproj +++ b/TLM/TMPE.API/TMPE.API.csproj @@ -20,7 +20,7 @@ DEBUG prompt 4 - ..\TMPE.ruleset + MinimumRecommendedRules.ruleset pdbonly diff --git a/TLM/TMPE.CitiesGameBridge/TMPE.CitiesGameBridge.csproj b/TLM/TMPE.CitiesGameBridge/TMPE.CitiesGameBridge.csproj index 95ad7e528..4141ff5bf 100644 --- a/TLM/TMPE.CitiesGameBridge/TMPE.CitiesGameBridge.csproj +++ b/TLM/TMPE.CitiesGameBridge/TMPE.CitiesGameBridge.csproj @@ -22,7 +22,7 @@ DEBUG;TRACE prompt 4 - ..\TMPE.ruleset + MinimumRecommendedRules.ruleset pdbonly diff --git a/TLM/TMPE.GenericGameBridge/TMPE.GenericGameBridge.csproj b/TLM/TMPE.GenericGameBridge/TMPE.GenericGameBridge.csproj index 5d8de6033..e9fa69632 100644 --- a/TLM/TMPE.GenericGameBridge/TMPE.GenericGameBridge.csproj +++ b/TLM/TMPE.GenericGameBridge/TMPE.GenericGameBridge.csproj @@ -22,7 +22,7 @@ DEBUG;TRACE prompt 4 - ..\TMPE.ruleset + MinimumRecommendedRules.ruleset pdbonly diff --git a/TLM/TMPE.UnitTest/TMPE.UnitTest.csproj b/TLM/TMPE.UnitTest/TMPE.UnitTest.csproj index 3e4ef0c2e..52eae201d 100644 --- a/TLM/TMPE.UnitTest/TMPE.UnitTest.csproj +++ b/TLM/TMPE.UnitTest/TMPE.UnitTest.csproj @@ -27,7 +27,7 @@ DEBUG;TRACE prompt 4 - ..\TMPE.ruleset + MinimumRecommendedRules.ruleset pdbonly diff --git a/TLM/TMPE.ruleset b/TLM/TMPE.ruleset deleted file mode 100644 index 3f6fbca50..000000000 --- a/TLM/TMPE.ruleset +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 70767bfbae88813b829cdbe69f2bd41ead3539ee Mon Sep 17 00:00:00 2001 From: egi Date: Tue, 13 Apr 2021 03:31:33 +0200 Subject: [PATCH 22/23] Removed warnings SA1600 Elements should be documented SA1602 Enumeration items should be documented SA1611 Element parameters should be documented SA1615 Element return value should be documented SA1623 The property's documentation summary text should begin with: 'Gets' --- TLM/.editorconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TLM/.editorconfig b/TLM/.editorconfig index 9e581602a..52d6f86a8 100644 --- a/TLM/.editorconfig +++ b/TLM/.editorconfig @@ -32,6 +32,11 @@ dotnet_diagnostic.SA1512.severity = none dotnet_diagnostic.SA1513.severity = none dotnet_diagnostic.SA1515.severity = none dotnet_diagnostic.SA1516.severity = silent +dotnet_diagnostic.SA1600.severity = none +dotnet_diagnostic.SA1602.severity = none +dotnet_diagnostic.SA1611.severity = none +dotnet_diagnostic.SA1615.severity = none +dotnet_diagnostic.SA1623.severity = none dotnet_diagnostic.SA1629.severity = none dotnet_diagnostic.SA1633.severity = none dotnet_diagnostic.SA1634.severity = none From a226e160954eba757cbd5f8971e5712dbd72654f Mon Sep 17 00:00:00 2001 From: krzychu124 Date: Wed, 14 Apr 2021 01:39:59 +0200 Subject: [PATCH 23/23] Removed invalid references (RedirectionFramework deleted after migration) --- TLM/TLM/Custom/PathFinding/CustomPathManager.cs | 1 - TLM/TLM/Lifecycle/Patcher.cs | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/TLM/TLM/Custom/PathFinding/CustomPathManager.cs b/TLM/TLM/Custom/PathFinding/CustomPathManager.cs index c3c957957..8facd27d7 100644 --- a/TLM/TLM/Custom/PathFinding/CustomPathManager.cs +++ b/TLM/TLM/Custom/PathFinding/CustomPathManager.cs @@ -11,7 +11,6 @@ namespace TrafficManager.Custom.PathFinding { using API.Traffic.Enums; using TrafficManager.API.Traffic.Data; using TrafficManager.Manager.Impl; - using TrafficManager.RedirectionFramework.Attributes; using TrafficManager.State; using UnityEngine; using ColossalFramework.UI; diff --git a/TLM/TLM/Lifecycle/Patcher.cs b/TLM/TLM/Lifecycle/Patcher.cs index 16a04a863..dcf5d16ef 100644 --- a/TLM/TLM/Lifecycle/Patcher.cs +++ b/TLM/TLM/Lifecycle/Patcher.cs @@ -1,9 +1,7 @@ namespace TrafficManager.Lifecycle { - using ColossalFramework.UI; using CSUtil.Commons; using HarmonyLib; using System; - using TrafficManager.RedirectionFramework; using CitiesHarmony.API; using System.Runtime.CompilerServices; using System.Reflection; @@ -43,16 +41,6 @@ public static void Install() { AssertCitiesHarmonyInstalled(); fail = !PatchAll(); - try { - Log.Info("Deploying attribute-driven detours"); - AssemblyRedirector.Deploy(); - } catch (Exception e) { - Log.Error("Could not deploy attribute-driven detours because the following exception occured:\n " - + e + - "\n -- End of inner exception stack trace -- "); - fail = true; - } - if (fail) { Log.Info("patcher failed"); Shortcuts.ShowErrorDialog( @@ -96,7 +84,6 @@ static bool PatchAll() { public static void Uninstall() { new Harmony(HARMONY_ID).UnpatchAll(HARMONY_ID); - AssemblyRedirector.Revert(); Log.Info("TMPE patches uninstalled."); } }