User prompt
Haz que el personaje aparezca aleatoriamente en cualquier casilla del tablero
User prompt
Acomoda la puntuación SCORE un poco más a la izquierda
User prompt
Existe un error que hace que al seleccionar una vez return no se regresa al estado anterior, el segundo click si regresa al estado anterior, haciendo que por ejemplo estando el turno 4, seleccionar RETURN -> turno 4, seleccionar RETURN -> turno 3, seleccionar RETURN -> turno 2, después de eso no puede bajar al turno 1, corrige para hacer que desde el primer click se pueda regresar al turno anterior, sino se acumula un offset de un turno, debe ser un error de selección de indices
User prompt
Que con solo un click en Return se pueda regresar al estado anterior, actualmente se requiere ssleccionarlo 2 veces
User prompt
Casi, la primera vez que se selecciona return solo se disminuye el turno, si se selecciona una segunda vez o más sí funciona, corrige para regresar al estado anterior desde la primera vez que se selecciona return
User prompt
EL botón RETURN disminuye el turno y sí elimina el edificio pero solo si estamos sobre la casilla del edificio, crea una estructura que almacene el historial de movimientos y construcciones, para manejar los estados anteriores
User prompt
El botón return no realiza el retorno de posición, BUILD funciona correctamente
User prompt
Los botones no están implementados, ya que al seleccionarlos no realizan ninguna acción
User prompt
El botón RETURN debe deshacer el último turno, es decir, regresar a la posición anterior, restar el turno, la puntuación y si se creó un edificio eliminarlo
User prompt
Que el botón RETURN esté arriba del botón BUILD
User prompt
Reorganicemos la idea, que ya no exista la opción de NEXT TURN, al mover al personaje se considera que se ha pasado el turno, pero se puede deshacer con el botón RETURN, además acomoda los botones RETURN y BUILD
User prompt
Correcto, agrega la opción de poder regresar a la casilla de donde viene el personaje antes de seleccionar NEXT TURN en caso de que el usuario quiera corregir el movimiento
User prompt
Ahora corrige que solo se pueda mover a una casilla adyacente yo regresar y después seleccionar NEXT TURN, actualmente se puede mover indefinidamente sin seleccionar el botón
Code edit (1 edits merged)
Please save this source code
User prompt
Hex Farm Builder
Initial prompt
Quiero un juego donde se tenga un mapa, este mapa está dividido en casillas con forma hexagonal todos del mismo color (verde) pero con la división remarcada para identificar cada casilla, en ciertas casillas deben existir características especiales representadas de distintos colores (rojo, azul, morado, amarillo, café). Se debe poder mover a un personaje que se pueda mover a una casilla adyacente. Esto está limitado a 1 movimiento por turno, en las casillas especiales se puede construir estructuras especiales que permitirán ganar puntos, el objetivo es obtener la mayor cantidad de puntos en 20 turnos, para pasar turno debe existir un botón y para construir una estructura especial debe existir otro botón, en las casillas comunes (verdes) no se puede construir nada. En la estructura más visual debe representar una granja y las casillas especiales aspectos de esta, color verde es el césped, rojo graneros, azul pozos, morado plantíos de frutas, amarillo plantaciones de trigo, café establos. El punto es que existan varias de estas casillas especiales y cuando se cree el edificio en las casillas especiales sea visible para el jugador
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Character = Container.expand(function () { var self = Container.call(this); var characterGraphics = self.attachAsset('character', { anchorX: 0.5, anchorY: 0.5 }); self.row = 0; self.col = 0; self.previousRow = 0; self.previousCol = 0; self.moveToTile = function (tile) { self.previousRow = self.row; self.previousCol = self.col; self.x = tile.x; self.y = tile.y; self.row = tile.row; self.col = tile.col; LK.getSound('move').play(); }; self.returnToPreviousPosition = function () { if (hexGrid[self.previousRow] && hexGrid[self.previousRow][self.previousCol]) { var previousTile = hexGrid[self.previousRow][self.previousCol]; self.x = previousTile.x; self.y = previousTile.y; self.row = self.previousRow; self.col = self.previousCol; LK.getSound('move').play(); } }; return self; }); var HexTile = Container.expand(function (type, row, col) { var self = Container.call(this); self.type = type; self.row = row; self.col = col; self.hasBuilding = false; self.building = null; var assetName = 'hexGrass'; if (type === 'barn') { assetName = 'hexRed'; } else if (type === 'well') { assetName = 'hexBlue'; } else if (type === 'orchard') { assetName = 'hexPurple'; } else if (type === 'wheat') { assetName = 'hexYellow'; } else if (type === 'stable') { assetName = 'hexBrown'; } self.originalAssetName = assetName; // Store original asset name for restoration var hexGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.addBuilding = function () { if (self.hasBuilding || self.type === 'grass') { return false; } var buildingAsset = null; var points = 0; if (self.type === 'barn') { buildingAsset = 'barn'; points = 15; } else if (self.type === 'well') { buildingAsset = 'well'; points = 10; } else if (self.type === 'orchard') { buildingAsset = 'orchard'; points = 20; } else if (self.type === 'wheat') { buildingAsset = 'wheat'; points = 12; } else if (self.type === 'stable') { buildingAsset = 'stable'; points = 18; } if (buildingAsset) { // Change the hex tile to grass when building is constructed hexGraphics.destroy(); hexGraphics = self.attachAsset('hexGrass', { anchorX: 0.5, anchorY: 0.5 }); self.building = self.attachAsset(buildingAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 1.25, scaleY: 1.25 }); self.hasBuilding = true; LK.getSound('build').play(); // Play building-specific sound after construction sound LK.setTimeout(function () { if (self.type === 'barn') { LK.getSound('barnSound').play(); } else if (self.type === 'well') { LK.getSound('wellSound').play(); } else if (self.type === 'orchard') { LK.getSound('orchardSound').play(); } else if (self.type === 'wheat') { LK.getSound('wheatSound').play(); } else if (self.type === 'stable') { LK.getSound('stableSound').play(); } }, 200); // Start breathing animation for the building self.startBuildingBreathing(); return points; } return 0; }; self.startBuildingBreathing = function () { if (self.building) { // Create continuous breathing animation var breatheIn = function breatheIn() { tween(self.building, { scaleX: 1.375, scaleY: 1.375 }, { duration: 2000, easing: tween.easeInOut, onFinish: breatheOut }); }; var breatheOut = function breatheOut() { tween(self.building, { scaleX: 1.125, scaleY: 1.125 }, { duration: 2000, easing: tween.easeInOut, onFinish: breatheIn }); }; breatheIn(); } }; self.removeBuilding = function () { if (self.hasBuilding && self.building) { // Stop any ongoing breathing animation tween.stop(self.building); self.building.destroy(); self.building = null; self.hasBuilding = false; // Restore original hex graphics hexGraphics.destroy(); hexGraphics = self.attachAsset(self.originalAssetName, { anchorX: 0.5, anchorY: 0.5 }); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x8BC34A }); /**** * Game Code ****/ // Game state var currentTurn = 1; var maxTurns = 20; var score = 0; var character = null; var hexGrid = []; var selectedTile = null; var gameStarted = false; var hasMovedThisTurn = false; var lastTurnBuilding = null; // Track building created this turn var lastTurnPoints = 0; // Track points gained this turn var isBuilding = false; // Track if character is currently building var showingMenu = true; // Track if we're showing the menu var menuContainer = null; // Container for menu elements // Game history for proper undo functionality var gameHistory = []; // Hex grid dimensions var gridWidth = 6; var gridHeight = 12; var hexSize = 200; var hexHeight = 173; // UI positioning offsets var scoreTextOffset = 300; // Create menu function createMenu() { menuContainer = new Container(); // Create PLAY button container var playButtonContainer = new Container(); var playButtonBox = LK.getAsset('buildButtonBg', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.2 }); playButtonContainer.addChild(playButtonBox); var playButton = new Text2('PLAY', { size: 100, fill: 0xFFFFFF, font: "Ink Free" }); playButton.anchor.set(0.5, 0.5); playButtonContainer.addChild(playButton); // Position in center of screen playButtonContainer.x = 1024; // Center X playButtonContainer.y = 1000; // Center Y - moved up double // Create high score display container var highScoreContainer = new Container(); var highScoreBackground = LK.getAsset('scoreContainerBg', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.25, scaleY: 1.75 }); highScoreContainer.addChild(highScoreBackground); var highScore = storage.highScore || 0; var highScoreText = new Text2('High Score: ' + highScore, { size: 45, fill: 0x000000, font: "Ink Free" }); highScoreText.anchor.set(0.5, 0.5); highScoreContainer.addChild(highScoreText); // Create mountain asset that extends to bottom margin var bottomBackgroundContainer = new Container(); var bottomBackground = LK.getAsset('mountain', { anchorX: 0.5, anchorY: 0.5, scaleX: 10.24, scaleY: 6.0 }); bottomBackgroundContainer.addChild(bottomBackground); bottomBackgroundContainer.x = 1024; // Center X bottomBackgroundContainer.y = 2400; // Position to extend to bottom menuContainer.addChild(bottomBackgroundContainer); // Create title asset container var titleAssetContainer = new Container(); // Create title background asset - rectangular shape var titleBackground = LK.getAsset('buildButtonBg', { anchorX: 0.5, anchorY: 0.5, scaleX: 4.0, scaleY: 1.5 }); titleBackground.alpha = 0.9; // Semi-transparent background titleAssetContainer.addChild(titleBackground); // Create game title container var gameTitleContainer = new Container(); var gameTitle = new Text2('Diagonal Farm Builder', { size: 200, fill: 0xFFFFFF, font: "Ink Free" }); gameTitle.anchor.set(0.5, 0.5); gameTitleContainer.addChild(gameTitle); titleAssetContainer.addChild(gameTitleContainer); titleAssetContainer.x = 1024; // Center X titleAssetContainer.y = 500; // Move title up from 600 to 500 menuContainer.addChild(titleAssetContainer); // Start title rotation animation var rotateRight = function rotateRight() { tween(titleAssetContainer, { rotation: 15 * Math.PI / 180 // Convert 15 degrees to radians }, { duration: 3000, easing: tween.easeInOut, onFinish: rotateLeft }); }; var rotateLeft = function rotateLeft() { tween(titleAssetContainer, { rotation: -15 * Math.PI / 180 // Convert -15 degrees to radians }, { duration: 3000, easing: tween.easeInOut, onFinish: rotateRight }); }; rotateRight(); // Start the rotation animation // Position high score container above the background asset highScoreContainer.x = 1024; // Center X highScoreContainer.y = 1700; // Position above the background asset - moved up highScoreContainer.scaleX = 1.875; // Original X scale (0% increase) highScoreContainer.scaleY = 2.1875; // Increase Y scale by 25% (1.75 * 1.25) menuContainer.addChild(highScoreContainer); // Add click handler playButtonContainer.down = function (x, y, obj) { // Animate button press tween(playButtonContainer, { scaleY: 0.9 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(playButtonContainer, { scaleY: 1.0 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { startGame(); } }); } }); }; menuContainer.addChild(playButtonContainer); game.addChild(menuContainer); } // UI elements // Create turn container with background box var turnContainer = new Container(); var turnBackgroundBox = LK.getAsset('turnContainerBg', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.25, scaleY: 1.0 }); turnContainer.addChild(turnBackgroundBox); var turnText = new Text2('Turn: 1/20', { size: 60, fill: 0x000000, font: "Ink Free" }); turnText.anchor.set(0.5, 0.5); turnContainer.addChild(turnText); LK.gui.topLeft.addChild(turnContainer); turnContainer.x = scoreTextOffset + 25; turnContainer.y = 130; // Create score container with background box var scoreContainer = new Container(); var scoreBackgroundBox = LK.getAsset('scoreContainerBg', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.25, scaleY: 1.0 }); scoreContainer.addChild(scoreBackgroundBox); var scoreText = new Text2('Score: 0', { size: 60, fill: 0x000000, font: "Ink Free" }); scoreText.anchor.set(0.5, 0.5); scoreContainer.addChild(scoreText); LK.gui.topRight.addChild(scoreContainer); scoreContainer.x = -scoreTextOffset - 25; scoreContainer.y = 130; // Create building list container var buildingListContainer = new Container(); var buildingTypes = [{ asset: 'barn', points: 15, name: 'Barn' }, { asset: 'well', points: 10, name: 'Well' }, { asset: 'orchard', points: 20, name: 'Orchard' }, { asset: 'wheat', points: 12, name: 'Wheat' }, { asset: 'stable', points: 18, name: 'Stable' }]; for (var i = 0; i < buildingTypes.length; i++) { var buildingItem = new Container(); // Add building icon var buildingIcon = buildingItem.attachAsset(buildingTypes[i].asset, { anchorX: 0, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); // Add points text var pointsText = new Text2(buildingTypes[i].points + ' pts', { size: 36, fill: 0xFFFFFF, font: "Ink Free" }); pointsText.anchor.set(0, 0.5); pointsText.x = 100; buildingItem.addChild(pointsText); // Position each building item vertically buildingItem.y = i * 70 - 140; // Center the list vertically buildingListContainer.addChild(buildingItem); } LK.gui.bottom.addChild(buildingListContainer); buildingListContainer.x = -600; // Position to the left of buttons buildingListContainer.y = -200; // Center vertically between buttons // Create button containers with background boxes var buildButtonContainer = new Container(); var buildButtonBox = LK.getAsset('buildButtonBg', { anchorX: 0.5, anchorY: 0.5 }); buildButtonContainer.addChild(buildButtonBox); var buildButton = new Text2('BUILD', { size: 80, fill: 0xFFFFFF, font: "Ink Free" }); buildButton.anchor.set(0.5, 0.5); buildButton.y = -20; buildButtonContainer.addChild(buildButton); var buildDescription = new Text2('Build on special tiles', { size: 48, fill: 0xFFFFFF, font: "Ink Free" }); buildDescription.anchor.set(0.5, 0.5); buildDescription.y = 30; buildButtonContainer.addChild(buildDescription); LK.gui.bottom.addChild(buildButtonContainer); buildButtonContainer.y = -100; var returnButtonContainer = new Container(); var returnButtonBox = LK.getAsset('returnButtonBg', { anchorX: 0.5, anchorY: 0.5 }); returnButtonContainer.addChild(returnButtonBox); var returnButton = new Text2('RETURN', { size: 80, fill: 0xFFFFFF, font: "Ink Free" }); returnButton.anchor.set(0.5, 0.5); returnButton.y = -20; returnButtonContainer.addChild(returnButton); var returnDescription = new Text2('Undo last turn', { size: 48, fill: 0xFFFFFF, font: "Ink Free" }); returnDescription.anchor.set(0.5, 0.5); returnDescription.y = 30; returnButtonContainer.addChild(returnDescription); LK.gui.bottom.addChild(returnButtonContainer); returnButtonContainer.y = -300; // Create hex grid function createHexGrid() { var startX = 200; var startY = 500; var specialTypes = ['barn', 'well', 'orchard', 'wheat', 'stable']; var specialCounts = { barn: 0, well: 0, orchard: 0, wheat: 0, stable: 0 }; var validPositions = []; var tiles = []; // First pass: collect all valid positions and create tiles array for (var row = 0; row < gridHeight; row++) { hexGrid[row] = []; for (var col = 0; col < gridWidth; col++) { var xOffset = row % 2 * (hexSize * 0.75); var x = startX + col * (hexSize * 1.5) + xOffset; var y = startY + row * (hexHeight * 0.75); // Skip if position would be off screen if (x > 1900 || y > 2400) { continue; } validPositions.push({ row: row, col: col, x: x, y: y }); tiles.push('grass'); // Initialize all as grass } } // Guarantee at least 1 of each special type for (var i = 0; i < specialTypes.length; i++) { if (validPositions.length > 0) { var randomIndex = Math.floor(Math.random() * validPositions.length); tiles[randomIndex] = specialTypes[i]; specialCounts[specialTypes[i]]++; } } // Add more special tiles randomly, respecting the maximum of 5 per type for (var j = 0; j < validPositions.length; j++) { if (tiles[j] === 'grass' && Math.random() < 0.15) { var availableTypes = []; for (var k = 0; k < specialTypes.length; k++) { if (specialCounts[specialTypes[k]] < 5) { availableTypes.push(specialTypes[k]); } } if (availableTypes.length > 0) { var selectedType = availableTypes[Math.floor(Math.random() * availableTypes.length)]; tiles[j] = selectedType; specialCounts[selectedType]++; } } } // Second pass: create actual tiles with determined types for (var m = 0; m < validPositions.length; m++) { var pos = validPositions[m]; var tile = new HexTile(tiles[m], pos.row, pos.col); tile.x = pos.x; tile.y = pos.y; hexGrid[pos.row][pos.col] = tile; game.addChild(tile); } } // Get adjacent tiles (only diagonals) function getAdjacentTiles(row, col) { var adjacent = []; var directions; if (row % 2 === 0) { directions = [[-1, -1], [-1, 0], [1, -1], [1, 0]]; // All diagonal moves for even rows } else { directions = [[-1, 0], [-1, 1], [1, 0], [1, 1]]; // All diagonal moves for odd rows } for (var i = 0; i < directions.length; i++) { var newRow = row + directions[i][0]; var newCol = col + directions[i][1]; if (newRow >= 0 && newRow < gridHeight && newCol >= 0 && newCol < gridWidth && hexGrid[newRow] && hexGrid[newRow][newCol]) { adjacent.push(hexGrid[newRow][newCol]); } } return adjacent; } // Start the actual game function startGame() { showingMenu = false; // Hide menu if (menuContainer) { menuContainer.destroy(); menuContainer = null; } // Show UI elements turnContainer.visible = true; scoreContainer.visible = true; buildingListContainer.visible = true; buildButtonContainer.visible = true; returnButtonContainer.visible = true; // Initialize game initGame(); } // Initialize game function initGame() { createHexGrid(); // Collect all available tiles var availableTiles = []; for (var row = 0; row < gridHeight; row++) { for (var col = 0; col < gridWidth; col++) { if (hexGrid[row] && hexGrid[row][col]) { availableTiles.push(hexGrid[row][col]); } } } // Place character on random available tile if (availableTiles.length > 0) { var randomIndex = Math.floor(Math.random() * availableTiles.length); var randomTile = availableTiles[randomIndex]; character = new Character(); character.moveToTile(randomTile); game.addChild(character); gameStarted = true; } } // Save current game state to history function saveGameState() { var currentState = { turn: currentTurn, score: score, characterRow: character.row, characterCol: character.col, buildings: [] }; // Save all buildings in the grid for (var row = 0; row < gridHeight; row++) { for (var col = 0; col < gridWidth; col++) { if (hexGrid[row] && hexGrid[row][col] && hexGrid[row][col].hasBuilding) { currentState.buildings.push({ row: row, col: col, type: hexGrid[row][col].type }); } } } // Keep only the last two states - add current state and limit to 2 entries gameHistory.push(currentState); if (gameHistory.length > 2) { gameHistory.shift(); // Remove oldest state if we have more than 2 } } // Update UI function updateUI() { turnText.setText('Turn: ' + currentTurn + '/' + maxTurns); scoreText.setText('Score: ' + score); } // Check if character can move to tile function canMoveTo(tile) { var adjacents = getAdjacentTiles(character.row, character.col); return adjacents.indexOf(tile) !== -1; } // End turn function endTurn() { // Save state before incrementing turn to capture previous turn state saveGameState(); currentTurn++; selectedTile = null; hasMovedThisTurn = false; lastTurnBuilding = null; // Reset building tracking lastTurnPoints = 0; // Reset points tracking if (character) { character.previousRow = character.row; character.previousCol = character.col; } if (currentTurn > maxTurns) { LK.setScore(score); resetGameAndShowMenu(); return; } updateUI(); } // Handle tile clicks game.down = function (x, y, obj) { if (showingMenu || !gameStarted || hasMovedThisTurn || isBuilding) { return; } // Check if clicking on a hex tile for (var row = 0; row < gridHeight; row++) { for (var col = 0; col < gridWidth; col++) { if (hexGrid[row] && hexGrid[row][col]) { var tile = hexGrid[row][col]; var distance = Math.sqrt(Math.pow(x - tile.x, 2) + Math.pow(y - tile.y, 2)); if (distance < 100) { if (canMoveTo(tile)) { character.moveToTile(tile); selectedTile = tile; hasMovedThisTurn = true; endTurn(); } return; } } } } }; // Build button handler buildButtonContainer.down = function (x, y, obj) { // Animate button press with Y scale reduction tween(buildButtonContainer, { scaleY: 0.9 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(buildButtonContainer, { scaleY: 1.0 }, { duration: 100, easing: tween.easeOut }); } }); if (!gameStarted) { return; } var currentTile = null; for (var row = 0; row < gridHeight; row++) { for (var col = 0; col < gridWidth; col++) { if (hexGrid[row] && hexGrid[row][col] && hexGrid[row][col].row === character.row && hexGrid[row][col].col === character.col) { currentTile = hexGrid[row][col]; break; } } } if (currentTile && currentTile.type !== 'grass' && !currentTile.hasBuilding && !isBuilding) { var points = currentTile.addBuilding(); if (points > 0) { isBuilding = true; // Block all input during building animation score += points; lastTurnBuilding = currentTile; // Track building created this turn lastTurnPoints = points; // Track points gained this turn updateUI(); // Create dust effect at current tile position var dustEffect = LK.getAsset('dust', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); dustEffect.x = currentTile.x; dustEffect.y = currentTile.y; game.addChild(dustEffect); // Animate dust growing while character bobs tween(dustEffect, { scaleX: 2.25, scaleY: 2.25, alpha: 0.85 }, { duration: 500, easing: tween.easeOut }); // Animate character bobbing up and down for half a second tween(character, { y: character.y - 20 }, { duration: 250, easing: tween.easeInOut, onFinish: function onFinish() { tween(character, { y: character.y + 20 }, { duration: 250, easing: tween.easeInOut, onFinish: function onFinish() { // Animate dust shrinking and disappearing tween(dustEffect, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { dustEffect.destroy(); isBuilding = false; // Re-enable input after animation } }); } }); } }); } } }; // Return button handler returnButtonContainer.down = function (x, y, obj) { // Animate button press with Y scale reduction tween(returnButtonContainer, { scaleY: 0.9 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(returnButtonContainer, { scaleY: 1.0 }, { duration: 100, easing: tween.easeOut }); } }); if (!gameStarted || currentTurn <= 1 || gameHistory.length < 2 || isBuilding) { return; } // Get the second most recent saved state (skip the most recent one) var lastState = gameHistory[gameHistory.length - 2]; // Remove both the most recent and second most recent states from history gameHistory.pop(); // Remove all current buildings for (var row = 0; row < gridHeight; row++) { for (var col = 0; col < gridWidth; col++) { if (hexGrid[row] && hexGrid[row][col] && hexGrid[row][col].hasBuilding) { hexGrid[row][col].removeBuilding(); } } } // Restore game state currentTurn = lastState.turn; score = lastState.score; // Move character back to previous position if (hexGrid[lastState.characterRow] && hexGrid[lastState.characterRow][lastState.characterCol]) { var previousTile = hexGrid[lastState.characterRow][lastState.characterCol]; character.x = previousTile.x; character.y = previousTile.y; character.row = lastState.characterRow; character.col = lastState.characterCol; } // Restore buildings for (var i = 0; i < lastState.buildings.length; i++) { var building = lastState.buildings[i]; if (hexGrid[building.row] && hexGrid[building.row][building.col]) { hexGrid[building.row][building.col].addBuilding(); } } selectedTile = null; hasMovedThisTurn = false; lastTurnBuilding = null; lastTurnPoints = 0; updateUI(); }; // Hide all UI elements initially turnContainer.visible = false; scoreContainer.visible = false; buildingListContainer.visible = false; buildButtonContainer.visible = false; returnButtonContainer.visible = false; // Show menu initially createMenu(); updateUI(); // Start character breathing animation function startCharacterBreathing() { var breatheIn = function breatheIn() { tween(character, { scaleY: 1.2 }, { duration: 2000, easing: tween.easeInOut, onFinish: breatheOut }); }; var breatheOut = function breatheOut() { tween(character, { scaleY: 1.0 }, { duration: 2000, easing: tween.easeInOut, onFinish: breatheIn }); }; breatheIn(); } // Start character breathing animation after character is created if (character) { startCharacterBreathing(); } // Start background music LK.playMusic('backgroundMusic'); // Background color cycling var backgroundColors = [0x8BC34A, 0x87CEEB, 0xDDA0DD, 0xF0E68C, 0xFFA07A, 0x98FB98]; var currentColorIndex = 0; function cycleBackgroundColor() { currentColorIndex = (currentColorIndex + 1) % backgroundColors.length; var currentColor = backgroundColors[currentColorIndex === 0 ? backgroundColors.length - 1 : currentColorIndex - 1]; var nextColor = backgroundColors[currentColorIndex]; // Extract RGB components from hex colors var currentR = currentColor >> 16 & 0xFF; var currentG = currentColor >> 8 & 0xFF; var currentB = currentColor & 0xFF; var nextR = nextColor >> 16 & 0xFF; var nextG = nextColor >> 8 & 0xFF; var nextB = nextColor & 0xFF; // Create a temporary object to tween the RGB components var colorTween = { r: currentR, g: currentG, b: currentB }; tween(colorTween, { r: nextR, g: nextG, b: nextB }, { duration: 3000, easing: tween.easeInOut, onFinish: function onFinish() { // Continue cycling LK.setTimeout(cycleBackgroundColor, 2000); } }); // Update the actual background color during the tween var _updateColor = function updateColor() { var r = Math.floor(colorTween.r); var g = Math.floor(colorTween.g); var b = Math.floor(colorTween.b); var combinedColor = r << 16 | g << 8 | b; game.setBackgroundColor(combinedColor); if (r !== nextR || g !== nextG || b !== nextB) { LK.setTimeout(_updateColor, 16); // ~60fps updates } }; _updateColor(); } // Reset game and show menu function resetGameAndShowMenu() { // Update high score if current score is higher var currentHighScore = storage.highScore || 0; if (score > currentHighScore) { storage.highScore = score; } // Reset all game variables currentTurn = 1; score = 0; gameStarted = false; hasMovedThisTurn = false; lastTurnBuilding = null; lastTurnPoints = 0; isBuilding = false; selectedTile = null; showingMenu = true; // Clear game history gameHistory = []; // Remove character if (character) { character.destroy(); character = null; } // Remove all hex tiles for (var row = 0; row < gridHeight; row++) { for (var col = 0; col < gridWidth; col++) { if (hexGrid[row] && hexGrid[row][col]) { hexGrid[row][col].destroy(); } } } hexGrid = []; // Hide UI elements turnContainer.visible = false; scoreContainer.visible = false; buildingListContainer.visible = false; buildButtonContainer.visible = false; returnButtonContainer.visible = false; // Show menu again createMenu(); updateUI(); } // Start the color cycling after a short delay LK.setTimeout(cycleBackgroundColor, 2000);
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Character = Container.expand(function () {
var self = Container.call(this);
var characterGraphics = self.attachAsset('character', {
anchorX: 0.5,
anchorY: 0.5
});
self.row = 0;
self.col = 0;
self.previousRow = 0;
self.previousCol = 0;
self.moveToTile = function (tile) {
self.previousRow = self.row;
self.previousCol = self.col;
self.x = tile.x;
self.y = tile.y;
self.row = tile.row;
self.col = tile.col;
LK.getSound('move').play();
};
self.returnToPreviousPosition = function () {
if (hexGrid[self.previousRow] && hexGrid[self.previousRow][self.previousCol]) {
var previousTile = hexGrid[self.previousRow][self.previousCol];
self.x = previousTile.x;
self.y = previousTile.y;
self.row = self.previousRow;
self.col = self.previousCol;
LK.getSound('move').play();
}
};
return self;
});
var HexTile = Container.expand(function (type, row, col) {
var self = Container.call(this);
self.type = type;
self.row = row;
self.col = col;
self.hasBuilding = false;
self.building = null;
var assetName = 'hexGrass';
if (type === 'barn') {
assetName = 'hexRed';
} else if (type === 'well') {
assetName = 'hexBlue';
} else if (type === 'orchard') {
assetName = 'hexPurple';
} else if (type === 'wheat') {
assetName = 'hexYellow';
} else if (type === 'stable') {
assetName = 'hexBrown';
}
self.originalAssetName = assetName; // Store original asset name for restoration
var hexGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.addBuilding = function () {
if (self.hasBuilding || self.type === 'grass') {
return false;
}
var buildingAsset = null;
var points = 0;
if (self.type === 'barn') {
buildingAsset = 'barn';
points = 15;
} else if (self.type === 'well') {
buildingAsset = 'well';
points = 10;
} else if (self.type === 'orchard') {
buildingAsset = 'orchard';
points = 20;
} else if (self.type === 'wheat') {
buildingAsset = 'wheat';
points = 12;
} else if (self.type === 'stable') {
buildingAsset = 'stable';
points = 18;
}
if (buildingAsset) {
// Change the hex tile to grass when building is constructed
hexGraphics.destroy();
hexGraphics = self.attachAsset('hexGrass', {
anchorX: 0.5,
anchorY: 0.5
});
self.building = self.attachAsset(buildingAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.25,
scaleY: 1.25
});
self.hasBuilding = true;
LK.getSound('build').play();
// Play building-specific sound after construction sound
LK.setTimeout(function () {
if (self.type === 'barn') {
LK.getSound('barnSound').play();
} else if (self.type === 'well') {
LK.getSound('wellSound').play();
} else if (self.type === 'orchard') {
LK.getSound('orchardSound').play();
} else if (self.type === 'wheat') {
LK.getSound('wheatSound').play();
} else if (self.type === 'stable') {
LK.getSound('stableSound').play();
}
}, 200);
// Start breathing animation for the building
self.startBuildingBreathing();
return points;
}
return 0;
};
self.startBuildingBreathing = function () {
if (self.building) {
// Create continuous breathing animation
var breatheIn = function breatheIn() {
tween(self.building, {
scaleX: 1.375,
scaleY: 1.375
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: breatheOut
});
};
var breatheOut = function breatheOut() {
tween(self.building, {
scaleX: 1.125,
scaleY: 1.125
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: breatheIn
});
};
breatheIn();
}
};
self.removeBuilding = function () {
if (self.hasBuilding && self.building) {
// Stop any ongoing breathing animation
tween.stop(self.building);
self.building.destroy();
self.building = null;
self.hasBuilding = false;
// Restore original hex graphics
hexGraphics.destroy();
hexGraphics = self.attachAsset(self.originalAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8BC34A
});
/****
* Game Code
****/
// Game state
var currentTurn = 1;
var maxTurns = 20;
var score = 0;
var character = null;
var hexGrid = [];
var selectedTile = null;
var gameStarted = false;
var hasMovedThisTurn = false;
var lastTurnBuilding = null; // Track building created this turn
var lastTurnPoints = 0; // Track points gained this turn
var isBuilding = false; // Track if character is currently building
var showingMenu = true; // Track if we're showing the menu
var menuContainer = null; // Container for menu elements
// Game history for proper undo functionality
var gameHistory = [];
// Hex grid dimensions
var gridWidth = 6;
var gridHeight = 12;
var hexSize = 200;
var hexHeight = 173;
// UI positioning offsets
var scoreTextOffset = 300;
// Create menu
function createMenu() {
menuContainer = new Container();
// Create PLAY button container
var playButtonContainer = new Container();
var playButtonBox = LK.getAsset('buildButtonBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.2
});
playButtonContainer.addChild(playButtonBox);
var playButton = new Text2('PLAY', {
size: 100,
fill: 0xFFFFFF,
font: "Ink Free"
});
playButton.anchor.set(0.5, 0.5);
playButtonContainer.addChild(playButton);
// Position in center of screen
playButtonContainer.x = 1024; // Center X
playButtonContainer.y = 1000; // Center Y - moved up double
// Create high score display container
var highScoreContainer = new Container();
var highScoreBackground = LK.getAsset('scoreContainerBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.25,
scaleY: 1.75
});
highScoreContainer.addChild(highScoreBackground);
var highScore = storage.highScore || 0;
var highScoreText = new Text2('High Score: ' + highScore, {
size: 45,
fill: 0x000000,
font: "Ink Free"
});
highScoreText.anchor.set(0.5, 0.5);
highScoreContainer.addChild(highScoreText);
// Create mountain asset that extends to bottom margin
var bottomBackgroundContainer = new Container();
var bottomBackground = LK.getAsset('mountain', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10.24,
scaleY: 6.0
});
bottomBackgroundContainer.addChild(bottomBackground);
bottomBackgroundContainer.x = 1024; // Center X
bottomBackgroundContainer.y = 2400; // Position to extend to bottom
menuContainer.addChild(bottomBackgroundContainer);
// Create title asset container
var titleAssetContainer = new Container();
// Create title background asset - rectangular shape
var titleBackground = LK.getAsset('buildButtonBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4.0,
scaleY: 1.5
});
titleBackground.alpha = 0.9; // Semi-transparent background
titleAssetContainer.addChild(titleBackground);
// Create game title container
var gameTitleContainer = new Container();
var gameTitle = new Text2('Diagonal Farm Builder', {
size: 200,
fill: 0xFFFFFF,
font: "Ink Free"
});
gameTitle.anchor.set(0.5, 0.5);
gameTitleContainer.addChild(gameTitle);
titleAssetContainer.addChild(gameTitleContainer);
titleAssetContainer.x = 1024; // Center X
titleAssetContainer.y = 500; // Move title up from 600 to 500
menuContainer.addChild(titleAssetContainer);
// Start title rotation animation
var rotateRight = function rotateRight() {
tween(titleAssetContainer, {
rotation: 15 * Math.PI / 180 // Convert 15 degrees to radians
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: rotateLeft
});
};
var rotateLeft = function rotateLeft() {
tween(titleAssetContainer, {
rotation: -15 * Math.PI / 180 // Convert -15 degrees to radians
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: rotateRight
});
};
rotateRight(); // Start the rotation animation
// Position high score container above the background asset
highScoreContainer.x = 1024; // Center X
highScoreContainer.y = 1700; // Position above the background asset - moved up
highScoreContainer.scaleX = 1.875; // Original X scale (0% increase)
highScoreContainer.scaleY = 2.1875; // Increase Y scale by 25% (1.75 * 1.25)
menuContainer.addChild(highScoreContainer);
// Add click handler
playButtonContainer.down = function (x, y, obj) {
// Animate button press
tween(playButtonContainer, {
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(playButtonContainer, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
startGame();
}
});
}
});
};
menuContainer.addChild(playButtonContainer);
game.addChild(menuContainer);
}
// UI elements
// Create turn container with background box
var turnContainer = new Container();
var turnBackgroundBox = LK.getAsset('turnContainerBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.25,
scaleY: 1.0
});
turnContainer.addChild(turnBackgroundBox);
var turnText = new Text2('Turn: 1/20', {
size: 60,
fill: 0x000000,
font: "Ink Free"
});
turnText.anchor.set(0.5, 0.5);
turnContainer.addChild(turnText);
LK.gui.topLeft.addChild(turnContainer);
turnContainer.x = scoreTextOffset + 25;
turnContainer.y = 130;
// Create score container with background box
var scoreContainer = new Container();
var scoreBackgroundBox = LK.getAsset('scoreContainerBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.25,
scaleY: 1.0
});
scoreContainer.addChild(scoreBackgroundBox);
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0x000000,
font: "Ink Free"
});
scoreText.anchor.set(0.5, 0.5);
scoreContainer.addChild(scoreText);
LK.gui.topRight.addChild(scoreContainer);
scoreContainer.x = -scoreTextOffset - 25;
scoreContainer.y = 130;
// Create building list container
var buildingListContainer = new Container();
var buildingTypes = [{
asset: 'barn',
points: 15,
name: 'Barn'
}, {
asset: 'well',
points: 10,
name: 'Well'
}, {
asset: 'orchard',
points: 20,
name: 'Orchard'
}, {
asset: 'wheat',
points: 12,
name: 'Wheat'
}, {
asset: 'stable',
points: 18,
name: 'Stable'
}];
for (var i = 0; i < buildingTypes.length; i++) {
var buildingItem = new Container();
// Add building icon
var buildingIcon = buildingItem.attachAsset(buildingTypes[i].asset, {
anchorX: 0,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
// Add points text
var pointsText = new Text2(buildingTypes[i].points + ' pts', {
size: 36,
fill: 0xFFFFFF,
font: "Ink Free"
});
pointsText.anchor.set(0, 0.5);
pointsText.x = 100;
buildingItem.addChild(pointsText);
// Position each building item vertically
buildingItem.y = i * 70 - 140; // Center the list vertically
buildingListContainer.addChild(buildingItem);
}
LK.gui.bottom.addChild(buildingListContainer);
buildingListContainer.x = -600; // Position to the left of buttons
buildingListContainer.y = -200; // Center vertically between buttons
// Create button containers with background boxes
var buildButtonContainer = new Container();
var buildButtonBox = LK.getAsset('buildButtonBg', {
anchorX: 0.5,
anchorY: 0.5
});
buildButtonContainer.addChild(buildButtonBox);
var buildButton = new Text2('BUILD', {
size: 80,
fill: 0xFFFFFF,
font: "Ink Free"
});
buildButton.anchor.set(0.5, 0.5);
buildButton.y = -20;
buildButtonContainer.addChild(buildButton);
var buildDescription = new Text2('Build on special tiles', {
size: 48,
fill: 0xFFFFFF,
font: "Ink Free"
});
buildDescription.anchor.set(0.5, 0.5);
buildDescription.y = 30;
buildButtonContainer.addChild(buildDescription);
LK.gui.bottom.addChild(buildButtonContainer);
buildButtonContainer.y = -100;
var returnButtonContainer = new Container();
var returnButtonBox = LK.getAsset('returnButtonBg', {
anchorX: 0.5,
anchorY: 0.5
});
returnButtonContainer.addChild(returnButtonBox);
var returnButton = new Text2('RETURN', {
size: 80,
fill: 0xFFFFFF,
font: "Ink Free"
});
returnButton.anchor.set(0.5, 0.5);
returnButton.y = -20;
returnButtonContainer.addChild(returnButton);
var returnDescription = new Text2('Undo last turn', {
size: 48,
fill: 0xFFFFFF,
font: "Ink Free"
});
returnDescription.anchor.set(0.5, 0.5);
returnDescription.y = 30;
returnButtonContainer.addChild(returnDescription);
LK.gui.bottom.addChild(returnButtonContainer);
returnButtonContainer.y = -300;
// Create hex grid
function createHexGrid() {
var startX = 200;
var startY = 500;
var specialTypes = ['barn', 'well', 'orchard', 'wheat', 'stable'];
var specialCounts = {
barn: 0,
well: 0,
orchard: 0,
wheat: 0,
stable: 0
};
var validPositions = [];
var tiles = [];
// First pass: collect all valid positions and create tiles array
for (var row = 0; row < gridHeight; row++) {
hexGrid[row] = [];
for (var col = 0; col < gridWidth; col++) {
var xOffset = row % 2 * (hexSize * 0.75);
var x = startX + col * (hexSize * 1.5) + xOffset;
var y = startY + row * (hexHeight * 0.75);
// Skip if position would be off screen
if (x > 1900 || y > 2400) {
continue;
}
validPositions.push({
row: row,
col: col,
x: x,
y: y
});
tiles.push('grass'); // Initialize all as grass
}
}
// Guarantee at least 1 of each special type
for (var i = 0; i < specialTypes.length; i++) {
if (validPositions.length > 0) {
var randomIndex = Math.floor(Math.random() * validPositions.length);
tiles[randomIndex] = specialTypes[i];
specialCounts[specialTypes[i]]++;
}
}
// Add more special tiles randomly, respecting the maximum of 5 per type
for (var j = 0; j < validPositions.length; j++) {
if (tiles[j] === 'grass' && Math.random() < 0.15) {
var availableTypes = [];
for (var k = 0; k < specialTypes.length; k++) {
if (specialCounts[specialTypes[k]] < 5) {
availableTypes.push(specialTypes[k]);
}
}
if (availableTypes.length > 0) {
var selectedType = availableTypes[Math.floor(Math.random() * availableTypes.length)];
tiles[j] = selectedType;
specialCounts[selectedType]++;
}
}
}
// Second pass: create actual tiles with determined types
for (var m = 0; m < validPositions.length; m++) {
var pos = validPositions[m];
var tile = new HexTile(tiles[m], pos.row, pos.col);
tile.x = pos.x;
tile.y = pos.y;
hexGrid[pos.row][pos.col] = tile;
game.addChild(tile);
}
}
// Get adjacent tiles (only diagonals)
function getAdjacentTiles(row, col) {
var adjacent = [];
var directions;
if (row % 2 === 0) {
directions = [[-1, -1], [-1, 0], [1, -1], [1, 0]]; // All diagonal moves for even rows
} else {
directions = [[-1, 0], [-1, 1], [1, 0], [1, 1]]; // All diagonal moves for odd rows
}
for (var i = 0; i < directions.length; i++) {
var newRow = row + directions[i][0];
var newCol = col + directions[i][1];
if (newRow >= 0 && newRow < gridHeight && newCol >= 0 && newCol < gridWidth && hexGrid[newRow] && hexGrid[newRow][newCol]) {
adjacent.push(hexGrid[newRow][newCol]);
}
}
return adjacent;
}
// Start the actual game
function startGame() {
showingMenu = false;
// Hide menu
if (menuContainer) {
menuContainer.destroy();
menuContainer = null;
}
// Show UI elements
turnContainer.visible = true;
scoreContainer.visible = true;
buildingListContainer.visible = true;
buildButtonContainer.visible = true;
returnButtonContainer.visible = true;
// Initialize game
initGame();
}
// Initialize game
function initGame() {
createHexGrid();
// Collect all available tiles
var availableTiles = [];
for (var row = 0; row < gridHeight; row++) {
for (var col = 0; col < gridWidth; col++) {
if (hexGrid[row] && hexGrid[row][col]) {
availableTiles.push(hexGrid[row][col]);
}
}
}
// Place character on random available tile
if (availableTiles.length > 0) {
var randomIndex = Math.floor(Math.random() * availableTiles.length);
var randomTile = availableTiles[randomIndex];
character = new Character();
character.moveToTile(randomTile);
game.addChild(character);
gameStarted = true;
}
}
// Save current game state to history
function saveGameState() {
var currentState = {
turn: currentTurn,
score: score,
characterRow: character.row,
characterCol: character.col,
buildings: []
};
// Save all buildings in the grid
for (var row = 0; row < gridHeight; row++) {
for (var col = 0; col < gridWidth; col++) {
if (hexGrid[row] && hexGrid[row][col] && hexGrid[row][col].hasBuilding) {
currentState.buildings.push({
row: row,
col: col,
type: hexGrid[row][col].type
});
}
}
}
// Keep only the last two states - add current state and limit to 2 entries
gameHistory.push(currentState);
if (gameHistory.length > 2) {
gameHistory.shift(); // Remove oldest state if we have more than 2
}
}
// Update UI
function updateUI() {
turnText.setText('Turn: ' + currentTurn + '/' + maxTurns);
scoreText.setText('Score: ' + score);
}
// Check if character can move to tile
function canMoveTo(tile) {
var adjacents = getAdjacentTiles(character.row, character.col);
return adjacents.indexOf(tile) !== -1;
}
// End turn
function endTurn() {
// Save state before incrementing turn to capture previous turn state
saveGameState();
currentTurn++;
selectedTile = null;
hasMovedThisTurn = false;
lastTurnBuilding = null; // Reset building tracking
lastTurnPoints = 0; // Reset points tracking
if (character) {
character.previousRow = character.row;
character.previousCol = character.col;
}
if (currentTurn > maxTurns) {
LK.setScore(score);
resetGameAndShowMenu();
return;
}
updateUI();
}
// Handle tile clicks
game.down = function (x, y, obj) {
if (showingMenu || !gameStarted || hasMovedThisTurn || isBuilding) {
return;
}
// Check if clicking on a hex tile
for (var row = 0; row < gridHeight; row++) {
for (var col = 0; col < gridWidth; col++) {
if (hexGrid[row] && hexGrid[row][col]) {
var tile = hexGrid[row][col];
var distance = Math.sqrt(Math.pow(x - tile.x, 2) + Math.pow(y - tile.y, 2));
if (distance < 100) {
if (canMoveTo(tile)) {
character.moveToTile(tile);
selectedTile = tile;
hasMovedThisTurn = true;
endTurn();
}
return;
}
}
}
}
};
// Build button handler
buildButtonContainer.down = function (x, y, obj) {
// Animate button press with Y scale reduction
tween(buildButtonContainer, {
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(buildButtonContainer, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeOut
});
}
});
if (!gameStarted) {
return;
}
var currentTile = null;
for (var row = 0; row < gridHeight; row++) {
for (var col = 0; col < gridWidth; col++) {
if (hexGrid[row] && hexGrid[row][col] && hexGrid[row][col].row === character.row && hexGrid[row][col].col === character.col) {
currentTile = hexGrid[row][col];
break;
}
}
}
if (currentTile && currentTile.type !== 'grass' && !currentTile.hasBuilding && !isBuilding) {
var points = currentTile.addBuilding();
if (points > 0) {
isBuilding = true; // Block all input during building animation
score += points;
lastTurnBuilding = currentTile; // Track building created this turn
lastTurnPoints = points; // Track points gained this turn
updateUI();
// Create dust effect at current tile position
var dustEffect = LK.getAsset('dust', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
dustEffect.x = currentTile.x;
dustEffect.y = currentTile.y;
game.addChild(dustEffect);
// Animate dust growing while character bobs
tween(dustEffect, {
scaleX: 2.25,
scaleY: 2.25,
alpha: 0.85
}, {
duration: 500,
easing: tween.easeOut
});
// Animate character bobbing up and down for half a second
tween(character, {
y: character.y - 20
}, {
duration: 250,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(character, {
y: character.y + 20
}, {
duration: 250,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Animate dust shrinking and disappearing
tween(dustEffect, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
dustEffect.destroy();
isBuilding = false; // Re-enable input after animation
}
});
}
});
}
});
}
}
};
// Return button handler
returnButtonContainer.down = function (x, y, obj) {
// Animate button press with Y scale reduction
tween(returnButtonContainer, {
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(returnButtonContainer, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeOut
});
}
});
if (!gameStarted || currentTurn <= 1 || gameHistory.length < 2 || isBuilding) {
return;
}
// Get the second most recent saved state (skip the most recent one)
var lastState = gameHistory[gameHistory.length - 2];
// Remove both the most recent and second most recent states from history
gameHistory.pop();
// Remove all current buildings
for (var row = 0; row < gridHeight; row++) {
for (var col = 0; col < gridWidth; col++) {
if (hexGrid[row] && hexGrid[row][col] && hexGrid[row][col].hasBuilding) {
hexGrid[row][col].removeBuilding();
}
}
}
// Restore game state
currentTurn = lastState.turn;
score = lastState.score;
// Move character back to previous position
if (hexGrid[lastState.characterRow] && hexGrid[lastState.characterRow][lastState.characterCol]) {
var previousTile = hexGrid[lastState.characterRow][lastState.characterCol];
character.x = previousTile.x;
character.y = previousTile.y;
character.row = lastState.characterRow;
character.col = lastState.characterCol;
}
// Restore buildings
for (var i = 0; i < lastState.buildings.length; i++) {
var building = lastState.buildings[i];
if (hexGrid[building.row] && hexGrid[building.row][building.col]) {
hexGrid[building.row][building.col].addBuilding();
}
}
selectedTile = null;
hasMovedThisTurn = false;
lastTurnBuilding = null;
lastTurnPoints = 0;
updateUI();
};
// Hide all UI elements initially
turnContainer.visible = false;
scoreContainer.visible = false;
buildingListContainer.visible = false;
buildButtonContainer.visible = false;
returnButtonContainer.visible = false;
// Show menu initially
createMenu();
updateUI();
// Start character breathing animation
function startCharacterBreathing() {
var breatheIn = function breatheIn() {
tween(character, {
scaleY: 1.2
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: breatheOut
});
};
var breatheOut = function breatheOut() {
tween(character, {
scaleY: 1.0
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: breatheIn
});
};
breatheIn();
}
// Start character breathing animation after character is created
if (character) {
startCharacterBreathing();
}
// Start background music
LK.playMusic('backgroundMusic');
// Background color cycling
var backgroundColors = [0x8BC34A, 0x87CEEB, 0xDDA0DD, 0xF0E68C, 0xFFA07A, 0x98FB98];
var currentColorIndex = 0;
function cycleBackgroundColor() {
currentColorIndex = (currentColorIndex + 1) % backgroundColors.length;
var currentColor = backgroundColors[currentColorIndex === 0 ? backgroundColors.length - 1 : currentColorIndex - 1];
var nextColor = backgroundColors[currentColorIndex];
// Extract RGB components from hex colors
var currentR = currentColor >> 16 & 0xFF;
var currentG = currentColor >> 8 & 0xFF;
var currentB = currentColor & 0xFF;
var nextR = nextColor >> 16 & 0xFF;
var nextG = nextColor >> 8 & 0xFF;
var nextB = nextColor & 0xFF;
// Create a temporary object to tween the RGB components
var colorTween = {
r: currentR,
g: currentG,
b: currentB
};
tween(colorTween, {
r: nextR,
g: nextG,
b: nextB
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Continue cycling
LK.setTimeout(cycleBackgroundColor, 2000);
}
});
// Update the actual background color during the tween
var _updateColor = function updateColor() {
var r = Math.floor(colorTween.r);
var g = Math.floor(colorTween.g);
var b = Math.floor(colorTween.b);
var combinedColor = r << 16 | g << 8 | b;
game.setBackgroundColor(combinedColor);
if (r !== nextR || g !== nextG || b !== nextB) {
LK.setTimeout(_updateColor, 16); // ~60fps updates
}
};
_updateColor();
}
// Reset game and show menu
function resetGameAndShowMenu() {
// Update high score if current score is higher
var currentHighScore = storage.highScore || 0;
if (score > currentHighScore) {
storage.highScore = score;
}
// Reset all game variables
currentTurn = 1;
score = 0;
gameStarted = false;
hasMovedThisTurn = false;
lastTurnBuilding = null;
lastTurnPoints = 0;
isBuilding = false;
selectedTile = null;
showingMenu = true;
// Clear game history
gameHistory = [];
// Remove character
if (character) {
character.destroy();
character = null;
}
// Remove all hex tiles
for (var row = 0; row < gridHeight; row++) {
for (var col = 0; col < gridWidth; col++) {
if (hexGrid[row] && hexGrid[row][col]) {
hexGrid[row][col].destroy();
}
}
}
hexGrid = [];
// Hide UI elements
turnContainer.visible = false;
scoreContainer.visible = false;
buildingListContainer.visible = false;
buildButtonContainer.visible = false;
returnButtonContainer.visible = false;
// Show menu again
createMenu();
updateUI();
}
// Start the color cycling after a short delay
LK.setTimeout(cycleBackgroundColor, 2000);
Fullscreen modern App Store landscape banner, 16:9, high definition, for a game titled "Hex Farm Builder" and with the description "Strategic turn-based farming game on a hexagonal grid where players move a character and build farm structures on special colored tiles to maximize points in 20 turns.". No text on banner!
Quítale el techo y aleja un poco la perspectiva
Un pozo con una cubeta llena de agua. In-Game asset. No shadows
Vuelve la parcela más densa de trigo
Una plantación de frutas rojas. In-Game asset. No shadows
Polvo de construcción denso. In-Game asset. No shadows
Rectángulo de madera color rojo. In-Game asset. 2d. High contrast. No shadows
Rectángulo de madera color café. In-Game asset. 2d. High contrast. No shadows
Elimina el fondo y que se vea derecho respecto al monitor
Cima de una colina redondeada con un sendero que lleva a la cima, mucho césped. In-Game asset. 2d. High contrast. No shadows