251 lines
7.7 KiB
JavaScript
251 lines
7.7 KiB
JavaScript
import {
|
||
getMoveDownValue,
|
||
getLandBlockVelocity,
|
||
getSwingBlockVelocity,
|
||
touchEventHandler,
|
||
addSuccessCount,
|
||
addFailedCount,
|
||
addScore
|
||
} from './utils'
|
||
import * as constant from './constant'
|
||
|
||
const checkCollision = (block, line) => {
|
||
// 0 goon 1 drop 2 rotate left 3 rotate right 4 ok 5 perfect
|
||
if (block.y + block.height >= line.y) {
|
||
if (block.x < line.x - block.calWidth || block.x > line.collisionX + block.calWidth) {
|
||
return 1
|
||
}
|
||
if (block.x < line.x) {
|
||
return 2
|
||
}
|
||
if (block.x > line.collisionX) {
|
||
return 3
|
||
}
|
||
if (block.x > line.x + (block.calWidth * 0.8) && block.x < line.x + (block.calWidth * 1.2)) {
|
||
// -10% +10%
|
||
return 5
|
||
}
|
||
return 4
|
||
}
|
||
return 0
|
||
}
|
||
const swing = (instance, engine, time) => {
|
||
const ropeHeight = engine.getVariable(constant.ropeHeight)
|
||
if (instance.status !== constant.swing) return
|
||
const i = instance
|
||
const initialAngle = engine.getVariable(constant.initialAngle)
|
||
i.angle = initialAngle *
|
||
getSwingBlockVelocity(engine, time)
|
||
i.weightX = i.x +
|
||
(Math.sin(i.angle) * ropeHeight)
|
||
i.weightY = i.y +
|
||
(Math.cos(i.angle) * ropeHeight)
|
||
}
|
||
|
||
const checkBlockOut = (instance, engine) => {
|
||
if (instance.status === constant.rotateLeft) {
|
||
// 左转 要等右上角消失才算消失
|
||
if (instance.y - instance.width >= engine.height) {
|
||
instance.visible = false
|
||
instance.status = constant.out
|
||
addFailedCount(engine)
|
||
}
|
||
} else if (instance.y >= engine.height) {
|
||
instance.visible = false
|
||
instance.status = constant.out
|
||
addFailedCount(engine)
|
||
}
|
||
}
|
||
|
||
export const blockAction = (instance, engine, time) => {
|
||
const i = instance
|
||
const ropeHeight = engine.getVariable(constant.ropeHeight)
|
||
if (!i.visible) {
|
||
return
|
||
}
|
||
if (!i.ready) {
|
||
i.ready = true
|
||
i.status = constant.swing
|
||
instance.updateWidth(engine.getVariable(constant.blockWidth))
|
||
instance.updateHeight(engine.getVariable(constant.blockHeight))
|
||
instance.x = engine.width / 2
|
||
instance.y = ropeHeight * -1.5
|
||
}
|
||
const line = engine.getInstance('line')
|
||
switch (i.status) {
|
||
case constant.swing:
|
||
engine.getTimeMovement(
|
||
constant.hookDownMovement,
|
||
[[instance.y, instance.y + ropeHeight]],
|
||
(value) => {
|
||
instance.y = value
|
||
},
|
||
{
|
||
name: 'block'
|
||
}
|
||
)
|
||
swing(instance, engine, time)
|
||
break
|
||
case constant.beforeDrop:
|
||
i.x = instance.weightX - instance.calWidth
|
||
i.y = instance.weightY + (0.3 * instance.height) // add rope height
|
||
i.rotate = 0
|
||
i.ay = engine.pixelsPerFrame(0.0003 * engine.height) // acceleration of gravity
|
||
i.startDropTime = time
|
||
i.status = constant.drop
|
||
break
|
||
case constant.drop:
|
||
const deltaTime = time - i.startDropTime
|
||
i.startDropTime = time
|
||
i.vy += i.ay * deltaTime
|
||
i.y += (i.vy * deltaTime) + (0.5 * i.ay * (deltaTime ** 2))
|
||
const collision = checkCollision(instance, line)
|
||
const blockY = line.y - instance.height
|
||
const calRotate = (ins) => {
|
||
ins.originOutwardAngle = Math.atan(ins.height / ins.outwardOffset)
|
||
ins.originHypotenuse = Math.sqrt((ins.height ** 2)
|
||
+ (ins.outwardOffset ** 2))
|
||
engine.playAudio('rotate')
|
||
}
|
||
switch (collision) {
|
||
case 1:
|
||
checkBlockOut(instance, engine)
|
||
break
|
||
case 2:
|
||
i.status = constant.rotateLeft
|
||
instance.y = blockY
|
||
instance.outwardOffset = (line.x + instance.calWidth) - instance.x
|
||
calRotate(instance)
|
||
break
|
||
case 3:
|
||
i.status = constant.rotateRight
|
||
instance.y = blockY
|
||
instance.outwardOffset = (line.collisionX + instance.calWidth) - instance.x
|
||
calRotate(instance)
|
||
break
|
||
case 4:
|
||
case 5:
|
||
i.status = constant.land
|
||
const lastSuccessCount = engine.getVariable(constant.successCount)
|
||
addSuccessCount(engine)
|
||
engine.setTimeMovement(constant.moveDownMovement, 500)
|
||
if (lastSuccessCount === 10 || lastSuccessCount === 15) {
|
||
engine.setTimeMovement(constant.lightningMovement, 150)
|
||
}
|
||
instance.y = blockY
|
||
line.y = blockY
|
||
line.x = i.x - i.calWidth
|
||
line.collisionX = line.x + i.width
|
||
// 作弊检测 超出左边或右边1/3
|
||
const cheatWidth = i.width * 0.3
|
||
if (i.x > engine.width - (cheatWidth * 2)
|
||
|| i.x < -cheatWidth) {
|
||
engine.setVariable(constant.hardMode, true)
|
||
}
|
||
if (collision === 5) {
|
||
instance.perfect = true
|
||
addScore(engine, true)
|
||
engine.playAudio('drop-perfect')
|
||
} else {
|
||
addScore(engine)
|
||
engine.playAudio('drop')
|
||
}
|
||
break
|
||
default:
|
||
break
|
||
}
|
||
break
|
||
case constant.land:
|
||
engine.getTimeMovement(
|
||
constant.moveDownMovement,
|
||
[[instance.y, instance.y + (getMoveDownValue(engine, { pixelsPerFrame: s => s / 2 }))]],
|
||
(value) => {
|
||
if (!instance.visible) return
|
||
instance.y = value
|
||
if (instance.y > engine.height) {
|
||
instance.visible = false
|
||
}
|
||
},
|
||
{
|
||
name: instance.name
|
||
}
|
||
)
|
||
instance.x += getLandBlockVelocity(engine, time)
|
||
break
|
||
case constant.rotateLeft:
|
||
case constant.rotateRight:
|
||
const isRight = i.status === constant.rotateRight
|
||
const rotateSpeed = engine.pixelsPerFrame(Math.PI * 4)
|
||
const isShouldFall = isRight ? instance.rotate > 1.3 : instance.rotate < -1.3// 75度
|
||
const leftFix = isRight ? 1 : -1
|
||
if (isShouldFall) {
|
||
instance.rotate += (rotateSpeed / 8) * leftFix
|
||
instance.y += engine.pixelsPerFrame(engine.height * 0.7)
|
||
instance.x += engine.pixelsPerFrame(engine.width * 0.3) * leftFix
|
||
} else {
|
||
let rotateRatio = (instance.calWidth - instance.outwardOffset)
|
||
/ instance.calWidth
|
||
rotateRatio = rotateRatio > 0.5 ? rotateRatio : 0.5
|
||
instance.rotate += rotateSpeed * rotateRatio * leftFix
|
||
const angle = instance.originOutwardAngle + instance.rotate
|
||
const rotateAxisX = isRight ? line.collisionX + instance.calWidth
|
||
: line.x + instance.calWidth
|
||
const rotateAxisY = line.y
|
||
instance.x = rotateAxisX -
|
||
(Math.cos(angle) * instance.originHypotenuse)
|
||
instance.y = rotateAxisY -
|
||
(Math.sin(angle) * instance.originHypotenuse)
|
||
}
|
||
checkBlockOut(instance, engine)
|
||
break
|
||
default:
|
||
break
|
||
}
|
||
}
|
||
|
||
const drawSwingBlock = (instance, engine) => {
|
||
const bl = engine.getImg('blockRope')
|
||
engine.ctx.drawImage(
|
||
bl, instance.weightX - instance.calWidth
|
||
, instance.weightY
|
||
, instance.width, instance.height * 1.3
|
||
)
|
||
const leftX = instance.weightX - instance.calWidth
|
||
engine.debugLineY(leftX)
|
||
}
|
||
|
||
const drawBlock = (instance, engine) => {
|
||
const { perfect } = instance
|
||
const bl = engine.getImg(perfect ? 'block-perfect' : 'block')
|
||
engine.ctx.drawImage(bl, instance.x, instance.y, instance.width, instance.height)
|
||
}
|
||
|
||
const drawRotatedBlock = (instance, engine) => {
|
||
const { ctx } = engine
|
||
ctx.save()
|
||
ctx.translate(instance.x, instance.y)
|
||
ctx.rotate(instance.rotate)
|
||
ctx.translate(-instance.x, -instance.y)
|
||
drawBlock(instance, engine)
|
||
ctx.restore()
|
||
}
|
||
|
||
export const blockPainter = (instance, engine) => {
|
||
const { status } = instance
|
||
switch (status) {
|
||
case constant.swing:
|
||
drawSwingBlock(instance, engine)
|
||
break
|
||
case constant.drop:
|
||
case constant.land:
|
||
drawBlock(instance, engine)
|
||
break
|
||
case constant.rotateLeft:
|
||
case constant.rotateRight:
|
||
drawRotatedBlock(instance, engine)
|
||
break
|
||
default:
|
||
break
|
||
}
|
||
}
|