Пример #1
0
class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        #Background sound (does not play infinitely)
        self.backgroundSound = base.loader.loadSfx("sounds/DireDireDocks.mp3")

        taskMgr.add(self.update, "moveTask")
        
        #Disable the mouse so that we may use it for our control scheme.
        self.props = WindowProperties()
        self.props.setSize(1920, 1080)
        self.props.setFullscreen(True)
        self.props.setCursorHidden(True)
        base.win.requestProperties(self.props)
        base.disableMouse()
        
        self.buildKeyMap()
        self.inMenu = True
        self.menuScreen = OnscreenImage("titlescreen.png", (0, .01, 0))
        self.menu()

    def initialize(self):
        self.timer = 0
        base.enableParticles()
        #base.setFrameRateMeter(True)

        ##########
        #
        # SETUP COLLISION HANDLERS AND FLAMIE'S MODEL
        #
        ##########
        
        #Create the collision handlers in order to build the level.
        WALL_MASK = BitMask32.bit(2)
        FLOOR_MASK = BitMask32.bit(1)
        
        #Start up the collision system
        self.cTrav = CollisionTraverser()

        #Determine how collisions with walls will be handled
        self.wallHandler = CollisionHandlerPusher()
        self.bobbing = False

        #Setup flamie's model
        self.flamieNP = base.render.attachNewNode(ActorNode('flamieNP'))
        self.flamieNP.reparentTo(base.render)
        self.flamie = loader.loadModel('models/Flame/body')
        self.flamie.setScale(.5)
        self.flamie.setTransparency(TransparencyAttrib.MAlpha)
        self.flamie.setAlphaScale(0)
        self.flamie.reparentTo(self.flamieNP)
        self.flamie.setCollideMask(BitMask32.allOff())
        flameLight = DirectionalLight("flameLight")
        fl = self.flamie.attachNewNode(flameLight)
        fl.setColor(255, 255, 255, 1)
        flameLight.setDirection((-5, -5, -5))
        self.flamie.setLight(fl)


        self.flamie2 = loader.loadModel("models/p.obj")
        self.flamie2.setTexture(loader.loadTexture("models/flamie2D/f7.png"))
        self.flamie2.reparentTo(self.flamieNP)
        self.flamie2.setScale(4)
        self.flamie2OriZ = 2
        self.flamie2.setPos(-6.5, 0, self.flamie2OriZ) #(length, depth, height)
        self.flamie2.setLight(fl)
        self.flamie2.setTransparency(TransparencyAttrib.MAlpha)
        self.flamieFire = PointLight('fire')
        self.flamieFire.setColor(VBase4(1,1,1,1))
        self.flamieFire.setAttenuation((1,0,1))
        plnp = render.attachNewNode(self.flamieFire)
        plnp.setPos(self.flamieNP.getPos())
        self.flamielight = AmbientLight('light')
        self.flamielight.setColor(VBase4(1, 0.5, 0.6, 1))

        self.flamielight = self.flamie2.attachNewNode(self.flamielight)
        self.flamie2.setLight(self.flamielight)
        self.flamie2.setLight(plnp)
        self.mayFlamieBob = True

        #self.flamie2.setAlphaScale(0.5)

        '''self.tree = loader.loadModel("models/p2.obj")
        self.tree.setTexture(loader.loadTexture("deadTree.png"))
        self.tree.reparentTo(render)
        self.tree.setScale(4)
        self.tree.setPos(25,25,0) #(length, depth, height)
        self.tree.setLight(fl)
        self.tree.setTransparency(TransparencyAttrib.MAlpha)
        self.treelight = AmbientLight('light')
        self.treelight.setColor(VBase4(0.9, 0.9, 0.6, 1))
        self.treelight = self.tree.attachNewNode(self.treelight)
        self.tree.setLight(self.treelight)'''

        x = 150
        y = 20
        offset1 = 0
        treeList2 = [loader.loadModel("models/p2.obj") for i in range(7)]
        for j in treeList2:
            k = random.randint(1, 100)
            if k%5 is 1 or k%5 is 2:
                j.setTexture(loader.loadTexture("deadTree.png"))
            else:
                j.setTexture(loader.loadTexture("tree.png"))
            j.reparentTo(render)
            j.setScale(random.randint(4,7))
            j.setTransparency(TransparencyAttrib.MAlpha)
            j.setPos(x + 3*offset1, y + 4*offset1, 0)
            treelight = AmbientLight('light')
            treelight = j.attachNewNode(treelight)
            j.setLight(treelight)
            offset1 = offset1 + random.randint(4, 10)

        x = 4
        y = 90
        offset1 = 0
        treeList2 = [loader.loadModel("models/p2.obj") for i in range(6)]
        for j in treeList2:
            k = random.randint(1, 100)
            if k%5 is 1 or k%5 is 2:
                j.setTexture(loader.loadTexture("deadTree.png"))
            else:
                j.setTexture(loader.loadTexture("tree.png"))
            j.reparentTo(render)
            j.setScale(random.randint(4,7))
            j.setTransparency(TransparencyAttrib.MAlpha)
            j.setPos(x + 3*offset1, y + 4*offset1, 0)
            treelight = AmbientLight('light')
            treelight = j.attachNewNode(treelight)
            j.setLight(treelight)
            offset1 = offset1 + random.randint(4, 10)

        x = 3
        y = 120
        offset1 = 0
        treeList2 = [loader.loadModel("models/p2.obj") for i in range(4)]
        for j in treeList2:
            k = random.randint(1, 100)
            if k%5 is 1 or k%5 is 2:
                j.setTexture(loader.loadTexture("deadTree.png"))
            else:
                j.setTexture(loader.loadTexture("tree.png"))
            j.reparentTo(render)
            j.setScale(random.randint(4,7))
            j.setTransparency(TransparencyAttrib.MAlpha)
            j.setPos(x + 3*offset1, y + 4*offset1, 0)
            treelight = AmbientLight('light')
            treelight = j.attachNewNode(treelight)
            j.setLight(treelight)
            offset1 = offset1 + random.randint(4, 10)

        x = 200
        y = 20
        offset1 = 0
        treeList2 = [loader.loadModel("models/p2.obj") for i in range(4)]
        for j in treeList2:
            k = random.randint(1, 100)
            if k%5 is 1 or k%5 is 2:
                j.setTexture(loader.loadTexture("deadTree.png"))
            else:
                j.setTexture(loader.loadTexture("tree.png"))
            j.reparentTo(render)
            j.setScale(random.randint(4,7))
            j.setTransparency(TransparencyAttrib.MAlpha)
            j.setPos(x + 3*offset1, y + 4*offset1, 0)
            treelight = AmbientLight('light')
            treelight = j.attachNewNode(treelight)
            j.setLight(treelight)
            offset1 = offset1 + random.randint(4, 10)

        ### Something that should look like water ###
        w = loader.loadModel("models/flatP.obj")
        w.setTexture(loader.loadTexture("ice.png"))
        w.reparentTo(render)
        w.setScale(75)
        w.setTransparency(TransparencyAttrib.MAlpha)
        w.setAlphaScale(.7)
        w.setLight(treelight)
        w.setPos(-200, 0, -10)

        self.waterOrigiZ = -10
        self.waterSecZ = -95
        self.waterThirdZ = -120
        self.water = w

        ### Reskying the sky ###
        w = loader.loadModel("models/biggerFlatP.obj")
        w.setTexture(loader.loadTexture("models/n2.jpg"))
        w.reparentTo(self.flamie2)
        w.setScale(15)
        w.setLight(treelight)
        w.setPos(-200, 450, -200) #(length, depth, height)

        #Give flamie gravity
        self.floorHandler = CollisionHandlerGravity()
        self.floorHandler.setGravity(9.81+100)
        self.floorHandler.setMaxVelocity(100)
        

        ##########
        #
        # GENERATING LEVEL PARTS
        #
        ##########
        self.ice_reset = ()
        self.start = PlatformSeg(LVector3(0,0,0))
        self.start.generateAllParts(render)
        self.checkpointCreator(70, 90, self.start.pos.z, 10)
        self.floater = False
        
        for p in self.start.parts:
            if isinstance(p, Prism):
                self.collisionBoxCreator(p.pos.x, p.pos.y, p.pos.z, p.len, p.wid, p.dep, 'terraincollision', 'wallcollision')
            if isinstance(p, Square):
                self.collisionBoxCreator(p.pos.x, p.pos.y, p.pos.z, p.len, p.wid, 3,  'terraincollision', 'wallcollision')
            if isinstance(p, IceCube):
                p.model.setCollideMask(BitMask32.allOff())
                self.ice_reset += (p,)
                iceCubefloor= p.model.find("**/iceFloor")
                iceCubewall = p.model.find("**/iceWall")
                iceCubefloor.node().setIntoCollideMask(FLOOR_MASK)
                iceCubewall.node().setIntoCollideMask(WALL_MASK)

        
        self.lostWood = LostWood(LVector3(self.start.pos.x + 750, self.start.parts[0].pos.y + self.start.parts[0].wid, self.start.pos.z))
        self.lostWood.generateAllParts(render)
        self.checkpointCreator(self.lostWood.pos.x+120, self.lostWood.pos.y+150, self.lostWood.pos.z,20)
        self.checkpointCreator(self.lostWood.parts[6].pos.x + self.lostWood.parts[6].len/2, self.lostWood.parts[6].pos.y + self.lostWood.parts[6].wid/2, self.lostWood.pos.z, 40)
        
        for p in self.lostWood.parts:
            if isinstance(p, Prism):
                self.collisionBoxCreator(p.pos.x, p.pos.y, p.pos.z, p.len, p.wid, p.dep, 'terraincollision', 'wallcollision')
            if isinstance(p, Square):
                self.collisionBoxCreator(p.pos.x, p.pos.y, p.pos.z, p.len, p.wid, 3,  'terraincollision', 'wallcollision')
            if isinstance(p, IceCube):
                p.model.setCollideMask(BitMask32.allOff())
                self.ice_reset += (p,)
                iceCubefloor= p.model.find("**/iceFloor")
                iceCubewall = p.model.find("**/iceWall")
                iceCubefloor.node().setIntoCollideMask(FLOOR_MASK)
                iceCubewall.node().setIntoCollideMask(WALL_MASK)
            
        self.cave = Cave(LVector3(self.lostWood.pos.x + 1100, self.lostWood.pos.y + 2000, self.lostWood.pos.z - 50))
        self.cave.generateAllParts(render)
        self.checkpointCreator(self.cave.thirdRoomParts[5].pos.x + self.cave.thirdRoomParts[5].len/2,
                               self.cave.thirdRoomParts[5].pos.y + self.cave.thirdRoomParts[5].wid/2,
                               self.cave.thirdRoomParts[5].pos.z, 30)
        
        for p in self.cave.parts:
            if isinstance(p, Prism):
                self.collisionBoxCreator(p.pos.x, p.pos.y, p.pos.z, p.len, p.wid, p.dep, 'terraincollision', 'wallcollision')
            if isinstance(p, Square):
                self.collisionBoxCreator(p.pos.x, p.pos.y, p.pos.z, p.len, p.wid, 3,  'terraincollision', 'wallcollision')
            if isinstance(p, IceCube):
                p.model.setCollideMask(BitMask32.allOff())
                self.ice_reset += (p,)
                iceCubefloor= p.model.find("**/iceFloor")
                iceCubewall = p.model.find("**/iceWall")
                iceCubefloor.node().setIntoCollideMask(FLOOR_MASK)
                iceCubewall.node().setIntoCollideMask(WALL_MASK)

        self.end = End(LVector3(self.cave.thirdRoomParts[8].pos.x - 200,
                       self.cave.thirdRoomParts[8].pos.y + self.cave.thirdRoomParts[8].wid,
                       self.cave.thirdRoomParts[8].pos.z))
        self.end.generate(render)
        self.collisionBoxCreator(self.end.floor.pos.x, self.end.floor.pos.y, self.end.floor.pos.z,
                                 self.end.floor.len, self.end.floor.wid, self.end.floor.dep,
                                 'terraincollision', 'wallcollision')
        #########
        # DRAWING THE CABIN AND FINAL CAMPFIRE
        #########
        self.checkpointCreator(self.end.floor.pos.x + self.end.floor.len/2,
                               self.end.floor.pos.y + self.end.floor.wid/2,
                               self.end.floor.pos.z, 30)
        self.cabin = loader.loadModel("models/p2.obj")
        self.cabin.setTexture(loader.loadTexture("models/cabin.png"))
        self.cabin.setScale(50)
        self.cabin.reparentTo(render)
        self.cabin.setPos(self.end.floor.pos.x + self.end.floor.len/2,
                          self.end.floor.pos.y + self.end.floor.wid/1.1,
                          self.end.floor.pos.z)
        self.cabin.setTransparency(TransparencyAttrib.MAlpha)
        

        #Manually creating starting position. Copy and paste the first three parameters of the checkpoint you want to start at.
        self.startPos = LVector3(70, 90, self.start.pos.z)
        self.flamieNP.setPos(self.startPos)


        '''#Testing the tree model
        self.tree = loader.loadModel('models/Tree/log')
        self.tree.reparentTo(render)
        self.tree.setPos(-50,0,100)
        self.tree.setScale(2)'''

        '''#Add sky background
        self.sky = loader.loadModel('models/sphere.obj')
        self.sky.reparentTo(self.camera)
        self.sky.set_two_sided(True)
        self.skyTexture = loader.loadTexture("models/n2.jpg")
        self.sky.setTexture(self.skyTexture)
        self.sky.set_bin('background', 0)
        self.sky.set_depth_write(False)
        self.sky.set_compass()'''

        ##########
        #
        # CREATE FLAMIE'S COLLISION GEOMETRY
        #
        ##########
        
        #Give flamie a collision sphere in order to collide with walls
        flamieCollider = self.flamie.attachNewNode(CollisionNode('flamiecnode'))
        flamieCollider.node().addSolid(CollisionSphere(0,0,0,5))
        flamieCollider.node().setFromCollideMask(WALL_MASK)
        flamieCollider.node().setIntoCollideMask(BitMask32.allOff())
        self.wallHandler.addCollider(flamieCollider, self.flamieNP)
        self.cTrav.addCollider(flamieCollider, self.wallHandler)

        #Give flamie a collision ray to collide with the floor
        flamieRay = self.flamie.attachNewNode(CollisionNode('flamieRay'))
        flamieRay.node().addSolid(CollisionRay(0,0,8,0,0,-1))
        flamieRay.node().setFromCollideMask(FLOOR_MASK)
        flamieRay.node().setIntoCollideMask(BitMask32.allOff())
        self.floorHandler.addCollider(flamieRay, self.flamieNP)
        self.cTrav.addCollider(flamieRay, self.floorHandler)

        #Add a sensor that lets us melt ice cubes without standing on the cube.
        meltSensor = self.flamie.attachNewNode(CollisionNode('meltSensor'))
        cs = CollisionSphere(-2,0,10, 50)
        meltSensor.node().addSolid(cs)
        meltSensor.node().setFromCollideMask(WALL_MASK)
        meltSensor.node().setIntoCollideMask(BitMask32.allOff())
        cs.setTangible(0)
        self.wallHandler.addCollider(meltSensor, self.flamieNP)
        self.cTrav.addCollider(meltSensor, self.wallHandler)
        self.wallHandler.addInPattern('%fn-into-%in')
        self.wallHandler.addAgainPattern('%fn-again-%in')
        self.accept('meltSensor-into-iceWall', self.melt)
        self.accept('meltSensor-again-iceWall', self.melt)
        self.accept('meltSensor-into-checkpointCol', self.newstart)
        
        #Add in an event handle to prevent the jumping glitch found on the bobbing ice cubes.
        self.floorHandler.addInPattern('%fn-into-%in')
        self.floorHandler.addAgainPattern('%fn-again-%in')
        self.floorHandler.addOutPattern('%fn-out-%in')
        self.accept('flamieRay-into-iceFloor', self.jittercancel)
        self.accept('flamieRay-again-iceFloor', self.jittercancel)
        self.accept('flamieRay-out-iceFloor', self.jittercanceloff)

        
        #Uncomment these lines to see flamie's collision geometry
        #flamieCollider.show()
        #flamieRay.show()
        #meltSensor.show()

        #Uncomment this line to see the actual collisions.
        #self.cTrav.showCollisions(render)
        
        #This plane is found at the very bottom of the level and adds global gravity.
        killfloor = CollisionPlane(Plane(Vec3(0,0,1), Point3(0,0,-1000)))
        killfloorCol = CollisionNode('kfcollision')
        killfloorCol.addSolid(killfloor)
        killfloorCol.setIntoCollideMask(BitMask32.bit(1))
        killfloorColNp = self.render.attachNewNode(killfloorCol)

        ####################
        #
        #   Setting light so that we could see the definition in the walls
        #
        ####################
        
        render.setShaderAuto()
        self.dlight = DirectionalLight('dlight')
        self.dlight.setColor(LVector4(0.3, 0.1, 0.7, 1))
        dlnp = render.attachNewNode(self.dlight)
        dlnp.setHpr(90, 20, 0)
        render.setLight(dlnp)

        self.alight = render.attachNewNode(AmbientLight("Ambient"))
        self.alight.node().setColor(LVector4(0.5, 0.5, 1, .1))
        render.setLight(self.alight)

        self.snow = loader.loadTexture("models/ground.jpg")

        #Create a floater object and have it float 2 units above fireball.
        #And use this as a target for the camera to focus on.
        #This idea is taken from the roaming ralph sample that came with the
        #Panda3D SDK.
        self.camFocus = NodePath(PandaNode("floater"))
        self.camFocus.reparentTo(render)
        self.camFocusCurrZ = self.flamie.getZ() + 10

        #The camera is locked to the avatar so it always follows regardless of movement.
        self.camera.reparentTo(render)
        self.cameraTargetHeight = 8.0
        self.cameraDistance = 100
        self.cameraHeightModes = (self.start.parts[0].pos.z + 45, self.start.parts[0].pos.z + 125, self.camFocus.getZ() + 10, self.camFocus.getZ() + 150,
                                  self.end.floor.pos.z + 10)
        self.cameraHeight = self.cameraHeightModes[0]

        
    #################
    #   Changes Camera orientation depending on where the player is in the stage to compensate for the fact that
    #   the player has no direct control of the camera.
    #   Checks using the arrays from the level parts.
    ##################
    def cameraModes(self, delta):
        #Is the fireball within the platforming section between the starting area and the lost woods?
        #Is the fireball near the simple platforming sections of the LostWoods?
        #Is the fireball near the drop off point into the cave?
        #Is the fireball near the entrance to the second room in the cave?
        #If yes to any of these, bring the camera up to give a bird's eye view of the platforming
        if ((self.flamieNP.getX() > self.start.parts[1].pos.x and self.flamieNP.getY() > self.start.parts[1].pos.y - self.start.parts[1].wid
            and self.flamieNP.getX() < self.lostWood.parts[0].pos.x)
            or (self.flamieNP.getX() > self.lostWood.parts[0].pos.x + self.lostWood.parts[0].len/1.1
                and self.flamieNP.getX() < self.lostWood.parts[2].pos.x + self.lostWood.parts[0].len/11
                and self.flamieNP.getY() < self.lostWood.parts[0].pos.y + self.lostWood.parts[0].wid)
            or (self.flamieNP.getY() > self.cave.parts[0].pos.y - 20 and self.flamieNP.getY() <= self.cave.parts[0].pos.y + self.cave.parts[0].wid/2)
            or (self.flamieNP.getX() < self.cave.parts[1].pos.x + self.cave.parts[1].wid/10 and self.flamieNP.getY() >= self.cave.parts[1].pos.x)):
                camMode = 1
        #Is the fireball in the beginning of the cave area?
        #If yes, bring the camera closer
        elif self.flamieNP.getY() > self.cave.parts[1].pos.y - self.cave.parts[0].wid/2 and self.flamieNP.getY() < self.cave.thirdRoomParts[5].pos.y:
            camMode = 2
        else:
            camMode = 0

        if self.flamieNP.getY() >= self.cave.thirdRoomParts[6].pos.y:
            self.cave.thirdRoomParts[0].hide()
            camMode = 3
        if self.flamieNP.getY() >= self.cave.thirdRoomParts[8].pos.y + self.cave.thirdRoomParts[8].wid/1.5:
            camMode = 4

        self.lerpCam(camMode, delta)

    def lerpCam(self, camMode, delta):
        CAMLERPSPEED = 25
        if camMode == 0:
            if not self.cameraHeight == self.cameraHeightModes[camMode]:
                if self.cameraHeight - CAMLERPSPEED * delta <= self.cameraHeightModes[camMode]:
                    self.cameraHeight = self.cameraHeightModes[camMode]
                else:
                    self.cameraHeight = self.cameraHeight - CAMLERPSPEED * delta
        elif camMode == 1:
            if not self.cameraHeight == self.cameraHeightModes[camMode]:
                if self.cameraHeight - CAMLERPSPEED * delta >= self.cameraHeightModes[camMode]:
                    self.cameraHeight = self.cameraHeightModes[camMode]
                else:
                    if self.cameraHeight < self.cameraHeightModes[camMode]:
                        self.cameraHeight = self.cameraHeight + CAMLERPSPEED * delta
                    else:
                        self.cameraHeight = self.cameraHeight - CAMLERPSPEED * delta
        elif camMode == 2:
            if not self.cameraHeight == self.cameraHeightModes[camMode]:
                if self.cameraHeight - CAMLERPSPEED * delta <= self.cameraHeightModes[camMode]:
                    self.cameraHeight = self.cameraHeightModes[camMode]
                    self.camFocusCurrZ = self.flamieNP.getZ() + 10
                else:
                    self.cameraHeight = self.cameraHeight - CAMLERPSPEED * delta
                    self.camFocusCurrZ = self.flamieNP.getZ() + 10
        elif camMode == 3:
            if not self.cameraHeight == self.cameraHeightModes[camMode]:
                if self.cameraHeight + CAMLERPSPEED * 3 * delta >= self.cameraHeightModes[camMode]:
                    self.cameraHeight = self.cameraHeightModes[camMode]
                else:
                    self.cameraHeight = self.cameraHeight + CAMLERPSPEED * 3 * delta
        elif camMode == 4:
            if not self.cameraHeight == self.cameraHeightModes[camMode]:
                if self.cameraHeight - CAMLERPSPEED * 3 * delta <= self.cameraHeightModes[camMode]:
                    self.cameraHeight = self.cameraHeightModes[camMode]
                else:
                    self.cameraHeight = self.cameraHeight - CAMLERPSPEED * 3 * delta

    def waterControl(self, delta):
        WATERLERPSPEED = .75
        if self.flamieNP.getY() <= self.lostWood.parts[6].pos.y + self.lostWood.parts[6].wid/2:
            if not self.water.getZ() == self.waterOrigiZ:
                if self.water.getZ() - WATERLERPSPEED * delta < self.waterOrigiZ and self.water.getZ() > self.waterOrigiZ:
                    self.water.setZ(self.waterOrigiZ)
                elif self.water.getZ() + WATERLERPSPEED * delta > self.waterOrigiZ and self.water.getZ() < self.waterOrigiZ:
                    self.water.setZ(self.waterOrigiZ)
                else:
                    if self.water.getZ() > self.waterOrigiZ:
                        self.water.setZ(self.water, - WATERLERPSPEED * delta)
                        if self.water.getZ() < self.waterOrigiZ:
                            self.water.setZ(self.waterOrigiZ)
                    else:
                        self.water.setZ(self.water, + WATERLERPSPEED * delta)
                        if self.water.getZ() > self.waterOrigiZ:
                            self.water.setZ(self.waterOrigiZ)
        elif self.flamieNP.getY() <= self.cave.parts[1].pos.y:
            if not self.water.getZ() == self.waterSecZ:
                if self.water.getZ() - WATERLERPSPEED * delta < self.waterSecZ:
                    self.water.setZ(self.waterSecZ)
                else:
                    self.water.setZ(self.water, - WATERLERPSPEED * delta)
        else:
            if not self.water.getZ() == self.waterThirdZ:
                if self.water.getZ() - WATERLERPSPEED * delta < self.waterThirdZ:
                    self.water.setZ(self.waterThirdZ)
                else:
                    self.water.setZ(self.water, - WATERLERPSPEED * delta)
        
        
    def reset(self):
        self.flamieNP.setPos(self.startPos)
        self.camFocusCurrZ = self.flamieNP.getZ() + 10
        for p in self.ice_reset:
            p.model.setScale(p.original_scale)
        
    def jump(self, dt):
        if self.bobbing:
            if self.floorHandler.getAirborneHeight() < 0.15:
                self.floorHandler.addVelocity(60) 
        elif self.floorHandler.isOnGround():
            self.floorHandler.addVelocity(60)

    def jittercancel(self, collEntry):
        model = collEntry.getIntoNodePath().getParent()
        modelRef = model.getPythonTag("iceRef")
        if model.getScale()[0] > 1.2:
            model.setScale(model.getScale()- modelRef.meltspeed)

        self.bobbing = True

    def jittercanceloff(self, collEntry):
        self.bobbing = False

    def melt(self, collEntry):
        model = collEntry.getIntoNodePath().getParent()
        modelRef = model.getPythonTag("iceRef")
        if model.getScale()[0] > 1.2 and self.bobbing != True:
            model.setScale(model.getScale()- modelRef.meltspeed)

    def newstart(self, collEntry):
        entry = collEntry.getInto().getCenter()
        self.startPos = (entry[0]+10, entry[1]+10, entry[2] +10)
        cp = loader.loadModel('models/Campfire/fire')
        cp.setPos(entry[0],entry[1], entry[2])
        cp.reparentTo(render)
            
    def buildKeyMap(self):
        self.keyMap = {"left": 0, "right": 0, "forward": 0, "back": 0, "down": 0, "up": 0, "lookUp": 0, "lookDown": 0, "lookLeft": 0, "lookRight": 0}

        #I changed the control scheme let me know if you would like me to try something else.
        #WASD for movement, space for jump
        self.accept("escape", sys.exit)
        self.accept("a", self.setKey, ["left", True])
        self.accept("a-up", self.setKey, ["left", False])
        self.accept("d", self.setKey, ["right", True])
        self.accept("d-up", self.setKey, ["right", False])
        self.accept("w", self.setKey, ["forward", True])
        self.accept("w-up", self.setKey, ["forward", False])
        self.accept("s", self.setKey, ["back", True])
        self.accept("s-up", self.setKey, ["back", False])
        self.accept("space", self.setKey, ["down", True])
        self.accept("space-up", self.setKey, ["down", False])
        self.accept("shift", self.setKey, ["up", True])
        self.accept("shift-up", self.setKey, ["up", False])

    def setKey(self, key, value):
        self.keyMap[key] = value

    def update(self, task):
        delta = globalClock.getDt()
        if not self.inMenu:
            SPEED = 125
            #Variable that holds what direction the player is inputting
            fblr = 0
            self.timer += delta * 25

            self.killPlane = self.water.getZ() - 25
            if self.flamieNP.getZ() < self.killPlane:
                self.reset()
                
            if self.keyMap["left"]:
                fblr = 1
                old_fblr = fblr
                self.flamieNP.setX(self.flamie, - SPEED * delta)
            if self.keyMap["right"]:
                fblr = 2
                old_fblr = fblr
                self.flamieNP.setX(self.flamie, + SPEED * delta)
            if self.keyMap["forward"]:
                fblr = 3
                old_fblr = fblr
                self.flamieNP.setY(self.flamie, + SPEED * delta)
            if self.keyMap["back"]:
                fblr = 4
                old_fblr = fblr
                self.flamieNP.setY(self.flamie, - SPEED * delta)
            if self.keyMap["up"]:
                #self.flamieNP.setZ(self.flamie, - SPEED * dt)
                self.reset()
                #self.cameraDistance = 20+self.cameraDistance
            if self.keyMap["down"] and self.timer > 1:
                #self.flamieNP.setZ(self.flamie, + SPEED * dt)
                self.timer = 0
                self.jump(delta)
                
            if fblr == 1:
                self.flamie2.setTexture(loader.loadTexture("models/flamie2D/f8.png"))
            elif fblr == 2:
                self.flamie2.setTexture(loader.loadTexture("models/flamie2D/f6.png"))
            elif fblr == 3:
                if old_fblr == 1:
                    self.flamie2.setTexture(loader.loadTexture("models/flamie2D/f1.png"))
                elif old_fblr == 2:
                    self.flamie2.setTexture(loader.loadTexture("models/flamie2D/f4.png"))
                else:
                    self.flamie2.setTexture(loader.loadTexture("models/flamie2D/f3.png"))
            else:
                self.flamie2.setTexture(loader.loadTexture("models/flamie2D/f7.png"))

            if self.floorHandler.isOnGround:
                self.flamieBob(delta)

            #The camera control is borrowed from Kristina's Tech Demo
            #This is also a demo found at: http://www.panda3d.org/forums/viewtopic.php?t=8452

            '''# mouse-controlled camera begins

            # Use mouse input to turn both the Character and the Camera
            if base.mouseWatcherNode.hasMouse():
                md = base.win.getPointer(0)
                x = md.getX()
                y = md.getY()
                deltaX = md.getX() - 200
                deltaY = md.getY() - 200
                # reset mouse cursor position
                base.win.movePointer(0, 200, 200)
                # alter flamie's yaw by an amount proportionate to deltaX
                self.flamie.setH(self.flamie.getH() - 0.3* deltaX)
                # find the new camera pitch and clamp it to a reasonable range
                self.cameraPitch = self.cameraPitch + 0.1 * deltaY
                if (self.cameraPitch < -60): self.cameraPitch = -60
                if (self.cameraPitch >  80): self.cameraPitch =  80
                base.camera.setHpr(0,self.cameraPitch,0)
                # set the camera at around ralph's middle
                # We should pivot around here instead of the view target which is noticebly higher
                base.camera.setPos(0,0,self.cameraTargetHeight/2)
                # back the camera out to its proper distance
                base.camera.setY(base.camera,self.cameraDistance)

            # point the camera at the view target
            viewTarget = Point3(0,0,self.cameraTargetHeight)
            base.camera.lookAt(viewTarget)
            # reposition the end of the  camera's obstruction ray trace
            #self.cameraRay.setPointB(base.camera.getPos())

            # mouse-controlled camera ends'''

            self.waterControl(delta)
            self.water.setX(self.flamieNP.getX() - 250)
            self.water.setY(self.flamieNP.getY() - 250)
            self.cameraModes(delta)
            base.camera.setPos(self.flamieNP.getX(), self.flamieNP.getY() - self.cameraDistance, self.cameraHeight)
            self.camFocus.setPos(self.flamieNP.getX(), self.flamieNP.getY(), self.camFocusCurrZ)
            base.camera.lookAt(self.camFocus)


            '''
            ######################
            #
            # SIMPLE OCCLUSION FOR START AREA
            #
            ######################

            for p in self.start.parts:
                if p.type == 'IceCube':
                    if math.fabs((math.sqrt((p.model.getX() * p.model.getX()) + (p.model.getY() * p.model.getY()))
                    - math.sqrt((self.camFocus.getX() * self.camFocus.getX()) + (self.camFocus.getY() * self.camFocus.getY())))) <= 400:
                        p.show()
                        #Ice cube movement
                        p.bob(delta)
                    else:
                        p.hide()
                    
                if p.type == 'Prism':
                    if p.type == 'Prism':
                        if math.fabs((math.sqrt((p.pos.x * p.pos.x) + (p.pos.y * p.pos.y))
                        - math.sqrt((self.camFocus.getX() * self.camFocus.getX()) + (self.camFocus.getY() * self.camFocus.getY())))) <= 1000:
                            p.show()
                        else:
                            p.hide()


            ######################
            #
            # SIMPLE OCCLUSION FOR CAVE PARTS
            #
            ######################
            for p in self.cave.parts:
                if p.type == 'Prism':
                    if math.fabs((math.sqrt((p.pos.x * p.pos.x) + (p.pos.y * p.pos.y))
                    - math.sqrt((self.flamieNP.getX() * self.flamieNP.getX()) + (self.flamieNP.getY() * self.flamieNP.getY())))) <= 2500:
                        p.show()
                    else:
                        p.hide()
                    
                if p.type == 'IceCube':
                    if math.fabs((math.sqrt((p.model.getX() * p.model.getX()) + (p.model.getY() * p.model.getY()))
                    - math.sqrt((self.flamieNP.getX() * self.flamieNP.getX()) + (self.flamieNP.getY() * self.flamieNP.getY())))) <= 2000:
                        p.show()

                        #Ice cube movement
                        self.cave.moveIceCubes(delta/25)
                        for p in self.cave.iceCubesThirdRoom:
                            p.bob(delta/25)
                        for p in self.cave.iceCubesSecondRoom:
                            p.bob(delta/25)
                        self.cave.bigCube.bob(delta/25)

                        for p in self.start.iceCubes:
                            p.bob(delta)
                    else:
                        p.hide()
            '''

            #Ice cube movement
            self.cave.moveIceCubes(delta)
            for p in self.cave.iceCubesThirdRoom:
                p.bob(delta)
            for p in self.cave.iceCubesSecondRoom:
                p.bob(delta)
            self.cave.bigCube.bob(delta)

            for p in self.start.iceCubes:
                p.bob(delta)

        elif self.inMenu:
            self.menu()


            

        if self.backgroundSound.status() is not self.backgroundSound.PLAYING:
            self.backgroundSound.play()

            
        return task.cont

    def menu(self):
        if self.keyMap["down"]:
            self.inMenu = False
            self.menuScreen.destroy()
            self.initialize()
            

    def flamieBob(self, delta):
        if self.mayFlamieBob:
            self.flamie2.setZ(self.flamie2.getZ() + .5*delta)
            if self.flamie2.getZ() - self.flamie2OriZ > 1:
                self.mayFlamieBob = False
        else:
            self.flamie2.setZ(self.flamie2.getZ() - .5*delta)
            if self.flamie2.getZ() - self.flamie2OriZ < -2:
                self.mayFlamieBob = True
        
    #Function to create a box collision using six polygon. The top face is created as terrain and thus provides gravity.
    #While the rest of the faces only act as wall pushers.
    def collisionBoxCreator(self, posx, posy, posz, length, width, height, floorname, wallname):
        ret = ()
        #Create top face
        terrain = CollisionPolygon(Point3(posx, posy+width, posz), Point3(posx, posy, posz),
                                Point3(posx+length, posy, posz), Point3(posx+length, posy+width, posz))
        terrainCol = CollisionNode(floorname)
        terrainCol.addSolid(terrain)
        terrainCol.setIntoCollideMask(BitMask32.bit(1))
        terrainColNp = self.render.attachNewNode(terrainCol)
        self.cTrav.addCollider(terrainColNp, self.floorHandler)
        ret += (terrainColNp,)
    
        #Create left face
        sideLeft = CollisionPolygon(Point3(posx, posy+width, posz-height), Point3(posx, posy, posz-height),
                                Point3(posx, posy, posz), Point3(posx, posy+width, posz))
        sideLeftCol = CollisionNode(wallname)
        sideLeftCol.addSolid(sideLeft)
        sideLeftCol.setIntoCollideMask(BitMask32.bit(2))
        sideLeftColNp = self.render.attachNewNode(sideLeftCol)
        self.cTrav.addCollider(sideLeftColNp, self.wallHandler)
        ret += (sideLeftColNp,)
        
        #Create right face
        sideRight = CollisionPolygon(Point3(posx+length, posy+width, posz), Point3(posx+length, posy, posz),
                                Point3(posx+length, posy, posz-height), Point3(posx+length, posy+width, posz-height))
        sideRightCol = CollisionNode(wallname)
        sideRightCol.addSolid(sideRight)
        sideRightCol.setIntoCollideMask(BitMask32.bit(2))
        sideRightColNp = self.render.attachNewNode(sideRightCol)
        self.cTrav.addCollider(sideRightColNp, self.wallHandler)
        ret += (sideRightColNp,)
        
        #Create front face
        sideFront = CollisionPolygon(Point3(posx, posy+width, posz-height), Point3(posx, posy+width, posz),
                                Point3(posx+length, posy+width, posz), Point3(posx+length, posy+width, posz-height))
        sideFrontCol = CollisionNode(wallname)
        sideFrontCol.addSolid(sideFront)
        sideFrontCol.setIntoCollideMask(BitMask32.bit(2))
        sideFrontColNp = self.render.attachNewNode(sideFrontCol)
        self.cTrav.addCollider(sideFrontColNp, self.wallHandler)
        ret += (sideFrontColNp,)
        
        #Create back face
        sideBack = CollisionPolygon(Point3(posx, posy, posz), Point3(posx, posy, posz-height),
                                Point3(posx+length, posy, posz-height), Point3(posx+length, posy, posz))
        sideBackCol = CollisionNode(wallname)
        sideBackCol.addSolid(sideBack)
        sideBackCol.setIntoCollideMask(BitMask32.bit(2))
        sideBackColNp = self.render.attachNewNode(sideBackCol)
        self.cTrav.addCollider(sideBackColNp, self.wallHandler)
        ret += (sideBackColNp,)

        #Create bottom face
        sideBot = CollisionPolygon(Point3(posx, posy, posz-height), Point3(posx, posy+width, posz-height),
                                Point3(posx+length, posy+width, posz-height), Point3(posx+length, posy, posz-height))
        sideBotCol = CollisionNode(wallname)
        sideBotCol.addSolid(sideBot)
        sideBotCol.setIntoCollideMask(BitMask32.bit(2))
        sideBotColNp = self.render.attachNewNode(sideBotCol)
        self.cTrav.addCollider(sideBotColNp, self.wallHandler)
        ret += (sideBotColNp,)

        #Uncomment these lines to see the collision polygons.
        '''terrainColNp.show()
        sideLeftColNp.show()
        sideRightColNp.show()
        sideFrontColNp.show()
        sideBackColNp.show()
        sideBotColNp.show()'''

        return ret
        #Old way of creating box collisions (left here for reference)
        '''box = CollisionBox((posx+(length/2), posy+(width/2),-(posz+height/2)), length/2, width/2, height/2)
        boxCol = CollisionNode('testcollision')
        boxCol.addSolid(box)
        boxCol.setIntoCollideMask(BitMask32.bit(2))
        boxColNp = self.render.attachNewNode(boxCol)
        boxHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(boxColNp, self.wallHandler)

        #Uncomment this line to see the collision solids.
        #boxColNp.show()'''

    def checkpointCreator(self, posx, posy, posz, radius):
        cp = loader.loadModel('models/Campfire/logs')
        cp.setPos(posx,posy, posz)
        cp.reparentTo(render)
        checkpoint = CollisionSphere(cp.getX(),cp.getY(),cp.getZ(),radius)
        checkpoint.setTangible(0)
        checkpointCol = CollisionNode('checkpointCol')
        checkpointCol.addSolid(checkpoint)
        checkpointCol.setIntoCollideMask(BitMask32.bit(2))
        checkpointColNp = self.render.attachNewNode(checkpointCol)
        self.cTrav.addCollider(checkpointColNp, self.wallHandler)
