102 lines
3.6 KiB
Python
102 lines
3.6 KiB
Python
from typing import List, Optional
|
|
import pygame as pg
|
|
from tetris_pygame_hatch import tetromino
|
|
|
|
from tetris_pygame_hatch.tetromino import Tetromino
|
|
|
|
from .settings import *
|
|
|
|
|
|
class Tetris:
|
|
def __init__(self, app) -> None:
|
|
from .app import App
|
|
from .tetromino import Tetromino, Block
|
|
self.app: App
|
|
self.sprite_group: pg.sprite.Group
|
|
self.tetromino: Tetromino
|
|
self.blocks: List[List[Optional[Block]]]
|
|
|
|
self.app = app
|
|
self.tetromino_shaper = TETROMINO_GENERATOR
|
|
# TODO Advanced sprites
|
|
# initialze before tetromino for dependency
|
|
self.sprite_group = pg.sprite.Group()
|
|
self.blocks = [[None for i in range(FIELD_W)] for j in range(FIELD_H)]
|
|
self.tetromino = Tetromino(self, next(self.tetromino_shaper))
|
|
|
|
def control(self, pressed_key):
|
|
'''
|
|
Game control. Note that pause is in app check_events.
|
|
|
|
Parameters
|
|
----------
|
|
pressed_key : int
|
|
pygame pressed_key
|
|
'''
|
|
if pressed_key == pg.K_LEFT or pressed_key == pg.K_a:
|
|
self.tetromino.move('l')
|
|
elif pressed_key == pg.K_RIGHT or pressed_key == pg.K_d:
|
|
self.tetromino.move('r')
|
|
elif pressed_key == pg.K_DOWN or pressed_key == pg.K_s:
|
|
self.tetromino.move('d')
|
|
elif pressed_key == pg.K_w or pressed_key == pg.K_UP:
|
|
self.tetromino.rotate()
|
|
elif pressed_key == pg.K_SPACE:
|
|
# FIXME race condition maybe
|
|
while not self.tetromino.landed:
|
|
self.tetromino.move('d')
|
|
self.app.fall_trigger = True
|
|
|
|
def remove_line(self, y: int) -> bool:
|
|
if all(self.blocks[y]):
|
|
for block_on_y in self.blocks[y]:
|
|
if block_on_y is not None:
|
|
block_on_y.dead = True
|
|
for i in range(y, 0, -1):
|
|
for j in range(FIELD_W):
|
|
block = self.blocks[i - 1][j]
|
|
if block is not None:
|
|
block.pos += vec(0, 1)
|
|
if self.blocks[i - 1][j] is not None:
|
|
self.blocks[i][j] = self.blocks[i - 1][j]
|
|
else: self.blocks[i][j] = None
|
|
return True
|
|
else: return False
|
|
|
|
def draw_grid(self):
|
|
for x in range(FIELD_W):
|
|
for y in range(FIELD_H):
|
|
pg.draw.rect(
|
|
self.app.screen, 'black',
|
|
(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE), 1)
|
|
|
|
def post_tetromino_landed(self):
|
|
if self.tetromino.landed:
|
|
# save block status into self.blocks
|
|
# Note: after removal the affect line above the operation line would fell by one line.
|
|
# this would affect the detection of block removal.
|
|
affect_line: List[int] = []
|
|
for block in self.tetromino.blocks:
|
|
x, y = tuple(map(int, block.pos))
|
|
self.blocks[y][x] = block
|
|
affect_line.append(y)
|
|
for y in affect_line:
|
|
while self.remove_line(y): pass
|
|
self.tetromino = Tetromino(self, next(self.tetromino_shaper))
|
|
|
|
def update(self):
|
|
'''
|
|
events to update,
|
|
mainly handle at every 'app.fall_timeout' seconds
|
|
'''
|
|
if self.app.fall_trigger:
|
|
self.tetromino.update()
|
|
self.post_tetromino_landed()
|
|
|
|
# update the tetromino location since it may changed by self.tetris.control in MainApp event handler
|
|
self.sprite_group.update()
|
|
|
|
def draw(self):
|
|
self.draw_grid()
|
|
self.sprite_group.draw(self.app.screen)
|