From 8e80afbf8635f3ea36dfb060a598f7de91611407 Mon Sep 17 00:00:00 2001 From: xxalpha Date: Sun, 25 Dec 2016 03:25:54 +0000 Subject: [PATCH] Map Conflict Fixing Helper (#22421) * Trimmed down map_merge function to be simpler and straightforward, no more recycling keys; New function get_key, looks up a dict key by abusing python; New function key_int_value, converts a base 52 key to a base 10 integer value; Map helper functions are no longer dependent on global values; Reorganized map_helpers.py; * Added Map Conflict Fixer Helper; Changed parse_map function to accept a map's raw text instead of a file path; Tweaked some map merger code; * Map Conflict Fixer readme.md entry completed readme.md more Removed debug code --- code/modules/admin/verbs/mapping.dm | 6 + icons/mob/screen_gen.dmi | Bin 115627 -> 115826 bytes tools/mapmerge/README.md | 31 + tools/mapmerge/Run Map Conflict Fixer.bat | 4 + tools/mapmerge/map_conflict_fixer.py | 103 +++ tools/mapmerge/map_helpers.py | 963 ++++++++++++---------- 6 files changed, 692 insertions(+), 415 deletions(-) create mode 100644 tools/mapmerge/Run Map Conflict Fixer.bat create mode 100644 tools/mapmerge/map_conflict_fixer.py diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 4d01a5b4d9f..09b7e3c79d9 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -45,6 +45,12 @@ var/list/admin_verbs_debug_mapping = list( /client/proc/manipulate_organs ) +/obj/effect/debugging/mapfix_marker + name = "map fix marker" + icon = 'icons/mob/screen_gen.dmi' + icon_state = "mapfixmarker" + desc = "I am a mappers mistake." + /obj/effect/debugging/marker icon = 'icons/turf/areas.dmi' icon_state = "yellow" diff --git a/icons/mob/screen_gen.dmi b/icons/mob/screen_gen.dmi index 256bd412b4fb47a424bd42ca7718e77875194c52..40500e84a8339b037712f2088662ac9a5930d308 100644 GIT binary patch delta 11907 zcmZ9SbyQT{7x#ydloF&HDd}zy1ra1A1z`wjBqc;kc zW|Mh0F1)VybH+*2eR@jB-q;yG-xCYZz5?iz9)gkCaeF(JZeQc__nnE~yLT=Ke1Gtj zs3n_LRX*_+mfHxfZvkjZNYnLApdz$PjddUWXz(_@FQsZ)6k6RBAjS4=K}X@uC3$>| z>pc4;IkZJF5qf08VKVz)leoLKqpeqnG{;aR3-G_nqpY&Y0$XyUC0|TMed&K&nR>l^ z+qzgnem6jLD{=!e@AS~T8G1S640#_(E||#mIm6dfq{Qt_{|P&F$bfQe+%G^H&5#P6 zKYZ5+p(B`hILIEmp6Bx-1K)tfF43 zIq&$kZq8+{I90YmMoIzrG+zKiOgr9l{o`1UBTJ*{E|rZwFXU!EI=k6I3=1GGhBWim zCH3Y=-=w|Sf_;dt&p&&uJaCrJziyYmtjI+*&40LBl6-Mg^LJIPE-RUrC1&9KWYKK= z19}Y}(uhVKF9w=Y@eG$HbfmqEzwIsNP}LTS*d9k! zPP!P|QMUCFgdoKEn0N>l`Wb;eamk3X#~EaPBxc#KSfV9*8JSjWx-6nUa(T$inWR0c ztpAiXOD{v|kS_R$=r>HTBk2RzqnV*vDR39p%);AsFT0HY{|lnuau=J~aRb?g`isoz zo)P&{6p;%gKIF@5R0hMpI??CvxA&Xxj^0Z@-d+XF^d?dStCL1jV&R+eU%4+y_$33K zzvL56FimdmGH9QSz(N&ijScUYCHd0xejAehZvLsaZKU^+H~y~fO#gfo?uUb41o8Zx z45PMJs5fr;Q6YxF7p>M%$D`6FbdyLOto%JpE2$_r?Pt6`2*e4})=)PI%KY2>#)Qtf zp*1{^pe~RA5BnJ?9%{m>NHf~=XZ4V_k@D7L`GXypA2}wA_7s)Xa#=p?FymZ`6pkw~ zXLHc~JlXXOS$v1k#x&U6|9;LLR)ZS127yaF*X;=C$8JQq|Vg6P|ggi|j*%i=j&>uvI_&Cb5sMdk|70lgz9+ zAJ7HkTw8B3aHYkTQhgRo4Ac6qhyJB%k}vPMR!Wq^-RH&`01jDeKZv7Jr#>E?n21PE zPtS?&qtjBivSN8wWHS25@jZg?i465J(PjR9V7Ob4jxTLM%t&;`v^l1PIcgC;>su66 zMXe|P6&%J1fUb)fd`vqkOM;C=C&kg}w#!68=f&`?WKLaL-OW@1{=aiqp zc0UN)Vd*We@hCa_o|8*7YmG=WDU|S-YC61wiBNy3;)f)C77yda6jvwjzh1Zt1ytm z8skpGsI+E7m_OF_w2;OT{)szdkgu47h?mJ$hx|!)UQ5JCDr{Z*r!@V8y3W}3q6x8g zpA|iG9;EONH+^{7`>WEz+mw?e?0-i)sbuRv&*FTgsZNyk-|a$y9*~%Lj%8&yJw6vn z&VsgY8#QOctw;2`zexaYZG4dk^%VZcu|I28?DP%e?fq54IAg>mbRw*&vi}eoQ4gqBAIZu7XGqNZFyBzz zFygLor?vQ$0b~M{xn5$|FhpwBRwHP zRjQ+GaHHSdKvAXrVE7@Y>uG<#Es=r3+4oPYU$>J00PD2OtbVSI|G&TN_%J=eyE@W6 zY;i7y;^6vlxGb5Y_|;lo3b`vIgI%3$=51S6l+iv*bPL1jrsi^amM;gu_^J}9#-vtW z*+0yRL0u-}@sWfH5JVVZ&@i=!7JTwgG6n|+`7<&yXp>L@7Xc~CH_zt0=ck{NX{4Zx z>LkEjhcEGY*xV;4zp4c?FB@ zt>wYc)Eu!wn_!UBeR;nEts5A* zdzkXp{>xqvEpAqL5>+Sl@?D);4WaoBn&#&NZ9WS}=r2$?`iiFsQ^fngY`Xe8bkNxl zzA5#Cs#HTK_9kjH$_CVvyT=I%xn%Y6~6P!TLpBRRbungH-YLzxP{^gU?(nWPn-@rY&<5D>@T zv>ai)yg?Sv0(s0#r|I|MJ6>>i4Hg+VbKq(W5 zj^XLB!55+{yVW?(a+R^4zlj~bJ$^>%zFUmvB1j?R_N)-xd`BM@orGn#hohsXS%&)^ zytxmKjoEB*K`+VKG5l+>Ny}nr_a^;PC0(Gpm_y)kNbCsA8@4xYU+`(C>{K%?zrXv$p$RM z!q}B9^LPge8F*N?E(LGlvNJ~)AW5gJG90M5>%(&XQ7;Q*`{FYH)UWnh`DYO=lz(LQ z2jQRm!w*C>kuB4xDxU=S7;>MFuuB2z{>MZEz#E)Fji!E_Kel4x-aU?X3&qXzV-fGC zlq_lWW@3Uv2#xM%S!aE0(#mxU{7%#5bK)Mx@abw%DJWBKa6Msmi~Hmr@GI}&aftM3 zm4gz_R%y{NZWQA6b!v?((LGAY=UI9pf!gDF-Ie!Gmxn_6{cnVG%I>>R{UR|UjXHv|QgxxiM3sdX3{I1FJSFy4SIbJ`xu1B*G`4GfN zsT_ao4<4%XmcOscSm0#wqg8t_o4mn!Z+AHgN z-whE83M7@xa==@QMp!#9oA3=&xDfpR>ZD-nx|@ z(C+ST2~|~_V15W^2KbE!p^+yE&w2Q6Co8DsGkid?QIS9^Yy+aysCY#l$KcQ}7B%ZE z=b0x^8F9Jhb8uvLTdgwkLSNth?}SBRcXuygbk_OEw~Xa^&m_pP9PNpwhdo+2-|0l& z_b-sQuj#F*79D&1D~(YtF3CwXpov6@OV>LOB})GLen9L0?Og@LDZfly=8jz`?&wc$ zRd#z#GbqUL_99?;El2%}x0@XJo* ze7aX78>|e>;xzqq?ceAKMjO4jSKxH=`)IXMlR;gq@WeiMB+#l&H&FxVR=DvUO^sc@ zd>j8z>&IcV{Fx>yeB|T!EhuIxBPgJo%K&BH*s`zob22rdGB&B=NUglrdrlV|MeC<{f|g_bS&i*Jp|Gi?=-8 z_`L3?s7XL;*N}^5!f}A*vdAu5J@mnS&1Fw}ONVhwC#VIw*kewmK*(bN}&uF`GQ|)O5f4~jx6vJWi@3YQ_G@%M8p*^e;{nwU`!n4jj z6;MmDg~Kb`70{`1hJ50&F<}}fJ+(+(o%b=-Sy6u~&FhC|xI9ARPLFXzTPc-FzM#MG zhwa_nU|?;__R*v5?!Kaludq(Zh2P$*?Tzh(Fjara0WLy&`?XIy+%q+HSu~RhzPfv` zn1h&Ca~bI)Gu2Z8KMM2C6bJ&JLO`G;O3~XvnFt0~ypKCnU;P(QG_@@-)|XYU><`s2 z7Lk}6zfURVHnb#|We+wD=(3#8aEpx==IuE$2Ow@$xejKFE*N`8bAD{j7Cu5z*@H*_ z=GTvE8Pt>jZSwM`9xR2QFd+?tbygNG(j?#79osLw9@lFb=lnP7dUA^q4D@0|p{+*i z_ktWTs2OB!LG}C_>#R8sggAd&{p1z0(LR%fLG(q?#X@}%##tXm=1%f%8~S?W^=)&& zblTq3Uj$ckE_d2;ALqH@;$wmAE>gX$L{sHrfXiU3{bb3=<>VgcLty=&dJCu^51XMaj6-VgM_;1%{Cogr0arDDL%pEbI~ zr`!b{9PmYk&?nrjcKVD@eNR}Z4*-jtm46NU+DDD_C)d{dFO=6TNDbM&QY`P!_{aAn z60DYmaEXW@P*YOcn{Q=T@l6>V8d}4RWR+BEoJz6cGWDc{le4pf44j*o+k-a5VKV2} z){x%5TA-&JVBtVYZmPQc=kocurs2qpMR*=D7AG&*r+;7d`p3;>|eO zDSEFk5%0$XVUU}f+nq^+A5D;;_MEY0^?Mp$*EeT!6qreQl^8Tx0B%nO>Tcixv^8aFgFFp#1tS^4|-)mKya?*lSVJsCn; zx(%83i9qohzMmty-ZLn6?r*{rPH7XBNJy(Z{72b?%w0cNy9gem|*4CYZ@+m6A44*`Zu^#$Awxj4~pe+L(K9< zLat<+_Bdvnla@ikXlpze&$R4FIt$iORzxJdFxAcKRZ~pe_0%9-Koj2bgKAmH_kk3r zit35s59t`-6H+a(H5E@P2ll8H!4a7UtF7Mu*p0P{MO~?ecx~ZVCk`c!zsK(GyruQcnpj!BGww>CwD7#jQCE)u@Re>^wARvchYpL&>3$ zoMr@F9u>_1T+!ocm)}7pQI5gCZnJ_HJIu5pfNCQ+#6oND6?3Bb#QgmCLb_Wkhx1a$670;^12j^isof}BD#h0$JY+5SJleTt zo0esi!j`C&#M`|_4il`fR7TNo<2P_Mc-TerYx}9SEBYFbqPyUbGsc~d=|5V9m}M^ z+&!|woLWGA&`a=Dwt}BiR_fL#jdjNI;_^ckSkzV;6%xO;l4lDce8qvj@s%;C>;03FAZGf$|hTx@LY(=NY+1eq?6 z<4P|G!Dh%xU{^qg9(YMLe!GAJgK~iI{bG3!O`^xs*8%gzo`Pdh+!XB%G4ZBYIlzke z_yWFPH+gulzk<`2Dq@cjk7p441iCmp#z)e@9Cd{p%kKmASEOd(5mm>njAAcjnOSOr zSM1je6t;=8Lg>CgSwBD>w*xN%yiF&}reO8gZx&=~`^~VpFx;^F3jVBWxO=IfvaL=P zJd*o@-8KH`_gralad2*N@ooZOTC$rR7#y?zNmX`DLZLRJ1j-K?jC8Sh+{ zePHZE66+Wa*iR8tma$7^6IZcunj`6E@*K??7SZTLQ#@@g>*QdxI>r&4^n6coN#f8aP>Ig$M9=M(pjPq@C(kIqUb~dQ1Xaxv zQIeug)+u&mEEO_(+ORWixGyp}taDu_S-+7p#1S|z{K+iSQJ z3!~)w_ly5V<{=~NOKpP7epCHCu9o=T$BX@YyQGi0apxD8r*Kf2%d}6gIx)gyu?vcJ z1-W7oUgp?S2o_WC?@KWz;DQTS!Pif4FNK};(@+j=MDHqGy(tJ-+m%uu&`MEgZ4be( z1gx8J?CrLKmhA;4yE6upQ7``8hNlgBmL*BXik!zwhJ4AbY$)10TplQRdFm|rw&6-6 ze}-^(|3y>E`0EShP3ad>QS}39$hdeXOAj-2Lze2HolZykyIRjiF^J1kYi^24X*osi zuWyo}R+iw@B~@QxFnV`7q$>dDfDLN^N7oGIG{gRxiStcAsk^iVda$h?%}n6XzTG!p zILArS9*=m|6EJE%)cP^X$t5b2r%IpZgvKOqx8_NAO7S zZPc~gsf_<5&WmBYFMqXPBdW9ARLJD*yTfxaz{9WJZJqb7y$ z(E8)pjJX_ZDh@25B5KoTH#p{osK43oV4SH3V};7@!i<^J?F`wFHORWuiNzSx$(q*X zpvh(YUp{7>T0k9Dbm^gF0FK|_gEW0exhYcwsc}=sN8FT$@Uqk4n4O5-X;AnFRh*!7 ztmv!2O9LwQ$(s@W%CbW3@4-7VTTK(bBhHVSu47Yy${eSeAf3Oza1ofm7HF?{SPHK~ z=%de8M!Cyw_AdxPL_e=h8}D|Z{ty{YNGoIPKpl*i-f)EtE>Fxm0$tOcwcW`QFp+H^ zmSqWxsuj6du6w#@ETewTbw6Wg=j$twxS}G;5#u~6d-B%pdrS9YEv>qm+FJSqq26Of zS21AX6m%)ol7g>J-R7Q&LX^f@bIGn>{(7nP%jWjrt_jl$9 zXCVdc{hSJJOsMm|UjSmnoU0EKyS>%A z;%}vPeg8~vnE%bZ^+DY7@ufN=P*9~cSZwOws6i?^d#`JjxDmlAfMo4C>@O}Z20sT+ z!%0GBH=AFa*`!?f>ljt47U)!Yhf8d}LEBtErY8Kp0kQ&a?<}<;N^$K`$v}ZEP6>`| zi*out)Ek{z*G0D<)Q`-~HS{YwsKgXe672c>I`19)W_95&rMBijdAXsg(7ah)#*5Fq zH`b`%0|r7(wm3kH4&R%JO|H&uXbv^qmrb@h8?t6(i-%#;Ei{DcpkT5HH(qFHou zs8|jjmpC-`;AcPan-a*eUbIz^;nsoN%~?qOZ7Q}!m0B#uQ-J@WMeWb6p}jIY1p~vS z?(~BdEj1VY&7+&?x>(j}&N&;@%Tytz+7=n?GvISlk)pYJb=khW>*cJOZb-)P@8*k* zmw7+*6KPr$zvfgF>=<^Q{%WQ5!eU>~YH_^4-J9@N$Ys%N<$-;C#H0 z_y`o*jDrA3*t`#k*k5#%Hr%~KkyRo8){BVV>XIjR1z0caiD+oxu^RRwl@*EI2QPKj z-S|MZQ9cP^Iakf$eg=6aF(ZjRsD548e&yp-w8j^@>$%4(e=f`NA05=~%9z5}d#cPK z?(pV#gBsX){YmhG>3)oC-ZD^DaW#A1|3iZ(xoPp>wRtOQ?#uPzJg?>lJi0N}u-*Pc z39)&+$WDQrF85`UutJX)B@fW3GC7scVnS&X%Az5cqN?dvGpWb%maw|;(*WdpBd&ht zYlGijuuAQapKj%g)xLGRPV(p$Lv3q;rQioRoW-FGv^T1Yl5D; zo@43Dzqb!2bXzJ7@c~;6r{-2B`N!O2bwv>J8G`{g56ifMz=gf19H7T_*q%Ii;IKni zpNsux=?WAU11#JLyOF4AX%WyILlD1rUYe&dbrq=CRC2l2-|V+cXdRAR8hnYm!sk3h zOmGxfZAfaTzW#N`jO;zk1hjYA8BzDGNM0Lpu4yV;@*}@>03bG7YD&Kb7M_`zdXwbb zF=Do@sy@OgQ5&2XaGSCup=h5 zsz=((3kOjT4&fxv!A;pjN0T{!KS>h>cp6Rd$?hB)?O`iUfd^Yx#)+skP zmyNY50apy!A6a5X5FelD_bM6-8Dny3yac_-NPs$#Rf$x&So6Yc(WpdanXvQ)s!9;G zOU#3sr+o*d;?(+5)+00=`=0S3RxXfgEgRDOo0uMwhCG~|Cih^Q0B%OCpIY9T$%7?6 zf$M#*m`2F#@Ni>ZGq*ly+DQM{UhXO1- znV3MX-`?)U%`_frgDj<`94DuFI?W(J)hqom;Gj7?>Wf9D!(n!J=sSW=l;WZm_|K9) zF-9&4?-e^Tkaa@WpU7g=adbAr5>^$HjNCQ;_9 zUu>@FWnedF1$uyi{#*fCM)v0O@%3Mim)6`Q?F5}PE3%l6rib79wz6<@+V<#kg_AiP zOj|YAzdkgz1khX>kZ3fxlB>o3+I#i9Dfox20Sa`qI}MK`247R|?v~Rfz;SI)X(%st zvY5)H8*J1(m+DQd`z|M%b4xpJmsTKG@ekDKt3loF zJ(TSC1w6y%3rq-mdxS*P3;k<&vy0JP90jFf5N&d9#PoeGF|uj|+H0(<{#4DM>i4sa z>v$8c#?OHW@+0qSXiKY^vi&2}IEV$pqo~vnK<$TO@CVW*tbUmcv(ba`K?6AaadO=f zODCM%;?cs1BZIOnC?p`e_jLaK-2+x6QlL7wMNh-3Xql*ELJ0Yb$pp|FOQnBI>WJQH zR}DY}b_kvT2(wSZg?J{b)gxgn0Y!I%?~+&3{f8|mCS#(G~s{s zSDI8{z#KWmunick^I6EkgCiqz?#}+XVi+#z16jgdER2Op=M`6LsiR!x`6|rcEG#ai zhGNNB12C0u2Zy(~gypv^YCV|bx6(3|_gtWOEA5fMMWyeCo3!lB-J3o5QlRI2?ksQa zQ|dMW*;Xc(O@0dHMD-@)W)|9@`NC5-k=2^OBl)+e*|&Ne13PF|MPry2rb;#B&<`(o zvK$i>;zE6x_i%(G?4!Ad)=kHr4{p77(ZFNG^Uyi;U?47tNlkiAdS}n*q}hwTV;nqh zGO+~wV}#Ypq$}Esx=tf-2qS~o4$G4(xF!mv@%x~R5;cNc&4EG3w%xJk*DBlah~~Fr zOK0;7U2u{ohjL5C`NvO52spu<%{D84P6Mt<(993quVsn)CL?CQ%9r3(6c?jR|E>9d z3*wa?O|-tY>E?V!oyqOpo_A}KW`2$hU@;{e*&z?D5oz?>M|wTy(LWVRgm)6M5G@?U zg|^z|e3jgFolXo|LTUcdVLX)E^HYCji%x<2&8w1W5Y8N;L#b3RIdj-)2v7I~kPq}6O)z9RaetFLXy@w>^0!M?O8-0_H ztIcl&agp}boiOd41kJ5406kJ2NaSuKT%~fnOVZUAMD<(T0y(Rii3J|RjS|{)aO+Np z3oB!abl1Ul@sFOH<7RNSRuV&O$OFvn>Jw+xvjnnR+*I-u%9*nXsiOsYgvm(%bciQqyNYSV=y&Y{W&DK`oc{m#56YXX@Jp#^+#EFic|I2>kxBO;bw8xKf@Ks(}W|*a+B-!SyTBKgjGY%j8=JGPA!O zd#Tz83nE=KXj*S5&dSHSwXHbaMLqv^T~5*Eu7ml)U(>Qjgp-aGOGxuQ#I%qcAu|wn zvv@z35zdQxsu5n8k!~`1JIrD`hIOUBKCb)<4vI%FJNAEh;|5N=`7&o7#yKEgPS`D< z76cmM2W0Tx0%QF+aQP_Ei})V|vu2sH!}St7=*)n( zzQp6)2ZPzeO*ujfvE-l}H9m~)V>$RvD1dS`PHA|u{I0Ukv6pDXAj2qSPe%uwwk&Xl zVf?G3a+9ZqJ;T^C;F)pVe-LuiM%=>P>bW`Hw4l4X`aG2LK3#XDxQTFiBfhSS0xZd6 zZ1;8SE+`9psXVmB2PsfaiELs2OjlnyW$E&dTd%u%e}@I{&;LH$!^UGj)Y71UTh+4v z_5#tJ+&Y!ZyNsS1Qh3ZrTi-e*i{8n&w#gGqnAVX;eK}S;zJn8Oo2fpOo~o42Lz&O{ zPabqM*y5CQ`as3_KF(ZIW^(Q2;1I;r1^G@gG;KeY{o}_EOXo&1O z*{FF;*@;7^Obl(6G|8}+A|TFxXt8esx9@A$T*vivv%w0u{OMQ|g-73I?igA&Fr<$# z0D>88Z?7oJO7*wZf8VJ-P*NB64$%r_!7Vr>7X+S?HYId*F@u78OJWkd)={rqduqo1 z&2V=9vUa+&)Lt&nBYLZdNHA`~_qTs>QQrP~{`~ZB^TJE06caLs^-GM$Jrsy_u;OJq z&p5Gd0vfQ?4*o>>AMa`5&>*y6v~f?FCf(EVPN{uBeY~cGFPx^_oIyS=h-GK^xNVhF z2^ztHhLXN7q=Tum+|cP6(N|-sj>|ZRcfh+fS;X;iTd2!bYXbETk>aK0KJc;4dWS=d z226s9^B@pN5$Mnp-U$S z(tC#x;KlF%=Dj!bGB=Z(o5`6yyL)!`{PrA25tK#}B;Lo8PI_=3V30lqZ4;N{#&lst zPxSRSWN3d;U&|QZgKtm@U9;{AQkRsdm7iXT#`jj=l%PG2u8vELfmXpFju+qvcipe8 zGJG}xF3AG=YxcTBtf=1wN1E!1RmQ2pH;IV^xyfnWL;mq=CC0QhWX}>-4_^Fzs-dTu ztSegc(v??#%^lVP(3KKpzJ41dM8nkVlcYn1wO$2e8ZNP!iGme$MJ*Su+n4m;(2yP{ zN#c%0zg6zw#-ne~^d2IhvOMkNb`9gShvM!dy}#NKXQ`%4%x~(R@H5DeEC`ic{Cu*! zk5>PXJ>X~VH(i%8s(ZkuaITtTvHtYqeByv^WAVDK1l-b(jZog^Yu9YQ0`qOmD`5Ac zZSmnpEQfD43Cix{(%;LIt+BuW&H%+vist*%tdVAPXWWnO@yjInf2({oU-d%rcdt;0 z@xlI>J6}{kMfo@09LM`C^0*J?4n74oP<8KgjrK9gjz^nEW##tOClTyBVwZXqax2(% zR+Gu&Uy;8%-7>2)PdE;6vXTnn-A!7f1z%xR(s?;G=Nn&@FCjl8lcff)006zz9F<~R zs4B}zA%1}7Iu@BuVM~b2SUQc88#*<5@%PX1<`X#ue+pzj+pWjEjN#9azA|C?3JJ1) zvb(+B?h9csP~y+%XXC`$bKlY`IDpCW-t&m6DLWl=^7a9!#;OQ66Bo|HAmfgRSa|vK zEMgBrJ+@_*NYQ>qre)I}z#uY-?k7G&AOUDX3SVM_dK%e1 zp3v%xqx3nHxII37Y&3LG5hX8YKoytAl)z@?GNr7M@${NhBjeIBKAA6c#Ff*-hudCF zF{lu7U3 ztv!^_tdk%1RS2m?eb%a>i)v1^7H!&QwA9jLd9_JQgsuP5N4JPGblJCrZYwY4A%pS` z_e<5!{EeyrukXGPx!bs5b#zS^%m>z*-$={G(AATT-kFoY?@4*!3WRc^%s^6iSq1W22+N0|Nu0kr{mCVV^(K-%sxUcOTEJFr_HRvb@9J@&qhl zo^B=vSN>t9d&XJfz^Hd;^QwTk=jo$%t01CK5KvZXs;o=aS+cG2<89}mL+BHh<=Mfd zuL)u78X_5xyp*a;G{e+)#nOJD;1QoIM+RmrTqVLX?mHC;zEOu`2%iS=sYNKmTe_$V z1i!+8NkO3%ZAOhi7Gz9PiL#(S0MH9sx|Wc5aDU~?Y|ak_ur|26f|552B3j3rG<+}u zysy7c7#fv!lW1nC*e{`Gqj`~?@ zQ1@XF_SdC_hFmEAO_Ym`k0UAR*VQGwFhl3LR)jN!PScN(JQ%s|6Q_uAR1gHO3Y=A z$7J?D&+^LHaa!649oxwb3B%c=-tZvso&_J$lfEln<%|NPk7_CLwKMG=4EzozkWmti zojSt|6sjyhaNK-X0eW0!Wfl;S(}M z8&i42OUe`akN@OPq2!aKz8oSgl1vk>d~9<;*rY%b(l9J69Ks}5FdFF|QPin$dvn9` z#PM>Lhy1@Orx5Zw~W$3EGf zU!2!Fy^OK%w1-IXQJQZ@F7^a6!Lx}-z0eOy(Sh=J!2nLCfq#J=&i?w1{(BayV4_fV zi|3Lh(}7N^?A$Z2vN%@+SdcwEJ(fK$UEg|M)E&{Ks(yh3iD!#FS0wMsiq%nKSg%pe z4`Jrp&08;i?8q|d^FWI{szyiP$ymbAA&Cb5-P5+N4%{31)QX-5z<*j5wRlt_q&UVs zqnTx+jg;nWUs)jlHRC5-B5LTa{UtC1&Z+qH-D0zThRw?hdP0jfJ3Db`amifX5uGV zSgu=(B`R~Hl#Wa&y2Syx z9{=sHwaBU(%0_L$ z01az+$V0qX22D5ZK;LHsK%wLMVfRp4A%g;F<|4-SD(jk@4|vP3fSGv`#^PS4d8xOz zKC)H!ZOh?zi?-DV42NYdLmz}}(AP*P{bFlm0|S)nqc?A!A# zC#5>S#F1#*<)?Res&Z03T+RJH{E2)h@8@_#yw*~_d2RD3;fl1ML_0}8dY@UYqtM2E zZr6j#@6)?CfQJw){c5}}`^>4{ooN(2y2~`U5OumL8w?=aG^6@<%n~Kya_u z?Vt&SQHSd03BXaRq8=CVI}YZS(={$Ici>I4n#Ts@;x`l&Jb08m6X+=JVrNH~Z?9if z%IIQmAFYwYzRL)M$50@V-4yraXfwyCn9XYykoHYLBVX0(!EapQ`zHci?&NU{3X0L# zLO&YmS7~gW<|!GJr0Rp>A_Uc;=5>370caaK+P{{RO)w3jU%aQ;ytYso3&%%4O;5XF zI^r_w!+%|GpX!O%zE+mFZPQ`z5c!wmJHA#x$ZIffaggtW>U09SYPvmEIZwhO3fACJ zc7XP;FkWwO54O{M91(dZdL7mh*m)+P?{tgs`RzpRsrZm8iMb{w$ILp9nCz^Sb)MQu zS_NtK>>P#QYJ%pErbSz#gXM|ueTHRC#x(RmDo_?3SzpB1i|*0p3(pytBnKChay%+O zk9J3Y`wJql5A8=&hl~HD3fNvzk@G#Z1K9TlsD@oCAPM@^F?_^=957!}jFOlL9Ge=W zr4|=xXO7&&+Lf}9+uPeL+uIJ{_bprNxe?1GS1YpFS)DHO=r+j%&G$TP#~$7c?o5r} zb<QvNj2 zB*<2b$x4g~)ShlOi;ho7unY`rxqR)fC<#BFA97WkC+o+<5pi9zl*wGV?=X zK5w^Ix;Ig4BG>DvGg`dQln}YsVCmpL*Ui2MfA9CiC*b)9?y9?v+EaLyT(xek_b~ty z9FQ*V9!+xx?#Qsngm?`q_(>yodf%1eQ5{?TUf-ArLS z_6_2^*04Fad)vLFmiYRlH{ul1hAq*C7XESsccuTO%?bwhZbs(62TnW33?8;*s6S^h zVnyNp`2g|q!UEOZPd>~__TOCQsC$s1{KA$%!FMz9TrLCh#01W2gYld;9R}f*(-0ir z$3CJdtCrTGVUOU*D@4<9oYZ2h%kK)JpF1i?SriJcshzwJKVf|H%QZSVG$xm2=-8rj z)W@)0Jzn{R%oAdP74vN`r?WWm7)-cuD+*l5xOUeL)O0$=l;AbpfYGfb$`XnXLv z!Hc<;#j0LS>rp}rEDp|JofkewPP1hPCAjyp?*$r3#)L(ZhqzT?90}~H&p_I7Dxck4 ztg;+!R_tPspOwO8=@pWd*}p}+rWu>V9{??)8|?V)^U*&Ns5nS=AFIzy*F#p}kGQn< zCd-3jo&8LT;EsocGy-?S0#5e~+IvtYy2fx&TqWfW%pt#J{(L9ePYwuRV4Wuf}%5C^T znfludQ(U0P!(%hZN~>8{*u~PT-BaV0G|ChS){aLC3bB(yd(pOAXEO+32(<#GPK6op zVvoK6tZ(iZSE8TqIlt9O&>cNckX7|tlL#5T+bdN1>wQP*v%giXI%sI`J|qpQ(lLtX~z z?=aF&(;LPdD%=ESn!MMU8qJR}0bu@EDiV{L#yNE{Ub?4OTuk9$EVSLdanbx3Jcs8_ z==**mcfZ!+6f)BUiQ$+1^d<%YI*7TL3 zubNq6jCW$3tf5yYJoH6XCe$)?=*w~#D4ZIL+`Yr#OENOptd|;}Lhcd(F(gQjD;v7F ztgTRWs|6qXc20_gnRGYEEft?F{(O)zDSuc~Q}dN<_gh%x%*Ct=8`Jc0W`6VZ0 z+V?EIy(QnA2)MI{O14ky2|o5holbt8C@MO~mH3=1GPse&s8G;MNW8rxa5Q$8am+1( z?eyGV5M^)2Ev@7-M0dJlkxsOq#a&}@i(SS((|K!ekB%tp@~EPwe44J8ZQO{Z zP@qEuh3V7dMO*dW986J=t(Uj~>E313BJRS@{rOW&@5X9;F_MROz^TQQQC7YO<7Jz> zn{86lmBRekq`dg3J+B>X;XOKhH>NX8n7^tkqgEnvz@n}fXaW38Bxi;Gi7p{7u0!45 zwz9Hrt#`AM`Km$m-G-)C$~55{WY6SwpQ32H(oSS&nic4S)csaenf zW1RmO$`lruPWd6oVvBBKVKzh7^ttl|g~<*N5{mC*nY4E0Q_G+Ir-r)gdSn}&jP-Rx z*@R{GrEzT%FzB`5oSvQC9rhx@!bsBSEp9_iK2B?LGUgdsX=%vud6Sp#;pSMIdNBy$%; zh^Yg!vM8!n19~~u-ue-1>nJgjzd(t${I0z9D?pk~f5VE+cFoCtlk&|eP3|1>adPDB z&x`qUe=#`dmoSOa)^&L>RYB1HNvWtr@-rWotaG;13HFvv9FZIJZ27It^RZQi90eB4 zHlz!?Al>NLvbQ%q>K4IZV{0pFP=xLzoyUziC)nA>)_@*51mTfP;Z843_CAI|W1J-$ zfLRsI1(dO|2hrJCs&sPpX;}eNfu7y-hPodVDq9ro(A&z*FcCo^#l3u$?N^QU6=c4K zP^*uu@P@eTP$$D^CQf4G6j*OxI!XMc9i$gV!fD*v@V?jncSK+^I2#?_s4 zzzB2&*!j#c3w3xj6W@a6X@Bd>{H1`lGjFSu`*NsM{?sU!5tN{-3P?b`Ajnmh<09dU zXIJGEK?pc_^E>fMKk$V>fQp}Hs@8U#w*rzIBi_5HE3 zN&ANI#nsg3R~&&X#Zjg47E-?M zp5mX&=79Nzmbou9P0gZ*LFjH1(xO#Gs%5%;Dus)FTd!8~RmmbY>8L zWdg^6Oal(j(is~5+9ByLBqIf=`LoZkZ4 zW&$-VR8`>>=X=tbmU)xz*^LrBQLj#JRDhASQ4;4UP-{bug2F7=q7S3 zGUTema{6CeSgeGe8$+Q5l16fyM#x}!@a1atJ=gKHdJ0a|mJ0mbH3zYapNGs>4qhEk zi(W;i<-=Zvi9F=Aocx!L(KiPuK2s#Mtj4T|9IfLh&f7~sQ zTpZ>ORc6R*?Ow_}*;JzNAI*Qu#us~7JXcm+>m0$-_Fi#d>Kt4uT&v({ zJF{o3Ek(e$=>K6aK}K5zo6Z9wX0ph$#w#gupQ% zJAu)3r{$hLgA7O|H}9sT+<~LW*RQTME-}1Ab~vl73(vseTb|>e$x5@Y&Me~NB9;&f zgOyD2sf+i&9i!Y;95*NU|IwY%-~H!ft!9bVXDE5Y?IMzd)N)vC z*gFR6^w1tB4V`fw=(sQhih-6mgC9hiFgE@;a32BbGu$7#Cl)mV>9bI={+U}0{A5}A z&9E+3eX3>-wpz6}f2xs&t`RG&?MVJeI%Jb8fmR z%0ZN;IwnRaSz4}&etY)MaNjj4m9H!^aRi5WK!a1V(m1_|P+afnWvbsucoGTCwj}kg z3i4ron;x8QbkRFecA*A!)Hekak$okL()l#0(rzIW$Te#YQ0Q+S(Q zVVBK^N~5h4HX8?n39a8w7vCwYf4uq*`n+QJ;=Xw0gp2BropGZV@oO=Vy=@l*=M94D z7RP^LmA<6hN|xVBtCUxwT>G&1rcZB7AJLW+YGD_#%T?`7Wz5qt2?&eq{k(G0&dcHq zZE52ERvw#|IB<!ya{<%Z@ELju(CmQBo%!n8C^e_SBry26UqOW{q?*uCWxpgz!z(SGL@i5f8|cVYYhDHc1YI|T}= z+`QA!ZLc7RM`K~2S+&3Y(|`(KPz1kjPy0@duX|%CHdmy$4mE(9f8|ioJ)lf?DCKHCS-Xoo!)b2aeNXvkKkOrYpVfklw||Aj%t zG&BDj2FVN8(w@#z9t5qBl@+u8{z`p~!G;#ld8c&5>`tXiGnvc2?AzQa_?kSZP0=P{ z*7>Sheyk!U6Te7-)Qk})=U}WzPPts6DlJ2|sUh&{*aYMWTmhW{OY;j}$kzGc0EhS- zXw1`KrzK|r>CdKf(p1nlX z&AVbaqKJfLRF_R|jt==uKUTD^v9ZywIqIQk z535TxbN$)R%4pdBm9$IqAoS=ln=s+3i`045yT+e3&AjaovbuP$&AeD~yT9UGLEmX> zA33h>Y%_#33oZ@f9jN$0nrQ*NRSz3J`qv^o#tBF#BnH{1u$qN&EK2x>dtoxB_z53| znPP8e<0-!`eu$dGneR9Fw!^ZCp~l@#fH|UUs9qmxj;+ER+~>59St3}qr-rlJ_3q@x zI+hV>-9&o;XPKk?E%tvV>(B7W} z-&$b($1Cdmax7$z5_80#Q13#CVKDV>{y+TdB#!pH+EGNRl$D+fCRIrn!tT(f19UTpFUHtU{ z^vcJabgu`wu+U#7z}&Hu6j*KVOlOB_H=}bq7N-PCNNY8 z!vsYnJV>+m95n<$>Kp4KqCvzUt=3a~_35F;Opylw*SLr@&fV7j=2=MKR2bqsu>2GQ zqkP?Vf-AiVdupJ{rV>r69?}h7|N0w93zsUN5@xodbbCW$2ugsy}mVJ;$M)c$ zv$c!#ti52g1+DpNoeL(DO2zB4|Fc2GK-!vy1Nnyh9WG=jaI4=pqI|@t-LLfOZ}SPm z&n8t!vp3uCEs~Rs@4|Hf4udk;C?E{>svL>%{T39o)}+2T9Ugquzy(cX;wfa-FSc8suBl;;(Fnxv>j_oodkt#CpT*&N+XerE`?CZRL0uL0#xOm+38kk1B zs7)Q~;!|HKTDJr+?-xH86@!9r3z;tPz4;7bO9^aY27jL9tu>P1 zmIM=E*Ew~t%Vd1`r4}ErDewj>c9nr<30=6-6*s}4J6lGdg)I&)h6T}G+%|msUb0N7 zEMwsi+&$hAKU?wbFrlrvpAXnG_w`-$e*}v5pVVZAL;uYv2u;F38a0+odN<4P~EeQpWlv|mPN2i zjS*nd7~iDrGvlH=C4<$-wM*;FtSqPDFY~*NY2fEH@6W&Rdi5|0Z1tymr>~u7s{9#-vC~?HuC2b~e+vO{Doc&R=H~n-r^=5G zi(j0bm8#jY<7xvD2J0Z4wyrL`$!Qz`+mc}XTo?zOvZKATH~2-$=F)&sY03sm@OA-Y zJFn7oG!OoSWAg&!ZZ#X#a{Q$@isKXr=w;tuSd5W1AB{ADsbmJ?1Sha#Sqt>(Aa+{!-;r2KdsT`T+aeESi`1g_|2pM{zlBi~JTqWP!Hc-sw zfu#alm*(9M{Q%RiTHc3;qUB{Xlj`JWWCP(m+Th>PSGym|ua8zv5ZUIef`ZWIrcNB3 zfD8j?CcqxVmm4tQI8%GA3Q~I{Za=Bd)%_e=?W8vj9PP?$yb?rIJ8tB+wJkk`rtUwh zG}n)eyCsMWp-)2@UGNn?CM(-~w0XiNIXd8XvA6fhpLj8A95WV0dn~Xczjj$PVNhM* ziZ+cdElYY=K9~D&cU#R4R;n}8`cM-;CR3UlP@3f+y*ysi?adR*?NPNmkvJAueS{?< z*eL*hRSzY}sswI!n&3P<S*}`d)Rq!REXt@@2aCk@;t56M` z66>`k-Z@MXq^!EUY1Jso!Ey}%q54=nti}~%q_H%giw7n6W<$Z>u}C`uV;)zD+0>_& z+nmep*|s?CN@CmF-j6Lr>{$?c^RaOrKh$q`onR0qjN97E$m3V^RYD}wPw{9y^@?h> zu#wL$NiLqpbth=sZ9P~!p}Zd}YE=Ua-0i8v@Vo9Wy!#^S^V|J;dt0}-#E?qQhcNUo zxDgBAlSDV_lbvbFW5J5XaF%2*P$08ewBo4|)(&1eon@W>k4#xX7%nKian<@CUR7n} zhQ13_dI`HG-M?*D*f}e3>L&K7LpjjS=^;7uoJx?`!;@Ltw&O1YA>(QE&jBJU^bfSW z-l|W>!{gPrpN1;29(6i@!{aG?BQVKN`MJey;SK;H#A@POJe`}_!$WCi0|*$sat0NA+lv-{>{GfP5`|D1Tou86Nb9xj)IE5 zrRUHdGU6Y(SrBj^Ch2WV8FV;es;YC$E_3sC^$KBN!ItNk&dpZ~X69*~aRfWg1zwqjIU?C4M3 zmRypdo{uT05fCk@#AATubSm0)G3)*5x9v_Pg|qL!r*DNW8b<+~X79p`|SK{zAOGwv&*|zdcGL1 z^qqZ1Wf=%pJt^g?t9>kKL#5a(4C<*XL)6a0t>buyE2VGoY08*fONFtqv!ZPZwKYq< zyH0w9IiH96!kKbTlCf6{(Ag8`=IoqprW*u-=d-pPXHo2g^|Xv;LKOOO*uU3N!7c}F zIk&#_cA#J#-=nJ=yc(mfy@PIvG_3@+Fa59cegoX89mFyej2OW8M?SapBx*!>xG7^>+mL z=k%v4MHF`0Gm&ESDAykeXvxp0mPoQHj<3_2r6@i}=ri28xA3(W+ofs&(Fdid`6gmg z41esN8NoyBcQ{#t;_w$7J^K;98GAJ*jiynXK^@P*g zaR(!F@3;e;Qem)-rKrQ>v$2N7z?^V>epf`2I(NHF@4|xL-WA4`41ek0@)^{G0`8=dB-jgniYJ=F3iSbCZxMy38+U!$Ze)AGagHGXCFExg9r+q>1=y%*L_CiQ9M;h9Y`3NzVtB@yV4B_FAo}G9FAfDPWG%? zx;&gYtCZ1~H=nG}GpFkG@PGZQv1jjrDsMysUf*Ibs|Htvi`p;`)7ea4>L^l~g+_rf zoBA0_M#FF4$-jpj+@67Gyv(r_D-7AyEVi_U8D9B`!yiKx#eaT1oszfgD}d3)VKx>q z8+B^+r@CBdN^iv?Ak|QUBOjQCL)9DiC!e6cBLck@j_{jud^_EAWmWF7TiS=yH*JIW z=jUsu)z7lNzC~3qAMW$aE$&pl>8;dr#vI<`K5N#UvqDE~3R98iIXf8ZiW6FV<8lw) zSVzdN-(im)18G7=^hyVWp;evU52SnAyKsUZ{~($IFRmE9O< zj%od`{q{-txfu>QDV@-~8cjPt%af`q9~4m%Ap5ht^b}%xm-Dr&RTfCV2$eJ-lD@UU z^~*l^LZn){x1ybk^vODrB_^-EGgT;mYt;u*9B78g;pfYPwC@-|%77z6~2j{KQU z6R%eYj7%wY@Dmx3ns=28hnA`J+{V+pU0he(d)V#h{~E+Z=7&bapk+ z{0EUC0}Llm*K(H!FXc{dQBPrEAUX-ciswm@TuL~MIhD4ts>jd{17BzU61jfM3A|uh zxDEq);IHNhZ{*ZiFw+erZfvu*L&bK_Tx*=SV1=1+Q1qWo5Jkl!{0=6My+{mTzJOcpr%lYbaQ zvg&9P95;k`y}Hm?lS2NSQuL1uwy}>60M5d&OwDDk$!UFTtyiWW-UmLLc2QNTNIhRU zxjreaA+j8Z@HLs>RF9uh+T^L?#Z>6hBH!n2&qyv@v#&IYh8fmGl&zkP5tum!J)mLB zlm}VME#9MQuDDdfU%A>s`)OnwM6!<$w5dJMc13U0ggD^db>UV!2KT`?}0qf1Kc|DENUq;(9&%0;1cVHJ5Bx5#(&OYfON e*}q)gRr(ob&f*H#gFwU}kgCdS<%*|fq5lgyNkruU diff --git a/tools/mapmerge/README.md b/tools/mapmerge/README.md index d8d696d8939..3a9d0cb8cf5 100644 --- a/tools/mapmerge/README.md +++ b/tools/mapmerge/README.md @@ -35,3 +35,34 @@ Your map is now ready to be committed, rejoice and wait for conflicts. Open the map in dreameditor before committing the results of the mapmerger - this can cause dreameditor to resave the map back to dmm, if you're having issues with your map getting stuck in dmm mode, try comitting and pushing the mapmerger changes before reopening in dreameditor. + + +#Map Conflict Fixer/Helper# +The map conflict fixer is a script that can help you fix map conflicts easier and faster. Here's how it works: + +###Before using### +You need git for this, of course. +Make sure your development branch is up to date before starting a map edit to ensure the script outputs a correct fix. + +##Dictionary mode## +Dictionary conflicts are the easiest to fix, you simply need to create more models to accommodate your changes and everyone elses. + +When you run in this mode, if the script finishes successfuly the map should be ready to be commited. + +If the script fails in dictionary mode, you can run it again in full fix mode. + +##Full Fix mode## +When you and someone else edit the same coordinate, there is no easy way to fix the conflict. You need to get your hands dirty. + +The script will mark every tile with a marker type to help you identify what needs fixing in the map editor. + +After you edit and fix a marked map, you should run it through the map merger. The .backup file should be the same you used before. + +###Priorities### +In Full Fix mode, the script needs to know which map version has higher priority, yours or someone elses. This important so tiles with multiple area and turf types aren't created. + +Your version has priority - In each conflicted coordinate, your floor type and your area type will be used +Their version has priority - In each conflicted coordinate, your floor type and your area type will not be used + +##IMPORTANT## +This script is in a testing phase and you should not consider any output to be safe. Always verify the maps this script produced to make sure nothing is out of place. \ No newline at end of file diff --git a/tools/mapmerge/Run Map Conflict Fixer.bat b/tools/mapmerge/Run Map Conflict Fixer.bat new file mode 100644 index 00000000000..34eac701bbc --- /dev/null +++ b/tools/mapmerge/Run Map Conflict Fixer.bat @@ -0,0 +1,4 @@ +@echo off +SET RELATIVEROOT="../../" +python map_conflict_fixer.py %1 %RELATIVEROOT% +pause \ No newline at end of file diff --git a/tools/mapmerge/map_conflict_fixer.py b/tools/mapmerge/map_conflict_fixer.py new file mode 100644 index 00000000000..c3dc378bf1c --- /dev/null +++ b/tools/mapmerge/map_conflict_fixer.py @@ -0,0 +1,103 @@ +import map_helpers +import sys +import os +import time + +def main(relative_root): + git_version = map_helpers.run_shell_command("git version") + if not git_version: + print("ERROR: Failed to run git. Make sure it is installed and in your PATH.") + return False + + print("--- DISCLAIMER ---") + print("This script is in a testing phase. Verify all the results yourself to make sure you got what you expected. Make sure to read the readme to learn how to use this.") + input("Press Enter to GO\n") + + file_conflicts = map_helpers.run_shell_command("git diff --name-only --diff-filter=U").split("\n") + map_conflicts = [path for path in file_conflicts if path[len(path)-3::] == "dmm"] + + for i in range(0, len(map_conflicts)): + print("[{}]: {}".format(i, map_conflicts[i])) + selection = input("Choose maps you want to fix (example: 1,3-5,12):\n") + selection = selection.replace(" ", "") + selection = selection.split(",") + + #shamelessly copied from mapmerger cli + valid_indices = list() + for m in selection: + index_range = m.split("-") + if len(index_range) == 1: + index = map_helpers.string_to_num(index_range[0]) + if index >= 0 and index < len(map_conflicts): + valid_indices.append(index) + elif len(index_range) == 2: + index0 = map_helpers.string_to_num(index_range[0]) + index1 = map_helpers.string_to_num(index_range[1]) + if index0 >= 0 and index0 <= index1 and index1 < len(map_conflicts): + valid_indices.extend(range(index0, index1 + 1)) + + if not len(valid_indices): + print("No map selected, exiting.") + sys.exit() + + print("Attempting to fix the following maps:") + for i in valid_indices: + print(map_conflicts[i]) + + + marker = None + priority = 0 + print("\nFixing modes:") + print("[{}]: Dictionary conflict fixing mode".format(map_helpers.MAP_FIX_DICTIONARY)) + print("[{}]: Full map conflict fixing mode".format(map_helpers.MAP_FIX_FULL)) + mode = map_helpers.string_to_num(input("Select fixing mode [Dictionary]: ")) + if mode != map_helpers.MAP_FIX_FULL: + mode = map_helpers.MAP_FIX_DICTIONARY + print("DICTIONARY mode selected.") + else: + marker = input("FULL mode selected. Input a marker [/obj/effect/debugging/marker]: ") + if not marker: + marker = "/obj/effect/debugging/marker" + print("Marker selected: {}".format(marker)) + + print("\nVersion priorities:") + print("[{}]: Your version".format(map_helpers.MAP_FIX_PRIORITY_OURS)) + print("[{}]: Their version".format(map_helpers.MAP_FIX_PRIORITY_THEIRS)) + priority = map_helpers.string_to_num(input("Select priority [Yours]: ")) + if priority != map_helpers.MAP_FIX_PRIORITY_THEIRS: + priority = map_helpers.MAP_FIX_PRIORITY_OURS + print("Your version will be prioritized.") + else: + print("Their version will be prioritized.") + + ed = "FIXED" if mode == map_helpers.MAP_FIX_DICTIONARY else "MARKED" + ing = "FIXING" if mode == map_helpers.MAP_FIX_DICTIONARY else "MARKING" + + print("\nMaps will be converted to TGM.") + print("Writing maps to 'file_path/file_name.fixed.dmm'. Please verify the results before commiting.") + if mode == map_helpers.MAP_FIX_FULL: + print("After editing the marked maps, run them through the map merger!") + input("Press Enter to start.") + + print(".") + time.sleep(0.3) + print(".") + + for i in valid_indices: + path = map_conflicts[i] + print("{}: {}".format(ing, path)) + ours_map_raw_text = map_helpers.run_shell_command("git show HEAD:{}".format(path)) + theirs_map_raw_text = map_helpers.run_shell_command("git show MERGE_HEAD:{}".format(path)) + + common_ancestor_hash = map_helpers.run_shell_command("git merge-base HEAD MERGE_HEAD").strip() + base_map_raw_text = map_helpers.run_shell_command("git show {}:{}".format(common_ancestor_hash, path)) + + ours_map = map_helpers.parse_map(ours_map_raw_text) + theirs_map = map_helpers.parse_map(theirs_map_raw_text) + base_map = map_helpers.parse_map(base_map_raw_text) + + if map_helpers.fix_map_git_conflicts(base_map, ours_map, theirs_map, mode, marker, priority, relative_root+path): + print("{}: {}".format(ed, path)) + print(".") + +main(sys.argv[1]) diff --git a/tools/mapmerge/map_helpers.py b/tools/mapmerge/map_helpers.py index 5c399a12e5e..cb4849e9825 100644 --- a/tools/mapmerge/map_helpers.py +++ b/tools/mapmerge/map_helpers.py @@ -1,4 +1,7 @@ import sys +import subprocess + +tgm_header = "//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE" try: version = sys.version_info @@ -11,148 +14,110 @@ except: import collections -error = {0:"OK", 1:"WARNING: Key lengths are different, all the lines change."} - -maxx = 0 -maxy = 0 -key_length = 1 - -def reset_globals(): - global key_length - global maxx - global maxy - key_length = 1 - maxx = 0 - maxy = 0 +error = {0:"OK", 1:"WARNING: Detected key length difference, not merging.", 2:"WARNING: Detected map size difference, not merging."} def merge_map(newfile, backupfile, tgm): - reset_globals() + key_length = 1 + maxx = 1 + maxy = 1 - shitmap = parse_map(newfile) - originalmap = parse_map(backupfile) + new_map = parse_map(get_map_raw_text(newfile)) + old_map = parse_map(get_map_raw_text(backupfile)) - global key_length - if shitmap["key_length"] != originalmap["key_length"]: + if new_map["key_length"] != old_map["key_length"]: if tgm: - write_dictionary_tgm(newfile, shitmap["dictionary"]) - write_grid_coord_small(newfile, shitmap["grid"]) + write_dictionary_tgm(newfile, new_map["dictionary"]) + write_grid_coord_small(newfile, new_map["grid"]) return 1 else: - key_length = originalmap["key_length"] + key_length = old_map["key_length"] - shitDict = shitmap["dictionary"] #key to tile data dictionary - shitGrid = shitmap["grid"] #x,y coords to tiles (keys) dictionary (the map's layout) - - originalDict = sort_dictionary(originalmap["dictionary"]) - originalGrid = originalmap["grid"] - - mergeGrid = dict() #final map layout - known_keys = dict() #mapping known keys to original keys - tempGrid = dict() #saving tiles with newly generated keys for later processing - temp_keys = dict() #mapping known keys to newly generated keys - unused_keys = list(originalDict.keys()) #list with all existing keys that aren't being used - tempDict = collections.OrderedDict() #mapping new keys to new data - originalDict_size = len(originalDict) - - for y in range(1,maxy+1): - for x in range(1,maxx+1): - shitKey = shitGrid[x,y] - - #if this key was seen before, add it to the pile immediately - if shitKey in known_keys: - mergeGrid[x,y] = known_keys[shitKey] - continue - #if this key was seen before, add it to the pile immediately - if shitKey in temp_keys: - tempGrid[x,y] = temp_keys[shitKey] - continue - - shitData = shitDict[shitKey] - originalKey = originalGrid[x,y] - originalData = originalDict[originalKey] - - #if new tile data at x,y is the same as original tile data at x,y, add to the pile - if shitData == originalData: - mergeGrid[x,y] = originalKey - known_keys[shitKey] = originalKey - unused_keys.remove(originalKey) - else: - #search for the new tile data in the original dictionary, if a key is found add it to the pile, else generate a new key - newKey = search_key(originalDict, shitData) - if newKey != None: - try: - unused_keys.remove(newKey) - except ValueError: #caused by a duplicate entry - print("NOTICE: Correcting duplicate dictionary entry. ({})".format(shitKey)) - mergeGrid[x,y] = newKey - known_keys[shitKey] = newKey - #if data at original x,y no longer exists we reuse the key immediately - elif search_key(shitDict, originalData) == None: - mergeGrid[x,y] = originalKey - originalDict[originalKey] = shitData - unused_keys.remove(originalKey) - known_keys[shitKey] = originalKey - else: - if len(tempDict) == 0: - newKey = generate_new_key(originalDict) - else: - newKey = generate_new_key(tempDict) - tempGrid[x,y] = newKey - temp_keys[shitKey] = newKey - tempDict[newKey] = shitData - - sort = 0 - #find gaps in the dictionary keys sequence and add the missing keys to be recycled - dict_list = list(originalDict.keys()) - for index in range(0, len(dict_list)): - if index + 1 == len(dict_list): - break - - key = dict_list[index] - next_key = dict_list[index+1] - - difference = key_difference(key, next_key) - if difference > 1: - i = 1 - nextnew = key - while i < difference: - nextnew = get_next_key(nextnew) - unused_keys.append(nextnew) - i += 1 - sort = 1 - - #Recycle outdated keys with any new tile data, starting from the bottom of the dictionary - i = 0 - for key, value in reversed(tempDict.items()): - recycled_key = key - if len(unused_keys) > 0: - recycled_key = unused_keys.pop() - - for coord, gridkey in tempGrid.items(): - if gridkey == None: - continue - if gridkey == key: - mergeGrid[coord] = recycled_key - tempGrid[coord] = None - - originalDict[recycled_key] = value - - #if gaps in the key sequence were found, sort the dictionary for cleanliness - if sort == 1: - originalDict = sort_dictionary(originalDict) - - if tgm: - write_dictionary_tgm(newfile, originalDict) - write_grid_coord_small(newfile, mergeGrid) + if new_map["maxx"] != old_map["maxx"] or new_map["maxy"] != old_map["maxy"]: + if tgm: + write_dictionary_tgm(newfile, new_map["dictionary"]) + write_grid_coord_small(newfile, new_map["grid"]) + return 2 else: - write_dictionary(newfile, originalDict) - write_grid(newfile, mergeGrid) + maxx = old_map["maxx"] + maxy = old_map["maxy"] + + new_dict = new_map["dictionary"] + new_grid = new_map["grid"] + old_dict = sort_dictionary(old_map["dictionary"]) #impose order; old_dict is used in the end as the merged dictionary + old_grid = old_map["grid"] + + merged_grid = dict() + known_keys = dict() + unused_keys = list(old_dict.keys()) + + #both new and old dictionary in lists, for faster key lookup by tile tuple + old_dict_keys = list(old_dict.keys()) + old_dict_values = list(old_dict.values()) + new_dict_keys = list(new_dict.keys()) + new_dict_values = list(new_dict.values()) + + #step one: parse the new version, compare it to the old version, merge both + for y in range(1, maxy+1): + for x in range(1, maxx+1): + + new_key = new_grid[x,y] + #if this key has been processed before, it can immediately be merged + if new_key in known_keys: + merged_grid[x,y] = known_keys[new_key] + continue + + old_key = old_grid[x,y] + old_tile = old_dict[old_key] + new_tile = new_dict[new_key] + + if new_tile == old_tile: #this tile is the exact same as before, so the old key is used + merged_grid[x,y] = old_key + known_keys[new_key] = old_key + unused_keys.remove(old_key) + continue + + #the tile is different here, but if it exists in the old dictionary, its old key can be used + newold_key = get_key(old_dict_keys, old_dict_values, new_tile) + if newold_key != None: + merged_grid[x,y] = newold_key + known_keys[new_key] = newold_key + try: + unused_keys.remove(newold_key) + except ValueError: + print("NOTICE: Correcting duplicate dictionary entry. ({})".format(new_key)) + + #the tile is brand new and it needs a new key, but if the old key isn't being used any longer it can be used instead + elif get_key(new_dict_keys, new_dict_values, old_tile) == None: + merged_grid[x,y] = old_key + old_dict[old_key] = new_tile + known_keys[new_key] = old_key + unused_keys.remove(old_key) + + #all other options ruled out, a brand new key is generated for the brand new tile + else: + fresh_key = generate_new_key(old_dict) + old_dict[fresh_key] = new_tile + merged_grid[x,y] = fresh_key + + #step two: clean the dictionary if it has too many unused keys + if len(unused_keys) > min(100, (len(old_dict) * 0.5)): + print("NOTICE: Trimming the dictionary.") + old_dict = trim_dictionary(old_dict) + + #step three: write the map to file + if tgm: + write_dictionary_tgm(newfile, old_dict) + write_grid_coord_small(newfile, merged_grid, maxx, maxy) + else: + write_dictionary(newfile, old_dict) + write_grid(newfile, merged_grid, maxx, maxy) return 0 -#write dictionary in tgm format -def write_dictionary_tgm(filename, dictionary): +####################### +#write to file helpers# +def write_dictionary_tgm(filename, dictionary): #write dictionary in tgm format with open(filename, "w") as output: - output.write("//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE \n") + output.write("{}\n".format(tgm_header)) for key, list_ in dictionary.items(): output.write("\"{}\" = (\n".format(key)) @@ -194,8 +159,8 @@ def write_dictionary_tgm(filename, dictionary): output.write(")\n") -#thanks to YotaXP for finding out about this one -def write_grid_coord_small(filename, grid): + +def write_grid_coord_small(filename, grid, maxx, maxy): #thanks to YotaXP for finding out about this one with open(filename, "a") as output: output.write("\n") @@ -205,17 +170,290 @@ def write_grid_coord_small(filename, grid): output.write("{}\n".format(grid[x,y])) output.write("{}\n\"}}\n".format(grid[x,maxy])) + +def write_dictionary(filename, dictionary): #writes a tile dictionary the same way Dreammaker does + with open(filename, "w") as output: + for key, value in dictionary.items(): + output.write("\"{}\" = ({})\n".format(key, ",".join(value))) + + +def write_grid(filename, grid, maxx, maxy): #writes a map grid the same way Dreammaker does + with open(filename, "a") as output: + output.write("\n") + output.write("(1,1,1) = {\"\n") + + for y in range(1, maxy+1): + for x in range(1, maxx+1): + try: + output.write(grid[x,y]) + except KeyError: + print("Key error: ({},{})".format(x,y)) + output.write("\n") + output.write("\"}") + output.write("\n") + +#################### +#dictionary helpers# def search_key(dictionary, data): for key, value in dictionary.items(): if value == data: return key return None +def get_key(keys, values, data): + try: + return keys[values.index(data)] + except: + return None + +def trim_dictionary(dictionary): #rewrites dictionary into an ordered dictionary with no unused keys + trimmed_dict = collections.OrderedDict() + key_length = len(list(dictionary.keys())[0]) + key = "" + for tile in dictionary.values(): + key = get_next_key(key, key_length) + trimmed_dict[key] = tile + return trimmed_dict + +def sort_dictionary(dictionary): + sorted_dict = collections.OrderedDict() + keys = list(dictionary.keys()) + keys.sort(key=key_int_value) + for key in keys: + sorted_dict[key] = dictionary[key] + return sorted_dict + +############ +#map parser# +def get_map_raw_text(mapfile): + with open(mapfile, "r") as reading: + return reading.read() + +def parse_map(map_raw_text): #still does not support more than one z level per file, but should parse any format + in_quote_block = False + in_key_block = False + in_data_block = False + in_varedit_block = False + after_data_block = False + escaping = False + skip_whitespace = False + + dictionary = collections.OrderedDict() + curr_key = "" + curr_datum = "" + curr_data = list() + + in_map_block = False + in_coord_block = False + in_map_string = False + iter_x = 0 + adjust_y = True + + curr_num = "" + reading_coord = "x" + + key_length = 0 + + maxx = 0 + maxy = 0 + + curr_x = 0 + curr_y = 0 + curr_z = 1 + grid = dict() + + for char in map_raw_text: + + if not in_map_block: + + if char == "\n" or char == "\t": + continue + + if in_data_block: + + if in_varedit_block: + + if in_quote_block: + if char == "\\": + curr_datum = curr_datum + char + escaping = True + continue + + if escaping: + curr_datum = curr_datum + char + escaping = False + continue + + if char == "\"": + curr_datum = curr_datum + char + in_quote_block = False + continue + + curr_datum = curr_datum + char + continue + + if skip_whitespace and char == " ": + skip_whitespace = False + continue + skip_whitespace = False + + if char == "\"": + curr_datum = curr_datum + char + in_quote_block = True + continue + + if char == ";": + skip_whitespace = True + curr_datum = curr_datum + char + continue + + if char == "}": + curr_datum = curr_datum + char + in_varedit_block = False + continue + + curr_datum = curr_datum + char + continue + + if char == "{": + curr_datum = curr_datum + char + in_varedit_block = True + continue + + if char == ",": + curr_data.append(curr_datum) + curr_datum = "" + continue + + if char == ")": + curr_data.append(curr_datum) + dictionary[curr_key] = tuple(curr_data) + curr_data = list() + curr_datum = "" + curr_key = "" + in_data_block = False + after_data_block = True + continue + + curr_datum = curr_datum + char + continue + + if in_key_block: + if char == "\"": + in_key_block = False + key_length = len(curr_key) + else: + curr_key = curr_key + char + continue + #else we're looking for a key block, a data block or the map block + + if char == "\"": + in_key_block = True + after_data_block = False + continue + + if char == "(": + if after_data_block: + in_map_block = True + in_coord_block = True + after_data_block = False + curr_key = "" + continue + else: + in_data_block = True + after_data_block = False + continue + + else: + + if in_coord_block: + if char == ",": + if reading_coord == "x": + curr_x = string_to_num(curr_num) + if curr_x > maxx: + maxx = curr_x + iter_x = 0 + curr_num = "" + reading_coord = "y" + elif reading_coord == "y": + curr_y = string_to_num(curr_num) + if curr_y > maxy: + maxy = curr_y + curr_num = "" + reading_coord = "z" + else: + pass + continue + + if char == ")": + in_coord_block = False + reading_coord = "x" + curr_num = "" + #read z here if needed + continue + + curr_num = curr_num + char + continue + + if in_map_string: + + if char == "\"": + in_map_string = False + adjust_y = True + curr_y -= 1 + continue + + if char == "\n": + if adjust_y: + adjust_y = False + else: + curr_y += 1 + if curr_x > maxx: + maxx = curr_x + if iter_x > 1: + curr_x = 1 + iter_x = 0 + continue + + + curr_key = curr_key + char + if len(curr_key) == key_length: + iter_x += 1 + if iter_x > 1: + curr_x += 1 + + grid[curr_x, curr_y] = curr_key + curr_key = "" + continue + + + #else look for coordinate block or a map string + + if char == "(": + in_coord_block = True + continue + if char == "\"": + in_map_string = True + continue + + if curr_y > maxy: + maxy = curr_y + + data = dict() + data["dictionary"] = dictionary + data["grid"] = grid + data["key_length"] = key_length + data["maxx"] = maxx + data["maxy"] = maxy + return data + +############# +#key helpers# def generate_new_key(dictionary): last_key = next(reversed(dictionary)) - return get_next_key(last_key) + return get_next_key(last_key, len(last_key)) -def get_next_key(key): +def get_next_key(key, key_length): if key == "": return "".join("a" for _ in range(key_length)) @@ -240,287 +478,13 @@ def get_next_key(key): carry -= 1 return new_key[::-1] -def sort_dictionary(dictionary): - sorted_dict = collections.OrderedDict() - next_key = get_next_key("") - while len(sorted_dict) < len(dictionary): - try: - sorted_dict[next_key] = dictionary[next_key] - except KeyError: - pass - next_key = get_next_key(next_key) - return sorted_dict - -#still does not support more than one z level per file, but should parse any format -def parse_map(map_file): - with open(map_file, "r") as map_input: - characters = map_input.read() - - in_quote_block = False - in_key_block = False - in_data_block = False - in_varedit_block = False - after_data_block = False - escaping = False - skip_whitespace = False - - dictionary = collections.OrderedDict() - curr_key = "" - curr_datum = "" - curr_data = list() - - in_map_block = False - in_coord_block = False - in_map_string = False - iter_x = 0 - adjust_y = True - - curr_num = "" - reading_coord = "x" - - - global maxx - global maxy - key_length_local = 0 - curr_x = 0 - curr_y = 0 - curr_z = 1 - grid = dict() - - for char in characters: - - if not in_map_block: - - if char == "\n" or char == "\t": - continue - - if in_data_block: - - if in_varedit_block: - - if in_quote_block: - if char == "\\": - curr_datum = curr_datum + char - escaping = True - continue - - if escaping: - curr_datum = curr_datum + char - escaping = False - continue - - if char == "\"": - curr_datum = curr_datum + char - in_quote_block = False - continue - - curr_datum = curr_datum + char - continue - - if skip_whitespace and char == " ": - skip_whitespace = False - continue - skip_whitespace = False - - if char == "\"": - curr_datum = curr_datum + char - in_quote_block = True - continue - - if char == ";": - skip_whitespace = True - curr_datum = curr_datum + char - continue - - if char == "}": - curr_datum = curr_datum + char - in_varedit_block = False - continue - - curr_datum = curr_datum + char - continue - - if char == "{": - curr_datum = curr_datum + char - in_varedit_block = True - continue - - if char == ",": - curr_data.append(curr_datum) - curr_datum = "" - continue - - if char == ")": - curr_data.append(curr_datum) - dictionary[curr_key] = tuple(curr_data) - curr_data = list() - curr_datum = "" - curr_key = "" - in_data_block = False - after_data_block = True - continue - - curr_datum = curr_datum + char - continue - - if in_key_block: - if char == "\"": - in_key_block = False - key_length_local = len(curr_key) - else: - curr_key = curr_key + char - continue - #else we're looking for a key block, a data block or the map block - - if char == "\"": - in_key_block = True - after_data_block = False - continue - - if char == "(": - if after_data_block: - in_map_block = True - in_coord_block = True - after_data_block = False - curr_key = "" - continue - else: - in_data_block = True - after_data_block = False - continue - - else: - - if in_coord_block: - if char == ",": - if reading_coord == "x": - curr_x = string_to_num(curr_num) - if curr_x > maxx: - maxx = curr_x - iter_x = 0 - curr_num = "" - reading_coord = "y" - elif reading_coord == "y": - curr_y = string_to_num(curr_num) - if curr_y > maxy: - maxy = curr_y - curr_num = "" - reading_coord = "z" - else: - pass - continue - - if char == ")": - in_coord_block = False - reading_coord = "x" - curr_num = "" - #read z here if needed - continue - - curr_num = curr_num + char - continue - - if in_map_string: - - if char == "\"": - in_map_string = False - adjust_y = True - curr_y -= 1 - continue - - if char == "\n": - if adjust_y: - adjust_y = False - else: - curr_y += 1 - if curr_x > maxx: - maxx = curr_x - if iter_x > 1: - curr_x = 1 - iter_x = 0 - continue - - - curr_key = curr_key + char - if len(curr_key) == key_length_local: - iter_x += 1 - if iter_x > 1: - curr_x += 1 - - grid[curr_x, curr_y] = curr_key - curr_key = "" - continue - - - #else look for coordinate block or a map string - - if char == "(": - in_coord_block = True - continue - if char == "\"": - in_map_string = True - continue - - if curr_y > maxy: - maxy = curr_y - - data = dict() - data["dictionary"] = dictionary - data["grid"] = grid - data["key_length"] = key_length_local - return data - -#subtract keyB from keyA -def key_difference(keyA, keyB): - if len(keyA) != len(keyB): - return "you fucked up" - - Ayek = keyA[::-1] - Byek = keyB[::-1] - - result = 0 - for i in range(0, len(keyA)): - base = 52**i - A = 26 if Ayek[i].isupper() else 0 - B = 26 if Byek[i].isupper() else 0 - result += ( (ord(Byek[i].lower()) + B) - (ord(Ayek[i].lower()) + A) ) * base - return result - -def string_to_num(s): - try: - return int(s) - except ValueError: - return -1 - -#writes a tile data dictionary the same way Dreammaker does -def write_dictionary(filename, dictionary): - with open(filename, "w") as output: - for key, value in dictionary.items(): - output.write("\"{}\" = ({})\n".format(key, ",".join(value))) - -#writes a map grid the same way Dreammaker does -def write_grid(filename, grid): - with open(filename, "a") as output: - output.write("\n") - output.write("(1,1,1) = {\"\n") - - for y in range(1, maxy+1): - for x in range(1, maxx+1): - try: - output.write(grid[x,y]) - except KeyError: - print("Key error: ({},{})".format(x,y)) - output.write("\n") - output.write("\"}") - output.write("\n") - -#inflated map grid; unused -def write_grid_coord(filename, grid): - with open(filename, "a") as output: - output.write("\n") - for y in range(1, maxy+1): - for x in range(1, maxx+1): - output.write("({},{},1) = {{\"{}\"}}\n".format(x, y, grid[x,y])) +def key_int_value(key): + value = 0 + b = 0 + for digit in reversed(key): + value += base52.index(digit) * (52 ** b) + b += 1 + return value def key_compare(keyA, keyB): #thanks byond for not respecting ascii pos = 0 @@ -548,3 +512,172 @@ def key_compare(keyA, keyB): #thanks byond for not respecting ascii return 1 break return 0 + + +def key_difference(keyA, keyB): #subtract keyB from keyA + if len(keyA) != len(keyB): + return "you fucked up" + + Ayek = keyA[::-1] + Byek = keyB[::-1] + + result = 0 + for i in range(0, len(keyA)): + base = 52**i + A = 26 if Ayek[i].isupper() else 0 + B = 26 if Byek[i].isupper() else 0 + result += ( (ord(Byek[i].lower()) + B) - (ord(Ayek[i].lower()) + A) ) * base + return result + +############# +#other stuff# + +#Base 52 a-z A-Z dictionary (it's a python list) for fast conversion +base52 = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', + 'p','q','r','s','t','u','v','w','x','y','z','A','B','C','D', + 'E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S', + 'T','U','V','W','X','Y','Z'] + +def string_to_num(s): + try: + return int(s) + except ValueError: + return -1 + +#################### +#map conflict fixer# + +MAP_FIX_DICTIONARY = 0 +MAP_FIX_FULL = 1 +MAP_FIX_PRIORITY_OURS = 0 +MAP_FIX_PRIORITY_THEIRS = 1 + +def fix_map_git_conflicts(base_map, ours_map, theirs_map, mode, marker, priority, path): #attempts to fix git map conflicts automagically + #mode values: + # 0 - Dictionary mode, only fixes dictionary conflicts and fails at coordinate conflicts + # 1 - Full mode, fixes dictionary conflicts and will join both versions of a coordinate and adds a marker to each conflicted coordinate + + if mode == MAP_FIX_FULL and not marker: + print("ERROR: Full fix mode selected but a marker wasn't provided.") + return False + + key_length = 1 + maxx = 1 + maxy = 1 + + if ours_map["key_length"] != theirs_map["key_length"] or ours_map["key_length"] != base_map["key_length"]: + print("ERROR: Key length is different, I am not smart enough for this yet.") + return False + else: + key_length = ours_map["key_length"] + + if ours_map["maxx"] != theirs_map["maxx"] or ours_map["maxy"] != theirs_map["maxy"] or ours_map["maxx"] != base_map["maxx"] or ours_map["maxy"] != base_map["maxy"]: + print("ERROR: Map size is different, I am not smart enough for this yet.") + return False + else: + maxx = ours_map["maxx"] + maxy = ours_map["maxy"] + + base_dict = base_map["dictionary"] + base_grid = base_map["grid"] + ours_dict = ours_map["dictionary"] + ours_grid = ours_map["grid"] + theirs_dict = theirs_map["dictionary"] + theirs_grid = theirs_map["grid"] + + merged_dict = collections.OrderedDict() + merged_grid = dict() + + new_key = generate_new_key(base_dict) + + grid_conflict_counter = 0 + + for y in range(1, maxy+1): + for x in range(1, maxx+1): + base_key = base_grid[x,y] + ours_key = ours_grid[x,y] + theirs_key = theirs_grid[x,y] + + #case 1: everything is the same + if base_dict[base_key] == ours_dict[ours_key] and base_dict[base_key] == theirs_dict[theirs_key]: + merged_dict[base_key] = base_dict[base_key] + merged_grid[x,y] = base_key + continue + + #case 2: ours is unchanged, theirs is different + if base_dict[base_key] == ours_dict[ours_key]: + some_key = search_key(base_dict, theirs_dict[theirs_key]) + if some_key != None: + merged_dict[some_key] = theirs_dict[theirs_key] + merged_grid[x,y] = some_key + else: + merged_dict[new_key] = theirs_dict[theirs_key] + merged_grid[x,y] = new_key + new_key = get_next_key(new_key, key_length) + + #case 3: ours is different, theirs is unchanged + elif base_dict[base_key] == theirs_dict[theirs_key]: + some_key = search_key(base_dict, ours_dict[ours_key]) + if some_key != None: + merged_dict[some_key] = ours_dict[ours_key] + merged_grid[x,y] = some_key + else: + merged_dict[new_key] = ours_dict[ours_key] + merged_grid[x,y] = new_key + new_key = get_next_key(new_key, key_length) + + #case 4: everything is different, grid conflict + else: + if mode != MAP_FIX_FULL: + print("FAILED: Map fixing failed due to grid conflicts. Try fixing in full fix mode.") + return False + else: + combined_tile = combine_tiles(ours_dict[ours_key], theirs_dict[theirs_key], priority, marker) + merged_dict[new_key] = combined_tile + merged_grid[x,y] = new_key + new_key = get_next_key(new_key, key_length) + grid_conflict_counter += 1 + + merged_dict = sort_dictionary(merged_dict) + write_dictionary_tgm(path+".fixed.dmm", merged_dict) + write_grid_coord_small(path+".fixed.dmm", merged_grid, maxx, maxy) + if grid_conflict_counter > 0: + print("Counted {} conflicts.".format(grid_conflict_counter)) + return True + +def combine_tiles(tile_A, tile_B, priority, marker): + new_tile = list() + turf_atom = None + area_atom = None + + low_tile = None + high_tile = None + if priority == MAP_FIX_PRIORITY_OURS: + high_tile = tile_A + low_tile = tile_B + else: + high_tile = tile_B + low_tile = tile_A + + new_tile.append(marker) + for atom in high_tile: + if atom[0:5] == "/turf": + turf_atom = atom + elif atom[0:5] == "/area": + area_atom = atom + else: + new_tile.append(atom) + + for atom in low_tile: + if atom[0:5] == "/turf" or atom[0:5] == "/area": + continue + else: + new_tile.append(atom) + + new_tile.append(turf_atom) + new_tile.append(area_atom) + return tuple(new_tile) + + +def run_shell_command(command): + return subprocess.run(command, shell=True, stdout=subprocess.PIPE, universal_newlines=True).stdout