ebiten/examples/blocks/blocks/field.go

213 lines
4.5 KiB
Go
Raw Normal View History

// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2014-12-09 15:16:04 +01:00
2014-12-08 17:35:35 +01:00
package blocks
2013-12-18 19:21:25 +01:00
import (
2014-12-09 14:09:22 +01:00
"github.com/hajimehoshi/ebiten"
2013-12-18 19:21:25 +01:00
)
const maxFlushCount = 20
2014-12-29 09:44:06 +01:00
2013-12-18 19:21:25 +01:00
type Field struct {
2014-12-29 09:44:06 +01:00
blocks [fieldBlockNumX][fieldBlockNumY]BlockType
flushCount int
onEndFlushing func(int)
2013-12-18 19:21:25 +01:00
}
func NewField() *Field {
return &Field{}
}
2013-12-19 17:08:32 +01:00
func (f *Field) IsBlocked(x, y int) bool {
if x < 0 || fieldBlockNumX <= x {
return true
}
if y < 0 {
return false
}
2013-12-19 17:23:00 +01:00
if fieldBlockNumY <= y {
2013-12-19 17:08:32 +01:00
return true
}
return f.blocks[x][y] != BlockTypeNone
}
func (f *Field) collides(piece *Piece, x, y int, angle Angle) bool {
return piece.collides(f, x, y, angle)
}
func (f *Field) MovePieceToLeft(piece *Piece, x, y int, angle Angle) int {
if f.collides(piece, x-1, y, angle) {
return x
}
return x - 1
}
func (f *Field) MovePieceToRight(piece *Piece, x, y int, angle Angle) int {
if f.collides(piece, x+1, y, angle) {
return x
}
return x + 1
}
2013-12-19 19:03:35 +01:00
func (f *Field) PieceDroppable(piece *Piece, x, y int, angle Angle) bool {
return !f.collides(piece, x, y+1, angle)
}
2013-12-19 17:23:00 +01:00
func (f *Field) DropPiece(piece *Piece, x, y int, angle Angle) int {
if f.collides(piece, x, y+1, angle) {
return y
}
return y + 1
}
2013-12-19 17:08:32 +01:00
func (f *Field) RotatePieceRight(piece *Piece, x, y int, angle Angle) Angle {
if f.collides(piece, x, y, angle.RotateRight()) {
return angle
}
return angle.RotateRight()
}
2015-01-07 16:50:02 +01:00
func (f *Field) RotatePieceLeft(piece *Piece, x, y int, angle Angle) Angle {
if f.collides(piece, x, y, angle.RotateLeft()) {
return angle
}
return angle.RotateLeft()
}
2014-12-29 09:44:06 +01:00
func (f *Field) AbsorbPiece(piece *Piece, x, y int, angle Angle) {
2013-12-26 16:37:36 +01:00
piece.AbsorbInto(f, x, y, angle)
2014-12-29 09:44:06 +01:00
if f.flushable() {
f.flushCount = maxFlushCount
}
}
func (f *Field) Flushing() bool {
return 0 < f.flushCount
}
func (f *Field) SetEndFlushing(fn func(lines int)) {
f.onEndFlushing = fn
}
func (f *Field) flushable() bool {
for j := fieldBlockNumY - 1; 0 <= j; j-- {
if f.flushableLine(j) {
return true
}
}
return false
}
func (f *Field) flushableLine(j int) bool {
for i := 0; i < fieldBlockNumX; i++ {
if f.blocks[i][j] == BlockTypeNone {
return false
}
}
return true
2013-12-26 16:37:36 +01:00
}
2014-12-07 16:07:36 +01:00
func (f *Field) setBlock(x, y int, blockType BlockType) {
f.blocks[x][y] = blockType
}
2014-12-29 09:44:06 +01:00
func (f *Field) endFlushing() int {
2014-12-07 16:07:36 +01:00
flushedLineNum := 0
for j := fieldBlockNumY - 1; 0 <= j; j-- {
if f.flushLine(j + flushedLineNum) {
flushedLineNum++
}
}
2014-12-29 07:24:20 +01:00
return flushedLineNum
2014-12-07 16:07:36 +01:00
}
2013-12-26 16:37:36 +01:00
func (f *Field) flushLine(j int) bool {
for i := 0; i < fieldBlockNumX; i++ {
if f.blocks[i][j] == BlockTypeNone {
return false
}
}
for j2 := j; 1 <= j2; j2-- {
for i := 0; i < fieldBlockNumX; i++ {
f.blocks[i][j2] = f.blocks[i][j2-1]
}
}
for i := 0; i < fieldBlockNumX; i++ {
f.blocks[i][0] = BlockTypeNone
}
return true
}
2014-12-29 09:44:06 +01:00
func (f *Field) Update() error {
if 0 <= f.flushCount {
f.flushCount--
if f.flushCount == 0 {
l := f.endFlushing()
if f.onEndFlushing != nil {
f.onEndFlushing(l)
}
}
}
return nil
}
2016-02-15 20:31:20 +01:00
func min(a, b float64) float64 {
if a > b {
return b
}
return a
}
2014-12-29 09:44:06 +01:00
func (f *Field) flushingColor() ebiten.ColorM {
2016-02-15 20:31:20 +01:00
c := f.flushCount
if c < 0 {
c = 0
}
rate := float64(c) / maxFlushCount
2014-12-29 09:44:06 +01:00
clr := ebiten.ColorM{}
2016-02-15 20:31:20 +01:00
alpha := min(1, rate*2)
clr.Scale(1, 1, 1, alpha)
2016-02-15 20:31:20 +01:00
r := min(1, (1-rate)*2)
clr.Translate(r, 0, 0, 0)
2014-12-29 09:44:06 +01:00
return clr
}
2014-12-29 05:36:29 +01:00
func (f *Field) Draw(r *ebiten.Image, x, y int) error {
2013-12-18 19:21:25 +01:00
blocks := make([][]BlockType, len(f.blocks))
2014-12-29 09:44:06 +01:00
flushingBlocks := make([][]BlockType, len(f.blocks))
for i := 0; i < fieldBlockNumX; i++ {
blocks[i] = make([]BlockType, fieldBlockNumY)
flushingBlocks[i] = make([]BlockType, fieldBlockNumY)
}
for j := 0; j < fieldBlockNumY; j++ {
if f.flushableLine(j) {
for i := 0; i < fieldBlockNumX; i++ {
flushingBlocks[i][j] = f.blocks[i][j]
}
} else {
for i := 0; i < fieldBlockNumX; i++ {
blocks[i][j] = f.blocks[i][j]
}
}
}
if err := drawBlocks(r, blocks, x, y, ebiten.ColorM{}); err != nil {
return err
}
if err := drawBlocks(r, flushingBlocks, x, y, f.flushingColor()); err != nil {
return err
2013-12-18 19:21:25 +01:00
}
2014-12-29 09:44:06 +01:00
return nil
2013-12-18 19:21:25 +01:00
}