routine.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. '''
  2. routine.py
  3. This is a module for setting up one-time or repeatable routines for mobs. This
  4. can include walking a path, forging a sword, singing verses of a song, or
  5. anything else. This was primarily meant to be for path-following, but I figured
  6. it was worth the time to generalize it out for more complex actions
  7. '''
  8. import mud, mudsys, auxiliary, storage, event
  9. ################################################################################
  10. # defines
  11. ################################################################################
  12. __dflt_routine_step_time__ = 10 # seconds between routine steps
  13. __global_routine_checks__ = []
  14. ################################################################################
  15. # auxiliary data
  16. ################################################################################
  17. class RoutineAuxData:
  18. '''Holds character data related to character routines.
  19. '''
  20. def __init__(self, set = None):
  21. self.routine = None # the routine we follow
  22. self.repeat = False # after we finish it, do we repeat?
  23. self.step = None # what step of the routine are we on
  24. self.checks = None # what checks do we perform before each step
  25. def copyTo(self, to):
  26. if isinstance(self.routine, list):
  27. to.routine = [x for x in self.routine]
  28. else:
  29. to.routine = None
  30. if isinstance(self.checks, list):
  31. to.checks = [x for x in self.checks]
  32. else:
  33. to.checks = None
  34. to.repeat = self.repeat
  35. to.step = self.step
  36. def copy(self):
  37. newdata = RoutineAuxData()
  38. self.copyTo(newdata)
  39. return newdata
  40. def store(self):
  41. return storage.StorageSet()
  42. def read(self, set):
  43. return
  44. ################################################################################
  45. # functions
  46. ################################################################################
  47. def register_routine_check(check):
  48. '''adds a routine check to the global list. Must be a function taking one
  49. argument, which is the character doing the routine. Return should be
  50. True if the check succeeded (i.e., we should not do a routine)'''
  51. __global_routine_checks__.append(check)
  52. def start_routine(ch):
  53. '''starts a character routine event in motion'''
  54. time = __dflt_routine_step_time__
  55. aux = ch.getAuxiliary("routine_data")
  56. item = aux.routine[aux.step]
  57. if isinstance(item, tuple):
  58. time = item[0]
  59. event.start_event(ch, time, routine_event)
  60. def set_routine(ch, routine, repeat = False, checks = None):
  61. '''Sets a routine to a character. Routine steps can constain commands
  62. (character strings), functions (one argument, ch), or tuples
  63. (delay, string | function). If a tuple is not supplied, the default
  64. step time is used'''
  65. aux = ch.getAuxiliary("routine_data")
  66. aux.checks = None
  67. if checks != None:
  68. aux.checks = [x for x in checks]
  69. aux.repeat = repeat
  70. aux.routine = None
  71. aux.step = None
  72. if routine != None:
  73. aux.routine = [x for x in routine]
  74. aux.step = 0
  75. start_routine(ch)
  76. def do_step(ch):
  77. '''Performs the current step increments'''
  78. aux = ch.getAuxiliary("routine_data")
  79. step = aux.routine[aux.step]
  80. time = __dflt_routine_step_time__
  81. # increment or decrement as necessary
  82. aux.step += 1
  83. # parse out the step
  84. if isinstance(step, tuple):
  85. time = step[0]
  86. step = step[1]
  87. # if it's a string, do it as an action. Otherwise, assume it is a function
  88. # and call it with ch as the only argument
  89. if isinstance(step, str):
  90. ch.act(step)
  91. else:
  92. step(ch)
  93. # figure out if we need to repeat it
  94. if aux.step == len(aux.routine) and aux.repeat == True:
  95. aux.step = 0
  96. # see if we need to queue up another event
  97. if aux.step < len(aux.routine):
  98. start_routine(ch)
  99. else:
  100. set_routine(ch, None)
  101. def try_step(ch):
  102. '''Checks to see if we can perform a step in the routine. Returns true or
  103. false if it did or not'''
  104. aux = ch.getAuxiliary("routine_data")
  105. if aux.routine == None:
  106. return False
  107. # build a list of the checks we need to perform
  108. checks = __global_routine_checks__
  109. if aux.checks != None:
  110. checks = checks + aux.checks
  111. # If we have checks, run them
  112. if checks != None:
  113. for check in checks:
  114. if check(ch) == True:
  115. # queue us back up to try again later
  116. start_routine(ch)
  117. return False
  118. # If we had no checks or they were all passed, do the step
  119. do_step(ch)
  120. return True
  121. ################################################################################
  122. # events
  123. ################################################################################
  124. def routine_event(owner, data, arg):
  125. '''this is the event that perpetuates NPC routines. Each NPC that has a
  126. routine running has one of these events tied to him or her. When the
  127. routine time expires, a check is made to see if the routine can go on.
  128. If it can, the routine step is performed and the step number is
  129. incremented'''
  130. try_step(owner)
  131. ################################################################################
  132. # commands
  133. ################################################################################
  134. def cmd_routine(ch, cmd, arg):
  135. '''Appends a routine onto a character. The second argument needs to be an
  136. evaluable list statement. Put it in parentheses to avoid being cut off
  137. as spaces, since parse treats it as a single word. Example:
  138. > routine man "[\'say hi\', (3, \'say I am a little teapot\')]" True
  139. this will say hi after the default delay, and I am a little teapot after
  140. a delay of 3. It will then loop through this process indefinitely.
  141. Alternatively, these commands can be replaced with function calls.
  142. '''
  143. try:
  144. tgt, routine, repeat = mud.parse_args(ch, True, cmd, arg,
  145. "ch.room.noself word(py_list) | bool(repeat)")
  146. except:
  147. return
  148. set_routine(tgt, eval(routine), repeat)
  149. ch.send("Routine set.")
  150. ################################################################################
  151. # initialization
  152. ################################################################################
  153. # auxiliary data
  154. auxiliary.install("routine_data", RoutineAuxData, "character")
  155. # base check. Don't do a routine if we're currently doing an action
  156. register_routine_check(lambda ch: ch.isActing())
  157. # commands
  158. mudsys.add_cmd("routine", None, cmd_routine, "admin", False)
  159. # misc initialization
  160. # mud.set_routine = set_routine
  161. #
  162. # now use: ch.set_routine(routine)
  163. # instead of: mud.set_routine(ch, routine)
  164. #
  165. mudsys.add_char_method("set_routine", set_routine)