(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.settingsMap = new Map(); var options = ""; // 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.getElementById("setting").innerHTML = settings; document.addEventListener('keydown', this.keyDown.bind(this)); document.addEventListener('keyup', this.keyUp.bind(this)); }, updateGamepad() { this.gpButtons = gamepad.update(); }, incDeciframes() { this.keyboardButtonsDeciframes++; this.keyboardDirectionArrowsDeciframes++; this.gamepadButtonsDeciFrames++; this.gamepadDirectionPadDeciFrames++; }, incTickCounter() { this.ticks++; }, getTickCounter() { return this.ticks; }, processGamepadInput() { // this.gamepadButtonsDown("RB"); // hard drop // this.gamepadButtonsDown("LB"); // hold // this.gamepadButtonsDown("A"); // rotate counter // this.gamepadButtonsDown("B"); // rotate cwise // this.gamepadButtonsDown("DPad-Up"); // Pop hold stack // this.gamepadButtonsDown("Back"); // reset //this.gamepadButtonsDown("X"); //this.gamepadButtonsDown("Y"); 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; }, 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]) { this.isDirectionArrowDown = false; if(this.keyboardKeys[key] == true) this.inputqueue.push(key); } var keyboardDASFrames = this.keyboardDirectionArrowsDeciframes; if (!this.isDirectionArrowDown) { if (keyboardDASFrames >= DAS) { this.keyboardDirectionArrowsDeciframes = 0; this.isDirectionArrowDown = 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 = {...this.keyboardKeys}; }, // button states isDirectionArrowDown: 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, // todo: change to human readable 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", "SameSite"], settingsDefault: ["true", "65.0", "20.0", "32", "16", "37", "39", "90", "88", "40", "17", "82", "81", "65.0", "20.0", "RB", "LB", "DPad-Left", "DPad-Right", "A", "B", "DPad-Down", "DPad-Up", "Back", "", "=/", "Strict"], 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: 600 }; /** 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.isFreePlay = true; this.currentOpener = 0; this.doTest = false; this.matrix = initMatrix(consts.ROW_COUNT, consts.COLUMN_COUNT); this.reset(); this._initEvents(); this._fireShape(); }, setFreePlay: function() { this.isFreePlay = true; this.doTest = false; this.hintQueue = []; this.shapeQueue = []; this.hintMino = 0; this._restartHandler(); this.currentOpener = 0; }, setTKIFonzieVar: function() { this.isFreePlay = false; this.doTest = false; this.currentOpener = 1; this._restartHandler(); }, setDTCannonVar: function() { this.isFreePlay = false; this.doTest = false; this.currentOpener = 2; this._restartHandler(); }, setMKOStackingVar: function () { this.isFreePlay = false; this.doTest = false; this.currentOpener = 3; this._restartHandler(); }, setDoTest: function() { if(this.isFreePlay) return; this.doTest = true;//!this.doTest; 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() { console.log(document.getElementById("setting_value").value = inputs.settingsDefault[document.getElementById("settings").selectedIndex-1]); }, setSettings: function() { //inputs.settingsDefault[document.getElementById("settings").selectedIndex-1] = document.getElementById("setting_value").value; 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.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; //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; clearMatrix(this.matrix); views.setLevel(this.level); views.setScore(this.score); views.setGameOver(this.isGameOver); openers.reset(); 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(); //canvas.drawHoldShape(this.holdStack); this._draw(); // update? } }, 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(); //canvas.drawHoldShape(this.holdStack); 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();// shapes.randomShape(); this.currentMinoInx++; if(this.currentMinoInx > openers.getLength()) { this.hintQueue = []; this.shapeQueue = []; 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 _processTick: async function() { var deltaTime = 1.0; // 1 millisecond var tenthOfFrame = 1.0//1;//1.6; // 1.6ms = 1 fram var halfFrame = 5.0//5;//8.0; var halfFramePlus = 10.0;//10.0; inputs.incDeciframes(); inputs.incTickCounter(); if(inputs.getTickCounter() >= tenthOfFrame) { inputs.updateGamepad(); inputs.processGamepadDPad(); inputs.processGamepadInput(); } // Don't process game related events if game over if(this.isGameOver) return; // 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 if(inputs.getTickCounter() > tenthOfFrame) // 120hz { inputs.processKeys(); } if (inputs.getTickCounter() > tenthOfFrame) { // 60hz inputs.processKeyShift(); // Keyboard inputs while((inputs.inputqueue != undefined && inputs.inputqueue.length >= 1)){ var curkey = inputs.inputqueue.shift(); if(curkey == 37) { 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._update(); this._draw(); } if(curkey == 17 || curkey == 67) { this.popHoldStack(); //this._update(); 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 = []; } if(inputs.getTickCounter() >= halfFramePlus) inputs.saveKeyboardKeys(); if(inputs.getTickCounter() >= tenthOfFrame) inputs.saveButtons(); }, // Refresh game canvas _refresh: async function() { if (!this.running) { return; } this.currentTime = new Date().getTime(); var curInputTime = new Date().getTime(); this.prevInputTime = curInputTime; var deltaLevelTime = this.currentTime - this.prevTime; if (deltaLevelTime > this.interval) { 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; 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(rows.length >= 4) // new Audio('./dist/Tetris.ogg').play(); 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 -- O I L S J Z T O I L J T O T 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: // O - 1, I - 6, L - 0, S - 5, J - 4, Z - 2, T - 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(6), shapes.getShape(0), shapes.getShape(4), 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,17,1, 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 -- O I L S J Z T O I L J T O T 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, 6,16,0, 6,17,1, 7,17,1, 4,17,-1, 3,17,3, 3,15,0, 5,15,0, 9,14,0, 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]); } /* // O this.hintQueue[0].x = -2; this.hintQueue[0].y = 18; // I this.hintQueue[1].x = 6; this.hintQueue[1].y = 16; // L this.hintQueue[2].x = 6; this.hintQueue[2].y = 17; this.hintQueue[2].state = this.hintQueue[2].nextState(1); // S this.hintQueue[3].x = 7; this.hintQueue[3].y = 17; this.hintQueue[3].state = this.hintQueue[3].nextState(1); // J this.hintQueue[4].x = 4; this.hintQueue[4].y = 17; this.hintQueue[4].state = this.hintQueue[4].nextState(-1); // Z this.hintQueue[5].x = 3; this.hintQueue[5].y = 17; this.hintQueue[5].state = this.hintQueue[5].nextState(3); // T this.hintQueue[6].x = 3; this.hintQueue[6].y = 15; // O this.hintQueue[7].x = 5; this.hintQueue[7].y = 15; // I this.hintQueue[8].x = 9; this.hintQueue[8].y = 14; // L this.hintQueue[9].x = 2; this.hintQueue[9].y = 13; this.hintQueue[9].state = this.hintQueue[9].nextState(-1); // J this.hintQueue[10].x = -1; this.hintQueue[10].y = 15; this.hintQueue[10].state = this.hintQueue[10].nextState(1); // T this.hintQueue[11].x = 1; this.hintQueue[11].y = 16; this.hintQueue[11].state = this.hintQueue[11].nextState(2); // O this.hintQueue[12].x = 3; this.hintQueue[12].y = 16; this.hintQueue[12].state = this.hintQueue[12].nextState(1); // T this.hintQueue[13].x = 1; this.hintQueue[13].y = 17; this.hintQueue[13].state = this.hintQueue[13].nextState(-1); */ 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(6), shapes.getShape(0), shapes.getShape(4), shapes.getShape(3)); 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'); // 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 = 4; 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 = 4; 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] ]; 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 = 5; 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 = 4; 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 = 4; 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 = 4; 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 = Object.assign(Object.create(Object.getPrototypeOf(this)), 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 < this.rotationPoints[j].length; i+=2) { var shiftX = this.rotationPoints[j][i]; var shiftY = this.rotationPoints[j][i+1]; if(!isNaN(shiftY) && !isNaN(shiftX)) { //console.log("shiftxy: " + shiftX + " " + shiftY); clone.x = this.x + shiftX; clone.y = this.y - shiftY; if(this.canMoveTo(clone, matrix) == true) { this.state = clone.state;// = Object.assign(Object.create(Object.getPrototypeOf(clone)), clone); this.x = clone.x; this.y = clone.y; return; } } } } } }, //Rotate shape rotate: function(matrix) { // TODO: rest of pieces //if(this.flag == 'T' || this.flag == 'L') this.kickShape(matrix, -1); //else if (isShapeCanMove(this, matrix, 'rotate')){ //this.state = this.nextState(-1); //fix position if shape is out of right border //var right = this.getRight(); //if ( right >= COLUMN_COUNT){ // this.x -= right - COLUMN_COUNT + 1; //} /*var left = this.getLeft(); if(left <= 0) this.x += 1;*/ //} }, //Rotate shape clockwise rotateClockwise: function(matrix) { //if(this.flag == 'T') this.kickShape(matrix, 1); //else if (isShapeCanMove(this, matrix, 'rotateclockwise')) { //this.state = this.nextState(1); //fix position if shape is out of right border //var right = this.getRight(); //if (right >= COLUMN_COUNT) { // this.x -= right - COLUMN_COUNT + 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 */ // Handles randomly generating and returning a tetromino var RandomGenerator = { bag: [], getTetrimino() { if (this.bag.length === 0) { this.bag = this.generateNewBag(); } return this.bag.shift(); }, generateNewBag() { //var tetrominoes = ['I', 'J', 'L', 'O', 'S', 'T', 'Z']; var tetrominoes = ['0', '1', '2', '3', '4', '5', '6']; //var tetrominoes = ['L','L','L','L','L','L','L',]; var bag = []; for (var i = 7; i > 0; i--) { var tetrominoIndex = Math.floor(Math.random() * i); bag.push(tetrominoes[tetrominoIndex]); tetrominoes.splice(tetrominoIndex, 1); } return bag; } }; function randomShape() { var result = parseInt(RandomGenerator.getTetrimino(),10);//Math.floor(Math.random() * 7); var shape; shape = new ShapeT(); 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.randomShape = randomShape; module.exports.getShape = getShape; // export randomShape; // export getShape; },{"./consts.js":2}],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]);