Example #1
0
    def leftClick(self):
        self.mouse1Down = True

        #Collision traversal
        pickerNode = CollisionNode('mouseRay')
        pickerNP = base.camera.attachNewNode(pickerNode)
        pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
        pickerRay = CollisionRay()
        pickerNode.addSolid(pickerRay)
        myTraverser = CollisionTraverser()
        myHandler = CollisionHandlerQueue()
        myTraverser.addCollider(pickerNP, myHandler)

        if base.mouseWatcherNode.hasMouse():

            mpos = base.mouseWatcherNode.getMouse()
            pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())

            myTraverser.traverse(render)
            # Assume for simplicity's sake that myHandler is a CollisionHandlerQueue.
            if myHandler.getNumEntries() > 0:
                # This is so we get the closest object
                myHandler.sortEntries()
                pickedObj = myHandler.getEntry(0).getIntoNodePath()
                objTag = pickedObj.findNetTag('mouseCollisionTag').getTag(
                    'mouseCollisionTag')
                if objTag and len(objTag) > 0:
                    messenger.send('object_click', [objTag])
        pickerNP.remove()
Example #2
0
class RepairMousePicker:
    def __init__(self):
        self.pickerNode = CollisionNode('RepairMousePicker.pickerNode')
        self.pickerNP = base.cam2d.attachNewNode(self.pickerNode)
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.collisionTraverser = CollisionTraverser()
        self.collisionHandler = CollisionHandlerQueue()
        self.collisionTraverser.addCollider(self.pickerNP,
                                            self.collisionHandler)
        self.clearCollisionMask()
        self.orthographic = True

    def destroy(self):
        del self.pickerNode
        self.pickerNP.removeNode()
        del self.pickerNP
        del self.pickerRay
        del self.collisionTraverser
        del self.collisionHandler

    def setOrthographic(self, ortho):
        self.orthographic = ortho

    def setCollisionMask(self, mask):
        self.pickerNode.setFromCollideMask(mask)

    def clearCollisionMask(self):
        self.pickerNode.setFromCollideMask(BitMask32.allOff())

    def getCollisions(self, traverseRoot, useIntoNodePaths=False):
        if not base.mouseWatcherNode.hasMouse():
            return []

        mpos = base.mouseWatcherNode.getMouse()
        if self.orthographic:
            self.pickerRay.setFromLens(base.cam2d.node(), 0, 0)
            self.pickerNP.setPos(mpos.getX(), 0.0, mpos.getY())
        else:
            self.pickerRay.setFromLens(base.cam2d.node(), mpos.getX(),
                                       mpos.getY())
            self.pickerNP.setPos(0.0, 0.0, 0.0)
        self.collisionTraverser.traverse(traverseRoot)
        pickedObjects = []
        if useIntoNodePaths:
            for i in range(self.collisionHandler.getNumEntries()):
                pickedObjects.append(
                    self.collisionHandler.getEntry(i).getIntoNodePath())

        else:
            for i in range(self.collisionHandler.getNumEntries()):
                pickedObjects.append(self.collisionHandler.getEntry(i))

        return pickedObjects
Example #3
0
class Selector(object):
    '''A Selector listens for mouse clicks and then runs select. Select then
       broadcasts the selected tag (if there is one)'''
    def __init__(self):
        ''' Should the traverser be shared? '''

        LOG.debug("[Selector] Initializing")

        # The collision traverser does the checking of solids for collisions
        self.cTrav = CollisionTraverser()

        # The collision handler queue is a simple handler that records all
        # detected collisions during traversal
        self.cHandler = CollisionHandlerQueue()

        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.cTrav.addCollider(self.pickerNP, self.cHandler)

        # Start listening to clicks
        self.resume()

    def select(self, event):
        LOG.debug("[Selector] Selecting ")
        if base.mouseWatcherNode.hasMouse():
            mpos = base.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
            self.cTrav.traverse(render)  # TODO - change this to a lower node
            if self.cHandler.getNumEntries() > 0:
                #LOG.debug("[Selector] Entries=%d"%self.cHandler.getNumEntries())
                self.cHandler.sortEntries()
                selectionNP = self.cHandler.getEntry(0).getIntoNodePath()
                selection = selectionNP.findNetTag('SelectorTag').getTag(
                    'SelectorTag')
                if selection is not '':
                    LOG.debug("[Selector] Collision with %s" % selection)
                    Event.Dispatcher().broadcast(
                        Event.Event('E_EntitySelect', src=self,
                                    data=selection))
                else:
                    LOG.debug("[Selector] No collision")
                    #Event.Dispatcher().broadcast(Event.Event('E_EntityUnSelect', src=self, data=selection))

    def pause(self):
        Event.Dispatcher().unregister(self, 'E_Mouse_1')

    def resume(self):
        print("unpausing selector")
        Event.Dispatcher().register(self, 'E_Mouse_1', self.select)
Example #4
0
class Gui3D:
    def __init__(self, game_data, gfx_manager):
        self.game_data = game_data
        self.gui_traverser = CollisionTraverser()
        self.handler = CollisionHandlerQueue()
        self.selectable_objects = {}
        for cid, model in gfx_manager.character_models.items():
            new_collision_node = CollisionNode('person_' + str(cid))
            new_collision_node.addSolid(
                CollisionTube(0, 0, 0.5, 0, 0, 1.5, 0.5))
            new_collision_nodepath = model.attachNewNode(new_collision_node)
            new_collision_nodepath.setTag("type", "character")
            new_collision_nodepath.setTag("id", str(cid))

        picker_node = CollisionNode('mouseRay')
        picker_np = camera.attachNewNode(picker_node)
        self.picker_ray = CollisionRay()
        picker_node.addSolid(self.picker_ray)
        self.gui_traverser.addCollider(picker_np, self.handler)
        self.floor = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0)))
        self.floor_np = render.attachNewNode(CollisionNode('floor'))
        self.floor_np.setTag("type", "ground")
        self.floor_np.node().addSolid(self.floor)

    def mouse_click(self):
        if base.mouseWatcherNode.hasMouse():
            mpos = base.mouseWatcherNode.getMouse()
            self.picker_ray.setFromLens(base.camNode, mpos.getX(), mpos.getY())
            self.gui_traverser.traverse(render)
            num_entries = self.handler.getNumEntries()
            if num_entries > 0:
                self.handler.sortEntries()
                entry = self.handler.getEntry(0)
                selected = entry.getIntoNodePath()
                selected_type = selected.getTag("type")
                if selected_type == "character":
                    self.game_data.select_character(int(selected.getTag("id")))
                elif selected_type == "ground":
                    self.game_data.click_point(entry.getSurfacePoint(render))
Example #5
0
class World(DirectObject):
    #class World, extends DirectObject, builds the world to play the game

###################### INITIALIZATIONS #########################################
    def __init__(self):

        mySplashScreen = SplashScreen()
        mySplashScreen.loading()
        mySplashScreen.introduction()
        self.promptMode()

        self.turnWallNotification()

        ##### Creating Scene #####
        self.createBackground()
        self.loadWallModel()
        self.loadBallModel()
        self.setCamera()
        self.createLighting()

        ##### Create Controls #####
        self.createKeyControls()
        self.keyMap = {"left":0, "right":0, "forward":0, "backward":0, "drop":0}

        ##### Task Manager #####
        timer = 0.2
        taskMgr.doMethodLater(timer, self.traverseTask, "tsk_traverse")
            #scans for collisions every 0.2 seconds
        taskMgr.add(self.move,"moveTask")
            #constant smooth movement

        ##### Collisions #####
        self.createBallColliderModel()
        self.disableForwardMovement = False
        self.disableBackwardMovement = False
        self.disableLeftMovement = False
        self.disableRightMovement = False

        ##### Game state variables #####
        self.isMoving = False
        self.isDropping = False 
        self.camAngle = math.pi/2
        self.direction = "W" #constant; does not change with relativity
        self.drop = False

        self.levelHeight = 2.1
        self.level = 0
        self.maxLevel = 6
        self.currentHeight = 13.302
        self.cameraHeight = 0.2
        self.mode = None
        self.timer = ""

        ##### Views #####
        self.xray_mode = False
        self.collision_mode = False
        self.wireframe = False

        ##### On-Screen Text #####
        self.title = addTitle("aMAZEing")
        self.instructions = OnscreenText(text="[ i ]: Toggle Instructions", 
                style=1, fg=(0, 0, 0, 1), pos=(1.3, 0.95), 
                align=TextNode.ARight, scale=0.05)
        self.instr = []
        self.messages = []
        self.levelText = OnscreenText(text= "Level = " + str(self.level), 
                style=1, fg=(0, 0, 0, 1), pos=(-1.3, -0.95), 
                align=TextNode.ALeft, scale=0.07)
        self.directionText = OnscreenText(text="Direction = " + self.direction,
                style=1, fg=(0, 0, 0, 1), pos=(-1.3, -0.85),
                align=TextNode.ALeft, scale=0.07)

        self.timerText = OnscreenText(text= self.timer, 
                style=1, fg=(1, 1, 1, 1), pos=(1.3, 0.85), 
                align=TextNode.ARight, scale=0.07)
    
    def setKey(self, key, value):
        #records the state of the arrow keys
        self.keyMap[key] = value

    ###################### Onscreen Text #######################################

    def postInstructions(self):
        #posts the instructions onto the screen

        inst1 = addInstructions(0.95, "[ESC]: Quit")
        self.instr.append(inst1)
        inst2 = addInstructions(0.90,  "[Left Arrow]: Turn Left")
        self.instr.append(inst2)
                               
        inst3 = addInstructions(0.85, "[Right Arrow]: Turn Right")
        self.instr.append(inst3)
                                
        inst4 = addInstructions(0.80, "[Up Arrow]: Move Ball Forward")
        self.instr.append(inst4)
                                
        inst5 = addInstructions(0.75,  "[Down Arrow]: Move Ball Backwards")
        self.instr.append(inst5)
                               
        inst6 = addInstructions(0.70,
                            "[Space]: Drop Levels (if level drop is availale)")
        self.instr.append(inst6)
                               
        inst7 = addInstructions(0.60,  "[x]: Toggle XRay Mode")
        self.instr.append(inst7)
                               
        inst8 = addInstructions(0.55, "[c]: Toggle Collision Mode")
        self.instr.append(inst8)
                                
        inst9 = addInstructions(0.50, "[z]: Toggle Wireframe")
        self.instr.append(inst9)

        inst10 = OnscreenText(text='''Hello!
        Welcome to aMAZEing!
        You are this sphere,
        and your goal is to find the exit of the maze! Each level
        of the maze has a hole you can drop through, to move on to the
        next level. This maze has six levels and each maze is a 12x12.
        If you chose timer mode, you have 5 minutes to finish the maze,
        or else you lose.
        Good luck! You're aMAZEing :)''', style = 1, 
                fg=(0, 0, 0, 1), pos=(0, -.1), align=TextNode.ACenter, scale=0.07)
        self.instr.append(inst10)

    def deleteInstructions(self):
        #deletes onscreen instructions
        for instr in self.instr:
            instr.destroy()

    def addNotification(self, txt):
        #adds a notification to the screen
        y = 0.9
        tex = OnscreenText(text=txt, style=1, fg= (0, 0, 0, 1), pos=(0, y))
        self.messages.append(tex)

    def deleteNotifications(self):
        #deletes all on-screen notifications
        for msg in self.messages:
            msg.destroy()

    def updateLevelText(self):
        #updates the level text
        self.levelText.destroy()

        levelTextPos = (-1.3, -0.95)
        levelScale = 0.07

        self.levelText = OnscreenText(text= "Level = " + str(self.level), 
                style=1, fg=(0, 0, 0, 1), pos=levelTextPos, 
                align=TextNode.ALeft, scale=levelScale)

    def updateDirectionText(self):
        #updates the direction text on the screen
        self.directionText.destroy()

        directionTextPos = (-1.3, -0.85)
        directionScale = 0.07

        self.directionText = OnscreenText(text="Direction = " + self.direction,
                style=1, fg=(0, 0, 0, 1), pos=directionTextPos,
                align=TextNode.ALeft, scale=directionScale)

    def updateTimerText(self):
        #updates timer on screen
        self.timerText.destroy()

        timerTextPos = (1.3, 0.85)
        timerScale = 0.07

        if self.mode == "timer":
            self.timerText = OnscreenText(text= self.timer, 
                style=1, fg=(1, 1, 1, 1), pos=timerTextPos, 
                align=TextNode.ARight, scale=timerScale)

    def turnWallNotification(self):
        #give a notification sequence at the beginning
        notificationSeq = Sequence()
        notificationSeq.append(Func(addNotification,"""
        If you just see a blank color,
        it means you are facing a wall :)"""))
        notificationSeq.append(Wait(8))
        notificationSeq.append(Func(deleteNotifications))
        notificationSeq.start()

    def promptMode(self):
        #prompts for the mode
        modeScreen = SplashScreen()
        modeScreen.mode()

    def setMode(self, mode):
        #sets the mode of the game
        self.mode = mode
        
        if self.mode == "timer":
            self.setTimer()

    ###################### Initialization Helper Functions #####################

    def createBackground(self):
        #black feautureless space
        base.win.setClearColor(Vec4(0,0,0,1))

    def loadWallModel(self):
        #loads the wall model (the maze) 
        wallScale = 0.3
        wallModelName = self.randomWallModel()
            #randomly select a maze

        self.wallModel = loader.loadModel(wallModelName)
        self.wallModel.setScale(wallScale)
        self.wallModel.setPos(0, 0, 0)
        self.wallModel.setCollideMask(BitMask32.allOff())
        self.wallModel.reparentTo(render)

        ### Setting Texture ###
        texScale = 0.08
        self.wallModel.setTexGen(TextureStage.getDefault(),
                                   TexGenAttrib.MWorldNormal)
        self.wallModel.setTexProjector(TextureStage.getDefault(),
                                         render, self.wallModel)
        self.wallModel.setTexScale(TextureStage.getDefault(), texScale)
        tex = loader.load3DTexture('/Users/jianwei/Documents/School/Freshman/Semester1/15-112/TERMPROJECT/Project/wallTex/wallTex_#.png')
        self.wallModel.setTexture(tex)

        #creating visual geometry collision
        self.wallModel.setCollideMask(BitMask32.bit(0))

    def randomWallModel(self):
        #generates a random wall in the library of mazes that were 
        #randomly generated by the Blender script "mazeGenerator"
        #and exported to this computer
        numMazes = 10

        name = str(random.randint(0, numMazes))
            #randomly selects a number saved in the computer

        path = "/Users/jianwei/Documents/School/Freshman/Semester1/15-112/TERMPROJECT/Project/mazeModels/maze"

        path += name 

        return path
        
    def loadBallModel(self):
        #loads the character, a ball model

        #ballModelStartPos = (-8, -8, 0.701) #THIS IS THE END
        ballModelStartPos = (8, 8, 13.301) #level 0 
        ballScale = 0.01
        self.ballModel = loader.loadModel("/Users/jianwei/Documents/School/Freshman/Semester1/15-112/TERMPROJECT/Project/ball")
        self.ballModel.reparentTo(render)
        self.ballModel.setScale(ballScale)
        self.ballModel.setPos(ballModelStartPos)


        ### Setting ball texture ###
        texScale = 0.08
        self.ballModel.setTexGen(TextureStage.getDefault(),
                                   TexGenAttrib.MWorldPosition)
        self.ballModel.setTexProjector(TextureStage.getDefault(), 
                                         render, self.ballModel)
        self.ballModel.setTexScale(TextureStage.getDefault(), texScale)
        tex = loader.load3DTexture('/Users/jianwei/Documents/School/Freshman/Semester1/15-112/TERMPROJECT/Project/ballTex/ballTex_#.png')
        self.ballModel.setTexture(tex)

    def setCamera(self):
        #sets up the initial camera location
        #camera will follow the sphere 
        followLength = 2
        camHeight = 0.2

        base.disableMouse()
        base.camera.setPos(self.ballModel.getX(),
                                self.ballModel.getY() - followLength,
                                self.ballModel.getZ() + camHeight)
        base.camLens.setNear(0.4)

        #creates a floater object - will look at the floater object 
        #above the sphere, so you can get a better view
        self.floater = NodePath(PandaNode("floater"))
        self.floater.reparentTo(render)

    def createKeyControls(self):
        #creates the controllers for the keys
        #event handler
        #describes what each key does when pressed and unpressed

        self.accept("escape", sys.exit)

        self.accept("arrow_left", self.turnLeft)
        self.accept("arrow_right", self.turnRight)
        self.accept("arrow_up", self.setKey, ["forward",1])
        self.accept("arrow_down", self.setKey, ["backward",1])
        self.accept("space", self.nowDropping)

        #unpressed event handlers
        self.accept("arrow_left-up", self.setKey, ["left",0])
        self.accept("arrow_right-up", self.setKey, ["right",0])
        self.accept("arrow_up-up", self.setKey, ["forward",0])
        self.accept("arrow_down-up", self.setKey, ["backward",0])
        self.accept("space_up", self.setKey, ["drop", 0])

        #views
        self.accept('x', self.toggle_xray_mode)
        self.accept('c', self.toggle_collision_mode)
        self.accept('z', self.toggle_wireframe)

        #information
        self.accept('i', self.postInstructions)
        self.accept('i-up', self.deleteInstructions)

        #restart
        self.accept('r', self.restart)

        #modes
        self.accept("t", self.setMode, ["timer"])
        self.accept("m", self.setMode, ["marathon"])

    def createBallColliderModel(self):
        #creates the collider sphere around the ball
        cSphereRad = 9.9
        self.cTrav = CollisionTraverser() #moves over all possible collisions

        self.ballModelSphere = CollisionSphere(0, 0, 0, cSphereRad)
            #collision mesh around ball is a simple sphere
        self.ballModelCol = CollisionNode('ballModelSphere')
        self.ballModelCol.addSolid(self.ballModelSphere)
        self.ballModelCol.setFromCollideMask(BitMask32.bit(0))
        self.ballModelCol.setIntoCollideMask(BitMask32.allOff())
        self.ballModelColNp = self.ballModel.attachNewNode(self.ballModelCol)
        self.ballModelGroundHandler = CollisionHandlerQueue()
            #collision handler queue stores all collision points
        self.cTrav.addCollider(self.ballModelColNp, self.ballModelGroundHandler)

    def createLighting(self):
        #creates lighting for the scene
        aLightVal = 0.3
        dLightVal1 = -5
        dLightVal2 = 5

        #set up the ambient light
        ambientLight = AmbientLight("ambientLight")
        ambientLight.setColor(Vec4(aLightVal, aLightVal, aLightVal, 1))
        ambientLight1 = AmbientLight("ambientLight1")
        ambientLight1.setColor(Vec4(aLightVal, aLightVal, aLightVal, 1))
        ambientLight2 = AmbientLight("ambientLight2")
        ambientLight2.setColor(Vec4(aLightVal, aLightVal, aLightVal, 1))

        #sets a directional light
        directionalLight = DirectionalLight("directionalLight")
        directionalLight.setDirection(Vec3(dLightVal1, dLightVal1, dLightVal1))
        directionalLight.setColor(Vec4(1, 1, 1, 1))
        directionalLight.setSpecularColor(Vec4(0, 0, 0, 1))

        #sets a directional light
        directionalLight1 = DirectionalLight("directionalLight2")
        directionalLight1.setDirection(Vec3(dLightVal2, dLightVal1, dLightVal1))
        directionalLight1.setColor(Vec4(1, 1, 1, 1))
        directionalLight1.setSpecularColor(Vec4(1, 1, 1, 1))


        #attaches lights to scene
        render.setLight(render.attachNewNode(ambientLight))
        render.setLight(render.attachNewNode(ambientLight1))
        render.setLight(render.attachNewNode(ambientLight1))
        render.setLight(render.attachNewNode(directionalLight))
        render.setLight(render.attachNewNode(directionalLight1))

