# Jools -- a graphical puzzle game in the Tetris tradition # # Copyright (C) 2002-2003 Paul Pelzl # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # grid.py # Implements game logic, jool position handling, etc. import pygame, math from jool import * from timer import * from initialize import * import options # FIXME: this should really be refactored into a class that manages the # game screen (and handles most input), along with a "grid" class # that does nothing more than display jools and handle jool movements. # Class that manages positions and movement of all # the various jools. class Grid: def __init__(self): # the grid is initially blank. self.rows = [] self.mousePressed = 0 self.mousePressedPoint = pygame.mouse.get_pos() self.hasSwaps = 0 self.hasFalseSwaps = 0 self.rotatingPos = (-1, -1) self.pointsToAdd = 0 self.totalPoints = 0 self.chainTotal = 0 self.totalJools = 0 self.joolsThisLevel = 0 self.timer = Timer() self.oldKeyTuple = pygame.key.get_pressed() self.spacePressed = 0 self.joolsRect = pygame.Rect(SCREENW-8*JOOLSIZE, SCREENH-8*JOOLSIZE, 8*JOOLSIZE, 8*JOOLSIZE) self.hasSelection1 = 0 self.oldMousePos = (0,0) self.oldJoolPos = (-1,-1) self.oldMousePressed = 0 self.usingKeyboard = 0 # create a brand new grid of jools. def new(self, timeTrial): self.pointsToAdd = 0 self.chainTotal = 0 self.jools = [] self.availableMoves = [] for i in range(8): # iterate through eight rows rows = [] for j in range(8): # iterate through eight jools per row tempJool = Jool(random.randint(0,6)) tempJool.setPos((280 + JOOLSIZE*j, 60*i - 500)) rows.append(tempJool) self.jools.append(rows) # iterate through all jools again, to set the collision rects self.screenBottom = pygame.Rect(0, SCREENH, SCREENW, 500) for i in range(8): for j in range(8): if i == 7: # If the jool is in the bottom row, then the bottom of the # screen is the collision point self.jools[i][j].rect_below = self.screenBottom else: # For all other jools, the jool lying below determines the # collision rect self.jools[i][j].rect_below = self.jools[i+1][j].rect if timeTrial: self.timer.new(255, 125) self.timer.draw_border(screen) pygame.display.update(self.timer.draw(screen)) self.oldSelection1 = self.jools[0][0] self.oldSelection2 = self.jools[0][0] self.oldSelPos1 = (0,0) # update the positions of all jools # (returns 1 if some jools were moved, else 0) # FIXME: Maintain a list of all jools that need to be updated at any # point in time. Don't need to waste time searching every jool. def updateAll(self, updateType): updatedSome = 0 spritesToRender.empty() renderList = [] self.cleanup_rects = [] irange = range(8) irange.reverse() # We need to move the lower jools before # the higher ones in order to avoid # collisions for i in irange: for j in range(8): # (deep copy; don't want this to change during update() ) old_rect = pygame.Rect(self.jools[i][j].rect) # If there was an update, we need to erase the old sprite and # draw the new one if self.jools[i][j].update(updateType): updatedSome = 1 renderList.append(self.jools[i][j]) self.cleanup_rects.append(old_rect) if updateType == UPDATEALL or updateType == UPDATESWAPS: self.hasSwaps = 0 self.hasFalseSwaps = 0 self.hasRotation = 0 # Do a (sorta) Z-buffer sort so that the "top" jools get drawn last renderList.sort(joolZCmp) renderList.reverse() for jool in renderList: spritesToRender.add(jool) return updatedSome # clean the positions of sprites that just moved def cleanUpdated(self): for r in self.cleanup_rects: screen.blit(background, r, r) # draw sprites in the render list def renderUpdated(self): pygame.display.update(spritesToRender.draw(screen)) # remove some jools, and drop in replacements # (takes a list of position tuples (row, col) as arguments) def removeJools(self, positions, tallyJools): positions.sort(posHeightCmp) # Higher jools get removed first # FIXME: is this what we want to do? I guess it would be better # to clean the matches out of new grids before the jools # even fall... if tallyJools: if self.chainTotal == 1: point1.play() elif self.chainTotal == 2: point2.play() elif self.chainTotal == 3: point3.play() elif self.chainTotal == 4: point4.play() elif self.chainTotal == 5: point5.play() else: point6.play() for pos in positions: if tallyJools: self.totalJools += 1 self.joolsThisLevel += 1 # the jools that are removed will have to be erased # (invoking deep copy) self.cleanup_rects.append(pygame.Rect(self.jools[pos[0]][pos[1]].rect)) # the jool above the removed one gets a different collision rect now if pos[0] > 0: self.jools[pos[0]-1][pos[1]].rect_below = self.jools[pos[0]][pos[1]].rect_below # loop over all jools that lie above the removed one rowsToDrop = range(pos[0]) rowsToDrop.reverse() for row in rowsToDrop: # those jools can move now self.jools[row][pos[1]].isFixed = 0 # they need to be "renumbered" self.jools[row+1][pos[1]] = self.jools[row][pos[1]] tempJool = Jool(random.randint(0,6)) tempJool.rect.top = self.jools[0][pos[1]].rect.top - JOOLSIZE tempJool.rect.left = self.jools[0][pos[1]].rect.left tempJool.rect_below = self.jools[1][pos[1]].rect self.jools[0][pos[1]] = tempJool self.cleanUpdated() # Search the grid for matched jools in rows of # three or more. Returns a list of jool positions # to remove, and the corresponding number of points to # add to the score. # # FIXME: plenty of room for optimization if necessary. (Doesn't look # like it is necessary, however...) # 1) If we have done an exhaustive search previously, then we only # need to search near areas that have changed. (This one at least # should be implemented.) # 2) If there is a match with a window of size 'n', then of course all # smaller windows will match. (Difficult to do cleanly.) # 3) If we *really* need to optimize, look for a completely new # algorithm that starts by finding matched pairs, then expanding into # rows of 3, etc. def getMatches(self, TallyPoints): # Slide a window of length 'windowLen' (which increments between 3 # and 8) over the entire grid in both horizontal and vertical # orientations. If all jools in the window match, add their # positions to a total match list. matchedList = [] points = 0 # Number of points scored for this set of matches ONLY, # not counting chains. windowRange = range(3, 6) windowRange.reverse() for windowLen in windowRange: # Horizontal orientation for x in range(8-windowLen+1): for y in range(8): matched = 1 joolType = self.jools[y][x].type for i in range(windowLen-1): if self.jools[y][x+i+1].type != joolType: matched = 0 break if matched: newPos = 0 for i in range(windowLen): tempPos = (y, x+i) # Append only positions that are not already in the # matchedList if tempPos not in matchedList: newPos = 1 matchedList.append(tempPos) # Now worry about getting the points right. if newPos: # OK, this set of jools is not entirely contained in # another set (e.g. 3 of a kind contained in 5 of # a kind), so we can score it. if windowLen == 3: if points > 0: points *= 2 else: points = 10 elif windowLen == 4: if points > 0: points *= 3 else: points = 30 elif windowLen == 5: if points > 0: points *= 4 else: points = 80 # Vertical orientation for y in range(8-windowLen+1): for x in range(8): matched = 1 joolType = self.jools[y][x].type for i in range(windowLen-1): if self.jools[y+i+1][x].type != joolType: matched = 0 break if matched: newPos = 0 for i in range(windowLen): tempPos = (y+i, x) # Append only positions that are not already in the # matchedList if tempPos not in matchedList: newPos = 1 matchedList.append(tempPos) # Now worry about getting the points right. if newPos: # OK, this set of jools is not entirely contained in # another set (e.g. 3 of a kind contained in 5 of # a kind), so we can score it. if windowLen == 3: if points > 0: points *= 2 else: points = 10 elif windowLen == 4: if points > 0: points *= 3 else: points = 30 elif windowLen == 5: if points > 0: points *= 4 else: points = 80 # If this is a chain, there's an extra points bonus if points > 0: if self.pointsToAdd > 0: self.pointsToAdd += points self.pointsToAdd *= 2 self.chainTotal += 1 else: self.pointsToAdd = points self.chainTotal = 1 else: self.chainTotal = 1 self.pointsToAdd = 0 # Only add the points if this grid is not new if TallyPoints: self.totalPoints += self.pointsToAdd return matchedList # Find all possible moves. # Save each move as a pair of positions to swap, with the left/top # position first. # # FIXME: Again, lots of room for optimization here. Primarily, if most # of the screen is unchanged then we don't need to scan it. def updateMoves(self): # Slide masks across the grid. # If all jools in those masks match, then there is a move with an # associated tuple. Add the move to the list if it isn't in there # already. moveList = [] # Using the masks: # # XX X X XX # for i in range(5): for j in range(8): # Horizontal orientation temp = self.jools[j][i].type if self.jools[j][i+3].type == temp: if self.jools[j][i+1].type == temp: move = ( (j,i+2), (j,i+3) ) if move not in moveList: moveList.append(move) if self.jools[j][i+2].type == temp: move = ( (j,i), (j,i+1) ) if move not in moveList: moveList.append(move) # Vertical orientation temp = self.jools[i][j].type if self.jools[i+3][j].type == temp: if self.jools[i+1][j].type == temp: move = ( (i+2,j), (i+3,j) ) if move not in moveList: moveList.append(move) if self.jools[i+2][j].type == temp: move = ( (i,j), (i+1,j) ) if move not in moveList: moveList.append(move) # using the masks: # # XX X XX X X X X # X XX X XX X X X # for i in range(6): for j in range(7): # Horizontal orientation # first two temp = self.jools[j][i].type if self.jools[j+1][i+2].type == temp: if self.jools[j][i+1].type == temp: move = ( (j,i+2), (j+1,i+2) ) if move not in moveList: moveList.append(move) if self.jools[j+1][i+1].type == temp: move = ( (j,i), (j+1,i) ) if move not in moveList: moveList.append(move) # middle two temp = self.jools[j+1][i].type if self.jools[j][i+2].type == temp: if self.jools[j][i+1].type == temp: move = ( (j,i), (j+1,i) ) if move not in moveList: moveList.append(move) if self.jools[j+1][i+1].type == temp: move = ( (j,i+2), (j+1,i+2) ) if move not in moveList: moveList.append(move) # last two temp = self.jools[j+1][i].type if (self.jools[j][i+1].type == temp and self.jools[j+1][i+2].type == temp): move = ( (j,i+1), (j+1,i+1) ) if move not in moveList: moveList.append(move) temp = self.jools[j][i].type if (self.jools[j+1][i+1].type == temp and self.jools[j][i+2].type == temp): move = ( (j,i+1), (j+1,i+1) ) if move not in moveList: moveList.append(move) # Vertical orientation # first two temp = self.jools[i][j].type if self.jools[i+2][j+1].type == temp: if self.jools[i+1][j].type == temp: move = ( (i+2,j), (i+2,j+1) ) if move not in moveList: moveList.append(move) if self.jools[i+1][j+1].type == temp: move = ( (i,j), (i,j+1) ) if move not in moveList: moveList.append(move) # middle two temp = self.jools[i][j+1].type if self.jools[i+2][j].type == temp: if self.jools[i+1][j].type == temp: move = ( (i,j), (i,j+1) ) if move not in moveList: moveList.append(move) if self.jools[i+1][j+1].type == temp: move = ( (i+2,j), (i+2,j+1) ) if move not in moveList: moveList.append(move) # last two temp = self.jools[i][j+1].type if (self.jools[i+1][j].type == temp and self.jools[i+2][j+1].type == temp): move = ( (i+1,j), (i+1,j+1) ) if move not in moveList: moveList.append(move) temp = self.jools[i][j].type if (self.jools[i+1][j+1].type == temp and self.jools[i+2][j].type == temp): move = ( (i+1,j), (i+1,j+1) ) if move not in moveList: moveList.append(move) self.availableMoves = moveList # control remains in this loop while pause is active def pauseLoop(self): screen.blit(background, (640-(8*JOOLSIZE), 480-(8*JOOLSIZE))) screen.blit(pausedImage, (SCREENW-(8*JOOLSIZE) + ((8*JOOLSIZE)-pausedRect.width)/2, (SCREENH-(8*JOOLSIZE) + ((8*JOOLSIZE)-pausedRect.height)/2))) pygame.display.update() if audio.Capable and audio.musicEnabled: pygame.mixer.music.pause() # loop while paused while 1: pygame.time.wait(READ_INPUT_DELAY) pygame.event.pump() if pygame.event.peek(QUIT): options.saveOptions() pygame.quit() sys.exit("Exiting Jools...") keyTuple = pygame.key.get_pressed() if not self.oldKeyTuple[pygame.K_p] and keyTuple[pygame.K_p]: # djk: unpause music if audio.Capable and audio.musicEnabled: pygame.mixer.music.unpause() # unpaused... r edraw all jools. screen.blit(background, (640-(8*JOOLSIZE), 480-(8*JOOLSIZE))) self.updateAll(UPDATEREDRAW) self.renderUpdated() pygame.display.flip() self.oldKeyTuple = keyTuple break elif not self.oldKeyTuple[pygame.K_q] and keyTuple[pygame.K_q]: screen.blit(background, (640-(8*JOOLSIZE), 480-(8*JOOLSIZE))) self.updateAll(UPDATEREDRAW) self.renderUpdated() pygame.display.flip() self.oldKeyTuple = keyTuple return 1 self.oldKeyTuple = keyTuple # enable and disable sound effects def toggleSound(self): if audio.soundEnabled == 1: audio.soundEnabled = 0 audioDisplay.draw() elif audio.Capable: audio.soundEnabled = 1 audioDisplay.draw() # enable and disable music def toggleMusic(self): if audio.musicEnabled == 1: audio.musicEnabled = 0 audioDisplay.draw() if audio.Capable: pygame.mixer.music.stop() elif audio.Capable: audio.musicEnabled = 1 audioDisplay.draw() try: pygame.mixer.music.play(-1) except: print "Warning: no music loaded." # Check for (and handle) mouse clicks, drags, etc. # FIXME: this is badly in need of refactoring, now that there are # more types of input to scan for. def processInput(self): # djk: handle minimization during gameplay if pygame.event.peek(ACTIVEEVENT): evtList = pygame.event.get(ACTIVEEVENT) #evt = evtList.pop() for evt in evtList: #print( evt.dict ) if( (evt.state == 6) and (evt.gain == 0) ): self.pauseLoop() keyTuple = pygame.key.get_pressed() # process keypresses for pause, quit, sound toggling if not self.oldKeyTuple[pygame.K_p] and keyTuple[pygame.K_p]: self.oldKeyTuple = keyTuple self.pauseLoop() elif not self.oldKeyTuple[pygame.K_q] and keyTuple[pygame.K_q]: return 1 elif not self.oldKeyTuple[pygame.K_s] and keyTuple[pygame.K_s]: self.toggleSound() elif not self.oldKeyTuple[pygame.K_m] and keyTuple[pygame.K_m]: self.toggleMusic() # check whether the user clicked on the audio status icons mousePos = pygame.mouse.get_pos() mousePressed = pygame.mouse.get_pressed()[0] if self.oldMousePressed: if not mousePressed: # check whether the user clicked on the audio status icons if audioDisplay.notesRect.collidepoint(mousePos): self.toggleMusic() elif audioDisplay.megaRect.collidepoint(mousePos): self.toggleSound() joolPos = self.oldJoolPos # jool selection and swapping # first, check for mouse movement if self.joolsRect.collidepoint(mousePos): if mousePos != self.oldMousePos or not self.usingKeyboard: self.usingKeyboard = 0 pygame.mouse.set_visible(1) joolPos = ((mousePos[1] - (SCREENH-8*JOOLSIZE))/JOOLSIZE, (mousePos[0] - (SCREENW-8*JOOLSIZE))/JOOLSIZE) if not self.hasSelection1: self.selectNewSecondary(joolPos, self.oldJoolPos) else: if (abs(self.sel1Pos[0]-joolPos[0]) + abs(self.sel1Pos[1]-joolPos[1]) == 1): if self.oldJoolPos != self.sel1Pos: self.selectNewSecondary(joolPos, self.oldJoolPos) else: # careful not to erase the primary selection self.hasRotation = 1 self.jools[joolPos[0]][joolPos[1]].isRotating = 1 self.jools[joolPos[0]][joolPos[1]].selectionStatus = SEL_SECONDARY self.jools[joolPos[0]][joolPos[1]].drawSelection() self.jools[self.oldJoolPos[0]][self.oldJoolPos[1]].needsRotateCleanup = 1 else: self.hasRotation = 1 self.jools[joolPos[0]][joolPos[1]].isRotating = 1 if self.oldJoolPos != self.sel1Pos: self.jools[self.oldJoolPos[0]][self.oldJoolPos[1]].selectionStatus = SEL_UNSELECTED self.jools[self.oldJoolPos[0]][self.oldJoolPos[1]].needsRotateCleanup = 1 else: if not self.usingKeyboard: self.jools[self.oldJoolPos[0]][self.oldJoolPos[1]].needsRotateCleanup = 1 # make mouse pointer visible if there is any mouse activity if mousePos != self.oldMousePos or mousePressed: self.usingKeyboard = 0 pygame.mouse.set_visible(1) # check for arrow key movement if ((keyTuple[pygame.K_UP] and not self.oldKeyTuple[pygame.K_UP]) or (keyTuple[pygame.K_k] and not self.oldKeyTuple[pygame.K_k])): self.usingKeyboard = 1 if self.oldJoolPos == (-1,-1): joolPos = (0,0) elif self.oldJoolPos[0] > 0: joolPos = (self.oldJoolPos[0] - 1, self.oldJoolPos[1]) else: joolPos = (7, self.oldJoolPos[1]) self.doKeyboardSelection(joolPos) elif ((keyTuple[pygame.K_DOWN] and not self.oldKeyTuple[pygame.K_DOWN]) or (keyTuple[pygame.K_j] and not self.oldKeyTuple[pygame.K_j])): self.usingKeyboard = 1 if self.oldJoolPos == (-1,-1): joolPos = (0,0) elif self.oldJoolPos[0] < 7: joolPos = (self.oldJoolPos[0] + 1, self.oldJoolPos[1]) else: joolPos = (0, self.oldJoolPos[1]) self.doKeyboardSelection(joolPos) elif ((keyTuple[pygame.K_LEFT] and not self.oldKeyTuple[pygame.K_LEFT]) or (keyTuple[pygame.K_h] and not self.oldKeyTuple[pygame.K_h])): self.usingKeyboard = 1 if self.oldJoolPos == (-1,-1): joolPos = (0,0) elif self.oldJoolPos[1] > 0: joolPos = (self.oldJoolPos[0], self.oldJoolPos[1] - 1) else: joolPos = (self.oldJoolPos[0], 7) self.doKeyboardSelection(joolPos) elif ((keyTuple[pygame.K_RIGHT] and not self.oldKeyTuple[pygame.K_RIGHT]) or (keyTuple[pygame.K_l] and not self.oldKeyTuple[pygame.K_l])): self.usingKeyboard = 1 if self.oldJoolPos == (-1,-1): joolPos = (0,0) elif self.oldJoolPos[1] < 7: joolPos = (self.oldJoolPos[0], self.oldJoolPos[1] + 1) else: joolPos = (self.oldJoolPos[0], 0) self.doKeyboardSelection(joolPos) # catch a few items that don't fall under the above if self.usingKeyboard: pygame.mouse.set_visible(0) self.hasRotation = 1 self.jools[joolPos[0]][joolPos[1]].isRotating = 1 if not self.hasSelection1: self.selectNewSecondary(joolPos, joolPos) # check for mouse button activity if self.joolsRect.collidepoint(mousePos): if mousePressed: self.usingKeyboard = 0 if not self.hasSelection1: if not self.oldMousePressed: joolPos = ((mousePos[1] - (SCREENH-8*JOOLSIZE))/JOOLSIZE, (mousePos[0] - (SCREENW-8*JOOLSIZE))/JOOLSIZE) self.selectNewSecondary(joolPos, self.oldJoolPos) self.selectPrimary(joolPos) else: if not self.oldMousePressed: joolPos = ((mousePos[1] - (SCREENH-8*JOOLSIZE))/JOOLSIZE, (mousePos[0] - (SCREENW-8*JOOLSIZE))/JOOLSIZE) if joolPos == self.sel1Pos: self.deselectPrimary() elif (abs(self.sel1Pos[0]-joolPos[0]) + abs(self.sel1Pos[1]-joolPos[1]) == 1): temp = self.sel1Pos self.deselectPrimary() self.jools[joolPos[0]][joolPos[1]].selectionStatus = SEL_UNSELECTED self.jools[joolPos[0]][joolPos[1]].needsRotateCleanup = 1 self.trySwap(temp, joolPos) else: if self.hasSelection1: if self.oldMousePressed: if (abs(self.sel1Pos[0]-joolPos[0]) + abs(self.sel1Pos[1]-joolPos[1]) == 1): temp = self.sel1Pos self.deselectPrimary() self.jools[joolPos[0]][joolPos[1]].selectionStatus = SEL_UNSELECTED self.jools[joolPos[0]][joolPos[1]].needsRotateCleanup = 1 self.trySwap(temp, joolPos) elif joolPos != self.sel1Pos: self.deselectPrimary() # check for keyboard selection activity if keyTuple[pygame.K_SPACE]: self.usingKeyboard = 1 if not self.oldKeyTuple[pygame.K_SPACE]: if not self.hasSelection1: self.hasSelection1 = 1 self.selectPrimary(joolPos) else: if joolPos == self.sel1Pos: self.deselectPrimary() elif (abs(self.sel1Pos[0]-joolPos[0]) + abs(self.sel1Pos[1]-joolPos[1]) == 1): temp = self.sel1Pos self.deselectPrimary() self.jools[joolPos[0]][joolPos[1]].selectionStatus = SEL_UNSELECTED self.jools[joolPos[0]][joolPos[1]].needsRotateCleanup = 1 self.trySwap(temp, joolPos) else: if self.hasSelection1: if self.oldKeyTuple[pygame.K_SPACE]: if (abs(self.sel1Pos[0]-joolPos[0]) + abs(self.sel1Pos[1]-joolPos[1]) == 1): temp = self.sel1Pos self.deselectPrimary() self.jools[joolPos[0]][joolPos[1]].selectionStatus = SEL_UNSELECTED self.jools[joolPos[0]][joolPos[1]].needsRotateCleanup = 1 self.trySwap(temp, joolPos) elif joolPos != self.sel1Pos: self.deselectPrimary() # prepare for next iteration of processInput() self.oldMousePos = mousePos self.oldMousePressed = mousePressed self.oldKeyTuple = keyTuple self.oldJoolPos = joolPos # the following functions are used for highlighting jools in # various colored boxes def selectNewSecondary(self, newPos, oldPos): self.hasRotation = 1 self.jools[newPos[0]][newPos[1]].isRotating = 1 self.jools[newPos[0]][newPos[1]].selectionStatus = SEL_SECONDARY self.jools[newPos[0]][newPos[1]].drawSelection() if newPos != oldPos: self.jools[oldPos[0]][oldPos[1]].selectionStatus = SEL_UNSELECTED self.jools[oldPos[0]][oldPos[1]].needsRotateCleanup = 1 def selectNewTertiary(self, newPos, oldPos): self.hasRotation = 1 self.jools[newPos[0]][newPos[1]].isRotating = 1 self.jools[newPos[0]][newPos[1]].selectionStatus = SEL_TERTIARY self.jools[newPos[0]][newPos[1]].drawSelection() if newPos != oldPos: if oldPos != self.sel1Pos: self.jools[oldPos[0]][oldPos[1]].selectionStatus = SEL_UNSELECTED self.jools[oldPos[0]][oldPos[1]].needsRotateCleanup = 1 def selectPrimary(self, pos): self.hasSelection1 = 1 self.sel1Pos = pos self.jools[pos[0]][pos[1]].selectionStatus = SEL_PRIMARY self.jools[pos[0]][pos[1]].drawSelection() def deselectPrimary(self): self.hasSelection1 = 0 self.jools[self.sel1Pos[0]][self.sel1Pos[1]].selectionStatus = SEL_UNSELECTED self.jools[self.sel1Pos[0]][self.sel1Pos[1]].needsRotateCleanup = 1 self.sel1Pos = (-1,-1) # this is called to select new jools when the arrow keys are pressed def doKeyboardSelection(self, newPos): if not self.hasSelection1: self.selectNewSecondary(newPos, self.oldJoolPos) else: if (abs(self.sel1Pos[0]-newPos[0]) + abs(self.sel1Pos[1]-newPos[1]) == 1): if self.oldJoolPos != self.sel1Pos: self.selectNewSecondary(newPos, self.oldJoolPos) else: # careful not to erase the primary selection self.hasRotation = 1 self.jools[newPos[0]][newPos[1]].isRotating = 1 self.jools[newPos[0]][newPos[1]].selectionStatus = SEL_SECONDARY self.jools[newPos[0]][newPos[1]].drawSelection() self.jools[self.oldJoolPos[0]][self.oldJoolPos[1]].needsRotateCleanup = 1 else: self.hasRotation = 1 self.jools[newPos[0]][newPos[1]].isRotating = 1 if newPos != self.sel1Pos: self.selectNewTertiary(newPos, self.oldJoolPos) else: self.jools[self.oldJoolPos[0]][self.oldJoolPos[1]].selectionStatus = SEL_UNSELECTED self.jools[self.oldJoolPos[0]][self.oldJoolPos[1]].needsRotateCleanup = 1 # Swap two jools, given the grid positions of the two to swap def swapByPosition(self, pos1, pos2): m1, n1 = pos1 m2, n2 = pos2 if ((pos1, pos2) in self.availableMoves or (pos2, pos1) in self.availableMoves): # Swap the objects in memory tempJool = self.jools[m1][n1] self.jools[m1][n1] = self.jools[m2][n2] self.jools[m2][n2] = tempJool # Mark those jools as swapped, so they will be caught # in the next graphics update self.jools[m1][n1].isSwapped = 1 self.jools[m2][n2].isSwapped = 1 self.jools[m1][n1].isOnTop = 1 # the selected jool is drawn last if n2 > n1: self.jools[m1][n1].swapDir = 3 # moving left self.jools[m2][n2].swapDir = 1 # moving right elif n2 < n1: self.jools[m1][n1].swapDir = 1 # moving right self.jools[m2][n2].swapDir = 3 # moving left elif m2 > m1: self.jools[m1][n1].swapDir = 4 # moving up self.jools[m2][n2].swapDir = 2 # moving down elif m2 < m1: self.jools[m1][n1].swapDir = 2 # moving down self.jools[m2][n2].swapDir = 4 # moving up # Update the collision rects # The jools above the swapped jools need to have new # collision rects if m1 > 0: self.jools[m1-1][n1].rect_below = self.jools[m1][n1].rect if m2 > 0: self.jools[m2-1][n2].rect_below = self.jools[m2][n2].rect # The swapped jools themselves need to have new collision # rects if m1 < 7: self.jools[m1][n1].rect_below = self.jools[m1+1][n1].rect else: self.jools[m1][n1].rect_below = self.screenBottom if m2 < 7: self.jools[m2][n2].rect_below = self.jools[m2+1][n2].rect else: self.jools[m2][n2].rect_below = self.screenBottom # The swapped jools may need a rotation cleanup animation. if self.jools[m1][n1].imageNum > 0: self.jools[m1][n1].needsRotateCleanup = 1 if self.jools[m2][n2].imageNum > 0: self.jools[m2][n2].needsRotateCleanup = 1 # Illegal move; enter false swap animation. else: # Mark those jools as swapped, so they will be caught # in the next graphics update self.hasFalseSwaps = 1 self.jools[m1][n1].isFalseSwapped = 1 self.jools[m2][n2].isFalseSwapped = 1 self.jools[m2][n2].isOnTop = 1 # the selected jool is drawn last if n2 > n1: self.jools[m1][n1].swapDir = 1 # moving left self.jools[m2][n2].swapDir = 3 # moving right elif n2 < n1: self.jools[m1][n1].swapDir = 3 # moving right self.jools[m2][n2].swapDir = 1 # moving left elif m2 > m1: self.jools[m1][n1].swapDir = 2 # moving up self.jools[m2][n2].swapDir = 4 # moving down elif m2 < m1: self.jools[m1][n1].swapDir = 4 # moving down self.jools[m2][n2].swapDir = 2 # moving up # The swapped jools may need a rotation cleanup animation. if self.jools[m1][n1].imageNum > 0: self.jools[m1][n1].needsRotateCleanup = 1 if self.jools[m2][n2].imageNum > 0: self.jools[m2][n2].needsRotateCleanup = 1 # Try swapping the positions of two jools. Checks whether the two jools # are adjacent. If the jools are adjacent but a swap will not create a # match, then the call to self.swapByPosition() will do a "false swap". def trySwap(self, joolPos1, joolPos2): if ((joolPos2 != joolPos1) and (abs(joolPos2[0]-joolPos1[0]) + abs(joolPos2[1]-joolPos1[1]) == 1)): self.swapByPosition(joolPos1, joolPos2) self.hasSwaps = 1 # arch-tag: jool grid manager class