Anonymous avatar Anonymous committed a8c6091

Moved *.py to Source and it's not immediately aborting anymore!

Comments (0)

Files changed (36)

Added
New image
Add a comment to this file

Graphics/dramaBackground.png

Added
New image
Added
New image
Added
New image
Added
New image
Add a comment to this file

Graphics/gas-oneThird.png

Added
New image
Add a comment to this file

Graphics/gas-twoThirds.png

Added
New image
Added
New image

MenuClass.py

-#! /usr/bin/python
-
-## \file  menu.py
-#  \brief General Menu Class
-#  \author Scott Barlow
-#  \date 2009
-#  \version 1.0.3
-#
-#  This is a menu class written for pygame/Python.  The menu is designed to work
-#  with a program using a finite state machine (but it could also be easily
-#  modified to have the 'buttons' return functions).  The menu 'buttons' contain
-#  a 'state' (a state could really be anything you want) and this 'state' is
-#  what is returned when the user selects/presses the button.  The program
-#  controlling the menu can then act on this returned state as required.  This
-#  helps to write non-blocking code.
-#
-#  The menu can have text buttons, image buttons (that get highlighted on all
-#  sides to detect which is selected), or any combination of the two.
-#
-#  The menu is flexible and can be dynamically changed.  The 'buttons' will
-#  auto-magically update themselves the next time they are drawn to the screen
-#  (via the update method, which calls the draw method).  The draw method should
-#  not be called itself.  'Buttons' can be added or removed at any time.
-#
-#  The menu can be positioned by the top left corner (a rectangle containing all
-#  buttons is what gets moved).  It can be changed to center the entire menu
-#  (i.e. center that containing rectangle) on that same position coordinate.  Or
-#  the user can center the entire menu on the self.draw_surface.  Note that if
-#  the pygame screen is given to the menu, then the entire window will be
-#  available to be drawn to.  But if the user gives the menu another pygame
-#  surface, then that surface itself will need to be blitted to the pygame
-#  screen at some point.  Furthermore, the user can align the buttons to align
-#  on the left, tobe centerd, or to align themselves on the right.  Also, they
-#  can be aligned vertically on the top, center, or bottom.
-#
-#  The user can dynamically change the colors of the font/highlights, the
-#  padding between buttons (left/right and top/bottom), the thickness of the
-#  highlight around image buttons, and the orientation of the menu (if the
-#  'buttons' will be stacked top to bottom ('vertical') or left to right
-#  ('horizontal').
-#
-#  The best way to figure out the menu is to tinker around with it.  Run the
-#  example programs, change attributes, and play with the menu.
-#
-#
-#       Copyright 2009 Scott Barlow
-#
-#       This program is free software; you can redistribute it and/or modify
-#       it under the terms of the GNU General Public License as published by
-#       the Free Software Foundation; either version 3 of the License, or
-#       (at your option) any later version.
-#
-#       This program is distributed in the hope that it will be useful,
-#       but WITHOUT ANY WARRANTY; without even the implied warranty of
-#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#       GNU General Public License for more details.
-#
-#       You should have received a copy of the GNU General Public License
-#       along with this program; if not, write to the Free Software
-#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-#       MA 02110-1301, USA or see <http://www.gnu.org/licenses/>.
-#
-#
-#  Changelog
-#     V1.0.0 - Initial Release
-#     V1.0.1 - Added get_current_image method
-#     V1.0.2 - Fixed a bug in the set_font method (to update the rect of the
-#              text buttons when the font is changed
-#     V1.0.3 - Added self.refresh_whole_surface_on_load functionality
-#
-
-
-#-------------------------------------------------------------------------------
-#---[ Imports ]-----------------------------------------------------------------
-#-------------------------------------------------------------------------------
-import pygame
-
-
-#-------------------------------------------------------------------------------
-#---[ Defines ]-----------------------------------------------------------------
-#-------------------------------------------------------------------------------
-## RGB color for Black
-BLACK = (0, 0, 0)
-
-## RGB color for White
-WHITE = (255, 255, 255)
-
-## RGB color for Red
-RED = (255, 0, 0)
-
-## RGB color for Green
-GREEN = (0, 255, 0)
-
-## RGB color for Blue
-BLUE = (0, 0, 255)
-
-## This is a user event that should be sent whenever the game state is changed
-#  (at the main game loop level)
-EVENT_CHANGE_STATE = pygame.USEREVENT + 1
-
-#-------------------------------------------------------------------------------
-#---[ cMenu Class ]-------------------------------------------------------------
-#-------------------------------------------------------------------------------
-## This class is used to display and control a menu
-#
-class cMenu:
-   ## ---[ __init__ ]-----------------------------------------------------------
-   #  @param   self        The class itself, Python standard
-   #  @param   x           The x location to shift the buttons by when the
-   #                       button is drawn to the surface in the update method
-   #  @param   y           The y location to shift the buttons by when the
-   #                       button is drawn to the surface in the update method
-   #  @param   h_pad       Number the extra pixels to pad the buttons by (on the
-   #                       left/right)
-   #  @param   v_pad       Number the extra pixels to pad the buttons by (on the
-   #                       top/bottom).
-   #  @param   orientation Should be 'vertical' or 'horizontal'.  The buttons
-   #                       will be put vertically or horizontally until 'number'
-   #                       (the next argument) of buttons have been created at
-   #                       which point it will start a new row or column
-   #  @param   number      The number of buttons to put vertically or
-   #                       horizontally before starting a new row or column
-   #  @param   background  The background to use for the buttons (what will show
-   #                       up behind the buttons).  This is often the
-   #                       surface that they will be blitted to via the update
-   #                       method
-   #  @param   buttonList  This is a list of buttons to be added (though
-   #                       more buttons can be added via another method).  The
-   #                       elements of the list should be tuples of 3 parts as
-   #                       shown: ('text', state, image) where text is the text
-   #                       that will be shown for the button, state is what will
-   #                       be returned when the button is pressed (enter is
-   #                       hit), and image is None if the button is just going
-   #                       to display text, or else is an image itself if the
-   #                       button will be displayed as an image instead of text.
-   #
-   #  Initialize the class
-   #
-   def __init__(self, x, y, h_pad, v_pad, orientation, number, background,
-                buttonList):
-      ## menu items
-      self.menu_items = []                      # List of menu items
-      self.font = pygame.font.Font(None, 32)    # Font to use
-
-      self.x = x                                # Top left corner (of surface)
-      self.y = y                                # relative to the screen/window
-      self.change_number = number               # See description above
-      self.orientation = orientation            # See description above
-      self.horizontal_padding = h_pad           # See description above
-      self.vertical_padding = v_pad             # See description above
-
-      self.selection = 0                        # The currently selected button
-      self.u_color = WHITE                      # Color for unselected text
-      self.s_color = RED                        # Color for selected text
-      self.image_highlight_color = BLUE         # Color for the image highlights
-      self.image_highlight_offset = 2           # Addition padding around image
-                                                # buttons only for the highlight
-
-      self.background = background.copy()       # The unedited background image
-      self.draw_surface = background            # Surface to draw to
-      self.centered = False                     # True if the menu is centered
-      self.centeredOnScreen = False             # True if the menu is centered
-      self.update_buttons = True                # True if the positions of the
-                                                # buttons need to be updated
-      self.refresh_whole_surface_on_load = False# When the menu is first
-                                                # displayed (when the event
-                                                # EVENT_CHANGE_STATE is given to
-                                                # the update method), the entire
-                                                # self.draw_surface will be
-                                                # updated
-
-      # This dictionary contains the alignment orientation of the buttons
-      # related to each other.  It shifts the button within the bounds of
-      # 'max_width' and 'max_height' in the self.position_buttons() method.
-      self.alignment = {'vertical'  :'top',
-                        'horizontal':'left'}
-
-      # Now add any buttons that were sent in
-      self.add_buttons(buttonList)
-    
-      self.mousePos = [0,0]
-
-
-   ## ---[ redraw_all ]---------------------------------------------------------
-   def redraw_all(self):
-      for button in self.menu_items:
-         button['redraw'] = True
-
-   ## ---[ get_current_text ]---------------------------------------------------
-   def get_current_text(self):
-      return self.menu_items[self.selection]['text']
-
-   ## ---[ get_current_image ]--------------------------------------------------
-   def get_current_image(self):
-      return self.menu_items[self.selection]['b_image']
-
-   ## ---[ set_unselected_color ]-----------------------------------------------
-   def set_unselected_color(self, new_color):
-      self.u_color = new_color
-      self.update_buttons = True
-
-   ## ---[ set_selected_color ]-------------------------------------------------
-   def set_selected_color(self, new_color):
-      self.s_color = new_color
-      self.update_buttons = True
-
-   ## ---[ set_image_highlight_color ]------------------------------------------
-   def set_image_highlight_color(self, new_color):
-      self.image_highlight_color = new_color
-      self.update_buttons = True
-
-   ## ---[ set_image_highlight_thickness ]--------------------------------------
-   def set_image_highlight_thickness(self, new_thick):
-      old_th = self.image_highlight_offset
-      # We need to update the width of the button images now (the images
-      # themselves will be updated before the next refresh/re-draw).  Note that
-      # we only change the rect on the image buttons since we only highlight the
-      # image buttons (not the text buttons)
-      for button in self.menu_items:
-         if button['b_image'] != None:
-            button['rect'][2] = button['rect'][2] - 2 * old_th + 2 * new_thick
-            button['rect'][3] = button['rect'][3] - 2 * old_th + 2 * new_thick
-      self.image_highlight_offset = new_thick
-      self.update_buttons = True
-
-   ## ---[ set_padding ]--------------------------------------------------------
-   def set_padding(self, h_pad, v_pad):
-      self.horizontal_padding = h_pad
-      self.vertical_padding   = v_pad
-      self.update_buttons = True
-
-   ## ---[ set_orientation ]----------------------------------------------------
-   def set_orientation(self, new_orientation):
-      if new_orientation == 'vertical' or new_orientation == 'horizontal':
-         self.orientation = new_orientation
-         self.update_buttons = True
-      else:
-         print 'WARNING:  cMenu.set_orientation:  Invalid argument '\
-               'new_orientation (value: %d)' % new_orientation
-
-   ## ---[ set_change_number ]--------------------------------------------------
-   def set_change_number(self, new_change_number):
-      self.change_number = new_change_number
-      self.update_buttons = True
-
-   ## ---[ set_refresh_whole_screen_on_load ]-----------------------------------
-   def set_refresh_whole_surface_on_load(self, new_val = True):
-      self.refresh_whole_surface_on_load = new_val # Should be True or False
-
-   ## ---[ set_font ]-----------------------------------------------------------
-   def set_font(self, font):
-      self.font = font
-
-      # We need to update the width and height of the text buttons since we
-      # calculated their width and height based on the font
-      for button in self.menu_items:
-         if button['b_image'] == None:
-            width, height = self.font.size(button['text'])
-            button['rect'][2] = width
-            button['rect'][3] = height
-
-      self.update_buttons = True
-
-   ## ---[ set_alignment ]------------------------------------------------------
-   #  @param   self     The class itself, Python standard
-   #  @param   v_align  The way to align the text vertically within its 'cell'
-   #  @param   h_align  The way to align the text horizontally within its 'cell'
-   #
-   #  This method sets the alignment of the buttons within their 'cell' (i.e. it
-   #  sets the alignment of the button (based on it's width and height) within
-   #  the max_width and max_height values calculated in the
-   #  self.position_buttons() method).  The self.position_buttons() method is
-   #  also where the alignment occurs.  The valid alignments are:
-   #     left
-   #     center
-   #     right
-   #
-   def set_alignment(self, v_align, h_align):
-      if v_align in ['top', 'center', 'bottom']:
-         self.alignment['vertical'] = v_align
-      if h_align in ['left', 'center', 'right']:
-         self.alignment['horizontal'] = h_align
-      self.update_buttons = True
-
-
-   ## ---[ set_position ]-------------------------------------------------------
-   #  @param   self   The class itself, Python standard
-   #  @param   x      The x (horizontal location)
-   #  @param   y      The y (vertical location)
-   #
-   #  This method sets the x and y locations for the menu.  By default, this
-   #  sets the position of the menu with respect to the top left corner of the
-   #  self.draw_surface.  If 'centered' is true, then this is the location of
-   #  the center of the menu.
-   #
-   def set_position(self, x, y):
-      self.x = x
-      self.y = y
-      self.update_buttons = True
-
-
-   ## ---[ set_center ]---------------------------------------------------------
-   #  @param   self           The class itself, Python standard
-   #  @param   centered       A boolean, centers the menu if it is True, default
-   #                          value is True
-   #  @param   centeredOnScreen  If this is true, then the menu will be centered
-   #                             on the entire self.draw_surface surface.
-   #
-   #  When passed a value of True, this centers the menu at the self.x and
-   #  self.y locations.  If False is passed to it, then this makes the top left
-   #  corner of the menu start at the x and y location with respect to the
-   #  self.draw_surface.  If centerScreen is True, then self.centered is set to
-   #  true, regardless of the value passed in
-   #
-   def set_center(self, centered, centeredOnScreen):
-      if centeredOnScreen:
-         self.centeredOnScreen = centeredOnScreen
-         self.centered = False
-      else:
-         self.centeredOnScreen = False
-         self.centered = centered
-      self.update_buttons = True
-
-
-   ## ---[ add_buttons ]--------------------------------------------------------
-   #  @param   self         The class itself, Python standard
-   #  @param   buttonList   List of menu buttons to be added
-   #
-   #  Used to add button(s) to the menu
-   #
-   def add_buttons(self, buttonList):
-      for button in buttonList:
-         self.menu_items.append(self.create_button(button))
-      self.update_buttons = True
-
-
-   ## ---[ remove_buttons ]-----------------------------------------------------
-   #  @param   self         The class itself, Python standard
-   #  @param   indexList    List of indexes to be removed
-   #
-   #  Used to remove button(s) from the menu
-   #
-   def remove_buttons(self, indexList):
-      old_contained_rect = self.contained_rect
-      for index in indexList:
-         if len(self.menu_items) > 1:
-            self.menu_items.pop(index)
-      self.update_buttons = True
-      return old_contained_rect
-
-
-   ## ---[ update_button_locations ]--------------------------------------------
-   #  @param   self         The class itself, Python standard
-   #
-   #  This method is just used to update the location of the buttons when the
-   #  a change is made
-   #
-   def update_button_locations(self):
-      self.position_buttons()
-      self.set_button_images()
-      self.update_buttons = False
-
-
-   ## ---[ create_button ]------------------------------------------------------
-   #  @param   self         The class itself, Python standard
-   #  @param   button_info  A list with the button text, the next state to
-   #                        return, and the image (if applicable)
-   #
-   #  Create the button dictionary for a new button.  Note that this button is
-   #  useless until the set_button_images method is called which is where the
-   #  button images are created and assigned.  The reason it is not done here
-   #  is becuase we need to know the location of the button on the background
-   #  which is not assigned until position_buttons() is called.  Since position
-   #  buttons depends on the width and height of each button, we just calculate
-   #  those here, then we set the location of the buttons via the
-   #  position_buttons() method, then we make the actual images via the
-   #  set_button_images() function
-   #
-   def create_button(self, button_info):
-      # If this button is not an image, set the width and height based on the
-      # text
-      if button_info[2] == None:
-         width, height = self.font.size(button_info[0])
-         button_rect = pygame.Rect((0, 0), (width, height))
-      # Else this button is a graphic button, so create the width and height
-      # based on the image provided
-      else:
-         width, height = button_info[2].get_size()
-         offset = (self.image_highlight_offset, self.image_highlight_offset)
-         new_width  = width  + 2 * offset[0]  # Make room for the highlight on
-         new_height = height + 2 * offset[1]  # all sides
-         button_rect = pygame.Rect((0, 0), (new_width, new_height))
-
-      set_redraw = True     # When the button is created, it needs to be drawn
-      set_selected = False  # When the button is created, it is not selected
-
-      new_button = {'text'    : button_info[0],
-                    'state'   : button_info[1],
-                    'selected': set_selected,
-                    'rect'    : button_rect,
-                    'offset'  : (0, 0),
-                    'redraw'  : set_redraw,
-                    'b_image' : button_info[2],  # base image
-                    's_image' : None,            # image when selected and not
-                    'u_image' : None}            # selected (created in
-                                                 # set_button_images)
-
-      return new_button
-
-
-   ## ---[ set_button_images ]--------------------------------------------------
-   #  @param   self         The class itself, Python standard
-   #
-   #  Create the button images to be displayed - adjusted for the location of
-   #  the button over the background image
-   #
-   def set_button_images(self):
-      for button in self.menu_items:
-         # If this button is not an image, create the selected and unselected
-         # images based on the text
-         if button['b_image'] == None:
-            r = self.font.render
-            width  = button['rect'][2]
-            height = button['rect'][3]
-            rect = pygame.Rect(button['offset'], (width, height))
-
-            # For each of the text button (selected and unselected), create a
-            # surface of the required size (already calculated before), blit
-            # the background image to the surface, then render the text and blit
-            # that text onto the same surface.
-            selected_image = pygame.Surface((width, height), -1)
-            selected_image.blit(self.background, (0, 0), rect)
-            text_image   = r(button['text'], True, self.s_color)
-            selected_image.blit(text_image, (0, 0))
-
-            unselected_image = pygame.Surface((width, height), -1)
-            unselected_image.blit(self.background, (0, 0), rect)
-            text_image   = r(button['text'], True, self.u_color)
-            unselected_image.blit(text_image, (0, 0))
-
-         # Else this button is a graphic button, so create the selected and
-         # unselected images based on the image provided
-         else:
-            orig_width, orig_height = button['b_image'].get_size()
-            new_width  = button['rect'][2]
-            new_height = button['rect'][3]
-            offset = (self.image_highlight_offset, self.image_highlight_offset)
-
-            # Selected image! --------------------------------------------------
-            # Create the surface, fill the surface with the highlight color,
-            # then blit the background image to the surface (inside of the
-            # highlight area), and then blit the actual button base image over
-            # the background
-            selected_image = pygame.Surface((new_width, new_height), -1)
-            selected_image.fill(self.image_highlight_color)
-            rect = pygame.Rect((button['offset'][0] + offset[0],
-                                button['offset'][1] + offset[1]),
-                               (orig_width, orig_height))
-            selected_image.blit(self.background, offset, rect)
-            selected_image.blit(button['b_image'], offset)
-
-            # Unselected image! ------------------------------------------------
-            # Create the surface, blit the background image onto the surface (to
-            # make sure effects go away when the button is no longer selected),
-            # and then blit the actual button base image over the background
-            unselected_image = pygame.Surface((new_width, new_height), -1)
-            rect = pygame.Rect(button['offset'], (new_width, new_height))
-            unselected_image.blit(self.background, (0, 0), rect)
-            unselected_image.blit(button['b_image'], offset)
-
-         button['s_image'] = selected_image
-         button['u_image'] = unselected_image
-
-
-   ## ---[ position_buttons ]---------------------------------------------------
-   #  @param   self    The class itself, Python standard
-   #
-   #  Sets the positions for the buttons
-   #
-   def position_buttons(self):
-      width = 0
-      height = 0
-      max_width = 0
-      max_height = 0
-      counter = 0
-      x_loc = self.x
-      y_loc = self.y
-
-      # Get the maximum width and height of the surfaces
-      for button in self.menu_items:
-         width  = button['rect'][2]
-         height = button['rect'][3]
-         max_width = max(width, max_width)
-         max_height = max(height, max_height)
-
-      # Position the button in relation to each other
-      for button in self.menu_items:
-         # Find the offsets for the alignment of the buttons (left, center, or
-         # right
-         # Vertical Alignment
-         if self.alignment['vertical'] == 'top':
-            offset_height = 0
-         elif self.alignment['vertical'] == 'center':
-            offset_height = (max_height - button['rect'][3])/2
-         elif self.alignment['vertical'] == 'bottom':
-            offset_height = (max_height - button['rect'][3])
-         else:
-            offset_height = 0
-            print 'WARNING:  cMenu.position_buttons:  Vertical Alignment '\
-                  '(value: %s) not recognized!  Left alignment will be used'\
-                                                    % self.alignment['vertical']
-
-         # Horizontal Alignment
-         if self.alignment['horizontal'] == 'left':
-            offset_width = 0
-         elif self.alignment['horizontal'] == 'center':
-            offset_width = (max_width - button['rect'][2])/2
-         elif self.alignment['horizontal'] == 'right':
-            offset_width = (max_width - button['rect'][2])
-         else:
-            offset_width = 0
-            print 'WARNING:  cMenu.position_buttons:  Horizontal Alignment '\
-                  '(value: %s) not recognized!  Left alignment will be used'\
-                                                  % self.alignment['horizontal']
-
-         # Move the button location slightly based on the alignment offsets
-         x_loc += offset_width
-         y_loc += offset_height
-
-         # Assign the location of the button
-         button['offset'] = (x_loc, y_loc)
-
-         # Take the alignment offsets away after the button position has been
-         # assigned so that the new button can start fresh again
-         x_loc -= offset_width
-         y_loc -= offset_height
-
-         # Add the width/height to the position based on the orientation of the
-         # menu.  Add in the padding.
-         if self.orientation == 'vertical':
-            y_loc += max_height + self.vertical_padding
-         else:
-            x_loc += max_width + self.horizontal_padding
-         counter += 1
-
-         # If we have reached the self.change_number of buttons, then it is time
-         # to start a new row or column
-         if counter == self.change_number:
-            counter = 0
-            if self.orientation == 'vertical':
-               x_loc += max_width + self.horizontal_padding
-               y_loc = self.y
-            else:
-               y_loc += max_height + self.vertical_padding
-               x_loc = self.x
-
-      # Find the smallest Rect that will contain all of the buttons
-      self.contained_rect = self.menu_items[0]['rect'].move(button['offset'])
-      for button in self.menu_items:
-         temp_rect = button['rect'].move(button['offset'])
-         self.contained_rect.union_ip(temp_rect)
-
-      # We shift the buttons around on the screen if they are supposed to be
-      # centered (on the surface itself or at (x, y).  We do it here instead of
-      # at the beginning of this function becuase we need to know what the
-      # self.contained_rect is to know the correct amount to shift them.
-      if self.centeredOnScreen:
-         shift_x = self.x - (self.draw_surface.get_rect()[2] -
-                             self.contained_rect[2]) / 2
-         shift_y = self.y - (self.draw_surface.get_rect()[3] -
-                             self.contained_rect[3]) / 2
-      elif self.centered:
-         shift_x = (self.contained_rect[2]) / 2
-         shift_y = (self.contained_rect[3]) / 2
-      if self.centeredOnScreen or self.centered:
-         # Move the buttons to make them centered
-         for button in self.menu_items:
-            button['offset'] = (button['offset'][0] - shift_x,
-                                button['offset'][1] - shift_y)
-
-         # Re-find the smallest Rect that will contain all of the buttons
-         self.contained_rect = self.menu_items[0]['rect'].move(button['offset'])
-         for button in self.menu_items:
-            temp_rect = button['rect'].move(button['offset'])
-            self.contained_rect.union_ip(temp_rect)
-
-
-   ## ---[ update ]-------------------------------------------------------------
-   #  @param   self      The class itself, Python standard
-   #  @param   e         The last event
-   #  @param   c_state   The current state of the game from where this is called
-   #  @return            A list of rectangles of where the screen changed
-   #  @return            The new state for the game
-   #
-   #  Update the menu surface, redraw it to the stored surface self.draw_surface
-   #
-   def update(self, e, c_state):
-      redraw_full_menu = False
-
-      self.selection_prev = self.selection
-      o = self.orientation
-      s = self.selection
-      n = self.change_number
-
-      if e.type == pygame.MOUSEBUTTONDOWN:
-        rectangle_list = self.draw_buttons()
-        mouseSelected = 0
-        for r in rectangle_list:
-            mouseSelected  += 1
-            mouseX = self.mousePos[0]
-            mouseY = self.mousePos[1]
-            if r[0] < mouseX and r[0]+r[2] > mouseX and r[1] < mouseY and r[1]+r[3] > mouseY:
-                self.selection = mouseSelected - 1
-      elif e.type == pygame.MOUSEMOTION:
-        self.mousePos = e.pos # seems to always fire once, so we're set
-      elif e.type == pygame.MOUSEBUTTONUP:
-        rectangle_list = self.draw_buttons()
-        mouseSelected = 0
-        for r in rectangle_list:
-            mouseSelected  += 1
-            mouseX = self.mousePos[0]
-            mouseY = self.mousePos[1]
-            if r[0] < mouseX and r[0]+r[2] > mouseX and r[1] < mouseY and r[1]+r[3] > mouseY:
-                if self.selection == mouseSelected - 1:
-                    return [None], self.menu_items[self.selection]['state']
-                else:
-                    return rectangle_list, c_state
-        
-        
-      elif e.type == pygame.KEYDOWN:    
-          if e.key == pygame.K_DOWN:
-             if (o == 'vertical') and ((s + 1) % n != 0):
-                self.selection += 1
-             elif o == 'horizontal':
-                self.selection += n
-          elif e.key == pygame.K_UP:
-             if (o == 'vertical') and ((s) % n != 0):
-                self.selection -= 1
-             elif o == 'horizontal':
-                self.selection -= n
-          elif e.key == pygame.K_RIGHT:
-             if o == 'vertical':
-                self.selection += n
-             elif (o == 'horizontal') and ((s + 1) % n != 0):
-                self.selection += 1
-          elif e.key == pygame.K_LEFT:
-             if o == 'vertical':
-                self.selection -= n
-             elif (o == 'horizontal') and ((s) % n != 0):
-                self.selection -= 1
-          elif e.key == pygame.K_r:
-             original_contained_rect = self.remove_buttons([s])
-             if self.selection -1 >= 0:
-                self.selection -= 1
-                self.selection_prev -= 1
-             redraw_full_menu = True
-          elif e.key == pygame.K_RETURN:
-             return [None], self.menu_items[s]['state']
-
-          if self.selection >= len(self.menu_items) or self.selection < 0:
-             self.selection = self.selection_prev
-
-      # If this is an EVENT_CHANGE_STATE, then this is the first time that we
-      # have entered this menu, so lets set it up
-      if e.type == EVENT_CHANGE_STATE:
-         #print "CHANGE STATE, init=", self.initialSelection
-         self.selection = self.initialSelection
-         self.menu_items[self.selection_prev]['selected'] = False
-         self.menu_items[self.selection]['selected'] = True
-         rectangle_list = self.draw_buttons()
-         if self.refresh_whole_surface_on_load:
-            print "did whole refresh"
-            rectangle_list = pygame.Rect((0, 0), self.draw_surface.get_size())
-            return [rectangle_list], c_state
-         else:           
-            return [self.contained_rect], c_state
-
-      elif redraw_full_menu:
-         print "did full menu"
-         self.menu_items[self.selection_prev]['selected'] = False
-         self.menu_items[self.selection]['selected'] = True
-         self.redraw_all()
-         rectangle_list = self.draw_buttons(original_contained_rect)
-         return rectangle_list, c_state
-
-      elif self.selection != self.selection_prev:
-         self.menu_items[self.selection_prev]['selected'] = False
-         self.menu_items[self.selection]['selected'] = True
-         rectangle_list = self.draw_buttons()
-         return rectangle_list, c_state
-
-      # If no updates were made, return defaults
-      return [None], c_state
-
-
-   ## ---[ draw_buttons ]-------------------------------------------------------
-   #  @param   self          The class itself, Python standard
-   #  @param   redraw_rect   If this pygame.Rect is provided, then the entire
-   #                         background will be drawn to the surface in the area
-   #                         of this rect before the buttons are drawn
-   #  @return                A list of rectangles of where the screen changed
-   #
-   #  Draw the buttons to the self.draw_surface and return a list of Rect's that
-   #  indicate where on the surface changes were made
-   #
-   def draw_buttons(self, redraw_rect = None):
-      rect_list = []
-
-      # If buttons have been changed (added button(s), deleted button(s),
-      # changed colors, etc, etc), then we need to update the button locations
-      # and images
-      if self.update_buttons:
-         self.update_button_locations()
-
-         # Print a warning if the buttons are partially/completely off the
-         # surface
-         if not self.draw_surface.get_rect().contains(self.contained_rect):
-            print 'WARNING:  cMenu.draw_buttons:  Some buttons are partially '\
-                  'or completely off of the self.draw_surface!'
-
-      # If a rect was provided, redraw the background surface to the area of the
-      # rect before we draw the buttons
-      if redraw_rect != None:
-         offset = (redraw_rect[0], redraw_rect[1])
-         drawn_rect = self.draw_surface.blit(self.background,
-                                             offset,
-                                             redraw_rect)
-         rect_list.append(drawn_rect)
-
-      # Cycle through the buttons, only draw the ones that need to be redrawn
-      for button in self.menu_items:
-         if button['redraw']:
-            if button['selected']:
-               image = button['s_image']
-            else:
-               image = button['u_image']
-
-            drawn_rect = self.draw_surface.blit(image,
-                                                button['offset'],
-                                                button['rect'])
-            rect_list.append(drawn_rect)
-
-      return rect_list
-
-
-#---[ END OF FILE ]-------------------------------------------------------------