###################### COLLISION DETECTION #####################################

    def traverseTask(self, task=None):
        # handles collisions with collision handers and a 
        # collision queue
        # essentially checks region of potential collision for collisions
        # and stops the ball if a collision is triggered
        # called by task manager
        self.ballModelGroundHandler.sortEntries()
        for i in range(self.ballModelGroundHandler.getNumEntries()):
            entry = self.ballModelGroundHandler.getEntry(i)

            if self.drop == True:
                #we cant drop in this situation
                self.ballModel.setZ(self.currentHeight)

                dropFailWait = 4
                dropFailSeq = Sequence()
                dropFailSeq.append(Func(addNotification,"Whoops! You can't drop here!"))
                dropFailSeq.append(Wait(dropFailWait))
                dropFailSeq.append(Func(deleteNotifications))
                dropFailSeq.start()

                self.drop = False

            elif self.direction == "N":
                self.northDisableMovements()

            elif self.direction == "S":
                self.southDisableMovements()

            elif self.direction == "E":
                self.eastDisableMovements()

            elif self.direction == "W":
                self.westDisableMovements()

            if task: return task.cont #exit task

        # If there are no collisions
        
        if task: return task.cont

    def northDisableMovements(self):
        #disables movements when direction is north
        if self.keyMap["forward"] != 0: #if the ball was moving foward
            self.disableForwardMovement = True #disable forward movement
        if self.keyMap["backward"] != 0:
            self.disableBackwardMovement = True

    def southDisableMovements(self):
        #disables movements when direction is south
        if self.keyMap["forward"] != 0: 
            self.disableBackwardMovement = True 
        if self.keyMap["backward"] != 0:
            self.disableForwardMovement = True

    def eastDisableMovements(self):
        #disables movements when direction is east
        if self.keyMap["forward"] != 0: 
            self.disableRightMovement = True 
        if self.keyMap["backward"] != 0:
            self.disableLeftMovement = True

    def westDisableMovements(self):
        #disables movements when direction is west
        if self.keyMap["forward"] != 0: 
            self.disableLeftMovement = True 
        if self.keyMap["backward"] != 0:
            self.disableRightMovement = True

    def checkCollisions(self):
        #checks for collisions
        self.cTrav.traverse(render)

    def enableAllWalls(self):
        #enables all walls by disabling all the disable wall functions
        self.disableLeftMovement = False
        self.disableRightMovement = False 
        self.disableForwardMovement = False
        self.disableBackwardMovement = False

    def inCollision(self):
        #return true if we are in a collision right now, false otherwise
        if (self.disableForwardMovement == True
            or self.disableBackwardMovement == True 
            or self.disableRightMovement == True 
            or self.disableLeftMovement):
            return True
        return False

    def checkForWin(self):
        #checks for a win, toggles win splash sceen if we win
        yLoc = self.ballModel.getY()
        exitBound = -9.1

        if yLoc < exitBound: 
            winScreen = SplashScreen()
            winScreen.win()

        if self.mode == "timer":
            self.checkForTimerLoss()

    def checkForTimerLoss(self):
        #checks to see the time, will lose if past 5 minutes
        
        if self.timer == "0:05:00":
            loseScreen = SplashScreen()
            loseScreen.lose()

###################### MOVEMENTS ###############################################

    def move(self, task):
        # Accepts arrow keys to move the player front and back
        # Also deals with grid checking and collision detection

        step = 0.03

        #movement animation
        self.movementAnimation(step)
        #rotation animation
        self.rotationAnimation()

        base.camera.setX(self.ballModel.getX() + math.sin(self.camAngle))
        base.camera.setY(self.ballModel.getY() + math.cos(self.camAngle))

        self.resetCamDist()
        self.checkCollisions()
        self.lookAtFloater()

        self.checkForWin()

        return task.cont

    def resetCamDist(self):
        #resets the camera distance to a specific distance
        #keeps distance relatively constant
        camFarDist = 0.75
        camCloseDist = 0.7

        camvec = self.ballModel.getPos() - base.camera.getPos()
            #vector between ball and camera
        camvec.setZ(0)
        camdist = camvec.length()
        camvec.normalize()

        if (camdist > camFarDist):
            base.camera.setPos(base.camera.getPos() + 
                                    camvec*(camdist-camFarDist))
            camdist = camFarDist

        if (camdist < camCloseDist):
            base.camera.setPos(base.camera.getPos() -
                                    camvec*(camCloseDist-camdist))
            camdist = camCloseDist

        base.camera.lookAt(self.ballModel)

    def lookAtFloater(self):
        #looks at the floater above the sphere
        floaterHeight = 0.23
        self.floater.setPos(self.ballModel.getPos())
        self.floater.setZ(self.ballModel.getZ() + floaterHeight)
        base.camera.lookAt(self.floater)

    ####################### Movement Animation #################################

    def ballIsMoving(self):
        #notes if the ball is moving or not with self.isMoving variable
        if (self.keyMap["forward"]!=0) or (self.keyMap["backward"]!=0):
            if self.isMoving == False:
                self.isMoving = True

        elif self.keyMap["forward"] == 0 and self.keyMap["backward"] == 0:
            self.isMoving = False

    def movementAnimation(self, step):
        #describes the movement animation
        if self.drop == True:
            self.dropMovementAnimation(step)
        elif self.direction == "N":
            self.northMovementAnimation(step)

        elif self.direction == "S":
            self.southMovementAnimation(step)

        elif self.direction == "E":
            self.eastMovementAnimation(step)

        elif self.direction == "W":
            self.westMovementAnimation(step)

    def northMovementAnimation(self, step):
        #describes animation when direction is north
        if (self.keyMap["forward"]!=0):
            #if you are pressing forward
            if self.disableForwardMovement == False:
                #if you are just moving through space...
                self.ballModel.setY(self.ballModel.getY() + step)
            if self.disableBackwardMovement == True:
                #if you had moved backwards into a wall
                #and you want to move forward again
                self.ballModel.setY(self.ballModel.getY() + step)
                self.disableBackwardMovement = False
                

        if (self.keyMap["backward"]!=0):
            #if you are pressing backwards
            if self.disableBackwardMovement == False:
                #if you are just moving backwards through space...
                self.ballModel.setY(self.ballModel.getY() - step)
            if self.disableForwardMovement == True:
                #if you had moved forward into a wall
                #and want to back away from the wall
                self.ballModel.setY(self.ballModel.getY() - step)
                self.disableForwardMovement = False        

    def southMovementAnimation(self, step):
        #describes animation when direction is north
        #same relative set of animations to northMovementAnimation
        #but opposite
        if (self.keyMap["forward"]!=0):
            if self.disableBackwardMovement == False:
                self.ballModel.setY(self.ballModel.getY() - step)
            if self.disableForwardMovement == True:
                self.ballModel.setY(self.ballModel.getY() - step)
                self.disableForwardMovement = False

        if (self.keyMap["backward"]!=0):
            if self.disableForwardMovement == False:
                self.ballModel.setY(self.ballModel.getY() + step)
            if self.disableBackwardMovement == True:
                self.ballModel.setY(self.ballModel.getY() + step)
                self.disableBackwardMovement = False        

    def eastMovementAnimation(self, step):
        #describes animation when direction is east
        #same relative as north and south movement animations
        #but relative to the x axis
        #and disabling/enabling right and left movement at collisions
        if (self.keyMap["forward"]!=0):
            if self.disableRightMovement == False:
                self.ballModel.setX(self.ballModel.getX() + step)
            if self.disableLeftMovement == True:
                self.ballModel.setX(self.ballModel.getX() + step)
                self.disableLeftMovement = False

        if (self.keyMap["backward"]!=0):
            if self.disableLeftMovement == False:
                self.ballModel.setX(self.ballModel.getX() - step)
            if self.disableRightMovement == True:
                self.ballModel.setX(self.ballModel.getX() - step)
                self.disableRightMovement = False

    def westMovementAnimation(self, step):
        #describes animation when direction is west
        #relatively same animations as the east movement animations
        #exact opposite
        if (self.keyMap["forward"]!=0):
            if self.disableLeftMovement == False:
                self.ballModel.setX(self.ballModel.getX() - step)
            if self.disableRightMovement == True:
                self.ballModel.setX(self.ballModel.getX() - step)
                self.disableRightMovement = False

        if (self.keyMap["backward"]!=0):
            if self.disableRightMovement == False:
                self.ballModel.setX(self.ballModel.getX() + step)
            if self.disableLeftMovement == True:
                self.ballModel.setX(self.ballModel.getX() + step)
                self.disableLeftMovement = False

    def turnRight(self):
        #turns right in the animation

        #uses an interval to slowly rotate camera around
        initial = self.camAngle
        final = self.camAngle + math.pi/2

        #turn animation
        turnTime = 0.2
        turnRightSeq = Sequence()
        turnRightSeq.append(LerpFunc(self.changeCamAngle, turnTime, initial,
                                                         final, 'easeInOut'))
        turnRightSeq.start()

        self.setKey("right", 1) #notes that the right key is pressed

        #changes the direction right, based on current direction
        if self.direction == "N":
            self.direction = "E"
        elif self.direction == "E":
            self.direction = "S"
        elif self.direction == "S":
            self.direction = "W"
        else:
            self.direction = "N"

        #when you turn, all the collision disablements should be True
        #just checking
        #self.enableAllWalls()

        #update the label
        self.updateDirectionText()

    def turnLeft(self):
        #turns left

        initial = self.camAngle
        final = self.camAngle - math.pi/2

        #turn animation
        turnTime = 0.2
        turnRightSeq = Sequence()
        turnRightSeq.append(LerpFunc(self.changeCamAngle, turnTime, initial,
                                                         final, 'easeInOut'))
        turnRightSeq.start()


        self.setKey("left", 1) #notes that left key is pressed

        #changes the direction left, based on current direction
        if self.direction == "N":
            self.direction = "W"
        elif self.direction == "W":
            self.direction = "S"
        elif self.direction == "S":
            self.direction = "E"
        else:
            self.direction = "N"

        #when you turn, all the collision disablements should be True
        #just checking
        #self.enableAllWalls()

        #update the label
        self.updateDirectionText()

    def changeCamAngle(self, angle):
        #changes the camAngle to angle
        self.camAngle = angle

    def dropMovementAnimation(self, step):
        #describes movement when drop is hit

        a = 0.1

        if self.keyMap["drop"] != 0:
            if self.ballModel.getZ() > self.currentHeight - self.levelHeight+ a:
                self.ballModel.setZ(self.ballModel.getZ() - step)
            else:
                self.currentHeight -= self.levelHeight
                self.level += 1
                self.updateLevelText()
                self.drop = False
                base.camera.setZ(self.ballModel.getZ() + self.cameraHeight)

    def nowDropping(self):
        #toggles isDropping boolean
        self.drop = True
        self.setKey("drop", 1)
        
    ################## Ball Rotation Animation #################################

    def rotationAnimation(self):
        #describes the rotation movement of sphere
        self.ballIsMoving()
        speed=300
        inCollision = self.inCollision()

        if self.isMoving and not inCollision:
            if self.direction == "N":
                self.northRotationAnimation(speed)
            if self.direction == "S":
                self.southRotationAnimation(speed)
            if self.direction == "E":
                self.eastRotationAnimation(speed)
            if self.direction == "W":
                self.westRotationAnimation(speed)

    def northRotationAnimation(self, speed):
        #describes the rotation animation if direction is north
        if self.keyMap["forward"] != 0:
            self.ballModel.setP(self.ballModel.getP()-speed*globalClock.getDt())
        elif self.keyMap["backward"] != 0:
            self.ballModel.setP(self.ballModel.getP()+speed*globalClock.getDt())

    def southRotationAnimation(self, speed):
        #describes the rotaiton animation if the direction is south
        if self.keyMap["backward"] != 0:
            self.ballModel.setP(self.ballModel.getP()-speed*globalClock.getDt())
        elif self.keyMap["forward"] != 0:
            self.ballModel.setP(self.ballModel.getP()+speed*globalClock.getDt())

    def eastRotationAnimation(self, speed):
        #describes the rotation animation if the direction is east
        if self.keyMap["backward"] != 0:
            self.ballModel.setR(self.ballModel.getR()-speed*globalClock.getDt())
        elif self.keyMap["forward"] != 0:
            self.ballModel.setR(self.ballModel.getR()+speed*globalClock.getDt())

    def westRotationAnimation(self, speed):
        #describes the rotation animation if the direction is west
        if self.keyMap["forward"] != 0:
            self.ballModel.setR(self.ballModel.getR()-speed*globalClock.getDt())
        elif self.keyMap["backward"] != 0:
            self.ballModel.setR(self.ballModel.getR()+speed*globalClock.getDt())

###################### VIEWS ###################################################

    def toggle_xray_mode(self):
        #Toggle X-ray mode on and off.
        #Note: slows down program considerably
        xRayA = 0.5
        self.xray_mode = not self.xray_mode
        if self.xray_mode:
            self.wallModel.setColorScale((1, 1, 1, xRayA))
            self.wallModel.setTransparency(TransparencyAttrib.MDual)
        else:
            self.wallModel.setColorScaleOff()
            self.wallModel.setTransparency(TransparencyAttrib.MNone)

    def toggle_collision_mode(self):
        #Toggle collision mode on and off
        #Shows visual representation of the collisions occuring
        self.collision_mode = not self.collision_mode
        if self.collision_mode == True:
            # Note: Slows the program down considerably
            self.cTrav.showCollisions(render)
        else:
            self.cTrav.hideCollisions()

    def toggle_wireframe(self):
        #toggles wireframe view
        self.wireframe = not self.wireframe
        if self.wireframe:
            self.wallModel.setRenderModeWireframe()
        else:
            self.wallModel.setRenderModeFilled()

##################### RESTART ##################################################
    
    def restart(self):
        #restarts the game
        loading = SplashScreen()
        loading.loading()
        self.reset()

    def reset(self):
        #resets the maze, resets the location of the character

        #removes all notes
        self.wallModel.removeNode()
        self.ballModel.removeNode()

        #resets notes
        self.loadWallModel()
        self.loadBallModel()
        self.createBallColliderModel()
        self.resetCamDist()

        #resets timers
        taskMgr.remove("timerTask")
        self.timer = ""
        self.timerText.destroy()

        self.promptMode()

#################### TIMER #####################################################

    def setTimer(self):
        #code from panda.egg user on Panda3D, 
        #"How to use Timer, a small example maybe?" forum
        #creates a timer
        self.timer = DirectLabel(pos=Vec3(1, 0.85),scale=0.08)

        taskMgr.add(self.timerTask, "timerTask")

    def dCharstr(self, theString):
        #code from panda.egg user on Panda3D, 
        #"How to use Timer, a small example maybe?" forum
        #turns time string into a readable clock string
        if len(theString) != 2:
            theString = '0' + theString
        return theString

    def timerTask(self, task):
        #code from panda.egg user on Panda3D, 
        #"How to use Timer, a small example maybe?" forum
        #task for resetting timer in timer mode
        secondsTime = int(task.time)
        minutesTime = int(secondsTime/60)
        hoursTime = int(minutesTime/60)
        self.timer = (str(hoursTime) + ':' 
                            + self.dCharstr(str(minutesTime%60)) + ':' 
                            + self.dCharstr(str(secondsTime%60)))

        self.updateTimerText()
        
        return Task.cont
Example #6
0
class Camera:
    """A floating 3rd person camera that follows an actor around, and can be 
    turned left or right around the actor. 

    Public fields: 
    self.controlMap -- The camera's movement controls. 
    actor -- The Actor object that the camera will follow. 
    
    Public functions: 
    init(actor) -- Initialise the camera. 
    move(task) -- Move the camera each frame, following the assigned actor. 
                  This task is called every frame to update the camera. 
    setControl -- Set the camera's turn left or turn right control on or off. 
    
    """
    def __init__(self, actor):
        """Initialise the camera, setting it to follow 'actor'. 
        
        Arguments: 
        actor -- The Actor that the camera will initially follow. 
        
        """

        self.actor = actor
        self.prevtime = 0

        # The camera's controls:
        # "left" = move the camera left, 0 = off, 1 = on
        # "right" = move the camera right, 0 = off, 1 = on
        self.controlMap = {"left": 0, "right": 0}

        taskMgr.add(self.move, "cameraMoveTask")

        # Create a "floater" object. It is used to orient the camera above the
        # target actor's head.

        self.floater = NodePath(PandaNode("floater"))
        self.floater.reparentTo(render)

        # Set up the camera.

        base.disableMouse()
        base.camera.setPos(self.actor.getX(), self.actor.getY() + 2, 2)
        # uncomment for topdown
        #base.camera.setPos(self.actor.getX(),self.actor.getY()+10,2)
        #base.camera.setHpr(180, -50, 0)

        # A CollisionRay beginning above the camera and going down toward the
        # ground is used to detect camera collisions and the height of the
        # camera above the ground. A ray may hit the terrain, or it may hit a
        # rock or a tree.  If it hits the terrain, we detect the camera's
        # height.  If it hits anything else, the camera is in an illegal
        # position.

        self.cTrav = CollisionTraverser()
        self.groundRay = CollisionRay()
        self.groundRay.setOrigin(0, 0, 1000)
        self.groundRay.setDirection(0, 0, -1)
        self.groundCol = CollisionNode('camRay')
        self.groundCol.addSolid(self.groundRay)
        self.groundCol.setFromCollideMask(BitMask32.bit(1))
        self.groundCol.setIntoCollideMask(BitMask32.allOff())
        self.groundColNp = base.camera.attachNewNode(self.groundCol)
        self.groundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.groundColNp, self.groundHandler)

        # Uncomment this line to see the collision rays
        #self.groundColNp.show()

    def move(self, task):
        """Update the camera's position before rendering the next frame. 
        
        This is a task function and is called each frame by Panda3D. The 
        camera follows self.actor, and tries to remain above the actor and 
        above the ground (whichever is highest) while looking at a point 
        slightly above the actor's head. 
        
        Arguments: 
        task -- A direct.task.Task object passed to this function by Panda3D. 
        
        Return: 
        Task.cont -- To tell Panda3D to call this task function again next 
                     frame. 
        
        """

        # FIXME: There is a bug with the camera -- if the actor runs up a
        # hill and then down again, the camera's Z position follows the actor
        # up the hill but does not come down again when the actor goes down
        # the hill.

        elapsed = task.time - self.prevtime

        # If the camera-left key is pressed, move camera left.
        # If the camera-right key is pressed, move camera right.

        # comment out for topdown
        base.camera.lookAt(self.actor)

        camright = base.camera.getNetTransform().getMat().getRow3(0)
        camright.normalize()
        if (self.controlMap["left"] != 0):
            base.camera.setPos(base.camera.getPos() - camright *
                               (elapsed * 20))
        if (self.controlMap["right"] != 0):
            base.camera.setPos(base.camera.getPos() + camright *
                               (elapsed * 20))

        # If the camera is too far from the actor, move it closer.
        # If the camera is too close to the actor, move it farther.

        camvec = self.actor.getPos() - base.camera.getPos()
        camvec.setZ(0)
        camdist = camvec.length()
        camvec.normalize()
        if (camdist > 10.0):
            base.camera.setPos(base.camera.getPos() + camvec * (camdist - 10))
            camdist = 10.0
        if (camdist < 5.0):
            base.camera.setPos(base.camera.getPos() - camvec * (5 - camdist))
            camdist = 5.0

        # Now check for collisions.

        self.cTrav.traverse(render)

        # Keep the camera at one foot above the terrain,
        # or two feet above the actor, whichever is greater.
        # comment out for topdown

        entries = []
        for i in range(self.groundHandler.getNumEntries()):
            entry = self.groundHandler.getEntry(i)
            entries.append(entry)
        entries.sort(lambda x, y: cmp(
            y.getSurfacePoint(render).getZ(),
            x.getSurfacePoint(render).getZ()))
        if (len(entries) > 0) and (entries[0].getIntoNode().getName()
                                   == "terrain"):
            base.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.0)
        if (base.camera.getZ() < self.actor.getZ() + 2.0):
            base.camera.setZ(self.actor.getZ() + 2.0)

        # The camera should look in the player's direction,
        # but it should also try to stay horizontal, so look at
        # a floater which hovers above the player's head.

        self.floater.setPos(self.actor.getPos())
        self.floater.setZ(self.actor.getZ() + 2.0)

        #self.floater.setZ(self.actor.getZ() + 10.0)
        #self.floater.setY(self.actor.getY() + 7.0)

        # comment out for topdown
        base.camera.lookAt(self.floater)

        base.camera.setPos(self.floater.getPos())

        # Store the task time and continue.
        self.prevtime = task.time
        return Task.cont

    def setControl(self, control, value):
        """Set the state of one of the camera's movement controls. 
        
        Arguments: 
        See self.controlMap in __init__. 
        control -- The control to be set, must be a string matching one of 
                   the strings in self.controlMap. 
        value -- The value to set the control to. 
        
        """

        # FIXME: this function is duplicated in Camera and Character, and
        # keyboard control settings are spread throughout the code. Maybe
        # add a Controllable class?

        self.controlMap[control] = value
