You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
6.8 KiB
Python

#!/usr/bin/env python3
# File: block.py
# Description: This file contains the declaration of basic tetris block class.
# Author: Pavel Benáček <pavel.benacek@gmail.com>
# 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 3 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, see <http://www.gnu.org/licenses/>.
import pdb
import constants
import pygame
import math
import copy
import sys
class Block(object):
"""
Class for handling of tetris block
"""
def __init__(self,shape,x,y,screen,color,rotate_en):
"""
Initialize the tetris block class
Parameters:
- shape - list of block data. The list contains [X,Y] coordinates of
building blocks.
- x - X coordinate of first tetris shape block
- y - Y coordinate of first tetris shape block
- screen - screen to draw on
- color - the color of each shape block in RGB notation
- rotate_en - enable or disable the rotation
"""
# The initial shape (convert all to Rect objects)
self.shape = []
for sh in shape:
bx = sh[0]*constants.BWIDTH + x
by = sh[1]*constants.BHEIGHT + y
block = pygame.Rect(bx,by,constants.BWIDTH,constants.BHEIGHT)
self.shape.append(block)
# Setup the rotation attribute
self.rotate_en = rotate_en
# Setup the rest of variables
self.x = x
self.y = y
# Movement in the X,Y coordinates
self.diffx = 0
self.diffy = 0
# Screen to drawn on
self.screen = screen
self.color = color
# Rotation of the screen
self.diff_rotation = 0
def draw(self):
"""
Draw the block from shape blocks. Each shape block
is filled with a color and black border.
"""
for bl in self.shape:
pygame.draw.rect(self.screen,self.color,bl)
pygame.draw.rect(self.screen,constants.BLACK,bl,constants.MESH_WIDTH)
def get_rotated(self,x,y):
"""
Compute the new coordinates based on the rotation angle.
Parameters:
- x - the X coordinate to transfer
- y - the Y coordinate to transfer
Returns the tuple with new (X,Y) coordinates.
"""
# Use the classic transformation matrix:
# https://www.siggraph.org/education/materials/HyperGraph/modeling/mod_tran/2drota.htm
rads = self.diff_rotation * (math.pi / 180.0)
newx = x*math.cos(rads) - y*math.sin(rads)
newy = y*math.cos(rads) + x*math.sin(rads)
return (newx,newy)
def move(self,x,y):
"""
Move all elements of the block using the given offset.
Parameters:
- x - movement in the X coordinate
- y - movement in the Y coordinate
"""
# Accumulate X,Y coordinates and call the update function
self.diffx += x
self.diffy += y
self._update()
def remove_blocks(self,y):
"""
Remove blocks on the Y coordinate. All blocks
above the Y are moved one step down.
Parameters:
- y - Y coordinate to work with.
"""
new_shape = []
for shape_i in range(len(self.shape)):
tmp_shape = self.shape[shape_i]
if tmp_shape.y < y:
# Block is above the y, move down and add it to the list of active shape
# blocks.
new_shape.append(tmp_shape)
tmp_shape.move_ip(0,constants.BHEIGHT)
elif tmp_shape.y > y:
# Block is below the y, add it to the list. The block doesn't need to be moved because
# the removed line is above it.
new_shape.append(tmp_shape)
# Setup the new list of block shapes.
self.shape = new_shape
def has_blocks(self):
"""
Returns true if the block has some shape blocks in the shape list.
"""
return True if len(self.shape) > 0 else False
def rotate(self):
"""
Setup the rotation value to 90 degrees.
"""
# Setup the rotation and update coordinates of all shape blocks.
# The block is rotated iff the rotation is enabled
if self.rotate_en:
self.diff_rotation = 90
self._update()
def _update(self):
"""
Update the position of all shape boxes.
"""
for bl in self.shape:
# Get old coordinates and compute new x,y coordinates.
# All rotation calculates are done in the original coordinates.
origX = (bl.x - self.x)/constants.BWIDTH
origY = (bl.y - self.y)/constants.BHEIGHT
rx,ry = self.get_rotated(origX,origY)
newX = rx*constants.BWIDTH + self.x + self.diffx
newY = ry*constants.BHEIGHT + self.y + self.diffy
# Compute the relative move
newPosX = newX - bl.x
newPosY = newY - bl.y
bl.move_ip(newPosX,newPosY)
# Everyhting was moved. Setup new x,y, coordinates and reset all disable the move
# variables.
self.x += self.diffx
self.y += self.diffy
self.diffx = 0
self.diffy = 0
self.diff_rotation = 0
def backup(self):
"""
Backup the current configuration of shape blocks.
"""
# Make the deep copy of the shape list. Also, remember
# the current configuration.
self.shape_copy = copy.deepcopy(self.shape)
self.x_copy = self.x
self.y_copy = self.y
self.rotation_copy = self.diff_rotation
def restore(self):
"""
Restore the previous configuraiton.
"""
self.shape = self.shape_copy
self.x = self.x_copy
self.y = self.y_copy
self.diff_rotation = self.rotation_copy
def check_collision(self,rect_list):
"""
The function checks if the block colides with any other block
in the shape list.
Parameters:
- rect_list - the function accepts the list of Rect object which
are used for the collistion detection.
"""
for blk in rect_list:
collist = blk.collidelistall(self.shape)
if len(collist):
return True
return False