ProduceDefendWin.py

 from pygame.color import THECOLORS
 from pygame.locals import *
 
-from Third_Party_Sources import pyconsole
-from Third_Party_Sources import pyconsole_syntax, \
-    re_add, console_add, re_function, console_func
-from pyconsole_functions import *
+from Source import pyconsole
+from Source import pyconsole_syntax
+#    re_add, console_add, re_function, console_func
+from Source import pyconsole_functions, fps, cheat, line
 
-import enviro # my global game environment
-import Noise # the noise module, all the music and effects stuff go here
+from Source import enviro # my global game environment
+from Source import Noise # the noise module, all the music and effects stuff go here
 
-from YourStuff import *
-from AttackerStuff import *
-from Fighter import Fighter
-from FloatingSprite import FloatingSprite
-from Harvester import Harvester
-from MotherShip import MotherShip
-from GasFactory import GasFactory
-from Laser import Laser
-from Shields import Shields
+from Source import YourStuff
+from Source import AttackerStuff
+from Source import Fighter
+from Source import FloatingSprite
+from Source import Harvester
+from Source import MotherShip
+from Source import GasFactory
+from Source import Laser
+from Source import Shields
 from Source import Bomb
-from DeadMother import DeadMother
-from Generator import Generator
+from Source import DeadMother
+from Source import Generator
 
