예제 #1
0
class mainGame(ShowBase):
    '''
    mainGame class is the actual instance of the panda application, inherits from ShowBase
    it contains functions and methods to control the actual game
    '''

    #procedure class constructor
    def __init__(self):
        '''
        class constructor
        Creates environment, player objects
        '''
        # initialize
        #ShowBase.__init__(self)
        # set keybinds
        setKeybinds()
        mission = self
        self.nextValidSpawn = 0
        # handle collisions
        # collision traversal 'traverses' instances of collides
        base.cTrav = CollisionTraverser()  #switched to base
        base.pusher = CollisionHandlerPusher()  #switched to base

        base.pusher.addInPattern('entityCollision')
        base.groundHandler = CollisionHandlerQueue()
        # add catch-all handler
        #base.entityCollisionHandler.addInPattern('entityCollision')

        # handle entity collisions
        # pass to entityCollided
        base.accept('entityCollision', entityCollided)
        # handle bullet collisions
        # accept this event and pass it to method bulletCollided
        base.accept('bulletCollision', bulletCollided)

        # self.render references base.render
        self.render = base.render

        mission.CAPTURE_TIME = 15  #time to capture single flag
        mission.CAPTURE_RADIUS = 300  #radius that needs to be controlled to cap flag

        #cleanup is list that saves items to destroy on mission end
        base.cleanup = []
        # entities is list of all entities in gamespace. Used for applying gravity etc
        base.entities = []
        # also keep a running list of all structures
        base.structures = []

        #setup for AI
        base.AIworld = AIWorld(base.render)

        # load environment
        self.makeEnviron("example")

        # sfx handler for 3D audio
        #base.audio3d = Audio3DManager(base.sfxManagerList[0], base.camera)
        #background music
        self.music = base.loader.loadSfx("sounds/dramatic.ogg")
        self.music.setLoop(True)
        self.music.setVolume(0.15)
        self.music.play()

        # load GUI
        self.makeGUI()

        #set up player and camera
        #set mouse mode to relative
        base.disableMouse()
        setMouseMode(1)

        #find nodes in environment and store on game object
        self.team0Node = self.environment.find("**/team0")
        self.team0BaseNode = self.environment.find("**/team0base")
        self.team1Node = self.environment.find("**/team1")
        self.team1BaseNode = self.environment.find("**/team1base")

        #load occlusion nodes from environment
        #format in blender:
        #occlusion planes all parented to base level 'occluders' empty
        #occlusion planes named 'occluder#'
        occluder_nodes = self.environment.findAllMatches(
            '**/occluders/+OccluderNode')
        for occluder_nodepath in occluder_nodes:
            base.render.setOccluder(occluder_nodepath)
            occluder_nodepath.node().setDoubleSided(True)

        #for now will attempt to load up to 12 flags, and reject entries that don't work
        #TODO: dynamically load based on amount of flag nodes found
        #also may change capture time depending on flag
        self.flags = []
        self.outposts = []
        for i in range(12):
            thisFlag = self.environment.find("**/flag" + str(i))
            #only append if found successfully
            if thisFlag.error_type == 0:
                self.flags.append(thisFlag)
                thisFlag.setPythonTag('lastCapture',
                                      None)  #last capture attempt
                thisFlag.setPythonTag('lastCaptureTeam',
                                      None)  #last team that tried to cap

        self.spawnPlayer()
        #standing=rifleman(base,self.team0Node.getPos(),0)
        #running = rifleman(base, self.team0Node.getPos(), 0)
        #soldat = rifleman(base, self.team0Node.getPos(), 0)
        #instancee=base.render.attachNewNode('blast')
        #instancee.setX(instancee.getX()+200)
        #instance=soldat.instanceTo(instancee)
        #instancee.setX(instancee.getX()-180)
        #enemy = rifleman(base, (20, 300, 0),1)
        #friendly = rifleman(base, self.team0Node.getPos(), 0)
        #friendly.setGoal(self.flags[1])

        #load gameplay logic

        # update all entities
        taskMgr.add(self.updateEntities, "updateEntitiesTask")
        self.playerScore = 0

        self.spawnBases()
        self.spawnEnemies()

    #procedure spawnPlayer
    # just logic for making player and putting on scene
    def spawnPlayer(self):
        #spawn player
        newPos = (self.team0Node.getX(), self.team0Node.getY(),
                  self.team0BaseNode.getZ() + 20)
        self.player = player("models/m15", base, self.team0Node.getPos())
        #store on base for external use
        base.player = self.player
        self.player.setScale(2)
        self.player.score = 0

        locationJoint = self.player.exposeJoint(None, "modelRoot",
                                                "frontWeaponPod")
        print((locationJoint.getParent()))

        M1X_M2(self.player, locationJoint)

    #procedure makeEnviron
    def makeEnviron(self, envModel):
        '''
        creates environment of specified name and parents to renderer
        '''
        #load environment model and add to renderer
        self.environment = base.loader.loadModel("models/" + envModel)
        base.navmesh = "models/" + envModel + ".navmesh.csv"
        self.environment.reparentTo(base.render)
        self.environment.setPos(0, 0, -10)
        #add to cleanup list
        base.cleanup.append(self.environment)

        # FROM PANDA3D DOCUMENTATION
        # Enable a 'light ramp' - this discretizes the lighting,
        # which is half of what makes a model look like a cartoon.
        # Light ramps only work if shader generation is enabled,
        # so we call 'setShaderAuto'.
        tempnode = NodePath(PandaNode("temp node"))
        tempnode.setAttrib(LightRampAttrib.makeSingleThreshold(0.5, 0.4))
        tempnode.setShaderAuto()
        # add to cleanup
        base.cleanup.append(tempnode)
        base.cam.node().setInitialState(tempnode.getState())

        self.separation = 1  # Pixels
        filterok = base.filters.setCartoonInk(separation=self.separation)
        if (filterok == False):
            print("not good enough GPU")
            exit()
        base.accept("v", base.bufferViewer.toggleEnable)
        plightnode = PointLight("point light")
        plightnode.setAttenuation((1, 0, 0))
        plight = base.render.attachNewNode(plightnode)
        plight.node().setScene(base.render)
        plight.node().setShadowCaster(True, 512, 512)
        plight.setPos(0, 190, 300)
        print(plight.getHpr())
        plight.lookAt(self.environment)
        print(plight.getHpr())
        alightnode = AmbientLight("ambient light")
        alightnode.setColor((0.8, 0.8, 0.8, 1))
        alight = base.render.attachNewNode(alightnode)
        base.render.setLight(alight)
        base.render.setLight(plight)

        base.cleanup.append(alight)
        base.cleanup.append(plight)

        #load skybox
        skybox = base.loader.loadModel("models/skybox.bam")
        skybox.reparent_to(base.render)
        skybox.set_scale(20000)

        skybox_texture = base.loader.loadTexture("models/tex/sky.png")
        skybox_texture.set_minfilter(SamplerState.FT_linear)
        skybox_texture.set_magfilter(SamplerState.FT_linear)
        skybox_texture.set_wrap_u(SamplerState.WM_repeat)
        skybox_texture.set_wrap_v(SamplerState.WM_mirror)
        skybox_texture.set_anisotropic_degree(16)
        skybox.set_texture(skybox_texture)

        skybox_shader = Shader.load(Shader.SL_GLSL, "skybox.vert.glsl",
                                    "skybox.frag.glsl")
        skybox.set_shader(skybox_shader)

    #procedure make GUI
    def makeGUI(self):
        '''
        Creates GUI controls and renders on screen
        Health, targeting reticle, ammunition, weapon display, score
        store results in self.playerGUI
        '''
        # TODO GUI logic for player
        self.playerGUI = DirectFrame(
            frameSize=(base.a2dLeft, base.a2dRight, base.a2dBottom,
                       base.a2dTop),
            frameColor=(0, 0, 0, 0)  #make completely transparent
        )
        reticle = OnscreenImage(image='images/reticle.png',
                                scale=(0.5, 1, .25))
        reticle.setTransparency(TransparencyAttrib.MAlpha)
        reticle.reparentTo(self.playerGUI)
        self.playerGUI.HP = OnscreenText(text="HEALTH",
                                         pos=(0.95, 0.8),
                                         scale=0.2,
                                         fg=(0, 0, 90, 1),
                                         align=TextNode.ACenter,
                                         mayChange=1)

        self.playerGUI.HP.reparentTo(self.playerGUI)
        self.playerGUI.score = OnscreenText(text="Score: 0",
                                            pos=(-1.5, 0.8),
                                            scale=0.2,
                                            fg=(0, 0, 90, 1),
                                            align=TextNode.ALeft,
                                            mayChange=1)
        self.playerGUI.score.reparentTo(self.playerGUI)

    #procedure updateEntities
    #task applies logic to all registered entities every frame
    def updateEntities(self, task):
        base.AIworld.update()
        timeNow = globalClock.getFrameTime()
        for item in base.entities:
            if item.is_empty():
                base.entities.remove(item)
                continue
            #call to overrideable updateState method
            item.updateState()

        #update player UI
        if self.playerGUI.HP.text != str(self.player.health):
            self.playerGUI.HP.text = str(self.player.health)
        if self.playerGUI.score.text != str(self.player.score):
            self.playerGUI.score.text = "Score: {}".format(
                str(self.player.score))

        #spawn entities
        if timeNow >= self.nextValidSpawn:
            self.spawnEnemies()

        #self.checkOutposts()

        return task.cont

    #procedure spawnBases
    def spawnBases(self):
        '''
        Create bases/outposts across the map based on environment mesh nodes
        Store in self.bases list
        '''
        #place HQs
        baseNode = base.loader.loadModel("models/hq")
        baseNode.reparentTo(base.render)
        baseNode.setPos(self.team0BaseNode.getPos())
        baseNode.setScale(4.5)
        #base.structures.append(baseNode)

        baseNode = base.loader.loadModel("models/hq")
        baseNode.reparentTo(base.render)
        baseNode.setPos(self.team1BaseNode.getPos())
        baseNode.setScale(4.5)
        baseNode.setH(self.team1BaseNode.getH())
        #base.structures.append(baseNode)

        #spawn bases on flags
        for i, flag in enumerate(self.flags):
            newBase = structure("models/outpost", flag.getPos())
            #newBase.reparentTo(base.render)
            #newBase.setScale(flag.getScale())
            #newBase.setPos(flag.getPos())
            #store base in flags array
            self.outposts.append(newBase)
            base.structures.append(newBase)
            #set initial team of each flag
            self.flags[i].setPythonTag('team', None)

    #procedure spawnEnemies
    def spawnEnemies(self):
        '''
        Every few ticks generate new enemies from hostile bases
        '''
        timeNow = globalClock.getFrameTime()
        self.nextValidSpawn = timeNow + random.randint(4, 11)
        #actually spawns entities for both sides from respective HQs
        if len(base.entities) < 12:
            #randomly choose which team to spawn more for
            team = random.randint(0, 1)
            unit = None
            if team == 0:
                unit = rifleman(base, self.team0Node.getPos(), 0)
                #for now just send units to the other base
                unit.setGoal(self.team1Node.getPos())
            else:
                unit = rifleman(base, self.team1Node.getPos(), 1)
                #for now just send units to the other base
                unit.setGoal(self.team0Node.getPos())
            # set goal to random point around flag
            flagTarget = random.randint(0, len(self.flags) - 1)
            # find reasonable inner and outer radius
            inner = self.outposts[flagTarget].getBounds().getRadius() / 2
            # outer is 1.2x the inner
            outer = 1.1 * inner
            #find random point in this circle
            x, y = randomPointInCircle(self.outposts[flagTarget].getPos(),
                                       outer, inner)
            flagTarget = (x, y, 0)
            #dest = base.loader.loadModel("models/basic")
            #dest.setPos(flagTarget)
            #dest.reparentTo(base.render)
            ##unit.setGoal(flagTarget)

    #procedure checkOutposts
    def checkOutposts(self):
        '''
        Checks all outposts for entities and starts conversion process
        '''
        #check all flags for capture state
        #there must be objects of team # exclusively in capture zone for capture_time
        for flag in self.flags:
            isCapturing = True
            lastTeam = None
            lastCapture = flag.getPythonTag('lastCapture')
            lastCaptureTeam = flag.getPythonTag('lastCaptureTeam')
            flagTeam = flag.getPythonTag('team')
            # check for base.entities and see if any are in the circle created by capture radius
            for troop in base.entities:
                if pointInCircle(flag.getPos(), mission.CAPTURE_RADIUS,
                                 troop.getPos()):
                    if (lastTeam != None) and lastTeam != troop.team:
                        #if multiple teams in zone all capture attempts aborted
                        isCapturing = False
                        flag.setPythonTag('lastCapture', None)
                        flag.setPythonTag('lastCaptureTeam', None)
                        break
                    else:
                        #otherwise add to capturezone
                        lastTeam = troop.team
            #if still isCapturing at this point all troops in the capture zone are on the same team
            #first check if capture attempt already in progress
            if isCapturing and flagTeam != lastTeam:
                timeNow = globalClock.getFrameTime()
                # check if enough time has elapsed to confer ownership
                if (lastCapture != None and lastCaptureTeam == lastTeam) \
                        and timeNow >= (lastCapture+flag.capture_time):
                    #if all these criteria true, give flag to team
                    flag.setPythonTag('team', lastTeam)
                    flag.setPythonTag('lastCapture', None)
                    flag.setPythonTag('lastCaptureTeam', None)
                # if flag.lastCapture IS none, start a capture
                elif flag.lastCaptureTeam == None:
                    flag.setPythonTag('lastCapture', lastTeam)
                    flag.setPythonTag('lastCaptureTeam', timeNow)

    #procedure captureBase
    def captureBase(self):
        '''
        Function to be called when player reaches center of base and attempts side conversion
        If successful base becomes player controlled and reinforceable
        '''
        pass

    #procedure spawnFriendly
    def spawnFriendly(self, friendly, location):
        '''
        Spawn friendly unit of type friendly (rifle, runner, tank, helo, turret)
        '''
        pass

    #procedure class deconstructor
    def delete(self):
        '''
        Cleanly deletes all objects created by this scene
        '''
        self.music.stop()
        self.playerGUI.removeNode()
        #clear all lights
        base.render.clearLight()

        #clear tasks created through course of gameloop
        taskMgr.remove("updateEntitiesTask")

        #clear shader effect
        base.filters.delCartoonInk()

        #delete all items in cleanup
        for garbage in base.cleanup:
            # check if actor type, if so, cleanup
            if isinstance(garbage, entity):
                continue
            else:
                garbage.removeNode()
                del garbage
        # clear entities list
        base.entities.clear()
        # clear cleanup list
        base.cleanup.clear()
        self.outposts.clear()
        self.flags.clear()
        # last alibis
        base.render.getChildren().detach()