DistributedCartesianGrid.py 17 KB

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