-import Title
-import mainline
+from Source import Title
+from Source import mainline
 
 def main(continuing, firstRun):
     
     newGen = Generator(5,enviro.gasGauges, "generator0.png")
     newGen.refill(enviro.gasGauges)
     enviro.generators.append(newGen)
-
-    dramaBackground = pygame.image.load("dramaBackground.png")
+    
+    
+    dramaBackground = \
+        pygame.image.load(os.path.join(enviro.artPathHack,'dramaBackground.png'))    
     
     # Yes, I could have made even more of these
     demoStep = 0
         (401,0,328,449), # the drama area - gets refreshed every cycle, required for console
         functions={"FPS":fps,"cheat":cheat,"credits":credits, "line":line}, # functions for the console
         key_calls={"d":sys.exit}, # ctrl+d calls sys.exit()
-        syntax={re_add:console_add, re_function:console_func}
+        syntax={pyconsole_syntax.re_add:pyconsole_syntax.console_add, pyconsole_syntax.re_function:pyconsole_syntax.console_func}
         )
     
     mousePos = [0,0]

Source/AttackerStuff.py

+# coding=UTF8
+# Copyright © 2010 Rob Leachman
+# Please see the file COPYING in this distribution for license information
+import pygame
+import random
+from pygame.color import THECOLORS
+
+import enviro # my global game environment
+from FloatingSprite import FloatingSprite
+from Fighter import Fighter
+
+
+    
+# Need a sprite to represent the shield, so we can test collision
+class ShieldSprite(pygame.sprite.Sprite):
+
+    def __init__(self, image_file, left, top, width, height):
+        pygame.sprite.Sprite.__init__(self)
+        
+        self.image = pygame.image.load(image_file)
+        self.rect = self.image.get_rect()
+
+        self.rect.left = left
+        self.rect.top = top
+        self.rect.width = width
+        self.rect.height = height
+        
+    # just for testing, this thing isn't drawn ever
+    def paint(self):
+        enviro.screen.blit(self.image, self.rect)
 # Please see the file COPYING in this distribution for license information
 import pygame
 import random
