(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.settingsList = this.keyboardKeySettings.concat(this.keyboardShiftEvents.concat(this.keyboardKeyEvents.concat(this.gamepadSettings.concat(this.gamepadShiftEvents.concat(this.gamepadButtonEvents))))); //;["Soft Drop Rate [1 - 100]"].concat(this.keyboardSettings.concat(this.gamepadSettings)); this.gamepadShiftTimer = new Date(); this.gamepadButtonTimer = new Date(); this.keyboardKeyTimer = new Date(); this.keyboardShiftTimer = new Date(); this.settingsMap = new Map(); this.gamepadEventMap = new Map(); ///this.gamepadEventMap = 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]); this.gamepadEvents = this.gamepadShiftEvents.concat(this.gameButtonsEvents); var mapIdx = [14, 7, 13, 5, 4, 1, 2, 12, 8, 3]; for(var i in mapIdx) { this.gamepadEventMap.set(this.gamepadEvents[i], gamepad.buttons[mapIdx[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; }, processGamepadInput() { this.gamepadButtonEvents.forEach(gamepadButton => this.gamepadButtonsDown(this.settingsMap.get(gamepadButton))); }, processGamepadDPad() { this.gamepadShiftEvents.forEach(dpadButton => this.gamepadDPadDown(this.settingsMap.get(dpadButton))); }, // Single press gamepad buttons gamepadButtonsDown(finds) { var DAS = 167.0; var ARR = 300.0; var isContained = this.gpButtons.includes(finds); var isPrevContained = this.prevGpButtons.includes(finds); if(isPrevContained != isContained ) { // Not being held yet this.gamepadButtonTimer = new Date(); this.isPassedDelayGamepadShift = false; // Add button to queue on pressed input if(isContained) this.gamepadQueue.push(finds); } var deltaTime = (new Date()).getTime() - this.gamepadButtonTimer.getTime(); if (!this.isPassedDelayGamepadShift) { if (deltaTime >= DAS) { this.gamepadButtonTimer = new Date(); this.isPassedDelayGamepadShift = true; } } else { if (deltaTime >= ARR && isContained) { this.gamepadQueue.push(finds); this.gamepadButtonTimer = new Date(); } } }, // Direction Pad gamepadDPadDown(finds) { var DAS = parseInt(this.settingsMap.get("Gamepad DAS")); var ARR = parseInt(this.settingsMap.get("Gamepad ARR")); var isContained = this.gpButtons.includes(finds); var isPrevContained = this.prevGpButtons.includes(finds); if(isPrevContained != isContained ) { // Not being held yet this.gamepadShiftTimer = new Date(); this.isDelayedPassedGamepadShift = false; // Add button to queue on pressed input if(isContained) this.gamepadQueue.push(finds); } var deltaTime = (new Date()).getTime() - this.gamepadShiftTimer.getTime(); if (!this.isDelayedPassedGamepadShift) { if (deltaTime >= DAS) { this.gamepadShiftTimer = new Date(); this.isDelayedPassedGamepadShift = true; } } else { if (deltaTime >= ARR && isContained) { this.gamepadQueue.push(finds); this.gamepadShiftTimer = new Date(); } } return; }, processKeys() { this.keyboardKeyEvents.forEach( key => this.settingsMap.get(key).split(',').forEach( idx => this.processKeyDown( parseInt(idx) ) ) ); }, // keyboard keys z,x,space processKeyDown(key) { var DAS = 167.0; var ARR = 300.0; if(this.prevKeyboardKeys[key] != this.keyboardKeys[key]) { // Not being held yet this.keyboardKeyTimer = new Date(); this.isPassedDelayKeyboardKey = false; // Add shift to queue on pressed input if(this.keyboardKeys[key] == true) this.inputQueue.push(key); } var deltaTime = (new Date()).getTime() - this.keyboardKeyTimer.getTime(); if (!this.isPassedDelayKeyboardKey) { if (deltaTime >= DAS) { this.keyboardKeyTimer = new Date(); this.keyboardButtonsDeciframes = 0; this.isPassedDelayKeyboardKey = true; } } else { if (deltaTime >= ARR && this.keyboardKeys[key] == true) { this.inputQueue.push(key); this.keyboardKeyTimer = new Date(); } } }, // Process applicable key inputs processKeyShift() { this.keyboardShiftEvents.forEach(arrowKey => arrowKey.split(',').forEach(option => this.processKeyboardArrowKeys(parseInt(this.settingsMap.get(option))))); }, // Direction arrows processKeyboardArrowKeys(key) { var DAS = parseInt(this.settingsMap.get("Keyboard DAS")); var ARR = parseInt(this.settingsMap.get("Keyboard ARR")); if(this.prevKeyboardKeys[key] != this.keyboardKeys[key]) { // Not being held yet this.isPassedDelayKeyboardShift = false; this.keyboardShiftTimer = new Date(); // Do shift if key has been pushed down if(this.keyboardKeys[key] == true) this.inputQueue.push(key); } var deltaTime = (new Date()).getTime() - this.keyboardShiftTimer.getTime(); if (!this.isPassedDelayKeyboardShift) { if (deltaTime >= DAS) { this.keyboardShiftTimer = new Date(); this.isPassedDelayKeyboardShift = true; } } else if(deltaTime >= ARR && this.keyboardKeys[key] == true) { this.inputQueue.push(key); this.keyboardShiftTimer = new Date(); } }, keyDown(event) { if(event.keyCode != 38) 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; }, gamepadClear() { //this.gpButtons = []; //this.gamepadQueue = []; }, saveButtons() { this.prevGpButtons = this.gpButtons; }, saveKeyboardKeys() { //this.prevKeyboardKeys = utils.deepClone(this.keyboardKeys); this.prevKeyboardKeys = {...this.keyboardKeys}; }, // button states isPassedDelayKeyboardShift: false, isPassedDelayKeyboardKey: false, isPassedDelayGamepadShift: false, isPassedDelayGamepadButton: false, // buttons state contatiners gpButtons: [], prevGpButtons:[], keyboardKeys: [], prevKeyboardKeys: [], // button pressed containers inputQueue: [], gamepadQueue: [], keyboardKeySettings: ["Keyboard DAS", "Keyboard ARR"], keyboardShiftEvents: ["Keyboard Left", "Keyboard Right", "Keyboard Down"], keyboardKeyEvents: ["Keyboard Harddrop", "Keyboard Hold", "Keyboard Rotateccw", "Keyboard Rotate", "Keyboard Pophold", "Keyboard Reset", "Keyboard Background"], gamepadSettings: ["Gamepad DAS", "Gamepad ARR"], gamepadShiftEvents: ["Gamepad Left", "Gamepad Right","Gamepad Down"], gamepadButtonEvents: ["Gamepad Harddrop", "Gamepad Hold", "Gamepad Rotateccw", "Gamepad Rotate", "Gamepad Pophold", "Gamepad Reset", "Gamepad Background"], settingsList: [], settingsDefault: [ "167.0", "33.0", "37", "39", "40", "32", "16", "90", "88,38", "17", "82", "81", "167.0", "33.0", "DPad-Left", "DPad-Right", "DPad-Down", "RB", "LB", "A", "B", "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) { 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.gameState = consts.DEFAULT_GAMESTATE; this.isTimerOn = false; this.currentOpener = 0; 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(); // ewww this._recurseGameState(); }, 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() { this.gameState = consts.GAMESTATES[0]; document.getElementById("Timer").value = "Timer Off"; document.getElementById("Time").value = ""; document.getElementById("besttime").value = ""; this.isTimerOn = false; this.hintQueue = []; this.shapeQueue = []; this.hintMino = 0; this._restartHandler(); this.currentOpener = 0; }, setCurrentOpener(opener) { document.getElementById("besttime").value = ""; this.gameState = consts.GAMESTATES[1]; this.currentOpener = opener; this._restartHandler(); }, setDoTest: function() { if(this.gameState != consts.GAMESTATES[1]) return; // set game state to do test this.gameState = consts.GAMESTATES[2]; 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() { // var setting = inputs.settingsList[document.getElementById("settings").selectedIndex-1]; // var doKeyToAlpha = inputs.keyboardShiftEvents.included(setting) || inputs.keyboardKeyEvents.included(setting); // if(doKeyToAlpha) // document.getElementById("setting_value").value = inputs.settingsMap.get(setting).fromCharCode(); // else // document.getElementById("setting_value").value = inputs.settingsMap.get(setting); document.getElementById("setting_value").value = inputs.settingsMap.get(inputs.settingsList[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 = []; this.shape = shapes.getShape(0); // 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.gameState == consts.GAMESTATES[0] && 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() { // 1 shape hold queue if(this.holdStack.length > 0) { this.canPopFromHoldStack = false; this.shapeQueue.unshift(utils.deepClone(this.shape)); this.shape = this.holdStack.pop(); this.shape.resetOrigin(); this._draw(); }else 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(); } /* 4 shape hold queue 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() { // todo: disable if 1 shape hold queue 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(); this._recurseGameState(); }, // Bind game events _initEvents: function() { setInterval(() => {this._processTick();}, 1); setInterval(() => {this.lockDownTimer++;}, 100 ); views.btnRestart.addEventListener('click', utils.proxy(this._restartHandler, this), false); }, // Process freeplay queue _processFreeplayQueue: function() { while(this.shapeQueue.length <= 4) { this.preparedShape = shapes.randomShape(); this.shapeQueue.push(this.preparedShape); } this.shape = this.shapeQueue.shift();// || shapes.randomShape(); this.currentMinoInx++; }, // Process opener trainer queue _processOpenerTrainerQueue: function() { 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()) { new Audio("./dist/sound/Affirm.ogg").play(); 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.hintQueue = []; this.shapeQueue = []; this.isSequenceCompleted = true; // Recursion warning this._restartHandler(); // this.reset(); // this.start(); return; } }, // Fill next queue and set next shape _fireShape: function() { //todo:should be in shapes.js this.landed = false; this.manipulationCounter = 0; this._draw(); }, _recurseGameState: function (){ switch(this.gameState) { case consts.GAMESTATES[0]: this._processFreeplayQueue(); this._fireShape(); break; case consts.GAMESTATES[1]: this._processOpenerTrainerQueue(); this._fireShape(); break; case consts.GAMESTATES[2]: this._processOpenerTrainerQueue(); this._fireShape(); break; default: break; } }, // 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.gameState != consts.GAMESTATES[2]) 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 and any ARR or DAS below 15ms will likely be broken _processTick: async function() { if(this.isTimerOn) { var deltaPlayTime = new Date().getTime() - this.sequencePrevTime; 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; 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(inputs.settingsMap.get("Gamepad Left").includes(curkey)) { this.shape.goLeft(this.matrix); this.resetLockdown(); this._draw(); } else if(inputs.settingsMap.get("Gamepad Right").includes(curkey)) { this.shape.goRight(this.matrix); this.resetLockdown(); this._draw(); } else if(inputs.settingsMap.get("Gamepad Rotateccw").includes(curkey)) { this.shape.rotate(this.matrix); this.resetLockdown(); this._draw(); } else if(inputs.settingsMap.get("Gamepad Rotate").includes(curkey)) { this.shape.rotateClockwise(this.matrix); this.resetLockdown(); this._draw(); } else if(inputs.settingsMap.get("Gamepad Down").includes(curkey)) { this.shape.goDown(this.matrix); this._draw(); } else if(inputs.settingsMap.get("Gamepad Harddrop").includes(curkey)) { this.shape.goBottom(this.matrix); this.lockDownTimer = 5000; this._update(); } else if(inputs.settingsMap.get("Gamepad Hold").includes(curkey)) { this.pushHoldStack(); this._draw(); } else if(inputs.settingsMap.get("Gamepad Pophold").includes(curkey)) { this.popHoldStack(); this._draw(); } else if(inputs.settingsMap.get("Gamepad Reset").includes(curkey)) { this._restartHandler(); return; } } inputs.saveButtons(); inputs.gamepadClear(); } // Do keyboard inputs.processKeys(); inputs.processKeyShift(); // Keyboard inputs while((inputs.inputQueue != undefined && inputs.inputQueue.length >= 1)){ var curkey = inputs.inputQueue.shift(); if(inputs.settingsMap.get("Keyboard Left").includes(curkey)) { this.shape.goLeft(this.matrix); this.resetLockdown(); this._draw(); } else if(inputs.settingsMap.get("Keyboard Right").includes(curkey)) { this.shape.goRight(this.matrix); this.resetLockdown(); this._draw(); } else if(inputs.settingsMap.get("Keyboard Down").includes(curkey)) { this.shape.goDown(this.matrix); this._draw(); } else if(inputs.settingsMap.get("Keyboard Rotateccw").includes(curkey)) { this.shape.rotate(this.matrix); this.resetLockdown(); this._draw(); } else if(inputs.settingsMap.get("Keyboard Rotate").includes(curkey)) { this.shape.rotateClockwise(this.matrix); this.resetLockdown(); this._draw(); } else if(inputs.settingsMap.get("Keyboard Harddrop").includes(curkey)) { this.shape.goBottom(this.matrix); this.lockDownTimer = 5000; this._update(); } else if(inputs.settingsMap.get("Keyboard Hold").includes(curkey)) { this.pushHoldStack(); this._draw(); } else if(inputs.settingsMap.get("Keyboard Pophold").includes(curkey)) { this.popHoldStack(); this._draw(); } else if(inputs.settingsMap.get("Keyboard Hold").includes(curkey)) { if(document.getElementById("divbg").style.display == "none") document.getElementById("divbg").style.display = "initial"; else document.getElementById("divbg").style.display="none"; } if(inputs.settingsMap.get("Keyboard Reset").includes(curkey)) { 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.gameState == consts.GAMESTATES[0]) 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(); this._recurseGameState(); new Audio('./dist/sound/Blop2.ogg').play(); } this._draw(); this.isGameOver = checkGameOver(this.matrix); // if game over and gamestate is free play views.setGameOver(this.gameState == consts.GAMESTATES[0] && 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[side4Y] != undefined && matrix[side3Y] != undefined) { 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][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; case 9: // Pelican this.shapeQueue = new Array( shapes.getShape(5), shapes.getShape(2), shapes.getShape(4), shapes.getShape(0), shapes.getShape(6), shapes.getShape(1), shapes.getShape(3)); break; case 10: // DT Cannon TSZ base this.shapeQueue = new Array( shapes.getShape(3), shapes.getShape(1), shapes.getShape(2), shapes.getShape(6), shapes.getShape(5), shapes.getShape(0), shapes.getShape(4), shapes.getShape(4), shapes.getShape(0), shapes.getShape(3), 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: this.hintQueue = []; break; 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 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; case 9: // Pelican this.hintQueue = new Array( shapes.getShape(5), shapes.getShape(2), shapes.getShape(4), shapes.getShape(0), shapes.getShape(6), shapes.getShape(1), shapes.getShape(3)); var hintDataList = [0,17,-1, 1,16,2, 5,18,0, 5,16,-1, 8,16,-1, 5,17,0, 3,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 10: // O - 1, I - 6, L - 0, S - 5, J - 4, Z - 2, T - 3 // DT Cannon TSZ base this.hintQueue = new Array( shapes.getShape(3), shapes.getShape(1), shapes.getShape(2), shapes.getShape(6), shapes.getShape(5), shapes.getShape(0), shapes.getShape(4), shapes.getShape(4), shapes.getShape(0), shapes.getShape(3), shapes.getShape(3)); var hintDataList = [4,18,0, -2,18,0, 2,17,1, 8,16,-1, 6,17,-1, 7,17,-1, 3,15,2, -1,15,1, 2,13,-1, 1,16,2, 1,17,3]; 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/sound/Click.ogg').play(); this.x -= 1; } }, //Move the shape to the right goRight: function(matrix) { if (isShapeCanMove(this, matrix, 'right')) { new Audio('./dist/sound/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]);