import pygame, sys, os from pygame.locals import * import pygame.font #MOVEMENT TYPES LINEAR = 0 def readDataFile(dataFile): dict = {} file = open(dataFile, "r") fileText = file.read() lines = fileText.splitlines() for line in lines: if not line.startswith("#"): key_value = line.split("=") key = key_value[0].strip() value = key_value[1].strip() if not value.startswith("\""): if value.startswith("("): values = value[1:-1] valuesArray = values.split(",") val = [] for v in valuesArray: val.append(int(v)) dict[key] = tuple(val) else: value = int(value) dict[key] = value else: dict[key] = value[1:-1] file.close() return dict #Controls class Control(object): def __init__(self, game, x = 0, y = 0, width = 0, height = 0, soundFilePath = 0, layer = 0): self.__x = x self.__y = y self.__width = width self.__height = height self.soundFilePath = soundFilePath self.type = "soundButton" self.__layer = layer self.game = game self.__trajectoryGenerator = None self.__oldX = x self.__oldY = y self.__oldWidth = width self.__oldHeight = height self.__visible = True self.__isBeingDragged = False self.isOrphan = True #Don't play with this property. Only uiMgr should use it. self.__rect = Rect(self.__x, self.__y, self.__width, self.__height) self.__oldRect = Rect(self.__x, self.__y, self.__width, self.__height) self.name = "" self.persistent = False #A persistent object can only be removed from the UIMgr by means of explicit removal self.__fakeMouse = None #used for demos #used in drag and drop self.__deltaX = 0 self.__deltaY = 0 self.__clickable = True def setDemoMode(self, fakeMouse): self.__fakeMouse = fakeMouse def setLayer(self, value): self.__layer = value if not self.isOrphan: self.game.getUiMgr().removeControl(self) self.game.getUiMgr().addControl(self) def getLayer(self): return self.__layer def isClickable(self): return self.__clickable def setAsClickable(self): self.__clickable = True def setAsUnclickable(self): self.__clickable = False def collidesWithMousePosition(self, mouseX, mouseY): return self.__rect.collidepoint(mouseX,mouseY) def setX(self, newX): self.__oldX = self.__x self.__x = newX # self.__updateRect() self.markChanges() def setY(self, newY): self.__oldY = self.__y self.__y = newY self.__updateRect() self.markChanges() def getX(self): return self.__x def getY(self): return self.__y def getWidth(self): return self.__width def getHeight(self): return self.__height def setWidth(self, newWidth): #use prudently self.__oldWidth = self.__width self.__width = newWidth self.__updateRect() self.markChanges() def setHeight(self, newHeight): #use with care self.__oldHeight = self.__height self.__height = newHeight self.__updateRect() self.markChanges() def __updateRect(self): if self.__rect.x == self.__x and self.__rect.y == self.__y and self.__rect.width == self.__width and self.__rect.height == self.__height: return self.__oldRect.x = self.__oldX self.__oldRect.y = self.__oldY self.__oldRect.width = self.__oldWidth self.__oldRect.height = self.__oldHeight self.__rect.x = self.__x self.__rect.y = self.__y # self.__rect.width = self.__width self.__rect.width = self.getWidth() #This must be so because of ImageControls. self.__rect.height = self.getHeight() # self.__rect.height = self.__height def getRect(self): return Rect(self.__x, self.__y, self.__width, self.__height) # return self.__rect def startMovement(self, destination, speed, type=LINEAR): self.__trajectoryGenerator = linearTrajectory((self.__x, self.__y), destination, speed) def move(self): if not self.__trajectoryGenerator is None: try: # (self.__oldX, self.__oldY) = (self.__x, self.__y) (newX, newY) = self.__trajectoryGenerator.next() self.setX(newX) self.setY(newY) # self.game.addDirtyRectangle(self.getRect()) except StopIteration: self.__trajectoryGenerator = None finally: self.markChanges() def markChanges(self): self.game.addDirtyRectangle(self.__oldRect) self.game.addDirtyRectangle(self.__rect) self.game.setScreenAsChanged() def isMoving(self): return not (self.__trajectoryGenerator is None) def show(self, surface): pass def makeVisible(self): if self.isOrphan or self.__visible: return #prevents exceptions. self.game.getUiMgr().makeControlVisible(self) self.__visible = True #WARNING def makeInvisible(self): if self.isOrphan or not self.__visible: return self.game.getUiMgr().makeControlInvisible(self) self.__visible = False #WARNING def update(self): '''This is called once after updateState. It commits updates to the state in a given main loop iteration''' self.move() self.__drag() def __getMousePos(self): if self.__fakeMouse is None: return pygame.mouse.get_pos() else: return (self.__fakeMouse.getX(), self.__fakeMouse.getY()) def __drag(self): if self.__isBeingDragged: # mouse_pos = pygame.mouse.get_pos() mouse_pos = self.__getMousePos() self.setX(mouse_pos[0] + self.__deltaX) self.setY(mouse_pos[1] + self.__deltaY) def drag(self): # mouse_pos = pygame.mouse.get_pos() mouse_pos = self.__getMousePos() self.__deltaX = self.getX() - mouse_pos[0] self.__deltaY = self.getY() - mouse_pos[1] self.__isBeingDragged = True def drop(self): self.__isBeingDragged = False def isBeingDragged(self): return self.__isBeingDragged == True def isVisible(self): return self.__visible == True class ImageControl(Control): def __init__(self, game, x, y, imageFilePath, soundFilePath = "", imageDivisions = 1): if imageDivisions == 1: self.image = Image(imageFilePath) self.imageType = "single" else: self.image = ImageStrip(imageFilePath, imageDivisions) self.imageType = "strip" Control.__init__(self, game, x, y, self.image.getWidth(), self.image.getHeight(), soundFilePath) self.__scalingGenerator = None self.name = imageFilePath def setAlpha(self, alpha): self.image.setAlpha(alpha) self.game.addDirtyRectangle(self.getRect()) self.game.setScreenAsChanged() def show(self, surface): myRect = self.getRect() portion = self.game.getUnionOfIntersectionsWithDirtyRects(myRect) if portion is None: return else: x = portion.x y = portion.y portion.x = portion.x - myRect.x portion.y = portion.y - myRect.y self.image.show(surface, (x, y), portion) def setImageDivisionIndex(self, index): if self.imageType == "single": raise "Not supported. The control holds a common image, not a strip." else: curDivInd = self.image.getDivisionIndex() if curDivInd != index: self.image.setDivisionIndex(index) self.markChanges() def getImageDivisionIndex(self): if self.imageType == "single": raise "Not supported. The control holds a common image, not a strip." else: return self.image.getDivisionIndex() def increaseDivisionIndex(self, increment): if self.imageType == "single": raise "Not supported. The control holds a common image, not a strip." else: self.image.increaseDivisionIndex(increment) self.game.addDirtyRectangle(self.getRect()) self.game.setScreenAsChanged() def setColorKey(self, colorKey): self.image.setColorKey(colorKey) self.game.addDirtyRectangle(self.getRect()) self.game.setScreenAsChanged() def startScaling(self, width, height, speed): self.__scalingGenerator = linearTrajectory((self.getWidth(), self.getHeight()), (width, height), speed) def update(self): Control.update(self) if not self.game.isPaused: self.scale() def isBeingScaled(self): return not self.__scalingGenerator is None def scale(self): if not self.__scalingGenerator is None: try: oldRect = self.getRect() (newWidth, newHeight) = self.__scalingGenerator.next() self.image.scale((newWidth, newHeight)) self.setWidth(newWidth) self.setHeight(newHeight) self.game.addDirtyRectangle(oldRect) except StopIteration: self.__scalingGenerator = None finally: self.markChanges() class SingleColorControl(Control): def __init__(self, game, x, y, width, height, color=0, layer = 0): Control.__init__(self, game, x, y, width, height, "", layer) #self.layer = layer self.color = color def setColor(self, color): self.color = color # def show(self, surface): # myRect = self.getRect() # dirtyRect = self.game.getDirtyRect() # if dirtyRect.colliderect(myRect): # portion = myRect.clip(dirtyRect) # if portion.width == 0: # return # else: # surface.fill(self.color, portion) def show(self, surface): myRect = self.getRect() portion = self.game.getUnionOfIntersectionsWithDirtyRects(myRect) if portion is None: return else: surface.fill(self.color, portion) class SingleColorControlWithBorder(SingleColorControl): def __init__(self, game, x, y, width, height, color=0, layer=0): SingleColorControl.__init__(self, game, x, y, width, height, color, layer) self.__borderWidth = 1 self.__borderColor = (0,0,0) def setBorderWidth(self, newWidth): self.__borderWidth = newWidth def setBorderColor(self, newColor): self.__borderColor = newColor def show(self, surface): dirtyRect = self.game.getDirtyRect() if dirtyRect.colliderect(self.getRect()): SingleColorControl.show(self, surface) pygame.draw.rect(surface, self.__borderColor, self.getRect(), self.__borderWidth) class Label(Control): def __init__(self, game, x, y, width, height, font, text, layer = 0): Control.__init__(self, game, x, y, width, height, "", layer) self.font = font self.text = text self.color = (255,255,255, 255) self.background = (0,0,0,0) def acquireRenderedSize(self): data = self.font.render(self.text, True, self.color) dataWidth = data.get_width() dataHeight = data.get_height() if dataWidth > self.getWidth() or dataHeight > self.getHeight(): self.setWidth(data.get_width()) self.setHeight(data.get_height()) def show(self, surface): if self.background is None: data = self.font.render(self.text, True, self.color) else: data = self.font.render(self.text, True, self.color, self.background) dataWidth = data.get_width() dataHeight = data.get_height() if dataWidth > self.getWidth() or dataHeight > self.getHeight(): self.setWidth(data.get_width()) self.setHeight(data.get_height()) dirtyRect = self.game.getDirtyRect() if dirtyRect.colliderect(self.getRect()): surface.blit(data, self.getRect()) def isClickable(self): return False class UIManager(object): def __init__(self, maxLayerLevel = 10): self.controls = [] self.invisibleControls = [] self.maxLayerLevel = maxLayerLevel self.controlsWithMaxLayerLevel = 0 self.draggingControl = None #only to be modified at the beginning self.controlsLeftMargin = 0 self.controlsTopMargin = 0 def dragControl(self, control): #If a control is already dragging, that control must be released before calling this method. #This method should be called inside the on_mouse_down handler. if self.draggingControl is None: self.draggingControl = control control.drag() def dropDraggingControl(self): #This method should be called inside the on_mouse_button_up method. if not self.draggingControl is None: self.draggingControl.drop() self.draggingControl = None def shiftLayers(self, n): for c in self.controls: c.isOrphan = True c.setLayer(c.getLayer() + n) c.isOrphan = False for c in self.invisibleControls: c.isOrphan = True c.setLayer(c.getLayer() + n) c.isOrphan = False if n > 0: self.maxLayerLevel += n elif n < 0: self.maxLayerLevel -= n def deleteGameControls(self, game): self.invisibleControls = [c for c in self.invisibleControls if not c.game is game or c.persistent] self.controls = [c for c in self.controls if not c.game is game or c.persistent] screen = game.getScreen() rect = Rect(0, 0, screen.get_width(), screen.get_height()) game.addDirtyRectangle(rect) def addControl(self, control): self.__addControlInternal(control) if control.isOrphan: control.setX(control.getX() + self.controlsLeftMargin) control.setY(control.getY() + self.controlsTopMargin) control.isOrphan = False def __addControlInternal(self, control): layer = control.getLayer() if layer > self.maxLayerLevel: self.maxLayerLevel = layer if len(self.controls) == 0 or layer == self.maxLayerLevel: self.controls.append(control) else: index = 0 added = False for c in self.controls: if c.getLayer() >= layer: self.controls.insert(index, control) added = True break index = index + 1 if not added: self.controls.append(control) control.game.addDirtyRectangle(control.getRect()) control.game.setScreenAsChanged() def removeControl(self, control): self.__removeControlInternal(control) control.isOrphan = True #it is assumed that only one uiMgr is used in the game. control.setX(control.getX() - self.controlsLeftMargin) control.setY(control.getY() - self.controlsTopMargin) def __removeControlInternal(self, control): if control.isVisible(): self.controls.remove(control) else: self.invisibleControls.remove(control) control.markChanges() # control.game.addDirtyRectangle(control.getRect()) # control.game.setScreenAsChanged() #More efficient def getAffectedControl(self): #print self.controls xMouse = pygame.mouse.get_pos()[0] yMouse = pygame.mouse.get_pos()[1] for control in self.controls: if control.isClickable(): if control.collidesWithMousePosition(xMouse, yMouse): return control def reset(self): """Clears the screen of all controls. Warning: the background will be removed to. Better use Game.cleanScreen()""" for control in self.controls: control.game.addDirtyRectangle(control.getRect()) control.game.setScreenAsChanged() self.controls = [] #TODO: take "persistent" into account (good for backgrounds, mouse pointers...) def updateControls(self): #Committs changes for control in self.controls: control.update() def drawControls(self, screen): for control in reversed(self.controls): control.show(screen) def removeControls(self, type): for control in self.controls: if control.type == type: control.game.addDirtyRectangle(control.getRect()) control.game.setScreenAsChanged() # control.markChanges() self.controls = [c for c in self.controls if c.type != type] def makeControlInvisible(self, control): self.__removeControlInternal(control) self.invisibleControls.append(control) def makeControlVisible(self, control): self.invisibleControls.remove(control) self.__addControlInternal(control) def linearTrajectory(start, stop, speed): #speed: pixels per frame currentX = int(round(start[0])) currentY = int(round(start[1])) targetX = int(round(stop[0])) targetY = int(round(stop[1])) distX = targetX - currentX distY = targetY - currentY dist = pow(pow(distX,2) + pow(distY,2),0.5) steps = dist/speed if steps != 0: xIncrement = distX/steps yIncrement = distY/steps else: currentX = targetX currentY = targetY yield (currentX, currentY) while(currentX != targetX or currentY != targetY): if abs(targetX - currentX) <= abs(xIncrement): currentX = targetX else: currentX = round(currentX + xIncrement) if abs(targetY - currentY) <= abs(yIncrement): currentY = targetY else: currentY = round(currentY + yIncrement) distX = targetX - currentX distY = targetY - currentY dist = pow(pow(distX,2) + pow(distY,2),0.5) steps = dist/speed if steps > 0: xIncrement = distX/steps yIncrement = distY/steps yield (currentX, currentY) class ScreenMgr(object): def __init__(self): self.__screenChanged = True self.dirtyRect = None self.dirtyRectangles = [] def setScreenAsChanged(self): self.__screenChanged = True def screenHasChanged(self): return self.__screenChanged def addDirtyRectangle(self, rect): if self.dirtyRect is None: self.dirtyRect = rect else: self.dirtyRect = self.dirtyRect.union(rect) self.dirtyRectangles.append(rect) def updateScreen(self): #pygame.display.update(self.dirtyRect) pygame.display.update(self.dirtyRectangles) self.dirtyRect = None self.dirtyRectangles = [] self.__screenChanged = False class Game(object): SETTINGS_FILE_NAME = "settings.txt" RESOURCES_DIR_NAME = "resources" DATA_FILE_PATH = "data.txt" def __init__(self, screenSurface, uiMgr, soundMgr, screenMgr, path): settingsFilePath = os.path.join(path, Game.SETTINGS_FILE_NAME) self.resourcesDir = os.path.join(path, Game.RESOURCES_DIR_NAME) #IN THE XO, THIS VALUE MUST BE CHANGED BY GAMES.TXT self.dataFilePath = os.path.join(path, Game.DATA_FILE_PATH) self.path = path self.settings = {} self.data = {} self.readSettings(settingsFilePath) self.__screen = screenSurface self.__uiMgr = uiMgr self.__soundMgr = soundMgr self.__screenMgr = screenMgr self.isPaused = False def getUnionOfIntersectionsWithDirtyRects(self, rect): dirtyRects = self.__screenMgr.dirtyRectangles union = None for dirtyRect in dirtyRects: if rect.colliderect(dirtyRect): intersection = rect.clip(dirtyRect) if union is None: union = intersection else: union = union.union(intersection) return union def readSettings(self, settingsFilePath): self.settings = readDataFile(settingsFilePath) def readDataFile(self, dataFile): self.data = readDataFile(dataFile) def getScreen(self): return self.__screen def setScreenAsChanged(self): self.__screenMgr.setScreenAsChanged() def screenHasChanged(self): return self.__screenMgr.screenHasChanged() def getScreen(self): return self.__screen def getUiMgr(self): return self.__uiMgr def getSoundMgr(self): return self.__soundMgr def getScreenMgr(self): return self.__screenMgr def executeMainLoopIteration(self): self.updateState() # self.getUiMgr().updateControls() #This is part of updating the state. self.__uiMgr.updateControls() self.updateScreen() self.playSounds() def getDirtyRect(self): return self.__screenMgr.dirtyRect def playSounds(self): self.getSoundMgr().playSoundsInQueue() def updateState(self): pass def updateScreen(self): if self.screenHasChanged(): self.__uiMgr.drawControls(self.__screen) self.__screenMgr.updateScreen() def initializeGameData(self): pass def processEvents(self): #This doesn't distinguish what button has been pressed. if self.getSoundMgr().thereAreSoundsPlayingSynchronously(): #pygame.mouse.set_cursor(*pygame.cursors.diamond) pass else: #pygame.mouse.set_cursor(*pygame.cursors.arrow) for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit(0) control = self.getUiMgr().getAffectedControl() if event.type == MOUSEBUTTONUP: if not control is None: self.on_mouse_button_up(control) if event.type == MOUSEBUTTONDOWN: if not control is None: self.on_mouse_button_down(control) if event.type == MOUSEMOTION: if not control is None: self.on_mouse_hover(control) if event.type == VIDEOEXPOSE or event.type == VIDEORESIZE: self.addDirtyRectangle(Rect(0,0,self.getScreen().get_width(), self.getScreen().get_height())) def on_quit(self): pygame.quit() sys.exit(0) def on_mouse_button_down(self, clickedControl): pass def on_mouse_button_up(self, clickedControl): pass def on_mouse_hover(self, clickedControl): pass def addDirtyRectangle(self, rect): self.__screenMgr.addDirtyRectangle(rect) def saveAsDone(self): self.data["finished"] = 1 self.saveData() def isDone(self): return self.data["done"] == True def saveData(self): try: file = open(self.dataFilePath, "w") text2Write = "" for key in self.data.keys(): text2Write = text2Write + str(key) + " = " + str(self.data[key]) + "\r\n" file.write(text2Write) file.close() except: pass #Game class: the mother of all games. class StandaloneGame(Game): def __init__(self, fps = 25, screenWidth = 640, screenHeight = 480, gameTitle = ""): self.__screenWidth = screenWidth self.__screenHeight = screenHeight self.__gameTitle = gameTitle screen = self.initPyGame() Game.__init__(self, screen, UIManager(), SoundMgr(), ScreenMgr(), os.curdir) self.fps = fps self.__clock = pygame.time.Clock() def initPyGame(self): pygame.init() if os.name == "nt": self.__window = pygame.display.set_mode((self.__screenWidth, self.__screenHeight)) else: self.__window = pygame.display.set_mode() pygame.display.set_caption(self.__gameTitle) return pygame.display.get_surface() def execute(self): self.initializeGameData() while True: ms = self.__clock.tick(self.fps) # ms = self.__clock.tick() self.processEvents() self.executeMainLoopIteration() # if ms > 0: # print "FPS:" + str(1000/ms) #Images class Image(object): def __init__(self, path): self.path = path self.data = None self.size = (0,0) self.__scaled = False self.__original = False #self.__alpha = 255 def load(self): if self.data is None: self.data = pygame.image.load(self.path) self.data.convert() self.size = self.data.get_size() def getWidth(self): self.load() return self.size[0] def getHeight(self): self.load() return self.size[1] def show(self, surface, position, portion = None): self.load() if portion is None: surface.blit(self.data, position) else: surface.blit(self.data, position, portion) def setColorKey(self, colorKey): self.load() self.data.set_colorkey(colorKey) def setAlpha(self, alpha): self.load() self.data.set_alpha(alpha) def scale(self, (width, height)): #New versions of pygame may offer better alternatives, such as smooth scale self.load() if not self.__scaled: self.__original = self.data self.__scaled = True if width == self.__original.get_size()[0] and height == self.__original.get_size()[1]: surface = self.__original else: surface = pygame.transform.scale(self.__original, (int(round(width)), int(round(height)))) self.data = surface self.size = (width, height) class ImageStrip(Image): def __init__(self, path, numberOfDivisions): Image.__init__(self, path) self.__numberOfDivisions = numberOfDivisions self.__divisionIndex = 0 self.__scaled = False self.__original = None #TODO: raise error if width/numberOfDivisions is not an integer def getWidth(self): totalWidth = Image.getWidth(self) return totalWidth / self.__numberOfDivisions def setDivisionIndex(self, divisionIndex): self.__divisionIndex = divisionIndex #TODO: raise exception if invalid index def getDivisionIndex(self): return self.__divisionIndex def show(self, surface, position, portion = None): self.load() rectWidth = self.getWidth() rectX = self.__divisionIndex * rectWidth rectArea = Rect(rectX, 0, rectWidth, self.getHeight()) if not portion is None: portion.x += rectArea.x else: portion = rectArea try: # surface.blit(self.data, position, rectArea) surface.blit(self.data, position, portion) except pygame.error: print position[0] + "," + position[1] def showAndMoveNext(self, surface, position): self.show(surface, position) self.setDivisionIndex(self.__divisionIndex + 1) def reset(self): self.setDivisionIndex(0) def next(self): self.setDivisionIndex(self.__divisionIndex + 1) #TODO: raise exception if EOF #TODO: next() method; making this class being capable of functioning like a list, with iterators. def increaseDivisionIndex(self, increment): self.setDivisionIndex(self.__divisionIndex + increment) def setColorKey(self, colorKey): self.load() self.data.set_colorkey(colorKey) def getNumberOfDivisions(self): return self.__numberOfDivisions def scale(self, (width, height)): #New versions of pygame may offer better alternatives, such as smooth scale self.load() width = self.__numberOfDivisions * width if not self.__scaled: self.__original = self.data self.__scaled = True if width == self.__original.get_size()[0] and height == self.__original.get_size()[1]: surface = self.__original else: surface = pygame.transform.scale(self.__original, (int(round(width)), int(round(height)))) self.data = surface self.size = (width, height) #Sound class SoundPlayback(object): def __init__(self, sound, synchronous): self.sound = sound self.playSynchronously = synchronous self.channel = None self.playAlone = False def play(self): self.channel = self.sound.play() def isPlaying(self): return self.channel.get_busy() def stop(self): self.sound.stop() class SoundMgr(object): def __init__(self): self.loadedSounds = {} self.soundsPlayingSynchronously = [] self.queue = [] self.currentlyPlaying = None #used only for sounds of the playAlone type self.soundsPlayingThatMustBeStoppedInCaseAnotherOneArrives = [] def clearSounds(self): self.loadedSounds = {} def thereAreSoundsPlaying(self): return pygame.mixer.get_busy() def addSoundForPlayback(self, soundFilePath, synchronous = False, playAlone = False, stopIfAnotherComes = False): self.stopSoundsThatMustBeStoppedInCaseAnotherOneArrives() sound = self.getSound(soundFilePath) soundPlayback = SoundPlayback(sound, synchronous) soundPlayback.playAlone = playAlone if stopIfAnotherComes: self.soundsPlayingThatMustBeStoppedInCaseAnotherOneArrives.append(soundPlayback) self.queue.append(soundPlayback) def stopSoundsThatMustBeStoppedInCaseAnotherOneArrives(self): for sound in self.soundsPlayingThatMustBeStoppedInCaseAnotherOneArrives: sound.stop() self.soundsPlayingThatMustBeStoppedInCaseAnotherOneArrives = [] def playSoundsInQueue(self): if not self.currentlyPlaying is None: if not self.currentlyPlaying.isPlaying(): self.currentlyPlaying = None else: return while not len(self.queue) == 0: soundPlayback = self.queue.pop(0) if soundPlayback.playSynchronously: self.soundsPlayingSynchronously.append(soundPlayback) soundPlayback.play() if soundPlayback.playAlone: self.currentlyPlaying = soundPlayback return def playSoundImmediately(self, soundFilePath): sound = self.getSound(soundFilePath) soundPlayback = SoundPlayback(sound, False) soundPlayback.play() def thereAreSoundsPlayingSynchronously(self): for soundPlayback in self.soundsPlayingSynchronously: if soundPlayback.isPlaying(): return True else: self.soundsPlayingSynchronously.remove(soundPlayback) def getSound(self, soundFilePath): """If the sound corresponding to soundFilePath is not loaded, loads the sound and stores it in a dictionary. Then returns it.""" if self.loadedSounds.has_key(soundFilePath): return self.loadedSounds[soundFilePath] else: sound = self.loadSound(soundFilePath) self.loadedSounds[soundFilePath] = sound return sound def loadSound(self, soundFilePath): """Loads the sound info of a sound file into a Sound object, then returns the object. If the pygame mixer isn't woking, returns a dummy sound which does nothing when played.""" if not pygame.mixer: return dummysound() try: sound = pygame.mixer.Sound(soundFilePath) return sound except pygame.error: print 'Warning, unable to load,', soundFilePath return dummysound() def unloadSound(self, soundFilePath): del self.loadedSounds[soundFilePath] class dummysound(object): def play(self): pass class ColorFilterControl(Control): def __init__(self, game, x, y, width, height, color, layer = 0, darknessFactor = 50): Control.__init__(self, game, x, y, width, height, "", layer) #self.layer = layer self.filterSurface = pygame.Surface((width, height), 0, 32) self.filterSurface.fill(color) self.filterSurface.set_alpha(darknessFactor) self.__on = False def filterOn(self): if not self.__on: self.__on = True self.markChanges() return True else: return False def filterOff(self): if self.__on: self.__on = False self.markChanges() return True else: return False def show(self, surface): if self.__on: myRect = self.getRect() portion = self.game.getUnionOfIntersectionsWithDirtyRects(myRect) if portion is None: return else: x = portion.x y = portion.y portion.x = portion.x - myRect.x portion.y = portion.y - myRect.y surface.blit(self.filterSurface, (x,y), portion) def isClickable(self): return False class HighlightedControl(ImageControl): def __init__(self, game, x, y, imagePath, soundPath, divisions): ImageControl.__init__(self, game, x, y, imagePath, soundPath, divisions) self.color = (255,255,255) self.filterIntensity = 100 self.filter = ColorFilterControl(game, x, y, self.getWidth(), self.getHeight(), self.color, 0, self.filterIntensity) def move(self): Control.move(self) self.filter.move() def filterOn(self): if self.filter.filterOn(): self.markChanges() def filterOff(self): if self.filter.filterOff(): self.markChanges() def setX(self, newX): Control.setX(self, newX) self.filter.setX(newX) def setY(self, newY): Control.setY(self, newY) self.filter.setY(newY) def show(self, surface): ImageControl.show(self, surface) self.filter.show(surface) class AnimatedImageControl(ImageControl): def __init__(self, game, x, y, imageFilePath, imageDivisions): ImageControl.__init__(self, game, x, y, imageFilePath, "", imageDivisions) self.__counter = 0 self.framesBetweenUpdates = 0 self.__frameCounter = 0 self.pingpong = False self.__direction = 1 def isPlaying(self): return self.__counter != 0 def playAnimation(self, repetitions): if repetitions < 0: repetitions = -2 self.__counter = repetitions + 1 def stop(self): self.pause() self.setImageDivisionIndex(0) def pause(self): self.__counter = 0 def getNumberOfFrames(self): return self.image.getNumberOfDivisions() def update(self): #TODO: correct main loop for child games #This will jump if imageDivisions < 2 ImageControl.update(self) if self.__frameCounter == 0: if self.__counter > 0 or self.__counter == -1: imageDivisionIndex = self.getImageDivisionIndex() numberOfDivisions = self.getNumberOfFrames() imageDivisionIndex = (imageDivisionIndex + self.__direction)%numberOfDivisions self.setImageDivisionIndex(imageDivisionIndex) if self.pingpong: if imageDivisionIndex == 0 or imageDivisionIndex == (numberOfDivisions-1): self.__direction = -self.__direction if self.__counter > -1 and imageDivisionIndex == numberOfDivisions - 1: #not accurate in ping pong mode. self.__counter = self.__counter - 1 self.__frameCounter = (self.__frameCounter + 1)% (self.framesBetweenUpdates+1) class MultipleFileAnimation(AnimatedImageControl): def __init__(self, game, x, y, dirPath): self.__index = 0 self.__framesCollection = os.listdir(dirPath) if "Thumbs.db" in self.__framesCollection: self.__framesCollection.remove("Thumbs.db") self.__framesCollection.sort() self.__dirPath = dirPath firstFrame = os.path.join(dirPath, self.__framesCollection[0]) AnimatedImageControl.__init__(self, game, x, y, firstFrame, 1) def getNumberOfFrames(self): return len(self.__framesCollection) def setImageDivisionIndex(self, index): self.__index = index self.image.path = os.path.join(self.__dirPath, self.__framesCollection[index]) self.image.data = None self.image.load() self.markChanges() def getImageDivisionIndex(self): return self.__index class ProgressBar(Control): def __init__(self, game, x, y, width, height, layer=0): Control.__init__(self, game, x, y, width, height, "", layer) self.maxValue = 100.0 self.curValue = 0.0 self.borderColor = (0,0,0) # self.emptyColor = (127,127,127) self.filledColor = (255,255,255) self.borderWidth = 1 def setCurValue(self, value): self.curValue = 0.0 + value self.markChanges() def show(self, surface): fraction = self.curValue/self.maxValue availableWidth = self.getWidth() - 2*self.borderWidth fillWidth = round(fraction * availableWidth) # surface.fill(self.emptyColor, Rect(self.getX(), self.getY(), self.getWidth(), self.getHeight())) fillRect = Rect(self.getX() + self.borderWidth, self.getY(), fillWidth, self.getHeight()) surface.fill(self.filledColor, fillRect) pygame.draw.rect(surface, self.borderColor, self.getRect(), self.borderWidth) class GrowsAndShrinksAnimatedControl(ImageControl): (RESTING, GROWING, SHRINKING) = (0,1,2) def __init__(self, game, x, y, imageFilePath, soundFilePath, imageDivisions): ImageControl.__init__(self, game, x, y, imageFilePath, soundFilePath, imageDivisions) self.state = GrowsAndShrinksAnimatedControl.RESTING self.__originalWidth = self.getWidth() self.__originalHeight = self.getHeight() self.loop = False self.delay = 0 def playAnimation(self): self.loop = True if not self.isBeingScaled(): self.state = GrowsAndShrinksAnimatedControl.GROWING self.startScaling(1.1*self.getWidth(), 1.1*self.getHeight(), 2) def update(self): if self.game.isPaused: return if self.delay > 0: self.delay = self.delay - 1 return ImageControl.update(self) if self.state == GrowsAndShrinksAnimatedControl.GROWING: if not self.isBeingScaled(): self.startScaling(self.__originalWidth, self.__originalHeight, 2) self.state = GrowsAndShrinksAnimatedControl.SHRINKING elif self.state == GrowsAndShrinksAnimatedControl.SHRINKING: if not self.isBeingScaled(): self.state = GrowsAndShrinksAnimatedControl.RESTING elif self.state == GrowsAndShrinksAnimatedControl.RESTING: if self.loop: self.playAnimation() def stopAnimation(self): self.loop = False