548 lines
18 KiB
Python
548 lines
18 KiB
Python
"""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 OpenGL import GL
|
|
import numpy
|
|
from albow import TableView, TableColumn, Label, Button, Column, CheckBox, AttrRef, Row, ask, alert
|
|
import config
|
|
from editortools.editortool import EditorTool
|
|
from editortools.tooloptions import ToolOptions
|
|
from glbackground import Panel
|
|
from glutils import DisplayList
|
|
from mceutils import loadPNGTexture, alertException, drawTerrainCuttingWire, drawCube
|
|
from operation import Operation
|
|
import pymclevel
|
|
from pymclevel.box import BoundingBox, FloatBox
|
|
import logging
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
class PlayerMoveOperation(Operation):
|
|
undoPos = None
|
|
|
|
def __init__(self, tool, pos, player="Player", yp=(None, None)):
|
|
super(PlayerMoveOperation, self).__init__(tool.editor, tool.editor.level)
|
|
self.tool = tool
|
|
self.pos = pos
|
|
self.player = player
|
|
self.yp = yp
|
|
|
|
def perform(self, recordUndo=True):
|
|
try:
|
|
level = self.tool.editor.level
|
|
try:
|
|
self.undoPos = level.getPlayerPosition(self.player)
|
|
self.undoDim = level.getPlayerDimension(self.player)
|
|
self.undoYP = level.getPlayerOrientation(self.player)
|
|
except Exception, e:
|
|
log.info("Couldn't get player position! ({0!r})".format(e))
|
|
|
|
yaw, pitch = self.yp
|
|
if yaw is not None and pitch is not None:
|
|
level.setPlayerOrientation((yaw, pitch), self.player)
|
|
level.setPlayerPosition(self.pos, self.player)
|
|
level.setPlayerDimension(level.dimNo, self.player)
|
|
self.tool.markerList.invalidate()
|
|
|
|
except pymclevel.PlayerNotFound, e:
|
|
print "Player move failed: ", e
|
|
|
|
def undo(self):
|
|
if not (self.undoPos is None):
|
|
level = self.tool.editor.level
|
|
level.setPlayerPosition(self.undoPos, self.player)
|
|
level.setPlayerDimension(self.undoDim, self.player)
|
|
level.setPlayerOrientation(self.undoYP, self.player)
|
|
self.tool.markerList.invalidate()
|
|
|
|
def bufferSize(self):
|
|
return 20
|
|
|
|
|
|
class SpawnPositionInvalid(Exception):
|
|
pass
|
|
|
|
|
|
def okayAt63(level, pos):
|
|
"""blocks 63 or 64 must be occupied"""
|
|
return level.blockAt(pos[0], 63, pos[2]) != 0 or level.blockAt(pos[0], 64, pos[2]) != 0
|
|
|
|
|
|
def okayAboveSpawn(level, pos):
|
|
"""3 blocks above spawn must be open"""
|
|
return not any([level.blockAt(pos[0], pos[1] + i, pos[2]) for i in range(1, 4)])
|
|
|
|
|
|
def positionValid(level, pos):
|
|
try:
|
|
return okayAt63(level, pos) and okayAboveSpawn(level, pos)
|
|
except EnvironmentError:
|
|
return False
|
|
|
|
|
|
class PlayerSpawnMoveOperation(Operation):
|
|
undoPos = None
|
|
|
|
def __init__(self, tool, pos):
|
|
self.tool, self.pos = tool, pos
|
|
|
|
def perform(self, recordUndo=True):
|
|
level = self.tool.editor.level
|
|
if isinstance(level, pymclevel.MCInfdevOldLevel):
|
|
if not positionValid(level, self.pos):
|
|
if SpawnSettings.spawnProtection.get():
|
|
raise SpawnPositionInvalid("You cannot have two air blocks at Y=63 and Y=64 in your spawn point's column. Additionally, you cannot have a solid block in the three blocks above your spawn point. It's weird, I know.")
|
|
|
|
self.undoPos = level.playerSpawnPosition()
|
|
level.setPlayerSpawnPosition(self.pos)
|
|
self.tool.markerList.invalidate()
|
|
|
|
def undo(self):
|
|
if self.undoPos is not None:
|
|
level = self.tool.editor.level
|
|
level.setPlayerSpawnPosition(self.undoPos)
|
|
self.tool.markerList.invalidate()
|
|
|
|
|
|
class PlayerPositionPanel(Panel):
|
|
def __init__(self, tool):
|
|
Panel.__init__(self)
|
|
self.tool = tool
|
|
level = tool.editor.level
|
|
if hasattr(level, 'players'):
|
|
players = level.players or ["[No players]"]
|
|
else:
|
|
players = ["Player"]
|
|
self.players = players
|
|
tableview = TableView(columns=[
|
|
TableColumn("Player Name", 200),
|
|
])
|
|
tableview.index = 0
|
|
tableview.num_rows = lambda: len(players)
|
|
tableview.row_data = lambda i: (players[i],)
|
|
tableview.row_is_selected = lambda x: x == tableview.index
|
|
tableview.zebra_color = (0, 0, 0, 48)
|
|
|
|
def selectTableRow(i, evt):
|
|
tableview.index = i
|
|
|
|
tableview.click_row = selectTableRow
|
|
self.table = tableview
|
|
l = Label("Player: ")
|
|
col = [l, tableview]
|
|
|
|
gotoButton = Button("Goto Player", action=self.tool.gotoPlayer)
|
|
gotoCameraButton = Button("Goto Player's View", action=self.tool.gotoPlayerCamera)
|
|
moveButton = Button("Move Player", action=self.tool.movePlayer)
|
|
moveToCameraButton = Button("Align Player to Camera", action=self.tool.movePlayerToCamera)
|
|
col.extend([gotoButton, gotoCameraButton, moveButton, moveToCameraButton])
|
|
|
|
col = Column(col)
|
|
self.add(col)
|
|
self.shrink_wrap()
|
|
|
|
@property
|
|
def selectedPlayer(self):
|
|
return self.players[self.table.index]
|
|
|
|
|
|
class PlayerPositionTool(EditorTool):
|
|
surfaceBuild = True
|
|
toolIconName = "player"
|
|
tooltipText = "Move Player"
|
|
movingPlayer = None
|
|
|
|
def reloadTextures(self):
|
|
self.charTex = loadPNGTexture('char.png')
|
|
|
|
@alertException
|
|
def movePlayer(self):
|
|
self.movingPlayer = self.panel.selectedPlayer
|
|
|
|
@alertException
|
|
def movePlayerToCamera(self):
|
|
player = self.panel.selectedPlayer
|
|
pos = self.editor.mainViewport.cameraPosition
|
|
y = self.editor.mainViewport.yaw
|
|
p = self.editor.mainViewport.pitch
|
|
d = self.editor.level.dimNo
|
|
|
|
op = PlayerMoveOperation(self, pos, player, (y, p))
|
|
self.movingPlayer = None
|
|
op.perform()
|
|
self.editor.addOperation(op)
|
|
self.editor.addUnsavedEdit()
|
|
|
|
def gotoPlayerCamera(self):
|
|
player = self.panel.selectedPlayer
|
|
try:
|
|
pos = self.editor.level.getPlayerPosition(player)
|
|
y, p = self.editor.level.getPlayerOrientation(player)
|
|
self.editor.gotoDimension(self.editor.level.getPlayerDimension(player))
|
|
|
|
self.editor.mainViewport.cameraPosition = pos
|
|
self.editor.mainViewport.yaw = y
|
|
self.editor.mainViewport.pitch = p
|
|
self.editor.mainViewport.stopMoving()
|
|
self.editor.mainViewport.invalidate()
|
|
except pymclevel.PlayerNotFound:
|
|
pass
|
|
|
|
def gotoPlayer(self):
|
|
player = self.panel.selectedPlayer
|
|
|
|
try:
|
|
if self.editor.mainViewport.pitch < 0:
|
|
self.editor.mainViewport.pitch = -self.editor.mainViewport.pitch
|
|
self.editor.mainViewport.cameraVector = self.editor.mainViewport._cameraVector()
|
|
cv = self.editor.mainViewport.cameraVector
|
|
|
|
pos = self.editor.level.getPlayerPosition(player)
|
|
pos = map(lambda p, c: p - c * 5, pos, cv)
|
|
self.editor.gotoDimension(self.editor.level.getPlayerDimension(player))
|
|
|
|
self.editor.mainViewport.cameraPosition = pos
|
|
self.editor.mainViewport.stopMoving()
|
|
except pymclevel.PlayerNotFound:
|
|
pass
|
|
|
|
def __init__(self, *args):
|
|
EditorTool.__init__(self, *args)
|
|
self.reloadTextures()
|
|
|
|
textureVertices = numpy.array(
|
|
(
|
|
24, 16,
|
|
24, 8,
|
|
32, 8,
|
|
32, 16,
|
|
|
|
8, 16,
|
|
8, 8,
|
|
16, 8,
|
|
16, 16,
|
|
|
|
24, 0,
|
|
16, 0,
|
|
16, 8,
|
|
24, 8,
|
|
|
|
16, 0,
|
|
16, 8,
|
|
8, 8,
|
|
8, 0,
|
|
|
|
8, 8,
|
|
0, 8,
|
|
0, 16,
|
|
8, 16,
|
|
|
|
16, 16,
|
|
24, 16,
|
|
24, 8,
|
|
16, 8,
|
|
|
|
), dtype='f4')
|
|
|
|
textureVertices.shape = (24, 2)
|
|
|
|
textureVertices *= 4
|
|
textureVertices[:, 1] *= 2
|
|
|
|
self.texVerts = textureVertices
|
|
|
|
self.markerList = DisplayList()
|
|
|
|
panel = None
|
|
|
|
def showPanel(self):
|
|
if not self.panel:
|
|
self.panel = PlayerPositionPanel(self)
|
|
|
|
self.panel.left = self.editor.left
|
|
self.panel.centery = self.editor.centery
|
|
|
|
self.editor.add(self.panel)
|
|
|
|
def hidePanel(self):
|
|
if self.panel and self.panel.parent:
|
|
self.panel.parent.remove(self.panel)
|
|
self.panel = None
|
|
|
|
def drawToolReticle(self):
|
|
if self.movingPlayer is None:
|
|
return
|
|
|
|
pos, direction = self.editor.blockFaceUnderCursor
|
|
pos = (pos[0], pos[1] + 2, pos[2])
|
|
|
|
x, y, z = pos
|
|
|
|
#x,y,z=map(lambda p,d: p+d, pos, direction)
|
|
GL.glEnable(GL.GL_BLEND)
|
|
GL.glColor(1.0, 1.0, 1.0, 0.5)
|
|
self.drawCharacterHead(x + 0.5, y + 0.75, z + 0.5)
|
|
GL.glDisable(GL.GL_BLEND)
|
|
|
|
GL.glEnable(GL.GL_DEPTH_TEST)
|
|
self.drawCharacterHead(x + 0.5, y + 0.75, z + 0.5)
|
|
drawTerrainCuttingWire(BoundingBox((x, y, z), (1, 1, 1)))
|
|
drawTerrainCuttingWire(BoundingBox((x, y - 1, z), (1, 1, 1)))
|
|
#drawTerrainCuttingWire( BoundingBox((x,y-2,z), (1,1,1)) )
|
|
GL.glDisable(GL.GL_DEPTH_TEST)
|
|
|
|
markerLevel = None
|
|
|
|
def drawToolMarkers(self):
|
|
if self.markerLevel != self.editor.level:
|
|
self.markerList.invalidate()
|
|
self.markerLevel = self.editor.level
|
|
self.markerList.call(self._drawToolMarkers)
|
|
|
|
def _drawToolMarkers(self):
|
|
GL.glColor(1.0, 1.0, 1.0, 0.5)
|
|
|
|
GL.glEnable(GL.GL_DEPTH_TEST)
|
|
GL.glMatrixMode(GL.GL_MODELVIEW)
|
|
|
|
for player in self.editor.level.players:
|
|
try:
|
|
pos = self.editor.level.getPlayerPosition(player)
|
|
yaw, pitch = self.editor.level.getPlayerOrientation(player)
|
|
dim = self.editor.level.getPlayerDimension(player)
|
|
if dim != self.editor.level.dimNo:
|
|
continue
|
|
x, y, z = pos
|
|
GL.glPushMatrix()
|
|
GL.glTranslate(x, y, z)
|
|
GL.glRotate(-yaw, 0, 1, 0)
|
|
GL.glRotate(pitch, 1, 0, 0)
|
|
GL.glColor(1, 1, 1, 1)
|
|
self.drawCharacterHead(0, 0, 0)
|
|
GL.glPopMatrix()
|
|
#GL.glEnable(GL.GL_BLEND)
|
|
drawTerrainCuttingWire(FloatBox((x - .5, y - .5, z - .5), (1, 1, 1)),
|
|
c0=(0.3, 0.9, 0.7, 1.0),
|
|
c1=(0, 0, 0, 0),
|
|
)
|
|
|
|
#GL.glDisable(GL.GL_BLEND)
|
|
|
|
except Exception, e:
|
|
print repr(e)
|
|
continue
|
|
|
|
GL.glDisable(GL.GL_DEPTH_TEST)
|
|
|
|
def drawCharacterHead(self, x, y, z):
|
|
GL.glEnable(GL.GL_CULL_FACE)
|
|
origin = (x - 0.25, y - 0.25, z - 0.25)
|
|
size = (0.5, 0.5, 0.5)
|
|
box = FloatBox(origin, size)
|
|
|
|
drawCube(box,
|
|
texture=self.charTex, textureVertices=self.texVerts)
|
|
GL.glDisable(GL.GL_CULL_FACE)
|
|
|
|
@property
|
|
def statusText(self):
|
|
if not self.panel:
|
|
return ""
|
|
player = self.panel.selectedPlayer
|
|
if player == "Player":
|
|
return "Click to move the player"
|
|
|
|
return "Click to move the player \"{0}\"".format(player)
|
|
|
|
@alertException
|
|
def mouseDown(self, evt, pos, direction):
|
|
if self.movingPlayer is None:
|
|
return
|
|
|
|
pos = (pos[0] + 0.5, pos[1] + 2.75, pos[2] + 0.5)
|
|
|
|
op = PlayerMoveOperation(self, pos, self.movingPlayer)
|
|
self.movingPlayer = None
|
|
op.perform()
|
|
self.editor.addOperation(op)
|
|
self.editor.addUnsavedEdit()
|
|
|
|
def levelChanged(self):
|
|
self.markerList.invalidate()
|
|
|
|
@alertException
|
|
def toolSelected(self):
|
|
self.showPanel()
|
|
self.movingPlayer = None
|
|
|
|
@alertException
|
|
def toolReselected(self):
|
|
if self.panel:
|
|
self.gotoPlayer()
|
|
|
|
|
|
class PlayerSpawnPositionOptions(ToolOptions):
|
|
def __init__(self, tool):
|
|
Panel.__init__(self)
|
|
self.tool = tool
|
|
self.spawnProtectionCheckBox = CheckBox(ref=AttrRef(tool, "spawnProtection"))
|
|
self.spawnProtectionLabel = Label("Spawn Position Safety")
|
|
self.spawnProtectionLabel.mouse_down = self.spawnProtectionCheckBox.mouse_down
|
|
|
|
tooltipText = "Minecraft will randomly move your spawn point if you try to respawn in a column where there are no blocks at Y=63 and Y=64. Only uncheck this box if Minecraft is changed."
|
|
self.spawnProtectionLabel.tooltipText = self.spawnProtectionCheckBox.tooltipText = tooltipText
|
|
|
|
row = Row((self.spawnProtectionCheckBox, self.spawnProtectionLabel))
|
|
col = Column((Label("Spawn Point Options"), row, Button("OK", action=self.dismiss)))
|
|
|
|
self.add(col)
|
|
self.shrink_wrap()
|
|
|
|
SpawnSettings = config.Settings("Spawn")
|
|
SpawnSettings.spawnProtection = SpawnSettings("Spawn Protection", True)
|
|
|
|
|
|
class PlayerSpawnPositionTool(PlayerPositionTool):
|
|
surfaceBuild = True
|
|
toolIconName = "playerspawn"
|
|
tooltipText = "Move Spawn Point"
|
|
|
|
def __init__(self, *args):
|
|
PlayerPositionTool.__init__(self, *args)
|
|
self.optionsPanel = PlayerSpawnPositionOptions(self)
|
|
|
|
def toolEnabled(self):
|
|
return self.editor.level.dimNo == 0
|
|
|
|
def showPanel(self):
|
|
self.panel = Panel()
|
|
button = Button("Goto Spawn", action=self.gotoSpawn)
|
|
self.panel.add(button)
|
|
self.panel.shrink_wrap()
|
|
|
|
self.panel.left = self.editor.left
|
|
self.panel.centery = self.editor.centery
|
|
self.editor.add(self.panel)
|
|
|
|
def gotoSpawn(self):
|
|
cv = self.editor.mainViewport.cameraVector
|
|
|
|
pos = self.editor.level.playerSpawnPosition()
|
|
pos = map(lambda p, c: p - c * 5, pos, cv)
|
|
|
|
self.editor.mainViewport.cameraPosition = pos
|
|
self.editor.mainViewport.stopMoving()
|
|
|
|
@property
|
|
def statusText(self):
|
|
return "Click to set the spawn position."
|
|
|
|
spawnProtection = SpawnSettings.spawnProtection.configProperty()
|
|
|
|
def drawToolReticle(self):
|
|
pos, direction = self.editor.blockFaceUnderCursor
|
|
x, y, z = map(lambda p, d: p + d, pos, direction)
|
|
|
|
color = (1.0, 1.0, 1.0, 0.5)
|
|
if isinstance(self.editor.level, pymclevel.MCInfdevOldLevel) and self.spawnProtection:
|
|
if not positionValid(self.editor.level, (x, y, z)):
|
|
color = (1.0, 0.0, 0.0, 0.5)
|
|
|
|
GL.glColor(*color)
|
|
GL.glEnable(GL.GL_BLEND)
|
|
self.drawCage(x, y, z)
|
|
self.drawCharacterHead(x + 0.5, y + 0.5, z + 0.5)
|
|
GL.glDisable(GL.GL_BLEND)
|
|
|
|
GL.glEnable(GL.GL_DEPTH_TEST)
|
|
self.drawCage(x, y, z)
|
|
self.drawCharacterHead(x + 0.5, y + 0.5, z + 0.5)
|
|
color2 = map(lambda a: a * 0.4, color)
|
|
drawTerrainCuttingWire(BoundingBox((x, y, z), (1, 1, 1)), color2, color)
|
|
GL.glDisable(GL.GL_DEPTH_TEST)
|
|
|
|
def _drawToolMarkers(self):
|
|
x, y, z = self.editor.level.playerSpawnPosition()
|
|
GL.glColor(1.0, 1.0, 1.0, 1.0)
|
|
GL.glEnable(GL.GL_DEPTH_TEST)
|
|
self.drawCage(x, y, z)
|
|
self.drawCharacterHead(x + 0.5, y + 0.5 + 0.125 * numpy.sin(self.editor.frames * 0.05), z + 0.5)
|
|
GL.glDisable(GL.GL_DEPTH_TEST)
|
|
|
|
def drawCage(self, x, y, z):
|
|
cageTexVerts = numpy.array(pymclevel.MCInfdevOldLevel.materials.blockTextures[52, 0])
|
|
|
|
pixelScale = 0.5 if self.editor.level.materials.name in ("Pocket", "Alpha") else 1.0
|
|
texSize = 16 * pixelScale
|
|
cageTexVerts *= pixelScale
|
|
|
|
cageTexVerts = numpy.array([((tx, ty), (tx + texSize, ty), (tx + texSize, ty + texSize), (tx, ty + texSize)) for (tx, ty) in cageTexVerts], dtype='float32')
|
|
GL.glEnable(GL.GL_ALPHA_TEST)
|
|
|
|
drawCube(BoundingBox((x, y, z), (1, 1, 1)), texture=pymclevel.alphaMaterials.terrainTexture, textureVertices=cageTexVerts)
|
|
GL.glDisable(GL.GL_ALPHA_TEST)
|
|
|
|
@alertException
|
|
def mouseDown(self, evt, pos, direction):
|
|
pos = map(lambda p, d: p + d, pos, direction)
|
|
op = PlayerSpawnMoveOperation(self, pos)
|
|
try:
|
|
op.perform()
|
|
|
|
self.editor.addOperation(op)
|
|
self.editor.addUnsavedEdit()
|
|
self.markerList.invalidate()
|
|
|
|
except SpawnPositionInvalid, e:
|
|
if "Okay" != ask(str(e), responses=["Okay", "Fix it for me!"]):
|
|
level = self.editor.level
|
|
status = ""
|
|
if not okayAt63(level, pos):
|
|
level.setBlockAt(pos[0], 63, pos[2], 1)
|
|
status += "Block added at y=63.\n"
|
|
|
|
if 59 < pos[1] < 63:
|
|
pos[1] = 63
|
|
status += "Spawn point moved upward to y=63.\n"
|
|
|
|
if not okayAboveSpawn(level, pos):
|
|
if pos[1] > 63 or pos[1] < 59:
|
|
lpos = (pos[0], pos[1] - 1, pos[2])
|
|
if level.blockAt(*pos) == 0 and level.blockAt(*lpos) != 0 and okayAboveSpawn(level, lpos):
|
|
pos = lpos
|
|
status += "Spawn point shifted down by one block.\n"
|
|
if not okayAboveSpawn(level, pos):
|
|
for i in range(1, 4):
|
|
level.setBlockAt(pos[0], pos[1] + i, pos[2], 0)
|
|
|
|
status += "Blocks above spawn point cleared.\n"
|
|
|
|
self.editor.invalidateChunks([(pos[0] // 16, pos[2] // 16)])
|
|
op = PlayerSpawnMoveOperation(self, pos)
|
|
try:
|
|
op.perform()
|
|
except SpawnPositionInvalid, e:
|
|
alert(str(e))
|
|
return
|
|
|
|
self.editor.addOperation(op)
|
|
self.editor.addUnsavedEdit()
|
|
self.markerList.invalidate()
|
|
if len(status):
|
|
alert("Spawn point fixed. Changes: \n\n" + status)
|
|
|
|
@alertException
|
|
def toolReselected(self):
|
|
self.gotoSpawn()
|