PhasedObject.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. from direct.directnotify.DirectNotifyGlobal import *
  2. class PhasedObject:
  3. """
  4. This class is governs the loading and unloading of successive
  5. phases in an ordered and automatic manner.
  6. An object can only have one phase at any given moment. At the
  7. completion of setPhase() the current and all previous phases are
  8. guaranteed to be loaded, while all later phases are guaranteed
  9. to be unloaded.
  10. In order to define a phase, simply define the functions:
  11. loadPhase<#> and unloadPhase<#> where # corresponds to the number
  12. of the phase to be defined and # >= 0.
  13. You also have the ability to define alias for phases so that
  14. your function definitions are more descriptive. The way to do
  15. this is to provide an aliasMap to __init__(). The aliasMap is
  16. of the form {'alias':#, ...}. You can then call setPhase() with
  17. this alias as well.
  18. So for example, if you wanted to alias phase 0 to 'Far' you
  19. would define loadPhaseFar() and unloadPhaseFar(). Upon calling
  20. setPhase(0), setPhase('Far'), setPhase(<any phase greater than 0>),
  21. or setPhase(<any alias greater than 'Far'>), loadPhaseFar() will
  22. be invoked.
  23. For a skeleton example class, see the AnfaPhasedObject class
  24. definition lower in this file.
  25. """
  26. notify = directNotify.newCategory("PhasedObject")
  27. def __init__(self, aliasMap = {}):
  28. self.phase = -1
  29. self.phaseAliasMap = {}
  30. self.aliasPhaseMap = {}
  31. self.__phasing = False
  32. for alias,phase in aliasMap.items():
  33. self.setAlias(phase, alias)
  34. def __repr__(self):
  35. return 'PhasedObject(%s)' % str(self.aliasPhaseMap)
  36. def __str__(self):
  37. outStr = PhasedObject.__repr__(self)
  38. outStr += ' in phase \'%s\'' % self.getPhase()
  39. return outStr
  40. def setAlias(self, phase, alias):
  41. """
  42. Map an alias to a phase number.
  43. phase must be >= 0 and alias must be a string
  44. of characters suitable for python variable names.
  45. The mapping must be one-to-one.
  46. """
  47. assert isinstance(phase,int) and phase >= 0
  48. assert isinstance(alias,str)
  49. self.phaseAliasMap[phase] = alias
  50. self.aliasPhaseMap[alias] = phase
  51. def getPhaseAlias(self, phase):
  52. """
  53. Returns the alias of a phase number, if it exists.
  54. Otherwise, returns the phase number.
  55. """
  56. return self.phaseAliasMap.get(phase, phase)
  57. def getAliasPhase(self, alias):
  58. """
  59. Returns the phase number of an alias, if it exists.
  60. Otherwise, returns the alias.
  61. """
  62. return self.aliasPhaseMap.get(alias, alias)
  63. def getPhase(self):
  64. """
  65. Returns the current phase (or alias, if defined)
  66. this object is currently in.
  67. """
  68. return self.getPhaseAlias(self.phase)
  69. def setPhase(self, aPhase):
  70. """
  71. aPhase can be either a phase number or a predefined alias.
  72. Will invoke a sequence of loadPhase*() or unloadPhase*()
  73. functions corresponding to the difference between the current
  74. phase and aPhase, starting at the current phase.
  75. """
  76. assert not self.__phasing, 'Already phasing. Cannot setPhase() while phasing in progress.'
  77. self.__phasing = True
  78. phase = self.aliasPhaseMap.get(aPhase,aPhase)
  79. assert isinstance(phase,int), 'Phase alias \'%s\' not found' % aPhase
  80. assert phase >= -1, 'Invalid phase number \'%s\'' % phase
  81. if phase > self.phase:
  82. for x in range(self.phase + 1, phase + 1):
  83. self.__loadPhase(x)
  84. elif phase < self.phase:
  85. for x in range(self.phase, phase, -1):
  86. self.__unloadPhase(x)
  87. self.__phasing = False
  88. def cleanup(self):
  89. """
  90. Will force the unloading, in correct order, of all currently
  91. loaded phases.
  92. """
  93. if self.phase >= 0:
  94. self.setPhase(-1)
  95. def __loadPhase(self, phase):
  96. aPhase = self.phaseAliasMap.get(phase,phase)
  97. getattr(self, 'loadPhase%s' % aPhase,
  98. lambda: self.__phaseNotFound('load',aPhase))()
  99. self.phase = phase
  100. def __unloadPhase(self, phase):
  101. aPhase = self.phaseAliasMap.get(phase,phase)
  102. getattr(self, 'unloadPhase%s' % aPhase,
  103. lambda: self.__phaseNotFound('unload',aPhase))()
  104. self.phase = (phase - 1)
  105. def __phaseNotFound(self, mode, aPhase):
  106. assert self.notify.debug('%s%s() not found!\n' % (mode,aPhase))
  107. if __debug__:
  108. class AnfaPhasedObject(PhasedObject):
  109. """
  110. This is an example class to demonstrate the concept of
  111. alias mapping for PhasedObjects.
  112. As the distance between an observer and this object closes,
  113. we would set the phase level succesively higher, with an initial
  114. phase of 'Away' being set in __init__:
  115. setPhase('Far') -> invokes loadPhaseFar()
  116. setPhase('Near') -> invokes loadPhaseNear()
  117. Now let's say the objects start moving away from each other:
  118. setPhase('Far') -> invokes unloadPhaseNear()
  119. setPhase('Away') -> invokes unloadPhaseFar()
  120. Now one object teleports to the other:
  121. setPhase('At') -> invokes loadPhase('Far'),
  122. then loadPhase('Near'),
  123. then loadPhase('At')
  124. Now the phased object is destroyed, we must clean it up
  125. before removal:
  126. cleanup() -> invokes unloadPhase('At')
  127. then unloadPhase('Near')
  128. then unloadPhase('Far')
  129. then unloadPhase('Away')
  130. """
  131. def __init__(self):
  132. PhasedObject.__init__(self, {'At':3, 'Near':2, 'Far':1, 'Away':0})
  133. self.setPhase('Away')
  134. def loadPhaseAway(self):
  135. print 'loading Away'
  136. def unloadPhaseAway(self):
  137. print 'unloading Away'
  138. def loadPhaseFar(self):
  139. print 'loading Far'
  140. def unloadPhaseFar(self):
  141. print 'unloading Far'
  142. def loadPhaseNear(self):
  143. print 'loading Near'
  144. def unloadPhaseNear(self):
  145. print 'unloading Near'
  146. def loadPhaseAt(self):
  147. print 'loading At'
  148. def unloadPhaseAt(self):
  149. print 'unloading At'