+import os
+
 from pygame.color import THECOLORS
 import sys # for testing
 
     
 if __name__ == '__main__':
     
+   
     pygame.init()
     enviro.screen = pygame.display.set_mode([800,600])
     myClock = pygame.time.Clock()
     
 # stuff needed for init:
 
-        
-    dramaBackground = pygame.image.load("dramaBackground.png") 
+    artPath = os.path.join('..','art')
+    dramaBackground = \
+        pygame.image.load(os.path.join(artPath,'dramaBackground.png'))
+
  
     b = Bomb("bomb.png",600,200,600,430, 0)
     
                 if (pygame.key.get_mods() == 1024): # command key
                     if (event.key == 113):   # q
                         sys.exit()
-                    
-            if event.type == pygame.KEYDOWN: 
-                if (pygame.key.get_mods() == 1024): # command key
-                    if (event.key == 113):   # q
-                        sys.exit()                
+               
                 if event.key == pygame.K_h:
                     print "hit"
 

Source/DeadFighter.py

+# coding=UTF8
+# Copyright © 2010 Rob Leachman
+# Please see the file COPYING in this distribution for license information
+import pygame
+import sys # for testing
+from pygame.color import THECOLORS
+
+import enviro # my global game environment
+from FloatingSprite import FloatingSprite
+from Fighter import Fighter # for testing...
+
+class DeadFighter:
+    def __init__(self, filenamePrefix):
+        self.deathImage = []
+        self.deathFrameCount = 0
+        self.top = 0
+        self.left = 0
+        
+        for i in range (1,9):
+            deathFile = filenamePrefix + str(i) + ".png"
+            deathImage = pygame.image.load(deathFile)
+            deathImage.convert()
+            self.deathImage.append(deathImage)
+            
+    def __str__(self):
+        msg =  "  Dead fighter X:" + str(self.top) + " Y:" + str(self.left)
+        msg += "    frameCount=" + str(self.deathFrameCount)
+        return msg               
+            
+    def paint(self):
+        #deathFrame = self.deathFrameCount / 10
+        deathFrame = self.deathFrameCount
+        # maybe we got called too many times... if so, let's not crash
+        if deathFrame >= len(self.deathImage):
+            return True
+        enviro.screen.blit(self.deathImage[deathFrame], [self.top, self.left])
+        self.deathFrameCount += 1
+        #nextFrame = self.deathFrameCount / 10
+        nextFrame = self.deathFrameCount
+        # the intention is to signal when we're done, and shouldn't be called again...
+        if nextFrame >= len(self.deathImage):
+            return True
+        return False
+    
+    def setLocation(self,fighterTop, fighterLeft):
+        self.top = fighterTop - 5
+        self.left = fighterLeft - 5
+        
+if __name__ == '__main__':
+    
+    pygame.init()
+    enviro.screen = pygame.display.set_mode([800,600])
+    myClock = pygame.time.Clock()
+    
+    delay=100
+    interval=50
+    pygame.key.set_repeat(delay,interval)
+    
+# init the test:
+    dramaBackground = pygame.image.load("dramaBackground.png") 
+    
+    fighterFile = "fighter.png"
+    fighterFleet = []
+    for i in range(0,9):
+        fighter = Fighter(fighterFile, len(fighterFleet))
+        fighterFleet.append(fighter)    
+    
+   #d = DeadFighter("deadFighter10PxA-", 450, 100)
+    
+    needDeadFighter = True
+    while True:
+        enviro.screen.blit(dramaBackground, [400,0,330,449])          
+    
+        for event in pygame.event.get():
+            if event.type == pygame.QUIT:
+                sys.exit()
+            if event.type == pygame.KEYDOWN:
+                if (pygame.key.get_mods() == 1024): # command key
+                    if (event.key == 113):   # q
+                        sys.exit()
+                                   
+                if event.key == pygame.K_SPACE:
+                    print "do it"
+                    if d.paint():
+                        print "that's all folks!"
+                        
+                        
+        # A loop like we use to get fighters launched:                
+        for i in range (0,9):
+            if fighterFleet[i].state == "launching":
+                if fighterFleet[i].move() == 0:
+                    print "fighter ",i, "READY TO SHOOT!"
+                    fighterFleet[i].state = "shoot"
+                    
+            if fighterFleet[i].state == "shoot" or fighterFleet[i].state == "shooting":
+                fighterFleet[i].paint()
+            if not (fighterFleet[i].state == "hit" or fighterFleet[i].state == "dead"):
+                fighterFleet[i].smokeCheck() 
+                
+            if fighterFleet[0].state == "shoot" or fighterFleet[0].state == "hit":
+                fighterFleet[0].state = "hit"
+                if needDeadFighter:
+                    needDeadFighter = False
+                    d = DeadFighter("deadFighter10PxA-")
+                    d.setLocation(fighterFleet[0].rect.left, fighterFleet[0].rect.top)
+                else:
+                    if d.paint():
+                        fighterFleet[0].state = "dead"
+                
+
+        myClock.tick(30)
+        pygame.display.flip()  
+    

Source/DeadMother.py

+# coding=UTF8
+# Copyright © 2010 Rob Leachman
+# Please see the file COPYING in this distribution for license information
+import pygame
+import sys # for testing
+from pygame.color import THECOLORS
+
+import enviro # my global game environment
+import Noise
+from Fighter import Fighter # for testing...
+from MotherShip import MotherShip # for testing...
+
+class DeadMother:
+    def __init__(self, filenamePrefix):
+        self.deathImage = []
+        self.deathFrameCount = 0
+        self.top = 0
+        self.left = 0
+        
+        for i in range (1,9):
+            deathFile = filenamePrefix + str(i) + ".png"
+            deathImage = pygame.image.load(deathFile)
+            deathImage.convert()
+            self.deathImage.append(deathImage)
+            
+    def __str__(self):
+        msg =  "  Dead mother X:" + str(self.top) + " Y:" + str(self.left)
+        msg += "    frameCount=" + str(self.deathFrameCount)
+        return msg               
+            
+    def paint(self):
+        deathFrame = self.deathFrameCount 
+        #print "  deathframe=", deathFrame
+        # maybe we got called too many times... if so, let's not crash
+        if deathFrame >= len(self.deathImage):
+            return False
+        enviro.screen.blit(self.deathImage[deathFrame], [self.left, self.top])
+        self.deathFrameCount += 1
+        nextFrame = self.deathFrameCount 
+        # the intention is to signal when we're done, and shouldn't be called again...
+        if nextFrame >= len(self.deathImage):
+            return False
+        return True
+    
+    def setLocation(self,fighterLeft, fighterTop):
+        self.top = fighterTop
+        self.left = fighterLeft - 20
+        self.deathFrameCount = 0
+        
+if __name__ == '__main__':
+    
+    print "TEST: dead mothership"
+    pygame.init()
+    enviro.screen = pygame.display.set_mode([800,600])
+    myClock = pygame.time.Clock()
+    
+    delay=100
+    interval=50
+    pygame.key.set_repeat(delay,interval)
+    
+# stuff needed for init: 
+    enviro.soundEffect = []
+    enviro.soundEffect.append(Noise.load_sound('63068__radian__odd_trimmedAndReworked.wav'))
+    Noise.MOTHER_ENTERS = len(enviro.soundEffect) - 1 
+    
+    dramaBackground = pygame.image.load("dramaBackground.png")   
+    fighterFile = "fighter.png"
+
+    fighterFleet = []
+
+    for i in range(0,9):
+        fighter = Fighter(fighterFile, len(fighterFleet))
+        fighterFleet.append(fighter)
+
+    motherShipFile = "mother.png"
+    bomb_image = "bomb.png"
+    
+    enviro.MOTHER_START_WAIT = 10
+    enviro.MOTHER_RESTART_WAIT = 10  
+    enviro.MOTHER_MIN_RESTART = 10    
+
+    m = MotherShip(motherShipFile, fighterFleet, bomb_image)
+    
+    bombList = pygame.sprite.Group()
+    fightersActive = 1
+    
+    needDeadMother = False
+    sheDied = False
+    dead = DeadMother("motherDead-") 
+    while True:
+    
+        for event in pygame.event.get():
+            if event.type == pygame.QUIT:
+                sys.exit()
+            if (event.type == pygame.KEYDOWN):
+                if (pygame.key.get_mods() == 1024): # command key
+                    if (event.key == 113):   # q
+                        sys.exit()
+                        
+                if event.key == pygame.K_SPACE:                    
+                    m.state = "Hit"
+                    needDeadMother = True
+
+                    
+        enviro.screen.blit(dramaBackground, [400,0,330,449])                    
+                    
+        theMother = m.paint(bombList, fightersActive) # paint her progress, and get any emitted bombs
+        print m.rect.left
+        if needDeadMother:
+
+            dead.setLocation(m.diedAt,0) 
+            needDeadMother = False
+            sheDied = True
+            
+        if sheDied:
+            dead.paint()
+        
+        myClock.tick(30)
+        pygame.display.flip()      
+ 
+    

