Tetromino class:
class Tetromino(object):
def __init__(self, board, matrix, type, color, x=0, y=None, updateinterval=FRAMERATE, queue=0):
self.matrix = matrix
self.type = type
self.board = board
self.color = color
self.updateinterval = updateinterval
self.time_until_update = self.updateinterval
self.draw_required = True
self.update_required = True
self.sped_up = False
self.x = x
self.y = y
self.queue = queue
self.level = 1
if y == None:
self.y = -(len(self.matrix))
## Hackety hack
def forBlock(self, func, boolean=False):
for y in xrange(len(self.matrix)):
for x in xrange(len(self.matrix[y])):
if self.matrix[y][x] and func(self.x + x, self.y + y, self.matrix) and boolean:
return True
def draw(self):
def drawBlock(x, y, _):
self.board.drawCube(x, y, self.color)
self.forBlock(drawBlock)
def insert(self):
def insert(x, y, _):
self.board.blocks[(x, y)] = self.color
if self.y < 0:
## XXX: GAME OVER
self.board.update_required = False
self.forBlock(insert)
self.board.checkTetris()
self.update_required = False
def update(self):
self.time_until_update -= 1
if self.time_until_update <= 0:
self.moveDiagonal(1)
self.time_until_update = self.updateinterval
def drop(self):
while self.update_required:
self.moveDiagonal(1)
def checkBlockCollision(self):
def colliding(x, y, _):
return self.board.blocks.get((x, y))
return self.forBlock(colliding, boolean=True)
def checkWallCollision(self, xp, yp):
for y in xrange(len(self.matrix)):
for x in xrange(len(self.matrix[y])):
## Some of the functions need to know which edge the collision happened on,
## otherwise the result can be treated like a boolean.
if self.matrix[y][x]:
if yp+y > self.board.height-1:
return "bottom"
if xp+x > self.board.width-1:
return "right"
if xp+x < 0:
return "left"
## Move diagonally, if possible
def moveDiagonal(self, direction):
self.y += direction
if self.checkBlockCollision():
self.y -= direction
self.insert()
if self.checkWallCollision(self.x, self.y) == "bottom":
self.y -= direction
self.insert()
## Move horizontally, if possible
def moveHorizontal(self, direction):
self.x += direction
if self.checkBlockCollision():
self.x -= direction
if self.checkWallCollision(self.x, self.y):
self.x -= direction
## Rotate if possible
def rotate(self, direction):
last_matrix = self.matrix
self.matrix = rot90(self.matrix)
if self.checkWallCollision(self.x, self.y) or self.checkBlockCollision():
self.matrix = last_matrix
## It makes the game WAAY to easy, but i kind of always wondered "what if"
def flip(self):
flip(self.matrix)
if self.checkWallCollision(self.x, self.y) or self.checkBlockCollision():
flip(self.matrix)
def eventHandler(self, events):
for event in events:
if event.type == KEYUP:
if event.key == keymap["game"]["speed_up"] and self.sped_up:
self.sped_up = False
self.updateinterval *= 10
self.time_until_update = self.updateinterval
if event.type == KEYDOWN:
if event.key == keymap["game"]["rotate_right"]:
self.rotate(1)
elif event.key == keymap["game"]["rotate_left"]:
self.rotate(-1)
elif event.key == keymap["game"]["reverse"]:
self.flip()
elif event.key == keymap["game"]["move_right"]:
self.moveHorizontal(1)
elif event.key == keymap["game"]["move_left"]:
self.moveHorizontal(-1)
elif event.key == keymap["game"]["drop_down"]:
self.drop()
elif event.key == keymap["game"]["speed_up"]:
self.sped_up = True
self.updateinterval /= 10
self.time_until_update = self.updateinterval
Game class:
class Game(object):
def __init__(self, _id, caption="", mouse_visible=True, bgcolor=(0x22,0x22,0x22), screen=None, ticktime=FRAMERATE,
width=SCREEN_WIDTH, height=SCREEN_HEIGHT, x=SCREEN_WIDTH, y=SCREEN_HEIGHT, sound_enabled=False, soundtrack=None):
self.caption = caption
self.mouse_visible = mouse_visible
self.bgcolor = bgcolor
self.screen = screen
self.ticktime = ticktime
self.batch = {}
self.drawqueue = []
self.ret = 0
self.windows = {}
self.height = y
self.width = x
self.events = None
self.id = _id
self.soundtrack = soundtrack
self.sound_enabled = sound_enabled
self.playing = ""
self.setup()
def stopMusic(self):
self.playing = ""
Pygame.mixer.music.stop()
## TODO: The call/quit model currently fails here, I'll just have to save the music's "progress."
def playMusic(self, path, loops=1):
try:
if not self.sound_enabled:
Log.warning("Attempted to play music in `{}' where sound has been disabled".format(self.id))
Pygame.mixer.music.load(path)
Pygame.mixer.music.play(loops)
Log.log("Playing sountrack `{}'".format(path))
self.playing = path
except:
Log.error("Unable to play music file: `{}'".format(path))
def getJob(self, name):
return self.batch[name]
def addJob(self, name, obj):
self.batch[name] = obj
self.drawqueue.append(name)
## Why not just call Sys.exit(), why create a separate method for this?
## Because finishing of can get more complex as this program develops.
def quit(self):
Sys.exit()
## We just "exploit" the stack to create things like pause menus or other "contexts"
## that take over the screen.
def call(self, obj, **kwargs):
game = obj(screen=self.screen, **kwargs)
ret = game.run()
self.setup()
if ret and self.id != ret:
self.quitGame(ret)
def quitGame(self, *args):
if args:
self.ret = args[0]
if self.playing:
self.stopMusic()
self.running = None
def setup(self):
Pygame.init()
Pygame.display.set_caption(self.caption)
Pygame.mouse.set_visible(int(self.mouse_visible))
if not Pygame.mixer.get_init() and self.sound_enabled:
Log.log("Initializing mixer")
Pygame.mixer.init()
if self.soundtrack and self.sound_enabled and not self.playing:
self.playMusic(self.soundtrack, loops=-1)
if not self.screen:
self.screen = Pygame.display.set_mode((self.width, self.height), DISPLAY_OPTIONS)
self.screen.fill(self.bgcolor)
Pygame.display.flip()
self.clock = Pygame.time.Clock()
def eventHandler(self, events):
pass
def run(self):
if not hasattr(self, "running") or not hasattr(self, "eventHandler"):
raise GameError("Game has not been properly initialized")
while self.running:
self.clock.tick(self.ticktime)
self.screen.fill(self.bgcolor)
self.events = Pygame.event.get()
queue = sorted(self.batch, key=lambda obj: self.batch[obj].queue)
for obj in queue:
obj = self.getJob(obj)
if obj.update_required:
obj.update()
if obj.draw_required:
obj.draw()
## Context is love, context is life.
obj.eventHandler(self.events)
Pygame.display.flip()
self.eventHandler(self.events)
if self.running:
self.running()
return self.ret