class CogdoFlyingLevelQuadrant: notify = directNotify.newCategory('CogdoFlyingLevelQuadrant') def __init__(self, serialNum, model, level, parent): self.serialNum = serialNum self._model = model self._level = level self._root = NodePath('Quadrant' + repr(serialNum)) self._model.reparentTo(self._root) self._root.reparentTo(parent) self._visible = True self.platforms = {} self.gatherables = [] self.obstacles = [] self._playing = False self._obstaclesRoot = NodePath('obstacles') self._obstaclesRoot.reparentTo(self._root) self._initObstacles(self._obstaclesRoot) self._gatherablesRoot = NodePath('gatherables') self._gatherablesRoot.reparentTo(self._root) self._initGatherables(self._gatherablesRoot) self._platformsRoot = NodePath('platforms') self._platformsRoot.reparentTo(self._model) self._initPlatforms(self._platformsRoot) self._optimize() self.place() def _optimize(self): lightCones = NodePath('lightCones') for np in self._platformsRoot.findAllMatches('**/*lightCone*'): np.wrtReparentTo(lightCones) lightCones.reparentTo(self._model) node = self._model.find('**/ducts') if not node.isEmpty(): node.flattenStrong() for np in node.getChildren(): np.wrtReparentTo(self._model) node = self._model.find('**/nests') if not node.isEmpty(): for np in node.getChildren(): np.flattenStrong() np.wrtReparentTo(self._model) for np in self._model.findAllMatches('**/*LayerStack*'): np.wrtReparentTo(self._model) for np in self._model.find('**/static').getChildren(): np.wrtReparentTo(self._model) self._model.flattenMedium() def _initPlatforms(self, parent): platformModels = self._model.findAllMatches('**/%s' % Globals.Level.PlatformName) for platformModel in platformModels: platform = CogdoFlyingPlatform(platformModel, parent=parent) self.platforms[platform.getName()] = platform def _destroyPlatforms(self): for platform in list(self.platforms.values()): platform.destroy() del self.platforms def _initGatherables(self, parent): self.generateGatherables(self._model, parent=parent) if Globals.Level.SpawnLaffPowerupsInNests: self.generateNestPowerups(self._model, parent=parent) def generateNestPowerups(self, gatherableModel, parent): nests = gatherableModel.findAllMatches('**/%s;+s' % Globals.Level.LegalEagleNestName) for nest in nests: offset = Globals.Level.LaffPowerupNestOffset pickup = self._level.gatherableFactory.createPowerup(Globals.Level.GatherableTypes.LaffPowerup) pickup.reparentTo(parent) pickup.setPos(parent, nest.getPos(parent) + offset) if Globals.Level.AddSparkleToPowerups: sparkles = self._level.gatherableFactory.createSparkles(Vec4(1, 1, 1, 1), Vec4(1, 1, 0, 1), 10.0) sparkles.reparentTo(pickup) sparkles.setPos(0, 0, 1) sparkles.start() self.gatherables.append(pickup) def generateGatherables(self, gatherableModel, parent = None, spread = Globals.Level.GatherablesDefaultSpread): parent = parent or self._root mopath = Mopath.Mopath(name='gatherables') def generateMemos(): gatherPaths = gatherableModel.findAllMatches('**/%s' % Globals.Level.GatherablesPathName) for gatherPath in gatherPaths: mopath.loadNodePath(gatherPath) t = 0.0 while t < mopath.getMaxT(): pickup = self._level.gatherableFactory.createMemo() pickup.reparentTo(parent) mopath.goTo(pickup, t) self.gatherables.append(pickup) t += spread gatherPath.removeNode() angleSpread = 360.0 / Globals.Level.NumMemosInRing gatherPaths = gatherableModel.findAllMatches('**/%s' % Globals.Level.GatherablesRingName) for gatherPath in gatherPaths: mopath.loadNodePath(gatherPath) t = 0.0 while t < mopath.getMaxT(): angle = 0 r = 3 while angle < 360.0: pickup = self._level.gatherableFactory.createMemo() pickup.reparentTo(parent) mopath.goTo(pickup, t) pickup.setX(parent, pickup.getX() + r * math.cos(math.radians(angle))) pickup.setZ(parent, pickup.getZ() + r * math.sin(math.radians(angle))) self.gatherables.append(pickup) angle += angleSpread t += spread + 0.5 gatherPath.removeNode() def generatePropellers(): gatherables = gatherableModel.findAllMatches('**/%s' % Globals.Level.PropellerName) for gatherable in gatherables: pickup = self._level.gatherableFactory.createPropeller() pickup.reparentTo(gatherable.getParent()) pickup.setPos(parent, gatherable.getPos(parent)) self.gatherables.append(pickup) gatherable.removeNode() def generatePowerUps(): for powerupType, locName in Globals.Level.PowerupType2Loc.items(): if powerupType == Globals.Level.GatherableTypes.LaffPowerup and Globals.Level.IgnoreLaffPowerups: continue gatherables = gatherableModel.findAllMatches('**/%s' % locName) for gatherable in gatherables: pickup = self._level.gatherableFactory.createPowerup(powerupType) pickup.reparentTo(parent) pickup.setPos(parent, gatherable.getPos(parent)) if Globals.Level.AddSparkleToPowerups: sparkles = self._level.gatherableFactory.createSparkles(Vec4(1, 1, 1, 1), Vec4(1, 1, 0, 1), 10.0) sparkles.reparentTo(pickup) sparkles.setPos(0, 0, 1) sparkles.start() self.gatherables.append(pickup) gatherable.removeNode() generateMemos() generatePropellers() generatePowerUps() def _initObstacles(self, parent): def initWhirlwinds(): obstacles = self._root.findAllMatches('**/%s' % Globals.Level.WhirlwindName) for obstacleLoc in obstacles: motionPath = self._model.find('**/%s%s' % (obstacleLoc.getName(), Globals.Level.WhirlwindPathName)) if motionPath.isEmpty(): motionPath = None obstacle = self._level.obstacleFactory.createWhirlwind(motionPath=motionPath) obstacle.model.reparentTo(parent) obstacle.model.setPos(parent, obstacleLoc.getPos(parent)) self.obstacles.append(obstacle) obstacleLoc.removeNode() return def initStreamers(): obstacles = self._model.findAllMatches('**/%s' % Globals.Level.StreamerName) for obstacleLoc in obstacles: obstacle = self._level.obstacleFactory.createFan() obstacle.model.reparentTo(parent) obstacle.model.setPos(parent, obstacleLoc.getPos(parent)) obstacle.model.setHpr(parent, obstacleLoc.getHpr(parent)) obstacle.model.setScale(parent, obstacleLoc.getScale(parent)) obstacle.setBlowDirection() if Globals.Level.AddParticlesToStreamers: particles = self._level.obstacleFactory.createStreamerParticles(Vec4(1, 1, 1, 1), Vec4(1, 1, 1, 1), 10.0) particles.reparentTo(obstacle.model) particles.start() self.obstacles.append(obstacle) obstacleLoc.removeNode() def initWalkingMinions(): motionPaths = self._model.findAllMatches('**/%s' % Globals.Level.MinionWalkingPathName) for motionPath in motionPaths: obstacle = self._level.obstacleFactory.createWalkingMinion(motionPath=motionPath) obstacle.model.reparentTo(parent) obstacle.model.setPos(parent, motionPath.getPos(parent)) self.obstacles.append(obstacle) def initFlyingMinions(): motionPaths = self._model.findAllMatches('**/%s' % Globals.Level.MinionFlyingPathName) for motionPath in motionPaths: obstacle = self._level.obstacleFactory.createFlyingMinion(motionPath=motionPath) obstacle.model.reparentTo(parent) obstacle.model.setPos(parent, motionPath.getPos(parent)) self.obstacles.append(obstacle) initWhirlwinds() initStreamers() initWalkingMinions() initFlyingMinions() def place(self): self._root.setPos(0, self._level.convertQuadNumToY(self.serialNum), 0) def destroy(self): if self._visible: self.offstage() self._destroyPlatforms() for obstacle in self.obstacles: obstacle.destroy() for gatherable in self.gatherables: gatherable.destroy() self._root.removeNode() del self._root del self._gatherablesRoot del self._obstaclesRoot del self._platformsRoot del self._level def onstage(self, elapsedTime = 0.0): if self._visible: return self._root.unstash() for obstacle in self.obstacles: obstacle.startMoving(elapsedTime) for gatherable in self.gatherables: gatherable.show() self._visible = True def offstage(self): if not self._visible: return self._root.stash() for obstacle in self.obstacles: obstacle.stopMoving() for gatherable in self.gatherables: gatherable.hide() self._visible = False def update(self, dt): if self._visible: for gatherable in self.gatherables: gatherable.update(dt) for obstacle in self.obstacles: obstacle.update(dt) def getModel(self): return self._root
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 = list(range(Globals.TotalBarriers)) self._rng.shuffle(barrierItems) for i in barrierItems[0:len(barrierItems) - Globals.NumBarriers]: self.openBarriers.append(i) self.quadrantData = [] quadrantKeys = list(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 extension = '' if hasattr(getBase(), 'air'): extension = '.bam' filepath = self.quadrantData[i][0] + extension 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