Source/Fighter.py

+# coding=UTF8
+# Copyright © 2010 Rob Leachman
+# Please see the file COPYING in this distribution for license information
+import pygame
+import random
+import sys # for testing
+from pygame.color import THECOLORS
+
+import enviro # my global game environment
+from FloatingSprite import FloatingSprite
+
+class Fighter(FloatingSprite):
+    def __init__(self, image_file, fleetCount):
+        
+        FloatingSprite.__init__(self, image_file)
+        self.maxBombs = 1
+        self.bewareILive(fleetCount)
+        self.bombsOut = 0
+        
+##        smoke_surface = pygame.surface.Surface([10,10])
+##        smokeArray = pygame.surfarray.pixels2d(smoke_surface)
+##        smokeArray[0][0] = 255
+##        smokeArray[9][9] = 255
+##        smokeOut = pygame.surfarray.make_surface(smokeArray)
+##        smokeOut.convert()
+##        
+##        enviro.screen.blit(smokeOut, [10,10])
+        
+    def __str__(self):
+        msg = FloatingSprite.__str__(self)
+        msg +=  "  Fighter state:" + str(self.state)
+        return msg         
+        
+    def bewareILive(self, fleetCount):
+        self.trueLeft = 552 # 552 will start right below mother
+        self.trueTop = 21 # 21 will start right below mother
+        
+        if fleetCount > -1:
+            self.fleetPosition = fleetCount        
+        
+##        fighterSpots = [ [560,91], [440,91], [500,91], [620,91], [680,91],
+##                            [470,71], [650,71], [590,71], [530,71]]
+        fighterSpots = ( (500,91), (680,91), (440,91), (620,91), (560,91),
+                            (470,71), (650,71), (590,71), (530,71) )                            
+        self.destX = fighterSpots[self.fleetPosition ][0]
+        self.destY = fighterSpots[self.fleetPosition ][1]
+        
+        self.calcSpeed(enviro.FIGHTER_LAUNCH_SPEED) # maybe 30.0 good for prod?
+        
+        self.state = "launching"
+        self.fullHealth = 150
+        self.life = self.fullHealth
+        
+    def smokeCheck(self):
+        top = self.rect.top+9
+        left = self.rect.left
+        if self.life > 0:
+            lifebar = (float(self.life)/self.fullHealth) * 20
+            pygame.draw.lines(enviro.screen, [255,255,255], False, [ [left,top],[left+lifebar,top]], 1)
+        
+    def test(self, targetSprite, nextState, laserPower):
+        # see if this fighter is it by the targeting sprite, if so set the next state
+
+        hitMe = False
+        killedMe = False
+        tester = pygame.sprite.Group()
+        tester.add(targetSprite)
+        if pygame.sprite.spritecollideany(self, tester):
+            #print "FIGHTER HIT! Laser power=", enviro.LASER_POWER
+            hitMe = True
+            self.life = self.life - laserPower
+            if self.life < 1:
+               self.state = nextState
+               killedMe = True
+        return [hitMe, killedMe]
+    
+    def takeAShot(self):
+        randShot = random.randint(1,10)
+        if randShot == 1:
+            if self.bombsOut < self.maxBombs:
+               self.state = "shooting"
+               self.bombsOut = self.bombsOut + 1
+               return True
+        return False
+    
+    def shotExpired(self):
+        self.bombsOut = self.bombsOut - 1
+        if self.state <> "hit" and self.state <> "launching":
+            self.state = "shoot"
+            
+        
+if __name__ == '__main__':
+    
+    print "TEST: fighter"
+    pygame.init()
+    enviro.screen = pygame.display.set_mode([800,600])
+    myClock = pygame.time.Clock()
+    
+# stuff needed for init: 
+    dramaBackground = pygame.image.load("dramaBackground.png")   
+    fighterFile = "fighter.png"
+
+    fighterFleet = []
+
+    for i in range(0,9):
+        fighter = Fighter(fighterFile, len(fighterFleet))
+        fighterFleet.append(fighter)
+       
+
+#    fightersActive = 1
+            
+    while True:
+
+
+    
+        for event in pygame.event.get():
+            if event.type == pygame.QUIT:
+                sys.exit()
+        if (event.type == pygame.KEYDOWN):
+            if (pygame.key.get_mods() == 1024): # command key
+                if (event.key == 113):   # q
+                    sys.exit()
+            if event.key == pygame.K_x:                    
+                fighterFleet[0].life = fighterFleet[0].life - 1
+                    
+            if event.key == pygame.K_SPACE:                    
+                fighterFleet[0].smokeCheck()
+                    
+        enviro.screen.blit(dramaBackground, [400,0,330,449])                    
+
+        for i in range (0,9):
+            if fighterFleet[i].state == "launching":
+                if fighterFleet[i].move() == 0:
+                    print "fighter ",i, "READY TO SHOOT!"
+                    fighterFleet[i].state = "shoot"
+                    
+            if fighterFleet[i].state == "shoot" or fighterFleet[i].state == "shooting":
+                fighterFleet[i].paint()
+            if i == 0:
+                fighterFleet[i].smokeCheck()
+        
+        myClock.tick(30)
+        pygame.display.flip()                 

Source/FloatingSprite.py

+# coding=UTF8
+# Copyright © 2010 Rob Leachman
+# Please see the file COPYING in this distribution for license information
+import pygame
+import os
+from pygame.color import THECOLORS
+import enviro # my global game environment
+
+
+# Floating sprites work properly when speed is a float
+# (compare with pygame sprites which don't move properly if not int)
+class FloatingSprite(pygame.sprite.Sprite):
+    def __init__(self, image_file):
+        
+        pygame.sprite.Sprite.__init__(self)
+        self.theImage = image_file
+        self.image = pygame.image.load(os.path.join(enviro.artPathHack,self.theImage))
+        self.rect = self.image.get_rect()
+        
+        self.trueLeft = 0
+        self.trueTop = 0
+        
+        self.destX = 0
+        self.destY = 0
+        
+    def calcSpeed(self, steps):
+
+        speedX = (self.destX - self.trueLeft) / steps
+        speedY = (self.destY - self.trueTop) / steps
+        self.speed = [speedX, speedY]
+        
+    def __str__(self):
+        msg =  "FloatingSprite at x: %.1f y: %.1f" % (self.trueLeft, self.trueTop)
+        msg += "  dest x: " + str(self.destX) + " y:" + str(self.destY)
+        msg += "  speed x: %.2f y: %.2f" % (self.speed[0], self.speed[1])
+        return msg
+    
+    def paint(self):
+        self.rect.left = self.trueLeft
+        self.rect.top = self.trueTop
+                
+        enviro.screen.blit(self.image, self.rect) 
+        
+    def move(self):
+        if self.speed == [0,0]:
+            #print "Floating sprite at destination!"
+            return 0
+        
+        #pygame.draw.rect(enviro.screen, THECOLORS["black"], [self.rect.left,self.rect.top,self.rect.width,self.rect.height], 0)
+        self.trueLeft = self.trueLeft + self.speed[0]
+        self.trueTop = self.trueTop + self.speed[1]
+
+        self.rect.left = self.trueLeft
+        self.rect.top = self.trueTop
+        #print "Plotting at x=", self.rect.left, "y=",self.rect.top
+        #self.rect = self.rect.move(self.speed) # won't work with float!
+        
+        if self.speed[0] > 0:
+            #print "moving right ", self.rect.left, self.speed[0]
+            if self.trueLeft + self.speed[0] > self.destX:
+                self.speed[0] = self.destX - self.trueLeft
+        elif self.speed[0] < 0:
+            #print "moving left", self.rect.left, self.speed[0]
+            if self.trueLeft + self.speed[0] < self.destX:
+                self.speed[0] = self.destX - self.trueLeft
+                
+        if self.speed[1] > 0:
+            #print "moving down ", self.rect.top, self.speed[1]
+            if self.trueTop + self.speed[1] > self.destY:              
+                self.speed[1] = self.destY - self.trueTop
+        elif self.speed[1] < 0:
+            #print "moving up ", self.rect.top, self.speed[1]            
+            if self.trueTop + self.speed[1] < self.destY:               
+                self.speed[1] = self.destY - self.trueTop
+            
+        #print "float X:", self.rect.left, "Y:",self.rect.top, "destX:",self.destX, "destY:", self.destY, "speedX", self.speed[0], "speedY", self.speed[1]
+        enviro.screen.blit(self.image, self.rect)
+        if self.rect.left == self.destX:
+            self.trueLeft = self.destX
+            self.speed = [0,self.speed[1]]
+        if self.rect.top == self.destY:
+            self.trueTop = self.destY
+            self.speed = [self.speed[0], 0]
+        return 1
+            
+    def erase(self):
+        pygame.draw.rect(enviro.screen, THECOLORS["black"], [self.rect.left,self.rect.top,self.rect.width,self.rect.height], 0)
+        
+        
+        
+        

