(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { if(shape != undefined) { var matrix = shape.matrix(); var gsize = this.previewGridSize; var startX = (this.preview.width - gsize*shape.getColumnCount()) / 2; var startY = ((this.preview.height - gsize*shape.getRowCount()) / 2 / 4)*(index*2+1); for(var i = 0;i { if(shape != undefined) { var matrix = shape.matrix(); var gsize = this.holdGridSize; var startX = (this.hold.width - gsize*shape.getColumnCount()) / 2; var startY = ((this.hold.height - gsize*shape.getRowCount()) / 2 / 4)*(index*2+1); for(var i = 0;i -1; if(!isFirefox) { for(var i = 0; i < 4; i++) if((gp = window.navigator.getGamepads()[i]) != undefined) // dumb gamepad update. fix. gamepadAPI.controller = gp; } gamepadAPI.buttonsCache = []; for (var k = 0; k < gamepadAPI.buttonsStatus.length; k++) { gamepadAPI.buttonsCache[k] = gamepadAPI.buttonsStatus[k]; } gamepadAPI.buttonsStatus = []; var c = gamepadAPI.controller || {}; var pressed = []; if (c.buttons) { for (var b = 0, t = c.buttons.length; b < t; b++) { if (c.buttons[b].pressed) { pressed.push(gamepadAPI.buttons[b]); } } } var axes = []; if (c.axes) { for (var a = 0, x = c.axes.length; a < x; a++) { axes.push(c.axes[a].toFixed(2)); } } gamepadAPI.axesStatus = axes; gamepadAPI.buttonsStatus = pressed; return pressed; }, buttonPressed: function(button, hold) { var newPress = false; for (var i = 0, s = gamepadAPI.buttonsStatus.length; i < s; i++) { if (gamepadAPI.buttonsStatus[i] == button) { newPress = true; if (!hold) { for (var j = 0, p = gamepadAPI.buttonsCache.length; j < p; j++) { if (gamepadAPI.buttonsCache[j] == button) { newPress = false; } } } } } return newPress; }, buttons: [ // XBox360 layout 'A', 'B', 'X', 'Y', 'LB', 'RB', 'Axis-Left', 'DPad-Right', 'Back', 'Start', 'Power', 'Axis-Right','DPad-Up', 'DPad-Down' , 'DPad-Left','DPad-Right' ], /* 'DPad-Up', 'DPad-Down', 'DPad-Left', 'DPad-Right', 'Start', 'Back', 'Axis-Left', 'Axis-Right', 'LB', 'RB', 'Power', 'A', 'B', 'X', 'Y', ],*/ buttonsCache: [], buttonsStatus: [], axesStatus: [] }; window.addEventListener("gamepadconnected", gamepadAPI.connect); window.addEventListener("gamepaddisconnected", gamepadAPI.disconnect); module.exports = gamepadAPI; // export gamepadAPI; },{}],4:[function(require,module,exports){ var gamepad = require('./gamepad.js'); var utils = require('./utils.js'); // import * as gamepad from './gamepad.js'; var UserInputs = { init() { this.dt = 0; this.debugTimer = new Date(); this.settingsMap = new Map(); // var init = utils.getCookie("init"); // if(init == "") for(var i in this.settingsList) utils.setCookie(this.settingsList[i], this.settingsDefault[i], 30); // cookies expire in 30 days // else // for(var i in this.settingsList) // this.settingsDefault[i] = utils.getCookie(this.settingsList[i]); for(var i in this.settingsList) this.settingsMap.set(this.settingsList[i], this.settingsDefault[i]); document.addEventListener('keydown', this.keyDown.bind(this)); document.addEventListener('keyup', this.keyUp.bind(this)); }, updateGamepad() { this.gpButtons = gamepad.update(); }, gamepadEnabled() { return gamepad.controller || false; }, incDeciframes() { this.keyboardButtonsDeciframes++; this.keyboardDirectionArrowsDeciframes++; this.gamepadButtonsDeciFrames++; this.gamepadDirectionPadDeciFrames++; }, incTickCounter() { this.ticks++; if(this.ticks > 1200000)this.ticks = 0; // set to 0 every 12 seconds? }, getTickCounter() { return this.ticks; }, processGamepadInput() { this.gamepadButtonsDown(this.settingsMap.get("Gamepad Harddrop")); // hard drop this.gamepadButtonsDown(this.settingsMap.get("Gamepad Hold")); // hold this.gamepadButtonsDown(this.settingsMap.get("Gamepad Rotateccw")); // rotate counter this.gamepadButtonsDown(this.settingsMap.get("Gamepad Rotate")); // rotate cwise this.gamepadButtonsDown(this.settingsMap.get("Gamepad Pophold")); // Pop hold stack this.gamepadButtonsDown(this.settingsMap.get("Gamepad Reset")); // reset return; }, processGamepadDPad() { this.gamepadDPadDown(this.settingsMap.get("Gamepad Left")); // shift left this.gamepadDPadDown(this.settingsMap.get("Gamepad Right")); // shift right this.gamepadDPadDown(this.settingsMap.get("Gamepad Down")); // down return; }, // X, Y, A, B , RB, LB Buttons gamepadButtonsDown(finds) { var deciDAS = 50.0; var deciARR = 10.0; var isContained = this.gpButtons.includes(finds); var isPrevContained = this.prevGpButtons.includes(finds); if(isPrevContained != isContained ) { this.isGamepadButtonDown = false; // Do once if(isContained) this.gamepadQueue.push(finds); } var gamepadDASFrames = this.gamepadButtonsDeciFrames; if (!this.isGamepadButtonDown) { if (gamepadDASFrames >= deciDAS) { this.gamepadButtonsDeciFrames = 0; this.isGamepadButtonDown = true; } } else { if (gamepadDASFrames >= deciARR && isContained) { //this.gamepadQueue.push(finds); this.gamepadButtonsDeciFrames = 0; } } }, // Direction Pad gamepadDPadDown(finds) { var DAS = parseInt(this.settingsMap.get("Gamepad DAS")); //65.0; var ARR = parseInt(this.settingsMap.get("Gamepad ARR")); //20.0; var isContained = this.gpButtons.includes(finds); var isPrevContained = this.prevGpButtons.includes(finds); if(isPrevContained != isContained ) { this.isGamepadDown = false; // Do once if(isContained) this.gamepadQueue.push(finds); } var gamepadDirectionDasFrames = this.gamepadDirectionPadDeciFrames; if (!this.isGamepadDown) { if (gamepadDirectionDasFrames >= DAS) { this.gamepadDirectionPadDeciFrames = 0; this.isGamepadDown = true; } } else { if (gamepadDirectionDasFrames >= ARR && isContained) { this.gamepadQueue.push(finds); this.gamepadDirectionPadDeciFrames = 0; } } return; }, // doing a lot of back and forth between strings and integers to represtent the same thing -- todo: fix processKeys() { this.processKeyDown(parseInt(this.settingsMap.get("Keyboard Harddrop"))); //32); // Space - hard drop this.processKeyDown(parseInt(this.settingsMap.get("Keyboard Rotate"))); //88); // X - rotate this.processKeyDown(parseInt(this.settingsMap.get("Keyboard Rotateccw"))); //90); // Z - rotateccw this.processKeyDown(parseInt(this.settingsMap.get("Keyboard Hold"))); //16); // shift - push hold stack this.processKeyDown(parseInt(this.settingsMap.get("Keyboard Pophold"))); // ctrl - pop hold stack this.processKeyDown(parseInt(this.settingsMap.get("Keyboard Background"))); // q - turn off background this.processKeyDown(parseInt(this.settingsMap.get("Keyboard Reset"))); // r - reset //this.processKeyDown(this.settingsMap.get("Keyboard hold"))); // c - pop hold stack }, // keyboard keys z,x,space processKeyDown(key) { var deciDAS = 50.0; var deciARR = 50.0; // todo: fix this mess if(this.prevKeyboardKeys[key] != this.keyboardKeys[key] && this.isKeyBoardKeyDown == true) { this.isKeyboardKeyDown = false; if(this.keyboardKeys[key] == true) this.inputQueue.push(key); //this.keyboardKeys[key] = false; } var keyboardDASFrames = this.keyboardButtonsDeciframes; if (!this.isKeyboardKeyDown) { if (keyboardDASFrames >= deciDAS) { this.keyboardButtonsDeciframes = 0; this.isKeyboardKeyDown = true; } } else { if (keyboardDASFrames >= deciARR && this.keyboardKeys[key] == true) { //this.inputQueue.push(key); this.keyboardButtonsDeciframes = 0; } } }, processKeyShift() { this.processKeyboardArrowKeys(parseInt(this.settingsMap.get("Keyboard Left"))); //39); // right this.processKeyboardArrowKeys(parseInt(this.settingsMap.get("Keyboard Right"))); //37); // left this.processKeyboardArrowKeys(parseInt(this.settingsMap.get("Keyboard Down"))); // down }, // Direction arrows processKeyboardArrowKeys(key) { var DAS = parseInt(this.settingsMap.get("Keyboard DAS")); //65.0; var ARR = parseInt(this.settingsMap.get("Keyboard ARR")); //20.0; if(this.prevKeyboardKeys[key] != this.keyboardKeys[key]) { // Not being held yet this.isPassedDelay = false; if(this.keyboardKeys[key] == true) this.inputQueue.push(key); } var keyboardDASFrames = this.keyboardDirectionArrowsDeciframes; if (!this.isPassedDelay) { if (keyboardDASFrames >= DAS) { this.keyboardDirectionArrowsDeciframes = 0; this.isPassedDelay = true; } } else if(keyboardDASFrames >= ARR && this.keyboardKeys[key] == true) { this.inputQueue.push(key); this.keyboardDirectionArrowsDeciframes = 0; } }, keyDown(event) { if (! ((event.keyCode >= 48 && event.keyCode <= 57) || (event.keyCode >= 96 && event.keyCode <= 105) || event.keyCode == 8)) event.preventDefault(); this.keyboardKeys[event.keyCode] = true; this.isKeyBoardKeyDown = true; }, keyUp(event) { this.isKeyDown = false; this.keyboardKeys[event.keyCode] = false; this.isKeyBoardKeyDown = false; }, gamepadButtonClear() { gpButtons = []; isGamepadDown = false; isGamepadButtonDown = false; gamepadQueue = []; }, saveButtons() { this.prevGpButtons = this.gpButtons; }, saveKeyboardKeys() { //this.prevKeyboardKeys = utils.deepClone(this.keyboardKeys); this.prevKeyboardKeys = {...this.keyboardKeys}; }, // button states isPassedDelay: false, isKeyboardKeyDown: false, isGamepadDown: false, isGamepadButtonDown: false, // das frame counters keyboardButtonsDeciframes: 0, // DAS controlled frames/10 for non-shifted keys keyboardDirectionArrowsDeciframes: 0, // DAS controlled frames/10 for mino shifting keys gamepadButtonsDeciFrames: 0, // DAS controlled frames/10 for non-shifted keys gamepadDirectionPadDeciFrames: 0, // DAS controlled frames/10 for mino shifting keys // buttons state contatiners gpButtons: [], prevGpButtons:[], keyboardKeys: [], prevKeyboardKeys: [], // button pressed containers inputQueue: [], gamepadQueue: [], ticks: 0, settingsList: ["init", "Keyboard DAS", "Keyboard ARR", "Keyboard Harddrop", "Keyboard Hold", "Keyboard Left", "Keyboard Right", "Keyboard Rotateccw", "Keyboard Rotate", "Keyboard Down", "Keyboard Pophold", "Keyboard Reset", "Keyboard Background", "Gamepad DAS", "Gamepad ARR", "Gamepad Harddrop", "Gamepad Hold", "Gamepad Left", "Gamepad Right", "Gamepad Rotateccw", "Gamepad Rotate", "Gamepad Down","Gamepad Pophold", "Gamepad Reset", "Gamepad Background", "path", "High Score"], settingsDefault: ["true", "50.0", "16.0", "32", "16", "37", "39", "90", "88", "40", "17", "82", "81", "50.0", "16.0", "RB", "LB", "DPad-Left", "DPad-Right", "A", "B", "DPad-Down", "DPad-Up", "Back", "", "=/",""], settingsMap: [] }; module.exports = UserInputs; // export UserInputs; },{"./gamepad.js":3,"./utils.js":8}],5:[function(require,module,exports){ var utils = require('./utils.js'); var consts = require('./consts.js'); var shapes = require('./shapes.js'); var views = require('./views.js'); var canvas = require('./canvas.js'); var inputs = require('./input.js'); var openers = require('./openers.js'); // import * as utils from './utils.js'; // import * as consts from './const.js'; // import * as shapes from './shapes.js'; // import * as views from './views.js'; // import * as canvas from './canvas.js'; // import * as inputs from './input.js'; // import * as openers from './openers.js'; //import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r125/build/three.module.js'; /** Init game matrix */ var initMatrix = function(rowCount, columnCount) { var result = []; for (var i = 0; i < rowCount; i++) { var row = []; result.push(row); for (var j = 0; j < columnCount; j++) { row.push(0); } } return result; }; /** Clear game matrix */ var clearMatrix = function(matrix) { for (var i = 0; i < matrix.length; i++) { for (var j = 0; j < matrix[i].length; j++) { matrix[i][j] = 0; } } }; /** Check all full rows in game matrix return rows number array. eg: [18,19]; */ var checkFullRows = function(matrix) { var rowNumbers = []; for (var i = 0; i < matrix.length; i++) { var row = matrix[i]; var full = true; for (var j = 0; j < row.length; j++) { full = full && row[j] !== 0; } if (full) { rowNumbers.push(i); } } return rowNumbers; }; /** Remove one row from game matrix. copy each previous row data to next row which row number less than row; */ var removeOneRow = function(matrix, row) { var colCount = matrix[0].length; for (var i = row; i >= 0; i--) { for (var j = 0; j < colCount; j++) { if (i > 0) { matrix[i][j] = matrix[i - 1][j]; } else { matrix[i][j] = 0; } } } }; /** Remove rows from game matrix by row numbers. */ var removeRows = function(matrix, rows) { for (var i in rows) { removeOneRow(matrix, rows[i]); } }; /** Check game data to determin wether the game is over */ var checkGameOver = function(matrix) { var firstRow = matrix[0]; for (var i = 0; i < firstRow.length; i++) { if (firstRow[i] !== 0) { return true; }; } return false; }; /** Calculate the extra rewards add to the score */ var calcRewards = function(rows, tspinType) { if(tspinType == 2) rows*=2+1; if (rows && rows.length > 1) { return Math.pow(2, rows.length - 1) * 100; } return 0; }; /** Calculate game score */ var calcScore = function(rows) { if (rows && rows.length) { return rows.length * 100; } return 0; }; /** Calculate time interval by level, the higher the level,the faster shape moves */ var calcIntervalByLevel = function(level) { return consts.DEFAULT_INTERVAL - (level - 1) * 60; }; // Default max scene size var defaults = { maxHeight: 700, maxWidth: 560 }; /** Tetris main object definition */ function Tetris(id) { this.id = id; this.init(); } Tetris.prototype = { init: function(options) { // this gameStates = {" // this.state = var cfg = this.config = utils.extend(options, defaults); this.interval = consts.DEFAULT_INTERVAL; views.init(this.id, cfg.maxWidth, cfg.maxHeight); canvas.init(views.scene, views.preview, views.hold); inputs.init(); this.createSettings(); // if true no openers. just random tetrinos this.isFreePlay = true; this.isTimerOn = false; this.currentOpener = 0; this.doTest = false; this.matrix = initMatrix(consts.ROW_COUNT, consts.COLUMN_COUNT); this.eventTimer = new Date(); this.debugTimer = new Date(); this.gamepadEnabled = false; this.reset(); this._initEvents(); this._fireShape(); }, toggleTimer: function() { document.getElementById("Timer").value = (this.isTimerOn = !this.isTimerOn) ? "Seconds:" : "Timer Off"; }, toggleGamepad: function(){ document.getElementById("enablegamepad").value = ((this.gamepadEnabled = !this.gamepadEnabled) ? "Disable Gamepad" : "Enable Gamepad"); }, setFreePlay: function() { document.getElementById("Timer").value = "Timer Off"; document.getElementById("Time").value = ""; document.getElementById("besttime").value = ""; this.isTimerOn = false; this.isFreePlay = true; this.doTest = false; this.hintQueue = []; this.shapeQueue = []; this.hintMino = 0; this._restartHandler(); this.currentOpener = 0; }, setCurrentOpener(opener) { document.getElementById("besttime").value = ""; this.isFreePlay = false; this.doTest = false; this.currentOpener = opener; this._restartHandler(); }, setDoTest: function() { if(this.isFreePlay) return; this.doTest = true; this._restartHandler(); }, createSettings: function () { var list = document.getElementById("settings"); var settings = inputs.settingsList; settings.forEach(function(item) { var option = document.createElement('option'); option.text = item; option.id = item; list.add(option); }); }, updateSettingTextBox: function() { document.getElementById("setting_value").value = inputs.settingsMap.get(inputs.settingsList[document.getElementById("settings").selectedIndex-1]); //inputs.settingsDefault[document.getElementById("settings").selectedIndex-1]; }, setSettings: function() { var newVal = document.getElementById("setting_value").value; var key = inputs.settingsList[document.getElementById("settings").selectedIndex-1]; utils.setCookie(key, newVal, 30); inputs.settingsMap.set(key, newVal); }, //Reset game reset: function() { this.numlefts = 0; this.running = false; this.isGameOver = false; this.level = 1; this.score = 0; this.lines = 0; // beginning of frame this.startTime = new Date().getTime(); this.currentTime = this.startTime; this.prevTime = this.startTime; this.sequencePrevTime = this.startTime; //todo:get rid of extra this.levelTime = this.startTime; this.prevInputTime = this.startTime; // current tetrino index gets set to 0 at the end of opener sequence this.currentMinoInx = 0; this.shapeQueue = []; this.hintQueue = []; this.holdStack = []; // gets set to false after mino has been popped from hold stack; set back to true on mino dropped this.canPopFromHoldStack = false; // manipulation counter for srs extended piece lockdown this.manipulationCounter = 0; // timer for srs extened piece lockdown this.lockdownTimer = 0; this.landed = false; this.isSequenceCompleted = false; clearMatrix(this.matrix); views.setLevel(this.level); views.setScore(this.score); views.setGameOver(this.isGameOver); openers.reset(); shapes.resetMinoRNG(); this._draw(); }, //Start game start: function() { this.running = true; window.requestAnimationFrame(utils.proxy(this._refresh, this)); }, //Pause game pause: function() { this.running = false; this.currentTime = new Date().getTime(); this.prevTime = this.currentTime; }, pushHoldStack: function() { if(this.holdStack.length < 4) { this.holdStack.push(utils.deepClone(this.shape)); this.shape = this.shapeQueue.shift(); this.canPopFromHoldStack = false; this.shape.resetOrigin(); this._draw(); } }, popHoldStack: function() { if(this.holdStack.length >= 1 && this.canPopFromHoldStack) { this.canPopFromHoldStack = false; this.shapeQueue.unshift(utils.deepClone(this.shape)); this.shape = this.holdStack.pop(); this.shape.resetOrigin(); this._draw(); } }, // Restart game _restartHandler: function() { this.reset(); this.start(); this._fireShape(); }, // Bind game events _initEvents: function() { setInterval(() => {this._processTick();}, 1); setInterval(() => {this.lockDownTimer++;}, 100 ); views.btnRestart.addEventListener('click', utils.proxy(this._restartHandler, this), false); }, // Fill next queue and set next shape _fireShape: function() { if(this.isFreePlay == false) { while(this.shapeQueue.length <= 4) { this.preparedShape = openers.getNextMino(this.currentOpener); this.shapeQueue.push(this.preparedShape); } while(this.hintQueue.length <= 4) { this.preparedShape = openers.getNextHint(this.currentOpener); this.hintQueue.push(this.preparedShape); } this.hintMino = this.hintQueue.shift(); this.shape = this.shapeQueue.shift(); this.currentMinoInx++; // Opener sequence completed if(this.currentMinoInx > openers.getLength()) { if(this.isTimerOn) { var besttime = document.getElementById("besttime").value; var deltaTime = new Date().getTime() - this.sequencePrevTime; if(besttime == "" || deltaTime/1000.0 < parseFloat(besttime)) { document.getElementById("besttime").value = (deltaTime/1000.0).toString(); } this.isSequenceCompleted = true; } this.hintQueue = []; this.shapeQueue = []; // Recursion warning this._restartHandler(); } } else { while(this.shapeQueue.length <= 4) { this.preparedShape = shapes.randomShape(); this.shapeQueue.push(this.preparedShape); } this.shape = this.shapeQueue.shift();// || shapes.randomShape(); this.currentMinoInx++; } //todo:should be in shapes.js this.landed = false; this.manipulationCounter = 0; // Reset matrix at successful end of opener //if(this.shapeQueue.length == openers.length) { // this.matrix = []; // new Audio("Tetris.ogg"); //} this._draw(); }, // lockdown timer with centisecond resolution resetLockdown: function() { if(this.shape.canDown(this.matrix) == false) this.landed = true; this.lockDownTimer = 0; if(this.landed) this.manipulationCounter++; }, // Return if the piece can be shifted or rotated isPieceLocked: function() { if(this.manipulationCounter > 15) return true; if(this.lockDownTimer >= 5) return true; return false; }, // Draw game data _draw: function() { canvas.drawScene(); canvas.drawShape(this.shape); canvas.drawHoldShape(this.holdStack); canvas.drawPreviewShape(this.shapeQueue); if(this.doTest != true) canvas.drawHintShape(this.hintMino); if(this.shape != undefined) { let clone = Object.assign(Object.create(Object.getPrototypeOf(this.shape)), this.shape); var bottomY = clone.bottomAt(this.matrix); canvas.drawGhostShape(clone, bottomY); } canvas.drawMatrix(this.matrix); }, // tick input data -- wont have better than 4-15ms resolution since javascript is single theaded _processTick: async function() { //var deltaTime = (new Date()).getTime() - this.eventTimer.getTime(); //console.log("desync time: " + deltaTime); inputs.incDeciframes(); inputs.incTickCounter(); if(this.isTimerOn) { var deltaPlayTime = new Date().getTime() - this.sequencePrevTime; if(inputs.getTickCounter() >= 20) { // Set html element at a reasonble rate document.getElementById("Time").value = (deltaPlayTime/1000).toString(); } } // Don't process game related events if game over if(this.isGameOver) return; if(this.gamepadEnabled && inputs.gamepadEnabled()) { var tenthOfFrame = 1.0 //1.6; // 1.6ms = 1 fram var halfFrame = 5.0 //8.0; var halfFramePlus = 10.0; if(inputs.getTickCounter() >= tenthOfFrame) { inputs.updateGamepad(); inputs.processGamepadDPad(); inputs.processGamepadInput(); } // drain gamepad queue if( inputs.getTickCounter() > halfFrame) // 8 millisecons { while((inputs.gamepadQueue != undefined && inputs.gamepadQueue.length >= 1)){ var curkey = inputs.gamepadQueue.shift(); if(curkey == "DPad-Left") { this.shape.goLeft(this.matrix); this.resetLockdown(); this._draw(); } if(curkey == "DPad-Right") { this.shape.goRight(this.matrix); this.resetLockdown(); this._draw(); } if(curkey == "A") { this.rotationCounter++; this.shape.rotate(this.matrix); this.resetLockdown(); this._draw(); } if(curkey == "B") { this.rotationCounter++; this.shape.rotateClockwise(this.matrix); this.resetLockdown(); this._draw(); } if(curkey == "DPad-Down") { this.shape.goDown(this.matrix); this._draw(); } if(curkey == "RB") { this.shape.goBottom(this.matrix); this.lockDownTimer = 5000; this._update(); } if(curkey == "LB") { this.pushHoldStack(); this._draw(); } if(curkey == "DPad-Up") { this.popHoldStack(); this._draw(); } if(curkey == "Back") { this._restartHandler(); return; } } inputs.gamepadQueue = []; } //inputs.gamepadButtonClear(); } // Do keyboard inputs.processKeys(); inputs.processKeyShift(); // Keyboard inputs while((inputs.inputQueue != undefined && inputs.inputQueue.length >= 1)){ var curkey = inputs.inputQueue.shift(); if(curkey == 37) { this.debugTimer = new Date(); this.shape.goLeft(this.matrix); this.resetLockdown(); this._draw(); } if(curkey == 39){ this.shape.goRight(this.matrix); this.resetLockdown(); this._draw(); } if(curkey == 40) { this.shape.goDown(this.matrix); this._draw(); } if(curkey == 90) { this.rotationCounter++; this.shape.rotate(this.matrix); this.resetLockdown(); this._draw(); } if(curkey == 88){ this.rotationCounter++; this.shape.rotateClockwise(this.matrix); this.resetLockdown(); this._draw(); } if(curkey == 32) { this.shape.goBottom(this.matrix); this.lockDownTimer = 5000; this._update(); } if(curkey == 16) { this.pushHoldStack(); this._draw(); } if(curkey == 17 || curkey == 67) { this.popHoldStack(); this._draw(); } if(curkey == 81) { if(document.getElementById("divbg").style.display == "none") document.getElementById("divbg").style.display = "initial"; else document.getElementById("divbg").style.display="none"; } if(curkey == 82) { this._restartHandler(); return; } } inputs.inputQueue = []; inputs.saveKeyboardKeys(); inputs.saveButtons(); }, // Refresh game canvas _refresh: function() { if (!this.running) return; this.currentTime = new Date().getTime(); var deltaLevelTime = this.currentTime - this.prevTime; if (deltaLevelTime > this.interval) { // every .6 seconds? this._update(); this._checkLevel(this.prevTime = this.currentTime); } // Draw Frame if (!this.isGameOver) { window.requestAnimationFrame(utils.proxy(this._refresh, this)); } }, // check if the current piece is in the same location as the hint piece _checkHint: function() { if(this.isFreePlay) return; if(!this.shape.isSameSRS(this.hintMino)) { //new Audio('./dist/Failed.ogg').play(); this._restartHandler(); // Restart return 1; } }, // Update game data _update: function() { if (this.shape.canDown(this.matrix)) { this.shape.goDown(this.matrix); } else if(this.isPieceLocked()){ this.canPopFromHoldStack = true; this.shape.copyTo(this.matrix); this._check(); if(this._checkHint()) return; this._fireShape(); new Audio('./dist/Blop2.ogg').play(); } this._draw(); this.isGameOver = checkGameOver(this.matrix); views.setGameOver(this.isGameOver); if (this.isGameOver) views.setFinalScore(this.score); }, // 0 - none, 1 - mini, 2 - tspin _tSpinType: function(tPiece, matrix) { var side1 = 0; var side2 = 0; var side3 = 0; var side4 = 0; side1X = tPiece.x; side1Y = tPiece.y; side2X = tPiece.x + 2; side2Y = tPiece.y; side3X = tPiece.x; side3Y = tPiece.y + 2; side4X = tPiece.x + 2; side4Y = tPiece.y + 2; if(matrix[side1Y][side1X] != 0) side1 = 1; if(matrix[side2Y][side2X] != 0) side2 = 1; // TODO: figure out why this occasionally is undefined if(matrix[side3Y] != undefined && matrix[side3Y][side3X] != 0) side3 = 1; if(matrix[side4Y][side4X] != 0) side4 = 1; console.log("sides: " + side1+side2+side3+side4); // if Sides A and B + (C or D) are touching a Surface //considered a T-Spin if((side1+side2+side3+side4) >= 3) return 2; //if Sides C and D + (A or B) are touching a Surface //considered a Mini T-Spin if((side1 || side2) && (side3 && side4)) return 1; return 0; }, // Check and update game data _check: function() { var rows = checkFullRows(this.matrix); if (rows.length) { var tspinType; if(this.shape.flag === 'T') tspinType = this._tSpinType(this.shape, this.matrix); removeRows(this.matrix, rows); console.log("type: " + tspinType); var score = calcScore(rows); var reward = calcRewards(rows, tspinType); this.score += score + reward; this.lines += rows.length; views.setScore(this.score); views.setReward(reward); views.setLines(this.lines); } }, // Check and update game level _checkLevel: function() { var currentTime = new Date().getTime(); if (currentTime - this.levelTime > consts.LEVEL_INTERVAL) { this.level += 1; this.interval = calcIntervalByLevel(this.level); views.setLevel(this.level); this.levelTime = currentTime; } } } window.Tetris = Tetris; // export {Tetris}; },{"./canvas.js":1,"./consts.js":2,"./input.js":4,"./openers.js":6,"./shapes.js":7,"./utils.js":8,"./views.js":9}],6:[function(require,module,exports){ var shapes = require("./shapes.js"); // import * as shapes from './shapes.js'; // https://harddrop.com/wiki/Opener // https://four.lol/ var openerGenerator = { shapeQueue: [], hintQueue: [], idx: 0, hintIdx: 0, isInit: 0, isHintInit: 0, // O - 1, I - 6, L - 0, S - 5, J - 4, Z - 2, T - 3 // Current Tetriminos init(opener) { if(!this.isInit || this.shapeQueue == undefined) { switch(opener) { case 0: case 1: // Fonzie Variation this.shapeQueue = new Array( shapes.getShape(0), shapes.getShape(6), shapes.getShape(1), shapes.getShape(5), shapes.getShape(2), shapes.getShape(4), shapes.getShape(3)); break; case 2: // DTCannon this.shapeQueue = new Array( shapes.getShape(1), shapes.getShape(6), shapes.getShape(0), shapes.getShape(5), shapes.getShape(4), shapes.getShape(2), shapes.getShape(3), shapes.getShape(1), shapes.getShape(6), shapes.getShape(0), shapes.getShape(4), shapes.getShape(3), shapes.getShape(1), shapes.getShape(3)); break; case 3: this.shapeQueue = new Array( shapes.getShape(4), shapes.getShape(5), shapes.getShape(6), shapes.getShape(0), shapes.getShape(2), shapes.getShape(1), shapes.getShape(5), shapes.getShape(3), shapes.getShape(1), shapes.getShape(2), shapes.getShape(6), shapes.getShape(0), shapes.getShape(4), shapes.getShape(3)); break; case 4: //Pokemino's STD this.shapeQueue = new Array( shapes.getShape(0), shapes.getShape(6), shapes.getShape(1), shapes.getShape(4), shapes.getShape(2), shapes.getShape(5), shapes.getShape(3), shapes.getShape(1), shapes.getShape(5), shapes.getShape(2), shapes.getShape(0), shapes.getShape(6), shapes.getShape(2), shapes.getShape(4), shapes.getShape(3), shapes.getShape(0), shapes.getShape(3)); break; case 5: // Mr TSpins STD reversed this.shapeQueue = new Array( shapes.getShape(1), shapes.getShape(2), shapes.getShape(5), shapes.getShape(0), shapes.getShape(4), shapes.getShape(6), shapes.getShape(3), shapes.getShape(1), shapes.getShape(6), shapes.getShape(2), shapes.getShape(4), shapes.getShape(5), shapes.getShape(0), shapes.getShape(0), shapes.getShape(3), shapes.getShape(3)); break; case 6: // Hachispin this.shapeQueue = new Array( shapes.getShape(1), shapes.getShape(2), shapes.getShape(6), shapes.getShape(5), shapes.getShape(4), shapes.getShape(0), shapes.getShape(3), shapes.getShape(6), shapes.getShape(1), shapes.getShape(5), shapes.getShape(4), shapes.getShape(2), shapes.getShape(0), shapes.getShape(3)); break; case 7: // Albatross this.shapeQueue = new Array( shapes.getShape(1), shapes.getShape(5), shapes.getShape(6), shapes.getShape(0), shapes.getShape(4), shapes.getShape(2), shapes.getShape(3)); break; case 8: // Number One this.shapeQueue = new Array( shapes.getShape(1), shapes.getShape(4), shapes.getShape(6), shapes.getShape(0), shapes.getShape(2), shapes.getShape(5), shapes.getShape(3)); break; default: return; } } this.isInit = 1; return; }, getNextMino(opener) { this.init(opener); var mino = this.shapeQueue[this.idx]; this.idx++; if(this.idx == this.shapeQueue.length) { this.idx = 0; this.isInit = 0; } return mino; }, // Hint Tetrimions initHint(opener) { if(!this.isHintInit || this.hintQueue == undefined) { switch(opener) { case 0: case 1: // Fonzie Variation this.hintQueue = new Array( shapes.getShape(0), shapes.getShape(6), shapes.getShape(1), shapes.getShape(5), shapes.getShape(2), shapes.getShape(4), shapes.getShape(3)); // position x, position y, orientation, position x,... var hintDataList = [-1,17,1, 3,18,0, 6,18,0, 5,17,1, 3,17,0, 7,16,0, 1,17,2]; for(var i = 0; i < this.hintQueue.length; i++) { this.hintQueue[i].x = hintDataList[i * 3]; this.hintQueue[i].y = hintDataList[i * 3 + 1]; this.hintQueue[i].state = this.hintQueue[i].nextState(hintDataList[i * 3 + 2]); } break; case 2: // DT Cannon this.hintQueue = new Array( shapes.getShape(1), shapes.getShape(6), shapes.getShape(0), shapes.getShape(5), shapes.getShape(4), shapes.getShape(2), shapes.getShape(3), shapes.getShape(1), shapes.getShape(6), shapes.getShape(0), shapes.getShape(4), shapes.getShape(3), shapes.getShape(1), shapes.getShape(3)); // position x, position y, orientation, position x,... var hintDataList = [-2,18,0, 4,16,1, 6,17,1, 7,17,1, 4,17,-1, 3,17,3, 3,15,0, 5,15,0, 7,14,1, 2,13,-1, -1,15,1, 1,16,2, 3,16,1, 1,17,-1]; for(var i = 0; i < this.hintQueue.length; i++) { this.hintQueue[i].x = hintDataList[i * 3]; this.hintQueue[i].y = hintDataList[i * 3 + 1]; this.hintQueue[i].state = this.hintQueue[i].nextState(hintDataList[i * 3 + 2]); } break; case 3: //MKO Stacking this.hintQueue = new Array( shapes.getShape(4), shapes.getShape(5), shapes.getShape(6), shapes.getShape(0), shapes.getShape(2), shapes.getShape(1), shapes.getShape(5), shapes.getShape(3), shapes.getShape(1), shapes.getShape(2), shapes.getShape(6), shapes.getShape(0), shapes.getShape(4), shapes.getShape(3)); // position x, position y, orientation, position x,... var hintDataList = [0,18,0, 0,16,-1, 8,16,-1, 4,18,0, 4,16,1, 5,18,0, 1,15,-1, 2,17,2, 5,18,0, 3,17,1, 6,16,0, 0,15,2, 0,14,0, 2,16,2]; for(var i = 0; i < this.hintQueue.length; i++) { this.hintQueue[i].x = hintDataList[i * 3]; this.hintQueue[i].y = hintDataList[i * 3 + 1]; this.hintQueue[i].state = this.hintQueue[i].nextState(hintDataList[i * 3 + 2]); } break; case 4: //Pokemino's STD this.hintQueue = new Array( shapes.getShape(0), shapes.getShape(6), shapes.getShape(1), shapes.getShape(4), shapes.getShape(2), shapes.getShape(5), shapes.getShape(3), shapes.getShape(1), shapes.getShape(5), shapes.getShape(2), shapes.getShape(0), shapes.getShape(6), shapes.getShape(2), shapes.getShape(4), shapes.getShape(3), shapes.getShape(0), shapes.getShape(3)); var hintDataList = [0,17,1, -2,16,1, 4,18,0, 4,17,-1, 3,15,1, 8,17,-1, 2,17,2, 0,17,0, 0,15,-1, 1,15,0, 8,16,-2, 5,15,-1, 3,14,1, 6,12,-1, 6,16,1, 2,16,-1, 7,17,2 ]; for(var i = 0; i < this.hintQueue.length; i++) { this.hintQueue[i].x = hintDataList[i * 3]; this.hintQueue[i].y = hintDataList[i * 3 + 1]; this.hintQueue[i].state = this.hintQueue[i].nextState(hintDataList[i * 3 + 2]); } break; case 5: // Mr TSpins STD reversed this.hintQueue = new Array( shapes.getShape(1), shapes.getShape(2), shapes.getShape(5), shapes.getShape(0), shapes.getShape(4), shapes.getShape(6), shapes.getShape(3), shapes.getShape(1), shapes.getShape(6), shapes.getShape(2), shapes.getShape(4), shapes.getShape(5), shapes.getShape(0), shapes.getShape(0), shapes.getShape(3), shapes.getShape(3)); var hintDataList = [4,18,0, 0,18,0, 7,17,1, 0,15,1, 4,17,-1, 5,14,-1, 2,17,2, 1,17,0, -1,16,-1, 2,15,-1, 0,14,0, 3,15,1, 8,16,-1, 5,13,2, 6,16,1, 7,17,2 ]; for(var i = 0; i < this.hintQueue.length; i++) { this.hintQueue[i].x = hintDataList[i * 3]; this.hintQueue[i].y = hintDataList[i * 3 + 1]; this.hintQueue[i].state = this.hintQueue[i].nextState(hintDataList[i * 3 + 2]); } break; case 6: // Hachispin this.hintQueue = new Array( shapes.getShape(1), shapes.getShape(2), shapes.getShape(6), shapes.getShape(5), shapes.getShape(4), shapes.getShape(0), shapes.getShape(3), shapes.getShape(6), shapes.getShape(1), shapes.getShape(5), shapes.getShape(4), shapes.getShape(2), shapes.getShape(0), shapes.getShape(3)); var hintDataList = [1,18,0, 0,18,0, 8,16,-1, 2,15,1, 6,17,2, 5,16,2, 1,16,2, -1,16,-1, -1,16,0, 5,16,0, 0,14,0, 3,15,0, 8,14,-1, 7,16,-1]; for(var i = 0; i < this.hintQueue.length; i++) { this.hintQueue[i].x = hintDataList[i * 3]; this.hintQueue[i].y = hintDataList[i * 3 + 1]; this.hintQueue[i].state = this.hintQueue[i].nextState(hintDataList[i * 3 + 2]); } break; case 7: // Albatross this.hintQueue = new Array( shapes.getShape(1), shapes.getShape(5), shapes.getShape(6), shapes.getShape(0), shapes.getShape(4), shapes.getShape(2), shapes.getShape(3)); var hintDataList = [1,18,0, 0,17,-1, 8,16,-1, 5,17,2, 6,16,2, 3,16,0, 1,16,2]; for(var i = 0; i < this.hintQueue.length; i++) { this.hintQueue[i].x = hintDataList[i * 3]; this.hintQueue[i].y = hintDataList[i * 3 + 1]; this.hintQueue[i].state = this.hintQueue[i].nextState(hintDataList[i * 3 + 2]); } break; case 8: // Number One // O - 1, I - 6, L - 0, S - 5, J - 4, Z - 2, T - 3 this.hintQueue = new Array( shapes.getShape(1), shapes.getShape(4), shapes.getShape(6), shapes.getShape(0), shapes.getShape(2), shapes.getShape(5), shapes.getShape(3)); var hintDataList = [3,18,0, 0,17,2, 0,16,0, 4,15,-1, 6,17,0, 8,16,-1, 3,17,-1]; for(var i = 0; i < this.hintQueue.length; i++) { this.hintQueue[i].x = hintDataList[i * 3]; this.hintQueue[i].y = hintDataList[i * 3 + 1]; this.hintQueue[i].state = this.hintQueue[i].nextState(hintDataList[i * 3 + 2]); } break; default: return; } } this.isHintInit = 1; return; }, // End initHint getNextHint(opener) { this.initHint(opener); var mino = this.hintQueue[this.hintIdx]; this.hintIdx++; if(this.hintIdx == this.hintQueue.length) { this.hintIdx = 0; this.isHintInit = 0; } return mino; }, reset() { this.shapeQueue = []; this.hintQueue = []; this.idx = 0; this.hintIdx = 0; this.isInit = 0; this.isHintInit = 0; }, getLength() { return this.hintQueue.length; } }; function reset() { openerGenerator.reset(); } function getNextMino(opener) { var mino = openerGenerator.getNextMino(opener); return mino; } function getNextHint(opener) { var mino = openerGenerator.getNextHint(opener); return mino; } function getLength() { return openerGenerator.getLength(); } module.exports.getNextMino = getNextMino; module.exports.getNextHint = getNextHint; module.exports.getLength = getLength; module.exports.reset = reset; // export getNextMino; // export getNextHint; // export getLength; // export reset; },{"./shapes.js":7}],7:[function(require,module,exports){ var consts = require('./consts.js'); var utils = require('./utils.js'); // import * as consts from './const.js'; var COLORS = consts.COLORS; var COLUMN_COUNT = consts.COLUMN_COUNT; /** Defined all shapes used in Tetris game. You can add more shapes if you wish. */ function ShapeL() { var state1 = [ [0, 0, 1, 0], [1, 1, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0] ]; var state2 = [ [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 1, 0], [0, 0, 0, 0] ]; var state3 = [ [0, 0, 0, 0], [1, 1, 1, 0], [1, 0, 0, 0], [0, 0, 0, 0] ]; var state4 = [ [1, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0] ]; // Rotation point offsets: clockwise, counterclockwise, // In guidline Tetris each piece has 5 possible rotation points with respect to each state/orientation. Iterate through all every rotation. var state1RotationPointsOffset = [ 0, 0, 0, 0, 1, 0, -1, 0, 1, 1, -1, 1, 0, -2, 0, -2, 1, -2, -1, -2 ]; var state2RotationPointsOffset = [ 0, 0, 0, 0, 1, 0, 1, 0, 1, -1, 1, -1, 0, 2, 0, 2, 1, 2, 1, 2 ]; var state3RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, 1, 0, -1, 1, 1, 1, 0, -2, 0, -2, -1, -2, 1, -2 ]; var state4RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, -1, 0, -1, -1, -1, -1, 0, 2, 0, 2, -1, 2, -1, 2 ]; this.rotationPoints = [state1RotationPointsOffset, state2RotationPointsOffset, state3RotationPointsOffset, state4RotationPointsOffset]; this.states = [state1, state2, state3, state4]; this.x = 3; this.y = -3; this.originY = -3; this.flag = 'L'; } function ShapeLR() { var state1 = [ [1, 0, 0, 0], [1, 1, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0] ]; var state2 = [ [0, 1, 1, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0] ]; var state3 = [ [0, 0, 0, 0], [1, 1, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0] ]; var state4 = [ [0, 1, 0, 0], [0, 1, 0, 0], [1, 1, 0, 0], [0, 0, 0, 0] ]; var state1RotationPointsOffset = [ 0, 0, 0, 0, 1, 0, -1, 0, 1, 1, -1, 1, 0, -2, 0, -2, 1, -2, -1, -2 ]; var state2RotationPointsOffset = [ 0, 0, 0, 0, 1, 0, 1, 0, 1, -1, 1, -1, 0, 2, 0, 2, 1, 2, 1, 2 ]; var state3RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, 1, 0, -1, 1, 1, 1, 0, -2, 0, -2, -1, -2, 1, -2 ]; var state4RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, -1, 0, -1, -1, -1, -1, 0, 2, 0, 2, -1, 2, -1, 2 ]; this.rotationPoints = [state1RotationPointsOffset, state2RotationPointsOffset, state3RotationPointsOffset, state4RotationPointsOffset]; this.states = [state1, state2, state3, state4]; this.x = 3; this.y = -3; this.originY = -3; this.flag = 'LR'; } function ShapeO() { var state1 = [ [0, 0, 1, 1], [0, 0, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0] ]; var state2 = [ [0, 0, 1, 1], [0, 0, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0] ]; var state3 = [ [0, 0, 1, 1], [0, 0, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0] ]; var state4 = [ [0, 0, 1, 1], [0, 0, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0] ]; var state1RotationPointsOffset = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; var state2RotationPointsOffset = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; var state3RotationPointsOffset = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; var state4RotationPointsOffset = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; this.rotationPoints = [state1RotationPointsOffset, state2RotationPointsOffset, state3RotationPointsOffset, state4RotationPointsOffset]; this.states = [state1, state2, state3, state4]; this.x = 2; this.y = -2; this.originY = -2; this.flag = 'O'; } function ShapeI() { /* // North var state1 = [ [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0] ]; // East var state2 = [ [0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0] ]; // South var state3 = [ [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0] ]; // West var state4 = [ [0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0] ]; */ // North var state1 = [ [0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0] ]; // East var state2 = [ [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0] ]; // South var state3 = [ [0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0] ]; // West var state4 = [ [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0] ]; var state1RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, -2, 0, 2, 0, 1, 0, -1, 2, -2, -1, 2, -1, 1, 2 ]; var state2RotationPointsOffset = [ 0, 0, 0, 0, 2, 0, -1, 0, -1, 0, 2, 0, 2, 1, -1, 2, -1, -2, 2, -1 ]; var state3RotationPointsOffset = [ 0, 0, 0, 0, 1, 0, 2, 0, -2, 0, -1, 0, 1, -2, 2, 1, -2, 1, -1, -2 ]; var state4RotationPointsOffset = [ 0, 0, 0, 0, -2, 0, 1, 0, 1, 0, -2, 0, -2, -1, 1, -2, 1, 2, -2, 1 ]; this.rotationPoints = [state1RotationPointsOffset, state2RotationPointsOffset, state3RotationPointsOffset, state4RotationPointsOffset]; this.states = [state1, state2, state3, state4]; this.x = 3; this.y = -4; this.originY = -4; this.flag = 'I'; } function ShapeT() { var state1 = [ [0, 1, 0, 0], [1, 1, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0] ]; var state2 = [ [0, 1, 0, 0], [0, 1, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0] ]; var state3 = [ [0, 0, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0] ]; var state4 = [ [0, 1, 0, 0], [1, 1, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0] ]; // rotation points clockwise, counterclockwise var state1RotationPointsOffset = [ 0, 0, 0, 0, 1, 0, -1, 0, 1, 1, -1, 1, NaN, NaN, NaN, NaN, 1, -2, -1, -2 ]; var state2RotationPointsOffset = [ 0, 0, 0, 0, 1, 0, 1, 0, 1, -1, 1, -1, 0, 2, 0, 2, 1, 2, 1, 2 ]; var state3RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, 1, 0, NaN, NaN, NaN, NaN, 0, -2, 0, -2, -1, -2, 1, -2 ]; var state4RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, -1, 0, -1, -1, -1, -1, 0, 2, 0, 2, -1, 2, -1, 2 ]; this.rotationPoints = [state1RotationPointsOffset, state2RotationPointsOffset, state3RotationPointsOffset, state4RotationPointsOffset]; this.states = [state1, state2, state3, state4]; this.x = 3; this.y = -2; this.originY = -2; this.flag = 'T'; } function ShapeZ() { var state1 = [ [1, 1, 0, 0], [0, 1, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0] ]; var state2 = [ [0, 0, 1, 0], [0, 1, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0] ]; var state3 = [ [0, 0, 0, 0], [1, 1, 0, 0], [0, 1, 1, 0], [0, 0, 0, 0] ]; var state4 = [ [0, 1, 0, 0], [1, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 0] ]; // Rotation point offsets: clockwise, counterclockwise, // In guidline Tetris each piece has 5 possible rotation points with respect to each state/orientation. Iterate through all every rotation. var state1RotationPointsOffset = [ 0, 0, 0, 0, 1, 0, -1, 0, 1, 1, -1, 1, 0, -2, 0, -2, 1, -2, -1, -2 ]; var state2RotationPointsOffset = [ 0, 0, 0, 0, 1, 0, 1, 0, 1, -1, 1, -1, 0, 2, 0, 2, 1, 2, 1, 2 ]; var state3RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, 1, 0, -1, 1, 1, 1, 0, -2, 0, -2, -1, -2, 1, -2 ]; var state4RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, -1, 0, -1, -1, -1, -1, 0, 2, 0, 2, -1, 2, -1, 2 ]; this.rotationPoints = [state1RotationPointsOffset, state2RotationPointsOffset, state3RotationPointsOffset, state4RotationPointsOffset]; this.states = [state1, state2, state3, state4]; this.x = 3; this.y = -2; this.originY = -2; this.flag = 'Z'; } function ShapeZR() { var state1 = [ [0, 1, 1, 0], [1, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0] ]; var state2 = [ [0, 1, 0, 0], [0, 1, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0] ]; var state3 = [ [0, 0, 0, 0], [0, 1, 1, 0], [1, 1, 0, 0], [0, 0, 0, 0] ]; var state4 = [ [1, 0, 0, 0], [1, 1, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0] ]; // Rotation point offsets: clockwise, counterclockwise, // In guidline Tetris each piece has 5 possible rotation points with respect to each state/orientation. Iterate through all every rotation. var state1RotationPointsOffset = [ 0, 0, 0, 0, 1, 0, -1, 0, 1, 1, -1, 1, 0, -2, 0, -2, 1, -2, -1, -2 ]; var state2RotationPointsOffset = [ 0, 0, 0, 0, 1, 0, 1, 0, 1, -1, 1, -1, 0, 2, 0, 2, 1, 2, 1, 2 ]; var state3RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, 1, 0, -1, 1, 1, 1, 0, -2, 0, -2, -1, -2, 1, -2 ]; var state4RotationPointsOffset = [ 0, 0, 0, 0, -1, 0, -1, 0, -1, -1, -1, -1, 0, 2, 0, 2, -1, 2, -1, 2 ]; this.rotationPoints = [state1RotationPointsOffset, state2RotationPointsOffset, state3RotationPointsOffset, state4RotationPointsOffset]; this.states = [state1, state2, state3, state4]; this.x = 3; this.y = -2 this.originY = -2; this.flag = 'ZR'; } /** doesShapeOverlap @param shape: tetris shape @param matrix: game matrix */ var doesShapeOverlap = function(shape, matrix) { var rows = matrix.length; var cols = matrix[0].length; var rotationDirection = 0; var isBoxInMatrix = function(box) { var x = shape.x + box.x; var y = shape.y + box.y; if(isNaN(x))return true; if(isNaN(y))return true; if(x < 0) return true; if(x > matrix.cols)return true; if(y > rows) return true; // todo: why is matrix not defined when piece popped from hold stack if(matrix[y] == undefined) return true; //console.log("matrix X Y: " + " " + x + " "+ y); return (matrix[y][x] != 0) }; boxes = shape.getBoxes(shape.state); for (var i in boxes) if (isBoxInMatrix(boxes[i])) return true; return false; }; /** Is same on matrix @param shape: tetris shape @param hintPiece: hintPiece shape @param matrix: game matrix @param action: 'left','right','down','rotate' */ var isBoxesSame = function(shape, hintPiece) { var isBoxSame = function(shapeBox, hintPieceBox) { var shapeX = shape.x + shapeBox.x; var shapeY = shape.y + shapeBox.y; var hintPieceX = hintPiece.x + hintPieceBox.x; var hintPieceY = hintPiece.y + hintPieceBox.y; if(shapeX == hintPieceX && shapeY == hintPieceY) return true; return false; }; //var boxes = action === 'rotate'?shape.getBoxes(shape.nextState()) : shape.getBoxes(shape.state); var boxes; var hintPieceBoxes; boxes = shape.getBoxes(shape.state); hintPieceBoxes = hintPiece.getBoxes(hintPiece.state); for (var i in boxes) { if (!isBoxSame(boxes[i], hintPieceBoxes[i])) { return false; } } return true; }; /** Is shape can move @param shape: tetris shape @param matrix: game matrix @param action: 'left','right','down','rotate' */ var isShapeCanMove = function(shape, matrix, action) { var rows = matrix.length; var cols = matrix[0].length; var rotationDirection = 0; var isBoxCanMove = function(box) { var x = shape.x + box.x; var y = shape.y + box.y; if (y < 0) { return true; } if (action === 'left') { x -= 1; return x >= 0 && x < cols && matrix[y][x] == 0; } else if (action === 'right') { x += 1; return x >= 0 && x < cols && matrix[y][x] == 0; } else if (action === 'down') { y += 1; return y < rows && matrix[y][x] == 0; } else if (action === 'rotate') { rotationDirection = 1; return y < rows && !matrix[y][x]; } else if (action === 'rotateclockwise') { rotationDirection = -1; return y < rows && !matrix[y][x]; } }; //var boxes = action === 'rotate'?shape.getBoxes(shape.nextState()) : shape.getBoxes(shape.state); var boxes; if(rotationDirection != 0) boxes = shape.getBoxes(shape.nextState(rotationDirection)); else boxes = shape.getBoxes(shape.state); for (var i in boxes) { if (!isBoxCanMove(boxes[i])) { return false; } } return true; }; /** All shapes shares the same method, use prototype for memory optimized */ ShapeL.prototype = ShapeLR.prototype = ShapeO.prototype = ShapeI.prototype = ShapeT.prototype = ShapeZ.prototype = ShapeZR.prototype = { init: function(result) { this.color = COLORS[result]; this.state = 0; this.allBoxes = {}; this.y = 0; }, // Get boxes matrix which composite the shape getBoxes: function(state) { var boxes = this.allBoxes[state] || []; if (boxes.length) { return boxes; } var matrix = this.matrix(state); for (var i = 0; i < matrix.length; i++) { var row = matrix[i]; for (var j = 0; j < row.length; j++) { if (row[j] === 1) { boxes.push({ x: j, y: i }); } } } this.allBoxes[state] = boxes; return boxes; }, //Get matrix for specified state matrix: function(state) { var st = state !== undefined ? state : this.state; return this.states[st]; }, canMoveTo: function(shape, matrix) { if(!doesShapeOverlap(shape, matrix)) return true; return false; }, // 0 - no, 1 - up,left, 2 - up,right, 3 - down,left, 4 - down, right kickShape: function(matrix, rotationDirection) { let clone = utils.deepClone(this); for(var j = 0; j < 4; j++) { if(this.state == j) { clone.state = this.nextState(rotationDirection); var i = 0; if(rotationDirection == 1) i = 2; for(; i < 5*4; i+=4) { var shiftX = this.rotationPoints[j][i]; var shiftY = this.rotationPoints[j][i+1]; if(!isNaN(shiftY) && !isNaN(shiftX)) { clone.x = this.x + shiftX; clone.y = this.y - shiftY; if(this.canMoveTo(clone, matrix) == true) { this.state = clone.state; this.x = clone.x; this.y = clone.y; return; } } } } } }, //Rotate shape rotate: function(matrix) { this.kickShape(matrix, -1); }, //Rotate shape clockwise rotateClockwise: function(matrix) { this.kickShape(matrix, 1); }, //Caculate the max column of the shape getColumnCount: function() { var mtx = this.matrix(); var colCount = 0; for (var i = 0; i < mtx.length; i++) { colCount = Math.max(colCount, mtx[i].length); } return colCount; }, //Caculate the max row of the shape getRowCount: function() { return this.matrix().length; }, //Get the right pos of the shape getRight: function() { var boxes = this.getBoxes(this.state); var right = 0; for (var i in boxes) { right = Math.max(boxes[i].x, right); } return this.x + right; }, //Return the next state of the shape nextState: function(direction) { if(direction == 0) return this.state; var rotate = this.state; rotate += direction; if(rotate < 0) return this.states.length - 1; return rotate % this.states.length; }, //Check if the shape can move down canDown: function(matrix) { return isShapeCanMove(this, matrix, 'down'); }, //Move the shape down goDown: function(matrix) { if (isShapeCanMove(this, matrix, 'down')) { this.y += 1; } }, //Move the shape to the Bottommost bottomAt: function(matrix) { var save = this.y; var ret; while (isShapeCanMove(this, matrix, 'down')) { this.y += 1; } ret = this.y; this.y = save; return ret; }, //Move the shape to the Bottommost goBottom: function(matrix) { while (isShapeCanMove(this, matrix, 'down')) { this.y += 1; } }, //Move the shape to the left goLeft: function(matrix) { if (isShapeCanMove(this, matrix, 'left')) { new Audio('./dist/Click.ogg').play(); this.x -= 1; } }, //Move the shape to the right goRight: function(matrix) { if (isShapeCanMove(this, matrix, 'right')) { new Audio('./dist/Click.ogg').play(); this.x += 1; } }, //Copy the shape data to the game data copyTo: function(matrix) { var smatrix = this.matrix(); for (var i = 0; i < smatrix.length; i++) { var row = smatrix[i]; for (var j = 0; j < row.length; j++) { if (row[j] === 1) { var x = this.x + j; var y = this.y + i; if (x >= 0 && x < matrix[0].length && y >= 0 && y < matrix.length) { matrix[y][x] = this.color; } } } } }, // check if piece is same on matrix isSameSRS: function(shape) { return isBoxesSame(this, shape) }, resetOrigin: function() { this.y = this.originY + 1; } } /** Create a random shape for game */ function getRandomInt(max) { return Math.floor(Math.random() * Math.floor(max)); } // Handles randomly generating and returning a tetromino var RandomGenerator = { returnBag: [], getTetrimino() { if(this.returnBag.length < 7) this.returnBag.push.apply(this.returnBag, this.generateNewBag()); return parseInt(this.returnBag.shift()); }, onlyUnique(value, index, self) { return self.indexOf(value) === index; }, generateNewBag() { var minoes = ['0','1','2','3','4','5','6']; var newBag = []; var bagLength = 7; while(newBag.length < bagLength) { mino = getRandomInt(bagLength); newBag.push(minoes[mino]); newBag = newBag.filter(this.onlyUnique); } return newBag; }, reset() { if(this.returnBag != undefined) this.returnBag.splice(0, returnBag.length); } }; function randomShape() { var result = RandomGenerator.getTetrimino(); var shape; switch (result) { case 0: shape = new ShapeL(); break; case 1: shape = new ShapeO(); break; case 2: shape = new ShapeZ(); break; case 3: shape = new ShapeT(); break; case 4: shape = new ShapeLR(); break; case 5: shape = new ShapeZR(); break; case 6: shape = new ShapeI(); break; } shape.init(result); return shape; } function getShape(shapei) { var result = shapei var shape; switch (result) { case 0: shape = new ShapeL(); break; case 1: shape = new ShapeO(); break; case 2: shape = new ShapeZ(); break; case 3: shape = new ShapeT(); break; case 4: shape = new ShapeLR(); break; case 5: shape = new ShapeZR(); break; case 6: shape = new ShapeI(); break; } shape.init(result); return shape; } module.exports.resetMinoRNG = RandomGenerator.reset; module.exports.randomShape = randomShape; module.exports.getShape = getShape; // export randomShape; // export getShape; },{"./consts.js":2,"./utils.js":8}],8:[function(require,module,exports){ var exports = module.exports = {}; var setCookie = function(cname, cvalue, exdays) { var d = new Date(); d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); var expires = "expires="+d.toUTCString(); document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; }; var getCookie = function(cname) { var name = cname + "="; var ca = document.cookie.split(';'); for(var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') { c = c.substring(1); } if (c.indexOf(name) == 0) { return c.substring(name.length, c.length); } } return ""; }; var $ = function(id){ return document.getElementById(id); }; //if object is plain object var _isPlainObject = function(obj) { if (typeof obj !== 'object') { return false; } if (obj.constructor && !hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf')) { return false; } // If the function hasn't returned already, we're confident that // |obj| is a plain object, created by {} or constructed with new Object return true; }; // Deeper clone var deepClone = function(copyObject) { return Object.assign(Object.create(Object.getPrototypeOf(copyObject)), copyObject); }; // this method source code is from jquery 2.0.x // merge object's value and return var extend = function() { var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = true; // Handle a deep copy situation if (typeof target === 'boolean') { deep = target; // skip the boolean and the target target = arguments[i] || {}; i++; } // Handle case when target is a string or something (possible in deep copy) if (typeof target !== 'object' && typeof obj !== 'function') { target = {}; } if (i === length) { target = this; i--; } for (; i < length; i++) { // Only deal with non-null/undefined values if ((options = arguments[i]) != null) { // Extend the base object for (name in options) { src = target[name]; copy = options[name]; // Prevent never-ending loop if (target === copy) { continue; } // Recurse if we're merging plain objects or arrays if (deep && copy && (_isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { if (copyIsArray) { copyIsArray = false; clone = src && Array.isArray(src) ? src : []; } else { clone = src && _isPlainObject(src) ? src : {}; } // Never move original objects, clone them //console.log('abc'); target[name] = extend(deep, clone, copy); // Don't bring in undefined values } else if (copy !== undefined) { target[name] = copy; } } } } // Return the modified object return target; }; var proxy = function(fn,context){ var args = [].slice.call( arguments, 2 ); proxy = function() { return fn.apply( context || this, args.concat( [].slice.call( arguments ) ) ); }; return proxy; }; var aniFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; window.requestAnimationFrame = aniFrame; exports.$ = $; exports.extend = extend; exports.proxy = proxy; exports.deepClone = deepClone; exports.setCookie = setCookie; exports.getCookie = getCookie; // export $; // export extend; // export proxy; },{}],9:[function(require,module,exports){ /** All dom definitions and actions */ var utils = require('./utils.js'); var consts = require('./consts.js'); // import * as utils from './utils.js'; // import * as consts from './const.js'; var $ = utils.$; //doms var scene = $('scene'); var side = $('side'); var info = $('info'); var preview = $('preview'); var hold = $('hold'); var leftSide = $('leftSide'); var level = $('level'); var score = $('score'); var lines = $('lines'); var rewardInfo = $('rewardInfo'); var reward = $('reward'); var gameOver = $('gameOver'); var btnRestart = $('restart'); var finalScore = $('finalScore'); //defaults var SIDE_WIDTH = consts.SIDE_WIDTH; /** Caculate the game container size */ var getContainerSize = function(maxW,maxH){ var dw = document.documentElement.clientWidth; var dh = document.documentElement.clientHeight; var size = {}; if (dw>dh){ size.height = Math.min(maxH,dh); size.width = Math.min(size.height /*/ 2*/ + SIDE_WIDTH,maxW); }else{ size.width = Math.min(maxW,dw); size.height = Math.min(maxH,dh); } return size; }; /** Layout game elements */ var layoutView = function(container,maxW,maxH){ var size = getContainerSize(maxW,maxH); var st = container.style; st.height = size.height + 'px'; st.width = size.width + 'px'; st.marginTop = (-(size.height/2)) + 'px'; st.marginLeft = (-(size.width/2)) + 'px'; //layout scene //hold.width = 80; //hold.height = 380; scene.height = size.height; scene.width = scene.height / 2; var sideW = size.width - scene.width + leftSide.width; side.style.width = sideW + 'px'; if (sideW < SIDE_WIDTH ){ info.style.width = side.style.width; } side.style.height = 500 + 'px'; hold.style.top = 10+'px';//preview.top + 10px pad preview.width = 80; preview.height = 380; hold.width = 80; hold.height = 380; gameOver.style.width = scene.width +'px'; } /** Main tetris game view */ var tetrisView = { init:function(id, maxW,maxH){ this.container = $(id); this.scene = scene; this.preview = preview; this.hold = hold; this.btnRestart = btnRestart; layoutView(this.container,maxW,maxH); this.scene.focus(); rewardInfo.addEventListener('animationEnd',function(e){ rewardInfo.className = 'invisible'; }); }, // Update the score setScore:function(scoreNumber){ score.innerHTML = scoreNumber; }, // Update the finnal score setFinalScore:function(scoreNumber){ finalScore.innerHTML = scoreNumber; }, // Update the level setLevel:function(levelNumber){ level.innerHTML = levelNumber; }, // Update the extra reward score setLines:function(setlines){ //lines.innerHTML = setlines; }, setReward:function(rewardScore){ if (rewardScore>0){ reward.innerHTML = rewardScore; rewardInfo.className = 'fadeOutUp animated'; }else{ rewardInfo.className = 'invisible'; } }, // Set game over view setGameOver:function(isGameOver){ gameOver.style.display = isGameOver?'block':'none'; } }; module.exports = tetrisView; // export tetrisView; },{"./consts.js":2,"./utils.js":8}]},{},[5]);