Пример #1
0
class PGPhysicsWorld:
    def __init__(self):
        # a dynamic world, moving objects or objects which react to other objects should be placed here
        self.dynamic_world = BulletWorld()
        CollisionGroup.set_collision_rule(self.dynamic_world)
        self.dynamic_world.setGravity(Vec3(0, 0, -9.81))  # set gravity
        # a static world which used to query position/overlap .etc. Don't implement doPhysics() in this world
        self.static_world = BulletWorld()

    def report_bodies(self):
        dynamic_bodies = \
            self.dynamic_world.getNumRigidBodies() + self.dynamic_world.getNumGhosts() + self.dynamic_world.getNumVehicles()
        static_bodies = \
            self.static_world.getNumRigidBodies() + self.static_world.getNumGhosts() + self.static_world.getNumVehicles()
        return "dynamic bodies:{}, static_bodies: {}".format(
            dynamic_bodies, static_bodies)

    def destroy(self):
        self.dynamic_world.clearDebugNode()
        self.dynamic_world.clearContactAddedCallback()
        self.dynamic_world.clearFilterCallback()

        self.static_world.clearDebugNode()
        self.static_world.clearContactAddedCallback()
        self.static_world.clearFilterCallback()

        self.dynamic_world = None
        self.static_world = None

    def __del__(self):
        logging.debug("Physics world is destroyed successfully!")