Source/GasFactory.py

+# coding=UTF8
+# Copyright © 2010 Rob Leachman
+# Please see the file COPYING in this distribution for license information
+#not sure all of these are needed...
+import pygame, random
+from pygame.color import THECOLORS
+
+import enviro # my global game environment
+import Noise
+    
+from AttackerStuff import ShieldSprite
+from Harvester import Harvester
+
+
+class GasFactory(pygame.sprite.Sprite):
+    def __init__(self, image_file, left, top, 
+                 needStuff0_img, needMoney0_img, needMoney1_img, needMoney2_img, needStuff_img, built_img):
+        
+        pygame.sprite.Sprite.__init__(self)
+        self.theImage = image_file
+        self.image = pygame.image.load(self.theImage)
+        self.rect = self.image.get_rect()
+        self.rect.left = left
+        self.rect.top = top
+        self.location = 8
+        
+        self.need0_white = pygame.image.load(needStuff0_img)
+        self.need0 = pygame.image.load(needMoney0_img)
+        self.need1 = pygame.image.load(needMoney1_img)
+        self.need2 = pygame.image.load(needMoney2_img)
+        self.needStuffToBuild = pygame.image.load(needStuff_img)
+        self.gasFactoryBuilt = pygame.image.load(built_img)
+        self.needCount = 0
+        self.needXOffset = 20
+        self.needYOffset = 60
+        self.needType = "none"         
+        self.built = 0
+        self.padPainted = False
+##        if self.built == 1:
+##            self.build()
+
+    def paintPad(self):
+        enviro.screen.blit(self.image, self.rect)
+        self.padPainted = True
+        
+    def build(self):
+        self.built = 1
+        enviro.gasFactoryIsBuilt = True
+        enviro.screen.blit(self.gasFactoryBuilt, self.rect)
+        
+    def needMoney(self):
+        #print "NEED MONEY"
+        paintX = self.rect.left
+        paintY = self.rect.top
+        enviro.screen.blit(self.need1, [paintX+self.needXOffset, paintY+self.needYOffset])
+        self.needType = "credits"
+        self.needCount = 100
+        
+    def needStuff(self):
+        #print "NEED STUFF"
+        paintX = self.rect.left
+        paintY = self.rect.top
+        self.needType = "stuff"
+        enviro.screen.blit(self.needStuffToBuild, [paintX+self.needXOffset, paintY+self.needYOffset])
+        self.needCount = 100
+        
+    def clearNeeds(self):
+
+        paintX = self.rect.left
+        paintY = self.rect.top
+        if self.needCount > 0:
+            self.needCount = self.needCount - 1
+            #print "factory need type:", self.needType, " count:", self.needCount
+            if self.needCount == 0:
+                if self.built == 0:
+                   enviro.screen.blit(self.need0, [paintX+self.needXOffset, paintY+self.needYOffset])
+                else:
+                   enviro.screen.blit(self.need0_white, [paintX+self.needXOffset, paintY+self.needYOffset]) 
+            else:
+                needPhase = self.needCount / 10 - 1
+                if needPhase % 2 == 1 and needPhase > 1:
+                    enviro.screen.blit(self.need2, [paintX+self.needXOffset, paintY+self.needYOffset])
+                else:
+                    if self.needType == "credits":
+                        enviro.screen.blit(self.need1, [paintX+self.needXOffset, paintY+self.needYOffset])
+                    elif self.needType == "stuff":
+                        enviro.screen.blit(self.needStuffToBuild, [paintX+self.needXOffset, paintY+self.needYOffset])                  
+        
+    def paint(self,fuelPile):
+        enviro.screen.blit(self.image, self.rect)
+        self.myFuelPile = fuelPile
+
+        self.myFuelPile[self.fuelBay[1]].paint([146,150])
+        self.myFuelPile[self.fuelBay[0]].paint([173,150])
+        self.myFuelPile[self.fuelBay[2]].paint([200,150])
+        
+    def repaintBays(self):
+        #enviro.screen.blit(self.image, self.rect)
+
+        self.myFuelPile[self.fuelBay[1]].paint([146,150])
+        self.myFuelPile[self.fuelBay[0]].paint([173,150])
+        self.myFuelPile[self.fuelBay[2]].paint([200,150])
+    
+    def stashStuff(self):
+        for i in range(0,3):
+            if self.fuelBay[i] == 0:
+                self.fuelBay[i] = 10
+                self.repaintBays()
+                return True
+        return False
+    
+    def getStuff(self):
+        for i in range(0,3):
+            if self.fuelBay[i] == 10:
+                self.fuelBay[i] = 0
+                self.repaintBays()
+                return True
+        return False
+            

Source/Generator.py

+# coding=UTF8
+# Copyright © 2010 Rob Leachman
+# Please see the file COPYING in this distribution for license information
+import pygame, random
+from pygame.color import THECOLORS
+
+import enviro # my global game environment
+import Noise
+
+from YourStuff import *
+
+    
+
+  
+class Generator:
+    def __init__(self, location, gauges, image_file):
+        
+        self.location = location
+        self.isReady = False
+        self.isTargeted = False
+        self.processCount = 0
+        self.cycleCount = 0
+        self.gas = 60
+        self.gasPromised = False
+                
+        coordX = enviro.getLocationX(location)
+        coordY = enviro.getLocationY(location)
+        
+        #image = pygame.image.load(image_file).convert()
+        image = pygame.image.load(image_file)
+        #print "Can't convert, it loses alpha..."
+        enviro.screen.blit(image,[coordX,coordY])
+        
+        #pygame.draw.rect(enviro.screen, [0,0,200], [coordX+5,coordY+5,80,80],0) # generator
+        #pygame.draw.rect(enviro.screen, THECOLORS["gray63"], [coordX+25,coordY+20,40,40],0) # result box
+        gauges[0].paint([coordX+15,coordY+10])
+        
+        self.genImage = []
+        for i in range (1,11):
+            genImg = pygame.image.load("generating-"+str(i)+".png")
+            self.genImage.append(genImg)
+            
+        
+    def __str__(self):
+        msg = "GENERATOR DEBUGGER:" + "\n"
+        msg += " Location: " + str(self.location) + "\n"
+        msg += " Ready: " + str(self.isReady) + "\n"
+        msg += " Targeted: " + str(self.isTargeted) + "\n"
+        msg += " Process count: "  + str(self.processCount) + "\n"
+        return msg
+    
+    def checkTankEmpty(self):
+        if self.gas == 0 and self.gasPromised == False:
+            return True
+        return False
+                
+    def produce(self, gauges):
+
+        coordX = enviro.getLocationX(self.location)
+        coordY = enviro.getLocationY(self.location)
+                
+        if (self.processCount == 10):
+            self.isReady = True
+            self.processCount = 0
+            pygame.draw.rect(enviro.screen, [0,255,0], 
+               [(coordX + 30), (coordY + 25),30,30],0) # the result
+    
+        hasGas = False
+        if (self.isReady == False):
+            if self.cycleCount == 0 and self.processCount == 0:
+                if self.gas > 0:
+                    self.gas = self.gas - 20
+                    hasGas = True
+                
+                    if self.gas == 40:
+                        gauges[1].paint([coordX+15,coordY+10])
+                    if self.gas == 20:
+                        gauges[2].paint([coordX+15,coordY+10])
+                    if self.gas == 0:
+                        gauges[3].paint([coordX+15,coordY+10])
+            else:
+                hasGas = True
+                
+            if hasGas:
+                self.cycleCount = self.cycleCount + 1
+                if self.cycleCount > enviro.PRODUCTION_TIMER:
+                    self.cycleCount = 0
+                    self.processCount = self.processCount + 1
+                    enviro.screen.blit(self.genImage[self.processCount-1], [coordX+5,coordY+15])
+##                    pygame.draw.rect(enviro.screen, [255,255,0], 
+##                        [(coordX + 4) + self.processCount * 7,(coordY + 70),5,5],0)
+                        
+    def refill(self, gasGauges):
+        coordX = enviro.getLocationX(self.location)
+        coordY = enviro.getLocationY(self.location)
+                
+        self.gas = 60
+        gasGauges[0].paint([coordX+15,coordY+10])
+        self.gasPromised = False
+                    
+    def harvestStuff(self):
+        if self.isReady == True:
+            self.isReady = False
+            self.isTargeted = False
+            self.processCount = 0
+            self.cycleCount = 0
+            coordX = enviro.getLocationX( self.location )
+            coordY = enviro.getLocationY( self.location )
+            #pygame.draw.rect(enviro.screen, [100,100,0], [coordX+25,coordY+10,40,40],0) # result box
+            pygame.draw.rect(enviro.screen, [0,0,200], 
+                        [ coordX + 5, coordY + 70,80,5],0) # clear the progress meter
+            pygame.draw.rect(enviro.screen, [0,0,200], 
+                   [(coordX + 30), (coordY + 25),30,30],0) # the result
+            return True
+        return False
+    
+    
+if __name__ == '__main__':
+    
+    pygame.init()
+    enviro.screen = pygame.display.set_mode([800,600])
+    myClock = pygame.time.Clock()
+    
+    delay=100
+    interval=50
+    pygame.key.set_repeat(delay,interval)
+        
+    
+# stuff needed for init:
+    gasFiles = ["gas-full.png", "gas-twoThirds.png", "gas-oneThird.png", "gas-empty.png"]
+    for gasFile in gasFiles:
+        gas = GasGauge(gasFile)
+        enviro.gasGauges.append(gas)
+
+
+    newGen = Generator(5,enviro.gasGauges, "generator0.png")
+    newGen.refill(enviro.gasGauges)
+    
+    enviro.PRODUCTION_TIMER = 1
+            
+    while True:
+        
+        for event in pygame.event.get():
+            if event.type == pygame.QUIT:
+                sys.exit()
+            if (event.type == pygame.KEYDOWN):
+                if (pygame.key.get_mods() == 1024): # command key
+                    if (event.key == 113):   # q
+                        sys.exit()
+                        
+                if event.key == pygame.K_SPACE:
+                    print "Generate!"
+                    newGen.produce(enviro.gasGauges)
+                   
+                    
+        myClock.tick(30)
+        pygame.display.flip()       
+              

