User prompt
GÖRÜNMEZ DUVARLARI DA KALDIR HALA ÇARPILIYOR TOP ORAYA GELİNCE
User prompt
kenardaki duvarları kaldır
User prompt
Ortadaki duvar gibi yapsana tüm duvarları haritanın ortasındaki duvar olması gerektiği gibi çalışıyor
User prompt
Still the ball gets stuck at the walls doesnt bounce
User prompt
hala bouncleamıyor kenardaki duvarlardan
User prompt
kenardaki duvarlardan sekmiyor
User prompt
Top duvardan sekmiyor sekmesi lazım
User prompt
Deliğin belli bir seviye yanına geldiğinde top otomatik içine girmeli
Code edit (1 edits merged)
Please save this source code
User prompt
Mini Golf Master
Initial prompt
A basic mini golf game, player should click and drag the minigolf ball and make it travel in the map, finally trying to get it in the hole
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ball class var GolfBall = Container.expand(function () { var self = Container.call(this); var ball = self.attachAsset('golfBall', { anchorX: 0.5, anchorY: 0.5 }); self.radius = ball.width / 2; self.vx = 0; self.vy = 0; self.moving = false; // For drag aiming self.aimArrow = null; // For tracking drag self.isAiming = false; self.aimStartX = 0; self.aimStartY = 0; self.aimEndX = 0; self.aimEndY = 0; // Show aim arrow self.showArrow = function (dx, dy) { if (!self.aimArrow) { self.aimArrow = LK.getAsset('golfArrow', { anchorX: 0.5, anchorY: 1 }); self.addChild(self.aimArrow); } var len = Math.sqrt(dx * dx + dy * dy); // Clamp arrow length for visual var maxLen = 300; var arrowLen = Math.min(len, maxLen); self.aimArrow.height = arrowLen; self.aimArrow.rotation = Math.atan2(dy, dx) + Math.PI / 2; self.aimArrow.visible = true; }; self.hideArrow = function () { if (self.aimArrow) { self.aimArrow.visible = false; } }; // Update ball physics self.update = function () { if (self.moving) { // Move ball self.x += self.vx; self.y += self.vy; // Friction var speed = Math.sqrt(self.vx * self.vx + self.vy * self.vy); if (speed > 0) { var friction = 0.98; self.vx *= friction; self.vy *= friction; // Stop if very slow if (Math.sqrt(self.vx * self.vx + self.vy * self.vy) < 1.2) { self.vx = 0; self.vy = 0; self.moving = false; } } } }; return self; }); // Hole class var GolfHole = Container.expand(function () { var self = Container.call(this); var hole = self.attachAsset('golfHole', { anchorX: 0.5, anchorY: 0.5 }); self.radius = hole.width / 2; return self; }); // Wall class var GolfWall = Container.expand(function () { var self = Container.call(this); var wall = self.attachAsset('golfWall', { anchorX: 0.5, anchorY: 0.5 }); self.width = wall.width; self.height = wall.height; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x6abf4b // green grass }); /**** * Game Code ****/ // Menu overlay container // --- MENU SYSTEM --- // Ball: white circle // Hole: black circle // Wall: green rectangle // Arrow: yellow rectangle (for aiming indicator) var menuOverlay = new Container(); // Menu background (semi-transparent) var menuBg = LK.getAsset('golfWall', { anchorX: 0.5, anchorY: 0.5, width: 1200, height: 1200 }); menuBg.alpha = 0.92; menuBg.tint = 0x222222; menuOverlay.addChild(menuBg); menuBg.x = 2048 / 2; menuBg.y = 2732 / 2; // Menu title var menuTitle = new Text2('SELECT MODE', { size: 140, fill: 0xFFFFFF }); menuTitle.anchor.set(0.5, 0.5); menuTitle.x = 2048 / 2; menuTitle.y = 2732 / 2 - 400; menuOverlay.addChild(menuTitle); // Menu options var menuOptions = [{ label: "SANDBOX", desc: "NO TIME LIMIT", timer: null, increment: 0 }, { label: "BEGINNER", desc: "START: 30s, +5s/hole", timer: 30, increment: 5 }, { label: "PRO", desc: "START: 10s, +3s/hole", timer: 10, increment: 3 }, { label: "EXPERT", desc: "START: 3s, +2s/hole", timer: 3, increment: 2 }]; var menuButtons = []; var menuDescLabels = []; var menuButtonYStart = 2732 / 2 - 180; var menuButtonSpacing = 220; for (var i = 0; i < menuOptions.length; i++) { // Button var btn = new Text2(menuOptions[i].label, { size: 120, fill: 0xFFFF00 }); btn.anchor.set(0.5, 0.5); btn.x = 2048 / 2; btn.y = menuButtonYStart + i * menuButtonSpacing; btn.interactive = true; btn.buttonMode = true; menuOverlay.addChild(btn); menuButtons.push(btn); // Description // (Removed description text under menu options) } // Add a back button to the menu overlay var menuBackBtn = new Text2('BACK', { size: 90, fill: 0xFF4444 }); menuBackBtn.anchor.set(0.5, 0.5); menuBackBtn.x = 2048 - 200; menuBackBtn.y = 200; menuBackBtn.interactive = true; menuBackBtn.buttonMode = true; menuBackBtn.down = function (x, y, obj) { // Just hide the menu overlay, do not start a game if (menuOverlay.parent) { menuOverlay.parent.removeChild(menuOverlay); } // Optionally, you could reset to a splash screen or do nothing // For now, just show the menu again (if needed, you can add more logic here) game.addChild(menuOverlay); }; menuOverlay.addChild(menuBackBtn); // Add menu overlay to game game.addChild(menuOverlay); // Hide back button when menu is shown var backBtn = null; menuOverlay.onAdded = function () { if (backBtn) { backBtn.visible = false; } }; // Hide game elements until menu is dismissed var margin = 120; var courseWidth = 2048 - margin * 2; var courseHeight = 2732 - margin * 2; var walls = []; var topWall, bottomWall, leftWall, rightWall; var hole, ball; var strokes = 0; var score = 0; var scoreLabel, scoreTxt, timerDuration, timerValue, timerInterval, timerTxt; var timeIncrement = 0; var sandboxMode = false; // Helper to show/hide game elements function setGameElementsVisible(visible) { if (topWall) { topWall.visible = visible; } if (bottomWall) { bottomWall.visible = visible; } if (leftWall) { leftWall.visible = visible; } if (rightWall) { rightWall.visible = visible; } if (hole) { hole.visible = visible; } if (ball) { ball.visible = visible; } if (scoreLabel) { scoreLabel.visible = visible; } if (scoreTxt) { scoreTxt.visible = visible; } if (timerTxt) { timerTxt.visible = visible; } if (typeof backBtn !== "undefined" && backBtn !== null) { backBtn.visible = visible; } } // Remove menu and start game with selected mode function startGameWithMode(modeIdx) { // Create back button if not already created if (typeof backBtn === "undefined" || backBtn === null) { backBtn = new Text2('BACK', { size: 90, fill: 0xFF4444 }); backBtn.anchor.set(0.5, 0.5); backBtn.x = 2048 - 200; backBtn.y = 200; backBtn.interactive = true; backBtn.buttonMode = true; backBtn.visible = false; backBtn.down = function (x, y, obj) { // Remove all game elements setGameElementsVisible(false); // Remove walls for (var i = 0; i < walls.length; i++) { if (walls[i] && walls[i].parent) { walls[i].parent.removeChild(walls[i]); } } walls = []; // Remove hole and ball if (hole && hole.parent) { hole.parent.removeChild(hole); } if (ball && ball.parent) { ball.parent.removeChild(ball); } hole = null; ball = null; // Remove score and timer if (scoreLabel && scoreLabel.parent) { scoreLabel.parent.removeChild(scoreLabel); } if (scoreTxt && scoreTxt.parent) { scoreTxt.parent.removeChild(scoreTxt); } if (timerTxt && timerTxt.parent) { timerTxt.parent.removeChild(timerTxt); } // Remove back button if (backBtn && backBtn.parent) { backBtn.parent.removeChild(backBtn); } // Stop timer if running if (timerInterval) { LK.clearInterval(timerInterval); timerInterval = null; } // Show menu overlay again if (!menuOverlay.parent) { game.addChild(menuOverlay); } setGameElementsVisible(false); }; // Always add backBtn after all walls and game elements so it's on top game.addChild(backBtn); if (backBtn.parent) { backBtn.parent.removeChild(backBtn); game.addChild(backBtn); } } backBtn.visible = true; if (backBtn.parent) { backBtn.parent.removeChild(backBtn); game.addChild(backBtn); } // Remove menu overlay if (menuOverlay.parent) { menuOverlay.parent.removeChild(menuOverlay); } // Set timer and increment var opt = menuOptions[modeIdx]; sandboxMode = opt.timer === null; timerDuration = opt.timer === null ? 0 : opt.timer; timeIncrement = opt.increment; // Create walls topWall = new GolfWall(); topWall.width = courseWidth; topWall.height = 600; topWall.x = 2048 / 2; topWall.y = margin - 200; topWall.children[0].width = courseWidth; topWall.children[0].height = 120; walls.push(topWall); game.addChild(topWall); bottomWall = new GolfWall(); bottomWall.width = courseWidth; bottomWall.height = 600; bottomWall.x = 2048 / 2; bottomWall.y = 2732 - margin + 200; bottomWall.children[0].width = courseWidth; bottomWall.children[0].height = 120; walls.push(bottomWall); game.addChild(bottomWall); leftWall = new GolfWall(); leftWall.width = 350; leftWall.height = courseHeight; leftWall.x = margin - 75; leftWall.y = 2732 / 2; leftWall.children[0].width = 120; leftWall.children[0].height = courseHeight; walls.push(leftWall); game.addChild(leftWall); rightWall = new GolfWall(); rightWall.width = 350; rightWall.height = courseHeight; rightWall.x = 2048 - margin + 75; rightWall.y = 2732 / 2; rightWall.children[0].width = 120; rightWall.children[0].height = courseHeight; walls.push(rightWall); game.addChild(rightWall); // Create hole hole = new GolfHole(); hole.x = 2048 - margin - 200; hole.y = 2732 - margin - 200; game.addChild(hole); // Create ball ball = new GolfBall(); ball.x = margin + 200; ball.y = 2732 - margin - 400; game.addChild(ball); // Strokes and score strokes = 0; score = 0; // Score label (number of holes) at the center of the screen scoreLabel = new Text2('Score: ' + score, { size: 100, fill: 0xFFFFFF }); scoreLabel.anchor.set(0.5, 0.5); game.addChild(scoreLabel); scoreLabel.x = 2048 / 2; scoreLabel.y = 150; // Score text (Strokes) at the top center scoreTxt = new Text2('Strokes: ' + strokes, { size: 100, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.x = 2048 / 2; scoreTxt.y = -80; // Move score text even higher (above the very top) // Timer variables timerValue = timerDuration; timerInterval = null; // Timer text timerTxt = new Text2(sandboxMode ? '∞' : timerValue, { size: 100, fill: 0xFFFFFF }); timerTxt.anchor.set(0.5, 0.5); game.addChild(timerTxt); timerTxt.x = 2048 / 2; timerTxt.y = 2732 / 2; // Remove the pause button from the GUI overlay if it exists if (LK.gui.pause) { LK.gui.pause.visible = false; if (LK.gui.pause.parent) { LK.gui.pause.parent.removeChild(LK.gui.pause); } } // Start timer if not sandbox if (!sandboxMode) { startTimer(); } else { timerTxt.setText('∞'); } setGameElementsVisible(true); } // Attach menu button handlers for (var i = 0; i < menuButtons.length; i++) { (function (idx) { menuButtons[idx].down = function (x, y, obj) { setGameElementsVisible(false); startGameWithMode(idx); }; })(i); } // Hide game elements until menu is dismissed setGameElementsVisible(false); // --- END MENU SYSTEM --- // Function to start/reset timer function startTimer() { timerValue = timerDuration; timerTxt.setText(timerValue); if (timerInterval) { LK.clearInterval(timerInterval); } timerInterval = LK.setInterval(function () { timerValue -= 1; if (timerValue < 0) { timerValue = 0; } timerTxt.setText(timerValue); if (timerValue <= 0) { LK.clearInterval(timerInterval); LK.showGameOver(); } }, 1000); } // Drag/aim state var isDragging = false; var dragStartX = 0; var dragStartY = 0; // Helper: clamp ball inside course function clampBallPosition() { // No clamping to invisible walls; allow ball to go to the very edge of the course. } // Helper: ball-wall collision function handleWallCollisions() { for (var i = 0; i < walls.length; i++) { var w = walls[i]; // Axis-aligned rectangle collision var wx = w.x; var wy = w.y; var ww = w.width; var wh = w.height; var bx = ball.x; var by = ball.y; var r = ball.radius; // Rectangle bounds var left = wx - ww / 2; var right = wx + ww / 2; var top = wy - wh / 2; var bottom = wy + wh / 2; // Find closest point on wall to ball var closestX = Math.max(left, Math.min(bx, right)); var closestY = Math.max(top, Math.min(by, bottom)); var dx = bx - closestX; var dy = by - closestY; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < r) { // Ball is colliding with wall // Push ball out var overlap = r - dist + 1; if (dist === 0) { // Prevent NaN dx = 1; dy = 0; dist = 1; } // Move ball out of wall ball.x += dx / dist * overlap; ball.y += dy / dist * overlap; // Calculate the normal vector var nx = dx / dist; var ny = dy / dist; // Calculate dot product of velocity and normal var dot = ball.vx * nx + ball.vy * ny; // Reflect velocity using the normal (like center wall) ball.vx = ball.vx - 2 * dot * nx; ball.vy = ball.vy - 2 * dot * ny; // Dampen speed ball.vx *= 0.7; ball.vy *= 0.7; } } } // Helper: ball-in-hole detection function checkHole() { var dx = ball.x - hole.x; var dy = ball.y - hole.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < hole.radius - ball.radius / 2) { // Ball in hole! Instantly hide ball and the hole ball.moving = false; ball.vx = 0; ball.vy = 0; ball.visible = false; hole.visible = false; return true; } return false; } // Helper: reset ball after win (not needed, handled by LK.showYouWin) // Game move handler (for drag aiming) function handleMove(x, y, obj) { if (isDragging && !ball.moving) { // Clamp drag to max length var dx = x - dragStartX; var dy = y - dragStartY; var maxDrag = 400; var dragLen = Math.sqrt(dx * dx + dy * dy); if (dragLen > maxDrag) { var scale = maxDrag / dragLen; dx *= scale; dy *= scale; } // Show aim arrow ball.showArrow(-dx, -dy); } } // Game down handler (start drag) game.down = function (x, y, obj) { // Only allow aiming if ball exists, is not moving, and touch is near ball if (typeof ball !== "undefined" && ball !== null && !ball.moving) { var dx = x - ball.x; var dy = y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < ball.radius + 40) { isDragging = true; dragStartX = ball.x; dragStartY = ball.y; ball.aimStartX = x; ball.aimStartY = y; ball.showArrow(0, 0); } } }; // Game move handler game.move = function (x, y, obj) { if (isDragging && !ball.moving) { // Update aim arrow var dx = x - dragStartX; var dy = y - dragStartY; var maxDrag = 400; var dragLen = Math.sqrt(dx * dx + dy * dy); if (dragLen > maxDrag) { var scale = maxDrag / dragLen; dx *= scale; dy *= scale; } ball.showArrow(-dx, -dy); } }; // Game up handler (release drag, shoot) game.up = function (x, y, obj) { if (isDragging && !ball.moving) { var dx = x - dragStartX; var dy = y - dragStartY; // Power proportional to drag length var power = Math.sqrt(dx * dx + dy * dy); var maxPower = 38; var minPower = 10; var shotPower = Math.min(power / 10, maxPower); if (shotPower < minPower / 2) { // Too short, ignore ball.hideArrow(); isDragging = false; return; } // Set velocity (opposite direction of drag) var angle = Math.atan2(dy, dx); ball.vx = -Math.cos(angle) * shotPower; ball.vy = -Math.sin(angle) * shotPower; ball.moving = true; ball.hideArrow(); isDragging = false; // Increment strokes strokes += 1; scoreTxt.setText('Strokes: ' + strokes); } }; // Main game update game.update = function () { // Ball physics if (typeof ball !== "undefined" && ball !== null && typeof ball.update === "function") { ball.update(); } // Clamp ball inside course clampBallPosition(); // Ball-wall collisions handleWallCollisions(); // Ball-in-hole if (typeof hole !== "undefined" && hole !== null && hole.visible && ball && ball.visible) { var dx = ball.x - hole.x; var dy = ball.y - hole.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < hole.radius + ball.radius / 2) { // Ball touched the hole! Instantly hide the hole, but do not dequeue or hide the ball if (hole.parent) { hole.parent.removeChild(hole); } hole.visible = false; // Increment score and update label score += 1; scoreLabel.setText('Score: ' + score); // Add time increment if not sandbox if (!sandboxMode) { timerValue = Math.min(timerValue + timeIncrement, 99); timerTxt.setText(timerValue); } else { timerTxt.setText('∞'); } // Do not stop the ball when it touches the hole; let it keep moving // Create a new hole at a random position (not too close to the ball or walls) var newHole = new GolfHole(); var safeMargin = margin + 200; var minDistFromBall = 400; var placed = false; for (var tries = 0; tries < 20 && !placed; tries++) { var hx = Math.random() * (2048 - 2 * safeMargin) + safeMargin; var hy = Math.random() * (2732 - 2 * safeMargin) + safeMargin; var bdx = hx - ball.x; var bdy = hy - ball.y; var bdist = Math.sqrt(bdx * bdx + bdy * bdy); if (bdist > minDistFromBall) { newHole.x = hx; newHole.y = hy; placed = true; } } if (!placed) { // fallback: just put it somewhere safe newHole.x = 2048 / 2; newHole.y = 2732 / 2; } game.addChild(newHole); hole = newHole; // No timer reset here, just add time increment above } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball class
var GolfBall = Container.expand(function () {
var self = Container.call(this);
var ball = self.attachAsset('golfBall', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = ball.width / 2;
self.vx = 0;
self.vy = 0;
self.moving = false;
// For drag aiming
self.aimArrow = null;
// For tracking drag
self.isAiming = false;
self.aimStartX = 0;
self.aimStartY = 0;
self.aimEndX = 0;
self.aimEndY = 0;
// Show aim arrow
self.showArrow = function (dx, dy) {
if (!self.aimArrow) {
self.aimArrow = LK.getAsset('golfArrow', {
anchorX: 0.5,
anchorY: 1
});
self.addChild(self.aimArrow);
}
var len = Math.sqrt(dx * dx + dy * dy);
// Clamp arrow length for visual
var maxLen = 300;
var arrowLen = Math.min(len, maxLen);
self.aimArrow.height = arrowLen;
self.aimArrow.rotation = Math.atan2(dy, dx) + Math.PI / 2;
self.aimArrow.visible = true;
};
self.hideArrow = function () {
if (self.aimArrow) {
self.aimArrow.visible = false;
}
};
// Update ball physics
self.update = function () {
if (self.moving) {
// Move ball
self.x += self.vx;
self.y += self.vy;
// Friction
var speed = Math.sqrt(self.vx * self.vx + self.vy * self.vy);
if (speed > 0) {
var friction = 0.98;
self.vx *= friction;
self.vy *= friction;
// Stop if very slow
if (Math.sqrt(self.vx * self.vx + self.vy * self.vy) < 1.2) {
self.vx = 0;
self.vy = 0;
self.moving = false;
}
}
}
};
return self;
});
// Hole class
var GolfHole = Container.expand(function () {
var self = Container.call(this);
var hole = self.attachAsset('golfHole', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = hole.width / 2;
return self;
});
// Wall class
var GolfWall = Container.expand(function () {
var self = Container.call(this);
var wall = self.attachAsset('golfWall', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = wall.width;
self.height = wall.height;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x6abf4b // green grass
});
/****
* Game Code
****/
// Menu overlay container
// --- MENU SYSTEM ---
// Ball: white circle
// Hole: black circle
// Wall: green rectangle
// Arrow: yellow rectangle (for aiming indicator)
var menuOverlay = new Container();
// Menu background (semi-transparent)
var menuBg = LK.getAsset('golfWall', {
anchorX: 0.5,
anchorY: 0.5,
width: 1200,
height: 1200
});
menuBg.alpha = 0.92;
menuBg.tint = 0x222222;
menuOverlay.addChild(menuBg);
menuBg.x = 2048 / 2;
menuBg.y = 2732 / 2;
// Menu title
var menuTitle = new Text2('SELECT MODE', {
size: 140,
fill: 0xFFFFFF
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 2048 / 2;
menuTitle.y = 2732 / 2 - 400;
menuOverlay.addChild(menuTitle);
// Menu options
var menuOptions = [{
label: "SANDBOX",
desc: "NO TIME LIMIT",
timer: null,
increment: 0
}, {
label: "BEGINNER",
desc: "START: 30s, +5s/hole",
timer: 30,
increment: 5
}, {
label: "PRO",
desc: "START: 10s, +3s/hole",
timer: 10,
increment: 3
}, {
label: "EXPERT",
desc: "START: 3s, +2s/hole",
timer: 3,
increment: 2
}];
var menuButtons = [];
var menuDescLabels = [];
var menuButtonYStart = 2732 / 2 - 180;
var menuButtonSpacing = 220;
for (var i = 0; i < menuOptions.length; i++) {
// Button
var btn = new Text2(menuOptions[i].label, {
size: 120,
fill: 0xFFFF00
});
btn.anchor.set(0.5, 0.5);
btn.x = 2048 / 2;
btn.y = menuButtonYStart + i * menuButtonSpacing;
btn.interactive = true;
btn.buttonMode = true;
menuOverlay.addChild(btn);
menuButtons.push(btn);
// Description
// (Removed description text under menu options)
}
// Add a back button to the menu overlay
var menuBackBtn = new Text2('BACK', {
size: 90,
fill: 0xFF4444
});
menuBackBtn.anchor.set(0.5, 0.5);
menuBackBtn.x = 2048 - 200;
menuBackBtn.y = 200;
menuBackBtn.interactive = true;
menuBackBtn.buttonMode = true;
menuBackBtn.down = function (x, y, obj) {
// Just hide the menu overlay, do not start a game
if (menuOverlay.parent) {
menuOverlay.parent.removeChild(menuOverlay);
}
// Optionally, you could reset to a splash screen or do nothing
// For now, just show the menu again (if needed, you can add more logic here)
game.addChild(menuOverlay);
};
menuOverlay.addChild(menuBackBtn);
// Add menu overlay to game
game.addChild(menuOverlay);
// Hide back button when menu is shown
var backBtn = null;
menuOverlay.onAdded = function () {
if (backBtn) {
backBtn.visible = false;
}
};
// Hide game elements until menu is dismissed
var margin = 120;
var courseWidth = 2048 - margin * 2;
var courseHeight = 2732 - margin * 2;
var walls = [];
var topWall, bottomWall, leftWall, rightWall;
var hole, ball;
var strokes = 0;
var score = 0;
var scoreLabel, scoreTxt, timerDuration, timerValue, timerInterval, timerTxt;
var timeIncrement = 0;
var sandboxMode = false;
// Helper to show/hide game elements
function setGameElementsVisible(visible) {
if (topWall) {
topWall.visible = visible;
}
if (bottomWall) {
bottomWall.visible = visible;
}
if (leftWall) {
leftWall.visible = visible;
}
if (rightWall) {
rightWall.visible = visible;
}
if (hole) {
hole.visible = visible;
}
if (ball) {
ball.visible = visible;
}
if (scoreLabel) {
scoreLabel.visible = visible;
}
if (scoreTxt) {
scoreTxt.visible = visible;
}
if (timerTxt) {
timerTxt.visible = visible;
}
if (typeof backBtn !== "undefined" && backBtn !== null) {
backBtn.visible = visible;
}
}
// Remove menu and start game with selected mode
function startGameWithMode(modeIdx) {
// Create back button if not already created
if (typeof backBtn === "undefined" || backBtn === null) {
backBtn = new Text2('BACK', {
size: 90,
fill: 0xFF4444
});
backBtn.anchor.set(0.5, 0.5);
backBtn.x = 2048 - 200;
backBtn.y = 200;
backBtn.interactive = true;
backBtn.buttonMode = true;
backBtn.visible = false;
backBtn.down = function (x, y, obj) {
// Remove all game elements
setGameElementsVisible(false);
// Remove walls
for (var i = 0; i < walls.length; i++) {
if (walls[i] && walls[i].parent) {
walls[i].parent.removeChild(walls[i]);
}
}
walls = [];
// Remove hole and ball
if (hole && hole.parent) {
hole.parent.removeChild(hole);
}
if (ball && ball.parent) {
ball.parent.removeChild(ball);
}
hole = null;
ball = null;
// Remove score and timer
if (scoreLabel && scoreLabel.parent) {
scoreLabel.parent.removeChild(scoreLabel);
}
if (scoreTxt && scoreTxt.parent) {
scoreTxt.parent.removeChild(scoreTxt);
}
if (timerTxt && timerTxt.parent) {
timerTxt.parent.removeChild(timerTxt);
}
// Remove back button
if (backBtn && backBtn.parent) {
backBtn.parent.removeChild(backBtn);
}
// Stop timer if running
if (timerInterval) {
LK.clearInterval(timerInterval);
timerInterval = null;
}
// Show menu overlay again
if (!menuOverlay.parent) {
game.addChild(menuOverlay);
}
setGameElementsVisible(false);
};
// Always add backBtn after all walls and game elements so it's on top
game.addChild(backBtn);
if (backBtn.parent) {
backBtn.parent.removeChild(backBtn);
game.addChild(backBtn);
}
}
backBtn.visible = true;
if (backBtn.parent) {
backBtn.parent.removeChild(backBtn);
game.addChild(backBtn);
}
// Remove menu overlay
if (menuOverlay.parent) {
menuOverlay.parent.removeChild(menuOverlay);
}
// Set timer and increment
var opt = menuOptions[modeIdx];
sandboxMode = opt.timer === null;
timerDuration = opt.timer === null ? 0 : opt.timer;
timeIncrement = opt.increment;
// Create walls
topWall = new GolfWall();
topWall.width = courseWidth;
topWall.height = 600;
topWall.x = 2048 / 2;
topWall.y = margin - 200;
topWall.children[0].width = courseWidth;
topWall.children[0].height = 120;
walls.push(topWall);
game.addChild(topWall);
bottomWall = new GolfWall();
bottomWall.width = courseWidth;
bottomWall.height = 600;
bottomWall.x = 2048 / 2;
bottomWall.y = 2732 - margin + 200;
bottomWall.children[0].width = courseWidth;
bottomWall.children[0].height = 120;
walls.push(bottomWall);
game.addChild(bottomWall);
leftWall = new GolfWall();
leftWall.width = 350;
leftWall.height = courseHeight;
leftWall.x = margin - 75;
leftWall.y = 2732 / 2;
leftWall.children[0].width = 120;
leftWall.children[0].height = courseHeight;
walls.push(leftWall);
game.addChild(leftWall);
rightWall = new GolfWall();
rightWall.width = 350;
rightWall.height = courseHeight;
rightWall.x = 2048 - margin + 75;
rightWall.y = 2732 / 2;
rightWall.children[0].width = 120;
rightWall.children[0].height = courseHeight;
walls.push(rightWall);
game.addChild(rightWall);
// Create hole
hole = new GolfHole();
hole.x = 2048 - margin - 200;
hole.y = 2732 - margin - 200;
game.addChild(hole);
// Create ball
ball = new GolfBall();
ball.x = margin + 200;
ball.y = 2732 - margin - 400;
game.addChild(ball);
// Strokes and score
strokes = 0;
score = 0;
// Score label (number of holes) at the center of the screen
scoreLabel = new Text2('Score: ' + score, {
size: 100,
fill: 0xFFFFFF
});
scoreLabel.anchor.set(0.5, 0.5);
game.addChild(scoreLabel);
scoreLabel.x = 2048 / 2;
scoreLabel.y = 150;
// Score text (Strokes) at the top center
scoreTxt = new Text2('Strokes: ' + strokes, {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.x = 2048 / 2;
scoreTxt.y = -80; // Move score text even higher (above the very top)
// Timer variables
timerValue = timerDuration;
timerInterval = null;
// Timer text
timerTxt = new Text2(sandboxMode ? '∞' : timerValue, {
size: 100,
fill: 0xFFFFFF
});
timerTxt.anchor.set(0.5, 0.5);
game.addChild(timerTxt);
timerTxt.x = 2048 / 2;
timerTxt.y = 2732 / 2;
// Remove the pause button from the GUI overlay if it exists
if (LK.gui.pause) {
LK.gui.pause.visible = false;
if (LK.gui.pause.parent) {
LK.gui.pause.parent.removeChild(LK.gui.pause);
}
}
// Start timer if not sandbox
if (!sandboxMode) {
startTimer();
} else {
timerTxt.setText('∞');
}
setGameElementsVisible(true);
}
// Attach menu button handlers
for (var i = 0; i < menuButtons.length; i++) {
(function (idx) {
menuButtons[idx].down = function (x, y, obj) {
setGameElementsVisible(false);
startGameWithMode(idx);
};
})(i);
}
// Hide game elements until menu is dismissed
setGameElementsVisible(false);
// --- END MENU SYSTEM ---
// Function to start/reset timer
function startTimer() {
timerValue = timerDuration;
timerTxt.setText(timerValue);
if (timerInterval) {
LK.clearInterval(timerInterval);
}
timerInterval = LK.setInterval(function () {
timerValue -= 1;
if (timerValue < 0) {
timerValue = 0;
}
timerTxt.setText(timerValue);
if (timerValue <= 0) {
LK.clearInterval(timerInterval);
LK.showGameOver();
}
}, 1000);
}
// Drag/aim state
var isDragging = false;
var dragStartX = 0;
var dragStartY = 0;
// Helper: clamp ball inside course
function clampBallPosition() {
// No clamping to invisible walls; allow ball to go to the very edge of the course.
}
// Helper: ball-wall collision
function handleWallCollisions() {
for (var i = 0; i < walls.length; i++) {
var w = walls[i];
// Axis-aligned rectangle collision
var wx = w.x;
var wy = w.y;
var ww = w.width;
var wh = w.height;
var bx = ball.x;
var by = ball.y;
var r = ball.radius;
// Rectangle bounds
var left = wx - ww / 2;
var right = wx + ww / 2;
var top = wy - wh / 2;
var bottom = wy + wh / 2;
// Find closest point on wall to ball
var closestX = Math.max(left, Math.min(bx, right));
var closestY = Math.max(top, Math.min(by, bottom));
var dx = bx - closestX;
var dy = by - closestY;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < r) {
// Ball is colliding with wall
// Push ball out
var overlap = r - dist + 1;
if (dist === 0) {
// Prevent NaN
dx = 1;
dy = 0;
dist = 1;
}
// Move ball out of wall
ball.x += dx / dist * overlap;
ball.y += dy / dist * overlap;
// Calculate the normal vector
var nx = dx / dist;
var ny = dy / dist;
// Calculate dot product of velocity and normal
var dot = ball.vx * nx + ball.vy * ny;
// Reflect velocity using the normal (like center wall)
ball.vx = ball.vx - 2 * dot * nx;
ball.vy = ball.vy - 2 * dot * ny;
// Dampen speed
ball.vx *= 0.7;
ball.vy *= 0.7;
}
}
}
// Helper: ball-in-hole detection
function checkHole() {
var dx = ball.x - hole.x;
var dy = ball.y - hole.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < hole.radius - ball.radius / 2) {
// Ball in hole! Instantly hide ball and the hole
ball.moving = false;
ball.vx = 0;
ball.vy = 0;
ball.visible = false;
hole.visible = false;
return true;
}
return false;
}
// Helper: reset ball after win (not needed, handled by LK.showYouWin)
// Game move handler (for drag aiming)
function handleMove(x, y, obj) {
if (isDragging && !ball.moving) {
// Clamp drag to max length
var dx = x - dragStartX;
var dy = y - dragStartY;
var maxDrag = 400;
var dragLen = Math.sqrt(dx * dx + dy * dy);
if (dragLen > maxDrag) {
var scale = maxDrag / dragLen;
dx *= scale;
dy *= scale;
}
// Show aim arrow
ball.showArrow(-dx, -dy);
}
}
// Game down handler (start drag)
game.down = function (x, y, obj) {
// Only allow aiming if ball exists, is not moving, and touch is near ball
if (typeof ball !== "undefined" && ball !== null && !ball.moving) {
var dx = x - ball.x;
var dy = y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < ball.radius + 40) {
isDragging = true;
dragStartX = ball.x;
dragStartY = ball.y;
ball.aimStartX = x;
ball.aimStartY = y;
ball.showArrow(0, 0);
}
}
};
// Game move handler
game.move = function (x, y, obj) {
if (isDragging && !ball.moving) {
// Update aim arrow
var dx = x - dragStartX;
var dy = y - dragStartY;
var maxDrag = 400;
var dragLen = Math.sqrt(dx * dx + dy * dy);
if (dragLen > maxDrag) {
var scale = maxDrag / dragLen;
dx *= scale;
dy *= scale;
}
ball.showArrow(-dx, -dy);
}
};
// Game up handler (release drag, shoot)
game.up = function (x, y, obj) {
if (isDragging && !ball.moving) {
var dx = x - dragStartX;
var dy = y - dragStartY;
// Power proportional to drag length
var power = Math.sqrt(dx * dx + dy * dy);
var maxPower = 38;
var minPower = 10;
var shotPower = Math.min(power / 10, maxPower);
if (shotPower < minPower / 2) {
// Too short, ignore
ball.hideArrow();
isDragging = false;
return;
}
// Set velocity (opposite direction of drag)
var angle = Math.atan2(dy, dx);
ball.vx = -Math.cos(angle) * shotPower;
ball.vy = -Math.sin(angle) * shotPower;
ball.moving = true;
ball.hideArrow();
isDragging = false;
// Increment strokes
strokes += 1;
scoreTxt.setText('Strokes: ' + strokes);
}
};
// Main game update
game.update = function () {
// Ball physics
if (typeof ball !== "undefined" && ball !== null && typeof ball.update === "function") {
ball.update();
}
// Clamp ball inside course
clampBallPosition();
// Ball-wall collisions
handleWallCollisions();
// Ball-in-hole
if (typeof hole !== "undefined" && hole !== null && hole.visible && ball && ball.visible) {
var dx = ball.x - hole.x;
var dy = ball.y - hole.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < hole.radius + ball.radius / 2) {
// Ball touched the hole! Instantly hide the hole, but do not dequeue or hide the ball
if (hole.parent) {
hole.parent.removeChild(hole);
}
hole.visible = false;
// Increment score and update label
score += 1;
scoreLabel.setText('Score: ' + score);
// Add time increment if not sandbox
if (!sandboxMode) {
timerValue = Math.min(timerValue + timeIncrement, 99);
timerTxt.setText(timerValue);
} else {
timerTxt.setText('∞');
}
// Do not stop the ball when it touches the hole; let it keep moving
// Create a new hole at a random position (not too close to the ball or walls)
var newHole = new GolfHole();
var safeMargin = margin + 200;
var minDistFromBall = 400;
var placed = false;
for (var tries = 0; tries < 20 && !placed; tries++) {
var hx = Math.random() * (2048 - 2 * safeMargin) + safeMargin;
var hy = Math.random() * (2732 - 2 * safeMargin) + safeMargin;
var bdx = hx - ball.x;
var bdy = hy - ball.y;
var bdist = Math.sqrt(bdx * bdx + bdy * bdy);
if (bdist > minDistFromBall) {
newHole.x = hx;
newHole.y = hy;
placed = true;
}
}
if (!placed) {
// fallback: just put it somewhere safe
newHole.x = 2048 / 2;
newHole.y = 2732 / 2;
}
game.addChild(newHole);
hole = newHole;
// No timer reset here, just add time increment above
}
}
};