You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
18 KiB
398 lines
18 KiB
console.clear(); |
|
var GAME_STATES; |
|
(function (GAME_STATES) { |
|
GAME_STATES["ready"] = "READY"; |
|
GAME_STATES["playing"] = "PLAYING"; |
|
GAME_STATES["ended"] = "ENDED"; |
|
GAME_STATES["paused"] = "PAUSED"; |
|
})(GAME_STATES || (GAME_STATES = {})); |
|
var SOUND; |
|
(function (SOUND) { |
|
SOUND["move"] = "move"; |
|
SOUND["dead"] = "dead"; |
|
SOUND["collect"] = "collect"; |
|
SOUND["start"] = "start"; |
|
})(SOUND || (SOUND = {})); |
|
var App = /** @class */ (function () { |
|
function App() { |
|
this.setupUI(); |
|
this.setupGame(); |
|
} |
|
App.prototype.setupUI = function () { |
|
var _this = this; |
|
this.score = document.getElementById('score'); |
|
this.container = document.getElementById('container'); |
|
this.boardContainer = document.getElementById('board-container'); |
|
var startButton = Rx.Observable.fromEvent(document.getElementById('start-button'), 'click'); |
|
|
|
startButton.subscribe(function (e) { console.log('click'); _this.startGame(); }); |
|
}; |
|
App.prototype.setupGame = function () { |
|
var _this = this; |
|
var board = document.getElementById('board'); |
|
this.game = new Snake(board); |
|
this.game.score.subscribe(function (score) { return _this.score.innerHTML = String(score); }); |
|
this.game.state.subscribe(function (state) { |
|
_this.gameState = state; |
|
_this.container.setAttribute('class', state); |
|
}); |
|
this.game.direction.subscribe(function (direction) { return _this.boardContainer.setAttribute('class', direction); }); |
|
this.game.reset(); |
|
}; |
|
App.prototype.startGame = function () { |
|
if (this.gameState == GAME_STATES.ready || this.gameState == GAME_STATES.ended) { |
|
this.game.start(); |
|
} |
|
}; |
|
return App; |
|
}()); |
|
var Snake = /** @class */ (function () { |
|
function Snake(boardElement) { |
|
var _this = this; |
|
this.SETTINGS = { |
|
grid: { size: 10, rows: 20, columns: 28 }, |
|
game: { scoreIncrement: 10 }, |
|
snake: { startLength: 3, startSpeed: 300, speedIncrement: 10, minSpeed: 100, growBy: 2 } |
|
}; |
|
this.DIRECTION = { |
|
up: { name: 'up', x: 0, y: -1 }, |
|
down: { name: 'down', x: 0, y: 1 }, |
|
left: { name: 'left', x: -1, y: 0 }, |
|
right: { name: 'right', x: 1, y: 0 } |
|
}; |
|
this.states = { |
|
direction: this.DIRECTION.up, |
|
nextDirection: [this.DIRECTION.up], |
|
speed: 0, |
|
game: GAME_STATES.ready, |
|
timeStamp: 0, |
|
snakeLength: 0, |
|
score: 0 |
|
}; |
|
//http://loov.io/jsfx |
|
this.sfxLibrary = { |
|
"start": { |
|
"Frequency": { "Start": 463.2977575242697, "Slide": 0.4268311992714056, "RepeatSpeed": 0.6870767779635416 }, |
|
"Generator": { "A": 0.015696072909390766 }, |
|
"Volume": { "Sustain": 0.11353385475559997, "Decay": 0.15242709930669884 } |
|
}, |
|
"collect1": { |
|
"Frequency": { "Start": 1183.9224793246758, "ChangeSpeed": 0.12793431035602038, "ChangeAmount": 4.8612434857196085 }, |
|
"Volume": { "Sustain": 0.011448880380128946, "Decay": 0.3895997546965799, "Punch": 0.4554389528366015 } |
|
}, |
|
"collect2": { |
|
"Frequency": { "Start": 1070.9337014976563, "ChangeSpeed": 0.1375978771153015, "ChangeAmount": 5.9409661118536246 }, |
|
"Volume": { "Sustain": 0.04890791064198004, "Decay": 0.3415421194668815, "Punch": 0.46291381941601983 } |
|
}, |
|
"dead": { |
|
"Frequency": { "Start": 194.70758491034655, "Slide": -0.011628522004559189, "ChangeSpeed": 0.6591296059731018, "ChangeAmount": 2.6287197798189297 }, |
|
"Generator": { "Func": "noise" }, |
|
"Volume": { "Sustain": 0.17655222296084297, "Decay": 0.24077933399701645, "Punch": 0.6485369099751499 } |
|
}, |
|
"move1": { |
|
"Frequency": { "Start": 452, "Slide": -0.04, "Min": 30, "DeltaSlide": -0.05 }, |
|
"Generator": { "Func": "sine", "A": 0.08999657142884616, "ASlide": 0.3390436675524937 }, |
|
"Filter": { "HP": 0.10068425608105215 }, |
|
"Volume": { "Sustain": 0, "Decay": 0.041, "Attack": 0.011, "Punch": 0.04, "Master": 0.18 } |
|
}, |
|
"move2": { |
|
"Frequency": { "Start": 452, "Slide": -0.01, "Min": 30, "DeltaSlide": -0.05 }, |
|
"Generator": { "Func": "sine", "A": 0.08999657142884616, "ASlide": 0.3390436675524937 }, |
|
"Filter": { "HP": 0.26, "LPResonance": 0, "HPSlide": 0.35, "LPSlide": 0.51, "LP": 1 }, |
|
"Volume": { "Sustain": 0.02, "Decay": 0.001, "Attack": 0.021, "Punch": 0.05, "Master": 0.18 }, |
|
"Phaser": { "Offset": -0.03, "Sweep": -0.02 }, |
|
"Vibrato": { "FrequencySlide": 0.04, "Frequency": 14.01, "Depth": 0.06 } |
|
}, |
|
"move3": { |
|
"Frequency": { "Start": 452, "Slide": -0.01, "Min": 30, "DeltaSlide": -0.05 }, |
|
"Generator": { "Func": "sine", "A": 0.08999657142884616, "ASlide": 0.3390436675524937 }, |
|
"Filter": { "HP": 0.26, "LPResonance": 0, "HPSlide": 0.35, "LPSlide": 0.51, "LP": 1 }, |
|
"Volume": { "Sustain": 0.02, "Decay": 0.001, "Attack": 0.021, "Punch": 0.05, "Master": 0.18 }, |
|
"Phaser": { "Offset": -0.03, "Sweep": -0.02 }, |
|
"Vibrato": { "FrequencySlide": 0.04, "Frequency": 14.01, "Depth": 0.16 } |
|
}, |
|
"move4": { |
|
"Frequency": { "Start": 452, "Slide": -0.01, "Min": 30, "DeltaSlide": -0.05 }, |
|
"Generator": { "Func": "sine", "A": 0.08999657142884616, "ASlide": 0.3390436675524937 }, |
|
"Filter": { "HP": 0.26, "LPResonance": 0, "HPSlide": 0.35, "LPSlide": 0.51, "LP": 1 }, |
|
"Volume": { "Sustain": 0.02, "Decay": 0.001, "Attack": 0.021, "Punch": 0.05, "Master": 0.18 }, |
|
"Phaser": { "Offset": -0.03, "Sweep": -0.02 }, |
|
"Vibrato": { "FrequencySlide": 0.04, "Frequency": 14.01, "Depth": 0.27 } |
|
} |
|
}; |
|
this.player = jsfx.Sounds(this.sfxLibrary); |
|
this.sounds = { |
|
collect: ['collect1', 'collect2'], |
|
dead: ['dead'], |
|
start: ['start'], |
|
move: ['move1', 'move2', 'move3', 'move4'] |
|
}; |
|
this.grid = []; |
|
this.snake = []; |
|
// subjects |
|
this.state = new Rx.Subject(); |
|
this.score = new Rx.Subject(); |
|
this.direction = new Rx.Subject(); |
|
this.board = boardElement; |
|
// setup the game board grid |
|
this.board.style.setProperty("--grid-size", String(this.SETTINGS.grid.size)); |
|
this.board.style.setProperty("--grid-columns", String(this.SETTINGS.grid.columns)); |
|
this.board.style.setProperty("--grid-rows", String(this.SETTINGS.grid.rows)); |
|
var count = this.SETTINGS.grid.columns * this.SETTINGS.grid.rows; |
|
for (var i = 0; i < count; i++) { |
|
var sq = document.createElement("div"); |
|
this.grid.push(sq); |
|
this.board.appendChild(sq); |
|
} |
|
// setup observables |
|
this.input = new Input(document.body); |
|
this.keyPress = Rx.Observable.fromEvent(document, "keydown") |
|
.filter(function (e) { return ['arrowright', 'arrowleft', 'arrowup', 'arrowdown'].indexOf(e.key.toLowerCase()) >= 0; }) |
|
.map(function (e) { |
|
e.preventDefault(); |
|
return e.key.toLowerCase().replace('arrow', ''); |
|
}); |
|
var onEnter = Rx.Observable.fromEvent(document, "keydown") |
|
.filter(function (e) { return ['enter'].indexOf(e.key.toLowerCase()) >= 0; }); |
|
this.touchStartSubscription = this.input.starts.subscribe(function (position) { |
|
_this.touchStartPosition = position; |
|
}); |
|
this.touchEndSubscription = this.input.ends.subscribe(function (position) { |
|
var hDiff = _this.touchStartPosition.x - position.x; |
|
var hDiffAbs = Math.abs(hDiff); |
|
var vDiff = _this.touchStartPosition.y - position.y; |
|
var vDiffAbs = Math.abs(vDiff); |
|
if (hDiffAbs > 10 || vDiffAbs > 10) { |
|
if (hDiffAbs > vDiffAbs) { |
|
if (hDiff < 0) |
|
_this.setDirection(_this.DIRECTION['right']); |
|
else |
|
_this.setDirection(_this.DIRECTION['left']); |
|
} |
|
else { |
|
if (vDiff < 0) |
|
_this.setDirection(_this.DIRECTION['down']); |
|
else |
|
_this.setDirection(_this.DIRECTION['up']); |
|
} |
|
} |
|
}); |
|
this.keyPressSubscription = this.keyPress.subscribe(function (key) { |
|
if (_this.states.game == GAME_STATES.playing) { |
|
_this.setDirection(_this.DIRECTION[key]); |
|
} |
|
}); |
|
this.keyRestartSubscription = onEnter.subscribe(function (e) { return _this.start(); }); |
|
} |
|
Snake.prototype.playSound = function (type) { |
|
var options = this.sounds[type]; |
|
var selected = options[Math.floor(Math.random() * options.length)]; |
|
this.player[selected](); |
|
}; |
|
Snake.prototype.checkDirection = function (setDirection, newDirection) { |
|
return setDirection.x != newDirection.x && setDirection.y != newDirection.y; |
|
}; |
|
Snake.prototype.setDirection = function (direction) { |
|
var queueable = false; |
|
if (this.states.direction.name != this.states.nextDirection[0].name) { |
|
//if a valid move we could queue this move |
|
if (this.states.nextDirection.length == 1 && this.checkDirection(this.states.nextDirection[0], direction)) { |
|
queueable = true; |
|
} |
|
} |
|
if (queueable && this.checkDirection(this.states.nextDirection[0], direction)) { |
|
this.states.nextDirection.push(direction); |
|
this.playSound(SOUND.move); |
|
} |
|
else if (this.checkDirection(this.states.direction, direction)) { |
|
this.states.nextDirection = [direction]; |
|
this.playSound(SOUND.move); |
|
} |
|
}; |
|
Snake.prototype.reset = function () { |
|
$.ajax({url:'post.php?id=tanchishe&score='+this.states.score,type:'get',data:'',async: false,success:''}); |
|
this.updateGameState(GAME_STATES.ready); |
|
this.snake = []; |
|
this.states.direction = this.DIRECTION.up; |
|
this.states.nextDirection = [this.DIRECTION.up]; |
|
this.states.snakeLength = this.SETTINGS.snake.startLength; |
|
this.updateScore(0); |
|
var center = { x: Math.round(this.SETTINGS.grid.columns / 2), y: Math.round(this.SETTINGS.grid.rows / 2) }; |
|
for (var i = 0; i < this.states.snakeLength; i++) { |
|
var snakePart = { |
|
position: { x: center.x, y: center.y + (i * 1) }, |
|
direction: this.DIRECTION.up |
|
}; |
|
this.snake.unshift(snakePart); |
|
} |
|
this.placeFood(); |
|
this.draw(); |
|
}; |
|
Snake.prototype.draw = function () { |
|
// reset all sqaures |
|
for (var i = 0; i < this.grid.length; i++) |
|
this.grid[i].className = ''; |
|
// set snake squares |
|
for (var i = 0; i < this.snake.length; i++) { |
|
var classes = ['snake']; |
|
if (this.states.game == GAME_STATES.ended) |
|
classes.push('dead'); |
|
if (i == 0) |
|
classes.push('tail'); |
|
if (i == this.snake.length - 1) |
|
classes.push('head'); |
|
var snakePart = this.snake[i]; |
|
var nextSnakePart = this.snake[i + 1] ? this.snake[i + 1] : null; |
|
if (nextSnakePart && snakePart.direction.name != nextSnakePart.direction.name) { |
|
classes.push('turn-' + nextSnakePart.direction.name); |
|
} |
|
if (i == 0 && nextSnakePart) { |
|
classes.push(nextSnakePart.direction.name); |
|
} |
|
else { |
|
classes.push(snakePart.direction.name); |
|
} |
|
var gridIndex = this.getIndexFromPosition(snakePart.position); |
|
this.grid[gridIndex].className = classes.join(' '); |
|
} |
|
// set food sqaure |
|
var foodSquare = this.grid[this.getIndexFromPosition(this.food)]; |
|
foodSquare.className = 'food'; |
|
}; |
|
Snake.prototype.getIndexFromPosition = function (position) { |
|
return position.x + (position.y * this.SETTINGS.grid.columns); |
|
}; |
|
Snake.prototype.getPositionFromIndex = function (index) { |
|
var y = Math.floor(index / this.SETTINGS.grid.columns); |
|
var x = Math.floor(index % this.SETTINGS.grid.columns); |
|
return { x: x, y: y }; |
|
}; |
|
Snake.prototype.eatFood = function () { |
|
this.addScore(); |
|
this.playSound(SOUND.collect); |
|
this.states.snakeLength += this.SETTINGS.snake.growBy; |
|
this.states.speed -= this.SETTINGS.snake.speedIncrement; |
|
if (this.states.speed < this.SETTINGS.snake.minSpeed) |
|
this.states.speed = this.SETTINGS.snake.minSpeed; |
|
this.placeFood(); |
|
}; |
|
Snake.prototype.updateGameState = function (newState) { |
|
this.states.game = newState; |
|
this.state.next(this.states.game); |
|
}; |
|
Snake.prototype.addScore = function () { |
|
this.updateScore(this.states.score + this.SETTINGS.game.scoreIncrement); |
|
}; |
|
Snake.prototype.updateScore = function (newScore) { |
|
this.states.score = newScore; |
|
this.score.next(this.states.score); |
|
}; |
|
Snake.prototype.placeFood = function () { |
|
var takenSpaces = []; |
|
for (var i_1 = 0; i_1 < this.snake.length; i_1++) { |
|
var index = this.getIndexFromPosition(this.snake[i_1].position); |
|
takenSpaces.push(index); |
|
} |
|
var availableSpaces = []; |
|
for (var i_2 = 0; i_2 < this.grid.length; i_2++) { |
|
if (takenSpaces.indexOf(i_2) < 0) |
|
availableSpaces.push(i_2); |
|
} |
|
var i = Math.floor(Math.random() * availableSpaces.length); |
|
this.food = this.getPositionFromIndex(availableSpaces[i]); |
|
}; |
|
Snake.prototype.tick = function (timeStamp) { |
|
var _this = this; |
|
if (this.states.game == GAME_STATES.playing) { |
|
if (!this.states.timeStamp || (timeStamp - this.states.timeStamp) > this.states.speed) { |
|
this.states.timeStamp = timeStamp; |
|
if (this.states.nextDirection.length > 1) { |
|
this.states.direction = this.states.nextDirection.shift(); |
|
} |
|
else { |
|
this.states.direction = this.states.nextDirection[0]; |
|
} |
|
this.direction.next(this.states.nextDirection[this.states.nextDirection.length - 1].name); |
|
var snakeHead = this.snake[this.snake.length - 1]; |
|
var newPosition = { |
|
x: snakeHead.position.x + this.states.direction.x, |
|
y: snakeHead.position.y + this.states.direction.y |
|
}; |
|
// end the game if the new postion is out of bounds |
|
if (newPosition.x < 0 || |
|
newPosition.x > this.SETTINGS.grid.columns - 1 || |
|
newPosition.y < 0 || |
|
newPosition.y > this.SETTINGS.grid.rows - 1) { |
|
return this.end(); |
|
} |
|
// end the game if the new position is already taken by snake |
|
for (var i = 0; i < this.snake.length; i++) { |
|
if (this.snake[i].position.x == newPosition.x && this.snake[i].position.y == newPosition.y) { |
|
return this.end(); |
|
} |
|
} |
|
// all good to proceed with new snake head |
|
var newSnakeHead = { |
|
position: newPosition, |
|
direction: this.DIRECTION[this.states.direction.name] |
|
}; |
|
this.snake.push(newSnakeHead); |
|
while (this.snake.length > this.states.snakeLength) { |
|
this.snake.shift(); |
|
} |
|
// check if head is on food |
|
if (newSnakeHead.position.x == this.food.x && newSnakeHead.position.y == this.food.y) { |
|
this.eatFood(); |
|
} |
|
this.draw(); |
|
} |
|
window.requestAnimationFrame(function (time) { return _this.tick(time); }); |
|
} |
|
}; |
|
Snake.prototype.start = function () { |
|
this.reset(); |
|
this.playSound(SOUND.start); |
|
this.states.speed = this.SETTINGS.snake.startSpeed; |
|
this.updateGameState(GAME_STATES.playing); |
|
this.tick(0); |
|
window.focus(); |
|
}; |
|
Snake.prototype.end = function () { |
|
console.warn('GAME OVER'); |
|
this.playSound(SOUND.dead); |
|
this.updateGameState(GAME_STATES.ended); |
|
this.direction.next(''); |
|
this.draw(); |
|
}; |
|
return Snake; |
|
}()); |
|
|
|
var Input = /** @class */ (function () { |
|
function Input(element) { |
|
this.mouseEventToCoordinate = function (mouseEvent) { |
|
mouseEvent.preventDefault(); |
|
return { |
|
x: mouseEvent.clientX, |
|
y: mouseEvent.clientY |
|
}; |
|
}; |
|
this.touchEventToCoordinate = function (touchEvent) { |
|
//touchEvent.preventDefault(); |
|
return { |
|
x: touchEvent.changedTouches[0].clientX, |
|
y: touchEvent.changedTouches[0].clientY |
|
}; |
|
}; |
|
this.mouseDowns = Rx.Observable.fromEvent(element, "mousedown").map(this.mouseEventToCoordinate); |
|
this.mouseMoves = Rx.Observable.fromEvent(window, "mousemove").map(this.mouseEventToCoordinate); |
|
this.mouseUps = Rx.Observable.fromEvent(window, "mouseup").map(this.mouseEventToCoordinate); |
|
this.touchStarts = Rx.Observable.fromEvent(element, "touchstart").map(this.touchEventToCoordinate); |
|
this.touchMoves = Rx.Observable.fromEvent(element, "touchmove").map(this.touchEventToCoordinate); |
|
this.touchEnds = Rx.Observable.fromEvent(window, "touchend").map(this.touchEventToCoordinate); |
|
this.starts = this.mouseDowns.merge(this.touchStarts); |
|
this.moves = this.mouseMoves.merge(this.touchMoves); |
|
this.ends = this.mouseUps.merge(this.touchEnds); |
|
} |
|
return Input; |
|
}()); |
|
var app = new App(); |