Merge branch 'develop' into spectate

develop
Josh Cooper 2022-10-14 17:41:34 -07:00
commit 9b1541add5
46 changed files with 557 additions and 186 deletions

@ -0,0 +1,6 @@
#query label(query)
,,"{givename name=""foo dumper""}"
,,"{givename name=""foo""}"
Can't render this file because it has a wrong number of fields in line 5.

@ -0,0 +1,28 @@
#build label(construct) start(14;14)
,trackNS,trackE,,trackW,trackS,trackN,,,,,,,,,,,,,,trackN,trackS,trackE,,trackW,trackNS
trackEW,,trackSE,,trackSW,trackNE,trackNW,,,,,,,,,,,,,,trackNE,trackNW,trackSE,,trackSW,,trackEW
trackS,trackSE,,,trackNSE,trackNSW,trackEW,,,,,,,,,,,,,,trackEW,trackNSE,trackNSW,,,trackSW,trackS
trackN,trackNE,trackSEW,,,trackSEW,trackNEW,,,,,,,,,,,,,,trackNEW,trackSEW,,,trackSEW,trackNW,trackN
trackE,trackSW,trackNEW,,trackNSE,,trackNSEW,,,,,,,,,,,,,,trackNSEW,,trackNSW,,trackNEW,trackSE,trackW
trackW,trackNW,trackNS,,trackNSW,trackNSEW,,,,,,,,,,,,,,,,trackNSEW,trackNSE,,trackNS,trackNE,trackE
,,,,,,,,,,trackrampNW,trackrampNS,trackrampN,,trackrampN,trackrampNS,trackrampNE
,,,,,,,,,trackrampNW,,trackrampNSE,trackrampNSW,,trackrampNSE,trackrampNSW,,trackrampNE
,,,,,,,,,trackrampEW,trackrampSEW,,trackrampNSEW,,trackrampNSEW,,trackrampSEW,trackrampEW
,,,,,,,,,trackrampW,trackrampNEW,trackrampNSEW,,,,trackrampNSEW,trackrampNEW,trackrampE
,,,,,,,,,trackrampW,trackrampSEW,trackrampNSEW,,,,trackrampNSEW,trackrampSEW,trackrampE
,,,,,,,,,trackrampEW,trackrampNEW,,trackrampNSEW,,trackrampNSEW,,trackrampNEW,trackrampEW
,,,,,,,,,trackrampSW,,trackrampNSE,trackrampNSW,,trackrampNSE,trackrampNSW,,trackrampSE
,,,,,,,,,,trackrampSW,trackrampNS,trackrampS,,trackrampS,trackrampNS,trackrampSE
trackW,trackSW,trackNS,,trackNSW,trackNSEW,,,,,,,,,,,,,,,,trackNSEW,trackNSE,,trackNS,trackSE,trackE
trackE,trackNW,trackSEW,,trackNSE,,trackNSEW,,,,,,,,,,,,,,trackNSEW,,trackNSW,,trackSEW,trackNE,trackW
trackS,trackSE,trackNEW,,,trackNEW,trackSEW,,,,,,,,,,,,,,trackSEW,trackNEW,,,trackNEW,trackSW,trackS
trackN,trackNE,,,trackNSE,trackNSW,trackEW,,,,,,,,,,,,,,trackEW,trackNSE,trackNSW,,,trackNW,trackN
trackEW,,trackNE,,trackNW,trackSE,trackSW,,,,,,,,,,,,,,trackSE,trackSW,trackNE,,trackNW,,trackEW
,trackNS,trackE,,trackW,trackN,trackS,,,,,,,,,,,,,,trackS,trackN,trackE,,trackW,trackNS
1 #build label(construct) start(14;14)
2 ,trackNS,trackE,,trackW,trackS,trackN,,,,,,,,,,,,,,trackN,trackS,trackE,,trackW,trackNS
3 trackEW,,trackSE,,trackSW,trackNE,trackNW,,,,,,,,,,,,,,trackNE,trackNW,trackSE,,trackSW,,trackEW
4 trackS,trackSE,,,trackNSE,trackNSW,trackEW,,,,,,,,,,,,,,trackEW,trackNSE,trackNSW,,,trackSW,trackS
5 trackN,trackNE,trackSEW,,,trackSEW,trackNEW,,,,,,,,,,,,,,trackNEW,trackSEW,,,trackSEW,trackNW,trackN
6 trackE,trackSW,trackNEW,,trackNSE,,trackNSEW,,,,,,,,,,,,,,trackNSEW,,trackNSW,,trackNEW,trackSE,trackW
7 trackW,trackNW,trackNS,,trackNSW,trackNSEW,,,,,,,,,,,,,,,,trackNSEW,trackNSE,,trackNS,trackNE,trackE
8 ,,,,,,,,,,trackrampNW,trackrampNS,trackrampN,,trackrampN,trackrampNS,trackrampNE
9 ,,,,,,,,,trackrampNW,,trackrampNSE,trackrampNSW,,trackrampNSE,trackrampNSW,,trackrampNE
10 ,,,,,,,,,trackrampEW,trackrampSEW,,trackrampNSEW,,trackrampNSEW,,trackrampSEW,trackrampEW
11 ,,,,,,,,,trackrampW,trackrampNEW,trackrampNSEW,,,,trackrampNSEW,trackrampNEW,trackrampE
12 ,,,,,,,,,trackrampW,trackrampSEW,trackrampNSEW,,,,trackrampNSEW,trackrampSEW,trackrampE
13 ,,,,,,,,,trackrampEW,trackrampNEW,,trackrampNSEW,,trackrampNSEW,,trackrampNEW,trackrampEW
14 ,,,,,,,,,trackrampSW,,trackrampNSE,trackrampNSW,,trackrampNSE,trackrampNSW,,trackrampSE
15 ,,,,,,,,,,trackrampSW,trackrampNS,trackrampS,,trackrampS,trackrampNS,trackrampSE
16 trackW,trackSW,trackNS,,trackNSW,trackNSEW,,,,,,,,,,,,,,,,trackNSEW,trackNSE,,trackNS,trackSE,trackE
17 trackE,trackNW,trackSEW,,trackNSE,,trackNSEW,,,,,,,,,,,,,,trackNSEW,,trackNSW,,trackSEW,trackNE,trackW
18 trackS,trackSE,trackNEW,,,trackNEW,trackSEW,,,,,,,,,,,,,,trackSEW,trackNEW,,,trackNEW,trackSW,trackS
19 trackN,trackNE,,,trackNSE,trackNSW,trackEW,,,,,,,,,,,,,,trackEW,trackNSE,trackNSW,,,trackNW,trackN
20 trackEW,,trackNE,,trackNW,trackSE,trackSW,,,,,,,,,,,,,,trackSE,trackSW,trackNE,,trackNW,,trackEW
21 ,trackNS,trackE,,trackW,trackN,trackS,,,,,,,,,,,,,,trackS,trackN,trackE,,trackW,trackNS

@ -0,0 +1,28 @@
#build label(build) start(14;14)
,~,~,,~,~,~,,gs(1x2),ga(2x1),,gx(1x2),gw(1x2),,gw(1x2),gx(1x2),gd(2x1),,gs(1x2),,~,~,~,,~,~
~,,~,,~,~,~,,,gd(2x1),,,,,,,ga(2x1),,,,~,~,~,,~,,~
~,~,,,~,~,~,,Mrsssqq(2x1),,,Msh,,,,,Msk,Mrsqq(2x1),,,~,~,~,,,~,~
,,,,,,,,CSdddaaaa,,Msk,,Mw,,Mw,,,Msh,CSddddaaaa
~,~,~,,,~,~,,CSa,,Mrss(1x2),Mw,,,,Mw,Mrss(1x2),,CSa,,~,~,,,~,~,~
~,~,~,,~,,~,,,Msm,,,Mhs(1x2),,Mhs(1x2),,,Msm,,,~,,~,,~,~,~
~,~,~,,~,~,,,Msu,,Mws,,,,,,Mws,,Msu,,,~,~,,~,~,~
,,,,,,,,,Mws,,Mh(2x1),,,Mh(2x1),,,Mws
gs(2x1),,Mrqq(1x2),CSddaaaa,CSa,,Msh,,,,,,,,,,,,,,,Msk,CSa,CSddaaaa,Mrqq(1x2),gs(2x1)
gw(1x2),gx(1x2),,,,Msk,,Mw,,,~,~,~,,~,~,~,,,Mw,,,Msh,,,gx(1x2),gw(1x2)
,,,Msm,Mrs(2x1),,Mw,,,~,,~,~,,~,~,,~,,,Mw,Mrsss(2x1),,Msm
gd(2x1),,Msu,,Mws,,,Mhs(1x2),,~,~,,~,,~,,~,~,,Mhs(1x2),,,Mws,,Msu,ga(2x1)
ga(2x1),,,Mws,,Mh(2x1),,,,~,~,~,,,,~,~,~,,,Mh(2x1),,,Mws,,gd(2x1)
ga(2x1),,,Mws,,Mh(2x1),,Mhs(1x2),,~,~,~,,,,~,~,~,,Mhs(1x2),Mh(2x1),,,Mws,,gd(2x1)
gd(2x1),,,,Mws,,,,,~,~,,~,,~,,~,~,,,,,Mws,,,ga(2x1)
gx(1x2),gw(1x2),Msm,,Mrs(2x1),,Mw,,,~,,~,~,,~,~,,~,,,Mw,Mrsss(2x1),,,Msm,gw(1x2),gx(1x2)
,,Mrssqq(1x2),Msu,,Msk,,Mw,,,~,~,~,,~,~,~,,,Mw,,,Msh,Msu,Mrssqq(1x2)
gs(2x1),,,CSdaaaa,CSa,,Msh,,,,,,,,,,,,,,,Msk,CSa,CSdaaaa,,gs(2x1)
,,,,,,,,,Mws,,Mh(2x1),,,Mh(2x1),,,Mws
~,~,~,,~,~,,,,,Mws,,Mhs(1x2),,Mhs(1x2),,Mws,,,,,~,~,,~,~,~
~,~,~,,~,,~,,Msm,,Mr(1x2),,,,,,Mr(1x2),,Msm,,~,,~,,~,~,~
~,~,~,,,~,~,,CSa,Msu,,Mw,,,,Mw,,Msu,CSa,,~,~,,,~,~,~
,,,,,,,,CSdddaaaa,,Msk,,Mw,,Mw,,,Msh,CSddddaaaa
~,~,,,~,~,~,,Mrsssqq(2x1),,,Msh,,,,,Msk,Mrsqq(2x1),,,~,~,~,,,~,~
~,,~,,~,~,~,,gs(1x2),gd(2x1),,gw(1x2),gx(1x2),,gx(1x2),gw(1x2),ga(2x1),,gs(1x2),,~,~,~,,~,,~
,~,~,,~,~,~,,,ga(2x1),,,,,,,gd(2x1),,,,~,~,~,,~,~
1 #build label(build) start(14;14)
2 ,~,~,,~,~,~,,gs(1x2),ga(2x1),,gx(1x2),gw(1x2),,gw(1x2),gx(1x2),gd(2x1),,gs(1x2),,~,~,~,,~,~
3 ~,,~,,~,~,~,,,gd(2x1),,,,,,,ga(2x1),,,,~,~,~,,~,,~
4 ~,~,,,~,~,~,,Mrsssqq(2x1),,,Msh,,,,,Msk,Mrsqq(2x1),,,~,~,~,,,~,~
5 ,,,,,,,,CSdddaaaa,,Msk,,Mw,,Mw,,,Msh,CSddddaaaa
6 ~,~,~,,,~,~,,CSa,,Mrss(1x2),Mw,,,,Mw,Mrss(1x2),,CSa,,~,~,,,~,~,~
7 ~,~,~,,~,,~,,,Msm,,,Mhs(1x2),,Mhs(1x2),,,Msm,,,~,,~,,~,~,~
8 ~,~,~,,~,~,,,Msu,,Mws,,,,,,Mws,,Msu,,,~,~,,~,~,~
9 ,,,,,,,,,Mws,,Mh(2x1),,,Mh(2x1),,,Mws
10 gs(2x1),,Mrqq(1x2),CSddaaaa,CSa,,Msh,,,,,,,,,,,,,,,Msk,CSa,CSddaaaa,Mrqq(1x2),gs(2x1)
11 gw(1x2),gx(1x2),,,,Msk,,Mw,,,~,~,~,,~,~,~,,,Mw,,,Msh,,,gx(1x2),gw(1x2)
12 ,,,Msm,Mrs(2x1),,Mw,,,~,,~,~,,~,~,,~,,,Mw,Mrsss(2x1),,Msm
13 gd(2x1),,Msu,,Mws,,,Mhs(1x2),,~,~,,~,,~,,~,~,,Mhs(1x2),,,Mws,,Msu,ga(2x1)
14 ga(2x1),,,Mws,,Mh(2x1),,,,~,~,~,,,,~,~,~,,,Mh(2x1),,,Mws,,gd(2x1)
15 ga(2x1),,,Mws,,Mh(2x1),,Mhs(1x2),,~,~,~,,,,~,~,~,,Mhs(1x2),Mh(2x1),,,Mws,,gd(2x1)
16 gd(2x1),,,,Mws,,,,,~,~,,~,,~,,~,~,,,,,Mws,,,ga(2x1)
17 gx(1x2),gw(1x2),Msm,,Mrs(2x1),,Mw,,,~,,~,~,,~,~,,~,,,Mw,Mrsss(2x1),,,Msm,gw(1x2),gx(1x2)
18 ,,Mrssqq(1x2),Msu,,Msk,,Mw,,,~,~,~,,~,~,~,,,Mw,,,Msh,Msu,Mrssqq(1x2)
19 gs(2x1),,,CSdaaaa,CSa,,Msh,,,,,,,,,,,,,,,Msk,CSa,CSdaaaa,,gs(2x1)
20 ,,,,,,,,,Mws,,Mh(2x1),,,Mh(2x1),,,Mws
21 ~,~,~,,~,~,,,,,Mws,,Mhs(1x2),,Mhs(1x2),,Mws,,,,,~,~,,~,~,~
22 ~,~,~,,~,,~,,Msm,,Mr(1x2),,,,,,Mr(1x2),,Msm,,~,,~,,~,~,~
23 ~,~,~,,,~,~,,CSa,Msu,,Mw,,,,Mw,,Msu,CSa,,~,~,,,~,~,~
24 ,,,,,,,,CSdddaaaa,,Msk,,Mw,,Mw,,,Msh,CSddddaaaa
25 ~,~,,,~,~,~,,Mrsssqq(2x1),,,Msh,,,,,Msk,Mrsqq(2x1),,,~,~,~,,,~,~
26 ~,,~,,~,~,~,,gs(1x2),gd(2x1),,gw(1x2),gx(1x2),,gx(1x2),gw(1x2),ga(2x1),,gs(1x2),,~,~,~,,~,,~
27 ,~,~,,~,~,~,,,ga(2x1),,,,,,,gd(2x1),,,,~,~,~,,~,~

@ -1,28 +0,0 @@
#build label(build) start(14;14)
,trackNS,trackE,,trackW,trackS,trackN,,gs(1x2),ga(2x1),,gx(1x2),gw(1x2),,gw(1x2),gx(1x2),gd(2x1),,gs(1x2),,trackN,trackS,trackE,,trackW,trackNS
trackEW,,trackSE,,trackSW,trackNE,trackNW,,,gd(2x1),,,,,,,ga(2x1),,,,trackNE,trackNW,trackSE,,trackSW,,trackEW
trackS,trackSE,,,trackNSE,trackNSW,trackEW,,Mrsssqq(2x1),,,Msh,,,,,Msk,Mrsqq(2x1),,,trackEW,trackNSE,trackNSW,,,trackSW,trackS
,,,,,,,,CSdddaaaa,,Msk,,Mw,,Mw,,,Msh,CSddddaaaa
trackN,trackNE,trackSEW,,,trackSEW,trackNEW,,CSa,,Mrss(1x2),Mw,,,,Mw,Mrss(1x2),,CSa,,trackNEW,trackSEW,,,trackSEW,trackNW,trackN
trackE,trackSW,trackNEW,,trackNSE,,trackNSEW,,,Msm,,,Mhs(1x2),,Mhs(1x2),,,Msm,,,trackNSEW,,trackNSW,,trackNEW,trackSE,trackW
trackW,trackNW,trackNS,,trackNSW,trackNSEW,,,Msu,,Mws,,,,,,Mws,,Msu,,,trackNSEW,trackNSE,,trackNS,trackNE,trackE
,,,,,,,,,Mws,,Mh(2x1),,,Mh(2x1),,,Mws
gs(2x1),,Mrqq(1x2),CSddaaaa,CSa,,Msh,,,,,,,,,,,,,,,Msk,CSa,CSddaaaa,Mrqq(1x2),gs(2x1)
gw(1x2),gx(1x2),,,,Msk,,Mw,,,trackrampNW,trackrampNS,trackrampN,,trackrampN,trackrampNS,trackrampNE,,,Mw,,,Msh,,,gx(1x2),gw(1x2)
,,,Msm,Mrs(2x1),,Mw,,,trackrampNW,,trackrampNSE,trackrampNSW,,trackrampNSE,trackrampNSW,,trackrampNE,,,Mw,Mrsss(2x1),,Msm
gd(2x1),,Msu,,Mws,,,Mhs(1x2),,trackrampEW,trackrampSEW,,trackrampNSEW,,trackrampNSEW,,trackrampSEW,trackrampEW,,Mhs(1x2),,,Mws,,Msu,ga(2x1)
ga(2x1),,,Mws,,Mh(2x1),,,,trackrampW,trackrampNEW,trackrampNSEW,,,,trackrampNSEW,trackrampNEW,trackrampE,,,Mh(2x1),,,Mws,,gd(2x1)
ga(2x1),,,Mws,,Mh(2x1),,Mhs(1x2),,trackrampW,trackrampSEW,trackrampNSEW,,,,trackrampNSEW,trackrampSEW,trackrampE,,Mhs(1x2),Mh(2x1),,,Mws,,gd(2x1)
gd(2x1),,,,Mws,,,,,trackrampEW,trackrampNEW,,trackrampNSEW,,trackrampNSEW,,trackrampNEW,trackrampEW,,,,,Mws,,,ga(2x1)
gx(1x2),gw(1x2),Msm,,Mrs(2x1),,Mw,,,trackrampSW,,trackrampNSE,trackrampNSW,,trackrampNSE,trackrampNSW,,trackrampSE,,,Mw,Mrsss(2x1),,,Msm,gw(1x2),gx(1x2)
,,Mrssqq(1x2),Msu,,Msk,,Mw,,,trackrampSW,trackrampNS,trackrampS,,trackrampS,trackrampNS,trackrampSE,,,Mw,,,Msh,Msu,Mrssqq(1x2)
gs(2x1),,,CSdaaaa,CSa,,Msh,,,,,,,,,,,,,,,Msk,CSa,CSdaaaa,,gs(2x1)
,,,,,,,,,Mws,,Mh(2x1),,,Mh(2x1),,,Mws
trackW,trackSW,trackNS,,trackNSW,trackNSEW,,,,,Mws,,Mhs(1x2),,Mhs(1x2),,Mws,,,,,trackNSEW,trackNSE,,trackNS,trackSE,trackE
trackE,trackNW,trackSEW,,trackNSE,,trackNSEW,,Msm,,Mr(1x2),,,,,,Mr(1x2),,Msm,,trackNSEW,,trackNSW,,trackSEW,trackNE,trackW
trackS,trackSE,trackNEW,,,trackNEW,trackSEW,,CSa,Msu,,Mw,,,,Mw,,Msu,CSa,,trackSEW,trackNEW,,,trackNEW,trackSW,trackS
,,,,,,,,CSdddaaaa,,Msk,,Mw,,Mw,,,Msh,CSddddaaaa
trackN,trackNE,,,trackNSE,trackNSW,trackEW,,Mrsssqq(2x1),,,Msh,,,,,Msk,Mrsqq(2x1),,,trackEW,trackNSE,trackNSW,,,trackNW,trackN
trackEW,,trackNE,,trackNW,trackSE,trackSW,,gs(1x2),gd(2x1),,gw(1x2),gx(1x2),,gx(1x2),gw(1x2),ga(2x1),,gs(1x2),,trackSE,trackSW,trackNE,,trackNW,,trackEW
,trackNS,trackE,,trackW,trackN,trackS,,,ga(2x1),,,,,,,gd(2x1),,,,trackS,trackN,trackE,,trackW,trackNS
1 #build label(build) start(14;14)
2 ,trackNS,trackE,,trackW,trackS,trackN,,gs(1x2),ga(2x1),,gx(1x2),gw(1x2),,gw(1x2),gx(1x2),gd(2x1),,gs(1x2),,trackN,trackS,trackE,,trackW,trackNS
3 trackEW,,trackSE,,trackSW,trackNE,trackNW,,,gd(2x1),,,,,,,ga(2x1),,,,trackNE,trackNW,trackSE,,trackSW,,trackEW
4 trackS,trackSE,,,trackNSE,trackNSW,trackEW,,Mrsssqq(2x1),,,Msh,,,,,Msk,Mrsqq(2x1),,,trackEW,trackNSE,trackNSW,,,trackSW,trackS
5 ,,,,,,,,CSdddaaaa,,Msk,,Mw,,Mw,,,Msh,CSddddaaaa
6 trackN,trackNE,trackSEW,,,trackSEW,trackNEW,,CSa,,Mrss(1x2),Mw,,,,Mw,Mrss(1x2),,CSa,,trackNEW,trackSEW,,,trackSEW,trackNW,trackN
7 trackE,trackSW,trackNEW,,trackNSE,,trackNSEW,,,Msm,,,Mhs(1x2),,Mhs(1x2),,,Msm,,,trackNSEW,,trackNSW,,trackNEW,trackSE,trackW
8 trackW,trackNW,trackNS,,trackNSW,trackNSEW,,,Msu,,Mws,,,,,,Mws,,Msu,,,trackNSEW,trackNSE,,trackNS,trackNE,trackE
9 ,,,,,,,,,Mws,,Mh(2x1),,,Mh(2x1),,,Mws
10 gs(2x1),,Mrqq(1x2),CSddaaaa,CSa,,Msh,,,,,,,,,,,,,,,Msk,CSa,CSddaaaa,Mrqq(1x2),gs(2x1)
11 gw(1x2),gx(1x2),,,,Msk,,Mw,,,trackrampNW,trackrampNS,trackrampN,,trackrampN,trackrampNS,trackrampNE,,,Mw,,,Msh,,,gx(1x2),gw(1x2)
12 ,,,Msm,Mrs(2x1),,Mw,,,trackrampNW,,trackrampNSE,trackrampNSW,,trackrampNSE,trackrampNSW,,trackrampNE,,,Mw,Mrsss(2x1),,Msm
13 gd(2x1),,Msu,,Mws,,,Mhs(1x2),,trackrampEW,trackrampSEW,,trackrampNSEW,,trackrampNSEW,,trackrampSEW,trackrampEW,,Mhs(1x2),,,Mws,,Msu,ga(2x1)
14 ga(2x1),,,Mws,,Mh(2x1),,,,trackrampW,trackrampNEW,trackrampNSEW,,,,trackrampNSEW,trackrampNEW,trackrampE,,,Mh(2x1),,,Mws,,gd(2x1)
15 ga(2x1),,,Mws,,Mh(2x1),,Mhs(1x2),,trackrampW,trackrampSEW,trackrampNSEW,,,,trackrampNSEW,trackrampSEW,trackrampE,,Mhs(1x2),Mh(2x1),,,Mws,,gd(2x1)
16 gd(2x1),,,,Mws,,,,,trackrampEW,trackrampNEW,,trackrampNSEW,,trackrampNSEW,,trackrampNEW,trackrampEW,,,,,Mws,,,ga(2x1)
17 gx(1x2),gw(1x2),Msm,,Mrs(2x1),,Mw,,,trackrampSW,,trackrampNSE,trackrampNSW,,trackrampNSE,trackrampNSW,,trackrampSE,,,Mw,Mrsss(2x1),,,Msm,gw(1x2),gx(1x2)
18 ,,Mrssqq(1x2),Msu,,Msk,,Mw,,,trackrampSW,trackrampNS,trackrampS,,trackrampS,trackrampNS,trackrampSE,,,Mw,,,Msh,Msu,Mrssqq(1x2)
19 gs(2x1),,,CSdaaaa,CSa,,Msh,,,,,,,,,,,,,,,Msk,CSa,CSdaaaa,,gs(2x1)
20 ,,,,,,,,,Mws,,Mh(2x1),,,Mh(2x1),,,Mws
21 trackW,trackSW,trackNS,,trackNSW,trackNSEW,,,,,Mws,,Mhs(1x2),,Mhs(1x2),,Mws,,,,,trackNSEW,trackNSE,,trackNS,trackSE,trackE
22 trackE,trackNW,trackSEW,,trackNSE,,trackNSEW,,Msm,,Mr(1x2),,,,,,Mr(1x2),,Msm,,trackNSEW,,trackNSW,,trackSEW,trackNE,trackW
23 trackS,trackSE,trackNEW,,,trackNEW,trackSEW,,CSa,Msu,,Mw,,,,Mw,,Msu,CSa,,trackSEW,trackNEW,,,trackNEW,trackSW,trackS
24 ,,,,,,,,CSdddaaaa,,Msk,,Mw,,Mw,,,Msh,CSddddaaaa
25 trackN,trackNE,,,trackNSE,trackNSW,trackEW,,Mrsssqq(2x1),,,Msh,,,,,Msk,Mrsqq(2x1),,,trackEW,trackNSE,trackNSW,,,trackNW,trackN
26 trackEW,,trackNE,,trackNW,trackSE,trackSW,,gs(1x2),gd(2x1),,gw(1x2),gx(1x2),,gx(1x2),gw(1x2),ga(2x1),,gs(1x2),,trackSE,trackSW,trackNE,,trackNW,,trackEW
27 ,trackNS,trackE,,trackW,trackN,trackS,,,ga(2x1),,,,,,,gd(2x1),,,,trackS,trackN,trackE,,trackW,trackNS

