105 lines
3.1 KiB
Python
105 lines
3.1 KiB
Python
|
import random
|
||
|
import pygame as pg
|
||
|
|
||
|
from .settings import *
|
||
|
|
||
|
class Block(pg.sprite.Sprite):
|
||
|
def __init__(self, tetromino, pos, color) -> None:
|
||
|
from .tetromino import Tetromino
|
||
|
self.tetromino: Tetromino = tetromino
|
||
|
self.pos = vec(pos) + vec((FIELD_W//2, 0))
|
||
|
self.dead = False
|
||
|
|
||
|
super().__init__(tetromino.tetris.sprite_group)
|
||
|
self.image = pg.Surface([TILE_SIZE, TILE_SIZE])
|
||
|
# TODO user custom border radius
|
||
|
pg.draw.rect(self.image, color, (1, 1, TILE_SIZE, TILE_SIZE), border_radius=TILE_SIZE//2-5)
|
||
|
self.rect = self.image.get_rect()
|
||
|
|
||
|
def rotate(self, pivot: vec) -> vec: # because pygame rotates by origin, need to specify pivot
|
||
|
'''
|
||
|
Rotate 90 degress with pivot as origin
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
pivot : vec
|
||
|
origin port for block rotation.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Vector2
|
||
|
rotated position.
|
||
|
'''
|
||
|
return (self.pos - pivot).rotate(90) + pivot
|
||
|
|
||
|
def set_rect_pos(self):
|
||
|
self.rect.topleft = int(self.pos[0]) * TILE_SIZE, int(self.pos[1]) * TILE_SIZE
|
||
|
|
||
|
def is_collide(self, dt: vec) -> bool:
|
||
|
x, y = tuple(map(int, self.pos + dt))
|
||
|
if (0 <= x < FIELD_W and y < FIELD_H) and (y < 0 or not self.tetromino.tetris.blocks[y][x]):
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
def update(self):
|
||
|
'''
|
||
|
make pos update ease, should update in tetromino class though
|
||
|
'''
|
||
|
if self.dead:
|
||
|
self.kill()
|
||
|
else:
|
||
|
self.set_rect_pos()
|
||
|
|
||
|
class Tetromino:
|
||
|
def __init__(self, tetris, shape: str) -> None:
|
||
|
from .tetris import Tetris
|
||
|
self.tetris: Tetris = tetris
|
||
|
|
||
|
self.shape = shape
|
||
|
self.color = TETROMINO_COLORS[shape]
|
||
|
self.blocks = [Block(self, pos, self.color) for pos in TETROMINOES[self.shape]]
|
||
|
self.landed = False
|
||
|
|
||
|
# hack to display full block
|
||
|
for i in range(-min((pos_y for _pos_x, pos_y in TETROMINOES[self.shape])) - 1):
|
||
|
self.move('d')
|
||
|
min((float(i) for i in range(5)))
|
||
|
|
||
|
def is_collide(self, dt: vec) -> bool:
|
||
|
'''
|
||
|
check every block if is collided.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
bool
|
||
|
True if collide
|
||
|
'''
|
||
|
return any((i.is_collide(dt) for i in self.blocks))
|
||
|
|
||
|
def rotate(self):
|
||
|
'''
|
||
|
Rotate the tetromino if possible, say not collide.
|
||
|
'''
|
||
|
rotatable = True
|
||
|
pivot_pos = self.blocks[0].pos
|
||
|
for i in self.blocks[1:]:
|
||
|
if i.is_collide(i.rotate(pivot_pos) - i.pos):
|
||
|
rotatable = False
|
||
|
break
|
||
|
if rotatable:
|
||
|
for i in self.blocks: i.pos = i.rotate(pivot_pos)
|
||
|
|
||
|
def move(self, direction):
|
||
|
movevec = MOVE_DIRECTIONS[direction]
|
||
|
# landing check
|
||
|
if not self.is_collide(movevec):
|
||
|
for block in self.blocks:
|
||
|
block.pos += movevec
|
||
|
elif direction == 'd':
|
||
|
self.landed = True
|
||
|
|
||
|
def update(self):
|
||
|
'''
|
||
|
update function called every time need to fall a cell
|
||
|
'''
|
||
|
self.move('d')
|