Example #7
0
class NodeRaycaster:
    def __init__(self, renderer):

        self.log = logging.getLogger('pano.raycaster')

        self.renderer = renderer

        #Stores the collisions of the camera ray with the cubemap
        self.collisionsQueue = None

        #Variables for setting up collision detection in Panda
        self.pickerNP = None
        self.pickerNode = None
        self.pickerRay = None
        self.traverser = None

    def initialize(self):
        """
        To setup collision detection we need:
            a. A CollisionNode having a ray as its solid and placed at the position
               of the camera while also having the same orientation as the camera.
            b. A new nodepath placed in the scenegraph as an immediate child of the
               camera. It will be used to insert the collision node in the scenegraph.
            c. A CollisionRay for firing rays based on mouse clicks.
            d. A collisions traverser.
            e. A collisions queue where all found collisions will be stored for later
               processing.
        """
        self.traverser = CollisionTraverser('Hotspots collision traverser')
        self.collisionsQueue = CollisionHandlerQueue()
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.pickerNP = self.renderer.getCamera().attachNewNode(
            self.pickerNode)
        self.traverser.addCollider(self.pickerNP, self.collisionsQueue)

    def dispose(self):
        if self.pickerNP is not None:
            self.traverser.removeCollider(self.pickerNP)
            self.pickerNode.clearSolids()
            self.pickerNP.removeNode()

    def raycastWindow(self, x, y, returnAll=False):
        '''
        Casts a camera ray, whose origin is implicitly defined by the given window coordinates, against 
        the rendered scene returns information regarding the hit point, if any.
        
        @param x: The x window coordinate of the ray's origin in render2d space.
        @param y: The y window coordinate of the ray's origin in render2d space 
        @param returnAll: If set to False then only the closest collided geometry is returned, otherwise
        all nodepaths whose collision nodes were intersected by the camera ray will be returned. 
        @return: 
        If returnAll was False, then a list containing a tuple of the form (topmost intersected NodePath, contact point Point3f).
        if returnAll was set to True, a list of tuples in the same form as above, one tuple for each intersection. 
        None if no collision occurred. 
        '''
        #This makes the ray's origin the camera and makes the ray point
        #to the screen coordinates of the mouse
        self.pickerRay.setFromLens(self.renderer.getCamera().node(), x, y)

        #Check for collision only with the node
        self.traverser.traverse(self.renderer.getSceneRoot())

        if self.collisionsQueue.getNumEntries() > 0:
            if not returnAll:
                self.collisionsQueue.sortEntries()
                cEntry = self.collisionsQueue.getEntry(0)
                if cEntry.hasInto():
                    return [(cEntry.getIntoNodePath(),
                             cEntry.getSurfacePoint())]
                else:
                    return None
            else:
                nodepaths = []
                for i in xrange(self.collisionsQueue.getNumEntries()):
                    cEntry = self.collisionsQueue.getEntry(i)
                    if cEntry.hasInto():
                        #                        self.log.debug('adding collision into-nodepath: %s' % str(cEntry.getIntoNodePath()))
                        intoNP = cEntry.getIntoNodePath()
                        nodepaths.append(
                            (intoNP, cEntry.getSurfacePoint(intoNP)))
                return nodepaths
Example #8
0
class cWorld:
    def __init__(self):
        #  set background color
        base.setBackgroundColor(0, 0, 0)
        # create target
        self.createTarget()
        # create boids
        self.createBoids()
        # setup camera
        self.setupCamera()
        # setup lights
        self.setupLights()
        # setup collision detection
        self.setupCollision()
        # add task
        taskMgr.add(self.steer, 'steer')  # steer task
        taskMgr.add(self.moveTarget, 'moveTarget')  # mouse move target task

    def createBoids(self):
        self.redBoid = cBoid()  # create red boid
        # setup blue boid with model path, starting location, max force, and max speed
        self.redBoid.setup('assets/models/boid_one.egg', Vec3(0.0, 0.0, 0.0),
                           4.0, 0.1)
        # create blue boid
        self.blueBoid = cBoid()
        # setup blue boid with model path, starting location, max force, and max speed
        self.blueBoid.setup('assets/models/boid_two.egg', Vec3(0.0, 0.0, 0.0),
                            4.0, 1.0)

    def createTarget(self):
        # load in model file
        self.target = loader.loadModel('assets/models/target.egg')
        # parent
        self.target.reparentTo(render)
        # set location
        self.target.setPos(Vec3(0.0, 0.0, 0.0))

    def setupCamera(self):
        # disable auto controls
        base.disableMouse()
        # set position, heading, pitch, and roll
        camera.setPosHpr(Vec3(0.0, -45.0, 45.0), Vec3(0.0, -45.0, 0))

    def setupLights(self):
        # create a point light
        plight = PointLight('plight')
        # set its color
        plight.setColor(VBase4(1.0, 1.0, 1.0, 1))
        # attach the light to the render
        plnp = render.attachNewNode(plight)
        # set position
        plnp.setPos(0.0, 0.0, 2.0)
        # turn on light
        render.setLight(plnp)

    def setupCollision(self):
        # create collision traverser
        self.picker = CollisionTraverser()
        # create collision handler
        self.pq = CollisionHandlerQueue()
        # create collision node
        self.pickerNode = CollisionNode('mouseRay')  # create collision node
        # attach new collision node to camera node
        self.pickerNP = camera.attachNewNode(
            self.pickerNode)  # attach collision node to camera
        # set bit mask to one
        self.pickerNode.setFromCollideMask(BitMask32.bit(1))  # set bit mask
        # create a collision ray
        self.pickerRay = CollisionRay()  # create collision ray
        # add picker ray to the picker node
        self.pickerNode.addSolid(
            self.pickerRay)  # add the collision ray to the collision node
        # make the traverser know about the picker node and its even handler queue
        self.picker.addCollider(
            self.pickerNP,
            self.pq)  # add the colision node path and collision handler queue
        #self.picker.showCollisions( render ) # render or draw the collisions
        #self.pickerNP.show( ) # render picker ray
        # create col node
        self.colPlane = CollisionNode('colPlane')
        # add solid to col node plane
        self.colPlane.addSolid(
            CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0))))
        # attach new node to the render
        self.colPlanePath = render.attachNewNode(self.colPlane)
        #self.colPlanePath.show( ) # render node
        # make the col plane look at the camera
        # this makes it alway look at the camera no matter the orientation
        # we need this because the ray nees to intersect a plane parallel
        # to the camera
        self.colPlanePath.lookAt(camera)
        # prop up the col plane
        self.colPlanePath.setP(-45)
        # set bit mask to one
        # as I understand it, this makes all col nodes with bit mask one
        # create collisions while ignoring others of other masks
        self.colPlanePath.node().setIntoCollideMask(BitMask32.bit(1))

    def steer(self, Task):
        # seek after target
        self.redBoid.seek(Vec3(self.target.getPos()))
        # run the algorithm
        self.redBoid.run()
        # arrive at the target
        self.blueBoid.arrive(Vec3(self.target.getPos()))
        # run the algorithm
        self.blueBoid.run()
        return Task.cont  # continue task

    def moveTarget(self, Task):
        # traverse through the render tree
        self.picker.traverse(render)
        # go through the queue of collisions
        for i in range(self.pq.getNumEntries()):
            entry = self.pq.getEntry(i)  # get entry
            surfacePoint = entry.getSurfacePoint(
                render)  # get surface point of collision
            self.target.setPos(
                surfacePoint)  # set surface point to target's position
        if base.mouseWatcherNode.hasMouse():  # if we have a mouse
            mpos = base.mouseWatcherNode.getMouse(
            )  # get the path to the mouse
            # shoot ray from camera
            # based on X & Y coordinate of mouse
            self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
        return Task.cont  # continue task
Example #9
0
class CogdoFlyingCameraManager:
    def __init__(self, cam, parent, player, level):
        self._toon = player.toon
        self._camera = cam
        self._parent = parent
        self._player = player
        self._level = level
        self._enabled = False

    def enable(self):
        if self._enabled:
            return
        self._toon.detachCamera()
        self._prevToonY = 0.0
        levelBounds = self._level.getBounds()
        l = Globals.Camera.LevelBoundsFactor
        self._bounds = ((levelBounds[0][0] * l[0], levelBounds[0][1] * l[0]),
                        (levelBounds[1][0] * l[1], levelBounds[1][1] * l[1]),
                        (levelBounds[2][0] * l[2], levelBounds[2][1] * l[2]))
        self._lookAtZ = self._toon.getHeight(
        ) + Globals.Camera.LookAtToonHeightOffset
        self._camParent = NodePath('CamParent')
        self._camParent.reparentTo(self._parent)
        self._camParent.setPos(self._toon, 0, 0, 0)
        self._camParent.setHpr(180, Globals.Camera.Angle, 0)
        self._camera.reparentTo(self._camParent)
        self._camera.setPos(0, Globals.Camera.Distance, 0)
        self._camera.lookAt(self._toon, 0, 0, self._lookAtZ)
        self._cameraLookAtNP = NodePath('CameraLookAt')
        self._cameraLookAtNP.reparentTo(self._camera.getParent())
        self._cameraLookAtNP.setPosHpr(self._camera.getPos(),
                                       self._camera.getHpr())
        self._levelBounds = self._level.getBounds()
        self._enabled = True
        self._frozen = False
        self._initCollisions()

    def _initCollisions(self):
        self._camCollRay = CollisionRay()
        camCollNode = CollisionNode('CameraToonRay')
        camCollNode.addSolid(self._camCollRay)
        camCollNode.setFromCollideMask(OTPGlobals.WallBitmask
                                       | OTPGlobals.CameraBitmask
                                       | ToontownGlobals.FloorEventBitmask
                                       | ToontownGlobals.CeilingBitmask)
        camCollNode.setIntoCollideMask(0)
        self._camCollNP = self._camera.attachNewNode(camCollNode)
        self._camCollNP.show()
        self._collOffset = Vec3(0, 0, 0.5)
        self._collHandler = CollisionHandlerQueue()
        self._collTrav = CollisionTraverser()
        self._collTrav.addCollider(self._camCollNP, self._collHandler)
        self._betweenCamAndToon = {}
        self._transNP = NodePath('trans')
        self._transNP.reparentTo(render)
        self._transNP.setTransparency(True)
        self._transNP.setAlphaScale(Globals.Camera.AlphaBetweenToon)
        self._transNP.setBin('fixed', 10000)

    def _destroyCollisions(self):
        self._collTrav.removeCollider(self._camCollNP)
        self._camCollNP.removeNode()
        del self._camCollNP
        del self._camCollRay
        del self._collHandler
        del self._collOffset
        del self._betweenCamAndToon
        self._transNP.removeNode()
        del self._transNP

    def freeze(self):
        self._frozen = True

    def unfreeze(self):
        self._frozen = False

    def disable(self):
        if not self._enabled:
            return
        self._destroyCollisions()
        self._camera.wrtReparentTo(render)
        self._cameraLookAtNP.removeNode()
        del self._cameraLookAtNP
        self._camParent.removeNode()
        del self._camParent
        del self._prevToonY
        del self._lookAtZ
        del self._bounds
        del self._frozen
        self._enabled = False

    def update(self, dt=0.0):
        self._updateCam(dt)
        self._updateCollisions()

    def _updateCam(self, dt):
        toonPos = self._toon.getPos()
        camPos = self._camParent.getPos()
        x = camPos[0]
        z = camPos[2]
        toonWorldX = self._toon.getX(render)
        maxX = Globals.Camera.MaxSpinX
        toonWorldX = clamp(toonWorldX, -1.0 * maxX, maxX)
        spinAngle = Globals.Camera.MaxSpinAngle * toonWorldX * toonWorldX / (
            maxX * maxX)
        newH = 180.0 + spinAngle
        self._camParent.setH(newH)
        spinAngle = spinAngle * (pi / 180.0)
        distBehindToon = Globals.Camera.SpinRadius * cos(spinAngle)
        distToRightOfToon = Globals.Camera.SpinRadius * sin(spinAngle)
        d = self._camParent.getX() - clamp(toonPos[0], *self._bounds[0])
        if abs(d) > Globals.Camera.LeewayX:
            if d > Globals.Camera.LeewayX:
                x = toonPos[0] + Globals.Camera.LeewayX
            else:
                x = toonPos[0] - Globals.Camera.LeewayX
        x = self._toon.getX(render) + distToRightOfToon
        boundToonZ = min(toonPos[2], self._bounds[2][1])
        d = z - boundToonZ
        if d > Globals.Camera.MinLeewayZ:
            if self._player.velocity[2] >= 0 and toonPos[
                    1] != self._prevToonY or self._player.velocity[2] > 0:
                z = boundToonZ + d * INVERSE_E**(dt *
                                                 Globals.Camera.CatchUpRateZ)
            elif d > Globals.Camera.MaxLeewayZ:
                z = boundToonZ + Globals.Camera.MaxLeewayZ
        elif d < -Globals.Camera.MinLeewayZ:
            z = boundToonZ - Globals.Camera.MinLeewayZ
        if self._frozen:
            y = camPos[1]
        else:
            y = self._toon.getY(render) - distBehindToon
        self._camParent.setPos(x, smooth(camPos[1], y), smooth(camPos[2], z))
        if toonPos[2] < self._bounds[2][1]:
            h = self._cameraLookAtNP.getH()
            if d >= Globals.Camera.MinLeewayZ:
                self._cameraLookAtNP.lookAt(self._toon, 0, 0, self._lookAtZ)
            elif d <= -Globals.Camera.MinLeewayZ:
                self._cameraLookAtNP.lookAt(self._camParent, 0, 0,
                                            self._lookAtZ)
            self._cameraLookAtNP.setHpr(h, self._cameraLookAtNP.getP(), 0)
            self._camera.setHpr(
                smooth(self._camera.getHpr(), self._cameraLookAtNP.getHpr()))
        self._prevToonY = toonPos[1]

    def _updateCollisions(self):
        pos = self._toon.getPos(self._camera) + self._collOffset
        self._camCollRay.setOrigin(pos)
        direction = -Vec3(pos)
        direction.normalize()
        self._camCollRay.setDirection(direction)
        self._collTrav.traverse(render)
        nodesInBetween = {}
        if self._collHandler.getNumEntries() > 0:
            self._collHandler.sortEntries()
            for entry in self._collHandler.getEntries():
                name = entry.getIntoNode().getName()
                if name.find('col_') >= 0:
                    np = entry.getIntoNodePath().getParent()
                    if not np in nodesInBetween:
                        nodesInBetween[np] = np.getParent()

        for np in nodesInBetween.keys():
            if np in self._betweenCamAndToon:
                del self._betweenCamAndToon[np]
            else:
                np.setTransparency(True)
                np.wrtReparentTo(self._transNP)
                if np.getName().find('lightFixture') >= 0:
                    if not np.find('**/*floor_mesh').isEmpty():
                        np.find('**/*floor_mesh').hide()
                elif np.getName().find('platform') >= 0:
                    if not np.find('**/*Floor').isEmpty():
                        np.find('**/*Floor').hide()

        for np, parent in self._betweenCamAndToon.items():
            np.wrtReparentTo(parent)
            np.setTransparency(False)
            if np.getName().find('lightFixture') >= 0:
                if not np.find('**/*floor_mesh').isEmpty():
                    np.find('**/*floor_mesh').show()
            elif np.getName().find('platform') >= 0:
                if not np.find('**/*Floor').isEmpty():
                    np.find('**/*Floor').show()

        self._betweenCamAndToon = nodesInBetween
Example #10
0
class Mouse(DirectObject):
    def __init__(self, app):
        # local variables for mouse class
        self.app = app
        self.init_collide()
        self.has_mouse = None
        self.prev_pos = None
        self.pos = None
        self.drag_start = None
        self.hovered_object = None
        self.button2 = False
        self.mouseTask = taskMgr.add(self.mouse_task, 'mouseTask')
        self.task = None
        # set up event and response to this event
        self.accept('mouse1', self.mouse1)
        self.accept('mouse1-up', self.mouse1_up)
        # change the mouse to accept 'right-click' to rotate camera
        self.accept('mouse3', self.rotateCamera)
        self.accept('mouse3-up', self.stopCamera)
        self.accept('wheel_up', self.zoomIn)
        self.accept('wheel_down', self.zoomOut)

    # set up the collision for object
    def init_collide(self):
        # why the heck he import within method
        from pandac.PandaModules import CollisionTraverser, CollisionNode
        from pandac.PandaModules import CollisionHandlerQueue, CollisionRay
        # init and import collision for object
        self.cTrav = CollisionTraverser('MousePointer')
        self.cQueue = CollisionHandlerQueue()
        self.cNode = CollisionNode('MousePointer')
        self.cNodePath = base.camera.attachNewNode(self.cNode)
        self.cNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
        self.cRay = CollisionRay()
        self.cNode.addSolid(self.cRay)
        self.cTrav.addCollider(self.cNodePath, self.cQueue)

    # by the collision methods mouse is able to find out which tile mouse is at
    def find_object(self):
        if self.app.world.nodePath:
            self.cRay.setFromLens(base.camNode, self.pos.getX(),
                                  self.pos.getY())
            self.cTrav.traverse(self.app.world.terrain.nodePath)
            if self.cQueue.getNumEntries() > 0:
                self.cQueue.sortEntries()
                return self.cQueue.getEntry(0).getIntoNodePath()
        return None

    # setting task for mouse
    def mouse_task(self, task):
        action = task.cont
        # if the current tile has a mouse point to this
        self.has_mouse = base.mouseWatcherNode.hasMouse()
        if self.has_mouse:
            self.pos = base.mouseWatcherNode.getMouse()
            if self.prev_pos:
                self.delta = self.pos - self.prev_pos
            else:
                self.delta = None
            if self.task:
                action = self.task(task)
        else:
            self.pos = None
        if self.pos:
            self.prev_pos = Point2(self.pos.getX(), self.pos.getY())
        return action

    # when mouse hover over this hexagon
    def hover(self, task):
        if self.hovered_object:
            self.hovered_object.unhover()
            self.hovered_object = None
        if self.button2:
            self.camera_drag()
        hovered_nodePath = self.find_object()
        if hovered_nodePath:
            tile = hovered_nodePath.findNetTag('tile')
            if not tile.isEmpty():
                tag = tile.getTag('tile')
                coords = tag.split(',')
                (x, y) = [int(n) for n in coords]
                # set the hovered target to be the corresponding hexagon on terrain
                self.hovered_object = self.app.world.terrain.rows[x][y]
                self.hovered_object.hover()
            character = hovered_nodePath.findNetTag('char')
            if not character.isEmpty():
                tag = character.getTag('char')
                (team_index, char_id) = [int(n) for n in tag.split(',')]
                self.hovered_object = self.app.world.teams[
                    team_index].characters_dict[char_id]
                self.hovered_object.hover()
            ghost = hovered_nodePath.findNetTag('ghost')
            if not ghost.isEmpty():
                tag = ghost.getTag('ghost')
                (team_index, char_id) = [int(n) for n in tag.split(',')]
                for ghostInstance in self.app.ghosts:
                    if (ghostInstance.team.index
                            == team_index) and (ghostInstance.id == char_id):
                        self.hovered_object = ghostInstance
                self.hovered_object.hover()
        return task.cont

    def mouse1(self):
        self.app.state.request('mouse1')

    def mouse1_up(self):
        self.app.state.request('mouse1-up')

    def camera_drag(self):
        if self.delta:
            old_heading = base.camera.getH()
            new_heading = old_heading - self.delta.getX() * 180
            base.camera.setH(new_heading % 360)
            old_pitch = base.camera.getP()
            new_pitch = old_pitch + self.delta.getY() * 90
            new_pitch = max(-90, min(-10, new_pitch))
            base.camera.setP(new_pitch)

    def rotateCamera(self):
        self.button2 = True

    def stopCamera(self):
        self.button2 = False

    def zoomIn(self):
        lens = base.cam.node().getLens()
        size = lens.getFilmSize()
        if size.length() >= 75:
            lens.setFilmSize(size / 1.2)

    def zoomOut(self):
        lens = base.cam.node().getLens()
        size = lens.getFilmSize()
        if size.length() <= 250:
            lens.setFilmSize(size * 1.2)
