<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">"""
An Invaders-like Controller module for lecture demos.

You should refer to this file to see how to had and remove laser bolts.

Authors: Walker White (wmw2), Lillian Lee (ljl2), and Steve Marschner (srm2)
Date:    November 10, 2017 (Python 3 Version)
"""
import introcs
import random
import math
from game2d import *

############# CONSTANTS #############
# Window Size
WINDOW_WIDTH  = 512
WINDOW_HEIGHT = 512

# Gravity of surface
GRAVITY = -0.2
# Upward velocity
ROCKET_VY = 1.2
# For the explosion
PARTICLE_DIAMETER = 5
MAX_INIT_VEL = 5
PARTICLES_PER_SHELL = 50

# Colors
WHITE_COLOR = introcs.RGB(255,255,255)
GRAY_COLOR  = introcs.RGB(128,128,128)


############# CONTROLLER CLASS #############
class Pyro(GameApp):
    """
    This class is a controller for lecture demos about interaction and animation.

    This class extends GameApp and implements the various methods necessary for
    an interactive application.

    * Method start() is called once at the beginning.

    * Method update() is called repeatedly to do animation.

    * Attribute _state keeps track of the current game state.

    * The on_touch methods handle mouse (or finger) input.

    INSTANCE ATTRIBUTES (Not hiding any):
        view :    the view (inherited from GameApp)     [GView]
        input :   the input (inherited from GameApp)    [GInput]
        _rockets: the list of rockets to animate        [list of Rocket]
        _sparks:  the list of sparks to animate         [list of Spark]
        _last:    the last position clicked
                  [GPoint, None if mouse not down last frame]
    """

    # THREE MAIN METHODS

    def start(self):
        """
        Initializes the program state.
        """
        self._rockets = []
        self._sparks  = []
        self._last = None # Have not clicked mouse yet.

    def update(self,dt):
        """
        Animates a single frame. Called every 1/60 of a second.

        Parameter dt: The time since the last animation frame.
        Precondition: dt is a float.
        """
        # helper method to process the mouse click
        self._checkClick()
        # Helper method to move rockets
        self._moveRockets()

    def draw(self):
        """
        Draws all particles in the view.
        """
        for rocket in self._rockets:
            rocket.draw(self.view)

        for spark in self._sparks:
            spark.draw(self.view)


    # HELPER METHODS
    def _explodeRocket(self, rocket):
        """
        Explodes the ship rocket.

        Parameter rocket: The rocket to explode
        Precondition: rocket must be of type Rocket.
        """
        color = introcs.RGB(random.randrange(256),
                            random.randrange(256),
                            random.randrange(256))
        for i in range(PARTICLES_PER_SHELL):
            spark = Spark(rocket.x, rocket.y, color)
            self._sparks.append(spark)

    def _checkClick(self):
        """
        Checks for a click and add a Rocket if necessary.

        A 'click' is the animation frame after the mouse is pressed for the first
        time in a while (so _lastclick is None).
        """
        # Input stores the touch information
        touch = self.input.touch

        if self._last is None and not touch is None:
            # Click happened.  Add rocket to particle list.
            rocket = Rocket(touch.x, touch.y)
            self._rockets.append(rocket)

        # Update lastclick
        self._last = touch


    def _moveRockets(self):
        """
        Moves all the rockets and explodes them when they get to high.
        """
        for rocket in self._rockets:
            rocket.move()
            # Handle a rocket if it explodes
            if rocket.isExploded():  # MUST use getter here
                self._explodeRocket(rocket)

        # Delete any exploded rockets
        i = 0
        while i &lt; len(self._rockets):
            if self._rockets[i].isExploded():  # MUST use getter here
                del self._rockets[i]
            else:
                i += 1

        # move all the sparks
        for spark in self._sparks:
            spark.move()

        # Delete any sparks out of view
        i = 0
        while i &lt; len(self._sparks):
            if self._sparks[i].y &lt; -10:
                del self._sparks[i]
            else:
                i += 1


############# MODEL CLASSES #############
class Spark(GEllipse):
    """
    This class represents particles created in shell explosions.

    INSTANCE ATTRIBUTES (Beyond those in GEllipse):
        _vx: velocity in x direction    [float]
        _vy: velocity in y direction    [float]
    """

    def __init__(self, x, y, color=WHITE_COLOR):
        """
        Initializer: Creates particle at (x,y) with random velocity and given color.

        Parameter x: the starting x-coordinate
        Precondition: x is a number (int or float)

        Parameter y: the starting y-coordinate
        Precondition: y is a number (int or float)

        Parameter color: the spark color
        Precondition: color is a valid color object or name (e.g. a string)
        """
        super().__init__(x=x, y=y,
                         width=PARTICLE_DIAMETER, height=PARTICLE_DIAMETER,
                         fillcolor=color)
        self._vy = random.uniform(-MAX_INIT_VEL,MAX_INIT_VEL)
        self._vx = math.sqrt(MAX_INIT_VEL**2 - self._vy**2) * math.sin(random.uniform(0,2*math.pi))

    def move(self):
        """
        Moves the spark by the current velocity
        """
        self.x += self._vx
        self.y += self._vy
        self._vy += GRAVITY


class Rocket(GEllipse):
    """
    This class represents rockets that will generate explosions later.

    INSTANCE ATTRIBUTES (Beyond those in GEllipse):
        _trigger_y: the y coordinate at which rocket will explode    [float]
        _exploded:  True if the rocket has exploded
                    [bool, True if self.y &gt; self.trigger_y]
    """

    # Must use getters to access with an object of another class
    def isExploded(self):
        """
        Returns: True if rocket has exploded, False otherwise
        """
        return self._exploded

    def __init__(self, x, y):
        """
        Initializer: Creates a rocket headed for an explosion at (x,y).

        Parameter x: the starting x-coordinate
        Precondition: x is a number (int or float)

        Parameter y: the y=threshold for the explosion
        Precondition: y is a number (int or float)
        """
        super().__init__(x=x, y=0,
                         width=PARTICLE_DIAMETER, height=PARTICLE_DIAMETER,
                         fillcolor=GRAY_COLOR)
        self._trigger_y = y
        self._exploded = False

    def move(self):
        """
        Moves the rocket and sets it to explode as necessary.
        """
        self.y += ROCKET_VY
        if self.y &gt; self._trigger_y:
            self._exploded = True


# Script Code
if __name__ == '__main__':
    Pyro(width=WINDOW_WIDTH,height=WINDOW_HEIGHT,fps=30.0).run()
</pre></body></html>