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)]
Example #3
0
def getMazeName(gameDoId, numPlayers, mazeNames):

    try:
        return forcedMaze
    except:
        names = mazeNames[numPlayers - 1]
        return names[RandomNumGen.randHash(gameDoId) % len(names)]
Example #4
0
    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)
Example #7
0
    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
Example #9
0
    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
Example #10
0
 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)
Example #11
0
    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)
Example #12
0
    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()
Example #16
0
    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)
Example #17
0
    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()
Example #19
0
 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()
Example #20
0
	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
Example #21
0
    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)
Example #23
0
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)])
Example #26
0
    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)
Example #27
0
    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
Example #31
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)