示例#1
0
文件: Tetris.py 项目: qdot/bartris
class Tetris():

    NORMAL_MODE = 0
    BOOZE_MODE = 1
    DESIGNATED_DRIVER_MODE = 2
    SINGLE_DRINK_MODE = 3

    def __init__(self):
        self._event_handlers = []
        self._setup_states()
        self._state["game_over"] = True
        self._setup_display()
        self._joystick = None
        if pygame.joystick.get_count() > 0:
            print "FOUND A JOYSTICK!"
            self._joystick = pygame.joystick.Joystick(0)
            self._joystick.init()
            
    def _setup_states(self, mode = 0):
        self._params = {
            "fullscreen"             : False,
            "fullscreen_width"       : 1024,
            "fullscreen_height"      : 768,
            "num_cells_wide"         : 10,
            "num_cells_high"         : 20,
            "cell_width"             : 25,
            "cell_height"            : 25,
            "drink_pouring_time"     : 10.0,
            "starting_cell_x"        : 4,
            "starting_cell_y"        : 1,
            "grid_top_x"             : 0,
            "grid_top_y"             : 0,
            "modes"                  : ["(N)ormal Mode", "(B)ooze Mode", "(S)ingle Drink Mode", "(D)esignated Driver Mode", "(Q)uit"]
            }

        self._level_params = {
            "moving_rate"            : [0.00005,.00004,0.00003,0.00002,0.00001],
            "rotating_rate"          : [0.00009,0.00008,0.00007,0.00006,0.00005],
            "falling_rate"           : [0.00050,0.00035,0.00020,0.00010,0.00005]
            }

        self._state = {
            "last_falling_time"      : 0,
            "falling_rate"           : self._level_params["falling_rate"][0],
            "last_moving_time"       : 0,
            "last_rotating_time"     : 0,
            "level_up_line_count"    : 5,
            "last_num_lines_cleared" : 0,
            "top_y"                  : 0,
            "times_found"            : 0,
            "current_level"          : 1,
            "game_over"              : False,
            "all_finished"           : False,
            "current_y"              : 0,
            "holding_down"           : False
            }


        if mode in [self.NORMAL_MODE, self.SINGLE_DRINK_MODE]:
            self._color_dict     = {1: COLORS["blue"], 6 : COLORS["brown"], 10: COLORS["darkGrey"]}        
        elif mode == self.BOOZE_MODE:
            self._color_dict     = {3 : COLORS["brown"], 10: COLORS["darkGrey"]}
        elif mode == self.DESIGNATED_DRIVER_MODE:
            self._color_dict     = {10: COLORS["blue"]}

        if mode == self.SINGLE_DRINK_MODE or mode == self.BOOZE_MODE:
            self._level_params = {
                "moving_rate"            : [0.00005],
                "rotating_rate"          : [0.00009],
                "falling_rate"           : [0.00040]
                }
            self._params["drink_pouring_time"] = 20.0

        self._color_range    = 10

        self._textBuff       = 2
        self._grid           = Grid( 1, 1, self._params["cell_width"], self._params["cell_height"], self._params["num_cells_wide"], self._params["num_cells_high"], COLORS["black"], self._params["fullscreen"], self._params["fullscreen_width"], self._params["fullscreen_height"])
        if self._params["fullscreen"]:
            self._params["grid_top_x"] = (self._params["fullscreen_width"] / 2) - (self._grid.width / 2)
            self._params["grid_top_y"] = (self._params["fullscreen_height"] / 2) - (self._grid.height / 2)

        self._tetrominoList  = ['T','I','O','S','Z','L','J']
        self._sound          = Sound()

        self._new_tetromino()

    def _setup_display(self):
        os.environ['SDL_VIDEO_CENTERED'] = '1'
        pygame.init()
        if self._params["fullscreen"]:
            self._screen = pygame.display.set_mode((1024, 768), pygame.FULLSCREEN, 24)
        else:
            self._screen = pygame.display.set_mode((self._grid.width+self._textBuff,self._grid.height+self._textBuff),0,32)
        if pygame.font:
            self._font = pygame.font.Font(None, 18)

    def init_game(self):
        pygame.display.set_caption("Python Tetris")

    def run_game(self):
        time.sleep(0.01)
        if self._state["game_over"]:
            self._menu_state()
        else:
            self._game_loop()
        self._render()
        return

    def _menu_state(self):
        current_key = ''
        for event in pygame.event.get():
            if event.type == QUIT:
                raise QuitException()
            if event.type == pygame.KEYDOWN:
                current_key = event.key
        if current_key == K_q:
            raise QuitException()
        elif current_key == K_n:
            self._setup_states(self.NORMAL_MODE)
        elif current_key == K_b:
            self._setup_states(self.BOOZE_MODE)
        elif current_key == K_d:
            self._setup_states(self.DESIGNATED_DRIVER_MODE)
        elif current_key == K_s:
            self._setup_states(self.SINGLE_DRINK_MODE)

    def _game_loop(self):

        # Grab vars
        if self._tetromino.active:
            self._move_tetromino()
        else: #New Tetromino
            osc.sendMsg("/tetris/piece_down", [0], "localhost", 9001)
            self._new_tetromino()
        #Levels and Speedup
        if self._grid.num_lines_cleared >= (self._state["level_up_line_count"] * self._state["current_level"]) and self._state["last_num_lines_cleared"] != self._grid.num_lines_cleared:
            self._level_up()

    def _move_tetromino(self):

        current_key = ''
        up_key = ''
        for event in pygame.event.get():
            legal_moves   = {
                "down"  : self._tetromino.max_y < self._grid.height,
                "left"  : self._tetromino.min_x > 0 and self._grid.accept(self._tetromino.id,self._tetromino.positions[self._tetromino.currentPosition],-1,0),
                "right" : self._tetromino.max_x < self._grid.width and self._grid.accept(self._tetromino.id,self._tetromino.positions[self._tetromino.currentPosition],1,0),
                }



            if event.type == QUIT:
                break

            if event.type == pygame.JOYAXISMOTION:
                a = event.axis
                p = event.value
                #turn joystick into key simulations
                if a == 0:
                    if p < -0.01:
                        current_key = K_LEFT
                    elif p > 0.01:
                        current_key = K_RIGHT
                else:
                    if p > 0.01:
                        current_key = K_DOWN
                    elif p < 0.01 and p > -0.01:
                        up_key = K_DOWN
            if event.type == pygame.JOYBUTTONDOWN:
                if event.button == 1:
                    current_key = K_z
                else:
                    current_key = K_x
            if event.type == pygame.KEYDOWN:
                current_key = event.key
            elif event.type == pygame.KEYUP:
                up_key = event.key

            if current_key == K_RIGHT and legal_moves["right"]:
                self._tetromino.move(self._grid,1,0)
            if current_key == K_LEFT and legal_moves["left"]:
                self._tetromino.move(self._grid,-1,0)
            if current_key == K_DOWN:
                self._state["holding_down"] = True
                osc.sendMsg("/mario/speed", [40], "localhost", 9001)
            elif up_key == K_DOWN:
                osc.sendMsg("/mario/speed", [0], "localhost", 9001)
                self._state["holding_down"] = False
            #TODO: Fix rotation states
            if current_key == K_z and False not in legal_moves.values():
                self._tetromino.rotate(self._grid, -1)
            if current_key == K_x and False not in legal_moves.values():
                self._tetromino.rotate(self._grid, 1)
            #ADDED: quit current_key
            if current_key == K_q:
                raise QuitException()

        legal_moves   = {
            "down"  : self._tetromino.max_y < self._grid.height,
            "left"  : self._tetromino.min_x > 0 and self._grid.accept(self._tetromino.id,self._tetromino.positions[self._tetromino.currentPosition],-1,0),
            "right" : self._tetromino.max_x < self._grid.width and self._grid.accept(self._tetromino.id,self._tetromino.positions[self._tetromino.currentPosition],1,0),
            }

        current_time  = time.time()/1000.0
        falling_time  = current_time - self._state["last_falling_time"]

        #Downward fall
        if self._state["holding_down"] is True and legal_moves["down"]:
            self._tetromino.move(self._grid,0,1)
            self._state["current_y"] += 1
        elif falling_time >= self._state["falling_rate"] and legal_moves["down"]:
            self._state["last_falling_time"] = current_time
            self._tetromino.move(self._grid,0,1)
            self._state["current_y"] += 1
        elif not legal_moves["down"] and falling_time >= self._state["falling_rate"]:
            self._tetromino.cluck.play()
            self._tetromino.active = False

    def _new_tetromino(self):
        #ADDED: Set tetromino color to one of our three allowed colors
        color_index = random.randint(0,self._color_range)
        color = ()
        for color_probability in sorted(self._color_dict.keys()):
            if color_index <= color_probability:
                color = self._color_dict[color_probability]
                break

        rand          = random.randint(0,len(self._tetrominoList)-1)
        self._tetromino = Tetromino(self._params["starting_cell_x"], self._params["starting_cell_y"], COLORS["black"],color,self._tetrominoList[rand])
        self._state["holding_down"] = False
        if self._grid.checkForLines():
            for e in self._event_handlers:
                e.on_line_created(self)

        #Test for GAME OVER
        top_y = self._grid.topY()
        if top_y <= 2:
            self._state["times_found"] += 1
        if self._state["times_found"] > 3:
            self._state["game_over"] = True

    def _level_up(self):
        self._state["last_num_lines_cleared"] = self._grid.num_lines_cleared
        self._state["current_level"] += 1
        if self._state["current_level"] < len(self._level_params["falling_rate"]):
            self._state["falling_rate"] = self._level_params["falling_rate"][self._state["current_level"]]
        else:
            self._state["all_finished"] = True
            self._state["game_over"] = True
        self._sound.play("../sound/levelup.wav")
        for e in self._event_handlers:
            e.on_level_up(self)

    def _render(self):
        #render Background
        pygame.draw.rect(self._screen,(255,255,255),Rect(self._params["grid_top_x"], self._params["grid_top_y"],self._grid.width+2,self._grid.height+2))
        #Render Grid
        self._grid.render(self._screen)
        self._render_status()
        pygame.display.update()

    def _render_status(self):
        if not self._state["game_over"]:
            lineText       = "Lines: " + str(self._grid.num_lines_cleared) + "  Level: " + str(self._state["current_level"])
            lines_text     = self._font.render(lineText, 1, COLORS["white"])
            lines_text_pos = Rect((self._params["grid_top_x"]+1, self._params["grid_top_y"]+1),(self._params["grid_top_x"] + 300, self._params["grid_top_y"] + 20))
            self._screen.blit(lines_text, lines_text_pos)
        else:
            game_over_text     = self._font.render("GAME OVER... LEVEL: "+str( self._state["current_level"])+" LINES: " + str(self._grid.num_lines_cleared),1, COLORS["red"])
            if self._state["all_finished"]:
                game_over_text     = self._font.render("ALL LEVELS FINISHED! LEVEL: "+str( self._state["current_level"])+" LINES: " + str(self._grid.num_lines_cleared),1, COLORS["red"])
            game_over_text_pos = Rect((self._params["grid_top_x"]+1, self._params["grid_top_y"]+1),(self._params["grid_top_x"] + 300, self._params["grid_top_y"] + 20))
            self._screen.blit(game_over_text,game_over_text_pos)
            for i in range(0, len(self._params["modes"])):
                pLineText       = self._params["modes"][i]
                p_lines_text     = self._font.render(pLineText, 1, COLORS["white"])
                p_lines_text_pos = Rect((1+self._params["grid_top_x"],100+(i*25)+self._params["grid_top_y"]),(300,250))
                self._screen.blit(p_lines_text, p_lines_text_pos)


    def add_event_handler(self, eh):
        self._event_handlers.append(eh)
                tetromino.rotate(grid)            

            #Downward fall
            if falling_time >= fallingRate and move_down_ok:
                last_falling_time = current_time            
                tetromino.move(grid,0,1)
                currentY += 1
            if not move_down_ok:
                tetromino.cluck.play()
                tetromino.active = False        
        
        else: #New Tetromino
            tetromino = Tetromino(starting_cell_x,starting_cell_y,black,blue,tetrominoList[rand])
            grid.checkForLines()
            #Test for GAME OVER
            topY = grid.topY()
            if topY <= 2:
                timesFound += 1
            if timesFound > 3:
                gameOver = True
        #Levels and Speedup
        if grid.numLinesCleared % level_up_line_count == 0 and lastNumLinesCleared != grid.numLinesCleared:
            lastNumLinesCleared = grid.numLinesCleared
            if current_level < len(levels):
                fallingRate = levels[current_level]
            current_level += 1
            sound.play("../sound/levelup.wav")
        #Render Background
        pygame.draw.rect(screen,(255,255,255),Rect(0,0,grid.width+2,grid.height+2))            
        #Render Grid
        grid.render(screen)