diff --git a/data/components/plants_mek.py b/data/components/plants_mek.py index b5da7ef..c7d4e50 100644 --- a/data/components/plants_mek.py +++ b/data/components/plants_mek.py @@ -43,6 +43,11 @@ def __init__(self,coords,location): frame_coords = [(0,0),(1,0)] _Plant.__init__(self,coords,location,frame_coords,"SHOOTER") +class Tomato(_Plant): + def __init__(self,coords,location): + frame_coords = [(0,2),(1,2),(2,2),(3,2),(4,2), + (0,3),(1,3),(2,3),(3,3),(4,3)] + _Plant.__init__(self,coords,location,frame_coords,"TOMATO") class Sunflower(_Plant): def __init__(self,coords,location): @@ -50,4 +55,5 @@ def __init__(self,coords,location): PLANT_DICT = {"SHOOTER" : Shooter, - "SUNFLOWER" : Sunflower} + "SUNFLOWER" : Sunflower, + "TOMATO" : Tomato} diff --git a/data/components/select_mek.py b/data/components/select_mek.py index 183a98e..dec9b21 100644 --- a/data/components/select_mek.py +++ b/data/components/select_mek.py @@ -11,7 +11,8 @@ def __init__(self,location,plant_names): self.pad_item = (self.rect.x+19,self.rect.y+3) self.spacer = 77 self.plant_dict = {"SHOOTER" : ShooterSelect, - "SUNFLOWER" : SunflowerSelect} + "SUNFLOWER" : SunflowerSelect, + "TOMATO" : TomatoSelect} self.plants = self.setup_plants(plant_names) self.selected = False self.current_time = 0.0 @@ -37,6 +38,7 @@ def update(self,surface,current_time): class _SelectPlant(object): + """Prototype for plants in the selector window.""" def __init__(self,location,sheet_coord,name): self.rect = pg.Rect(location,setup.CELL_SIZE) sheet = setup.GFX["plant_sheet"].copy() @@ -51,12 +53,15 @@ def __init__(self,location,sheet_coord,name): self.make_all_highlights() def deployed(self): + """This function is called if a selected plant is placed on the grid.""" self.ready = False - self.timer = pg.time.get_ticks()#self.current_time + self.timer = pg.time.get_ticks() #A direct call here is justified. self.recharge_highlight = pg.Surface((setup.CELL_SIZE)).convert_alpha() self.recharge_highlight.fill((0,0,0,200)) def make_all_highlights(self): + """Creates the highlights for hovering and selected plants and + creates an initial surface and rect for the recharge highlight.""" self.highlight = pg.Surface((self.rect.width+2,self.rect.height+21)).convert_alpha() self.select_highlight = self.highlight.copy() self.highlight.fill((100,100,255,100)) @@ -66,6 +71,10 @@ def make_all_highlights(self): self.ghost = self.make_ghost() def make_ghost(self): + """Creates the semi-transparent ghost image that appears at the location + a plant would grow if confirmed. As the images already contain + per-pixel-alpha, it is necessary to change the alpha as follows; using + pygame.Surface.set_alpha won't work unfortunately.""" ghost = self.image.copy() array = pg.surfarray.pixels_alpha(ghost) for j,y in enumerate(array): @@ -74,7 +83,10 @@ def make_ghost(self): return ghost def make_recharge_highlight(self): - elapsed = pg.time.get_ticks()-self.timer + """Creates the clock style recharging highlight. I wanted to use + pygame.draw.arc for this but unfortunately that draw method is badly + written and leaves moire patterns.""" + elapsed = self.current_time-self.timer percent_recharged = elapsed/(self.time_for_recharge*1000) angle = 2*math.pi*percent_recharged x = self.recharge_rect.centerx+50*math.cos(angle) @@ -86,6 +98,7 @@ def make_recharge_highlight(self): return self.recharge_highlight def setup_cost(self,cost): + """Creates rendered cost and appropriately centered rect.""" self.cost = cost target_rect = pg.Rect(self.rect.x,self.rect.bottom,setup.CELL_SIZE[0],21) font = pg.font.Font(setup.FONTS["Fixedsys500c"],20) @@ -93,6 +106,8 @@ def setup_cost(self,cost): self.cost_txt_rect = self.cost_txt.get_rect(center=target_rect.center) def update(self,surface,selected,current_time): + """Updates for selector window plants, including highlights and + recharging.""" self.current_time = current_time if self != selected: if self.ready and self.rect.collidepoint(pg.mouse.get_pos()): @@ -111,6 +126,9 @@ def __init__(self,location): self.setup_cost(100) self.time_for_recharge = 10.0 +class TomatoSelect(_SelectPlant): + def __init__(self,location): + _SelectPlant.__init__(self,location,(2,2),"TOMATO") class SunflowerSelect(_SelectPlant): def __init__(self,location): diff --git a/data/components/sun_objects.py b/data/components/sun_objects.py index 38b17d0..428a14d 100644 --- a/data/components/sun_objects.py +++ b/data/components/sun_objects.py @@ -1,6 +1,6 @@ -from sun import Sun import pygame as pg -from .. import setup as setup, tools +from .sun import Sun +from .. import setup, tools class SunObjects: def __init__(self): @@ -11,19 +11,19 @@ def __init__(self): self.sun_timer = 0 self.sun_delay = 5 self.text_update() - + def text_update(self): selected_font = pg.font.Font(setup.FONTS["Fixedsys500c"], 20) self.sun_amount = selected_font.render(str(self.sun_total), 1, (255,255,255)) self.sun_amount_rect = self.sun_amount.get_rect() - + def sun_updates(self, surface): self.text_update() if (pg.time.get_ticks() - self.sun_timer) > 1000*self.sun_delay: self.sun_timer = pg.time.get_ticks() - self.suns.append(Sun(self.large_sun_value)) - + self.suns.append(Sun(self.large_sun_value)) + for obj in self.suns[:]: if obj.image_rect.collidepoint(pg.mouse.get_pos()) and pg.mouse.get_pressed()[0]: mouse = pg.mouse.get_pos() diff --git a/data/setup.py b/data/setup.py index 089aeb9..64f5a3e 100644 --- a/data/setup.py +++ b/data/setup.py @@ -11,33 +11,37 @@ ORIGINAL_CAPTION SCREEN SCREEN_RECT + CELL_SIZE + GRID_MARGIN + SELECTOR_MARGIN FONTS MUSIC GFX SFX """ + import os import pygame as pg from . import tools + SCREEN_SIZE = 800,600 ORIGINAL_CAPTION = "Botany vs Biomass" -#Locations of widgets during gameplay -CELL_SIZE = (72,72) -GRID_MARGIN = (75,165) -SELECTOR_MARGIN = (150,0) - #Initialization ##os.environ['SDL_VIDEO_CENTERED'] = '1' pg.init() pg.display.set_caption(ORIGINAL_CAPTION) - SCREEN = pg.display.set_mode(SCREEN_SIZE) SCREEN_RECT = SCREEN.get_rect() +#Locations of widgets during gameplay +CELL_SIZE = (72,72) +GRID_MARGIN = (75,165) +SELECTOR_MARGIN = (150,0) + #Resource loading (Fonts and music just contain path names). FONTS = tools.load_all_fonts(os.path.join("resources","fonts")) MUSIC = tools.load_all_music(os.path.join("resources","music")) GFX = tools.load_all_gfx(os.path.join("resources","graphics")) -SFX = tools.load_all_sfx(os.path.join("resources","sound")) \ No newline at end of file +SFX = tools.load_all_sfx(os.path.join("resources","sound")) diff --git a/data/states/survive.py b/data/states/survive.py index 55680d6..d763b68 100644 --- a/data/states/survive.py +++ b/data/states/survive.py @@ -1,8 +1,37 @@ +""" +Module: survive.py +Overview: + This module contains the survive state. +Imports: + random + pygame as pg + from .. import setup,tools + from ..components import sun_mek,select_mek,plants_mek +Classes: + Survive(tools._State): + Methods: + __init__(self) + startup(self,current_time,persistant) + render_font(self,font,size,msg,color=(255,255,255)) + update(self,surface,keys,current_time) + update_cursor(self,surface) + get_coordinates(self,mouse) + get_position_from_coordinates(self,coords) + update_suns(self,surface) + update_plants(self,surface) + update_energy(self,surface) + clicked_sun(self,event) + clicked_selector(self,event) + add_plant(self,event) + get_event(self,event) +""" + import random import pygame as pg from .. import setup,tools from ..components import sun_mek,select_mek,plants_mek + class Survive(tools._State): """This State is updated while our game shows the Survive screen.""" def __init__(self): @@ -19,7 +48,7 @@ def startup(self,current_time,persistant): self.mode = "READY" self.energy = 200 self.energy_rect = pg.Rect(31,49,88,32) - self.available_plants = ["SHOOTER","SUNFLOWER"] #Initialized thusly for testing. + self.available_plants = ["SHOOTER","SUNFLOWER","TOMATO"] #Initialized thusly for testing. self.selector = select_mek.Selector(setup.SELECTOR_MARGIN,self.available_plants) self.suns = [] self.plants = [] @@ -85,10 +114,12 @@ def update_suns(self,surface): sun.update(surface,self.current_time) def update_plants(self,surface): + """Update all plants on the grid.""" for plant in self.plants: plant.update(surface,self.current_time) def update_energy(self,surface): + """Render and blit the energy amount to the screen.""" energy_txt = self.render_font("Fixedsys500c",35,str(self.energy),(0,0,0)) energy_txt_rect = energy_txt.get_rect(center=self.energy_rect.center) surface.blit(energy_txt,energy_txt_rect) @@ -110,8 +141,7 @@ def clicked_selector(self,event): self.selector.select_plant(plant) self.plant_cursor = plant.image.copy() return True - else: - self.selector.selected = None + self.selector.selected = None def add_plant(self,event): """Adds currently selected plant to the grid.""" @@ -131,7 +161,7 @@ def get_event(self,event): if event.key == pg.K_ESCAPE: self.next = "MENU" self.done = True - if event.type == pg.MOUSEBUTTONDOWN: + elif event.type == pg.MOUSEBUTTONDOWN: if self.mode == "PLAY": if event.button == 1: if self.clicked_sun(event): diff --git a/data/tools.py b/data/tools.py index 189321e..fdec4f1 100644 --- a/data/tools.py +++ b/data/tools.py @@ -27,11 +27,12 @@ load_all_music(directory,accept=(".wav",".mp3",".ogg",".mdi")) load_all_fonts(directory,accept=(".ttf",)) load_all_sfx(directory,accept=(".wav",".mp3",".ogg",".mdi")) - """ + import os import pygame as pg + class Control(object): """Control class for entire project. Contains the game loop, and contains the event_loop which passes events to States as needed. Logic for flipping @@ -48,12 +49,14 @@ def __init__(self,caption): self.state_dict = {} self.state_name = None self.state = None + def setup_states(self,state_dict,start_state): """Given a dictionary of States and a State to start in, builds the self.state_dict.""" self.state_dict = state_dict self.state_name = start_state self.state = self.state_dict[self.state_name] + def update(self): """Checks if a state is done or has called for a game quit. State is flipped if neccessary and State.update is called.""" @@ -63,6 +66,7 @@ def update(self): elif self.state.done: self.flip_state() self.state.update(self.screen,self.keys,self.current_time) + def flip_state(self): """When a State changes to done necessary startup and cleanup functions are called and the current State is changed.""" @@ -71,6 +75,7 @@ def flip_state(self): self.state = self.state_dict[self.state_name] self.state.startup(self.current_time,persist) self.state.previous = previous + def event_loop(self): """Process all events and pass them down to current State. The f5 key globally turns on/off the display of FPS in the caption""" @@ -81,12 +86,14 @@ def event_loop(self): self.keys = pg.key.get_pressed() self.toggle_show_fps() self.state.get_event(event) + def toggle_show_fps(self): """Press f5 to turn on/off displaying the framerate in the caption.""" if self.keys[pg.K_F5]: self.show_fps = not self.show_fps if not self.show_fps: pg.display.set_caption(self.caption) + def main(self): """Main loop for entire program.""" while not self.done: @@ -98,6 +105,7 @@ def main(self): with_fps = "{} - {:.2f} FPS".format(self.caption,self.clock.get_fps()) pg.display.set_caption(with_fps) + class _State(object): """This is a prototype class for States. All states should inherit from it. No direct instances of this class should be created. get_event and update @@ -105,30 +113,35 @@ class _State(object): overloaded when there is data that must persist between States.""" def __init__(self): self.start_time = 0.0 - self.current_time = 0.0## + self.current_time = 0.0 self.done = False self.quit = False self.next = None self.previous = None self.persist = {} - def get_event(self,event,keys,mouse): + + def get_event(self,event): """Processes events that were passed from the main event loop. Must be overloaded in children.""" pass + def startup(self,current_time,persistant): """Add variables passed in persistant to the proper attributes and set the start time of the State to the current time.""" self.persist = persistant self.start_time = current_time + def cleanup(self): """Add variables that should persist to the self.persist dictionary. Then reset State.done to False.""" self.done = False return self.persist + def update(self,surface,keys,current_time): """Update function for state. Must be overloaded in children.""" pass + ### Resource loading functions. def load_all_gfx(directory,colorkey=(255,0,255),accept=(".png",".jpg",".bmp")): """Load all graphics with extensions in the accept argument. If alpha @@ -172,4 +185,4 @@ def load_all_sfx(directory,accept=(".wav",".mp3",".ogg",".mdi")): name,ext = os.path.splitext(fx) if ext.lower() in accept: effects[name] = pg.mixer.Sound(pg.os.path.join(directory,fx)) - return effects \ No newline at end of file + return effects diff --git a/plantsvszombies.py b/plantsvszombies.py index 1e88590..572b2eb 100644 --- a/plantsvszombies.py +++ b/plantsvszombies.py @@ -5,10 +5,12 @@ -appended by Mekire June 30th, 2013. """ + import sys import pygame as pg from data.main import main + if __name__ == '__main__': try: main() diff --git a/resources/graphics/plant_sheet.png b/resources/graphics/plant_sheet.png index 9c3afc6..4258894 100644 Binary files a/resources/graphics/plant_sheet.png and b/resources/graphics/plant_sheet.png differ diff --git a/resources/graphics/survival.png b/resources/graphics/survival.png index 5bd8afb..cc3f293 100644 Binary files a/resources/graphics/survival.png and b/resources/graphics/survival.png differ