This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
mcedit/filters/CreateBusses.py
2012-10-25 00:09:56 -10:00

458 lines
10 KiB
Python

# Feel free to modify and use this filter however you wish. If you do,
# please give credit to SethBling.
# http://youtube.com/SethBling
from numpy import sign
displayName = "Create Busses"
def perform(level, box, options):
level.markDirtyBox(box)
bus = BusCreator(level, box, options)
bus.getTerminals()
bus.getGuides()
bus.pickAllPaths()
bus.createAllBusses()
HorizDirs = [
(1, 0, 0),
(-1, 0, 0),
(0, 0, 1),
(0, 0, -1),
]
Down = (0, -1, 0)
Up = (0, 1, 0)
def getHorizDir((x1, y1, z1), (x2, y2, z2)):
if abs(x2-x1) > abs(z2-z1):
return (sign(x2-x1), 0, 0)
else:
if(z2 == z1):
return (1, 0, 0)
else:
return (0, 0, sign(z2-z1))
def getSecondaryDir((x1, y1, z1), (x2, y2, z2)):
if abs(x2-x1) > abs(z2-z1):
return (0, 0, sign(z2-z1))
else:
return (sign(x2-x1), 0, 0)
def leftOf((dx1, dy1, dz1), (dx2, dy2, dz2)):
return dx1 == dz2 or dz1 == dx2 * -1
def rotateRight((dx, dy, dz)):
return ((-dz, dy, dx))
def rotateLeft((dx, dy, dz)):
return ((dz, dy, -dx))
def allAdjacentSamePlane(dir, secondaryDir):
right = rotateRight(dir)
left = rotateLeft(dir)
back = rotateRight(right)
if leftOf(secondaryDir, dir):
return (
dir,
left,
right,
getDir(dir, Up),
getDir(dir, Down),
getDir(left, Up),
getDir(right, Up),
getDir(left, Down),
getDir(right, Down),
back,
getDir(back, Up),
getDir(back, Down),
)
else:
return (
dir,
right,
left,
getDir(dir, Up),
getDir(dir, Down),
getDir(right, Up),
getDir(left, Up),
getDir(right, Down),
getDir(left, Down),
back,
getDir(back, Up),
getDir(back, Down),
)
def allAdjacentUp(dir, secondaryDir):
right = rotateRight(dir)
left = rotateLeft(dir)
back = rotateRight(right)
if leftOf(secondaryDir, dir):
return (
getDir(dir, Up),
getDir(left, Up),
getDir(right, Up),
getDir(back, Up),
dir,
left,
right,
back,
getDir(dir, Down),
getDir(left, Down),
getDir(right, Down),
getDir(back, Down),
)
else:
return (
getDir(dir, Up),
getDir(right, Up),
getDir(left, Up),
getDir(back, Up),
dir,
right,
left,
back,
getDir(dir, Down),
getDir(right, Down),
getDir(left, Down),
getDir(back, Down),
)
def allAdjacentDown(dir, secondaryDir):
right = rotateRight(dir)
left = rotateLeft(dir)
back = rotateRight(right)
if leftOf(secondaryDir, dir):
return (
getDir(dir, Down),
getDir(left, Down),
getDir(right, Down),
getDir(back, Down),
dir,
left,
right,
back,
getDir(dir, Up),
getDir(left, Up),
getDir(right, Up),
getDir(back, Up),
)
else:
return (
getDir(dir, Down),
getDir(right, Down),
getDir(left, Down),
getDir(back, Down),
dir,
right,
left,
back,
getDir(dir, Up),
getDir(right, Up),
getDir(left, Up),
getDir(back, Up),
)
def getDir((x, y, z), (dx, dy, dz)):
return (x+dx, y+dy, z+dz)
def dist((x1, y1, z1), (x2, y2, z2)):
return abs(x2-x1) + abs(y2-y1) + abs(z2-z1)
def above((x1, y1, z1), (x2, y2, z2)):
return y1 > y2
def below((x1, y1, z1), (x2, y2, z2)):
return y1 < y2
def insideBox(box, (x, y, z)):
return x >= box.minx and x < box.maxx and y >= box.miny and y < box.maxy and z >= box.minz and z < box.maxz
Colors = {
0: "white",
1: "orange",
2: "magenta",
3: "light blue",
4: "yellow",
5: "lime green",
6: "pink",
7: "gray",
8: "light gray",
9: "cyan",
10:"purple",
11:"blue",
12:"brown",
13:"green",
14:"red",
15:"black",
}
class BusCreator:
starts = {}
ends = {}
guides = {}
path = {}
def __init__(self, level, box, options):
self.level = level
self.box = box
self.options = options
def getTerminals(self):
for x in xrange(self.box.minx, self.box.maxx):
for y in xrange(self.box.miny, self.box.maxy):
for z in xrange(self.box.minz, self.box.maxz):
(color, start) = self.isTerminal((x, y, z))
if color != None and start != None:
if start:
if color in self.starts:
raise Exception("Duplicate starting point for " + Colors[color] + " bus")
self.starts[color] = (x, y, z)
else:
if color in self.ends:
raise Exception("Duplicate ending point for " + Colors[color] + " bus")
self.ends[color] = (x, y, z)
def getGuides(self):
for x in xrange(self.box.minx, self.box.maxx):
for y in xrange(self.box.miny, self.box.maxy):
for z in xrange(self.box.minz, self.box.maxz):
pos = (x, y, z)
if self.getBlockAt(pos) == 35:
color = self.getBlockDataAt(pos)
if color not in self.starts or color not in self.ends:
continue
if color not in self.guides:
self.guides[color] = []
rs = getDir(pos, Up)
if rs == self.starts[color] or rs == self.ends[color]:
continue
self.guides[color].append(rs)
def isTerminal(self, (x, y, z)):
pos = (x, y, z)
for dir in HorizDirs:
otherPos = getDir(pos, dir)
towards = self.repeaterPointingTowards(pos, otherPos)
away = self.repeaterPointingAway(pos, otherPos)
if not (away or towards): # it's not a repeater pointing towards or away
continue
if self.getBlockAt(otherPos) != 55: # the other block isn't redstone
continue
if self.getBlockAt(getDir(pos, Down)) != 35: # it's not sitting on wool
continue
if self.getBlockAt(getDir(otherPos, Down)) != 35: # the other block isn't sitting on wool
continue
data = self.getBlockDataAt(getDir(pos, Down))
if self.getBlockDataAt(getDir(otherPos, Down)) != data: # the wool colors don't match
continue
return (data, towards)
return (None, None)
def pickAllPaths(self):
for color in range(0, 16):
if color in self.starts and color in self.ends:
self.pickPath(color)
def pickPath(self, color):
self.path[color] = ()
currentPos = self.starts[color]
while True:
minDist = None
minGuide = None
for guide in self.guides[color]:
guideDist = dist(currentPos, guide)
if minDist == None or guideDist < minDist:
minDist = guideDist
minGuide = guide
if dist(currentPos, self.ends[color]) == 1:
return
if minGuide == None:
return
self.path[color] = self.path[color] + (minGuide,)
currentPos = minGuide
self.guides[color].remove(minGuide)
def createAllBusses(self):
for color in range(0, 16):
if color in self.path:
self.connectDots(color)
def connectDots(self, color):
prevGuide = None
self.power = 1
for guide in self.path[color]:
if prevGuide != None:
self.createConnection(prevGuide, guide, color)
prevGuide = guide
def createConnection(self, pos1, pos2, color):
currentPos = pos1
while currentPos != pos2:
self.power = self.power + 1
hdir = getHorizDir(currentPos, pos2)
secondaryDir = getSecondaryDir(currentPos, pos2)
if above(currentPos, pos2):
dirs = allAdjacentDown(hdir, secondaryDir)
elif below(currentPos, pos2):
dirs = allAdjacentUp(hdir, secondaryDir)
else:
dirs = allAdjacentSamePlane(hdir, secondaryDir)
if self.power == 1:
restrictions = 2
elif self.power == 15:
restrictions = 1
else:
restrictions = 0
placed = False
for dir in dirs:
pos = getDir(currentPos, dir)
if self.canPlaceRedstone(pos, currentPos, pos2, restrictions):
if self.power == 15:
self.placeRepeater(pos, dir, color)
self.power = 0
else:
self.placeRedstone(pos, color)
currentPos = pos
placed = True
break
if not placed:
#raise Exception("Algorithm failed to create bus for " + Colors[color] + " wire.")
return
def canPlaceRedstone(self, pos, fromPos, destinationPos, restrictions):
if restrictions == 1 and above(pos, fromPos): #repeater
return False
if restrictions == 2 and below(pos, fromPos): #just after repeater
return False
if restrictions == 2 and not self.repeaterPointingTowards(fromPos, pos): #just after repeater
return False
if above(pos, fromPos) and self.getBlockAt(getDir(getDir(pos, Down), Down)) == 55:
return False
if below(pos, fromPos) and self.getBlockAt(getDir(pos, Up)) != 0:
return False
if getDir(pos, Down) == destinationPos:
return False
if pos == destinationPos:
return True
if self.getBlockAt(pos) != 0:
return False
if self.getBlockAt(getDir(pos, Down)) != 0:
return False
if not insideBox(self.box, pos):
return False
for dir in allAdjacentSamePlane((1, 0, 0), (0, 0, 0)):
testPos = getDir(pos, dir)
if testPos == fromPos or testPos == getDir(fromPos, Down):
continue
if testPos == destinationPos or testPos == getDir(destinationPos, Down):
continue
blockid = self.getBlockAt(testPos)
if blockid != 0:
return False
return True
def placeRedstone(self, pos, color):
self.setBlockAt(pos, 55) #redstone
self.setBlockAt(getDir(pos, Down), 35, color) # wool
def placeRepeater(self, pos, (dx, dy, dz), color):
if dz == -1:
self.setBlockAt(pos, 93, 0) #north
elif dx == 1:
self.setBlockAt(pos, 93, 1) #east
elif dz == 1:
self.setBlockAt(pos, 93, 2) #south
elif dx == -1:
self.setBlockAt(pos, 93, 3) #west
self.setBlockAt(getDir(pos, Down), 35, color) #wool
def getBlockAt(self, (x, y, z)):
return self.level.blockAt(x, y, z)
def getBlockDataAt(self, (x, y, z)):
return self.level.blockDataAt(x, y, z)
def setBlockAt(self, (x, y, z), id, dmg = 0):
self.level.setBlockAt(x, y, z, id)
self.level.setBlockDataAt(x, y, z, dmg)
def repeaterPointingTowards(self, (x1, y1, z1), (x2, y2, z2)):
blockid = self.getBlockAt((x1, y1, z1))
if blockid != 93 and blockid != 94:
return False
direction = self.level.blockDataAt(x1, y1, z1) % 4
if direction == 0 and z2 - z1 == -1:
return True
if direction == 1 and x2 - x1 == 1:
return True
if direction == 2 and z2 - z1 == 1:
return True
if direction == 3 and x2 - x1 == -1:
return True
return False
def repeaterPointingAway(self, (x1, y1, z1), (x2, y2, z2)):
blockid = self.getBlockAt((x1, y1, z1))
if blockid != 93 and blockid != 94:
return False
direction = self.level.blockDataAt(x1, y1, z1) % 4
if direction == 0 and z2 - z1 == 1:
return True
if direction == 1 and x2 - x1 == -1:
return True
if direction == 2 and z2 - z1 == -1:
return True
if direction == 3 and x2 - x1 == 1:
return True
return False