/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Tween plugin import removed to fix plugin load error var BananaClickEffect = Container.expand(function () { var self = Container.call(this); var effect = self.attachAsset('bananaClickEffect', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, alpha: 1 }); self.init = function (x, y) { self.x = x; self.y = y; // Animate: scale up and fade out tween(effect, { scaleX: 1.2, scaleY: 1.2, alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var BananaEffect = Container.expand(function () { var self = Container.call(this); self.init = function (x, y) { // Create multiple banana particles for (var i = 0; i < 8; i++) { var particle = self.attachAsset('banana', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.2, scaleY: 0.2, alpha: 0.8 }); // Calculate angle for particle direction var angle = Math.PI * 2 * (i / 8); // Position particle particle.x = 0; particle.y = 0; // Calculate target position for the tween var targetX = Math.cos(angle) * 100; var targetY = Math.sin(angle) * 100; // Animate particle tween(particle, { x: targetX, y: targetY, alpha: 0, rotation: Math.random() * Math.PI * 2, scaleX: 0.05, scaleY: 0.05 }, { duration: 800 + Math.random() * 400, easing: tween.easeOut }); } // Set position of the effect self.x = x; self.y = y; // Auto-destroy after all animations complete LK.setTimeout(function () { self.destroy(); }, 1500); }; return self; }); var FallingItem = Container.expand(function () { var self = Container.call(this); self.type = "banana"; // default type self.speed = 2; // default speed self.lastY = 0; self.active = true; self.graphics = null; self.init = function (type, speed) { self.type = type || "banana"; self.speed = speed || 2; // Remove any existing graphics if (self.graphics) { self.removeChild(self.graphics); } // Create the appropriate graphics based on type if (self.type === "banana") { self.graphics = self.attachAsset('banana', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.type === "bomb") { self.graphics = self.attachAsset('bomb', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.type === "heart") { self.graphics = self.attachAsset('heartRestore', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.type === "rottenBanana") { self.graphics = self.attachAsset('rottenBanana', { anchorX: 0.5, anchorY: 0.5 }); } // Set initial position self.x = Math.random() * (2048 - 200) + 100; self.y = -100; self.lastY = self.y; // Interactive for tapping self.interactive = true; }; self.update = function () { if (!self.active) { return; } self.lastY = self.y; self.y += self.speed; }; self.down = function (x, y, obj) { if (!self.active) { return; } if (self.type === "banana") { // Score 1 point for each banana LK.setScore(LK.getScore() + 1); LK.getSound('pop').play(); // Create banana effect at tap position createBananaEffect(self.x, self.y); // Create banana click effect at tap position createBananaClickEffect(self.x, self.y); self.remove(false); } else if (self.type === "bomb") { // Lost a life by tapping bomb LK.getSound('explosion').play(); decreaseLife(); self.remove(); } else if (self.type === "heart") { // Gained a life by tapping heart LK.getSound('heartRestoreSound').play(); increaseLife(); self.remove(); } else if (self.type === "rottenBanana") { // Lost a life by tapping rotten banana LK.getSound('rottenBananaSound').play(); decreaseLife(); self.remove(); } }; self.remove = function (createSouls) { self.active = false; // Create soul effect based on item type, but only if createSouls is not false if (createSouls !== false) { if (self.type === "banana") { createSoulEffect(self.x, self.y, 3); } else if (self.type === "heart") { createSoulEffect(self.x, self.y, 5); } else if (self.type === "bomb" || self.type === "rottenBanana") { // No souls for negative items } } tween(self, { alpha: 0, scaleX: 0.3, scaleY: 0.3 }, { duration: 200, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var Menu = Container.expand(function () { var self = Container.call(this); // Menu background var bg = self.attachAsset('menuBg', { anchorX: 0.5, anchorY: 0.5, x: SCREEN_WIDTH / 2, y: SCREEN_HEIGHT / 2 }); // Play button var playBtn = self.attachAsset('playButton', { anchorX: 0.5, anchorY: 0.5, x: SCREEN_WIDTH / 2, y: SCREEN_HEIGHT / 2 - 100 }); // Settings button var settingsBtn = self.attachAsset('settingsButton', { anchorX: 0.5, anchorY: 0.5, x: SCREEN_WIDTH / 2, y: SCREEN_HEIGHT / 2 + 120 }); // Title text var titleText = new Text2('Banana Game', { size: 140, fill: 0xFFF200, stroke: 0x000000, strokeThickness: 10, bold: true }); titleText.anchor.set(0.5, 0.5); titleText.x = SCREEN_WIDTH / 2; titleText.y = SCREEN_HEIGHT / 2 - 350; self.addChild(titleText); // Play button text var playText = new Text2('Play', { size: 90, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 8, bold: true }); playText.anchor.set(0.5, 0.5); playText.x = SCREEN_WIDTH / 2; playText.y = SCREEN_HEIGHT / 2 - 100; self.addChild(playText); // Settings button text var settingsText = new Text2('Settings', { size: 70, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 6, bold: true }); settingsText.anchor.set(0.5, 0.5); settingsText.x = SCREEN_WIDTH / 2; settingsText.y = SCREEN_HEIGHT / 2 + 120; self.addChild(settingsText); // Assets button (top right) var assetsBtn = self.attachAsset('pastScoresMenuButton', { anchorX: 1, anchorY: 0, x: SCREEN_WIDTH - 50, y: 23.05 // moved down by 0.3cm (11.55px) }); var assetsText = new Text2('Past Scores', { size: 60, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 5, bold: true }); assetsText.anchor.set(1, 0.5); assetsText.x = SCREEN_WIDTH - 80; assetsText.y = 50 + 60; self.addChild(assetsText); assetsBtn.interactive = true; assetsBtn.down = function () { showScoreHistoryPopup(); }; // Button interactivity playBtn.interactive = true; playBtn.down = function () { if (self.parent) self.parent.removeChild(self); showGame(); }; settingsBtn.interactive = true; settingsBtn.down = function () { if (self.parent) self.parent.removeChild(self); showSettingsMenu(); }; return self; }); var SettingsMenu = Container.expand(function () { var self = Container.call(this); // Settings background var bg = self.attachAsset('menuBg', { anchorX: 0.5, anchorY: 0.5, x: SCREEN_WIDTH / 2, y: SCREEN_HEIGHT / 2 }); // Title var settingsTitle = new Text2('Settings', { size: 120, fill: 0xFFF200, stroke: 0x000000, strokeThickness: 8, bold: true }); settingsTitle.anchor.set(0.5, 0.5); settingsTitle.x = SCREEN_WIDTH / 2; settingsTitle.y = SCREEN_HEIGHT / 2 - 300; self.addChild(settingsTitle); // Back button var backBtn = self.attachAsset('menuButton', { anchorX: 0.5, anchorY: 0.5, x: SCREEN_WIDTH / 2, y: SCREEN_HEIGHT / 2 + 350 }); var backText = new Text2('Back', { size: 80, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 7, bold: true }); backText.anchor.set(0.5, 0.5); backText.x = SCREEN_WIDTH / 2; backText.y = SCREEN_HEIGHT / 2 + 350; self.addChild(backText); // Example settings text var infoText = new Text2('Sound: On\nMusic: On', { size: 70, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 5, bold: true }); infoText.anchor.set(0.5, 0.5); infoText.x = SCREEN_WIDTH / 2; infoText.y = SCREEN_HEIGHT / 2; self.addChild(infoText); backBtn.interactive = true; backBtn.down = function () { if (self.parent) self.parent.removeChild(self); showMenu(); }; return self; }); var Soul = Container.expand(function () { var self = Container.call(this); self.init = function (x, y) { // Create soul graphics var soulGraphics = self.attachAsset('heart', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, alpha: 0.7 }); // Set initial position self.x = x; self.y = y; // Randomize direction slightly self.targetX = Math.random() * 300 - 150 + x; self.targetY = y - 300 - Math.random() * 200; // Always float upward // Start the float animation tween(self, { x: self.targetX, y: self.targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 1500 + Math.random() * 1000, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // New effect asset // Menu and settings assets ; // Game constants var SCREEN_WIDTH = 2048; var SCREEN_HEIGHT = 2732; var MAX_LIVES = 3; var BOMB_THRESHOLD_SCORE = 15; var SPAWN_INTERVAL_INITIAL = 60; // frames var MIN_SPAWN_INTERVAL = 20; // frames var INTERVAL_DECREMENT = 1; // Game variables var fallingItems = []; var lives = MAX_LIVES; var spawnCounter = 0; var currentSpawnInterval = SPAWN_INTERVAL_INITIAL; var currentSpeed = 2; var isGameOver = false; var extraHeartSpawned = false; var extraHeartCaught = false; var gameStarted = false; // Banana combo system variables (REMOVED) // var bananaCombo = 0; // var bananaComboTimer = null; // var BANANA_COMBO_TIMEOUT = 1500; // ms // var maxBananaCombo = 0; // Combo text UI (REMOVED) // var comboText = new Text2('', { // size: 120, // fill: 0xA259FF, // // purple // stroke: 0x000000, // strokeThickness: 8, // bold: true // }); // comboText.anchor.set(0.5, 0); // comboText.x = SCREEN_WIDTH / 2; // comboText.y = 200; // comboText.visible = false; // game.addChild(comboText); // Add background var background = game.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 0 })); // Menu logic var menuInstance = null; var settingsMenuInstance = null; // Global variable to store score history (in-memory, not persistent) var scoreHistory = []; // Show popup with past scores function showScoreHistoryPopup() { // Remove existing popup if present if (game.scoreHistoryPopup && game.scoreHistoryPopup.parent) { game.removeChild(game.scoreHistoryPopup); game.scoreHistoryPopup = null; } var popup = new Container(); // Popup background var bg = popup.attachAsset('menuBg', { anchorX: 0.5, anchorY: 0.5, x: SCREEN_WIDTH / 2, y: SCREEN_HEIGHT / 2, scaleX: 0.7, scaleY: 0.7 }); // Title var title = new Text2('Past Scores', { size: 90, fill: 0xFFF200, stroke: 0x000000, strokeThickness: 7, bold: true }); title.anchor.set(0.5, 0.5); title.x = SCREEN_WIDTH / 2; title.y = SCREEN_HEIGHT / 2 - 350; popup.addChild(title); // Get score history from global variable (show up to 10 most recent) var history = scoreHistory || []; var displayScores = history.slice(-10).reverse(); var scoresText = displayScores.length > 0 ? displayScores.map(function (s, i) { return i + 1 + '. ' + s; }).join('\n') : 'No scores yet!'; var scoresList = new Text2(scoresText, { size: 70, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 5, bold: true }); scoresList.anchor.set(0.5, 0); scoresList.x = SCREEN_WIDTH / 2; scoresList.y = SCREEN_HEIGHT / 2 - 220; popup.addChild(scoresList); // Close button var closeBtn = popup.attachAsset('menuButton', { anchorX: 0.5, anchorY: 0.5, x: SCREEN_WIDTH / 2, y: SCREEN_HEIGHT / 2 + 350, scaleX: 0.7, scaleY: 0.7 }); var closeText = new Text2('Close', { size: 70, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 6, bold: true }); closeText.anchor.set(0.5, 0.5); closeText.x = SCREEN_WIDTH / 2; closeText.y = SCREEN_HEIGHT / 2 + 350; popup.addChild(closeText); closeBtn.interactive = true; closeBtn.down = function () { if (popup.parent) game.removeChild(popup); game.scoreHistoryPopup = null; }; game.addChild(popup); game.scoreHistoryPopup = popup; } // Show menu function showMenu() { if (menuInstance) { if (!menuInstance.parent) game.addChild(menuInstance); return; } menuInstance = new Menu(); game.addChild(menuInstance); } // Show settings menu function showSettingsMenu() { if (settingsMenuInstance) { if (!settingsMenuInstance.parent) game.addChild(settingsMenuInstance); return; } settingsMenuInstance = new SettingsMenu(); game.addChild(settingsMenuInstance); } // Hide menu and start game function showGame() { gameStarted = true; clickToPlayText.visible = false; initGame(); } // Show menu on game start LK.setTimeout(function () { showMenu(); }, 10); // Initialize score text var scoreText = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 8, bold: true }); scoreText.anchor.set(0.5, 0); scoreText.x = SCREEN_WIDTH / 2; scoreText.y = 50; game.addChild(scoreText); // Create click to play text (hidden, menu will be used instead) var clickToPlayText = new Text2('Click to Start', { size: 120, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 8, bold: true }); clickToPlayText.anchor.set(0.5, 0.5); clickToPlayText.x = SCREEN_WIDTH / 2; clickToPlayText.y = SCREEN_HEIGHT / 2; clickToPlayText.visible = false; game.addChild(clickToPlayText); // Create hearts container var heartsContainer = new Container(); heartsContainer.x = 1410; heartsContainer.y = 50; game.addChild(heartsContainer); // Initialize hearts display function initLives() { // Clear existing hearts while (heartsContainer.children.length > 0) { heartsContainer.removeChildAt(0); } // Add hearts based on current lives for (var i = 0; i < lives; i++) { var heart = LK.getAsset('heart', { anchorX: 0, anchorY: 0, x: i * 190, y: 0 }); heartsContainer.addChild(heart); } } // Function to create floating soul effect function createSoulEffect(x, y, count) { count = count || 1; for (var i = 0; i < count; i++) { var soul = new Soul(); soul.init(x, y); game.addChild(soul); } } // Function to create banana effect when banana is clicked function createBananaEffect(x, y) { var effect = new BananaEffect(); effect.init(x, y); game.addChild(effect); } // Function to create banana click effect animation function createBananaClickEffect(x, y) { var effect = new BananaClickEffect(); effect.init(x, y); game.addChild(effect); } // Function to check if a heart is already falling function isHeartAlreadyFalling() { for (var i = 0; i < fallingItems.length; i++) { if (fallingItems[i] && fallingItems[i].type === "heart" && fallingItems[i].active) { return true; } } return false; } // Function to increase life function increaseLife() { if (lives < MAX_LIVES) { lives++; initLives(); extraHeartCaught = true; // Mark that a heart was caught } } // Function to decrease life function decreaseLife() { // Create soul effect at heart position that was lost var heartIndex = lives - 1; if (heartIndex >= 0 && heartsContainer.children[heartIndex]) { var heartPos = heartsContainer.children[heartIndex]; var globalPos = heartsContainer.toGlobal({ x: heartPos.x + 50, y: heartPos.y + 50 }); var gamePos = game.toLocal(globalPos); createSoulEffect(gamePos.x, gamePos.y, 7); } lives--; initLives(); // We only play the heart loss sound for actual life losses, not from bombs or rotten bananas // This is handled in the game.update and FallingItem.down methods where decreaseLife is called. // LK.getSound('heartLossSound').play(); // Removed sound here // Shake the screen using the tween library tween(game, { x: game.x + 5 }, { duration: 50, easing: tween.easeOut, onFinish: function onFinish() { tween(game, { x: game.x - 10 }, { duration: 100, easing: tween.easeInOut, onFinish: function onFinish() { tween(game, { x: game.x + 5 }, { duration: 50, easing: tween.easeIn, onFinish: function onFinish() { // Check for game over after shake is complete if (lives <= 0) { gameOver(); } } }); } }); } }); } // Function to handle game over function gameOver() { if (isGameOver) { return; } isGameOver = true; gameStarted = false; // Reset banana combo system (REMOVED) // bananaCombo = 0; // comboText.visible = false; // if (bananaComboTimer) { // LK.clearTimeout(bananaComboTimer); // bananaComboTimer = null; // } LK.getSound('gameover').play(); LK.effects.flashScreen(0xff0000, 1000); // [Bu kısım kaldırıldı] -> createSoulEffect ile kalp efektleri artık çıkmayacak // Remove all falling items for (var i = fallingItems.length - 1; i >= 0; i--) { fallingItems[i].destroy(); } fallingItems = []; // Save score to global score history (not persistent) var score = LK.getScore(); if (typeof score === "number" && score > 0) { scoreHistory = scoreHistory || []; scoreHistory.push(score); // Keep only last 20 scores if (scoreHistory.length > 20) { scoreHistory = scoreHistory.slice(scoreHistory.length - 20); } } // Show game over screen LK.setTimeout(function () { LK.showGameOver(); }, 1000); // Show click to play text again clickToPlayText.visible = true; } function spawnItem() { var score = LK.getScore(); var isBomb = false; var isRottenBanana = false; var isHeart = false; // Heart spawn logic if (!isHeartAlreadyFalling()) { if (score >= 50 && lives === 1 && !extraHeartSpawned) { isHeart = true; extraHeartSpawned = true; } else if (score >= 70 && lives === 1 && extraHeartSpawned && !extraHeartCaught) { isHeart = true; } } // Rotten banana and bomb logic (independent, so both can spawn) if (!isHeart) { if (score >= 20 && Math.random() < 0.3) { isRottenBanana = true; } else if (score >= 10 && Math.random() < 0.3) { isBomb = true; } } var itemType = "banana"; if (isHeart) { itemType = "heart"; } else if (isRottenBanana) { itemType = "rottenBanana"; } else if (isBomb) { itemType = "bomb"; } var item = new FallingItem(); if (itemType === "heart" && score > 100 && lives === 1) { item.init(itemType, 16); } else if (itemType === "heart" && score >= 50 && lives === 1) { item.init(itemType, 10); } else { item.init(itemType, currentSpeed); } fallingItems.push(item); game.addChild(item); return item; } // Update game state each frame game.update = function () { if (!gameStarted || isGameOver) { return; } // Update all falling items for (var i = fallingItems.length - 1; i >= 0; i--) { var item = fallingItems[i]; // Check if item has fallen off screen if (item && item.lastY !== undefined && item.active && item.lastY < SCREEN_HEIGHT && item.y >= SCREEN_HEIGHT) { if (item.type === "banana" && item.active) { // Missed a banana - lose a life decreaseLife(); LK.getSound('heartLossSound').play(); // Play sound for missed banana item.remove(); } else if (item.type === "bomb" && item.active || item.type === "rottenBanana" && item.active) { // Bomb or rotten banana fell off screen - remove it with no penalty item.destroy(); fallingItems.splice(i, 1); } } // Remove destroyed items from array if (!item || !item.active || item.alpha <= 0) { fallingItems.splice(i, 1); } } // Spawn new items at intervals spawnCounter++; // Get current score for difficulty calculations var score = LK.getScore(); // Hearts will be spawned based on specific conditions in spawnItem function if (spawnCounter >= currentSpawnInterval) { spawnItem(); spawnCounter = 0; // Set speed multiplier based on score thresholds var speedMultiplier = 1; // 0-150 arası sabit artışlar (orijinal hız tablosu) if (score >= 150) { speedMultiplier = 21; } else if (score >= 140) { speedMultiplier = 20.5; } else if (score >= 130) { speedMultiplier = 20; } else if (score >= 120) { speedMultiplier = 19.5; } else if (score >= 110) { speedMultiplier = 19; } else if (score >= 100) { speedMultiplier = 18; } else if (score >= 90) { speedMultiplier = 17; } else if (score >= 80) { speedMultiplier = 16; } else if (score >= 70) { speedMultiplier = 15; } else if (score >= 60) { speedMultiplier = 14; } else if (score >= 50) { speedMultiplier = 12.5; } else if (score >= 40) { speedMultiplier = 12; } else if (score >= 30) { speedMultiplier = 11.5; } else if (score >= 20) { speedMultiplier = 11; } else if (score >= 10) { speedMultiplier = 10; } else if (score >= 0) { speedMultiplier = 9; } else { speedMultiplier = 7; } // 150’den sonra 300000’e kadar dinamik artış: +0.25 her 1 skor artışı için if (score > 150 && score <= 300000) { speedMultiplier = 15 + (score - 150) * 0.5; } // Eğer istersen 300000’den sonrası için burada farklı artış mantığı eklenebilir. // Örnek olarak, 300000’den sonra her 1000 puanda 0.1 artış veya başka bir şey gibi. // Güncellenen hız, oyun içindeki hız değişkenine atanıyor currentSpeed = 2 * speedMultiplier; // Spawn interval (yeniden item çıkma aralığı) puana göre azalıyor ama minimum sınırı var currentSpawnInterval = Math.max(MIN_SPAWN_INTERVAL, SPAWN_INTERVAL_INITIAL - score * INTERVAL_DECREMENT); } // Score text’i güncelle scoreText.setText('Score: ' + score); }; // Initialize the game function initGame() { // Reset game variables lives = MAX_LIVES; LK.setScore(0); spawnCounter = 0; currentSpawnInterval = SPAWN_INTERVAL_INITIAL; currentSpeed = 2; isGameOver = false; extraHeartSpawned = false; // Reset heart spawn tracking extraHeartCaught = false; // Reset heart catch tracking fallingItems = []; // Reset banana combo system (REMOVED) // bananaCombo = 0; // maxBananaCombo = 0; // comboText.visible = false; // if (bananaComboTimer) { // LK.clearTimeout(bananaComboTimer); // bananaComboTimer = null; // } // Initialize UI initLives(); scoreText.setText('Score: 0'); // Play background music LK.playMusic('gameMusic'); } // Set up click handler to start game // (Removed, menu handles game start) // Initialize but don't start the game yet lives = MAX_LIVES; initLives();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Tween plugin import removed to fix plugin load error
var BananaClickEffect = Container.expand(function () {
var self = Container.call(this);
var effect = self.attachAsset('bananaClickEffect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 1
});
self.init = function (x, y) {
self.x = x;
self.y = y;
// Animate: scale up and fade out
tween(effect, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var BananaEffect = Container.expand(function () {
var self = Container.call(this);
self.init = function (x, y) {
// Create multiple banana particles
for (var i = 0; i < 8; i++) {
var particle = self.attachAsset('banana', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
alpha: 0.8
});
// Calculate angle for particle direction
var angle = Math.PI * 2 * (i / 8);
// Position particle
particle.x = 0;
particle.y = 0;
// Calculate target position for the tween
var targetX = Math.cos(angle) * 100;
var targetY = Math.sin(angle) * 100;
// Animate particle
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
rotation: Math.random() * Math.PI * 2,
scaleX: 0.05,
scaleY: 0.05
}, {
duration: 800 + Math.random() * 400,
easing: tween.easeOut
});
}
// Set position of the effect
self.x = x;
self.y = y;
// Auto-destroy after all animations complete
LK.setTimeout(function () {
self.destroy();
}, 1500);
};
return self;
});
var FallingItem = Container.expand(function () {
var self = Container.call(this);
self.type = "banana"; // default type
self.speed = 2; // default speed
self.lastY = 0;
self.active = true;
self.graphics = null;
self.init = function (type, speed) {
self.type = type || "banana";
self.speed = speed || 2;
// Remove any existing graphics
if (self.graphics) {
self.removeChild(self.graphics);
}
// Create the appropriate graphics based on type
if (self.type === "banana") {
self.graphics = self.attachAsset('banana', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.type === "bomb") {
self.graphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.type === "heart") {
self.graphics = self.attachAsset('heartRestore', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.type === "rottenBanana") {
self.graphics = self.attachAsset('rottenBanana', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Set initial position
self.x = Math.random() * (2048 - 200) + 100;
self.y = -100;
self.lastY = self.y;
// Interactive for tapping
self.interactive = true;
};
self.update = function () {
if (!self.active) {
return;
}
self.lastY = self.y;
self.y += self.speed;
};
self.down = function (x, y, obj) {
if (!self.active) {
return;
}
if (self.type === "banana") {
// Score 1 point for each banana
LK.setScore(LK.getScore() + 1);
LK.getSound('pop').play();
// Create banana effect at tap position
createBananaEffect(self.x, self.y);
// Create banana click effect at tap position
createBananaClickEffect(self.x, self.y);
self.remove(false);
} else if (self.type === "bomb") {
// Lost a life by tapping bomb
LK.getSound('explosion').play();
decreaseLife();
self.remove();
} else if (self.type === "heart") {
// Gained a life by tapping heart
LK.getSound('heartRestoreSound').play();
increaseLife();
self.remove();
} else if (self.type === "rottenBanana") {
// Lost a life by tapping rotten banana
LK.getSound('rottenBananaSound').play();
decreaseLife();
self.remove();
}
};
self.remove = function (createSouls) {
self.active = false;
// Create soul effect based on item type, but only if createSouls is not false
if (createSouls !== false) {
if (self.type === "banana") {
createSoulEffect(self.x, self.y, 3);
} else if (self.type === "heart") {
createSoulEffect(self.x, self.y, 5);
} else if (self.type === "bomb" || self.type === "rottenBanana") {
// No souls for negative items
}
}
tween(self, {
alpha: 0,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 200,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var Menu = Container.expand(function () {
var self = Container.call(this);
// Menu background
var bg = self.attachAsset('menuBg', {
anchorX: 0.5,
anchorY: 0.5,
x: SCREEN_WIDTH / 2,
y: SCREEN_HEIGHT / 2
});
// Play button
var playBtn = self.attachAsset('playButton', {
anchorX: 0.5,
anchorY: 0.5,
x: SCREEN_WIDTH / 2,
y: SCREEN_HEIGHT / 2 - 100
});
// Settings button
var settingsBtn = self.attachAsset('settingsButton', {
anchorX: 0.5,
anchorY: 0.5,
x: SCREEN_WIDTH / 2,
y: SCREEN_HEIGHT / 2 + 120
});
// Title text
var titleText = new Text2('Banana Game', {
size: 140,
fill: 0xFFF200,
stroke: 0x000000,
strokeThickness: 10,
bold: true
});
titleText.anchor.set(0.5, 0.5);
titleText.x = SCREEN_WIDTH / 2;
titleText.y = SCREEN_HEIGHT / 2 - 350;
self.addChild(titleText);
// Play button text
var playText = new Text2('Play', {
size: 90,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 8,
bold: true
});
playText.anchor.set(0.5, 0.5);
playText.x = SCREEN_WIDTH / 2;
playText.y = SCREEN_HEIGHT / 2 - 100;
self.addChild(playText);
// Settings button text
var settingsText = new Text2('Settings', {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 6,
bold: true
});
settingsText.anchor.set(0.5, 0.5);
settingsText.x = SCREEN_WIDTH / 2;
settingsText.y = SCREEN_HEIGHT / 2 + 120;
self.addChild(settingsText);
// Assets button (top right)
var assetsBtn = self.attachAsset('pastScoresMenuButton', {
anchorX: 1,
anchorY: 0,
x: SCREEN_WIDTH - 50,
y: 23.05 // moved down by 0.3cm (11.55px)
});
var assetsText = new Text2('Past Scores', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5,
bold: true
});
assetsText.anchor.set(1, 0.5);
assetsText.x = SCREEN_WIDTH - 80;
assetsText.y = 50 + 60;
self.addChild(assetsText);
assetsBtn.interactive = true;
assetsBtn.down = function () {
showScoreHistoryPopup();
};
// Button interactivity
playBtn.interactive = true;
playBtn.down = function () {
if (self.parent) self.parent.removeChild(self);
showGame();
};
settingsBtn.interactive = true;
settingsBtn.down = function () {
if (self.parent) self.parent.removeChild(self);
showSettingsMenu();
};
return self;
});
var SettingsMenu = Container.expand(function () {
var self = Container.call(this);
// Settings background
var bg = self.attachAsset('menuBg', {
anchorX: 0.5,
anchorY: 0.5,
x: SCREEN_WIDTH / 2,
y: SCREEN_HEIGHT / 2
});
// Title
var settingsTitle = new Text2('Settings', {
size: 120,
fill: 0xFFF200,
stroke: 0x000000,
strokeThickness: 8,
bold: true
});
settingsTitle.anchor.set(0.5, 0.5);
settingsTitle.x = SCREEN_WIDTH / 2;
settingsTitle.y = SCREEN_HEIGHT / 2 - 300;
self.addChild(settingsTitle);
// Back button
var backBtn = self.attachAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
x: SCREEN_WIDTH / 2,
y: SCREEN_HEIGHT / 2 + 350
});
var backText = new Text2('Back', {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 7,
bold: true
});
backText.anchor.set(0.5, 0.5);
backText.x = SCREEN_WIDTH / 2;
backText.y = SCREEN_HEIGHT / 2 + 350;
self.addChild(backText);
// Example settings text
var infoText = new Text2('Sound: On\nMusic: On', {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5,
bold: true
});
infoText.anchor.set(0.5, 0.5);
infoText.x = SCREEN_WIDTH / 2;
infoText.y = SCREEN_HEIGHT / 2;
self.addChild(infoText);
backBtn.interactive = true;
backBtn.down = function () {
if (self.parent) self.parent.removeChild(self);
showMenu();
};
return self;
});
var Soul = Container.expand(function () {
var self = Container.call(this);
self.init = function (x, y) {
// Create soul graphics
var soulGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.7
});
// Set initial position
self.x = x;
self.y = y;
// Randomize direction slightly
self.targetX = Math.random() * 300 - 150 + x;
self.targetY = y - 300 - Math.random() * 200; // Always float upward
// Start the float animation
tween(self, {
x: self.targetX,
y: self.targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 1500 + Math.random() * 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// New effect asset
// Menu and settings assets
;
// Game constants
var SCREEN_WIDTH = 2048;
var SCREEN_HEIGHT = 2732;
var MAX_LIVES = 3;
var BOMB_THRESHOLD_SCORE = 15;
var SPAWN_INTERVAL_INITIAL = 60; // frames
var MIN_SPAWN_INTERVAL = 20; // frames
var INTERVAL_DECREMENT = 1;
// Game variables
var fallingItems = [];
var lives = MAX_LIVES;
var spawnCounter = 0;
var currentSpawnInterval = SPAWN_INTERVAL_INITIAL;
var currentSpeed = 2;
var isGameOver = false;
var extraHeartSpawned = false;
var extraHeartCaught = false;
var gameStarted = false;
// Banana combo system variables (REMOVED)
// var bananaCombo = 0;
// var bananaComboTimer = null;
// var BANANA_COMBO_TIMEOUT = 1500; // ms
// var maxBananaCombo = 0;
// Combo text UI (REMOVED)
// var comboText = new Text2('', {
// size: 120,
// fill: 0xA259FF,
// // purple
// stroke: 0x000000,
// strokeThickness: 8,
// bold: true
// });
// comboText.anchor.set(0.5, 0);
// comboText.x = SCREEN_WIDTH / 2;
// comboText.y = 200;
// comboText.visible = false;
// game.addChild(comboText);
// Add background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0
}));
// Menu logic
var menuInstance = null;
var settingsMenuInstance = null;
// Global variable to store score history (in-memory, not persistent)
var scoreHistory = [];
// Show popup with past scores
function showScoreHistoryPopup() {
// Remove existing popup if present
if (game.scoreHistoryPopup && game.scoreHistoryPopup.parent) {
game.removeChild(game.scoreHistoryPopup);
game.scoreHistoryPopup = null;
}
var popup = new Container();
// Popup background
var bg = popup.attachAsset('menuBg', {
anchorX: 0.5,
anchorY: 0.5,
x: SCREEN_WIDTH / 2,
y: SCREEN_HEIGHT / 2,
scaleX: 0.7,
scaleY: 0.7
});
// Title
var title = new Text2('Past Scores', {
size: 90,
fill: 0xFFF200,
stroke: 0x000000,
strokeThickness: 7,
bold: true
});
title.anchor.set(0.5, 0.5);
title.x = SCREEN_WIDTH / 2;
title.y = SCREEN_HEIGHT / 2 - 350;
popup.addChild(title);
// Get score history from global variable (show up to 10 most recent)
var history = scoreHistory || [];
var displayScores = history.slice(-10).reverse();
var scoresText = displayScores.length > 0 ? displayScores.map(function (s, i) {
return i + 1 + '. ' + s;
}).join('\n') : 'No scores yet!';
var scoresList = new Text2(scoresText, {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5,
bold: true
});
scoresList.anchor.set(0.5, 0);
scoresList.x = SCREEN_WIDTH / 2;
scoresList.y = SCREEN_HEIGHT / 2 - 220;
popup.addChild(scoresList);
// Close button
var closeBtn = popup.attachAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
x: SCREEN_WIDTH / 2,
y: SCREEN_HEIGHT / 2 + 350,
scaleX: 0.7,
scaleY: 0.7
});
var closeText = new Text2('Close', {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 6,
bold: true
});
closeText.anchor.set(0.5, 0.5);
closeText.x = SCREEN_WIDTH / 2;
closeText.y = SCREEN_HEIGHT / 2 + 350;
popup.addChild(closeText);
closeBtn.interactive = true;
closeBtn.down = function () {
if (popup.parent) game.removeChild(popup);
game.scoreHistoryPopup = null;
};
game.addChild(popup);
game.scoreHistoryPopup = popup;
}
// Show menu
function showMenu() {
if (menuInstance) {
if (!menuInstance.parent) game.addChild(menuInstance);
return;
}
menuInstance = new Menu();
game.addChild(menuInstance);
}
// Show settings menu
function showSettingsMenu() {
if (settingsMenuInstance) {
if (!settingsMenuInstance.parent) game.addChild(settingsMenuInstance);
return;
}
settingsMenuInstance = new SettingsMenu();
game.addChild(settingsMenuInstance);
}
// Hide menu and start game
function showGame() {
gameStarted = true;
clickToPlayText.visible = false;
initGame();
}
// Show menu on game start
LK.setTimeout(function () {
showMenu();
}, 10);
// Initialize score text
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 8,
bold: true
});
scoreText.anchor.set(0.5, 0);
scoreText.x = SCREEN_WIDTH / 2;
scoreText.y = 50;
game.addChild(scoreText);
// Create click to play text (hidden, menu will be used instead)
var clickToPlayText = new Text2('Click to Start', {
size: 120,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 8,
bold: true
});
clickToPlayText.anchor.set(0.5, 0.5);
clickToPlayText.x = SCREEN_WIDTH / 2;
clickToPlayText.y = SCREEN_HEIGHT / 2;
clickToPlayText.visible = false;
game.addChild(clickToPlayText);
// Create hearts container
var heartsContainer = new Container();
heartsContainer.x = 1410;
heartsContainer.y = 50;
game.addChild(heartsContainer);
// Initialize hearts display
function initLives() {
// Clear existing hearts
while (heartsContainer.children.length > 0) {
heartsContainer.removeChildAt(0);
}
// Add hearts based on current lives
for (var i = 0; i < lives; i++) {
var heart = LK.getAsset('heart', {
anchorX: 0,
anchorY: 0,
x: i * 190,
y: 0
});
heartsContainer.addChild(heart);
}
}
// Function to create floating soul effect
function createSoulEffect(x, y, count) {
count = count || 1;
for (var i = 0; i < count; i++) {
var soul = new Soul();
soul.init(x, y);
game.addChild(soul);
}
}
// Function to create banana effect when banana is clicked
function createBananaEffect(x, y) {
var effect = new BananaEffect();
effect.init(x, y);
game.addChild(effect);
}
// Function to create banana click effect animation
function createBananaClickEffect(x, y) {
var effect = new BananaClickEffect();
effect.init(x, y);
game.addChild(effect);
}
// Function to check if a heart is already falling
function isHeartAlreadyFalling() {
for (var i = 0; i < fallingItems.length; i++) {
if (fallingItems[i] && fallingItems[i].type === "heart" && fallingItems[i].active) {
return true;
}
}
return false;
}
// Function to increase life
function increaseLife() {
if (lives < MAX_LIVES) {
lives++;
initLives();
extraHeartCaught = true; // Mark that a heart was caught
}
}
// Function to decrease life
function decreaseLife() {
// Create soul effect at heart position that was lost
var heartIndex = lives - 1;
if (heartIndex >= 0 && heartsContainer.children[heartIndex]) {
var heartPos = heartsContainer.children[heartIndex];
var globalPos = heartsContainer.toGlobal({
x: heartPos.x + 50,
y: heartPos.y + 50
});
var gamePos = game.toLocal(globalPos);
createSoulEffect(gamePos.x, gamePos.y, 7);
}
lives--;
initLives();
// We only play the heart loss sound for actual life losses, not from bombs or rotten bananas
// This is handled in the game.update and FallingItem.down methods where decreaseLife is called.
// LK.getSound('heartLossSound').play(); // Removed sound here
// Shake the screen using the tween library
tween(game, {
x: game.x + 5
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
x: game.x - 10
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(game, {
x: game.x + 5
}, {
duration: 50,
easing: tween.easeIn,
onFinish: function onFinish() {
// Check for game over after shake is complete
if (lives <= 0) {
gameOver();
}
}
});
}
});
}
});
}
// Function to handle game over
function gameOver() {
if (isGameOver) {
return;
}
isGameOver = true;
gameStarted = false;
// Reset banana combo system (REMOVED)
// bananaCombo = 0;
// comboText.visible = false;
// if (bananaComboTimer) {
// LK.clearTimeout(bananaComboTimer);
// bananaComboTimer = null;
// }
LK.getSound('gameover').play();
LK.effects.flashScreen(0xff0000, 1000);
// [Bu kısım kaldırıldı] -> createSoulEffect ile kalp efektleri artık çıkmayacak
// Remove all falling items
for (var i = fallingItems.length - 1; i >= 0; i--) {
fallingItems[i].destroy();
}
fallingItems = [];
// Save score to global score history (not persistent)
var score = LK.getScore();
if (typeof score === "number" && score > 0) {
scoreHistory = scoreHistory || [];
scoreHistory.push(score);
// Keep only last 20 scores
if (scoreHistory.length > 20) {
scoreHistory = scoreHistory.slice(scoreHistory.length - 20);
}
}
// Show game over screen
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
// Show click to play text again
clickToPlayText.visible = true;
}
function spawnItem() {
var score = LK.getScore();
var isBomb = false;
var isRottenBanana = false;
var isHeart = false;
// Heart spawn logic
if (!isHeartAlreadyFalling()) {
if (score >= 50 && lives === 1 && !extraHeartSpawned) {
isHeart = true;
extraHeartSpawned = true;
} else if (score >= 70 && lives === 1 && extraHeartSpawned && !extraHeartCaught) {
isHeart = true;
}
}
// Rotten banana and bomb logic (independent, so both can spawn)
if (!isHeart) {
if (score >= 20 && Math.random() < 0.3) {
isRottenBanana = true;
} else if (score >= 10 && Math.random() < 0.3) {
isBomb = true;
}
}
var itemType = "banana";
if (isHeart) {
itemType = "heart";
} else if (isRottenBanana) {
itemType = "rottenBanana";
} else if (isBomb) {
itemType = "bomb";
}
var item = new FallingItem();
if (itemType === "heart" && score > 100 && lives === 1) {
item.init(itemType, 16);
} else if (itemType === "heart" && score >= 50 && lives === 1) {
item.init(itemType, 10);
} else {
item.init(itemType, currentSpeed);
}
fallingItems.push(item);
game.addChild(item);
return item;
}
// Update game state each frame
game.update = function () {
if (!gameStarted || isGameOver) {
return;
}
// Update all falling items
for (var i = fallingItems.length - 1; i >= 0; i--) {
var item = fallingItems[i];
// Check if item has fallen off screen
if (item && item.lastY !== undefined && item.active && item.lastY < SCREEN_HEIGHT && item.y >= SCREEN_HEIGHT) {
if (item.type === "banana" && item.active) {
// Missed a banana - lose a life
decreaseLife();
LK.getSound('heartLossSound').play(); // Play sound for missed banana
item.remove();
} else if (item.type === "bomb" && item.active || item.type === "rottenBanana" && item.active) {
// Bomb or rotten banana fell off screen - remove it with no penalty
item.destroy();
fallingItems.splice(i, 1);
}
}
// Remove destroyed items from array
if (!item || !item.active || item.alpha <= 0) {
fallingItems.splice(i, 1);
}
}
// Spawn new items at intervals
spawnCounter++;
// Get current score for difficulty calculations
var score = LK.getScore();
// Hearts will be spawned based on specific conditions in spawnItem function
if (spawnCounter >= currentSpawnInterval) {
spawnItem();
spawnCounter = 0;
// Set speed multiplier based on score thresholds
var speedMultiplier = 1;
// 0-150 arası sabit artışlar (orijinal hız tablosu)
if (score >= 150) {
speedMultiplier = 21;
} else if (score >= 140) {
speedMultiplier = 20.5;
} else if (score >= 130) {
speedMultiplier = 20;
} else if (score >= 120) {
speedMultiplier = 19.5;
} else if (score >= 110) {
speedMultiplier = 19;
} else if (score >= 100) {
speedMultiplier = 18;
} else if (score >= 90) {
speedMultiplier = 17;
} else if (score >= 80) {
speedMultiplier = 16;
} else if (score >= 70) {
speedMultiplier = 15;
} else if (score >= 60) {
speedMultiplier = 14;
} else if (score >= 50) {
speedMultiplier = 12.5;
} else if (score >= 40) {
speedMultiplier = 12;
} else if (score >= 30) {
speedMultiplier = 11.5;
} else if (score >= 20) {
speedMultiplier = 11;
} else if (score >= 10) {
speedMultiplier = 10;
} else if (score >= 0) {
speedMultiplier = 9;
} else {
speedMultiplier = 7;
}
// 150’den sonra 300000’e kadar dinamik artış: +0.25 her 1 skor artışı için
if (score > 150 && score <= 300000) {
speedMultiplier = 15 + (score - 150) * 0.5;
}
// Eğer istersen 300000’den sonrası için burada farklı artış mantığı eklenebilir.
// Örnek olarak, 300000’den sonra her 1000 puanda 0.1 artış veya başka bir şey gibi.
// Güncellenen hız, oyun içindeki hız değişkenine atanıyor
currentSpeed = 2 * speedMultiplier;
// Spawn interval (yeniden item çıkma aralığı) puana göre azalıyor ama minimum sınırı var
currentSpawnInterval = Math.max(MIN_SPAWN_INTERVAL, SPAWN_INTERVAL_INITIAL - score * INTERVAL_DECREMENT);
}
// Score text’i güncelle
scoreText.setText('Score: ' + score);
};
// Initialize the game
function initGame() {
// Reset game variables
lives = MAX_LIVES;
LK.setScore(0);
spawnCounter = 0;
currentSpawnInterval = SPAWN_INTERVAL_INITIAL;
currentSpeed = 2;
isGameOver = false;
extraHeartSpawned = false; // Reset heart spawn tracking
extraHeartCaught = false; // Reset heart catch tracking
fallingItems = [];
// Reset banana combo system (REMOVED)
// bananaCombo = 0;
// maxBananaCombo = 0;
// comboText.visible = false;
// if (bananaComboTimer) {
// LK.clearTimeout(bananaComboTimer);
// bananaComboTimer = null;
// }
// Initialize UI
initLives();
scoreText.setText('Score: 0');
// Play background music
LK.playMusic('gameMusic');
}
// Set up click handler to start game
// (Removed, menu handles game start)
// Initialize but don't start the game yet
lives = MAX_LIVES;
initLives();
Pixel banana. In-Game asset. High contrast. No shadows
Pixel art Bomb.. In-Game asset. High contrast. No shadows
Pixel art Heart. In-Game asset. High contrast. No shadows
Pixel art Forest background. In-Game asset. High contrast. No shadows
Purple color "+1" in pixels art style . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Simple empty clean pixel art background in brown. In-Game asset. 2d. High contrast. No shadows. Game menu background.
Menu button. Pixel art dark brown.. In-Game asset. 2d. High contrast. No shadows No writing inside