@ -0,0 +1,21 @@
#build label(construct)
Cw
Cf
Cr
Cu
Cd
Cx
CF
1 #build label(construct)
2 Cw
3 Cf
4 Cr
5 Cu
6 Cd
7 Cx
8 CF

@ -0,0 +1,33 @@
#build label(build)
a,Mg,,CS
b,Mh(1x1),S,CSa,,,,,,Mw,,,wm,,,wp
c,Mhs(1x1),m,CSaa,,,,,,,,,,,,,,,,,D
n,Mv,v,CSaaa,,Msu
,Mr(1x1),j,CSaaaa,,,,,,Mws,,,wu,,,ew
d,Mrq(1x1),A,CSd,,,Msk
,Mrqq(1x1),R,CSda
l,Mrqqq(1x1),N,CSdaa,,Msm,,,,we,,,wn,,,es,,,,,k
x,Mrqqqq(1x1),~h,CSdaaa
H,Mrs(1x1),~a,CSdaaaa,,,Msh
W,Mrsq(1x1),~c,CSdd,,,,,,wq,,,wr,,,el
G,Mrsqq(1x1),F,CSdda
B,Mrsqqq(1x1),o(1x1),CSddaa,,,,,,,,,,,,,,,,,ws
~,~b,Mrsqqqq(1x1),CSddaaa,,,,,,wM,,,wt,,,eg
~,f,Mrss(1x1),CSddaaaa
~,h,Mrssq(1x1),CSddd
~,r,Mrssqq(1x1),CSddda,,,,,,wo,,,wl,,,ea,,,,gx(1x2),gx(1x2)
~,s,Mrssqqq(1x1),CSdddaa
~,~s,Mrssqqqq(1x1),CSdddaaa,,,,,,,,,,,,,,gd(2x1),,gs(2x1),,ga(2x1)
~,t,Mrsss(1x1),CSdddaaaa,,,,,,wk,,,ww,,,ek,,gd(2x1),,gs(2x1),,ga(2x1)
gs(1x1),Mrsssq(1x1),,CSdddd,,,,,,,,,,,,,,,,gw(1x2),gw(1x2)
ga(1x1),Mrsssqq(1x1),,CSdddda
gd(1x1),Mrsssqqq(1x1),,CSddddaa,,,,,,wb,,,wz,,,en
gw(1x1),Mrsssqqqq(1x1),,CSddddaaa,,,,,,,,,,,,,,Mh(2x1),,Mh(2x1),,Mhs(1x2),Mhs(1x2)
gx(1x1),,,CSddddaaaa,,,,,,,,,,,,,,Mh(2x1),,Mh(2x1)
,,,Ts,,,,,,wc,,,wh,,,ib,,Mr(1x2),Mr(1x2),Mrs(2x1),,Mhs(1x2),Mhs(1x2)
y,,,Tw,,,,,,,,,,,,,,,,Mrs(2x1)
Y,,,Tl,,,,,,,,,,,,,,Mr(1x2),Mr(1x2),Mrsq(2x1),,Mrsq(2x1)
,,,Tp,,,,,,wf,,,wy,,,ic,,,,Mrsssqqqq(2x1),,Mrsssqqqq(2x1)
,,,Tc
,,,TS
,,,,,,,,,,wv,,,wd,,,wj,,,wS
1 #build label(build)
2 a,Mg,,CS
3 b,Mh(1x1),S,CSa,,,,,,Mw,,,wm,,,wp
4 c,Mhs(1x1),m,CSaa,,,,,,,,,,,,,,,,,D
5 n,Mv,v,CSaaa,,Msu
6 ,Mr(1x1),j,CSaaaa,,,,,,Mws,,,wu,,,ew
7 d,Mrq(1x1),A,CSd,,,Msk
8 ,Mrqq(1x1),R,CSda
9 l,Mrqqq(1x1),N,CSdaa,,Msm,,,,we,,,wn,,,es,,,,,k
10 x,Mrqqqq(1x1),~h,CSdaaa
11 H,Mrs(1x1),~a,CSdaaaa,,,Msh
12 W,Mrsq(1x1),~c,CSdd,,,,,,wq,,,wr,,,el
13 G,Mrsqq(1x1),F,CSdda
14 B,Mrsqqq(1x1),o(1x1),CSddaa,,,,,,,,,,,,,,,,,ws
15 ~,~b,Mrsqqqq(1x1),CSddaaa,,,,,,wM,,,wt,,,eg
16 ~,f,Mrss(1x1),CSddaaaa
17 ~,h,Mrssq(1x1),CSddd
18 ~,r,Mrssqq(1x1),CSddda,,,,,,wo,,,wl,,,ea,,,,gx(1x2),gx(1x2)
19 ~,s,Mrssqqq(1x1),CSdddaa
20 ~,~s,Mrssqqqq(1x1),CSdddaaa,,,,,,,,,,,,,,gd(2x1),,gs(2x1),,ga(2x1)
21 ~,t,Mrsss(1x1),CSdddaaaa,,,,,,wk,,,ww,,,ek,,gd(2x1),,gs(2x1),,ga(2x1)
22 gs(1x1),Mrsssq(1x1),,CSdddd,,,,,,,,,,,,,,,,gw(1x2),gw(1x2)
23 ga(1x1),Mrsssqq(1x1),,CSdddda
24 gd(1x1),Mrsssqqq(1x1),,CSddddaa,,,,,,wb,,,wz,,,en
25 gw(1x1),Mrsssqqqq(1x1),,CSddddaaa,,,,,,,,,,,,,,Mh(2x1),,Mh(2x1),,Mhs(1x2),Mhs(1x2)
26 gx(1x1),,,CSddddaaaa,,,,,,,,,,,,,,Mh(2x1),,Mh(2x1)
27 ,,,Ts,,,,,,wc,,,wh,,,ib,,Mr(1x2),Mr(1x2),Mrs(2x1),,Mhs(1x2),Mhs(1x2)
28 y,,,Tw,,,,,,,,,,,,,,,,Mrs(2x1)
29 Y,,,Tl,,,,,,,,,,,,,,Mr(1x2),Mr(1x2),Mrsq(2x1),,Mrsq(2x1)
30 ,,,Tp,,,,,,wf,,,wy,,,ic,,,,Mrsssqqqq(2x1),,Mrsssqqqq(2x1)
31 ,,,Tc
32 ,,,TS
33 ,,,,,,,,,,wv,,,wd,,,wj,,,wS

