/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { bestScore: "undefined" }); /**** * Classes ****/ var AimGuide = Container.expand(function () { var self = Container.call(this); var arrowGraphics = self.attachAsset('aimArrow', { anchorX: 0, anchorY: 0.5 }); self.setAngle = function (angle) { self.rotation = angle; }; self.setVisible = function (visible) { self.visible = visible; }; return self; }); var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.vx = 0; self.vy = 0; self.friction = 0.96; self.isMoving = false; self.inWater = false; self.inHole = false; self.update = function () { if (self.isMoving && !self.inHole) { self.x += self.vx; self.y += self.vy; self.vx *= self.friction; self.vy *= self.friction; // Check if ball has basically stopped if (Math.abs(self.vx) < 0.1 && Math.abs(self.vy) < 0.1) { self.vx = 0; self.vy = 0; self.isMoving = false; self.checkIfStoppedInHazard(); } // Check if ball is out of screen if (self.x < 0 || self.x > COURSE_WIDTH || self.y < 0 || self.y > COURSE_HEIGHT) { LK.showGameOver(); // Trigger game over when the ball goes out of screen } } }; self.hit = function (angle, power) { if (!self.isMoving && !self.inHole && !self.inWater) { self.vx = Math.cos(angle) * power * 1.2; // Further reduce power multiplier for even shorter range self.vy = Math.sin(angle) * power * 1.2; // Further reduce power multiplier for even shorter range self.isMoving = true; LK.getSound('hit').play(); } }; self.bounce = function () { // Reduced velocity bounce self.vx *= -0.7; self.vy *= -0.7; LK.getSound('bounce').play(); }; self.resetPosition = function (x, y) { self.x = x; self.y = y; self.vx = 0; self.vy = 0; self.isMoving = false; self.inWater = false; self.inHole = false; self.visible = true; self.alpha = 1; // Reset ball transparency to fully visible }; self.fallInHole = function () { self.inHole = true; self.isMoving = false; LK.getSound('holeIn').play(); tween(self, { alpha: 0, width: 15, height: 15 }, { duration: 500, easing: tween.easeIn, onFinish: function onFinish() { self.visible = false; } }); }; self.checkIfStoppedInHazard = function () { // This will be implemented in the main game code when hazards are detected }; self.fallInWater = function () { self.inWater = true; self.isMoving = false; LK.getSound('splash').play(); tween(self, { alpha: 0 }, { duration: 800, easing: tween.easeIn, onFinish: function onFinish() { self.visible = false; self.alpha = 0; // Ensure ball is fully transparent when in water } }); }; return self; }); var Hole = Container.expand(function () { var self = Container.call(this); var holeGraphics = self.attachAsset('hole', { anchorX: 0.5, anchorY: 0.5 }); // Add a flag or other indicator later return self; }); var Obstacle = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'bunker'; var obstacleGraphics = self.attachAsset(self.type, { anchorX: 0.5, anchorY: 0.5 }); return self; }); var PowerMeter = Container.expand(function () { var self = Container.call(this); var barBg = self.attachAsset('powerBar', { anchorX: 0, anchorY: 0.5 }); var barFill = self.attachAsset('powerFill', { anchorX: 0, anchorY: 0.5, width: 0 // Start with no power }); self.power = 0; self.charging = false; self.chargeRate = 0.0025; // Further slow down the charge rate for an even slower speed gauge self.maxPower = 30; self.update = function () { if (self.charging) { self.power += self.chargeRate; if (self.power > 1) { self.power = 0; self.chargeRate *= -1; // Reverse direction } else if (self.power < 0) { self.power = 0; self.chargeRate *= -1; // Reverse direction } barFill.width = barBg.width * self.power; } }; self.startCharging = function () { self.power = 0; self.chargeRate = Math.abs(self.chargeRate); self.charging = true; }; self.stopCharging = function () { self.charging = false; return self.power * self.maxPower; // Return actual power value }; self.reset = function () { self.power = 0; self.charging = false; barFill.width = 0; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x33AA33 // Initial color for the grass }); /**** * Game Code ****/ // UI elements var timerText = new Text2('Time: 0s', { size: 60, fill: 0xFFFFFF }); timerText.anchor.set(0, 0); LK.gui.topLeft.addChild(timerText); timerText.x = 200; // Position next to the hole text var holeTimer = 0; var timerInterval; var COURSE_WIDTH = 2048; var COURSE_HEIGHT = 2732; var TILE_SIZE = 100; var GAME_STATE = { AIMING: 'aiming', CHARGING: 'charging', BALL_MOVING: 'ballMoving', HOLE_COMPLETE: 'holeComplete' }; // Game variables var courseLayout = []; var ball; var currentHole; var obstacles = []; var aimGuide; var powerMeter; var currentState; var strokeCount = 0; var holeNumber = 1; var startX, startY; var ballLastPosition = { x: 0, y: 0 }; var maxCols = Math.ceil(COURSE_WIDTH / TILE_SIZE); var maxRows = Math.ceil(COURSE_HEIGHT / TILE_SIZE); // UI elements var strokeText = new Text2('Strokes: 0', { size: 60, fill: 0xFFFFFF }); strokeText.anchor.set(0, 0); LK.gui.topRight.addChild(strokeText); var holeText = new Text2('Hole: 1', { size: 60, fill: 0xFFFFFF }); holeText.anchor.set(1, 0); LK.gui.topLeft.addChild(holeText); holeText.x = 100; // Move away from the top left corner // Generate a random course layout function generateCourse() { clearCourse(); courseLayout = []; // Start the timer holeTimer = 0; timerText.setText('Time: ' + holeTimer + 's'); timerInterval = LK.setInterval(function () { holeTimer++; timerText.setText('Time: ' + holeTimer + 's'); }, 1000); // Set initial game state for (var i = 0; i < maxRows; i++) { courseLayout[i] = []; for (var j = 0; j < maxCols; j++) { courseLayout[i][j] = { type: 'grass', object: null }; } } // Create a fairway from start to hole using a simple algorithm createFairway(); // Add random bunkers and water hazards addHazards(); // Render the course renderCourse(); // Place the hole placeHole(); // Place the ball at the starting position placeBall(); // Update UI updateUI(); // Set initial game state currentState = GAME_STATE.AIMING; } function clearCourse() { // Remove all existing course elements if (ball) { game.removeChild(ball); } if (currentHole) { game.removeChild(currentHole); } for (var i = 0; i < obstacles.length; i++) { game.removeChild(obstacles[i]); } obstacles = []; } function createFairway() { // Define start and end points startX = Math.floor(maxCols / 2); startY = maxRows - 2; var endX = Math.floor(Math.random() * (maxCols - 4)) + 2; var endY = 2; // Mark the path as fairway var x = startX; var y = startY; while (x !== endX || y !== endY) { // Mark current and surrounding tiles as fairway for (var i = -1; i <= 1; i++) { for (var j = -1; j <= 1; j++) { var newY = y + i; var newX = x + j; if (newY >= 0 && newY < maxRows && newX >= 0 && newX < maxCols) { courseLayout[newY][newX].type = 'fairway'; } } } // Move one step closer to the hole if (x < endX) { x++; } else if (x > endX) { x--; } if (y < endY) { y++; } else if (y > endY) { y--; } } // Mark the end point (hole location) courseLayout[endY][endX].type = 'fairway'; for (var i = -1; i <= 1; i++) { for (var j = -1; j <= 1; j++) { var y = endY + i; var x = endX + j; if (y >= 0 && y < maxRows && x >= 0 && x < maxCols) { courseLayout[y][x].type = 'fairway'; } } } // Remember hole position holeX = endX; holeY = endY; } function addHazards() { // Add rough along the sides of the fairway for (var y = 0; y < maxRows; y++) { for (var x = 0; x < maxCols; x++) { if (courseLayout[y][x].type === 'grass') { // Check if adjacent to fairway var adjacentToFairway = false; for (var j = -1; j <= 1; j++) { for (var k = -1; k <= 1; k++) { var newY = y + j; var newX = x + k; if (newY >= 0 && newY < maxRows && newX >= 0 && newX < maxCols && courseLayout[newY][newX].type === 'fairway') { adjacentToFairway = true; break; } } if (adjacentToFairway) { break; } } if (adjacentToFairway) { courseLayout[y][x].type = 'roughGrass'; } } } } } function renderCourse() { // Create the obstacles for hazards for (var y = 0; y < maxRows; y++) { for (var x = 0; x < maxCols; x++) { var tileType = courseLayout[y][x].type; var screenX = x * TILE_SIZE; var screenY = y * TILE_SIZE; } } } function placeHole() { currentHole = new Hole(); currentHole.x = holeX * TILE_SIZE + TILE_SIZE / 2; currentHole.y = holeY * TILE_SIZE + TILE_SIZE / 2; game.addChild(currentHole); } function placeBall() { if (!ball) { ball = new Ball(); game.addChild(ball); // Create aim guide aimGuide = new AimGuide(); game.addChild(aimGuide); // Create power meter powerMeter = new PowerMeter(); game.addChild(powerMeter); powerMeter.x = COURSE_WIDTH / 2 - 200; powerMeter.y = COURSE_HEIGHT - 100; powerMeter.visible = false; } // Position the ball at the starting point ball.resetPosition(startX * TILE_SIZE + TILE_SIZE / 2, startY * TILE_SIZE + TILE_SIZE / 2); ballLastPosition = { x: ball.x, y: ball.y }; // Update aim guide position aimGuide.x = ball.x; aimGuide.y = ball.y; aimGuide.visible = true; aimGuide.setAngle(Math.PI * 1.5); // Point upward by default } function updateUI() { strokeText.setText('Strokes: ' + strokeCount); holeText.setText('Hole: ' + holeNumber); } function getTileAtPosition(x, y) { var gridX = Math.floor(x / TILE_SIZE); var gridY = Math.floor(y / TILE_SIZE); if (gridX >= 0 && gridX < maxCols && gridY >= 0 && gridY < maxRows) { return courseLayout[gridY][gridX]; } return null; } function checkBallCollisions() { // Get current tile var tile = getTileAtPosition(ball.x, ball.y); if (!tile) { return; } // Check for hole if (Math.sqrt(Math.pow(ball.x - currentHole.x, 2) + Math.pow(ball.y - currentHole.y, 2)) < 20) { ball.fallInHole(); completeHole(); LK.showGameOver(); // Trigger game over when the ball falls into the hole return; } // Check for bunker (slows the ball down more) if (tile.type === 'bunker') { ball.friction = 0.92; // More friction in bunkers } else if (tile.type === 'fairway') { ball.friction = 0.98; // Less friction on fairway for easier target } else { ball.friction = 0.96; } // Check for course boundaries if (ball.x < 0 || ball.x > COURSE_WIDTH || ball.y < 0 || ball.y > COURSE_HEIGHT) { ball.bounce(); } } function resetBallToLastPosition() { strokeCount++; // Penalty stroke updateUI(); ball.resetPosition(ballLastPosition.x, ballLastPosition.y); ball.visible = true; // Ensure ball is visible after repositioning ball.alpha = 1; // Reset ball transparency to fully visible currentState = GAME_STATE.AIMING; aimGuide.x = ball.x; aimGuide.y = ball.y; aimGuide.visible = true; } function completeHole() { currentState = GAME_STATE.HOLE_COMPLETE; // Stop the timer LK.clearInterval(timerInterval); // Show score for this hole var scoreText = new Text2('Hole ' + holeNumber + ' completed in ' + strokeCount + ' strokes!', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0.5); scoreText.x = COURSE_WIDTH / 2; scoreText.y = COURSE_HEIGHT / 2 - 100; game.addChild(scoreText); // Save best score var bestHoleScore = storage['hole' + holeNumber] || null; if (bestHoleScore === null || strokeCount < bestHoleScore) { storage['hole' + holeNumber] = strokeCount; var newRecordText = new Text2('New Best Score!', { size: 50, fill: 0xFFFF00 }); newRecordText.anchor.set(0.5, 0.5); newRecordText.x = COURSE_WIDTH / 2; newRecordText.y = COURSE_HEIGHT / 2; game.addChild(newRecordText); } // Add continue button var continueText = new Text2('Tap to continue', { size: 40, fill: 0xFFFFFF }); continueText.anchor.set(0.5, 0.5); continueText.x = COURSE_WIDTH / 2; continueText.y = COURSE_HEIGHT / 2 + 100; game.addChild(continueText); // Flash the text var flashInterval = LK.setInterval(function () { continueText.visible = !continueText.visible; }, 500); // Wait for tap to continue var tapHandler = function tapHandler(x, y) { LK.clearInterval(flashInterval); game.removeChild(scoreText); game.removeChild(continueText); if (newRecordText) { game.removeChild(newRecordText); } // Prepare next hole holeNumber++; strokeCount = 0; generateCourse(); // Remove this handler game.up = function () {}; }; game.up = tapHandler; } // Initialize the course generateCourse(); // Start playing background music LK.playMusic('bgMusic'); // Input handling game.down = function (x, y) { if (currentState === GAME_STATE.AIMING && !ball.isMoving && !ball.inHole && !ball.inWater) { // Start tracking swipe swipeStartX = x; swipeStartY = y; aimGuide.x = ball.x; aimGuide.y = ball.y; aimGuide.visible = true; } }; game.move = function (x, y) { if (currentState === GAME_STATE.AIMING && aimGuide.visible) { // Calculate angle from swipe movement var dx = x - swipeStartX; var dy = y - swipeStartY; var angle = Math.atan2(dy, dx); aimGuide.setAngle(angle); } }; game.up = function (x, y) { if (currentState === GAME_STATE.AIMING && aimGuide.visible) { // Calculate power based on swipe distance var dx = x - swipeStartX; var dy = y - swipeStartY; var distance = Math.sqrt(dx * dx + dy * dy); var power = Math.min(distance / 100, 1) * powerMeter.maxPower; // Hit the ball var angle = aimGuide.rotation; ball.hit(angle, power); aimGuide.visible = false; currentState = GAME_STATE.BALL_MOVING; strokeCount++; updateUI(); // Remember position for potential penalty ballLastPosition = { x: ball.x, y: ball.y }; } }; // Game update loop game.update = function () { // Update ball physics if (ball && currentState === GAME_STATE.BALL_MOVING) { checkBallCollisions(); // Check if ball has stopped moving if (!ball.isMoving && !ball.inHole && !ball.inWater) { currentState = GAME_STATE.AIMING; aimGuide.x = ball.x; aimGuide.y = ball.y; aimGuide.visible = true; } } // Update power meter if (powerMeter && currentState === GAME_STATE.CHARGING) { powerMeter.update(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
bestScore: "undefined"
});
/****
* Classes
****/
var AimGuide = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('aimArrow', {
anchorX: 0,
anchorY: 0.5
});
self.setAngle = function (angle) {
self.rotation = angle;
};
self.setVisible = function (visible) {
self.visible = visible;
};
return self;
});
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.friction = 0.96;
self.isMoving = false;
self.inWater = false;
self.inHole = false;
self.update = function () {
if (self.isMoving && !self.inHole) {
self.x += self.vx;
self.y += self.vy;
self.vx *= self.friction;
self.vy *= self.friction;
// Check if ball has basically stopped
if (Math.abs(self.vx) < 0.1 && Math.abs(self.vy) < 0.1) {
self.vx = 0;
self.vy = 0;
self.isMoving = false;
self.checkIfStoppedInHazard();
}
// Check if ball is out of screen
if (self.x < 0 || self.x > COURSE_WIDTH || self.y < 0 || self.y > COURSE_HEIGHT) {
LK.showGameOver(); // Trigger game over when the ball goes out of screen
}
}
};
self.hit = function (angle, power) {
if (!self.isMoving && !self.inHole && !self.inWater) {
self.vx = Math.cos(angle) * power * 1.2; // Further reduce power multiplier for even shorter range
self.vy = Math.sin(angle) * power * 1.2; // Further reduce power multiplier for even shorter range
self.isMoving = true;
LK.getSound('hit').play();
}
};
self.bounce = function () {
// Reduced velocity bounce
self.vx *= -0.7;
self.vy *= -0.7;
LK.getSound('bounce').play();
};
self.resetPosition = function (x, y) {
self.x = x;
self.y = y;
self.vx = 0;
self.vy = 0;
self.isMoving = false;
self.inWater = false;
self.inHole = false;
self.visible = true;
self.alpha = 1; // Reset ball transparency to fully visible
};
self.fallInHole = function () {
self.inHole = true;
self.isMoving = false;
LK.getSound('holeIn').play();
tween(self, {
alpha: 0,
width: 15,
height: 15
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
self.visible = false;
}
});
};
self.checkIfStoppedInHazard = function () {
// This will be implemented in the main game code when hazards are detected
};
self.fallInWater = function () {
self.inWater = true;
self.isMoving = false;
LK.getSound('splash').play();
tween(self, {
alpha: 0
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
self.visible = false;
self.alpha = 0; // Ensure ball is fully transparent when in water
}
});
};
return self;
});
var Hole = Container.expand(function () {
var self = Container.call(this);
var holeGraphics = self.attachAsset('hole', {
anchorX: 0.5,
anchorY: 0.5
});
// Add a flag or other indicator later
return self;
});
var Obstacle = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'bunker';
var obstacleGraphics = self.attachAsset(self.type, {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var PowerMeter = Container.expand(function () {
var self = Container.call(this);
var barBg = self.attachAsset('powerBar', {
anchorX: 0,
anchorY: 0.5
});
var barFill = self.attachAsset('powerFill', {
anchorX: 0,
anchorY: 0.5,
width: 0 // Start with no power
});
self.power = 0;
self.charging = false;
self.chargeRate = 0.0025; // Further slow down the charge rate for an even slower speed gauge
self.maxPower = 30;
self.update = function () {
if (self.charging) {
self.power += self.chargeRate;
if (self.power > 1) {
self.power = 0;
self.chargeRate *= -1; // Reverse direction
} else if (self.power < 0) {
self.power = 0;
self.chargeRate *= -1; // Reverse direction
}
barFill.width = barBg.width * self.power;
}
};
self.startCharging = function () {
self.power = 0;
self.chargeRate = Math.abs(self.chargeRate);
self.charging = true;
};
self.stopCharging = function () {
self.charging = false;
return self.power * self.maxPower; // Return actual power value
};
self.reset = function () {
self.power = 0;
self.charging = false;
barFill.width = 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x33AA33 // Initial color for the grass
});
/****
* Game Code
****/
// UI elements
var timerText = new Text2('Time: 0s', {
size: 60,
fill: 0xFFFFFF
});
timerText.anchor.set(0, 0);
LK.gui.topLeft.addChild(timerText);
timerText.x = 200; // Position next to the hole text
var holeTimer = 0;
var timerInterval;
var COURSE_WIDTH = 2048;
var COURSE_HEIGHT = 2732;
var TILE_SIZE = 100;
var GAME_STATE = {
AIMING: 'aiming',
CHARGING: 'charging',
BALL_MOVING: 'ballMoving',
HOLE_COMPLETE: 'holeComplete'
};
// Game variables
var courseLayout = [];
var ball;
var currentHole;
var obstacles = [];
var aimGuide;
var powerMeter;
var currentState;
var strokeCount = 0;
var holeNumber = 1;
var startX, startY;
var ballLastPosition = {
x: 0,
y: 0
};
var maxCols = Math.ceil(COURSE_WIDTH / TILE_SIZE);
var maxRows = Math.ceil(COURSE_HEIGHT / TILE_SIZE);
// UI elements
var strokeText = new Text2('Strokes: 0', {
size: 60,
fill: 0xFFFFFF
});
strokeText.anchor.set(0, 0);
LK.gui.topRight.addChild(strokeText);
var holeText = new Text2('Hole: 1', {
size: 60,
fill: 0xFFFFFF
});
holeText.anchor.set(1, 0);
LK.gui.topLeft.addChild(holeText);
holeText.x = 100; // Move away from the top left corner
// Generate a random course layout
function generateCourse() {
clearCourse();
courseLayout = [];
// Start the timer
holeTimer = 0;
timerText.setText('Time: ' + holeTimer + 's');
timerInterval = LK.setInterval(function () {
holeTimer++;
timerText.setText('Time: ' + holeTimer + 's');
}, 1000);
// Set initial game state
for (var i = 0; i < maxRows; i++) {
courseLayout[i] = [];
for (var j = 0; j < maxCols; j++) {
courseLayout[i][j] = {
type: 'grass',
object: null
};
}
}
// Create a fairway from start to hole using a simple algorithm
createFairway();
// Add random bunkers and water hazards
addHazards();
// Render the course
renderCourse();
// Place the hole
placeHole();
// Place the ball at the starting position
placeBall();
// Update UI
updateUI();
// Set initial game state
currentState = GAME_STATE.AIMING;
}
function clearCourse() {
// Remove all existing course elements
if (ball) {
game.removeChild(ball);
}
if (currentHole) {
game.removeChild(currentHole);
}
for (var i = 0; i < obstacles.length; i++) {
game.removeChild(obstacles[i]);
}
obstacles = [];
}
function createFairway() {
// Define start and end points
startX = Math.floor(maxCols / 2);
startY = maxRows - 2;
var endX = Math.floor(Math.random() * (maxCols - 4)) + 2;
var endY = 2;
// Mark the path as fairway
var x = startX;
var y = startY;
while (x !== endX || y !== endY) {
// Mark current and surrounding tiles as fairway
for (var i = -1; i <= 1; i++) {
for (var j = -1; j <= 1; j++) {
var newY = y + i;
var newX = x + j;
if (newY >= 0 && newY < maxRows && newX >= 0 && newX < maxCols) {
courseLayout[newY][newX].type = 'fairway';
}
}
}
// Move one step closer to the hole
if (x < endX) {
x++;
} else if (x > endX) {
x--;
}
if (y < endY) {
y++;
} else if (y > endY) {
y--;
}
}
// Mark the end point (hole location)
courseLayout[endY][endX].type = 'fairway';
for (var i = -1; i <= 1; i++) {
for (var j = -1; j <= 1; j++) {
var y = endY + i;
var x = endX + j;
if (y >= 0 && y < maxRows && x >= 0 && x < maxCols) {
courseLayout[y][x].type = 'fairway';
}
}
}
// Remember hole position
holeX = endX;
holeY = endY;
}
function addHazards() {
// Add rough along the sides of the fairway
for (var y = 0; y < maxRows; y++) {
for (var x = 0; x < maxCols; x++) {
if (courseLayout[y][x].type === 'grass') {
// Check if adjacent to fairway
var adjacentToFairway = false;
for (var j = -1; j <= 1; j++) {
for (var k = -1; k <= 1; k++) {
var newY = y + j;
var newX = x + k;
if (newY >= 0 && newY < maxRows && newX >= 0 && newX < maxCols && courseLayout[newY][newX].type === 'fairway') {
adjacentToFairway = true;
break;
}
}
if (adjacentToFairway) {
break;
}
}
if (adjacentToFairway) {
courseLayout[y][x].type = 'roughGrass';
}
}
}
}
}
function renderCourse() {
// Create the obstacles for hazards
for (var y = 0; y < maxRows; y++) {
for (var x = 0; x < maxCols; x++) {
var tileType = courseLayout[y][x].type;
var screenX = x * TILE_SIZE;
var screenY = y * TILE_SIZE;
}
}
}
function placeHole() {
currentHole = new Hole();
currentHole.x = holeX * TILE_SIZE + TILE_SIZE / 2;
currentHole.y = holeY * TILE_SIZE + TILE_SIZE / 2;
game.addChild(currentHole);
}
function placeBall() {
if (!ball) {
ball = new Ball();
game.addChild(ball);
// Create aim guide
aimGuide = new AimGuide();
game.addChild(aimGuide);
// Create power meter
powerMeter = new PowerMeter();
game.addChild(powerMeter);
powerMeter.x = COURSE_WIDTH / 2 - 200;
powerMeter.y = COURSE_HEIGHT - 100;
powerMeter.visible = false;
}
// Position the ball at the starting point
ball.resetPosition(startX * TILE_SIZE + TILE_SIZE / 2, startY * TILE_SIZE + TILE_SIZE / 2);
ballLastPosition = {
x: ball.x,
y: ball.y
};
// Update aim guide position
aimGuide.x = ball.x;
aimGuide.y = ball.y;
aimGuide.visible = true;
aimGuide.setAngle(Math.PI * 1.5); // Point upward by default
}
function updateUI() {
strokeText.setText('Strokes: ' + strokeCount);
holeText.setText('Hole: ' + holeNumber);
}
function getTileAtPosition(x, y) {
var gridX = Math.floor(x / TILE_SIZE);
var gridY = Math.floor(y / TILE_SIZE);
if (gridX >= 0 && gridX < maxCols && gridY >= 0 && gridY < maxRows) {
return courseLayout[gridY][gridX];
}
return null;
}
function checkBallCollisions() {
// Get current tile
var tile = getTileAtPosition(ball.x, ball.y);
if (!tile) {
return;
}
// Check for hole
if (Math.sqrt(Math.pow(ball.x - currentHole.x, 2) + Math.pow(ball.y - currentHole.y, 2)) < 20) {
ball.fallInHole();
completeHole();
LK.showGameOver(); // Trigger game over when the ball falls into the hole
return;
}
// Check for bunker (slows the ball down more)
if (tile.type === 'bunker') {
ball.friction = 0.92; // More friction in bunkers
} else if (tile.type === 'fairway') {
ball.friction = 0.98; // Less friction on fairway for easier target
} else {
ball.friction = 0.96;
}
// Check for course boundaries
if (ball.x < 0 || ball.x > COURSE_WIDTH || ball.y < 0 || ball.y > COURSE_HEIGHT) {
ball.bounce();
}
}
function resetBallToLastPosition() {
strokeCount++; // Penalty stroke
updateUI();
ball.resetPosition(ballLastPosition.x, ballLastPosition.y);
ball.visible = true; // Ensure ball is visible after repositioning
ball.alpha = 1; // Reset ball transparency to fully visible
currentState = GAME_STATE.AIMING;
aimGuide.x = ball.x;
aimGuide.y = ball.y;
aimGuide.visible = true;
}
function completeHole() {
currentState = GAME_STATE.HOLE_COMPLETE;
// Stop the timer
LK.clearInterval(timerInterval);
// Show score for this hole
var scoreText = new Text2('Hole ' + holeNumber + ' completed in ' + strokeCount + ' strokes!', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = COURSE_WIDTH / 2;
scoreText.y = COURSE_HEIGHT / 2 - 100;
game.addChild(scoreText);
// Save best score
var bestHoleScore = storage['hole' + holeNumber] || null;
if (bestHoleScore === null || strokeCount < bestHoleScore) {
storage['hole' + holeNumber] = strokeCount;
var newRecordText = new Text2('New Best Score!', {
size: 50,
fill: 0xFFFF00
});
newRecordText.anchor.set(0.5, 0.5);
newRecordText.x = COURSE_WIDTH / 2;
newRecordText.y = COURSE_HEIGHT / 2;
game.addChild(newRecordText);
}
// Add continue button
var continueText = new Text2('Tap to continue', {
size: 40,
fill: 0xFFFFFF
});
continueText.anchor.set(0.5, 0.5);
continueText.x = COURSE_WIDTH / 2;
continueText.y = COURSE_HEIGHT / 2 + 100;
game.addChild(continueText);
// Flash the text
var flashInterval = LK.setInterval(function () {
continueText.visible = !continueText.visible;
}, 500);
// Wait for tap to continue
var tapHandler = function tapHandler(x, y) {
LK.clearInterval(flashInterval);
game.removeChild(scoreText);
game.removeChild(continueText);
if (newRecordText) {
game.removeChild(newRecordText);
}
// Prepare next hole
holeNumber++;
strokeCount = 0;
generateCourse();
// Remove this handler
game.up = function () {};
};
game.up = tapHandler;
}
// Initialize the course
generateCourse();
// Start playing background music
LK.playMusic('bgMusic');
// Input handling
game.down = function (x, y) {
if (currentState === GAME_STATE.AIMING && !ball.isMoving && !ball.inHole && !ball.inWater) {
// Start tracking swipe
swipeStartX = x;
swipeStartY = y;
aimGuide.x = ball.x;
aimGuide.y = ball.y;
aimGuide.visible = true;
}
};
game.move = function (x, y) {
if (currentState === GAME_STATE.AIMING && aimGuide.visible) {
// Calculate angle from swipe movement
var dx = x - swipeStartX;
var dy = y - swipeStartY;
var angle = Math.atan2(dy, dx);
aimGuide.setAngle(angle);
}
};
game.up = function (x, y) {
if (currentState === GAME_STATE.AIMING && aimGuide.visible) {
// Calculate power based on swipe distance
var dx = x - swipeStartX;
var dy = y - swipeStartY;
var distance = Math.sqrt(dx * dx + dy * dy);
var power = Math.min(distance / 100, 1) * powerMeter.maxPower;
// Hit the ball
var angle = aimGuide.rotation;
ball.hit(angle, power);
aimGuide.visible = false;
currentState = GAME_STATE.BALL_MOVING;
strokeCount++;
updateUI();
// Remember position for potential penalty
ballLastPosition = {
x: ball.x,
y: ball.y
};
}
};
// Game update loop
game.update = function () {
// Update ball physics
if (ball && currentState === GAME_STATE.BALL_MOVING) {
checkBallCollisions();
// Check if ball has stopped moving
if (!ball.isMoving && !ball.inHole && !ball.inWater) {
currentState = GAME_STATE.AIMING;
aimGuide.x = ball.x;
aimGuide.y = ball.y;
aimGuide.visible = true;
}
}
// Update power meter
if (powerMeter && currentState === GAME_STATE.CHARGING) {
powerMeter.update();
}
};