def resync(self, seed=None): if seed is None: seed = self.avId self.mainRandomGen = RandomNumGen.RandomNumGen(self.avId) self.attackRandomGen = RandomNumGen.RandomNumGen(self.avId) self.mainCounter = 0 self.attackCounter = 0 return
def getMazeName(gameDoId, numPlayers, mazeNames): try: return forcedMaze except: names = mazeNames[numPlayers - 1] return names[RandomNumGen.randHash(gameDoId) % len(names)]
def __init__(self, air, tableNumber, x, y, z, h, p, r): """__init__(air) """ DistributedObjectAI.DistributedObjectAI.__init__(self, air) self.seats = [None, None, None, None] self.posHpr = (x, y, z, h, p, r) self.tableNumber = int(tableNumber) self.seed = RandomNumGen.randHash(globalClock.getRealTime()) # Flag that tells whether the trolley is currently accepting boarders self.accepting = 0 self.numPlayersExiting = 0 self.trolleyCountdownTime = \ ConfigVariableDouble("picnic-countdown-time", ToontownGlobals.PICNIC_COUNTDOWN_TIME).getValue() self.fsm = ClassicFSM.ClassicFSM( 'DistributedPicnicBasketAI', [ State.State('off', self.enterOff, self.exitOff, ['waitEmpty']), State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty']) ], # Initial State 'off', # Final State 'off', ) self.fsm.enterInitialState()
def onstage(self): self.notify.debug('onstage') DistributedMinigame.onstage(self) self.gameBoard.reparentTo(render) self.sky.reparentTo(render) lt = base.localAvatar lt.reparentTo(render) self.__placeToon(self.localAvId) lt.setSpeed(0, 0) toonSD = self.toonSDs[self.localAvId] toonSD.enter() toonSD.fsm.request('normal') self.stopGameWalk() for cogIndex in xrange(self.getNumCogs()): suit = self.cogInfo[cogIndex]['suit'].suit pos = self.cogInfo[cogIndex]['pos'] suit.reparentTo(self.gameBoard) suit.setPos(pos) for avId in self.avIdList: self.toonHitTracks[avId] = Wait(0.1) self.toonRNGs = [] for i in xrange(self.numPlayers): self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen)) self.sndTable = {'hitBySuit': [None] * self.numPlayers, 'falling': [None] * self.numPlayers} for i in xrange(self.numPlayers): self.sndTable['hitBySuit'][i] = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg') self.sndTable['falling'][i] = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg') base.playMusic(self.music, looping=1, volume=0.8) return
def generateCard(self, tileSeed, zoneId): rng = RandomNumGen.RandomNumGen(tileSeed) rowSize = self.game.getRowSize() fishList = FishGlobals.getPondGeneraList(zoneId) for i in range(len(fishList)): fishTuple = fishList.pop(0) weight = FishGlobals.getRandomWeight(fishTuple[0], fishTuple[1]) fish = FishBase.FishBase(fishTuple[0], fishTuple[1], weight) fishList.append(fish) emptyCells = self.game.getCardSize() - 1 - len(fishList) rodId = 0 for i in range(emptyCells): fishVitals = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) while not fishVitals[0]: fishVitals = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) fish = FishBase.FishBase(fishVitals[1], fishVitals[2], fishVitals[3]) fishList.append(fish) rodId += 1 if rodId > 4: rodId = 0 for i in range(rowSize): for j in range(self.game.getColSize()): color = self.getCellColor(i * rowSize + j) if i * rowSize + j == self.game.getCardSize() / 2: tmpFish = 'Free' else: choice = rng.randrange(0, len(fishList)) tmpFish = fishList.pop(choice) xPos = BG.CellImageScale * (j - 2) + BG.GridXOffset yPos = BG.CellImageScale * (i - 2) - 0.015 cellGui = BingoCardCell.BingoCardCell(i * rowSize + j, tmpFish, self.model, color, self, image_scale=BG.CellImageScale, pos=(xPos, 0, yPos)) self.cellGuiList.append(cellGui)
def generateCard(self, tileSeed, zoneId): rng = RandomNumGen.RandomNumGen(tileSeed) # Retrieve a list of Fish based on the Genus Type. Each Genus # found in the pond will be represented on the board. fishList = FishGlobals.getPondGeneraList(zoneId) # Determine the number of cells left to fill. emptyCells = (self.cardSize - 1) - len(fishList) rodId = 0 for i in range(emptyCells): fish = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) while (not fish[0]): fish = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) fishList.append((fish[1], fish[2])) rodId += 1 if rodId > 4: rodId = 0 # Now, fill up the the card by randomly placing the fish in a cell. for index in range(self.cardSize): if index != self.cardSize // 2: choice = rng.randrange(0, len(fishList)) self.cellList.append(fishList.pop(choice)) else: self.cellList.append((None, None))
def initializeHeightMap(self, id=0): """ """ logging.info("initializing heightmap...") if id == 0: self.dice = RandomNumGen(TimeVal().getUsec()) id = self.dice.randint(2, 1000000) self.id = id #Remove old tiles that will not conform to a new heightmap for pos, tile in self.tiles.items(): self.deleteTile(pos) self.storage.clear() self.heightMap = HeightMap(id, self.waterHeight + 0.03) self.getHeight = self.heightMap.getHeight
def onstage(self): self.notify.debug('onstage') DistributedMinigame.onstage(self) self.maze.onstage() self.randomNumGen.shuffle(self.startPosHTable) lt = base.localAvatar lt.reparentTo(render) lt.hideName() self.__placeToon(self.localAvId) lt.setAnimState('Happy', 1.0) lt.setSpeed(0, 0) self.camParent = render.attachNewNode('mazeGameCamParent') self.camParent.reparentTo(base.localAvatar) self.camParent.setPos(0, 0, 0) self.camParent.setHpr(render, 0, 0, 0) camera.reparentTo(self.camParent) camera.setPos(self.camOffset) self.__spawnCameraTask() self.toonRNGs = [] for i in xrange(self.numPlayers): self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen)) self.treasures = [] for i in xrange(self.maze.numTreasures): self.treasures.append(MazeTreasure.MazeTreasure(self.treasureModel, self.maze.treasurePosList[i], i, self.doId)) self.__loadSuits() for suit in self.suits: suit.onstage() self.sndTable = {'hitBySuit': [None] * self.numPlayers, 'falling': [ None] * self.numPlayers} for i in xrange(self.numPlayers): self.sndTable['hitBySuit'][i] = base.loader.loadSfx('phase_4/audio/sfx/MG_Tag_C.ogg') self.sndTable['falling'][i] = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.ogg') self.grabSounds = [] for i in xrange(5): self.grabSounds.append(base.loader.loadSfx('phase_4/audio/sfx/MG_maze_pickup.ogg')) self.grabSoundIndex = 0 for avId in self.avIdList: self.toonHitTracks[avId] = Wait(0.1) self.scores = [0] * self.numPlayers self.goalBar = DirectWaitBar(parent=render2d, relief=DGG.SUNKEN, frameSize=(-0.35, 0.35, -0.15, 0.15), borderWidth=(0.02, 0.02), scale=0.42, pos=(0.84, 0, 0.5 - 0.28 * self.numPlayers + 0.05), barColor=(0, 0.7, 0, 1)) self.goalBar.setBin('unsorted', 0) self.goalBar.hide() self.introTrack = self.getIntroTrack() self.introTrack.start() return
def generate(self): DistributedObject.DistributedObject.generate(self) if self.butterfly: return None self.butterfly = Actor.Actor() self.butterfly.loadModel('phase_4/models/props/SZ_butterfly-mod.bam') self.butterfly.loadAnims({ 'flutter': 'phase_4/models/props/SZ_butterfly-flutter.bam', 'glide': 'phase_4/models/props/SZ_butterfly-glide.bam', 'land': 'phase_4/models/props/SZ_butterfly-land.bam' }) index = self.doId % len(self.wingTypes) chosenType = self.wingTypes[index] node = self.butterfly.getGeomNode() for type in self.wingTypes: wing = node.find('**/' + type) if type != chosenType: wing.removeNode() continue if index == 0 or index == 1: color = self.yellowColors[self.doId % len(self.yellowColors)] elif index == 2 or index == 3: color = self.whiteColors[self.doId % len(self.whiteColors)] elif index == 4: color = self.paleYellowColors[self.doId % len(self.paleYellowColors)] else: color = Vec4(1, 1, 1, 1) wing.setColor(color) self.butterfly2 = Actor.Actor(other = self.butterfly) self.butterfly.enableBlend(blendType = PartBundle.BTLinear) self.butterfly.loop('flutter') self.butterfly.loop('land') self.butterfly.loop('glide') rng = RandomNumGen.RandomNumGen(self.doId) playRate = 0.59999999999999998 + 0.80000000000000004 * rng.random() self.butterfly.setPlayRate(playRate, 'flutter') self.butterfly.setPlayRate(playRate, 'land') self.butterfly.setPlayRate(playRate, 'glide') self.butterfly2.setPlayRate(playRate, 'flutter') self.butterfly2.setPlayRate(playRate, 'land') self.butterfly2.setPlayRate(playRate, 'glide') self.glideWeight = rng.random() * 2 lodNode = LODNode('butterfly-node') lodNode.addSwitch(100, 40) lodNode.addSwitch(40, 0) self.butterflyNode = NodePath(lodNode) self.butterfly2.setH(180.0) self.butterfly2.reparentTo(self.butterflyNode) self.butterfly.setH(180.0) self.butterfly.reparentTo(self.butterflyNode) self._DistributedButterfly__initCollisions() self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') self.dropShadow.setColor(0, 0, 0, 0.29999999999999999) self.dropShadow.setPos(0, 0.10000000000000001, -0.050000000000000003) self.dropShadow.setScale(self.shadowScaleBig) self.dropShadow.reparentTo(self.butterfly)
def __createRandomNumGen(self): self.notify.debug('BASE: self.doId=0x%08X' % self.doId) self.randomNumGen = RandomNumGen.RandomNumGen(self.doId) def destroy(self=self): self.notify.debug('BASE: destroying random num gen') del self.randomNumGen self.cleanupActions.append(destroy)
def __createRandomNumGen(self): self.notify.debug("BASE: self.doId=0x%08X" % self.doId) # seed the random number generator with the minigame doId self.randomNumGen = RandomNumGen.RandomNumGen(self.doId) def destroy(self=self): self.notify.debug("BASE: destroying random num gen") del self.randomNumGen self.cleanupActions.append(destroy)
def populate(self, tile): terrain = tile.terrain xOff = tile.xOffset yOff = tile.yOffset tileSize = terrain.tileSize seed = terrain.heightMap.getHeight(yOff * -2, xOff * -2)+1 * 2147483647 dice = RandomNumGen(seed) for factory in self.factories: #num = dice.randint(0, factory.averageNumber) + dice.randint(0, factory.averageNumber) num = int((dice.random() + dice.random()) * factory.averageNumber) for iterator in range(num): x = dice.random() * tileSize y = dice.random() * tileSize if terrain.getHeight(x+xOff, y+yOff) > terrain.waterHeight: object = factory.factoryFunction(*factory.constructorParams) #logging.info( object) #logging.info( factory.factoryFunction) self.addToTile(tile, object, x, y) tile.statics.flattenStrong()
def __init__(self): """Create a new terrain centered on the focus. The focus is the NodePath where the LOD is the greatest. id is a seed for the map and unique name for any cached heightmap images """ ##### Terrain Tile physical properties self.maxHeight = 300 self.dice = RandomNumGen(TimeVal().getUsec()) self.id = self.dice.randint(2, 1000000) # scale the terrain vertically to its maximum height #self.setSz(self.maxHeight) # scale horizontally to appearance/performance balance #self.horizontalScale = 1.0 #self.setSx(self.horizontalScale) #self.setSy(self.horizontalScale) ##### heightmap properties self.initializeHeightMap(id)
def populate(self, tile): terrain = tile.terrain xOff = tile.xOffset yOff = tile.yOffset tileSize = terrain.tileSize seed = terrain.heightMap.getHeight(yOff * -2, xOff * -2) + 1 * 2147483647 dice = RandomNumGen(seed) for factory in self.factories: #num = dice.randint(0, factory.averageNumber) + dice.randint(0, factory.averageNumber) num = int((dice.random() + dice.random()) * factory.averageNumber) for iterator in range(num): x = dice.random() * tileSize y = dice.random() * tileSize if terrain.getHeight(x + xOff, y + yOff) > terrain.waterHeight: object = factory.factoryFunction( *factory.constructorParams) #logging.info( object) #logging.info( factory.factoryFunction) self.addToTile(tile, object, x, y) tile.statics.flattenStrong()
def generateCard(self): if self.card: self.card.destroy() del self.card self.tileSeed = RandomNumGen.randHash(globalClock.getRealTime()) # Determine whether the next game should be a super game. If # we are coming out of an intermission, then it will be a # super game. These occur each hour on the hour, and for the # last game of the evening. if self.nextGameSuper: self.notify.info("generateCard: SUPER CARD GENERATION") self.typeId = BingoGlobals.BLOCKOUT_CARD self.jackpot = self.air.bingoMgr.getSuperJackpot(self.zoneId) # If this was an hourly super jackpot, not the final game of the # evening, reset so that we don't go into another intermission # at the end of this game. Otherwise, we want to transition # to the CloseEvent state after the game has ended, either by # a win or "loss." if self.finalGame == BG.INTERMISSION: self.finalGame = BG.NORMAL_GAME self.nextGameSuper = False else: rng = RandomNumGen.RandomNumGen(self.tileSeed) self.typeId = rng.randint(BingoGlobals.NORMAL_CARD, BingoGlobals.THREEWAY_CARD) self.jackpot = BingoGlobals.getJackpot(self.typeId) self.card = self.__cardChoice() self.card.generateCard(self.tileSeed, self.pond.getArea()) self.cardId += 1 self.numMarkedCells= 0 self.maxPlayers = len(self.avId2Fish)
def __init__(self, serialNum, maze, randomNumGen, cellWalkPeriod, difficulty): self.serialNum = serialNum self.maze = maze self.rng = RandomNumGen.RandomNumGen(randomNumGen) self.difficulty = difficulty self.suit = Suit.Suit() d = SuitDNA.SuitDNA() d.newSuit('f') # flunky self.suit.setDNA(d) self.ticPeriod = int(cellWalkPeriod) self.cellWalkDuration = float(self.ticPeriod) / \ float(MazeGameGlobals.SUIT_TIC_FREQ) self.turnDuration = 0.6 * self.cellWalkDuration
def __init__(self, air, tableNumber, x, y, z, h, p, r): DistributedObjectAI.DistributedObjectAI.__init__(self, air) self.seats = [None, None, None, None] self.posHpr = (x, y, z, h, p, r) self.tableNumber = int(tableNumber) self.seed = RandomNumGen.randHash(globalClock.getRealTime()) self.accepting = 0 self.numPlayersExiting = 0 self.trolleyCountdownTime = simbase.config.GetFloat('picnic-countdown-time', ToontownGlobals.PICNIC_COUNTDOWN_TIME) self.fsm = ClassicFSM.ClassicFSM( 'DistributedPicnicBasketAI', [ State.State('off', self.enterOff, self.exitOff, ['waitEmpty']), State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty']) ], 'off', 'off') self.fsm.enterInitialState()
def initializeHeightMap(self, id=0): """ """ logging.info("initalizing heightmap...") if id == 0: self.dice = RandomNumGen(TimeVal().getUsec()) id = self.dice.randint(2, 1000000) self.id = id #Remove old tiles that will not conform to a new heightmap for pos, tile in self.tiles.items(): self.deleteTile(pos) self.storage.clear() self.heightMap = HeightMap(id, self.waterHeight + 0.03) self.getHeight = self.heightMap.getHeight
def generateCard(self, tileSeed, zoneId): rng = RandomNumGen.RandomNumGen(tileSeed) fishList = FishGlobals.getPondGeneraList(zoneId) emptyCells = self.cardSize - 1 - len(fishList) rodId = 0 for i in xrange(emptyCells): fish = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) while not fish[0]: fish = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) fishList.append((fish[1], fish[2])) rodId += 1 if rodId > 4: rodId = 0 continue for index in xrange(self.cardSize): if index != self.cardSize / 2: choice = rng.randrange(0, len(fishList)) self.cellList.append(fishList.pop(choice)) continue self.cellList.append((None, None))
def advanceAttackSeed(self): seedVal = self.mainRandomGen.randint(0, (1 << 16) - 1) self.mainCounter += 1 self.attackCounter = 0 self.attackRandomGen = RandomNumGen.RandomNumGen(seedVal)
class Terrain(NodePath): """A terrain contains a set of geomipmaps, and maintains their common properties.""" def __init__(self, name, focus, maxRange, populator=None, feedBackString=None, id=0): """Create a new terrain centered on the focus. The focus is the NodePath where the LOD is the greatest. id is a seed for the map and unique name for any cached heightmap images """ NodePath.__init__(self, name) ### Basic Parameters self.name = name # nodepath to center of the level of detail self.focus = focus # stores all terrain tiles that make up the terrain self.tiles = {} # stores previously built tiles we can readd to the terrain self.storage = {} self.feedBackString = feedBackString if populator == None: populator = TerrainPopulator() self.populator = populator self.graphReducer = SceneGraphReducer() if THREAD_LOAD_TERRAIN: self.tileBuilder = TerrainTileBuilder(self) ##### Terrain Tile physical properties self.maxHeight = MAX_TERRAIN_HEIGHT self.tileSize = 128 self.heightMapSize = self.tileSize + 1 ##### Terrain scale and tile distances # distances are measured in tile's smallest unit # conversion to world units may be necessary # Don't show untiled terrain below this distance etc. # scale the terrain vertically to its maximum height self.setSz(self.maxHeight) # scale horizontally to appearance/performance balance self.horizontalScale = TERRAIN_HORIZONTAL_STRETCH self.setSx(self.horizontalScale) self.setSy(self.horizontalScale) # waterHeight is expressed as a multiplier to the max height self.waterHeight = 0.3 #this is the furthest the camera can view self.maxViewRange = maxRange # Add half the tile size because distance is checked from the center, # not from the closest edge. self.minTileDistance = self.maxViewRange / self.horizontalScale + self.tileSize / 2 # to avoid excessive store / retrieve behavior on tiles we have a small # buffer where it doesn't matter whether or not the tile is present self.maxTileDistance = self.minTileDistance + self.tileSize / 2 ##### heightmap properties self.initializeHeightMap(id) ##### rendering properties self.initializeRenderProperties() ##### task handling #self._setupThreadedTasks() # newTile is a placeholder for a tile currently under construction # this has to be initialized last because it requires values from self #self.newTile = TerrainTile(self, 0, 0) # loads all terrain tiles in range immediately if THREAD_LOAD_TERRAIN: self.preload(self.focus.getX() / self.horizontalScale, self.focus.getY() / self.horizontalScale) else: taskMgr.add(self.oldPreload, "preloadTask", extraArgs=[self.focus.getX() / self.horizontalScale, self.focus.getY() / self.horizontalScale]) #self.flattenLight() def initializeHeightMap(self, id=0): """ """ logging.info("initalizing heightmap...") if id == 0: self.dice = RandomNumGen(TimeVal().getUsec()) id = self.dice.randint(2, 1000000) self.id = id #Remove old tiles that will not conform to a new heightmap for pos, tile in self.tiles.items(): self.deleteTile(pos) self.storage.clear() self.heightMap = HeightMap(id, self.waterHeight + 0.03) self.getHeight = self.heightMap.getHeight def initializeRenderingProperties(self): logging.info("initializing terrain rendering properties...") #self.bruteForce = True self.bruteForce = BRUTE_FORCE_TILES if self.bruteForce: self.blockSize = self.tileSize else: #self.blockSize = 16 self.blockSize = self.tileSize self.near = 100 self.far = self.maxViewRange * 0.5 + self.blockSize self.wireFrame = 0 #self.texturer = MonoTexturer(self) self.texturer = ShaderTexturer(self) #self.texturer = DetailTexturer(self) #self.texturer.apply(self) #self.setShaderInput("zMultiplier",) logging.info("rendering properties initialized...") def _setupSimpleTasks(self): """This sets up tasks to maintain the terrain as the focus moves.""" logging.info("initializing terrain update task...") ##Add tasks to keep updating the terrain #taskMgr.add(self.updateTilesTask, "updateTiles", sort=9, priority=0) taskMgr.doMethodLater(5, self.update, "update", sort=9, priority=0) self.updateStep = 1 def reduceSceneGraph(self, radius): gr = self.graphReducer gr.applyAttribs(self.node()) gr.setCombineRadius(radius) gr.flatten(self.node(), SceneGraphReducer.CSRecurse) gr.makeCompatibleState(self.node()) gr.collectVertexData(self.node()) gr.unify(self.node(), False) def update(self, task): """This task updates terrain as needed.""" if self.updateStep == 1: self.makeNewTile() if THREAD_LOAD_TERRAIN: self.grabBuiltTile() self.removeOldTiles() self.updateStep += 1 return Task.cont #self.updateTiles() self.tileLodUpdate() #self.buildDetailLevels() self.updateStep = 1 return Task.cont def updateLight(self): """This task moves point and directional lights. For larger projects this should be externalized. """ self.pointLight = Vec3(0, 5, 0)#self.focus.getPos() + Vec3(0,5,0) self.setShaderInput("LightPosition", self.pointLight) def updateTiles(self): """This task updates each tile, which updates the LOD. GeoMipMap updates are slow however and may cause unacceptable lag. """ #logging.info(self.focus.getPos()) for pos, tile in self.tiles.items(): tile.update() #logging.info(str(tile.getFocalPoint().getPos())) #if tile.update(): #logging.info("update success") #yield Task.cont def tileLodUpdate(self): """Updates tiles to LOD appropriate for their distance. setMinDetailLevel() doesn't flag a geomipterrain as dirty, so update will not alter detail level. It would have to be regenerated. Instead we will use a special LodTerrainTile. """ if not self.bruteForce: self.updateTiles() return focusx = self.focus.getX() / self.horizontalScale focusy = self.focus.getY() / self.horizontalScale halfTile = self.tileSize * 0.5 # switch to high, mid, and low LOD's at these distances # having a gap between the zones avoids switching back and forth too # if the focus is moving erratically highOuter = self.minTileDistance * 0.02 + self.tileSize highOuter *= highOuter midInner = self.minTileDistance * 0.02 + self.tileSize + halfTile midInner *= midInner midOuter = self.minTileDistance * 0.2 + self.tileSize midOuter *= midOuter lowInner = self.minTileDistance * 0.2 + self.tileSize + halfTile lowInner *= lowInner lowOuter = self.minTileDistance * 0.5 + self.tileSize lowOuter *= lowOuter horizonInner = self.minTileDistance * 0.5 + self.tileSize + halfTile horizonInner *= horizonInner for pos, tile in self.tiles.items(): deltaX = focusx - (pos[0] + halfTile) deltaY = focusy - (pos[1] + halfTile) distance = deltaX * deltaX + deltaY * deltaY if distance < highOuter: tile.setDetail(0) elif distance < midOuter: if distance > midInner or tile.getDetail() > 1: tile.setDetail(1) elif distance < lowOuter: if distance > lowInner or tile.getDetail() > 2: tile.setDetail(2) elif distance > horizonInner: tile.setDetail(3) def buildDetailLevels(self): """Unused.""" n = len(self.buildQueue) / 5.0 if n > 0 and n < 1: n = 1 else: n = int(n) for i in range(n): request = self.buildQueue.popleft() request[0].buildAndSet(request[1]) def oldPreload(self, task, xpos=0, ypos=0): """Loads all tiles in range immediately. This can suspend the program for a long time and is best used when first loading a level. It simply iterates through a square region building any tile that is reasonably within the max distance. It does not prioritize tiles closest to the focus. """ logging.info("preloading terrain tiles...") self.buildQueue = deque() # x and y start are rounded to the nearest multiple of tile size xstart = (int(xpos / self.horizontalScale) / self.tileSize) * self.tileSize ystart = (int(ypos / self.horizontalScale) / self.tileSize) * self.tileSize # check radius is rounded up to the nearest tile size from maxTileDistance # not every tile in checkRadius will be made checkRadius = (int(self.maxTileDistance) / self.tileSize + 1) * self.tileSize halfTile = self.tileSize * 0.5 # build distance for the preloader will be halfway in between the normal # load distance and the unloading distance buildDistanceSquared = (self.minTileDistance + self.maxTileDistance) / 2 buildDistanceSquared = buildDistanceSquared * buildDistanceSquared for x in range (xstart - checkRadius, xstart + checkRadius, self.tileSize): for y in range (ystart - checkRadius, ystart + checkRadius, self.tileSize): if not (x, y) in self.tiles: deltaX = xpos - (x + halfTile) deltaY = ypos - (y + halfTile) distanceSquared = deltaX * deltaX + deltaY * deltaY if distanceSquared < buildDistanceSquared: self.buildQueue.append((x, y)) total = len(self.buildQueue) while len(self.buildQueue): if self.feedBackString: done = total - len(self.buildQueue) feedback = "Loading Terrain " + str(done) + "/" + str(total) logging.info(feedback) self.feedBackString.setText(feedback) tile = self.buildQueue.popleft() self._generateTile(tile) yield Task.cont self._setupSimpleTasks() yield Task.done def preload(self, xpos=1, ypos=1): """ """ logging.info("preloading terrain tiles...") # x and y start are rounded to the nearest multiple of tile size xstart = (int(xpos / self.horizontalScale) / self.tileSize) * self.tileSize ystart = (int(ypos / self.horizontalScale) / self.tileSize) * self.tileSize # check radius is rounded up to the nearest tile size from maxTileDistance # not every tile in checkRadius will be made checkRadius = (int(self.maxTileDistance) / self.tileSize + 1) * self.tileSize halfTile = self.tileSize * 0.5 # build distance for the preloader will be halfway in between the normal # load distance and the unloading distance buildDistanceSquared = (self.minTileDistance + self.maxTileDistance) / 2 buildDistanceSquared = buildDistanceSquared * buildDistanceSquared for x in range (xstart - checkRadius, xstart + checkRadius, self.tileSize): for y in range (ystart - checkRadius, ystart + checkRadius, self.tileSize): if not (x, y) in self.tiles: deltaX = xpos - (x + halfTile) deltaY = ypos - (y + halfTile) distanceSquared = deltaX * deltaX + deltaY * deltaY if distanceSquared < buildDistanceSquared: self.tileBuilder.preload((x, y)) self.preloadTotal = self.tileBuilder.queue.qsize() taskMgr.add(self.preloadWait, "preloadWaitTask") def preloadWait(self, task): #loggin.info( "preloadWait()") if self.feedBackString: done = self.preloadTotal - self.tileBuilder.queue.qsize() feedback = "Loading Terrain " + str(done) + "/" + str(self.preloadTotal) logging.info(feedback) self.feedBackString.setText(feedback) #self.grabBuiltTile() if self.tileBuilder.queue.qsize() > 0: #logging.info( self.tileBuilder.queue.qsize()) return Task.cont self._setupSimpleTasks() return Task.done #@pstat def makeNewTile(self): """Generate the closest terrain tile needed.""" # tiles are placed under the terrain node path which may be scaled x = self.focus.getX(self) / self.horizontalScale y = self.focus.getY(self) / self.horizontalScale # start position is the focus position rounded to the multiple of tile size xstart = (int(x) / self.tileSize) * self.tileSize ystart = (int(y) / self.tileSize) * self.tileSize # radius is rounded up from minTileDistance to nearest multiple of tile size # not every tile within checkRadius will be made checkRadius = (int(self.minTileDistance) / self.tileSize + 1) * self.tileSize halfTile = self.tileSize * 0.49 tiles = self.tiles #logging.info( xstart, ystart, checkRadius) vec = 0 minFoundDistance = 9999999999.0 minDistanceSq = self.minTileDistance * self.minTileDistance for checkX in range (xstart - checkRadius, xstart + checkRadius, self.tileSize): for checkY in range (ystart - checkRadius, ystart + checkRadius, self.tileSize): if not (checkX, checkY) in tiles: deltaX = x - (checkX + halfTile) deltaY = y - (checkY + halfTile) distanceSq = deltaX * deltaX + deltaY * deltaY if distanceSq < minDistanceSq and distanceSq < minFoundDistance: minFoundDistance = distanceSq vec = (checkX, checkY) if not vec == 0: #logging.info( distance," < ", self.minTileDistance," and ", distance," < ", minDistance) #self.generateTile(vec.getX(), vec.getY()) if THREAD_LOAD_TERRAIN: self.dispatchTile(vec) else: self._generateTile(vec) #@pstat def dispatchTile(self, pos): """Creates a terrain tile at the input coordinates.""" if pos in self.storage: tile = self.storage[pos] self.tiles[pos] = tile tile.getRoot().reparentTo(self) del self.storage[pos] logging.info("tile recovered from storage at " + str(pos)) return self.tileBuilder.build(pos) self.tiles[pos] = 1 #@pstat def _generateTile(self, pos): """Creates a terrain tile at the input coordinates.""" if pos in self.storage: tile = self.storage[pos] self.tiles[pos] = tile tile.getRoot().reparentTo(self) del self.storage[pos] logging.info("tile recovered from storage at " + str(pos)) #self.flattenMedium() return if SAVED_TEXTURE_MAPS: tile = TextureMappedTerrainTile(self, pos[0], pos[1]) elif self.bruteForce: tile = LodTerrainTile(self, pos[0], pos[1]) else: tile = TerrainTile(self, pos[0], pos[1]) tile.make() tile.getRoot().reparentTo(self) self.tiles[pos] = tile logging.info("tile generated at " + str(pos)) #self.flattenMedium() return tile def grabBuiltTile(self): #logging.info( "grabBuiltTile()") tile = self.tileBuilder.grab() #logging.info( "tile = "+ str(tile)) if tile: pos = (tile.xOffset, tile.yOffset) tile.getRoot().reparentTo(self) self.tiles[pos] = tile logging.info("tile generated at " + str(pos)) return tile return None #@pstat def removeOldTiles(self): """Remove distant tiles to free system resources.""" x = self.focus.getX(self) / self.horizontalScale y = self.focus.getY(self) / self.horizontalScale center = self.tileSize * 0.5 maxDistanceSquared = self.maxTileDistance * self.maxTileDistance for pos, tile in self.tiles.items(): deltaX = x - (pos[0] + center) deltaY = y - (pos[1] + center) distance = deltaX * deltaX + deltaY * deltaY if distance > maxDistanceSquared: #logging.info( distance+ " > "+ self.maxTileDistance * self.maxTileDistance) self.storeTile(pos) def storeTile(self, pos): tile = self.tiles[pos] if tile != 1: tile.getRoot().detachNode() self.storage[pos] = tile del self.tiles[pos] logging.info("Tile removed from " + str(pos)) def deleteTile(self, pos): """Removes a specific tile from the Terrain.""" self.tiles[pos].getRoot().detachNode() del self.tiles[pos] logging.info("Tile deleted from " + str(pos)) def getElevation(self, x, y): """Returns the height of the terrain at the input world coordinates.""" x /= self.horizontalScale y /= self.horizontalScale if SAVED_HEIGHT_MAPS: tilex = (int(x) / self.tileSize) * self.tileSize tiley = (int(y) / self.tileSize) * self.tileSize x -= tilex y -= tiley if (tilex, tiley) in self.tiles: return self.tiles[tilex, tiley].getElevation(x, y) * self.getSz() if (tilex, tiley) in self.storage: return self.storage[tilex, tiley].getElevation(x, y) * self.getSz() return self.getHeight(x, y) * self.getSz() def setWireFrame(self, state): self.wireFrame = state if state: self.setRenderModeWireframe() else: self.setRenderModeFilled() #for pos, tile in self.tiles.items(): # tile.setWireFrame(state) def toggleWireFrame(self): self.setWireFrame(not self.wireFrame) def test(self): self.texturer.test() def setShaderFloatInput(self, name, input): logging.info("set shader input " + name + " to " + str(input)) self.setShaderInput(name, PTAFloat([input])) def setFocus(self, nodePath): self.focus = nodePath for pos, tile in self.tiles.items(): tile.setFocalPoint(self.focus)
class Terrain(NodePath): """A terrain contains a set of geomipmaps, and maintains their common properties.""" def __init__(self, name, focus, maxRange, populator=None, feedBackString=None, id=0): """Create a new terrain centered on the focus. The focus is the NodePath where the LOD is the greatest. id is a seed for the map and unique name for any cached heightmap images """ NodePath.__init__(self, name) ### Basic Parameters self.name = name # nodepath to center of the level of detail self.focus = focus # stores all terrain tiles that make up the terrain self.tiles = {} # stores previously built tiles we can readd to the terrain self.storage = {} self.feedBackString = feedBackString if populator == None: populator = TerrainPopulator() self.populator = populator self.graphReducer = SceneGraphReducer() if THREAD_LOAD_TERRAIN: self.tileBuilder = TerrainTileBuilder(self) ##### Terrain Tile physical properties self.maxHeight = MAX_TERRAIN_HEIGHT self.tileSize = 128 self.heightMapSize = self.tileSize + 1 ##### Terrain scale and tile distances # distances are measured in tile's smallest unit # conversion to world units may be necessary # Don't show untiled terrain below this distance etc. # scale the terrain vertically to its maximum height self.setSz(self.maxHeight) # scale horizontally to appearance/performance balance self.horizontalScale = TERRAIN_HORIZONTAL_STRETCH self.setSx(self.horizontalScale) self.setSy(self.horizontalScale) # waterHeight is expressed as a multiplier to the max height self.waterHeight = 0.3 #this is the furthest the camera can view self.maxViewRange = maxRange # Add half the tile size because distance is checked from the center, # not from the closest edge. self.minTileDistance = self.maxViewRange / self.horizontalScale + self.tileSize / 2 # to avoid excessive store / retrieve behavior on tiles we have a small # buffer where it doesn't matter whether or not the tile is present self.maxTileDistance = self.minTileDistance + self.tileSize / 2 ##### heightmap properties self.initializeHeightMap(id) ##### rendering properties self.initializeRenderingProperties() ##### task handling #self._setupThreadedTasks() # newTile is a placeholder for a tile currently under construction # this has to be initialized last because it requires values from self #self.newTile = TerrainTile(self, 0, 0) # loads all terrain tiles in range immediately if THREAD_LOAD_TERRAIN: self.preload(self.focus.getX() / self.horizontalScale, self.focus.getY() / self.horizontalScale) else: taskMgr.add(self.oldPreload, "preloadTask", extraArgs=[self.focus.getX() / self.horizontalScale, self.focus.getY() / self.horizontalScale]) #self.flattenLight() def initializeHeightMap(self, id=0): """ """ logging.info("initializing heightmap...") if id == 0: self.dice = RandomNumGen(TimeVal().getUsec()) id = self.dice.randint(2, 1000000) self.id = id #Remove old tiles that will not conform to a new heightmap for pos, tile in self.tiles.items(): self.deleteTile(pos) self.storage.clear() self.heightMap = HeightMap(id, self.waterHeight + 0.03) self.getHeight = self.heightMap.getHeight def initializeRenderingProperties(self): logging.info("initializing terrain rendering properties...") #self.bruteForce = True self.bruteForce = BRUTE_FORCE_TILES if self.bruteForce: self.blockSize = self.tileSize else: #self.blockSize = 16 self.blockSize = self.tileSize self.near = 100 self.far = self.maxViewRange * 0.5 + self.blockSize self.wireFrame = 0 #self.texturer = MonoTexturer(self) self.texturer = ShaderTexturer(self) #self.texturer = DetailTexturer(self) #self.texturer.apply(self) #self.setShaderInput("zMultiplier", ) logging.info("rendering properties initialized...") def _setupSimpleTasks(self): """This sets up tasks to maintain the terrain as the focus moves.""" logging.info("initializing terrain update task...") ##Add tasks to keep updating the terrain #taskMgr.add(self.updateTilesTask, "updateTiles", sort=9, priority=0) taskMgr.doMethodLater(5, self.update, "update", sort=9, priority=0) self.updateStep = 1 def reduceSceneGraph(self, radius): gr = self.graphReducer gr.applyAttribs(self.node()) gr.setCombineRadius(radius) gr.flatten(self.node(), SceneGraphReducer.CSRecurse) gr.makeCompatibleState(self.node()) gr.collectVertexData(self.node()) gr.unify(self.node(), False) def update(self, task): """This task updates terrain as needed.""" if self.updateStep == 1: self.makeNewTile() if THREAD_LOAD_TERRAIN: self.grabBuiltTile() self.removeOldTiles() self.updateStep += 1 return Task.cont #self.updateTiles() self.tileLodUpdate() #self.buildDetailLevels() self.updateStep = 1 return Task.cont def updateLight(self): """This task moves point and directional lights. For larger projects this should be externalized. """ self.pointLight = vec3(0, 5, 0)#self.focus.getPos() + vec3(0,5,0) self.setShaderInput("LightPosition", self.pointLight) def updateTiles(self): """This task updates each tile, which updates the LOD. GeoMipMap updates are slow however and may cause unacceptable lag. """ #logging.info(self.focus.getPos()) for pos, tile in self.tiles.items(): tile.update() #logging.info(str(tile.getFocalPoint().getPos())) #if tile.update(): #logging.info("update success") #yield Task.cont def tileLodUpdate(self): """Updates tiles to LOD appropriate for their distance. setMinDetailLevel() doesn't flag a geomipterrain as dirty, so update will not alter detail level. It would have to be regenerated. Instead we will use a special LodTerrainTile. """ if not self.bruteForce: self.updateTiles() return focusx = self.focus.getX() / self.horizontalScale focusy = self.focus.getY() / self.horizontalScale halfTile = self.tileSize * 0.5 # switch to high, mid, and low LOD's at these distances # having a gap between the zones avoids switching back and forth too # if the focus is moving erratically highOuter = self.minTileDistance * 0.02 + self.tileSize highOuter *= highOuter midInner = self.minTileDistance * 0.02 + self.tileSize + halfTile midInner *= midInner midOuter = self.minTileDistance * 0.2 + self.tileSize midOuter *= midOuter lowInner = self.minTileDistance * 0.2 + self.tileSize + halfTile lowInner *= lowInner lowOuter = self.minTileDistance * 0.5 + self.tileSize lowOuter *= lowOuter horizonInner = self.minTileDistance * 0.5 + self.tileSize + halfTile horizonInner *= horizonInner for pos, tile in self.tiles.items(): deltaX = focusx - (pos[0] + halfTile) deltaY = focusy - (pos[1] + halfTile) distance = deltaX * deltaX + deltaY * deltaY if distance < highOuter: tile.setDetail(0) elif distance < midOuter: if distance > midInner or tile.getDetail() > 1: tile.setDetail(1) elif distance < lowOuter: if distance > lowInner or tile.getDetail() > 2: tile.setDetail(2) elif distance > horizonInner: tile.setDetail(3) def buildDetailLevels(self): """Unused.""" n = len(self.buildQueue) / 5.0 if n > 0 and n < 1: n = 1 else: n = int(n) for i in range(n): request = self.buildQueue.popleft() request[0].buildAndSet(request[1]) def oldPreload(self, task, xpos=0, ypos=0): """Loads all tiles in range immediately. This can suspend the program for a long time and is best used when first loading a level. It simply iterates through a square region building any tile that is reasonably within the max distance. It does not prioritize tiles closest to the focus. """ logging.info("preloading terrain tiles...") self.buildQueue = deque() # x and y start are rounded to the nearest multiple of tile size xstart = (int(xpos / self.horizontalScale) / self.tileSize) * self.tileSize ystart = (int(ypos / self.horizontalScale) / self.tileSize) * self.tileSize # check radius is rounded up to the nearest tile size from maxTileDistance # not every tile in checkRadius will be made checkRadius = (int(self.maxTileDistance) / self.tileSize + 1) * self.tileSize halfTile = self.tileSize * 0.5 # build distance for the preloader will be halfway in between the normal # load distance and the unloading distance buildDistanceSquared = (self.minTileDistance + self.maxTileDistance) / 2 buildDistanceSquared = buildDistanceSquared * buildDistanceSquared for x in range (xstart - checkRadius, xstart + checkRadius, self.tileSize): for y in range (ystart - checkRadius, ystart + checkRadius, self.tileSize): if not (x, y) in self.tiles: deltaX = xpos - (x + halfTile) deltaY = ypos - (y + halfTile) distanceSquared = deltaX * deltaX + deltaY * deltaY if distanceSquared < buildDistanceSquared: self.buildQueue.append((x, y)) total = len(self.buildQueue) while len(self.buildQueue): if self.feedBackString: done = total - len(self.buildQueue) feedback = "Loading Terrain " + str(done) + "/" + str(total) logging.info(feedback) self.feedBackString.setText(feedback) tile = self.buildQueue.popleft() self._generateTile(tile) yield Task.cont self._setupSimpleTasks() yield Task.done def preload(self, xpos=1, ypos=1): """ """ logging.info("preloading terrain tiles...") # x and y start are rounded to the nearest multiple of tile size xstart = (int(xpos / self.horizontalScale) / self.tileSize) * self.tileSize ystart = (int(ypos / self.horizontalScale) / self.tileSize) * self.tileSize # check radius is rounded up to the nearest tile size from maxTileDistance # not every tile in checkRadius will be made checkRadius = (int(self.maxTileDistance) / self.tileSize + 1) * self.tileSize halfTile = self.tileSize * 0.5 # build distance for the preloader will be halfway in between the normal # load distance and the unloading distance buildDistanceSquared = (self.minTileDistance + self.maxTileDistance) / 2 buildDistanceSquared = buildDistanceSquared * buildDistanceSquared for x in range (xstart - checkRadius, xstart + checkRadius, self.tileSize): for y in range (ystart - checkRadius, ystart + checkRadius, self.tileSize): if not (x, y) in self.tiles: deltaX = xpos - (x + halfTile) deltaY = ypos - (y + halfTile) distanceSquared = deltaX * deltaX + deltaY * deltaY if distanceSquared < buildDistanceSquared: self.tileBuilder.preload((x, y)) self.preloadTotal = self.tileBuilder.queue.qsize() taskMgr.add(self.preloadWait, "preloadWaitTask") def preloadWait(self, task): #logging.info( "preloadWait()") if self.feedBackString: done = self.preloadTotal - self.tileBuilder.queue.qsize() feedback = "Loading Terrain " + str(done) + "/" + str(self.preloadTotal) logging.info(feedback) self.feedBackString.setText(feedback) #self.grabBuiltTile() if self.tileBuilder.queue.qsize() > 0: #logging.info( self.tileBuilder.queue.qsize()) return Task.cont self._setupSimpleTasks() return Task.done #@pstat def makeNewTile(self): """Generate the closest terrain tile needed.""" # tiles are placed under the terrain node path which may be scaled x = self.focus.getX(self) / self.horizontalScale y = self.focus.getY(self) / self.horizontalScale # start position is the focus position rounded to the multiple of tile size xstart = (int(x) / self.tileSize) * self.tileSize ystart = (int(y) / self.tileSize) * self.tileSize # radius is rounded up from minTileDistance to nearest multiple of tile size # not every tile within checkRadius will be made checkRadius = (int(self.minTileDistance) / self.tileSize + 1) * self.tileSize halfTile = self.tileSize * 0.49 tiles = self.tiles #logging.info( xstart, ystart, checkRadius) vec = 0 minFoundDistance = 9999999999.0 minDistanceSq = self.minTileDistance * self.minTileDistance for checkX in range (xstart - checkRadius, xstart + checkRadius, self.tileSize): for checkY in range (ystart - checkRadius, ystart + checkRadius, self.tileSize): if not (checkX, checkY) in tiles: deltaX = x - (checkX + halfTile) deltaY = y - (checkY + halfTile) distanceSq = deltaX * deltaX + deltaY * deltaY if distanceSq < minDistanceSq and distanceSq < minFoundDistance: minFoundDistance = distanceSq vec = (checkX, checkY) if not vec == 0: #logging.info( distance," < ",self.minTileDistance," and ",distance," < ",minDistance) #self.generateTile(vec.getX(), vec.getY()) if THREAD_LOAD_TERRAIN: self.dispatchTile(vec) else: self._generateTile(vec) #@pstat def dispatchTile(self, pos): """Creates a terrain tile at the input coordinates.""" if pos in self.storage: tile = self.storage[pos] self.tiles[pos] = tile tile.getRoot().reparentTo(self) del self.storage[pos] logging.info("tile recovered from storage at " + str(pos)) return self.tileBuilder.build(pos) self.tiles[pos] = 1 #@pstat def _generateTile(self, pos): """Creates a terrain tile at the input coordinates.""" if pos in self.storage: tile = self.storage[pos] self.tiles[pos] = tile tile.getRoot().reparentTo(self) del self.storage[pos] logging.info("tile recovered from storage at " + str(pos)) #self.flattenMedium() return if SAVED_TEXTURE_MAPS: tile = TextureMappedTerrainTile(self, pos[0], pos[1]) elif self.bruteForce: tile = LodTerrainTile(self, pos[0], pos[1]) else: tile = TerrainTile(self, pos[0], pos[1]) tile.make() tile.getRoot().reparentTo(self) self.tiles[pos] = tile logging.info("tile generated at " + str(pos)) #self.flattenMedium() return tile def grabBuiltTile(self): #logging.info( "grabBuiltTile()") tile = self.tileBuilder.grab() #logging.info( "tlie = "+ str(tile)) if tile: pos = (tile.xOffset, tile.yOffset) tile.getRoot().reparentTo(self) self.tiles[pos] = tile logging.info("tile generated at " + str(pos)) return tile return None #@pstat def removeOldTiles(self): """Remove distant tiles to free system resources.""" x = self.focus.getX(self) / self.horizontalScale y = self.focus.getY(self) / self.horizontalScale center = self.tileSize * 0.5 maxDistanceSquared = self.maxTileDistance * self.maxTileDistance for pos, tile in self.tiles.items(): deltaX = x - (pos[0] + center) deltaY = y - (pos[1] + center) distance = deltaX * deltaX + deltaY * deltaY if distance > maxDistanceSquared: #logging.info( distance+ " > "+ self.maxTileDistance * self.maxTileDistance) self.storeTile(pos) def storeTile(self, pos): tile = self.tiles[pos] if tile != 1: tile.getRoot().detachNode() self.storage[pos] = tile del self.tiles[pos] logging.info("Tile removed from " + str(pos)) def deleteTile(self, pos): """Removes a specific tile from the Terrain.""" self.tiles[pos].getRoot().detachNode() del self.tiles[pos] logging.info("Tile deleted from " + str(pos)) def getElevation(self, x, y): """Returns the height of the terrain at the input world coordinates.""" x /= self.horizontalScale y /= self.horizontalScale if SAVED_HEIGHT_MAPS: tilex = (int(x) / self.tileSize) * self.tileSize tiley = (int(y) / self.tileSize) * self.tileSize x -= tilex y -= tiley if (tilex, tiley) in self.tiles: return self.tiles[tilex, tiley].getElevation(x, y) * self.getSz() if (tilex, tiley) in self.storage: return self.storage[tilex, tiley].getElevation(x, y) * self.getSz() return self.getHeight(x, y) * self.getSz() def setWireFrame(self, state): self.wireFrame = state if state: self.setRenderModeWireframe() else: self.setRenderModeFilled() #for pos, tile in self.tiles.items(): # tile.setWireFrame(state) def toggleWireFrame(self): self.setWireFrame(not self.wireFrame) def test(self): self.texturer.test() def setShaderFloatInput(self, name, input): logging.info("set shader input " + name + " to " + str(input)) self.setShaderInput(name, PTAFloat([input])) def setFocus(self, nodePath): self.focus = nodePath for pos, tile in self.tiles.items(): tile.setFocalPoint(self.focus)
def setState(self, state, seed, timestamp): self.seed = seed if not self.random: self.random = RandomNumGen.RandomNumGen(seed) self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)])
def generate(self): """generate(self) This method is called when the DistributedObject is reintroduced to the world, either for the first time or from the cache. """ DistributedObject.DistributedObject.generate(self) if self.butterfly: return self.butterfly = Actor.Actor() self.butterfly.loadModel('phase_4/models/props/SZ_butterfly-mod.bam') self.butterfly.loadAnims({ 'flutter': 'phase_4/models/props/SZ_butterfly-flutter.bam', 'glide': 'phase_4/models/props/SZ_butterfly-glide.bam', 'land': 'phase_4/models/props/SZ_butterfly-land.bam' }) # Randomly choose one of the butterfly wing patterns index = self.doId % len(self.wingTypes) chosenType = self.wingTypes[index] node = self.butterfly.getGeomNode() for type in self.wingTypes: wing = node.find('**/' + type) if (type != chosenType): wing.removeNode() else: # Choose an appropriate blend color if (index == 0 or index == 1): color = self.yellowColors[self.doId % len(self.yellowColors)] elif (index == 2 or index == 3): color = self.whiteColors[self.doId % len(self.whiteColors)] elif (index == 4): color = self.paleYellowColors[self.doId % len(self.paleYellowColors)] else: color = Vec4(1, 1, 1, 1) wing.setColor(color) # Make another copy of the butterfly model so we can LOD the # blending. Butterflies that are far away won't bother to # blend animations; nearby butterflies will use dynamic # blending to combine two or more animations at once on # playback for a nice fluttering and landing effect. self.butterfly2 = Actor.Actor(other=self.butterfly) # Allow the nearby butterfly to blend between its three # animations. All animations will be playing all the time; # we'll control which one is visible by varying the control # effect. self.butterfly.enableBlend(blendType=PartBundle.BTLinear) self.butterfly.loop('flutter') self.butterfly.loop('land') self.butterfly.loop('glide') # Make a random play rate so all the butterflies will be # flapping at slightly different rates. This doesn't affect # the rate at which the butterfly moves, just the rate at # which the animation plays on the butterfly. rng = RandomNumGen.RandomNumGen(self.doId) playRate = 0.6 + 0.8 * rng.random() self.butterfly.setPlayRate(playRate, 'flutter') self.butterfly.setPlayRate(playRate, 'land') self.butterfly.setPlayRate(playRate, 'glide') self.butterfly2.setPlayRate(playRate, 'flutter') self.butterfly2.setPlayRate(playRate, 'land') self.butterfly2.setPlayRate(playRate, 'glide') # Also, a random glide contribution ratio. We'll blend a bit # of the glide animation in with the flutter animation to # dampen the effect of flutter. The larger the number here, # the greater the dampening effect. Some butterflies will be # more active than others. (Except when seen from a long way # off, because of the LODNode, below.) self.glideWeight = rng.random() * 2 lodNode = LODNode('butterfly-node') lodNode.addSwitch(100, 40) # self.butterfly2 lodNode.addSwitch(40, 0) # self.butterfly self.butterflyNode = NodePath(lodNode) self.butterfly2.setH(180.0) self.butterfly2.reparentTo(self.butterflyNode) self.butterfly.setH(180.0) self.butterfly.reparentTo(self.butterflyNode) self.__initCollisions() # Set up the drop shadow self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') self.dropShadow.setColor(0, 0, 0, 0.3) self.dropShadow.setPos(0, 0.1, -0.05) self.dropShadow.setScale(self.shadowScaleBig) self.dropShadow.reparentTo(self.butterfly)
def generateCard(self, tileSeed, zoneId): assert (self.game != None) rng = RandomNumGen.RandomNumGen(tileSeed) rowSize = self.game.getRowSize() # Retrieve a list of Fish based on the Genus Type. Each Genus # found in the pond will be represented on the board. fishList = FishGlobals.getPondGeneraList(zoneId) # Go through the fish list and generate actual fish. # NOTE: This should likely be removed when the fish logos come into play. # There is no need to generate a Fish object. Tuples (genus, species) # can effectively be used to identify the type of fish for a specific # BingoCardCell. for i in xrange(len(fishList)): fishTuple = fishList.pop(0) weight = FishGlobals.getRandomWeight(fishTuple[0], fishTuple[1]) fish = FishBase.FishBase(fishTuple[0], fishTuple[1], weight) fishList.append(fish) # Determine the number of cells left to fill. emptyCells = (self.game.getCardSize() - 1) - len(fishList) # Fill up the empty cells with randomly generated fish. In order to # maintain fairness, iterate through the rods as well. rodId = 0 for i in xrange(emptyCells): fishVitals = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) while (not fishVitals[0]): fishVitals = FishGlobals.getRandomFishVitals( zoneId, rodId, rng) fish = FishBase.FishBase(fishVitals[1], fishVitals[2], fishVitals[3]) fishList.append(fish) rodId += 1 if rodId > 4: rodId = 0 # Now that we have generated all of the fish that will make up the card, # it is time to actually generate a BingoCardCell for every fish. This # cell will be parented to the GUI instance and its position and scale # are based on the CardImageScale. (See base positions above) for i in xrange(rowSize): for j in xrange(self.game.getColSize()): color = self.getCellColor(i * rowSize + j) if i * rowSize + j == self.game.getCardSize() / 2: tmpFish = 'Free' else: choice = rng.randrange(0, len(fishList)) tmpFish = fishList.pop(choice) xPos = BG.CellImageScale * (j - 2) + BG.GridXOffset yPos = BG.CellImageScale * (i - 2) - 0.015 cellGui = BingoCardCell.BingoCardCell( i * rowSize + j, tmpFish, self.model, color, self, image_scale=BG.CellImageScale, pos=(xPos, 0, yPos), ) self.cellGuiList.append(cellGui)
class _Terrain(): """A terrain contains a set of geomipmaps, and maintains their common properties.""" def __init__(self): """Create a new terrain centered on the focus. The focus is the NodePath where the LOD is the greatest. id is a seed for the map and unique name for any cached heightmap images """ ##### Terrain Tile physical properties self.maxHeight = 300 self.dice = RandomNumGen(TimeVal().getUsec()) self.id = self.dice.randint(2, 1000000) # scale the terrain vertically to its maximum height #self.setSz(self.maxHeight) # scale horizontally to appearance/performance balance #self.horizontalScale = 1.0 #self.setSx(self.horizontalScale) #self.setSy(self.horizontalScale) ##### heightmap properties self.initializeHeightMap(id) def initializeHeightMap(self, id=0): """ """ # the overall smoothness/roughness of the terrain self.smoothness = 80 # how quickly altitude and roughness shift self.consistency = self.smoothness * 8 # waterHeight is expressed as a multiplier to the max height self.waterHeight = 0.3 # for realism the flatHeight should be at or very close to waterHeight self.flatHeight = self.waterHeight + 0.04 #creates noise objects that will be used by the getHeight function self.generateNoiseObjects() def generateNoiseObjects(self): """Create perlin noise.""" # See getHeight() for more details.... # where perlin 1 is low terrain will be mostly low and flat # where it is high terrain will be higher and slopes will be exagerrated # increase perlin1 to create larger areas of geographic consistency self.perlin1 = StackedPerlinNoise2() perlin1a = PerlinNoise2(0, 0, 256, seed=self.id) perlin1a.setScale(self.consistency) self.perlin1.addLevel(perlin1a) perlin1b = PerlinNoise2(0, 0, 256, seed=self.id * 2 + 123) perlin1b.setScale(self.consistency / 2) self.perlin1.addLevel(perlin1b, 1 / 2) # perlin2 creates the noticeable noise in the terrain # without perlin2 everything would look unnaturally smooth and regular # increase perlin2 to make the terrain smoother self.perlin2 = StackedPerlinNoise2() frequencySpread = 3.0 amplitudeSpread = 3.4 perlin2a = PerlinNoise2(0, 0, 256, seed=self.id * 2) perlin2a.setScale(self.smoothness) self.perlin2.addLevel(perlin2a) perlin2b = PerlinNoise2(0, 0, 256, seed=self.id * 3 + 3) perlin2b.setScale(self.smoothness / frequencySpread) self.perlin2.addLevel(perlin2b, 1 / amplitudeSpread) perlin2c = PerlinNoise2(0, 0, 256, seed=self.id * 4 + 4) perlin2c.setScale(self.smoothness / (frequencySpread * frequencySpread)) self.perlin2.addLevel(perlin2c, 1 / (amplitudeSpread * amplitudeSpread)) perlin2d = PerlinNoise2(0, 0, 256, seed=self.id * 5 + 5) perlin2d.setScale(self.smoothness / (math.pow(frequencySpread, 3))) self.perlin2.addLevel(perlin2d, 1 / (math.pow(amplitudeSpread, 3))) perlin2e = PerlinNoise2(0, 0, 256, seed=self.id * 6 + 6) perlin2e.setScale(self.smoothness / (math.pow(frequencySpread, 4))) self.perlin2.addLevel(perlin2e, 1 / (math.pow(amplitudeSpread, 4))) def getHeight(self, x, y): """Returns the height at the specified terrain coordinates. The values returned should be between 0 and 1 and use the full range. Heights should be the smoothest and flatest at flatHeight. """ # all of these should be in the range of 0 to 1 p1 = (self.perlin1(x, y) + 1) / 2 # low frequency p2 = (self.perlin2(x, y) + 1) / 2 # high frequency fh = self.flatHeight # p1 varies what kind of terrain is in the area, p1 alone would be smooth # p2 introduces the visible noise and roughness # when p1 is high the altitude will be high overall # when p1 is close to fh most of the visible noise will be muted return (p1 - fh + (p1 - fh) * (p2 - fh)) / 2 + fh
def onstage(self): self.notify.debug("onstage") DistributedMinigame.onstage(self) # start up the minigame; parent things to render, start playing # music... # at this point we cannot yet show the remote players' toons self.maze.onstage() # place the toons in random starting lineups by # shuffling the starting position list self.randomNumGen.shuffle(self.startPosHTable) lt = base.localAvatar lt.reparentTo(render) lt.hideName() self.__placeToon(self.localAvId) lt.setAnimState('Happy', 1.0) lt.setSpeed(0, 0) self.camParent = render.attachNewNode('mazeGameCamParent') self.camParent.reparentTo(base.localAvatar) self.camParent.setPos(0, 0, 0) self.camParent.setHpr(render, 0, 0, 0) camera.reparentTo(self.camParent) camera.setPos(self.camOffset) self.__spawnCameraTask() # create random num generators for each toon self.toonRNGs = [] for i in range(self.numPlayers): self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen)) # create the treasures self.treasures = [] for i in range(self.maze.numTreasures): self.treasures.append( MazeTreasure.MazeTreasure(self.treasureModel, self.maze.treasurePosList[i], i, self.doId)) self.__loadSuits() for suit in self.suits: suit.onstage() # create an instance of each sound so that they can be # played simultaneously, one for each toon # these sounds must be loaded here (and not in load()) because # we don't know how many players there will be until the # minigame has recieved all required fields self.sndTable = { "hitBySuit": [None] * self.numPlayers, "falling": [None] * self.numPlayers, } for i in range(self.numPlayers): self.sndTable["hitBySuit"][i] = base.loadSfx( "phase_4/audio/sfx/MG_Tag_C.mp3" #"phase_4/audio/sfx/MG_cannon_fire_alt.mp3" ) self.sndTable["falling"][i] = base.loadSfx( "phase_4/audio/sfx/MG_cannon_whizz.mp3") # load a few copies of the grab sound self.grabSounds = [] for i in range(5): self.grabSounds.append( base.loadSfx("phase_4/audio/sfx/MG_maze_pickup.mp3")) # play the sounds round-robin self.grabSoundIndex = 0 # fill in the toonHitTracks dict with bogus tracks for avId in self.avIdList: self.toonHitTracks[avId] = Wait(0.1) self.scores = [0] * self.numPlayers # this will show what percentage of the treasures # have been picked up self.goalBar = DirectWaitBar( parent=render2d, relief=DGG.SUNKEN, frameSize=(-0.35, 0.35, -0.15, 0.15), borderWidth=(0.02, 0.02), scale=0.42, pos=(.84, 0, (0.5 - .28 * self.numPlayers) + .05), barColor=(0, 0.7, 0, 1), ) self.goalBar.setBin('unsorted', 0) self.goalBar.hide() self.introTrack = self.getIntroTrack() self.introTrack.start()
def __init__(self, avId): self.avId = avId self.mainRandomGen = RandomNumGen.RandomNumGen(self.avId) self.attackRandomGen = RandomNumGen.RandomNumGen(self.avId) self.mainCounter = 0 self.attackCounter = 0
def generate(self): DistributedObject.DistributedObject.generate(self) if self.butterfly: return self.butterfly = Actor.Actor() self.butterfly.loadModel('phase_4/models/props/SZ_butterfly-mod') self.butterfly.loadAnims({ 'flutter': 'phase_4/models/props/SZ_butterfly-flutter', 'glide': 'phase_4/models/props/SZ_butterfly-glide', 'land': 'phase_4/models/props/SZ_butterfly-land' }) self.butterfly.setBlend( frameBlend=config.GetBool('interpolate-animations', True)) index = self.doId % len(self.wingTypes) chosenType = self.wingTypes[index] node = self.butterfly.getGeomNode() for type in self.wingTypes: wing = node.find('**/' + type) if type != chosenType: wing.removeNode() else: if base.cr.isHalloween: color = self.halloweenColors[(self.doId % len(self.halloweenColors))] else: if index == 0 or index == 1: color = self.yellowColors[(self.doId % len(self.yellowColors))] else: if index == 2 or index == 3: color = self.whiteColors[(self.doId % len(self.whiteColors))] else: if index == 4: color = self.paleYellowColors[( self.doId % len(self.paleYellowColors))] else: color = Vec4(1, 1, 1, 1) wing.setColor(color) self.butterfly2 = Actor.Actor(other=self.butterfly) self.butterfly.enableBlend(blendType=PartBundle.BTLinear) self.butterfly.setBlend( frameBlend=config.GetBool('interpolate-animations', True)) self.butterfly.loop('flutter') self.butterfly.loop('land') self.butterfly.loop('glide') rng = RandomNumGen.RandomNumGen(self.doId) playRate = 0.6 + 0.8 * rng.random() self.butterfly.setPlayRate(playRate, 'flutter') self.butterfly.setPlayRate(playRate, 'land') self.butterfly.setPlayRate(playRate, 'glide') self.butterfly2.setPlayRate(playRate, 'flutter') self.butterfly2.setPlayRate(playRate, 'land') self.butterfly2.setPlayRate(playRate, 'glide') self.glideWeight = rng.random() * 2 lodNode = LODNode('butterfly-node') lodNode.addSwitch(100, 40) lodNode.addSwitch(40, 0) self.butterflyNode = NodePath(lodNode) self.butterfly2.setH(180.0) self.butterfly2.reparentTo(self.butterflyNode) self.butterfly.setH(180.0) self.butterfly.reparentTo(self.butterflyNode) self.__initCollisions() self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') self.dropShadow.setColor(0, 0, 0, 0.3) self.dropShadow.setPos(0, 0.1, -0.05) self.dropShadow.setScale(self.shadowScaleBig) self.dropShadow.reparentTo(self.butterfly)