2 import sys
, time
, math
, argparse
, os
, os
.path
4 from overviewer_core
import world
8 parser
= argparse
.ArgumentParser(description
='Anvil 2d map generator.')
9 parser
.add_argument("--grid", type=int, metavar
="BLOCKS", dest
="gridStep",
10 help="Grid marks step")
11 parser
.add_argument("-f", "--filter", metavar
="NAME", type=str, choices
=chunkFilters
.keys(), default
="all",
12 help="Chunk filtering method")
13 parser
.add_argument("-F", "--blockfilter", metavar
="NAME", type=str, choices
=blockFilters
.keys(),
14 help="Block filtering method")
15 parser
.add_argument("-g", "--arg", metavar
="VALUE", type=int, action
="append", default
= [],
16 help="Chunk selection parameters")
17 parser
.add_argument("-i", "--in", dest
="worldPath", metavar
="PATH", type=str,
18 help="Source world directory")
19 parser
.add_argument("-o", "--out", dest
="destPath", metavar
="PATH", type=str, default
=".",
20 help="Output directory")
21 parser
.add_argument("-r", "--region", metavar
="NAME", type=str, default
="overworld",
23 parser
.add_argument("-v", "--verbose", action
="store_true", default
=False,
24 help="Print some progress info")
25 args
= parser
.parse_args()
28 srcWorld
= world
.World(args
.worldPath
)
29 srcRegion
= srcWorld
.get_regionset(args
.region
)
31 sys
.exit("Error opening world")
33 if not os
.access(args
.destPath
, os
.W_OK
):
34 sys
.exit("Unable to write at %s" % args
.destPath
)
37 print "Preparing chunks"
40 if not args
.blockfilter
:
42 myChunks
= [i
for i
in srcRegion
.iterate_chunks() if chunkFilters
[args
.filter](i
[0], i
[1], i
[2], args
.arg
)]
44 blockFilter
= blockFilters
[args
.blockfilter
]
45 myChunks
= [i
for i
in srcRegion
.iterate_chunks() if chunkBlockFilter(i
[0], i
[1], blockFilter
, args
.arg
)]
47 # find out northernmost, easternmost... chunks
48 xChunks
, yChunks
, mtimes
= zip(*myChunks
)
49 limitMinX
, limitMinY
, limitMaxX
, limitMaxY
= min(xChunks
), min(yChunks
), max(xChunks
), max(yChunks
)
50 # resulting image dimensions
51 dstSize
= limitMinX
* -16 + limitMaxX
* 16 + 16, limitMinY
* -16 + limitMaxY
* 16 + 16
52 dstXoffset
, dstYoffset
= 0, 0 # helper to move map around
55 print "Image size: %sx%s" % dstSize
58 dstImg
= PIL
.Image
.new("RGB", dstSize
)
59 dstDraw
= PIL
.ImageDraw
.Draw(dstImg
)
61 axisStep
= args
.gridStep
65 for chx
, chy
, chmtime
in myChunks
:
67 if args
.verbose
and not cnt
% 10:
69 msg
= "Chunk: \t%s,%s \t%s/%s \tETA %d/%ds " % \
70 (chx
, chy
, cnt
, len(myChunks
), time
.time() - start
, (time
.time() - start
) * len(myChunks
) / cnt
)
71 sys
.stdout
.write("\r"+cleaner
+"\r"+msg
)
72 cleaner
= " "*len(msg
.expandtabs())
73 sys
.stdout
.flush() # if you do not like this, better turn off whole of verbosity
75 chunk
= srcRegion
.get_chunk(chx
, chy
)
76 for y
,x
in chunkWalker():
78 realX
= (chx
- limitMinX
) * 16 + x
+ dstXoffset
79 realY
= (chy
- limitMinY
) * 16 + y
+ dstYoffset
83 if blockFilter
and not blockFilter(mapX
, mapY
, args
.arg
): continue
85 #if chx, x, chy, y == 0,0,0,0: # mark world bellybutton # NOTE: slowdown
86 #dstDraw.point((realX, realY), fill=(0, 0, 0))
90 #if (not mapX % axisStep or not mapY % axisStep) and not x+y & 3: # dotted axis
91 # TODO: draw in separate pass to save some time
92 if axisStep
and (((mapX
+3) % axisStep
< 7 and not mapY
% axisStep
) or ((mapY
+3) % axisStep
< 7 and not mapX
% axisStep
)): # cross axis
93 dstDraw
.point((realX
, realY
), fill
=(255 - 30*abs((mapX
+mapY
+3)%axisStep
-3) , 0, 0))
96 # core job, find highest block and draw it
100 for bid
, prevbid
, height
in FallThru(chunk
, x
, y
):
101 if bid
in blockId2color
:
107 color
, layer
= getColor(bid
, height
)
109 #if realX == 0 or realY == 0: color = 0, 0, 0
111 color
, garb
= getColor(watermode
, height
)
112 dstDraw
.point((realX
, realY
), fill
=color
)
113 # TODO: for debuging block ids
114 if color
== (238, 130, 238):
115 print "\nunknown block id at %d/%d/%d : %s" % (realX
, height
, realY
, bid
)
118 sys
.stdout
.write("\r"+cleaner
+"\rChunk: %d done in %ds\n"% (cnt
, time
.time() - start
))
121 dstImg
.save(args
.destPath
, "PNG")
125 "all": lambda x
,y
,t
,a
: True,
126 "square": lambda x
,y
,t
,a
: abs(x
) < a
[0] and abs(y
) < a
[0],
127 "rect": lambda x
,y
,t
,a
: abs(x
) < a
[0] and abs(y
) < a
[1],
128 "circle": lambda x
,y
,t
,a
: math
.sqrt(x
**2 + y
**2) < a
[0],
129 "test": lambda x
,y
,t
,a
: x
== 0 and y
== 0,
130 "single": lambda x
,y
,t
,a
: x
== a
[0] and y
== a
[1],
133 # special case, args will be block filter function
134 chunkBlockFilter
= lambda x
,y
,f
,a
: \
136 f(x
*16+16,y
*16, a
) or \
137 f(x
*16,y
*16+16, a
) or \
138 f(x
*16+16,y
*16+16, a
) # anything within given block filter
142 "circle": lambda x
,y
,a
: math
.sqrt(x
**2 + y
**2) < a
[0],
145 planks
= (160, 128, 0) # carpentry is kinda ubiquitous
148 # value tuple - color triple, layer, height tint flag
150 1: ((128, 128, 128), 0, True), # stone
151 2: ((51, 102, 0), 0, True), # grass
152 3: ((102, 51, 0), 0, True), # dirt
153 4: ((160, 160, 160), 10, True), # cobblestone
154 5: (planks
, 10, True), # planks
155 7: ((0, 0, 0), 0, True), # bedrock
156 8: ((0, 77, 153), 0, True), # water
157 9: ((0, 77, 153), 0, True), # water
158 10: ((255, 51, 51), 0, True), # lavavv
159 11: ((255, 51, 51), 0, True), # lava
160 12: ((160, 160, 80), 0, True), # sand
161 13: ((153, 51, 153), 0, True), # gravel
162 14: ((255, 255, 153), 2, False), # gold ore
163 15: ((218, 188, 166), 2, False), # iron ore
164 16: ((0, 0, 0), 2, False), # coal ore
165 17: ((102, 51, 0), 3, False), # wood
166 18: ((0, 153, 0), 3, False), # leaves
167 21: ((0, 0, 153), 2, False), # lazuli ore
168 22: ((0, 0, 204), 10, False), # lazuli block
169 24: ((204, 204, 0), 10, True), # sandstone
170 27: ((200, 200, 212), 10, True), # power rail
171 28: ((200, 200, 212), 10, True), # switch rail
172 35: ((224, 224, 224), 10, True), # wool
173 42: ((200, 200, 212), 10, False), # iron block
174 45: ((153, 0, 0), 10, True), # red bricks
175 60: ((255, 153, 51), 10, True), # irigated dirt
176 66: ((200, 200, 212), 10, True), # rails
177 73: ((153, 0, 0), 0, False), # redstone ore
178 74: ((153, 0, 0), 0, False), # redstone ore
179 78: ((204, 204, 204), 0, True), # snow
180 79: ((153, 204, 255), 0, True), # ice
181 80: ((204, 204, 204), 0, True), # snow
182 91: ((204, 102, 0), 3, False), # pumpkin
183 103: ((128, 255, 0), 3, False), # melon
184 126: (planks
, 10, True), # wood slab
185 128: ((204, 204, 0), 10, True), # sandstone stairs
186 134: (planks
, 10, True), # wood
187 135: (planks
, 10, True), # wood
188 136: (planks
, 10, True), # wood
190 unknown
= (238, 130, 238), 20, False
193 def getColor(bid
, height
):
194 """Decide pixel color for block of given type in given height"""
195 color
, layer
, tint
= blockId2color
.get(bid
, unknown
)
196 #color = blockId2color.get(bid, unknown)
198 heightPart
= (height
) / 64.0
199 #return tuple([int(i * heightPart) for i in color]), layer
200 return (int(color
[0] * heightPart
), int(color
[1] * heightPart
), int(color
[2] * heightPart
)), layer
205 def FallThru(chunk
, x
, y
):
206 """Iterate blocks top to bottom"""
207 height
= len(chunk
["Sections"]) * 16
209 for sect
in reversed(chunk
["Sections"]):
210 for layer
in reversed(sect
["Blocks"]):
212 yield cur
, prev
, height
218 """Iterate 16x16 matrix"""
224 if __name__
== "__main__":