Exemple #1
0
class Suit(Avatar.Avatar):
	healthColors = (Vec4(0, 1, 0, 1),
		Vec4(1, 1, 0, 1),
		Vec4(1, 0.5, 0, 1),
		Vec4(1, 0, 0, 1),
		Vec4(0.3, 0.3, 0.3, 1))
	healthGlowColors = (Vec4(0.25, 1, 0.25, 0.5),
		Vec4(1, 1, 0.25, 0.5),
		Vec4(1, 0.5, 0.25, 0.5),
		Vec4(1, 0.25, 0.25, 0.5),
		Vec4(0.3, 0.3, 0.3, 0))
	medallionColors = {'c': Vec4(0.863, 0.776, 0.769, 1.0),
		's': Vec4(0.843, 0.745, 0.745, 1.0),
		'l': Vec4(0.749, 0.776, 0.824, 1.0),
		'm': Vec4(0.749, 0.769, 0.749, 1.0)}
	health2DmgMultiplier = 2.5
	
	def __init__(self):
		try:
			self.Suit_initialized
			return
		except:
			self.Suit_initialized = 1
		
		Avatar.Avatar.__init__(self)
		self.avatarType = CIGlobals.Suit
		self.name = ''
		self.chat = ''
		self.suit = None
		self.suitHeads = None
		self.suitHead = None
		self.loserSuit = None
		self.healthMeterGlow = None
		self.healthMeter = None
		self.weapon = None
		self.weapon_sfx = None
		self.anim = None
		self.suit_dial = None
		self.shadow = None
		self.balloon_sfx = None
		self.add_sfx = None
		self.explosion = None
		self.largeExp = None
		self.smallExp = None
		self.death_sfx = None
		self.attack = None
		self.wtrajectory = None
		self.throwObjectId = None
		self.hasSpawned = False
		self.condition = 0
		self.type = ""
		self.head = ""
		self.team = ""
		self.isSkele = 0
		self.animFSM = ClassicFSM('Suit', [State('off', self.enterOff, self.exitOff),
								State('neutral', self.enterNeutral, self.exitNeutral),
								State('walk', self.enterWalk, self.exitWalk),
								State('die', self.enterDie, self.exitDie),
								State('attack', self.enterAttack, self.exitAttack),
								State('flydown', self.enterFlyDown, self.exitFlyDown),
								State('pie', self.enterPie, self.exitPie),
								State('win', self.enterWin, self.exitWin),
								State('flyaway', self.enterFlyAway, self.exitFlyAway),
								State('rollodex', self.enterRollodex, self.exitRollodex)], 'off', 'off')
		animStateList = self.animFSM.getStates()
		self.animFSM.enterInitialState()
		
		self.initializeBodyCollisions()
		
	def delete(self):
		try:
			self.Suit_deleted
		except:
			self.Suit_deleted = 1
			if self.suit:
				self.cleanupSuit()
			if self.loserSuit:
				self.cleanupLoserSuit()
			if self.suitHeads:
				self.suitHeads.remove()
				self.suitHeads = None
			if self.suitHead:
				self.suitHead.remove()
				self.suitHead = None
			if self.healthMeterGlow:
				self.healthMeterGlow.remove()
				self.healthMeterGlow = None
			if self.healthMeter:
				self.healthMeter.remove()
				self.healthMeter = None
			if self.shadow:
				self.shadow.remove()
				self.shadow = None
			self.weapon = None
			self.weapon_sfx = None
			self.suit_dial = None
			del self.shadowPlacer
			
	def generateSuit(self, suitType, suitHead, suitTeam, suitHealth, skeleton):
		self.health = suitHealth
		self.maxHealth = suitHealth
		self.generateBody(suitType, suitTeam, suitHead, skeleton)
		self.generateHealthMeter()
		self.generateHead(suitType, suitHead)
		self.setupNameTag()
		Avatar.Avatar.initShadow(self)
		
	def generateBody(self, suitType, suitTeam, suitHead, skeleton):
		if self.suit:
			self.cleanupSuit()
			
		self.team = suitTeam
		self.type = suitType
		self.head = suitHead
		self.isSkele = skeleton
		
		if suitType == "A":
			if skeleton:
				self.suit = Actor("phase_5/models/char/cogA_robot-zero.bam")
			else:
				self.suit = Actor("phase_3.5/models/char/suitA-mod.bam")
			self.suit.loadAnims({"neutral": "phase_4/models/char/suitA-neutral.bam",
							"walk": "phase_4/models/char/suitA-walk.bam",
							"pie": "phase_4/models/char/suitA-pie-small.bam",
							"land": "phase_5/models/char/suitA-landing.bam",
							"throw-object": "phase_5/models/char/suitA-throw-object.bam",
							"throw-paper": "phase_5/models/char/suitA-throw-paper.bam",
							"glower": "phase_5/models/char/suitA-glower.bam",
							"win": "phase_4/models/char/suitA-victory.bam",
							"rollodex": "phase_5/models/char/suitA-roll-o-dex.bam"})
		if suitType == "B":
			if skeleton:
				self.suit = Actor("phase_5/models/char/cogB_robot-zero.bam")
			else:
				self.suit = Actor("phase_3.5/models/char/suitB-mod.bam")
			self.suit.loadAnims({"neutral": "phase_4/models/char/suitB-neutral.bam",
							"walk": "phase_4/models/char/suitB-walk.bam",
							"pie": "phase_4/models/char/suitB-pie-small.bam",
							"land": "phase_5/models/char/suitB-landing.bam",
							"throw-object": "phase_5/models/char/suitB-throw-object.bam",
							"throw-paper": "phase_5/models/char/suitB-throw-paper.bam",
							"glower": "phase_5/models/char/suitB-magic1.bam",
							"win": "phase_4/models/char/suitB-victory.bam",
							"rollodex": "phase_5/models/char/suitB-roll-o-dex.bam"})
		if suitType == "C":
			if skeleton:
				self.suit = Actor("phase_5/models/char/cogC_robot-zero.bam")
			else:
				self.suit = Actor("phase_3.5/models/char/suitC-mod.bam")
			self.suit.loadAnims({"neutral": "phase_3.5/models/char/suitC-neutral.bam",
						"walk": "phase_3.5/models/char/suitC-walk.bam",
						"pie": "phase_3.5/models/char/suitC-pie-small.bam",
						"land": "phase_5/models/char/suitC-landing.bam",
						"throw-object": "phase_3.5/models/char/suitC-throw-paper.bam",
						"throw-paper": "phase_3.5/models/char/suitC-throw-paper.bam",
						"glower": "phase_5/models/char/suitC-glower.bam",
						"win": "phase_4/models/char/suitC-victory.bam"})
		if skeleton:
			self.suit.setTwoSided(1)
		self.suit.getGeomNode().setScale(CIGlobals.SuitScales[suitHead] / CIGlobals.SuitScaleFactors[suitType])
		
		if skeleton:
			if suitTeam == "s":
				self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_sales.jpg")
			elif suitTeam == "m":
				self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_money.jpg")
			elif suitTeam == "l":
				self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_legal.jpg")
			elif suitTeam == "c":
				self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_boss.jpg")
			self.suit.find('**/tie').setTexture(self.suit_tie, 1)
		else:
			self.suit_blazer = loader.loadTexture("phase_3.5/maps/" + suitTeam + "_blazer.jpg")
			self.suit_leg = loader.loadTexture("phase_3.5/maps/" + suitTeam + "_leg.jpg")
			self.suit_sleeve = loader.loadTexture("phase_3.5/maps/" + suitTeam + "_sleeve.jpg")
			
			self.suit.find('**/legs').setTexture(self.suit_leg, 1)
			self.suit.find('**/arms').setTexture(self.suit_sleeve, 1)
			self.suit.find('**/torso').setTexture(self.suit_blazer, 1)
		
			if suitHead == "coldcaller":
				self.suit.find('**/hands').setColor(0.55, 0.65, 1.0, 1.0)
			elif suitHead == "corporateraider":
				self.suit.find('**/hands').setColor(0.85, 0.55, 0.55, 1.0)
			elif suitHead == "bigcheese":
				self.suit.find('**/hands').setColor(0.75, 0.95, 0.75, 1.0)
			elif suitHead == "bloodsucker":
				self.suit.find('**/hands').setColor(0.95, 0.95, 1.0, 1.0)
			elif suitHead == "spindoctor":
				self.suit.find('**/hands').setColor(0.5, 0.8, 0.75, 1.0)
			elif suitHead == "legaleagle":
				self.suit.find('**/hands').setColor(0.25, 0.25, 0.5, 1.0)
			elif suitHead == "pennypincher":
				self.suit.find('**/hands').setColor(1.0, 0.5, 0.6, 1.0)
			elif suitHead == "loanshark":
				self.suit.find('**/hands').setColor(0.5, 0.85, 0.75, 1.0)
			else:
				self.suit.find('**/hands').setColor(CIGlobals.SuitHandColors[suitTeam])
		
		self.suit.reparentTo(self)
		
	def generateHead(self, suitType, suitHead):
		if self.suitHead:
			self.cleanupSuitHead()
			
		self.type = suitType
		self.head = suitHead
		
		if suitHead == "vp":
			self.suitHead = Actor("phase_9/models/char/sellbotBoss-head-zero.bam",
								{"neutral": "phase_9/models/char/bossCog-head-Ff_neutral.bam"})
			self.suitHead.setTwoSided(True)
			self.suitHead.loop("neutral")
			self.suitHead.setScale(0.35)
			self.suitHead.setHpr(270, 0, 270)
			self.suitHead.setZ(-0.10)
		else:
			if suitType == "A" or suitType == "B":
				self.suitHeads = loader.loadModel("phase_4/models/char/suit" + suitType + "-heads.bam")
			else:
				self.suitHeads = loader.loadModel("phase_3.5/models/char/suit" + suitType + "-heads.bam")
			self.suitHead = self.suitHeads.find('**/' + CIGlobals.SuitHeads[suitHead])
			if suitHead == "flunky":
				glasses = self.suitHeads.find('**/glasses')
				glasses.reparentTo(self.suitHead)
				glasses.setTwoSided(True)
			if suitHead in CIGlobals.SuitSharedHeads:
				if suitHead == "coldcaller":
					self.suitHead.setColor(0.25, 0.35, 1.0, 1.0)
				else:
					headTexture = loader.loadTexture("phase_3.5/maps/" + suitHead + ".jpg")
					self.suitHead.setTexture(headTexture, 1)
		if not self.isSkele:
			self.suitHead.reparentTo(self.suit.find('**/joint_head'))
			
	def cleanupSuit(self):
		if self.shadow:
			self.shadow.remove()
			self.shadow = None
		self.removeHealthBar()
		self.suit.cleanup()
		self.suit = None
		
	def cleanupSuitHead(self):
		if self.suitHeads:
			self.suitHeads.remove()
			self.suitHeads = None
		if self.suitHead:
			self.suitHead.remove()
			self.suitHead = None
		
	def cleanupLoserSuit(self):
		if self.explosion:
			self.explosion.remove()
			self.explosion = None
		if self.smallExp:
			self.smallExp = None
		if self.largeExp:
			self.largeExp.cleanup()
			self.largeExp = None
		if self.loserSuit:
			self.loserSuit.cleanup()
			self.loserSuit = None
		
	def setName(self, nameString, charName):
		Avatar.Avatar.setName(self, nameString, avatarType=self.avatarType, charName=charName)
		
	def setChat(self, chatString):
		self.chat = chatString
		if self.isSkele:
			if "?" in chatString:
				self.suit_dial = audio3d.loadSfx("phase_5/audio/sfx/Skel_COG_VO_question.ogg")
			elif "!" in chatString:
				self.suit_dial = audio3d.loadSfx("phase_5/audio/sfx/Skel_COG_VO_grunt.ogg")
			else:
				self.suit_dial = audio3d.loadSfx("phase_5/audio/sfx/Skel_COG_VO_statement.ogg")
		elif self.head == "vp":
			if "?" in chatString:
				self.suit_dial = audio3d.loadSfx("phase_9/audio/sfx/Boss_COG_VO_question.ogg")
			elif "!" in chatString:
				self.suit_dial = audio3d.loadSfx("phase_9/audio/sfx/Boss_COG_VO_grunt.ogg")
			else:
				self.suit_dial = audio3d.loadSfx("phase_9/audio/sfx/Boss_COG_VO_statement.ogg")
		else:
			if "?" in chatString:
				self.suit_dial = audio3d.loadSfx("phase_3.5/audio/dial/COG_VO_question.ogg")
			elif "!" in chatString:
				self.suit_dial = audio3d.loadSfx("phase_3.5/audio/dial/COG_VO_grunt.ogg")
			else:
				self.suit_dial = audio3d.loadSfx("phase_3.5/audio/dial/COG_VO_statement.ogg")
		if self.isSkele:
			audio3d.attachSoundToObject(self.suit_dial, self.suit)
		else:
			audio3d.attachSoundToObject(self.suit_dial, self.suitHead)
		self.suit_dial.play()
		Avatar.Avatar.setChat(self, chatString)
		
	def generateHealthMeter(self):
		self.removeHealthBar()
		model = loader.loadModel("phase_3.5/models/gui/matching_game_gui.bam")
		button = model.find('**/minnieCircle')
		button.setScale(3.0)
		button.setH(180)
		button.setColor(self.healthColors[0])
		chestNull = self.suit.find('**/def_joint_attachMeter')
		if chestNull.isEmpty():
			chestNull = self.suit.find('**/joint_attachMeter')
		button.reparentTo(chestNull)
		self.healthBar = button
		glow = loader.loadModel("phase_3.5/models/props/glow.bam")
		glow.reparentTo(self.healthBar)
		glow.setScale(0.28)
		glow.setPos(-0.005, 0.01, 0.015)
		glow.setColor(self.healthGlowColors[0])
		button.flattenLight()
		self.healthBarGlow = glow
		self.condition = 0
		
	def updateHealthBar(self, hp):
		if hp > self.hp:
			self.hp = hp
		self.hp -= hp
		health = float(self.health) / float(self.maxHP)
		if health > 0.95:
			condition = 0
		elif health > 0.7:
			condition = 1
		elif health > 0.3:
			condition = 2
		elif health > 0.05:
			condition = 3
		elif health > 0.0:
			condition = 4
		else:
			condition = 5
			
		if self.condition != condition:
			if condition == 4:
				blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.75), Task(self.__blinkGray), Task.pause(0.1))
				taskMgr.add(blinkTask, self.taskName('blink-task'))
			elif condition == 5:
				if self.condition == 4:
					taskMgr.remove(self.taskName('blink-task'))
				blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.25), Task(self.__blinkGray), Task.pause(0.1))
				taskMgr.add(blinkTask, self.taskName('blink-task'))
			else:
				self.healthBar.setColor(self.healthColors[condition], 1)
				self.healthBarGlow.setColor(self.healthGlowColors[condition], 1)
			self.condition = condition
			
	def __blinkRed(self, task):
		self.healthBar.setColor(self.healthColors[3], 1)
		self.healthBarGlow.setColor(self.healthGlowColors[3], 1)
		if self.condition == 5:
			self.healthBar.setScale(1.17)
		return Task.done
		
	def __blinkGray(self, task):
		if not self.healthBar:
			return
		self.healthBar.setColor(self.healthColors[4], 1)
		self.healthBarGlow.setColor(self.healthGlowColors[4], 1)
		if self.condition == 5:
			self.healthBar.setScale(1.0)
		return Task.done
		
	def removeHealthBar(self):
		if self.healthMeter:
			self.healthBar.removeNode()
			self.healthBar = None
		if self.condition == 4 or self.condition == 5:
			taskMgr.remove(self.taskName('blink-task'))
		self.healthCondition = 0
		return
			
	def enterOff(self):
		self.anim = None
		return
		
	def exitOff(self):
		pass
		
	def exitGeneral(self):
		self.suit.stop()
		
	def enterNeutral(self):
		self.suit.loop("neutral")
		
	def exitNeutral(self):
		self.exitGeneral()
		
	def enterRollodex(self):
		self.suit.play("rollodex")
		
	def exitRollodex(self):
		self.exitGeneral()
		
	def enterWalk(self):
		self.suit.loop("walk")
		
	def exitWalk(self):
		self.exitGeneral()
		
	def generateLoserSuit(self):
		self.cleanupLoserSuit()
		if self.type == "A":
			if self.isSkele:
				self.loserSuit = Actor("phase_5/models/char/cogA_robot-lose-mod.bam")
			else:
				self.loserSuit = Actor("phase_4/models/char/suitA-lose-mod.bam")
			self.loserSuit.loadAnims({"lose": "phase_4/models/char/suitA-lose.bam"})
		if self.type == "B":
			if self.isSkele:
				self.loserSuit = Actor("phase_5/models/char/cogB_robot-lose-mod.bam")
			else:
				self.loserSuit = Actor("phase_4/models/char/suitB-lose-mod.bam")
			self.loserSuit.loadAnims({"lose": "phase_4/models/char/suitB-lose.bam"})
		if self.type == "C":
			if self.isSkele:
				self.loserSuit = Actor("phase_5/models/char/cogC_robot-lose-mod.bam")
			else:
				self.loserSuit = Actor("phase_3.5/models/char/suitC-lose-mod.bam")
			self.loserSuit.loadAnims({"lose": "phase_3.5/models/char/suitC-lose.bam"})
		if self.isSkele:
			self.loserSuit.find('**/tie').setTexture(self.suit_tie, 1)
			self.loserSuit.setTwoSided(1)
		else:
			self.loserSuit.find('**/hands').setColor(self.suit.find('**/hands').getColor())
			self.loserSuit.find('**/legs').setTexture(self.suit_leg, 1)
			self.loserSuit.find('**/arms').setTexture(self.suit_sleeve, 1)
			self.loserSuit.find('**/torso').setTexture(self.suit_blazer, 1)
		self.loserSuit.getGeomNode().setScale(self.suit.getScale())
		self.loserSuit.reparentTo(self)
		if not self.isSkele:
			self.suitHead.reparentTo(self.loserSuit.find('**/joint_head'))
		self.loserSuit.setPos(self.suit.getPos(render))
		self.loserSuit.setHpr(self.suit.getHpr(render))
		self.cleanupSuit()
		Avatar.Avatar.initShadow(self, self.avatarType)
		self.loserSuit.reparentTo(render)
		
	def enterDie(self):
		self.generateLoserSuit()
		self.state = "dead"
		self.loserSuit.play("lose")
		spinningSound = base.loadSfx("phase_3.5/audio/sfx/Cog_Death.ogg")
		deathSound = base.loadSfx("phase_3.5/audio/sfx/ENC_cogfall_apart.ogg")
		#audio3d.attachSoundToObject(spinningSound, self.loserSuit)
		#audio3d.attachSoundToObject(deathSound, self.loserSuit)
		Sequence(Wait(0.8), SoundInterval(spinningSound, duration=1.2, startTime=1.5, volume=0.4, node=self.loserSuit),
			SoundInterval(spinningSound, duration=3.0, startTime=0.6, volume=2.0, node=self.loserSuit),
			SoundInterval(deathSound, volume=0.32, node=self.loserSuit)).start()
		Sequence(Wait(0.8), Func(self.smallDeathParticles), Wait(4.2), Func(self.suitExplode),
				Wait(1.0), Func(self.delSuit)).start()
		
	def smallDeathParticles(self):
		self.smallExp = ParticleLoader.loadParticleEffect("phase_3.5/etc/gearExplosionSmall.ptf")
		self.smallExp.start(self.loserSuit)
		
	def suitExplode(self):
		self.smallExp.cleanup()
		self.largeExp = ParticleLoader.loadParticleEffect("phase_3.5/etc/gearExplosion.ptf")
		self.largeExp.start(self.loserSuit)
		self.explosion = loader.loadModel("phase_3.5/models/props/explosion.bam")
		self.explosion.setScale(0.5)
		self.explosion.reparentTo(render)
		self.explosion.setBillboardPointEye()
		if self.isSkele:
			self.explosion.setPos(self.loserSuit.find('**/joint_head').getPos(render) + (0, 0, 2))
		else:
			self.explosion.setPos(self.suitHead.getPos(render) + (0,0,2))
		
	def delSuit(self):
		self.cleanupLoserSuit()
		self.disableBodyCollisions()
		
	def exitDie(self):
		pass
		
	def enterFlyDown(self):
		self.fd_sfx = audio3d.loadSfx("phase_5/audio/sfx/ENC_propeller_in.ogg")
		self.prop = Actor("phase_4/models/props/propeller-mod.bam",
						{"chan": "phase_4/models/props/propeller-chan.bam"})
		audio3d.attachSoundToObject(self.fd_sfx, self.prop)
		self.fd_sfx.play()
		self.prop.reparentTo(self.suit.find('**/joint_head'))
		propTrack = Sequence(Func(self.prop.loop, 'chan', fromFrame=0, toFrame=3),
							Wait(1.75),
							Func(self.prop.play, 'chan', fromFrame=3))
		propTrack.start()
		if not self.hasSpawned:
			showSuit = Task.sequence(Task(self.hideSuit), Task.pause(0.3), Task(self.showSuit))
			taskMgr.add(showSuit, "showsuit")
			self.hasSpawned = True
		dur = self.suit.getDuration('land')
		suitTrack = Sequence(Func(self.suit.pose, 'land', 0),
							Wait(1.9),
							ActorInterval(self.suit, 'land', duration=dur))
		suitTrack.start()
		
	def hideSuit(self, task):
		self.hide()
		return Task.done
	
	def showSuit(self, task):
		self.show()
		fadeIn = Sequence(Func(self.setTransparency, 1), self.colorScaleInterval(0.6, colorScale=Vec4(1,1,1,1), startColorScale=Vec4(1,1,1,0)), Func(self.clearColorScale), Func(self.clearTransparency), Func(self.reparentTo, render))
		fadeIn.start()
		return Task.done
		
	def initializeLocalCollisions(self, name):
		Avatar.Avatar.initializeLocalCollisions(self, 1, 3, name)
		
	def initializeBodyCollisions(self):
		Avatar.Avatar.initializeBodyCollisions(self, self.avatarType, 2.25, 1, 4, 2)
		self.initializeRay(self.avatarType)
		self.collTube.setTangible(0)
		
	def deleteBean(self):
		if self.bean:
			self.bean.remove_node()
			self.bean = None
			
	def createJellyBean(self):
		self.deleteBean()
		money = int(self.maxHP / CIGlobals.SuitAttackDamageFactors['clipontie'])
		if money == 1:
			self.bean = loader.loadModel("phase_5.5/models/estate/jellyBean.bam")
			self.bean.set_two_sided(True)
			random_r = random.uniform(0, 1)
			random_g = random.uniform(0, 1)
			random_b = random.uniform(0, 1)
			self.bean.set_color(random_r, random_g, random_b, 1)
		else:
			self.bean = loader.loadModel("phase_5.5/models/estate/jellybeanJar.bam")
		self.bean.reparent_to(render)
		self.bean.set_pos(self.get_pos(render) + (0, 0, 1))
		bean_int = self.bean.hprInterval(1,
										Point3(360, 0, 0),
										startHpr=(0, 0, 0))
		bean_int.loop()
		
	def exitFlyDown(self):
		audio3d.detachSound(self.fd_sfx)
		self.prop.cleanup()
		self.prop = None
		
	def enterFlyAway(self):
		self.fa_sfx = audio3d.loadSfx("phase_5/audio/sfx/ENC_propeller_out.ogg")
		self.prop = Actor("phase_4/models/props/propeller-mod.bam",
						{"chan": "phase_4/models/props/propeller-chan.bam"})
		audio3d.attachSoundToObject(self.fa_sfx, self.prop)
		self.fa_sfx.play()
		self.prop.reparentTo(self.suit.find('**/joint_head'))
		self.prop.setPlayRate(-1.0, "chan")
		propTrack = Sequence(Func(self.prop.play, 'chan', fromFrame=3),
							Wait(1.75),
							Func(self.prop.play, 'chan', fromFrame=0, toFrame=3))
		propTrack.start()
		self.suit.setPlayRate(-1.0, 'land')
		self.suit.play('land')
		
	def exitFlyAway(self):
		audio3d.detachSound(self.fa_sfx)
		self.prop.cleanup()
		self.prop = None
		
	def enterAttack(self, attack):
		self.attack = attack
		
		self.weapon_state = 'start'
		
		if attack == "canned":
			self.weapon = loader.loadModel("phase_5/models/props/can.bam")
			self.weapon.setScale(15)
			self.weapon.setR(180)
			self.wss = CollisionSphere(0,0,0,0.05)
		elif attack == "clipontie":
			self.weapon = loader.loadModel("phase_5/models/props/power-tie.bam")
			self.weapon.setScale(4)
			self.weapon.setR(180)
			self.wss = CollisionSphere(0,0,0,0.2)
		elif attack == "sacked":
			self.weapon = loader.loadModel("phase_5/models/props/sandbag-mod.bam")
			self.weapon.setScale(2)
			self.weapon.setR(180)
			self.weapon.setP(90)
			self.weapon.setY(-2.8)
			self.weapon.setZ(-0.3)
			self.wss = CollisionSphere(0,0,0,1)
		elif attack == "playhardball":
			self.weapon = loader.loadModel("phase_5/models/props/baseball.bam")
			self.weapon.setScale(10)
			self.wss = CollisionSphere(0,0,0,0.1)
			self.weapon.setZ(-0.5)
		elif attack == "marketcrash":
			self.weapon = loader.loadModel("phase_5/models/props/newspaper.bam")
			self.weapon.setScale(3)
			self.weapon.setPos(0.41, -0.06, -0.06)
			self.weapon.setHpr(90, 0, 270)
			self.wss = CollisionSphere(0,0,0,0.35)
		elif attack == "glowerpower":
			self.weapon = loader.loadModel("phase_5/models/props/dagger.bam")
			self.wss = CollisionSphere(0,0,0,1)
		else:
			notify.warning("unknown attack!")
			
		self.throwObjectId = random.uniform(0, 101010101010)
		
		if attack == "canned" or attack == "playhardball":
			self.weapon.reparentTo(self.suit.find('**/joint_Rhold'))
			if self.type == "C":
				taskMgr.doMethodLater(2.2, self.throwObject, "throwObject" + str(self.throwObjectId))
			else:
				taskMgr.doMethodLater(3, self.throwObject, "throwObject" + str(self.throwObjectId))
			self.suit.play("throw-object")
		elif attack == "clipontie" or attack == "marketcrash" or attack == "sacked":
			self.weapon.reparentTo(self.suit.find('**/joint_Rhold'))
			if self.type == "C":
				taskMgr.doMethodLater(2.2, self.throwObject, "throwObject" + str(self.throwObjectId))
			else:
				taskMgr.doMethodLater(3, self.throwObject, "throwObject" + str(self.throwObjectId))
			self.suit.play("throw-paper")
		elif attack == "glowerpower":
			taskMgr.doMethodLater(1, self.throwObject, "throwObject" + str(self.throwObjectId))
			self.suit.play("glower")
		self.weaponSensorId = random.uniform(0, 1010101010101001)
		wsnode = CollisionNode('weaponSensor' + str(self.weaponSensorId))
		wsnode.addSolid(self.wss)
		self.wsnp = self.weapon.attachNewNode(wsnode)
		if attack == "sacked":
			self.wsnp.setZ(1)
		elif attack == "marketcrash":
			self.wsnp.setPos(-0.25, 0.3, 0)
			
	def delWeapon(self, task):
		if self.weapon:
			self.weapon.removeNode()
			self.weapon = None
		return task.done
		
	def interruptAttack(self):
		if self.wtrajectory:
			if self.wtrajectory.isStopped():
				if self.weapon:
					self.weapon.removeNode()
					self.weapon = None
		if self.throwObjectId:
			taskMgr.remove("throwObject" + str(self.throwObjectId))
		
	def handleWeaponTouch(self):
		if not self.attack == "glowerpower" or not self.attack == "clipontie":
			if self.weapon_sfx:
				self.weapon_sfx.stop()
		try: self.wtrajectory.pause()
		except: pass
		if self.weapon:
			self.weapon.removeNode()
			self.weapon = None
		
	def weaponCollisions(self):
		self.wsnp.setCollideMask(BitMask32(0))
		self.wsnp.node().setFromCollideMask(CIGlobals.EventBitmask)
			
		event = CollisionHandlerEvent()
		event.setInPattern("%fn-into")
		event.setOutPattern("%fn-out")
		base.cTrav.addCollider(self.wsnp, event)
		
	def throwObject(self, task):
		self.playWeaponSound()
		
		self.weaponNP = NodePath("weaponNP")
		self.weaponNP.setScale(render, 1)
		try: self.weaponNP.reparentTo(self.find('**/joint_nameTag'))
		except: return task.done
		self.weaponNP.setPos(0, 50, 0)
		self.weaponNP.setHpr(0, 0, 0)
		
		if self.weapon:
			self.weapon.setScale(self.weapon.getScale(render))
		try: self.weapon.reparentTo(render)
		except: return task.done
		
		self.weapon.setPos(0,0,0)
		self.weapon.setHpr(0,0,0)
		
		if self.attack == "glowerpower":
			self.weapon.setH(self.weaponNP.getH(render))
			self.wtrajectory = self.weapon.posInterval(0.5,
										Point3(self.weaponNP.getPos(render)),
										startPos=(self.getX(render), self.getY(render) + 3, self.find('**/joint_head').getZ(render)))
			self.wtrajectory.start()
		else:
			self.wtrajectory = ProjectileInterval(self.weapon,
										startPos = (self.suit.find('**/joint_Rhold').getPos(render)),
										endPos = self.weaponNP.getPos(render),
										gravityMult = 0.7, duration = 1)
			self.wtrajectory.start()
		if self.attack == "glowerpower":
			taskMgr.doMethodLater(0.5, self.delWeapon, "delWeapon")
		else:
			taskMgr.doMethodLater(1, self.delWeapon, "delWeapon")
		self.weapon_state = 'released'
		
	def playWeaponSound(self):
		if self.attack == "glowerpower":
			self.weapon_sfx = audio3d.loadSfx("phase_5/audio/sfx/SA_glower_power.ogg")
		elif self.attack == "canned":
			self.weapon_sfx = audio3d.loadSfx("phase_5/audio/sfx/SA_canned_tossup_only.ogg")
		elif self.attack == "clipontie":
			self.weapon_sfx = audio3d.loadSfx("phase_5/audio/sfx/SA_powertie_throw.ogg")
		elif self.attack == "sacked":
			self.weapon_sfx = audio3d.loadSfx("phase_5/audio/sfx/SA_canned_tossup_only.ogg")
		elif self.attack == "playhardball":
			self.weapon_sfx = audio3d.loadSfx("phase_5/audio/sfx/SA_canned_tossup_only.ogg")
		elif self.attack == "marketcrash":
			self.weapon_sfx = audio3d.loadSfx("phase_5/audio/sfx/SA_canned_tossup_only.ogg")
		if self.weapon:
			audio3d.attachSoundToObject(self.weapon_sfx, self.weapon)
			self.weapon_sfx.play()
			
	def exitAttack(self):
		pass
		
	def enterPie(self):
		self.suit.play("pie")
		
	def exitPie(self):
		self.exitGeneral()
		
	def enterWin(self):
		self.suit.play("win")
		
	def exitWin(self):
		self.exitGeneral()
