Excercise 5-8.
  1. Now we will make the main ship's defenses. All units related to defenses should be placed in a folder called Defenses. Create an abstract base class called TDefense. This class should have a field called LastFired of type Integer which will store the frame at which the defense was previously fired (to prevent continuous firing by holding the space bar). Also create an abstract function called CanFire and a procedure called Fire which takes the current position of the ship as a vector. The unit DefenseClass should be placed in a folder called Defenses.
  2. Now create four classes that descend from TDefense: TBlaster, TDualBlaster, TDetonator, and TWideSpray. All of these classes will implement CanFire and Fire, but don't worry about filling them in, just override these functions and write the basic implementation header and empty begin-end statement.
  3. Now that we have an idea of where we are going with the defenses, let's change gears for a moment and implement the special effects that will be required for drawing them. We will use flares so as you might expect we need to create a TFlare class. We will create everything related to flares in a folder called Common. We are doing this (instead of placing the unit in Defenses) because flares will be used for other effects other than Defenses later on. TFlare will need fields such as CreationFrame and DetonationTime (to measure the amount of time that has elapsed for flares that detonate) as well as Position, Velocity, and Intensity. We should implement StepAndDraw (implement this in a similar way to how we have implemented flares before) as well as a constructor that initializes Intensity to 255. The hue for all flares is constant and will be the color of clOrange. We will also need a global Flares TList that will be used to keep track of all the flares.
  4. Let's test our TFlare class now. Go to the main program source file. You will first need to initialize the Flares list in Initialize (i.e., create an instance of TList). Next implement DrawAllFlares in which you loop through the Flares list and call StepAndDraw for each flare. You will also need to add FlareClass to the uses clause of course in order to get access to TFlare and Flares. Add a call to DrawAllFlares below HandleUserInput and then call DoConvolutionFilter after DrawAllFlares but before drawing the ships. The reason we call DoConvolutionFilter before any more drawing is that anything drawn with conventional graphics before DoConvolutionFilter is erased because flares are blurred in the background.
  5. Let's briefly test the flare graphics we just created by adding a flare to the Flares list in Initialize. The flare should have an initial position somewhere on the screen and have a very slow velocity so we can see that it moves correctly.
  6. Now we will return to implementing the defenses classes. To begin with, where should we create instances of defenses? Should we create them in the main program source file? Should they be created as fields in some class? Think about what uses defenses (assuming enemies don't have them yet). Also, remember that we want to use the up and down arrow keys to toggle through defenses. What is the best way to store the defense objects? As separate variables or as something else?
  7. Create a constructor for TMainShip that initializes the Defenses list, adds instances of the four defense classes to the list, sets DefenseIndex to 0, and sets Defense to Defenses[0]. Implement ChangeDefense so that the next item in the Defenses list is set to current if Dir is 1 and the previous item in the list is set to current if Dir is -1. If either end of the list is reached then do not change the defense.
  8. Implement Fire. To do this, think of what methods are contained on the abstract base class TDefense (at this point we do not need to do anything with actual flare drawing). Keep in mind also that a defense should be fired if and only if CanFire returns True.
  9. Return to the main program source file. Add code to the HandleUserInput procedure that will call the routines ChangeDefense when the up and down arrow keys are pressed and Fire when the spacebar is pressed. (Hint: The key codes are VK_UP, VK_DOWN, and VK_SPACE).
  10. Now start implementing the Fire method on the actual defense classes. Start with TBlaster. Set the return value of CanFire to True for now. Inside of Fire, you will need to create a TFlare, set its position and velocity, and add it to the Flares list. The position should be the same as the ship's position for now. Velocity can be something like (0,60) and Intensity should be 50. Also update the LastFired field to the current FrameCount.
  11. Let's modify the behavior of flares so that we can make their motion more continuous if we want. You will remember from previous chapters that to make the flare effect continuous we have to take multiple steps per call to StepAndDraw. Let's make a field called Multistep that will determine how many extra steps to take (i.e., for some flares we will want more discrete behavior and others we will want more continuous behavior). You should implement a constructor in TFlare to initialize Multistep to 1 by default. After modifying TFlare, go to TBlaster and change the initialization of the flare so that its Multistep is 15.
  12. You will notice that in the previous problem if we hold the spacebar we get continuous firing of the defense. Let's limit the firing rate by modifying CanFire. Allow the defense to fire only if 5 frames have elapsed since the defense was last fired.
  13. Although an Intensity of 50 is appropriate for the flare effect we want, it is somewhat difficult to see the actual projectile. Let's modify the drawing of flares to highlight where the flare actually is. We'll do this by creating a procedure called DrawFlarePoint on TFlare. This procedure should simply draw a yellow circle of radius 0.05 at the flare's current position. Then return to the main program source file and call this procedure after DoConvolutionFilter for all flares in the Flares list.
  14. Next let's implement the TDualBlaster CanFire and Fire methods. For CanFire, let's allow the dual blaster to rapid fire only occasionally. To do this use Random(5) for the frame count cutoff. As the name implies, the dual blaster will fire two flares. We will need to create a left flare that has a position 0.5 units to the left of the current ship position and a right flare that has a position 0.5 units to the right. Make the velocity (0,80). The Multistep should be about 5 (we want it more discrete than the regular blaster) and Intensity should again be 50. Test your code by running the program and then using the down arrow to switch defenses.
  15. Implement the TDetonator class. Delay rapid firing in CanFire by 10 frames. The flares in Fire will be similar to single blaster flares except they will be slower (Velocity = (0,30)), they will have a DetonationTime in seconds (make it something between 0.1s and 0.3s), and they should have a Multistep of 10.
  16. If you run the code written in the previous problem you will notice that the Detonator seems to just simply be a slower Blaster. We still need to implement detonating behavior in the TFlare class. First add code to StepAndDraw that will handle detonation if enough time has elapsed by calling a procedure called Detonate. You'll need to add a line to the constructor that sets CreationFrame to FrameCount as well. Then implement Detonate to remove the flare from the list and produce 40 fragments that spread out radially at different random velocities. Since you should use Flares[Flares.IndexOf(Self)] := nil you'll need to use Flares.Pack in DrawAllFlares of the main source file right after the loop completes or you will get a runtime error.
  17. Finally let's implement the TWideSpray class. Allow for rapid fire this time. Set the velocity to both a random angle (between 45 and 135) and a random magnitude (between 20 and 50). Intensity is 50. Multistep should not be set (i.e., keep the default of 1). Create 5 flares per fire.
  18. One last thing we should do concerns optimization. As the program is written now, when flares go off the screen they are still in the Flares loop. Over time this will tend to slow the game down. Use the OutOfBounds(Position) function which returns True when Position is out of bounds to remove flares from the Flares list after StepAndDraw is called.