458 lines
10 KiB
Python
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
|
|
|