/****
* 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