DistributedLevel.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. """DistributedLevel.py: contains the DistributedLevel class"""
  2. from ClockDelta import *
  3. from PythonUtil import Functor, sameElements, list2dict, uniqueElements
  4. from IntervalGlobal import *
  5. import ToontownGlobals
  6. import DistributedObject
  7. import Level
  8. import LevelConstants
  9. import DirectNotifyGlobal
  10. import EntityCreator
  11. import OnscreenText
  12. import Task
  13. import LevelUtil
  14. import FactoryCameraViews
  15. class DistributedLevel(DistributedObject.DistributedObject,
  16. Level.Level):
  17. """DistributedLevel"""
  18. notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevel')
  19. WantVisibility = config.GetBool('level-visibility', 1)
  20. HideZones = config.GetBool('level-hidezones', 1)
  21. # TODO: move level-model stuff to LevelMgr or FactoryLevelMgr?
  22. FloorCollPrefix = 'zoneFloor'
  23. OuchTaskName = 'ouchTask'
  24. VisChangeTaskName = 'visChange'
  25. def __init__(self, cr):
  26. DistributedObject.DistributedObject.__init__(self, cr)
  27. Level.Level.__init__(self)
  28. self.lastToonZone = 0
  29. self.lastCamZone = 0
  30. self.titleColor = (1,1,1,1)
  31. self.titleText = OnscreenText.OnscreenText(
  32. "",
  33. fg = self.titleColor,
  34. shadow = (0,0,0,1),
  35. font = ToontownGlobals.getSuitFont(),
  36. pos = (0,-0.5),
  37. scale = 0.16,
  38. drawOrder = 0,
  39. mayChange = 1,
  40. )
  41. self.smallTitleText = OnscreenText.OnscreenText(
  42. "",
  43. fg = self.titleColor,
  44. font = ToontownGlobals.getSuitFont(),
  45. pos = (0.65,0.9),
  46. scale = 0.08,
  47. drawOrder = 0,
  48. mayChange = 1,
  49. bg = (.5,.5,.5,.5),
  50. align = TextNode.ARight,
  51. )
  52. self.zonesEnteredList = []
  53. self.fColorZones = 0
  54. def generate(self):
  55. DistributedLevel.notify.debug('generate')
  56. DistributedObject.DistributedObject.generate(self)
  57. # this dict stores entity reparents if the parent hasn't been
  58. # created yet
  59. self.parent2pendingChildren = {}
  60. # if the AI sends us a full spec, it will be put here
  61. self.curSpec = None
  62. # Most (if not all) of the timed entities of levels
  63. # run on looping intervals that are started once based on
  64. # the level's start time.
  65. # This sync request is *NOT* guaranteed to finish by the time
  66. # the entities get created.
  67. # We should listen for any and all time-sync events and re-sync
  68. # all our entities at that time.
  69. toonbase.tcr.timeManager.synchronize('DistributedLevel.generate')
  70. # add factory menu to SpeedChat
  71. toonbase.localToon.chatMgr.chatInputSpeedChat.addFactoryMenu()
  72. # add special camera views
  73. self.factoryViews = FactoryCameraViews.FactoryCameraViews(self)
  74. # the real required fields
  75. def setLevelZoneId(self, zoneId):
  76. # this is the zone that the level is in; we should listen to this
  77. # zone the entire time we're in here
  78. self.levelZone = zoneId
  79. def setPlayerIds(self, avIdList):
  80. self.avIdList = avIdList
  81. assert toonbase.localToon.doId in self.avIdList
  82. def setEntranceId(self, entranceId):
  83. self.entranceId = entranceId
  84. def getEntranceId(self):
  85. return self.entranceId
  86. # "required" fields (these ought to be required fields, but
  87. # the AI level obj doesn't know the data values until it has been
  88. # generated.)
  89. def setZoneIds(self, zoneIds):
  90. DistributedLevel.notify.debug('setZoneIds: %s' % zoneIds)
  91. self.zoneIds = zoneIds
  92. def setStartTimestamp(self, timestamp):
  93. DistributedLevel.notify.debug('setStartTimestamp: %s' % timestamp)
  94. self.startTime = globalClockDelta.networkToLocalTime(timestamp,bits=32)
  95. def setScenarioIndex(self, scenarioIndex):
  96. self.scenarioIndex = scenarioIndex
  97. # ugly hack: we treat a few DC fields as if they were required,
  98. # and use 'levelAnnounceGenerate()' in place of regular old
  99. # announceGenerate(). Note that we have to call
  100. # gotAllRequired() in the last 'faux-required' DC update
  101. # handler. If you add another field, move this to the last one.
  102. self.privGotAllRequired()
  103. def privGotAllRequired(self):
  104. self.levelAnnounceGenerate()
  105. def levelAnnounceGenerate(self):
  106. pass
  107. def initializeLevel(self, levelSpec):
  108. """subclass should call this as soon as it's located its level spec.
  109. Must be called after obj has been generated."""
  110. if __debug__:
  111. # if we're in debug, give the server the opportunity to send us
  112. # a full spec
  113. self.candidateSpec = levelSpec
  114. self.sendUpdate('requestCurrentLevelSpec',
  115. [hash(levelSpec),
  116. levelSpec.entTypeReg.getHashStr()])
  117. else:
  118. self.privGotSpec(levelSpec)
  119. if __debug__:
  120. def setSpecDeny(self, reason):
  121. DistributedLevel.notify.error(reason)
  122. def setSpecSenderDoId(self, doId):
  123. DistributedLevel.notify.debug('setSpecSenderDoId: %s' % doId)
  124. blobSender = toonbase.tcr.doId2do[doId]
  125. def setSpecBlob(specBlob, blobSender=blobSender, self=self):
  126. blobSender.sendAck()
  127. from LevelSpec import LevelSpec
  128. spec = eval(specBlob)
  129. if spec is None:
  130. spec = self.candidateSpec
  131. del self.candidateSpec
  132. self.privGotSpec(spec)
  133. if blobSender.isComplete():
  134. setSpecBlob(blobSender.getBlob())
  135. else:
  136. evtName = self.uniqueName('specDone')
  137. blobSender.setDoneEvent(evtName)
  138. self.acceptOnce(evtName, setSpecBlob)
  139. def privGotSpec(self, levelSpec):
  140. Level.Level.initializeLevel(self, self.doId, levelSpec,
  141. self.scenarioIndex)
  142. # all of the local entities have been created now.
  143. # TODO: have any of the distributed entities been created at this point?
  144. # there should not be any pending reparents left at this point
  145. # TODO: is it possible for a local entity to be parented to a
  146. # distributed entity? I think so!
  147. assert len(self.parent2pendingChildren) == 0
  148. # make sure the zoneNums from the model match the zoneNums from
  149. # the zone entities
  150. modelZoneNums = self.zoneNums
  151. entityZoneNums = self.zoneNum2entId.keys()
  152. if not sameElements(modelZoneNums, entityZoneNums):
  153. DistributedLevel.notify.error(
  154. 'model zone nums (%s) do not match entity zone nums (%s)\n'
  155. 'use SpecUtil.updateSpec' %
  156. (modelZoneNums, entityZoneNums))
  157. # load stuff
  158. self.initVisibility()
  159. self.placeLocalToon()
  160. def announceLeaving(self):
  161. """call this just before leaving the level; this may result in
  162. the factory being destroyed on the AI"""
  163. self.doneBarrier()
  164. def placeLocalToon(self):
  165. # the entrancePoint entities register themselves with us
  166. if self.entranceId not in self.entranceId2entity:
  167. self.notify.warning('unknown entranceId %s' % self.entranceId)
  168. toonbase.localToon.setPos(0,0,0)
  169. else:
  170. epEnt = self.entranceId2entity[self.entranceId]
  171. epEnt.placeToon(toonbase.localToon,
  172. self.avIdList.index(toonbase.localToon.doId),
  173. len(self.avIdList))
  174. # kickstart the visibility
  175. firstZoneEnt = self.getEntity(epEnt.getZoneEntId())
  176. self.enterZone(firstZoneEnt.getZoneNum())
  177. def createEntityCreator(self):
  178. """Create the object that will be used to create Entities.
  179. Inheritors, override if desired."""
  180. return EntityCreator.EntityCreator(level=self)
  181. def onEntityTypePostCreate(self, entType):
  182. """listen for certain entity types to be created"""
  183. Level.Level.onEntityTypePostCreate(self, entType)
  184. # NOTE: these handlers are private in order to avoid overriding
  185. # similar handlers in base classes
  186. if entType == 'levelMgr':
  187. self.__handleLevelMgrCreated()
  188. def __handleLevelMgrCreated(self):
  189. # as soon as the levelMgr has been created, load up the model
  190. # and extract zone info. We need to do this before any entities
  191. # get parented to the level!
  192. levelMgr = self.getEntity(LevelConstants.LevelMgrEntId)
  193. self.geom = levelMgr.geom
  194. # find the zones in the model and fix them up
  195. self.zoneNum2node = LevelUtil.getZoneNum2Node(self.geom)
  196. self.zoneNums = self.zoneNum2node.keys()
  197. self.zoneNums.sort()
  198. DistributedLevel.notify.debug('zones: %s' % self.zoneNums)
  199. # fix up the floor collisions for walkable zones *before*
  200. # any entities get put under the model
  201. for zoneNum,zoneNode in self.zoneNum2node.items():
  202. # don't do this to the uberzone
  203. if zoneNum == LevelConstants.UberZoneNum:
  204. continue
  205. # if this is a walkable zone, fix up the model
  206. allColls = zoneNode.findAllMatches('**/+CollisionNode').asList()
  207. # which of them, if any, are floors?
  208. floorColls = []
  209. for coll in allColls:
  210. bitmask = coll.node().getIntoCollideMask()
  211. if not (bitmask & ToontownGlobals.FloorBitmask).isZero():
  212. floorColls.append(coll)
  213. if len(floorColls) > 0:
  214. # rename the floor collision nodes, and make sure no other
  215. # nodes under the ZoneNode have that name
  216. floorCollName = '%s%s' % (DistributedLevel.FloorCollPrefix,
  217. zoneNum)
  218. others = zoneNode.findAllMatches(
  219. '**/%s' % floorCollName).asList()
  220. for other in others:
  221. other.setName('%s_renamed' % floorCollName)
  222. for floorColl in floorColls:
  223. floorColl.setName(floorCollName)
  224. # listen for zone enter events from floor collisions
  225. def handleZoneEnter(collisionEntry,
  226. self=self, zoneNum=zoneNum):
  227. self.toonEnterZone(zoneNum)
  228. floorNode = collisionEntry.getIntoNode()
  229. if floorNode.hasTag('ouch'):
  230. ouchLevel = int(floorNode.getTag('ouch'))
  231. self.startOuch(ouchLevel*2)
  232. self.accept('enter%s' % floorCollName, handleZoneEnter)
  233. # also listen for zone exit events for the sake of the
  234. # ouch system
  235. def handleZoneExit(collisionEntry,
  236. self=self, zoneNum=zoneNum):
  237. floorNode = collisionEntry.getIntoNode()
  238. if floorNode.hasTag('ouch'):
  239. self.stopOuch()
  240. self.accept('exit%s' % floorCollName, handleZoneExit)
  241. def announceGenerate(self):
  242. DistributedLevel.notify.debug('announceGenerate')
  243. DistributedObject.DistributedObject.announceGenerate(self)
  244. def disable(self):
  245. DistributedLevel.notify.debug('disable')
  246. # geom is owned by the levelMgr
  247. if hasattr(self, 'geom'):
  248. del self.geom
  249. self.shutdownVisibility()
  250. self.destroyLevel()
  251. DistributedObject.DistributedObject.disable(self)
  252. self.ignoreAll()
  253. # NOTE: this should be moved to FactoryInterior
  254. if self.smallTitleText:
  255. self.smallTitleText.cleanup()
  256. self.smallTitleText = None
  257. if self.titleText:
  258. self.titleText.cleanup()
  259. self.titleText = None
  260. self.zonesEnteredList = []
  261. def delete(self):
  262. DistributedLevel.notify.debug('delete')
  263. DistributedObject.DistributedObject.delete(self)
  264. # remove factory menu to SpeedChat
  265. toonbase.localToon.chatMgr.chatInputSpeedChat.removeFactoryMenu()
  266. # remove special camera views
  267. del self.factoryViews
  268. # make sure the ouch task is stopped
  269. self.stopOuch()
  270. def getZoneNode(self, zoneNum):
  271. return self.zoneNum2node[zoneNum]
  272. def requestReparent(self, entity, parentId):
  273. if __debug__:
  274. # some things (like cogs) are not actually entities yet;
  275. # they don't have an entId. Big deal, let it go through.
  276. if hasattr(entity, 'entId'):
  277. assert(entity.entId != parentId)
  278. parent = self.getEntity(parentId)
  279. if parent is not None:
  280. # parent has already been created
  281. entity.reparentTo(parent.getNodePath())
  282. else:
  283. # parent hasn't been created yet; schedule the reparent
  284. DistributedLevel.notify.debug(
  285. 'entity %s requesting reparent to %s, not yet created' %
  286. (entity, parentId))
  287. entity.reparentTo(hidden)
  288. # if this parent doesn't already have another child pending,
  289. # do some setup
  290. if not self.parent2pendingChildren.has_key(parentId):
  291. self.parent2pendingChildren[parentId] = []
  292. # do the reparent(s) once the parent is initialized
  293. def doReparent(parentId=parentId, self=self):
  294. assert self.parent2pendingChildren.has_key(parentId)
  295. parent=self.getEntity(parentId)
  296. for child in self.parent2pendingChildren[parentId]:
  297. DistributedLevel.notify.debug(
  298. 'performing pending reparent of %s to %s' %
  299. (child, parent))
  300. child.reparentTo(parent.getNodePath())
  301. del self.parent2pendingChildren[parentId]
  302. self.ignore(self.getEntityCreateEvent(parentId))
  303. self.accept(self.getEntityCreateEvent(parentId), doReparent)
  304. self.parent2pendingChildren[parentId].append(entity)
  305. def showZone(self, zoneNum):
  306. zone = self.zoneNum2node[zoneNum]
  307. zone.unstash()
  308. zone.clearColor()
  309. def setColorZones(self, fColorZones):
  310. self.fColorZones = fColorZones
  311. def getColorZones(self):
  312. return self.fColorZones
  313. def hideZone(self, zoneNum):
  314. zone = self.zoneNum2node[zoneNum]
  315. if self.fColorZones:
  316. zone.unstash()
  317. zone.setColor(1,0,0)
  318. else:
  319. zone.stash()
  320. def setTransparency(self, alpha, zone=None):
  321. self.geom.setTransparency(1)
  322. if zone is None:
  323. node = self.geom
  324. else:
  325. node = self.zoneNum2node[zone]
  326. node.setAlphaScale(alpha)
  327. def initVisibility(self):
  328. # start out with every zone visible, since none of the zones have
  329. # been hidden
  330. self.curVisibleZoneNums = list2dict(self.zoneNums)
  331. # the UberZone is always visible, so it's not included in the
  332. # zones' viz lists
  333. del self.curVisibleZoneNums[0]
  334. # we have not entered any zone yet
  335. self.curZoneNum = None
  336. self.visChangedThisFrame = 0
  337. # listen for camera-ray/floor collision events
  338. def handleCameraRayFloorCollision(collEntry, self=self):
  339. name = collEntry.getIntoNode().getName()
  340. prefixLen = len(DistributedLevel.FloorCollPrefix)
  341. if (name[:prefixLen] == DistributedLevel.FloorCollPrefix):
  342. try:
  343. zoneNum = int(name[prefixLen:])
  344. except:
  345. DistributedLevel.notify.debug(
  346. 'Invalid zone floor collision node: %s'
  347. % name)
  348. else:
  349. self.camEnterZone(zoneNum)
  350. self.accept('on-floor', handleCameraRayFloorCollision)
  351. # if no viz, listen to all the zones
  352. if not DistributedLevel.WantVisibility:
  353. zoneNums = list(self.zoneNums)
  354. zoneNums.remove(LevelConstants.UberZoneNum)
  355. self.setVisibility(zoneNums)
  356. # send out any zone changes at the end of the frame, just before
  357. # rendering
  358. taskMgr.add(self.visChangeTask,
  359. self.uniqueName(DistributedLevel.VisChangeTaskName),
  360. priority=49)
  361. def shutdownVisibility(self):
  362. taskMgr.remove(self.uniqueName(DistributedLevel.VisChangeTaskName))
  363. def toonEnterZone(self, zoneNum, ouchLevel=None):
  364. """
  365. zoneNum is an int.
  366. ouchLevel is a ??.
  367. The avatar (and not necessarily the camera) has entered
  368. a zone.
  369. See camEnterZone()
  370. """
  371. DistributedLevel.notify.debug('toonEnterZone%s' % zoneNum)
  372. if zoneNum != self.lastToonZone:
  373. self.lastToonZone = zoneNum
  374. print "toon is standing in zone %s" % zoneNum
  375. messenger.send("factoryZoneChanged", [zoneNum])
  376. def camEnterZone(self, zoneNum):
  377. """
  378. zoneNum is an int.
  379. The camera (and not necessarily the avatar) has entered
  380. a zone.
  381. See toonEnterZone()
  382. """
  383. DistributedLevel.notify.debug('camEnterZone%s' % zoneNum)
  384. self.enterZone(zoneNum)
  385. if zoneNum != self.lastCamZone:
  386. self.lastCamZone = zoneNum
  387. self.smallTitleText.hide()
  388. self.spawnTitleText()
  389. def enterZone(self, zoneNum):
  390. DistributedLevel.notify.debug("entering zone %s" % zoneNum)
  391. if not DistributedLevel.WantVisibility:
  392. return
  393. if zoneNum == self.curZoneNum:
  394. return
  395. if zoneNum not in self.zoneNum2entId:
  396. DistributedLevel.notify.error(
  397. 'no ZoneEntity for this zone (%s)!!' % zoneNum)
  398. self.updateVisibility(zoneNum)
  399. def updateVisibility(self, zoneNum=None):
  400. """update the visibility assuming that we're in the specified
  401. zone; don't check to see if it's the zone we're already in"""
  402. #print 'updateVisibility %s' % globalClock.getFrameCount()
  403. if zoneNum is None:
  404. zoneNum = self.curZoneNum
  405. zoneEntId = self.zoneNum2entId[zoneNum]
  406. zoneEnt = self.getEntity(zoneEntId)
  407. # use dicts to efficiently ensure that there are no duplicates
  408. visibleZoneNums = list2dict([zoneNum])
  409. visibleZoneNums.update(list2dict(zoneEnt.getVisibleZoneNums()))
  410. # we should not have the uberZone in the list at this point
  411. assert not 0 in visibleZoneNums
  412. if DistributedLevel.HideZones:
  413. # figure out which zones are new and which are going invisible
  414. # use dicts because it's faster to use dict.has_key(x)
  415. # than 'x in list'
  416. addedZoneNums = []
  417. removedZoneNums = []
  418. allVZ = dict(visibleZoneNums)
  419. allVZ.update(self.curVisibleZoneNums)
  420. for vz,dummy in allVZ.items():
  421. new = vz in visibleZoneNums
  422. old = vz in self.curVisibleZoneNums
  423. if new and old:
  424. continue
  425. if new:
  426. addedZoneNums.append(vz)
  427. else:
  428. removedZoneNums.append(vz)
  429. # show the new, hide the old
  430. DistributedLevel.notify.debug('showing zones %s' % addedZoneNums)
  431. for az in addedZoneNums:
  432. self.showZone(az)
  433. DistributedLevel.notify.debug('hiding zones %s' % removedZoneNums)
  434. for rz in removedZoneNums:
  435. self.hideZone(rz)
  436. self.setVisibility(visibleZoneNums.keys())
  437. self.curZoneNum = zoneNum
  438. self.curVisibleZoneNums = visibleZoneNums
  439. def setVisibility(self, vizList):
  440. """
  441. vizList is a list of visible zone numbers.
  442. """
  443. # convert the zone numbers into their actual zoneIds
  444. # always include Toontown and factory uberZones
  445. uberZone = self.getZoneId(zoneNum=LevelConstants.UberZoneNum)
  446. # the level itself is in the 'level zone'
  447. visibleZoneIds = [ToontownGlobals.UberZone, self.levelZone, uberZone]
  448. for vz in vizList:
  449. visibleZoneIds.append(self.getZoneId(zoneNum=vz))
  450. assert(uniqueElements(visibleZoneIds))
  451. DistributedLevel.notify.debug('new viz list: %s' % visibleZoneIds)
  452. toonbase.tcr.sendSetZoneMsg(self.levelZone, visibleZoneIds)
  453. def resetVisibility(self):
  454. # start out with every zone visible, since none of the zones have
  455. # been hidden
  456. self.curVisibleZoneNums = list2dict(self.zoneNums)
  457. # the UberZone is always visible, so it's not included in the
  458. # zones' viz lists
  459. del self.curVisibleZoneNums[0]
  460. # Make sure every zone is visible
  461. for vz,dummy in self.curVisibleZoneNums.items():
  462. self.showZone(vz)
  463. # Redo visibility using current zone num
  464. self.updateVisibility()
  465. def handleVisChange(self):
  466. """the zone visibility lists have changed on-the-fly"""
  467. Level.Level.handleVisChange(self)
  468. self.visChangedThisFrame = 1
  469. def visChangeTask(self, task):
  470. # this runs just before igloop; if viz lists have changed
  471. # this frame, updates the visibility and sends out a setZoneMsg
  472. if self.visChangedThisFrame:
  473. self.updateVisibility()
  474. self.visChangedThisFrame = 0
  475. return Task.cont
  476. if __debug__:
  477. # level editing stuff
  478. def setAttribChange(self, entId, attribName, valueStr, username):
  479. """every time the spec is edited, we get this message
  480. from the AI"""
  481. value = eval(valueStr)
  482. self.levelSpec.setAttribChange(entId, attribName, value, username)
  483. def spawnTitleText(self):
  484. def getDescription(zoneId, self=self):
  485. entId = self.zoneNum2entId.get(zoneId)
  486. if entId:
  487. ent = self.entities.get(entId)
  488. if ent and hasattr(ent, 'description'):
  489. return ent.description
  490. return None
  491. description = getDescription(self.lastCamZone)
  492. if description and description != '':
  493. taskMgr.remove("titleText")
  494. self.smallTitleText.setText(description)
  495. self.titleText.setText(description)
  496. self.titleText.setColor(Vec4(*self.titleColor))
  497. self.titleText.setFg(self.titleColor)
  498. # Only show the big title once per session.
  499. # If we've already seen it, just show the small title
  500. titleSeq = None
  501. if not self.lastCamZone in self.zonesEnteredList:
  502. self.zonesEnteredList.append(self.lastCamZone)
  503. titleSeq = Task.sequence(
  504. Task.Task(self.hideSmallTitleTextTask),
  505. Task.Task(self.showTitleTextTask),
  506. Task.pause(0.1),
  507. Task.pause(6.0),
  508. self.titleText.lerpColor(Vec4(self.titleColor[0],
  509. self.titleColor[1],
  510. self.titleColor[2],
  511. self.titleColor[3]),
  512. Vec4(self.titleColor[0],
  513. self.titleColor[1],
  514. self.titleColor[2],
  515. 0.0),
  516. 0.5),
  517. )
  518. smallTitleSeq = Task.sequence(Task.Task(self.hideTitleTextTask),
  519. Task.Task(self.showSmallTitleTask),
  520. Task.Task(self.showSmallTitleTask))
  521. if titleSeq:
  522. seq = Task.sequence(titleSeq, smallTitleSeq)
  523. else:
  524. seq = smallTitleSeq
  525. taskMgr.add(seq, "titleText")
  526. def showTitleTextTask(self, task):
  527. assert(DistributedLevel.notify.debug("hideTitleTextTask()"))
  528. self.titleText.show()
  529. return Task.done
  530. def hideTitleTextTask(self, task):
  531. assert(DistributedLevel.notify.debug("hideTitleTextTask()"))
  532. self.titleText.hide()
  533. return Task.done
  534. def showSmallTitleTask(self, task):
  535. # make sure large title is hidden
  536. self.titleText.hide()
  537. # show the small title
  538. self.smallTitleText.show()
  539. return Task.done
  540. def hideSmallTitleTextTask(self, task):
  541. assert(DistributedLevel.notify.debug("hideTitleTextTask()"))
  542. self.smallTitleText.hide()
  543. return Task.done
  544. # Ouch!
  545. def startOuch(self, ouchLevel, period=2):
  546. print 'startOuch %s' % ouchLevel
  547. if not hasattr(self, 'doingOuch'):
  548. def doOuch(task, self=self, ouchLevel=ouchLevel, period=period):
  549. self.b_setOuch(ouchLevel)
  550. self.lastOuchTime = globalClock.getFrameTime()
  551. taskMgr.doMethodLater(period, doOuch,
  552. DistributedLevel.OuchTaskName)
  553. # check to make sure we haven't done an ouch too recently
  554. delay = 0
  555. if hasattr(self, 'lastOuchTime'):
  556. curFrameTime = globalClock.getFrameTime()
  557. timeSinceLastOuch = (curFrameTime - self.lastOuchTime)
  558. if timeSinceLastOuch < period:
  559. delay = period - timeSinceLastOuch
  560. if delay > 0:
  561. taskMgr.doMethodLater(
  562. period, doOuch,
  563. DistributedLevel.OuchTaskName)
  564. else:
  565. doOuch(None)
  566. self.doingOuch = 1
  567. def stopOuch(self):
  568. if hasattr(self, 'doingOuch'):
  569. taskMgr.remove(DistributedLevel.OuchTaskName)
  570. del self.doingOuch
  571. def b_setOuch(self, penalty, anim=None):
  572. self.notify.debug('b_setOuch %s' % penalty)
  573. av = toonbase.localToon
  574. # play the stun track (flashing toon)
  575. if not av.isStunned:
  576. self.d_setOuch(penalty)
  577. self.setOuch(penalty, anim)
  578. def d_setOuch(self, penalty):
  579. self.sendUpdate("setOuch", [penalty])
  580. def setOuch(self, penalty, anim = None):
  581. if anim == "Squish":
  582. toonbase.tcr.playGame.getPlace().fsm.request('squished')
  583. av = toonbase.localToon
  584. av.stunToon()
  585. av.playDialogueForString("!")