Example #11
0
class Game3T(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        #base.setFrameRateMeter(True)

        OnscreenText(text='3T', pos=(0, 0.85), scale=0.2, fg=(0.3, 0.2, 8, 1))
        OnscreenText(text='by: Hellmaster - 2011',
                     pos=(0, -0.9),
                     scale=0.1,
                     fg=(0.5, 0, 8, 1))

        FPS = 30
        globalClock = ClockObject.getGlobalClock()
        globalClock.setMode(ClockObject.MLimited)
        globalClock.setFrameRate(FPS)

        # ***************** SETUP SHITS *****************
        base.disableMouse()
        self.nextmove = True
        self.camera_plane_angle = 0

        # ***************** LOAD GAME LOGIC AND OTHERS CLASSES *************
        self.game = gameMecahnics()

        # ***************** LOAD MODELS AND SOUNDS ******************
        self.loadModels()
        self.loadSounds()

        # ***************** SELECT PLAYER ******************

        self.a = getPlayer()

        base.taskMgr.add(self.selectPlayer, "selectPlayer")

    def rotateCamera(self, task):
        dt = globalClock.getDt()
        self.camera_plane_angle += dt
        if self.camera_plane_angle > 360:
            self.camera_plane_angle = 0
        self.cam.setPos(30 * math.cos(self.camera_plane_angle),
                        30 * math.sin(self.camera_plane_angle), 30)
        self.cam.lookAt(0, 0, 0)
        return task.cont

    def selectPlayer(self, task):
        self.player = self.a.getSelectedPlayer()
        if self.player != None:
            #print "player selectecd"
            base.taskMgr.add(self.gameLoop, "gameLoop")
            return task.done
        return task.cont

    def gameLoop(self, task):
        #print "game task"

        # ***************** SET CAMERA ******************

        base.taskMgr.add(self.rotateCamera, "rotateCamera")

        self.game.setPlayer(self.player)

        # ***************** PICKABLE SHITS ***************
        # setup the pickable suqre planes on the board
        planes = []
        id_plane = 0
        for k in range(3):
            for i in range(3):
                #print "adding plane: ", id_plane
                p = addPlane(3)
                p.setPos(3 * i - 2.5, 3 * k - 2.5, 3.1)
                p.setTag('pickable', str(id_plane))
                p.hide()
                planes.append(p)
                id_plane += 1

        # set picks
        pickerNode = CollisionNode('mouseRay')
        pickerNP = self.cam.attachNewNode(pickerNode)
        pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
        self.pickerRay = CollisionRay()
        pickerNode.addSolid(self.pickerRay)
        #pickerNP.show()
        self.rayQueue = CollisionHandlerQueue()
        self.cTrav = CollisionTraverser()
        self.cTrav.addCollider(pickerNP, self.rayQueue)

        # set action in case of pick
        self.accept('mouse1', self.picked)

    def loadModels(self):
        self.arena = loader.loadModel("models/arena/arena")
        self.arena.reparentTo(render)
        self.arena.setPos(0, 0, 0)

        self.playero = loader.loadModel("models/players/o")

        self.playerx = loader.loadModel("models/players/x")

    def loadSounds(self):
        self.sound1 = loader.loadSfx("sounds/sound1.ogg")
        self.sound2 = loader.loadSfx("sounds/sound2.ogg")

    # detects if mouse picks some shit
    # if yes =>  set piece if valid and must wait 3 seconds for next move else do shit
    def picked(self):
        if base.mouseWatcherNode.hasMouse():
            mpos = base.mouseWatcherNode.getMouse()
            self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
            self.cTrav.traverse(render)
            if self.rayQueue.getNumEntries() > 0:
                self.rayQueue.sortEntries()
                pickedNP = self.rayQueue.getEntry(0).getIntoNodePath()
                if pickedNP.hasNetTag('pickable'):
                    if self.nextmove:
                        #print "picked: ", pickedNP, " | id 1: ", pickedNP.findNetTag('pickable'), " id 2: ", pickedNP.getNetTag('pickable')
                        move = int(pickedNP.getNetTag('pickable'))
                        if self.game.isValidMove(move):
                            #print "------VALID------", move
                            self.putPieceIN(move)
                            self.game.setMove(move)

                            self.nextmove = False
                        #else:
                        #print "------INVALID------"
                    #self.game.printBoard()

    def changeNextMoveValue(self, task):
        self.nextmove = True
        return task.done

    def putPieceIN(self, pos):
        if self.game.getCurrentPlayer() == 'o':
            self.newPiece = self.playero.copyTo(render)
        else:
            self.newPiece = self.playerx.copyTo(render)
        base.taskMgr.add(self.falling, "falling")
        if pos == 0:
            self.newPiece.setPos(-3, -3, STARTING_Z)
            return 0
        if pos == 1:
            self.newPiece.setPos(0, -3, STARTING_Z)
            return 0
        if pos == 2:
            self.newPiece.setPos(3, -3, STARTING_Z)
            return 0
        if pos == 3:
            self.newPiece.setPos(-3, 0, STARTING_Z)
            return 0
        if pos == 4:
            self.newPiece.setPos(0, 0, STARTING_Z)
            return 0
        if pos == 5:
            self.newPiece.setPos(3, 0, STARTING_Z)
            return 0
        if pos == 6:
            self.newPiece.setPos(-3, 3, STARTING_Z)
            return 0
        if pos == 7:
            self.newPiece.setPos(0, 3, STARTING_Z)
            return 0
        if pos == 8:
            self.newPiece.setPos(3, 3, STARTING_Z)
            return 0
        return -1

    def falling(self, task):
        dt = globalClock.getDt()
        self.newPiece.setZ(self.newPiece.getZ() - dt * 10)
        if self.newPiece.getZ() < 3:
            self.nextmove = True
            if self.game.isWinner() or self.game.isOver():
                self.gameOver()
            else:
                self.game.setNextPlayer()
            if self.game.getCurrentPlayer() == 'o':
                self.sound1.play()
            else:
                self.sound2.play()
            return task.done
        return task.cont

    def gameOver(self):
        #print "--------------------------"
        #print "-------- GAME OVER -------"
        #print "--------------------------"
        if self.game.isWinner():
            printWinner(self.game.getCurrentPlayer())
        else:
            printWinner(None)
        print "--------------------------"
        button_exit = DirectButton(text='Exit Game',
                                   pos=(0, 0, -0.4),
                                   scale=0.1,
                                   command=exitProg)
Example #12
0
class MousePicker(p3d.SingleTask):
    """
    Class to represent a ray fired from the input camera lens using the mouse.
    """
    def __init__(self, *args, **kwargs):
        p3d.SingleTask.__init__(self, *args, **kwargs)

        self.fromCollideMask = kwargs.pop('fromCollideMask', None)

        self.node = None
        self.collEntry = None

        # Create collision nodes
        self.collTrav = CollisionTraverser()
        #self.collTrav.showCollisions( render )
        self.collHandler = CollisionHandlerQueue()
        self.pickerRay = CollisionRay()

        # Create collision ray
        pickerNode = CollisionNode(self.name)
        pickerNode.addSolid(self.pickerRay)
        pickerNode.setIntoCollideMask(BitMask32.allOff())
        pickerNp = self.camera.attachNewNode(pickerNode)
        self.collTrav.addCollider(pickerNp, self.collHandler)

        # Create collision mask for the ray if one is specified
        if self.fromCollideMask is not None:
            pickerNode.setFromCollideMask(self.fromCollideMask)

        # Bind mouse button events
        eventNames = ['mouse1', 'control-mouse1', 'mouse1-up']
        for eventName in eventNames:
            self.accept(eventName, self.FireEvent, [eventName])

    def OnUpdate(self, task, x=None, y=None):

        # Update the ray's position
        if self.mouseWatcherNode.hasMouse():
            mp = self.mouseWatcherNode.getMouse()
            x, y = mp.getX(), mp.getY()
        if x is None or y is None:
            return
        self.pickerRay.setFromLens(self.camera.node(), x, y)

        # Traverse the hierarchy and find collisions
        self.collTrav.traverse(self.rootNp)
        if self.collHandler.getNumEntries():

            # If we have hit something, sort the hits so that the closest is first
            self.collHandler.sortEntries()
            collEntry = self.collHandler.getEntry(0)
            node = collEntry.getIntoNode()

            # If this node is different to the last node, send a mouse leave
            # event to the last node, and a mouse enter to the new node
            if node != self.node:
                if self.node is not None:
                    messenger.send('%s-mouse-leave' % self.node.getName(),
                                   [self.collEntry])
                messenger.send('%s-mouse-enter' % node.getName(), [collEntry])

            # Send a message containing the node name and the event over name,
            # including the collision entry as arguments
            messenger.send('%s-mouse-over' % node.getName(), [collEntry])

            # Keep these values
            self.collEntry = collEntry
            self.node = node

        elif self.node is not None:

            # No collisions, clear the node and send a mouse leave to the last
            # node that stored
            messenger.send('%s-mouse-leave' % self.node.getName(),
                           [self.collEntry])
            self.node = None

    def FireEvent(self, event):
        """
        Send a message containing the node name and the event name, including
        the collision entry as arguments.
        """
        if self.node is not None:
            messenger.send('%s-%s' % (self.node.getName(), event),
                           [self.collEntry])

    def GetFirstNodePath(self):
        """
        Return the first node in the collision queue if there is one, None
        otherwise.
        """
        if self.collHandler.getNumEntries():
            collEntry = self.collHandler.getEntry(0)
            return collEntry.getIntoNodePath()

        return None
Example #13
0
class Game(DirectObject.DirectObject):
    def __init__(self, showbase, usersData, gameData):
        self.showbase = showbase
        self.usersData = usersData
        self.gameData = gameData

        random.seed(self.gameData.randSeed)

        # Initialize the collision traverser.
        self.cTrav = CollisionTraverser()

        # Initialize the handler.
        self.collHandEvent = CollisionHandlerEvent()
        self.collHandEvent.addInPattern('into-%in')

        self.world = World(showbase)

        for user in self.usersData:
            user.centipede = Centipede(showbase, len(self.usersData),
                                       self.addToCollisions)
            if user.thisPlayer:
                self.centipede = user.centipede
                self.centipede.attachRing(showbase)

        self.foods = []
        for i in range(self.gameData.maxFoods):
            self.foods.append(Food(self.showbase, i, self.addToCollisions))

        self.ticks = 0

    def destroy(self):
        self.world.destroy()
        for user in self.usersData:
            user.centipede.destroy()
        for food in self.foods:
            food.destroy()

    def runTick(self, dt):
        # run each of the centipedes simulations
        for user in self.usersData:
            user.centipede.update(dt)
            if len(user.centipede.body) > 10:
                return False

        for food in self.foods:
            food.update(dt)

        self.cTrav.traverse(self.showbase.render)

        self.ticks += 1

        # Return true if game is still not over (false to end game)
        return True

    def collideInto(self, collEntry):
        for user in self.usersData:
            if collEntry.getFromNodePath(
            ) == user.centipede.head.collisionNode[0]:
                for food in self.foods:
                    if collEntry.getIntoNodePath(
                    ) == food.model.collisionNode[0]:
                        user.centipede.addLength(self.showbase)
                        food.reset()
                if len(user.centipede.body) > 2:
                    if collEntry.getIntoNodePath(
                    ) == user.centipede.tail.collisionNode[0]:
                        user.centipede.reset()
                    for i in range(len(user.centipede.body) - 1 - 2):
                        if collEntry.getIntoNodePath() == user.centipede.body[
                                i + 2].collisionNode[0]:
                            user.centipede.reset()
                            break

    def addToCollisions(self, item):
        # Add this object to the traverser.
        self.cTrav.addCollider(item[0], self.collHandEvent)

        # Accept the events sent by the collisions.
        self.accept('into-' + str(item[1]), self.collideInto)
Example #14
0
class Character: 
    
    """A character with an animated avatar that moves left, right or forward 
       according to the controls turned on or off in self.controlMap. 
    
    Public fields: 
    self.controlMap -- The character's movement controls 
    self.actor -- The character's Actor (3D animated model) 
    
    
    Public functions: 
    __init__ -- Initialise the character 
    move -- Move and animate the character for one frame. This is a task 
            function that is called every frame by Panda3D. 
    setControl -- Set one of the character's controls on or off. 
    
    """ 

    def __init__(self, model, run, walk, startPos, scale):        
        """Initialise the character. 
        
        Arguments: 
        model -- The path to the character's model file (string) 
           run : The path to the model's run animation (string) 
           walk : The path to the model's walk animation (string) 
           startPos : Where in the world the character will begin (pos) 
           scale : The amount by which the size of the model will be scaled 
                   (float) 
                    
           """ 

        self.controlMap = {"left":0, "right":0, "up":0, "down":0} 

        self.actor = Actor(Config.MYDIR+model, 
                                 {"run":Config.MYDIR+run, 
                                  "walk":Config.MYDIR+walk})        
        self.actor.reparentTo(render) 
        self.actor.setScale(scale) 
        self.actor.setPos(startPos) 

        self.controller = Controller.LocalController(self)
        
        taskMgr.add(self.move,"moveTask") # Note: deriving classes DO NOT need 
                                          # to add their own move tasks to the 
                                          # task manager. If they override 
                                          # self.move, then their own self.move 
                                          # function will get called by the 
                                          # task manager (they must then 
                                          # explicitly call Character.move in 
                                          # that function if they want it). 
        self.prevtime = 0 
        self.isMoving = False 

        # We will detect the height of the terrain by creating a collision 
        # ray and casting it downward toward the terrain.  One ray will 
        # start above ralph's head, and the other will start above the camera. 
        # A ray may hit the terrain, or it may hit a rock or a tree.  If it 
        # hits the terrain, we can detect the height.  If it hits anything 
        # else, we rule that the move is illegal. 

        self.cTrav = CollisionTraverser() 

        self.groundRay = CollisionRay() 
        self.groundRay.setOrigin(0,0,1000) 
        self.groundRay.setDirection(0,0,-1) 
        self.groundCol = CollisionNode('ralphRay') 
        self.groundCol.addSolid(self.groundRay) 
        self.groundCol.setFromCollideMask(BitMask32.bit(1)) 
        self.groundCol.setIntoCollideMask(BitMask32.allOff()) 
        self.groundColNp = self.actor.attachNewNode(self.groundCol)
        self.groundHandler = CollisionHandlerQueue() 
        self.cTrav.addCollider(self.groundColNp, self.groundHandler) 

        # Uncomment this line to see the collision rays 
        # self.groundColNp.show() 

        #Uncomment this line to show a visual representation of the
        #collisions occuring 
        # self.cTrav.showCollisions(render) 

    def move(self, task): 
        """Move and animate the character for one frame. 
        
        This is a task function that is called every frame by Panda3D. 
        The character is moved according to which of it's movement controls 
        are set, and the function keeps the character's feet on the ground 
        and stops the character from moving if a collision is detected. 
        This function also handles playing the characters movement 
        animations. 

        Arguments: 
        task -- A direct.task.Task object passed to this function by Panda3D. 
        
        Return: 
        Task.cont -- To tell Panda3D to call this task function again next 
                     frame. 
        """ 
        
        elapsed = task.time - self.prevtime 

        # save the character's initial position so that we can restore it, 
        # in case he falls off the map or runs into something. 

        startpos = self.actor.getPos() 

        # pass on input
        self.controller.move(task, elapsed)
        
        # If the character is moving, loop the run animation. 
        # If he is standing still, stop the animation. 

        if (self.controlMap["up"]!=0) or (self.controlMap["left"]!=0) or (self.controlMap["right"]!=0) or (self.controlMap["down"]!=0): 
            
            if self.isMoving is False: 
                self.actor.loop("run") 
                self.isMoving = True 
        else: 
            if self.isMoving: 
                self.actor.stop() 
                self.actor.pose("walk",5) 
                self.isMoving = False 

        # Now check for collisions. 

        self.cTrav.traverse(render) 

        # Adjust the character's Z coordinate.  If the character's ray hit terrain, 
        # update his Z. If it hit anything else, or didn't hit anything, put 
        # him back where he was last frame. 

        entries = [] 
        for i in range(self.groundHandler.getNumEntries()): 
            entry = self.groundHandler.getEntry(i) 
            entries.append(entry) 
        entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(), 
                                     x.getSurfacePoint(render).getZ())) 
        if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
            self.actor.setZ(entries[0].getSurfacePoint(render).getZ()) 
        else: 
            self.actor.setPos(startpos) 

        # Store the task time and continue. 
        self.prevtime = task.time 
        return Task.cont 

    def setControl(self, control, value): 
        """Set the state of one of the character's movement controls. 
        
        Arguments: 
        See self.controlMap in __init__. 
        control -- The control to be set, must be a string matching one of 
                   the strings in self.controlMap. 
        value -- The value to set the control to. 
        
        """ 

        # FIXME: this function is duplicated in Camera and Character, and 
        # keyboard control settings are spread throughout the code. Maybe 
        # add a Controllable class? 
        
        self.controlMap[control] = value 
Example #15
0
class thirdPerson(DirectObject):
    def __init__(self, parserClass, mainClass, mapLoaderClass,
                 modelLoaderClass):
        self.switchState = False

        #self.t = Timer()

        self.keyMap = {"left": 0, "right": 0, "forward": 0, "backward": 0}
        self.ralph = Actor(
            "data/models/units/ralph/ralph", {
                "run": "data/models/units/ralph/ralph-run",
                "walk": "data/models/units/ralph/ralph-walk"
            })
        self.ralph.reparentTo(render)
        #		self.ralph.setPos(42, 30, 0)
        self.ralph.setPos(6, 10, 0)
        self.ralph.setScale(0.1)

        self.accept("escape", sys.exit)
        self.accept("arrow_left", self.setKey, ["left", 1])
        self.accept("arrow_left-up", self.setKey, ["left", 0])
        self.accept("arrow_right", self.setKey, ["right", 1])
        self.accept("arrow_right-up", self.setKey, ["right", 0])
        self.accept("arrow_up", self.setKey, ["forward", 1])
        self.accept("arrow_up-up", self.setKey, ["forward", 0])
        self.accept("arrow_down", self.setKey, ["backward", 1])
        self.accept("arrow_down-up", self.setKey, ["backward", 0])

        self.isMoving = False

        self.cTrav = CollisionTraverser()

        self.ralphGroundRay = CollisionRay()
        self.ralphGroundRay.setOrigin(0, 0, 1000)
        self.ralphGroundRay.setDirection(0, 0, -1)
        self.ralphGroundCol = CollisionNode('ralphRay')
        self.ralphGroundCol.addSolid(self.ralphGroundRay)
        self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff())
        self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol)
        self.ralphGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler)
        #self.ralphGroundCol.show()

        base.cam.reparentTo(self.ralph)
        base.cam.setPos(0, 9, 7)
        self.floater2 = NodePath(PandaNode("floater2"))
        self.floater2.reparentTo(self.ralph)
        self.floater2.setZ(self.floater2.getZ() + 6)
        base.cam.lookAt(self.floater2)

        # Uncomment this line to see the collision rays
        #		self.ralphGroundColNp.show()
        #		self.camGroundColNp.show()

        #Uncomment this line to show a visual representation of the
        #collisions occuring
        #		self.cTrav.showCollisions(render)

        self.floater = NodePath(PandaNode("floater"))
        self.floater.reparentTo(render)

        taskMgr.add(self.move,
                    "movingTask",
                    extraArgs=[
                        mainClass, parserClass, mapLoaderClass,
                        modelLoaderClass
                    ])

    #Records the state of the arrow keys
    def setKey(self, key, value):
        self.keyMap[key] = value

    def move(self, mainClass, parserClass, mapLoaderClass, modelLoaderClass):
        # Get the time elapsed since last frame. We need this
        # for framerate-independent movement.
        elapsed = globalClock.getDt()

        # save ralph's initial position so that we can restore it,
        # in case he falls off the map or runs into something.

        startpos = self.ralph.getPos()

        # If a move-key is pressed, move ralph in the specified direction.

        if (self.keyMap["left"] != 0):
            self.ralph.setH(self.ralph.getH() + elapsed * 300)
        if (self.keyMap["right"] != 0):
            self.ralph.setH(self.ralph.getH() - elapsed * 300)
        if (self.keyMap["forward"] != 0):
            self.ralph.setY(self.ralph, -(elapsed * 50))  #25))
        if (self.keyMap["backward"] != 0):
            self.ralph.setY(self.ralph, +(elapsed * 20))

        if (self.keyMap["forward"] != 0) or (self.keyMap["left"] !=
                                             0) or (self.keyMap["right"] != 0):
            if self.isMoving is False:
                self.ralph.loop("run")
                self.isMoving = True

        elif (self.keyMap["backward"] != 0):
            if self.isMoving is False:
                self.ralph.stop()
                self.ralph.pose("walk", 5)
                self.isMoving = False

        else:
            if self.isMoving:
                self.ralph.stop()
                self.ralph.pose("walk", 5)
                self.isMoving = False

        # Now check for collisions.

        self.cTrav.traverse(render)

        # Adjust ralph's Z coordinate.  If ralph's ray hit terrain,
        # update his Z. If it hit anything else, or didn't hit anything, put
        # him back where he was last frame.

        entries = []
        for i in range(self.ralphGroundHandler.getNumEntries()):
            entry = self.ralphGroundHandler.getEntry(i)
            entries.append(entry)
        entries.sort(lambda x, y: cmp(
            y.getSurfacePoint(render).getZ(),
            x.getSurfacePoint(render).getZ()))

        if (len(entries) > 0) and (entries[0].getIntoNode().getName()[0:4]
                                   == "tile"):
            self.ralph.setZ(entries[0].getSurfacePoint(render).getZ())

        elif (len(entries) > 0) and (entries[0].getIntoNode().getName()[0:5]
                                     == "solid"):
            self.ralph.setPos(startpos)
            x = int(entries[0].getIntoNode().getName()
                    [len(entries[0].getIntoNode().getName()) -
                     6:len(entries[0].getIntoNode().getName()) - 4])
            y = int(entries[0].getIntoNode().getName()
                    [len(entries[0].getIntoNode().getName()) - 2:])
            if (mapLoaderClass.tileArray[y][x].drillTime != None):
                mainClass.changeTile(mapLoaderClass.tileArray[y][x], 0,
                                     parserClass, modelLoaderClass,
                                     mapLoaderClass)
        else:
            self.ralph.setPos(startpos)

        self.ralph.setP(0)
        return Task.cont