Exemple #2
0
class Char(Avatar.Avatar):

    def __init__(self):
        try:
            self.Char_initialized
            return
        except:
            self.Char_initialized = 1

        Avatar.Avatar.__init__(self)
        self.avatarType = CIGlobals.CChar
        self.avatarName = None
        self.currentAnim = None
        self.charType = ''
        self.eyes = loader.loadTexture('phase_3/maps/eyes1.jpg', 'phase_3/maps/eyes1_a.rgb')
        self.closedEyes = loader.loadTexture('phase_3/maps/mickey_eyes_closed.jpg', 'phase_3/maps/mickey_eyes_closed_a.rgb')
        self.animFSM = ClassicFSM('Char', [State('off', self.enterOff, self.exitOff),
         State('neutral', self.enterNeutral, self.exitNeutral),
         State('walk', self.enterWalk, self.exitWalk),
         State('run', self.enterRun, self.exitRun)], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()
        Avatar.Avatar.initializeBodyCollisions(self, self.avatarType, 3.5, 1)
        return

    def getNametagJoints(self):
        return []

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                notify.warning('animFSM in flux, state=%s, not requesting off' % self.animFSM.getCurrentState().getName())
        else:
            notify.warning('animFSM has been deleted')

    def disable(self):
        self.stopBlink()
        self.stopAnimations()
        Avatar.Avatar.disable(self)

    def delete(self):
        try:
            self.Char_deleted
        except:
            self.Char_deleted = 1
            del self.animFSM
            Avatar.Avatar.delete(self)

    def setChat(self, chatString):
        if self.charType == CIGlobals.Mickey:
            self.dial = base.audio3d.loadSfx('phase_3/audio/dial/mickey.ogg')
        else:
            if self.charType == CIGlobals.Minnie:
                self.dial = base.audio3d.loadSfx('phase_3/audio/dial/minnie.ogg')
            else:
                if self.charType == CIGlobals.Goofy:
                    self.dial = base.audio3d.loadSfx('phase_6/audio/dial/goofy.ogg')
        base.audio3d.attachSoundToObject(self.dial, self)
        self.dial.play()
        Avatar.Avatar.setChat(self, chatString)

    def setName(self, nameString, charName=None):
        self.avatarName = nameString
        Avatar.Avatar.setName(self, nameString, avatarType=self.avatarType, charName=charName)

    def setupNameTag(self, tempName=None):
        Avatar.Avatar.setupNameTag(self, tempName)
        self.nametag.setNametagColor(NametagGlobals.NametagColors[NametagGlobals.CCNPC])
        self.nametag.setActive(0)
        self.nametag.updateAll()

    def generateChar(self, charType):
        self.charType = charType
        if charType == CIGlobals.Mickey or charType == CIGlobals.Minnie:
            self.loadModel('phase_3/models/char/' + charType.lower() + '-' + str(CIGlobals.ModelDetail(self.avatarType)) + '.bam')
            self.loadAnims({'neutral': 'phase_3/models/char/' + charType.lower() + '-wait.bam', 'walk': 'phase_3/models/char/' + charType.lower() + '-walk.bam', 
               'run': 'phase_3/models/char/' + charType.lower() + '-run.bam', 
               'left-start': 'phase_3.5/models/char/' + charType.lower() + '-left-start.bam', 
               'left': 'phase_3.5/models/char/' + charType.lower() + '-left.bam', 
               'right-start': 'phase_3.5/models/char/' + charType.lower() + '-right-start.bam', 
               'right': 'phase_3.5/models/char/' + charType.lower() + '-right.bam'})
            if charType == CIGlobals.Mickey:
                self.mickeyEye = self.controlJoint(None, 'modelRoot', 'joint_pupilR')
                self.mickeyEye.setY(0.025)
            for bundle in self.getPartBundleDict().values():
                bundle = bundle['modelRoot'].getBundle()
                earNull = bundle.findChild('sphere3')
                if not earNull:
                    earNull = bundle.findChild('*sphere3')
                earNull.clearNetTransforms()

            for bundle in self.getPartBundleDict().values():
                charNodepath = bundle['modelRoot'].partBundleNP
                bundle = bundle['modelRoot'].getBundle()
                earNull = bundle.findChild('sphere3')
                if not earNull:
                    earNull = bundle.findChild('*sphere3')
                ears = charNodepath.find('**/sphere3')
                if ears.isEmpty():
                    ears = charNodepath.find('**/*sphere3')
                ears.clearEffect(CharacterJointEffect.getClassType())
                earRoot = charNodepath.attachNewNode('earRoot')
                earPitch = earRoot.attachNewNode('earPitch')
                earPitch.setP(40.0)
                ears.reparentTo(earPitch)
                earNull.addNetTransform(earRoot.node())
                ears.clearMat()
                ears.node().setPreserveTransform(ModelNode.PTNone)
                ears.setP(-40.0)
                ears.flattenMedium()
                ears.setBillboardAxis()
                self.startBlink()

        else:
            if charType == CIGlobals.Pluto:
                self.loadModel('phase_6/models/char/pluto-1000.bam')
                self.loadAnims({'walk': 'phase_6/models/char/pluto-walk.bam', 'neutral': 'phase_6/models/char/pluto-neutral.bam', 
                   'sit': 'phase_6/models/char/pluto-sit.bam', 
                   'stand': 'phase_6/models/char/pluto-stand.bam'})
            else:
                if charType == CIGlobals.Goofy:
                    self.loadModel('phase_6/models/char/TT_G-1500.bam')
                    self.loadAnims({'neutral': 'phase_6/models/char/TT_GWait.bam', 'walk': 'phase_6/models/char/TT_GWalk.bam'})
                else:
                    raise StandardError('unknown char %s!' % charType)
        Avatar.Avatar.initShadow(self)
        return

    def initializeLocalCollisions(self, name, radius):
        Avatar.Avatar.initializeLocalCollisions(self, radius, 2, name)

    def startBlink(self):
        randomStart = random.uniform(0.5, 5)
        taskMgr.add(self.blinkTask, 'blinkTask')

    def stopBlink(self):
        taskMgr.remove('blinkTask')
        taskMgr.remove('doBlink')
        taskMgr.remove('openEyes')

    def blinkTask(self, task):
        taskMgr.add(self.doBlink, 'doBlink')
        delay = random.uniform(0.5, 7)
        task.delayTime = delay
        return task.again

    def doBlink(self, task):
        self.closeEyes()
        taskMgr.doMethodLater(0.2, self.openEyes, 'openEyes')
        return task.done

    def closeEyes(self):
        self.find('**/joint_pupilR').hide()
        self.find('**/joint_pupilL').hide()
        if self.charType == CIGlobals.Mickey:
            self.mickeyEye.setY(-0.025)
            self.mickeyEye.hide()
        self.find('**/eyes').setTexture(self.closedEyes, 1)

    def openEyes(self, task):
        self.find('**/joint_pupilR').show()
        self.find('**/joint_pupilL').show()
        if self.charType == CIGlobals.Mickey:
            self.mickeyEye.setY(0.025)
            self.mickeyEye.show()
        self.find('**/eyes').setTexture(self.eyes, 1)
        return task.done

    def enterOff(self):
        self.currentAnim = None
        return

    def exitOff(self):
        pass

    def enterNeutral(self):
        self.loop('neutral')

    def exitNeutral(self):
        self.stop()

    def enterWalk(self):
        self.loop('walk')

    def exitWalk(self):
        self.stop()

    def enterRun(self):
        self.loop('run')

    def exitRun(self):
        self.stop()
class DistributedSuit(Suit, DistributedAvatar, DistributedSmoothNode, DelayDeletable):
    notify = directNotify.newCategory("DistributedSuit")

    def __init__(self, cr):
        try:
            self.DistributedSuit_initialized
            return
        except:
            self.DistributedSuit_initialized = 1
        Suit.__init__(self)
        DistributedAvatar.__init__(self, cr)
        DistributedSmoothNode.__init__(self, cr)

        self.suitFSM = ClassicFSM('DistributedSuit',
            [
                State('off', self.enterSuitOff, self.exitSuitOff),
                State('walking', self.enterWalking, self.exitWalking),
                State('flyingDown', self.enterFlyingDown, self.exitFlyingDown),
                State('flyingUp', self.enterFlyingUp, self.exitFlyingUp),
                State('bossFlying', self.enterBossFlying, self.exitBossFlying)
            ],
            'off', 'off'
        )
        self.suitFSM.enterInitialState()
        self.makeStateDict()
        self.makeAnimStateDict()

        # These are just default values, we'll set them later on.
        self.anim = None
        self.state = "alive"
        self.health = None
        self.type = None
        self.team = None
        self.head = None
        self.skeleton = 0
        self.battle = None
        self.suitState = None
        self.startPoint = None
        self.endPoint = None
        self.moveIval = None
        self.walkPaused = None
        self.animIval = None
        self.level = None
        return

    def d_disableMovement(self, wantRay = False):
        self.sendUpdate('disableMovement', [])
        self.interruptAttack()
        if not wantRay:
            Suit.disableRay(self)

    def d_enableMovement(self):
        self.sendUpdate('enableMovement', [])
        Suit.initializeRay(self, self.avatarType, 2)

    def startRay(self):
        Suit.initializeRay(self, self.avatarType, 2)

    def setLevel(self, level):
        self.level = level

    def getLevel(self):
        return self.level

    def makeStateDict(self):
        self.suitState2stateIndex = {}
        for state in self.suitFSM.getStates():
            self.suitState2stateIndex[state.getName()] = self.suitFSM.getStates().index(state)
        self.stateIndex2suitState = {v: k for k, v in self.suitState2stateIndex.items()}

    def makeAnimStateDict(self):
        self.animState2animId = {}
        for index in range(len(self.animFSM.getStates())):
            self.animState2animId[self.animFSM.getStates()[index].getName()] = index
        self.animId2animState = {v: k for k, v in self.animState2animId.items()}

    def setLatePos(self, x, y):
        self.setX(x)
        self.setY(y)

    def enterAttack(self, attack, target, ts = 0):
        Suit.enterAttack(self, attack, target, ts)
        if target:
            self.headsUp(target)

    def setSuitState(self, index, startPoint, endPoint, timestamp = None):
        if timestamp != None:
            ts = globalClockDelta.localElapsedTime(timestamp)
        else:
            ts = 0.0

        self.suitState = self.stateIndex2suitState[index]
        self.startPoint = startPoint
        self.endPoint = endPoint

        self.suitFSM.request(self.suitState, [startPoint, endPoint, ts])

    def getSuitState(self):
        return self.suitState

    def enterWalking(self, startIndex, endIndex, ts = 0.0):
        durationFactor = 0.2
        if startIndex > -1:
            startPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[startIndex]
            startPos = CIGlobals.SuitSpawnPoints[self.getHood()][startPoint]
        else:
            startPos = self.getPos(render)
        endPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[endIndex]
        endPos = CIGlobals.SuitSpawnPoints[self.getHood()][endPoint]

        if self.moveIval:
            self.moveIval.pause()
            self.moveIval = None

        self.moveIval = NPCWalkInterval(self, endPos, durationFactor, startPos, fluid = 1)
        self.moveIval.start(ts)
        self.animFSM.request('walk')

    def exitWalking(self):
        if self.moveIval:
            self.moveIval.pause()
            self.moveIval = None
        if not self.isDead():
            self.animFSM.request('off')

    def enterFlyingDown(self, startIndex, endIndex, ts = 0.0):
        duration = 3
        startPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[startIndex]
        startPos = CIGlobals.SuitSpawnPoints[self.getHood()][startPoint] + (0, 0, 50)
        endPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[endIndex]
        endPos = CIGlobals.SuitSpawnPoints[self.getHood()][endPoint]
        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None
        self.moveIval = LerpPosInterval(self, duration = duration, pos = endPos, startPos = startPos, fluid = 1)
        self.moveIval.start(ts)
        self.animFSM.request('flydown', [ts])
        yaw = random.uniform(0.0, 360.0)
        self.setH(yaw)

    def exitFlyingDown(self):
        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None
        self.animFSM.request('off')

    def enterFlyingUp(self, startIndex, endIndex, ts = 0.0):
        duration = 3
        startPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[startIndex]
        endPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[endIndex]
        startPos = CIGlobals.SuitSpawnPoints[self.getHood()][startPoint]
        endPos = CIGlobals.SuitSpawnPoints[self.getHood()][endPoint] + (0, 0, 50)

        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None

        self.moveIval = LerpPosInterval(self, duration = duration, pos = endPos, startPos = startPos, fluid = 1)
        self.moveIval.start(ts)
        self.animFSM.request('flyaway', [ts])

    def exitFlyingUp(self):
        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None
        self.animFSM.request('off')

    def enterBossFlying(self, startIndex, endIndex, ts = 0.0):
        duration = 3.5
        startPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[startIndex]
        endPoint = CIGlobals.SuitSpawnPoints[self.getHood()].keys()[endIndex]
        startPos = CIGlobals.SuitSpawnPoints[self.getHood()][startPoint]
        endPos = CIGlobals.SuitSpawnPoints[self.getHood()][endPoint]

        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None

        self.animIval = Sequence(
            Func(self.animFSM.request, 'flyaway', [ts]),
            Wait(1.0),
            Func(self.animFSM.request, 'flydown', [ts])
        )

        self.moveIval = Sequence(
            Wait(0.5),
            Func(self.headsUp, endPos),
            ProjectileInterval(
                self,
                startPos = startPos,
                endPos = endPos,
                gravityMult = 0.25,
                duration = duration
            )
        )
        self.moveIval.start(ts)
        self.animIval.start(ts)

    def exitBossFlying(self):
        if self.animIval:
            self.animIval.finish()
            self.animIval = None
        if self.moveIval:
            self.moveIval.finish()
            self.moveIval = None
        self.animFSM.request('off')

    def enterSuitOff(self, foo1 = None, foo2 = None, foo3 = None):
        pass

    def exitSuitOff(self):
        pass

    #def setWalking(self, value, startPos, endPath, timestamp = None):
    #
    #
    #	self.value = value
    #	self.startPath = startPath
    #	self.endPath = endPath
    #
    #	if self.walkIval:
    #		self.walkIval.finish()
    #		self.walkIval = None
    #
    #	if value:
    #		durationFactor = 0.2
    #		path = CIGlobals.SuitSpawnPoints.values()[endPath]
    #		self.walkIval = NPCWalkInterval(self, path, startPos = self.getPos(render), name = pathName, durationFactor = durationFactor, fluid = 1)

    def setBattle(self, battle):
        self.battle = battle

    def getBattle(self):
        return self.battle

    def printPos(self, task):
        print self.getPos(render)
        print self.getHpr(render)
        return task.cont

    def announceHealth(self, level, hp):
        DistributedAvatar.announceHealth(self, level, hp)
        if level == 1:
            healthSfx = base.audio3d.loadSfx("phase_3/audio/sfx/health.mp3")
            base.audio3d.attachSoundToObject(healthSfx, self)
            SoundInterval(healthSfx).start()
            del healthSfx

    def setSuit(self, suitType, head, team, skeleton):
        for obj in self.cr.doId2do.values():
            if obj.zoneId == self.zoneId:
                if obj.__class__.__name__ == "DistributedCogBattle":
                    # This has to be the Cog Battle we're in because it's in the same zone.
                    self.setBattle(obj)
        hp = CIGlobals.SuitHealthAmounts[head]
        Suit.generateSuit(self, suitType, head, team, hp, skeleton)

    def getSuit(self):
        return tuple((self.type, self.head, self.team, self.skeleton))

    def setName(self, name):
        Suit.setName(self, name, self.head)

    def setHealth(self, health):
        DistributedAvatar.setHealth(self, health)
        self.updateHealthBar(health)

    def setAnimState(self, anim, timestamp = None):
        self.anim = anim

        if timestamp == None:
            ts = 0.0
        else:
            ts = globalClockDelta.localElapsedTime(timestamp)

        if type(anim) == types.IntType:
            anim = self.animId2animState[anim]

        if self.animFSM.getStateNamed(anim):
            self.animFSM.request(anim, [ts])

    def doAttack(self, attackId, avId, timestamp = None):
        if timestamp == None:
            ts = 0.0
        else:
            ts = globalClockDelta.localElapsedTime(timestamp)
        attackName = SuitAttacks.SuitAttackLengths.keys()[attackId]
        avatar = self.cr.doId2do.get(avId)
        self.animFSM.request('attack', [attackName, avatar, ts])

    def throwObject(self):
        self.acceptOnce("enter" + self.wsnp.node().getName(), self.__handleWeaponCollision)
        Suit.throwObject(self)

    def __handleWeaponCollision(self, entry):
        self.sendUpdate('toonHitByWeapon', [self.attack, base.localAvatar.doId])
        base.localAvatar.handleHitByWeapon(self.attack, self)
        self.b_handleWeaponTouch()

    def b_handleWeaponTouch(self):
        self.sendUpdate('handleWeaponTouch', [])
        self.handleWeaponTouch()

    def announceGenerate(self):
        DistributedAvatar.announceGenerate(self)
        if self.animFSM.getCurrentState().getName() == 'off':
            self.setAnimState('neutral')

    def generate(self):
        DistributedAvatar.generate(self)
        DistributedSmoothNode.generate(self)
        self.startSmooth()

    def disable(self):
        if self.suitTrack != None:
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.stopSmooth()
        self.suitFSM.requestFinalState()
        self.suitFSM = None
        self.suitState2stateIndex = None
        self.stateIndex2suitState = None
        self.anim = None
        self.state = None
        self.health = None
        self.type = None
        self.team = None
        self.head = None
        self.skeleton = None
        self.battle = None
        Suit.disable(self)
        DistributedAvatar.disable(self)

    def delete(self):
        Suit.delete(self)
        DistributedAvatar.delete(self)
        DistributedSmoothNode.delete(self)
Exemple #4
0
class Toon(Avatar.Avatar, ToonHead, ToonDNA.ToonDNA):
    notify = directNotify.newCategory("Toon")

    def __init__(self, cr, mat=0):
        self.cr = cr
        try:
            self.Toon_initialized
            return
        except:
            self.Toon_initialized = 1
        Avatar.Avatar.__init__(self, mat)
        ToonDNA.ToonDNA.__init__(self)
        ToonHead.__init__(self, cr)
        self.collsSetup = False
        self.forwardSpeed = 0.0
        self.rotateSpeed = 0.0
        self.strafeSpeed = 0.0
        self.avatarType = CIGlobals.Toon
        self.track = None
        self.standWalkRunReverse = None
        self.playingAnim = None
        self.playingRate = None
        self.tag = None
        self.money = 0
        self.lookAtTrack = None
        self.portal1 = None
        self.portal2 = None
        self.spineA = NodePath()
        self.tokenIcon = None
        self.tokenIconIval = None
        self.fallSfx = base.audio3d.loadSfx(
            "phase_4/audio/sfx/MG_cannon_hit_dirt.ogg")
        base.audio3d.attachSoundToObject(self.fallSfx, self)
        self.eyes = loader.loadTexture("phase_3/maps/eyes.jpg",
                                       "phase_3/maps/eyes_a.rgb")
        self.myTaskId = random.uniform(0, 1231231232132131231232)
        self.closedEyes = loader.loadTexture("phase_3/maps/eyesClosed.jpg",
                                             "phase_3/maps/eyesClosed_a.rgb")
        self.soundChatBubble = loader.loadSfx(
            "phase_3/audio/sfx/GUI_balloon_popup.ogg")
        self.shadowCaster = None
        self.accessories = []
        self.chatSoundDict = {}
        self.backpack = None
        self.forceRunSpeed = False
        self.animFSM = ClassicFSM('Toon', [
            State('off', self.enterOff, self.exitOff),
            State('neutral', self.enterNeutral, self.exitNeutral),
            State('swim', self.enterSwim, self.exitSwim),
            State('walk', self.enterWalk, self.exitWalk),
            State('run', self.enterRun, self.exitRun),
            State('bow', self.enterBow, self.exitBow),
            State('openBook', self.enterOpenBook, self.exitOpenBook),
            State('readBook', self.enterReadBook, self.exitReadBook),
            State('closeBook', self.enterCloseBook, self.exitCloseBook),
            State('teleportOut', self.enterTeleportOut, self.exitTeleportOut),
            State('teleportIn', self.enterTeleportIn, self.exitTeleportIn),
            State('died', self.enterDied, self.exitDied),
            State('fallFWD', self.enterFallFWD, self.exitFallFWD),
            State('fallBCK', self.enterFallBCK, self.exitFallBCK),
            State('jump', self.enterJump, self.exitJump),
            State('leap', self.enterLeap, self.exitLeap),
            State('laugh', self.enterLaugh, self.exitLaugh),
            State('happy', self.enterHappyJump, self.exitHappyJump),
            State('shrug', self.enterShrug, self.exitShrug),
            State('hdance', self.enterHDance, self.exitHDance),
            State('wave', self.enterWave, self.exitWave),
            State('scientistEmcee', self.enterScientistEmcee,
                  self.exitScientistEmcee),
            State('scientistWork', self.enterScientistWork,
                  self.exitScientistWork),
            State('scientistGame', self.enterScientistGame,
                  self.exitScientistGame),
            State('scientistJealous', self.enterScientistJealous,
                  self.exitScientistJealous),
            State('cringe', self.enterCringe, self.exitCringe),
            State('conked', self.enterConked, self.exitConked),
            State('win', self.enterWin, self.exitWin),
            State('walkBack', self.enterWalkBack, self.exitWalkBack),
            State('deadNeutral', self.enterDeadNeutral, self.exitDeadNeutral),
            State('deadWalk', self.enterDeadWalk, self.exitDeadWalk),
            State('squish', self.enterSquish, self.exitSquish),
            State('Happy', self.enterHappy, self.exitHappy),
            State('Sad', self.enterSad, self.exitSad),
            State('Swim', self.enterSwim, self.exitSwim)
        ], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()

        if not hasattr(self, 'uniqueName'):
            self.uniqueName = types.MethodType(uniqueName, self)

        self.activities = {
            ACT_DIE: Die(self),
            ACT_VICTORY_DANCE: VictoryDance(self),
            ACT_TOON_BOW: Bow(self),
            ACT_JUMP: Jump(self)
        }

    def setActivity(self, act, timestamp=0):
        Avatar.Avatar.setActivity(self, act, timestamp)
        if act == ACT_NONE:
            self.animFSM.request("Happy")

    def getUpperBodySubpart(self):
        if self.getAnimal() == "dog":
            return ["torso-top", "head"]

        return ["torso-top"]

    def getLowerBodySubpart(self):
        return ["legs", "torso-pants"]

    def getRightHandNode(self):
        return self.find("**/def_joint_right_hold")

    def getLeftHandNode(self):
        return self.find("**/def_joint_left_hold")

    def getHeadNode(self):
        return self.getPart('head')

    def getEyePoint(self):
        # middle of the head
        return Point3(0, 0, self.getHeight() - (self.getHeadHeight() / 2.0))

    def setForceRunSpeed(self, flag):
        self.forceRunSpeed = flag

    def resetTorsoRotation(self):
        if not self.isEmpty():
            spine = self.find("**/def_spineB")
            if not spine.isEmpty():
                spine.setH(0)
                spine.detachNode()
                self.getPart("legs").setH(0)
                self.releaseJoint("torso", "def_spineB")

    def showAvId(self):
        pass

    def showName(self):
        pass

    def getNametagJoints(self):
        joints = []
        for lodName in self.getLODNames():
            bundle = self.getPartBundle('legs', lodName)
            joint = bundle.findChild('joint_nameTag')
            if joint:
                joints.append(joint)

        return joints

    def enterHappy(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = None
        self.standWalkRunReverse = (('neutral', 1.0), ('walk', 1.0),
                                    ('run', 1.0), ('walk', -1.0),
                                    ('strafe', 1.0), ('strafe', -1.0))
        self.setSpeed(self.forwardSpeed, self.rotateSpeed)

    def exitHappy(self):
        self.standWalkRunReverse = None
        self.stop()

    def enterSad(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'sad'
        self.standWalkRunReverse = (('dneutral', 1.0), ('dwalk', 1.2),
                                    ('dwalk', 1.2), ('dwalk', -1.0))
        self.setSpeed(0, 0)

    def exitSad(self):
        self.standWalkRunReverse = None
        self.stop()
        #if hasattr(self, 'doId'):
        #    if hasattr(base, 'localAvatar'):
        #        if base.localAvatar.doId == self.doId:
        #            self.controlManager.enableAvatarJump()

    def setSpeed(self, forwardSpeed, rotateSpeed, strafeSpeed=0.0):
        if self.forceRunSpeed:
            forwardSpeed = CIGlobals.RunCutOff
        self.forwardSpeed = forwardSpeed
        self.rotateSpeed = rotateSpeed
        self.strafeSpeed = strafeSpeed
        action = None
        if self.standWalkRunReverse != None:

            rotateCutOff = CIGlobals.RotateCutOff if not self.isLocalAvatar(
            ) else CIGlobals.WalkCutOff

            if strafeSpeed < CIGlobals.StrafeCutOff and strafeSpeed > -CIGlobals.StrafeCutOff:
                self.resetTorsoRotation()

            if forwardSpeed >= CIGlobals.RunCutOff:
                action = CIGlobals.RUN_INDEX
            elif forwardSpeed > CIGlobals.WalkCutOff:
                action = CIGlobals.WALK_INDEX
            elif forwardSpeed < -CIGlobals.WalkCutOff:
                action = CIGlobals.REVERSE_INDEX
            elif abs(rotateSpeed) > rotateCutOff:
                action = CIGlobals.WALK_INDEX
            elif abs(strafeSpeed) > CIGlobals.RunCutOff:
                action = CIGlobals.RUN_INDEX
            elif abs(strafeSpeed) > CIGlobals.WalkCutOff:
                action = CIGlobals.WALK_INDEX
            else:
                action = CIGlobals.STAND_INDEX

            if abs(strafeSpeed) > CIGlobals.WalkCutOff:
                spine = self.find("**/def_spineB")

                if spine.isEmpty():
                    spine = self.controlJoint(None, "torso", "def_spineB")

                movementVec = Vec3(strafeSpeed, forwardSpeed, 0)
                movementVec.normalize()
                movementAngle = rad2Deg(
                    math.atan2(movementVec[1], movementVec[0])) - 90.0

                if action == CIGlobals.REVERSE_INDEX:
                    movementAngle -= 180

                spine.setH(-movementAngle)
                self.getPart('legs').setH(movementAngle)

            anim, rate = self.standWalkRunReverse[action]
            if anim != self.playingAnim or rate != self.playingRate or self.forcedTorsoAnim != self.lastForcedTorsoAnim:
                self.playingAnim = anim
                self.playingRate = rate
                self.lastForcedTorsoAnim = self.forcedTorsoAnim

                if self.forcedTorsoAnim is None:
                    self.loop(anim)
                else:

                    # Whatever happens to the legs should also happen on the pants.
                    self.loop(anim, partName='torso-pants')
                    self.loop(anim, partName='legs')
                self.setPlayRate(rate, anim)
        return action

    def enterSquish(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'squish'
        sound = loader.loadSfx('phase_9/audio/sfx/toon_decompress.ogg')
        lerpTime = 0.1
        node = self.getGeomNode().getChild(0)
        origScale = node.getScale()
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('getSquished')
        else:
            name = 'getSquished'
        self.track = Sequence(LerpScaleInterval(node,
                                                lerpTime,
                                                VBase3(2, 2, 0.025),
                                                blendType='easeInOut'),
                              Wait(1.0),
                              Parallel(
                                  Sequence(
                                      Wait(0.4),
                                      LerpScaleInterval(node,
                                                        lerpTime,
                                                        VBase3(1.4, 1.4, 1.4),
                                                        blendType='easeInOut'),
                                      LerpScaleInterval(node,
                                                        lerpTime / 2.0,
                                                        VBase3(0.8, 0.8, 0.8),
                                                        blendType='easeInOut'),
                                      LerpScaleInterval(
                                          node,
                                          lerpTime / 3.0,
                                          origScale,
                                          blendType='easeInOut')),
                                  ActorInterval(self, 'happy', startTime=0.2),
                                  SoundInterval(sound)),
                              name=name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.squishDone,
                        [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.start(ts)

    def squishDone(self, callback=None, extraArgs=[]):
        self.__doCallback(callback, extraArgs)

    def exitSquish(self):
        if self.track:
            self.ignore(self.track.getName())
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track.finish()
            self.track = None
        self.playingAnim = 'neutral'

    def enterDeadNeutral(self, ts=0, callback=None, extraArgs=[]):
        self.loop('dneutral')

    def exitDeadNeutral(self):
        self.stop()

    def enterDeadWalk(self, ts=0, callback=None, extraArgs=[]):
        self.loop('dwalk')

    def exitDeadWalk(self):
        self.stop()

    def setBackpack(self, pack):
        self.backpack = pack

    def getGhost(self):
        return 0

    def updateChatSoundDict(self):
        self.chatSoundDict['exclaim'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('exclaim'))
        self.chatSoundDict['question'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('question'))
        self.chatSoundDict['short'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('short'))
        self.chatSoundDict['medium'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('med'))
        self.chatSoundDict['long'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('long'))
        self.chatSoundDict['howl'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('howl'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['exclaim'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['question'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['short'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['medium'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['long'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['howl'],
                                         self.getPart('head'))

    def __actAsGone(self):
        if self.nametag3d:
            self.nametag3d.hide()
        if self.getShadow():
            self.getShadow().hide()
        if self.tokenIcon:
            self.tokenIcon.hide()
        #self.stashBodyCollisions()

    def __restoreHide(self):
        if self.tokenIcon:
            self.tokenIcon.show()
        if self.getShadow():
            self.getShadow().show()
        if self.nametag3d:
            self.nametag3d.show()
        if self.getGeomNode():
            self.getGeomNode().setTransparency(False)
            self.getGeomNode().setAlphaScale(1.0)
            self.getGeomNode().show()
        #self.unstashBodyCollisions()

    def handleGhost(self, flag):
        alpha = 1.0 if not flag else 0.25
        local = self == base.localAvatar
        if flag:
            if self.getAccessLevel() >= base.localAvatar.getAccessLevel():
                # Other staff members at this access level or higher should
                # be able to see this avatar still.
                alpha = 0.25
                #self.stashBodyCollisions()
            elif not local:
                self.getGeomNode().setTransparency(True)
                self.getGeomNode().setColorScale(1.0, 1.0, 1.0, 0.0)
                self.__actAsGone()
        else:
            self.__restoreHide()
        if local:
            self.getGeomNode().setTransparency(flag)
            self.getGeomNode().setColorScale(1.0, 1.0, 1.0, alpha)

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                self.notify.warning(
                    "animFSM in flux, state=%s, not requesting off" %
                    self.animFSM.getCurrentState().getName())
        else:
            self.notify.warning("animFSM has been deleted")
        if self.track != None:
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        return

    def disable(self):
        try:
            self.Toon_disabled
        except:
            self.Toon_disabled = 1
            self.ignoreAll()
            self.backpack = None
            self.collsSetup = False
            self.stopAnimations()
            self.removeAdminToken()
            ToonHead.delete(self)
            self.deleteCurrentToon()
            self.chatSoundDict = {}
            Avatar.Avatar.disable(self)

    def delete(self):
        try:
            self.Toon_deleted
        except:
            self.Toon_deleted = 1
            del self.animFSM
            self.forwardSpeed = None
            self.chatSoundDict = None
            self.rotateSpeed = None
            self.avatarType = None
            self.track = None
            self.standWalkRunReverse = None
            self.currentAnim = None
            self.toon_head = None
            self.forcedTorsoAnim = None
            self.lastForcedTorsoAnim = None
            self.toon_torso = None
            self.toon_legs = None
            self.gender = None
            self.headtype = None
            self.head = None
            self.legtype = None
            self.torsotype = None
            self.hr = None
            self.hg = None
            self.hb = None
            self.tr = None
            self.tg = None
            self.tb = None
            self.lr = None
            self.lg = None
            self.lb = None
            self.shir = None
            self.shig = None
            self.shib = None
            self.shor = None
            self.shog = None
            self.shob = None
            self.shirt = None
            self.sleeve = None
            self.short = None
            self.tag = None
            self.money = None
            self.lookAtTrack = None
            self.portal1 = None
            self.portal2 = None
            self.backpack = None
            self.fallSfx = None
            self.eyes = None
            self.myTaskId = None
            self.closedEyes = None
            self.soundChatBubble = None
            self.lastAction = None
            self.lastState = None
            self.playingAnim = None
            self.playingRate = None
            self.accessories = None
            Avatar.Avatar.delete(self)
        return

    def initCollisions(self):
        self.collNodePath.setCollideMask(BitMask32(0))
        self.collNodePath.node().setFromCollideMask(CIGlobals.WallBitmask)

        pusher = CollisionHandlerPusher()
        pusher.setInPattern("%in")
        pusher.addCollider(self.collNodePath, self)
        base.cTrav.addCollider(self.collNodePath, pusher)

    def deleteCurrentToon(self):
        if self.shadowCaster:
            self.shadowCaster.clear()
            self.shadowCaster = None

        for accessory in self.accessories:
            accessory.removeNode()
        self.accessories = []

        self.pupils = []

        if 'head' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['head']
        if 'torso' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['torso']
        if 'legs' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['legs']

        self.deleteShadow()
        self.removePart('head')
        self.removePart('torso')
        self.removePart('legs')

        self.clearPythonData()
        self.flush()

    def setAdminToken(self, tokenInstance):

        if tokenInstance:
            matPath = tokenInstance.getMaterialPath()
            self.tokenIcon = loader.loadModel(
                "phase_3/models/props/staffIcon.bam")
            self.tokenIcon.reparentTo(self)
            self.tokenIcon.setScale(0.75)
            self.tokenIcon.setShaderAuto()

            # Let's update the material.
            self.tokenIcon.setBSPMaterial(matPath, 1)

            # Let's position the icon above the nametag.
            x, y, z = self.nametag3d.getPos()
            self.tokenIcon.setPos(Vec3(x, y, z + self.tokenIcon.getSz()))

            r, g, b, _ = tokenInstance.getColor()

            # Let's add the glow.
            glow = loader.loadModel(
                'phase_4/models/minigames/particleGlow.bam')
            glow.reparentTo(self.tokenIcon)
            glow.setScale(2.50)
            glow.setColorScale((r, g, b, 0.50), 1)
            glow.setBSPMaterial('phase_4/maps/particleGlow.mat', 1)
            glow.setDepthWrite(False, 1)
            glow.setShaderAuto()
            glow.setTwoSided(1)

            self.tokenIconIval = Sequence(
                LerpHprInterval(self.tokenIcon,
                                duration=3.0,
                                hpr=Vec3(360, 0, 0),
                                startHpr=Vec3(0, 0, 0)))
            self.tokenIconIval.loop()
        else:
            self.removeAdminToken()

    def removeAdminToken(self):
        if self.tokenIcon != None and self.tokenIconIval != None:
            self.tokenIconIval.finish()
            self.tokenIcon.removeNode()
            self.tokenIconIval = None
            self.tokenIcon = None

    def playChatSfx(self, chatString):
        if not self.getGhost() or self.doId == base.localAvatar.doId:
            if "ooo" in chatString.lower():
                sfx = self.chatSoundDict['howl']
            elif "!" in chatString.lower():
                sfx = self.chatSoundDict['exclaim']
            elif "?" in chatString.lower():
                sfx = self.chatSoundDict['question']
            elif len(chatString) <= 9:
                sfx = self.chatSoundDict['short']
            elif 10 <= len(chatString) <= 19:
                sfx = self.chatSoundDict['medium']
            elif len(chatString) >= 20:
                sfx = self.chatSoundDict['long']
            base.playSfx(sfx, node=self)

    def chatStompComplete(self, chatString):
        if not self.thoughtInProg and CIGlobals.getSettingsMgr().getSetting(
                "chs").getValue():
            self.playChatSfx(chatString)

    def setName(self, nameString):
        Avatar.Avatar.setName(self, nameString)

    def setDNAStrand(self, dnaStrand, makeTag=1):
        ToonDNA.ToonDNA.setDNAStrand(self, dnaStrand)
        self.deleteCurrentToon()
        self.generateToon(makeTag)

    def generateMask(self):
        # No accessories yet.

        if self.shirt == self.maleTopDNA2maleTop['135'][
                0] or self.shirt == self.maleTopDNA2maleTop['136'][0]:
            # This toon is wearing the tsa suit, give them some sweet shades.
            name = 'tsaGlasses'
            glasses = loader.loadModel(
                AccessoryGlobals.AccessoryName2Model[name])
            glassesNode = self.getPart('head').attachNewNode('glassesNode')
            glasses.reparentTo(glassesNode)
            data = AccessoryGlobals.MaskTransExtended[name].get(self.animal)
            if not data:
                data = AccessoryGlobals.MaskTrans.get(self.animal)
                posHprScale = AccessoryGlobals.MaskTrans[self.animal][
                    self.headLength]
            else:
                posHprScale = AccessoryGlobals.MaskTransExtended[name][
                    self.animal].get(self.headLength)
                if not posHprScale:
                    posHprScale = AccessoryGlobals.MaskTrans[self.animal][
                        self.headLength]

            glasses.setPos(posHprScale[0])
            glasses.setHpr(posHprScale[1])
            glasses.setScale(posHprScale[2])

            self.accessories.append(glassesNode)

    def generateToon(self, makeTag=1):
        self.generateLegs()
        self.generateTorso()
        self.generateHead()
        self.setToonColor()
        self.setClothes()
        self.setGloves()
        self.parentToonParts()
        self.rescaleToon()
        self.generateMask()

        # Make torso subparts so we can play a run animation on the pants but another animation on the spine and arms.
        if self.gender == 'boy':
            self.makeSubpart("torso-pants", [
                "def_left_pant_bottom", "def_left_pant_top",
                "def_right_pant_bottom", "def_right_pant_top"
            ],
                             parent="torso")
        elif self.gender == 'girl':
            if self.torso == 'dgs_skirt':
                self.makeSubpart("torso-pants", [
                    "def_left_skirt_backA", "def_left_skirt_frontA",
                    "def_left_skirt_topA", "def_right_skirt_backA",
                    "def_right_skirt_frontA", "def_right_skirt_topA"
                ],
                                 parent="torso")
            elif self.torso == 'dgl_skirt':
                self.makeSubpart("torso-pants", [
                    "def_left_skirt_bottomA", "def_left_skirt_topA",
                    "def_right_hip"
                ],
                                 parent="torso")
            else:
                self.makeSubpart("torso-pants", [
                    "def_left_skirt_bottomA", "def_left_skirt_topA",
                    "def_right_skirt_bottomA", "def_right_skirt_topA"
                ],
                                 parent="torso")
        self.makeSubpart("torso-top", ["def_spineB"], parent="torso")

        Avatar.Avatar.initShadow(self)

        self.updateChatSoundDict()
        self.setBlend(frameBlend=True)

        bodyMat = CIGlobals.getCharacterMaterial(shininess=5,
                                                 specular=(0.5, 0.5, 0.5, 1))
        self.setMaterial(bodyMat, 1)

        if not hasattr(base, 'localAvatar') or base.localAvatar != self:
            self.setupPhysics(1.0, self.getHeight())

        # We can safely optimize the scene graph and combine nodes since we're done manipulating
        # the separate pieces. After this point, the separate pieces of the toon are no
        # longer manipulatable, such as arms, sleeves, shirt, etc. If this needs to be done,
        # the toon will have to be regenerated.

        # Don't do it in Make-A-Toon though, as we have to be constantly modifying the pieces.
        if not self.mat:
            self.optimize()

        if makeTag:
            self.setupNameTag()
        if self.cr.isShowingPlayerIds:
            self.showAvId()

        self.loop('neutral')

    def optimize(self):
        self.getPart('legs').flattenStrong()
        self.postFlatten()

    def attachTNT(self):
        self.pies.attachTNT()
        self.holdTNTAnim()

    def detachTNT(self):
        self.pies.detachTNT()
        self.animFSM.request(self.animFSM.getCurrentState().getName())

    def holdTNTAnim(self):
        self.pose("toss", 22, partName="torso")

    def parentToonParts(self):
        self.attach('head', 'torso', 'def_head')
        self.attach('torso', 'legs', 'joint_hips')

    def unparentToonParts(self):
        self.getPart('head').reparentTo(self.getGeomNode())
        self.getPart('torso').reparentTo(self.getGeomNode())
        self.getPart('legs').reparentTo(self.getGeomNode())

    def getHeadHeight(self):
        animal = self.getAnimal()
        headScale = ToonGlobals.HeadScales[animal][2]
        headHeight = ToonGlobals.HeadHeightDict[self.head] * headScale
        return headHeight

    def rescaleToon(self):
        if not self.getHead():
            return

        animal = self.getAnimal()
        bodyScale = ToonGlobals.BodyScales[animal]
        headScale = ToonGlobals.HeadScales[animal][2]
        shoulderHeight = ToonGlobals.LegHeightDict[
            self.getLegs()] * bodyScale + ToonGlobals.TorsoHeightDict[
                self.getTorso()] * bodyScale
        height = shoulderHeight + ToonGlobals.HeadHeightDict[
            self.getHead()] * headScale
        bodyScale = ToonGlobals.BodyScales[animal]
        self.setAvatarScale(bodyScale)
        self.getPart('head').setScale(headScale)
        self.setHeight(height)

    def setGloves(self):
        color = self.getGloveColor()
        gloves = self.find('**/hands')
        gloves.setColor(color)

    def setClothes(self):
        shirt, shirtcolor = self.getShirtStyle()
        short, shortcolor = self.getShortStyle()
        sleeve, sleevecolor = self.getSleeveStyle()
        torsot = self.findAllMatches('**/torso-top')
        torsob = self.findAllMatches('**/torso-bot')
        sleeves = self.findAllMatches('**/sleeves')
        torsot.setBSPMaterial(shirt, 1)
        torsob.setBSPMaterial(short, 1)
        sleeves.setBSPMaterial(sleeve, 1)
        torsot.setColor(shirtcolor)
        sleeves.setColor(sleevecolor)
        torsob.setColor(shortcolor)

    def generateLegs(self):
        ToonGlobals.generateBodyPart(self, 'legs', self.getLegs(), 3, 'shorts')
        self.find('**/boots_long').stash()
        self.find('**/boots_short').stash()
        self.find('**/shoes').stash()

    def generateTorso(self):
        ToonGlobals.generateBodyPart(self, 'torso', self.getTorso(), 3, '')

    def generateHead(self, pat=0):
        gender = self.getGender()
        head = self.getAnimal()
        headtype = self.getHead()
        ToonHead.generateHead(self, gender, head, headtype)

    def setToonColor(self):
        self.setHeadColor()
        self.setTorsoColor()
        self.setLegColor()

    def setLegColor(self):
        legcolor = self.getLegColor()
        self.findAllMatches('**/legs').setColor(legcolor)
        self.findAllMatches('**/feet').setColor(legcolor)

    def setTorsoColor(self):
        torsocolor = self.getTorsoColor()
        self.findAllMatches('**/arms').setColor(torsocolor)
        self.findAllMatches('**/neck').setColor(torsocolor)
        self.findAllMatches('**/hands').setColor(1, 1, 1, 1)

    def enterOff(self, ts=0, callback=None, extraArgs=[]):
        self.currentAnim = None
        return

    def exitOff(self):
        pass

    def enterWin(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'win'
        self.sfx = base.audio3d.loadSfx("phase_3.5/audio/sfx/ENC_Win.ogg")
        self.sfx.setLoop(True)
        base.audio3d.attachSoundToObject(self.sfx, self)
        base.playSfx(self.sfx, node=self, looping=1)
        self.loop("win")

    def exitWin(self):
        self.stop()
        self.sfx.stop()
        del self.sfx
        self.playingAnim = 'neutral'

    def enterShrug(self, ts=0, callback=None, extraArgs=[]):
        self.play("shrug")

    def exitShrug(self):
        self.exitGeneral()

    def enterHDance(self, ts=0, callback=None, extraArgs=[]):
        self.play("hdance")

    def exitHDance(self):
        self.exitGeneral()

    def enterScientistWork(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scwork")

    def exitScientistWork(self):
        self.exitGeneral()

    def enterScientistEmcee(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scemcee")

    def exitScientistEmcee(self):
        self.exitGeneral()

    def enterScientistGame(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scgame")

    def exitScientistGame(self):
        self.exitGeneral()

    def enterScientistJealous(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scjealous")

    def exitScientistJealous(self):
        self.exitGeneral()

    def enterWave(self, ts=0, callback=None, extraArgs=[]):
        self.play("wave")

    def exitWave(self):
        self.exitGeneral()

    def enterLaugh(self, ts=0, callback=None, extraArgs=[]):
        self.setPlayRate(5.0, "neutral")
        self.loop("neutral")

    def exitLaugh(self):
        self.setPlayRate(1.0, "neutral")
        self.stop()

    def enterNeutral(self, ts=0, callback=None, extraArgs=[]):
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop("neutral", partName="legs")
            return
        self.loop("neutral")
        self.playingAnim = 'neutral'

    def exitNeutral(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def exitGeneral(self):
        self.stop()

    def enterRun(self, ts=0, callback=None, extraArgs=[]):
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop('run', partName='legs')
            return
        self.loop("run")

    def exitRun(self):
        self.exitGeneral()

    def enterWalk(self, ts=0, callback=None, extraArgs=[]):
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop('walk', partName='legs')
            return
        self.loop('walk')

    def exitWalk(self):
        self.exitGeneral()

    def enterWalkBack(self, ts=0, callback=None, extraArgs=[]):
        self.setPlayRate(-1.0, "walk")
        self.enterWalk()

    def exitWalkBack(self):
        self.exitWalk()
        self.setPlayRate(1.0, "walk")

    def enterOpenBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book1 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book1.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self,
                                   "book",
                                   startFrame=CIGlobals.OpenBookFromFrame,
                                   endFrame=CIGlobals.OpenBookToFrame,
                                   name=self.uniqueName('enterOpenBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback,
                        [callback, extraArgs])
        self.track.start(ts)
        self.book1.play("chan",
                        fromFrame=CIGlobals.OpenBookFromFrame,
                        toFrame=CIGlobals.OpenBookToFrame)

    def exitOpenBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book1:
            self.book1.cleanup()
            self.book1 = None
        self.playingAnim = 'neutral'

    def enterReadBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book2 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book2.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))

        self.pingpong("book",
                      fromFrame=CIGlobals.ReadBookFromFrame,
                      toFrame=CIGlobals.ReadBookToFrame)
        self.book2.pingpong("chan",
                            fromFrame=CIGlobals.ReadBookFromFrame,
                            toFrame=CIGlobals.ReadBookToFrame)

    def exitReadBook(self):
        if self.book2:
            self.book2.cleanup()
            self.book2 = None
        self.playingAnim = 'neutral'

    def enterCloseBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book3 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book3.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self,
                                   "book",
                                   startFrame=CIGlobals.CloseBookFromFrame,
                                   endFrame=CIGlobals.CloseBookToFrame,
                                   name=self.uniqueName('enterCloseBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback,
                        [callback, extraArgs])
        self.track.start(ts)
        self.book3.play("chan",
                        fromFrame=CIGlobals.CloseBookFromFrame,
                        toFrame=CIGlobals.CloseBookToFrame)
        self.lerpLookAt(self.getPart('head'), (0, 0, 0))

    def exitCloseBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book3:
            self.book3.cleanup()
            self.book3 = None
        self.playingAnim = 'neutral'

    def enterTeleportOut(self, ts=0, callback=None, extraArgs=[]):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": enterTeleportOut")
        self.playingAnim = 'tele'
        self.portal1 = Actor(
            "phase_3.5/models/props/portal-mod.bam",
            {"chan": "phase_3.5/models/props/portal-chan.bam"})
        self.portal1.play("chan")
        self.portal1.reparentTo(
            self.getPart('legs').find('**/def_joint_right_hold'))
        self.play("tele")
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterTeleportOut')
        else:
            name = 'enterTeleportOut'

        self.track = Sequence(Wait(0.4),
                              Func(self.teleportOutSfx),
                              Wait(1.3),
                              Func(self.throwPortal),
                              Wait(1.1),
                              Func(self.__actAsGone),
                              Wait(1.5),
                              name=name)

        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportOutDone,
                        [callback, extraArgs])
        self.track.start(ts)

    def doPortalBins(self, portal):
        portal.setBin('portal', 19)
        portal.setDepthWrite(0)
        portal.setDepthTest(0)

    def teleportOutDone(self, callback, requestStatus):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": teleportOutDone")
        self.__doCallback(callback, requestStatus)
        self.exitTeleportOut()

    def teleportOutSfx(self):
        self.outSfx = base.audio3d.loadSfx(
            "phase_3.5/audio/sfx/AV_teleport.ogg")
        base.audio3d.attachSoundToObject(self.outSfx, self.portal1)
        base.playSfx(self.outSfx, node=self)

    def throwPortal(self):
        self.doPortalBins(self.portal1)
        self.portal1.reparentTo(self.getPart('legs').find('**/joint_nameTag'))
        self.portal1.setScale(CIGlobals.PortalScale)
        self.portal1.setY(6.5)
        self.portal1.setH(180)

    def exitTeleportOut(self):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": exitTeleportOut")
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal1:
            self.portal1.cleanup()
            self.portal1 = None
        if hasattr(self, 'shadow') and self.shadow is not None:
            self.shadow.show()
        self.__restoreHide()
        self.playingAnim = 'neutral'

    def getTeleportInTrack(self, portal):
        self.doPortalBins(portal)

        holeTrack = Sequence()
        holeTrack.append(Func(portal.reparentTo, self))
        pos = Point3(0, -2.4, 0)
        holeTrack.append(Func(portal.setPos, pos))
        holeTrack.append(
            ActorInterval(portal, 'chan', startTime=3.4, endTime=3.1))
        holeTrack.append(Wait(0.6))
        holeTrack.append(
            ActorInterval(portal, 'chan', startTime=3.1, endTime=3.4))

        def restorePortal(portal):
            portal.setPos(0, 0, 0)
            portal.detachNode()
            portal.clearBin()
            portal.clearDepthTest()
            portal.clearDepthWrite()

        holeTrack.append(Func(restorePortal, portal))
        toonTrack = Sequence(Wait(0.3), Func(self.__restoreHide),
                             ActorInterval(self, 'happy', startTime=0.45))

        if hasattr(self, 'uniqueName'):
            trackName = self.uniqueName('teleportIn')
        else:
            trackName = 'teleportIn'
        return Parallel(toonTrack, holeTrack, name=trackName)

    def enterTeleportIn(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'happy'
        self.portal2 = Actor(
            "phase_3.5/models/props/portal-mod.bam",
            {"chan": "phase_3.5/models/props/portal-chan.bam"})
        self.show()
        self.getGeomNode().hide()
        self.nametag3d.hide()
        self.track = self.getTeleportInTrack(self.portal2)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportInDone,
                        [callback, extraArgs])
        if hasattr(self, 'acquireDelayDelete'):
            self.track.delayDelete = DelayDelete.DelayDelete(
                self, self.track.getName())
        self.track.start(ts)

    def teleportInDone(self, callback, extraArgs):
        self.exitTeleportIn()
        self.__doCallback(callback, extraArgs)

    def exitTeleportIn(self):
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal2:
            self.portal2.cleanup()
            self.portal2 = None
        if self.nametag3d:
            self.nametag3d.show()
        self.playingAnim = 'neutral'

    def enterFallFWD(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'fallf'
        self.play("fallf")
        Sequence(Wait(0.5), SoundInterval(self.fallSfx, node=self)).start()

    def exitFallFWD(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterFallBCK(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'fallb'
        self.play("fallb")
        Sequence(Wait(0.5), SoundInterval(self.fallSfx, node=self)).start()

    def exitFallBCK(self):
        self.playingAnim = 'neutral'
        self.exitGeneral()

    def enterHappyJump(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'happy'
        self.play("happy")

    def exitHappyJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterSwim(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'swim'
        self.loop("swim")

        self.resetTorsoRotation()

        toon = self.getGeomNode()
        toon.setP(-89.0)

        if self.shadow:
            self.shadow.hide()

        self.swimBobTrack = Sequence(
            LerpPosInterval(toon,
                            duration=1,
                            pos=(0, -3, 3),
                            startPos=(0, -3, 4),
                            blendType='easeInOut'),
            LerpPosInterval(toon,
                            duration=1,
                            pos=(0, -3, 4),
                            startPos=(0, -3, 3),
                            blendType='easeInOut'))
        self.swimBobTrack.loop()
        self.nametag3d.setZ(5.0)

    def exitSwim(self):
        self.swimBobTrack.finish()
        del self.swimBobTrack
        if self.shadow:
            self.shadow.show()
        self.exitGeneral()
        self.getGeomNode().setPosHpr(0, 0, 0, 0, 0, 0)
        nt = self.nametag3d
        nt.setX(0)
        nt.setY(0)
        nt.setZ(self.getHeight() + 0.5)
        self.playingAnim = 'neutral'

    def enterDied(self, ts=0, callback=None, extraArgs=[]):
        def shouldDisableGags():
            if hasattr(self, 'disableGags'):
                self.disableGags()
            if hasattr(self, 'setEquippedAttack'):
                self.setEquippedAttack(-1)

        self.playingAnim = 'lose'
        self.isdying = True
        self.play("lose")
        self.track = Sequence(Func(self.clearForcedTorsoAnim),
                              Func(shouldDisableGags),
                              Wait(2.2),
                              Func(self.dieSfx),
                              Wait(2.8),
                              self.getGeomNode().scaleInterval(
                                  2,
                                  Point3(0.01),
                                  startScale=(self.getGeomNode().getScale())),
                              Func(self.delToon),
                              name=self.uniqueName('enterDied'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.diedDone,
                        [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(
            self, 'enterTeleportOut')
        self.track.start(ts)

    def diedDone(self, callback, extraArgs):
        self.__doCallback(callback, extraArgs)
        self.exitDied()

    def __doCallback(self, callback, extraArgs):
        if callback:
            if extraArgs:
                callback(*extraArgs)
            else:
                callback()

    def dieSfx(self):
        self.Losesfx = base.audio3d.loadSfx("phase_5/audio/sfx/ENC_Lose.ogg")
        base.audio3d.attachSoundToObject(self.Losesfx, self)
        base.playSfx(self.Losesfx, node=self)

    def delToon(self):
        self.isdead = True

    def exitDied(self):
        if self.track != None:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if hasattr(self, 'enableGags'):
            self.enableGags()

        self.rescaleToon()
        self.playingAnim = 'neutral'

    def enterBow(self, ts=0, callback=None, extraArgs=[]):
        self.play("bow")
        self.playingAnim = 'bow'

    def exitBow(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterJump(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'jump'
        self.loop("jump")

    def exitJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterLeap(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'leap'
        self.loop("leap")

    def exitLeap(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterCringe(self, ts=0, callback=None, extraArgs=[]):
        self.play("cringe")

    def exitCringe(self):
        self.exitGeneral()

    def enterConked(self, ts=0, callback=None, extraArgs=[]):
        self.play("conked")

    def exitConked(self):
        self.exitGeneral()
Exemple #5
0
class Toon(Avatar.Avatar, ToonHead, ToonDNA.ToonDNA):

    def __init__(self, cr, mat = 0):
        self.cr = cr
        try:
            self.Toon_initialized
            return
        except:
            self.Toon_initialized = 1

        Avatar.Avatar.__init__(self, mat)
        ToonDNA.ToonDNA.__init__(self)
        ToonHead.__init__(self, cr)
        self.forwardSpeed = 0.0
        self.rotateSpeed = 0.0
        self.avatarType = CIGlobals.Toon
        self.track = None
        self.standWalkRunReverse = None
        self.playingAnim = None
        self.tag = None
        self.money = 0
        self.lookAtTrack = None
        self.portal1 = None
        self.portal2 = None
        self.gunAttached = False
        self.gun = None
        self.tokenIcon = None
        self.tokenIconIval = None
        self.backpack = None
        self.forcedTorsoAnim = None
        self.fallSfx = base.audio3d.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3')
        base.audio3d.attachSoundToObject(self.fallSfx, self)
        self.eyes = loader.loadTexture('phase_3/maps/eyes.jpg', 'phase_3/maps/eyes_a.rgb')
        self.myTaskId = random.uniform(0, 1231231232132131231232L)
        self.closedEyes = loader.loadTexture('phase_3/maps/eyesClosed.jpg', 'phase_3/maps/eyesClosed_a.rgb')
        self.soundChatBubble = loader.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.mp3')
        self.shadowCaster = None
        self.chatSoundDict = {}
        self.animFSM = ClassicFSM('Toon', [State('off', self.enterOff, self.exitOff),
         State('neutral', self.enterNeutral, self.exitNeutral),
         State('swim', self.enterSwim, self.exitSwim),
         State('walk', self.enterWalk, self.exitWalk),
         State('run', self.enterRun, self.exitRun),
         State('openBook', self.enterOpenBook, self.exitOpenBook),
         State('readBook', self.enterReadBook, self.exitReadBook),
         State('closeBook', self.enterCloseBook, self.exitCloseBook),
         State('teleportOut', self.enterTeleportOut, self.exitTeleportOut),
         State('teleportIn', self.enterTeleportIn, self.exitTeleportIn),
         State('died', self.enterDied, self.exitDied),
         State('fallFWD', self.enterFallFWD, self.exitFallFWD),
         State('fallBCK', self.enterFallBCK, self.exitFallBCK),
         State('jump', self.enterJump, self.exitJump),
         State('leap', self.enterLeap, self.exitLeap),
         State('laugh', self.enterLaugh, self.exitLaugh),
         State('happy', self.enterHappyJump, self.exitHappyJump),
         State('shrug', self.enterShrug, self.exitShrug),
         State('hdance', self.enterHDance, self.exitHDance),
         State('wave', self.enterWave, self.exitWave),
         State('scientistEmcee', self.enterScientistEmcee, self.exitScientistEmcee),
         State('scientistWork', self.enterScientistWork, self.exitScientistWork),
         State('scientistGame', self.enterScientistGame, self.exitScientistGame),
         State('scientistJealous', self.enterScientistJealous, self.exitScientistJealous),
         State('cringe', self.enterCringe, self.exitCringe),
         State('conked', self.enterConked, self.exitConked),
         State('win', self.enterWin, self.exitWin),
         State('walkBack', self.enterWalkBack, self.exitWalkBack),
         State('deadNeutral', self.enterDeadNeutral, self.exitDeadNeutral),
         State('deadWalk', self.enterDeadWalk, self.exitDeadWalk),
         State('squish', self.enterSquish, self.exitSquish),
         State('Happy', self.enterHappy, self.exitHappy),
         State('Sad', self.enterSad, self.exitSad)], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()
        if not hasattr(base, 'localAvatar') or not base.localAvatar == self:
            Avatar.Avatar.initializeBodyCollisions(self, self.avatarType, 3, 1)
        return

    def enterHappy(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = None
        self.standWalkRunReverse = (('neutral', 1.0),
         ('walk', 1.0),
         ('run', 1.0),
         ('walk', -1.0))
        self.setSpeed(self.forwardSpeed, self.rotateSpeed)
        return

    def exitHappy(self):
        self.standWalkRunReverse = None
        self.stop()
        return

    def enterSad(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'sad'
        self.standWalkRunReverse = (('dneutral', 1.0),
         ('dwalk', 1.2),
         ('dwalk', 1.2),
         ('dwalk', -1.0))
        self.setSpeed(0, 0)

    def exitSad(self):
        self.standWalkRunReverse = None
        self.stop()
        return

    def setSpeed(self, forwardSpeed, rotateSpeed):
        self.forwardSpeed = forwardSpeed
        self.rotateSpeed = rotateSpeed
        action = None
        if self.standWalkRunReverse != None:
            if forwardSpeed >= CIGlobals.RunCutOff:
                action = CIGlobals.RUN_INDEX
            elif forwardSpeed > CIGlobals.WalkCutOff:
                action = CIGlobals.WALK_INDEX
            elif forwardSpeed < -CIGlobals.WalkCutOff:
                action = CIGlobals.REVERSE_INDEX
            elif rotateSpeed != 0.0:
                action = CIGlobals.WALK_INDEX
            else:
                action = CIGlobals.STAND_INDEX
            anim, rate = self.standWalkRunReverse[action]
            if anim != self.playingAnim:
                self.playingAnim = anim
                self.stop()
                self.loop(anim)
                self.setPlayRate(rate, anim)
        return action

    def enterSquish(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'squish'
        sound = loader.loadSfx('phase_9/audio/sfx/toon_decompress.mp3')
        lerpTime = 0.1
        node = self.getGeomNode().getChild(0)
        origScale = node.getScale()
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('getSquished')
        else:
            name = 'getSquished'
        self.track = Sequence(LerpScaleInterval(node, lerpTime, VBase3(2, 2, 0.025), blendType='easeInOut'), Wait(1.0), Parallel(Sequence(Wait(0.4), LerpScaleInterval(node, lerpTime, VBase3(1.4, 1.4, 1.4), blendType='easeInOut'), LerpScaleInterval(node, lerpTime / 2.0, VBase3(0.8, 0.8, 0.8), blendType='easeInOut'), LerpScaleInterval(node, lerpTime / 3.0, origScale, blendType='easeInOut')), ActorInterval(self, 'happy', startTime=0.2), SoundInterval(sound)), name=name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.squishDone, [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.start(ts)

    def squishDone(self, callback = None, extraArgs = []):
        self.__doCallback(callback, extraArgs)

    def exitSquish(self):
        if self.track:
            self.ignore(self.track.getName())
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track.finish()
            self.track = None
        self.playingAnim = 'neutral'
        return

    def enterDeadNeutral(self, ts = 0, callback = None, extraArgs = []):
        self.loop('dneutral')

    def exitDeadNeutral(self):
        self.stop()

    def enterDeadWalk(self, ts = 0, callback = None, extraArgs = []):
        self.loop('dwalk')

    def exitDeadWalk(self):
        self.stop()

    def setBackpack(self, pack):
        self.backpack = pack

    def getGhost(self):
        return 0

    def updateChatSoundDict(self):
        self.chatSoundDict['exclaim'] = base.audio3d.loadSfx(self.getToonAnimalNoise('exclaim'))
        self.chatSoundDict['question'] = base.audio3d.loadSfx(self.getToonAnimalNoise('question'))
        self.chatSoundDict['short'] = base.audio3d.loadSfx(self.getToonAnimalNoise('short'))
        self.chatSoundDict['medium'] = base.audio3d.loadSfx(self.getToonAnimalNoise('med'))
        self.chatSoundDict['long'] = base.audio3d.loadSfx(self.getToonAnimalNoise('long'))
        self.chatSoundDict['howl'] = base.audio3d.loadSfx(self.getToonAnimalNoise('howl'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['exclaim'], self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['question'], self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['short'], self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['medium'], self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['long'], self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['howl'], self.getPart('head'))

    def ghostOn(self):
        self.getGeomNode().hide()
        self.getNameTag().hide()
        self.getShadow().hide()
        if self.tokenIcon:
            self.tokenIcon.hide()
        self.stashBodyCollisions()

    def ghostOff(self):
        self.unstashBodyCollisions()
        if self.tokenIcon:
            self.tokenIcon.show()
        self.getShadow().show()
        self.getNameTag().show()
        self.getGeomNode().show()

    def attachGun(self, gunName):
        self.detachGun()
        if gunName == 'pistol':
            self.gun = loader.loadModel('phase_4/models/props/water-gun.bam')
            self.gun.reparentTo(self.find('**/def_joint_right_hold'))
            self.gun.setPos(Point3(0.28, 0.1, 0.08))
            self.gun.setHpr(VBase3(85.6, -4.44, 94.43))
            self.gunAttached = True
        elif gunName == 'shotgun':
            self.gun = loader.loadModel('phase_4/models/props/shotgun.egg')
            self.gun.setScale(0.75)
            self.gun.reparentTo(self.find('**/def_joint_right_hold'))
            self.gun.setPos(Point3(-0.5, -0.2, 0.19))
            self.gun.setHpr(Vec3(350, 272.05, 0))
            color = random.choice([VBase4(1, 0.25, 0.25, 1), VBase4(0.25, 1, 0.25, 1), VBase4(0.25, 0.25, 1, 1)])
            self.gun.setColorScale(color)
            self.gunAttached = True

    def detachGun(self):
        if self.gun and self.gunAttached:
            self.gun.removeNode()
            self.gun = None
            self.gunAttached = False
        return

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                notify.warning('animFSM in flux, state=%s, not requesting off' % self.animFSM.getCurrentState().getName())
        else:
            notify.warning('animFSM has been deleted')
        if self.track != None:
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        return

    def disable(self):
        try:
            self.Toon_disabled
        except:
            self.Toon_disabled = 1
            self.backpack = None
            self.stopAnimations()
            self.removeAdminToken()
            ToonHead.delete(self)
            self.deleteCurrentToon()
            self.chatSoundDict = {}

        return

    def delete(self):
        try:
            self.Toon_deleted
        except:
            self.Toon_deleted = 1
            del self.animFSM
            self.forwardSpeed = None
            self.chatSoundDict = None
            self.rotateSpeed = None
            self.avatarType = None
            self.track = None
            self.standWalkRunReverse = None
            self.currentAnim = None
            self.toon_head = None
            self.forcedTorsoAnim = None
            self.toon_torso = None
            self.toon_legs = None
            self.gender = None
            self.headtype = None
            self.head = None
            self.legtype = None
            self.torsotype = None
            self.hr = None
            self.hg = None
            self.hb = None
            self.tr = None
            self.tg = None
            self.tb = None
            self.lr = None
            self.lg = None
            self.lb = None
            self.shir = None
            self.shig = None
            self.shib = None
            self.shor = None
            self.shog = None
            self.shob = None
            self.shirt = None
            self.sleeve = None
            self.short = None
            self.tag = None
            self.money = None
            self.lookAtTrack = None
            self.portal1 = None
            self.portal2 = None
            self.backpack = None
            self.fallSfx = None
            self.eyes = None
            self.myTaskId = None
            self.closedEyes = None
            self.soundChatBubble = None
            self.lastAction = None
            self.lastState = None
            self.playingAnim = None
            Avatar.Avatar.delete(self)

        return

    def initCollisions(self):
        self.collNodePath.setCollideMask(BitMask32(0))
        self.collNodePath.node().setFromCollideMask(CIGlobals.WallBitmask)
        pusher = CollisionHandlerPusher()
        pusher.setInPattern('%in')
        pusher.addCollider(self.collNodePath, self)
        base.cTrav.addCollider(self.collNodePath, pusher)

    def deleteCurrentToon(self):
        if self.shadowCaster:
            self.shadowCaster.clear()
            self.shadowCaster = None
        try:
            self.stopLookAround()
            self.stopBlink()
        except:
            pass

        self.pupils = []
        if 'head' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['head']
        if 'torso' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['torso']
        if 'legs' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['legs']
        self.deleteShadow()
        self.removePart('head')
        self.removePart('torso')
        self.removePart('legs')
        self.detachGun()
        return

    def enterGagShop(self):
        DirectLabel(text='ENTERED GAG SHOP', relief=None, text_scale=0.08)
        return

    def setAdminToken(self, tokenId):
        tokens = {0: 500}
        if tokenId in tokens.keys():
            icons = loader.loadModel('phase_3/models/props/gm_icons.bam')
            self.tokenIcon = icons.find('**/access_level_%s' % tokens[tokenId])
            self.tokenIcon.reparentTo(self)
            x = self.getNameTag().getX()
            y = self.getNameTag().getY()
            z = self.getNameTag().getZ()
            self.tokenIcon.setPos(Vec3(x, y, z) + (0, 0, 0.5))
            self.tokenIcon.setScale(0.4)
            self.tokenIconIval = Sequence(LerpHprInterval(self.tokenIcon, duration=3.0, hpr=Vec3(360, 0, 0), startHpr=Vec3(0, 0, 0)))
            self.tokenIconIval.loop()
            icons.removeNode()

    def removeAdminToken(self):
        if self.tokenIcon != None and self.tokenIconIval != None:
            self.tokenIconIval.finish()
            self.tokenIcon.removeNode()
            self.tokenIconIval = None
            self.tokenIcon = None
        return

    def setChat(self, chatString):
        if not self.isThought(chatString):
            if not self.getGhost() or self.doId == base.localAvatar.doId:
                if 'ooo' in chatString.lower():
                    sfx = self.chatSoundDict['howl']
                elif '!' in chatString.lower():
                    sfx = self.chatSoundDict['exclaim']
                elif '?' in chatString.lower():
                    sfx = self.chatSoundDict['question']
                elif len(chatString) <= 9:
                    sfx = self.chatSoundDict['short']
                elif 10 <= len(chatString) <= 19:
                    sfx = self.chatSoundDict['medium']
                elif len(chatString) >= 20:
                    sfx = self.chatSoundDict['long']
                sfx.play()
        Avatar.Avatar.setChat(self, chatString)

    def setName(self, nameString):
        Avatar.Avatar.setName(self, nameString, avatarType=self.avatarType)

    def setDNAStrand(self, dnaStrand, makeTag = 1):
        ToonDNA.ToonDNA.setDNAStrand(self, dnaStrand)
        self.deleteCurrentToon()
        self.generateToon(makeTag)

    def generateToon(self, makeTag = 1):
        self.generateLegs()
        self.generateTorso()
        self.generateHead()
        self.setToonColor()
        self.setClothes()
        self.setGloves()
        self.parentToonParts()
        self.rescaleToon()
        if makeTag:
            self.setupNameTag()
        Avatar.Avatar.initShadow(self)
        if self.cr.isShowingPlayerIds:
            self.showAvId()
        self.updateChatSoundDict()

    def attachTNT(self):
        self.pies.attachTNT()
        self.holdTNTAnim()

    def detachTNT(self):
        self.pies.detachTNT()
        self.animFSM.request(self.animFSM.getCurrentState().getName())

    def holdTNTAnim(self):
        self.pose('toss', 22, partName='torso')

    def parentToonParts(self):
        self.attach('head', 'torso', 'def_head')
        self.attach('torso', 'legs', 'joint_hips')

    def unparentToonParts(self):
        self.getPart('head').reparentTo(self.getGeomNode())
        self.getPart('torso').reparentTo(self.getGeomNode())
        self.getPart('legs').reparentTo(self.getGeomNode())

    def rescaleToon(self):
        animal = self.getAnimal()
        bodyScale = CIGlobals.toonBodyScales[animal]
        headScale = CIGlobals.toonHeadScales[animal][2]
        shoulderHeight = CIGlobals.legHeightDict[self.legs] * bodyScale + CIGlobals.torsoHeightDict[self.torso] * bodyScale
        height = shoulderHeight + CIGlobals.headHeightDict[self.head] * headScale
        bodyScale = CIGlobals.toonBodyScales[animal]
        self.setAvatarScale(bodyScale)
        self.setHeight(height)

    def setGloves(self):
        color = self.getGloveColor()
        gloves = self.find('**/hands')
        gloves.setColor(color)

    def setClothes(self):
        shirt, shirtcolor = self.getShirtStyle()
        short, shortcolor = self.getShortStyle()
        sleeve, sleevecolor = self.getSleeveStyle()
        torsot = self.findAllMatches('**/torso-top')
        torsob = self.findAllMatches('**/torso-bot')
        sleeves = self.findAllMatches('**/sleeves')
        torsot.setTexture(loader.loadTexture(shirt), 1)
        torsob.setTexture(loader.loadTexture(short), 1)
        sleeves.setTexture(loader.loadTexture(sleeve), 1)
        torsot.setColor(shirtcolor)
        sleeves.setColor(sleevecolor)
        torsob.setColor(shortcolor)

    def generateLegs(self):
        legtype = self.getLegs()
        self.loadModel('phase_3/models/char/tt_a_chr_' + legtype + '_shorts_legs_' + str(CIGlobals.getModelDetail(self.avatarType)) + '.bam', 'legs')
        self.loadAnims({'neutral': 'phase_3/models/char/tt_a_chr_' + legtype + '_shorts_legs_neutral.bam',
         'run': 'phase_3/models/char/tt_a_chr_' + legtype + '_shorts_legs_run.bam',
         'walk': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_walk.bam',
         'pie': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_pie-throw.bam',
         'fallb': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_slip-backward.bam',
         'fallf': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_slip-forward.bam',
         'lose': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_lose.bam',
         'win': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_victory-dance.bam',
         'squirt': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_water-gun.bam',
         'zend': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_jump-zend.bam',
         'tele': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_teleport.bam',
         'book': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_book.bam',
         'leap': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_leap_zhang.bam',
         'jump': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_jump-zhang.bam',
         'happy': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_jump.bam',
         'shrug': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_shrug.bam',
         'hdance': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_happy-dance.bam',
         'wave': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_wave.bam',
         'scemcee': 'phase_4/models/char/tt_a_chr_dgm_shorts_legs_scientistEmcee.bam',
         'scwork': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_scientistWork.bam',
         'scgame': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_scientistGame.bam',
         'scjealous': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_scientistJealous.bam',
         'swim': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_swim.bam',
         'toss': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_toss.bam',
         'cringe': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_cringe.bam',
         'conked': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_conked.bam',
         'catchneutral': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_gameneutral.bam',
         'catchrun': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_gamerun.bam',
         'hold-bottle': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_hold-bottle.bam',
         'push-button': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_press-button.bam',
         'happy-dance': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_happy-dance.bam',
         'juggle': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_juggle.bam',
         'shout': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_shout.bam',
         'dneutral': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_sad-neutral.bam',
         'dwalk': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_losewalk.bam',
         'smooch': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_smooch.bam',
         'conked': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_conked.bam',
         'sound': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_shout.bam',
         'sprinkle-dust': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_sprinkle-dust.bam',
         'start-sit': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_intoSit.bam',
         'sit': 'phase_4/models/char/char/tt_a_chr_' + legtype + '_shorts_legs_sit.bam'}, 'legs')
        self.findAllMatches('**/boots_long').stash()
        self.findAllMatches('**/boots_short').stash()
        self.findAllMatches('**/shoes').stash()

    def generateTorso(self):
        torsotype = self.getTorso()
        self.loadModel('phase_3/models/char/tt_a_chr_' + torsotype + '_torso_' + str(CIGlobals.getModelDetail(self.avatarType)) + '.bam', 'torso')
        self.loadAnims({'neutral': 'phase_3/models/char/tt_a_chr_' + torsotype + '_torso_neutral.bam',
         'run': 'phase_3/models/char/tt_a_chr_' + torsotype + '_torso_run.bam',
         'walk': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_walk.bam',
         'pie': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_pie-throw.bam',
         'fallb': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_slip-backward.bam',
         'fallf': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_slip-forward.bam',
         'lose': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_lose.bam',
         'win': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_victory-dance.bam',
         'squirt': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_water-gun.bam',
         'zend': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_jump-zend.bam',
         'tele': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_teleport.bam',
         'book': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_book.bam',
         'leap': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_leap_zhang.bam',
         'jump': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_jump-zhang.bam',
         'happy': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_jump.bam',
         'shrug': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_shrug.bam',
         'hdance': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_happy-dance.bam',
         'wave': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_wave.bam',
         'scemcee': 'phase_4/models/char/tt_a_chr_dgm_shorts_torso_scientistEmcee.bam',
         'scwork': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_scientistWork.bam',
         'scgame': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_scientistGame.bam',
         'scjealous': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_scientistJealous.bam',
         'swim': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_swim.bam',
         'toss': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_toss.bam',
         'cringe': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_cringe.bam',
         'conked': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_conked.bam',
         'catchneutral': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_gameneutral.bam',
         'catchrun': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_gamerun.bam',
         'hold-bottle': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_hold-bottle.bam',
         'push-button': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_press-button.bam',
         'happy-dance': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_happy-dance.bam',
         'juggle': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_juggle.bam',
         'shout': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_shout.bam',
         'dneutral': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_sad-neutral.bam',
         'dwalk': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_losewalk.bam',
         'smooch': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_smooch.bam',
         'conked': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_conked.bam',
         'sound': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_shout.bam',
         'sprinkle-dust': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_sprinkle-dust.bam',
         'start-sit': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_intoSit.bam',
         'sit': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_sit.bam'}, 'torso')

    def generateHead(self, pat = 0):
        gender = self.getGender()
        head = self.getAnimal()
        headtype = self.getHead()
        ToonHead.generateHead(self, gender, head, headtype)

    def setToonColor(self):
        self.setHeadColor()
        self.setTorsoColor()
        self.setLegColor()

    def setLegColor(self):
        legcolor = self.getLegColor()
        self.findAllMatches('**/legs').setColor(legcolor)
        self.findAllMatches('**/feet').setColor(legcolor)

    def setTorsoColor(self):
        torsocolor = self.getTorsoColor()
        self.findAllMatches('**/arms').setColor(torsocolor)
        self.findAllMatches('**/neck').setColor(torsocolor)
        self.findAllMatches('**/hands').setColor(1, 1, 1, 1)

    def setForcedTorsoAnim(self, string):
        self.forcedTorsoAnim = string
        self.loop(string, partName='torso')

    def clearForcedTorsoAnim(self):
        self.forcedTorsoAnim = None
        self.animFSM.request(self.animFSM.getCurrentState().getName())
        return

    def enterOff(self, ts = 0, callback = None, extraArgs = []):
        self.currentAnim = None
        return

    def exitOff(self):
        pass

    def enterWin(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'win'
        self.sfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/ENC_Win.mp3')
        self.sfx.setLoop(True)
        base.audio3d.attachSoundToObject(self.sfx, self)
        base.playSfx(self.sfx)
        self.loop('win')

    def exitWin(self):
        self.stop()
        self.sfx.stop()
        del self.sfx
        self.playingAnim = 'neutral'

    def enterShrug(self, ts = 0, callback = None, extraArgs = []):
        self.play('shrug')

    def exitShrug(self):
        self.exitGeneral()

    def enterHDance(self, ts = 0, callback = None, extraArgs = []):
        self.play('hdance')

    def exitHDance(self):
        self.exitGeneral()

    def enterScientistWork(self, ts = 0, callback = None, extraArgs = []):
        self.loop('scwork')

    def exitScientistWork(self):
        self.exitGeneral()

    def enterScientistEmcee(self, ts = 0, callback = None, extraArgs = []):
        self.loop('scemcee')

    def exitScientistEmcee(self):
        self.exitGeneral()

    def enterScientistGame(self, ts = 0, callback = None, extraArgs = []):
        self.loop('scgame')

    def exitScientistGame(self):
        self.exitGeneral()

    def enterScientistJealous(self, ts = 0, callback = None, extraArgs = []):
        self.loop('scjealous')

    def exitScientistJealous(self):
        self.exitGeneral()

    def enterWave(self, ts = 0, callback = None, extraArgs = []):
        self.play('wave')

    def exitWave(self):
        self.exitGeneral()

    def enterLaugh(self, ts = 0, callback = None, extraArgs = []):
        self.setPlayRate(5.0, 'neutral')
        self.loop('neutral')

    def exitLaugh(self):
        self.setPlayRate(1.0, 'neutral')
        self.stop()

    def enterNeutral(self, ts = 0, callback = None, extraArgs = []):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() != GagState.LOADED:
                    self.loop('neutral', partName='legs')
                    if self.animal == 'dog':
                        self.loop('neutral', partName='head')
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop('neutral', partName='legs')
            return
        else:
            self.loop('neutral')
            self.playingAnim = 'neutral'
            return

    def exitNeutral(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def exitGeneral(self):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() != GagState.LOADED:
                    self.stop(partName='legs')
                else:
                    self.stop()
            else:
                self.stop()
        else:
            self.stop()

    def enterRun(self, ts = 0, callback = None, extraArgs = []):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() != GagState.LOADED:
                    self.loop('run', partName='legs')
                    if self.animal == 'dog':
                        self.loop('run', partName='head')
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop('run', partName='legs')
            return
        else:
            self.loop('run')
            return

    def exitRun(self):
        self.exitGeneral()

    def enterWalk(self, ts = 0, callback = None, extraArgs = []):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() != GagState.LOADED:
                    self.loop('walk', partName='legs')
                    if self.animal == 'dog':
                        self.loop('walk', partName='head')
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop('walk', partName='legs')
            return
        else:
            self.loop('walk')
            return

    def exitWalk(self):
        self.exitGeneral()

    def enterWalkBack(self, ts = 0, callback = None, extraArgs = []):
        self.setPlayRate(-1.0, 'walk')
        self.enterWalk()

    def exitWalkBack(self):
        self.exitWalk()
        self.setPlayRate(1.0, 'walk')

    def enterOpenBook(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'book'
        self.book1 = Actor('phase_3.5/models/props/book-mod.bam', {'chan': 'phase_3.5/models/props/book-chan.bam'})
        self.book1.reparentTo(self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self, 'book', startFrame=CIGlobals.OpenBookFromFrame, endFrame=CIGlobals.OpenBookToFrame, name=self.uniqueName('enterOpenBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback, [callback, extraArgs])
        self.track.start(ts)
        self.book1.play('chan', fromFrame=CIGlobals.OpenBookFromFrame, toFrame=CIGlobals.OpenBookToFrame)

    def exitOpenBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book1:
            self.book1.cleanup()
            self.book1 = None
        self.playingAnim = 'neutral'
        return

    def enterReadBook(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'book'
        self.book2 = Actor('phase_3.5/models/props/book-mod.bam', {'chan': 'phase_3.5/models/props/book-chan.bam'})
        self.book2.reparentTo(self.getPart('torso').find('**/def_joint_right_hold'))
        self.pingpong('book', fromFrame=CIGlobals.ReadBookFromFrame, toFrame=CIGlobals.ReadBookToFrame)
        self.book2.pingpong('chan', fromFrame=CIGlobals.ReadBookFromFrame, toFrame=CIGlobals.ReadBookToFrame)

    def exitReadBook(self):
        if self.book2:
            self.book2.cleanup()
            self.book2 = None
        self.playingAnim = 'neutral'
        return

    def enterCloseBook(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'book'
        self.book3 = Actor('phase_3.5/models/props/book-mod.bam', {'chan': 'phase_3.5/models/props/book-chan.bam'})
        self.book3.reparentTo(self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self, 'book', startFrame=CIGlobals.CloseBookFromFrame, endFrame=CIGlobals.CloseBookToFrame, name=self.uniqueName('enterCloseBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback, [callback, extraArgs])
        self.track.start(ts)
        self.book3.play('chan', fromFrame=CIGlobals.CloseBookFromFrame, toFrame=CIGlobals.CloseBookToFrame)

    def exitCloseBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book3:
            self.book3.cleanup()
            self.book3 = None
        self.playingAnim = 'neutral'
        return

    def enterTeleportOut(self, ts = 0, callback = None, extraArgs = []):
        self.notify.info(str(self.doId) + '-' + str(self.zoneId) + ': enterTeleportOut')
        self.playingAnim = 'tele'
        self.portal1 = Actor('phase_3.5/models/props/portal-mod.bam', {'chan': 'phase_3.5/models/props/portal-chan.bam'})
        self.portal1.play('chan')
        self.portal1.reparentTo(self.getPart('legs').find('**/def_joint_right_hold'))
        self.play('tele')
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterTeleportOut')
        else:
            name = 'enterTeleportOut'
        self.track = Sequence(Wait(0.4), Func(self.teleportOutSfx), Wait(1.3), Func(self.throwPortal), Wait(3.4), name=name)
        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportOutDone, [callback, extraArgs])
        self.track.start(ts)

    def doPortalBins(self, portal):
        portal.setBin('shadow', 0)
        portal.setDepthWrite(0)
        portal.setDepthTest(0)

    def teleportOutDone(self, callback, requestStatus):
        self.notify.info(str(self.doId) + '-' + str(self.zoneId) + ': teleportOutDone')
        self.__doCallback(callback, requestStatus)
        self.exitTeleportOut()

    def teleportOutSfx(self):
        self.outSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/AV_teleport.mp3')
        base.audio3d.attachSoundToObject(self.outSfx, self.portal1)
        self.outSfx.play()

    def throwPortal(self):
        self.doPortalBins(self.portal1)
        self.portal1.reparentTo(self.getPart('legs').find('**/joint_nameTag'))
        self.portal1.setScale(CIGlobals.PortalScale)
        self.portal1.setY(6.5)
        self.portal1.setH(180)

    def exitTeleportOut(self):
        self.notify.info(str(self.doId) + '-' + str(self.zoneId) + ': exitTeleportOut')
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal1:
            self.portal1.cleanup()
            self.portal1 = None
        self.playingAnim = 'neutral'
        return

    def getTeleportInTrack(self, portal):
        self.doPortalBins(portal)
        holeTrack = Sequence()
        holeTrack.append(Func(portal.reparentTo, self))
        pos = Point3(0, -2.4, 0)
        holeTrack.append(Func(portal.setPos, pos))
        holeTrack.append(ActorInterval(portal, 'chan', startTime=3.4, endTime=3.1))
        holeTrack.append(Wait(0.6))
        holeTrack.append(ActorInterval(portal, 'chan', startTime=3.1, endTime=3.4))

        def restorePortal(portal):
            portal.setPos(0, 0, 0)
            portal.detachNode()
            portal.clearBin()
            portal.clearDepthTest()
            portal.clearDepthWrite()

        holeTrack.append(Func(restorePortal, portal))
        toonTrack = Sequence(Wait(0.3), Func(self.getGeomNode().show), Func(self.getNameTag().show), ActorInterval(self, 'happy', startTime=0.45))
        if hasattr(self, 'uniqueName'):
            trackName = self.uniqueName('teleportIn')
        else:
            trackName = 'teleportIn'
        return Parallel(toonTrack, holeTrack, name=trackName)

    def enterTeleportIn(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'happy'
        self.portal2 = Actor('phase_3.5/models/props/portal-mod.bam', {'chan': 'phase_3.5/models/props/portal-chan.bam'})
        self.show()
        self.getGeomNode().hide()
        self.getNameTag().hide()
        self.track = self.getTeleportInTrack(self.portal2)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportInDone, [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(self, self.track.getName())
        self.track.start(ts)

    def teleportInDone(self, callback, extraArgs):
        self.__doCallback(callback, extraArgs)
        self.exitTeleportIn()

    def exitTeleportIn(self):
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal2:
            self.portal2.cleanup()
            self.portal2 = None
        if self.getGeomNode():
            self.getGeomNode().show()
        if self.getNameTag():
            self.getNameTag().show()
        self.playingAnim = 'neutral'
        return

    def enterFallFWD(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'fallf'
        self.play('fallf')
        Sequence(Wait(0.5), Func(self.fallSfx.play)).start()

    def exitFallFWD(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterFallBCK(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'fallb'
        self.play('fallb')
        Sequence(Wait(0.5), Func(self.fallSfx.play)).start()

    def exitFallBCK(self):
        self.playingAnim = 'neutral'
        self.exitGeneral()

    def enterHappyJump(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'happy'
        self.play('happy')

    def exitHappyJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterSwim(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'swim'
        self.loop('swim')
        self.getGeomNode().setP(-89.0)
        self.getGeomNode().setZ(4.0)
        nt = self.getNameTag()
        nt.setX(0)
        nt.setY(-2)
        nt.setZ(5.0)

    def exitSwim(self):
        self.exitGeneral()
        self.getGeomNode().setP(0.0)
        self.getGeomNode().setZ(0.0)
        nt = self.getNameTag()
        nt.setX(0)
        nt.setY(0)
        nt.setZ(self.getHeight() + 0.3)
        self.playingAnim = 'neutral'

    def enterDied(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'lose'
        self.isdying = True
        self.play('lose')
        self.track = Sequence(Wait(2.2), Func(self.dieSfx), Wait(2.8), self.getGeomNode().scaleInterval(2, Point3(0.01), startScale=self.getGeomNode().getScale()), Func(self.delToon), name=self.uniqueName('enterDied'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.diedDone, [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(self, 'enterTeleportOut')
        self.track.start(ts)

    def diedDone(self, callback, extraArgs):
        self.__doCallback(callback, extraArgs)
        self.exitDied()

    def __doCallback(self, callback, extraArgs):
        if callback:
            if extraArgs:
                callback(*extraArgs)
            else:
                callback()

    def dieSfx(self):
        self.Losesfx = base.audio3d.loadSfx('phase_5/audio/sfx/ENC_Lose.mp3')
        base.audio3d.attachSoundToObject(self.Losesfx, self)
        self.Losesfx.play()

    def delToon(self):
        self.isdead = True

    def exitDied(self):
        if self.track != None:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        self.rescaleToon()
        self.playingAnim = 'neutral'
        return

    def enterJump(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'jump'
        self.loop('jump')

    def exitJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterLeap(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'leap'
        self.loop('leap')

    def exitLeap(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterCringe(self, ts = 0, callback = None, extraArgs = []):
        self.play('cringe')

    def exitCringe(self):
        self.exitGeneral()

    def enterConked(self, ts = 0, callback = None, extraArgs = []):
        self.play('conked')

    def exitConked(self):
        self.exitGeneral()
class Suit(Avatar.Avatar):
    healthColors = (Vec4(0, 1, 0, 1),
        Vec4(1, 1, 0, 1),
        Vec4(1, 0.5, 0, 1),
        Vec4(1, 0, 0, 1),
        Vec4(0.3, 0.3, 0.3, 1))
    healthGlowColors = (Vec4(0.25, 1, 0.25, 0.5),
        Vec4(1, 1, 0.25, 0.5),
        Vec4(1, 0.5, 0.25, 0.5),
        Vec4(1, 0.25, 0.25, 0.5),
        Vec4(0.3, 0.3, 0.3, 0))
    medallionColors = {'c': Vec4(0.863, 0.776, 0.769, 1.0),
        's': Vec4(0.843, 0.745, 0.745, 1.0),
        'l': Vec4(0.749, 0.776, 0.824, 1.0),
        'm': Vec4(0.749, 0.769, 0.749, 1.0)}
    health2DmgMultiplier = 2.5

    def __init__(self):
        try:
            self.Suit_initialized
            return
        except:
            self.Suit_initialized = 1

        Avatar.Avatar.__init__(self)
        self.avatarType = CIGlobals.Suit
        self.name = ''
        self.chat = ''
        self.suit = None
        self.suitHeads = None
        self.suitHead = None
        self.loserSuit = None
        self.healthBarGlow = None
        self.healthBar = None
        self.weapon = None
        self.weapon_sfx = None
        self.anim = None
        self.suit_dial = None
        self.shadow = None
        self.balloon_sfx = None
        self.add_sfx = None
        self.explosion = None
        self.largeExp = None
        self.smallExp = None
        self.death_sfx = None
        self.attack = None
        self.wtrajectory = None
        self.throwObjectId = None
        self.hasSpawned = False
        self.suitTrack = None
        self.headModel = None
        self.condition = 0
        self.type = ""
        self.head = ""
        self.team = ""
        self.isSkele = 0
        self.timestampAnimTrack = None
        self.animFSM = ClassicFSM('Suit', [State('off', self.enterOff, self.exitOff),
                                State('neutral', self.enterNeutral, self.exitNeutral),
                                State('walk', self.enterWalk, self.exitWalk),
                                State('die', self.enterDie, self.exitDie),
                                State('attack', self.enterAttack, self.exitAttack),
                                State('flydown', self.enterFlyDown, self.exitFlyDown),
                                State('pie', self.enterPie, self.exitPie),
                                State('win', self.enterWin, self.exitWin),
                                State('flyaway', self.enterFlyAway, self.exitFlyAway),
                                State('rollodex', self.enterRollodex, self.exitRollodex),
                                State('flyNeutral', self.enterFlyNeutral, self.exitFlyNeutral),
                                State('flail', self.enterFlail, self.exitFlail),
                                State('drop', self.enterDrop, self.exitDrop),
                                State('drop-react', self.enterDropReact, self.exitDropReact),
                                State('squirt-large', self.enterLSquirt, self.exitLSquirt),
                                State('squirt-small', self.enterSSquirt, self.exitSSquirt),
                                State('soak', self.enterSoak, self.exitSoak)], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()

        self.initializeBodyCollisions()

    def delete(self):
        try:
            self.Suit_deleted
        except:
            self.Suit_deleted = 1
            Avatar.Avatar.delete(self)
            self.weapon = None
            self.weapon_sfx = None
            self.suit_dial = None
            del self.shadowPlacer

    def disable(self):
        if self.suitTrack:
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        self.animFSM.requestFinalState()
        self.cleanupSuit()
        self.animFSM = None
        self.avatarType = None
        self.name = None
        self.chat = None
        self.suit = None
        self.state = None
        self.weapon_state = None
        self.suitHeads = None
        self.suitHead = None
        self.loserSuit = None
        self.healthMeterGlow = None
        self.healthMeter = None
        self.weapon = None
        self.weapon_sfx = None
        self.anim = None
        self.suit_dial = None
        self.shadow = None
        self.balloon_sfx = None
        self.add_sfx = None
        self.explosion = None
        self.largeExp = None
        self.smallExp = None
        self.death_sfx = None
        self.attack = None
        self.wtrajectory = None
        self.throwObjectId = None
        self.hasSpawned = None
        self.suitTrack = None
        self.headModel = None
        self.condition = None
        self.type = None
        self.head = None
        self.team = None
        self.isSkele = None
        self.timestampAnimTrack = None
        Avatar.Avatar.disable(self)

    def generateSuit(self, suitType, suitHead, suitTeam, suitHealth, skeleton, hideFirst = True):
        self.type = suitType
        self.head = suitHead
        self.isSkele = skeleton
        self.team = suitTeam
        self.health = suitHealth
        self.maxHealth = suitHealth
        self.cleanupSuit()
        self.generateBody(suitType, suitTeam, suitHead, skeleton)
        self.generateHealthMeter()
        self.generateHead(suitType, suitHead)
        #self.setupNameTag()
        self.parentSuitParts()
        self.rescaleSuit()
        Avatar.Avatar.initShadow(self)
        if hideFirst:
            self.hide()

    def rescaleSuit(self):
        self.setAvatarScale(CIGlobals.SuitScales[self.head] / CIGlobals.SuitScaleFactors[self.type])

    def parentSuitParts(self):
        if not self.isSkele:
            self.headModel.reparentTo(self.find('**/joint_head'))

    def unparentSuitParts(self):
        self.getPart('body').reparentTo(self.getGeomNode())
        if not self.isSkele:
            self.headModel.reparentTo(self.getGeomNode())

    def generateBody(self, suitType, suitTeam, suitHead, skeleton):

        self.team = suitTeam
        self.type = suitType
        self.head = suitHead
        self.isSkele = skeleton

        if suitType == "A":
            if skeleton:
                self.loadModel("phase_5/models/char/cogA_robot-zero.bam", "body")
            else:
                self.loadModel("phase_3.5/models/char/suitA-mod.bam", "body")
            self.loadAnims({"neutral": "phase_4/models/char/suitA-neutral.bam",
                            "walk": "phase_4/models/char/suitA-walk.bam",
                            "pie": "phase_4/models/char/suitA-pie-small.bam",
                            "land": "phase_5/models/char/suitA-landing.bam",
                            "throw-object": "phase_5/models/char/suitA-throw-object.bam",
                            "throw-paper": "phase_5/models/char/suitA-throw-paper.bam",
                            "glower": "phase_5/models/char/suitA-glower.bam",
                            "win": "phase_4/models/char/suitA-victory.bam",
                            "rollodex": "phase_5/models/char/suitA-roll-o-dex.bam",
                            "pickpocket": "phase_5/models/char/suitA-pickpocket.bam",
                            "fountainpen": "phase_7/models/char/suitA-fountain-pen.bam",
                            "phone": "phase_5/models/char/suitA-phone.bam",
                            "flail": "phase_4/models/char/suitA-flailing.bam",
                            "drop" : "phase_5/models/char/suitA-drop.bam",
                            "drop-react" : "phase_5/models/char/suitA-anvil-drop.bam",
                            "squirt-large" : "phase_5/models/char/suitA-squirt-large.bam",
                            "squirt-small" : "phase_4/models/char/suitA-squirt-small.bam",
                            "slip-forward" : "phase_4/models/char/suitA-slip-forward.bam",
                            "slip-backward" : "phase_4/models/char/suitA-slip-backward.bam",
                            "sit": "phase_12/models/char/suitA-sit.bam",
                            "speak": "phase_5/models/char/suitA-speak.bam",
                            "fingerwag": "phase_5/models/char/suitA-fingerwag.bam",
                            "soak" : "phase_5/models/char/suitA-soak.bam"}, "body")
        if suitType == "B":
            if skeleton:
                self.loadModel("phase_5/models/char/cogB_robot-zero.bam", "body")
            else:
                self.loadModel("phase_3.5/models/char/suitB-mod.bam", "body")
            self.loadAnims({"neutral": "phase_4/models/char/suitB-neutral.bam",
                            "walk": "phase_4/models/char/suitB-walk.bam",
                            "pie": "phase_4/models/char/suitB-pie-small.bam",
                            "land": "phase_5/models/char/suitB-landing.bam",
                            "throw-object": "phase_5/models/char/suitB-throw-object.bam",
                            "throw-paper": "phase_5/models/char/suitB-throw-paper.bam",
                            "glower": "phase_5/models/char/suitB-magic1.bam",
                            "win": "phase_4/models/char/suitB-victory.bam",
                            "rollodex": "phase_5/models/char/suitB-roll-o-dex.bam",
                            "pickpocket": "phase_5/models/char/suitB-pickpocket.bam",
                            "fountainpen": "phase_5/models/char/suitB-pen-squirt.bam",
                            "phone": "phase_5/models/char/suitB-phone.bam",
                            "flail": "phase_4/models/char/suitB-flailing.bam",
                            "drop" : "phase_5/models/char/suitB-drop.bam",
                            "drop-react" : "phase_5/models/char/suitB-anvil-drop.bam",
                            "squirt-large" : "phase_5/models/char/suitB-squirt-large.bam",
                            "squirt-small" : "phase_4/models/char/suitB-squirt-small.bam",
                            "slip-forward" : "phase_4/models/char/suitB-slip-forward.bam",
                            "slip-backward" : "phase_4/models/char/suitB-slip-backward.bam",
                            "speak": "phase_5/models/char/suitB-speak.bam",
                            "fingerwag": "phase_5/models/char/suitB-finger-wag.bam",
                            "soak" : "phase_5/models/char/suitB-soak.bam"}, "body")
        if suitType == "C":
            if skeleton:
                self.loadModel("phase_5/models/char/cogC_robot-zero.bam", "body")
            else:
                self.loadModel("phase_3.5/models/char/suitC-mod.bam", "body")
            self.loadAnims({"neutral": "phase_3.5/models/char/suitC-neutral.bam",
                        "walk": "phase_3.5/models/char/suitC-walk.bam",
                        "pie": "phase_3.5/models/char/suitC-pie-small.bam",
                        "land": "phase_5/models/char/suitC-landing.bam",
                        "throw-object": "phase_3.5/models/char/suitC-throw-paper.bam",
                        "throw-paper": "phase_3.5/models/char/suitC-throw-paper.bam",
                        "glower": "phase_5/models/char/suitC-glower.bam",
                        "win": "phase_4/models/char/suitC-victory.bam",
                        "pickpocket": "phase_5/models/char/suitC-pickpocket.bam",
                        "fountainpen": "phase_5/models/char/suitC-fountain-pen.bam",
                        "phone": "phase_3.5/models/char/suitC-phone.bam",
                        "flail": "phase_4/models/char/suitC-flailing.bam",
                        "drop" : "phase_5/models/char/suitC-drop.bam",
                        "drop-react" : "phase_5/models/char/suitC-anvil-drop.bam",
                        "squirt-large" : "phase_5/models/char/suitC-squirt-large.bam",
                        "squirt-small" : "phase_3.5/models/char/suitC-squirt-small.bam",
                        "slip-forward" : "phase_4/models/char/suitC-slip-forward.bam",
                        "slip-backward" : "phase_4/models/char/suitC-slip-backward.bam",
                        "sit": "phase_12/models/char/suitC-sit.bam",
                        "speak": "phase_5/models/char/suitC-speak.bam",
                        "fingerwag": "phase_5/models/char/suitC-finger-wag.bam",
                        "soak" : "phase_5/models/char/suitC-soak.bam"}, "body")
        if skeleton:
            self.setTwoSided(1)

        if skeleton:
            if suitTeam == "s":
                self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_sales.jpg")
            elif suitTeam == "m":
                self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_money.jpg")
            elif suitTeam == "l":
                self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_legal.jpg")
            elif suitTeam == "c":
                self.suit_tie = loader.loadTexture("phase_5/maps/cog_robot_tie_boss.jpg")
            self.find('**/tie').setTexture(self.suit_tie, 1)
        else:
            if hasattr(self, 'getBattle') and self.getBattle() != None:
                if self.getBattle().getHoodIndex() == CogBattleGlobals.WaiterHoodIndex:
                    self.suit_blazer = loader.loadTexture("phase_3.5/maps/waiter_m_blazer.jpg")
                    self.suit_leg = loader.loadTexture("phase_3.5/maps/waiter_m_leg.jpg")
                    self.suit_sleeve = loader.loadTexture("phase_3.5/maps/waiter_m_sleeve.jpg")
            if not hasattr(self, 'getBattle') or self.getBattle() == None or self.getBattle().getHoodIndex() != CogBattleGlobals.WaiterHoodIndex:
                self.suit_blazer = loader.loadTexture("phase_3.5/maps/" + suitTeam + "_blazer.jpg")
                self.suit_leg = loader.loadTexture("phase_3.5/maps/" + suitTeam + "_leg.jpg")
                self.suit_sleeve = loader.loadTexture("phase_3.5/maps/" + suitTeam + "_sleeve.jpg")

            self.find('**/legs').setTexture(self.suit_leg, 1)
            self.find('**/arms').setTexture(self.suit_sleeve, 1)
            self.find('**/torso').setTexture(self.suit_blazer, 1)

            if suitHead == "coldcaller":
                self.find('**/hands').setColor(0.55, 0.65, 1.0, 1.0)
            elif suitHead == "corporateraider":
                self.find('**/hands').setColor(0.85, 0.55, 0.55, 1.0)
            elif suitHead == "bigcheese":
                self.find('**/hands').setColor(0.75, 0.95, 0.75, 1.0)
            elif suitHead == "bloodsucker":
                self.find('**/hands').setColor(0.95, 0.95, 1.0, 1.0)
            elif suitHead == "spindoctor":
                self.find('**/hands').setColor(0.5, 0.8, 0.75, 1.0)
            elif suitHead == "legaleagle":
                self.find('**/hands').setColor(0.25, 0.25, 0.5, 1.0)
            elif suitHead == "pennypincher":
                self.find('**/hands').setColor(1.0, 0.5, 0.6, 1.0)
            elif suitHead == "loanshark":
                self.find('**/hands').setColor(0.5, 0.85, 0.75, 1.0)
            else:
                self.find('**/hands').setColor(CIGlobals.SuitHandColors[suitTeam])

    def generateHead(self, suitType, suitHead):

        self.type = suitType
        self.head = suitHead

        if suitHead == "vp":
            self.headModel = Actor("phase_9/models/char/sellbotBoss-head-zero.bam")
            self.headModel.loadAnims({"neutral": "phase_9/models/char/bossCog-head-Ff_neutral.bam"})
            self.headModel.setTwoSided(True)
            self.headModel.loop("neutral")
            self.headModel.setScale(0.35)
            self.headModel.setHpr(270, 0, 270)
            self.headModel.setZ(-0.10)
        else:
            if suitType == "A" or suitType == "B":
                heads = loader.loadModel("phase_4/models/char/suit" + suitType + "-heads.bam")
            else:
                heads = loader.loadModel("phase_3.5/models/char/suit" + suitType + "-heads.bam")
            self.headModel = heads.find('**/' + CIGlobals.SuitHeads[suitHead])
            if suitHead == "flunky":
                glasses = heads.find('**/glasses')
                glasses.reparentTo(self.headModel)
                glasses.setTwoSided(True)
            if suitHead in CIGlobals.SuitSharedHeads:
                if suitHead == "coldcaller":
                    self.headModel.setColor(0.25, 0.35, 1.0, 1.0)
                else:
                    headTexture = loader.loadTexture("phase_3.5/maps/" + suitHead + ".jpg")
                    self.headModel.setTexture(headTexture, 1)

    def cleanupSuit(self):
        self.removeHealthBar()
        if 'body' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['body']
        if self.shadow:
            self.deleteShadow()
        if self.headModel:
            self.headModel.removeNode()
            self.headModel = None
        if self.getPart('body'):
            self.removePart('body')

    def setName(self, nameString, charName):
        Avatar.Avatar.setName(self, nameString, avatarType=self.avatarType, charName=charName, createNow = 1)

    def setupNameTag(self):
        Avatar.Avatar.setupNameTag(self)
        self.nameTag.setText(self.nameTag.getText() + "\nLevel %d" % self.level)

    def setChat(self, chatString):
        self.chat = chatString
        if self.isSkele:
            if "?" in chatString:
                self.suit_dial = base.audio3d.loadSfx("phase_5/audio/sfx/Skel_COG_VO_question.ogg")
            elif "!" in chatString:
                self.suit_dial = base.audio3d.loadSfx("phase_5/audio/sfx/Skel_COG_VO_grunt.ogg")
            else:
                self.suit_dial = base.audio3d.loadSfx("phase_5/audio/sfx/Skel_COG_VO_statement.ogg")
        elif self.head in ["vp"]:
            if "?" in chatString:
                self.suit_dial = base.audio3d.loadSfx("phase_9/audio/sfx/Boss_COG_VO_question.ogg")
            elif "!" in chatString:
                self.suit_dial = base.audio3d.loadSfx("phase_9/audio/sfx/Boss_COG_VO_grunt.ogg")
            else:
                self.suit_dial = base.audio3d.loadSfx("phase_9/audio/sfx/Boss_COG_VO_statement.ogg")
        else:
            if "?" in chatString:
                self.suit_dial = base.audio3d.loadSfx(
                    random.choice(
                        [
                            "phase_3.5/audio/dial/COG_VO_question.ogg",
                            "phase_3.5/audio/dial/COG_VO_question_2.ogg"
                        ]
                    )
                )
            elif "!" in chatString:
                self.suit_dial = base.audio3d.loadSfx("phase_3.5/audio/dial/COG_VO_grunt.ogg")
            else:
                self.suit_dial = base.audio3d.loadSfx("phase_3.5/audio/dial/COG_VO_statement.ogg")
        if self.isSkele:
            base.audio3d.attachSoundToObject(self.suit_dial, self)
        else:
            base.audio3d.attachSoundToObject(self.suit_dial, self.headModel)
        self.suit_dial.play()
        Avatar.Avatar.setChat(self, chatString)

    def generateHealthMeter(self):
        self.removeHealthBar()
        model = loader.loadModel("phase_3.5/models/gui/matching_game_gui.bam")
        button = model.find('**/minnieCircle')
        button.setScale(3.0)
        button.setH(180)
        button.setColor(self.healthColors[0])
        chestNull = self.find('**/def_joint_attachMeter')
        if chestNull.isEmpty():
            chestNull = self.find('**/joint_attachMeter')
        button.reparentTo(chestNull)
        self.healthBar = button
        glow = loader.loadModel("phase_3.5/models/props/glow.bam")
        glow.reparentTo(self.healthBar)
        glow.setScale(0.28)
        glow.setPos(-0.005, 0.01, 0.015)
        glow.setColor(self.healthGlowColors[0])
        button.flattenLight()
        self.healthBarGlow = glow
        self.condition = 0
        if hasattr(self, 'getHealth'):
            self.updateHealthBar(self.getHealth())

    def updateHealthBar(self, hp):
        if not self.healthBar:
            return
        if hp > self.health:
            self.health = hp
        #self.health -= hp
        health = 0.0
        try:
            health = float(hp) / float(self.maxHealth)
        except:
            pass
        if health > 0.95:
            condition = 0
        elif health > 0.7:
            condition = 1
        elif health > 0.3:
            condition = 2
        elif health > 0.05:
            condition = 3
        elif health > 0.0:
            condition = 4
        else:
            condition = 5

        if self.condition != condition:
            if condition == 4:
                blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.75), Task(self.__blinkGray), Task.pause(0.1))
                taskMgr.add(blinkTask, self.taskName('blink-task'))
            elif condition == 5:
                if self.condition == 4:
                    taskMgr.remove(self.taskName('blink-task'))
                blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.25), Task(self.__blinkGray), Task.pause(0.1))
                taskMgr.add(blinkTask, self.taskName('blink-task'))
            else:
                self.healthBar.setColor(self.healthColors[condition], 1)
                self.healthBarGlow.setColor(self.healthGlowColors[condition], 1)
            self.condition = condition

    def __blinkRed(self, task):
        self.healthBar.setColor(self.healthColors[3], 1)
        self.healthBarGlow.setColor(self.healthGlowColors[3], 1)
        if self.condition == 5:
            self.healthBar.setScale(1.17)
        return Task.done

    def __blinkGray(self, task):
        if not self.healthBar:
            return
        self.healthBar.setColor(self.healthColors[4], 1)
        self.healthBarGlow.setColor(self.healthGlowColors[4], 1)
        if self.condition == 5:
            self.healthBar.setScale(1.0)
        return Task.done

    def removeHealthBar(self):
        if self.healthBar:
            self.healthBar.removeNode()
            self.healthBar = None
        if self.condition == 4 or self.condition == 5:
            taskMgr.remove(self.taskName('blink-task'))
        self.healthCondition = 0
        return

    def enterOff(self, ts = 0):
        self.anim = None
        return

    def exitOff(self):
        pass

    def exitGeneral(self):
        self.stop()

    def enterFlail(self, ts = 0):
        self.pingpong("flail", fromFrame = 30, toFrame = 35)

    def exitFlail(self):
        self.stop()

    def enterNeutral(self, ts = 0):
        self.show()
        self.timestampAnimTrack = Sequence(Wait(ts), Func(self.loop, "neutral"))
        self.timestampAnimTrack.start()

    def exitNeutral(self):
        self.timestampAnimTrack.pause()
        self.timestampAnimTrack = None
        self.exitGeneral()

    def enterRollodex(self, ts = 0):
        self.play("rollodex")

    def exitRollodex(self):
        self.exitGeneral()

    def enterWalk(self, ts = 0):
        self.show()
        self.timestampAnimTrack = Sequence(Wait(ts), Func(self.loop, "walk"))
        self.timestampAnimTrack.start()
        self.disableShadowRay()

    def exitWalk(self):
        self.timestampAnimTrack.pause()
        self.timestampAnimTrack = None
        self.exitGeneral()
        self.enableShadowRay()

    def __moveSuitUpForDropAnim(self):
        self.getGeomNode().setZ(0.5)

    def __moveSuitDownToNormal(self):
        self.getGeomNode().setZ(0)

    def enterDrop(self, ts = 0):
        self.suitTrack = Parallel(
            ActorInterval(self, 'drop')#,
            #Sequence(Wait(0.1), Func(self.__moveSuitUpForDropAnim), Wait(2.7), Func(self.__moveSuitDownToNormal))
        )
        self.suitTrack.start()

    def exitDrop(self):
        if self.suitTrack:
            self.suitTrack.finish()
            self.suitTrack = None

    def enterDropReact(self, ts = 0):
        self.play('drop-react')

    def exitDropReact(self):
        self.stop()

    def enterSSquirt(self, ts = 0):
        self.play('squirt-small')

    def exitSSquirt(self):
        self.stop()

    def enterLSquirt(self, ts = 0):
        self.play('squirt-large')

    def exitLSquirt(self):
        self.stop()

    def enterSoak(self, ts = 0):
        self.play('soak')

    def exitSoak(self):
        self.stop()

    def generateLoserSuit(self):
        if not self.isSkele:
            handColor = self.find('**/hands').getColor()
        self.cleanupSuit()
        if self.type == "A":
            if self.isSkele:
                self.loadModel("phase_5/models/char/cogA_robot-lose-mod.bam", 'body')
            else:
                self.loadModel("phase_4/models/char/suitA-lose-mod.bam", 'body')
            self.loadAnims({"lose": "phase_4/models/char/suitA-lose.bam"}, 'body')
        if self.type == "B":
            if self.isSkele:
                self.loadModel("phase_5/models/char/cogB_robot-lose-mod.bam", 'body')
            else:
                self.loadModel("phase_4/models/char/suitB-lose-mod.bam", 'body')
            self.loadAnims({"lose": "phase_4/models/char/suitB-lose.bam"}, 'body')
        if self.type == "C":
            if self.isSkele:
                self.loadModel("phase_5/models/char/cogC_robot-lose-mod.bam", 'body')
            else:
                self.loadModel("phase_3.5/models/char/suitC-lose-mod.bam", 'body')
            self.loadAnims({"lose": "phase_3.5/models/char/suitC-lose.bam"}, 'body')
        if self.isSkele:
            self.find('**/tie').setTexture(self.suit_tie, 1)
            self.setTwoSided(1)
        else:
            self.find('**/hands').setColor(handColor)
            self.find('**/legs').setTexture(self.suit_leg, 1)
            self.find('**/arms').setTexture(self.suit_sleeve, 1)
            self.find('**/torso').setTexture(self.suit_blazer, 1)
        self.generateHead(self.type, self.head)
        self.rescaleSuit()
        self.parentSuitParts()
        self.deleteNameTag()
        Avatar.Avatar.initShadow(self)

    def enterDie(self, ts = 0):
        self.show()
        self.generateLoserSuit()
        self.clearChat()
        self.state = "dead"
        self.play("lose")
        deathSound = base.audio3d.loadSfx("phase_3.5/audio/sfx/Cog_Death_Full.ogg")
        base.audio3d.attachSoundToObject(deathSound, self)
        trackName = self.uniqueName('enterDie')

        smallGears = ParticleLoader.loadParticleEffect('phase_3.5/etc/gearExplosionSmall.ptf')
        smallGears.getParticlesNamed('particles-1').setPoolSize(30)

        singleGear = ParticleLoader.loadParticleEffect('phase_3.5/etc/gearExplosion.ptf')
        singleGear.getParticlesNamed('particles-1').setPoolSize(1)

        smallGearExplosion = ParticleLoader.loadParticleEffect('phase_3.5/etc/gearExplosion.ptf')
        smallGearExplosion.getParticlesNamed('particles-1').setPoolSize(10)

        bigGearExplosion = ParticleLoader.loadParticleEffect('phase_3.5/etc/gearExplosionBig.ptf')
        bigGearExplosion.getParticlesNamed('particles-1').setPoolSize(30)

        smallGears.setDepthWrite(False)
        singleGear.setDepthWrite(False)
        smallGearExplosion.setDepthWrite(False)
        bigGearExplosion.setDepthWrite(False)

        self.smallGears = smallGears
        self.smallGears.setPos(self.find('**/joint_head').getPos() + (0,0, 2))
        self.singleGear = singleGear
        self.smallGearExp = smallGearExplosion
        self.bigGearExp = bigGearExplosion

        gearTrack = Sequence(Wait(0.7), Func(self.doSingleGear), Wait(1.5), Func(self.doSmallGears), Wait(3.0), Func(self.doBigExp))
        self.suitTrack = Parallel(Sequence(Wait(0.7), Func(self.doSingleGear), Wait(4.3),
                Func(self.suitExplode), Wait(1.0), Func(self.delSuit)), gearTrack, name = trackName)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        Sequence(Wait(0.8), SoundInterval(deathSound)).start()
        self.acceptOnce(self.suitTrack.getName(), self.exitDie)
        if "Distributed" in self.__class__.__name__:
            self.suitTrack.delayDelete = DelayDelete.DelayDelete(self, trackName)
        self.suitTrack.start(ts)
        del deathSound

    def doSingleGear(self):
        self.singleGear.start(self.getGeomNode())

    def doSmallGears(self):
        self.smallGears.start(self.getGeomNode())

    def doSmallExp(self):
        self.smallGearExp.start(self.getGeomNode())

    def doBigExp(self):
        self.bigGearExp.start(self.getGeomNode())

    def suitExplode(self):
        self.explosion = loader.loadModel("phase_3.5/models/props/explosion.bam")
        self.explosion.setScale(0.5)
        self.explosion.reparentTo(render)
        self.explosion.setBillboardPointEye()
        self.explosion.setDepthWrite(False)
        if self.isSkele:
            self.explosion.setPos(self.getPart("body").find('**/joint_head').getPos(render) + (0, 0, 2))
        else:
            self.explosion.setPos(self.headModel.getPos(render) + (0,0,2))

    def delSuit(self):
        self.disableBodyCollisions()

    def exitDie(self):
        if self.suitTrack != None:
            self.ignore(self.suitTrack.getName())
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        if hasattr(self, 'singleGear'):
            self.singleGear.cleanup()
            del self.singleGear
        if hasattr(self, 'smallGears'):
            self.smallGears.cleanup()
            del self.smallGears
        if hasattr(self, 'smallGearExp'):
            self.smallGearExp.cleanup()
            del self.smallGearExp
        if hasattr(self, 'bigGearExp'):
            self.bigGearExp.cleanup()
            del self.bigGearExp
        if self.explosion:
            self.explosion.removeNode()
            self.explosion = None

    def enterFlyNeutral(self, ts = 0):
        self.disableRay()
        self.sfx = base.audio3d.loadSfx("phase_4/audio/sfx/TB_propeller.ogg")
        self.prop = Actor("phase_4/models/props/propeller-mod.bam",
                        {"chan": "phase_4/models/props/propeller-chan.bam"})
        base.audio3d.attachSoundToObject(self.sfx, self.prop)
        self.prop.reparentTo(self.find('**/joint_head'))
        self.sfx.setLoop(True)
        self.sfx.play()
        self.prop.loop('chan', fromFrame = 0, toFrame = 3)
        self.setPlayRate(0.8, 'land')
        self.pingpong('land', fromFrame = 0, toFrame = 10)

    def exitFlyNeutral(self):
        self.prop.cleanup()
        del self.prop
        base.audio3d.detachSound(self.sfx)
        del self.sfx
        self.stop()

    def enterFlyDown(self, ts = 0):
        self.disableRay()
        self.fd_sfx = base.audio3d.loadSfx("phase_5/audio/sfx/ENC_propeller_in.ogg")
        self.prop = Actor("phase_4/models/props/propeller-mod.bam",
                        {"chan": "phase_4/models/props/propeller-chan.bam"})
        base.audio3d.attachSoundToObject(self.fd_sfx, self.prop)
        self.prop.reparentTo(self.find('**/joint_head'))
        self.fd_sfx.play()
        dur = self.getDuration('land')
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterFlyDown')
        else:
            name = 'enterFlyDown'
        self.suitTrack = Parallel(Sequence(Func(self.pose, 'land', 0), Func(self.prop.loop, 'chan', fromFrame=0, toFrame=3),
                            Wait(1.75),
                            Func(self.prop.play, 'chan', fromFrame=3),
                            Wait(0.15),
                            ActorInterval(self, 'land', duration=dur)), name = name)
        if not self.hasSpawned:
            showSuit = Sequence(Func(self.hideSuit), Wait(0.3), Func(self.showSuit))
            self.hasSpawned = True
            self.suitTrack.append(showSuit)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.exitFlyAway)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self, name)
        self.suitTrack.start(ts)

    def hideSuit(self):
        self.hide()

    def showSuit(self):
        self.show()
        fadeIn = Sequence(Func(self.setTransparency, 1), self.colorScaleInterval(0.6, colorScale=Vec4(1,1,1,1), startColorScale=Vec4(1,1,1,0)), Func(self.clearColorScale), Func(self.clearTransparency), Func(self.reparentTo, render))
        fadeIn.start()

    def initializeLocalCollisions(self, name):
        Avatar.Avatar.initializeLocalCollisions(self, 1, 3, name)

    def initializeBodyCollisions(self):
        Avatar.Avatar.initializeBodyCollisions(self, self.avatarType, 6, 2)
        self.initializeRay(self.avatarType, 2)

    def exitFlyDown(self):
        self.initializeRay(self.avatarType, 2)
        if self.suitTrack != None:
            self.ignore(self.suitTrack.getDoneEvent())
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        if hasattr(self, 'fd_sfx'):
            base.audio3d.detachSound(self.fd_sfx)
        self.exitGeneral()
        if self.prop:
            self.prop.cleanup()
            self.prop = None

    def enterFlyAway(self, ts = 0):
        self.show()
        self.fa_sfx = base.audio3d.loadSfx("phase_5/audio/sfx/ENC_propeller_out.ogg")
        self.prop = Actor("phase_4/models/props/propeller-mod.bam",
                        {"chan": "phase_4/models/props/propeller-chan.bam"})
        base.audio3d.attachSoundToObject(self.fa_sfx, self.prop)
        self.fa_sfx.play()
        self.prop.reparentTo(self.find('**/joint_head'))
        self.prop.setPlayRate(-1.0, "chan")
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterFlyAway')
        else:
            name = 'enterFlyAway'
        self.suitTrack = Sequence(Func(self.prop.play, 'chan', fromFrame=3),
                            Wait(1.75),
                            Func(self.prop.play, 'chan', fromFrame=0, toFrame=3), name = name)
        self.suitTrack.setDoneEvent(self.suitTrack.getName())
        self.acceptOnce(self.suitTrack.getDoneEvent(), self.exitFlyAway)
        self.suitTrack.delayDelete = DelayDelete.DelayDelete(self, name)
        self.suitTrack.start(ts)
        self.setPlayRate(-1.0, 'land')
        self.play('land')

    def exitFlyAway(self):
        if self.suitTrack:
            self.ignore(self.suitTrack.getDoneEvent())
            self.suitTrack.finish()
            DelayDelete.cleanupDelayDeletes(self.suitTrack)
            self.suitTrack = None
        if hasattr(self, 'fa_sfx'):
            base.audio3d.detachSound(self.fa_sfx)
        self.exitGeneral()
        if self.prop:
            self.prop.cleanup()
            self.prop = None

    def enterAttack(self, attack, target, ts = 0):
        self.show()

        if hasattr(self, 'uniqueName'):
            doneEvent = self.uniqueName('suitAttackDone')
        else:
            doneEvent = 'suitAttackDone'
        self.suitAttackState = SuitAttacks(doneEvent, self, target)
        self.suitAttackState.load(attack)
        self.suitAttackState.enter(ts)
        self.acceptOnce(doneEvent, self.handleSuitAttackDone)

    def handleSuitAttackDone(self):
        self.exitAttack()

    def exitAttack(self):
        if hasattr(self, 'uniqueName'):
            self.ignore(self.uniqueName('suitAttackDone'))
        else:
            self.ignore('suitAttackDone')
        if hasattr(self, 'suitAttackState'):
            self.suitAttackState.exit()
        if hasattr(self, 'suitAttackState'):
            self.suitAttackState.unload()
        if hasattr(self, 'suitAttackState'):
            del self.suitAttackState

    def interruptAttack(self):
        if hasattr(self, 'suitAttackState'):
            self.suitAttackState.currentAttack.interruptAttack()

    def handleWeaponTouch(self):
        if hasattr(self, 'suitAttackState'):
            self.suitAttackState.currentAttack.handleWeaponTouch()

    def enterPie(self, ts = 0):
        self.show()
        self.play("pie")

    def exitPie(self):
        self.exitGeneral()

    def enterWin(self, ts = 0):
        self.play("win")

    def exitWin(self):
        self.exitGeneral()
class Toon(Avatar.Avatar, ToonHead, ToonDNA.ToonDNA):
    def __init__(self, cr, mat=0):
        self.cr = cr
        try:
            self.Toon_initialized
            return
        except:
            self.Toon_initialized = 1
        Avatar.Avatar.__init__(self, mat)
        ToonDNA.ToonDNA.__init__(self)
        ToonHead.__init__(self, cr)
        self.forwardSpeed = 0.0
        self.rotateSpeed = 0.0
        self.strafeSpeed = 0.0
        self.avatarType = CIGlobals.Toon
        self.track = None
        self.standWalkRunReverse = None
        self.playingAnim = None
        self.playingRate = None
        self.tag = None
        self.money = 0
        self.lookAtTrack = None
        self.portal1 = None
        self.portal2 = None
        self.gunAttached = False
        self.gun = None
        self.tokenIcon = None
        self.tokenIconIval = None
        self.forcedTorsoAnim = None
        self.fallSfx = base.audio3d.loadSfx(
            "phase_4/audio/sfx/MG_cannon_hit_dirt.ogg")
        base.audio3d.attachSoundToObject(self.fallSfx, self)
        self.eyes = loader.loadTexture("phase_3/maps/eyes.jpg",
                                       "phase_3/maps/eyes_a.rgb")
        self.myTaskId = random.uniform(0, 1231231232132131231232)
        self.closedEyes = loader.loadTexture("phase_3/maps/eyesClosed.jpg",
                                             "phase_3/maps/eyesClosed_a.rgb")
        self.soundChatBubble = loader.loadSfx(
            "phase_3/audio/sfx/GUI_balloon_popup.ogg")
        self.shadowCaster = None
        self.chatSoundDict = {}
        self.backpack = None
        self.animFSM = ClassicFSM('Toon', [
            State('off', self.enterOff, self.exitOff),
            State('neutral', self.enterNeutral, self.exitNeutral),
            State('swim', self.enterSwim, self.exitSwim),
            State('walk', self.enterWalk, self.exitWalk),
            State('run', self.enterRun, self.exitRun),
            State('openBook', self.enterOpenBook, self.exitOpenBook),
            State('readBook', self.enterReadBook, self.exitReadBook),
            State('closeBook', self.enterCloseBook, self.exitCloseBook),
            State('teleportOut', self.enterTeleportOut, self.exitTeleportOut),
            State('teleportIn', self.enterTeleportIn, self.exitTeleportIn),
            State('died', self.enterDied, self.exitDied),
            State('fallFWD', self.enterFallFWD, self.exitFallFWD),
            State('fallBCK', self.enterFallBCK, self.exitFallBCK),
            State('jump', self.enterJump, self.exitJump),
            State('leap', self.enterLeap, self.exitLeap),
            State('laugh', self.enterLaugh, self.exitLaugh),
            State('happy', self.enterHappyJump, self.exitHappyJump),
            State('shrug', self.enterShrug, self.exitShrug),
            State('hdance', self.enterHDance, self.exitHDance),
            State('wave', self.enterWave, self.exitWave),
            State('scientistEmcee', self.enterScientistEmcee,
                  self.exitScientistEmcee),
            State('scientistWork', self.enterScientistWork,
                  self.exitScientistWork),
            State('scientistGame', self.enterScientistGame,
                  self.exitScientistGame),
            State('scientistJealous', self.enterScientistJealous,
                  self.exitScientistJealous),
            State('cringe', self.enterCringe, self.exitCringe),
            State('conked', self.enterConked, self.exitConked),
            State('win', self.enterWin, self.exitWin),
            State('walkBack', self.enterWalkBack, self.exitWalkBack),
            State('deadNeutral', self.enterDeadNeutral, self.exitDeadNeutral),
            State('deadWalk', self.enterDeadWalk, self.exitDeadWalk),
            State('squish', self.enterSquish, self.exitSquish),
            State('Happy', self.enterHappy, self.exitHappy),
            State('Sad', self.enterSad, self.exitSad)
        ], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()

        if not hasattr(base, 'localAvatar') or not base.localAvatar == self:
            Avatar.Avatar.initializeBodyCollisions(self, self.avatarType, 3, 1)

    def showAvId(self):
        pass

    def showName(self):
        pass

    def getNametagJoints(self):
        joints = []
        for lodName in self.getLODNames():
            bundle = self.getPartBundle('legs', lodName)
            joint = bundle.findChild('joint_nameTag')
            if joint:
                joints.append(joint)

        return joints

    def enterHappy(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = None
        self.standWalkRunReverse = (('neutral', 1.0), ('walk', 1.0),
                                    ('run', 1.0), ('walk', -1.0),
                                    ('strafe', 1.0), ('strafe', -1.0))
        self.setSpeed(self.forwardSpeed, self.rotateSpeed)

    def exitHappy(self):
        self.standWalkRunReverse = None
        self.stop()

    def enterSad(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'sad'
        self.standWalkRunReverse = (('dneutral', 1.0), ('dwalk', 1.2),
                                    ('dwalk', 1.2), ('dwalk', -1.0))
        self.setSpeed(0, 0)

    def exitSad(self):
        self.standWalkRunReverse = None
        self.stop()
        #if hasattr(self, 'doId'):
        #    if hasattr(base, 'localAvatar'):
        #        if base.localAvatar.doId == self.doId:
        #            self.controlManager.enableAvatarJump()

    def setSpeed(self, forwardSpeed, rotateSpeed, strafeSpeed=0.0):
        self.forwardSpeed = forwardSpeed
        self.rotateSpeed = rotateSpeed
        self.strafeSpeed = strafeSpeed
        action = None
        if self.standWalkRunReverse != None:
            if (forwardSpeed >= CIGlobals.RunCutOff
                    and strafeSpeed < CIGlobals.RunCutOff
                    and strafeSpeed > -CIGlobals.RunCutOff):
                action = CIGlobals.RUN_INDEX
            elif strafeSpeed >= CIGlobals.RunCutOff or strafeSpeed <= -CIGlobals.RunCutOff:
                if strafeSpeed > 0:
                    action = CIGlobals.STRAFE_RIGHT_INDEX
                elif strafeSpeed < 0:
                    action = CIGlobals.STRAFE_LEFT_INDEX
            elif forwardSpeed > CIGlobals.WalkCutOff:
                action = CIGlobals.WALK_INDEX
            elif forwardSpeed < -CIGlobals.WalkCutOff:
                action = CIGlobals.REVERSE_INDEX
            elif rotateSpeed != 0.0:
                action = CIGlobals.WALK_INDEX
            else:
                action = CIGlobals.STAND_INDEX
            anim, rate = self.standWalkRunReverse[action]
            if anim != self.playingAnim or rate != self.playingRate:
                self.playingAnim = anim
                self.playingRate = rate
                doingGagAnim = False
                if self.backpack:
                    if self.backpack.getCurrentGag():
                        if self.backpack.getCurrentGag().getState() in [
                                GagState.START, GagState.RELEASED
                        ]:
                            doingGagAnim = True
                            self.loop(anim, partName="legs")
                            if self.animal == "dog":
                                self.loop(anim, partName="head")
                if not doingGagAnim:
                    if self.forcedTorsoAnim == None:
                        self.loop(anim)
                    else:
                        self.loop(self.forcedTorsoAnim, partName='head')
                        self.loop(self.forcedTorsoAnim, partName='torso')
                        self.loop(anim, partName='legs')
                self.setPlayRate(rate, anim)
        return action

    def enterSquish(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'squish'
        sound = loader.loadSfx('phase_9/audio/sfx/toon_decompress.ogg')
        lerpTime = 0.1
        node = self.getGeomNode().getChild(0)
        origScale = node.getScale()
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('getSquished')
        else:
            name = 'getSquished'
        self.track = Sequence(LerpScaleInterval(node,
                                                lerpTime,
                                                VBase3(2, 2, 0.025),
                                                blendType='easeInOut'),
                              Wait(1.0),
                              Parallel(
                                  Sequence(
                                      Wait(0.4),
                                      LerpScaleInterval(node,
                                                        lerpTime,
                                                        VBase3(1.4, 1.4, 1.4),
                                                        blendType='easeInOut'),
                                      LerpScaleInterval(node,
                                                        lerpTime / 2.0,
                                                        VBase3(0.8, 0.8, 0.8),
                                                        blendType='easeInOut'),
                                      LerpScaleInterval(
                                          node,
                                          lerpTime / 3.0,
                                          origScale,
                                          blendType='easeInOut')),
                                  ActorInterval(self, 'happy', startTime=0.2),
                                  SoundInterval(sound)),
                              name=name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.squishDone,
                        [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.start(ts)

    def squishDone(self, callback=None, extraArgs=[]):
        self.__doCallback(callback, extraArgs)

    def exitSquish(self):
        if self.track:
            self.ignore(self.track.getName())
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track.finish()
            self.track = None
        self.playingAnim = 'neutral'

    def enterDeadNeutral(self, ts=0, callback=None, extraArgs=[]):
        self.loop('dneutral')

    def exitDeadNeutral(self):
        self.stop()

    def enterDeadWalk(self, ts=0, callback=None, extraArgs=[]):
        self.loop('dwalk')

    def exitDeadWalk(self):
        self.stop()

    def setBackpack(self, pack):
        self.backpack = pack

    def getGhost(self):
        return 0

    def updateChatSoundDict(self):
        self.chatSoundDict['exclaim'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('exclaim'))
        self.chatSoundDict['question'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('question'))
        self.chatSoundDict['short'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('short'))
        self.chatSoundDict['medium'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('med'))
        self.chatSoundDict['long'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('long'))
        self.chatSoundDict['howl'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('howl'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['exclaim'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['question'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['short'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['medium'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['long'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['howl'],
                                         self.getPart('head'))

    def ghostOn(self):
        self.getGeomNode().hide()
        self.nametag3d.hide()
        self.getShadow().hide()
        if self.tokenIcon:
            self.tokenIcon.hide()
        self.stashBodyCollisions()

    def ghostOff(self):
        self.unstashBodyCollisions()
        if self.tokenIcon:
            self.tokenIcon.show()
        self.getShadow().show()
        self.nametag3d.show()
        self.getGeomNode().show()

    def attachGun(self, gunName):
        self.detachGun()
        if gunName == "pistol":
            self.gun = loader.loadModel("phase_4/models/props/water-gun.bam")
            self.gun.reparentTo(self.find('**/def_joint_right_hold'))
            self.gun.setPos(Point3(0.28, 0.1, 0.08))
            self.gun.setHpr(VBase3(85.6, -4.44, 94.43))
            self.gunAttached = True
        elif gunName == "shotgun":
            self.gun = loader.loadModel("phase_4/models/props/shotgun.egg")
            self.gun.setScale(0.75)
            self.gun.reparentTo(self.find('**/def_joint_right_hold'))
            self.gun.setPos(Point3(-0.5, -0.2, 0.19))
            self.gun.setHpr(Vec3(350, 272.05, 0))
            color = random.choice([
                VBase4(1, 0.25, 0.25, 1),
                VBase4(0.25, 1, 0.25, 1),
                VBase4(0.25, 0.25, 1, 1)
            ])
            self.gun.setColorScale(color)
            self.gunAttached = True
        elif gunName == "sniper":
            self.gun = loader.loadModel("phase_4/models/props/sniper.egg")
            self.gun.setScale(0.75)
            self.gun.reparentTo(self.find('**/def_joint_right_hold'))
            self.gun.setPos(Point3(-0.5, -0.2, 0.19))
            self.gun.setHpr(Vec3(350, 272.05, 0))
            color = random.choice([
                VBase4(1, 0.25, 0.25, 1),
                VBase4(0.25, 1, 0.25, 1),
                VBase4(0.25, 0.25, 1, 1)
            ])
            self.gun.setColorScale(color)
            self.gunAttached = True

    def detachGun(self):
        if self.gun and self.gunAttached:
            self.gun.removeNode()
            self.gun = None
            self.gunAttached = False

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                notify.warning(
                    "animFSM in flux, state=%s, not requesting off" %
                    self.animFSM.getCurrentState().getName())
        else:
            notify.warning("animFSM has been deleted")
        if self.track != None:
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        return

    def disable(self):
        try:
            self.Toon_disabled
        except:
            self.Toon_disabled = 1
            self.backpack = None
            self.stopAnimations()
            self.removeAdminToken()
            ToonHead.delete(self)
            self.deleteCurrentToon()
            self.chatSoundDict = {}
            Avatar.Avatar.disable(self)

    def delete(self):
        try:
            self.Toon_deleted
        except:
            self.Toon_deleted = 1
            del self.animFSM
            self.forwardSpeed = None
            self.chatSoundDict = None
            self.rotateSpeed = None
            self.avatarType = None
            self.track = None
            self.standWalkRunReverse = None
            self.currentAnim = None
            self.toon_head = None
            self.forcedTorsoAnim = None
            self.toon_torso = None
            self.toon_legs = None
            self.gender = None
            self.headtype = None
            self.head = None
            self.legtype = None
            self.torsotype = None
            self.hr = None
            self.hg = None
            self.hb = None
            self.tr = None
            self.tg = None
            self.tb = None
            self.lr = None
            self.lg = None
            self.lb = None
            self.shir = None
            self.shig = None
            self.shib = None
            self.shor = None
            self.shog = None
            self.shob = None
            self.shirt = None
            self.sleeve = None
            self.short = None
            self.tag = None
            self.money = None
            self.lookAtTrack = None
            self.portal1 = None
            self.portal2 = None
            self.backpack = None
            self.fallSfx = None
            self.eyes = None
            self.myTaskId = None
            self.closedEyes = None
            self.soundChatBubble = None
            self.lastAction = None
            self.lastState = None
            self.playingAnim = None
            self.playingRate = None
            Avatar.Avatar.delete(self)
        return

    def initCollisions(self):
        self.collNodePath.setCollideMask(BitMask32(0))
        self.collNodePath.node().setFromCollideMask(CIGlobals.WallBitmask)

        pusher = CollisionHandlerPusher()
        pusher.setInPattern("%in")
        pusher.addCollider(self.collNodePath, self)
        base.cTrav.addCollider(self.collNodePath, pusher)

    def deleteCurrentToon(self):
        if self.shadowCaster:
            self.shadowCaster.clear()
            self.shadowCaster = None
        try:
            self.stopLookAround()
            self.stopBlink()
        except:
            pass
        self.pupils = []
        if 'head' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['head']
        if 'torso' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['torso']
        if 'legs' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['legs']
        self.deleteShadow()
        self.removePart('head')
        self.removePart('torso')
        self.removePart('legs')
        self.detachGun()

    def setAdminToken(self, tokenId):
        if tokenId in ToonGlobals.STAFF_TOKENS.keys():
            icons = loader.loadModel("phase_3/models/props/gm_icons.bam")
            self.tokenIcon = icons.find('**/access_level_%s' %
                                        (ToonGlobals.STAFF_TOKENS[tokenId]))
            self.tokenIcon.reparentTo(self)
            x = self.nametag3d.getX()
            y = self.nametag3d.getY()
            z = self.nametag3d.getZ()
            self.tokenIcon.setPos(Vec3(x, y, z) + (0, 0, 0.5))
            self.tokenIcon.setScale(0.4)
            self.tokenIconIval = Sequence(
                LerpHprInterval(self.tokenIcon,
                                duration=3.0,
                                hpr=Vec3(360, 0, 0),
                                startHpr=Vec3(0, 0, 0)))
            self.tokenIconIval.loop()
            icons.removeNode()

    def removeAdminToken(self):
        if self.tokenIcon != None and self.tokenIconIval != None:
            self.tokenIconIval.finish()
            self.tokenIcon.removeNode()
            self.tokenIconIval = None
            self.tokenIcon = None

    def setChat(self, chatString):
        if not self.isThought(chatString):
            if not self.getGhost() or self.doId == base.localAvatar.doId:
                if "ooo" in chatString.lower():
                    sfx = self.chatSoundDict['howl']
                elif "!" in chatString.lower():
                    sfx = self.chatSoundDict['exclaim']
                elif "?" in chatString.lower():
                    sfx = self.chatSoundDict['question']
                elif len(chatString) <= 9:
                    sfx = self.chatSoundDict['short']
                elif 10 <= len(chatString) <= 19:
                    sfx = self.chatSoundDict['medium']
                elif len(chatString) >= 20:
                    sfx = self.chatSoundDict['long']
                base.playSfx(sfx, node=self)

        Avatar.Avatar.setChat(self, chatString)

    def setName(self, nameString):
        Avatar.Avatar.setName(self, nameString, avatarType=self.avatarType)

    def setDNAStrand(self, dnaStrand, makeTag=1):
        ToonDNA.ToonDNA.setDNAStrand(self, dnaStrand)
        self.deleteCurrentToon()
        self.generateToon(makeTag)

    def generateToon(self, makeTag=1):
        self.generateLegs()
        self.generateTorso()
        self.generateHead()
        self.setToonColor()
        self.setClothes()
        self.setGloves()
        self.parentToonParts()
        self.rescaleToon()
        if makeTag:
            self.setupNameTag()
        Avatar.Avatar.initShadow(self)
        if self.cr.isShowingPlayerIds:
            self.showAvId()
        self.updateChatSoundDict()

    def attachTNT(self):
        self.pies.attachTNT()
        self.holdTNTAnim()

    def detachTNT(self):
        self.pies.detachTNT()
        self.animFSM.request(self.animFSM.getCurrentState().getName())

    def holdTNTAnim(self):
        self.pose("toss", 22, partName="torso")

    def parentToonParts(self):
        self.attach('head', 'torso', 'def_head')
        self.attach('torso', 'legs', 'joint_hips')

    def unparentToonParts(self):
        self.getPart('head').reparentTo(self.getGeomNode())
        self.getPart('torso').reparentTo(self.getGeomNode())
        self.getPart('legs').reparentTo(self.getGeomNode())

    def rescaleToon(self):
        animal = self.getAnimal()
        bodyScale = CIGlobals.toonBodyScales[animal]
        headScale = CIGlobals.toonHeadScales[animal][2]
        shoulderHeight = CIGlobals.legHeightDict[
            self.legs] * bodyScale + CIGlobals.torsoHeightDict[
                self.torso] * bodyScale
        height = shoulderHeight + CIGlobals.headHeightDict[
            self.head] * headScale
        bodyScale = CIGlobals.toonBodyScales[animal]
        self.setAvatarScale(bodyScale)
        self.getPart('head').setScale(headScale)
        self.setHeight(height)

    def setGloves(self):
        color = self.getGloveColor()
        gloves = self.find('**/hands')
        gloves.setColor(color)

    def setClothes(self):
        shirt, shirtcolor = self.getShirtStyle()
        short, shortcolor = self.getShortStyle()
        sleeve, sleevecolor = self.getSleeveStyle()
        torsot = self.findAllMatches('**/torso-top')
        torsob = self.findAllMatches('**/torso-bot')
        sleeves = self.findAllMatches('**/sleeves')
        torsot.setTexture(loader.loadTexture(shirt), 1)
        torsob.setTexture(loader.loadTexture(short), 1)
        sleeves.setTexture(loader.loadTexture(sleeve), 1)
        torsot.setColor(shirtcolor)
        sleeves.setColor(sleevecolor)
        torsob.setColor(shortcolor)

    def generateLegs(self):
        ToonGlobals.generateBodyPart(self, 'legs', self.getLegs(), 3, 'shorts')
        self.find('**/boots_long').stash()
        self.find('**/boots_short').stash()
        self.find('**/shoes').stash()

    def generateTorso(self):
        ToonGlobals.generateBodyPart(self, 'torso', self.getTorso(), 3, '')

    def generateHead(self, pat=0):
        gender = self.getGender()
        head = self.getAnimal()
        headtype = self.getHead()
        ToonHead.generateHead(self, gender, head, headtype)

    def setToonColor(self):
        self.setHeadColor()
        self.setTorsoColor()
        self.setLegColor()

    def setLegColor(self):
        legcolor = self.getLegColor()
        self.findAllMatches('**/legs').setColor(legcolor)
        self.findAllMatches('**/feet').setColor(legcolor)

    def setTorsoColor(self):
        torsocolor = self.getTorsoColor()
        self.findAllMatches('**/arms').setColor(torsocolor)
        self.findAllMatches('**/neck').setColor(torsocolor)
        self.findAllMatches('**/hands').setColor(1, 1, 1, 1)

    def setForcedTorsoAnim(self, string):
        self.forcedTorsoAnim = string
        self.loop(string, partName="torso")

    def clearForcedTorsoAnim(self):
        self.forcedTorsoAnim = None
        self.animFSM.request(self.animFSM.getCurrentState().getName())

    def enterOff(self, ts=0, callback=None, extraArgs=[]):
        self.currentAnim = None
        return

    def exitOff(self):
        pass

    def enterWin(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'win'
        self.sfx = base.audio3d.loadSfx("phase_3.5/audio/sfx/ENC_Win.ogg")
        self.sfx.setLoop(True)
        base.audio3d.attachSoundToObject(self.sfx, self)
        base.playSfx(self.sfx, node=self)
        self.loop("win")

    def exitWin(self):
        self.stop()
        self.sfx.stop()
        del self.sfx
        self.playingAnim = 'neutral'

    def enterShrug(self, ts=0, callback=None, extraArgs=[]):
        self.play("shrug")

    def exitShrug(self):
        self.exitGeneral()

    def enterHDance(self, ts=0, callback=None, extraArgs=[]):
        self.play("hdance")

    def exitHDance(self):
        self.exitGeneral()

    def enterScientistWork(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scwork")

    def exitScientistWork(self):
        self.exitGeneral()

    def enterScientistEmcee(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scemcee")

    def exitScientistEmcee(self):
        self.exitGeneral()

    def enterScientistGame(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scgame")

    def exitScientistGame(self):
        self.exitGeneral()

    def enterScientistJealous(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scjealous")

    def exitScientistJealous(self):
        self.exitGeneral()

    def enterWave(self, ts=0, callback=None, extraArgs=[]):
        self.play("wave")

    def exitWave(self):
        self.exitGeneral()

    def enterLaugh(self, ts=0, callback=None, extraArgs=[]):
        self.setPlayRate(5.0, "neutral")
        self.loop("neutral")

    def exitLaugh(self):
        self.setPlayRate(1.0, "neutral")
        self.stop()

    def enterNeutral(self, ts=0, callback=None, extraArgs=[]):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() in [
                        GagState.START, GagState.RELEASED
                ]:
                    self.loop("neutral", partName="legs")
                    if self.animal == "dog":
                        self.loop("neutral", partName="head")
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop("neutral", partName="legs")
            return
        self.loop("neutral")
        self.playingAnim = 'neutral'

    def exitNeutral(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def exitGeneral(self):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() in [
                        GagState.START, GagState.RELEASED
                ]:
                    self.stop(partName='legs')
                else:
                    self.stop()
            else:
                self.stop()
        else:
            self.stop()

    def enterRun(self, ts=0, callback=None, extraArgs=[]):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() in [
                        GagState.START, GagState.RELEASED
                ]:
                    self.loop("run", partName="legs")
                    if self.animal == "dog":
                        self.loop("run", partName="head")
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop("run", partName="legs")
            return
        self.loop("run")

    def exitRun(self):
        self.exitGeneral()

    def enterWalk(self, ts=0, callback=None, extraArgs=[]):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() in [
                        GagState.START, GagState.RELEASED
                ]:
                    self.loop("walk", partName="legs")
                    if self.animal == "dog":
                        self.loop("walk", partName="head")
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop("walk", partName="legs")
            return
        self.loop("walk")

    def exitWalk(self):
        self.exitGeneral()

    def enterWalkBack(self, ts=0, callback=None, extraArgs=[]):
        self.setPlayRate(-1.0, "walk")
        self.enterWalk()

    def exitWalkBack(self):
        self.exitWalk()
        self.setPlayRate(1.0, "walk")

    def enterOpenBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book1 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book1.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self,
                                   "book",
                                   startFrame=CIGlobals.OpenBookFromFrame,
                                   endFrame=CIGlobals.OpenBookToFrame,
                                   name=self.uniqueName('enterOpenBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback,
                        [callback, extraArgs])
        self.track.start(ts)
        self.book1.play("chan",
                        fromFrame=CIGlobals.OpenBookFromFrame,
                        toFrame=CIGlobals.OpenBookToFrame)

    def exitOpenBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book1:
            self.book1.cleanup()
            self.book1 = None
        self.playingAnim = 'neutral'

    def enterReadBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book2 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book2.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))

        self.pingpong("book",
                      fromFrame=CIGlobals.ReadBookFromFrame,
                      toFrame=CIGlobals.ReadBookToFrame)
        self.book2.pingpong("chan",
                            fromFrame=CIGlobals.ReadBookFromFrame,
                            toFrame=CIGlobals.ReadBookToFrame)

    def exitReadBook(self):
        if self.book2:
            self.book2.cleanup()
            self.book2 = None
        self.playingAnim = 'neutral'

    def enterCloseBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book3 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book3.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self,
                                   "book",
                                   startFrame=CIGlobals.CloseBookFromFrame,
                                   endFrame=CIGlobals.CloseBookToFrame,
                                   name=self.uniqueName('enterCloseBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback,
                        [callback, extraArgs])
        self.track.start(ts)
        self.book3.play("chan",
                        fromFrame=CIGlobals.CloseBookFromFrame,
                        toFrame=CIGlobals.CloseBookToFrame)

    def exitCloseBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book3:
            self.book3.cleanup()
            self.book3 = None
        self.playingAnim = 'neutral'

    def enterTeleportOut(self, ts=0, callback=None, extraArgs=[]):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": enterTeleportOut")
        self.playingAnim = 'tele'
        self.portal1 = Actor(
            "phase_3.5/models/props/portal-mod.bam",
            {"chan": "phase_3.5/models/props/portal-chan.bam"})
        self.portal1.play("chan")
        self.portal1.reparentTo(
            self.getPart('legs').find('**/def_joint_right_hold'))
        self.play("tele")
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterTeleportOut')
        else:
            name = 'enterTeleportOut'
        self.track = Sequence(Wait(0.4),
                              Func(self.teleportOutSfx),
                              Wait(1.3),
                              Func(self.throwPortal),
                              Wait(3.4),
                              name=name)
        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportOutDone,
                        [callback, extraArgs])
        self.track.start(ts)

    def doPortalBins(self, portal):
        portal.setBin('shadow', 0)
        portal.setDepthWrite(0)
        portal.setDepthTest(0)

    def teleportOutDone(self, callback, requestStatus):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": teleportOutDone")
        self.__doCallback(callback, requestStatus)
        self.exitTeleportOut()

    def teleportOutSfx(self):
        self.outSfx = base.audio3d.loadSfx(
            "phase_3.5/audio/sfx/AV_teleport.ogg")
        base.audio3d.attachSoundToObject(self.outSfx, self.portal1)
        base.playSfx(self.outSfx, node=self)

    def throwPortal(self):
        self.doPortalBins(self.portal1)
        self.portal1.reparentTo(self.getPart('legs').find('**/joint_nameTag'))
        self.portal1.setScale(CIGlobals.PortalScale)
        self.portal1.setY(6.5)
        self.portal1.setH(180)

    def exitTeleportOut(self):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": exitTeleportOut")
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal1:
            self.portal1.cleanup()
            self.portal1 = None
        self.playingAnim = 'neutral'

    def getTeleportInTrack(self, portal):
        self.doPortalBins(portal)

        holeTrack = Sequence()
        holeTrack.append(Func(portal.reparentTo, self))
        pos = Point3(0, -2.4, 0)
        holeTrack.append(Func(portal.setPos, pos))
        holeTrack.append(
            ActorInterval(portal, 'chan', startTime=3.4, endTime=3.1))
        holeTrack.append(Wait(0.6))
        holeTrack.append(
            ActorInterval(portal, 'chan', startTime=3.1, endTime=3.4))

        def restorePortal(portal):
            portal.setPos(0, 0, 0)
            portal.detachNode()
            portal.clearBin()
            portal.clearDepthTest()
            portal.clearDepthWrite()

        holeTrack.append(Func(restorePortal, portal))
        toonTrack = Sequence(Wait(0.3), Func(self.getGeomNode().show),
                             Func(self.nametag3d.show),
                             ActorInterval(self, 'happy', startTime=0.45))
        if hasattr(self, 'uniqueName'):
            trackName = self.uniqueName('teleportIn')
        else:
            trackName = 'teleportIn'
        return Parallel(toonTrack, holeTrack, name=trackName)

    def enterTeleportIn(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'happy'
        self.portal2 = Actor(
            "phase_3.5/models/props/portal-mod.bam",
            {"chan": "phase_3.5/models/props/portal-chan.bam"})
        self.show()
        self.getGeomNode().hide()
        self.nametag3d.hide()
        self.track = self.getTeleportInTrack(self.portal2)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportInDone,
                        [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(
            self, self.track.getName())
        self.track.start(ts)

    def teleportInDone(self, callback, extraArgs):
        self.__doCallback(callback, extraArgs)
        self.exitTeleportIn()

    def exitTeleportIn(self):
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal2:
            self.portal2.cleanup()
            self.portal2 = None
        if self.getGeomNode():
            self.getGeomNode().show()
        if self.nametag3d:
            self.nametag3d.show()
        self.playingAnim = 'neutral'

    def enterFallFWD(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'fallf'
        self.play("fallf")
        Sequence(Wait(0.5), SoundInterval(self.fallSfx, node=self)).start()

    def exitFallFWD(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterFallBCK(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'fallb'
        self.play("fallb")
        Sequence(Wait(0.5), SoundInterval(self.fallSfx, node=self)).start()

    def exitFallBCK(self):
        self.playingAnim = 'neutral'
        self.exitGeneral()

    def enterHappyJump(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'happy'
        self.play("happy")

    def exitHappyJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterSwim(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'swim'
        self.loop("swim")
        self.getGeomNode().setP(-89.0)
        self.getGeomNode().setZ(4.0)
        nt = self.nametag3d
        nt.setX(0)
        nt.setY(-2)
        nt.setZ(5.0)

    def exitSwim(self):
        self.exitGeneral()
        self.getGeomNode().setP(0.0)
        self.getGeomNode().setZ(0.0)
        nt = self.nametag3d
        nt.setX(0)
        nt.setY(0)
        nt.setZ(self.getHeight() + 0.3)
        self.playingAnim = 'neutral'

    def enterDied(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'lose'
        self.isdying = True
        self.play("lose")
        self.track = Sequence(Wait(2.2),
                              Func(self.dieSfx),
                              Wait(2.8),
                              self.getGeomNode().scaleInterval(
                                  2,
                                  Point3(0.01),
                                  startScale=(self.getGeomNode().getScale())),
                              Func(self.delToon),
                              name=self.uniqueName('enterDied'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.diedDone,
                        [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(
            self, 'enterTeleportOut')
        self.track.start(ts)

    def diedDone(self, callback, extraArgs):
        self.__doCallback(callback, extraArgs)
        self.exitDied()

    def __doCallback(self, callback, extraArgs):
        if callback:
            if extraArgs:
                callback(*extraArgs)
            else:
                callback()

    def dieSfx(self):
        self.Losesfx = base.audio3d.loadSfx("phase_5/audio/sfx/ENC_Lose.ogg")
        base.audio3d.attachSoundToObject(self.Losesfx, self)
        base.playSfx(self.Losesfx, node=self)

    def delToon(self):
        self.isdead = True

    def exitDied(self):
        if self.track != None:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        self.rescaleToon()
        self.playingAnim = 'neutral'

    def enterJump(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'jump'
        self.loop("jump")

    def exitJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterLeap(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'leap'
        self.loop("leap")

    def exitLeap(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterCringe(self, ts=0, callback=None, extraArgs=[]):
        self.play("cringe")

    def exitCringe(self):
        self.exitGeneral()

    def enterConked(self, ts=0, callback=None, extraArgs=[]):
        self.play("conked")

    def exitConked(self):
        self.exitGeneral()
Exemple #8
0
class Toon(Avatar.Avatar, ToonHead):
    notify = DirectNotifyGlobal.directNotify.newCategory('Toon')

    def __init__(self):
        Avatar.Avatar.__init__(self)
        ToonHead.__init__(self)
        try:
            self.Toon_initialized
            return
        except:
            self.Toon_initialized = 1

        self.avatarType = 'toon'
        self.soundChatBubble = base.loadSfx(
            'phase_3/audio/sfx/GUI_balloon_popup.ogg')
        self.swimRunSfx = base.loadSfx(
            'phase_4/audio/sfx/AV_footstep_runloop_water.ogg')
        self.swimRunLooping = False
        self.animFSM = ClassicFSM('Toon', [
            State('off', self.enterOff, self.exitOff),
            State('neutral', self.enterNeutral, self.exitNeutral)
        ], 'off', 'off')

        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                self.notify.warning(
                    'animFSM in flux, state=%s, not requesting off' %
                    self.animFSM.getCurrentState().getName())
        else:
            self.notify.warning('animFSM has been deleted')
        if self.effectTrack != None:
            self.effectTrack.finish()
            self.effectTrack = None
        if self.emoteTrack != None:
            self.emoteTrack.finish()
            self.emoteTrack = None
        if self.stunTrack != None:
            self.stunTrack.finish()
            self.stunTrack = None
        if self.wake:
            self.wake.stop()
            self.wake.destroy()
            self.wake = None
        self.cleanupPieModel()
        return

    def delete(self):
        try:
            self.Toon_deleted
        except:
            self.Toon_deleted = 1
            self.stopAnimations()
            self.rightHands = None
            self.rightHand = None
            self.leftHands = None
            self.leftHand = None
            self.headParts = None
            self.torsoParts = None
            self.hipsParts = None
            self.legsParts = None
            del self.animFSM
            for bookActor in self.__bookActors:
                bookActor.cleanup()

            del self.__bookActors
            for holeActor in self.__holeActors:
                holeActor.cleanup()

            del self.__holeActors
            self.soundTeleport = None
            self.motion.delete()
            self.motion = None

            self.removeHeadMeter()
            self.removeGMIcon()
            self.removePartyHat()
            Avatar.Avatar.delete(self)
            ToonHead.delete(self)

    def updateToonDNA(self, newDNA, fForce=0):
        self.newDNA = newDNA
        self.style.gender = newDNA.getGender()
        oldDNA = self.style
        if fForce or newDNA.head != oldDNA.head:
            self.swapToonHead(newDNA.head)
        if fForce or newDNA.torso != oldDNA.torso:
            self.swapToonTorso(newDNA.torso, genClothes=0)
            self.loop('neutral')
        if fForce or newDNA.legs != oldDNA.legs:
            self.swapToonLegs(newDNA.legs)
        self.swapToonColor(newDNA)
        self.__swapToonClothes(newDNA)

    def parentToonParts(self):
        if self.hasLOD():
            for lodName in self.getLODNames():
                if base.config.GetBool('want-new-anims', 1):
                    if not self.getPart('torso',
                                        lodName).find('**/def_head').isEmpty():
                        self.attach('head', 'torso', 'def_head', lodName)
                    else:
                        self.attach('head', 'torso', 'joint_head', lodName)
                else:
                    self.attach('head', 'torso', 'joint_head', lodName)
                self.attach('torso', 'legs', 'joint_hips', lodName)

        else:
            self.attach('head', 'torso', 'joint_head')
            self.attach('torso', 'legs', 'joint_hips')

    def unparentToonParts(self):
        if self.hasLOD():
            for lodName in self.getLODNames():
                self.getPart('head', lodName).reparentTo(self.getLOD(lodName))
                self.getPart('torso', lodName).reparentTo(self.getLOD(lodName))
                self.getPart('legs', lodName).reparentTo(self.getLOD(lodName))

        else:
            self.getPart('head').reparentTo(self.getGeomNode())
            self.getPart('torso').reparentTo(self.getGeomNode())
            self.getPart('legs').reparentTo(self.getGeomNode())

    def generateToon(self):
        self.setDNAString()
        self.generateToonLegs()
        self.generateToonHead()
        self.generateToonTorso()
        self.generateToonColor()
        self.parentToonParts()
        #self.rescaleToon()
        #self.resetHeight()
        self.setupToonNodes()

    def setupToonNodes(self):
        rightHand = NodePath('rightHand')
        self.rightHand = None
        self.rightHands = []
        leftHand = NodePath('leftHand')
        self.leftHands = []
        self.leftHand = None
        for lodName in self.getLODNames():
            hand = self.getPart('torso', lodName).find('**/joint_Rhold')
            if base.config.GetBool('want-new-anims', 1):
                if not self.getPart(
                        'torso',
                        lodName).find('**/def_joint_right_hold').isEmpty():
                    hand = self.getPart(
                        'torso', lodName).find('**/def_joint_right_hold')
            else:
                hand = self.getPart('torso', lodName).find('**/joint_Rhold')
            self.rightHands.append(hand)
            rightHand = rightHand.instanceTo(hand)
            if base.config.GetBool('want-new-anims', 1):
                if not self.getPart(
                        'torso',
                        lodName).find('**/def_joint_left_hold').isEmpty():
                    hand = self.getPart('torso',
                                        lodName).find('**/def_joint_left_hold')
            else:
                hand = self.getPart('torso', lodName).find('**/joint_Lhold')
            self.leftHands.append(hand)
            leftHand = leftHand.instanceTo(hand)
            if self.rightHand == None:
                self.rightHand = rightHand
            if self.leftHand == None:
                self.leftHand = leftHand

        self.headParts = self.findAllMatches('**/__Actor_head')
        self.legsParts = self.findAllMatches('**/__Actor_legs')
        self.hipsParts = self.legsParts.findAllMatches('**/joint_hips')
        self.torsoParts = self.hipsParts.findAllMatches('**/__Actor_torso')
        return

    def initializeBodyCollisions(self, collIdStr):
        Avatar.Avatar.initializeBodyCollisions(self, collIdStr)
        if not self.ghostMode:
            self.collNode.setCollideMask(self.collNode.getIntoCollideMask()
                                         | BitmaskGlobals.PieBitmask)

    def generateToonLegs(self, copy=1):
        global Preloaded
        legStyle = self.newDNA.legs
        filePrefix = LegDict.get(legStyle)
        if filePrefix is None:
            self.notify.error('unknown leg style: %s' % legStyle)
        print(Preloaded[filePrefix + '-1000'])
        # self.loadModel(Preloaded[filePrefix+'-1000'], 'legs', '1000', True)
        self.loadModel(Preloaded[filePrefix + '-1000'])
        if not copy:
            self.showPart('legs', '1000')
        self.loadAnims(LegsAnimDict[legStyle], 'legs', '1000')
        self.findAllMatches('**/boots_short').stash()
        self.findAllMatches('**/boots_long').stash()
        self.findAllMatches('**/shoes').stash()
        return

    def swapToonLegs(self, legStyle, copy=1):
        self.unparentToonParts()
        self.removePart('legs', '1000')
        # Bugfix: Until upstream Panda3D includes this, we have to do it here.
        if 'legs' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['legs']
        self.style.legs = legStyle
        self.generateToonLegs(copy)
        self.generateToonColor()
        self.parentToonParts()
        self.rescaleToon()
        self.resetHeight()
        del self.shadowJoint
        self.initializeDropShadow()
        self.initializeNametag3d()

    def generateToonTorso(self, copy=1, genClothes=1):
        global Preloaded
        torsoStyle = self.style.torso
        filePrefix = TorsoDict.get(torsoStyle)
        if filePrefix is None:
            self.notify.error('unknown torso style: %s' % torsoStyle)
        self.loadModel(Preloaded[filePrefix + '-1000'], 'torso', '1000', True)
        if not copy:
            self.showPart('torso', '1000')
        self.loadAnims(TorsoAnimDict[torsoStyle], 'torso', '1000')
        if genClothes == 1 and not len(torsoStyle) == 1:
            self.generateToonClothes()
        return

    def swapToonTorso(self, torsoStyle, copy=1, genClothes=1):
        self.unparentToonParts()
        self.removePart('torso', '1000')
        # Bugfix: Until upstream Panda3D includes this, we have to do it here.
        if 'torso' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['torso']
        self.style.torso = torsoStyle
        self.generateToonTorso(copy, genClothes)
        self.generateToonColor()
        self.parentToonParts()
        self.rescaleToon()
        self.resetHeight()
        self.setupToonNodes()

    def setDNAString(self):
        self.newDNA = ToonDNA.ToonDNA()
        self.setDNA(self.newDNA)

    def setDNA(self, dna):
        self.style = dna
        #self.generateToon()

    def generateToonHead(self, copy=1):
        headHeight = ToonHead.generateToonHead(self, copy, self.style, '1000')
        if self.style.getAnimal() == 'dog':
            self.loadAnims(HeadAnimDict[self.style.head], 'head', '1000')

    def swapToonHead(self, headStyle=-1, copy=1):
        self.stopLookAroundNow()
        self.eyelids.request('open')
        self.unparentToonParts()
        self.removePart('head', '1000')
        # Bugfix: Until upstream Panda3D includes this, we have to do it here.
        if 'head' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['head']
        if headStyle > -1:
            self.style.head = headStyle
        self.generateToonHead(copy)
        self.generateToonColor()
        self.parentToonParts()
        self.rescaleToon()
        self.resetHeight()
        self.eyelids.request('open')
        self.startLookAround()

    def generateToonColor(self):
        ToonHead.generateToonColor(self, self.style)
        armColor = self.style.getArmColor()
        gloveColor = self.style.getGloveColor()
        legColor = self.style.getLegColor()
        for lodName in self.getLODNames():
            torso = self.getPart('torso', lodName)
            if len(self.style.torso) == 1:
                parts = torso.findAllMatches('**/torso*')
                parts.setColor(*armColor)
            for pieceName in ('arms', 'neck'):
                piece = torso.find('**/' + pieceName)
                piece.setColor(*armColor)

            hands = torso.find('**/hands')
            hands.setColor(*gloveColor)
            legs = self.getPart('legs', lodName)
            for pieceName in ('legs', 'feet'):
                piece = legs.find('**/%s;+s' % pieceName)
                piece.setColor(*legColor)

        if self.cheesyEffect == ToontownGlobals.CEGreenToon:
            self.reapplyCheesyEffect()

    def swapToonColor(self, dna):
        self.setStyle(dna)
        self.generateToonColor()

    def __swapToonClothes(self, dna):
        self.setStyle(dna)
        self.generateToonClothes(fromNet=1)

    def generateToonClothes(self, fromNet=0):
        swappedTorso = 0
        if self.hasLOD():
            if self.style.getGender() == 'f' and fromNet == 0:
                try:
                    bottomPair = ToonDNA.GirlBottoms[self.style.botTex]
                except:
                    bottomPair = ToonDNA.GirlBottoms[0]

                if len(self.style.torso) < 2:
                    self.sendLogSuspiciousEvent(
                        'nakedToonDNA %s was requested' % self.style.torso)
                    return 0
                elif self.style.torso[1] == 's' and bottomPair[
                        1] == ToonDNA.SKIRT:
                    self.swapToonTorso(self.style.torso[0] + 'd', genClothes=0)
                    swappedTorso = 1
                elif self.style.torso[1] == 'd' and bottomPair[
                        1] == ToonDNA.SHORTS:
                    self.swapToonTorso(self.style.torso[0] + 's', genClothes=0)
                    swappedTorso = 1
            try:
                texName = ToonDNA.Shirts[self.style.topTex]
            except:
                texName = ToonDNA.Shirts[0]

            shirtTex = loader.loadTexture(texName, okMissing=True)
            if shirtTex is None:
                self.sendLogSuspiciousEvent('failed to load texture %s' %
                                            texName)
                shirtTex = loader.loadTexture(ToonDNA.Shirts[0])
            shirtTex.setMinfilter(Texture.FTLinearMipmapLinear)
            shirtTex.setMagfilter(Texture.FTLinear)
            try:
                shirtColor = ToonDNA.ClothesColors[self.style.topTexColor]
            except:
                shirtColor = ToonDNA.ClothesColors[0]

            try:
                texName = ToonDNA.Sleeves[self.style.sleeveTex]
            except:
                texName = ToonDNA.Sleeves[0]

            sleeveTex = loader.loadTexture(texName, okMissing=True)
            if sleeveTex is None:
                self.sendLogSuspiciousEvent('failed to load texture %s' %
                                            texName)
                sleeveTex = loader.loadTexture(ToonDNA.Sleeves[0])
            sleeveTex.setMinfilter(Texture.FTLinearMipmapLinear)
            sleeveTex.setMagfilter(Texture.FTLinear)
            try:
                sleeveColor = ToonDNA.ClothesColors[self.style.sleeveTexColor]
            except:
                sleeveColor = ToonDNA.ClothesColors[0]

            if self.style.getGender() == 'm':
                try:
                    texName = ToonDNA.BoyShorts[self.style.botTex]
                except:
                    texName = ToonDNA.BoyShorts[0]

            else:
                try:
                    texName = ToonDNA.GirlBottoms[self.style.botTex][0]
                except:
                    texName = ToonDNA.GirlBottoms[0][0]

            bottomTex = loader.loadTexture(texName, okMissing=True)
            if bottomTex is None:
                self.sendLogSuspiciousEvent('failed to load texture %s' %
                                            texName)
                if self.style.getGender() == 'm':
                    bottomTex = loader.loadTexture(ToonDNA.BoyShorts[0])
                else:
                    bottomTex = loader.loadTexture(ToonDNA.GirlBottoms[0][0])
            bottomTex.setMinfilter(Texture.FTLinearMipmapLinear)
            bottomTex.setMagfilter(Texture.FTLinear)
            try:
                bottomColor = ToonDNA.ClothesColors[self.style.botTexColor]
            except:
                bottomColor = ToonDNA.ClothesColors[0]

            darkBottomColor = bottomColor * 0.5
            darkBottomColor.setW(1.0)
            for lodName in self.getLODNames():
                thisPart = self.getPart('torso', lodName)
                top = thisPart.find('**/torso-top')
                top.setTexture(shirtTex, 1)
                top.setColor(shirtColor)
                sleeves = thisPart.find('**/sleeves')
                sleeves.setTexture(sleeveTex, 1)
                sleeves.setColor(sleeveColor)
                bottoms = thisPart.findAllMatches('**/torso-bot')
                for bottomNum in xrange(0, bottoms.getNumPaths()):
                    bottom = bottoms.getPath(bottomNum)
                    bottom.setTexture(bottomTex, 1)
                    bottom.setColor(bottomColor)

                caps = thisPart.findAllMatches('**/torso-bot-cap')
                caps.setColor(darkBottomColor)

        return swappedTorso

    def getDialogueArray(self):
        animalType = self.style.getType()
        if animalType == 'dog':
            dialogueArray = DogDialogueArray
        elif animalType == 'cat':
            dialogueArray = CatDialogueArray
        elif animalType == 'horse':
            dialogueArray = HorseDialogueArray
        elif animalType == 'mouse':
            dialogueArray = MouseDialogueArray
        elif animalType == 'rabbit':
            dialogueArray = RabbitDialogueArray
        elif animalType == 'duck':
            dialogueArray = DuckDialogueArray
        elif animalType == 'monkey':
            dialogueArray = MonkeyDialogueArray
        elif animalType == 'bear':
            dialogueArray = BearDialogueArray
        elif animalType == 'pig':
            dialogueArray = PigDialogueArray
        else:
            dialogueArray = None
        return dialogueArray

    def findSomethingToLookAt(self):
        if self.randGen.random() < 0.1 or not hasattr(self, 'cr'):
            x = self.randGen.choice((-0.8, -0.5, 0, 0.5, 0.8))
            y = self.randGen.choice((-0.5, 0, 0.5, 0.8))
            self.lerpLookAt(Point3(x, 1.5, y), blink=1)
            return
        nodePathList = []
        for id, obj in self.cr.doId2do.items():
            if hasattr(obj, 'getStareAtNodeAndOffset') and obj != self:
                node, offset = obj.getStareAtNodeAndOffset()
                if node.getY(self) > 0.0:
                    nodePathList.append((node, offset))

        if nodePathList:
            nodePathList.sort(lambda x, y: cmp(x[0].getDistance(self), y[0].
                                               getDistance(self)))
            if len(nodePathList) >= 2:
                if self.randGen.random() < 0.9:
                    chosenNodePath = nodePathList[0]
                else:
                    chosenNodePath = nodePathList[1]
            else:
                chosenNodePath = nodePathList[0]
            self.lerpLookAt(chosenNodePath[0].getPos(self), blink=1)
        else:
            ToonHead.findSomethingToLookAt(self)

    def setupPickTrigger(self):
        Avatar.Avatar.setupPickTrigger(self)
        torso = self.getPart('torso', '1000')
        if torso == None:
            return 0
        self.pickTriggerNp.reparentTo(torso)
        size = self.style.getTorsoSize()
        if size == 'short':
            self.pickTriggerNp.setPosHprScale(0, 0, 0.5, 0, 0, 0, 1.5, 1.5, 2)
        elif size == 'medium':
            self.pickTriggerNp.setPosHprScale(0, 0, 0.5, 0, 0, 0, 1, 1, 2)
        else:
            self.pickTriggerNp.setPosHprScale(0, 0, 1, 0, 0, 0, 1, 1, 2)
        return 1

    def enterNeutral(self,
                     animMultiplier=1,
                     ts=0,
                     callback=None,
                     extraArgs=[]):
        anim = 'neutral'
        self.pose(anim, int(self.getNumFrames(anim) * self.randGen.random()))
        self.loop(anim, restart=0)
        self.setPlayRate(animMultiplier, anim)
        self.playingAnim = anim
        self.setActiveShadow(1)

    def exitNeutral(self):
        self.stop()

    def enterOff(self, animMultiplier=1, ts=0, callback=None):
        self.playingAnim = None
        return

    def exitOff(self):
        pass

    def __returnToLastAnim(self, task):
        if self.playingAnim:
            self.loop(self.playingAnim)
        elif self.hp > 0:
            self.loop('neutral')
        else:
            self.loop('sad-neutral')
        return Task.done

    def getPieces(self, *pieces):
        results = []
        for lodName in self.getLODNames():
            for partName, pieceNames in pieces:
                part = self.getPart(partName, lodName)
                if part:
                    if type(pieceNames) == types.StringType:
                        pieceNames = (pieceNames, )
                    for pieceName in pieceNames:
                        npc = part.findAllMatches('**/%s;+s' % pieceName)
                        for i in xrange(npc.getNumPaths()):
                            results.append(npc[i])

        return results

    def __doHeadScale(self, scale, lerpTime):
        if scale == None:
            scale = ToontownGlobals.toonHeadScales[self.style.getAnimal()]
        track = Parallel()
        for hi in xrange(self.headParts.getNumPaths()):
            head = self.headParts[hi]
            track.append(
                LerpScaleInterval(head, lerpTime, scale,
                                  blendType='easeInOut'))

        return track

    def __doLegsScale(self, scale, lerpTime):
        if scale == None:
            scale = 1
            invScale = 1
        else:
            invScale = 1.0 / scale
        track = Parallel()
        for li in xrange(self.legsParts.getNumPaths()):
            legs = self.legsParts[li]
            torso = self.torsoParts[li]
            track.append(
                LerpScaleInterval(legs, lerpTime, scale,
                                  blendType='easeInOut'))
            track.append(
                LerpScaleInterval(torso,
                                  lerpTime,
                                  invScale,
                                  blendType='easeInOut'))

        return track

    def __doToonScale(self, scale, lerpTime):
        if scale == None:
            scale = 1
        node = self.getGeomNode().getChild(0)
        track = Sequence(
            Parallel(
                LerpHprInterval(node,
                                lerpTime,
                                Vec3(0.0, 0.0, 0.0),
                                blendType='easeInOut'),
                LerpScaleInterval(node, lerpTime, scale,
                                  blendType='easeInOut')),
            Func(self.resetHeight))
        return track

    def doToonColorScale(self, scale, lerpTime, keepDefault=0):
        if keepDefault:
            self.defaultColorScale = scale
        if scale == None:
            scale = VBase4(1, 1, 1, 1)
        node = self.getGeomNode()
        caps = self.getPieces(('torso', 'torso-bot-cap'))
        track = Sequence()
        track.append(Func(node.setTransparency, 1))
        if scale[3] != 1:
            for cap in caps:
                track.append(HideInterval(cap))

        track.append(
            LerpColorScaleInterval(node,
                                   lerpTime,
                                   scale,
                                   blendType='easeInOut'))
        if scale[3] == 1:
            track.append(Func(node.clearTransparency))
            for cap in caps:
                track.append(ShowInterval(cap))

        elif scale[3] == 0:
            track.append(Func(node.clearTransparency))
        return track

    def __colorToonSkin(self, color, lerpTime):
        track = Sequence()
        colorTrack = Parallel()
        torsoPieces = self.getPieces(('torso', ('arms', 'neck')))
        legPieces = self.getPieces(('legs', ('legs', 'feet')))
        headPieces = self.getPieces(('head', '*head*'))
        if color == None:
            armColor = self.style.getArmColor()
            legColor = self.style.getLegColor()
            headColor = self.style.getHeadColor()
        else:
            armColor = color
            legColor = color
            headColor = color
        for piece in torsoPieces:
            colorTrack.append(Func(piece.setColor, *armColor))

        for piece in legPieces:
            colorTrack.append(Func(piece.setColor, *legColor))

        for piece in headPieces:
            if 'hatNode' not in str(piece) and 'glassesNode' not in str(piece):
                colorTrack.append(Func(piece.setColor, *headColor))

        track.append(colorTrack)
        return track

    def __colorToonEars(self, color, colorScale, lerpTime):
        track = Sequence()
        earPieces = self.getPieces(('head', '*ear*'))
        if len(earPieces) == 0:
            return track
        colorTrack = Parallel()
        if earPieces[0].hasColor():
            if color == None:
                headColor = self.style.getHeadColor()
            else:
                headColor = color
            for piece in earPieces:
                colorTrack.append(Func(piece.setColor, *headColor))

        else:
            if colorScale == None:
                colorScale = VBase4(1, 1, 1, 1)
            for piece in earPieces:
                colorTrack.append(Func(piece.setColorScale, *colorScale))

        track.append(colorTrack)
        return track

    def __colorScaleToonMuzzle(self, scale, lerpTime):
        track = Sequence()
        colorTrack = Parallel()
        muzzlePieces = self.getPieces(('head', '*muzzle*'))
        if scale == None:
            scale = VBase4(1, 1, 1, 1)
        for piece in muzzlePieces:
            colorTrack.append(Func(piece.setColorScale, scale))

        track.append(colorTrack)
        return track

    def __colorToonGloves(self, color, lerpTime):
        track = Sequence()
        colorTrack = Parallel()
        glovePieces = self.getPieces(('torso', '*hands*'))
        if color == None:
            for piece in glovePieces:
                colorTrack.append(Func(piece.clearColor))

        else:
            for piece in glovePieces:
                colorTrack.append(Func(piece.setColor, color))

        track.append(colorTrack)
        return track

    def restoreDefaultColorScale(self):
        node = self.getGeomNode()
        if node:
            if self.defaultColorScale:
                node.setColorScale(self.defaultColorScale)
                if self.defaultColorScale[3] != 1:
                    node.setTransparency(1)
                else:
                    node.clearTransparency()
            else:
                node.clearColorScale()
                node.clearTransparency()

    def __doToonColor(self, color, lerpTime):
        node = self.getGeomNode()
        if color == None:
            return Func(node.clearColor)
        else:
            return Func(node.setColor, color, 1)
        return

    def __doPartsColorScale(self, scale, lerpTime):
        if scale == None:
            scale = VBase4(1, 1, 1, 1)
        node = self.getGeomNode()
        pieces = self.getPieces(('torso', ('arms', 'neck')),
                                ('legs', ('legs', 'feet')),
                                ('head', '+GeomNode'))
        track = Sequence()
        track.append(Func(node.setTransparency, 1))
        for piece in pieces:
            if piece.getName(
            )[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral':
                continue
            track.append(ShowInterval(piece))

        p1 = Parallel()
        for piece in pieces:
            if piece.getName(
            )[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral':
                continue
            p1.append(
                LerpColorScaleInterval(piece,
                                       lerpTime,
                                       scale,
                                       blendType='easeInOut'))

        track.append(p1)
        if scale[3] == 1:
            track.append(Func(node.clearTransparency))
        elif scale[3] == 0:
            track.append(Func(node.clearTransparency))
            for piece in pieces:
                if piece.getName(
                )[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral':
                    continue
                track.append(HideInterval(piece))

        self.generateHat()
        self.generateGlasses()
        return track

    def putOnSuit(self, suitType, setDisplayName=True, rental=False):
        if self.isDisguised:
            self.takeOffSuit()
        from toontown.suit import Suit
        deptIndex = suitType
        suit = Suit.Suit()
        dna = SuitDNA.SuitDNA()
        if rental == True:
            if SuitDNA.suitDepts[deptIndex] == 's':
                suitType = 'cc'
            elif SuitDNA.suitDepts[deptIndex] == 'm':
                suitType = 'sc'
            elif SuitDNA.suitDepts[deptIndex] == 'l':
                suitType = 'bf'
            elif SuitDNA.suitDepts[deptIndex] == 'c':
                suitType = 'f'
            else:
                self.notify.warning(
                    'Suspicious: Incorrect rental suit department requested')
                suitType = 'cc'
        dna.newSuit(suitType)
        suit.setStyle(dna)
        suit.isDisguised = 1
        suit.generateSuit()
        suit.initializeDropShadow()
        suit.setPos(self.getPos())
        suit.setHpr(self.getHpr())
        for part in suit.getHeadParts():
            part.hide()

        suitHeadNull = suit.find('**/joint_head')
        toonHead = self.getPart('head', '1000')
        Emote.globalEmote.disableAll(self)
        toonGeom = self.getGeomNode()
        toonGeom.hide()
        worldScale = toonHead.getScale(render)
        self.headOrigScale = toonHead.getScale()
        headPosNode = hidden.attachNewNode('headPos')
        toonHead.reparentTo(headPosNode)
        toonHead.setPos(0, 0, 0.2)
        headPosNode.reparentTo(suitHeadNull)
        headPosNode.setScale(render, worldScale)
        suitGeom = suit.getGeomNode()
        suitGeom.reparentTo(self)
        if rental == True:
            suit.makeRentalSuit(SuitDNA.suitDepts[deptIndex])
        self.suit = suit
        self.suitGeom = suitGeom
        self.setHeight(suit.getHeight())
        self.nametag3d.setPos(0, 0, self.height + 1.3)
        self.suit.loop('neutral')
        self.isDisguised = 1
        self.setFont(ToontownGlobals.getSuitFont())
        self.nametag.setSpeechFont(ToontownGlobals.getSuitFont())
        if setDisplayName:
            if hasattr(base, 'idTags') and base.idTags:
                name = self.getAvIdName()
            else:
                name = self.getName()
            suitDept = SuitDNA.suitDepts.index(SuitDNA.getSuitDept(suitType))
            suitName = SuitBattleGlobals.SuitAttributes[suitType]['name']
            self.nametag.setDisplayName(
                TTLocalizer.SuitBaseNameWithLevel % {
                    'name': name,
                    'dept': suitName,
                    'level': self.cogLevels[suitDept] + 1
                })
            self.nametag.setWordwrap(9.0)

    def takeOffSuit(self):
        if not self.isDisguised:
            return
        suitType = self.suit.style.name
        toonHeadNull = self.find('**/1000/**/def_head')
        if not toonHeadNull:
            toonHeadNull = self.find('**/1000/**/joint_head')
        toonHead = self.getPart('head', '1000')
        toonHead.reparentTo(toonHeadNull)
        toonHead.setScale(self.headOrigScale)
        toonHead.setPos(0, 0, 0)
        headPosNode = self.suitGeom.find('**/headPos')
        headPosNode.removeNode()
        self.suitGeom.reparentTo(self.suit)
        self.resetHeight()
        self.nametag3d.setPos(0, 0, self.height + 0.5)
        toonGeom = self.getGeomNode()
        toonGeom.show()
        Emote.globalEmote.releaseAll(self)
        self.isDisguised = 0
        self.setFont(ToontownGlobals.getToonFont())
        self.nametag.setSpeechFont(ToontownGlobals.getToonFont())
        self.nametag.setWordwrap(None)
        if hasattr(base, 'idTags') and base.idTags:
            name = self.getAvIdName()
        else:
            name = self.getName()
        self.setDisplayName(name)
        self.suit.delete()
        del self.suit
        del self.suitGeom

    def makeWaiter(self):
        if not self.isDisguised:
            return
        self.suit.makeWaiter(self.suitGeom)
Exemple #9
0
class Char(Avatar.Avatar):

    def __init__(self):
        try:
            self.Char_initialized
            return
        except:
            self.Char_initialized = 1

        Avatar.Avatar.__init__(self)
        self.avatarType = CIGlobals.CChar
        self.avatarName = None
        self.currentAnim = None
        self.charType = ''
        self.eyes = loader.loadTexture('phase_3/maps/eyes1.jpg', 'phase_3/maps/eyes1_a.rgb')
        self.closedEyes = loader.loadTexture('phase_3/maps/mickey_eyes_closed.jpg', 'phase_3/maps/mickey_eyes_closed_a.rgb')
        self.animFSM = ClassicFSM('Char', [State('off', self.enterOff, self.exitOff),
         State('neutral', self.enterNeutral, self.exitNeutral),
         State('walk', self.enterWalk, self.exitWalk),
         State('run', self.enterRun, self.exitRun)], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()
        Avatar.Avatar.initializeBodyCollisions(self, self.avatarType, 3.5, 1)
        return

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                notify.warning('animFSM in flux, state=%s, not requesting off' % self.animFSM.getCurrentState().getName())
        else:
            notify.warning('animFSM has been deleted')

    def disable(self):
        self.stopBlink()
        self.stopAnimations()
        Avatar.Avatar.disable(self)

    def delete(self):
        try:
            self.Char_deleted
        except:
            self.Char_deleted = 1
            del self.animFSM
            Avatar.Avatar.delete(self)

    def setChat(self, chatString):
        if self.charType == CIGlobals.Mickey:
            self.dial = base.audio3d.loadSfx('phase_3/audio/dial/mickey.wav')
        elif self.charType == CIGlobals.Minnie:
            self.dial = base.audio3d.loadSfx('phase_3/audio/dial/minnie.wav')
        elif self.charType == CIGlobals.Goofy:
            self.dial = base.audio3d.loadSfx('phase_6/audio/dial/goofy.wav')
        base.audio3d.attachSoundToObject(self.dial, self)
        self.dial.play()
        Avatar.Avatar.setChat(self, chatString)

    def setName(self, nameString, charName = None):
        self.avatarName = nameString
        Avatar.Avatar.setName(self, nameString, avatarType=self.avatarType, charName=charName)

    def generateChar(self, charType):
        self.charType = charType
        if charType == CIGlobals.Mickey or charType == CIGlobals.Minnie:
            self.loadModel('phase_3/models/char/' + charType.lower() + '-' + str(CIGlobals.ModelDetail(self.avatarType)) + '.bam')
            self.loadAnims({'neutral': 'phase_3/models/char/' + charType.lower() + '-wait.bam',
             'walk': 'phase_3/models/char/' + charType.lower() + '-walk.bam',
             'run': 'phase_3/models/char/' + charType.lower() + '-run.bam',
             'left-start': 'phase_3.5/models/char/' + charType.lower() + '-left-start.bam',
             'left': 'phase_3.5/models/char/' + charType.lower() + '-left.bam',
             'right-start': 'phase_3.5/models/char/' + charType.lower() + '-right-start.bam',
             'right': 'phase_3.5/models/char/' + charType.lower() + '-right.bam'})
            if charType == CIGlobals.Mickey:
                self.mickeyEye = self.controlJoint(None, 'modelRoot', 'joint_pupilR')
                self.mickeyEye.setY(0.025)
            for bundle in self.getPartBundleDict().values():
                bundle = bundle['modelRoot'].getBundle()
                earNull = bundle.findChild('sphere3')
                if not earNull:
                    earNull = bundle.findChild('*sphere3')
                earNull.clearNetTransforms()

            for bundle in self.getPartBundleDict().values():
                charNodepath = bundle['modelRoot'].partBundleNP
                bundle = bundle['modelRoot'].getBundle()
                earNull = bundle.findChild('sphere3')
                if not earNull:
                    earNull = bundle.findChild('*sphere3')
                ears = charNodepath.find('**/sphere3')
                if ears.isEmpty():
                    ears = charNodepath.find('**/*sphere3')
                ears.clearEffect(CharacterJointEffect.getClassType())
                earRoot = charNodepath.attachNewNode('earRoot')
                earPitch = earRoot.attachNewNode('earPitch')
                earPitch.setP(40.0)
                ears.reparentTo(earPitch)
                earNull.addNetTransform(earRoot.node())
                ears.clearMat()
                ears.node().setPreserveTransform(ModelNode.PTNone)
                ears.setP(-40.0)
                ears.flattenMedium()
                ears.setBillboardAxis()
                self.startBlink()

        elif charType == CIGlobals.Pluto:
            self.loadModel('phase_6/models/char/pluto-1000.bam')
            self.loadAnims({'walk': 'phase_6/models/char/pluto-walk.bam',
             'neutral': 'phase_6/models/char/pluto-neutral.bam',
             'sit': 'phase_6/models/char/pluto-sit.bam',
             'stand': 'phase_6/models/char/pluto-stand.bam'})
        elif charType == CIGlobals.Goofy:
            self.loadModel('phase_6/models/char/TT_G-1500.bam')
            self.loadAnims({'neutral': 'phase_6/models/char/TT_GWait.bam',
             'walk': 'phase_6/models/char/TT_GWalk.bam'})
        else:
            raise StandardError('unknown char %s!' % charType)
        Avatar.Avatar.initShadow(self)
        return

    def initializeLocalCollisions(self, name, radius):
        Avatar.Avatar.initializeLocalCollisions(self, radius, 2, name)

    def startBlink(self):
        randomStart = random.uniform(0.5, 5)
        taskMgr.add(self.blinkTask, 'blinkTask')

    def stopBlink(self):
        taskMgr.remove('blinkTask')
        taskMgr.remove('doBlink')
        taskMgr.remove('openEyes')

    def blinkTask(self, task):
        taskMgr.add(self.doBlink, 'doBlink')
        delay = random.uniform(0.5, 7)
        task.delayTime = delay
        return task.again

    def doBlink(self, task):
        self.closeEyes()
        taskMgr.doMethodLater(0.2, self.openEyes, 'openEyes')
        return task.done

    def closeEyes(self):
        self.find('**/joint_pupilR').hide()
        self.find('**/joint_pupilL').hide()
        if self.charType == CIGlobals.Mickey:
            self.mickeyEye.setY(-0.025)
            self.mickeyEye.hide()
        self.find('**/eyes').setTexture(self.closedEyes, 1)

    def openEyes(self, task):
        self.find('**/joint_pupilR').show()
        self.find('**/joint_pupilL').show()
        if self.charType == CIGlobals.Mickey:
            self.mickeyEye.setY(0.025)
            self.mickeyEye.show()
        self.find('**/eyes').setTexture(self.eyes, 1)
        return task.done

    def enterOff(self):
        self.currentAnim = None
        return

    def exitOff(self):
        pass

    def enterNeutral(self):
        self.loop('neutral')

    def exitNeutral(self):
        self.stop()

    def enterWalk(self):
        self.loop('walk')

    def exitWalk(self):
        self.stop()

    def enterRun(self):
        self.loop('run')

    def exitRun(self):
        self.stop()