Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/eduGames.py
diff options
context:
space:
mode:
Diffstat (limited to 'eduGames.py')
-rw-r--r--eduGames.py1190
1 files changed, 1190 insertions, 0 deletions
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
+
+
+
+
+
+
+
+
+
+