Example #16
0
class World(DirectObject):
    def __init__(self):
        base.win.setClearColor(Vec4(0, 0, 0, 1))

        # enable physics (and particle) engine

        self.throwMode = False
        self.freelook = False

        self.score = OnscreenText('0',
                                  pos=(-1.32, 0.9),
                                  fg=(1, 1, 1, 1),
                                  bg=(0, 0, 0, 0.5),
                                  scale=0.1,
                                  align=TextNode.ALeft)

        # Load the environment in which Eve will walk. Set its parent
        # to the render variable so that it is a top-lplayerl node.
        self.env = loader.loadModel('models/world/world.egg.pz')
        self.env.reparentTo(render)
        self.env.setPos(0, 0, 0)

        self.createCollisionHandlers()

        # Create an Actor instance for Eve. We also specify the animation
        # models that we want to use as a dictionary, where we can use to
        # keys to refer to the animations later on. The start point of Eve
        # is hardcoded in the world model somewhere, so we look that up.
        self.player = Eve('Eve', self,
                          self.env.find('**/start_point').getPos())
        #self.player.nodePath.setZ(self.player.nodePath.getZ() + 10)
        self.player.nodePath.reparentTo(render)

        # Create a floater object that always floats 2 units above Eve.
        # We make sure that it is attached to Eve by reparenting it to
        # Eve's object instance.
        self.floater = NodePath(PandaNode('floater'))
        self.floater.reparentTo(self.player.nodePath)
        self.floater.setZ(self.floater.getZ() + 2)

        # load baseball
        self.baseball = Baseball('baseball', self,
                                 self.player.nodePath.getPos())
        self.baseball.nodePath.reparentTo(render)
        self.player.pickUpItem(self.baseball)

        # Load the panda bear
        self.panda = Panda('panda', self, self.player.nodePath.getPos())
        self.panda.nodePath.reparentTo(render)

        # Disable controlling the camera using the mouse. Note that this does
        # not disable the mouse completely, it merely disables the camera
        # movement by mouse.
        base.disableMouse()

        self.hideMouseCursor()

        # Set the initial position for the camera as X, Y and Z values.
        base.camera.setPos(self.player.nodePath.getX(),
                           self.player.nodePath.getY() + 10, 2)

        # Disable modifier button compound events.
        base.mouseWatcherNode.setModifierButtons(ModifierButtons())
        base.buttonThrowers[0].node().setModifierButtons(ModifierButtons())

        # Register any control callbacks.
        self.accept('escape', sys.exit)
        self.accept('d', self.dropItem)
        self.accept('f', self.toggleFullscreen)

        self.accept('space', self.enterThrowMode)
        self.accept('space-up', self.leaveThrowMode)

        # Also make sure that we can, at any time, request the state (pressed
        # or not) for these keys.
        self.keys = keys.KeyStateManager()
        self.keys.registerKeys({
            'arrow_left': 'left',
            'arrow_right': 'right',
            'arrow_up': 'forward',
            'arrow_down': 'backward',
            'shift': 'shift',
            'r': 'reset'
        })

        self.mouse = mouse.MousePointerManager(0)

        # Schedule the move method to be executed in the game's main loop.
        taskMgr.add(self.update, 'update')

    def hideMouseCursor(self):
        props = WindowProperties()
        props.setCursorHidden(True)
        base.win.requestProperties(props)

    def toggleFullscreen(self):
        props = WindowProperties()
        props.setFullscreen(not base.win.getProperties().getFullscreen())
        base.win.requestProperties(props)

    def enableFreelook(self):
        self.freelook = True

        # Make sure we reset the MouseMovementManager's last known mouse position,
        # so we don't get a huge delta on the first attempt.
        self.mouse.reset()

        base.camera.setP(0)

    def disableFreelook(self):
        self.freelook = False

    def createCollisionHandlers(self):
        # Create a new collision traverser instance. We will use this to determine
        # if any collisions occurred after performing movement.
        self.cTrav = CollisionTraverser()

        camGroundRay = CollisionRay()
        camGroundRay.setOrigin(0, 0, 1000)
        camGroundRay.setDirection(0, 0, -1)
        camGroundCol = CollisionNode('camRay')
        camGroundCol.addSolid(camGroundRay)
        camGroundCol.setFromCollideMask(BitMask32.bit(0))
        camGroundCol.setIntoCollideMask(BitMask32.allOff())
        camGroundColNp = base.camera.attachNewNode(camGroundCol)
        self.camGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(camGroundColNp, self.camGroundHandler)

        # register the collision pusher
        self.pusher = CollisionHandlerPusher()

        # register collision event pattern names
        self.pusher.addInPattern('col-%fn-into')

    def update(self, task):
        # get the time passed since the last frame
        timePassed = globalClock.getDt()

        # update player
        self.player.forceMove(timePassed)
        self.panda.forceMove(timePassed)

        # Do collision detection. This iterates all the collider nodes
        self.cTrav.traverse(render)

        # check if player's move is valid
        self.player.validateMove()
        self.panda.validateMove()

        # Set the initial position for the camera as X, Y and Z values.
        base.camera.setPos(self.player.nodePath.getPos())

        if self.throwMode:
            # Position the camera a bit above the ground.
            base.camera.setZ(base.camera, 1.5)

            if self.freelook:
                mx, my = self.mouse.getDelta()

                h = -mx * 0.1
                p = -my * 0.1

                base.camera.setHpr(base.camera, h, p, 0)
                self.player.nodePath.setH(self.player.nodePath, h)
            else:
                # Set the heading, pitch and roll of the camera.
                base.camera.setHpr(self.player.nodePath.getHpr())
        else:
            # Set the heading, pitch and roll of the camera.
            base.camera.setHpr(self.player.nodePath.getHpr())

            # Position the camera somewhat behind the player.
            base.camera.setY(base.camera, 10)

            # Make sure the camera is above the ground.
            camGroundEntry = self.getGroundEntry(self.camGroundHandler)
            if camGroundEntry is not None and camGroundEntry.getIntoNode(
            ).getName() == 'terrain':
                base.camera.setZ(
                    camGroundEntry.getSurfacePoint(render).getZ() + 1.5)

            # Let the camera look at the floater object above Eve.
            base.camera.lookAt(self.floater)

        return Task.cont

    def dropItem(self):
        self.player.dropItem()

    def getGroundEntry(self, collisionHandler):
        # Put all the collision entries into a Python list so we can sort it,
        # properly.
        entries = []
        for i in range(collisionHandler.getNumEntries()):
            entries.append(collisionHandler.getEntry(i))

        # Sort the list by the collision points' Z values, making sure the
        # highest value ends up at the front of the list.
        entries.sort(lambda x, y: cmp(
            y.getSurfacePoint(render).getZ(),
            x.getSurfacePoint(render).getZ()))

        if len(entries) > 0:
            return entries[0]
        else:
            return None

    def enterThrowMode(self):
        self.throwMode = True
        self.player.enterStrafeMode()
        self.enableFreelook()

    def leaveThrowMode(self):
        self.throwMode = False
        self.player.leaveStrafeMode()
        self.disableFreelook()
