/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var AimArrow = Container.expand(function () { var self = Container.call(this); var arrowGraphics = self.attachAsset('arrow', { anchorX: 0.5, anchorY: 1 }); self.visible = false; self.updateArrow = function (startX, startY, endX, endY) { var deltaX = endX - startX; var deltaY = endY - startY; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); var angle = Math.atan2(deltaY, deltaX) + Math.PI / 2; self.x = startX; self.y = startY; self.rotation = angle; self.scaleY = Math.min(distance / 40, 5); self.visible = true; }; return self; }); var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.isMoving = false; self.windResistance = 0.98; self.update = function () { // Calculate ball scale based on Y position var fieldTop = 400; var fieldBottom = 2732; var minScale = 1.2; // Smallest scale at top var maxScale = 2.5; // Largest scale at bottom var fieldProgress = (self.y - fieldTop) / (fieldBottom - fieldTop); fieldProgress = Math.max(0, Math.min(1, fieldProgress)); var targetScale = minScale + (maxScale - minScale) * fieldProgress; // Apply smooth scaling with tween if (self.lastScale === undefined) { self.lastScale = targetScale; } if (Math.abs(targetScale - self.lastScale) > 0.05) { tween(self, { scaleX: targetScale, scaleY: targetScale }, { duration: 100 }); self.lastScale = targetScale; } if (self.isMoving) { // Apply wind effect with moderate influence self.velocityX += windX * 0.1; self.velocityY += windY * 0.1; // Apply movement self.x += self.velocityX; self.y += self.velocityY; // Add slow rotation based on movement speed var speed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY); ballGraphics.rotation += speed * 0.005; // Apply less friction for smoother movement self.velocityX *= 0.995; self.velocityY *= 0.995; // Stop if velocity is very low if (Math.abs(self.velocityX) < 0.3 && Math.abs(self.velocityY) < 0.3) { self.isMoving = false; self.velocityX = 0; self.velocityY = 0; checkRoundEnd(); } // Check boundaries and bounce off walls - trapezoidal field // Calculate field width at current Y position for trapezoid shape var fieldTop = 400; var fieldBottom = 2732; // Full screen height at bottom var fieldTopWidth = 1200; // Narrower at top var fieldBottomWidth = 2048; // Full width at bottom var fieldProgress = (self.y - fieldTop) / (fieldBottom - fieldTop); fieldProgress = Math.max(0, Math.min(1, fieldProgress)); var currentFieldWidth = fieldTopWidth + (fieldBottomWidth - fieldTopWidth) * fieldProgress; var leftBoundary = (2048 - currentFieldWidth) / 2; var rightBoundary = leftBoundary + currentFieldWidth; // Left wall bounce if (self.x < leftBoundary) { self.x = leftBoundary; self.velocityX = -self.velocityX * 0.8; // Bounce with some energy loss } // Right wall bounce if (self.x > rightBoundary) { self.x = rightBoundary; self.velocityX = -self.velocityX * 0.8; // Bounce with some energy loss } // Top wall - game over if (self.y < fieldTop) { LK.getSound('blocked').play(); self.isMoving = false; checkRoundEnd(); } // Banner collision - prevent ball from passing through banner area var bannerTop = 2612; // Banner y position (2672) minus half banner height (60) if (self.y > bannerTop) { self.y = bannerTop; self.velocityY = -self.velocityY * 0.8; // Bounce with some energy loss } // Bottom wall bounce if (self.y > fieldBottom) { self.y = fieldBottom; self.velocityY = -self.velocityY * 0.8; // Bounce with some energy loss } } }; self.shoot = function (targetX, targetY, power) { var deltaX = targetX - self.x; var deltaY = targetY - self.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance > 0) { self.velocityX = deltaX / distance * power * 1.5; self.velocityY = deltaY / distance * power * 1.5; self.isMoving = true; LK.getSound('kick').play(); } }; return self; }); var Defender = Container.expand(function () { var self = Container.call(this); var defenderGraphics = self.attachAsset('defender', { anchorX: 0.5, anchorY: 0.5 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Array of random celebration messages // Current implementation correctly uses LK.showGameOver() which handles all game over UX // The engine does not provide APIs to customize game over text or add custom links // Game over customization is handled automatically by LK.showGameOver() var celebrationMessages = ["Güzel şuttu. Takımını yönetmeye ne dersin? – volleytics", "Şutunu çektin, şimdi sahayı yönet! – volleytics", "Vaay, antrenman şimdi başlıyor. – volleytics", "Gol senin, yönetim bizim işimiz. – volleytics", "İyi oynuyorsun. Peki takımın geri kalanı? – volleytics", "Spor sahada başlar, volleytics'te tamamlanır.", "Şut çekmesi kolay, ya spor okulu yönetmek? – volleytics", "Topa hükmettin, takıma da hükmedebilirsin. – volleytics", "Skor sende, sistem bizde. – volleytics", "Sahada yıldızsın, yönetimde de olabilirsin. – volleytics", "Bir gol daha! Şimdi yoklama alalım. – volleytics", "Refleksin hızlı, yönetimin de öyle olsun. – volleytics", "Antrenmanlar başlasın, volleytics seni bekliyor!", "Oyunu kazandın, şimdi sistemi kur. – volleytics", "İyi şut! Şimdi antrenmanlarını düzenle. – volleytics"]; var ball; var defenders = []; var goal; var aimArrow; var isDragging = false; var dragStartX = 0; var dragStartY = 0; var windX = 0; var windY = 0; var windIndicator; var roundNumber = 1; var gameState = 'menu'; // 'menu', 'aiming', 'shooting', 'roundEnd' var menuContainer; var startButton; // Create field - full width at bottom, narrowing toward top var field = game.addChild(LK.getAsset('field', { anchorX: 0, anchorY: 0, x: 0, y: 400, width: 2048, height: 2332 })); // Create field boundary lines for trapezoidal shape var fieldLines = []; // Left boundary line - angled using rotation var leftLine = game.addChild(LK.getAsset('fieldLine', { anchorX: 0.5, anchorY: 0, x: 424, // Left edge at top y: 400, width: 4, height: 2332 })); leftLine.rotation = 0.19; // Angle for trapezoid shape fieldLines.push(leftLine); // Right boundary line - angled using rotation var rightLine = game.addChild(LK.getAsset('fieldLine', { anchorX: 0.5, anchorY: 0, x: 1624, // Right edge at top y: 400, width: 4, height: 2332 })); rightLine.rotation = -0.19; // Angle for trapezoid shape fieldLines.push(rightLine); // Top boundary line var topLine = game.addChild(LK.getAsset('fieldLine', { anchorX: 0, anchorY: 0.5, x: 424, y: 400, width: 1200, height: 4 })); fieldLines.push(topLine); // Bottom boundary line var bottomLine = game.addChild(LK.getAsset('fieldLine', { anchorX: 0, anchorY: 0.5, x: 0, y: 2732, width: 2048, height: 4 })); fieldLines.push(bottomLine); // Create goal at top center - half extending above field goal = game.addChild(LK.getAsset('goal', { anchorX: 0.5, anchorY: 0, x: 1024, y: 300 })); // Create UI elements var scoreTxt = new Text2('Gol: 0', { size: 80, fill: 0xFFFFFF, font: "monospace" }); scoreTxt.anchor.set(0.5, 0); scoreTxt.visible = false; // Hide in menu initially LK.gui.top.addChild(scoreTxt); var windTxt = new Text2('Rüzgar Yönü: →', { size: 60, fill: 0x00FFFF, font: "monospace" }); windTxt.anchor.set(0, 0); windTxt.x = 100; windTxt.y = 100; windTxt.visible = false; // Hide in menu initially LK.gui.topLeft.addChild(windTxt); // Create aim arrow aimArrow = game.addChild(new AimArrow()); function spawnBall() { if (ball) { ball.destroy(); } ball = game.addChild(new Ball()); // Spawn ball in bottom half of trapezoidal field, but above banner ball.x = 400 + Math.random() * 1248; ball.y = 2000 + Math.random() * 612; // Reduced from 732 to 612 to stay above banner (2612) // Set initial scale based on spawn position var fieldTop = 400; var fieldBottom = 2732; var minScale = 1.2; var maxScale = 2.5; var fieldProgress = (ball.y - fieldTop) / (fieldBottom - fieldTop); fieldProgress = Math.max(0, Math.min(1, fieldProgress)); var initialScale = minScale + (maxScale - minScale) * fieldProgress; ball.scaleX = initialScale; ball.scaleY = initialScale; ball.lastScale = initialScale; } function spawnDefenders() { // Clear existing defenders for (var i = 0; i < defenders.length; i++) { defenders[i].destroy(); } defenders = []; // Spawn defenders based on round number var defenderCount = Math.min(2 + Math.floor(roundNumber / 2), 6); for (var i = 0; i < defenderCount; i++) { var defender = game.addChild(new Defender()); var validPosition = false; var attempts = 0; var maxAttempts = 100; // Increased attempts for better placement var minDistance = 150; // Increased minimum distance to prevent intersection // Keep trying to find a valid position away from ball and other defenders while (!validPosition && attempts < maxAttempts) { // Spawn defenders within trapezoidal field bounds var spawnY = 500 + Math.random() * 1900; var fieldProgress = (spawnY - 400) / 2332; var fieldWidthAtY = 1200 + 848 * fieldProgress; var leftBound = (2048 - fieldWidthAtY) / 2; var spawnX = leftBound + Math.random() * fieldWidthAtY; validPosition = true; // Assume valid until proven otherwise // Check distance from ball if (ball) { var distanceFromBall = Math.sqrt((spawnX - ball.x) * (spawnX - ball.x) + (spawnY - ball.y) * (spawnY - ball.y)); if (distanceFromBall < minDistance) { validPosition = false; } } // Check distance from other defenders for (var j = 0; j < defenders.length; j++) { var existingDefender = defenders[j]; var distanceFromDefender = Math.sqrt((spawnX - existingDefender.x) * (spawnX - existingDefender.x) + (spawnY - existingDefender.y) * (spawnY - existingDefender.y)); if (distanceFromDefender < 100) { // Minimum distance between defenders validPosition = false; break; } } if (validPosition) { defender.x = spawnX; defender.y = spawnY; } attempts++; } // If we couldn't find a valid position after max attempts, place in upper field area away from ball if (!validPosition) { var safeY = 500 + Math.random() * 800; // Upper portion of field var fieldProgress = (safeY - 400) / 2332; var fieldWidthAtY = 1200 + 848 * fieldProgress; var leftBound = (2048 - fieldWidthAtY) / 2; defender.x = leftBound + Math.random() * fieldWidthAtY; defender.y = safeY; } defenders.push(defender); } } function generateWind() { var windStrength = 0.4 + roundNumber * 0.05; var windAngle = Math.random() * Math.PI * 2; windX = Math.cos(windAngle) * windStrength; windY = Math.sin(windAngle) * windStrength; // Update wind indicator var windDirection = ''; if (Math.abs(windX) > Math.abs(windY)) { windDirection = windX > 0 ? '→' : '←'; } else { windDirection = windY > 0 ? '↓' : '↑'; } windTxt.setText('Rüzgar Yönü: ' + windDirection); } function checkCollisions() { if (!ball || !ball.isMoving) { return; } // Check defender collisions for (var i = 0; i < defenders.length; i++) { if (ball.intersects(defenders[i])) { ball.isMoving = false; LK.getSound('blocked').play(); LK.effects.flashScreen(0xff0000, 500); // Wait for blocked sound to finish before showing retry screen LK.setTimeout(function () { showRetryScreen(); }, 1000); return; } } // Check goal collision if (ball.intersects(goal) && ball.y < goal.y + 200) { ball.isMoving = false; LK.getSound('goal').play(); LK.effects.flashScreen(0x00ff00, 500); // Create animated celebration text only var randomMessage = celebrationMessages[Math.floor(Math.random() * celebrationMessages.length)]; // Split message into parts (main message and volleytics credit) var messageParts = randomMessage.split(' – volleytics'); var mainMessage = messageParts[0]; var hasVolleyticsCredit = messageParts.length > 1; // Split main message into two lines (roughly half the words per line) var words = mainMessage.split(' '); var midPoint = Math.ceil(words.length / 2); var line1 = words.slice(0, midPoint).join(' '); var line2 = words.slice(midPoint).join(' '); // Create container for all text elements var celebrationContainer = new Container(); celebrationContainer.x = -400; // Start off-screen left celebrationContainer.y = 1366; // Center of screen vertically game.addChild(celebrationContainer); // Create text shadow for line 1 var line1Shadow = new Text2(line1, { size: 126, fill: 0x000000, font: "monospace" }); line1Shadow.anchor.set(0.5, 0.5); line1Shadow.x = 2; line1Shadow.y = -42; celebrationContainer.addChild(line1Shadow); // Create line 1 text var line1Text = new Text2(line1, { size: 126, fill: 0xFFFFFF, font: "monospace" }); line1Text.anchor.set(0.5, 0.5); line1Text.x = 0; line1Text.y = -40; celebrationContainer.addChild(line1Text); // Create text shadow for line 2 var line2Shadow = new Text2(line2, { size: 120, fill: 0x000000, font: "monospace" }); line2Shadow.anchor.set(0.5, 0.5); line2Shadow.x = 2; line2Shadow.y = 62; celebrationContainer.addChild(line2Shadow); // Create line 2 text var line2Text = new Text2(line2, { size: 120, fill: 0xFFFFFF, font: "monospace" }); line2Text.anchor.set(0.5, 0.5); line2Text.x = 0; line2Text.y = 60; celebrationContainer.addChild(line2Text); // Create volleytics credit if present if (hasVolleyticsCredit) { // Create shadow for volleytics credit var creditShadow = new Text2('– volleytics', { size: 100, fill: 0x000000, font: "monospace" }); creditShadow.anchor.set(1.0, 0.5); creditShadow.x = 402; creditShadow.y = 151; celebrationContainer.addChild(creditShadow); // Create volleytics credit text (right-aligned) var creditText = new Text2('– volleytics', { size: 100, fill: 0xb63147, font: "monospace" }); creditText.anchor.set(1.0, 0.5); creditText.x = 400; creditText.y = 150; celebrationContainer.addChild(creditText); } // Slide in from left to center tween(celebrationContainer, { x: 1024 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { // Wait a moment, then slide out to the right LK.setTimeout(function () { tween(celebrationContainer, { x: 2448 }, { duration: 800, easing: tween.easeIn, onFinish: function onFinish() { celebrationContainer.destroy(); } }); }, 1500); } }); // Increment goal count var currentText = scoreTxt.text || 'Gol: 0'; var goalCount = 1; if (currentText && currentText.indexOf(': ') !== -1) { goalCount = parseInt(currentText.split(': ')[1]) + 1; } scoreTxt.setText('Gol: ' + goalCount); // Check win condition if (goalCount >= 10) { LK.showYouWin(); } else { // Next round roundNumber++; // Delay starting new round to allow celebration message to complete LK.setTimeout(function () { startNewRound(); }, 3100); } } } function checkRoundEnd() { if (gameState === 'shooting' && ball && !ball.isMoving) { // Ball stopped without scoring LK.getSound('blocked').play(); LK.setTimeout(function () { showRetryScreen(); }, 1000); } } function startNewRound() { gameState = 'aiming'; spawnBall(); spawnDefenders(); generateWind(); windTxt.visible = true; // Show wind indicator when game starts scoreTxt.visible = true; // Show score when game starts aimArrow.visible = false; } // Event handlers game.down = function (x, y, obj) { if (gameState === 'menu' && startButton) { var buttonPos = game.toLocal(startButton.parent.toGlobal(startButton.position)); var distance = Math.sqrt((x - buttonPos.x) * (x - buttonPos.x) + (y - buttonPos.y) * (y - buttonPos.y)); if (distance < 200) { LK.getSound('kick').play(); gameState = 'aiming'; hideMenu(); startNewRound(); } } else if (gameState === 'aiming' && ball) { var ballPos = game.toLocal(ball.parent.toGlobal(ball.position)); var distance = Math.sqrt((x - ballPos.x) * (x - ballPos.x) + (y - ballPos.y) * (y - ballPos.y)); if (distance < 60) { isDragging = true; dragStartX = x; dragStartY = y; } } }; game.move = function (x, y, obj) { if (gameState === 'aiming' && isDragging && ball) { aimArrow.updateArrow(ball.x, ball.y, x, y); } }; game.up = function (x, y, obj) { if (gameState === 'aiming' && isDragging && ball) { var distance = Math.sqrt((x - ball.x) * (x - ball.x) + (y - ball.y) * (y - ball.y)); var power = Math.min(distance / 3, 20); ball.shoot(x, y, power); gameState = 'shooting'; aimArrow.visible = false; isDragging = false; } }; game.update = function () { checkCollisions(); }; // Create banner at bottom of screen var banner = new Container(); game.addChild(banner); // Position banner at very bottom center banner.x = 1024; banner.y = 2672; // Move to very bottom (2732 - 60 for half banner height) // Create banner background var bannerBg = LK.getAsset('fieldLine', { anchorX: 0.5, anchorY: 0.5, width: 2048, height: 120 }); bannerBg.tint = 0x333333; banner.addChild(bannerBg); // Create main text var bannerText1 = new Text2('Gerçek Hayatta spora devam etmek için:', { size: 50, fill: 0xFFFFFF }); bannerText1.anchor.set(0.5, 0.5); bannerText1.y = -25; banner.addChild(bannerText1); // Create link text (no longer clickable) var bannerText2 = new Text2('volleytics.com', { size: 55, fill: 0xb63147 }); bannerText2.anchor.set(0.5, 0.5); bannerText2.y = 30; banner.addChild(bannerText2); function createMenu() { menuContainer = new Container(); game.addChild(menuContainer); // Menu background var menuBg = menuContainer.attachAsset('menuBackground', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); // Game title image in upper half of menu var titleImage = menuContainer.attachAsset('titleImage', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 800, width: 600, height: 600 }); // Start button startButton = menuContainer.attachAsset('startButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1500, width: 800, height: 400 }); // Title text shadow var titleShadow = new Text2('Volleytics ile sporun verimli olsun', { size: 120, fill: 0x000000, font: "Kanit" }); titleShadow.anchor.set(0.5, 0.5); titleShadow.x = 1027; titleShadow.y = 2403; menuContainer.addChild(titleShadow); // Title text var titleText = new Text2('Volleytics ile sporun verimli olsun', { size: 120, fill: 0xFFFFFF, font: "Kanit" }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 2400; menuContainer.addChild(titleText); // Link text shadow var linkShadow = new Text2('volleytics.com', { size: 90, fill: 0x000000, font: "Kanit" }); linkShadow.anchor.set(0.5, 0.5); linkShadow.x = 1027; linkShadow.y = 2553; menuContainer.addChild(linkShadow); // Link text var linkText = new Text2('volleytics.com', { size: 90, fill: 0xb63147, font: "Kanit" }); linkText.anchor.set(0.5, 0.5); linkText.x = 1024; linkText.y = 2550; menuContainer.addChild(linkText); } function showRetryScreen() { gameState = 'retry'; // Create retry screen container var retryContainer = new Container(); game.addChild(retryContainer); retryContainer.x = 1024; retryContainer.y = 1366; // Create retry text shadow var retryTextShadow = new Text2('TEKRAR DENE!', { size: 120, fill: 0x000000, font: "monospace" }); retryTextShadow.anchor.set(0.5, 0.5); retryTextShadow.x = 2; retryTextShadow.y = -198; retryContainer.addChild(retryTextShadow); // Create retry text var retryText = new Text2('TEKRAR DENE!', { size: 120, fill: 0xFFFFFF, font: "monospace" }); retryText.anchor.set(0.5, 0.5); retryText.x = 0; retryText.y = -200; retryContainer.addChild(retryText); // Create retry image var retryImage = retryContainer.attachAsset('retryImage', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 100, width: 300, height: 300 }); // Make retry screen clickable retryContainer.down = function (x, y, obj) { // Restart the game without going to menu retryContainer.destroy(); gameState = 'aiming'; roundNumber = 1; // Reset score scoreTxt.setText('Gol: 0'); startNewRound(); }; } function hideMenu() { if (menuContainer) { menuContainer.destroy(); menuContainer = null; startButton = null; } scoreTxt.visible = true; // Show score when leaving menu } // Initialize menu createMenu();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AimArrow = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1
});
self.visible = false;
self.updateArrow = function (startX, startY, endX, endY) {
var deltaX = endX - startX;
var deltaY = endY - startY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var angle = Math.atan2(deltaY, deltaX) + Math.PI / 2;
self.x = startX;
self.y = startY;
self.rotation = angle;
self.scaleY = Math.min(distance / 40, 5);
self.visible = true;
};
return self;
});
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.isMoving = false;
self.windResistance = 0.98;
self.update = function () {
// Calculate ball scale based on Y position
var fieldTop = 400;
var fieldBottom = 2732;
var minScale = 1.2; // Smallest scale at top
var maxScale = 2.5; // Largest scale at bottom
var fieldProgress = (self.y - fieldTop) / (fieldBottom - fieldTop);
fieldProgress = Math.max(0, Math.min(1, fieldProgress));
var targetScale = minScale + (maxScale - minScale) * fieldProgress;
// Apply smooth scaling with tween
if (self.lastScale === undefined) {
self.lastScale = targetScale;
}
if (Math.abs(targetScale - self.lastScale) > 0.05) {
tween(self, {
scaleX: targetScale,
scaleY: targetScale
}, {
duration: 100
});
self.lastScale = targetScale;
}
if (self.isMoving) {
// Apply wind effect with moderate influence
self.velocityX += windX * 0.1;
self.velocityY += windY * 0.1;
// Apply movement
self.x += self.velocityX;
self.y += self.velocityY;
// Add slow rotation based on movement speed
var speed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY);
ballGraphics.rotation += speed * 0.005;
// Apply less friction for smoother movement
self.velocityX *= 0.995;
self.velocityY *= 0.995;
// Stop if velocity is very low
if (Math.abs(self.velocityX) < 0.3 && Math.abs(self.velocityY) < 0.3) {
self.isMoving = false;
self.velocityX = 0;
self.velocityY = 0;
checkRoundEnd();
}
// Check boundaries and bounce off walls - trapezoidal field
// Calculate field width at current Y position for trapezoid shape
var fieldTop = 400;
var fieldBottom = 2732; // Full screen height at bottom
var fieldTopWidth = 1200; // Narrower at top
var fieldBottomWidth = 2048; // Full width at bottom
var fieldProgress = (self.y - fieldTop) / (fieldBottom - fieldTop);
fieldProgress = Math.max(0, Math.min(1, fieldProgress));
var currentFieldWidth = fieldTopWidth + (fieldBottomWidth - fieldTopWidth) * fieldProgress;
var leftBoundary = (2048 - currentFieldWidth) / 2;
var rightBoundary = leftBoundary + currentFieldWidth;
// Left wall bounce
if (self.x < leftBoundary) {
self.x = leftBoundary;
self.velocityX = -self.velocityX * 0.8; // Bounce with some energy loss
}
// Right wall bounce
if (self.x > rightBoundary) {
self.x = rightBoundary;
self.velocityX = -self.velocityX * 0.8; // Bounce with some energy loss
}
// Top wall - game over
if (self.y < fieldTop) {
LK.getSound('blocked').play();
self.isMoving = false;
checkRoundEnd();
}
// Banner collision - prevent ball from passing through banner area
var bannerTop = 2612; // Banner y position (2672) minus half banner height (60)
if (self.y > bannerTop) {
self.y = bannerTop;
self.velocityY = -self.velocityY * 0.8; // Bounce with some energy loss
}
// Bottom wall bounce
if (self.y > fieldBottom) {
self.y = fieldBottom;
self.velocityY = -self.velocityY * 0.8; // Bounce with some energy loss
}
}
};
self.shoot = function (targetX, targetY, power) {
var deltaX = targetX - self.x;
var deltaY = targetY - self.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance > 0) {
self.velocityX = deltaX / distance * power * 1.5;
self.velocityY = deltaY / distance * power * 1.5;
self.isMoving = true;
LK.getSound('kick').play();
}
};
return self;
});
var Defender = Container.expand(function () {
var self = Container.call(this);
var defenderGraphics = self.attachAsset('defender', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Array of random celebration messages
// Current implementation correctly uses LK.showGameOver() which handles all game over UX
// The engine does not provide APIs to customize game over text or add custom links
// Game over customization is handled automatically by LK.showGameOver()
var celebrationMessages = ["Güzel şuttu. Takımını yönetmeye ne dersin? – volleytics", "Şutunu çektin, şimdi sahayı yönet! – volleytics", "Vaay, antrenman şimdi başlıyor. – volleytics", "Gol senin, yönetim bizim işimiz. – volleytics", "İyi oynuyorsun. Peki takımın geri kalanı? – volleytics", "Spor sahada başlar, volleytics'te tamamlanır.", "Şut çekmesi kolay, ya spor okulu yönetmek? – volleytics", "Topa hükmettin, takıma da hükmedebilirsin. – volleytics", "Skor sende, sistem bizde. – volleytics", "Sahada yıldızsın, yönetimde de olabilirsin. – volleytics", "Bir gol daha! Şimdi yoklama alalım. – volleytics", "Refleksin hızlı, yönetimin de öyle olsun. – volleytics", "Antrenmanlar başlasın, volleytics seni bekliyor!", "Oyunu kazandın, şimdi sistemi kur. – volleytics", "İyi şut! Şimdi antrenmanlarını düzenle. – volleytics"];
var ball;
var defenders = [];
var goal;
var aimArrow;
var isDragging = false;
var dragStartX = 0;
var dragStartY = 0;
var windX = 0;
var windY = 0;
var windIndicator;
var roundNumber = 1;
var gameState = 'menu'; // 'menu', 'aiming', 'shooting', 'roundEnd'
var menuContainer;
var startButton;
// Create field - full width at bottom, narrowing toward top
var field = game.addChild(LK.getAsset('field', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 400,
width: 2048,
height: 2332
}));
// Create field boundary lines for trapezoidal shape
var fieldLines = [];
// Left boundary line - angled using rotation
var leftLine = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0,
x: 424,
// Left edge at top
y: 400,
width: 4,
height: 2332
}));
leftLine.rotation = 0.19; // Angle for trapezoid shape
fieldLines.push(leftLine);
// Right boundary line - angled using rotation
var rightLine = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0,
x: 1624,
// Right edge at top
y: 400,
width: 4,
height: 2332
}));
rightLine.rotation = -0.19; // Angle for trapezoid shape
fieldLines.push(rightLine);
// Top boundary line
var topLine = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0,
anchorY: 0.5,
x: 424,
y: 400,
width: 1200,
height: 4
}));
fieldLines.push(topLine);
// Bottom boundary line
var bottomLine = game.addChild(LK.getAsset('fieldLine', {
anchorX: 0,
anchorY: 0.5,
x: 0,
y: 2732,
width: 2048,
height: 4
}));
fieldLines.push(bottomLine);
// Create goal at top center - half extending above field
goal = game.addChild(LK.getAsset('goal', {
anchorX: 0.5,
anchorY: 0,
x: 1024,
y: 300
}));
// Create UI elements
var scoreTxt = new Text2('Gol: 0', {
size: 80,
fill: 0xFFFFFF,
font: "monospace"
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.visible = false; // Hide in menu initially
LK.gui.top.addChild(scoreTxt);
var windTxt = new Text2('Rüzgar Yönü: →', {
size: 60,
fill: 0x00FFFF,
font: "monospace"
});
windTxt.anchor.set(0, 0);
windTxt.x = 100;
windTxt.y = 100;
windTxt.visible = false; // Hide in menu initially
LK.gui.topLeft.addChild(windTxt);
// Create aim arrow
aimArrow = game.addChild(new AimArrow());
function spawnBall() {
if (ball) {
ball.destroy();
}
ball = game.addChild(new Ball());
// Spawn ball in bottom half of trapezoidal field, but above banner
ball.x = 400 + Math.random() * 1248;
ball.y = 2000 + Math.random() * 612; // Reduced from 732 to 612 to stay above banner (2612)
// Set initial scale based on spawn position
var fieldTop = 400;
var fieldBottom = 2732;
var minScale = 1.2;
var maxScale = 2.5;
var fieldProgress = (ball.y - fieldTop) / (fieldBottom - fieldTop);
fieldProgress = Math.max(0, Math.min(1, fieldProgress));
var initialScale = minScale + (maxScale - minScale) * fieldProgress;
ball.scaleX = initialScale;
ball.scaleY = initialScale;
ball.lastScale = initialScale;
}
function spawnDefenders() {
// Clear existing defenders
for (var i = 0; i < defenders.length; i++) {
defenders[i].destroy();
}
defenders = [];
// Spawn defenders based on round number
var defenderCount = Math.min(2 + Math.floor(roundNumber / 2), 6);
for (var i = 0; i < defenderCount; i++) {
var defender = game.addChild(new Defender());
var validPosition = false;
var attempts = 0;
var maxAttempts = 100; // Increased attempts for better placement
var minDistance = 150; // Increased minimum distance to prevent intersection
// Keep trying to find a valid position away from ball and other defenders
while (!validPosition && attempts < maxAttempts) {
// Spawn defenders within trapezoidal field bounds
var spawnY = 500 + Math.random() * 1900;
var fieldProgress = (spawnY - 400) / 2332;
var fieldWidthAtY = 1200 + 848 * fieldProgress;
var leftBound = (2048 - fieldWidthAtY) / 2;
var spawnX = leftBound + Math.random() * fieldWidthAtY;
validPosition = true; // Assume valid until proven otherwise
// Check distance from ball
if (ball) {
var distanceFromBall = Math.sqrt((spawnX - ball.x) * (spawnX - ball.x) + (spawnY - ball.y) * (spawnY - ball.y));
if (distanceFromBall < minDistance) {
validPosition = false;
}
}
// Check distance from other defenders
for (var j = 0; j < defenders.length; j++) {
var existingDefender = defenders[j];
var distanceFromDefender = Math.sqrt((spawnX - existingDefender.x) * (spawnX - existingDefender.x) + (spawnY - existingDefender.y) * (spawnY - existingDefender.y));
if (distanceFromDefender < 100) {
// Minimum distance between defenders
validPosition = false;
break;
}
}
if (validPosition) {
defender.x = spawnX;
defender.y = spawnY;
}
attempts++;
}
// If we couldn't find a valid position after max attempts, place in upper field area away from ball
if (!validPosition) {
var safeY = 500 + Math.random() * 800; // Upper portion of field
var fieldProgress = (safeY - 400) / 2332;
var fieldWidthAtY = 1200 + 848 * fieldProgress;
var leftBound = (2048 - fieldWidthAtY) / 2;
defender.x = leftBound + Math.random() * fieldWidthAtY;
defender.y = safeY;
}
defenders.push(defender);
}
}
function generateWind() {
var windStrength = 0.4 + roundNumber * 0.05;
var windAngle = Math.random() * Math.PI * 2;
windX = Math.cos(windAngle) * windStrength;
windY = Math.sin(windAngle) * windStrength;
// Update wind indicator
var windDirection = '';
if (Math.abs(windX) > Math.abs(windY)) {
windDirection = windX > 0 ? '→' : '←';
} else {
windDirection = windY > 0 ? '↓' : '↑';
}
windTxt.setText('Rüzgar Yönü: ' + windDirection);
}
function checkCollisions() {
if (!ball || !ball.isMoving) {
return;
}
// Check defender collisions
for (var i = 0; i < defenders.length; i++) {
if (ball.intersects(defenders[i])) {
ball.isMoving = false;
LK.getSound('blocked').play();
LK.effects.flashScreen(0xff0000, 500);
// Wait for blocked sound to finish before showing retry screen
LK.setTimeout(function () {
showRetryScreen();
}, 1000);
return;
}
}
// Check goal collision
if (ball.intersects(goal) && ball.y < goal.y + 200) {
ball.isMoving = false;
LK.getSound('goal').play();
LK.effects.flashScreen(0x00ff00, 500);
// Create animated celebration text only
var randomMessage = celebrationMessages[Math.floor(Math.random() * celebrationMessages.length)];
// Split message into parts (main message and volleytics credit)
var messageParts = randomMessage.split(' – volleytics');
var mainMessage = messageParts[0];
var hasVolleyticsCredit = messageParts.length > 1;
// Split main message into two lines (roughly half the words per line)
var words = mainMessage.split(' ');
var midPoint = Math.ceil(words.length / 2);
var line1 = words.slice(0, midPoint).join(' ');
var line2 = words.slice(midPoint).join(' ');
// Create container for all text elements
var celebrationContainer = new Container();
celebrationContainer.x = -400; // Start off-screen left
celebrationContainer.y = 1366; // Center of screen vertically
game.addChild(celebrationContainer);
// Create text shadow for line 1
var line1Shadow = new Text2(line1, {
size: 126,
fill: 0x000000,
font: "monospace"
});
line1Shadow.anchor.set(0.5, 0.5);
line1Shadow.x = 2;
line1Shadow.y = -42;
celebrationContainer.addChild(line1Shadow);
// Create line 1 text
var line1Text = new Text2(line1, {
size: 126,
fill: 0xFFFFFF,
font: "monospace"
});
line1Text.anchor.set(0.5, 0.5);
line1Text.x = 0;
line1Text.y = -40;
celebrationContainer.addChild(line1Text);
// Create text shadow for line 2
var line2Shadow = new Text2(line2, {
size: 120,
fill: 0x000000,
font: "monospace"
});
line2Shadow.anchor.set(0.5, 0.5);
line2Shadow.x = 2;
line2Shadow.y = 62;
celebrationContainer.addChild(line2Shadow);
// Create line 2 text
var line2Text = new Text2(line2, {
size: 120,
fill: 0xFFFFFF,
font: "monospace"
});
line2Text.anchor.set(0.5, 0.5);
line2Text.x = 0;
line2Text.y = 60;
celebrationContainer.addChild(line2Text);
// Create volleytics credit if present
if (hasVolleyticsCredit) {
// Create shadow for volleytics credit
var creditShadow = new Text2('– volleytics', {
size: 100,
fill: 0x000000,
font: "monospace"
});
creditShadow.anchor.set(1.0, 0.5);
creditShadow.x = 402;
creditShadow.y = 151;
celebrationContainer.addChild(creditShadow);
// Create volleytics credit text (right-aligned)
var creditText = new Text2('– volleytics', {
size: 100,
fill: 0xb63147,
font: "monospace"
});
creditText.anchor.set(1.0, 0.5);
creditText.x = 400;
creditText.y = 150;
celebrationContainer.addChild(creditText);
}
// Slide in from left to center
tween(celebrationContainer, {
x: 1024
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Wait a moment, then slide out to the right
LK.setTimeout(function () {
tween(celebrationContainer, {
x: 2448
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
celebrationContainer.destroy();
}
});
}, 1500);
}
});
// Increment goal count
var currentText = scoreTxt.text || 'Gol: 0';
var goalCount = 1;
if (currentText && currentText.indexOf(': ') !== -1) {
goalCount = parseInt(currentText.split(': ')[1]) + 1;
}
scoreTxt.setText('Gol: ' + goalCount);
// Check win condition
if (goalCount >= 10) {
LK.showYouWin();
} else {
// Next round
roundNumber++;
// Delay starting new round to allow celebration message to complete
LK.setTimeout(function () {
startNewRound();
}, 3100);
}
}
}
function checkRoundEnd() {
if (gameState === 'shooting' && ball && !ball.isMoving) {
// Ball stopped without scoring
LK.getSound('blocked').play();
LK.setTimeout(function () {
showRetryScreen();
}, 1000);
}
}
function startNewRound() {
gameState = 'aiming';
spawnBall();
spawnDefenders();
generateWind();
windTxt.visible = true; // Show wind indicator when game starts
scoreTxt.visible = true; // Show score when game starts
aimArrow.visible = false;
}
// Event handlers
game.down = function (x, y, obj) {
if (gameState === 'menu' && startButton) {
var buttonPos = game.toLocal(startButton.parent.toGlobal(startButton.position));
var distance = Math.sqrt((x - buttonPos.x) * (x - buttonPos.x) + (y - buttonPos.y) * (y - buttonPos.y));
if (distance < 200) {
LK.getSound('kick').play();
gameState = 'aiming';
hideMenu();
startNewRound();
}
} else if (gameState === 'aiming' && ball) {
var ballPos = game.toLocal(ball.parent.toGlobal(ball.position));
var distance = Math.sqrt((x - ballPos.x) * (x - ballPos.x) + (y - ballPos.y) * (y - ballPos.y));
if (distance < 60) {
isDragging = true;
dragStartX = x;
dragStartY = y;
}
}
};
game.move = function (x, y, obj) {
if (gameState === 'aiming' && isDragging && ball) {
aimArrow.updateArrow(ball.x, ball.y, x, y);
}
};
game.up = function (x, y, obj) {
if (gameState === 'aiming' && isDragging && ball) {
var distance = Math.sqrt((x - ball.x) * (x - ball.x) + (y - ball.y) * (y - ball.y));
var power = Math.min(distance / 3, 20);
ball.shoot(x, y, power);
gameState = 'shooting';
aimArrow.visible = false;
isDragging = false;
}
};
game.update = function () {
checkCollisions();
};
// Create banner at bottom of screen
var banner = new Container();
game.addChild(banner);
// Position banner at very bottom center
banner.x = 1024;
banner.y = 2672; // Move to very bottom (2732 - 60 for half banner height)
// Create banner background
var bannerBg = LK.getAsset('fieldLine', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 120
});
bannerBg.tint = 0x333333;
banner.addChild(bannerBg);
// Create main text
var bannerText1 = new Text2('Gerçek Hayatta spora devam etmek için:', {
size: 50,
fill: 0xFFFFFF
});
bannerText1.anchor.set(0.5, 0.5);
bannerText1.y = -25;
banner.addChild(bannerText1);
// Create link text (no longer clickable)
var bannerText2 = new Text2('volleytics.com', {
size: 55,
fill: 0xb63147
});
bannerText2.anchor.set(0.5, 0.5);
bannerText2.y = 30;
banner.addChild(bannerText2);
function createMenu() {
menuContainer = new Container();
game.addChild(menuContainer);
// Menu background
var menuBg = menuContainer.attachAsset('menuBackground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
// Game title image in upper half of menu
var titleImage = menuContainer.attachAsset('titleImage', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 800,
width: 600,
height: 600
});
// Start button
startButton = menuContainer.attachAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1500,
width: 800,
height: 400
});
// Title text shadow
var titleShadow = new Text2('Volleytics ile sporun verimli olsun', {
size: 120,
fill: 0x000000,
font: "Kanit"
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 1027;
titleShadow.y = 2403;
menuContainer.addChild(titleShadow);
// Title text
var titleText = new Text2('Volleytics ile sporun verimli olsun', {
size: 120,
fill: 0xFFFFFF,
font: "Kanit"
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 2400;
menuContainer.addChild(titleText);
// Link text shadow
var linkShadow = new Text2('volleytics.com', {
size: 90,
fill: 0x000000,
font: "Kanit"
});
linkShadow.anchor.set(0.5, 0.5);
linkShadow.x = 1027;
linkShadow.y = 2553;
menuContainer.addChild(linkShadow);
// Link text
var linkText = new Text2('volleytics.com', {
size: 90,
fill: 0xb63147,
font: "Kanit"
});
linkText.anchor.set(0.5, 0.5);
linkText.x = 1024;
linkText.y = 2550;
menuContainer.addChild(linkText);
}
function showRetryScreen() {
gameState = 'retry';
// Create retry screen container
var retryContainer = new Container();
game.addChild(retryContainer);
retryContainer.x = 1024;
retryContainer.y = 1366;
// Create retry text shadow
var retryTextShadow = new Text2('TEKRAR DENE!', {
size: 120,
fill: 0x000000,
font: "monospace"
});
retryTextShadow.anchor.set(0.5, 0.5);
retryTextShadow.x = 2;
retryTextShadow.y = -198;
retryContainer.addChild(retryTextShadow);
// Create retry text
var retryText = new Text2('TEKRAR DENE!', {
size: 120,
fill: 0xFFFFFF,
font: "monospace"
});
retryText.anchor.set(0.5, 0.5);
retryText.x = 0;
retryText.y = -200;
retryContainer.addChild(retryText);
// Create retry image
var retryImage = retryContainer.attachAsset('retryImage', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 100,
width: 300,
height: 300
});
// Make retry screen clickable
retryContainer.down = function (x, y, obj) {
// Restart the game without going to menu
retryContainer.destroy();
gameState = 'aiming';
roundNumber = 1;
// Reset score
scoreTxt.setText('Gol: 0');
startNewRound();
};
}
function hideMenu() {
if (menuContainer) {
menuContainer.destroy();
menuContainer = null;
startButton = null;
}
scoreTxt.visible = true; // Show score when leaving menu
}
// Initialize menu
createMenu();
pixerlart soccer ball. In-Game asset. 2d. High contrast. No shadows
pixelart soccer goal. In-Game asset. 2d. High contrast. No shadows
pixelart grass texture low quality. have soccer field half center circle at bottom. In-Game asset. 2d. High contrast. No shadows
pixelart arrow up. In-Game asset. 2d. High contrast. No shadows
pixelart football shoot scene cinematic. In-Game asset. 2d. High contrast. No shadows
pixelart button. have text in middle "BAŞLA". In-Game asset. 2d. High contrast. No shadows
fancy pixelart title have shadow and white letters. text is "ŞUT ve GOL". In-Game asset. 2d. High contrast. No shadows
pixelart roling arrow button orange color. In-Game asset. 2d. High contrast. No shadows