@ -1,33 +0,0 @@
#build label(build)
a,Mg,,CS,trackN
b,Mh(1x1),S,CSa,trackS,,,,,,Mw,,,wm,,,wp
c,Mhs(1x1),m,CSaa,trackE,,,,,,,,,,,,,,,,,D
n,Mv,v,CSaaa,trackW,,Msu
,Mr(1x1),j,CSaaaa,trackNS,,,,,,Mws,,,wu,,,ew
d,Mrq(1x1),A,CSd,trackNE,,,Msk
,Mrqq(1x1),R,CSda,trackNW
l,Mrqqq(1x1),N,CSdaa,trackSE,,Msm,,,,we,,,wn,,,es,,,,,k
x,Mrqqqq(1x1),~h,CSdaaa,trackSW
H,Mrs(1x1),~a,CSdaaaa,trackEW,,,Msh
W,Mrsq(1x1),~c,CSdd,trackNSE,,,,,,wq,,,wr,,,el
G,Mrsqq(1x1),F,CSdda,trackNSW
B,Mrsqqq(1x1),o(1x1),CSddaa,trackNEW,,,,,,,,,,,,,,,,,ws
~b,Mrsqqqq(1x1),Cw,CSddaaa,trackSEW,,,,,,wM,,,wt,,,eg
f,Mrss(1x1),Cf,CSddaaaa,trackNSEW
h,Mrssq(1x1),Cr,CSddd,trackrampN
r,Mrssqq(1x1),Cu,CSddda,trackrampS,,,,,,wo,,,wl,,,ea,,,,gx(1x2),gx(1x2)
s,Mrssqqq(1x1),Cd,CSdddaa,trackrampE
~s,Mrssqqqq(1x1),Cx,CSdddaaa,trackrampW,,,,,,,,,,,,,,gd(2x1),,gs(2x1),,ga(2x1)
t,Mrsss(1x1),CF,CSdddaaaa,trackrampNS,,,,,,wk,,,ww,,,ek,,gd(2x1),,gs(2x1),,ga(2x1)
gs(1x1),Mrsssq(1x1),,CSdddd,trackrampNE,,,,,,,,,,,,,,,,gw(1x2),gw(1x2)
ga(1x1),Mrsssqq(1x1),,CSdddda,trackrampNW
gd(1x1),Mrsssqqq(1x1),,CSddddaa,trackrampSE,,,,,,wb,,,wz,,,en
gw(1x1),Mrsssqqqq(1x1),,CSddddaaa,trackrampSW,,,,,,,,,,,,,,Mh(2x1),,Mh(2x1),,Mhs(1x2),Mhs(1x2)
gx(1x1),,,CSddddaaaa,trackrampEW,,,,,,,,,,,,,,Mh(2x1),,Mh(2x1)
,,,Ts,trackrampNSE,,,,,,wc,,,wh,,,ib,,Mr(1x2),Mr(1x2),Mrs(2x1),,Mhs(1x2),Mhs(1x2)
y,,,Tw,trackrampNSW,,,,,,,,,,,,,,,,Mrs(2x1)
Y,,,Tl,trackrampNEW,,,,,,,,,,,,,,Mr(1x2),Mr(1x2),Mrsq(2x1),,Mrsq(2x1)
,,,Tp,trackrampSEW,,,,,,wf,,,wy,,,ic,,,,Mrsssqqqq(2x1),,Mrsssqqqq(2x1)
,,,Tc,trackrampNSEW
,,,TS
,,,,,,,,,,wv,,,wd,,,wj,,,wS
1 #build label(build)
2 a,Mg,,CS,trackN
3 b,Mh(1x1),S,CSa,trackS,,,,,,Mw,,,wm,,,wp
4 c,Mhs(1x1),m,CSaa,trackE,,,,,,,,,,,,,,,,,D
5 n,Mv,v,CSaaa,trackW,,Msu
6 ,Mr(1x1),j,CSaaaa,trackNS,,,,,,Mws,,,wu,,,ew
7 d,Mrq(1x1),A,CSd,trackNE,,,Msk
8 ,Mrqq(1x1),R,CSda,trackNW
9 l,Mrqqq(1x1),N,CSdaa,trackSE,,Msm,,,,we,,,wn,,,es,,,,,k
10 x,Mrqqqq(1x1),~h,CSdaaa,trackSW
11 H,Mrs(1x1),~a,CSdaaaa,trackEW,,,Msh
12 W,Mrsq(1x1),~c,CSdd,trackNSE,,,,,,wq,,,wr,,,el
13 G,Mrsqq(1x1),F,CSdda,trackNSW
14 B,Mrsqqq(1x1),o(1x1),CSddaa,trackNEW,,,,,,,,,,,,,,,,,ws
15 ~b,Mrsqqqq(1x1),Cw,CSddaaa,trackSEW,,,,,,wM,,,wt,,,eg
16 f,Mrss(1x1),Cf,CSddaaaa,trackNSEW
17 h,Mrssq(1x1),Cr,CSddd,trackrampN
18 r,Mrssqq(1x1),Cu,CSddda,trackrampS,,,,,,wo,,,wl,,,ea,,,,gx(1x2),gx(1x2)
19 s,Mrssqqq(1x1),Cd,CSdddaa,trackrampE
20 ~s,Mrssqqqq(1x1),Cx,CSdddaaa,trackrampW,,,,,,,,,,,,,,gd(2x1),,gs(2x1),,ga(2x1)
21 t,Mrsss(1x1),CF,CSdddaaaa,trackrampNS,,,,,,wk,,,ww,,,ek,,gd(2x1),,gs(2x1),,ga(2x1)
22 gs(1x1),Mrsssq(1x1),,CSdddd,trackrampNE,,,,,,,,,,,,,,,,gw(1x2),gw(1x2)
23 ga(1x1),Mrsssqq(1x1),,CSdddda,trackrampNW
24 gd(1x1),Mrsssqqq(1x1),,CSddddaa,trackrampSE,,,,,,wb,,,wz,,,en
25 gw(1x1),Mrsssqqqq(1x1),,CSddddaaa,trackrampSW,,,,,,,,,,,,,,Mh(2x1),,Mh(2x1),,Mhs(1x2),Mhs(1x2)
26 gx(1x1),,,CSddddaaaa,trackrampEW,,,,,,,,,,,,,,Mh(2x1),,Mh(2x1)
27 ,,,Ts,trackrampNSE,,,,,,wc,,,wh,,,ib,,Mr(1x2),Mr(1x2),Mrs(2x1),,Mhs(1x2),Mhs(1x2)
28 y,,,Tw,trackrampNSW,,,,,,,,,,,,,,,,Mrs(2x1)
29 Y,,,Tl,trackrampNEW,,,,,,,,,,,,,,Mr(1x2),Mr(1x2),Mrsq(2x1),,Mrsq(2x1)
30 ,,,Tp,trackrampSEW,,,,,,wf,,,wy,,,ic,,,,Mrsssqqqq(2x1),,Mrsssqqqq(2x1)
31 ,,,Tc,trackrampNSEW
32 ,,,TS
33 ,,,,,,,,,,wv,,,wd,,,wj,,,wS

@ -1,12 +1,3 @@
#build label(big) hidden()
gw(1x2),gx(1x2),gd(2x1),,gs(1x2)
,,ga(2x1)
,,Msk,Mrsqq(2x1)
Mw,,,Msh,CSddddaaaa
,Mw,Mrss(1x2),,CSa
Mhs(1x2),,,Msm
,,Mws,,Msu
Mh(2x1),,,Mws
#build label(outer) hidden() #build label(outer) hidden()
trackN,trackS,trackE,,trackW,trackNS trackN,trackS,trackE,,trackW,trackNS
trackNE,trackNW,trackSE,,trackSW trackNE,trackNW,trackSE,,trackSW
@ -19,10 +10,9 @@ trackrampN,trackrampNS,trackrampNE
trackrampNSE,trackrampNSW trackrampNSE,trackrampNSW
trackrampNSEW trackrampNSEW
#meta label(chunk) hidden() #meta label(chunk) hidden()
/big shift(1 -13)
/outer shift(7 -13) /outer shift(7 -13)
/inner shift(1 -4) /inner shift(1 -4)
#meta label(build) #meta label(construct)
/chunk /chunk
/chunk transform(cw) /chunk transform(cw)
/chunk transform(cw cw) /chunk transform(cw cw)
1 #build label(big) hidden() #build label(outer) hidden()
#build label(big) hidden()
gw(1x2),gx(1x2),gd(2x1),,gs(1x2)
,,ga(2x1)
,,Msk,Mrsqq(2x1)
Mw,,,Msh,CSddddaaaa
,Mw,Mrss(1x2),,CSa
Mhs(1x2),,,Msm
,,Mws,,Msu
Mh(2x1),,,Mws
1 #build label(outer) hidden() #build label(outer) hidden()
2 trackN,trackS,trackE,,trackW,trackNS trackN,trackS,trackE,,trackW,trackNS
3 trackNE,trackNW,trackSE,,trackSW trackNE,trackNW,trackSE,,trackSW
10 trackrampNSEW trackrampNSEW
11 #meta label(chunk) hidden() #meta label(chunk) hidden()
12 /big shift(1 -13) /outer shift(7 -13)
/outer shift(7 -13)
13 /inner shift(1 -4) /inner shift(1 -4)
14 #meta label(build) #meta label(construct)
15 /chunk /chunk
16 /chunk transform(cw) /chunk transform(cw)
17 /chunk transform(cw cw) /chunk transform(cw cw)
18 /chunk transform(ccw) /chunk transform(ccw)

@ -0,0 +1,20 @@
#build label(big) hidden()
gw(1x2),gx(1x2),gd(2x1),,gs(1x2)
,,ga(2x1)
,,Msk,Mrsqq(2x1)
Mw,,,Msh,CSddddaaaa
,Mw,Mrss(1x2),,CSa
Mhs(1x2),,,Msm
,,Mws,,Msu
Mh(2x1),,,Mws
#meta label(chunk) hidden()
/big shift(1 -13)
#meta label(build)
/chunk
/chunk transform(cw)
/chunk transform(cw cw)
/chunk transform(ccw)
/chunk transform(fliph)
/chunk transform(flipv)
/chunk transform(cw flipv)
/chunk transform(ccw flipv)
1 #build label(big) hidden()
2 gw(1x2),gx(1x2),gd(2x1),,gs(1x2)
3 ,,ga(2x1)
4 ,,Msk,Mrsqq(2x1)
5 Mw,,,Msh,CSddddaaaa
6 ,Mw,Mrss(1x2),,CSa
7 Mhs(1x2),,,Msm
8 ,,Mws,,Msu
9 Mh(2x1),,,Mws
10 #meta label(chunk) hidden()
11 /big shift(1 -13)
12 #meta label(build)
13 /chunk
14 /chunk transform(cw)
15 /chunk transform(cw cw)
16 /chunk transform(ccw)
17 /chunk transform(fliph)
18 /chunk transform(flipv)
19 /chunk transform(cw flipv)
20 /chunk transform(ccw flipv)

@ -1906,6 +1906,14 @@ Constructions module
coordinates, designates it for removal, or instantly cancels the planned one. coordinates, designates it for removal, or instantly cancels the planned one.
Returns *true, was_only_planned* if removed; or *false* if none found. Returns *true, was_only_planned* if removed; or *false* if none found.
* ``dfhack.constructions.findAtTile(pos)``, or ``findAtTile(x,y,z)``
Returns the construction at the given position, or ``nil`` if there isn't one.
* ``dfhack.constructions.insert(construction)``
Properly inserts the given construction into the game. Returns false and fails to
insert if there was already a construction at the position.
Kitchen module Kitchen module
-------------- --------------