Example #17
0
class Mode(object):
    """This is the base Mode class"""
    def __init__(self, game):
        self.name = "MODE"
        self.guiMediaPath = '../Packages/anw/gui/media/'
        self.alive = 1
        self.enableMouseCamControl = 1
        self.enableScrollWheelZoom = 1
        self.canSelectFlags = {}
        self.messagePositions = []
        self.selectTypes = []
        self.gui = []
        self.sims = []
        self.game = game
        self.depth = 20.0
        self.zoomCameraDepth = 10.0
        self.zoomCameraOutDepth = -10.0
        self.zoomSpeed = 5
        self.panSpeed = 1.0
        self.runningTasks = []
        if globals.serverMode == 0:
            self.setMyBackground()
            camera.setHpr(0,0,0)
        self.mainmenu = None
        self.scrollSpeed = 0.1

        if globals.serverMode == 0:
            self.setMousePicker()
            self.setCameraPosition()
        
        self.selector = None
        self.selector2 = None
        
        self.log = logging.getLogger('mode')
        
        self.entryFocusList = ('anw.gui.mainmenubuttons','anw.gui.industryvalue',
                                'anw.gui.cityindustry','anw.gui.weapondirection',
                                'anw.gui.scrollvalue','anw.gui.shipdesignvalue',
                                'anw.gui.systemmenu','anw.gui.tradevalue',
                                'anw.gui.designmenu','anw.gui.shipyardmenu', 'anw.gui.mimenu',
                                'anw.gui.textentry', 'anw.gui.marketsystemsellvalue', 
                                'anw.gui.sendcreditsvalue')
    
    def __getstate__(self):
        odict = self.__dict__.copy() # copy the dict since we change it
        del odict['log']             # remove stuff not to be pickled
        return odict

    def __setstate__(self,dict):
        log=logging.getLogger('mode')
        self.__dict__.update(dict)
        self.log=log
    
    def setMousePicker(self):
        self.picker = CollisionTraverser()
        self.pq = CollisionHandlerQueue()
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(BitMask32.bit(1))
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.picker.addCollider(self.pickerNP, self.pq)
        self.selectable = render.attachNewNode("selectable")

    def setCameraPosition(self):
        self.cameraPos = (camera.getX(), camera.getY(), camera.getZ())
        self.cameraMoving = 0
    
    def setCanSelectFlag(self, key):
        """Set the Flag"""
        self.clearAllCanSelectFlags()
        self.canSelectFlags[key] = 1
        
    def clearAllCanSelectFlags(self):
        """Clear any selection flags"""
        for key in self.canSelectFlags.keys():
            self.canSelectFlags[key] = 0
     
    def isAnyFlagSelected(self):
        """Return 1 if any flags are selected"""
        for key in self.canSelectFlags.keys():
            if self.canSelectFlags[key] == 1:
                return 1
        return 0
            
    def validateSelection(self):
        """Can something be selected right now"""
        if self.cameraMoving == 0:
            return 1
        else:
            return 0
        
    def removeMyGui(self, myGuiName):
        """Remove gui"""
        myGui = getattr(self, myGuiName)
        if myGui in self.gui:
            self.gui.remove(myGui)
        if myGui != None:
            myGui.destroy()
            setattr(self, myGuiName, None)
    
    def createMainMenu(self, key):
        self.mainmenu = mainmenubuttons.MainMenuButtons(self.guiMediaPath)
        self.mainmenu.setMyGame(self.game)
        self.mainmenu.setMyMode(self)
        self.mainmenu.enableLastButton(key)
        self.mainmenu.checkDisableButton(key)
        self.mainmenu.writeGameInfo()
        self.mainmenu.acceptSpaceBarKey()
        self.gui.append(self.mainmenu)
    
    def removeMainMenu(self):
        if self.mainmenu != None:
            self.mainmenu.destroyMe()
            self.mainmenu = None
    
    def centerCameraOnSim(self, sim):
        """Center the camera on the sim position"""
        self.game.app.disableMouseCamControl()
        camera.setPos(sim.getX(), camera.getY(), sim.getZ())
        camera.setHpr(0,0,0)
        if self.enableMouseCamControl == 1:
            self.game.app.enableMouseCamControl()
    
    def drawBox(self, x, y, width, height, color='guiblue1', lineWidth=0.15, glow=1):
        """Draw a box"""
        #LEFT
        myLine = line.Line(self.guiMediaPath,(x,y),(x,y+height), 'square_grey', lineWidth, glow)
        myLine.sim.setColor(globals.colors[color])
        self.gui.append(myLine)
        #TOP
        myLine = line.Line(self.guiMediaPath,(x,y+height),(x+width,y+height), 'square_grey', lineWidth, glow)
        myLine.sim.setColor(globals.colors[color])
        self.gui.append(myLine)
        #RIGHT
        myLine = line.Line(self.guiMediaPath,(x+width,y+height),(x+width,y), 'square_grey', lineWidth, glow)
        myLine.sim.setColor(globals.colors[color])
        self.gui.append(myLine)
        #BOTTOM
        myLine = line.Line(self.guiMediaPath,(x+width,y),(x,y), 'square_grey', lineWidth, glow)
        myLine.sim.setColor(globals.colors[color])
        self.gui.append(myLine)
    
    def stopCameraTasks(self):
        taskMgr.remove('zoomInCameraTask')
        taskMgr.remove('zoomOutCameraTask')
        self.cameraMoving = 0
        self.game.app.enableMouseCamControl()
        self.enableMouseCamControl=1
        
    def resetCamera(self):
        self.game.app.disableMouseCamControl()
        camera.setPos(self.cameraPos[0], self.zoomCameraOutDepth, self.cameraPos[2])
        # I don't really understand why this doesn't reset the view when having a planet selected and hitting spacebar?    
        camera.setHpr(0,0,0)

        if self.enableMouseCamControl == 1:
            self.game.app.enableMouseCamControl()
    
    def zoomInCamera(self):
        
        if camera.getY() <= self.zoomCameraDepth:
            self.game.app.disableMouseCamControl()
            taskMgr.add(self.zoomInCameraTask, 'zoomInCameraTask', extraArgs=[self.zoomCameraDepth])
            self.runningTasks.append('zoomInCameraTask')
    
    def zoomInCameraAmount(self, amount):
        """Zoom in Camera a certain amount specified"""
        depth = camera.getY()+amount
        self.game.app.disableMouseCamControl()
        taskMgr.add(self.zoomInCameraTask, 'zoomInCameraTask', extraArgs=[depth])
        self.runningTasks.append('zoomInCameraTask')
    
    def zoomInCameraTask(self, depth):
        """Zoom in the camera until its at depth"""
        y = camera.getY()
        if y + 0.1 >= depth: # or y >= 8.0:  # TODO: tacking this on will mess with the design screen but prevents you from zooming in too close everywhere else.  
            self.cameraMoving = 0
            if self.enableMouseCamControl == 1:
                self.game.app.enableMouseCamControl()
            camera.setY(y)    
            return Task.done
        else:
            camera.setY(y+self.getZoomSpeed(y, depth))
            self.cameraMoving = 1
            return Task.cont
        
    def getZoomSpeed(self, y, depth):
        """Make Camera zoom in faster if camera is further away"""
        diff = depth-y
        return diff/5.0
    
    def zoomOutCamera(self):
        if camera.getY() >= self.zoomCameraOutDepth:
            self.game.app.disableMouseCamControl()
            taskMgr.add(self.zoomOutCameraTask, 'zoomOutCameraTask', extraArgs=[self.zoomCameraOutDepth])
            self.runningTasks.append('zoomOutCameraTask')
            
    def zoomOutCameraAmount(self, amount):
        """Zoom out Camera a certain amount sepecified"""
        depth = camera.getY()-amount
        self.game.app.disableMouseCamControl()
        taskMgr.add(self.zoomOutCameraTask, 'zoomOutCameraTask', extraArgs=[depth])
        self.runningTasks.append('zoomOutCameraTask')
    
    def zoomOutCameraTask(self, depth):
        """Zoom out the camera until its at 0 Depth"""
        y = camera.getY()
        if y - 0.1 <= depth:
            self.cameraMoving = 0
            if self.enableMouseCamControl == 1:
                self.game.app.enableMouseCamControl()
            camera.setY(y)
            return Task.done
        else:
            camera.setY(y+self.getZoomSpeed(y, depth))
            self.cameraMoving = 1
            return Task.cont
    
    def panCameraLeft(self, amount):
        """Pan Camera"""
        pos = camera.getX()-amount
        self.game.app.disableMouseCamControl()
        taskMgr.add(self.panCameraLeftTask, 'panCameraLeftTask', extraArgs=[pos])
        self.runningTasks.append('panCameraLeftTask')
    
    def panCameraLeftTask(self, pos):
        """pan the camera to new position"""
        x = camera.getX()
        if x <= pos:
            self.cameraMoving = 0
            if self.enableMouseCamControl == 1:
                self.game.app.enableMouseCamControl()
            return Task.done
        else:
            camera.setX(x-self.panSpeed)
            self.cameraMoving = 1
            return Task.cont

    def panCameraRight(self, amount):
        """Pan Camera"""
        pos = camera.getX()+amount
        self.game.app.disableMouseCamControl()
        taskMgr.add(self.panCameraRightTask, 'panCameraRightTask', extraArgs=[pos])
        self.runningTasks.append('panCameraRightTask')
    
    def panCameraRightTask(self, pos):
        """pan the camera to new position"""
        x = camera.getX()
        if x >= pos:
            self.cameraMoving = 0
            if self.enableMouseCamControl == 1:
                self.game.app.enableMouseCamControl()
            return Task.done
        else:
            camera.setX(x+self.panSpeed)
            self.cameraMoving = 1
            return Task.cont
        
    def panCameraUp(self, amount):
        """Pan Camera"""
        pos = camera.getZ()+amount
        self.game.app.disableMouseCamControl()
        taskMgr.add(self.panCameraUpTask, 'panCameraUpTask', extraArgs=[pos])
        self.runningTasks.append('panCameraUpTask')
    
    def panCameraUpTask(self, pos):
        """pan the camera to new position"""
        z = camera.getZ()
        if z >= pos:
            self.cameraMoving = 0
            if self.enableMouseCamControl == 1:
                self.game.app.enableMouseCamControl()
            return Task.done
        else:
            camera.setZ(z+self.panSpeed)
            self.cameraMoving = 1
            return Task.cont

    def panCameraDown(self, amount):
        """Pan Camera"""
        pos = camera.getZ()-amount
        self.game.app.disableMouseCamControl()
        taskMgr.add(self.panCameraDownTask, 'panCameraDownTask', extraArgs=[pos])
        self.runningTasks.append('panCameraDownTask')
    
    def panCameraDownTask(self, pos):
        """pan the camera to new position"""
        z = camera.getZ()
        if z <= pos:
            self.cameraMoving = 0
            if self.enableMouseCamControl == 1:
                self.game.app.enableMouseCamControl()
            return Task.done
        else:
            camera.setZ(z-self.panSpeed)
            self.cameraMoving = 1
            return Task.cont
        
    def createSelector(self,type='select',speed=2.0):
        """Create selector for indication of selected objects"""
        self.selector = self.loadObject(type, scale=2, parent=render, transparency=True, pos=Point2(0,0), glow=1)
        self.selector.hide()
        ival = self.selector.hprInterval((speed), Vec3(0, 0, 360))
        ival.loop()
    
    def createSelector2(self,type='select',speed=2.0):
        """Create selector2 for indication of secondary selected objects"""
        self.selector2 = self.loadObject(type, scale=2, parent=render, transparency=True, pos=Point2(0,0), glow=1)
        self.selector2.hide()
        ival = self.selector2.hprInterval((speed), Vec3(0, 0, 360))
        ival.loop()
    
    def playSound(self, soundName):
        """Play a Sound based on soundName given, call app"""
        if globals.serverMode == 0:
            self.game.app.playSound(soundName)
    
    def askForHelp(self):
        """Ask the Server to analyse Player and provide help"""
        try:
            serverResult = self.game.server.askForHelp(self.game.authKey)
            if type(serverResult) == types.ListType:
                (message, self.game.myEmpire['help']) = serverResult
                self.modeMsgBox(message)
            else:
                self.modeMsgBox(serverResult)
        except:
            self.modeMsgBox('askForHelp->Connection to Server Lost')
        
    def assignSelector(self, myObj, scale):
        """create the Selector and assign to myObj at scale"""
        if self.selector == None:
            self.createSelector()
            self.selector.show()
            
        self.selector.setPos(myObj.getX(), myObj.getY(), myObj.getZ())
        self.selector.setScale(scale)
    
    def assignSelector2(self, myObj, scale):
        """create the Selector2 and assign to myObj at scale"""
        if self.selector2 == None:
            self.createSelector2()
            self.selector2.show()
            
        self.selector2.setPos(myObj.getX(), myObj.getY(), myObj.getZ())
        self.selector2.setScale(scale)
       
    ##def checkEndTurn(self):
        ##"""Do a Server Assesment of turn before ending the turn"""
        ##try:
            ##if 'EndTurn' in self.game.myEmpire['help']:
                ### turn not ended yet
                ##(serverResult, self.game.myEmpire['help']) = self.game.server.askForHelp(self.game.authKey)
                ##if serverResult == 'Server Assessment: WARNINGS:0, CRITICAL:0 (Check Mail for Assesment)':
                    ### server assessment is good, end the turn without asking
                    ##self.endMyTurn()
                ##else:
                    ### server assessment has not come back without warnings ask for confirmation
                    ##self.modeYesNoBox('%s - Do you still want to end your turn?' % serverResult, 'endturnYes', 'yesNoBoxNo')
            ##else:
                ### turn already ended, unend turn
                ##self.modeYesNoBox('Do you want to cancel your end turn?' , 'endturnYes', 'yesNoBoxNo')
        ##except:
            ##self.modeMsgBox('checkEndTurn->Connection to Server Lost, Login Again')
    
    def exitGame(self, doLogout=True):
        """Exit the game"""
        self.setEmpireDefaults(self.game.authKey)
        if doLogout:
            self.setLogout(self.game.authKey)
        self.alive = 0
        self.game.app.quit()
    
    def getCreditInfoFromServer(self):
        self.getEmpireUpdate(['CR'])
    
    def refreshCredit(self):
        """Ask the Server for an updated Credit Info"""
        self.mainmenu.updateCR()
    
    def getEmpireUpdate(self, listAttr):
        """Ask the Server for updated Empire info"""
        try:
            serverResult = self.game.server.getEmpireUpdate(self.game.authKey, listAttr)
            if type(serverResult) == types.StringType:
                self.modeMsgBox(serverResult)
            else:
                for key, value in serverResult.iteritems():
                    self.game.myEmpire[key] = value
        except:
            self.modeMsgBox('getEmpireUpdate->Connection to Server Lost')
    
    def getMailUpdate(self):
        """Ask the Server for any updated mail"""
        try:
            myMailDict = self.game.myEmpire['mailBox']
            serverResult = self.game.server.getMailUpdate(self.game.authKey, myMailDict.keys())
            if type(serverResult) == types.StringType:
                self.modeMsgBox(serverResult)
            else:
                for key, value in serverResult.iteritems():
                    myMailDict[key] = value
        except:
            self.modeMsgBox('getMailUpdate->Connection to Server Lost')
    
    def getGalaxyUpdate(self, listAttr):
        """Ask the Server for updated Galaxy info"""
        try:
            serverResult = self.game.server.getGalaxyUpdate(listAttr, self.game.authKey)
            if type(serverResult) == types.StringType:
                self.modeMsgBox(serverResult)
            else:
                for key, value in serverResult.iteritems():
                    self.game.myGalaxy[key] = value
        except:
            self.modeMsgBox('getGalaxyUpdate->Connection to Server Lost')
    
    def getSystemUpdate(self, listAttr, systemID):
        """Ask the Server for updated System info"""
        try:
            serverResult = self.game.server.getSystemUpdate(listAttr, systemID, self.game.authKey)
            if type(serverResult) == types.StringType:
                self.modeMsgBox(serverResult)
            else:
                mySystemDict = self.game.allSystems[systemID]
                for key, value in serverResult.iteritems():
                    mySystemDict[key] = value
        except:
            self.modeMsgBox('getSystemUpdate->Connection to Server Lost')
        
    def enterMode(self):
        """Enter the mode."""
        self.alive = 1
        self.setShortcuts()
    
    def setShortcuts(self):
        """Set the default mode shortcuts"""
        self.game.app.accept('mouse1', self.onMouse1Down)
        self.game.app.accept('mouse3', self.onMouse2Down)
        self.game.app.accept('space', self.onSpaceBarClear)
        if self.enableMouseCamControl == 1:
            self.game.app.accept('wheel_up', self.onMouseWheelUp)
            self.game.app.accept('wheel_down', self.onMouseWheelDown)
        
    def exitMode(self):
        """Exit the mode"""
        self.removeMySims()
        self.removeAllGui()
        self.game.app.ignoreAll()
        self.removeAllTasks()
        self.alive = 0
    
    def removeAllTasks(self):
        """Remove and Stop any tasks running"""
        for taskName in self.runningTasks:
            taskMgr.remove(taskName)

    def removeMySims(self):
        """Remove all sims in mode"""
        for sim in self.sims:
            try:
                sim.destroy()
            except:
                sim.removeNode()
    
    def removeAllGui(self):
        """Remove all DirectGUI"""
        for gui in self.gui:
            gui.destroy()
    
    def setPlanePickable(self, obj, dictName):
        """Set the plane model itself to be collideable with the mouse ray"""
        obj.sim.reparentTo(self.selectable)
        obj.sim.find('**/pPlane1').node().setIntoCollideMask(BitMask32.bit(1))
        obj.sim.find('**/pPlane1').node().setTag(dictName, obj.id)
    
    def setSpherePickable(self, obj, dictName):
        """Set the sphere model itself to be collideable with the mouse ray"""
        obj.sim.reparentTo(self.selectable)
        obj.sim.find('**/pSphere1').node().setIntoCollideMask(BitMask32.bit(1))
        obj.sim.find('**/pSphere1').node().setTag(dictName, obj.id)
    
    def setMySelector(self, x, y, z, scale):
        """Show selector if it is not in current position else return false"""
        selectorPos = (self.selector.getX(), self.selector.getY(), self.selector.getZ())
        if selectorPos != (x,y,z):
            self.selector.setPos(x,y,z)
            self.selector.show()
            self.selector.setScale(scale)
            return 1
        else:
            self.selector.setPos(-1,-1,-1)
            return 0
        #self.enableScrollWheelZoom = 0
    
    def getListButton(self, id, myScrolledList):
        """Return Button selected from buttonList gui based on id"""
        for button in myScrolledList.buttonsList:
            if button['extraArgs'][1] == id:
                return button
        
    def setMySelector2(self, x, y, z, scale):
        """Show selector2 if it is not in current position else return false"""
        selectorPos = (self.selector2.getX(), self.selector2.getY(), self.selector2.getZ())
        if selectorPos != (x,y,z):
            self.selector2.setPos(x,y,z)
            self.selector2.show()
            self.selector2.setScale(scale)
            return 1
        else:
            self.selector2.setPos(-1,-1,-1)
            return 0
        #self.enableScrollWheelZoom = 0
    
    def hideMySelector(self):
        """Hide the selector, move its position"""
        self.selector.setPos(-1,-1,-1)
        self.selector.hide()
        if self.selector2 != None:
            self.selector2.hide()
    
    def onMouse1Down(self):
        """Allow dynamic picking of an object within mode"""
        #Check to see if we can access the mouse. We need it to do anything else
        if base.mouseWatcherNode.hasMouse():
            #get the mouse position
            mpos = base.mouseWatcherNode.getMouse()
         
            #Set the position of the ray based on the mouse position
            self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
            
            #Do the actual collision pass (Do it only on the selectable for
            #efficiency purposes)
            self.picker.traverse(self.selectable)
            if self.pq.getNumEntries() > 0:
                #if we have hit something, sort the hits so that the closest
                #is first, and highlight that node
                self.pq.sortEntries()
                for selectable in self.selectTypes:
                    name = self.pq.getEntry(0).getIntoNode().getTag(selectable)
                    if name != '':
                        self.clearAnyGui()
                        mySelectedDict = getattr(self, selectable)
                        mySelected = mySelectedDict[name]
                        myMethod = getattr(self, '%sSelected' % selectable)
                        if self.validateSelection():
                            myMethod(mySelected)
                        break
                    
    def onMouseWheelUp(self):
        """ zoom out """
        if self.enableScrollWheelZoom:
            self.stopCameraTasks()
            self.zoomInCameraAmount(20.0)
        
    def onMouseWheelDown(self):
        """ zoom in """
        if self.enableScrollWheelZoom:
            self.stopCameraTasks()
            self.zoomOutCameraAmount(20.0)
        
    def onMouse2Down(self):
        """clear"""
        self.onSpaceBarClear()
    
    def onSpaceBarClear(self):
        """Space bar should reset the view in the mode"""
        if self.validateSelection():
            self.resetCamera()
            self.clearMouseSelection()
            self.zoomOutCamera()
            self.setShortcuts()
            self.enableScrollWheelZoom = 1
    
    def clearMouseSelection(self):
        """Clear mouse selection before selecting something new"""
        pass

    def clearAnyGui(self):
        pass
    
    def update(self, interval):
        """update the mode, return the status, 0 means stop game"""
        return self.alive
        
    def setMyBackground(self):
        """Set the Background of mode"""
        base.setBackgroundColor(globals.colors['guiblue3'])
        
    def setEmpireDefaults(self, clientKey):
        """Read the defaults currently set and change them in the database"""
        try:
            # setup attributes to send to server
            defaults = ['viewIndustry', 'viewMilitary', 'viewResources', 'viewTradeRoutes']
            d = {}
            for item in defaults:
                d[item] = self.game.myEmpire[item]
            serverResult = self.game.server.setEmpire(clientKey, d)
            if serverResult == 1:
                print 'Setup Empire Defaults Success'
            else:
                self.modeMsgBox(serverResult)
        except:
            self.modeMsgBox('SetEmpireDefaults->Connection to Server Lost, Login Again')

    def setEmpireValues(self, dValues):
        """Update Empire with d = key: empire attribute name,
        value = new value"""
        try:
            serverResult = self.game.server.setEmpire(self.game.authKey, dValues)
            if serverResult == 1:
                for key, value in dValues.iteritems():
                    self.game.myEmpire[key] = value
                print 'Empire Update Success'
            else:
                self.modeMsgBox(serverResult)
        except:
            self.modeMsgBox('setEmpireValues->Connection to Server Lost, Login Again')

    def setLogout(self, clientKey):
        """Send a Logout Request to the Server"""
        try:
            serverResult = self.game.server.logout(clientKey)
            if serverResult == 1:
                print 'Logout Successful, Exit Program'
            else:
                self.modeMsgBox(serverResult)
        except:
            self.modeMsgBox('setLogout->Connection to Server Lost, Login Again')
    
    def submitDesign(self, name):
        """Take Ship Design and submit it to Server for verification and storage"""
        (oldName, hullID, compDict, weaponDict) = self.myShipDesign.getMyDesign()
        dOrder = {'name':name, 'hullID':hullID, 'compDict':compDict, 'weaponDict':weaponDict}
        try:
            serverResult = self.game.server.addShipDesign(self.game.authKey, dOrder)
            if type(serverResult) == types.StringType:
                self.modeMsgBox(serverResult)
            else:
                # design has been accepted by server, retrieve design ID and add to client
                (ID,name) = serverResult
                self.game.shipDesigns[ID] = (name, hullID, compDict, weaponDict)
                self.getEmpireUpdate(['designsLeft'])
        except:
            self.modeMsgBox('submitDesign->Connection to Server Lost, Login Again')
      
    def destroyTempFrames(self):
        """Destroy any Temp Frames"""
        for frame in self.tempFrames:
            frame.destroy()
        self.tempFrames = []
    
    def modeMsgBox(self, messageText):
        """Create a message for the user"""
        self.createMessage(messageText)
    
    def createMessage(self, text):
        """Create a new message for user"""
        myMessage = fadingtext.FadingText(self.guiMediaPath, text, self.messagePositions)
        self.messagePositions.append(myMessage.getMyPosition())
        self.playSound('beep03')
    
    def writeToScreen(self, myText, x, z, scale=0.2, 
                      color='default', font=3, wordwrap=10):
        if color == 'default':
            color = Vec4(.1,.1,.8,.8)
        text = textonscreen.TextOnScreen(self.guiMediaPath, myText, scale,font=3)
        text.writeTextToScreen(x, self.depth, z, wordwrap=wordwrap)
        text.setColor(color)
        self.gui.append(text)
    
    def loadObject(self, tex=None, pos='default', depth=55, scale=1,
               transparency=True, parent='cam', model='plane', glow=0):
        if pos == 'default':
            pos = Point2(0,0)
        if parent == 'cam':
            parent = camera
        scaleX = 187.5
        scaleZ = 117.1875
        obj = loader.loadModelCopy('%s%s' % (self.guiMediaPath, model)) #default object uses the plane model
        if parent:
            obj.reparentTo(parent)              #Everything is parented to the camera so
                                            #that it faces the screen
        obj.setPos(Point3(pos.getX(), depth, pos.getY())) #Set initial position
        obj.setSx(scaleX)
        obj.setSz(scaleZ)
        obj.setBin("unsorted", 0)           #This tells Panda not to worry about the
                                            #order this is drawn in. (it prevents an
                                            #effect known as z-fighting)
        if transparency: obj.setTransparency(1) #All of our objects are trasnparent
        if tex:
            tex = loader.loadTexture('%s%s.png' % (self.guiMediaPath, tex)) #Load the texture
            obj.setTexture(tex, 1)                           #Set the texture
      
        self.sims.append(obj)
        obj.setShaderInput('glow',Vec4(glow,0,0,0),glow)
        return obj

    def onEntryFocus(self):
        """When a text Entry is in focus disable all shortcut keys"""
        for gui in self.gui:
            if gui.__module__ in self.entryFocusList:
                gui.ignoreShortcuts()
    
    def onEntryOutFocus(self):
        """When an text Entry is out of focus enable all shortcut keys"""
        for gui in self.gui:
            if gui.__module__ in self.entryFocusList:
                gui.setShortcuts()
Example #18
0
class AreaMapper(object):
    def __init__(self, environment):
        '''
        Create a map of the free space in a given area.
        '''
        self.csRadius = 1
        self.csHeight = 2
        self.avatarRadius = 1.4
        self.cs = CollisionSphere(0, 0, 0, 1)

        self.csNode = CollisionNode("AreaMapperCollisionSphere")
        self.csNode.setFromCollideMask(OTPGlobals.WallBitmask)
        self.csNode.setIntoCollideMask(BitMask32.allOff())
        self.csNode.addSolid(self.cs)

        self.environment = environment

        self.csNodePath = self.environment.getTop().attachNewNode(self.csNode)

        self.floorRay = CollisionRay()
        self.floorRay.setDirection(0, 0, -1)
        self.floorRay.setOrigin(0, 0, 0)

        self.floorRayNode = CollisionNode("AreaMapperFloorRay")
        self.floorRayNode.setFromCollideMask(OTPGlobals.FloorBitmask)
        self.floorRayNode.setIntoCollideMask(BitMask32.allOff())
        self.floorRayNode.addSolid(self.floorRay)

        self.floorRayNodePath = self.environment.getTop().attachNewNode(
            self.floorRayNode)

        self.chq = CollisionHandlerQueue()
        self.traverser = CollisionTraverser()

        self.startX = 0
        self.startY = 0
        self.startZ = 0

        self.frontierSquares = {(0, 0): 1}
        self.frontierSquaresQueue = [(0, 0)]
        self.walkableSquares = {(0, 0): 1}
        self.blockedSquares = {}

        self.setSquareSize(2)
        self.startAtPlayerSpawn()

        self.visGeom = None
        self.visGN = None
        self.visNodePath = None

        self.triVertexLookup = {}
        self.triList = []

        self.minX = 500
        self.maxX = -500
        self.minY = 500
        self.maxY = -500

        self.quadTree = QuadTree(width=1024)
        self.squares = []

        self.runDiscovery(100000)

        self._subdivide()

        #self._fixZValues()

        self.csNodePath.removeNode()
        self.floorRayNodePath.removeNode()

##     def _unstashEnvironment(self):
##         # Would be nice if we could just do this  :(
##         #for np in self.environment.findAllMatches("**/+CollisionNode;+s"):
##         #    np.unstash()
##         b = self.environment.builder
##         for s in b.sections.values():
##             s.unstash()
##         for o in b.largeObjects.values():
##             o.unstash()

    def setStart(self, x, y):
        self.startX = x
        self.startY = y
        self.startZ = self.findFloor(x, y)

    def startAtLocalAvatar(self):
        startPos = localAvatar.getPos(self.environment)
        self.setStart(startPos.getX(), startPos.getY())

    def startAtPlayerSpawn(self):
        # XXX Bleugh, this is really pirates-specific.  Nasty.
        for spawnPt in self.environment.world.getAllPlayerSpawnPts():
            parentDoId = self.environment.world.uidMgr.getDoId(spawnPt[1])
            if parentDoId == self.environment.doId:
                # Sweet, we found a spawn point for this grid's gamearea.  Use it!

                z = self.findFloor(spawnPt[0][0], spawnPt[0][1])
                if not self.isSphereBlocked(spawnPt[0][0], spawnPt[0][1], z):
                    self.setStart(spawnPt[0][0], spawnPt[0][1])
                    return

        raise "No player spawn points found for the given game area!  D:"

    def setSquareSize(self, size):
        self.squareSize = size
        self.csRadius = math.sqrt(
            2 * (self.squareSize * self.squareSize / 4)) + self.avatarRadius
        self.csNodePath.setScale(self.environment, self.csRadius,
                                 self.csRadius, self.csRadius)
        self.csHeight = self.csRadius * 2

    def findFloor(self, x, y):
        self.floorRayNodePath.setPos(self.environment, x, y, 50000)

        self.chq.clearEntries()

        self.traverser.clearColliders()
        self.traverser.addCollider(self.floorRayNodePath, self.chq)
        self.traverser.traverse(self.environment)

        highestZ = -50000

        for e in self.chq.getEntries():
            assert e.hasInto()
            assert e.getInto().isTangible()
            assert e.hasSurfacePoint()

            z = e.getSurfacePoint(self.environment).getZ()

            if z > highestZ:
                highestZ = z

        return highestZ

    def isSphereBlocked(self, x, y, z):
        if z < self.csHeight:
            return True

        self.csNodePath.setPos(self.environment, x, y, z)

        self.chq.clearEntries()

        self.traverser.clearColliders()
        self.traverser.addCollider(self.csNodePath, self.chq)
        self.traverser.traverse(self.environment)

        for entry in self.chq.getEntries():
            if entry.hasInto():
                if entry.getInto().isTangible():
                    return True

        return False

    def _neighbors(self, x, y):
        return [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]

    def _explore(self, p):
        x, y = p
        x1 = self.startX + self.squareSize * x
        y1 = self.startY + self.squareSize * y
        z1 = self.findFloor(x1, y1)
        if self.isSphereBlocked(x1, y1, z1 + self.csHeight):
            self.blockedSquares[p] = z1
            return
        else:
            self.walkableSquares[p] = z1
            self.quadTree.fill(x, y)
            for n in self._neighbors(x, y):
                if not (n in self.frontierSquares or n in self.walkableSquares
                        or n in self.blockedSquares):
                    self.frontierSquares[n] = 1
                    self.frontierSquaresQueue.append(n)

    def _exploreFrontier(self):
        if len(self.frontierSquaresQueue) == 0:
            assert len(self.frontierSquares.keys()) == 0
            return 0
        else:
            qlen = len(self.frontierSquaresQueue)
            for i in xrange(qlen):
                p = self.frontierSquaresQueue.pop(0)
                del self.frontierSquares[p]
                self._explore(p)
            return qlen

    def runDiscovery(self, maxSquares):
        print "Discovering walkable space (this will take 30-60 seconds)..."
        #self._unstashEnvironment()
        squaresExplored = 1

        self.walkableSquares[(0, 0)] = self.findFloor(self.startX, self.startY)

        while (squaresExplored < maxSquares) and (len(
                self.frontierSquaresQueue) > 0):
            squaresExplored += self._exploreFrontier()

