def _updateBots(self): self._botUpdateInterval = max(500,self._botUpdateInterval * 0.98) self._botUpdateTimer = bs.Timer(int(self._botUpdateInterval),bs.WeakCall(self._updateBots)) botSpawnPoints = [[-5,5.5,-4.14],[0,5.5,-4.14],[5,5.5,-4.14]] dists = [0,0,0] playerPts = [] for player in self.players: try: if player.isAlive(): playerPts.append(player.actor.node.position) except Exception,e: print 'EXC in _updateBots',e
def _dropPowerups(self,standardPoints=False,powerupType=None): """ Generic powerup drop """ if standardPoints: pts = self.getMap().powerupSpawnPoints for i,pt in enumerate(pts): bs.gameTimer(1000+i*500,bs.WeakCall(self._dropPowerup,i,powerupType if i == 0 else None)) else: pt = (self._powerupCenter[0]+random.uniform(-1.0*self._powerupSpread[0],1.0*self._powerupSpread[0]), self._powerupCenter[1],self._powerupCenter[2]+random.uniform(-self._powerupSpread[1],self._powerupSpread[1])) # drop one random one somewhere.. bs.Powerup(position=pt,powerupType=bs.Powerup.getFactory().getRandomPowerupType(excludeTypes=self._excludePowerups)).autoRetain()
def _updateBots(self): self._botUpdateInterval = max(500, self._botUpdateInterval * 0.98) self._botUpdateTimer = bs.Timer(int(self._botUpdateInterval), bs.WeakCall(self._updateBots)) botSpawnPoints = [[-5, 5.5, -4.14], [0, 5.5, -4.14], [5, 5.5, -4.14]] dists = [0, 0, 0] playerPts = [] for player in self.players: try: if player.isAlive(): playerPts.append(player.actor.node.position) except Exception as e: print 'EXC in _updateBots', e for i in range(3): for p in playerPts: dists[i] += abs(p[0] - botSpawnPoints[i][0]) # little random variation dists[i] += random.random() * 5.0 if dists[0] > dists[1] and dists[0] > dists[2]: pt = botSpawnPoints[0] elif dists[1] > dists[2]: pt = botSpawnPoints[1] else: pt = botSpawnPoints[2] pt = (pt[0] + 3.0 * (random.random() - 0.5), pt[1], 2.0 * (random.random() - 0.5) + pt[2]) # normalize our bot type total and find a random number within that total = 0.0 for t in self._botSpawnTypes.items(): total += t[1][0] r = random.random() * total # now go back through and see where this value falls total = 0 for t in self._botSpawnTypes.items(): total += t[1][0] if r <= total: spazType = t[0] break spawnTime = 1000 self._bots.spawnBot(spazType, pos=pt, spawnTime=spawnTime) # after every spawn we adjust our ratios slightly to get more # difficult.. for t in self._botSpawnTypes.items(): t[1][0] += t[1][1] # increase spawn rate t[1][1] += t[1][2] # increase spawn rate increase rate
def _spawnBomb(self): if self._frontRaceRegion is None: return region = (self._frontRaceRegion + (3)) % len(self._regions) #print 'WOULD SPAWN BOMB AT',bs.getGameTime(),'AT REGION',region pt = self._regions[region]._pt regionScale = 0.8 # dont use the full region so we're less likely to spawn off a cliff xRange = (-0.5, 0.5) if pt[3] == 0 else (-regionScale * pt[3], regionScale * pt[3]) zRange = (-0.5, 0.5) if pt[5] == 0 else (-regionScale * pt[5], regionScale * pt[5]) pt = (pt[0] + random.uniform(*xRange), pt[1] + 1.0, pt[2] + random.uniform(*zRange)) bs.gameTimer(random.randrange(2000), bs.WeakCall(self._spawnBombAtPt, pt))
def _handleDropped(self, m): if self.bombType == 'landMine': self.armTimer = bs.Timer( 1250, bs.WeakCall(self.handleMessage, ArmMessage())) # once we've thrown a sticky bomb we can stick to it.. elif self.bombType == 'sticky': def _safeSetAttr(node, attr, value): if node.exists(): setattr(node, attr, value) #bs.gameTimer(250,bs.Call(_safeSetAttr,self.node,'stickToOwner',True)) bs.gameTimer(250, lambda: _safeSetAttr(self.node, 'stickToOwner', True))
def explode(self): """ Blows up the bomb if it has not yet done so. """ if self._exploded: return self._exploded = True activity = self.getActivity() if activity is not None and self.node.exists(): blast = Blast(position=self.node.position,velocity=self.node.velocity, blastRadius=self.blastRadius,blastType=self.bombType,sourcePlayer=self.sourcePlayer,hitType=self.hitType,hitSubType=self.hitSubType).autoRetain() for c in self._explodeCallbacks: c(self,blast) # we blew up so we need to go away bs.gameTimer(1 ,bs.WeakCall(self.handleMessage, bs.DieMessage()))
def __init__(self, position, team): bsHockey.Puck.__init__(self, position) self.team = team self.tickrate = 100 self._timeout = 5000 / self.tickrate self._count = self._timeout self._tickTimer = bs.Timer(self.tickrate, call=bs.WeakCall(self._tick), repeat=True) self._counter = bs.newNode('text', owner=self.node, attrs={'inWorld':True, 'color': (1, 1, 1, 0.7), 'scale': 0.015, 'shadow': 0.5, 'flatness': 1.0, 'hAlign':'center'}) self.age = 0 self.scored = False self.lastHoldingPlayer = None self.light = None self.movedSinceSpawn = False
def explode(self): if self._exploded: return self._exploded = True activity = self.getActivity() if activity is not None and self.node.exists(): blast = bs.Blast(position=self.node.position, velocity=self.node.velocity, blastRadius=self.blastRadius, blastType=self.bombType, sourcePlayer=self.sourcePlayer, hitType=self.hitType, hitSubType=self.hitSubType).autoRetain() for c in self._explodeCallbacks: c(self, blast) bs.gameTimer(1, bs.WeakCall(self.handleMessage, bs.DieMessage()))
def explode(self): if self._exploded: return self._exploded = True size = int(self.blastRadius) for mod in range(-size, size+1): pos = self.node.position posX = (pos[0] + mod*1.0, pos[1], pos[2]) posY = (pos[0], pos[1], pos[2] + mod*1.0) if Map.inBounds(posX): bs.gameTimer(abs(mod)*150, bs.Call(blast, posX, self.bombType, self.sourcePlayer, self.hitType, self.hitSubType)) if Map.inBounds(posY): bs.gameTimer(abs(mod)*150, bs.Call(blast, posY, self.bombType, self.sourcePlayer, self.hitType, self.hitSubType)) bs.gameTimer(1, bs.WeakCall(self.handleMessage, bs.DieMessage()))
def onBegin(self): bs.TeamGameActivity.onBegin(self) self.setupStandardTimeLimit(self.settings['Time Limit']) self.setupStandardPowerupDrops(enableTNT=False) self._pow = None self._tntDropTimer = bs.Timer(1000 * 30, bs.WeakCall(self._dropPowBox), repeat=True) self._updateIcons() def end(): if len(self._getLivingTeams()) < 2: self._roundEndTimer = bs.Timer(500, self.endGame) bs.gameTimer(1000, bs.Call(end), repeat=True)
def onTransitionIn(self): import bsInternal bs.Activity.onTransitionIn(self) bsInternal._addCleanFrameCallback(bs.WeakCall(self._startPreloads)) self._background = bsUtils.Background(fadeTime=500, startFaded=True, showLogo=False) self._part = 1 self._image = bsUtils.Image(self._tex, transition='fadeIn', modelTransparent=bs.getModel('image4x1'), scale=(800, 200), transitionDelay=500, transitionOutDelay=self._part1Duration - 1300) bs.gameTimer(self._part1Duration, self.end)
def curse(self): if not self._cursed: factory = self.getFactory() self._cursed = True for attr in ['materials', 'rollerMaterials']: materials = getattr(self.node, attr) if not factory.curseMaterial in materials: setattr(self.node, attr, materials + (factory.curseMaterial, )) # -1 specifies no time limit if self.curseTime == -1: self.node.curseDeathTime = -1 else: self.node.curseDeathTime = bs.getGameTime() + 15000 bs.gameTimer(15000, bs.WeakCall(self.curseExplode))
def __init__(self, position=(0, 1, 0), velocity=(5, 0, 5), sourcePlayer=None, owner=None): bs.Actor.__init__(self) activity = bs.getActivity() factory = self.getFactory() # spawn at the provided point self._spawnPos = (position[0], position[1] + 0.1, position[2]) self.node = bs.newNode( "prop", attrs={ 'model': factory.doorModel, 'body': 'sphere', 'colorTexture': factory.texColor, 'reflection': 'soft', 'modelScale': 1, 'bodyScale': 1, 'density': 1, 'reflectionScale': [0.8], 'shadowSize': 0.1, 'position': self._spawnPos, 'velocity': velocity, 'materials': [bs.getSharedObject('objectMaterial'), factory.ballMaterial] }, delegate=self) self.sourcePlayer = sourcePlayer self.owner = owner if factory._autoDisappear: # defaults to True. # Snowballs should melt after some time bs.gameTimer(6500, bs.WeakCall(self._disappear)) self._hitNodes = set() self._used = False self.canUse = False
def __init__(self, color=(1, 1, 1), highlight=(0.5, 0.5, 0.5), character="Spaz", player=None, powerupsExpire=True): bs.PlayerSpaz.__init__(self, color=color, highlight=highlight, character=character, player=player, powerupsExpire=powerupsExpire) self.node.fly = False self.node.hockey = True self.hitPointsMax = self.hitPoints = 1500 # more HP to handle drop bs.gameTimer(self.getActivity().peace_time, bs.WeakCall(self.safeConnectControlsToPlayer))
def respawnPlayerZombie(self, player, respawnTime=None): """ Given a bs.Player, sets up a standard respawn timer, along with the standard counter display, etc. At the end of the respawn period spawnPlayer() will be called if the Player still exists. An explicit 'respawnTime' can optionally be provided (in milliseconds). """ if player is None or not player.exists(): if player is None: bs.printError('None passed as player to respawnPlayer()') else: bs.printError( 'Nonexistant bs.Player passed to respawnPlayer(); call player.exists() to make sure a player is still there.' ) return if player.getTeam() is None: bs.printError('player has no team in respawnPlayer()') return if respawnTime is None: if len(player.getTeam().players) == 1: respawnTime = 3000 elif len(player.getTeam().players) == 2: respawnTime = 5000 elif len(player.getTeam().players) == 3: respawnTime = 6000 else: respawnTime = 7000 # if this standard setting is present, factor it in if 'Respawn Times' in self.settings: respawnTime *= self.settings['Respawn Times'] respawnTime = int(max(1000, respawnTime)) if respawnTime % 1000 != 0: respawnTime -= respawnTime % 1000 # we want whole seconds if player.actor and not self.hasEnded(): import bsSpaz player.gameData['respawnTimer'] = bs.Timer( respawnTime, bs.WeakCall(self.spawnPlayerIfExistsAsZombie, player)) player.gameData['respawnIcon'] = bsSpaz.RespawnIcon( player, respawnTime)
def activateArea(self): mineOK = False if self.exists(): r = self.defRad fudge = self.getActivity().minOverlap #This is the minimum overlap to join owner's territory (not used to check enemy overlap) p1 = self.node.position self.node.maxSpeed = 0.0 #We don't want mines moving around. They could leave their zone. self.damping = 100 #First, confirm that this mine "touches" owner's mines if self.sourcePlayer.exists(): for m in self.sourcePlayer.gameData['mines']: if m.exists() and not m.died: if m.rad != 0: #Don't check un-activated mines p2 = m.node.position diff = (bs.Vector(p1[0]-p2[0],0.0,p1[2]-p2[2])) dist = (diff.length()) if dist < (m.rad + r)-fudge: #We check m.rad just in case it's somehow different. However, this probably shouldn't happen. Unless I change gameplay later. mineOK = True #mine adjoins owner's territory. Will set to false if it also adjoin's enemy though. break #Get out of the loop takeovers = [] if mineOK: for p in self.getActivity().players: if not p is self.sourcePlayer: if p.exists(): for m in p.gameData['mines']: if m.rad != 0.0: #Don't check un-activated mines p2 = m.node.position diff = (bs.Vector(p1[0]-p2[0],0.0,p1[2]-p2[2])) dist = (diff.length()) if dist < m.rad + r: #We check m.rad just in case it's somehowdifferent. However, this probably shouldn't happen. Unless I change gameplay later. mineOK = False takeovers = [] break #If we made it to here and mineOK is true, we can activate. Otherwise, we'll flash red and die. self.zone = bs.newNode('locator',attrs={'shape':'circle','position':self.node.position,'color':self.sourcePlayer.color,'opacity':0.5,'drawBeauty':False,'additive':True}) bs.animateArray(self.zone,'size',1,{0:[0.0],150:[2*r]}) #Make circle at the default radius to show players where it would go if OK if mineOK or self.isHome: self.activated = True self.rad = r #Immediately set this mine's radius else: #mine was not OK keys = {0:(1,0,0),49:(1,0,0),50:(1,1,1),100:(0,1,0)} bs.animateArray(self.zone,'color',3,keys,loop=True) bs.gameTimer(800, bs.WeakCall(self.handleMessage, bs.DieMessage()), repeat=False)
def move(self): px = eval(self.px) py = eval(self.py) pz = eval(self.pz) try: if self.node.exists(): pn = self.node.position # time = self.distance(pn[0],pn[1],pn[2],px,py,pz)*2000 dist = self.distance(pn[0], pn[1], pn[2], px, py, pz) except: time = 1000 print 'Floater Time Error' if self.node.exists(): # bsUtils.animateArray(self.node,'position',3,{0:self.node.position,time:(px,py,pz)}) # bs.gameTimer(int(round(time)),bs.WeakCall(self.move)) self.node.velocity = ((px - pn[0]) / dist, (py - pn[1]) / dist, (pz - pn[2]) / dist) if not self.controlled: bs.gameTimer(int(round(dist * 1000)), bs.WeakCall(self.move))
def onJumpPress(self): """ Called to 'press jump' on this spaz; used for player or AI connections. """ if not self.node.exists() or self.frozen or self.node.knockout > 0.0: return if self.punchCallback is not None: self.punchCallback(self) t = bs.getGameTime() self._punchedNodes = set() # reset this.. if (t - self.lastPunchTime) / 0.3 > self._punchCooldown: self.lastPunchTime = t self.node.punchPressed = True self.landMineCount = 1 if not self.node.holdNode.exists(): bs.gameTimer( 1, bs.WeakCall(self._safePlaySound, self.getFactory().swishSound, 0.8))
def spawnPlayerSpaz(self, player, position=(0, 5, -3), angle=None): s = self.settings name = player.getName() color = player.color highlight = player.highlight players = self.players spaz = PlagueSpaz(color=color, highlight=highlight, character=player.character, sourcePlayer=player) spaz.connectControlsToPlayer(enablePunch=False, enablePickUp=s["Enable Picking Up"], enableRun=s["Enable Running"], enableJump=s["Enable Jumping"]) player.setActor(spaz) spaz.node.addDeathAction( bs.WeakCall(self.handleMessage, PlagueSpazDeathMessage())) spaz.handleMessage( bs.StandMessage(self.getMap().getFFAStartPosition(self.players), 90))
def dropBomb(self): if (self.landMineCount <= 0 and self.bombCount <= 0) or self.frozen: return p = self.node.positionForward v = self.node.velocity droppingBomb = True bombType = self.bombType bomb = PlagueBomb(position=(p[0], p[1] - 0.0, p[2]), velocity=(v[0], v[1], v[2]), bombType='plague', blastRadius=self.blastRadius, sourcePlayer=self.sourcePlayer, owner=self.node).autoRetain() if droppingBomb: self.bombCount -= 1 bomb.node.addDeathAction( bs.WeakCall(self.handleMessage, _BombDiedMessage())) self._pickUp(bomb.node) for c in self._droppedBombCallbacks: c(self, bomb) return bomb
def endGame(self): self._timer.stop() results = bs.TeamGameResults() if self._won: bs.playMusic('Victory') self._awardAchievement( self.settings.get("preset") + " Boss Fight completed") elapsedTime = bs.getGameTime() - self._timer.getStartTime() self.cameraFlash() bs.playSound(self._winSound) for team in self.teams: team.celebrate() # woooo! par-tay! results.setTeamScore(team, elapsedTime) else: self._boss.finalCelebrate() self.fadeToRed() bs.playMusic(None) bs.netTimer(3000, bs.WeakCall(self.end, results))
def __init__(self,position=(0,1,0),color=(1,1,1),materials=[],touchable=True,droppedTimeout=None): """ Instantiate a flag. If 'touchable' is False, the flag will only touch terrain; useful for things like king-of-the-hill where players should not be moving the flag around. 'materials is a list of extra bs.Materials to apply to the flag. If 'droppedTimeout' is provided (in seconds), the flag will die after remaining untouched for that long once it has been moved from its initial position. """ bs.Actor.__init__(self) self._initialPosition = None self._hasMoved = False factory = self.getFactory() if type(materials) is not list: materials = list(materials) # in case they passed a tuple or whatnot.. if not touchable: materials = [factory.noHitMaterial]+materials self.node = bs.newNode("flag", attrs={'position':(position[0],position[1]+0.75,position[2]), 'colorTexture':factory.flagTexture, 'color':color, 'materials':[bs.getSharedObject('objectMaterial'),factory.flagMaterial]+materials}, delegate=self) self._droppedTimeout = droppedTimeout if self._droppedTimeout is not None: self._count = self._droppedTimeout self._tickTimer = bs.Timer(1000,call=bs.WeakCall(self._tick),repeat=True) self._counter = bs.newNode('text',owner=self.node,attrs={'inWorld':True,'color':(1,1,1,0.7),'scale':0.015,'shadow':0.5,'flatness':1.0,'hAlign':'center'}) else: self._counter = None self._heldCount = 0
def __init__(self, color=(1, 1, 1), highlight=(0.5, 0.5, 0.5), character="Spaz", player=None, force_fly=False, allowAim=False): if player is None: return bs.PlayerSpaz.__init__(self, color=color, highlight=highlight, character=character, player=player) self.extras = {} self.archerCoolDown = 0 self.lastDropTime = -10000 self.hitPointsMax = self.hitPoints = 3000 self.shotAngle = 40 self.isUpPressing = False self.isDownPressing = False self.allowAim = allowAim if force_fly: self.node.fly = True self.updateShotAngleTimer = bs.Timer(20, bs.WeakCall(self.updateShotAngle), repeat=True)
def doBust(self): if self.exists(): if not self._exploded: self._exploded = True bs.emitBGDynamics( position=self.node.position, velocity=[v * 0.1 for v in self.node.velocity], count=10, spread=0.1, scale=0.4, chunkType='ice') #Do a muffled punch sound sound = self.getFactory().impactSound bs.playSound(sound, 1.0, position=self.node.position) scl = self.node.modelScale bsUtils.animate(self.node, "modelScale", { 0: scl * 1.0, 20: scl * 0.5, 50: 0.0 }) bs.gameTimer(80, bs.WeakCall(self.handleMessage, bs.DieMessage()))
def handleMessage(self, m): if isinstance(m, bs.OutOfBoundsMessage): self.getActivity().respawnBall((not self.getActivity().possession)) bs.Bomb.handleMessage(self, m) elif isinstance(m, bs.PickedUpMessage): self.heldLast = m.node.getDelegate().getPlayer() self.getActivity().heldLast = self.heldLast if self.heldLast in self.getActivity().teams[0].players: self.getActivity().possession = True else: self.getActivity().possession = False bs.Bomb.handleMessage(self, m) if self.up == True: activity = self.getActivity() bs.gameTimer(3000, bs.WeakCall(activity.jumpBall)) self.up = True elif isinstance(m, ImpactMessage): self.getActivity().handleShot(self) elif isinstance(m, bs.DroppedMessage): self.up = False else: bs.Bomb.handleMessage(self, m)
def spawnPlayer(self, player): #Overloaded for this game to respawn at home instead of random FFA spots if not player.exists(): bs.printError('spawnPlayer() called for nonexistant player') return if player.gameData['home'] is None: pos = self.getMap().getFFAStartPosition(self.players) bomb = myMine(pos, (0.0,0.0,0.0), 0.0, player, None).autoRetain() bomb.isHome = True bomb.handleMessage(bsBomb.ArmMessage()) position = [pos[0],pos[1]+0.3,pos[2]] player.gameData['home'] = position player.gameData['mines'].append(bomb) else: position = player.gameData['home'] spaz = self.spawnPlayerSpaz(player, position) # lets reconnect this player's controls to this # spaz but *without* the ability to attack or pick stuff up spaz.connectControlsToPlayer(enablePunch=True, enableBomb=True, enablePickUp=True) #Wire up the spaz with mines spaz.landMineCount = 1 spaz.node.billboardTexture = self._mineIconTex bs.animate(spaz.node,"billboardOpacity",{0:0.0,100:1.0,400:1.0}) t = bs.getGameTime() if t - spaz.lastMine < spaz.mineTimeout: spaz.node.billboardCrossOut = True bs.gameTimer((spaz.mineTimeout-t+spaz.lastMine),bs.WeakCall(spaz.unCrossBillboard)) spaz.dropEggs = self.settings['Eggs Not Bombs'] spaz.eggsHatch = self.settings['Snowman Eggs'] # also lets have them make some noise when they die.. spaz.playBigDeathSound = True
def dropBomb(self): """ Tell the spaz to drop one of his bombs, and returns the resulting bomb object. If the spaz has no bombs or is otherwise unable to drop a bomb, returns None. """ if (self.landMineCount <= 0 and self.bombCount <= 0) or self.frozen: return p = self.node.positionForward v = self.node.velocity if self.landMineCount > 0: droppingBomb = False self.setLandMineCount(self.landMineCount - 1) bombType = 'landMine' else: droppingBomb = True bombType = self.bombType bomb = Bomb(position=(p[0], p[1] - 0.0, p[2]), velocity=(v[0], v[1], v[2]), bombType=bombType, blastRadius=self.blastRadius, sourcePlayer=self.sourcePlayer, owner=self.node).autoRetain() if droppingBomb: self.bombCount -= 1 bomb.node.addDeathAction( bs.WeakCall(self.handleMessage, bsSpaz._BombDiedMessage())) self._pickUp(bomb.node) for c in self._droppedBombCallbacks: c(self, bomb) return bomb
def spawn_hold_node(self): if self.node is None or not self.node.exists(): return self.delete_hold_node() t = self.node.position t = (t[0], t[1]+1, t[2]) self.hold_node = bs.newNode('prop', owner=self.node, delegate=self, attrs={ 'position': t, 'body': 'box', 'bodyScale': 0.000001, 'model': None, 'modelScale': 0.000001, 'colorTexture': None, 'maxSpeed': 0, 'sticky': True, 'stickToOwner': True, 'owner': self.node, 'materials': []}) self._c = c = bs.newNode('combine', owner=self.hold_node, attrs={'size': 3}) self._c_move = [0, 0, 0] c.input0, c.input1, c.input2 = t self._c.connectAttr('output', self.hold_node, 'position') self._fly_timer = bs.Timer(100, bs.WeakCall(self.move_hold_node, 'all'), repeat=True)
def onBegin(self): bs.TeamGameActivity.onBegin(self) # bs.gameTimer(t,self._decrementMeteorTime,repeat=True) # kick off the first wave in a few seconds t = self.settings[JungleHunterLanguage.spawnDelay] * 1000 if self.settings['Epic Mode']: t /= 4 # bs.gameTimer(t,self._setMeteorTimer) self._timer = bs.OnScreenTimer() self._timer.start() self._updateScoreBoard() bs.gameTimer(4000, self._checkEndGame) # 4秒之后检测一波 self._bots = bs.BotSet() bs.gameTimer(1000, bs.Call(self._bots.spawnBot, NinjaPrey, pos=self.getMap().getFFAStartPosition(self.players), spawnTime=100), repeat=False) bs.gameTimer(t, bs.WeakCall(self.botsGener), repeat=True)
def setScoreText(self, text): """ Utility func to show a message over the flag; handy for scores. """ if not self.node.exists(): return try: exists = self._scoreText.exists() except Exception: exists = False if not exists: startScale = 0.0 math = bs.newNode('math', owner=self.node, attrs={ 'input1': (0, 1.4, 0), 'operation': 'add' }) self.node.connectAttr('position', math, 'input2') self._scoreText = bs.newNode('text', owner=self.node, attrs={ 'text': text, 'inWorld': True, 'scale': 0.02, 'shadow': 0.5, 'flatness': 1.0, 'hAlign': 'center' }) math.connectAttr('output', self._scoreText, 'position') else: startScale = self._scoreText.scale self._scoreText.text = text self._scoreText.color = bs.getSafeColor(self.node.color) bs.animate(self._scoreText, 'scale', {0: startScale, 200: 0.02}) self._scoreTextHideTimer = bs.Timer(1000, bs.WeakCall(self._hideScoreText))