Пример #2
0
class Physics:
    def __init__(self):
        self.rayCTrav = CollisionTraverser("collision traverser for ray tests")
        #self.pusher = PhysicsCollisionHandler()
        self.pusher = CollisionHandlerPusher()
        self.pusher.addInPattern('%fn-in-%in')
        self.pusher.addOutPattern('%fn-out-%in')
        self.pusher.addInPattern('%fn-in')
        self.pusher.addOutPattern('%fn-out')

    def startPhysics(self):
        #self.actorNode = ActorNode("playerPhysicsControler")
        #base.physicsMgr.attachPhysicalNode(self.actorNode)
        #self.actorNode.getPhysicsObject().setMass(self.player_mass)
        #self.mainNode = render.attachNewNode(self.actorNode)
        self.mainNode = render.attachNewNode("CharacterColliders")
        self.reparentTo(self.mainNode)

        charCollisions = self.mainNode.attachNewNode(
            CollisionNode(self.char_collision_name))
        #charCollisions.node().addSolid(CollisionSphere(0, 0, self.player_height/4.0, self.player_height/4.0))
        #charCollisions.node().addSolid(CollisionSphere(0, 0, self.player_height/4.0*3.05, self.player_height/4.0))
        charCollisions.node().addSolid(
            CollisionSphere(0, 0, self.player_height / 2.0,
                            self.player_height / 4.0))
        charCollisions.node().setIntoCollideMask(BitMask32(0x80))  # 1000 0000
        if self.show_collisions:
            charCollisions.show()
        self.pusher.addCollider(charCollisions, self.mainNode)
        base.cTrav.addCollider(charCollisions, self.pusher)

        charFFootCollisions = self.attachNewNode(CollisionNode("floor_ray"))
        charFFootCollisions.node().addSolid(CollisionRay(0, 0, 0.5, 0, 0, -1))
        #charFFootCollisions.node().addSolid(CollisionSegment((0, 0, 0.2), (0, 0, -1)))
        charFFootCollisions.node().setIntoCollideMask(BitMask32.allOff())
        charFFootCollisions.node().setFromCollideMask(
            BitMask32(0x7f))  # 0111 1111
        if self.show_collisions:
            charFFootCollisions.show()

        self.floor_handler = CollisionHandlerFloor()
        self.floor_handler.addCollider(charFFootCollisions, self.mainNode)
        #self.floor_handler.setOffset(0)
        self.floor_handler.setMaxVelocity(5)
        base.cTrav.addCollider(charFFootCollisions, self.floor_handler)

        self.accept("{}-in".format(self.char_collision_name),
                    self.checkCharCollisions)

        self.raytest_segment = CollisionSegment(0, 1)
        self.raytest_np = render.attachNewNode(CollisionNode("testRay"))
        self.raytest_np.node().addSolid(self.raytest_segment)
        self.raytest_np.node().setIntoCollideMask(BitMask32.allOff())
        self.raytest_np.node().setFromCollideMask(BitMask32(0x7f))  # 0111 1111
        if self.show_collisions:
            self.raytest_np.show()

        self.raytest_queue = CollisionHandlerQueue()
        self.rayCTrav.addCollider(self.raytest_np, self.raytest_queue)

    def stopPhysics(self):
        self.raytest_segment.removeNode()
        self.pusher.clearColliders()
        self.floor_handler.clearColliders()
        self.rayCTrav.clearColliders()

    def updatePlayerPos(self, speed, heading, dt):
        if heading is not None:
            self.mainNode.setH(camera, heading)
            self.mainNode.setP(0)
            self.mainNode.setR(0)
        self.mainNode.setFluidPos(self.mainNode, speed)
        self.doStep()

    def checkCharCollisions(self, args):
        self.doStep()

    def doStep(self):
        # do the step height check
        tmpNP = self.mainNode.attachNewNode("temporary")
        tmpNP.setPos(self.mainNode, 0, 0, -self.stepheight)
        pointA = self.mainNode.getPos(render)
        pointA.setZ(pointA.getZ() + self.player_height / 1.8)
        pointB = tmpNP.getPos(render)
        if pointA == pointB: return
        char_step_collision = self.getFirstCollisionInLine(pointA, pointB)
        tmpNP.removeNode()
        if char_step_collision is not None:
            self.mainNode.setFluidZ(char_step_collision.getZ())
            return True
        return False

    def getFirstCollisionInLine(self, pointA, pointB):
        """A simple raycast check which will return the first collision point as
        seen from point A towards pointB"""
        self.raytest_segment.setPointA(pointA)
        self.raytest_segment.setPointB(pointB)
        self.rayCTrav.traverse(render)
        self.raytest_queue.sortEntries()
        pos = None
        if self.raytest_queue.getNumEntries() > 0:
            pos = self.raytest_queue.getEntry(0).getSurfacePoint(render)
        return pos
