DistributedCartesianGrid.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. from pandac.PandaModules import *
  2. from direct.interval.IntervalGlobal import *
  3. from direct.distributed.DistributedNode import DistributedNode
  4. from direct.task import Task
  5. from direct.gui import DirectGuiGlobals
  6. from direct.showbase.EventGroup import EventGroup
  7. from direct.showbase.PythonUtil import report
  8. from direct.distributed.GridParent import GridParent
  9. if __debug__:
  10. # For grid drawing
  11. from direct.directtools.DirectGeometry import *
  12. from direct.showbase.PythonUtil import randFloat
  13. from CartesianGridBase import CartesianGridBase
  14. # increase this number if you want to visualize the grid lines
  15. # above water level
  16. GRID_Z_OFFSET = 0.0
  17. class DistributedCartesianGrid(DistributedNode, CartesianGridBase):
  18. notify = directNotify.newCategory("DistributedCartesianGrid")
  19. notify.setDebug(0)
  20. VisualizeGrid = config.GetBool("visualize-cartesian-grid", 0)
  21. RuleSeparator = ":"
  22. def __init__(self, cr):
  23. DistributedNode.__init__(self, cr)
  24. # Let the derived classes instantiate the NodePath
  25. self.visAvatar = None
  26. self.gridVisContext = None
  27. # Do we have grid lines visualized?
  28. self._onOffState = False
  29. if __debug__:
  30. self.haveGridLines = 0
  31. def generate(self):
  32. DistributedNode.generate(self)
  33. def disable(self):
  34. DistributedNode.disable(self)
  35. self.stopProcessVisibility()
  36. def delete(self):
  37. DistributedNode.delete(self)
  38. # TODO: when teleporting off an island...
  39. taskMgr.remove(self.taskName("processVisibility"))
  40. def isGridParent(self):
  41. # If this distributed object is a DistributedGrid return 1. 0 by default
  42. return 1
  43. def setCellWidth(self, width):
  44. self.cellWidth = width
  45. def setParentingRules(self, style, rule):
  46. assert self.notify.debug("setParentingRules: style: %s, rule: %s" % (style, rule))
  47. rules = rule.split(self.RuleSeparator)
  48. assert len(rules) == 3
  49. self.style = style
  50. self.startingZone = int(rules[0])
  51. self.gridSize = int(rules[1])
  52. self.viewingRadius = int(rules[2])
  53. # Store the center of the grid
  54. cx = self.cellWidth * self.gridSize/2.0
  55. self.centerPos = Vec3(cx, cx, 0)
  56. if __debug__:
  57. if self.VisualizeGrid:
  58. self.visualizeGrid()
  59. def getCenterPos(self):
  60. return self.centerPos
  61. def handleChildArrive(self, child, zoneId):
  62. DistributedNode.handleChildArrive(self, child, zoneId)
  63. if (zoneId >= self.startingZone):
  64. if not child.gridParent:
  65. child.gridParent = GridParent(child)
  66. child.gridParent.setGridParent(self, zoneId)
  67. elif child.gridParent:
  68. child.gridParent.delete()
  69. child.gridParent = None
  70. def handleChildArriveZone(self, child, zoneId):
  71. DistributedNode.handleChildArrive(self, child, zoneId)
  72. if (zoneId >= self.startingZone):
  73. if not child.gridParent:
  74. child.gridParent = GridParent(child)
  75. child.gridParent.setGridParent(self, zoneId)
  76. elif child.gridParent:
  77. child.gridParent.delete()
  78. child.gridParent = None
  79. def handleChildLeave(self, child, zoneId):
  80. if child.gridParent:
  81. child.gridParent.delete()
  82. child.gridParent = None
  83. @report(types = ['deltaStamp', 'avLocation', 'args'], dConfigParam = ['connector','shipboard'])
  84. def startProcessVisibility(self, avatar):
  85. if not self._onOffState:
  86. # if we've been told that we're OFF, don't try
  87. # to process visibilty
  88. return
  89. assert not self.cr._noNewInterests
  90. if self.cr.noNewInterests():
  91. self.notify.warning(
  92. 'startProcessVisibility(%s): tried to open a new interest during logout'
  93. % self.doId)
  94. return
  95. taskMgr.remove(self.taskName("processVisibility"))
  96. self.acceptOnce(self.cr.StopVisibilityEvent, self.stopProcessVisibility)
  97. self.visAvatar = avatar
  98. self.visZone = None
  99. self.visDirty = True
  100. taskMgr.add(
  101. self.processVisibility, self.taskName("processVisibility"))
  102. self.processVisibility(0)
  103. @report(types = ['deltaStamp', 'avLocation', 'args'], dConfigParam = ['connector','shipboard'])
  104. def stopProcessVisibility(self, clearAll=False, event=None):
  105. self.ignore(self.cr.StopVisibilityEvent)
  106. taskMgr.remove(self.taskName("processVisibility"))
  107. if event is not None:
  108. eventGroup = EventGroup('DistCartesianGrid.stopProcessVis',
  109. doneEvent=event)
  110. if self.gridVisContext is not None:
  111. if event is not None:
  112. removeEvent = eventGroup.newEvent('%s.removeInterest' % self.doId)
  113. else:
  114. removeEvent = None
  115. self.cr.removeInterest(self.gridVisContext, removeEvent)
  116. self.gridVisContext = None
  117. else:
  118. # if we were given an event but we have not interest open,
  119. # just send the event right away
  120. if event is not None:
  121. messenger.send(event)
  122. self.visAvatar = None
  123. self.visZone = None
  124. # sometimes we also need to remove vis avatar from
  125. # my parent if it is also a grid
  126. if (clearAll):
  127. if event is not None:
  128. parentEvent = eventGroup.newEvent('%s.parent.removeInterest' % self.doId)
  129. else:
  130. parentEvent = None
  131. ##HACK BANDAID FOR PVP INSTANCES
  132. if(hasattr(self.cr.doId2do[self.parentId],"worldGrid")):
  133. self.cr.doId2do[self.parentId].worldGrid.stopProcessVisibility(event=parentEvent)
  134. def processVisibility(self, task):
  135. if self.visAvatar == None:
  136. # no avatar to process visibility for
  137. return Task.done
  138. if(self.visAvatar.isDisabled()):
  139. self.visAvatar = None
  140. return Task.done
  141. if self.visAvatar.gameFSM.state == 'Cutscene':
  142. return Task.cont
  143. pos = self.visAvatar.getPos(self)
  144. # Check to make sure our x and y are positive
  145. dx = self.cellWidth * self.gridSize * .5
  146. x = pos[0] + dx
  147. y = pos[1] + dx
  148. col = x // self.cellWidth
  149. row = y // self.cellWidth
  150. assert self.notify.debug(
  151. "processVisibility: %s: avatar pos: %s %s" % (self.doId, x, y))
  152. if (row < 0) or (col < 0) or (row > self.gridSize) or (col > self.gridSize):
  153. assert self.notify.debug("processVisibility: %s: not on the grid" % (self.doId))
  154. # If we are viewingRadius away from this entire grid,
  155. # remove interest in any current visZone we may have
  156. if self.gridVisContext:
  157. self.cr.removeInterest(self.gridVisContext)
  158. self.visZone = None
  159. self.gridVisContext = None
  160. return Task.cont
  161. # Compute which zone we are in
  162. zoneId = int(self.startingZone + ((row * self.gridSize) + col))
  163. assert self.notify.debug("processVisibility: %s: row: %s col: %s zoneId: %s" %
  164. (self.doId, row, col, zoneId))
  165. if (zoneId == self.visZone):
  166. assert self.notify.debug(
  167. "processVisibility: %s: interest did not change" % (self.doId))
  168. if self.visDirty:
  169. messenger.send(self.uniqueName("visibility"))
  170. self.visDirty = False
  171. return Task.cont
  172. else:
  173. assert self.notify.debug(
  174. "processVisibility: %s: new interest" % (self.doId))
  175. self.visZone = zoneId
  176. if not self.gridVisContext:
  177. self.gridVisContext = self.cr.addInterest(
  178. self.getDoId(), self.visZone,
  179. self.uniqueName("visibility"),
  180. event = self.uniqueName("visibility"))
  181. else:
  182. assert self.notify.debug(
  183. "processVisibility: %s: altering interest to zoneId: %s" %
  184. (self.doId, zoneId))
  185. event = None
  186. if self.visDirty:
  187. event = self.uniqueName("visibility")
  188. self.cr.alterInterest(
  189. self.gridVisContext, self.getDoId(), self.visZone,
  190. event = event)
  191. # If the visAvatar is parented to this grid, also do a
  192. # setLocation
  193. parentId = self.visAvatar.parentId
  194. oldZoneId = self.visAvatar.zoneId
  195. assert self.notify.debug(
  196. "processVisibility: %s: parentId: %s oldZoneId: %s" %
  197. (self.doId, parentId, oldZoneId))
  198. if parentId == self.doId:
  199. assert self.notify.debug(
  200. "processVisibility: %s: changing location" %
  201. (self.doId))
  202. messenger.send("avatarZoneChanged", [self.visAvatar, self.doId, zoneId])
  203. #self.handleAvatarZoneChange(self.visAvatar, zoneId)
  204. self.visDirty = False
  205. return Task.cont
  206. # Update our location based on our avatar's position on the grid
  207. # Assumes our position is correct, relative to the grid
  208. def addObjectToGrid(self, av):
  209. assert self.notify.debug("addObjectToGrid %s" % av)
  210. # Get our pos relative to the island grid
  211. pos = av.getPos(self)
  212. # Figure out what zone in that island grid
  213. zoneId = self.getZoneFromXYZ(pos)
  214. # Do the wrtReparenting to the grid node
  215. messenger.send("avatarZoneChanged", [av, self.doId, zoneId])
  216. #self.handleAvatarZoneChange(av, zoneId)
  217. def removeObjectFromGrid(self, av):
  218. assert self.notify.debug("removeObjectFromGrid %s" % av)
  219. # TODO: WHAT LOCATION SHOULD WE SET THIS TO?
  220. #av.reparentTo(hidden)
  221. if (av.getParent().compareTo(self) == 0):
  222. # only detach if object is directly parented
  223. av.detachNode()
  224. #av.b_setLocation(0, 0)
  225. def handleAvatarZoneChange(self, av, zoneId):
  226. assert self.notify.debug("handleAvatarZoneChange(%s, %s)" % (av.doId, zoneId))
  227. # This method can be overridden by derived classes that
  228. # want to do some special management when the avatar changes
  229. # zones.
  230. # Make sure this is a valid zone
  231. if not self.isValidZone(zoneId):
  232. assert self.notify.warning("handleAvatarZoneChange: not a valid zone (%s)" % zoneId)
  233. return
  234. # Set the location on the server
  235. av.b_setLocation(self.doId, zoneId)
  236. def turnOff(self):
  237. self._onOffState = False
  238. self.stopProcessVisibility()
  239. def turnOn(self, av = None):
  240. self._onOffState = True
  241. if av:
  242. self.startProcessVisibility(av)
  243. ##################################################
  244. # Visualization Tools
  245. ##################################################
  246. if __debug__:
  247. def initializeGridLines(self):
  248. # Grid Lines
  249. self.gridColor = VBase4(0.4 + randFloat(0.4),
  250. 0.4 + randFloat(0.4),
  251. 0.4 + randFloat(0.4),
  252. 1)
  253. # A Dark version of the grid color
  254. color = self.gridColor * 0.5
  255. color.setW(1)
  256. self.lines = self.attachNewNode('gridLines')
  257. self.minorLines = LineNodePath(self.lines)
  258. self.minorLines.lineNode.setName('minorLines')
  259. self.minorLines.setColor(color)
  260. self.minorLines.setThickness(1)
  261. self.majorLines = LineNodePath(self.lines)
  262. self.majorLines.lineNode.setName('majorLines')
  263. self.majorLines.setColor(color)
  264. self.majorLines.setThickness(5)
  265. self.centerLines = LineNodePath(self.lines)
  266. self.centerLines.lineNode.setName('centerLines')
  267. self.centerLines.setColor(VBase4(1, 0, 0, 0))
  268. self.centerLines.setThickness(3)
  269. # Load up grid parts to initialize grid object
  270. # Polygon used to mark grid plane
  271. # self.gridBack = loader.loadModel('models/misc/gridBack')
  272. # self.gridBack.reparentTo(self)
  273. # self.gridBack.setColor(0.2, 0.2, 0.2, 0.5)
  274. self.cellLabelParent = None
  275. self.markerParent = None
  276. self.haveGridLines = 1
  277. def updateGrid(self):
  278. # Update grid lines based upon current grid spacing and grid size
  279. # First reset existing grid lines
  280. self.minorLines.reset()
  281. self.majorLines.reset()
  282. self.centerLines.reset()
  283. # Now redraw lines
  284. numLines = self.gridSize
  285. scaledSize = numLines * self.cellWidth / 2.0
  286. center = self.centerLines
  287. minor = self.minorLines
  288. major = self.majorLines
  289. cw = self.cellWidth
  290. dx = cw * self.gridSize * .5
  291. for i in range(numLines+1):
  292. icw = i * cw - dx
  293. if i == numLines/2:
  294. center.moveTo(icw, -scaledSize, GRID_Z_OFFSET)
  295. center.drawTo(icw, scaledSize, GRID_Z_OFFSET)
  296. center.moveTo(-scaledSize, icw, GRID_Z_OFFSET)
  297. center.drawTo(scaledSize, icw, GRID_Z_OFFSET)
  298. else:
  299. if (i % 5) == 0:
  300. major.moveTo(icw, -scaledSize, GRID_Z_OFFSET)
  301. major.drawTo(icw, scaledSize, GRID_Z_OFFSET)
  302. major.moveTo(-scaledSize, icw, GRID_Z_OFFSET)
  303. major.drawTo(scaledSize, icw, GRID_Z_OFFSET)
  304. else:
  305. minor.moveTo(icw, -scaledSize, GRID_Z_OFFSET)
  306. minor.drawTo(icw, scaledSize, GRID_Z_OFFSET)
  307. minor.moveTo(-scaledSize, icw, GRID_Z_OFFSET)
  308. minor.drawTo(scaledSize, icw, GRID_Z_OFFSET)
  309. center.create()
  310. minor.create()
  311. major.create()
  312. # self.gridBack.setScale(scaledSize)
  313. self.labelCells()
  314. def labelCells(self):
  315. if self.cellLabelParent:
  316. self.cellLabelParent.removeNode()
  317. self.cellLabelParent = self.attachNewNode('cellLabels')
  318. cw = self.cellWidth
  319. scale = cw / 10.0
  320. dx = cw * self.gridSize * .5
  321. font = DirectGuiGlobals.getDefaultFont()
  322. color = self.gridColor
  323. for i in range(self.gridSize):
  324. for j in range(self.gridSize):
  325. zoneId = self.startingZone + ((j * self.gridSize) + i)
  326. zoneStr = str(zoneId)
  327. textNode = TextNode(zoneStr)
  328. textNode.setText(zoneStr)
  329. textNode.setFont(font)
  330. textNode.setTextColor(color)
  331. textNode.setAlign(TextNode.ACenter)
  332. genTextNode = textNode.generate()
  333. textNodePath = self.cellLabelParent.attachNewNode(genTextNode)
  334. # Place the text node in the center of the cell
  335. textNodePath.setPosHprScale((i * cw - dx) + (cw * 0.5), # x
  336. (j * cw - dx) + (cw * 0.5), # y
  337. GRID_Z_OFFSET+3.0, # z
  338. # Lay them down flat
  339. 0, -90, 0, # hpr
  340. scale, scale, scale)
  341. self.cellLabelParent.flattenLight()
  342. def markCells(self):
  343. if self.markerParent:
  344. self.markerParent.removeNode()
  345. self.markerParent = self.attachNewNode('markers')
  346. self.cellMarkers = []
  347. dx = self.cellWidth * self.gridSize * .5
  348. for i in range(self.gridSize):
  349. for j in range(self.gridSize):
  350. marker = loader.loadModel("models/misc/smiley")
  351. marker.reparentTo(self.markerParent)
  352. marker.setPos(i * self.cellWidth - dx,
  353. j * self.cellWidth - dx,
  354. GRID_Z_OFFSET + 1.0)
  355. marker.setScale(5)
  356. self.cellMarkers.append(marker)
  357. def unmarkCells(self):
  358. if self.markerParent:
  359. self.markerParent.removeNode()
  360. self.markerParent = None
  361. def visualizeGrid(self):
  362. if not self.haveGridLines:
  363. self.initializeGridLines()
  364. self.updateGrid()
  365. def setWorldContext(self, worldContext):
  366. pass
  367. def clearWorldContext(self, event = None):
  368. pass