Skip to content

Instantly share code, notes, and snippets.

@plugnburn
Last active June 14, 2016 09:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save plugnburn/daba358402f595b349aa0ba14afb6d69 to your computer and use it in GitHub Desktop.
Save plugnburn/daba358402f595b349aa0ba14afb6d69 to your computer and use it in GitHub Desktop.
Arc Escape: Super Hexagon for the very poor, see complete compressed game at http://arc-escape.surge.sh
function T() {
var track = 'w=t>>9,k=32,m=2048,a=1-((t/m)%1),d=(((14*t*t)^t)%m)*a,y=([3,3,4.7,2][p=w/k&3])*(t/4),h=((("IQNNNN!!]]!Q!IW]WQNN??!!W]WQNNN?".charCodeAt(w/2&15|p/3<<4))/33)*t)-t,s=y*.98%80+y%80+(w>>7&&a*((5*t%m*a&128)*(0x53232323>>w/4&1)+(d&127)*(0xa444c444>>w/4&1)*1.5+(d*w&1)+(h%k+h*1.99%k+h*.49%k+h*.97%k-64)*(4-(2*a)))),(s*s>>14?127:s)+128',
duration = 120,
beat=function(b,e,a,t){return(a="data")+":audio/wav;base64,UklGRl9fX19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgA"+btoa(eval("for(t=0;++t<e*8e3;)a+=String.fromCharCode(255&("+b+"))"))}
return beat(track, duration)
}
!function() {
var rAF = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.mozRequestAnimationFrame,
aud, screenHeight = 0, screenWidth = 0, minSide = 0, maxSide = 0, centerX = 0, centerY = 0, realLoop = null, score = 0, baseSpeed = 0, speed = 0,
radUnit = Math.PI / 180,
gameAction = 0, //gameAction: 0 - nothing, 1 - left, 2 - right
gameMode = 1, //gameMode: 0 - gameOver, 1 - normal
playerAngle = 0, //arrow angle in deg
spinAngle = 0, //currentSpinAngle in deg
spinDelta = 1, //spin angle per frame
spinDir = 1, //spin direction (1 or -1)
bgColor = '#030303', //background color
wallColor = 'cyan', //wall color
wallWidth = 10, //wall width in px
arrowColor = 'yellow', //player arrow color
arrowAngleSize = 18, //arrow angle size in deg
baseColor = 'lightgreen', //player base color
baseWidth = 10, //base width in px
wallThreshold = 0, //threshold until the next wall is generated
walls = []; //walls (up to 4)
function mod3(x) {
while(x<0) x+=360;
while(x>360) x-=360;
return x
}
function angleBetween(angle, start, end){
return mod3(angle - start) <= mod3(end - start)
}
// render loop
function gameLoop(C) {
if(gameAction) playerAngle += 10 * (gameAction - 1.5)
//draw bg
C.fillStyle = bgColor
C.fillRect(0, 0, screenWidth, screenHeight)
//define arrow angle
var arrowStartAngle = spinAngle + playerAngle,
arrowEndAngle = spinAngle + playerAngle + arrowAngleSize,
baseRadius = minSide / 9;
C.font = (baseRadius/1.5|0)+'px sans'
C.textAlign = 'center'
C.textBaseline = 'middle'
//hangle gameover mode
if(!gameMode) {
var bestScore = localStorage.getItem('best')|0
if(score > bestScore) {
bestScore = score
localStorage.setItem('best', score)
}
C.font = (baseRadius*1.2|0)+'px sans'
C.fillStyle = 'cyan'
C.fillText('GAME OVER', centerX, centerY-baseRadius*2)
C.font = (baseRadius/1.5|0)+'px sans'
C.fillStyle = 'yellow'
C.fillText('Your score: '+score, centerX, centerY-baseRadius/2)
C.fillText('Best score: '+bestScore, centerX, centerY+baseRadius/2)
C.font = (baseRadius/2.5|0)+'px sans'
C.fillStyle = 'lightgreen'
C.fillText('Press SPACE or tap to restart', centerX, centerY+2*baseRadius)
if(gameAction) window.location.reload();
else rAF(realLoop);
return
}
//draw player base
C.beginPath()
C.strokeStyle = baseColor
C.lineWidth = baseWidth
C.arc(centerX, centerY, baseRadius, 0, Math.PI*2)
C.stroke()
//draw player arrow
C.strokeStyle = C.fillStyle = arrowColor
var q = 10, i, k, lw = baseWidth/q;
C.lineWidth = lw
for(i=0;i<q;i++) {
var angleDelta = arrowAngleSize * i/q * 0.5;
C.beginPath()
C.arc(centerX, centerY, baseRadius + baseWidth * 0.5 + i*lw, radUnit*(arrowStartAngle + angleDelta), radUnit*(arrowEndAngle - angleDelta))
C.stroke()
}
//draw arc walls
if(!walls.length || walls[walls.length - 1].r < maxSide - wallThreshold)
walls.push({a:Math.ceil(Math.random()*180)|0, r:maxSide, s:Math.ceil(Math.random()*4)})
for(i=0,q=walls.length;i<q;i++) {
if(walls[i].r <= baseRadius + baseWidth) { //collision or not?
var coll = true, baseWallAngle = walls[i].a, steadyWallAngle = walls[i].s > 1 ? 180/walls[i].s|0 : 315;
for(k=0;k<walls[i].s;k++) {
baseWallAngle += steadyWallAngle
if(angleBetween(playerAngle, baseWallAngle, (baseWallAngle + (walls[i].s > 1 ? steadyWallAngle : 45)))) {
coll = false
break
}
baseWallAngle += steadyWallAngle
}
if(coll) { //collision
gameMode = 0
gameAction = 0
aud.pause()
rAF(realLoop)
return;
}
else { //no collision
score++
if(score % 20 === 0) spinDir = -spinDir
speed = baseSpeed * (1 + score / 64)
walls[i].r = null
}
}
else {
walls[i].r -= speed
C.strokeStyle = wallColor
C.lineWidth = wallWidth
var baseWallAngle = walls[i].a, steadyWallAngle = walls[i].s > 1 ? 180/walls[i].s|0 : 315
for(k=0;k<walls[i].s;k++) {
C.beginPath()
C.arc(centerX, centerY, walls[i].r, (baseWallAngle + spinAngle)*radUnit, (baseWallAngle + spinAngle + steadyWallAngle)*radUnit)
C.stroke()
baseWallAngle += steadyWallAngle*2
}
}
}
var newWalls = []
for(i=0;i<q;i++)
if(walls[i].r!=null)
newWalls.push(walls[i])
walls = newWalls
C.fillText(score, centerX, centerY)
spinAngle = mod3(spinAngle + spinDelta * spinDir)
rAF(realLoop)
}
//keyboard events
addEventListener('keydown', function(e){
if(gameMode) {
if(e.which === 37 || e.which === 65) gameAction = 1
else if(e.which === 39 || e.which === 68) gameAction = 2
}
else if(e.which === 32) gameAction = 1
})
addEventListener('keyup', function(e){gameAction = 0})
//touch events
addEventListener('touchstart', function(e){
if(e.touches.length === 1) {
e.preventDefault()
var t = e.touches[0], tX = t.clientX
gameAction = (t.clientX > screenWidth >> 1) ? 2 : 1
}
else gameAction = 0
})
addEventListener('touchend', function(e){e.preventDefault();gameAction = 0});
//init sequence
var screenInit = function() {
Z.width = screenWidth = document.documentElement.clientWidth
Z.height = screenHeight = document.documentElement.clientHeight
minSide = Math.min(screenWidth, screenHeight)
maxSide = Math.max(screenWidth, screenHeight)
centerX = screenWidth >> 1
centerY = screenHeight >> 1
wallThreshold = maxSide / 6
baseSpeed = Math.ceil(maxSide / 320)
if(!speed) speed = baseSpeed
}
addEventListener('resize', screenInit)
addEventListener('orientationchange', screenInit)
screenInit();
//draw loading screen
var C = Z.getContext('2d')
C.fillStyle = bgColor
C.fillRect(0, 0, screenWidth, screenHeight)
C.font = (minSide/9|0)+'px sans'
C.textAlign = 'center'
C.textBaseline = 'middle'
C.fillStyle = 'yellow'
C.fillText('LOADING...', centerX, centerY)
setTimeout(function(){
//init the soundtrack
var audUrl
if(!(audUrl = localStorage.getItem('sound')))
localStorage.setItem('sound', audUrl = T())
aud = new Audio(audUrl)
aud.loop = true
aud.volume = 0.5
aud.play()
// run the loop
;(realLoop = gameLoop.bind(null, Z.getContext('2d')))()
}, 10)
}()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment