Fortunately, we'll be able to do a good bit of copying and pasting code we've already written to make the main cannon. Let's get started.

  1. Open ExplosionClasses_01.py. The first thing we'll do is to make a new kind of explosion for the main cannon.
  2. Add a new import to the top of the file:
    from pandac.PandaModules import *
    
  3. Copy the entire Pop class and paste it into the bottom of the file. Change the class name to Boom, and use the find and replace feature of Notepad++ to change self.pop into self.boom everywhere it appears in the Boom class.
  4. Change the definition of the __init__ method for the Boom class to look like this:
    def __init__(self, pos, scale, damage):
    
  5. Add the following line to the __init__ method of the Boom class, immediately after self.boom.setPos(pos):
    self.boom.setScale(scale)
    
  6. At the bottom of the Boom class's __init__ method, add the following block of code:
    self.boomCN = CollisionNode("BoomCN")
    self.boomCS = CollisionSphere(0,0,0,scale)
    self.boomCN.addSolid(self.boomCS)
    self.boomCN.setIntoCollideMask(BitMask32.allOff())
    self.boomCN.setFromCollideMask(BitMask32.bit(4))
    self.boomCNP = render.attachNewNode(self.boomCN)
    self.boomCNP.setPos(pos)
    self.boomCTrav = CollisionTraverser()
    self.boomCHan = CollisionHandlerQueue()
    self.boomCTrav.addCollider(self.boomCNP, self.boomCHan)
    self.checkCollision(damage)
    
  7. Now, we need to add the checkCollision() method we just put in a call to. Put this method right above the destroy() method in the Boom class:
    def checkCollision(self, damage):
    cyclesDamaged = []
    self.boomCTrav.traverse(render)
    if(self.boomCHan.getNumEntries() > 0):
    for E in range(self.boomCHan.getNumEntries()):
    entry = self.boomCHan.getEntry(E)
    cycleHit = entry.getIntoNodePath().getPythonTag("owner")
    if(cycleHit not in cyclesDamaged):
    cycleHit.hit(damage)
    cyclesDamaged.append(cycleHit)
    
  8. Finally, we'll edit the destroy() method to clean up the collision sphere we made. Add this line, right after self.boom.removeNode():
    self.boomCNP.removeNode()
    
  9. Resave the file as ExplosionClasses_02.py and we're done with it. Next, open GunClasses_02.py.
  10. Update the import to use the new file for explosions.
  11. Copy the entire MachineGun class and paste it into the bottom of the file. Change the name of this new class to Cannon.
  12. Change the lines in the __init__ method where we load the Actor and model for the gun to look similar to the following code:
    self.actor = Actor("../Models/CannonActor.egg")
    self.model = loader.loadModel("../Models/Cannon.bam")
    
  13. Remove this line from the Cannon class' __init__ method:
    self.projModel.setScale(.25, 1, .25)
    
  14. Change reloadTime to equal 1.5, and change self.damage to equal 75.
  15. Add this new variable to the __init__ method, right underneath our declaration of self.damage:
    self.blastR = 10
    
  16. Modify the declaration of self.flashLerp to make the flash bigger:
    self.flashLerp = LerpScaleInterval(self.flashModel,
    reloadTime * .1, Point3(2,2,2), Point3(.2,.2,.2))
    
  17. Alter the declaration of self.fireSeq to add a longer wait, like this:
    self.fireSeq = Sequence(self.firePar,
    Func(self.clearEffects),
    Wait(reloadTime * .9))
    
  18. Next, change the declaration of self.firePar so that it looks like the following code:
    self.firePar = Parallel(
    Func(self.checkForHit),
    Func(self.setEffects),
    self.flashLerp)
    
  19. In the checkForHit() method, delete all the lines that contain the thingHit variable anywhere in them. Then, edit the method to use Boom instead of Pop. The method should look similar to the following code when we're finished:
    def checkForHit(self):
    self.cycle.trgtrCTrav.traverse(render)
    if(self.cycle.trgtrCHan.getNumEntries() > 0):
    self.cycle.trgtrCHan.sortEntries()
    entry = self.cycle.trgtrCHan.getEntry(0)
    colPoint = entry.getSurfacePoint(render)
    self.refNP.setPos(render, colPoint)
    boom = Boom(colPoint, self.blastR, self.damage)
    else:
    self.refNP.setPos(self.cycle.trgtrCNP, 0, 300, 0)
    boom = Boom(self.refNP.getPos(render),
    self.blastR, self.damage)
    
  20. Resave the file as GunClasses_03.py and open CycleClass_03.py. We need to add the cannons to the cycle, and tie the cannon's fire control to the right mouse button.
  21. To start with, update the import of the gun classes to use the new file.
  22. Scroll down to the setupVarsNPs() method and add the following two lines to it, right underneath where we create our machine guns:
    self.CannonMount = self.turretActor.exposeJoint(None,
    "modelRoot", "CannonMount")
    self.cannon = Cannon(self, self.CannonMount)
    
  23. Now, scroll down to the CycleControl() method and add these lines right above the line where we declare aimPoint.
    if(self.inputManager.keyMap["mouse3"] == True):
    self.cannon.fire()
    
  24. The last edit here is to add these lines to the destroy() method, right beneath the lines that remove the machine guns:
    self.cannon.destroy()
    self.cannon = None
    
  25. Resave the file as CycleClass_04.py.
  26. Update RaceClass_02.py to use CycleClass_04.py, and then resave it as RaceClass_03.py.
  27. Update WorldClass_02.py to use RaceClass_03.py, and then resave it as WorldClass_03.py.
  28. Run WorldClass_03.py from the command prompt and try out the cannon!
Time for action - creating the main cannon