/**** * Classes ****/ var Card = Container.expand(function (context, uid, index) { var self = Container.call(this); self.row = 0; self.col = 0; self.uid = uid; self.id = index; if (!index) { self.isMatched = true; return self; } self.isMatched = false; nbCardsLeft++; self.shakeAnimation = function (card) { if (!card) { return; } var shakeCount = 0; var shakeTimes = 9; var shakeStrength = 70; var originalX = card.x; var originalY = card.y; function shake() { if (shakeCount < shakeTimes) { card.x = originalX + (Math.random() - 0.5) * shakeStrength; shakeCount++; LK.setTimeout(shake, 50); } else { card.x = originalX; card.y = originalY; card.scale.x = 1; card.scale.y = 1; if (card === selectedCard1) { selectedCard1 = null; } if (card === selectedCard2) { selectedCard2 = null; } isAnimating = false; } } shake(); }; self.id = index; self.on('down', function (x, y, obj) { if (!self.isMatched && !isAnimating) { if (!selectedCard1 || selectedCard1 && selectedCard2) { if (selectedCard1) { selectedCard1.scale.x = 1; selectedCard1.scale.y = 1; if (selectedCard1 === self) { selectedCard1 = null; return; } } selectedCard1 = self; selectedCard2 = null; self.scale.x = 1.2; self.scale.y = 1.2; } else if (selectedCard1 && !selectedCard2 && selectedCard1 === self) { selectedCard1.scale.x = 1; selectedCard1.scale.y = 1; selectedCard1 = null; } else if (selectedCard1 && !selectedCard2 && selectedCard1 !== self) { selectedCard2 = self; var canLink = canBeLinked(selectedCard1, selectedCard2); self.scale.x = 1.2; self.scale.y = 1.2; if (canLink) { lastMatched = true; selectedCard1.match(selectedCard2); selectedCard2.match(selectedCard1); selectedCard1 = null; selectedCard2 = null; } else { lastMatched = false; isAnimating = true; LK.setTimeout(function () { self.shakeAnimation(selectedCard1); self.shakeAnimation(selectedCard2); }, 300); } } } }); var cardGraphics = self.attachAsset('card', { anchorX: 0.5, anchorY: 0.5 }); self.match = function (otherCard) { if (checkGameEndTimeout) { LK.clearTimeout(checkGameEndTimeout); } var line = new Line(self.x, self.y, otherCard.x, otherCard.y); game.addChild(line); var tadaAnimationSteps = 10; var tadaAnimationCount = 0; var scaleDelta = 0.2; var tadaDelay = 0; var tadaDelayMax = 60; self.tadaAnimation = function () { if (tadaAnimationCount < tadaAnimationSteps) { self.scale.x += tadaAnimationCount % 2 === 0 ? scaleDelta : -scaleDelta; self.scale.y += tadaAnimationCount % 2 === 0 ? scaleDelta : -scaleDelta; tadaAnimationCount++; } else { LK.off('tick', self.tadaAnimation); } }; self.shrinkAnimation = function () { if (self.scale.x > 0.1) { self.scale.x -= 0.01; self.scale.y -= 0.01; self.rotation += 0.1; } else { self.visible = false; LK.off('tick', self.tadaAnimation); LK.off('tick', self.shrinkAnimation); currentSolvedPairs[self.id] = true; currentNbSolved = Object.keys(currentSolvedPairs).length; if (checkGameEndTimeout) { LK.clearTimeout(checkGameEndTimeout); } checkGameEndTimeout = LK.setTimeout(function () { if (currentNbSolved >= lastNbPossible) { checkGameEnd(context, self.id); } }, 1000); } }; LK.on('tick', self.tadaAnimation); self.isMatched = true; LK.setTimeout(function () { LK.on('tick', self.shrinkAnimation); }, tadaAnimationSteps * 50); LK.setTimeout(function () { line.destroy(); }, 1000); }; var picture = self.addChild(new Picture(index)); picture.x = 0; picture.y = 0; return self; }); var Line = Container.expand(function (startX, startY, endX, endY) { var self = Container.call(this); var lineGraphics = self.attachAsset('line', {}); lineGraphics.width = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2)); lineGraphics.rotation = Math.atan2(endY - startY, endX - startX); lineGraphics.x = startX; lineGraphics.y = startY; self.addChild(lineGraphics); return self; }); var Picture = Container.expand(function (index) { var self = Container.call(this); self.index = index; var pictureGraphics = self.createAsset('picture' + self.index, { anchorX: 0.5, anchorY: 0.5 }); return self; }); var TimerLine = Container.expand(function (width, height) { var self = Container.call(this); var timerGraphics = self.attachAsset('timerLine', { anchorX: 0.5, anchorY: 0.5 }); timerGraphics.width = width; timerGraphics.height = height; timerGraphics.tint = 0x00FF00; self.addChild(timerGraphics); self._update_migrated = function (elapsedTime) { var totalTicks = levelDurationInSeconds * 1000; var deltaWidth = this.initialWidth / totalTicks; timerGraphics.width = Math.max(0, timerGraphics.width - deltaWidth * elapsedTime); if (timerGraphics.width / this.initialWidth < 0.25) { timerGraphics.tint = 0xFF1E1E - Math.round(0xFF * (this.initialWidth / timerGraphics.width)) << 16; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) { return _arrayLikeToArray(r, a); } var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) { n[e] = r[e]; } return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) { return; } f = !1; } else { for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0) { ; } } } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) { return; } } finally { if (o) { throw n; } } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) { return r; } } var level = 0; var levels = [{ 'rows': 2, 'cols': 2, 'time': 20 }, { 'rows': 4, 'cols': 4, 'time': 60 }, { 'rows': 6, 'cols': 6, 'time': 120 }, { 'rows': 8, 'cols': 8, 'time': 180 }, { 'rows': 10, 'cols': 10, 'time': 300 }, { 'rows': 11, 'cols': 10, 'time': 420 }, { 'rows': 12, 'cols': 10, 'time': 600 }]; level = Math.min(levels.length - 1, Math.max(0, level)); var levelDurationInSeconds = levels[level].time; var MAX_INIT_TRIES = 1000; var debug = false; var lastMatched = false; var cardPairs = []; var cards = []; var selectedCard1 = null; var selectedCard2 = null; var isInitializing = false; var isAnimating = false; var isRemixing = false; var nbRows = 2 + 2; var nbCols = 1 + 2; var nbCards = nbRows * nbCols; var nbPairs = (nbCards - nbRows * 2 - (nbCols - 2) * 2) / 2; var nbCardsLeft = 0; var lastNbPossible = 0; var currentSolvedPairs = []; var currentNbSolved = 0; var cardWidth = 200; var cardHeight = 200; var horizontalOffset = (2048 - (nbCols - 1) * cardWidth) / 2; var verticalOffset = (2732 - (nbRows - 1) * cardHeight) / 2; var shuffleDifficulty = 4; var lastCheckCaller = 0; var isPlaying = false; var isGameEndChecking = false; var isTryingInitGame = false; var checkGameEndTimeout = null; if (nbCards % 2) { console.error('Invalid number of cards ! Should be even.', nbCards); LK.showGameOver(); nbCols++; } function prepareConfig(level) { cardPairs = []; cards.forEach(function (row) { row.forEach(function (card) { if (card) { card.destroy(); } }); }); cards = []; nbRows = levels[level].rows + 2; nbCols = levels[level].cols + 2; nbCards = nbRows * nbCols; if (nbCards % 2) { console.error('Invalid number of cards ! Should be even.', nbCards); LK.showGameOver(); nbCols++; } nbPairs = (nbCards - nbRows * 2 - (nbCols - 2) * 2) / 2; horizontalOffset = (2048 - (nbCols - 1) * cardWidth) / 2; verticalOffset = (2732 - (nbRows - 1) * cardHeight) / 2; nbCardsLeft = 0; lastNbPossible = 0; currentSolvedPairs = []; currentNbSolved = 0; lastCheckCaller = 0; checkGameEndTimeout = null; selectedCard1 = null; selectedCard2 = null; isAnimating = false; isRemixing = false; } function timeBasedRandom() { var currentTime = Date.now(); var timeSeed = currentTime % 1000; var randomValue = Math.random() * timeSeed; return randomValue - Math.floor(randomValue); } function shuffleArray(array, onlyNonEmpty) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(timeBasedRandom() * (i + 1)); if (onlyNonEmpty && (!array[i] || !array[j])) { continue; } var temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; } function shuffleWithDifficulty(array, difficulty, onlyNonEmpty) { difficulty = Math.max(0, Math.min(10, difficulty)); var maxSwapDistance = Math.ceil(array.length * (difficulty / 10)); var numberOfSwaps = Math.ceil(array.length * (difficulty / 10)); for (var i = 0; i < numberOfSwaps; i++) { var firstIndex = Math.floor(timeBasedRandom() * array.length); var swapDistance = Math.floor(timeBasedRandom() * maxSwapDistance); var secondIndex = (firstIndex + swapDistance) % array.length; if (onlyNonEmpty && (!array[firstIndex] || !array[secondIndex])) { continue; } var temp = array[firstIndex]; array[firstIndex] = array[secondIndex]; array[secondIndex] = temp; } ensureAdjacentIndexes(array, difficulty); return array; } function ensureAdjacentIndexes(array, difficulty) { var numberOfPairsToSwap = Math.floor(array.length * 0.5 * (1 / difficulty) * 0.5); var pairsToSwap = []; for (var i = 0; i < array.length - 1; i++) { for (var j = i + 1; j < array.length; j++) { if (array[i] && array[j] && array[i] === array[j] && !pairsToSwap.includes(array[i])) { pairsToSwap.push(array[i]); } } } shuffleArray(pairsToSwap, false); pairsToSwap = pairsToSwap.slice(0, numberOfPairsToSwap); for (var i = 0; i < array.length - 1; i++) { for (var j = i + 1; j < array.length; j++) { if (array[i] && array[j] && array[i] === array[j] && pairsToSwap.includes(array[i]) && array[i + 1]) { var temp = array[i + 1]; array[i + 1] = array[j]; array[j] = temp; pairsToSwap.splice(pairsToSwap.indexOf(array[i]), 1); break; } } } } function canBeLinked(card1, card2) { if (card1 === card2 || !card1 || !card2 || card1.id !== card2.id) { return false; } var visited = Array.from({ length: nbRows }, function () { return Array(nbCols).fill(false); }); function dfs(card, bends, direction) { if (!card || visited[card.row][card.col] || bends > 2) { return false; } if (card === card2) { return true; } visited[card.row][card.col] = true; var directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; for (var _i = 0, _directions = directions; _i < _directions.length; _i++) { var _directions$_i = _slicedToArray(_directions[_i], 2), di = _directions$_i[0], dj = _directions$_i[1]; var ni = card.row + di, nj = card.col + dj; if (ni >= 0 && ni < nbRows && nj >= 0 && nj < nbCols) { var nextCard = cards[ni][nj]; if (!nextCard || !nextCard.id || nextCard.isMatched || nextCard === card2) { if (di !== 0) { if (dfs(nextCard, bends + (direction === -1), 1)) { return true; } } else if (dj !== 0) { if (dfs(nextCard, bends + (direction === 1), -1)) { return true; } } } } } visited[card.row][card.col] = false; return false; } return dfs(card1, 0); } function remixCards(currentDifficulty, callback) { if (isRemixing) { return false; } isRemixing = true; var unmatchedCards = cards.flat().filter(function (card) { return !card.isMatched; }); var oldPositions = unmatchedCards.map(function (card) { return { uid: card.uid, id: card.id, x: card.x, y: card.y }; }); var indexes = unmatchedCards.map(function (card) { return card.id; }); indexes = shuffleWithDifficulty(indexes, currentDifficulty); var reorderedPosition = []; var treatedIndexes = []; for (var i = 0; i < oldPositions.length; i++) { var originId = oldPositions[i].id; for (var j = 0; j < indexes.length; j++) { var newId = indexes[j]; if (originId === newId && !treatedIndexes[j]) { reorderedPosition[i] = { uid: oldPositions[j].uid, id: oldPositions[j].id, x: oldPositions[j].x, y: oldPositions[j].y }; treatedIndexes[j] = true; break; } } } unmatchedCards.forEach(function (card, index) { var newPos = reorderedPosition[index]; var _moveCard = function moveCard() { var dx = 0; var dy = 0; if (newPos) { dx = newPos.x - card.x; dy = newPos.y - card.y; } if (index == 1) {} var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 1) { card.x = newPos.x; card.y = newPos.y; var newCol = Math.floor((card.x - horizontalOffset) / cardWidth); var newRow = Math.floor((card.y - verticalOffset) / cardHeight); if (cards[card.row] && cards[card.row][card.col] === card) { cards[card.row][card.col] = undefined; } card.col = newCol; card.row = newRow; cards[newRow][newCol] = card; LK.off('tick', _moveCard); } else { card.x += dx * 0.05; card.y += dy * 0.05; } }; LK.on('tick', _moveCard); }); LK.setTimeout(function () { isRemixing = false; if (callback) { callback(); } }, 4000); } function checkGameEnd(context, callerId) { if (isGameEndChecking) { return; } if (lastCheckCaller === callerId) { return; } isGameEndChecking = true; lastCheckCaller = callerId; nbCardsLeft = cards.flat().filter(function (card) { return !card.isMatched; }).length; if (!nbCardsLeft) { isPlaying = false; LK.setTimeout(function () { if (level + 1 >= levels.length) { showConfettiAnimation(context, function () { LK.showGameOver(); }, true); } else { level++; level = Math.min(levels.length - 1, Math.max(0, level)); showConfettiAnimation(context, function () { isGameEndChecking = false; tryInitGame(context); }); } }, 300); return; } if (currentNbSolved >= lastNbPossible) { var isPossible = checkBoardPossible(); var canContinue = currentNbSolved < lastNbPossible; if (!canContinue) { tryRemixCards(); } } isGameEndChecking = false; } function checkBoardPossible() { var nbPossible = 0; var nbImpossible = 0; var indexesChecked = []; for (var i = 0; i < cardPairs.length; i++) { var index = cardPairs[i]; if (!index || indexesChecked[index]) { continue; } var card1 = cards.flat().find(function (card) { return card.id === index; }); var card2 = cards.flat().find(function (card) { return card.id === index && card !== card1; }); if (canBeLinked(card1, card2)) { nbPossible++; } else { nbImpossible++; } indexesChecked[index] = true; } lastNbPossible = nbPossible; return Math.max(currentNbSolved, nbPossible) >= (nbImpossible + nbPossible) * 0.1; } function initGame(self, currentDifficulty) { if (isInitializing) { console.log('already Initializing...'); return; } isInitializing = true; prepareConfig(level); var nextPairIndex = 1; for (var i = 0; i < nbCards; i++) { var row = Math.floor(i / nbCols); var col = i % nbCols; if (row === 0 || row === nbRows - 1 || col === 0 || col === nbCols - 1) { cardPairs.push(0); } else { cardPairs.push(nextPairIndex); nextPairIndex = nextPairIndex % nbPairs + 1; } } cardPairs = shuffleWithDifficulty(cardPairs, currentDifficulty, true); for (var i = 0; i < nbCards; i++) { var row = Math.floor(i / nbCols); var col = i % nbCols; var card = self.addChild(new Card(self, i, cardPairs[i])); card.col = col; card.row = row; card.x = horizontalOffset + card.col * cardWidth; card.y = verticalOffset + card.row * cardHeight; if (!cards[card.row]) { cards[card.row] = []; } cards[card.row][card.col] = card; } isInitializing = false; } function tryInitGame(context) { if (isTryingInitGame) { console.log('Already tryInitGame...'); return; } var isPossible = false; var nbTries = 0; var currentDifficulty = shuffleDifficulty; while (!isPossible && nbTries <= MAX_INIT_TRIES) { initGame(context, currentDifficulty); isPossible = checkBoardPossible(); isPlaying = isPossible; nbTries++; currentDifficulty--; } if (nbTries > MAX_INIT_TRIES) { console.warn('Unable to init game after ' + nbTries + ' tries'); } isTryingInitGame = false; } function tryRemixCards(nbTries, currentDifficulty) { nbTries = nbTries || 0; currentDifficulty = currentDifficulty || shuffleDifficulty; remixCards(currentDifficulty, function () { var isPossible = checkBoardPossible(); nbTries++; currentDifficulty--; if (nbTries > MAX_INIT_TRIES) { isPossible = true; console.warn('Unable to remix game after ' + nbTries + ' tries'); } if (!isPossible) { tryRemixCards(nbTries, currentDifficulty); } }); } function showConfettiAnimation(self, callback, isFinal) { var confettiCount = 50 + 5 * level + (isFinal ? 50 : 0); var confettiGraphics; var backgroundImage = self.createAsset('background' + (level + (isFinal ? 1 : 0)), { anchorX: 0.5, anchorY: 0.5 }); backgroundImage.x = 2048 / 2; backgroundImage.y = 2732 / 2; self.addChild(backgroundImage); var congratulationsText = new Text2('Congratulations!', { size: 150, fill: '#ffffff' }); if (isFinal) { congratulationsText.anchor.set(0.5, 0); congratulationsText.x = 0; congratulationsText.y = 20; LK.gui.top.addChild(congratulationsText); } for (var i = 0; i < confettiCount; i++) { confettiGraphics = self.createAsset('picture' + (1 + i % (nbPairs - 1)), {}); confettiGraphics.x = Math.random() * 2048; confettiGraphics.y = -30 - Math.random() * 2000; confettiGraphics.rotation = Math.random() * Math.PI * 2; self.addChild(confettiGraphics); (function (confetti) { var totalTicks = 420 + (isFinal ? 300 : 0); var tickCount = 0; LK.on('tick', function () { if (tickCount < totalTicks) { confetti.y += 7 + 10 * Math.random() + (isFinal ? 10 : 0); confetti.rotation += 0.05; tickCount++; } else { LK.off('tick', arguments.callee); if (i === confettiCount && callback) { if (!isFinal) { backgroundImage.destroy(); } callback(); } confetti.destroy(); } }); })(confettiGraphics); } } function preload(context) { var titleText = new Text2('Xmas Pairs', { size: 200, fill: '#9bc5bc', anchor: { x: 0.5, y: 0.5 } }); titleText.x = 2048 / 8; titleText.y = 2732 / 8; LK.gui.addChild(titleText); var loadingText = new Text2('Loading...', { size: 100, fill: '#ffffff', anchor: { x: 0.5, y: 0.5 } }); loadingText.x = 2048 / 4; loadingText.y = 2732 / 4; LK.gui.addChild(loadingText); var assetsToLoad = ['line', 'picture1', 'picture2', 'picture3', 'picture8', 'picture7', 'picture24', 'picture17', 'picture16', 'picture4', 'picture29', 'picture6', 'picture14', 'picture15', 'picture20', 'picture9', 'picture10', 'picture27', 'picture12', 'picture19', 'picture26', 'picture22', 'picture18', 'picture28', 'picture23', 'picture13', 'picture21', 'picture5', 'picture11', 'picture25', 'picture30', 'picture32', 'picture31', 'picture37', 'picture39', 'picture44', 'picture38', 'picture48', 'picture47', 'picture50', 'picture42', 'picture46', 'picture45', 'picture43', 'picture49', 'picture34', 'picture33', 'picture40', 'picture36', 'picture35', 'picture41', 'picture55', 'picture52', 'picture54', 'picture53', 'picture51', 'picture56', 'picture59', 'picture60', 'picture57', 'picture58', 'picture64', 'picture63', 'picture61', 'picture62', 'picture65', 'card', 'timerLine', 'background1', 'background2', 'background3', 'background4', 'background5', 'background6', 'background7']; var loadedAssets = 0; assetsToLoad.forEach(function (asset) { var assetInstance = LK.getAsset(asset, { anchorX: 0.5, anchorY: 0.5 }); loadedAssets++; if (loadedAssets === assetsToLoad.length) { LK.setTimeout(function () { loadingText.destroy(); titleText.destroy(); tryInitGame(context); }, 1200); } }); } preload(game); if (debug) { game.timerLine = new TimerLine(1400, 20); game.timerLine.initialWidth = 1400; game.timerLine.x = 710; game.timerLine.y = game.timerLine.y; LK.gui.topLeft.addChild(game.timerLine); var tickTime = Date.now(); LK.on('tick', function () { if (isPlaying && !isRemixing) { var elapsedTime = Date.now() - tickTime; game.timerLine._update_migrated(elapsedTime); tickTime = Date.now(); } }); var matchDebugText = new Text2('Match:', { size: 50, fill: '#ffffff' }); matchDebugText.anchor.set(0, 0); LK.gui.topLeft.addChild(matchDebugText); LK.on('tick', function () { matchDebugText.setText('Level: ' + (level + 1) + ' Solved: ' + currentNbSolved + '/' + lastNbPossible + ' / isPlaying=' + isPlaying + ' / time=' + levelDurationInSeconds); }); }
/****
* Classes
****/
var Card = Container.expand(function (context, uid, index) {
var self = Container.call(this);
self.row = 0;
self.col = 0;
self.uid = uid;
self.id = index;
if (!index) {
self.isMatched = true;
return self;
}
self.isMatched = false;
nbCardsLeft++;
self.shakeAnimation = function (card) {
if (!card) {
return;
}
var shakeCount = 0;
var shakeTimes = 9;
var shakeStrength = 70;
var originalX = card.x;
var originalY = card.y;
function shake() {
if (shakeCount < shakeTimes) {
card.x = originalX + (Math.random() - 0.5) * shakeStrength;
shakeCount++;
LK.setTimeout(shake, 50);
} else {
card.x = originalX;
card.y = originalY;
card.scale.x = 1;
card.scale.y = 1;
if (card === selectedCard1) {
selectedCard1 = null;
}
if (card === selectedCard2) {
selectedCard2 = null;
}
isAnimating = false;
}
}
shake();
};
self.id = index;
self.on('down', function (x, y, obj) {
if (!self.isMatched && !isAnimating) {
if (!selectedCard1 || selectedCard1 && selectedCard2) {
if (selectedCard1) {
selectedCard1.scale.x = 1;
selectedCard1.scale.y = 1;
if (selectedCard1 === self) {
selectedCard1 = null;
return;
}
}
selectedCard1 = self;
selectedCard2 = null;
self.scale.x = 1.2;
self.scale.y = 1.2;
} else if (selectedCard1 && !selectedCard2 && selectedCard1 === self) {
selectedCard1.scale.x = 1;
selectedCard1.scale.y = 1;
selectedCard1 = null;
} else if (selectedCard1 && !selectedCard2 && selectedCard1 !== self) {
selectedCard2 = self;
var canLink = canBeLinked(selectedCard1, selectedCard2);
self.scale.x = 1.2;
self.scale.y = 1.2;
if (canLink) {
lastMatched = true;
selectedCard1.match(selectedCard2);
selectedCard2.match(selectedCard1);
selectedCard1 = null;
selectedCard2 = null;
} else {
lastMatched = false;
isAnimating = true;
LK.setTimeout(function () {
self.shakeAnimation(selectedCard1);
self.shakeAnimation(selectedCard2);
}, 300);
}
}
}
});
var cardGraphics = self.attachAsset('card', {
anchorX: 0.5,
anchorY: 0.5
});
self.match = function (otherCard) {
if (checkGameEndTimeout) {
LK.clearTimeout(checkGameEndTimeout);
}
var line = new Line(self.x, self.y, otherCard.x, otherCard.y);
game.addChild(line);
var tadaAnimationSteps = 10;
var tadaAnimationCount = 0;
var scaleDelta = 0.2;
var tadaDelay = 0;
var tadaDelayMax = 60;
self.tadaAnimation = function () {
if (tadaAnimationCount < tadaAnimationSteps) {
self.scale.x += tadaAnimationCount % 2 === 0 ? scaleDelta : -scaleDelta;
self.scale.y += tadaAnimationCount % 2 === 0 ? scaleDelta : -scaleDelta;
tadaAnimationCount++;
} else {
LK.off('tick', self.tadaAnimation);
}
};
self.shrinkAnimation = function () {
if (self.scale.x > 0.1) {
self.scale.x -= 0.01;
self.scale.y -= 0.01;
self.rotation += 0.1;
} else {
self.visible = false;
LK.off('tick', self.tadaAnimation);
LK.off('tick', self.shrinkAnimation);
currentSolvedPairs[self.id] = true;
currentNbSolved = Object.keys(currentSolvedPairs).length;
if (checkGameEndTimeout) {
LK.clearTimeout(checkGameEndTimeout);
}
checkGameEndTimeout = LK.setTimeout(function () {
if (currentNbSolved >= lastNbPossible) {
checkGameEnd(context, self.id);
}
}, 1000);
}
};
LK.on('tick', self.tadaAnimation);
self.isMatched = true;
LK.setTimeout(function () {
LK.on('tick', self.shrinkAnimation);
}, tadaAnimationSteps * 50);
LK.setTimeout(function () {
line.destroy();
}, 1000);
};
var picture = self.addChild(new Picture(index));
picture.x = 0;
picture.y = 0;
return self;
});
var Line = Container.expand(function (startX, startY, endX, endY) {
var self = Container.call(this);
var lineGraphics = self.attachAsset('line', {});
lineGraphics.width = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
lineGraphics.rotation = Math.atan2(endY - startY, endX - startX);
lineGraphics.x = startX;
lineGraphics.y = startY;
self.addChild(lineGraphics);
return self;
});
var Picture = Container.expand(function (index) {
var self = Container.call(this);
self.index = index;
var pictureGraphics = self.createAsset('picture' + self.index, {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var TimerLine = Container.expand(function (width, height) {
var self = Container.call(this);
var timerGraphics = self.attachAsset('timerLine', {
anchorX: 0.5,
anchorY: 0.5
});
timerGraphics.width = width;
timerGraphics.height = height;
timerGraphics.tint = 0x00FF00;
self.addChild(timerGraphics);
self._update_migrated = function (elapsedTime) {
var totalTicks = levelDurationInSeconds * 1000;
var deltaWidth = this.initialWidth / totalTicks;
timerGraphics.width = Math.max(0, timerGraphics.width - deltaWidth * elapsedTime);
if (timerGraphics.width / this.initialWidth < 0.25) {
timerGraphics.tint = 0xFF1E1E - Math.round(0xFF * (this.initialWidth / timerGraphics.width)) << 16;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
function _slicedToArray(r, e) {
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) {
return _arrayLikeToArray(r, a);
}
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) {
n[e] = r[e];
}
return n;
}
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = !0,
o = !1;
try {
if (i = (t = t.call(r)).next, 0 === l) {
if (Object(t) !== t) {
return;
}
f = !1;
} else {
for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0) {
;
}
}
} catch (r) {
o = !0, n = r;
} finally {
try {
if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) {
return;
}
} finally {
if (o) {
throw n;
}
}
}
return a;
}
}
function _arrayWithHoles(r) {
if (Array.isArray(r)) {
return r;
}
}
var level = 0;
var levels = [{
'rows': 2,
'cols': 2,
'time': 20
}, {
'rows': 4,
'cols': 4,
'time': 60
}, {
'rows': 6,
'cols': 6,
'time': 120
}, {
'rows': 8,
'cols': 8,
'time': 180
}, {
'rows': 10,
'cols': 10,
'time': 300
}, {
'rows': 11,
'cols': 10,
'time': 420
}, {
'rows': 12,
'cols': 10,
'time': 600
}];
level = Math.min(levels.length - 1, Math.max(0, level));
var levelDurationInSeconds = levels[level].time;
var MAX_INIT_TRIES = 1000;
var debug = false;
var lastMatched = false;
var cardPairs = [];
var cards = [];
var selectedCard1 = null;
var selectedCard2 = null;
var isInitializing = false;
var isAnimating = false;
var isRemixing = false;
var nbRows = 2 + 2;
var nbCols = 1 + 2;
var nbCards = nbRows * nbCols;
var nbPairs = (nbCards - nbRows * 2 - (nbCols - 2) * 2) / 2;
var nbCardsLeft = 0;
var lastNbPossible = 0;
var currentSolvedPairs = [];
var currentNbSolved = 0;
var cardWidth = 200;
var cardHeight = 200;
var horizontalOffset = (2048 - (nbCols - 1) * cardWidth) / 2;
var verticalOffset = (2732 - (nbRows - 1) * cardHeight) / 2;
var shuffleDifficulty = 4;
var lastCheckCaller = 0;
var isPlaying = false;
var isGameEndChecking = false;
var isTryingInitGame = false;
var checkGameEndTimeout = null;
if (nbCards % 2) {
console.error('Invalid number of cards ! Should be even.', nbCards);
LK.showGameOver();
nbCols++;
}
function prepareConfig(level) {
cardPairs = [];
cards.forEach(function (row) {
row.forEach(function (card) {
if (card) {
card.destroy();
}
});
});
cards = [];
nbRows = levels[level].rows + 2;
nbCols = levels[level].cols + 2;
nbCards = nbRows * nbCols;
if (nbCards % 2) {
console.error('Invalid number of cards ! Should be even.', nbCards);
LK.showGameOver();
nbCols++;
}
nbPairs = (nbCards - nbRows * 2 - (nbCols - 2) * 2) / 2;
horizontalOffset = (2048 - (nbCols - 1) * cardWidth) / 2;
verticalOffset = (2732 - (nbRows - 1) * cardHeight) / 2;
nbCardsLeft = 0;
lastNbPossible = 0;
currentSolvedPairs = [];
currentNbSolved = 0;
lastCheckCaller = 0;
checkGameEndTimeout = null;
selectedCard1 = null;
selectedCard2 = null;
isAnimating = false;
isRemixing = false;
}
function timeBasedRandom() {
var currentTime = Date.now();
var timeSeed = currentTime % 1000;
var randomValue = Math.random() * timeSeed;
return randomValue - Math.floor(randomValue);
}
function shuffleArray(array, onlyNonEmpty) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(timeBasedRandom() * (i + 1));
if (onlyNonEmpty && (!array[i] || !array[j])) {
continue;
}
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
function shuffleWithDifficulty(array, difficulty, onlyNonEmpty) {
difficulty = Math.max(0, Math.min(10, difficulty));
var maxSwapDistance = Math.ceil(array.length * (difficulty / 10));
var numberOfSwaps = Math.ceil(array.length * (difficulty / 10));
for (var i = 0; i < numberOfSwaps; i++) {
var firstIndex = Math.floor(timeBasedRandom() * array.length);
var swapDistance = Math.floor(timeBasedRandom() * maxSwapDistance);
var secondIndex = (firstIndex + swapDistance) % array.length;
if (onlyNonEmpty && (!array[firstIndex] || !array[secondIndex])) {
continue;
}
var temp = array[firstIndex];
array[firstIndex] = array[secondIndex];
array[secondIndex] = temp;
}
ensureAdjacentIndexes(array, difficulty);
return array;
}
function ensureAdjacentIndexes(array, difficulty) {
var numberOfPairsToSwap = Math.floor(array.length * 0.5 * (1 / difficulty) * 0.5);
var pairsToSwap = [];
for (var i = 0; i < array.length - 1; i++) {
for (var j = i + 1; j < array.length; j++) {
if (array[i] && array[j] && array[i] === array[j] && !pairsToSwap.includes(array[i])) {
pairsToSwap.push(array[i]);
}
}
}
shuffleArray(pairsToSwap, false);
pairsToSwap = pairsToSwap.slice(0, numberOfPairsToSwap);
for (var i = 0; i < array.length - 1; i++) {
for (var j = i + 1; j < array.length; j++) {
if (array[i] && array[j] && array[i] === array[j] && pairsToSwap.includes(array[i]) && array[i + 1]) {
var temp = array[i + 1];
array[i + 1] = array[j];
array[j] = temp;
pairsToSwap.splice(pairsToSwap.indexOf(array[i]), 1);
break;
}
}
}
}
function canBeLinked(card1, card2) {
if (card1 === card2 || !card1 || !card2 || card1.id !== card2.id) {
return false;
}
var visited = Array.from({
length: nbRows
}, function () {
return Array(nbCols).fill(false);
});
function dfs(card, bends, direction) {
if (!card || visited[card.row][card.col] || bends > 2) {
return false;
}
if (card === card2) {
return true;
}
visited[card.row][card.col] = true;
var directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
for (var _i = 0, _directions = directions; _i < _directions.length; _i++) {
var _directions$_i = _slicedToArray(_directions[_i], 2),
di = _directions$_i[0],
dj = _directions$_i[1];
var ni = card.row + di,
nj = card.col + dj;
if (ni >= 0 && ni < nbRows && nj >= 0 && nj < nbCols) {
var nextCard = cards[ni][nj];
if (!nextCard || !nextCard.id || nextCard.isMatched || nextCard === card2) {
if (di !== 0) {
if (dfs(nextCard, bends + (direction === -1), 1)) {
return true;
}
} else if (dj !== 0) {
if (dfs(nextCard, bends + (direction === 1), -1)) {
return true;
}
}
}
}
}
visited[card.row][card.col] = false;
return false;
}
return dfs(card1, 0);
}
function remixCards(currentDifficulty, callback) {
if (isRemixing) {
return false;
}
isRemixing = true;
var unmatchedCards = cards.flat().filter(function (card) {
return !card.isMatched;
});
var oldPositions = unmatchedCards.map(function (card) {
return {
uid: card.uid,
id: card.id,
x: card.x,
y: card.y
};
});
var indexes = unmatchedCards.map(function (card) {
return card.id;
});
indexes = shuffleWithDifficulty(indexes, currentDifficulty);
var reorderedPosition = [];
var treatedIndexes = [];
for (var i = 0; i < oldPositions.length; i++) {
var originId = oldPositions[i].id;
for (var j = 0; j < indexes.length; j++) {
var newId = indexes[j];
if (originId === newId && !treatedIndexes[j]) {
reorderedPosition[i] = {
uid: oldPositions[j].uid,
id: oldPositions[j].id,
x: oldPositions[j].x,
y: oldPositions[j].y
};
treatedIndexes[j] = true;
break;
}
}
}
unmatchedCards.forEach(function (card, index) {
var newPos = reorderedPosition[index];
var _moveCard = function moveCard() {
var dx = 0;
var dy = 0;
if (newPos) {
dx = newPos.x - card.x;
dy = newPos.y - card.y;
}
if (index == 1) {}
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 1) {
card.x = newPos.x;
card.y = newPos.y;
var newCol = Math.floor((card.x - horizontalOffset) / cardWidth);
var newRow = Math.floor((card.y - verticalOffset) / cardHeight);
if (cards[card.row] && cards[card.row][card.col] === card) {
cards[card.row][card.col] = undefined;
}
card.col = newCol;
card.row = newRow;
cards[newRow][newCol] = card;
LK.off('tick', _moveCard);
} else {
card.x += dx * 0.05;
card.y += dy * 0.05;
}
};
LK.on('tick', _moveCard);
});
LK.setTimeout(function () {
isRemixing = false;
if (callback) {
callback();
}
}, 4000);
}
function checkGameEnd(context, callerId) {
if (isGameEndChecking) {
return;
}
if (lastCheckCaller === callerId) {
return;
}
isGameEndChecking = true;
lastCheckCaller = callerId;
nbCardsLeft = cards.flat().filter(function (card) {
return !card.isMatched;
}).length;
if (!nbCardsLeft) {
isPlaying = false;
LK.setTimeout(function () {
if (level + 1 >= levels.length) {
showConfettiAnimation(context, function () {
LK.showGameOver();
}, true);
} else {
level++;
level = Math.min(levels.length - 1, Math.max(0, level));
showConfettiAnimation(context, function () {
isGameEndChecking = false;
tryInitGame(context);
});
}
}, 300);
return;
}
if (currentNbSolved >= lastNbPossible) {
var isPossible = checkBoardPossible();
var canContinue = currentNbSolved < lastNbPossible;
if (!canContinue) {
tryRemixCards();
}
}
isGameEndChecking = false;
}
function checkBoardPossible() {
var nbPossible = 0;
var nbImpossible = 0;
var indexesChecked = [];
for (var i = 0; i < cardPairs.length; i++) {
var index = cardPairs[i];
if (!index || indexesChecked[index]) {
continue;
}
var card1 = cards.flat().find(function (card) {
return card.id === index;
});
var card2 = cards.flat().find(function (card) {
return card.id === index && card !== card1;
});
if (canBeLinked(card1, card2)) {
nbPossible++;
} else {
nbImpossible++;
}
indexesChecked[index] = true;
}
lastNbPossible = nbPossible;
return Math.max(currentNbSolved, nbPossible) >= (nbImpossible + nbPossible) * 0.1;
}
function initGame(self, currentDifficulty) {
if (isInitializing) {
console.log('already Initializing...');
return;
}
isInitializing = true;
prepareConfig(level);
var nextPairIndex = 1;
for (var i = 0; i < nbCards; i++) {
var row = Math.floor(i / nbCols);
var col = i % nbCols;
if (row === 0 || row === nbRows - 1 || col === 0 || col === nbCols - 1) {
cardPairs.push(0);
} else {
cardPairs.push(nextPairIndex);
nextPairIndex = nextPairIndex % nbPairs + 1;
}
}
cardPairs = shuffleWithDifficulty(cardPairs, currentDifficulty, true);
for (var i = 0; i < nbCards; i++) {
var row = Math.floor(i / nbCols);
var col = i % nbCols;
var card = self.addChild(new Card(self, i, cardPairs[i]));
card.col = col;
card.row = row;
card.x = horizontalOffset + card.col * cardWidth;
card.y = verticalOffset + card.row * cardHeight;
if (!cards[card.row]) {
cards[card.row] = [];
}
cards[card.row][card.col] = card;
}
isInitializing = false;
}
function tryInitGame(context) {
if (isTryingInitGame) {
console.log('Already tryInitGame...');
return;
}
var isPossible = false;
var nbTries = 0;
var currentDifficulty = shuffleDifficulty;
while (!isPossible && nbTries <= MAX_INIT_TRIES) {
initGame(context, currentDifficulty);
isPossible = checkBoardPossible();
isPlaying = isPossible;
nbTries++;
currentDifficulty--;
}
if (nbTries > MAX_INIT_TRIES) {
console.warn('Unable to init game after ' + nbTries + ' tries');
}
isTryingInitGame = false;
}
function tryRemixCards(nbTries, currentDifficulty) {
nbTries = nbTries || 0;
currentDifficulty = currentDifficulty || shuffleDifficulty;
remixCards(currentDifficulty, function () {
var isPossible = checkBoardPossible();
nbTries++;
currentDifficulty--;
if (nbTries > MAX_INIT_TRIES) {
isPossible = true;
console.warn('Unable to remix game after ' + nbTries + ' tries');
}
if (!isPossible) {
tryRemixCards(nbTries, currentDifficulty);
}
});
}
function showConfettiAnimation(self, callback, isFinal) {
var confettiCount = 50 + 5 * level + (isFinal ? 50 : 0);
var confettiGraphics;
var backgroundImage = self.createAsset('background' + (level + (isFinal ? 1 : 0)), {
anchorX: 0.5,
anchorY: 0.5
});
backgroundImage.x = 2048 / 2;
backgroundImage.y = 2732 / 2;
self.addChild(backgroundImage);
var congratulationsText = new Text2('Congratulations!', {
size: 150,
fill: '#ffffff'
});
if (isFinal) {
congratulationsText.anchor.set(0.5, 0);
congratulationsText.x = 0;
congratulationsText.y = 20;
LK.gui.top.addChild(congratulationsText);
}
for (var i = 0; i < confettiCount; i++) {
confettiGraphics = self.createAsset('picture' + (1 + i % (nbPairs - 1)), {});
confettiGraphics.x = Math.random() * 2048;
confettiGraphics.y = -30 - Math.random() * 2000;
confettiGraphics.rotation = Math.random() * Math.PI * 2;
self.addChild(confettiGraphics);
(function (confetti) {
var totalTicks = 420 + (isFinal ? 300 : 0);
var tickCount = 0;
LK.on('tick', function () {
if (tickCount < totalTicks) {
confetti.y += 7 + 10 * Math.random() + (isFinal ? 10 : 0);
confetti.rotation += 0.05;
tickCount++;
} else {
LK.off('tick', arguments.callee);
if (i === confettiCount && callback) {
if (!isFinal) {
backgroundImage.destroy();
}
callback();
}
confetti.destroy();
}
});
})(confettiGraphics);
}
}
function preload(context) {
var titleText = new Text2('Xmas Pairs', {
size: 200,
fill: '#9bc5bc',
anchor: {
x: 0.5,
y: 0.5
}
});
titleText.x = 2048 / 8;
titleText.y = 2732 / 8;
LK.gui.addChild(titleText);
var loadingText = new Text2('Loading...', {
size: 100,
fill: '#ffffff',
anchor: {
x: 0.5,
y: 0.5
}
});
loadingText.x = 2048 / 4;
loadingText.y = 2732 / 4;
LK.gui.addChild(loadingText);
var assetsToLoad = ['line', 'picture1', 'picture2', 'picture3', 'picture8', 'picture7', 'picture24', 'picture17', 'picture16', 'picture4', 'picture29', 'picture6', 'picture14', 'picture15', 'picture20', 'picture9', 'picture10', 'picture27', 'picture12', 'picture19', 'picture26', 'picture22', 'picture18', 'picture28', 'picture23', 'picture13', 'picture21', 'picture5', 'picture11', 'picture25', 'picture30', 'picture32', 'picture31', 'picture37', 'picture39', 'picture44', 'picture38', 'picture48', 'picture47', 'picture50', 'picture42', 'picture46', 'picture45', 'picture43', 'picture49', 'picture34', 'picture33', 'picture40', 'picture36', 'picture35', 'picture41', 'picture55', 'picture52', 'picture54', 'picture53', 'picture51', 'picture56', 'picture59', 'picture60', 'picture57', 'picture58', 'picture64', 'picture63', 'picture61', 'picture62', 'picture65', 'card', 'timerLine', 'background1', 'background2', 'background3', 'background4', 'background5', 'background6', 'background7'];
var loadedAssets = 0;
assetsToLoad.forEach(function (asset) {
var assetInstance = LK.getAsset(asset, {
anchorX: 0.5,
anchorY: 0.5
});
loadedAssets++;
if (loadedAssets === assetsToLoad.length) {
LK.setTimeout(function () {
loadingText.destroy();
titleText.destroy();
tryInitGame(context);
}, 1200);
}
});
}
preload(game);
if (debug) {
game.timerLine = new TimerLine(1400, 20);
game.timerLine.initialWidth = 1400;
game.timerLine.x = 710;
game.timerLine.y = game.timerLine.y;
LK.gui.topLeft.addChild(game.timerLine);
var tickTime = Date.now();
LK.on('tick', function () {
if (isPlaying && !isRemixing) {
var elapsedTime = Date.now() - tickTime;
game.timerLine._update_migrated(elapsedTime);
tickTime = Date.now();
}
});
var matchDebugText = new Text2('Match:', {
size: 50,
fill: '#ffffff'
});
matchDebugText.anchor.set(0, 0);
LK.gui.topLeft.addChild(matchDebugText);
LK.on('tick', function () {
matchDebugText.setText('Level: ' + (level + 1) + ' Solved: ' + currentNbSolved + '/' + lastNbPossible + ' / isPlaying=' + isPlaying + ' / time=' + levelDurationInSeconds);
});
}
a photo realistic top view of empty flat beige plastic square. Single Game Texture. In-Game asset. 2d. No background. High contrast. No shadows.
a christmas tree. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas gift. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas ball. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a golden christmas tree star. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas hat. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas leaf. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas holly leaf. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas snow flake. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas snow man. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas reindeer. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas candy cane. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas green ball . plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas gingerbrean man. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas pine cone. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas present green. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas boe tie. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas socks. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas penguin. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas decorated blue present. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas scarf. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas gloves. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas blue decorated ball. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas candle. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas snow globe. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
an christmas elongated cuboid present. plastic style. No shadow. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a round christmas gift.plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a round christmas gift.plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
one cute christmas elf. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
one cute christmas elf. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
one cute christmas reindeer head. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
one cute christmas reindeer head with a red nose. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
one christmas bell. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a couple of christmas bells. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas gifts bag. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas candle. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a cute santa clauss. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Santa's sleigh. Side view. Plastic style Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Reimagine the cute teddy bear sitting without background. Plastic style Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Christmas Rocking Horse. Plastic style Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
One Christmas Matryoshka Doll. Plastic style Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
A christmas Miniature Train. Plastic style Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
A christmas Toy Soldier. Plastic style Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
A christmas Music Box . Plastic style Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a cute polar bear cub. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a sled plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas gingerbread girl. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas gingerbread house. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a cute christmas jack in the box. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a cute christmas cookie. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas log cacke. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a christmas donut. plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a cute christmas owl. Plastic style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a feeric christmas landscape at night with decorated trees Background image
a feeric christmas landscape at night with snow men and snow flakes Background image
a feeric christmas landscape at night with a snow man , candy canes ,holly leafs and snow flakes, Background image
a feeric christmas landscape at night with a snow man , candy canes ,holly leafs and snow flakes, Santa's reindeers, green, red and blue presents Background image
a feeric christmas landscape at night with a snow man , candy canes ,holly leafs and snow flakes, cute penguin, Santa's reindeers, green, red and blue presents and a lot of toys, a sled, gingerbread boy and girl, snow globes, a cute polar bear cub. a gingerbread house Background image
A Magical feeric starry christmas landscape at night with a snow man , candy canes ,holly leafs and snow flakes, cute penguin, Santa's reindeers, green, red and blue presents and a lot of toys, a sled, gingerbread boy and girl, snow globes, cute polar bears cub and a gingerbread house. Realistic. Plastic style. Background image