/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Button = Container.expand(function (styleType) {
var self = Container.call(this);
self.buttonText = '';
self.onClick = function () {};
self.isHovered = false;
self.enabled = true;
self.hitWidth = UI.buttonWidth;
self.hitHeight = styleType === 'small' ? UI.smallButtonHeight : UI.buttonHeight;
self.styleType = styleType || 'normal';
var assetName = self.styleType === 'small' ? 'smallButtonBg' : 'buttonBg';
var bg = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
var text = new Text2('', {
size: self.styleType === 'small' ? 24 : 28,
fill: COLORS.white,
align: 'center'
});
text.anchor.set(0.5, 0.5);
self.addChild(text);
self.setText = function (str) {
self.buttonText = str;
text.setText(str);
};
self.setTint = function (color) {
bg.tint = color;
self.baseTint = color;
};
self.setEnabled = function (enabled) {
self.enabled = enabled;
self.alpha = enabled ? 1 : 0.45;
if (!enabled) {
bg.tint = COLORS.buttonDisabled;
} else {
bg.tint = self.isHovered ? COLORS.buttonHover : self.baseTint || COLORS.button;
}
};
self.setHovered = function (hovered) {
self.isHovered = hovered;
if (!self.enabled) {
bg.tint = COLORS.buttonDisabled;
return;
}
bg.tint = hovered ? COLORS.buttonHover : self.baseTint || COLORS.button;
};
self.press = function () {
if (!self.enabled) {
return;
}
safePlaySound('buttonClickSound');
self.scale.set(0.98);
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeOut
});
self.onClick();
};
self.setTint(COLORS.button);
return self;
});
var DustParticle = Container.expand(function () {
var self = Container.call(this);
var dust = self.attachAsset('dustParticle', {
anchorX: 0.5,
anchorY: 0.5
});
dust.alpha = 0.82;
self.velocityX = (Math.random() - 0.5) * 6;
self.velocityY = (Math.random() - 0.5) * 6;
self.gravity = 0.08;
self.life = 0.45;
self.maxLife = 0.45;
self.update = function () {
self.velocityY += self.gravity;
self.x += self.velocityX;
self.y += self.velocityY;
self.life -= 1 / 60;
self.scale.set(1 + (1 - self.life / self.maxLife) * 0.6);
dust.alpha = 0.82 * (self.life / self.maxLife);
if (self.life <= 0) {
if (self.parent) {
self.parent.removeChild(self);
}
self.destroy();
}
};
return self;
});
var ScratchTicket = Container.expand(function (ticketType) {
var self = Container.call(this);
self.ticketType = ticketType || 0;
self.symbols = [];
self.scratchAmount = 0;
self.resolved = false;
self.canScratch = true;
self.ticketWidth = UI.ticketWidth;
self.ticketHeight = UI.ticketHeight;
self.modifier = rollTicketModifier(ticketType);
var scratchSpeedBonus = gameState.upgrades[2] * 0.03;
var prestigeScratchBonus = gameState.prestigeChips * 0.003;
self.autoResolveThreshold = clamp(0.56 - scratchSpeedBonus - prestigeScratchBonus, 0.22, 0.56);
var bg = self.attachAsset('ticketBg', {
anchorX: 0.5,
anchorY: 0.5
});
bg.tint = getTicketTint(self.ticketType);
var overlay = self.attachAsset('scratchOverlay', {
anchorX: 0.5,
anchorY: 0.5
});
overlay.alpha = 0.95;
var titleText = new Text2(getTicketName(self.ticketType), {
size: 38,
fill: COLORS.white,
align: 'center'
});
titleText.anchor.set(0.5, 0);
titleText.y = -500;
self.addChild(titleText);
var infoText = new Text2('Scratch to reveal 3 matching symbols', {
size: 24,
fill: COLORS.primary,
align: 'center'
});
infoText.anchor.set(0.5, 0);
infoText.y = -450;
self.addChild(infoText);
var modifierLine = new Text2(self.modifier.label, {
size: 26,
fill: self.modifier.color,
align: 'center'
});
modifierLine.anchor.set(0.5, 0);
modifierLine.y = -405;
self.addChild(modifierLine);
var gridStartX = -UI.symbolSpacing;
var gridStartY = -UI.symbolSpacing;
var spacing = UI.symbolSpacing;
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
var symbol = new _Symbol();
symbol.x = gridStartX + col * spacing;
symbol.y = gridStartY + row * spacing + 70;
self.symbols.push(symbol);
self.addChild(symbol);
}
}
applyModifierToTicket(self);
self.containsGlobalPoint = function (x, y) {
return pointInRect(x, y, self.x, self.y, self.ticketWidth, self.ticketHeight);
};
self.updateScratch = function (amount) {
if (!self.canScratch || self.resolved) {
return;
}
self.scratchAmount = clamp(self.scratchAmount + amount, 0, 1);
overlay.alpha = clamp(0.95 - self.scratchAmount, 0, 0.95);
var revealTarget = Math.floor(self.scratchAmount * self.symbols.length);
var alreadyRevealed = 0;
for (var i = 0; i < self.symbols.length; i++) {
if (self.symbols[i].revealed) {
alreadyRevealed++;
}
}
while (alreadyRevealed < revealTarget) {
var hidden = [];
for (var j = 0; j < self.symbols.length; j++) {
if (!self.symbols[j].revealed) {
hidden.push(self.symbols[j]);
}
}
if (hidden.length <= 0) {
break;
}
var pick = hidden[Math.floor(Math.random() * hidden.length)];
pick.reveal();
alreadyRevealed++;
}
if (self.scratchAmount >= self.autoResolveThreshold) {
self.resolveTicket();
}
};
self.checkMatch = function () {
var counts = {};
for (var i = 0; i < self.symbols.length; i++) {
var type = self.symbols[i].symbolType;
counts[type] = (counts[type] || 0) + 1;
}
var bestType = null;
var bestCount = 0;
for (var key in counts) {
if (counts[key] > bestCount) {
bestType = key;
bestCount = counts[key];
}
}
return {
win: bestCount >= 3,
type: bestType,
count: bestCount
};
};
self.revealAll = function () {
for (var i = 0; i < self.symbols.length; i++) {
self.symbols[i].reveal();
}
overlay.alpha = 0;
};
self.resolveTicket = function () {
if (self.resolved) {
return;
}
self.resolved = true;
self.canScratch = false;
self.revealAll();
var result = self.checkMatch();
result.modifier = self.modifier;
result.ticketType = self.ticketType;
resolveCurrentTicket(result);
};
return self;
});
/****
* Modifiers
****/
var UpgradeCard = Container.expand(function () {
var self = Container.call(this);
self.upgradeId = 0;
self.hitWidth = UI.buttonWidth;
self.hitHeight = UI.upgradeCardHeight;
self.onClick = function () {};
var bg = self.attachAsset('upgradeCardBg', {
anchorX: 0.5,
anchorY: 0.5
});
var nameText = new Text2('', {
size: 24,
fill: COLORS.white,
align: 'center'
});
nameText.anchor.set(0.5, 0);
nameText.y = -28;
self.addChild(nameText);
var descText = new Text2('', {
size: 16,
fill: COLORS.muted,
align: 'center'
});
descText.anchor.set(0.5, 0);
descText.y = 2;
self.addChild(descText);
var costText = new Text2('', {
size: 20,
fill: COLORS.gold,
align: 'center'
});
costText.anchor.set(0.5, 0);
costText.y = 34;
self.addChild(costText);
self.setUpgrade = function (id, level, cost, affordable) {
self.upgradeId = id;
var names = ['Better Odds', 'Bigger Payouts', 'Faster Scratch', 'Combo Boost', 'Jackpot Gain', 'Coin Magnet'];
var descs = ['More good modifier rolls', 'Higher match payouts', 'Resolve cards faster', 'Bigger streak scaling', 'Fill jackpot quicker', 'Extra economy support'];
nameText.setText(names[id] + ' Lv.' + (level + 1));
descText.setText(descs[id]);
costText.setText('BUY ' + formatMoney(cost));
self.alpha = affordable ? 1 : 0.52;
};
self.setHovered = function (hovered) {
bg.tint = hovered ? 0x2c5c8f : 0x1f3b60;
};
self.containsGlobalPoint = function (x, y) {
var gx = rightPanel.x + self.x;
var gy = rightPanel.y + self.y;
return pointInRect(x, y, gx, gy, self.hitWidth, self.hitHeight);
};
self.press = function () {
safePlaySound('buttonClickSound');
self.scale.set(0.985);
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeOut
});
self.onClick();
};
return self;
});
var _Symbol = Container.expand(function () {
var self = Container.call(this);
self.symbolType = 0;
self.revealed = false;
var bg = self.attachAsset('symbolBg', {
anchorX: 0.5,
anchorY: 0.5
});
var symbolText = new Text2('', {
size: 74,
fill: COLORS.white,
align: 'center'
});
symbolText.anchor.set(0.5, 0.5);
symbolText.alpha = 0.22;
self.addChild(symbolText);
self.setSymbol = function (type) {
self.symbolType = type;
var symbols = ['🍀', '💎', '🎁', '⭐', '🔥', '🎰'];
symbolText.setText(symbols[type % symbols.length]);
};
self.highlight = function (color) {
bg.tint = color || 0x8b7a31;
};
self.reveal = function () {
if (self.revealed) {
return;
}
self.revealed = true;
bg.tint = 0x4c5f82;
symbolText.alpha = 1;
safePlaySound('revealSound');
};
self.setSymbol(Math.floor(Math.random() * 6));
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x081018
});
/****
* Game Code
****/
/****
* UI SCALE + LAYOUT
****/
/****
* LUCKY SCRATCH TYCOON
* Larger, cleaner, more polished Upit version
****/
/****
* Runtime State
****/
var SCREEN_W = 2048;
var SCREEN_H = 2732;
var CENTER_X = 1024;
var CENTER_Y = 1366;
var UI = {
panelWidth: 430,
panelHeight: 1040,
panelCornerOffset: 90,
buttonWidth: 560,
buttonHeight: 92,
smallButtonHeight: 84,
upgradeCardHeight: 108,
meterWidth: 320,
meterHeight: 26,
ticketWidth: 860,
ticketHeight: 1120,
symbolSize: 180,
symbolSpacing: 220,
leftPanelX: 70,
leftPanelY: 180,
rightPanelX: 1978,
rightPanelY: 180,
bottomPanelY: 2520,
titleY: 54,
modifierY: 170,
ticketY: 1320,
titleSize: 56,
sectionLabelSize: 28,
bigValueSize: 54,
mediumValueSize: 34,
bodySize: 24,
smallSize: 20,
tinySize: 17,
popupSize: 42
};
/****
* COLORS
****/
var COLORS = {
bg: 0x081018,
panel: 0x162235,
panelAlt: 0x1b2a40,
button: 0x255c99,
buttonHover: 0x3a78bf,
buttonDisabled: 0x3a4558,
primary: '#ffd76a',
white: '#ffffff',
soft: '#c9d3e3',
muted: '#94a3b8',
success: '#44e39a',
warning: '#ffb25f',
danger: '#ff6f7d',
cyan: '#69d2ff',
gold: '#ffe07a',
purple: '#b48cff'
};
/****
* Helpers
****/
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function safePlaySound(name) {
try {
var s = LK.getSound(name);
if (s && s.play) {
s.play();
}
} catch (e) {}
}
function safePlayMusic(name, options) {
try {
LK.playMusic(name, options || {});
} catch (e) {}
}
function formatMoney(value) {
return '$' + Math.floor(value);
}
function pointInRect(px, py, cx, cy, width, height) {
return px >= cx - width * 0.5 && px <= cx + width * 0.5 && py >= cy - height * 0.5 && py <= cy + height * 0.5;
}
function getUpgradeCost(id, level) {
var baseCosts = [50, 75, 60, 80, 100, 70];
return Math.floor(baseCosts[id] * Math.pow(1.32, level));
}
function getTicketName(type) {
if (type === 1) {
return 'LUCKY TICKET';
}
if (type === 2) {
return 'GOLD TICKET';
}
return 'BASIC TICKET';
}
function getTicketTint(type) {
if (type === 1) {
return 0x183b31;
}
if (type === 2) {
return 0x4a3818;
}
return 0x28344e;
}
function getTicketCost(type) {
return [5, 15, 50][type] || 5;
}
function canBuyTicket(type) {
if (type === 1 && !gameState.unlockLuckyTicket) {
return false;
}
if (type === 2 && !gameState.unlockGoldTicket) {
return false;
}
return true;
}
function getPrestigeReward() {
return Math.floor(Math.sqrt(gameState.totalCoins / 500));
}
function getPrestigeMultiplier() {
return 1 + gameState.prestigeChips * 0.05;
}
function getCurrentAchievementsCount() {
var count = 0;
if (gameState.achievementFirstWin) {
count++;
}
if (gameState.achievementLuckyUnlocked) {
count++;
}
if (gameState.achievementGoldUnlocked) {
count++;
}
if (gameState.achievementFirstJackpot) {
count++;
}
if (gameState.achievementStreak5) {
count++;
}
if (gameState.achievementStreak10) {
count++;
}
if (gameState.achievementFirstPrestige) {
count++;
}
if (gameState.achievementTickets50) {
count++;
}
if (gameState.achievementTickets200) {
count++;
}
if (gameState.achievementBigWin100) {
count++;
}
if (gameState.achievementModifierWin) {
count++;
}
if (gameState.achievementWealthy1000) {
count++;
}
return count;
}
function throttleScratchSound() {
if (LK.ticks - gameState.lastScratchSoundTick > 3) {
gameState.lastScratchSoundTick = LK.ticks;
safePlaySound('scratchSound');
}
}
function showFlash(color, alpha) {
if (!flashOverlay) {
return;
}
flashOverlay.tint = color || 0xffffff;
flashOverlay.alpha = alpha || 0.3;
tween(flashOverlay, {
alpha: 0
}, {
duration: 260,
easing: tween.easeOut
});
}
/****
* Storage Defaults - Flat Keys Only
****/
if (storage.coins === undefined) {
storage.coins = 100;
}
if (storage.totalCoins === undefined) {
storage.totalCoins = 100;
}
if (storage.bestStreak === undefined) {
storage.bestStreak = 0;
}
if (storage.jackpotMeter === undefined) {
storage.jackpotMeter = 0;
}
if (storage.ticketsScratchedTotal === undefined) {
storage.ticketsScratchedTotal = 0;
}
if (storage.biggestWin === undefined) {
storage.biggestWin = 0;
}
if (storage.jackpotsHit === undefined) {
storage.jackpotsHit = 0;
}
if (storage.prestigeCount === undefined) {
storage.prestigeCount = 0;
}
if (storage.prestigeChips === undefined) {
storage.prestigeChips = 0;
}
if (storage.upgrade0 === undefined) {
storage.upgrade0 = 0;
}
if (storage.upgrade1 === undefined) {
storage.upgrade1 = 0;
}
if (storage.upgrade2 === undefined) {
storage.upgrade2 = 0;
}
if (storage.upgrade3 === undefined) {
storage.upgrade3 = 0;
}
if (storage.upgrade4 === undefined) {
storage.upgrade4 = 0;
}
if (storage.upgrade5 === undefined) {
storage.upgrade5 = 0;
}
if (storage.unlockLuckyTicket === undefined) {
storage.unlockLuckyTicket = false;
}
if (storage.unlockGoldTicket === undefined) {
storage.unlockGoldTicket = false;
}
if (storage.unlockJackpotSystem === undefined) {
storage.unlockJackpotSystem = false;
}
if (storage.unlockModifiers === undefined) {
storage.unlockModifiers = false;
}
if (storage.unlockPrestige === undefined) {
storage.unlockPrestige = false;
}
if (storage.statsTotalWins === undefined) {
storage.statsTotalWins = 0;
}
if (storage.statsTotalLosses === undefined) {
storage.statsTotalLosses = 0;
}
if (storage.statsNearMisses === undefined) {
storage.statsNearMisses = 0;
}
if (storage.statsCardsBasic === undefined) {
storage.statsCardsBasic = 0;
}
if (storage.statsCardsLucky === undefined) {
storage.statsCardsLucky = 0;
}
if (storage.statsCardsGold === undefined) {
storage.statsCardsGold = 0;
}
if (storage.statsModifierWins === undefined) {
storage.statsModifierWins = 0;
}
if (storage.statsPrestigeEarnedLifetime === undefined) {
storage.statsPrestigeEarnedLifetime = 0;
}
if (storage.achievementFirstWin === undefined) {
storage.achievementFirstWin = false;
}
if (storage.achievementLuckyUnlocked === undefined) {
storage.achievementLuckyUnlocked = false;
}
if (storage.achievementGoldUnlocked === undefined) {
storage.achievementGoldUnlocked = false;
}
if (storage.achievementFirstJackpot === undefined) {
storage.achievementFirstJackpot = false;
}
if (storage.achievementStreak5 === undefined) {
storage.achievementStreak5 = false;
}
if (storage.achievementStreak10 === undefined) {
storage.achievementStreak10 = false;
}
if (storage.achievementFirstPrestige === undefined) {
storage.achievementFirstPrestige = false;
}
if (storage.achievementTickets50 === undefined) {
storage.achievementTickets50 = false;
}
if (storage.achievementTickets200 === undefined) {
storage.achievementTickets200 = false;
}
if (storage.achievementBigWin100 === undefined) {
storage.achievementBigWin100 = false;
}
if (storage.achievementModifierWin === undefined) {
storage.achievementModifierWin = false;
}
if (storage.achievementWealthy1000 === undefined) {
storage.achievementWealthy1000 = false;
}
var gameState = {
coins: storage.coins,
totalCoins: storage.totalCoins,
streak: 0,
bestStreak: storage.bestStreak,
jackpotMeter: storage.jackpotMeter,
jackpotMax: 100,
ticketsScratchedTotal: storage.ticketsScratchedTotal,
biggestWin: storage.biggestWin,
jackpotsHit: storage.jackpotsHit,
prestigeCount: storage.prestigeCount,
prestigeChips: storage.prestigeChips,
upgrades: [storage.upgrade0, storage.upgrade1, storage.upgrade2, storage.upgrade3, storage.upgrade4, storage.upgrade5],
unlockLuckyTicket: storage.unlockLuckyTicket,
unlockGoldTicket: storage.unlockGoldTicket,
unlockJackpotSystem: storage.unlockJackpotSystem,
unlockModifiers: storage.unlockModifiers,
unlockPrestige: storage.unlockPrestige,
statsTotalWins: storage.statsTotalWins,
statsTotalLosses: storage.statsTotalLosses,
statsNearMisses: storage.statsNearMisses,
statsCardsBasic: storage.statsCardsBasic,
statsCardsLucky: storage.statsCardsLucky,
statsCardsGold: storage.statsCardsGold,
statsModifierWins: storage.statsModifierWins,
statsPrestigeEarnedLifetime: storage.statsPrestigeEarnedLifetime,
achievementFirstWin: storage.achievementFirstWin,
achievementLuckyUnlocked: storage.achievementLuckyUnlocked,
achievementGoldUnlocked: storage.achievementGoldUnlocked,
achievementFirstJackpot: storage.achievementFirstJackpot,
achievementStreak5: storage.achievementStreak5,
achievementStreak10: storage.achievementStreak10,
achievementFirstPrestige: storage.achievementFirstPrestige,
achievementTickets50: storage.achievementTickets50,
achievementTickets200: storage.achievementTickets200,
achievementBigWin100: storage.achievementBigWin100,
achievementModifierWin: storage.achievementModifierWin,
achievementWealthy1000: storage.achievementWealthy1000,
lastScratchSoundTick: -999,
isGameOver: false
};
var currentTicket = null;
var flashOverlay = null;
var gameOverOverlay = null;
var gameOverRestartButton = null;
var dragData = {
active: false,
lastX: 0,
lastY: 0
};
var dustParticles = [];
var popupTexts = [];
var leftPanel, rightPanel, bottomPanel, centerInfoPanel;
var coinText, streakText, jackpotBar, jackpotText, statsText, modifierText, unlockText, achievementText, prestigeText;
var ticketBuyButtons = [];
var upgradeCards = [];
var prestigeButton;
var restartRunButton;
var wipeSaveButton;
/****
* Modifiers
****/
function rollTicketModifier(ticketType) {
if (!gameState.unlockModifiers) {
return {
id: 'none',
label: 'Standard Ticket',
color: COLORS.soft,
payoutMult: 1,
jackpotBonus: 0
};
}
var pool = [{
id: 'none',
label: 'Standard Ticket',
color: COLORS.soft,
payoutMult: 1,
jackpotBonus: 0
}, {
id: 'double',
label: 'Double Payout',
color: COLORS.success,
payoutMult: 2,
jackpotBonus: 0
}, {
id: 'jackpot',
label: 'Double Jackpot Gain',
color: COLORS.gold,
payoutMult: 1,
jackpotBonus: 2
}, {
id: 'wild',
label: 'Lucky Wild Card',
color: COLORS.cyan,
payoutMult: 1.35,
jackpotBonus: 0
}, {
id: 'hot',
label: 'Hot Streak Boost',
color: COLORS.warning,
payoutMult: 1.5,
jackpotBonus: 0
}];
var oddsBoost = gameState.upgrades[0] * 0.02 + gameState.prestigeChips * 0.005;
var roll = Math.random();
if (roll < 0.35 - oddsBoost) {
return pool[0];
}
if (roll < 0.55) {
return pool[1];
}
if (roll < 0.72) {
return pool[2];
}
if (roll < 0.87) {
return pool[3];
}
return pool[4];
}
function applyModifierToTicket(ticket) {
if (!ticket || !ticket.modifier) {
return;
}
if (ticket.modifier.id === 'wild') {
var wildIndex = Math.floor(Math.random() * ticket.symbols.length);
var baseType = ticket.symbols[Math.floor(Math.random() * ticket.symbols.length)].symbolType;
ticket.symbols[wildIndex].setSymbol(baseType);
ticket.symbols[wildIndex].highlight(0x2d7697);
}
}
/****
* Achievements / Milestones
****/
function unlockAchievement(key, label) {
if (gameState[key]) {
return;
}
gameState[key] = true;
createPopupText(CENTER_X, 920, 'ACHIEVEMENT: ' + label, COLORS.primary, 30);
showFlash(0xffd700, 0.18);
}
function checkMilestones() {
if (!gameState.unlockLuckyTicket && gameState.ticketsScratchedTotal >= 10) {
gameState.unlockLuckyTicket = true;
unlockAchievement('achievementLuckyUnlocked', 'Lucky Tickets Unlocked!');
createPopupText(CENTER_X, 680, 'LUCKY TICKETS UNLOCKED', COLORS.success, 44);
}
if (!gameState.unlockGoldTicket && gameState.totalCoins >= 1000) {
gameState.unlockGoldTicket = true;
unlockAchievement('achievementGoldUnlocked', 'Gold Tickets Unlocked!');
createPopupText(CENTER_X, 740, 'GOLD TICKETS UNLOCKED', COLORS.primary, 44);
}
if (!gameState.unlockJackpotSystem && gameState.ticketsScratchedTotal >= 15) {
gameState.unlockJackpotSystem = true;
createPopupText(CENTER_X, 800, 'JACKPOT SYSTEM UNLOCKED', COLORS.cyan, 34);
}
if (!gameState.unlockModifiers && gameState.ticketsScratchedTotal >= 25) {
gameState.unlockModifiers = true;
createPopupText(CENTER_X, 860, 'TICKET MODIFIERS UNLOCKED', COLORS.warning, 34);
}
if (!gameState.unlockPrestige && gameState.totalCoins >= 5000) {
gameState.unlockPrestige = true;
createPopupText(CENTER_X, 920, 'PRESTIGE UNLOCKED', COLORS.white, 34);
}
}
function evaluateAchievements() {
if (gameState.statsTotalWins >= 1) {
unlockAchievement('achievementFirstWin', 'First Win');
}
if (gameState.jackpotsHit >= 1) {
unlockAchievement('achievementFirstJackpot', 'First Jackpot');
}
if (gameState.bestStreak >= 5) {
unlockAchievement('achievementStreak5', '5 Win Streak');
}
if (gameState.bestStreak >= 10) {
unlockAchievement('achievementStreak10', '10 Win Streak');
}
if (gameState.ticketsScratchedTotal >= 50) {
unlockAchievement('achievementTickets50', '50 Tickets Scratched');
}
if (gameState.ticketsScratchedTotal >= 200) {
unlockAchievement('achievementTickets200', '200 Tickets Scratched');
}
if (gameState.biggestWin >= 100) {
unlockAchievement('achievementBigWin100', 'Big Win 100+');
}
if (gameState.statsModifierWins >= 1) {
unlockAchievement('achievementModifierWin', 'Modifier Master');
}
if (gameState.totalCoins >= 1000) {
unlockAchievement('achievementWealthy1000', 'Earned 1000 Coins');
}
if (gameState.prestigeCount >= 1) {
unlockAchievement('achievementFirstPrestige', 'First Prestige');
}
}
/****
* Reset / Save
****/
function restartRunOnly() {
gameState.coins = 100 + gameState.prestigeChips * 10;
gameState.streak = 0;
gameState.jackpotMeter = 0;
gameState.upgrades = [0, 0, 0, 0, 0, 0];
gameState.isGameOver = false;
if (currentTicket) {
if (currentTicket.parent) {
currentTicket.parent.removeChild(currentTicket);
}
currentTicket.destroy();
currentTicket = null;
}
hideGameOver();
createPopupText(CENTER_X, 1040, 'RUN RESTARTED', COLORS.white, 42);
updateUI();
saveProgress();
}
function hardRestartGame() {
gameState.coins = 100;
gameState.totalCoins = 100;
gameState.streak = 0;
gameState.bestStreak = 0;
gameState.jackpotMeter = 0;
gameState.ticketsScratchedTotal = 0;
gameState.biggestWin = 0;
gameState.jackpotsHit = 0;
gameState.prestigeCount = 0;
gameState.prestigeChips = 0;
gameState.upgrades = [0, 0, 0, 0, 0, 0];
gameState.unlockLuckyTicket = false;
gameState.unlockGoldTicket = false;
gameState.unlockJackpotSystem = false;
gameState.unlockModifiers = false;
gameState.unlockPrestige = false;
gameState.statsTotalWins = 0;
gameState.statsTotalLosses = 0;
gameState.statsNearMisses = 0;
gameState.statsCardsBasic = 0;
gameState.statsCardsLucky = 0;
gameState.statsCardsGold = 0;
gameState.statsModifierWins = 0;
gameState.statsPrestigeEarnedLifetime = 0;
gameState.achievementFirstWin = false;
gameState.achievementLuckyUnlocked = false;
gameState.achievementGoldUnlocked = false;
gameState.achievementFirstJackpot = false;
gameState.achievementStreak5 = false;
gameState.achievementStreak10 = false;
gameState.achievementFirstPrestige = false;
gameState.achievementTickets50 = false;
gameState.achievementTickets200 = false;
gameState.achievementBigWin100 = false;
gameState.achievementModifierWin = false;
gameState.achievementWealthy1000 = false;
gameState.isGameOver = false;
if (currentTicket) {
if (currentTicket.parent) {
currentTicket.parent.removeChild(currentTicket);
}
currentTicket.destroy();
currentTicket = null;
}
hideGameOver();
createPopupText(CENTER_X, 1040, 'SAVE WIPED', COLORS.danger, 42);
updateUI();
saveProgress();
}
function resetRunForPrestige() {
var reward = getPrestigeReward();
if (!gameState.unlockPrestige || reward <= 0) {
return;
}
gameState.prestigeCount++;
gameState.prestigeChips += reward;
gameState.statsPrestigeEarnedLifetime += reward;
gameState.coins = 100 + reward * 10;
gameState.streak = 0;
gameState.jackpotMeter = 0;
gameState.upgrades = [0, 0, 0, 0, 0, 0];
gameState.isGameOver = false;
if (currentTicket) {
if (currentTicket.parent) {
currentTicket.parent.removeChild(currentTicket);
}
currentTicket.destroy();
currentTicket = null;
}
hideGameOver();
createPopupText(CENTER_X, 980, 'PRESTIGE +' + reward + ' CHIPS', COLORS.white, 46);
showFlash(0xffffff, 0.32);
evaluateAchievements();
updateUI();
saveProgress();
}
function saveProgress() {
storage.coins = gameState.coins;
storage.totalCoins = gameState.totalCoins;
storage.bestStreak = gameState.bestStreak;
storage.jackpotMeter = gameState.jackpotMeter;
storage.ticketsScratchedTotal = gameState.ticketsScratchedTotal;
storage.biggestWin = gameState.biggestWin;
storage.jackpotsHit = gameState.jackpotsHit;
storage.prestigeCount = gameState.prestigeCount;
storage.prestigeChips = gameState.prestigeChips;
storage.upgrade0 = gameState.upgrades[0];
storage.upgrade1 = gameState.upgrades[1];
storage.upgrade2 = gameState.upgrades[2];
storage.upgrade3 = gameState.upgrades[3];
storage.upgrade4 = gameState.upgrades[4];
storage.upgrade5 = gameState.upgrades[5];
storage.unlockLuckyTicket = gameState.unlockLuckyTicket;
storage.unlockGoldTicket = gameState.unlockGoldTicket;
storage.unlockJackpotSystem = gameState.unlockJackpotSystem;
storage.unlockModifiers = gameState.unlockModifiers;
storage.unlockPrestige = gameState.unlockPrestige;
storage.statsTotalWins = gameState.statsTotalWins;
storage.statsTotalLosses = gameState.statsTotalLosses;
storage.statsNearMisses = gameState.statsNearMisses;
storage.statsCardsBasic = gameState.statsCardsBasic;
storage.statsCardsLucky = gameState.statsCardsLucky;
storage.statsCardsGold = gameState.statsCardsGold;
storage.statsModifierWins = gameState.statsModifierWins;
storage.statsPrestigeEarnedLifetime = gameState.statsPrestigeEarnedLifetime;
storage.achievementFirstWin = gameState.achievementFirstWin;
storage.achievementLuckyUnlocked = gameState.achievementLuckyUnlocked;
storage.achievementGoldUnlocked = gameState.achievementGoldUnlocked;
storage.achievementFirstJackpot = gameState.achievementFirstJackpot;
storage.achievementStreak5 = gameState.achievementStreak5;
storage.achievementStreak10 = gameState.achievementStreak10;
storage.achievementFirstPrestige = gameState.achievementFirstPrestige;
storage.achievementTickets50 = gameState.achievementTickets50;
storage.achievementTickets200 = gameState.achievementTickets200;
storage.achievementBigWin100 = gameState.achievementBigWin100;
storage.achievementModifierWin = gameState.achievementModifierWin;
storage.achievementWealthy1000 = gameState.achievementWealthy1000;
}
/****
* UI
****/
function initUI() {
leftPanel = new Container();
leftPanel.x = UI.leftPanelX;
leftPanel.y = UI.leftPanelY;
game.addChild(leftPanel);
var leftBg = LK.getAsset('panelBg', {
anchorX: 0,
anchorY: 0
});
leftPanel.addChild(leftBg);
var leftHeader = new Text2('PLAYER STATS', {
size: UI.sectionLabelSize,
fill: COLORS.primary
});
leftHeader.anchor.set(0.5, 0);
leftHeader.x = UI.panelWidth * 0.5;
leftHeader.y = 26;
leftPanel.addChild(leftHeader);
var coinsLabelText = new Text2('COINS', {
size: UI.smallSize,
fill: COLORS.primary
});
coinsLabelText.anchor.set(0.5, 0);
coinsLabelText.x = UI.panelWidth * 0.5;
coinsLabelText.y = 92;
leftPanel.addChild(coinsLabelText);
coinText = new Text2('$100', {
size: UI.bigValueSize,
fill: COLORS.white
});
coinText.anchor.set(0.5, 0);
coinText.x = UI.panelWidth * 0.5;
coinText.y = 126;
leftPanel.addChild(coinText);
var streakLabelText = new Text2('STREAK', {
size: UI.smallSize,
fill: COLORS.danger
});
streakLabelText.anchor.set(0.5, 0);
streakLabelText.x = UI.panelWidth * 0.5;
streakLabelText.y = 212;
leftPanel.addChild(streakLabelText);
streakText = new Text2('x0', {
size: UI.mediumValueSize,
fill: COLORS.white
});
streakText.anchor.set(0.5, 0);
streakText.x = UI.panelWidth * 0.5;
streakText.y = 246;
leftPanel.addChild(streakText);
var jackpotLabelText = new Text2('JACKPOT METER', {
size: UI.smallSize,
fill: COLORS.primary
});
jackpotLabelText.anchor.set(0.5, 0);
jackpotLabelText.x = UI.panelWidth * 0.5;
jackpotLabelText.y = 328;
leftPanel.addChild(jackpotLabelText);
var jackpotBgBar = LK.getAsset('meterBarEmpty', {
anchorX: 0.5,
anchorY: 0
});
jackpotBgBar.x = UI.panelWidth * 0.5;
jackpotBgBar.y = 366;
leftPanel.addChild(jackpotBgBar);
jackpotBar = LK.getAsset('meterBarFull', {
anchorX: 0.5,
anchorY: 0
});
jackpotBar.x = UI.panelWidth * 0.5;
jackpotBar.y = 366;
jackpotBar.scaleX = 0;
leftPanel.addChild(jackpotBar);
jackpotText = new Text2('0 / 100', {
size: UI.smallSize,
fill: COLORS.white
});
jackpotText.anchor.set(0.5, 0);
jackpotText.x = UI.panelWidth * 0.5;
jackpotText.y = 402;
leftPanel.addChild(jackpotText);
prestigeText = new Text2('', {
size: UI.bodySize,
fill: COLORS.cyan,
align: 'center'
});
prestigeText.anchor.set(0.5, 0);
prestigeText.x = UI.panelWidth * 0.5;
prestigeText.y = 468;
leftPanel.addChild(prestigeText);
statsText = new Text2('', {
size: UI.smallSize,
fill: COLORS.soft,
align: 'center'
});
statsText.anchor.set(0.5, 0);
statsText.x = UI.panelWidth * 0.5;
statsText.y = 622;
leftPanel.addChild(statsText);
unlockText = new Text2('', {
size: UI.smallSize,
fill: COLORS.success,
align: 'center'
});
unlockText.anchor.set(0.5, 0);
unlockText.x = UI.panelWidth * 0.5;
unlockText.y = 780;
leftPanel.addChild(unlockText);
achievementText = new Text2('', {
size: UI.smallSize,
fill: COLORS.gold,
align: 'center'
});
achievementText.anchor.set(0.5, 0);
achievementText.x = UI.panelWidth * 0.5;
achievementText.y = 910;
leftPanel.addChild(achievementText);
rightPanel = new Container();
rightPanel.x = UI.rightPanelX;
rightPanel.y = UI.rightPanelY;
game.addChild(rightPanel);
var rightBg = LK.getAsset('panelBgAlt', {
anchorX: 1,
anchorY: 0
});
rightPanel.addChild(rightBg);
var shopLabelText = new Text2('UPGRADES', {
size: UI.sectionLabelSize,
fill: COLORS.success
});
shopLabelText.anchor.set(0.5, 0);
shopLabelText.x = -UI.panelWidth * 0.5;
shopLabelText.y = 26;
rightPanel.addChild(shopLabelText);
for (var i = 0; i < 6; i++) {
var card = new UpgradeCard();
card.y = 118 + i * 126;
card.x = -UI.panelWidth * 0.5;
card.onClick = function () {
onUpgradeClick(this.upgradeId);
}.bind(card);
upgradeCards.push(card);
rightPanel.addChild(card);
}
prestigeButton = new Button();
prestigeButton.x = -UI.panelWidth * 0.5;
prestigeButton.y = 878;
prestigeButton.setText('PRESTIGE');
prestigeButton.setTint(0x5b46a3);
prestigeButton.onClick = function () {
resetRunForPrestige();
};
rightPanel.addChild(prestigeButton);
restartRunButton = new Button('small');
restartRunButton.x = -UI.panelWidth * 0.5;
restartRunButton.y = 958;
restartRunButton.setText('RESTART RUN');
restartRunButton.setTint(0x2a6e56);
restartRunButton.onClick = function () {
restartRunOnly();
};
rightPanel.addChild(restartRunButton);
wipeSaveButton = new Button('small');
wipeSaveButton.x = -UI.panelWidth * 0.5;
wipeSaveButton.y = 1026;
wipeSaveButton.setText('WIPE SAVE');
wipeSaveButton.setTint(0x8a3340);
wipeSaveButton.onClick = function () {
hardRestartGame();
};
rightPanel.addChild(wipeSaveButton);
bottomPanel = new Container();
bottomPanel.x = CENTER_X;
bottomPanel.y = UI.bottomPanelY;
game.addChild(bottomPanel);
var ticketTypes = [{
name: 'BASIC\n$5',
cost: 5,
type: 0,
tint: 0x255c99
}, {
name: 'LUCKY\n$15',
cost: 15,
type: 1,
tint: 0x29765c
}, {
name: 'GOLD\n$50',
cost: 50,
type: 2,
tint: 0x977336
}];
for (var t = 0; t < ticketTypes.length; t++) {
var btn = new Button();
btn.x = -420 + t * 420;
btn.y = 0;
btn.setText(ticketTypes[t].name);
btn.setTint(ticketTypes[t].tint);
btn.cost = ticketTypes[t].cost;
btn.type = ticketTypes[t].type;
btn.onClick = function () {
onBuyTicket(this.type, this.cost);
}.bind(btn);
ticketBuyButtons.push(btn);
bottomPanel.addChild(btn);
}
centerInfoPanel = new Container();
centerInfoPanel.x = CENTER_X;
centerInfoPanel.y = UI.modifierY;
game.addChild(centerInfoPanel);
modifierText = new Text2('', {
size: 28,
fill: COLORS.white,
align: 'center'
});
modifierText.anchor.set(0.5, 0);
centerInfoPanel.addChild(modifierText);
var titleText = new Text2('LUCKY SCRATCH TYCOON', {
size: UI.titleSize,
fill: COLORS.primary
});
titleText.anchor.set(0.5, 0);
titleText.x = CENTER_X;
titleText.y = UI.titleY;
game.addChild(titleText);
flashOverlay = LK.getAsset('flashBg', {
anchorX: 0.5,
anchorY: 0.5
});
flashOverlay.x = CENTER_X;
flashOverlay.y = CENTER_Y;
flashOverlay.alpha = 0;
game.addChild(flashOverlay);
initGameOverUI();
}
function initGameOverUI() {
gameOverOverlay = new Container();
gameOverOverlay.visible = false;
game.addChild(gameOverOverlay);
var dark = LK.getAsset('overlayDark', {
anchorX: 0.5,
anchorY: 0.5
});
dark.x = CENTER_X;
dark.y = CENTER_Y;
dark.alpha = 0.72;
gameOverOverlay.addChild(dark);
var title = new Text2('YOU RAN OUT OF MONEY', {
size: 60,
fill: COLORS.danger,
align: 'center'
});
title.anchor.set(0.5, 0.5);
title.x = CENTER_X;
title.y = 1050;
gameOverOverlay.addChild(title);
var sub = new Text2('Your run is over. Restart the run or wipe your save.', {
size: 28,
fill: COLORS.white,
align: 'center'
});
sub.anchor.set(0.5, 0.5);
sub.x = CENTER_X;
sub.y = 1130;
gameOverOverlay.addChild(sub);
gameOverRestartButton = new Button();
gameOverRestartButton.x = CENTER_X;
gameOverRestartButton.y = 1260;
gameOverRestartButton.setText('RESTART RUN');
gameOverRestartButton.setTint(0x2a6e56);
gameOverRestartButton.onClick = function () {
restartRunOnly();
};
gameOverOverlay.addChild(gameOverRestartButton);
}
function showGameOver() {
gameState.isGameOver = true;
dragData.active = false;
if (gameOverOverlay) {
gameOverOverlay.visible = true;
}
showFlash(0xff0000, 0.14);
}
function hideGameOver() {
if (gameOverOverlay) {
gameOverOverlay.visible = false;
}
}
function updateUI() {
if (coinText) {
coinText.setText(formatMoney(gameState.coins));
}
if (streakText) {
streakText.setText('x' + gameState.streak);
}
if (jackpotBar) {
jackpotBar.scaleX = clamp(gameState.jackpotMeter / gameState.jackpotMax, 0, 1);
}
if (jackpotText) {
jackpotText.setText(gameState.jackpotMeter + ' / ' + gameState.jackpotMax);
}
if (prestigeText) {
prestigeText.setText('Prestige Chips: ' + gameState.prestigeChips + '\nNext Prestige: +' + getPrestigeReward() + '\nMultiplier: x' + getPrestigeMultiplier().toFixed(2));
}
if (statsText) {
statsText.setText('Best Streak: ' + gameState.bestStreak + '\nBiggest Win: ' + formatMoney(gameState.biggestWin) + '\nTickets Scratched: ' + gameState.ticketsScratchedTotal + '\nWins / Losses: ' + gameState.statsTotalWins + ' / ' + gameState.statsTotalLosses);
}
if (unlockText) {
unlockText.setText('Unlocks' + '\nLucky Ticket: ' + (gameState.unlockLuckyTicket ? 'Yes' : 'No') + '\nGold Ticket: ' + (gameState.unlockGoldTicket ? 'Yes' : 'No') + '\nModifiers: ' + (gameState.unlockModifiers ? 'Yes' : 'No') + '\nPrestige: ' + (gameState.unlockPrestige ? 'Yes' : 'No'));
}
if (achievementText) {
achievementText.setText('Achievements: ' + getCurrentAchievementsCount() + '\nNear Misses: ' + gameState.statsNearMisses + '\nModifier Wins: ' + gameState.statsModifierWins);
}
if (modifierText) {
modifierText.setText(currentTicket ? 'Current Modifier: ' + currentTicket.modifier.label : 'Buy a ticket to start');
}
for (var i = 0; i < upgradeCards.length; i++) {
var level = gameState.upgrades[i];
var cost = getUpgradeCost(i, level);
upgradeCards[i].setUpgrade(i, level, cost, gameState.coins >= cost && !gameState.isGameOver);
}
for (var j = 0; j < ticketBuyButtons.length; j++) {
var canAfford = gameState.coins >= ticketBuyButtons[j].cost;
var unlocked = canBuyTicket(ticketBuyButtons[j].type);
ticketBuyButtons[j].setEnabled(canAfford && unlocked && !gameState.isGameOver);
if (!unlocked) {
if (ticketBuyButtons[j].type === 1) {
ticketBuyButtons[j].setText('LUCKY\nLOCKED');
}
if (ticketBuyButtons[j].type === 2) {
ticketBuyButtons[j].setText('GOLD\nLOCKED');
}
} else {
if (ticketBuyButtons[j].type === 0) {
ticketBuyButtons[j].setText('BASIC\n$5');
}
if (ticketBuyButtons[j].type === 1) {
ticketBuyButtons[j].setText('LUCKY\n$15');
}
if (ticketBuyButtons[j].type === 2) {
ticketBuyButtons[j].setText('GOLD\n$50');
}
}
}
if (prestigeButton) {
prestigeButton.setText('PRESTIGE\n+' + getPrestigeReward());
prestigeButton.setEnabled(gameState.unlockPrestige && getPrestigeReward() > 0 && !gameState.isGameOver);
}
if (restartRunButton) {
restartRunButton.setEnabled(true);
}
if (wipeSaveButton) {
wipeSaveButton.setEnabled(true);
}
}
/****
* Gameplay
****/
function createNewTicket(ticketType) {
if (currentTicket) {
if (currentTicket.parent) {
currentTicket.parent.removeChild(currentTicket);
}
currentTicket.destroy();
}
currentTicket = new ScratchTicket(ticketType);
currentTicket.x = CENTER_X;
currentTicket.y = UI.ticketY;
game.addChild(currentTicket);
dragData.active = false;
updateUI();
}
function onBuyTicket(type, cost) {
if (gameState.isGameOver) {
return;
}
if (currentTicket && !currentTicket.resolved) {
return;
}
if (!canBuyTicket(type)) {
return;
}
if (gameState.coins < cost) {
return;
}
gameState.coins -= cost;
gameState.ticketsScratchedTotal++;
if (type === 0) {
gameState.statsCardsBasic++;
}
if (type === 1) {
gameState.statsCardsLucky++;
}
if (type === 2) {
gameState.statsCardsGold++;
}
createNewTicket(type);
checkMilestones();
evaluateAchievements();
updateUI();
saveProgress();
}
function onUpgradeClick(upgradeId) {
if (gameState.isGameOver) {
return;
}
var level = gameState.upgrades[upgradeId];
var cost = getUpgradeCost(upgradeId, level);
if (gameState.coins < cost) {
return;
}
gameState.coins -= cost;
gameState.upgrades[upgradeId]++;
safePlaySound('upgradeSound');
createPopupText(1720, 960, 'UPGRADE BOUGHT', COLORS.success, 28);
updateUI();
saveProgress();
checkForGameOver();
}
function createPopupText(x, y, textValue, color, size) {
var text = new Text2(textValue, {
size: size || UI.popupSize,
fill: color || COLORS.primary,
align: 'center'
});
text.anchor.set(0.5, 0.5);
text.x = x;
text.y = y;
game.addChild(text);
popupTexts.push(text);
tween(text, {
y: y - 110,
alpha: 0
}, {
duration: 850,
easing: tween.easeOut,
onFinish: function onFinish() {
if (text.parent) {
text.parent.removeChild(text);
}
var index = popupTexts.indexOf(text);
if (index >= 0) {
popupTexts.splice(index, 1);
}
}
});
}
function createDustParticles(x, y) {
for (var i = 0; i < 10; i++) {
var particle = new DustParticle();
particle.x = x + (Math.random() - 0.5) * 40;
particle.y = y + (Math.random() - 0.5) * 40;
game.addChild(particle);
dustParticles.push(particle);
}
}
function createCoinBurst(x, y, count) {
for (var i = 0; i < count; i++) {
createPopupText(x + (Math.random() - 0.5) * 140, y + (Math.random() - 0.5) * 60, '+', COLORS.primary, 24);
}
}
function resolveCurrentTicket(result) {
if (!currentTicket) {
return;
}
var wasNearMiss = !result.win && result.count === 2;
if (wasNearMiss) {
gameState.statsNearMisses++;
createPopupText(currentTicket.x, currentTicket.y - 170, 'NEAR MISS!', COLORS.warning, 30);
}
if (result.win) {
safePlaySound('winSound');
showFlash(0xffd700, 0.13);
var basePayouts = [10, 25, 50];
var matchBonus = 1 + (result.count - 3) * 0.35;
var payoutUpgrade = 1 + gameState.upgrades[1] * 0.2;
var comboBoost = 1 + gameState.streak * (0.1 + gameState.upgrades[3] * 0.02);
var prestigeBoost = getPrestigeMultiplier();
var modifierBoost = result.modifier ? result.modifier.payoutMult : 1;
if (result.modifier && result.modifier.id === 'hot') {
modifierBoost += gameState.streak * 0.05;
}
var finalPayout = Math.floor(basePayouts[result.ticketType] * matchBonus * payoutUpgrade * comboBoost * prestigeBoost * modifierBoost);
gameState.coins += finalPayout;
gameState.totalCoins += finalPayout;
gameState.streak++;
gameState.statsTotalWins++;
if (result.modifier && result.modifier.id !== 'none') {
gameState.statsModifierWins++;
}
if (gameState.streak > gameState.bestStreak) {
gameState.bestStreak = gameState.streak;
}
if (finalPayout > gameState.biggestWin) {
gameState.biggestWin = finalPayout;
}
if (gameState.unlockJackpotSystem) {
var jackpotGain = 1 + gameState.upgrades[4] + Math.floor(gameState.prestigeChips * 0.1);
if (result.modifier) {
jackpotGain += result.modifier.jackpotBonus || 0;
}
gameState.jackpotMeter += jackpotGain;
if (gameState.jackpotMeter >= gameState.jackpotMax) {
gameState.jackpotMeter = 0;
gameState.jackpotsHit++;
var jackpotReward = Math.floor((100 + gameState.jackpotsHit * 25) * getPrestigeMultiplier());
gameState.coins += jackpotReward;
gameState.totalCoins += jackpotReward;
createPopupText(currentTicket.x, currentTicket.y - 220, 'JACKPOT BONUS ' + formatMoney(jackpotReward), COLORS.success, 40);
createCoinBurst(currentTicket.x, currentTicket.y - 50, 14);
showFlash(0x00ff88, 0.24);
}
}
createPopupText(currentTicket.x, currentTicket.y, '+' + formatMoney(finalPayout), COLORS.primary, 44);
createCoinBurst(currentTicket.x, currentTicket.y + 55, 10);
} else {
gameState.streak = 0;
gameState.statsTotalLosses++;
createPopupText(currentTicket.x, currentTicket.y, 'NO MATCH', COLORS.danger, 34);
}
checkMilestones();
evaluateAchievements();
updateUI();
saveProgress();
checkForGameOver();
}
function checkForGameOver() {
if (gameState.isGameOver) {
return;
}
var hasActiveTicket = currentTicket && !currentTicket.resolved;
if (gameState.coins <= 0 && !hasActiveTicket) {
showGameOver();
}
}
/****
* Boot
****/
initUI();
updateUI();
safePlayMusic('bgMusic', {
loop: true
});
/****
* Input
****/
game.down = function (x, y, obj) {
if (gameState.isGameOver) {
if (gameOverRestartButton) {
if (pointInRect(x, y, gameOverRestartButton.x, gameOverRestartButton.y, gameOverRestartButton.hitWidth, gameOverRestartButton.hitHeight)) {
gameOverRestartButton.press();
return;
}
}
return;
}
for (var i = 0; i < ticketBuyButtons.length; i++) {
var btnX = bottomPanel.x + ticketBuyButtons[i].x;
var btnY = bottomPanel.y + ticketBuyButtons[i].y;
if (pointInRect(x, y, btnX, btnY, ticketBuyButtons[i].hitWidth, ticketBuyButtons[i].hitHeight)) {
ticketBuyButtons[i].press();
return;
}
}
for (var j = 0; j < upgradeCards.length; j++) {
if (upgradeCards[j].containsGlobalPoint(x, y)) {
upgradeCards[j].press();
return;
}
}
if (prestigeButton) {
var pX = rightPanel.x + prestigeButton.x;
var pY = rightPanel.y + prestigeButton.y;
if (pointInRect(x, y, pX, pY, prestigeButton.hitWidth, prestigeButton.hitHeight)) {
prestigeButton.press();
return;
}
}
if (restartRunButton) {
var rrX = rightPanel.x + restartRunButton.x;
var rrY = rightPanel.y + restartRunButton.y;
if (pointInRect(x, y, rrX, rrY, restartRunButton.hitWidth, restartRunButton.hitHeight)) {
restartRunButton.press();
return;
}
}
if (wipeSaveButton) {
var wsX = rightPanel.x + wipeSaveButton.x;
var wsY = rightPanel.y + wipeSaveButton.y;
if (pointInRect(x, y, wsX, wsY, wipeSaveButton.hitWidth, wipeSaveButton.hitHeight)) {
wipeSaveButton.press();
return;
}
}
if (currentTicket && !currentTicket.resolved && currentTicket.containsGlobalPoint(x, y)) {
dragData.active = true;
dragData.lastX = x;
dragData.lastY = y;
}
};
game.move = function (x, y, obj) {
if (gameState.isGameOver) {
return;
}
for (var i = 0; i < ticketBuyButtons.length; i++) {
var btnGX = bottomPanel.x + ticketBuyButtons[i].x;
var btnGY = bottomPanel.y + ticketBuyButtons[i].y;
ticketBuyButtons[i].setHovered(pointInRect(x, y, btnGX, btnGY, ticketBuyButtons[i].hitWidth, ticketBuyButtons[i].hitHeight));
}
for (var j = 0; j < upgradeCards.length; j++) {
upgradeCards[j].setHovered(upgradeCards[j].containsGlobalPoint(x, y));
}
if (prestigeButton) {
var pX = rightPanel.x + prestigeButton.x;
var pY = rightPanel.y + prestigeButton.y;
prestigeButton.setHovered(pointInRect(x, y, pX, pY, prestigeButton.hitWidth, prestigeButton.hitHeight));
}
if (restartRunButton) {
var rrX = rightPanel.x + restartRunButton.x;
var rrY = rightPanel.y + restartRunButton.y;
restartRunButton.setHovered(pointInRect(x, y, rrX, rrY, restartRunButton.hitWidth, restartRunButton.hitHeight));
}
if (wipeSaveButton) {
var wsX = rightPanel.x + wipeSaveButton.x;
var wsY = rightPanel.y + wipeSaveButton.y;
wipeSaveButton.setHovered(pointInRect(x, y, wsX, wsY, wipeSaveButton.hitWidth, wipeSaveButton.hitHeight));
}
if (!dragData.active || !currentTicket || currentTicket.resolved) {
return;
}
if (!currentTicket.containsGlobalPoint(x, y)) {
return;
}
var dx = x - dragData.lastX;
var dy = y - dragData.lastY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 6) {
var scratchAmount = distance / 520;
currentTicket.updateScratch(scratchAmount);
createDustParticles(x, y);
throttleScratchSound();
dragData.lastX = x;
dragData.lastY = y;
updateUI();
}
};
game.up = function () {
dragData.active = false;
};
/****
* Update
****/
game.update = function () {
for (var i = dustParticles.length - 1; i >= 0; i--) {
if (!dustParticles[i] || !dustParticles[i].parent) {
dustParticles.splice(i, 1);
continue;
}
dustParticles[i].update();
}
if (LK.ticks % 300 === 0) {
saveProgress();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Button = Container.expand(function (styleType) {
var self = Container.call(this);
self.buttonText = '';
self.onClick = function () {};
self.isHovered = false;
self.enabled = true;
self.hitWidth = UI.buttonWidth;
self.hitHeight = styleType === 'small' ? UI.smallButtonHeight : UI.buttonHeight;
self.styleType = styleType || 'normal';
var assetName = self.styleType === 'small' ? 'smallButtonBg' : 'buttonBg';
var bg = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
var text = new Text2('', {
size: self.styleType === 'small' ? 24 : 28,
fill: COLORS.white,
align: 'center'
});
text.anchor.set(0.5, 0.5);
self.addChild(text);
self.setText = function (str) {
self.buttonText = str;
text.setText(str);
};
self.setTint = function (color) {
bg.tint = color;
self.baseTint = color;
};
self.setEnabled = function (enabled) {
self.enabled = enabled;
self.alpha = enabled ? 1 : 0.45;
if (!enabled) {
bg.tint = COLORS.buttonDisabled;
} else {
bg.tint = self.isHovered ? COLORS.buttonHover : self.baseTint || COLORS.button;
}
};
self.setHovered = function (hovered) {
self.isHovered = hovered;
if (!self.enabled) {
bg.tint = COLORS.buttonDisabled;
return;
}
bg.tint = hovered ? COLORS.buttonHover : self.baseTint || COLORS.button;
};
self.press = function () {
if (!self.enabled) {
return;
}
safePlaySound('buttonClickSound');
self.scale.set(0.98);
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeOut
});
self.onClick();
};
self.setTint(COLORS.button);
return self;
});
var DustParticle = Container.expand(function () {
var self = Container.call(this);
var dust = self.attachAsset('dustParticle', {
anchorX: 0.5,
anchorY: 0.5
});
dust.alpha = 0.82;
self.velocityX = (Math.random() - 0.5) * 6;
self.velocityY = (Math.random() - 0.5) * 6;
self.gravity = 0.08;
self.life = 0.45;
self.maxLife = 0.45;
self.update = function () {
self.velocityY += self.gravity;
self.x += self.velocityX;
self.y += self.velocityY;
self.life -= 1 / 60;
self.scale.set(1 + (1 - self.life / self.maxLife) * 0.6);
dust.alpha = 0.82 * (self.life / self.maxLife);
if (self.life <= 0) {
if (self.parent) {
self.parent.removeChild(self);
}
self.destroy();
}
};
return self;
});
var ScratchTicket = Container.expand(function (ticketType) {
var self = Container.call(this);
self.ticketType = ticketType || 0;
self.symbols = [];
self.scratchAmount = 0;
self.resolved = false;
self.canScratch = true;
self.ticketWidth = UI.ticketWidth;
self.ticketHeight = UI.ticketHeight;
self.modifier = rollTicketModifier(ticketType);
var scratchSpeedBonus = gameState.upgrades[2] * 0.03;
var prestigeScratchBonus = gameState.prestigeChips * 0.003;
self.autoResolveThreshold = clamp(0.56 - scratchSpeedBonus - prestigeScratchBonus, 0.22, 0.56);
var bg = self.attachAsset('ticketBg', {
anchorX: 0.5,
anchorY: 0.5
});
bg.tint = getTicketTint(self.ticketType);
var overlay = self.attachAsset('scratchOverlay', {
anchorX: 0.5,
anchorY: 0.5
});
overlay.alpha = 0.95;
var titleText = new Text2(getTicketName(self.ticketType), {
size: 38,
fill: COLORS.white,
align: 'center'
});
titleText.anchor.set(0.5, 0);
titleText.y = -500;
self.addChild(titleText);
var infoText = new Text2('Scratch to reveal 3 matching symbols', {
size: 24,
fill: COLORS.primary,
align: 'center'
});
infoText.anchor.set(0.5, 0);
infoText.y = -450;
self.addChild(infoText);
var modifierLine = new Text2(self.modifier.label, {
size: 26,
fill: self.modifier.color,
align: 'center'
});
modifierLine.anchor.set(0.5, 0);
modifierLine.y = -405;
self.addChild(modifierLine);
var gridStartX = -UI.symbolSpacing;
var gridStartY = -UI.symbolSpacing;
var spacing = UI.symbolSpacing;
for (var row = 0; row < 3; row++) {
for (var col = 0; col < 3; col++) {
var symbol = new _Symbol();
symbol.x = gridStartX + col * spacing;
symbol.y = gridStartY + row * spacing + 70;
self.symbols.push(symbol);
self.addChild(symbol);
}
}
applyModifierToTicket(self);
self.containsGlobalPoint = function (x, y) {
return pointInRect(x, y, self.x, self.y, self.ticketWidth, self.ticketHeight);
};
self.updateScratch = function (amount) {
if (!self.canScratch || self.resolved) {
return;
}
self.scratchAmount = clamp(self.scratchAmount + amount, 0, 1);
overlay.alpha = clamp(0.95 - self.scratchAmount, 0, 0.95);
var revealTarget = Math.floor(self.scratchAmount * self.symbols.length);
var alreadyRevealed = 0;
for (var i = 0; i < self.symbols.length; i++) {
if (self.symbols[i].revealed) {
alreadyRevealed++;
}
}
while (alreadyRevealed < revealTarget) {
var hidden = [];
for (var j = 0; j < self.symbols.length; j++) {
if (!self.symbols[j].revealed) {
hidden.push(self.symbols[j]);
}
}
if (hidden.length <= 0) {
break;
}
var pick = hidden[Math.floor(Math.random() * hidden.length)];
pick.reveal();
alreadyRevealed++;
}
if (self.scratchAmount >= self.autoResolveThreshold) {
self.resolveTicket();
}
};
self.checkMatch = function () {
var counts = {};
for (var i = 0; i < self.symbols.length; i++) {
var type = self.symbols[i].symbolType;
counts[type] = (counts[type] || 0) + 1;
}
var bestType = null;
var bestCount = 0;
for (var key in counts) {
if (counts[key] > bestCount) {
bestType = key;
bestCount = counts[key];
}
}
return {
win: bestCount >= 3,
type: bestType,
count: bestCount
};
};
self.revealAll = function () {
for (var i = 0; i < self.symbols.length; i++) {
self.symbols[i].reveal();
}
overlay.alpha = 0;
};
self.resolveTicket = function () {
if (self.resolved) {
return;
}
self.resolved = true;
self.canScratch = false;
self.revealAll();
var result = self.checkMatch();
result.modifier = self.modifier;
result.ticketType = self.ticketType;
resolveCurrentTicket(result);
};
return self;
});
/****
* Modifiers
****/
var UpgradeCard = Container.expand(function () {
var self = Container.call(this);
self.upgradeId = 0;
self.hitWidth = UI.buttonWidth;
self.hitHeight = UI.upgradeCardHeight;
self.onClick = function () {};
var bg = self.attachAsset('upgradeCardBg', {
anchorX: 0.5,
anchorY: 0.5
});
var nameText = new Text2('', {
size: 24,
fill: COLORS.white,
align: 'center'
});
nameText.anchor.set(0.5, 0);
nameText.y = -28;
self.addChild(nameText);
var descText = new Text2('', {
size: 16,
fill: COLORS.muted,
align: 'center'
});
descText.anchor.set(0.5, 0);
descText.y = 2;
self.addChild(descText);
var costText = new Text2('', {
size: 20,
fill: COLORS.gold,
align: 'center'
});
costText.anchor.set(0.5, 0);
costText.y = 34;
self.addChild(costText);
self.setUpgrade = function (id, level, cost, affordable) {
self.upgradeId = id;
var names = ['Better Odds', 'Bigger Payouts', 'Faster Scratch', 'Combo Boost', 'Jackpot Gain', 'Coin Magnet'];
var descs = ['More good modifier rolls', 'Higher match payouts', 'Resolve cards faster', 'Bigger streak scaling', 'Fill jackpot quicker', 'Extra economy support'];
nameText.setText(names[id] + ' Lv.' + (level + 1));
descText.setText(descs[id]);
costText.setText('BUY ' + formatMoney(cost));
self.alpha = affordable ? 1 : 0.52;
};
self.setHovered = function (hovered) {
bg.tint = hovered ? 0x2c5c8f : 0x1f3b60;
};
self.containsGlobalPoint = function (x, y) {
var gx = rightPanel.x + self.x;
var gy = rightPanel.y + self.y;
return pointInRect(x, y, gx, gy, self.hitWidth, self.hitHeight);
};
self.press = function () {
safePlaySound('buttonClickSound');
self.scale.set(0.985);
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeOut
});
self.onClick();
};
return self;
});
var _Symbol = Container.expand(function () {
var self = Container.call(this);
self.symbolType = 0;
self.revealed = false;
var bg = self.attachAsset('symbolBg', {
anchorX: 0.5,
anchorY: 0.5
});
var symbolText = new Text2('', {
size: 74,
fill: COLORS.white,
align: 'center'
});
symbolText.anchor.set(0.5, 0.5);
symbolText.alpha = 0.22;
self.addChild(symbolText);
self.setSymbol = function (type) {
self.symbolType = type;
var symbols = ['🍀', '💎', '🎁', '⭐', '🔥', '🎰'];
symbolText.setText(symbols[type % symbols.length]);
};
self.highlight = function (color) {
bg.tint = color || 0x8b7a31;
};
self.reveal = function () {
if (self.revealed) {
return;
}
self.revealed = true;
bg.tint = 0x4c5f82;
symbolText.alpha = 1;
safePlaySound('revealSound');
};
self.setSymbol(Math.floor(Math.random() * 6));
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x081018
});
/****
* Game Code
****/
/****
* UI SCALE + LAYOUT
****/
/****
* LUCKY SCRATCH TYCOON
* Larger, cleaner, more polished Upit version
****/
/****
* Runtime State
****/
var SCREEN_W = 2048;
var SCREEN_H = 2732;
var CENTER_X = 1024;
var CENTER_Y = 1366;
var UI = {
panelWidth: 430,
panelHeight: 1040,
panelCornerOffset: 90,
buttonWidth: 560,
buttonHeight: 92,
smallButtonHeight: 84,
upgradeCardHeight: 108,
meterWidth: 320,
meterHeight: 26,
ticketWidth: 860,
ticketHeight: 1120,
symbolSize: 180,
symbolSpacing: 220,
leftPanelX: 70,
leftPanelY: 180,
rightPanelX: 1978,
rightPanelY: 180,
bottomPanelY: 2520,
titleY: 54,
modifierY: 170,
ticketY: 1320,
titleSize: 56,
sectionLabelSize: 28,
bigValueSize: 54,
mediumValueSize: 34,
bodySize: 24,
smallSize: 20,
tinySize: 17,
popupSize: 42
};
/****
* COLORS
****/
var COLORS = {
bg: 0x081018,
panel: 0x162235,
panelAlt: 0x1b2a40,
button: 0x255c99,
buttonHover: 0x3a78bf,
buttonDisabled: 0x3a4558,
primary: '#ffd76a',
white: '#ffffff',
soft: '#c9d3e3',
muted: '#94a3b8',
success: '#44e39a',
warning: '#ffb25f',
danger: '#ff6f7d',
cyan: '#69d2ff',
gold: '#ffe07a',
purple: '#b48cff'
};
/****
* Helpers
****/
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function safePlaySound(name) {
try {
var s = LK.getSound(name);
if (s && s.play) {
s.play();
}
} catch (e) {}
}
function safePlayMusic(name, options) {
try {
LK.playMusic(name, options || {});
} catch (e) {}
}
function formatMoney(value) {
return '$' + Math.floor(value);
}
function pointInRect(px, py, cx, cy, width, height) {
return px >= cx - width * 0.5 && px <= cx + width * 0.5 && py >= cy - height * 0.5 && py <= cy + height * 0.5;
}
function getUpgradeCost(id, level) {
var baseCosts = [50, 75, 60, 80, 100, 70];
return Math.floor(baseCosts[id] * Math.pow(1.32, level));
}
function getTicketName(type) {
if (type === 1) {
return 'LUCKY TICKET';
}
if (type === 2) {
return 'GOLD TICKET';
}
return 'BASIC TICKET';
}
function getTicketTint(type) {
if (type === 1) {
return 0x183b31;
}
if (type === 2) {
return 0x4a3818;
}
return 0x28344e;
}
function getTicketCost(type) {
return [5, 15, 50][type] || 5;
}
function canBuyTicket(type) {
if (type === 1 && !gameState.unlockLuckyTicket) {
return false;
}
if (type === 2 && !gameState.unlockGoldTicket) {
return false;
}
return true;
}
function getPrestigeReward() {
return Math.floor(Math.sqrt(gameState.totalCoins / 500));
}
function getPrestigeMultiplier() {
return 1 + gameState.prestigeChips * 0.05;
}
function getCurrentAchievementsCount() {
var count = 0;
if (gameState.achievementFirstWin) {
count++;
}
if (gameState.achievementLuckyUnlocked) {
count++;
}
if (gameState.achievementGoldUnlocked) {
count++;
}
if (gameState.achievementFirstJackpot) {
count++;
}
if (gameState.achievementStreak5) {
count++;
}
if (gameState.achievementStreak10) {
count++;
}
if (gameState.achievementFirstPrestige) {
count++;
}
if (gameState.achievementTickets50) {
count++;
}
if (gameState.achievementTickets200) {
count++;
}
if (gameState.achievementBigWin100) {
count++;
}
if (gameState.achievementModifierWin) {
count++;
}
if (gameState.achievementWealthy1000) {
count++;
}
return count;
}
function throttleScratchSound() {
if (LK.ticks - gameState.lastScratchSoundTick > 3) {
gameState.lastScratchSoundTick = LK.ticks;
safePlaySound('scratchSound');
}
}
function showFlash(color, alpha) {
if (!flashOverlay) {
return;
}
flashOverlay.tint = color || 0xffffff;
flashOverlay.alpha = alpha || 0.3;
tween(flashOverlay, {
alpha: 0
}, {
duration: 260,
easing: tween.easeOut
});
}
/****
* Storage Defaults - Flat Keys Only
****/
if (storage.coins === undefined) {
storage.coins = 100;
}
if (storage.totalCoins === undefined) {
storage.totalCoins = 100;
}
if (storage.bestStreak === undefined) {
storage.bestStreak = 0;
}
if (storage.jackpotMeter === undefined) {
storage.jackpotMeter = 0;
}
if (storage.ticketsScratchedTotal === undefined) {
storage.ticketsScratchedTotal = 0;
}
if (storage.biggestWin === undefined) {
storage.biggestWin = 0;
}
if (storage.jackpotsHit === undefined) {
storage.jackpotsHit = 0;
}
if (storage.prestigeCount === undefined) {
storage.prestigeCount = 0;
}
if (storage.prestigeChips === undefined) {
storage.prestigeChips = 0;
}
if (storage.upgrade0 === undefined) {
storage.upgrade0 = 0;
}
if (storage.upgrade1 === undefined) {
storage.upgrade1 = 0;
}
if (storage.upgrade2 === undefined) {
storage.upgrade2 = 0;
}
if (storage.upgrade3 === undefined) {
storage.upgrade3 = 0;
}
if (storage.upgrade4 === undefined) {
storage.upgrade4 = 0;
}
if (storage.upgrade5 === undefined) {
storage.upgrade5 = 0;
}
if (storage.unlockLuckyTicket === undefined) {
storage.unlockLuckyTicket = false;
}
if (storage.unlockGoldTicket === undefined) {
storage.unlockGoldTicket = false;
}
if (storage.unlockJackpotSystem === undefined) {
storage.unlockJackpotSystem = false;
}
if (storage.unlockModifiers === undefined) {
storage.unlockModifiers = false;
}
if (storage.unlockPrestige === undefined) {
storage.unlockPrestige = false;
}
if (storage.statsTotalWins === undefined) {
storage.statsTotalWins = 0;
}
if (storage.statsTotalLosses === undefined) {
storage.statsTotalLosses = 0;
}
if (storage.statsNearMisses === undefined) {
storage.statsNearMisses = 0;
}
if (storage.statsCardsBasic === undefined) {
storage.statsCardsBasic = 0;
}
if (storage.statsCardsLucky === undefined) {
storage.statsCardsLucky = 0;
}
if (storage.statsCardsGold === undefined) {
storage.statsCardsGold = 0;
}
if (storage.statsModifierWins === undefined) {
storage.statsModifierWins = 0;
}
if (storage.statsPrestigeEarnedLifetime === undefined) {
storage.statsPrestigeEarnedLifetime = 0;
}
if (storage.achievementFirstWin === undefined) {
storage.achievementFirstWin = false;
}
if (storage.achievementLuckyUnlocked === undefined) {
storage.achievementLuckyUnlocked = false;
}
if (storage.achievementGoldUnlocked === undefined) {
storage.achievementGoldUnlocked = false;
}
if (storage.achievementFirstJackpot === undefined) {
storage.achievementFirstJackpot = false;
}
if (storage.achievementStreak5 === undefined) {
storage.achievementStreak5 = false;
}
if (storage.achievementStreak10 === undefined) {
storage.achievementStreak10 = false;
}
if (storage.achievementFirstPrestige === undefined) {
storage.achievementFirstPrestige = false;
}
if (storage.achievementTickets50 === undefined) {
storage.achievementTickets50 = false;
}
if (storage.achievementTickets200 === undefined) {
storage.achievementTickets200 = false;
}
if (storage.achievementBigWin100 === undefined) {
storage.achievementBigWin100 = false;
}
if (storage.achievementModifierWin === undefined) {
storage.achievementModifierWin = false;
}
if (storage.achievementWealthy1000 === undefined) {
storage.achievementWealthy1000 = false;
}
var gameState = {
coins: storage.coins,
totalCoins: storage.totalCoins,
streak: 0,
bestStreak: storage.bestStreak,
jackpotMeter: storage.jackpotMeter,
jackpotMax: 100,
ticketsScratchedTotal: storage.ticketsScratchedTotal,
biggestWin: storage.biggestWin,
jackpotsHit: storage.jackpotsHit,
prestigeCount: storage.prestigeCount,
prestigeChips: storage.prestigeChips,
upgrades: [storage.upgrade0, storage.upgrade1, storage.upgrade2, storage.upgrade3, storage.upgrade4, storage.upgrade5],
unlockLuckyTicket: storage.unlockLuckyTicket,
unlockGoldTicket: storage.unlockGoldTicket,
unlockJackpotSystem: storage.unlockJackpotSystem,
unlockModifiers: storage.unlockModifiers,
unlockPrestige: storage.unlockPrestige,
statsTotalWins: storage.statsTotalWins,
statsTotalLosses: storage.statsTotalLosses,
statsNearMisses: storage.statsNearMisses,
statsCardsBasic: storage.statsCardsBasic,
statsCardsLucky: storage.statsCardsLucky,
statsCardsGold: storage.statsCardsGold,
statsModifierWins: storage.statsModifierWins,
statsPrestigeEarnedLifetime: storage.statsPrestigeEarnedLifetime,
achievementFirstWin: storage.achievementFirstWin,
achievementLuckyUnlocked: storage.achievementLuckyUnlocked,
achievementGoldUnlocked: storage.achievementGoldUnlocked,
achievementFirstJackpot: storage.achievementFirstJackpot,
achievementStreak5: storage.achievementStreak5,
achievementStreak10: storage.achievementStreak10,
achievementFirstPrestige: storage.achievementFirstPrestige,
achievementTickets50: storage.achievementTickets50,
achievementTickets200: storage.achievementTickets200,
achievementBigWin100: storage.achievementBigWin100,
achievementModifierWin: storage.achievementModifierWin,
achievementWealthy1000: storage.achievementWealthy1000,
lastScratchSoundTick: -999,
isGameOver: false
};
var currentTicket = null;
var flashOverlay = null;
var gameOverOverlay = null;
var gameOverRestartButton = null;
var dragData = {
active: false,
lastX: 0,
lastY: 0
};
var dustParticles = [];
var popupTexts = [];
var leftPanel, rightPanel, bottomPanel, centerInfoPanel;
var coinText, streakText, jackpotBar, jackpotText, statsText, modifierText, unlockText, achievementText, prestigeText;
var ticketBuyButtons = [];
var upgradeCards = [];
var prestigeButton;
var restartRunButton;
var wipeSaveButton;
/****
* Modifiers
****/
function rollTicketModifier(ticketType) {
if (!gameState.unlockModifiers) {
return {
id: 'none',
label: 'Standard Ticket',
color: COLORS.soft,
payoutMult: 1,
jackpotBonus: 0
};
}
var pool = [{
id: 'none',
label: 'Standard Ticket',
color: COLORS.soft,
payoutMult: 1,
jackpotBonus: 0
}, {
id: 'double',
label: 'Double Payout',
color: COLORS.success,
payoutMult: 2,
jackpotBonus: 0
}, {
id: 'jackpot',
label: 'Double Jackpot Gain',
color: COLORS.gold,
payoutMult: 1,
jackpotBonus: 2
}, {
id: 'wild',
label: 'Lucky Wild Card',
color: COLORS.cyan,
payoutMult: 1.35,
jackpotBonus: 0
}, {
id: 'hot',
label: 'Hot Streak Boost',
color: COLORS.warning,
payoutMult: 1.5,
jackpotBonus: 0
}];
var oddsBoost = gameState.upgrades[0] * 0.02 + gameState.prestigeChips * 0.005;
var roll = Math.random();
if (roll < 0.35 - oddsBoost) {
return pool[0];
}
if (roll < 0.55) {
return pool[1];
}
if (roll < 0.72) {
return pool[2];
}
if (roll < 0.87) {
return pool[3];
}
return pool[4];
}
function applyModifierToTicket(ticket) {
if (!ticket || !ticket.modifier) {
return;
}
if (ticket.modifier.id === 'wild') {
var wildIndex = Math.floor(Math.random() * ticket.symbols.length);
var baseType = ticket.symbols[Math.floor(Math.random() * ticket.symbols.length)].symbolType;
ticket.symbols[wildIndex].setSymbol(baseType);
ticket.symbols[wildIndex].highlight(0x2d7697);
}
}
/****
* Achievements / Milestones
****/
function unlockAchievement(key, label) {
if (gameState[key]) {
return;
}
gameState[key] = true;
createPopupText(CENTER_X, 920, 'ACHIEVEMENT: ' + label, COLORS.primary, 30);
showFlash(0xffd700, 0.18);
}
function checkMilestones() {
if (!gameState.unlockLuckyTicket && gameState.ticketsScratchedTotal >= 10) {
gameState.unlockLuckyTicket = true;
unlockAchievement('achievementLuckyUnlocked', 'Lucky Tickets Unlocked!');
createPopupText(CENTER_X, 680, 'LUCKY TICKETS UNLOCKED', COLORS.success, 44);
}
if (!gameState.unlockGoldTicket && gameState.totalCoins >= 1000) {
gameState.unlockGoldTicket = true;
unlockAchievement('achievementGoldUnlocked', 'Gold Tickets Unlocked!');
createPopupText(CENTER_X, 740, 'GOLD TICKETS UNLOCKED', COLORS.primary, 44);
}
if (!gameState.unlockJackpotSystem && gameState.ticketsScratchedTotal >= 15) {
gameState.unlockJackpotSystem = true;
createPopupText(CENTER_X, 800, 'JACKPOT SYSTEM UNLOCKED', COLORS.cyan, 34);
}
if (!gameState.unlockModifiers && gameState.ticketsScratchedTotal >= 25) {
gameState.unlockModifiers = true;
createPopupText(CENTER_X, 860, 'TICKET MODIFIERS UNLOCKED', COLORS.warning, 34);
}
if (!gameState.unlockPrestige && gameState.totalCoins >= 5000) {
gameState.unlockPrestige = true;
createPopupText(CENTER_X, 920, 'PRESTIGE UNLOCKED', COLORS.white, 34);
}
}
function evaluateAchievements() {
if (gameState.statsTotalWins >= 1) {
unlockAchievement('achievementFirstWin', 'First Win');
}
if (gameState.jackpotsHit >= 1) {
unlockAchievement('achievementFirstJackpot', 'First Jackpot');
}
if (gameState.bestStreak >= 5) {
unlockAchievement('achievementStreak5', '5 Win Streak');
}
if (gameState.bestStreak >= 10) {
unlockAchievement('achievementStreak10', '10 Win Streak');
}
if (gameState.ticketsScratchedTotal >= 50) {
unlockAchievement('achievementTickets50', '50 Tickets Scratched');
}
if (gameState.ticketsScratchedTotal >= 200) {
unlockAchievement('achievementTickets200', '200 Tickets Scratched');
}
if (gameState.biggestWin >= 100) {
unlockAchievement('achievementBigWin100', 'Big Win 100+');
}
if (gameState.statsModifierWins >= 1) {
unlockAchievement('achievementModifierWin', 'Modifier Master');
}
if (gameState.totalCoins >= 1000) {
unlockAchievement('achievementWealthy1000', 'Earned 1000 Coins');
}
if (gameState.prestigeCount >= 1) {
unlockAchievement('achievementFirstPrestige', 'First Prestige');
}
}
/****
* Reset / Save
****/
function restartRunOnly() {
gameState.coins = 100 + gameState.prestigeChips * 10;
gameState.streak = 0;
gameState.jackpotMeter = 0;
gameState.upgrades = [0, 0, 0, 0, 0, 0];
gameState.isGameOver = false;
if (currentTicket) {
if (currentTicket.parent) {
currentTicket.parent.removeChild(currentTicket);
}
currentTicket.destroy();
currentTicket = null;
}
hideGameOver();
createPopupText(CENTER_X, 1040, 'RUN RESTARTED', COLORS.white, 42);
updateUI();
saveProgress();
}
function hardRestartGame() {
gameState.coins = 100;
gameState.totalCoins = 100;
gameState.streak = 0;
gameState.bestStreak = 0;
gameState.jackpotMeter = 0;
gameState.ticketsScratchedTotal = 0;
gameState.biggestWin = 0;
gameState.jackpotsHit = 0;
gameState.prestigeCount = 0;
gameState.prestigeChips = 0;
gameState.upgrades = [0, 0, 0, 0, 0, 0];
gameState.unlockLuckyTicket = false;
gameState.unlockGoldTicket = false;
gameState.unlockJackpotSystem = false;
gameState.unlockModifiers = false;
gameState.unlockPrestige = false;
gameState.statsTotalWins = 0;
gameState.statsTotalLosses = 0;
gameState.statsNearMisses = 0;
gameState.statsCardsBasic = 0;
gameState.statsCardsLucky = 0;
gameState.statsCardsGold = 0;
gameState.statsModifierWins = 0;
gameState.statsPrestigeEarnedLifetime = 0;
gameState.achievementFirstWin = false;
gameState.achievementLuckyUnlocked = false;
gameState.achievementGoldUnlocked = false;
gameState.achievementFirstJackpot = false;
gameState.achievementStreak5 = false;
gameState.achievementStreak10 = false;
gameState.achievementFirstPrestige = false;
gameState.achievementTickets50 = false;
gameState.achievementTickets200 = false;
gameState.achievementBigWin100 = false;
gameState.achievementModifierWin = false;
gameState.achievementWealthy1000 = false;
gameState.isGameOver = false;
if (currentTicket) {
if (currentTicket.parent) {
currentTicket.parent.removeChild(currentTicket);
}
currentTicket.destroy();
currentTicket = null;
}
hideGameOver();
createPopupText(CENTER_X, 1040, 'SAVE WIPED', COLORS.danger, 42);
updateUI();
saveProgress();
}
function resetRunForPrestige() {
var reward = getPrestigeReward();
if (!gameState.unlockPrestige || reward <= 0) {
return;
}
gameState.prestigeCount++;
gameState.prestigeChips += reward;
gameState.statsPrestigeEarnedLifetime += reward;
gameState.coins = 100 + reward * 10;
gameState.streak = 0;
gameState.jackpotMeter = 0;
gameState.upgrades = [0, 0, 0, 0, 0, 0];
gameState.isGameOver = false;
if (currentTicket) {
if (currentTicket.parent) {
currentTicket.parent.removeChild(currentTicket);
}
currentTicket.destroy();
currentTicket = null;
}
hideGameOver();
createPopupText(CENTER_X, 980, 'PRESTIGE +' + reward + ' CHIPS', COLORS.white, 46);
showFlash(0xffffff, 0.32);
evaluateAchievements();
updateUI();
saveProgress();
}
function saveProgress() {
storage.coins = gameState.coins;
storage.totalCoins = gameState.totalCoins;
storage.bestStreak = gameState.bestStreak;
storage.jackpotMeter = gameState.jackpotMeter;
storage.ticketsScratchedTotal = gameState.ticketsScratchedTotal;
storage.biggestWin = gameState.biggestWin;
storage.jackpotsHit = gameState.jackpotsHit;
storage.prestigeCount = gameState.prestigeCount;
storage.prestigeChips = gameState.prestigeChips;
storage.upgrade0 = gameState.upgrades[0];
storage.upgrade1 = gameState.upgrades[1];
storage.upgrade2 = gameState.upgrades[2];
storage.upgrade3 = gameState.upgrades[3];
storage.upgrade4 = gameState.upgrades[4];
storage.upgrade5 = gameState.upgrades[5];
storage.unlockLuckyTicket = gameState.unlockLuckyTicket;
storage.unlockGoldTicket = gameState.unlockGoldTicket;
storage.unlockJackpotSystem = gameState.unlockJackpotSystem;
storage.unlockModifiers = gameState.unlockModifiers;
storage.unlockPrestige = gameState.unlockPrestige;
storage.statsTotalWins = gameState.statsTotalWins;
storage.statsTotalLosses = gameState.statsTotalLosses;
storage.statsNearMisses = gameState.statsNearMisses;
storage.statsCardsBasic = gameState.statsCardsBasic;
storage.statsCardsLucky = gameState.statsCardsLucky;
storage.statsCardsGold = gameState.statsCardsGold;
storage.statsModifierWins = gameState.statsModifierWins;
storage.statsPrestigeEarnedLifetime = gameState.statsPrestigeEarnedLifetime;
storage.achievementFirstWin = gameState.achievementFirstWin;
storage.achievementLuckyUnlocked = gameState.achievementLuckyUnlocked;
storage.achievementGoldUnlocked = gameState.achievementGoldUnlocked;
storage.achievementFirstJackpot = gameState.achievementFirstJackpot;
storage.achievementStreak5 = gameState.achievementStreak5;
storage.achievementStreak10 = gameState.achievementStreak10;
storage.achievementFirstPrestige = gameState.achievementFirstPrestige;
storage.achievementTickets50 = gameState.achievementTickets50;
storage.achievementTickets200 = gameState.achievementTickets200;
storage.achievementBigWin100 = gameState.achievementBigWin100;
storage.achievementModifierWin = gameState.achievementModifierWin;
storage.achievementWealthy1000 = gameState.achievementWealthy1000;
}
/****
* UI
****/
function initUI() {
leftPanel = new Container();
leftPanel.x = UI.leftPanelX;
leftPanel.y = UI.leftPanelY;
game.addChild(leftPanel);
var leftBg = LK.getAsset('panelBg', {
anchorX: 0,
anchorY: 0
});
leftPanel.addChild(leftBg);
var leftHeader = new Text2('PLAYER STATS', {
size: UI.sectionLabelSize,
fill: COLORS.primary
});
leftHeader.anchor.set(0.5, 0);
leftHeader.x = UI.panelWidth * 0.5;
leftHeader.y = 26;
leftPanel.addChild(leftHeader);
var coinsLabelText = new Text2('COINS', {
size: UI.smallSize,
fill: COLORS.primary
});
coinsLabelText.anchor.set(0.5, 0);
coinsLabelText.x = UI.panelWidth * 0.5;
coinsLabelText.y = 92;
leftPanel.addChild(coinsLabelText);
coinText = new Text2('$100', {
size: UI.bigValueSize,
fill: COLORS.white
});
coinText.anchor.set(0.5, 0);
coinText.x = UI.panelWidth * 0.5;
coinText.y = 126;
leftPanel.addChild(coinText);
var streakLabelText = new Text2('STREAK', {
size: UI.smallSize,
fill: COLORS.danger
});
streakLabelText.anchor.set(0.5, 0);
streakLabelText.x = UI.panelWidth * 0.5;
streakLabelText.y = 212;
leftPanel.addChild(streakLabelText);
streakText = new Text2('x0', {
size: UI.mediumValueSize,
fill: COLORS.white
});
streakText.anchor.set(0.5, 0);
streakText.x = UI.panelWidth * 0.5;
streakText.y = 246;
leftPanel.addChild(streakText);
var jackpotLabelText = new Text2('JACKPOT METER', {
size: UI.smallSize,
fill: COLORS.primary
});
jackpotLabelText.anchor.set(0.5, 0);
jackpotLabelText.x = UI.panelWidth * 0.5;
jackpotLabelText.y = 328;
leftPanel.addChild(jackpotLabelText);
var jackpotBgBar = LK.getAsset('meterBarEmpty', {
anchorX: 0.5,
anchorY: 0
});
jackpotBgBar.x = UI.panelWidth * 0.5;
jackpotBgBar.y = 366;
leftPanel.addChild(jackpotBgBar);
jackpotBar = LK.getAsset('meterBarFull', {
anchorX: 0.5,
anchorY: 0
});
jackpotBar.x = UI.panelWidth * 0.5;
jackpotBar.y = 366;
jackpotBar.scaleX = 0;
leftPanel.addChild(jackpotBar);
jackpotText = new Text2('0 / 100', {
size: UI.smallSize,
fill: COLORS.white
});
jackpotText.anchor.set(0.5, 0);
jackpotText.x = UI.panelWidth * 0.5;
jackpotText.y = 402;
leftPanel.addChild(jackpotText);
prestigeText = new Text2('', {
size: UI.bodySize,
fill: COLORS.cyan,
align: 'center'
});
prestigeText.anchor.set(0.5, 0);
prestigeText.x = UI.panelWidth * 0.5;
prestigeText.y = 468;
leftPanel.addChild(prestigeText);
statsText = new Text2('', {
size: UI.smallSize,
fill: COLORS.soft,
align: 'center'
});
statsText.anchor.set(0.5, 0);
statsText.x = UI.panelWidth * 0.5;
statsText.y = 622;
leftPanel.addChild(statsText);
unlockText = new Text2('', {
size: UI.smallSize,
fill: COLORS.success,
align: 'center'
});
unlockText.anchor.set(0.5, 0);
unlockText.x = UI.panelWidth * 0.5;
unlockText.y = 780;
leftPanel.addChild(unlockText);
achievementText = new Text2('', {
size: UI.smallSize,
fill: COLORS.gold,
align: 'center'
});
achievementText.anchor.set(0.5, 0);
achievementText.x = UI.panelWidth * 0.5;
achievementText.y = 910;
leftPanel.addChild(achievementText);
rightPanel = new Container();
rightPanel.x = UI.rightPanelX;
rightPanel.y = UI.rightPanelY;
game.addChild(rightPanel);
var rightBg = LK.getAsset('panelBgAlt', {
anchorX: 1,
anchorY: 0
});
rightPanel.addChild(rightBg);
var shopLabelText = new Text2('UPGRADES', {
size: UI.sectionLabelSize,
fill: COLORS.success
});
shopLabelText.anchor.set(0.5, 0);
shopLabelText.x = -UI.panelWidth * 0.5;
shopLabelText.y = 26;
rightPanel.addChild(shopLabelText);
for (var i = 0; i < 6; i++) {
var card = new UpgradeCard();
card.y = 118 + i * 126;
card.x = -UI.panelWidth * 0.5;
card.onClick = function () {
onUpgradeClick(this.upgradeId);
}.bind(card);
upgradeCards.push(card);
rightPanel.addChild(card);
}
prestigeButton = new Button();
prestigeButton.x = -UI.panelWidth * 0.5;
prestigeButton.y = 878;
prestigeButton.setText('PRESTIGE');
prestigeButton.setTint(0x5b46a3);
prestigeButton.onClick = function () {
resetRunForPrestige();
};
rightPanel.addChild(prestigeButton);
restartRunButton = new Button('small');
restartRunButton.x = -UI.panelWidth * 0.5;
restartRunButton.y = 958;
restartRunButton.setText('RESTART RUN');
restartRunButton.setTint(0x2a6e56);
restartRunButton.onClick = function () {
restartRunOnly();
};
rightPanel.addChild(restartRunButton);
wipeSaveButton = new Button('small');
wipeSaveButton.x = -UI.panelWidth * 0.5;
wipeSaveButton.y = 1026;
wipeSaveButton.setText('WIPE SAVE');
wipeSaveButton.setTint(0x8a3340);
wipeSaveButton.onClick = function () {
hardRestartGame();
};
rightPanel.addChild(wipeSaveButton);
bottomPanel = new Container();
bottomPanel.x = CENTER_X;
bottomPanel.y = UI.bottomPanelY;
game.addChild(bottomPanel);
var ticketTypes = [{
name: 'BASIC\n$5',
cost: 5,
type: 0,
tint: 0x255c99
}, {
name: 'LUCKY\n$15',
cost: 15,
type: 1,
tint: 0x29765c
}, {
name: 'GOLD\n$50',
cost: 50,
type: 2,
tint: 0x977336
}];
for (var t = 0; t < ticketTypes.length; t++) {
var btn = new Button();
btn.x = -420 + t * 420;
btn.y = 0;
btn.setText(ticketTypes[t].name);
btn.setTint(ticketTypes[t].tint);
btn.cost = ticketTypes[t].cost;
btn.type = ticketTypes[t].type;
btn.onClick = function () {
onBuyTicket(this.type, this.cost);
}.bind(btn);
ticketBuyButtons.push(btn);
bottomPanel.addChild(btn);
}
centerInfoPanel = new Container();
centerInfoPanel.x = CENTER_X;
centerInfoPanel.y = UI.modifierY;
game.addChild(centerInfoPanel);
modifierText = new Text2('', {
size: 28,
fill: COLORS.white,
align: 'center'
});
modifierText.anchor.set(0.5, 0);
centerInfoPanel.addChild(modifierText);
var titleText = new Text2('LUCKY SCRATCH TYCOON', {
size: UI.titleSize,
fill: COLORS.primary
});
titleText.anchor.set(0.5, 0);
titleText.x = CENTER_X;
titleText.y = UI.titleY;
game.addChild(titleText);
flashOverlay = LK.getAsset('flashBg', {
anchorX: 0.5,
anchorY: 0.5
});
flashOverlay.x = CENTER_X;
flashOverlay.y = CENTER_Y;
flashOverlay.alpha = 0;
game.addChild(flashOverlay);
initGameOverUI();
}
function initGameOverUI() {
gameOverOverlay = new Container();
gameOverOverlay.visible = false;
game.addChild(gameOverOverlay);
var dark = LK.getAsset('overlayDark', {
anchorX: 0.5,
anchorY: 0.5
});
dark.x = CENTER_X;
dark.y = CENTER_Y;
dark.alpha = 0.72;
gameOverOverlay.addChild(dark);
var title = new Text2('YOU RAN OUT OF MONEY', {
size: 60,
fill: COLORS.danger,
align: 'center'
});
title.anchor.set(0.5, 0.5);
title.x = CENTER_X;
title.y = 1050;
gameOverOverlay.addChild(title);
var sub = new Text2('Your run is over. Restart the run or wipe your save.', {
size: 28,
fill: COLORS.white,
align: 'center'
});
sub.anchor.set(0.5, 0.5);
sub.x = CENTER_X;
sub.y = 1130;
gameOverOverlay.addChild(sub);
gameOverRestartButton = new Button();
gameOverRestartButton.x = CENTER_X;
gameOverRestartButton.y = 1260;
gameOverRestartButton.setText('RESTART RUN');
gameOverRestartButton.setTint(0x2a6e56);
gameOverRestartButton.onClick = function () {
restartRunOnly();
};
gameOverOverlay.addChild(gameOverRestartButton);
}
function showGameOver() {
gameState.isGameOver = true;
dragData.active = false;
if (gameOverOverlay) {
gameOverOverlay.visible = true;
}
showFlash(0xff0000, 0.14);
}
function hideGameOver() {
if (gameOverOverlay) {
gameOverOverlay.visible = false;
}
}
function updateUI() {
if (coinText) {
coinText.setText(formatMoney(gameState.coins));
}
if (streakText) {
streakText.setText('x' + gameState.streak);
}
if (jackpotBar) {
jackpotBar.scaleX = clamp(gameState.jackpotMeter / gameState.jackpotMax, 0, 1);
}
if (jackpotText) {
jackpotText.setText(gameState.jackpotMeter + ' / ' + gameState.jackpotMax);
}
if (prestigeText) {
prestigeText.setText('Prestige Chips: ' + gameState.prestigeChips + '\nNext Prestige: +' + getPrestigeReward() + '\nMultiplier: x' + getPrestigeMultiplier().toFixed(2));
}
if (statsText) {
statsText.setText('Best Streak: ' + gameState.bestStreak + '\nBiggest Win: ' + formatMoney(gameState.biggestWin) + '\nTickets Scratched: ' + gameState.ticketsScratchedTotal + '\nWins / Losses: ' + gameState.statsTotalWins + ' / ' + gameState.statsTotalLosses);
}
if (unlockText) {
unlockText.setText('Unlocks' + '\nLucky Ticket: ' + (gameState.unlockLuckyTicket ? 'Yes' : 'No') + '\nGold Ticket: ' + (gameState.unlockGoldTicket ? 'Yes' : 'No') + '\nModifiers: ' + (gameState.unlockModifiers ? 'Yes' : 'No') + '\nPrestige: ' + (gameState.unlockPrestige ? 'Yes' : 'No'));
}
if (achievementText) {
achievementText.setText('Achievements: ' + getCurrentAchievementsCount() + '\nNear Misses: ' + gameState.statsNearMisses + '\nModifier Wins: ' + gameState.statsModifierWins);
}
if (modifierText) {
modifierText.setText(currentTicket ? 'Current Modifier: ' + currentTicket.modifier.label : 'Buy a ticket to start');
}
for (var i = 0; i < upgradeCards.length; i++) {
var level = gameState.upgrades[i];
var cost = getUpgradeCost(i, level);
upgradeCards[i].setUpgrade(i, level, cost, gameState.coins >= cost && !gameState.isGameOver);
}
for (var j = 0; j < ticketBuyButtons.length; j++) {
var canAfford = gameState.coins >= ticketBuyButtons[j].cost;
var unlocked = canBuyTicket(ticketBuyButtons[j].type);
ticketBuyButtons[j].setEnabled(canAfford && unlocked && !gameState.isGameOver);
if (!unlocked) {
if (ticketBuyButtons[j].type === 1) {
ticketBuyButtons[j].setText('LUCKY\nLOCKED');
}
if (ticketBuyButtons[j].type === 2) {
ticketBuyButtons[j].setText('GOLD\nLOCKED');
}
} else {
if (ticketBuyButtons[j].type === 0) {
ticketBuyButtons[j].setText('BASIC\n$5');
}
if (ticketBuyButtons[j].type === 1) {
ticketBuyButtons[j].setText('LUCKY\n$15');
}
if (ticketBuyButtons[j].type === 2) {
ticketBuyButtons[j].setText('GOLD\n$50');
}
}
}
if (prestigeButton) {
prestigeButton.setText('PRESTIGE\n+' + getPrestigeReward());
prestigeButton.setEnabled(gameState.unlockPrestige && getPrestigeReward() > 0 && !gameState.isGameOver);
}
if (restartRunButton) {
restartRunButton.setEnabled(true);
}
if (wipeSaveButton) {
wipeSaveButton.setEnabled(true);
}
}
/****
* Gameplay
****/
function createNewTicket(ticketType) {
if (currentTicket) {
if (currentTicket.parent) {
currentTicket.parent.removeChild(currentTicket);
}
currentTicket.destroy();
}
currentTicket = new ScratchTicket(ticketType);
currentTicket.x = CENTER_X;
currentTicket.y = UI.ticketY;
game.addChild(currentTicket);
dragData.active = false;
updateUI();
}
function onBuyTicket(type, cost) {
if (gameState.isGameOver) {
return;
}
if (currentTicket && !currentTicket.resolved) {
return;
}
if (!canBuyTicket(type)) {
return;
}
if (gameState.coins < cost) {
return;
}
gameState.coins -= cost;
gameState.ticketsScratchedTotal++;
if (type === 0) {
gameState.statsCardsBasic++;
}
if (type === 1) {
gameState.statsCardsLucky++;
}
if (type === 2) {
gameState.statsCardsGold++;
}
createNewTicket(type);
checkMilestones();
evaluateAchievements();
updateUI();
saveProgress();
}
function onUpgradeClick(upgradeId) {
if (gameState.isGameOver) {
return;
}
var level = gameState.upgrades[upgradeId];
var cost = getUpgradeCost(upgradeId, level);
if (gameState.coins < cost) {
return;
}
gameState.coins -= cost;
gameState.upgrades[upgradeId]++;
safePlaySound('upgradeSound');
createPopupText(1720, 960, 'UPGRADE BOUGHT', COLORS.success, 28);
updateUI();
saveProgress();
checkForGameOver();
}
function createPopupText(x, y, textValue, color, size) {
var text = new Text2(textValue, {
size: size || UI.popupSize,
fill: color || COLORS.primary,
align: 'center'
});
text.anchor.set(0.5, 0.5);
text.x = x;
text.y = y;
game.addChild(text);
popupTexts.push(text);
tween(text, {
y: y - 110,
alpha: 0
}, {
duration: 850,
easing: tween.easeOut,
onFinish: function onFinish() {
if (text.parent) {
text.parent.removeChild(text);
}
var index = popupTexts.indexOf(text);
if (index >= 0) {
popupTexts.splice(index, 1);
}
}
});
}
function createDustParticles(x, y) {
for (var i = 0; i < 10; i++) {
var particle = new DustParticle();
particle.x = x + (Math.random() - 0.5) * 40;
particle.y = y + (Math.random() - 0.5) * 40;
game.addChild(particle);
dustParticles.push(particle);
}
}
function createCoinBurst(x, y, count) {
for (var i = 0; i < count; i++) {
createPopupText(x + (Math.random() - 0.5) * 140, y + (Math.random() - 0.5) * 60, '+', COLORS.primary, 24);
}
}
function resolveCurrentTicket(result) {
if (!currentTicket) {
return;
}
var wasNearMiss = !result.win && result.count === 2;
if (wasNearMiss) {
gameState.statsNearMisses++;
createPopupText(currentTicket.x, currentTicket.y - 170, 'NEAR MISS!', COLORS.warning, 30);
}
if (result.win) {
safePlaySound('winSound');
showFlash(0xffd700, 0.13);
var basePayouts = [10, 25, 50];
var matchBonus = 1 + (result.count - 3) * 0.35;
var payoutUpgrade = 1 + gameState.upgrades[1] * 0.2;
var comboBoost = 1 + gameState.streak * (0.1 + gameState.upgrades[3] * 0.02);
var prestigeBoost = getPrestigeMultiplier();
var modifierBoost = result.modifier ? result.modifier.payoutMult : 1;
if (result.modifier && result.modifier.id === 'hot') {
modifierBoost += gameState.streak * 0.05;
}
var finalPayout = Math.floor(basePayouts[result.ticketType] * matchBonus * payoutUpgrade * comboBoost * prestigeBoost * modifierBoost);
gameState.coins += finalPayout;
gameState.totalCoins += finalPayout;
gameState.streak++;
gameState.statsTotalWins++;
if (result.modifier && result.modifier.id !== 'none') {
gameState.statsModifierWins++;
}
if (gameState.streak > gameState.bestStreak) {
gameState.bestStreak = gameState.streak;
}
if (finalPayout > gameState.biggestWin) {
gameState.biggestWin = finalPayout;
}
if (gameState.unlockJackpotSystem) {
var jackpotGain = 1 + gameState.upgrades[4] + Math.floor(gameState.prestigeChips * 0.1);
if (result.modifier) {
jackpotGain += result.modifier.jackpotBonus || 0;
}
gameState.jackpotMeter += jackpotGain;
if (gameState.jackpotMeter >= gameState.jackpotMax) {
gameState.jackpotMeter = 0;
gameState.jackpotsHit++;
var jackpotReward = Math.floor((100 + gameState.jackpotsHit * 25) * getPrestigeMultiplier());
gameState.coins += jackpotReward;
gameState.totalCoins += jackpotReward;
createPopupText(currentTicket.x, currentTicket.y - 220, 'JACKPOT BONUS ' + formatMoney(jackpotReward), COLORS.success, 40);
createCoinBurst(currentTicket.x, currentTicket.y - 50, 14);
showFlash(0x00ff88, 0.24);
}
}
createPopupText(currentTicket.x, currentTicket.y, '+' + formatMoney(finalPayout), COLORS.primary, 44);
createCoinBurst(currentTicket.x, currentTicket.y + 55, 10);
} else {
gameState.streak = 0;
gameState.statsTotalLosses++;
createPopupText(currentTicket.x, currentTicket.y, 'NO MATCH', COLORS.danger, 34);
}
checkMilestones();
evaluateAchievements();
updateUI();
saveProgress();
checkForGameOver();
}
function checkForGameOver() {
if (gameState.isGameOver) {
return;
}
var hasActiveTicket = currentTicket && !currentTicket.resolved;
if (gameState.coins <= 0 && !hasActiveTicket) {
showGameOver();
}
}
/****
* Boot
****/
initUI();
updateUI();
safePlayMusic('bgMusic', {
loop: true
});
/****
* Input
****/
game.down = function (x, y, obj) {
if (gameState.isGameOver) {
if (gameOverRestartButton) {
if (pointInRect(x, y, gameOverRestartButton.x, gameOverRestartButton.y, gameOverRestartButton.hitWidth, gameOverRestartButton.hitHeight)) {
gameOverRestartButton.press();
return;
}
}
return;
}
for (var i = 0; i < ticketBuyButtons.length; i++) {
var btnX = bottomPanel.x + ticketBuyButtons[i].x;
var btnY = bottomPanel.y + ticketBuyButtons[i].y;
if (pointInRect(x, y, btnX, btnY, ticketBuyButtons[i].hitWidth, ticketBuyButtons[i].hitHeight)) {
ticketBuyButtons[i].press();
return;
}
}
for (var j = 0; j < upgradeCards.length; j++) {
if (upgradeCards[j].containsGlobalPoint(x, y)) {
upgradeCards[j].press();
return;
}
}
if (prestigeButton) {
var pX = rightPanel.x + prestigeButton.x;
var pY = rightPanel.y + prestigeButton.y;
if (pointInRect(x, y, pX, pY, prestigeButton.hitWidth, prestigeButton.hitHeight)) {
prestigeButton.press();
return;
}
}
if (restartRunButton) {
var rrX = rightPanel.x + restartRunButton.x;
var rrY = rightPanel.y + restartRunButton.y;
if (pointInRect(x, y, rrX, rrY, restartRunButton.hitWidth, restartRunButton.hitHeight)) {
restartRunButton.press();
return;
}
}
if (wipeSaveButton) {
var wsX = rightPanel.x + wipeSaveButton.x;
var wsY = rightPanel.y + wipeSaveButton.y;
if (pointInRect(x, y, wsX, wsY, wipeSaveButton.hitWidth, wipeSaveButton.hitHeight)) {
wipeSaveButton.press();
return;
}
}
if (currentTicket && !currentTicket.resolved && currentTicket.containsGlobalPoint(x, y)) {
dragData.active = true;
dragData.lastX = x;
dragData.lastY = y;
}
};
game.move = function (x, y, obj) {
if (gameState.isGameOver) {
return;
}
for (var i = 0; i < ticketBuyButtons.length; i++) {
var btnGX = bottomPanel.x + ticketBuyButtons[i].x;
var btnGY = bottomPanel.y + ticketBuyButtons[i].y;
ticketBuyButtons[i].setHovered(pointInRect(x, y, btnGX, btnGY, ticketBuyButtons[i].hitWidth, ticketBuyButtons[i].hitHeight));
}
for (var j = 0; j < upgradeCards.length; j++) {
upgradeCards[j].setHovered(upgradeCards[j].containsGlobalPoint(x, y));
}
if (prestigeButton) {
var pX = rightPanel.x + prestigeButton.x;
var pY = rightPanel.y + prestigeButton.y;
prestigeButton.setHovered(pointInRect(x, y, pX, pY, prestigeButton.hitWidth, prestigeButton.hitHeight));
}
if (restartRunButton) {
var rrX = rightPanel.x + restartRunButton.x;
var rrY = rightPanel.y + restartRunButton.y;
restartRunButton.setHovered(pointInRect(x, y, rrX, rrY, restartRunButton.hitWidth, restartRunButton.hitHeight));
}
if (wipeSaveButton) {
var wsX = rightPanel.x + wipeSaveButton.x;
var wsY = rightPanel.y + wipeSaveButton.y;
wipeSaveButton.setHovered(pointInRect(x, y, wsX, wsY, wipeSaveButton.hitWidth, wipeSaveButton.hitHeight));
}
if (!dragData.active || !currentTicket || currentTicket.resolved) {
return;
}
if (!currentTicket.containsGlobalPoint(x, y)) {
return;
}
var dx = x - dragData.lastX;
var dy = y - dragData.lastY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 6) {
var scratchAmount = distance / 520;
currentTicket.updateScratch(scratchAmount);
createDustParticles(x, y);
throttleScratchSound();
dragData.lastX = x;
dragData.lastY = y;
updateUI();
}
};
game.up = function () {
dragData.active = false;
};
/****
* Update
****/
game.update = function () {
for (var i = dustParticles.length - 1; i >= 0; i--) {
if (!dustParticles[i] || !dustParticles[i].parent) {
dustParticles.splice(i, 1);
continue;
}
dustParticles[i].update();
}
if (LK.ticks % 300 === 0) {
saveProgress();
}
};