2 * Copyright (c) 2012, Intel Corporation.
4 * This program is licensed under the terms and conditions of the
5 * Apache License, version 2.0. The full text of the Apache License is at
6 * http://www.apache.org/licenses/LICENSE-2.0
12 this.cardFrontId = "";
13 this.cardGraphics = "";
20 var normalCardImages = [
21 "images/noglow_balloon.png",
22 "images/noglow_butterflies.png",
23 "images/noglow_crown.png",
24 "images/noglow_cup.png",
25 "images/noglow_elephant.png",
26 "images/noglow_guitar.png",
27 "images/noglow_horn.png",
28 "images/noglow_lion.png",
29 "images/noglow_moon.png",
30 "images/noglow_sun.png",
31 "images/noglow_zebra.png",
32 "images/noglow_blank.png"
34 var glowingCardImages = [
35 "images/card_flip_balloon.png",
36 "images/card_flip_butterflies.png",
37 "images/card_flip_crown.png",
38 "images/card_flip_cup.png",
39 "images/card_flip_elephant.png",
40 "images/card_flip_guitar.png",
41 "images/card_flip_horn.png",
42 "images/card_flip_lion.png",
43 "images/card_flip_moon.png",
44 "images/card_flip_sun.png",
45 "images/card_flip_zebra.png",
46 "images/card_flip_blank.png"
50 "images/purple_star.png",
51 "images/green_star.png",
67 "audio/StartPage.wav",
68 "audio/TheFinale.wav",
73 var SOUND_FLIPCARD1 = 0;
74 var SOUND_FLIPCARD2 = 1;
75 var SOUND_STARTGAME = 2;
76 var SOUND_FINALEINTRO = 3;
77 var SOUND_LEVEL_WON = 4;
78 var SOUND_VICTORY = 5;
81 var LOCKED_LEVELCARD_STYLE = "setLevel_lockedLevel";
82 var SHOWCARD_STYLE = "flip_to_visible";
83 var GAMES_PER_LEVEL = 3;
84 var LOCAL_STORAGE_KEY = "memorygame_locked_levels";
88 var cardsArray = new Array();
89 var levelSelectionUserChoice = -1;
90 var ignoreInputs = false;
91 var firstFlippedCard = undefined;
92 var clickedCardElement = undefined;
95 var lastPlayedFlipSound = SOUND_FLIPCARD2;
96 var levelLockingStatus = [ false, true, true, true ];
98 // Set memberfunctions.
99 Game.flipCallback = flipCallback;
100 Game.flipDelayCallback = flipDelayCallback;
101 Game.gotoNextGame = gotoNextGame;
102 Game.levelSelectionAnimCallback = levelSelectionAnimCallback;
103 Game.introViewSkipCallback = introViewSkipCallback;
106 * Create sound element base on their ID
108 function createSoundElement(soundId) {
109 var audioElement = document.createElement('audio');
110 audioElement.setAttribute("id", audioItems[soundId]);
111 audioElement.setAttribute("src", audioSrc[soundId]);
112 if (soundId == SOUND_STARTGAME) {
113 audioElement.setAttribute("preload", "auto");
114 audioElement.setAttribute("autoplay", "autoplay");
116 audioElement.setAttribute("preload", "none");
118 document.body.appendChild(audioElement);
122 * Plays sounds base on their ID
124 function playSound(soundId) {
125 var audioElement = document.getElementById(audioItems[soundId]);
126 audioElement.pause();
131 * This function prepares the graphical elements of Victory-screen.
133 function prepareVictoryScreen() {
134 // Draw the curved YOU WIN text.
135 var drawer = new CurvedTextDrawer(document.getElementById("curvedText"));
136 var centerPos = drawer.getCanvasCenterPos();
138 drawer.useFont = '70px Romantiques';
139 drawer.drawSectorArc("YOU WIN", centerPos.mX, centerPos.mY, 780, 180, 270, 'ccw', true, 'center');
141 $("#homebutton_backtomain").hide();
145 * Reads the level lock status from local storage and sets the card element styles
148 function prepareSelectLevelScreen() {
149 console.log("--> prepareSelectLevelScreen()");
150 var lockedLevels = localStorage.getItem(LOCAL_STORAGE_KEY);
151 for (var i=1; i < levelLockingStatus.length; ++i) {
152 if (lockedLevels != undefined && lockedLevels != null && lockedLevels.length > i) {
153 console.log(" read data: " + lockedLevels);
154 if (lockedLevels[i] == '0') {
155 levelLockingStatus[i] = false;
157 levelLockingStatus[i] = true;
160 levelLockingStatus[i] = true;
162 var cardElement = $("#selLevel_levelCard"+(i+1));
163 cardElement.removeClass(LOCKED_LEVELCARD_STYLE);
164 if (levelLockingStatus[i]) {
165 cardElement.addClass(LOCKED_LEVELCARD_STYLE);
168 console.log("<-- prepareSelectLevelScreen()");
172 * Saves the level lock status to local storage.
174 function saveStatus() {
175 console.log("--> saveStatus()");
176 var lockedLevelsStr = '0';
177 for (var i = 1; i < levelLockingStatus.length; ++i) {
178 if (levelLockingStatus[i]) {
179 console.log(" level: " + i + ", lock: locked");
180 lockedLevelsStr += '1';
182 console.log(" level: " + i + ", lock: unlocked");
183 lockedLevelsStr += '0';
186 console.log(" data: " + lockedLevelsStr);
187 localStorage.setItem(LOCAL_STORAGE_KEY, lockedLevelsStr);
188 console.log("<-- saveStatus()");
193 * Returns the card object of the card whose DIV element has given ID.
194 * @param cardDivName Name of the cards div element.
195 * @return The matching Card object or undefined if card was not found.
197 function getCardObject(cardDivName) {
198 for (var i=0; i < cardsArray.length; ++i) {
199 var card = cardsArray[i];
200 if (card.cardId == cardDivName) {
208 * Marks the cards as found ones and changes the graphics.
209 * @param card1 First one of the found cards.
210 * @param card2 Second one of the found cards.
212 function matchFound(card1, card2) {
216 // Change the graphics to glowing ones.
217 var gfxIndex = card1.cardType;
218 $(card1.cardId).children(".back").css("visibility", "hidden");
219 $(card2.cardId).children(".back").css("visibility", "hidden");
220 $(card1.cardFrontId).css("background", "url("+glowingCardImages[gfxIndex]+")");
221 $(card2.cardFrontId).css("background", "url("+glowingCardImages[gfxIndex]+")");
225 * Checks the states of the cards and tells if all the pairs have been found.
226 * @return true if all the pairs have been found.
228 function allPairsFound() {
229 for (var i=0; i < cardsArray.length; ++i) {
230 var card = cardsArray[i];
231 if (card.found == false) {
240 * A callback function that gets called when card rotation animation ends. This
241 * function checks if two rotated cards are pairs.
243 function flipCallback() {
244 console.log("--> Game.flipCallback()");
245 if (firstFlippedCard == undefined) {
246 // This is the first card being turned.
247 firstFlippedCard = clickedCardElement;
248 clickedCardElement = undefined;
249 ignoreInputs = false;
252 // This is the second card being turned. Check if they are equal.
253 var cardObj1 = getCardObject("#" + firstFlippedCard.attr("id"));
254 var cardObj2 = getCardObject("#" + clickedCardElement.attr("id"));
255 if (cardObj1.cardType == cardObj2.cardType) {
256 matchFound(cardObj1, cardObj2);
257 if (allPairsFound()) {
260 ignoreInputs = false;
261 firstFlippedCard = undefined;
262 clickedCardElement = undefined;
265 console.log(" no match");
266 window.setTimeout("Game.flipDelayCallback()", 300);
269 console.log("<-- Game.flipCallback()");
273 * Callbackfunction that rotates the two latest cards upside down again.
275 function flipDelayCallback() {
276 ignoreInputs = false;
277 firstFlippedCard.removeClass(SHOWCARD_STYLE);
278 clickedCardElement.removeClass(SHOWCARD_STYLE);
279 firstFlippedCard = undefined;
280 clickedCardElement = undefined;
284 * Shuffles the cards in the beginning of the game.
285 * @param cardsArray Array that contains the card objects.
287 function shuffleLevel(cardsArray) {
288 console.log("--> shuffleLevel1()");
289 // Make an array that contains 2 copies of card type ids.
290 var typeIndexArray = new Array();
291 for (var i=0; i < cardsArray.length; ++i) {
292 typeIndexArray.push( (Math.floor(i / 2)) % normalCardImages.length );
294 for (var i=0; i < typeIndexArray.length; ++i) {
295 var swapWithIndex = Math.floor(Math.random() * 11);
296 var tmpValue = typeIndexArray[swapWithIndex];
297 typeIndexArray[swapWithIndex] = typeIndexArray[i];
298 typeIndexArray[i] = tmpValue;
301 for (var i=0; i < cardsArray.length; ++i) {
302 var gfxIndex = typeIndexArray.pop();
303 var card = cardsArray[i];
304 card.cardType = gfxIndex;
305 console.log("elementId: " + card.cardFrontId + ", graphics: " + normalCardImages[gfxIndex]);
306 $(card.cardFrontId).css("background", "url("+normalCardImages[gfxIndex]+")");
307 $(card.cardId).removeClass(SHOWCARD_STYLE);
309 console.log("<-- shuffleLevel1()");
313 * This should be called when game has been played through. It does the actions that are
314 * needed to handle game and level progress and also showing victory screen.
316 function gotoNextGame() {
317 console.log("--> gotoNextGame()");
318 if (levelNumber == 4) {
319 levelLockingStatus[0] = false;
320 levelLockingStatus[1] = false;
321 levelLockingStatus[2] = false;
322 levelLockingStatus[3] = false;
324 // Currently finished level was 4. It means that player has finished the game.
325 prepareVictoryScreen();
326 createSoundElement(SOUND_VICTORY);
327 playSound(SOUND_VICTORY);
329 $("#victory").show();
333 var levelOfNextGame = levelNumber;
334 if (passedGames >= GAMES_PER_LEVEL) {
335 // Move to next level.
339 // Next level unlocked.
340 levelLockingStatus[levelNumber] = false;
341 if (levelOfNextGame != 4) {
342 playSound(SOUND_LEVEL_WON);
345 if (levelOfNextGame == 4) {
346 // Show intro view before entring the final level.
347 levelLockingStatus[3] = false;
348 createSoundElement(SOUND_FINALEINTRO);
349 playSound(SOUND_FINALEINTRO);
350 $("#homebutton_backtomain").hide();
351 $("#handitem").hide();
353 $("#finaleIntro").show();
355 startGame(levelOfNextGame, false);
358 console.log("<-- gotoNextGame()");
362 * A callback function that gets called when level selection animation has ended.
364 function levelSelectionAnimCallback() {
365 console.log("--> levelSelectionAnimCallback()");
366 $("#selLevel_page").hide();
367 $("#selLevel_levelCard1").removeClass("selLevel_selectedCard selLevel_anim1 selLevel_anim2 selLevel_anim3 selLevel_anim4");
368 $("#selLevel_levelCard2").removeClass("selLevel_selectedCard selLevel_anim1 selLevel_anim2 selLevel_anim3 selLevel_anim4");
369 $("#selLevel_levelCard3").removeClass("selLevel_selectedCard selLevel_anim1 selLevel_anim2 selLevel_anim3 selLevel_anim4");
370 $("#selLevel_levelCard4").removeClass("selLevel_selectedCard selLevel_anim1 selLevel_anim2 selLevel_anim3 selLevel_anim4");
371 startGame(levelSelectionUserChoice, true);
372 playSound(SOUND_LEVEL_WON);
373 console.log("<-- levelSelectionAnimCallback()");
377 * Initializes the structures to start new game.
378 * @param levelNum The level that will be started.
379 * @param resteState Pass true to reset the game counts to 0.
381 function startGame(levelNum, resetState) {
382 console.log("--> startGame()");
387 // Figure out the amount of cards needed in this level.
388 var cardBgGraphics = "url(images/card_purple.png)"
389 cardsArray = new Array();
390 levelNumber = levelNum;
394 cardBgGraphics = "url(images/card_green.png)"
395 } else if (levelNum == 3) {
397 cardBgGraphics = "url(images/card_red.png)"
398 } else if (levelNum == 4) {
400 cardBgGraphics = "url(images/card_teal.png)"
402 // Create card objects.
403 for (var i=0; i < numOfCards; ++i) {
404 var card = new Card();
405 card.cardFrontId = "#level"+levelNum+"_card_"+(i+1)+"_front";
406 card.cardId = "#" + $(card.cardFrontId).parent().attr("id");
407 cardsArray.push(card);
409 // Manually set toggle the card backgrounds between invisible
410 // background and real card background. We can't use plain css
411 // because the backface-visibility during rotation does not
412 // work in linux Chrome as it should.
413 $(card.cardId).children(".back").css("visibility", "visible");
415 setTimeout(function() {shuffleLevel(cardsArray);}, 300);
416 $(".card").removeClass(SHOWCARD_STYLE);
417 firstFlippedCard = undefined;
418 clickedCardElement = undefined;
421 // Update the hand that holds the game count note.
422 var starIconName = stariconsList[levelNum-1];
423 for (var gameIndex = 0; gameIndex < 3; ++gameIndex) {
424 var starImgElement = $("#handleitem_star"+(gameIndex+1));
425 starImgElement.removeClass("unplayedGameStar");
426 if (gameIndex <= passedGames) {
427 starImgElement.attr("src", starIconName);
430 starImgElement.addClass("unplayedGameStar");
431 starImgElement.attr("src", "images/star.png");
435 $("#handitem").css("display", "block");
437 // The hand image is not shown in final level.
438 $("#handitem").hide();
441 // Control the level elements visibility.
442 for (var i=1; i < 5; ++i) {
444 $("#level"+i).show();
446 $("#level"+i).hide();
449 if (passedGames == 0) {
450 $("#handitem_gamenum_title").text("GAME 1");
451 } else if (passedGames == 1) {
452 $("#handitem_gamenum_title").text("GAME 2");
453 } else if (passedGames == 2) {
454 $("#handitem_gamenum_title").text("GAME 3");
457 $("#homebutton_backtomain").show();
458 console.log("<-- startGame()");
461 function introViewSkipCallback() {
462 prepareSelectLevelScreen();
463 if ($("#main_page").is(":visible")) {
464 $("#main_page").hide();
465 $("#selLevel_page").show();
467 license_init("license", "pagebg");
468 help_init("main_help", "help_");
469 createSoundElement(SOUND_FLIPCARD1);
470 createSoundElement(SOUND_FLIPCARD2);
471 createSoundElement(SOUND_LEVEL_WON);
474 // Initialize game once everything has been loaded.
475 $(document).ready(function () {
476 console.log("--> document.ready()");
478 createSoundElement(SOUND_STARTGAME);
480 // Add the event handler functions.
481 $("#main_page").click(function () {
482 // Hide mainview and show level selection.
483 introViewSkipCallback();
486 $("#selLevel_levelCard1").click(function () {
488 levelSelectionUserChoice = 1;
489 playSound(SOUND_FLIPCARD2);
490 $("#selLevel_levelCard1").addClass("selLevel_selectedCard");
491 $("#selLevel_levelCard2").addClass("selLevel_anim1");
492 $("#selLevel_levelCard3").addClass("selLevel_anim1");
493 $("#selLevel_levelCard4").addClass("selLevel_anim1");
494 window.setTimeout("Game.levelSelectionAnimCallback()", 1000);
496 $("#selLevel_levelCard2").click(function() {
497 if ($(this).hasClass(LOCKED_LEVELCARD_STYLE) == false) {
498 playSound(SOUND_FLIPCARD2);
499 levelSelectionUserChoice = 2;
500 $("#selLevel_levelCard2").addClass("selLevel_selectedCard");
501 $("#selLevel_levelCard1").addClass("selLevel_anim2");
502 $("#selLevel_levelCard3").addClass("selLevel_anim2");
503 $("#selLevel_levelCard4").addClass("selLevel_anim2");
504 window.setTimeout("Game.levelSelectionAnimCallback()", 1000);
507 $("#selLevel_levelCard3").click(function() {
508 if ($(this).hasClass(LOCKED_LEVELCARD_STYLE) == false) {
509 playSound(SOUND_FLIPCARD2);
510 levelSelectionUserChoice = 3;
511 $("#selLevel_levelCard3").addClass("selLevel_selectedCard");
512 $("#selLevel_levelCard1").addClass("selLevel_anim3");
513 $("#selLevel_levelCard2").addClass("selLevel_anim3");
514 $("#selLevel_levelCard4").addClass("selLevel_anim3");
515 window.setTimeout("Game.levelSelectionAnimCallback()", 1000);
518 $("#selLevel_levelCard4").click(function() {
519 if ($(this).hasClass(LOCKED_LEVELCARD_STYLE) == false) {
520 playSound(SOUND_FLIPCARD2);
521 levelSelectionUserChoice = 4;
522 $("#selLevel_levelCard4").addClass("selLevel_selectedCard");
523 $("#selLevel_levelCard1").addClass("selLevel_anim4");
524 $("#selLevel_levelCard2").addClass("selLevel_anim4");
525 $("#selLevel_levelCard3").addClass("selLevel_anim4");
526 window.setTimeout("Game.levelSelectionAnimCallback()", 1000);
530 $(".card").click(function() {
531 console.log("--> card.click()");
532 if (!ignoreInputs && !($(this).hasClass(SHOWCARD_STYLE))) {
533 // We have to use 2 different audio items for flip sound because
534 // audio API doesn't replay the sound if it is already playing.
535 if (lastPlayedFlipSound == SOUND_FLIPCARD1) {
536 lastPlayedFlipSound = SOUND_FLIPCARD2;
538 lastPlayedFlipSound = SOUND_FLIPCARD1;
540 createSoundElement(SOUND_FLIPCARD1);
541 playSound(lastPlayedFlipSound);
542 clickedCardElement = $(this);
544 console.log(" card id: " + $(this).attr("id"));
545 $(this).addClass(SHOWCARD_STYLE);
546 window.setTimeout("Game.flipCallback()", 350);
548 console.log("<-- card.click()");
551 $("#finaleIntro").click( function() {
552 // Start playing final level.
553 $("#finaleIntro").hide();
557 $("#victory_playagain_box").click(function() {
558 console.log("--> victory_playagain_box.click()");
559 prepareSelectLevelScreen();
560 $("#victory").hide();
561 $("#selLevel_page").show();
562 console.log("<-- victory_playagain_box.click()");
565 $("#homebutton_backtomain").click(function() {
566 console.log("--> homebutton.click()");
567 // Hide current levels and show mainpage.
572 $("#handitem").hide();
573 $("#main_page").show();
575 console.log("<-- homebutton.click()");
578 $("#selLevel_resetLocked").click(function() {
579 localStorage.setItem(LOCAL_STORAGE_KEY, '0000');
580 prepareSelectLevelScreen();
584 // Skip the welcome screen after a while.
585 window.setTimeout("Game.introViewSkipCallback()", 3500);