309 lines
11 KiB
Python
309 lines
11 KiB
Python
from pandac.PandaModules import NodePath, VBase4
|
|
from direct.showbase.DirectObject import DirectObject
|
|
from direct.showbase.RandomNumGen import RandomNumGen
|
|
from toontown.minigame.MazeBase import MazeBase
|
|
import CogdoMazeGameGlobals as Globals
|
|
from CogdoMazeGameObjects import CogdoMazeWaterCooler
|
|
import CogdoMazeData
|
|
import CogdoUtil
|
|
|
|
class CogdoMaze(MazeBase, DirectObject):
|
|
|
|
def __init__(self, model, data, cellWidth):
|
|
MazeBase.__init__(self, model, data, cellWidth)
|
|
self._initWaterCoolers()
|
|
self.elevatorPos = self.maze.find('**/elevator_loc').getPos(render)
|
|
self.exitPos = self.maze.find('**/exit_loc').getPos(render)
|
|
self.maze.flattenStrong()
|
|
self._clearColor = VBase4(base.win.getClearColor())
|
|
self._clearColor.setW(1.0)
|
|
base.win.setClearColor(VBase4(0.0, 0.0, 0.0, 1.0))
|
|
if __debug__ and config.GetBool('cogdomaze-dev', False):
|
|
self._initCollisionVisuals()
|
|
|
|
def _initWaterCoolers(self):
|
|
self._waterCoolers = []
|
|
self._waterCoolerRoot = NodePath('WaterCoolerRoot')
|
|
self._waterCoolerRoot.reparentTo(render)
|
|
models = []
|
|
for model in self.maze.findAllMatches('**/*waterCooler'):
|
|
model.wrtReparentTo(render)
|
|
models.append((model.getPos(self.maze), model.getHpr(self.maze), model))
|
|
|
|
models.sort()
|
|
i = 0
|
|
for pos, hpr, model in models:
|
|
wc = CogdoMazeWaterCooler(i, model)
|
|
wc.wrtReparentTo(self._waterCoolerRoot)
|
|
wc.setPos(pos)
|
|
wc.setHpr(hpr)
|
|
self._waterCoolers.append(wc)
|
|
i += 1
|
|
|
|
self._waterCoolerRoot.stash()
|
|
|
|
def getWaterCoolers(self):
|
|
return self._waterCoolers
|
|
|
|
def isAccessible(self, tX, tY):
|
|
if tX < 0 or tY < 0 or tX >= self.width or tY >= self.height:
|
|
return 0
|
|
return self.collisionTable[tY][tX] != 1
|
|
|
|
def destroy(self):
|
|
for waterCooler in self._waterCoolers:
|
|
waterCooler.destroy()
|
|
|
|
del self._waterCoolers
|
|
self._waterCoolerRoot.removeNode()
|
|
del self._waterCoolerRoot
|
|
base.win.setClearColor(self._clearColor)
|
|
del self._clearColor
|
|
MazeBase.destroy(self)
|
|
if __debug__ and hasattr(self, '_cubes'):
|
|
self.ignoreAll()
|
|
self._cubes.removeNode()
|
|
del self._cubes
|
|
|
|
def onstage(self):
|
|
MazeBase.onstage(self)
|
|
self._waterCoolerRoot.unstash()
|
|
|
|
def offstage(self):
|
|
self._waterCoolerRoot.stash()
|
|
MazeBase.offstage(self)
|
|
|
|
|
|
BARRIER_DATA_RIGHT = 1
|
|
BARRIER_DATA_TOP = 1
|
|
|
|
class CogdoMazeFactory:
|
|
|
|
def __init__(self, randomNumGen, width, height, frameWallThickness = Globals.FrameWallThickness, cogdoMazeData = CogdoMazeData):
|
|
self._rng = RandomNumGen(randomNumGen)
|
|
self.width = width
|
|
self.height = height
|
|
self.frameWallThickness = frameWallThickness
|
|
self._cogdoMazeData = cogdoMazeData
|
|
self.quadrantSize = self._cogdoMazeData.QuadrantSize
|
|
self.cellWidth = self._cogdoMazeData.QuadrantCellWidth
|
|
|
|
def getMazeData(self):
|
|
if not hasattr(self, '_data'):
|
|
self._generateMazeData()
|
|
return self._data
|
|
|
|
def createCogdoMaze(self, flattenModel = True):
|
|
if not hasattr(self, '_maze'):
|
|
self._loadAndBuildMazeModel(flatten=flattenModel)
|
|
return CogdoMaze(self._model, self._data, self.cellWidth)
|
|
|
|
def _gatherQuadrantData(self):
|
|
self.openBarriers = []
|
|
barrierItems = range(Globals.TotalBarriers)
|
|
self._rng.shuffle(barrierItems)
|
|
for i in barrierItems[0:len(barrierItems) - Globals.NumBarriers]:
|
|
self.openBarriers.append(i)
|
|
|
|
self.quadrantData = []
|
|
quadrantKeys = self._cogdoMazeData.QuadrantCollisions.keys()
|
|
self._rng.shuffle(quadrantKeys)
|
|
i = 0
|
|
for y in range(self.height):
|
|
for x in range(self.width):
|
|
key = quadrantKeys[i]
|
|
collTable = self._cogdoMazeData.QuadrantCollisions[key]
|
|
angle = self._cogdoMazeData.QuadrantAngles[self._rng.randint(0, len(self._cogdoMazeData.QuadrantAngles) - 1)]
|
|
self.quadrantData.append((key, collTable[angle], angle))
|
|
i += 1
|
|
if x * y >= self._cogdoMazeData.NumQuadrants:
|
|
i = 0
|
|
|
|
def _generateBarrierData(self):
|
|
data = []
|
|
for y in range(self.height):
|
|
data.append([])
|
|
for x in range(self.width):
|
|
if x == self.width - 1:
|
|
ax = -1
|
|
else:
|
|
ax = 1
|
|
if y == self.height - 1:
|
|
ay = -1
|
|
else:
|
|
ay = 1
|
|
data[y].append([ax, ay])
|
|
|
|
dirUp = 0
|
|
dirDown = 1
|
|
dirLeft = 2
|
|
dirRight = 3
|
|
|
|
def getAvailableDirections(ax, ay, ignore = None):
|
|
dirs = []
|
|
if ax - 1 >= 0 and data[ay][ax - 1][BARRIER_DATA_RIGHT] == 1 and (ax, ay) != ignore:
|
|
dirs.append(dirLeft)
|
|
if ax + 1 < self.width and data[ay][ax][BARRIER_DATA_RIGHT] == 1 and (ax, ay) != ignore:
|
|
dirs.append(dirRight)
|
|
if ay - 1 >= 0 and data[ay - 1][ax][BARRIER_DATA_TOP] == 1 and (ax, ay) != ignore:
|
|
dirs.append(dirDown)
|
|
if ay + 1 < self.height and data[ay][ax][BARRIER_DATA_TOP] == 1 and (ax, ay) != ignore:
|
|
dirs.append(dirUp)
|
|
return dirs
|
|
|
|
visited = []
|
|
|
|
def tryVisitNeighbor(ax, ay, ad):
|
|
if ad == dirUp:
|
|
if data[ay][ax] in visited:
|
|
return None
|
|
visited.append(data[ay][ax])
|
|
data[ay][ax][BARRIER_DATA_TOP] = 0
|
|
ay += 1
|
|
elif ad == dirDown:
|
|
if data[ay - 1][ax] in visited:
|
|
return None
|
|
visited.append(data[ay - 1][ax])
|
|
data[ay - 1][ax][BARRIER_DATA_TOP] = 0
|
|
ay -= 1
|
|
elif ad == dirLeft:
|
|
if data[ay][ax - 1] in visited:
|
|
return None
|
|
visited.append(data[ay][ax - 1])
|
|
data[ay][ax - 1][BARRIER_DATA_RIGHT] = 0
|
|
ax -= 1
|
|
elif ad == dirRight:
|
|
if data[ay][ax] in visited:
|
|
return None
|
|
visited.append(data[ay][ax])
|
|
data[ay][ax][BARRIER_DATA_RIGHT] = 0
|
|
ax += 1
|
|
return (ax, ay)
|
|
|
|
def openBarriers(x, y):
|
|
dirs = getAvailableDirections(x, y)
|
|
for dir in dirs:
|
|
next = tryVisitNeighbor(x, y, dir)
|
|
if next is not None:
|
|
openBarriers(*next)
|
|
|
|
return
|
|
|
|
x = self._rng.randint(0, self.width - 1)
|
|
y = self._rng.randint(0, self.height - 1)
|
|
openBarriers(x, y)
|
|
self._barrierData = data
|
|
return
|
|
|
|
def _generateMazeData(self):
|
|
if not hasattr(self, 'quadrantData'):
|
|
self._gatherQuadrantData()
|
|
self._data = {}
|
|
self._data['width'] = (self.width + 1) * self.frameWallThickness + self.width * self.quadrantSize
|
|
self._data['height'] = (self.height + 1) * self.frameWallThickness + self.height * self.quadrantSize
|
|
self._data['originX'] = int(self._data['width'] / 2)
|
|
self._data['originY'] = int(self._data['height'] / 2)
|
|
collisionTable = []
|
|
horizontalWall = [ 1 for x in range(self._data['width']) ]
|
|
collisionTable.append(horizontalWall)
|
|
for i in range(0, len(self.quadrantData), self.width):
|
|
for y in range(self.quadrantSize):
|
|
row = [1]
|
|
for x in range(i, i + self.width):
|
|
if x == 1 and y < self.quadrantSize / 2 - 2:
|
|
newData = []
|
|
for j in self.quadrantData[x][1][y]:
|
|
if j == 0:
|
|
newData.append(2)
|
|
else:
|
|
newData.append(j + 0)
|
|
|
|
row += newData + [1]
|
|
else:
|
|
row += self.quadrantData[x][1][y] + [1]
|
|
|
|
collisionTable.append(row)
|
|
|
|
collisionTable.append(horizontalWall[:])
|
|
|
|
barriers = Globals.MazeBarriers
|
|
for i in range(len(barriers)):
|
|
for coords in barriers[i]:
|
|
collisionTable[coords[1]][coords[0]] = 0
|
|
|
|
y = self._data['originY']
|
|
for x in range(len(collisionTable[y])):
|
|
if collisionTable[y][x] == 0:
|
|
collisionTable[y][x] = 2
|
|
|
|
x = self._data['originX']
|
|
for y in range(len(collisionTable)):
|
|
if collisionTable[y][x] == 0:
|
|
collisionTable[y][x] = 2
|
|
|
|
self._data['collisionTable'] = collisionTable
|
|
|
|
def _loadAndBuildMazeModel(self, flatten = False):
|
|
self.getMazeData()
|
|
self._model = NodePath('CogdoMazeModel')
|
|
levelModel = CogdoUtil.loadMazeModel('level')
|
|
self.quadrants = []
|
|
quadrantUnitSize = int(self.quadrantSize * self.cellWidth)
|
|
frameActualSize = self.frameWallThickness * self.cellWidth
|
|
size = quadrantUnitSize + frameActualSize
|
|
halfWidth = int(self.width / 2)
|
|
halfHeight = int(self.height / 2)
|
|
i = 0
|
|
for y in range(self.height):
|
|
for x in range(self.width):
|
|
ax = (x - halfWidth) * size
|
|
ay = (y - halfHeight) * size
|
|
filepath = self.quadrantData[i][0]
|
|
angle = self.quadrantData[i][2]
|
|
m = self._createQuadrant(filepath, i, angle, quadrantUnitSize)
|
|
m.setPos(ax, ay, 0)
|
|
m.reparentTo(self._model)
|
|
self.quadrants.append(m)
|
|
i += 1
|
|
|
|
quadrantHalfUnitSize = quadrantUnitSize * 0.5
|
|
barrierModel = CogdoUtil.loadMazeModel('grouping_blockerDivider').find('**/divider')
|
|
y = 3
|
|
for x in range(self.width):
|
|
if x == (self.width - 1) / 2:
|
|
continue
|
|
ax = (x - halfWidth) * size
|
|
ay = (y - halfHeight) * size - quadrantHalfUnitSize - (self.cellWidth - 0.5)
|
|
b = NodePath('barrier')
|
|
barrierModel.instanceTo(b)
|
|
b.setPos(ax, ay, 0)
|
|
b.reparentTo(self._model)
|
|
|
|
offset = self.cellWidth - 0.5
|
|
for x in (0, 3):
|
|
for y in range(self.height):
|
|
ax = (x - halfWidth) * size - quadrantHalfUnitSize - frameActualSize + offset
|
|
ay = (y - halfHeight) * size
|
|
b = NodePath('barrier')
|
|
barrierModel.instanceTo(b)
|
|
b.setPos(ax, ay, 0)
|
|
b.setH(90)
|
|
b.reparentTo(self._model)
|
|
|
|
offset -= 2.0
|
|
|
|
barrierModel.removeNode()
|
|
levelModel.getChildren().reparentTo(self._model)
|
|
for np in self._model.findAllMatches('**/*lightCone*'):
|
|
CogdoUtil.initializeLightCone(np, 'fixed', 3)
|
|
|
|
if flatten:
|
|
self._model.flattenStrong()
|
|
return self._model
|
|
|
|
def _createQuadrant(self, filepath, serialNum, angle, size):
|
|
root = NodePath('QuadrantRoot-%i' % serialNum)
|
|
quadrant = loader.loadModel(filepath)
|
|
quadrant.getChildren().reparentTo(root)
|
|
root.setH(angle)
|
|
return root
|