from pandac.PandaModules import * from direct.interval.IntervalGlobal import * from direct.distributed.DistributedNode import DistributedNode from direct.task import Task from direct.gui import DirectGuiGlobals from direct.showbase.EventGroup import EventGroup from direct.showbase.PythonUtil import report from direct.distributed.GridParent import GridParent if __debug__: # For grid drawing from direct.directtools.DirectGeometry import * from direct.showbase.PythonUtil import randFloat from CartesianGridBase import CartesianGridBase # increase this number if you want to visualize the grid lines # above water level GRID_Z_OFFSET = 0.0 class DistributedCartesianGrid(DistributedNode, CartesianGridBase): notify = directNotify.newCategory("DistributedCartesianGrid") notify.setDebug(0) VisualizeGrid = config.GetBool("visualize-cartesian-grid", 0) RuleSeparator = ":" def __init__(self, cr): DistributedNode.__init__(self, cr) # Let the derived classes instantiate the NodePath self.visAvatar = None self.gridVisContext = None # Do we have grid lines visualized? self._onOffState = False if __debug__: self.haveGridLines = 0 def generate(self): DistributedNode.generate(self) def disable(self): DistributedNode.disable(self) self.stopProcessVisibility() def delete(self): DistributedNode.delete(self) # TODO: when teleporting off an island... taskMgr.remove(self.taskName("processVisibility")) def isGridParent(self): # If this distributed object is a DistributedGrid return 1. 0 by default return 1 def setCellWidth(self, width): self.cellWidth = width def setParentingRules(self, style, rule): assert self.notify.debug("setParentingRules: style: %s, rule: %s" % (style, rule)) rules = rule.split(self.RuleSeparator) assert len(rules) == 3 self.style = style self.startingZone = int(rules[0]) self.gridSize = int(rules[1]) self.viewingRadius = int(rules[2]) # Store the center of the grid cx = self.cellWidth * self.gridSize/2.0 self.centerPos = Vec3(cx, cx, 0) if __debug__: if self.VisualizeGrid: self.visualizeGrid() def getCenterPos(self): return self.centerPos def handleChildArrive(self, child, zoneId): DistributedNode.handleChildArrive(self, child, zoneId) if (zoneId >= self.startingZone): if not child.gridParent: child.gridParent = GridParent(child) child.gridParent.setGridParent(self, zoneId) elif child.gridParent: child.gridParent.delete() child.gridParent = None def handleChildArriveZone(self, child, zoneId): DistributedNode.handleChildArrive(self, child, zoneId) if (zoneId >= self.startingZone): if not child.gridParent: child.gridParent = GridParent(child) child.gridParent.setGridParent(self, zoneId) elif child.gridParent: child.gridParent.delete() child.gridParent = None def handleChildLeave(self, child, zoneId): if child.gridParent: child.gridParent.delete() child.gridParent = None @report(types = ['deltaStamp', 'avLocation', 'args'], dConfigParam = ['connector','shipboard']) def startProcessVisibility(self, avatar): if not self._onOffState: # if we've been told that we're OFF, don't try # to process visibilty return assert not self.cr._noNewInterests if self.cr.noNewInterests(): self.notify.warning( 'startProcessVisibility(%s): tried to open a new interest during logout' % self.doId) return taskMgr.remove(self.taskName("processVisibility")) self.acceptOnce(self.cr.StopVisibilityEvent, self.stopProcessVisibility) self.visAvatar = avatar self.visZone = None self.visDirty = True taskMgr.add( self.processVisibility, self.taskName("processVisibility")) self.processVisibility(0) @report(types = ['deltaStamp', 'avLocation', 'args'], dConfigParam = ['connector','shipboard']) def stopProcessVisibility(self, clearAll=False, event=None): self.ignore(self.cr.StopVisibilityEvent) taskMgr.remove(self.taskName("processVisibility")) if event is not None: eventGroup = EventGroup('DistCartesianGrid.stopProcessVis', doneEvent=event) if self.gridVisContext is not None: if event is not None: removeEvent = eventGroup.newEvent('%s.removeInterest' % self.doId) else: removeEvent = None self.cr.removeInterest(self.gridVisContext, removeEvent) self.gridVisContext = None else: # if we were given an event but we have not interest open, # just send the event right away if event is not None: messenger.send(event) self.visAvatar = None self.visZone = None # sometimes we also need to remove vis avatar from # my parent if it is also a grid if (clearAll): if event is not None: parentEvent = eventGroup.newEvent('%s.parent.removeInterest' % self.doId) else: parentEvent = None ##HACK BANDAID FOR PVP INSTANCES if(hasattr(self.cr.doId2do[self.parentId],"worldGrid")): self.cr.doId2do[self.parentId].worldGrid.stopProcessVisibility(event=parentEvent) def processVisibility(self, task): if self.visAvatar == None: # no avatar to process visibility for return Task.done if(self.visAvatar.isDisabled()): self.visAvatar = None return Task.done if self.visAvatar.gameFSM.state == 'Cutscene': return Task.cont pos = self.visAvatar.getPos(self) # Check to make sure our x and y are positive dx = self.cellWidth * self.gridSize * .5 x = pos[0] + dx y = pos[1] + dx col = x // self.cellWidth row = y // self.cellWidth assert self.notify.debug( "processVisibility: %s: avatar pos: %s %s" % (self.doId, x, y)) if (row < 0) or (col < 0) or (row > self.gridSize) or (col > self.gridSize): assert self.notify.debug("processVisibility: %s: not on the grid" % (self.doId)) # If we are viewingRadius away from this entire grid, # remove interest in any current visZone we may have if self.gridVisContext: self.cr.removeInterest(self.gridVisContext) self.visZone = None self.gridVisContext = None return Task.cont # Compute which zone we are in zoneId = int(self.startingZone + ((row * self.gridSize) + col)) assert self.notify.debug("processVisibility: %s: row: %s col: %s zoneId: %s" % (self.doId, row, col, zoneId)) if (zoneId == self.visZone): assert self.notify.debug( "processVisibility: %s: interest did not change" % (self.doId)) if self.visDirty: messenger.send(self.uniqueName("visibility")) self.visDirty = False return Task.cont else: assert self.notify.debug( "processVisibility: %s: new interest" % (self.doId)) self.visZone = zoneId if not self.gridVisContext: self.gridVisContext = self.cr.addInterest( self.getDoId(), self.visZone, self.uniqueName("visibility"), event = self.uniqueName("visibility")) else: assert self.notify.debug( "processVisibility: %s: altering interest to zoneId: %s" % (self.doId, zoneId)) event = None if self.visDirty: event = self.uniqueName("visibility") self.cr.alterInterest( self.gridVisContext, self.getDoId(), self.visZone, event = event) # If the visAvatar is parented to this grid, also do a # setLocation parentId = self.visAvatar.parentId oldZoneId = self.visAvatar.zoneId assert self.notify.debug( "processVisibility: %s: parentId: %s oldZoneId: %s" % (self.doId, parentId, oldZoneId)) if parentId == self.doId: assert self.notify.debug( "processVisibility: %s: changing location" % (self.doId)) messenger.send("avatarZoneChanged", [self.visAvatar, self.doId, zoneId]) #self.handleAvatarZoneChange(self.visAvatar, zoneId) self.visDirty = False return Task.cont # Update our location based on our avatar's position on the grid # Assumes our position is correct, relative to the grid def addObjectToGrid(self, av): assert self.notify.debug("addObjectToGrid %s" % av) # Get our pos relative to the island grid pos = av.getPos(self) # Figure out what zone in that island grid zoneId = self.getZoneFromXYZ(pos) # Do the wrtReparenting to the grid node messenger.send("avatarZoneChanged", [av, self.doId, zoneId]) #self.handleAvatarZoneChange(av, zoneId) def removeObjectFromGrid(self, av): assert self.notify.debug("removeObjectFromGrid %s" % av) # TODO: WHAT LOCATION SHOULD WE SET THIS TO? #av.reparentTo(hidden) if (av.getParent().compareTo(self) == 0): # only detach if object is directly parented av.detachNode() #av.b_setLocation(0, 0) def handleAvatarZoneChange(self, av, zoneId): assert self.notify.debug("handleAvatarZoneChange(%s, %s)" % (av.doId, zoneId)) # This method can be overridden by derived classes that # want to do some special management when the avatar changes # zones. # Make sure this is a valid zone if not self.isValidZone(zoneId): assert self.notify.warning("handleAvatarZoneChange: not a valid zone (%s)" % zoneId) return # Set the location on the server av.b_setLocation(self.doId, zoneId) def turnOff(self): self._onOffState = False self.stopProcessVisibility() def turnOn(self, av = None): self._onOffState = True if av: self.startProcessVisibility(av) ################################################## # Visualization Tools ################################################## if __debug__: def initializeGridLines(self): # Grid Lines self.gridColor = VBase4(0.4 + randFloat(0.4), 0.4 + randFloat(0.4), 0.4 + randFloat(0.4), 1) # A Dark version of the grid color color = self.gridColor * 0.5 color.setW(1) self.lines = self.attachNewNode('gridLines') self.minorLines = LineNodePath(self.lines) self.minorLines.lineNode.setName('minorLines') self.minorLines.setColor(color) self.minorLines.setThickness(1) self.majorLines = LineNodePath(self.lines) self.majorLines.lineNode.setName('majorLines') self.majorLines.setColor(color) self.majorLines.setThickness(5) self.centerLines = LineNodePath(self.lines) self.centerLines.lineNode.setName('centerLines') self.centerLines.setColor(VBase4(1, 0, 0, 0)) self.centerLines.setThickness(3) # Load up grid parts to initialize grid object # Polygon used to mark grid plane # self.gridBack = loader.loadModel('models/misc/gridBack') # self.gridBack.reparentTo(self) # self.gridBack.setColor(0.2, 0.2, 0.2, 0.5) self.cellLabelParent = None self.markerParent = None self.haveGridLines = 1 def updateGrid(self): # Update grid lines based upon current grid spacing and grid size # First reset existing grid lines self.minorLines.reset() self.majorLines.reset() self.centerLines.reset() # Now redraw lines numLines = self.gridSize scaledSize = numLines * self.cellWidth / 2.0 center = self.centerLines minor = self.minorLines major = self.majorLines cw = self.cellWidth dx = cw * self.gridSize * .5 for i in range(numLines+1): icw = i * cw - dx if i == numLines/2: center.moveTo(icw, -scaledSize, GRID_Z_OFFSET) center.drawTo(icw, scaledSize, GRID_Z_OFFSET) center.moveTo(-scaledSize, icw, GRID_Z_OFFSET) center.drawTo(scaledSize, icw, GRID_Z_OFFSET) else: if (i % 5) == 0: major.moveTo(icw, -scaledSize, GRID_Z_OFFSET) major.drawTo(icw, scaledSize, GRID_Z_OFFSET) major.moveTo(-scaledSize, icw, GRID_Z_OFFSET) major.drawTo(scaledSize, icw, GRID_Z_OFFSET) else: minor.moveTo(icw, -scaledSize, GRID_Z_OFFSET) minor.drawTo(icw, scaledSize, GRID_Z_OFFSET) minor.moveTo(-scaledSize, icw, GRID_Z_OFFSET) minor.drawTo(scaledSize, icw, GRID_Z_OFFSET) center.create() minor.create() major.create() # self.gridBack.setScale(scaledSize) self.labelCells() def labelCells(self): if self.cellLabelParent: self.cellLabelParent.removeNode() self.cellLabelParent = self.attachNewNode('cellLabels') cw = self.cellWidth scale = cw / 10.0 dx = cw * self.gridSize * .5 font = DirectGuiGlobals.getDefaultFont() color = self.gridColor for i in range(self.gridSize): for j in range(self.gridSize): zoneId = self.startingZone + ((j * self.gridSize) + i) zoneStr = str(zoneId) textNode = TextNode(zoneStr) textNode.setText(zoneStr) textNode.setFont(font) textNode.setTextColor(color) textNode.setAlign(TextNode.ACenter) genTextNode = textNode.generate() textNodePath = self.cellLabelParent.attachNewNode(genTextNode) # Place the text node in the center of the cell textNodePath.setPosHprScale((i * cw - dx) + (cw * 0.5), # x (j * cw - dx) + (cw * 0.5), # y GRID_Z_OFFSET+3.0, # z # Lay them down flat 0, -90, 0, # hpr scale, scale, scale) self.cellLabelParent.flattenLight() def markCells(self): if self.markerParent: self.markerParent.removeNode() self.markerParent = self.attachNewNode('markers') self.cellMarkers = [] dx = self.cellWidth * self.gridSize * .5 for i in range(self.gridSize): for j in range(self.gridSize): marker = loader.loadModel("models/misc/smiley") marker.reparentTo(self.markerParent) marker.setPos(i * self.cellWidth - dx, j * self.cellWidth - dx, GRID_Z_OFFSET + 1.0) marker.setScale(5) self.cellMarkers.append(marker) def unmarkCells(self): if self.markerParent: self.markerParent.removeNode() self.markerParent = None def visualizeGrid(self): if not self.haveGridLines: self.initializeGridLines() self.updateGrid() def setWorldContext(self, worldContext): pass def clearWorldContext(self, event = None): pass