##     def visualize(self):
##         gFormat = GeomVertexFormat.getV3cp()
##         self.vertexData = GeomVertexData("OMGVERTEXDATA", gFormat, Geom.UHDynamic)
##         self.vertexWriter = GeomVertexWriter(self.vertexData, "vertex")
##         self.colorWriter = GeomVertexWriter(self.vertexData, "color")

##         numVerts = 0

##         for xa,ya,xb,yb in self.squares:
##             x1 = self.startX + self.squareSize*(xa) - self.squareSize*0.5
##             y1 = self.startY + self.squareSize*(ya) - self.squareSize*0.5

##             x2 = self.startX + self.squareSize*(xb) + self.squareSize*0.5
##             y2 = self.startY + self.squareSize*(yb) + self.squareSize*0.5

##             self.vertexWriter.addData3f(x1,y1,self.findFloor(x1,y1)+0.1)
##             self.colorWriter.addData4f(0.0, 1.0, 0.0, 0.5)

##             self.vertexWriter.addData3f(x2,y1,self.findFloor(x2,y1)+0.1)
##             self.colorWriter.addData4f(0.0, 1.0, 0.0, 0.5)

##             self.vertexWriter.addData3f(x2,y2,self.findFloor(x2,y2)+0.1)
##             self.colorWriter.addData4f(0.0, 1.0, 0.0, 0.5)

##             self.vertexWriter.addData3f(x1,y2,self.findFloor(x1,y2)+0.1)
##             self.colorWriter.addData4f(0.0, 1.0, 0.0, 0.5)

##             numVerts += 4

##         print "NUMVERTS: ", numVerts

##         self.pointVis = GeomLinestrips(Geom.UHStatic)

##         for i in xrange(numVerts/4):
##             self.pointVis.addVertex(i*4)
##             self.pointVis.addVertex(i*4+1)
##             self.pointVis.addVertex(i*4+2)
##             self.pointVis.addVertex(i*4+3)
##             self.pointVis.addVertex(i*4)
##             self.pointVis.closePrimitive()

##         self.visGeom = Geom(self.vertexData)
##         self.visGeom.addPrimitive(self.pointVis)

##         self.visGN = GeomNode("NavigationGridVis")
##         self.visGN.addGeom(self.visGeom)

##         self.visNodePath = self.environment.attachNewNode(self.visGN)

##         self.visNodePath.setTwoSided(True)
##         self.visNodePath.setRenderModeThickness(4)
##         #self.visNodePath.setTransparency(1)

# ---------- Begin Triangulation Code ------------

##     def _addTriVertex(self,x,y):
##         '''
##         lookup[(x,y)] is a reference to the vert located to the UPPER-LEFT of grid square (x,y)
##         '''
##         if (x,y) not in self.gridCoordToVertexId:
##             vId = self.vertexCounter
##             self.vertexCounter += 1

##             self.gridCoordToVertexId[(x,y)] = vId

##             x1 = self.startX + self.squareSize*x - (0.5 * self.squareSize)
##             y1 = self.startY + self.squareSize*y - (0.5 * self.squareSize)
##             z1 = self.findFloor(x1,y1)
##             self.vertexIdToXYZ[vId] = (x1,y1,z1)

##             self.vertexToTris[vId] = []

##         return self.gridCoordToVertexId[(x,y)]

##     def _triangulateGridSquare(self,x,y,left=True):
##         a = self._addTriVertex(x,y)
##         b = self._addTriVertex(x+1,y)
##         c = self._addTriVertex(x+1,y+1)
##         d = self._addTriVertex(x,y+1)

##         if x < self.minX:
##             self.minX = x
##         if x > self.maxX:
##             self.maxX = x
##         if y < self.minY:
##             self.minY = y
##         if y > self.maxY:
##             self.maxY = y

##         if left:
##             self.triToVertices[self.triCounter] = [a,b,d]
##             self.triToAngles[self.triCounter] = [90,45,45]

##             self.triToVertices[self.triCounter+1] = [b,c,d]
##             self.triToAngles[self.triCounter+1] = [45,90,45]

##             self.vertexToTris[a].append(self.triCounter)
##             self.vertexToTris[b].append(self.triCounter)
##             self.vertexToTris[b].append(self.triCounter+1)
##             self.vertexToTris[c].append(self.triCounter+1)
##             self.vertexToTris[d].append(self.triCounter)
##             self.vertexToTris[d].append(self.triCounter+1)
##         else:
##             self.triToVertices[self.triCounter] = [a,b,c]
##             self.triToAngles[self.triCounter] = [45,90,45]

##             self.triToVertices[self.triCounter+1] = [a,c,d]
##             self.triToAngles[self.triCounter+1] = [45,45,90]

##             self.vertexToTris[a].append(self.triCounter)
##             self.vertexToTris[a].append(self.triCounter+1)
##             self.vertexToTris[b].append(self.triCounter)
##             self.vertexToTris[c].append(self.triCounter)
##             self.vertexToTris[c].append(self.triCounter+1)
##             self.vertexToTris[d].append(self.triCounter+1)

##         self.triCounter += 2

##     def countCruft(self):
##         count = 0
##         for s in self.squares:
##             if (s[0] == s[2]) and (s[1] == s[3]):
##                 x = s[0]
##                 y = s[1]
##                 numNeighbors = 0
##                 for (x1,y1) in [(x+1,y),(x-1,y),(x,y+1),(x,y-1)]:
##                     if (x1,y1) in self.walkableSquares:
##                         numNeighbors += 1
##                 if numNeighbors < 3:
##                     count += 1
##         return count

##     def killCruft(self):
##         for i in xrange(len(self.squares)):
##             s = self.squares[i]
##             if (s[0] == s[2]) and (s[1] == s[3]):
##                 x = s[0]
##                 y = s[1]
##                 numNeighbors = 0
##                 for (x1,y1) in [(x+1,y),(x-1,y),(x,y+1),(x,y-1)]:
##                     if (x1,y1) in self.walkableSquares:
##                         numNeighbors += 1
##                 if numNeighbors < 3:
##                     self.squares[i] = None

##         self.squares = [s for s in self.squares if s != None]

    def _addVertexByGridCoords(self, x, y):
        '''
        lookup[(x,y)] is a reference to the vert located at (-0.5,-0.5) from grid square (x,y)
        '''
        if (x, y) not in self.gridCoordToVertexId:
            vId = self.vertexCounter
            self.vertexCounter += 1

            self.gridCoordToVertexId[(x, y)] = vId

            x1 = self.startX + self.squareSize * x - (0.5 * self.squareSize)
            y1 = self.startY + self.squareSize * y - (0.5 * self.squareSize)
            z1 = self.findFloor(x1, y1)
            self.vertexIdToXYZ[vId] = (x1, y1, z1)

            self.vertToPolys[vId] = []

        return self.gridCoordToVertexId[(x, y)]

    def _addOpenSquare(self, gridX1, gridY1, gridX2, gridY2):
        curSpot = [gridX1, gridY1]

        verts = []
        angles = []

        while curSpot[0] <= gridX2:
            verts.append(self._addVertexByGridCoords(curSpot[0], curSpot[1]))
            if curSpot[0] == gridX1:
                angles.append(90)
            else:
                angles.append(180)
            self.vertToPolys[verts[-1]].append(self.polyCounter)
            curSpot[0] += 1

        while curSpot[1] <= gridY2:
            verts.append(self._addVertexByGridCoords(curSpot[0], curSpot[1]))
            if curSpot[1] == gridY1:
                angles.append(90)
            else:
                angles.append(180)
            self.vertToPolys[verts[-1]].append(self.polyCounter)
            curSpot[1] += 1

        while curSpot[0] > gridX1:
            verts.append(self._addVertexByGridCoords(curSpot[0], curSpot[1]))
            if curSpot[0] == gridX2 + 1:
                angles.append(90)
            else:
                angles.append(180)
            self.vertToPolys[verts[-1]].append(self.polyCounter)
            curSpot[0] -= 1

        while curSpot[1] > gridY1:
            if curSpot[1] == gridY2 + 1:
                angles.append(90)
            else:
                angles.append(180)
            verts.append(self._addVertexByGridCoords(curSpot[0], curSpot[1]))
            self.vertToPolys[verts[-1]].append(self.polyCounter)
            curSpot[1] -= 1

        self.polyToVerts[self.polyCounter] = verts
        self.polyToAngles[self.polyCounter] = angles
        self.polyCounter += 1

    def _subdivide(self):
        print "Growing squares..."
        self.vertexCounter = 0
        self.polyCounter = 0

        self.gridCoordToVertexId = {}
        self.vertexIdToXYZ = {}

        self.polyToVerts = {}
        self.polyToAngles = {}
        self.vertToPolys = {}

        self.squares = self.quadTree.squarify()

        for (gridX1, gridY1, gridX2, gridY2) in self.squares:
            self._addOpenSquare(gridX1, gridY1, gridX2, gridY2)
Example #19
0
class NavMesh(object):

    notify = directNotify.newCategory("NavMesh")

    def __init__(self, filepath=None, filename=None):
        if filename is not None:
            self._initFromFilename(filepath, filename)

    def initFromPolyData(self, polyToVerts, vertToPolys, polyToAngles,
                         vertexCoords, environmentHash):
        '''
        Initialize the mesh from a set of polygons.

        polyToVerts:     Dictionary mapping a polygon ID to a set of N vertex IDs
        vertToPolys:     Dictionary mapping a vertex ID to a set of poly IDs (of every poly that includes it)
        polyToAngles:    Dictionary mapping a polygon ID to a set of N angles (in vertex order)
        vertexCoords:    Dictionary mapping a vertex ID to the coordinates of the vertex in worldspace
        environmentHash: Hash value derived from the same collision geometry as the other arguments.  See AreaMapper.getEnvironmentHash().
        '''
        self.polyToVerts = polyToVerts
        self.vertToPolys = vertToPolys
        self.polyToAngles = polyToAngles
        self.vertexCoords = vertexCoords
        self.environmentHash = environmentHash

        self.connectionLookup = {}

        self.connections = []

        self._discoverInitialConnectivity()

        self.optimizeMesh()

    def visualize(self,
                  parentNodePath,
                  highlightVerts=[],
                  pathVerts=[],
                  visitedVerts=[]):
        '''
        XXX Should move this into a product-specific class.
        '''
        gFormat = GeomVertexFormat.getV3cp()
        self.visVertexData = GeomVertexData("OMGVERTEXDATA2", gFormat,
                                            Geom.UHDynamic)
        self.visVertexWriter = GeomVertexWriter(self.visVertexData, "vertex")
        self.visVertexColorWriter = GeomVertexWriter(self.visVertexData,
                                                     "color")

        vertToWriterIndex = {}
        currIndex = 0

        for v in self.vertexCoords.keys():
            vertToWriterIndex[v] = currIndex
            x = self.vertexCoords[v][0]
            y = self.vertexCoords[v][1]
            z = self.vertexCoords[v][2]
            self.visVertexWriter.addData3f(x, y, z + 0.5)
            if v in highlightVerts:
                self.visVertexColorWriter.addData4f(1.0, 0.0, 0.0, 1.0)
            elif v in visitedVerts:
                self.visVertexColorWriter.addData4f(0.0, 0.0, 1.0, 1.0)
            else:
                self.visVertexColorWriter.addData4f(1.0, 1.0, 0.0, 1.0)
            currIndex += 1

        pathOffsetIntoIndex = currIndex

        for v in pathVerts:
            self.visVertexWriter.addData3f(v[0], v[1], v[2] + 0.5)
            self.visVertexColorWriter.addData4f(0.0, 1.0, 0.0, 1.0)
            currIndex += 1

        lines = GeomLinestrips(Geom.UHStatic)

        for p in self.polyToVerts.keys():
            for v in self.polyToVerts[p]:
                lines.addVertex(vertToWriterIndex[v])
            lines.addVertex(vertToWriterIndex[self.polyToVerts[p][0]])
            lines.closePrimitive()

        if len(pathVerts) > 0:
            for i in xrange(len(pathVerts)):
                lines.addVertex(pathOffsetIntoIndex + i)
            lines.closePrimitive()

        self.visGeom = Geom(self.visVertexData)
        self.visGeom.addPrimitive(lines)

        self.visGN = GeomNode("NavMeshVis")
        self.visGN.addGeom(self.visGeom)

        self.visNodePath = parentNodePath.attachNewNode(self.visGN)

        self.visNodePath.setTwoSided(True)

    def _discoverInitialConnectivity(self):
        print "Building initial connectivity graph..."
        for pId in self.polyToVerts.keys():
            verts = self.polyToVerts[pId]

            numVerts = len(verts)
            candidates = []
            neighborPolys = []

            for v in verts:
                candidates += [
                    p for p in self.vertToPolys[v]
                    if (p not in candidates) and (p != pId)
                ]

            for vNum in xrange(numVerts):
                neighbor = [p for p in candidates if ((verts[vNum] in self.polyToVerts[p]) and \
                                                      (verts[(vNum+1)%numVerts] in self.polyToVerts[p]))]
                if len(neighbor) == 0:
                    neighborPolys.append(None)
                elif len(neighbor) == 1:
                    neighborPolys.append(neighbor[0])
                else:
                    raise "Two neighbors found for the same edge?!?!"

            self.connectionLookup[pId] = neighborPolys

    # --------- Begin stitching code ---------

    def _attemptToMergePolys(self, polyA, polyB):
        newVerts = []
        newAngles = []
        newConnections = []

        vertsA = self.polyToVerts[polyA]
        vertsB = self.polyToVerts[polyB]

        lenA = len(vertsA)
        lenB = len(vertsB)

        anglesA = self.polyToAngles[polyA]
        anglesB = self.polyToAngles[polyB]

        sharedVerts = [v for v in vertsA if (v in vertsB)]

        locA = 0

        while vertsA[locA] not in sharedVerts:
            locA += 1

        while vertsA[locA] in sharedVerts:
            locA = (locA - 1) % lenA

        locA = (locA + 1) % lenA

        CCWmost = vertsA[locA]
        CCWmostLocA = locA

        while vertsA[locA] in sharedVerts:
            locA = (locA + 1) % lenA

        locA = (locA - 1) % lenA

        CWmost = vertsA[locA]
        CWmostLocA = locA

        # Convexity Check.
        # Verify that removing the edge preserves convexity and bail out if not.

        locA = 0
        locB = 0
        while vertsA[locA] != CCWmost:
            locA += 1
        while vertsB[locB] != CCWmost:
            locB += 1
        CCWmostAngleSum = anglesA[locA] + anglesB[locB]
        CCWmostLocB = locB
        if CCWmostAngleSum > 180:
            return False

        locA = 0
        locB = 0
        while vertsA[locA] != CWmost:
            locA += 1
        while vertsB[locB] != CWmost:
            locB += 1
        CWmostAngleSum = anglesA[locA] + anglesB[locB]
        if CWmostAngleSum > 180:
            return False

        # We've found the CW-most vert of the shared edge.
        # Now walk A clockwise until we hit the CCW-most vert of the shared edge.

        newVerts.append(CWmost)
        newAngles.append(CWmostAngleSum)
        newConnections.append(self.connectionLookup[polyA][locA])
        locA = (locA + 1) % lenA

        while vertsA[locA] != CCWmost:
            newVerts.append(vertsA[locA])
            newAngles.append(anglesA[locA])
            newConnections.append(self.connectionLookup[polyA][locA])
            locA = (locA + 1) % lenA

        # Now we've hit the CCW-most vert of the shared edge.
        # Walk B clockwise until we get back to the CW-most vert of the shared edge.

        locB = CCWmostLocB

        newVerts.append(CCWmost)
        newAngles.append(CCWmostAngleSum)
        neighbor = self.connectionLookup[polyB][locB]
        newConnections.append(neighbor)
        if neighbor is not None:
            for i in xrange(len(self.connectionLookup[neighbor])):
                if self.connectionLookup[neighbor][i] == polyB:
                    self.connectionLookup[neighbor][i] = polyA

        locB = (locB + 1) % lenB

        while vertsB[locB] != CWmost:
            newVerts.append(vertsB[locB])
            newAngles.append(anglesB[locB])
            neighbor = self.connectionLookup[polyB][locB]
            newConnections.append(neighbor)
            if neighbor is not None:
                for i in xrange(len(self.connectionLookup[neighbor])):
                    if self.connectionLookup[neighbor][i] == polyB:
                        self.connectionLookup[neighbor][i] = polyA
            locB = (locB + 1) % lenB

        # We've added every vertex, its proper angle, and connectivity info
        # to the new polygon.  Now replace A with the new guy and remove B.

        self.polyToVerts[polyA] = newVerts
        self.polyToAngles[polyA] = newAngles
        self.connectionLookup[polyA] = newConnections

        # Make sure we have vertex->poly pointers for all the new verts we added to A.
        for v in newVerts:
            if polyA not in self.vertToPolys[v]:
                self.vertToPolys[v].append(polyA)

        # Clean up all of B's old vertices.
        for v in vertsB:
            self.vertToPolys[v].remove(polyB)
            if len(self.vertToPolys[v]) == 0:
                # No one's using this vertex anymore, remove it
                del self.vertToPolys[v]
                del self.vertexCoords[v]

        del self.polyToVerts[polyB]
        del self.polyToAngles[polyB]
        del self.connectionLookup[polyB]

        return True

    def _attemptToGrowPoly(self, pId):
        for neighbor in self.connectionLookup.get(pId, []):
            if (neighbor is not None) and self._attemptToMergePolys(
                    pId, neighbor):
                return True
        return False

    def _growEachPolyOnce(self):
        grewAtLeastOne = False

        for pId in self.connectionLookup.keys():
            if self._attemptToGrowPoly(pId):
                grewAtLeastOne = True

        return grewAtLeastOne

    def optimizeMesh(self):
        '''
        Takes a mesh that is already functionally complete and optimizes it for better performance.
        Reduces poly count and cuts out redundant vertices.
        Also compacts the polygon IDs into a contiguous range from 0 to N.
        No need to do the same for vertex IDs yet.
        '''
        '''print "Stitching polygons: %s -> " % (len(self.polyToVerts)),
        orig = len(self.polyToVerts)
        numPasses = 1
        while self._growEachPolyOnce():
            print "%s -> " % (len(self.polyToVerts)),
            numPasses += 1
        print "Done!\nPoly count reduced to %0.1f%% of original." % (len(self.polyToVerts)/float(orig)*100.0)'''

        self._pruneExtraVerts()

        self._compactPolyIds()

        self.numNodes = len(self.connections)

        biggest = 0
        biggestPoly = -1
        for p in self.polyToVerts:
            if len(self.polyToVerts[p]) > biggest:
                biggest = len(self.polyToVerts[p])
                biggestPoly = p

        print "Most verts in a single poly: ", biggest
        assert biggest < 256

    def _cleanPoly(self, polyId):
        verts = self.polyToVerts[polyId]
        angles = self.polyToAngles[polyId]
        neighbors = self.connectionLookup[polyId]
        numVerts = len(verts)

        newVerts = []
        newAngles = []
        newNeighbors = []

        for i in xrange(numVerts):
            if (angles[i] != 180) or \
               (len(self.vertToPolys.get(verts[i],[])) > 2) or \
               (neighbors[i] != neighbors[(i-1)%numVerts]):
                # Keep vertex
                newVerts.append(verts[i])
                newAngles.append(angles[i])
                newNeighbors.append(neighbors[i])
            else:
                # Remove vertex, this will happen twice so pop it
                self.vertToPolys.pop(verts[i], None)
                self.vertexCoords.pop(verts[i], None)

        if len(verts) != len(newVerts):
            self.polyToVerts[polyId] = newVerts
            self.polyToAngles[polyId] = newAngles
            self.connectionLookup[polyId] = newNeighbors

        assert len(newVerts) < 256

    def _pruneExtraVerts(self):
        print "Pruning extra vertices..."
        print "Starting verts: %s" % len(self.vertToPolys)
        for polyId in self.connectionLookup.keys():
            self._cleanPoly(polyId)
        print "Ending verts: %s" % len(self.vertToPolys)

    def _compactPolyIds(self):
        polyList = self.polyToVerts.keys()
        polyList.sort()

        oldToNewId = {None: None}

        newPolyToVerts = {}
        newPolyToAngles = {}
        self.connections = []

        currId = 0

        for oldId in polyList:
            oldToNewId[oldId] = currId
            self.connections.append([])
            currId += 1

        for oldId in polyList:
            newPolyToVerts[oldToNewId[oldId]] = self.polyToVerts[oldId]
            newPolyToAngles[oldToNewId[oldId]] = self.polyToAngles[oldId]
            #self.connections[oldToNewId[oldId]] = []
            for edgeNum in xrange(len(self.connectionLookup[oldId])):
                self.connections[oldToNewId[oldId]].append(
                    oldToNewId[self.connectionLookup[oldId][edgeNum]])

        self.polyToVerts = newPolyToVerts
        self.polyToAngles = newPolyToAngles
        del self.connectionLookup

    # --------- Begin pathfinding code ---------

    def _findCentroid(self, polyId):
        verts = self.polyToVerts[polyId]
        numVerts = len(verts)
        x = 0
        y = 0
        z = 0
        for v in verts:
            x += self.vertexCoords[v][0]
            y += self.vertexCoords[v][1]
            z += self.vertexCoords[v][2]

        x /= numVerts
        y /= numVerts
        z /= numVerts

        return (x, y, z)