Source/Harvester.py

+# coding=UTF8
+# Copyright © 2010 Rob Leachman
+# Please see the file COPYING in this distribution for license information
+import pygame, random
+from pygame.color import THECOLORS
+
+import enviro # my global game environment
+import Noise
+
+import sys    # for testing
+    
+from AttackerStuff import ShieldSprite
+
+class Harvester:
+    def __init__(self, count):
+        
+        self.imageEmpty = pygame.image.load("botEmpty.png")
+        self.imageEmpty.convert() # they say this makes it faster...
+        self.imageStuff = pygame.image.load("botWithStuff.png")
+        self.imageStuff.convert()
+        self.imageGas = pygame.image.load("botWithGas.png")
+        self.imageGas.convert()
+        self.rect = self.imageEmpty.get_rect()
+        
+        self.state = "Returning" # they used to start Idle, now "return" to home
+        self.target = 0 # we're on the path to location 0
+        self.progress = 3 # was 0
+        self.fleetPosition = count
+        self.cooldown = 0
+        self.gasTarget = -1
+
+        self.locationX = 35
+        self.locationY = 100
+        pygame.draw.rect(enviro.screen, [200,0,0], [self.locationX,self.locationY,40,40], 0)
+        
+    def __str__(self):
+##        msg = "HARVESTER DEBUGGER:" + "\n"
+        msg = " Harvester: " + str(self.fleetPosition) + "\n"
+        msg += " State: " + str(self.state) + "\n"
+        msg += " Location: " + str(self.locationX) + "," + str(self.locationY) + "\n"
+        msg += " Progress: " + str(self.progress)
+        return msg        
+                    
+    def spawn(self):
+        # home location
+        self.locationX = 165
+        self.locationY = 230
+        
+        coordX = enviro.getLocationX(4)
+        coordY = enviro.getLocationY(4)
+        pygame.draw.rect(enviro.screen, [255,255,255], 
+            [(coordX + 5) + (self.fleetPosition+1) * 7,(coordY + 70),5,5],0)
+            
+        enviro.screen.blit(self.imageEmpty, [self.locationX,self.locationY])
+
+    def move(self, goalX, goalY, pathDirection):
+        #print "Target=", self.target, "progress=", self.progress
+        
+        # erase where we're at now -- or not, since in the end I decided to paint the road every frame
+        #pygame.draw.rect(enviro.screen, [0,0,0], [self.locationX,self.locationY,40,40], 0)
+        #self.rect.top = self.locationY
+        #self.rect.left = self.locationX
+        #enviro.screen.blit(enviro.roadImage,self.rect,self.rect)
+        
+        # then move, like add 1 to y
+        if self.locationY < goalY:
+            self.locationY = self.locationY + enviro.SPEED
+            if self.locationY > goalY:
+                self.locationY = goalY
+        if self.locationY > goalY:
+            self.locationY = self.locationY - enviro.SPEED
+            if self.locationY < goalY:
+                self.locationY = goalY
+        if self.locationX < goalX:
+            self.locationX = self.locationX + enviro.SPEED
+            if self.locationX > goalX:
+                self.locationX = goalX
+        if self.locationX > goalX:
+            self.locationX = self.locationX - enviro.SPEED
+            if self.locationX < goalX:
+                self.locationX = goalX
+        
+        # draw new
+        if self.state == "GotGas" or self.state == "DeliverGas":
+           enviro.screen.blit(self.imageGas, [self.locationX,self.locationY])            
+        elif (self.state == "Carrying" or
+            self.state == "Selling" or 
+            self.state == "Fueling" or 
+            self.state == "Stuck" or 
+            self.state == "Reenergizing" or
+            self.state == "GetGas"):
+           self.paintHarvesterWithStuff()
+        else:
+           enviro.screen.blit(self.imageEmpty, [self.locationX,self.locationY])
+        
+        if self.locationX == goalX and self.locationY == goalY:
+            if pathDirection == 1:
+                self.progress = self.progress + 1
+            else:
+                self.progress = self.progress - 1
+        return self.progress
+    
+    def paintHarvesterWithStuff(self):
+        #pygame.draw.rect(enviro.screen, [0,255,0], 
+        #       [(self.locationX + 5), (self.locationY + 5),30,30],0)
+        enviro.screen.blit(self.imageStuff, [self.locationX,self.locationY])            
+            
+    def home(self):
+        # erase from playfield
+        #pygame.draw.rect(enviro.screen, [0,0,0], [self.locationX,self.locationY,40,40], 0)
+        self.rect.top = self.locationY
+        self.rect.left = self.locationX
+        #enviro.screen.blit(enviro.roadImage,self.rect,self.rect)        
+        
+        # add back to home list
+        coordX = enviro.getLocationX(4)
+        coordY = enviro.getLocationY(4)
+    
+        pygame.draw.rect(enviro.screen, [255,0,0], 
+            [(coordX + 5) + (self.fleetPosition+1) * 7,(coordY + 70),5,5],0)
+
+    def getNextState(self, shields, base, generators, laser):
+        #print "state ", self.state, "progress", self.progress, "x", self.locationX
+        
+        if self.state == "Cooling":
+            self.cooldown = self.cooldown - 1
+            if self.cooldown < 1:
+                self.state = "Idle"
+        
+        # First priority is to capitalize on when she's dead, sell
+        if self.state == "Idle" and base.isFull():
+            if enviro.mother.state == "Hit" or enviro.mother.state == "Waiting":
+                if enviro.combatStarted:
+                    if base.getStuff():
+                        self.state = "Selling"
+                        self.target = 13 # the market
+                        self.progress = 0
+                        self.spawn() 
+                        
+        # Under combat, don't let the shields or laser run dry...
+        # but they don't need to be chock full either, not if we've got
+        # a generator out of gas
+        if self.state == "Idle":
+            # Be sure the shields are pumping safety above our head
+            if shields.nearlyEmpty():
+                if base.getStuff():
+                    self.state = "Fueling"
+                    shields.promiseRefuel()
+                    self.target = 9 # the shield generator
+                    self.progress = 0
+                    self.spawn()
+            # ...and then be sure we're blasting anything we can
+            elif laser.nearlyEmpty():
+                if base.getStuff():
+                    self.state = "Reenergizing"
+                    laser.promiseRefuel()
+                    self.target = 10
+                    self.progress = 0
+                    self.spawn()                        
+                        
+        # They're not running dry, so next priority is to keep the generators fueled up
+        if self.state == "Idle" and enviro.gasFactory.built == 1:
+            for generator in generators:
+                genIsEmpty = generator.checkTankEmpty()
+                if genIsEmpty:
+                    #print "Found empty tank"
+                    if base.getStuff():
+                        generator.gasPromised = True                    
+                        self.state = "GetGas"
+                        self.target = 8
+                        self.gasTarget = generator.location
+                        self.progress = 0
+                        self.spawn()                       
+                        break                                          
+        
+        if self.state == "Idle":
+            # Be sure the shields are pumping safety above our head
+            if shields.needFuel():
+                if base.getStuff():
+                    self.state = "Fueling"
+                    shields.promiseRefuel()
+                    self.target = 9 # the shield generator
+                    self.progress = 0
+                    self.spawn()
+            # ...and then be sure we're blasting anything we can
+            elif laser.needFuel():
+                if base.getStuff():
+                    self.state = "Reenergizing"
+                    laser.promiseRefuel()
+                    self.target = 10
+                    self.progress = 0
+                    self.spawn()
+                    
+            # Find a generator that has stuff ready to collect...
+            for generator in generators:
+                # need to check idle here again because two generators could be ready at the same time...
+                if self.state == "Idle" and generator.isReady == True and generator.isTargeted == False:
+                    #print "Found target to harvest"
+                    self.state = "Harvesting"
+                    self.target = generator.location
+                    generator.isTargeted = True
+                    self.progress = 0
+                    self.spawn()
+
+        if self.state == "Fueling" or self.state == "Selling":
+            goalX = enviro.path[self.target][self.progress][0]
+            goalY = enviro.path[self.target][self.progress][1]
+            if goalX <> -1:
+                self.move(goalX, goalY, 1)
+            else:
+                if self.state == "Fueling":
+                    if shields.loadFuel():
+                        self.state = "Returning"
+                        self.progress = self.progress - 1;
+                    else: # couldn't refuel, must return
+                        self.state = "Carrying"
+                        self.progress = self.progress - 1;
+                elif self.state == "Selling":
+                    #print "At market!"
+                    enviro.credits = enviro.credits + 1000
+                    #print "now you have ", enviro.credits
+                    enviro.writeCredits(enviro.credits)
+                    self.state = "Returning"
+                    self.progress = self.progress - 1
+                    enviro.SPEED = enviro.SPEED + .25
+                    enviro.YOURSPEED = enviro.YOURSPEED + .25
+                    enviro.PRODUCTION_TIMER -= 1
+                    if enviro.PRODUCTION_TIMER < 1:
+                        enviro.PRODUCTION_TIMER = 1
+                    
+        if self.state == "Reenergizing":
+            goalX = enviro.path[self.target][self.progress][0]
+            goalY = enviro.path[self.target][self.progress][1]
+            if goalX <> -1:
+                self.move(goalX, goalY, 1)
+            else:
+                if laser.stashStuff():
+                    self.state = "Returning"
+                    self.progress = self.progress - 1;
+                else: # couldn't refuel, must return
+                    self.state = "Carrying"
+                    self.progress = self.progress - 1; 
+                    
+        if self.state == "GetGas" or self.state == "DeliverGas":
+            goalX = enviro.path[self.target][self.progress][0]
+            goalY = enviro.path[self.target][self.progress][1]
+            if goalX <> -1:
+                self.move(goalX, goalY, 1)
+            else:
+                if self.state == "GetGas":
+                    self.state = "GotGas"
+                    self.progress = self.progress - 1;            
+                elif self.state == "DeliverGas":
+                    #print "At gas target!"
+                    for generator in generators:
+                        if generator.location == self.target:
+                            generator.refill(enviro.gasGauges)
+                            break
+                    self.state = "Returning"
+                    self.progress = self.progress - 1;
+            
+        if self.state == "Harvesting":
+            goalX = enviro.path[self.target][self.progress][0]
+            goalY = enviro.path[self.target][self.progress][1]
+            
+            if goalX <> -1:
+                self.move(goalX, goalY, 1)
+            else:
+                for generator in generators:
+                    if self.target == generator.location:
+                        if generator.harvestStuff():
+                            self.state = "Carrying"
+                            self.paintHarvesterWithStuff()
+                            self.progress = self.progress - 1;
+                        else: # somebody beat me to it
+                            self.state = "Returning"
+                            self.progress = self.progress - 1
+
+        # Come back, on path direction negative
+        if (self.state == "Carrying" or 
+            self.state == "Returning" or 
+            self.state == "GotGas"):
+            goalX = enviro.path[self.target][self.progress][0]
+            goalY = enviro.path[self.target][self.progress][1]
+            newProgress = self.move(goalX, goalY, -1) 
+            if newProgress == -1: # made it home
+                if self.state == "Carrying":
+                    if base.stashStuff():
+                       self.state = "Cooling"
+                       self.cooldown = enviro.HARVESTER_COOLDOWN
+                       #print "Home!"
+                       self.home()
+                    else:
+                       self.state = "Stuck"
+                       self.progress = 0
+                       self.target = random.randint(11,12)
+                elif self.state == "Returning":
+                    self.state = "Cooling"
+                    self.cooldown = enviro.HARVESTER_COOLDOWN
+                    #print "Home!"
+                    self.home()
+                elif self.state == "GotGas":
+                    self.state = "DeliverGas"
+                    self.progress = 0
+                    self.target = self.gasTarget
+                    
+                    
+        if self.state == "Stuck":
+            if enviro.mother.state == "Hit" or enviro.mother.state == "Waiting":
+                if enviro.combatStarted:                
+                    self.state = "Selling"
+                    self.target = 13 # the market
+                    self.progress = 0
+                            
+            if shields.needFuel():
+                self.state = "Fueling"
+                self.target = 9 # the shield generator
+                self.progress = 0
+                shields.promiseRefuel()
+                
+            elif laser.needFuel():
+                self.state = "Reenergizing"
+                laser.promiseRefuel()                
+                self.target = 10
+                self.progress = 0
+                
+            else:
+                # see if the stuff should be made into gas
+                genNeedsGas = False
+                for generator in generators:
+                    genIsEmpty = generator.checkTankEmpty()
+                    if genIsEmpty:
+                        genNeedsGas = True
+                        generator.gasPromised = True                    
+                        self.state = "GetGas"
+                        self.target = 8
+                        self.gasTarget = generator.location
+                        self.progress = 0
+                        break
+                        
+                if not genNeedsGas:
+                    # we're just stuck, pace awhile and try again in a bit
+                    goalX = enviro.path[self.target][self.progress][0]
+                    goalY = enviro.path[self.target][self.progress][1]
+                    if goalX <> -1:
+                        self.move(goalX, goalY, 1)
+                    else:
+                        self.state = "Carrying"
+                        self.progress = self.progress - 1
+                        # Huh, I needed to move here to avoid some mean flicker
+                        # Not sure why just here though, odd?
+                        goalX = enviro.path[self.target][self.progress][0]
+                        goalY = enviro.path[self.target][self.progress][1]                        
+                        self.move(goalX, goalY, -1) 
+
+
+if __name__ == '__main__':
+
+    
+    print "TEST: Harvester"
+    pygame.init()
+    enviro.screen = pygame.display.set_mode([800,600])    
+    h = Harvester(0)
+
+    pygame.display.flip()
+    while True:
+    
+        for event in pygame.event.get():
+            if event.type == pygame.QUIT:
+                sys.exit()
+        if (event.type == pygame.KEYDOWN):
+            if (pygame.key.get_mods() == 1024): # command key
+                if (event.key == 113):   # q
+                    sys.exit()                    
+# coding=UTF8
+# Copyright © 2010 Rob Leachman
+# Please see the file COPYING in this distribution for license information
+#not sure all of these are needed...
+import sys
+import pygame
+from pygame.color import THECOLORS
+
+import enviro # my global game environment
+import Noise
+    
+from AttackerStuff import ShieldSprite
+from YourStuff import TargetSprite
+from YourStuff import FuelStuff # for testing
+
+
+class Laser(pygame.sprite.Sprite):
+    def __init__(self, image_file):
+        
+        pygame.sprite.Sprite.__init__(self)
+        self.theImage = image_file
+        self.image = pygame.image.load(self.theImage)
+        self.rect = self.image.get_rect()
+        self.location = 11
+        
+        self.state = "Armed"
+        self.aimCount = 0
+        
+        self.fuelBay = [10,10,10]
+        self.promises = 0
+        
+        self.consuming = enviro.LASER_PER_PELLET
+        self.consumingBay = 0
+##        self.upgrades = init_Upgrades
+##        for i in range(0,self.upgrades):
+##            self.applyUpgrade()
+        self.upgrades = enviro.laserUpgrades
+        self.initUpgrades() # I added continue/restart too late, killing me now
+            
+    def initUpgrades(self):
+        self.laserPower = enviro.LASER_UPGRADE_POWER
+        self.aimTime = enviro.LASER_AIM_TIME
+        for i in range(0,self.upgrades):
+            self.applyUpgrade()        
+            
+    def paintPad(self):
+        pass
+        
+    def paint(self, location, fuelPile):
+        self.myFuelPile = fuelPile
+        
+        self.rect.left = location[0]
+        self.rect.top = location[1]
+        
+        enviro.screen.blit(self.image, self.rect)
+        
+        left = self.rect.left
+        top = self.rect.top
+        self.myFuelPile[self.fuelBay[0]].paint([left+5,top + 44])
+        self.myFuelPile[self.fuelBay[1]].paint([left+5+(173-146),top + 44])
+        self.myFuelPile[self.fuelBay[2]].paint([left+5+(200-146),top + 44])
+        
+        pygame.draw.rect(enviro.screen, THECOLORS["blue"], [left+4,top+71,80,9], 0)
+        for i in range(0, self.upgrades):
+            pygame.draw.rect(enviro.screen, [130,202,253], [left+6+i*7,top+73,5,5], 0)
+        
+    def repaint(self):
+        enviro.screen.blit(self.image, self.rect)
+        
+        left = self.rect.left
+        top = self.rect.top
+        self.myFuelPile[self.fuelBay[0]].paint([left+5,top + 44])
+        self.myFuelPile[self.fuelBay[1]].paint([left+5+(173-146),top + 44])
+        self.myFuelPile[self.fuelBay[2]].paint([left+5+(200-146),top + 44])
+        
+        pygame.draw.rect(enviro.screen, THECOLORS["blue"], [left+4,top+71,80,9], 0)
+        for i in range(0, self.upgrades):
+            pygame.draw.rect(enviro.screen, [130,202,253], [left+6+i*7,top+73,5,5], 0)
+            
+    def upgrade(self):
+        if self.upgrades < 11:
+            self.upgrades = self.upgrades + 1
+            enviro.laserUpgrades = enviro.laserUpgrades + 1
+            self.applyUpgrade()
+            self.repaint()
+            return True
+        return False
+    
+    def applyUpgrade(self):
+        self.laserPower += enviro.LASER_UPGRADE_POWER
+        self.aimTime -= 7.5
+        #print "Laser power=", self.laserPower, " aimTime=", self.aimTime
+
+        
+    def stashStuff(self):
+        for i in range(0,3):
+            if self.fuelBay[i] == 0:
+                self.fuelBay[i] = 10
+                self.promises = self.promises - 1
+                
+                # pretty sure this is too simple, but moving on...
+                # "if we're out of gas then this pad got loaded with 9
+                #  and the laser is restocked..." -- but what if there's
+                #  another bay with fuel? I think we restock too soon?
+                if self.consuming == 0:
+                    self.fuelBay[i] = 9
+                    self.consuming = enviro.LASER_PER_PELLET
+                self.repaint()
+                return True
+        return False
+    
+    def unloadStuff(self):
+        for i in range(0,3):
+            if self.fuelBay[i] == 10:
+                self.fuelBay[i] = 0
+                self.repaint()
+                return True
+        return False
+        
+    
+    # See if we could use more fuel for laser
+    def needFuel(self):
+        if enviro.laserRefuelDisabled:
+            return False
+        
+        emptyBays = 0
+        for i in range(0,3):
+            if self.fuelBay[i] == 0:
+                emptyBays += 1
+        if emptyBays > self.promises:
+            return True
+        return False 
+    
+    # See if we're about to run out of laser power
+    def nearlyEmpty(self):
+        if enviro.laserRefuelDisabled:
+            return False
+        
+        emptyBays = 0
+        for i in range(0,3):
+            if self.fuelBay[i] == 0:
+                emptyBays += 1
+        if emptyBays < 2:
+            return False
+        if emptyBays > self.promises:
+            return True
+        return False     
+               
+    def promiseRefuel(self):
+        self.promises = self.promises + 1    
+
+    def autoTarget(self, bombList):
+        targetCoord = [0,0]
+        
+        if self.state == "Armed":
+            self.aimCount = self.aimCount + 1
+            if self.aimCount > self.aimTime:
+                self.aimCount = 0
+                self.state = "Shooting"
+        elif self.state == "Shooting":
+            if len(bombList) > 0:
+                
+                lowest = 0
+                for bomb in bombList:
+                    if bomb.rect.top > lowest:
+                        lowest = bomb.rect.top
+                        targetBomb = bomb
+                targetCoord = targetBomb.rect.center
+                #print "autoshoot! target=",targetCoord
+                
+                self.state = "Armed"
+            
+        return targetCoord
+        
+        
+    def shoot(self, bombList, cursor, mouseButtonUp, autoTargeting):