From 6d79836fe1bb76965a80447cc18bdb837f431b20 Mon Sep 17 00:00:00 2001 From: flavio Date: Wed, 13 Mar 2013 21:50:29 +0000 Subject: Base --- (limited to 'eduGames.py') diff --git a/eduGames.py b/eduGames.py new file mode 100644 index 0000000..cc54660 --- /dev/null +++ b/eduGames.py @@ -0,0 +1,1190 @@ +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 + + + + + + + + + + -- cgit v0.9.1