Пример #3
0
class Physics:
    def __init__(self):
        self.rayCTrav = CollisionTraverser("collision traverser for ray tests")
        #self.pusher = PhysicsCollisionHandler()
        self.pusher = CollisionHandlerPusher()
        self.pusher.addInPattern('%fn-in-%in')
        self.pusher.addOutPattern('%fn-out-%in')
        self.pusher.addInPattern('%fn-in')
        self.pusher.addOutPattern('%fn-out')

    def startPhysics(self):
        #self.actorNode = ActorNode("playerPhysicsControler")
        #base.physicsMgr.attachPhysicalNode(self.actorNode)
        #self.actorNode.getPhysicsObject().setMass(self.player_mass)
        #self.mainNode = render.attachNewNode(self.actorNode)
        self.mainNode = render.attachNewNode("CharacterColliders")
        self.reparentTo(self.mainNode)

        charCollisions = self.mainNode.attachNewNode(CollisionNode(self.char_collision_name))
        #charCollisions.node().addSolid(CollisionSphere(0, 0, self.player_height/4.0, self.player_height/4.0))
        #charCollisions.node().addSolid(CollisionSphere(0, 0, self.player_height/4.0*3.05, self.player_height/4.0))
        charCollisions.node().addSolid(CollisionSphere(0, 0, self.player_height/2.0, self.player_height/4.0))
        charCollisions.node().setIntoCollideMask(BitMask32(0x80))  # 1000 0000
        if self.show_collisions:
            charCollisions.show()
        self.pusher.addCollider(charCollisions, self.mainNode)
        base.cTrav.addCollider(charCollisions, self.pusher)

        charFFootCollisions = self.attachNewNode(CollisionNode("floor_ray"))
        charFFootCollisions.node().addSolid(CollisionRay(0, 0, 0.5, 0, 0, -1))
        #charFFootCollisions.node().addSolid(CollisionSegment((0, 0, 0.2), (0, 0, -1)))
        charFFootCollisions.node().setIntoCollideMask(BitMask32.allOff())
        charFFootCollisions.node().setFromCollideMask(BitMask32(0x7f))  # 0111 1111
        if self.show_collisions:
            charFFootCollisions.show()

        self.floor_handler = CollisionHandlerFloor()
        self.floor_handler.addCollider(charFFootCollisions, self.mainNode)
        #self.floor_handler.setOffset(0)
        self.floor_handler.setMaxVelocity(5)
        base.cTrav.addCollider(charFFootCollisions, self.floor_handler)

        self.accept("{}-in".format(self.char_collision_name), self.checkCharCollisions)

        self.raytest_segment = CollisionSegment(0, 1)
        self.raytest_np = render.attachNewNode(CollisionNode("testRay"))
        self.raytest_np.node().addSolid(self.raytest_segment)
        self.raytest_np.node().setIntoCollideMask(BitMask32.allOff())
        self.raytest_np.node().setFromCollideMask(BitMask32(0x7f))  # 0111 1111
        if self.show_collisions:
            self.raytest_np.show()

        self.raytest_queue = CollisionHandlerQueue()
        self.rayCTrav.addCollider(self.raytest_np, self.raytest_queue)

    def stopPhysics(self):
        self.raytest_segment.removeNode()
        self.pusher.clearColliders()
        self.floor_handler.clearColliders()
        self.rayCTrav.clearColliders()

    def updatePlayerPos(self, speed, heading, dt):
        if heading is not None:
            self.mainNode.setH(camera, heading)
            self.mainNode.setP(0)
            self.mainNode.setR(0)
        self.mainNode.setFluidPos(self.mainNode, speed)
        self.doStep()

    def checkCharCollisions(self, args):
        self.doStep()

    def doStep(self):
        # do the step height check
        tmpNP = self.mainNode.attachNewNode("temporary")
        tmpNP.setPos(self.mainNode, 0, 0, -self.stepheight)
        pointA = self.mainNode.getPos(render)
        pointA.setZ(pointA.getZ() + self.player_height/1.8)
        pointB = tmpNP.getPos(render)
        if pointA == pointB: return
        char_step_collision = self.getFirstCollisionInLine(pointA, pointB)
        tmpNP.removeNode()
        if char_step_collision is not None:
            self.mainNode.setFluidZ(char_step_collision.getZ())
            return True
        return False

    def getFirstCollisionInLine(self, pointA, pointB):
        """A simple raycast check which will return the first collision point as
        seen from point A towards pointB"""
        self.raytest_segment.setPointA(pointA)
        self.raytest_segment.setPointB(pointB)
        self.rayCTrav.traverse(render)
        self.raytest_queue.sortEntries()
        pos = None
        if self.raytest_queue.getNumEntries() > 0:
            pos = self.raytest_queue.getEntry(0).getSurfacePoint(render)
        return pos
