"use strict"; //----- HELPERS -----// var LogHelper = function(){} // Tiny wrapper for the console log. //LogHelper.log = function(s){ // console.log("[MAP PUZZLE] " + s); //} // Really basic timer for timing stuff. var Timer = function(){} Timer.prototype.start = function(){ this.startTime = new Date().getTime(); } Timer.prototype.stop = function(){ return new Date().getTime() - this.startTime; } //----- MAIN -----// var PuzzleGame = function(){}; PuzzleGame.main = function(){ // Load the HTML file with the necessary elements. $("#mpStage").load("mappuzzle-support.html", PuzzleGame.init); } PuzzleGame.init = function(){ $("#mpMenuContainer").hide(); $("#mpGameContainer").hide(); $(document).ready(PuzzleGame.preload); } PuzzleGame.preload = function(){ // Time counter start. var timer = new Timer(); timer.start(); // Array for collecting our preload assets. var preloadArray = new Array(); // Sparse assets -- menus, capital, etc. preloadArray.push(PuzzleConfig.assetBackgroundMenu); preloadArray.push(PuzzleConfig.assetBackgroundEasy); preloadArray.push(PuzzleConfig.assetBackgroundHard); preloadArray.push(PuzzleConfig.assetCapital); // All of the pieces images. for(var i = 0; i < PuzzleConfig.drag_array.length; i++){ //console.log(PuzzleConfig.assetsDirCountries + PuzzleConfig.drag_array[i].image.toLowerCase() + "1.png"); preloadArray.push(PuzzleConfig.assetsDirCountries + PuzzleConfig.drag_array[i].image.toLowerCase() + "1.png"); preloadArray.push(PuzzleConfig.assetsDirCountries + PuzzleConfig.drag_array[i].image.toLowerCase() + "2.png"); } for(i = 0; i < preloadArray.length; i++){ var img = new Image(); img.src = preloadArray[i]; } var doPreloadWait = function(assets){ var isImageLoaded = function(asset){ var img = new Image(); img.src = asset; return img.naturalWidth !== 0; } for(var key in assets){ var asset = assets[key]; if(!isImageLoaded){ setTimeout(doPreloadWait, 50, assets); return; } } PuzzleGame.startup(); } doPreloadWait(preloadArray); } PuzzleGame.startup = function(){ // Done preloading? Great. $("#mpPreloader").hide(); $("#mpMenuContainer").show(); PuzzleGame.setBackground(PuzzleConfig.assetBackgroundMenu); $("#mpPieceContainer").draggable({ scroll: false, // Clears the bounding box when the piece is grabbed. start: function(e, ui){ $("#mpPieceBoundingBox").hide(); }, // Creates our virtual constraints based off of mouse movement instead of the object. drag: function(e, ui){ var relMouseX = e.pageX - $("#mpStage").offset().left; var relMouseY = e.pageY - $("#mpStage").offset().top; var disableX = relMouseX < 0 || relMouseX > parseInt($("#mpStage").css("width")); var disableY = relMouseY < 0 || relMouseY > parseInt($("#mpStage").css("height")); var lockX = ui.lockX; var lockY = ui.lockY; if(disableX && typeof(lockX) == undefined){ lockX = ui.position.left; } else if (disableX){ ui.position.left = lockX; } else { delete ui.helper.lockX; } if(disableY && typeof(lockY) == undefined){ lockY = ui.position.top; } else if (disableY){ ui.position.top = lockY; } else { delete ui.lockY; } } }); // Fix up the canvas. document.getElementById("mpHint").width = $("#mpStage").width(); document.getElementById("mpHint").height = $("#mpStage").height(); // Set up the bounding box to indicate the piece grab constraints with the proper asset and then hide it. $("#mpPieceBoundingBox").hide(); // Setup the proper button names. $("#mpCountriesEasy").attr("value", PuzzleConfig.countriesEasyButtonName); $("#mpCountriesHard").attr("value", PuzzleConfig.countriesHardButtonName); $("#mpCapitalsEasy").attr("value", PuzzleConfig.capitalsEasyButtonName); $("#mpCapitalsHard").attr("value", PuzzleConfig.capitalsHardButtonName); PuzzleGame.timer = new Timer(); PuzzleGame.bindEvents(); } PuzzleGame.bindEvents = function(){ $(document).on("click", "#mpCountriesEasy", function(e){PuzzleGame.newGame(PuzzleConfig.assetBackgroundEasy, 0)}); $(document).on("click", "#mpCountriesHard", function(e){PuzzleGame.newGame(PuzzleConfig.assetBackgroundHard, 0, true)}); $(document).on("click", "#mpCapitalsEasy", function(e){PuzzleGame.newGame(PuzzleConfig.assetBackgroundEasy, 1)}); $(document).on("click", "#mpCapitalsHard", function(e){PuzzleGame.newGame(PuzzleConfig.assetBackgroundEasy, 2)}); $("#mpPieceContainer").bind("dragstop", PuzzleGame.dropHandler); $("#mpEndgameButton").on("click", function(e){ $("#mpGameContainer").hide(); $("#mpMenuContainer").show(); $("#mpOverlay").html(""); $("#mpStarOverlay").html(""); PuzzleGame.setBackground(PuzzleConfig.assetBackgroundMenu); } ); } PuzzleGame.newGame = function(background, type, doEdgesFirst){ $("#mpMenuContainer").hide(); $("#mpGameContainer").show(); $("#mpPieceContainer").show(); $("#mpEndgameDialog").hide(); PuzzleGame.gameType = type; PuzzleGame.doEdgesFirst = doEdgesFirst != undefined? doEdgesFirst : false; PuzzleGame.edgesDone = 0; PuzzleGame.setBackground(background); PuzzleGame.resetPieces(); PuzzleGame.shufflePieces(); PuzzleGame.gotoPiece(0); PuzzleGame.timer.start(); if(PuzzleGame.gameType == 1){ $("#mpHint").show(); } else { $("#mpHint").hide(); } } /** * Returns the next edge, or undefined if it does not exist. */ PuzzleGame.getNextEdge = function(){ for(var n = 0; n < PuzzleGame.pieces.length; n++){ if(PuzzleGame.pieces[n].isEdge){ return PuzzleGame.pieces[n]; } } return undefined; } /** * Rotates left the given number of times. */ PuzzleGame.rotatePiecesLeftTo = function(n){ for(n; n > 0; n--){ PuzzleGame.rotatePiecesLeft(); } } PuzzleGame.gotoPiece = function(n){ PuzzleGame.resetPiecePosition(); // This is a really crude solution. In reality we should be using a circular doubly linked list // for the pieces but avoid doing so. Might be meaningful to do a wrapper around the array to // treat it as a circular doubly linked list. if(PuzzleGame.doEdgesFirst){ if(PuzzleConfig.minEdges == 0 || PuzzleGame.edgesDone < PuzzleConfig.minEdges){ var edge = PuzzleGame.getNextEdge(); // Edge exists, so we should go to it. if(edge != undefined){ PuzzleGame.rotatePiecesLeftTo(PuzzleGame.pieces.indexOf(edge)); // Edge doesn't exist, flip the toggle. } else { PuzzleGame.doEdgesFirst = false; } } } // COUNTRIES if(PuzzleGame.gameType == 0){ $("#mpPiece").html(""); $("#mpLocationName").html(this.pieces[n].country); // CAPITALS } else { // Basically another crude solution. Skips over places without a specified capital. if(PuzzleGame.pieces[n].capital == ""){ // LogHelper.log("Skipping " + PuzzleGame.pieces[n].country + "; no capital specified."); PuzzleGame.pieces.shift(); PuzzleGame.gotoPiece(0); return; } if(PuzzleGame.endPieceReady){ $("#mpPiece").html(""); } else { $("#mpPiece").html(""); } $("#mpLocationName").html(PuzzleGame.pieces[n].capital); var img = new Image(); // Safety net in case something doesn't load. img.onload = function(e){ var ctx = document.getElementById("mpHint").getContext("2d"); ctx.drawImage(img, PuzzleGame.pieces[n].targetX, PuzzleGame.pieces[n].targetY); } img.src = PuzzleConfig.assetsDirCountries + PuzzleGame.pieces[n].image + "2.png"; } var c = $("#mpPieceContainer"); var b = $("#mpPieceBoundingBox"); b.css("left", c.css("left")); b.css("top", c.css("top")); var w = parseInt(c.css("width")) + parseInt(c.css("padding-left")) + parseInt(c.css("padding-right")); var h = parseInt(c.css("height")) + parseInt(c.css("padding-top")) + parseInt(c.css("padding-bottom")); var canvas = document.getElementById("mpPieceBoundingBox"); // Make sure its width/height are right. canvas.width = w; canvas.height = h; var ctx = canvas.getContext("2d"); //JT added code to fill the rectangle with a semi-transparent background. ctx.fillStyle = 'rgba(255,255,255,0.7)'; ctx.fillRect(0, 0, w, h); ctx.strokeStyle = "#FF00F0"; ctx.lineWidth = 4; ctx.strokeRect(0, 0, w, h); b.show(); } PuzzleGame.shufflePieces = function(){ this.pieces.sort(function(){return 0.5 - Math.random()}); } PuzzleGame.resetPieces = function(){ PuzzleGame.pieces = PuzzleConfig.drag_array.slice(0); } PuzzleGame.resetPiecePosition = function(){ $("#mpPieceContainer").css("left", PuzzleConfig.pieceSpawnX); $("#mpPieceContainer").css("top", PuzzleConfig.pieceSpawnY); } PuzzleGame.rotatePiecesLeft = function(){ PuzzleGame.pieces.push(PuzzleGame.pieces.shift()); } PuzzleGame.dropHandler = function(){ $("#mpHint").html(''); var pieceContainer = $("#mpPieceContainer"); // Countries if(PuzzleGame.gameType == 0 || PuzzleGame.endPieceReady){ var oX = pieceContainer.position().left + parseInt(pieceContainer.css("padding-left")); var oY = pieceContainer.position().top + parseInt(pieceContainer.css("padding-top")); var tX = PuzzleGame.gameType > 0? PuzzleGame.pieces[0].capX : PuzzleGame.pieces[0].targetX; var tY = PuzzleGame.gameType > 0? PuzzleGame.pieces[0].capY : PuzzleGame.pieces[0].targetY; var d = Math.sqrt(Math.pow(oX - tX, 2) + Math.pow(oY - tY, 2)); // HIT if(d < PuzzleGame.pieces[0].dither /*+ 10000*/){ PuzzleGame.pieceSnapped(); // MISS } else { PuzzleGame.pieceMissed(); } // Capitals. } else { // Get the center of the capital star. var oX = pieceContainer.position().left + parseInt(pieceContainer.css("padding-left")) + pieceContainer.height()/2; var oY = pieceContainer.position().top + parseInt(pieceContainer.css("padding-top")) + pieceContainer.width()/2; var c = document.getElementById("mpHint"); var ctx = c.getContext("2d"); var imgdata = ctx.getImageData(oX, oY, 1, 1).data; // HIT if(imgdata[0] != 0 || imgdata[1] != 0 || imgdata[2] != 0 || imgdata[3] != 0){ PuzzleGame.pieceSnapped(); // MISS } else { PuzzleGame.pieceMissed(); } ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.clearRect(0, 0, c.width, c.height); ctx.restore(); } } PuzzleGame.pieceSnapped = function(){ $("#mpOverlay").append(""); if(PuzzleGame.gameType > 0){ if(PuzzleGame.endPieceReady){ $("#mpStarOverlay").append(""); } else { $("#mpStarOverlay").append(""); } } if(PuzzleGame.pieces[0].isEdge){ PuzzleGame.edgesDone += 1; } // Game is still going. if(PuzzleGame.pieces.length > 1){ PuzzleGame.pieces.shift(); PuzzleGame.gotoPiece(0); // We're at the end and have a country capital to place. } else if(PuzzleConfig.endPiece && !PuzzleGame.endPieceReady && PuzzleGame.gameType != 0){ //LogHelper.log("Prompting with country capital."); PuzzleGame.endPieceReady = true; PuzzleGame.pieces.push(PuzzleConfig.endPiece); PuzzleGame.pieces.shift(); PuzzleGame.gotoPiece(0); // Game is completely over. } else { PuzzleGame.endPieceReady = false; PuzzleGame.endGame(); } } PuzzleGame.pieceMissed = function(){ PuzzleGame.rotatePiecesLeft(); PuzzleGame.gotoPiece(0); } PuzzleGame.setBackground = function(img){ $("#mpStage").css("background-image", "url(" + img + ")"); } PuzzleGame.endGame = function(){ $("#mpPieceContainer").hide(); $("#mpEndgameDialog").show(); var timeTaken = PuzzleGame.timer.stop(); var tmp, sec, min, hour; //Convert values as needed. tmp = timeTaken/1000; sec = Math.floor(tmp % 60); tmp /= 60; min = Math.floor(tmp % 60); tmp /= 60; hour = Math.floor(tmp % 24); // If they've taken under an hour we can give them some meaningful feedback. if(hour == 0){ $("#mpEndgameText").html("
You completed the map in " + min + ":" + (sec > 9? sec : "0" + sec) + "!
"); // If they've taken over an hour, don't bother. } else { $("#mpEndgameText").html("You completed the map!
"); } } PuzzleGame.main();