Пример #2
0
class DistributedBattleZoneAI(DistributedObjectAI, AvatarWatcher):
    notify = directNotify.newCategory('DistributedBattleZoneAI')

    battleType = BattleGlobals.BTBattle

    MapFormatString = "phase_14/etc/{0}/{0}.bsp"

    def __init__(self, air):
        DistributedObjectAI.__init__(self, air)
        AvatarWatcher.__init__(self, air)

        # Stores the Cogs each avatar kills.
        # Key (Avatar Id)
        # Value:
        # [{GAG_ID : USES}, [DeadCogData...]]
        self.avatarData = {}

        self.avId2suitsTargeting = {}

        # List of avatars that have acknowledged that they've completed the reward panel sequence.
        self.avReadyToContinue = []

        self.bspLoader = None
        self.navMeshNp = None

        # List of info_hint_cover entites, which indicate cover locations for AIs.
        self.coverKDTree = None
        self.coverHints = []

        self.physicsWorld = None

        self.gameRules = self.makeGameRules()

        self.readyAvatars = []

        self.map = ""

        # We have to put entities loaded from the BSP level in a separate zone.
        # This is a workaround for an issue where the player would receive
        # DistributedEntity generates but there is no BSP level loaded yet.
        #
        # The player will enter the battle zone, and then upon
        # loading the BSP level, add interest into the entity zone.
        self.entZone = self.air.allocateZone()

    def d_emitSound(self, soundPath, worldPos, volume=1.0):
        if isinstance(soundPath, list) or isinstance(soundPath, tuple):
            if len(soundPath) == 0:
                return
            import random
            soundPath = random.choice(soundPath)
        self.sendUpdate(
            'emitSound',
            [soundPath, [worldPos[0], worldPos[1], worldPos[2]], volume])

    def getEntZone(self):
        return self.entZone

    def loadedMap(self):
        """
        Sent by the client when they finish loading the map.
        """
        pass

    def setMap(self, map):
        self.map = map
        if len(map) and self.bspLoader:
            self.loadBSPLevel(self.MapFormatString.format(map))

    def b_setMap(self, map):
        self.sendUpdate('setMap', [map])
        self.setMap(map)

    def getMap(self):
        return self.map

    def handleAvatarsReady(self):
        pass

    def readyToStart(self):
        avId = self.air.getAvatarIdFromSender()
        if not avId in self.readyAvatars:
            avatar = self.air.doId2do.get(avId, None)
            if avatar:
                self.gameRules.setupPlayerAttackList(avatar)
            self.readyAvatars.append(avId)
        if len(self.readyAvatars) == len(self.watchingAvatarIds):
            self.handleAvatarsReady()

    def makeGameRules(self):
        return GameRulesAI(self)

    def getGameRules(self):
        return self.gameRules

    def getCoverHints(self):
        return self.coverHints

    def traceLine(self, start, end):
        if not self.bspLoader.hasActiveLevel():
            return True

        return self.bspLoader.traceLine(start, end)

    def deadSuit(self, doId):
        pass

    def suitHPAtZero(self, doId):
        pass

    def generate(self):
        DistributedObjectAI.generate(self)
        self.air.battleZones[self.zoneId] = self

        self.bspLoader = Py_AI_BSPLoader()
        self.bspLoader.setAi(True)
        self.bspLoader.setMaterialsFile("phase_14/etc/materials.txt")
        #self.bspLoader.setTextureContentsFile("phase_14/etc/texturecontents.txt")
        self.bspLoader.setServerEntityDispatcher(self)
        AvatarWatcher.zoneId = self.zoneId

        # Link up networked entities
        from src.coginvasion.szboss import (DistributedTriggerAI,
                                            DistributedFuncDoorAI,
                                            DistributedButtonAI,
                                            DistributedFuncRotatingAI,
                                            LogicCounter, HintsAI, InfoTimer,
                                            InfoBgmAI)
        from src.coginvasion.szboss.InfoPlayerStart import InfoPlayerStart
        self.bspLoader.linkServerEntityToClass(
            "trigger_once", DistributedTriggerAI.DistributedTriggerOnceAI)
        self.bspLoader.linkServerEntityToClass(
            "trigger_multiple",
            DistributedTriggerAI.DistributedTriggerMultipleAI)
        self.bspLoader.linkServerEntityToClass(
            "func_door", DistributedFuncDoorAI.DistributedFuncDoorAI)
        self.bspLoader.linkServerEntityToClass(
            "func_button", DistributedButtonAI.DistributedButtonAI)
        self.bspLoader.linkServerEntityToClass(
            "func_rotating",
            DistributedFuncRotatingAI.DistributedFuncRotatingAI)
        self.bspLoader.linkServerEntityToClass("logic_counter",
                                               LogicCounter.LogicCounter)
        self.bspLoader.linkServerEntityToClass("info_hint_cover",
                                               HintsAI.InfoHintCover)
        self.bspLoader.linkServerEntityToClass("info_timer",
                                               InfoTimer.InfoTimer)
        self.bspLoader.linkServerEntityToClass("info_player_start",
                                               InfoPlayerStart)
        self.bspLoader.linkServerEntityToClass("info_bgm", InfoBgmAI.InfoBgmAI)

        self.physicsWorld = BulletWorld()
        self.physicsWorld.setGravity(Vec3(0, 0, -32.1740))
        self.bspLoader.setPhysicsWorld(self.physicsWorld)

    def announceGenerate(self):
        DistributedObjectAI.announceGenerate(self)

        taskMgr.add(self.__updateTask, self.uniqueName('battleZoneUpdate'))

    def __updateTask(self, task):
        dt = globalClock.getDt()
        try:
            #self.physicsWorld.doPhysics(dt, metadata.PHYS_SUBSTEPS, dt / (metadata.PHYS_SUBSTEPS + 1))
            self.physicsWorld.doPhysics(dt, 0)
        except:
            pass
        self.update()
        return task.cont

    def update(self):
        pass

    def getPhysicsWorld(self):
        return self.physicsWorld

    def loadBSPLevel(self, lfile):
        self.bspLoader.read(lfile)
        self.setupNavMesh(self.bspLoader.getResult())

        coverPositions = []
        self.coverHints = []
        for hint in self.bspLoader.findAllEntities("info_hint_cover"):
            pos = hint.getCEntity().getOrigin()
            coverPositions.append([pos[0], pos[1], pos[2]])
            self.coverHints.append(hint)
        if len(coverPositions) > 0:
            self.coverKDTree = cKDTree(coverPositions)

    def findClosestCoverPoint(self, currPos, n=1):
        if not self.coverKDTree:
            return []

        return list(
            self.coverKDTree.query([currPos[0], currPos[1], currPos[2]], n)[1])

    def unloadBSPLevel(self):
        self.cleanupNavMesh()
        self.coverKDTree = None
        self.coverHints = []
        if self.bspLoader:
            detachAndRemoveBulletNodes(self.bspLoader.getResult(),
                                       world=self.physicsWorld)
            self.bspLoader.cleanup()

    def cleanupNavMesh(self):
        if self.navMeshNp:
            self.navMeshNp.removeNode()
            self.navMeshNp = None

    def setupNavMesh(self, node):
        self.cleanupNavMesh()

        nmMgr = base.nmMgr
        self.navMeshNp = nmMgr.create_nav_mesh()
        self.navMeshNp.node().set_owner_node_path(node)
        self.navMeshNp.node().setup()

    def planPath(self, startPos, endPos):
        """Uses recast/detour to find a path from the generated nav mesh from the BSP file."""

        if not self.navMeshNp:
            return [startPos, endPos]
        result = []
        valueList = self.navMeshNp.node().path_find_follow(startPos, endPos)
        currDir = Vec3(0)
        for i in xrange(valueList.get_num_values()):
            if i > 0 and i < valueList.get_num_values() - 1:
                dir = (valueList.get_value(i - 1) -
                       valueList.get_value(i)).normalized()
                if dir.almostEqual(currDir, 0.05):
                    continue
                currDir = dir
            result.append(valueList.get_value(i))
        return result

    def createServerEntity(self, cls, entnum):
        """
        Called by BSPLoader when it encounters a networked entity that we have to generate.
        """
        dobj = cls(self.air, self)
        dobj.entnum = entnum
        dobj.bspLoader = self.bspLoader
        dobj.zoneId = self.entZone
        return dobj

    def getBattleType(self):
        return self.battleType

    def toonAvailableForTargeting(self, avId):
        #return len(self.avId2suitsTargeting.get(avId, [])) < 2
        return True

    def clearTargets(self, suitId):
        # Remove the current target of this suit.
        for avId in self.avId2suitsTargeting.keys():
            if suitId in self.avId2suitsTargeting[avId]:
                self.avId2suitsTargeting[avId].remove(suitId)

    def newTarget(self, suitId, targetId):
        self.clearTargets(suitId)

        self.avId2suitsTargeting[targetId].append(suitId)

    def getSuitsTargetingAvId(self, avId):
        return self.avId2suitsTargeting.get(avId, [])

    def getNumSuitsTargeting(self, avId):
        return len(self.getSuitsTargetingAvId(avId))

    def acknowledgeAvatarReady(self):
        avId = self.air.getAvatarIdFromSender()

        if avId in self.watchingAvatarIds and avId not in self.avReadyToContinue:
            self.avReadyToContinue.append(avId)

        if len(self.avReadyToContinue) == len(self.watchingAvatarIds):
            self.b_rewardSequenceComplete()

    def b_rewardSequenceComplete(self):
        timestamp = globalClockDelta.getFrameNetworkTime()
        self.rewardSequenceComplete(timestamp)
        self.d_rewardSequenceComplete(timestamp)

    def d_rewardSequenceComplete(self, timestamp):
        self.sendUpdate('rewardSequenceComplete', [timestamp])

    def rewardSequenceComplete(self, timestamp):
        pass

    def resetPhysics(self):
        if self.physicsWorld:
            # We are counting on other types of objects never being added on the server side.
            numRigids = self.physicsWorld.getNumRigidBodies()
            numGhosts = self.physicsWorld.getNumGhosts()
            numConstraints = self.physicsWorld.getNumConstraints()
            for i in xrange(numRigids - 1, -1, -1):
                self.physicsWorld.removeRigidBody(
                    self.physicsWorld.getRigidBody(i))
            for i in xrange(numGhosts - 1, -1, -1):
                self.physicsWorld.removeGhost(self.physicsWorld.getGhost(i))
            for i in xrange(numConstraints - 1, -1, -1):
                self.physicsWorld.removeConstraint(
                    self.physicsWorld.getConstraint(i))

    def delete(self):
        taskMgr.remove(self.uniqueName('battleZoneUpdate'))
        self.ignoreEvents()

        self.coverHints = None

        del self.air.battleZones[self.zoneId]

        self.resetStats()

        self.unloadBSPLevel()
        self.bspLoader = None

        self.resetPhysics()
        self.physicsWorld = None

        self.readyAvatars = None

        self.avId2suitsTargeting = None
        self.watchingAvatarIds = None
        self.avatarData = None
        self.gameRules.cleanup()
        self.gameRules = None
        self.map = None
        self.entZone = None
        DistributedObjectAI.delete(self)

    def resetStats(self):
        self.stopTrackingAll()
        self.avatarData = {}
        self.avId2suitsTargeting = {}

    def addAvatar(self, avId, andUpdateAvatars=0):
        self.setupAvatarData(avId)
        self.startTrackingAvatarId(avId)

        if andUpdateAvatars:
            self.b_setAvatars(self.watchingAvatarIds)

    def setupAvatarData(self, avId):
        self.avatarData.update({avId: [{}, []]})
        self.avId2suitsTargeting[avId] = []

    def handleAvatarLeave(self, avatar, reason):
        self.removeAvatar(avatar.doId)
        if reason in [DIED, ZONE_CHANGE]:
            self.gameRules.restorePlayerBackpack(avatar)

    def handleAvatarChangeHealth(self, avatar, newHealth, prevHealth):
        pass

    def removeAvatar(self, avId, andUpdateAvatars=1):
        if self.watchingAvatarIds != None:
            if avId in self.watchingAvatarIds:
                self.stopTrackingAvatarId(avId)
                self.sendUpdate('clearAvatarDebris', [avId])

            if avId in self.avReadyToContinue:
                self.avReadyToContinue.remove(avId)

            if avId in self.avId2suitsTargeting.keys():
                del self.avId2suitsTargeting[avId]

            if avId in self.avatarData.keys():
                self.avatarData.pop(avId)

            if andUpdateAvatars:
                self.b_setAvatars(self.watchingAvatarIds)

    # Send the distributed message and
    # set the avatars on here.
    def b_setAvatars(self, avIds):
        self.d_setAvatars(avIds)
        self.setAvatars(avIds)

    # Send the distributed message.
    def d_setAvatars(self, avIds):
        self.sendUpdate('setAvatars', [avIds])

    # Set the avatar ids array to a list of
    # avatar ids.
    def setAvatars(self, avIds):
        # Let's make sure we're only listening to the avatars
        # we intend to listen to.
        for avId in itertools.chain(self.watchingAvatarIds, avIds):
            if avId in avIds:
                self.addAvatar(avId)
            else:
                self.removeAvatar(avId, andUpdateAvatars=0)
        self.watchingAvatarIds = avIds

    # Get the avatar ids.
    def getAvatars(self):
        return self.watchingAvatarIds

    def handleGagUse(self, gagId, user):
        if not self.gameRules.givesExperience():
            return

        gagUses = {}
        currentKills = []
        uses = 0

        if user in self.avatarData.keys():
            data = self.avatarData.get(user)
            gagUses = data[0]
            currentKills = data[1]

        if gagId in gagUses.keys():
            uses = gagUses[gagId]

        gagUses.update({gagId: uses + 1})

        self.avatarData.update({user: [gagUses, currentKills]})

    def handleCogDeath(self, cog, killerId):
        if not self.gameRules.countsTowardsQuests():
            return

        plan = cog.suitPlan
        cogData = DeadCogData(plan.name, plan.dept, cog.level, cog.variant,
                              cog.hood)
        gagUses = {}
        currentKills = []

        if killerId in self.avatarData.keys():
            data = self.avatarData.get(killerId)
            gagUses = data[0]
            currentKills = data[1]
        currentKills.append(cogData)

        # Add the Cog kill into the player's dictionary.
        self.avatarData.update({killerId: [gagUses, currentKills]})

    def setToonData(self, netStrings):
        pass

    def d_setToonData(self):
        data = self.parseToonData()
        self.sendUpdate('setToonData', [data])

    def getToonData(self):
        return []

    def parseToonData(self):
        blobs = []
        for avId, data in self.avatarData.iteritems():
            avatar = base.air.doId2do.get(avId, None)
            deadCogData = data[1]
            favGagId = 0  # Make it whole cream pie by default
            favGagUses = 0
            gagUnlocked = False

            if avatar:
                rpData = RPToonData(avatar)
                trackIncrements = {}

                for track in avatar.trackExperience.keys():
                    trackIncrements[track] = 0

                for gagId, uses in data[0].iteritems():
                    gagName = self.air.attackMgr.getAttackName(gagId)
                    gagData = GagGlobals.gagData.get(gagName)
                    track = gagData['track']
                    if uses > favGagUses:
                        favGagId = gagId

                    trackGags = GagGlobals.TrackGagNamesByTrackName.get(track)

                    incr = (trackGags.index(gagName) + 1) * uses
                    if track in trackIncrements.keys():
                        incr = incr + trackIncrements[track]
                    trackIncrements[track] = incr

                rpData.favoriteGag = self.air.attackMgr.getAttackName(favGagId)

                for track, exp in avatar.trackExperience.iteritems():
                    rpDataTrack = rpData.getTrackByName(track)
                    increment = trackIncrements.get(track)
                    maxExp = GagGlobals.getMaxExperienceValue(exp, track)
                    incrMaxExp = GagGlobals.getMaxExperienceValue(
                        exp + increment, track)
                    rpDataTrack.exp = exp
                    rpDataTrack.maxExp = maxExp
                    rpDataTrack.increment = increment
                    avatar.trackExperience[track] = (exp +
                                                     rpDataTrack.increment)

                    if incrMaxExp != maxExp:
                        # We've unlocked a gag.
                        maxExpIndex = GagGlobals.TrackExperienceAmounts.get(
                            track).index(incrMaxExp)
                        newGagName = GagGlobals.TrackGagNamesByTrackName.get(
                            track)[maxExpIndex]
                        gagId = self.air.attackMgr.getAttackIDByName(
                            newGagName)
                        avatar.backpack.addGag(gagId, 1)
                        gagUnlocked = True
                avatar.b_setTrackExperience(
                    GagGlobals.trackExperienceToNetString(
                        avatar.trackExperience))

                if gagUnlocked:
                    netString = avatar.getBackpackAmmo()
                    avatar.d_setBackpackAmmo(netString)

                # Let's update quest stuff for this avatar.
                questManager = avatar.questManager

                objectiveProgresses = []

                for quest in questManager.quests.values():
                    objectiveProgress = []
                    for i, objective in enumerate(quest.accessibleObjectives):
                        objectiveProgress.append(objective.progress)
                        if objective.type == DefeatCog:
                            progress = objective.progress
                            for killData in deadCogData:
                                if not (
                                        progress == objective.goal
                                ) and objective.isNeededCogFromDeadCogData(
                                        killData):
                                    progress += 1
                                elif (progress == objective.goal):
                                    break
                            objectiveProgress[i] = progress
                        elif objective.type == DefeatCogBuilding and self.isCogOffice(
                        ) and objective.isAcceptable(self.hood, self.dept,
                                                     self.numFloors):
                            objectiveProgress[i] = objectiveProgress[i] + 1
                        elif objective.type == RecoverItem:
                            for killData in deadCogData:
                                objective.handleProgressFromDeadCogData(
                                    killData)

                    objectiveProgresses.append(objectiveProgress)
                questManager.updateQuestData(
                    objectiveProgresses=objectiveProgresses)

                blobs.append(rpData.toNetString(avId))

        return blobs

    def isCogOffice(self):
        return self.battleType == BattleGlobals.BTOffice

    def battleComplete(self):
        self.d_setToonData()
        self.startRewardSeq()

    def startRewardSeq(self):
        timestamp = globalClockDelta.getFrameNetworkTime()
        self.sendUpdate('startRewardSeq', [timestamp])