local Object = require 'libs.classic' local Piece = Object:extend() local drawBlock = require 'tetris.components.draw_block' function Piece:new(shape, rotation, position, block_offsets, gravity, lock_delay, skin, colour, big) self.shape = shape self.rotation = rotation self.position = position self.block_offsets = block_offsets self.gravity = gravity self.lock_delay = lock_delay self.skin = skin self.colour = colour self.ghost = false self.locked = false self.big = big end -- Functions that return a new piece to test in rotation systems. function Piece:withOffset(offset) return Piece( self.shape, self.rotation, {x = self.position.x + offset.x, y = self.position.y + offset.y}, self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big ) end function Piece:withRelativeRotation(rot) local new_rot = self.rotation + rot while new_rot < 0 do new_rot = new_rot + 4 end while new_rot >= 4 do new_rot = new_rot - 4 end return Piece( self.shape, new_rot, self.position, self.block_offsets, self.gravity, self.lock_delay, self.skin, self.colour, self.big ) end -- Functions that return predicates relative to a grid. function Piece:getBlockOffsets() return self.block_offsets[self.shape][self.rotation + 1] end function Piece:occupiesSquare(x, y) local offsets = self:getBlockOffsets() for index, offset in pairs(offsets) do local new_offset = {x = self.position.x + offset.x, y = self.position.y + offset.y} if new_offset.x == x and new_offset.y == y then return true end end return false end function Piece:isMoveBlocked(grid, offset) local moved_piece = self:withOffset(offset) return not grid:canPlacePiece(moved_piece) end function Piece:isDropBlocked(grid) return self:isMoveBlocked(grid, { x=0, y=1 }) end -- Procedures to actually do stuff to pieces. function Piece:setOffset(offset) self.position.x = self.position.x + offset.x self.position.y = self.position.y + offset.y return self end function Piece:setRelativeRotation(rot) new_rot = self.rotation + rot while new_rot < 0 do new_rot = new_rot + 4 end while new_rot >= 4 do new_rot = new_rot - 4 end self.rotation = new_rot return self end function Piece:moveInGrid(step, squares, grid, instant) local moved = false for x = 1, squares do if grid:canPlacePiece(self:withOffset(step)) then moved = true self:setOffset(step) if instant then self:dropToBottom(grid) end else break end end if moved and step.y == 0 then playSE("move") end return self end function Piece:dropSquares(dropped_squares, grid) self:moveInGrid({ x = 0, y = 1 }, dropped_squares, grid) end function Piece:dropToBottom(grid) local piece_y = self.position.y self:dropSquares(math.huge, grid) self.gravity = 0 if self.position.y > piece_y then if self.ghost == false then playSE("bottom") end -- self.lock_delay = 0 end return self end function Piece:lockIfBottomed(grid) if self:isDropBlocked(grid) then self.locked = true end return self end function Piece:addGravity(gravity, grid, classic_lock) gravity = gravity / (self.big and 2 or 1) local new_gravity = self.gravity + gravity if self:isDropBlocked(grid) then if classic_lock then self.gravity = new_gravity else self.gravity = 0 self.lock_delay = self.lock_delay + 1 end elseif not ( self:isMoveBlocked(grid, { x=0, y=-1 }) and gravity < 0 ) then local dropped_squares = math.floor(math.abs(new_gravity)) if gravity >= 0 then local new_frac_gravity = new_gravity - dropped_squares self.gravity = new_frac_gravity self:dropSquares(dropped_squares, grid) if self:isDropBlocked(grid) then playSE("bottom") end else local new_frac_gravity = new_gravity + dropped_squares self.gravity = new_frac_gravity self:moveInGrid({ x=0, y=-1 }, dropped_squares, grid) if self:isMoveBlocked(grid, { x=0, y=-1 }) then playSE("bottom") end end else self.gravity = 0 end return self end -- Procedures for drawing. function Piece:draw(opacity, brightness, grid, partial_das) if opacity == nil then opacity = 1 end if brightness == nil then brightness = 1 end local offsets = self:getBlockOffsets() local gravity_offset = 0 if config.gamesettings.smooth_movement == 1 and grid ~= nil and not self:isDropBlocked(grid) then gravity_offset = self.gravity * 16 end if partial_das == nil then partial_das = 0 end for index, offset in pairs(offsets) do local x = self.position.x + offset.x local y = self.position.y + offset.y local scale = self.big and 2 or 1 if grid ~= nil then drawBlock( brightness, brightness, brightness, opacity, blocks[self.skin][self.colour], 64 + (x*16+partial_das)*scale, 16 + (y*16+gravity_offset)*scale, 48, 48+(grid.width+1)*16, 0, (grid.height+1)*16, self.big ) else drawBlock( brightness, brightness, brightness, opacity, blocks[self.skin][self.colour], 64 + (x*16+partial_das)*scale, 16 + (y*16+gravity_offset)*scale, false, false, false, false, self.big ) end end return false end return Piece