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 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 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) # 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 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 __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 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) # 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 __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 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 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 __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 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 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 advanceAttackSeed(self): seedVal = self.mainRandomGen.randint(0, (1 << 16) - 1) self.mainCounter += 1 self.attackCounter = 0 self.attackRandomGen = RandomNumGen.RandomNumGen(seedVal)
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)
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)