Trying to Add Intangibility to My Player-Character

I’m trying to modify some code of a tutorial game to learn more about coding in Python, and one of the things that I am trying to do is give the Player-Character intangibility for 2 seconds after getting hit by any type of enemy; however, I am running into problems in trying to solve it.

My attempt at first is just to make the Player-Character Intangible period and figure out the time later. To do so, I created the method intangible() to run when the Player took damage. What I tried to do was change the BitMask of the Collision so that the Player would neither collide with WalkingEnemy or TrapEnemy as well as make WalkingEnemy deal 0 damage. Ultiamtely, it is not working and I do not know why.

Here is the main game file:

from direct.showbase.ShowBase import ShowBase
from panda3d.core import WindowProperties
from direct.actor.Actor import Actor
from panda3d.core import AmbientLight
from panda3d.core import DirectionalLight
from panda3d.core import Vec4, Vec3
from panda3d.core import CollisionTraverser
from panda3d.core import CollisionHandlerPusher
from panda3d.core import CollisionSphere, CollisionNode
from panda3d.core import CollisionTube
from direct.gui.DirectGui import *
from gameobject import *

import random

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        self.disableMouse()

        properties = WindowProperties()
        properties.setSize(1000, 750)
        self.win.requestProperties(properties)

        self.exitFunc = self.cleanup

        self.environment = loader.loadModel("PandaSampleModels-master/PandaSampleModels-master/Environment/environment")
        self.environment.reparentTo(render)

        self.camera.setPos(0, 0, 32)
        self.camera.setP(-90)

        ambientLight = AmbientLight("ambient light")
        ambientLight.setColor(Vec4(0.2, 0.2, 0.2, 1))
        self.ambientLightNodePath = render.attachNewNode(ambientLight)
        render.setLight(self.ambientLightNodePath)

        mainLight = DirectionalLight("main light")
        self.mainLightNodePath = render.attachNewNode(mainLight)
        self.mainLightNodePath.setHpr(45, -45, 0)
        render.setLight(self.mainLightNodePath)

        render.setShaderAuto()

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

        self.pusher.setHorizontal(True)

        self.pusher.add_in_pattern("%fn-into-%in")
        self.accept("trapEnemy-into-wall", self.stopTrap)
        self.accept("trapEnemy-into-trapEnemy", self.stopTrap)
        self.accept("trapEnemy-into-player", self.trapHitsSomething)
        self.accept("trapEnemy-into-walkingEnemy", self.trapHitsSomething)
        
        wallSolid = CollisionTube(-8.0, 0, 0, 8.0, 0, 0, 0.2)
        wallNode = CollisionNode("wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.setY(8.0)

        wallSolid = CollisionTube(-8.0, 0, 0, 8.0, 0, 0, 0.2)
        wallNode = CollisionNode("wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.setY(-8.0)

        wallSolid = CollisionTube(0, -8.0, 0, 0, 8.0, 0, 0.2)
        wallNode = CollisionNode("wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.setX(8.0)

        wallSolid = CollisionTube(0, -8.0, 0, 0, 8.0, 0, 0.2)
        wallNode = CollisionNode("wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.setX(-8.0)

        self.keyMap = {
            "up" : False,
            "down" : False,
            "left" : False,
            "right" : False,
            "shoot" : False
        }

        self.accept("w", self.updateKeyMap, ["up", True])
        self.accept("w-up", self.updateKeyMap, ["up", False])
        self.accept("s", self.updateKeyMap, ["down", True])
        self.accept("s-up", self.updateKeyMap, ["down", False])
        self.accept("a", self.updateKeyMap, ["left", True])
        self.accept("a-up", self.updateKeyMap, ["left", False])
        self.accept("d", self.updateKeyMap, ["right", True])
        self.accept("d-up", self.updateKeyMap, ["right", False])
        self.accept("mouse1", self.updateKeyMap, ["shoot", True])
        self.accept("mouse1-up", self.updateKeyMap, ["shoot", False])

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

        self.player = None

        self.enemies = []
        self.trapEnemies = []
        self.deadEnemies = []

        self.spawnPoints = []
        numPointsPerWall = 5
        for i in range(numPointsPerWall):
            coord = 7.0/numPointsPerWall + 0.5
            self.spawnPoints.append(Vec3(-7.0, coord, 0))
            self.spawnPoints.append(Vec3(7.0, coord, 0))
            self.spawnPoints.append(Vec3(coord, -7.0, 0))
            self.spawnPoints.append(Vec3(coord, 7.0, 0))

        self.initialSpawnInterval = 1.0
        self.minimumSpawnInterval = 0.2
        self.spawnInterval = self.initialSpawnInterval
        self.spawnTimer = self.spawnInterval
        self.maxEnemies = 2
        self.maximumMaxEnemies = 20

        self.numTrapsPerSide = 2

        self.difficultyInterval = 5.0
        self.difficultyTimer = self.difficultyInterval

        music = loader.loadMusic("Sounds_&_Music/Defending-the-Princess-Haunted_v002.ogg")
        music.setLoop(True)
        music.setVolume(0.075)
        music.play()

        self.enemySpawnSound = loader.loadSfx("Sounds_&_Music/Sounds_enemySpawn.ogg")

        self.gameOverScreen = DirectDialog(frameSize= (-0.7, 0.7, -0.7, 0.7),
                                           fadeScreen = 0.4,
                                           relief = DGG.FLAT,
                                           frameTexture = "PandaSampleModels-master/PandaSampleModels-master/UI/stoneFrame.png")
        self.gameOverScreen.hide()

        self.font = loader.loadFont("Fonts/Wbxkomik.ttf")

        label = DirectLabel(text = "Game Over!",
                            parent = self.gameOverScreen,
                            scale = 0.1,
                            pos = (0, 0, 0.2),
                            text_font = self.font,
                            relief = None)

        self.finalScoreLabel = DirectLabel(text = "",
                                           parent = self.gameOverScreen,
                                           scale = 0.07,
                                           pos = (0, 0, 0),
                                           text_font = self.font,
                                           relief = None)

        buttonImages = (
            loader.loadTexture("PandaSampleModels-master/PandaSampleModels-master/UI/UIButton.png"),
            loader.loadTexture("PandaSampleModels-master/PandaSampleModels-master/UI/UIButtonPressed.png"),
            loader.loadTexture("PandaSampleModels-master/PandaSampleModels-master/UI/UIButtonHighlighted.png"),
            loader.loadTexture("PandaSampleModels-master/PandaSampleModels-master/UI/UIButtonDisabled.png")
        )

        btn = DirectButton(text = "Restart",
                           command = self.startGame,
                           pos = (-0.3, 0, -0.2),
                           parent = self.gameOverScreen,
                           scale = 0.07,
                           text_font = self.font,
                           clickSound = loader.loadSfx("Sounds_&_Music/Sounds_UIClick.ogg"),
                           frameTexture = buttonImages,
                           frameSize = (-4, 4, -1, 1),
                           text_scale = 0.75,
                           relief = DGG.FLAT,
                           text_pos = (0, -0.2))
        
        btn.setTransparency(True)
    

        btn = DirectButton(text = "Quit",
                           command = self.quit,
                           pos = (0.3, 0, -0.2),
                           parent = self.gameOverScreen,
                           scale = 0.07,
                           text_font = self.font,
                           clickSound = loader.loadSfx("Sounds_&_Music/Sounds_UIClick.ogg"),
                           frameTexture = buttonImages,
                           frameSize = (-4, 4, -1, 1),
                           text_scale = 0.75,
                           relief = DGG.FLAT,
                           text_pos = (0, -0.2))
        
        btn.setTransparency(True)

        self.titleMenuBackdrop = DirectFrame(frameColor = (0, 0, 0, 1),
                                             frameSize = (-1, 1, -1, 1),
                                             parent = render2d)
        self.titleMenu = DirectFrame(frameColor = (1, 1, 1, 0))

        title = DirectLabel(text = "Panda-chan",
                            scale = 0.1,
                            pos = (0, 0, 0.9),
                            parent = self.titleMenu,
                            relief = None,
                            text_font = self.font,
                            text_fg = (1, 1, 1, 1))
        title2 = DirectLabel(text = "and the",
                             scale = 0.07,
                             pos = (0, 0, 0.79),
                             parent = self.titleMenu,
                             text_font = self.font,
                             frameColor = (0.5, 0.5, 0.5, 1))
        title3 = DirectLabel(text = "Endless Horde",
                              scale = 0.125,
                              pos = (0, 0, 0.65),
                              parent = self.titleMenu,
                              relief = None,
                              text_font = self.font,
                              text_fg = (1, 1, 1, 1))
        btn = DirectButton(text = "Start Game",
                           command = self.startGame,
                           pos = (0, 0, 0.2),
                           parent = self.titleMenu,
                           scale = 0.1,
                           text_font = self.font,
                           clickSound = loader.loadSfx("Sounds_&_Music/Sounds_UIClick.ogg"),
                           frameTexture = buttonImages,
                           frameSize = (-4, 4, -1, 1),
                           text_scale = 0.75,
                           relief = DGG.FLAT,
                           text_pos = (0, -0.2))
        btn.setTransparency(True)

        btn = DirectButton(text = "Quit",
                           command = self.quit,
                           pos = (0, 0, -0.2),
                           parent = self.titleMenu,
                           scale = 0.1,
                           text_font = self.font,
                           clickSound = loader.loadSfx("Sounds_&_Music/Sounds_UIClick.ogg"),
                           frameTexture = buttonImages,
                           frameSize = (-4, 4, -1, 1),
                           text_scale = 0.75,
                           relief = DGG.FLAT,
                           text_pos = (0, -0.2))
        btn.setTransparency(True)
        
    def startGame(self):
        self.titleMenu.hide()
        self.titleMenuBackdrop.hide()
        self.gameOverScreen.hide()
        

        self.cleanup()

        self.player = Player()

        self.maxEnemies = 2
        self.spawnInterval = self.initialSpawnInterval

        self.difficultyTimer = self.difficultyInterval

        sideTrapSlots = [
            [],
            [],
            [],
            []
        ]
        trapSlotDistance = 0.4
        slotPos = -8 + trapSlotDistance
        while slotPos < 8:
            if abs(slotPos) > 1.0:
                sideTrapSlots[0].append(slotPos)
                sideTrapSlots[1].append(slotPos)
                sideTrapSlots[2].append(slotPos)
                sideTrapSlots[3].append(slotPos)
            slotPos += trapSlotDistance

        for i in range(self.numTrapsPerSide):
            slot = sideTrapSlots[0].pop(random.randint(0, len(sideTrapSlots[0])-1))
            trap = TrapEnemy(Vec3(slot, -7.0, 0))
            self.trapEnemies.append(trap)

            slot = sideTrapSlots[1].pop(random.randint(0, len(sideTrapSlots[1])-1))
            trap = TrapEnemy(Vec3(slot, -7.0, 0))
            self.trapEnemies.append(trap)

            slot = sideTrapSlots[2].pop(random.randint(0, len(sideTrapSlots[2])-1))
            trap = TrapEnemy(Vec3(7.0, slot, 0))
            trap.moveInX = True
            self.trapEnemies.append(trap)

            slot = sideTrapSlots[3].pop(random.randint(0, len(sideTrapSlots[3])-1))
            trap = TrapEnemy(Vec3(-7.0, slot, 0))
            trap.moveInX = True
            self.trapEnemies.append(trap)


    def updateKeyMap(self, controlName, controlState):
        self.keyMap[controlName] = controlState

    def stopTrap(self, entry):
        collider = entry.getFromNodePath()
        if collider.hasPythonTag("owner"):
            trap = collider.getPythonTag("owner")
            trap.moveDirection = 0
            trap.ignorePlayer = False
            trap.movementSound.stop()
            trap.stopSound.play()
            
    def trapHitsSomething(self, entry):
        collider = entry.getFromNodePath()
        if collider.hasPythonTag("owner"):
            trap = collider.getPythonTag("owner")
            if trap.moveDirection == 0:
                return
            collider = entry.getIntoNodePath()
            if collider.hasPythonTag("owner"):
                obj = collider.getPythonTag("owner")
                if isinstance(obj, Player):
                    if not trap.ignorePlayer:
                        obj.alterHealth(-1)
                        trap.ignorePlayer = True
                else:
                    obj.alterHealth(-10)
                trap.impactSound.play()
                    

    def spawnEnemy(self):
        if len(self.enemies) < self.maxEnemies:
            spawnPoint = random.choice(self.spawnPoints)

            newEnemy = WalkingEnemy(spawnPoint)

            self.enemies.append(newEnemy)

            self.enemySpawnSound.play()

    def update(self, task):

        dt = globalClock.getDt()

        if self.player is not None:
                if self.player.health > 0:
                    self.player.update(self.keyMap, dt)

                    self.spawnTimer -= dt
                    if self.spawnTimer <= 0:
                        self.spawnTimer = self.spawnInterval
                        self.spawnEnemy()

                    [enemy.update(self.player, dt) for enemy in self.enemies]
                    [trap.update(self.player, dt) for trap in self.trapEnemies]

                    newlyDeadEnemies = [enemy for enemy in self.enemies if enemy.health <= 0]
                    self.enemies = [enemy for enemy in self.enemies if enemy.health > 0]

                    for enemy in newlyDeadEnemies:
                        enemy.collider.removeNode()
                        enemy.actor.play("die")
                        self.player.score += enemy.scoreValue
                    if len(newlyDeadEnemies) > 0:
                        self.player.updateScore()

                    self.deadEnemies += newlyDeadEnemies

                    enemiesAnimatingDeaths = []
                    for enemy in self.deadEnemies:
                        deathAnimControl = enemy.actor.getAnimControl("die")
                        if deathAnimControl is None or not deathAnimControl.isPlaying():
                            enemy.cleanup()
                        else:
                            enemiesAnimatingDeaths.append(enemy)
                    self.deadEnemies = enemiesAnimatingDeaths

                    self.difficultyTimer -= dt
                    if self.difficultyTimer <= 0:
                        self.difficultyTimer = self.difficultyInterval
                        if self.maxEnemies < self.maximumMaxEnemies:
                            self.maxEnemies += 1
                        if self.spawnInterval > self.minimumSpawnInterval:
                            self.spawnInterval -= 0.1

                else:
                    if self.gameOverScreen.isHidden():
                        self.gameOverScreen.show()
                        self.finalScoreLabel["text"] = "Final score: " + str(self.player.score)
                        self.finalScoreLabel.setText()
                        
        return task.cont
    
    def cleanup(self):
        for enemy in self.enemies:
            enemy.cleanup()
        self.enemies = []

        for enemy in self.deadEnemies:
            enemy.cleanup()
        self.deadEnemies = []

        for trap in self.trapEnemies:
            trap.cleanup()
        self.trapEnemies = []

        if self.player is not None:
            self.player.cleanup()
            self.player = None

    def quit(self):
        self.cleanup()

        base.userExit()

game = Game()
game.run()

And here is the file for the different characters in the game (also where I put in the Intangibility Code):

from panda3d.core import Vec3, Vec2, Plane, Point3, BitMask32, Vec4
from direct.actor.Actor import Actor
from panda3d.core import CollisionSphere, CollisionNode, CollisionRay, CollisionHandlerQueue, CollisionSegment
from direct.gui.OnscreenText import OnscreenText
from direct.gui.OnscreenImage import OnscreenImage
from panda3d.core import TextNode
from panda3d.core import PointLight
from panda3d.core import AudioSound

import math
import random

FRICTION = 150.0

class GameObject():
    def __init__(self, pos, modelName, modelAnims, maxHealth, maxSpeed, colliderName):
        self.actor = Actor(modelName, modelAnims)
        self.actor.reparentTo(render)
        self.actor.setPos(pos)

        self.maxHealth = maxHealth
        self.health = maxHealth

        self.maxSpeed = maxSpeed

        self.velocity = Vec3(0, 0, 0)
        self.acceleration = 300.0

        self.walking = False

        colliderNode = CollisionNode(colliderName)
        colliderNode.addSolid(CollisionSphere(0, 0, 0, 0.3))
        self.collider = self.actor.attachNewNode(colliderNode)
        self.collider.setPythonTag("owner", self)

        self.deathSound = None

    def update(self, dt):
        speed = self.velocity.length()
        if speed > self.maxSpeed:
            self.velocity.normalize()
            self.velocity *= self.maxSpeed
            speed = self.maxSpeed

        if not self.walking:
            frictionVal = FRICTION*dt
            if frictionVal > speed:
                self.velocity.set(0, 0, 0)
            else:
                frictionVec = -self.velocity
                frictionVec.normalize()
                frictionVec *= frictionVal

                self.velocity += frictionVec

        self.actor.setPos(self.actor.getPos() + self.velocity*dt)

    def alterHealth(self, dHealth):
        previousHealth = self.health
        
        self.health += dHealth

        if self.health > self.maxHealth:
            self.health = self.maxHealth

        if previousHealth > 0 and self.health <= 0 and self.deathSound is not None:
            self.deathSound.play()

    def cleanup(self):
        if self.collider is not None and not self.collider.isEmpty():
            self.collider.clearPythonTag("owner")
            base.cTrav.removeCollider(self.collider)
            base.pusher.removeCollider(self.collider)

        if self.actor is not None:
            self.actor.cleanup()
            self.actor.removeNode()
            self.actor = None

        self.collider = None




class Player(GameObject):
    def __init__(self):
        GameObject.__init__(self,
                            Vec3(0, 0, 0),
                            "p3d_samples-master/models/act_p3d_chan",
                                {
                                  "stand" : "p3d_samples-master/models/a_p3d_chan_idle",
                                  "walk" : "p3d_samples-master/models/a_p3d_chan_run"
                                },
                            5,
                            10,
                            "player")
        self.actor.getChild(0).setH(180)

        mask = BitMask32()
        mask.setBit(1)

        self.collider.node().setIntoCollideMask(mask)

        mask = BitMask32()
        mask.setBit(1)

        self.collider.node().setFromCollideMask(mask)
        
        base.pusher.addCollider(self.collider, self.actor)
        base.cTrav.addCollider(self.collider, base.pusher)

        self.lastMousePos = Vec2(0, 0)

        self.groundPlane = Plane(Vec3(0, 0, 1), Vec3(0, 0, 0))

        self.ray = CollisionRay(0, 0, 0, 0, 1, 0)

        rayNode = CollisionNode("playerRay")
        rayNode.addSolid(self.ray)

        mask = BitMask32()

        mask.setBit(2)
        rayNode.setFromCollideMask(mask)

        mask = BitMask32()
        rayNode.setIntoCollideMask(mask)

        self.rayNodePath = render.attachNewNode(rayNode)
        self.rayQueue = CollisionHandlerQueue()

        base.cTrav.addCollider(self.rayNodePath, self.rayQueue)

        self.damagePerSecond = -5.0

        self.beamModel = loader.loadModel("PandaSampleModels-master/PandaSampleModels-master/BambooLaser/bambooLaser")
        self.beamModel.reparentTo(self.actor)
        self.beamModel.setZ(1.5)
        self.beamModel.setLightOff()
        self.beamModel.hide()

        self.beamHitModel = loader.loadModel("PandaSampleModels-master/PandaSampleModels-master/BambooLaser/bambooLaserHit")
        self.beamHitModel.reparentTo(render)
        self.beamHitModel.setZ(1.5)
        self.beamHitModel.setLightOff()
        self.beamHitModel.hide()

        self.beamHitPulseRate = 0.15
        self.beamHitTimer = 0

        self.beamHitLight = PointLight("beamHitLight")
        self.beamHitLight.setColor(Vec4(0.1, 1.0, 0.2, 1))
        self.beamHitLight.setAttenuation((1.0, 0.1, 0.5))
        self.beamHitLightNodePath = render.attachNewNode(self.beamHitLight)

        self.lastMousePos = Vec2(0, 0)

        self.groundPlane = Plane(Vec3(0, 0, 1), Vec3(0, 0, 0))

        self.yVector = Vec2(0, 1)

        self.actor.loop("stand")

        self.score = 0

        self.healthIcons = []
        for i in range(self.maxHealth):
            icon = OnscreenImage(image = "PandaSampleModels-master/PandaSampleModels-master/UI/health.png",
                                 pos = (-1.275 + i*0.075, 0, 0.95),
                                 scale = 0.04)
            icon.setTransparency(True)
            self.healthIcons.append(icon)

        self.damageTakenModel = loader.loadModel("PandaSampleModels-master/PandaSampleModels-master/BambooLaser/playerHit")
        self.damageTakenModel.setLightOff()
        self.damageTakenModel.setZ(1.0)
        self.damageTakenModel.reparentTo(self.actor)
        self.damageTakenModel.hide()

        self.damageTakenModelTimer = 0
        self.damageTakenModelDuration = 0.15

        self.laserSoundNoHit = loader.loadSfx("Sounds_&_Music/Sounds_laserNoHit.ogg")
        self.laserSoundNoHit.setLoop(True)
        self.laserSoundHit = loader.loadSfx("Sounds_&_Music/Sounds_laserHit.ogg")
        self.laserSoundHit.setLoop(True)

        self.hurtSound = loader.loadSfx("Sounds_&_Music/Sounds_FemaleDmgNoise.ogg")

        self.scoreUI = OnscreenText(text = "0",
                                    pos = (-1.3, 0.825),
                                    mayChange = True,
                                    align = TextNode.ALeft,
                                    font = base.font)
    def update(self, keys, dt):
        GameObject.update(self, dt)

        self.walking = False

        if keys["up"]:
            self.walking = True
            self.velocity.addY(self.acceleration*dt)
        if keys["down"]:
            self.walking = True
            self.velocity.addY(-self.acceleration*dt)
        if keys["left"]:
            self.walking = True
            self.velocity.addX(-self.acceleration*dt)
        if keys["right"]:
            self.walking = True
            self.velocity.addX(self.acceleration*dt)

        if self.walking:
            standControl = self.actor.getAnimControl("stand")
            if standControl.isPlaying():
                standControl.stop()

            walkControl = self.actor.getAnimControl("walk")
            if not walkControl.isPlaying():
                self.actor.loop("walk")
        else:
            standControl = self.actor.getAnimControl("stand")
            if not standControl.isPlaying():
                self.actor.stop("walk")
                self.actor.loop("stand")

        if keys["shoot"]:
            if self.rayQueue.getNumEntries() > 0:
                scoredHit = False
                
                self.rayQueue.sortEntries()
                rayHit = self.rayQueue.getEntry(0)
                hitPos = rayHit.getSurfacePoint(render)

                hitNodePath = rayHit.getIntoNodePath()
                print (hitNodePath)
                if hitNodePath.hasPythonTag("owner"):
                    hitObject = hitNodePath.getPythonTag("owner")
                    if not isinstance(hitObject, TrapEnemy):
                        hitObject.alterHealth(self.damagePerSecond*dt)
                        scoredHit = True

                beamLength = (hitPos - self.actor.getPos()).length()
                self.beamModel.setSy(beamLength)

                self.beamModel.show()

                if scoredHit:
                    if self.laserSoundNoHit.status() == AudioSound.PLAYING:
                        self.laserSoundNoHit.stop()
                    if self.laserSoundHit.status() != AudioSound.PLAYING:
                        self.laserSoundHit.play()
                        
                    self.beamHitModel.show()

                    self.beamHitModel.setPos(hitPos)
                    self.beamHitLightNodePath.setPos(hitPos + Vec3(0, 0, 0.5))

                    if not render.hasLight(self.beamHitLightNodePath):
                        render.setLight(self.beamHitLightNodePath)

                else:
                    if self.laserSoundHit.status() == AudioSound.PLAYING:
                        self.laserSoundHit.stop()
                    if self.laserSoundNoHit.status() != AudioSound.PLAYING:
                        self.laserSoundNoHit.play()
                        
                    if render.hasLight(self.beamHitLightNodePath):
                        render.clearLight(self.beamHitLightNodePath)

                    self.beamHitModel.hide()
        else:
            if self.laserSoundNoHit.status() == AudioSound.PLAYING:
                self.laserSoundNoHit.stop()
            if self.laserSoundHit.status() == AudioSound.PLAYING:
                self.laserSoundHit.stop()
                
            if render.hasLight(self.beamHitLightNodePath):
                render.clearLight(self.beamHitLightNodePath)
                
            self.beamModel.hide()
            self.beamHitModel.hide()

        mouseWatcher = base.mouseWatcherNode
        if mouseWatcher.hasMouse():
            mousePos = mouseWatcher.getMouse()
        else:
            mousePos = self.lastMousePos

        mousePos3D = Point3()
        nearPoint = Point3()
        farPoint = Point3()

        base.camLens.extrude(mousePos, nearPoint, farPoint)

        self.groundPlane.intersectsLine(mousePos3D,
                                        render.getRelativePoint(base.camera, nearPoint),
                                        render.getRelativePoint(base.camera, farPoint))

        firingVector = Vec3(mousePos3D - self.actor.getPos())
        firingVector2D = firingVector.getXy()
        firingVector2D.normalize()
        firingVector.normalize()

        heading = self.yVector.signedAngleDeg(firingVector2D)

        self.actor.setH(heading)

        if firingVector.length() > 0.001:
            self.ray.setOrigin(self.actor.getPos())
            self.ray.setDirection(firingVector)

        self.lastMousePos = mousePos

        self.beamHitTimer -= dt
        if self.beamHitTimer <= 0:
            self.beamHitTimer = self.beamHitPulseRate
            self.beamHitModel.setH(random.uniform(0.0, 360.0))
        self.beamHitModel.setScale(math.sin(self.beamHitTimer*3.142/self.beamHitPulseRate)*0.4 + 0.9)
        
        if self.damageTakenModelTimer > 0:
            self.damageTakenModelTimer -= dt
            self.damageTakenModel.setScale(2.0 - self.damageTakenModelTimer/self.damageTakenModelDuration)
            if self.damageTakenModelTimer <= 0:
                self.damageTakenModel.hide()
                #Part 2 of Intangibility Coding
                self.intangible(2.0)
                
    def updateScore(self):
        self.scoreUI.setText(str(self.score))

    def alterHealth(self, dHealth):
        GameObject.alterHealth(self, dHealth)

        self.updateHealthUI()

        self.damageTakenModel.show()
        self.damageTakenModel.setH(random.uniform(0.0, 360.0))
        self.damageTakenModelTimer = self.damageTakenModelDuration

        self.hurtSound.play()

    #Part 1 of Intangibility Coding
    def intangible(self, time):
        
        if self.damageTakenModelTimer > 0:
            mask = BitMask32()
            mask.setBit(0)

            self.collider.node().setIntoCollideMask(mask)

            mask = BitMask32()
            mask.setBit(0)

            self.collider.node().setFromCollideMask(mask)

            WalkingEnemy.__init__.attackDamage = 0

            if time <= 0 and self.damageTakenModelTimer <= 0:
                mask = BitMask32()
                mask.setBit(1)

                self.collider.node().setIntoCollideMask(mask)

                mask = BitMask32()
                mask.setBit(1)

                self.collider.node().setFromCollideMask(mask)

                WalkingEnemy.__init__.attackDamage = -1
#Intangibility Code Ends
    def updateHealthUI(self):
        for index, icon in enumerate(self.healthIcons):
            if index < self.health:
                icon.show()
            else:
                icon.hide()

    
    def cleanup(self):
        base.cTrav.removeCollider(self.rayNodePath)

        self.scoreUI.removeNode()

        for icon in self.healthIcons:
            icon.removeNode()

        self.beamHitModel.removeNode()

        render.clearLight(self.beamHitLightNodePath)
        self.beamHitLightNodePath.removeNode()

        GameObject.cleanup(self)

        self.laserSoundHit.stop()
        self.laserSoundNoHit.stop()
                                                        
class Enemy(GameObject):
    def __init__(self, pos, modelName, modelAnims, maxHealth, maxSpeed, colliderName):
        GameObject.__init__(self, pos, modelName, modelAnims, maxHealth, maxSpeed, colliderName)

        self.scoreValue = 1

    def update(self, player, dt):
        GameObject.update(self, dt)
        self.runLogic(player, dt)

        if self.walking:
            walkingControl = self.actor.getAnimControl("walk")
            if not walkingControl.isPlaying():
                self.actor.loop("walk")
        else:
            spawnControl = self.actor.getAnimControl("spawn")
            if spawnControl is None or not spawnControl.isPlaying():
                attackControl = self.actor.getAnimControl("attack")
                if attackControl is None or not attackControl.isPlaying():
                    standControl = self.actor.getAnimControl("stand")
                    if not standControl.isPlaying():
                        self.actor.loop("stand")

    def runLogic(self, player, dt):
        pass

class WalkingEnemy(Enemy):
    def __init__(self, pos):
        Enemy.__init__(self, pos,
                       "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy",
                       {
                        "stand" : "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy-stand",
                        "walk" : "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy-walk",
                        "attack" : "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy-attack",
                        "die" : "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy-die",
                        "spawn" : "PandaSampleModels-master/PandaSampleModels-master/SimpleEnemy/simpleEnemy-spawn"
                        },
                       3.0,
                       7.0,
                       "walkingEnemy")

        self.attackDistance = 0.75
        self.acceleration = 100.0

        self.yVector = Vec2(0, 1)

        mask = BitMask32()
        mask.setBit(2)

        self.collider.node().setIntoCollideMask(mask)

        self.attackSegment = CollisionSegment(0, 0, 0, 1, 0, 0)

        segmentNode = CollisionNode("enemyAttackSegment")
        segmentNode.addSolid(self.attackSegment)

        mask = BitMask32()
        mask.setBit(1)

        segmentNode.setFromCollideMask(mask)

        mask = BitMask32()

        segmentNode.setIntoCollideMask(mask)

        self.attackSegmentNodePath = render.attachNewNode(segmentNode)
        self.segmentQueue = CollisionHandlerQueue()

        base.cTrav.addCollider(self.attackSegmentNodePath, self.segmentQueue)

        self.attackDamage = -1

        self.attackDelay = 0.3
        self.attackDelayTimer = 0
        self.attackWaitTimer = 0

        self.actor.play("spawn")

        self.deathSound = loader.loadSfx("Sounds_&_Music/Sounds_enemyDie.ogg")
        self.attackSound = loader.loadSfx("Sounds_&_Music/Sounds_enemyAttack.ogg")
    def runLogic(self, player, dt):
        spawnControl = self.actor.getAnimControl("spawn")
        if spawnControl is not None and spawnControl.isPlaying():
            return
        
        vectorToPlayer = player.actor.getPos() - self.actor.getPos()

        vectorToPlayer2D = vectorToPlayer.getXy()
        distanceToPlayer = vectorToPlayer2D.length()

        vectorToPlayer2D.normalize()

        heading = self.yVector.signedAngleDeg(vectorToPlayer2D)

        if distanceToPlayer > self.attackDistance*0.9:
            self.walking = True
            vectorToPlayer.setZ(0)
            vectorToPlayer.normalize()
            self.velocity += vectorToPlayer*self.acceleration*dt
            self.attackWaitTimer = 0.2
            self.attackDelayTimer = 0
        else:
            self.walking = False
            self.velocity.set(0, 0, 0)

            if self.attackDelayTimer > 0:
                self.attackDelayTimer -= dt
                if self.attackDelayTimer <= 0:
                    if self.segmentQueue.getNumEntries() > 0:
                        self.segmentQueue.sortEntries()
                        segmentHit = self.segmentQueue.getEntry(0)

                        hitNodePath = segmentHit.getIntoNodePath()
                        if hitNodePath.hasPythonTag("owner"):
                            hitObject = hitNodePath.getPythonTag("owner")
                            hitObject.alterHealth(self.attackDamage)
                            self.attackWaitTimer = 1.0

            elif self.attackWaitTimer > 0:
                self.attackWaitTimer -= dt
                if self.attackWaitTimer <= 0:
                    self.attackWaitTimer = random.uniform(0.5, 0.7)
                    self.attackDelayTimer = self.attackDelay
                    self.actor.play("attack")
                    self.attackSound.play()

        self.actor.setH(heading)

        self.attackSegment.setPointA(self.actor.getPos())
        self.attackSegment.setPointB(self.actor.getPos() + self.actor.getQuat().getForward()*self.attackDistance)

    def alterHealth(self, dHealth):
        Enemy.alterHealth(self, dHealth)
        self.updateHealthVisual()

    def updateHealthVisual(self):
        perc = self.health/self.maxHealth
        if perc < 0:
            perc = 0

        self.actor.setColorScale(perc, perc, perc, 1)
        
    def cleanup(self):
        base.cTrav.removeCollider(self.attackSegmentNodePath)
        self.attackSegmentNodePath.removeNode()

        GameObject.cleanup(self)
class TrapEnemy(Enemy):
    def __init__(self, pos):
        Enemy.__init__(self, pos,
                       "PandaSampleModels-master/PandaSampleModels-master/SlidingTrap/trap",
                       {
                        "stand" : "PandaSampleModels-master/PandaSampleModels-master/SlidingTrap/trap-stand",
                        "walk" : "PandaSampleModels-master/PandaSampleModels-master/SlidingTrap/trap-walk",
                        },
                       100.0,
                       10.0,
                       "trapEnemy")
        base.pusher.addCollider(self.collider, self.actor)
        base.cTrav.addCollider(self.collider, base.pusher)

        self.moveInX = False
        self.moveDirection = 0
        self.ignorePlayer = False

        mask = BitMask32()
        mask.setBit(2)
        mask.setBit(1)

        self.collider.node().setIntoCollideMask(mask)

        mask = BitMask32()
        mask.setBit(2)
        mask.setBit(1)

        self.collider.node().setFromCollideMask(mask)

        self.impactSound = loader.loadSfx("Sounds_&_Music/Sounds_trapHitsSomething.ogg")
        self.stopSound = loader.loadSfx("Sounds_&_Music/Sounds_trapStop.ogg")
        self.movementSound = loader.loadSfx("Sounds_&_Music/Sounds_trapSlide.ogg")
        self.movementSound.setLoop(True)

    def runLogic(self, player, dt):
        if self.moveDirection != 0:
            self.walking = True
            if self.moveInX:
                self.velocity.addX(self.moveDirection*self.acceleration*dt)
            else:
                self.velocity.addY(self.moveDirection*self.acceleration*dt)
        else:
            self.walking = False
            diff = player.actor.getPos() - self.actor.getPos()
            if self.moveInX:
                detector = diff.y
                movement = diff.x
            else:
                detector = diff.x
                movement = diff.y

            if abs(detector) < 0.5:
                self.moveDirection = math.copysign(1, movement)
                self.movementSound.play()

    def alterHealth(self, dHealth):
        pass

    def cleanup(self):
        self.movementSound.stop()

        Enemy.cleanup(self)
                       
                       


Before I start, please do only post the relevant portions of code: it makes life easier for people attempting to help, I think.

That said, looking at your code, I see a few potential issues:

            WalkingEnemy.__init__.attackDamage = 0

This won’t, I think, change the attack-damage of the walking enemies. Indeed, I’m not quite sure of what it’ll do: it looks like it’s instructing the program to set the value of the “attackDamage” variable of the “__init__” method of “WalkingEnemy”.

I am curious: Does this code run?

Instead, changing the attack damage of enemies that have already been spawned would be done in the spawned enemies themselves, I believe. Enemies spawned afterwards would have to be altered, too. (Or some additional logic added to their spawning.)

However, all that said, I would suggest that with the change to the player’s collision you likely don’t need to change the enemy damage-value: enemies can’t do damage if they don’t make contact, after all.

if time <= 0 and self.damageTakenModelTimer <= 0:

Since the method is usually called with a value of “2” passed into time, the code in this section will never be called, I believe.

I’m guessing that your intention for this code is that it cause the intangibility effect to time-out. If so, then I would suggest moving it to a method that is called each frame, so that it can detect changes.

I see that you’re calling “intangible” only when “damageTakenModelTimer” is less than or equal to zero, having been greater than zero and reduced. Surely that means that the intangibility will only take effect after the timer has run out, and thus two seconds after the player is hit?

Furthermore, the if-statement at the start of “intangible” requires that “damageTakenModelTimer” be greater than zero. Combined with the above, this should mean that the code inside that if-statement will never be run.

Overall, however, I think that you’re on the right track here! Much of the problem seems to be where/when you’re running or placing your various bits of code.

OK. I made the changes to move some of the code, and I finally have intangibility! Thanks for that. Now, how do I know if a method is called each frame? (Also, somehow that coding did run without crashing. It just didn’t change anything about the original game.)

1 Like

Excellent, and well done! :slight_smile:

It’s my pleasure. :slight_smile:

In short, it’s called each frame if you have it be called each frame.

Most commonly in Panda, I think, this involves setting up a task that calls a method, and having that method return “task.cont”.

The task will execute, the method will be called–and “task.cont” will instruct Panda to call the method again.

Presuming that there’s no delay applied to the task (as in the case of a call to “taskMgr.doMethodLater”), said task should be called on the next frame. As a result, it will be called on one frame, see the return-value of “task.cont”, and so be called on the next frame–and so on.

In the code above, the first part of this is done in the call to “taskMgr.add”. The second part is done at the end of the “update” method (being the method that is called by the task that’s created in the first part).

See here for more information:
https://www.panda3d.org/manual/?title=Tasks

Huh, weird! Maybe methods can have variables of their own? If so, then that’s feature that I wasn’t aware of!

OK, so what I have tried to do for the time is to turn the intangible method into a task to check every frame; however, I am receiving this error:

AttributeError: module ‘direct.task.Task’ has no attribute ‘time’

How can I add the ‘time’ attribute to use it? Here is the code now:

def intangible(self, Task):
        
        if self.damageTakenModelTimer > 0:
            mask = BitMask32()
            mask.setBit(0)

            self.collider.node().setIntoCollideMask(mask)

            mask = BitMask32()
            mask.setBit(0)

            self.collider.node().setFromCollideMask(mask)

            return Task.cont

        elif Task.time >= 2 and self.damageTakenModelTimer <= 0:
            mask = BitMask32()
            mask.setBit(1)

            self.collider.node().setIntoCollideMask(mask)

            mask = BitMask32()
            mask.setBit(1)

            self.collider.node().setFromCollideMask(mask)

            return Task.done
                
        #Intangibility Code Ends

I think that the problem is that you’ve capitalised the word “task”, which is confusing the program into thinking that you’re referring to the “Task” module, rather than an individual task object.

OK. With that, I get a different error:

intangible() missing 1 required positional argument: ‘task’

And when I put task in as an argument for self.intangible(), I get this error message:

NameError: name ‘task’ is not defined

I’m not sure what the problem is.

I’m guessing that you’re referencing “intangible” in the call to “taskMgr.add”, correct?

If so, are you perhaps including brackets after “intangible”? That is, are you doing something like the following?

taskMgr.add(self.intangible(), "intangible task name")

If so, then the problem is twofold:

  1. As the error says, the “intangible” method requires a task-object. And of course, at the point at which you’re using it, you don’t have a task-object to provide to it.
    And
  2. More importantly, however, doing so will immediately call the “intangible” method, and whatever it returns–if anything–is what will be passed into “taskMgr.add” as the method to be called.

That is to say, doing so doesn’t pass a reference to the “intangible” method into “taskMgr.add”. Instead, it calls the “intangible” method, and the result is passed into “taskMgr.add”.

If that is what you’re doing, then the solution is to simply leave out the brackets after “intangible”. This then doesn’t call the method, but rather passes in a reference to it.

Something like this:

taskMgr.add(intangible, "intangible task name")

With the adding of the taskMgr.add, would I still need to call the function using self.intangible()? Also, where would I put the taskMgr.add to make it work? It crashed when I left the call in there. Right now, I removed to original call to it and put the task.Mgr.add in its place, and I’m able to get the intangibility still; however, the intangibility never goes away.

Ah, that’s not quite what I had in mind. You had the “taskMgr.add” call itself correct beforehand, as far as I’m aware. No need to move it, or to change it.

Rather, the problem was with the manner in which you were passing in “self.intangible”. As noted above, to pass in the method itself, you would provide it without brackets.

No. After all, what you’re doing is asking the task-manager to call it for you, automatically; there’s thus no reason to call it yourself.

What error message did it give?

I was thinking along the lines that the original taskMgr.add() was in the main game files rather than the gameobject file with the intangible method, so I would have to put a taskMgr.add() in the gameobject file connected to the intangible function. The error I received when leaving the call in was the same NameError as before since I tried self.intangible(task) to call it.

Now, I have changed the code and removed the taskMgr.add() from gameobject and added intangible to the main game file at the same spot as the taskMgr.add() for self.update.

self.updateTask = taskMgr.add(self.update, "update")
taskMgr.add(Player.intangible, "intangible")

When I tried just to have intangible, it said that it was not defined. Now, it gives me the message that it is missing the task argument again.

No, it should work from either location, as long as its provided a valid reference to the method that it’s supposed to call, I believe.

That suggests that the problem may have been the same: you were trying to pass in a “task” object that didn’t yet exist. (But again, that implies that you were calling “intangible” rather than passing it in, which would be the core problem if so, I daresay.)

Correct: “intangible” is defined the the “Player” class, and not the general scope of the main Game class, I daresay.

By the looks of it, here you’re passing in a reference to the method as it is in the class itself (since you’re using the capitalised spelling of “Player”, which refers to the class). The method isn’t really meant to be called that way. When it is called that way, the “task” object that the task-manager passes in is likely ending up in the first parameter–the “self” parameter"–leaving the second parameter–the “task” parameter–unfilled, so I imagine. Hence the error!

So, there are two main options that I see: Either:

  1. You reference the “intangible” method in an instance of the “Player” class–i.e. the actual “player” object that you have,
    Or
  2. You move the call to “addTask” back into the “Player” class.

Either should work in this scenario, I believe, so for this project it’s a matter of which design you prefer!

Here is how I’m putting in the code:

taskMgr.add(intangible, "intangible")

I have included it in the update method of the Player class of gameobject (same place I originally and wrongly put the call to the method earlier), and when I try to run it, it says:

NameError: name ‘intangible’ is not defined

I did not store taskMgr.add() into any variables for the intangible task; I put it in raw.

Ah, I see now–thank you for posting that code-snippet!

Yes, the problem there is that you’re just referring to “intangible”, and not specifying the object in which it exists. For example:

class Cat:
    def __init__(self):
        self.meow = "meow!"

    def someMethod(self):
        print (meow)

This won’t work, I believe: we haven’t specified that we’re referring to the object’s “meow” variable, and there’s nothing by that name defined in the method. (Or in some greater scope.)

However:

class Cat:
    def __init__(self):
        self.meow = "meow!"

    def someMethod(self):
        print (self.meow)

This should work: we’ve now specified that we want the “meow” variable belonging to the object referenced in “self” (which should be the instance of the “Cat” class on which the “someMethod” is being called).

On another note:

If I understand you correctly, this specific spot may not be wise: that “update” method should be being called on every frame (being controlled by a task itself). As a result, you’ll be creating the task each frame…

Sorry for the late update, but with your help and some tinkering on my own, I finally got the code working!
I had to do some weird things such as adding the task in the update section of the gameobject file so that the task would only start when the player was hit as well as change my actual task to make it continue at the end as well as to stop the task after 3 seconds. I also had to add to the cleanup method to remove the intangible task once the game restarted to avoid it crashing. Thanks for your help!

1 Like

I’m glad that you got it working–well done! And it’s my pleasure to have helped! :slight_smile: