User prompt
ekranı biraz aşşağı taşı
User prompt
atların boyutunu ve bet 10 butonların boyutunu tekrar %10 büyüt
User prompt
bet 10 butonlarını da %20 büyüt
User prompt
atların boyutunu %20 kulvarların genişliğini %15 artır
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'number')' in or related to this line: 'betHorseNum = tvHorses[i].number;' Line Number: 493
Code edit (1 edits merged)
Please save this source code
User prompt
Horse Race TV & My Champion
Initial prompt
2 farklı oynanışlı bir oyun istiyorum tek ekranda herşey. oyun ingilizce. üste bir at yarışı ekranı televizyondan seyrediyormuş gibi olcak başta kapalı ama yarış başladığında 6 kulvarlı bir pistte 6 rasgele at yarışacak. atlarda hız dayanıklılık ve şans statları var 20 at olsun bunlar sabit özellikli özelliklerini rasgele ata 1 ile 100 arası. sabit olacak özellikleri oynunun kodlarına sabit kaydet. atlar rasgele 6 sı yarışacak yarış başlamadan önce televizyonun alt sağ kısımda hangi atların yarışacağı yazacak birini seçtiğimizde kazanırsa 10 altın kazanıcağız bir atı seçip sağ kısımda atların altındaki oyna tuşuna tıklayınca . bir de bizim atımız olacak televizyon alt sol kısımda solda onun resmi gözükecek altında onun özellikleri yazcak altalta. hız dayanıklılık şans. hepsi 10 ile başlıycak. atımıza tıkladığımızda 20 altın varsa harcayip rasgele bir özeliği 1 artıcak.yani hem atımızı geliştirebileceğiz hem de sadece bahis oynayabileceğiz. sağ kısımda oyna tuşunun altında da katıl tuşuna basınca 6. at çıkacak yerine bizim atımız girecek. atımız kazanırsa 50 altın kazanacak.maç sistemi 6 at televizyon kısmında soldan sağa ilerliycek.hızına göre ilerkerken ekranın yarısında hızı % dayanıklılık statı kadar düşecek (100 hız 30 dayanıklılığı varsa 30 hızda gidecek) şans da ekranın yarısında % şans statı kadar şansla ekranın %10 u kadar bir ilerleme sağlıycak ( 90 ise %90 şansla çalışacak)
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Horse class for both TV and player horses var Horse = Container.expand(function () { var self = Container.call(this); // Asset: Each horse gets a colored ellipse as body and a number label var colorList = [0x8e44ad, 0xe67e22, 0x16a085, 0xc0392b, 0x2980b9, 0xf1c40f, 0x2ecc71, 0x34495e, 0xd35400, 0x1abc9c, 0x7f8c8d, 0x27ae60, 0x9b59b6, 0x2c3e50, 0xe74c3c, 0x3498db, 0xf39c12, 0x95a5a6, 0x22313f, 0x6ab04c]; // colorIndex is set on instance self.colorIndex = 0; self.number = 1; // 1-20 self.isPlayer = false; // true if this is the player's horse // Stats self.speed = 10; self.stamina = 10; self.luck = 10; // For race self.lane = 0; // 0-5 self.progress = 0; // 0-1 self.energy = 0; // stamina left self.finished = false; self.finishTime = 0; // Visuals self.body = self.attachAsset('horseBody_' + self.colorIndex, { anchorX: 0.5, anchorY: 0.5, width: 90 * 1.32, height: 60 * 1.32, color: colorList[self.colorIndex % colorList.length], shape: 'ellipse' }); self.label = new Text2(self.number + '', { size: 40, fill: "#fff" }); self.label.anchor.set(0.5, 0.5); self.addChild(self.label); // For player horse, show "YOU" label self.youLabel = null; if (self.isPlayer) { self.youLabel = new Text2("YOU", { size: 32, fill: "#fff" }); self.youLabel.anchor.set(0.5, 0); self.youLabel.y = 40; self.addChild(self.youLabel); } // Set color and number self.setHorse = function (colorIndex, number, isPlayer) { self.colorIndex = colorIndex; self.number = number; self.isPlayer = !!isPlayer; self.body.tint = colorList[self.colorIndex % colorList.length]; self.label.setText(self.number + ''); if (self.isPlayer && !self.youLabel) { self.youLabel = new Text2("YOU", { size: 32, fill: "#fff" }); self.youLabel.anchor.set(0.5, 0); self.youLabel.y = 40; self.addChild(self.youLabel); } if (!self.isPlayer && self.youLabel) { self.removeChild(self.youLabel); self.youLabel = null; } }; // Set stats self.setStats = function (speed, stamina, luck) { self.speed = speed; self.stamina = stamina; self.luck = luck; }; // Reset for race self.resetRace = function () { self.progress = 0; self.energy = self.stamina; self.finished = false; self.finishTime = 0; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a1a }); /**** * Game Code ****/ // --- GLOBALS --- var NUM_HORSES = 40; var NUM_LANES = 6; // Set race track to 90% and bet area to 10% of available width (2048px - TV_LEFT - margin) var TV_LEFT = 120; var TOTAL_WIDTH = 2048 - TV_LEFT - 60; // 60px right margin var BET_AREA_WIDTH = Math.floor(TOTAL_WIDTH * 0.10); // 10% for bet area var RACE_LENGTH = Math.floor(TOTAL_WIDTH * 0.90); // 90% for race track var TV_TOP = 220; // px from top (moved down by 100) var TV_HEIGHT = 700; // px, height of TV race area var TV_WIDTH = RACE_LENGTH; // TV board is now just the race track width var LANE_HEIGHT = 100 * 1.15; var HORSE_SIZE = 90; var PLAYER_HORSE_Y = 2000; // moved up by 200 var PLAYER_HORSE_X = 220; var GOLD_START = 100; var BET_COST = 10; var UPGRADE_COST = 20; var BET_PRIZE = 40; var PLAYER_PRIZE = 100; // --- STATE --- var gold = typeof storage.gold === "number" ? storage.gold : GOLD_START; // Persist gold in storage var playerStats = storage.playerStats || { speed: 10, stamina: 10, luck: 10 }; var playerHorseNum = storage.playerHorseNum || 20; // always 20 var horses = []; // all 20 horses (Horse instances, not attached) var tvHorses = []; // 6 horses in current race (Horse instances, attached) var playerHorse = null; // player's horse (Horse instance, attached) var raceInProgress = false; var raceTimer = 0; var raceResults = []; var betHorseNum = null; // number of horse player bet on (1-20) var betHorseUniqueNum = null; // unique horse number (1-20) the player bet on, for payout validation var playerEntered = false; // did player enter their horse? var raceCountdown = 0; // ticks left before race starts var raceMessage = ''; var messageTimeout = null; // --- UI ELEMENTS --- var goldTxt = new Text2('', { size: 70, fill: 0xFFD700 }); goldTxt.anchor.set(0, 0); LK.gui.top.addChild(goldTxt); goldTxt.x = 120; goldTxt.y = 10; var betTxt = new Text2('', { size: 50, fill: "#fff" }); betTxt.anchor.set(0.5, 0); LK.gui.top.addChild(betTxt); betTxt.x = 2048 / 2; betTxt.y = 10; var messageTxt = new Text2('', { size: 60, fill: "#fff" }); messageTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(messageTxt); messageTxt.visible = false; // --- TV RACE BOARD --- var tvBoard = LK.getAsset('tvBoard', { anchorX: 0, anchorY: 0, width: TV_WIDTH, height: TV_HEIGHT, color: 0x222244, shape: 'box', x: TV_LEFT, y: TV_TOP }); game.addChild(tvBoard); // --- TV LANE LINES --- for (var i = 1; i < NUM_LANES; i++) { var line = LK.getAsset('laneLine' + i, { anchorX: 0, anchorY: 0, width: TV_WIDTH, height: 4, color: 0x444466, shape: 'box', x: TV_LEFT, y: TV_TOP + i * LANE_HEIGHT }); game.addChild(line); } // --- TV FINISH LINE --- var finishLine = LK.getAsset('finishLine', { anchorX: 0, anchorY: 0, width: 8, height: TV_HEIGHT, color: 0xffffff, shape: 'box', x: TV_LEFT + RACE_LENGTH, y: TV_TOP }); game.addChild(finishLine); // --- PLAYER HORSE AREA --- var playerArea = LK.getAsset('playerArea', { anchorX: 0, anchorY: 0, width: 500, height: 700, // increased height for more stat visibility color: 0x222244, shape: 'box', x: 60, y: PLAYER_HORSE_Y - 60 // move area a bit lower to match new height }); game.addChild(playerArea); // --- PLAYER HORSE STATS TEXT --- var playerStatsTxt = new Text2('', { size: 48, fill: "#fff" }); playerStatsTxt.anchor.set(0, 0); playerStatsTxt.x = playerArea.x + 20; playerStatsTxt.y = playerArea.y + 400; // move stats text lower for new area game.addChild(playerStatsTxt); // --- UPGRADE BUTTON --- var upgradeBtn = LK.getAsset('upgradeBtn', { anchorX: 0.5, anchorY: 0.5, width: 220, height: 80, color: 0x27ae60, shape: 'box', x: playerArea.x + 130, y: playerArea.y + 600 // move button lower for new area }); game.addChild(upgradeBtn); var upgradeTxt = new Text2('Upgrade (20)', { size: 36, fill: "#fff" }); upgradeTxt.anchor.set(0.5, 0.5); upgradeTxt.x = upgradeBtn.x; upgradeTxt.y = upgradeBtn.y; game.addChild(upgradeTxt); // --- ENTER RACE BUTTON --- var enterBtn = LK.getAsset('enterBtn', { anchorX: 0.5, anchorY: 0.5, width: 220, height: 80, color: 0x2980b9, shape: 'box', x: playerArea.x + 350, y: playerArea.y + 600 // move button lower for new area }); game.addChild(enterBtn); var enterTxt = new Text2('Enter Race', { size: 36, fill: "#fff" }); enterTxt.anchor.set(0.5, 0.5); enterTxt.x = enterBtn.x; enterTxt.y = enterBtn.y; game.addChild(enterTxt); // --- BET BUTTONS (for each TV horse) --- var betBtns = []; var betBtnTxts = []; for (var i = 0; i < NUM_LANES; i++) { var betAreaWidth = BET_AREA_WIDTH; var btn = LK.getAsset('betBtn' + i, { anchorX: 0.5, anchorY: 0.5, width: 120 * 1.32, height: 60 * 1.32, color: 0xe67e22, shape: 'box', // Place bet buttons in the center of the new bet area, right after the finish line x: TV_LEFT + RACE_LENGTH + betAreaWidth / 2, y: TV_TOP + i * LANE_HEIGHT + LANE_HEIGHT / 2 }); game.addChild(btn); betBtns.push(btn); // Use bet image as button instead of text var betImgBtn = LK.getAsset('bet', { anchorX: 0.5, anchorY: 0.5, // Increase by 15% width: 90 * 1.32 * 1.15, height: 90 * 1.32 * 1.15, x: btn.x, y: btn.y }); game.addChild(betImgBtn); betBtnTxts.push(betImgBtn); } // --- HORSE ASSET INIT (for all horses) --- for (var i = 0; i < NUM_HORSES; i++) { var h = new Horse(); h.setHorse(i, i + 1, false); // Random stats for TV horses var s = 40 + Math.floor(Math.random() * 61); // 40-100 var st = 40 + Math.floor(Math.random() * 61); var l = 40 + Math.floor(Math.random() * 61); h.setStats(s, st, l); horses.push(h); } // --- PLAYER HORSE ANIMATION FRAMES --- var playerHorseFrames = []; var playerHorseFrameNames = ['ourhorse', 'ourhorse2', 'ourhorse3', 'ourhorse4', 'ourhorse5']; for (var i = 0; i < playerHorseFrameNames.length; i++) { // Use ourhorse textures as animation frames for player horse // Adjust width +5%, height -5% playerHorseFrames.push(LK.getAsset(playerHorseFrameNames[i], { anchorX: 0.5, anchorY: 0.5, width: 100 * 1.05, height: 100 * 0.95 })); } // --- PLAYER HORSE INIT --- playerHorse = new Horse(); playerHorse.setHorse(NUM_HORSES - 1, playerHorseNum, true); playerHorse.setStats(playerStats.speed, playerStats.stamina, playerStats.luck); // Add animation state playerHorse.animFrame = 0; playerHorse.animTick = 0; playerHorse.animSprites = []; // Remove default body if (playerHorse.body && playerHorse.body.parent === playerHorse) { playerHorse.removeChild(playerHorse.body); } // Add all frames, only show the first for (var i = 0; i < playerHorseFrames.length; i++) { var sprite = playerHorseFrames[i]; sprite.visible = i === 0; playerHorse.addChild(sprite); playerHorse.animSprites.push(sprite); } playerHorse.updateAnimation = function () { this.animTick++; if (this.animTick % 12 === 0) { // Change frame every 12 ticks (~5 FPS) this.animSprites[this.animFrame].visible = false; this.animFrame = (this.animFrame + 1) % this.animSprites.length; this.animSprites[this.animFrame].visible = true; } }; // Move player horse up to match new area playerHorse.x = PLAYER_HORSE_X; playerHorse.y = PLAYER_HORSE_Y + 100; game.addChild(playerHorse); // --- FUNCTIONS --- function updateGoldText() { goldTxt.setText("Gold: " + gold); } function updatePlayerStatsText() { playerStatsTxt.setText("Speed: " + playerHorse.speed + "\nStamina: " + playerHorse.stamina + "\nLuck: " + playerHorse.luck); } function showMessage(msg, duration) { messageTxt.setText(msg); messageTxt.visible = true; if (messageTimeout) { LK.clearTimeout(messageTimeout); } if (duration) { messageTimeout = LK.setTimeout(function () { messageTxt.visible = false; }, duration); } } function hideMessage() { messageTxt.visible = false; if (messageTimeout) { LK.clearTimeout(messageTimeout); } } function saveState() { // Persist gold, player stats and horse number storage.gold = gold; storage.playerStats = { speed: playerHorse.speed, stamina: playerHorse.stamina, luck: playerHorse.luck }; storage.playerHorseNum = playerHorseNum; } // Pick 6 random horses for TV race (optionally include player horse) function pickRaceHorses(includePlayer) { var pool = []; for (var i = 0; i < NUM_HORSES - 1; i++) { pool.push(i); } // 0-18 if (includePlayer) { pool.push(NUM_HORSES - 1); } // 19 var selected = []; while (selected.length < NUM_LANES) { var idx = Math.floor(Math.random() * pool.length); selected.push(pool[idx]); pool.splice(idx, 1); } return selected; } // Start a new race (after betting phase) function startRace() { raceInProgress = true; raceResults = []; raceTimer = 0; raceMessage = ''; hideMessage(); // Reset TV horses for (var i = 0; i < tvHorses.length; i++) { game.removeChild(tvHorses[i]); } tvHorses = []; // Pick horses var includePlayer = playerEntered; var selected; // Use the stored selected horses from betting phase if available if (game.selectedRaceHorseIndices && Array.isArray(game.selectedRaceHorseIndices) && game.selectedRaceHorseIndices.length === NUM_LANES) { selected = game.selectedRaceHorseIndices.slice(); } else { selected = pickRaceHorses(includePlayer); } for (var i = 0; i < NUM_LANES; i++) { var hidx = selected[i]; var h; // If playerEntered, always put playerHorse in the last lane (bottom) if (playerEntered && i === NUM_LANES - 1) { h = playerHorse; h.setHorse(NUM_HORSES - 1, playerHorseNum, true); h.setStats(playerHorse.speed, playerHorse.stamina, playerHorse.luck); } else if (hidx === NUM_HORSES - 1 && !playerEntered) { // Defensive: if player not entered but random picked player horse, use a TV horse instead // Pick a non-player horse from horses not already in tvHorses for (var alt = 0; alt < NUM_HORSES - 1; alt++) { var alreadyUsed = false; for (var t = 0; t < tvHorses.length; t++) { if (tvHorses[t] === horses[alt]) { alreadyUsed = true; } } if (!alreadyUsed) { hidx = alt; break; } } h = horses[hidx]; h.setHorse(hidx, hidx + 1, false); } else if (hidx === NUM_HORSES - 1 && playerEntered) { // If playerEntered, skip this slot (playerHorse will be added at last lane) // Instead, pick a non-player horse for (var alt = 0; alt < NUM_HORSES - 1; alt++) { var alreadyUsed = false; for (var t = 0; t < tvHorses.length; t++) { if (tvHorses[t] === horses[alt]) { alreadyUsed = true; } } if (!alreadyUsed) { hidx = alt; break; } } h = horses[hidx]; h.setHorse(hidx, hidx + 1, false); } else { h = horses[hidx]; h.setHorse(hidx, hidx + 1, false); } h.lane = i; h.resetRace(); h.x = TV_LEFT + 40; h.y = TV_TOP + i * LANE_HEIGHT + LANE_HEIGHT / 2; h.scaleX = 1.1; h.scaleY = 1.1; game.addChild(h); tvHorses.push(h); } // Set bet buttons for (var i = 0; i < NUM_LANES; i++) { betBtns[i].visible = false; betBtnTxts[i].visible = false; } // Start race after short countdown raceCountdown = 60; // 1 second showMessage("Race starting!", 1000); } // End race, show results, pay out function endRace() { raceInProgress = false; var winner = raceResults[0]; var winnerNum = winner.number; var playerWin = false; var playerPlace = -1; // Find the actual TV horse object the player bet on (by unique number) var betHorseObj = null; if (betHorseUniqueNum !== null) { for (var i = 0; i < tvHorses.length; i++) { if (tvHorses[i].number === betHorseUniqueNum) { betHorseObj = tvHorses[i]; break; } } } for (var i = 0; i < raceResults.length; i++) { if (raceResults[i].isPlayer) { playerPlace = i + 1; break; } } var msg = "Winner: #" + winnerNum; if (betHorseUniqueNum !== null) { // Find the placement of the bet horse in the results var betPlace = -1; if (betHorseObj) { for (var i = 0; i < raceResults.length; i++) { if (raceResults[i] === betHorseObj) { betPlace = i + 1; break; } } } if (betPlace === 1) { gold += 50; msg += "\nYour bet WON! (+50)"; playerWin = true; } else if (betPlace === 2) { gold += 30; msg += "\nYour bet placed 2nd! (+30)"; playerWin = true; } else if (betPlace === 3) { gold += 10; msg += "\nYour bet placed 3rd! (+10)"; playerWin = true; } else { msg += "\nYou lost your bet."; } } if (playerEntered) { if (playerPlace === 1) { gold += PLAYER_PRIZE; msg += "\nYour horse WON! (+100)"; } else if (playerPlace > 0) { msg += "\nYour horse placed #" + playerPlace; } else { msg += "\nYour horse did not finish."; } } updateGoldText(); saveState(); showMessage(msg, 2500); // Move player horse back to player area after race ends if (playerHorse && playerHorse.parent === game) { // Remove from TV area if present for (var i = 0; i < tvHorses.length; i++) { if (tvHorses[i] === playerHorse) { game.removeChild(playerHorse); break; } } // Reset position to player area playerHorse.x = PLAYER_HORSE_X; playerHorse.y = PLAYER_HORSE_Y + 100; game.addChild(playerHorse); } // After a delay, start new betting phase LK.setTimeout(function () { startBettingPhase(); }, 2500); } // Start betting phase: show 6 horses, allow bet/enter function startBettingPhase() { raceInProgress = false; playerEntered = false; betHorseNum = null; betHorseUniqueNum = null; raceResults = []; hideMessage(); // Remove TV horses for (var i = 0; i < tvHorses.length; i++) { game.removeChild(tvHorses[i]); } tvHorses = []; // Always re-add player horse to player area after race if (playerHorse && playerHorse.parent !== game) { playerHorse.x = PLAYER_HORSE_X; playerHorse.y = PLAYER_HORSE_Y + 100; game.addChild(playerHorse); } // Pick 6 horses (player horse not included yet) var selected = pickRaceHorses(false); // Store selected horses for this betting phase, so they are reused for the race game.selectedRaceHorseIndices = selected.slice(); for (var i = 0; i < NUM_LANES; i++) { var hidx = selected[i]; var h = horses[hidx]; h.setHorse(hidx, hidx + 1, false); h.lane = i; h.resetRace(); h.x = TV_LEFT + 40; h.y = TV_TOP + i * LANE_HEIGHT + LANE_HEIGHT / 2; h.scaleX = 1.1; h.scaleY = 1.1; game.addChild(h); tvHorses.push(h); } // Set bet buttons for (var i = 0; i < NUM_LANES; i++) { betBtns[i].visible = true; betBtnTxts[i].visible = true; // No text to set, betBtnTxts now holds image buttons } betTxt.setText("Pick a horse to bet on, or enter your own!"); } // --- BUTTON HANDLERS --- // Bet buttons for (var i = 0; i < NUM_LANES; i++) { (function (idx) { // Set handler on both the colored box and the image button for full compatibility betBtns[idx].down = function (x, y, obj) { if (raceInProgress || betHorseNum !== null || gold < BET_COST) { return; } if (!tvHorses[idx]) { return; } // Defensive: avoid undefined betHorseNum = idx; // index in tvHorses for UI, but not for payout betHorseUniqueNum = tvHorses[idx].number; // store the actual horse number for payout gold -= BET_COST; updateGoldText(); saveState(); betTxt.setText("Bet placed on #" + betHorseUniqueNum + ". Waiting for race..."); for (var j = 0; j < NUM_LANES; j++) { betBtns[j].visible = false; betBtnTxts[j].visible = false; } // Start race after short delay LK.setTimeout(function () { startRace(); }, 1000); }; // Also set handler on the image button betBtnTxts[idx].down = betBtns[idx].down; })(i); } // Upgrade button upgradeBtn.down = function (x, y, obj) { if (raceInProgress) { return; } if (gold < UPGRADE_COST) { showMessage("Not enough gold!", 1200); return; } gold -= UPGRADE_COST; updateGoldText(); // Randomly pick stat to upgrade var statNames = ['speed', 'stamina', 'luck']; var idx = Math.floor(Math.random() * 3); var stat = statNames[idx]; playerHorse[stat] += 1 + Math.floor(Math.random() * 3); // +1~3 updatePlayerStatsText(); saveState(); showMessage("Upgraded " + stat + "!", 1000); }; // Enter race button enterBtn.down = function (x, y, obj) { if (raceInProgress || playerEntered) { return; } // Only allow if player's horse is not already in TV horses for (var i = 0; i < tvHorses.length; i++) { if (tvHorses[i].isPlayer) { showMessage("Already entered!", 1000); return; } } playerEntered = true; betTxt.setText("You entered your horse! Waiting for race..."); for (var j = 0; j < NUM_LANES; j++) { betBtns[j].visible = false; betBtnTxts[j].visible = false; } // Start race after short delay LK.setTimeout(function () { startRace(); }, 1000); }; // --- WORK AREA & BUTTON (for 0 gold) --- var workArea = null; var workBtn = null; var workBtnTxt = null; var workTimerTxt = null; var workTimer = 0; var workActive = false; var workTimeout = null; // --- GAME UPDATE LOOP --- game.update = function () { // Animate player horse if present if (playerHorse && playerHorse.animSprites && playerHorse.animSprites.length > 0) { playerHorse.updateAnimation(); } // Show/hide work area if gold is 0 if (gold === 0 && !workArea) { // Create work area on the right, symmetric to playerArea workArea = LK.getAsset('playerArea', { anchorX: 0, anchorY: 0, width: 500, height: 700, color: 0x222244, shape: 'box', x: 2048 - 60 - 500, y: PLAYER_HORSE_Y - 60 }); game.addChild(workArea); // Work button workBtn = LK.getAsset('upgradeBtn', { anchorX: 0.5, anchorY: 0.5, width: 220, height: 80, color: 0xf368bd, shape: 'box', x: workArea.x + 370, y: workArea.y + 600 }); game.addChild(workBtn); workBtnTxt = new Text2('Work!', { size: 36, fill: "#fff" }); workBtnTxt.anchor.set(0.5, 0.5); workBtnTxt.x = workBtn.x; workBtnTxt.y = workBtn.y; game.addChild(workBtnTxt); workTimerTxt = new Text2('', { size: 40, fill: "#fff" }); workTimerTxt.anchor.set(0.5, 0.5); workTimerTxt.x = workBtn.x; workTimerTxt.y = workBtn.y + 70; workTimerTxt.visible = false; game.addChild(workTimerTxt); workBtn.down = function (x, y, obj) { if (workActive) { return; } workActive = true; workBtn.visible = false; workBtnTxt.visible = false; workTimer = 15; workTimerTxt.visible = true; workTimerTxt.setText("15"); // Start countdown workTimeout = LK.setInterval(function () { workTimer--; if (workTimer > 0) { workTimerTxt.setText(workTimer + ""); } else { LK.clearInterval(workTimeout); workTimeout = null; workTimerTxt.visible = false; gold += 10; updateGoldText(); saveState(); workActive = false; // Remove work area and button if (workArea && workArea.parent === game) { game.removeChild(workArea); } if (workBtn && workBtn.parent === game) { game.removeChild(workBtn); } if (workBtnTxt && workBtnTxt.parent === game) { game.removeChild(workBtnTxt); } if (workTimerTxt && workTimerTxt.parent === game) { game.removeChild(workTimerTxt); } workArea = null; workBtn = null; workBtnTxt = null; workTimerTxt = null; } }, 1000); }; } // Remove work area if gold > 0 if (gold > 0 && workArea) { if (workTimeout) { LK.clearInterval(workTimeout); workTimeout = null; } if (workArea && workArea.parent === game) { game.removeChild(workArea); } if (workBtn && workBtn.parent === game) { game.removeChild(workBtn); } if (workBtnTxt && workBtnTxt.parent === game) { game.removeChild(workBtnTxt); } if (workTimerTxt && workTimerTxt.parent === game) { game.removeChild(workTimerTxt); } workArea = null; workBtn = null; workBtnTxt = null; workTimerTxt = null; workActive = false; } // Update UI updateGoldText(); updatePlayerStatsText(); // Race countdown if (raceCountdown > 0) { raceCountdown--; if (raceCountdown === 0) { showMessage("Go!", 600); } return; } // Race in progress if (raceInProgress) { var finishedCount = 0; for (var i = 0; i < tvHorses.length; i++) { var h = tvHorses[i]; if (h.finished) { finishedCount++; continue; } // Calculate move var baseSpeed = h.speed / 100 * 8 + 2; // 2-10 px per tick var luckFactor = 1 + (Math.random() - 0.5) * (h.luck / 200); // up to +/- luck/200 var move = baseSpeed * luckFactor; if (h.energy > 0) { h.energy -= 0.04; } else { move *= 0.6; // tired } h.progress += move / RACE_LENGTH; if (h.progress >= 1) { h.progress = 1; h.finished = true; h.finishTime = raceTimer; raceResults.push(h); } h.x = TV_LEFT + 40 + h.progress * (RACE_LENGTH - 80); } raceTimer++; // If all finished, end race if (raceResults.length === tvHorses.length) { endRace(); } } }; // --- INITIALIZE --- updateGoldText(); updatePlayerStatsText(); startBettingPhase();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Horse class for both TV and player horses
var Horse = Container.expand(function () {
var self = Container.call(this);
// Asset: Each horse gets a colored ellipse as body and a number label
var colorList = [0x8e44ad, 0xe67e22, 0x16a085, 0xc0392b, 0x2980b9, 0xf1c40f, 0x2ecc71, 0x34495e, 0xd35400, 0x1abc9c, 0x7f8c8d, 0x27ae60, 0x9b59b6, 0x2c3e50, 0xe74c3c, 0x3498db, 0xf39c12, 0x95a5a6, 0x22313f, 0x6ab04c];
// colorIndex is set on instance
self.colorIndex = 0;
self.number = 1; // 1-20
self.isPlayer = false; // true if this is the player's horse
// Stats
self.speed = 10;
self.stamina = 10;
self.luck = 10;
// For race
self.lane = 0; // 0-5
self.progress = 0; // 0-1
self.energy = 0; // stamina left
self.finished = false;
self.finishTime = 0;
// Visuals
self.body = self.attachAsset('horseBody_' + self.colorIndex, {
anchorX: 0.5,
anchorY: 0.5,
width: 90 * 1.32,
height: 60 * 1.32,
color: colorList[self.colorIndex % colorList.length],
shape: 'ellipse'
});
self.label = new Text2(self.number + '', {
size: 40,
fill: "#fff"
});
self.label.anchor.set(0.5, 0.5);
self.addChild(self.label);
// For player horse, show "YOU" label
self.youLabel = null;
if (self.isPlayer) {
self.youLabel = new Text2("YOU", {
size: 32,
fill: "#fff"
});
self.youLabel.anchor.set(0.5, 0);
self.youLabel.y = 40;
self.addChild(self.youLabel);
}
// Set color and number
self.setHorse = function (colorIndex, number, isPlayer) {
self.colorIndex = colorIndex;
self.number = number;
self.isPlayer = !!isPlayer;
self.body.tint = colorList[self.colorIndex % colorList.length];
self.label.setText(self.number + '');
if (self.isPlayer && !self.youLabel) {
self.youLabel = new Text2("YOU", {
size: 32,
fill: "#fff"
});
self.youLabel.anchor.set(0.5, 0);
self.youLabel.y = 40;
self.addChild(self.youLabel);
}
if (!self.isPlayer && self.youLabel) {
self.removeChild(self.youLabel);
self.youLabel = null;
}
};
// Set stats
self.setStats = function (speed, stamina, luck) {
self.speed = speed;
self.stamina = stamina;
self.luck = luck;
};
// Reset for race
self.resetRace = function () {
self.progress = 0;
self.energy = self.stamina;
self.finished = false;
self.finishTime = 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// --- GLOBALS ---
var NUM_HORSES = 40;
var NUM_LANES = 6;
// Set race track to 90% and bet area to 10% of available width (2048px - TV_LEFT - margin)
var TV_LEFT = 120;
var TOTAL_WIDTH = 2048 - TV_LEFT - 60; // 60px right margin
var BET_AREA_WIDTH = Math.floor(TOTAL_WIDTH * 0.10); // 10% for bet area
var RACE_LENGTH = Math.floor(TOTAL_WIDTH * 0.90); // 90% for race track
var TV_TOP = 220; // px from top (moved down by 100)
var TV_HEIGHT = 700; // px, height of TV race area
var TV_WIDTH = RACE_LENGTH; // TV board is now just the race track width
var LANE_HEIGHT = 100 * 1.15;
var HORSE_SIZE = 90;
var PLAYER_HORSE_Y = 2000; // moved up by 200
var PLAYER_HORSE_X = 220;
var GOLD_START = 100;
var BET_COST = 10;
var UPGRADE_COST = 20;
var BET_PRIZE = 40;
var PLAYER_PRIZE = 100;
// --- STATE ---
var gold = typeof storage.gold === "number" ? storage.gold : GOLD_START; // Persist gold in storage
var playerStats = storage.playerStats || {
speed: 10,
stamina: 10,
luck: 10
};
var playerHorseNum = storage.playerHorseNum || 20; // always 20
var horses = []; // all 20 horses (Horse instances, not attached)
var tvHorses = []; // 6 horses in current race (Horse instances, attached)
var playerHorse = null; // player's horse (Horse instance, attached)
var raceInProgress = false;
var raceTimer = 0;
var raceResults = [];
var betHorseNum = null; // number of horse player bet on (1-20)
var betHorseUniqueNum = null; // unique horse number (1-20) the player bet on, for payout validation
var playerEntered = false; // did player enter their horse?
var raceCountdown = 0; // ticks left before race starts
var raceMessage = '';
var messageTimeout = null;
// --- UI ELEMENTS ---
var goldTxt = new Text2('', {
size: 70,
fill: 0xFFD700
});
goldTxt.anchor.set(0, 0);
LK.gui.top.addChild(goldTxt);
goldTxt.x = 120;
goldTxt.y = 10;
var betTxt = new Text2('', {
size: 50,
fill: "#fff"
});
betTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(betTxt);
betTxt.x = 2048 / 2;
betTxt.y = 10;
var messageTxt = new Text2('', {
size: 60,
fill: "#fff"
});
messageTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(messageTxt);
messageTxt.visible = false;
// --- TV RACE BOARD ---
var tvBoard = LK.getAsset('tvBoard', {
anchorX: 0,
anchorY: 0,
width: TV_WIDTH,
height: TV_HEIGHT,
color: 0x222244,
shape: 'box',
x: TV_LEFT,
y: TV_TOP
});
game.addChild(tvBoard);
// --- TV LANE LINES ---
for (var i = 1; i < NUM_LANES; i++) {
var line = LK.getAsset('laneLine' + i, {
anchorX: 0,
anchorY: 0,
width: TV_WIDTH,
height: 4,
color: 0x444466,
shape: 'box',
x: TV_LEFT,
y: TV_TOP + i * LANE_HEIGHT
});
game.addChild(line);
}
// --- TV FINISH LINE ---
var finishLine = LK.getAsset('finishLine', {
anchorX: 0,
anchorY: 0,
width: 8,
height: TV_HEIGHT,
color: 0xffffff,
shape: 'box',
x: TV_LEFT + RACE_LENGTH,
y: TV_TOP
});
game.addChild(finishLine);
// --- PLAYER HORSE AREA ---
var playerArea = LK.getAsset('playerArea', {
anchorX: 0,
anchorY: 0,
width: 500,
height: 700,
// increased height for more stat visibility
color: 0x222244,
shape: 'box',
x: 60,
y: PLAYER_HORSE_Y - 60 // move area a bit lower to match new height
});
game.addChild(playerArea);
// --- PLAYER HORSE STATS TEXT ---
var playerStatsTxt = new Text2('', {
size: 48,
fill: "#fff"
});
playerStatsTxt.anchor.set(0, 0);
playerStatsTxt.x = playerArea.x + 20;
playerStatsTxt.y = playerArea.y + 400; // move stats text lower for new area
game.addChild(playerStatsTxt);
// --- UPGRADE BUTTON ---
var upgradeBtn = LK.getAsset('upgradeBtn', {
anchorX: 0.5,
anchorY: 0.5,
width: 220,
height: 80,
color: 0x27ae60,
shape: 'box',
x: playerArea.x + 130,
y: playerArea.y + 600 // move button lower for new area
});
game.addChild(upgradeBtn);
var upgradeTxt = new Text2('Upgrade (20)', {
size: 36,
fill: "#fff"
});
upgradeTxt.anchor.set(0.5, 0.5);
upgradeTxt.x = upgradeBtn.x;
upgradeTxt.y = upgradeBtn.y;
game.addChild(upgradeTxt);
// --- ENTER RACE BUTTON ---
var enterBtn = LK.getAsset('enterBtn', {
anchorX: 0.5,
anchorY: 0.5,
width: 220,
height: 80,
color: 0x2980b9,
shape: 'box',
x: playerArea.x + 350,
y: playerArea.y + 600 // move button lower for new area
});
game.addChild(enterBtn);
var enterTxt = new Text2('Enter Race', {
size: 36,
fill: "#fff"
});
enterTxt.anchor.set(0.5, 0.5);
enterTxt.x = enterBtn.x;
enterTxt.y = enterBtn.y;
game.addChild(enterTxt);
// --- BET BUTTONS (for each TV horse) ---
var betBtns = [];
var betBtnTxts = [];
for (var i = 0; i < NUM_LANES; i++) {
var betAreaWidth = BET_AREA_WIDTH;
var btn = LK.getAsset('betBtn' + i, {
anchorX: 0.5,
anchorY: 0.5,
width: 120 * 1.32,
height: 60 * 1.32,
color: 0xe67e22,
shape: 'box',
// Place bet buttons in the center of the new bet area, right after the finish line
x: TV_LEFT + RACE_LENGTH + betAreaWidth / 2,
y: TV_TOP + i * LANE_HEIGHT + LANE_HEIGHT / 2
});
game.addChild(btn);
betBtns.push(btn);
// Use bet image as button instead of text
var betImgBtn = LK.getAsset('bet', {
anchorX: 0.5,
anchorY: 0.5,
// Increase by 15%
width: 90 * 1.32 * 1.15,
height: 90 * 1.32 * 1.15,
x: btn.x,
y: btn.y
});
game.addChild(betImgBtn);
betBtnTxts.push(betImgBtn);
}
// --- HORSE ASSET INIT (for all horses) ---
for (var i = 0; i < NUM_HORSES; i++) {
var h = new Horse();
h.setHorse(i, i + 1, false);
// Random stats for TV horses
var s = 40 + Math.floor(Math.random() * 61); // 40-100
var st = 40 + Math.floor(Math.random() * 61);
var l = 40 + Math.floor(Math.random() * 61);
h.setStats(s, st, l);
horses.push(h);
}
// --- PLAYER HORSE ANIMATION FRAMES ---
var playerHorseFrames = [];
var playerHorseFrameNames = ['ourhorse', 'ourhorse2', 'ourhorse3', 'ourhorse4', 'ourhorse5'];
for (var i = 0; i < playerHorseFrameNames.length; i++) {
// Use ourhorse textures as animation frames for player horse
// Adjust width +5%, height -5%
playerHorseFrames.push(LK.getAsset(playerHorseFrameNames[i], {
anchorX: 0.5,
anchorY: 0.5,
width: 100 * 1.05,
height: 100 * 0.95
}));
}
// --- PLAYER HORSE INIT ---
playerHorse = new Horse();
playerHorse.setHorse(NUM_HORSES - 1, playerHorseNum, true);
playerHorse.setStats(playerStats.speed, playerStats.stamina, playerStats.luck);
// Add animation state
playerHorse.animFrame = 0;
playerHorse.animTick = 0;
playerHorse.animSprites = [];
// Remove default body
if (playerHorse.body && playerHorse.body.parent === playerHorse) {
playerHorse.removeChild(playerHorse.body);
}
// Add all frames, only show the first
for (var i = 0; i < playerHorseFrames.length; i++) {
var sprite = playerHorseFrames[i];
sprite.visible = i === 0;
playerHorse.addChild(sprite);
playerHorse.animSprites.push(sprite);
}
playerHorse.updateAnimation = function () {
this.animTick++;
if (this.animTick % 12 === 0) {
// Change frame every 12 ticks (~5 FPS)
this.animSprites[this.animFrame].visible = false;
this.animFrame = (this.animFrame + 1) % this.animSprites.length;
this.animSprites[this.animFrame].visible = true;
}
};
// Move player horse up to match new area
playerHorse.x = PLAYER_HORSE_X;
playerHorse.y = PLAYER_HORSE_Y + 100;
game.addChild(playerHorse);
// --- FUNCTIONS ---
function updateGoldText() {
goldTxt.setText("Gold: " + gold);
}
function updatePlayerStatsText() {
playerStatsTxt.setText("Speed: " + playerHorse.speed + "\nStamina: " + playerHorse.stamina + "\nLuck: " + playerHorse.luck);
}
function showMessage(msg, duration) {
messageTxt.setText(msg);
messageTxt.visible = true;
if (messageTimeout) {
LK.clearTimeout(messageTimeout);
}
if (duration) {
messageTimeout = LK.setTimeout(function () {
messageTxt.visible = false;
}, duration);
}
}
function hideMessage() {
messageTxt.visible = false;
if (messageTimeout) {
LK.clearTimeout(messageTimeout);
}
}
function saveState() {
// Persist gold, player stats and horse number
storage.gold = gold;
storage.playerStats = {
speed: playerHorse.speed,
stamina: playerHorse.stamina,
luck: playerHorse.luck
};
storage.playerHorseNum = playerHorseNum;
}
// Pick 6 random horses for TV race (optionally include player horse)
function pickRaceHorses(includePlayer) {
var pool = [];
for (var i = 0; i < NUM_HORSES - 1; i++) {
pool.push(i);
} // 0-18
if (includePlayer) {
pool.push(NUM_HORSES - 1);
} // 19
var selected = [];
while (selected.length < NUM_LANES) {
var idx = Math.floor(Math.random() * pool.length);
selected.push(pool[idx]);
pool.splice(idx, 1);
}
return selected;
}
// Start a new race (after betting phase)
function startRace() {
raceInProgress = true;
raceResults = [];
raceTimer = 0;
raceMessage = '';
hideMessage();
// Reset TV horses
for (var i = 0; i < tvHorses.length; i++) {
game.removeChild(tvHorses[i]);
}
tvHorses = [];
// Pick horses
var includePlayer = playerEntered;
var selected;
// Use the stored selected horses from betting phase if available
if (game.selectedRaceHorseIndices && Array.isArray(game.selectedRaceHorseIndices) && game.selectedRaceHorseIndices.length === NUM_LANES) {
selected = game.selectedRaceHorseIndices.slice();
} else {
selected = pickRaceHorses(includePlayer);
}
for (var i = 0; i < NUM_LANES; i++) {
var hidx = selected[i];
var h;
// If playerEntered, always put playerHorse in the last lane (bottom)
if (playerEntered && i === NUM_LANES - 1) {
h = playerHorse;
h.setHorse(NUM_HORSES - 1, playerHorseNum, true);
h.setStats(playerHorse.speed, playerHorse.stamina, playerHorse.luck);
} else if (hidx === NUM_HORSES - 1 && !playerEntered) {
// Defensive: if player not entered but random picked player horse, use a TV horse instead
// Pick a non-player horse from horses not already in tvHorses
for (var alt = 0; alt < NUM_HORSES - 1; alt++) {
var alreadyUsed = false;
for (var t = 0; t < tvHorses.length; t++) {
if (tvHorses[t] === horses[alt]) {
alreadyUsed = true;
}
}
if (!alreadyUsed) {
hidx = alt;
break;
}
}
h = horses[hidx];
h.setHorse(hidx, hidx + 1, false);
} else if (hidx === NUM_HORSES - 1 && playerEntered) {
// If playerEntered, skip this slot (playerHorse will be added at last lane)
// Instead, pick a non-player horse
for (var alt = 0; alt < NUM_HORSES - 1; alt++) {
var alreadyUsed = false;
for (var t = 0; t < tvHorses.length; t++) {
if (tvHorses[t] === horses[alt]) {
alreadyUsed = true;
}
}
if (!alreadyUsed) {
hidx = alt;
break;
}
}
h = horses[hidx];
h.setHorse(hidx, hidx + 1, false);
} else {
h = horses[hidx];
h.setHorse(hidx, hidx + 1, false);
}
h.lane = i;
h.resetRace();
h.x = TV_LEFT + 40;
h.y = TV_TOP + i * LANE_HEIGHT + LANE_HEIGHT / 2;
h.scaleX = 1.1;
h.scaleY = 1.1;
game.addChild(h);
tvHorses.push(h);
}
// Set bet buttons
for (var i = 0; i < NUM_LANES; i++) {
betBtns[i].visible = false;
betBtnTxts[i].visible = false;
}
// Start race after short countdown
raceCountdown = 60; // 1 second
showMessage("Race starting!", 1000);
}
// End race, show results, pay out
function endRace() {
raceInProgress = false;
var winner = raceResults[0];
var winnerNum = winner.number;
var playerWin = false;
var playerPlace = -1;
// Find the actual TV horse object the player bet on (by unique number)
var betHorseObj = null;
if (betHorseUniqueNum !== null) {
for (var i = 0; i < tvHorses.length; i++) {
if (tvHorses[i].number === betHorseUniqueNum) {
betHorseObj = tvHorses[i];
break;
}
}
}
for (var i = 0; i < raceResults.length; i++) {
if (raceResults[i].isPlayer) {
playerPlace = i + 1;
break;
}
}
var msg = "Winner: #" + winnerNum;
if (betHorseUniqueNum !== null) {
// Find the placement of the bet horse in the results
var betPlace = -1;
if (betHorseObj) {
for (var i = 0; i < raceResults.length; i++) {
if (raceResults[i] === betHorseObj) {
betPlace = i + 1;
break;
}
}
}
if (betPlace === 1) {
gold += 50;
msg += "\nYour bet WON! (+50)";
playerWin = true;
} else if (betPlace === 2) {
gold += 30;
msg += "\nYour bet placed 2nd! (+30)";
playerWin = true;
} else if (betPlace === 3) {
gold += 10;
msg += "\nYour bet placed 3rd! (+10)";
playerWin = true;
} else {
msg += "\nYou lost your bet.";
}
}
if (playerEntered) {
if (playerPlace === 1) {
gold += PLAYER_PRIZE;
msg += "\nYour horse WON! (+100)";
} else if (playerPlace > 0) {
msg += "\nYour horse placed #" + playerPlace;
} else {
msg += "\nYour horse did not finish.";
}
}
updateGoldText();
saveState();
showMessage(msg, 2500);
// Move player horse back to player area after race ends
if (playerHorse && playerHorse.parent === game) {
// Remove from TV area if present
for (var i = 0; i < tvHorses.length; i++) {
if (tvHorses[i] === playerHorse) {
game.removeChild(playerHorse);
break;
}
}
// Reset position to player area
playerHorse.x = PLAYER_HORSE_X;
playerHorse.y = PLAYER_HORSE_Y + 100;
game.addChild(playerHorse);
}
// After a delay, start new betting phase
LK.setTimeout(function () {
startBettingPhase();
}, 2500);
}
// Start betting phase: show 6 horses, allow bet/enter
function startBettingPhase() {
raceInProgress = false;
playerEntered = false;
betHorseNum = null;
betHorseUniqueNum = null;
raceResults = [];
hideMessage();
// Remove TV horses
for (var i = 0; i < tvHorses.length; i++) {
game.removeChild(tvHorses[i]);
}
tvHorses = [];
// Always re-add player horse to player area after race
if (playerHorse && playerHorse.parent !== game) {
playerHorse.x = PLAYER_HORSE_X;
playerHorse.y = PLAYER_HORSE_Y + 100;
game.addChild(playerHorse);
}
// Pick 6 horses (player horse not included yet)
var selected = pickRaceHorses(false);
// Store selected horses for this betting phase, so they are reused for the race
game.selectedRaceHorseIndices = selected.slice();
for (var i = 0; i < NUM_LANES; i++) {
var hidx = selected[i];
var h = horses[hidx];
h.setHorse(hidx, hidx + 1, false);
h.lane = i;
h.resetRace();
h.x = TV_LEFT + 40;
h.y = TV_TOP + i * LANE_HEIGHT + LANE_HEIGHT / 2;
h.scaleX = 1.1;
h.scaleY = 1.1;
game.addChild(h);
tvHorses.push(h);
}
// Set bet buttons
for (var i = 0; i < NUM_LANES; i++) {
betBtns[i].visible = true;
betBtnTxts[i].visible = true;
// No text to set, betBtnTxts now holds image buttons
}
betTxt.setText("Pick a horse to bet on, or enter your own!");
}
// --- BUTTON HANDLERS ---
// Bet buttons
for (var i = 0; i < NUM_LANES; i++) {
(function (idx) {
// Set handler on both the colored box and the image button for full compatibility
betBtns[idx].down = function (x, y, obj) {
if (raceInProgress || betHorseNum !== null || gold < BET_COST) {
return;
}
if (!tvHorses[idx]) {
return;
} // Defensive: avoid undefined
betHorseNum = idx; // index in tvHorses for UI, but not for payout
betHorseUniqueNum = tvHorses[idx].number; // store the actual horse number for payout
gold -= BET_COST;
updateGoldText();
saveState();
betTxt.setText("Bet placed on #" + betHorseUniqueNum + ". Waiting for race...");
for (var j = 0; j < NUM_LANES; j++) {
betBtns[j].visible = false;
betBtnTxts[j].visible = false;
}
// Start race after short delay
LK.setTimeout(function () {
startRace();
}, 1000);
};
// Also set handler on the image button
betBtnTxts[idx].down = betBtns[idx].down;
})(i);
}
// Upgrade button
upgradeBtn.down = function (x, y, obj) {
if (raceInProgress) {
return;
}
if (gold < UPGRADE_COST) {
showMessage("Not enough gold!", 1200);
return;
}
gold -= UPGRADE_COST;
updateGoldText();
// Randomly pick stat to upgrade
var statNames = ['speed', 'stamina', 'luck'];
var idx = Math.floor(Math.random() * 3);
var stat = statNames[idx];
playerHorse[stat] += 1 + Math.floor(Math.random() * 3); // +1~3
updatePlayerStatsText();
saveState();
showMessage("Upgraded " + stat + "!", 1000);
};
// Enter race button
enterBtn.down = function (x, y, obj) {
if (raceInProgress || playerEntered) {
return;
}
// Only allow if player's horse is not already in TV horses
for (var i = 0; i < tvHorses.length; i++) {
if (tvHorses[i].isPlayer) {
showMessage("Already entered!", 1000);
return;
}
}
playerEntered = true;
betTxt.setText("You entered your horse! Waiting for race...");
for (var j = 0; j < NUM_LANES; j++) {
betBtns[j].visible = false;
betBtnTxts[j].visible = false;
}
// Start race after short delay
LK.setTimeout(function () {
startRace();
}, 1000);
};
// --- WORK AREA & BUTTON (for 0 gold) ---
var workArea = null;
var workBtn = null;
var workBtnTxt = null;
var workTimerTxt = null;
var workTimer = 0;
var workActive = false;
var workTimeout = null;
// --- GAME UPDATE LOOP ---
game.update = function () {
// Animate player horse if present
if (playerHorse && playerHorse.animSprites && playerHorse.animSprites.length > 0) {
playerHorse.updateAnimation();
}
// Show/hide work area if gold is 0
if (gold === 0 && !workArea) {
// Create work area on the right, symmetric to playerArea
workArea = LK.getAsset('playerArea', {
anchorX: 0,
anchorY: 0,
width: 500,
height: 700,
color: 0x222244,
shape: 'box',
x: 2048 - 60 - 500,
y: PLAYER_HORSE_Y - 60
});
game.addChild(workArea);
// Work button
workBtn = LK.getAsset('upgradeBtn', {
anchorX: 0.5,
anchorY: 0.5,
width: 220,
height: 80,
color: 0xf368bd,
shape: 'box',
x: workArea.x + 370,
y: workArea.y + 600
});
game.addChild(workBtn);
workBtnTxt = new Text2('Work!', {
size: 36,
fill: "#fff"
});
workBtnTxt.anchor.set(0.5, 0.5);
workBtnTxt.x = workBtn.x;
workBtnTxt.y = workBtn.y;
game.addChild(workBtnTxt);
workTimerTxt = new Text2('', {
size: 40,
fill: "#fff"
});
workTimerTxt.anchor.set(0.5, 0.5);
workTimerTxt.x = workBtn.x;
workTimerTxt.y = workBtn.y + 70;
workTimerTxt.visible = false;
game.addChild(workTimerTxt);
workBtn.down = function (x, y, obj) {
if (workActive) {
return;
}
workActive = true;
workBtn.visible = false;
workBtnTxt.visible = false;
workTimer = 15;
workTimerTxt.visible = true;
workTimerTxt.setText("15");
// Start countdown
workTimeout = LK.setInterval(function () {
workTimer--;
if (workTimer > 0) {
workTimerTxt.setText(workTimer + "");
} else {
LK.clearInterval(workTimeout);
workTimeout = null;
workTimerTxt.visible = false;
gold += 10;
updateGoldText();
saveState();
workActive = false;
// Remove work area and button
if (workArea && workArea.parent === game) {
game.removeChild(workArea);
}
if (workBtn && workBtn.parent === game) {
game.removeChild(workBtn);
}
if (workBtnTxt && workBtnTxt.parent === game) {
game.removeChild(workBtnTxt);
}
if (workTimerTxt && workTimerTxt.parent === game) {
game.removeChild(workTimerTxt);
}
workArea = null;
workBtn = null;
workBtnTxt = null;
workTimerTxt = null;
}
}, 1000);
};
}
// Remove work area if gold > 0
if (gold > 0 && workArea) {
if (workTimeout) {
LK.clearInterval(workTimeout);
workTimeout = null;
}
if (workArea && workArea.parent === game) {
game.removeChild(workArea);
}
if (workBtn && workBtn.parent === game) {
game.removeChild(workBtn);
}
if (workBtnTxt && workBtnTxt.parent === game) {
game.removeChild(workBtnTxt);
}
if (workTimerTxt && workTimerTxt.parent === game) {
game.removeChild(workTimerTxt);
}
workArea = null;
workBtn = null;
workBtnTxt = null;
workTimerTxt = null;
workActive = false;
}
// Update UI
updateGoldText();
updatePlayerStatsText();
// Race countdown
if (raceCountdown > 0) {
raceCountdown--;
if (raceCountdown === 0) {
showMessage("Go!", 600);
}
return;
}
// Race in progress
if (raceInProgress) {
var finishedCount = 0;
for (var i = 0; i < tvHorses.length; i++) {
var h = tvHorses[i];
if (h.finished) {
finishedCount++;
continue;
}
// Calculate move
var baseSpeed = h.speed / 100 * 8 + 2; // 2-10 px per tick
var luckFactor = 1 + (Math.random() - 0.5) * (h.luck / 200); // up to +/- luck/200
var move = baseSpeed * luckFactor;
if (h.energy > 0) {
h.energy -= 0.04;
} else {
move *= 0.6; // tired
}
h.progress += move / RACE_LENGTH;
if (h.progress >= 1) {
h.progress = 1;
h.finished = true;
h.finishTime = raceTimer;
raceResults.push(h);
}
h.x = TV_LEFT + 40 + h.progress * (RACE_LENGTH - 80);
}
raceTimer++;
// If all finished, end race
if (raceResults.length === tvHorses.length) {
endRace();
}
}
};
// --- INITIALIZE ---
updateGoldText();
updatePlayerStatsText();
startBettingPhase();