@ -38,12 +38,21 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
## Fixes ## Fixes
## Misc Improvements ## Misc Improvements
- `blueprint`: new ``--smooth`` option for recording all smoothed floors and walls instead of just the ones that require smoothing for later carving
- `blueprint`: record built constructions in blueprints
- `blueprint`: record stockpile/building/zone names in blueprints
- `blueprint`: record room sizes in blueprints
- `blueprint`: generate meta blueprints to reduce the number of blueprints you have to apply
- `blueprint`: support splitting the output file into phases grouped by when they can be applied
- `blueprint`: when splitting output files, number them so they sort into the order you should apply them in
- `ls`: indent tag listings and wrap them in the right column for better readability - `ls`: indent tag listings and wrap them in the right column for better readability
- `ls`: new ``--exclude`` option for hiding matched scripts from the output. this can be especially useful for modders who don't want their mod scripts to be included in ``ls`` output. - `ls`: new ``--exclude`` option for hiding matched scripts from the output. this can be especially useful for modders who don't want their mod scripts to be included in ``ls`` output.
- `digtype`: new ``-z`` option for digtype to restrict designations to the current z-level and down - `digtype`: new ``-z`` option for digtype to restrict designations to the current z-level and down
- UX: List widgets now have mouse-interactive scrollbars - UX: List widgets now have mouse-interactive scrollbars
- UX: You can now hold down the mouse button on a scrollbar to make it scroll multiple times. - UX: You can now hold down the mouse button on a scrollbar to make it scroll multiple times.
- UX: You can now drag the scrollbar to scroll to a specific spot - UX: You can now drag the scrollbar to scroll to a specific spot
- `overlay`: reduce the size of the "DFHack Launcher" button
- Constructions module: ``findAtTile`` now uses a binary search intead of a linear search.
- `spectate`: new ``auto-unpause`` option for auto-dismissal of announcement pause events (e.g. sieges). - `spectate`: new ``auto-unpause`` option for auto-dismissal of announcement pause events (e.g. sieges).
- `spectate`: new ``auto-disengage`` option for auto-disengagement of plugin through player interaction whilst unpaused. - `spectate`: new ``auto-disengage`` option for auto-disengagement of plugin through player interaction whilst unpaused.
- `spectate`: new ``focus-jobs`` option for following a dwarf after their job has finished (when disabled). - `spectate`: new ``focus-jobs`` option for following a dwarf after their job has finished (when disabled).
@ -54,9 +63,12 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
- `spectate`: improved documentation of features and functionality - `spectate`: improved documentation of features and functionality
## API ## API
- Constructions module: added ``insert()`` to insert constructions into the game's sorted list.
## Lua ## Lua
- ``widgets.Scrollbar``: new scrollbar widget that can be paired with an associated scrollable widget. Integrated with ``widgets.Label`` and ``widgets.List``. - ``widgets.Scrollbar``: new scrollbar widget that can be paired with an associated scrollable widget. Integrated with ``widgets.Label`` and ``widgets.List``.
- ``dfhack.constructions.findAtTile()``: exposed preexisting function to Lua.
- ``dfhack.constructions.insert()``: exposed new function to Lua.
# 0.47.05-r7 # 0.47.05-r7

@ -7,11 +7,11 @@ blueprint
With ``blueprint``, you can export the structure of a portion of your fortress With ``blueprint``, you can export the structure of a portion of your fortress
in a blueprint file that you (or anyone else) can later play back with in a blueprint file that you (or anyone else) can later play back with
`quickfort`. `gui/quickfort`.
Blueprints are ``.csv`` or ``.xlsx`` files created in the ``blueprints`` Blueprints are ``.csv`` or ``.xlsx`` files created in the ``blueprints``
subdirectory of your DF folder. The map area to turn into a blueprint is either subdirectory of your DF folder. The map area to turn into a blueprint is either
selected interactively with the ``blueprint gui`` command or, if the GUI is not selected interactively with the ``gui/blueprint`` command or, if the GUI is not
used, starts at the active cursor location and extends right and down for the used, starts at the active cursor location and extends right and down for the
requested width and height. requested width and height.
@ -27,16 +27,16 @@ Examples
-------- --------
``blueprint gui`` ``blueprint gui``
Runs `gui/blueprint`, the interactive frontend, where all configuration for Runs `gui/blueprint`, the GUI frontend, where all configuration for a
a ``blueprint`` command can be set visually and interactively. ``blueprint`` command can be set visually and interactively.
``blueprint 30 40 bedrooms`` ``blueprint 30 40 bedrooms``
Generates blueprints for an area 30 tiles wide by 40 tiles tall, starting Generates blueprints for an area 30 tiles wide by 40 tiles tall, starting
from the active cursor on the current z-level. Blueprints are written to from the active cursor on the current z-level. Blueprints are written to
``bedrooms.csv`` in the ``blueprints`` directory. ``bedrooms.csv`` in the ``blueprints`` directory.
``blueprint 30 40 bedrooms dig --cursor 108,100,150`` ``blueprint 30 40 bedrooms dig --cursor 108,100,150``
Generates only the ``#dig`` blueprint in the ``bedrooms.csv`` file, and Generates only the ``#dig`` blueprint in the ``bedrooms.csv`` file, and
the start of the blueprint area is set to a specific value instead of using the start of the blueprint area is set to a specific coordinate instead of
the in-game cursor position. using the in-game cursor position.
Positional parameters Positional parameters
--------------------- ---------------------
@ -66,14 +66,21 @@ phases; just separate them with a space.
Generate quickfort ``#dig`` blueprints for digging natural stone. Generate quickfort ``#dig`` blueprints for digging natural stone.
``carve`` ``carve``
Generate quickfort ``#dig`` blueprints for smoothing and carving. Generate quickfort ``#dig`` blueprints for smoothing and carving.
``construct``
Generate quickfort ``#build`` blueprints for constructions (e.g. flooring
and walls).
``build`` ``build``
Generate quickfort ``#build`` blueprints for constructions and buildings. Generate quickfort ``#build`` blueprints for buildings (including
furniture).
``place`` ``place``
Generate quickfort ``#place`` blueprints for placing stockpiles. Generate quickfort ``#place`` blueprints for placing stockpiles.
``zone`` ``zone``
Generate quickfort ``#zone`` blueprints for designating zones. Generate quickfort ``#zone`` blueprints for designating zones.
``query`` ``query``
Generate quickfort ``#query`` blueprints for configuring rooms. Generate quickfort ``#query`` blueprints for configuring stockpiles and
naming buildings.
``rooms``
Generate quickfort ``#query`` blueprints for defining rooms.
If no phases are specified, phases are autodetected. For example, a ``#place`` If no phases are specified, phases are autodetected. For example, a ``#place``
blueprint will be created only if there are stockpiles in the blueprint area. blueprint will be created only if there are stockpiles in the blueprint area.
@ -92,8 +99,14 @@ Options
Select the output format of the generated files. See the `Output formats`_ Select the output format of the generated files. See the `Output formats`_
section below for options. If not specified, the output format defaults to section below for options. If not specified, the output format defaults to
"minimal", which will produce a small, fast ``.csv`` file. "minimal", which will produce a small, fast ``.csv`` file.
``-h``, ``--help`` ``--nometa``
Show command help text. `Meta blueprints <quickfort-meta>` let you apply all blueprints that can be
replayed at the same time (without unpausing the game) with a single
command. This usually reduces the number of `quickfort` commands you need to
run to rebuild your fort from about 6 to 2 or 3. If you would rather just
have the low-level blueprints, this flag will prevent meta blueprints from
being generated and any low-level blueprints from being
`hidden <quickfort-hidden>` from the ``quickfort list`` command.
``-s``, ``--playback-start <x>,<y>,<comment>`` ``-s``, ``--playback-start <x>,<y>,<comment>``
Specify the column and row offsets (relative to the upper-left corner of the Specify the column and row offsets (relative to the upper-left corner of the
blueprint, which is ``1,1``) where the player should put the cursor when the blueprint, which is ``1,1``) where the player should put the cursor when the
@ -133,5 +146,10 @@ The ``--splitby`` flag can take any of the following values:
``none`` ``none``
Writes all blueprints into a single file. This is the standard format for Writes all blueprints into a single file. This is the standard format for
quickfort fortress blueprint bundles and is the default. quickfort fortress blueprint bundles and is the default.
``group``
Creates one file per group of blueprints that can be played back at the same
time (without have to unpause the game and let dwarves fulfill jobs between
blueprint runs).
``phase`` ``phase``
Creates a separate file for each phase. Creates a separate file for each phase. Implies ``--nometa`` since meta
blueprints can't combine blueprints that are in separate files.

@ -2264,6 +2264,7 @@ static const luaL_Reg dfhack_buildings_funcs[] = {
static const LuaWrapper::FunctionReg dfhack_constructions_module[] = { static const LuaWrapper::FunctionReg dfhack_constructions_module[] = {
WRAPM(Constructions, designateNew), WRAPM(Constructions, designateNew),
WRAPM(Constructions, insert),
{ NULL, NULL } { NULL, NULL }
}; };
@ -2276,8 +2277,16 @@ static int constructions_designateRemove(lua_State *L)
return 2; return 2;
} }
static int constructions_findAtTile(lua_State *L)
{
auto pos = CheckCoordXYZ(L, 1, true);
Lua::PushDFObject(L, Constructions::findAtTile(pos));
return 1;
}
static const luaL_Reg dfhack_constructions_funcs[] = { static const luaL_Reg dfhack_constructions_funcs[] = {
{ "designateRemove", constructions_designateRemove }, { "designateRemove", constructions_designateRemove },
{ "findAtTile", constructions_findAtTile },
{ NULL, NULL } { NULL, NULL }
}; };