##     def _estimateDistanceBetweenPolys(self, polyA, polyB):
##         centroidA = self._findCentroid(polyA)
##         centroidB = self._findCentroid(polyB)

##         dx = centroidA[0] - centroidB[0]
##         dy = centroidA[1] - centroidB[1]
##         dz = centroidA[2] - centroidB[2]

##         return math.sqrt(dx*dx + dy*dy + dz*dz)

    def _walkToNeighbor(self, currPoly, neighborPoly):
        currVerts = self.polyToVerts[currPoly]
        neighborVerts = self.polyToVerts[neighborPoly]

        lenCurr = len(currVerts)

        sharedVerts = [v for v in currVerts if (v in neighborVerts)]

        loc = 0

        while currVerts[loc] not in sharedVerts:
            loc += 1

        while currVerts[loc] in sharedVerts:
            loc = (loc - 1) % lenCurr

        loc = (loc + 1) % lenCurr

        CCWmost = currVerts[loc]
        CCWmostLoc = loc

        while currVerts[loc] in sharedVerts:
            loc = (loc + 1) % lenCurr

        loc = (loc - 1) % lenCurr

        CWmost = currVerts[loc]

        CCWmostCoords = self.vertexCoords[CCWmost]
        CWmostCoords = self.vertexCoords[CWmost]

        # For now, walk to the midpoint of the connecting edge

        departingEdge = CCWmostLoc  # Don't need this with goal->start search

        neighborsEdge = 0
        while self.connections[neighborPoly][neighborsEdge] != currPoly:
            neighborsEdge += 1

        return (neighborsEdge, ((CWmostCoords[0] + CCWmostCoords[0]) / 2.0,
                                (CWmostCoords[1] + CCWmostCoords[1]) / 2.0,
                                (CWmostCoords[2] + CCWmostCoords[2]) / 2.0))

##     def _remakePath(self,walkBack,currNode):
##         if currNode in walkBack:
##             p = self._remakePath(walkBack,walkBack[currNode])
##             return p + [currNode,]
##         return [currNode,]

##     def findRoute(self, startNode, goalNode):
##         '''
##         So much love for A*.
##         '''
##         nodeToF = {}
##         nodeToG = {}
##         nodeToH = {}

##         walkBack = {}

##         #nodeToEntryPoint = {}
##         self.nodeToEntryPoint[startNode] = self._findCentroid(startNode)

##         nodeToG[startNode] = 0
##         nodeToH[startNode] = self._estimateDistanceBetweenPolys(startNode,goalNode)
##         nodeToF[startNode] = nodeToG[startNode] + nodeToH[startNode]

##         closedSet = {}
##         openSet = {}
##         openQueue = PriQueue() # Priority = F score

##         openSet[startNode] = 1
##         openQueue.push((nodeToF[startNode],startNode))

##         goalPoint = self._findCentroid(goalNode)

##         while len(openSet) > 0:
##             f,currNode = openQueue.pop(0)
##             del openSet[currNode]

##             self.aStarWasHere[currNode] = 1

##             if currNode == goalNode:
##                 return self._remakePath(walkBack,currNode)

##             closedSet[currNode] = 1

##             currPoint = self.nodeToEntryPoint[currNode]

##             for neighbor in self.connections[currNode]:
##                 if (neighbor is not None) and (neighbor not in closedSet):
##                     departingEdge,newEntryPoint = self._walkToNeighbor(currNode,currPoint,neighbor)
##                     newG = nodeToG[currNode] + math.sqrt((newEntryPoint[0] - currPoint[0])**2 + \
##                                                          (newEntryPoint[1] - currPoint[1])**2 + \
##                                                          (newEntryPoint[2] - currPoint[2])**2)
##                     gotHereFasterThanBefore = False

##                     if neighbor not in openSet:
##                         openSet[neighbor] = 1
##                         gotHereFasterThanBefore = True
##                     elif newG < nodeToG[neighbor]:
##                         openQueue.remove((nodeToF[neighbor],neighbor))
##                         gotHereFasterThanBefore = True

##                     if gotHereFasterThanBefore:
##                         walkBack[neighbor] = currNode
##                         self.nodeToEntryPoint[neighbor] = newEntryPoint
##                         nodeToH[neighbor] = math.sqrt((goalPoint[0] - newEntryPoint[0])**2 + \
##                                                       (goalPoint[1] - newEntryPoint[1])**2 + \
##                                                       (goalPoint[2] - newEntryPoint[2])**2)
##                         nodeToG[neighbor] = newG
##                         nodeToF[neighbor] = nodeToG[neighbor] + nodeToH[neighbor]
##                         openQueue.push((nodeToF[neighbor],neighbor))

##         raise "No path found!  D:"

    def _findAllRoutesToGoal(self, goalNode):
        '''
        Find the shortest path from ALL start nodes to the given goal node.  (Djikstra)
        
        After running, self.pathData[startNode][goalNode] == outgoing edge from startNode to the next node
        for the given value of goalNode and ALL values of startNode.
        '''
        nodeToG = {}

        walkBack = {}

        nodeDeparturePoint = {}
        nodeDeparturePoint[goalNode] = self._findCentroid(goalNode)

        nodeToG[goalNode] = 0

        closedSet = {}
        openSet = {}
        openQueue = PriQueue()

        openSet[goalNode] = 1
        openQueue.push((nodeToG[goalNode], goalNode))

        walkBack[goalNode] = (0, goalNode)

        while len(openSet) > 0:
            f, currNode = openQueue.pop(0)
            del openSet[currNode]

            closedSet[currNode] = 1

            currPoint = nodeDeparturePoint[currNode]

            for neighbor in self.connections[currNode]:
                if (neighbor is not None) and (neighbor not in closedSet):
                    neighborsEdge, newPoint = self._walkToNeighbor(
                        currNode, neighbor)
                    newG = nodeToG[currNode] + math.sqrt((newPoint[0] - currPoint[0])**2 + \
                                                         (newPoint[1] - currPoint[1])**2 + \
                                                         (newPoint[2] - currPoint[2])**2)
                    gotHereFasterThanBefore = False

                    if neighbor not in openSet:
                        openSet[neighbor] = 1
                        gotHereFasterThanBefore = True
                    elif newG < nodeToG[neighbor]:
                        openQueue.remove((nodeToG[neighbor], neighbor))
                        gotHereFasterThanBefore = True

                    if gotHereFasterThanBefore:
                        walkBack[neighbor] = (neighborsEdge, currNode)
                        nodeDeparturePoint[neighbor] = newPoint
                        nodeToG[neighbor] = newG
                        openQueue.push((nodeToG[neighbor], neighbor))

        for startNode in xrange(len(self.connections)):
            departingEdge = walkBack[startNode][0]

            assert self.pathData[startNode][goalNode] is None

            self.pathData[startNode][goalNode] = departingEdge

    def generatePathData(self, rowRange=None):
        '''
        Entry point for path preprocessing.
        Solves all pairs shortest path for this mesh.
        Stores the result in self.pathData.
        SLOW.  Expect 8-10 minutes on Port Royal alone.

        Currently runs Djikstra on every possible start node.
        There are faster approaches for APSP, but...
        '''
        if rowRange is None:
            rowRange = (0, self.numNodes)

        self.initPathData()

        for goalNode in xrange(rowRange[0], rowRange[1]):
            self._findAllRoutesToGoal(goalNode)

    def createPathTable(self):
        '''
        Takes a 2D array self.pathData and changes it in place.
        Each row is changed into a run-length encoded string.
        Then, feeds the data into a new PathTable instance.
        '''
        for row in self.pathData:
            for val in row:
                if val == None:
                    raise "Incomplete path data!"

        shortestPathLookup = self.pathData

        self.pathData = []

        # Run-Length Encode the whole thing!
        for start in xrange(self.numNodes):
            row = []
            lastVal = None
            nodesInRow = 0
            for goal in xrange(self.numNodes):
                val = shortestPathLookup[start][goal]
                if val != lastVal:
                    row.append([goal, val])
                    lastVal = val
                    nodesInRow += 1
                else:
                    nodesInRow += 1

            assert nodesInRow == self.numNodes

            stringsRow = []

            # Convert row to a bytestring to save space
            for item in row:
                assert item[0] < 65536
                assert item[1] < 256

                stringsRow.append(
                    chr(item[0] / 256) + chr(item[0] % 256) + chr(item[1]))

                assert len(stringsRow[-1]) == 3

            rowString = string.join(stringsRow, "")

            self.pathData.append(rowString)

        self.pathTable = PathTable(self.pathData, self.connections)

    def printPathData(self):
        '''
        Outputs the pickled path table to stdout.
        '''
        import sys
        sys.stdout.write(pickle.dumps(self.pathData, protocol=0))

    def initPathData(self):
        self.pathData = []

        for i in xrange(self.numNodes):
            self.pathData.append([
                None,
            ] * self.numNodes)

    def addPaths(self, partialData):
        for i in xrange(len(partialData)):
            for j in xrange(len(partialData[i])):
                if partialData[i][j] is not None:
                    assert self.pathData[i][j] is None
                    self.pathData[i][j] = partialData[i][j]

##     def pathTableLookup(self, startNode, goalNode):
##         '''
##         Look up the equivalent of pathData[goalNode][startNode] in our run-length encoded data.
##         '''
##         if startNode >= self.numNodes or goalNode >= self.numNodes:
##             raise "Invalid node ID.  Must be less than self.numNodes (%s)." % self.numNodes

##         str = self.pathData[startNode]

##         pos = 0

##         while (pos < len(str)) and (256*ord(str[pos]) + ord(str[pos+1]) <= goalNode):
##             #print pos, ": ",256*ord(str[pos]) + ord(str[pos+1])
##             pos += 3

##         pos -= 3

##         return ord(str[pos+2])

    def findRoute(self, startNode, goalNode):
        '''
        Returns the node-by-node route from startNode to goalNode.
        '''
        return self.pathTable.findRoute(startNode, goalNode)

    def makeNodeLocator(self, environment):
        meshNode = CollisionNode("NavMeshNodeLocator")
        meshNode.setFromCollideMask(BitMask32.allOff())
        meshNode.setIntoCollideMask(OTPGlobals.PathFindingBitmask)

        self.polyHashToPID = {}

        for pId in self.polyToAngles:
            vertCount = 0
            corners = []
            for angle in self.polyToAngles[pId]:
                if angle != 180:
                    # It's a corner
                    corners.append(vertCount)
                vertCount += 1

            # XXX this code only works for square nodes at present
            # Unfortunately we can only make triangle or square CollisionPolygons on the fly
            assert len(corners) == 4

            #import pdb
            #pdb.set_trace()

            verts = []

            for vert in corners:
                verts.append(
                    (self.vertexCoords[self.polyToVerts[pId][vert]][0],
                     self.vertexCoords[self.polyToVerts[pId][vert]][1], 0))

            #import pdb
            #pdb.set_trace()

            poly = CollisionPolygon(verts[0], verts[1], verts[2], verts[3])

            assert poly not in self.polyHashToPID

            self.polyHashToPID[poly] = pId

            meshNode.addSolid(poly)

        ray = CollisionRay()
        ray.setDirection(0, 0, -1)
        ray.setOrigin(0, 0, 0)

        rayNode = CollisionNode("NavMeshRay")
        rayNode.setFromCollideMask(OTPGlobals.PathFindingBitmask)
        rayNode.setIntoCollideMask(BitMask32.allOff())
        rayNode.addSolid(ray)

        self.meshNodePath = environment.attachNewNode(meshNode)
        self.rayNodePath = environment.attachNewNode(rayNode)

        self.meshNodePath.setTwoSided(True)

        self.chq = CollisionHandlerQueue()
        self.traverser = CollisionTraverser()
        self.traverser.addCollider(self.rayNodePath, self.chq)

    def findNodeFromPos(self, environment, x, y):
        self.rayNodePath.setPos(environment, x, y, 50000)
        self.chq.clearEntries()

        self.traverser.traverse(self.meshNodePath)

        if self.chq.getNumEntries() != 1:
            self.notify.warning("No node found at position: %s, %s in %s" %
                                (x, y, environment))
            return 0

        e = self.chq.getEntry(0)

        assert e.hasInto()
        if not e.hasInto():
            self.notify.warning("No into found for collision %s" % (e))

        pId = self.polyHashToPID[e.getInto()]

        return pId

    # --------- Begin long-term storage code ---------

    def writeToFile(self, filename, storePathTable=True):
        '''
        Output the contents of this mesh to the file specified.
        Saving to a file lets us avoid doing expensive precomputation every time a mesh instance is required.
        '''
        if self.environmentHash is None:
            raise "Attempted write to file without valid environment hash!"

        if storePathTable and not self.pathData:
            raise "Attempted to write empty pathData.  Call NavMesh.generatePathTable() first!"

        f = open(filename, 'wb')

        if storePathTable:
            pickle.dump([
                self.environmentHash, self.polyToVerts, self.polyToAngles,
                self.vertexCoords, self.connections, self.pathData
            ],
                        f,
                        protocol=2)
            f.close()
            self.pathData = None
        else:
            pickle.dump([
                self.environmentHash, self.polyToVerts, self.polyToAngles,
                self.vertexCoords, self.connections, None
            ],
                        f,
                        protocol=2)
            f.close()

        print "Successfully wrote to file %s." % filename

    def _initFromString(self, str):
        contents = pickle.loads(str)

        self.environmentHash = contents[0]
        self.polyToVerts = contents[1]
        self.polyToAngles = contents[2]
        self.vertexCoords = contents[3]
        self.connections = contents[4]
        self.pathData = contents[5]

        if self.pathData is not None:
            self.pathTable = PathTable(self.pathData, self.connections)
            self.pathData = None

        self.numNodes = len(self.connections)

    def _initFromFilename(self, filepath, filename):
        vfs = VirtualFileSystem.getGlobalPtr()
        filename = Filename(filename)
        searchPath = DSearchPath()
        #searchPath.appendDirectory(Filename('.'))
        #searchPath.appendDirectory(Filename('etc'))
        #searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('~')))
        #searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('$HOME')))
        searchPath.appendDirectory(
            Filename.fromOsSpecific(os.path.expandvars(filepath)))

        found = vfs.resolveFilename(filename, searchPath)

        if not found:
            raise IOError, "File not found!"

        str = vfs.readFile(filename, 1)

        self._initFromString(str)

    def checkHash(self, envHash):
        '''
        "Does this mesh represent the environment I think it does?"
        If this check fails, the mesh is out of date (or being used with the wrong environment).
        In either case, whoever generated this instance should discard it and create a new mesh from scratch.
        '''
        return envHash == self.environmentHash
Example #20
0
class ArcadeFlightGame(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        self.debug = False
        self.maxdistance = 400
        self.statusLabel = self.makeStatusLabel(0)
        self.collisionLabel = self.makeStatusLabel(1)

        self.player = AlliedFlanker(self.loader, self.render, self.taskMgr)
        self.world = GameWorld(1024, self.loader, self.render, self.camera)

        self.taskMgr.add(self.updateTask, "update")
        self.keyboardSetup()

        # performance and map to player so can't fly beyond visible terrain
        self.player.setMaxHeight(self.maxdistance)

        if self.debug == False:
            self.camLens.setFar(self.maxdistance)
        else:
            base.oobe()

        self.camLens.setFov(60)
        self.setupCollisions()
        self.textCounter = 0

    def makeStatusLabel(self, i):
        """ Create a status label at the top-left of the screen,
        Parameter 'i' is the row number """
        return OnscreenText(style=2, fg=(.5,1,.5,1), pos=(-1.3,0.92-(.08 * i)), \
                               align=TextNode.ALeft, scale = .08, mayChange = 1)

    def keyboardSetup(self):
        self.keyMap = {"left":0, "right":0, "climb":0, "fall":0, \
                        "accelerate":0, "decelerate":0, "fire":0}
        self.accept("escape", sys.exit)
        self.accept("a", self.setKey, ["accelerate", 1])
        self.accept("a-up", self.setKey, ["accelerate", 0])
        self.accept("z", self.setKey, ["decelerate", 1])
        self.accept("z-up", self.setKey, ["decelerate", 0])
        self.accept("arrow_left", self.setKey, ["left", 1])
        self.accept("arrow_left-up", self.setKey, ["left", 0])
        self.accept("arrow_right", self.setKey, ["right", 1])
        self.accept("arrow_right-up", self.setKey, ["right", 0])
        self.accept("arrow_down", self.setKey, ["climb", 1])
        self.accept("arrow_down-up", self.setKey, ["climb", 0])
        self.accept("arrow_up", self.setKey, ["fall", 1])
        self.accept("arrow_up-up", self.setKey, ["fall", 0])
        self.accept("space", self.setKey, ["fire", 1])
        self.accept("space-up", self.setKey, ["fire", 0])
        base.disableMouse()  # or updateCamera will fail!

    def setKey(self, key, value):
        """ Used by keyboard setup """
        self.keyMap[key] = value

    def setupCollisions(self):
        self.collTrav = CollisionTraverser()

        # rapid collisions detected using below plus FLUID pos
        self.collTrav.setRespectPrevTransform(True)

        self.playerGroundSphere = CollisionSphere(0, 1.5, -1.5, 1.5)
        self.playerGroundCol = CollisionNode('playerSphere')
        self.playerGroundCol.addSolid(self.playerGroundSphere)

        # bitmasks
        self.playerGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.playerGroundCol.setIntoCollideMask(BitMask32.allOff())
        self.world.setGroundMask(BitMask32.bit(0))
        self.world.setWaterMask(BitMask32.bit(0))

        # and done
        self.playerGroundColNp = self.player.attach(self.playerGroundCol)
        self.playerGroundHandler = CollisionHandlerQueue()
        self.collTrav.addCollider(self.playerGroundColNp,
                                  self.playerGroundHandler)

        # DEBUG as per video:
        if (self.debug == True):
            self.playerGroundColNp.show()
            self.collTrav.showCollisions(self.render)

    def updateTask(self, task):
        """ Gets added to the task manager, updates the player, deals with inputs,
        collisions, game logic etc. """
        self.player.calculate()
        self.actionInput()
        validMove = self.player.move(self.world.getSize())

        # lets not be doing this every frame...
        if validMove == False and self.textCounter > 30:
            self.statusLabel.setText("STATUS: MAP END; TURN AROUND")
        elif self.textCounter > 30:
            self.statusLabel.setText("STATUS: OK")
        if self.textCounter > 30:
            self.textCounter = 0
        else:
            self.textCounter = self.textCounter + 1
        self.updateCamera()

        self.collTrav.traverse(self.render)
        for i in range(self.playerGroundHandler.getNumEntries()):
            entry = self.playerGroundHandler.getEntry(i)
            if (self.debug == True):
                self.collisionLabel.setText("DEAD:" +
                                            str(globalClock.getFrameTime()))
            self.player.die()
        return Task.cont

    def actionInput(self):
        """ Used by updateTask to process keyboard input """
        if (self.keyMap["climb"] != 0):
            self.player.climb()
        elif (self.keyMap["fall"] != 0):
            self.player.dive()
        else:
            self.player.unwindVertical()

        if (self.keyMap["left"] != 0):
            self.player.bankLeft()
        elif (self.keyMap["right"] != 0):
            self.player.bankRight()
        else:
            self.player.unwindHorizontal()

        if (self.keyMap["accelerate"] != 0):
            self.player.accelerate()
        elif (self.keyMap["decelerate"] != 0):
            self.player.brake()

    def updateCamera(self):
        self.player.lookAtMe(self.camera)