User prompt
add a line to flight and add a grapf. Shortly improve visuals
User prompt
make ui better
Code edit (1 edits merged)
Please save this source code
User prompt
Aviator Cashout
Initial prompt
make me a game like "Aviator" gambling game. It works with game money. So add money system and user must be select bet amount before the game. Than game has start. If user withdraws his money back before the flied crash it win his money with mulitplied.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { balance: 1000 }); /**** * Classes ****/ // Button class var GameButton = Container.expand(function () { var self = Container.call(this); self.bg = null; self.label = null; self.setAsset = function (assetId, labelText, color) { if (self.bg) self.bg.destroy(); self.bg = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); if (color !== undefined) self.bg.tint = color; if (self.label) self.label.destroy(); self.label = new Text2(labelText, { size: 60, fill: "#fff" }); self.label.anchor.set(0.5, 0.5); self.addChild(self.label); }; self.setText = function (txt) { if (self.label) self.label.setText(txt); }; return self; }); // GraphBackground class: draws the multiplier vs time graph as a grid and curve var GraphBackground = Container.expand(function () { var self = Container.call(this); // Graph area (leave margin for axes) self.graphX = 180; self.graphY = 420; self.graphW = 2048 - 360; self.graphH = 900; // For curve drawing self.maxTicks = 600; // 10s at 60fps self.maxMultiplier = 20; // Draw grid lines and axes self.drawGrid = function () { // Remove previous children while (self.children.length) self.children[0].destroy(); // Draw horizontal grid lines (multiplier) for (var i = 1; i <= 5; i++) { var y = self.graphY + self.graphH - i * self.graphH / 5; var line = LK.getAsset('betBtn', { anchorX: 0, anchorY: 0, x: self.graphX, y: y }); line.width = self.graphW; line.height = 4; line.tint = 0x2c3e50; line.alpha = 0.18; self.addChild(line); // Multiplier label var mult = (i * self.maxMultiplier / 5).toFixed(0) + "x"; var label = new Text2(mult, { size: 38, fill: "#fff", stroke: "#222", strokeThickness: 4, fontWeight: "bold" }); label.anchor.set(1, 0.5); label.x = self.graphX - 18; label.y = y + 2; label.alpha = 0.7; self.addChild(label); } // Draw vertical grid lines (time) for (var j = 0; j <= 6; j++) { var x = self.graphX + j * self.graphW / 6; var vline = LK.getAsset('betBtn', { anchorX: 0, anchorY: 0, x: x, y: self.graphY }); vline.width = 4; vline.height = self.graphH; vline.tint = 0x2c3e50; vline.alpha = 0.18; self.addChild(vline); // Time label var t = (j * self.maxTicks / 60 / 6).toFixed(1) + "s"; var tlabel = new Text2(t, { size: 34, fill: "#fff", stroke: "#222", strokeThickness: 4, fontWeight: "bold" }); tlabel.anchor.set(0.5, 0); tlabel.x = x; tlabel.y = self.graphY + self.graphH + 8; tlabel.alpha = 0.7; self.addChild(tlabel); } }; // Draw the curve for the current round self.drawCurve = function (ticksElapsed, crashed) { // Remove previous curve if any if (self.curveNodes) { for (var i = 0; i < self.curveNodes.length; i++) { self.curveNodes[i].destroy(); } } self.curveNodes = []; // Draw curve as small dots for each tick var lastX = null, lastY = null; var maxDrawTicks = crashed ? ticksElapsed : Math.min(ticksElapsed, self.maxTicks); for (var t = 0; t <= maxDrawTicks; t += 2) { var mult = Math.max(1, Math.floor(100 * Math.pow(1.002, t * 2)) / 100); if (mult > self.maxMultiplier) break; var px = self.graphX + t / self.maxTicks * self.graphW; var py = self.graphY + self.graphH - mult / self.maxMultiplier * self.graphH; // Draw dot var dot = LK.getAsset('chip', { anchorX: 0.5, anchorY: 0.5, x: px, y: py }); dot.width = 18; dot.height = 18; dot.tint = crashed && t === maxDrawTicks ? 0xff3333 : 0x3a8ee6; dot.alpha = crashed && t === maxDrawTicks ? 1 : 0.7; self.addChild(dot); self.curveNodes.push(dot); // Optionally, connect with a line (simulate with thin box) if (lastX !== null && lastY !== null) { var dx = px - lastX; var dy = py - lastY; var dist = Math.sqrt(dx * dx + dy * dy); var line = LK.getAsset('betBtn', { anchorX: 0, anchorY: 0.5, x: lastX, y: lastY }); line.width = dist; line.height = 8; line.tint = 0x3a8ee6; line.alpha = 0.5; // Angle line.rotation = Math.atan2(dy, dx); self.addChild(line); self.curveNodes.push(line); } lastX = px; lastY = py; } }; self.drawGrid(); return self; }); // Default balance // Plane class var Plane = Container.expand(function () { var self = Container.call(this); var planeGfx = self.attachAsset('plane', { anchorX: 0.5, anchorY: 0.5 }); self.crashed = false; self.setCrashed = function () { self.crashed = true; // Show crash effect var crash = LK.getAsset('crash', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 }); self.addChild(crash); tween(crash, { alpha: 0 }, { duration: 700, onFinish: function onFinish() { crash.destroy(); } }); // Fade out plane tween(planeGfx, { alpha: 0 }, { duration: 700 }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a2233 }); /**** * Game Code ****/ // New sounds added to the game // Plane explosion sound effect // Hype/energetic soundtrack for flight // --- Game State Variables --- // Plane (the flying object) // Bet button // Cashout button // Crash explosion // Virtual chip // --- Game State Variables --- // Reset all balances to 1000 on page refresh var balance = 1000; storage.balance = 1000; var betAmount = 100; var minBet = 10; var maxBet = 1000; var betStep = 10; var inRound = false; var hasBet = false; var hasCashedOut = false; var roundMultiplier = 1.00; var roundStartTick = 0; var crashTick = 0; var cashoutMultiplier = 0; var plane = null; var crashHappened = false; var lastTick = 0; var betBtn = null; var cashoutBtn = null; var betAmountTxt = null; var balanceTxt = null; var multiplierTxt = null; var infoTxt = null; var chipIcon = null; // --- Player and Bot Colors --- var playerColor = 0xFFE066; // gold/yellow for player // Bot color codes: Jack (blue), Arthur (pink), John (orange) var botColors = [0x3a8ee6, 0xff69b4, 0xffa500]; // blue, pink, orange var botNameColors = [0x3a8ee6, 0xff69b4, 0xffa500]; // name text color matches dot color // --- Bot State --- var bots = [{ name: "Jack", balance: 1000, betAmount: 0, hasBet: false, hasCashedOut: false, cashoutMultiplier: 0, displayTxt: null, chipIcon: null, win: 0, color: botColors[0], nameColor: botNameColors[0], dotColor: botColors[0] }, { name: "Arthur", balance: 1000, betAmount: 0, hasBet: false, hasCashedOut: false, cashoutMultiplier: 0, displayTxt: null, chipIcon: null, win: 0, color: botColors[1], nameColor: botNameColors[1], dotColor: botColors[1] }, { name: "John", balance: 1000, betAmount: 0, hasBet: false, hasCashedOut: false, cashoutMultiplier: 0, displayTxt: null, chipIcon: null, win: 0, color: botColors[2], nameColor: botNameColors[2], dotColor: botColors[2] }]; // Set win target for all to 2000 var botWinTarget = 2000; var botWinner = null; // --- GUI Elements --- // --- Graph Background --- var graphBg = new GraphBackground(); game.addChild(graphBg); // --- GUI Elements --- multiplierTxt = new Text2("1.00x", { size: 180, fill: 0xFFE066, stroke: "#222", strokeThickness: 10, dropShadow: true, dropShadowColor: "#000", dropShadowDistance: 8, dropShadowAngle: Math.PI / 2, dropShadowBlur: 8, fontWeight: "bold" }); multiplierTxt.anchor.set(0.5, 0); multiplierTxt.y = 40; LK.gui.top.addChild(multiplierTxt); // Info text (centered, below multiplier, with shadow and spacing) infoTxt = new Text2("Place your bet!", { size: 70, fill: "#fff", stroke: "#222", strokeThickness: 6, dropShadow: true, dropShadowColor: "#000", dropShadowDistance: 4, dropShadowAngle: Math.PI / 2, dropShadowBlur: 6, fontWeight: "bold" }); infoTxt.anchor.set(0.5, 0); infoTxt.y = multiplierTxt.y + multiplierTxt.height + 20; LK.gui.top.addChild(infoTxt); balanceTxt = new Text2("Balance: $" + balance, { size: 60, fill: playerColor, stroke: "#222", strokeThickness: 5, fontWeight: "bold" }); balanceTxt.anchor.set(0.5, 1); // Bet amount display (larger, bolder) betAmountTxt = new Text2("Bet: $" + betAmount, { size: 90, fill: "#fff", stroke: "#222", strokeThickness: 6, fontWeight: "bold" }); betAmountTxt.anchor.set(1, 0.5); betAmountTxt.x = 2048 - 60; LK.gui.topRight.addChild(betAmountTxt); // --- Bet Button --- betBtn = new GameButton(); betBtn.setAsset('betBtn', "BET", 0x2ecc40); betBtn.bg.width = 520; betBtn.bg.height = 180; betBtn.bg.radius = 40; betBtn.bg.dropShadow = true; betBtn.bg.dropShadowColor = 0x222222; betBtn.bg.dropShadowDistance = 8; betBtn.bg.dropShadowAngle = Math.PI / 2; betBtn.bg.dropShadowBlur = 8; // Centered horizontally, near bottom betBtn.x = 2048 / 2; betBtn.y = 2732 - 180; betBtn.anchorX = 0.5; betBtn.anchorY = 0.5; game.addChild(betBtn); // --- Balance text and chip icon above bet button (centered) --- // Center balance text horizontally above bet button balanceTxt.anchor.set(0.5, 1); balanceTxt.x = betBtn.x; balanceTxt.y = betBtn.y - betBtn.bg.height / 2 - 30; // --- Player win/lose indicator text (above balance) --- var playerWinFloatTxt = null; // Center balance text horizontally above bet button game.addChild(balanceTxt); // --- Add bot balance and chip display above bet button --- var botDisplayYOffset = 60; for (var i = 0; i < bots.length; i++) { // Balance text for bot var botTxt = new Text2(bots[i].name + ": $" + bots[i].balance, { size: 44, fill: bots[i].nameColor, stroke: "#222", strokeThickness: 4, fontWeight: "bold" }); botTxt.anchor.set(0.5, 1); botTxt.x = betBtn.x - 400 + i * 400; botTxt.y = balanceTxt.y - 120; bots[i].displayTxt = botTxt; game.addChild(botTxt); } // Update bet button text to "BET 100" betBtn.setText("BET " + betAmount); // --- Cashout Button --- cashoutBtn = new GameButton(); cashoutBtn.setAsset('cashoutBtn', "CASHOUT", 0xe67e22); cashoutBtn.bg.width = 520; cashoutBtn.bg.height = 180; cashoutBtn.bg.radius = 40; cashoutBtn.bg.dropShadow = true; cashoutBtn.bg.dropShadowColor = 0x222222; cashoutBtn.bg.dropShadowDistance = 8; cashoutBtn.bg.dropShadowAngle = Math.PI / 2; cashoutBtn.bg.dropShadowBlur = 8; // Centered horizontally, same Y as betBtn cashoutBtn.x = 2048 / 2; cashoutBtn.y = 2732 - 180; cashoutBtn.anchorX = 0.5; cashoutBtn.anchorY = 0.5; game.addChild(cashoutBtn); cashoutBtn.visible = false; // --- Bet Amount Adjust (plus/minus buttons) --- var betMinusBtn = new GameButton(); betMinusBtn.setAsset('betBtn', "-", 0x16a085); betMinusBtn.bg.width = 120; betMinusBtn.bg.height = 120; betMinusBtn.bg.radius = 60; betMinusBtn.x = 2048 - 420; betMinusBtn.y = 180; LK.gui.topRight.addChild(betMinusBtn); var betPlusBtn = new GameButton(); betPlusBtn.setAsset('betBtn', "+", 0x16a085); betPlusBtn.bg.width = 120; betPlusBtn.bg.height = 120; betPlusBtn.bg.radius = 60; betPlusBtn.x = 2048 - 180; betPlusBtn.y = 180; LK.gui.topRight.addChild(betPlusBtn); betMinusBtn.down = function (x, y, obj) { if (inRound) return; betAmount -= betStep; if (betAmount < minBet) betAmount = maxBet; betAmountTxt.setText("Bet: $" + betAmount); }; betPlusBtn.down = function (x, y, obj) { if (inRound) return; betAmount += betStep; if (betAmount > maxBet) betAmount = minBet; betAmountTxt.setText("Bet: $" + betAmount); }; // --- Bet Button Handler --- betBtn.down = function (x, y, obj) { if (inRound || hasBet) return; if (balance < betAmount) { infoTxt.setText("Not enough balance!"); return; } hasBet = true; infoTxt.setText("Waiting for takeoff..."); betBtn.setText("WAIT"); betBtn.bg.tint = 0xaaaaaa; // Deduct bet immediately balance -= betAmount; storage.balance = balance; balanceTxt.setText("Balance: $" + balance); if (typeof balanceTxt.setFill === "function") balanceTxt.setFill(playerColor); // Start round after short delay LK.setTimeout(startRound, 900); }; // --- Cashout Button Handler --- cashoutBtn.down = function (x, y, obj) { if (!inRound || !hasBet || hasCashedOut || crashHappened) return; hasCashedOut = true; cashoutMultiplier = roundMultiplier; var win = Math.floor(betAmount * cashoutMultiplier); balance += win; storage.balance = balance; balanceTxt.setText("Balance: $" + balance); if (typeof balanceTxt.setFill === "function") balanceTxt.setFill(playerColor); infoTxt.setText("You cashed out: $" + win + " (" + cashoutMultiplier.toFixed(2) + "x)"); // Show floating win text above balance if (playerWinFloatTxt) { playerWinFloatTxt.destroy(); playerWinFloatTxt = null; } playerWinFloatTxt = new Text2("+" + win + " chips", { size: 48, fill: 0x3FDC5A, stroke: "#222", strokeThickness: 5, fontWeight: "bold" }); playerWinFloatTxt.anchor.set(0.5, 1); playerWinFloatTxt.x = balanceTxt.x; playerWinFloatTxt.y = balanceTxt.y - 60; game.addChild(playerWinFloatTxt); tween(playerWinFloatTxt, { y: playerWinFloatTxt.y - 60, alpha: 0 }, { duration: 1200, onFinish: function onFinish() { if (playerWinFloatTxt) { playerWinFloatTxt.destroy(); playerWinFloatTxt = null; } } }); // Show color-coded dot on graph at cashout point if (typeof graphBg !== "undefined") { var cashoutTick = LK.ticks - roundStartTick; var mult = Math.max(1, Math.floor(100 * Math.pow(1.002, cashoutTick * 2)) / 100); if (mult > graphBg.maxMultiplier) mult = graphBg.maxMultiplier; var px = graphBg.graphX + cashoutTick / graphBg.maxTicks * graphBg.graphW; var py = graphBg.graphY + graphBg.graphH - mult / graphBg.maxMultiplier * graphBg.graphH; var dot = LK.getAsset('chip', { anchorX: 0.5, anchorY: 0.5, x: px, y: py }); dot.width = 32; dot.height = 32; dot.tint = playerColor; dot.alpha = 1; game.addChild(dot); // Track for removal next round if (!game.cashoutDots) game.cashoutDots = []; game.cashoutDots.push(dot); // Animate dot pop dot.scaleX = dot.scaleY = 0.5; tween(dot, { scaleX: 1, scaleY: 1 }, { duration: 300 }); // Optionally fade out after a while LK.setTimeout(function () { dot.alpha = 0.7; }, 1200); } cashoutBtn.visible = false; betBtn.visible = false; // Animate plane off screen if (plane) { tween(plane, { y: -200 }, { duration: 800, easing: tween.cubicOut, onFinish: function onFinish() { endRound(); } }); } else { endRound(); } }; // --- Start Round --- function startRound() { inRound = true; crashHappened = false; hasCashedOut = false; cashoutMultiplier = 0; roundMultiplier = 1.00; roundStartTick = LK.ticks; // Remove all previous player/bot cashout dots if (typeof game.cashoutDots !== "undefined" && game.cashoutDots && game.cashoutDots.length) { for (var i = 0; i < game.cashoutDots.length; i++) { if (game.cashoutDots[i] && typeof game.cashoutDots[i].destroy === "function") { game.cashoutDots[i].destroy(); } } game.cashoutDots = []; } else { game.cashoutDots = []; } // Remove player win/lose indicator if present if (playerWinFloatTxt) { playerWinFloatTxt.destroy(); playerWinFloatTxt = null; } // Random crash time: between 2s and 8s (120-480 ticks) crashTick = roundStartTick + 120 + Math.floor(Math.random() * 360); // Place plane at bottom center if (plane) plane.destroy(); plane = new Plane(); plane.x = 2048 / 2; plane.y = 2732 - 600; game.addChild(plane); // Animate plane takeoff tween(plane, { y: 2732 / 2 }, { duration: 1200, easing: tween.cubicOut }); // Start hype soundtrack LK.playMusic('hype_flight', { fade: { start: 0, end: 1, duration: 600 } }); // UI betBtn.visible = false; cashoutBtn.visible = true; cashoutBtn.setText("CASHOUT"); cashoutBtn.bg.tint = 0xe67e22; infoTxt.setText("Flying... Tap CASHOUT to win!"); multiplierTxt.setText("1.00x"); } // --- End Round --- function endRound() { inRound = false; hasBet = false; hasCashedOut = false; betBtn.visible = true; betBtn.setText("BET"); betBtn.bg.tint = 0x2ecc40; cashoutBtn.visible = false; if (plane) { plane.destroy(); plane = null; } // If player lost, show info if (crashHappened && !cashoutMultiplier) { infoTxt.setText("Crashed! You lost $" + betAmount); } else if (!crashHappened && cashoutMultiplier) { infoTxt.setText("You won $" + Math.floor(betAmount * cashoutMultiplier) + "!"); } else { infoTxt.setText("Place your bet!"); } // Reset multiplier display multiplierTxt.setText("1.00x"); // Update bet amount display betAmountTxt.setText("Bet: $" + betAmount); } // --- Crash Handler --- function handleCrash() { crashHappened = true; // Stop hype soundtrack and play explosion sound LK.stopMusic(); LK.getSound('plane_explode').play(); if (plane) plane.setCrashed(); cashoutBtn.visible = false; betBtn.visible = false; if (!hasCashedOut) { cashoutMultiplier = 0; // Show crash info infoTxt.setText("Crashed! You lost $" + betAmount); // Show floating lose text above balance if (playerWinFloatTxt) { playerWinFloatTxt.destroy(); playerWinFloatTxt = null; } playerWinFloatTxt = new Text2("-" + betAmount + " chips", { size: 48, fill: 0xFF3333, stroke: "#222", strokeThickness: 5, fontWeight: "bold" }); playerWinFloatTxt.anchor.set(0.5, 1); playerWinFloatTxt.x = balanceTxt.x; playerWinFloatTxt.y = balanceTxt.y - 60; game.addChild(playerWinFloatTxt); tween(playerWinFloatTxt, { y: playerWinFloatTxt.y - 60, alpha: 0 }, { duration: 1200, onFinish: function onFinish() { if (playerWinFloatTxt) { playerWinFloatTxt.destroy(); playerWinFloatTxt = null; } } }); // End round after short delay LK.setTimeout(endRound, 1200); } else { // Already cashed out, just end round LK.setTimeout(endRound, 800); } } // --- Main Game Update --- game.update = function () { if (!inRound) { // Reset bot round state if not in round for (var b = 0; b < bots.length; b++) { bots[b].hasBet = false; bots[b].hasCashedOut = false; bots[b].betAmount = 0; bots[b].cashoutMultiplier = 0; bots[b].win = 0; } return; } // Calculate multiplier: Exponential growth, e.g. 1.00x to ~20x in 8s var ticksElapsed = LK.ticks - roundStartTick; // Multiplier formula: 1.002^(ticksElapsed*2) for smooth curve roundMultiplier = Math.max(1, Math.floor(100 * Math.pow(1.002, ticksElapsed * 2)) / 100); multiplierTxt.setText(roundMultiplier.toFixed(2) + "x"); // --- Bot logic: Place bets at start of round --- for (var b = 0; b < bots.length; b++) { var bot = bots[b]; // Place bet if not already bet and round just started if (!bot.hasBet && ticksElapsed < 10) { // All bots always bet 100 chips if enough balance var botBet = 100; if (bot.balance >= botBet) { bot.betAmount = botBet; bot.balance -= botBet; bot.hasBet = true; bot.hasCashedOut = false; bot.cashoutMultiplier = 0; bot.win = 0; // Reset win/lose status color and text if (bot.displayTxt) { bot.displayTxt.setText(bot.name + ": $" + bot.balance); if (typeof bot.displayTxt.setFill === "function") { bot.displayTxt.setFill("#fff"); } } // Remove any win/lose floating text if present if (bot.winFloatTxt) { bot.winFloatTxt.destroy(); bot.winFloatTxt = null; } } } } // --- Bot logic: Cash out at random multiplier --- for (var b = 0; b < bots.length; b++) { var bot = bots[b]; if (bot.hasBet && !bot.hasCashedOut && !crashHappened) { // Each bot picks a random cashout multiplier at bet time, with 75% winrate hack if (!bot.targetCashout) { // Calculate the crash multiplier for this round var crashTicks = crashTick - roundStartTick; var crashMult = Math.max(1, Math.floor(100 * Math.pow(1.002, crashTicks * 2)) / 100); // 75% chance to win (cashout before crash), 25% to lose (cashout after crash) var willWin = Math.random() < 0.75; if (willWin) { // Pick a random cashout between 1.5x and just before crash multiplier (with margin) var minMult = 1.5; var maxMult = Math.max(minMult + 0.1, crashMult - 0.2); // leave margin to avoid edge if (maxMult < minMult) maxMult = minMult + 0.1; bot.targetCashout = minMult + Math.random() * (maxMult - minMult); // Clamp to max 5x for realism if (bot.targetCashout > 5) bot.targetCashout = 1.5 + Math.random() * 3.5; } else { // Pick a random cashout after crash (guaranteed lose) var minLose = crashMult + 0.1; var maxLose = Math.max(minLose + 0.1, crashMult + 2.0); bot.targetCashout = minLose + Math.random() * (maxLose - minLose); // Clamp to max 8x if (bot.targetCashout > 8) bot.targetCashout = 6 + Math.random() * 2; } } if (roundMultiplier >= bot.targetCashout) { // Cash out! bot.hasCashedOut = true; bot.cashoutMultiplier = roundMultiplier; bot.win = Math.floor(bot.betAmount * bot.cashoutMultiplier) - bot.betAmount; bot.balance += bot.betAmount + bot.win; // Show color-coded dot on graph at cashout point if (typeof graphBg !== "undefined") { var cashoutTick = ticksElapsed; var mult = Math.max(1, Math.floor(100 * Math.pow(1.002, cashoutTick * 2)) / 100); if (mult > graphBg.maxMultiplier) mult = graphBg.maxMultiplier; var px = graphBg.graphX + cashoutTick / graphBg.maxTicks * graphBg.graphW; var py = graphBg.graphY + graphBg.graphH - mult / graphBg.maxMultiplier * graphBg.graphH; var dot = LK.getAsset('chip', { anchorX: 0.5, anchorY: 0.5, x: px, y: py }); dot.width = 32; dot.height = 32; dot.tint = bot.dotColor; dot.alpha = 1; game.addChild(dot); // Track for removal next round if (!game.cashoutDots) game.cashoutDots = []; game.cashoutDots.push(dot); // Animate dot pop dot.scaleX = dot.scaleY = 0.5; tween(dot, { scaleX: 1, scaleY: 1 }, { duration: 300 }); // Optionally fade out after a while LK.setTimeout(function () { dot.alpha = 0.7; }, 1200); } // Show win status: green text, floating "+chips" if (bot.displayTxt) { bot.displayTxt.setText(bot.name + ": $" + bot.balance); if (typeof bot.displayTxt.setFill === "function") { bot.displayTxt.setFill("#3fdc5a"); } // Show floating "+chips" above bot balance if (bot.win > 0) { var floatTxt = new Text2("+" + bot.win + " chips", { size: 38, fill: 0x3FDC5A, stroke: "#222", strokeThickness: 4, fontWeight: "bold" }); floatTxt.anchor.set(0.5, 1); floatTxt.x = bot.displayTxt.x; floatTxt.y = bot.displayTxt.y - 60; game.addChild(floatTxt); bot.winFloatTxt = floatTxt; // Animate float up and fade out tween(floatTxt, { y: floatTxt.y - 60, alpha: 0 }, { duration: 1200, onFinish: function onFinish() { floatTxt.destroy(); } }); } } } } // If crash happened and bot didn't cash out, reset targetCashout if (crashHappened && !bot.hasCashedOut) { bot.targetCashout = null; } } // --- Bot logic: Handle crash (lose bet if not cashed out) --- if (crashHappened) { for (var b = 0; b < bots.length; b++) { var bot = bots[b]; if (bot.hasBet && !bot.hasCashedOut) { // Lost bet, nothing to add bot.betAmount = 0; bot.cashoutMultiplier = 0; bot.targetCashout = null; // Show lose status: red text and floating lose text if (bot.displayTxt) { bot.displayTxt.setText(bot.name + ": $" + bot.balance); if (typeof bot.displayTxt.setFill === "function") { bot.displayTxt.setFill("#ff3333"); } // Show floating "-100 chips" above bot balance var floatTxt = new Text2("-100 chips", { size: 38, fill: 0xFF3333, stroke: "#222", strokeThickness: 4, fontWeight: "bold" }); floatTxt.anchor.set(0.5, 1); floatTxt.x = bot.displayTxt.x; floatTxt.y = bot.displayTxt.y - 60; game.addChild(floatTxt); bot.winFloatTxt = floatTxt; // Animate float up and fade out tween(floatTxt, { y: floatTxt.y - 60, alpha: 0 }, { duration: 1200, onFinish: function onFinish() { floatTxt.destroy(); } }); } } } } // --- Eliminate player if balance is 0 --- if (balance <= 0 && !botWinner) { infoTxt.setText("You are eliminated!"); betBtn.visible = false; cashoutBtn.visible = false; // Optionally show lose popup LK.setTimeout(function () { LK.showGameOver(); }, 1200); return; } // --- Eliminate bots at 0 balance --- for (var b = 0; b < bots.length; b++) { if (bots[b].balance <= 0 && !bots[b].eliminated) { bots[b].eliminated = true; if (bots[b].displayTxt) { bots[b].displayTxt.setText(bots[b].name + ": ELIMINATED"); if (typeof bots[b].displayTxt.setFill === "function") { bots[b].displayTxt.setFill("#888"); } } } } // --- Player win detection --- if (!botWinner) { if (balance >= botWinTarget) { botWinner = "You"; infoTxt.setText("You win the game!"); LK.setTimeout(function () { LK.showYouWin(); }, 1800); } else { for (var b = 0; b < bots.length; b++) { if (bots[b].balance >= botWinTarget) { botWinner = bots[b].name; infoTxt.setText(botWinner + " wins the game!"); LK.setTimeout(function () { LK.showYouWin(); }, 1800); } } } } // Animate plane moving along the graph curve and make camera follow if (plane && !plane.crashed) { // Calculate the graph tip position for the current tick var graphX = graphBg.graphX; var graphY = graphBg.graphY; var graphW = graphBg.graphW; var graphH = graphBg.graphH; var maxTicks = graphBg.maxTicks; var maxMultiplier = graphBg.maxMultiplier; // Calculate current multiplier var mult = Math.max(1, Math.floor(100 * Math.pow(1.002, ticksElapsed * 2)) / 100); if (mult > maxMultiplier) mult = maxMultiplier; // Calculate the tip position on the graph var px = graphX + ticksElapsed / maxTicks * graphW; var py = graphY + graphH - mult / maxMultiplier * graphH; // Clamp px, py to graph area if (px < graphX) px = graphX; if (px > graphX + graphW) px = graphX + graphW; if (py < graphY) py = graphY; if (py > graphY + graphH) py = graphY + graphH; // Place plane at the tip of the graph, perfectly aligned to the curve // Adjust for plane's width/height so its nose is at the tip if (plane.children && plane.children.length > 0) { var planeGfx = plane.children[0]; // Assume plane is facing right, anchorX: 0.5, anchorY: 0.5 // Move plane so its front (right edge) is at (px, py) // Offset by half width to the left plane.x = px + planeGfx.width / 2 - planeGfx.width; // nose exactly at tip plane.y = py; // --- Calculate tangent angle for rotation --- // Use two points: current tick and a small delta before (or after if at start) var deltaT = 2; var t0 = Math.max(0, ticksElapsed - deltaT); var t1 = ticksElapsed; var mult0 = Math.max(1, Math.floor(100 * Math.pow(1.002, t0 * 2)) / 100); var mult1 = Math.max(1, Math.floor(100 * Math.pow(1.002, t1 * 2)) / 100); if (mult0 > maxMultiplier) mult0 = maxMultiplier; if (mult1 > maxMultiplier) mult1 = maxMultiplier; var px0 = graphX + t0 / maxTicks * graphW; var py0 = graphY + graphH - mult0 / maxMultiplier * graphH; var px1 = graphX + t1 / maxTicks * graphW; var py1 = graphY + graphH - mult1 / maxMultiplier * graphH; var dx = px1 - px0; var dy = py1 - py0; // If dx,dy is zero (shouldn't happen), fallback to 0 var angle = 0; if (dx !== 0 || dy !== 0) { angle = Math.atan2(dy, dx); } // Set plane rotation to match tangent planeGfx.rotation = angle; } else { plane.x = px; plane.y = py; } // Camera follow: shift the graphBg and all game elements so the plane stays visible and centered horizontally if possible // Calculate desired camera offset so plane is centered, but clamp so graph doesn't go off screen var desiredCenterX = 2048 / 2; var cameraOffsetX = desiredCenterX - px; // Clamp cameraOffsetX so graphBg doesn't go off screen var minOffsetX = 2048 - (graphX + graphW); // rightmost: graph right edge at screen right var maxOffsetX = -graphX; // leftmost: graph left edge at screen left if (cameraOffsetX < minOffsetX) cameraOffsetX = minOffsetX; if (cameraOffsetX > maxOffsetX) cameraOffsetX = maxOffsetX; // Apply camera offset to graphBg and plane (plane is already at px,py so just move graphBg) graphBg.x = cameraOffsetX; // If you have other game elements that should move with the camera, offset them by cameraOffsetX as well } // Draw/update graph curve if (typeof graphBg !== "undefined") { graphBg.drawCurve(ticksElapsed, crashHappened); } // Crash? if (LK.ticks >= crashTick && !crashHappened) { handleCrash(); } }; // --- Reset on Game Over (handled by LK) --- // --- Touchscreen: Prevent elements in top left 100x100 // --- Rainbow punchscale text at bottom center --- var winRaceTxt = new Text2("(The first to reach 2000 chips wins)", { size: 70, fill: 0xFF0000, stroke: "#222", strokeThickness: 6, fontWeight: "bold" }); winRaceTxt.anchor.set(0.5, 0); // Position just below the bet button, with a small margin winRaceTxt.x = betBtn.x; winRaceTxt.y = betBtn.y + betBtn.bg.height / 2 + 30; winRaceTxt.scaleX = winRaceTxt.scaleY = 0.7; game.addChild(winRaceTxt); // Animate rainbow color and punchscale var rainbowHue = 0; var punchScaleDir = 1; var punchScale = 1; game.updateWinRaceTxt = function () { // Rainbow color rainbowHue += 2; if (rainbowHue > 360) rainbowHue = 0; // Convert hue to rgb var h = rainbowHue / 60; var c = 1, x = 1 - Math.abs(h % 2 - 1); var r = 0, g = 0, b = 0; if (h < 1) { r = c; g = x; } else if (h < 2) { r = x; g = c; } else if (h < 3) { g = c; b = x; } else if (h < 4) { g = x; b = c; } else if (h < 5) { r = x; b = c; } else { r = c; b = x; } var rgb = Math.floor(r * 255) << 16 | Math.floor(g * 255) << 8 | Math.floor(b * 255); if (typeof winRaceTxt.setFill === "function") winRaceTxt.setFill(rgb); // Punchscale if (punchScaleDir === 1) { punchScale += 0.012; if (punchScale >= 1.13) punchScaleDir = -1; } else { punchScale -= 0.012; if (punchScale <= 1.0) punchScaleDir = 1; } winRaceTxt.scaleX = winRaceTxt.scaleY = punchScale; }; // Patch into main game update var _oldUpdate = game.update; game.update = function () { if (typeof game.updateWinRaceTxt === "function") game.updateWinRaceTxt(); if (_oldUpdate) _oldUpdate.apply(this, arguments); }; // --- Initial UI State --- betBtn.visible = true; cashoutBtn.visible = false; infoTxt.setText("Place your bet!"); multiplierTxt.setText("1.00x"); balanceTxt.setText("Balance: $" + balance); if (typeof balanceTxt.setFill === "function") balanceTxt.setFill(playerColor); betAmountTxt.setText("Bet: $" + betAmount);
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
balance: 1000
});
/****
* Classes
****/
// Button class
var GameButton = Container.expand(function () {
var self = Container.call(this);
self.bg = null;
self.label = null;
self.setAsset = function (assetId, labelText, color) {
if (self.bg) self.bg.destroy();
self.bg = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
if (color !== undefined) self.bg.tint = color;
if (self.label) self.label.destroy();
self.label = new Text2(labelText, {
size: 60,
fill: "#fff"
});
self.label.anchor.set(0.5, 0.5);
self.addChild(self.label);
};
self.setText = function (txt) {
if (self.label) self.label.setText(txt);
};
return self;
});
// GraphBackground class: draws the multiplier vs time graph as a grid and curve
var GraphBackground = Container.expand(function () {
var self = Container.call(this);
// Graph area (leave margin for axes)
self.graphX = 180;
self.graphY = 420;
self.graphW = 2048 - 360;
self.graphH = 900;
// For curve drawing
self.maxTicks = 600; // 10s at 60fps
self.maxMultiplier = 20;
// Draw grid lines and axes
self.drawGrid = function () {
// Remove previous children
while (self.children.length) self.children[0].destroy();
// Draw horizontal grid lines (multiplier)
for (var i = 1; i <= 5; i++) {
var y = self.graphY + self.graphH - i * self.graphH / 5;
var line = LK.getAsset('betBtn', {
anchorX: 0,
anchorY: 0,
x: self.graphX,
y: y
});
line.width = self.graphW;
line.height = 4;
line.tint = 0x2c3e50;
line.alpha = 0.18;
self.addChild(line);
// Multiplier label
var mult = (i * self.maxMultiplier / 5).toFixed(0) + "x";
var label = new Text2(mult, {
size: 38,
fill: "#fff",
stroke: "#222",
strokeThickness: 4,
fontWeight: "bold"
});
label.anchor.set(1, 0.5);
label.x = self.graphX - 18;
label.y = y + 2;
label.alpha = 0.7;
self.addChild(label);
}
// Draw vertical grid lines (time)
for (var j = 0; j <= 6; j++) {
var x = self.graphX + j * self.graphW / 6;
var vline = LK.getAsset('betBtn', {
anchorX: 0,
anchorY: 0,
x: x,
y: self.graphY
});
vline.width = 4;
vline.height = self.graphH;
vline.tint = 0x2c3e50;
vline.alpha = 0.18;
self.addChild(vline);
// Time label
var t = (j * self.maxTicks / 60 / 6).toFixed(1) + "s";
var tlabel = new Text2(t, {
size: 34,
fill: "#fff",
stroke: "#222",
strokeThickness: 4,
fontWeight: "bold"
});
tlabel.anchor.set(0.5, 0);
tlabel.x = x;
tlabel.y = self.graphY + self.graphH + 8;
tlabel.alpha = 0.7;
self.addChild(tlabel);
}
};
// Draw the curve for the current round
self.drawCurve = function (ticksElapsed, crashed) {
// Remove previous curve if any
if (self.curveNodes) {
for (var i = 0; i < self.curveNodes.length; i++) {
self.curveNodes[i].destroy();
}
}
self.curveNodes = [];
// Draw curve as small dots for each tick
var lastX = null,
lastY = null;
var maxDrawTicks = crashed ? ticksElapsed : Math.min(ticksElapsed, self.maxTicks);
for (var t = 0; t <= maxDrawTicks; t += 2) {
var mult = Math.max(1, Math.floor(100 * Math.pow(1.002, t * 2)) / 100);
if (mult > self.maxMultiplier) break;
var px = self.graphX + t / self.maxTicks * self.graphW;
var py = self.graphY + self.graphH - mult / self.maxMultiplier * self.graphH;
// Draw dot
var dot = LK.getAsset('chip', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py
});
dot.width = 18;
dot.height = 18;
dot.tint = crashed && t === maxDrawTicks ? 0xff3333 : 0x3a8ee6;
dot.alpha = crashed && t === maxDrawTicks ? 1 : 0.7;
self.addChild(dot);
self.curveNodes.push(dot);
// Optionally, connect with a line (simulate with thin box)
if (lastX !== null && lastY !== null) {
var dx = px - lastX;
var dy = py - lastY;
var dist = Math.sqrt(dx * dx + dy * dy);
var line = LK.getAsset('betBtn', {
anchorX: 0,
anchorY: 0.5,
x: lastX,
y: lastY
});
line.width = dist;
line.height = 8;
line.tint = 0x3a8ee6;
line.alpha = 0.5;
// Angle
line.rotation = Math.atan2(dy, dx);
self.addChild(line);
self.curveNodes.push(line);
}
lastX = px;
lastY = py;
}
};
self.drawGrid();
return self;
});
// Default balance
// Plane class
var Plane = Container.expand(function () {
var self = Container.call(this);
var planeGfx = self.attachAsset('plane', {
anchorX: 0.5,
anchorY: 0.5
});
self.crashed = false;
self.setCrashed = function () {
self.crashed = true;
// Show crash effect
var crash = LK.getAsset('crash', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
self.addChild(crash);
tween(crash, {
alpha: 0
}, {
duration: 700,
onFinish: function onFinish() {
crash.destroy();
}
});
// Fade out plane
tween(planeGfx, {
alpha: 0
}, {
duration: 700
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a2233
});
/****
* Game Code
****/
// New sounds added to the game
// Plane explosion sound effect
// Hype/energetic soundtrack for flight
// --- Game State Variables ---
// Plane (the flying object)
// Bet button
// Cashout button
// Crash explosion
// Virtual chip
// --- Game State Variables ---
// Reset all balances to 1000 on page refresh
var balance = 1000;
storage.balance = 1000;
var betAmount = 100;
var minBet = 10;
var maxBet = 1000;
var betStep = 10;
var inRound = false;
var hasBet = false;
var hasCashedOut = false;
var roundMultiplier = 1.00;
var roundStartTick = 0;
var crashTick = 0;
var cashoutMultiplier = 0;
var plane = null;
var crashHappened = false;
var lastTick = 0;
var betBtn = null;
var cashoutBtn = null;
var betAmountTxt = null;
var balanceTxt = null;
var multiplierTxt = null;
var infoTxt = null;
var chipIcon = null;
// --- Player and Bot Colors ---
var playerColor = 0xFFE066; // gold/yellow for player
// Bot color codes: Jack (blue), Arthur (pink), John (orange)
var botColors = [0x3a8ee6, 0xff69b4, 0xffa500]; // blue, pink, orange
var botNameColors = [0x3a8ee6, 0xff69b4, 0xffa500]; // name text color matches dot color
// --- Bot State ---
var bots = [{
name: "Jack",
balance: 1000,
betAmount: 0,
hasBet: false,
hasCashedOut: false,
cashoutMultiplier: 0,
displayTxt: null,
chipIcon: null,
win: 0,
color: botColors[0],
nameColor: botNameColors[0],
dotColor: botColors[0]
}, {
name: "Arthur",
balance: 1000,
betAmount: 0,
hasBet: false,
hasCashedOut: false,
cashoutMultiplier: 0,
displayTxt: null,
chipIcon: null,
win: 0,
color: botColors[1],
nameColor: botNameColors[1],
dotColor: botColors[1]
}, {
name: "John",
balance: 1000,
betAmount: 0,
hasBet: false,
hasCashedOut: false,
cashoutMultiplier: 0,
displayTxt: null,
chipIcon: null,
win: 0,
color: botColors[2],
nameColor: botNameColors[2],
dotColor: botColors[2]
}];
// Set win target for all to 2000
var botWinTarget = 2000;
var botWinner = null;
// --- GUI Elements ---
// --- Graph Background ---
var graphBg = new GraphBackground();
game.addChild(graphBg);
// --- GUI Elements ---
multiplierTxt = new Text2("1.00x", {
size: 180,
fill: 0xFFE066,
stroke: "#222",
strokeThickness: 10,
dropShadow: true,
dropShadowColor: "#000",
dropShadowDistance: 8,
dropShadowAngle: Math.PI / 2,
dropShadowBlur: 8,
fontWeight: "bold"
});
multiplierTxt.anchor.set(0.5, 0);
multiplierTxt.y = 40;
LK.gui.top.addChild(multiplierTxt);
// Info text (centered, below multiplier, with shadow and spacing)
infoTxt = new Text2("Place your bet!", {
size: 70,
fill: "#fff",
stroke: "#222",
strokeThickness: 6,
dropShadow: true,
dropShadowColor: "#000",
dropShadowDistance: 4,
dropShadowAngle: Math.PI / 2,
dropShadowBlur: 6,
fontWeight: "bold"
});
infoTxt.anchor.set(0.5, 0);
infoTxt.y = multiplierTxt.y + multiplierTxt.height + 20;
LK.gui.top.addChild(infoTxt);
balanceTxt = new Text2("Balance: $" + balance, {
size: 60,
fill: playerColor,
stroke: "#222",
strokeThickness: 5,
fontWeight: "bold"
});
balanceTxt.anchor.set(0.5, 1);
// Bet amount display (larger, bolder)
betAmountTxt = new Text2("Bet: $" + betAmount, {
size: 90,
fill: "#fff",
stroke: "#222",
strokeThickness: 6,
fontWeight: "bold"
});
betAmountTxt.anchor.set(1, 0.5);
betAmountTxt.x = 2048 - 60;
LK.gui.topRight.addChild(betAmountTxt);
// --- Bet Button ---
betBtn = new GameButton();
betBtn.setAsset('betBtn', "BET", 0x2ecc40);
betBtn.bg.width = 520;
betBtn.bg.height = 180;
betBtn.bg.radius = 40;
betBtn.bg.dropShadow = true;
betBtn.bg.dropShadowColor = 0x222222;
betBtn.bg.dropShadowDistance = 8;
betBtn.bg.dropShadowAngle = Math.PI / 2;
betBtn.bg.dropShadowBlur = 8;
// Centered horizontally, near bottom
betBtn.x = 2048 / 2;
betBtn.y = 2732 - 180;
betBtn.anchorX = 0.5;
betBtn.anchorY = 0.5;
game.addChild(betBtn);
// --- Balance text and chip icon above bet button (centered) ---
// Center balance text horizontally above bet button
balanceTxt.anchor.set(0.5, 1);
balanceTxt.x = betBtn.x;
balanceTxt.y = betBtn.y - betBtn.bg.height / 2 - 30;
// --- Player win/lose indicator text (above balance) ---
var playerWinFloatTxt = null;
// Center balance text horizontally above bet button
game.addChild(balanceTxt);
// --- Add bot balance and chip display above bet button ---
var botDisplayYOffset = 60;
for (var i = 0; i < bots.length; i++) {
// Balance text for bot
var botTxt = new Text2(bots[i].name + ": $" + bots[i].balance, {
size: 44,
fill: bots[i].nameColor,
stroke: "#222",
strokeThickness: 4,
fontWeight: "bold"
});
botTxt.anchor.set(0.5, 1);
botTxt.x = betBtn.x - 400 + i * 400;
botTxt.y = balanceTxt.y - 120;
bots[i].displayTxt = botTxt;
game.addChild(botTxt);
}
// Update bet button text to "BET 100"
betBtn.setText("BET " + betAmount);
// --- Cashout Button ---
cashoutBtn = new GameButton();
cashoutBtn.setAsset('cashoutBtn', "CASHOUT", 0xe67e22);
cashoutBtn.bg.width = 520;
cashoutBtn.bg.height = 180;
cashoutBtn.bg.radius = 40;
cashoutBtn.bg.dropShadow = true;
cashoutBtn.bg.dropShadowColor = 0x222222;
cashoutBtn.bg.dropShadowDistance = 8;
cashoutBtn.bg.dropShadowAngle = Math.PI / 2;
cashoutBtn.bg.dropShadowBlur = 8;
// Centered horizontally, same Y as betBtn
cashoutBtn.x = 2048 / 2;
cashoutBtn.y = 2732 - 180;
cashoutBtn.anchorX = 0.5;
cashoutBtn.anchorY = 0.5;
game.addChild(cashoutBtn);
cashoutBtn.visible = false;
// --- Bet Amount Adjust (plus/minus buttons) ---
var betMinusBtn = new GameButton();
betMinusBtn.setAsset('betBtn', "-", 0x16a085);
betMinusBtn.bg.width = 120;
betMinusBtn.bg.height = 120;
betMinusBtn.bg.radius = 60;
betMinusBtn.x = 2048 - 420;
betMinusBtn.y = 180;
LK.gui.topRight.addChild(betMinusBtn);
var betPlusBtn = new GameButton();
betPlusBtn.setAsset('betBtn', "+", 0x16a085);
betPlusBtn.bg.width = 120;
betPlusBtn.bg.height = 120;
betPlusBtn.bg.radius = 60;
betPlusBtn.x = 2048 - 180;
betPlusBtn.y = 180;
LK.gui.topRight.addChild(betPlusBtn);
betMinusBtn.down = function (x, y, obj) {
if (inRound) return;
betAmount -= betStep;
if (betAmount < minBet) betAmount = maxBet;
betAmountTxt.setText("Bet: $" + betAmount);
};
betPlusBtn.down = function (x, y, obj) {
if (inRound) return;
betAmount += betStep;
if (betAmount > maxBet) betAmount = minBet;
betAmountTxt.setText("Bet: $" + betAmount);
};
// --- Bet Button Handler ---
betBtn.down = function (x, y, obj) {
if (inRound || hasBet) return;
if (balance < betAmount) {
infoTxt.setText("Not enough balance!");
return;
}
hasBet = true;
infoTxt.setText("Waiting for takeoff...");
betBtn.setText("WAIT");
betBtn.bg.tint = 0xaaaaaa;
// Deduct bet immediately
balance -= betAmount;
storage.balance = balance;
balanceTxt.setText("Balance: $" + balance);
if (typeof balanceTxt.setFill === "function") balanceTxt.setFill(playerColor);
// Start round after short delay
LK.setTimeout(startRound, 900);
};
// --- Cashout Button Handler ---
cashoutBtn.down = function (x, y, obj) {
if (!inRound || !hasBet || hasCashedOut || crashHappened) return;
hasCashedOut = true;
cashoutMultiplier = roundMultiplier;
var win = Math.floor(betAmount * cashoutMultiplier);
balance += win;
storage.balance = balance;
balanceTxt.setText("Balance: $" + balance);
if (typeof balanceTxt.setFill === "function") balanceTxt.setFill(playerColor);
infoTxt.setText("You cashed out: $" + win + " (" + cashoutMultiplier.toFixed(2) + "x)");
// Show floating win text above balance
if (playerWinFloatTxt) {
playerWinFloatTxt.destroy();
playerWinFloatTxt = null;
}
playerWinFloatTxt = new Text2("+" + win + " chips", {
size: 48,
fill: 0x3FDC5A,
stroke: "#222",
strokeThickness: 5,
fontWeight: "bold"
});
playerWinFloatTxt.anchor.set(0.5, 1);
playerWinFloatTxt.x = balanceTxt.x;
playerWinFloatTxt.y = balanceTxt.y - 60;
game.addChild(playerWinFloatTxt);
tween(playerWinFloatTxt, {
y: playerWinFloatTxt.y - 60,
alpha: 0
}, {
duration: 1200,
onFinish: function onFinish() {
if (playerWinFloatTxt) {
playerWinFloatTxt.destroy();
playerWinFloatTxt = null;
}
}
});
// Show color-coded dot on graph at cashout point
if (typeof graphBg !== "undefined") {
var cashoutTick = LK.ticks - roundStartTick;
var mult = Math.max(1, Math.floor(100 * Math.pow(1.002, cashoutTick * 2)) / 100);
if (mult > graphBg.maxMultiplier) mult = graphBg.maxMultiplier;
var px = graphBg.graphX + cashoutTick / graphBg.maxTicks * graphBg.graphW;
var py = graphBg.graphY + graphBg.graphH - mult / graphBg.maxMultiplier * graphBg.graphH;
var dot = LK.getAsset('chip', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py
});
dot.width = 32;
dot.height = 32;
dot.tint = playerColor;
dot.alpha = 1;
game.addChild(dot);
// Track for removal next round
if (!game.cashoutDots) game.cashoutDots = [];
game.cashoutDots.push(dot);
// Animate dot pop
dot.scaleX = dot.scaleY = 0.5;
tween(dot, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
// Optionally fade out after a while
LK.setTimeout(function () {
dot.alpha = 0.7;
}, 1200);
}
cashoutBtn.visible = false;
betBtn.visible = false;
// Animate plane off screen
if (plane) {
tween(plane, {
y: -200
}, {
duration: 800,
easing: tween.cubicOut,
onFinish: function onFinish() {
endRound();
}
});
} else {
endRound();
}
};
// --- Start Round ---
function startRound() {
inRound = true;
crashHappened = false;
hasCashedOut = false;
cashoutMultiplier = 0;
roundMultiplier = 1.00;
roundStartTick = LK.ticks;
// Remove all previous player/bot cashout dots
if (typeof game.cashoutDots !== "undefined" && game.cashoutDots && game.cashoutDots.length) {
for (var i = 0; i < game.cashoutDots.length; i++) {
if (game.cashoutDots[i] && typeof game.cashoutDots[i].destroy === "function") {
game.cashoutDots[i].destroy();
}
}
game.cashoutDots = [];
} else {
game.cashoutDots = [];
}
// Remove player win/lose indicator if present
if (playerWinFloatTxt) {
playerWinFloatTxt.destroy();
playerWinFloatTxt = null;
}
// Random crash time: between 2s and 8s (120-480 ticks)
crashTick = roundStartTick + 120 + Math.floor(Math.random() * 360);
// Place plane at bottom center
if (plane) plane.destroy();
plane = new Plane();
plane.x = 2048 / 2;
plane.y = 2732 - 600;
game.addChild(plane);
// Animate plane takeoff
tween(plane, {
y: 2732 / 2
}, {
duration: 1200,
easing: tween.cubicOut
});
// Start hype soundtrack
LK.playMusic('hype_flight', {
fade: {
start: 0,
end: 1,
duration: 600
}
});
// UI
betBtn.visible = false;
cashoutBtn.visible = true;
cashoutBtn.setText("CASHOUT");
cashoutBtn.bg.tint = 0xe67e22;
infoTxt.setText("Flying... Tap CASHOUT to win!");
multiplierTxt.setText("1.00x");
}
// --- End Round ---
function endRound() {
inRound = false;
hasBet = false;
hasCashedOut = false;
betBtn.visible = true;
betBtn.setText("BET");
betBtn.bg.tint = 0x2ecc40;
cashoutBtn.visible = false;
if (plane) {
plane.destroy();
plane = null;
}
// If player lost, show info
if (crashHappened && !cashoutMultiplier) {
infoTxt.setText("Crashed! You lost $" + betAmount);
} else if (!crashHappened && cashoutMultiplier) {
infoTxt.setText("You won $" + Math.floor(betAmount * cashoutMultiplier) + "!");
} else {
infoTxt.setText("Place your bet!");
}
// Reset multiplier display
multiplierTxt.setText("1.00x");
// Update bet amount display
betAmountTxt.setText("Bet: $" + betAmount);
}
// --- Crash Handler ---
function handleCrash() {
crashHappened = true;
// Stop hype soundtrack and play explosion sound
LK.stopMusic();
LK.getSound('plane_explode').play();
if (plane) plane.setCrashed();
cashoutBtn.visible = false;
betBtn.visible = false;
if (!hasCashedOut) {
cashoutMultiplier = 0;
// Show crash info
infoTxt.setText("Crashed! You lost $" + betAmount);
// Show floating lose text above balance
if (playerWinFloatTxt) {
playerWinFloatTxt.destroy();
playerWinFloatTxt = null;
}
playerWinFloatTxt = new Text2("-" + betAmount + " chips", {
size: 48,
fill: 0xFF3333,
stroke: "#222",
strokeThickness: 5,
fontWeight: "bold"
});
playerWinFloatTxt.anchor.set(0.5, 1);
playerWinFloatTxt.x = balanceTxt.x;
playerWinFloatTxt.y = balanceTxt.y - 60;
game.addChild(playerWinFloatTxt);
tween(playerWinFloatTxt, {
y: playerWinFloatTxt.y - 60,
alpha: 0
}, {
duration: 1200,
onFinish: function onFinish() {
if (playerWinFloatTxt) {
playerWinFloatTxt.destroy();
playerWinFloatTxt = null;
}
}
});
// End round after short delay
LK.setTimeout(endRound, 1200);
} else {
// Already cashed out, just end round
LK.setTimeout(endRound, 800);
}
}
// --- Main Game Update ---
game.update = function () {
if (!inRound) {
// Reset bot round state if not in round
for (var b = 0; b < bots.length; b++) {
bots[b].hasBet = false;
bots[b].hasCashedOut = false;
bots[b].betAmount = 0;
bots[b].cashoutMultiplier = 0;
bots[b].win = 0;
}
return;
}
// Calculate multiplier: Exponential growth, e.g. 1.00x to ~20x in 8s
var ticksElapsed = LK.ticks - roundStartTick;
// Multiplier formula: 1.002^(ticksElapsed*2) for smooth curve
roundMultiplier = Math.max(1, Math.floor(100 * Math.pow(1.002, ticksElapsed * 2)) / 100);
multiplierTxt.setText(roundMultiplier.toFixed(2) + "x");
// --- Bot logic: Place bets at start of round ---
for (var b = 0; b < bots.length; b++) {
var bot = bots[b];
// Place bet if not already bet and round just started
if (!bot.hasBet && ticksElapsed < 10) {
// All bots always bet 100 chips if enough balance
var botBet = 100;
if (bot.balance >= botBet) {
bot.betAmount = botBet;
bot.balance -= botBet;
bot.hasBet = true;
bot.hasCashedOut = false;
bot.cashoutMultiplier = 0;
bot.win = 0;
// Reset win/lose status color and text
if (bot.displayTxt) {
bot.displayTxt.setText(bot.name + ": $" + bot.balance);
if (typeof bot.displayTxt.setFill === "function") {
bot.displayTxt.setFill("#fff");
}
}
// Remove any win/lose floating text if present
if (bot.winFloatTxt) {
bot.winFloatTxt.destroy();
bot.winFloatTxt = null;
}
}
}
}
// --- Bot logic: Cash out at random multiplier ---
for (var b = 0; b < bots.length; b++) {
var bot = bots[b];
if (bot.hasBet && !bot.hasCashedOut && !crashHappened) {
// Each bot picks a random cashout multiplier at bet time, with 75% winrate hack
if (!bot.targetCashout) {
// Calculate the crash multiplier for this round
var crashTicks = crashTick - roundStartTick;
var crashMult = Math.max(1, Math.floor(100 * Math.pow(1.002, crashTicks * 2)) / 100);
// 75% chance to win (cashout before crash), 25% to lose (cashout after crash)
var willWin = Math.random() < 0.75;
if (willWin) {
// Pick a random cashout between 1.5x and just before crash multiplier (with margin)
var minMult = 1.5;
var maxMult = Math.max(minMult + 0.1, crashMult - 0.2); // leave margin to avoid edge
if (maxMult < minMult) maxMult = minMult + 0.1;
bot.targetCashout = minMult + Math.random() * (maxMult - minMult);
// Clamp to max 5x for realism
if (bot.targetCashout > 5) bot.targetCashout = 1.5 + Math.random() * 3.5;
} else {
// Pick a random cashout after crash (guaranteed lose)
var minLose = crashMult + 0.1;
var maxLose = Math.max(minLose + 0.1, crashMult + 2.0);
bot.targetCashout = minLose + Math.random() * (maxLose - minLose);
// Clamp to max 8x
if (bot.targetCashout > 8) bot.targetCashout = 6 + Math.random() * 2;
}
}
if (roundMultiplier >= bot.targetCashout) {
// Cash out!
bot.hasCashedOut = true;
bot.cashoutMultiplier = roundMultiplier;
bot.win = Math.floor(bot.betAmount * bot.cashoutMultiplier) - bot.betAmount;
bot.balance += bot.betAmount + bot.win;
// Show color-coded dot on graph at cashout point
if (typeof graphBg !== "undefined") {
var cashoutTick = ticksElapsed;
var mult = Math.max(1, Math.floor(100 * Math.pow(1.002, cashoutTick * 2)) / 100);
if (mult > graphBg.maxMultiplier) mult = graphBg.maxMultiplier;
var px = graphBg.graphX + cashoutTick / graphBg.maxTicks * graphBg.graphW;
var py = graphBg.graphY + graphBg.graphH - mult / graphBg.maxMultiplier * graphBg.graphH;
var dot = LK.getAsset('chip', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py
});
dot.width = 32;
dot.height = 32;
dot.tint = bot.dotColor;
dot.alpha = 1;
game.addChild(dot);
// Track for removal next round
if (!game.cashoutDots) game.cashoutDots = [];
game.cashoutDots.push(dot);
// Animate dot pop
dot.scaleX = dot.scaleY = 0.5;
tween(dot, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
// Optionally fade out after a while
LK.setTimeout(function () {
dot.alpha = 0.7;
}, 1200);
}
// Show win status: green text, floating "+chips"
if (bot.displayTxt) {
bot.displayTxt.setText(bot.name + ": $" + bot.balance);
if (typeof bot.displayTxt.setFill === "function") {
bot.displayTxt.setFill("#3fdc5a");
}
// Show floating "+chips" above bot balance
if (bot.win > 0) {
var floatTxt = new Text2("+" + bot.win + " chips", {
size: 38,
fill: 0x3FDC5A,
stroke: "#222",
strokeThickness: 4,
fontWeight: "bold"
});
floatTxt.anchor.set(0.5, 1);
floatTxt.x = bot.displayTxt.x;
floatTxt.y = bot.displayTxt.y - 60;
game.addChild(floatTxt);
bot.winFloatTxt = floatTxt;
// Animate float up and fade out
tween(floatTxt, {
y: floatTxt.y - 60,
alpha: 0
}, {
duration: 1200,
onFinish: function onFinish() {
floatTxt.destroy();
}
});
}
}
}
}
// If crash happened and bot didn't cash out, reset targetCashout
if (crashHappened && !bot.hasCashedOut) {
bot.targetCashout = null;
}
}
// --- Bot logic: Handle crash (lose bet if not cashed out) ---
if (crashHappened) {
for (var b = 0; b < bots.length; b++) {
var bot = bots[b];
if (bot.hasBet && !bot.hasCashedOut) {
// Lost bet, nothing to add
bot.betAmount = 0;
bot.cashoutMultiplier = 0;
bot.targetCashout = null;
// Show lose status: red text and floating lose text
if (bot.displayTxt) {
bot.displayTxt.setText(bot.name + ": $" + bot.balance);
if (typeof bot.displayTxt.setFill === "function") {
bot.displayTxt.setFill("#ff3333");
}
// Show floating "-100 chips" above bot balance
var floatTxt = new Text2("-100 chips", {
size: 38,
fill: 0xFF3333,
stroke: "#222",
strokeThickness: 4,
fontWeight: "bold"
});
floatTxt.anchor.set(0.5, 1);
floatTxt.x = bot.displayTxt.x;
floatTxt.y = bot.displayTxt.y - 60;
game.addChild(floatTxt);
bot.winFloatTxt = floatTxt;
// Animate float up and fade out
tween(floatTxt, {
y: floatTxt.y - 60,
alpha: 0
}, {
duration: 1200,
onFinish: function onFinish() {
floatTxt.destroy();
}
});
}
}
}
}
// --- Eliminate player if balance is 0 ---
if (balance <= 0 && !botWinner) {
infoTxt.setText("You are eliminated!");
betBtn.visible = false;
cashoutBtn.visible = false;
// Optionally show lose popup
LK.setTimeout(function () {
LK.showGameOver();
}, 1200);
return;
}
// --- Eliminate bots at 0 balance ---
for (var b = 0; b < bots.length; b++) {
if (bots[b].balance <= 0 && !bots[b].eliminated) {
bots[b].eliminated = true;
if (bots[b].displayTxt) {
bots[b].displayTxt.setText(bots[b].name + ": ELIMINATED");
if (typeof bots[b].displayTxt.setFill === "function") {
bots[b].displayTxt.setFill("#888");
}
}
}
}
// --- Player win detection ---
if (!botWinner) {
if (balance >= botWinTarget) {
botWinner = "You";
infoTxt.setText("You win the game!");
LK.setTimeout(function () {
LK.showYouWin();
}, 1800);
} else {
for (var b = 0; b < bots.length; b++) {
if (bots[b].balance >= botWinTarget) {
botWinner = bots[b].name;
infoTxt.setText(botWinner + " wins the game!");
LK.setTimeout(function () {
LK.showYouWin();
}, 1800);
}
}
}
}
// Animate plane moving along the graph curve and make camera follow
if (plane && !plane.crashed) {
// Calculate the graph tip position for the current tick
var graphX = graphBg.graphX;
var graphY = graphBg.graphY;
var graphW = graphBg.graphW;
var graphH = graphBg.graphH;
var maxTicks = graphBg.maxTicks;
var maxMultiplier = graphBg.maxMultiplier;
// Calculate current multiplier
var mult = Math.max(1, Math.floor(100 * Math.pow(1.002, ticksElapsed * 2)) / 100);
if (mult > maxMultiplier) mult = maxMultiplier;
// Calculate the tip position on the graph
var px = graphX + ticksElapsed / maxTicks * graphW;
var py = graphY + graphH - mult / maxMultiplier * graphH;
// Clamp px, py to graph area
if (px < graphX) px = graphX;
if (px > graphX + graphW) px = graphX + graphW;
if (py < graphY) py = graphY;
if (py > graphY + graphH) py = graphY + graphH;
// Place plane at the tip of the graph, perfectly aligned to the curve
// Adjust for plane's width/height so its nose is at the tip
if (plane.children && plane.children.length > 0) {
var planeGfx = plane.children[0];
// Assume plane is facing right, anchorX: 0.5, anchorY: 0.5
// Move plane so its front (right edge) is at (px, py)
// Offset by half width to the left
plane.x = px + planeGfx.width / 2 - planeGfx.width; // nose exactly at tip
plane.y = py;
// --- Calculate tangent angle for rotation ---
// Use two points: current tick and a small delta before (or after if at start)
var deltaT = 2;
var t0 = Math.max(0, ticksElapsed - deltaT);
var t1 = ticksElapsed;
var mult0 = Math.max(1, Math.floor(100 * Math.pow(1.002, t0 * 2)) / 100);
var mult1 = Math.max(1, Math.floor(100 * Math.pow(1.002, t1 * 2)) / 100);
if (mult0 > maxMultiplier) mult0 = maxMultiplier;
if (mult1 > maxMultiplier) mult1 = maxMultiplier;
var px0 = graphX + t0 / maxTicks * graphW;
var py0 = graphY + graphH - mult0 / maxMultiplier * graphH;
var px1 = graphX + t1 / maxTicks * graphW;
var py1 = graphY + graphH - mult1 / maxMultiplier * graphH;
var dx = px1 - px0;
var dy = py1 - py0;
// If dx,dy is zero (shouldn't happen), fallback to 0
var angle = 0;
if (dx !== 0 || dy !== 0) {
angle = Math.atan2(dy, dx);
}
// Set plane rotation to match tangent
planeGfx.rotation = angle;
} else {
plane.x = px;
plane.y = py;
}
// Camera follow: shift the graphBg and all game elements so the plane stays visible and centered horizontally if possible
// Calculate desired camera offset so plane is centered, but clamp so graph doesn't go off screen
var desiredCenterX = 2048 / 2;
var cameraOffsetX = desiredCenterX - px;
// Clamp cameraOffsetX so graphBg doesn't go off screen
var minOffsetX = 2048 - (graphX + graphW); // rightmost: graph right edge at screen right
var maxOffsetX = -graphX; // leftmost: graph left edge at screen left
if (cameraOffsetX < minOffsetX) cameraOffsetX = minOffsetX;
if (cameraOffsetX > maxOffsetX) cameraOffsetX = maxOffsetX;
// Apply camera offset to graphBg and plane (plane is already at px,py so just move graphBg)
graphBg.x = cameraOffsetX;
// If you have other game elements that should move with the camera, offset them by cameraOffsetX as well
}
// Draw/update graph curve
if (typeof graphBg !== "undefined") {
graphBg.drawCurve(ticksElapsed, crashHappened);
}
// Crash?
if (LK.ticks >= crashTick && !crashHappened) {
handleCrash();
}
};
// --- Reset on Game Over (handled by LK) ---
// --- Touchscreen: Prevent elements in top left 100x100
// --- Rainbow punchscale text at bottom center ---
var winRaceTxt = new Text2("(The first to reach 2000 chips wins)", {
size: 70,
fill: 0xFF0000,
stroke: "#222",
strokeThickness: 6,
fontWeight: "bold"
});
winRaceTxt.anchor.set(0.5, 0);
// Position just below the bet button, with a small margin
winRaceTxt.x = betBtn.x;
winRaceTxt.y = betBtn.y + betBtn.bg.height / 2 + 30;
winRaceTxt.scaleX = winRaceTxt.scaleY = 0.7;
game.addChild(winRaceTxt);
// Animate rainbow color and punchscale
var rainbowHue = 0;
var punchScaleDir = 1;
var punchScale = 1;
game.updateWinRaceTxt = function () {
// Rainbow color
rainbowHue += 2;
if (rainbowHue > 360) rainbowHue = 0;
// Convert hue to rgb
var h = rainbowHue / 60;
var c = 1,
x = 1 - Math.abs(h % 2 - 1);
var r = 0,
g = 0,
b = 0;
if (h < 1) {
r = c;
g = x;
} else if (h < 2) {
r = x;
g = c;
} else if (h < 3) {
g = c;
b = x;
} else if (h < 4) {
g = x;
b = c;
} else if (h < 5) {
r = x;
b = c;
} else {
r = c;
b = x;
}
var rgb = Math.floor(r * 255) << 16 | Math.floor(g * 255) << 8 | Math.floor(b * 255);
if (typeof winRaceTxt.setFill === "function") winRaceTxt.setFill(rgb);
// Punchscale
if (punchScaleDir === 1) {
punchScale += 0.012;
if (punchScale >= 1.13) punchScaleDir = -1;
} else {
punchScale -= 0.012;
if (punchScale <= 1.0) punchScaleDir = 1;
}
winRaceTxt.scaleX = winRaceTxt.scaleY = punchScale;
};
// Patch into main game update
var _oldUpdate = game.update;
game.update = function () {
if (typeof game.updateWinRaceTxt === "function") game.updateWinRaceTxt();
if (_oldUpdate) _oldUpdate.apply(this, arguments);
};
// --- Initial UI State ---
betBtn.visible = true;
cashoutBtn.visible = false;
infoTxt.setText("Place your bet!");
multiplierTxt.setText("1.00x");
balanceTxt.setText("Balance: $" + balance);
if (typeof balanceTxt.setFill === "function") balanceTxt.setFill(playerColor);
betAmountTxt.setText("Bet: $" + betAmount);