@ -116,7 +116,7 @@ namespace DFHack
//This is a static string, overwritten with every call! //This is a static string, overwritten with every call!
//Support values > 2 even though they should never happen. //Support values > 2 even though they should never happen.
//Copy string if it will be used. //Copy string if it will be used.
inline char * getStr() const inline const char * getStr() const
{ {
static char str[16]; static char str[16];

@ -45,6 +45,8 @@ namespace Constructions
DFHACK_EXPORT df::construction * findAtTile(df::coord pos); DFHACK_EXPORT df::construction * findAtTile(df::coord pos);
DFHACK_EXPORT bool insert(df::construction * constr);
DFHACK_EXPORT bool designateNew(df::coord pos, df::construction_type type, DFHACK_EXPORT bool designateNew(df::coord pos, df::construction_type type,
df::item_type item = df::item_type::NONE, int mat_index = -1); df::item_type item = df::item_type::NONE, int mat_index = -1);

@ -54,11 +54,18 @@ using df::global::world;
df::construction * Constructions::findAtTile(df::coord pos) df::construction * Constructions::findAtTile(df::coord pos)
{ {
for (auto it = world->constructions.begin(); it != world->constructions.end(); ++it) { int index = binsearch_index(world->constructions, pos);
if ((*it)->pos == pos) if (index == -1) {
return *it; return NULL;
} }
return NULL; return world->constructions[index];
}
bool Constructions::insert(df::construction * constr)
{
bool toInsert;
insert_into_vector(world->constructions, &df::construction::pos, constr, &toInsert);
return toInsert;
} }
bool Constructions::designateNew(df::coord pos, df::construction_type type, bool Constructions::designateNew(df::coord pos, df::construction_type type,

@ -5,7 +5,6 @@
* Written by cdombroski. * Written by cdombroski.
*/ */
#include <algorithm>
#include <sstream> #include <sstream>
#include <unordered_map> #include <unordered_map>
@ -13,6 +12,7 @@
#include "DataDefs.h" #include "DataDefs.h"
#include "DataFuncs.h" #include "DataFuncs.h"
#include "DataIdentity.h" #include "DataIdentity.h"
#include "Debug.h"
#include "LuaTools.h" #include "LuaTools.h"
#include "PluginManager.h" #include "PluginManager.h"
#include "TileTypes.h" #include "TileTypes.h"
@ -46,6 +46,10 @@ using namespace DFHack;
DFHACK_PLUGIN("blueprint"); DFHACK_PLUGIN("blueprint");
REQUIRE_GLOBAL(world); REQUIRE_GLOBAL(world);
namespace DFHack {
DBG_DECLARE(blueprint,log);
}
struct blueprint_options { struct blueprint_options {
// whether to display help // whether to display help
bool help = false; bool help = false;
@ -58,6 +62,9 @@ struct blueprint_options {
// for it. // for it.
string format; string format;
// whether to skip generating meta blueprints
bool nometa = false;
// offset and comment to write in the quickfort start() modeline marker // offset and comment to write in the quickfort start() modeline marker
// if not set, coordinates are set to 0 and the comment will be empty // if not set, coordinates are set to 0 and the comment will be empty
df::coord2d playback_start = df::coord2d(0, 0); df::coord2d playback_start = df::coord2d(0, 0);
@ -85,12 +92,14 @@ struct blueprint_options {
bool auto_phase = false; bool auto_phase = false;
// if not autodetecting, which phases to output // if not autodetecting, which phases to output
bool dig = false; bool dig = false;
bool carve = false; bool carve = false;
bool construct = false;
bool build = false; bool build = false;
bool place = false; bool place = false;
bool zone = false; bool zone = false;
bool query = false; bool query = false;
bool rooms = false;
static struct_identity _identity; static struct_identity _identity;
}; };
@ -98,6 +107,7 @@ static const struct_field_info blueprint_options_fields[] = {
{ struct_field_info::PRIMITIVE, "help", offsetof(blueprint_options, help), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "help", offsetof(blueprint_options, help), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::SUBSTRUCT, "start", offsetof(blueprint_options, start), &df::coord::_identity, 0, 0 }, { struct_field_info::SUBSTRUCT, "start", offsetof(blueprint_options, start), &df::coord::_identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "format", offsetof(blueprint_options, format), df::identity_traits<string>::get(), 0, 0 }, { struct_field_info::PRIMITIVE, "format", offsetof(blueprint_options, format), df::identity_traits<string>::get(), 0, 0 },
{ struct_field_info::PRIMITIVE, "nometa", offsetof(blueprint_options, nometa), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::SUBSTRUCT, "playback_start", offsetof(blueprint_options, playback_start), &df::coord2d::_identity, 0, 0 }, { struct_field_info::SUBSTRUCT, "playback_start", offsetof(blueprint_options, playback_start), &df::coord2d::_identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "playback_start_comment", offsetof(blueprint_options, playback_start_comment), df::identity_traits<string>::get(), 0, 0 }, { struct_field_info::PRIMITIVE, "playback_start_comment", offsetof(blueprint_options, playback_start_comment), df::identity_traits<string>::get(), 0, 0 },
{ struct_field_info::PRIMITIVE, "split_strategy", offsetof(blueprint_options, split_strategy), df::identity_traits<string>::get(), 0, 0 }, { struct_field_info::PRIMITIVE, "split_strategy", offsetof(blueprint_options, split_strategy), df::identity_traits<string>::get(), 0, 0 },
@ -110,10 +120,12 @@ static const struct_field_info blueprint_options_fields[] = {
{ struct_field_info::PRIMITIVE, "auto_phase", offsetof(blueprint_options, auto_phase), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "auto_phase", offsetof(blueprint_options, auto_phase), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "dig", offsetof(blueprint_options, dig), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "dig", offsetof(blueprint_options, dig), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "carve", offsetof(blueprint_options, carve), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "carve", offsetof(blueprint_options, carve), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "construct", offsetof(blueprint_options, construct), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "build", offsetof(blueprint_options, build), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "build", offsetof(blueprint_options, build), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "place", offsetof(blueprint_options, place), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "place", offsetof(blueprint_options, place), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "zone", offsetof(blueprint_options, zone), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "zone", offsetof(blueprint_options, zone), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "query", offsetof(blueprint_options, query), &df::identity_traits<bool>::identity, 0, 0 }, { struct_field_info::PRIMITIVE, "query", offsetof(blueprint_options, query), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::PRIMITIVE, "rooms", offsetof(blueprint_options, rooms), &df::identity_traits<bool>::identity, 0, 0 },
{ struct_field_info::END } { struct_field_info::END }
}; };
struct_identity blueprint_options::_identity(sizeof(blueprint_options), &df::allocator_fn<blueprint_options>, NULL, "blueprint_options", NULL, blueprint_options_fields); struct_identity blueprint_options::_identity(sizeof(blueprint_options), &df::allocator_fn<blueprint_options>, NULL, "blueprint_options", NULL, blueprint_options_fields);
@ -132,11 +144,36 @@ DFhackCExport command_result plugin_shutdown(color_ostream &) {
return CR_OK; return CR_OK;
} }
struct blueprint_processor;
struct tile_context { struct tile_context {
blueprint_processor *processor;
bool pretty = false; bool pretty = false;
df::building* b = NULL; df::building* b = NULL;
}; };
typedef vector<const char *> bp_row; // index is x coordinate
typedef map<int16_t, bp_row> bp_area; // key is y coordinate
typedef map<int16_t, bp_area> bp_volume; // key is z coordinate
typedef const char * (get_tile_fn)(const df::coord &pos,
const tile_context &ctx);
typedef void (init_ctx_fn)(const df::coord &pos, tile_context &ctx);
struct blueprint_processor {
bp_volume mapdata;
const string mode;
const string phase;
const bool force_create;
get_tile_fn * const get_tile;
init_ctx_fn * const init_ctx;
std::set<df::building *> seen;
blueprint_processor(const string &mode, const string &phase,
bool force_create, get_tile_fn *get_tile,
init_ctx_fn *init_ctx)
: mode(mode), phase(phase), force_create(force_create),
get_tile(get_tile), init_ctx(init_ctx) { }
};
// global engravings cache, cleared when the string cache is cleared // global engravings cache, cleared when the string cache is cleared
struct PosHash { struct PosHash {
size_t operator()(const df::coord &c) const { size_t operator()(const df::coord &c) const {
@ -332,6 +369,108 @@ static const char * get_tile_carve(const df::coord &pos, const tile_context &tc)
return NULL; return NULL;
} }
static const char * get_construction_str(df::building *b) {
df::building_constructionst *cons =
virtual_cast<df::building_constructionst>(b);
if (!cons)
return "~";
switch (cons->type) {
case construction_type::Fortification: return "CF";
case construction_type::Wall: return "Cw";
case construction_type::Floor: return "Cf";
case construction_type::UpStair: return "Cu";
case construction_type::DownStair: return "Cd";
case construction_type::UpDownStair: return "Cx";
case construction_type::Ramp: return "Cr";
case construction_type::TrackN: return "trackN";
case construction_type::TrackS: return "trackS";
case construction_type::TrackE: return "trackE";
case construction_type::TrackW: return "trackW";
case construction_type::TrackNS: return "trackNS";
case construction_type::TrackNE: return "trackNE";
case construction_type::TrackNW: return "trackNW";
case construction_type::TrackSE: return "trackSE";
case construction_type::TrackSW: return "trackSW";
case construction_type::TrackEW: return "trackEW";
case construction_type::TrackNSE: return "trackNSE";
case construction_type::TrackNSW: return "trackNSW";
case construction_type::TrackNEW: return "trackNEW";
case construction_type::TrackSEW: return "trackSEW";
case construction_type::TrackNSEW: return "trackNSEW";
case construction_type::TrackRampN: return "trackrampN";
case construction_type::TrackRampS: return "trackrampS";
case construction_type::TrackRampE: return "trackrampE";
case construction_type::TrackRampW: return "trackrampW";
case construction_type::TrackRampNS: return "trackrampNS";
case construction_type::TrackRampNE: return "trackrampNE";
case construction_type::TrackRampNW: return "trackrampNW";
case construction_type::TrackRampSE: return "trackrampSE";
case construction_type::TrackRampSW: return "trackrampSW";
case construction_type::TrackRampEW: return "trackrampEW";
case construction_type::TrackRampNSE: return "trackrampNSE";
case construction_type::TrackRampNSW: return "trackrampNSW";
case construction_type::TrackRampNEW: return "trackrampNEW";
case construction_type::TrackRampSEW: return "trackrampSEW";
case construction_type::TrackRampNSEW: return "trackrampNSEW";
case construction_type::NONE:
default:
return "~";
}
}
static const char * get_constructed_track_str(df::tiletype *tt,
const char * base) {
TileDirection dir = tileDirection(*tt);
if (!dir.whole)
return "~";
std::ostringstream str;
str << base;
if (dir.north) str << "N";
if (dir.south) str << "S";
if (dir.east) str << "E";
if (dir.west) str << "W";
return cache(str);
}
static const char * get_constructed_floor_str(df::tiletype *tt) {
if (tileSpecial(*tt) != df::tiletype_special::TRACK)
return "Cf";
return get_constructed_track_str(tt, "track");
}
static const char * get_constructed_ramp_str(df::tiletype *tt) {
if (tileSpecial(*tt) != df::tiletype_special::TRACK)
return "Cr";
return get_constructed_track_str(tt, "trackramp");
}
static const char * get_tile_construct(const df::coord &pos,
const tile_context &ctx) {
if (ctx.b && ctx.b->getType() == building_type::Construction)
return get_construction_str(ctx.b);
df::tiletype *tt = Maps::getTileType(pos);
if (!tt || tileMaterial(*tt) != df::tiletype_material::CONSTRUCTION)
return NULL;
switch (tileShape(*tt)) {
case tiletype_shape::WALL: return "Cw";
case tiletype_shape::FLOOR: return get_constructed_floor_str(tt);
case tiletype_shape::RAMP: return get_constructed_ramp_str(tt);
case tiletype_shape::FORTIFICATION: return "CF";
case tiletype_shape::STAIR_UP: return "Cu";
case tiletype_shape::STAIR_DOWN: return "Cd";
case tiletype_shape::STAIR_UPDOWN: return "Cx";
default:
return "~";
}
return NULL;
}
static pair<uint32_t, uint32_t> get_building_size(const df::building *b) { static pair<uint32_t, uint32_t> get_building_size(const df::building *b) {
return pair<uint32_t, uint32_t>(b->x2 - b->x1 + 1, b->y2 - b->y1 + 1); return pair<uint32_t, uint32_t>(b->x2 - b->x1 + 1, b->y2 - b->y1 + 1);
} }
@ -461,56 +600,6 @@ static const char * get_furnace_str(df::building *b) {
} }
} }
static const char * get_construction_str(df::building *b) {
df::building_constructionst *cons =
virtual_cast<df::building_constructionst>(b);
if (!cons)
return "~";
switch (cons->type) {
case construction_type::Fortification: return "CF";
case construction_type::Wall: return "Cw";
case construction_type::Floor: return "Cf";
case construction_type::UpStair: return "Cu";
case construction_type::DownStair: return "Cd";
case construction_type::UpDownStair: return "Cx";
case construction_type::Ramp: return "Cr";
case construction_type::TrackN: return "trackN";
case construction_type::TrackS: return "trackS";
case construction_type::TrackE: return "trackE";
case construction_type::TrackW: return "trackW";
case construction_type::TrackNS: return "trackNS";
case construction_type::TrackNE: return "trackNE";
case construction_type::TrackNW: return "trackNW";
case construction_type::TrackSE: return "trackSE";
case construction_type::TrackSW: return "trackSW";
case construction_type::TrackEW: return "trackEW";
case construction_type::TrackNSE: return "trackNSE";
case construction_type::TrackNSW: return "trackNSW";
case construction_type::TrackNEW: return "trackNEW";
case construction_type::TrackSEW: return "trackSEW";
case construction_type::TrackNSEW: return "trackNSEW";
case construction_type::TrackRampN: return "trackrampN";
case construction_type::TrackRampS: return "trackrampS";
case construction_type::TrackRampE: return "trackrampE";
case construction_type::TrackRampW: return "trackrampW";
case construction_type::TrackRampNS: return "trackrampNS";
case construction_type::TrackRampNE: return "trackrampNE";
case construction_type::TrackRampNW: return "trackrampNW";
case construction_type::TrackRampSE: return "trackrampSE";
case construction_type::TrackRampSW: return "trackrampSW";
case construction_type::TrackRampEW: return "trackrampEW";
case construction_type::TrackRampNSE: return "trackrampNSE";
case construction_type::TrackRampNSW: return "trackrampNSW";
case construction_type::TrackRampNEW: return "trackrampNEW";
case construction_type::TrackRampSEW: return "trackrampSEW";
case construction_type::TrackRampNSEW: return "trackrampNSEW";
case construction_type::NONE:
default:
return "~";
}
}
static const char * get_trap_str(df::building *b) { static const char * get_trap_str(df::building *b) {
df::building_trapst *trap = virtual_cast<df::building_trapst>(b); df::building_trapst *trap = virtual_cast<df::building_trapst>(b);
if (!trap) if (!trap)
@ -622,6 +711,7 @@ static const char * get_build_keys(const df::coord &pos,
bool at_center = static_cast<int32_t>(pos.x) == ctx.b->centerx bool at_center = static_cast<int32_t>(pos.x) == ctx.b->centerx
&& static_cast<int32_t>(pos.y) == ctx.b->centery; && static_cast<int32_t>(pos.y) == ctx.b->centery;
// building_type::Construction is handled by the construction phase
switch(ctx.b->getType()) { switch(ctx.b->getType()) {
case building_type::Armorstand: case building_type::Armorstand:
return "a"; return "a";
@ -666,8 +756,6 @@ static const char * get_build_keys(const df::coord &pos,
return "y"; return "y";
case building_type::WindowGem: case building_type::WindowGem:
return "Y"; return "Y";
case building_type::Construction:
return get_construction_str(ctx.b);
case building_type::Shop: case building_type::Shop:
return do_block_building(ctx, "z", at_center); return do_block_building(ctx, "z", at_center);
case building_type::AnimalTrap: case building_type::AnimalTrap:
@ -921,10 +1009,82 @@ static const char * get_tile_zone(const df::coord &pos,
return add_expansion_syntax(zone, get_zone_keys(zone)); return add_expansion_syntax(zone, get_zone_keys(zone));
} }
static const char * get_tile_query(const df::coord &, const tile_context &ctx) { // surrounds the given string in quotes and replaces internal double quotes (")
// with double double quotes ("") (as per the csv spec)
static string csv_quote(const string &str) {
std::ostringstream outstr;
outstr << "\"";
size_t start = 0;
auto end = str.find('"');
while (end != std::string::npos) {
outstr << str.substr(start, end - start);
outstr << "\"\"";
start = end + 1;
end = str.find('"', start);
}
outstr << str.substr(start, end) << "\"";
return outstr.str();
}
static const char * get_tile_query(const df::coord &pos,
const tile_context &ctx) {
string bld_name, zone_name;
auto & seen = ctx.processor->seen;
if (ctx.b && !seen.count(ctx.b)) {
bld_name = ctx.b->name;
seen.emplace(ctx.b);
}
vector<df::building_civzonest*> civzones;
if (Buildings::findCivzonesAt(&civzones, pos)) {
auto civzone = civzones.back();
if (!seen.count(civzone)) {
zone_name = civzone->name;
seen.emplace(civzone);
}
}
if (!bld_name.size() && !zone_name.size())
return NULL;
std::ostringstream str;
if (bld_name.size())
str << "{givename name=" + csv_quote(bld_name) + "}";
if (zone_name.size())
str << "{namezone name=" + csv_quote(zone_name) + "}";
return cache(csv_quote(str.str()));
}
static const char * get_tile_rooms(const df::coord &, const tile_context &ctx) {
if (!ctx.b || !ctx.b->is_room) if (!ctx.b || !ctx.b->is_room)
return NULL; return NULL;
return "r+";
// get the maximum distance from the center of the building
df::building_extents &room = ctx.b->room;
int32_t x1 = room.x;
int32_t x2 = room.x + room.width - 1;
int32_t y1 = room.y;
int32_t y2 = room.y + room.height - 1;
int32_t dimx = std::max(ctx.b->centerx - x1, x2 - ctx.b->centerx);
int32_t dimy = std::max(ctx.b->centery - y1, y2 - ctx.b->centery);
int32_t max_dim = std::max(dimx, dimy);
switch (max_dim) {
case 0: return "r---&";
case 1: return "r--&";
case 2: return "r-&";
case 3: return "r&";
case 4: return "r+&";
}
std::ostringstream str;
str << "r{+ " << (max_dim - 3) << "}&";
return cache(str);
} }
static bool create_output_dir(color_ostream &out, static bool create_output_dir(color_ostream &out,
@ -945,11 +1105,12 @@ static bool create_output_dir(color_ostream &out,
static bool get_filename(string &fname, static bool get_filename(string &fname,
color_ostream &out, color_ostream &out,
blueprint_options opts, // copy because we can't const blueprint_options opts, // copy because we can't const
const string &phase) { const string &phase,
int32_t ordinal) {
auto L = Lua::Core::State; auto L = Lua::Core::State;
Lua::StackUnwinder top(L); Lua::StackUnwinder top(L);
if (!lua_checkstack(L, 3) || if (!lua_checkstack(L, 4) ||
!Lua::PushModulePublic( !Lua::PushModulePublic(
out, L, "plugins.blueprint", "get_filename")) { out, L, "plugins.blueprint", "get_filename")) {
out.printerr("Failed to load blueprint Lua code\n"); out.printerr("Failed to load blueprint Lua code\n");
@ -958,8 +1119,9 @@ static bool get_filename(string &fname,
Lua::Push(L, &opts); Lua::Push(L, &opts);
Lua::Push(L, phase); Lua::Push(L, phase);
Lua::Push(L, ordinal);
if (!Lua::SafeCall(out, L, 2, 1)) { if (!Lua::SafeCall(out, L, 3, 1)) {
out.printerr("Failed Lua call to get_filename\n"); out.printerr("Failed Lua call to get_filename\n");
return false; return false;
} }
@ -974,27 +1136,31 @@ static bool get_filename(string &fname,
return true; return true;
} }
typedef vector<const char *> bp_row; // index is x coordinate // returns true if we could interface with lua and could verify that the given
typedef map<int16_t, bp_row> bp_area; // key is y coordinate // phase is a meta phase
typedef map<int16_t, bp_area> bp_volume; // key is z coordinate static bool is_meta_phase(color_ostream &out,
blueprint_options opts, // copy because we can't const
const string &phase) {
auto L = Lua::Core::State;
Lua::StackUnwinder top(L);
typedef const char * (get_tile_fn)(const df::coord &pos, if (!lua_checkstack(L, 3) ||
const tile_context &ctx); !Lua::PushModulePublic(
typedef void (init_ctx_fn)(const df::coord &pos, tile_context &ctx); out, L, "plugins.blueprint", "is_meta_phase")) {
out.printerr("Failed to load blueprint Lua code\n");
return false;
}
struct blueprint_processor { Lua::Push(L, &opts);
bp_volume mapdata; Lua::Push(L, phase);
const string mode;
const string phase; if (!Lua::SafeCall(out, L, 2, 1)) {
const bool force_create; out.printerr("Failed Lua call to is_meta_phase\n");
get_tile_fn * const get_tile; return false;
init_ctx_fn * const init_ctx; }
blueprint_processor(const string &mode, const string &phase,
bool force_create, get_tile_fn *get_tile, return lua_toboolean(L, -1);
init_ctx_fn *init_ctx) }
: mode(mode), phase(phase), force_create(force_create),
get_tile(get_tile), init_ctx(init_ctx) { }
};
static void write_minimal(ofstream &ofile, const blueprint_options &opts, static void write_minimal(ofstream &ofile, const blueprint_options &opts,
const bp_volume &mapdata) { const bp_volume &mapdata) {
@ -1052,8 +1218,8 @@ static void write_pretty(ofstream &ofile, const blueprint_options &opts,
} }
} }
static string get_modeline(const blueprint_options &opts, const string &mode, static string get_modeline(color_ostream &out, const blueprint_options &opts,
const string &phase) { const string &mode, const string &phase) {
std::ostringstream modeline; std::ostringstream modeline;
modeline << "#" << mode << " label(" << phase << ")"; modeline << "#" << mode << " label(" << phase << ")";
if (opts.playback_start.x > 0) { if (opts.playback_start.x > 0) {
@ -1064,6 +1230,8 @@ static string get_modeline(const blueprint_options &opts, const string &mode,
} }
modeline << ")"; modeline << ")";
} }
if (is_meta_phase(out, opts, phase))
modeline << " hidden()";
return modeline.str(); return modeline.str();
} }
@ -1072,15 +1240,15 @@ static bool write_blueprint(color_ostream &out,
std::map<string, ofstream*> &output_files, std::map<string, ofstream*> &output_files,
const blueprint_options &opts, const blueprint_options &opts,
const blueprint_processor &processor, const blueprint_processor &processor,
bool pretty) { bool pretty, int32_t ordinal) {
string fname; string fname;
if (!get_filename(fname, out, opts, processor.phase)) if (!get_filename(fname, out, opts, processor.phase, ordinal))
return false; return false;
if (!output_files.count(fname)) if (!output_files.count(fname))
output_files[fname] = new ofstream(fname, ofstream::trunc); output_files[fname] = new ofstream(fname, ofstream::trunc);
ofstream &ofile = *output_files[fname]; ofstream &ofile = *output_files[fname];
ofile << get_modeline(opts, processor.mode, processor.phase) << endl; ofile << get_modeline(out, opts, processor.mode, processor.phase) << endl;
if (pretty) if (pretty)
write_pretty(ofile, opts, processor.mapdata); write_pretty(ofile, opts, processor.mapdata);
@ -1090,6 +1258,28 @@ static bool write_blueprint(color_ostream &out,
return true; return true;
} }
static void write_meta_blueprint(color_ostream &out,
std::map<string, ofstream*> &output_files,
const blueprint_options &opts,
const std::vector<string> & meta_phases,
int32_t ordinal) {
string fname;
get_filename(fname, out, opts, meta_phases.front(), ordinal);
ofstream &ofile = *output_files[fname];
ofile << "#meta label(";
for (string phase : meta_phases) {
ofile << phase;
if (phase != meta_phases.back())
ofile << "_";
}
ofile << ")" << endl;
for (string phase : meta_phases) {
ofile << "/" << phase << endl;
}
}
static void ensure_building(const df::coord &pos, tile_context &ctx) { static void ensure_building(const df::coord &pos, tile_context &ctx) {
if (ctx.b) if (ctx.b)
return; return;
@ -1108,7 +1298,7 @@ static void add_processor(vector<blueprint_processor> &processors,
static bool do_transform(color_ostream &out, static bool do_transform(color_ostream &out,
const df::coord &start, const df::coord &end, const df::coord &start, const df::coord &end,
const blueprint_options &opts, blueprint_options opts, // copy so we can munge it
vector<string> &filenames) { vector<string> &filenames) {
// empty map instances to pass to emplace() below // empty map instances to pass to emplace() below
static const bp_area EMPTY_AREA; static const bp_area EMPTY_AREA;
@ -1132,6 +1322,8 @@ static bool do_transform(color_ostream &out,
smooth_get_tile_fn); smooth_get_tile_fn);
add_processor(processors, opts, "dig", "carve", opts.carve, add_processor(processors, opts, "dig", "carve", opts.carve,
opts.engrave ? get_tile_carve : get_tile_carve_minimal); opts.engrave ? get_tile_carve : get_tile_carve_minimal);
add_processor(processors, opts, "build", "construct", opts.construct,
get_tile_construct, ensure_building);
add_processor(processors, opts, "build", "build", opts.build, add_processor(processors, opts, "build", "build", opts.build,
get_tile_build, ensure_building); get_tile_build, ensure_building);
add_processor(processors, opts, "place", "place", opts.place, add_processor(processors, opts, "place", "place", opts.place,
@ -1139,6 +1331,8 @@ static bool do_transform(color_ostream &out,
add_processor(processors, opts, "zone", "zone", opts.zone, get_tile_zone); add_processor(processors, opts, "zone", "zone", opts.zone, get_tile_zone);
add_processor(processors, opts, "query", "query", opts.query, add_processor(processors, opts, "query", "query", opts.query,
get_tile_query, ensure_building); get_tile_query, ensure_building);
add_processor(processors, opts, "query", "rooms", opts.rooms,
get_tile_rooms, ensure_building);
if (processors.empty()) { if (processors.empty()) {
out.printerr("no phases requested! nothing to do!\n"); out.printerr("no phases requested! nothing to do!\n");
@ -1157,6 +1351,7 @@ static bool do_transform(color_ostream &out,
tile_context ctx; tile_context ctx;
ctx.pretty = pretty; ctx.pretty = pretty;
for (blueprint_processor &processor : processors) { for (blueprint_processor &processor : processors) {
ctx.processor = &processor;
if (processor.init_ctx) if (processor.init_ctx)
processor.init_ctx(pos, ctx); processor.init_ctx(pos, ctx);
const char *tile_str = processor.get_tile(pos, ctx); const char *tile_str = processor.get_tile(pos, ctx);
@ -1176,13 +1371,36 @@ static bool do_transform(color_ostream &out,
} }
} }
std::vector<string> meta_phases;
for (blueprint_processor &processor : processors) {
if (processor.mapdata.empty() && !processor.force_create)
continue;
if (is_meta_phase(out, opts, processor.phase))
meta_phases.push_back(processor.phase);
}
if (meta_phases.size() <= 1)
opts.nometa = true;
bool in_meta = false;
int32_t ordinal = 0;
std::map<string, ofstream*> output_files; std::map<string, ofstream*> output_files;
for (blueprint_processor &processor : processors) { for (blueprint_processor &processor : processors) {
if (processor.mapdata.empty() && !processor.force_create) if (processor.mapdata.empty() && !processor.force_create)
continue; continue;
if (!write_blueprint(out, output_files, opts, processor, pretty)) bool meta_phase = is_meta_phase(out, opts, processor.phase);
if (!in_meta)
++ordinal;
if (in_meta && !meta_phase) {
write_meta_blueprint(out, output_files, opts, meta_phases, ordinal);
++ordinal;
}
in_meta = meta_phase;
if (!write_blueprint(out, output_files, opts, processor, pretty,
ordinal))
break; break;
} }
if (in_meta)
write_meta_blueprint(out, output_files, opts, meta_phases, ordinal);
for (auto &it : output_files) { for (auto &it : output_files) {
filenames.push_back(it.first); filenames.push_back(it.first);

@ -6,13 +6,23 @@ local utils = require('utils')
local valid_phase_list = { local valid_phase_list = {
'dig', 'dig',
'carve', 'carve',
'construct',
'build', 'build',
'place', 'place',
'zone', 'zone',
'query', 'query',
'rooms',
} }
valid_phases = utils.invert(valid_phase_list) valid_phases = utils.invert(valid_phase_list)
local meta_phase_list = {
'build',
'place',
'zone',
'query',
}
meta_phases = utils.invert(meta_phase_list)
local valid_formats_list = { local valid_formats_list = {
'minimal', 'minimal',
'pretty', 'pretty',
@ -21,6 +31,7 @@ valid_formats = utils.invert(valid_formats_list)
local valid_split_strategies_list = { local valid_split_strategies_list = {
'none', 'none',
'group',
'phase', 'phase',
} }
valid_split_strategies = utils.invert(valid_split_strategies_list) valid_split_strategies = utils.invert(valid_split_strategies_list)
@ -121,6 +132,7 @@ local function process_args(opts, args)
{'f', 'format', hasArg=true, {'f', 'format', hasArg=true,
handler=function(optarg) parse_format(opts, optarg) end}, handler=function(optarg) parse_format(opts, optarg) end},
{'h', 'help', handler=function() opts.help = true end}, {'h', 'help', handler=function() opts.help = true end},
{nil, 'nometa', handler=function() opts.nometa = true end},
{'s', 'playback-start', hasArg=true, {'s', 'playback-start', hasArg=true,
handler=function(optarg) parse_start(opts, optarg) end}, handler=function(optarg) parse_start(opts, optarg) end},
{nil, 'smooth', handler=function() opts.smooth = true end}, {nil, 'smooth', handler=function() opts.smooth = true end},
@ -132,6 +144,10 @@ local function process_args(opts, args)
return return
end end
if opts.split_strategy == 'phase' then
opts.nometa = true
end
return positionals return positionals
end end
@ -183,8 +199,13 @@ function parse_commandline(opts, ...)
parse_positionals(opts, positionals, depth and 4 or 3) parse_positionals(opts, positionals, depth and 4 or 3)
end end
function is_meta_phase(opts, phase)
-- this is called directly by cpp so ensure we return a boolean, not nil
return not opts.nometa and meta_phases[phase] or false
end
-- returns the name of the output file for the given context -- returns the name of the output file for the given context
function get_filename(opts, phase) function get_filename(opts, phase, ordinal)
local fullname = 'blueprints/' .. opts.name local fullname = 'blueprints/' .. opts.name
local _,_,basename = fullname:find('/([^/]+)/?$') local _,_,basename = fullname:find('/([^/]+)/?$')
if not basename then if not basename then
@ -194,11 +215,13 @@ function get_filename(opts, phase)
if fullname:endswith('/') then if fullname:endswith('/') then
fullname = fullname .. basename fullname = fullname .. basename
end end
if opts.split_strategy == 'phase' then if opts.split_strategy == 'none' then
return ('%s-%s.csv'):format(fullname, phase) return ('%s.csv'):format(fullname)
end
if is_meta_phase(opts, phase) then
phase = 'meta'
end end
-- no splitting return ('%s-%d-%s.csv'):format(fullname, ordinal, phase)
return ('%s.csv'):format(fullname)
end end
-- compatibility with old exported API. -- compatibility with old exported API.

@ -99,7 +99,7 @@ namespace DFHack {
DBG_DECLARE(overlay, log, DebugCategory::LINFO); DBG_DECLARE(overlay, log, DebugCategory::LINFO);
} }
static const std::string button_text = "[ DFHack Launcher ]"; static const std::string button_text = "[ DFHack ]";
static bool clicked = false; static bool clicked = false;
static bool handle_click() { static bool handle_click() {

@ -1 +1 @@
Subproject commit 937cf27f9b41301be6df0fe1d75d77b6f089f688 Subproject commit 0be5cc0eaf625029ef26392224db5c3d2822a63d

@ -20,6 +20,12 @@ function test.parse_gui_commandline()
b.parse_gui_commandline(opts, {'-h'}) b.parse_gui_commandline(opts, {'-h'})
expect.table_eq({help=true, format='minimal', split_strategy='none'}, opts) expect.table_eq({help=true, format='minimal', split_strategy='none'}, opts)
opts = {}
b.parse_gui_commandline(opts, {'--nometa'})
expect.table_eq({auto_phase=true, format='minimal', split_strategy='none',
name='blueprint', nometa=true},
opts)
opts = {} opts = {}
mock.patch(dfhack.maps, 'isValidTilePos', mock.func(true), mock.patch(dfhack.maps, 'isValidTilePos', mock.func(true),
function() function()
@ -95,7 +101,7 @@ function test.parse_gui_commandline()
opts = {} opts = {}
b.parse_gui_commandline(opts, {'--splitby', 'phase'}) b.parse_gui_commandline(opts, {'--splitby', 'phase'})
expect.table_eq({auto_phase=true, format='minimal', split_strategy='phase', expect.table_eq({auto_phase=true, format='minimal', split_strategy='phase',
name='blueprint'}, name='blueprint', nometa=true},
opts) opts)
expect.error_match('unknown split_strategy', expect.error_match('unknown split_strategy',
@ -235,16 +241,16 @@ end
function test.get_filename() function test.get_filename()
local opts = {name='a', split_strategy='none'} local opts = {name='a', split_strategy='none'}
expect.eq('blueprints/a.csv', b.get_filename(opts, 'dig')) expect.eq('blueprints/a.csv', b.get_filename(opts, 'dig', 1))
opts = {name='a/', split_strategy='none'} opts = {name='a/', split_strategy='none'}
expect.eq('blueprints/a/a.csv', b.get_filename(opts, 'dig')) expect.eq('blueprints/a/a.csv', b.get_filename(opts, 'dig', 1))
opts = {name='a', split_strategy='phase'} opts = {name='a', split_strategy='phase'}
expect.eq('blueprints/a-dig.csv', b.get_filename(opts, 'dig')) expect.eq('blueprints/a-1-dig.csv', b.get_filename(opts, 'dig', 1))
opts = {name='a/', split_strategy='phase'} opts = {name='a/', split_strategy='phase'}
expect.eq('blueprints/a/a-dig.csv', b.get_filename(opts, 'dig')) expect.eq('blueprints/a/a-5-dig.csv', b.get_filename(opts, 'dig', 5))
expect.error_match('could not parse basename', function() expect.error_match('could not parse basename', function()
b.get_filename({name='', split_strategy='none'}) b.get_filename({name='', split_strategy='none'})

@ -327,10 +327,12 @@ function test.end_to_end()
do_dig_phase(phases.dig, area, spec) do_dig_phase(phases.dig, area, spec)
if phases.smooth then do_dig_phase(phases.smooth, area, spec) end if phases.smooth then do_dig_phase(phases.smooth, area, spec) end
if phases.carve then do_dig_phase(phases.carve, area, spec) end if phases.carve then do_dig_phase(phases.carve, area, spec) end
if phases.construct then do_phase(phases.construct, area, spec) end
if phases.build then do_phase(phases.build, area, spec) end if phases.build then do_phase(phases.build, area, spec) end
if phases.place then do_phase(phases.place, area, spec) end if phases.place then do_phase(phases.place, area, spec) end
if phases.zone then do_phase(phases.zone, area, spec) end if phases.zone then do_phase(phases.zone, area, spec) end
if phases.query then do_phase(phases.query, area, spec) end if phases.query then do_phase(phases.query, area, spec) end
if phases.rooms then do_phase(phases.rooms, area, spec) end
-- run any extra commands, if defined by the blueprint spec -- run any extra commands, if defined by the blueprint spec
if spec.extra_fn then if spec.extra_fn then