/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Card class: represents a single card in the game var Card = Container.expand(function () { var self = Container.call(this); // Card properties self.rank = null; // 2-10, 'J', 'Q', 'K', 'A' self.suit = null; // '♠', '♥', '♦', '♣' self.faceUp = true; // Card size var CARD_WIDTH = 220; var CARD_HEIGHT = 320; // Card background var bg = self.attachAsset('cardBg', { width: CARD_WIDTH, height: CARD_HEIGHT, color: 0xffffff, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); // Card border var border = self.attachAsset('cardBorder', { width: CARD_WIDTH, height: CARD_HEIGHT, color: 0x222222, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); border.alpha = 0.12; // Card text (rank and suit) var txt = new Text2('', { size: 90, fill: 0x222222 }); txt.anchor.set(0.5, 0.5); txt.x = 0; txt.y = 0; self.addChild(txt); // Face down overlay var back = self.attachAsset('cardBack', { width: CARD_WIDTH, height: CARD_HEIGHT, color: 0x3a3a3a, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); back.visible = false; // Set card data self.setCard = function (rank, suit, faceUp) { self.rank = rank; self.suit = suit; self.faceUp = faceUp !== false; self.updateVisual(); }; // Update card visuals self.updateVisual = function () { if (self.faceUp) { txt.visible = true; txt.setText(self.rank + self.suit); txt.setStyle({ fill: self.suit === '♥' || self.suit === '♦' ? "#d83318" : "#222222" }); back.visible = false; } else { txt.visible = false; back.visible = true; } }; self.scaleX = 1; // Ensure scaleX is initialized for flip animation return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x156c2f // green felt color }); /**** * Game Code ****/ // Card deck setup var SUITS = ['♠', '♥', '♦', '♣']; var RANKS = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']; // Game state var playerHand = []; var dealerHand = []; var deck = []; var inPlay = false; var playerStands = false; var revealDealer = false; var messageTxt = null; var scoreTxt = null; var streakTxt = null; var highScoreTxt = null; var hitBtn = null; var standBtn = null; var playerScore = 0; var winStreak = 0; var highScore = storage.highScore || 0; // Card layout var CARD_SPACING = 160; var PLAYER_Y = 2732 - 420; // Move dealer cards lower to make room for streak/high score var DEALER_Y = 420 + 120; // 120px lower // UI: Player hand value var playerValueTxt = new Text2("", { size: 70, fill: 0xffffff }); playerValueTxt.anchor.set(0.5, 1); playerValueTxt.x = 2048 / 2; playerValueTxt.y = PLAYER_Y - 180; game.addChild(playerValueTxt); // UI: Dealer hand value var dealerValueTxt = new Text2("", { size: 70, fill: 0xffffff }); dealerValueTxt.anchor.set(0.5, 0); dealerValueTxt.x = 2048 / 2; dealerValueTxt.y = DEALER_Y + 220; game.addChild(dealerValueTxt); // Utility: shuffle array function shuffle(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var t = array[i]; array[i] = array[j]; array[j] = t; } return array; } // Utility: create a new deck function createDeck() { var d = []; for (var s = 0; s < SUITS.length; s++) { for (var r = 0; r < RANKS.length; r++) { d.push({ rank: RANKS[r], suit: SUITS[s] }); } } return shuffle(d); } // Utility: get hand value function handValue(hand) { var total = 0; var aces = 0; for (var i = 0; i < hand.length; i++) { var r = hand[i].rank; if (r === 'A') { total += 11; aces++; } else if (r === 'K' || r === 'Q' || r === 'J') { total += 10; } else { total += parseInt(r); } } while (total > 21 && aces > 0) { total -= 10; aces--; } return total; } // Utility: clear all cards from the table function clearTable() { for (var i = 0; i < playerHand.length; i++) { playerHand[i].destroy(); } for (var i = 0; i < dealerHand.length; i++) { dealerHand[i].destroy(); } playerHand = []; dealerHand = []; } // Utility: update all UI text function updateUI() { scoreTxt.setText(playerScore + ""); streakTxt.setText("Streak: " + winStreak); highScoreTxt.setText("High: " + highScore); // Show player hand value if (playerHand.length > 0) { var playerVal = handValue(playerHand); playerValueTxt.setText("Your total: " + playerVal); playerValueTxt.visible = true; // Check for blackjack (21) and auto-win if in play, but only if it's the initial deal (2 cards) if (playerVal === 21 && inPlay && playerHand.length === 2) { // Show BLACKJACK message in the center of the screen, yellow color messageTxt.setText("BLACKJACK"); messageTxt.setStyle({ fill: 0xFFE600 }); messageTxt.visible = true; // End round as blackjack after a short delay to show the message inPlay = false; hitBtn.visible = false; standBtn.visible = false; // Set a flag so endRound knows this was a blackjack game._justBlackjack = true; LK.setTimeout(function () { endRound('win'); }, 900); } } else { playerValueTxt.visible = false; } // Show dealer hand value (only reveal if dealer's hidden card is up) if (dealerHand.length > 0) { var showDealer = false; if (dealerHand.length > 1 && dealerHand[1].faceUp) showDealer = true; if (dealerHand.length === 1) showDealer = true; if (showDealer) { dealerValueTxt.setText("Dealer: " + handValue(dealerHand)); dealerValueTxt.visible = true; } else { // Show only the visible card's value dealerValueTxt.setText("Dealer: " + handValue([dealerHand[0]])); dealerValueTxt.visible = true; } } else { dealerValueTxt.visible = false; } } // Utility: show message function showMessage(msg, color) { messageTxt.setText(msg); messageTxt.setStyle({ fill: color || "#ffffff" }); messageTxt.visible = true; } // Utility: hide message function hideMessage() { messageTxt.visible = false; } // Deal a card to a hand function dealCard(hand, faceUp, x, y) { if (deck.length === 0) { deck = createDeck(); } var cardData = deck.pop(); var card = new Card(); card.setCard(cardData.rank, cardData.suit, faceUp); // Start card off-screen above the table for animation card.x = x; card.y = y - 400; game.addChild(card); hand.push(card); // Animate card dropping into place tween(card, { y: y }, { duration: 320, easing: tween.cubicOut }); return card; } // Layout cards in a hand function layoutHand(hand, y) { var totalWidth = (hand.length - 1) * CARD_SPACING; var startX = 2048 / 2 - totalWidth / 2; for (var i = 0; i < hand.length; i++) { var targetX = startX + i * CARD_SPACING; // Animate both x and y for smooth movement tween(hand[i], { x: targetX, y: y }, { duration: 220, easing: tween.cubicOut }); } } // Start a new round function startRound() { clearTable(); hideMessage(); inPlay = true; playerStands = false; revealDealer = false; // Shuffle deck if low if (deck.length < 10) { deck = createDeck(); } // Deal initial cards dealCard(playerHand, true, 2048 / 2 - CARD_SPACING / 2, PLAYER_Y); dealCard(dealerHand, true, 2048 / 2 - CARD_SPACING / 2, DEALER_Y); dealCard(playerHand, true, 2048 / 2 + CARD_SPACING / 2, PLAYER_Y); dealCard(dealerHand, false, 2048 / 2 + CARD_SPACING / 2, DEALER_Y); layoutHand(playerHand, PLAYER_Y); layoutHand(dealerHand, DEALER_Y); updateUI(); hitBtn.visible = true; standBtn.visible = true; } // End round and show result function endRound(result) { inPlay = false; revealDealer = true; // Reveal dealer's hidden card if (dealerHand.length > 1) { // Animate card flip: scaleX to 0, swap face, then scaleX to 1 tween(dealerHand[1], { scaleX: 0 }, { duration: 120, easing: tween.cubicIn, onFinish: function onFinish() { dealerHand[1].faceUp = true; dealerHand[1].updateVisual(); tween(dealerHand[1], { scaleX: 1 }, { duration: 120, easing: tween.cubicOut }); } }); } // Animate dealer hand to show all cards layoutHand(dealerHand, DEALER_Y); if (result === 'win') { playerScore++; winStreak++; if (game._justBlackjack) { // Already showed BLACKJACK, just play sound and flash LK.effects.flashObject(playerHand[0], 0xffe600, 600); LK.setTimeout(function () { LK.getSound('ding_sound_effect').play(); }, 100); // Animate BLACKJACK message: spin and hold for 3 seconds messageTxt.rotation = 0; tween(messageTxt, { rotation: Math.PI * 2 }, { duration: 900, easing: tween.cubicOut, onFinish: function onFinish() { messageTxt.rotation = 0; } }); // Reset flag for next round game._justBlackjack = false; // Delay next round for 3 seconds var nextRoundDelay = 3000; } else { showMessage("You Win!", "#83de44"); LK.effects.flashObject(playerHand[0], 0x83de44, 600); // Play win sound effect after a short delay to ensure message is visible and not interrupted LK.setTimeout(function () { LK.getSound('ding_sound_effect').play(); }, 100); // Animate win message: spin and hold for 3 seconds messageTxt.rotation = 0; tween(messageTxt, { rotation: Math.PI * 2 }, { duration: 900, easing: tween.cubicOut, onFinish: function onFinish() { messageTxt.rotation = 0; } }); var nextRoundDelay = 3000; } } else if (result === 'lose') { winStreak = 0; showMessage("You Lose", "#d83318"); LK.effects.flashObject(playerHand[0], 0xd83318, 600); // Animate lose message: spin and hold for 3 seconds messageTxt.rotation = 0; tween(messageTxt, { rotation: Math.PI * 2 }, { duration: 900, easing: tween.cubicOut, onFinish: function onFinish() { messageTxt.rotation = 0; } }); var nextRoundDelay = 3000; } else { showMessage("Push", "#b8b031"); var nextRoundDelay = 1200; } // Only allow high score to increase if playerScore exceeds it, and only after surpassing it with a win streak if (playerScore > highScore && winStreak > 1) { highScore = playerScore; storage.highScore = highScore; } // If player loses, do not reset highScore, just reset winStreak (already handled above) updateUI(); // Hide buttons hitBtn.visible = false; standBtn.visible = false; // Start next round after delay LK.setTimeout(function () { startRound(); }, nextRoundDelay); } // Dealer's turn logic function dealerTurn() { // Reveal dealer's hidden card if (dealerHand.length > 1) { // Animate card flip: scaleX to 0, swap face, then scaleX to 1 tween(dealerHand[1], { scaleX: 0 }, { duration: 120, easing: tween.cubicIn, onFinish: function onFinish() { dealerHand[1].faceUp = true; dealerHand[1].updateVisual(); tween(dealerHand[1], { scaleX: 1 }, { duration: 120, easing: tween.cubicOut }); } }); } layoutHand(dealerHand, DEALER_Y); // Dealer draws to 17 or higher var dealerVal = handValue(dealerHand); var playerVal = handValue(playerHand); function dealerDraw() { dealerVal = handValue(dealerHand); updateUI(); if (dealerVal < 17) { var card = dealCard(dealerHand, true, 2048 / 2 + (dealerHand.length - 1) * CARD_SPACING / 2, DEALER_Y); layoutHand(dealerHand, DEALER_Y); LK.setTimeout(dealerDraw, 500); } else { // Compare hands if (dealerVal > 21) { endRound('win'); } else if (dealerVal > playerVal) { endRound('lose'); } else if (dealerVal < playerVal) { endRound('win'); } else { endRound('push'); } } } LK.setTimeout(dealerDraw, 600); } // Handle player "Hit" function onHit() { if (!inPlay) return; var card = dealCard(playerHand, true, 2048 / 2 + (playerHand.length - 1) * CARD_SPACING / 2, PLAYER_Y); layoutHand(playerHand, PLAYER_Y); var val = handValue(playerHand); updateUI(); if (val > 21) { endRound('lose'); } } // Handle player "Stand" function onStand() { if (!inPlay) return; playerStands = true; hitBtn.visible = false; standBtn.visible = false; dealerTurn(); } // Handle touch on hit/stand buttons function buttonDown(x, y, obj) { if (!inPlay) return; if (obj === hitBtn) { onHit(); } else if (obj === standBtn) { onStand(); } } // UI: Score scoreTxt = new Text2("0", { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // UI: Streak streakTxt = new Text2("Streak: 0", { size: 60, fill: 0xB8B031 }); streakTxt.anchor.set(0.5, 0); streakTxt.y = 120; LK.gui.top.addChild(streakTxt); // UI: High Score highScoreTxt = new Text2("High: " + highScore, { size: 60, fill: 0x83DE44 }); highScoreTxt.anchor.set(0.5, 0); highScoreTxt.y = 200; LK.gui.top.addChild(highScoreTxt); // UI: Message messageTxt = new Text2("", { size: 120, fill: 0xFFFFFF }); messageTxt.anchor.set(0.5, 0.5); messageTxt.x = 2048 / 2; messageTxt.y = 2732 / 2 - 100; messageTxt.visible = false; game.addChild(messageTxt); // UI: Hit Button hitBtn = LK.getAsset('hitBtn', { width: 420, height: 160, color: 0x83de44, shape: 'box', anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 260, y: 2732 - 180 }); game.addChild(hitBtn); var hitTxt = new Text2("HIT", { size: 80, fill: 0xFFFFFF }); hitTxt.anchor.set(0.5, 0.5); hitTxt.x = 0; hitTxt.y = 0; hitBtn.addChild(hitTxt); // UI: Stand Button standBtn = LK.getAsset('standBtn', { width: 420, height: 160, color: 0xd83318, shape: 'box', anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 260, y: 2732 - 180 }); game.addChild(standBtn); var standTxt = new Text2("STAND", { size: 80, fill: 0xFFFFFF }); standTxt.anchor.set(0.5, 0.5); standTxt.x = 0; standTxt.y = 0; standBtn.addChild(standTxt); // Button event handlers hitBtn.down = function (x, y, obj) { buttonDown(x, y, hitBtn); }; standBtn.down = function (x, y, obj) { buttonDown(x, y, standBtn); }; // Prevent overlap with top left menu // (All UI is centered or at bottom, so nothing in top left 100x100) // Play music at game start LK.playMusic('blackjackmusic'); // Start first round startRound(); // No need for game.update, as all logic is event-driven // End of file
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Card class: represents a single card in the game
var Card = Container.expand(function () {
var self = Container.call(this);
// Card properties
self.rank = null; // 2-10, 'J', 'Q', 'K', 'A'
self.suit = null; // '♠', '♥', '♦', '♣'
self.faceUp = true;
// Card size
var CARD_WIDTH = 220;
var CARD_HEIGHT = 320;
// Card background
var bg = self.attachAsset('cardBg', {
width: CARD_WIDTH,
height: CARD_HEIGHT,
color: 0xffffff,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
// Card border
var border = self.attachAsset('cardBorder', {
width: CARD_WIDTH,
height: CARD_HEIGHT,
color: 0x222222,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
border.alpha = 0.12;
// Card text (rank and suit)
var txt = new Text2('', {
size: 90,
fill: 0x222222
});
txt.anchor.set(0.5, 0.5);
txt.x = 0;
txt.y = 0;
self.addChild(txt);
// Face down overlay
var back = self.attachAsset('cardBack', {
width: CARD_WIDTH,
height: CARD_HEIGHT,
color: 0x3a3a3a,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
back.visible = false;
// Set card data
self.setCard = function (rank, suit, faceUp) {
self.rank = rank;
self.suit = suit;
self.faceUp = faceUp !== false;
self.updateVisual();
};
// Update card visuals
self.updateVisual = function () {
if (self.faceUp) {
txt.visible = true;
txt.setText(self.rank + self.suit);
txt.setStyle({
fill: self.suit === '♥' || self.suit === '♦' ? "#d83318" : "#222222"
});
back.visible = false;
} else {
txt.visible = false;
back.visible = true;
}
};
self.scaleX = 1; // Ensure scaleX is initialized for flip animation
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x156c2f // green felt color
});
/****
* Game Code
****/
// Card deck setup
var SUITS = ['♠', '♥', '♦', '♣'];
var RANKS = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
// Game state
var playerHand = [];
var dealerHand = [];
var deck = [];
var inPlay = false;
var playerStands = false;
var revealDealer = false;
var messageTxt = null;
var scoreTxt = null;
var streakTxt = null;
var highScoreTxt = null;
var hitBtn = null;
var standBtn = null;
var playerScore = 0;
var winStreak = 0;
var highScore = storage.highScore || 0;
// Card layout
var CARD_SPACING = 160;
var PLAYER_Y = 2732 - 420;
// Move dealer cards lower to make room for streak/high score
var DEALER_Y = 420 + 120; // 120px lower
// UI: Player hand value
var playerValueTxt = new Text2("", {
size: 70,
fill: 0xffffff
});
playerValueTxt.anchor.set(0.5, 1);
playerValueTxt.x = 2048 / 2;
playerValueTxt.y = PLAYER_Y - 180;
game.addChild(playerValueTxt);
// UI: Dealer hand value
var dealerValueTxt = new Text2("", {
size: 70,
fill: 0xffffff
});
dealerValueTxt.anchor.set(0.5, 0);
dealerValueTxt.x = 2048 / 2;
dealerValueTxt.y = DEALER_Y + 220;
game.addChild(dealerValueTxt);
// Utility: shuffle array
function shuffle(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var t = array[i];
array[i] = array[j];
array[j] = t;
}
return array;
}
// Utility: create a new deck
function createDeck() {
var d = [];
for (var s = 0; s < SUITS.length; s++) {
for (var r = 0; r < RANKS.length; r++) {
d.push({
rank: RANKS[r],
suit: SUITS[s]
});
}
}
return shuffle(d);
}
// Utility: get hand value
function handValue(hand) {
var total = 0;
var aces = 0;
for (var i = 0; i < hand.length; i++) {
var r = hand[i].rank;
if (r === 'A') {
total += 11;
aces++;
} else if (r === 'K' || r === 'Q' || r === 'J') {
total += 10;
} else {
total += parseInt(r);
}
}
while (total > 21 && aces > 0) {
total -= 10;
aces--;
}
return total;
}
// Utility: clear all cards from the table
function clearTable() {
for (var i = 0; i < playerHand.length; i++) {
playerHand[i].destroy();
}
for (var i = 0; i < dealerHand.length; i++) {
dealerHand[i].destroy();
}
playerHand = [];
dealerHand = [];
}
// Utility: update all UI text
function updateUI() {
scoreTxt.setText(playerScore + "");
streakTxt.setText("Streak: " + winStreak);
highScoreTxt.setText("High: " + highScore);
// Show player hand value
if (playerHand.length > 0) {
var playerVal = handValue(playerHand);
playerValueTxt.setText("Your total: " + playerVal);
playerValueTxt.visible = true;
// Check for blackjack (21) and auto-win if in play, but only if it's the initial deal (2 cards)
if (playerVal === 21 && inPlay && playerHand.length === 2) {
// Show BLACKJACK message in the center of the screen, yellow color
messageTxt.setText("BLACKJACK");
messageTxt.setStyle({
fill: 0xFFE600
});
messageTxt.visible = true;
// End round as blackjack after a short delay to show the message
inPlay = false;
hitBtn.visible = false;
standBtn.visible = false;
// Set a flag so endRound knows this was a blackjack
game._justBlackjack = true;
LK.setTimeout(function () {
endRound('win');
}, 900);
}
} else {
playerValueTxt.visible = false;
}
// Show dealer hand value (only reveal if dealer's hidden card is up)
if (dealerHand.length > 0) {
var showDealer = false;
if (dealerHand.length > 1 && dealerHand[1].faceUp) showDealer = true;
if (dealerHand.length === 1) showDealer = true;
if (showDealer) {
dealerValueTxt.setText("Dealer: " + handValue(dealerHand));
dealerValueTxt.visible = true;
} else {
// Show only the visible card's value
dealerValueTxt.setText("Dealer: " + handValue([dealerHand[0]]));
dealerValueTxt.visible = true;
}
} else {
dealerValueTxt.visible = false;
}
}
// Utility: show message
function showMessage(msg, color) {
messageTxt.setText(msg);
messageTxt.setStyle({
fill: color || "#ffffff"
});
messageTxt.visible = true;
}
// Utility: hide message
function hideMessage() {
messageTxt.visible = false;
}
// Deal a card to a hand
function dealCard(hand, faceUp, x, y) {
if (deck.length === 0) {
deck = createDeck();
}
var cardData = deck.pop();
var card = new Card();
card.setCard(cardData.rank, cardData.suit, faceUp);
// Start card off-screen above the table for animation
card.x = x;
card.y = y - 400;
game.addChild(card);
hand.push(card);
// Animate card dropping into place
tween(card, {
y: y
}, {
duration: 320,
easing: tween.cubicOut
});
return card;
}
// Layout cards in a hand
function layoutHand(hand, y) {
var totalWidth = (hand.length - 1) * CARD_SPACING;
var startX = 2048 / 2 - totalWidth / 2;
for (var i = 0; i < hand.length; i++) {
var targetX = startX + i * CARD_SPACING;
// Animate both x and y for smooth movement
tween(hand[i], {
x: targetX,
y: y
}, {
duration: 220,
easing: tween.cubicOut
});
}
}
// Start a new round
function startRound() {
clearTable();
hideMessage();
inPlay = true;
playerStands = false;
revealDealer = false;
// Shuffle deck if low
if (deck.length < 10) {
deck = createDeck();
}
// Deal initial cards
dealCard(playerHand, true, 2048 / 2 - CARD_SPACING / 2, PLAYER_Y);
dealCard(dealerHand, true, 2048 / 2 - CARD_SPACING / 2, DEALER_Y);
dealCard(playerHand, true, 2048 / 2 + CARD_SPACING / 2, PLAYER_Y);
dealCard(dealerHand, false, 2048 / 2 + CARD_SPACING / 2, DEALER_Y);
layoutHand(playerHand, PLAYER_Y);
layoutHand(dealerHand, DEALER_Y);
updateUI();
hitBtn.visible = true;
standBtn.visible = true;
}
// End round and show result
function endRound(result) {
inPlay = false;
revealDealer = true;
// Reveal dealer's hidden card
if (dealerHand.length > 1) {
// Animate card flip: scaleX to 0, swap face, then scaleX to 1
tween(dealerHand[1], {
scaleX: 0
}, {
duration: 120,
easing: tween.cubicIn,
onFinish: function onFinish() {
dealerHand[1].faceUp = true;
dealerHand[1].updateVisual();
tween(dealerHand[1], {
scaleX: 1
}, {
duration: 120,
easing: tween.cubicOut
});
}
});
}
// Animate dealer hand to show all cards
layoutHand(dealerHand, DEALER_Y);
if (result === 'win') {
playerScore++;
winStreak++;
if (game._justBlackjack) {
// Already showed BLACKJACK, just play sound and flash
LK.effects.flashObject(playerHand[0], 0xffe600, 600);
LK.setTimeout(function () {
LK.getSound('ding_sound_effect').play();
}, 100);
// Animate BLACKJACK message: spin and hold for 3 seconds
messageTxt.rotation = 0;
tween(messageTxt, {
rotation: Math.PI * 2
}, {
duration: 900,
easing: tween.cubicOut,
onFinish: function onFinish() {
messageTxt.rotation = 0;
}
});
// Reset flag for next round
game._justBlackjack = false;
// Delay next round for 3 seconds
var nextRoundDelay = 3000;
} else {
showMessage("You Win!", "#83de44");
LK.effects.flashObject(playerHand[0], 0x83de44, 600);
// Play win sound effect after a short delay to ensure message is visible and not interrupted
LK.setTimeout(function () {
LK.getSound('ding_sound_effect').play();
}, 100);
// Animate win message: spin and hold for 3 seconds
messageTxt.rotation = 0;
tween(messageTxt, {
rotation: Math.PI * 2
}, {
duration: 900,
easing: tween.cubicOut,
onFinish: function onFinish() {
messageTxt.rotation = 0;
}
});
var nextRoundDelay = 3000;
}
} else if (result === 'lose') {
winStreak = 0;
showMessage("You Lose", "#d83318");
LK.effects.flashObject(playerHand[0], 0xd83318, 600);
// Animate lose message: spin and hold for 3 seconds
messageTxt.rotation = 0;
tween(messageTxt, {
rotation: Math.PI * 2
}, {
duration: 900,
easing: tween.cubicOut,
onFinish: function onFinish() {
messageTxt.rotation = 0;
}
});
var nextRoundDelay = 3000;
} else {
showMessage("Push", "#b8b031");
var nextRoundDelay = 1200;
}
// Only allow high score to increase if playerScore exceeds it, and only after surpassing it with a win streak
if (playerScore > highScore && winStreak > 1) {
highScore = playerScore;
storage.highScore = highScore;
}
// If player loses, do not reset highScore, just reset winStreak (already handled above)
updateUI();
// Hide buttons
hitBtn.visible = false;
standBtn.visible = false;
// Start next round after delay
LK.setTimeout(function () {
startRound();
}, nextRoundDelay);
}
// Dealer's turn logic
function dealerTurn() {
// Reveal dealer's hidden card
if (dealerHand.length > 1) {
// Animate card flip: scaleX to 0, swap face, then scaleX to 1
tween(dealerHand[1], {
scaleX: 0
}, {
duration: 120,
easing: tween.cubicIn,
onFinish: function onFinish() {
dealerHand[1].faceUp = true;
dealerHand[1].updateVisual();
tween(dealerHand[1], {
scaleX: 1
}, {
duration: 120,
easing: tween.cubicOut
});
}
});
}
layoutHand(dealerHand, DEALER_Y);
// Dealer draws to 17 or higher
var dealerVal = handValue(dealerHand);
var playerVal = handValue(playerHand);
function dealerDraw() {
dealerVal = handValue(dealerHand);
updateUI();
if (dealerVal < 17) {
var card = dealCard(dealerHand, true, 2048 / 2 + (dealerHand.length - 1) * CARD_SPACING / 2, DEALER_Y);
layoutHand(dealerHand, DEALER_Y);
LK.setTimeout(dealerDraw, 500);
} else {
// Compare hands
if (dealerVal > 21) {
endRound('win');
} else if (dealerVal > playerVal) {
endRound('lose');
} else if (dealerVal < playerVal) {
endRound('win');
} else {
endRound('push');
}
}
}
LK.setTimeout(dealerDraw, 600);
}
// Handle player "Hit"
function onHit() {
if (!inPlay) return;
var card = dealCard(playerHand, true, 2048 / 2 + (playerHand.length - 1) * CARD_SPACING / 2, PLAYER_Y);
layoutHand(playerHand, PLAYER_Y);
var val = handValue(playerHand);
updateUI();
if (val > 21) {
endRound('lose');
}
}
// Handle player "Stand"
function onStand() {
if (!inPlay) return;
playerStands = true;
hitBtn.visible = false;
standBtn.visible = false;
dealerTurn();
}
// Handle touch on hit/stand buttons
function buttonDown(x, y, obj) {
if (!inPlay) return;
if (obj === hitBtn) {
onHit();
} else if (obj === standBtn) {
onStand();
}
}
// UI: Score
scoreTxt = new Text2("0", {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// UI: Streak
streakTxt = new Text2("Streak: 0", {
size: 60,
fill: 0xB8B031
});
streakTxt.anchor.set(0.5, 0);
streakTxt.y = 120;
LK.gui.top.addChild(streakTxt);
// UI: High Score
highScoreTxt = new Text2("High: " + highScore, {
size: 60,
fill: 0x83DE44
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.y = 200;
LK.gui.top.addChild(highScoreTxt);
// UI: Message
messageTxt = new Text2("", {
size: 120,
fill: 0xFFFFFF
});
messageTxt.anchor.set(0.5, 0.5);
messageTxt.x = 2048 / 2;
messageTxt.y = 2732 / 2 - 100;
messageTxt.visible = false;
game.addChild(messageTxt);
// UI: Hit Button
hitBtn = LK.getAsset('hitBtn', {
width: 420,
height: 160,
color: 0x83de44,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 260,
y: 2732 - 180
});
game.addChild(hitBtn);
var hitTxt = new Text2("HIT", {
size: 80,
fill: 0xFFFFFF
});
hitTxt.anchor.set(0.5, 0.5);
hitTxt.x = 0;
hitTxt.y = 0;
hitBtn.addChild(hitTxt);
// UI: Stand Button
standBtn = LK.getAsset('standBtn', {
width: 420,
height: 160,
color: 0xd83318,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 260,
y: 2732 - 180
});
game.addChild(standBtn);
var standTxt = new Text2("STAND", {
size: 80,
fill: 0xFFFFFF
});
standTxt.anchor.set(0.5, 0.5);
standTxt.x = 0;
standTxt.y = 0;
standBtn.addChild(standTxt);
// Button event handlers
hitBtn.down = function (x, y, obj) {
buttonDown(x, y, hitBtn);
};
standBtn.down = function (x, y, obj) {
buttonDown(x, y, standBtn);
};
// Prevent overlap with top left menu
// (All UI is centered or at bottom, so nothing in top left 100x100)
// Play music at game start
LK.playMusic('blackjackmusic');
// Start first round
startRound();
// No need for game.update, as all logic is event-driven
// End of file