DistributedCartesianGrid.py 17 KB

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