Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1120 lines (865 sloc) 38.513 kb
"""Copyright (c) 2010-2012 David Rio Vierra
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE."""
from toolbasics import *
from select import SelectionOperation
from pymclevel.pocket import PocketWorld
CloneSettings = config.Settings("Clone")
CloneSettings.copyAir = CloneSettings("Copy Air", True)
CloneSettings.copyWater = CloneSettings("Copy Water", True)
CloneSettings.placeImmediately = CloneSettings("Place Immediately", True)
class CoordsInput(Widget):
is_gl_container = True
def __init__(self):
Widget.__init__(self)
self.nudgeButton = NudgeButton()
self.nudgeButton.nudge = self._nudge
self.xField = IntField(value=0)
self.yField = IntField(value=0)
self.zField = IntField(value=0)
for field in (self.xField, self.yField, self.zField):
field.change_action = self._coordsChanged
field.enter_passes = False
offsetCol = Column((self.xField, self.yField, self.zField))
nudgeOffsetRow = Row((offsetCol, self.nudgeButton))
self.add(nudgeOffsetRow)
self.shrink_wrap()
def getCoords(self):
return self.xField.value, self.yField.value, self.zField.value
def setCoords(self, coords):
x, y, z = coords
self.xField.text = str(x)
self.yField.text = str(y)
self.zField.text = str(z)
coords = property(getCoords, setCoords, None)
def _coordsChanged(self):
self.coordsChanged()
def coordsChanged(self):
# called when the inputs change. override or replace
pass
def _nudge(self, nudge):
self.nudge(nudge)
def nudge(self, nudge):
# nudge is a 3-tuple where one of the elements is -1 or 1, and the others are 0.
pass
class BlockCopyOperation(Operation):
def __init__(self, editor, sourceLevel, sourceBox, destLevel, destPoint, copyAir, copyWater):
self.editor = editor
self.sourceLevel = sourceLevel
self.sourceBox = sourceBox
self.destLevel = destLevel
self.destPoint = destPoint
self.copyAir = copyAir
self.copyWater = copyWater
self.sourceBox, self.destPoint = self.destLevel.adjustCopyParameters(self.sourceLevel, self.sourceBox, self.destPoint)
def dirtyBox(self):
return BoundingBox(self.destPoint, self.sourceBox.size)
def name(self):
return "Copy {0} blocks".format(self.sourceBox.volume)
def recordUndo(self):
self.undoSchematic = self.extractUndoSchematicFrom(self.destLevel, BoundingBox(self.destPoint, self.sourceBox.size))
def perform(self, recordUndo=True):
dirtyBox = self.dirtyBox()
sourceBox = self.sourceBox
destBox = BoundingBox(self.destPoint, sourceBox.size)
if recordUndo:
self.recordUndo()
blocksToCopy = None
if not (self.copyAir and self.copyWater):
blocksToCopy = range(256)
if not self.copyAir:
blocksToCopy.remove(0)
if not self.copyWater:
blocksToCopy.remove(8)
if not self.copyWater:
blocksToCopy.remove(9)
with setWindowCaption("Copying - "):
i = self.destLevel.copyBlocksFromIter(self.sourceLevel, self.sourceBox, self.destPoint, blocksToCopy, create=True)
showProgress("Copying {0:n} blocks...".format(self.sourceBox.volume), i)
def undo(self):
if self.undoSchematic:
self.destLevel.removeEntitiesInBox(BoundingBox(self.destPoint, self.sourceBox.size))
self.destLevel.removeTileEntitiesInBox(BoundingBox(self.destPoint, self.sourceBox.size))
with setWindowCaption("Undoing - "):
i = self.destLevel.copyBlocksFromIter(self.undoSchematic, BoundingBox((0, 0, 0), self.sourceBox.size), self.destPoint, create=True)
showProgress("Copying {0:n} blocks...".format(self.sourceBox.volume), i)
def bufferSize(self):
return self.undoSchematic.compressedSize()
class CloneOperation (Operation):
def __init__(self, editor, sourceLevel, sourceBox, originSourceBox, destLevel, destPoint, copyAir, copyWater, repeatCount):
self.blockCopyOps = []
dirtyBoxes = []
if repeatCount > 1: # clone tool only
delta = map(operator.sub, destPoint, editor.toolbar.tools[0].selectionBox().origin)
else:
delta = (0, 0, 0)
for i in range(repeatCount):
op = BlockCopyOperation(editor, sourceLevel, sourceBox, destLevel, destPoint, copyAir, copyWater)
dirty = op.dirtyBox()
# bounds check - xxx move to BoundingBox
if dirty.miny >= destLevel.Height or dirty.maxy < 0:
continue
if destLevel.Width != 0:
if dirty.minx >= destLevel.Width or dirty.maxx < 0:
continue
if dirty.minz >= destLevel.Length or dirty.maxz < 0:
continue
dirtyBoxes.append(dirty)
self.blockCopyOps.append(op)
destPoint = map(operator.add, delta, destPoint)
if len(dirtyBoxes):
def enclosingBox(dirtyBoxes):
minx = min(map(lambda x: x.minx, dirtyBoxes))
miny = min(map(lambda x: x.miny, dirtyBoxes))
minz = min(map(lambda x: x.minz, dirtyBoxes))
maxx = max(map(lambda x: x.maxx, dirtyBoxes))
maxy = max(map(lambda x: x.maxy, dirtyBoxes))
maxz = max(map(lambda x: x.maxz, dirtyBoxes))
origin = (minx, miny, minz)
maximum = (maxx, maxy, maxz)
return BoundingBox(origin, map(operator.sub, maximum, origin))
self._dirtyBox = enclosingBox(dirtyBoxes)
if repeatCount > 1 and self.selectOriginalAfterRepeat:
dirtyBoxes.append(originSourceBox)
dirty = enclosingBox(dirtyBoxes)
points = (dirty.origin, map(lambda x: x - 1, dirty.maximum))
self.selectionOps = [SelectionOperation(editor.selectionTool, points)]
else:
self._dirtyBox = None
self.selectionOps = []
selectOriginalAfterRepeat = True
def dirtyBox(self):
return self._dirtyBox
def perform(self, recordUndo=True):
if recordUndo:
[i.recordUndo() for i in self.blockCopyOps]
[i.perform(False) for i in self.blockCopyOps]
[i.perform(recordUndo) for i in self.selectionOps]
def undo(self):
[i.undo() for i in self.blockCopyOps]
[i.undo() for i in self.selectionOps]
class CloneToolPanel(Panel):
useOffsetInput = True
def transformEnable(self):
return not isinstance(self.tool.level, MCInfdevOldLevel)
def __init__(self, tool):
Panel.__init__(self)
self.tool = tool
rotateRow = Row((
Label(config.config.get("Keys", "Rotate").upper()), Button("Rotate", width=80, action=tool.rotate, enable=self.transformEnable),
))
rollRow = Row((
Label(config.config.get("Keys", "Roll").upper()), Button("Roll", width=80, action=tool.roll, enable=self.transformEnable),
))
flipRow = Row((
Label(config.config.get("Keys", "Flip").upper()), Button("Flip", width=80, action=tool.flip, enable=self.transformEnable),
))
mirrorRow = Row((
Label(config.config.get("Keys", "Mirror").upper()), Button("Mirror", width=80, action=tool.mirror, enable=self.transformEnable),
))
alignRow = Row((
CheckBox(ref=AttrRef(self.tool, 'chunkAlign')), Label("Chunk Align")
))
# headerLabel = Label("Clone Offset")
if self.useOffsetInput:
self.offsetInput = CoordsInput()
self.offsetInput.coordsChanged = tool.offsetChanged
self.offsetInput.nudgeButton.bg_color = tool.color
self.offsetInput.nudge = tool.nudge
else:
self.nudgeButton = NudgeButton()
self.nudgeButton.bg_color = tool.color
self.nudgeButton.nudge = tool.nudge
repeatField = IntField(ref=AttrRef(tool, 'repeatCount'))
repeatField.min = 1
repeatField.max = 50
repeatRow = Row((
Label("Repeat"), repeatField
))
self.repeatField = repeatField
scaleField = FloatField(ref=AttrRef(tool, 'scaleFactor'))
scaleField.min = 0.125
scaleField.max = 8
dv = scaleField.decrease_value
iv = scaleField.increase_value
def scaleFieldDecrease():
if scaleField.value > 1 / 8.0 and scaleField.value <= 1.0:
scaleField.value *= 0.5
else:
dv()
def scaleFieldIncrease():
if scaleField.value < 1.0:
scaleField.value *= 2.0
else:
iv()
scaleField.decrease_value = scaleFieldDecrease
scaleField.increase_value = scaleFieldIncrease
scaleRow = Row((
Label("Scale Factor"), scaleField
))
self.scaleField = scaleField
self.copyAirCheckBox = CheckBox(ref=AttrRef(self.tool, "copyAir"))
self.copyAirLabel = Label("Copy Air")
self.copyAirLabel.mouse_down = self.copyAirCheckBox.mouse_down
self.copyAirLabel.tooltipText = "Shortcut: ALT-1"
self.copyAirCheckBox.tooltipText = self.copyAirLabel.tooltipText
copyAirRow = Row((self.copyAirCheckBox, self.copyAirLabel))
self.copyWaterCheckBox = CheckBox(ref=AttrRef(self.tool, "copyWater"))
self.copyWaterLabel = Label("Copy Water")
self.copyWaterLabel.mouse_down = self.copyWaterCheckBox.mouse_down
self.copyWaterLabel.tooltipText = "Shortcut: ALT-2"
self.copyWaterCheckBox.tooltipText = self.copyWaterLabel.tooltipText
copyWaterRow = Row((self.copyWaterCheckBox, self.copyWaterLabel))
self.performButton = Button("Clone", width=100, align="c")
self.performButton.tooltipText = "Shortcut: ENTER"
self.performButton.action = tool.confirm
self.performButton.enable = lambda: (tool.destPoint is not None)
if self.useOffsetInput:
col = Column((rotateRow, rollRow, flipRow, mirrorRow, alignRow, self.offsetInput, repeatRow, scaleRow, copyAirRow, copyWaterRow, self.performButton))
else:
col = Column((rotateRow, rollRow, flipRow, mirrorRow, alignRow, self.nudgeButton, copyAirRow, copyWaterRow, self.performButton))
self.add(col)
self.anchor = "lwh"
self.shrink_wrap()
class CloneToolOptions(ToolOptions):
def __init__(self, tool):
Panel.__init__(self)
self.tool = tool
self.autoPlaceCheckBox = CheckBox(ref=AttrRef(tool, "placeImmediately"))
self.autoPlaceLabel = Label("Place Immediately")
self.autoPlaceLabel.mouse_down = self.autoPlaceCheckBox.mouse_down
tooltipText = "When the clone tool is chosen, place the clone at the selection right away."
self.autoPlaceLabel.tooltipText = self.autoPlaceCheckBox.tooltipText = tooltipText
row = Row((self.autoPlaceCheckBox, self.autoPlaceLabel))
col = Column((Label("Clone Options"), row, Button("OK", action=self.dismiss)))
self.add(col)
self.shrink_wrap()
class CloneTool(EditorTool):
surfaceBuild = True
toolIconName = "clone"
tooltipText = "Clone\nRight-click for options"
level = None
repeatCount = 1
_scaleFactor = 1.0
_chunkAlign = False
@property
def scaleFactor(self):
return self._scaleFactor
@scaleFactor.setter
def scaleFactor(self, val):
self.rescaleLevel(val)
self._scaleFactor = val
@property
def chunkAlign(self):
return self._chunkAlign
@chunkAlign.setter
def chunkAlign(self, value):
self._chunkAlign = value
self.alignDestPoint()
def alignDestPoint(self):
if self.destPoint is not None:
x, y, z = self.destPoint
self.destPoint = (x >> 4) << 4, y, (z >> 4) << 4
placeImmediately = CloneSettings.placeImmediately.configProperty()
panelClass = CloneToolPanel
# color = (0.89, 0.65, 0.35, 0.33)
color = (0.3, 1.0, 0.3, 0.19)
def __init__(self, *args):
self.rotation = 0
EditorTool.__init__(self, *args)
self.previewRenderer = None
self.panel = None
self.optionsPanel = CloneToolOptions(self)
self.destPoint = None
@property
def statusText(self):
if self.destPoint == None:
return "Click to set this item down."
if self.draggingFace is not None:
return "Mousewheel to move along the third axis. Hold SHIFT to only move along one axis."
return "Click and drag to reposition the item. Double-click to pick it up. Click Clone or press ENTER to confirm."
def quickNudge(self, nudge):
return map(int.__mul__, nudge, self.selectionBox().size)
copyAir = CloneSettings.copyAir.configProperty()
copyWater = CloneSettings.copyWater.configProperty()
def nudge(self, nudge):
if self.destPoint is None:
if self.selectionBox() is None:
return
self.destPoint = self.selectionBox().origin
if self.chunkAlign:
x, y, z = nudge
nudge = x << 4, y, z << 4
if key.get_mods() & KMOD_SHIFT:
nudge = self.quickNudge(nudge)
# self.panel.performButton.enabled = True
self.destPoint = map(lambda a, b: a + b, self.destPoint, nudge)
self.updateOffsets()
def selectionChanged(self):
if self.selectionBox() is not None:
self.updateSchematic()
self.updateOffsets()
def updateOffsets(self):
if self.panel and self.panel.useOffsetInput and self.destPoint is not None:
self.panel.offsetInput.setCoords(map(operator.sub, self.destPoint, self.selectionBox().origin))
def offsetChanged(self):
if self.panel:
if not self.panel.useOffsetInput:
return
box = self.selectionBox()
if box is None:
return
delta = self.panel.offsetInput.coords
self.destPoint = map(operator.add, box.origin, delta)
def toolEnabled(self):
return not (self.selectionBox() is None)
def cancel(self):
self.discardPreviewer()
if self.panel:
self.panel.parent.remove(self.panel)
self.panel = None
self.destPoint = None
self.level = None
self.originalLevel = None
def toolReselected(self):
self.pickUp()
def safeToolDistance(self):
return sqrt(sum([self.level.Width ** 2, self.level.Height ** 2, self.level.Length ** 2]))
def toolSelected(self):
box = self.selectionBox()
if box is None:
self.editor.toolbar.selectTool(-1)
return
if box.volume > self.maxBlocks:
self.editor.mouseLookOff()
alert("Selection exceeds {0:n} blocks. Increase the block buffer setting and try again.".format(self.maxBlocks))
self.editor.toolbar.selectTool(-1)
return
self.rotation = 0
self.repeatCount = 1
self._scaleFactor = 1.0
if self.placeImmediately:
self.destPoint = list(box.origin)
else:
self.destPoint = None
self.updateSchematic()
self.cloneCameraDistance = max(self.cloneCameraDistance, self.safeToolDistance())
self.showPanel()
cloneCameraDistance = 0
@property
def cameraDistance(self):
return self.cloneCameraDistance
@alertException
def rescaleLevel(self, factor):
# if self.level.cloneToolScaleFactor == newFactor:
# return
# oldfactor = self.level.cloneToolScaleFactor
# factor = newFactor / oldfactor
if factor == 1:
self.level = self.originalLevel
self.setupPreview()
return
oldshape = self.originalLevel.Blocks.shape
blocks = self.originalLevel.Blocks
data = self.originalLevel.Data
if factor < 1.0:
roundedShape = map(lambda x: int(int(x * factor) / factor), oldshape)
roundedSlices = map(lambda x: slice(0, x), roundedShape)
blocks = blocks[roundedSlices]
data = data[roundedSlices]
else:
roundedShape = oldshape
newshape = map(lambda x: int(x * factor), oldshape)
xyzshape = newshape[0], newshape[2], newshape[1]
newlevel = MCSchematic(xyzshape, mats=self.editor.level.materials)
srcgrid = mgrid[0:roundedShape[0]:1.0 / factor, 0:roundedShape[1]:1.0 / factor, 0:roundedShape[2]:1.0 / factor].astype('uint')
dstgrid = mgrid[0:newshape[0], 0:newshape[1], 0:newshape[2]].astype('uint')
srcgrid = srcgrid[map(slice, dstgrid.shape)]
dstgrid = dstgrid[map(slice, srcgrid.shape)]
def copyArray(dest, src):
dest[dstgrid[0], dstgrid[1], dstgrid[2]] = src[srcgrid[0], srcgrid[1], srcgrid[2]]
copyArray(newlevel.Blocks, blocks)
copyArray(newlevel.Data, data)
self.level = newlevel
self.setupPreview()
#
# """
# use array broadcasting to fill in the extra dimensions with copies of the
# existing ones, then later change the shape to "fold" the extras back
# into the original three
# """
# # if factor > 1.0:
# sourceSlice = slice(0, 1)
# destSlice = slice(None)
#
# # if factor < 1.0:
#
# destfactor = factor
# srcfactor = 1
# if factor < 1.0:
# destfactor = 1.0
# srcfactor = 1.0 / factor
#
# intershape = newshape[0]/destfactor, destfactor, newshape[1]/destfactor, destfactor, newshape[2]/destfactor, destfactor
# srcshape = roundedShape[0]/srcfactor, srcfactor, roundedShape[1]/srcfactor, srcfactor, roundedShape[2]/srcfactor, srcfactor
#
# newlevel = MCSchematic(xyzshape)
#
# def copyArray(dest, src):
# dest.shape = intershape
# src.shape = srcshape
#
# dest[:, destSlice, :, destSlice, :, destSlice] = src[:, sourceSlice, :, sourceSlice, :, sourceSlice]
# dest.shape = newshape
# src.shape = roundedShape
#
# copyArray(newlevel.Blocks, blocks)
# copyArray(newlevel.Data, data)
#
# newlevel.cloneToolScaleFactor = newFactor
#
@alertException
def updateSchematic(self):
# extract blocks
with setWindowCaption("COPYING - "):
self.editor.freezeStatus("Copying to clone buffer...")
box = self.selectionBox()
self.level = self.editor.level.extractSchematic(box)
self.originalLevel = self.level
# self.level.cloneToolScaleFactor = 1.0
self.rescaleLevel(self.scaleFactor)
self.setupPreview()
def showPanel(self):
if self.panel:
self.panel.set_parent(None)
self.panel = self.panelClass(self)
# self.panel.performButton.enabled = False
self.panel.centery = self.editor.centery
self.panel.left = self.editor.left
self.editor.add(self.panel)
def setupPreview(self, alpha=1.0):
self.discardPreviewer()
if self.level:
self.previewRenderer = PreviewRenderer(self.level, alpha)
self.previewRenderer.position = self.editor.renderer.position
self.editor.addWorker(self.previewRenderer)
else:
self.editor.toolbar.selectTool(-1)
@property
def canRotateLevel(self):
return not isinstance(self.level, (MCInfdevOldLevel, PocketWorld))
def rotatedSelectionSize(self):
if self.canRotateLevel:
sizes = self.level.Blocks.shape
return sizes[0], sizes[2], sizes[1]
else:
return self.level.size
# ===========================================================================
# def getSelectionRanges(self):
# return self.editor.selectionTool.selectionBox()
#
# ===========================================================================
def getBlockAt(self):
return None # use level's blockAt
def getReticleOrigin(self):
# returns a new origin for the current selection, where the old origin is at the new selection's center.
pos, direction = self.editor.blockFaceUnderCursor
lev = self.editor.level
size = self.rotatedSelectionSize()
if not size:
return
if size[1] >= self.editor.level.Height:
direction = (0, 1, 0) # always use the upward face whenever we're splicing full-height pieces, to avoid "jitter"
# print size; raise SystemExit
if any(direction) and pos[1] >= 0:
x, y, z = map(lambda p, s, d: p - s / 2 + s * d / 2 + (d > 0), pos, size, direction)
else:
x, y, z = map(lambda p, s: p - s / 2, pos, size)
if self.chunkAlign:
x = x & ~0xf
z = z & ~0xf
sy = size[1]
if sy > lev.Height: # don't snap really tall stuff to the height
return x, y, z
if y + sy > lev.Height:
y = lev.Height - sy
if y < 0:
y = 0
if not isinstance(lev, MCInfdevOldLevel):
sx = size[0]
if x + sx > lev.Width:
x = lev.Width - sx
if x < 0:
x = 0
sz = size[2]
if z + sz > lev.Length:
z = lev.Length - sz
if z < 0:
z = 0
return x, y, z
def getReticleBox(self):
pos = self.getReticleOrigin()
sizes = self.rotatedSelectionSize()
if None is sizes:
return
return BoundingBox(pos, sizes)
def getDestBox(self):
selectionSize = self.rotatedSelectionSize()
return BoundingBox(self.destPoint, selectionSize)
def drawTerrainReticle(self):
if self.level is None:
return
if self.destPoint != None:
destPoint = self.destPoint
if self.draggingFace is not None:
# debugDrawPoint()
destPoint = self.draggingOrigin()
self.drawTerrainPreview(destPoint)
else:
self.drawTerrainPreview(self.getReticleBox().origin)
draggingColor = (0.77, 1.0, 0.55, 0.05)
def drawToolReticle(self):
if self.level is None:
return
glPolygonOffset(DepthOffset.CloneMarkers, DepthOffset.CloneMarkers)
color = self.color
if self.destPoint is not None:
alpha = sin((self.editor.frameStartTime.second + self.editor.frameStartTime.microsecond / 1000000) * 3.14) * 0.124 + 0.25
color = (self.color[0], self.color[1], self.color[2], 0.06)
box = self.getDestBox()
if self.draggingFace is not None:
box.origin = self.draggingOrigin()
guideBox = BoundingBox(box)
for i in range(3):
if i == self.draggingFace >> 1:
continue
guideBox._origin[i] -= 1000
guideBox._size[i] += 2000
color = self.draggingColor
glColor(1.0, 1.0, 1.0, 0.33)
with gl.glEnable(GL_BLEND, GL_TEXTURE_2D, GL_DEPTH_TEST):
self.editor.sixteenBlockTex.bind()
drawFace(guideBox, self.draggingFace ^ 1)
else:
box = self.getReticleBox()
if box is None:
return
self.drawRepeatedCube(box, color)
glPolygonOffset(DepthOffset.CloneReticle, DepthOffset.CloneReticle)
if self.destPoint:
box = self.getDestBox()
if self.draggingFace is not None:
face = self.draggingFace
box.origin = self.draggingOrigin()
face, point = self.boxFaceUnderCursor(box)
if face is not None:
glEnable(GL_BLEND)
glDisable(GL_DEPTH_TEST)
glColor(*self.color)
drawFace(box, face)
glDisable(GL_BLEND)
glEnable(GL_DEPTH_TEST)
def drawRepeatedCube(self, box, color):
# draw several cubes according to the repeat count
# it's not really sensible to repeat a crane because the origin point is literally out of this world.
delta = map(operator.sub, box.origin, self.selectionBox().origin)
for i in range(self.repeatCount):
self.editor.drawConstructionCube(box, color)
box.origin = map(operator.add, box.origin, delta)
def sourceLevel(self):
return self.level
@alertException
def rotate(self, amount=1):
if self.canRotateLevel:
self.rotation += amount
self.rotation &= 0x3
for i in range(amount & 0x3):
self.level.rotateLeft()
self.previewRenderer.level = self.level
@alertException
def roll(self, amount=1):
if self.canRotateLevel:
for i in range(amount & 0x3):
self.level.roll()
self.previewRenderer.level = self.level
@alertException
def flip(self, amount=1):
if self.canRotateLevel:
for i in range(amount & 0x1):
self.level.flipVertical()
self.previewRenderer.level = self.level
@alertException
def mirror(self):
if self.canRotateLevel:
yaw = int(self.editor.mainViewport.yaw) % 360
if (yaw >= 45 and yaw < 135) or (yaw > 225 and yaw <= 315):
self.level.flipEastWest()
else:
self.level.flipNorthSouth()
self.previewRenderer.level = self.level
def option1(self):
self.copyAir = not self.copyAir
def option2(self):
self.copyWater = not self.copyWater
draggingFace = None
draggingStartPoint = None
def draggingOrigin(self):
p = self._draggingOrigin()
return p
def _draggingOrigin(self):
dragPos = map(int, map(floor, self.positionOnDraggingPlane()))
delta = map(lambda s, e: e - int(floor(s)), self.draggingStartPoint, dragPos)
if key.get_mods() & KMOD_SHIFT:
ad = map(abs, delta)
midx = ad.index(max(ad))
d = [0, 0, 0]
d[midx] = delta[midx]
dragY = self.draggingFace >> 1
d[dragY] = delta[dragY]
delta = d
p = map(lambda a, b: a + b, delta, self.destPoint)
if self.chunkAlign:
p = [i // 16 * 16 for i in p]
return p
def positionOnDraggingPlane(self):
pos = self.editor.mainViewport.cameraPosition
dim = self.draggingFace >> 1
# if key.get_mods() & KMOD_SHIFT:
# dim = self.findBestTrackingPlane(self.draggingFace)
#
distance = self.draggingStartPoint[dim] - pos[dim]
distance += self.draggingY
mouseVector = self.editor.mainViewport.mouseVector
scale = distance / (mouseVector[dim] or 1)
point = map(lambda a, b: a * scale + b, mouseVector, pos)
return point
draggingY = 0
@alertException
def mouseDown(self, evt, pos, direction):
box = self.selectionBox()
if not box:
return
self.draggingY = 0
if self.destPoint is not None:
if evt.num_clicks == 2:
self.pickUp()
return
face, point = self.boxFaceUnderCursor(self.getDestBox())
if face is not None:
self.draggingFace = face
self.draggingStartPoint = point
else:
self.destPoint = self.getReticleOrigin()
if self.panel and self.panel.useOffsetInput:
self.panel.offsetInput.setCoords(map(operator.sub, self.destPoint, box.origin))
print "Destination: ", self.destPoint
@alertException
def mouseUp(self, evt, pos, direction):
if self.draggingFace is not None:
self.destPoint = self.draggingOrigin()
self.draggingFace = None
self.draggingStartPoint = None
def increaseToolReach(self):
if self.draggingFace is not None:
d = (1, -1)[self.draggingFace & 1]
if self.draggingFace >> 1 != 1: # xxxxx y
d = -d
self.draggingY += d
x, y, z = self.editor.mainViewport.cameraPosition
pos = [x, y, z]
pos[self.draggingFace >> 1] += d
self.editor.mainViewport.cameraPosition = tuple(pos)
else:
self.cloneCameraDistance = self.editor._incrementReach(self.cloneCameraDistance)
return True
def decreaseToolReach(self):
if self.draggingFace is not None:
d = (1, -1)[self.draggingFace & 1]
if self.draggingFace >> 1 != 1: # xxxxx y
d = -d
self.draggingY -= d
x, y, z = self.editor.mainViewport.cameraPosition
pos = [x, y, z]
pos[self.draggingFace >> 1] -= d
self.editor.mainViewport.cameraPosition = tuple(pos)
else:
self.cloneCameraDistance = self.editor._decrementReach(self.cloneCameraDistance)
return True
def resetToolReach(self):
if self.draggingFace is not None:
x, y, z = self.editor.mainViewport.cameraPosition
pos = [x, y, z]
pos[self.draggingFace >> 1] += (1, -1)[self.draggingFace & 1] * -self.draggingY
self.editor.mainViewport.cameraPosition = tuple(pos)
self.draggingY = 0
else:
self.cloneCameraDistance = max(self.editor.defaultCameraToolDistance, self.safeToolDistance())
return True
def pickUp(self):
if self.destPoint == None:
return
box = self.selectionBox()
# pick up the object. reset the tool distance to the object's distance from the camera
d = map(lambda a, b, c: abs(a - b - c / 2), self.editor.mainViewport.cameraPosition, self.destPoint, box.size)
self.cloneCameraDistance = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2])
self.destPoint = None
# self.panel.performButton.enabled = False
print "Picked up"
@alertException
def confirm(self):
destPoint = self.destPoint
if destPoint is None:
return
sourceLevel = self.sourceLevel()
sourceBox = sourceLevel.bounds
destLevel = self.editor.level
destVolume = BoundingBox(destPoint, sourceBox.size).volume
op = CloneOperation(editor=self.editor,
sourceLevel=sourceLevel,
sourceBox=sourceBox,
originSourceBox=self.selectionBox(),
destLevel=destLevel,
destPoint=self.destPoint,
copyAir=self.copyAir,
copyWater=self.copyWater,
repeatCount=self.repeatCount)
self.editor.toolbar.selectTool(-1) # deselect tool so that the clone tool's selection change doesn't update its schematic
with setWindowCaption("COPYING - "):
self.editor.freezeStatus("Copying %0.1f million blocks" % (float(destVolume) / 1048576.,))
self.performWithRetry(op)
self.editor.addUnsavedEdit()
self.editor.addOperation(op)
dirtyBox = op.dirtyBox()
if dirtyBox:
self.editor.invalidateBox(dirtyBox)
self.editor.renderer.invalidateChunkMarkers()
self.editor.currentOperation = None
self.destPoint = None
self.level = None
def discardPreviewer(self):
if self.previewRenderer is None:
return
self.previewRenderer.stopWork()
self.previewRenderer.discardAllChunks()
self.editor.removeWorker(self.previewRenderer)
self.previewRenderer = None
class ConstructionToolPanel (CloneToolPanel):
useOffsetInput = False
class ConstructionTool(CloneTool):
surfaceBuild = True
toolIconName = "crane"
tooltipText = "Import"
panelClass = ConstructionToolPanel
def toolEnabled(self):
return True
def selectionChanged(self):
pass
def updateSchematic(self):
pass
def quickNudge(self, nudge):
return map(lambda x: x * 8, nudge)
def __init__(self, *args):
CloneTool.__init__(self, *args)
self.level = None
self.optionsPanel = None
@property
def statusText(self):
if self.destPoint == None:
return "Click to set this item down."
return "Click and drag to reposition the item. Double-click to pick it up. Click Import or press ENTER to confirm."
def showPanel(self):
CloneTool.showPanel(self)
self.panel.performButton.text = "Import"
def toolReselected(self):
self.toolSelected()
# def cancel(self):
# print "Cancelled Clone"
# self.level = None
# super(ConstructionTool, self).cancel(self)
#
def createTestBoard(self, anyBlock=True):
if anyBlock:
allBlocks = [self.editor.level.materials[a, b] for a in range(256) for b in range(16)]
blockWidth = 64
else:
allBlocks = self.editor.level.materials.allBlocks
blockWidth = 16
blockCount = len(allBlocks)
width = blockWidth * 3 + 1
rows = blockCount // blockWidth + 1
length = rows * 3 + 1
height = 3
schematic = MCSchematic((width, height, length), mats=self.editor.level.materials)
schematic.Blocks[:, :, 0] = 1
for i, block in enumerate(allBlocks):
col = (i % blockWidth) * 3 + 1
row = (i // blockWidth) * 3
schematic.Blocks[col:col + 2, row:row + 2, 2] = block.ID
schematic.Data[col:col + 2, row:row + 2, 2] = block.blockData
return schematic
def toolSelected(self):
self.editor.mouseLookOff()
mods = key.get_mods()
if mods & KMOD_ALT and mods & KMOD_SHIFT:
self.loadLevel(self.createTestBoard())
return
self.editor.mouseLookOff()
clipFilename = mcplatform.askOpenFile(title='Import a schematic or level...', schematics=True)
# xxx mouthful
if clipFilename:
self.loadSchematic(clipFilename)
print "Canceled"
if self.level is None:
print "No level selected."
self.editor.toolbar.selectTool(-1)
# CloneTool.toolSelected(self)
originalLevelSize = (0, 0, 0)
def loadSchematic(self, filename):
""" actually loads a schematic or a level """
try:
level = fromFile(filename)
self.loadLevel(level)
except Exception, e:
print u"Unable to import file {0} : {1}".format(filename, e)
traceback.print_exc()
if filename:
# self.editor.toolbar.selectTool(-1)
alert(u"I don't know how to import this file: {0}.\n\nError: {1!r}".format(os.path.basename(filename), e))
return
@alertException
def loadLevel(self, level):
if level:
self.level = level
self.repeatCount = 1
self.destPoint = None
self.editor.currentTool = self # because save window triggers loseFocus, which triggers tool.cancel... hmmmmmm
self.cloneCameraDistance = self.safeToolDistance()
self.chunkAlign = isinstance(self.level, MCInfdevOldLevel) and all(b % 16 == 0 for b in self.level.bounds.size)
self.setupPreview()
self.originalLevelSize = (self.level.Width, self.level.Height, self.level.Length)
self.showPanel()
return
def selectionSize(self):
if not self.level:
return None
return self.originalLevelSize
def selectionBox(self):
if not self.level:
return None
return BoundingBox((0, 0, 0), self.selectionSize())
def sourceLevel(self):
return self.level
def mouseDown(self, evt, pos, direction):
# x,y,z = pos
box = self.selectionBox()
if not box:
return
CloneTool.mouseDown(self, evt, pos, direction)
Jump to Line
Something went wrong with that request. Please try again.