Пример #4
0
class Game(DirectObject):
    def __init__(self):

        self.setAI()

        self.keyMap = {"left": 0, "right": 0, "forward": 0, "backward": 0, "cam-left": 0, "cam-right": 0}
        base.win.setClearColor(Vec4(0, 0, 0, 1))

        # the menu
        self.loadAudio()
        self.showMenu()

        # keyboard and mouse events
        self.accept("escape", sys.exit)
        self.accept("w", self.setKey, ["forward", 1])
        self.accept("a", self.setKey, ["left", 1])
        self.accept("s", self.setKey, ["backward", 1])
        self.accept("d", self.setKey, ["right", 1])
        self.accept("w-up", self.setKey, ["forward", 0])
        self.accept("a-up", self.setKey, ["left", 0])
        self.accept("s-up", self.setKey, ["backward", 0])
        self.accept("d-up", self.setKey, ["right", 0])
        self.accept("arrow_left", self.setKey, ["cam-left", 1])
        self.accept("arrow_left-up", self.setKey, ["cam-left", 0])
        self.accept("arrow_right", self.setKey, ["cam-right", 1])
        self.accept("arrow_right-up", self.setKey, ["cam-right", 0])

        # create some lighting
        ambientLight = AmbientLight("ambientLight")
        ambientLight.setColor(Vec4(0.3, 0.3, 0.3, 1))
        directionalLight = DirectionalLight("directionalLight")
        directionalLight.setDirection(Vec3(-5, -5, -5))
        directionalLight.setColor(Vec4(1, 1, 1, 1))
        directionalLight.setSpecularColor(Vec4(1, 1, 1, 1))
        render.setLight(render.attachNewNode(ambientLight))
        render.setLight(render.attachNewNode(directionalLight))

    def loadAudio(self):
        self.startAudio = base.loader.loadSfx("../assets/audio/start.mp3")
        self.gameAudio = base.loader.loadSfx("../assets/audio/game.mp3")
        self.winAudio = base.loader.loadSfx("../assets/audio/win.mp3")
        self.loseAudio = base.loader.loadSfx("../assets/audio/lose.mp3")

    def createPresents(self):
        self.speedPill = loader.loadModel("../assets/models/capsule/capsule")
        self.speedPill.reparentTo(render)
        self.speedPill.setScale(0.025)
        self.speedPill.setPos(-43.9744, 32.031, 0.6)

        self.speedPillb = loader.loadModel("../assets/models/capsule/capsule")
        self.speedPillb.reparentTo(render)
        self.speedPillb.setScale(0.025)
        self.speedPillb.setPos(-57.7858, -61.5068, 3.80818)

        self.banana = loader.loadModel("../assets/models/banana/banana")
        self.banana.reparentTo(render)
        self.banana.setScale(3)
        self.banana.setPos(-72.484, -7.14435, 5.05246)

        self.sphinx = loader.loadModel("../assets/models/sphinx/sphinx")
        self.sphinx.reparentTo(render)
        self.sphinx.setScale(0.0025)
        self.sphinx.setPos(-48.0702, -39.4669, 0.5)

    def createMilesAI(self):
        startPos = self.env.find("**/start_point").getPos()

        # load miles actor
        self.miles1 = Actor(
            "../assets/models/miles/tails",
            {
                "board": "../assets/models/miles/tails-board",
                "win": "../assets/models/miles/tails-win",
                "fwboard": "../assets/models/miles/tails-fallingwboard",
                "fwoboard": "../assets/models/miles/tails-fallingwoboard",
            },
        )

        self.miles2 = Actor(
            "../assets/models/miles/tails",
            {
                "board": "../assets/models/miles/tails-board",
                "win": "../assets/models/miles/tails-win",
                "fwboard": "../assets/models/miles/tails-fallingwboard",
                "fwoboard": "../assets/models/miles/tails-fallingwoboard",
            },
        )

        self.miles = [self.miles1, self.miles2]

        self.miles1.reparentTo(render)
        self.miles1.setScale(0.05)
        self.miles1.setPlayRate(3, "run")
        self.miles1.loop("board")
        self.miles1.setPos(startPos[0] + 5, startPos[1] - 20, startPos[2])

        self.miles1GroundRay = CollisionRay()
        self.miles1GroundRay.setOrigin(0, 0, 1000)
        self.miles1GroundRay.setDirection(0, 0, -1)
        self.miles1GroundCol = CollisionNode("miles1Ray")
        self.miles1GroundCol.addSolid(self.miles1GroundRay)
        self.miles1GroundCol.setFromCollideMask(BitMask32.bit(0))
        self.miles1GroundCol.setIntoCollideMask(BitMask32.allOff())
        self.miles1GroundColNp = self.miles1.attachNewNode(self.miles1GroundCol)
        self.miles1GroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.miles1GroundColNp, self.miles1GroundHandler)

        # AI code for miles1
        self.miles1AI = AICharacter("miles1", self.miles1, 100, 1, 7)
        self.AIworld.addAiChar(self.miles1AI)
        self.miles1AIbehaviors = self.miles1AI.getAiBehaviors()

        # pursue behavior
        self.miles1AIbehaviors.pursue(self.sonic)

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

        self.miles2.reparentTo(render)
        self.miles2.setScale(0.05)
        self.miles2.setPlayRate(3, "run")
        self.miles2.loop("win")
        self.miles2.setPos(startPos[0] - 5, startPos[1] - 20, startPos[2])

        self.miles2GroundRay = CollisionRay()
        self.miles2GroundRay.setOrigin(0, 0, 1000)
        self.miles2GroundRay.setDirection(0, 0, -1)
        self.miles2GroundCol = CollisionNode("miles2Ray")
        self.miles2GroundCol.addSolid(self.miles2GroundRay)
        self.miles2GroundCol.setFromCollideMask(BitMask32.bit(0))
        self.miles2GroundCol.setIntoCollideMask(BitMask32.allOff())
        self.miles2GroundColNp = self.miles2.attachNewNode(self.miles2GroundCol)
        self.miles2GroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.miles2GroundColNp, self.miles2GroundHandler)

        self.miles2AI = AICharacter("miles2", self.miles2, 100, 1, 7)
        self.AIworld.addAiChar(self.miles2AI)
        self.miles2AIbehaviors = self.miles2AI.getAiBehaviors()

        # puruse behavior
        self.miles2AIbehaviors.pursue(self.sonic)

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

    def createTrexAI(self):
        startPos = self.env.find("**/start_point").getPos()

        # Load the trex actor and loop its animation
        self.trex = Actor(
            "../assets/models/trex/trex",
            {"run": "../assets/models/trex/trex-run", "eat": "../assets/models/trex/trex-eat"},
        )
        self.trex.reparentTo(render)
        self.trex.setScale(0.25)
        self.trex.setPlayRate(3, "run")
        self.trex.loop("run")
        self.trex.setPos(startPos[0], startPos[-1] - 20, startPos[2])

        self.trexGroundRay = CollisionRay()
        self.trexGroundRay.setOrigin(0, 0, 1000)
        self.trexGroundRay.setDirection(0, 0, -1)
        self.trexGroundCol = CollisionNode("trexRay")
        self.trexGroundCol.addSolid(self.trexGroundRay)
        self.trexGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.trexGroundCol.setIntoCollideMask(BitMask32.allOff())
        self.trexGroundColNp = self.trex.attachNewNode(self.trexGroundCol)
        self.trexGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.trexGroundColNp, self.trexGroundHandler)

        # self.pusher.addCollider(self.trexGroundColNp, self.sonic)

        # AI code for trex
        self.trexAI = AICharacter("trex", self.trex, 100, 1, 7)
        self.AIworld.addAiChar(self.trexAI)
        self.trexAIbehaviors = self.trexAI.getAiBehaviors()

        # pursue behavior
        # self.trexAIbehaviors.pursue(self.sonic)

        self.trexAIbehaviors.pursue(self.sonic)

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

    # to create the AI world
    def setAI(self):
        self.AIworld = AIWorld(render)
        taskMgr.add(self.AIUpdate, "AIUpdate")

    def AIUpdate(self, task):
        self.AIworld.update()
        return task.cont

    def moveMiles1AI(self, task):
        startpos = self.miles1.getPos()

        entries = []
        for i in range(self.miles1GroundHandler.getNumEntries()):
            entry = self.miles1GroundHandler.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"):
            miles1Z = entries[0].getSurfacePoint(render).getZ()
            miles1Y = entries[0].getSurfacePoint(render).getY()
            miles1X = entries[0].getSurfacePoint(render).getX()

            self.miles1.setZ(entries[0].getSurfacePoint(render).getZ())
        else:
            self.miles1.setPos(startpos)

        return task.cont

    def moveMiles2AI(self, task):
        startpos = self.miles2.getPos()

        entries = []
        for i in range(self.miles2GroundHandler.getNumEntries()):
            entry = self.miles2GroundHandler.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"):
            miles2Z = entries[0].getSurfacePoint(render).getZ()
            miles2Y = entries[0].getSurfacePoint(render).getY()
            miles2X = entries[0].getSurfacePoint(render).getX()

            self.miles2.setZ(entries[0].getSurfacePoint(render).getZ())
        else:
            self.miles2.setPos(startpos)

        return task.cont

    def moveTrexAI(self, task):

        startpos = self.trex.getPos()

        entries = []
        for i in range(self.trexGroundHandler.getNumEntries()):
            entry = self.trexGroundHandler.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"):
            trexZ = entries[0].getSurfacePoint(render).getZ()
            trexY = entries[0].getSurfacePoint(render).getY()
            trexX = entries[0].getSurfacePoint(render).getX()

            self.trex.setZ(entries[0].getSurfacePoint(render).getZ())
        else:
            self.trex.setPos(startpos)

        return task.cont

    def setKey(self, action, value):
        self.keyMap[action] = value

    def move(self, task):

        base.camera.lookAt(self.sonic)
        if self.keyMap["cam-left"] != 0:
            base.camera.setX(base.camera, -30 * globalClock.getDt())
        if self.keyMap["cam-right"] != 0:
            base.camera.setX(base.camera, +30 * globalClock.getDt())

        startpos = self.sonic.getPos()

        if self.keyMap["left"] != 0:
            self.sonic.setH(self.sonic.getH() + 300 * globalClock.getDt())
        if self.keyMap["right"] != 0:
            self.sonic.setH(self.sonic.getH() - 300 * globalClock.getDt())
        if self.keyMap["forward"] != 0:
            self.sonic.setY(self.sonic, -100 * globalClock.getDt() * SPEED)
        if self.keyMap["backward"] != 0:
            self.sonic.setY(self.sonic, 100 * globalClock.getDt() * SPEED)

        # If sonic is moving, loop the run animation
        # If he is standing still, stop the animation
        if (
            (self.keyMap["forward"] != 0)
            or (self.keyMap["left"] != 0)
            or (self.keyMap["right"] != 0)
            or (self.keyMap["backward"] != 0)
        ):
            if self.isMoving is False:
                self.sonic.loop("board")
                self.isMoving = True
        else:
            if self.isMoving:
                self.sonic.stop()
                self.sonic.pose("fwboard", 5)
                self.isMoving = False

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

        camvec = self.sonic.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)

        entries = []
        for i in range(self.sonicGroundHandler.getNumEntries()):
            entry = self.sonicGroundHandler.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.sonic.setZ(entries[0].getSurfacePoint(render).getZ())
        else:
            self.sonic.setPos(startpos)

        # keep the camera at one foot avoce the terrian
        # or two feet above sonic, whichever is greater

        entries = []
        for i in range(self.camGroundHandler.getNumEntries()):
            entry = self.camGroundHandler.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.sonic.getZ() + 2.0:
            base.camera.setZ(self.sonic.getZ() + 2.0)

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

        self.floater.setPos(self.sonic.getPos())
        self.floater.setZ(self.sonic.getZ() + 2.0)
        base.camera.lookAt(self.floater)

        # Update the camera based on mouse movement
        md = base.win.getPointer(0)
        x = md.getX()
        y = md.getY()
        if base.win.movePointer(0, base.win.getXSize() / 2, base.win.getYSize() / 2):
            base.camera.setX(base.camera, (x - base.win.getXSize() / 2) * globalClock.getDt() * 0.1)

        # timer countdown
        global TIME
        TIME = TOTAL_TIME - int(task.time)

        self.time_text.setText("Time Remaining :: %i" % TIME)

        return task.cont

    def checkWin(self, task):
        delta = self.sonic.getDistance(self.flag)
        if delta < 25:
            self.winText = OnscreenText(
                text="You Win!", style=1, fg=(1, 1, 1, 1), pos=(0, 0), align=TextNode.ACenter, scale=0.4
            )
            self.resetBtn = DirectButton(
                text=("Restart", "Restart", "Restart"), scale=0.1, command=self.resetGame, pos=(0, 0, -0.7)
            )

            taskMgr.remove("moveTask")
            if self.gameAudio.status() == self.gameAudio.PLAYING:
                self.gameAudio.stop()
                self.winAudio.play()
            return task.done
        return task.cont

    def checkTime(self, task):
        time_left = TIME
        if time_left == 0:
            taskMgr.remove("moveTask")
            self.gameOverText = OnscreenText(
                text="Game Over!", style=1, fg=(1, 1, 1, 1), pos=(0, 0), align=TextNode.ACenter, scale=0.4
            )
            self.resetBtn = DirectButton(
                text=("Restart", "Restart", "Restart"), scale=0.1, command=self.resetGame, pos=(0, 0, -0.7)
            )
            return task.done

        return task.cont

    def checkGameOver(self, task):
        health_left = HEALTH
        if 1 <= health_left <= 10:
            global SPEED
            SPEED = 0.5
        if health_left <= 0:
            taskMgr.remove("moveTask")
            self.gameOverText = OnscreenText(
                text="Game Over!", style=1, fg=(1, 1, 1, 1), pos=(0, 0), align=TextNode.ACenter, scale=0.4
            )
            self.resetBtn = DirectButton(
                text=("Restart", "Restart", "Restart"), scale=0.1, command=self.resetGame, pos=(0, 0, -0.7)
            )

            if self.gameAudio.status() == self.gameAudio.PLAYING:
                self.gameAudio.stop()
                self.loseAudio.play()
            return task.done
        return task.cont

    def resetGame(self):
        # re-add the move task
        taskMgr.add(self.move, "moveTask")

        # remove GUI elements
        if hasattr(game, "gameOverText"):
            self.gameOverText.destroy()
        if hasattr(game, "resetBtn"):
            self.resetBtn.destroy()
        if hasattr(game, "winText"):
            self.winText.destroy()

        if (self.winAudio.status() == self.winAudio.PLAYING) or (self.loseAudio.status() == self.loseAudio.PLAYING):
            self.winAudio.stop()
            self.loseAudio.stop()
            self.gameAudio.play()

        # reset position
        self.sonic.setPos(self.START)

        # reset time count
        global TIME, TOTAL_TIME
        TIME = 120
        TOTAL_TIME = 120

    def showMenu(self):
        # TODO: add more controls text
        self.startAudio.play()
        self.gameNameText = OnscreenText(
            text="Run, Baby, Run", style=1, fg=(1, 1, 1, 1), pos=(0, 0.5), align=TextNode.ACenter, scale=0.3
        )
        text = """
        Controls
        ===============
        [W] - Move Forward
        [A] - Move Left
        [S] - Move Backwards
        [D] - Move Right
        [<-] - Rotate Camera (Left)
        [->] - Rotate Camera (Right)

        Camera Movements are also controlled by mouse
        """
        self.controlsText = OnscreenText(
            text=text, style=1, fg=(1, 1, 1, 1), pos=(0, 0), align=TextNode.ACenter, scale=0.05
        )
        self.startBtn = DirectButton(
            text=("Start", "Start", "Start"), scale=0.1, command=self.startGame, pos=(0, 0, -0.7)
        )

    def checkConditions(self, task):
        trexDelta = self.sonic.getDistance(self.trex)
        miles1Delta = self.sonic.getDistance(self.miles1)
        miles2Delta = self.sonic.getDistance(self.miles2)
        if trexDelta <= 5 or miles1Delta <= 15 or miles2Delta <= 15:
            global HEALTH
            HEALTH = HEALTH - 1
            if HEALTH < 0:
                HEALTH = 0
            self.health_text.setText("Health :: %i" % HEALTH)

        return task.cont

    def checkSpeed(self, task):
        speedDelta = self.sonic.getDistance(self.speedPill)
        if speedDelta <= 30:
            global SPEED
            SPEED = SPEED + 1
            self.speedPill.removeNode()
            return task.done
        return task.cont

    def checkSpeed2(self, task):
        speedDelta2 = self.sonic.getDistance(self.speedPillb)
        if speedDelta2 <= 3720:
            global SPEED
            SPEED = SPEED + 1
            self.speedPillb.removeNode()
            return task.done
        return task.cont

    def checkHealthBoost(self, task):
        healthDelta = self.sonic.getDistance(self.banana)
        if healthDelta <= 3:
            global HEALTH
            HEALTH = HEALTH + 10
            if HEALTH > 100:
                HEALTH = 100
            self.banana.removeNode()
            self.health_text.setText("Health :: %i" % HEALTH)
            return task.done
        return task.cont

    def checkTimeAdd(self, task):
        timeAddDelta = self.sonic.getDistance(self.sphinx)
        if timeAddDelta <= 507:
            global TOTAL_TIME
            TOTAL_TIME = TOTAL_TIME + 10
            self.time_text.setText("Time Remaining :: %i" % TOTAL_TIME)
            self.sphinx.removeNode()
            return task.done
        return task.cont

    def startGame(self):
        # remove menu main elements
        if hasattr(game, "startBtn"):
            self.startBtn.destroy()
        if hasattr(game, "gameNameText"):
            self.gameNameText.destroy()
        if hasattr(game, "controlsText"):
            self.controlsText.destroy()
        if self.startAudio.status() == self.startAudio.PLAYING:
            self.startAudio.stop()
            self.gameAudio.play()

        # HUD information (time, health)
        self.time_text = addInstruction((-1.2, 0.9), "Time Reamining :: %i" % TIME)
        self.health_text = addInstruction((0.9, 0.9), "Health :: %i" % HEALTH)

        # the environment
        self.env = loader.loadModel("../assets/models/world")
        self.env.reparentTo(render)
        self.env.setPos(0, 0, 0)

        # the flag(destination)
        self.flag = loader.loadModel("../assets/models/flag/flag")
        self.flag.reparentTo(render)
        self.flag.setScale(0.1)
        self.flag.setPos(0, 0, 5)
        self.DESTINATION = self.flag.getPos()

        # main character(sonic)
        self.START = self.env.find("**/start_point").getPos()
        self.sonic = Actor(
            "../assets/models/sonic/sonic",
            {
                "run": "../assets/models/sonic/sonic-run",
                "win": "../assets/models/sonic/sonic-win",
                "board": "../assets/models/sonic/sonic-board",
                "fwboard": "../assets/models/sonic/sonic-fallingwboard",
                "fwoboard": "../assets/models/sonic/sonic-fallingwoboard",
            },
        )
        self.sonic.reparentTo(render)
        self.sonic.setScale(0.05)
        self.sonic.setPos(self.START)

        # create a floater object to be used as a temporary
        # variable in a variety of calculations
        self.floater = NodePath(PandaNode("floater"))
        self.floater.reparentTo(render)

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

        # varible to keep track of moving state
        self.isMoving = False

        # variable to keep track of speed boost
        self.speedBoost = False

        # variable to keep track of invulnerability
        self.invul = False

        # setup the camera
        base.disableMouse()
        base.camera.setPos(self.sonic.getX(), self.sonic.getY() + 10, 2)

        self.cTrav = CollisionTraverser()
        # self.pusher = CollisionHandlerPusher()

        self.sonicGroundRay = CollisionRay()
        self.sonicGroundRay.setOrigin(0, 0, 1000)
        self.sonicGroundRay.setDirection(0, 0, -1)
        self.sonicGroundCol = CollisionNode("sonicRay")
        self.sonicGroundCol.addSolid(self.sonicGroundRay)
        # self.sonicGroundCol.addSolid(CollisionSphere(0, 0, 1.5, 1.5))
        self.sonicGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.sonicGroundCol.setIntoCollideMask(BitMask32.allOff())
        self.sonicGroundColNp = self.sonic.attachNewNode(self.sonicGroundCol)
        self.sonicGroundHandler = CollisionHandlerQueue()
        self.sonicPusher = CollisionHandlerPusher()
        self.sonicPusher.addInPattern("%fn-into-%in")
        self.cTrav.addCollider(self.sonicGroundColNp, self.sonicGroundHandler)
        # self.cTrav.addCollider(self.sonicGroundColNp, self.sonicPusher)
        # self.sonicPusher.addCollider(self.sonicGroundColNp, self.sonic)

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

        # see the collision rays
        # self.sonicGroundColNp.show()
        # self.camGroundColNp.show()

        # visual representation of the collisions occuring
        # self.cTrav.showCollisions(render)

        # create AI characters
        self.createTrexAI()
        self.createMilesAI()

        # place the presents
        self.createPresents()

        # check winning condition
        taskMgr.add(self.checkWin, "checkWin")

        # check time conditions
        taskMgr.add(self.checkTime, "checkTime")

        # check health conditions
        taskMgr.add(self.checkGameOver, "checkGameOver")

        # check other conditions
        taskMgr.add(self.checkConditions, "checkConditions")

        taskMgr.add(self.checkSpeed, "checkSpeed")
        taskMgr.add(self.checkSpeed2, "checkSpeed2")
        taskMgr.add(self.checkHealthBoost, "checkHealthBoost")
        taskMgr.add(self.checkTimeAdd, "checkTimeAdd")