/****
* 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();
}
}; ===================================================================
--- original.js
+++ change.js
@@ -6,67 +6,91 @@
/****
* Classes
****/
-var Button = Container.expand(function () {
+var Button = Container.expand(function (styleType) {
var self = Container.call(this);
self.buttonText = '';
self.onClick = function () {};
self.isHovered = false;
self.enabled = true;
- self.hitWidth = 280;
- self.hitHeight = 60;
- var bg = self.attachAsset('buttonBg', {
+ 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: 22,
- fill: '#ffffff',
+ 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;
- bg.tint = hovered ? 0x15537a : 0x0f3460;
+ 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.8;
- self.velocityX = (Math.random() - 0.5) * 5;
- self.velocityY = (Math.random() - 0.5) * 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.5);
- dust.alpha = 0.8 * (self.life / self.maxLife);
+ 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);
}
@@ -81,14 +105,14 @@
self.symbols = [];
self.scratchAmount = 0;
self.resolved = false;
self.canScratch = true;
- self.ticketWidth = 600;
- self.ticketHeight = 800;
+ 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.55 - scratchSpeedBonus - prestigeScratchBonus, 0.22, 0.55);
+ self.autoResolveThreshold = clamp(0.56 - scratchSpeedBonus - prestigeScratchBonus, 0.22, 0.56);
var bg = self.attachAsset('ticketBg', {
anchorX: 0.5,
anchorY: 0.5
});
@@ -96,41 +120,41 @@
var overlay = self.attachAsset('scratchOverlay', {
anchorX: 0.5,
anchorY: 0.5
});
- overlay.alpha = 0.96;
+ overlay.alpha = 0.95;
var titleText = new Text2(getTicketName(self.ticketType), {
- size: 28,
- fill: '#ffffff',
+ size: 38,
+ fill: COLORS.white,
align: 'center'
});
titleText.anchor.set(0.5, 0);
- titleText.y = -350;
+ titleText.y = -500;
self.addChild(titleText);
var infoText = new Text2('Scratch to reveal 3 matching symbols', {
- size: 16,
- fill: '#ffd700',
+ size: 24,
+ fill: COLORS.primary,
align: 'center'
});
infoText.anchor.set(0.5, 0);
- infoText.y = -310;
+ infoText.y = -450;
self.addChild(infoText);
var modifierLine = new Text2(self.modifier.label, {
- size: 18,
+ size: 26,
fill: self.modifier.color,
align: 'center'
});
modifierLine.anchor.set(0.5, 0);
- modifierLine.y = -280;
+ modifierLine.y = -405;
self.addChild(modifierLine);
- var gridStartX = -140;
- var gridStartY = -140;
- var spacing = 160;
+ 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;
+ symbol.y = gridStartY + row * spacing + 70;
self.symbols.push(symbol);
self.addChild(symbol);
}
}
@@ -142,9 +166,9 @@
if (!self.canScratch || self.resolved) {
return;
}
self.scratchAmount = clamp(self.scratchAmount + amount, 0, 1);
- overlay.alpha = clamp(0.96 - self.scratchAmount, 0, 0.96);
+ 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) {
@@ -214,49 +238,66 @@
****/
var UpgradeCard = Container.expand(function () {
var self = Container.call(this);
self.upgradeId = 0;
- self.hitWidth = 280;
- self.hitHeight = 42;
+ self.hitWidth = UI.buttonWidth;
+ self.hitHeight = UI.upgradeCardHeight;
self.onClick = function () {};
- var bg = self.attachAsset('buttonBg', {
+ var bg = self.attachAsset('upgradeCardBg', {
anchorX: 0.5,
anchorY: 0.5
});
- bg.scaleY = 0.7;
var nameText = new Text2('', {
- size: 16,
- fill: '#ffffff',
+ size: 24,
+ fill: COLORS.white,
align: 'center'
});
nameText.anchor.set(0.5, 0);
- nameText.y = -15;
+ 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: 14,
- fill: '#ffd700',
+ size: 20,
+ fill: COLORS.gold,
align: 'center'
});
costText.anchor.set(0.5, 0);
- costText.y = 5;
+ 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'];
- nameText.setText(names[id] + ' Lv' + (level + 1));
- costText.setText('Cost: ' + formatMoney(cost));
- self.alpha = affordable ? 1 : 0.55;
+ 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 ? 0x15537a : 0x0f3460;
+ 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;
});
@@ -268,29 +309,29 @@
anchorX: 0.5,
anchorY: 0.5
});
var symbolText = new Text2('', {
- size: 56,
- fill: '#ffffff',
+ size: 74,
+ fill: COLORS.white,
align: 'center'
});
symbolText.anchor.set(0.5, 0.5);
- symbolText.alpha = 0.25;
+ 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 || 0x7a6a2a;
+ bg.tint = color || 0x8b7a31;
};
self.reveal = function () {
if (self.revealed) {
return;
}
self.revealed = true;
- bg.tint = 0x4a4a6a;
+ bg.tint = 0x4c5f82;
symbolText.alpha = 1;
safePlaySound('revealSound');
};
self.setSymbol(Math.floor(Math.random() * 6));
@@ -300,20 +341,83 @@
/****
* Initialize Game
****/
var game = new LK.Game({
- backgroundColor: 0x0a0a0a
+ backgroundColor: 0x081018
});
/****
* Game Code
****/
/****
-* Helpers
+* 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) {
@@ -336,9 +440,9 @@
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.3, level));
+ return Math.floor(baseCosts[id] * Math.pow(1.32, level));
}
function getTicketName(type) {
if (type === 1) {
return 'LUCKY TICKET';
@@ -349,14 +453,14 @@
return 'BASIC TICKET';
}
function getTicketTint(type) {
if (type === 1) {
- return 0x1a3a2e;
+ return 0x183b31;
}
if (type === 2) {
- return 0x3a2e1a;
+ return 0x4a3818;
}
- return 0x2a2a4e;
+ return 0x28344e;
}
function getTicketCost(type) {
return [5, 15, 50][type] || 5;
}
@@ -425,13 +529,13 @@
if (!flashOverlay) {
return;
}
flashOverlay.tint = color || 0xffffff;
- flashOverlay.alpha = alpha || 0.35;
+ flashOverlay.alpha = alpha || 0.3;
tween(flashOverlay, {
alpha: 0
}, {
- duration: 250,
+ duration: 260,
easing: tween.easeOut
});
}
/****
@@ -623,41 +727,41 @@
if (!gameState.unlockModifiers) {
return {
id: 'none',
label: 'Standard Ticket',
- color: '#cccccc',
+ color: COLORS.soft,
payoutMult: 1,
jackpotBonus: 0
};
}
var pool = [{
id: 'none',
label: 'Standard Ticket',
- color: '#cccccc',
+ color: COLORS.soft,
payoutMult: 1,
jackpotBonus: 0
}, {
id: 'double',
label: 'Double Payout',
- color: '#00ff88',
+ color: COLORS.success,
payoutMult: 2,
jackpotBonus: 0
}, {
id: 'jackpot',
label: 'Double Jackpot Gain',
- color: '#ffd700',
+ color: COLORS.gold,
payoutMult: 1,
jackpotBonus: 2
}, {
id: 'wild',
label: 'Lucky Wild Card',
- color: '#66ccff',
+ color: COLORS.cyan,
payoutMult: 1.35,
jackpotBonus: 0
}, {
id: 'hot',
label: 'Hot Streak Boost',
- color: '#ff9966',
+ color: COLORS.warning,
payoutMult: 1.5,
jackpotBonus: 0
}];
var oddsBoost = gameState.upgrades[0] * 0.02 + gameState.prestigeChips * 0.005;
@@ -683,9 +787,9 @@
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(0x2a6a8a);
+ ticket.symbols[wildIndex].highlight(0x2d7697);
}
}
/****
* Achievements / Milestones
@@ -694,33 +798,33 @@
if (gameState[key]) {
return;
}
gameState[key] = true;
- createPopupText(1024, 760, 'ACHIEVEMENT: ' + label, '#ffd700', 28);
- showFlash(0xffd700, 0.2);
+ 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(1024, 500, 'LUCKY TICKETS UNLOCKED', '#00ff88', 38);
+ 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(1024, 550, 'GOLD TICKETS UNLOCKED', '#ffd700', 38);
+ createPopupText(CENTER_X, 740, 'GOLD TICKETS UNLOCKED', COLORS.primary, 44);
}
if (!gameState.unlockJackpotSystem && gameState.ticketsScratchedTotal >= 15) {
gameState.unlockJackpotSystem = true;
- createPopupText(1024, 600, 'JACKPOT SYSTEM UNLOCKED', '#66ccff', 30);
+ createPopupText(CENTER_X, 800, 'JACKPOT SYSTEM UNLOCKED', COLORS.cyan, 34);
}
if (!gameState.unlockModifiers && gameState.ticketsScratchedTotal >= 25) {
gameState.unlockModifiers = true;
- createPopupText(1024, 650, 'TICKET MODIFIERS UNLOCKED', '#ff9966', 30);
+ createPopupText(CENTER_X, 860, 'TICKET MODIFIERS UNLOCKED', COLORS.warning, 34);
}
if (!gameState.unlockPrestige && gameState.totalCoins >= 5000) {
gameState.unlockPrestige = true;
- createPopupText(1024, 700, 'PRESTIGE UNLOCKED', '#ffffff', 30);
+ createPopupText(CENTER_X, 920, 'PRESTIGE UNLOCKED', COLORS.white, 34);
}
}
function evaluateAchievements() {
if (gameState.statsTotalWins >= 1) {
@@ -770,9 +874,9 @@
currentTicket.destroy();
currentTicket = null;
}
hideGameOver();
- createPopupText(1024, 950, 'RUN RESTARTED', '#ffffff', 34);
+ createPopupText(CENTER_X, 1040, 'RUN RESTARTED', COLORS.white, 42);
updateUI();
saveProgress();
}
function hardRestartGame() {
@@ -820,9 +924,9 @@
currentTicket.destroy();
currentTicket = null;
}
hideGameOver();
- createPopupText(1024, 950, 'SAVE WIPED', '#ff6b6b', 34);
+ createPopupText(CENTER_X, 1040, 'SAVE WIPED', COLORS.danger, 42);
updateUI();
saveProgress();
}
function resetRunForPrestige() {
@@ -845,10 +949,10 @@
currentTicket.destroy();
currentTicket = null;
}
hideGameOver();
- createPopupText(1024, 900, 'PRESTIGE +' + reward + ' CHIPS', '#ffffff', 40);
- showFlash(0xffffff, 0.35);
+ createPopupText(CENTER_X, 980, 'PRESTIGE +' + reward + ' CHIPS', COLORS.white, 46);
+ showFlash(0xffffff, 0.32);
evaluateAchievements();
updateUI();
saveProgress();
}
@@ -898,190 +1002,203 @@
* UI
****/
function initUI() {
leftPanel = new Container();
- leftPanel.x = 100;
- leftPanel.y = 100;
+ leftPanel.x = UI.leftPanelX;
+ leftPanel.y = UI.leftPanelY;
game.addChild(leftPanel);
var leftBg = LK.getAsset('panelBg', {
anchorX: 0,
anchorY: 0
});
- leftBg.scaleY = 1.05;
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: 20,
- fill: '#ffd700'
+ size: UI.smallSize,
+ fill: COLORS.primary
});
coinsLabelText.anchor.set(0.5, 0);
- coinsLabelText.x = 150;
- coinsLabelText.y = 20;
+ coinsLabelText.x = UI.panelWidth * 0.5;
+ coinsLabelText.y = 92;
leftPanel.addChild(coinsLabelText);
coinText = new Text2('$100', {
- size: 32,
- fill: '#ffffff'
+ size: UI.bigValueSize,
+ fill: COLORS.white
});
coinText.anchor.set(0.5, 0);
- coinText.x = 150;
- coinText.y = 60;
+ coinText.x = UI.panelWidth * 0.5;
+ coinText.y = 126;
leftPanel.addChild(coinText);
var streakLabelText = new Text2('STREAK', {
- size: 20,
- fill: '#ff6b6b'
+ size: UI.smallSize,
+ fill: COLORS.danger
});
streakLabelText.anchor.set(0.5, 0);
- streakLabelText.x = 150;
- streakLabelText.y = 130;
+ streakLabelText.x = UI.panelWidth * 0.5;
+ streakLabelText.y = 212;
leftPanel.addChild(streakLabelText);
streakText = new Text2('x0', {
- size: 24,
- fill: '#ffffff'
+ size: UI.mediumValueSize,
+ fill: COLORS.white
});
streakText.anchor.set(0.5, 0);
- streakText.x = 150;
- streakText.y = 160;
+ streakText.x = UI.panelWidth * 0.5;
+ streakText.y = 246;
leftPanel.addChild(streakText);
- var jackpotLabelText = new Text2('JACKPOT', {
- size: 20,
- fill: '#ffd700'
+ var jackpotLabelText = new Text2('JACKPOT METER', {
+ size: UI.smallSize,
+ fill: COLORS.primary
});
jackpotLabelText.anchor.set(0.5, 0);
- jackpotLabelText.x = 150;
- jackpotLabelText.y = 220;
+ jackpotLabelText.x = UI.panelWidth * 0.5;
+ jackpotLabelText.y = 328;
leftPanel.addChild(jackpotLabelText);
var jackpotBgBar = LK.getAsset('meterBarEmpty', {
anchorX: 0.5,
anchorY: 0
});
- jackpotBgBar.x = 150;
- jackpotBgBar.y = 250;
+ jackpotBgBar.x = UI.panelWidth * 0.5;
+ jackpotBgBar.y = 366;
leftPanel.addChild(jackpotBgBar);
jackpotBar = LK.getAsset('meterBarFull', {
anchorX: 0.5,
anchorY: 0
});
- jackpotBar.x = 150;
- jackpotBar.y = 250;
+ jackpotBar.x = UI.panelWidth * 0.5;
+ jackpotBar.y = 366;
jackpotBar.scaleX = 0;
leftPanel.addChild(jackpotBar);
- jackpotText = new Text2('0/100', {
- size: 20,
- fill: '#ffffff'
+ jackpotText = new Text2('0 / 100', {
+ size: UI.smallSize,
+ fill: COLORS.white
});
jackpotText.anchor.set(0.5, 0);
- jackpotText.x = 150;
- jackpotText.y = 280;
+ jackpotText.x = UI.panelWidth * 0.5;
+ jackpotText.y = 402;
leftPanel.addChild(jackpotText);
prestigeText = new Text2('', {
- size: 14,
- fill: '#66ccff',
+ size: UI.bodySize,
+ fill: COLORS.cyan,
align: 'center'
});
prestigeText.anchor.set(0.5, 0);
- prestigeText.x = 150;
- prestigeText.y = 320;
+ prestigeText.x = UI.panelWidth * 0.5;
+ prestigeText.y = 468;
leftPanel.addChild(prestigeText);
statsText = new Text2('', {
- size: 20,
- fill: '#bbbbbb',
+ size: UI.smallSize,
+ fill: COLORS.soft,
align: 'center'
});
statsText.anchor.set(0.5, 0);
- statsText.x = 150;
- statsText.y = 370;
+ statsText.x = UI.panelWidth * 0.5;
+ statsText.y = 622;
leftPanel.addChild(statsText);
unlockText = new Text2('', {
- size: 20,
- fill: '#00ff88',
+ size: UI.smallSize,
+ fill: COLORS.success,
align: 'center'
});
unlockText.anchor.set(0.5, 0);
- unlockText.x = 150;
- unlockText.y = 500;
+ unlockText.x = UI.panelWidth * 0.5;
+ unlockText.y = 780;
leftPanel.addChild(unlockText);
achievementText = new Text2('', {
- size: 20,
- fill: '#ffd700',
+ size: UI.smallSize,
+ fill: COLORS.gold,
align: 'center'
});
achievementText.anchor.set(0.5, 0);
- achievementText.x = 150;
- achievementText.y = 570;
+ achievementText.x = UI.panelWidth * 0.5;
+ achievementText.y = 910;
leftPanel.addChild(achievementText);
rightPanel = new Container();
- rightPanel.x = 1848;
- rightPanel.y = 100;
+ rightPanel.x = UI.rightPanelX;
+ rightPanel.y = UI.rightPanelY;
game.addChild(rightPanel);
- var rightBg = LK.getAsset('panelBg', {
+ var rightBg = LK.getAsset('panelBgAlt', {
anchorX: 1,
anchorY: 0
});
- rightBg.scaleY = 1.25;
rightPanel.addChild(rightBg);
var shopLabelText = new Text2('UPGRADES', {
- size: 20,
- fill: '#00ff88'
+ size: UI.sectionLabelSize,
+ fill: COLORS.success
});
shopLabelText.anchor.set(0.5, 0);
- shopLabelText.x = -150;
- shopLabelText.y = 20;
+ 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 = 90 + i * 82;
- card.x = -150;
+ 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 = -150;
- prestigeButton.y = 610;
- prestigeButton.setText('Prestige');
+ 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();
- restartRunButton.x = -150;
- restartRunButton.y = 690;
- restartRunButton.setText('Restart Run');
+ 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();
- wipeSaveButton.x = -150;
- wipeSaveButton.y = 770;
- wipeSaveButton.setText('Wipe Save');
+ 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 = 1024;
- bottomPanel.y = 2520;
+ bottomPanel.x = CENTER_X;
+ bottomPanel.y = UI.bottomPanelY;
game.addChild(bottomPanel);
var ticketTypes = [{
- name: 'Basic\n$5',
+ name: 'BASIC\n$5',
cost: 5,
- type: 0
+ type: 0,
+ tint: 0x255c99
}, {
- name: 'Lucky\n$15',
+ name: 'LUCKY\n$15',
cost: 15,
- type: 1
+ type: 1,
+ tint: 0x29765c
}, {
- name: 'Gold\n$50',
+ name: 'GOLD\n$50',
cost: 50,
- type: 2
+ type: 2,
+ tint: 0x977336
}];
for (var t = 0; t < ticketTypes.length; t++) {
var btn = new Button();
- btn.x = -300 + t * 300;
+ 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);
@@ -1089,32 +1206,32 @@
ticketBuyButtons.push(btn);
bottomPanel.addChild(btn);
}
centerInfoPanel = new Container();
- centerInfoPanel.x = 1024;
- centerInfoPanel.y = 180;
+ centerInfoPanel.x = CENTER_X;
+ centerInfoPanel.y = UI.modifierY;
game.addChild(centerInfoPanel);
modifierText = new Text2('', {
- size: 18,
- fill: '#ffffff',
+ size: 28,
+ fill: COLORS.white,
align: 'center'
});
modifierText.anchor.set(0.5, 0);
centerInfoPanel.addChild(modifierText);
var titleText = new Text2('LUCKY SCRATCH TYCOON', {
- size: 40,
- fill: '#ffd700'
+ size: UI.titleSize,
+ fill: COLORS.primary
});
titleText.anchor.set(0.5, 0);
- titleText.x = 1024;
- titleText.y = 30;
+ titleText.x = CENTER_X;
+ titleText.y = UI.titleY;
game.addChild(titleText);
flashOverlay = LK.getAsset('flashBg', {
anchorX: 0.5,
anchorY: 0.5
});
- flashOverlay.x = 1024;
- flashOverlay.y = 1366;
+ flashOverlay.x = CENTER_X;
+ flashOverlay.y = CENTER_Y;
flashOverlay.alpha = 0;
game.addChild(flashOverlay);
initGameOverUI();
}
@@ -1125,34 +1242,35 @@
var dark = LK.getAsset('overlayDark', {
anchorX: 0.5,
anchorY: 0.5
});
- dark.x = 1024;
- dark.y = 1366;
- dark.alpha = 0.7;
+ 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: 42,
- fill: '#ff6b6b',
+ size: 60,
+ fill: COLORS.danger,
align: 'center'
});
title.anchor.set(0.5, 0.5);
- title.x = 1024;
+ 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: 22,
- fill: '#ffffff',
+ size: 28,
+ fill: COLORS.white,
align: 'center'
});
sub.anchor.set(0.5, 0.5);
- sub.x = 1024;
- sub.y = 1120;
+ sub.x = CENTER_X;
+ sub.y = 1130;
gameOverOverlay.addChild(sub);
gameOverRestartButton = new Button();
- gameOverRestartButton.x = 1024;
- gameOverRestartButton.y = 1240;
- gameOverRestartButton.setText('Restart Run');
+ gameOverRestartButton.x = CENTER_X;
+ gameOverRestartButton.y = 1260;
+ gameOverRestartButton.setText('RESTART RUN');
+ gameOverRestartButton.setTint(0x2a6e56);
gameOverRestartButton.onClick = function () {
restartRunOnly();
};
gameOverOverlay.addChild(gameOverRestartButton);
@@ -1162,9 +1280,9 @@
dragData.active = false;
if (gameOverOverlay) {
gameOverOverlay.visible = true;
}
- showFlash(0xff0000, 0.15);
+ showFlash(0xff0000, 0.14);
}
function hideGameOver() {
if (gameOverOverlay) {
gameOverOverlay.visible = false;
@@ -1180,18 +1298,18 @@
if (jackpotBar) {
jackpotBar.scaleX = clamp(gameState.jackpotMeter / gameState.jackpotMax, 0, 1);
}
if (jackpotText) {
- jackpotText.setText(gameState.jackpotMeter + '/' + gameState.jackpotMax);
+ 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: ' + gameState.ticketsScratchedTotal + '\nWins/Losses: ' + gameState.statsTotalWins + '/' + gameState.statsTotalLosses);
+ 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: ' + (gameState.unlockLuckyTicket ? 'Yes' : 'No') + '\nGold: ' + (gameState.unlockGoldTicket ? 'Yes' : 'No') + '\nModifiers: ' + (gameState.unlockModifiers ? 'Yes' : 'No') + '\nPrestige: ' + (gameState.unlockPrestige ? 'Yes' : 'No'));
+ 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);
}
@@ -1208,27 +1326,27 @@
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');
+ ticketBuyButtons[j].setText('LUCKY\nLOCKED');
}
if (ticketBuyButtons[j].type === 2) {
- ticketBuyButtons[j].setText('Gold\nLocked');
+ ticketBuyButtons[j].setText('GOLD\nLOCKED');
}
} else {
if (ticketBuyButtons[j].type === 0) {
- ticketBuyButtons[j].setText('Basic\n$5');
+ ticketBuyButtons[j].setText('BASIC\n$5');
}
if (ticketBuyButtons[j].type === 1) {
- ticketBuyButtons[j].setText('Lucky\n$15');
+ ticketBuyButtons[j].setText('LUCKY\n$15');
}
if (ticketBuyButtons[j].type === 2) {
- ticketBuyButtons[j].setText('Gold\n$50');
+ ticketBuyButtons[j].setText('GOLD\n$50');
}
}
}
if (prestigeButton) {
- prestigeButton.setText('Prestige\n+' + getPrestigeReward());
+ prestigeButton.setText('PRESTIGE\n+' + getPrestigeReward());
prestigeButton.setEnabled(gameState.unlockPrestige && getPrestigeReward() > 0 && !gameState.isGameOver);
}
if (restartRunButton) {
restartRunButton.setEnabled(true);
@@ -1247,10 +1365,10 @@
}
currentTicket.destroy();
}
currentTicket = new ScratchTicket(ticketType);
- currentTicket.x = 1024;
- currentTicket.y = 1200;
+ currentTicket.x = CENTER_X;
+ currentTicket.y = UI.ticketY;
game.addChild(currentTicket);
dragData.active = false;
updateUI();
}
@@ -1295,29 +1413,29 @@
}
gameState.coins -= cost;
gameState.upgrades[upgradeId]++;
safePlaySound('upgradeSound');
- createPopupText(1700, 700, 'UPGRADE BOUGHT', '#00ff88', 24);
+ 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 || 32,
- fill: color || '#ffd700',
+ 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 - 90,
+ y: y - 110,
alpha: 0
}, {
- duration: 800,
+ duration: 850,
easing: tween.easeOut,
onFinish: function onFinish() {
if (text.parent) {
text.parent.removeChild(text);
@@ -1329,19 +1447,19 @@
}
});
}
function createDustParticles(x, y) {
- for (var i = 0; i < 8; i++) {
+ for (var i = 0; i < 10; i++) {
var particle = new DustParticle();
- particle.x = x + (Math.random() - 0.5) * 30;
- particle.y = y + (Math.random() - 0.5) * 30;
+ 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) * 120, y + (Math.random() - 0.5) * 50, '+', '#ffd700', 20);
+ createPopupText(x + (Math.random() - 0.5) * 140, y + (Math.random() - 0.5) * 60, '+', COLORS.primary, 24);
}
}
function resolveCurrentTicket(result) {
if (!currentTicket) {
@@ -1349,13 +1467,13 @@
}
var wasNearMiss = !result.win && result.count === 2;
if (wasNearMiss) {
gameState.statsNearMisses++;
- createPopupText(currentTicket.x, currentTicket.y - 120, 'NEAR MISS!', '#ff9966', 24);
+ createPopupText(currentTicket.x, currentTicket.y - 170, 'NEAR MISS!', COLORS.warning, 30);
}
if (result.win) {
safePlaySound('winSound');
- showFlash(0xffd700, 0.15);
+ 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);
@@ -1389,19 +1507,19 @@
gameState.jackpotsHit++;
var jackpotReward = Math.floor((100 + gameState.jackpotsHit * 25) * getPrestigeMultiplier());
gameState.coins += jackpotReward;
gameState.totalCoins += jackpotReward;
- createPopupText(currentTicket.x, currentTicket.y - 160, 'JACKPOT BONUS ' + formatMoney(jackpotReward), '#00ff88', 34);
- createCoinBurst(currentTicket.x, currentTicket.y - 40, 12);
- showFlash(0x00ff88, 0.28);
+ 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), '#ffd700', 34);
- createCoinBurst(currentTicket.x, currentTicket.y + 40, 8);
+ 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', '#ff6b6b', 30);
+ createPopupText(currentTicket.x, currentTicket.y, 'NO MATCH', COLORS.danger, 34);
}
checkMilestones();
evaluateAchievements();
updateUI();
@@ -1518,9 +1636,9 @@
var dx = x - dragData.lastX;
var dy = y - dragData.lastY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 6) {
- var scratchAmount = distance / 430;
+ var scratchAmount = distance / 520;
currentTicket.updateScratch(scratchAmount);
createDustParticles(x, y);
throttleScratchSound();
dragData.lastX = x;