/****
* 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();
}
};