Last active
February 13, 2019 14:01
-
-
Save gabrielflorit/ede19adb3a48ca0fd6b30818c5309f5b to your computer and use it in GitHub Desktop.
SCRIPT-8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"0": { | |
"0": { | |
"0": 2 | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// title: Break-9 | |
// BUGS: | |
// - diagonal hits shouldn't always reverse both directions | |
const e = { | |
paddle: { | |
width: 24, | |
height: 3, | |
speed: 3, | |
brake: 0.8 | |
}, | |
ball: { | |
radius: 2 | |
}, | |
maxlives: 3, | |
brick: { | |
width: 14, | |
height: 3, | |
color: 3 | |
} | |
} | |
const modes = { | |
start: 'start', | |
game: 'game', | |
gameover: 'gameover' | |
} | |
// called on init, and when the game is over | |
const defaultBricks = () => | |
flatten( | |
range(8).map(row => | |
range(8).map(col => ({ | |
x: 1 + col * (e.brick.width + 2), | |
y: 24 + row * (e.brick.height + 2), | |
color: e.brick.color, | |
destroyed: false | |
})) | |
) | |
) | |
// called on init, and when we lose a ball | |
const defaultBall = () => ({ | |
x: 64, | |
y: 98, | |
dx: -1, | |
dy: -1, | |
sticky: true | |
}) | |
// called when game is over | |
const resetGame = ({ state, bricks, paddle, ball }) => { | |
state.lives = e.maxlives | |
state.score = 0 | |
bricks.bricks = defaultBricks() | |
ball.sticky = true | |
paddle.x = 64 - e.paddle.width / 2 | |
} | |
initialState = { | |
collision: '', | |
mode: modes.start, | |
lives: e.maxlives, | |
score: 0, | |
actors: [ | |
{ | |
name: 'paddle', | |
x: 64 - e.paddle.width / 2, | |
y: 120 - e.paddle.height - 2, | |
dx: 0, | |
color: 2 | |
}, | |
{ | |
name: 'bricks', | |
bricks: defaultBricks() | |
}, | |
{ | |
name: 'ball', | |
color: 0, | |
...defaultBall() | |
} | |
] | |
} | |
const collider = { | |
lineLine (a, b) { | |
const [[ax1, ay1], [ax2, ay2]] = a | |
const [[bx1, by1], [bx2, by2]] = b | |
// calculate the distance to intersection point | |
const uA = | |
((bx2 - bx1) * (ay1 - by1) - (by2 - by1) * (ax1 - bx1)) / | |
((by2 - by1) * (ax2 - ax1) - (bx2 - bx1) * (ay2 - ay1)) | |
const uB = | |
((ax2 - ax1) * (ay1 - by1) - (ay2 - ay1) * (ax1 - bx1)) / | |
((by2 - by1) * (ax2 - ax1) - (bx2 - bx1) * (ay2 - ay1)) | |
// if uA and uB are between 0-1, lines are colliding | |
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) { | |
return true | |
} else { | |
return false | |
} | |
}, | |
lineRect ({ line, rect }) { | |
let collision = [] | |
if ( | |
line[1][0] >= rect.x && | |
line[1][0] <= rect.x + rect.width && | |
line[1][1] >= rect.y && | |
line[1][1] <= rect.y + rect.height | |
) { | |
const pointLine = [[line[0][0], line[0][1]], [line[1][0], line[1][1]]] | |
const leftLine = [[rect.x, rect.y], [rect.x, rect.y + rect.height]] | |
const rightLine = [ | |
[rect.x + rect.width, rect.y], | |
[rect.x + rect.width, rect.y + rect.height] | |
] | |
const topLine = [[rect.x, rect.y], [rect.x + rect.width, rect.y]] | |
const bottomLine = [ | |
[rect.x, rect.y + rect.height], | |
[rect.x + rect.width, rect.y + rect.height] | |
] | |
if (collider.lineLine(pointLine, leftLine)) collision.push('left') | |
if (collider.lineLine(pointLine, rightLine)) collision.push('right') | |
if (collider.lineLine(pointLine, topLine)) collision.push('top') | |
if (collider.lineLine(pointLine, bottomLine)) collision.push('bottom') | |
} | |
return collision | |
} | |
} | |
const bounceBallOffPaddle = ({ ball, paddle, state }) => { | |
const collision = collider.lineRect({ | |
line: [[ball.x - ball.dx, ball.y - ball.dy], [ball.x, ball.y]], | |
rect: { | |
...paddle, | |
width: e.paddle.width, | |
height: e.paddle.height | |
} | |
}) | |
if (collision.length === 1) { | |
const [single] = collision | |
if (single === 'left') ball.dx = -Math.abs(ball.dx) | |
if (single === 'right') ball.dx = Math.abs(ball.dx) | |
if (single === 'top') { | |
ball.dy = -Math.abs(ball.dy) | |
if (ball.x < paddle.x + e.paddle.width / 4) { | |
ball.dx -= 0.5 | |
} else if (ball.x > paddle.x + 3 * e.paddle.width / 4) { | |
ball.dx += 0.5 | |
} | |
} | |
if (single === 'bottom') ball.dy = Math.abs(ball.dy) | |
playPhrase(1) | |
} | |
if (collision.length === 2) { | |
ball.dy *= -1 | |
playPhrase(1) | |
} | |
state.collision = collision.join(',') | |
} | |
const movePaddle = ({ ball, paddle, input }) => { | |
// If we're pressing left or right, set paddle speed to max. | |
if (input.left && paddle.x > 0) { | |
paddle.dx = -e.paddle.speed | |
} else if (input.right && paddle.x < 128 - e.paddle.width) { | |
paddle.dx = e.paddle.speed | |
} else { | |
// Otherwise decelerate. | |
paddle.dx *= e.paddle.brake | |
} | |
// Move the paddle, | |
paddle.x = Math.round(paddle.dx + paddle.x) | |
// but don't let it go past the screen edges. | |
paddle.x = clamp(paddle.x, 0, 128 - e.paddle.width) | |
} | |
const bounceBallOffWalls = ball => { | |
if (ball.x <= e.ball.radius - 1) { | |
ball.dx = Math.abs(ball.dx) | |
playPhrase(0) | |
} | |
if (ball.x >= 127 - e.ball.radius + 1) { | |
ball.dx = -Math.abs(ball.dx) | |
playPhrase(0) | |
} | |
if (ball.y <= 11 - e.ball.radius - 1) { | |
ball.dy = Math.abs(ball.dx) | |
playPhrase(0) | |
} | |
} | |
const bounceBallOffBricks = ({ ball, bricks, state }) => { | |
bricks.bricks.filter(brick => !brick.destroyed).forEach(brick => { | |
const collision = collider.lineRect({ | |
line: [[ball.x - ball.dx, ball.y - ball.dy], [ball.x, ball.y]], | |
rect: { | |
...brick, | |
width: e.brick.width, | |
height: e.brick.height | |
} | |
}) | |
if (collision.length) { | |
if (collision.length === 1) { | |
const [single] = collision | |
if (single === 'left') ball.dx = -Math.abs(ball.dx) | |
if (single === 'right') ball.dx = Math.abs(ball.dx) | |
if (single === 'top') ball.dy = -Math.abs(ball.dy) | |
if (single === 'bottom') ball.dy = Math.abs(ball.dy) | |
} | |
if (collision.length === 2) { | |
ball.dy *= -1 | |
ball.dx *= -1 | |
} | |
state.score++ | |
playPhrase(3) | |
brick.destroyed = true | |
} | |
}) | |
} | |
const moveBall = ({ ball, paddle, input }) => { | |
if (ball.sticky) { | |
ball.x = paddle.x + e.paddle.width / 2 | |
ball.y = paddle.y - e.ball.radius | |
ball.dx = paddle.x + e.paddle.width / 2 < 64 ? 1 : -1 | |
ball.sticky = !input.a | |
} else { | |
ball.x = ball.x + ball.dx | |
ball.y = ball.y + ball.dy | |
} | |
} | |
const loseBall = ({ ball, state, paddle }) => { | |
if (ball.y >= 127) { | |
Object.entries(defaultBall()).forEach(([key, value]) => { | |
ball[key] = value | |
}) | |
state.lives-- | |
playPhrase(2) | |
} | |
} | |
update = (state, input) => { | |
if (state.mode === modes.game) { | |
const paddle = state.actors.find(d => d.name === 'paddle') | |
const ball = state.actors.find(d => d.name === 'ball') | |
const bricks = state.actors.find(d => d.name === 'bricks') | |
bounceBallOffWalls(ball) | |
bounceBallOffPaddle({ ball, paddle, state }) | |
bounceBallOffBricks({ ball, bricks, state }) | |
loseBall({ ball, paddle, state }) | |
movePaddle({ ball, paddle, input }) | |
moveBall({ ball, paddle, input }) | |
if (state.lives < 0) { | |
state.mode = modes.gameover | |
resetGame({ ball, paddle, bricks, state }) | |
} | |
} else if (state.mode === modes.start) { | |
if (input.start) { | |
state.mode = modes.game | |
} | |
} else if (state.mode === modes.gameover) { | |
if (input.start) { | |
state.mode = modes.game | |
} | |
} | |
} | |
const drawActor = (actor, fade) => { | |
if (actor.name === 'paddle') { | |
// Draw entire paddle. | |
rectFill( | |
actor.x, | |
actor.y, | |
e.paddle.width, | |
e.paddle.height, | |
actor.color + (fade ? 3 : 0) | |
) | |
// Draw inner highlight. | |
rectFill( | |
actor.x + 1, | |
actor.y + 1, | |
e.paddle.width - 2, | |
e.paddle.height - 2, | |
actor.color + (fade ? 4 : 2) | |
) | |
} | |
if (actor.name === 'bricks') { | |
actor.bricks.forEach(brick => { | |
if (!brick.destroyed) { | |
rectFill(brick.x, brick.y, e.brick.width, e.brick.height, brick.color) | |
} | |
}) | |
} | |
if (actor.name === 'ball') { | |
// Draw sticky preview. | |
if (actor.sticky) { | |
line(actor.x, actor.y, actor.x + 5 * actor.dx, actor.y + 5 * actor.dy, 4) | |
} | |
circFill(actor.x, actor.y, e.ball.radius, actor.color + (fade ? 3 : 0)) | |
} | |
} | |
drawActors = (state, fade) => { | |
state.actors.forEach(actor => drawActor(actor, fade)) | |
} | |
draw = state => { | |
if (state.mode === modes.start) { | |
clear() | |
print(51, 46, 'break-8', 0) | |
print(35, 95, 'left / right / a', 4) | |
print(44, 76, 'press start', 3) | |
// line(0, 0, 127, 127, 0) | |
// line(127, 0, 0, 127, 0) | |
} else if (state.mode === modes.game) { | |
clear() | |
print(51, 46, 'break-8', 0) | |
rectFill(0, 7, 128, 128, 6) | |
print(0, 120, state.collision, 0) | |
print(0, 0, `lives:${state.lives}`, 2) | |
print(40, 0, `score:${state.score}`, 2) | |
drawActors(state) | |
} else if (state.mode === modes.gameover) { | |
// clear() | |
rectFill(0, 64 - 12, 128, 24, 7) | |
print(47, 57, 'game over', 0) | |
print(43, 66, 'press start', 3) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"0": [ | |
"0c07" | |
], | |
"1": [ | |
"0c17" | |
], | |
"2": [ | |
"0b37", | |
"1d#37", | |
"2g27", | |
"3b17", | |
"4d#17", | |
"5g07", | |
"6c07" | |
], | |
"3": [ | |
"0g37" | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"0": { | |
"0": 0 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment